From 1fcb30dee9487719947786ab91b76ddc0e5ca1b1 Mon Sep 17 00:00:00 2001 From: shuiyuPyLab Date: Sun, 9 Aug 2026 10:15:16 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E9=A1=B9=E7=9B=AE=E5=B7=B2=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E8=87=B3=20Gitee/GitHub=EF=BC=8C=E4=BB=85=E4=BF=9D?= =?UTF-8?q?=E7=95=99=E8=BF=81=E7=A7=BB=E8=AF=B4=E6=98=8E=20(tombstone)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 - LICENSE | 21 - README.md | 3 +- android_pyapi.egg-info/PKG-INFO | 264 - android_pyapi.egg-info/SOURCES.txt | 230 - android_pyapi.egg-info/dependency_links.txt | 1 - android_pyapi.egg-info/requires.txt | 26 - android_pyapi.egg-info/top_level.txt | 1 - android_pyapi/__init__.py | 604 -- android_pyapi/_core.py | 186 - android_pyapi/_linux_impl.py | 1266 ----- android_pyapi/_windows_impl.py | 1380 ----- android_pyapi/android/__init__.py | 0 android_pyapi/android/accessibilityservice.py | 382 -- android_pyapi/android/accounts.py | 456 -- android_pyapi/android/animation.py | 951 ---- android_pyapi/android/app.py | 2521 --------- android_pyapi/android/app_actionbar.py | 539 -- android_pyapi/android/app_admin.py | 424 -- android_pyapi/android/app_assist.py | 469 -- android_pyapi/android/app_backup.py | 477 -- android_pyapi/android/app_dialog.py | 874 --- android_pyapi/android/app_fragment.py | 1055 ---- android_pyapi/android/app_job.py | 444 -- android_pyapi/android/app_notification.py | 1307 ----- android_pyapi/android/app_people.py | 268 - android_pyapi/android/app_role.py | 117 - android_pyapi/android/app_search.py | 494 -- android_pyapi/android/app_slice.py | 409 -- android_pyapi/android/app_tasks.py | 969 ---- android_pyapi/android/app_time.py | 181 - android_pyapi/android/app_usage.py | 527 -- android_pyapi/android/app_voiceinteractor.py | 590 -- android_pyapi/android/app_wallpaper.py | 571 -- android_pyapi/android/appwidget.py | 360 -- android_pyapi/android/bluetooth.py | 849 --- android_pyapi/android/bluetooth_le.py | 512 -- android_pyapi/android/companion.py | 415 -- android_pyapi/android/companion_ext.py | 716 --- android_pyapi/android/content.py | 2054 ------- android_pyapi/android/content_intent.py | 2087 ------- android_pyapi/android/content_om.py | 551 -- android_pyapi/android/content_pm.py | 801 --- android_pyapi/android/content_res.py | 587 -- android_pyapi/android/content_sync.py | 915 --- android_pyapi/android/credentials.py | 418 -- android_pyapi/android/database.py | 1092 ---- android_pyapi/android/database_ext.py | 866 --- android_pyapi/android/database_sqlite.py | 1118 ---- android_pyapi/android/drm.py | 1090 ---- android_pyapi/android/gesture.py | 545 -- android_pyapi/android/gesture_ext.py | 1158 ---- android_pyapi/android/graphics.py | 3455 ------------ android_pyapi/android/graphics_drawable.py | 1128 ---- .../android/graphics_drawable_ext.py | 668 --- .../android/graphics_drawable_shapes.py | 589 -- android_pyapi/android/graphics_fonts.py | 298 - android_pyapi/android/graphics_pdf.py | 335 -- android_pyapi/android/graphics_surface.py | 699 --- android_pyapi/android/graphics_text.py | 779 --- android_pyapi/android/hardware.py | 746 --- android_pyapi/android/hardware_biometrics.py | 291 - android_pyapi/android/hardware_camera2.py | 761 --- .../android/hardware_camera2_params.py | 854 --- android_pyapi/android/hardware_consumerir.py | 93 - android_pyapi/android/hardware_display.py | 299 - android_pyapi/android/hardware_fingerprint.py | 179 - android_pyapi/android/hardware_hdmi.py | 775 --- android_pyapi/android/hardware_input.py | 254 - android_pyapi/android/hardware_light.py | 660 --- android_pyapi/android/hardware_location.py | 848 --- android_pyapi/android/hardware_radio.py | 417 -- android_pyapi/android/hardware_sensor_ext.py | 831 --- android_pyapi/android/hardware_serial.py | 513 -- android_pyapi/android/hardware_thermal.py | 218 - android_pyapi/android/hardware_usb.py | 409 -- android_pyapi/android/inputmethod.py | 804 --- android_pyapi/android/inputmethodservice.py | 1078 ---- android_pyapi/android/location.py | 1266 ----- android_pyapi/android/location_ext.py | 464 -- android_pyapi/android/location_geofence.py | 537 -- android_pyapi/android/managers.py | 1773 ------ android_pyapi/android/media.py | 2525 --------- android_pyapi/android/media_async.py | 1621 ------ android_pyapi/android/media_audio.py | 1538 ------ android_pyapi/android/media_audiofx.py | 547 -- android_pyapi/android/media_browse.py | 310 -- android_pyapi/android/media_capture.py | 748 --- android_pyapi/android/media_effect.py | 615 --- android_pyapi/android/media_face.py | 499 -- android_pyapi/android/media_image.py | 649 --- android_pyapi/android/media_midi.py | 315 -- android_pyapi/android/media_projection.py | 171 - android_pyapi/android/media_remoteblob.py | 633 --- android_pyapi/android/media_session.py | 854 --- android_pyapi/android/media_tv.py | 359 -- android_pyapi/android/media_tv_ext.py | 1500 ----- android_pyapi/android/mtp.py | 476 -- android_pyapi/android/net.py | 1559 ------ android_pyapi/android/net_ethernet.py | 551 -- android_pyapi/android/net_ext.py | 1447 ----- android_pyapi/android/net_hotspot2.py | 375 -- android_pyapi/android/net_http.py | 643 --- android_pyapi/android/net_nsd.py | 514 -- android_pyapi/android/net_rtp.py | 246 - android_pyapi/android/net_sip.py | 639 --- android_pyapi/android/net_ssl.py | 406 -- android_pyapi/android/net_vpn.py | 279 - android_pyapi/android/net_wifi.py | 1136 ---- android_pyapi/android/net_wifi_aware.py | 428 -- android_pyapi/android/net_wifi_rtt.py | 319 -- android_pyapi/android/nfc.py | 390 -- android_pyapi/android/nfc_ext.py | 816 --- android_pyapi/android/opengl.py | 464 -- android_pyapi/android/os.py | 2370 -------- android_pyapi/android/os_health.py | 215 - android_pyapi/android/os_power.py | 902 --- android_pyapi/android/os_storage.py | 387 -- android_pyapi/android/os_user.py | 408 -- android_pyapi/android/permission.py | 162 - android_pyapi/android/preference.py | 654 --- android_pyapi/android/print.py | 581 -- android_pyapi/android/printservice.py | 917 --- android_pyapi/android/provider.py | 688 --- android_pyapi/android/provider_calendar.py | 1379 ----- android_pyapi/android/provider_contacts.py | 776 --- android_pyapi/android/provider_settings.py | 1660 ------ android_pyapi/android/renderscript.py | 903 --- android_pyapi/android/se.py | 175 - android_pyapi/android/security.py | 182 - android_pyapi/android/service_autofill.py | 423 -- android_pyapi/android/service_controls.py | 350 -- android_pyapi/android/service_dreams.py | 140 - android_pyapi/android/service_media.py | 812 --- android_pyapi/android/service_notification.py | 378 -- .../android/service_quicksettings.py | 214 - android_pyapi/android/service_voice.py | 869 --- android_pyapi/android/service_vr.py | 62 - android_pyapi/android/service_wallpaper.py | 264 - android_pyapi/android/sip.py | 519 -- android_pyapi/android/speech.py | 370 -- android_pyapi/android/speech_tts.py | 401 -- android_pyapi/android/system.py | 592 -- android_pyapi/android/telecom.py | 737 --- android_pyapi/android/telephony.py | 709 --- android_pyapi/android/telephony_cdma.py | 714 --- android_pyapi/android/telephony_data.py | 1200 ---- android_pyapi/android/telephony_euicc.py | 1181 ---- android_pyapi/android/telephony_gsm.py | 1082 ---- android_pyapi/android/telephony_ims.py | 1545 ------ android_pyapi/android/telephony_mbms.py | 1180 ---- android_pyapi/android/test.py | 485 -- android_pyapi/android/text.py | 1167 ---- android_pyapi/android/text_format.py | 456 -- android_pyapi/android/text_method.py | 676 --- android_pyapi/android/text_style.py | 974 ---- android_pyapi/android/text_util.py | 297 - android_pyapi/android/transition.py | 504 -- android_pyapi/android/util.py | 1111 ---- android_pyapi/android/uwb.py | 420 -- android_pyapi/android/view.py | 4207 -------------- android_pyapi/android/view_accessibility.py | 1488 ----- .../android/view_accessibility_ext.py | 744 --- android_pyapi/android/view_animation.py | 935 ---- android_pyapi/android/view_animator.py | 887 --- android_pyapi/android/view_autofill.py | 313 -- android_pyapi/android/view_dragndrop.py | 248 - android_pyapi/android/view_ext.py | 2520 --------- android_pyapi/android/view_gesture.py | 647 --- android_pyapi/android/view_haptic.py | 381 -- android_pyapi/android/view_inputmethod.py | 859 --- android_pyapi/android/view_textclassifier.py | 426 -- android_pyapi/android/view_textservice.py | 395 -- android_pyapi/android/view_translation.py | 435 -- android_pyapi/android/view_window.py | 969 ---- android_pyapi/android/webkit.py | 992 ---- android_pyapi/android/webkit_ext.py | 1554 ------ android_pyapi/android/widget.py | 4891 ----------------- android_pyapi/android/widget_adapter.py | 1145 ---- android_pyapi/android/widget_ext.py | 2232 -------- android_pyapi/dalvik/__init__.py | 0 android_pyapi/dalvik/system.py | 721 --- android_pyapi/java/__init__.py | 4 - android_pyapi/java/beans.py | 806 --- android_pyapi/java/crypto.py | 628 --- android_pyapi/java/io.py | 2120 ------- android_pyapi/java/io_ext.py | 1414 ----- android_pyapi/java/io_file.py | 1451 ----- android_pyapi/java/lang.py | 2128 ------- android_pyapi/java/lang_annotation.py | 303 - android_pyapi/java/lang_invoke.py | 766 --- android_pyapi/java/lang_management.py | 830 --- android_pyapi/java/lang_ref.py | 386 -- android_pyapi/java/lang_reflect.py | 968 ---- android_pyapi/java/math.py | 683 --- android_pyapi/java/net.py | 1759 ------ android_pyapi/java/nio.py | 622 --- android_pyapi/java/nio_byteorder.py | 33 - android_pyapi/java/nio_channels.py | 1103 ---- android_pyapi/java/nio_charset.py | 573 -- android_pyapi/java/nio_file.py | 703 --- android_pyapi/java/rmi.py | 776 --- android_pyapi/java/security.py | 1145 ---- android_pyapi/java/security_cert.py | 907 --- android_pyapi/java/security_spec.py | 697 --- android_pyapi/java/sql.py | 2242 -------- android_pyapi/java/text.py | 1041 ---- android_pyapi/java/time.py | 1619 ------ android_pyapi/java/util.py | 4096 -------------- android_pyapi/java/util_concurrent.py | 2037 ------- android_pyapi/java/util_concurrent_atomic.py | 1044 ---- android_pyapi/java/util_concurrent_locks.py | 681 --- android_pyapi/java/util_function.py | 599 -- android_pyapi/java/util_logging.py | 939 ---- android_pyapi/java/util_prefs.py | 544 -- android_pyapi/java/util_regex.py | 287 - android_pyapi/java/util_stream.py | 1380 ----- android_pyapi/java/util_zip.py | 697 --- android_pyapi/javax/__init__.py | 0 android_pyapi/javax/crypto.py | 1031 ---- android_pyapi/javax/net.py | 785 --- android_pyapi/javax/security.py | 768 --- android_pyapi/javax/sql.py | 1248 ----- android_pyapi/org/__init__.py | 0 android_pyapi/org/apache/__init__.py | 0 android_pyapi/org/apache/http.py | 1109 ---- android_pyapi/org/json.py | 910 --- android_pyapi/org/w3c_dom.py | 1292 ----- android_pyapi/org/xml_pull.py | 589 -- android_pyapi/org/xml_sax.py | 890 --- setup.py | 64 - test_scripts/network_test.js | 0 test_scripts/quick_start.js | 0 test_scripts/system_tools.js | 0 test_scripts/test_android_api.py | 141 - test_scripts/test_extended_api.py | 400 -- test_scripts/test_network.py | 73 - test_scripts/test_system.py | 97 - test_scripts/time.js | 0 test_windows.py | 145 - 240 files changed, 1 insertion(+), 186470 deletions(-) delete mode 100644 .gitignore delete mode 100644 LICENSE delete mode 100644 android_pyapi.egg-info/PKG-INFO delete mode 100644 android_pyapi.egg-info/SOURCES.txt delete mode 100644 android_pyapi.egg-info/dependency_links.txt delete mode 100644 android_pyapi.egg-info/requires.txt delete mode 100644 android_pyapi.egg-info/top_level.txt delete mode 100644 android_pyapi/__init__.py delete mode 100644 android_pyapi/_core.py delete mode 100644 android_pyapi/_linux_impl.py delete mode 100644 android_pyapi/_windows_impl.py delete mode 100644 android_pyapi/android/__init__.py delete mode 100644 android_pyapi/android/accessibilityservice.py delete mode 100644 android_pyapi/android/accounts.py delete mode 100644 android_pyapi/android/animation.py delete mode 100644 android_pyapi/android/app.py delete mode 100644 android_pyapi/android/app_actionbar.py delete mode 100644 android_pyapi/android/app_admin.py delete mode 100644 android_pyapi/android/app_assist.py delete mode 100644 android_pyapi/android/app_backup.py delete mode 100644 android_pyapi/android/app_dialog.py delete mode 100644 android_pyapi/android/app_fragment.py delete mode 100644 android_pyapi/android/app_job.py delete mode 100644 android_pyapi/android/app_notification.py delete mode 100644 android_pyapi/android/app_people.py delete mode 100644 android_pyapi/android/app_role.py delete mode 100644 android_pyapi/android/app_search.py delete mode 100644 android_pyapi/android/app_slice.py delete mode 100644 android_pyapi/android/app_tasks.py delete mode 100644 android_pyapi/android/app_time.py delete mode 100644 android_pyapi/android/app_usage.py delete mode 100644 android_pyapi/android/app_voiceinteractor.py delete mode 100644 android_pyapi/android/app_wallpaper.py delete mode 100644 android_pyapi/android/appwidget.py delete mode 100644 android_pyapi/android/bluetooth.py delete mode 100644 android_pyapi/android/bluetooth_le.py delete mode 100644 android_pyapi/android/companion.py delete mode 100644 android_pyapi/android/companion_ext.py delete mode 100644 android_pyapi/android/content.py delete mode 100644 android_pyapi/android/content_intent.py delete mode 100644 android_pyapi/android/content_om.py delete mode 100644 android_pyapi/android/content_pm.py delete mode 100644 android_pyapi/android/content_res.py delete mode 100644 android_pyapi/android/content_sync.py delete mode 100644 android_pyapi/android/credentials.py delete mode 100644 android_pyapi/android/database.py delete mode 100644 android_pyapi/android/database_ext.py delete mode 100644 android_pyapi/android/database_sqlite.py delete mode 100644 android_pyapi/android/drm.py delete mode 100644 android_pyapi/android/gesture.py delete mode 100644 android_pyapi/android/gesture_ext.py delete mode 100644 android_pyapi/android/graphics.py delete mode 100644 android_pyapi/android/graphics_drawable.py delete mode 100644 android_pyapi/android/graphics_drawable_ext.py delete mode 100644 android_pyapi/android/graphics_drawable_shapes.py delete mode 100644 android_pyapi/android/graphics_fonts.py delete mode 100644 android_pyapi/android/graphics_pdf.py delete mode 100644 android_pyapi/android/graphics_surface.py delete mode 100644 android_pyapi/android/graphics_text.py delete mode 100644 android_pyapi/android/hardware.py delete mode 100644 android_pyapi/android/hardware_biometrics.py delete mode 100644 android_pyapi/android/hardware_camera2.py delete mode 100644 android_pyapi/android/hardware_camera2_params.py delete mode 100644 android_pyapi/android/hardware_consumerir.py delete mode 100644 android_pyapi/android/hardware_display.py delete mode 100644 android_pyapi/android/hardware_fingerprint.py delete mode 100644 android_pyapi/android/hardware_hdmi.py delete mode 100644 android_pyapi/android/hardware_input.py delete mode 100644 android_pyapi/android/hardware_light.py delete mode 100644 android_pyapi/android/hardware_location.py delete mode 100644 android_pyapi/android/hardware_radio.py delete mode 100644 android_pyapi/android/hardware_sensor_ext.py delete mode 100644 android_pyapi/android/hardware_serial.py delete mode 100644 android_pyapi/android/hardware_thermal.py delete mode 100644 android_pyapi/android/hardware_usb.py delete mode 100644 android_pyapi/android/inputmethod.py delete mode 100644 android_pyapi/android/inputmethodservice.py delete mode 100644 android_pyapi/android/location.py delete mode 100644 android_pyapi/android/location_ext.py delete mode 100644 android_pyapi/android/location_geofence.py delete mode 100644 android_pyapi/android/managers.py delete mode 100644 android_pyapi/android/media.py delete mode 100644 android_pyapi/android/media_async.py delete mode 100644 android_pyapi/android/media_audio.py delete mode 100644 android_pyapi/android/media_audiofx.py delete mode 100644 android_pyapi/android/media_browse.py delete mode 100644 android_pyapi/android/media_capture.py delete mode 100644 android_pyapi/android/media_effect.py delete mode 100644 android_pyapi/android/media_face.py delete mode 100644 android_pyapi/android/media_image.py delete mode 100644 android_pyapi/android/media_midi.py delete mode 100644 android_pyapi/android/media_projection.py delete mode 100644 android_pyapi/android/media_remoteblob.py delete mode 100644 android_pyapi/android/media_session.py delete mode 100644 android_pyapi/android/media_tv.py delete mode 100644 android_pyapi/android/media_tv_ext.py delete mode 100644 android_pyapi/android/mtp.py delete mode 100644 android_pyapi/android/net.py delete mode 100644 android_pyapi/android/net_ethernet.py delete mode 100644 android_pyapi/android/net_ext.py delete mode 100644 android_pyapi/android/net_hotspot2.py delete mode 100644 android_pyapi/android/net_http.py delete mode 100644 android_pyapi/android/net_nsd.py delete mode 100644 android_pyapi/android/net_rtp.py delete mode 100644 android_pyapi/android/net_sip.py delete mode 100644 android_pyapi/android/net_ssl.py delete mode 100644 android_pyapi/android/net_vpn.py delete mode 100644 android_pyapi/android/net_wifi.py delete mode 100644 android_pyapi/android/net_wifi_aware.py delete mode 100644 android_pyapi/android/net_wifi_rtt.py delete mode 100644 android_pyapi/android/nfc.py delete mode 100644 android_pyapi/android/nfc_ext.py delete mode 100644 android_pyapi/android/opengl.py delete mode 100644 android_pyapi/android/os.py delete mode 100644 android_pyapi/android/os_health.py delete mode 100644 android_pyapi/android/os_power.py delete mode 100644 android_pyapi/android/os_storage.py delete mode 100644 android_pyapi/android/os_user.py delete mode 100644 android_pyapi/android/permission.py delete mode 100644 android_pyapi/android/preference.py delete mode 100644 android_pyapi/android/print.py delete mode 100644 android_pyapi/android/printservice.py delete mode 100644 android_pyapi/android/provider.py delete mode 100644 android_pyapi/android/provider_calendar.py delete mode 100644 android_pyapi/android/provider_contacts.py delete mode 100644 android_pyapi/android/provider_settings.py delete mode 100644 android_pyapi/android/renderscript.py delete mode 100644 android_pyapi/android/se.py delete mode 100644 android_pyapi/android/security.py delete mode 100644 android_pyapi/android/service_autofill.py delete mode 100644 android_pyapi/android/service_controls.py delete mode 100644 android_pyapi/android/service_dreams.py delete mode 100644 android_pyapi/android/service_media.py delete mode 100644 android_pyapi/android/service_notification.py delete mode 100644 android_pyapi/android/service_quicksettings.py delete mode 100644 android_pyapi/android/service_voice.py delete mode 100644 android_pyapi/android/service_vr.py delete mode 100644 android_pyapi/android/service_wallpaper.py delete mode 100644 android_pyapi/android/sip.py delete mode 100644 android_pyapi/android/speech.py delete mode 100644 android_pyapi/android/speech_tts.py delete mode 100644 android_pyapi/android/system.py delete mode 100644 android_pyapi/android/telecom.py delete mode 100644 android_pyapi/android/telephony.py delete mode 100644 android_pyapi/android/telephony_cdma.py delete mode 100644 android_pyapi/android/telephony_data.py delete mode 100644 android_pyapi/android/telephony_euicc.py delete mode 100644 android_pyapi/android/telephony_gsm.py delete mode 100644 android_pyapi/android/telephony_ims.py delete mode 100644 android_pyapi/android/telephony_mbms.py delete mode 100644 android_pyapi/android/test.py delete mode 100644 android_pyapi/android/text.py delete mode 100644 android_pyapi/android/text_format.py delete mode 100644 android_pyapi/android/text_method.py delete mode 100644 android_pyapi/android/text_style.py delete mode 100644 android_pyapi/android/text_util.py delete mode 100644 android_pyapi/android/transition.py delete mode 100644 android_pyapi/android/util.py delete mode 100644 android_pyapi/android/uwb.py delete mode 100644 android_pyapi/android/view.py delete mode 100644 android_pyapi/android/view_accessibility.py delete mode 100644 android_pyapi/android/view_accessibility_ext.py delete mode 100644 android_pyapi/android/view_animation.py delete mode 100644 android_pyapi/android/view_animator.py delete mode 100644 android_pyapi/android/view_autofill.py delete mode 100644 android_pyapi/android/view_dragndrop.py delete mode 100644 android_pyapi/android/view_ext.py delete mode 100644 android_pyapi/android/view_gesture.py delete mode 100644 android_pyapi/android/view_haptic.py delete mode 100644 android_pyapi/android/view_inputmethod.py delete mode 100644 android_pyapi/android/view_textclassifier.py delete mode 100644 android_pyapi/android/view_textservice.py delete mode 100644 android_pyapi/android/view_translation.py delete mode 100644 android_pyapi/android/view_window.py delete mode 100644 android_pyapi/android/webkit.py delete mode 100644 android_pyapi/android/webkit_ext.py delete mode 100644 android_pyapi/android/widget.py delete mode 100644 android_pyapi/android/widget_adapter.py delete mode 100644 android_pyapi/android/widget_ext.py delete mode 100644 android_pyapi/dalvik/__init__.py delete mode 100644 android_pyapi/dalvik/system.py delete mode 100644 android_pyapi/java/__init__.py delete mode 100644 android_pyapi/java/beans.py delete mode 100644 android_pyapi/java/crypto.py delete mode 100644 android_pyapi/java/io.py delete mode 100644 android_pyapi/java/io_ext.py delete mode 100644 android_pyapi/java/io_file.py delete mode 100644 android_pyapi/java/lang.py delete mode 100644 android_pyapi/java/lang_annotation.py delete mode 100644 android_pyapi/java/lang_invoke.py delete mode 100644 android_pyapi/java/lang_management.py delete mode 100644 android_pyapi/java/lang_ref.py delete mode 100644 android_pyapi/java/lang_reflect.py delete mode 100644 android_pyapi/java/math.py delete mode 100644 android_pyapi/java/net.py delete mode 100644 android_pyapi/java/nio.py delete mode 100644 android_pyapi/java/nio_byteorder.py delete mode 100644 android_pyapi/java/nio_channels.py delete mode 100644 android_pyapi/java/nio_charset.py delete mode 100644 android_pyapi/java/nio_file.py delete mode 100644 android_pyapi/java/rmi.py delete mode 100644 android_pyapi/java/security.py delete mode 100644 android_pyapi/java/security_cert.py delete mode 100644 android_pyapi/java/security_spec.py delete mode 100644 android_pyapi/java/sql.py delete mode 100644 android_pyapi/java/text.py delete mode 100644 android_pyapi/java/time.py delete mode 100644 android_pyapi/java/util.py delete mode 100644 android_pyapi/java/util_concurrent.py delete mode 100644 android_pyapi/java/util_concurrent_atomic.py delete mode 100644 android_pyapi/java/util_concurrent_locks.py delete mode 100644 android_pyapi/java/util_function.py delete mode 100644 android_pyapi/java/util_logging.py delete mode 100644 android_pyapi/java/util_prefs.py delete mode 100644 android_pyapi/java/util_regex.py delete mode 100644 android_pyapi/java/util_stream.py delete mode 100644 android_pyapi/java/util_zip.py delete mode 100644 android_pyapi/javax/__init__.py delete mode 100644 android_pyapi/javax/crypto.py delete mode 100644 android_pyapi/javax/net.py delete mode 100644 android_pyapi/javax/security.py delete mode 100644 android_pyapi/javax/sql.py delete mode 100644 android_pyapi/org/__init__.py delete mode 100644 android_pyapi/org/apache/__init__.py delete mode 100644 android_pyapi/org/apache/http.py delete mode 100644 android_pyapi/org/json.py delete mode 100644 android_pyapi/org/w3c_dom.py delete mode 100644 android_pyapi/org/xml_pull.py delete mode 100644 android_pyapi/org/xml_sax.py delete mode 100644 setup.py delete mode 100644 test_scripts/network_test.js delete mode 100644 test_scripts/quick_start.js delete mode 100644 test_scripts/system_tools.js delete mode 100644 test_scripts/test_android_api.py delete mode 100644 test_scripts/test_extended_api.py delete mode 100644 test_scripts/test_network.py delete mode 100644 test_scripts/test_system.py delete mode 100644 test_scripts/time.js delete mode 100644 test_windows.py diff --git a/.gitignore b/.gitignore deleted file mode 100644 index bb7f1a2..0000000 --- a/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -__pycache__/ -*.pyc -*.pyo -.DS_Store \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 7203af6..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 shuiyu - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index d162484..74857a2 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,7 @@ 你好呀~这个仓库已经搬到更好用的地方啦。 -为了让代码管理更清爽、多平台同步更省心,我们把原先分散的镜像统一收拢到 -**Gitee** 与 **GitHub** 主仓维护,这里就只保留这张"迁移小纸条"咯。 +为了让代码管理更清爽、多平台同步更省心,我们把原先分散的镜像统一收拢到主仓维护,这里就只保留这张"迁移小纸条"咯。 📦 新家在这里: - Gitee:https://gitee.com/shuiyu1123/Android-pyApi diff --git a/android_pyapi.egg-info/PKG-INFO b/android_pyapi.egg-info/PKG-INFO deleted file mode 100644 index 603666b..0000000 --- a/android_pyapi.egg-info/PKG-INFO +++ /dev/null @@ -1,264 +0,0 @@ -Metadata-Version: 2.4 -Name: android-pyapi -Version: 1.0.0 -Summary: Android API for Python — 在桌面/Linux 上原生运行 Android API -Home-page: https://gitee.com/shuiyu1123/Android-pyApi -Author: shuiyu1123 -License: MIT -Classifier: Development Status :: 4 - Beta -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: POSIX :: Linux -Classifier: Operating System :: MacOS -Classifier: Operating System :: Microsoft :: Windows -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: System :: Hardware -Requires-Python: >=3.8 -Description-Content-Type: text/markdown -License-File: LICENSE -Provides-Extra: linux -Requires-Dist: psutil; extra == "linux" -Requires-Dist: requests; extra == "linux" -Requires-Dist: Pillow; extra == "linux" -Requires-Dist: opencv-python; extra == "linux" -Requires-Dist: numpy; extra == "linux" -Provides-Extra: windows -Requires-Dist: psutil; extra == "windows" -Requires-Dist: requests; extra == "windows" -Requires-Dist: Pillow; extra == "windows" -Requires-Dist: opencv-python; extra == "windows" -Requires-Dist: numpy; extra == "windows" -Requires-Dist: pyautogui; extra == "windows" -Requires-Dist: pyperclip; extra == "windows" -Requires-Dist: screeninfo; extra == "windows" -Requires-Dist: pywin32; extra == "windows" -Requires-Dist: wmi; extra == "windows" -Requires-Dist: pycaw; extra == "windows" -Requires-Dist: comtypes; extra == "windows" -Requires-Dist: win10toast; extra == "windows" -Provides-Extra: dev -Requires-Dist: pytest; extra == "dev" -Requires-Dist: pytest-cov; extra == "dev" -Dynamic: author -Dynamic: classifier -Dynamic: description -Dynamic: description-content-type -Dynamic: home-page -Dynamic: license -Dynamic: license-file -Dynamic: provides-extra -Dynamic: requires-python -Dynamic: summary - -# Android-pyApi - -> **Android API for Python** — 在桌面/Linux 上原生运行 Android API 的 Python 库 - -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/) -[![Platform](https://img.shields.io/badge/platform-Linux%20%7C%20macOS%20%7C%20Windows-green.svg)](#) - -## 这是什么 - -Android-pyApi 将完整的 Android SDK API 翻译为纯 Python 实现,让你在桌面环境直接调用 Android API——不需要模拟器、不需要真机、不需要 Android Studio。 - -**213 个模块,178,000+ 行代码,覆盖 android/java/javax/org/dalvik 全包。** - -在 Linux 上,系统级 API(网络、传感器、蓝牙、音频、位置等)使用真实系统调用,返回真实数据。 - -## 核心特性 - -### API 覆盖 - -| 包 | 模块数 | 内容 | -|---|---|---| -| `android.*` | 167 | app, content, os, view, widget, graphics, media, hardware, net, telephony, provider 等 | -| `java.*` | 35 | lang, io, util, net, nio, security, crypto, sql, math, time 等 | -| `javax.*` | 4 | net, crypto, security, sql | -| `org.*` | 5 | json, w3c_dom, xml_sax, xml_pull, apache_http | -| `dalvik.*` | 1 | system | - -### Linux 真实实现 - -在 Linux 环境下,以下 API 使用真实系统调用而非存根: - -| API | 实现方式 | 真实行为 | -|-----|---------|---------| -| ConnectivityManager | `ip` 命令 + `/proc/net` | 真实 IP、接口、DNS、网关 | -| LocationManager | IP 地理定位 | 真实经纬度、城市、ISP | -| SensorManager | `/sys/class/hwmon` | 真实温度、CPU 频率、风扇 | -| AudioManager | `amixer` (ALSA) | 真实音量控制 | -| BluetoothAdapter | `bluetoothctl` (BlueZ) | 真实扫描、配对、连接 | -| ClipboardManager | `xclip` | 真实剪贴板读写 | -| PowerManager / BatteryManager | `psutil` + `/sys/class/power_supply` | 真实 CPU、内存、电池 | -| TelephonyManager | MAC 地址 + 内核版本 | 真实设备 ID | -| NotificationManager | `notify-send` | 真实桌面通知 | -| Activity / Display | `xdotool` + X11 | 真实屏幕尺寸、截屏 | -| ActivityManager | `psutil` + `/proc` | 真实进程列表 | -| Shell | `subprocess` | 真实命令执行 | -| Camera | OpenCV / V4L2 | 真实拍照 | -| UI 自动化 | `xdotool` | 真实点击、输入、按键 | - -### Windows 真实实现 - -在 Windows 环境下,以下 API 使用真实系统调用而非存根: - -| API | 实现方式 | 真实行为 | -|-----|---------|---------| -| ConnectivityManager | `ipconfig` + `netsh` + `socket` | 真实 IP、接口、DNS、网关、WiFi 信息 | -| LocationManager | IP 地理定位 | 真实经纬度、城市、ISP | -| SensorManager | `wmi` + `psutil` | 真实温度、CPU 频率、风扇转速 | -| AudioManager | `pycaw` / PowerShell | 真实音量控制、静音 | -| BluetoothAdapter | PowerShell Bluetooth cmdlets | 真实设备列表、配对状态 | -| ClipboardManager | `pyperclip` | 真实剪贴板读写 | -| PowerManager / BatteryManager | `psutil` | 真实 CPU、内存、电池状态 | -| NotificationManager | `win10toast` / PowerShell | 真实桌面通知 (Toast) | -| Activity / Display | `pyautogui` + `screeninfo` + `win32api` | 真实屏幕尺寸、截屏 | -| ActivityManager | `psutil` + `tasklist` | 真实进程列表 | -| Shell | `subprocess` + PowerShell | 真实命令执行 | -| Camera | OpenCV | 真实拍照 | -| UI 自动化 | `pyautogui` + `win32gui` | 真实点击、输入、按键、窗口操作 | - -### 跨平台 - -- **Linux** — 系统级 API 使用真实实现 (`_linux_impl.py`) -- **Windows** — 系统级 API 使用真实实现 (`_windows_impl.py`) -- **macOS** — 系统级 API 使用存根(后续支持) -- **API 形状** — 所有平台一致,方法签名完全对齐 Android SDK - -## 安装 - -```bash -# 从源码安装 -git clone https://gitee.com/shuiyu1123/Android-pyApi.git -cd Android-pyApi -pip install -e . - -# 或直接使用 -export PYTHONPATH=/path/to/Android-pyApi:$PYTHONPATH -``` - -### 可选依赖 - -```bash -# Python 库 (所有平台通用) -pip install psutil requests Pillow opencv-python numpy - -# Linux 系统工具 -sudo apt install xclip xdotool libnotify-bin alsa-utils bluez lm-sensors - -# Windows 专用库 (推荐安装以启用完整功能) -pip install pyautogui pyperclip screeninfo pywin32 wmi pycaw comtypes win10toast -``` - -## 快速开始 - -```python -from android_pyapi import Intent, Uri, Bundle, File, JSONObject, ArrayList, HashMap - -# Intent — Android 最核心的 API -intent = Intent("android.intent.action.VIEW") -intent.setData(Uri.parse("https://example.com")) -intent.putExtra("key", "value") -print(intent.getAction()) # android.intent.action.VIEW -print(intent.getData()) # https://example.com -print(intent.getStringExtra("key")) # value - -# java.util 集合 -lst = ArrayList() -lst.add("hello") -lst.add("world") -print(lst.size()) # 2 -print(lst.get(0)) # hello - -# JSONObject -json = JSONObject() -json.put("name", "test") -json.put("version", 42) -print(json.toString()) # {"name":"test","version":42} - -# Linux 真实实现 -from android_pyapi import Build, LocationManager, ConnectivityManager -print(Build.MODEL) # 真实主机名 -print(Build.MANUFACTURER) # Linux - -loc = LocationManager().getLastKnownLocation("network") -print(loc) # 真实经纬度 -``` - -## 项目结构 - -``` -android_pyapi/ -├── _core.py # Stub 基类、全局上下文 -├── _linux_impl.py # Linux 真实实现层 (14个实现类) -├── _windows_impl.py # Windows 真实实现层 (14个实现类) -├── __init__.py # 统一导出 -├── android/ # 167 个 Android 模块 -│ ├── app.py # Activity, Service, Fragment, Notification -│ ├── content.py # Context, Intent, ContentResolver, ClipboardManager -│ ├── os.py # Handler, Looper, Bundle, Build, PowerManager -│ ├── view.py # View, ViewGroup, MotionEvent, KeyEvent -│ ├── widget.py # TextView, Button, ListView, RecyclerView, Toast -│ ├── graphics.py # Bitmap, Canvas, Paint, Color, Path, Matrix -│ ├── hardware.py # SensorManager, Camera, BiometricPrompt -│ ├── net.py # ConnectivityManager, NetworkInfo -│ ├── telephony.py # TelephonyManager, SmsManager -│ ├── location.py # LocationManager, Location, Geocoder -│ ├── media.py # AudioManager, MediaPlayer, MediaRecorder -│ └── ... -├── java/ # 35 个 Java 标准库模块 -│ ├── lang.py # String, Math, System, Thread, Exception 体系 -│ ├── io.py # File, InputStream, OutputStream, Reader, Writer -│ ├── util.py # ArrayList, HashMap, Calendar, Collections, UUID -│ ├── net.py # URL, HttpURLConnection, Socket -│ └── ... -├── javax/ # 4 个 javax 模块 -├── org/ # 5 个 org 模块 -└── dalvik/ # 1 个 dalvik 模块 -``` - -## 设计理念 - -### 不是模拟器 - -Android-pyApi 不是 Android 模拟器。它不包含 ART 虚拟机、SurfaceFlinger、binder 等底层基础设施。 - -它是 **API 兼容层**: -- **API 形状** — 类名、方法签名、常量值完全对齐 Android SDK -- **系统级行为** — 在 Linux 上用真实系统调用实现 -- **框架级行为** — Activity 生命周期、View 渲染等保持存根(桌面无对应框架) - -### 渐进式真实化 - -``` -存根 (所有平台) → Linux 真实实现 → macOS 真实实现 → Windows 真实实现 -``` - -每个 API 从"返回硬编码值"逐步演进为"调用真实系统功能"。 - -## 使用场景 - -- **Android 脚本开发** — 在桌面编写和调试依赖 Android API 的 Python 脚本 -- **跨平台工具** — 用 Android API 风格编写跨平台系统工具 -- **学习 Android API** — 不需要 Android Studio 就能探索 API -- **自动化测试** — 在 CI/CD 中测试 Android API 调用逻辑 -- **IoT / 嵌入式** — 在 Linux 设备上用 Android API 风格操作硬件 - -## 贡献 - -欢迎贡献!特别需要: - -- macOS 真实实现层 (`_macos_impl.py`) -- 更多 API 的真实行为实现 -- 测试用例 - -## License - -[MIT](LICENSE) diff --git a/android_pyapi.egg-info/SOURCES.txt b/android_pyapi.egg-info/SOURCES.txt deleted file mode 100644 index e585b57..0000000 --- a/android_pyapi.egg-info/SOURCES.txt +++ /dev/null @@ -1,230 +0,0 @@ -LICENSE -README.md -setup.py -android_pyapi/__init__.py -android_pyapi/_core.py -android_pyapi/_linux_impl.py -android_pyapi/_windows_impl.py -android_pyapi.egg-info/PKG-INFO -android_pyapi.egg-info/SOURCES.txt -android_pyapi.egg-info/dependency_links.txt -android_pyapi.egg-info/requires.txt -android_pyapi.egg-info/top_level.txt -android_pyapi/android/__init__.py -android_pyapi/android/accessibilityservice.py -android_pyapi/android/accounts.py -android_pyapi/android/animation.py -android_pyapi/android/app.py -android_pyapi/android/app_actionbar.py -android_pyapi/android/app_admin.py -android_pyapi/android/app_assist.py -android_pyapi/android/app_backup.py -android_pyapi/android/app_dialog.py -android_pyapi/android/app_fragment.py -android_pyapi/android/app_job.py -android_pyapi/android/app_notification.py -android_pyapi/android/app_people.py -android_pyapi/android/app_role.py -android_pyapi/android/app_search.py -android_pyapi/android/app_slice.py -android_pyapi/android/app_tasks.py -android_pyapi/android/app_time.py -android_pyapi/android/app_usage.py -android_pyapi/android/app_voiceinteractor.py -android_pyapi/android/app_wallpaper.py -android_pyapi/android/appwidget.py -android_pyapi/android/bluetooth.py -android_pyapi/android/bluetooth_le.py -android_pyapi/android/companion.py -android_pyapi/android/companion_ext.py -android_pyapi/android/content.py -android_pyapi/android/content_intent.py -android_pyapi/android/content_om.py -android_pyapi/android/content_pm.py -android_pyapi/android/content_res.py -android_pyapi/android/content_sync.py -android_pyapi/android/credentials.py -android_pyapi/android/database.py -android_pyapi/android/database_ext.py -android_pyapi/android/database_sqlite.py -android_pyapi/android/drm.py -android_pyapi/android/gesture.py -android_pyapi/android/gesture_ext.py -android_pyapi/android/graphics.py -android_pyapi/android/graphics_drawable.py -android_pyapi/android/graphics_drawable_ext.py -android_pyapi/android/graphics_drawable_shapes.py -android_pyapi/android/graphics_fonts.py -android_pyapi/android/graphics_pdf.py -android_pyapi/android/graphics_surface.py -android_pyapi/android/graphics_text.py -android_pyapi/android/hardware.py -android_pyapi/android/hardware_biometrics.py -android_pyapi/android/hardware_camera2.py -android_pyapi/android/hardware_camera2_params.py -android_pyapi/android/hardware_consumerir.py -android_pyapi/android/hardware_display.py -android_pyapi/android/hardware_fingerprint.py -android_pyapi/android/hardware_hdmi.py -android_pyapi/android/hardware_input.py -android_pyapi/android/hardware_light.py -android_pyapi/android/hardware_location.py -android_pyapi/android/hardware_radio.py -android_pyapi/android/hardware_sensor_ext.py -android_pyapi/android/hardware_serial.py -android_pyapi/android/hardware_thermal.py -android_pyapi/android/hardware_usb.py -android_pyapi/android/inputmethod.py -android_pyapi/android/inputmethodservice.py -android_pyapi/android/location.py -android_pyapi/android/location_ext.py -android_pyapi/android/location_geofence.py -android_pyapi/android/managers.py -android_pyapi/android/media.py -android_pyapi/android/media_async.py -android_pyapi/android/media_audio.py -android_pyapi/android/media_audiofx.py -android_pyapi/android/media_browse.py -android_pyapi/android/media_capture.py -android_pyapi/android/media_effect.py -android_pyapi/android/media_face.py -android_pyapi/android/media_image.py -android_pyapi/android/media_midi.py -android_pyapi/android/media_projection.py -android_pyapi/android/media_remoteblob.py -android_pyapi/android/media_session.py -android_pyapi/android/media_tv.py -android_pyapi/android/media_tv_ext.py -android_pyapi/android/mtp.py -android_pyapi/android/net.py -android_pyapi/android/net_ethernet.py -android_pyapi/android/net_ext.py -android_pyapi/android/net_hotspot2.py -android_pyapi/android/net_http.py -android_pyapi/android/net_nsd.py -android_pyapi/android/net_rtp.py -android_pyapi/android/net_sip.py -android_pyapi/android/net_ssl.py -android_pyapi/android/net_vpn.py -android_pyapi/android/net_wifi.py -android_pyapi/android/net_wifi_aware.py -android_pyapi/android/net_wifi_rtt.py -android_pyapi/android/nfc.py -android_pyapi/android/nfc_ext.py -android_pyapi/android/opengl.py -android_pyapi/android/os.py -android_pyapi/android/os_health.py -android_pyapi/android/os_power.py -android_pyapi/android/os_storage.py -android_pyapi/android/os_user.py -android_pyapi/android/permission.py -android_pyapi/android/preference.py -android_pyapi/android/print.py -android_pyapi/android/printservice.py -android_pyapi/android/provider.py -android_pyapi/android/provider_calendar.py -android_pyapi/android/provider_contacts.py -android_pyapi/android/provider_settings.py -android_pyapi/android/renderscript.py -android_pyapi/android/se.py -android_pyapi/android/security.py -android_pyapi/android/service_autofill.py -android_pyapi/android/service_controls.py -android_pyapi/android/service_dreams.py -android_pyapi/android/service_media.py -android_pyapi/android/service_notification.py -android_pyapi/android/service_quicksettings.py -android_pyapi/android/service_voice.py -android_pyapi/android/service_vr.py -android_pyapi/android/service_wallpaper.py -android_pyapi/android/sip.py -android_pyapi/android/speech.py -android_pyapi/android/speech_tts.py -android_pyapi/android/system.py -android_pyapi/android/telecom.py -android_pyapi/android/telephony.py -android_pyapi/android/telephony_cdma.py -android_pyapi/android/telephony_data.py -android_pyapi/android/telephony_euicc.py -android_pyapi/android/telephony_gsm.py -android_pyapi/android/telephony_ims.py -android_pyapi/android/telephony_mbms.py -android_pyapi/android/test.py -android_pyapi/android/text.py -android_pyapi/android/text_format.py -android_pyapi/android/text_method.py -android_pyapi/android/text_style.py -android_pyapi/android/text_util.py -android_pyapi/android/transition.py -android_pyapi/android/util.py -android_pyapi/android/uwb.py -android_pyapi/android/view.py -android_pyapi/android/view_accessibility.py -android_pyapi/android/view_accessibility_ext.py -android_pyapi/android/view_animation.py -android_pyapi/android/view_animator.py -android_pyapi/android/view_autofill.py -android_pyapi/android/view_dragndrop.py -android_pyapi/android/view_ext.py -android_pyapi/android/view_gesture.py -android_pyapi/android/view_haptic.py -android_pyapi/android/view_inputmethod.py -android_pyapi/android/view_textclassifier.py -android_pyapi/android/view_textservice.py -android_pyapi/android/view_translation.py -android_pyapi/android/view_window.py -android_pyapi/android/webkit.py -android_pyapi/android/webkit_ext.py -android_pyapi/android/widget.py -android_pyapi/android/widget_adapter.py -android_pyapi/android/widget_ext.py -android_pyapi/dalvik/__init__.py -android_pyapi/dalvik/system.py -android_pyapi/java/__init__.py -android_pyapi/java/beans.py -android_pyapi/java/crypto.py -android_pyapi/java/io.py -android_pyapi/java/io_ext.py -android_pyapi/java/io_file.py -android_pyapi/java/lang.py -android_pyapi/java/lang_annotation.py -android_pyapi/java/lang_invoke.py -android_pyapi/java/lang_management.py -android_pyapi/java/lang_ref.py -android_pyapi/java/lang_reflect.py -android_pyapi/java/math.py -android_pyapi/java/net.py -android_pyapi/java/nio.py -android_pyapi/java/nio_byteorder.py -android_pyapi/java/nio_channels.py -android_pyapi/java/nio_charset.py -android_pyapi/java/nio_file.py -android_pyapi/java/rmi.py -android_pyapi/java/security.py -android_pyapi/java/security_cert.py -android_pyapi/java/security_spec.py -android_pyapi/java/sql.py -android_pyapi/java/text.py -android_pyapi/java/time.py -android_pyapi/java/util.py -android_pyapi/java/util_concurrent.py -android_pyapi/java/util_concurrent_atomic.py -android_pyapi/java/util_concurrent_locks.py -android_pyapi/java/util_function.py -android_pyapi/java/util_logging.py -android_pyapi/java/util_prefs.py -android_pyapi/java/util_regex.py -android_pyapi/java/util_stream.py -android_pyapi/java/util_zip.py -android_pyapi/javax/__init__.py -android_pyapi/javax/crypto.py -android_pyapi/javax/net.py -android_pyapi/javax/security.py -android_pyapi/javax/sql.py -android_pyapi/org/__init__.py -android_pyapi/org/json.py -android_pyapi/org/w3c_dom.py -android_pyapi/org/xml_pull.py -android_pyapi/org/xml_sax.py -android_pyapi/org/apache/__init__.py -android_pyapi/org/apache/http.py \ No newline at end of file diff --git a/android_pyapi.egg-info/dependency_links.txt b/android_pyapi.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/android_pyapi.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/android_pyapi.egg-info/requires.txt b/android_pyapi.egg-info/requires.txt deleted file mode 100644 index e01aae4..0000000 --- a/android_pyapi.egg-info/requires.txt +++ /dev/null @@ -1,26 +0,0 @@ - -[dev] -pytest -pytest-cov - -[linux] -psutil -requests -Pillow -opencv-python -numpy - -[windows] -psutil -requests -Pillow -opencv-python -numpy -pyautogui -pyperclip -screeninfo -pywin32 -wmi -pycaw -comtypes -win10toast diff --git a/android_pyapi.egg-info/top_level.txt b/android_pyapi.egg-info/top_level.txt deleted file mode 100644 index f6b9637..0000000 --- a/android_pyapi.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -android_pyapi diff --git a/android_pyapi/__init__.py b/android_pyapi/__init__.py deleted file mode 100644 index 183018d..0000000 --- a/android_pyapi/__init__.py +++ /dev/null @@ -1,604 +0,0 @@ -""" -Android-pyApi — Android API 全面 Python 翻译 -============================================== -方法签名完全对齐 Android SDK,跨平台运行于 Windows/macOS/Linux。 - -用法: - from android_pyapi import Intent, Context, Uri, Bitmap, View - from android_pyapi.java.io import File, FileInputStream - from android_pyapi.java.util import ArrayList, HashMap - from android_pyapi.org.json import JSONObject, JSONArray - -设计原则: - 1. 方法名、参数名、返回类型完全对齐 Android SDK - 2. 能桌面实现的 → 真实实现 (基于 PIL/sqlite3/socket 等) - 3. 无法桌面实现的 → 返回合理默认值或抛出 AndroidOnlyError - 4. 所有常量值与 Android SDK 一致 -""" - -from ._core import ( - AndroidOnlyError, - Stub, - android_context, - get_context, - _sdcard_root, - _app_data_dir, - _package_name, -) - -# android.* 包 -from .android.app import * -from .android.content import * -from .android.content_pm import * -from .android.os import * -from .android.view import * -from .android.view_accessibility import * -from .android.view_animation import * -from .android.view_inputmethod import * -from .android.widget import * -from .android.graphics import * -from .android.graphics_drawable import * -from .android.graphics_pdf import * -from .android.net import * -from .android.net_wifi import * -from .android.media import * -from .android.hardware import * -from .android.hardware_camera2 import * -from .android.hardware_biometrics import * -from .android.location import * -from .android.database import * -from .android.database_sqlite import * -from .android.provider import * -from .android.telephony import * -from .android.bluetooth import * -from .android.bluetooth_le import * -from .android.text import * -from .android.util import * -from .android.preference import * -from .android.webkit import * -from .android.nfc import * -from .android.speech_tts import * -from .android.inputmethod import * -from .android.app_admin import * -from .android.app_backup import * -from .android.app_job import * -from .android.app_people import * -from .android.app_role import * -from .android.app_usage import * -from .android.appwidget import * -from .android.companion import * -from .android.credentials import * -from .android.hardware_display import * -from .android.hardware_fingerprint import * -from .android.hardware_input import * -from .android.hardware_usb import * -from .android.media_audiofx import * -from .android.media_browse import * -from .android.media_midi import * -from .android.media_projection import * -from .android.media_session import * -from .android.media_tv import * -from .android.mtp import * -from .android.opengl import * -from .android.print import * -from .android.renderscript import * -from .android.se import * -from .android.security import * -from .android.service_autofill import * -from .android.service_controls import * -from .android.service_dreams import * -from .android.service_notification import * -from .android.service_quicksettings import * -from .android.service_vr import * -from .android.service_wallpaper import * -from .android.sip import * -from .android.speech import * -from .android.system import * -from .android.telecom import * -from .android.test import * -from .android.transition import * -from .android.uwb import * -from .android.view_autofill import * -from .android.view_textclassifier import * -from .android.view_translation import * -from .android.content_res import * -from .android.accounts import * -from .android.animation import * -from .android.permission import * -from .android.accessibilityservice import * -from .android.os_storage import * -from .android.os_health import * -from .android.os_user import * -from .android.app_slice import * -from .android.app_time import * -from .android.hardware_radio import * -from .android.hardware_consumerir import * -from .android.hardware_thermal import * -from .android.location_ext import * -from .android.net_ssl import * -from .android.net_wifi_aware import * -from .android.text_method import * -from .android.text_style import * -from .android.text_format import * -from .android.text_util import * -from .android.graphics_fonts import * -from .android.graphics_drawable_ext import * -from .android.view_textservice import * -from .android.net_rtp import * -from .android.net_vpn import * -from .android.net_wifi_rtt import * -from .android.net_hotspot2 import * -from .android.app_assist import * -from .android.app_search import * -from .android.gesture import * -from .android.nfc_ext import * -from .android.telephony_mbms import * -from .android.telephony_ims import * -from .android.telephony_euicc import * -from .android.telephony_data import * -from .android.telephony_gsm import * -from .android.telephony_cdma import * -from .android.printservice import * -from .android.service_voice import * -from .android.service_media import * -from .android.inputmethodservice import * -from .android.hardware_camera2_params import * -from .android.graphics_text import * -from .android.graphics_drawable_shapes import * -from .android.graphics_surface import * -from .android.database_ext import * -from .android.drm import * -from .android.app_notification import * -from .android.app_fragment import * -from .android.app_dialog import * -from .android.app_actionbar import * -from .android.app_wallpaper import * -from .android.app_voiceinteractor import * -from .android.content_sync import * -from .android.content_om import * -from .android.managers import * -from .android.net_nsd import * -from .android.net_sip import * -from .android.net_http import * -from .android.os_power import * -from .android.hardware_light import * -from .android.hardware_hdmi import * -from .android.media_effect import * -from .android.media_face import * -from .android.media_async import * -from .android.media_image import * -from .android.media_capture import * -from .android.view_ext import * -from .android.widget_ext import * -from .android.view_accessibility_ext import * -from .android.view_dragndrop import * -from .android.view_gesture import * -from .android.view_haptic import * -from .android.webkit_ext import * -from .android.location_geofence import * -from .android.provider_contacts import * -from .android.provider_calendar import * -from .android.provider_settings import * -from .android.media_tv_ext import * -from .android.media_audio import * -from .android.media_remoteblob import * -from .android.net_ext import * -from .android.net_ethernet import * -from .android.hardware_sensor_ext import * -from .android.hardware_location import * -from .android.hardware_serial import * -from .android.app_tasks import * -from .android.companion_ext import * -from .android.view_animator import * -from .android.view_window import * -from .android.widget_adapter import * -from .android.content_intent import * -from .android.gesture_ext import * - -# java.* 包 -from .java.io import * -from .java.net import * -from .java.util import * -from .java.util_regex import * -from .java.util_zip import * -from .java.util_concurrent import * -from .java.lang import * -from .java.math import * -from .java.text import * -from .java.time import * -from .java.nio import * -from .java.nio_file import * -from .java.security import * -from .java.crypto import * -from .java.lang_ref import * -from .java.lang_reflect import * -from .java.lang_annotation import * -from .java.nio_channels import * -from .java.nio_charset import * -from .java.security_cert import * -from .java.security_spec import * -from .java.sql import * -from .java.beans import * -from .java.rmi import * -from .java.util_function import * -from .java.util_logging import * -from .java.util_stream import * -from .java.util_prefs import * -from .java.lang_invoke import * -from .java.lang_management import * -from .java.nio_byteorder import * -from .java.io_ext import * -from .java.io_file import * -from .java.util_concurrent_atomic import * -from .java.util_concurrent_locks import * - -# javax.* 包 -from .javax.net import * -from .javax.crypto import * -from .javax.security import * -from .javax.sql import * - -# org.* 包 -from .org.json import * -from .org.w3c_dom import * -from .org.xml_sax import * -from .org.xml_pull import * -from .org.apache.http import * - -# dalvik.* 包 -from .dalvik.system import * - -# Base64 (标准 Android API) -import base64 as _base64 - -class Base64: - DEFAULT = 0 - NO_PADDING = 1 - NO_WRAP = 2 - CRLF = 4 - URL_SAFE = 8 - NO_CLOSE = 16 - - @staticmethod - def encodeToString(input_data, flags=0): - if isinstance(input_data, str): - input_data = input_data.encode('utf-8') - return _base64.b64encode(input_data).decode('ascii') - - @staticmethod - def decode(str_val, flags=0): - return _base64.b64decode(str_val) - - @staticmethod - def encode(input_data, flags=0): - if isinstance(input_data, str): - input_data = input_data.encode('utf-8') - return _base64.b64encode(input_data) - -# Scanner (java.util) -class Scanner: - def __init__(self, source): - if isinstance(source, str): - self._source = source - else: - self._source = str(source) - self._pos = 0 - - def next(self): - while self._pos < len(self._source) and self._source[self._pos].isspace(): - self._pos += 1 - start = self._pos - while self._pos < len(self._source) and not self._source[self._pos].isspace(): - self._pos += 1 - if start == self._pos: - raise Exception("NoSuchElementException") - return self._source[start:self._pos] - - def hasNext(self): - pos = self._pos - while pos < len(self._source) and self._source[pos].isspace(): - pos += 1 - return pos < len(self._source) - - def nextInt(self): - return int(self.next()) - - def close(self): - pass - -# Timer (java.util) -class Timer: - def __init__(self, name=None, daemon=False): - import threading - self._thread = None - self._tasks = [] - - def schedule(self, task, delay=None, period=None): - import threading - if delay is None: - self._run_once(task) - elif period is None: - self._run_delayed(task, delay) - else: - self._run_periodic(task, delay, period) - - def _run_once(self, task): - t = threading.Thread(target=task.run) - t.daemon = True - self._tasks.append(t) - t.start() - - def _run_delayed(self, task, delay_ms): - import threading - def _run(): - import time - time.sleep(delay_ms / 1000.0) - task.run() - t = threading.Thread(target=_run) - t.daemon = True - self._tasks.append(t) - t.start() - - def _run_periodic(self, task, delay_ms, period_ms): - import threading - def _run(): - import time - time.sleep(delay_ms / 1000.0) - while True: - task.run() - time.sleep(period_ms / 1000.0) - t = threading.Thread(target=_run) - t.daemon = True - self._tasks.append(t) - t.start() - - def cancel(self): - pass - - def purge(self): - return 0 - - -class TimerTask: - def __init__(self): - self._cancelled = False - - def run(self): - pass - - def cancel(self): - self._cancelled = True - - def scheduledExecutionTime(self): - return 0 - -__version__ = "1.0.0" -__all__ = [ - # android — 核心 - "AccessibilityNodeInfo", "AccessibilityService", - "AccessibilityServiceInfo", - "Account", "AccountManager", - "Activity", "ActivityManager", - "Fragment", "FragmentManager", "FragmentTransaction", - "Dialog", "AlertDialog", "ProgressDialog", - "DatePickerDialog", "TimePickerDialog", - "Service", "Application", - "PendingIntent", "ComponentName", "AlarmManager", - "NotificationChannel", "NotificationChannelGroup", - "NotificationCompat", "TaskStackBuilder", - "AlphaAnimation", - "Animation", - "Animator", "ValueAnimator", "ObjectAnimator", "AnimatorSet", - "AppWidgetManager", "AppWidgetProvider", "AppWidgetProviderInfo", - "AssistManager", "AssistStructure", - "AudioManager", - "AutofillManager", "AutofillValue", - "BackupAgent", "BackupManager", - "Base64", - "BatteryManager", - "BiometricPrompt", - "Bitmap", "BitmapDrawable", "BitmapFactory", - "Camera", - "Region", "PathMeasure", - "NinePatch", "Picture", "PictureDrawable", - "PorterDuff", "PorterDuffXfermode", - "Shader", "LinearGradient", "RadialGradient", "SweepGradient", - "BitmapShader", "ComposeShader", - "CornerPathEffect", "DashPathEffect", "PathDashPathEffect", - "SumPathEffect", - "BluetoothAdapter", "BluetoothDevice", - "BroadcastReceiver", - "Build", - "Bundle", - "Button", - "CalendarContract", - "Call", - "CameraManager", - "Canvas", - "CheckBox", - "Color", - "CompanionDeviceManager", - "ConnectivityManager", - "ConsumerIrManager", - "ContentResolver", - "ContentValues", - "ContactsContract", - "Context", - "CredentialManager", - "Cursor", - "DevicePolicyManager", - "DisplayManager", - "Drawable", - "EditText", - "Environment", - "FingerprintManager", - "Geocoder", - "Gesture", "GestureOverlayView", "GestureLibrary", - "Handler", - "Html", - "ImageView", - "InputDevice", - "KeyEvent", "MotionEvent", - "GestureDetector", "ScaleGestureDetector", - "SurfaceHolder", "Surface", "SurfaceView", "TextureView", - "ViewOutlineProvider", - "WindowManager", "Window", - "LayoutInflater", "Menu", "MenuItem", "ContextMenu", - "Display", "DisplayMetrics", - "InputManager", - "InputMethodManager", - "Intent", - "IntentFilter", - "JobInfo", "JobScheduler", "JobService", - "KeyChain", - "ListView", - "Location", "LocationManager", - "Log", - "Looper", - "Matrix", - "MediaBrowser", "MediaController", - "MediaPlayer", - "MediaProjectionManager", - "MediaRecorder", - "MediaMetadataRetriever", - "MediaSession", - "MediaStore", - "Message", - "MidiDevice", "MidiManager", - "MtpDevice", - "NfcAdapter", "NdefMessage", "NdefRecord", "Tag", - "NetworkInfo", - "Os", "OsConstants", - "PackageInfo", "PackageManager", - "Paint", - "PdfRenderer", - "PeopleManager", - "PermissionManager", - "PowerManager", - "PrintAttributes", "PrintDocumentAdapter", "PrintManager", - "LinearLayout", "FrameLayout", "RelativeLayout", - "TableLayout", "TableRow", - "BaseAdapter", "ArrayAdapter", "SimpleAdapter", "CursorAdapter", - "ImageButton", "ToggleButton", "RadioButton", "SeekBar", "RatingBar", - "ViewSwitcher", "TextSwitcher", "ImageSwitcher", - "SearchView", "NumberPicker", "Chronometer", - "TabHost", "TabWidget", "ViewAnimator", - "ZoomControls", "DigitalClock", "AnalogClock", - "ProgressBar", - "RadioManager", "RadioTuner", - "Rect", "RectF", - "RecyclerView", - "Resources", "AssetManager", - "RoleManager", - "ScrollView", - "SearchManager", "SearchableInfo", - "SensorManager", - "Settings", - "SharedPreferences", - "Slice", "SliceManager", "SliceProvider", - "SmsManager", - "SpannableString", - "SpannableStringBuilder", - "SpeechRecognizer", - "Spinner", - "SQLiteDatabase", "SQLiteOpenHelper", "SQLiteStatement", - "MatrixCursor", - "StorageManager", "StorageVolume", - "StructStat", - "Switch", - "TelecomManager", - "TelephonyManager", - "TextClassifier", - "TextToSpeech", - "TextUtils", - "TextView", - "ThermalManager", - "TimeManager", - "Toast", - "TrafficStats", - "TranslateAnimation", - "TranslationManager", - "TvInputManager", - "Typeface", - "TypedValue", - "Uri", - "UsageStatsManager", - "UsbDevice", "UsbManager", - "UserManager", "UserHandle", - "UwbManager", - "View", "ViewGroup", - "VirtualDisplay", - "Vibrator", "VibratorEffect", - "VpnManager", "VpnService", - "WebView", - "WifiAwareManager", - "WifiManager", - "WifiRttManager", - # java - "File", "FileInputStream", "FileOutputStream", - "BufferedReader", "BufferedWriter", - "InputStreamReader", "OutputStreamWriter", - "URL", "HttpURLConnection", "Socket", "ServerSocket", - "NetworkInterface", "InetAddress", "InetSocketAddress", "InterfaceAddress", - "ArrayList", "HashMap", "HashSet", "LinkedList", - "TreeMap", "LinkedHashMap", "PriorityQueue", - "Collections", "Arrays", "Calendar", "Date", "Random", - "UUID", "Scanner", "Timer", "TimerTask", - "Pattern", "Matcher", - "ZipFile", "GZIPInputStream", "GZIPOutputStream", - "ExecutorService", "Executors", "Future", "Callable", - "String", "StringBuilder", "StringBuffer", - "Integer", "Math", "System", "Thread", - "BigInteger", "BigDecimal", - # Java Exception 体系 - "Throwable", "Exception", "RuntimeException", "Error", - "NullPointerException", "IllegalArgumentException", "IllegalStateException", - "IndexOutOfBoundsException", "ArrayIndexOutOfBoundsException", - "StringIndexOutOfBoundsException", "ClassCastException", - "UnsupportedOperationException", "ArithmeticException", - "NumberFormatException", "SecurityException", - "ConcurrentModificationException", "NegativeArraySizeException", - "NoSuchFieldException", "NoSuchMethodException", "InterruptedException", - "CloneNotSupportedException", "IllegalMonitorStateException", - "IllegalThreadStateException", "EnumConstantNotPresentException", - "TypeNotPresentException", - # Java Exception 体系 (扩展) - "ClassNotFoundException", "IllegalAccessException", - "InstantiationException", "InvocationTargetException", - "NoSuchElementException", "AssertionError", - "OutOfMemoryError", "StackOverflowError", "NoClassDefFoundError", - "UnsatisfiedLinkError", "LinkageError", "VerifyError", - "AbstractMethodError", "IncompatibleClassChangeError", - "ExceptionInInitializerError", - "SimpleDateFormat", "DecimalFormat", - "LocalDateTime", "LocalDate", "LocalTime", "Instant", "Duration", "Period", - "ChronoUnit", "ChronoField", "DayOfWeek", "Month", "ZoneId", "ZoneOffset", - "DateTimeFormatter", - "ByteBuffer", "Files", "Path", "Paths", - "MessageDigest", "SecureRandom", - "Cipher", "Mac", "KeyGenerator", - "SecretKey", "SecretKeySpec", - "Reference", "SoftReference", "WeakReference", "PhantomReference", - "Field", "Method", "Constructor", "Proxy", "Modifier", - "Annotation", "Retention", "Target", "RetentionPolicy", "ElementType", - "Channel", "Selector", "SelectionKey", "SocketChannel", "ServerSocketChannel", - "FileChannel", "DatagramChannel", - "Charset", "CharsetEncoder", "CharsetDecoder", "StandardCharsets", - "CoderResult", - # javax - "SSLSocketFactory", "SSLServerSocketFactory", - "SSLSocket", "SSLServerSocket", "HttpsURLConnection", - "TrustManager", "X509TrustManager", "KeyManager", "X509KeyManager", - "Cipher", "KeyGenerator", "Mac", "SecretKey", "SecretKeySpec", - "Subject", "Principal", "Policy", - "DataSource", "RowSet", "CachedRowSet", "RowSetListener", - # org - "JSONObject", "JSONArray", "JSONException", - "Document", "Node", "Element", "Attr", "Text", "NodeList", - "XMLReader", "ContentHandler", "InputSource", "SAXException", - "XmlPullParser", "XmlSerializer", "XmlPullParserFactory", - "HttpResponse", "HttpEntity", "HttpClient", "DefaultHttpClient", - "HttpGet", "HttpPost", "HttpPut", "HttpDelete", - # dalvik - "DexClassLoader", "PathClassLoader", "DexFile", -] diff --git a/android_pyapi/_core.py b/android_pyapi/_core.py deleted file mode 100644 index d8b2990..0000000 --- a/android_pyapi/_core.py +++ /dev/null @@ -1,186 +0,0 @@ -""" -Android-pyApi 核心基础设施 -""" - -import os -import sys -import platform -import tempfile -import json -import hashlib -import random -import time -from typing import Any, Dict, List, Optional, Callable - - -class AndroidOnlyError(Exception): - """当调用仅 Android 可用的 API 时抛出""" - pass - - -class Stub: - """ - 通用 Android API 存根基类。 - 子类可以覆盖具体方法实现真实逻辑; - 未覆盖的方法调用会返回合理默认值。 - """ - - _android_only = False - - def __init__(self, *args, **kwargs): - pass - - def __getattr__(self, name: str): - if name.startswith("_"): - raise AttributeError(name) - - def _stub_method(*args, **kwargs): - if self._android_only: - raise AndroidOnlyError( - f"{self.__class__.__name__}.{name}() 仅在 Android 设备上可用" - ) - return None - - return _stub_method - - def toString(self) -> str: - return f"{self.__class__.__name__}@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - def getClass(self) -> type: - return self.__class__ - - -# ═══════════════════════════════════════════════════════════════ -# 全局路径和环境 -# ═══════════════════════════════════════════════════════════════ - -_package_name = "com.ai.assistance.operit" -_sdcard_root = os.path.join(os.path.expanduser("~"), "Android-compat", "sdcard") -_app_data_dir = os.path.join(os.path.expanduser("~"), "Android-compat", "app-data") - - -def _ensure_dirs(): - dirs = [ - _sdcard_root, - os.path.join(_sdcard_root, "Download"), - os.path.join(_sdcard_root, "DCIM"), - os.path.join(_sdcard_root, "DCIM", "Screenshots"), - os.path.join(_sdcard_root, "Documents"), - os.path.join(_sdcard_root, "Music"), - os.path.join(_sdcard_root, "Movies"), - os.path.join(_sdcard_root, "Pictures"), - os.path.join(_sdcard_root, "Android", "data"), - os.path.join(_sdcard_root, "Android", "data", _package_name, "files"), - os.path.join(_sdcard_root, "Android", "data", _package_name, "cache"), - _app_data_dir, - os.path.join(_app_data_dir, "files"), - os.path.join(_app_data_dir, "cache"), - os.path.join(_app_data_dir, "shared_prefs"), - ] - for d in dirs: - os.makedirs(d, exist_ok=True) - - -_ensure_dirs() - - -def resolve_android_path(path: str) -> str: - """将 Android 路径解析为桌面路径""" - if path.startswith("/sdcard"): - return os.path.join(_sdcard_root, path[7:].lstrip("/")) - if path.startswith("/data/data/"): - return os.path.join(_app_data_dir, path[11:].lstrip("/")) - if path.startswith("content://"): - # 简单解析 content URI - from urllib.parse import urlparse - parsed = urlparse(path) - return parsed.path or path - if path.startswith("file://"): - from urllib.parse import unquote - return unquote(path[7:]) - return path - - -def to_android_path(desktop_path: str) -> str: - """将桌面路径映射回 Android 路径""" - if desktop_path.startswith(_sdcard_root): - rel = os.path.relpath(desktop_path, _sdcard_root) - return f"/sdcard/{rel}" - if desktop_path.startswith(_app_data_dir): - rel = os.path.relpath(desktop_path, _app_data_dir) - return f"/data/data/{_package_name}/{rel}" - return desktop_path - - -# ═══════════════════════════════════════════════════════════════ -# 全局 Context 单例 -# ═══════════════════════════════════════════════════════════════ - -class _GlobalContext: - """全局应用上下文占位符,在 android.content 模块中被替换为真实 Context""" - - @property - def packageName(self): - return _package_name - - def getPackageName(self): - return _package_name - - def getFilesDir(self): - return os.path.join(_app_data_dir, "files") - - def getCacheDir(self): - return os.path.join(_app_data_dir, "cache") - - def getExternalFilesDir(self, type_=None): - base = os.path.join(_sdcard_root, "Android", "data", _package_name, "files") - if type_: - base = os.path.join(base, type_) - os.makedirs(base, exist_ok=True) - return base - - def getExternalStorageDirectory(self): - return _sdcard_root - - def getSystemService(self, name): - return None # 由具体模块填充 - - -android_context = _GlobalContext() - - -def get_context(): - """获取全局 Android Context""" - return android_context - - -# ═══════════════════════════════════════════════════════════════ -# 可选依赖检测 -# ═══════════════════════════════════════════════════════════════ - -def _check_optional(name: str) -> bool: - """检查可选依赖是否可用""" - try: - __import__(name) - return True - except ImportError: - return False - - -HAS_PIL = _check_optional("PIL") -HAS_REQUESTS = _check_optional("requests") -HAS_HTTPX = _check_optional("httpx") -HAS_PYAUTOGUI = _check_optional("pyautogui") -HAS_SCREENINFO = _check_optional("screeninfo") -HAS_PSUTIL = _check_optional("psutil") -HAS_PYMUPDF = _check_optional("fitz") -HAS_PDF2IMAGE = _check_optional("pdf2image") -HAS_CRYPTOGRAPHY = _check_optional("cryptography") -HAS_PYPERCLIP = _check_optional("pyperclip") -HAS_JPYPE = _check_optional("jpype") diff --git a/android_pyapi/_linux_impl.py b/android_pyapi/_linux_impl.py deleted file mode 100644 index d9c30c7..0000000 --- a/android_pyapi/_linux_impl.py +++ /dev/null @@ -1,1266 +0,0 @@ -""" -Linux 真实实现层 -================ -在 Linux 环境下,将 Android API 的存根行为替换为真实的系统调用。 -使 Android-pyApi 从"存根库"变为"可用的生产力工具"。 - -覆盖能力: - - 网络: ip 命令, /proc/net, socket - - 蓝牙: bluetoothctl / dbus - - 传感器: /sys/class/hwmon, /sys/class/thermal, sensors - - 音频: amixer / ALSA - - 剪贴板: xclip - - 电源/电池: /sys/class/power_supply, psutil - - 位置: IP 地理定位 (requests) - - 通知: notify-send - - Shell: subprocess - - 显示: xdotool, X11 - - UI自动化: xdotool - - 摄像头: opencv (V4L2) - - 进程管理: psutil, /proc - - 文件: 原生 Python os - - 媒体: ffmpeg/ffprobe -""" - -from __future__ import annotations - -import os -import sys -import re -import json -import time -import socket -import struct -import shutil -import subprocess -import platform -import hashlib -from pathlib import Path -from datetime import datetime -from typing import Any, Dict, List, Optional, Tuple - -# ═══════════════════════════════════════════════════════════════ -# 能力检测 -# ═══════════════════════════════════════════════════════════════ - -IS_LINUX = platform.system() == "Linux" - -def _which(name: str) -> Optional[str]: - return shutil.which(name) - -HAS_IP = _which("ip") is not None -HAS_BLUETOOTHCTL = _which("bluetoothctl") is not None -HAS_AMIXER = _which("amixer") is not None -HAS_XCLIP = _which("xclip") is not None -HAS_XDOTOOL = _which("xdotool") is not None -HAS_NOTIFY_SEND = _which("notify-send") is not None -HAS_SENSORS = _which("sensors") is not None -HAS_FFMPEG = _which("ffmpeg") is not None -HAS_FFPROBE = _which("ffprobe") is not None -HAS_XRANDR = _which("xrandr") is not None - -try: - import psutil - HAS_PSUTIL = True -except ImportError: - HAS_PSUTIL = False - -try: - import requests - HAS_REQUESTS = True -except ImportError: - HAS_REQUESTS = False - -try: - import cv2 - HAS_CV2 = True -except ImportError: - HAS_CV2 = False - -try: - import numpy as np - HAS_NUMPY = True -except ImportError: - HAS_NUMPY = False - -try: - from PIL import Image, ImageDraw, ImageGrab - HAS_PIL = True -except ImportError: - HAS_PIL = False - -try: - import dbus - HAS_DBUS = True -except ImportError: - HAS_DBUS = False - - -def _run(cmd: List[str] or str, timeout: int = 10, capture: bool = True) -> Tuple[int, str, str]: - """执行命令,返回 (exit_code, stdout, stderr)""" - try: - if isinstance(cmd, str): - result = subprocess.run(cmd, shell=True, capture_output=capture, - text=True, timeout=timeout) - else: - result = subprocess.run(cmd, capture_output=capture, - text=True, timeout=timeout) - return result.returncode, result.stdout.strip(), result.stderr.strip() - except subprocess.TimeoutExpired: - return -1, "", "timeout" - except Exception as e: - return -1, "", str(e) - - -def _read_file(path: str) -> Optional[str]: - try: - with open(path, "r") as f: - return f.read().strip() - except Exception: - return None - - -def _read_file_int(path: str) -> Optional[int]: - val = _read_file(path) - if val is not None: - try: - return int(val) - except ValueError: - return None - return None - - -def _list_dir(path: str) -> List[str]: - try: - return os.listdir(path) - except Exception: - return [] - - -# ═══════════════════════════════════════════════════════════════ -# 网络 — 真实实现 -# ═══════════════════════════════════════════════════════════════ - -class LinuxNetwork: - """真实网络信息 — 通过 ip 命令和 /proc/net""" - - @staticmethod - def get_interfaces() -> List[dict]: - """获取网络接口列表""" - if not HAS_IP: - return [] - code, out, _ = _run(["ip", "-j", "addr", "show"]) - if code == 0 and out: - try: - return json.loads(out) - except json.JSONDecodeError: - pass - # 回退到 /proc/net - interfaces = [] - for name in _list_dir("/sys/class/net/"): - state = _read_file(f"/sys/class/net/{name}/operstate") or "unknown" - mac = _read_file(f"/sys/class/net/{name}/address") or "" - interfaces.append({"ifname": name, "operstate": state, "address": mac}) - return interfaces - - @staticmethod - def get_ip_address(interface: str = None) -> str: - """获取IP地址""" - # 优先用 socket 获取外网IP - try: - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - s.connect(("8.8.8.8", 80)) - ip = s.getsockname()[0] - s.close() - return ip - except Exception: - pass - # 回退到 hostname - try: - return socket.gethostbyname(socket.gethostname()) - except Exception: - return "127.0.0.1" - - @staticmethod - def get_wifi_info() -> dict: - """获取WiFi信息""" - # 尝试 /proc/net/wireless - data = _read_file("/proc/net/wireless") - if data: - lines = data.strip().split("\n") - if len(lines) >= 3: - parts = lines[2].split() - if len(parts) >= 7: - return { - "ssid": "", - "interface": parts[0].rstrip(":"), - "status": parts[1], - "quality": float(parts[2]), - "signal": float(parts[3]), - "noise": float(parts[4]), - } - # 尝试 nmcli - code, out, _ = _run(["nmcli", "-t", "-f", "active,ssid,signal", "dev", "wifi"]) - if code == 0 and out: - for line in out.split("\n"): - if line.startswith("yes:"): - parts = line.split(":") - if len(parts) >= 3: - return {"ssid": parts[1], "signal": int(parts[2])} - return {"ssid": "", "signal": 0} - - @staticmethod - def get_connectivity() -> bool: - """检测网络连通性""" - # 方法1: 尝试 TCP 连接到公共 DNS - for host, port in [("8.8.8.8", 53), ("1.1.1.1", 53), ("114.114.114.114", 53)]: - try: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.settimeout(3) - s.connect((host, port)) - s.close() - return True - except Exception: - continue - # 方法2: 尝试 HTTP 请求 - if HAS_REQUESTS: - try: - requests.get("http://www.baidu.com", timeout=3) - return True - except Exception: - pass - # 方法3: 检查是否有非 lo 接口且状态为 UP - for iface in LinuxNetwork.get_interfaces(): - name = iface.get("ifname", iface.get("name", "")) - state = iface.get("operstate", iface.get("state", "")) - if name and name != "lo" and state in ("up", "UP"): - return True - return False - - @staticmethod - def get_dns_servers() -> List[str]: - """获取DNS服务器""" - data = _read_file("/etc/resolv.conf") - if data: - dns = [] - for line in data.split("\n"): - line = line.strip() - if line.startswith("nameserver"): - parts = line.split() - if len(parts) >= 2: - dns.append(parts[1]) - return dns - return [] - - @staticmethod - def get_gateway() -> str: - """获取默认网关""" - data = _read_file("/proc/net/route") - if data: - for line in data.split("\n")[1:]: - parts = line.split() - if len(parts) >= 3 and parts[1] == "00000000": - # 网关是十六进制小端 - gw_hex = parts[2] - try: - gw_bytes = bytes.fromhex(gw_hex) - gw = socket.inet_ntoa(gw_bytes[::-1]) - return gw - except Exception: - pass - return "" - - @staticmethod - def get_mac_address(interface: str = None) -> str: - """获取MAC地址""" - if interface is None: - ifaces = _list_dir("/sys/class/net/") - if ifaces and "lo" in ifaces: - ifaces.remove("lo") - if ifaces: - interface = ifaces[0] - if interface: - return _read_file(f"/sys/class/net/{interface}/address") or "" - return "" - - @staticmethod - def get_rx_tx_bytes(interface: str = None) -> dict: - """获取网络收发字节数""" - if interface is None: - ifaces = _list_dir("/sys/class/net/") - if "lo" in ifaces: - ifaces.remove("lo") - if ifaces: - interface = ifaces[0] - if interface: - rx = _read_file_int(f"/sys/class/net/{interface}/statistics/rx_bytes") or 0 - tx = _read_file_int(f"/sys/class/net/{interface}/statistics/tx_bytes") or 0 - return {"rx_bytes": rx, "tx_bytes": tx, "interface": interface} - return {"rx_bytes": 0, "tx_bytes": 0, "interface": ""} - - -# ═══════════════════════════════════════════════════════════════ -# 蓝牙 — 真实实现 (bluetoothctl) -# ═══════════════════════════════════════════════════════════════ - -class LinuxBluetooth: - """真实蓝牙操作 — 通过 bluetoothctl""" - - STATE_OFF = 10 - STATE_TURNING_ON = 11 - STATE_ON = 12 - STATE_TURNING_OFF = 13 - - @staticmethod - def get_state() -> int: - if not HAS_BLUETOOTHCTL: - return LinuxBluetooth.STATE_OFF - code, out, _ = _run(["bluetoothctl", "show"]) - if "Powered: yes" in out: - return LinuxBluetooth.STATE_ON - return LinuxBluetooth.STATE_OFF - - @staticmethod - def get_address() -> Optional[str]: - """获取本机蓝牙适配器地址""" - if not HAS_BLUETOOTHCTL: - return None - code, out, _ = _run(["bluetoothctl", "show"]) - if code == 0 and out: - for line in out.split("\n"): - line = line.strip() - if line.startswith("Controller "): - parts = line.split() - if len(parts) >= 2: - return parts[1] - return None - - @staticmethod - def get_name() -> Optional[str]: - """获取本机蓝牙适配器名称 (Alias 优先, 回退到 Controller 行括号内名称)""" - if not HAS_BLUETOOTHCTL: - return None - code, out, _ = _run(["bluetoothctl", "show"]) - if code == 0 and out: - alias = None - controller_name = None - for line in out.split("\n"): - stripped = line.strip() - if stripped.startswith("Controller ") and "(" in stripped: - start = stripped.find("(") - end = stripped.find(")", start) - if start != -1 and end != -1: - controller_name = stripped[start + 1:end] - elif stripped.startswith("Alias:"): - alias = stripped[len("Alias:"):].strip() - if alias: - return alias - if controller_name: - return controller_name - return None - - @staticmethod - def enable() -> bool: - if not HAS_BLUETOOTHCTL: - return False - code, _, _ = _run(["bluetoothctl", "power", "on"]) - return code == 0 - - @staticmethod - def disable() -> bool: - if not HAS_BLUETOOTHCTL: - return False - code, _, _ = _run(["bluetoothctl", "power", "off"]) - return code == 0 - - @staticmethod - def get_bonded_devices() -> List[dict]: - if not HAS_BLUETOOTHCTL: - return [] - code, out, _ = _run(["bluetoothctl", "devices", "Paired"]) - devices = [] - for line in out.split("\n"): - if line.startswith("Device "): - parts = line.split(None, 2) - if len(parts) >= 3: - devices.append({ - "address": parts[1], - "name": parts[2], - "bonded": True, - }) - return devices - - @staticmethod - def scan(timeout: int = 10) -> List[dict]: - if not HAS_BLUETOOTHCTL: - return [] - # 启动扫描 - _run(["bluetoothctl", "scan", "on"], timeout=2) - time.sleep(min(timeout, 5)) - _run(["bluetoothctl", "scan", "off"], timeout=2) - # 获取设备列表 - code, out, _ = _run(["bluetoothctl", "devices"]) - devices = [] - for line in out.split("\n"): - if line.startswith("Device "): - parts = line.split(None, 2) - if len(parts) >= 3: - devices.append({ - "address": parts[1], - "name": parts[2], - }) - return devices - - @staticmethod - def pair(address: str) -> bool: - if not HAS_BLUETOOTHCTL: - return False - code, _, _ = _run(["bluetoothctl", "pair", address], timeout=30) - return code == 0 - - @staticmethod - def connect(address: str) -> bool: - if not HAS_BLUETOOTHCTL: - return False - code, _, _ = _run(["bluetoothctl", "connect", address], timeout=15) - return code == 0 - - @staticmethod - def disconnect(address: str) -> bool: - if not HAS_BLUETOOTHCTL: - return False - code, _, _ = _run(["bluetoothctl", "disconnect", address], timeout=10) - return code == 0 - - @staticmethod - def unpair(address: str) -> bool: - if not HAS_BLUETOOTHCTL: - return False - code, _, _ = _run(["bluetoothctl", "remove", address], timeout=10) - return code == 0 - - -# ═══════════════════════════════════════════════════════════════ -# 传感器 — 真实实现 (/sys/class/hwmon, /sys/class/thermal) -# ═══════════════════════════════════════════════════════════════ - -class LinuxSensors: - """真实传感器数据 — 通过 /sys 文件系统""" - - # Android Sensor Types - TYPE_ACCELEROMETER = 1 - TYPE_GYROSCOPE = 4 - TYPE_LIGHT = 5 - TYPE_PRESSURE = 6 - TYPE_PROXIMITY = 8 - TYPE_TEMPERATURE = 7 - TYPE_RELATIVE_HUMIDITY = 12 - TYPE_AMBIENT_TEMPERATURE = 13 - - @staticmethod - def get_temperatures() -> List[dict]: - """获取温度传感器读数""" - temps = [] - # /sys/class/thermal - for name in _list_dir("/sys/class/thermal/"): - if name.startswith("thermal_zone"): - temp = _read_file_int(f"/sys/class/thermal/{name}/temp") - typ = _read_file(f"/sys/class/thermal/{name}/type") or "" - if temp is not None: - temps.append({ - "name": name, - "type": typ, - "value": temp / 1000.0, # millicelsius -> celsius - "unit": "°C", - }) - # /sys/class/hwmon - for name in _list_dir("/sys/class/hwmon/"): - temp_files = [f for f in _list_dir(f"/sys/class/hwmon/{name}/") - if f.startswith("temp") and f.endswith("_input")] - for tf in temp_files: - temp = _read_file_int(f"/sys/class/hwmon/{name}/{tf}") - label_file = tf.replace("_input", "_label") - label = _read_file(f"/sys/class/hwmon/{name}/{label_file}") or tf - if temp is not None: - temps.append({ - "name": f"{name}/{label}", - "type": "hwmon", - "value": temp / 1000.0, - "unit": "°C", - }) - return temps - - @staticmethod - def get_fan_speeds() -> List[dict]: - """获取风扇转速""" - fans = [] - for name in _list_dir("/sys/class/hwmon/"): - fan_files = [f for f in _list_dir(f"/sys/class/hwmon/{name}/") - if f.startswith("fan") and f.endswith("_input")] - for ff in fan_files: - rpm = _read_file_int(f"/sys/class/hwmon/{name}/{ff}") - label_file = ff.replace("_input", "_label") - label = _read_file(f"/sys/class/hwmon/{name}/{label_file}") or ff - if rpm is not None: - fans.append({"name": label, "rpm": rpm}) - return fans - - @staticmethod - def get_cpu_frequency() -> List[dict]: - """获取CPU频率""" - freqs = [] - for name in _list_dir("/sys/devices/system/cpu/"): - if name.startswith("cpu") and name[3:].isdigit(): - cpu_id = name[3:] - cur = _read_file_int(f"/sys/devices/system/cpu/{name}/cpufreq/scaling_cur_freq") - if cur is not None: - freqs.append({ - "cpu": int(cpu_id), - "freq_mhz": cur / 1000.0, - }) - return freqs - - @staticmethod - def get_light_level() -> Optional[float]: - """获取环境光传感器 (如果有)""" - for name in _list_dir("/sys/class/hwmon/"): - for f in _list_dir(f"/sys/class/hwmon/{name}/"): - if "in" in f and f.endswith("_input"): - val = _read_file_int(f"/sys/class/hwmon/{name}/{f}") - if val is not None: - return val / 1000.0 - return None - - @staticmethod - def get_all_sensors() -> dict: - """获取所有传感器数据""" - return { - "temperatures": LinuxSensors.get_temperatures(), - "fans": LinuxSensors.get_fan_speeds(), - "cpu_freq": LinuxSensors.get_cpu_frequency(), - "light": LinuxSensors.get_light_level(), - } - - -# ═══════════════════════════════════════════════════════════════ -# 音频 — 真实实现 (amixer / ALSA) -# ═══════════════════════════════════════════════════════════════ - -class LinuxAudio: - """真实音频控制 — 通过 amixer""" - - @staticmethod - def get_volume() -> int: - """获取当前音量 (0-100)""" - if not HAS_AMIXER: - return 0 - code, out, _ = _run(["amixer", "sget", "Master"]) - if code == 0: - match = re.search(r'\[(\d+)%\]', out) - if match: - return int(match.group(1)) - return 0 - - @staticmethod - def set_volume(volume: int) -> bool: - """设置音量 (0-100)""" - if not HAS_AMIXER: - return False - volume = max(0, min(100, volume)) - code, _, _ = _run(["amixer", "sset", "Master", f"{volume}%"]) - return code == 0 - - @staticmethod - def get_mute() -> bool: - """获取静音状态""" - if not HAS_AMIXER: - return False - code, out, _ = _run(["amixer", "sget", "Master"]) - if code == 0: - return "[on]" not in out - return False - - @staticmethod - def set_mute(mute: bool) -> bool: - """设置静音""" - if not HAS_AMIXER: - return False - cmd = "mute" if mute else "unmute" - code, _, _ = _run(["amixer", "sset", "Master", cmd]) - return code == 0 - - @staticmethod - def get_audio_devices() -> List[dict]: - """获取音频设备列表""" - devices = [] - if HAS_AMIXER: - code, out, _ = _run(["amixer", "controls"]) - if code == 0: - for line in out.split("\n"): - devices.append({"name": line, "type": "alsa"}) - return devices - - -# ═══════════════════════════════════════════════════════════════ -# 剪贴板 — 真实实现 (xclip) -# ═══════════════════════════════════════════════════════════════ - -class LinuxClipboard: - """真实剪贴板 — 通过 xclip""" - - @staticmethod - def read() -> str: - if HAS_XCLIP: - code, out, _ = _run(["xclip", "-selection", "clipboard", "-o"]) - if code == 0: - return out - # 回退到 tkinter - try: - import tkinter - root = tkinter.Tk() - root.withdraw() - text = root.clipboard_get() - root.destroy() - return text - except Exception: - return "" - - @staticmethod - def write(text: str) -> bool: - if HAS_XCLIP: - try: - proc = subprocess.Popen( - ["xclip", "-selection", "clipboard"], - stdin=subprocess.PIPE, text=True - ) - proc.communicate(text) - return proc.returncode == 0 - except Exception: - pass - # 回退到 tkinter - try: - import tkinter - root = tkinter.Tk() - root.withdraw() - root.clipboard_clear() - root.clipboard_append(text) - root.update() - root.destroy() - return True - except Exception: - return False - - -# ═══════════════════════════════════════════════════════════════ -# 电源/电池 — 真实实现 (/sys/class/power_supply, psutil) -# ═══════════════════════════════════════════════════════════════ - -class LinuxPower: - """真实电源/电池信息""" - - BATTERY_STATUS_UNKNOWN = 1 - BATTERY_STATUS_CHARGING = 2 - BATTERY_STATUS_DISCHARGING = 3 - BATTERY_STATUS_NOT_CHARGING = 4 - BATTERY_STATUS_FULL = 5 - - @staticmethod - def get_battery_info() -> dict: - """获取电池信息""" - info = {"present": False, "level": -1, "status": "unknown", - "temperature": 0, "voltage": 0, "technology": ""} - # /sys/class/power_supply - for name in _list_dir("/sys/class/power_supply/"): - typ = _read_file(f"/sys/class/power_supply/{name}/type") or "" - if "Battery" in typ: - info["present"] = True - info["technology"] = _read_file(f"/sys/class/power_supply/{name}/technology") or "" - capacity = _read_file_int(f"/sys/class/power_supply/{name}/capacity") - if capacity is not None: - info["level"] = capacity - status = _read_file(f"/sys/class/power_supply/{name}/status") or "" - info["status"] = status.lower() - # 电压 (微伏) - volt = _read_file_int(f"/sys/class/power_supply/{name}/voltage_now") - if volt is not None: - info["voltage"] = volt / 1000000.0 - # 温度 (十分之一度) - temp = _read_file_int(f"/sys/class/power_supply/{name}/temp") - if temp is not None: - info["temperature"] = temp / 10.0 - return info - # psutil 回退 - if HAS_PSUTIL: - try: - battery = psutil.sensors_battery() - if battery: - info["present"] = True - info["level"] = battery.percent - info["status"] = "charging" if battery.power_plugged else "discharging" - except Exception: - pass - return info - - @staticmethod - def get_power_status() -> dict: - """获取电源状态""" - info = LinuxPower.get_battery_info() - return { - "battery_present": info["present"], - "battery_level": info["level"], - "battery_status": info["status"], - "is_charging": "charging" in info["status"], - "temperature": info["temperature"], - "voltage": info["voltage"], - } - - @staticmethod - def get_cpu_usage() -> float: - """获取CPU使用率""" - if HAS_PSUTIL: - return psutil.cpu_percent(interval=0.5) - # /proc/stat 回退 - def read_cpu(): - data = _read_file("/proc/stat") - if data: - parts = data.split("\n")[0].split()[1:] - return [int(x) for x in parts] - return [0] * 10 - c1 = read_cpu() - time.sleep(0.5) - c2 = read_cpu() - idle = c2[3] - c1[3] - total = sum(c2) - sum(c1) - if total > 0: - return (1 - idle / total) * 100 - return 0.0 - - @staticmethod - def get_memory_info() -> dict: - """获取内存信息""" - if HAS_PSUTIL: - mem = psutil.virtual_memory() - return { - "total": mem.total, - "available": mem.available, - "used": mem.used, - "free": mem.free, - "percent": mem.percent, - } - # /proc/meminfo 回退 - data = _read_file("/proc/meminfo") - info = {} - if data: - for line in data.split("\n"): - parts = line.split() - if len(parts) >= 2: - info[parts[0].rstrip(":")] = int(parts[1]) * 1024 - return { - "total": info.get("MemTotal", 0), - "available": info.get("MemAvailable", 0), - "used": info.get("MemTotal", 0) - info.get("MemAvailable", 0), - "free": info.get("MemFree", 0), - "percent": 0, - } - - -# ═══════════════════════════════════════════════════════════════ -# 位置 — 真实实现 (IP 地理定位) -# ═══════════════════════════════════════════════════════════════ - -class LinuxLocation: - """真实位置信息 — 通过 IP 地理定位""" - - _cached_location = None - _cache_time = 0 - - @staticmethod - def get_location() -> dict: - """通过 IP 获取地理位置""" - if not HAS_REQUESTS: - return {"latitude": 0.0, "longitude": 0.0, "accuracy": 0, "source": "none"} - - # 缓存 5 分钟 - now = time.time() - if LinuxLocation._cached_location and now - LinuxLocation._cache_time < 300: - return LinuxLocation._cached_location - - # 尝试多个 IP 定位服务 - services = [ - ("http://ip-api.com/json/", "ip-api"), - ("https://ipapi.co/json/", "ipapi"), - ("https://freeipapi.com/api/json", "freeipapi"), - ] - - for url, source in services: - try: - resp = requests.get(url, timeout=5) - if resp.status_code == 200: - data = resp.json() - lat = data.get("lat") or data.get("latitude") or 0.0 - lon = data.get("lon") or data.get("longitude") or 0.0 - location = { - "latitude": float(lat), - "longitude": float(lon), - "accuracy": 50000, # IP定位精度约 50km - "source": source, - "city": data.get("city", ""), - "region": data.get("regionName", data.get("region", "")), - "country": data.get("country", data.get("country_name", "")), - "country_code": data.get("countryCode", data.get("country", "")), - "timezone": data.get("timezone", ""), - "isp": data.get("isp", ""), - } - LinuxLocation._cached_location = location - LinuxLocation._cache_time = now - return location - except Exception: - continue - - return {"latitude": 0.0, "longitude": 0.0, "accuracy": 0, "source": "failed"} - - -# ═══════════════════════════════════════════════════════════════ -# 通知 — 真实实现 (notify-send) -# ═══════════════════════════════════════════════════════════════ - -class LinuxNotify: - """真实桌面通知 — 通过 notify-send""" - - @staticmethod - def send(title: str, text: str, icon: str = None, urgency: str = "normal", - timeout: int = -1) -> bool: - if not HAS_NOTIFY_SEND: - # 回退到 stderr - print(f"[{title}] {text}", file=sys.stderr) - return False - cmd = ["notify-send"] - cmd.extend(["-u", urgency]) - if timeout >= 0: - cmd.extend(["-t", str(timeout)]) - if icon: - cmd.extend(["-i", icon]) - cmd.extend([title, text]) - code, _, _ = _run(cmd) - return code == 0 - - -# ═══════════════════════════════════════════════════════════════ -# 显示/屏幕 — 真实实现 (xdotool, X11) -# ═══════════════════════════════════════════════════════════════ - -class LinuxDisplay: - """真实显示/屏幕信息""" - - @staticmethod - def get_screen_size() -> Tuple[int, int]: - """获取屏幕尺寸""" - if HAS_XDOTOOL: - code, out, _ = _run(["xdotool", "getdisplaygeometry"]) - if code == 0: - parts = out.split() - if len(parts) >= 2: - return int(parts[0]), int(parts[1]) - if HAS_XRANDR: - code, out, _ = _run(["xrandr", "--current"]) - if code == 0: - for line in out.split("\n"): - if " connected" in line and "*" in line: - match = re.search(r'(\d+)x(\d+)', line) - if match: - return int(match.group(1)), int(match.group(2)) - # 回退 - try: - import tkinter - root = tkinter.Tk() - w = root.winfo_screenwidth() - h = root.winfo_screenheight() - root.destroy() - return w, h - except Exception: - return 1920, 1080 - - @staticmethod - def screenshot(path: str = None) -> Optional[str]: - """截屏""" - if HAS_PIL: - try: - img = ImageGrab.grab() - if path: - img.save(path) - return path - else: - buf = f"/tmp/screenshot_{int(time.time())}.png" - img.save(buf) - return buf - except Exception: - pass - if HAS_XDOTOOL: - # 用 xdotool + import - if not path: - path = f"/tmp/screenshot_{int(time.time())}.png" - code, _, _ = _run(["import", "-window", "root", path]) - if code == 0: - return path - return None - - @staticmethod - def get_mouse_position() -> Tuple[int, int]: - """获取鼠标位置""" - if HAS_XDOTOOL: - code, out, _ = _run(["xdotool", "getmouselocation"]) - if code == 0: - parts = out.split() - x = int(parts[0].split(":")[1]) - y = int(parts[1].split(":")[1]) - return x, y - return 0, 0 - - -# ═══════════════════════════════════════════════════════════════ -# UI 自动化 — 真实实现 (xdotool) -# ═══════════════════════════════════════════════════════════════ - -class LinuxUIAutomation: - """真实 UI 自动化 — 通过 xdotool""" - - @staticmethod - def click(x: int, y: int) -> bool: - if not HAS_XDOTOOL: - return False - code, _, _ = _run(["xdotool", "mousemove", str(x), str(y), "click", "1"]) - return code == 0 - - @staticmethod - def double_click(x: int, y: int) -> bool: - if not HAS_XDOTOOL: - return False - code, _, _ = _run(["xdotool", "mousemove", str(x), str(y), "click", "--repeat", "2", "1"]) - return code == 0 - - @staticmethod - def right_click(x: int, y: int) -> bool: - if not HAS_XDOTOOL: - return False - code, _, _ = _run(["xdotool", "mousemove", str(x), str(y), "click", "3"]) - return code == 0 - - @staticmethod - def type_text(text: str) -> bool: - if not HAS_XDOTOOL: - return False - code, _, _ = _run(["xdotool", "type", "--clearmodifiers", text]) - return code == 0 - - @staticmethod - def press_key(key: str) -> bool: - if not HAS_XDOTOOL: - return False - # 转换常见键名 - key_map = { - "enter": "Return", "return": "Return", - "tab": "Tab", "escape": "Escape", "esc": "Escape", - "backspace": "BackSpace", "delete": "Delete", - "space": "space", "up": "Up", "down": "Down", - "left": "Left", "right": "Right", - "home": "Home", "end": "End", - "page_up": "Page_Up", "page_down": "Page_Down", - } - xdotool_key = key_map.get(key.lower(), key) - code, _, _ = _run(["xdotool", "key", xdotool_key]) - return code == 0 - - @staticmethod - def hotkey(*keys) -> bool: - if not HAS_XDOTOOL: - return False - key_map = { - "ctrl": "ctrl", "control": "ctrl", - "alt": "alt", "shift": "shift", - "super": "super", "win": "super", "meta": "super", - } - mapped = [key_map.get(k.lower(), k) for k in keys] - combo = "+".join(mapped) - code, _, _ = _run(["xdotool", "key", combo]) - return code == 0 - - @staticmethod - def scroll(amount: int) -> bool: - if not HAS_XDOTOOL: - return False - button = "4" if amount > 0 else "5" - count = abs(amount) - code, _, _ = _run(["xdotool", "click", "--repeat", str(count), button]) - return code == 0 - - @staticmethod - def find_window(title: str) -> Optional[int]: - """查找窗口""" - if not HAS_XDOTOOL: - return None - code, out, _ = _run(["xdotool", "search", "--name", title]) - if code == 0 and out: - return int(out.split("\n")[0]) - return None - - @staticmethod - def activate_window(window_id: int) -> bool: - if not HAS_XDOTOOL: - return False - code, _, _ = _run(["xdotool", "windowactivate", str(window_id)]) - return code == 0 - - @staticmethod - def close_window(window_id: int) -> bool: - if not HAS_XDOTOOL: - return False - code, _, _ = _run(["xdotool", "windowclose", str(window_id)]) - return code == 0 - - -# ═══════════════════════════════════════════════════════════════ -# 摄像头 — 真实实现 (opencv/V4L2) -# ═══════════════════════════════════════════════════════════════ - -class LinuxCamera: - """真实摄像头 — 通过 OpenCV/V4L2""" - - @staticmethod - def list_cameras() -> List[int]: - """列出可用摄像头""" - cameras = [] - for dev in _list_dir("/dev/"): - if dev.startswith("video"): - try: - idx = int(dev[5:]) - cameras.append(idx) - except ValueError: - pass - return cameras - - @staticmethod - def capture(camera_id: int = 0, output_path: str = None) -> Optional[str]: - """拍照""" - if not HAS_CV2: - return None - cap = cv2.VideoCapture(camera_id) - if not cap.isOpened(): - return None - ret, frame = cap.read() - cap.release() - if not ret: - return None - if not output_path: - output_path = f"/tmp/camera_{int(time.time())}.jpg" - cv2.imwrite(output_path, frame) - return output_path - - @staticmethod - def get_camera_info(camera_id: int = 0) -> dict: - """获取摄像头信息""" - info = {"id": camera_id, "available": False, "width": 0, "height": 0} - if HAS_CV2: - cap = cv2.VideoCapture(camera_id) - if cap.isOpened(): - info["available"] = True - info["width"] = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) - info["height"] = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) - info["fps"] = cap.get(cv2.CAP_PROP_FPS) - cap.release() - return info - - -# ═══════════════════════════════════════════════════════════════ -# 进程/应用管理 — 真实实现 (psutil, /proc) -# ═══════════════════════════════════════════════════════════════ - -class LinuxProcess: - """真实进程/应用管理""" - - @staticmethod - def list_processes() -> List[dict]: - """列出所有进程""" - if HAS_PSUTIL: - procs = [] - for p in psutil.process_iter(['pid', 'name', 'username', 'memory_percent', - 'cpu_percent', 'status', 'cmdline']): - try: - info = p.info - procs.append({ - "pid": info['pid'], - "name": info['name'], - "user": info['username'], - "memory": info.get('memory_percent', 0), - "cpu": info.get('cpu_percent', 0), - "status": info['status'], - "cmdline": " ".join(info.get('cmdline') or []), - }) - except (psutil.NoSuchProcess, psutil.AccessDenied): - pass - return procs - # /proc 回退 - procs = [] - for name in _list_dir("/proc/"): - if name.isdigit(): - pid = int(name) - comm = _read_file(f"/proc/{name}/comm") or "" - cmdline = _read_file(f"/proc/{name}/cmdline") or "" - status = _read_file(f"/proc/{name}/status") or "" - user = "" - for line in status.split("\n"): - if line.startswith("Uid:"): - uid = line.split()[1] - try: - import pwd - user = pwd.getpwuid(int(uid)).pw_name - except Exception: - user = uid - break - procs.append({ - "pid": pid, "name": comm, "user": user, - "cmdline": cmdline.replace("\x00", " "), - "status": "", "memory": 0, "cpu": 0, - }) - return procs - - @staticmethod - def get_running_apps() -> List[dict]: - """获取运行中的应用 (GUI 窗口)""" - if not HAS_XDOTOOL: - return [] - code, out, _ = _run(["xdotool", "search", "--onlyvisible", "--class", ""]) - apps = [] - if code == 0: - seen = set() - for wid in out.split("\n"): - if wid and wid not in seen: - seen.add(wid) - # 获取窗口名称 - code2, name, _ = _run(["xdotool", "getwindowname", wid]) - if name: - apps.append({"window_id": int(wid), "name": name}) - return apps - - @staticmethod - def kill_process(pid: int) -> bool: - """终止进程""" - if HAS_PSUTIL: - try: - psutil.Process(pid).terminate() - return True - except Exception: - return False - code, _, _ = _run(["kill", str(pid)]) - return code == 0 - - @staticmethod - def start_app(name: str) -> bool: - """启动应用""" - code, _, _ = _run([name], timeout=1) - # 非阻塞启动 - try: - subprocess.Popen([name], stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) - return True - except Exception: - return False - - @staticmethod - def get_system_info() -> dict: - """获取系统信息""" - info = { - "platform": platform.system(), - "hostname": socket.gethostname(), - "kernel": platform.release(), - "arch": platform.machine(), - "python": platform.python_version(), - } - if HAS_PSUTIL: - info["cpu_count"] = psutil.cpu_count() - info["cpu_freq"] = psutil.cpu_freq().current if psutil.cpu_freq() else 0 - boot_time = psutil.boot_time() - info["uptime"] = time.time() - boot_time - mem = psutil.virtual_memory() - info["memory_total"] = mem.total - info["memory_used"] = mem.used - info["memory_percent"] = mem.percent - disk = psutil.disk_usage('/') - info["disk_total"] = disk.total - info["disk_used"] = disk.used - info["disk_percent"] = disk.percent - return info - - -# ═══════════════════════════════════════════════════════════════ -# Shell — 真实实现 -# ═══════════════════════════════════════════════════════════════ - -class LinuxShell: - """真实 Shell 执行""" - - @staticmethod - def exec(command: str, timeout: int = 30, root: bool = False) -> dict: - """执行 Shell 命令""" - if root and os.geteuid() != 0: - command = f"sudo {command}" - code, out, err = _run(command, timeout=timeout) - return { - "success": code == 0, - "output": out + ("\n" + err if err else ""), - "exit_code": code, - "stdout": out, - "stderr": err, - } - - @staticmethod - def exec_async(command: str) -> int: - """异步执行命令,返回 PID""" - proc = subprocess.Popen(command, shell=True, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) - return proc.pid - - -# ═══════════════════════════════════════════════════════════════ -# 统一入口 -# ═══════════════════════════════════════════════════════════════ - -def get_linux_capabilities() -> dict: - """获取当前 Linux 环境的可用能力""" - return { - "is_linux": IS_LINUX, - "network": HAS_IP, - "bluetooth": HAS_BLUETOOTHCTL, - "audio": HAS_AMIXER, - "clipboard": HAS_XCLIP, - "ui_automation": HAS_XDOTOOL, - "notifications": HAS_NOTIFY_SEND, - "sensors": True, # /sys 总是可用 - "display": True, - "camera": HAS_CV2, - "ffmpeg": HAS_FFMPEG, - "ffprobe": HAS_FFPROBE, - "psutil": HAS_PSUTIL, - "requests": HAS_REQUESTS, - "PIL": HAS_PIL, - "opencv": HAS_CV2, - "numpy": HAS_NUMPY, - "dbus": HAS_DBUS, - "xrandr": HAS_XRANDR, - } - - -# 导出所有实现类 -__all__ = [ - "IS_LINUX", "get_linux_capabilities", - "LinuxNetwork", "LinuxBluetooth", "LinuxSensors", - "LinuxAudio", "LinuxClipboard", "LinuxPower", - "LinuxLocation", "LinuxNotify", "LinuxDisplay", - "LinuxUIAutomation", "LinuxCamera", "LinuxProcess", - "LinuxShell", - # 能力标志 - "HAS_IP", "HAS_BLUETOOTHCTL", "HAS_AMIXER", "HAS_XCLIP", - "HAS_XDOTOOL", "HAS_NOTIFY_SEND", "HAS_SENSORS", - "HAS_FFMPEG", "HAS_FFPROBE", "HAS_PSUTIL", "HAS_REQUESTS", - "HAS_CV2", "HAS_NUMPY", "HAS_PIL", "HAS_DBUS", "HAS_XRANDR", -] diff --git a/android_pyapi/_windows_impl.py b/android_pyapi/_windows_impl.py deleted file mode 100644 index 83e173f..0000000 --- a/android_pyapi/_windows_impl.py +++ /dev/null @@ -1,1380 +0,0 @@ -""" -Windows 真实实现层 -================== -在 Windows 环境下,将 Android API 的存根行为替换为真实的系统调用。 -使 Android-pyApi 从"存根库"变为"可用的生产力工具"。 - -覆盖能力: - - 网络: ipconfig, netsh, socket, wmi - - 蓝牙: PowerShell Bluetooth cmdlets - - 传感器: wmi, psutil - - 音频: pycaw / PowerShell - - 剪贴板: pyperclip / win32clipboard - - 电源/电池: psutil, wmi - - 位置: IP 地理定位 (requests) - - 通知: win10toast / plyer - - 显示: pyautogui, screeninfo, win32api - - UI自动化: pyautogui - - 摄像头: opencv - - 进程管理: psutil, subprocess - - 文件: 原生 Python os - - 媒体: ffmpeg/ffprobe -""" - -from __future__ import annotations - -import os -import sys -import re -import json -import time -import socket -import shutil -import subprocess -import platform -import hashlib -from pathlib import Path -from datetime import datetime -from typing import Any, Dict, List, Optional, Tuple - -# ═══════════════════════════════════════════════════════════════ -# 能力检测 -# ═══════════════════════════════════════════════════════════════ - -IS_WINDOWS = platform.system() == "Windows" - -def _which(name: str) -> Optional[str]: - return shutil.which(name) - -HAS_POWERSHELL = _which("powershell") is not None or _which("pwsh") is not None -HAS_FFMPEG = _which("ffmpeg") is not None -HAS_FFPROBE = _which("ffprobe") is not None - -try: - import psutil - HAS_PSUTIL = True -except ImportError: - HAS_PSUTIL = False - -try: - import requests - HAS_REQUESTS = True -except ImportError: - HAS_REQUESTS = False - -try: - import cv2 - HAS_CV2 = True -except ImportError: - HAS_CV2 = False - -try: - import numpy as np - HAS_NUMPY = True -except ImportError: - HAS_NUMPY = False - -try: - from PIL import Image, ImageDraw, ImageGrab - HAS_PIL = True -except ImportError: - HAS_PIL = False - -try: - import pyautogui - HAS_PYAUTOGUI = True -except ImportError: - HAS_PYAUTOGUI = False - -try: - import pyperclip - HAS_PYPERCLIP = True -except ImportError: - HAS_PYPERCLIP = False - -try: - from win10toast import ToastNotifier - HAS_WIN10TOAST = True -except ImportError: - HAS_WIN10TOAST = False - -try: - import wmi - HAS_WMI = True -except ImportError: - HAS_WMI = False - -try: - import win32api - import win32con - import win32gui - HAS_WIN32 = True -except ImportError: - HAS_WIN32 = False - -try: - from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume - from comtypes import CLSCTX_ALL - HAS_PYCAW = True -except ImportError: - HAS_PYCAW = False - -try: - from screeninfo import get_monitors - HAS_SCREENINFO = True -except ImportError: - HAS_SCREENINFO = False - - -def _run(cmd: List[str] or str, timeout: int = 10, capture: bool = True) -> Tuple[int, str, str]: - """执行命令,返回 (exit_code, stdout, stderr)""" - try: - if isinstance(cmd, str): - result = subprocess.run(cmd, shell=True, capture_output=capture, - text=True, timeout=timeout, encoding='utf-8', errors='replace') - else: - result = subprocess.run(cmd, capture_output=capture, - text=True, timeout=timeout, encoding='utf-8', errors='replace') - return result.returncode, result.stdout.strip(), result.stderr.strip() - except subprocess.TimeoutExpired: - return -1, "", "timeout" - except Exception as e: - return -1, "", str(e) - - -def _run_ps(script: str, timeout: int = 10) -> Tuple[int, str, str]: - """执行 PowerShell 脚本""" - ps_exe = "pwsh" if _which("pwsh") else "powershell" - cmd = [ps_exe, "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script] - return _run(cmd, timeout=timeout) - - -def _read_file(path: str) -> Optional[str]: - try: - with open(path, "r", encoding='utf-8', errors='replace') as f: - return f.read().strip() - except Exception: - return None - - -def _read_file_int(path: str) -> Optional[int]: - val = _read_file(path) - if val is not None: - try: - return int(val) - except ValueError: - return None - return None - - -# ═══════════════════════════════════════════════════════════════ -# 网络 — 真实实现 -# ═══════════════════════════════════════════════════════════════ - -class WindowsNetwork: - """真实网络信息 — 通过 ipconfig, netsh, socket""" - - @staticmethod - def get_interfaces() -> List[dict]: - """获取网络接口列表""" - interfaces = [] - # 使用 PowerShell 获取网络适配器信息 - code, out, _ = _run_ps( - "Get-NetAdapter | Select-Object Name, Status, MacAddress, InterfaceDescription | ConvertTo-Json" - ) - if code == 0 and out: - try: - data = json.loads(out) - if isinstance(data, dict): - data = [data] - for iface in data: - interfaces.append({ - "ifname": iface.get("Name", ""), - "operstate": iface.get("Status", ""), - "address": iface.get("MacAddress", ""), - "description": iface.get("InterfaceDescription", ""), - }) - return interfaces - except json.JSONDecodeError: - pass - # 回退到 ipconfig - code, out, _ = _run("ipconfig /all") - if code == 0: - current = {} - for line in out.split("\n"): - line = line.strip() - if "adapter" in line.lower() and ":" in line: - if current: - interfaces.append(current) - current = {"ifname": line.split(":")[0].strip(), "operstate": "up", "address": ""} - elif "Physical Address" in line or "物理地址" in line: - current["address"] = line.split(":")[-1].strip() - if current: - interfaces.append(current) - return interfaces - - @staticmethod - def get_ip_address(interface: str = None) -> str: - """获取IP地址""" - try: - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - s.connect(("8.8.8.8", 80)) - ip = s.getsockname()[0] - s.close() - return ip - except Exception: - pass - try: - return socket.gethostbyname(socket.gethostname()) - except Exception: - return "127.0.0.1" - - @staticmethod - def get_wifi_info() -> dict: - """获取WiFi信息""" - code, out, _ = _run("netsh wlan show interfaces") - if code == 0: - info = {"ssid": "", "signal": 0, "status": "disconnected"} - for line in out.split("\n"): - line = line.strip() - if "SSID" in line and "BSSID" not in line: - parts = line.split(":", 1) - if len(parts) >= 2: - info["ssid"] = parts[1].strip() - elif "Signal" in line: - parts = line.split(":", 1) - if len(parts) >= 2: - signal_str = parts[1].strip().rstrip("%") - try: - info["signal"] = int(signal_str) - except ValueError: - pass - elif "State" in line: - parts = line.split(":", 1) - if len(parts) >= 2: - info["status"] = parts[1].strip().lower() - return info - return {"ssid": "", "signal": 0, "status": "unknown"} - - @staticmethod - def get_connectivity() -> bool: - """检测网络连通性""" - for host, port in [("8.8.8.8", 53), ("1.1.1.1", 53), ("114.114.114.114", 53)]: - try: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.settimeout(3) - s.connect((host, port)) - s.close() - return True - except Exception: - continue - if HAS_REQUESTS: - try: - requests.get("http://www.baidu.com", timeout=3) - return True - except Exception: - pass - return False - - @staticmethod - def get_dns_servers() -> List[str]: - """获取DNS服务器""" - code, out, _ = _run_ps( - "Get-DnsClientServerAddress | Select-Object -ExpandProperty ServerAddresses" - ) - if code == 0 and out: - return [s.strip() for s in out.split("\n") if s.strip()] - # 回退到 ipconfig - code, out, _ = _run("ipconfig /all") - if code == 0: - dns = [] - for line in out.split("\n"): - if "DNS" in line and ":" in line: - parts = line.split(":", 1) - if len(parts) >= 2: - addr = parts[1].strip() - if addr and not addr.startswith("("): - dns.append(addr) - return dns - return [] - - @staticmethod - def get_gateway() -> str: - """获取默认网关""" - code, out, _ = _run("ipconfig") - if code == 0: - for line in out.split("\n"): - if ("Default Gateway" in line or "默认网关" in line) and ":" in line: - parts = line.split(":", 1) - if len(parts) >= 2: - gw = parts[1].strip() - if gw and gw != "0.0.0.0": - return gw - return "" - - @staticmethod - def get_mac_address(interface: str = None) -> str: - """获取MAC地址""" - code, out, _ = _run("getmac /fo csv /v") - if code == 0: - lines = out.split("\n") - for line in lines[1:]: # 跳过标题行 - parts = line.split(",") - if len(parts) >= 3: - mac = parts[2].strip('"').replace("-", ":") - if mac and mac != "N/A": - return mac - return "" - - @staticmethod - def get_rx_tx_bytes(interface: str = None) -> dict: - """获取网络收发字节数""" - if HAS_PSUTIL: - counters = psutil.net_io_counters(pernic=True) - if interface and interface in counters: - c = counters[interface] - return {"rx_bytes": c.bytes_recv, "tx_bytes": c.bytes_sent, "interface": interface} - elif counters: - # 取第一个非回环接口 - for name, c in counters.items(): - if name.lower() != "lo": - return {"rx_bytes": c.bytes_recv, "tx_bytes": c.bytes_sent, "interface": name} - return {"rx_bytes": 0, "tx_bytes": 0, "interface": ""} - - -# ═══════════════════════════════════════════════════════════════ -# 蓝牙 — 真实实现 (PowerShell) -# ═══════════════════════════════════════════════════════════════ - -class WindowsBluetooth: - """真实蓝牙操作 — 通过 PowerShell Bluetooth cmdlets""" - - STATE_OFF = 10 - STATE_TURNING_ON = 11 - STATE_ON = 12 - STATE_TURNING_OFF = 13 - - @staticmethod - def get_state() -> int: - code, out, _ = _run_ps( - "Get-PnpDevice -Class Bluetooth | Where-Object {$_.FriendlyName -like '*Radio*' -or $_.FriendlyName -like '*Adapter*'} | Select-Object Status" - ) - if code == 0 and "OK" in out: - return WindowsBluetooth.STATE_ON - return WindowsBluetooth.STATE_OFF - - @staticmethod - def get_address() -> Optional[str]: - code, out, _ = _run_ps( - "Get-PnpDevice -Class Bluetooth -Status OK | Select-Object InstanceId | ConvertTo-Json" - ) - if code == 0 and out: - try: - data = json.loads(out) - if isinstance(data, dict): - data = [data] - for dev in data: - instance_id = dev.get("InstanceId", "") - # 从 InstanceId 提取 MAC 地址 - mac_match = re.search(r'([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}', instance_id) - if mac_match: - return mac_match.group().replace("-", ":") - except json.JSONDecodeError: - pass - return None - - @staticmethod - def get_name() -> Optional[str]: - code, out, _ = _run_ps( - "Get-PnpDevice -Class Bluetooth -Status OK | Where-Object {$_.FriendlyName -like '*Radio*' -or $_.FriendlyName -like '*Adapter*'} | Select-Object FriendlyName | ConvertTo-Json" - ) - if code == 0 and out: - try: - data = json.loads(out) - if isinstance(data, dict): - return data.get("FriendlyName") - elif isinstance(data, list) and data: - return data[0].get("FriendlyName") - except json.JSONDecodeError: - pass - return None - - @staticmethod - def enable() -> bool: - code, _, err = _run_ps( - "Get-PnpDevice -Class Bluetooth | Enable-PnpDevice -Confirm:$false" - ) - return code == 0 - - @staticmethod - def disable() -> bool: - code, _, err = _run_ps( - "Get-PnpDevice -Class Bluetooth | Disable-PnpDevice -Confirm:$false" - ) - return code == 0 - - @staticmethod - def get_bonded_devices() -> List[dict]: - code, out, _ = _run_ps( - "Get-PnpDevice -Class Bluetooth -Status OK | Select-Object FriendlyName, InstanceId | ConvertTo-Json" - ) - devices = [] - if code == 0 and out: - try: - data = json.loads(out) - if isinstance(data, dict): - data = [data] - for dev in data: - name = dev.get("FriendlyName", "") - if name and "Enumerator" not in name: - devices.append({ - "address": "", - "name": name, - "bonded": True, - }) - except json.JSONDecodeError: - pass - return devices - - @staticmethod - def scan(timeout: int = 10) -> List[dict]: - """扫描蓝牙设备 (Windows 限制,只能列出已配对设备)""" - return WindowsBluetooth.get_bonded_devices() - - @staticmethod - def pair(address: str) -> bool: - # Windows 需要通过设置界面配对 - code, _, _ = _run_ps("Start-Process ms-settings:bluetooth") - return code == 0 - - @staticmethod - def connect(address: str) -> bool: - return False # Windows 无命令行直接连接方式 - - @staticmethod - def disconnect(address: str) -> bool: - return False - - @staticmethod - def unpair(address: str) -> bool: - return False - - -# ═══════════════════════════════════════════════════════════════ -# 传感器 — 真实实现 (wmi, psutil) -# ═══════════════════════════════════════════════════════════════ - -class WindowsSensors: - """真实传感器数据 — 通过 WMI 和 psutil""" - - TYPE_ACCELEROMETER = 1 - TYPE_GYROSCOPE = 4 - TYPE_LIGHT = 5 - TYPE_PRESSURE = 6 - TYPE_PROXIMITY = 8 - TYPE_TEMPERATURE = 7 - TYPE_RELATIVE_HUMIDITY = 12 - TYPE_AMBIENT_TEMPERATURE = 13 - - @staticmethod - def get_temperatures() -> List[dict]: - """获取温度传感器读数""" - temps = [] - # 通过 WMI 获取 CPU 温度 - if HAS_WMI: - try: - w = wmi.WMI(namespace="root\\wmi") - for sensor in w.MSAcpi_ThermalZoneTemperature(): - temp_celsius = (sensor.CurrentTemperature / 10.0) - 273.15 - temps.append({ - "name": sensor.InstanceName, - "type": "thermal_zone", - "value": temp_celsius, - "unit": "°C", - }) - except Exception: - pass - # 通过 psutil 获取 (如果支持) - if HAS_PSUTIL: - try: - if hasattr(psutil, "sensors_temperatures"): - for name, entries in psutil.sensors_temperatures().items(): - for entry in entries: - temps.append({ - "name": f"{name}/{entry.label or 'unknown'}", - "type": name, - "value": entry.current, - "unit": "°C", - }) - except Exception: - pass - return temps - - @staticmethod - def get_fan_speeds() -> List[dict]: - """获取风扇转速""" - fans = [] - if HAS_PSUTIL: - try: - if hasattr(psutil, "sensors_fans"): - for name, entries in psutil.sensors_fans().items(): - for entry in entries: - fans.append({ - "name": f"{name}/{entry.label or 'unknown'}", - "rpm": entry.current, - }) - except Exception: - pass - return fans - - @staticmethod - def get_cpu_frequency() -> List[dict]: - """获取CPU频率""" - freqs = [] - if HAS_PSUTIL: - try: - cpu_freq = psutil.cpu_freq(percpu=True) - if cpu_freq: - for i, freq in enumerate(cpu_freq): - freqs.append({ - "cpu": i, - "freq_mhz": freq.current, - }) - else: - # 单个频率值 - freq = psutil.cpu_freq() - if freq: - freqs.append({ - "cpu": 0, - "freq_mhz": freq.current, - }) - except Exception: - pass - return freqs - - @staticmethod - def get_light_level() -> Optional[float]: - """获取环境光传感器 (Windows 通常无直接访问)""" - return None - - @staticmethod - def get_all_sensors() -> dict: - """获取所有传感器数据""" - return { - "temperatures": WindowsSensors.get_temperatures(), - "fans": WindowsSensors.get_fan_speeds(), - "cpu_freq": WindowsSensors.get_cpu_frequency(), - "light": WindowsSensors.get_light_level(), - } - - -# ═══════════════════════════════════════════════════════════════ -# 音频 — 真实实现 (pycaw / PowerShell) -# ═══════════════════════════════════════════════════════════════ - -class WindowsAudio: - """真实音频控制 — 通过 pycaw 或 PowerShell""" - - @staticmethod - def _get_pycaw_volume(): - """获取 pycaw 音量接口""" - if not HAS_PYCAW: - return None - try: - devices = AudioUtilities.GetSpeakers() - interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None) - return interface.QueryInterface(IAudioEndpointVolume) - except Exception: - return None - - @staticmethod - def get_volume() -> int: - """获取当前音量 (0-100)""" - # 方法1: pycaw - vol = WindowsAudio._get_pycaw_volume() - if vol: - try: - return int(vol.GetMasterVolumeLevelScalar() * 100) - except Exception: - pass - # 方法2: PowerShell - code, out, _ = _run_ps( - "(Get-AudioDevice -PlaybackVolume) 2>$null; if($?) {$out} else {(Get-WmiObject -Class Win32_SoundDevice | Select-Object -First 1).Volume}" - ) - if code == 0 and out: - try: - return int(out.strip()) - except ValueError: - pass - return 50 # 默认值 - - @staticmethod - def set_volume(volume: int) -> bool: - """设置音量 (0-100)""" - volume = max(0, min(100, volume)) - # 方法1: pycaw - vol = WindowsAudio._get_pycaw_volume() - if vol: - try: - vol.SetMasterVolumeLevelScalar(volume / 100.0, None) - return True - except Exception: - pass - # 方法2: PowerShell - code, _, _ = _run_ps(f"(Get-AudioDevice -PlaybackVolume {volume}) 2>$null") - return code == 0 - - @staticmethod - def get_mute() -> bool: - """获取静音状态""" - vol = WindowsAudio._get_pycaw_volume() - if vol: - try: - return vol.GetMute() - except Exception: - pass - return False - - @staticmethod - def set_mute(mute: bool) -> bool: - """设置静音""" - vol = WindowsAudio._get_pycaw_volume() - if vol: - try: - vol.SetMute(mute, None) - return True - except Exception: - pass - return False - - @staticmethod - def get_audio_devices() -> List[dict]: - """获取音频设备列表""" - devices = [] - code, out, _ = _run_ps( - "Get-AudioDevice -List 2>$null; if(-not $?) { Get-WmiObject Win32_SoundDevice | Select-Object Name, Status }" - ) - if code == 0 and out: - for line in out.split("\n"): - if line.strip(): - devices.append({"name": line.strip(), "type": "windows"}) - return devices - - -# ═══════════════════════════════════════════════════════════════ -# 剪贴板 — 真实实现 (pyperclip) -# ═══════════════════════════════════════════════════════════════ - -class WindowsClipboard: - """真实剪贴板 — 通过 pyperclip 或 tkinter""" - - @staticmethod - def read() -> str: - if HAS_PYPERCLIP: - try: - return pyperclip.paste() - except Exception: - pass - # 回退到 tkinter - try: - import tkinter - root = tkinter.Tk() - root.withdraw() - text = root.clipboard_get() - root.destroy() - return text - except Exception: - return "" - - @staticmethod - def write(text: str) -> bool: - if HAS_PYPERCLIP: - try: - pyperclip.copy(text) - return True - except Exception: - pass - # 回退到 tkinter - try: - import tkinter - root = tkinter.Tk() - root.withdraw() - root.clipboard_clear() - root.clipboard_append(text) - root.update() - root.destroy() - return True - except Exception: - return False - - -# ═══════════════════════════════════════════════════════════════ -# 电源/电池 — 真实实现 (psutil) -# ═══════════════════════════════════════════════════════════════ - -class WindowsPower: - """真实电源/电池信息""" - - BATTERY_STATUS_UNKNOWN = 1 - BATTERY_STATUS_CHARGING = 2 - BATTERY_STATUS_DISCHARGING = 3 - BATTERY_STATUS_NOT_CHARGING = 4 - BATTERY_STATUS_FULL = 5 - - @staticmethod - def get_battery_info() -> dict: - """获取电池信息""" - info = {"present": False, "level": -1, "status": "unknown", - "temperature": 0, "voltage": 0, "technology": ""} - if HAS_PSUTIL: - try: - battery = psutil.sensors_battery() - if battery: - info["present"] = True - info["level"] = battery.percent - if battery.power_plugged: - info["status"] = "charging" if battery.percent < 100 else "full" - else: - info["status"] = "discharging" - if battery.secsleft and battery.secsleft > 0: - info["seconds_left"] = battery.secsleft - except Exception: - pass - return info - - @staticmethod - def get_power_status() -> dict: - """获取电源状态""" - info = WindowsPower.get_battery_info() - return { - "battery_present": info["present"], - "battery_level": info["level"], - "battery_status": info["status"], - "is_charging": "charging" in info["status"], - "temperature": info["temperature"], - "voltage": info["voltage"], - } - - @staticmethod - def get_cpu_usage() -> float: - """获取CPU使用率""" - if HAS_PSUTIL: - return psutil.cpu_percent(interval=0.5) - return 0.0 - - @staticmethod - def get_memory_info() -> dict: - """获取内存信息""" - if HAS_PSUTIL: - mem = psutil.virtual_memory() - return { - "total": mem.total, - "available": mem.available, - "used": mem.used, - "free": mem.free, - "percent": mem.percent, - } - return {"total": 0, "available": 0, "used": 0, "free": 0, "percent": 0} - - -# ═══════════════════════════════════════════════════════════════ -# 位置 — 真实实现 (IP 地理定位) -# ═══════════════════════════════════════════════════════════════ - -class WindowsLocation: - """真实位置信息 — 通过 IP 地理定位""" - - _cached_location = None - _cache_time = 0 - - @staticmethod - def get_location() -> dict: - """通过 IP 获取地理位置""" - if not HAS_REQUESTS: - return {"latitude": 0.0, "longitude": 0.0, "accuracy": 0, "source": "none"} - - now = time.time() - if WindowsLocation._cached_location and now - WindowsLocation._cache_time < 300: - return WindowsLocation._cached_location - - services = [ - ("http://ip-api.com/json/", "ip-api"), - ("https://ipapi.co/json/", "ipapi"), - ("https://freeipapi.com/api/json", "freeipapi"), - ] - - for url, source in services: - try: - resp = requests.get(url, timeout=5) - if resp.status_code == 200: - data = resp.json() - lat = data.get("lat") or data.get("latitude") or 0.0 - lon = data.get("lon") or data.get("longitude") or 0.0 - location = { - "latitude": float(lat), - "longitude": float(lon), - "accuracy": 50000, - "source": source, - "city": data.get("city", ""), - "region": data.get("regionName", data.get("region", "")), - "country": data.get("country", data.get("country_name", "")), - "country_code": data.get("countryCode", data.get("country", "")), - "timezone": data.get("timezone", ""), - "isp": data.get("isp", ""), - } - WindowsLocation._cached_location = location - WindowsLocation._cache_time = now - return location - except Exception: - continue - - return {"latitude": 0.0, "longitude": 0.0, "accuracy": 0, "source": "failed"} - - -# ═══════════════════════════════════════════════════════════════ -# 通知 — 真实实现 (win10toast / PowerShell) -# ═══════════════════════════════════════════════════════════════ - -class WindowsNotify: - """真实桌面通知 — 通过 win10toast 或 PowerShell""" - - @staticmethod - def send(title: str, text: str, icon: str = None, urgency: str = "normal", - timeout: int = 5) -> bool: - # 方法1: win10toast - if HAS_WIN10TOAST: - try: - toaster = ToastNotifier() - toaster.show_toast(title, text, duration=timeout, threaded=True) - return True - except Exception: - pass - # 方法2: PowerShell BurntToast 模块 - script = f''' - try {{ - Import-Module BurntToast -ErrorAction Stop - New-BurntToastNotification -Text "{title}", "{text}" - }} catch {{ - [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") - [System.Windows.Forms.MessageBox]::Show("{text}", "{title}") - }} - ''' - code, _, _ = _run_ps(script) - if code == 0: - return True - # 方法3: PowerShell 原生方式 - script = f''' - [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null - [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null - $template = @" - - - - {title} - {text} - - - -"@ - $xml = New-Object Windows.Data.Xml.Dom.XmlDocument - $xml.LoadXml($template) - $toast = [Windows.UI.Notifications.ToastNotification]::new($xml) - [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("Android-pyApi").Show($toast) - ''' - code, _, _ = _run_ps(script) - return code == 0 - - -# ═══════════════════════════════════════════════════════════════ -# 显示/屏幕 — 真实实现 (pyautogui, screeninfo, win32api) -# ═══════════════════════════════════════════════════════════════ - -class WindowsDisplay: - """真实显示/屏幕信息""" - - @staticmethod - def get_screen_size() -> Tuple[int, int]: - """获取屏幕尺寸""" - # 方法1: screeninfo - if HAS_SCREENINFO: - try: - monitors = get_monitors() - if monitors: - m = monitors[0] - return m.width, m.height - except Exception: - pass - # 方法2: win32api - if HAS_WIN32: - try: - width = win32api.GetSystemMetrics(0) - height = win32api.GetSystemMetrics(1) - return width, height - except Exception: - pass - # 方法3: pyautogui - if HAS_PYAUTOGUI: - try: - size = pyautogui.size() - return size.width, size.height - except Exception: - pass - # 方法4: tkinter - try: - import tkinter - root = tkinter.Tk() - w = root.winfo_screenwidth() - h = root.winfo_screenheight() - root.destroy() - return w, h - except Exception: - return 1920, 1080 - - @staticmethod - def screenshot(path: str = None) -> Optional[str]: - """截屏""" - if HAS_PYAUTOGUI: - try: - img = pyautogui.screenshot() - if path: - img.save(path) - return path - else: - buf = os.path.join(os.environ.get("TEMP", "C:\\Temp"), - f"screenshot_{int(time.time())}.png") - img.save(buf) - return buf - except Exception: - pass - if HAS_PIL: - try: - img = ImageGrab.grab() - if path: - img.save(path) - return path - else: - buf = os.path.join(os.environ.get("TEMP", "C:\\Temp"), - f"screenshot_{int(time.time())}.png") - img.save(buf) - return buf - except Exception: - pass - return None - - @staticmethod - def get_mouse_position() -> Tuple[int, int]: - """获取鼠标位置""" - if HAS_PYAUTOGUI: - try: - pos = pyautogui.position() - return pos.x, pos.y - except Exception: - pass - if HAS_WIN32: - try: - pos = win32api.GetCursorPos() - return pos[0], pos[1] - except Exception: - pass - return 0, 0 - - -# ═══════════════════════════════════════════════════════════════ -# UI 自动化 — 真实实现 (pyautogui) -# ═══════════════════════════════════════════════════════════════ - -class WindowsUIAutomation: - """真实 UI 自动化 — 通过 pyautogui""" - - @staticmethod - def click(x: int, y: int) -> bool: - if not HAS_PYAUTOGUI: - return False - try: - pyautogui.click(x, y) - return True - except Exception: - return False - - @staticmethod - def double_click(x: int, y: int) -> bool: - if not HAS_PYAUTOGUI: - return False - try: - pyautogui.doubleClick(x, y) - return True - except Exception: - return False - - @staticmethod - def right_click(x: int, y: int) -> bool: - if not HAS_PYAUTOGUI: - return False - try: - pyautogui.rightClick(x, y) - return True - except Exception: - return False - - @staticmethod - def type_text(text: str) -> bool: - if not HAS_PYAUTOGUI: - return False - try: - pyautogui.typewrite(text, interval=0.02) - return True - except Exception: - return False - - @staticmethod - def press_key(key: str) -> bool: - if not HAS_PYAUTOGUI: - return False - key_map = { - "enter": "enter", "return": "enter", - "tab": "tab", "escape": "escape", "esc": "escape", - "backspace": "backspace", "delete": "delete", - "space": "space", "up": "up", "down": "down", - "left": "left", "right": "right", - "home": "home", "end": "end", - "page_up": "pageup", "page_down": "pagedown", - } - pyautogui_key = key_map.get(key.lower(), key) - try: - pyautogui.press(pyautogui_key) - return True - except Exception: - return False - - @staticmethod - def hotkey(*keys) -> bool: - if not HAS_PYAUTOGUI: - return False - key_map = { - "ctrl": "ctrl", "control": "ctrl", - "alt": "alt", "shift": "shift", - "super": "win", "win": "win", "meta": "win", - } - mapped = [key_map.get(k.lower(), k) for k in keys] - try: - pyautogui.hotkey(*mapped) - return True - except Exception: - return False - - @staticmethod - def scroll(amount: int) -> bool: - if not HAS_PYAUTOGUI: - return False - try: - pyautogui.scroll(amount) - return True - except Exception: - return False - - @staticmethod - def find_window(title: str) -> Optional[int]: - """查找窗口""" - if HAS_WIN32: - try: - hwnd = win32gui.FindWindow(None, title) - if hwnd: - return hwnd - except Exception: - pass - # 模糊搜索 - if HAS_WIN32: - found = [] - def callback(hwnd, _): - if win32gui.IsWindowVisible(hwnd): - window_title = win32gui.GetWindowText(hwnd) - if title.lower() in window_title.lower(): - found.append(hwnd) - return True - try: - win32gui.EnumWindows(callback, None) - if found: - return found[0] - except Exception: - pass - return None - - @staticmethod - def activate_window(window_id: int) -> bool: - if HAS_WIN32: - try: - win32gui.SetForegroundWindow(window_id) - return True - except Exception: - pass - return False - - @staticmethod - def close_window(window_id: int) -> bool: - if HAS_WIN32: - try: - win32gui.PostMessage(window_id, win32con.WM_CLOSE, 0, 0) - return True - except Exception: - pass - return False - - -# ═══════════════════════════════════════════════════════════════ -# 摄像头 — 真实实现 (opencv) -# ═══════════════════════════════════════════════════════════════ - -class WindowsCamera: - """真实摄像头 — 通过 OpenCV""" - - @staticmethod - def list_cameras() -> List[int]: - """列出可用摄像头""" - cameras = [] - if HAS_CV2: - for i in range(10): - cap = cv2.VideoCapture(i) - if cap.isOpened(): - cameras.append(i) - cap.release() - else: - break - return cameras - - @staticmethod - def capture(camera_id: int = 0, output_path: str = None) -> Optional[str]: - """拍照""" - if not HAS_CV2: - return None - cap = cv2.VideoCapture(camera_id) - if not cap.isOpened(): - return None - ret, frame = cap.read() - cap.release() - if not ret: - return None - if not output_path: - output_path = os.path.join(os.environ.get("TEMP", "C:\\Temp"), - f"camera_{int(time.time())}.jpg") - cv2.imwrite(output_path, frame) - return output_path - - @staticmethod - def get_camera_info(camera_id: int = 0) -> dict: - """获取摄像头信息""" - info = {"id": camera_id, "available": False, "width": 0, "height": 0} - if HAS_CV2: - cap = cv2.VideoCapture(camera_id) - if cap.isOpened(): - info["available"] = True - info["width"] = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) - info["height"] = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) - info["fps"] = cap.get(cv2.CAP_PROP_FPS) - cap.release() - return info - - -# ═══════════════════════════════════════════════════════════════ -# 进程/应用管理 — 真实实现 (psutil) -# ═══════════════════════════════════════════════════════════════ - -class WindowsProcess: - """真实进程/应用管理""" - - @staticmethod - def list_processes() -> List[dict]: - """列出所有进程""" - if HAS_PSUTIL: - procs = [] - for p in psutil.process_iter(['pid', 'name', 'username', 'memory_percent', - 'cpu_percent', 'status', 'cmdline']): - try: - info = p.info - procs.append({ - "pid": info['pid'], - "name": info['name'], - "user": info['username'], - "memory": info.get('memory_percent', 0), - "cpu": info.get('cpu_percent', 0), - "status": info['status'], - "cmdline": " ".join(info.get('cmdline') or []), - }) - except (psutil.NoSuchProcess, psutil.AccessDenied): - pass - return procs - # 回退到 tasklist - code, out, _ = _run("tasklist /fo csv /v") - if code == 0: - procs = [] - lines = out.split("\n") - for line in lines[1:]: - parts = line.split('","') - if len(parts) >= 8: - procs.append({ - "name": parts[0].strip('"'), - "pid": int(parts[1].strip('"')), - "user": parts[6].strip('"') if len(parts) > 6 else "", - "status": parts[7].strip('"') if len(parts) > 7 else "", - "memory": 0, - "cpu": 0, - "cmdline": "", - }) - return procs - return [] - - @staticmethod - def get_running_apps() -> List[dict]: - """获取运行中的应用 (GUI 窗口)""" - apps = [] - if HAS_WIN32: - def callback(hwnd, _): - if win32gui.IsWindowVisible(hwnd): - title = win32gui.GetWindowText(hwnd) - if title and title not in ("Default IME", "MSCTFIME UI"): - apps.append({"window_id": hwnd, "name": title}) - return True - try: - win32gui.EnumWindows(callback, None) - except Exception: - pass - return apps - - @staticmethod - def kill_process(pid: int) -> bool: - """终止进程""" - if HAS_PSUTIL: - try: - psutil.Process(pid).terminate() - return True - except Exception: - return False - code, _, _ = _run(f"taskkill /pid {pid} /f") - return code == 0 - - @staticmethod - def start_app(name: str) -> bool: - """启动应用""" - try: - os.startfile(name) - return True - except Exception: - pass - try: - subprocess.Popen(name, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - return True - except Exception: - return False - - @staticmethod - def get_system_info() -> dict: - """获取系统信息""" - info = { - "platform": platform.system(), - "hostname": socket.gethostname(), - "kernel": platform.release(), - "arch": platform.machine(), - "python": platform.python_version(), - "windows_edition": platform.win32_edition() if hasattr(platform, 'win32_edition') else "", - "windows_version": platform.version(), - } - if HAS_PSUTIL: - info["cpu_count"] = psutil.cpu_count() - cpu_freq = psutil.cpu_freq() - info["cpu_freq"] = cpu_freq.current if cpu_freq else 0 - boot_time = psutil.boot_time() - info["uptime"] = time.time() - boot_time - mem = psutil.virtual_memory() - info["memory_total"] = mem.total - info["memory_used"] = mem.used - info["memory_percent"] = mem.percent - disk = psutil.disk_usage('C:\\') - info["disk_total"] = disk.total - info["disk_used"] = disk.used - info["disk_percent"] = disk.percent - return info - - -# ═══════════════════════════════════════════════════════════════ -# Shell — 真实实现 -# ═══════════════════════════════════════════════════════════════ - -class WindowsShell: - """真实 Shell 执行""" - - @staticmethod - def exec(command: str, timeout: int = 30, root: bool = False) -> dict: - """执行 Shell 命令""" - if root: - # 以管理员权限运行 - script = f'Start-Process cmd -ArgumentList "/c {command}" -Verb RunAs -Wait' - code, out, err = _run_ps(script, timeout=timeout) - else: - code, out, err = _run(command, timeout=timeout) - return { - "success": code == 0, - "output": out + ("\n" + err if err else ""), - "exit_code": code, - "stdout": out, - "stderr": err, - } - - @staticmethod - def exec_async(command: str) -> int: - """异步执行命令,返回 PID""" - try: - proc = subprocess.Popen(command, shell=True, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) - return proc.pid - except Exception: - return -1 - - -# ═══════════════════════════════════════════════════════════════ -# 统一入口 -# ═══════════════════════════════════════════════════════════════ - -def get_windows_capabilities() -> dict: - """获取当前 Windows 环境的可用能力""" - return { - "is_windows": IS_WINDOWS, - "network": True, - "bluetooth": HAS_POWERSHELL, - "audio": HAS_PYCAW or HAS_POWERSHELL, - "clipboard": HAS_PYPERCLIP, - "ui_automation": HAS_PYAUTOGUI, - "notifications": HAS_WIN10TOAST or HAS_POWERSHELL, - "sensors": HAS_WMI or HAS_PSUTIL, - "display": True, - "camera": HAS_CV2, - "ffmpeg": HAS_FFMPEG, - "ffprobe": HAS_FFPROBE, - "psutil": HAS_PSUTIL, - "requests": HAS_REQUESTS, - "PIL": HAS_PIL, - "opencv": HAS_CV2, - "numpy": HAS_NUMPY, - "wmi": HAS_WMI, - "pyautogui": HAS_PYAUTOGUI, - "pyperclip": HAS_PYPERCLIP, - "pycaw": HAS_PYCAW, - "win32": HAS_WIN32, - "screeninfo": HAS_SCREENINFO, - "win10toast": HAS_WIN10TOAST, - } - - -# 导出所有实现类 -__all__ = [ - "IS_WINDOWS", "get_windows_capabilities", - "WindowsNetwork", "WindowsBluetooth", "WindowsSensors", - "WindowsAudio", "WindowsClipboard", "WindowsPower", - "WindowsLocation", "WindowsNotify", "WindowsDisplay", - "WindowsUIAutomation", "WindowsCamera", "WindowsProcess", - "WindowsShell", - # 能力标志 - "HAS_POWERSHELL", "HAS_FFMPEG", "HAS_FFPROBE", "HAS_PSUTIL", - "HAS_REQUESTS", "HAS_CV2", "HAS_NUMPY", "HAS_PIL", - "HAS_PYAUTOGUI", "HAS_PYPERCLIP", "HAS_WIN10TOAST", - "HAS_WMI", "HAS_WIN32", "HAS_PYCAW", "HAS_SCREENINFO", -] diff --git a/android_pyapi/android/__init__.py b/android_pyapi/android/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/android_pyapi/android/accessibilityservice.py b/android_pyapi/android/accessibilityservice.py deleted file mode 100644 index e07e123..0000000 --- a/android_pyapi/android/accessibilityservice.py +++ /dev/null @@ -1,382 +0,0 @@ -""" -Android accessibilityservice 组件 — AccessibilityService, AccessibilityServiceInfo, GestureDescription 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import threading -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityServiceInfo -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityServiceInfo(Stub): - """AccessibilityServiceInfo — 无障碍服务信息""" - - # Flags - FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x00000001 - FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000002 - FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000004 - FLAG_REPORT_VIEW_IDS = 0x00000010 - FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 0x00000020 - FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000040 - FLAG_REQUEST_ACCESSIBILITY_BUTTON = 0x00000100 - FLAG_SERVICE_HANDLES_DOUBLETAP = 0x00000200 - FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000400 - FLAG_REQUEST_SHORTCUT_WARN_DIALOG = 0x00000800 - FLAG_SEND_TRACE_TO_DEBUG_TOOL = 0x00001000 - - # Feedback types - FEEDBACK_SPOKEN = 0x00000001 - FEEDBACK_HAPTIC = 0x00000002 - FEEDBACK_AUDIBLE = 0x00000004 - FEEDBACK_VISUAL = 0x00000008 - FEEDBACK_GENERIC = 0x00000010 - FEEDBACK_BRAILLE = 0x00000020 - - # Capabilities - CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 0x00000001 - CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 0x00000002 - CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000004 - CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 0x00000008 - CAPABILITY_CAN_CONTROL_MAGNIFICATION = 0x00000010 - CAPABILITY_CAN_PERFORM_GESTURES = 0x00000020 - CAPABILITY_CAN_TAKE_SCREENSHOT = 0x00000040 - - def __init__(self): - super().__init__() - self.eventTypes: int = 0 - self.feedbackType: int = self.FEEDBACK_GENERIC - self.flags: int = 0 - self.notificationTimeout: int = 0 - self.fetchDescription: Optional[str] = None - self.capabilities: int = 0 - self.packageNames: Optional[List[str]] = None - - def toString(self) -> str: - return (f"AccessibilityServiceInfo{{eventTypes={self.eventTypes}, " - f"feedbackType={self.feedbackType}, flags={self.flags}}}") - - -# ═══════════════════════════════════════════════════════════════ -# GestureDescription -# ═══════════════════════════════════════════════════════════════ - -class GestureDescription(Stub): - """GestureDescription — 手势描述""" - - class StrokeDescription(Stub): - """GestureDescription.StrokeDescription — 笔画描述""" - - def __init__(self, path: Any, startTime: int, duration: int): - super().__init__() - self._path = path - self._start_time = startTime - self._duration = duration - - def getPath(self) -> Any: - return self._path - - def getStartTime(self) -> int: - return self._start_time - - def getDuration(self) -> int: - return self._duration - - def toString(self) -> str: - return f"StrokeDescription{{startTime={self._start_time}, duration={self._duration}}}" - - _MAX_STROKE_COUNT = 10 - - @staticmethod - def getMaxStrokeCount() -> int: - return GestureDescription._MAX_STROKE_COUNT - - def __init__(self): - super().__init__() - self._strokes: List[GestureDescription.StrokeDescription] = [] - - class Builder(Stub): - """GestureDescription.Builder — 构建器""" - - def __init__(self): - super().__init__() - self._strokes: List[GestureDescription.StrokeDescription] = [] - - def addStroke(self, stroke: GestureDescription.StrokeDescription) -> "GestureDescription.Builder": - if len(self._strokes) < GestureDescription.getMaxStrokeCount(): - self._strokes.append(stroke) - return self - - def build(self) -> "GestureDescription": - desc = GestureDescription() - desc._strokes = list(self._strokes) - return desc - - -# ═══════════════════════════════════════════════════════════════ -# GestureResultCallback -# ═══════════════════════════════════════════════════════════════ - -class GestureResultCallback(Stub): - """GestureResultCallback — 手势结果回调""" - - def onCompleted(self, gestureDescription: GestureDescription): - pass - - def onCancelled(self, gestureDescription: GestureDescription): - pass - - def onGestureCancelled(self, gestureDescription: GestureDescription): - self.onCancelled(gestureDescription) - - def onGestureCompleted(self, gestureDescription: GestureDescription): - self.onCompleted(gestureDescription) - - -# ═══════════════════════════════════════════════════════════════ -# MagnificationConfig -# ═══════════════════════════════════════════════════════════════ - -class MagnificationConfig(Stub): - """MagnificationConfig — 放大配置""" - - MAGNIFICATION_MODE_FULLSCREEN = 1 - MAGNIFICATION_MODE_WINDOW = 2 - - def __init__(self): - super().__init__() - self._mode: int = self.MAGNIFICATION_MODE_FULLSCREEN - self._center_x: float = 0.0 - self._center_y: float = 0.0 - self._scale: float = 1.0 - - def getMode(self) -> int: - return self._mode - - def getCenterX(self) -> float: - return self._center_x - - def getCenterY(self) -> float: - return self._center_y - - def getScale(self) -> float: - return self._scale - - def setMode(self, mode: int): - self._mode = mode - - def setCenterX(self, centerX: float): - self._center_x = centerX - - def setCenterY(self, centerY: float): - self._center_y = centerY - - def setScale(self, scale: float): - self._scale = scale - - def toString(self) -> str: - return f"MagnificationConfig{{mode={self._mode}, scale={self._scale}}}" - - -# ═══════════════════════════════════════════════════════════════ -# SoftKeyboardController -# ═══════════════════════════════════════════════════════════════ - -class SoftKeyboardController(Stub): - """SoftKeyboardController — 软键盘控制器""" - - SHOW_MODE_AUTO = 0 - SHOW_MODE_HIDDEN = 1 - SHOW_MODE_IGNORE_HARD_KEYBOARD = 2 - SHOW_MODE_HIDDEN_IDLE_TIMEOUT = 3 - - def __init__(self): - super().__init__() - self._show_mode: int = self.SHOW_MODE_AUTO - self._listeners: List[Callable[[int], None]] = [] - - def setShowMode(self, showMode: int): - self._show_mode = showMode - - def getShowMode(self) -> int: - return self._show_mode - - def addOnShowModeChangedListener(self, listener: Callable[[int], None]): - if listener not in self._listeners: - self._listeners.append(listener) - - def removeOnShowModeChangedListener(self, listener: Callable[[int], None]): - if listener in self._listeners: - self._listeners.remove(listener) - - def toString(self) -> str: - return f"SoftKeyboardController@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# FingerprintGestureController -# ═══════════════════════════════════════════════════════════════ - -class FingerprintGestureController(Stub): - """FingerprintGestureController — 指纹手势控制器""" - - def __init__(self): - super().__init__() - self._callbacks: List[Any] = [] - - def isGestureAvailable(self) -> bool: - return False - - def onGestureDetectionActiveChange(self, active: bool): - pass - - def registerFingerprintGestureCallback(self, callback: Any, handler: Any = None): - if callback not in self._callbacks: - self._callbacks.append(callback) - - def unregisterFingerprintGestureCallback(self, callback: Any): - if callback in self._callbacks: - self._callbacks.remove(callback) - - def toString(self) -> str: - return f"FingerprintGestureController@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# TouchInteractionController -# ═══════════════════════════════════════════════════════════════ - -class TouchInteractionController(Stub): - """TouchInteractionController — 触控交互控制器""" - - def __init__(self): - super().__init__() - - def performClick(self, x: int, y: int) -> bool: - return True - - def performLongClick(self, x: int, y: int) -> bool: - return True - - def requestDefer(self, timeout: int): - pass - - def clearCache(self): - pass - - def toString(self) -> str: - return f"TouchInteractionController@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityService -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityService(Stub): - """AccessibilityService — 无障碍服务""" - - # Global actions - GLOBAL_ACTION_BACK = 1 - GLOBAL_ACTION_HOME = 2 - GLOBAL_ACTION_RECENTS = 3 - GLOBAL_ACTION_NOTIFICATIONS = 4 - GLOBAL_ACTION_QUICK_SETTINGS = 5 - GLOBAL_ACTION_POWER_DIALOG = 6 - GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7 - GLOBAL_ACTION_LOCK_SCREEN = 8 - GLOBAL_ACTION_TAKE_SCREENSHOT = 9 - - # Show modes - SHOW_MODE_AUTO = SoftKeyboardController.SHOW_MODE_AUTO - SHOW_MODE_HIDDEN = SoftKeyboardController.SHOW_MODE_HIDDEN - SHOW_MODE_IGNORE_HARD_KEYBOARD = SoftKeyboardController.SHOW_MODE_IGNORE_HARD_KEYBOARD - SHOW_MODE_HIDDEN_IDLE_TIMEOUT = SoftKeyboardController.SHOW_MODE_HIDDEN_IDLE_TIMEOUT - - # Flags - FLAG_REQUEST_TOUCH_EXPLORATION_MODE = AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE - FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = AccessibilityServiceInfo.FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY - FLAG_REQUEST_FILTER_KEY_EVENTS = AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS - FLAG_REPORT_VIEW_IDS = AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS - FLAG_RETRIEVE_INTERACTIVE_WINDOWS = AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS - FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS - FLAG_REQUEST_ACCESSIBILITY_BUTTON = AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON - FLAG_SERVICE_HANDLES_DOUBLETAP = AccessibilityServiceInfo.FLAG_SERVICE_HANDLES_DOUBLETAP - FLAG_REQUEST_MULTI_FINGER_GESTURES = AccessibilityServiceInfo.FLAG_REQUEST_MULTI_FINGER_GESTURES - FLAG_REQUEST_SHORTCUT_WARN_DIALOG = AccessibilityServiceInfo.FLAG_REQUEST_SHORTCUT_WARN_DIALOG - FLAG_SEND_TRACE_TO_DEBUG_TOOL = AccessibilityServiceInfo.FLAG_SEND_TRACE_TO_DEBUG_TOOL - - def __init__(self): - super().__init__() - self._service_info = AccessibilityServiceInfo() - self._soft_keyboard_controller = SoftKeyboardController() - self._fingerprint_gesture_controller = FingerprintGestureController() - self._touch_interaction_controller = TouchInteractionController() - - # ── Lifecycle ── - - def onAccessibilityEvent(self, event: Any): - pass - - def onInterrupt(self): - pass - - def onServiceConnected(self): - pass - - def setServiceInfo(self, info: AccessibilityServiceInfo): - self._service_info = info - - def getServiceInfo(self) -> AccessibilityServiceInfo: - return self._service_info - - # ── Window ── - - def getWindows(self) -> List[Any]: - return [] - - def getRootInActiveWindow(self) -> Any: - return None - - def findFocus(self, focusType: int) -> Any: - return None - - # ── Actions ── - - def performGlobalAction(self, action: int) -> bool: - return True - - # ── Controllers ── - - def getSoftKeyboardController(self) -> SoftKeyboardController: - return self._soft_keyboard_controller - - def setGestureDetectionPassthrough(self, passthrough: bool): - pass - - def toString(self) -> str: - return f"AccessibilityService@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "AccessibilityService", - "AccessibilityServiceInfo", - "GestureDescription", - "GestureResultCallback", - "MagnificationConfig", - "SoftKeyboardController", - "FingerprintGestureController", - "TouchInteractionController", -] \ No newline at end of file diff --git a/android_pyapi/android/accounts.py b/android_pyapi/android/accounts.py deleted file mode 100644 index 4d6d4e5..0000000 --- a/android_pyapi/android/accounts.py +++ /dev/null @@ -1,456 +0,0 @@ -""" -Android accounts 组件 — AccountManager, Account, AuthenticatorDescription 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import threading -import uuid -from typing import Any, Callable, Dict, List, Optional, Set - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# Account -# ═══════════════════════════════════════════════════════════════ - -class Account(Stub): - """Account — 账户""" - - def __init__(self, name: str, type_: str): - super().__init__() - self.name: str = name - self.type: str = type_ - self._extras: Dict[str, Any] = {} - - def toString(self) -> str: - return f"Account{{name={self.name}, type={self.type}}}" - - def describeContents(self) -> int: - return 0 - - def hashCode(self) -> int: - return hash((self.name, self.type)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, Account): - return False - return self.name == other.name and self.type == other.type - - -# ═══════════════════════════════════════════════════════════════ -# AccountManagerFuture -# ═══════════════════════════════════════════════════════════════ - -class AccountManagerFuture(Stub): - """AccountManagerFuture — 异步结果""" - - def __init__(self): - super().__init__() - self._result: Any = None - self._exception: Optional[Exception] = None - self._done: bool = False - self._event = threading.Event() - - def getResult(self) -> Any: - self._event.wait() - if self._exception: - raise self._exception - return self._result - - def getException(self) -> Optional[Exception]: - return self._exception - - def isDone(self) -> bool: - return self._done - - def isCancelled(self) -> bool: - return False - - def cancel(self, mayInterruptIfRunning: bool) -> bool: - return False - - def _set_result(self, result: Any): - self._result = result - self._done = True - self._event.set() - - def _set_exception(self, exc: Exception): - self._exception = exc - self._done = True - self._event.set() - - -# ═══════════════════════════════════════════════════════════════ -# AccountManagerCallback -# ═══════════════════════════════════════════════════════════════ - -class AccountManagerCallback(Stub): - """AccountManagerCallback — 回调接口""" - - def run(self, future: AccountManagerFuture): - pass - - -# ═══════════════════════════════════════════════════════════════ -# AuthenticatorDescription -# ═══════════════════════════════════════════════════════════════ - -class AuthenticatorDescription(Stub): - """AuthenticatorDescription — 认证器描述""" - - KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse" - - def __init__(self, type_: str, packageName: str = "", - labelId: int = 0, iconId: int = 0, - smallIconId: int = 0, accountPreferencesId: int = 0, - customTokens: bool = False, prefMinId: int = 0): - super().__init__() - self.type: str = type_ - self.packageName: str = packageName - self.labelId: int = labelId - self.iconId: int = iconId - self.smallIconId: int = smallIconId - self.accountPreferencesId: int = accountPreferencesId - self.customTokens: bool = customTokens - self.prefMinId: int = prefMinId - - def toString(self) -> str: - return f"AuthenticatorDescription{{type={self.type}}}" - - def hashCode(self) -> int: - return hash(self.type) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, AuthenticatorDescription): - return False - return self.type == other.type - - -# ═══════════════════════════════════════════════════════════════ -# OnAccountsUpdateListener -# ═══════════════════════════════════════════════════════════════ - -class OnAccountsUpdateListener(Stub): - """OnAccountsUpdateListener — 账户更新监听器""" - - def onAccountsUpdated(self, accounts: Optional[List[Account]]): - pass - - -# ═══════════════════════════════════════════════════════════════ -# AccountManager -# ═══════════════════════════════════════════════════════════════ - -class AccountManager(Stub): - """AccountManager — 账户管理器""" - - ACTION_ACCOUNT_REMOVED = "android.accounts.action.ACCOUNT_REMOVED" - ACTION_VISIBLE_ACCOUNTS_CHANGED = "android.accounts.action.VISIBLE_ACCOUNTS_CHANGED" - KEY_ACCOUNTS = "accounts" - KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse" - KEY_ACCOUNT_TYPE = "accountType" - KEY_AUTHTOKEN = "authtoken" - KEY_AUTH_FAILED_MESSAGE = "authFailedMessage" - KEY_AUTH_TOKEN_LABEL = "authTokenLabelKey" - KEY_BOOLEAN_RESULT = "booleanResult" - KEY_ERROR_CODE = "errorCode" - KEY_ERROR_MESSAGE = "errorMessage" - KEY_INTENT = "intent" - KEY_LAST_SEEN_PIN = "lastSeenPin" - KEY_PASSWORD = "password" - KEY_USERDATA = "userdata" - LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED" - PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE = "android.accounts.legacy_not_visible" - PACKAGE_NAME_KEY_LEGACY_VISIBLE = "android.accounts.legacy_visible" - PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE = "android.accounts.legacy_not_visible" - - ERROR_CODE_REMOTE_EXCEPTION = 1 - ERROR_CODE_NETWORK_ERROR = 2 - ERROR_CODE_CANCELED = 3 - ERROR_CODE_INVALID_RESPONSE = 4 - ERROR_CODE_UNSUPPORTED_OPERATION = 5 - ERROR_CODE_BAD_ARGUMENTS = 6 - ERROR_CODE_BAD_AUTHENTICATION = 7 - ERROR_CODE_BAD_REQUEST = 8 - ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE = 101 - - def __init__(self): - super().__init__() - self._lock = threading.Lock() - self._accounts: Dict[str, Account] = {} - self._passwords: Dict[str, str] = {} - self._userdata: Dict[str, Dict[str, str]] = {} - self._auth_tokens: Dict[str, Dict[str, str]] = {} - self._listeners: List[OnAccountsUpdateListener] = [] - self._listener_handler: Dict[OnAccountsUpdateListener, Any] = {} - - @staticmethod - def get(context: Any = None) -> "AccountManager": - return AccountManager() - - # ── Account listing ── - - def getAccounts(self) -> List[Account]: - with self._lock: - return list(self._accounts.values()) - - def getAccountsByType(self, type_: str) -> List[Account]: - with self._lock: - return [a for a in self._accounts.values() if a.type == type_] - - def getAccountsByTypeAndFeatures(self, type_: str, - features: List[str], - callback: Optional[AccountManagerCallback] = None, - handler: Any = None) -> AccountManagerFuture: - future = AccountManagerFuture() - accounts = self.getAccountsByType(type_) - if callback: - callback.run(future) - future._set_result(accounts) - return future - - def getAccountsForPackage(self, packageName: str, - uid: int = -1) -> List[Account]: - return self.getAccounts() - - def getSharedAccounts(self, userId: int) -> List[Account]: - return self.getAccounts() - - # ── CRUD ── - - def addAccount(self, accountType: str, authTokenType: Optional[str] = None, - requiredFeatures: Optional[List[str]] = None, - addAccountOptions: Optional[Dict[str, Any]] = None, - activity: Any = None, - callback: Optional[AccountManagerCallback] = None, - handler: Any = None) -> AccountManagerFuture: - future = AccountManagerFuture() - name = f"account_{uuid.uuid4().hex[:8]}" - account = Account(name, accountType) - with self._lock: - self._accounts[f"{name}:{accountType}"] = account - future._set_result(None) - if callback: - callback.run(future) - return future - - def addAccountExplicitly(self, account: Account, - password: Optional[str] = None, - userdata: Optional[Dict[str, Any]] = None) -> bool: - key = f"{account.name}:{account.type}" - with self._lock: - if key in self._accounts: - return False - self._accounts[key] = account - if password is not None: - self._passwords[key] = password - if userdata: - self._userdata[key] = {k: str(v) for k, v in userdata.items()} - return True - - def removeAccount(self, account: Account, - callback: Optional[AccountManagerCallback] = None, - handler: Any = None) -> AccountManagerFuture: - future = AccountManagerFuture() - key = f"{account.name}:{account.type}" - with self._lock: - self._accounts.pop(key, None) - self._passwords.pop(key, None) - self._userdata.pop(key, None) - self._auth_tokens.pop(key, None) - future._set_result(True) - if callback: - callback.run(future) - return future - - def renameAccount(self, account: Account, newName: str) -> AccountManagerFuture: - future = AccountManagerFuture() - key = f"{account.name}:{account.type}" - with self._lock: - if key in self._accounts: - new_key = f"{newName}:{account.type}" - self._accounts[new_key] = Account(newName, account.type) - if key in self._passwords: - self._passwords[new_key] = self._passwords.pop(key) - if key in self._userdata: - self._userdata[new_key] = self._userdata.pop(key) - if key in self._auth_tokens: - self._auth_tokens[new_key] = self._auth_tokens.pop(key) - del self._accounts[key] - future._set_result(account) - return future - - # ── Password ── - - def getPassword(self, account: Account) -> Optional[str]: - key = f"{account.name}:{account.type}" - with self._lock: - return self._passwords.get(key) - - def setPassword(self, account: Account, password: str): - key = f"{account.name}:{account.type}" - with self._lock: - if password: - self._passwords[key] = password - else: - self._passwords.pop(key, None) - - def clearPassword(self, account: Account): - self.setPassword(account, "") - - # ── User data ── - - def getUserData(self, account: Account, key: str) -> Optional[str]: - ak = f"{account.name}:{account.type}" - with self._lock: - userdata = self._userdata.get(ak, {}) - return userdata.get(key) - - def setUserData(self, account: Account, key: str, value: str): - ak = f"{account.name}:{account.type}" - with self._lock: - if ak not in self._userdata: - self._userdata[ak] = {} - self._userdata[ak][key] = value - - # ── Auth tokens ── - - def getAuthToken(self, account: Account, authTokenType: str, - notifyAuthFailure: bool = True, - callback: Optional[AccountManagerCallback] = None, - handler: Any = None) -> AccountManagerFuture: - future = AccountManagerFuture() - key = f"{account.name}:{account.type}" - with self._lock: - tokens = self._auth_tokens.get(key, {}) - token = tokens.get(authTokenType) - future._set_result(token) - if callback: - callback.run(future) - return future - - def peekAuthToken(self, account: Account, authTokenType: str) -> Optional[str]: - key = f"{account.name}:{account.type}" - with self._lock: - tokens = self._auth_tokens.get(key, {}) - return tokens.get(authTokenType) - - def addAuthToken(self, account: Account, authTokenType: str, authToken: str): - key = f"{account.name}:{account.type}" - with self._lock: - if key not in self._auth_tokens: - self._auth_tokens[key] = {} - self._auth_tokens[key][authTokenType] = authToken - - def setAuthToken(self, account: Account, authTokenType: str, authToken: str): - self.addAuthToken(account, authTokenType, authToken) - - def invalidateAuthToken(self, accountType: str, authToken: str): - with self._lock: - for key in list(self._auth_tokens.keys()): - if key.endswith(f":{accountType}"): - tokens = self._auth_tokens[key] - for ttype, token in list(tokens.items()): - if token == authToken: - del tokens[ttype] - - # ── Features ── - - def hasFeatures(self, account: Account, features: List[str], - callback: AccountManagerCallback, handler: Any = None) -> AccountManagerFuture: - future = AccountManagerFuture() - future._set_result(True) - if callback: - callback.run(future) - return future - - # ── Create account ── - - def createAccount(self, accountType: str, authTokenType: Optional[str] = None, - requiredFeatures: Optional[List[str]] = None, - options: Optional[Dict[str, Any]] = None, - activity: Any = None, - callback: Optional[AccountManagerCallback] = None, - handler: Any = None) -> AccountManagerFuture: - return self.addAccount(accountType, authTokenType, requiredFeatures, - options, activity, callback, handler) - - # ── Shared accounts ── - - def removeSharedAccount(self, account: Account, - callback: Optional[AccountManagerCallback] = None, - handler: Any = None) -> AccountManagerFuture: - return self.removeAccount(account, callback, handler) - - def updateAppPermission(self, account: Account, authTokenType: str, uid: int, value: bool): - pass - - def setAuthTokenType(self, account: Account, authTokenType: str): - pass - - # ── Confirm / Edit ── - - def confirmCredentials(self, account: Account, - options: Optional[Dict[str, Any]] = None, - activity: Any = None, - callback: Optional[AccountManagerCallback] = None, - handler: Any = None) -> AccountManagerFuture: - future = AccountManagerFuture() - future._set_result(None) - if callback: - callback.run(future) - return future - - def editProperties(self, accountType: str, - activity: Any = None, - callback: Optional[AccountManagerCallback] = None, - handler: Any = None) -> AccountManagerFuture: - future = AccountManagerFuture() - future._set_result(None) - if callback: - callback.run(future) - return future - - # ── Listeners ── - - def addOnAccountsUpdatedListener(self, listener: OnAccountsUpdateListener, - handler: Any = None, - updateImmediately: bool = False): - with self._lock: - self._listeners.append(listener) - self._listener_handler[listener] = handler - - def removeOnAccountsUpdatedListener(self, listener: OnAccountsUpdateListener): - with self._lock: - if listener in self._listeners: - self._listeners.remove(listener) - self._listener_handler.pop(listener, None) - - def _notify_listeners(self): - with self._lock: - accounts = list(self._accounts.values()) - for listener in self._listeners: - listener.onAccountsUpdated(accounts) - - def toString(self) -> str: - return f"AccountManager@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Account", - "AccountManager", - "AccountManagerFuture", - "AccountManagerCallback", - "AuthenticatorDescription", - "OnAccountsUpdateListener", -] \ No newline at end of file diff --git a/android_pyapi/android/animation.py b/android_pyapi/android/animation.py deleted file mode 100644 index 5c03bc1..0000000 --- a/android_pyapi/android/animation.py +++ /dev/null @@ -1,951 +0,0 @@ -""" -Android animation 组件 — Animator, ValueAnimator, ObjectAnimator, 插值器等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import math -import threading -import time as _time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, TypeVar - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# TypeEvaluator -# ═══════════════════════════════════════════════════════════════ - -class TypeEvaluator(Stub): - """TypeEvaluator — 类型估值器接口""" - - def evaluate(self, fraction: float, startValue: Any, endValue: Any) -> Any: - return startValue - - -class FloatEvaluator(TypeEvaluator): - """FloatEvaluator — 浮点估值器""" - - def evaluate(self, fraction: float, startValue: Any, endValue: Any) -> float: - sv = float(startValue) - ev = float(endValue) - return sv + fraction * (ev - sv) - - -class IntEvaluator(TypeEvaluator): - """IntEvaluator — 整数估值器""" - - def evaluate(self, fraction: float, startValue: Any, endValue: Any) -> int: - sv = int(startValue) - ev = int(endValue) - return int(sv + fraction * (ev - sv)) - - -class PointFEvaluator(TypeEvaluator): - """PointFEvaluator — PointF 估值器""" - - def evaluate(self, fraction: float, startValue: Any, endValue: Any) -> Any: - if hasattr(startValue, 'x') and hasattr(startValue, 'y'): - sv_x, sv_y = startValue.x, startValue.y - ev_x, ev_y = endValue.x, endValue.y - class PointF: - def __init__(self, x, y): - self.x = x - self.y = y - return PointF(sv_x + fraction * (ev_x - sv_x), - sv_y + fraction * (ev_y - sv_y)) - return startValue - - -class RectEvaluator(TypeEvaluator): - """RectEvaluator — Rect 估值器""" - - def evaluate(self, fraction: float, startValue: Any, endValue: Any) -> Any: - if hasattr(startValue, 'left') and hasattr(startValue, 'top'): - class Rect: - def __init__(self, l, t, r, b): - self.left = l - self.top = t - self.right = r - self.bottom = b - return Rect( - int(startValue.left + fraction * (endValue.left - startValue.left)), - int(startValue.top + fraction * (endValue.top - startValue.top)), - int(startValue.right + fraction * (endValue.right - startValue.right)), - int(startValue.bottom + fraction * (endValue.bottom - startValue.bottom)), - ) - return startValue - - -class ArgbEvaluator(TypeEvaluator): - """ArgbEvaluator — ARGB 颜色估值器""" - - def evaluate(self, fraction: float, startValue: Any, endValue: Any) -> int: - sv = int(startValue) - ev = int(endValue) - a = ((sv >> 24) & 0xFF) + int(fraction * (((ev >> 24) & 0xFF) - ((sv >> 24) & 0xFF))) - r = ((sv >> 16) & 0xFF) + int(fraction * (((ev >> 16) & 0xFF) - ((sv >> 16) & 0xFF))) - g = ((sv >> 8) & 0xFF) + int(fraction * (((ev >> 8) & 0xFF) - ((sv >> 8) & 0xFF))) - b = (sv & 0xFF) + int(fraction * ((ev & 0xFF) - (sv & 0xFF))) - return (min(a, 255) << 24) | (min(r, 255) << 16) | (min(g, 255) << 8) | min(b, 255) - - -# ═══════════════════════════════════════════════════════════════ -# TimeInterpolator -# ═══════════════════════════════════════════════════════════════ - -class TimeInterpolator(Stub): - """TimeInterpolator — 时间插值器接口""" - - def getInterpolation(self, input_: float) -> float: - return input_ - - -class AccelerateDecelerateInterpolator(TimeInterpolator): - """AccelerateDecelerateInterpolator — 加速减速插值器""" - - def getInterpolation(self, input_: float) -> float: - return (math.cos((input_ + 1) * math.pi) / 2.0) + 0.5 - - -class AccelerateInterpolator(TimeInterpolator): - """AccelerateInterpolator — 加速插值器""" - - def __init__(self, factor: float = 1.0): - super().__init__() - self._factor = factor - - def getInterpolation(self, input_: float) -> float: - if self._factor == 1.0: - return input_ * input_ - return input_ ** (2.0 * self._factor) - - -class DecelerateInterpolator(TimeInterpolator): - """DecelerateInterpolator — 减速插值器""" - - def __init__(self, factor: float = 1.0): - super().__init__() - self._factor = factor - - def getInterpolation(self, input_: float) -> float: - if self._factor == 1.0: - return 1.0 - (1.0 - input_) * (1.0 - input_) - return 1.0 - (1.0 - input_) ** (2.0 * self._factor) - - -class LinearInterpolator(TimeInterpolator): - """LinearInterpolator — 线性插值器""" - - def getInterpolation(self, input_: float) -> float: - return input_ - - -class BounceInterpolator(TimeInterpolator): - """BounceInterpolator — 弹跳插值器""" - - def getInterpolation(self, input_: float) -> float: - t = input_ - if t < 0.363636: - return 7.5625 * t * t - elif t < 0.727272: - t -= 0.545454 - return 7.5625 * t * t + 0.75 - elif t < 0.909090: - t -= 0.818181 - return 7.5625 * t * t + 0.9375 - else: - t -= 0.954545 - return 7.5625 * t * t + 0.984375 - - -class AnticipateInterpolator(TimeInterpolator): - """AnticipateInterpolator — 预期插值器""" - - def __init__(self, tension: float = 2.0): - super().__init__() - self._tension = tension - - def getInterpolation(self, input_: float) -> float: - return input_ * input_ * ((self._tension + 1) * input_ - self._tension) - - -class OvershootInterpolator(TimeInterpolator): - """OvershootInterpolator — 过冲插值器""" - - def __init__(self, tension: float = 2.0): - super().__init__() - self._tension = tension - - def getInterpolation(self, input_: float) -> float: - t = input_ - 1.0 - return t * t * ((self._tension + 1) * t + self._tension) + 1.0 - - -class AnticipateOvershootInterpolator(TimeInterpolator): - """AnticipateOvershootInterpolator — 预期过冲插值器""" - - def __init__(self, tension: float = 2.0, extraTension: float = 1.5): - super().__init__() - self._tension = tension - self._extra_tension = extraTension - - def getInterpolation(self, input_: float) -> float: - if input_ < 0.5: - t = input_ * 2.0 - return 0.5 * t * t * ((self._tension + 1) * t - self._tension) - else: - t = (input_ - 0.5) * 2.0 - 1.0 - return 0.5 * (t * t * ((self._tension + 1) * t + self._tension) + 2.0) - - -class CycleInterpolator(TimeInterpolator): - """CycleInterpolator — 周期插值器""" - - def __init__(self, cycles: float = 1.0): - super().__init__() - self._cycles = cycles - - def getInterpolation(self, input_: float) -> float: - return math.sin(2 * math.pi * self._cycles * input_) - - -class PathInterpolator(TimeInterpolator): - """PathInterpolator — 路径插值器""" - - def __init__(self, controlX1: float = 0.0, controlY1: float = 0.0, - controlX2: float = 1.0, controlY2: float = 1.0): - super().__init__() - self._cx1 = controlX1 - self._cy1 = controlY1 - self._cx2 = controlX2 - self._cy2 = controlY2 - - def getInterpolation(self, input_: float) -> float: - # Cubic bezier approximation - t = input_ - return (3 * self._cy1 * t * (1 - t) ** 2 + - 3 * self._cy2 * t ** 2 * (1 - t) + - t ** 3) - - -# ═══════════════════════════════════════════════════════════════ -# AnimatorListener -# ═══════════════════════════════════════════════════════════════ - -class AnimatorListener(Stub): - """AnimatorListener — 动画监听器""" - - def onAnimationStart(self, animation: "Animator"): - pass - - def onAnimationEnd(self, animation: "Animator"): - pass - - def onAnimationCancel(self, animation: "Animator"): - pass - - def onAnimationRepeat(self, animation: "Animator"): - pass - - -class AnimatorPauseListener(Stub): - """AnimatorPauseListener — 动画暂停监听器""" - - def onAnimationPause(self, animation: "Animator"): - pass - - def onAnimationResume(self, animation: "Animator"): - pass - - -# ═══════════════════════════════════════════════════════════════ -# Animator -# ═══════════════════════════════════════════════════════════════ - -class Animator(Stub): - """Animator — 动画基类""" - - def __init__(self): - super().__init__() - self._duration: int = 300 - self._start_delay: int = 0 - self._interpolator: Optional[TimeInterpolator] = None - self._listeners: List[AnimatorListener] = [] - self._pause_listeners: List[AnimatorPauseListener] = [] - self._running: bool = False - self._started: bool = False - self._paused: bool = False - self._start_time: float = 0.0 - - def start(self): - self._running = True - self._started = True - self._start_time = _time.time() + self._start_delay / 1000.0 - self._notify_start() - - def end(self): - if self._running: - self._running = False - self._notify_end() - - def cancel(self): - if self._running: - self._running = False - self._started = False - self._notify_cancel() - - def pause(self): - if self._running and not self._paused: - self._paused = True - for l in self._pause_listeners: - l.onAnimationPause(self) - - def resume(self): - if self._paused: - self._paused = False - for l in self._pause_listeners: - l.onAnimationResume(self) - - def isRunning(self) -> bool: - return self._running - - def isStarted(self) -> bool: - return self._started - - def isPaused(self) -> bool: - return self._paused - - def addListener(self, listener: AnimatorListener): - if listener not in self._listeners: - self._listeners.append(listener) - - def removeListener(self, listener: AnimatorListener): - if listener in self._listeners: - self._listeners.remove(listener) - - def getListeners(self) -> List[AnimatorListener]: - return list(self._listeners) - - def addPauseListener(self, listener: AnimatorPauseListener): - if listener not in self._pause_listeners: - self._pause_listeners.append(listener) - - def removePauseListener(self, listener: AnimatorPauseListener): - if listener in self._pause_listeners: - self._pause_listeners.remove(listener) - - def setDuration(self, duration: int) -> "Animator": - self._duration = duration - return self - - def getDuration(self) -> int: - return self._duration - - def setStartDelay(self, startDelay: int) -> "Animator": - self._start_delay = startDelay - return self - - def getStartDelay(self) -> int: - return self._start_delay - - def setInterpolator(self, interpolator: TimeInterpolator): - self._interpolator = interpolator - - def getInterpolator(self) -> Optional[TimeInterpolator]: - return self._interpolator - - def clone(self) -> "Animator": - cloned = Animator() - cloned._duration = self._duration - cloned._start_delay = self._start_delay - cloned._interpolator = self._interpolator - return cloned - - def _notify_start(self): - for l in self._listeners: - l.onAnimationStart(self) - - def _notify_end(self): - for l in self._listeners: - l.onAnimationEnd(self) - - def _notify_cancel(self): - for l in self._listeners: - l.onAnimationCancel(self) - - def _notify_repeat(self): - for l in self._listeners: - l.onAnimationRepeat(self) - - def toString(self) -> str: - return f"Animator@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# ValueAnimator.AnimatorUpdateListener -# ═══════════════════════════════════════════════════════════════ - -class ValueAnimator: - class AnimatorUpdateListener(Stub): - """AnimatorUpdateListener — 值更新监听器""" - - def onAnimationUpdate(self, animation: "ValueAnimator"): - pass - - -# ═══════════════════════════════════════════════════════════════ -# ValueAnimator -# ═══════════════════════════════════════════════════════════════ - -class ValueAnimator(Animator): - """ValueAnimator — 值动画""" - - INFINITE = -1 - RESTART = 1 - REVERSE = 2 - - def __init__(self): - super().__init__() - self._values: List[Any] = [] - self._animated_value: Any = None - self._animated_fraction: float = 0.0 - self._repeat_count: int = 0 - self._repeat_mode: int = self.RESTART - self._evaluator: Optional[TypeEvaluator] = None - self._update_listeners: List[ValueAnimator.AnimatorUpdateListener] = [] - self._current_play_time: int = 0 - self._reverse_direction: bool = False - self._property_name: Optional[str] = None - - @staticmethod - def ofInt(*values: int) -> "ValueAnimator": - anim = ValueAnimator() - anim._values = list(values) - anim._evaluator = IntEvaluator() - return anim - - @staticmethod - def ofFloat(*values: float) -> "ValueAnimator": - anim = ValueAnimator() - anim._values = list(values) - anim._evaluator = FloatEvaluator() - return anim - - @staticmethod - def ofObject(*values: Any) -> "ValueAnimator": - anim = ValueAnimator() - anim._values = list(values) - return anim - - def setIntValues(self, *values: int): - self._values = list(values) - - def setFloatValues(self, *values: float): - self._values = list(values) - - def setObjectValues(self, *values: Any): - self._values = list(values) - - def getAnimatedValue(self) -> Any: - return self._animated_value - - def getAnimatedFraction(self) -> float: - return self._animated_fraction - - def setRepeatCount(self, count: int): - self._repeat_count = count - - def getRepeatCount(self) -> int: - return self._repeat_count - - def setRepeatMode(self, mode: int): - self._repeat_mode = mode - - def getRepeatMode(self) -> int: - return self._repeat_mode - - def setEvaluator(self, evaluator: TypeEvaluator): - self._evaluator = evaluator - - def getEvaluator(self) -> Optional[TypeEvaluator]: - return self._evaluator - - def setCurrentPlayTime(self, playTime: int): - self._current_play_time = playTime - if self._duration > 0: - self._animated_fraction = playTime / self._duration - self._evaluate() - - def getCurrentPlayTime(self) -> int: - return self._current_play_time - - def reverse(self): - self._reverse_direction = not self._reverse_direction - self.start() - - def addUpdateListener(self, listener: ValueAnimator.AnimatorUpdateListener): - if listener not in self._update_listeners: - self._update_listeners.append(listener) - - def removeUpdateListener(self, listener: ValueAnimator.AnimatorUpdateListener): - if listener in self._update_listeners: - self._update_listeners.remove(listener) - - def removeAllUpdateListeners(self): - self._update_listeners.clear() - - def start(self): - super().start() - if len(self._values) >= 2: - self._evaluate() - - def _evaluate(self): - if len(self._values) >= 2 and self._evaluator: - fraction = self._animated_fraction - if self._interpolator: - fraction = self._interpolator.getInterpolation(fraction) - self._animated_value = self._evaluator.evaluate( - fraction, self._values[0], self._values[-1] - ) - for l in self._update_listeners: - l.onAnimationUpdate(self) - - def clone(self) -> "ValueAnimator": - cloned = ValueAnimator() - cloned._values = list(self._values) - cloned._duration = self._duration - cloned._start_delay = self._start_delay - cloned._repeat_count = self._repeat_count - cloned._repeat_mode = self._repeat_mode - cloned._evaluator = self._evaluator - cloned._interpolator = self._interpolator - return cloned - - def toString(self) -> str: - return f"ValueAnimator@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# ObjectAnimator -# ═══════════════════════════════════════════════════════════════ - -class ObjectAnimator(ValueAnimator): - """ObjectAnimator — 对象属性动画""" - - def __init__(self): - super().__init__() - self._target: Any = None - self._property_name: Optional[str] = None - - @staticmethod - def ofInt(target: Any, propertyName: str, *values: int) -> "ObjectAnimator": - anim = ObjectAnimator() - anim._target = target - anim._property_name = propertyName - anim._values = list(values) - anim._evaluator = IntEvaluator() - return anim - - @staticmethod - def ofFloat(target: Any, propertyName: str, *values: float) -> "ObjectAnimator": - anim = ObjectAnimator() - anim._target = target - anim._property_name = propertyName - anim._values = list(values) - anim._evaluator = FloatEvaluator() - return anim - - @staticmethod - def ofObject(target: Any, propertyName: str, *values: Any) -> "ObjectAnimator": - anim = ObjectAnimator() - anim._target = target - anim._property_name = propertyName - anim._values = list(values) - return anim - - def setPropertyName(self, propertyName: str): - self._property_name = propertyName - - def getPropertyName(self) -> Optional[str]: - return self._property_name - - def getTarget(self) -> Any: - return self._target - - def setTarget(self, target: Any): - self._target = target - - def setProperty(self, property_: Any): - pass - - def setupStartValues(self): - pass - - def setupEndValues(self): - pass - - def clone(self) -> "ObjectAnimator": - cloned = ObjectAnimator() - cloned._target = self._target - cloned._property_name = self._property_name - cloned._values = list(self._values) - cloned._duration = self._duration - cloned._start_delay = self._start_delay - cloned._evaluator = self._evaluator - cloned._interpolator = self._interpolator - return cloned - - def toString(self) -> str: - return f"ObjectAnimator{{property={self._property_name}, target={self._target}}}" - - -# ═══════════════════════════════════════════════════════════════ -# AnimatorSet -# ═══════════════════════════════════════════════════════════════ - -class AnimatorSet(Animator): - """AnimatorSet — 动画集合""" - - def __init__(self): - super().__init__() - self._children: List[Animator] = [] - self._play_order: List[Animator] = [] - - def play(self, anim: Animator) -> "AnimatorSet.Builder": - return self.Builder(self, anim) - - def playSequentially(self, *items: Animator): - self._children = list(items) - self._play_order = list(items) - - def playTogether(self, *items: Animator): - self._children = list(items) - self._play_order = list(items) - - def start(self): - super().start() - for anim in self._play_order: - anim.start() - - def end(self): - super().end() - for anim in self._play_order: - anim.end() - - def cancel(self): - super().cancel() - for anim in self._play_order: - anim.cancel() - - class Builder(Stub): - """AnimatorSet.Builder — 构建器""" - - def __init__(self, parent: "AnimatorSet", anim: Animator): - super().__init__() - self._parent = parent - self._current = anim - - def with_(self, anim: Animator) -> "AnimatorSet.Builder": - self._parent._play_order.append(anim) - return self - - def before(self, anim: Animator) -> "AnimatorSet.Builder": - self._parent._play_order.append(anim) - return self - - def after(self, anim: Animator) -> "AnimatorSet.Builder": - idx = self._parent._play_order.index(self._current) if self._current in self._parent._play_order else -1 - if idx >= 0: - self._parent._play_order.insert(idx + 1, anim) - else: - self._parent._play_order.append(anim) - return self - - def after(self, delay: int) -> "AnimatorSet.Builder": - return self - - def toString(self) -> str: - return f"AnimatorSet{{{len(self._children)} animations}}" - - -# ═══════════════════════════════════════════════════════════════ -# TimeAnimator -# ═══════════════════════════════════════════════════════════════ - -class TimeAnimator(ValueAnimator): - """TimeAnimator — 时间动画""" - - class TimeListener(Stub): - """TimeListener — 时间监听器""" - - def onTimeUpdate(self, animation: "TimeAnimator", elapsedTime: int, totalTime: int): - pass - - def __init__(self): - super().__init__() - self._time_listener: Optional[TimeAnimator.TimeListener] = None - self._elapsed_time: int = 0 - self._total_time: int = 0 - - def setTimeListener(self, listener: TimeAnimator.TimeListener): - self._time_listener = listener - - def start(self): - super().start() - if self._time_listener: - self._time_listener.onTimeUpdate(self, self._elapsed_time, self._total_time) - - def toString(self) -> str: - return f"TimeAnimator@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# Keyframe -# ═══════════════════════════════════════════════════════════════ - -class Keyframe(Stub): - """Keyframe — 关键帧""" - - def __init__(self, fraction: float): - super().__init__() - self._fraction: float = fraction - self._value: Any = None - self._type: Optional[type] = None - self._interpolator: Optional[TimeInterpolator] = None - - @staticmethod - def ofInt(fraction: float, value: int) -> "Keyframe": - kf = Keyframe(fraction) - kf._value = value - kf._type = int - return kf - - @staticmethod - def ofFloat(fraction: float, value: float) -> "Keyframe": - kf = Keyframe(fraction) - kf._value = value - kf._type = float - return kf - - @staticmethod - def ofObject(fraction: float, value: Any = None) -> "Keyframe": - kf = Keyframe(fraction) - kf._value = value - return kf - - def setValue(self, value: Any): - self._value = value - - def getValue(self) -> Any: - return self._value - - def setFraction(self, fraction: float): - self._fraction = fraction - - def getFraction(self) -> float: - return self._fraction - - def getType(self) -> Optional[type]: - return self._type - - def setInterpolator(self, interpolator: Optional[TimeInterpolator]): - self._interpolator = interpolator - - def getInterpolator(self) -> Optional[TimeInterpolator]: - return self._interpolator - - def clone(self) -> "Keyframe": - kf = Keyframe(self._fraction) - kf._value = self._value - kf._type = self._type - kf._interpolator = self._interpolator - return kf - - def toString(self) -> str: - return f"Keyframe{{fraction={self._fraction}, value={self._value}}}" - - -# ═══════════════════════════════════════════════════════════════ -# PropertyValuesHolder -# ═══════════════════════════════════════════════════════════════ - -class PropertyValuesHolder(Stub): - """PropertyValuesHolder — 属性值持有者""" - - def __init__(self): - super().__init__() - self._property_name: str = "" - self._values: List[Any] = [] - self._animated_value: Any = None - self._evaluator: Optional[TypeEvaluator] = None - self._keyframes: List[Keyframe] = [] - - @staticmethod - def ofInt(propertyName: str, *values: int) -> "PropertyValuesHolder": - holder = PropertyValuesHolder() - holder._property_name = propertyName - holder._values = list(values) - return holder - - @staticmethod - def ofFloat(propertyName: str, *values: float) -> "PropertyValuesHolder": - holder = PropertyValuesHolder() - holder._property_name = propertyName - holder._values = list(values) - return holder - - @staticmethod - def ofObject(propertyName: str, *values: Any) -> "PropertyValuesHolder": - holder = PropertyValuesHolder() - holder._property_name = propertyName - holder._values = list(values) - return holder - - @staticmethod - def ofKeyframes(propertyName: str, *keyframes: Keyframe) -> "PropertyValuesHolder": - holder = PropertyValuesHolder() - holder._property_name = propertyName - holder._keyframes = list(keyframes) - return holder - - def setPropertyName(self, propertyName: str): - self._property_name = propertyName - - def getPropertyName(self) -> str: - return self._property_name - - def getAnimatedValue(self) -> Any: - return self._animated_value - - def setEvaluator(self, evaluator: TypeEvaluator): - self._evaluator = evaluator - - def clone(self) -> "PropertyValuesHolder": - holder = PropertyValuesHolder() - holder._property_name = self._property_name - holder._values = list(self._values) - holder._keyframes = list(self._keyframes) - holder._evaluator = self._evaluator - return holder - - def toString(self) -> str: - return f"PropertyValuesHolder{{property={self._property_name}}}" - - -# ═══════════════════════════════════════════════════════════════ -# TypeConverter -# ═══════════════════════════════════════════════════════════════ - -class TypeConverter(Stub): - """TypeConverter — 类型转换器""" - - def convert(self, value: Any) -> Any: - return value - - -# ═══════════════════════════════════════════════════════════════ -# LayoutTransition -# ═══════════════════════════════════════════════════════════════ - -class LayoutTransition(Stub): - """LayoutTransition — 布局过渡动画""" - - APPEARING = 0 - CHANGE_APPEARING = 1 - CHANGE_DISAPPEARING = 2 - DISAPPEARING = 3 - CHANGING = 4 - - def __init__(self): - super().__init__() - self._animators: Dict[int, Optional[Animator]] = { - self.APPEARING: None, - self.CHANGE_APPEARING: None, - self.CHANGE_DISAPPEARING: None, - self.DISAPPEARING: None, - self.CHANGING: None, - } - - def setAnimator(self, transitionType: int, animator: Animator): - self._animators[transitionType] = animator - - def getAnimator(self, transitionType: int) -> Optional[Animator]: - return self._animators.get(transitionType) - - def startTransition(self, container: Any, transitionType: int): - pass - - def endTransition(self, transitionType: int): - pass - - def toString(self) -> str: - return f"LayoutTransition@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# StateListAnimator -# ═══════════════════════════════════════════════════════════════ - -class StateListAnimator(Stub): - """StateListAnimator — 状态列表动画""" - - def __init__(self): - super().__init__() - self._tuples: List[Tuple[List[int], Animator]] = [] - self._target: Any = None - - def addState(self, specs: List[int], animator: Animator): - self._tuples.append((specs, animator)) - - def setTarget(self, target: Any): - self._target = target - - def clone(self) -> "StateListAnimator": - cloned = StateListAnimator() - cloned._tuples = list(self._tuples) - return cloned - - def toString(self) -> str: - return f"StateListAnimator{{{len(self._tuples)} states}}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Animator", - "ValueAnimator", - "ObjectAnimator", - "AnimatorSet", - "TimeAnimator", - "AnimatorListener", - "AnimatorPauseListener", - "TypeEvaluator", - "FloatEvaluator", - "IntEvaluator", - "PointFEvaluator", - "RectEvaluator", - "ArgbEvaluator", - "TypeConverter", - "Keyframe", - "PropertyValuesHolder", - "LayoutTransition", - "StateListAnimator", - "TimeInterpolator", - "AccelerateDecelerateInterpolator", - "AccelerateInterpolator", - "AnticipateInterpolator", - "AnticipateOvershootInterpolator", - "BounceInterpolator", - "CycleInterpolator", - "DecelerateInterpolator", - "LinearInterpolator", - "OvershootInterpolator", - "PathInterpolator", -] \ No newline at end of file diff --git a/android_pyapi/android/app.py b/android_pyapi/android/app.py deleted file mode 100644 index ab8cd54..0000000 --- a/android_pyapi/android/app.py +++ /dev/null @@ -1,2521 +0,0 @@ -""" -Android App 组件 — Activity, Service, Intent, Notification 等 -方法签名完全对齐 Android SDK -""" - -import json -import os -import time -import re -import sys -import threading -from datetime import datetime -from typing import Any, Dict, List, Optional, Tuple, Callable, Union -from urllib.parse import urlparse, urlunparse - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) -from ..java.lang import IllegalStateException - -try: - from .._linux_impl import IS_LINUX, LinuxNotify, LinuxShell, LinuxProcess, LinuxDisplay, LinuxUIAutomation, LinuxPower - _USE_LINUX = IS_LINUX -except ImportError: - _USE_LINUX = False - - -# ═══════════════════════════════════════════════════════════════ -# Intent -# ═══════════════════════════════════════════════════════════════ - -class Intent(Stub): - """Android Intent — 组件间消息传递""" - - # Action 常量 - ACTION_MAIN = "android.intent.action.MAIN" - ACTION_VIEW = "android.intent.action.VIEW" - ACTION_EDIT = "android.intent.action.EDIT" - ACTION_SEND = "android.intent.action.SEND" - ACTION_CALL = "android.intent.action.CALL" - ACTION_DIAL = "android.intent.action.DIAL" - ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH" - ACTION_PICK = "android.intent.action.PICK" - ACTION_GET_CONTENT = "android.intent.action.GET_CONTENT" - ACTION_INSTALL_PACKAGE = "android.intent.action.INSTALL_PACKAGE" - ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED" - ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED" - ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED" - ACTION_BATTERY_LOW = "android.intent.action.BATTERY_LOW" - ACTION_BATTERY_OKAY = "android.intent.action.BATTERY_OKAY" - ACTION_TIME_TICK = "android.intent.action.TIME_TICK" - ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED" - ACTION_SCREEN_ON = "android.intent.action.SCREEN_ON" - ACTION_SCREEN_OFF = "android.intent.action.SCREEN_OFF" - ACTION_AIRPLANE_MODE_CHANGED = "android.intent.action.AIRPLANE_MODE_CHANGED" - - # Flag 常量 - FLAG_ACTIVITY_NEW_TASK = 0x10000000 - FLAG_ACTIVITY_CLEAR_TOP = 0x04000000 - FLAG_ACTIVITY_SINGLE_TOP = 0x20000000 - FLAG_ACTIVITY_NO_HISTORY = 0x40000000 - FLAG_FROM_BACKGROUND = 0x00004000 - FLAG_RECEIVER_FOREGROUND = 0x10000000 - FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020 - - # Category 常量 - CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER" - CATEGORY_DEFAULT = "android.intent.category.DEFAULT" - CATEGORY_HOME = "android.intent.category.HOME" - CATEGORY_BROWSABLE = "android.intent.category.BROWSABLE" - CATEGORY_OPENABLE = "android.intent.category.OPENABLE" - - def __init__(self, action: Optional[str] = None, uri: Optional[str] = None): - super().__init__() - self._action = action - self._uri = uri - self._data_str = None - self._type = None - self._extras: Dict[str, Any] = {} - self._flags = 0 - self._categories: List[str] = [] - self._package = None - self._component: Optional[Tuple[str, str]] = None # (pkg, cls) - self._clip_data = None - - def setClass(self, package_context: Any, cls: type) -> "Intent": - self._package = package_context.packageName if hasattr(package_context, 'packageName') else _package_name - self._component = (self._package, cls.__name__ if isinstance(cls, type) else str(cls)) - return self - - def setAction(self, action: str) -> "Intent": - self._action = action - return self - - def getAction(self) -> Optional[str]: - return self._action - - def setData(self, uri: str) -> "Intent": - self._data_str = uri - return self - - def getData(self) -> Optional[str]: - return self._data_str - - def setType(self, type_: str) -> "Intent": - self._type = type_ - return self - - def getType(self) -> Optional[str]: - return self._type - - def setDataAndType(self, uri: str, type_: str) -> "Intent": - self._data_str = uri - self._type = type_ - return self - - def putExtra(self, name: str, value: Any) -> "Intent": - self._extras[name] = value - return self - - def getStringExtra(self, name: str) -> Optional[str]: - return self._extras.get(name) - - def getIntExtra(self, name: str, default: int = 0) -> int: - return self._extras.get(name, default) - - def getBooleanExtra(self, name: str, default: bool = False) -> bool: - return self._extras.get(name, default) - - def getLongExtra(self, name: str, default: int = 0) -> int: - return self._extras.get(name, default) - - def getFloatExtra(self, name: str, default: float = 0.0) -> float: - return self._extras.get(name, default) - - def getDoubleExtra(self, name: str, default: float = 0.0) -> float: - return self._extras.get(name, default) - - def getSerializableExtra(self, name: str): - return self._extras.get(name) - - def getIntegerArrayListExtra(self, name: str) -> Optional[List[int]]: - return self._extras.get(name) - - def getStringArrayListExtra(self, name: str) -> Optional[List[str]]: - return self._extras.get(name) - - def getExtras(self) -> Dict[str, Any]: - return self._extras - - def hasExtra(self, name: str) -> bool: - return name in self._extras - - def removeExtra(self, name: str): - self._extras.pop(name, None) - - def addCategory(self, category: str) -> "Intent": - if category not in self._categories: - self._categories.append(category) - return self - - def getCategories(self) -> Optional[List[str]]: - return self._categories if self._categories else None - - def hasCategory(self, category: str) -> bool: - return category in self._categories - - def setFlags(self, flags: int) -> "Intent": - self._flags = flags - return self - - def getFlags(self) -> int: - return self._flags - - def addFlags(self, flags: int) -> "Intent": - self._flags |= flags - return self - - def setPackage(self, package_name: str) -> "Intent": - self._package = package_name - return self - - def getPackage(self) -> Optional[str]: - return self._package - - def setClassName(self, package_name: str, class_name: str) -> "Intent": - self._component = (package_name, class_name) - return self - - def resolveActivity(self, pm) -> Optional["ComponentName"]: - from .content import ComponentName - if self._component: - return ComponentName(self._component[0], self._component[1]) - return None - - def createChooser(self, title: str) -> "Intent": - """Create a chooser intent""" - return self - - def toURI(self) -> str: - return str(self._uri or self._data_str or "") - - def toString(self) -> str: - return ( - f"Intent {{ act={self._action} " - f"pkg={self._package} " - f"cmp={self._component} " - f"hasExtras={bool(self._extras)} }}" - ) - - def clone(self) -> "Intent": - cloned = Intent(self._action, self._uri) - cloned._data_str = self._data_str - cloned._type = self._type - cloned._extras = dict(self._extras) - cloned._flags = self._flags - cloned._categories = list(self._categories) - cloned._package = self._package - cloned._component = self._component - return cloned - - def fillIn(self, other: "Intent", flags: int = 0) -> int: - filled = 0 - if other._action and not self._action: - self._action = other._action; filled |= 1 - if other._data_str and not self._data_str: - self._data_str = other._data_str; filled |= 2 - if other._type and not self._type: - self._type = other._type; filled |= 4 - if other._categories: - for c in other._categories: - if c not in self._categories: - self._categories.append(c) - filled |= 8 - return filled - - def filterEquals(self, other: "Intent") -> bool: - return ( - self._action == other._action - and self._data_str == other._data_str - and self._type == other._type - and self._package == other._package - and self._component == other._component - ) - - def filterHashCode(self) -> int: - return hash((self._action, self._data_str, self._type, self._package, self._component)) & 0x7FFFFFFF - - def getParcelableExtra(self, name): - return self._extras.get(name) if self._extras else None - - def setComponent(self, component): - self._component = component - return self - - def getComponent(self): - return getattr(self, '_component', None) - - def getDataString(self): - data = self.getData() - return str(data) if data else None - - def getScheme(self): - data = self.getData() - if data: - return data.getScheme() - return None - - def putExtras(self, src): - if src._extras: - if self._extras is None: - self._extras = {} - self._extras.update(src._extras) - return self - - def putIntegerArrayListExtra(self, name, value): - if self._extras is None: - self._extras = {} - self._extras[name] = value - return self - - def putStringArrayListExtra(self, name, value): - if self._extras is None: - self._extras = {} - self._extras[name] = value - return self - - def putParcelableArrayListExtra(self, name, value): - if self._extras is None: - self._extras = {} - self._extras[name] = value - return self - - def getBundleExtra(self, name): - return self._extras.get(name) if self._extras else None - - def putBundleExtra(self, name, value): - if self._extras is None: - self._extras = {} - self._extras[name] = value - return self - - def getParcelableArrayListExtra(self, name): - return self._extras.get(name) if self._extras else None - - def getStringArrayExtra(self, name): - return self._extras.get(name) if self._extras else None - - def getIntArrayExtra(self, name): - return self._extras.get(name) if self._extras else None - - def getBooleanArrayExtra(self, name): - return self._extras.get(name) if self._extras else None - - def getCharSequenceExtra(self, name): - return self._extras.get(name) if self._extras else None - - def getCharSequenceArrayListExtra(self, name): - return self._extras.get(name) if self._extras else None - - def removeCategory(self, category): - if self._categories: - self._categories.discard(category) - return self - - def setSelector(self, selector): - self._selector = selector - - def getSelector(self): - return getattr(self, '_selector', None) - - def setSourceBounds(self, bounds): - self._source_bounds = bounds - - def getSourceBounds(self): - return getattr(self, '_source_bounds', None) - - def makeMainActivity(self, component): - self.addCategory("android.intent.category.LAUNCHER") - self.setComponent(component) - return self - - def cloneFilter(self): - new_intent = Intent(self.getAction()) - if self._categories: - new_intent._categories = set(self._categories) - if self._type: - new_intent._type = self._type - return new_intent - - -# ═══════════════════════════════════════════════════════════════ -# ComponentName (forward reference) -# ═══════════════════════════════════════════════════════════════ - -class ComponentName(Stub): - """Identifies a specific component class within a package""" - - def __init__(self, pkg: str, cls: str): - super().__init__() - self._package = pkg - self._classname = cls - - def getPackageName(self) -> str: - return self._package - - def getClassName(self) -> str: - return self._classname - - def getShortClassName(self) -> str: - return self._classname.split(".")[-1] if "." in self._classname else self._classname - - def flattenToString(self) -> str: - return f"{self._package}/{self._classname}" - - def flattenToShortString(self) -> str: - return f"{self._package}/{self.getShortClassName()}" - - @staticmethod - def unflattenFromString(str_: str) -> Optional["ComponentName"]: - if "/" in str_: - parts = str_.split("/", 1) - return ComponentName(parts[0], parts[1]) - return None - - def toString(self) -> str: - return f"ComponentInfo{{{self._package}/{self._classname}}}" - - def __repr__(self): - return self.toString() - - -# ═══════════════════════════════════════════════════════════════ -# PendingIntent -# ═══════════════════════════════════════════════════════════════ - -class PendingIntent(Stub): - """A description of an Intent and target action to perform with it""" - - FLAG_ONE_SHOT = 1 << 30 - FLAG_NO_CREATE = 1 << 29 - FLAG_CANCEL_CURRENT = 1 << 28 - FLAG_UPDATE_CURRENT = 1 << 27 - FLAG_IMMUTABLE = 1 << 26 - - _pending_intents: Dict[int, "PendingIntent"] = {} - _next_id = 1000 - - def __init__(self, intent: Intent, request_code: int, flags: int): - super().__init__() - self._intent = intent - self._request_code = request_code - self._flags = flags - self._id = PendingIntent._next_id - PendingIntent._next_id += 1 - PendingIntent._pending_intents[self._id] = self - - @staticmethod - def getActivity(context, request_code: int, intent: Intent, flags: int) -> "PendingIntent": - pi = PendingIntent(intent, request_code, flags) - pi._type = "activity" - return pi - - @staticmethod - def getBroadcast(context, request_code: int, intent: Intent, flags: int) -> "PendingIntent": - pi = PendingIntent(intent, request_code, flags) - pi._type = "broadcast" - return pi - - @staticmethod - def getService(context, request_code: int, intent: Intent, flags: int) -> "PendingIntent": - pi = PendingIntent(intent, request_code, flags) - pi._type = "service" - return pi - - @staticmethod - def getActivities(context, request_code: int, intents: List[Intent], flags: int) -> "PendingIntent": - pi = PendingIntent(intents[0] if intents else Intent(), request_code, flags) - pi._type = "activities" - pi._intents = intents - return pi - - def send(self): - """Simulate sending the pending intent""" - pass - - def cancel(self): - PendingIntent._pending_intents.pop(self._id, None) - - def getIntent(self) -> Intent: - return self._intent - - def getTargetPackage(self) -> str: - return self._intent.getPackage() or "" - - def toString(self) -> str: - return f"PendingIntent{{{hex(id(self))}}}" - - -# ═══════════════════════════════════════════════════════════════ -# AlarmManager -# ═══════════════════════════════════════════════════════════════ - -class AlarmManager(Stub): - """AlarmManager — 调度定时操作""" - - RTC_WAKEUP = 0 - RTC = 1 - ELAPSED_REALTIME = 3 - ELAPSED_REALTIME_WAKEUP = 2 - INTERVAL_FIFTEEN_MINUTES = 900000 - INTERVAL_HALF_HOUR = 1800000 - INTERVAL_HOUR = 3600000 - INTERVAL_DAY = 86400000 - - def __init__(self): - super().__init__() - self._alarms: Dict[int, Dict] = {} - - def set(self, type_: int, trigger_at_ms: int, operation: PendingIntent): - self._alarms[id(operation)] = { - "type": type_, - "trigger_at": trigger_at_ms, - "operation": operation, - "repeating": False, - } - - def setRepeating(self, type_: int, trigger_at_ms: int, interval_ms: int, operation: PendingIntent): - self._alarms[id(operation)] = { - "type": type_, - "trigger_at": trigger_at_ms, - "interval": interval_ms, - "operation": operation, - "repeating": True, - } - - def setExact(self, type_: int, trigger_at_ms: int, operation: PendingIntent): - self.set(type_, trigger_at_ms, operation) - - def setAndAllowWhileIdle(self, type_: int, trigger_at_ms: int, operation: PendingIntent): - self.set(type_, trigger_at_ms, operation) - - def setExactAndAllowWhileIdle(self, type_: int, trigger_at_ms: int, operation: PendingIntent): - self.set(type_, trigger_at_ms, operation) - - def cancel(self, operation: PendingIntent): - self._alarms.pop(id(operation), None) - - def getNextAlarmClock(self): - return None - - -# ═══════════════════════════════════════════════════════════════ -# NotificationChannel -# ═══════════════════════════════════════════════════════════════ - -class NotificationChannel(Stub): - """Notification channel for Android O+""" - - IMPORTANCE_NONE = 0 - IMPORTANCE_MIN = 1 - IMPORTANCE_LOW = 2 - IMPORTANCE_DEFAULT = 3 - IMPORTANCE_HIGH = 4 - IMPORTANCE_MAX = 5 - IMPORTANCE_UNSPECIFIED = -1000 - - def __init__(self, channel_id: str, name: str, importance: int): - super().__init__() - self._id = channel_id - self._name = name - self._importance = importance - self._description = "" - self._enabled = True - self._show_badge = True - self._sound = None - self._lights = False - self._vibration = True - self._bypass_dnd = False - self._lockscreen_visibility = 0 - self._group = None - self._can_bubble = False - - def getId(self) -> str: - return self._id - - def getName(self) -> str: - return self._name - - def getImportance(self) -> int: - return self._importance - - def setDescription(self, description: str): - self._description = description - - def getDescription(self) -> str: - return self._description - - def setShowBadge(self, show_badge: bool): - self._show_badge = show_badge - - def canShowBadge(self) -> bool: - return self._show_badge - - def enableLights(self, lights: bool): - self._lights = lights - - def shouldShowLights(self) -> bool: - return self._lights - - def enableVibration(self, vibration: bool): - self._vibration = vibration - - def shouldVibrate(self) -> bool: - return self._vibration - - def setBypassDnd(self, bypass_dnd: bool): - self._bypass_dnd = bypass_dnd - - def canBypassDnd(self) -> bool: - return self._bypass_dnd - - def setSound(self, sound: Any, audio_attributes: Any = None): - self._sound = sound - - def getSound(self): - return self._sound - - def setGroup(self, group_id: str): - self._group = group_id - - def getGroup(self) -> Optional[str]: - return self._group - - def setLockScreenVisibility(self, visibility: int): - self._lockscreen_visibility = visibility - - def getLockScreenVisibility(self) -> int: - return self._lockscreen_visibility - - def toString(self) -> str: - return f"NotificationChannel(id={self._id}, name={self._name}, importance={self._importance})" - - -class NotificationChannelGroup(Stub): - """Group of notification channels""" - - def __init__(self, group_id: str, name: str): - super().__init__() - self._id = group_id - self._name = name - self._channels: List[NotificationChannel] = [] - - def getId(self) -> str: - return self._id - - def getName(self) -> str: - return self._name - - def getChannels(self) -> List[NotificationChannel]: - return list(self._channels) - - -# ═══════════════════════════════════════════════════════════════ -# Notification -# ═══════════════════════════════════════════════════════════════ - -class Notification(Stub): - """Android Notification""" - - DEFAULT_ALL = -1 - DEFAULT_SOUND = 1 - DEFAULT_VIBRATE = 2 - DEFAULT_LIGHTS = 4 - EXTRA_TITLE = "android.title" - EXTRA_TEXT = "android.text" - EXTRA_SUB_TEXT = "android.subText" - EXTRA_INFO_TEXT = "android.infoText" - EXTRA_SUMMARY_TEXT = "android.summaryText" - EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID" - EXTRA_BIG_TEXT = "android.bigText" - EXTRA_PICTURE = "android.picture" - PRIORITY_MIN = -2 - PRIORITY_LOW = -1 - PRIORITY_DEFAULT = 0 - PRIORITY_HIGH = 1 - PRIORITY_MAX = 2 - VISIBILITY_PRIVATE = 0 - VISIBILITY_PUBLIC = 1 - VISIBILITY_SECRET = -1 - - class Builder(Stub): - """Notification builder pattern""" - - def __init__(self, context, channel_id: Optional[str] = None): - super().__init__() - self._context = context - self._channel_id = channel_id or "" - self._title = "" - self._text = "" - self._sub_text = "" - self._info_text = "" - self._summary_text = "" - self._big_text = "" - self._small_icon = 0 - self._large_icon = None - self._color = 0 - self._priority = Notification.PRIORITY_DEFAULT - self._defaults = 0 - self._auto_cancel = False - self._ongoing = False - self._when = 0 - self._number = 0 - self._content_intent = None - self._delete_intent = None - self._full_screen_intent = None - self._ticker = "" - self._visibility = Notification.VISIBILITY_PRIVATE - self._category = "" - self._group = "" - self._group_summary = False - self._public_version = None - self._extras: Dict[str, Any] = {} - self._actions: List[Dict] = [] - self._style = None - self._badge_icon_type = 0 - self._settings_text = None - self._uses_channel_defaults = False - - def setChannelId(self, channel_id: str) -> "Notification.Builder": - self._channel_id = channel_id - return self - - def setContentTitle(self, title: str) -> "Notification.Builder": - self._title = title - return self - - def setContentText(self, text: str) -> "Notification.Builder": - self._text = text - return self - - def setSubText(self, text: str) -> "Notification.Builder": - self._sub_text = text - return self - - def setContentInfo(self, info: str) -> "Notification.Builder": - self._info_text = info - return self - - def setSummaryText(self, text: str) -> "Notification.Builder": - self._summary_text = text - return self - - def setSmallIcon(self, icon: int) -> "Notification.Builder": - self._small_icon = icon - return self - - def setLargeIcon(self, icon: Any) -> "Notification.Builder": - self._large_icon = icon - return self - - def setColor(self, color: int) -> "Notification.Builder": - self._color = color - return self - - def setPriority(self, priority: int) -> "Notification.Builder": - self._priority = priority - return self - - def setDefaults(self, defaults: int) -> "Notification.Builder": - self._defaults = defaults - return self - - def setAutoCancel(self, auto_cancel: bool) -> "Notification.Builder": - self._auto_cancel = auto_cancel - return self - - def setOngoing(self, ongoing: bool) -> "Notification.Builder": - self._ongoing = ongoing - return self - - def setWhen(self, when: int) -> "Notification.Builder": - self._when = when - return self - - def setNumber(self, number: int) -> "Notification.Builder": - self._number = number - return self - - def setContentIntent(self, intent: PendingIntent) -> "Notification.Builder": - self._content_intent = intent - return self - - def setDeleteIntent(self, intent: PendingIntent) -> "Notification.Builder": - self._delete_intent = intent - return self - - def setFullScreenIntent(self, intent: PendingIntent, high_priority: bool) -> "Notification.Builder": - self._full_screen_intent = intent - return self - - def setTicker(self, ticker: str) -> "Notification.Builder": - self._ticker = ticker - return self - - def setVisibility(self, visibility: int) -> "Notification.Builder": - self._visibility = visibility - return self - - def setCategory(self, category: str) -> "Notification.Builder": - self._category = category - return self - - def setGroup(self, group: str) -> "Notification.Builder": - self._group = group - return self - - def setGroupSummary(self, is_group_summary: bool) -> "Notification.Builder": - self._group_summary = is_group_summary - return self - - def setPublicVersion(self, notification: "Notification") -> "Notification.Builder": - self._public_version = notification - return self - - def addAction(self, icon: int, title: str, intent: PendingIntent) -> "Notification.Builder": - self._actions.append({"icon": icon, "title": title, "intent": intent}) - return self - - def setStyle(self, style: Any) -> "Notification.Builder": - self._style = style - return self - - def setBadgeIconType(self, icon_type: int) -> "Notification.Builder": - self._badge_icon_type = icon_type - return self - - def setSettingsText(self, text: str) -> "Notification.Builder": - self._settings_text = text - return self - - def addExtras(self, extras: Dict[str, Any]) -> "Notification.Builder": - if extras: - self._extras.update(extras) - return self - - def extend(self, builder: Any) -> "Notification.Builder": - return self - - def build(self) -> "Notification": - n = Notification() - n._channel_id = self._channel_id - n._title = self._title - n._text = self._text - n._sub_text = self._sub_text - n._info_text = self._info_text - n._summary_text = self._summary_text - n._big_text = self._big_text - n._small_icon = self._small_icon - n._large_icon = self._large_icon - n._color = self._color - n._priority = self._priority - n._defaults = self._defaults - n._auto_cancel = self._auto_cancel - n._ongoing = self._ongoing - n._when = self._when or int(time.time() * 1000) - n._number = self._number - n._content_intent = self._content_intent - n._delete_intent = self._delete_intent - n._full_screen_intent = self._full_screen_intent - n._ticker = self._ticker - n._visibility = self._visibility - n._category = self._category - n._group = self._group - n._group_summary = self._group_summary - n._public_version = self._public_version - n._actions = list(self._actions) - n._style = self._style - n._extras = dict(self._extras) - return n - - def setProgress(self, max, progress, indeterminate): - self._progress_max = max - self._progress = progress - self._progress_indeterminate = indeterminate - return self - - def setShowWhen(self, show): - self._show_when = show - return self - - def setOnlyAlertOnce(self, onlyAlertOnce): - self._only_alert_once = onlyAlertOnce - return self - - def setLocalOnly(self, localOnly): - self._local_only = localOnly - return self - - def setSound(self, sound): - self._sound = sound - return self - - def setVibrate(self, pattern): - self._vibrate = pattern - return self - - def setLights(self, argb, onMs, offMs): - self._light_color = argb - self._light_on = onMs - self._light_off = offMs - return self - - def setSortKey(self, sortKey): - self._sort_key = sortKey - return self - - def setTimeoutAfter(self, timeoutMs): - self._timeout_after = timeoutMs - return self - - def setColorized(self, colorize): - self._colorized = colorize - return self - - def setUsesChronometer(self, usesChronometer): - self._uses_chronometer = usesChronometer - return self - - def setShortcutId(self, shortcutId): - self._shortcut_id = shortcutId - return self - - def setContent(self, views): - self._content_view = views - return self - - def __init__(self): - super().__init__() - self._channel_id = "" - self._title = "" - self._text = "" - self._sub_text = "" - self._info_text = "" - self._summary_text = "" - self._big_text = "" - self._small_icon = 0 - self._large_icon = None - self._color = 0 - self._priority = self.PRIORITY_DEFAULT - self._defaults = 0 - self._auto_cancel = False - self._ongoing = False - self._when = 0 - self._number = 0 - self._content_intent = None - self._delete_intent = None - self._full_screen_intent = None - self._ticker = "" - self._visibility = self.VISIBILITY_PRIVATE - self._category = "" - self._group = "" - self._group_summary = False - self._public_version = None - self._extras: Dict[str, Any] = {} - self._actions: List[Dict] = [] - self._style = None - - def getChannelId(self) -> str: - return self._channel_id - - def toString(self) -> str: - return f"Notification(channel={self._channel_id}, title={self._title})" - - -class NotificationCompat(Stub): - """NotificationCompat — 兼容版本""" - - class Builder(Stub): - def __init__(self, context, channel_id: Optional[str] = None): - super().__init__() - self._builder = Notification.Builder(context, channel_id) - - def setContentTitle(self, title: str) -> "NotificationCompat.Builder": - self._builder.setContentTitle(title) - return self - - def setContentText(self, text: str) -> "NotificationCompat.Builder": - self._builder.setContentText(text) - return self - - def setSmallIcon(self, icon: int) -> "NotificationCompat.Builder": - self._builder.setSmallIcon(icon) - return self - - def setPriority(self, priority: int) -> "NotificationCompat.Builder": - self._builder.setPriority(priority) - return self - - def setAutoCancel(self, auto_cancel: bool) -> "NotificationCompat.Builder": - self._builder.setAutoCancel(auto_cancel) - return self - - def setContentIntent(self, intent: PendingIntent) -> "NotificationCompat.Builder": - self._builder.setContentIntent(intent) - return self - - def setDefaults(self, defaults: int) -> "NotificationCompat.Builder": - self._builder.setDefaults(defaults) - return self - - def build(self) -> Notification: - return self._builder.build() - - -# ═══════════════════════════════════════════════════════════════ -# NotificationManager -# ═══════════════════════════════════════════════════════════════ - -class NotificationManager(Stub): - """NotificationManager — 发送通知""" - - IMPORTANCE_NONE = 0 - IMPORTANCE_MIN = 1 - IMPORTANCE_LOW = 2 - IMPORTANCE_DEFAULT = 3 - IMPORTANCE_HIGH = 4 - IMPORTANCE_MAX = 5 - IMPORTANCE_UNSPECIFIED = -1000 - - def __init__(self): - super().__init__() - self._notifications: Dict[int, Notification] = {} - self._channels: Dict[str, NotificationChannel] = {} - self._groups: Dict[str, NotificationChannelGroup] = {} - self._notification_id_counter = 0 - - def notify(self, id_: int, notification: Notification): - self._notifications[id_] = notification - if _USE_LINUX: - title = getattr(notification, "_title", "") or "" - text = getattr(notification, "_text", "") or "" - if title or text: - LinuxNotify.send(title, text) - else: - sys.stderr.write( - f"[NotificationManager] notify id={id_}: " - f"title={notification._title} text={notification._text}\n" - ) - - def notifyAsPackage(self, package_name: str, tag: Optional[str], id_: int, notification: Notification): - self.notify(id_, notification) - - def cancel(self, id_: int): - self._notifications.pop(id_, None) - - def cancelAll(self): - self._notifications.clear() - - def cancelAsPackage(self, package_name: str, tag: Optional[str], id_: int): - self.cancel(id_) - - def createNotificationChannel(self, channel: NotificationChannel): - self._channels[channel.getId()] = channel - sys.stderr.write( - f"[NotificationManager] create channel: {channel.getId()}\n" - ) - - def createNotificationChannels(self, channels: List[NotificationChannel]): - for c in channels: - self.createNotificationChannel(c) - - def getNotificationChannel(self, channel_id: str) -> Optional[NotificationChannel]: - return self._channels.get(channel_id) - - def getNotificationChannels(self) -> List[NotificationChannel]: - return list(self._channels.values()) - - def deleteNotificationChannel(self, channel_id: str): - self._channels.pop(channel_id, None) - - def createNotificationChannelGroup(self, group: NotificationChannelGroup): - self._groups[group.getId()] = group - - def getNotificationChannelGroups(self) -> List[NotificationChannelGroup]: - return list(self._groups.values()) - - def getActiveNotifications(self) -> List[Notification]: - return list(self._notifications.values()) - - def areNotificationsEnabled(self) -> bool: - return True - - def isNotificationPolicyAccessGranted(self) -> bool: - return True - - def matchesCallFilter(self, address: Any) -> bool: - return True - - def toString(self) -> str: - return f"NotificationManager(notifications={len(self._notifications)})" - - -# ═══════════════════════════════════════════════════════════════ -# Dialog / AlertDialog / ProgressDialog / DatePickerDialog / TimePickerDialog -# ═══════════════════════════════════════════════════════════════ - -class Dialog(Stub): - """Base dialog class""" - - def __init__(self, context, theme_res_id: int = 0): - super().__init__() - self._context = context - self._theme_res_id = theme_res_id - self._showing = False - self._cancelable = True - self._on_cancel_listener = None - self._on_dismiss_listener = None - self._on_show_listener = None - self._title = "" - self._window = None - - def show(self): - self._showing = True - sys.stderr.write(f"[Dialog] show: {self.__class__.__name__}\n") - - def dismiss(self): - self._showing = False - sys.stderr.write(f"[Dialog] dismiss: {self.__class__.__name__}\n") - - def isShowing(self) -> bool: - return self._showing - - def cancel(self): - self.dismiss() - if self._on_cancel_listener: - self._on_cancel_listener.onCancel(self) - - def setCancelable(self, flag: bool): - self._cancelable = flag - - def setCanceledOnTouchOutside(self, cancel: bool): - pass - - def setTitle(self, title: str): - self._title = title - - def getContext(self): - return self._context - - def setOnCancelListener(self, listener): - self._on_cancel_listener = listener - - def setOnDismissListener(self, listener): - self._on_dismiss_listener = listener - - def setOnShowListener(self, listener): - self._on_show_listener = listener - - def getWindow(self): - return self._window - - def findViewById(self, id_: int): - return None - - def onBackPressed(self): - if self._cancelable: - self.cancel() - - -class AlertDialog(Dialog): - """Alert dialog with builder pattern""" - - BUTTON_POSITIVE = -1 - BUTTON_NEGATIVE = -2 - BUTTON_NEUTRAL = -3 - - def __init__(self, context, theme_res_id: int = 0): - super().__init__(context, theme_res_id) - self._message = "" - self._buttons: Dict[int, Dict] = {} - self._items: List[str] = [] - self._single_choice_items = None - self._multi_choice_items = None - self._selected_index = -1 - self._selected_indices: List[int] = [] - self._view = None - self._icon = 0 - self._custom_title = None - - class Builder(Stub): - """AlertDialog builder""" - def __init__(self, context, theme_res_id: int = 0): - super().__init__() - self._context = context - self._theme_res_id = theme_res_id - self._title = "" - self._message = "" - self._positive_text = "" - self._positive_listener = None - self._negative_text = "" - self._negative_listener = None - self._neutral_text = "" - self._neutral_listener = None - self._cancelable = True - self._on_cancel_listener = None - self._items: List[str] = [] - self._on_item_click_listener = None - self._single_choice_items = None - self._single_choice_selected = -1 - self._single_choice_listener = None - self._multi_choice_items = None - self._multi_choice_selected = None - self._multi_choice_listener = None - self._icon = 0 - self._view = None - self._custom_title_view = None - - def setTitle(self, title: str) -> "AlertDialog.Builder": - self._title = title - return self - - def setMessage(self, message: str) -> "AlertDialog.Builder": - self._message = message - return self - - def setPositiveButton(self, text: str, listener=None) -> "AlertDialog.Builder": - self._positive_text = text - self._positive_listener = listener - return self - - def setNegativeButton(self, text: str, listener=None) -> "AlertDialog.Builder": - self._negative_text = text - self._negative_listener = listener - return self - - def setNeutralButton(self, text: str, listener=None) -> "AlertDialog.Builder": - self._neutral_text = text - self._neutral_listener = listener - return self - - def setCancelable(self, cancelable: bool) -> "AlertDialog.Builder": - self._cancelable = cancelable - return self - - def setOnCancelListener(self, listener) -> "AlertDialog.Builder": - self._on_cancel_listener = listener - return self - - def setItems(self, items: List[str], listener=None) -> "AlertDialog.Builder": - self._items = list(items) - self._on_item_click_listener = listener - return self - - def setSingleChoiceItems(self, items: List[str], checked_item: int, listener=None) -> "AlertDialog.Builder": - self._single_choice_items = list(items) - self._single_choice_selected = checked_item - self._single_choice_listener = listener - return self - - def setMultiChoiceItems(self, items: List[str], checked_items: List[bool], listener=None) -> "AlertDialog.Builder": - self._multi_choice_items = list(items) - self._multi_choice_selected = checked_items or [] - self._multi_choice_listener = listener - return self - - def setIcon(self, icon: int) -> "AlertDialog.Builder": - self._icon = icon - return self - - def setView(self, view: Any) -> "AlertDialog.Builder": - self._view = view - return self - - def setCustomTitle(self, view: Any) -> "AlertDialog.Builder": - self._custom_title_view = view - return self - - def create(self) -> "AlertDialog": - dialog = AlertDialog(self._context, self._theme_res_id) - dialog._title = self._title - dialog._message = self._message - dialog._cancelable = self._cancelable - dialog._icon = self._icon - dialog._view = self._view - if self._positive_text: - dialog._buttons[AlertDialog.BUTTON_POSITIVE] = { - "text": self._positive_text, - "listener": self._positive_listener, - } - if self._negative_text: - dialog._buttons[AlertDialog.BUTTON_NEGATIVE] = { - "text": self._negative_text, - "listener": self._negative_listener, - } - if self._neutral_text: - dialog._buttons[AlertDialog.BUTTON_NEUTRAL] = { - "text": self._neutral_text, - "listener": self._neutral_listener, - } - dialog._items = list(self._items) - dialog._single_choice_items = self._single_choice_items - dialog._multi_choice_items = self._multi_choice_items - return dialog - - def show(self) -> "AlertDialog": - dialog = self.create() - dialog.show() - return dialog - - -class ProgressDialog(AlertDialog): - """Progress dialog with a progress bar""" - - STYLE_SPINNER = 0 - STYLE_HORIZONTAL = 1 - - def __init__(self, context, theme_res_id: int = 0): - super().__init__(context, theme_res_id) - self._progress_style = ProgressDialog.STYLE_SPINNER - self._progress = 0 - self._max = 100 - self._indeterminate = True - self._progress_number_format = "%d/%d" - self._progress_percent_format = "%d%%" - - def setProgressStyle(self, style: int): - self._progress_style = style - - def setProgress(self, value: int): - self._progress = value - - def getProgress(self) -> int: - return self._progress - - def setMax(self, max_: int): - self._max = max_ - - def getMax(self) -> int: - return self._max - - def setIndeterminate(self, indeterminate: bool): - self._indeterminate = indeterminate - - def isIndeterminate(self) -> bool: - return self._indeterminate - - def incrementProgressBy(self, diff: int): - self._progress = min(self._max, max(0, self._progress + diff)) - - @staticmethod - def show(context, title: str, message: str, indeterminate: bool = True, cancelable: bool = False) -> "ProgressDialog": - dialog = ProgressDialog(context) - dialog.setTitle(title) - dialog._message = message - dialog._indeterminate = indeterminate - dialog.setCancelable(cancelable) - dialog.show() - return dialog - - -class DatePickerDialog(AlertDialog): - """Date picker dialog""" - - def __init__(self, context, listener=None, year: int = 2024, month: int = 0, day_of_month: int = 1, theme_res_id: int = 0): - super().__init__(context, theme_res_id) - self._listener = listener - self._year = year - self._month = month - self._day_of_month = day_of_month - self._date_picker = None - - def getDatePicker(self): - return self._date_picker - - def updateDate(self, year: int, month: int, day_of_month: int): - self._year = year - self._month = month - self._day_of_month = day_of_month - - def onDateChanged(self, view, year: int, month: int, day: int): - self._year = year - self._month = month - self._day_of_month = day - - class OnDateSetListener: - def onDateSet(self, view, year: int, month: int, day_of_month: int): - pass - - -class TimePickerDialog(AlertDialog): - """Time picker dialog""" - - def __init__(self, context, listener=None, hour_of_day: int = 12, minute: int = 0, is24_hour_view: bool = True, theme_res_id: int = 0): - super().__init__(context, theme_res_id) - self._listener = listener - self._hour_of_day = hour_of_day - self._minute = minute - self._is24_hour_view = is24_hour_view - - class OnTimeSetListener: - def onTimeSet(self, view, hour_of_day: int, minute: int): - pass - - -# ═══════════════════════════════════════════════════════════════ -# Fragment / FragmentManager / FragmentTransaction -# ═══════════════════════════════════════════════════════════════ - -class Fragment(Stub): - """Fragment — UI 片段""" - - def __init__(self): - super().__init__() - self._arguments: Optional[Bundle] = None - self._activity = None - self._view = None - self._fragment_manager = None - self._state = 0 - self._tag = None - self._id = -1 - self._container_id = -1 - self._menu = True - self._retain_instance = False - self._target_fragment = None - self._target_request_code = -1 - self._user_visible_hint = True - - def setArguments(self, args: "Bundle"): - self._arguments = args - - def getArguments(self) -> Optional["Bundle"]: - return self._arguments - - def getActivity(self): - return self._activity - - def getContext(self): - return self._activity - - def getFragmentManager(self) -> Optional["FragmentManager"]: - return self._fragment_manager - - def getView(self): - return self._view - - def getResources(self): - if self._activity: - return getattr(self._activity, 'getResources', lambda: None)() - return None - - def getString(self, res_id: int, *args) -> str: - return str(res_id) - - def getTag(self) -> Optional[str]: - return self._tag - - def setId(self, id_: int): - self._id = id_ - - def getId(self) -> int: - return self._id - - def onAttach(self, context): - pass - - def onCreate(self, savedInstanceState: Optional["Bundle"] = None): - pass - - def onCreateView(self, inflater, container, savedInstanceState): - return None - - def onViewCreated(self, view, savedInstanceState: Optional["Bundle"] = None): - pass - - def onStart(self): - pass - - def onResume(self): - pass - - def onPause(self): - pass - - def onStop(self): - pass - - def onDestroyView(self): - pass - - def onDestroy(self): - pass - - def onDetach(self): - pass - - def onActivityCreated(self, savedInstanceState: Optional["Bundle"] = None): - pass - - def onSaveInstanceState(self, out_state: "Bundle"): - pass - - def onViewStateRestored(self, savedInstanceState: Optional["Bundle"] = None): - pass - - def isAdded(self) -> bool: - return self._activity is not None - - def isDetached(self) -> bool: - return self._activity is None - - def isVisible(self) -> bool: - return False - - def isRemoving(self) -> bool: - return False - - def isInLayout(self) -> bool: - return False - - def isResumed(self) -> bool: - return self._state >= 7 - - def setHasOptionsMenu(self, has_menu: bool): - self._menu = has_menu - - def setRetainInstance(self, retain: bool): - self._retain_instance = retain - - def getRetainInstance(self) -> bool: - return self._retain_instance - - def setTargetFragment(self, fragment: "Fragment", request_code: int): - self._target_fragment = fragment - self._target_request_code = request_code - - def getTargetFragment(self) -> Optional["Fragment"]: - return self._target_fragment - - def getTargetRequestCode(self) -> int: - return self._target_request_code - - def setUserVisibleHint(self, is_visible_to_user: bool): - self._user_visible_hint = is_visible_to_user - - def getUserVisibleHint(self) -> bool: - return self._user_visible_hint - - def registerForContextMenu(self, view): - pass - - def unregisterForContextMenu(self, view): - pass - - def getChildFragmentManager(self): - if not hasattr(self, '_child_fragment_manager'): - from .app import FragmentManager - self._child_fragment_manager = FragmentManager() - return self._child_fragment_manager - - def getParentFragment(self): - return getattr(self, '_parent_fragment', None) - - def onCreateOptionsMenu(self, menu, inflater): - return False - - def onPrepareOptionsMenu(self, menu): - return False - - def onOptionsItemSelected(self, item): - return False - - def onCreateContextMenu(self, menu, v, menuInfo): - pass - - def onContextItemSelected(self, item): - return False - - def onContextMenuClosed(self, menu): - pass - - def onConfigurationChanged(self, newConfig): - pass - - def onLowMemory(self): - pass - - def onHiddenChanged(self, hidden): - self._hidden = hidden - - def getLayoutInflater(self): - return getattr(self, '_layout_inflater', None) - - def onGetLayoutInflater(self, savedInstanceState): - return self.getLayoutInflater() - - def requireContext(self): - if not hasattr(self, '_context') or self._context is None: - raise IllegalStateException("Fragment " + self.__class__.__name__ + " not attached to a context") - return self._context - - def requireActivity(self): - if not hasattr(self, '_activity') or self._activity is None: - raise IllegalStateException("Fragment " + self.__class__.__name__ + " not attached to an activity") - return self._activity - - def requireView(self): - if not hasattr(self, '_view') or self._view is None: - raise IllegalStateException("Fragment " + self.__class__.__name__ + " view is null") - return self._view - - def requireFragmentManager(self): - fm = getattr(self, '_fragment_manager', None) - if fm is None: - raise IllegalStateException("Fragment " + self.__class__.__name__ + " not associated with a fragment manager") - return fm - - def setMenuVisibility(self, menuVisible): - self._menu_visible = menuVisible - - def onMenuVisibilityChanged(self, menuVisible): - pass - - def postponeEnterTransition(self): - pass - - def startPostponedEnterTransition(self): - pass - - def getEnterTransition(self): - return getattr(self, '_enter_transition', None) - - def setEnterTransition(self, transition): - self._enter_transition = transition - - def onInflate(self, context, attrs, savedInstanceState): - pass - - -class FragmentTransaction(Stub): - """FragmentTransaction — 管理 Fragment 事务""" - - TRANSIT_ENTER_MASK = 0x1000 - TRANSIT_EXIT_MASK = 0x2000 - TRANSIT_FRAGMENT_FADE = 0x1001 - TRANSIT_FRAGMENT_OPEN = 0x1003 - TRANSIT_FRAGMENT_CLOSE = 0x1004 - TRANSIT_NONE = 0 - TRANSIT_UNSET = -1 - - def __init__(self): - super().__init__() - self._ops: List[Dict] = [] - self._transition = FragmentTransaction.TRANSIT_NONE - self._transition_style = 0 - self._allow_state_loss = False - self._commit_allowing_state_loss = False - - def add(self, container_view_id: int, fragment: Fragment, tag: Optional[str] = None) -> "FragmentTransaction": - self._ops.append({"type": "add", "id": container_view_id, "fragment": fragment, "tag": tag}) - return self - - def replace(self, container_view_id: int, fragment: Fragment, tag: Optional[str] = None) -> "FragmentTransaction": - self._ops.append({"type": "replace", "id": container_view_id, "fragment": fragment, "tag": tag}) - return self - - def remove(self, fragment: Fragment) -> "FragmentTransaction": - self._ops.append({"type": "remove", "fragment": fragment}) - return self - - def hide(self, fragment: Fragment) -> "FragmentTransaction": - self._ops.append({"type": "hide", "fragment": fragment}) - return self - - def show(self, fragment: Fragment) -> "FragmentTransaction": - self._ops.append({"type": "show", "fragment": fragment}) - return self - - def detach(self, fragment: Fragment) -> "FragmentTransaction": - self._ops.append({"type": "detach", "fragment": fragment}) - return self - - def attach(self, fragment: Fragment) -> "FragmentTransaction": - self._ops.append({"type": "attach", "fragment": fragment}) - return self - - def addToBackStack(self, name: Optional[str] = None) -> "FragmentTransaction": - self._ops.append({"type": "addToBackStack", "name": name}) - return self - - def setTransition(self, transit: int) -> "FragmentTransaction": - self._transition = transit - return self - - def setTransitionStyle(self, style: int) -> "FragmentTransaction": - self._transition_style = style - return self - - def setCustomAnimations(self, enter: int, exit: int, pop_enter: int = 0, pop_exit: int = 0) -> "FragmentTransaction": - return self - - def disallowAddToBackStack(self) -> "FragmentTransaction": - return self - - def setBreadCrumbShortTitle(self, title: str) -> "FragmentTransaction": - return self - - def setBreadCrumbTitle(self, title: str) -> "FragmentTransaction": - return self - - def commit(self) -> int: - return 0 - - def commitAllowingStateLoss(self) -> int: - return 0 - - def commitNow(self) -> int: - return 0 - - def commitNowAllowingStateLoss(self) -> int: - return 0 - - def isAddToBackStackAllowed(self) -> bool: - return True - - -class FragmentManager(Stub): - """FragmentManager — Fragment 管理器""" - - POP_BACK_STACK_INCLUSIVE = 1 - - def __init__(self): - super().__init__() - self._fragments: Dict[str, Fragment] = {} - self._back_stack: List[Dict] = [] - - def beginTransaction(self) -> FragmentTransaction: - return FragmentTransaction() - - def findFragmentById(self, id_: int) -> Optional[Fragment]: - for f in self._fragments.values(): - if f.getId() == id_: - return f - return None - - def findFragmentByTag(self, tag: str) -> Optional[Fragment]: - return self._fragments.get(tag) - - def popBackStack(self): - if self._back_stack: - self._back_stack.pop() - - def popBackStackImmediate(self, name: str, flags: int) -> bool: - return True - - def getBackStackEntryCount(self) -> int: - return len(self._back_stack) - - def getFragment(self, bundle: "Bundle", key: str) -> Optional[Fragment]: - return None - - def getFragments(self) -> List[Fragment]: - return list(self._fragments.values()) - - def saveFragmentInstanceState(self, fragment: Fragment): - return None - - def isDestroyed(self) -> bool: - return False - - def isStateSaved(self) -> bool: - return False - - def putFragment(self, bundle: "Bundle", key: str, fragment: Fragment): - pass - - def executePendingTransactions(self) -> bool: - return True - - def clearFragmentResult(self, request_key: str): - pass - - def setFragmentResult(self, request_key: str, result: "Bundle"): - pass - - def setFragmentResultListener(self, lifecycle: Any, listener: Any): - pass - - def registerFragmentLifecycleCallbacks(self, cb: Any, recursive: bool): - pass - - def unregisterFragmentLifecycleCallbacks(self, cb: Any): - pass - - -# ═══════════════════════════════════════════════════════════════ -# Activity -# ═══════════════════════════════════════════════════════════════ - -class Activity(Stub): - """Activity — 应用界面入口""" - - # 结果码 - RESULT_OK = -1 - RESULT_CANCELED = 0 - RESULT_FIRST_USER = 1 - - def __init__(self): - super().__init__() - self._created = False - self._started = False - self._resumed = False - self._destroyed = False - self._intent: Optional[Intent] = None - self._callbacks: Dict[int, Tuple[int, Intent]] = {} - self._fragment_manager = FragmentManager() - self._application = None - self._title = "" - self._request_code_counter = 0 - - def onCreate(self, savedInstanceState: Optional["Bundle"] = None): - self._created = True - sys.stderr.write(f"[Activity] onCreate: {self.__class__.__name__}\n") - - def onStart(self): - self._started = True - sys.stderr.write(f"[Activity] onStart: {self.__class__.__name__}\n") - - def onResume(self): - self._resumed = True - sys.stderr.write(f"[Activity] onResume: {self.__class__.__name__}\n") - - def onPause(self): - self._resumed = False - sys.stderr.write(f"[Activity] onPause: {self.__class__.__name__}\n") - - def onStop(self): - self._started = False - sys.stderr.write(f"[Activity] onStop: {self.__class__.__name__}\n") - - def onDestroy(self): - self._destroyed = True - self._created = False - sys.stderr.write(f"[Activity] onDestroy: {self.__class__.__name__}\n") - - def onRestart(self): - sys.stderr.write(f"[Activity] onRestart: {self.__class__.__name__}\n") - - def onBackPressed(self): - sys.stderr.write("[Activity] onBackPressed\n") - - def onSaveInstanceState(self, out_state: "Bundle"): - pass - - def onRestoreInstanceState(self, savedInstanceState: "Bundle"): - pass - - def onNewIntent(self, intent: Intent): - self._intent = intent - - def onActivityResult(self, request_code: int, result_code: int, data: Optional[Intent]): - pass - - def onRequestPermissionsResult(self, request_code: int, permissions: List[str], grant_results: List[int]): - pass - - def setContentView(self, layout_res_id: int): - sys.stderr.write(f"[Activity] setContentView: {layout_res_id}\n") - - def findViewById(self, id_: int) -> Any: - return None - - def startActivity(self, intent: Intent): - sys.stderr.write( - f"[Activity] startActivity: action={intent.getAction()} " - f"pkg={intent.getPackage()}\n" - ) - - def startActivityForResult(self, intent: Intent, request_code: int, options: Optional["Bundle"] = None): - sys.stderr.write( - f"[Activity] startActivityForResult: code={request_code} " - f"action={intent.getAction()}\n" - ) - - def setResult(self, result_code: int, data: Optional[Intent] = None): - sys.stderr.write(f"[Activity] setResult: code={result_code}\n") - - def finish(self): - sys.stderr.write(f"[Activity] finish: {self.__class__.__name__}\n") - self.onPause() - self.onStop() - self.onDestroy() - - def finishActivity(self, request_code: int): - pass - - def getIntent(self) -> Optional[Intent]: - return self._intent - - def setIntent(self, intent: Intent): - self._intent = intent - - def getString(self, res_id: int, *args) -> str: - return str(res_id) - - def getResources(self): - return None - - def getApplication(self): - return self._application - - def getFragmentManager(self) -> FragmentManager: - return self._fragment_manager - - def getSupportFragmentManager(self) -> FragmentManager: - return self._fragment_manager - - def getPackageManager(self): - from .content_pm import PackageManager - return PackageManager() - - def getPackageName(self) -> str: - return _package_name - - def getContentResolver(self): - from .content import ContentResolver - return ContentResolver() - - def getSystemService(self, name: str): - return android_context.getSystemService(name) - - def isFinishing(self) -> bool: - return self._destroyed - - def isDestroyed(self) -> bool: - return self._destroyed - - def recreate(self): - self.onDestroy() - self.onCreate(None) - self.onStart() - self.onResume() - - def getPreferences(self, mode: int): - from .content import SharedPreferences - return SharedPreferences(_package_name + "_preferences") - - def getSharedPreferences(self, name: str, mode: int): - from .content import SharedPreferences - return SharedPreferences(name) - - def getApplicationContext(self): - return android_context - - def getBaseContext(self): - return android_context - - def getCallingActivity(self) -> Optional[ComponentName]: - return None - - def getCallingPackage(self) -> Optional[str]: - return None - - def getTaskId(self) -> int: - return 1 - - def hasWindowFocus(self) -> bool: - return True - - def isInMultiWindowMode(self) -> bool: - return False - - def isInPictureInPictureMode(self) -> bool: - return False - - def moveTaskToBack(self, non_root: bool) -> bool: - return True - - def onLowMemory(self): - pass - - def onTrimMemory(self, level: int): - pass - - def onConfigurationChanged(self, config): - pass - - def onMultiWindowModeChanged(self, is_in_multi_window_mode: bool, new_config): - pass - - def onPictureInPictureModeChanged(self, is_in_pip_mode: bool, new_config): - pass - - # ─── 生命周期 (扩展) ─────────────────────────────────────── - - def onPostCreate(self, savedInstanceState): - pass - - def onPostResume(self): - pass - - def onUserLeaveHint(self): - pass - - def onUserInteraction(self): - pass - - def onCreateContextMenu(self, menu, v, menuInfo): - pass - - def onContextMenuClosed(self, menu): - pass - - def onContextItemSelected(self, item): - return False - - def onWindowFocusChanged(self, hasFocus): - pass - - def onWindowAttributesChanged(self, params): - pass - - def onContentChanged(self): - pass - - def onAttachedToWindow(self): - pass - - def onDetachedFromWindow(self): - pass - - def onTitleChanged(self, title, color): - pass - - # ─── 权限 (扩展) ─────────────────────────────────────────── - - def requestPermissions(self, permissions, requestCode): - pass - - def checkSelfPermission(self, permission): - return 0 # PERMISSION_GRANTED - - def shouldShowRequestPermissionRationale(self, permission): - return False - - # ─── UI (扩展) ───────────────────────────────────────────── - - def getWindow(self): - if not hasattr(self, '_window'): - from .view import Window - self._window = Window() - return self._window - - def getWindowManager(self): - if not hasattr(self, '_window_manager'): - from .view import WindowManager - self._window_manager = WindowManager() - return self._window_manager - - def setRequestedOrientation(self, requestedOrientation): - self._requested_orientation = requestedOrientation - - def getRequestedOrientation(self): - return getattr(self, '_requested_orientation', 0) - - def runOnUiThread(self, action): - import threading - # 在桌面环境直接执行 - action() - - def overridePendingTransition(self, enterAnim, exitAnim): - pass - - def getActionBar(self): - return getattr(self, '_action_bar', None) - - def setActionBar(self, toolbar): - self._action_bar = toolbar - - def getMenuInflater(self): - from .view import MenuInflater - return MenuInflater() - - def onCreateOptionsMenu(self, menu): - return True - - def onPrepareOptionsMenu(self, menu): - return True - - def onOptionsItemSelected(self, item): - return False - - def registerForContextMenu(self, view): - pass - - def unregisterForContextMenu(self, view): - pass - - def openContextMenu(self, view): - pass - - def finishAffinity(self): - self.finish() - - def invalidateOptionsMenu(self): - pass - - def setVisible(self, visible): - self._visible = visible - - def setTheme(self, resid): - self._theme = resid - - def getTheme(self): - return getattr(self, '_theme', 0) - - def getDeviceInfo(self): - """获取设备信息""" - if _USE_LINUX: - return LinuxProcess.get_system_info() - return {} - - def executeShell(self, command): - """执行 Shell 命令""" - if _USE_LINUX: - return LinuxShell.exec(command) - return {"success": False, "output": "Shell not available"} - - def screenshot(self, path=None): - """截屏""" - if _USE_LINUX: - return LinuxDisplay.screenshot(path) - return None - - def getScreenSize(self): - """获取屏幕尺寸""" - if _USE_LINUX: - return LinuxDisplay.get_screen_size() - return (1920, 1080) - - def click(self, x, y): - """点击屏幕坐标""" - if _USE_LINUX: - return LinuxUIAutomation.click(x, y) - return False - - def typeText(self, text): - """输入文本""" - if _USE_LINUX: - return LinuxUIAutomation.type_text(text) - return False - - def pressKey(self, key): - """按键""" - if _USE_LINUX: - return LinuxUIAutomation.press_key(key) - return False - - def requestWindowFeature(self, featureId): - self._window_features = getattr(self, '_window_features', set()) - self._window_features.add(featureId) - return True - - def setTitle(self, title): - self._title = title - - def getTitle(self): - return getattr(self, '_title', '') - - def onKeyDown(self, keyCode, event): - return False - - def onKeyUp(self, keyCode, event): - return False - - def onTouchEvent(self, event): - return False - - def enterPictureInPictureMode(self): - pass - - def getReferrer(self): - return getattr(self, '_referrer', None) - - def setReferrer(self, referrer): - self._referrer = referrer - - def isTaskRoot(self): - return True - - def getChangingConfigurations(self): - return getattr(self, '_changing_config', 0) - - def navigateUpTo(self, intent): - return True - - def shouldUpRecreateTask(self, intent): - return False - - def getParentActivityIntent(self): - return None - - def onOptionsMenuClosed(self, menu): - pass - - def setVolumeControlStream(self, streamType): - self._volume_control_stream = streamType - - def getVolumeControlStream(self): - return getattr(self, '_volume_control_stream', 0) - - def getLocalClassName(self): - return self.__class__.__name__ - - def finishAndRemoveTask(self): - self.finish() - - def getLoaderManager(self): - if not hasattr(self, '_loader_manager'): - self._loader_manager = type('LoaderManager', (), { - 'initLoader': lambda *a: None, - 'restartLoader': lambda *a: None, - 'destroyLoader': lambda *a: None, - 'getLoader': lambda *a: None, - })() - return self._loader_manager - - def onRetainNonConfigurationInstance(self): - return None - - def getLastNonConfigurationInstance(self): - return getattr(self, '_last_non_config_instance', None) - - def setProgressBarVisibility(self, visible): - pass - - def setProgress(self, progress): - pass - - def setSecondaryProgress(self, secondaryProgress): - pass - - def onSearchRequested(self): - return False - - def startSearch(self, initialQuery, selectInitial, appSearchData, globalSearch): - pass - - def startIntentSender(self, intent, fillInIntent, flagsMask, flagsValues, extraFlags): - pass - - def startIntentSenderForResult(self, intent, requestCode, fillInIntent, flagsMask, flagsValues, extraFlags): - pass - - -# ═══════════════════════════════════════════════════════════════ -# Service -# ═══════════════════════════════════════════════════════════════ - -class Service(Stub): - """Service — 后台服务""" - - START_CONTINUATION_MASK = 0xF - START_FLAG_REDELIVERY = 1 - START_FLAG_RETRY = 2 - START_NOT_STICKY = 2 - START_REDELIVER_INTENT = 3 - START_STICKY = 1 - START_STICKY_COMPATIBILITY = 0 - STOP_FOREGROUND_DETACH = 1 - STOP_FOREGROUND_REMOVE = 2 - - def __init__(self): - super().__init__() - self._started = False - - def onCreate(self): - self._started = True - sys.stderr.write(f"[Service] onCreate: {self.__class__.__name__}\n") - - def onStartCommand(self, intent: Optional[Intent], flags: int, start_id: int) -> int: - return Service.START_NOT_STICKY - - def onBind(self, intent: Intent): - return None - - def onUnbind(self, intent: Intent) -> bool: - return False - - def onRebind(self, intent: Intent): - pass - - def onDestroy(self): - self._started = False - sys.stderr.write(f"[Service] onDestroy: {self.__class__.__name__}\n") - - def stopSelf(self): - self.onDestroy() - - def stopSelfResult(self, start_id: int) -> bool: - self.onDestroy() - return True - - def startForeground(self, id_: int, notification: Notification): - pass - - def stopForeground(self, flags: int): - pass - - def getApplication(self): - return None - - def getClass(self): - return self.__class__ - - -# ═══════════════════════════════════════════════════════════════ -# Application -# ═══════════════════════════════════════════════════════════════ - -class Application(Stub): - """Application — 应用级别上下文""" - - def __init__(self): - super().__init__() - self._activity_lifecycle_callbacks: List = [] - self._on_low_memory_listeners: List = [] - self._on_trim_memory_listeners: List = [] - - def onCreate(self): - sys.stderr.write(f"[Application] onCreate: {self.__class__.__name__}\n") - - def onTerminate(self): - pass - - def onConfigurationChanged(self, new_config): - pass - - def onLowMemory(self): - pass - - def onTrimMemory(self, level: int): - pass - - def registerActivityLifecycleCallbacks(self, callback): - self._activity_lifecycle_callbacks.append(callback) - - def unregisterActivityLifecycleCallbacks(self, callback): - try: - self._activity_lifecycle_callbacks.remove(callback) - except ValueError: - pass - - def getApplicationContext(self): - return android_context - - def getPackageManager(self): - from .content_pm import PackageManager - return PackageManager() - - def getContentResolver(self): - from .content import ContentResolver - return ContentResolver() - - def getSystemService(self, name: str): - return android_context.getSystemService(name) - - def getSharedPreferences(self, name: str, mode: int): - from .content import SharedPreferences - return SharedPreferences(name) - - def getFilesDir(self): - return os.path.join(_app_data_dir, "files") - - def getCacheDir(self): - return os.path.join(_app_data_dir, "cache") - - def getBaseContext(self): - return android_context - - -# ═══════════════════════════════════════════════════════════════ -# ActivityManager -# ═══════════════════════════════════════════════════════════════ - -class ActivityManager(Stub): - """ActivityManager — 管理 Activity 运行状态""" - - RECENT_IGNORE_UNAVAILABLE = 0x02 - RECENT_WITH_EXCLUDED = 0x01 - MOVE_TASK_WITH_HOME = 0x01 - MOVE_TASK_NO_USER_ACTION = 0x02 - - class RunningAppProcessInfo(Stub): - """Information about a running process""" - IMPORTANCE_FOREGROUND = 100 - IMPORTANCE_VISIBLE = 200 - IMPORTANCE_SERVICE = 300 - IMPORTANCE_BACKGROUND = 400 - IMPORTANCE_EMPTY = 500 - IMPORTANCE_FOREGROUND_SERVICE = 125 - IMPORTANCE_TOP_SLEEPING = 150 - IMPORTANCE_CACHED = 400 - IMPORTANCE_GONE = 1000 - - def __init__(self): - super().__init__() - self.processName = "" - self.pid = 0 - self.uid = 0 - self.importance = self.IMPORTANCE_FOREGROUND - self.lru = 0 - self.importanceReasonCode = 0 - self.importanceReasonPid = 0 - self.importanceReasonComponent = None - self.pkgList: List[str] = [] - - class AppTask(Stub): - """Represents a task managed by ActivityManager""" - def finishAndRemoveTask(self): - pass - def getTaskInfo(self): - return None - def moveToFront(self): - pass - def setExcludeFromRecents(self, exclude: bool): - pass - - class MemoryInfo(Stub): - """Memory information""" - def __init__(self): - super().__init__() - self.availMem = 0 - self.totalMem = 0 - self.threshold = 0 - self.lowMemory = False - - def __init__(self): - super().__init__() - self._memory_info = self.MemoryInfo() - - def getMemoryInfo(self) -> "ActivityManager.MemoryInfo": - if _USE_LINUX: - return LinuxPower.get_memory_info() - return self._memory_info - - def getRunningAppProcesses(self) -> Optional[List["ActivityManager.RunningAppProcessInfo"]]: - if _USE_LINUX: - return LinuxProcess.list_processes() - return [] - - def killBackgroundProcesses(self, package_name: str): - pass - - def clearApplicationUserData(self) -> bool: - return True - - def getRecentTasks(self, max_num: int, flags: int) -> List: - return [] - - def getAppTasks(self) -> List["ActivityManager.AppTask"]: - return [] - - def moveTaskToFront(self, task_id: int, flags: int): - pass - - def isBackgroundRestricted(self) -> bool: - return False - - def isLowRamDevice(self) -> bool: - return False - - def getLauncherLargeIconSize(self) -> int: - return 48 - - -# ═══════════════════════════════════════════════════════════════ -# TaskStackBuilder -# ═══════════════════════════════════════════════════════════════ - -class TaskStackBuilder(Stub): - """Builder for synthetic back stack""" - - def __init__(self, context): - super().__init__() - self._context = context - self._intents: List[Intent] = [] - - @staticmethod - def create(context) -> "TaskStackBuilder": - return TaskStackBuilder(context) - - def addNextIntent(self, intent: Intent) -> "TaskStackBuilder": - self._intents.append(intent) - return self - - def addNextIntentWithParentStack(self, intent: Intent) -> "TaskStackBuilder": - self._intents.append(intent) - return self - - def addParentStack(self, activity_cls: type) -> "TaskStackBuilder": - return self - - def startActivities(self, options: Optional["Bundle"] = None): - for intent in self._intents: - sys.stderr.write(f"[TaskStackBuilder] start activity: {intent}\n") - - def startActivitiesIfNeeded(self, options: Optional["Bundle"] = None): - self.startActivities(options) - - def getIntent(self, request_code: int) -> Optional[Intent]: - if self._intents: - return self._intents[-1] - return None - - def editIntentAt(self, index: int) -> Optional[Intent]: - if 0 <= index < len(self._intents): - return self._intents[index] - return None - - def getIntentCount(self) -> int: - return len(self._intents) - - -# ═══════════════════════════════════════════════════════════════ -# Bundle (forward reference for Fragment uses) -# ═══════════════════════════════════════════════════════════════ - -from .os import Bundle - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Intent", - "ComponentName", - "PendingIntent", - "AlarmManager", - "Notification", - "NotificationChannel", - "NotificationChannelGroup", - "NotificationManager", - "NotificationCompat", - "Dialog", - "AlertDialog", - "ProgressDialog", - "DatePickerDialog", - "TimePickerDialog", - "Fragment", - "FragmentManager", - "FragmentTransaction", - "Activity", - "Service", - "Application", - "ActivityManager", - "TaskStackBuilder", -] \ No newline at end of file diff --git a/android_pyapi/android/app_actionbar.py b/android_pyapi/android/app_actionbar.py deleted file mode 100644 index c2f0c1f..0000000 --- a/android_pyapi/android/app_actionbar.py +++ /dev/null @@ -1,539 +0,0 @@ -""" -Android ActionBar API — ActionBar, ActionBar.Tab, ActionBar.TabListener, ActionBar.LayoutParams -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -from typing import Any, Dict, List, Optional, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -# DISPLAY_* 常量 -DISPLAY_USE_LOGO = 0x0001 -DISPLAY_SHOW_HOME = 0x0002 -DISPLAY_HOME_AS_UP = 0x0004 -DISPLAY_SHOW_TITLE = 0x0008 -DISPLAY_SHOW_CUSTOM = 0x0010 -DISPLAY_TITLE_MULTIPLE_LINES = 0x0020 - -# NAVIGATION_MODE_* 常量 -NAVIGATION_MODE_STANDARD = 0 -NAVIGATION_MODE_LIST = 1 -NAVIGATION_MODE_TABS = 2 - -# TAB 状态常量 -TAB_STATE_CREATED = 1 -TAB_STATE_INITIALIZED = 2 -TAB_STATE_SELECTED = 3 -TAB_STATE_UNSELECTED = 4 -TAB_STATE_RESELECTED = 5 - -# 其它常量 -SHOW_AS_ACTION_NEVER = 0 -SHOW_AS_ACTION_IF_ROOM = 1 -SHOW_AS_ACTION_ALWAYS = 2 -SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8 -SHOW_AS_ACTION_WITH_TEXT = 4 - - -# ═══════════════════════════════════════════════════════════════ -# ActionBar -# ═══════════════════════════════════════════════════════════════ - -class ActionBar(Stub): - """ActionBar — 操作栏 (桌面存根)""" - - DISPLAY_USE_LOGO = DISPLAY_USE_LOGO - DISPLAY_SHOW_HOME = DISPLAY_SHOW_HOME - DISPLAY_HOME_AS_UP = DISPLAY_HOME_AS_UP - DISPLAY_SHOW_TITLE = DISPLAY_SHOW_TITLE - DISPLAY_SHOW_CUSTOM = DISPLAY_SHOW_CUSTOM - DISPLAY_HOME_AS_UP_FLAG = DISPLAY_HOME_AS_UP - DISPLAY_TITLE_MULTIPLE_LINES = DISPLAY_TITLE_MULTIPLE_LINES - - NAVIGATION_MODE_STANDARD = NAVIGATION_MODE_STANDARD - NAVIGATION_MODE_LIST = NAVIGATION_MODE_LIST - NAVIGATION_MODE_TABS = NAVIGATION_MODE_TABS - - def __init__(self): - super().__init__() - self._mTitle: Optional[str] = None - self._mSubtitle: Optional[str] = None - self._mLogo: Optional[Any] = None - self._mIcon: Optional[Any] = None - self._mCustomView: Optional[Any] = None - self._mDisplayOptions = DISPLAY_SHOW_HOME | DISPLAY_SHOW_TITLE - self._mNavigationMode = NAVIGATION_MODE_STANDARD - self._mSelectedNavigationIndex = 0 - self._mListNavigationCallbacks: Optional[Callable] = None - self._mTabs: List["ActionBar.Tab"] = [] - self._mSelectedTab: Optional["ActionBar.Tab"] = None - self._mHomeButtonEnabled = True - self._mShowing = True - self._mElevation = 0.0 - self._mHideOffset = 0 - self._mHideOnContentScroll = False - self._mBackgroundDrawable: Optional[Any] = None - self._mStackedBackgroundDrawable: Optional[Any] = None - self._mSplitBackgroundDrawable: Optional[Any] = None - self._mCustomViewLayoutParams: Optional["ActionBar.LayoutParams"] = None - self._mContext: Optional[Any] = None - self._mThemedContext: Optional[Any] = None - self._mMenuVisibilityListeners: List[Callable] = [] - self._mMenuVisible = True - self._mShowHideAnimationEnabled = False - self._mShowHideAnimationListener: Optional[Any] = None - self._mActionMode: Optional[Any] = None - self._mActionModeListener: Optional[Any] = None - self._mTabListener: Optional["ActionBar.TabListener"] = None - - def setTitle(self, title: str) -> None: - self._mTitle = title - - def setTitle(self, titleResId: int) -> None: - self._mTitle = f"title_{titleResId}" - - def getTitle(self) -> Optional[str]: - return self._mTitle - - def setSubtitle(self, subtitle: str) -> None: - self._mSubtitle = subtitle - - def setSubtitle(self, subtitleResId: int) -> None: - self._mSubtitle = f"subtitle_{subtitleResId}" - - def getSubtitle(self) -> Optional[str]: - return self._mSubtitle - - def setLogo(self, logo: Any) -> None: - self._mLogo = logo - - def setIcon(self, icon: Any) -> None: - self._mIcon = icon - - def setIcon(self, iconResId: int) -> None: - pass - - def setLogo(self, logoResId: int) -> None: - pass - - def setHomeButtonEnabled(self, enabled: bool) -> None: - self._mHomeButtonEnabled = enabled - - def isHomeButtonEnabled(self) -> bool: - return self._mHomeButtonEnabled - - def setDisplayOptions(self, options: int) -> None: - self._mDisplayOptions = options - - def setDisplayOptions(self, options: int, mask: int) -> None: - self._mDisplayOptions = (self._mDisplayOptions & ~mask) | (options & mask) - - def getDisplayOptions(self) -> int: - return self._mDisplayOptions - - def setNavigationMode(self, mode: int) -> None: - self._mNavigationMode = mode - - def getNavigationMode(self) -> int: - return self._mNavigationMode - - def setSelectedNavigationItem(self, position: int) -> None: - self._mSelectedNavigationIndex = position - if self._mNavigationMode == NAVIGATION_MODE_TABS: - if 0 <= position < len(self._mTabs): - self._mSelectedTab = self._mTabs[position] - - def getSelectedNavigationIndex(self) -> int: - if self._mNavigationMode == NAVIGATION_MODE_TABS and self._mSelectedTab: - try: - return self._mTabs.index(self._mSelectedTab) - except ValueError: - pass - return self._mSelectedNavigationIndex - - def setListNavigationCallbacks(self, adapter: Any, callback: Any) -> None: - self._mListNavigationCallbacks = callback - - def getListNavigationCallbacks(self) -> Optional[Any]: - return self._mListNavigationCallbacks - - def getSelectedTab(self) -> Optional["ActionBar.Tab"]: - return self._mSelectedTab - - def addTab(self, tab: "ActionBar.Tab") -> None: - self._mTabs.append(tab) - if len(self._mTabs) == 1: - self._mSelectedTab = tab - - def addTab(self, tab: "ActionBar.Tab", setSelected: bool) -> None: - self._mTabs.append(tab) - if setSelected: - self._mSelectedTab = tab - - def addTab(self, tab: "ActionBar.Tab", position: int, setSelected: bool) -> None: - if position < 0: - position = 0 - if position > len(self._mTabs): - position = len(self._mTabs) - self._mTabs.insert(position, tab) - if setSelected: - self._mSelectedTab = tab - - def removeTab(self, tab: "ActionBar.Tab") -> None: - if tab in self._mTabs: - self._mTabs.remove(tab) - if self._mSelectedTab is tab: - self._mSelectedTab = self._mTabs[0] if self._mTabs else None - - def removeTabAt(self, position: int) -> None: - if 0 <= position < len(self._mTabs): - tab = self._mTabs[position] - self._mTabs.pop(position) - if self._mSelectedTab is tab: - self._mSelectedTab = self._mTabs[0] if self._mTabs else None - - def removeAllTabs(self) -> None: - self._mTabs.clear() - self._mSelectedTab = None - - def selectTab(self, tab: "ActionBar.Tab") -> None: - if tab in self._mTabs: - old = self._mSelectedTab - self._mSelectedTab = tab - if old and old._mListener: - old._mListener.onTabUnselected(old, None) - if tab._mListener: - tab._mListener.onTabSelected(tab, None) - - def getTabCount(self) -> int: - return len(self._mTabs) - - def getTabAt(self, index: int) -> Optional["ActionBar.Tab"]: - if 0 <= index < len(self._mTabs): - return self._mTabs[index] - return None - - def newTab(self) -> "ActionBar.Tab": - return ActionBar.Tab() - - def setCustomView(self, view: Any) -> None: - self._mCustomView = view - - def setCustomView(self, view: Any, layoutParams: "ActionBar.LayoutParams") -> None: - self._mCustomView = view - self._mCustomViewLayoutParams = layoutParams - - def setCustomView(self, layoutResId: int) -> None: - pass - - def getCustomView(self) -> Optional[Any]: - return self._mCustomView - - def setBackgroundDrawable(self, drawable: Any) -> None: - self._mBackgroundDrawable = drawable - - def setStackedBackgroundDrawable(self, drawable: Any) -> None: - self._mStackedBackgroundDrawable = drawable - - def setSplitBackgroundDrawable(self, drawable: Any) -> None: - self._mSplitBackgroundDrawable = drawable - - def show(self) -> None: - self._mShowing = True - - def hide(self) -> None: - self._mShowing = False - - def isShowing(self) -> bool: - return self._mShowing - - def setShowHideAnimationEnabled(self, enabled: bool) -> None: - self._mShowHideAnimationEnabled = enabled - - def getThemedContext(self) -> Optional[Any]: - return self._mThemedContext or self._mContext - - def setHomeAsUpIndicator(self, indicator: Any) -> None: - pass - - def setHomeAsUpIndicator(self, indicatorResId: int) -> None: - pass - - def setHomeActionContentDescription(self, description: str) -> None: - pass - - def setHomeActionContentDescription(self, resId: int) -> None: - pass - - def setElevation(self, elevation: float) -> None: - self._mElevation = elevation - - def getElevation(self) -> float: - return self._mElevation - - def setHideOffset(self, offset: int) -> None: - self._mHideOffset = offset - - def getHideOffset(self) -> int: - return self._mHideOffset - - def setHideOnContentScrollEnabled(self, hideOnContentScroll: bool) -> None: - self._mHideOnContentScroll = hideOnContentScroll - - def isHideOnContentScrollEnabled(self) -> bool: - return self._mHideOnContentScroll - - def getContext(self) -> Optional[Any]: - return self._mContext - - def addOnMenuVisibilityListener(self, listener: Callable) -> None: - if listener not in self._mMenuVisibilityListeners: - self._mMenuVisibilityListeners.append(listener) - - def removeOnMenuVisibilityListener(self, listener: Callable) -> None: - if listener in self._mMenuVisibilityListeners: - self._mMenuVisibilityListeners.remove(listener) - - def dispatchMenuVisibilityChanged(self, isVisible: bool) -> None: - self._mMenuVisible = isVisible - for listener in self._mMenuVisibilityListeners: - listener(isVisible) - - def startActionMode(self, callback: Any) -> Optional[Any]: - self._mActionMode = callback - return None - - def invalidateOptionsMenu(self) -> None: - pass - - def onConfigurationChanged(self, config: Any) -> None: - pass - - def onDestroy(self) -> None: - self._mTabs.clear() - self._mSelectedTab = None - - def toString(self) -> str: - return ( - f"ActionBar(title={self._mTitle}, subtitle={self._mSubtitle}, " - f"tabs={len(self._mTabs)}, display=0x{self._mDisplayOptions:x}, " - f"showing={self._mShowing})" - ) - - # ═══════════════════════════════════════════════════════════ - # ActionBar.Tab - # ═══════════════════════════════════════════════════════════ - - class Tab(Stub): - """ActionBar.Tab — 操作栏标签""" - - INVALID_POSITION = -1 - - def __init__(self): - super().__init__() - self._mListener: Optional["ActionBar.TabListener"] = None - self._mObjectTag: Optional[Any] = None - self._mPosition = Tab.INVALID_POSITION - self._mIcon: Optional[Any] = None - self._mText: Optional[str] = None - self._mContentDescription: Optional[str] = None - self._mCustomView: Optional[Any] = None - self._mParentActionBar: Optional["ActionBar"] = None - self._mIconResId = 0 - - def getPosition(self) -> int: - return self._mPosition - - def getIcon(self) -> Optional[Any]: - return self._mIcon - - def getText(self) -> Optional[str]: - return self._mText - - def setIcon(self, icon: Any) -> "ActionBar.Tab": - self._mIcon = icon - return self - - def setIcon(self, iconResId: int) -> "ActionBar.Tab": - self._mIconResId = iconResId - return self - - def setText(self, text: str) -> "ActionBar.Tab": - self._mText = text - return self - - def setText(self, textResId: int) -> "ActionBar.Tab": - self._mText = f"tab_text_{textResId}" - return self - - def setCustomView(self, view: Any) -> "ActionBar.Tab": - self._mCustomView = view - return self - - def setCustomView(self, layoutResId: int) -> "ActionBar.Tab": - return self - - def getCustomView(self) -> Optional[Any]: - return self._mCustomView - - def setTag(self, tag: Any) -> "ActionBar.Tab": - self._mObjectTag = tag - return self - - def getTag(self) -> Optional[Any]: - return self._mObjectTag - - def setTabListener(self, listener: "ActionBar.TabListener") -> "ActionBar.Tab": - self._mListener = listener - return self - - def getTabListener(self) -> Optional["ActionBar.TabListener"]: - return self._mListener - - def setContentDescription(self, description: str) -> "ActionBar.Tab": - self._mContentDescription = description - return self - - def getContentDescription(self) -> Optional[str]: - return self._mContentDescription - - def select(self) -> None: - if self._mParentActionBar: - self._mParentActionBar.selectTab(self) - - def isSelected(self) -> bool: - if self._mParentActionBar: - return self._mParentActionBar.getSelectedTab() is self - return False - - def toString(self) -> str: - return f"ActionBar.Tab(text={self._mText}, position={self._mPosition})" - - # ═══════════════════════════════════════════════════════════ - # ActionBar.TabListener - # ═══════════════════════════════════════════════════════════ - - class TabListener(Stub): - """ActionBar.TabListener — 标签事件监听器""" - - def __init__(self): - super().__init__() - - def onTabSelected(self, tab: "ActionBar.Tab", ft: Any) -> None: - pass - - def onTabUnselected(self, tab: "ActionBar.Tab", ft: Any) -> None: - pass - - def onTabReselected(self, tab: "ActionBar.Tab", ft: Any) -> None: - pass - - def toString(self) -> str: - return "ActionBar.TabListener" - - # ═══════════════════════════════════════════════════════════ - # ActionBar.LayoutParams - # ═══════════════════════════════════════════════════════════ - - class LayoutParams(Stub): - """ActionBar.LayoutParams — 操作栏布局参数""" - - # 对齐方式 - GRAVITY_LEFT = 0x03 - GRAVITY_CENTER_HORIZONTAL = 0x01 - GRAVITY_RIGHT = 0x05 - GRAVITY_TOP = 0x30 - GRAVITY_CENTER_VERTICAL = 0x10 - GRAVITY_BOTTOM = 0x50 - GRAVITY_CENTER = 0x11 - - def __init__(self, context: Any = None): - super().__init__() - self._gravity = LayoutParams.GRAVITY_CENTER_VERTICAL - self._width = -1 # MATCH_PARENT - self._height = -2 # WRAP_CONTENT - self._margin_left = 0 - self._margin_top = 0 - self._margin_right = 0 - self._margin_bottom = 0 - - def __init__(self, gravity: int): - super().__init__() - self._gravity = gravity - self._width = -1 - self._height = -1 - - def __init__(self, width: int, height: int): - super().__init__() - self._width = width - self._height = height - self._gravity = LayoutParams.GRAVITY_CENTER_VERTICAL - - def __init__(self, width: int, height: int, gravity: int): - super().__init__() - self._width = width - self._height = height - self._gravity = gravity - - def getGravity(self) -> int: - return self._gravity - - def setGravity(self, gravity: int) -> None: - self._gravity = gravity - - def getWidth(self) -> int: - return self._width - - def setWidth(self, width: int) -> None: - self._width = width - - def getHeight(self) -> int: - return self._height - - def setHeight(self, height: int) -> None: - self._height = height - - def getMargins(self) -> Tuple[int, int, int, int]: - return (self._margin_left, self._margin_top, self._margin_right, self._margin_bottom) - - def setMargins(self, left: int, top: int, right: int, bottom: int) -> None: - self._margin_left = left - self._margin_top = top - self._margin_right = right - self._margin_bottom = bottom - - def toString(self) -> str: - return f"ActionBar.LayoutParams(width={self._width}, height={self._height}, gravity=0x{self._gravity:x})" - - # 兼容多个 __init__ 签名 - def __init__(self, *args, **kwargs): - super().__init__() - self._gravity = LayoutParams.GRAVITY_CENTER_VERTICAL - self._width = -1 - self._height = -1 - self._margin_left = 0 - self._margin_top = 0 - self._margin_right = 0 - self._margin_bottom = 0 - if len(args) == 1: - if isinstance(args[0], int): - self._gravity = args[0] - elif isinstance(args[0], ActionBar.LayoutParams): - other = args[0] - self._width = other._width - self._height = other._height - self._gravity = other._gravity - elif len(args) == 2: - self._width, self._height = args - elif len(args) == 3: - self._width, self._height, self._gravity = args \ No newline at end of file diff --git a/android_pyapi/android/app_admin.py b/android_pyapi/android/app_admin.py deleted file mode 100644 index 6727e86..0000000 --- a/android_pyapi/android/app_admin.py +++ /dev/null @@ -1,424 +0,0 @@ -""" -Android Device Administration API — DevicePolicyManager, DeviceAdminInfo, DeviceAdminReceiver, SystemUpdatePolicy -方法签名完全对齐 Android SDK -""" - -import os as _os -import sys -import tempfile -import shutil -from typing import Any, Dict, List, Optional, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "DevicePolicyManager", - "DeviceAdminInfo", - "DeviceAdminReceiver", - "SystemUpdatePolicy", -] - - -# ═══════════════════════════════════════════════════════════════ -# DevicePolicyManager 常量 -# ═══════════════════════════════════════════════════════════════ - -class DevicePolicyManager(Stub): - """DevicePolicyManager — 设备策略管理器 (桌面存根)""" - - # --- Action 常量 --- - ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN" - ACTION_DEVICE_ADMIN_SET_DOWNLOAD_URL = "android.app.action.DEVICE_ADMIN_SET_DOWNLOAD_URL" - ACTION_ADMIN_POLICY_COMPLIANCE = "android.app.action.ADMIN_POLICY_COMPLIANCE" - ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.PROVISION_MANAGED_PROFILE" - ACTION_PROVISION_FINANCED_DEVICE = "android.app.action.PROVISION_FINANCED_DEVICE" - ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE" - ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED" - ACTION_PROFILE_OWNER_CHANGED = "android.app.action.PROFILE_OWNER_CHANGED" - ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = "android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD" - ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD" - ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION" - ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED" - ACTION_APPLICATION_VERB = "android.app.action.APPLICATION_VERB" - ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED" - ACTION_ORG_OWNED_PROVISIONING = "android.app.action.ORG_OWNED_PROVISIONING" - ACTION_GET_PROVISIONING_MODE = "android.app.action.GET_PROVISIONING_MODE" - ACTION_ADMIN_SERVICE = "android.app.action.ADMIN_SERVICE" - - # --- Password quality constants --- - PASSWORD_QUALITY_UNSPECIFIED = 0 - PASSWORD_QUALITY_BIOMETRIC_WEAK = 0x8000 - PASSWORD_QUALITY_SOMETHING = 0x10000 - PASSWORD_QUALITY_NUMERIC = 0x20000 - PASSWORD_QUALITY_NUMERIC_COMPLEX = 0x30000 - PASSWORD_QUALITY_ALPHABETIC = 0x40000 - PASSWORD_QUALITY_ALPHANUMERIC = 0x50000 - PASSWORD_QUALITY_COMPLEX = 0x60000 - PASSWORD_QUALITY_MANAGED = 0x80000 - - # --- Encryption status --- - ENCRYPTION_STATUS_UNSUPPORTED = 0 - ENCRYPTION_STATUS_INACTIVE = 1 - ENCRYPTION_STATUS_ACTIVATING = 2 - ENCRYPTION_STATUS_ACTIVE = 3 - ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4 - - # --- Keyguard disable flags --- - KEYGUARD_DISABLE_FEATURES_NONE = 0 - KEYGUARD_DISABLE_FEATURES_ALL = 0x7FFFFFFF - KEYGUARD_DISABLE_SECURE_CAMERA = 2 - KEYGUARD_DISABLE_SECURE_NOTIFICATIONS = 4 - KEYGUARD_DISABLE_TRUST_AGENTS = 8 - KEYGUARD_DISABLE_FINGERPRINT = 16 - KEYGUARD_DISABLE_REMOTE_INPUT = 32 - KEYGUARD_DISABLE_FACE = 64 - KEYGUARD_DISABLE_IRIS = 128 - KEYGUARD_DISABLE_PASSCODE = 256 - - # --- Lock screen info flags --- - FLAG_EVOLVING_DEVICE_ADMIN = 1 << 0 - FLAG_PARENT_CAN_ACCESS_LOCK_SCREEN = 1 << 1 - FLAG_SELF_PROFILE_DISABLED = 1 << 2 - - # --- Other constants --- - PERMISSION_GRANT_STATE_DEFAULT = 0 - PERMISSION_GRANT_STATE_GRANTED = 1 - PERMISSION_GRANT_STATE_DENIED = 2 - STATE_USER_SETUP_COMPLETE = 1 - STATE_USER_UNMANAGED = 0 - STATE_USER_MANAGED = 2 - LEAVE_ALL_KEYS = 0xFFFFFFFF - WIPE_EXTERNAL_STORAGE = 1 - WIPE_RESET_PROTECTION_DATA = 2 - WIPE_SILENTLY = 4 - SKIP_SETUP_WIZARD = 1 - SETTINGS_HOME = "settings_home" - SETTINGS_GLOBAL = "settings_global" - SETTINGS_SECURE = "settings_secure" - SETTINGS_SYSTEM = "settings_system" - LOCK_TASK_FEATURE_NONE = 0 - LOCK_TASK_FEATURE_SYSTEM_INFO = 1 - LOCK_TASK_FEATURE_KEYGUARD = 2 - LOCK_TASK_FEATURE_HOME = 4 - LOCK_TASK_FEATURE_GLOBAL_ACTIONS = 8 - LOCK_TASK_FEATURE_NOTIFICATIONS = 16 - LOCK_TASK_FEATURE_OVERVIEW = 32 - LOCK_TASK_FEATURE_CREDENTIALS = 64 - LOCK_TASK_FEATURE_ACCESSIBILITY = 128 - LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START = 256 - - def __init__(self): - super().__init__() - self._active_admins: List[Dict[str, Any]] = [] - self._password_quality = self.PASSWORD_QUALITY_UNSPECIFIED - self._camera_disabled = False - self._keyguard_disabled = False - - def isAdminActive(self, admin: "DeviceAdminInfo") -> bool: - """检查设备管理器是否已激活""" - return any(a.getComponent() == admin.getComponent() for a in self._active_admins) - - def isDeviceOwnerApp(self, packageName: str) -> bool: - """检查应用是否为设备所有者""" - return False - - def isProfileOwnerApp(self, packageName: str) -> bool: - """检查应用是否为配置文件所有者""" - return False - - def resetPassword(self, password: str, flags: int = 0) -> bool: - """重置设备密码 (桌面存根)""" - raise AndroidOnlyError("DevicePolicyManager.resetPassword() 仅在 Android 设备上可用") - - def lockNow(self): - """立即锁定设备""" - raise AndroidOnlyError("DevicePolicyManager.lockNow() 仅在 Android 设备上可用") - - def wipeData(self, flags: int = 0): - """擦除设备数据""" - raise AndroidOnlyError("DevicePolicyManager.wipeData() 仅在 Android 设备上可用") - - def setCameraDisabled(self, admin: "DeviceAdminInfo", disabled: bool): - """设置相机禁用状态""" - self._camera_disabled = disabled - - def setKeyguardDisabled(self, admin: "DeviceAdminInfo", disabled: bool) -> bool: - """设置锁屏禁用状态""" - self._keyguard_disabled = disabled - return True - - def installCaCert(self, admin: "DeviceAdminInfo", certBuffer: bytes) -> bool: - """安装 CA 证书 (桌面存根)""" - raise AndroidOnlyError("DevicePolicyManager.installCaCert() 仅在 Android 设备上可用") - - def uninstallCaCert(self, admin: "DeviceAdminInfo", certBuffer: bytes): - """卸载 CA 证书 (桌面存根)""" - raise AndroidOnlyError("DevicePolicyManager.uninstallCaCert() 仅在 Android 设备上可用") - - def getActiveAdmins(self) -> Optional[List["DeviceAdminInfo"]]: - """获取活跃的管理器列表""" - return list(self._active_admins) if self._active_admins else None - - def createAdminProfileInfo( - self, - packageName: str, - localizedName: str, - parentComponent: Any = None, - adminInfo: Any = None, - profileOwner: bool = False, - ) -> "DeviceAdminInfo": - """创建管理员配置文件信息""" - return DeviceAdminInfo( - packageName=packageName, - localizedName=localizedName, - parentComponent=parentComponent, - adminInfo=adminInfo, - profileOwner=profileOwner, - ) - - def toString(self) -> str: - return f"DevicePolicyManager@active_admins={len(self._active_admins)}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# DeviceAdminInfo -# ═══════════════════════════════════════════════════════════════ - -class DeviceAdminInfo(Stub): - """DeviceAdminInfo — 设备管理员信息""" - - USES_POLICY_WATCH_LOG = "uses_policy_watch_log" - USES_POLICY_DISABLE_KEYGUARD_FEATURES = "uses_policy_disable_keyguard_features" - USES_POLICY_DISABLE_CAMERA = "uses_policy_disable_camera" - USES_POLICY_RESET_PASSWORD = "uses_policy_reset_password" - USES_POLICY_FORCE_LOCK = "uses_policy_force_lock" - USES_POLICY_WIPE_DATA = "uses_policy_wipe_data" - USES_POLICY_EXPIRE_PASSWORD = "uses_policy_expire_password" - USES_POLICY_LIMIT_PASSWORD = "uses_policy_limit_password" - USES_ENCRYPTED_STORAGE = "uses_encrypted_storage" - USES_POLICY_SETS_DEVICE_OWNER = "uses_policy_sets_device_owner" - USES_POLICY_SETS_PROFILE_OWNER = "uses_policy_sets_profile_owner" - USES_POLICY_INSTALL_CERT = "uses_policy_install_cert" - USES_POLICY_UNINSTALL_CERT = "uses_policy_uninstall_cert" - - def __init__( - self, - packageName: str = "", - localizedName: str = "", - parentComponent: Any = None, - adminInfo: Any = None, - profileOwner: bool = False, - ): - super().__init__() - self._packageName = packageName - self._localizedName = localizedName - self._parentComponent = parentComponent - self._adminInfo = adminInfo - self._profileOwner = profileOwner - - def getComponent(self) -> Optional[str]: - """获取组件标识""" - return self._parentComponent - - def getPackageName(self) -> str: - return self._packageName - - def isVisible(self) -> bool: - return True - - def isProfileOwner(self) -> bool: - return self._profileOwner - - def toString(self) -> str: - return f"DeviceAdminInfo(package={self._packageName})" - - def equals(self, other) -> bool: - if not isinstance(other, DeviceAdminInfo): - return False - return self._packageName == other._packageName and self._parentComponent == other._parentComponent - - def hashCode(self) -> int: - return hash(self._packageName) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# DeviceAdminReceiver -# ═══════════════════════════════════════════════════════════════ - -class DeviceAdminReceiver(Stub): - """DeviceAdminReceiver — 设备管理员广播接收器""" - - ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED" - ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED" - ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED" - ACTION_PASSWORD_CHANGED = "android.app.action.ACTION_PASSWORD_CHANGED" - ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED" - ACTION_PASSWORD_SUCCEEDED = "android.app.action.ACTION_PASSWORD_SUCCEEDED" - ACTION_PASSWORD_EXPIRING = "android.app.action.ACTION_PASSWORD_EXPIRING" - ACTION_PROFILE_PROVISIONING_COMPLETE = "android.app.action.PROFILE_PROVISIONING_COMPLETE" - ACTION_BUGREPORT_SHARING_ACCEPTED = "android.app.action.BUGREPORT_SHARING_ACCEPTED" - ACTION_BUGREPORT_SHARING_DECLINED = "android.app.action.BUGREPORT_SHARING_DECLINED" - ACTION_BUGREPORT_FAILED = "android.app.action.BUGREPORT_FAILED" - ACTION_NOTIFY_PENDING_SYSTEM_UPDATE = "android.app.action.NOTIFY_PENDING_SYSTEM_UPDATE" - ACTION_CHOOSE_PRIVATE_KEY_ALIAS = "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS" - ACTION_NETWORK_LOGS_AVAILABLE = "android.app.action.NETWORK_LOGS_AVAILABLE" - ACTION_SECURITY_LOGS_AVAILABLE = "android.app.action.SECURITY_LOGS_AVAILABLE" - ACTION_USER_ADDED = "android.app.action.USER_ADDED" - ACTION_USER_REMOVED = "android.app.action.USER_REMOVED" - ACTION_USER_STARTED = "android.app.action.USER_STARTED" - ACTION_USER_STOPPED = "android.app.action.USER_STOPPED" - ACTION_USER_SWITCHED = "android.app.action.USER_SWITCHED" - ACTION_LOCK_TASK_ENTERING = "android.app.action.LOCK_TASK_ENTERING" - ACTION_LOCK_TASK_EXITING = "android.app.action.LOCK_TASK_EXITING" - - def __init__(self): - super().__init__() - - def onEnabled(self, context: Any, intent: Any): - """管理员启用回调""" - pass - - def onDisabled(self, context: Any, intent: Any): - """管理员禁用回调""" - pass - - def onPasswordChanged(self, context: Any, intent: Any): - """密码更改回调""" - pass - - def onPasswordExpiring(self, context: Any, intent: Any): - """密码过期回调""" - pass - - def onPasswordFailed(self, context: Any, intent: Any): - """密码失败回调""" - pass - - def onPasswordSucceeded(self, context: Any, intent: Any): - """密码成功回调""" - pass - - def onProfileProvisioningComplete(self, context: Any, intent: Any): - """配置文件配置完成回调""" - pass - - def onBugreportSharingAccepted(self, context: Any, intent: Any, bugreportHash: bytes): - """错误报告分享接受回调""" - pass - - def onBugreportSharingDeclined(self, context: Any, intent: Any): - """错误报告分享拒绝回调""" - pass - - def onBugreportFailed(self, context: Any, intent: Any, bugreportHash: bytes): - """错误报告失败回调""" - pass - - def onNotifyPendingSystemUpdate(self, context: Any, intent: Any, updateReceivedTime: int): - """系统更新待处理通知回调""" - pass - - def onNetworkLogsAvailable(self, context: Any, intent: Any, batchToken: int, networkLogsCount: int): - """网络日志可用回调""" - pass - - def onSecurityLogsAvailable(self, context: Any, intent: Any): - """安全日志可用回调""" - pass - - def onUserAdded(self, context: Any, intent: Any, newUserHandle: Any): - """用户添加回调""" - pass - - def onUserRemoved(self, context: Any, intent: Any, removedUserHandle: Any): - """用户移除回调""" - pass - - def onUserStarted(self, context: Any, intent: Any, userHandle: Any): - """用户启动回调""" - pass - - def onUserStopped(self, context: Any, intent: Any, userHandle: Any): - """用户停止回调""" - pass - - def onUserSwitched(self, context: Any, intent: Any, switchedUserHandle: Any): - """用户切换回调""" - pass - - def onLockTaskModeEntering(self, context: Any, intent: Any, pkg: str): - """锁任务模式进入回调""" - pass - - def onLockTaskModeExiting(self, context: Any, intent: Any): - """锁任务模式退出回调""" - pass - - def toString(self) -> str: - return f"DeviceAdminReceiver@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# SystemUpdatePolicy -# ═══════════════════════════════════════════════════════════════ - -class SystemUpdatePolicy(Stub): - """SystemUpdatePolicy — 系统更新策略""" - - TYPE_INSTALL_AUTOMATIC = 1 - TYPE_INSTALL_WINDOWED = 2 - TYPE_POSTPONE = 3 - - def __init__(self, policyType: int = TYPE_INSTALL_AUTOMATIC): - super().__init__() - self._policyType = policyType - self._startTime: Optional[str] = None - self._endTime: Optional[str] = None - - def getPolicyType(self) -> int: - return self._policyType - - def setPolicyType(self, policyType: int): - self._policyType = policyType - - def setInstallWindow(self, startTime: str, endTime: str): - """设置安装窗口时间段 (格式 HH:mm)""" - self._startTime = startTime - self._endTime = endTime - - def getStartTime(self) -> Optional[str]: - return self._startTime - - def getEndTime(self) -> Optional[str]: - return self._endTime - - @staticmethod - def createPolicy(policyType: int) -> "SystemUpdatePolicy": - return SystemUpdatePolicy(policyType=policyType) - - def toString(self) -> str: - return f"SystemUpdatePolicy(type={self._policyType})" - - def equals(self, other) -> bool: - if not isinstance(other, SystemUpdatePolicy): - return False - return self._policyType == other._policyType - - def hashCode(self) -> int: - return self._policyType & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/app_assist.py b/android_pyapi/android/app_assist.py deleted file mode 100644 index 4bb15f4..0000000 --- a/android_pyapi/android/app_assist.py +++ /dev/null @@ -1,469 +0,0 @@ -""" -Android app.assist 包 — AssistManager, AssistStructure, AssistContent -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import sys -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "AssistManager", - "AssistStructure", - "AssistStructureWindowNode", - "AssistStructureViewNode", - "AssistContent", -] - - -# ═══════════════════════════════════════════════════════════════ -# AssistManager -# ═══════════════════════════════════════════════════════════════ - -class AssistManager(Stub): - """AssistManager — 辅助管理器 (桌面存根)""" - - EXTRA_ASSIST_STRUCTURE = "android.app.extra.ASSIST_STRUCTURE" - EXTRA_ASSIST_CONTENT = "android.app.extra.ASSIST_CONTENT" - EXTRA_ASSIST_INPUT_HINT = "android.app.extra.ASSIST_INPUT_HINT" - EXTRA_ASSIST_PACKAGE = "android.app.extra.ASSIST_PACKAGE" - EXTRA_ASSIST_UID = "android.app.extra.ASSIST_UID" - KEY_RECEIVER_EXTRA = "receiver_extra" - KEY_DATA = "data" - KEY_STRUCTURE = "structure" - KEY_CONTENT = "content" - KEY_INPUT_HINT = "input_hint" - KEY_URI = "uri" - KEY_ACTIVITY_ID = "activity_id" - KEY_TIMESTAMP = "timestamp" - KEY_ACTIVITY_NAME = "activity_name" - ASSIST_STATE_UNKNOWN = 0 - ASSIST_STATE_DISALLOWED = 1 - ASSIST_STATE_SUPPORTED = 2 - ASSIST_STATE_ALLOWED = 3 - ASSIST_STRUCTURE_EXTRA_ARGS = "android.app.extra.ASSIST_STRUCTURE_ARGS" - ASSIST_CONTENT_EXTRA_ARGS = "android.app.extra.ASSIST_CONTENT_ARGS" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._assist_state = AssistManager.ASSIST_STATE_ALLOWED - self._assist_content: Optional["AssistContent"] = None - - def showScreenshot(self, screenshot: Any) -> None: - """显示截图""" - raise AndroidOnlyError("showScreenshot 仅在 Android 设备上可用") - - def showContext(self, context_data: Any) -> None: - """显示上下文""" - pass - - def getAssistContent(self) -> Optional["AssistContent"]: - """获取辅助内容""" - return self._assist_content - - def getAssistState(self) -> int: - """获取辅助状态""" - return self._assist_state - - def setAssistContent(self, content: "AssistContent") -> None: - """设置辅助内容""" - self._assist_content = content - - def getAssistContentForIntent(self, intent: Any) -> Optional["AssistContent"]: - """获取 Intent 的辅助内容""" - return None - - def dispatchAssistStructure(self, structure: "AssistStructure") -> None: - """分发辅助结构""" - pass - - def isAssistAllowed(self) -> bool: - """检查是否允许辅助""" - return self._assist_state == AssistManager.ASSIST_STATE_ALLOWED - - def suggestAssistInput(self, input_data: Any) -> None: - """建议辅助输入""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# AssistStructure -# ═══════════════════════════════════════════════════════════════ - -class AssistStructure(Stub): - """AssistStructure — 辅助结构 (桌面存根)""" - - def __init__(self): - super().__init__() - self._activity_name = "" - self._package_name = "" - self._windows: List["AssistStructureWindowNode"] = [] - self._description = "" - self._activity_id = 0 - self._is_home_activity = False - self._global_state: Optional[Any] = None - self._data_valid = True - - def getActivityName(self) -> str: - """获取活动名称""" - return self._activity_name - - def getPackageName(self) -> str: - """获取包名""" - return self._package_name - - def getWindowNodeCount(self) -> int: - """获取窗口节点数量""" - return len(self._windows) - - def getWindowNodeAt(self, index: int) -> Optional["AssistStructureWindowNode"]: - """获取指定索引的窗口节点""" - if 0 <= index < len(self._windows): - return self._windows[index] - return None - - def getDescription(self) -> str: - """获取描述""" - return self._description - - def getActivityId(self) -> int: - """获取活动 ID""" - return self._activity_id - - def getIsHomeActivity(self) -> bool: - """检查是否为桌面活动""" - return self._is_home_activity - - def getGlobalState(self) -> Optional[Any]: - """获取全局状态""" - return self._global_state - - def toString(self) -> str: - return f"AssistStructure(package={self._package_name}, activity={self._activity_name})" - - @staticmethod - def longHashCode(value: int) -> int: - """长哈希码""" - return value & 0x7FFFFFFF - - def ensureData(self) -> None: - """确保数据有效""" - self._data_valid = True - - def populateWindowNodes(self) -> None: - """填充窗口节点""" - pass - - def updateClearCallback(self, callback: Any) -> None: - """更新清除回调""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# AssistStructure.WindowNode -# ═══════════════════════════════════════════════════════════════ - -class AssistStructureWindowNode(Stub): - """AssistStructure.WindowNode — 辅助结构窗口节点""" - - def __init__(self): - super().__init__() - self._title = "" - self._left = 0 - self._top = 0 - self._width = 0 - self._height = 0 - self._display_frame = (0, 0, 0, 0) - self._root_view_node: Optional["AssistStructureViewNode"] = None - - def getTitle(self) -> str: - """获取标题""" - return self._title - - def getLeft(self) -> int: - """获取左坐标""" - return self._left - - def getTop(self) -> int: - """获取顶坐标""" - return self._top - - def getWidth(self) -> int: - """获取宽度""" - return self._width - - def getHeight(self) -> int: - """获取高度""" - return self._height - - def getDisplayFrame(self) -> Tuple[int, int, int, int]: - """获取显示框架""" - return self._display_frame - - def getRootViewNode(self) -> Optional["AssistStructureViewNode"]: - """获取根视图节点""" - return self._root_view_node - - def toString(self) -> str: - return f"WindowNode(title={self._title}, rect=({self._left},{self._top},{self._width}x{self._height}))" - - -# ═══════════════════════════════════════════════════════════════ -# AssistStructure.ViewNode -# ═══════════════════════════════════════════════════════════════ - -class AssistStructureViewNode(Stub): - """AssistStructure.ViewNode — 辅助结构视图节点""" - - def __init__(self): - super().__init__() - self._class_name = "" - self._text = "" - self._text_size = 0.0 - self._text_color = 0 - self._text_background_color = 0 - self._content_description: Optional[str] = None - self._input_type = 0 - self._enabled = True - self._focusable = False - self._focused = False - self._accessibility_focused = False - self._clickable = False - self._long_clickable = False - self._selected = False - self._activated = False - self._checkable = False - self._checked = False - self._context_clickable = False - self._drawing_order = 0 - self._id = 0 - self._label_for = 0 - self._left = 0 - self._top = 0 - self._scroll_x = 0 - self._scroll_y = 0 - self._width = 0 - self._height = 0 - self._alpha = 1.0 - self._autofill_hints: Optional[List[str]] = None - self._opaque = True - self._visibility = 0 - self._assist_blocked = False - self._important_for_accessibility = 1 - self._children: List["AssistStructureViewNode"] = [] - - def getClassName(self) -> str: - return self._class_name - - def getText(self) -> str: - return self._text - - def getTextSize(self) -> float: - return self._text_size - - def getTextColor(self) -> int: - return self._text_color - - def getTextBackgroundColor(self) -> int: - return self._text_background_color - - def getContentDescription(self) -> Optional[str]: - return self._content_description - - def getInputType(self) -> int: - return self._input_type - - def isEnabled(self) -> bool: - return self._enabled - - def isFocusable(self) -> bool: - return self._focusable - - def isFocused(self) -> bool: - return self._focused - - def isAccessibilityFocused(self) -> bool: - return self._accessibility_focused - - def isClickable(self) -> bool: - return self._clickable - - def isLongClickable(self) -> bool: - return self._long_clickable - - def isSelected(self) -> bool: - return self._selected - - def isActivated(self) -> bool: - return self._activated - - def isCheckable(self) -> bool: - return self._checkable - - def isChecked(self) -> bool: - return self._checked - - def isContextClickable(self) -> bool: - return self._context_clickable - - def getDrawingOrder(self) -> int: - return self._drawing_order - - def getId(self) -> int: - return self._id - - def getLabelFor(self) -> int: - return self._label_for - - def getLeft(self) -> int: - return self._left - - def getTop(self) -> int: - return self._top - - def getScrollX(self) -> int: - return self._scroll_x - - def getScrollY(self) -> int: - return self._scroll_y - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def getAlpha(self) -> float: - return self._alpha - - def getAutofillHints(self) -> Optional[List[str]]: - return self._autofill_hints - - def isOpaque(self) -> bool: - return self._opaque - - def getVisibility(self) -> int: - return self._visibility - - def getAssistBlocked(self) -> bool: - return self._assist_blocked - - def getImportantForAccessibility(self) -> int: - return self._important_for_accessibility - - def getChildCount(self) -> int: - return len(self._children) - - def getChildAt(self, index: int) -> Optional["AssistStructureViewNode"]: - if 0 <= index < len(self._children): - return self._children[index] - return None - - def toString(self) -> str: - return f"ViewNode(class={self._class_name}, text={self._text})" - - -# ═══════════════════════════════════════════════════════════════ -# AssistContent -# ═══════════════════════════════════════════════════════════════ - -class AssistContent(Stub): - """AssistContent — 辅助内容 (桌面存根)""" - - def __init__(self): - super().__init__() - self._intent: Optional[Any] = None - self._clip_data: Optional[Any] = None - self._structured_data: Optional[str] = None - self._provider_uri: Optional[str] = None - self._extras: Optional[Any] = None - self._is_app_provided_intent = False - self._is_app_provided_web_uri = False - self._web_uri: Optional[str] = None - self._uri: Optional[str] = None - - def getIntent(self) -> Optional[Any]: - """获取 Intent""" - return self._intent - - def setIntent(self, intent: Any) -> None: - """设置 Intent""" - self._intent = intent - self._is_app_provided_intent = True - - def setClipData(self, clip_data: Any) -> None: - """设置剪贴数据""" - self._clip_data = clip_data - - def getClipData(self) -> Optional[Any]: - """获取剪贴数据""" - return self._clip_data - - def getStructuredData(self) -> Optional[str]: - """获取结构化数据""" - return self._structured_data - - def setStructuredData(self, data: str) -> None: - """设置结构化数据""" - self._structured_data = data - - def getProviderUri(self) -> Optional[str]: - """获取提供商 URI""" - return self._provider_uri - - def setProviderUri(self, uri: str) -> None: - """设置提供商 URI""" - self._provider_uri = uri - - def getExtras(self) -> Optional[Any]: - """获取附加信息""" - return self._extras - - def setExtras(self, extras: Any) -> None: - """设置附加信息""" - self._extras = extras - - def isAppProvidedIntent(self) -> bool: - """检查是否为应用提供的 Intent""" - return self._is_app_provided_intent - - def isAppProvidedWebUri(self) -> bool: - """检查是否为应用提供的 Web URI""" - return self._is_app_provided_web_uri - - def getWebUri(self) -> Optional[str]: - """获取 Web URI""" - return self._web_uri - - def setWebUri(self, uri: str) -> None: - """设置 Web URI""" - self._web_uri = uri - self._is_app_provided_web_uri = True - - def getUri(self) -> Optional[str]: - """获取 URI""" - return self._uri - - def getLaunchUri(self) -> Optional[str]: - """获取启动 URI""" - return self._uri - - def clone(self) -> "AssistContent": - """克隆""" - new = AssistContent() - new._intent = self._intent - new._structured_data = self._structured_data - new._provider_uri = self._provider_uri - new._uri = self._uri - new._web_uri = self._web_uri - return new - - def toString(self) -> str: - return f"AssistContent(intent={self._intent})" \ No newline at end of file diff --git a/android_pyapi/android/app_backup.py b/android_pyapi/android/app_backup.py deleted file mode 100644 index 5e23961..0000000 --- a/android_pyapi/android/app_backup.py +++ /dev/null @@ -1,477 +0,0 @@ -""" -Android Backup API — BackupManager, BackupAgent, BackupDataInput/Output, RestoreObserver 等 -方法签名完全对齐 Android SDK -""" - -import json -import os as _os -import sys -import tempfile -import shutil -import time -from datetime import datetime -from typing import Any, Dict, List, Optional, Set, Tuple, Callable, BinaryIO - -from .._core import Stub, AndroidOnlyError, resolve_android_path, _app_data_dir -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "BackupManager", - "BackupAgent", - "BackupDataInput", - "BackupDataOutput", - "FullBackupDataOutput", - "BackupHelper", - "FileBackupHelper", - "SharedPreferencesBackupHelper", - "RestoreObserver", - "BackupDataInputStream", -] - - -# ═══════════════════════════════════════════════════════════════ -# 备份存储路径 -# ═══════════════════════════════════════════════════════════════ - -_BACKUP_DIR = _os.path.join(_app_data_dir, "backups") - - -def _ensure_backup_dir(): - _os.makedirs(_BACKUP_DIR, exist_ok=True) - - -_ensure_backup_dir() - - -# ═══════════════════════════════════════════════════════════════ -# BackupManager -# ═══════════════════════════════════════════════════════════════ - -class BackupManager(Stub): - """BackupManager — 备份管理器 (桌面文件备份模拟)""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._backup_enabled = True - - def dataChanged(self, packageName: str = None): - """通知系统数据已更改,需要备份""" - if packageName: - flag_path = _os.path.join(_BACKUP_DIR, f"{packageName}.pending") - elif self._context: - flag_path = _os.path.join(_BACKUP_DIR, f"{self._context.getPackageName()}.pending") - else: - flag_path = _os.path.join(_BACKUP_DIR, "default.pending") - with open(flag_path, "w") as f: - f.write(time.time()) - - def requestRestore(self, observer: "RestoreObserver") -> int: - """请求恢复备份数据""" - raise AndroidOnlyError("BackupManager.requestRestore() 仅在 Android 设备上可用") - - def getCurrentTransport(self) -> Optional[str]: - """获取当前备份传输名称""" - return "com.android.localtransport.LocalTransport" - - def listAllTransports(self) -> List[str]: - """列出所有可用备份传输""" - return ["com.android.localtransport.LocalTransport"] - - def isBackupEnabled(self) -> bool: - """检查备份是否启用""" - return self._backup_enabled - - def setBackupEnabled(self, isEnabled: bool): - """设置备份启用状态""" - self._backup_enabled = isEnabled - - def toString(self) -> str: - return f"BackupManager(backup_enabled={self._backup_enabled})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# BackupAgent -# ═══════════════════════════════════════════════════════════════ - -class BackupAgent(Stub): - """BackupAgent — 备份代理基类""" - - def __init__(self): - super().__init__() - - def onCreate(self): - """创建备份代理时调用""" - pass - - def onBackup( - self, - oldState: "BackupDataInput", - data: "BackupDataOutput", - newState: "BackupDataOutput", - ): - """执行备份操作""" - pass - - def onRestore( - self, - data: "BackupDataInput", - appVersionCode: int, - newState: "BackupDataOutput", - ): - """执行恢复操作""" - pass - - def toString(self) -> str: - return f"BackupAgent@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# BackupDataInput -# ═══════════════════════════════════════════════════════════════ - -class BackupDataInput(Stub): - """BackupDataInput — 备份数据输入流""" - - def __init__(self, filepath: str = ""): - super().__init__() - self._filepath = filepath - self._data: Dict[str, Any] = {} - self._keys: List[str] = [] - self._index = 0 - if filepath and _os.path.exists(filepath): - with open(filepath, "r") as f: - self._data = json.load(f) - self._keys = list(self._data.keys()) - - def readNextHeader(self) -> bool: - """读取下一个数据头""" - if self._index < len(self._keys): - return True - return False - - def getKey(self) -> str: - """获取当前键名""" - if self._index < len(self._keys): - return self._keys[self._index] - return "" - - def getDataSize(self) -> int: - """获取当前数据大小""" - if self._index < len(self._keys): - val = self._data[self._keys[self._index]] - return len(str(val)) - return 0 - - def readEntityData(self, data: bytearray, offset: int, size: int) -> int: - """读取实体数据""" - if self._index < len(self._keys): - val = str(self._data[self._keys[self._index]]) - raw = val.encode("utf-8") - copy_len = min(size, len(raw) - offset) - for i in range(copy_len): - data[offset + i] = raw[i] - self._index += 1 - return copy_len - return -1 - - def skipEntityData(self): - """跳过当前实体数据""" - self._index += 1 - - def toString(self) -> str: - return f"BackupDataInput(file={self._filepath})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# BackupDataOutput -# ═══════════════════════════════════════════════════════════════ - -class BackupDataOutput(Stub): - """BackupDataOutput — 备份数据输出流""" - - def __init__(self, filepath: str = ""): - super().__init__() - self._filepath = filepath - self._data: Dict[str, str] = {} - - def writeEntityHeader(self, key: str, dataSize: int): - """写入实体数据头""" - pass - - def writeEntityData(self, data: bytes, size: int): - """写入实体数据""" - key = f"entity_{len(self._data)}" - self._data[key] = data[:size].decode("utf-8", errors="replace") - if self._filepath: - _os.makedirs(_os.path.dirname(self._filepath), exist_ok=True) - with open(self._filepath, "w") as f: - json.dump(self._data, f) - - def toString(self) -> str: - return f"BackupDataOutput(file={self._filepath})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# FullBackupDataOutput -# ═══════════════════════════════════════════════════════════════ - -class FullBackupDataOutput(Stub): - """FullBackupDataOutput — 完整备份数据输出""" - - def __init__(self, quota: int = 0, transportFlags: int = 0): - super().__init__() - self._quota = quota - self._transportFlags = transportFlags - - def getQuota(self) -> int: - return self._quota - - def getTransportFlags(self) -> int: - return self._transportFlags - - def toString(self) -> str: - return f"FullBackupDataOutput(quota={self._quota})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# BackupHelper -# ═══════════════════════════════════════════════════════════════ - -class BackupHelper(Stub): - """BackupHelper — 备份辅助接口""" - - def __init__(self): - super().__init__() - - def performBackup( - self, - oldState: "BackupDataInput", - data: "BackupDataOutput", - newState: "BackupDataOutput", - ): - """执行备份""" - raise AndroidOnlyError("BackupHelper.performBackup() 仅在 Android 设备上可用") - - def restoreEntity(self, data: "BackupDataInput", appVersionCode: int): - """恢复实体""" - raise AndroidOnlyError("BackupHelper.restoreEntity() 仅在 Android 设备上可用") - - def toString(self) -> str: - return f"BackupHelper@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# FileBackupHelper -# ═══════════════════════════════════════════════════════════════ - -class FileBackupHelper(BackupHelper): - """FileBackupHelper — 文件备份辅助""" - - def __init__(self, context: Any, files: List[str]): - super().__init__() - self._context = context - self._files = list(files) - - def performBackup( - self, - oldState: "BackupDataInput", - data: "BackupDataOutput", - newState: "BackupDataOutput", - ): - """执行文件备份""" - for fname in self._files: - if self._context: - filepath = _os.path.join(self._context.getFilesDir(), fname) - if _os.path.exists(filepath): - with open(filepath, "rb") as f: - content = f.read() - data.writeEntityHeader(fname, len(content)) - data.writeEntityData(content, len(content)) - - def restoreEntity(self, data: "BackupDataInput", appVersionCode: int): - """恢复文件实体""" - key = data.getKey() - if self._context and key in self._files: - filepath = _os.path.join(self._context.getFilesDir(), key) - size = data.getDataSize() - buf = bytearray(size) - data.readEntityData(buf, 0, size) - _os.makedirs(_os.path.dirname(filepath), exist_ok=True) - with open(filepath, "wb") as f: - f.write(buf) - - def toString(self) -> str: - return f"FileBackupHelper(files={self._files})" - - def equals(self, other) -> bool: - if not isinstance(other, FileBackupHelper): - return False - return self._files == other._files - - def hashCode(self) -> int: - return hash(tuple(self._files)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# SharedPreferencesBackupHelper -# ═══════════════════════════════════════════════════════════════ - -class SharedPreferencesBackupHelper(BackupHelper): - """SharedPreferencesBackupHelper — SharedPreferences 备份辅助""" - - def __init__(self, context: Any, prefNames: List[str]): - super().__init__() - self._context = context - self._prefNames = list(prefNames) - - def performBackup( - self, - oldState: "BackupDataInput", - data: "BackupDataOutput", - newState: "BackupDataOutput", - ): - """执行 SharedPreferences 备份""" - if not self._context: - return - prefs_dir = _os.path.join(_app_data_dir, "shared_prefs") - for name in self._prefNames: - filepath = _os.path.join(prefs_dir, f"{name}.xml") - if _os.path.exists(filepath): - with open(filepath, "rb") as f: - content = f.read() - data.writeEntityHeader(name, len(content)) - data.writeEntityData(content, len(content)) - - def restoreEntity(self, data: "BackupDataInput", appVersionCode: int): - """恢复 SharedPreferences""" - key = data.getKey() - if self._context and key in self._prefNames: - prefs_dir = _os.path.join(_app_data_dir, "shared_prefs") - filepath = _os.path.join(prefs_dir, f"{key}.xml") - size = data.getDataSize() - buf = bytearray(size) - data.readEntityData(buf, 0, size) - _os.makedirs(prefs_dir, exist_ok=True) - with open(filepath, "wb") as f: - f.write(buf) - - def toString(self) -> str: - return f"SharedPreferencesBackupHelper(prefs={self._prefNames})" - - def equals(self, other) -> bool: - if not isinstance(other, SharedPreferencesBackupHelper): - return False - return self._prefNames == other._prefNames - - def hashCode(self) -> int: - return hash(tuple(self._prefNames)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# RestoreObserver -# ═══════════════════════════════════════════════════════════════ - -class RestoreObserver(Stub): - """RestoreObserver — 恢复过程观察者""" - - def __init__(self): - super().__init__() - - def onUpdate(self, nowBeingRestored: int, currentPackage: str): - """恢复进度更新回调""" - pass - - def onRestoreStarting(self, numPackages: int): - """恢复开始回调""" - pass - - def onRestoreFinished(self, error: int): - """恢复完成回调""" - pass - - def toString(self) -> str: - return f"RestoreObserver@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# BackupDataInputStream -# ═══════════════════════════════════════════════════════════════ - -class BackupDataInputStream(Stub): - """BackupDataInputStream — 备份数据输入流包装器""" - - def __init__(self, dataInput: "BackupDataInput" = None): - super().__init__() - self._input = dataInput - self._pos = 0 - self._size = 0 - - def read(self) -> int: - """读取一个字节""" - if self._input and self._pos < self._size: - self._pos += 1 - return 0 - return -1 - - def readFully(self, data: bytearray, offset: int, length: int): - """读取完整数据块""" - if self._input: - self._input.readEntityData(data, offset, length) - - def size(self) -> int: - return self._size - - def toString(self) -> str: - return f"BackupDataInputStream(size={self._size})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/app_dialog.py b/android_pyapi/android/app_dialog.py deleted file mode 100644 index 3c70c50..0000000 --- a/android_pyapi/android/app_dialog.py +++ /dev/null @@ -1,874 +0,0 @@ -""" -Android Dialog API — Dialog, AlertDialog, DatePickerDialog, TimePickerDialog, ProgressDialog, etc. -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import time -from datetime import datetime -from typing import Any, Dict, List, Optional, Set, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -# BUTTON_* 常量 -BUTTON_POSITIVE = -1 -BUTTON_NEGATIVE = -2 -BUTTON_NEUTRAL = -3 - -# THEME_* 常量 -THEME_TRADITIONAL = 0 -THEME_DEVICE_DEFAULT_DARK = 1 -THEME_DEVICE_DEFAULT_LIGHT = 2 -THEME_HOLO_DARK = 3 -THEME_HOLO_LIGHT = 4 -THEME_MATERIAL_DARK = 5 -THEME_MATERIAL_LIGHT = 6 - -# Dialog 常量 -DISMISS_OTHER = 0 -DISMISS_BUTTON_POSITIVE = 1 -DISMISS_BUTTON_NEGATIVE = 2 -DISMISS_BUTTON_NEUTRAL = 3 - - -# ═══════════════════════════════════════════════════════════════ -# DialogInterface -# ═══════════════════════════════════════════════════════════════ - -class DialogInterface(Stub): - """DialogInterface — 对话框接口""" - - BUTTON_POSITIVE = BUTTON_POSITIVE - BUTTON_NEGATIVE = BUTTON_NEGATIVE - BUTTON_NEUTRAL = BUTTON_NEUTRAL - - def __init__(self): - super().__init__() - - def cancel(self) -> None: - pass - - def dismiss(self) -> None: - pass - - class OnClickListener(Stub): - """DialogInterface.OnClickListener — 点击监听器""" - - def __init__(self): - super().__init__() - - def onClick(self, dialog: Any, which: int) -> None: - pass - - def toString(self) -> str: - return "OnClickListener" - - class OnDismissListener(Stub): - """DialogInterface.OnDismissListener — 消除监听器""" - - def __init__(self): - super().__init__() - - def onDismiss(self, dialog: Any) -> None: - pass - - def toString(self) -> str: - return "OnDismissListener" - - class OnCancelListener(Stub): - """DialogInterface.OnCancelListener — 取消监听器""" - - def __init__(self): - super().__init__() - - def onCancel(self, dialog: Any) -> None: - pass - - def toString(self) -> str: - return "OnCancelListener" - - class OnMultiChoiceClickListener(Stub): - """DialogInterface.OnMultiChoiceClickListener — 多选点击监听器""" - - def __init__(self): - super().__init__() - - def onClick(self, dialog: Any, which: int, isChecked: bool) -> None: - pass - - def toString(self) -> str: - return "OnMultiChoiceClickListener" - - class OnShowListener(Stub): - """DialogInterface.OnShowListener — 显示监听器""" - - def __init__(self): - super().__init__() - - def onShow(self, dialog: Any) -> None: - pass - - def toString(self) -> str: - return "OnShowListener" - - class OnKeyListener(Stub): - """DialogInterface.OnKeyListener — 按键监听器""" - - def __init__(self): - super().__init__() - - def onKey(self, dialog: Any, keyCode: int, event: Any) -> bool: - return False - - def toString(self) -> str: - return "OnKeyListener" - - def toString(self) -> str: - return "DialogInterface" - - -# ═══════════════════════════════════════════════════════════════ -# Dialog -# ═══════════════════════════════════════════════════════════════ - -class Dialog(DialogInterface): - """Dialog — 对话框基类 (桌面存根)""" - - def __init__(self, context: Any = None, themeResId: int = 0): - super().__init__() - self._context = context - self._theme_res_id = themeResId - self._mShowing = False - self._mCancelable = True - self._mCanceled = False - self._mCancelMessage: Optional[Any] = None - self._mDismissMessage: Optional[Any] = None - self._mShowMessage: Optional[Any] = None - self._mWindow: Optional[Any] = None - self._mOwnerActivity: Optional[Any] = None - self._mOnCancelListener: Optional[DialogInterface.OnCancelListener] = None - self._mOnDismissListener: Optional[DialogInterface.OnDismissListener] = None - self._mOnShowListener: Optional[DialogInterface.OnShowListener] = None - self._mOnKeyListener: Optional[DialogInterface.OnKeyListener] = None - self._mCancelListeners: List[DialogInterface.OnCancelListener] = [] - self._mDismissListeners: List[DialogInterface.OnDismissListener] = [] - self._mShowListeners: List[DialogInterface.OnShowListener] = [] - self._mCreated = False - self._mDecor: Optional[Any] = None - self._mActionMode: Optional[Any] = None - self._mActionModeType: Optional[Any] = None - self._mDismissRunnable: Optional[Any] = None - - def show(self) -> None: - self._mShowing = True - if self._mOnShowListener: - self._mOnShowListener.onShow(self) - for listener in self._mShowListeners: - listener.onShow(self) - - def hide(self) -> None: - self._mShowing = False - - def dismiss(self) -> None: - self._mShowing = False - if self._mOnDismissListener: - self._mOnDismissListener.onDismiss(self) - for listener in self._mDismissListeners: - listener.onDismiss(self) - - def cancel(self) -> None: - if not self._mCancelable: - return - self._mCanceled = True - if self._mOnCancelListener: - self._mOnCancelListener.onCancel(self) - for listener in self._mCancelListeners: - listener.onCancel(self) - self.dismiss() - - def isShowing(self) -> bool: - return self._mShowing - - def setCancelable(self, flag: bool) -> None: - self._mCancelable = flag - - def setCanceledOnTouchOutside(self, cancel: bool) -> None: - pass - - def isCancelable(self) -> bool: - return self._mCancelable - - def getContext(self) -> Optional[Any]: - return self._context - - def getWindow(self) -> Optional[Any]: - return self._mWindow - - def getOwnerActivity(self) -> Optional[Any]: - return self._mOwnerActivity - - def setOwnerActivity(self, activity: Any) -> None: - self._mOwnerActivity = activity - - def setOnCancelListener(self, listener: Optional[DialogInterface.OnCancelListener]) -> None: - self._mOnCancelListener = listener - - def setOnDismissListener(self, listener: Optional[DialogInterface.OnDismissListener]) -> None: - self._mOnDismissListener = listener - - def setOnShowListener(self, listener: Optional[DialogInterface.OnShowListener]) -> None: - self._mOnShowListener = listener - - def setOnKeyListener(self, listener: Optional[DialogInterface.OnKeyListener]) -> None: - self._mOnKeyListener = listener - - def setCancelMessage(self, msg: Any) -> None: - self._mCancelMessage = msg - - def setDismissMessage(self, msg: Any) -> None: - self._mDismissMessage = msg - - def setTitle(self, title: str) -> None: - pass - - def setVolumeControlStream(self, streamType: int) -> None: - pass - - def getVolumeControlStream(self) -> int: - return -1 - - def onWindowFocusChanged(self, hasFocus: bool) -> None: - pass - - def onBackPressed(self) -> None: - self.cancel() - - def onAttachedToWindow(self) -> None: - pass - - def onDetachedFromWindow(self) -> None: - pass - - def onContentChanged(self) -> None: - pass - - def onWindowAttributesChanged(self, attrs: Any) -> None: - pass - - def stopManagingActionMode(self, actionMode: Any) -> None: - pass - - def startManagingActionMode(self, actionMode: Any) -> None: - pass - - def takeActionMode(self, actionMode: Any) -> None: - pass - - def onActionModeStarted(self, actionMode: Any) -> None: - pass - - def onActionModeFinished(self, actionMode: Any) -> None: - pass - - def addContentView(self, view: Any, params: Any) -> None: - pass - - def setContentView(self, viewOrRes: Any, params: Any = None) -> None: - pass - - def requireViewById(self, id: int) -> Any: - return None - - def requireViewById(self, id: int) -> Any: - result = self.findViewById(id) - if result is None: - raise IllegalArgumentException(f"View id=0x{id:x} not found") - return result - - def requestWindowFeature(self, featureId: int) -> bool: - return True - - def getLayoutInflater(self) -> Any: - return None - - def onRestoreInstanceState(self, savedInstanceState: Any) -> None: - pass - - def onSaveInstanceState(self) -> Any: - return None - - def toString(self) -> str: - return f"Dialog(showing={self._mShowing}, cancelable={self._mCancelable})" - - -# ═══════════════════════════════════════════════════════════════ -# AlertDialog -# ═══════════════════════════════════════════════════════════════ - -class AlertDialog(Dialog): - """AlertDialog — 警示对话框""" - - THEME_DEVICE_DEFAULT_DARK = THEME_DEVICE_DEFAULT_DARK - THEME_DEVICE_DEFAULT_LIGHT = THEME_DEVICE_DEFAULT_LIGHT - THEME_HOLO_DARK = THEME_HOLO_DARK - THEME_HOLO_LIGHT = THEME_HOLO_LIGHT - THEME_MATERIAL_DARK = THEME_MATERIAL_DARK - THEME_MATERIAL_LIGHT = THEME_MATERIAL_LIGHT - THEME_TRADITIONAL = THEME_TRADITIONAL - - def __init__(self, context: Any, themeResId: int = 0): - super().__init__(context, themeResId) - self._mTitle: Optional[str] = None - self._mMessage: Optional[str] = None - self._mIcon: Optional[Any] = None - self._mIconAttrId = 0 - self._mPositiveButtonText: Optional[str] = None - self._mNegativeButtonText: Optional[str] = None - self._mNeutralButtonText: Optional[str] = None - self._mPositiveButtonListener: Optional[DialogInterface.OnClickListener] = None - self._mNegativeButtonListener: Optional[DialogInterface.OnClickListener] = None - self._mNeutralButtonListener: Optional[DialogInterface.OnClickListener] = None - self._mItems: Optional[List[str]] = None - self._mItemClickListener: Optional[DialogInterface.OnClickListener] = None - self._mAdapter: Optional[Any] = None - self._mMultiChoiceItems: Optional[List[str]] = None - self._mMultiChoiceListener: Optional[DialogInterface.OnMultiChoiceClickListener] = None - self._mCheckedItems: Optional[List[bool]] = None - self._mView: Optional[Any] = None - self._mViewSpacingLeft = 0 - self._mViewSpacingTop = 0 - self._mViewSpacingRight = 0 - self._mViewSpacingBottom = 0 - self._mViewSpacingSpecified = False - self._mCancelable = True - self._mCancelListener: Optional[DialogInterface.OnCancelListener] = None - self._mDismissListener: Optional[DialogInterface.OnDismissListener] = None - self._mKeyListener: Optional[DialogInterface.OnKeyListener] = None - self._mCustomTitleView: Optional[Any] = None - self._mMessageView: Optional[Any] = None - self._mScrollView: Optional[Any] = None - self._mListView: Optional[Any] = None - self._mButtonPanelLayout: Optional[Any] = None - self._mAlertDialogLayout = 0 - self._mButtonPanelSideLayout = 0 - self._mListLayout = 0 - self._mMultiChoiceLayout = 0 - self._mSingleChoiceLayout = 0 - self._mProgressLayout = 0 - self._mProgressNumber: Optional[str] = None - self._mProgressPercent: Optional[str] = None - self._mProgressStyle = 0 - self._mProgressValue = 0 - self._mProgressValueMax = 100 - self._mProgressIndeterminate = False - self._mCheckedItem = -1 - self._mForceInverseBackground = False - self._mRecycleOnMeasure = True - self._mShowTitle = True - - def getButton(self, whichButton: int) -> Optional[Any]: - return None - - def setTitle(self, title: str) -> None: - self._mTitle = title - - def setMessage(self, message: str) -> None: - self._mMessage = message - - def setIcon(self, icon: Any) -> None: - self._mIcon = icon - - def setIconAttribute(self, attrId: int) -> None: - self._mIconAttrId = attrId - - def setCustomTitle(self, customTitleView: Any) -> None: - self._mCustomTitleView = customTitleView - - def setView(self, view: Any) -> None: - self._mView = view - - def setView(self, view: Any, viewSpacingLeft: int, viewSpacingTop: int, viewSpacingRight: int, viewSpacingBottom: int) -> None: - self._mView = view - self._mViewSpacingLeft = viewSpacingLeft - self._mViewSpacingTop = viewSpacingTop - self._mViewSpacingRight = viewSpacingRight - self._mViewSpacingBottom = viewSpacingBottom - self._mViewSpacingSpecified = True - - def setButton(self, whichButton: int, text: Any, listenerOrMsg: Any = None) -> None: - text_str = str(text) if not isinstance(text, str) else text - if whichButton == BUTTON_POSITIVE: - self._mPositiveButtonText = text_str - if isinstance(listenerOrMsg, DialogInterface.OnClickListener): - self._mPositiveButtonListener = listenerOrMsg - elif whichButton == BUTTON_NEGATIVE: - self._mNegativeButtonText = text_str - if isinstance(listenerOrMsg, DialogInterface.OnClickListener): - self._mNegativeButtonListener = listenerOrMsg - elif whichButton == BUTTON_NEUTRAL: - self._mNeutralButtonText = text_str - if isinstance(listenerOrMsg, DialogInterface.OnClickListener): - self._mNeutralButtonListener = listenerOrMsg - - def setIconAttribute(self, attrId: int) -> None: - self._mIconAttrId = attrId - - def getListView(self) -> Optional[Any]: - return self._mListView - - def show(self) -> None: - super().show() - # 模拟按钮点击触发 - if self._mPositiveButtonListener: - pass - if self._mNegativeButtonListener: - pass - if self._mNeutralButtonListener: - pass - - def toString(self) -> str: - return f"AlertDialog(title={self._mTitle}, message={self._mMessage})" - - # ═══════════════════════════════════════════════════════════ - # AlertDialog.Builder - # ═══════════════════════════════════════════════════════════ - - class Builder(Stub): - """AlertDialog.Builder — 警示对话框构建器""" - - def __init__(self, context: Any): - super().__init__() - self._P = AlertDialog(context) - self._context = context - - def setTitle(self, titleOrId: Any) -> "AlertDialog.Builder": - if isinstance(titleOrId, int): - self._P._mTitle = f"title_{titleOrId}" - else: - self._P._mTitle = str(titleOrId) - return self - - def setCustomTitle(self, customTitleView: Any) -> "AlertDialog.Builder": - self._P._mCustomTitleView = customTitleView - return self - - def setMessage(self, messageOrId: Any) -> "AlertDialog.Builder": - if isinstance(messageOrId, int): - self._P._mMessage = f"msg_{messageOrId}" - else: - self._P._mMessage = str(messageOrId) - return self - - def setIcon(self, iconOrId: Any) -> "AlertDialog.Builder": - if isinstance(iconOrId, int): - pass - else: - self._P._mIcon = iconOrId - return self - - def setIconAttribute(self, attrId: int) -> "AlertDialog.Builder": - self._P._mIconAttrId = attrId - return self - - def setPositiveButton( - self, textOrId: Any, listener: Optional[DialogInterface.OnClickListener] = None - ) -> "AlertDialog.Builder": - if isinstance(textOrId, int): - self._P._mPositiveButtonText = f"positive_{textOrId}" - else: - self._P._mPositiveButtonText = str(textOrId) - self._P._mPositiveButtonListener = listener - return self - - def setNegativeButton( - self, textOrId: Any, listener: Optional[DialogInterface.OnClickListener] = None - ) -> "AlertDialog.Builder": - if isinstance(textOrId, int): - self._P._mNegativeButtonText = f"negative_{textOrId}" - else: - self._P._mNegativeButtonText = str(textOrId) - self._P._mNegativeButtonListener = listener - return self - - def setNeutralButton( - self, textOrId: Any, listener: Optional[DialogInterface.OnClickListener] = None - ) -> "AlertDialog.Builder": - if isinstance(textOrId, int): - self._P._mNeutralButtonText = f"neutral_{textOrId}" - else: - self._P._mNeutralButtonText = str(textOrId) - self._P._mNeutralButtonListener = listener - return self - - def setCancelable(self, cancelable: bool) -> "AlertDialog.Builder": - self._P._mCancelable = cancelable - return self - - def setOnCancelListener(self, listener: DialogInterface.OnCancelListener) -> "AlertDialog.Builder": - self._P._mCancelListener = listener - return self - - def setOnDismissListener(self, listener: DialogInterface.OnDismissListener) -> "AlertDialog.Builder": - self._P._mDismissListener = listener - return self - - def setOnKeyListener(self, listener: DialogInterface.OnKeyListener) -> "AlertDialog.Builder": - self._P._mKeyListener = listener - return self - - def setItems( - self, itemsOrId: Any, listener: Optional[DialogInterface.OnClickListener] = None - ) -> "AlertDialog.Builder": - if isinstance(itemsOrId, int): - self._P._mItems = [f"item_{itemsOrId}"] - else: - self._P._mItems = list(itemsOrId) - self._P._mItemClickListener = listener - return self - - def setAdapter( - self, adapter: Any, listener: Optional[DialogInterface.OnClickListener] = None - ) -> "AlertDialog.Builder": - self._P._mAdapter = adapter - self._P._mItemClickListener = listener - return self - - def setSingleChoiceItems( - self, itemsOrId: Any, checkedItem: int, listener: Optional[DialogInterface.OnClickListener] = None - ) -> "AlertDialog.Builder": - if isinstance(itemsOrId, int): - self._P._mItems = [f"item_{itemsOrId}"] - else: - self._P._mItems = list(itemsOrId) - self._P._mCheckedItem = checkedItem - self._P._mItemClickListener = listener - return self - - def setMultiChoiceItems( - self, itemsOrId: Any, checkedItems: Optional[List[bool]], - listener: Optional[DialogInterface.OnMultiChoiceClickListener] = None - ) -> "AlertDialog.Builder": - if isinstance(itemsOrId, int): - self._P._mMultiChoiceItems = [f"item_{itemsOrId}"] - self._P._mCheckedItems = [False] - else: - self._P._mMultiChoiceItems = list(itemsOrId) - self._P._mCheckedItems = checkedItems or [False] * len(itemsOrId) - self._P._mMultiChoiceListener = listener - return self - - def setView(self, viewOrRes: Any) -> "AlertDialog.Builder": - self._P._mView = viewOrRes - return self - - def setInverseBackgroundForced(self, useInverse: bool) -> "AlertDialog.Builder": - self._P._mForceInverseBackground = useInverse - return self - - def setRecycleOnMeasureEnabled(self, enabled: bool) -> "AlertDialog.Builder": - self._P._mRecycleOnMeasure = enabled - return self - - def create(self) -> AlertDialog: - return self._P - - def show(self) -> AlertDialog: - dialog = self.create() - dialog.show() - return dialog - - def toString(self) -> str: - return f"AlertDialog.Builder(title={self._P._mTitle})" - - -# ═══════════════════════════════════════════════════════════════ -# CharacterPickerDialog -# ═══════════════════════════════════════════════════════════════ - -class CharacterPickerDialog(Dialog): - """CharacterPickerDialog — 字符选择对话框""" - - def __init__(self, context: Any, view: Any, listener: Any, options: List[str], allowMultiple: bool): - super().__init__(context) - self._view = view - self._listener = listener - self._options = list(options) - self._allow_multiple = allowMultiple - self._selected_indices: List[int] = [] - self._selected_characters: List[str] = [] - - def onClick(self, which: int) -> None: - if 0 <= which < len(self._options): - char = self._options[which] - if self._allow_multiple: - if which in self._selected_indices: - self._selected_indices.remove(which) - self._selected_characters.remove(char) - else: - self._selected_indices.append(which) - self._selected_characters.append(char) - else: - self._selected_indices = [which] - self._selected_characters = [char] - if self._listener: - self._listener(char) - - def getSelectedCharacters(self) -> List[str]: - return list(self._selected_characters) - - def toString(self) -> str: - return f"CharacterPickerDialog(options={len(self._options)}, multiple={self._allow_multiple})" - - -# ═══════════════════════════════════════════════════════════════ -# DatePickerDialog -# ═══════════════════════════════════════════════════════════════ - -class DatePickerDialog(AlertDialog): - """DatePickerDialog — 日期选择对话框""" - - class OnDateSetListener(Stub): - """DatePickerDialog.OnDateSetListener — 日期设置监听器""" - - def __init__(self): - super().__init__() - - def onDateSet(self, view: Any, year: int, month: int, dayOfMonth: int) -> None: - pass - - def toString(self) -> str: - return "OnDateSetListener" - - def __init__( - self, - context: Any, - listener: Optional["DatePickerDialog.OnDateSetListener"] = None, - year: int = 2024, - month: int = 0, - dayOfMonth: int = 1, - ): - super().__init__(context) - self._mListener = listener - self._mYear = year - self._mMonth = month - self._mDay = dayOfMonth - self._mMinDate: Optional[int] = None - self._mMaxDate: Optional[int] = None - self._mDatePicker: Optional[Any] = None - self._mTitle: Optional[str] = None - self._mInitialDate = datetime(year, month + 1, dayOfMonth) - - def onDateChanged(self, view: Any, year: int, month: int, dayOfMonth: int) -> None: - self._mYear = year - self._mMonth = month - self._mDay = dayOfMonth - - def getDatePicker(self) -> Optional[Any]: - return self._mDatePicker - - def setOnDateSetListener(self, listener: "DatePickerDialog.OnDateSetListener") -> None: - self._mListener = listener - - def updateDate(self, year: int, month: int, dayOfMonth: int) -> None: - self._mYear = year - self._mMonth = month - self._mDay = dayOfMonth - - def getYear(self) -> int: - return self._mYear - - def getMonth(self) -> int: - return self._mMonth - - def getDayOfMonth(self) -> int: - return self._mDay - - def setMinDate(self, minDate: int) -> None: - self._mMinDate = minDate - - def setMaxDate(self, maxDate: int) -> None: - self._mMaxDate = maxDate - - def getMinDate(self) -> Optional[int]: - return self._mMinDate - - def getMaxDate(self) -> Optional[int]: - return self._mMaxDate - - def show(self) -> None: - super().show() - if self._mListener: - self._mListener.onDateSet(None, self._mYear, self._mMonth, self._mDay) - - def toString(self) -> str: - return f"DatePickerDialog({self._mYear}-{self._mMonth + 1:02d}-{self._mDay:02d})" - - -# ═══════════════════════════════════════════════════════════════ -# TimePickerDialog -# ═══════════════════════════════════════════════════════════════ - -class TimePickerDialog(AlertDialog): - """TimePickerDialog — 时间选择对话框""" - - class OnTimeSetListener(Stub): - """TimePickerDialog.OnTimeSetListener — 时间设置监听器""" - - def __init__(self): - super().__init__() - - def onTimeSet(self, view: Any, hourOfDay: int, minute: int) -> None: - pass - - def toString(self) -> str: - return "OnTimeSetListener" - - def __init__( - self, - context: Any, - listener: Optional["TimePickerDialog.OnTimeSetListener"] = None, - hourOfDay: int = 0, - minute: int = 0, - is24HourView: bool = True, - ): - super().__init__(context) - self._mListener = listener - self._mHour = hourOfDay - self._mMinute = minute - self._mIs24HourView = is24HourView - self._mTimePicker: Optional[Any] = None - self._mTitle: Optional[str] = None - - def onTimeChanged(self, view: Any, hourOfDay: int, minute: int) -> None: - self._mHour = hourOfDay - self._mMinute = minute - - def getTimePicker(self) -> Optional[Any]: - return self._mTimePicker - - def setOnTimeSetListener(self, listener: "TimePickerDialog.OnTimeSetListener") -> None: - self._mListener = listener - - def updateTime(self, hourOfDay: int, minute: int) -> None: - self._mHour = hourOfDay - self._mMinute = minute - - def getHour(self) -> int: - return self._mHour - - def getMinute(self) -> int: - return self._mMinute - - def is24HourView(self) -> bool: - return self._mIs24HourView - - def show(self) -> None: - super().show() - if self._mListener: - self._mListener.onTimeSet(None, self._mHour, self._mMinute) - - def toString(self) -> str: - return f"TimePickerDialog({self._mHour:02d}:{self._mMinute:02d})" - - -# ═══════════════════════════════════════════════════════════════ -# ProgressDialog -# ═══════════════════════════════════════════════════════════════ - -class ProgressDialog(AlertDialog): - """ProgressDialog — 进度对话框""" - - STYLE_SPINNER = 0 - STYLE_HORIZONTAL = 1 - - def __init__(self, context: Any): - super().__init__(context) - self._mProgressStyle = ProgressDialog.STYLE_SPINNER - self._mMax = 100 - self._mProgress = 0 - self._mSecondaryProgress = 0 - self._mIndeterminate = False - self._mHasProgress = True - self._mProgressDrawable: Optional[Any] = None - self._mProgressNumber: Optional[str] = None - self._mProgressPercent: Optional[str] = None - self._mProgressNumberFormat = "%1d/%2d" - self._mProgressPercentFormat = "%.0f%%" - self._mCurrentProgressText: Optional[str] = None - self._mProgressTextColor = 0 - self._mProgressBackgroundColor = 0 - self._mMessageViewUpdate: bool = False - - def getMax(self) -> int: - return self._mMax - - def setMax(self, max: int) -> None: - self._mMax = max - - def getProgress(self) -> int: - return self._mProgress - - def setProgress(self, value: int) -> None: - self._mProgress = value - - def getSecondaryProgress(self) -> int: - return self._mSecondaryProgress - - def setSecondaryProgress(self, secondaryProgress: int) -> None: - self._mSecondaryProgress = secondaryProgress - - def getProgressStyle(self) -> int: - return self._mProgressStyle - - def setProgressStyle(self, style: int) -> None: - self._mProgressStyle = style - - def isIndeterminate(self) -> bool: - return self._mIndeterminate - - def setIndeterminate(self, indeterminate: bool) -> None: - self._mIndeterminate = indeterminate - - def incrementProgressBy(self, diff: int) -> None: - self._mProgress = min(self._mProgress + diff, self._mMax) - - def incrementSecondaryProgressBy(self, diff: int) -> None: - self._mSecondaryProgress += diff - - def setProgressDrawable(self, drawable: Any) -> None: - self._mProgressDrawable = drawable - - def setProgressNumberFormat(self, format: str) -> None: - self._mProgressNumberFormat = format - - def setProgressPercentFormat(self, format: str) -> None: - self._mProgressPercentFormat = format - - def onStart(self) -> None: - pass - - def setMessage(self, message: str) -> None: - super().setMessage(message) - - def show( - cls, context: Any, title: str, message: str, indeterminate: bool = True, cancelable: bool = False - ) -> "ProgressDialog": - dialog = ProgressDialog(context) - dialog.setTitle(title) - dialog.setMessage(message) - dialog.setIndeterminate(indeterminate) - dialog.setCancelable(cancelable) - dialog.show() - return dialog - - def toString(self) -> str: - return f"ProgressDialog(style={self._mProgressStyle}, progress={self._mProgress}/{self._mMax})" \ No newline at end of file diff --git a/android_pyapi/android/app_fragment.py b/android_pyapi/android/app_fragment.py deleted file mode 100644 index 861d30e..0000000 --- a/android_pyapi/android/app_fragment.py +++ /dev/null @@ -1,1055 +0,0 @@ -""" -Android Fragment API — Fragment, FragmentManager, FragmentTransaction, etc. -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import uuid -import threading -from typing import Any, Dict, List, Optional, Set, Tuple, Callable -from enum import IntEnum - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException, IllegalArgumentException, IllegalStateException -from .app import Fragment as _BaseFragment -from .app import FragmentManager as _BaseFragmentManager -from .app import FragmentTransaction as _BaseFragmentTransaction - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -# TRANSIT_* 常量 -TRANSIT_NONE = 0 -TRANSIT_FRAGMENT_OPEN = 1 -TRANSIT_FRAGMENT_CLOSE = 2 -TRANSIT_FRAGMENT_FADE = 3 -TRANSIT_UNSET = -1 -TRANSIT_ENTER_MASK = 0x1000 -TRANSIT_EXIT_MASK = 0x2000 - -# TRANSACTION_* 常量 -TRANSACTION_NONE = 0 -TRANSACTION_ADD = 1 -TRANSACTION_REMOVE = 2 -TRANSACTION_REPLACE = 3 -TRANSACTION_SHOW = 4 -TRANSACTION_HIDE = 5 -TRANSACTION_ATTACH = 6 -TRANSACTION_DETACH = 7 -TRANSACTION_SET_PRIMARY_NAV = 8 - -# OP_* 操作常量 (内部) -_OP_NONE = 0 -_OP_ADD = 1 -_OP_REPLACE = 2 -_OP_REMOVE = 3 -_OP_HIDE = 4 -_OP_SHOW = 5 -_OP_DETACH = 6 -_OP_ATTACH = 7 -_OP_SET_PRIMARY_NAV = 8 - -# Fragment 常量 -Fragment_USE_DEFAULT_TRANSITION = 0 -FRAGMENT_RESULT_CANCELED = 0 -FRAGMENT_RESULT_OK = -1 - -# FragmentManager 常量 -POP_BACK_STACK_INCLUSIVE = 1 - - -# ═══════════════════════════════════════════════════════════════ -# Fragment -# ═══════════════════════════════════════════════════════════════ - -class Fragment(_BaseFragment): - """Fragment — 片段基类 (桌面存根,继承 app.Fragment 的所有方法)""" - - def __init__(self): - super().__init__() - self._mCalled = False - self._mTag: Optional[str] = None - self._mFragmentId = 0 - self._mContainerId = 0 - self._mView: Optional[Any] = None - self._mArguments: Optional[Any] = None - self._mHidden = False - self._mDetached = False - self._mRemoving = False - self._mInLayout = False - self._mMenuVisible = True - self._mHasMenu = False - self._mRetainInstance = False - self._mUserVisibleHint = True - self._mState = 0 - self._mSavedState: Optional[Any] = None - self._mActivity: Optional["FragmentActivity"] = None - self._mHost: Optional["FragmentHostCallback"] = None - self._mFragmentManager: Optional["FragmentManager"] = None - self._mChildFragmentManager: Optional["FragmentManager"] = None - self._mParentFragment: Optional["Fragment"] = None - self._mTargetFragment: Optional["Fragment"] = None - self._mTargetRequestCode = 0 - self._mNextAnim = 0 - self._mEnterTransition: Optional[Any] = None - self._mExitTransition: Optional[Any] = None - self._mSharedElementEnterTransition: Optional[Any] = None - self._mSharedElementExitTransition: Optional[Any] = None - self._mAllowEnterTransitionOverlap: Optional[bool] = None - self._mAllowReturnTransitionOverlap: Optional[bool] = None - self._mReenterTransition: Optional[Any] = None - self._mReturnTransition: Optional[Any] = None - self._mSharedElementReturnTransition: Optional[Any] = None - self._mSharedElementReenterTransition: Optional[Any] = None - self._mLifecycleRegistry: Optional[Any] = None - self._mViewModelStore: Optional[Any] = None - self._mMaxState: Optional[str] = None - self._mInitialLifecycleState: Optional[int] = None - self._mPrimaryNavigationFragment = False - self._mIsNewlyAdded = False - self._mIsCreated = False - self._mIsViewCreated = False - self._mIsStateSaved = False - self._mIsStateSavedBefore = False - self._mFragmentTransitionState = 0 - self._mInTransition = False - self._mPostponedHandler: Optional[Any] = None - self._mAnimationInfo: Optional[Any] = None - self._mDebug = False - self._mCalledForTransition = False - self._mHiddenChanged = False - self._mAnimationResId = 0 - self._mContainer: Optional["FragmentContainer"] = None - self._mFactory: Optional[Any] = None - self._mRequestPermissionsResult: List[Tuple[int, List[str], List[int]]] = [] - self._mResultListeners: List[Any] = [] - - def getActivity(self) -> Optional["FragmentActivity"]: - return self._mActivity - - def getHost(self) -> Optional["FragmentHostCallback"]: - return self._mHost - - def getFragmentManager(self) -> Optional["FragmentManager"]: - return self._mFragmentManager - - def getChildFragmentManager(self) -> Optional["FragmentManager"]: - if self._mChildFragmentManager is None: - from .._core import Stub as _Stub - self._mChildFragmentManager = FragmentManager() - return self._mChildFragmentManager - - def getParentFragment(self) -> Optional["Fragment"]: - return self._mParentFragment - - def getTag(self) -> Optional[str]: - return self._mTag - - def getId(self) -> int: - return self._mFragmentId - - def getView(self) -> Optional[Any]: - return self._mView - - def getArguments(self) -> Optional[Any]: - return self._mArguments - - def setArguments(self, args: Any) -> None: - if self._mState > 0: - raise IllegalStateException("Fragment already active") - self._mArguments = args - - def isAdded(self) -> bool: - return self._mActivity is not None and self._mState > 0 - - def isDetached(self) -> bool: - return self._mDetached - - def isRemoving(self) -> bool: - return self._mRemoving - - def isInLayout(self) -> bool: - return self._mInLayout - - def isResumed(self) -> bool: - return self._mState >= 7 - - def isVisible(self) -> bool: - return (not self._mHidden) and self.isAdded() and self._mView is not None - - def isHidden(self) -> bool: - return self._mHidden - - def isMenuVisible(self) -> bool: - return self._mMenuVisible - - def getUserVisibleHint(self) -> bool: - return self._mUserVisibleHint - - def setUserVisibleHint(self, isVisible: bool) -> None: - self._mUserVisibleHint = isVisible - self._mHiddenChanged = not isVisible - - def getRetainInstance(self) -> bool: - return self._mRetainInstance - - def setRetainInstance(self, retain: bool) -> None: - self._mRetainInstance = retain - - def hasOptionsMenu(self) -> bool: - return self._mHasMenu - - def setHasOptionsMenu(self, hasMenu: bool) -> None: - self._mHasMenu = hasMenu - - def setMenuVisibility(self, menuVisible: bool) -> None: - self._mMenuVisible = menuVisible - - def getTargetFragment(self) -> Optional["Fragment"]: - return self._mTargetFragment - - def setTargetFragment(self, fragment: Optional["Fragment"], requestCode: int) -> None: - self._mTargetFragment = fragment - self._mTargetRequestCode = requestCode - - def getTargetRequestCode(self) -> int: - return self._mTargetRequestCode - - def getContext(self) -> Optional[Any]: - return self._mActivity - - def getResources(self) -> Optional[Any]: - if self._mActivity: - return self._mActivity.getResources() - return None - - def requireActivity(self) -> "FragmentActivity": - if self._mActivity is None: - raise IllegalStateException("Fragment not attached to Activity") - return self._mActivity - - def requireFragmentManager(self) -> "FragmentManager": - if self._mFragmentManager is None: - raise IllegalStateException("Fragment not attached to FragmentManager") - return self._mFragmentManager - - def requireContext(self) -> Any: - ctx = self.getContext() - if ctx is None: - raise IllegalStateException("Fragment not attached to a context") - return ctx - - def requireView(self) -> Any: - view = self.getView() - if view is None: - raise IllegalStateException("Fragment does not have a view") - return view - - def requireHost(self) -> "FragmentHostCallback": - if self._mHost is None: - raise IllegalStateException("Fragment not attached to host") - return self._mHost - - def isStateSaved(self) -> bool: - return self._mIsStateSaved - - def getLifecycle(self) -> Optional[Any]: - return self._mLifecycleRegistry - - def getViewModelStore(self) -> Optional[Any]: - return self._mViewModelStore - - def postponeEnterTransition(self) -> None: - self._mPostponedHandler = True - - def startPostponedEnterTransition(self) -> None: - self._mPostponedHandler = None - - def getEnterTransition(self) -> Optional[Any]: - return self._mEnterTransition - - def setEnterTransition(self, transition: Any) -> None: - self._mEnterTransition = transition - - def getExitTransition(self) -> Optional[Any]: - return self._mExitTransition - - def setExitTransition(self, transition: Any) -> None: - self._mExitTransition = transition - - def getSharedElementEnterTransition(self) -> Optional[Any]: - return self._mSharedElementEnterTransition - - def setSharedElementEnterTransition(self, transition: Any) -> None: - self._mSharedElementEnterTransition = transition - - def getSharedElementExitTransition(self) -> Optional[Any]: - return self._mSharedElementExitTransition - - def setSharedElementExitTransition(self, transition: Any) -> None: - self._mSharedElementExitTransition = transition - - def getReenterTransition(self) -> Optional[Any]: - return self._mReenterTransition or self._mEnterTransition - - def setReenterTransition(self, transition: Any) -> None: - self._mReenterTransition = transition - - def getReturnTransition(self) -> Optional[Any]: - return self._mReturnTransition or self._mExitTransition - - def setReturnTransition(self, transition: Any) -> None: - self._mReturnTransition = transition - - def getSharedElementReturnTransition(self) -> Optional[Any]: - return self._mSharedElementReturnTransition or self._mSharedElementEnterTransition - - def setSharedElementReturnTransition(self, transition: Any) -> None: - self._mSharedElementReturnTransition = transition - - def getSharedElementReenterTransition(self) -> Optional[Any]: - return self._mSharedElementReenterTransition or self._mSharedElementExitTransition - - def setSharedElementReenterTransition(self, transition: Any) -> None: - self._mSharedElementReenterTransition = transition - - def getAllowEnterTransitionOverlap(self) -> bool: - return self._mAllowEnterTransitionOverlap is not False - - def setAllowEnterTransitionOverlap(self, allow: bool) -> None: - self._mAllowEnterTransitionOverlap = allow - - def getAllowReturnTransitionOverlap(self) -> bool: - return self._mAllowReturnTransitionOverlap is not False - - def setAllowReturnTransitionOverlap(self, allow: bool) -> None: - self._mAllowReturnTransitionOverlap = allow - - def isInBackStack(self) -> bool: - return self._mFragmentManager is not None - - def getString(self, resId: int, *formatArgs: Any) -> str: - return f"string_{resId}" - - def getText(self, resId: int) -> str: - return f"text_{resId}" - - def getInt(self, resId: int) -> int: - return 0 - - def getBoolean(self, resId: int) -> bool: - return False - - def instantiate(self, context: Any, fname: str, args: Optional[Any] = None) -> "Fragment": - return Fragment() - - # ── 生命周期 ── - def onAttach(self, context: Any) -> None: - self._mCalled = True - - def onCreate(self, savedInstanceState: Optional[Any]) -> None: - self._mCalled = True - self._mIsCreated = True - - def onCreateView(self, inflater: Any, container: Any, savedInstanceState: Optional[Any]) -> Optional[Any]: - self._mCalled = True - return None - - def onViewCreated(self, view: Any, savedInstanceState: Optional[Any]) -> None: - self._mCalled = True - self._mIsViewCreated = True - - def onActivityCreated(self, savedInstanceState: Optional[Any]) -> None: - self._mCalled = True - - def onViewStateRestored(self, savedInstanceState: Optional[Any]) -> None: - self._mCalled = True - - def onStart(self) -> None: - self._mCalled = True - - def onResume(self) -> None: - self._mCalled = True - - def onPause(self) -> None: - self._mCalled = True - - def onStop(self) -> None: - self._mCalled = True - - def onDestroyView(self) -> None: - self._mCalled = True - self._mIsViewCreated = False - - def onDestroy(self) -> None: - self._mCalled = True - self._mIsCreated = False - - def onDetach(self) -> None: - self._mCalled = True - - def onSaveInstanceState(self, outState: Any) -> None: - pass - - def onMultiWindowModeChanged(self, isInMultiWindowMode: bool, newConfig: Any) -> None: - pass - - def onPictureInPictureModeChanged(self, isInPictureInPictureMode: bool, newConfig: Any) -> None: - pass - - def onConfigurationChanged(self, newConfig: Any) -> None: - pass - - def onLowMemory(self) -> None: - pass - - def onHiddenChanged(self, hidden: bool) -> None: - pass - - def onAttachFragment(self, childFragment: "Fragment") -> None: - pass - - def dump(self, prefix: str, fd: Any, writer: Any, args: List[str]) -> None: - pass - - def registerForActivityResult(self, contract: Any, callback: Any) -> Any: - return None - - def unregisterForActivityResult(self, token: Any) -> None: - pass - - def toString(self) -> str: - return f"Fragment({self._mTag or hex(id(self))})" - - -# ═══════════════════════════════════════════════════════════════ -# FragmentManager -# ═══════════════════════════════════════════════════════════════ - -class FragmentManager(_BaseFragmentManager): - """FragmentManager — 片段管理器 (继承 app.FragmentManager 的所有方法)""" - - def __init__(self): - super().__init__() - self._lock = threading.Lock() - self._fragments: Dict[str, Fragment] = {} - self._back_stack: List["FragmentTransaction"] = [] - self._back_stack_indices: Dict[int, str] = {} - self._next_index = 0 - self._primary_nav_fragment: Optional[Fragment] = None - self._fragment_container: Optional["FragmentContainer"] = None - - def beginTransaction(self) -> "FragmentTransaction": - return FragmentTransaction(self) - - def executePendingTransactions(self) -> bool: - return True - - def findFragmentById(self, id: int) -> Optional[Fragment]: - with self._lock: - for frag in self._fragments.values(): - if frag.getId() == id: - return frag - return None - - def findFragmentByTag(self, tag: str) -> Optional[Fragment]: - with self._lock: - return self._fragments.get(tag) - - def popBackStack(self) -> None: - with self._lock: - if self._back_stack: - trans = self._back_stack.pop() - trans._reverse() - - def popBackStackImmediate(self, name: Optional[str] = None, flags: int = 0) -> bool: - self.popBackStack() - return True - - def popBackStack( - self, name: Optional[str], flags: int - ) -> None: - with self._lock: - if name is None: - if self._back_stack: - trans = self._back_stack.pop() - trans._reverse() - return - idx = -1 - for i, t in enumerate(self._back_stack): - if t._name == name: - idx = i - break - if idx >= 0: - inclusive = (flags & POP_BACK_STACK_INCLUSIVE) != 0 - start = idx if inclusive else idx + 1 - to_remove = self._back_stack[start:] - self._back_stack = self._back_stack[:start] - for t in reversed(to_remove): - t._reverse() - - def getBackStackEntryCount(self) -> int: - with self._lock: - return len(self._back_stack) - - def getBackStackEntryAt(self, index: int) -> Optional[Any]: - with self._lock: - if 0 <= index < len(self._back_stack): - return self._back_stack[index] - return None - - def saveFragmentInstanceState(self, fragment: Fragment) -> Optional[Any]: - return None - - def isDestroyed(self) -> bool: - return False - - def isStateSaved(self) -> bool: - return False - - def getFragments(self) -> List[Fragment]: - with self._lock: - return list(self._fragments.values()) - - def getFragment(self, bundle: Any, key: str) -> Optional[Fragment]: - return None - - def putFragment(self, bundle: Any, key: str, fragment: Fragment) -> None: - pass - - def getPrimaryNavigationFragment(self) -> Optional[Fragment]: - return self._primary_nav_fragment - - def registerFragmentLifecycleCallbacks(self, cb: Any, recursive: bool) -> None: - pass - - def unregisterFragmentLifecycleCallbacks(self, cb: Any) -> None: - pass - - def isPrimaryNavigation(self, fragment: Fragment) -> bool: - return fragment == self._primary_nav_fragment - - def toString(self) -> str: - with self._lock: - return f"FragmentManager(fragments={len(self._fragments)}, backstack={len(self._back_stack)})" - - -# ═══════════════════════════════════════════════════════════════ -# FragmentTransaction -# ═══════════════════════════════════════════════════════════════ - -class FragmentTransaction(_BaseFragmentTransaction): - """FragmentTransaction — 片段事务 (继承 app.FragmentTransaction 的所有方法)""" - - TRANSIT_ENTER_MASK = TRANSIT_ENTER_MASK - TRANSIT_EXIT_MASK = TRANSIT_EXIT_MASK - TRANSIT_UNSET = TRANSIT_UNSET - TRANSIT_NONE = TRANSIT_NONE - TRANSIT_FRAGMENT_OPEN = TRANSIT_FRAGMENT_OPEN - TRANSIT_FRAGMENT_CLOSE = TRANSIT_FRAGMENT_CLOSE - TRANSIT_FRAGMENT_FADE = TRANSIT_FRAGMENT_FADE - - def __init__(self, manager: "FragmentManager"): - super().__init__() - self._manager = manager - self._ops: List[Dict[str, Any]] = [] - self._name: Optional[str] = None - self._transition = TRANSIT_NONE - self._transition_style = 0 - self._transition_shared = False - self._allow_add_to_back_stack = True - self._add_to_back_stack = False - self._is_reordering_allowed = False - self._bread_crumb_title_res = 0 - self._bread_crumb_title: Optional[str] = None - self._mCommitListener: Optional[Any] = None - - def add(self, containerViewId: Any = 0, fragment: Optional[Any] = None, tag: Optional[str] = None) -> "FragmentTransaction": - if fragment is None and not isinstance(containerViewId, int): - fragment = containerViewId - containerViewId = 0 - self._ops.append({ - "op": _OP_ADD, - "fragment": fragment, - "containerId": int(containerViewId) if isinstance(containerViewId, int) else 0, - "tag": tag, - }) - if tag: - fragment._mTag = tag - return self - - def replace(self, containerViewId: int, fragment: Fragment, tag: Optional[str] = None) -> "FragmentTransaction": - self._ops.append({ - "op": _OP_REPLACE, - "fragment": fragment, - "containerId": containerViewId, - "tag": tag, - }) - if tag: - fragment._mTag = tag - return self - - def remove(self, fragment: Fragment) -> "FragmentTransaction": - self._ops.append({"op": _OP_REMOVE, "fragment": fragment}) - return self - - def hide(self, fragment: Fragment) -> "FragmentTransaction": - self._ops.append({"op": _OP_HIDE, "fragment": fragment}) - return self - - def show(self, fragment: Fragment) -> "FragmentTransaction": - self._ops.append({"op": _OP_SHOW, "fragment": fragment}) - return self - - def detach(self, fragment: Fragment) -> "FragmentTransaction": - self._ops.append({"op": _OP_DETACH, "fragment": fragment}) - return self - - def attach(self, fragment: Fragment) -> "FragmentTransaction": - self._ops.append({"op": _OP_ATTACH, "fragment": fragment}) - return self - - def setPrimaryNavigationFragment(self, fragment: Fragment) -> "FragmentTransaction": - self._ops.append({"op": _OP_SET_PRIMARY_NAV, "fragment": fragment}) - return self - - def addToBackStack(self, name: Optional[str] = None) -> "FragmentTransaction": - self._add_to_back_stack = True - self._name = name - return self - - def setTransition(self, transit: int) -> "FragmentTransaction": - self._transition = transit - return self - - def setTransitionStyle(self, styleRes: int) -> "FragmentTransaction": - self._transition_style = styleRes - return self - - def setAllowOptimization(self, allow: bool) -> "FragmentTransaction": - self._is_reordering_allowed = allow - return self - - def setReorderingAllowed(self, allowed: bool) -> "FragmentTransaction": - self._is_reordering_allowed = allowed - return self - - def setBreadCrumbTitle(self, titleOrRes: Any) -> "FragmentTransaction": - if isinstance(titleOrRes, int): - self._bread_crumb_title_res = titleOrRes - else: - self._bread_crumb_title = str(titleOrRes) - return self - - def runOnCommit(self, runnable: Callable[[], None]) -> "FragmentTransaction": - self._mCommitListener = runnable - return self - - def commit(self) -> int: - with self._manager._lock: - entry_id = self._manager._next_index - self._manager._next_index += 1 - self._commit_ops() - if self._add_to_back_stack: - self._manager._back_stack.append(self) - return entry_id - - def commitNow(self) -> None: - self.commit() - - def commitAllowingStateLoss(self) -> int: - return self.commit() - - def _commit_ops(self) -> None: - for op in self._ops: - frag = op["fragment"] - tag = op.get("tag") or frag._mTag - if op["op"] == _OP_ADD or op["op"] == _OP_REPLACE: - if tag: - self._manager._fragments[tag] = frag - frag._mFragmentManager = self._manager - elif op["op"] == _OP_REMOVE: - if tag and tag in self._manager._fragments: - del self._manager._fragments[tag] - elif op["op"] == _OP_SET_PRIMARY_NAV: - self._manager._primary_nav_fragment = frag - - def _reverse(self) -> None: - for op in reversed(self._ops): - frag = op["fragment"] - tag = frag._mTag - if op["op"] == _OP_ADD: - if tag and tag in self._manager._fragments: - del self._manager._fragments[tag] - elif op["op"] == _OP_REMOVE: - if tag: - self._manager._fragments[tag] = frag - - def toString(self) -> str: - return f"FragmentTransaction(ops={len(self._ops)}, backstack={self._add_to_back_stack})" - - -# ═══════════════════════════════════════════════════════════════ -# FragmentActivity -# ═══════════════════════════════════════════════════════════════ - -class FragmentActivity(Stub): - """FragmentActivity — 支持 Fragment 的 Activity""" - - def __init__(self): - super().__init__() - self._mFragments: FragmentController = FragmentController(FragmentManager()) - self._mFragmentManager: FragmentManager = FragmentManager() - self._mCreated = False - self._mResumed = False - self._mStarted = False - - def getSupportFragmentManager(self) -> FragmentManager: - return self._mFragmentManager - - def getSupportFragmentManager(self) -> FragmentManager: - return self._mFragments.getFragmentManager() - - def onAttachFragment(self, fragment: Fragment) -> None: - pass - - def supportInvalidateOptionsMenu(self) -> None: - pass - - def dump(self, prefix: str, fd: Any, writer: Any, args: List[str]) -> None: - pass - - def getLifecycle(self) -> Optional[Any]: - return None - - def getViewModelStore(self) -> Optional[Any]: - return None - - def toString(self) -> str: - return f"FragmentActivity@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# FragmentController -# ═══════════════════════════════════════════════════════════════ - -class FragmentController(Stub): - """FragmentController — 片段控制器""" - - def __init__(self, manager: FragmentManager): - super().__init__() - self._manager = manager - - def getFragmentManager(self) -> FragmentManager: - return self._manager - - def execPendingActions(self) -> None: - pass - - def doLoaderStart(self) -> None: - pass - - def doLoaderStop(self, retain: bool) -> None: - pass - - def doLoaderRetain(self) -> None: - pass - - def doLoaderDestroy(self) -> None: - pass - - def noteStateNotSaved(self) -> None: - pass - - def saveAllState(self) -> Optional[Any]: - return None - - def restoreAllState(self, state: Any) -> None: - pass - - def createLoader(self, id: int, bundle: Any, callback: Any) -> Any: - return None - - def dispatchCreate(self) -> None: - pass - - def dispatchActivityCreated(self) -> None: - pass - - def dispatchStart(self) -> None: - pass - - def dispatchResume(self) -> None: - pass - - def dispatchPause(self) -> None: - pass - - def dispatchStop(self) -> None: - pass - - def dispatchDestroy(self) -> None: - pass - - def dispatchMultiWindowModeChanged(self, isInMultiWindowMode: bool, newConfig: Any) -> None: - pass - - def dispatchPictureInPictureModeChanged(self, isInPictureInPictureMode: bool, newConfig: Any) -> None: - pass - - def dispatchConfigurationChanged(self, newConfig: Any) -> None: - pass - - def dispatchLowMemory(self) -> None: - pass - - def toString(self) -> str: - return f"FragmentController(manager={self._manager})" - - -# ═══════════════════════════════════════════════════════════════ -# FragmentHostCallback -# ═══════════════════════════════════════════════════════════════ - -class FragmentHostCallback(Stub): - """FragmentHostCallback — 片段宿主的回调基类""" - - def __init__(self, context: Any = None, fragmentManager: Any = None, loaderManager: Any = None): - super().__init__() - self._context = context - self._fragmentManager = fragmentManager - self._loaderManager = loaderManager - - def onStartLoadingFragment(self, fragment: Fragment) -> None: - pass - - def onDump(self, prefix: str, fd: Any, writer: Any, args: List[str]) -> None: - pass - - def onShouldShowRequestPermissionRationale(self, permission: str) -> bool: - return True - - def onGetLayoutInflater(self) -> Any: - return None - - def onFindViewById(self, id: int) -> Optional[Any]: - return None - - def onHasView(self) -> bool: - return False - - def onGetWindowAnimations(self) -> int: - return 0 - - def onHasWindowAnimations(self) -> bool: - return False - - def onGetActivityResultRegistry(self) -> Any: - return None - - def onAttachFragment(self, fragment: Fragment) -> None: - pass - - def onGetHostIdentifier(self) -> Any: - return None - - def onValidateRequestPermissionsRequestCode(self, requestCode: int) -> None: - pass - - def toString(self) -> str: - return f"FragmentHostCallback@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# FragmentContainer -# ═══════════════════════════════════════════════════════════════ - -class FragmentContainer(Stub): - """FragmentContainer — 片段容器接口""" - - def __init__(self): - super().__init__() - - def instantiate(self, context: Any, className: str, arguments: Optional[Any] = None) -> Fragment: - return Fragment() - - def onFindViewById(self, id: int) -> Optional[Any]: - return None - - def onHasView(self) -> bool: - return False - - def toString(self) -> str: - return f"FragmentContainer@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# DialogFragment -# ═══════════════════════════════════════════════════════════════ - -class DialogFragment(Fragment): - """DialogFragment — 对话框片段""" - - STYLE_NORMAL = 0 - STYLE_NO_TITLE = 1 - STYLE_NO_FRAME = 2 - STYLE_NO_INPUT = 3 - - def __init__(self): - super().__init__() - self._dialog: Optional[Any] = None - self._style = DialogFragment.STYLE_NORMAL - self._theme = 0 - self._cancelable = True - self._showing = False - self._dismissed = False - self._dismissed_by_nav = False - self._shows_dialog = True - self._mBackStackId = -1 - self._mDialogCanceled = False - - def setStyle(self, style: int, theme: int = 0) -> None: - self._style = style - self._theme = theme - - def show(self, manager: FragmentManager, tag: str) -> None: - self._showing = True - manager.beginTransaction().add(self, tag).commit() - - def showNow(self, manager: FragmentManager, tag: str) -> None: - self.show(manager, tag) - - def dismiss(self) -> None: - self._dismissed = True - fm = self.getFragmentManager() - if fm: - fm.beginTransaction().remove(self).commit() - - def dismissAllowingStateLoss(self) -> None: - self.dismiss() - - def getDialog(self) -> Optional[Any]: - return self._dialog - - def getTheme(self) -> int: - return self._theme - - def setCancelable(self, cancelable: bool) -> None: - self._cancelable = cancelable - - def isCancelable(self) -> bool: - return self._cancelable - - def setShowsDialog(self, showsDialog: bool) -> None: - self._shows_dialog = showsDialog - - def getShowsDialog(self) -> bool: - return self._shows_dialog - - def onDismiss(self, dialog: Any) -> None: - pass - - def onCancel(self, dialog: Any) -> None: - pass - - def onCreateDialog(self, savedInstanceState: Optional[Any]) -> Any: - return None - - def toString(self) -> str: - return f"DialogFragment(style={self._style}, showing={self._showing})" - - -# ═══════════════════════════════════════════════════════════════ -# ListFragment -# ═══════════════════════════════════════════════════════════════ - -class ListFragment(Fragment): - """ListFragment — 列表片段""" - - def __init__(self): - super().__init__() - self._adapter: Optional[Any] = None - self._list_view: Optional[Any] = None - self._selected_position = -1 - self._empty_text: Optional[str] = None - self._empty_view: Optional[Any] = None - self._progress_container: Optional[Any] = None - self._standard_empty_view = False - self._list_shown = False - self._list_shown_no_animation = True - - def getListAdapter(self) -> Optional[Any]: - return self._adapter - - def setListAdapter(self, adapter: Any) -> None: - self._adapter = adapter - - def getListView(self) -> Optional[Any]: - return self._list_view - - def getSelectedItemPosition(self) -> int: - return self._selected_position - - def getSelectedItemId(self) -> int: - return 0 - - def setSelection(self, position: int) -> None: - self._selected_position = position - - def setEmptyText(self, text: str) -> None: - self._empty_text = text - - def getEmptyText(self) -> Optional[str]: - return self._empty_text - - def listViewScrollToTop(self) -> None: - pass - - def isListShown(self) -> bool: - return self._list_shown - - def setListShown(self, shown: bool) -> None: - self._list_shown = shown - self._list_shown_no_animation = True - - def setListShownNoAnimation(self, shown: bool) -> None: - self._list_shown = shown - self._list_shown_no_animation = True - - def onListItemClick(self, lv: Any, v: Any, position: int, id: int) -> None: - pass - - def onListAdapterChanged(self, adapter: Any) -> None: - pass - - def toString(self) -> str: - return f"ListFragment(list_shown={self._list_shown})" - - -# ═══════════════════════════════════════════════════════════════ -# FragmentManagerNonConfig -# ═══════════════════════════════════════════════════════════════ - -class FragmentManagerNonConfig(Stub): - """FragmentManagerNonConfig — 非配置保留片段 (已弃用)""" - - def __init__(self): - super().__init__() - self._fragments: List[Fragment] = [] - self._child_non_configs: List["FragmentManagerNonConfig"] = [] - self._view_model_stores: List[Any] = [] - - def getFragments(self) -> List[Fragment]: - return list(self._fragments) - - def getChildNonConfigs(self) -> List["FragmentManagerNonConfig"]: - return list(self._child_non_configs) - - def getViewModelStores(self) -> List[Any]: - return list(self._view_model_stores) - - def toString(self) -> str: - return f"FragmentManagerNonConfig(fragments={len(self._fragments)})" \ No newline at end of file diff --git a/android_pyapi/android/app_job.py b/android_pyapi/android/app_job.py deleted file mode 100644 index 6422700..0000000 --- a/android_pyapi/android/app_job.py +++ /dev/null @@ -1,444 +0,0 @@ -""" -Android Job Scheduler API — JobScheduler, JobInfo, JobService, JobParameters, JobWorkItem -方法签名完全对齐 Android SDK -""" - -import heapq -import threading -import time -from datetime import datetime, timedelta -from typing import Any, Dict, List, Optional, Set, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "JobScheduler", - "JobInfo", - "JobService", - "JobParameters", - "JobWorkItem", -] - - -# ═══════════════════════════════════════════════════════════════ -# JobScheduler -# ═══════════════════════════════════════════════════════════════ - -class JobScheduler(Stub): - """JobScheduler — 作业调度器 (桌面线程模拟)""" - - def __init__(self): - super().__init__() - self._lock = threading.Lock() - self._jobs: Dict[int, "JobInfo"] = {} - self._next_id = 1 - self._worker_thread: Optional[threading.Thread] = None - self._running = False - self._start_worker() - - def _start_worker(self): - """启动后台工作线程""" - if self._worker_thread is None or not self._worker_thread.is_alive(): - self._running = True - self._worker_thread = threading.Thread(target=self._run_loop, daemon=True) - self._worker_thread.start() - - def _run_loop(self): - """运行作业调度循环""" - while self._running: - with self._lock: - now = time.time() - for job_id, info in list(self._jobs.items()): - if info._deadline and now >= info._deadline: - self._jobs.pop(job_id, None) - - time.sleep(1.0) - - def schedule(self, job: "JobInfo") -> int: - """调度一个作业""" - with self._lock: - job_id = self._next_id - self._next_id += 1 - job._id = job_id - if job._periodic: - job._deadline = time.time() + job._period_ms / 1000.0 - elif job._deadline_ms: - job._deadline = time.time() + job._deadline_ms / 1000.0 - else: - job._deadline = None - self._jobs[job_id] = job - return job_id - - def enqueue(self, job: "JobInfo", workItem: "JobWorkItem") -> int: - """将作业加入队列""" - return self.schedule(job) - - def cancel(self, jobId: int): - """取消指定作业""" - with self._lock: - self._jobs.pop(jobId, None) - - def cancelAll(self): - """取消所有作业""" - with self._lock: - self._jobs.clear() - - def getAllPendingJobs(self) -> List["JobInfo"]: - """获取所有待处理作业""" - with self._lock: - return list(self._jobs.values()) - - def getPendingJob(self, jobId: int) -> Optional["JobInfo"]: - """获取指定待处理作业""" - with self._lock: - return self._jobs.get(jobId) - - def getTransientNetwork(self) -> Optional[Any]: - """获取瞬时网络""" - return None - - def getStartedJobs(self) -> List["JobInfo"]: - """获取已开始的作业""" - return [] - - def toString(self) -> str: - with self._lock: - return f"JobScheduler(pending={len(self._jobs)})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# JobInfo -# ═══════════════════════════════════════════════════════════════ - -class JobInfo(Stub): - """JobInfo — 作业信息""" - - # --- NetworkType 常量 --- - NETWORK_TYPE_NONE = 0 - NETWORK_TYPE_ANY = 1 - NETWORK_TYPE_UNMETERED = 2 - NETWORK_TYPE_NOT_ROAMING = 3 - NETWORK_TYPE_CELLULAR = 4 - - # --- Backoff 策略 --- - BACKOFF_POLICY_LINEAR = 0 - BACKOFF_POLICY_EXPONENTIAL = 1 - - def __init__(self): - super().__init__() - self._id = 0 - self._service = None - self._required_network_type = self.NETWORK_TYPE_NONE - self._requires_charging = False - self._requires_device_idle = False - self._deadline_ms = 0 - self._min_latency_ms = 0 - self._period_ms = 0 - self._periodic = False - self._backoff_policy = self.BACKOFF_POLICY_EXPONENTIAL - self._backoff_delay_ms = 30000 - self._extras = None - self._transient_network = False - self._estimated_network_bytes = -1 - self._prefetch_network_bytes = -1 - self._requires_storage_not_low = False - - def getId(self) -> int: - return self._id - - def getService(self) -> Optional[Any]: - return self._service - - def getRequiredNetworkType(self) -> int: - return self._required_network_type - - def isRequiresCharging(self) -> bool: - return self._requires_charging - - def isRequiresDeviceIdle(self) -> bool: - return self._requires_device_idle - - def isRequiresStorageNotLow(self) -> bool: - return self._requires_storage_not_low - - def getMinLatencyMillis(self) -> int: - return self._min_latency_ms - - def getMaxExecutionDelayMillis(self) -> int: - return self._deadline_ms - - def isPeriodic(self) -> bool: - return self._periodic - - def getBackoffPolicy(self) -> int: - return self._backoff_policy - - def getInitialBackoffMillis(self) -> int: - return self._backoff_delay_ms - - def getEstimatedNetworkBytes(self) -> int: - return self._estimated_network_bytes - - def getPrefetchNetworkBytes(self) -> int: - return self._prefetch_network_bytes - - def getTransientNetwork(self) -> Optional[Any]: - return None - - def isPersisted(self) -> bool: - return False - - def getExtras(self) -> Optional[Any]: - return self._extras - - # ═══════════════════════════════════════════════════════════ - # Builder - # ═══════════════════════════════════════════════════════════ - - class Builder(Stub): - """JobInfo.Builder — 作业构建器""" - - def __init__(self, jobId: int, jobService: Any): - super().__init__() - self._info = JobInfo() - self._info._id = jobId - self._info._service = jobService - - def setRequiredNetworkType(self, networkType: int) -> "JobInfo.Builder": - self._info._required_network_type = networkType - return self - - def setRequiresCharging(self, requiresCharging: bool) -> "JobInfo.Builder": - self._info._requires_charging = requiresCharging - return self - - def setRequiresDeviceIdle(self, requiresDeviceIdle: bool) -> "JobInfo.Builder": - self._info._requires_device_idle = requiresDeviceIdle - return self - - def setRequiresStorageNotLow(self, requiresStorageNotLow: bool) -> "JobInfo.Builder": - self._info._requires_storage_not_low = requiresStorageNotLow - return self - - def setOverrideDeadline(self, deadlineMillis: int) -> "JobInfo.Builder": - self._info._deadline_ms = deadlineMillis - return self - - def setMinimumLatency(self, minLatencyMillis: int) -> "JobInfo.Builder": - self._info._min_latency_ms = minLatencyMillis - return self - - def setBackoffCriteria( - self, initialBackoffMillis: int, backoffPolicy: int - ) -> "JobInfo.Builder": - self._info._backoff_delay_ms = initialBackoffMillis - self._info._backoff_policy = backoffPolicy - return self - - def setPeriodic(self, intervalMillis: int) -> "JobInfo.Builder": - self._info._periodic = True - self._info._period_ms = intervalMillis - return self - - def setTransientNetwork(self, transient: bool) -> "JobInfo.Builder": - self._info._transient_network = transient - return self - - def setEstimatedNetworkBytes(self, estimatedBytes: int) -> "JobInfo.Builder": - self._info._estimated_network_bytes = estimatedBytes - return self - - def setPrefetchNetworkBytes(self, prefetchBytes: int) -> "JobInfo.Builder": - self._info._prefetch_network_bytes = prefetchBytes - return self - - def setExtras(self, extras: Any) -> "JobInfo.Builder": - self._info._extras = extras - return self - - def build(self) -> "JobInfo": - return self._info - - def toString(self) -> str: - return f"Builder(jobId={self._info._id})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def toString(self) -> str: - return f"JobInfo(id={self._id}, network={self._required_network_type})" - - def equals(self, other) -> bool: - if not isinstance(other, JobInfo): - return False - return self._id == other._id - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# JobService -# ═══════════════════════════════════════════════════════════════ - -class JobService(Stub): - """JobService — 作业服务基类""" - - def __init__(self): - super().__init__() - - def onStartJob(self, params: "JobParameters") -> bool: - """作业开始时调用。返回 True 表示需要后台线程处理。""" - return False - - def onStopJob(self, params: "JobParameters") -> bool: - """作业停止时调用。返回 True 表示应重试。""" - return False - - def jobFinished(self, params: "JobParameters", needsReschedule: bool): - """通知系统作业已完成""" - pass - - def toString(self) -> str: - return f"JobService@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# JobParameters -# ═══════════════════════════════════════════════════════════════ - -class JobParameters(Stub): - """JobParameters — 作业参数""" - - # --- StopReason 常量 --- - STOP_REASON_UNDEFINED = 0 - STOP_REASON_DATA_CLEARED = 1 - STOP_REASON_APP_BACKGROUND = 2 - STOP_REASON_APP_STANDBY = 3 - STOP_REASON_DEVICE_IDLE = 4 - STOP_REASON_PREEXISTING = 5 - STOP_REASON_ESTIMATED_NETWORK_BYTES_EXCEEDED = 6 - STOP_REASON_QUOTA = 7 - STOP_REASON_TAPPED_CANCEL = 8 - STOP_REASON_USER = 9 - STOP_REASON_CONSTRAINT_BATTERY_NOT_OK = 10 - STOP_REASON_CONSTRAINT_NETWORK_NOT_OK = 11 - STOP_REASON_CONSTRAINT_CONNECTIVITY_NOT_OK = 12 - STOP_REASON_CONSTRAINT_STORAGE_NOT_OK = 13 - STOP_REASON_CONSTRAINT_CHARGING_NOT_OK = 14 - STOP_REASON_CONSTRAINT_IDLE_NOT_OK = 15 - STOP_REASON_CANCELED = 16 - STOP_REASON_CANCELLED_BY_APP = 17 - STOP_REASON_DEVICE_STATE = 18 - STOP_REASON_SYSTEM_PROCESSING = 19 - STOP_REASON_WORK_POLICY = 20 - STOP_REASON_SYSTEM_EXECUTION = 21 - STOP_REASON_RESTRICTED_BACKGROUND = 22 - STOP_REASON_TIMEOUT = 23 - - def __init__(self, jobId: int = 0): - super().__init__() - self._job_id = jobId - self._extras = None - self._network = None - self._transient_network = None - self._stop_reason = 0 - self._override_deadline_expired = False - self._requires_charging = False - self._requires_device_idle = False - self._requires_storage_not_low = False - self._required_network_type = JobInfo.NETWORK_TYPE_NONE - - def getJobId(self) -> int: - return self._job_id - - def getExtras(self) -> Optional[Any]: - return self._extras - - def getNetwork(self) -> Optional[Any]: - return self._network - - def getTransientNetwork(self) -> Optional[Any]: - return self._transient_network - - def getStopReason(self) -> int: - return self._stop_reason - - def isOverrideDeadlineExpired(self) -> bool: - return self._override_deadline_expired - - def isRequiresCharging(self) -> bool: - return self._requires_charging - - def isRequiresDeviceIdle(self) -> bool: - return self._requires_device_idle - - def isRequiresStorageNotLow(self) -> bool: - return self._requires_storage_not_low - - def getRequiredNetworkType(self) -> int: - return self._required_network_type - - def toString(self) -> str: - return f"JobParameters(jobId={self._job_id})" - - def equals(self, other) -> bool: - if not isinstance(other, JobParameters): - return False - return self._job_id == other._job_id - - def hashCode(self) -> int: - return self._job_id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# JobWorkItem -# ═══════════════════════════════════════════════════════════════ - -class JobWorkItem(Stub): - """JobWorkItem — 作业工作项""" - - def __init__(self, intent: Any = None): - super().__init__() - self._intent = intent - self._delivery_count = 0 - self._latest_delivery_intent = None - - def getIntent(self) -> Optional[Any]: - return self._intent - - def getDeliveryCount(self) -> int: - return self._delivery_count - - def getLatestDeliveryIntent(self) -> Optional[Any]: - return self._latest_delivery_intent - - def toString(self) -> str: - return f"JobWorkItem(deliveryCount={self._delivery_count})" - - def equals(self, other) -> bool: - if not isinstance(other, JobWorkItem): - return False - return self._intent == other._intent - - def hashCode(self) -> int: - return hash(str(self._intent)) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/app_notification.py b/android_pyapi/android/app_notification.py deleted file mode 100644 index 7024aa2..0000000 --- a/android_pyapi/android/app_notification.py +++ /dev/null @@ -1,1307 +0,0 @@ -""" -Android Notification API — NotificationChannel, NotificationManager, NotificationCompat, etc. -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import json -import os -import time -import threading -from datetime import datetime -from enum import IntEnum -from typing import Any, Dict, List, Optional, Tuple, Callable, Union - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# 常量定义 -# ═══════════════════════════════════════════════════════════════ - -# IMPORTANCE 常量 -IMPORTANCE_UNSPECIFIED = -1000 -IMPORTANCE_NONE = 0 -IMPORTANCE_MIN = 1 -IMPORTANCE_LOW = 2 -IMPORTANCE_DEFAULT = 3 -IMPORTANCE_HIGH = 4 -IMPORTANCE_MAX = 5 - -# VISIBILITY 常量 -VISIBILITY_PRIVATE = 0 -VISIBILITY_PUBLIC = 1 -VISIBILITY_SECRET = -1 - -# PRIORITY 常量 -PRIORITY_MIN = -2 -PRIORITY_LOW = -1 -PRIORITY_DEFAULT = 0 -PRIORITY_HIGH = 1 -PRIORITY_MAX = 2 - -# FLAG 常量 -FLAG_SHOW_LIGHTS = 0x00000001 -FLAG_ONGOING_EVENT = 0x00000002 -FLAG_INSISTENT = 0x00000004 -FLAG_ONLY_ALERT_ONCE = 0x00000008 -FLAG_AUTO_CANCEL = 0x00000010 -FLAG_NO_CLEAR = 0x00000020 -FLAG_FOREGROUND_SERVICE = 0x00000040 -FLAG_HIGH_PRIORITY = 0x00000080 -FLAG_BUBBLE = 0x00000100 -FLAG_GROUP_SUMMARY = 0x00000200 -FLAG_LOCAL_ONLY = 0x00000400 -FLAG_CAN_COLORIZE = 0x00000800 -FLAG_ALERT_ONLY_ONCE = 0x00001000 - -# DEFAULT 常量 -DEFAULT_ALL = -1 -DEFAULT_SOUND = 1 -DEFAULT_VIBRATE = 2 -DEFAULT_LIGHTS = 4 - -# STREAM 常量 (deprecated) -STREAM_DEFAULT = -1 -STREAM_VOICE_CALL = 0 -STREAM_SYSTEM = 1 -STREAM_RING = 2 -STREAM_MUSIC = 3 -STREAM_ALARM = 4 -STREAM_NOTIFICATION = 5 -STREAM_DTMF = 8 - -# BADGE 常量 -BADGE_ICON_NONE = 0 -BADGE_ICON_SMALL = 1 -BADGE_ICON_LARGE = 2 - -# INTERRUPTION_FILTER 常量 -INTERRUPTION_FILTER_UNKNOWN = 0 -INTERRUPTION_FILTER_ALL = 1 -INTERRUPTION_FILTER_PRIORITY = 2 -INTERRUPTION_FILTER_NONE = 3 -INTERRUPTION_FILTER_ALARMS = 4 - -# CATEGORY 常量 -CATEGORY_CALL = "call" -CATEGORY_ALARM = "alarm" -CATEGORY_REMINDER = "reminder" -CATEGORY_EVENT = "event" -CATEGORY_MESSAGE = "msg" -CATEGORY_EMAIL = "email" -CATEGORY_SOCIAL = "social" -CATEGORY_ERROR = "err" -CATEGORY_PROGRESS = "progress" -CATEGORY_PROMO = "promo" -CATEGORY_RECOMMENDATION = "recommendation" -CATEGORY_STATUS = "status" -CATEGORY_SYSTEM = "sys" -CATEGORY_SERVICE = "service" -CATEGORY_TRANSPORT = "transport" -CATEGORY_WORKOUT = "workout" -CATEGORY_LOCATION_SHARING = "location_sharing" -CATEGORY_STOPWATCH = "stopwatch" -CATEGORY_ALERT = "alert" -CATEGORY_MISSED_CALL = "missed_call" - -# GROUP_ALERT 常量 -GROUP_ALERT_ALL = 0 -GROUP_ALERT_SUMMARY = 1 -GROUP_ALERT_CHILDREN = 2 - -# SEMANTIC_ACTION 常量 -SEMANTIC_ACTION_NONE = 0 -SEMANTIC_ACTION_REPLY = 1 -SEMANTIC_ACTION_MARK_AS_READ = 2 -SEMANTIC_ACTION_MARK_AS_IMPORTANT = 3 -SEMANTIC_ACTION_DELETE = 4 -SEMANTIC_ACTION_ARCHIVE = 5 -SEMANTIC_ACTION_MUTE = 6 -SEMANTIC_ACTION_UNMUTE = 7 -SEMANTIC_ACTION_CALL = 8 -SEMANTIC_ACTION_THUMBS_UP = 9 -SEMANTIC_ACTION_THUMBS_DOWN = 10 - - -# ═══════════════════════════════════════════════════════════════ -# NotificationChannel -# ═══════════════════════════════════════════════════════════════ - -class NotificationChannel(Stub): - """NotificationChannel — 通知渠道""" - - def __init__( - self, - channelId: str, - channelName: str, - importance: int = IMPORTANCE_DEFAULT, - ): - super().__init__() - self._id = channelId - self._name = channelName - self._description: Optional[str] = None - self._importance = importance - self._bypass_dnd = False - self._lockscreen_visibility = VISIBILITY_PRIVATE - self._show_badge = True - self._sound: Optional[str] = None - self._vibration_enabled = False - self._vibration_pattern: Optional[List[int]] = None - self._light_color = 0 - self._light_enabled = False - self._group: Optional[str] = None - self._can_bubble = False - self._allow_bubbles = False - self._is_vibration = False - self._original_importance = importance - self._fbr_enabled = False - self._conversation_id: Optional[str] = None - self._is_conversation = False - self._demoted = False - self._deleted = False - self._has_user_set = False - self._audio_attributes = None - self._can_show_badge = True - self._is_important_conversation = False - self._important_conversation_quota = 0 - - def getId(self) -> str: - return self._id - - def getName(self) -> str: - return self._name - - def setName(self, name: str) -> None: - self._name = name - - def getDescription(self) -> Optional[str]: - return self._description - - def setDescription(self, description: str) -> None: - self._description = description - - def getImportance(self) -> int: - return self._importance - - def setImportance(self, importance: int) -> None: - self._importance = importance - - def canBypassDnd(self) -> bool: - return self._bypass_dnd - - def setBypassDnd(self, bypassDnd: bool) -> None: - self._bypass_dnd = bypassDnd - - def getLockscreenVisibility(self) -> int: - return self._lockscreen_visibility - - def setLockscreenVisibility(self, visibility: int) -> None: - self._lockscreen_visibility = visibility - - def canShowBadge(self) -> bool: - return self._show_badge - - def setShowBadge(self, showBadge: bool) -> None: - self._show_badge = showBadge - - def getSound(self) -> Optional[str]: - return self._sound - - def setSound(self, sound: Optional[str], audioAttributes: Any = None) -> None: - self._sound = sound - self._audio_attributes = audioAttributes - - def shouldVibrate(self) -> bool: - return self._vibration_enabled - - def enableVibration(self, vibrationPattern: Optional[List[int]] = None) -> None: - self._vibration_enabled = True - if vibrationPattern is not None: - self._vibration_pattern = vibrationPattern - - def getVibrationPattern(self) -> Optional[List[int]]: - return self._vibration_pattern - - def getLightColor(self) -> int: - return self._light_color - - def setLightColor(self, color: int) -> None: - self._light_color = color - self._light_enabled = True - - def getGroup(self) -> Optional[str]: - return self._group - - def setGroup(self, groupId: Optional[str]) -> None: - self._group = groupId - - def canBubble(self) -> bool: - return self._can_bubble - - def setAllowBubbles(self, allow: bool) -> None: - self._allow_bubbles = allow - - def isConversation(self) -> bool: - return self._is_conversation - - def setConversationId(self, conversationId: str, parentChannelId: str = "") -> None: - self._conversation_id = conversationId - self._is_conversation = True - - def isDeleted(self) -> bool: - return self._deleted - - def isImportantConversation(self) -> bool: - return self._is_important_conversation - - def toString(self) -> str: - return f"NotificationChannel(id={self._id}, name={self._name}, importance={self._importance})" - - -# ═══════════════════════════════════════════════════════════════ -# NotificationChannelGroup -# ═══════════════════════════════════════════════════════════════ - -class NotificationChannelGroup(Stub): - """NotificationChannelGroup — 通知渠道组""" - - def __init__(self, groupId: str, groupName: str): - super().__init__() - self._id = groupId - self._name = groupName - self._description: Optional[str] = None - self._channels: List[NotificationChannel] = [] - self._blocked = False - - def getId(self) -> str: - return self._id - - def getName(self) -> str: - return self._name - - def getDescription(self) -> Optional[str]: - return self._description - - def setDescription(self, description: str) -> None: - self._description = description - - def getChannels(self) -> List[NotificationChannel]: - return list(self._channels) - - def isBlocked(self) -> bool: - return self._blocked - - def toString(self) -> str: - return f"NotificationChannelGroup(id={self._id}, name={self._name})" - - -# ═══════════════════════════════════════════════════════════════ -# Notification.Action -# ═══════════════════════════════════════════════════════════════ - -class NotificationAction(Stub): - """Notification.Action — 通知操作""" - - def __init__( - self, - icon: Optional[int] = None, - title: Optional[str] = None, - pendingIntent: Any = None, - ): - super().__init__() - self.icon = icon - self.title = title - self.actionIntent = pendingIntent - self._extras: Optional[Any] = None - self._remote_inputs: List[Any] = [] - self._semantic_action = SEMANTIC_ACTION_NONE - self._contextual = False - self._allow_generated_replies = False - self._is_authentication_required = False - self._is_bubble = False - - def getExtras(self) -> Optional[Any]: - return self._extras - - def getRemoteInputs(self) -> List[Any]: - return list(self._remote_inputs) - - def getSemanticAction(self) -> int: - return self._semantic_action - - def isContextual(self) -> bool: - return self._contextual - - def getAllowGeneratedReplies(self) -> bool: - return self._allow_generated_replies - - def isAuthenticationRequired(self) -> bool: - return self._is_authentication_required - - def toString(self) -> str: - return f"Notification.Action(title={self.title}, icon={self.icon})" - - -# ═══════════════════════════════════════════════════════════════ -# Notification.Style 基类 -# ═══════════════════════════════════════════════════════════════ - -class NotificationStyle(Stub): - """Notification.Style — 通知样式基类""" - - def __init__(self): - super().__init__() - self._big_content_title: Optional[str] = None - self._summary_text: Optional[str] = None - self._colorized = False - - def setBigContentTitle(self, title: str) -> "NotificationStyle": - self._big_content_title = title - return self - - def setSummaryText(self, summary: str) -> "NotificationStyle": - self._summary_text = summary - return self - - def getBigContentTitle(self) -> Optional[str]: - return self._big_content_title - - def getSummaryText(self) -> Optional[str]: - return self._summary_text - - -class BigPictureStyle(NotificationStyle): - """Notification.BigPictureStyle — 大图通知样式""" - - def __init__(self): - super().__init__() - self._big_large_icon: Optional[Any] = None - self._big_picture: Optional[Any] = None - self._content_description: Optional[str] = None - self._show_icon = False - - def bigPicture(self, picture: Any) -> "BigPictureStyle": - self._big_picture = picture - return self - - def bigLargeIcon(self, icon: Any) -> "BigPictureStyle": - self._big_large_icon = icon - return self - - def setContentDescription(self, description: str) -> "BigPictureStyle": - self._content_description = description - return self - - def showBigPictureWhenCollapsed(self, show: bool) -> "BigPictureStyle": - self._show_icon = show - return self - - def getBigPicture(self) -> Optional[Any]: - return self._big_picture - - def toString(self) -> str: - return f"BigPictureStyle(title={self._big_content_title})" - - -class BigTextStyle(NotificationStyle): - """Notification.BigTextStyle — 大文本通知样式""" - - def __init__(self): - super().__init__() - self._big_text: Optional[str] = None - self._lines: List[str] = [] - - def bigText(self, text: str) -> "BigTextStyle": - self._big_text = text - return self - - def setLines(self, lines: List[str]) -> "BigTextStyle": - self._lines = list(lines) - return self - - def getBigText(self) -> Optional[str]: - return self._big_text - - def toString(self) -> str: - return f"BigTextStyle(text_len={len(self._big_text) if self._big_text else 0})" - - -class InboxStyle(NotificationStyle): - """Notification.InboxStyle — 收件箱通知样式""" - - def __init__(self): - super().__init__() - self._lines: List[str] = [] - - def addLine(self, line: str) -> "InboxStyle": - self._lines.append(line) - return self - - def setLines(self, lines: List[str]) -> "InboxStyle": - self._lines = list(lines) - return self - - def getLines(self) -> List[str]: - return list(self._lines) - - def toString(self) -> str: - return f"InboxStyle(lines={len(self._lines)})" - - -class MessagingStyle(NotificationStyle): - """Notification.MessagingStyle — 消息通知样式""" - - class Message(Stub): - """Notification.MessagingStyle.Message — 单条消息""" - - def __init__( - self, - text: str, - timestamp: int, - sender: Optional[str] = None, - bundle: Optional[Any] = None, - ): - super().__init__() - self._text = text - self._timestamp = timestamp - self._sender = sender - self._bundle = bundle - self._data_mime_type: Optional[str] = None - self._data_uri: Optional[str] = None - self._remote_input_history: List[Any] = [] - self._extras: Optional[Any] = None - - def getText(self) -> str: - return self._text - - def getTimestamp(self) -> int: - return self._timestamp - - def getSender(self) -> Optional[str]: - return self._sender - - def getDataMimeType(self) -> Optional[str]: - return self._data_mime_type - - def getDataUri(self) -> Optional[str]: - return self._data_uri - - def setData(self, mimeType: str, uri: str) -> None: - self._data_mime_type = mimeType - self._data_uri = uri - - def getExtras(self) -> Optional[Any]: - return self._extras - - def getRemoteInputHistory(self) -> List[Any]: - return list(self._remote_input_history) - - def toString(self) -> str: - return f"Message(sender={self._sender}, text={self._text})" - - def __init__(self, user_display_name: str, conversation_title: Optional[str] = None): - super().__init__() - self._user_display_name = user_display_name - self._conversation_title = conversation_title - self._messages: List[MessagingStyle.Message] = [] - self._group_conversation = False - self._is_group_conversation = False - - def addMessage(self, message: "MessagingStyle.Message") -> "MessagingStyle": - self._messages.append(message) - return self - - def addHistoricMessage(self, message: "MessagingStyle.Message") -> "MessagingStyle": - if message not in self._messages: - self._messages.append(message) - return self - - def setConversationTitle(self, title: str) -> "MessagingStyle": - self._conversation_title = title - return self - - def setGroupConversation(self, isGroup: bool) -> "MessagingStyle": - self._is_group_conversation = isGroup - return self - - def getUserDisplayName(self) -> str: - return self._user_display_name - - def getConversationTitle(self) -> Optional[str]: - return self._conversation_title - - def getMessages(self) -> List["MessagingStyle.Message"]: - return list(self._messages) - - def isGroupConversation(self) -> bool: - return self._is_group_conversation - - def toString(self) -> str: - return f"MessagingStyle(user={self._user_display_name}, msgs={len(self._messages)})" - - -class CarExtender(Stub): - """Notification.CarExtender — 车载通知扩展""" - - class UnreadConversation(Stub): - """Notification.CarExtender.UnreadConversation — 未读对话""" - - def __init__(self): - super().__init__() - self._name: Optional[str] = None - self._latest_timestamp: Optional[int] = None - self._messages: List[str] = [] - self._remote_input: Optional[Any] = None - self._participants: List[str] = [] - self._pending_intent: Optional[Any] = None - - def getName(self) -> Optional[str]: - return self._name - - def getLatestTimestamp(self) -> Optional[int]: - return self._latest_timestamp - - def getMessages(self) -> List[str]: - return list(self._messages) - - def getRemoteInput(self) -> Optional[Any]: - return self._remote_input - - def getParticipants(self) -> List[str]: - return list(self._participants) - - def getPendingIntent(self) -> Optional[Any]: - return self._pending_intent - - def toString(self) -> str: - return f"UnreadConversation(name={self._name})" - - def __init__(self): - super().__init__() - self._color = 0 - self._large_icon: Optional[Any] = None - self._unread_conversation: Optional["CarExtender.UnreadConversation"] = None - - def setColor(self, color: int) -> "CarExtender": - self._color = color - return self - - def getColor(self) -> int: - return self._color - - def setLargeIcon(self, icon: Any) -> "CarExtender": - self._large_icon = icon - return self - - def getLargeIcon(self) -> Optional[Any]: - return self._large_icon - - def setUnreadConversation( - self, conversation: "CarExtender.UnreadConversation" - ) -> "CarExtender": - self._unread_conversation = conversation - return self - - def getUnreadConversation(self) -> Optional["CarExtender.UnreadConversation"]: - return self._unread_conversation - - def toString(self) -> str: - return "CarExtender" - - -# ═══════════════════════════════════════════════════════════════ -# NotificationCompat -# ═══════════════════════════════════════════════════════════════ - -class NotificationCompat(Stub): - """NotificationCompat — 兼容通知构建器""" - - class Builder(Stub): - """NotificationCompat.Builder — 通知构建器""" - - def __init__(self, context: Any = None, channelId: Optional[str] = None): - super().__init__() - self._context = context - self._channel_id = channelId or "" - self._content_title: Optional[str] = None - self._content_text: Optional[str] = None - self._content_info: Optional[str] = None - self._sub_text: Optional[str] = None - self._ticker_text: Optional[str] = None - self._small_icon: Optional[Any] = None - self._large_icon: Optional[Any] = None - self._priority = PRIORITY_DEFAULT - self._flags = 0 - self._defaults = 0 - self._when: Optional[int] = None - self._color = 0 - self._colorized = False - self._category: Optional[str] = None - self._group: Optional[str] = None - self._group_summary = False - self._group_alert_behavior = GROUP_ALERT_ALL - self._number = 0 - self._content_intent: Optional[Any] = None - self._delete_intent: Optional[Any] = None - self._full_screen_intent: Optional[Any] = None - self._actions: List[NotificationAction] = [] - self._style: Optional[NotificationStyle] = None - self._extras: Dict[str, Any] = {} - self._people: List[str] = [] - self._uses_chronometer = False - self._auto_cancel = False - self._local_only = False - self._ongoing = False - self._only_alert_once = False - self._show_when = True - self._sort_key: Optional[str] = None - self._public_version: Optional[Any] = None - self._notification_sound: Optional[str] = None - self._notification_vibrate: Optional[List[int]] = None - self._notification_led_color = 0 - self._notification_led_on_ms = 0 - self._notification_led_off_ms = 0 - self._timeout_ms: Optional[int] = None - self._timeout_after_ms: Optional[int] = None - self._shortcut_id: Optional[str] = None - self._time: Optional[int] = None - self._visibility = VISIBILITY_PRIVATE - self._badge_icon_type = BADGE_ICON_NONE - self._settings_text: Optional[str] = None - self._time_zone: Optional[Any] = None - self._contextual = False - self._allow_system_generated_contextual_actions = True - self._bubble_metadata: Optional[Any] = None - self._is_bubble = False - self._fgs_delegate: Optional[Any] = None - - def setChannelId(self, channelId: str) -> "NotificationCompat.Builder": - self._channel_id = channelId - return self - - def setContentTitle(self, title: str) -> "NotificationCompat.Builder": - self._content_title = title - return self - - def setContentText(self, text: str) -> "NotificationCompat.Builder": - self._content_text = text - return self - - def setSubText(self, text: str) -> "NotificationCompat.Builder": - self._sub_text = text - return self - - def setContentInfo(self, info: str) -> "NotificationCompat.Builder": - self._content_info = info - return self - - def setTicker(self, tickerText: str) -> "NotificationCompat.Builder": - self._ticker_text = tickerText - return self - - def setSmallIcon(self, icon: Any) -> "NotificationCompat.Builder": - self._small_icon = icon - return self - - def setLargeIcon(self, icon: Any) -> "NotificationCompat.Builder": - self._large_icon = icon - return self - - def setPriority(self, priority: int) -> "NotificationCompat.Builder": - self._priority = priority - return self - - def setDefaults(self, defaults: int) -> "NotificationCompat.Builder": - self._defaults = defaults - return self - - def setWhen(self, when: int) -> "NotificationCompat.Builder": - self._when = when - return self - - def setUsesChronometer(self, b: bool) -> "NotificationCompat.Builder": - self._uses_chronometer = b - return self - - def setColor(self, color: int) -> "NotificationCompat.Builder": - self._color = color - return self - - def setColorized(self, colorized: bool) -> "NotificationCompat.Builder": - self._colorized = colorized - return self - - def setCategory(self, category: str) -> "NotificationCompat.Builder": - self._category = category - return self - - def setGroup(self, groupKey: str) -> "NotificationCompat.Builder": - self._group = groupKey - return self - - def setGroupSummary(self, isGroupSummary: bool) -> "NotificationCompat.Builder": - self._group_summary = isGroupSummary - return self - - def setGroupAlertBehavior(self, behavior: int) -> "NotificationCompat.Builder": - self._group_alert_behavior = behavior - return self - - def setNumber(self, number: int) -> "NotificationCompat.Builder": - self._number = number - return self - - def setContentIntent(self, intent: Any) -> "NotificationCompat.Builder": - self._content_intent = intent - return self - - def setDeleteIntent(self, intent: Any) -> "NotificationCompat.Builder": - self._delete_intent = intent - return self - - def setFullScreenIntent(self, intent: Any, highPriority: bool) -> "NotificationCompat.Builder": - self._full_screen_intent = intent - if highPriority: - self._flags |= FLAG_HIGH_PRIORITY - return self - - def addAction( - self, iconOrAction: Any, title: Optional[str] = None, pendingIntent: Any = None - ) -> "NotificationCompat.Builder": - if isinstance(iconOrAction, NotificationAction): - self._actions.append(iconOrAction) - elif isinstance(iconOrAction, int): - self._actions.append(NotificationAction(iconOrAction, title, pendingIntent)) - else: - self._actions.append(NotificationAction(None, str(iconOrAction), pendingIntent)) - return self - - def setStyle(self, style: NotificationStyle) -> "NotificationCompat.Builder": - self._style = style - return self - - def addPerson(self, uri: str) -> "NotificationCompat.Builder": - self._people.append(uri) - return self - - def setShowWhen(self, show: bool) -> "NotificationCompat.Builder": - self._show_when = show - return self - - def setLocalOnly(self, localOnly: bool) -> "NotificationCompat.Builder": - self._local_only = localOnly - return self - - def setOngoing(self, ongoing: bool) -> "NotificationCompat.Builder": - self._ongoing = ongoing - if ongoing: - self._flags |= FLAG_ONGOING_EVENT - else: - self._flags &= ~FLAG_ONGOING_EVENT - return self - - def setAutoCancel(self, autoCancel: bool) -> "NotificationCompat.Builder": - self._auto_cancel = autoCancel - if autoCancel: - self._flags |= FLAG_AUTO_CANCEL - else: - self._flags &= ~FLAG_AUTO_CANCEL - return self - - def setOnlyAlertOnce(self, onlyAlertOnce: bool) -> "NotificationCompat.Builder": - self._only_alert_once = onlyAlertOnce - if onlyAlertOnce: - self._flags |= FLAG_ONLY_ALERT_ONCE - else: - self._flags &= ~FLAG_ONLY_ALERT_ONCE - return self - - def setVisibility(self, visibility: int) -> "NotificationCompat.Builder": - self._visibility = visibility - return self - - def setBadgeIconType(self, iconType: int) -> "NotificationCompat.Builder": - self._badge_icon_type = iconType - return self - - def setSettingsText(self, text: str) -> "NotificationCompat.Builder": - self._settings_text = text - return self - - def setSortKey(self, sortKey: str) -> "NotificationCompat.Builder": - self._sort_key = sortKey - return self - - def setTimeout(self, durationMs: int) -> "NotificationCompat.Builder": - self._timeout_ms = durationMs - return self - - def setTimeoutAfter(self, durationMs: int) -> "NotificationCompat.Builder": - self._timeout_after_ms = durationMs - return self - - def setShortcutId(self, shortcutId: str) -> "NotificationCompat.Builder": - self._shortcut_id = shortcutId - return self - - def setPublicVersion(self, notification: Any) -> "NotificationCompat.Builder": - self._public_version = notification - return self - - def extend(self, extender: Any) -> "NotificationCompat.Builder": - if extender: - extender.extend(self) - return self - - def setSound(self, sound: Optional[str]) -> "NotificationCompat.Builder": - self._notification_sound = sound - return self - - def setVibrate(self, pattern: Optional[List[int]]) -> "NotificationCompat.Builder": - self._notification_vibrate = pattern - return self - - def setLights(self, color: int, onMs: int, offMs: int) -> "NotificationCompat.Builder": - self._notification_led_color = color - self._notification_led_on_ms = onMs - self._notification_led_off_ms = offMs - return self - - def setFlag(self, flag: int, enabled: bool) -> "NotificationCompat.Builder": - if enabled: - self._flags |= flag - else: - self._flags &= ~flag - return self - - def addExtras(self, extras: Dict[str, Any]) -> "NotificationCompat.Builder": - self._extras.update(extras) - return self - - def getExtras(self) -> Dict[str, Any]: - return self._extras - - def build(self) -> "NotificationCompat": - return NotificationCompat(self) - - def toString(self) -> str: - return f"NotificationCompat.Builder(title={self._content_title})" - - def __init__(self, builder: "NotificationCompat.Builder"): - super().__init__() - self._channel_id = builder._channel_id - self._content_title = builder._content_title - self._content_text = builder._content_text - self._sub_text = builder._sub_text - self._priority = builder._priority - self._flags = builder._flags - self._defaults = builder._defaults - self._when = builder._when - self._small_icon = builder._small_icon - self._large_icon = builder._large_icon - self._actions = list(builder._actions) - self._style = builder._style - self._category = builder._category - self._group = builder._group - self._color = builder._color - self._extras = dict(builder._extras) - self._visibility = builder._visibility - - def getChannelId(self) -> Optional[str]: - return self._channel_id - - def getContentTitle(self) -> Optional[str]: - return self._content_title - - def getContentText(self) -> Optional[str]: - return self._content_text - - def getSubText(self) -> Optional[str]: - return self._sub_text - - def getPriority(self) -> int: - return self._priority - - def getFlags(self) -> int: - return self._flags - - def getDefaults(self) -> int: - return self._defaults - - def getCategory(self) -> Optional[str]: - return self._category - - def getGroup(self) -> Optional[str]: - return self._group - - def getColor(self) -> int: - return self._color - - def getVisibility(self) -> int: - return self._visibility - - def getExtras(self) -> Dict[str, Any]: - return dict(self._extras) - - def getActions(self) -> List[NotificationAction]: - return list(self._actions) - - def getStyle(self) -> Optional[NotificationStyle]: - return self._style - - def toString(self) -> str: - return f"Notification(channel={self._channel_id}, title={self._content_title})" - - -# ═══════════════════════════════════════════════════════════════ -# Notification (简化) -# ═══════════════════════════════════════════════════════════════ - -class Notification(Stub): - """Notification — Android 通知""" - - Action = NotificationAction - BigPictureStyle = BigPictureStyle - BigTextStyle = BigTextStyle - InboxStyle = InboxStyle - MessagingStyle = MessagingStyle - CarExtender = CarExtender - Style = NotificationStyle - - def __init__(self): - super().__init__() - self._when: Optional[int] = None - self._content_title: Optional[str] = None - self._content_text: Optional[str] = None - self._small_icon: Optional[Any] = None - self._large_icon: Optional[Any] = None - self._ticker_text: Optional[str] = None - self._content_intent: Optional[Any] = None - self._delete_intent: Optional[Any] = None - self._full_screen_intent: Optional[Any] = None - self._flags = 0 - self._defaults = 0 - self._sound: Optional[str] = None - self._vibrate: Optional[List[int]] = None - self._led_color = 0 - self._led_on_ms = 0 - self._led_off_ms = 0 - self._number = 0 - self._icon = 0 - self._icon_level = 0 - self._priority = PRIORITY_DEFAULT - self._category: Optional[str] = None - self._group: Optional[str] = None - self._group_summary = False - self._color = 0 - self._visibility = VISIBILITY_PRIVATE - self._extras: Dict[str, Any] = {} - self._actions: List[NotificationAction] = [] - - def getWhen(self) -> Optional[int]: - return self._when - - def getContentTitle(self) -> Optional[str]: - return self._content_title - - def getContentText(self) -> Optional[str]: - return self._content_text - - def getSmallIcon(self) -> Optional[Any]: - return self._small_icon - - def getLargeIcon(self) -> Optional[Any]: - return self._large_icon - - def getFlags(self) -> int: - return self._flags - - def setFlags(self, flags: int) -> None: - self._flags = flags - - def getDefaults(self) -> int: - return self._defaults - - def getNumber(self) -> int: - return self._number - - def getColor(self) -> int: - return self._color - - def getCategory(self) -> Optional[str]: - return self._category - - def getGroup(self) -> Optional[str]: - return self._group - - def getPriority(self) -> int: - return self._priority - - def getVisibility(self) -> int: - return self._visibility - - def getExtras(self) -> Dict[str, Any]: - return dict(self._extras) - - def getActions(self) -> List[NotificationAction]: - return list(self._actions) - - def toString(self) -> str: - return f"Notification(title={self._content_title})" - - -# ═══════════════════════════════════════════════════════════════ -# NotificationManager -# ═══════════════════════════════════════════════════════════════ - -class NotificationManager(Stub): - """NotificationManager — 通知管理器 (桌面存根)""" - - # ImportErrorBehavior 常量 - IMPORTANCE_UNSPECIFIED = IMPORTANCE_UNSPECIFIED - IMPORTANCE_NONE = IMPORTANCE_NONE - IMPORTANCE_MIN = IMPORTANCE_MIN - IMPORTANCE_LOW = IMPORTANCE_LOW - IMPORTANCE_DEFAULT = IMPORTANCE_DEFAULT - IMPORTANCE_HIGH = IMPORTANCE_HIGH - IMPORTANCE_MAX = IMPORTANCE_MAX - - INTERRUPTION_FILTER_UNKNOWN = INTERRUPTION_FILTER_UNKNOWN - INTERRUPTION_FILTER_ALL = INTERRUPTION_FILTER_ALL - INTERRUPTION_FILTER_PRIORITY = INTERRUPTION_FILTER_PRIORITY - INTERRUPTION_FILTER_NONE = INTERRUPTION_FILTER_NONE - INTERRUPTION_FILTER_ALARMS = INTERRUPTION_FILTER_ALARMS - - BUBBLE_PREFERENCE_NONE = 0 - BUBBLE_PREFERENCE_SELECTED = 1 - BUBBLE_PREFERENCE_ALL = 2 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._lock = threading.Lock() - self._notifications: Dict[str, List[Tuple[int, Any]]] = {} - self._channels: Dict[str, NotificationChannel] = {} - self._channel_groups: Dict[str, NotificationChannelGroup] = {} - self._interruption_filter = INTERRUPTION_FILTER_ALL - self._notification_delegate: Optional[str] = None - self._notification_delegate_enabled = False - self._are_notifications_enabled = True - self._bubble_preference = NotificationManager.BUBBLE_PREFERENCE_SELECTED - self._can_post_notifications = True - self._auto_bundling_enabled = True - self._notification_assist: Optional[str] = None - - def notify(self, tag: Optional[str], id: int, notification: Any) -> None: - """发送通知""" - with self._lock: - key = tag or "__default__" - if key not in self._notifications: - self._notifications[key] = [] - self._notifications[key].append((id, notification)) - - def cancel(self, tag: Optional[str], id: int) -> None: - """取消指定通知""" - with self._lock: - key = tag or "__default__" - if key in self._notifications: - self._notifications[key] = [ - (nid, n) for nid, n in self._notifications[key] if nid != id - ] - - def cancelAll(self) -> None: - """取消所有通知""" - with self._lock: - self._notifications.clear() - - def getActiveNotifications(self) -> List[Any]: - """获取活动通知列表""" - with self._lock: - result = [] - for entries in self._notifications.values(): - for _, notification in entries: - result.append(notification) - return result - - def createNotificationChannel(self, channel: NotificationChannel) -> None: - """创建通知渠道""" - with self._lock: - self._channels[channel.getId()] = channel - - def createNotificationChannelGroup(self, group: NotificationChannelGroup) -> None: - """创建通知渠道组""" - with self._lock: - self._channel_groups[group.getId()] = group - - def createNotificationChannels(self, channels: List[NotificationChannel]) -> None: - """批量创建通知渠道""" - with self._lock: - for ch in channels: - self._channels[ch.getId()] = ch - - def createNotificationChannelGroups(self, groups: List[NotificationChannelGroup]) -> None: - """批量创建通知渠道组""" - with self._lock: - for g in groups: - self._channel_groups[g.getId()] = g - - def getNotificationChannel(self, channelId: str) -> Optional[NotificationChannel]: - """获取通知渠道""" - with self._lock: - return self._channels.get(channelId) - - def getNotificationChannels(self) -> List[NotificationChannel]: - """获取所有通知渠道""" - with self._lock: - return list(self._channels.values()) - - def getNotificationChannelGroups(self) -> List[NotificationChannelGroup]: - """获取所有通知渠道组""" - with self._lock: - return list(self._channel_groups.values()) - - def deleteNotificationChannel(self, channelId: str) -> None: - """删除通知渠道""" - with self._lock: - if channelId in self._channels: - channel = self._channels.pop(channelId) - channel._deleted = True - - def getImportance(self) -> int: - """获取当前通知重要性""" - return IMPORTANCE_DEFAULT - - def areNotificationsEnabled(self) -> bool: - """检查通知是否启用""" - return self._are_notifications_enabled - - def isNotificationPolicyAccessGranted(self) -> bool: - """检查是否授予通知策略访问权限""" - return True - - def getCurrentInterruptionFilter(self) -> int: - """获取当前中断过滤器""" - return self._interruption_filter - - def setInterruptionFilter(self, filterType: int) -> None: - """设置中断过滤器""" - self._interruption_filter = filterType - - def getNotificationDelegate(self) -> Optional[str]: - """获取通知代理""" - return self._notification_delegate - - def isNotificationListenerAccessGranted(self, listener: Any) -> bool: - """检查是否授予通知监听访问权限""" - return True - - def matchesCallFilter(self, incomingAddress: Any) -> bool: - """检查是否匹配来电过滤""" - return True - - def getBubblePreference(self) -> int: - """获取气泡偏好""" - return self._bubble_preference - - def canNotifyAsPackage(self, pkg: str) -> bool: - """检查能否以指定包名发送通知""" - return True - - def setNotificationDelegate(self, delegate: str) -> None: - """设置通知代理""" - self._notification_delegate = delegate - - def areBubblesAllowed(self) -> bool: - """检查是否允许气泡""" - return True - - def getActiveNotificationCount(self) -> int: - """获取活动通知数量""" - with self._lock: - count = 0 - for entries in self._notifications.values(): - count += len(entries) - return count - - def isAutoBundlingEnabled(self) -> bool: - """检查自动捆绑是否启用""" - return self._auto_bundling_enabled - - def setAutoBundlingEnabled(self, enabled: bool) -> None: - """设置自动捆绑""" - self._auto_bundling_enabled = enabled - - def getNotificationAssist(self) -> Optional[str]: - """获取通知助手""" - return self._notification_assist - - def getConsolidatedNotificationPolicy(self) -> Optional[Any]: - """获取合并通知策略""" - return None - - def toString(self) -> str: - with self._lock: - return ( - f"NotificationManager(channels={len(self._channels)}, " - f"groups={len(self._channel_groups)}, " - f"active_notifications={self.getActiveNotificationCount()})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# NotificationManager.Policy -# ═══════════════════════════════════════════════════════════════ - -class NotificationManagerPolicy(Stub): - """NotificationManager.Policy — 通知策略""" - - PRIORITY_CATEGORY_UNKNOWN = 0 - PRIORITY_CATEGORY_CALLS = 1 - PRIORITY_CATEGORY_MESSAGES = 2 - PRIORITY_CATEGORY_CONVERSATIONS = 3 - PRIORITY_CATEGORY_REMINDERS = 4 - PRIORITY_CATEGORY_EVENTS = 5 - PRIORITY_CATEGORY_ALARMS = 6 - PRIORITY_CATEGORY_MEDIA = 7 - PRIORITY_CATEGORY_SYSTEM = 8 - PRIORITY_CATEGORY_ALERTS = 9 - - PRIORITY_SENDERS_ANY = 0 - PRIORITY_SENDERS_CONTACTS = 1 - PRIORITY_SENDERS_STARRED = 2 - - SUPPRESSED_EFFECT_SCREEN_OFF = 1 - SUPPRESSED_EFFECT_SCREEN_ON = 2 - SUPPRESSED_EFFECT_FULL_SCREEN_INTENT = 4 - SUPPRESSED_EFFECT_AMBIENT = 8 - SUPPRESSED_EFFECT_PEEK = 16 - SUPPRESSED_EFFECT_STATUS_BAR = 32 - SUPPRESSED_EFFECT_BADGE = 64 - SUPPRESSED_EFFECT_BANNER = 128 - SUPPRESSED_EFFECT_LIGHTS = 256 - - def __init__( - self, - priorityCategories: int = 0, - priorityCallSenders: int = PRIORITY_SENDERS_ANY, - priorityMessageSenders: int = PRIORITY_SENDERS_ANY, - suppressedEffects: int = 0, - ): - super().__init__() - self.priorityCategories = priorityCategories - self.priorityCallSenders = priorityCallSenders - self.priorityMessageSenders = priorityMessageSenders - self.suppressedEffects = suppressedEffects - - def toString(self) -> str: - return ( - f"NotificationManager.Policy(" - f"priorityCategories={self.priorityCategories}, " - f"priorityCallSenders={self.priorityCallSenders}, " - f"priorityMessageSenders={self.priorityMessageSenders})" - ) \ No newline at end of file diff --git a/android_pyapi/android/app_people.py b/android_pyapi/android/app_people.py deleted file mode 100644 index c139a83..0000000 --- a/android_pyapi/android/app_people.py +++ /dev/null @@ -1,268 +0,0 @@ -""" -Android People API — PeopleManager, ConversationStatus, PeopleManager.Person, PeopleManager.Conversation -方法签名完全对齐 Android SDK -""" - -import sys -from typing import Any, Dict, List, Optional, Set, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "PeopleManager", - "ConversationStatus", -] - - -# ═══════════════════════════════════════════════════════════════ -# PeopleManager -# ═══════════════════════════════════════════════════════════════ - -class PeopleManager(Stub): - """PeopleManager — 人物管理器 (桌面存根)""" - - ACTION_PEOPLE_CONVERSATION = "android.app.people.action.PEOPLE_CONVERSATION" - ACTION_REMOVE_STATUS = "android.app.people.action.REMOVE_STATUS" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._statuses: Dict[str, "ConversationStatus"] = {} - self._people: Dict[str, "PeopleManager.Person"] = {} - self._conversations: Dict[str, "PeopleManager.Conversation"] = {} - self._listeners: List[Callable] = [] - - def getStatus(self, packageName: str, personUri: str) -> Optional["ConversationStatus"]: - """获取指定人的状态""" - key = f"{packageName}:{personUri}" - return self._statuses.get(key) - - def getDataIdFromPhoneNumber(self, phoneNumber: str) -> Optional[str]: - """根据电话号码获取数据 ID""" - return None - - def addOrUpdateStatus( - self, packageName: str, status: "ConversationStatus" - ): - """添加或更新状态""" - key = f"{packageName}:{status.getPersonUri()}" - self._statuses[key] = status - - def removeStatus(self, packageName: str, personUri: str): - """移除状态""" - key = f"{packageName}:{personUri}" - self._statuses.pop(key, None) - - def getStoredPeople(self) -> List["PeopleManager.Person"]: - """获取已存储的人物列表""" - return list(self._people.values()) - - def isConversation(self, packageName: str, personUri: str) -> bool: - """检查是否为对话""" - key = f"{packageName}:{personUri}" - return key in self._conversations - - def getConversation( - self, packageName: str, personUri: str - ) -> Optional["PeopleManager.Conversation"]: - """获取对话信息""" - key = f"{packageName}:{personUri}" - return self._conversations.get(key) - - # ═══════════════════════════════════════════════════════════ - # Status - 状态内部类 - # ═══════════════════════════════════════════════════════════ - - class Status(Stub): - """PeopleManager.Status — 状态基类""" - - def __init__(self): - super().__init__() - - def toString(self) -> str: - return f"Status@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - # ═══════════════════════════════════════════════════════════ - # Person - 人物内部类 - # ═══════════════════════════════════════════════════════════ - - class Person(Stub): - """PeopleManager.Person — 人物信息""" - - def __init__( - self, - name: str = "", - key: str = "", - uri: str = "", - importantForPeople: bool = False, - ): - super().__init__() - self._name = name - self._key = key - self._uri = uri - self._important_for_people = importantForPeople - - def getName(self) -> str: - return self._name - - def getKey(self) -> str: - return self._key - - def getUri(self) -> str: - return self._uri - - def getImportantForPeople(self) -> bool: - return self._important_for_people - - def toString(self) -> str: - return f"Person(name={self._name}, key={self._key})" - - def equals(self, other) -> bool: - if not isinstance(other, PeopleManager.Person): - return False - return self._key == other._key - - def hashCode(self) -> int: - return hash(self._key) & 0x7FFFFFFF - - # ═══════════════════════════════════════════════════════════ - # Conversation - 对话内部类 - # ═══════════════════════════════════════════════════════════ - - class Conversation(Stub): - """PeopleManager.Conversation — 对话信息""" - - def __init__( - self, - conv_id: str = "", - person: "PeopleManager.Person" = None, - notificationChannelId: str = "", - shortcutInfo: Any = None, - parentUserHandle: Any = None, - ): - super().__init__() - self._id = conv_id - self._person = person - self._notification_channel_id = notificationChannelId - self._shortcut_info = shortcutInfo - self._parent_user_handle = parentUserHandle - - def getId(self) -> str: - return self._id - - def getPerson(self) -> Optional["PeopleManager.Person"]: - return self._person - - def getNotificationChannelId(self) -> str: - return self._notification_channel_id - - def getShortcutInfo(self) -> Optional[Any]: - return self._shortcut_info - - def getParentUserHandle(self) -> Optional[Any]: - return self._parent_user_handle - - def toString(self) -> str: - return f"Conversation(id={self._id})" - - def equals(self, other) -> bool: - if not isinstance(other, PeopleManager.Conversation): - return False - return self._id == other._id - - def hashCode(self) -> int: - return hash(self._id) & 0x7FFFFFFF - - def toString(self) -> str: - return f"PeopleManager@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# ConversationStatus -# ═══════════════════════════════════════════════════════════════ - -class ConversationStatus(Stub): - """ConversationStatus — 对话状态""" - - def __init__( - self, - userId: int = 0, - packageName: str = "", - personUri: str = "", - status: int = 0, - statusIcon: int = 0, - notificationKey: str = "", - notificationContentDescription: str = "", - ): - super().__init__() - self._user_id = userId - self._package_name = packageName - self._person_uri = personUri - self._status = status - self._status_icon = statusIcon - self._notification_key = notificationKey - self._notification_content_description = notificationContentDescription - self._active = False - self._muted = False - - def getUserId(self) -> int: - return self._user_id - - def getPackageName(self) -> str: - return self._package_name - - def getPersonUri(self) -> str: - return self._person_uri - - def getStatus(self) -> int: - return self._status - - def getStatusIcon(self) -> int: - return self._status_icon - - def getNotificationKey(self) -> str: - return self._notification_key - - def getNotificationContentDescription(self) -> str: - return self._notification_content_description - - def isActive(self) -> bool: - return self._active - - def isMuted(self) -> bool: - return self._muted - - def setActive(self, active: bool): - self._active = active - - def setMuted(self, muted: bool): - self._muted = muted - - def toString(self) -> str: - return (f"ConversationStatus(pkg={self._package_name}, " - f"uri={self._person_uri}, active={self._active})") - - def equals(self, other) -> bool: - if not isinstance(other, ConversationStatus): - return False - return (self._package_name == other._package_name - and self._person_uri == other._person_uri) - - def hashCode(self) -> int: - return hash((self._package_name, self._person_uri)) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/app_role.py b/android_pyapi/android/app_role.py deleted file mode 100644 index cb276a1..0000000 --- a/android_pyapi/android/app_role.py +++ /dev/null @@ -1,117 +0,0 @@ -""" -Android Role API — RoleManager -方法签名完全对齐 Android SDK -""" - -import sys -from typing import Any, Dict, List, Optional, Set, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "RoleManager", -] - - -# ═══════════════════════════════════════════════════════════════ -# RoleManager -# ═══════════════════════════════════════════════════════════════ - -class RoleManager(Stub): - """RoleManager — 角色管理器 (桌面存根)""" - - ACTION_REQUEST_ROLE = "android.app.role.action.REQUEST_ROLE" - - # --- 角色常量 --- - ROLE_ASSISTANT = "android.app.role.ASSISTANT" - ROLE_BROWSER = "android.app.role.BROWSER" - ROLE_DIALER = "android.app.role.DIALER" - ROLE_SMS = "android.app.role.SMS" - ROLE_HOME = "android.app.role.HOME" - ROLE_EMERGENCY = "android.app.role.EMERGENCY" - ROLE_CALL_REDIRECTION = "android.app.role.CALL_REDIRECTION" - ROLE_SYSTEM_EXCLUSIVE = "android.app.role.SYSTEM_EXCLUSIVE" - ROLE_WALLPAPER = "android.app.role.WALLPAPER" - ROLE_NOTIFICATION_ACCESS = "android.app.role.NOTIFICATION_ACCESS" - ROLE_FOREGROUND_SERVICE = "android.app.role.FOREGROUND_SERVICE" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - # 模拟桌面角色持有者 (key=角色名, value=持有者包名列表) - self._role_holders: Dict[str, List[str]] = { - self.ROLE_HOME: ["com.example.launcher"], - self.ROLE_BROWSER: ["org.mozilla.firefox"], - self.ROLE_DIALER: ["com.google.android.dialer"], - self.ROLE_SMS: ["com.google.android.messaging"], - } - self._listeners: List[Callable] = [] - - def isRoleAvailable(self, roleName: str) -> bool: - """检查角色是否可用""" - available_roles = [ - self.ROLE_ASSISTANT, self.ROLE_BROWSER, self.ROLE_DIALER, - self.ROLE_SMS, self.ROLE_HOME, self.ROLE_EMERGENCY, - self.ROLE_CALL_REDIRECTION, self.ROLE_SYSTEM_EXCLUSIVE, - self.ROLE_WALLPAPER, self.ROLE_NOTIFICATION_ACCESS, - self.ROLE_FOREGROUND_SERVICE, - ] - return roleName in available_roles - - def isRoleHeld(self, roleName: str) -> bool: - """检查当前应用是否持有指定角色""" - if not self._context: - return False - pkg = self._context.getPackageName() if hasattr(self._context, "getPackageName") else "" - holders = self._role_holders.get(roleName, []) - return pkg in holders - - def createRequestRoleIntent(self, roleName: str) -> Any: - """创建请求角色的 Intent""" - from .app import Intent - intent = Intent(self.ACTION_REQUEST_ROLE) - intent.putExtra("android.app.role.extra.ROLE_NAME", roleName) - return intent - - def getRoleHolders(self, roleName: str) -> List[str]: - """获取角色持有者列表""" - return list(self._role_holders.get(roleName, [])) - - def addOnRoleHoldersChangedListener( - self, executor: Any, listener: Callable - ): - """添加角色持有者变更监听器""" - if listener not in self._listeners: - self._listeners.append(listener) - - def removeOnRoleHoldersChangedListener(self, listener: Callable): - """移除角色持有者变更监听器""" - if listener in self._listeners: - self._listeners.remove(listener) - - def getRoleNamesFromController(self) -> List[str]: - """获取控制器管理的角色名列表""" - return [ - self.ROLE_ASSISTANT, self.ROLE_BROWSER, self.ROLE_DIALER, - self.ROLE_SMS, self.ROLE_HOME, self.ROLE_EMERGENCY, - self.ROLE_CALL_REDIRECTION, self.ROLE_SYSTEM_EXCLUSIVE, - self.ROLE_WALLPAPER, self.ROLE_NOTIFICATION_ACCESS, - self.ROLE_FOREGROUND_SERVICE, - ] - - def getApplicationVisibleRoleNames(self) -> List[str]: - """获取应用可见的角色名列表""" - return self.getRoleNamesFromController() - - def toString(self) -> str: - return f"RoleManager@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/app_search.py b/android_pyapi/android/app_search.py deleted file mode 100644 index c69e34a..0000000 --- a/android_pyapi/android/app_search.py +++ /dev/null @@ -1,494 +0,0 @@ -""" -Android app.search 包 — SearchManager, SearchableInfo, SearchView -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import sys -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "SearchManager", - "SearchableInfo", - "SearchView", - "SearchViewSearchAutoComplete", - "OnQueryTextListener", - "OnSuggestionListener", - "OnCloseListener", -] - - -# ═══════════════════════════════════════════════════════════════ -# SearchManager -# ═══════════════════════════════════════════════════════════════ - -class SearchManager(Stub): - """SearchManager — 搜索管理器 (桌面存根)""" - - INTENT_ACTION_SEARCHABLES_CHANGED = "android.search.action.SEARCHABLES_CHANGED" - INTENT_ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS" - INTENT_ACTION_SEARCH_SETTINGS_CHANGED = "android.search.action.SEARCH_SETTINGS_CHANGED" - APP_DATA_SEARCH_IS_GLOBAL_SEARCH = "search_is_global_search" - EXTRA_DATA_KEY = "data_key" - EXTRA_NEW_SEARCH = "new_search" - EXTRA_SELECT_QUERY = "select_query" - EXTRA_WEB_SEARCH_KEY = "web_search_key" - FLAG_QUERY_REFINEMENT = 0x1 - MENU_KEY = "search" - MENU_ORDER = 0 - QUERY = "query" - SUGGEST_COLUMN_FORMAT = "suggest_format" - SUGGEST_COLUMN_ICON_1 = "suggest_icon_1" - SUGGEST_COLUMN_ICON_2 = "suggest_icon_2" - SUGGEST_COLUMN_INTENT_ACTION = "suggest_intent_action" - SUGGEST_COLUMN_INTENT_DATA = "suggest_intent_data" - SUGGEST_COLUMN_INTENT_DATA_ID = "suggest_intent_data_id" - SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data" - SUGGEST_COLUMN_QUERY = "suggest_query" - SUGGEST_COLUMN_SHORTCUT_ID = "suggest_shortcut_id" - SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING = "suggest_spinner_while_refreshing" - SUGGEST_COLUMN_TEXT_1 = "suggest_text_1" - SUGGEST_COLUMN_TEXT_2 = "suggest_text_2" - SUGGEST_COLUMN_TEXT_2_URL = "suggest_text_2_url" - SUGGEST_MIME_TYPE = "vnd.android.cursor.dir/vnd.android.search.suggest" - SUGGEST_NEVER_MAKE_SHORTCUT = "never_make_shortcut" - SUGGEST_URI_PATH_QUERY = "/search_suggest_query" - SUGGEST_URI_PATH_SHORTCUT = "/search_suggest_shortcut" - SUGGEST_URI_PATH_SEARCH = "/search" - - _instance: Optional["SearchManager"] = None - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._searchable_info: Dict[str, "SearchableInfo"] = {} - self._global_search_activity: Optional[Any] = None - self._online_search_enabled = True - - @staticmethod - def get(context: Any) -> "SearchManager": - """获取搜索管理器""" - if SearchManager._instance is None: - SearchManager._instance = SearchManager(context) - return SearchManager._instance - - def startSearch(self, initial_query: str, select_init_key: bool, app_search_activity: Any, - global_search: bool, app_search_data: Any, extra_info: Any) -> None: - """开始搜索""" - pass - - def triggerSearch(self, query: str, app_search_activity: Any, app_search_data: Any) -> None: - """触发搜索""" - pass - - def stopSearch(self) -> None: - """停止搜索""" - pass - - def search(self, query: str) -> None: - """执行搜索""" - pass - - def setSearchableInfo(self, info: "SearchableInfo") -> None: - """设置可搜索信息""" - self._searchable_info[info.getSearchActivity() or ""] = info - - def getSearchableInfo(self, component_name: Any) -> Optional["SearchableInfo"]: - """获取可搜索信息""" - return self._searchable_info.get(str(component_name)) - - def getSuggestions(self, query: str, limit: int = 5) -> List[str]: - """获取建议""" - return [] - - def getSearchablesInGlobalSearch(self) -> List["SearchableInfo"]: - """获取全局搜索中的可搜索项""" - return list(self._searchable_info.values()) - - def setGlobalSearch(self, component_name: Any) -> None: - """设置全局搜索""" - pass - - def getGlobalSearchActivity(self) -> Optional[Any]: - """获取全局搜索活动""" - return self._global_search_activity - - def launchAssist(self, intent: Any) -> None: - """启动辅助""" - pass - - def launchQuery(self, query: str, component_name: Any, bundle: Any) -> None: - """启动查询""" - pass - - def getOnlineSearchEnabled(self) -> bool: - """检查在线搜索是否启用""" - return self._online_search_enabled - - def setOnlineSearchEnabled(self, enabled: bool) -> None: - """设置在线搜索启用""" - self._online_search_enabled = enabled - - def onDestroy(self) -> None: - """销毁""" - pass - - def dismissSearch(self) -> None: - """取消搜索""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# SearchableInfo -# ═══════════════════════════════════════════════════════════════ - -class SearchableInfo(Stub): - """SearchableInfo — 可搜索信息 (桌面存根)""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._suggest_authority = "" - self._suggest_package = "" - self._suggest_intent_action = "" - self._suggest_intent_data = "" - self._suggest_path = "" - self._suggest_selection = "" - self._suggest_threshold = 1 - self._input_type = 0x0001 - self._ime_options = 0 - self._action_key = 0 - self._action_key_text = "" - self._action_key_id = 0 - self._voice_search_launch_web_search = False - self._voice_search_launch_recognizer = False - self._settings_description = "" - self._search_activity = "" - self._search_hint = "" - self._search_label = "" - self._search_hint_id = 0 - self._search_label_id = 0 - self._description_id = 0 - self._settings_description_id = 0 - self._icon_id = 0 - self._use_browser_link = False - self._voice_search_enabled = True - self._query_after_zero_results = True - self._is_default = False - self._search_permissions: List[str] = [] - - def getSuggestAuthority(self) -> str: - return self._suggest_authority - - def getSuggestPackage(self) -> str: - return self._suggest_package - - def getSuggestIntentAction(self) -> str: - return self._suggest_intent_action - - def getSuggestIntentData(self) -> str: - return self._suggest_intent_data - - def getSuggestPath(self) -> str: - return self._suggest_path - - def getSuggestSelection(self) -> str: - return self._suggest_selection - - def getSuggestThreshold(self) -> int: - return self._suggest_threshold - - def getInputType(self) -> int: - return self._input_type - - def getImeOptions(self) -> int: - return self._ime_options - - def getActionKey(self) -> int: - return self._action_key - - def getActionKeyText(self) -> str: - return self._action_key_text - - def getActionKeyId(self) -> int: - return self._action_key_id - - def getVoiceSearchLaunchWebSearch(self) -> bool: - return self._voice_search_launch_web_search - - def getVoiceSearchLaunchRecognizer(self) -> bool: - return self._voice_search_launch_recognizer - - def getSettingsDescription(self) -> str: - return self._settings_description - - def getSearchActivity(self) -> str: - return self._search_activity - - def getSearchHint(self) -> str: - return self._search_hint - - def getSearchLabel(self) -> str: - return self._search_label - - def getSearchHintId(self) -> int: - return self._search_hint_id - - def getSearchLabelId(self) -> int: - return self._search_label_id - - def getDescriptionId(self) -> int: - return self._description_id - - def getSettingsDescriptionId(self) -> int: - return self._settings_description_id - - def getIconId(self) -> int: - return self._icon_id - - def getUseBrowserLink(self) -> bool: - return self._use_browser_link - - def isVoiceSearchEnabled(self) -> bool: - return self._voice_search_enabled - - def isQueryAfterZeroResults(self) -> bool: - return self._query_after_zero_results - - def isDefault(self) -> bool: - return self._is_default - - def getSearchSuggestPermission(self) -> str: - return "android.permission.SEARCH" - - def getSearchPermissions(self) -> List[str]: - return self._search_permissions.copy() - - def getSearchPermission(self, index: int) -> str: - if 0 <= index < len(self._search_permissions): - return self._search_permissions[index] - return "" - - -# ═══════════════════════════════════════════════════════════════ -# SearchView (and inner interfaces / classes) -# ═══════════════════════════════════════════════════════════════ - -class OnQueryTextListener(Stub): - """OnQueryTextListener — 查询文本监听器""" - - def __init__(self): - super().__init__() - - def onQueryTextSubmit(self, query: str) -> bool: - """查询文本提交""" - return False - - def onQueryTextChange(self, new_text: str) -> bool: - """查询文本变化""" - return False - - -class OnSuggestionListener(Stub): - """OnSuggestionListener — 建议监听器""" - - def __init__(self): - super().__init__() - - def onSuggestionSelect(self, position: int) -> bool: - """建议选中""" - return False - - def onSuggestionClick(self, position: int) -> bool: - """建议点击""" - return False - - -class OnCloseListener(Stub): - """OnCloseListener — 关闭监听器""" - - def __init__(self): - super().__init__() - - def onClose(self) -> bool: - """关闭事件""" - return False - - -class SearchViewSearchAutoComplete(Stub): - """SearchView.SearchAutoComplete — 搜索自动完成 (桌面存根)""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._threshold = 1 - self._text = "" - - def setThreshold(self, threshold: int) -> None: - """设置阈值""" - self._threshold = threshold - - def getThreshold(self) -> int: - """获取阈值""" - return self._threshold - - def enoughToFilter(self) -> bool: - """检查是否足够过滤""" - return len(self._text) >= self._threshold - - def setText(self, text: str) -> None: - self._text = text - - -class SearchView(Stub): - """SearchView — 搜索视图 (桌面存根)""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._query_hint = "" - self._query = "" - self._iconified = True - self._iconified_by_default = True - self._submit_button_enabled = True - self._query_refinement_enabled = True - self._max_width = 0 - self._input_type = 0x0001 - self._ime_options = 0 - self._searchable_info: Optional[SearchableInfo] = None - self._auto_complete = SearchViewSearchAutoComplete(context) - self._query_text_listener: Optional[OnQueryTextListener] = None - self._suggestion_listener: Optional[OnSuggestionListener] = None - self._close_listener: Optional[OnCloseListener] = None - - def setQueryHint(self, hint: str) -> None: - """设置查询提示""" - self._query_hint = hint - self._auto_complete.setText(hint) - - def setQuery(self, query: str, submit: bool) -> None: - """设置查询""" - self._query = query - if self._query_text_listener: - self._query_text_listener.onQueryTextChange(query) - if submit and self._query_text_listener: - self._query_text_listener.onQueryTextSubmit(query) - - def setIconified(self, iconified: bool) -> None: - """设置图标化""" - self._iconified = iconified - - def setIconifiedByDefault(self, iconified: bool) -> None: - """设置默认图标化""" - self._iconified_by_default = iconified - - def isIconifiedByDefault(self) -> bool: - """检查是否默认图标化""" - return self._iconified_by_default - - def setSubmitButtonEnabled(self, enabled: bool) -> None: - """设置提交按钮启用""" - self._submit_button_enabled = enabled - - def isSubmitButtonEnabled(self) -> bool: - """检查提交按钮是否启用""" - return self._submit_button_enabled - - def setQueryRefinementEnabled(self, enabled: bool) -> None: - """设置查询细化启用""" - self._query_refinement_enabled = enabled - - def isQueryRefinementEnabled(self) -> bool: - """检查查询细化是否启用""" - return self._query_refinement_enabled - - def setSearchableInfo(self, info: SearchableInfo) -> None: - """设置可搜索信息""" - self._searchable_info = info - - def onActionViewExpanded(self) -> None: - """操作视图展开""" - pass - - def onActionViewCollapsed(self) -> None: - """操作视图折叠""" - pass - - def onClose(self) -> None: - """关闭""" - if self._close_listener: - self._close_listener.onClose() - - def onQueryRefine(self, query: str) -> bool: - """查询细化""" - return False - - def onQueryTextChange(self, new_text: str) -> bool: - """查询文本变化""" - if self._query_text_listener: - return self._query_text_listener.onQueryTextChange(new_text) - return False - - def onQueryTextSubmit(self, query: str) -> bool: - """查询文本提交""" - if self._query_text_listener: - return self._query_text_listener.onQueryTextSubmit(query) - return False - - def onSuggestionSelect(self, position: int) -> bool: - """建议选中""" - if self._suggestion_listener: - return self._suggestion_listener.onSuggestionSelect(position) - return False - - def onSuggestionClick(self, position: int) -> bool: - """建议点击""" - if self._suggestion_listener: - return self._suggestion_listener.onSuggestionClick(position) - return False - - def onSuggestionsDisplayed(self, display: bool) -> None: - """建议显示""" - pass - - def getQuery(self) -> str: - """获取查询""" - return self._query - - def getCurrentQuery(self) -> str: - """获取当前查询""" - return self._query - - def getIconified(self) -> bool: - """获取图标化状态""" - return self._iconified - - def getMaxWidth(self) -> int: - """获取最大宽度""" - return self._max_width - - def setMaxWidth(self, max_width: int) -> None: - """设置最大宽度""" - self._max_width = max_width - - def getInputType(self) -> int: - """获取输入类型""" - return self._input_type - - def setInputType(self, input_type: int) -> None: - """设置输入类型""" - self._input_type = input_type - - def getImeOptions(self) -> int: - """获取 IME 选项""" - return self._ime_options - - def setImeOptions(self, ime_options: int) -> None: - """设置 IME 选项""" - self._ime_options = ime_options \ No newline at end of file diff --git a/android_pyapi/android/app_slice.py b/android_pyapi/android/app_slice.py deleted file mode 100644 index d61f780..0000000 --- a/android_pyapi/android/app_slice.py +++ /dev/null @@ -1,409 +0,0 @@ -""" -Android app.slice 组件 — Slice, SliceManager, SliceProvider, SliceSpec 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import uuid -from typing import Any, Dict, List, Optional, Set - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# SliceSpec -# ═══════════════════════════════════════════════════════════════ - -class SliceSpec(Stub): - """SliceSpec — Slice 规范""" - - def __init__(self, type_: str, revision: int = 1): - super().__init__() - self._type = type_ - self._revision = revision - - def getType(self) -> str: - return self._type - - def getRevision(self) -> int: - return self._revision - - def toString(self) -> str: - return f"SliceSpec{{type={self._type}, revision={self._revision}}}" - - def hashCode(self) -> int: - return hash((self._type, self._revision)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, SliceSpec): - return False - return self._type == other._type and self._revision == other._revision - - -# ═══════════════════════════════════════════════════════════════ -# SliceItem -# ═══════════════════════════════════════════════════════════════ - -class SliceItem(Stub): - """SliceItem — Slice 数据项""" - - FORMAT_TEXT = "text" - FORMAT_IMAGE = "image" - FORMAT_ACTION = "action" - FORMAT_INT = "int" - FORMAT_LONG = "long" - FORMAT_REMOTE_INPUT = "remote_input" - FORMAT_SLICE = "slice" - - def __init__(self, format_: str, hints: List[str], sub_type: str = ""): - super().__init__() - self._format = format_ - self._hints = list(hints) - self._sub_type = sub_type - self._text: Optional[str] = None - self._icon: Any = None - self._intent: Any = None - self._slice: Optional["Slice"] = None - self._remote_input: Any = None - self._action: Any = None - self._color: int = 0 - - def getFormat(self) -> str: - return self._format - - def getHints(self) -> List[str]: - return list(self._hints) - - def getSubType(self) -> str: - return self._sub_type - - def getText(self) -> Optional[str]: - return self._text - - def getIcon(self) -> Any: - return self._icon - - def getIntent(self) -> Any: - return self._intent - - def getSlice(self) -> Optional["Slice"]: - return self._slice - - def getRemoteInput(self) -> Any: - return self._remote_input - - def getAction(self) -> Any: - return self._action - - def getColor(self) -> int: - return self._color - - def toString(self) -> str: - return f"SliceItem{{format={self._format}, hints={self._hints}}}" - - -# ═══════════════════════════════════════════════════════════════ -# Slice -# ═══════════════════════════════════════════════════════════════ - -class Slice(Stub): - """Slice — 切片数据""" - - HINT_TITLE = "title" - HINT_SUBTITLE = "subtitle" - HINT_SUMMARY = "summary" - HINT_KEYWORDS = "keywords" - HINT_TTL = "ttl" - HINT_LAST_UPDATED = "last_updated" - HINT_PERMISSION = "permission" - HINT_SHORTCUT = "shortcut" - HINT_TOGGLE = "toggle" - HINT_SLIDER = "slider" - HINT_TINT = "tint" - HINT_LIST = "list" - HINT_GRID = "grid" - HINT_VERTICAL = "vertical" - HINT_HORIZONTAL = "horizontal" - HINT_PARTIAL = "partial" - HINT_SEE_MORE = "see_more" - HINT_NO_TINT = "no_tint" - HINT_SHOW_BACKGROUND = "show_background" - HINT_LARGE = "large" - HINT_ERROR = "error" - - def __init__(self, uri: str = "", hints: Optional[List[str]] = None, - spec: Optional[SliceSpec] = None): - super().__init__() - self._uri = uri - self._hints = hints or [] - self._items: List[SliceItem] = [] - self._spec = spec or SliceSpec("default") - self._calling_uid: int = -1 - self._calling_package: Optional[str] = None - - def getUri(self) -> str: - return self._uri - - def getHints(self) -> List[str]: - return list(self._hints) - - def getItems(self) -> List[SliceItem]: - return list(self._items) - - def getSpec(self) -> SliceSpec: - return self._spec - - def getCallingUid(self) -> int: - return self._calling_uid - - def getCallingPackage(self) -> Optional[str]: - return self._calling_package - - def getItem(self, format_: str) -> Optional[SliceItem]: - for item in self._items: - if item.getFormat() == format_: - return item - return None - - def toString(self) -> str: - return f"Slice{{uri={self._uri}, hints={self._hints}}}" - - class Builder(Stub): - """Slice.Builder — 构建器""" - - def __init__(self, uri: str, spec: SliceSpec): - super().__init__() - self._uri = uri - self._spec = spec - self._hints: List[str] = [] - self._items: List[SliceItem] = [] - self._caller_needed: bool = False - - def setCallerNeeded(self, needed: bool) -> "Slice.Builder": - self._caller_needed = needed - return self - - def addHints(self, hints: List[str]) -> "Slice.Builder": - self._hints.extend(hints) - return self - - def addSubSlice(self, sub_slice: Slice, hints: List[str]) -> "Slice.Builder": - item = SliceItem(SliceItem.FORMAT_SLICE, hints) - item._slice = sub_slice - self._items.append(item) - return self - - def addIcon(self, icon: Any, hints: List[str], sub_type: str = "") -> "Slice.Builder": - item = SliceItem(SliceItem.FORMAT_IMAGE, hints, sub_type) - item._icon = icon - self._items.append(item) - return self - - def addText(self, text: str, hints: List[str], sub_type: str = "") -> "Slice.Builder": - item = SliceItem(SliceItem.FORMAT_TEXT, hints, sub_type) - item._text = text - self._items.append(item) - return self - - def addAction(self, action: Any, hints: List[str], sub_type: str = "") -> "Slice.Builder": - item = SliceItem(SliceItem.FORMAT_ACTION, hints, sub_type) - item._action = action - self._items.append(item) - return self - - def addInt(self, value: int, hints: List[str], sub_type: str = "") -> "Slice.Builder": - item = SliceItem(SliceItem.FORMAT_INT, hints, sub_type) - item._text = str(value) - self._items.append(item) - return self - - def addLong(self, value: int, hints: List[str], sub_type: str = "") -> "Slice.Builder": - item = SliceItem(SliceItem.FORMAT_LONG, hints, sub_type) - item._text = str(value) - self._items.append(item) - return self - - def addRemoteInput(self, remote_input: Any, hints: List[str], - sub_type: str = "") -> "Slice.Builder": - item = SliceItem(SliceItem.FORMAT_REMOTE_INPUT, hints, sub_type) - item._remote_input = remote_input - self._items.append(item) - return self - - def build(self) -> "Slice": - s = Slice(self._uri, self._hints, self._spec) - s._items = list(self._items) - return s - - -# ═══════════════════════════════════════════════════════════════ -# ListBuilder -# ═══════════════════════════════════════════════════════════════ - -class ListBuilder(Stub): - """ListBuilder — Slice 列表构建器""" - - def __init__(self, uri: str, spec: SliceSpec): - super().__init__() - self._uri = uri - self._spec = spec - self._header_slice: Optional[Slice] = None - self._rows: List[Slice] = [] - self._grid_rows: List[Slice] = [] - self._input_ranges: List[Slice] = [] - self._sliders: List[Slice] = [] - self._ranges: List[Slice] = [] - self._select_toggles: List[Slice] = [] - - def setHeader(self, slice_: Slice) -> "ListBuilder": - self._header_slice = slice_ - return self - - def addRow(self, slice_: Slice) -> "ListBuilder": - self._rows.append(slice_) - return self - - def addGridRow(self, slice_: Slice) -> "ListBuilder": - self._grid_rows.append(slice_) - return self - - def addInputRange(self, slice_: Slice) -> "ListBuilder": - self._input_ranges.append(slice_) - return self - - def addSlider(self, slice_: Slice) -> "ListBuilder": - self._sliders.append(slice_) - return self - - def addRange(self, slice_: Slice) -> "ListBuilder": - self._ranges.append(slice_) - return self - - def addSelectToggle(self, slice_: Slice) -> "ListBuilder": - self._select_toggles.append(slice_) - return self - - def build(self) -> Slice: - return Slice(self._uri, ["list"], self._spec) - - def toString(self) -> str: - return f"ListBuilder{{uri={self._uri}}}" - - -# ═══════════════════════════════════════════════════════════════ -# SliceManager -# ═══════════════════════════════════════════════════════════════ - -class SliceManager(Stub): - """SliceManager — Slice 管理器""" - - def __init__(self): - super().__init__() - self._pinned_slices: Dict[str, Slice] = {} - self._permissions: Dict[str, Set[str]] = {} - - def bindSlice(self, uri: str, specs: Set[SliceSpec]) -> Optional[Slice]: - return self._pinned_slices.get(uri) - - def bindSliceForIntent(self, intent: Any, specs: Set[SliceSpec]) -> Optional[Slice]: - return None - - def mapIntentToUri(self, intent: Any) -> Optional[str]: - return None - - def grantSlicePermission(self, toPackage: str, uri: str): - if uri not in self._permissions: - self._permissions[uri] = set() - self._permissions[uri].add(toPackage) - - def revokeSlicePermission(self, toPackage: str, uri: str): - if uri in self._permissions: - self._permissions[uri].discard(toPackage) - - def checkSlicePermission(self, uri: str, pid: int, uid: int) -> int: - return 0 # PERMISSION_GRANTED - - def pinSlice(self, uri: str, specs: Set[SliceSpec]) -> Optional[Slice]: - slice_ = self._pinned_slices.get(uri) - if slice_: - return slice_ - s = Slice(uri, spec=list(specs)[0] if specs else None) - self._pinned_slices[uri] = s - return s - - def unpinSlice(self, uri: str): - self._pinned_slices.pop(uri, None) - - def getPinnedSlices(self) -> List[Slice]: - return list(self._pinned_slices.values()) - - def getSliceDescendants(self, uri: str) -> List[str]: - return [] - - def getSlice(self, uri: str) -> Optional[Slice]: - return self._pinned_slices.get(uri) - - def getAllowedPackages(self) -> List[str]: - return [] - - def slicePinUser(self, uri: str) -> int: - return 0 - - def getPinnedSliceSpecs(self, uri: str) -> Set[SliceSpec]: - return set() - - def toString(self) -> str: - return f"SliceManager@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# SliceProvider -# ═══════════════════════════════════════════════════════════════ - -class SliceProvider(Stub): - """SliceProvider — Slice 提供者""" - - def __init__(self): - super().__init__() - - def onCreateSliceProvider(self) -> bool: - return True - - def onBindSlice(self, sliceUri: str, specs: Set[SliceSpec]) -> Optional[Slice]: - return None - - def onSlicePinned(self, sliceUri: str): - pass - - def onSliceUnpinned(self, sliceUri: str): - pass - - def onMapIntentToUri(self, intent: Any) -> Optional[str]: - return None - - def handleSliceRequest(self, uri: str, specs: Set[SliceSpec]) -> Optional[Slice]: - return self.onBindSlice(uri, specs) - - def getCallingPackage(self) -> Optional[str]: - return None - - def toString(self) -> str: - return f"SliceProvider@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Slice", - "SliceManager", - "SliceProvider", - "SliceSpec", - "SliceItem", - "ListBuilder", -] \ No newline at end of file diff --git a/android_pyapi/android/app_tasks.py b/android_pyapi/android/app_tasks.py deleted file mode 100644 index eb8680a..0000000 --- a/android_pyapi/android/app_tasks.py +++ /dev/null @@ -1,969 +0,0 @@ -""" -Android app.tasks 包 — TaskManager, TaskInfo, TaskStackListener, -ActivityManager 任务/进程/内存信息等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import sys -import time -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError - -try: - from .._linux_impl import IS_LINUX, LinuxProcess, LinuxPower - _USE_LINUX = IS_LINUX -except ImportError: - _USE_LINUX = False - -__all__ = [ - "TaskManager", "TaskInfo", "TaskStackListener", "TaskSnapshot", - "TaskDescription", "ActivityManager", -] - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -PROCESS_STATE_UNKNOWN = -1 -PROCESS_STATE_PERSISTENT = 0 -PROCESS_STATE_PERSISTENT_UI = 1 -PROCESS_STATE_TOP = 2 -PROCESS_STATE_BOUND_TOP = 3 -PROCESS_STATE_FOREGROUND_SERVICE = 4 -PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 5 -PROCESS_STATE_IMPORTANT_FOREGROUND = 6 -PROCESS_STATE_IMPORTANT_BACKGROUND = 7 -PROCESS_STATE_TRANSIENT_BACKGROUND = 8 -PROCESS_STATE_BACKUP = 9 -PROCESS_STATE_SERVICE = 10 -PROCESS_STATE_RECEIVER = 11 -PROCESS_STATE_TOP_SLEEPING = 12 -PROCESS_STATE_HEAVY_WEIGHT = 13 -PROCESS_STATE_HOME = 14 -PROCESS_STATE_LAST_ACTIVITY = 15 -PROCESS_STATE_CACHED_ACTIVITY = 16 -PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 17 -PROCESS_STATE_CACHED_EMPTY = 18 - -IMPORTANCE_FOREGROUND = 100 -IMPORTANCE_FOREGROUND_SERVICE = 125 -IMPORTANCE_VISIBLE = 200 -IMPORTANCE_SERVICE = 300 -IMPORTANCE_CACHED = 400 -IMPORTANCE_CANT_SAVE_STATE = 170 -IMPORTANCE_GONE = 1000 -IMPORTANCE_BACKGROUND = 500 -IMPORTANCE_TOP_SLEEPING = 325 -IMPORTANCE_HAS_PREVIOUS = 650 - -TASK_ID_NONE = -1 -TASK_LOCK_STATE_UNLOCKED = 0 -TASK_LOCK_STATE_LOCKED = 1 -TASK_LOCK_STATE_PINNED = 2 - -TASK_NON_FULLSCREEN = 0 -TASK_FULLSCREEN = 1 -TASK_FLOATING = 2 -TASK_SPLIT_SCREEN_PRIMARY = 3 -TASK_SPLIT_SCREEN_SECONDARY = 4 -TASK_FREEFORM = 5 - - -# ═══════════════════════════════════════════════════════════════ -# TaskDescription -# ═══════════════════════════════════════════════════════════════ - -class TaskDescription(Stub): - """TaskDescription — 任务描述""" - - def __init__(self, label: str = "", icon: Any = None, colorPrimary: int = 0, - colorBackground: int = 0, statusBarColor: int = 0, - navigationBarColor: int = 0): - super().__init__() - self._label = label - self._icon = icon - self._color_primary = colorPrimary - self._color_background = colorBackground - self._status_bar_color = statusBarColor - self._navigation_bar_color = navigationBarColor - - def getLabel(self) -> str: - return self._label - - def getIcon(self) -> Optional[Any]: - return self._icon - - def getPrimaryColor(self) -> int: - return self._color_primary - - def getBackgroundColor(self) -> int: - return self._color_background - - def getStatusBarColor(self) -> int: - return self._status_bar_color - - def getNavigationBarColor(self) -> int: - return self._navigation_bar_color - - def toString(self) -> str: - return f"TaskDescription(label={self._label})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, TaskDescription): - return False - return self._label == other._label - - def hashCode(self) -> int: - return hash(self._label) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# TaskSnapshot -# ═══════════════════════════════════════════════════════════════ - -class TaskSnapshot(Stub): - """TaskSnapshot — 任务快照""" - - def __init__(self, taskId: int = 0, snapshot: Any = None, - width: int = 0, height: int = 0, - isRealSnapshot: bool = True, - isTranslucent: bool = False, - isPrivate: bool = False, - topActivityComponent: str = "", - snapshotScale: float = 1.0): - super().__init__() - self._task_id = taskId - self._snapshot = snapshot - self._width = width - self._height = height - self._is_real_snapshot = isRealSnapshot - self._is_translucent = isTranslucent - self._is_private = isPrivate - self._top_activity = topActivityComponent - self._snapshot_scale = snapshotScale - - def getTaskId(self) -> int: - return self._task_id - - def getSnapshot(self) -> Any: - return self._snapshot - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def isRealSnapshot(self) -> bool: - return self._is_real_snapshot - - def isTranslucent(self) -> bool: - return self._is_translucent - - def isPrivate(self) -> bool: - return self._is_private - - def getTopActivityComponent(self) -> str: - return self._top_activity - - def getSnapshotScale(self) -> float: - return self._snapshot_scale - - def toString(self) -> str: - return f"TaskSnapshot(task={self._task_id}, size={self._width}x{self._height})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, TaskSnapshot): - return False - return self._task_id == other._task_id - - def hashCode(self) -> int: - return self._task_id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# TaskInfo -# ═══════════════════════════════════════════════════════════════ - -class TaskInfo(Stub): - """TaskInfo — 任务信息""" - - def __init__(self, taskId: int = 0, baseIntent: Any = None, - baseActivity: Any = None, topActivity: Any = None, - origActivity: Any = None, description: Any = None, - numActivities: int = 0, numRunning: int = 0, - isRunning: bool = False, supportsSplitScreen: bool = False, - resizeMode: int = TASK_FULLSCREEN, - userId: int = 0, displayId: int = 0, - isVisible: bool = False, isPersistable: bool = True, - lockState: int = TASK_LOCK_STATE_UNLOCKED, - isRooted: bool = False, - pictureInPictureEnabled: bool = False): - super().__init__() - self._task_id = taskId - self._base_intent = baseIntent - self._base_activity = baseActivity - self._top_activity = topActivity - self._orig_activity = origActivity - self._description = description or TaskDescription() - self._num_activities = numActivities - self._num_running = numRunning - self._is_running = isRunning - self._supports_split_screen = supportsSplitScreen - self._resize_mode = resizeMode - self._user_id = userId - self._display_id = displayId - self._is_visible = isVisible - self._is_persistable = isPersistable - self._lock_state = lockState - self._is_rooted = isRooted - self._pip_enabled = pictureInPictureEnabled - - def getTaskId(self) -> int: - return self._task_id - - def getBaseIntent(self) -> Any: - return self._base_intent - - def getBaseActivity(self) -> Any: - return self._base_activity - - def getTopActivity(self) -> Any: - return self._top_activity - - def getOrigActivity(self) -> Any: - return self._orig_activity - - def getDescription(self) -> Any: - return self._description - - def getNumActivities(self) -> int: - return self._num_activities - - def getNumRunning(self) -> int: - return self._num_running - - def isRunning(self) -> bool: - return self._is_running - - def supportsSplitScreen(self) -> bool: - return self._supports_split_screen - - def getResizeMode(self) -> int: - return self._resize_mode - - def getUserId(self) -> int: - return self._user_id - - def getDisplayId(self) -> int: - return self._display_id - - def isVisible(self) -> bool: - return self._is_visible - - def isPersistable(self) -> bool: - return self._is_persistable - - def getLockState(self) -> int: - return self._lock_state - - def isRooted(self) -> bool: - return self._is_rooted - - def isPictureInPictureEnabled(self) -> bool: - return self._pip_enabled - - def toString(self) -> str: - return (f"TaskInfo(id={self._task_id}, " - f"activities={self._num_activities}, " - f"running={self._is_running})") - - def equals(self, other: Any) -> bool: - if not isinstance(other, TaskInfo): - return False - return self._task_id == other._task_id - - def hashCode(self) -> int: - return self._task_id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# TaskStackListener -# ═══════════════════════════════════════════════════════════════ - -class TaskStackListener(Stub): - """TaskStackListener — 任务栈监听器""" - - def onTaskStackChanged(self): - pass - - def onTaskCreated(self, taskId: int, componentName: Any): - pass - - def onTaskRemoved(self, taskId: int): - pass - - def onTaskMovedToFront(self, taskInfo: TaskInfo): - pass - - def onTaskMovedToBack(self, taskInfo: TaskInfo): - pass - - def onTaskDescriptionChanged(self, taskInfo: TaskInfo, - taskDescription: TaskDescription): - pass - - def onTaskSnapshotChanged(self, taskId: int, snapshot: TaskSnapshot): - pass - - def onTaskDisplayChanged(self, taskId: int, newDisplayId: int): - pass - - def onTaskProfileLocked(self, taskId: int, userId: int): - pass - - def onRecentTaskListUpdated(self): - pass - - def toString(self) -> str: - return f"TaskStackListener@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# TaskManager -# ═══════════════════════════════════════════════════════════════ - -class TaskManager(Stub): - """TaskManager — 任务管理器""" - - def __init__(self): - super().__init__() - self._tasks: Dict[int, TaskInfo] = {} - self._listeners: Dict[int, TaskStackListener] = {} - self._snapshots: Dict[int, TaskSnapshot] = {} - self._next_id = 100 - - def getTasks(self, maxNum: int = 10) -> List[TaskInfo]: - return list(self._tasks.values())[:maxNum] - - def getTaskInfo(self, taskId: int) -> Optional[TaskInfo]: - return self._tasks.get(taskId) - - def getRecentTasks(self, maxNum: int = 10, flags: int = 0) -> List[TaskInfo]: - return list(self._tasks.values())[:maxNum] - - def addTask(self, taskInfo: TaskInfo): - self._tasks[taskInfo.getTaskId()] = taskInfo - - def removeTask(self, taskId: int): - self._tasks.pop(taskId, None) - - def moveTaskToFront(self, taskId: int, flags: int = 0): - pass - - def moveTaskToBack(self, taskId: int): - pass - - def cancelTaskWindowTransition(self, taskId: int): - pass - - def setTaskDescription(self, taskId: int, description: TaskDescription): - task = self._tasks.get(taskId) - if task: - self._tasks[taskId] = task - - def takeTaskSnapshot(self, taskId: int) -> Optional[TaskSnapshot]: - return self._snapshots.get(taskId) - - def registerTaskStackListener(self, listener: TaskStackListener): - lid = id(listener) - self._listeners[lid] = listener - - def unregisterTaskStackListener(self, listener: TaskStackListener): - lid = id(listener) - self._listeners.pop(lid, None) - - def createTask(self, componentName: str) -> int: - task_id = self._next_id - self._next_id += 1 - task = TaskInfo(taskId=task_id, baseActivity=componentName, isRunning=True) - self._tasks[task_id] = task - return task_id - - def getTaskCount(self) -> int: - return len(self._tasks) - - def getRunningTaskCount(self) -> int: - return sum(1 for t in self._tasks.values() if t.isRunning()) - - def toString(self) -> str: - return f"TaskManager(tasks={len(self._tasks)})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# ActivityManager 任务/进程/内存信息内部类 -# ═══════════════════════════════════════════════════════════════ - -class ActivityManager(Stub): - """ActivityManager — 活动管理器 (任务/进程/内存相关内部类)""" - - RECENT_IGNORE_UNAVAILABLE = 0x00000002 - RECENT_WITH_EXCLUDED = 0x00000001 - RECENT_INCLUDE_PROFILES = 0x00000004 - RECENT_INGORE_DOCKED_STACK_TOP = 0x00000008 - RECENT_INGORE_PINNED_STACK = 0x00000010 - - START_SUCCESS = 0 - START_INTENT_NOT_RESOLVED = -1 - START_CLASS_NOT_FOUND = -2 - START_FORWARD_AND_REQUEST_CONFLICT = -3 - START_PERMISSION_DENIED = -4 - START_NOT_ACTIVITY = -5 - START_NOT_VOICE_COMPATIBLE = -6 - START_NOT_CURRENT_USER = -7 - START_RETURN_LOCK_ERROR = -8 - START_LOCK_ERROR = -9 - START_SWITCHES_CANCELED = -10 - START_ABORTED = -11 - - MOVETASK_WITH_HOME = 0x00000001 - MOVETASK_NO_ANIMATION = 0x00000002 - - # ═══════════════════════════════════════════════════════════ - # RecentTaskInfo - # ═══════════════════════════════════════════════════════════ - - class RecentTaskInfo(Stub): - """ActivityManager.RecentTaskInfo — 最近任务信息""" - - def __init__(self, taskId: int = 0, baseIntent: Any = None, - origActivity: Any = None, description: Any = None, - userId: int = 0, persistentId: int = 0, - affiliatedTaskId: int = 0, affiliatedTaskColor: int = 0, - isExcluded: bool = False, isDockable: bool = True, - resizeMode: int = TASK_FULLSCREEN, - topActivity: Any = None, baseActivity: Any = None): - super().__init__() - self._task_id = taskId - self._base_intent = baseIntent - self._orig_activity = origActivity - self._description = description - self._user_id = userId - self._persistent_id = persistentId - self._affiliated_task_id = affiliatedTaskId - self._affiliated_task_color = affiliatedTaskColor - self._is_excluded = isExcluded - self._is_dockable = isDockable - self._resize_mode = resizeMode - self._top_activity = topActivity - self._base_activity = baseActivity - - def getTaskId(self) -> int: - return self._task_id - - def getBaseIntent(self) -> Any: - return self._base_intent - - def getOrigActivity(self) -> Any: - return self._orig_activity - - def getDescription(self) -> Any: - return self._description - - def getUserId(self) -> int: - return self._user_id - - def getPersistentId(self) -> int: - return self._persistent_id - - def getAffiliatedTaskId(self) -> int: - return self._affiliated_task_id - - def getAffiliatedTaskColor(self) -> int: - return self._affiliated_task_color - - def isExcluded(self) -> bool: - return self._is_excluded - - def isDockable(self) -> bool: - return self._is_dockable - - def getResizeMode(self) -> int: - return self._resize_mode - - def getTopActivity(self) -> Any: - return self._top_activity - - def getBaseActivity(self) -> Any: - return self._base_activity - - def toString(self) -> str: - return f"RecentTaskInfo(id={self._task_id})" - - # ═══════════════════════════════════════════════════════════ - # AppTask - # ═══════════════════════════════════════════════════════════ - - class AppTask(Stub): - """ActivityManager.AppTask — 应用任务""" - - def __init__(self, taskInfo: Optional[RecentTaskInfo] = None): - super().__init__() - self._task_info = taskInfo - - def getTaskInfo(self) -> Optional["ActivityManager.RecentTaskInfo"]: - return self._task_info - - def moveToFront(self): - pass - - def startActivity(self, intent: Any, options: Any = None): - pass - - def setExcludeFromRecents(self, exclude: bool): - pass - - def finishAndRemoveTask(self): - pass - - def setTaskDescription(self, description: TaskDescription): - pass - - def toString(self) -> str: - return f"AppTask(info={self._task_info})" - - # ═══════════════════════════════════════════════════════════ - # StackInfo - # ═══════════════════════════════════════════════════════════ - - class StackInfo(Stub): - """ActivityManager.StackInfo — 栈信息""" - - def __init__(self, stackId: int = 0, bounds: Any = None, - taskIds: List[int] = None, taskNames: List[str] = None, - displayId: int = 0, topActivity: Any = None, - visible: bool = False, position: int = 0, - isWindowingMode: int = TASK_FULLSCREEN): - super().__init__() - self._stack_id = stackId - self._bounds = bounds - self._task_ids = taskIds or [] - self._task_names = taskNames or [] - self._display_id = displayId - self._top_activity = topActivity - self._visible = visible - self._position = position - self._windowing_mode = isWindowingMode - - def getStackId(self) -> int: - return self._stack_id - - def getBounds(self) -> Any: - return self._bounds - - def getTaskIds(self) -> List[int]: - return list(self._task_ids) - - def getTaskNames(self) -> List[str]: - return list(self._task_names) - - def getDisplayId(self) -> int: - return self._display_id - - def getTopActivity(self) -> Any: - return self._top_activity - - def isVisible(self) -> bool: - return self._visible - - def getPosition(self) -> int: - return self._position - - def getWindowingMode(self) -> int: - return self._windowing_mode - - def toString(self) -> str: - return f"StackInfo(id={self._stack_id}, tasks={len(self._task_ids)})" - - # ═══════════════════════════════════════════════════════════ - # RunningTaskInfo - # ═══════════════════════════════════════════════════════════ - - class RunningTaskInfo(Stub): - """ActivityManager.RunningTaskInfo — 运行中任务信息""" - - def __init__(self, taskId: int = 0, baseActivity: Any = None, - topActivity: Any = None, numActivities: int = 0, - numRunning: int = 0, description: Any = None, - thumbnail: Any = None, isRunning: bool = True): - super().__init__() - self._task_id = taskId - self._base_activity = baseActivity - self._top_activity = topActivity - self._num_activities = numActivities - self._num_running = numRunning - self._description = description - self._thumbnail = thumbnail - self._is_running = isRunning - - def getTaskId(self) -> int: - return self._task_id - - def getBaseActivity(self) -> Any: - return self._base_activity - - def getTopActivity(self) -> Any: - return self._top_activity - - def getNumActivities(self) -> int: - return self._num_activities - - def getNumRunning(self) -> int: - return self._num_running - - def getDescription(self) -> Any: - return self._description - - def getThumbnail(self) -> Any: - return self._thumbnail - - def isRunning(self) -> bool: - return self._is_running - - def toString(self) -> str: - return f"RunningTaskInfo(id={self._task_id}, activities={self._num_activities})" - - # ═══════════════════════════════════════════════════════════ - # RunningServiceInfo - # ═══════════════════════════════════════════════════════════ - - class RunningServiceInfo(Stub): - """ActivityManager.RunningServiceInfo — 运行中服务信息""" - - def __init__(self, service: Any = None, process: str = "", - uid: int = 0, pid: int = 0, clientCount: int = 0, - clientLabel: int = 0, clientPackage: str = "", - crashCount: int = 0, flags: int = 0, - started: bool = False, foreground: bool = False, - restarting: bool = False): - super().__init__() - self._service = service - self._process = process - self._uid = uid - self._pid = pid - self._client_count = clientCount - self._client_label = clientLabel - self._client_package = clientPackage - self._crash_count = crashCount - self._flags = flags - self._started = started - self._foreground = foreground - self._restarting = restarting - - def getService(self) -> Any: - return self._service - - def getProcess(self) -> str: - return self._process - - def getUid(self) -> int: - return self._uid - - def getPid(self) -> int: - return self._pid - - def getClientCount(self) -> int: - return self._client_count - - def getClientLabel(self) -> int: - return self._client_label - - def getClientPackage(self) -> str: - return self._client_package - - def getCrashCount(self) -> int: - return self._crash_count - - def getFlags(self) -> int: - return self._flags - - def isStarted(self) -> bool: - return self._started - - def isForeground(self) -> bool: - return self._foreground - - def isRestarting(self) -> bool: - return self._restarting - - def toString(self) -> str: - return f"RunningServiceInfo(service={self._service}, pid={self._pid})" - - # ═══════════════════════════════════════════════════════════ - # RunningAppProcessInfo - # ═══════════════════════════════════════════════════════════ - - class RunningAppProcessInfo(Stub): - """ActivityManager.RunningAppProcessInfo — 运行中应用进程信息""" - - IMPORTANCE_FOREGROUND = IMPORTANCE_FOREGROUND - IMPORTANCE_FOREGROUND_SERVICE = IMPORTANCE_FOREGROUND_SERVICE - IMPORTANCE_VISIBLE = IMPORTANCE_VISIBLE - IMPORTANCE_SERVICE = IMPORTANCE_SERVICE - IMPORTANCE_CACHED = IMPORTANCE_CACHED - IMPORTANCE_GONE = IMPORTANCE_GONE - IMPORTANCE_BACKGROUND = IMPORTANCE_BACKGROUND - IMPORTANCE_TOP_SLEEPING = IMPORTANCE_TOP_SLEEPING - - REASON_UNKNOWN = 0 - REASON_PROVIDER_IN_USE = 1 - REASON_SERVICE_IN_USE = 2 - REASON_BACKGROUND = 3 - REASON_CRASH = 4 - REASON_ANR = 5 - REASON_LOW_MEMORY = 6 - REASON_USER_STOPPED = 7 - REASON_PERMISSION_CHANGE = 8 - REASON_ABORTED = 9 - - def __init__(self, processName: str = "", pid: int = 0, - uid: int = 0, pkgList: List[str] = None, - importance: int = IMPORTANCE_FOREGROUND, - importanceReasonCode: int = REASON_UNKNOWN, - importanceReasonPid: int = 0, - importanceReasonComponent: Any = None, - lastActivityTime: int = 0, - lru: int = 0): - super().__init__() - self.processName = processName - self.pid = pid - self.uid = uid - self.pkgList = pkgList or [] - self.importance = importance - self.importanceReasonCode = importanceReasonCode - self.importanceReasonPid = importanceReasonPid - self.importanceReasonComponent = importanceReasonComponent - self.lastActivityTime = lastActivityTime - self.lru = lru - - def getProcessName(self) -> str: - return self.processName - - def getPid(self) -> int: - return self.pid - - def getUid(self) -> int: - return self.uid - - def getPackageList(self) -> List[str]: - return list(self.pkgList) - - def getImportance(self) -> int: - return self.importance - - def getImportanceReasonCode(self) -> int: - return self.importanceReasonCode - - def getImportanceReasonPid(self) -> int: - return self.importanceReasonPid - - def getImportanceReasonComponent(self) -> Any: - return self.importanceReasonComponent - - def toString(self) -> str: - return (f"RunningAppProcessInfo(process={self.processName}, " - f"pid={self.pid}, importance={self.importance})") - - # ═══════════════════════════════════════════════════════════ - # ProcessErrorStateInfo - # ═══════════════════════════════════════════════════════════ - - class ProcessErrorStateInfo(Stub): - """ActivityManager.ProcessErrorStateInfo — 进程错误状态信息""" - - CRASHED = 1 - NOT_RESPONDING = 2 - NO_ERROR = 0 - - def __init__(self, condition: int = NO_ERROR, processName: str = "", - pid: int = 0, uid: int = 0, tag: str = "", - shortMessage: str = "", longMessage: str = "", - crashedApp: str = "", - stackTrace: str = ""): - super().__init__() - self._condition = condition - self._process_name = processName - self._pid = pid - self._uid = uid - self._tag = tag - self._short_message = shortMessage - self._long_message = longMessage - self._crash_app = crashedApp - self._stack_trace = stackTrace - - def getCondition(self) -> int: - return self._condition - - def getProcessName(self) -> str: - return self._process_name - - def getPid(self) -> int: - return self._pid - - def getUid(self) -> int: - return self._uid - - def getTag(self) -> str: - return self._tag - - def getShortMessage(self) -> str: - return self._short_message - - def getLongMessage(self) -> str: - return self._long_message - - def getCrashApp(self) -> str: - return self._crash_app - - def getStackTrace(self) -> str: - return self._stack_trace - - def toString(self) -> str: - return (f"ProcessErrorStateInfo(condition={self._condition}, " - f"process={self._process_name})") - - # ═══════════════════════════════════════════════════════════ - # MemoryInfo - # ═══════════════════════════════════════════════════════════ - - class MemoryInfo(Stub): - """ActivityManager.MemoryInfo — 内存信息""" - - def __init__(self): - super().__init__() - self.availMem: int = 0 - self.totalMem: int = 0 - self.threshold: int = 0 - self.lowMemory: bool = False - self.hiddenAppThreshold: int = 0 - self.secondaryServerThreshold: int = 0 - self.visibleAppThreshold: int = 0 - self.foregroundAppThreshold: int = 0 - - def getAvailMem(self) -> int: - return self.availMem - - def getTotalMem(self) -> int: - return self.totalMem - - def getThreshold(self) -> int: - return self.threshold - - def isLowMemory(self) -> bool: - return self.lowMemory - - def getHiddenAppThreshold(self) -> int: - return self.hiddenAppThreshold - - def getSecondaryServerThreshold(self) -> int: - return self.secondaryServerThreshold - - def getVisibleAppThreshold(self) -> int: - return self.visibleAppThreshold - - def getForegroundAppThreshold(self) -> int: - return self.foregroundAppThreshold - - def toString(self) -> str: - avail_mb = self.availMem / (1024 * 1024) - total_mb = self.totalMem / (1024 * 1024) - return (f"MemoryInfo(avail={avail_mb:.0f}MB, " - f"total={total_mb:.0f}MB, " - f"lowMemory={self.lowMemory})") - - def __init__(self): - super().__init__() - self._tasks: Dict[int, ActivityManager.RecentTaskInfo] = {} - self._processes: Dict[int, ActivityManager.RunningAppProcessInfo] = {} - self._services: Dict[int, ActivityManager.RunningServiceInfo] = {} - - def getRecentTasks(self, maxNum: int, flags: int) -> List[RecentTaskInfo]: - return list(self._tasks.values())[:maxNum] - - def getRunningTasks(self, maxNum: int) -> List[RunningTaskInfo]: - return [] - - def getRunningServices(self, maxNum: int) -> List[RunningServiceInfo]: - return list(self._services.values())[:maxNum] - - def getRunningAppProcesses(self) -> List[RunningAppProcessInfo]: - if _USE_LINUX: - return LinuxProcess.list_processes() - return list(self._processes.values()) - - def getMemoryInfo(self, outInfo: MemoryInfo): - if _USE_LINUX: - mem = LinuxPower.get_memory_info() - outInfo.availMem = mem.get("available", 0) - outInfo.totalMem = mem.get("total", 0) - outInfo.threshold = int(mem.get("total", 0) * 0.1) - outInfo.lowMemory = mem.get("percent", 0) > 90.0 - return outInfo - import psutil - try: - import psutil - mem = psutil.virtual_memory() - outInfo.availMem = mem.available - outInfo.totalMem = mem.total - outInfo.threshold = int(mem.total * 0.1) - outInfo.lowMemory = mem.percent > 90.0 - except ImportError: - outInfo.totalMem = 8 * 1024 * 1024 * 1024 - outInfo.availMem = 4 * 1024 * 1024 * 1024 - outInfo.threshold = outInfo.totalMem // 10 - - def getMyMemoryState(self, outInfo: RunningAppProcessInfo): - outInfo.uid = os.getuid() - outInfo.pid = os.getpid() - outInfo.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND - - def isLowRamDevice(self) -> bool: - return False - - def clearApplicationUserData(self) -> bool: - return False - - def killBackgroundProcesses(self, packageName: str): - pass - - def restartPackage(self, packageName: str): - pass - - def getProcessMemoryInfo(self, pids: List[int]) -> List[Any]: - return [] - - def toString(self) -> str: - return f"ActivityManager(tasks={len(self._tasks)}, processes={len(self._processes)})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/app_time.py b/android_pyapi/android/app_time.py deleted file mode 100644 index e03cbf3..0000000 --- a/android_pyapi/android/app_time.py +++ /dev/null @@ -1,181 +0,0 @@ -""" -Android app.time 组件 — TimeManager, TimeZoneCapabilities, TimeConfiguration 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import datetime -import time as _time -from typing import Any, Dict, List, Optional, Set - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# TimeConfiguration -# ═══════════════════════════════════════════════════════════════ - -class TimeConfiguration(Stub): - """TimeConfiguration — 时间配置""" - - def __init__(self): - super().__init__() - self._override_enabled: bool = False - self._override_time_zone_ids: List[str] = [] - - def setOverrideEnabled(self, enabled: bool): - self._override_enabled = enabled - - def isOverrideEnabled(self) -> bool: - return self._override_enabled - - def getOverrideTimeZoneIds(self) -> List[str]: - return list(self._override_time_zone_ids) - - def setOverrideTimeZoneIds(self, ids: List[str]): - self._override_time_zone_ids = list(ids) - - def toString(self) -> str: - return f"TimeConfiguration{{overrideEnabled={self._override_enabled}}}" - - -# ═══════════════════════════════════════════════════════════════ -# TimeZoneCapabilities -# ═══════════════════════════════════════════════════════════════ - -class TimeZoneCapabilities(Stub): - """TimeZoneCapabilities — 时区能力""" - - CAPABILITY_NOT_SUPPORTED = 0 - CAPABILITY_NOT_ALLOWED = 1 - CAPABILITY_ALLOWED = 2 - - def __init__(self): - super().__init__() - self._auto_detection_capability: int = self.CAPABILITY_ALLOWED - self._manual_detection_capability: int = self.CAPABILITY_ALLOWED - self._configure_auto_detection_capability: int = self.CAPABILITY_ALLOWED - - def getAutoDetectionCapability(self) -> int: - return self._auto_detection_capability - - def getManualDetectionCapability(self) -> int: - return self._manual_detection_capability - - def isAutoDetectionSupported(self) -> bool: - return True - - def isManualDetectionSupported(self) -> bool: - return True - - def toString(self) -> str: - return (f"TimeZoneCapabilities{{autoDetection={self._auto_detection_capability}, " - f"manualDetection={self._manual_detection_capability}}}") - - -# ═══════════════════════════════════════════════════════════════ -# TimeZoneCapabilitiesAndConfig -# ═══════════════════════════════════════════════════════════════ - -class TimeZoneCapabilitiesAndConfig(Stub): - """TimeZoneCapabilitiesAndConfig — 时区能力与配置""" - - def __init__(self): - super().__init__() - self._capabilities = TimeZoneCapabilities() - self._configuration = TimeConfiguration() - - def getCapabilities(self) -> TimeZoneCapabilities: - return self._capabilities - - def getConfiguration(self) -> TimeConfiguration: - return self._configuration - - def toString(self) -> str: - return (f"TimeZoneCapabilitiesAndConfig{{capabilities={self._capabilities}, " - f"config={self._configuration}}}") - - -# ═══════════════════════════════════════════════════════════════ -# TimeManager -# ═══════════════════════════════════════════════════════════════ - -class TimeManager(Stub): - """TimeManager — 时间管理器""" - - EXTRA_SUGGESTION = "suggestion" - EXTRA_RESULT = "result" - - RESULT_OK = 0 - RESULT_ERROR = -1 - RESULT_NOT_ALLOWED = -2 - RESULT_SKIPPED = -3 - - def __init__(self): - super().__init__() - - # ── Time zone ── - - def getTimeZoneCapabilitiesAndConfig(self) -> TimeZoneCapabilitiesAndConfig: - return TimeZoneCapabilitiesAndConfig() - - def getDetectedTimeZoneSource(self) -> int: - return 0 - - def suggestExternalTimeZone(self, suggestion: Any) -> int: - return self.RESULT_OK - - def suggestExternalTime(self, suggestion: Any) -> int: - return self.RESULT_OK - - def confirmTimeChange(self, confirmation: Any) -> int: - return self.RESULT_OK - - # ── Time / Time zone setting ── - - def isAutoTimeEnabled(self) -> bool: - return True - - def isAutoTimeZoneEnabled(self) -> bool: - return True - - def setAutoTimeEnabled(self, enabled: bool): - pass - - def setAutoTimeZoneEnabled(self, enabled: bool): - pass - - def setTime(self, millis: int): - try: - dt = datetime.datetime.fromtimestamp(millis / 1000.0) - _time.mktime(dt.timetuple()) - except (OSError, ValueError): - pass - - def setTimeZone(self, timeZone: str): - pass - - def updateTime(self, time: Any): - pass - - def updateTimeZone(self, timeZone: Any): - pass - - def toString(self) -> str: - return f"TimeManager@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "TimeManager", - "TimeZoneCapabilities", - "TimeZoneCapabilitiesAndConfig", - "TimeConfiguration", -] \ No newline at end of file diff --git a/android_pyapi/android/app_usage.py b/android_pyapi/android/app_usage.py deleted file mode 100644 index 8d6b150..0000000 --- a/android_pyapi/android/app_usage.py +++ /dev/null @@ -1,527 +0,0 @@ -""" -Android Usage Stats API — UsageStatsManager, UsageStats, UsageEvents, ConfigurationStats, StorageStatsManager -方法签名完全对齐 Android SDK -""" - -import json -import os as _os -import sys -import time -from datetime import datetime, timedelta -from typing import Any, Dict, List, Optional, Set, Tuple, Callable -from collections import defaultdict - -from .._core import Stub, AndroidOnlyError, resolve_android_path, _app_data_dir, _sdcard_root -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "UsageStatsManager", - "UsageStats", - "UsageEvents", - "ConfigurationStats", - "CacheUsageStats", - "NetworkUsageStats", - "UsageStatsManagerInternal", - "StorageStatsManager", - "StorageStats", -] - - -# ═══════════════════════════════════════════════════════════════ -# UsageStatsManager -# ═══════════════════════════════════════════════════════════════ - -class UsageStatsManager(Stub): - """UsageStatsManager — 使用统计管理器 (桌面模拟)""" - - INTERVAL_DAILY = 0 - INTERVAL_WEEKLY = 1 - INTERVAL_MONTHLY = 2 - INTERVAL_YEARLY = 3 - INTERVAL_BEST = 4 - INTERVAL_COUNT = 5 - - USAGE_SOURCE_UNSPECIFIED = 0 - USAGE_SOURCE_CURRENT_ACTIVITY = 1 - USAGE_SOURCE_TASK_ROOT_ACTIVITY = 2 - - def __init__(self): - super().__init__() - self._app_inactive: Dict[str, bool] = {} - - def queryUsageStats( - self, intervalType: int, beginTime: int, endTime: int - ) -> List["UsageStats"]: - """查询使用统计信息""" - result: List[UsageStats] = [] - stats_dir = _os.path.join(_app_data_dir, "usagestats") - if _os.path.exists(stats_dir): - for fname in _os.listdir(stats_dir): - if fname.endswith(".json"): - filepath = _os.path.join(stats_dir, fname) - try: - with open(filepath, "r") as f: - data = json.load(f) - stats = UsageStats() - stats._package_name = data.get("package", fname[:-5]) - stats._first_time = data.get("first_time", beginTime) - stats._last_time = data.get("last_time", endTime) - stats._last_used = data.get("last_used", beginTime) - stats._total_foreground = data.get("total_foreground", 0) - stats._launch_count = data.get("launch_count", 0) - if stats._last_time >= beginTime and stats._last_time <= endTime: - result.append(stats) - except (json.JSONDecodeError, IOError): - pass - return result - - def queryConfigurations( - self, intervalType: int, beginTime: int, endTime: int - ) -> List["ConfigurationStats"]: - """查询配置统计""" - return [] - - def queryEvents( - self, beginTime: int, endTime: int - ) -> "UsageEvents": - """查询使用事件""" - return UsageEvents() - - def isAppInactive(self, packageName: str) -> bool: - """检查应用是否处于非活跃状态""" - return self._app_inactive.get(packageName, False) - - def setAppInactive(self, packageName: str, inactive: bool): - """设置应用非活跃状态""" - self._app_inactive[packageName] = inactive - - def onCarrierPrivilegedAppsChanged(self): - """运营商特权应用变更 (桌面存根)""" - pass - - def toString(self) -> str: - return f"UsageStatsManager@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# UsageStats -# ═══════════════════════════════════════════════════════════════ - -class UsageStats(Stub): - """UsageStats — 应用使用统计""" - - def __init__(self): - super().__init__() - self._package_name = "" - self._first_time = 0 - self._last_time = 0 - self._last_used = 0 - self._total_foreground = 0 - self._launch_count = 0 - - def getPackageName(self) -> str: - return self._package_name - - def getFirstTimeStamp(self) -> int: - return self._first_time - - def getLastTimeStamp(self) -> int: - return self._last_time - - def getLastUsedTime(self) -> int: - return self._last_used - - def getTotalTimeInForeground(self) -> int: - return self._total_foreground - - def getAppLaunchCount(self) -> int: - return self._launch_count - - def toString(self) -> str: - return (f"UsageStats(pkg={self._package_name}, " - f"foreground={self._total_foreground}ms, " - f"launches={self._launch_count})") - - def equals(self, other) -> bool: - if not isinstance(other, UsageStats): - return False - return self._package_name == other._package_name - - def hashCode(self) -> int: - return hash(self._package_name) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# UsageEvents -# ═══════════════════════════════════════════════════════════════ - -class UsageEvents(Stub): - """UsageEvents — 使用事件列表""" - - def __init__(self): - super().__init__() - self._events: List["UsageEvents.Event"] = [] - self._index = 0 - - def getNextEvent(self, eventOut: "UsageEvents.Event") -> bool: - """获取下一个事件""" - if self._index < len(self._events): - ev = self._events[self._index] - eventOut._package_name = ev._package_name - eventOut._class_name = ev._class_name - eventOut._action = ev._action - eventOut._time_stamp = ev._time_stamp - self._index += 1 - return True - return False - - def hasNextEvent(self) -> bool: - """检查是否还有下一个事件""" - return self._index < len(self._events) - - def resetToStart(self): - """重置到起始位置""" - self._index = 0 - - # ═══════════════════════════════════════════════════════════ - # Event — 内部事件类 - # ═══════════════════════════════════════════════════════════ - - class Event(Stub): - """UsageEvents.Event — 使用事件""" - - # --- Event action constants --- - MOVE_TO_FOREGROUND = 1 - MOVE_TO_BACKGROUND = 2 - CONFIGURATION_CHANGE = 3 - SHORTCUT_INVOCATION = 4 - STANDBY_BUDGET_CHANGED = 5 - USER_INTERACTION = 6 - NOTIFICATION_SEEN = 7 - NOTIFICATION_INTERRUPTION = 8 - CHOOSER_ACTION = 9 - APP_COMPONENT_USED = 10 - ACTIVITY_STOPPED = 11 - SYSTEM_INTERACTION = 12 - FLAG_IS_PACKAGE_INSTALL = 13 - FLAG_NO_INTERRUPTION = 14 - - def __init__(self): - super().__init__() - self._package_name = "" - self._class_name = "" - self._action = 0 - self._time_stamp = 0 - - def getPackageName(self) -> str: - return self._package_name - - def getClassName(self) -> str: - return self._class_name - - def getAction(self) -> int: - return self._action - - def getTimeStamp(self) -> int: - return self._time_stamp - - def toString(self) -> str: - return (f"Event(pkg={self._package_name}, " - f"action={self._action}, time={self._time_stamp})") - - def equals(self, other) -> bool: - if not isinstance(other, UsageEvents.Event): - return False - return (self._package_name == other._package_name - and self._action == other._action - and self._time_stamp == other._time_stamp) - - def hashCode(self) -> int: - return hash((self._package_name, self._action, self._time_stamp)) & 0x7FFFFFFF - - def toString(self) -> str: - return f"UsageEvents(count={len(self._events)})" - - def equals(self, other) -> bool: - if not isinstance(other, UsageEvents): - return False - return self._events == other._events - - def hashCode(self) -> int: - return hash(len(self._events)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# ConfigurationStats -# ═══════════════════════════════════════════════════════════════ - -class ConfigurationStats(Stub): - """ConfigurationStats — 配置统计""" - - def __init__(self): - super().__init__() - self._first_time = 0 - self._last_time = 0 - self._last_active = 0 - self._total_active = 0 - self._activation_count = 0 - - def getFirstTimeStamp(self) -> int: - return self._first_time - - def getLastTimeStamp(self) -> int: - return self._last_time - - def getLastTimeActive(self) -> int: - return self._last_active - - def getTotalTimeActive(self) -> int: - return self._total_active - - def getActivationCount(self) -> int: - return self._activation_count - - def toString(self) -> str: - return f"ConfigurationStats(active={self._total_active}ms, count={self._activation_count})" - - def equals(self, other) -> bool: - if not isinstance(other, ConfigurationStats): - return False - return (self._first_time == other._first_time - and self._last_time == other._last_time) - - def hashCode(self) -> int: - return hash((self._first_time, self._last_time)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# CacheUsageStats -# ═══════════════════════════════════════════════════════════════ - -class CacheUsageStats(Stub): - """CacheUsageStats — 缓存使用统计""" - - def __init__(self): - super().__init__() - self._cache_size = 0 - self._total_size = 0 - - def getCacheSize(self) -> int: - return self._cache_size - - def getTotalSize(self) -> int: - return self._total_size - - def toString(self) -> str: - return f"CacheUsageStats(cache={self._cache_size}B)" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# NetworkUsageStats -# ═══════════════════════════════════════════════════════════════ - -class NetworkUsageStats(Stub): - """NetworkUsageStats — 网络使用统计""" - - def __init__(self): - super().__init__() - self._rx_bytes = 0 - self._tx_bytes = 0 - self._rx_packets = 0 - self._tx_packets = 0 - - def getRxBytes(self) -> int: - return self._rx_bytes - - def getTxBytes(self) -> int: - return self._tx_bytes - - def getRxPackets(self) -> int: - return self._rx_packets - - def getTxPackets(self) -> int: - return self._tx_packets - - def toString(self) -> str: - return f"NetworkUsageStats(rx={self._rx_bytes}B, tx={self._tx_bytes}B)" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# UsageStatsManagerInternal -# ═══════════════════════════════════════════════════════════════ - -class UsageStatsManagerInternal(Stub): - """UsageStatsManagerInternal — 内部使用统计管理器""" - - def __init__(self): - super().__init__() - - def reportEvent(self, event: Any): - """报告使用事件""" - pass - - def toString(self) -> str: - return f"UsageStatsManagerInternal@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# StorageStatsManager -# ═══════════════════════════════════════════════════════════════ - -class StorageStatsManager(Stub): - """StorageStatsManager — 存储统计管理器""" - - def __init__(self): - super().__init__() - - def queryStatsForPackage( - self, storageUuid: Any, packageName: str, userHandle: Any - ) -> "StorageStats": - """查询指定应用存储统计""" - return self._query_stats(storageUuid, packageName) - - def queryStatsForUid( - self, storageUuid: Any, uid: int - ) -> "StorageStats": - """查询指定 UID 存储统计""" - return self._query_stats(storageUuid, f"uid_{uid}") - - def queryStatsForUser( - self, storageUuid: Any, userHandle: Any - ) -> "StorageStats": - """查询指定用户存储统计""" - return self._query_stats(storageUuid, f"user_{userHandle}") - - def queryExternalStatsForUser( - self, storageUuid: Any, userHandle: Any - ) -> "StorageStats": - """查询外部用户存储统计""" - return self._query_stats(storageUuid, f"external_user_{userHandle}") - - def getFreeBytes(self, storageUuid: Any) -> int: - """获取空闲字节数""" - try: - st = _os.statvfs(_sdcard_root) - return st.f_bavail * st.f_frsize - except (OSError, AttributeError): - return 1024 * 1024 * 1024 # 默认 1GB - - def getTotalBytes(self, storageUuid: Any) -> int: - """获取总字节数""" - try: - st = _os.statvfs(_sdcard_root) - return st.f_blocks * st.f_frsize - except (OSError, AttributeError): - return 64 * 1024 * 1024 * 1024 # 默认 64GB - - def _query_stats(self, storage_uuid: Any, tag: str) -> "StorageStats": - """内部查询存储统计""" - stats = StorageStats() - pkg_dir = _os.path.join(_app_data_dir, "files") - if _os.path.exists(pkg_dir): - for dirpath, dirnames, filenames in _os.walk(pkg_dir): - for fname in filenames: - fpath = _os.path.join(dirpath, fname) - try: - fsize = _os.path.getsize(fpath) - ext = _os.path.splitext(fname)[1].lower() - if ext in (".jpg", ".png", ".gif", ".webp", ".bmp"): - stats._image_bytes += fsize - elif ext in (".mp3", ".wav", ".flac", ".ogg", ".aac"): - stats._music_bytes += fsize - elif ext in (".mp4", ".avi", ".mkv", ".mov", ".webm"): - stats._video_bytes += fsize - else: - stats._data_bytes += fsize - except OSError: - pass - return stats - - def toString(self) -> str: - return f"StorageStatsManager@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# StorageStats -# ═══════════════════════════════════════════════════════════════ - -class StorageStats(Stub): - """StorageStats — 存储统计""" - - def __init__(self): - super().__init__() - self._app_bytes = 0 - self._data_bytes = 0 - self._cache_bytes = 0 - self._music_bytes = 0 - self._video_bytes = 0 - self._image_bytes = 0 - self._game_bytes = 0 - - def getAppBytes(self) -> int: - return self._app_bytes - - def getDataBytes(self) -> int: - return self._data_bytes - - def getCacheBytes(self) -> int: - return self._cache_bytes - - def getMusicBytes(self) -> int: - return self._music_bytes - - def getVideoBytes(self) -> int: - return self._video_bytes - - def getImageBytes(self) -> int: - return self._image_bytes - - def getGameBytes(self) -> int: - return self._game_bytes - - def toString(self) -> str: - return (f"StorageStats(app={self._app_bytes}B, data={self._data_bytes}B, " - f"cache={self._cache_bytes}B, music={self._music_bytes}B, " - f"video={self._video_bytes}B, image={self._image_bytes}B)") - - def equals(self, other) -> bool: - if not isinstance(other, StorageStats): - return False - return (self._app_bytes == other._app_bytes - and self._data_bytes == other._data_bytes) - - def hashCode(self) -> int: - return hash((self._app_bytes, self._data_bytes)) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/app_voiceinteractor.py b/android_pyapi/android/app_voiceinteractor.py deleted file mode 100644 index 924153d..0000000 --- a/android_pyapi/android/app_voiceinteractor.py +++ /dev/null @@ -1,590 +0,0 @@ -""" -Android VoiceInteractor API — VoiceInteractor, Request, Prompt, etc. -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import uuid -from typing import Any, Dict, List, Optional, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# VoiceInteractor -# ═══════════════════════════════════════════════════════════════ - -class VoiceInteractor(Stub): - """VoiceInteractor — 语音交互器 (桌面存根)""" - - # 结果码 - RESULT_CANCELED = 0 - RESULT_SUCCESS = -1 - RESULT_TIMEOUT = -2 - RESULT_ERROR = -3 - RESULT_NO_MATCH = -4 - RESULT_SKIPPED = -5 - RESULT_ABORTED = -6 - RESULT_UNSUPPORTED = -7 - RESULT_BUSY = -8 - RESULT_NOT_FOUND = -9 - - # 语音活动状态 - SPEECH_ACTIVITY_UNKNOWN = 0 - SPEECH_ACTIVITY_STARTING = 1 - SPEECH_ACTIVITY_STARTED = 2 - SPEECH_ACTIVITY_STOPPING = 3 - SPEECH_ACTIVITY_STOPPED = 4 - SPEECH_ACTIVITY_IN_PROGRESS = 5 - SPEECH_ACTIVITY_INTERRUPTED = 6 - SPEECH_ACTIVITY_TIMED_OUT = 7 - SPEECH_ACTIVITY_COMPLETED = 8 - SPEECH_ACTIVITY_ERROR = 9 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._requests: Dict[str, "VoiceInteractor.Request"] = {} - self._active_request: Optional["VoiceInteractor.Request"] = None - self._speech_activity = VoiceInteractor.SPEECH_ACTIVITY_UNKNOWN - self._available = True - self._supported_actions: List[str] = [] - self._voice_activity_callback: Optional[Callable] = None - self._interaction_callback: Optional[Callable] = None - self._voice_interaction_service: Optional[Any] = None - self._voice_interaction_session: Optional[Any] = None - self._system_voice_interaction = False - - def isVoiceInteraction(self) -> bool: - return True - - def isVoiceInteractionRoot(self) -> bool: - return True - - def notifyVoiceActivity(self, state: int) -> None: - self._speech_activity = state - if self._voice_activity_callback: - self._voice_activity_callback(state) - - def startVoiceActivity(self, voiceInteractionSession: Any) -> None: - pass - - def setVoiceActivityCallback(self, callback: Callable) -> None: - self._voice_activity_callback = callback - - def getVoiceActivityState(self) -> int: - return self._speech_activity - - def submitRequest(self, request: "VoiceInteractor.Request") -> None: - self._submitRequest(request) - - def submitRequest(self, request: "VoiceInteractor.Request", callback: Any) -> None: - request._callback = callback - self._submitRequest(request) - - def _submitRequest(self, request: "VoiceInteractor.Request") -> None: - request_id = str(uuid.uuid4()) - request._request_id = request_id - self._requests[request_id] = request - self._active_request = request - if request._callback: - req = request - # 模拟回调 - - def getActiveRequest(self) -> Optional["VoiceInteractor.Request"]: - return self._active_request - - def cancelRequest(self, request: "VoiceInteractor.Request") -> None: - if request._request_id in self._requests: - del self._requests[request._request_id] - if self._active_request is request: - self._active_request = None - - def cancelRequests(self, requestClass: type = None) -> None: - if requestClass: - to_remove = [ - rid for rid, req in self._requests.items() - if isinstance(req, requestClass) - ] - for rid in to_remove: - del self._requests[rid] - else: - self._requests.clear() - if self._active_request and (not requestClass or isinstance(self._active_request, requestClass)): - self._active_request = None - - def getCurrentRequest(self) -> Optional["VoiceInteractor.Request"]: - return self._active_request - - def areNotificationsSupported(self, supportedActions: List[str]) -> bool: - return True - - def startConfirmation(self, prompt: "VoiceInteractor.Prompt", callback: Any) -> None: - request = VoiceInteractor.ConfirmationRequest(prompt, None) - request._callback = callback - self._submitRequest(request) - - def startPickOption(self, prompts: List["VoiceInteractor.Prompt"], options: List[Any], callback: Any) -> None: - request = VoiceInteractor.PickOptionRequest(prompts, options, None) - request._callback = callback - self._submitRequest(request) - - def startCommand(self, command: str, callback: Any) -> None: - request = VoiceInteractor.CommandRequest(command, None) - request._callback = callback - self._submitRequest(request) - - def startCompleteVoice(self, prompt: "VoiceInteractor.Prompt", callback: Any) -> None: - request = VoiceInteractor.CompleteVoiceRequest(prompt, None) - request._callback = callback - self._submitRequest(request) - - def startAbortVoice(self, prompt: "VoiceInteractor.Prompt", callback: Any) -> None: - request = VoiceInteractor.AbortVoiceRequest(prompt, None) - request._callback = callback - self._submitRequest(request) - - def getPackageName(self) -> str: - return "com.android.voiceinteractor" - - def getVoiceInteractorService(self) -> Optional[Any]: - return self._voice_interaction_service - - def destroy(self) -> None: - self._requests.clear() - self._active_request = None - self._available = False - - def toString(self) -> str: - return f"VoiceInteractor(active={self._active_request is not None}, requests={len(self._requests)})" - - # ═══════════════════════════════════════════════════════════ - # VoiceInteractor.Request - # ═══════════════════════════════════════════════════════════ - - class Request(Stub): - """VoiceInteractor.Request — 请求基类""" - - def __init__(self): - super().__init__() - self._request_id: Optional[str] = None - self._context: Optional[Any] = None - self._callback: Optional[Any] = None - self._cancelled = False - self._result: Optional[Any] = None - self._timeout_ms: Optional[int] = None - self._start_time: Optional[float] = None - self._activity: Optional[Any] = None - self._interactor: Optional["VoiceInteractor"] = None - - def getRequestId(self) -> Optional[str]: - return self._request_id - - def getActivity(self) -> Optional[Any]: - return self._activity - - def onActivityResult(self, requestCode: int, resultCode: int, data: Any) -> None: - pass - - def onCancel(self) -> None: - self._cancelled = True - if self._callback and hasattr(self._callback, "onCancel"): - self._callback.onCancel(self) - - def onTimeout(self) -> None: - if self._callback and hasattr(self._callback, "onTimeout"): - self._callback.onTimeout(self) - - def isCancelled(self) -> bool: - return self._cancelled - - def getResult(self) -> Optional[Any]: - return self._result - - def cancel(self) -> None: - self._cancelled = True - - def toString(self) -> str: - return f"VoiceInteractor.Request(id={self._request_id})" - - # ═══════════════════════════════════════════════════════════ - # VoiceInteractor.Prompt - # ═══════════════════════════════════════════════════════════ - - class Prompt(Stub): - """VoiceInteractor.Prompt — 语音提示""" - - def __init__(self, prompt: Any): - super().__init__() - if isinstance(prompt, str): - self._voice_prompts: List[str] = [prompt] - self._visual_prompt: Optional[str] = prompt - elif isinstance(prompt, list): - self._voice_prompts = list(prompt) - self._visual_prompt = prompt[0] if prompt else None - elif isinstance(prompt, tuple): - self._voice_prompts = [str(p) for p in prompt] - self._visual_prompt = str(prompt[0]) if prompt else None - else: - self._voice_prompts = [str(prompt)] - self._visual_prompt = str(prompt) - self._count = len(self._voice_prompts) - - def getVoicePromptAt(self, index: int) -> Optional[str]: - if 0 <= index < self._count: - return self._voice_prompts[index] - return None - - def getVisualPrompt(self) -> Optional[str]: - return self._visual_prompt - - def countVoicePrompts(self) -> int: - return self._count - - def toString(self) -> str: - return f"VoiceInteractor.Prompt(voice_prompts={self._count})" - - # ═══════════════════════════════════════════════════════════ - # VoiceInteractor.CompleteVoiceRequest - # ═══════════════════════════════════════════════════════════ - - class CompleteVoiceRequest(Request): - """VoiceInteractor.CompleteVoiceRequest — 完成语音请求""" - - def __init__(self, prompt: "VoiceInteractor.Prompt", bundle: Any = None): - super().__init__() - self._prompt = prompt - self._bundle = bundle - self._result_code = VoiceInteractor.RESULT_SUCCESS - self._result_data: Optional[Any] = None - - def getPrompt(self) -> Optional["VoiceInteractor.Prompt"]: - return self._prompt - - def getBundle(self) -> Optional[Any]: - return self._bundle - - def setResult(self, resultCode: int, result: Any) -> None: - self._result_code = resultCode - self._result_data = result - - def toString(self) -> str: - return f"CompleteVoiceRequest(prompt={self._prompt})" - - # ═══════════════════════════════════════════════════════════ - # VoiceInteractor.AbortVoiceRequest - # ═══════════════════════════════════════════════════════════ - - class AbortVoiceRequest(Request): - """VoiceInteractor.AbortVoiceRequest — 中止语音请求""" - - def __init__(self, prompt: "VoiceInteractor.Prompt", bundle: Any = None): - super().__init__() - self._prompt = prompt - self._bundle = bundle - self._result_code = VoiceInteractor.RESULT_ABORTED - self._reason: Optional[str] = None - - def getPrompt(self) -> Optional["VoiceInteractor.Prompt"]: - return self._prompt - - def getBundle(self) -> Optional[Any]: - return self._bundle - - def getReason(self) -> Optional[str]: - return self._reason - - def setAbortReason(self, reason: str) -> None: - self._reason = reason - - def toString(self) -> str: - return f"AbortVoiceRequest(prompt={self._prompt}, reason={self._reason})" - - # ═══════════════════════════════════════════════════════════ - # VoiceInteractor.CommandRequest - # ═══════════════════════════════════════════════════════════ - - class CommandRequest(Request): - """VoiceInteractor.CommandRequest — 命令请求""" - - def __init__(self, command: str, bundle: Any = None): - super().__init__() - self._command = command - self._bundle = bundle - self._result_code = VoiceInteractor.RESULT_SUCCESS - self._result_data: Optional[Any] = None - - def getCommand(self) -> str: - return self._command - - def getBundle(self) -> Optional[Any]: - return self._bundle - - def setResult(self, resultCode: int, result: Any) -> None: - self._result_code = resultCode - self._result_data = result - - def getResultCode(self) -> int: - return self._result_code - - def getResultData(self) -> Optional[Any]: - return self._result_data - - def onCommandResult(self, isCompleted: bool, result: Any) -> None: - if self._callback and hasattr(self._callback, "onCommandResult"): - self._callback.onCommandResult(isCompleted, result) - - def toString(self) -> str: - return f"CommandRequest(command={self._command})" - - # ═══════════════════════════════════════════════════════════ - # VoiceInteractor.PickOptionRequest - # ═══════════════════════════════════════════════════════════ - - class PickOptionRequest(Request): - """VoiceInteractor.PickOptionRequest — 选项选择请求""" - - class Option(Stub): - """PickOptionRequest.Option — 选项""" - - def __init__(self, label: str, index: int = -1): - super().__init__() - self._label = label - self._index = index - self._synonyms: List[str] = [] - self._extras: Optional[Any] = None - self._is_selected = False - - def getLabel(self) -> str: - return self._label - - def getIndex(self) -> int: - return self._index - - def setIndex(self, index: int) -> None: - self._index = index - - def addSynonym(self, synonym: str) -> None: - self._synonyms.append(synonym) - - def getSynonyms(self) -> List[str]: - return list(self._synonyms) - - def setExtras(self, extras: Any) -> None: - self._extras = extras - - def getExtras(self) -> Optional[Any]: - return self._extras - - def isSelected(self) -> bool: - return self._is_selected - - def toString(self) -> str: - return f"Option(label={self._label}, index={self._index})" - - def __init__(self, prompts: Any, options: List[Any], bundle: Any = None): - super().__init__() - if isinstance(prompts, VoiceInteractor.Prompt): - self._prompts = [prompts] - elif isinstance(prompts, list): - self._prompts = prompts - else: - self._prompts = [VoiceInteractor.Prompt(str(prompts))] - self._options = options - self._bundle = bundle - self._selected_option: Optional[Any] = None - self._result_code = VoiceInteractor.RESULT_SUCCESS - - def getPrompts(self) -> List["VoiceInteractor.Prompt"]: - return list(self._prompts) - - def getOptions(self) -> List[Any]: - return list(self._options) - - def getBundle(self) -> Optional[Any]: - return self._bundle - - def getSelectedOption(self) -> Optional[Any]: - return self._selected_option - - def setPickOptionResult(self, option: Any, result: Any) -> None: - self._selected_option = option - self._result_data = result - self._result_code = VoiceInteractor.RESULT_SUCCESS - - def onPickOptionResult(self, completed: bool, option: Any, result: Any) -> None: - if completed: - self._selected_option = option - if self._callback and hasattr(self._callback, "onPickOptionResult"): - self._callback.onPickOptionResult(completed, option, result) - - def toString(self) -> str: - return f"PickOptionRequest(options={len(self._options)})" - - # ═══════════════════════════════════════════════════════════ - # VoiceInteractor.ConfirmationRequest - # ═══════════════════════════════════════════════════════════ - - class ConfirmationRequest(Request): - """VoiceInteractor.ConfirmationRequest — 确认请求""" - - def __init__(self, prompt: "VoiceInteractor.Prompt", bundle: Any = None): - super().__init__() - self._prompt = prompt - self._bundle = bundle - self._confirmed = False - self._result_code = VoiceInteractor.RESULT_SUCCESS - self._result_data: Optional[Any] = None - - def getPrompt(self) -> Optional["VoiceInteractor.Prompt"]: - return self._prompt - - def getBundle(self) -> Optional[Any]: - return self._bundle - - def isConfirmed(self) -> bool: - return self._confirmed - - def setConfirmationResult(self, confirmed: bool, result: Any) -> None: - self._confirmed = confirmed - self._result_data = result - self._result_code = VoiceInteractor.RESULT_SUCCESS if confirmed else VoiceInteractor.RESULT_CANCELED - - def onConfirmationResult(self, confirmed: bool, result: Any) -> None: - self._confirmed = confirmed - if self._callback and hasattr(self._callback, "onConfirmationResult"): - self._callback.onConfirmationResult(confirmed, result) - - def toString(self) -> str: - return f"ConfirmationRequest(confirmed={self._confirmed})" - - -# ═══════════════════════════════════════════════════════════════ -# VoiceInteractor 扩展功能 -# ═══════════════════════════════════════════════════════════════ - -class VoiceInteractorResult(Stub): - """VoiceInteractor.Result — 语音交互结果""" - - def __init__(self, resultCode: int = VoiceInteractor.RESULT_SUCCESS, resultData: Any = None): - super().__init__() - self._result_code = resultCode - self._result_data = resultData - self._timed_out = False - self._cancelled = False - self._error_message: Optional[str] = None - - def getResultCode(self) -> int: - return self._result_code - - def getResultData(self) -> Optional[Any]: - return self._result_data - - def getErrorMessage(self) -> Optional[str]: - return self._error_message - - def isTimedOut(self) -> bool: - return self._timed_out - - def isCancelled(self) -> bool: - return self._cancelled - - def isSuccess(self) -> bool: - return self._result_code == VoiceInteractor.RESULT_SUCCESS - - def toString(self) -> str: - return f"VoiceInteractorResult(code={self._result_code})" - - -class VoiceInteractorCallback(Stub): - """VoiceInteractor.Callback — 语音交互回调基类""" - - def __init__(self): - super().__init__() - - def onRequestSuccess(self, request: VoiceInteractor.Request) -> None: - pass - - def onRequestFailure(self, request: VoiceInteractor.Request) -> None: - pass - - def onRequestCancelled(self, request: VoiceInteractor.Request) -> None: - pass - - def onRequestTimeout(self, request: VoiceInteractor.Request) -> None: - pass - - def toString(self) -> str: - return "VoiceInteractor.Callback" - - -class VoiceInteractorNotification(Stub): - """VoiceInteractor.Notifications — 语音交互通知支持""" - - def __init__(self): - super().__init__() - self._supported_actions: List[str] = [] - self._notification_channel_id: Optional[str] = None - self._notification_title: Optional[str] = None - self._notification_text: Optional[str] = None - - def getSupportedActions(self) -> List[str]: - return list(self._supported_actions) - - def isActionSupported(self, action: str) -> bool: - return action in self._supported_actions - - def setNotificationChannel(self, channelId: str) -> None: - self._notification_channel_id = channelId - - def getNotificationChannel(self) -> Optional[str]: - return self._notification_channel_id - - def toString(self) -> str: - return f"VoiceInteractorNotifications(actions={len(self._supported_actions)})" - - -class VoiceInteractorSession(Stub): - """VoiceInteractor.Session — 语音交互会话""" - - def __init__(self, interactor: "VoiceInteractor"): - super().__init__() - self._interactor = interactor - self._session_id = str(uuid.uuid4()) - self._active = False - self._start_time = time.time() - self._requests: List[VoiceInteractor.Request] = [] - self._current_prompt: Optional[VoiceInteractor.Prompt] = None - self._state = VoiceInteractor.SPEECH_ACTIVITY_UNKNOWN - - def getSessionId(self) -> str: - return self._session_id - - def isActive(self) -> bool: - return self._active - - def start(self) -> None: - self._active = True - self._state = VoiceInteractor.SPEECH_ACTIVITY_STARTING - - def stop(self) -> None: - self._active = False - self._state = VoiceInteractor.SPEECH_ACTIVITY_STOPPED - - def getDuration(self) -> float: - if self._active: - return time.time() - self._start_time - return 0.0 - - def getState(self) -> int: - return self._state - - def getRequestCount(self) -> int: - return len(self._requests) - - def toString(self) -> str: - return f"VoiceInteractorSession(active={self._active}, state={self._state})" \ No newline at end of file diff --git a/android_pyapi/android/app_wallpaper.py b/android_pyapi/android/app_wallpaper.py deleted file mode 100644 index ae59b8c..0000000 --- a/android_pyapi/android/app_wallpaper.py +++ /dev/null @@ -1,571 +0,0 @@ -""" -Android Wallpaper API — WallpaperManager, WallpaperInfo, WallpaperService, WallpaperService.Engine -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import time -import tempfile -from typing import Any, Dict, List, Optional, Tuple, Callable, BinaryIO - -from .._core import Stub, AndroidOnlyError, resolve_android_path, _sdcard_root, _app_data_dir - -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -# FLAG_* 常量 -FLAG_SYSTEM = 0x00000001 -FLAG_LOCK = 0x00000002 -FLAG_SYSTEM_AND_LOCK = FLAG_SYSTEM | FLAG_LOCK -FLAG_SET_BOTH = FLAG_SYSTEM | FLAG_LOCK - -# ACTION_* 常量 -ACTION_CHANGE_LIVE_WALLPAPER = "android.service.wallpaper.CHANGE_LIVE_WALLPAPER" -ACTION_LIVE_WALLPAPER_CHOOSER = "android.service.wallpaper.LIVE_WALLPAPER_CHOOSER" -ACTION_CROP_AND_SET_WALLPAPER = "android.service.wallpaper.CROP_AND_SET_WALLPAPER" -ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED" -ACTION_SET_WALLPAPER = "android.service.wallpaper.SET_WALLPAPER" - -# WALLPAPER_TYPE 常量 -WALLPAPER_TYPE_SYSTEM = 1 -WALLPAPER_TYPE_LOCK = 2 -WALLPAPER_TYPE_BOTH = 3 -WALLPAPER_TYPE_LIVE = 4 - -# 其它常量 -WALLPAPER_HEIGHT = 1920 -WALLPAPER_WIDTH = 1080 -MIN_CROP_WIDTH = 320 -MIN_CROP_HEIGHT = 480 - - -# ═══════════════════════════════════════════════════════════════ -# WallpaperManager -# ═══════════════════════════════════════════════════════════════ - -class WallpaperManager(Stub): - """WallpaperManager — 壁纸管理器 (桌面存根)""" - - ACTION_CHANGE_LIVE_WALLPAPER = ACTION_CHANGE_LIVE_WALLPAPER - ACTION_LIVE_WALLPAPER_CHOOSER = ACTION_LIVE_WALLPAPER_CHOOSER - ACTION_CROP_AND_SET_WALLPAPER = ACTION_CROP_AND_SET_WALLPAPER - ACTION_WALLPAPER_CHANGED = ACTION_WALLPAPER_CHANGED - ACTION_SET_WALLPAPER = ACTION_SET_WALLPAPER - - FLAG_SYSTEM = FLAG_SYSTEM - FLAG_LOCK = FLAG_LOCK - FLAG_SYSTEM_AND_LOCK = FLAG_SYSTEM_AND_LOCK - - WALLPAPER_TYPE_SYSTEM = WALLPAPER_TYPE_SYSTEM - WALLPAPER_TYPE_LOCK = WALLPAPER_TYPE_LOCK - WALLPAPER_TYPE_BOTH = WALLPAPER_TYPE_BOTH - WALLPAPER_TYPE_LIVE = WALLPAPER_TYPE_LIVE - - _instance: Optional["WallpaperManager"] = None - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._wallpaper_dir = os.path.join(_app_data_dir, "wallpapers") - os.makedirs(self._wallpaper_dir, exist_ok=True) - self._system_wallpaper_path = os.path.join(self._wallpaper_dir, "system_wallpaper") - self._lock_wallpaper_path = os.path.join(self._wallpaper_dir, "lock_wallpaper") - self._wallpaper_info: Optional["WallpaperInfo"] = None - self._is_wallpaper_supported = True - self._wallpaper_offset = 0.5 - self._wallpaper_offsets = (0.0, 0.0, 0.0, 0.0) - self._wallpaper_dimensions = (WALLPAPER_WIDTH, WALLPAPER_HEIGHT) - self._wallpaper_zoom = 1.0 - self._suggest_wallpaper_dimensions = (WALLPAPER_WIDTH, WALLPAPER_HEIGHT) - self._crop_hint = (0, 0, WALLPAPER_WIDTH, WALLPAPER_HEIGHT) - self._padding = (0, 0, 0, 0) - self._pre_wallpaper_components: List[Any] = [] - self._wallpaper_visibility = True - self._wallpaper_callbacks: List[Callable] = [] - self._wallpaper_parallax_effect = True - self._wallpaper_dimming = 0.0 - - @staticmethod - def getInstance(context: Any) -> "WallpaperManager": - if WallpaperManager._instance is None: - WallpaperManager._instance = WallpaperManager(context) - return WallpaperManager._instance - - def getDrawable(self) -> Optional[Any]: - """获取当前壁纸 Drawable""" - if os.path.exists(self._system_wallpaper_path): - from .._core import HAS_PIL - if HAS_PIL: - try: - from PIL import Image as _PILImage - return _PILImage.open(self._system_wallpaper_path) - except Exception: - pass - return None - - def peekDrawable(self) -> Optional[Any]: - """获取当前壁纸但不触发加载""" - return self.getDrawable() - - def getFastDrawable(self) -> Optional[Any]: - """快速获取壁纸""" - return self.getDrawable() - - def peekFastDrawable(self) -> Optional[Any]: - return self.peekDrawable() - - def getBitmap(self) -> Optional[Any]: - """获取壁纸位图""" - if os.path.exists(self._system_wallpaper_path): - try: - from PIL import Image as _PILImage - return _PILImage.open(self._system_wallpaper_path) - except ImportError: - pass - return None - - def peekBitmap(self) -> Optional[Any]: - return self.getBitmap() - - def setBitmap(self, bitmap: Any) -> None: - """设置壁纸位图""" - self.setBitmap(bitmap, None, True, FLAG_SYSTEM) - - def setBitmap(self, bitmap: Any, hint: Any, allowBackup: bool = True, which: int = FLAG_SYSTEM) -> None: - path = self._get_wallpaper_path(which) - try: - if hasattr(bitmap, "save"): - bitmap.save(path) - elif hasattr(bitmap, "tobytes"): - with open(path, "wb") as f: - f.write(bitmap.tobytes()) - else: - with open(path, "w") as f: - f.write(str(bitmap)) - except Exception as e: - raise RuntimeException(f"Failed to set wallpaper: {e}") - - def setStream(self, data: BinaryIO) -> None: - self.setStream(data, None, True, FLAG_SYSTEM) - - def setStream(self, data: BinaryIO, hint: Any, allowBackup: bool = True, which: int = FLAG_SYSTEM) -> None: - path = self._get_wallpaper_path(which) - with open(path, "wb") as f: - f.write(data.read()) - - def getResourceContext(self) -> Optional[Any]: - return self._context - - def getWallpaperInfo(self) -> Optional["WallpaperInfo"]: - return self._wallpaper_info - - def getWallpaperFile(self, which: int = FLAG_SYSTEM) -> Optional[Any]: - path = self._get_wallpaper_path(which) - if os.path.exists(path): - return open(path, "rb") - return None - - def getWallpaperId(self, which: int = FLAG_SYSTEM) -> int: - path = self._get_wallpaper_path(which) - if os.path.exists(path): - return int(os.path.getmtime(path)) - return -1 - - def hasResourceWallpaper(self, resid: int) -> bool: - return False - - def suggestDesiredDimensions(self, minimumWidth: int, minimumHeight: int) -> None: - self._suggest_wallpaper_dimensions = (minimumWidth, minimumHeight) - - def getDesiredMinimumWidth(self) -> int: - return self._suggest_wallpaper_dimensions[0] - - def getDesiredMinimumHeight(self) -> int: - return self._suggest_wallpaper_dimensions[1] - - def clearWallpaper(self) -> None: - self.clear(FLAG_SYSTEM) - - def clear(self, which: int = FLAG_SYSTEM) -> None: - if which & FLAG_SYSTEM: - if os.path.exists(self._system_wallpaper_path): - os.remove(self._system_wallpaper_path) - if which & FLAG_LOCK: - if os.path.exists(self._lock_wallpaper_path): - os.remove(self._lock_wallpaper_path) - - def setWallpaperComponent(self, component: Any) -> None: - pass - - def sendWallpaperCommand(self, window: Any, action: str, x: int, y: int, z: int, extras: Any) -> None: - pass - - def setWallpaperOffsetSteps(self, xStep: float, yStep: float) -> None: - pass - - def setWallpaperOffsets(self, window: Any, xOffset: float, yOffset: float) -> None: - self._wallpaper_offset = xOffset - self._wallpaper_offsets = (xOffset, yOffset, xOffset, yOffset) - - def getWallpaperOffsets(self, window: Any) -> Tuple[float, float, float, float]: - return self._wallpaper_offsets - - def computeScrollRange(self, window: Any) -> int: - return WALLPAPER_WIDTH - - def computeScrollXRange(self, window: Any) -> int: - return WALLPAPER_WIDTH - - def computeScrollYRange(self, window: Any) -> int: - return WALLPAPER_HEIGHT - - def isWallpaperSupported(self) -> bool: - return self._is_wallpaper_supported - - def isSetWallpaperAllowed(self) -> bool: - return True - - def isWallpaperBackupEligible(self, which: int = FLAG_SYSTEM) -> bool: - return True - - def getWallpaperDimAmount(self) -> float: - return self._wallpaper_dimming - - def setWallpaperDimAmount(self, dimAmount: float) -> None: - self._wallpaper_dimming = max(0.0, min(1.0, dimAmount)) - - def setWallpaperZoomOut(self, zoom: float) -> None: - self._wallpaper_zoom = max(0.1, min(1.0, zoom)) - - def getWallpaperZoomOut(self) -> float: - return self._wallpaper_zoom - - def getCropHint(self) -> Tuple[int, int, int, int]: - return self._crop_hint - - def setCropHint(self, cropHint: Tuple[int, int, int, int]) -> None: - self._crop_hint = cropHint - - def getPadding(self) -> Tuple[int, int, int, int]: - return self._padding - - def isParallaxEnabled(self) -> bool: - return self._wallpaper_parallax_effect - - def setParallaxEnabled(self, enabled: bool) -> None: - self._wallpaper_parallax_effect = enabled - - def addOnColorsChangedListener(self, listener: Any, handler: Any = None) -> None: - if listener not in self._wallpaper_callbacks: - self._wallpaper_callbacks.append(listener) - - def removeOnColorsChangedListener(self, listener: Any) -> None: - if listener in self._wallpaper_callbacks: - self._wallpaper_callbacks.remove(listener) - - def _get_wallpaper_path(self, which: int) -> str: - if which & FLAG_SYSTEM: - return self._system_wallpaper_path - if which & FLAG_LOCK: - return self._lock_wallpaper_path - return self._system_wallpaper_path - - def toString(self) -> str: - return ( - f"WallpaperManager(system_exists={os.path.exists(self._system_wallpaper_path)}, " - f"lock_exists={os.path.exists(self._lock_wallpaper_path)})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# WallpaperInfo -# ═══════════════════════════════════════════════════════════════ - -class WallpaperInfo(Stub): - """WallpaperInfo — 壁纸信息""" - - def __init__(self, context: Any = None, component: Any = None): - super().__init__() - self._context = context - self._component = component - self._package_name: Optional[str] = None - self._service_name: Optional[str] = None - self._label: Optional[str] = None - self._description: Optional[str] = None - self._author: Optional[str] = None - self._context_description: Optional[str] = None - self._settings_activity: Optional[str] = None - self._settings_piece: Optional[str] = None - self._thumbnail: Optional[Any] = None - self._system_wallpaper = False - self._supports_ambient_mode = False - self._is_standalone = False - self._show_metadata_in_preview = True - self._shared_library = "" - self._wallpaper_author: Optional[str] = None - self._wallpaper_description: Optional[str] = None - self._wallpaper_context_description: Optional[str] = None - self._wallpaper_settings_activity: Optional[str] = None - self._wallpaper_thumbnail: Optional[Any] = None - self._wallpaper_show_metadata = True - self._wallpaper_supports_ambient = False - - def getPackageName(self) -> Optional[str]: - return self._package_name - - def getServiceName(self) -> Optional[str]: - return self._service_name - - def getServiceInfo(self) -> Optional[Any]: - return None - - def getComponent(self) -> Optional[Any]: - return self._component - - def loadLabel(self, pm: Any) -> str: - return self._label or "Wallpaper" - - def loadDescription(self, pm: Any) -> str: - return self._description or "" - - def loadAuthor(self, pm: Any) -> str: - return self._author or "" - - def loadContextDescription(self, pm: Any) -> str: - return self._context_description or "" - - def loadIcon(self, pm: Any) -> Optional[Any]: - return None - - def loadThumbnail(self, pm: Any) -> Optional[Any]: - return self._thumbnail - - def getSettingsActivity(self) -> Optional[str]: - return self._settings_activity - - def getSettingsSliceUri(self) -> Optional[str]: - return None - - def getSharedLibrary(self) -> str: - return self._shared_library - - def getShowMetadataInPreview(self) -> bool: - return self._show_metadata_in_preview - - def isSystemWallpaper(self) -> bool: - return self._system_wallpaper - - def supportsAmbientMode(self) -> bool: - return self._supports_ambient_mode - - def toString(self) -> str: - return f"WallpaperInfo(package={self._package_name}, service={self._service_name})" - - -# ═══════════════════════════════════════════════════════════════ -# WallpaperService -# ═══════════════════════════════════════════════════════════════ - -class WallpaperService(Stub): - """WallpaperService — 动态壁纸服务基类""" - - SERVICE_INTERFACE = "android.service.wallpaper.WallpaperService" - SERVICE_META_DATA = "android.service.wallpaper" - WALLPAPER_HEIGHT = WALLPAPER_HEIGHT - WALLPAPER_WIDTH = WALLPAPER_WIDTH - - def __init__(self): - super().__init__() - self._engine: Optional["WallpaperService.Engine"] = None - self._created = False - self._wallpaper_connected = False - - def onCreate(self) -> None: - self._created = True - - def onDestroy(self) -> None: - self._created = False - self._wallpaper_connected = False - - def onCreateEngine(self) -> Optional["WallpaperService.Engine"]: - return None - - def onCreateSurfaceHolder(self) -> Optional[Any]: - return None - - def getWallpaperEngine(self) -> Optional["WallpaperService.Engine"]: - return self._engine - - def toString(self) -> str: - return f"WallpaperService(created={self._created})" - - # ═══════════════════════════════════════════════════════════ - # WallpaperService.Engine - # ═══════════════════════════════════════════════════════════ - - class Engine(Stub): - """WallpaperService.Engine — 动态壁纸引擎""" - - def __init__(self, service: "WallpaperService"): - super().__init__() - self._service = service - self._is_visible = False - self._is_preview = False - self._surface_holder: Optional[Any] = None - self._surface_created = False - self._surface_changed = False - self._surface_destroyed = False - self._offsets_changed = False - self._desired_width = WALLPAPER_WIDTH - self._desired_height = WALLPAPER_HEIGHT - self._x_offset = 0.5 - self._y_offset = 0.5 - self._x_step = 0.0 - self._y_step = 0.0 - self._visible = True - self._touch_events_enabled = True - self._mSurfaceCreated = False - self._mSurfaceValid = False - self._mDrawingAllowed = False - self._mOffsetMessageEnqueued = False - self._mZoom = 1.0 - self._mZoomOut = 1.0 - self._mOffsetRequired = False - self._mOffsetsChanged = False - self._mSurfaceChanged = False - self._mCreated = True - self._mSurfaceDestroyed = False - - def getSurfaceHolder(self) -> Optional[Any]: - return self._surface_holder - - def isVisible(self) -> bool: - return self._is_visible - - def isPreview(self) -> bool: - return self._is_preview - - def setTouchEventsEnabled(self, enabled: bool) -> None: - self._touch_events_enabled = enabled - - def getTouchEventsEnabled(self) -> bool: - return self._touch_events_enabled - - def setOffsetNotificationsEnabled(self, enabled: bool) -> None: - pass - - def onCreate(self, surfaceHolder: Any) -> None: - self._surface_holder = surfaceHolder - self._surface_created = True - - def onDestroy(self) -> None: - self._surface_destroyed = True - - def onVisibilityChanged(self, visible: bool) -> None: - self._is_visible = visible - self._visible = visible - - def onTouchEvent(self, event: Any) -> None: - pass - - def onSurfaceChanged(self, holder: Any, format: int, width: int, height: int) -> None: - self._desired_width = width - self._desired_height = height - self._surface_changed = True - - def onSurfaceCreated(self, holder: Any) -> None: - self._surface_created = True - self._mSurfaceCreated = True - self._mSurfaceValid = True - - def onSurfaceDestroyed(self, holder: Any) -> None: - self._surface_destroyed = True - self._mSurfaceDestroyed = True - self._mSurfaceValid = False - - def onSurfaceRedrawNeeded(self, holder: Any) -> None: - pass - - def onOffsetsChanged( - self, - xOffset: float, - yOffset: float, - xOffsetStep: float, - yOffsetStep: float, - xPixelOffset: int, - yPixelOffset: int, - ) -> None: - self._x_offset = xOffset - self._y_offset = yOffset - self._x_step = xOffsetStep - self._y_step = yOffsetStep - - def onDesiredSizeChanged(self, desiredWidth: int, desiredHeight: int) -> None: - self._desired_width = desiredWidth - self._desired_height = desiredHeight - - def onZoomChanged(self, zoom: float) -> None: - self._mZoom = zoom - - def setZoomOut(self, zoom: float) -> None: - self._mZoomOut = zoom - - def getZoomOut(self) -> float: - return self._mZoomOut - - def notifyZoomChanged(self) -> None: - pass - - def notifyColorsChanged(self) -> None: - pass - - def getDesiredMinimumWidth(self) -> int: - return self._desired_width - - def getDesiredMinimumHeight(self) -> int: - return self._desired_height - - def getXOffset(self) -> float: - return self._x_offset - - def getYOffset(self) -> float: - return self._y_offset - - def getXStep(self) -> float: - return self._x_step - - def getYStep(self) -> float: - return self._y_step - - def isCreated(self) -> bool: - return self._mCreated - - def isSurfaceCreated(self) -> bool: - return self._mSurfaceCreated - - def isSurfaceValid(self) -> bool: - return self._mSurfaceValid - - def isVisible(self) -> bool: - return self._visible - - def getSurfaceHolder(self) -> Optional[Any]: - return self._surface_holder - - def getService(self) -> "WallpaperService": - return self._service - - def toString(self) -> str: - return ( - f"WallpaperService.Engine(visible={self._visible}, " - f"surface_created={self._mSurfaceCreated}, " - f"zoom={self._mZoom})" - ) \ No newline at end of file diff --git a/android_pyapi/android/appwidget.py b/android_pyapi/android/appwidget.py deleted file mode 100644 index 91fda3e..0000000 --- a/android_pyapi/android/appwidget.py +++ /dev/null @@ -1,360 +0,0 @@ -""" -Android App Widget API — AppWidgetProvider, AppWidgetManager, AppWidgetProviderInfo, AppWidgetHost, AppWidgetHostView -方法签名完全对齐 Android SDK -""" - -import sys -from typing import Any, Dict, List, Optional, Set, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "AppWidgetProvider", - "AppWidgetManager", - "AppWidgetProviderInfo", - "AppWidgetHost", - "AppWidgetHostView", -] - - -# ═══════════════════════════════════════════════════════════════ -# AppWidgetProvider -# ═══════════════════════════════════════════════════════════════ - -class AppWidgetProvider(Stub): - """AppWidgetProvider — 应用小部件提供者""" - - def __init__(self): - super().__init__() - - def onUpdate(self, context: Any, appWidgetManager: "AppWidgetManager", appWidgetIds: List[int]): - """小部件更新回调""" - pass - - def onDeleted(self, context: Any, appWidgetIds: List[int]): - """小部件删除回调""" - pass - - def onEnabled(self, context: Any): - """首个小部件启用回调""" - pass - - def onDisabled(self, context: Any): - """最后一个小部件禁用回调""" - pass - - def onAppWidgetOptionsChanged( - self, context: Any, appWidgetManager: "AppWidgetManager", - appWidgetId: int, newOptions: Dict[str, Any] - ): - """小部件选项变更回调""" - pass - - def onReceive(self, context: Any, intent: Any): - """接收广播回调""" - pass - - def onRestored(self, context: Any, oldWidgetIds: List[int], newWidgetId: int): - """小部件恢复回调""" - pass - - def onProviderChanged( - self, context: Any, appWidgetManager: "AppWidgetManager", - appWidgetId: int, newInfo: "AppWidgetProviderInfo" - ): - """提供者变更回调""" - pass - - def toString(self) -> str: - return f"AppWidgetProvider@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AppWidgetManager -# ═══════════════════════════════════════════════════════════════ - -class AppWidgetManager(Stub): - """AppWidgetManager — 应用小部件管理器 (桌面存根)""" - - ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE" - ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED" - ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED" - ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED" - ACTION_APPWIDGET_OPTIONS_CHANGED = "android.appwidget.action.APPWIDGET_OPTIONS_CHANGED" - ACTION_APPWIDGET_RESTORED = "android.appwidget.action.APPWIDGET_RESTORED" - ACTION_APPWIDGET_HOST_RESTORED = "android.appwidget.action.APPWIDGET_HOST_RESTORED" - EXTRA_APPWIDGET_ID = "appWidgetId" - EXTRA_APPWIDGET_IDS = "appWidgetIds" - EXTRA_APPWIDGET_OLD_IDS = "appWidgetOldIds" - EXTRA_APPWIDGET_OPTIONS = "appWidgetOptions" - EXTRA_APPWIDGET_PROVIDER = "appWidgetProvider" - EXTRA_APPWIDGET_PROVIDER_PROFILE = "appWidgetProviderProfile" - WIDGET_CATEGORY_HOME_SCREEN = 1 - WIDGET_CATEGORY_KEYGUARD = 2 - WIDGET_CATEGORY_SEARCHBOX = 4 - OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth" - OPTION_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight" - OPTION_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth" - OPTION_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._widget_views: Dict[int, Any] = {} - self._widget_options: Dict[int, Dict[str, Any]] = {} - self._providers: List["AppWidgetProviderInfo"] = [] - self._next_widget_id = 1 - - @staticmethod - def getInstance(context: Any) -> "AppWidgetManager": - """获取 AppWidgetManager 实例""" - return AppWidgetManager(context) - - def getAppWidgetIds(self, info: "AppWidgetProviderInfo") -> List[int]: - """获取指定提供者的小部件 ID 列表""" - return list(self._widget_views.keys()) - - def updateAppWidget(self, appWidgetId: int, view: Any): - """更新小部件视图""" - self._widget_views[appWidgetId] = view - - def updateAppWidgetOptions(self, appWidgetId: int, options: Dict[str, Any]): - """更新小部件选项""" - if appWidgetId in self._widget_options: - self._widget_options[appWidgetId].update(options) - else: - self._widget_options[appWidgetId] = dict(options) - - def getAppWidgetOptions(self, appWidgetId: int) -> Dict[str, Any]: - """获取小部件选项""" - return self._widget_options.get(appWidgetId, {}) - - def bindAppWidgetIdIfAllowed( - self, appWidgetId: int, info: "AppWidgetProviderInfo" - ) -> bool: - """绑定小部件 ID(如允许)""" - return True - - def getAppWidgetInfo(self, appWidgetId: int) -> Optional["AppWidgetProviderInfo"]: - """获取小部件信息""" - if appWidgetId in self._widget_views: - return self._providers[0] if self._providers else None - return None - - def getInstalledProviders(self) -> List["AppWidgetProviderInfo"]: - """获取已安装的提供者列表""" - return list(self._providers) - - def isBindAppWidgetIdAllowed(self, appWidgetId: int, info: "AppWidgetProviderInfo") -> bool: - """检查是否允许绑定小部件 ID""" - return True - - def createAppWidgetConfigIntentSender( - self, info: "AppWidgetProviderInfo", appWidgetId: int - ) -> Optional[Any]: - """创建小部件配置 IntentSender""" - return None - - def requestPinAppWidget( - self, info: "AppWidgetProviderInfo", successCallback: Any = None - ) -> bool: - """请求固定小部件""" - return True - - def getAppWidgetHostViews(self, host: "AppWidgetHost") -> List["AppWidgetHostView"]: - """获取主机视图列表""" - return [] - - def toString(self) -> str: - return f"AppWidgetManager(widgets={len(self._widget_views)})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AppWidgetProviderInfo -# ═══════════════════════════════════════════════════════════════ - -class AppWidgetProviderInfo(Stub): - """AppWidgetProviderInfo — 小部件提供者信息""" - - # --- ResizeMode --- - RESIZE_MODE_NONE = 0 - RESIZE_MODE_HORIZONTAL = 1 - RESIZE_MODE_VERTICAL = 2 - RESIZE_MODE_RESIZE_ALL = 3 - - # --- WidgetCategory --- - WIDGET_CATEGORY_HOME_SCREEN = 1 - WIDGET_CATEGORY_KEYGUARD = 2 - WIDGET_CATEGORY_SEARCHBOX = 4 - - # --- WidgetFeatures --- - WIDGET_FEATURE_HIDE_FROM_PICKER = 1 - WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 2 - WIDGET_FEATURE_RECONFIGURABLE = 4 - - def __init__(self): - super().__init__() - self.provider: Any = None - self.configure: Any = None - self.icon: int = 0 - self.label: str = "" - self.previewImage: int = 0 - self.previewLayout: int = 0 - self.autoAdvanceViewId: int = -1 - self.resizeMode: int = self.RESIZE_MODE_NONE - self.widgetCategory: int = self.WIDGET_CATEGORY_HOME_SCREEN - self.widgetFeatures: int = 0 - self.description: str = "" - self.targetCellWidth: int = 0 - self.targetCellHeight: int = 0 - self.minWidth: int = 0 - self.minHeight: int = 0 - self.maxWidth: int = 0 - self.maxHeight: int = 0 - self.minResizeWidth: int = 0 - self.minResizeHeight: int = 0 - self.updatePeriodMillis: int = 0 - self.initialLayout: int = 0 - - def getProfile(self) -> Optional[Any]: - """获取配置文件""" - return None - - def getActivityInfo(self) -> Optional[Any]: - """获取 Activity 信息""" - return None - - def getIcon(self) -> int: - return self.icon - - def getLabel(self) -> str: - return self.label - - def loadPreviewImage(self, context: Any, density: int) -> Any: - """加载预览图像""" - return None - - def toString(self) -> str: - return f"AppWidgetProviderInfo(label={self.label}, icon={self.icon})" - - def equals(self, other) -> bool: - if not isinstance(other, AppWidgetProviderInfo): - return False - return self.provider == other.provider and self.label == other.label - - def hashCode(self) -> int: - return hash((str(self.provider), self.label)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AppWidgetHost -# ═══════════════════════════════════════════════════════════════ - -class AppWidgetHost(Stub): - """AppWidgetHost — 小部件主机""" - - def __init__(self, context: Any, hostId: int): - super().__init__() - self._context = context - self._host_id = hostId - self._widget_ids: List[int] = [] - self._listening = False - - def createView( - self, context: Any, appWidgetId: int, info: "AppWidgetProviderInfo" - ) -> "AppWidgetHostView": - """创建小部件视图""" - view = AppWidgetHostView(context) - view.setAppWidget(appWidgetId, info) - return view - - def startListening(self): - """开始监听""" - self._listening = True - - def stopListening(self): - """停止监听""" - self._listening = False - - def allocateAppWidgetId(self) -> int: - """分配小部件 ID""" - widget_id = len(self._widget_ids) + 1000 + self._host_id - self._widget_ids.append(widget_id) - return widget_id - - def deleteAppWidgetId(self, appWidgetId: int): - """删除小部件 ID""" - if appWidgetId in self._widget_ids: - self._widget_ids.remove(appWidgetId) - - def deleteHost(self): - """删除主机""" - self._widget_ids.clear() - - def clearViews(self): - """清除所有视图""" - self._widget_ids.clear() - - def toString(self) -> str: - return f"AppWidgetHost(hostId={self._host_id}, widgets={len(self._widget_ids)})" - - def equals(self, other) -> bool: - if not isinstance(other, AppWidgetHost): - return False - return self._host_id == other._host_id - - def hashCode(self) -> int: - return self._host_id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AppWidgetHostView -# ═══════════════════════════════════════════════════════════════ - -class AppWidgetHostView(Stub): - """AppWidgetHostView — 小部件主机视图""" - - def __init__(self, context: Any): - super().__init__() - self._context = context - self._app_widget_id = -1 - self._info: Optional["AppWidgetProviderInfo"] = None - - def setAppWidget(self, appWidgetId: int, info: "AppWidgetProviderInfo"): - """设置小部件""" - self._app_widget_id = appWidgetId - self._info = info - - def updateAppWidget(self, info: "AppWidgetProviderInfo"): - """更新小部件""" - self._info = info - - def getAppWidgetInfo(self) -> Optional["AppWidgetProviderInfo"]: - return self._info - - def toString(self) -> str: - return f"AppWidgetHostView(widgetId={self._app_widget_id})" - - def equals(self, other) -> bool: - if not isinstance(other, AppWidgetHostView): - return False - return self._app_widget_id == other._app_widget_id - - def hashCode(self) -> int: - return self._app_widget_id & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/bluetooth.py b/android_pyapi/android/bluetooth.py deleted file mode 100644 index 6c75710..0000000 --- a/android_pyapi/android/bluetooth.py +++ /dev/null @@ -1,849 +0,0 @@ -""" -Android Bluetooth 组件 — BluetoothAdapter, BluetoothDevice, BluetoothGatt 等 -方法签名完全对齐 Android SDK -""" - -import os -import sys -import uuid -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) - -# Linux 真实实现导入 -try: - from .._linux_impl import IS_LINUX, LinuxBluetooth, HAS_BLUETOOTHCTL - _USE_LINUX = IS_LINUX -except ImportError: - _USE_LINUX = False - - -# ═══════════════════════════════════════════════════════════════ -# BluetoothProfile -# ═══════════════════════════════════════════════════════════════ - -class BluetoothProfile(Stub): - """BluetoothProfile — 蓝牙配置档案""" - - A2DP = 2 - HEALTH = 3 - HEADSET = 1 - GATT = 7 - GATT_SERVER = 8 - A2DP_SINK = 11 - AVRC = 12 - HEARING_AID = 21 - LE_AUDIO = 22 - LE_AUDIO_BROADCAST = 26 - - STATE_DISCONNECTED = 0 - STATE_CONNECTING = 1 - STATE_CONNECTED = 2 - STATE_DISCONNECTING = 3 - - def getConnectedDevices(self) -> List[Any]: - return [] - - def getDevicesMatchingConnectionState(self, state: int) -> List[Any]: - return [] - - def getConnectionState(self, device: Any) -> int: - return self.STATE_DISCONNECTED - - -class BluetoothProfileServiceListener(Stub): - """BluetoothProfile.ServiceListener""" - - def onServiceConnected(self, profile_id: int, proxy: Any): - pass - - def onServiceDisconnected(self, profile_id: int): - pass - - -# ═══════════════════════════════════════════════════════════════ -# BluetoothClass -# ═══════════════════════════════════════════════════════════════ - -class BluetoothClass(Stub): - """BluetoothClass — 蓝牙设备类别""" - - class Device(Stub): - COMPUTER_DESKTOP = 0x100 - COMPUTER_LAPTOP = 0x10C - PHONE_SMART = 0x200 - PHONE_UNCATEGORIZED = 0x204 - AUDIO_VIDEO_WEARABLE_HEADSET = 0x404 - AUDIO_VIDEO_HANDSFREE = 0x408 - - class Major(Stub): - MISC = 0x0000 - COMPUTER = 0x0100 - PHONE = 0x0200 - NETWORKING = 0x0300 - AUDIO_VIDEO = 0x0400 - PERIPHERAL = 0x0500 - IMAGING = 0x0600 - WEARABLE = 0x0700 - TOY = 0x0800 - HEALTH = 0x0900 - UNCATEGORIZED = 0x1F00 - - def __init__(self): - super().__init__() - self._device_class = 0 - self._major_class = 0 - - def getDeviceClass(self) -> int: - return self._device_class - - def getMajorDeviceClass(self) -> int: - return self._major_class - - def hasService(self, service: int) -> bool: - return False - - def toString(self) -> str: - return f"BluetoothClass(device=0x{self._device_class:04X})" - - -# ═══════════════════════════════════════════════════════════════ -# BluetoothSocket -# ═══════════════════════════════════════════════════════════════ - -class BluetoothSocket(Stub): - """BluetoothSocket — 蓝牙套接字""" - - TYPE_RFCOMM = 1 - TYPE_L2CAP = 3 - TYPE_SCO = 2 - - def __init__(self, type_: int = TYPE_RFCOMM): - super().__init__() - self._type = type_ - self._closed = False - self._connected = False - self._device: Optional["BluetoothDevice"] = None - - def connect(self): - self._connected = True - - def bind(self): - pass - - def getInputStream(self) -> Any: - return None - - def getOutputStream(self) -> Any: - return None - - def close(self): - self._closed = True - self._connected = False - - def getRemoteDevice(self) -> Optional["BluetoothDevice"]: - return self._device - - def getMaxReceivePacketSize(self) -> int: - return 0 - - def isConnected(self) -> bool: - return self._connected - - def isClosed(self) -> bool: - return self._closed - - -class BluetoothServerSocket(Stub): - """BluetoothServerSocket — 蓝牙服务端套接字""" - - def __init__(self): - super().__init__() - self._closed = False - - def accept(self, timeout: int = 0) -> Optional[BluetoothSocket]: - return None - - def close(self): - self._closed = True - - -# ═══════════════════════════════════════════════════════════════ -# BluetoothDevice -# ═══════════════════════════════════════════════════════════════ - -class BluetoothDevice(Stub): - """BluetoothDevice — 蓝牙设备""" - - BOND_NONE = 10 - BOND_BONDING = 11 - BOND_BONDED = 12 - - DEVICE_TYPE_DUAL = 3 - DEVICE_TYPE_LE = 2 - DEVICE_TYPE_BREDR = 1 - DEVICE_TYPE_UNKNOWN = 0 - - ACTION_FOUND = "android.bluetooth.device.action.FOUND" - ACTION_NAME_CHANGED = "android.bluetooth.device.action.NAME_CHANGED" - ACTION_BOND_STATE_CHANGED = "android.bluetooth.device.action.BOND_STATE_CHANGED" - EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE" - EXTRA_NAME = "android.bluetooth.device.extra.NAME" - EXTRA_RSSI = "android.bluetooth.device.extra.RSSI" - EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE" - EXTRA_PREVIOUS_BOND_STATE = "android.bluetooth.device.extra.PREVIOUS_BOND_STATE" - - def __init__(self, address: str = "00:00:00:00:00:00"): - super().__init__() - self._address = address - self._name = "Unknown" - self._bond_state = self.BOND_NONE - self._device_type = self.DEVICE_TYPE_UNKNOWN - - def getAddress(self) -> str: - return self._address - - def getName(self) -> str: - return self._name - - def setName(self, name: str): - self._name = name - - def getBondState(self) -> int: - return self._bond_state - - def setBondState(self, state: int): - self._bond_state = state - - def getType(self) -> int: - return self._device_type - - def untergehende(self) -> bool: - return self._bond_state == self.BOND_BONDED - - def createRfcommSocketToServiceRecord(self, uuid_str: str) -> BluetoothSocket: - sock = BluetoothSocket(BluetoothSocket.TYPE_RFCOMM) - sock._device = self - return sock - - def createInsecureRfcommSocketToServiceRecord(self, uuid_str: str) -> BluetoothSocket: - sock = BluetoothSocket(BluetoothSocket.TYPE_RFCOMM) - sock._device = self - return sock - - def createL2capChannel(self, psm: int) -> BluetoothSocket: - sock = BluetoothSocket(BluetoothSocket.TYPE_L2CAP) - sock._device = self - return sock - - def createInsecureL2capChannel(self, psm: int) -> BluetoothSocket: - sock = BluetoothSocket(BluetoothSocket.TYPE_L2CAP) - sock._device = self - return sock - - def fetchUuidsWithSdp(self) -> bool: - return False - - def uuidEquals(self, uuid_str: str) -> bool: - return False - - def setPairingConfirmation(self, accept: bool): - pass - - def setPin(self, pin: bytes) -> bool: - return True - - def toString(self) -> str: - return f"BluetoothDevice(address={self._address}, name={self._name})" - - -# ═══════════════════════════════════════════════════════════════ -# BluetoothGattDescriptor -# ═══════════════════════════════════════════════════════════════ - -class BluetoothGattDescriptor(Stub): - """BluetoothGattDescriptor — GATT 描述符""" - - ENABLE_NOTIFICATION_VALUE = bytes([0x01, 0x00]) - ENABLE_INDICATION_VALUE = bytes([0x02, 0x00]) - DISABLE_NOTIFICATION_VALUE = bytes([0x00, 0x00]) - - def __init__(self, uuid_str: str, permissions: int = 0): - super().__init__() - self._uuid = uuid_str - self._value: Optional[bytes] = None - self._permissions = permissions - self._characteristic: Optional["BluetoothGattCharacteristic"] = None - - def getUuid(self) -> str: - return self._uuid - - def getValue(self) -> Optional[bytes]: - return self._value - - def setValue(self, value: bytes): - self._value = value - - def getCharacteristic(self) -> Optional["BluetoothGattCharacteristic"]: - return self._characteristic - - def getPermissions(self) -> int: - return self._permissions - - -# ═══════════════════════════════════════════════════════════════ -# BluetoothGattCharacteristic -# ═══════════════════════════════════════════════════════════════ - -class BluetoothGattCharacteristic(Stub): - """BluetoothGattCharacteristic — GATT 特征""" - - PROPERTY_BROADCAST = 1 - PROPERTY_READ = 2 - PROPERTY_WRITE_NO_RESPONSE = 4 - PROPERTY_WRITE = 8 - PROPERTY_NOTIFY = 16 - PROPERTY_INDICATE = 32 - PROPERTY_SIGNED_WRITE = 64 - PROPERTY_EXTENDED_PROPS = 128 - - WRITE_TYPE_NO_RESPONSE = 1 - WRITE_TYPE_DEFAULT = 2 - WRITE_TYPE_SIGNED = 4 - - PERMISSION_READ = 1 - PERMISSION_READ_ENCRYPTED = 2 - PERMISSION_READ_ENCRYPTED_MITM = 4 - PERMISSION_WRITE = 16 - PERMISSION_WRITE_ENCRYPTED = 32 - PERMISSION_WRITE_ENCRYPTED_MITM = 64 - - FORMAT_FLOAT = 0x20 - FORMAT_SFLOAT = 0x10 - FORMAT_SINT16 = 0x02 - FORMAT_SINT32 = 0x04 - FORMAT_SINT8 = 0x01 - FORMAT_UINT16 = 0x12 - FORMAT_UINT32 = 0x14 - FORMAT_UINT8 = 0x11 - - def __init__(self, uuid_str: str, properties: int = 0, permissions: int = 0): - super().__init__() - self._uuid = uuid_str - self._properties = properties - self._permissions = permissions - self._write_type = self.WRITE_TYPE_DEFAULT - self._value: Optional[bytes] = None - self._service: Optional["BluetoothGattService"] = None - self._descriptors: List[BluetoothGattDescriptor] = [] - - def getUuid(self) -> str: - return self._uuid - - def getService(self) -> Optional["BluetoothGattService"]: - return self._service - - def getPermissions(self) -> int: - return self._permissions - - def getProperties(self) -> int: - return self._properties - - def getWriteType(self) -> int: - return self._write_type - - def setWriteType(self, write_type: int): - self._write_type = write_type - - def getValue(self) -> Optional[bytes]: - return self._value - - def setValue(self, value: bytes): - self._value = value - - def getDescriptors(self) -> List[BluetoothGattDescriptor]: - return list(self._descriptors) - - def getDescriptor(self, uuid_str: str) -> Optional[BluetoothGattDescriptor]: - for d in self._descriptors: - if d.getUuid() == uuid_str: - return d - return None - - def addDescriptor(self, descriptor: BluetoothGattDescriptor): - self._descriptors.append(descriptor) - descriptor._characteristic = self - - def getIntValue(self, format_type: int, offset: int) -> Optional[int]: - if self._value is None: - return None - import struct - try: - fmt_map = { - self.FORMAT_UINT8: ("B", 1), self.FORMAT_SINT8: ("b", 1), - self.FORMAT_UINT16: ("H", 2), self.FORMAT_SINT16: ("h", 2), - self.FORMAT_UINT32: ("I", 4), self.FORMAT_SINT32: ("i", 4), - self.FORMAT_FLOAT: ("f", 4), self.FORMAT_SFLOAT: ("e", 2), - } - fmt, size = fmt_map.get(format_type, ("B", 1)) - if offset + size > len(self._value): - return None - val = struct.unpack(fmt, self._value[offset:offset + size])[0] - return int(val) - except Exception: - return None - - -# ═══════════════════════════════════════════════════════════════ -# BluetoothGattService -# ═══════════════════════════════════════════════════════════════ - -class BluetoothGattService(Stub): - """BluetoothGattService — GATT 服务""" - - SERVICE_TYPE_PRIMARY = 0 - SERVICE_TYPE_SECONDARY = 1 - - def __init__(self, uuid_str: str, service_type: int = SERVICE_TYPE_PRIMARY): - super().__init__() - self._uuid = uuid_str - self._type = service_type - self._characteristics: List[BluetoothGattCharacteristic] = [] - self._included_services: List["BluetoothGattService"] = [] - self._device: Optional[BluetoothDevice] = None - - def getUuid(self) -> str: - return self._uuid - - def getType(self) -> int: - return self._type - - def getCharacteristics(self) -> List[BluetoothGattCharacteristic]: - return list(self._characteristics) - - def getCharacteristic(self, uuid_str: str) -> Optional[BluetoothGattCharacteristic]: - for c in self._characteristics: - if c.getUuid() == uuid_str: - return c - return None - - def addCharacteristic(self, characteristic: BluetoothGattCharacteristic): - self._characteristics.append(characteristic) - characteristic._service = self - - def getIncludedServices(self) -> List["BluetoothGattService"]: - return list(self._included_services) - - def addIncludedService(self, service: "BluetoothGattService"): - self._included_services.append(service) - - def getDevice(self) -> Optional[BluetoothDevice]: - return self._device - - -# ═══════════════════════════════════════════════════════════════ -# BluetoothGattCallback -# ═══════════════════════════════════════════════════════════════ - -class BluetoothGattCallback(Stub): - """BluetoothGattCallback — GATT 回调""" - - def onConnectionStateChange(self, gatt: "BluetoothGatt", status: int, new_state: int): - pass - - def onServicesDiscovered(self, gatt: "BluetoothGatt", status: int): - pass - - def onCharacteristicRead(self, gatt: "BluetoothGatt", - characteristic: BluetoothGattCharacteristic, value: bytes, status: int): - pass - - def onCharacteristicWrite(self, gatt: "BluetoothGatt", - characteristic: BluetoothGattCharacteristic, status: int): - pass - - def onDescriptorRead(self, gatt: "BluetoothGatt", - descriptor: BluetoothGattDescriptor, status: int): - pass - - def onDescriptorWrite(self, gatt: "BluetoothGatt", - descriptor: BluetoothGattDescriptor, status: int): - pass - - def onCharacteristicChanged(self, gatt: "BluetoothGatt", - characteristic: BluetoothGattCharacteristic, value: bytes): - pass - - def onReadRemoteRssy(self, gatt: "BluetoothGatt", rssi: int, status: int): - pass - - def onMtuChanged(self, gatt: "BluetoothGatt", mtu: int, status: int): - pass - - def onPhyUpdate(self, gatt: "BluetoothGatt", tx_phy: int, rx_phy: int, status: int): - pass - - def onPhyRead(self, gatt: "BluetoothGatt", tx_phy: int, rx_phy: int, status: int): - pass - - def onServiceChanged(self, gatt: "BluetoothGatt"): - pass - - def onNotificationSent(self, gatt: "BluetoothGatt", status: int): - pass - - def onConnectionUpdated(self, gatt: "BluetoothGatt", interval: int, - latency: int, timeout: int, status: int): - pass - - -# ═══════════════════════════════════════════════════════════════ -# BluetoothGatt -# ═══════════════════════════════════════════════════════════════ - -class BluetoothGatt(Stub): - """BluetoothGatt — GATT 客户端""" - - CONNECTION_STATE_DISCONNECTED = 0 - CONNECTION_STATE_CONNECTING = 1 - CONNECTION_STATE_CONNECTED = 2 - CONNECTION_STATE_DISCONNECTING = 3 - - GATT_SUCCESS = 0 - GATT_FAILURE = 1 - GATT_CONNECTION_CONGESTED = 143 - GATT_INSUFFICIENT_AUTHENTICATION = 5 - GATT_INSUFFICIENT_ENCRYPTION = 15 - GATT_INSUFFICIENT_KEY_SIZE = 12 - GATT_INVALID_ATTRIBUTE_LENGTH = 13 - GATT_REQUEST_NOT_SUPPORTED = 6 - GATT_WRITE_NOT_PERMITTED = 3 - GATT_READ_NOT_PERMITTED = 2 - GATT_WRITE_NO_RESPONSE = 1 - GATT_WRITE_DEFAULT = 0 - - def __init__(self, device: BluetoothDevice): - super().__init__() - self._device = device - self._services: List[BluetoothGattService] = [] - self._connected = False - self._mtu = 23 - - def connect(self) -> bool: - self._connected = True - return True - - def disconnect(self): - self._connected = False - - def close(self): - self._connected = False - self._services.clear() - - def discoverServices(self) -> bool: - return True - - def getServices(self) -> List[BluetoothGattService]: - return list(self._services) - - def getService(self, uuid_str: str) -> Optional[BluetoothGattService]: - for s in self._services: - if s.getUuid() == uuid_str: - return s - return None - - def getConnectedDevices(self) -> List[BluetoothDevice]: - return [self._device] if self._connected else [] - - def getDevicesMatchingConnectionState(self, state: int) -> List[BluetoothDevice]: - if state == self.CONNECTION_STATE_CONNECTED and self._connected: - return [self._device] - return [] - - def readCharacteristic(self, characteristic: BluetoothGattCharacteristic) -> bool: - return True - - def writeCharacteristic(self, characteristic: BluetoothGattCharacteristic) -> bool: - return True - - def readDescriptor(self, descriptor: BluetoothGattDescriptor) -> bool: - return True - - def writeDescriptor(self, descriptor: BluetoothGattDescriptor) -> bool: - return True - - def setCharacteristicNotification(self, characteristic: BluetoothGattCharacteristic, - enable: bool) -> bool: - return True - - def readRemoteRssy(self) -> bool: - return True - - def requestMtu(self, mtu: int) -> bool: - self._mtu = mtu - return True - - def getMtu(self) -> int: - return self._mtu - - def requestConnectionPriority(self, connection_priority: int) -> bool: - return True - - def getDevice(self) -> BluetoothDevice: - return self._device - - -# ═══════════════════════════════════════════════════════════════ -# BluetoothManager -# ═══════════════════════════════════════════════════════════════ - -class BluetoothManager(Stub): - """BluetoothManager — 蓝牙管理器""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - - def getAdapter(self) -> Optional["BluetoothAdapter"]: - return BluetoothAdapter.getDefaultAdapter() - - -# ═══════════════════════════════════════════════════════════════ -# BluetoothAdapter -# ═══════════════════════════════════════════════════════════════ - -class BluetoothAdapter(Stub): - """BluetoothAdapter — 蓝牙适配器""" - - STATE_OFF = 10 - STATE_TURNING_ON = 11 - STATE_ON = 12 - STATE_TURNING_OFF = 13 - - ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE" - ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE" - ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED" - ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED" - ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED" - - SCAN_MODE_NONE = 20 - SCAN_MODE_CONNECTABLE = 21 - SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23 - - _default_adapter: Optional["BluetoothAdapter"] = None - - def __init__(self): - super().__init__() - self._enabled = True - self._state = self.STATE_ON - self._address = "00:1A:2B:3C:4D:5E" - self._name = "MockBluetooth" - self._discovering = False - self._bonded_devices: List[BluetoothDevice] = [] - - @staticmethod - def getDefaultAdapter() -> Optional["BluetoothAdapter"]: - if BluetoothAdapter._default_adapter is None: - BluetoothAdapter._default_adapter = BluetoothAdapter() - return BluetoothAdapter._default_adapter - - def isEnabled(self) -> bool: - if _USE_LINUX: - return LinuxBluetooth.get_state() == LinuxBluetooth.STATE_ON - return self._enabled - - def enable(self) -> bool: - if _USE_LINUX: - return LinuxBluetooth.enable() - self._enabled = True - self._state = self.STATE_ON - return True - - def disable(self) -> bool: - if _USE_LINUX: - return LinuxBluetooth.disable() - self._enabled = False - self._state = self.STATE_OFF - return True - - def getAddress(self) -> str: - if _USE_LINUX: - addr = LinuxBluetooth.get_address() - if addr: - return addr - return self._address - - def getName(self) -> str: - if _USE_LINUX: - name = LinuxBluetooth.get_name() - if name: - return name - return self._name - - def setName(self, name: str) -> bool: - self._name = name - return True - - def getBondedDevices(self) -> Set[BluetoothDevice]: - if _USE_LINUX: - devices = set() - for d in LinuxBluetooth.get_bonded_devices(): - dev = BluetoothDevice(d.get("address", "00:00:00:00:00:00")) - dev._name = d.get("name", "Unknown") - dev._bond_state = BluetoothDevice.BOND_BONDED - devices.add(dev) - return devices - return set(self._bonded_devices) - - def getBondedDeviceAddresses(self) -> Set[str]: - """获取已配对设备地址集合""" - if _USE_LINUX: - return {d.get("address", "") for d in LinuxBluetooth.get_bonded_devices()} - return {d.getAddress() for d in self._bonded_devices} - - def startDiscovery(self) -> bool: - if _USE_LINUX: - try: - LinuxBluetooth.scan(timeout=5) - return True - except Exception: - return False - self._discovering = True - return True - - def cancelDiscovery(self) -> bool: - if _USE_LINUX: - # bluetoothctl scan off 在 LinuxBluetooth.scan 内部已处理 - return True - self._discovering = False - return True - - def isDiscovering(self) -> bool: - return self._discovering - - @staticmethod - def checkBluetoothAddress(address: str) -> bool: - import re - return bool(re.match(r"^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$", address)) - - def getProfileProxy(self, context: Any, listener: BluetoothProfileServiceListener, - profile_id: int) -> bool: - return True - - def closeProfileProxy(self, profile_id: int, proxy: Any): - pass - - def getState(self) -> int: - if _USE_LINUX: - return LinuxBluetooth.get_state() - return self._state - - def listenUsingRfcommWithServiceRecord(self, name: str, uuid_str: str) -> BluetoothServerSocket: - return BluetoothServerSocket() - - def listenUsingInsecureRfcommWithServiceRecord(self, name: str, - uuid_str: str) -> BluetoothServerSocket: - return BluetoothServerSocket() - - def getRemoteDevice(self, address: str) -> Optional[BluetoothDevice]: - if _USE_LINUX: - # 从已配对设备中查找 - for d in LinuxBluetooth.get_bonded_devices(): - if d.get("address", "").upper() == address.upper(): - dev = BluetoothDevice(d.get("address", address)) - dev._name = d.get("name", "Unknown") - dev._bond_state = BluetoothDevice.BOND_BONDED - return dev - # 未配对但地址合法则返回一个设备对象 - if BluetoothAdapter.checkBluetoothAddress(address): - return BluetoothDevice(address) - return None - if not BluetoothAdapter.checkBluetoothAddress(address): - return None - return BluetoothDevice(address) - - def getLeScanner(self) -> Any: - from .bluetooth_le import BluetoothLeScanner - return BluetoothLeScanner() - - def toString(self) -> str: - return f"BluetoothAdapter(name={self._name})" - - -# ═══════════════════════════════════════════════════════════════ -# Additional profile stubs -# ═══════════════════════════════════════════════════════════════ - -class BluetoothHeadset(Stub): - pass - - -class BluetoothA2dp(Stub): - pass - - -class BluetoothHealth(Stub): - pass - - -class BluetoothHealthCallback(Stub): - pass - - -class BluetoothHealthAppConfiguration(Stub): - pass - - -class BluetoothCodecConfig(Stub): - pass - - -class BluetoothLeAudio(Stub): - pass - - -class BluetoothLeBroadcast(Stub): - pass - - -class BluetoothLeBroadcastMetadata(Stub): - pass - - -class BluetoothLeBroadcastSubgroup(Stub): - pass - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "BluetoothAdapter", - "BluetoothDevice", - "BluetoothServerSocket", - "BluetoothSocket", - "BluetoothClass", - "BluetoothGatt", - "BluetoothGattCallback", - "BluetoothGattCharacteristic", - "BluetoothGattDescriptor", - "BluetoothGattService", - "BluetoothHeadset", - "BluetoothA2dp", - "BluetoothHealth", - "BluetoothHealthCallback", - "BluetoothHealthAppConfiguration", - "BluetoothProfile", - "BluetoothProfileServiceListener", - "BluetoothManager", - "BluetoothCodecConfig", - "BluetoothLeAudio", - "BluetoothLeBroadcast", - "BluetoothLeBroadcastMetadata", - "BluetoothLeBroadcastSubgroup", -] \ No newline at end of file diff --git a/android_pyapi/android/bluetooth_le.py b/android_pyapi/android/bluetooth_le.py deleted file mode 100644 index 0a8d311..0000000 --- a/android_pyapi/android/bluetooth_le.py +++ /dev/null @@ -1,512 +0,0 @@ -""" -Android BLE 组件 — BluetoothLeScanner, ScanCallback, ScanFilter 等 -方法签名完全对齐 Android SDK -""" - -import os -import sys -from typing import Any, Dict, List, Optional, Set, Tuple, Union - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) - - -# ═══════════════════════════════════════════════════════════════ -# ScanRecord -# ═══════════════════════════════════════════════════════════════ - -class ScanRecord(Stub): - """ScanRecord — 扫描记录""" - - def __init__(self): - super().__init__() - self._service_uuids: List[str] = [] - self._service_solicitation_uuids: List[str] = [] - self._service_data: Dict[str, bytes] = {} - self._manufacturer_specific_data: Dict[int, bytes] = {} - self._tx_power_level: int = -127 - self._device_name: str = "" - self._advertise_flags: int = 0 - self._bytes_data: bytes = b"" - - def getServiceUuids(self) -> Optional[List[str]]: - return list(self._service_uuids) if self._service_uuids else None - - def getServiceSolicitationUuids(self) -> Optional[List[str]]: - return list(self._service_solicitation_uuids) if self._service_solicitation_uuids else None - - def getServiceData(self) -> Optional[Dict[str, bytes]]: - return dict(self._service_data) if self._service_data else None - - def getServiceDataForService(self, uuid_str: str) -> Optional[bytes]: - return self._service_data.get(uuid_str) - - def getManufacturerSpecificData(self) -> Optional[Dict[int, bytes]]: - return dict(self._manufacturer_specific_data) if self._manufacturer_specific_data else None - - def getTxPowerLevel(self) -> int: - return self._tx_power_level - - def getDeviceName(self) -> Optional[str]: - return self._device_name or None - - def getAdvertiseFlags(self) -> int: - return self._advertise_flags - - def getBytes(self) -> bytes: - return self._bytes_data - - -# ═══════════════════════════════════════════════════════════════ -# ScanFilter -# ═══════════════════════════════════════════════════════════════ - -class ScanFilter(Stub): - """ScanFilter — 扫描过滤器""" - - class Builder(Stub): - def __init__(self): - super().__init__() - self._device_name: Optional[str] = None - self._device_address: Optional[str] = None - self._service_uuid: Optional[str] = None - self._service_data: Optional[bytes] = None - self._manufacturer_id: int = -1 - self._manufacturer_data: Optional[bytes] = None - - def setDeviceName(self, name: str) -> "ScanFilter.Builder": - self._device_name = name - return self - - def setDeviceAddress(self, address: str) -> "ScanFilter.Builder": - self._device_address = address - return self - - def setServiceUuid(self, uuid_str: str) -> "ScanFilter.Builder": - self._service_uuid = uuid_str - return self - - def setServiceData(self, data: bytes) -> "ScanFilter.Builder": - self._service_data = data - return self - - def setManufacturerData(self, manufacturer_id: int, data: bytes) -> "ScanFilter.Builder": - self._manufacturer_id = manufacturer_id - self._manufacturer_data = data - return self - - def build(self) -> "ScanFilter": - return ScanFilter( - device_name=self._device_name, - device_address=self._device_address, - service_uuid=self._service_uuid, - service_data=self._service_data, - manufacturer_id=self._manufacturer_id, - manufacturer_data=self._manufacturer_data, - ) - - def __init__(self, device_name: Optional[str] = None, - device_address: Optional[str] = None, - service_uuid: Optional[str] = None, - service_data: Optional[bytes] = None, - manufacturer_id: int = -1, - manufacturer_data: Optional[bytes] = None): - super().__init__() - self._device_name = device_name - self._device_address = device_address - self._service_uuid = service_uuid - self._service_data = service_data - self._manufacturer_id = manufacturer_id - self._manufacturer_data = manufacturer_data - - -class ScanFilterBuilder(ScanFilter.Builder): - """ScanFilter.Builder 别名""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# ScanSettings -# ═══════════════════════════════════════════════════════════════ - -class ScanSettings(Stub): - """ScanSettings — 扫描设置""" - - SCAN_MODE_OPPORTUNISTIC = -1 - SCAN_MODE_LOW_POWER = 0 - SCAN_MODE_BALANCED = 1 - SCAN_MODE_LOW_LATENCY = 2 - - CALLBACK_TYPE_ALL_MATCHES = 1 - CALLBACK_TYPE_FIRST_MATCH = 2 - CALLBACK_TYPE_MATCH_LOST = 4 - - MATCH_NUM_FEW_CFG = 2 - MATCH_NUM_MAX_CFG = 1 - MATCH_MODE_AGGRESSIVE = 0 - MATCH_MODE_STICKY = 1 - - class Builder(Stub): - def __init__(self): - super().__init__() - self._scan_mode = ScanSettings.SCAN_MODE_LOW_POWER - self._callback_type = ScanSettings.CALLBACK_TYPE_ALL_MATCHES - self._legacy = True - self._phy = 1 - - def setScanMode(self, scan_mode: int) -> "ScanSettings.Builder": - self._scan_mode = scan_mode - return self - - def setCallbackType(self, callback_type: int) -> "ScanSettings.Builder": - self._callback_type = callback_type - return self - - def setLegacy(self, legacy: bool) -> "ScanSettings.Builder": - self._legacy = legacy - return self - - def setPhy(self, phy: int) -> "ScanSettings.Builder": - self._phy = phy - return self - - def build(self) -> "ScanSettings": - return ScanSettings( - scan_mode=self._scan_mode, - callback_type=self._callback_type, - legacy=self._legacy, - phy=self._phy, - ) - - def __init__(self, scan_mode: int = SCAN_MODE_LOW_POWER, - callback_type: int = CALLBACK_TYPE_ALL_MATCHES, - legacy: bool = True, phy: int = 1): - super().__init__() - self._scan_mode = scan_mode - self._callback_type = callback_type - self._legacy = legacy - self._phy = phy - - def getScanMode(self) -> int: - return self._scan_mode - - def getCallbackType(self) -> int: - return self._callback_type - - def getLegacy(self) -> bool: - return self._legacy - - def getPhy(self) -> int: - return self._phy - - -# ═══════════════════════════════════════════════════════════════ -# ScanResult -# ═══════════════════════════════════════════════════════════════ - -class ScanResult(Stub): - """ScanResult — BLE 扫描结果""" - - DATA_COMPLETE = 0 - DATA_TRUNCATED = 2 - PHY_LE_1M = 1 - PHY_LE_2M = 2 - PHY_LE_CODED = 3 - PERIODIC_INTERVAL_NOT_PRESENT = 0 - - def __init__(self, device: Any = None, scan_record: Optional[ScanRecord] = None, - rssi: int = -127, timestamp_nanos: int = 0): - super().__init__() - self._device = device - self._scan_record = scan_record or ScanRecord() - self._rssi = rssi - self._timestamp_nanos = timestamp_nanos - self._primary_phy = self.PHY_LE_1M - self._secondary_phy = 0 - self._advertising_sid = -1 - self._tx_power = -127 - self._periodic_interval = self.PERIODIC_INTERVAL_NOT_PRESENT - self._data_status = self.DATA_COMPLETE - - def getDevice(self) -> Any: - return self._device - - def getScanRecord(self) -> Optional[ScanRecord]: - return self._scan_record - - def getRssi(self) -> int: - return self._rssi - - def getTimestampNanos(self) -> int: - return self._timestamp_nanos - - def getPrimaryPhy(self) -> int: - return self._primary_phy - - def getSecondaryPhy(self) -> int: - return self._secondary_phy - - def getAdvertisingSid(self) -> int: - return self._advertising_sid - - def getTxPower(self) -> int: - return self._tx_power - - def getPeriodicAdvertisingInterval(self) -> int: - return self._periodic_interval - - def getDataStatus(self) -> int: - return self._data_status - - -# ═══════════════════════════════════════════════════════════════ -# ScanCallback -# ═══════════════════════════════════════════════════════════════ - -class ScanCallback(Stub): - """ScanCallback — 扫描回调""" - - SCAN_FAILED_ALREADY_STARTED = 1 - SCAN_FAILED_APPLICATION_REGISTRATION_FAILED = 2 - SCAN_FAILED_INTERNAL_ERROR = 3 - SCAN_FAILED_FEATURE_UNSUPPORTED = 4 - SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5 - SCAN_FAILED_SCANNING_TOO_FREQUENTLY = 6 - - def onScanResult(self, callback_type: int, result: ScanResult): - pass - - def onBatchScanResults(self, results: List[ScanResult]): - pass - - def onScanFailed(self, error_code: int): - pass - - -# ═══════════════════════════════════════════════════════════════ -# BluetoothLeScanner -# ═══════════════════════════════════════════════════════════════ - -class BluetoothLeScanner(Stub): - """BluetoothLeScanner — BLE 扫描器""" - - def __init__(self): - super().__init__() - - def startScan(self, filters: Optional[List[ScanFilter]] = None, - settings: Optional[ScanSettings] = None, - callback: Optional[ScanCallback] = None): - pass - - def startScanWithFilters(self, filters: List[ScanFilter], - callback: ScanCallback): - self.startScan(filters, None, callback) - - def stopScan(self, callback: Optional[ScanCallback] = None): - pass - - def flushPendingScanResults(self, callback: ScanCallback): - pass - - -# ═══════════════════════════════════════════════════════════════ -# AdvertiseData / AdvertiseSettings / AdvertiseCallback -# ═══════════════════════════════════════════════════════════════ - -class AdvertiseData(Stub): - """AdvertiseData — 广播数据""" - - class Builder(Stub): - def __init__(self): - super().__init__() - self._include_device_name = False - self._include_tx_power_level = False - self._service_uuids: List[str] = [] - self._service_data: Dict[str, bytes] = {} - self._manufacturer_data: Dict[int, bytes] = {} - - def setIncludeDeviceName(self, include: bool) -> "AdvertiseData.Builder": - self._include_device_name = include - return self - - def setIncludeTxPowerLevel(self, include: bool) -> "AdvertiseData.Builder": - self._include_tx_power_level = include - return self - - def addServiceUuid(self, uuid_str: str) -> "AdvertiseData.Builder": - self._service_uuids.append(uuid_str) - return self - - def addServiceData(self, uuid_str: str, data: bytes) -> "AdvertiseData.Builder": - self._service_data[uuid_str] = data - return self - - def addManufacturerData(self, manufacturer_id: int, data: bytes) -> "AdvertiseData.Builder": - self._manufacturer_data[manufacturer_id] = data - return self - - def build(self) -> "AdvertiseData": - return AdvertiseData( - include_device_name=self._include_device_name, - include_tx_power_level=self._include_tx_power_level, - service_uuids=self._service_uuids, - service_data=self._service_data, - manufacturer_data=self._manufacturer_data, - ) - - def __init__(self, include_device_name: bool = False, - include_tx_power_level: bool = False, - service_uuids: Optional[List[str]] = None, - service_data: Optional[Dict[str, bytes]] = None, - manufacturer_data: Optional[Dict[int, bytes]] = None): - super().__init__() - self._include_device_name = include_device_name - self._include_tx_power_level = include_tx_power_level - self._service_uuids = service_uuids or [] - self._service_data = service_data or {} - self._manufacturer_data = manufacturer_data or {} - - -class AdvertiseDataBuilder(AdvertiseData.Builder): - pass - - -class AdvertiseSettings(Stub): - """AdvertiseSettings — 广播设置""" - - ADVERTISE_MODE_LOW_POWER = 0 - ADVERTISE_MODE_BALANCED = 1 - ADVERTISE_MODE_LOW_LATENCY = 2 - - ADVERTISE_TX_POWER_ULTRA_LOW = 0 - ADVERTISE_TX_POWER_LOW = 1 - ADVERTISE_TX_POWER_MEDIUM = 2 - ADVERTISE_TX_POWER_HIGH = 3 - - class Builder(Stub): - def __init__(self): - super().__init__() - self._advertise_mode = AdvertiseSettings.ADVERTISE_MODE_LOW_POWER - self._tx_power_level = AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM - self._connectable = True - self._timeout = 0 - - def setAdvertiseMode(self, mode: int) -> "AdvertiseSettings.Builder": - self._advertise_mode = mode - return self - - def setTxPowerLevel(self, power: int) -> "AdvertiseSettings.Builder": - self._tx_power_level = power - return self - - def setConnectable(self, connectable: bool) -> "AdvertiseSettings.Builder": - self._connectable = connectable - return self - - def setTimeout(self, timeout: int) -> "AdvertiseSettings.Builder": - self._timeout = timeout - return self - - def build(self) -> "AdvertiseSettings": - return AdvertiseSettings( - advertise_mode=self._advertise_mode, - tx_power_level=self._tx_power_level, - connectable=self._connectable, - timeout=self._timeout, - ) - - def __init__(self, advertise_mode: int = ADVERTISE_MODE_LOW_POWER, - tx_power_level: int = ADVERTISE_TX_POWER_MEDIUM, - connectable: bool = True, timeout: int = 0): - super().__init__() - self._advertise_mode = advertise_mode - self._tx_power_level = tx_power_level - self._connectable = connectable - self._timeout = timeout - - def getAdvertiseMode(self) -> int: - return self._advertise_mode - - def getTxPowerLevel(self) -> int: - return self._tx_power_level - - def isConnectable(self) -> bool: - return self._connectable - - def getTimeout(self) -> int: - return self._timeout - - -class AdvertiseCallback(Stub): - """AdvertiseCallback — 广播回调""" - - ADVERTISE_FAILED_DATA_TOO_LARGE = 1 - ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2 - ADVERTISE_FAILED_ALREADY_STARTED = 3 - ADVERTISE_FAILED_INTERNAL_ERROR = 4 - ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5 - - def onStartSuccess(self, settings_in_effect: AdvertiseSettings): - pass - - def onStartFailure(self, error_code: int): - pass - - -class BluetoothLeAdvertiser(Stub): - """BluetoothLeAdvertiser — BLE 广播器""" - - def startAdvertising(self, settings: AdvertiseSettings, - advertise_data: AdvertiseData, - scan_response: Optional[AdvertiseData], - callback: AdvertiseCallback): - try: - callback.onStartSuccess(settings) - except Exception: - pass - - def stopAdvertising(self, callback: AdvertiseCallback): - pass - - -class PeriodicAdvertisingManager(Stub): - pass - - -class PeriodicAdvertisingCallback(Stub): - pass - - -class DistanceMeasurementManager(Stub): - pass - - -class DistanceMeasurementResult(Stub): - pass - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "BluetoothLeScanner", - "ScanCallback", - "ScanResult", - "ScanFilter", - "ScanFilterBuilder", - "ScanSettings", - "ScanRecord", - "BluetoothLeAdvertiser", - "AdvertiseCallback", - "AdvertiseData", - "AdvertiseDataBuilder", - "AdvertiseSettings", - "PeriodicAdvertisingManager", - "PeriodicAdvertisingCallback", - "DistanceMeasurementManager", - "DistanceMeasurementResult", -] \ No newline at end of file diff --git a/android_pyapi/android/companion.py b/android_pyapi/android/companion.py deleted file mode 100644 index 569f8a3..0000000 --- a/android_pyapi/android/companion.py +++ /dev/null @@ -1,415 +0,0 @@ -""" -Android Companion Device API — CompanionDeviceManager, AssociationInfo, DeviceFilters -方法签名完全对齐 Android SDK -""" - -import sys -from typing import Any, Dict, List, Optional, Set, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "CompanionDeviceManager", - "AssociationInfo", - "WifiDeviceFilter", - "BluetoothDeviceFilter", - "BluetoothLeDeviceFilter", - "CommsDevice", - "DeviceActivityCallback", - "CompanionService", -] - - -# ═══════════════════════════════════════════════════════════════ -# CompanionDeviceManager -# ═══════════════════════════════════════════════════════════════ - -class CompanionDeviceManager(Stub): - """CompanionDeviceManager — 配套设备管理器 (桌面存根)""" - - EXTRA_DEVICE = "android.companion.extra.DEVICE" - EXTRA_ASSOCIATION = "android.companion.extra.ASSOCIATION" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._associations: Dict[str, "AssociationInfo"] = {} - self._callbacks: List[Callable] = [] - - def associate( - self, - request: Any, - callback: "CompanionDeviceManager.Callback", - handler: Any = None, - ): - """发起设备关联请求""" - raise AndroidOnlyError("CompanionDeviceManager.associate() 仅在 Android 设备上可用") - - def associateCallback( - self, - request: Any, - callback: Callable, - executor: Any = None, - ): - """使用回调方式发起设备关联""" - raise AndroidOnlyError("CompanionDeviceManager.associateCallback() 仅在 Android 设备上可用") - - def requestNotificationAccess(self, pairingDevice: "AssociationInfo"): - """请求通知访问权限""" - raise AndroidOnlyError("CompanionDeviceManager.requestNotificationAccess() 仅在 Android 设备上可用") - - def requestSystemAppStore(self, pairingDevice: "AssociationInfo"): - """请求系统应用商店""" - pass - - def startObservingDevicePresence(self, deviceAddress: str): - """开始监听设备存在""" - pass - - def stopObservingDevicePresence(self, deviceAddress: str): - """停止监听设备存在""" - pass - - def isDeviceAssociated(self, deviceAddress: str) -> bool: - """检查设备是否已关联""" - return deviceAddress in self._associations - - def getDeviceId(self, deviceAddress: str) -> Optional[str]: - """获取设备 ID""" - info = self._associations.get(deviceAddress) - return info.getSelfManagedDeviceId() if info else None - - def getAssociations(self) -> List["AssociationInfo"]: - """获取所有关联信息""" - return list(self._associations.values()) - - def removeAssociation(self, deviceAddress: str): - """移除设备关联""" - self._associations.pop(deviceAddress, None) - - # ═══════════════════════════════════════════════════════════ - # Callback - 回调类 - # ═══════════════════════════════════════════════════════════ - - class Callback(Stub): - """CompanionDeviceManager.Callback — 设备发现回调""" - - def __init__(self): - super().__init__() - - def onDeviceFound(self, intent: Any): - """设备找到回调""" - pass - - def onFailure(self, error: str): - """设备发现失败回调""" - pass - - def toString(self) -> str: - return f"Callback@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def toString(self) -> str: - return f"CompanionDeviceManager(associations={len(self._associations)})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AssociationInfo -# ═══════════════════════════════════════════════════════════════ - -class AssociationInfo(Stub): - """AssociationInfo — 设备关联信息""" - - def __init__( - self, - userId: int = 0, - deviceProfile: str = "", - selfManagedDeviceId: str = "", - associatedDevice: Any = None, - isSelfManaged: bool = False, - isNotAssociated: bool = False, - ): - super().__init__() - self._user_id = userId - self._device_profile = deviceProfile - self._self_managed_device_id = selfManagedDeviceId - self._associated_device = associatedDevice - self._self_managed = isSelfManaged - self._not_associated = isNotAssociated - - def getUserId(self) -> int: - return self._user_id - - def getDeviceProfile(self) -> str: - return self._device_profile - - def getSelfManagedDeviceId(self) -> str: - return self._self_managed_device_id - - def getAssociatedDevice(self) -> Optional[Any]: - return self._associated_device - - def isSelfManaged(self) -> bool: - return self._self_managed - - def isNotAssociated(self) -> bool: - return self._not_associated - - def toString(self) -> str: - return (f"AssociationInfo(deviceId={self._self_managed_device_id}, " - f"profile={self._device_profile})") - - def equals(self, other) -> bool: - if not isinstance(other, AssociationInfo): - return False - return self._self_managed_device_id == other._self_managed_device_id - - def hashCode(self) -> int: - return hash(self._self_managed_device_id) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# WifiDeviceFilter -# ═══════════════════════════════════════════════════════════════ - -class WifiDeviceFilter(Stub): - """WifiDeviceFilter — WiFi 设备过滤器""" - - def __init__(self, namePattern: str = ""): - super().__init__() - self._name_pattern = namePattern - - def getNamePattern(self) -> str: - return self._name_pattern - - def toString(self) -> str: - return f"WifiDeviceFilter(pattern={self._name_pattern})" - - def equals(self, other) -> bool: - if not isinstance(other, WifiDeviceFilter): - return False - return self._name_pattern == other._name_pattern - - def hashCode(self) -> int: - return hash(self._name_pattern) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# BluetoothDeviceFilter -# ═══════════════════════════════════════════════════════════════ - -class BluetoothDeviceFilter(Stub): - """BluetoothDeviceFilter — 蓝牙设备过滤器""" - - class Builder(Stub): - """BluetoothDeviceFilter.Builder — 蓝牙过滤器构建器""" - - def __init__(self): - super().__init__() - self._name_pattern = "" - self._address = "" - - def setNamePattern(self, pattern: str) -> "BluetoothDeviceFilter.Builder": - self._name_pattern = pattern - return self - - def setAddress(self, address: str) -> "BluetoothDeviceFilter.Builder": - self._address = address - return self - - def build(self) -> "BluetoothDeviceFilter": - return BluetoothDeviceFilter( - namePattern=self._name_pattern, - address=self._address, - ) - - def toString(self) -> str: - return f"Builder(pattern={self._name_pattern})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def __init__(self, namePattern: str = "", address: str = ""): - super().__init__() - self._name_pattern = namePattern - self._address = address - - def getNamePattern(self) -> str: - return self._name_pattern - - def getAddress(self) -> str: - return self._address - - def toString(self) -> str: - return f"BluetoothDeviceFilter(address={self._address})" - - def equals(self, other) -> bool: - if not isinstance(other, BluetoothDeviceFilter): - return False - return self._address == other._address - - def hashCode(self) -> int: - return hash(self._address) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# BluetoothLeDeviceFilter -# ═══════════════════════════════════════════════════════════════ - -class BluetoothLeDeviceFilter(Stub): - """BluetoothLeDeviceFilter — BLE 设备过滤器""" - - class Builder(Stub): - """BluetoothLeDeviceFilter.Builder — BLE 过滤器构建器""" - - def __init__(self): - super().__init__() - self._name_pattern = "" - self._scan_filter = None - - def setNamePattern(self, pattern: str) -> "BluetoothLeDeviceFilter.Builder": - self._name_pattern = pattern - return self - - def setScanFilter(self, scanFilter: Any) -> "BluetoothLeDeviceFilter.Builder": - self._scan_filter = scanFilter - return self - - def build(self) -> "BluetoothLeDeviceFilter": - return BluetoothLeDeviceFilter( - namePattern=self._name_pattern, - scanFilter=self._scan_filter, - ) - - def toString(self) -> str: - return f"BLEBuilder(pattern={self._name_pattern})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def __init__(self, namePattern: str = "", scanFilter: Any = None): - super().__init__() - self._name_pattern = namePattern - self._scan_filter = scanFilter - - def getNamePattern(self) -> str: - return self._name_pattern - - def getScanFilter(self) -> Optional[Any]: - return self._scan_filter - - def toString(self) -> str: - return f"BluetoothLeDeviceFilter(pattern={self._name_pattern})" - - def equals(self, other) -> bool: - if not isinstance(other, BluetoothLeDeviceFilter): - return False - return self._name_pattern == other._name_pattern - - def hashCode(self) -> int: - return hash(self._name_pattern) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# CommsDevice -# ═══════════════════════════════════════════════════════════════ - -class CommsDevice(Stub): - """CommsDevice — 通信设备信息""" - - def __init__(self, deviceId: str = "", displayName: str = ""): - super().__init__() - self._device_id = deviceId - self._display_name = displayName - - def getDeviceId(self) -> str: - return self._device_id - - def getDisplayName(self) -> str: - return self._display_name - - def toString(self) -> str: - return f"CommsDevice(id={self._device_id}, name={self._display_name})" - - def equals(self, other) -> bool: - if not isinstance(other, CommsDevice): - return False - return self._device_id == other._device_id - - def hashCode(self) -> int: - return hash(self._device_id) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# DeviceActivityCallback -# ═══════════════════════════════════════════════════════════════ - -class DeviceActivityCallback(Stub): - """DeviceActivityCallback — 设备活动回调""" - - def __init__(self): - super().__init__() - - def onDeviceActivityChanged(self, associationInfo: "AssociationInfo", active: bool): - """设备活动状态变更回调""" - pass - - def toString(self) -> str: - return f"DeviceActivityCallback@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# CompanionService -# ═══════════════════════════════════════════════════════════════ - -class CompanionService(Stub): - """CompanionService — 配套服务""" - - SERVICE_INTERFACE = "android.companion.CompanionDeviceService" - - def __init__(self): - super().__init__() - - def onConnected(self, deviceInfo: Any): - """已连接回调""" - pass - - def onDisconnected(self, deviceInfo: Any): - """已断开回调""" - pass - - def toString(self) -> str: - return f"CompanionService@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/companion_ext.py b/android_pyapi/android/companion_ext.py deleted file mode 100644 index 21ee6fd..0000000 --- a/android_pyapi/android/companion_ext.py +++ /dev/null @@ -1,716 +0,0 @@ -""" -Android Companion Device API 扩展 — CompanionDeviceManager (扩展), -AssociationRequest, DeviceFilter, DevicePresenceEvent, -CompanionTransport, CompanionWireless, CompanionManager 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "CompanionDeviceManager", - "AssociationRequest", - "DeviceFilter", - "DevicePresenceEvent", - "DevicePresenceNotifier", - "CompanionTransport", - "CompanionWireless", - "CompanionBluetooth", - "CompanionWifi", - "CompanionEthernet", - "CompanionVendor", - "CompanionManager", -] - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -DEVICE_PRESENCE_ENTER = 0 -DEVICE_PRESENCE_EXIT = 1 -DEVICE_PRESENCE_PROXIMITY_NEAR = 2 -DEVICE_PRESENCE_PROXIMITY_FAR = 3 -DEVICE_PRESENCE_APPEAR = 4 -DEVICE_PRESENCE_DISAPPEAR = 5 -DEVICE_PRESENCE_AVAILABLE = 6 -DEVICE_PRESENCE_UNAVAILABLE = 7 - -DEVICE_FLAG_NONE = 0 -DEVICE_FLAG_SELF_MANAGED = 1 -DEVICE_FLAG_NOT_ASSOCIATED = 2 -DEVICE_FLAG_SYSTEM_SERVICE = 4 -DEVICE_FLAG_DEBUG_ONLY = 8 -DEVICE_FLAG_VENDOR_SPECIFIC = 16 - -COMPANION_TRANSPORT_BLUETOOTH = 0 -COMPANION_TRANSPORT_BLUETOOTH_LE = 1 -COMPANION_TRANSPORT_WIFI = 2 -COMPANION_TRANSPORT_ETHERNET = 3 -COMPANION_TRANSPORT_USB = 4 -COMPANION_TRANSPORT_NFC = 5 -COMPANION_TRANSPORT_VENDOR = 100 - -ASSOCIATION_PENDING = 0 -ASSOCIATION_ESTABLISHED = 1 -ASSOCIATION_FAILED = 2 -ASSOCIATION_CANCELLED = 3 -ASSOCIATION_REVOKED = 4 - - -# ═══════════════════════════════════════════════════════════════ -# DeviceFilter -# ═══════════════════════════════════════════════════════════════ - -class DeviceFilter(Stub): - """DeviceFilter — 设备过滤器基类""" - - def __init__(self, namePattern: str = "", address: str = "", - isSelfManaged: bool = False): - super().__init__() - self._name_pattern = namePattern - self._address = address - self._is_self_managed = isSelfManaged - - def getNamePattern(self) -> str: - return self._name_pattern - - def getAddress(self) -> str: - return self._address - - def isSelfManaged(self) -> bool: - return self._is_self_managed - - def matches(self, device: Any) -> bool: - return True - - def toString(self) -> str: - return f"DeviceFilter(pattern={self._name_pattern})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, DeviceFilter): - return False - return self._address == other._address - - def hashCode(self) -> int: - return hash(self._address) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AssociationRequest -# ═══════════════════════════════════════════════════════════════ - -class AssociationRequest(Stub): - """AssociationRequest — 关联请求""" - - def __init__(self, isSelfManaged: bool = False, - deviceFilter: Optional[DeviceFilter] = None, - displayName: str = "", - transportTypes: List[int] = None, - flags: int = DEVICE_FLAG_NONE, - profile: str = ""): - super().__init__() - self._is_self_managed = isSelfManaged - self._device_filter = deviceFilter - self._display_name = displayName - self._transport_types = transportTypes or [] - self._flags = flags - self._profile = profile - - def isSelfManaged(self) -> bool: - return self._is_self_managed - - def getDeviceFilter(self) -> Optional[DeviceFilter]: - return self._device_filter - - def getDisplayName(self) -> str: - return self._display_name - - def getTransportTypes(self) -> List[int]: - return list(self._transport_types) - - def getFlags(self) -> int: - return self._flags - - def getProfile(self) -> str: - return self._profile - - class Builder(Stub): - """AssociationRequest.Builder — 关联请求构建器""" - - def __init__(self): - super().__init__() - self._is_self_managed = False - self._device_filter: Optional[DeviceFilter] = None - self._display_name = "" - self._transport_types: List[int] = [] - self._flags = DEVICE_FLAG_NONE - self._profile = "" - - def setSelfManaged(self, selfManaged: bool) -> "AssociationRequest.Builder": - self._is_self_managed = selfManaged - return self - - def setDeviceFilter(self, filter_: DeviceFilter) -> "AssociationRequest.Builder": - self._device_filter = filter_ - return self - - def setDisplayName(self, name: str) -> "AssociationRequest.Builder": - self._display_name = name - return self - - def addTransportType(self, transportType: int) -> "AssociationRequest.Builder": - if transportType not in self._transport_types: - self._transport_types.append(transportType) - return self - - def setFlags(self, flags: int) -> "AssociationRequest.Builder": - self._flags = flags - return self - - def setProfile(self, profile: str) -> "AssociationRequest.Builder": - self._profile = profile - return self - - def build(self) -> "AssociationRequest": - return AssociationRequest( - isSelfManaged=self._is_self_managed, - deviceFilter=self._device_filter, - displayName=self._display_name, - transportTypes=self._transport_types, - flags=self._flags, - profile=self._profile, - ) - - def toString(self) -> str: - return (f"AssociationRequest(selfManaged={self._is_self_managed}, " - f"name={self._display_name})") - - def equals(self, other: Any) -> bool: - if not isinstance(other, AssociationRequest): - return False - return (self._is_self_managed == other._is_self_managed and - self._display_name == other._display_name) - - def hashCode(self) -> int: - return hash((self._is_self_managed, self._display_name)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# DevicePresenceEvent -# ═══════════════════════════════════════════════════════════════ - -class DevicePresenceEvent(Stub): - """DevicePresenceEvent — 设备存在事件""" - - def __init__(self, deviceAddress: str = "", - eventType: int = DEVICE_PRESENCE_ENTER, - timestamp: int = 0, - rssi: int = 0, transportType: int = COMPANION_TRANSPORT_BLUETOOTH, - deviceId: str = ""): - super().__init__() - self._device_address = deviceAddress - self._event_type = eventType - self._timestamp = timestamp - self._rssi = rssi - self._transport_type = transportType - self._device_id = deviceId - - def getDeviceAddress(self) -> str: - return self._device_address - - def getEventType(self) -> int: - return self._event_type - - def getTimestamp(self) -> int: - return self._timestamp - - def getRssi(self) -> int: - return self._rssi - - def getTransportType(self) -> int: - return self._transport_type - - def getDeviceId(self) -> str: - return self._device_id - - def toString(self) -> str: - return (f"DevicePresenceEvent(addr={self._device_address}, " - f"type={self._event_type})") - - def equals(self, other: Any) -> bool: - if not isinstance(other, DevicePresenceEvent): - return False - return (self._device_address == other._device_address and - self._event_type == other._event_type) - - def hashCode(self) -> int: - return hash((self._device_address, self._event_type)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# DevicePresenceNotifier -# ═══════════════════════════════════════════════════════════════ - -class DevicePresenceNotifier(Stub): - """DevicePresenceNotifier — 设备存在通知器""" - - def __init__(self): - super().__init__() - self._listeners: Dict[int, Callable] = {} - - def registerCallback(self, callback: Callable, handler: Any = None): - lid = id(callback) - self._listeners[lid] = callback - - def unregisterCallback(self, callback: Callable): - lid = id(callback) - self._listeners.pop(lid, None) - - def notifyPresence(self, event: DevicePresenceEvent): - for cb in self._listeners.values(): - try: - cb(event) - except Exception: - pass - - def toString(self) -> str: - return f"DevicePresenceNotifier(listeners={len(self._listeners)})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# CompanionTransport -# ═══════════════════════════════════════════════════════════════ - -class CompanionTransport(Stub): - """CompanionTransport — 配套设备传输基类""" - - def __init__(self, transportType: int = COMPANION_TRANSPORT_BLUETOOTH, - name: str = ""): - super().__init__() - self._transport_type = transportType - self._name = name - - def getTransportType(self) -> int: - return self._transport_type - - def getName(self) -> str: - return self._name - - def isAvailable(self) -> bool: - return True - - def isEnabled(self) -> bool: - return True - - def enable(self): - pass - - def disable(self): - pass - - def toString(self) -> str: - return f"CompanionTransport(type={self._transport_type})" - - -# ═══════════════════════════════════════════════════════════════ -# CompanionWireless (WiFi 传输) -# ═══════════════════════════════════════════════════════════════ - -class CompanionWireless(CompanionTransport): - """CompanionWireless — 无线传输 (WiFi Direct / WiFi Aware)""" - - SECURITY_NONE = 0 - SECURITY_WPA2 = 1 - SECURITY_WPA3 = 2 - SECURITY_WPA2_WPA3 = 3 - - def __init__(self, ssid: str = "", bssid: str = "", - frequency: int = 0, securityType: int = SECURITY_WPA2): - super().__init__(transportType=COMPANION_TRANSPORT_WIFI, name="Wifi") - self._ssid = ssid - self._bssid = bssid - self._frequency = frequency - self._security_type = securityType - - def getSsid(self) -> str: - return self._ssid - - def getBssid(self) -> str: - return self._bssid - - def getFrequency(self) -> int: - return self._frequency - - def getSecurityType(self) -> int: - return self._security_type - - def toString(self) -> str: - return f"CompanionWireless(ssid={self._ssid})" - - -# ═══════════════════════════════════════════════════════════════ -# CompanionBluetooth -# ═══════════════════════════════════════════════════════════════ - -class CompanionBluetooth(CompanionTransport): - """CompanionBluetooth — 蓝牙传输""" - - TRANSPORT_BREDR = 0 - TRANSPORT_LE = 1 - TRANSPORT_DUAL = 2 - - def __init__(self, address: str = "", name: str = "", - transport: int = TRANSPORT_BREDR, - isBonded: bool = False): - super().__init__(transportType=COMPANION_TRANSPORT_BLUETOOTH, name="Bluetooth") - self._address = address - self._device_name = name - self._transport = transport - self._is_bonded = isBonded - - def getAddress(self) -> str: - return self._address - - def getDeviceName(self) -> str: - return self._device_name - - def getTransportMode(self) -> int: - return self._transport - - def isBonded(self) -> bool: - return self._is_bonded - - def toString(self) -> str: - return f"CompanionBluetooth(addr={self._address}, name={self._device_name})" - - -# ═══════════════════════════════════════════════════════════════ -# CompanionWifi -# ═══════════════════════════════════════════════════════════════ - -class CompanionWifi(CompanionTransport): - """CompanionWifi — WiFi 传输""" - - def __init__(self, ssid: str = "", bssid: str = "", - ipAddress: str = "", port: int = 0, - isDirect: bool = False, isAware: bool = False): - super().__init__(transportType=COMPANION_TRANSPORT_WIFI, name="WiFi") - self._ssid = ssid - self._bssid = bssid - self._ip_address = ipAddress - self._port = port - self._is_direct = isDirect - self._is_aware = isAware - - def getSsid(self) -> str: - return self._ssid - - def getBssid(self) -> str: - return self._bssid - - def getIpAddress(self) -> str: - return self._ip_address - - def getPort(self) -> int: - return self._port - - def isDirect(self) -> bool: - return self._is_direct - - def isAware(self) -> bool: - return self._is_aware - - def toString(self) -> str: - return f"CompanionWifi(ssid={self._ssid})" - - -# ═══════════════════════════════════════════════════════════════ -# CompanionEthernet -# ═══════════════════════════════════════════════════════════════ - -class CompanionEthernet(CompanionTransport): - """CompanionEthernet — 以太网传输""" - - def __init__(self, interfaceName: str = "", macAddress: str = "", - ipAddress: str = "", linkSpeed: int = 1000): - super().__init__(transportType=COMPANION_TRANSPORT_ETHERNET, name="Ethernet") - self._interface_name = interfaceName - self._mac_address = macAddress - self._ip_address = ipAddress - self._link_speed = linkSpeed - - def getInterfaceName(self) -> str: - return self._interface_name - - def getMacAddress(self) -> str: - return self._mac_address - - def getIpAddress(self) -> str: - return self._ip_address - - def getLinkSpeed(self) -> int: - return self._link_speed - - def toString(self) -> str: - return f"CompanionEthernet(iface={self._interface_name})" - - -# ═══════════════════════════════════════════════════════════════ -# CompanionVendor -# ═══════════════════════════════════════════════════════════════ - -class CompanionVendor(CompanionTransport): - """CompanionVendor — 厂商自定义传输""" - - class RfcommEndpoint(Stub): - """CompanionVendor.RfcommEndpoint — RFCOMM 端点""" - - def __init__(self, address: str = "", channel: int = 0, - uuid: str = "", name: str = ""): - super().__init__() - self._address = address - self._channel = channel - self._uuid = uuid - self._name = name - - def getAddress(self) -> str: - return self._address - - def getChannel(self) -> int: - return self._channel - - def getUuid(self) -> str: - return self._uuid - - def getName(self) -> str: - return self._name - - def toString(self) -> str: - return f"RfcommEndpoint(addr={self._address}, channel={self._channel})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, CompanionVendor.RfcommEndpoint): - return False - return self._address == other._address and self._channel == other._channel - - def hashCode(self) -> int: - return hash((self._address, self._channel)) & 0x7FFFFFFF - - class Transport(Stub): - """CompanionVendor.Transport — 厂商传输层""" - - def __init__(self, name: str = "", vendorId: str = "", - version: int = 1, endpoints: List["CompanionVendor.RfcommEndpoint"] = None): - super().__init__() - self._name = name - self._vendor_id = vendorId - self._version = version - self._endpoints = endpoints or [] - - def getName(self) -> str: - return self._name - - def getVendorId(self) -> str: - return self._vendor_id - - def getVersion(self) -> int: - return self._version - - def getEndpoints(self) -> List["CompanionVendor.RfcommEndpoint"]: - return list(self._endpoints) - - def addEndpoint(self, endpoint: "CompanionVendor.RfcommEndpoint"): - self._endpoints.append(endpoint) - - def __init__(self, vendorName: str = "", vendorId: str = "", - protocolVersion: int = 1, - endpoints: List[RfcommEndpoint] = None, - transport: Optional["CompanionVendor.Transport"] = None): - super().__init__(transportType=COMPANION_TRANSPORT_VENDOR, name=vendorName) - self._vendor_id = vendorId - self._protocol_version = protocolVersion - self._endpoints = endpoints or [] - self._transport_layer = transport - - def getVendorId(self) -> str: - return self._vendor_id - - def getProtocolVersion(self) -> int: - return self._protocol_version - - def getEndpoints(self) -> List["CompanionVendor.RfcommEndpoint"]: - return list(self._endpoints) - - def getTransportLayer(self) -> Optional["CompanionVendor.Transport"]: - return self._transport_layer - - def toString(self) -> str: - return f"CompanionVendor(name={self._name}, vendorId={self._vendor_id})" - - -# ═══════════════════════════════════════════════════════════════ -# CompanionManager -# ═══════════════════════════════════════════════════════════════ - -class CompanionManager(Stub): - """CompanionManager — 配套设备管理器""" - - def __init__(self): - super().__init__() - self._associations: Dict[str, AssociationRequest] = {} - self._transports: Dict[int, CompanionTransport] = {} - self._presence_notifier = DevicePresenceNotifier() - self._callbacks: Dict[int, Callable] = {} - - def associate(self, request: AssociationRequest, callback: Callable, - handler: Any = None): - raise AndroidOnlyError("CompanionManager.associate() is not available on desktop") - - def associateCallback(self, request: AssociationRequest, callback: Callable, - executor: Any = None): - raise AndroidOnlyError("CompanionManager.associateCallback() is not available on desktop") - - def revokeAssociation(self, deviceAddress: str): - self._associations.pop(deviceAddress, None) - - def getAssociations(self) -> List[AssociationRequest]: - return list(self._associations.values()) - - def isDeviceAssociated(self, deviceAddress: str) -> bool: - return deviceAddress in self._associations - - def registerTransport(self, transport: CompanionTransport): - self._transports[transport.getTransportType()] = transport - - def unregisterTransport(self, transport: CompanionTransport): - self._transports.pop(transport.getTransportType(), None) - - def getTransport(self, transportType: int) -> Optional[CompanionTransport]: - return self._transports.get(transportType) - - def getPresenceNotifier(self) -> DevicePresenceNotifier: - return self._presence_notifier - - def registerDevicePresenceCallback(self, callback: Callable): - self._presence_notifier.registerCallback(callback) - - def unregisterDevicePresenceCallback(self, callback: Callable): - self._presence_notifier.unregisterCallback(callback) - - def startObservingDevicePresence(self, deviceAddress: str): - pass - - def stopObservingDevicePresence(self, deviceAddress: str): - pass - - def toString(self) -> str: - return (f"CompanionManager(associations={len(self._associations)}, " - f"transports={len(self._transports)})") - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# CompanionDeviceManager (扩展) -# ═══════════════════════════════════════════════════════════════ - -class CompanionDeviceManager(Stub): - """CompanionDeviceManager — 配套设备管理器 (扩展)""" - - EXTRA_DEVICE = "android.companion.extra.DEVICE" - EXTRA_ASSOCIATION = "android.companion.extra.ASSOCIATION" - EXTRA_EVENT = "android.companion.extra.EVENT" - EXTRA_EVENT_TYPE = "android.companion.extra.EVENT_TYPE" - - FLAG_SELF_MANAGED = DEVICE_FLAG_SELF_MANAGED - FLAG_NOT_ASSOCIATED = DEVICE_FLAG_NOT_ASSOCIATED - - def __init__(self): - super().__init__() - self._manager = CompanionManager() - - def associate(self, request: AssociationRequest, callback: Callable, - handler: Any = None): - raise AndroidOnlyError("CompanionDeviceManager.associate() is not available on desktop") - - def associateCallback(self, request: AssociationRequest, callback: Callable, - executor: Any = None): - raise AndroidOnlyError("CompanionDeviceManager.associateCallback() is not available on desktop") - - def requestNotificationAccess(self, pairingDevice: Any): - raise AndroidOnlyError("CompanionDeviceManager.requestNotificationAccess() is not available on desktop") - - def requestSystemAppStore(self, pairingDevice: Any): - pass - - def startObservingDevicePresence(self, deviceAddress: str): - self._manager.startObservingDevicePresence(deviceAddress) - - def stopObservingDevicePresence(self, deviceAddress: str): - self._manager.stopObservingDevicePresence(deviceAddress) - - def isDeviceAssociated(self, deviceAddress: str) -> bool: - return self._manager.isDeviceAssociated(deviceAddress) - - def getDeviceId(self, deviceAddress: str) -> Optional[str]: - return deviceAddress - - def getAssociations(self) -> List[Any]: - return self._manager.getAssociations() - - def removeAssociation(self, deviceAddress: str): - self._manager.revokeAssociation(deviceAddress) - - def registerDevicePresenceCallback(self, callback: Callable): - self._manager.registerDevicePresenceCallback(callback) - - def unregisterDevicePresenceCallback(self, callback: Callable): - self._manager.unregisterDevicePresenceCallback(callback) - - def getCompanionManager(self) -> CompanionManager: - return self._manager - - class Callback(Stub): - """CompanionDeviceManager.Callback — 设备发现回调""" - - def onDeviceFound(self, intent: Any): - pass - - def onFailure(self, error: str): - pass - - def toString(self) -> str: - return f"Callback@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def toString(self) -> str: - return f"CompanionDeviceManager(mgr={self._manager})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/content.py b/android_pyapi/android/content.py deleted file mode 100644 index c3d3df7..0000000 --- a/android_pyapi/android/content.py +++ /dev/null @@ -1,2054 +0,0 @@ -""" -Android Content 组件 — Context, ContentResolver, Uri, SharedPreferences 等 -方法签名完全对齐 Android SDK -""" - -import json -import mimetypes -import os -import sys -import threading -from typing import Any, Dict, List, Optional, Set, Tuple, Union -from urllib.parse import urlparse, urlunparse, urlencode, parse_qs, quote, unquote - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) - -try: - from .._linux_impl import IS_LINUX, LinuxClipboard, LinuxNotify, LinuxLocation, LinuxProcess - _USE_LINUX = IS_LINUX -except ImportError: - _USE_LINUX = False - - -# ═══════════════════════════════════════════════════════════════ -# Uri -# ═══════════════════════════════════════════════════════════════ - -class Uri(Stub): - """Android Uri — 统一资源标识符""" - - def __init__(self, scheme: str = "", authority: str = "", path: str = "", - query: str = "", fragment: str = "", host: str = "", port: int = -1, - ssp: str = "", user_info: str = ""): - super().__init__() - self._scheme = scheme - self._authority = authority - self._path = path - self._query = query - self._fragment = fragment - self._host = host - self._port = port - self._ssp = ssp - self._user_info = user_info - self._opaque = bool(ssp) - self._cached_string: Optional[str] = None - - @staticmethod - def parse(uri_string: str) -> "Uri": - parsed = urlparse(uri_string) - host = parsed.hostname or "" - port = parsed.port or -1 - return Uri( - scheme=parsed.scheme, - authority=parsed.netloc, - path=parsed.path, - query=parsed.query, - fragment=parsed.fragment, - host=host, - port=port, - user_info=parsed.username or "", - ) - - @staticmethod - def fromParts(scheme: str, ssp: str, fragment: str) -> "Uri": - uri = Uri(scheme=scheme, ssp=ssp, fragment=fragment) - uri._opaque = True - return uri - - @staticmethod - def withAppendedPath(base_uri: "Uri", path_segment: str) -> "Uri": - new_path = base_uri._path.rstrip("/") + "/" + path_segment - return Uri( - scheme=base_uri._scheme, - authority=base_uri._authority, - path=new_path, - query=base_uri._query, - fragment=base_uri._fragment, - host=base_uri._host, - port=base_uri._port, - ) - - @staticmethod - def encode(s: str) -> str: - return quote(s, safe=":/?#[]@!$&'()*+,;=-._~") - - @staticmethod - def decode(s: str) -> str: - return unquote(s) - - def buildUpon(self) -> "Uri.Builder": - b = Uri.Builder() - b._scheme = self._scheme - b._authority = self._authority - b._path = self._path - b._query = self._query - b._fragment = self._fragment - return b - - def getScheme(self) -> Optional[str]: - return self._scheme or None - - def getAuthority(self) -> Optional[str]: - return self._authority or None - - def getPath(self) -> Optional[str]: - return self._path or None - - def getQuery(self) -> Optional[str]: - return self._query or None - - def getFragment(self) -> Optional[str]: - return self._fragment or None - - def getHost(self) -> Optional[str]: - return self._host or None - - def getPort(self) -> int: - return self._port - - def getPathSegments(self) -> List[str]: - return [s for s in self._path.split("/") if s] if self._path else [] - - def getLastPathSegment(self) -> Optional[str]: - segments = self.getPathSegments() - return segments[-1] if segments else None - - def getUserInfo(self) -> Optional[str]: - return self._user_info or None - - def isAbsolute(self) -> bool: - return bool(self._scheme) - - def isOpaque(self) -> bool: - return self._opaque - - def isHierarchical(self) -> bool: - return not self._opaque - - def getSchemeSpecificPart(self) -> Optional[str]: - return self._ssp or None - - def normalizeScheme(self) -> "Uri": - if self._scheme: - self._scheme = self._scheme.lower() - return self - - def toString(self) -> str: - if self._cached_string is not None: - return self._cached_string - if self._opaque and self._ssp: - result = f"{self._scheme}:{self._ssp}" - if self._fragment: - result += f"#{self._fragment}" - else: - parts = [] - if self._scheme: - parts.append(f"{self._scheme}:") - if self._authority: - parts.append(f"//{self._authority}") - if self._path: - parts.append(self._path) - if self._query: - parts.append(f"?{self._query}") - if self._fragment: - parts.append(f"#{self._fragment}") - result = "".join(parts) - self._cached_string = result - return result - - def __repr__(self): - return self.toString() - - def __str__(self): - return self.toString() - - def __eq__(self, other): - if isinstance(other, Uri): - return self.toString() == other.toString() - return NotImplemented - - def __hash__(self): - return hash(self.toString()) - - class Builder(Stub): - """Uri builder pattern""" - - def __init__(self): - super().__init__() - self._scheme = "" - self._authority = "" - self._path = "" - self._query = "" - self._fragment = "" - self._host = "" - self._port = -1 - self._segments: List[str] = [] - - def scheme(self, scheme: str) -> "Uri.Builder": - self._scheme = scheme - return self - - def authority(self, authority: str) -> "Uri.Builder": - self._authority = authority - return self - - def path(self, path: str) -> "Uri.Builder": - self._path = path - return self - - def appendPath(self, new_segment: str) -> "Uri.Builder": - self._segments.append(new_segment) - return self - - def query(self, query: str) -> "Uri.Builder": - self._query = query - return self - - def appendQueryParameter(self, key: str, value: str) -> "Uri.Builder": - params = dict(parse_qs(self._query, keep_blank_values=True)) if self._query else {} - if key in params: - params[key].append(value) - else: - params[key] = [value] - self._query = urlencode(params, doseq=True) - return self - - def fragment(self, fragment: str) -> "Uri.Builder": - self._fragment = fragment - return self - - def opaquePart(self, opaque_part: str) -> "Uri.Builder": - return self - - def build(self) -> "Uri": - path = self._path - if self._segments: - path = (path.rstrip("/") if path else "") + "/" + "/".join(self._segments) - return Uri(self._scheme, self._authority, path, self._query, self._fragment) - - -# ═══════════════════════════════════════════════════════════════ -# ContentValues -# ═══════════════════════════════════════════════════════════════ - -class ContentValues(Stub): - """ContentValues — 类似 dict 的内容值容器""" - - def __init__(self, from_dict: Optional[Dict[str, Any]] = None): - super().__init__() - self._values: Dict[str, Any] = dict(from_dict) if from_dict else {} - - def put(self, key: str, value: Any): - self._values[key] = value - - def putNull(self, key: str): - self._values[key] = None - - def get(self, key: str) -> Optional[Any]: - return self._values.get(key) - - def getAsString(self, key: str) -> Optional[str]: - v = self._values.get(key) - return str(v) if v is not None else None - - def getAsInteger(self, key: str) -> Optional[int]: - v = self._values.get(key) - if v is None: - return None - try: - return int(v) - except (ValueError, TypeError): - return None - - def getAsLong(self, key: str) -> Optional[int]: - return self.getAsInteger(key) - - def getAsFloat(self, key: str) -> Optional[float]: - v = self._values.get(key) - if v is None: - return None - try: - return float(v) - except (ValueError, TypeError): - return None - - def getAsDouble(self, key: str) -> Optional[float]: - return self.getAsFloat(key) - - def getAsBoolean(self, key: str) -> Optional[bool]: - v = self._values.get(key) - if v is None: - return None - if isinstance(v, bool): - return v - if isinstance(v, str): - return v.lower() in ("true", "1", "yes", "y") - return bool(v) - - def getAsByteArray(self, key: str) -> Optional[bytes]: - v = self._values.get(key) - if isinstance(v, bytes): - return v - if isinstance(v, str): - return v.encode("utf-8") - return None - - def containsKey(self, key: str) -> bool: - return key in self._values - - def valueSet(self) -> Set[str]: - return set(self._values.keys()) - - def keySet(self) -> Set[str]: - return set(self._values.keys()) - - def size(self) -> int: - return len(self._values) - - def isEmpty(self) -> bool: - return len(self._values) == 0 - - def clear(self): - self._values.clear() - - def remove(self, key: str): - self._values.pop(key, None) - - def putAll(self, other: "ContentValues"): - if other: - self._values.update(other._values) - - def equals(self, other) -> bool: - if isinstance(other, ContentValues): - return self._values == other._values - return False - - def hashCode(self) -> int: - return hash(frozenset(self._values.items())) & 0x7FFFFFFF - - def toString(self) -> str: - return f"ContentValues({self._values})" - - def __getitem__(self, key: str): - return self._values[key] - - def __setitem__(self, key: str, value: Any): - self._values[key] = value - - def __contains__(self, key: str) -> bool: - return key in self._values - - def __len__(self) -> int: - return len(self._values) - - def __repr__(self): - return self.toString() - - -# ═══════════════════════════════════════════════════════════════ -# ContentResolver -# ═══════════════════════════════════════════════════════════════ - -class ContentResolver(Stub): - """ContentResolver — 内容解析器""" - - NOTIFY_SYNC_TO_NETWORK = 1 - NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2 - - def __init__(self): - super().__init__() - self._observers: Dict[str, List[Any]] = {} - - @staticmethod - def _uri_to_file_path(uri) -> Optional[str]: - """将 file:// 或纯路径 Uri 转换为本地文件系统路径""" - try: - uri_str = str(uri) - except Exception: - return None - # 处理字符串输入 - if isinstance(uri, str): - if uri.startswith("file://"): - return uri[7:] - if "://" not in uri: - return uri if uri else None - return None - scheme = getattr(uri, "_scheme", "") or "" - path = getattr(uri, "_path", "") or "" - if scheme == "file": - return path if path else None - if scheme == "" and path: - return path - return None - - def query(self, uri: Uri, projection: Optional[List[str]], selection: Optional[str], - selection_args: Optional[List[str]], sort_order: Optional[str], - cancellation_signal: Any = None) -> Any: - """Query content provider. 对 file:// URI 返回基于目录列表的 Cursor。""" - path = self._uri_to_file_path(uri) - if path is not None and os.path.isdir(path): - try: - from .database import Cursor, MatrixCursor - cols = projection or ["_id", "name", "size", "last_modified"] - cursor = MatrixCursor(cols) - for idx, name in enumerate(sorted(os.listdir(path))): - full = os.path.join(path, name) - try: - st = os.stat(full) - size = st.st_size - mtime = int(st.st_mtime * 1000) - except OSError: - size = 0 - mtime = 0 - cursor.addRow([idx, name, size, mtime]) - return cursor - except Exception: - pass - return None - - def insert(self, uri: Uri, values: ContentValues) -> Optional[Uri]: - """Insert. 对 file:// URI 将 ContentValues 写为 JSON 文件。""" - path = self._uri_to_file_path(uri) - if path is not None and isinstance(values, ContentValues): - try: - os.makedirs(path if os.path.isdir(path) else os.path.dirname(path) or ".", exist_ok=True) - target = path if not os.path.isdir(path) else os.path.join(path, "insert.json") - data = values._values if hasattr(values, "_values") else {} - with open(target, "w", encoding="utf-8") as f: - json.dump(data, f, ensure_ascii=False, default=str) - return Uri.withAppendedPath(uri, os.path.basename(target)) - except OSError: - pass - return uri - - def update(self, uri: Uri, values: ContentValues, selection: Optional[str], - selection_args: Optional[List[str]]) -> int: - """Update. 对 file:// URI 更新对应 JSON 文件。""" - path = self._uri_to_file_path(uri) - if path is not None and isinstance(values, ContentValues) and os.path.isfile(path): - try: - data = values._values if hasattr(values, "_values") else {} - with open(path, "r", encoding="utf-8") as f: - existing = json.load(f) - if isinstance(existing, dict): - existing.update(data) - with open(path, "w", encoding="utf-8") as f: - json.dump(existing, f, ensure_ascii=False, default=str) - return 1 - except (OSError, ValueError): - pass - return 0 - - def delete(self, uri: Uri, selection: Optional[str], - selection_args: Optional[List[str]]) -> int: - """Delete. 对 file:// URI 删除对应文件或目录。""" - path = self._uri_to_file_path(uri) - if path is not None: - try: - if os.path.isfile(path): - os.remove(path) - return 1 - if os.path.isdir(path): - import shutil - shutil.rmtree(path) - return 1 - except OSError: - pass - return 0 - - def applyBatch(self, authority: str, operations: List[Any]) -> List[Any]: - """Apply batch operations, returning per-op results.""" - results: List[Any] = [] - for op in operations: - try: - op_type = getattr(op, "_type", None) - uri = getattr(op, "_uri", None) - values = getattr(op, "_values", None) - if op_type == 1 and uri is not None: # INSERT - results.append(ContentProviderResult(uri=self.insert(uri, values or ContentValues()))) - elif op_type == 2 and uri is not None: # UPDATE - count = self.update(uri, values or ContentValues(), - getattr(op, "_selection", None), - getattr(op, "_selection_args", None)) - results.append(ContentProviderResult(count=count)) - elif op_type == 3 and uri is not None: # DELETE - count = self.delete(uri, getattr(op, "_selection", None), - getattr(op, "_selection_args", None)) - results.append(ContentProviderResult(count=count)) - else: - results.append(ContentProviderResult(count=0)) - except Exception: - results.append(ContentProviderResult(count=0)) - return results - - def bulkInsert(self, uri: Uri, values: List[ContentValues]) -> int: - count = 0 - for v in values or []: - if self.insert(uri, v) is not None: - count += 1 - return count - - def call(self, uri: Uri, method: str, arg: Optional[str], extras: Optional[ContentValues]) -> Optional[ContentValues]: - return None - - def notifyChange(self, uri: Uri, observer: Any, sync_to_network: bool = False): - uri_str = str(uri) - if uri_str in self._observers: - for obs in list(self._observers[uri_str]): - try: - obs.onChange(True) - except Exception: - pass - - def registerContentObserver(self, uri: Uri, notify_for_descendants: bool, observer: Any): - uri_str = str(uri) - if uri_str not in self._observers: - self._observers[uri_str] = [] - self._observers[uri_str].append(observer) - - def unregisterContentObserver(self, observer: Any): - for uri_str in list(self._observers.keys()): - if observer in self._observers[uri_str]: - self._observers[uri_str].remove(observer) - if not self._observers[uri_str]: - del self._observers[uri_str] - - def getType(self, uri) -> Optional[str]: - """返回 URI 指向资源的 MIME 类型。""" - path = self._uri_to_file_path(uri) - if path: - mime, _ = mimetypes.guess_type(path) - return mime or "application/octet-stream" - scheme = getattr(uri, "_scheme", "") or "" - if isinstance(uri, str) and "://" in uri: - scheme = uri.split("://")[0] - if scheme == "content": - return "vnd.android.cursor.dir/vnd.android.content" - return None - - def acquireContentProviderClient(self, uri: Uri) -> Optional["ContentProviderClient"]: - return ContentProviderClient(uri) - - def acquireUnstableContentProviderClient(self, uri: Uri) -> Optional["ContentProviderClient"]: - return ContentProviderClient(uri) - - def getStreamTypes(self, uri: Uri, mime_type_filter: str) -> Optional[List[str]]: - """返回 URI 支持的流 MIME 类型列表。""" - mime = self.getType(uri) - if mime is None: - return None - if mime_type_filter and mime_type_filter.endswith("/*"): - prefix = mime_type_filter[:-1] - if mime.startswith(prefix): - return [mime] - return None - if mime == mime_type_filter: - return [mime] - return [mime] if mime else None - - def openInputStream(self, uri: Uri) -> Any: - """打开输入流。对 file:// URI 返回真实文件流。""" - path = self._uri_to_file_path(uri) - if path and os.path.isfile(path): - try: - return open(path, "rb") - except OSError: - return None - return None - - def openOutputStream(self, uri: Uri) -> Any: - """打开输出流。对 file:// URI 返回真实文件流。""" - path = self._uri_to_file_path(uri) - if path: - try: - d = os.path.dirname(path) - if d: - os.makedirs(d, exist_ok=True) - return open(path, "wb") - except OSError: - return None - return None - - -# ═══════════════════════════════════════════════════════════════ -# Context -# ═══════════════════════════════════════════════════════════════ - -class Context(Stub): - """Android Context — 应用上下文""" - - # Service 常量 - POWER_SERVICE = "power" - WINDOW_SERVICE = "window" - LAYOUT_INFLATER_SERVICE = "layout_inflater" - ACTIVITY_SERVICE = "activity" - ALARM_SERVICE = "alarm" - NOTIFICATION_SERVICE = "notification" - ACCESSIBILITY_SERVICE = "accessibility" - KEYGUARD_SERVICE = "keyguard" - LOCATION_SERVICE = "location" - SENSOR_SERVICE = "sensor" - STORAGE_SERVICE = "storage" - VIBRATOR_SERVICE = "vibrator" - CONNECTIVITY_SERVICE = "connectivity" - WIFI_SERVICE = "wifi" - AUDIO_SERVICE = "audio" - TELEPHONY_SERVICE = "telephony" - INPUT_METHOD_SERVICE = "input_method" - CAMERA_SERVICE = "camera" - BATTERY_SERVICE = "battery" - CLIPBOARD_SERVICE = "clipboard" - - # Mode 常量 - MODE_PRIVATE = 0x0000 - MODE_WORLD_READABLE = 0x0001 - MODE_WORLD_WRITEABLE = 0x0002 - MODE_APPEND = 0x8000 - MODE_MULTI_PROCESS = 0x0004 - - # Flag 常量 - BIND_AUTO_CREATE = 0x0001 - BIND_DEBUG_UNBIND = 0x0002 - BIND_NOT_FOREGROUND = 0x0004 - BIND_ABOVE_CLIENT = 0x0008 - BIND_ALLOW_OOM_MANAGEMENT = 0x0010 - BIND_WAIVE_PRIORITY = 0x0020 - BIND_IMPORTANT = 0x0040 - BIND_ADJUST_WITH_ACTIVITY = 0x0080 - - def __init__(self): - super().__init__() - self._package_name = _package_name - self._services: Dict[str, Any] = {} - self._shared_prefs: Dict[str, "SharedPreferences"] = {} - self._registered_receivers: List[Dict] = [] - self._content_resolver = ContentResolver() - - def getPackageName(self) -> str: - return self._package_name - - def getFilesDir(self): - return os.path.join(_app_data_dir, "files") - - def getCacheDir(self): - return os.path.join(_app_data_dir, "cache") - - def getCodeCacheDir(self): - return os.path.join(_app_data_dir, "code_cache") - - def getNoBackupFilesDir(self): - return os.path.join(_app_data_dir, "no_backup") - - def getExternalFilesDir(self, type_: Optional[str] = None) -> str: - base = os.path.join(_sdcard_root, "Android", "data", self._package_name, "files") - if type_: - base = os.path.join(base, type_) - os.makedirs(base, exist_ok=True) - return base - - def getExternalCacheDir(self): - base = os.path.join(_sdcard_root, "Android", "data", self._package_name, "cache") - os.makedirs(base, exist_ok=True) - return base - - def getObbDir(self): - base = os.path.join(_sdcard_root, "Android", "obb", self._package_name) - os.makedirs(base, exist_ok=True) - return base - - def getDataDir(self): - return _app_data_dir - - def getPackageCodePath(self) -> str: - return os.path.join(_app_data_dir, "base.apk") - - def getPackageResourcePath(self) -> str: - return os.path.join(_app_data_dir, "base.apk") - - def startActivity(self, intent: Any): - sys.stderr.write(f"[Context] startActivity: {intent}\n") - - def startActivities(self, intents: List[Any], options: Optional[Any] = None): - for intent in intents: - self.startActivity(intent) - - def sendBroadcast(self, intent: Any): - sys.stderr.write(f"[Context] sendBroadcast: {intent}\n") - for receiver in self._registered_receivers: - if self._matches_intent_filter(intent, receiver.get("filter")): - try: - receiver["receiver"].onReceive(self, intent) - except Exception as e: - sys.stderr.write(f"[Context] broadcast error: {e}\n") - - def sendOrderedBroadcast(self, intent: Any, receiver_permission: Optional[str] = None): - self.sendBroadcast(intent) - - def sendStickyBroadcast(self, intent: Any): - self.sendBroadcast(intent) - - def registerReceiver(self, receiver: Optional[Any], filter: Any) -> Optional[Any]: - if receiver: - self._registered_receivers.append({"receiver": receiver, "filter": filter}) - return receiver - - def unregisterReceiver(self, receiver: Any): - self._registered_receivers = [ - r for r in self._registered_receivers - if r["receiver"] is not receiver - ] - - def _matches_intent_filter(self, intent: Any, intent_filter: Any) -> bool: - if intent_filter is None: - return True - action = getattr(intent, "getAction", lambda: None)() - if intent_filter._action and action != intent_filter._action: - return False - return True - - def getContentResolver(self) -> ContentResolver: - return self._content_resolver - - def getSharedPreferences(self, name: str, mode: int = MODE_PRIVATE) -> "SharedPreferences": - if name not in self._shared_prefs: - self._shared_prefs[name] = SharedPreferences(name) - return self._shared_prefs[name] - - def getPackageManager(self): - from .content_pm import PackageManager - return PackageManager() - - def getResources(self): - from .content_res import Resources, AssetManager - if not hasattr(self, "_resources") or self._resources is None: - self._resources = Resources(self.getAssets()) - return self._resources - - def getAssets(self): - from .content_res import AssetManager - if not hasattr(self, "_assets") or self._assets is None: - self._assets = AssetManager() - return self._assets - - def getMainLooper(self): - from .os import Looper - return Looper.getMainLooper() - - def getApplicationContext(self): - return self - - def getSystemService(self, name: str) -> Any: - if name == Context.ACTIVITY_SERVICE: - from .app import ActivityManager - return ActivityManager() - if name == Context.ALARM_SERVICE: - from .app import AlarmManager - return AlarmManager() - if name == Context.NOTIFICATION_SERVICE: - from .app import NotificationManager - return NotificationManager() - if name == Context.VIBRATOR_SERVICE: - from .os import Vibrator - return Vibrator() - if name == Context.POWER_SERVICE: - from .os import PowerManager - return PowerManager() - if name == Context.LOCATION_SERVICE: - from .location import LocationManager - return LocationManager() - if name == Context.SENSOR_SERVICE: - from .hardware import SensorManager - return SensorManager() - if name == Context.CONNECTIVITY_SERVICE: - from .net import ConnectivityManager - return ConnectivityManager() - if name == Context.WIFI_SERVICE: - from .net_wifi import WifiManager - return WifiManager() - if name == Context.AUDIO_SERVICE: - from .media import AudioManager - return AudioManager() - if name == Context.TELEPHONY_SERVICE: - from .telephony import TelephonyManager - return TelephonyManager() - if name == Context.INPUT_METHOD_SERVICE: - from .view_inputmethod import InputMethodManager - return InputMethodManager() - if name == Context.CAMERA_SERVICE: - from .hardware_camera2 import CameraManager - return CameraManager() - if name == Context.BATTERY_SERVICE: - from .os import BatteryManager - return BatteryManager() - if name == Context.CLIPBOARD_SERVICE: - return ClipboardManager() - return self._services.get(name) - - def startService(self, intent: Any) -> Optional[Any]: - sys.stderr.write(f"[Context] startService: {intent}\n") - return None - - def stopService(self, intent: Any) -> bool: - return True - - def bindService(self, intent: Any, conn: Any, flags: int) -> bool: - sys.stderr.write(f"[Context] bindService: {intent}\n") - return True - - def unbindService(self, conn: Any): - pass - - def getString(self, res_id: int, *args) -> str: - """返回资源字符串。优先委托给 Resources,否则返回合理默认值。""" - try: - res = self.getResources() - val = res.getString(res_id, *args) - if val is not None: - return val - except Exception: - pass - return str(res_id) - - def getColor(self, color_id: int) -> int: - """返回资源颜色。优先委托给 Resources,否则返回不透明黑色。""" - try: - res = self.getResources() - return res.getColor(color_id) - except Exception: - return 0xFF000000 - - def getDrawable(self, drawable_id: int): - """返回 Drawable。优先委托给 Resources。""" - try: - res = self.getResources() - return res.getDrawable(drawable_id) - except Exception: - return None - - def getWallpaper(self): - """返回壁纸 Drawable。桌面端返回 None(无系统壁纸)。""" - return None - - def getWallpaperDesiredMinimumWidth(self) -> int: - return 1080 - - def getWallpaperDesiredMinimumHeight(self) -> int: - return 1920 - - def checkCallingPermission(self, permission: str) -> int: - from .content_pm import PackageManager - return PackageManager.PERMISSION_GRANTED - - def checkSelfPermission(self, permission: str) -> int: - from .content_pm import PackageManager - return PackageManager.PERMISSION_GRANTED - - def shouldShowRequestPermissionRationale(self, permission: str) -> bool: - return False - - def enforcePermission(self, permission: str, pid: int, uid: int, message: Optional[str]): - pass - - def createPackageContext(self, package_name: str, flags: int) -> "Context": - ctx = Context() - ctx._package_name = package_name - return ctx - - def isDeviceProtectedStorage(self) -> bool: - return False - - def isCredentialProtectedStorage(self) -> bool: - return False - - def deleteFile(self, name: str) -> bool: - path = os.path.join(self.getFilesDir(), name) - try: - os.remove(path) - return True - except OSError: - return False - - def fileList(self) -> List[str]: - try: - return os.listdir(self.getFilesDir()) - except OSError: - return [] - - def getDatabasePath(self, name: str): - path = os.path.join(_app_data_dir, "databases", name) - os.makedirs(os.path.dirname(path), exist_ok=True) - return path - - def databaseList(self) -> List[str]: - db_dir = os.path.join(_app_data_dir, "databases") - try: - return [f for f in os.listdir(db_dir) if f.endswith(".db")] - except OSError: - return [] - - def deleteDatabase(self, name: str) -> bool: - db_path = os.path.join(_app_data_dir, "databases", name) - try: - os.remove(db_path) - return True - except OSError: - return False - - def getDir(self, name: str, mode: int) -> str: - path = os.path.join(_app_data_dir, name) - os.makedirs(path, exist_ok=True) - return path - - def toString(self) -> str: - return f"Context{{{self._package_name}}}" - - # ─── 文件流 / 权限 (扩展) ───────────────────────────────── - - def openFileInput(self, name): - path = os.path.join(self.getFilesDir(), name) - return open(path, 'rb') - - def openFileOutput(self, name, mode): - path = os.path.join(self.getFilesDir(), name) - os.makedirs(os.path.dirname(path), exist_ok=True) - return open(path, 'wb') - - def getFileStreamPath(self, name): - from ..java.io import File - return File(self.getFilesDir(), name) - - def grantUriPermission(self, toPackage, uri, modeFlags): - pass - - def revokeUriPermission(self, uri, modeFlags): - pass - - def checkUriPermission(self, uri, pid, uid, modeFlags): - return 0 # PERMISSION_GRANTED - - def checkPermission(self, permission, pid, uid): - return 0 # PERMISSION_GRANTED - - def enforceUriPermission(self, uri, pid, uid, modeFlags, message): - pass - - # ─── 资源 / 主题 (扩展) ─────────────────────────────────── - - def getApplicationInfo(self): - from .content_pm import ApplicationInfo - return ApplicationInfo() - - def getTheme(self): - return getattr(self, '_theme', 0) - - def setTheme(self, resid): - self._theme = resid - - def getText(self, resId): - """返回资源文本。优先委托给 Resources。""" - try: - res = self.getResources() - val = res.getText(resId) - if val is not None: - return val - except Exception: - pass - return str(resId) - - def getQuantityString(self, resId, quantity, *formatArgs): - """返回复数字符串。优先委托给 Resources。""" - try: - res = self.getResources() - return res.getQuantityString(resId, quantity, *formatArgs) - except Exception: - return str(resId) - - def getStringArray(self, resId): - """返回字符串数组。优先委托给 Resources。""" - try: - res = self.getResources() - val = res.getStringArray(resId) - if val is not None: - return val - except Exception: - pass - return [] - - def getTextArray(self, resId): - """返回文本数组。优先委托给 Resources。""" - try: - res = self.getResources() - val = res.getTextArray(resId) - if val is not None: - return val - except Exception: - pass - return [] - - def getIntArray(self, resId): - """返回整型数组。优先委托给 Resources。""" - try: - res = self.getResources() - val = res.getIntArray(resId) - if val is not None: - return val - except Exception: - pass - return [] - - # ─── Context 派生 / 回调 (扩展) ─────────────────────────── - - def createConfigurationContext(self, overrideConfiguration): - return Context() - - def createDisplayContext(self, display): - return Context() - - def registerComponentCallbacks(self, callback): - pass - - def unregisterComponentCallbacks(self, callback): - pass - - def getExternalStoragePublicDirectory(self, type_): - from ..java.io import File - from .os import Environment - return Environment.getExternalStoragePublicDirectory(type_) - - def getDisplayMetrics(self): - from .util import DisplayMetrics - return DisplayMetrics() - - -class ContextWrapper(Context): - """ContextWrapper — Context 的包装器""" - - def __init__(self, base: Context): - super().__init__() - self._base = base - - def getBaseContext(self) -> Context: - return self._base - - def getPackageName(self) -> str: - return self._base.getPackageName() - - def getFilesDir(self): - return self._base.getFilesDir() - - def getCacheDir(self): - return self._base.getCacheDir() - - def startActivity(self, intent: Any): - self._base.startActivity(intent) - - def sendBroadcast(self, intent: Any): - self._base.sendBroadcast(intent) - - def getContentResolver(self) -> ContentResolver: - return self._base.getContentResolver() - - def getSharedPreferences(self, name: str, mode: int) -> "SharedPreferences": - return self._base.getSharedPreferences(name, mode) - - def getSystemService(self, name: str) -> Any: - return self._base.getSystemService(name) - - def startService(self, intent: Any) -> Optional[Any]: - return self._base.startService(intent) - - def bindService(self, intent: Any, conn: Any, flags: int) -> bool: - return self._base.bindService(intent, conn, flags) - - def registerReceiver(self, receiver: Optional[Any], filter: Any) -> Optional[Any]: - return self._base.registerReceiver(receiver, filter) - - def unregisterReceiver(self, receiver: Any): - self._base.unregisterReceiver(receiver) - - -# ═══════════════════════════════════════════════════════════════ -# IntentFilter -# ═══════════════════════════════════════════════════════════════ - -class IntentFilter(Stub): - """IntentFilter — 描述 Intent 匹配规则""" - - MATCH_CATEGORY_EMPTY = 0x100000 - MATCH_CATEGORY_HOST = 0x200000 - MATCH_CATEGORY_PORT = 0x400000 - MATCH_CATEGORY_PATH = 0x800000 - MATCH_CATEGORY_SCHEME = 0x1000000 - MATCH_CATEGORY_SCHEME_SPECIFIC_PART = 0x2000000 - MATCH_CATEGORY_TYPE = 0x4000000 - NO_MATCH_ACTION = -3 - NO_MATCH_CATEGORY = -4 - NO_MATCH_DATA = -5 - NO_MATCH_TYPE = -6 - - def __init__(self, action: Optional[str] = None): - super().__init__() - self._action = action - self._actions: List[str] = [] - self._categories: List[str] = [] - self._data_schemes: List[str] = [] - self._data_authorities: List[str] = [] - self._data_paths: List[str] = [] - self._data_types: List[str] = [] - self._priority = 0 - if action: - self.addAction(action) - - def addAction(self, action: str): - if action not in self._actions: - self._actions.append(action) - - def addCategory(self, category: str): - if category not in self._categories: - self._categories.append(category) - - def addDataScheme(self, scheme: str): - if scheme not in self._data_schemes: - self._data_schemes.append(scheme) - - def addDataAuthority(self, host: str, port: int = -1): - auth = f"{host}:{port}" if port >= 0 else host - if auth not in self._data_authorities: - self._data_authorities.append(auth) - - def addDataPath(self, path: str, pattern_type: int = 0): - if path not in self._data_paths: - self._data_paths.append(path) - - def addDataType(self, type_: str): - if type_ not in self._data_types: - self._data_types.append(type_) - - def hasAction(self, action: str) -> bool: - return action in self._actions - - def hasCategory(self, category: str) -> bool: - return category in self._categories - - def hasDataScheme(self, scheme: str) -> bool: - return scheme in self._data_schemes - - def countActions(self) -> int: - return len(self._actions) - - def getAction(self, index: int) -> str: - return self._actions[index] - - def setPriority(self, priority: int): - self._priority = priority - - def getPriority(self) -> int: - return self._priority - - def match(self, action: str, type_: Optional[str], scheme: Optional[str], - data: Optional[Any], categories: Optional[Set[str]], tag: str) -> int: - if action and action not in self._actions: - return self.NO_MATCH_ACTION - return self.MATCH_CATEGORY_EMPTY - - def matchAction(self, action: str) -> bool: - return action in self._actions - - def matchCategories(self, categories: Set[str]) -> Optional[str]: - return None - - def matchData(self, type_: str, scheme: str, data: Any) -> int: - return self.MATCH_CATEGORY_EMPTY - - def dump(self, du: Any, prefix: str): - pass - - def describeContents(self) -> int: - return 0 - - def toString(self) -> str: - return f"IntentFilter(actions={self._actions})" - - -# ═══════════════════════════════════════════════════════════════ -# BroadcastReceiver -# ═══════════════════════════════════════════════════════════════ - -class BroadcastReceiver(Stub): - """BroadcastReceiver — 广播接收器""" - - RESULT_OK = -1 - RESULT_CANCELED = 0 - RESULT_REDIRECT = 1 - - def __init__(self): - super().__init__() - self._result_code = self.RESULT_OK - self._result_data = "" - self._result_extras: Optional[ContentValues] = None - self._ordered = False - self._initial_sticky = False - - def onReceive(self, context: Context, intent: Any): - """Override this method to handle broadcasts""" - pass - - def setResultCode(self, code: int): - self._result_code = code - - def getResultCode(self) -> int: - return self._result_code - - def setResultData(self, data: str): - self._result_data = data - - def getResultData(self) -> str: - return self._result_data - - def setResult(self, code: int, data: str, extras: Optional[ContentValues]): - self._result_code = code - self._result_data = data - self._result_extras = extras - - def setResultExtras(self, extras: ContentValues): - self._result_extras = extras - - def getResultExtras(self, make_map: bool) -> Optional[ContentValues]: - return self._result_extras - - def abortBroadcast(self): - self._result_code = self.RESULT_CANCELED - - def isOrderedBroadcast(self) -> bool: - return self._ordered - - def isInitialStickyBroadcast(self) -> bool: - return self._initial_sticky - - def getDebugUnregister(self) -> bool: - return False - - def toString(self) -> str: - return f"BroadcastReceiver@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# SharedPreferences / SharedPreferencesEditor -# ═══════════════════════════════════════════════════════════════ - -_SHARED_PREFS_DIR = os.path.join(_app_data_dir, "shared_prefs") -os.makedirs(_SHARED_PREFS_DIR, exist_ok=True) - -class SharedPreferences(Stub): - """SharedPreferences — JSON 文件实现的持久化键值存储""" - - def __init__(self, name: str): - super().__init__() - self._name = name - self._lock = threading.Lock() - self._file_path = os.path.join(_SHARED_PREFS_DIR, f"{name}.json") - self._data: Dict[str, Any] = {} - self._load() - - def _load(self): - try: - if os.path.exists(self._file_path): - with open(self._file_path, "r", encoding="utf-8") as f: - self._data = json.load(f) - else: - self._data = {} - except (json.JSONDecodeError, OSError): - self._data = {} - - def _save(self): - with self._lock: - os.makedirs(os.path.dirname(self._file_path), exist_ok=True) - with open(self._file_path, "w", encoding="utf-8") as f: - json.dump(self._data, f, indent=2, ensure_ascii=False) - - def getString(self, key: str, def_value: Optional[str] = None) -> Optional[str]: - v = self._data.get(key) - return str(v) if v is not None else def_value - - def getInt(self, key: str, def_value: int = 0) -> int: - v = self._data.get(key) - if isinstance(v, int) or isinstance(v, float): - return int(v) - return def_value - - def getLong(self, key: str, def_value: int = 0) -> int: - return self.getInt(key, def_value) - - def getFloat(self, key: str, def_value: float = 0.0) -> float: - v = self._data.get(key) - if isinstance(v, (int, float)): - return float(v) - return def_value - - def getBoolean(self, key: str, def_value: bool = False) -> bool: - v = self._data.get(key) - if isinstance(v, bool): - return v - if isinstance(v, str): - return v.lower() in ("true", "1", "yes") - return def_value - - def getAll(self) -> Dict[str, Any]: - return dict(self._data) - - def contains(self, key: str) -> bool: - return key in self._data - - def edit(self) -> "SharedPreferencesEditor": - return SharedPreferencesEditor(self) - - def registerOnSharedPreferenceChangeListener(self, listener): - if not hasattr(self, '_listeners'): - self._listeners = [] - self._listeners.append(listener) - - def unregisterOnSharedPreferenceChangeListener(self, listener): - if hasattr(self, '_listeners'): - try: - self._listeners.remove(listener) - except ValueError: - pass - - def getSharedPrefsFile(self): - return self._file_path - - def toString(self) -> str: - return f"SharedPreferences({self._name})" - - -class SharedPreferencesEditor(Stub): - """SharedPreferences Editor""" - - def __init__(self, prefs: SharedPreferences): - super().__init__() - self._prefs = prefs - self._modified: Dict[str, Any] = {} - self._removed: Set[str] = set() - self._clear_all = False - - def putString(self, key: str, value: str) -> "SharedPreferencesEditor": - self._modified[key] = value - self._removed.discard(key) - return self - - def putInt(self, key: str, value: int) -> "SharedPreferencesEditor": - self._modified[key] = value - self._removed.discard(key) - return self - - def putLong(self, key: str, value: int) -> "SharedPreferencesEditor": - self._modified[key] = value - self._removed.discard(key) - return self - - def putFloat(self, key: str, value: float) -> "SharedPreferencesEditor": - self._modified[key] = value - self._removed.discard(key) - return self - - def putBoolean(self, key: str, value: bool) -> "SharedPreferencesEditor": - self._modified[key] = value - self._removed.discard(key) - return self - - def remove(self, key: str) -> "SharedPreferencesEditor": - self._removed.add(key) - self._modified.pop(key, None) - return self - - def clear(self) -> "SharedPreferencesEditor": - self._clear_all = True - self._modified.clear() - self._removed.clear() - return self - - def apply(self): - """异步保存""" - self.commit() - - def commit(self) -> bool: - try: - if self._clear_all: - self._prefs._data.clear() - for key in self._removed: - self._prefs._data.pop(key, None) - self._prefs._data.update(self._modified) - self._prefs._save() - # 触发监听器 - sp = getattr(self, '_prefs', None) - if sp: - for key in self._modified: - for listener in getattr(sp, '_listeners', []): - listener.onSharedPreferenceChanged(sp, key) - self._modified.clear() - self._removed.clear() - self._clear_all = False - return True - except Exception: - return False - - -# ═══════════════════════════════════════════════════════════════ -# ComponentName -# ═══════════════════════════════════════════════════════════════ - -class ComponentName(Stub): - """ComponentName — 标识包中的特定组件""" - - def __init__(self, pkg: str, cls: str): - super().__init__() - self._package = pkg - self._classname = cls - - def getPackageName(self) -> str: - return self._package - - def getClassName(self) -> str: - return self._classname - - def getShortClassName(self) -> str: - return self._classname.split(".")[-1] if "." in self._classname else self._classname - - def flattenToString(self) -> str: - return f"{self._package}/{self._classname}" - - def flattenToShortString(self) -> str: - return f"{self._package}/{self.getShortClassName()}" - - @staticmethod - def unflattenFromString(str_: str) -> Optional["ComponentName"]: - if "/" in str_: - parts = str_.split("/", 1) - return ComponentName(parts[0], parts[1]) - return None - - def toString(self) -> str: - return f"ComponentInfo{{{self._package}/{self._classname}}}" - - def __repr__(self): - return self.toString() - - -# ═══════════════════════════════════════════════════════════════ -# ClipData / ClipDescription -# ═══════════════════════════════════════════════════════════════ - -class ClipDescription(Stub): - """ClipDescription — 描述剪贴板数据""" - - MIMETYPE_TEXT_PLAIN = "text/plain" - MIMETYPE_TEXT_HTML = "text/html" - MIMETYPE_TEXT_URILIST = "text/uri-list" - MIMETYPE_TEXT_INTENT = "text/vnd.android.intent" - - def __init__(self, label: str, mime_types: List[str]): - super().__init__() - self._label = label - self._mime_types = list(mime_types) - self._timestamp = 0 - - def getLabel(self) -> str: - return self._label - - def getMimeType(self, index: int) -> str: - if 0 <= index < len(self._mime_types): - return self._mime_types[index] - return "" - - def getMimeTypeCount(self) -> int: - return len(self._mime_types) - - def filterMimeTypes(self, mime_type: str) -> bool: - return mime_type in self._mime_types - - def compareMimeTypes(self, mime_type1: str, mime_type2: str) -> bool: - return mime_type1 == mime_type2 - - def getTimestamp(self) -> int: - return self._timestamp - - def toString(self) -> str: - return f"ClipDescription({self._label}, {self._mime_types})" - - -class ClipData(Stub): - """ClipData — 剪贴板数据""" - - def __init__(self, description: ClipDescription, item: "ClipData.Item"): - super().__init__() - self._description = description - self._items: List["ClipData.Item"] = [item] if item else [] - - class Item(Stub): - """单个剪贴板条目""" - def __init__(self, text: Optional[str] = None, intent: Optional[Any] = None, uri: Optional[Uri] = None): - super().__init__() - self._text = text - self._intent = intent - self._uri = uri - self._html_text = None - - def getText(self) -> Optional[str]: - return self._text - - def getIntent(self): - return self._intent - - def getUri(self) -> Optional[Uri]: - return self._uri - - def getHtmlText(self) -> Optional[str]: - return self._html_text - - def setHtmlText(self, html_text: str): - self._html_text = html_text - - def coerceToText(self, context: Context) -> str: - if self._text: - return self._text - if self._uri: - return str(self._uri) - if self._intent: - return str(self._intent) - return "" - - def coerceToHtmlText(self, context: Context) -> Optional[str]: - return self._html_text - - def toString(self) -> str: - return f"ClipData.Item({self._text})" - - @staticmethod - def newPlainText(label: str, text: str) -> "ClipData": - desc = ClipDescription(label, [ClipDescription.MIMETYPE_TEXT_PLAIN]) - return ClipData(desc, ClipData.Item(text=text)) - - @staticmethod - def newUri(cr: ContentResolver, label: str, uri: Uri) -> "ClipData": - desc = ClipDescription(label, [ClipDescription.MIMETYPE_TEXT_URILIST]) - return ClipData(desc, ClipData.Item(uri=uri)) - - @staticmethod - def newIntent(label: str, intent: Any) -> "ClipData": - desc = ClipDescription(label, [ClipDescription.MIMETYPE_TEXT_INTENT]) - return ClipData(desc, ClipData.Item(intent=intent)) - - def getDescription(self) -> ClipDescription: - return self._description - - def addItem(self, item: "ClipData.Item"): - self._items.append(item) - - def getItemAt(self, index: int) -> Optional["ClipData.Item"]: - if 0 <= index < len(self._items): - return self._items[index] - return None - - def getItemCount(self) -> int: - return len(self._items) - - def toString(self) -> str: - return f"ClipData({self._description})" - - -# ═══════════════════════════════════════════════════════════════ -# ContentProvider / ContentProviderClient -# ═══════════════════════════════════════════════════════════════ - -class ContentProvider(Stub): - """ContentProvider — 内容提供者基类""" - - def __init__(self): - super().__init__() - self._context: Optional[Context] = None - self._calling_package = "" - - def onCreate(self) -> bool: - return True - - def query(self, uri: Uri, projection: Optional[List[str]], selection: Optional[str], - selection_args: Optional[List[str]], sort_order: Optional[str]) -> Optional[Any]: - """默认实现:委托给关联 Context 的 ContentResolver。""" - if self._context is not None: - return self._context.getContentResolver().query( - uri, projection, selection, selection_args, sort_order) - return None - - def insert(self, uri: Uri, values: ContentValues) -> Optional[Uri]: - if self._context is not None: - return self._context.getContentResolver().insert(uri, values) - return None - - def update(self, uri: Uri, values: ContentValues, selection: Optional[str], - selection_args: Optional[List[str]]) -> int: - if self._context is not None: - return self._context.getContentResolver().update(uri, values, selection, selection_args) - return 0 - - def delete(self, uri: Uri, selection: Optional[str], selection_args: Optional[List[str]]) -> int: - if self._context is not None: - return self._context.getContentResolver().delete(uri, selection, selection_args) - return 0 - - def getType(self, uri: Uri) -> Optional[str]: - """返回 URI 的 MIME 类型。""" - path = ContentResolver._uri_to_file_path(uri) - if path: - mime, _ = mimetypes.guess_type(path) - return mime or "application/octet-stream" - return None - - def getStreamTypes(self, uri: Uri, mime_type_filter: str) -> Optional[List[str]]: - mime = self.getType(uri) - if mime is None: - return None - return [mime] - - def openFile(self, uri: Uri, mode: str) -> Any: - """打开文件。对 file:// URI 返回真实文件对象。""" - path = ContentResolver._uri_to_file_path(uri) - if path and os.path.exists(path): - try: - fmode = "rb" if "r" in mode else "wb" - return open(path, fmode) - except OSError: - return None - return None - - def openAssetFile(self, uri: Uri, mode: str) -> Any: - return self.openFile(uri, mode) - - def attachInfo(self, context: Context, info: Any): - self._context = context - - def getContext(self) -> Optional[Context]: - return self._context - - def getCallingPackage(self) -> str: - return self._calling_package - - def applyBatch(self, operations: List[Any]) -> List[Any]: - return [] - - def bulkInsert(self, uri: Uri, values: List[ContentValues]) -> int: - return len(values) if values else 0 - - def isTemporary(self) -> bool: - return False - - def shutdown(self): - pass - - def toString(self) -> str: - return f"ContentProvider@{hex(id(self))}" - - -class ContentProviderClient(Stub): - """ContentProviderClient — 内容提供者客户端""" - - def __init__(self, uri: Optional[Uri] = None): - super().__init__() - self._uri = uri - - def query(self, uri: Uri, projection: Optional[List[str]], selection: Optional[str], - selection_args: Optional[List[str]], sort_order: Optional[str]) -> Optional[Any]: - """委托给 ContentResolver 处理 file:// URI。""" - resolver = ContentResolver() - return resolver.query(uri, projection, selection, selection_args, sort_order) - - def insert(self, uri: Uri, values: ContentValues) -> Optional[Uri]: - resolver = ContentResolver() - return resolver.insert(uri, values) - - def update(self, uri: Uri, values: ContentValues, selection: Optional[str], - selection_args: Optional[List[str]]) -> int: - resolver = ContentResolver() - return resolver.update(uri, values, selection, selection_args) - - def delete(self, uri: Uri, selection: Optional[str], selection_args: Optional[List[str]]) -> int: - resolver = ContentResolver() - return resolver.delete(uri, selection, selection_args) - - def applyBatch(self, operations: List[Any]) -> List[Any]: - resolver = ContentResolver() - return resolver.applyBatch("", operations) - - def bulkInsert(self, uri: Uri, values: List[ContentValues]) -> int: - resolver = ContentResolver() - return resolver.bulkInsert(uri, values) - - def getType(self, uri: Uri) -> Optional[str]: - """返回 URI 的 MIME 类型。""" - path = ContentResolver._uri_to_file_path(uri) - if path: - mime, _ = mimetypes.guess_type(path) - return mime or "application/octet-stream" - return None - - def getStreamTypes(self, uri: Uri, mime_type_filter: str) -> Optional[List[str]]: - mime = self.getType(uri) - if mime is None: - return None - return [mime] - - def call(self, method: str, arg: Optional[str], extras: Optional[ContentValues]) -> Optional[ContentValues]: - return None - - def release(self): - pass - - def close(self): - pass - - def openFile(self, uri: Uri, mode: str) -> Any: - """打开文件。对 file:// URI 返回真实文件对象。""" - path = ContentResolver._uri_to_file_path(uri) - if path and os.path.exists(path): - try: - fmode = "rb" if "r" in mode else "wb" - return open(path, fmode) - except OSError: - return None - return None - - def openAssetFile(self, uri: Uri, mode: str) -> Any: - return self.openFile(uri, mode) - - def openTypedAssetFile(self, uri: Uri, mime_type: str, opts: Optional[ContentValues]) -> Any: - return self.openFile(uri, "r") - - def getLocalContentProvider(self) -> Optional[ContentProvider]: - return None - - -class ContentProviderOperation(Stub): - """ContentProviderOperation — 批量操作""" - - TYPE_INSERT = 1 - TYPE_UPDATE = 2 - TYPE_DELETE = 3 - TYPE_ASSERT = 4 - - class Builder(Stub): - def __init__(self, type_: int, uri: Uri): - super().__init__() - self._type = type_ - self._uri = uri - self._values: Optional[ContentValues] = None - self._selection: Optional[str] = None - self._selection_args: Optional[List[str]] = None - - def withValue(self, key: str, value: Any) -> "ContentProviderOperation.Builder": - if self._values is None: - self._values = ContentValues() - self._values.put(key, value) - return self - - def withValues(self, values: ContentValues) -> "ContentProviderOperation.Builder": - self._values = values - return self - - def withSelection(self, selection: str, selection_args: List[str]) -> "ContentProviderOperation.Builder": - self._selection = selection - self._selection_args = selection_args - return self - - def build(self) -> "ContentProviderOperation": - op = ContentProviderOperation() - op._type = self._type - op._uri = self._uri - op._values = self._values - op._selection = self._selection - op._selection_args = self._selection_args - return op - - @staticmethod - def newInsert(uri: Uri) -> "ContentProviderOperation.Builder": - return ContentProviderOperation.Builder(ContentProviderOperation.TYPE_INSERT, uri) - - @staticmethod - def newUpdate(uri: Uri) -> "ContentProviderOperation.Builder": - return ContentProviderOperation.Builder(ContentProviderOperation.TYPE_UPDATE, uri) - - @staticmethod - def newDelete(uri: Uri) -> "ContentProviderOperation.Builder": - return ContentProviderOperation.Builder(ContentProviderOperation.TYPE_DELETE, uri) - - @staticmethod - def newAssert(uri: Uri) -> "ContentProviderOperation.Builder": - return ContentProviderOperation.Builder(ContentProviderOperation.TYPE_ASSERT, uri) - - -class ContentProviderResult(Stub): - """ContentProviderResult — 操作结果""" - - def __init__(self, uri: Optional[Uri] = None, count: Optional[int] = None): - super().__init__() - self.uri = uri - self.count = count - - -# ═══════════════════════════════════════════════════════════════ -# Loader / CursorLoader / DialogInterface -# ═══════════════════════════════════════════════════════════════ - -class Loader(Stub): - """Loader — 异步数据加载器基类""" - - def __init__(self, context: Context): - super().__init__() - self._context = context - self._started = False - self._abandoned = False - self._listeners: List = [] - - def deliverResult(self, data: Any): - for listener in self._listeners: - try: - listener.onLoadFinished(self, data) - except Exception: - pass - - def startLoading(self): - self._started = True - - def forceLoad(self): - self.deliverResult(None) - - def stopLoading(self): - self._started = False - - def abandon(self): - self._abandoned = True - - def isStarted(self) -> bool: - return self._started - - def isAbandoned(self) -> bool: - return self._abandoned - - def reset(self): - self._started = False - self._abandoned = False - - def registerListener(self, listener): - if listener not in self._listeners: - self._listeners.append(listener) - - def unregisterListener(self, listener): - if listener in self._listeners: - self._listeners.remove(listener) - - def dataToString(self, data: Any) -> str: - return str(data) - - def toString(self) -> str: - return f"Loader@{hex(id(self))}" - - -class CursorLoader(Loader): - """CursorLoader — 游标加载器""" - - def __init__(self, context: Context, uri: Uri, projection: Optional[List[str]], - selection: Optional[str], selection_args: Optional[List[str]], sort_order: Optional[str]): - super().__init__(context) - self._uri = uri - self._projection = projection - self._selection = selection - self._selection_args = selection_args - self._sort_order = sort_order - - def getUri(self) -> Uri: - return self._uri - - def setUri(self, uri: Uri): - self._uri = uri - - def getProjection(self) -> Optional[List[str]]: - return self._projection - - def setProjection(self, projection: Optional[List[str]]): - self._projection = projection - - def getSelection(self) -> Optional[str]: - return self._selection - - def setSelection(self, selection: Optional[str]): - self._selection = selection - - def getSelectionArgs(self) -> Optional[List[str]]: - return self._selection_args - - def setSelectionArgs(self, args: Optional[List[str]]): - self._selection_args = args - - def getSortOrder(self) -> Optional[str]: - return self._sort_order - - def setSortOrder(self, sort_order: Optional[str]): - self._sort_order = sort_order - - def loadInBackground(self): - """在后台加载数据,委托给 ContentResolver.query。""" - if self._context is not None: - try: - resolver = self._context.getContentResolver() - return resolver.query(self._uri, self._projection, self._selection, - self._selection_args, self._sort_order) - except Exception: - pass - return None - - -class DialogInterface(Stub): - """DialogInterface — 对话框接口常量""" - - BUTTON_POSITIVE = -1 - BUTTON_NEGATIVE = -2 - BUTTON_NEUTRAL = -3 - - def cancel(self): - pass - - def dismiss(self): - pass - - -# ═══════════════════════════════════════════════════════════════ -# SyncAdapter / SyncResult -# ═══════════════════════════════════════════════════════════════ - -class SyncAdapter(Stub): - """SyncAdapter — 同步适配器""" - - def __init__(self, context: Context, auto_initialize: bool, allow_parallel_syncs: bool): - super().__init__() - self._context = context - - def onPerformSync(self, account: Any, extras: ContentValues, authority: str, - provider: ContentProviderClient, sync_result: "SyncResult"): - pass - - def onSecurityException(self, account: Any, extras: ContentValues, - authority: str, sync_result: "SyncResult"): - pass - - -class SyncResult(Stub): - """SyncResult — 同步结果""" - - def __init__(self): - super().__init__() - self.stats = self._Stats() - - class _Stats: - def __init__(self): - self.numEntries = 0 - self.numUpdates = 0 - self.numDeletes = 0 - self.numInserts = 0 - self.numSkippedEntries = 0 - - def clear(self): - self.stats = self._Stats() - - def hasError(self) -> bool: - return False - - def hasSoftError(self) -> bool: - return False - - def hasHardError(self) -> bool: - return False - - def madeSomeProgress(self) -> bool: - return self.stats.numEntries > 0 - - def toString(self) -> str: - return f"SyncResult({self.stats.numEntries} entries)" - - -# ═══════════════════════════════════════════════════════════════ -# ClipboardManager -# ═══════════════════════════════════════════════════════════════ - -class ClipboardManager(Stub): - """ClipboardManager — 剪贴板管理器""" - - def getText(self): - if _USE_LINUX: - return LinuxClipboard.read() - return "" - - def setText(self, text): - if _USE_LINUX: - return LinuxClipboard.write(text) - return False - - def hasText(self): - if _USE_LINUX: - return len(LinuxClipboard.read()) > 0 - return False - - -# ═══════════════════════════════════════════════════════════════ -# 替换 _core.py 中的 _GlobalContext -# ═══════════════════════════════════════════════════════════════ - -_real_context = Context() -# 将 _core 中的 android_context 替换为真实 Context -import importlib -try: - _core_mod = importlib.import_module(".._core", __package__) - _core_mod.android_context.__class__ = Context -except Exception: - pass - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Uri", - "ContentValues", - "ContentResolver", - "Context", - "ContextWrapper", - "IntentFilter", - "BroadcastReceiver", - "SharedPreferences", - "SharedPreferencesEditor", - "ComponentName", - "ClipData", - "ClipDescription", - "ContentProvider", - "ContentProviderClient", - "ContentProviderOperation", - "ContentProviderResult", - "CursorLoader", - "Loader", - "DialogInterface", - "SyncAdapter", - "SyncResult", - "ClipboardManager", -] \ No newline at end of file diff --git a/android_pyapi/android/content_intent.py b/android_pyapi/android/content_intent.py deleted file mode 100644 index 31ef44c..0000000 --- a/android_pyapi/android/content_intent.py +++ /dev/null @@ -1,2087 +0,0 @@ -""" -Android content Intent 完整扩展组件 — Intent (完整版), PendingIntent, -IntentSender, IntentFilter (扩展), ShortcutInfo, ShortcutManager 等 -包含所有标准 ACTION_*, CATEGORY_*, EXTRA_*, FLAG_*, URI_*, SCHEME_*, MIME_* 常量 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import json -import time -from typing import Any, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException -from .app import Intent as _BaseIntent -from .app import PendingIntent as _BasePendingIntent -from .content import IntentFilter as _BaseIntentFilter - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "Intent", "PendingIntent", "IntentSender", - "IntentService", - "IntentFilter", "MatchingFlags", "ShortcutInfo", - "ShortcutManager", -] - - -# ═══════════════════════════════════════════════════════════════ -# ACTION_* — 标准 Android Intent Action 常量 -# ═══════════════════════════════════════════════════════════════ - -ACTION_MAIN = "android.intent.action.MAIN" -ACTION_VIEW = "android.intent.action.VIEW" -ACTION_ATTACH_DATA = "android.intent.action.ATTACH_DATA" -ACTION_EDIT = "android.intent.action.EDIT" -ACTION_PICK = "android.intent.action.PICK" -ACTION_CHOOSER = "android.intent.action.CHOOSER" -ACTION_GET_CONTENT = "android.intent.action.GET_CONTENT" -ACTION_DIAL = "android.intent.action.DIAL" -ACTION_CALL = "android.intent.action.CALL" -ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON" -ACTION_SEND = "android.intent.action.SEND" -ACTION_SENDTO = "android.intent.action.SENDTO" -ACTION_SEND_MULTIPLE = "android.intent.action.SEND_MULTIPLE" -ACTION_ANSWER = "android.intent.action.ANSWER" -ACTION_INSERT = "android.intent.action.INSERT" -ACTION_DELETE = "android.intent.action.DELETE" -ACTION_RUN = "android.intent.action.RUN" -ACTION_SYNC = "android.intent.action.SYNC" -ACTION_PICK_ACTIVITY = "android.intent.action.PICK_ACTIVITY" -ACTION_SEARCH = "android.intent.action.SEARCH" -ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH" -ACTION_FACTORY_TEST = "android.intent.action.FACTORY_TEST" -ACTION_TIME_TICK = "android.intent.action.TIME_TICK" -ACTION_TIME_CHANGED = "android.intent.action.TIME_SET" -ACTION_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED" -ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED" -ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED" -ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED" -ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED" -ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED" -ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED" -ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED" -ACTION_PACKAGE_FULLY_REMOVED = "android.intent.action.PACKAGE_FULLY_REMOVED" -ACTION_PACKAGE_FIRST_LAUNCH = "android.intent.action.PACKAGE_FIRST_LAUNCH" -ACTION_PACKAGE_NEEDS_VERIFICATION = "android.intent.action.PACKAGE_NEEDS_VERIFICATION" -ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED" -ACTION_EXTERNAL_APPLICATIONS_AVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE" -ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE" -ACTION_APPLICATION_PREFERENCES = "android.intent.action.APPLICATION_PREFERENCES" -ACTION_APPLICATION_LOCALE_CHANGED = "android.intent.action.APPLICATION_LOCALE_CHANGED" -ACTION_APPLICATION_GRANT_URI_PERMISSION = "android.intent.action.APPLICATION_GRANT_URI_PERMISSION" -ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED" -ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON" -ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED" -ACTION_DOCK_EVENT = "android.intent.action.DOCK_EVENT" -ACTION_DREAMING_STARTED = "android.intent.action.DREAMING_STARTED" -ACTION_DREAMING_STOPPED = "android.intent.action.DREAMING_STOPPED" -ACTION_HEADSET_PLUG = "android.intent.action.HEADSET_PLUG" -ACTION_INPUT_METHOD_CHANGED = "android.intent.action.INPUT_METHOD_CHANGED" -ACTION_LOCALE_CHANGED = "android.intent.action.LOCALE_CHANGED" -ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED" -ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED" -ACTION_MANAGED_PROFILE_AVAILABLE = "android.intent.action.MANAGED_PROFILE_AVAILABLE" -ACTION_MANAGED_PROFILE_UNAVAILABLE = "android.intent.action.MANAGED_PROFILE_UNAVAILABLE" -ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED" -ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON" -ACTION_MEDIA_CHECKING = "android.intent.action.MEDIA_CHECKING" -ACTION_MEDIA_EJECT = "android.intent.action.MEDIA_EJECT" -ACTION_MEDIA_MOUNTED = "android.intent.action.MEDIA_MOUNTED" -ACTION_MEDIA_NOFS = "android.intent.action.MEDIA_NOFS" -ACTION_MEDIA_REMOVED = "android.intent.action.MEDIA_REMOVED" -ACTION_MEDIA_SCANNER_FINISHED = "android.intent.action.MEDIA_SCANNER_FINISHED" -ACTION_MEDIA_SCANNER_SCAN_FILE = "android.intent.action.MEDIA_SCANNER_SCAN_FILE" -ACTION_MEDIA_SCANNER_STARTED = "android.intent.action.MEDIA_SCANNER_STARTED" -ACTION_MEDIA_SHARED = "android.intent.action.MEDIA_SHARED" -ACTION_MEDIA_UNMOUNTABLE = "android.intent.action.MEDIA_UNMOUNTABLE" -ACTION_MEDIA_UNMOUNTED = "android.intent.action.MEDIA_UNMOUNTED" -ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL" -ACTION_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL" -ACTION_SCREEN_OFF = "android.intent.action.SCREEN_OFF" -ACTION_SCREEN_ON = "android.intent.action.SCREEN_ON" -ACTION_USER_PRESENT = "android.intent.action.USER_PRESENT" -ACTION_PROVIDER_CHANGED = "android.intent.action.PROVIDER_CHANGED" -ACTION_GTALK_SERVICE_CONNECTED = "android.intent.action.GTALK_CONNECTED" -ACTION_GTALK_SERVICE_DISCONNECTED = "android.intent.action.GTALK_DISCONNECTED" -ACTION_MY_PACKAGE_REPLACED = "android.intent.action.MY_PACKAGE_REPLACED" -ACTION_MY_PACKAGE_SUSPENDED = "android.intent.action.MY_PACKAGE_SUSPENDED" -ACTION_MY_PACKAGE_UNSUSPENDED = "android.intent.action.MY_PACKAGE_UNSUSPENDED" -ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED" -ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED" -ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED" -ACTION_WIFI_SCAN_AVAILABLE = "android.intent.action.WIFI_SCAN_AVAILABLE" -ACTION_DEVICE_STORAGE_LOW = "android.intent.action.DEVICE_STORAGE_LOW" -ACTION_DEVICE_STORAGE_OK = "android.intent.action.DEVICE_STORAGE_OK" -ACTION_BATTERY_CHANGED = "android.intent.action.BATTERY_CHANGED" -ACTION_BATTERY_LOW = "android.intent.action.BATTERY_LOW" -ACTION_BATTERY_OKAY = "android.intent.action.BATTERY_OKAY" -ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED" -ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED" -ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN" -ACTION_REBOOT = "android.intent.action.REBOOT" -ACTION_AIRPLANE_MODE_CHANGED = "android.intent.action.AIRPLANE_MODE" -ACTION_NETWORK_SET_TIME = "android.intent.action.NETWORK_SET_TIME" -ACTION_NETWORK_SET_TIMEZONE = "android.intent.action.NETWORK_SET_TIMEZONE" -ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED" -ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE" -ACTION_SIGNAL_STRENGTH_CHANGED = "android.intent.action.SIG_STR" -ACTION_DATA_CONNECTION_STATE_CHANGED = "android.intent.action.DATA_CONNECTION_STATE_CHANGED" -ACTION_ANY_DATA_CONNECTION_STATE_CHANGED = "android.intent.action.ANY_DATA_STATE" -ACTION_DATA_ACTIVITY = "android.intent.action.DATA_ACTIVITY" -ACTION_DATA_STALL = "android.intent.action.DATA_STALL" -ACTION_PROXY_CHANGE = "android.intent.action.PROXY_CHANGE" -ACTION_WALLPAPER_SETTINGS = "android.intent.action.WALLPAPER_SETTINGS" -ACTION_HDMI_AUDIO_PLUG = "android.intent.action.HDMI_AUDIO_PLUG" -ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT" -ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT" -ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE" -ACTION_VIEW_DOWNLOADS = "android.intent.action.VIEW_DOWNLOADS" -ACTION_DOCUMENT_SETTINGS = "android.intent.action.DOCUMENT_SETTINGS" -ACTION_QUICK_CLOCK = "android.intent.action.QUICK_CLOCK" -ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION" -ACTION_MANAGED_ROAMING_INDICATION = "android.intent.action.MANAGED_ROAMING_INDICATION" -ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND" -ACTION_ASSIST = "android.intent.action.ASSIST" -ACTION_QUICK_VIEW = "android.intent.action.QUICK_VIEW" -ACTION_CUSTOM_EVENT = "android.intent.action.CUSTOM_EVENT" -ACTION_GET_RESTRICTION_ENTRIES = "android.intent.action.GET_RESTRICTION_ENTRIES" -ACTION_SET_RESTRICTION = "android.intent.action.SET_RESTRICTION" -ACTION_DEFAULT = "android.intent.action.DEFAULT" -ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT" -ACTION_INSTALL_SHORTCUT = "android.intent.action.INSTALL_SHORTCUT" -ACTION_UNINSTALL_SHORTCUT = "com.android.launcher.action.UNINSTALL_SHORTCUT" -ACTION_INSTALL_PACKAGE = "android.intent.action.INSTALL_PACKAGE" -ACTION_UNINSTALL_PACKAGE = "android.intent.action.UNINSTALL_PACKAGE" -ACTION_INSTALL_FAILURE = "android.intent.action.INSTALL_FAILURE" -ACTION_UNINSTALL_FAILURE = "android.intent.action.UNINSTALL_FAILURE" -ACTION_VIEW_SECURITY_POLICY = "android.intent.action.VIEW_SECURITY_POLICY" -ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED" -ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED" -ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED" -ACTION_LOCK_TASK_ENTERING = "android.intent.action.LOCK_TASK_ENTERING" -ACTION_LOCK_TASK_EXITING = "android.intent.action.LOCK_TASK_EXITING" -ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR" -ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET" -ACTION_GET_ACCOUNTS = "android.accounts.GET_ACCOUNTS" -ACTION_ADD_ACCOUNT = "android.settings.ADD_ACCOUNT_SETTINGS" -ACTION_SETTINGS = "android.settings.SETTINGS" -ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS" -ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS" -ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS" -ACTION_NFC_SETTINGS = "android.settings.NFC_SETTINGS" -ACTION_NFCSHARING_SETTINGS = "android.settings.NFCSHARING_SETTINGS" -ACTION_SOUND_SETTINGS = "android.settings.SOUND_SETTINGS" -ACTION_DISPLAY_SETTINGS = "android.settings.DISPLAY_SETTINGS" -ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS" -ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS" -ACTION_NETWORK_OPERATOR_SETTINGS = "android.settings.NETWORK_OPERATOR_SETTINGS" -ACTION_DATA_ROAMING_SETTINGS = "android.settings.DATA_ROAMING_SETTINGS" -ACTION_INTERNAL_STORAGE_SETTINGS = "android.settings.INTERNAL_STORAGE_SETTINGS" -ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS" -ACTION_APP_NOTIFICATION_SETTINGS = "android.settings.APP_NOTIFICATION_SETTINGS" -ACTION_APPLICATION_DETAILS_SETTINGS = "android.settings.APPLICATION_DETAILS_SETTINGS" -ACTION_APPLICATION_DEVELOPMENT_SETTINGS = "android.settings.APPLICATION_DEVELOPMENT_SETTINGS" -ACTION_APPLICATION_SETTINGS = "android.settings.APPLICATION_SETTINGS" -ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS" -ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS" -ACTION_SYNC_SETTINGS = "android.settings.SYNC_SETTINGS" -ACTION_ADD_ACCOUNT_SETTINGS = "android.settings.ADD_ACCOUNT_SETTINGS" -ACTION_AIRPLANE_MODE_SETTINGS = "android.settings.AIRPLANE_MODE_SETTINGS" -ACTION_APN_SETTINGS = "android.settings.APN_SETTINGS" -ACTION_APPLICATION_SETTINGS_SHORT = "android.settings.APPLICATION_SETTINGS" -ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS = "android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS" -ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS" -ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION" -ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS" -ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" -ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS" -ACTION_USAGE_ACCESS_SETTINGS = "android.settings.USAGE_ACCESS_SETTINGS" -ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS" -ACTION_VPN_SETTINGS = "android.settings.VPN_SETTINGS" -ACTION_WIRELESS_SETTINGS = "android.settings.WIRELESS_SETTINGS" -ACTION_WIFI_ADD_NETWORKS = "android.settings.WIFI_ADD_NETWORKS" -ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS" -ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS" -ACTION_ACCESSIBILITY_SETTINGS = "android.settings.ACCESSIBILITY_SETTINGS" -ACTION_ACCOUNT_SYNC_SETTINGS = "android.settings.ACCOUNT_SYNC_SETTINGS" -ACTION_APP_OPEN_BY_DEFAULT_SETTINGS = "android.settings.APP_OPEN_BY_DEFAULT_SETTINGS" -ACTION_APP_LOCALE_SETTINGS = "android.settings.APP_LOCALE_SETTINGS" -ACTION_APPLICATION_SETTINGS_SYSTEM = "android.settings.APPLICATION_SETTINGS_SYSTEM" -ACTION_BATTERY_SAVER_SETTINGS = "android.settings.BATTERY_SAVER_SETTINGS" -ACTION_BIOMETRIC_ENROLL = "android.settings.BIOMETRIC_ENROLL" -ACTION_BLUETOOTH_SCAN_SETTINGS = "android.settings.BLUETOOTH_SCAN_SETTINGS" -ACTION_CAST_SETTINGS = "android.settings.CAST_SETTINGS" -ACTION_CHANNEL_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_NOTIFICATION_SETTINGS" -ACTION_CONDITION_PROVIDER_SETTINGS = "android.settings.CONDITION_PROVIDER_SETTINGS" -ACTION_DATA_USAGE_SETTINGS = "android.settings.DATA_USAGE_SETTINGS" -ACTION_DATE_SETTINGS = "android.settings.DATE_SETTINGS" -ACTION_DEVICE_INFO_SETTINGS = "android.settings.DEVICE_INFO_SETTINGS" -ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS" -ACTION_END_KEY_LONG_PRESS = "android.settings.END_KEY_LONG_PRESS" -ACTION_FINGERPRINT_ENROLL = "android.settings.FINGERPRINT_ENROLL" -ACTION_FINGERPRINT_SETTINGS = "android.settings.FINGERPRINT_SETTINGS" -ACTION_HARD_KEY_ASSIST = "android.settings.HARD_KEY_ASSIST" -ACTION_HOME_SETTINGS = "android.settings.HOME_SETTINGS" -ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS_SHORT = "android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS" -ACTION_INPUT_METHOD_SETTINGS = "android.settings.INPUT_METHOD_SETTINGS" -ACTION_INPUT_METHOD_SUBTYPE_SETTINGS = "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS" -ACTION_INTERNAL_STORAGE_SETTINGS_SHORT = "android.settings.INTERNAL_STORAGE_SETTINGS" -ACTION_LOCALE_SETTINGS = "android.settings.LOCALE_SETTINGS" -ACTION_MANAGE_APPLICATIONS_SETTINGS_SHORT = "android.settings.MANAGE_APPLICATIONS_SETTINGS" -ACTION_MANAGE_SUPERVISOR_RESTRICTION = "android.settings.MANAGE_SUPERVISOR_RESTRICTION" -ACTION_MOBILE_DATA_SETTINGS = "android.settings.MOBILE_DATA_SETTINGS" -ACTION_PRINT_SETTINGS = "android.settings.PRINT_SETTINGS" -ACTION_PROCESS_WIFI_CREDENTIAL = "android.settings.PROCESS_WIFI_CREDENTIAL" -ACTION_QUICK_ACCESS_WALLET_SETTINGS = "android.settings.QUICK_ACCESS_WALLET_SETTINGS" -ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS" -ACTION_SEARCH_SETTINGS = "android.settings.SEARCH_SETTINGS" -ACTION_STORAGE_VOLUME_ACCESS_SETTINGS = "android.settings.STORAGE_VOLUME_ACCESS_SETTINGS" -ACTION_SYSTEM_UPDATE_SETTINGS = "android.settings.SYSTEM_UPDATE_SETTINGS" -ACTION_TEST_SETTINGS = "android.settings.TEST_SETTINGS" -ACTION_TTS_SETTINGS = "android.settings.TTS_SETTINGS" -ACTION_USER_DICTIONARY_SETTINGS = "android.settings.USER_DICTIONARY_SETTINGS" -ACTION_VOICE_CONTROL_AIRPLANE_MODE = "android.settings.VOICE_CONTROL_AIRPLANE_MODE" -ACTION_VOICE_CONTROL_BATTERY_SAVER = "android.settings.VOICE_CONTROL_BATTERY_SAVER" -ACTION_VOICE_CONTROL_DO_NOT_DISTURB = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB" -ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS" -ACTION_WEBVIEW_SETTINGS = "android.settings.WEBVIEW_SETTINGS" -ACTION_WIFI_AP_SETTINGS = "android.settings.WIFI_AP_SETTINGS" -ACTION_WIFI_AP_SETTINGS_SHORT = "android.settings.WIFI_AP_SETTINGS" -ACTION_WIFI_SCAN_SETTINGS = "android.settings.WIFI_SCAN_SETTINGS" -ACTION_WIFI_SETUP = "android.settings.WIFI_SETUP" -ACTION_WIRELESS_SETTINGS_SHORT = "android.settings.WIRELESS_SETTINGS" -ACTION_ENTER_CAR_MODE = "android.intent.action.ENTER_CAR_MODE" -ACTION_EXIT_CAR_MODE = "android.intent.action.EXIT_CAR_MODE" -ACTION_CAR_MODE_SETTINGS = "android.settings.CAR_MODE_SETTINGS" -ACTION_CAR_DOCK_CONNECT = "android.intent.action.CAR_DOCK" -ACTION_DESK_DOCK_CONNECT = "android.intent.action.DESK_DOCK" -ACTION_LE_DESK_DOCK_CONNECT = "android.intent.action.LE_DESK_DOCK" -ACTION_HEADSET_PLUG_SHORT = "android.intent.action.HEADSET_PLUG" -ACTION_USB_ANLG_HEADSET_PLUG = "android.intent.action.USB_ANLG_HEADSET_PLUG" -ACTION_USB_DGTL_HEADSET_PLUG = "android.intent.action.USB_DGTL_HEADSET_PLUG" -ACTION_HTC_CONNECTOR_PLUG = "android.intent.action.HTC_CONNECTOR_PLUG" -ACTION_HTC_CONNECTOR_PLUG_SHORT = "android.intent.action.HTC_CONNECTOR_PLUG" -ACTION_NFC_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED" -ACTION_TAG_FOUND = "android.nfc.action.TAG_FOUND" -ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED" -ACTION_TAB_CHANGED = "android.intent.action.TAB_CHANGED" -ACTION_TETHER_STATE_CHANGED = "android.net.wifi.WIFI_AP_STATE_CHANGED" -ACTION_TRANSACTION_DETECTED = "android.nfc.action.TRANSACTION_DETECTED" -ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE" -ACTION_ACCOUNT_REMOVED = "android.accounts.action.ACCOUNT_REMOVED" -ACTION_ACCOUNT_VISIBLE = "android.accounts.action.ACCOUNT_VISIBLE" -ACTION_VOICEMAIL_SMS_RECEIVED = "android.intent.action.VOICEMAIL_SMS_RECEIVED" -ACTION_USER_ADDED = "android.intent.action.USER_ADDED" -ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED" -ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED" -ACTION_USER_STARTED = "android.intent.action.USER_STARTED" -ACTION_USER_STOPPED = "android.intent.action.USER_STOPPED" -ACTION_USER_BACKGROUND = "android.intent.action.USER_BACKGROUND" -ACTION_USER_FOREGROUND = "android.intent.action.USER_FOREGROUND" -ACTION_USER_INITIALIZE = "android.intent.action.USER_INITIALIZE" -ACTION_USER_UNLOCKED = "android.intent.action.USER_UNLOCKED" -ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED" -ACTION_SYSTEM_TUTORIAL = "android.intent.action.SYSTEM_TUTORIAL" -ACTION_APP_ERROR = "android.intent.action.APP_ERROR" -ACTION_BUG_REPORT = "android.intent.action.BUG_REPORT" -ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS" -ACTION_PREFERRED_ACTIVITY_CHANGED = "android.intent.action.PREFERRED_ACTIVITY_CHANGED" -ACTION_REBOOT_SHORT = "android.intent.action.REBOOT" -ACTION_SCREEN_ON_LATE = "android.intent.action.SCREEN_ON_LATE" -ACTION_SUBSCRIBER_FEATURES_CHANGED = "android.intent.action.SUBSCRIBER_FEATURES_CHANGED" -ACTION_SYSTEM_OVERLAY = "android.intent.action.SYSTEM_OVERLAY" -ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED" -ACTION_CONTENT_CHANGED = "android.intent.action.CONTENT_CHANGED" -ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE" -ACTION_DOWNLOAD_NOTIFICATION_CLICKED = "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED" -ACTION_NOTIFICATION_CLICK = "android.intent.action.NOTIFICATION_CLICK" -ACTION_DELETE_SHORT = "android.intent.action.DELETE" -ACTION_INSERT_SHORT = "android.intent.action.INSERT" -ACTION_REBOOT_2 = "android.intent.action.REBOOT" -ACTION_PICK_ACTIVITY_SHORT = "android.intent.action.PICK_ACTIVITY" -ACTION_GET_CONTENT_SHORT = "android.intent.action.GET_CONTENT" -ACTION_SEND_SHORT = "android.intent.action.SEND" -ACTION_ANSWER_SHORT = "android.intent.action.ANSWER" -ACTION_INSERT_OR_EDIT = "android.intent.action.INSERT_OR_EDIT" -ACTION_PASTE = "android.intent.action.PASTE" -ACTION_CLOSE = "android.intent.action.CLOSE" -ACTION_SAVE = "android.intent.action.SAVE" -ACTION_UNDO = "android.intent.action.UNDO" -ACTION_REDO = "android.intent.action.REDO" -ACTION_CUT = "android.intent.action.CUT" -ACTION_COPY = "android.intent.action.COPY" -ACTION_PRINT = "android.intent.action.PRINT" -ACTION_PLAY = "android.intent.action.PLAY" -ACTION_PAUSE = "android.intent.action.PAUSE" -ACTION_STOP = "android.intent.action.STOP" -ACTION_NEXT = "android.intent.action.NEXT" -ACTION_PREVIOUS = "android.intent.action.PREVIOUS" -ACTION_FAST_FORWARD = "android.intent.action.FAST_FORWARD" -ACTION_REWIND = "android.intent.action.REWIND" -ACTION_PLAY_PAUSE = "android.intent.action.PLAY_PAUSE" -ACTION_MUTE = "android.intent.action.MUTE" -ACTION_VOLUME_UP = "android.intent.action.VOLUME_UP" -ACTION_VOLUME_DOWN = "android.intent.action.VOLUME_DOWN" -ACTION_INFO = "android.intent.action.INFO" -ACTION_REPLY = "android.intent.action.REPLY" -ACTION_FORWARD = "android.intent.action.FORWARD" -ACTION_COMPOSE = "android.intent.action.COMPOSE" -ACTION_REFRESH = "android.intent.action.REFRESH" -ACTION_SEARCH_LONG_PRESS = "android.intent.action.SEARCH_LONG_PRESS" -ACTION_MUSIC_PLAYER = "android.intent.action.MUSIC_PLAYER" -ACTION_ALARM_CHANGED = "android.intent.action.ALARM_CHANGED" -ACTION_NEXT_ALARM_SET = "android.intent.action.NEXT_ALARM_SET" - -ACTION_ALL_APPS = "android.intent.action.ALL_APPS" -ACTION_BOOKMARK = "android.intent.action.BOOKMARK" -ACTION_CHROME_ICON = "android.intent.action.CHROME_ICON" -ACTION_ERROR = "android.intent.action.ERROR" -ACTION_GADGET = "android.intent.action.GADGET" -ACTION_GOOGLE = "android.intent.action.GOOGLE" -ACTION_HOTWORD = "android.intent.action.HOTWORD" -ACTION_ICON = "android.intent.action.ICON" -ACTION_LAUNCH = "android.intent.action.LAUNCH" -ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE" -ACTION_MEDIA_PLAY_FROM_SEARCH = "android.intent.action.MEDIA_PLAY_FROM_SEARCH" -ACTION_MEDIA_PAUSED = "android.intent.action.MEDIA_PAUSED" -ACTION_MEDIA_PLAYING = "android.intent.action.MEDIA_PLAYING" -ACTION_MEDIA_UNPAUSED = "android.intent.action.MEDIA_UNPAUSED" -ACTION_NAVIGATE = "android.intent.action.NAVIGATE" -ACTION_NEW_APPLICATION = "android.intent.action.NEW_APPLICATION" -ACTION_NO_DOWNLOAD = "android.intent.action.NO_DOWNLOAD" -ACTION_NO_INSTALL = "android.intent.action.NO_INSTALL" -ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL" -ACTION_RADIO = "android.intent.action.RADIO" -ACTION_RECORD = "android.intent.action.RECORD" -ACTION_REMEMBER = "android.intent.action.REMEMBER" -ACTION_RINGER_MODE_CHANGED = "android.media.RINGER_MODE_CHANGED" -ACTION_SEND_REPORT = "android.intent.action.SEND_REPORT" -ACTION_SET_WALLPAPER = "android.intent.action.SET_WALLPAPER" -ACTION_SHOW_ALARM = "android.intent.action.SHOW_ALARM" -ACTION_SHORTCUT = "android.intent.action.SHORTCUT" -ACTION_START = "android.intent.action.START" -ACTION_STATISTICS = "android.intent.action.STATISTICS" -ACTION_STOP_APPLICATION = "android.intent.action.STOP_APPLICATION" -ACTION_STOP_SERVICE = "android.intent.action.STOP_SERVICE" -ACTION_SYSTEM_DEVICE = "android.intent.action.SYSTEM_DEVICE" -ACTION_SYSTEM_UPDATE = "android.intent.action.SYSTEM_UPDATE" -ACTION_THEME = "android.intent.action.THEME" -ACTION_UNINSTALL = "android.intent.action.UNINSTALL" -ACTION_UNUSED = "android.intent.action.UNUSED" -ACTION_UPDATE = "android.intent.action.UPDATE" -ACTION_UPGRADE = "android.intent.action.UPGRADE" -ACTION_VERIFY = "android.intent.action.VERIFY" -ACTION_VIEW_APK = "android.intent.action.VIEW_APK" -ACTION_VIEW_CONTACTS = "android.intent.action.VIEW_CONTACTS" -ACTION_VIEW_URL = "android.intent.action.VIEW_URL" -ACTION_VOICE_RECOGNIZE = "android.intent.action.VOICE_RECOGNIZE" -ACTION_WEB_SEARCH_SHORT = "android.intent.action.WEB_SEARCH" - - -# ═══════════════════════════════════════════════════════════════ -# CATEGORY_* — 标准 Intent Category 常量 -# ═══════════════════════════════════════════════════════════════ - -CATEGORY_DEFAULT = "android.intent.category.DEFAULT" -CATEGORY_BROWSABLE = "android.intent.category.BROWSABLE" -CATEGORY_TAB = "android.intent.category.TAB" -CATEGORY_ALTERNATIVE = "android.intent.category.ALTERNATIVE" -CATEGORY_SELECTED_ALTERNATIVE = "android.intent.category.SELECTED_ALTERNATIVE" -CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER" -CATEGORY_INFO = "android.intent.category.INFO" -CATEGORY_HOME = "android.intent.category.HOME" -CATEGORY_PREFERENCE = "android.intent.category.PREFERENCE" -CATEGORY_TEST = "android.intent.category.TEST" -CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK" -CATEGORY_DESK_DOCK = "android.intent.category.DESK_DOCK" -CATEGORY_LE_DESK_DOCK = "android.intent.category.LE_DESK_DOCK" -CATEGORY_HE_DESK_DOCK = "android.intent.category.HE_DESK_DOCK" -CATEGORY_CAR_MODE = "android.intent.category.CAR_MODE" -CATEGORY_APP_MARKET = "android.intent.category.APP_MARKET" -CATEGORY_APP_BROWSER = "android.intent.category.APP_BROWSER" -CATEGORY_APP_CALCULATOR = "android.intent.category.APP_CALCULATOR" -CATEGORY_APP_CALENDAR = "android.intent.category.APP_CALENDAR" -CATEGORY_APP_CONTACTS = "android.intent.category.APP_CONTACTS" -CATEGORY_APP_EMAIL = "android.intent.category.APP_EMAIL" -CATEGORY_APP_GALLERY = "android.intent.category.APP_GALLERY" -CATEGORY_APP_MAPS = "android.intent.category.APP_MAPS" -CATEGORY_APP_MESSAGING = "android.intent.category.APP_MESSAGING" -CATEGORY_APP_MUSIC = "android.intent.category.APP_MUSIC" -CATEGORY_APP_FILES = "android.intent.category.APP_FILES" -CATEGORY_APP_CLOCK = "android.intent.category.APP_CLOCK" -CATEGORY_APP_SETTINGS = "android.intent.category.APP_SETTINGS" -CATEGORY_APP_WEATHER = "android.intent.category.APP_WEATHER" -CATEGORY_APP_VIDEO = "android.intent.category.APP_VIDEO" -CATEGORY_APP_CHAT = "android.intent.category.APP_CHAT" -CATEGORY_APP_GAME = "android.intent.category.APP_GAME" -CATEGORY_APP_FINANCE = "android.intent.category.APP_FINANCE" -CATEGORY_APP_NEWS = "android.intent.category.APP_NEWS" -CATEGORY_APP_SOCIAL = "android.intent.category.APP_SOCIAL" -CATEGORY_APP_PHOTOGRAPHY = "android.intent.category.APP_PHOTOGRAPHY" -CATEGORY_APP_PRODUCTIVITY = "android.intent.category.APP_PRODUCTIVITY" -CATEGORY_APP_TRAVEL = "android.intent.category.APP_TRAVEL" -CATEGORY_APP_WALLPAPER = "android.intent.category.APP_WALLPAPER" -CATEGORY_APP_RECENTS = "android.intent.category.APP_RECENTS" -CATEGORY_OPENABLE = "android.intent.category.OPENABLE" -CATEGORY_TYPED_OPENABLE = "android.intent.category.TYPED_OPENABLE" -CATEGORY_MONKEY = "android.intent.category.MONKEY" -CATEGORY_DEVELOPMENT_PREFERENCE = "android.intent.category.DEVELOPMENT_PREFERENCE" -CATEGORY_EMBED = "android.intent.category.EMBED" -CATEGORY_FRAMEWORK_INSTRUMENTATION_TEST = "android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" -CATEGORY_MASTER_CLEAR = "android.intent.category.MASTER_CLEAR" -CATEGORY_UNIT_TEST = "android.intent.category.UNIT_TEST" -CATEGORY_SAMPLE_CODE = "android.intent.category.SAMPLE_CODE" -CATEGORY_ACCESSIBILITY = "android.intent.category.ACCESSIBILITY" -CATEGORY_VOICE = "android.intent.category.VOICE" -CATEGORY_NOTIFICATION_PREFERENCES = "android.intent.category.NOTIFICATION_PREFERENCES" -CATEGORY_SEARCH_RESULT = "android.intent.category.SEARCH_RESULT" -CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER" -CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS" -CATEGORY_LEANBACK_VIDEO = "android.intent.category.LEANBACK_VIDEO" -CATEGORY_LEANBACK_DOCUMENT = "android.intent.category.LEANBACK_DOCUMENT" -CATEGORY_LEANBACK_HOME = "android.intent.category.LEANBACK_HOME" -CATEGORY_LEANBACK_SEARCH = "android.intent.category.LEANBACK_SEARCH" -CATEGORY_SECONDARY_HOME = "android.intent.category.SECONDARY_HOME" -CATEGORY_PREFERENCE_SHORT = "android.intent.category.PREFERENCE" -CATEGORY_TEST_SHORT = "android.intent.category.TEST" -CATEGORY_DESK_DOCK_SHORT = "android.intent.category.DESK_DOCK" -CATEGORY_CAR_DOCK_SHORT = "android.intent.category.CAR_DOCK" -CATEGORY_LE_DESK_DOCK_SHORT = "android.intent.category.LE_DESK_DOCK" - - -# ═══════════════════════════════════════════════════════════════ -# EXTRA_* — 标准 Intent Extra 常量 -# ═══════════════════════════════════════════════════════════════ - -EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT" -EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT" -EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED" -EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE" -EXTRA_DOCK_STATE_HE_DESK = 4 -EXTRA_DOCK_STATE_LE_DESK = 3 -EXTRA_DOCK_STATE_CAR = 2 -EXTRA_DOCK_STATE_DESK = 1 -EXTRA_DOCK_STATE_UNDOCKED = 0 -EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP" -EXTRA_EMAIL = "android.intent.extra.EMAIL" -EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS" -EXTRA_INTENT = "android.intent.extra.INTENT" -EXTRA_KEY_EVENT = "android.intent.extra.KEY_EVENT" -EXTRA_LOCAL_ONLY = "android.intent.extra.LOCAL_ONLY" -EXTRA_MIME_TYPES = "android.intent.extra.MIME_TYPES" -EXTRA_NOT_INITIALIZED = "android.intent.extra.NOT_INITIALIZED" -EXTRA_PACKAGES = "android.intent.extra.PACKAGES" -EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER" -EXTRA_REFERRER = "android.intent.extra.REFERRER" -EXTRA_REFERRER_NAME = "android.intent.extra.REFERRER_NAME" -EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token" -EXTRA_REPLACING = "android.intent.extra.REPLACING" -EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON" -EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE" -EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT" -EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME" -EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY" -EXTRA_STREAM = "android.intent.extra.STREAM" -EXTRA_SUBJECT = "android.intent.extra.SUBJECT" -EXTRA_TEMPLATE = "android.intent.extra.TEMPLATE" -EXTRA_TEXT = "android.intent.extra.TEXT" -EXTRA_TITLE = "android.intent.extra.TITLE" -EXTRA_UID = "android.intent.extra.UID" -EXTRA_USER = "android.intent.extra.USER" -EXTRA_USER_ID = "android.intent.extra.USER_ID" -EXTRA_USER_HANDLE = "android.intent.extra.USER_HANDLE" -EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE" -EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT" -EXTRA_CC = "android.intent.extra.CC" -EXTRA_BCC = "android.intent.extra.BCC" -EXTRA_FROM_STORAGE = "android.intent.extra.FROM_STORAGE" -EXTRA_INSTALLER_PACKAGE_NAME = "android.intent.extra.INSTALLER_PACKAGE_NAME" -EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID" -EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG" -EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID" -EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID" -EXTRA_CONVERSATION_ID = "android.intent.extra.CONVERSATION_ID" -EXTRA_IS_GROUP_CONVERSATION = "android.intent.extra.IS_GROUP_CONVERSATION" -EXTRA_MESSAGES = "android.intent.extra.MESSAGES" -EXTRA_PEOPLE = "android.intent.extra.PEOPLE" -EXTRA_SELF_DISPLAY_NAME = "android.intent.extra.SELF_DISPLAY_NAME" -EXTRA_TIME = "android.intent.extra.TIME" -EXTRA_SIZE = "android.intent.extra.SIZE" -EXTRA_COUNTRY = "android.intent.extra.COUNTRY" -EXTRA_LOCATION = "android.intent.extra.LOCATION" -EXTRA_LATITUDE = "android.intent.extra.LATITUDE" -EXTRA_LONGITUDE = "android.intent.extra.LONGITUDE" -EXTRA_ALTITUDE = "android.intent.extra.ALTITUDE" -EXTRA_BEARING = "android.intent.extra.BEARING" -EXTRA_SPEED = "android.intent.extra.SPEED" -EXTRA_ACCURACY = "android.intent.extra.ACCURACY" -EXTRA_POI_NAME = "android.intent.extra.POI_NAME" -EXTRA_PHONE_NUMBER_SHORT = "android.intent.extra.PHONE_NUMBER" -EXTRA_RINGER_MODE = "android.intent.extra.RINGER_MODE" -EXTRA_VIBRATE_SETTING = "android.intent.extra.VIBRATE_SETTING" -EXTRA_VOLUME_STREAM_TYPE = "android.intent.extra.VOLUME_STREAM_TYPE" -EXTRA_VOLUME_STREAM_VALUE = "android.intent.extra.VOLUME_STREAM_VALUE" -EXTRA_VOLUME_STREAM_VOLUME = "android.intent.extra.VOLUME_STREAM_VOLUME" -EXTRA_FORCE_MASTER_VOLUME_MUTE = "android.intent.extra.FORCE_MASTER_VOLUME_MUTE" -EXTRA_PREVIOUS_VOLUME_STREAM_VALUE = "android.intent.extra.PREVIOUS_VOLUME_STREAM_VALUE" -EXTRA_VIBRATE_TYPE = "android.intent.extra.VIBRATE_TYPE" -EXTRA_SCREEN_OFF_TIMEOUT = "android.intent.extra.SCREEN_OFF_TIMEOUT" -EXTRA_TASK_ID = "android.intent.extra.TASK_ID" -EXTRA_WINDOW_MINIMUM_SIZE_MAJOR = "android.intent.extra.WINDOW_MINIMUM_SIZE_MAJOR" -EXTRA_WINDOW_MINIMUM_SIZE_MINOR = "android.intent.extra.WINDOW_MINIMUM_SIZE_MINOR" -EXTRA_CHANGED_COMPONENT_NAME = "android.intent.extra.changed_component_name" -EXTRA_CHANGED_COMPONENT_NAME_LIST = "android.intent.extra.changed_component_name_list" -EXTRA_CHANGED_UID = "android.intent.extra.changed_uid" -EXTRA_DATA_REMOVED_SHORT = "android.intent.extra.DATA_REMOVED" -EXTRA_DOCK_STATE_SHORT = "android.intent.extra.DOCK_STATE" -EXTRA_REPLACING_SHORT = "android.intent.extra.REPLACING" -EXTRA_SERVICE_INFO = "android.intent.extra.SERVICE_INFO" -EXTRA_SERVICE_COMPONENT = "android.intent.extra.SERVICE_COMPONENT" -EXTRA_REQUEST_QUALITY = "android.intent.extra.REQUEST_QUALITY" -EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI" -EXTRA_REFERRER_SHORT = "android.intent.extra.REFERRER" -EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID" -EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE" -EXTRA_CALLING_UID = "android.intent.extra.CALLING_UID" -EXTRA_CALLING_ACTIVITY = "android.intent.extra.CALLING_ACTIVITY" -EXTRA_LAUNCHER_INTENT = "android.intent.extra.LAUNCHER_INTENT" -EXTRA_WALLPAPER_OFFSET = "android.intent.extra.WALLPAPER_OFFSET" -EXTRA_WALLPAPER_ID = "android.intent.extra.WALLPAPER_ID" -EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS" -EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER" -EXTRA_RESULT_RECEIVER = "android.intent.extra.RESULT_RECEIVER" -EXTRA_ALARM_GROUP_ID = "android.intent.extra.ALARM_GROUP_ID" -EXTRA_DELAY = "android.intent.extra.DELAY" -EXTRA_HEADER = "android.intent.extra.HEADER" -EXTRA_PROTOCOL = "android.intent.extra.PROTOCOL" -EXTRA_FETCH_MODE = "android.intent.extra.FETCH_MODE" -EXTRA_ID = "android.intent.extra.ID" -EXTRA_NAME = "android.intent.extra.NAME" -EXTRA_QUERY = "android.intent.extra.QUERY" -EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION" -EXTRA_ICON = "android.intent.extra.ICON" -EXTRA_COLOR = "android.intent.extra.COLOR" -EXTRA_ORIENTATION = "android.intent.extra.ORIENTATION" -EXTRA_PRIORITY = "android.intent.extra.PRIORITY" -EXTRA_STATE = "android.intent.extra.STATE" -EXTRA_STATUS = "android.intent.extra.STATUS" -EXTRA_TAG = "android.intent.extra.TAG" -EXTRA_TARGET = "android.intent.extra.TARGET" -EXTRA_TOKEN = "android.intent.extra.TOKEN" -EXTRA_VERSION = "android.intent.extra.VERSION" -EXTRA_URL = "android.intent.extra.URL" -EXTRA_REQUEST_ID = "android.intent.extra.REQUEST_ID" -EXTRA_RESPONSE_CODE = "android.intent.extra.RESPONSE_CODE" -EXTRA_RESPONSE_MESSAGE = "android.intent.extra.RESPONSE_MESSAGE" -EXTRA_AUTHENTICATOR_RESPONSE = "android.accounts.extra.AUTHENTICATOR_RESPONSE" -EXTRA_AUTHENTICATOR_ACCOUNT = "android.accounts.extra.AUTHENTICATOR_ACCOUNT" -EXTRA_AUTHENTICATOR_INTENT = "android.accounts.extra.AUTHENTICATOR_INTENT" -EXTRA_ACCOUNT_TYPE = "android.accounts.extra.ACCOUNT_TYPE" -EXTRA_ACCOUNT_LABEL = "android.accounts.extra.ACCOUNT_LABEL" -EXTRA_ACCOUNT_AUTH_TOKEN_LABEL = "android.accounts.extra.AUTH_TOKEN_LABEL" -EXTRA_PASSWORD = "android.accounts.extra.PASSWORD" -EXTRA_AUTH_TOKEN_TYPE = "android.accounts.extra.AUTH_TOKEN_TYPE" -EXTRA_CONFIRM_CREDENTIALS = "android.accounts.extra.CONFIRM_CREDENTIALS" -EXTRA_UPDATE_CURRENT_ACCOUNT = "android.accounts.extra.UPDATE_CURRENT_ACCOUNT" -EXTRA_ALLOW_ACCOUNT_REAUTH = "android.accounts.extra.ALLOW_ACCOUNT_REAUTH" -EXTRA_UNKNOWN_USER = "android.accounts.extra.UNKNOWN_USER" - - -# ═══════════════════════════════════════════════════════════════ -# FLAG_* — Intent Flag 常量(Android SDK 实际数值) -# ═══════════════════════════════════════════════════════════════ - -FLAG_GRANT_READ_URI_PERMISSION = 0x00000001 -FLAG_GRANT_WRITE_URI_PERMISSION = 0x00000002 -FLAG_FROM_BACKGROUND = 0x00000004 -FLAG_DEBUG_LOG_RESOLUTION = 0x00000008 -FLAG_EXCLUDE_STOPPED_PACKAGES = 0x00000010 -FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020 -FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 0x00000040 -FLAG_GRANT_PREFIX_URI_PERMISSION = 0x00000080 -FLAG_ACTIVITY_MATCH_EXTERNAL = 0x00001000 -FLAG_ACTIVITY_NO_HISTORY = 0x40000000 -FLAG_ACTIVITY_SINGLE_TOP = 0x20000000 -FLAG_ACTIVITY_NEW_TASK = 0x10000000 -FLAG_ACTIVITY_MULTIPLE_TASK = 0x08000000 -FLAG_ACTIVITY_CLEAR_TOP = 0x04000000 -FLAG_ACTIVITY_FORWARD_RESULT = 0x02000000 -FLAG_ACTIVITY_PREVIOUS_IS_TOP = 0x01000000 -FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 0x00800000 -FLAG_ACTIVITY_BROUGHT_TO_FRONT = 0x00400000 -FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 0x00200000 -FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 0x00100000 -FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 0x00080000 -FLAG_ACTIVITY_NEW_DOCUMENT = 0x00080000 -FLAG_ACTIVITY_CLEAR_TASK = 0x00008000 -FLAG_ACTIVITY_TASK_ON_HOME = 0x00004000 -FLAG_ACTIVITY_RETAIN_IN_RECENTS = 0x00002000 -FLAG_ACTIVITY_LAUNCH_ADJACENT = 0x00001000 -FLAG_ACTIVITY_REORDER_TO_FRONT = 0x00020000 -FLAG_ACTIVITY_NO_USER_ACTION = 0x00010000 -FLAG_ACTIVITY_NO_ANIMATION = 0x00010000 -FLAG_RECEIVER_REGISTERED_ONLY = 0x40000000 -FLAG_RECEIVER_REPLACE_PENDING = 0x20000000 -FLAG_RECEIVER_FOREGROUND = 0x10000000 -FLAG_RECEIVER_NO_ABORT = 0x08000000 -FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x04000000 -FLAG_RECEIVER_BOOT_UPGRADE = 0x02000000 -FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000 -FLAG_RECEIVER_EXCLUDE_BACKGROUND = 0x00800000 -FLAG_RECEIVER_FROM_SHELL = 0x00400000 -FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS = 0x00200000 -FLAG_RECEIVER_OFFLOAD = 0x00100000 -FLAG_SYSTEM_BROADCAST = 0x00000040 - - -# ═══════════════════════════════════════════════════════════════ -# URI_*, SCHEME_*, MIME_* 常量 -# ═══════════════════════════════════════════════════════════════ - -URI_INTENT_SCHEME = "intent" -SCHEME_FILE = "file" -SCHEME_CONTENT = "content" -SCHEME_ANDROID_RESOURCE = "android.resource" -SCHEME_PACKAGE = "package" -SCHEME_HTTP = "http" -SCHEME_HTTPS = "https" -SCHEME_TEL = "tel" -SCHEME_SMSTO = "smsto" -SCHEME_MAILTO = "mailto" -SCHEME_GEO = "geo" -SCHEME_MARKET = "market" -SCHEME_ABOUT = "about" - -MIME_TYPE_TEXT_PLAIN = "text/plain" -MIME_TYPE_TEXT_HTML = "text/html" -MIME_TYPE_TEXT_CALENDAR = "text/calendar" -MIME_TYPE_TEXT_XML = "text/xml" -MIME_TYPE_IMAGE_JPEG = "image/jpeg" -MIME_TYPE_IMAGE_PNG = "image/png" -MIME_TYPE_IMAGE_GIF = "image/gif" -MIME_TYPE_IMAGE_WEBP = "image/webp" -MIME_TYPE_IMAGE_ALL = "image/*" -MIME_TYPE_AUDIO_MPEG = "audio/mpeg" -MIME_TYPE_AUDIO_MP4 = "audio/mp4" -MIME_TYPE_AUDIO_ALL = "audio/*" -MIME_TYPE_VIDEO_MP4 = "video/mp4" -MIME_TYPE_VIDEO_ALL = "video/*" -MIME_TYPE_APPLICATION_PDF = "application/pdf" -MIME_TYPE_APPLICATION_ZIP = "application/zip" -MIME_TYPE_APPLICATION_JSON = "application/json" -MIME_TYPE_APPLICATION_XML = "application/xml" -MIME_TYPE_APPLICATION_OCTET_STREAM = "application/octet-stream" -MIME_TYPE_MULTIPART_MIXED = "multipart/mixed" -MIME_TYPE_MULTIPART_FORM_DATA = "multipart/form-data" -MIME_TYPE_ALL = "*/*" - - -# ═══════════════════════════════════════════════════════════════ -# Intent — 完整扩展 -# ═══════════════════════════════════════════════════════════════ - -class Intent(_BaseIntent): - """Android Intent — 完整扩展 (桌面存根,继承 app.Intent 的所有方法)""" - - # ── ShortcutIconResource inner class ── - - class ShortcutIconResource(Stub): - """ShortcutIconResource — 快捷方式图标资源""" - - def __init__(self, packageName: str = "", resourceName: str = ""): - super().__init__() - self.packageName = packageName - self.resourceName = resourceName - - @staticmethod - def fromContext(context: Any, iconId: int) -> "Intent.ShortcutIconResource": - return Intent.ShortcutIconResource( - packageName=getattr(context, "getPackageName", lambda: "unknown")(), - resourceName=str(iconId), - ) - - def toString(self) -> str: - return f"ShortcutIconResource(packageName={self.packageName}, resourceName={self.resourceName})" - - # ── FilterComparison inner class ── - - class FilterComparison(Stub): - """FilterComparison — Intent 过滤器比较""" - - def __init__(self, intent: "Intent"): - super().__init__() - self._intent = intent - - def getIntent(self) -> "Intent": - return self._intent - - def equals(self, other: Any) -> bool: - if not isinstance(other, Intent.FilterComparison): - return False - return self._intent.filterEquals(other._intent) - - def hashCode(self) -> int: - return self._intent.filterHashCode() - - def toString(self) -> str: - return f"FilterComparison({self._intent})" - - # ── Intent 主构造 ── - - def __init__(self, action: str = "", uri: Optional[str] = None, - package: str = "", component: Optional[Any] = None): - super().__init__() - self._action = action - self._data: Optional[str] = uri - self._type: Optional[str] = None - self._package = package - self._component = component - self._flags: int = 0 - self._categories: Set[str] = set() - self._extras: Dict[str, Any] = {} - self._scheme: str = "" - self._authority: str = "" - self._path: str = "" - self._clip_data: Optional[Any] = None - self._identifier: Optional[str] = None - self._selector: Optional["Intent"] = None - self._source_bounds: Optional[Tuple[int, int, int, int]] = None - - # ── Action ── - - def getAction(self) -> str: - return self._action - - def setAction(self, action: str) -> "Intent": - self._action = action - return self - - # ── Data / URI ── - - def getData(self) -> Optional[str]: - return self._data - - def setData(self, data) -> "Intent": - self._data = data - self._type = None - # 从 Uri 提取 scheme - if data is not None: - if hasattr(data, 'getScheme'): - self._scheme = data.getScheme() or "" - elif isinstance(data, str): - from urllib.parse import urlparse - self._scheme = urlparse(data).scheme - return self - - def getDataString(self) -> Optional[str]: - if self._data is None: - return None - if isinstance(self._data, str): - return self._data - return str(self._data) - - def getScheme(self) -> str: - if self._scheme: - return self._scheme - # 从 data 动态提取 - if self._data is not None: - if hasattr(self._data, 'getScheme'): - return self._data.getScheme() or "" - elif isinstance(self._data, str): - from urllib.parse import urlparse - return urlparse(self._data).scheme - return "" - - # ── Type ── - - def getType(self) -> Optional[str]: - return self._type - - def setType(self, type_: str) -> "Intent": - self._type = type_ - self._data = None - return self - - def setDataAndType(self, data: str, type_: str) -> "Intent": - self._data = data - self._type = type_ - return self - - def resolveType(self, resolver: Any = None) -> Optional[str]: - return self._type - - # ── Scheme ── - - def getSchemeSpecificPart(self) -> str: - if self._data and ":" in self._data: - return self._data.split(":", 1)[1] - return "" - - # ── Package ── - - def getPackage(self) -> str: - return self._package - - def setPackage(self, package: str) -> "Intent": - self._package = package or "" - return self - - # ── Component ── - - def getComponent(self) -> Optional[Any]: - return self._component - - def setComponent(self, component: Any) -> "Intent": - self._component = component - return self - - def getClassName(self) -> str: - if self._component: - return getattr(self._component, "getClassName", lambda: str(self._component))() - return "" - - # ── Flags ── - - def getFlags(self) -> int: - return self._flags - - def setFlags(self, flags: int) -> "Intent": - self._flags = flags - return self - - def addFlags(self, flags: int) -> "Intent": - self._flags |= flags - return self - - def removeFlags(self, flags: int) -> None: - self._flags &= ~flags - - # ── Categories ── - - def hasCategory(self, category: str) -> bool: - return category in self._categories - - def getCategories(self) -> Set[str]: - return self._categories.copy() - - def addCategory(self, category: str) -> "Intent": - self._categories.add(category) - return self - - def removeCategory(self, category: str) -> None: - self._categories.discard(category) - - # ── Extras ── - - def getExtras(self) -> Dict[str, Any]: - return dict(self._extras) - - def hasExtra(self, name: str) -> bool: - return name in self._extras - - def getStringExtra(self, name: str) -> Optional[str]: - val = self._extras.get(name) - return str(val) if val is not None else None - - def getIntExtra(self, name: str, default: int = 0) -> int: - val = self._extras.get(name) - return int(val) if val is not None else default - - def getLongExtra(self, name: str, default: int = 0) -> int: - val = self._extras.get(name) - return int(val) if val is not None else default - - def getFloatExtra(self, name: str, default: float = 0.0) -> float: - val = self._extras.get(name) - return float(val) if val is not None else default - - def getDoubleExtra(self, name: str, default: float = 0.0) -> float: - val = self._extras.get(name) - return float(val) if val is not None else default - - def getBooleanExtra(self, name: str, default: bool = False) -> bool: - val = self._extras.get(name) - return bool(val) if val is not None else default - - def getCharExtra(self, name: str, default: str = "") -> str: - val = self._extras.get(name) - return str(val) if val is not None else default - - def getShortExtra(self, name: str, default: int = 0) -> int: - val = self._extras.get(name) - return int(val) if val is not None else default - - def getByteExtra(self, name: str, default: int = 0) -> int: - val = self._extras.get(name) - return int(val) if val is not None else default - - def getSerializableExtra(self, name: str) -> Optional[Any]: - return self._extras.get(name) - - def getIntegerArrayListExtra(self, name: str) -> Optional[List[int]]: - val = self._extras.get(name) - if isinstance(val, list) and all(isinstance(v, int) for v in val): - return val - return None - - def getStringArrayListExtra(self, name: str) -> Optional[List[str]]: - val = self._extras.get(name) - if isinstance(val, list) and all(isinstance(v, str) for v in val): - return val - return None - - def getCharSequenceArrayListExtra(self, name: str) -> Optional[List[str]]: - val = self._extras.get(name) - if isinstance(val, list): - return [str(v) for v in val] - return None - - def getBundleExtra(self, name: str) -> Optional[Dict[str, Any]]: - val = self._extras.get(name) - return val if isinstance(val, dict) else None - - def putExtra(self, name: str, value: Any) -> "Intent": - self._extras[name] = value - return self - - def putExtras(self, extras: Dict[str, Any]) -> "Intent": - self._extras.update(extras) - return self - - def removeExtra(self, name: str) -> None: - self._extras.pop(name, None) - - def replaceExtras(self, extras: Dict[str, Any]) -> "Intent": - self._extras = dict(extras) - return self - - def putIntegerArrayListExtra(self, name: str, values: List[int]) -> "Intent": - self._extras[name] = list(values) - return self - - def putStringArrayListExtra(self, name: str, values: List[str]) -> "Intent": - self._extras[name] = list(values) - return self - - def putCharSequenceArrayListExtra(self, name: str, values: List[str]) -> "Intent": - self._extras[name] = [str(v) for v in values] - return self - - # ── Selector ── - - def getSelector(self) -> Optional["Intent"]: - return self._selector - - def setSelector(self, selector: "Intent") -> None: - self._selector = selector - - # ── ClipData ── - - def getClipData(self) -> Optional[Any]: - return self._clip_data - - def setClipData(self, clip: Optional[Any]) -> None: - self._clip_data = clip - - # ── Identifier ── - - def getIdentifier(self) -> Optional[str]: - return self._identifier - - def setIdentifier(self, identifier: str) -> None: - self._identifier = identifier - - # ── SourceBounds ── - - def getSourceBounds(self) -> Optional[Tuple[int, int, int, int]]: - return self._source_bounds - - def setSourceBounds(self, bounds: Optional[Tuple[int, int, int, int]]) -> None: - self._source_bounds = bounds - - # ── FillIn ── - - def fillIn(self, other: "Intent", flags: int = 0) -> int: - """将 other 的内容合并到当前 Intent""" - changes = 0 - if not self._action and other._action: - self._action = other._action - changes |= 1 - if not self._data and other._data: - self._data = other._data - changes |= 2 - if not self._type and other._type: - self._type = other._type - changes |= 4 - if not self._package and other._package: - self._package = other._package - changes |= 8 - if not self._component and other._component: - self._component = other._component - changes |= 16 - if not self._scheme and other._scheme: - self._scheme = other._scheme - changes |= 32 - for cat in other._categories: - if cat not in self._categories: - self._categories.add(cat) - changes |= 64 - self._flags |= other._flags - for k, v in other._extras.items(): - if k not in self._extras: - self._extras[k] = v - changes |= 128 - return changes - - # ── FilterEquals (Intent resolution matching) ── - - def filterEquals(self, other: Any) -> bool: - """判断两个 Intent 在 filter 匹配意义上是否相等""" - if not isinstance(other, Intent): - return False - return (self._action == other._action - and self._data == other._data - and self._type == other._type - and self._package == other._package - and self._component == other._component - and self._categories == other._categories) - - def filterHashCode(self) -> int: - """计算 filter 哈希码""" - h = hash(self._action) if self._action else 0 - h = h * 31 + (hash(self._data) if self._data else 0) - h = h * 31 + (hash(self._type) if self._type else 0) - h = h * 31 + (hash(self._package) if self._package else 0) - h = h * 31 + (hash(str(self._component)) if self._component else 0) - for cat in sorted(self._categories): - h = h * 31 + hash(cat) - return h & 0x7FFFFFFF - - # ── DescribeContents / WriteToParcel ── - - def describeContents(self) -> int: - return 0 - - def writeToParcel(self, dest: Any, flags: int = 0) -> None: - """写入 Parcel (桌面存根)""" - pass - - def readFromParcel(self, source: Any) -> None: - """从 Parcel 读取 (桌面存根)""" - pass - - # ── Create chooser ── - - @staticmethod - def createChooser(target: "Intent", title: Optional[str] = None, - sender: Optional[Any] = None) -> "Intent": - intent = Intent(ACTION_CHOOSER) - intent.putExtra(EXTRA_INTENT, target) - if title: - intent.putExtra(EXTRA_TITLE, title) - if sender: - intent.putExtra(EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER, sender) - return intent - - @staticmethod - def getIntentOld(action: str) -> "Intent": - return Intent(action) - - @staticmethod - def getIntent(uri: str) -> "Intent": - return Intent(uri=uri) - - @staticmethod - def parseUri(uri: str, flags: int = 0) -> "Intent": - return Intent(uri=uri) - - def toURI(self) -> str: - return f"{self._scheme}:{self._data or ''}#Intent;action={self._action};end" - - def toUri(self, flags: int = 0) -> str: - return self.toURI() - - def toString(self) -> str: - parts = [f"Intent {{ act={self._action}"] - if self._data: - parts.append(f" dat={self._data}") - if self._type: - parts.append(f" typ={self._type}") - if self._package: - parts.append(f" pkg={self._package}") - if self._component: - parts.append(f" cmp={self._component}") - if self._flags: - parts.append(f" flg=0x{self._flags:08x}") - if self._categories: - parts.append(f" cat={self._categories}") - parts.append(" }") - return "".join(parts) - - def equals(self, other: Any) -> bool: - if not isinstance(other, Intent): - return False - return (self._action == other._action - and self._data == other._data - and self._type == other._type - and self._package == other._package) - - def hashCode(self) -> int: - h = hash(self._action) if self._action else 0 - h = h * 31 + (hash(self._data) if self._data else 0) - h = h * 31 + (hash(self._type) if self._type else 0) - h = h * 31 + (hash(self._package) if self._package else 0) - return h & 0x7FFFFFFF - - def clone(self) -> "Intent": - cloned = Intent(self._action, self._data, self._package, self._component) - cloned._type = self._type - cloned._flags = self._flags - cloned._categories = set(self._categories) - cloned._extras = dict(self._extras) - cloned._scheme = self._scheme - cloned._selector = self._selector - cloned._clip_data = self._clip_data - cloned._identifier = self._identifier - cloned._source_bounds = self._source_bounds - return cloned - - def toInsecureString(self) -> str: - return self.toString() - - def toSecureString(self) -> str: - return self.toString() - - def isDocument(self) -> bool: - return bool(self._flags & FLAG_ACTIVITY_NEW_DOCUMENT) - - def isExcludingStopped(self) -> bool: - return bool(self._flags & FLAG_EXCLUDE_STOPPED_PACKAGES) - - def isWebIntent(self) -> bool: - return self._scheme in (SCHEME_HTTP, SCHEME_HTTPS) - - def getBooleanArrayExtra(self, name: str) -> Optional[List[bool]]: - val = self._extras.get(name) - if isinstance(val, list): - return [bool(v) for v in val] - return None - - def getIntArrayExtra(self, name: str) -> Optional[List[int]]: - val = self._extras.get(name) - if isinstance(val, list): - return [int(v) for v in val] - return None - - def getLongArrayExtra(self, name: str) -> Optional[List[int]]: - val = self._extras.get(name) - if isinstance(val, list): - return [int(v) for v in val] - return None - - def getFloatArrayExtra(self, name: str) -> Optional[List[float]]: - val = self._extras.get(name) - if isinstance(val, list): - return [float(v) for v in val] - return None - - def getDoubleArrayExtra(self, name: str) -> Optional[List[float]]: - val = self._extras.get(name) - if isinstance(val, list): - return [float(v) for v in val] - return None - - def getStringArrayExtra(self, name: str) -> Optional[List[str]]: - val = self._extras.get(name) - if isinstance(val, list): - return [str(v) for v in val] - return None - - def getCharSequenceArrayExtra(self, name: str) -> Optional[List[str]]: - val = self._extras.get(name) - if isinstance(val, list): - return [str(v) for v in val] - return None - - def getBundleArrayExtra(self, name: str) -> Optional[List[Dict[str, Any]]]: - val = self._extras.get(name) - if isinstance(val, list) and all(isinstance(v, dict) for v in val): - return val - return None - - def hasExtraSecret(self, name: str) -> bool: - return name in self._extras - - def putExtraSecret(self, name: str, value: Any) -> "Intent": - self._extras[name] = value - return self - - def toShortString(self) -> str: - return self.toString()[:80] - - def toShortUri(self) -> str: - return self.toUri()[:80] - - -# ═══════════════════════════════════════════════════════════════ -# IntentReceiver -# ═══════════════════════════════════════════════════════════════ - -class IntentReceiver(Stub): - """IntentReceiver — Intent 接收器 (桌面存根)""" - - def __init__(self): - super().__init__() - self._result_code = 0 - self._result_data: Optional[str] = None - self._result_extras: Optional[Dict[str, Any]] = None - self._abort = False - - def onReceive(self, context: Any, intent: Intent) -> None: - """接收 Intent (桌面存根)""" - pass - - def getResultCode(self) -> int: - return self._result_code - - def setResultCode(self, code: int) -> None: - self._result_code = code - - def getResultData(self) -> Optional[str]: - return self._result_data - - def setResultData(self, data: str) -> None: - self._result_data = data - - def getResultExtras(self) -> Optional[Dict[str, Any]]: - return self._result_extras - - def setResultExtras(self, extras: Dict[str, Any]) -> None: - self._result_extras = dict(extras) - - def abortBroadcast(self) -> None: - self._abort = True - - def isAborted(self) -> bool: - return self._abort - - def clearAbortBroadcast(self) -> None: - self._abort = False - - def toString(self) -> str: - return f"IntentReceiver(resultCode={self._result_code})" - - -# ═══════════════════════════════════════════════════════════════ -# PendingIntent -# ═══════════════════════════════════════════════════════════════ - -class PendingIntent(_BasePendingIntent): - """PendingIntent — 延迟 Intent (继承 app.PendingIntent 的所有方法)""" - - FLAG_ONE_SHOT = 1 << 30 - FLAG_NO_CREATE = 1 << 29 - FLAG_CANCEL_CURRENT = 1 << 28 - FLAG_UPDATE_CURRENT = 1 << 27 - FLAG_IMMUTABLE = 1 << 26 - FLAG_MUTABLE = 1 << 25 - - def __init__(self, intent: Optional[Intent] = None, requestCode: int = 0, - flags: int = 0): - super().__init__(intent, requestCode, flags) - self._intent = intent - self._request_code = requestCode - self._flags = flags - self._canceled = False - - @staticmethod - def getActivity(context: Any, requestCode: int, intent: Intent, - flags: int) -> "PendingIntent": - return PendingIntent(intent, requestCode, flags) - - @staticmethod - def getActivities(context: Any, requestCode: int, - intents: List[Intent], flags: int) -> "PendingIntent": - return PendingIntent(intents[0] if intents else None, requestCode, flags) - - @staticmethod - def getBroadcast(context: Any, requestCode: int, intent: Intent, - flags: int) -> "PendingIntent": - return PendingIntent(intent, requestCode, flags) - - @staticmethod - def getService(context: Any, requestCode: int, intent: Intent, - flags: int) -> "PendingIntent": - return PendingIntent(intent, requestCode, flags) - - @staticmethod - def getForegroundService(context: Any, requestCode: int, intent: Intent, - flags: int) -> "PendingIntent": - return PendingIntent(intent, requestCode, flags) - - def getIntent(self) -> Optional[Intent]: - return self._intent - - def getCreatorPackage(self) -> str: - return "" - - def getCreatorUid(self) -> int: - return -1 - - def getTargetPackage(self) -> str: - return self._intent.getPackage() if self._intent else "" - - def getRequestCode(self) -> int: - return self._request_code - - def isActivity(self) -> bool: - return True - - def isForegroundService(self) -> bool: - return False - - def isBroadcast(self) -> bool: - return False - - def isService(self) -> bool: - return not self.isActivity() and not self.isBroadcast() - - def send(self, code: int = 0, onFinished: Any = None, - handler: Any = None, requiredPermission: Optional[str] = None, - options: Optional[Dict[str, Any]] = None) -> None: - """发送 PendingIntent (桌面存根)""" - pass - - def cancel(self) -> None: - self._canceled = True - - def equals(self, other: Any) -> bool: - if not isinstance(other, PendingIntent): - return False - return (self._intent == other._intent - and self._request_code == other._request_code) - - def hashCode(self) -> int: - h = hash(self._intent) if self._intent else 0 - return (h * 31 + self._request_code) & 0x7FFFFFFF - - def toString(self) -> str: - return f"PendingIntent(requestCode={self._request_code}, flags=0x{self._flags:08x})" - - def getCreatorToken(self) -> Optional[Any]: - return None - - def getTag(self, tagType: str) -> str: - return "" - - def writeToParcel(self, dest: Any, flags: int = 0) -> None: - pass - - def describeContents(self) -> int: - return 0 - - -# ═══════════════════════════════════════════════════════════════ -# IntentSender -# ═══════════════════════════════════════════════════════════════ - -class IntentSender(Stub): - """IntentSender — Intent 发送器""" - - class SendIntentException(Exception): - """SendIntentException — Intent 发送异常""" - pass - - def __init__(self, intent: Optional[Intent] = None): - super().__init__() - self._intent = intent - - @staticmethod - def getActivity(context: Any, requestCode: int, intent: Intent, - flags: int = 0) -> "IntentSender": - return IntentSender(intent) - - def getTargetPackage(self) -> str: - return self._intent.getPackage() if self._intent else "" - - def getCreatorUid(self) -> int: - return -1 - - def sendIntent(self, context: Any, code: int, onFinished: Any = None, - handler: Any = None, requiredPermission: str = "") -> None: - """发送 Intent (桌面存根)""" - pass - - def equals(self, other: Any) -> bool: - if not isinstance(other, IntentSender): - return False - return self._intent == other._intent - - def hashCode(self) -> int: - return hash(self._intent) & 0x7FFFFFFF if self._intent else 0 - - def toString(self) -> str: - return f"IntentSender({self._intent})" - - def describeContents(self) -> int: - return 0 - - def writeToParcel(self, dest: Any, flags: int = 0) -> None: - pass - - -# ═══════════════════════════════════════════════════════════════ -# IntentService -# ═══════════════════════════════════════════════════════════════ - -class IntentService(Stub): - """IntentService — Intent 服务 (桌面存根)""" - - def __init__(self, name: str = "IntentService"): - super().__init__() - self._name = name - self._started = False - self._redelivery = False - - def onCreate(self) -> None: - """服务创建""" - self._started = True - - def onStartCommand(self, intent: Optional[Intent], flags: int, startId: int) -> int: - """接收启动命令""" - return 1 # START_REDELIVER_INTENT - - def onBind(self, intent: Intent) -> Optional[Any]: - return None - - def onDestroy(self) -> None: - self._started = False - - def setIntentRedelivery(self, enabled: bool) -> None: - self._redelivery = enabled - - def stopSelf(self) -> None: - self._started = False - - def stopSelfResult(self, startId: int) -> bool: - self._started = False - return True - - def toString(self) -> str: - return f"IntentService(name={self._name}, started={self._started})" - - -# ═══════════════════════════════════════════════════════════════ -# IntentFilter -# ═══════════════════════════════════════════════════════════════ - -class IntentFilter(_BaseIntentFilter): - """IntentFilter — Intent 过滤器 (桌面存根,继承 content.IntentFilter 的所有方法)""" - - class AuthorityEntry(Stub): - """AuthorityEntry — 权限条目""" - - def __init__(self, host: str, port: int = -1): - super().__init__() - self._host = host - self._port = port - - def getHost(self) -> str: - return self._host - - def getPort(self) -> int: - return self._port - - def match(self, data: Optional[str]) -> int: - return IntentFilter.MATCH_CATEGORY_HOST if self._host else IntentFilter.NO_MATCH_DATA - - def toString(self) -> str: - return f"AuthorityEntry(host={self._host}, port={self._port})" - - class MalformedMimeTypeException(Exception): - """MalformedMimeTypeException — MIME 类型格式错误""" - pass - - class PriorityGroup: - """PriorityGroup — 优先级分组""" - PRIORITY_GROUP_UNDEFINED = 0 - PRIORITY_GROUP_BACKGROUND = 0x1000 - PRIORITY_GROUP_FOREGROUND = 0x2000 - PRIORITY_GROUP_SYSTEM = 0x3000 - - MATCH_CATEGORY_EMPTY = 0x00000000 - MATCH_CATEGORY_SCHEME = 0x00040000 - MATCH_CATEGORY_AUTHORITY = 0x00080000 - MATCH_CATEGORY_HOST = 0x00100000 - MATCH_CATEGORY_PORT = 0x00200000 - MATCH_CATEGORY_PATH = 0x00400000 - MATCH_CATEGORY_TYPE = 0x00800000 - MATCH_ADJUSTMENT_NORMAL = 0x00008000 - MATCH_ADJUSTMENT_MASK = 0x0000FFFF - NO_MATCH_ACTION = -3 - NO_MATCH_CATEGORY = -4 - NO_MATCH_DATA = -2 - NO_MATCH_TYPE = -1 - MATCH_CATEGORY_MASK = 0xFFFF0000 - - SYSTEM_HIGH_PRIORITY = 10000 - SYSTEM_LOW_PRIORITY = -10000 - - def __init__(self, action: str = "", dataType: str = "", - dataScheme: str = "", dataAuthority: str = "", - dataPath: str = ""): - super().__init__() - self._actions: List[str] = [action] if action else [] - self._categories: List[str] = [] - self._data_types: List[str] = [dataType] if dataType else [] - self._data_schemes: List[str] = [dataScheme] if dataScheme else [] - self._data_authorities: List[IntentFilter.AuthorityEntry] = [] - if dataAuthority: - self._data_authorities.append( - IntentFilter.AuthorityEntry(dataAuthority)) - self._data_paths: List[str] = [dataPath] if dataPath else [] - self._priority: int = 0 - self._has_partial_types = False - self._mime_group: Optional[str] = None - self._scheme_specific_parts: List[str] = [] - self._data_scheme_specific_parts: List[str] = [] - - def addAction(self, action: str) -> None: - if action not in self._actions: - self._actions.append(action) - - def addCategory(self, category: str) -> None: - if category not in self._categories: - self._categories.append(category) - - def addDataType(self, type_: str) -> None: - self._data_types.append(type_) - - def addDataScheme(self, scheme: str) -> None: - if scheme not in self._data_schemes: - self._data_schemes.append(scheme) - - def addDataAuthority(self, host: str, port: int = -1) -> None: - self._data_authorities.append( - IntentFilter.AuthorityEntry(host, port)) - - def addDataPath(self, path: str) -> None: - self._data_paths.append(path) - - def addDataSchemeSpecificPart(self, ssp: str) -> None: - self._scheme_specific_parts.append(ssp) - - def hasAction(self, action: str) -> bool: - return action in self._actions - - def hasCategory(self, category: str) -> bool: - return category in self._categories - - def hasDataType(self, type_: str) -> bool: - return type_ in self._data_types - - def hasDataScheme(self, scheme: str) -> bool: - return scheme in self._data_schemes - - def getAction(self, index: int) -> str: - return self._actions[index] if 0 <= index < len(self._actions) else "" - - def getCategory(self, index: int) -> str: - return self._categories[index] if 0 <= index < len(self._categories) else "" - - def getDataType(self, index: int) -> str: - return self._data_types[index] if 0 <= index < len(self._data_types) else "" - - def getDataScheme(self, index: int) -> str: - return self._data_schemes[index] if 0 <= index < len(self._data_schemes) else "" - - def getDataAuthority(self, index: int) -> Optional[AuthorityEntry]: - if 0 <= index < len(self._data_authorities): - return self._data_authorities[index] - return None - - def countActions(self) -> int: - return len(self._actions) - - def countCategories(self) -> int: - return len(self._categories) - - def countDataTypes(self) -> int: - return len(self._data_types) - - def countDataSchemes(self) -> int: - return len(self._data_schemes) - - def countDataAuthorities(self) -> int: - return len(self._data_authorities) - - def countDataPaths(self) -> int: - return len(self._data_paths) - - def setPriority(self, priority: int) -> None: - self._priority = priority - - def getPriority(self) -> int: - return self._priority - - def match(self, action: str, type_: Optional[str], scheme: str, - data: Optional[str], categories: Set[str], tag: str = "") -> int: - """匹配 Intent (桌面存根)""" - if self._actions and action not in self._actions: - return IntentFilter.NO_MATCH_ACTION - # 简化匹配逻辑 - return IntentFilter.MATCH_CATEGORY_EMPTY - - def matchAction(self, action: str) -> bool: - return action in self._actions - - def matchCategories(self, categories: Set[str]) -> Optional[str]: - for cat in categories: - if cat not in self._categories: - return cat - return None - - def matchData(self, type_: str, scheme: str, data: str) -> int: - """匹配数据 (桌面存根)""" - return IntentFilter.MATCH_CATEGORY_EMPTY - - def matchDataAuthority(self, data: Optional[str]) -> int: - for auth in self._data_authorities: - if auth.match(data) >= 0: - return IntentFilter.MATCH_CATEGORY_AUTHORITY - return IntentFilter.NO_MATCH_DATA - - def describeContents(self) -> int: - return 0 - - def writeToParcel(self, dest: Any, flags: int = 0) -> None: - pass - - def toString(self) -> str: - return (f"IntentFilter(actions={self._actions}, " - f"categories={self._categories}, " - f"types={self._data_types}, " - f"priority={self._priority})") - - def equals(self, other: Any) -> bool: - if not isinstance(other, IntentFilter): - return False - return (self._actions == other._actions - and self._categories == other._categories) - - def hashCode(self) -> int: - h = 0 - for a in self._actions: - h = h * 31 + hash(a) - for c in self._categories: - h = h * 31 + hash(c) - return h & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# MatchingFlags -# ═══════════════════════════════════════════════════════════════ - -class MatchingFlags(Stub): - """MatchingFlags — Intent 匹配标志""" - - MATCH_ALL = 0x00000000 - MATCH_DEFAULT_ONLY = 0x00010000 - MATCH_SYSTEM_ONLY = 0x00020000 - MATCH_DEBUG_TRIANGULATION = 0x00000010 - MATCH_INSTANT = 0x00000020 - MATCH_VISIBLE_TO_INSTANT_APP_ONLY = 0x00000040 - MATCH_DIRECT_BOOT_AUTO = 0x00000100 - MATCH_DIRECT_BOOT_AWARE = 0x00000200 - MATCH_DIRECT_BOOT_UNAWARE = 0x00000400 - MATCH_SYSTEM = 0x00010000 - MATCH_KNOWN_PACKAGES = 0x00080000 - MATCH_VERIFIED = 0x00100000 - MATCH_APEX = 0x01000000 - - def __init__(self, flags: int = MATCH_ALL): - super().__init__() - self._flags = flags - - def getFlags(self) -> int: - return self._flags - - def setFlags(self, flags: int) -> None: - self._flags = flags - - def toString(self) -> str: - return f"MatchingFlags(0x{self._flags:08x})" - - -# ═══════════════════════════════════════════════════════════════ -# ShortcutInfo -# ═══════════════════════════════════════════════════════════════ - -class ShortcutInfo(Stub): - """ShortcutInfo — 快捷方式信息""" - - DISABLED_REASON_UNDEFINED = 0 - DISABLED_REASON_NOT_DISABLED = 1 - DISABLED_REASON_BY_APP = 2 - DISABLED_REASON_BY_USER = 3 - DISABLED_REASON_UNKNOWN = 4 - DISABLED_REASON_BACKUP_NOT_SUPPORTED = 5 - DISABLED_REASON_BLOCKED = 6 - DISABLED_REASON_EXCLUDED = 7 - DISABLED_REASON_APP_CHANGED = 8 - DISABLED_REASON_RESTORE_APP_REMOVED = 9 - DISABLED_REASON_RESTORE_BACKUP_NOT_AVAILABLE = 10 - DISABLED_REASON_RESTORE_VERSION_LOWER = 11 - DISABLED_REASON_RESTORE_SIGNATURE_MISMATCH = 12 - DISABLED_REASON_RESTORE_UID_MISMATCH = 13 - DISABLED_REASON_RESTORE_STALE = 14 - DISABLED_REASON_RESTORE_OTHER = 15 - SHORTCUT_TYPE_CLASSIC = 0 - SHORTCUT_TYPE_DYNAMIC = 1 - SHORTCUT_TYPE_PINNED = 2 - - class Builder(Stub): - """ShortcutInfo.Builder — ShortcutInfo 构建器""" - - def __init__(self, context: Any, id_: str): - super().__init__() - self._id = id_ - self._context = context - self._short_label = "" - self._long_label = "" - self._disabled_message = "" - self._intent: Optional[Intent] = None - self._intents: List[Intent] = [] - self._icon: Optional[Any] = None - self._rank = 0 - self._activity: Optional[Any] = None - self._categories: Set[str] = set() - self._is_enabled = True - self._is_declared = False - self._shortcut_type = ShortcutInfo.SHORTCUT_TYPE_CLASSIC - self._icon_resource: Optional[Intent.ShortcutIconResource] = None - self._long_lived = False - self._person: Optional[Any] = None - self._package_name = "" - - def setShortLabel(self, label: str) -> "ShortcutInfo.Builder": - self._short_label = label - return self - - def setLongLabel(self, label: str) -> "ShortcutInfo.Builder": - self._long_label = label - return self - - def setDisabledMessage(self, msg: str) -> "ShortcutInfo.Builder": - self._disabled_message = msg - return self - - def setIntent(self, intent: Intent) -> "ShortcutInfo.Builder": - self._intent = intent - return self - - def setIntents(self, intents: List[Intent]) -> "ShortcutInfo.Builder": - self._intents = list(intents) - return self - - def setIcon(self, icon: Any) -> "ShortcutInfo.Builder": - self._icon = icon - return self - - def setRank(self, rank: int) -> "ShortcutInfo.Builder": - self._rank = rank - return self - - def setActivity(self, activity: Any) -> "ShortcutInfo.Builder": - self._activity = activity - return self - - def setCategories(self, categories: Set[str]) -> "ShortcutInfo.Builder": - self._categories = set(categories) - return self - - def setLongLived(self, longLived: bool) -> "ShortcutInfo.Builder": - self._long_lived = longLived - return self - - def setShortcutType(self, shortcutType: int) -> "ShortcutInfo.Builder": - self._shortcut_type = shortcutType - return self - - def setIconResource(self, resource: Intent.ShortcutIconResource) -> "ShortcutInfo.Builder": - self._icon_resource = resource - return self - - def setPackageName(self, packageName: str) -> "ShortcutInfo.Builder": - self._package_name = packageName - return self - - def build(self) -> "ShortcutInfo": - info = ShortcutInfo(self._id) - info._short_label = self._short_label - info._long_label = self._long_label - info._disabled_message = self._disabled_message - info._intent = self._intent - info._intents = list(self._intents) - info._icon = self._icon - info._rank = self._rank - info._activity = self._activity - info._categories = set(self._categories) - info._is_enabled = self._is_enabled - info._shortcut_type = self._shortcut_type - info._icon_resource = self._icon_resource - info._long_lived = self._long_lived - info._person = self._person - info._package_name = self._package_name - return info - - def __init__(self, id_: str = ""): - super().__init__() - self._id = id_ - self._package_name = "" - self._short_label = "" - self._long_label = "" - self._disabled_message = "" - self._intent: Optional[Intent] = None - self._intents: List[Intent] = [] - self._icon: Optional[Any] = None - self._rank = 0 - self._activity: Optional[Any] = None - self._categories: Set[str] = set() - self._is_enabled = True - self._is_declared = False - self._shortcut_type = ShortcutInfo.SHORTCUT_TYPE_CLASSIC - self._icon_resource: Optional[Intent.ShortcutIconResource] = None - self._last_changed_timestamp = 0 - self._long_lived = False - self._person: Optional[Any] = None - self._package_name = "" - - def getId(self) -> str: - return self._id - - def getPackage(self) -> str: - return self._package_name - - def getShortLabel(self) -> str: - return self._short_label - - def getLongLabel(self) -> str: - return self._long_label - - def getDisabledMessage(self) -> str: - return self._disabled_message - - def getIntent(self) -> Optional[Intent]: - return self._intent - - def getIntents(self) -> List[Intent]: - return list(self._intents) - - def getIcon(self) -> Optional[Any]: - return self._icon - - def getRank(self) -> int: - return self._rank - - def getActivity(self) -> Optional[Any]: - return self._activity - - def getCategories(self) -> Set[str]: - return set(self._categories) - - def isEnabled(self) -> bool: - return self._is_enabled - - def isDeclared(self) -> bool: - return self._is_declared - - def getShortcutType(self) -> int: - return self._shortcut_type - - def getLastChangedTimestamp(self) -> int: - return self._last_changed_timestamp - - def isLongLived(self) -> bool: - return self._long_lived - - def hasIconResource(self) -> bool: - return self._icon_resource is not None - - def getIconResource(self) -> Optional[Intent.ShortcutIconResource]: - return self._icon_resource - - def setRank(self, rank: int) -> None: - self._rank = rank - - def setCategories(self, categories: Set[str]) -> None: - self._categories = set(categories) - - def addCategory(self, category: str) -> None: - self._categories.add(category) - - def toString(self) -> str: - return (f"ShortcutInfo(id={self._id}, " - f"shortLabel={self._short_label}, " - f"rank={self._rank})") - - def equals(self, other: Any) -> bool: - if not isinstance(other, ShortcutInfo): - return False - return self._id == other._id - - def hashCode(self) -> int: - return hash(self._id) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# ShortcutManager -# ═══════════════════════════════════════════════════════════════ - -class ShortcutManager(Stub): - """ShortcutManager — 快捷方式管理器 (扩展)""" - - FLAG_MATCH_CACHED = 0x00000001 - FLAG_MATCH_DYNAMIC = 0x00000002 - FLAG_MATCH_MANIFEST = 0x00000004 - FLAG_MATCH_PINNED = 0x00000008 - FLAG_GET_ALL = (FLAG_MATCH_DYNAMIC | FLAG_MATCH_MANIFEST | FLAG_MATCH_PINNED) - RESULT_OK = 0 - RESULT_FAILURE = -1 - RESULT_DISABLED = -2 - RESULT_DUPLICATE = -3 - RESULT_MAX_SHORTCUTS_REACHED = -4 - RESULT_PINNED_MAX_SHORTCUTS_REACHED = -5 - RESULT_CALLER_NOT_ALLOWED = -6 - RESULT_PACKAGE_NOT_FOUND = -7 - RESULT_STORAGE_FULL = -8 - - def __init__(self, context: Optional[Any] = None): - super().__init__() - self._context = context - self._dynamic_shortcuts: Dict[str, ShortcutInfo] = {} - self._pinned_shortcuts: Dict[str, ShortcutInfo] = {} - self._manifest_shortcuts: Dict[str, ShortcutInfo] = {} - self._cached_shortcuts: Dict[str, ShortcutInfo] = {} - self._max_shortcut_count_per_activity = 5 - self._icon_max_width = -1 - self._icon_max_height = -1 - self._rate_limit_attempts = 0 - self._shortcuts_changed_listeners: List[Any] = [] - - def setDynamicShortcuts(self, shortcutInfos: List[ShortcutInfo]) -> bool: - self._dynamic_shortcuts.clear() - for info in shortcutInfos: - self._dynamic_shortcuts[info.getId()] = info - return True - - def getDynamicShortcuts(self) -> List[ShortcutInfo]: - return list(self._dynamic_shortcuts.values()) - - def getManifestShortcuts(self) -> List[ShortcutInfo]: - return list(self._manifest_shortcuts.values()) - - def getPinnedShortcuts(self) -> List[ShortcutInfo]: - return list(self._pinned_shortcuts.values()) - - def getCachedShortcuts(self) -> List[ShortcutInfo]: - return list(self._cached_shortcuts.values()) - - def addDynamicShortcuts(self, shortcutInfos: List[ShortcutInfo]) -> bool: - for info in shortcutInfos: - self._dynamic_shortcuts[info.getId()] = info - return True - - def removeDynamicShortcuts(self, shortcutIds: List[str]) -> None: - for sid in shortcutIds: - self._dynamic_shortcuts.pop(sid, None) - - def removeAllDynamicShortcuts(self) -> None: - self._dynamic_shortcuts.clear() - - def disableShortcuts(self, shortcutIds: List[str], - disabledMessage: str = "") -> None: - for sid in shortcutIds: - if sid in self._dynamic_shortcuts: - self._dynamic_shortcuts[sid]._is_enabled = False - if sid in self._pinned_shortcuts: - self._pinned_shortcuts[sid]._is_enabled = False - - def enableShortcuts(self, shortcutIds: List[str]) -> None: - for sid in shortcutIds: - if sid in self._dynamic_shortcuts: - self._dynamic_shortcuts[sid]._is_enabled = True - if sid in self._pinned_shortcuts: - self._pinned_shortcuts[sid]._is_enabled = True - - def getShortcuts(self, matchFlags: int = FLAG_GET_ALL) -> List[ShortcutInfo]: - result: List[ShortcutInfo] = [] - if matchFlags & ShortcutManager.FLAG_MATCH_DYNAMIC: - result.extend(self._dynamic_shortcuts.values()) - if matchFlags & ShortcutManager.FLAG_MATCH_PINNED: - result.extend(self._pinned_shortcuts.values()) - if matchFlags & ShortcutManager.FLAG_MATCH_MANIFEST: - result.extend(self._manifest_shortcuts.values()) - if matchFlags & ShortcutManager.FLAG_MATCH_CACHED: - result.extend(self._cached_shortcuts.values()) - return result - - def updateShortcuts(self, shortcutInfos: List[ShortcutInfo]) -> bool: - for info in shortcutInfos: - sid = info.getId() - if sid in self._dynamic_shortcuts: - self._dynamic_shortcuts[sid] = info - if sid in self._pinned_shortcuts: - self._pinned_shortcuts[sid] = info - return True - - def isRateLimitingActive(self) -> bool: - return self._rate_limit_attempts > 5 - - def getIconMaxWidth(self) -> int: - return self._icon_max_width - - def getIconMaxHeight(self) -> int: - return self._icon_max_height - - def pushDynamicShortcut(self, shortcut: ShortcutInfo) -> None: - self._dynamic_shortcuts[shortcut.getId()] = shortcut - - def removeLongLivedShortcuts(self, shortcutIds: List[str]) -> None: - for sid in shortcutIds: - self._dynamic_shortcuts.pop(sid, None) - self._pinned_shortcuts.pop(sid, None) - - def onApplicationActive(self, packageName: str, userId: int) -> None: - """应用激活回调 (桌面存根)""" - pass - - def registerShortcutsChangedListener( - self, listener: Any, handler: Any = None) -> None: - self._shortcuts_changed_listeners.append(listener) - - def unregisterShortcutsChangedListener(self, listener: Any) -> None: - if listener in self._shortcuts_changed_listeners: - self._shortcuts_changed_listeners.remove(listener) - - def reportShortcutUsed(self, shortcutId: str) -> None: - """报告快捷方式被使用""" - pass - - def setShareTargets(self, packageName: str, shareTargets: List[Any]) -> None: - """设置分享目标 (桌面存根)""" - pass - - def getShareTargets(self, packageName: str) -> List[Any]: - """获取分享目标 (桌面存根)""" - return [] - - def getMaxShortcutCountPerActivity(self) -> int: - return self._max_shortcut_count_per_activity - - def toString(self) -> str: - return (f"ShortcutManager(dynamic={len(self._dynamic_shortcuts)}, " - f"pinned={len(self._pinned_shortcuts)})") \ No newline at end of file diff --git a/android_pyapi/android/content_om.py b/android_pyapi/android/content_om.py deleted file mode 100644 index cf6c030..0000000 --- a/android_pyapi/android/content_om.py +++ /dev/null @@ -1,551 +0,0 @@ -""" -Android OverlayManager API — OverlayManager, OverlayInfo, OverlayManagerTransaction, OverlayStore -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import time -import json -import uuid -import threading -from typing import Any, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -# CATEGORY_* 常量 -CATEGORY_ALL = "android.overlay.category.ALL" -CATEGORY_FONT = "android.overlay.category.FONT" -CATEGORY_THEME = "android.overlay.category.THEME" -CATEGORY_ICON = "android.overlay.category.ICON" -CATEGORY_ICON_OVERLAY = "android.overlay.category.ICON_OVERLAY" -CATEGORY_SOUND = "android.overlay.category.SOUND" -CATEGORY_BOOT_ANIMATION = "android.overlay.category.BOOT_ANIMATION" -CATEGORY_WALLPAPER = "android.overlay.category.WALLPAPER" -CATEGORY_DYNAMIC = "android.overlay.category.DYNAMIC" -CATEGORY_RRO = "android.overlay.category.RRO" -CATEGORY_STATIC = "android.overlay.category.STATIC" - -# STATE_* 常量 -STATE_UNKNOWN = 0 -STATE_NOT_INSTALLED = 1 -STATE_INSTALLED = 2 -STATE_ENABLED = 3 -STATE_DISABLED = 4 -STATE_ENABLEMENT_DISABLED = 5 -STATE_ENABLEMENT_ENABLED = 6 -STATE_ENABLEMENT_UNKNOWN = 7 -STATE_OVERLAY_NOT_FOUND = 8 -STATE_OVERLAY_UPGRADING = 9 -STATE_OVERLAY_REMOVED = 10 -STATE_PACKAGE_INSTALLED = 11 -STATE_PACKAGE_UPDATED = 12 -STATE_PACKAGE_REMOVED = 13 -STATE_TARGET_PACKAGE_CHANGED = 14 -STATE_MISSING_TARGET_PACKAGE = 15 -STATE_UPGRADE_PENDING = 16 - -# FLAG_* 常量 -FLAG_ENABLE = 0x00000001 -FLAG_DISABLE = 0x00000002 -FLAG_REMOVE = 0x00000004 -FLAG_UPGRADE = 0x00000008 -FLAG_INSTALL = 0x00000010 -FLAG_IMMUTABLE = 0x00000020 -FLAG_SYSTEM = 0x00000040 -FLAG_STATIC_OVERLAY = 0x00000080 -FLAG_DYNAMIC_OVERLAY = 0x00000100 -FLAG_FIRST_TARGET = 0x00000200 -FLAG_LAST_TARGET = 0x00000400 -FLAG_EXTERNAL = 0x00000800 -FLAG_PERSISTENT = 0x00001000 - -# PRIORITY_ORDER 常量 -PRIORITY_ORDER_UNDEFINED = 0 -PRIORITY_ORDER_LOWEST = 1 -PRIORITY_ORDER_LOW = 2 -PRIORITY_ORDER_MEDIUM = 3 -PRIORITY_ORDER_HIGH = 4 -PRIORITY_ORDER_HIGHEST = 5 - - -# ═══════════════════════════════════════════════════════════════ -# OverlayInfo -# ═══════════════════════════════════════════════════════════════ - -class OverlayInfo(Stub): - """OverlayInfo — 覆盖层信息""" - - CATEGORY_ALL = CATEGORY_ALL - CATEGORY_FONT = CATEGORY_FONT - CATEGORY_THEME = CATEGORY_THEME - CATEGORY_ICON = CATEGORY_ICON - CATEGORY_ICON_OVERLAY = CATEGORY_ICON_OVERLAY - CATEGORY_SOUND = CATEGORY_SOUND - CATEGORY_BOOT_ANIMATION = CATEGORY_BOOT_ANIMATION - CATEGORY_WALLPAPER = CATEGORY_WALLPAPER - CATEGORY_DYNAMIC = CATEGORY_DYNAMIC - - STATE_UNKNOWN = STATE_UNKNOWN - STATE_NOT_INSTALLED = STATE_NOT_INSTALLED - STATE_INSTALLED = STATE_INSTALLED - STATE_ENABLED = STATE_ENABLED - STATE_DISABLED = STATE_DISABLED - STATE_ENABLEMENT_DISABLED = STATE_ENABLEMENT_DISABLED - STATE_ENABLEMENT_ENABLED = STATE_ENABLEMENT_ENABLED - STATE_ENABLEMENT_UNKNOWN = STATE_ENABLEMENT_UNKNOWN - STATE_OVERLAY_NOT_FOUND = STATE_OVERLAY_NOT_FOUND - STATE_OVERLAY_UPGRADING = STATE_OVERLAY_UPGRADING - STATE_PACKAGE_INSTALLED = STATE_PACKAGE_INSTALLED - STATE_PACKAGE_UPDATED = STATE_PACKAGE_UPDATED - STATE_PACKAGE_REMOVED = STATE_PACKAGE_REMOVED - STATE_TARGET_PACKAGE_CHANGED = STATE_TARGET_PACKAGE_CHANGED - STATE_UPGRADE_PENDING = STATE_UPGRADE_PENDING - - def __init__( - self, - packageName: str = "", - targetPackageName: str = "", - category: str = CATEGORY_THEME, - state: int = STATE_UNKNOWN, - ): - super().__init__() - self._package_name = packageName - self._target_package_name = targetPackageName - self._category = category - self._state = state - self._is_enabled = (state == STATE_ENABLED or state == STATE_ENABLEMENT_ENABLED) - self._priority = 0 - self._is_static = False - self._is_upgradable = False - self._is_system = False - self._version = 0 - self._config: Optional[str] = None - self._label: Optional[str] = None - self._icon: Optional[Any] = None - self._fabricated = False - self._overlay_path: Optional[str] = None - self._target_overlayable_name: Optional[str] = None - self._require_all = False - self._overlay_category: Optional[str] = category - self._base_path: Optional[str] = None - self._overlay_id = str(uuid.uuid4()) - self._install_time = time.time() - self._mod_time = time.time() - - def getPackageName(self) -> str: - return self._package_name - - def getTargetPackageName(self) -> str: - return self._target_package_name - - def getCategory(self) -> str: - return self._category - - def getState(self) -> int: - return self._state - - def setState(self, state: int) -> None: - self._state = state - self._is_enabled = (state == STATE_ENABLED) - - def isEnabled(self) -> bool: - return self._is_enabled - - def getPriority(self) -> int: - return self._priority - - def setPriority(self, priority: int) -> None: - self._priority = priority - - def isStatic(self) -> bool: - return self._is_static - - def isUpgradable(self) -> bool: - return self._is_upgradable - - def isSystem(self) -> bool: - return self._is_system - - def getVersion(self) -> int: - return self._version - - def getLabel(self) -> Optional[str]: - return self._label - - def getIcon(self) -> Optional[Any]: - return self._icon - - def isFabricated(self) -> bool: - return self._fabricated - - def getOverlayPath(self) -> Optional[str]: - return self._overlay_path - - def getTargetOverlayableName(self) -> Optional[str]: - return self._target_overlayable_name - - def getOverlayId(self) -> str: - return self._overlay_id - - def getInstallTime(self) -> float: - return self._install_time - - def getModTime(self) -> float: - return self._mod_time - - def getConfig(self) -> Optional[str]: - return self._config - - def requiresAll(self) -> bool: - return self._require_all - - def toString(self) -> str: - return ( - f"OverlayInfo(package={self._package_name}, " - f"target={self._target_package_name}, " - f"category={self._category}, " - f"state={self._state})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# OverlayManagerTransaction -# ═══════════════════════════════════════════════════════════════ - -class OverlayManagerTransaction(Stub): - """OverlayManagerTransaction — 覆盖层管理器事务""" - - TYPE_UNKNOWN = 0 - TYPE_SET_ENABLED = 1 - TYPE_SET_DISABLED = 2 - TYPE_REGISTER = 3 - TYPE_UNREGISTER = 4 - TYPE_SET_PRIORITY = 5 - TYPE_SET_CATEGORY = 6 - TYPE_SET_TARGET = 7 - TYPE_REMOVE = 8 - TYPE_SET_STATE = 9 - - def __init__(self): - super().__init__() - self._operations: List[Dict[str, Any]] = [] - self._transaction_id = str(uuid.uuid4()) - self._created_at = time.time() - self._committed = False - self._rollback_on_failure = True - - def setEnabled(self, packageName: str, enabled: bool) -> "OverlayManagerTransaction": - self._operations.append({ - "type": OverlayManagerTransaction.TYPE_SET_ENABLED, - "packageName": packageName, - "enabled": enabled, - }) - return self - - def setDisabled(self, packageName: str) -> "OverlayManagerTransaction": - self._operations.append({ - "type": OverlayManagerTransaction.TYPE_SET_DISABLED, - "packageName": packageName, - }) - return self - - def registerOverlay(self, packageName: str) -> "OverlayManagerTransaction": - self._operations.append({ - "type": OverlayManagerTransaction.TYPE_REGISTER, - "packageName": packageName, - }) - return self - - def unregisterOverlay(self, packageName: str) -> "OverlayManagerTransaction": - self._operations.append({ - "type": OverlayManagerTransaction.TYPE_UNREGISTER, - "packageName": packageName, - }) - return self - - def setPriority(self, packageName: str, priority: int) -> "OverlayManagerTransaction": - self._operations.append({ - "type": OverlayManagerTransaction.TYPE_SET_PRIORITY, - "packageName": packageName, - "priority": priority, - }) - return self - - def setCategory(self, packageName: str, category: str) -> "OverlayManagerTransaction": - self._operations.append({ - "type": OverlayManagerTransaction.TYPE_SET_CATEGORY, - "packageName": packageName, - "category": category, - }) - return self - - def setTarget(self, packageName: str, target: str) -> "OverlayManagerTransaction": - self._operations.append({ - "type": OverlayManagerTransaction.TYPE_SET_TARGET, - "packageName": packageName, - "target": target, - }) - return self - - def removeOverlay(self, packageName: str) -> "OverlayManagerTransaction": - self._operations.append({ - "type": OverlayManagerTransaction.TYPE_REMOVE, - "packageName": packageName, - }) - return self - - def getOperations(self) -> List[Dict[str, Any]]: - return list(self._operations) - - def getTransactionId(self) -> str: - return self._transaction_id - - def isCommitted(self) -> bool: - return self._committed - - def getOperationCount(self) -> int: - return len(self._operations) - - def toString(self) -> str: - return f"OverlayManagerTransaction(ops={len(self._operations)}, committed={self._committed})" - - -# ═══════════════════════════════════════════════════════════════ -# OverlayStore -# ═══════════════════════════════════════════════════════════════ - -class OverlayStore(Stub): - """OverlayStore — 覆盖层存储""" - - def __init__(self): - super().__init__() - self._overlays: Dict[str, OverlayInfo] = {} - self._lock = threading.Lock() - self._store_path: Optional[str] = None - self._max_overlays = 100 - self._persist_enabled = False - - def getOverlay(self, packageName: str) -> Optional[OverlayInfo]: - with self._lock: - return self._overlays.get(packageName) - - def getAllOverlays(self) -> List[OverlayInfo]: - with self._lock: - return list(self._overlays.values()) - - def getOverlaysForTarget(self, targetPackage: str) -> List[OverlayInfo]: - with self._lock: - return [ - info for info in self._overlays.values() - if info.getTargetPackageName() == targetPackage - ] - - def getOverlaysByCategory(self, category: str) -> List[OverlayInfo]: - with self._lock: - return [ - info for info in self._overlays.values() - if info.getCategory() == category - ] - - def addOverlay(self, info: OverlayInfo) -> None: - with self._lock: - self._overlays[info.getPackageName()] = info - - def removeOverlay(self, packageName: str) -> Optional[OverlayInfo]: - with self._lock: - return self._overlays.pop(packageName, None) - - def updateOverlay(self, packageName: str, info: OverlayInfo) -> bool: - with self._lock: - if packageName in self._overlays: - self._overlays[packageName] = info - return True - return False - - def contains(self, packageName: str) -> bool: - with self._lock: - return packageName in self._overlays - - def getEnabledOverlays(self) -> List[OverlayInfo]: - with self._lock: - return [info for info in self._overlays.values() if info.isEnabled()] - - def getDisabledOverlays(self) -> List[OverlayInfo]: - with self._lock: - return [info for info in self._overlays.values() if not info.isEnabled()] - - def getStaticOverlays(self) -> List[OverlayInfo]: - with self._lock: - return [info for info in self._overlays.values() if info.isStatic()] - - def getOverlayCount(self) -> int: - with self._lock: - return len(self._overlays) - - def clear(self) -> None: - with self._lock: - self._overlays.clear() - - def toString(self) -> str: - with self._lock: - return f"OverlayStore(overlays={len(self._overlays)})" - - -# ═══════════════════════════════════════════════════════════════ -# OverlayManager -# ═══════════════════════════════════════════════════════════════ - -class OverlayManager(Stub): - """OverlayManager — 覆盖层管理器 (桌面存根)""" - - CATEGORY_ALL = CATEGORY_ALL - CATEGORY_FONT = CATEGORY_FONT - CATEGORY_THEME = CATEGORY_THEME - CATEGORY_ICON = CATEGORY_ICON - CATEGORY_SOUND = CATEGORY_SOUND - - FLAG_ENABLE = FLAG_ENABLE - FLAG_DISABLE = FLAG_DISABLE - FLAG_REMOVE = FLAG_REMOVE - FLAG_UPGRADE = FLAG_UPGRADE - FLAG_INSTALL = FLAG_INSTALL - FLAG_IMMUTABLE = FLAG_IMMUTABLE - FLAG_SYSTEM = FLAG_SYSTEM - FLAG_STATIC_OVERLAY = FLAG_STATIC_OVERLAY - FLAG_DYNAMIC_OVERLAY = FLAG_DYNAMIC_OVERLAY - FLAG_EXTERNAL = FLAG_EXTERNAL - FLAG_PERSISTENT = FLAG_PERSISTENT - - STATE_UNKNOWN = STATE_UNKNOWN - STATE_NOT_INSTALLED = STATE_NOT_INSTALLED - STATE_INSTALLED = STATE_INSTALLED - STATE_ENABLED = STATE_ENABLED - STATE_DISABLED = STATE_DISABLED - STATE_ENABLEMENT_DISABLED = STATE_ENABLEMENT_DISABLED - STATE_ENABLEMENT_ENABLED = STATE_ENABLEMENT_ENABLED - STATE_ENABLEMENT_UNKNOWN = STATE_ENABLEMENT_UNKNOWN - STATE_OVERLAY_NOT_FOUND = STATE_OVERLAY_NOT_FOUND - STATE_OVERLAY_UPGRADING = STATE_OVERLAY_UPGRADING - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._store = OverlayStore() - self._lock = threading.Lock() - self._transaction_history: List[OverlayManagerTransaction] = [] - self._callbacks: Dict[str, List[Callable]] = {} - self._is_available = True - - def getOverlayInfo(self, packageName: str) -> Optional[OverlayInfo]: - with self._lock: - return self._store.getOverlay(packageName) - - def getAllOverlays(self) -> List[OverlayInfo]: - with self._lock: - return self._store.getAllOverlays() - - def getOverlaysForTarget(self, targetPackage: str) -> List[OverlayInfo]: - with self._lock: - return self._store.getOverlaysForTarget(targetPackage) - - def setEnabled(self, packageName: str, enabled: bool, userId: int = 0) -> None: - with self._lock: - info = self._store.getOverlay(packageName) - if info: - new_state = STATE_ENABLED if enabled else STATE_DISABLED - info.setState(new_state) - - def setEnabledExclusive( - self, packageName: str, withinCategory: bool, userId: int = 0 - ) -> None: - with self._lock: - info = self._store.getOverlay(packageName) - if info: - info.setState(STATE_ENABLED) - category = info.getCategory() - for overlay in self._store.getAllOverlays(): - if overlay.getPackageName() != packageName: - if withinCategory and overlay.getCategory() == category: - overlay.setState(STATE_DISABLED) - elif not withinCategory: - overlay.setState(STATE_DISABLED) - - def isEnabled(self, packageName: str) -> bool: - with self._lock: - info = self._store.getOverlay(packageName) - return info is not None and info.isEnabled() - - def commit( - self, transaction: OverlayManagerTransaction - ) -> None: - with self._lock: - for op in transaction.getOperations(): - op_type = op.get("type") - pkg = op.get("packageName", "") - if op_type == OverlayManagerTransaction.TYPE_SET_ENABLED: - self.setEnabled(pkg, op.get("enabled", True)) - elif op_type == OverlayManagerTransaction.TYPE_SET_DISABLED: - self.setEnabled(pkg, False) - elif op_type == OverlayManagerTransaction.TYPE_REGISTER: - info = OverlayInfo(packageName=pkg) - self._store.addOverlay(info) - elif op_type == OverlayManagerTransaction.TYPE_UNREGISTER: - self._store.removeOverlay(pkg) - elif op_type == OverlayManagerTransaction.TYPE_SET_PRIORITY: - info = self._store.getOverlay(pkg) - if info: - info.setPriority(op.get("priority", 0)) - elif op_type == OverlayManagerTransaction.TYPE_REMOVE: - self._store.removeOverlay(pkg) - self._transaction_history.append(transaction) - - def getDefaultOverlayPackages(self) -> List[str]: - return [] - - def getOverlayInfosForTarget(self, targetPackage: str, userId: int = 0) -> List[OverlayInfo]: - return self.getOverlaysForTarget(targetPackage) - - def getOverlayStore(self) -> OverlayStore: - return self._store - - def registerCallback(self, callback: Any) -> None: - pass - - def unregisterCallback(self, callback: Any) -> None: - pass - - def onOverlayChanged(self, packageName: str) -> None: - pass - - def onOverlayStateChanged(self, packageName: str, newState: int) -> None: - pass - - def isUpgradePending(self, packageName: str) -> bool: - return False - - def getCategoryForOverlay(self, packageName: str) -> Optional[str]: - with self._lock: - info = self._store.getOverlay(packageName) - return info.getCategory() if info else None - - def getPriority(self, packageName: str) -> int: - with self._lock: - info = self._store.getOverlay(packageName) - return info.getPriority() if info else 0 - - def toString(self) -> str: - with self._lock: - return ( - f"OverlayManager(overlays={self._store.getOverlayCount()}, " - f"transactions={len(self._transaction_history)})" - ) \ No newline at end of file diff --git a/android_pyapi/android/content_pm.py b/android_pyapi/android/content_pm.py deleted file mode 100644 index 1f2283e..0000000 --- a/android_pyapi/android/content_pm.py +++ /dev/null @@ -1,801 +0,0 @@ -""" -Android PackageManager 组件 — PackageManager, PackageInfo, ApplicationInfo 等 -方法签名完全对齐 Android SDK -""" - -import json -import os -import sys -from typing import Any, Dict, List, Optional, Set - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) - - -# ═══════════════════════════════════════════════════════════════ -# 数据存储路径 -# ═══════════════════════════════════════════════════════════════ - -_PACKAGE_DATA_DIR = os.path.join(_app_data_dir, "package_data") -os.makedirs(_PACKAGE_DATA_DIR, exist_ok=True) - -_DEFAULT_PACKAGE_INFO_PATH = os.path.join(_PACKAGE_DATA_DIR, f"{_package_name}.json") - - -def _get_default_package_info() -> Dict[str, Any]: - """获取默认包信息""" - return { - "packageName": _package_name, - "versionName": "1.0.0", - "versionCode": 1, - "applicationInfo": { - "name": _package_name, - "packageName": _package_name, - "className": f"{_package_name}.MainApplication", - "icon": 0, - "targetSdkVersion": 34, - "sourceDir": os.path.join(_app_data_dir, "base.apk"), - "dataDir": _app_data_dir, - "enabled": True, - "flags": 0, - "theme": 0, - "labelRes": 0, - "descriptionRes": 0, - "logo": 0, - "banner": 0, - "processName": _package_name, - "uid": 10001, - "manageSpaceActivityName": "", - "taskAffinity": "", - "permission": "", - }, - "activities": [], - "services": [], - "receivers": [], - "providers": [], - "permissions": [], - "requestedPermissions": [ - "android.permission.INTERNET", - "android.permission.ACCESS_NETWORK_STATE", - ], - "features": { - "android.hardware.camera": True, - "android.hardware.wifi": True, - "android.hardware.bluetooth": True, - "android.hardware.location": True, - "android.hardware.sensor.accelerometer": True, - "android.hardware.sensor.gyroscope": True, - "android.hardware.touchscreen": True, - "android.hardware.microphone": True, - "android.hardware.nfc": False, - "android.hardware.telephony": True, - "android.software.picture_in_picture": True, - "android.hardware.usb.host": False, - } - } - - -# ═══════════════════════════════════════════════════════════════ -# PackageItemInfo (基类) -# ═══════════════════════════════════════════════════════════════ - -class PackageItemInfo(Stub): - """PackageItemInfo — 包条目信息基类""" - - def __init__(self, data: Optional[Dict[str, Any]] = None): - super().__init__() - data = data or {} - self.name = data.get("name", "") - self.packageName = data.get("packageName", "") - self.labelRes = data.get("labelRes", 0) - self.nonLocalizedLabel = data.get("nonLocalizedLabel", None) - self.icon = data.get("icon", 0) - self.banner = data.get("banner", 0) - self.logo = data.get("logo", 0) - - def loadLabel(self, pm: "PackageManager") -> str: - return self.nonLocalizedLabel or self.name.split(".")[-1] if self.name else "" - - def loadIcon(self, pm: "PackageManager") -> Any: - return None - - def loadLogo(self, pm: "PackageManager") -> Any: - return None - - def loadBanner(self, pm: "PackageManager") -> Any: - return None - - def toString(self) -> str: - return f"{self.__class__.__name__}({self.packageName}:{self.name})" - - -class ComponentInfo(PackageItemInfo): - """ComponentInfo — 组件信息基类""" - - def __init__(self, data: Optional[Dict[str, Any]] = None): - super().__init__(data) - data = data or {} - self.processName = data.get("processName", self.packageName) - self.splitName = data.get("splitName", None) - self.enabled = data.get("enabled", True) - self.exported = data.get("exported", False) - self.directBootAware = data.get("directBootAware", False) - - def getComponentName(self) -> Any: - from .content import ComponentName - return ComponentName(self.packageName, self.name) - - def isEnabled(self) -> bool: - return self.enabled - - -# ═══════════════════════════════════════════════════════════════ -# ApplicationInfo -# ═══════════════════════════════════════════════════════════════ - -class ApplicationInfo(ComponentInfo): - """ApplicationInfo — 应用信息""" - - FLAG_SYSTEM = 1 - FLAG_DEBUGGABLE = 2 - FLAG_HAS_CODE = 4 - FLAG_ALLOW_CLEAR_USER_DATA = 0x00400000 - FLAG_ALLOW_BACKUP = 0x00008000 - FLAG_EXTERNAL_STORAGE = 0x00100000 - FLAG_LARGE_HEAP = 0x00100000 - FLAG_MULTIPROCESS = 0x00010000 - FLAG_TEST_ONLY = 0x00000100 - FLAG_STOPPED = 0x00200000 - FLAG_RESTART_ON_UPGRADE = 0x00000020 - - def __init__(self, data: Optional[Dict[str, Any]] = None): - super().__init__(data) - data = data or {} - self.taskAffinity = data.get("taskAffinity", "") - self.permission = data.get("permission", "") - self.processName = data.get("processName", self.packageName) - self.className = data.get("className", "") - self.descriptionRes = data.get("descriptionRes", 0) - self.theme = data.get("theme", 0) - self.flags = data.get("flags", 0) - self.sourceDir = data.get("sourceDir", "") - self.publicSourceDir = data.get("publicSourceDir", self.sourceDir) - self.dataDir = data.get("dataDir", "") - self.nativeLibraryDir = data.get("nativeLibraryDir", "") - self.metaData = data.get("metaData", None) - self.manageSpaceActivityName = data.get("manageSpaceActivityName", "") - self.targetSdkVersion = data.get("targetSdkVersion", 34) - self.uid = data.get("uid", 10001) - self.backupAgentName = data.get("backupAgentName", "") - self.enabled = data.get("enabled", True) - - def loadDescription(self, pm: "PackageManager") -> Optional[str]: - return None - - def toString(self) -> str: - return f"ApplicationInfo({self.packageName}, targetSdk={self.targetSdkVersion})" - - -# ═══════════════════════════════════════════════════════════════ -# ActivityInfo -# ═══════════════════════════════════════════════════════════════ - -class ActivityInfo(ComponentInfo): - """ActivityInfo — Activity 组件信息""" - - CONFIG_MCC = 0x0001 - CONFIG_MNC = 0x0002 - CONFIG_LOCALE = 0x0004 - CONFIG_TOUCHSCREEN = 0x0008 - CONFIG_KEYBOARD = 0x0010 - CONFIG_KEYBOARD_HIDDEN = 0x0020 - CONFIG_NAVIGATION = 0x0040 - CONFIG_ORIENTATION = 0x0080 - CONFIG_SCREEN_LAYOUT = 0x0100 - CONFIG_UI_MODE = 0x0200 - CONFIG_SCREEN_SIZE = 0x0400 - CONFIG_SMALLEST_SCREEN_SIZE = 0x0800 - CONFIG_DENSITY = 0x1000 - CONFIG_LAYOUT_DIRECTION = 0x2000 - CONFIG_COLOR_MODE = 0x4000 - - LAUNCH_MULTIPLE = 0 - LAUNCH_SINGLE_TOP = 1 - LAUNCH_SINGLE_TASK = 2 - LAUNCH_SINGLE_INSTANCE = 3 - LAUNCH_SINGLE_INSTANCE_PER_TASK = 4 - - SCREEN_ORIENTATION_UNSPECIFIED = -1 - SCREEN_ORIENTATION_LANDSCAPE = 0 - SCREEN_ORIENTATION_PORTRAIT = 1 - SCREEN_ORIENTATION_USER = 2 - SCREEN_ORIENTATION_BEHIND = 3 - SCREEN_ORIENTATION_SENSOR = 4 - SCREEN_ORIENTATION_NOSENSOR = 5 - SCREEN_ORIENTATION_SENSOR_LANDSCAPE = 6 - SCREEN_ORIENTATION_SENSOR_PORTRAIT = 7 - SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8 - SCREEN_ORIENTATION_REVERSE_PORTRAIT = 9 - SCREEN_ORIENTATION_FULL_SENSOR = 10 - SCREEN_ORIENTATION_USER_LANDSCAPE = 11 - SCREEN_ORIENTATION_USER_PORTRAIT = 12 - SCREEN_ORIENTATION_FULL_USER = 13 - SCREEN_ORIENTATION_LOCKED = 14 - - def __init__(self, data: Optional[Dict[str, Any]] = None): - super().__init__(data) - data = data or {} - self.launchMode = data.get("launchMode", self.LAUNCH_MULTIPLE) - self.screenOrientation = data.get("screenOrientation", self.SCREEN_ORIENTATION_UNSPECIFIED) - self.configChanges = data.get("configChanges", 0) - self.softInputMode = data.get("softInputMode", 0) - self.theme = data.get("theme", 0) - self.parentActivityName = data.get("parentActivityName", "") - self.permission = data.get("permission", "") - self.taskAffinity = data.get("taskAffinity", "") - self.targetActivity = data.get("targetActivity", "") - - def toString(self) -> str: - return f"ActivityInfo({self.name}, launchMode={self.launchMode})" - - -# ═══════════════════════════════════════════════════════════════ -# ServiceInfo -# ═══════════════════════════════════════════════════════════════ - -class ServiceInfo(ComponentInfo): - """ServiceInfo — Service 组件信息""" - - FLAG_STOP_WITH_TASK = 0x01 - FLAG_ISOLATED_PROCESS = 0x02 - FLAG_EXTERNAL_SERVICE = 0x04 - FLAG_SINGLE_USER = 0x40000000 - FLAG_USE_APP_OP = 0x08 - - def __init__(self, data: Optional[Dict[str, Any]] = None): - super().__init__(data) - data = data or {} - self.flags = data.get("flags", 0) - self.permission = data.get("permission", "") - self.foregroundServiceType = data.get("foregroundServiceType", 0) - - def toString(self) -> str: - return f"ServiceInfo({self.name})" - - -# ═══════════════════════════════════════════════════════════════ -# ProviderInfo -# ═══════════════════════════════════════════════════════════════ - -class ProviderInfo(ComponentInfo): - """ProviderInfo — ContentProvider 组件信息""" - - def __init__(self, data: Optional[Dict[str, Any]] = None): - super().__init__(data) - data = data or {} - self.authority = data.get("authority", "") - self.readPermission = data.get("readPermission", "") - self.writePermission = data.get("writePermission", "") - self.grantUriPermissions = data.get("grantUriPermissions", False) - self.multiprocess = data.get("multiprocess", False) - self.initOrder = data.get("initOrder", 0) - self.flags = data.get("flags", 0) - - def toString(self) -> str: - return f"ProviderInfo({self.name}, authority={self.authority})" - - -# ═══════════════════════════════════════════════════════════════ -# PermissionInfo -# ═══════════════════════════════════════════════════════════════ - -class PermissionInfo(PackageItemInfo): - """PermissionInfo — 权限信息""" - - PROTECTION_NORMAL = 0 - PROTECTION_DANGEROUS = 1 - PROTECTION_SIGNATURE = 2 - PROTECTION_SIGNATURE_OR_SYSTEM = 3 - PROTECTION_INTERNAL = 4 - - FLAG_COSTS_MONEY = 0x01 - FLAG_HARDWARE_RESTRICTED = 0x04 - FLAG_IMMUTABLY_RESTRICTED = 0x08 - FLAG_INSTALLED = 0x10 - - def __init__(self, data: Optional[Dict[str, Any]] = None): - super().__init__(data) - data = data or {} - self.group = data.get("group", "") - self.descriptionRes = data.get("descriptionRes", 0) - self.protectionLevel = data.get("protectionLevel", self.PROTECTION_NORMAL) - self.flags = data.get("flags", 0) - - def loadDescription(self, pm: "PackageManager") -> Optional[str]: - return None - - def toString(self) -> str: - return f"PermissionInfo({self.name}, protectionLevel={self.protectionLevel})" - - -# ═══════════════════════════════════════════════════════════════ -# ResolveInfo -# ═══════════════════════════════════════════════════════════════ - -class ResolveInfo(Stub): - """ResolveInfo — 解析意图的结果""" - - def __init__(self): - super().__init__() - self.activityInfo: Optional[ActivityInfo] = None - self.serviceInfo: Optional[ServiceInfo] = None - self.providerInfo: Optional[ProviderInfo] = None - self.filter: Optional[Any] = None - self.priority = 0 - self.match = 0 - self.preferredOrder = 0 - self.isDefault = False - self.labelRes = 0 - self.nonLocalizedLabel = None - self.icon = 0 - self.resolvePackageName = "" - self.specificIndex = -1 - self.system = False - - def loadLabel(self, pm: "PackageManager") -> str: - if self.activityInfo: - return self.activityInfo.name.split(".")[-1] - if self.serviceInfo: - return self.serviceInfo.name.split(".")[-1] - return "" - - def loadIcon(self, pm: "PackageManager"): - return None - - def getIconResource(self) -> int: - return self.icon - - def toString(self) -> str: - return f"ResolveInfo(priority={self.priority})" - - -# ═══════════════════════════════════════════════════════════════ -# PackageInfo -# ═══════════════════════════════════════════════════════════════ - -class PackageInfo(Stub): - """PackageInfo — 包信息""" - - INSTALL_LOCATION_AUTO = 0 - INSTALL_LOCATION_INTERNAL_ONLY = 1 - INSTALL_LOCATION_PREFER_EXTERNAL = 2 - REQUESTED_PERMISSION_REQUIRED = 0x00100000 - REQUESTED_PERMISSION_GRANTED = 0x01000000 - REQUESTED_PERMISSION_DENIED = 0x02000000 - - def __init__(self): - super().__init__() - self.packageName = "" - self.versionName = "" - self.versionCode = 0 - self.applicationInfo: Optional[ApplicationInfo] = None - self.activities: List[ActivityInfo] = [] - self.services: List[ServiceInfo] = [] - self.receivers: List[ActivityInfo] = [] - self.providers: List[ProviderInfo] = [] - self.permissions: List[PermissionInfo] = [] - self.requestedPermissions: List[str] = [] - self.requestedPermissionsFlags: List[int] = [] - self.sharedUserId = "" - self.sharedUserLabel = 0 - self.firstInstallTime = 0 - self.lastUpdateTime = 0 - self.gids: List[int] = [] - self.installLocation = self.INSTALL_LOCATION_AUTO - self.configPreferences: List = [] - self.featureGroups: List = [] - self.packageName_ = "" - - def toString(self) -> str: - return (f"PackageInfo({self.packageName} v{self.versionName} " - f"code={self.versionCode})") - - -# ═══════════════════════════════════════════════════════════════ -# Signature -# ═══════════════════════════════════════════════════════════════ - -class Signature(Stub): - """Signature — APK 签名""" - - def __init__(self, signature: str = ""): - super().__init__() - self._signature = signature - - def toCharsString(self) -> str: - return self._signature - - def toByteArray(self) -> bytes: - return self._signature.encode("utf-8") if self._signature else b"" - - def equals(self, other) -> bool: - if isinstance(other, Signature): - return self._signature == other._signature - return False - - def hashCode(self) -> int: - return hash(self._signature) & 0x7FFFFFFF - - def toString(self) -> str: - return self._signature[:64] if len(self._signature) > 64 else self._signature - - -# ═══════════════════════════════════════════════════════════════ -# PackageStats -# ═══════════════════════════════════════════════════════════════ - -class PackageStats(Stub): - """PackageStats — 包存储统计""" - - def __init__(self, package_name: str = ""): - super().__init__() - self.packageName = package_name - self.codeSize = 0 - self.dataSize = 0 - self.cacheSize = 0 - self.externalCodeSize = 0 - self.externalDataSize = 0 - self.externalCacheSize = 0 - self.externalMediaSize = 0 - self.externalObbSize = 0 - - def toString(self) -> str: - return (f"PackageStats({self.packageName}, " - f"code={self.codeSize}, data={self.dataSize})") - - -# ═══════════════════════════════════════════════════════════════ -# PackageManager -# ═══════════════════════════════════════════════════════════════ - -class PackageManager(Stub): - """PackageManager — 包管理器""" - - # Flag 常量 - GET_ACTIVITIES = 1 - GET_CONFIGURATIONS = 2 - GET_GIDS = 4 - GET_INSTRUMENTATION = 8 - GET_INTENT_FILTERS = 16 - GET_META_DATA = 128 - GET_PERMISSIONS = 4096 - GET_PROVIDERS = 8 - GET_RECEIVERS = 4 - GET_SERVICES = 2 - GET_SHARED_LIBRARY_FILES = 32 - GET_SIGNATURES = 64 - GET_UNINSTALLED_PACKAGES = 512 - GET_URI_PERMISSION_PATTERNS = 256 - MATCH_ALL = 0x10000 - MATCH_DISABLED_COMPONENTS = 0x200 - MATCH_DISABLED_UNTIL_USED_COMPONENTS = 0x8000 - MATCH_DEFAULT_ONLY = 0x10000 - MATCH_SYSTEM_ONLY = 0x800000 - MATCH_UNINSTALLED_PACKAGES = 0x2000000 - PERMISSION_DENIED = -1 - PERMISSION_GRANTED = 0 - - # 特性常量 - FEATURE_CAMERA = "android.hardware.camera" - FEATURE_CAMERA_AUTOFOCUS = "android.hardware.camera.autofocus" - FEATURE_CAMERA_FLASH = "android.hardware.camera.flash" - FEATURE_CAMERA_FRONT = "android.hardware.camera.front" - FEATURE_LOCATION = "android.hardware.location" - FEATURE_LOCATION_GPS = "android.hardware.location.gps" - FEATURE_LOCATION_NETWORK = "android.hardware.location.network" - FEATURE_MICROPHONE = "android.hardware.microphone" - FEATURE_NFC = "android.hardware.nfc" - FEATURE_SENSOR_ACCELEROMETER = "android.hardware.sensor.accelerometer" - FEATURE_SENSOR_BAROMETER = "android.hardware.sensor.barometer" - FEATURE_SENSOR_COMPASS = "android.hardware.sensor.compass" - FEATURE_SENSOR_GYROSCOPE = "android.hardware.sensor.gyroscope" - FEATURE_SENSOR_LIGHT = "android.hardware.sensor.light" - FEATURE_SENSOR_PROXIMITY = "android.hardware.sensor.proximity" - FEATURE_TELEPHONY = "android.hardware.telephony" - FEATURE_TOUCHSCREEN = "android.hardware.touchscreen" - FEATURE_USB_HOST = "android.hardware.usb.host" - FEATURE_WIFI = "android.hardware.wifi" - FEATURE_BLUETOOTH = "android.hardware.bluetooth" - - def __init__(self): - super().__init__() - self._packages: Dict[str, Dict[str, Any]] = {} - self._load_packages() - - def _load_packages(self): - """从 JSON 文件加载包信息""" - try: - if os.path.exists(_DEFAULT_PACKAGE_INFO_PATH): - with open(_DEFAULT_PACKAGE_INFO_PATH, "r", encoding="utf-8") as f: - data = json.load(f) - if data.get("packageName"): - self._packages[data["packageName"]] = data - else: - # 创建默认信息 - default_info = _get_default_package_info() - self._packages[_package_name] = default_info - os.makedirs(os.path.dirname(_DEFAULT_PACKAGE_INFO_PATH), exist_ok=True) - with open(_DEFAULT_PACKAGE_INFO_PATH, "w", encoding="utf-8") as f: - json.dump(default_info, f, indent=2, ensure_ascii=False) - except (json.JSONDecodeError, OSError): - self._packages[_package_name] = _get_default_package_info() - - def _make_app_info(self, data: Dict) -> ApplicationInfo: - ai_data = data.get("applicationInfo", data) - return ApplicationInfo(ai_data) - - def _make_activity_info(self, data: Dict) -> ActivityInfo: - return ActivityInfo(data) - - def _make_service_info(self, data: Dict) -> ServiceInfo: - return ServiceInfo(data) - - def _make_provider_info(self, data: Dict) -> ProviderInfo: - return ProviderInfo(data) - - def getPackageInfo(self, package_name: str, flags: int) -> Optional[PackageInfo]: - if package_name not in self._packages: - return None - raw = self._packages[package_name] - pi = PackageInfo() - pi.packageName = raw.get("packageName", package_name) - pi.versionName = raw.get("versionName", "1.0.0") - pi.versionCode = raw.get("versionCode", 1) - pi.applicationInfo = self._make_app_info(raw) - - if flags & self.GET_ACTIVITIES: - pi.activities = [self._make_activity_info(a) for a in raw.get("activities", [])] - if flags & self.GET_SERVICES: - pi.services = [self._make_service_info(s) for s in raw.get("services", [])] - if flags & self.GET_RECEIVERS: - pi.receivers = [self._make_activity_info(r) for r in raw.get("receivers", [])] - if flags & self.GET_PROVIDERS: - pi.providers = [self._make_provider_info(p) for p in raw.get("providers", [])] - if flags & self.GET_PERMISSIONS: - pi.permissions = [PermissionInfo(p) for p in raw.get("permissions", [])] - - pi.requestedPermissions = raw.get("requestedPermissions", []) - return pi - - def getApplicationInfo(self, package_name: str, flags: int) -> Optional[ApplicationInfo]: - if package_name not in self._packages: - return None - return self._make_app_info(self._packages[package_name]) - - def getActivityInfo(self, component: Any, flags: int) -> Optional[ActivityInfo]: - cn = component - pkg = getattr(cn, "getPackageName", lambda: "")() - cls = getattr(cn, "getClassName", lambda: "")() - if pkg in self._packages: - for a in self._packages[pkg].get("activities", []): - if a.get("name") == cls: - return ActivityInfo(a) - return ActivityInfo({"name": cls, "packageName": pkg}) - - def getServiceInfo(self, component: Any, flags: int) -> Optional[ServiceInfo]: - cn = component - pkg = getattr(cn, "getPackageName", lambda: "")() - cls = getattr(cn, "getClassName", lambda: "")() - if pkg in self._packages: - for s in self._packages[pkg].get("services", []): - if s.get("name") == cls: - return ServiceInfo(s) - return ServiceInfo({"name": cls, "packageName": pkg}) - - def getInstalledPackages(self, flags: int) -> List[PackageInfo]: - result = [] - for pkg_name in self._packages: - pi = self.getPackageInfo(pkg_name, flags) - if pi: - result.append(pi) - return result - - def getInstalledApplications(self, flags: int) -> List[ApplicationInfo]: - result = [] - for pkg_name in self._packages: - ai = self.getApplicationInfo(pkg_name, flags) - if ai: - result.append(ai) - return result - - def queryIntentActivities(self, intent: Any, flags: int) -> List[ResolveInfo]: - """查询匹配 Intent 的 Activity 列表""" - results = [] - action = getattr(intent, "getAction", lambda: None)() - for pkg_name, raw in self._packages.items(): - for a in raw.get("activities", []): - ri = ResolveInfo() - ri.activityInfo = ActivityInfo(a) - ri.priority = 0 - ri.match = 0 - results.append(ri) - return results - - def queryBroadcastReceivers(self, intent: Any, flags: int) -> List[ResolveInfo]: - results = [] - for pkg_name, raw in self._packages.items(): - for r in raw.get("receivers", []): - ri = ResolveInfo() - ri.activityInfo = ActivityInfo(r) - results.append(ri) - return results - - def queryIntentServices(self, intent: Any, flags: int) -> List[ResolveInfo]: - results = [] - for pkg_name, raw in self._packages.items(): - for s in raw.get("services", []): - ri = ResolveInfo() - ri.serviceInfo = ServiceInfo(s) - results.append(ri) - return results - - def queryIntentContentProviders(self, intent: Any, flags: int) -> List[ResolveInfo]: - results = [] - for pkg_name, raw in self._packages.items(): - for p in raw.get("providers", []): - ri = ResolveInfo() - ri.providerInfo = ProviderInfo(p) - results.append(ri) - return results - - def resolveActivity(self, intent: Any, flags: int) -> Optional[ResolveInfo]: - activities = self.queryIntentActivities(intent, flags) - return activities[0] if activities else None - - def resolveService(self, intent: Any, flags: int) -> Optional[ResolveInfo]: - services = self.queryIntentServices(intent, flags) - return services[0] if services else None - - def checkPermission(self, permission: str, package_name: str) -> int: - if package_name in self._packages: - perms = self._packages[package_name].get("requestedPermissions", []) - if permission in perms: - return self.PERMISSION_GRANTED - return self.PERMISSION_DENIED if permission else self.PERMISSION_GRANTED - - def getPermissionInfo(self, name: str, flags: int) -> Optional[PermissionInfo]: - return PermissionInfo({"name": name, "packageName": "android"}) - - def getDefaultActivityIcon(self) -> Any: - return None - - def getApplicationLabel(self, info: ApplicationInfo) -> str: - return info.name.split(".")[-1] if info.name else "" - - def getApplicationIcon(self, package_name: str) -> Any: - return None - - def hasSystemFeature(self, feature: str) -> bool: - for pkg_name, raw in self._packages.items(): - features = raw.get("features", {}) - if feature in features: - return features[feature] - return False - - def getLaunchIntentForPackage(self, package_name: str) -> Any: - from .app import Intent - if package_name in self._packages: - intent = Intent(Intent.ACTION_MAIN) - intent.setPackage(package_name) - return intent - return None - - def getLeanbackLaunchIntentForPackage(self, package_name: str) -> Any: - return self.getLaunchIntentForPackage(package_name) - - def getPackageSizeInfo(self, package_name: str, observer: Any): - pass - - def getPackageGids(self, package_name: str) -> Optional[List[int]]: - return [] - - def getPackageUid(self, package_name: str, flags: int) -> int: - return 10001 - - def getNameForUid(self, uid: int) -> Optional[str]: - return _package_name - - def getPackagesForUid(self, uid: int) -> Optional[List[str]]: - return [_package_name] - - def getResourcesForActivity(self, component: Any) -> Any: - return None - - def getResourcesForApplication(self, app_info: ApplicationInfo) -> Any: - return None - - def isSafeMode(self) -> bool: - return False - - def addPackageToPreferred(self, package_name: str): - pass - - def removePackageFromPreferred(self, package_name: str): - pass - - def getPreferredPackages(self, flags: int) -> List[PackageInfo]: - return [] - - def setApplicationEnabledSetting(self, package_name: str, new_state: int, flags: int): - pass - - def getApplicationEnabledSetting(self, package_name: str) -> int: - return 0 - - def getComponentEnabledSetting(self, component_name: Any) -> int: - return 1 - - def setComponentEnabledSetting(self, component_name: Any, new_state: int, flags: int): - pass - - def clearInstantAppCookie(self): - pass - - def getInstantAppCookie(self) -> bytes: - return b"" - - def getInstantAppCookieMaxSize(self) -> int: - return 4096 - - def getMimeGroup(self, mime_group: str) -> List[str]: - return [] - - def isInstantApp(self) -> bool: - return False - - def isInstantAppPackageName(self, package_name: str) -> bool: - return False - - def getSyntheticAppDetailsActivityEnabled(self, package_name: str) -> int: - return 1 - - def setSyntheticAppDetailsActivityEnabled(self, package_name: str, enabled: int): - pass - - def canRequestPackageInstalls(self) -> bool: - return True - - def currentToCanonicalPackageNames(self, names: List[str]) -> List[str]: - return list(names) - - def canonicalToCurrentPackageNames(self, names: List[str]) -> List[str]: - return list(names) - - def getInstallerPackageName(self, package_name: str) -> Optional[str]: - return None - - def toString(self) -> str: - return f"PackageManager(packages={len(self._packages)})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "PackageManager", - "PackageInfo", - "ApplicationInfo", - "ActivityInfo", - "ServiceInfo", - "ProviderInfo", - "PermissionInfo", - "ResolveInfo", - "PackageItemInfo", - "ComponentInfo", - "Signature", - "PackageStats", -] \ No newline at end of file diff --git a/android_pyapi/android/content_res.py b/android_pyapi/android/content_res.py deleted file mode 100644 index 857b0f2..0000000 --- a/android_pyapi/android/content_res.py +++ /dev/null @@ -1,587 +0,0 @@ -""" -Android content.res 组件 — Resources, AssetManager, Configuration 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import io -import os -import struct -import sys -from pathlib import Path -from typing import Any, Dict, List, Optional, Set, Tuple, Union -from xml.etree import ElementTree - -from .._core import Stub, AndroidOnlyError, android_context, resolve_android_path -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# Resources -# ═══════════════════════════════════════════════════════════════ - -class Resources(Stub): - """Resources — 资源访问""" - - def __init__(self, assets: Optional["AssetManager"] = None): - super().__init__() - self._assets = assets or AssetManager() - self._strings: Dict[str, str] = {} - self._colors: Dict[str, int] = {} - self._dimens: Dict[str, float] = {} - self._drawables: Dict[str, Any] = {} - self._booleans: Dict[str, bool] = {} - self._integers: Dict[str, int] = {} - self._string_arrays: Dict[str, List[str]] = {} - self._int_arrays: Dict[str, List[int]] = {} - self._identifiers: Dict[str, int] = {} - self._next_id = 0x7f010000 - self._configuration = Configuration() - self._display_metrics = DisplayMetrics() - - # ── String ── - - def getString(self, id: Union[int, str], *format_args: Any) -> Optional[str]: - if isinstance(id, str): - return self._strings.get(id) - for key, val in self._strings.items(): - if self._identifiers.get(key) == id: - return val.format(*format_args) if format_args else val - return None - - def getText(self, id: Union[int, str]) -> Optional[str]: - return self.getString(id) - - def getQuantityString(self, id: Union[int, str], quantity: int, *format_args: Any) -> str: - base = self.getString(id) or "" - return base.format(quantity, *format_args) - - # ── Color ── - - def getColor(self, id: Union[int, str], theme: Optional["Resources.Theme"] = None) -> int: - if isinstance(id, str): - return self._colors.get(id, 0xFF000000) - for key, val in self._colors.items(): - if self._identifiers.get(key) == id: - return val - return 0xFF000000 - - def getColorStateList(self, id: Union[int, str], theme: Optional["Resources.Theme"] = None) -> Any: - return self.getColor(id, theme) - - # ── Dimension ── - - def getDimension(self, id: Union[int, str]) -> float: - if isinstance(id, str): - return self._dimens.get(id, 0.0) - for key, val in self._dimens.items(): - if self._identifiers.get(key) == id: - return val - return 0.0 - - def getDimensionPixelSize(self, id: Union[int, str]) -> int: - return int(self.getDimension(id)) - - def getDimensionPixelOffset(self, id: Union[int, str]) -> int: - return int(self.getDimension(id)) - - # ── Drawable ── - - def getDrawable(self, id: Union[int, str], theme: Optional["Resources.Theme"] = None) -> Any: - if isinstance(id, str): - return self._drawables.get(id) - for key, val in self._drawables.items(): - if self._identifiers.get(key) == id: - return val - return None - - # ── Identifier ── - - def getIdentifier(self, name: str, defType: Optional[str] = None, - defPackage: Optional[str] = None) -> int: - key = f"{defPackage}:{defType}/{name}" if defType else name - if key not in self._identifiers: - self._next_id += 1 - self._identifiers[key] = self._next_id - return self._identifiers[key] - - # ── Boolean / Integer ── - - def getBoolean(self, id: Union[int, str]) -> bool: - if isinstance(id, str): - return self._booleans.get(id, False) - for key, val in self._booleans.items(): - if self._identifiers.get(key) == id: - return val - return False - - def getInteger(self, id: Union[int, str]) -> int: - if isinstance(id, str): - return self._integers.get(id, 0) - for key, val in self._integers.items(): - if self._identifiers.get(key) == id: - return val - return 0 - - # ── Arrays ── - - def getStringArray(self, id: Union[int, str]) -> List[str]: - if isinstance(id, str): - return self._string_arrays.get(id, []) - for key, val in self._string_arrays.items(): - if self._identifiers.get(key) == id: - return val - return [] - - def getTextArray(self, id: Union[int, str]) -> List[str]: - return self.getStringArray(id) - - def getIntArray(self, id: Union[int, str]) -> List[int]: - if isinstance(id, str): - return self._int_arrays.get(id, []) - for key, val in self._int_arrays.items(): - if self._identifiers.get(key) == id: - return val - return [] - - # ── Layout / Animation / Xml ── - - def getLayout(self, id: Union[int, str]) -> Any: - return self.getDrawable(id) - - def getAnimation(self, id: Union[int, str]) -> Any: - return self.getDrawable(id) - - def getXml(self, id: Union[int, str]) -> "XmlResourceParser": - return XmlResourceParser() - - # ── Assets ── - - def getAssets(self) -> "AssetManager": - return self._assets - - # ── Config / Metrics ── - - def getConfiguration(self) -> "Configuration": - return self._configuration - - def getDisplayMetrics(self) -> "DisplayMetrics": - return self._display_metrics - - def getSystem(self) -> "Resources": - return self - - # ── Resource names ── - - def getResourceEntryName(self, resid: int) -> str: - for key, val in self._identifiers.items(): - if val == resid: - return key.split("/")[-1] if "/" in key else key - return "unknown" - - def getResourceName(self, resid: int) -> str: - for key, val in self._identifiers.items(): - if val == resid: - return key - return "unknown" - - def getResourcePackageName(self, resid: int) -> str: - for key, val in self._identifiers.items(): - if val == resid: - parts = key.split(":") - return parts[0] if len(parts) > 1 else "" - return "" - - def getResourceTypeName(self, resid: int) -> str: - for key, val in self._identifiers.items(): - if val == resid: - parts = key.split("/") - if len(parts) > 1: - type_part = parts[0] - if ":" in type_part: - return type_part.split(":")[1] - return type_part - return "" - return "" - - # ── Raw resources ── - - def openRawResource(self, id: int) -> io.BytesIO: - return io.BytesIO(b"") - - def openRawResourceFd(self, id: int) -> Any: - return None - - # ── Mutation helpers ── - - def _put_string(self, name: str, value: str): - self._strings[name] = value - - def _put_color(self, name: str, value: int): - self._colors[name] = value - - def _put_dimension(self, name: str, value: float): - self._dimens[name] = value - - def _put_drawable(self, name: str, value: Any): - self._drawables[name] = value - - def _put_boolean(self, name: str, value: bool): - self._booleans[name] = value - - def _put_integer(self, name: str, value: int): - self._integers[name] = value - - # ── Theme ── - - class Theme(Stub): - """Resources.Theme — 主题""" - - def __init__(self, resources: "Resources"): - super().__init__() - self._resources = resources - self._styles: Dict[int, Any] = {} - - def applyStyle(self, resid: int, force: bool = False): - pass - - def setTo(self, other: "Resources.Theme"): - self._styles = dict(other._styles) - - def getDrawable(self, id: int) -> Any: - return self._resources.getDrawable(id) - - def getColor(self, id: int) -> int: - return self._resources.getColor(id) - - def getResources(self) -> "Resources": - return self._resources - - def toString(self) -> str: - return f"Resources.Theme@{hex(id(self))}" - - def hashCode(self) -> int: - return hash(id(self)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# AssetManager -# ═══════════════════════════════════════════════════════════════ - -class AssetManager(Stub): - """AssetManager — 资源资产管理""" - - ACCESS_UNKNOWN = 0 - ACCESS_STREAMING = 1 - ACCESS_BUFFER = 2 - ACCESS_RANDOM = 3 - - def __init__(self): - super().__init__() - self._assets_dir = os.path.join( - os.path.expanduser("~"), "Android-compat", "assets" - ) - os.makedirs(self._assets_dir, exist_ok=True) - self._open: bool = True - - def open(self, fileName: str, access: int = ACCESS_STREAMING) -> io.BytesIO: - path = os.path.join(self._assets_dir, fileName) - if os.path.isfile(path): - with open(path, "rb") as f: - return io.BytesIO(f.read()) - return io.BytesIO(b"") - - def openFd(self, fileName: str) -> Any: - path = os.path.join(self._assets_dir, fileName) - if os.path.isfile(path): - return open(path, "rb") - return None - - def openNonAssetFd(self, fileName: str) -> Any: - return self.openFd(fileName) - - def list(self, path: str = "") -> List[str]: - dir_path = os.path.join(self._assets_dir, path) - if os.path.isdir(dir_path): - return os.listdir(dir_path) - return [] - - def close(self): - self._open = False - - def toString(self) -> str: - return f"AssetManager@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# Configuration -# ═══════════════════════════════════════════════════════════════ - -class Configuration(Stub): - """Configuration — 设备配置""" - - # Color mode constants - COLOR_MODE_DEFAULT = 0 - COLOR_MODE_WIDE_COLOR_GAMUT = 1 - COLOR_MODE_HDR = 2 - - # Density constants - DENSITY_DPI_UNDEFINED = 0 - DENSITY_DPI_LOW = 120 - DENSITY_DPI_MEDIUM = 160 - DENSITY_DPI_HIGH = 240 - DENSITY_DPI_XHIGH = 320 - DENSITY_DPI_XXHIGH = 480 - DENSITY_DPI_XXXHIGH = 640 - DENSITY_DPI_TV = 213 - - # Screen layout size - SCREENLAYOUT_SIZE_SMALL = 1 - SCREENLAYOUT_SIZE_NORMAL = 2 - SCREENLAYOUT_SIZE_LARGE = 3 - SCREENLAYOUT_SIZE_XLARGE = 4 - - # Orientation - ORIENTATION_LANDSCAPE = 2 - ORIENTATION_PORTRAIT = 1 - ORIENTATION_SQUARE = 3 - ORIENTATION_UNDEFINED = 0 - - # UI mode - UI_MODE_TYPE_NORMAL = 1 - UI_MODE_TYPE_DESK = 2 - UI_MODE_TYPE_CAR = 3 - UI_MODE_TYPE_TELEVISION = 4 - UI_MODE_TYPE_APPLIANCE = 5 - UI_MODE_TYPE_WATCH = 6 - UI_MODE_TYPE_VR_HEADSET = 7 - - UI_MODE_NIGHT_UNDEFINED = 0 - UI_MODE_NIGHT_NO = 1 - UI_MODE_NIGHT_YES = 2 - - # Touchscreen - TOUCHSCREEN_UNDEFINED = 0 - TOUCHSCREEN_NOTOUCH = 1 - TOUCHSCREEN_FINGER = 3 - - # Keyboard - KEYBOARD_UNDEFINED = 0 - KEYBOARD_NOKEYS = 1 - KEYBOARD_QWERTY = 2 - KEYBOARD_12KEY = 3 - - # Keyboard hidden - KEYBOARDHIDDEN_UNDEFINED = 0 - KEYBOARDHIDDEN_NO = 1 - KEYBOARDHIDDEN_YES = 2 - - # Hard keyboard hidden - HARDKEYBOARDHIDDEN_UNDEFINED = 0 - HARDKEYBOARDHIDDEN_NO = 1 - HARDKEYBOARDHIDDEN_YES = 2 - - # Navigation - NAVIGATION_UNDEFINED = 0 - NAVIGATION_NONAV = 1 - NAVIGATION_DPAD = 2 - NAVIGATION_TRACKBALL = 3 - NAVIGATION_WHEEL = 4 - - # Navigation hidden - NAVIGATIONHIDDEN_UNDEFINED = 0 - NAVIGATIONHIDDEN_NO = 1 - NAVIGATIONHIDDEN_YES = 2 - - def __init__(self): - super().__init__() - self.fontScale: float = 1.0 - self.mcc: int = 310 - self.mnc: int = 260 - self.locale: Optional[str] = "en-US" - self.screenLayout: int = self.SCREENLAYOUT_SIZE_NORMAL - self.touchscreen: int = self.TOUCHSCREEN_FINGER - self.keyboard: int = self.KEYBOARD_QWERTY - self.keyboardHidden: int = self.KEYBOARDHIDDEN_NO - self.hardKeyboardHidden: int = self.HARDKEYBOARDHIDDEN_NO - self.navigation: int = self.NAVIGATION_NONAV - self.navigationHidden: int = self.NAVIGATIONHIDDEN_NO - self.orientation: int = self.ORIENTATION_PORTRAIT - self.screenWidthDp: int = 360 - self.screenHeightDp: int = 640 - self.smallestScreenWidthDp: int = 360 - self.densityDpi: int = self.DENSITY_DPI_MEDIUM - self.uiMode: int = self.UI_MODE_TYPE_NORMAL | self.UI_MODE_NIGHT_NO - - def toString(self) -> str: - return (f"Configuration{{fontScale={self.fontScale}, mcc={self.mcc}, " - f"mnc={self.mnc}, locale={self.locale}, " - f"screenLayout={self.screenLayout}, orientation={self.orientation}, " - f"screenWidthDp={self.screenWidthDp}, screenHeightDp={self.screenHeightDp}}}") - - -# ═══════════════════════════════════════════════════════════════ -# DisplayMetrics -# ═══════════════════════════════════════════════════════════════ - -class DisplayMetrics(Stub): - """DisplayMetrics — 显示指标""" - - DENSITY_DEFAULT = 160 - DENSITY_LOW = 120 - DENSITY_MEDIUM = 160 - DENSITY_HIGH = 240 - DENSITY_XHIGH = 320 - DENSITY_XXHIGH = 480 - DENSITY_XXXHIGH = 640 - DENSITY_TV = 213 - - def __init__(self): - super().__init__() - self.widthPixels: int = 1080 - self.heightPixels: int = 1920 - self.densityDpi: int = self.DENSITY_DEFAULT - self.density: float = 1.0 - self.scaledDensity: float = 1.0 - self.xdpi: float = 160.0 - self.ydpi: float = 160.0 - self.noncompatWidthPixels: int = 1080 - self.noncompatHeightPixels: int = 1920 - self.noncompatDensityDpi: int = self.DENSITY_DEFAULT - self.noncompatDensity: float = 1.0 - self.noncompatScaledDensity: float = 1.0 - self.noncompatXdpi: float = 160.0 - self.noncompatYdpi: float = 160.0 - self.densityDpi: int = self.DENSITY_DEFAULT - - def toString(self) -> str: - return (f"DisplayMetrics{{width={self.widthPixels}, height={self.heightPixels}, " - f"densityDpi={self.densityDpi}, density={self.density:.2f}}}") - - -# ═══════════════════════════════════════════════════════════════ -# TypedArray -# ═══════════════════════════════════════════════════════════════ - -class TypedArray(Stub): - """TypedArray — 类型化数组""" - - def __init__(self): - super().__init__() - self._data: Dict[int, Any] = {} - - def getString(self, index: int) -> Optional[str]: - val = self._data.get(index) - return str(val) if val is not None else None - - def getInt(self, index: int, defValue: int = 0) -> int: - val = self._data.get(index) - return int(val) if val is not None else defValue - - def getFloat(self, index: int, defValue: float = 0.0) -> float: - val = self._data.get(index) - return float(val) if val is not None else defValue - - def getBoolean(self, index: int, defValue: bool = False) -> bool: - val = self._data.get(index) - return bool(val) if val is not None else defValue - - def getColor(self, index: int, defValue: int = 0) -> int: - val = self._data.get(index) - return int(val) if val is not None else defValue - - def getDimension(self, index: int, defValue: float = 0.0) -> float: - val = self._data.get(index) - return float(val) if val is not None else defValue - - def getDrawable(self, index: int) -> Any: - return self._data.get(index) - - def getResourceId(self, index: int, defValue: int = 0) -> int: - val = self._data.get(index) - return int(val) if val is not None else defValue - - def getText(self, index: int) -> Optional[str]: - return self.getString(index) - - def hasValue(self, index: int) -> bool: - return index in self._data - - def length(self) -> int: - return len(self._data) - - def recycle(self): - self._data.clear() - - def toString(self) -> str: - return f"TypedArray@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# XmlResourceParser -# ═══════════════════════════════════════════════════════════════ - -class XmlResourceParser(Stub): - """XmlResourceParser — XML 资源解析器""" - - START_DOCUMENT = 0 - END_DOCUMENT = 1 - START_TAG = 2 - END_TAG = 3 - TEXT = 4 - - def __init__(self): - super().__init__() - self._root: Optional[ElementTree.Element] = None - self._event_type = self.START_DOCUMENT - - def next(self) -> int: - return self.END_DOCUMENT - - def getEventType(self) -> int: - return self._event_type - - def getName(self) -> Optional[str]: - return None - - def getText(self) -> Optional[str]: - return None - - def getAttributeCount(self) -> int: - return 0 - - def getAttributeName(self, index: int) -> Optional[str]: - return None - - def getAttributeValue(self, index: int) -> Optional[str]: - return None - - def getAttributeValue(self, namespace: Optional[str], name: str) -> Optional[str]: - return None - - def close(self): - pass - - def toString(self) -> str: - return f"XmlResourceParser@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Resources", - "AssetManager", - "Configuration", - "DisplayMetrics", - "TypedArray", - "XmlResourceParser", -] \ No newline at end of file diff --git a/android_pyapi/android/content_sync.py b/android_pyapi/android/content_sync.py deleted file mode 100644 index 5d8e747..0000000 --- a/android_pyapi/android/content_sync.py +++ /dev/null @@ -1,915 +0,0 @@ -""" -Android Content Sync API — SyncManager, SyncAdapter, SyncResult, SyncRequest, ContentResolver, etc. -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import sys -import time -import json -import uuid -import threading -from datetime import datetime, timedelta -from typing import Any, Dict, List, Optional, Set, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -# SYNC_* 常量 -SYNC_EXTRAS_REQUIRE_CHARGING = "require_charging" -SYNC_EXTRAS_UPLOAD = "upload" -SYNC_EXTRAS_MANUAL = "force" -SYNC_EXTRAS_IGNORE_BACKOFF = "ignore_backoff" -SYNC_EXTRAS_DO_NOT_RETRY = "do_not_retry" -SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_local_deletions" -SYNC_EXTRAS_EXPEDITED = "expedited" -SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED = "schedule_as_expedited" -SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "override_too_many_deletions" -SYNC_EXTRAS_ALLOW_METERED = "allow_metered" -SYNC_EXTRAS_INITIALIZE = "initialize" -SYNC_EXTRAS_VIRTUAL_EXTRAS = "virtual_extras" -SYNC_EXTRAS_IGNORE_SETTINGS = "ignore_settings" -SYNC_EXTRAS_IS_EXPEDITED = "is_expedited" -SYNC_EXTRAS_IS_RETRY = "is_retry" - -# SYNC_ERROR 常量 -SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS = 1 -SYNC_ERROR_AUTHENTICATION = 2 -SYNC_ERROR_IO = 3 -SYNC_ERROR_PARSE = 4 -SYNC_ERROR_CONFLICT = 5 -SYNC_ERROR_TOO_MANY_DELETIONS = 6 -SYNC_ERROR_TOO_MANY_RETRIES = 7 -SYNC_ERROR_INTERNAL = 8 -SYNC_ERROR_SYNC_EXTRAS_INVALID = 9 -SYNC_ERROR_REMOTE_SERVER_ERROR = 10 -SYNC_ERROR_MISMATCHED_UID = 11 -SYNC_ERROR_USER_CANCELED = 12 - -# 通知码 -NOTIFY_SYNC_RESULT = 0 -NOTIFY_SETTINGS_CHANGE = 1 -NOTIFY_SYNC_START = 2 -NOTIFY_SYNC_STOP = 3 - -# 同步触发器 -SYNC_TRIGGER_MANUAL = 0 -SYNC_TRIGGER_AUTO = 1 -SYNC_TRIGGER_EXPEDITED = 2 -SYNC_TRIGGER_SCHEDULED = 3 - -# 同步状态 -SYNC_STATUS_PENDING = 0 -SYNC_STATUS_IN_PROGRESS = 1 -SYNC_STATUS_SUCCESS = 2 -SYNC_STATUS_ERROR = 3 -SYNC_STATUS_CANCELLED = 4 - - -# ═══════════════════════════════════════════════════════════════ -# SyncResult -# ═══════════════════════════════════════════════════════════════ - -class SyncResult(Stub): - """SyncResult — 同步结果""" - - def __init__(self): - super().__init__() - self.syncAlreadlyInProgress = False - self.tooManyDeletions = False - self.tooManyRetries = False - self.databaseError = False - self.fullSyncRequested = False - self.partialSyncUnavailable = False - self.moreRecordsToGet = False - self.delayUntil = 0 - self.stats: "SyncStats" = SyncStats() - self._sync_error = 0 - self._sync_exception: Optional[Exception] = None - self._sync_start_time: Optional[float] = None - self._sync_end_time: Optional[float] = None - self._sync_duration_ms: Optional[int] = None - self._num_conflicts = 0 - self._num_auth_exceptions = 0 - self._num_io_exceptions = 0 - self._num_parse_exceptions = 0 - self._num_skipped_entries = 0 - self._num_inserted_entries = 0 - self._num_updated_entries = 0 - self._num_deleted_entries = 0 - self._has_error = False - self._has_hard_error = False - self._has_soft_error = False - - def hasError(self) -> bool: - return ( - self.syncAlreadlyInProgress - or self.tooManyDeletions - or self.tooManyRetries - or self.databaseError - or self.stats.numAuthExceptions > 0 - or self.stats.numIoExceptions > 0 - or self.stats.numParseExceptions > 0 - or self._has_error - ) - - def hasHardError(self) -> bool: - return ( - self.tooManyDeletions - or self.databaseError - or self.stats.numAuthExceptions > 0 - or self.stats.numParseExceptions > 0 - ) - - def hasSoftError(self) -> bool: - return ( - self.syncAlreadlyInProgress - or self.tooManyRetries - or self.stats.numIoExceptions > 0 - ) - - def clear(self) -> None: - self.syncAlreadlyInProgress = False - self.tooManyDeletions = False - self.tooManyRetries = False - self.databaseError = False - self.fullSyncRequested = False - self.partialSyncUnavailable = False - self.moreRecordsToGet = False - self.delayUntil = 0 - self.stats.clear() - self._has_error = False - self._has_hard_error = False - self._has_soft_error = False - - def getStats(self) -> "SyncStats": - return self.stats - - def toString(self) -> str: - return ( - f"SyncResult(hasError={self.hasError()}, " - f"fullSyncRequested={self.fullSyncRequested}, " - f"moreRecordsToGet={self.moreRecordsToGet}, " - f"stats={self.stats})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# SyncStats -# ═══════════════════════════════════════════════════════════════ - -class SyncStats(Stub): - """SyncStats — 同步统计""" - - def __init__(self): - super().__init__() - self.numAuthExceptions = 0 - self.numIoExceptions = 0 - self.numParseExceptions = 0 - self.numConflictDetectedExceptions = 0 - self.numSkippedEntries = 0 - self.numInserts = 0 - self.numUpdates = 0 - self.numDeletes = 0 - self.numEntries = 0 - self._total_entries = 0 - self._total_errors = 0 - self._total_bytes_downloaded = 0 - self._total_bytes_uploaded = 0 - self._sync_duration_ms = 0 - - def clear(self) -> None: - self.numAuthExceptions = 0 - self.numIoExceptions = 0 - self.numParseExceptions = 0 - self.numConflictDetectedExceptions = 0 - self.numSkippedEntries = 0 - self.numInserts = 0 - self.numUpdates = 0 - self.numDeletes = 0 - self.numEntries = 0 - self._total_bytes_downloaded = 0 - self._total_bytes_uploaded = 0 - self._sync_duration_ms = 0 - - def getTotalBytesDownloaded(self) -> int: - return self._total_bytes_downloaded - - def setTotalBytesDownloaded(self, bytes_: int) -> None: - self._total_bytes_downloaded = bytes_ - - def getTotalBytesUploaded(self) -> int: - return self._total_bytes_uploaded - - def setTotalBytesUploaded(self, bytes_: int) -> None: - self._total_bytes_uploaded = bytes_ - - def getSyncDurationMs(self) -> int: - return self._sync_duration_ms - - def setSyncDurationMs(self, ms: int) -> None: - self._sync_duration_ms = ms - - def toString(self) -> str: - return ( - f"SyncStats(inserts={self.numInserts}, updates={self.numUpdates}, " - f"deletes={self.numDeletes}, errors={self._total_errors})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# SyncInfo -# ═══════════════════════════════════════════════════════════════ - -class SyncInfo(Stub): - """SyncInfo — 同步信息""" - - def __init__(self, authority: str, account: Any, userId: int): - super().__init__() - self._authority = authority - self._account = account - self._user_id = userId - self._start_time = time.time() - self._status = SYNC_STATUS_PENDING - self._sync_id = str(uuid.uuid4()) - self._elapsed_time_ms = 0 - self._result: Optional[SyncResult] = None - self._description: Optional[str] = None - self._is_manual = False - self._is_expedited = False - self._trigger = SYNC_TRIGGER_AUTO - self._sync_request: Optional["SyncRequest"] = None - - def getAuthority(self) -> str: - return self._authority - - def getAccount(self) -> Any: - return self._account - - def getUserId(self) -> int: - return self._user_id - - def getStartTime(self) -> float: - return self._start_time - - def getStatus(self) -> int: - return self._status - - def setStatus(self, status: int) -> None: - self._status = status - - def getSyncId(self) -> str: - return self._sync_id - - def getElapsedTimeMs(self) -> int: - return self._elapsed_time_ms - - def getResult(self) -> Optional[SyncResult]: - return self._result - - def isManual(self) -> bool: - return self._is_manual - - def isExpedited(self) -> bool: - return self._is_expedited - - def getTriggerSource(self) -> int: - return self._trigger - - def toString(self) -> str: - return f"SyncInfo(authority={self._authority}, status={self._status})" - - -# ═══════════════════════════════════════════════════════════════ -# SyncStatusObserver -# ═══════════════════════════════════════════════════════════════ - -class SyncStatusObserver(Stub): - """SyncStatusObserver — 同步状态观察者""" - - def __init__(self): - super().__init__() - - def onStatusChanged(self, which: int) -> None: - pass - - def toString(self) -> str: - return "SyncStatusObserver" - - -# ═══════════════════════════════════════════════════════════════ -# SyncAdapter -# ═══════════════════════════════════════════════════════════════ - -class SyncAdapter(Stub): - """SyncAdapter — 同步适配器基类""" - - def __init__(self, context: Any, autoInitialize: bool, allowParallelSyncs: bool): - super().__init__() - self._context = context - self._auto_initialize = autoInitialize - self._allow_parallel_syncs = allowParallelSyncs - self._sync_auto_initialized = False - self._is_initialized = False - self._options: Dict[str, Any] = {} - - def onPerformSync( - self, - account: Any, - extras: Any, - authority: str, - provider: Any, - syncResult: SyncResult, - ) -> None: - pass - - def onSyncCanceled(self) -> None: - pass - - def onSecurityException(self, account: Any, authority: str, exception: Any) -> None: - pass - - def onUnsyncableAccount(self) -> bool: - return True - - def getAutoInitialize(self) -> bool: - return self._auto_initialize - - def getAllowParallelSyncs(self) -> bool: - return self._allow_parallel_syncs - - def isSyncAdapterInitialized(self) -> bool: - return self._is_initialized - - def toString(self) -> str: - return f"SyncAdapter(auto_init={self._auto_initialize}, parallel={self._allow_parallel_syncs})" - - -# ═══════════════════════════════════════════════════════════════ -# SyncRequest -# ═══════════════════════════════════════════════════════════════ - -class SyncRequest(Stub): - """SyncRequest — 同步请求""" - - def __init__(self): - super().__init__() - self._account: Optional[Any] = None - self._authority: Optional[str] = None - self._extras: Dict[str, Any] = {} - self._sync_flexibility_time = 0 - self._sync_run_time = 0 - self._is_expedited = False - self._is_manual = False - self._is_periodic = False - self._ignore_backoff = False - self._ignore_settings = False - self._do_not_retry = False - self._require_charging = False - self._is_initialized = False - self._schedule_as_expedited = False - self._allow_metered = False - self._discard_local_deletions = False - self._override_too_many_deletions = False - - def getAccount(self) -> Optional[Any]: - return self._account - - def getAuthority(self) -> Optional[str]: - return self._authority - - def getExtras(self) -> Dict[str, Any]: - return dict(self._extras) - - def isExpedited(self) -> bool: - return self._is_expedited - - def isPeriodic(self) -> bool: - return self._is_periodic - - def getSyncFlexibilityTime(self) -> int: - return self._sync_flexibility_time - - def getSyncRunTime(self) -> int: - return self._sync_run_time - - def toString(self) -> str: - return f"SyncRequest(authority={self._authority}, periodic={self._is_periodic})" - - # ═══════════════════════════════════════════════════════════ - # SyncRequest.Builder - # ═══════════════════════════════════════════════════════════ - - class Builder(Stub): - """SyncRequest.Builder — 同步请求构建器""" - - def __init__(self): - super().__init__() - self._request = SyncRequest() - - def sync(self) -> "SyncRequest.Builder": - return self - - def setExpedited(self, expedited: bool) -> "SyncRequest.Builder": - self._request._is_expedited = expedited - return self - - def setManual(self, manual: bool) -> "SyncRequest.Builder": - self._request._is_manual = manual - return self - - def setExtras(self, extras: Dict[str, Any]) -> "SyncRequest.Builder": - self._request._extras = dict(extras) - return self - - def setIgnoreBackoff(self, ignoreBackoff: bool) -> "SyncRequest.Builder": - self._request._ignore_backoff = ignoreBackoff - return self - - def setIgnoreSettings(self, ignoreSettings: bool) -> "SyncRequest.Builder": - self._request._ignore_settings = ignoreSettings - return self - - def setDoNotRetry(self, doNotRetry: bool) -> "SyncRequest.Builder": - self._request._do_not_retry = doNotRetry - return self - - def setRequireCharging(self, requireCharging: bool) -> "SyncRequest.Builder": - self._request._require_charging = requireCharging - return self - - def setScheduleAsExpedited(self, expedited: bool) -> "SyncRequest.Builder": - self._request._schedule_as_expedited = expedited - return self - - def setAllowMetered(self, allowMetered: bool) -> "SyncRequest.Builder": - self._request._allow_metered = allowMetered - return self - - def setDiscardLocalDeletions(self, discard: bool) -> "SyncRequest.Builder": - self._request._discard_local_deletions = discard - return self - - def setOverrideTooManyDeletions(self, override: bool) -> "SyncRequest.Builder": - self._request._override_too_many_deletions = override - return self - - def setSyncAdapter(self, account: Any, authority: str) -> "SyncRequest.Builder": - self._request._account = account - self._request._authority = authority - return self - - def setPeriodic(self, pollFrequency: int, flexTime: int = 0) -> "SyncRequest.Builder": - self._request._is_periodic = True - self._request._sync_run_time = time.time() + pollFrequency - self._request._sync_flexibility_time = flexTime - return self - - def build(self) -> SyncRequest: - return self._request - - def toString(self) -> str: - return f"SyncRequest.Builder(existing={self._request})" - - -# ═══════════════════════════════════════════════════════════════ -# SyncConfiguration -# ═══════════════════════════════════════════════════════════════ - -class SyncConfiguration(Stub): - """SyncConfiguration — 同步配置""" - - def __init__(self, authority: str, account: Any, userId: int = 0): - super().__init__() - self._authority = authority - self._account = account - self._user_id = userId - self._sync_enabled = True - self._sync_automatically = True - self._sync_interval = 3600 - self._sync_flex = 300 - self._is_active = False - self._pending = False - self._last_sync_time: Optional[float] = None - self._last_sync_result: Optional[SyncResult] = None - self._delay_until = 0 - self._backoff_time = 0 - self._backoff_delay = 0 - self._authority_settings: Dict[str, Any] = {} - - def getAuthority(self) -> str: - return self._authority - - def getAccount(self) -> Any: - return self._account - - def getUserId(self) -> int: - return self._user_id - - def isSyncEnabled(self) -> bool: - return self._sync_enabled - - def setSyncEnabled(self, enabled: bool) -> None: - self._sync_enabled = enabled - - def isSyncAutomatically(self) -> bool: - return self._sync_automatically - - def setSyncAutomatically(self, auto: bool) -> None: - self._sync_automatically = auto - - def getSyncInterval(self) -> int: - return self._sync_interval - - def setSyncInterval(self, intervalSec: int) -> None: - self._sync_interval = intervalSec - - def getSyncFlex(self) -> int: - return self._sync_flex - - def setSyncFlex(self, flexSec: int) -> None: - self._sync_flex = flexSec - - def getDelayUntil(self) -> int: - return self._delay_until - - def setDelayUntil(self, delayUntil: int) -> None: - self._delay_until = delayUntil - - def getLastSyncTime(self) -> Optional[float]: - return self._last_sync_time - - def setLastSyncTime(self, syncTime: float) -> None: - self._last_sync_time = syncTime - - def isActive(self) -> bool: - return self._is_active - - def isPending(self) -> bool: - return self._pending - - def toString(self) -> str: - return f"SyncConfiguration(authority={self._authority}, enabled={self._sync_enabled})" - - -# ═══════════════════════════════════════════════════════════════ -# SyncAdaptersCache -# ═══════════════════════════════════════════════════════════════ - -class SyncAdaptersCache(Stub): - """SyncAdaptersCache — 同步适配器缓存""" - - def __init__(self): - super().__init__() - self._adapters: Dict[str, SyncAdapter] = {} - self._configs: Dict[str, SyncConfiguration] = {} - self._lock = threading.Lock() - - def put(self, authority: str, adapter: SyncAdapter) -> None: - with self._lock: - self._adapters[authority] = adapter - - def get(self, authority: str) -> Optional[SyncAdapter]: - with self._lock: - return self._adapters.get(authority) - - def remove(self, authority: str) -> None: - with self._lock: - self._adapters.pop(authority, None) - - def containsAuthority(self, authority: str) -> bool: - with self._lock: - return authority in self._adapters - - def getAuthorities(self) -> List[str]: - with self._lock: - return list(self._adapters.keys()) - - def getAdapterCount(self) -> int: - with self._lock: - return len(self._adapters) - - def clear(self) -> None: - with self._lock: - self._adapters.clear() - - def getConfiguration(self, authority: str) -> Optional[SyncConfiguration]: - with self._lock: - return self._configs.get(authority) - - def putConfiguration(self, config: SyncConfiguration) -> None: - with self._lock: - self._configs[config.getAuthority()] = config - - def getAllConfigurations(self) -> List[SyncConfiguration]: - with self._lock: - return list(self._configs.values()) - - def toString(self) -> str: - with self._lock: - return f"SyncAdaptersCache(adapters={len(self._adapters)}, configs={len(self._configs)})" - - -# ═══════════════════════════════════════════════════════════════ -# SyncManager -# ═══════════════════════════════════════════════════════════════ - -class SyncManager(Stub): - """SyncManager — 同步管理器 (桌面存根)""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._lock = threading.Lock() - self._active_syncs: Dict[str, SyncInfo] = {} - self._pending_syncs: List[SyncRequest] = [] - self._observers: List[SyncStatusObserver] = [] - self._adapters_cache = SyncAdaptersCache() - self._sync_automatically = True - self._master_sync_automatically = True - self._sync_enabled = True - self._is_syncable = True - self._sync_backoff_enabled = True - self._worker_thread: Optional[threading.Thread] = None - self._running = True - self._pending_sync_count = 0 - self._total_sync_count = 0 - self._last_sync_time: Optional[float] = None - self._start_worker() - - def _start_worker(self) -> None: - if self._worker_thread is None or not self._worker_thread.is_alive(): - self._running = True - self._worker_thread = threading.Thread(target=self._sync_loop, daemon=True) - self._worker_thread.start() - - def _sync_loop(self) -> None: - while self._running: - with self._lock: - if self._pending_syncs: - req = self._pending_syncs.pop(0) - self._execute_sync(req) - time.sleep(1.0) - - def _execute_sync(self, request: SyncRequest) -> None: - authority = request.getAuthority() - if authority: - info = SyncInfo(authority, request._account, 0) - self._active_syncs[authority] = info - self._total_sync_count += 1 - adapter = self._adapters_cache.get(authority) - if adapter: - result = SyncResult() - try: - adapter.onPerformSync( - request._account, request._extras, authority, None, result - ) - except Exception: - pass - info._result = result - time.sleep(0.1) - self._active_syncs.pop(authority, None) - self._last_sync_time = time.time() - - def getSyncableAccountsForAuthority(self, authority: str) -> List[Any]: - return [] - - def getSyncableAccounts(self) -> List[Any]: - return [] - - def scheduleSync(self, request: SyncRequest) -> None: - with self._lock: - self._pending_syncs.append(request) - self._pending_sync_count = len(self._pending_syncs) - - def schedule(self, account: Any, authority: str, extras: Any) -> None: - req = SyncRequest.Builder().setSyncAdapter(account, authority).build() - self.scheduleSync(req) - - def cancelSync(self, request: SyncRequest) -> None: - with self._lock: - self._pending_syncs = [ - r for r in self._pending_syncs - if r.getAuthority() != request.getAuthority() - ] - self._pending_sync_count = len(self._pending_syncs) - - def cancelSync(self, authority: str = None) -> None: - with self._lock: - if authority: - self._pending_syncs = [ - r for r in self._pending_syncs - if r.getAuthority() != authority - ] - else: - self._pending_syncs.clear() - self._pending_sync_count = len(self._pending_syncs) - - def getCurrentSyncs(self) -> List[SyncInfo]: - with self._lock: - return list(self._active_syncs.values()) - - def getSyncAutomatically(self, account: Any, authority: str) -> bool: - return self._sync_automatically - - def setSyncAutomatically(self, account: Any, authority: str, sync: bool) -> None: - self._sync_automatically = sync - - def getMasterSyncAutomatically(self) -> bool: - return self._master_sync_automatically - - def setMasterSyncAutomatically(self, sync: bool) -> None: - self._master_sync_automatically = sync - - def setIsSyncable(self, account: Any, authority: str, syncable: int) -> None: - self._is_syncable = syncable != 0 - - def getIsSyncable(self, account: Any, authority: str) -> int: - return 1 if self._is_syncable else 0 - - def addStatusChangeListener(self, which: int, observer: SyncStatusObserver) -> None: - if observer not in self._observers: - self._observers.append(observer) - - def removeStatusChangeListener(self, observer: SyncStatusObserver) -> None: - if observer in self._observers: - self._observers.remove(observer) - - def notifyStatusChange(self, which: int) -> None: - for observer in self._observers: - try: - observer.onStatusChanged(which) - except Exception: - pass - - def getSyncAdapterTypes(self) -> List[Any]: - return [] - - def getSyncAdapterPackagesForAuthority(self, authority: str) -> List[str]: - return [] - - def isSyncActive(self, account: Any, authority: str) -> bool: - with self._lock: - return authority in self._active_syncs - - def isSyncPending(self, account: Any, authority: str) -> bool: - with self._lock: - for req in self._pending_syncs: - if req.getAuthority() == authority: - return True - return False - - def getSyncInfo(self, authority: str) -> Optional[SyncInfo]: - with self._lock: - return self._active_syncs.get(authority) - - def getAdaptersCache(self) -> SyncAdaptersCache: - return self._adapters_cache - - def getTotalSyncCount(self) -> int: - return self._total_sync_count - - def getLastSyncTime(self) -> Optional[float]: - return self._last_sync_time - - def getPendingSyncCount(self) -> int: - with self._lock: - return len(self._pending_syncs) - - def toString(self) -> str: - with self._lock: - return ( - f"SyncManager(active={len(self._active_syncs)}, " - f"pending={len(self._pending_syncs)}, " - f"total={self._total_sync_count})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# ContentResolver (Sync 扩展) -# ═══════════════════════════════════════════════════════════════ - -class ContentResolverSync(Stub): - """ContentResolver — 同步扩展方法""" - - SYNC_EXTRAS_REQUIRE_CHARGING = SYNC_EXTRAS_REQUIRE_CHARGING - SYNC_EXTRAS_UPLOAD = SYNC_EXTRAS_UPLOAD - SYNC_EXTRAS_MANUAL = SYNC_EXTRAS_MANUAL - SYNC_EXTRAS_IGNORE_BACKOFF = SYNC_EXTRAS_IGNORE_BACKOFF - SYNC_EXTRAS_DO_NOT_RETRY = SYNC_EXTRAS_DO_NOT_RETRY - SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS - SYNC_EXTRAS_EXPEDITED = SYNC_EXTRAS_EXPEDITED - SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS - SYNC_EXTRAS_ALLOW_METERED = SYNC_EXTRAS_ALLOW_METERED - SYNC_EXTRAS_INITIALIZE = SYNC_EXTRAS_INITIALIZE - SYNC_EXTRAS_IGNORE_SETTINGS = SYNC_EXTRAS_IGNORE_SETTINGS - - NOTIFY_SYNC_RESULT = NOTIFY_SYNC_RESULT - NOTIFY_SETTINGS_CHANGE = NOTIFY_SETTINGS_CHANGE - NOTIFY_SYNC_START = NOTIFY_SYNC_START - NOTIFY_SYNC_STOP = NOTIFY_SYNC_STOP - - def __init__(self): - super().__init__() - - @staticmethod - def requestSync(account: Any, authority: str, extras: Any) -> None: - pass - - @staticmethod - def requestSync(request: SyncRequest) -> None: - pass - - @staticmethod - def cancelSync(request: SyncRequest) -> None: - pass - - @staticmethod - def cancelSync(account: Any, authority: str) -> None: - pass - - @staticmethod - def getSyncAdapterTypes() -> List[Any]: - return [] - - @staticmethod - def getSyncAutomatically(account: Any, authority: str) -> bool: - return True - - @staticmethod - def setSyncAutomatically(account: Any, authority: str, sync: bool) -> None: - pass - - @staticmethod - def getMasterSyncAutomatically() -> bool: - return True - - @staticmethod - def setMasterSyncAutomatically(sync: bool) -> None: - pass - - @staticmethod - def isSyncActive(account: Any, authority: str) -> bool: - return False - - @staticmethod - def isSyncPending(account: Any, authority: str) -> bool: - return False - - @staticmethod - def addStatusChangeListener(which: int, observer: SyncStatusObserver) -> None: - pass - - @staticmethod - def removeStatusChangeListener(observer: SyncStatusObserver) -> None: - pass - - @staticmethod - def validateSyncExtrasBundle(extras: Any) -> None: - pass - - @staticmethod - def getPeriodicSyncs(account: Any, authority: str) -> List[SyncRequest]: - return [] - - @staticmethod - def addPeriodicSync(account: Any, authority: str, extras: Any, pollFrequency: int) -> None: - pass - - @staticmethod - def removePeriodicSync(account: Any, authority: str, extras: Any) -> None: - pass - - @staticmethod - def setIsSyncable(account: Any, authority: str, syncable: int) -> None: - pass - - @staticmethod - def getIsSyncable(account: Any, authority: str) -> int: - return 1 - - @staticmethod - def getSyncAdapterPackagesForAuthority(authority: str) -> List[str]: - return [] - - @staticmethod - def invalidateSegments(authority: str) -> None: - pass - - @staticmethod - def wrapSyncExtras(extras: Any) -> Any: - return extras - - def toString(self) -> str: - return "ContentResolver(Sync)" - -SyncRequest.Builder = SyncRequest.Builder \ No newline at end of file diff --git a/android_pyapi/android/credentials.py b/android_pyapi/android/credentials.py deleted file mode 100644 index 8445bba..0000000 --- a/android_pyapi/android/credentials.py +++ /dev/null @@ -1,418 +0,0 @@ -""" -Android Credentials API — CredentialManager, CreateCredentialRequest, GetCredentialRequest 等 -方法签名完全对齐 Android SDK -""" - -import sys -from typing import Any, Dict, List, Optional, Set, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "CredentialManager", - "CreateCredentialRequest", - "CreateCredentialResponse", - "GetCredentialRequest", - "GetCredentialResponse", - "CredentialDescription", - "CredentialManagerState", - "PrepareGetCredentialResponse", -] - - -# ═══════════════════════════════════════════════════════════════ -# CredentialManager -# ═══════════════════════════════════════════════════════════════ - -class CredentialManager(Stub): - """CredentialManager — 凭据管理器 (桌面存根)""" - - ACTION_CREATE_CREDENTIAL = "android.credentials.action.CREATE_CREDENTIAL" - ACTION_GET_CREDENTIAL = "android.credentials.action.GET_CREDENTIAL" - ACTION_CLEAR_CREDENTIAL = "android.credentials.action.CLEAR_CREDENTIAL" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._descriptions: List["CredentialDescription"] = [] - - def createCredential( - self, - request: "CreateCredentialRequest", - cancellationSignal: Any = None, - executor: Any = None, - callback: Callable = None, - ): - """创建凭据""" - raise AndroidOnlyError("CredentialManager.createCredential() 仅在 Android 设备上可用") - - def getCredential( - self, - request: "GetCredentialRequest", - cancellationSignal: Any = None, - executor: Any = None, - callback: Callable = None, - ): - """获取凭据""" - raise AndroidOnlyError("CredentialManager.getCredential() 仅在 Android 设备上可用") - - def clearCredentialState( - self, - request: Any = None, - cancellationSignal: Any = None, - executor: Any = None, - callback: Callable = None, - ): - """清除凭据状态""" - raise AndroidOnlyError("CredentialManager.clearCredentialState() 仅在 Android 设备上可用") - - def registerCredentialDescription( - self, - credentialDescription: "CredentialDescription", - cancellationSignal: Any = None, - executor: Any = None, - callback: Callable = None, - ): - """注册凭据描述""" - if credentialDescription not in self._descriptions: - self._descriptions.append(credentialDescription) - - def unregisterCredentialDescription( - self, - credentialDescription: "CredentialDescription", - ): - """注销凭据描述""" - if credentialDescription in self._descriptions: - self._descriptions.remove(credentialDescription) - - def getCredentialManagerState(self) -> "CredentialManagerState": - """获取凭据管理器状态""" - return CredentialManagerState() - - def toString(self) -> str: - return f"CredentialManager@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# CreateCredentialRequest -# ═══════════════════════════════════════════════════════════════ - -class CreateCredentialRequest(Stub): - """CreateCredentialRequest — 创建凭据请求""" - - class Builder(Stub): - """CreateCredentialRequest.Builder — 创建凭据请求构建器""" - - def __init__(self, credentialType: str = ""): - super().__init__() - self._credential_type = credentialType - self._credential_data = None - self._candidate_query_data = None - self._always_send_app_uri = False - self._origin = "" - - def setCredentialData(self, data: Any) -> "CreateCredentialRequest.Builder": - self._credential_data = data - return self - - def setCandidateQueryData( - self, data: Any - ) -> "CreateCredentialRequest.Builder": - self._candidate_query_data = data - return self - - def setAlwaysSendAppUri( - self, alwaysSend: bool - ) -> "CreateCredentialRequest.Builder": - self._always_send_app_uri = alwaysSend - return self - - def setOrigin(self, origin: str) -> "CreateCredentialRequest.Builder": - self._origin = origin - return self - - def build(self) -> "CreateCredentialRequest": - req = CreateCredentialRequest.__new__(CreateCredentialRequest) - req._credential_type = self._credential_type - req._credential_data = self._credential_data - req._candidate_query_data = self._candidate_query_data - req._always_send_app_uri = self._always_send_app_uri - req._origin = self._origin - return req - - def toString(self) -> str: - return f"CreateCredentialRequest.Builder(type={self._credential_type})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def __init__(self): - super().__init__() - self._credential_type = "" - self._credential_data = None - self._candidate_query_data = None - self._always_send_app_uri = False - self._origin = "" - - def getCredentialData(self) -> Optional[Any]: - return self._credential_data - - def getCredentialType(self) -> str: - return self._credential_type - - def getCandidateQueryData(self) -> Optional[Any]: - return self._candidate_query_data - - def alwaysSendAppUri(self) -> bool: - return self._always_send_app_uri - - def getOrigin(self) -> str: - return self._origin - - def toString(self) -> str: - return f"CreateCredentialRequest(type={self._credential_type})" - - def equals(self, other) -> bool: - if not isinstance(other, CreateCredentialRequest): - return False - return self._credential_type == other._credential_type - - def hashCode(self) -> int: - return hash(self._credential_type) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# CreateCredentialResponse -# ═══════════════════════════════════════════════════════════════ - -class CreateCredentialResponse(Stub): - """CreateCredentialResponse — 创建凭据响应""" - - def __init__(self): - super().__init__() - self._credential_data = None - self._credential_data_result = None - - def getCredentialData(self) -> Optional[Any]: - return self._credential_data - - def getCredentialDataResult(self) -> Optional[Any]: - return self._credential_data_result - - def toString(self) -> str: - return f"CreateCredentialResponse@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# GetCredentialRequest -# ═══════════════════════════════════════════════════════════════ - -class GetCredentialRequest(Stub): - """GetCredentialRequest — 获取凭据请求""" - - class Builder(Stub): - """GetCredentialRequest.Builder — 获取凭据请求构建器""" - - def __init__(self): - super().__init__() - self._credential_data = None - self._origin = "" - self._always_send_app_uri = False - self._candidate_query_data = None - - def setCredentialData( - self, data: Any - ) -> "GetCredentialRequest.Builder": - self._credential_data = data - return self - - def setOrigin(self, origin: str) -> "GetCredentialRequest.Builder": - self._origin = origin - return self - - def setAlwaysSendAppUri( - self, alwaysSend: bool - ) -> "GetCredentialRequest.Builder": - self._always_send_app_uri = alwaysSend - return self - - def setCandidateQueryData( - self, data: Any - ) -> "GetCredentialRequest.Builder": - self._candidate_query_data = data - return self - - def build(self) -> "GetCredentialRequest": - req = GetCredentialRequest.__new__(GetCredentialRequest) - req._credential_data = self._credential_data - req._origin = self._origin - req._always_send_app_uri = self._always_send_app_uri - req._candidate_query_data = self._candidate_query_data - return req - - def toString(self) -> str: - return f"GetCredentialRequest.Builder@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def __init__(self): - super().__init__() - self._credential_data = None - self._origin = "" - self._always_send_app_uri = False - self._candidate_query_data = None - - def getCredentialData(self) -> Optional[Any]: - return self._credential_data - - def getOrigin(self) -> str: - return self._origin - - def alwaysSendAppUri(self) -> bool: - return self._always_send_app_uri - - def getCandidateQueryData(self) -> Optional[Any]: - return self._candidate_query_data - - def toString(self) -> str: - return f"GetCredentialRequest@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# GetCredentialResponse -# ═══════════════════════════════════════════════════════════════ - -class GetCredentialResponse(Stub): - """GetCredentialResponse — 获取凭据响应""" - - def __init__(self): - super().__init__() - self._credential_data_results: List[Any] = [] - - def getCredentialDataResults(self) -> List[Any]: - return list(self._credential_data_results) - - def toString(self) -> str: - return f"GetCredentialResponse(results={len(self._credential_data_results)})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# CredentialDescription -# ═══════════════════════════════════════════════════════════════ - -class CredentialDescription(Stub): - """CredentialDescription — 凭据描述""" - - def __init__(self, credentialType: str = "", origin: str = ""): - super().__init__() - self._credential_type = credentialType - self._origin = origin - - def getCredentialType(self) -> str: - return self._credential_type - - def getOrigin(self) -> str: - return self._origin - - def toString(self) -> str: - return f"CredentialDescription(type={self._credential_type})" - - def equals(self, other) -> bool: - if not isinstance(other, CredentialDescription): - return False - return (self._credential_type == other._credential_type - and self._origin == other._origin) - - def hashCode(self) -> int: - return hash((self._credential_type, self._origin)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# CredentialManagerState -# ═══════════════════════════════════════════════════════════════ - -class CredentialManagerState(Stub): - """CredentialManagerState — 凭据管理器状态""" - - def __init__(self): - super().__init__() - self._registered_descriptions: List["CredentialDescription"] = [] - self._is_enabled = True - - def getRegisteredCredentialDescriptions(self) -> List["CredentialDescription"]: - return list(self._registered_descriptions) - - def isEnabled(self) -> bool: - return self._is_enabled - - def toString(self) -> str: - return f"CredentialManagerState(enabled={self._is_enabled})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# PrepareGetCredentialResponse -# ═══════════════════════════════════════════════════════════════ - -class PrepareGetCredentialResponse(Stub): - """PrepareGetCredentialResponse — 预备获取凭据响应""" - - def __init__(self): - super().__init__() - self._has_results = False - self._pending_credential = None - - def hasResults(self) -> bool: - return self._has_results - - def getPendingGetCredentialResponse(self) -> Optional["GetCredentialResponse"]: - return self._pending_credential - - def toString(self) -> str: - return f"PrepareGetCredentialResponse(hasResults={self._has_results})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/database.py b/android_pyapi/android/database.py deleted file mode 100644 index 807ec00..0000000 --- a/android_pyapi/android/database.py +++ /dev/null @@ -1,1092 +0,0 @@ -""" -Android Database 组件 — Cursor, MatrixCursor, ContentObserver 等 -方法签名完全对齐 Android SDK -""" - -import os -import sys -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) - - -# ═══════════════════════════════════════════════════════════════ -# SQLException hierarchy -# ═══════════════════════════════════════════════════════════════ - -class SQLException(RuntimeError): - """SQLException — 数据库异常基类""" - - def __init__(self, message: str = ""): - super().__init__(message) - self._error_message = message - - def getMessage(self) -> str: - return self._error_message - - def toString(self) -> str: - return f"SQLException: {self._error_message}" - - -class CursorIndexOutOfBoundsException(SQLException): - """CursorIndexOutOfBoundsException — 游标越界""" - - def __init__(self, message: str = ""): - super().__init__(message) - - -# ═══════════════════════════════════════════════════════════════ -# ContentObserver -# ═══════════════════════════════════════════════════════════════ - -class ContentObserver(Stub): - """ContentObserver — 内容观察者""" - - def __init__(self, handler: Any = None): - super().__init__() - self._handler = handler - self._notify_for_descendants = False - - def onChange(self, self_change: bool = True): - pass - - def onChange(self, self_change: bool, uri: Any = None): # type: ignore - pass - - def deliverSelfNotifications(self) -> bool: - return True - - def getSelfChangeNotify(self) -> bool: - return True - - def dispatchChange(self, self_change: bool, uri: Any = None): - self.onChange(self_change, uri) - - -# ═══════════════════════════════════════════════════════════════ -# DataSetObserver -# ═══════════════════════════════════════════════════════════════ - -class DataSetObserver(Stub): - """DataSetObserver — 数据集观察者""" - - def onChanged(self): - pass - - def onInvalidated(self): - pass - - -# ═══════════════════════════════════════════════════════════════ -# ContentObservable -# ═══════════════════════════════════════════════════════════════ - -class ContentObservable(Stub): - """ContentObservable — 内容可观察对象""" - - def __init__(self): - super().__init__() - self._observers: List[ContentObserver] = [] - - def registerObserver(self, observer: ContentObserver): - if observer not in self._observers: - self._observers.append(observer) - - def unregisterObserver(self, observer: ContentObserver): - self._observers = [o for o in self._observers if o is not observer] - - def dispatchChange(self, self_change: bool, uri: Any = None): - for observer in list(self._observers): - observer.dispatchChange(self_change, uri) - - def notifyChange(self, self_change: bool): - for observer in list(self._observers): - observer.onChange(self_change) - - -# ═══════════════════════════════════════════════════════════════ -# DataSetObservable -# ═══════════════════════════════════════════════════════════════ - -class DataSetObservable(Stub): - """DataSetObservable — 数据集可观察对象""" - - def __init__(self): - super().__init__() - self._observers: List[DataSetObserver] = [] - - def registerObserver(self, observer: DataSetObserver): - if observer not in self._observers: - self._observers.append(observer) - - def unregisterObserver(self, observer: DataSetObserver): - self._observers = [o for o in self._observers if o is not observer] - - def notifyChanged(self): - for observer in list(self._observers): - observer.onChanged() - - def notifyInvalidated(self): - for observer in list(self._observers): - observer.onInvalidated() - - -# ═══════════════════════════════════════════════════════════════ -# CharArrayBuffer -# ═══════════════════════════════════════════════════════════════ - -class CharArrayBuffer(Stub): - """CharArrayBuffer — 字符数组缓冲区""" - - def __init__(self, size: int = 0): - super().__init__() - self.data: List[str] = [""] * size - - -# ═══════════════════════════════════════════════════════════════ -# DatabaseErrorHandler -# ═══════════════════════════════════════════════════════════════ - -class DatabaseErrorHandler(Stub): - """DatabaseErrorHandler — 数据库错误处理器""" - - def onCorruption(self, db_obj: Any): - pass - - -class DefaultDatabaseErrorHandler(DatabaseErrorHandler): - """DefaultDatabaseErrorHandler — 默认数据库错误处理器""" - - def onCorruption(self, db_obj: Any): - import sys - sys.stderr.write(f"[DatabaseError] Corruption detected for {db_obj}\n") - - -# ═══════════════════════════════════════════════════════════════ -# CursorWindow -# ═══════════════════════════════════════════════════════════════ - -class CursorWindow(Stub): - """CursorWindow — 游标窗口""" - - def __init__(self, name: str = "unnamed"): - super().__init__() - self._name = name - self._start_position: int = 0 - self._num_rows: int = 0 - self._num_columns: int = 0 - self._data: List[List[Optional[Any]]] = [] - - def setStartPosition(self, pos: int): - self._start_position = pos - - def getStartPosition(self) -> int: - return self._start_position - - def allocRow(self) -> bool: - self._data.append([None] * self._num_columns) - self._num_rows = len(self._data) - return True - - def freeRow(self, row: int): - if 0 <= row < len(self._data): - self._data[row] = [None] * self._num_columns - - def freeLastRow(self): - if self._data: - self._data.pop() - self._num_rows = len(self._data) - - def getNumRows(self) -> int: - return self._num_rows - - def setNumColumns(self, num_columns: int): - self._num_columns = num_columns - - def getNumColumns(self) -> int: - return self._num_columns - - def writeString(self, value: str, row: int, col: int): - while len(self._data) <= row: - self._data.append([None] * self._num_columns) - while len(self._data[row]) <= col: - self._data[row].append(None) - self._data[row][col] = value - - def writeLong(self, value: int, row: int, col: int): - while len(self._data) <= row: - self._data.append([None] * self._num_columns) - while len(self._data[row]) <= col: - self._data[row].append(None) - self._data[row][col] = value - - def writeDouble(self, value: float, row: int, col: int): - while len(self._data) <= row: - self._data.append([None] * self._num_columns) - while len(self._data[row]) <= col: - self._data[row].append(None) - self._data[row][col] = value - - def writeBlob(self, value: bytes, row: int, col: int): - while len(self._data) <= row: - self._data.append([None] * self._num_columns) - while len(self._data[row]) <= col: - self._data[row].append(None) - self._data[row][col] = value - - def writeNull(self, row: int, col: int): - while len(self._data) <= row: - self._data.append([None] * self._num_columns) - while len(self._data[row]) <= col: - self._data[row].append(None) - self._data[row][col] = None - - def getString(self, row: int, col: int) -> Optional[str]: - try: - v = self._data[row][col] - return str(v) if v is not None else None - except (IndexError, TypeError): - return None - - def getLong(self, row: int, col: int) -> int: - try: - v = self._data[row][col] - return int(v) if v is not None else 0 - except (IndexError, ValueError, TypeError): - return 0 - - def getDouble(self, row: int, col: int) -> float: - try: - v = self._data[row][col] - return float(v) if v is not None else 0.0 - except (IndexError, ValueError, TypeError): - return 0.0 - - def getBlob(self, row: int, col: int) -> Optional[bytes]: - try: - v = self._data[row][col] - if isinstance(v, bytes): - return v - if isinstance(v, str): - return v.encode("utf-8") - return None - except IndexError: - return None - - def isNull(self, row: int, col: int) -> bool: - try: - return self._data[row][col] is None - except IndexError: - return True - - def clear(self): - self._data.clear() - self._num_rows = 0 - - def toString(self) -> str: - return f"CursorWindow(name={self._name}, rows={self._num_rows}, cols={self._num_columns})" - - -# ═══════════════════════════════════════════════════════════════ -# Cursor interface -# ═══════════════════════════════════════════════════════════════ - -class Cursor(Stub): - """Cursor — 游标接口""" - - FIELD_TYPE_NULL = 0 - FIELD_TYPE_INTEGER = 1 - FIELD_TYPE_FLOAT = 2 - FIELD_TYPE_STRING = 3 - FIELD_TYPE_BLOB = 4 - - def __init__(self): - super().__init__() - self._pos: int = -1 - self._closed: bool = False - - def getCount(self) -> int: - return 0 - - def getPosition(self) -> int: - return self._pos - - def moveToFirst(self) -> bool: - return self.moveToPosition(0) - - def moveToLast(self) -> bool: - return self.moveToPosition(self.getCount() - 1) - - def moveToNext(self) -> bool: - return self.moveToPosition(self._pos + 1) - - def moveToPrevious(self) -> bool: - return self.moveToPosition(self._pos - 1) - - def move(self, offset: int) -> bool: - return self.moveToPosition(self._pos + offset) - - def moveToPosition(self, position: int) -> bool: - count = self.getCount() - if position >= count: - self._pos = count - return False - if position < -1: - self._pos = -1 - return False - self._pos = position - return True - - def isFirst(self) -> bool: - return self._pos == 0 and self.getCount() > 0 - - def isLast(self) -> bool: - count = self.getCount() - return count > 0 and self._pos == count - 1 - - def isBeforeFirst(self) -> bool: - if self.getCount() == 0: - return True - return self._pos < 0 - - def isAfterLast(self) -> bool: - if self.getCount() == 0: - return True - return self._pos >= self.getCount() - - def getColumnIndex(self, column_name: str) -> int: - names = self.getColumnNames() - try: - return names.index(column_name) - except ValueError: - return -1 - - def getColumnIndexOrThrow(self, column_name: str) -> int: - idx = self.getColumnIndex(column_name) - if idx < 0: - raise IllegalArgumentException(f"Column '{column_name}' does not exist") - return idx - - def getColumnName(self, column_index: int) -> str: - names = self.getColumnNames() - if 0 <= column_index < len(names): - return names[column_index] - return "" - - def getColumnNames(self) -> List[str]: - return [] - - def getColumnCount(self) -> int: - return len(self.getColumnNames()) - - def getType(self, column_index: int) -> int: - return self.FIELD_TYPE_NULL - - def getString(self, column_index: int) -> Optional[str]: - return None - - def getInt(self, column_index: int) -> int: - return 0 - - def getLong(self, column_index: int) -> int: - return 0 - - def getFloat(self, column_index: int) -> float: - return 0.0 - - def getDouble(self, column_index: int) -> float: - return 0.0 - - def getShort(self, column_index: int) -> int: - return 0 - - def getBlob(self, column_index: int) -> Optional[bytes]: - return None - - def getBytes(self, column_index: int) -> Optional[bytes]: - return self.getBlob(column_index) - - def close(self): - self._closed = True - - def isClosed(self) -> bool: - return self._closed - - def requery(self) -> bool: - return False - - def deactivate(self): - pass - - def registerContentObserver(self, observer: ContentObserver): - pass - - def unregisterContentObserver(self, observer: ContentObserver): - pass - - def registerDataSetObserver(self, observer: DataSetObserver): - pass - - def unregisterDataSetObserver(self, observer: DataSetObserver): - pass - - def getWantsAllOnMoveCalls(self) -> bool: - return False - - def getExtras(self) -> Any: - return None - - def respond(self, extras: Any) -> Any: - return None - - def setNotificationUri(self, cr: Any, uri: Any): - pass - - def getNotificationUri(self) -> Any: - return None - - def fillWindow(self, pos: int, window: CursorWindow): - pass - - def copyStringToBuffer(self, column_index: int, buffer: CharArrayBuffer): - val = self.getString(column_index) - if val is not None: - buffer.data = list(val) - - def isNull(self, column_index: int) -> bool: - return self.getType(column_index) == self.FIELD_TYPE_NULL - - -# ═══════════════════════════════════════════════════════════════ -# AbstractCursor -# ═══════════════════════════════════════════════════════════════ - -class AbstractCursor(Cursor): - """AbstractCursor — 游标抽象基类""" - - def __init__(self): - super().__init__() - self._content_observable = ContentObservable() - self._data_set_observable = DataSetObservable() - self._notification_uri: Any = None - self._self_observer: Optional[ContentObserver] = None - - def getCount(self) -> int: - return 0 - - def getColumnNames(self) -> List[str]: - return [] - - def registerContentObserver(self, observer: ContentObserver): - self._content_observable.registerObserver(observer) - - def unregisterContentObserver(self, observer: ContentObserver): - self._content_observable.unregisterObserver(observer) - - def registerDataSetObserver(self, observer: DataSetObserver): - self._data_set_observable.registerObserver(observer) - - def unregisterDataSetObserver(self, observer: DataSetObserver): - self._data_set_observable.unregisterObserver(observer) - - def setNotificationUri(self, cr: Any, uri: Any): - self._notification_uri = uri - - def getNotificationUri(self) -> Any: - return self._notification_uri - - def onChange(self, self_change: bool): - self._content_observable.dispatchChange(self_change) - - def getWantsAllOnMoveCalls(self) -> bool: - return False - - def getExtras(self) -> Any: - return None - - def respond(self, extras: Any) -> Any: - return None - - def fillWindow(self, pos: int, window: CursorWindow): - pass - - -# ═══════════════════════════════════════════════════════════════ -# AbstractWindowedCursor -# ═══════════════════════════════════════════════════════════════ - -class AbstractWindowedCursor(AbstractCursor): - """AbstractWindowedCursor — 带窗口的游标""" - - def __init__(self): - super().__init__() - self._window: Optional[CursorWindow] = None - - def getWindow(self) -> Optional[CursorWindow]: - return self._window - - def setWindow(self, window: CursorWindow): - self._window = window - - def hasWindow(self) -> bool: - return self._window is not None - - def clearBlobCache(self): - pass - - def fillWindow(self, pos: int, window: CursorWindow): - if self._window: - self._window.clear() - - def getString(self, column_index: int) -> Optional[str]: - if self._window and self._pos >= 0: - return self._window.getString(self._pos, column_index) - return None - - def getInt(self, column_index: int) -> int: - if self._window and self._pos >= 0: - return self._window.getLong(self._pos, column_index) - return 0 - - def getLong(self, column_index: int) -> int: - if self._window and self._pos >= 0: - return self._window.getLong(self._pos, column_index) - return 0 - - def getFloat(self, column_index: int) -> float: - if self._window and self._pos >= 0: - return self._window.getDouble(self._pos, column_index) - return 0.0 - - def getDouble(self, column_index: int) -> float: - if self._window and self._pos >= 0: - return self._window.getDouble(self._pos, column_index) - return 0.0 - - def getBlob(self, column_index: int) -> Optional[bytes]: - if self._window and self._pos >= 0: - return self._window.getBlob(self._pos, column_index) - return None - - def isNull(self, column_index: int) -> bool: - if self._window and self._pos >= 0: - return self._window.isNull(self._pos, column_index) - return True - - -# ═══════════════════════════════════════════════════════════════ -# CursorWrapper -# ═══════════════════════════════════════════════════════════════ - -class CursorWrapper(Cursor): - """CursorWrapper — 游标包装器""" - - def __init__(self, cursor: Cursor): - super().__init__() - self._wrapped_cursor = cursor - - def getWrappedCursor(self) -> Cursor: - return self._wrapped_cursor - - def getCount(self) -> int: - return self._wrapped_cursor.getCount() - - def getPosition(self) -> int: - return self._wrapped_cursor.getPosition() - - def moveToFirst(self) -> bool: - return self._wrapped_cursor.moveToFirst() - - def moveToLast(self) -> bool: - return self._wrapped_cursor.moveToLast() - - def moveToNext(self) -> bool: - return self._wrapped_cursor.moveToNext() - - def moveToPrevious(self) -> bool: - return self._wrapped_cursor.moveToPrevious() - - def move(self, offset: int) -> bool: - return self._wrapped_cursor.move(offset) - - def moveToPosition(self, position: int) -> bool: - return self._wrapped_cursor.moveToPosition(position) - - def getColumnNames(self) -> List[str]: - return self._wrapped_cursor.getColumnNames() - - def getColumnIndex(self, column_name: str) -> int: - return self._wrapped_cursor.getColumnIndex(column_name) - - def getColumnIndexOrThrow(self, column_name: str) -> int: - return self._wrapped_cursor.getColumnIndexOrThrow(column_name) - - def getString(self, column_index: int) -> Optional[str]: - return self._wrapped_cursor.getString(column_index) - - def getInt(self, column_index: int) -> int: - return self._wrapped_cursor.getInt(column_index) - - def getLong(self, column_index: int) -> int: - return self._wrapped_cursor.getLong(column_index) - - def getFloat(self, column_index: int) -> float: - return self._wrapped_cursor.getFloat(column_index) - - def getDouble(self, column_index: int) -> float: - return self._wrapped_cursor.getDouble(column_index) - - def getShort(self, column_index: int) -> int: - return self._wrapped_cursor.getShort(column_index) - - def getBlob(self, column_index: int) -> Optional[bytes]: - return self._wrapped_cursor.getBlob(column_index) - - def close(self): - self._wrapped_cursor.close() - - def isClosed(self) -> bool: - return self._wrapped_cursor.isClosed() - - def registerContentObserver(self, observer: ContentObserver): - self._wrapped_cursor.registerContentObserver(observer) - - def unregisterContentObserver(self, observer: ContentObserver): - self._wrapped_cursor.unregisterContentObserver(observer) - - def registerDataSetObserver(self, observer: DataSetObserver): - self._wrapped_cursor.registerDataSetObserver(observer) - - def unregisterDataSetObserver(self, observer: DataSetObserver): - self._wrapped_cursor.unregisterDataSetObserver(observer) - - def getWantsAllOnMoveCalls(self) -> bool: - return self._wrapped_cursor.getWantsAllOnMoveCalls() - - -# ═══════════════════════════════════════════════════════════════ -# CrossProcessCursor / CrossProcessCursorWrapper -# ═══════════════════════════════════════════════════════════════ - -class CrossProcessCursor(Cursor): - """CrossProcessCursor — 跨进程游标""" - - def fillWindow(self, pos: int, window: CursorWindow): - pass - - def getWindow(self) -> Optional[CursorWindow]: - return None - - def onMove(self, old_position: int, new_position: int) -> bool: - return True - - -class CrossProcessCursorWrapper(CursorWrapper): - """CrossProcessCursorWrapper — 跨进程游标包装器""" - - def __init__(self, cursor: Cursor): - super().__init__(cursor) - self._cursor = cursor - - def fillWindow(self, pos: int, window: CursorWindow): - if isinstance(self._cursor, CrossProcessCursor): - self._cursor.fillWindow(pos, window) - - def getWindow(self) -> Optional[CursorWindow]: - if isinstance(self._cursor, CrossProcessCursor): - return self._cursor.getWindow() - return None - - def onMove(self, old_position: int, new_position: int) -> bool: - if isinstance(self._cursor, CrossProcessCursor): - return self._cursor.onMove(old_position, new_position) - return True - - -# ═══════════════════════════════════════════════════════════════ -# MatrixCursor -# ═══════════════════════════════════════════════════════════════ - -class MatrixCursor(AbstractCursor): - """MatrixCursor — 内存矩阵游标""" - - class RowBuilder(Stub): - """RowBuilder — 行构建器""" - - def __init__(self, cursor: "MatrixCursor", row_index: int): - super().__init__() - self._cursor = cursor - self._row_index = row_index - - def add(self, column_value: Any) -> "MatrixCursor.RowBuilder": - self._cursor._add_row_value(self._row_index, column_value) - return self - - def __init__(self, column_names: List[str], initial_capacity: int = 0): - super().__init__() - self._column_names = list(column_names) - self._rows: List[List[Optional[Any]]] = [] - self._num_columns = len(column_names) - - def _add_row_value(self, row_index: int, value: Any): - while len(self._rows) <= row_index: - self._rows.append([]) - self._rows[row_index].append(value) - - def addRow(self, values: Union[List[Any], Tuple[Any, ...]]) -> None: - row = list(values) + [None] * (self._num_columns - len(values)) - self._rows.append(row[:self._num_columns]) - - def newRow(self) -> RowBuilder: - row_index = len(self._rows) - self._rows.append([]) - return MatrixCursor.RowBuilder(self, row_index) - - def getCount(self) -> int: - return len(self._rows) - - def getColumnNames(self) -> List[str]: - return list(self._column_names) - - def getString(self, column_index: int) -> Optional[str]: - if self._pos < 0 or self._pos >= len(self._rows): - return None - if column_index < 0 or column_index >= len(self._rows[self._pos]): - return None - v = self._rows[self._pos][column_index] - return str(v) if v is not None else None - - def getInt(self, column_index: int) -> int: - v = self.getString(column_index) - if v is None: - return 0 - try: - return int(float(v)) - except (ValueError, TypeError): - return 0 - - def getLong(self, column_index: int) -> int: - return self.getInt(column_index) - - def getShort(self, column_index: int) -> int: - return self.getInt(column_index) - - def getFloat(self, column_index: int) -> float: - v = self.getString(column_index) - if v is None: - return 0.0 - try: - return float(v) - except (ValueError, TypeError): - return 0.0 - - def getDouble(self, column_index: int) -> float: - return self.getFloat(column_index) - - def getBlob(self, column_index: int) -> Optional[bytes]: - if self._pos < 0 or self._pos >= len(self._rows): - return None - if column_index < 0 or column_index >= len(self._rows[self._pos]): - return None - v = self._rows[self._pos][column_index] - if isinstance(v, bytes): - return v - if isinstance(v, str): - return v.encode("utf-8") - return None - - def getType(self, column_index: int) -> int: - v = None - if 0 <= self._pos < len(self._rows): - row = self._rows[self._pos] - if 0 <= column_index < len(row): - v = row[column_index] - if v is None: - return Cursor.FIELD_TYPE_NULL - if isinstance(v, int): - return Cursor.FIELD_TYPE_INTEGER - if isinstance(v, float): - return Cursor.FIELD_TYPE_FLOAT - if isinstance(v, bytes): - return Cursor.FIELD_TYPE_BLOB - return Cursor.FIELD_TYPE_STRING - - def isNull(self, column_index: int) -> bool: - return self.getType(column_index) == Cursor.FIELD_TYPE_NULL - - def toString(self) -> str: - return f"MatrixCursor(cols={self._column_names}, rows={len(self._rows)})" - - -# ═══════════════════════════════════════════════════════════════ -# MergeCursor -# ═══════════════════════════════════════════════════════════════ - -class MergeCursor(AbstractCursor): - """MergeCursor — 合并多个游标""" - - def __init__(self, cursors: List[Cursor]): - super().__init__() - self._cursors = cursors - self._cursor_index = 0 - self._current_cursor: Optional[Cursor] = None - - def getCount(self) -> int: - return sum(c.getCount() for c in self._cursors) - - def getColumnNames(self) -> List[str]: - if self._cursors: - return self._cursors[0].getColumnNames() - return [] - - def moveToPosition(self, position: int) -> bool: - if position < -1: - return False - if position >= self.getCount(): - return False - offset = 0 - for i, c in enumerate(self._cursors): - count = c.getCount() - if position < offset + count: - self._cursor_index = i - self._current_cursor = c - return c.moveToPosition(position - offset) - offset += count - return False - - def getString(self, column_index: int) -> Optional[str]: - if self._current_cursor and self._pos >= 0: - return self._current_cursor.getString(column_index) - return None - - def getInt(self, column_index: int) -> int: - if self._current_cursor and self._pos >= 0: - return self._current_cursor.getInt(column_index) - return 0 - - def close(self): - for c in self._cursors: - c.close() - super().close() - - -# ═══════════════════════════════════════════════════════════════ -# CursorJoiner -# ═══════════════════════════════════════════════════════════════ - -class CursorJoiner(Stub): - """CursorJoiner — 游标连接器""" - - class Result(Stub): - BOTH = 0 - LEFT = 1 - RIGHT = 2 - - def __init__(self, cursor_a: Cursor, column_names_a: List[str], - cursor_b: Cursor, column_names_b: List[str]): - super().__init__() - self._cursor_a = cursor_a - self._cursor_b = cursor_b - self._col_names_a = column_names_a - self._col_names_b = column_names_b - self._has_next = False - - def hasNext(self) -> bool: - return self._has_next - - def next(self) -> int: - return self.Result.BOTH - - -# ═══════════════════════════════════════════════════════════════ -# DatabaseUtils -# ═══════════════════════════════════════════════════════════════ - -class DatabaseUtils(Stub): - """DatabaseUtils — 数据库工具""" - - STATEMENT_ABORT = "ABORT" - STATEMENT_FAIL = "FAIL" - STATEMENT_IGNORE = "IGNORE" - STATEMENT_REPLACE = "REPLACE" - STATEMENT_ROLLBACK = "ROLLBACK" - - @staticmethod - def cursorFillWindow(cursor: Cursor, pos: int, window: CursorWindow): - cursor.fillWindow(pos, window) - - @staticmethod - def cursorStringToContentValues(cursor: Cursor, field: str, values: Any): - pass - - @staticmethod - def cursorIntToContentValues(cursor: Cursor, field: str, values: Any): - pass - - @staticmethod - def cursorLongToContentValues(cursor: Cursor, field: str, values: Any): - pass - - @staticmethod - def cursorDoubleToContentValues(cursor: Cursor, field: str, values: Any): - pass - - @staticmethod - def cursorRowToContentValues(cursor: Cursor, values: Any): - for col_name in cursor.getColumnNames(): - idx = cursor.getColumnIndex(col_name) - val = cursor.getString(idx) - if hasattr(values, "put"): - values.put(col_name, val) - - @staticmethod - def dumpCursor(cursor: Cursor): - col_names = cursor.getColumnNames() - sys.stderr.write(f"Rows: {cursor.getCount()}\n") - sys.stderr.write(f"Columns: {col_names}\n") - - @staticmethod - def dumpCursorToString(cursor: Cursor) -> str: - return f"Cursor(count={cursor.getCount()})" - - @staticmethod - def bindObjectToProgram(program: Any, index: int, value: Any): - pass - - @staticmethod - def cursorToContentValues(cursor: Cursor, values: Any): - DatabaseUtils.cursorRowToContentValues(cursor, values) - - @staticmethod - def longForQuery(db: Any, query: str, args: Optional[List[str]] = None) -> int: - return 0 - - @staticmethod - def stringForQuery(db: Any, query: str, args: Optional[List[str]] = None) -> Optional[str]: - return None - - @staticmethod - def blobForQuery(db: Any, query: str, args: Optional[List[str]] = None) -> Optional[bytes]: - return None - - @staticmethod - def sqlEscapeString(value: str) -> str: - return value.replace("'", "''") - - @staticmethod - def appendEscapedSQLString(sb: Any, value: str): - escaped = DatabaseUtils.sqlEscapeString(value) - sb.append(f"'{escaped}'") - - @staticmethod - def appendValueToSql(sb: Any, value: Any): - if value is None: - sb.append("NULL") - elif isinstance(value, bool): - sb.append("1" if value else "0") - elif isinstance(value, (int, float)): - sb.append(str(value)) - else: - DatabaseUtils.appendEscapedSQLString(sb, str(value)) - - @staticmethod - def appendSelectionArgs(original_values: List[str], new_values: List[str]) -> List[str]: - return list(original_values) + list(new_values) - - @staticmethod - def getCollationKey(name: str) -> str: - return name.lower() - - @staticmethod - def getHexCollationKey(name: str) -> str: - return name.lower().encode("utf-8").hex() - - @staticmethod - def createDbFromSqlStatements(context: Any, db_name: str, db_version: int, sql_statements: str): - from .database_sqlite import SQLiteDatabase - db_path = os.path.join(_app_data_dir, "databases", db_name) - os.makedirs(os.path.dirname(db_path), exist_ok=True) - db = SQLiteDatabase.openOrCreateDatabase(db_path, None) - for stmt in sql_statements.split(";"): - stmt = stmt.strip() - if stmt: - try: - db.execSQL(stmt) - except Exception: - pass - return db - - -# ═══════════════════════════════════════════════════════════════ -# DatabaseUtilsInsertHelper -# ═══════════════════════════════════════════════════════════════ - -class DatabaseUtilsInsertHelper(Stub): - """DatabaseUtils.InsertHelper — 插入辅助类""" - - TABLE_INFO_PRAGMA_COLUMNNAME_INDEX = 1 - - def __init__(self, db: Any, table_name: str): - super().__init__() - self._db = db - self._table_name = table_name - - def getTableName(self) -> str: - return self._table_name - - def prepareForInsert(self) -> int: - return 0 - - def prepareForReplace(self) -> int: - return 0 - - def bind(self, index: int, value: Any): - pass - - def execute(self) -> int: - return 0 - - def close(self): - pass - - -# ═══════════════════════════════════════════════════════════════ -# Helper -# ═══════════════════════════════════════════════════════════════ - -class IllegalArgumentException(RuntimeError): - """IllegalArgumentException — 非法参数异常""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Cursor", - "MatrixCursor", - "AbstractCursor", - "AbstractWindowedCursor", - "ContentObservable", - "ContentObserver", - "DataSetObservable", - "DatabaseErrorHandler", - "DatabaseUtils", - "DefaultDatabaseErrorHandler", - "CharArrayBuffer", - "CursorJoiner", - "CursorWindow", - "CursorWrapper", - "CrossProcessCursor", - "CrossProcessCursorWrapper", - "DataSetObserver", - "MergeCursor", - "SQLException", - "CursorIndexOutOfBoundsException", - "DatabaseUtilsInsertHelper", -] \ No newline at end of file diff --git a/android_pyapi/android/database_ext.py b/android_pyapi/android/database_ext.py deleted file mode 100644 index 11c2cca..0000000 --- a/android_pyapi/android/database_ext.py +++ /dev/null @@ -1,866 +0,0 @@ -""" -Android Database Extension 组件 — ContentObserver, DataSetObserver, CrossProcessCursor, CursorWindow 等 -方法签名完全对齐 Android SDK (android.database 包扩展) -""" - -from __future__ import annotations - -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# ContentObserver -# ═══════════════════════════════════════════════════════════════ - -class ContentObserver(Stub): - """ContentObserver — 内容观察者""" - - NOTIFY_URIS = 0 - NOTIFY_SYNC_TO_NETWORK = 1 - NOTIFY_SKIP_SYNC = 2 - - def __init__(self, handler: Any = None): - super().__init__() - self._handler = handler - self._is_alive: bool = True - self._self_change_count: int = 0 - self._change_count: int = 0 - self._observed_uris: List[str] = [] - - def onChange(self, self_change: bool = False): - """内容变化回调""" - pass - - def onChange(self, self_change: bool, uri: Any = None): - """内容变化回调 (带 URI)""" - pass - - def onChange(self, self_change: bool, uri: Any, user_id: int = 0): - pass - - def deliverSelfNotifications(self) -> bool: - """是否传递自身通知""" - return False - - def isAlive(self) -> bool: - return self._is_alive - - def release(self): - """释放观察者""" - self._is_alive = False - self._observed_uris.clear() - - def dispatchChange(self, self_change: bool, uri: Any = None, user_id: int = 0): - """调度变化""" - if self._is_alive: - if uri is not None: - self.onChange(self_change, uri, user_id) - else: - self.onChange(self_change) - - def getHandler(self) -> Any: - return self._handler - - def getChangeCount(self) -> int: - return self._change_count - - def incrementChangeCount(self): - self._change_count += 1 - - def addObservedUri(self, uri: str): - if uri not in self._observed_uris: - self._observed_uris.append(uri) - - def removeObservedUri(self, uri: str): - self._observed_uris = [u for u in self._observed_uris if u != uri] - - def getObservedUris(self) -> List[str]: - return list(self._observed_uris) - - def isObservingUri(self, uri: str) -> bool: - return uri in self._observed_uris - - def toString(self) -> str: - return f"ContentObserver(alive={self._is_alive}, uris={len(self._observed_uris)})" - - -# ═══════════════════════════════════════════════════════════════ -# DataSetObserver -# ═══════════════════════════════════════════════════════════════ - -class DataSetObserver(Stub): - """DataSetObserver — 数据集观察者""" - - def __init__(self): - super().__init__() - self._is_alive: bool = True - self._notify_count: int = 0 - - def onChanged(self): - """数据集变化回调""" - self._notify_count += 1 - - def onInvalidated(self): - """数据集失效回调""" - pass - - def isAlive(self) -> bool: - return self._is_alive - - def release(self): - self._is_alive = False - - def getNotifyCount(self) -> int: - return self._notify_count - - def toString(self) -> str: - return f"DataSetObserver(alive={self._is_alive}, notified={self._notify_count})" - - -# ═══════════════════════════════════════════════════════════════ -# DataSetValidatable -# ═══════════════════════════════════════════════════════════════ - -class DataSetValidatable(Stub): - """DataSetValidatable — 数据集可验证接口""" - - def isValid(self) -> bool: - """检查数据集是否有效""" - return True - - def validate(self) -> bool: - """验证数据集""" - return self.isValid() - - def getValidationError(self) -> Optional[str]: - """获取验证错误信息""" - return None - - def resetValidation(self): - """重置验证状态""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# CrossProcessCursor -# ═══════════════════════════════════════════════════════════════ - -class CrossProcessCursor(Stub): - """CrossProcessCursor — 跨进程游标""" - - def __init__(self): - super().__init__() - self._window: Optional[CursorWindow] = None - self._position: int = -1 - self._row_count: int = 0 - self._column_count: int = 0 - - def getWindow(self) -> Optional[CursorWindow]: - return self._window - - def setWindow(self, window: CursorWindow): - self._window = window - - def fillWindow(self, position: int, window: CursorWindow): - """填充窗口""" - self._window = window - self._position = position - - def onMove(self, old_position: int, new_position: int) -> bool: - """移动回调""" - return 0 <= new_position < self._row_count - - def getPosition(self) -> int: - return self._position - - def moveToPosition(self, position: int) -> bool: - if 0 <= position < self._row_count: - old = self._position - self._position = position - return self.onMove(old, position) - return False - - def moveToFirst(self) -> bool: - return self.moveToPosition(0) - - def moveToLast(self) -> bool: - return self.moveToPosition(self._row_count - 1) - - def moveToNext(self) -> bool: - return self.moveToPosition(self._position + 1) - - def moveToPrevious(self) -> bool: - return self.moveToPosition(self._position - 1) - - def isFirst(self) -> bool: - return self._position == 0 - - def isLast(self) -> bool: - return self._position == self._row_count - 1 - - def isBeforeFirst(self) -> bool: - return self._position < 0 - - def isAfterLast(self) -> bool: - return self._position >= self._row_count - - def getRowCount(self) -> int: - return self._row_count - - def setRowCount(self, count: int): - self._row_count = count - - def getColumnCount(self) -> int: - return self._column_count - - def setColumnCount(self, count: int): - self._column_count = count - - def getColumnIndex(self, column_name: str) -> int: - return -1 - - def getColumnName(self, index: int) -> str: - return "" - - def getColumnNames(self) -> List[str]: - return [] - - def getString(self, column_index: int) -> Optional[str]: - return None - - def getInt(self, column_index: int) -> int: - return 0 - - def getLong(self, column_index: int) -> int: - return 0 - - def getFloat(self, column_index: int) -> float: - return 0.0 - - def getDouble(self, column_index: int) -> float: - return 0.0 - - def isNull(self, column_index: int) -> bool: - return True - - def close(self): - self._window = None - self._row_count = 0 - - def requery(self) -> bool: - return False - - def toString(self) -> str: - return f"CrossProcessCursor(rows={self._row_count}, pos={self._position})" - - -# ═══════════════════════════════════════════════════════════════ -# CrossProcessCursorWrapper -# ═══════════════════════════════════════════════════════════════ - -class CrossProcessCursorWrapper(CrossProcessCursor): - """CrossProcessCursorWrapper — 跨进程游标包装器""" - - def __init__(self, cursor: Optional[CrossProcessCursor] = None): - super().__init__() - self._wrapped_cursor = cursor - - def getWrappedCursor(self) -> Optional[CrossProcessCursor]: - return self._wrapped_cursor - - def setWrappedCursor(self, cursor: CrossProcessCursor): - self._wrapped_cursor = cursor - - def getWindow(self) -> Optional[CursorWindow]: - if self._wrapped_cursor: - return self._wrapped_cursor.getWindow() - return self._window - - def fillWindow(self, position: int, window: CursorWindow): - if self._wrapped_cursor: - self._wrapped_cursor.fillWindow(position, window) - else: - super().fillWindow(position, window) - - def onMove(self, old_position: int, new_position: int) -> bool: - if self._wrapped_cursor: - return self._wrapped_cursor.onMove(old_position, new_position) - return super().onMove(old_position, new_position) - - def getRowCount(self) -> int: - if self._wrapped_cursor: - return self._wrapped_cursor.getRowCount() - return self._row_count - - def getColumnCount(self) -> int: - if self._wrapped_cursor: - return self._wrapped_cursor.getColumnCount() - return self._column_count - - def getColumnNames(self) -> List[str]: - if self._wrapped_cursor: - return self._wrapped_cursor.getColumnNames() - return [] - - def getColumnIndex(self, column_name: str) -> int: - if self._wrapped_cursor: - return self._wrapped_cursor.getColumnIndex(column_name) - return -1 - - def getString(self, column_index: int) -> Optional[str]: - if self._wrapped_cursor: - return self._wrapped_cursor.getString(column_index) - return None - - def getInt(self, column_index: int) -> int: - if self._wrapped_cursor: - return self._wrapped_cursor.getInt(column_index) - return 0 - - def getLong(self, column_index: int) -> int: - if self._wrapped_cursor: - return self._wrapped_cursor.getLong(column_index) - return 0 - - def getDouble(self, column_index: int) -> float: - if self._wrapped_cursor: - return self._wrapped_cursor.getDouble(column_index) - return 0.0 - - def moveToPosition(self, position: int) -> bool: - if self._wrapped_cursor: - return self._wrapped_cursor.moveToPosition(position) - return super().moveToPosition(position) - - def close(self): - if self._wrapped_cursor: - self._wrapped_cursor.close() - super().close() - - def toString(self) -> str: - return f"CrossProcessCursorWrapper(wrapped={self._wrapped_cursor})" - - -# ═══════════════════════════════════════════════════════════════ -# CursorWindow -# ═══════════════════════════════════════════════════════════════ - -class CursorWindow(Stub): - """CursorWindow — 游标窗口""" - - WINDOW_SIZE_LIMIT = 2097152 # 2MB - - TYPE_NULL = 0 - TYPE_INTEGER = 1 - TYPE_FLOAT = 2 - TYPE_BLOB = 4 - TYPE_STRING = 5 - - def __init__(self, name: str = "CursorWindow"): - super().__init__() - self._name = name - self._start_position: int = 0 - self._num_rows: int = 0 - self._is_open: bool = True - self._data: Dict[Tuple[int, int], Any] = {} - self._types: Dict[Tuple[int, int], int] = {} - self._allocated_memory: int = 0 - self._max_allocated_memory: int = CursorWindow.WINDOW_SIZE_LIMIT - - @staticmethod - def newFromParcel(data: Any) -> CursorWindow: - return CursorWindow("from_parcel") - - def clear(self): - """清空窗口""" - self._data.clear() - self._types.clear() - self._num_rows = 0 - self._allocated_memory = 0 - - def getStartPosition(self) -> int: - return self._start_position - - def setStartPosition(self, pos: int): - self._start_position = pos - - def getNumRows(self) -> int: - return self._num_rows - - def setNumColumns(self, num_columns: int): - self._num_columns = num_columns - - def getNumColumns(self) -> int: - return self._num_columns - - def allocRow(self) -> bool: - """分配一行""" - self._num_rows += 1 - return True - - def freeLastRow(self): - """释放最后一行""" - if self._num_rows > 0: - self._num_rows -= 1 - keys_to_remove = [k for k in self._data if k[0] == self._num_rows] - for k in keys_to_remove: - del self._data[k] - del self._types[k] - - def putString(self, value: str, row: int, col: int) -> bool: - self._data[(row, col)] = value - self._types[(row, col)] = CursorWindow.TYPE_STRING - self._allocated_memory += len(value) - return True - - def putLong(self, value: int, row: int, col: int) -> bool: - self._data[(row, col)] = value - self._types[(row, col)] = CursorWindow.TYPE_INTEGER - self._allocated_memory += 8 - return True - - def putDouble(self, value: float, row: int, col: int) -> bool: - self._data[(row, col)] = value - self._types[(row, col)] = CursorWindow.TYPE_FLOAT - self._allocated_memory += 8 - return True - - def putBlob(self, value: bytes, row: int, col: int) -> bool: - self._data[(row, col)] = value - self._types[(row, col)] = CursorWindow.TYPE_BLOB - self._allocated_memory += len(value) - return True - - def putNull(self, row: int, col: int) -> bool: - self._data[(row, col)] = None - self._types[(row, col)] = CursorWindow.TYPE_NULL - return True - - def getString(self, row: int, col: int) -> Optional[str]: - val = self._data.get((row, col)) - if isinstance(val, str): - return val - if val is None: - return None - return str(val) - - def getLong(self, row: int, col: int) -> int: - val = self._data.get((row, col), 0) - if isinstance(val, int): - return val - if isinstance(val, float): - return int(val) - if isinstance(val, str): - try: - return int(val) - except ValueError: - return 0 - return 0 - - def getDouble(self, row: int, col: int) -> float: - val = self._data.get((row, col), 0.0) - if isinstance(val, (int, float)): - return float(val) - if isinstance(val, str): - try: - return float(val) - except ValueError: - return 0.0 - return 0.0 - - def getBlob(self, row: int, col: int) -> Optional[bytes]: - val = self._data.get((row, col)) - if isinstance(val, bytes): - return val - if isinstance(val, str): - return val.encode("utf-8") - return None - - def getType(self, row: int, col: int) -> int: - return self._types.get((row, col), CursorWindow.TYPE_NULL) - - def isNull(self, row: int, col: int) -> bool: - return self.getType(row, col) == CursorWindow.TYPE_NULL - - def isOpen(self) -> bool: - return self._is_open - - def close(self): - """关闭窗口""" - self._is_open = False - self.clear() - - def toString(self) -> str: - return f"CursorWindow(name={self._name}, rows={self._num_rows}, open={self._is_open})" - - -# ═══════════════════════════════════════════════════════════════ -# DatabaseUtils -# ═══════════════════════════════════════════════════════════════ - -class DatabaseUtils(Stub): - """DatabaseUtils — 数据库工具""" - - STATEMENT_ABORT = 0 - STATEMENT_ATTACH = 1 - STATEMENT_BEGIN = 2 - STATEMENT_COMMIT = 3 - STATEMENT_DELETE = 4 - STATEMENT_DETACH = 5 - STATEMENT_DROP_INDEX = 6 - STATEMENT_DROP_TABLE = 7 - STATEMENT_DROP_TRIGGER = 8 - STATEMENT_DROP_VIEW = 9 - STATEMENT_INSERT = 10 - STATEMENT_PRAGMA = 11 - STATEMENT_REINDEX = 12 - STATEMENT_RELEASE = 13 - STATEMENT_REPLACE = 14 - STATEMENT_ROLLBACK = 15 - STATEMENT_SAVEPOINT = 16 - STATEMENT_SELECT = 17 - STATEMENT_UPDATE = 18 - STATEMENT_OTHER = 99 - - @staticmethod - def sqlEscapeString(value: str) -> str: - """SQL 转义字符串""" - return "'" + value.replace("'", "''") + "'" - - @staticmethod - def appendValueToSql(sql: str, value: Any) -> str: - """将值附加到 SQL""" - if value is None: - return sql + " NULL" - if isinstance(value, bool): - return sql + (" 1" if value else " 0") - if isinstance(value, int): - return sql + f" {value}" - if isinstance(value, float): - return sql + f" {value}" - return sql + f" '{DatabaseUtils.sqlEscapeString(str(value))}'" - - @staticmethod - def concatenateWhere(a: str, b: str) -> str: - """连接 WHERE 子句""" - if a is None or a == "": - return b - if b is None or b == "": - return a - return f"({a}) AND ({b})" - - @staticmethod - def getCollationKey(name: str) -> str: - """获取排序键""" - return name.lower() - - @staticmethod - def getHexCollationKey(name: str) -> str: - """获取十六进制排序键""" - if not name: - return "" - return name.encode("utf-8").hex() - - @staticmethod - def dumpCursor(cursor: CrossProcessCursor): - """转储游标内容""" - rows = cursor.getRowCount() - cols = cursor.getColumnCount() - names = cursor.getColumnNames() - for r in range(rows): - cursor.moveToPosition(r) - row_data = {} - for c in range(cols): - name = names[c] if c < len(names) else f"col{c}" - row_data[name] = cursor.getString(c) - print(str(row_data)) - - @staticmethod - def cursorRowToContentValues(cursor: CrossProcessCursor) -> Dict[str, Any]: - """将游标行转换为内容值""" - values = {} - for c in range(cursor.getColumnCount()): - name = cursor.getColumnName(c) - values[name] = cursor.getString(c) - return values - - @staticmethod - def LongToHexString(value: int) -> str: - """将长整型转换为十六进制字符串""" - return f"{value:#018x}" - - @staticmethod - def bindObjectToProgram(statement: Any, index: int, value: Any): - """绑定对象到 SQL 程序""" - pass - - @staticmethod - def createDbFromSqlStatements(context: Any, name: str, - sql_statements: List[str]) -> Any: - """从 SQL 语句创建数据库""" - return None - - @staticmethod - def queryNumEntries(db: Any, table: str, selection: str = "") -> int: - """查询条目数""" - return 0 - - @staticmethod - def stringOrEmpty(s: Optional[str]) -> str: - """返回字符串或空""" - return s if s else "" - - @staticmethod - def isTableExists(db: Any, table_name: str) -> bool: - """检查表是否存在""" - return True - - -# ═══════════════════════════════════════════════════════════════ -# DefaultDatabaseErrorHandler -# ═══════════════════════════════════════════════════════════════ - -class DefaultDatabaseErrorHandler(Stub): - """DefaultDatabaseErrorHandler — 默认数据库错误处理器""" - - ERROR_TYPE_CORRUPT = 1 - ERROR_TYPE_FULL = 2 - ERROR_TYPE_PERMISSION = 3 - ERROR_TYPE_IO = 4 - ERROR_TYPE_NOMEM = 5 - ERROR_TYPE_TOOBIG = 6 - ERROR_TYPE_CONSTRAINT = 7 - ERROR_TYPE_MISMATCH = 8 - ERROR_TYPE_MISUSE = 9 - ERROR_TYPE_NOTADB = 10 - ERROR_TYPE_INTERNAL = 11 - - def __init__(self): - super().__init__() - self._error_count: int = 0 - self._last_error: int = 0 - self._error_message: str = "" - self._is_recovering: bool = False - - def onCorruption(self, db_obj: Any): - """数据库损坏回调""" - self._error_count += 1 - self._last_error = DefaultDatabaseErrorHandler.ERROR_TYPE_CORRUPT - self._error_message = "Database corruption detected" - self._is_recovering = True - self._tryRecover(db_obj) - self._is_recovering = False - - def onError(self, db_obj: Any, error_type: int, message: str = ""): - """错误回调""" - self._error_count += 1 - self._last_error = error_type - self._error_message = message - - def _tryRecover(self, db_obj: Any): - """尝试恢复数据库""" - pass - - def getErrorCount(self) -> int: - return self._error_count - - def getLastError(self) -> int: - return self._last_error - - def getErrorMessage(self) -> str: - return self._error_message - - def isRecovering(self) -> bool: - return self._is_recovering - - def toString(self) -> str: - return f"DefaultDatabaseErrorHandler(errors={self._error_count})" - - -# ═══════════════════════════════════════════════════════════════ -# Extras (Database 扩展) -# ═══════════════════════════════════════════════════════════════ - -class Extras(Stub): - """Extras — 数据库扩展附加类""" - - EXTRA_DATABASE_NAME = "database_name" - EXTRA_TABLE_NAME = "table_name" - EXTRA_SQL_COMMAND = "sql_command" - EXTRA_ERROR_DESCRIPTION = "error_description" - EXTRA_ROW_COUNT = "row_count" - EXTRA_PAGE_SIZE = "page_size" - EXTRA_PAGE_COUNT = "page_count" - EXTRA_DATABASE_SIZE = "database_size" - EXTRA_JOURNAL_MODE = "journal_mode" - EXTRA_SYNC_MODE = "sync_mode" - EXTRA_CACHE_SIZE = "cache_size" - EXTRA_FOREIGN_KEYS = "foreign_keys" - EXTRA_ENCODING = "encoding" - EXTRA_SCHEMA_VERSION = "schema_version" - EXTRA_USER_VERSION = "user_version" - - FLAG_READ_ONLY = 1 - FLAG_WRITE_AHEAD_LOGGING = 2 - FLAG_ENABLE_FOREIGN_KEYS = 4 - FLAG_CREATE_IF_NECESSARY = 8 - FLAG_NO_LOCALIZED_COLLATORS = 16 - FLAG_OPEN_READONLY = 32 - FLAG_ENABLE_WRITE_AHEAD_LOGGING = 64 - FLAG_DISABLE_COMPATIBILITY_WAL = 128 - FLAG_OPEN_READ_WRITE = 256 - - def __init__(self): - super().__init__() - self._extras: Dict[str, Any] = {} - self._flags: int = 0 - - def getString(self, key: str, default: str = "") -> str: - return str(self._extras.get(key, default)) - - def getInt(self, key: str, default: int = 0) -> int: - val = self._extras.get(key, default) - return int(val) if val is not None else default - - def getBoolean(self, key: str, default: bool = False) -> bool: - val = self._extras.get(key, default) - return bool(val) if val is not None else default - - def putString(self, key: str, value: str): - self._extras[key] = value - - def putInt(self, key: str, value: int): - self._extras[key] = value - - def putBoolean(self, key: str, value: bool): - self._extras[key] = value - - def putAll(self, extras: Dict[str, Any]): - self._extras.update(extras) - - def containsKey(self, key: str) -> bool: - return key in self._extras - - def getKeys(self) -> List[str]: - return list(self._extras.keys()) - - def size(self) -> int: - return len(self._extras) - - def isEmpty(self) -> bool: - return len(self._extras) == 0 - - def clear(self): - self._extras.clear() - - def getFlags(self) -> int: - return self._flags - - def setFlags(self, flags: int): - self._flags = flags - - def hasFlag(self, flag: int) -> bool: - return bool(self._flags & flag) - - def toString(self) -> str: - return f"Extras(entries={len(self._extras)}, flags={self._flags})" - - -# ═══════════════════════════════════════════════════════════════ -# BulkCursorDescriptor (helper) -# ═══════════════════════════════════════════════════════════════ - -class BulkCursorDescriptor(Stub): - """BulkCursorDescriptor — 批量游标描述符""" - - def __init__(self): - super().__init__() - self._cursor: Optional[CrossProcessCursor] = None - self._column_names: List[str] = [] - self._wants_all_on_move_calls: bool = False - self._count: int = 0 - self._window: Optional[CursorWindow] = None - - def getCursor(self) -> Optional[CrossProcessCursor]: - return self._cursor - - def setCursor(self, cursor: CrossProcessCursor): - self._cursor = cursor - - def getColumnNames(self) -> List[str]: - return list(self._column_names) - - def setColumnNames(self, names: List[str]): - self._column_names = list(names) - - def wantsAllOnMoveCalls(self) -> bool: - return self._wants_all_on_move_calls - - def setWantsAllOnMoveCalls(self, wants: bool): - self._wants_all_on_move_calls = wants - - def getCount(self) -> int: - return self._count - - def setCount(self, count: int): - self._count = count - - def getWindow(self) -> Optional[CursorWindow]: - return self._window - - def setWindow(self, window: CursorWindow): - self._window = window - - def toString(self) -> str: - return f"BulkCursorDescriptor(columns={len(self._column_names)}, count={self._count})" - - -# ═══════════════════════════════════════════════════════════════ -# CursorJoiner (helper) -# ═══════════════════════════════════════════════════════════════ - -class CursorJoiner(Stub): - """CursorJoiner — 游标连接器""" - - RESULT_BOTH = 0 - RESULT_LEFT = 1 - RESULT_RIGHT = 2 - - def __init__(self, cursor_left: CrossProcessCursor, - column_names_left: List[str], - cursor_right: CrossProcessCursor, - column_names_right: List[str]): - super().__init__() - self._left = cursor_left - self._right = cursor_right - self._left_cols = column_names_left - self._right_cols = column_names_right - self._left_pos: int = -1 - self._right_pos: int = -1 - self._result: int = CursorJoiner.RESULT_BOTH - - def hasNext(self) -> bool: - return (self._left_pos + 1 < self._left.getRowCount() or - self._right_pos + 1 < self._right.getRowCount()) - - def next(self) -> int: - self._left_pos += 1 - self._right_pos += 1 - return self._result - - def getLeftCursor(self) -> CrossProcessCursor: - return self._left - - def getRightCursor(self) -> CrossProcessCursor: - return self._right - - def reset(self): - self._left_pos = -1 - self._right_pos = -1 - - def toString(self) -> str: - return f"CursorJoiner(left={self._left_pos}, right={self._right_pos})" \ No newline at end of file diff --git a/android_pyapi/android/database_sqlite.py b/android_pyapi/android/database_sqlite.py deleted file mode 100644 index 650ed2f..0000000 --- a/android_pyapi/android/database_sqlite.py +++ /dev/null @@ -1,1118 +0,0 @@ -""" -Android SQLite 组件 — SQLiteDatabase, SQLiteOpenHelper, SQLiteStatement 等 -使用 Python sqlite3 模块作为后端 -方法签名完全对齐 Android SDK -""" - -import os -import sys -import threading -import sqlite3 as _sqlite3 -from typing import Any, Dict, List, Optional, Set, Tuple, Union - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) -from .database import ( - Cursor, AbstractCursor, ContentObserver, DataSetObserver, SQLException as BaseSQLException, -) - - -# ═══════════════════════════════════════════════════════════════ -# SQLiteException hierarchy -# ═══════════════════════════════════════════════════════════════ - -class SQLiteException(BaseSQLException): - """SQLiteException — SQLite 异常基类""" - - def __init__(self, message: str = ""): - super().__init__(message) - self._error_message = message - - -class SQLiteAbortException(SQLiteException): - pass - - -class SQLiteAccessPermException(SQLiteException): - pass - - -class SQLiteBindOrColumnIndexOutOfRangeException(SQLiteException): - pass - - -class SQLiteBlobTooBigException(SQLiteException): - pass - - -class SQLiteCantOpenDatabaseException(SQLiteException): - pass - - -class SQLiteConstraintException(SQLiteException): - pass - - -class SQLiteDatabaseCorruptException(SQLiteException): - pass - - -class SQLiteDatabaseLockedException(SQLiteException): - pass - - -class SQLiteDatatypeMismatchException(SQLiteException): - pass - - -class SQLiteDiskIOException(SQLiteException): - pass - - -class SQLiteDoneException(SQLiteException): - pass - - -class SQLiteException_General(SQLiteException): - pass - - -class SQLiteFullException(SQLiteException): - pass - - -class SQLiteMisuseException(SQLiteException): - pass - - -class SQLiteOutOfMemoryException(SQLiteException): - pass - - -class SQLiteReadOnlyDatabaseException(SQLiteException): - pass - - -class SQLiteTableLockedException(SQLiteException): - pass - - -class SQLiteUnsupportedOperationException(SQLiteException): - pass - - -# ═══════════════════════════════════════════════════════════════ -# SQLiteClosable -# ═══════════════════════════════════════════════════════════════ - -class SQLiteClosable(Stub): - """SQLiteClosable — 可关闭资源基类""" - - def __init__(self): - super().__init__() - self._closed = False - self._reference_count = 1 - - def acquireReference(self): - if self._closed: - raise IllegalStateException("already closed") - self._reference_count += 1 - - def releaseReference(self): - self._reference_count -= 1 - if self._reference_count <= 0: - self.onAllReferencesReleased() - - def releaseReferenceFromContainer(self): - self.releaseReference() - - def onAllReferencesReleased(self): - self.close() - - def close(self): - self._closed = True - - def isClosed(self) -> bool: - return self._closed - - -class IllegalStateException(RuntimeError): - pass - - -# ═══════════════════════════════════════════════════════════════ -# SQLiteProgram -# ═══════════════════════════════════════════════════════════════ - -class SQLiteProgram(SQLiteClosable): - """SQLiteProgram — SQL 程序基类""" - - def __init__(self, db: "SQLiteDatabase", sql: str): - super().__init__() - self._db = db - self._sql = sql - self._bindings: Dict[int, Any] = {} - self._next_bind_index = 1 - - def getDatabase(self) -> "SQLiteDatabase": - return self._db - - def getSql(self) -> str: - return self._sql - - def bindNull(self, index: int): - self._bindings[index] = None - - def bindLong(self, index: int, value: int): - self._bindings[index] = value - - def bindDouble(self, index: int, value: float): - self._bindings[index] = value - - def bindString(self, index: int, value: str): - self._bindings[index] = value - - def bindBlob(self, index: int, value: bytes): - self._bindings[index] = value - - def clearBindings(self): - self._bindings.clear() - - def _ordered_bind_args(self) -> list: - """按绑定索引升序返回参数列表(1-based 索引排序)。""" - return [self._bindings[k] for k in sorted(self._bindings.keys())] - - def _bind_parameters(self, cursor: _sqlite3.Cursor): - for idx in sorted(self._bindings.keys()): - cursor.bind(idx, self._bindings[idx]) - - -# ═══════════════════════════════════════════════════════════════ -# SQLiteStatement -# ═══════════════════════════════════════════════════════════════ - -class SQLiteStatement(SQLiteProgram): - """SQLiteStatement — 预编译语句""" - - def __init__(self, db: "SQLiteDatabase", sql: str): - super().__init__(db, sql) - - def execute(self): - try: - conn = self._db._acquire_connection() - conn.execute(self._sql, self._ordered_bind_args()) - if not self._db._in_transaction: - conn.commit() - except _sqlite3.DatabaseError as e: - raise _map_sqlite_error(str(e), self._sql) - - def executeUpdateDelete(self) -> int: - try: - conn = self._db._acquire_connection() - cursor = conn.execute(self._sql, self._ordered_bind_args()) - if not self._db._in_transaction: - conn.commit() - return cursor.rowcount - except _sqlite3.DatabaseError as e: - raise _map_sqlite_error(str(e), self._sql) - - def executeInsert(self) -> int: - try: - conn = self._db._acquire_connection() - cursor = conn.execute(self._sql, self._ordered_bind_args()) - if not self._db._in_transaction: - conn.commit() - return cursor.lastrowid - except _sqlite3.DatabaseError as e: - raise _map_sqlite_error(str(e), self._sql) - - def simpleQueryForLong(self) -> int: - try: - conn = self._db._acquire_connection() - cursor = conn.execute(self._sql, self._ordered_bind_args()) - row = cursor.fetchone() - if not self._db._in_transaction: - conn.commit() - if row: - return int(row[0]) if row[0] is not None else 0 - raise SQLiteDoneException("no results") - except _sqlite3.DatabaseError as e: - raise _map_sqlite_error(str(e), self._sql) - - def simpleQueryForString(self) -> Optional[str]: - try: - conn = self._db._acquire_connection() - cursor = conn.execute(self._sql, self._ordered_bind_args()) - row = cursor.fetchone() - if not self._db._in_transaction: - conn.commit() - if row: - return str(row[0]) if row[0] is not None else None - raise SQLiteDoneException("no results") - except _sqlite3.DatabaseError as e: - raise _map_sqlite_error(str(e), self._sql) - - def simpleQueryForBlobString(self) -> Optional[bytes]: - try: - conn = self._db._acquire_connection() - cursor = conn.execute(self._sql, self._ordered_bind_args()) - row = cursor.fetchone() - if not self._db._in_transaction: - conn.commit() - if row: - v = row[0] - if v is None: - return None - if isinstance(v, bytes): - return v - if isinstance(v, str): - return v.encode("utf-8") - return bytes(str(v), "utf-8") - raise SQLiteDoneException("no results") - except _sqlite3.DatabaseError as e: - raise _map_sqlite_error(str(e), self._sql) - - def execQuery(self) -> "SQLiteCursor": - """执行查询 SQL 并返回 SQLiteCursor。""" - query = SQLiteQuery(self._db, self._sql) - for idx, val in self._bindings.items(): - query.bind(idx, val) - return SQLiteCursor(self._db, query) - - -# ═══════════════════════════════════════════════════════════════ -# SQLiteQuery -# ═══════════════════════════════════════════════════════════════ - -class SQLiteQuery(SQLiteProgram): - """SQLiteQuery — 查询对象""" - - def __init__(self, db: "SQLiteDatabase", sql: str): - super().__init__(db, sql) - - def toString(self) -> str: - return f"SQLiteQuery: {self._sql}" - - def sql(self) -> str: - return self._sql - - def bind(self, index: int, value: Any): - self._bindings[index] = value - - -# ═══════════════════════════════════════════════════════════════ -# SQLiteCursor -# ═══════════════════════════════════════════════════════════════ - -class SQLiteCursor(AbstractCursor): - """SQLiteCursor — SQLite 游标实现""" - - def __init__(self, db: "SQLiteDatabase", query: SQLiteQuery): - super().__init__() - self._db = db - self._query = query - self._column_names: List[str] = [] - self._rows: List[List[Any]] = [] - self._current_row: Optional[List[Any]] = None - self._loaded = False - self._closed = False - - def _load(self): - if self._loaded or self._closed: - return - self._loaded = True - try: - conn = self._db._acquire_connection() - cursor = conn.execute(self._query.getSql(), list(self._query._bindings.values())) - self._column_names = [desc[0] for desc in cursor.description] if cursor.description else [] - self._rows = cursor.fetchall() - except _sqlite3.DatabaseError as e: - raise _map_sqlite_error(str(e), self._query.getSql()) - - def getCount(self) -> int: - self._load() - return len(self._rows) - - def getColumnNames(self) -> List[str]: - self._load() - return list(self._column_names) - - def getColumnIndex(self, column_name: str) -> int: - self._load() - try: - return self._column_names.index(column_name) - except ValueError: - return -1 - - def moveToPosition(self, position: int) -> bool: - self._load() - count = len(self._rows) - if position >= count: - self._pos = count - self._current_row = None - return False - if position < -1: - self._pos = -1 - self._current_row = None - return False - self._pos = position - if 0 <= position < count: - self._current_row = self._rows[position] - else: - self._current_row = None - return True - - def getString(self, column_index: int) -> Optional[str]: - if self._current_row is None or column_index < 0 or column_index >= len(self._current_row): - return None - v = self._current_row[column_index] - return str(v) if v is not None else None - - def getInt(self, column_index: int) -> int: - if self._current_row is None or column_index < 0 or column_index >= len(self._current_row): - return 0 - v = self._current_row[column_index] - if v is None: - return 0 - try: - return int(v) - except (ValueError, TypeError): - return 0 - - def getLong(self, column_index: int) -> int: - return self.getInt(column_index) - - def getFloat(self, column_index: int) -> float: - if self._current_row is None or column_index < 0 or column_index >= len(self._current_row): - return 0.0 - v = self._current_row[column_index] - if v is None: - return 0.0 - try: - return float(v) - except (ValueError, TypeError): - return 0.0 - - def getDouble(self, column_index: int) -> float: - return self.getFloat(column_index) - - def getShort(self, column_index: int) -> int: - return self.getInt(column_index) - - def getBlob(self, column_index: int) -> Optional[bytes]: - if self._current_row is None or column_index < 0 or column_index >= len(self._current_row): - return None - v = self._current_row[column_index] - if isinstance(v, bytes): - return v - if v is not None: - return bytes(str(v), "utf-8") - return None - - def getType(self, column_index: int) -> int: - self._load() - if self._current_row is None or column_index < 0 or column_index >= len(self._current_row): - return Cursor.FIELD_TYPE_NULL - v = self._current_row[column_index] - if v is None: - return Cursor.FIELD_TYPE_NULL - if isinstance(v, int): - return Cursor.FIELD_TYPE_INTEGER - if isinstance(v, float): - return Cursor.FIELD_TYPE_FLOAT - if isinstance(v, bytes): - return Cursor.FIELD_TYPE_BLOB - return Cursor.FIELD_TYPE_STRING - - def isNull(self, column_index: int) -> bool: - return self.getType(column_index) == Cursor.FIELD_TYPE_NULL - - def close(self): - self._closed = True - self._rows.clear() - self._current_row = None - - def requery(self) -> bool: - self._loaded = False - self._rows.clear() - self._current_row = None - self._pos = -1 - return True - - -# ═══════════════════════════════════════════════════════════════ -# SQLiteQueryBuilder -# ═══════════════════════════════════════════════════════════════ - -class SQLiteQueryBuilder(Stub): - """SQLiteQueryBuilder — 查询构建器""" - - def __init__(self): - super().__init__() - self._tables: str = "" - self._distinct: bool = False - self._where: Optional[str] = None - - def setTables(self, tables: str): - self._tables = tables - - def getTables(self) -> str: - return self._tables - - def appendWhere(self, where: str): - if self._where: - self._where = f"({self._where}) AND ({where})" - else: - self._where = where - - def appendWhereStandalone(self, where: str): - self._where = where - - def setDistinct(self, distinct: bool): - self._distinct = distinct - - def buildQueryString(self, distinct: bool, tables: str, - columns: Optional[List[str]], where: Optional[str], - group_by: Optional[str], having: Optional[str], - order_by: Optional[str], limit: Optional[str]) -> str: - sql = "SELECT " - if distinct: - sql += "DISTINCT " - if columns and len(columns) > 0: - sql += ", ".join(columns) - else: - sql += "*" - sql += f" FROM {tables}" - if where: - sql += f" WHERE ({where})" - if group_by: - sql += f" GROUP BY {group_by}" - if having: - sql += f" HAVING {having}" - if order_by: - sql += f" ORDER BY {order_by}" - if limit: - sql += f" LIMIT {limit}" - return sql - - def buildQuery(self, columns: Optional[List[str]], where: Optional[str], - group_by: Optional[str], having: Optional[str], - order_by: Optional[str], limit: Optional[str]) -> str: - return self.buildQueryString(self._distinct, self._tables, - columns, where or self._where, - group_by, having, order_by, limit) - - def query(self, db: "SQLiteDatabase", columns: Optional[List[str]], - where: Optional[str], group_by: Optional[str], - having: Optional[str], order_by: Optional[str], - limit: Optional[str]) -> SQLiteCursor: - sql = self.buildQuery(columns, where, group_by, having, order_by, limit) - return db.rawQuery(sql, None) - - -# ═══════════════════════════════════════════════════════════════ -# SQLiteDatabase -# ═══════════════════════════════════════════════════════════════ - -def _map_sqlite_error(msg: str, sql: str = "") -> SQLiteException: - msg_lower = msg.lower() - if "constraint" in msg_lower: - return SQLiteConstraintException(msg) - if "locked" in msg_lower: - return SQLiteDatabaseLockedException(msg) - if "corrupt" in msg_lower: - return SQLiteDatabaseCorruptException(msg) - if "readonly" in msg_lower or "read only" in msg_lower: - return SQLiteReadOnlyDatabaseException(msg) - if "disk" in msg_lower or "full" in msg_lower: - return SQLiteDiskIOException(msg) if "disk" in msg_lower else SQLiteFullException(msg) - if "abort" in msg_lower: - return SQLiteAbortException(msg) - if "permission" in msg_lower: - return SQLiteAccessPermException(msg) - if "out of memory" in msg_lower: - return SQLiteOutOfMemoryException(msg) - if "not a database" in msg_lower or "cantopen" in msg_lower: - return SQLiteCantOpenDatabaseException(msg) - if "misuse" in msg_lower: - return SQLiteMisuseException(msg) - return SQLiteException(msg) - - -def _extract_value_pairs(values: Any) -> List[Tuple[str, Any]]: - """从 ContentValues / dict / 类似对象中提取 (列名, 值) 列表。 - - 注意: 本实现中 ContentValues.valueSet() 返回的是键集合而非 (键, 值) 对, - 因此需要通过 get() 取值。 - """ - if values is None: - return [] - if isinstance(values, dict): - return list(values.items()) - # ContentValues 内部使用 _values dict - inner = getattr(values, "_values", None) - if isinstance(inner, dict): - return list(inner.items()) - # ContentValues.valueSet() 在本实现中返回键集合 - if hasattr(values, "valueSet") and hasattr(values, "get"): - try: - return [(str(k), values.get(k)) for k in values.valueSet()] - except Exception: - pass - if hasattr(values, "keySet") and hasattr(values, "get"): - try: - return [(str(k), values.get(k)) for k in values.keySet()] - except Exception: - pass - if hasattr(values, "items"): - try: - return [(str(k), v) for k, v in values.items()] - except Exception: - pass - return [] - - -class SQLiteDatabase(SQLiteClosable): - """SQLiteDatabase — 数据库操作类""" - - CONFLICT_ROLLBACK = 1 - CONFLICT_ABORT = 2 - CONFLICT_FAIL = 3 - CONFLICT_IGNORE = 4 - CONFLICT_REPLACE = 5 - - OPEN_READWRITE = 0 - OPEN_READONLY = 1 - CREATE_IF_NECESSARY = 268435456 - NO_LOCALIZED_COLLATORS = 16 - - _db_registry: Dict[str, "SQLiteDatabase"] = {} - _lock = threading.Lock() - - def __init__(self, path: str, flags: int = 0): - super().__init__() - self._path = path - self._flags = flags - self._conn: Optional[_sqlite3.Connection] = None - self._in_transaction = False - self._transaction_successful = False - self._version: int = 0 - self._max_size: int = 0 - self._page_size: int = 4096 - self._enable_wal = False - self._attached_dbs: List[Dict[str, Any]] = [] - - @staticmethod - def openDatabase(path: str, cursor_factory: Any = None, - flags: int = 0, error_handler: Any = None) -> "SQLiteDatabase": - db = SQLiteDatabase(path, flags) - try: - db._conn = _sqlite3.connect(path) - db._conn.execute("PRAGMA journal_mode=WAL") - conn_cursor = db._conn.execute("PRAGMA user_version") - row = conn_cursor.fetchone() - if row: - db._version = int(row[0]) - except _sqlite3.DatabaseError as e: - raise _map_sqlite_error(str(e)) - SQLiteDatabase._db_registry[path] = db - return db - - @staticmethod - def openOrCreateDatabase(path: str, cursor_factory: Any = None, - error_handler: Any = None) -> "SQLiteDatabase": - return SQLiteDatabase.openDatabase(path, cursor_factory, - SQLiteDatabase.CREATE_IF_NECESSARY, error_handler) - - @staticmethod - def create(cursor_factory: Any = None, error_handler: Any = None) -> "SQLiteDatabase": - import tempfile - tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) - tmp.close() - return SQLiteDatabase.openOrCreateDatabase(tmp.name, cursor_factory, error_handler) - - @staticmethod - def deleteDatabase(path: str) -> bool: - try: - if path in SQLiteDatabase._db_registry: - db = SQLiteDatabase._db_registry[path] - db.close() - del SQLiteDatabase._db_registry[path] - os.remove(path) - wal_path = path + "-wal" - if os.path.exists(wal_path): - os.remove(wal_path) - shm_path = path + "-shm" - if os.path.exists(shm_path): - os.remove(shm_path) - return True - except (OSError, Exception): - return False - - @staticmethod - def databaseList() -> List[str]: - return list(SQLiteDatabase._db_registry.keys()) - - def _acquire_connection(self) -> _sqlite3.Connection: - if self._conn is None: - raise SQLiteCantOpenDatabaseException("Database not open") - return self._conn - - def execSQL(self, sql: str, bind_args: Optional[List[Any]] = None): - self._ensure_open() - try: - conn = self._acquire_connection() - args = bind_args or [] - conn.execute(sql, args) - if not self._in_transaction: - conn.commit() - except _sqlite3.DatabaseError as e: - raise _map_sqlite_error(str(e), sql) - - def rawQuery(self, sql: str, selection_args: Optional[List[Any]] = None) -> SQLiteCursor: - self._ensure_open() - query = SQLiteQuery(self, sql) - if selection_args: - for i, arg in enumerate(selection_args): - query.bind(i + 1, arg) - cursor = SQLiteCursor(self, query) - return cursor - - def insert(self, table: str, null_column_hack: Optional[str], - values: Any) -> int: - self._ensure_open() - if values is None: - return -1 - items = _extract_value_pairs(values) - if not items: - cols = ["_id"] - vals: List[Any] = [None] - else: - cols = [str(k) for k, _ in items] - vals = [v for _, v in items] - col_str = ", ".join(cols) - placeholders = ", ".join(["?"] * len(vals)) - sql = f"INSERT INTO {table} ({col_str}) VALUES ({placeholders})" - try: - conn = self._acquire_connection() - cursor = conn.execute(sql, vals) - if not self._in_transaction: - conn.commit() - return cursor.lastrowid - except _sqlite3.DatabaseError: - return -1 - - def insertOrThrow(self, table: str, null_column_hack: Optional[str], - values: Any) -> int: - self._ensure_open() - if values is None: - raise SQLiteException("null values not allowed") - items = _extract_value_pairs(values) - if not items: - cols = ["_id"] - vals: List[Any] = [None] - else: - cols = [str(k) for k, _ in items] - vals = [v for _, v in items] - col_str = ", ".join(cols) - placeholders = ", ".join(["?"] * len(vals)) - sql = f"INSERT INTO {table} ({col_str}) VALUES ({placeholders})" - try: - conn = self._acquire_connection() - cursor = conn.execute(sql, vals) - if not self._in_transaction: - conn.commit() - return cursor.lastrowid - except _sqlite3.DatabaseError as e: - raise _map_sqlite_error(str(e), sql) - - def replace(self, table: str, null_column_hack: Optional[str], - values: Any) -> int: - self._ensure_open() - if values is None: - return -1 - items = _extract_value_pairs(values) - if not items: - cols = ["_id"] - vals: List[Any] = [None] - else: - cols = [str(k) for k, _ in items] - vals = [v for _, v in items] - col_str = ", ".join(cols) - placeholders = ", ".join(["?"] * len(vals)) - sql = f"INSERT OR REPLACE INTO {table} ({col_str}) VALUES ({placeholders})" - try: - conn = self._acquire_connection() - cursor = conn.execute(sql, vals) - if not self._in_transaction: - conn.commit() - return cursor.lastrowid - except _sqlite3.DatabaseError: - return -1 - - def update(self, table: str, values: Any, - where_clause: Optional[str] = None, - where_args: Optional[List[Any]] = None) -> int: - self._ensure_open() - if values is None: - return 0 - items = _extract_value_pairs(values) - if not items: - return 0 - set_pairs = [f"{k} = ?" for k, _ in items] - vals: List[Any] = [v for _, v in items] - set_str = ", ".join(set_pairs) - sql = f"UPDATE {table} SET {set_str}" - if where_clause: - sql += f" WHERE {where_clause}" - if where_args: - vals.extend(where_args) - try: - conn = self._acquire_connection() - cursor = conn.execute(sql, vals) - if not self._in_transaction: - conn.commit() - return cursor.rowcount - except _sqlite3.DatabaseError as e: - raise _map_sqlite_error(str(e), sql) - - def delete(self, table: str, where_clause: Optional[str] = None, - where_args: Optional[List[Any]] = None) -> int: - self._ensure_open() - sql = f"DELETE FROM {table}" - vals: List[Any] = [] - if where_clause: - sql += f" WHERE {where_clause}" - if where_args: - vals = list(where_args) - try: - conn = self._acquire_connection() - cursor = conn.execute(sql, vals) - if not self._in_transaction: - conn.commit() - return cursor.rowcount - except _sqlite3.DatabaseError as e: - raise _map_sqlite_error(str(e), sql) - - def query(self, table: str, columns: Optional[List[str]] = None, - selection: Optional[str] = None, - selection_args: Optional[List[Any]] = None, - group_by: Optional[str] = None, having: Optional[str] = None, - order_by: Optional[str] = None, - limit: Optional[str] = None) -> SQLiteCursor: - col_str = ", ".join(columns) if columns else "*" - sql = f"SELECT {col_str} FROM {table}" - if selection: - sql += f" WHERE {selection}" - if group_by: - sql += f" GROUP BY {group_by}" - if having: - sql += f" HAVING {having}" - if order_by: - sql += f" ORDER BY {order_by}" - if limit: - sql += f" LIMIT {limit}" - return self.rawQuery(sql, selection_args) - - def compileStatement(self, sql: str) -> SQLiteStatement: - return SQLiteStatement(self, sql) - - def beginTransaction(self): - self._ensure_open() - try: - conn = self._acquire_connection() - conn.execute("BEGIN TRANSACTION") - self._in_transaction = True - self._transaction_successful = False - except _sqlite3.DatabaseError as e: - raise _map_sqlite_error(str(e)) - - def endTransaction(self): - if self._in_transaction and self._conn: - try: - if not self._transaction_successful: - self._conn.rollback() - else: - self._conn.commit() - except _sqlite3.DatabaseError: - pass - self._in_transaction = False - - def setTransactionSuccessful(self): - if not self._in_transaction: - raise IllegalStateException("Not in transaction") - self._transaction_successful = True - - def inTransaction(self) -> bool: - return self._in_transaction - - def isOpen(self) -> bool: - return self._conn is not None - - def getPath(self) -> str: - return self._path - - def getVersion(self) -> int: - if self._conn: - try: - cursor = self._conn.execute("PRAGMA user_version") - row = cursor.fetchone() - if row: - self._version = int(row[0]) - return self._version - except _sqlite3.DatabaseError: - pass - return self._version - - def setVersion(self, version: int): - self._version = int(version) - if self._conn: - try: - self._conn.execute(f"PRAGMA user_version = {int(version)}") - if not self._in_transaction: - self._conn.commit() - except _sqlite3.DatabaseError: - pass - - def onCreate(self, db: "SQLiteDatabase"): - pass - - def onConfigure(self, db: "SQLiteDatabase"): - pass - - def enableWriteAheadLogging(self) -> bool: - self._ensure_open() - try: - self._conn.execute("PRAGMA journal_mode=WAL") - self._enable_wal = True - return True - except _sqlite3.DatabaseError: - return False - - def disableWriteAheadLogging(self): - try: - self._conn.execute("PRAGMA journal_mode=DELETE") - self._enable_wal = False - except _sqlite3.DatabaseError: - pass - - def isWriteAheadLoggingEnabled(self) -> bool: - return self._enable_wal - - def getMaximumSize(self) -> int: - if self._conn: - try: - page_size = self.getPageSize() - if page_size <= 0: - page_size = 4096 - cursor = self._conn.execute("PRAGMA max_page_count") - row = cursor.fetchone() - if row: - self._max_size = int(row[0]) * page_size - return self._max_size - except _sqlite3.DatabaseError: - pass - return self._max_size - - def getPageSize(self) -> int: - if self._conn: - try: - cursor = self._conn.execute("PRAGMA page_size") - row = cursor.fetchone() - if row: - self._page_size = int(row[0]) - return self._page_size - except _sqlite3.DatabaseError: - pass - return self._page_size - - def setMaximumSize(self, size: int) -> int: - self._max_size = int(size) - if self._conn: - try: - page_size = self.getPageSize() - if page_size <= 0: - page_size = 4096 - max_pages = max(1, int(size) // page_size) - cursor = self._conn.execute(f"PRAGMA max_page_count = {max_pages}") - row = cursor.fetchone() - if row: - self._max_size = int(row[0]) * page_size - return self._max_size - except _sqlite3.DatabaseError: - pass - return self._max_size - - def setPageSize(self, size: int): - self._page_size = int(size) - if self._conn: - try: - self._conn.execute(f"PRAGMA page_size = {int(size)}") - except _sqlite3.DatabaseError: - pass - - def getPageCount(self) -> int: - if self._conn: - try: - cursor = self._conn.execute("PRAGMA page_count") - row = cursor.fetchone() - if row: - return row[0] - except _sqlite3.DatabaseError: - pass - return 0 - - def yieldIfContendedSafely(self, sleep_time: int = 0) -> bool: - return True - - def needUpgrade(self, new_version: int) -> bool: - return new_version > self._version - - def getAttachedDbs(self) -> Optional[List[Dict[str, Any]]]: - return self._attached_dbs - - def isReadOnly(self) -> bool: - return bool(self._flags & self.OPEN_READONLY) - - def close(self): - if self._conn: - try: - self._conn.close() - except Exception: - pass - self._conn = None - super().close() - - def _ensure_open(self): - if self._conn is None: - raise SQLiteCantOpenDatabaseException("Database not open") - - def toString(self) -> str: - return f"SQLiteDatabase(path={self._path})" - - -# ═══════════════════════════════════════════════════════════════ -# SQLiteOpenHelper -# ═══════════════════════════════════════════════════════════════ - -class SQLiteOpenHelper(Stub): - """SQLiteOpenHelper — 数据库打开辅助类""" - - def __init__(self, context: Any, name: Optional[str], - factory: Any, version: int, - error_handler: Any = None): - super().__init__() - self._context = context - self._name = name - self._factory = factory - self._version = version - self._error_handler = error_handler - self._readable_db: Optional[SQLiteDatabase] = None - self._writable_db: Optional[SQLiteDatabase] = None - self._database_path: Optional[str] = None - self._is_initialized = False - - def _get_database_path(self) -> str: - if self._database_path: - return self._database_path - db_dir = os.path.join(_app_data_dir, "databases") - os.makedirs(db_dir, exist_ok=True) - if self._name: - self._database_path = os.path.join(db_dir, self._name) - else: - import tempfile - tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False) - tmp.close() - self._database_path = tmp.name - return self._database_path - - def getWritableDatabase(self) -> SQLiteDatabase: - if self._writable_db and self._writable_db.isOpen(): - return self._writable_db - path = self._get_database_path() - db = SQLiteDatabase.openOrCreateDatabase(path, self._factory, self._error_handler) - if not self._is_initialized: - self._is_initialized = True - current_version = db.getVersion() - if current_version == 0: - self.onCreate(db) - db.setVersion(self._version) - elif current_version < self._version: - self.onUpgrade(db, current_version, self._version) - elif current_version > self._version: - self.onDowngrade(db, current_version, self._version) - self.onOpen(db) - self._writable_db = db - self._readable_db = db - return db - - def getReadableDatabase(self) -> SQLiteDatabase: - return self.getWritableDatabase() - - def onCreate(self, db: SQLiteDatabase): - pass - - def onUpgrade(self, db: SQLiteDatabase, old_version: int, new_version: int): - pass - - def onDowngrade(self, db: SQLiteDatabase, old_version: int, new_version: int): - pass - - def onOpen(self, db: SQLiteDatabase): - pass - - def close(self): - if self._writable_db: - self._writable_db.close() - self._writable_db = None - if self._readable_db: - self._readable_db = None - - def getDatabaseName(self) -> Optional[str]: - return self._name - - def getReadableDB(self) -> Optional[SQLiteDatabase]: - return self._readable_db - - def getWriteableDB(self) -> Optional[SQLiteDatabase]: - return self._writable_db - - def toString(self) -> str: - return f"SQLiteOpenHelper(name={self._name}, version={self._version})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "SQLiteDatabase", - "SQLiteOpenHelper", - "SQLiteStatement", - "SQLiteCursor", - "SQLiteQuery", - "SQLiteQueryBuilder", - "SQLiteClosable", - "SQLiteProgram", - "SQLiteAbortException", - "SQLiteAccessPermException", - "SQLiteBindOrColumnIndexOutOfRangeException", - "SQLiteBlobTooBigException", - "SQLiteCantOpenDatabaseException", - "SQLiteConstraintException", - "SQLiteDatabaseCorruptException", - "SQLiteDatabaseLockedException", - "SQLiteDatatypeMismatchException", - "SQLiteDiskIOException", - "SQLiteDoneException", - "SQLiteException", - "SQLiteFullException", - "SQLiteMisuseException", - "SQLiteOutOfMemoryException", - "SQLiteReadOnlyDatabaseException", - "SQLiteTableLockedException", - "SQLiteUnsupportedOperationException", -] \ No newline at end of file diff --git a/android_pyapi/android/drm.py b/android_pyapi/android/drm.py deleted file mode 100644 index c385b37..0000000 --- a/android_pyapi/android/drm.py +++ /dev/null @@ -1,1090 +0,0 @@ -""" -Android DRM 组件 — DrmManager, DrmStore, DrmInfo, DrmManagerClient, DrmOutputStream 等 -方法签名完全对齐 Android SDK (android.drm 包) -""" - -from __future__ import annotations - -import os -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# DrmStore -# ═══════════════════════════════════════════════════════════════ - -class DrmStore(Stub): - """DrmStore — DRM 存储常量""" - - # ═══════════════════════════════════════════════════ - # DrmStore.Action - # ═══════════════════════════════════════════════════ - - class Action(Stub): - """DrmStore.Action — DRM 操作类型""" - - PLAY = 1 - RINGTONE = 2 - TRANSFER = 3 - OUTPUT = 4 - PREVIEW = 5 - EXECUTE = 6 - DISPLAY = 7 - PRINT = 8 - WALLPAPER = 9 - RINGTONEOUTGOING = 10 - RINGTONECALL = 11 - RINGTONESILENT = 12 - RINGTONESILENTCALL = 13 - RINGTONESILENTOUTGOING = 14 - RINGTONESILENTALARM = 15 - ALARM = 16 - NOTIFICATION = 17 - CALENDAR = 18 - CLOCK = 19 - MAX = 20 - - @staticmethod - def toString(action: int) -> str: - names = { - DrmStore.Action.PLAY: "PLAY", - DrmStore.Action.RINGTONE: "RINGTONE", - DrmStore.Action.TRANSFER: "TRANSFER", - DrmStore.Action.OUTPUT: "OUTPUT", - DrmStore.Action.PREVIEW: "PREVIEW", - DrmStore.Action.EXECUTE: "EXECUTE", - DrmStore.Action.DISPLAY: "DISPLAY", - DrmStore.Action.PRINT: "PRINT", - DrmStore.Action.WALLPAPER: "WALLPAPER", - DrmStore.Action.ALARM: "ALARM", - DrmStore.Action.NOTIFICATION: "NOTIFICATION", - } - return names.get(action, f"UNKNOWN({action})") - - class ActionConstant(Stub): - PLAY = 1 - RINGTONE = 2 - TRANSFER = 3 - OUTPUT = 4 - PREVIEW = 5 - EXECUTE = 6 - DISPLAY = 7 - PRINT = 8 - WALLPAPER = 9 - ALARM = 10 - NOTIFICATION = 11 - - class ActionDescription(Stub): - PLAY_DESCRIPTION = "play" - RINGTONE_DESCRIPTION = "ringtone" - TRANSFER_DESCRIPTION = "transfer" - OUTPUT_DESCRIPTION = "output" - PREVIEW_DESCRIPTION = "preview" - EXECUTE_DESCRIPTION = "execute" - DISPLAY_DESCRIPTION = "display" - PRINT_DESCRIPTION = "print" - WALLPAPER_DESCRIPTION = "wallpaper" - ALARM_DESCRIPTION = "alarm" - NOTIFICATION_DESCRIPTION = "notification" - - # ═══════════════════════════════════════════════════ - # DrmStore.ConstraintsColumns - # ═══════════════════════════════════════════════════ - - class ConstraintsColumns(Stub): - """DrmStore.ConstraintsColumns — DRM 约束列""" - - MAX_REPEAT_COUNT = "max_repeat_count" - REMAINING_REPEAT_COUNT = "remaining_repeat_count" - LICENSE_START_TIME = "license_start_time" - LICENSE_EXPIRY_TIME = "license_expiry_time" - AVAILABLE_PERIOD = "available_period" - EXTENDED_METADATA = "extended_metadata" - LICENSE_AVAILABLE_TIME = "license_available_time" - LICENSE_AVAILABLE_PERIOD = "license_available_period" - LICENSE_USE_COUNT = "license_use_count" - LICENSE_USE_LIMIT = "license_use_limit" - LICENSE_RIGHTS = "license_rights" - LICENSE_RIGHTS_INFO = "license_rights_info" - LICENSE_CATEGORY = "license_category" - LICENSE_VERSION = "license_version" - - # ═══════════════════════════════════════════════════ - # DrmStore.DrmObjectType - # ═══════════════════════════════════════════════════ - - class DrmObjectType(Stub): - """DrmStore.DrmObjectType — DRM 对象类型""" - - UNKNOWN = 0 - CONTENT = 1 - RIGHTS_OBJECT = 2 - TRIGGER_OBJECT = 3 - - # ═══════════════════════════════════════════════════ - # DrmStore.PlaybackCtrl - # ═══════════════════════════════════════════════════ - - class PlaybackCtrl(Stub): - """DrmStore.PlaybackCtrl — 播放控制""" - - START = 0 - STOP = 1 - PAUSE = 2 - RESUME = 3 - - # ═══════════════════════════════════════════════════ - # DrmStore.RightsStatus - # ═══════════════════════════════════════════════════ - - class RightsStatus(Stub): - """DrmStore.RightsStatus — 权限状态""" - - RIGHTS_VALID = 0 - RIGHTS_INVALID = 1 - RIGHTS_EXPIRED = 2 - RIGHTS_NOT_ACQUIRED = 3 - RIGHTS_PROTECTED = 4 - RIGHTS_UNPROTECTED = 5 - RIGHTS_NOT_INSTALLED = 6 - RIGHTS_INSTALLED = 7 - RIGHTS_META = 8 - RIGHTS_COPYRIGHT = 9 - RIGHTS_COPYRIGHT_EXPIRED = 10 - RIGHTS_NOT_COPYRIGHT = 11 - RIGHTS_SECURE = 12 - RIGHTS_NOT_SECURE = 13 - RIGHTS_UNLIMITED = 14 - RIGHTS_LIMITED = 15 - - # ═══════════════════════════════════════════════════ - # DrmStore.DrmMethod - # ═══════════════════════════════════════════════════ - - class DrmMethod(Stub): - """DrmStore.DrmMethod — DRM 方法""" - - METHOD_UNKNOWN = 0 - METHOD_FL = 1 - METHOD_NONE = 2 - METHOD_FLD = 3 - METHOD_SD = 4 - METHOD_SD_PRELOAD = 5 - METHOD_SD_DOWNLOAD = 6 - METHOD_FLD_FOREGROUND = 7 - METHOD_FLD_BACKGROUND = 8 - - # ═══════════════════════════════════════════════════ - # DrmStore.DrmStatus - # ═══════════════════════════════════════════════════ - - class DrmStatus(Stub): - """DrmStore.DrmStatus — DRM 状态""" - - STATUS_OK = 0 - STATUS_ERROR = 1 - STATUS_WAITING = 2 - STATUS_CANCELLED = 3 - STATUS_REJECTED = 4 - STATUS_NOT_SUPPORTED = 5 - STATUS_NO_RIGHTS = 6 - STATUS_NO_PERMISSION = 7 - STATUS_PENDING = 8 - STATUS_PROCESSING = 9 - STATUS_COMPLETED = 10 - STATUS_FAILED = 11 - STATUS_INTERRUPTED = 12 - STATUS_TIMEOUT = 13 - STATUS_NO_MEMORY = 14 - STATUS_SERVICE_NOT_FOUND = 15 - STATUS_SERVICE_NOT_AVAILABLE = 16 - STATUS_INVALID_ARGUMENT = 17 - STATUS_ILLEGAL_STATE = 18 - STATUS_NETWORK_ERROR = 19 - STATUS_STORAGE_ERROR = 20 - STATUS_SECURITY_ERROR = 21 - STATUS_LICENSE_EXPIRED = 22 - STATUS_LICENSE_NOT_FOUND = 23 - STATUS_LICENSE_NOT_AVAILABLE = 24 - - DRM_METHOD_FL = 1 - DRM_METHOD_NONE = 2 - DRM_METHOD_UNKNOWN = 0 - - EXTENSION_NONE = "none" - EXTENSION_FL = ".fl" - EXTENSION_DCF = ".dcf" - EXTENSION_DM = ".dm" - EXTENSION_ODF = ".odf" - EXTENSION_SDCF = ".sdcf" - EXTENSION_FLD = ".fld" - - -# ═══════════════════════════════════════════════════════════════ -# DrmInfo -# ═══════════════════════════════════════════════════════════════ - -class DrmInfo(Stub): - """DrmInfo — DRM 信息""" - - def __init__(self, mime_type: str = "", data: bytes = b""): - super().__init__() - self._mime_type = mime_type - self._data = data - self._attributes: Dict[str, str] = {} - self._info_type: int = 0 - self._content_type: str = "" - self._content_uri: str = "" - self._content_data: bytes = b"" - self._is_valid: bool = True - self._metadata: Dict[str, str] = {} - - def getMimeType(self) -> str: - return self._mime_type - - def setMimeType(self, mime_type: str): - self._mime_type = mime_type - - def getData(self) -> bytes: - return self._data - - def setData(self, data: bytes): - self._data = data - - def get(self, key: str) -> Optional[str]: - return self._attributes.get(key) - - def put(self, key: str, value: str): - self._attributes[key] = value - - def getAttributes(self) -> Dict[str, str]: - return dict(self._attributes) - - def getInfoType(self) -> int: - return self._info_type - - def setInfoType(self, type_: int): - self._info_type = type_ - - def getContentType(self) -> str: - return self._content_type - - def setContentType(self, type_: str): - self._content_type = type_ - - def getContentUri(self) -> str: - return self._content_uri - - def setContentUri(self, uri: str): - self._content_uri = uri - - def getContentData(self) -> bytes: - return self._content_data - - def setContentData(self, data: bytes): - self._content_data = data - - def isValid(self) -> bool: - return self._is_valid - - def setValid(self, valid: bool): - self._is_valid = valid - - def getMetadata(self) -> Dict[str, str]: - return dict(self._metadata) - - def setMetadata(self, metadata: Dict[str, str]): - self._metadata = dict(metadata) - - def toString(self) -> str: - return f"DrmInfo(mime={self._mime_type}, type={self._info_type})" - - -# ═══════════════════════════════════════════════════════════════ -# DrmInfoRequest -# ═══════════════════════════════════════════════════════════════ - -class DrmInfoRequest(Stub): - """DrmInfoRequest — DRM 信息请求""" - - TYPE_REGISTRATION_INFO = 1 - TYPE_UNREGISTRATION_INFO = 2 - TYPE_RIGHTS_ACQUISITION_INFO = 3 - TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO = 4 - TYPE_MERGE_INFO = 5 - TYPE_REMOVE_INFO = 6 - TYPE_ACCOUNT_INFO = 7 - TYPE_ACCOUNT_REGISTRATION_INFO = 8 - TYPE_ACCOUNT_UNREGISTRATION_INFO = 9 - TYPE_ACCOUNT_UPDATE_INFO = 10 - TYPE_ADAPTER_INFO = 11 - TYPE_ADAPTER_CONFIG_INFO = 12 - TYPE_ADAPTER_REGISTRATION_INFO = 13 - TYPE_METADATA_INFO = 14 - - def __init__(self, info_type: int = TYPE_REGISTRATION_INFO, - mime_type: str = ""): - super().__init__() - self._info_type = info_type - self._mime_type = mime_type - self._attributes: Dict[str, str] = {} - - def getInfoType(self) -> int: - return self._info_type - - def getMimeType(self) -> str: - return self._mime_type - - def get(self, key: str) -> Optional[str]: - return self._attributes.get(key) - - def put(self, key: str, value: str): - self._attributes[key] = value - - def getAttributes(self) -> Dict[str, str]: - return dict(self._attributes) - - def toString(self) -> str: - return f"DrmInfoRequest(type={self._info_type}, mime={self._mime_type})" - - -# ═══════════════════════════════════════════════════════════════ -# DrmInfoResponse -# ═══════════════════════════════════════════════════════════════ - -class DrmInfoResponse(Stub): - """DrmInfoResponse — DRM 信息响应""" - - def __init__(self): - super().__init__() - self._info: Optional[DrmInfo] = None - self._status: int = 0 - self._error_message: str = "" - self._request: Optional[DrmInfoRequest] = None - self._response_data: bytes = b"" - self._timestamp: int = 0 - - def getInfo(self) -> Optional[DrmInfo]: - return self._info - - def setInfo(self, info: DrmInfo): - self._info = info - - def getStatus(self) -> int: - return self._status - - def setStatus(self, status: int): - self._status = status - - def getErrorMessage(self) -> str: - return self._error_message - - def setErrorMessage(self, msg: str): - self._error_message = msg - - def getRequest(self) -> Optional[DrmInfoRequest]: - return self._request - - def setRequest(self, request: DrmInfoRequest): - self._request = request - - def getResponseData(self) -> bytes: - return self._response_data - - def setResponseData(self, data: bytes): - self._response_data = data - - def getTimestamp(self) -> int: - return self._timestamp - - def setTimestamp(self, ts: int): - self._timestamp = ts - - def isSuccessful(self) -> bool: - return self._status == DrmStore.DrmStatus.STATUS_OK - - def toString(self) -> str: - return f"DrmInfoResponse(status={self._status})" - - -# ═══════════════════════════════════════════════════════════════ -# DrmConvertedStatus -# ═══════════════════════════════════════════════════════════════ - -class DrmConvertedStatus(Stub): - """DrmConvertedStatus — DRM 转换状态""" - - STATUS_OK = 0 - STATUS_ERROR = 1 - STATUS_INPUT_DATA_ERROR = 2 - STATUS_OPERATION_NOT_SUPPORTED = 3 - STATUS_INTERNAL_ERROR = 4 - STATUS_INVALID_DATA = 5 - STATUS_NO_RIGHTS = 6 - STATUS_ENCRYPTION_ERROR = 7 - - def __init__(self, status_code: int = STATUS_OK, - converted_data: bytes = b"", - mime_type: str = ""): - super().__init__() - self._status_code = status_code - self._converted_data = converted_data - self._mime_type = mime_type - self._converted_file_path: str = "" - self._original_file_path: str = "" - self._conversion_time: int = 0 - - def getStatusCode(self) -> int: - return self._status_code - - def setStatusCode(self, code: int): - self._status_code = code - - def getConvertedData(self) -> bytes: - return self._converted_data - - def setConvertedData(self, data: bytes): - self._converted_data = data - - def getMimeType(self) -> str: - return self._mime_type - - def setMimeType(self, mime_type: str): - self._mime_type = mime_type - - def getConvertedFilePath(self) -> str: - return self._converted_file_path - - def setConvertedFilePath(self, path: str): - self._converted_file_path = path - - def getOriginalFilePath(self) -> str: - return self._original_file_path - - def setOriginalFilePath(self, path: str): - self._original_file_path = path - - def isSuccessful(self) -> bool: - return self._status_code == DrmConvertedStatus.STATUS_OK - - def toString(self) -> str: - return f"DrmConvertedStatus(status={self._status_code})" - - -# ═══════════════════════════════════════════════════════════════ -# DrmEvent -# ═══════════════════════════════════════════════════════════════ - -class DrmEvent(Stub): - """DrmEvent — DRM 事件""" - - TYPE_ALL_RIGHTS_REMOVED = 1001 - TYPE_DRM_INFO_PROCESSED = 1002 - TYPE_RIGHTS_INSTALLED = 1003 - TYPE_RIGHTS_REMOVED = 1004 - TYPE_RIGHTS_EXPIRED = 1005 - TYPE_RIGHTS_ACQUISITION_STARTED = 1006 - TYPE_RIGHTS_ACQUISITION_FINISHED = 1007 - TYPE_RIGHTS_ACQUISITION_PROGRESS = 1008 - TYPE_RIGHTS_ACQUISITION_FAILED = 1009 - TYPE_META_DATA_AVAILABLE = 1010 - TYPE_META_DATA_UPDATED = 1011 - TYPE_META_DATA_REMOVED = 1012 - TYPE_ACCOUNT_ALREADY_REGISTERED = 1013 - TYPE_ACCOUNT_REGISTERED = 1014 - TYPE_ACCOUNT_UNREGISTERED = 1015 - - def __init__(self, unique_id: int = 0, type_: int = 0, - message: str = ""): - super().__init__() - self._unique_id = unique_id - self._type = type_ - self._message = message - self._attributes: Dict[str, Any] = {} - self._timestamp: int = int(time.time() * 1000) - - def getUniqueId(self) -> int: - return self._unique_id - - def getType(self) -> int: - return self._type - - def getMessage(self) -> str: - return self._message - - def getTimestamp(self) -> int: - return self._timestamp - - def getAttribute(self, key: str) -> Optional[Any]: - return self._attributes.get(key) - - def getAttributes(self) -> Dict[str, Any]: - return dict(self._attributes) - - def setAttribute(self, key: str, value: Any): - self._attributes[key] = value - - def toString(self) -> str: - return f"DrmEvent(type={self._type}, msg={self._message})" - - -# ═══════════════════════════════════════════════════════════════ -# DrmErrorEvent -# ═══════════════════════════════════════════════════════════════ - -class DrmErrorEvent(DrmEvent): - """DrmErrorEvent — DRM 错误事件""" - - TYPE_RIGHTS_NOT_INSTALLED = 2001 - TYPE_RIGHTS_ACQUISITION_FAILED = 2002 - TYPE_RIGHTS_NOT_ACQUIRED = 2003 - TYPE_PROCESS_DRM_INFO_FAILED = 2004 - TYPE_REMOVE_ALL_RIGHTS_FAILED = 2005 - TYPE_ACQUIRE_RIGHTS_FAILED = 2006 - TYPE_SIGNATURE_MISMATCH = 2007 - TYPE_INVALID_DRM_INFO = 2008 - TYPE_CERTIFICATE_REVOKED = 2009 - TYPE_LICENSE_EXPIRED = 2010 - TYPE_LICENSE_NOT_FOUND = 2011 - TYPE_SECURITY_VIOLATION = 2012 - TYPE_NETWORK_ERROR = 2013 - - def __init__(self, unique_id: int = 0, type_: int = 0, - message: str = ""): - super().__init__(unique_id, type_, message) - self._error_code: int = 0 - self._recoverable: bool = False - self._stack_trace: str = "" - - def getErrorCode(self) -> int: - return self._error_code - - def setErrorCode(self, code: int): - self._error_code = code - - def isRecoverable(self) -> bool: - return self._recoverable - - def setRecoverable(self, recoverable: bool): - self._recoverable = recoverable - - def getStackTrace(self) -> str: - return self._stack_trace - - def setStackTrace(self, trace: str): - self._stack_trace = trace - - -# ═══════════════════════════════════════════════════════════════ -# DrmInfoEvent -# ═══════════════════════════════════════════════════════════════ - -class DrmInfoEvent(DrmEvent): - """DrmInfoEvent — DRM 信息事件""" - - TYPE_ACCOUNT_ALREADY_REGISTERED = 3001 - TYPE_ACCOUNT_REGISTERED = 3002 - TYPE_ACCOUNT_UNREGISTERED = 3003 - TYPE_ACCOUNT_REGISTRATION_COMPLETED = 3004 - TYPE_ACCOUNT_REGISTRATION_FAILED = 3005 - TYPE_ACCOUNT_UNREGISTRATION_COMPLETED = 3006 - TYPE_ACCOUNT_UNREGISTRATION_FAILED = 3007 - TYPE_ADAPTER_REGISTRATION_COMPLETED = 3008 - TYPE_ADAPTER_REGISTRATION_FAILED = 3009 - TYPE_METADATA_RETRIEVED = 3010 - TYPE_METADATA_RETRIEVAL_FAILED = 3011 - TYPE_RIGHTS_ACQUISITION_PROGRESS = 3012 - TYPE_RIGHTS_ACQUISITION_COMPLETED = 3013 - TYPE_RIGHTS_ACQUISITION_FAILED = 3014 - - def __init__(self, unique_id: int = 0, type_: int = 0, - message: str = ""): - super().__init__(unique_id, type_, message) - self._data: Any = None - self._info_request: Optional[DrmInfoRequest] = None - - def getData(self) -> Any: - return self._data - - def setData(self, data: Any): - self._data = data - - def getInfoRequest(self) -> Optional[DrmInfoRequest]: - return self._info_request - - def setInfoRequest(self, request: DrmInfoRequest): - self._info_request = request - - -# ═══════════════════════════════════════════════════════════════ -# DrmManagerClient -# ═══════════════════════════════════════════════════════════════ - -class DrmManagerClient(Stub): - """DrmManagerClient — DRM 管理器客户端""" - - ERROR_NONE = 0 - ERROR_UNKNOWN = -1 - ERROR_NOT_INITIALIZED = -2 - ERROR_NOT_SUPPORTED = -3 - ERROR_INVALID_ARGUMENT = -4 - ERROR_PERMISSION_DENIED = -5 - ERROR_SERVICE_NOT_FOUND = -6 - ERROR_SERVICE_NOT_AVAILABLE = -7 - ERROR_NETWORK = -8 - ERROR_STORAGE = -9 - ERROR_IO = -10 - ERROR_INVALID_DRM_INFO = -11 - ERROR_RIGHTS_NOT_ACQUIRED = -12 - ERROR_RIGHTS_EXPIRED = -13 - ERROR_NO_RIGHTS = -14 - ERROR_INTERNAL = -99 - - class OnErrorListener(Stub): - """DrmManagerClient.OnErrorListener — 错误监听器""" - - def onError(self, client: DrmManagerClient, event: DrmErrorEvent): - pass - - class OnInfoListener(Stub): - """DrmManagerClient.OnInfoListener — 信息监听器""" - - def onInfo(self, client: DrmManagerClient, event: DrmInfoEvent): - pass - - class OnEventListener(Stub): - """DrmManagerClient.OnEventListener — 事件监听器""" - - def onEvent(self, client: DrmManagerClient, event: DrmEvent): - pass - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._unique_id: int = id(self) - self._is_initialized: bool = True - self._error_listeners: List[DrmManagerClient.OnErrorListener] = [] - self._info_listeners: List[DrmManagerClient.OnInfoListener] = [] - self._event_listeners: List[DrmManagerClient.OnEventListener] = [] - - def acquireRights(self, drm_info_request: DrmInfoRequest) -> DrmInfoResponse: - """获取权限""" - response = DrmInfoResponse() - response.setRequest(drm_info_request) - response.setStatus(DrmStore.DrmStatus.STATUS_OK) - return response - - def processDrmInfo(self, drm_info: DrmInfo) -> DrmInfoResponse: - """处理 DRM 信息""" - response = DrmInfoResponse() - response.setInfo(drm_info) - response.setStatus(DrmStore.DrmStatus.STATUS_OK) - return response - - def getDrmInfo(self, drm_info_request: DrmInfoRequest) -> Optional[DrmInfo]: - """获取 DRM 信息""" - drm_info = DrmInfo(drm_info_request.getMimeType()) - drm_info.setInfoType(drm_info_request.getInfoType()) - return drm_info - - def getConstraints(self, path: str, action: int) -> Dict[str, Any]: - """获取约束""" - return {} - - def getMetadata(self, path: str) -> Dict[str, str]: - """获取元数据""" - return {} - - def canHandle(self, path: str, mime_type: str) -> bool: - """检查能否处理""" - return True - - def removeAllListener(self): - """移除所有监听器""" - self._error_listeners.clear() - self._info_listeners.clear() - self._event_listeners.clear() - - def removeListener(self, listener: Any): - """移除监听器""" - if listener in self._error_listeners: - self._error_listeners.remove(listener) - if listener in self._info_listeners: - self._info_listeners.remove(listener) - if listener in self._event_listeners: - self._event_listeners.remove(listener) - - def setOnErrorListener(self, listener: OnErrorListener): - self._error_listeners.append(listener) - - def setOnInfoListener(self, listener: OnInfoListener): - self._info_listeners.append(listener) - - def setOnEventListener(self, listener: OnEventListener): - self._event_listeners.append(listener) - - def getUniqueId(self) -> int: - return self._unique_id - - def release(self): - """释放客户端""" - self._is_initialized = False - self.removeAllListener() - - def isInitialized(self) -> bool: - return self._is_initialized - - def toString(self) -> str: - return f"DrmManagerClient(id={self._unique_id})" - - -# ═══════════════════════════════════════════════════════════════ -# DrmOutputStream -# ═══════════════════════════════════════════════════════════════ - -class DrmOutputStream(Stub): - """DrmOutputStream — DRM 输出流""" - - def __init__(self, file_path: str = ""): - super().__init__() - self._file_path = file_path - self._is_open: bool = False - self._is_finished: bool = False - self._buffer: bytes = b"" - self._bytes_written: int = 0 - self._mime_type: str = "" - self._drm_info: Optional[DrmInfo] = None - - def open(self) -> bool: - """打开输出流""" - self._is_open = True - return True - - def write(self, data: bytes) -> int: - """写入数据""" - if not self._is_open: - return -1 - self._buffer += data - self._bytes_written += len(data) - return len(data) - - def close(self): - """关闭输出流""" - if self._is_open: - self._is_open = False - self._is_finished = True - - def finish(self): - """完成写入""" - self._is_finished = True - - def getFilePath(self) -> str: - return self._file_path - - def setFilePath(self, path: str): - self._file_path = path - - def isOpen(self) -> bool: - return self._is_open - - def isFinished(self) -> bool: - return self._is_finished - - def getBytesWritten(self) -> int: - return self._bytes_written - - def getBuffer(self) -> bytes: - return self._buffer - - def getMimeType(self) -> str: - return self._mime_type - - def setMimeType(self, mime_type: str): - self._mime_type = mime_type - - def toString(self) -> str: - return f"DrmOutputStream(path={self._file_path}, written={self._bytes_written})" - - -# ═══════════════════════════════════════════════════════════════ -# DrmRights -# ═══════════════════════════════════════════════════════════════ - -class DrmRights(Stub): - """DrmRights — DRM 权限""" - - def __init__(self, rights_data: bytes = b"", mime_type: str = "", - account_id: str = "", subscription_id: str = ""): - super().__init__() - self._rights_data = rights_data - self._mime_type = mime_type - self._account_id = account_id - self._subscription_id = subscription_id - self._is_valid: bool = True - self._expiry_time: int = 0 - self._issued_time: int = int(time.time() * 1000) - - def getData(self) -> bytes: - return self._rights_data - - def setData(self, data: bytes): - self._rights_data = data - - def getMimeType(self) -> str: - return self._mime_type - - def setMimeType(self, mime_type: str): - self._mime_type = mime_type - - def getAccountId(self) -> str: - return self._account_id - - def setAccountId(self, account_id: str): - self._account_id = account_id - - def getSubscriptionId(self) -> str: - return self._subscription_id - - def setSubscriptionId(self, sub_id: str): - self._subscription_id = sub_id - - def isValid(self) -> bool: - return self._is_valid and (self._expiry_time == 0 or - self._expiry_time > int(time.time() * 1000)) - - def setValid(self, valid: bool): - self._is_valid = valid - - def getExpiryTime(self) -> int: - return self._expiry_time - - def setExpiryTime(self, expiry_ms: int): - self._expiry_time = expiry_ms - - def getIssuedTime(self) -> int: - return self._issued_time - - def isExpired(self) -> bool: - if self._expiry_time == 0: - return False - return self._expiry_time < int(time.time() * 1000) - - def toString(self) -> str: - return f"DrmRights(mime={self._mime_type}, valid={self._is_valid})" - - -# ═══════════════════════════════════════════════════════════════ -# DrmSupportInfo -# ═══════════════════════════════════════════════════════════════ - -class DrmSupportInfo(Stub): - """DrmSupportInfo — DRM 支持信息""" - - def __init__(self): - super().__init__() - self._mime_types: Set[str] = set() - self._file_suffixes: Set[str] = set() - self._descriptions: List[str] = [] - self._plug_in_id: str = "" - self._vendor_name: str = "" - self._plugin_version: str = "" - self._is_available: bool = True - - def addMimeType(self, mime_type: str): - self._mime_types.add(mime_type) - - def addFileSuffix(self, file_suffix: str): - self._file_suffixes.add(file_suffix) - - def addDescription(self, description: str): - self._descriptions.append(description) - - def getMimeTypes(self) -> Set[str]: - return set(self._mime_types) - - def getFileSuffixes(self) -> Set[str]: - return set(self._file_suffixes) - - def getDescriptions(self) -> List[str]: - return list(self._descriptions) - - def isSupportedMimeType(self, mime_type: str) -> bool: - return mime_type in self._mime_types - - def isSupportedFileSuffix(self, suffix: str) -> bool: - return suffix in self._file_suffixes - - def getPlugInId(self) -> str: - return self._plug_in_id - - def setPlugInId(self, plugin_id: str): - self._plug_in_id = plugin_id - - def getVendorName(self) -> str: - return self._vendor_name - - def setVendorName(self, name: str): - self._vendor_name = name - - def getPluginVersion(self) -> str: - return self._plugin_version - - def setPluginVersion(self, version: str): - self._plugin_version = version - - def isAvailable(self) -> bool: - return self._is_available - - def setAvailable(self, available: bool): - self._is_available = available - - def toString(self) -> str: - return f"DrmSupportInfo(mimeTypes={len(self._mime_types)}, suffixes={len(self._file_suffixes)})" - - -# ═══════════════════════════════════════════════════════════════ -# DrmUtils -# ═══════════════════════════════════════════════════════════════ - -class DrmUtils(Stub): - """DrmUtils — DRM 工具类""" - - EXTENSION_DCF = ".dcf" - EXTENSION_FL = ".fl" - EXTENSION_DM = ".dm" - EXTENSION_ODF = ".odf" - EXTENSION_SDCF = ".sdcf" - EXTENSION_FLD = ".fld" - EXTENSION_HTML = ".html" - EXTENSION_XHTML = ".xhtml" - EXTENSION_XML = ".xml" - - MIME_TYPE_DCF = "application/vnd.oma.drm.content" - MIME_TYPE_FL = "application/vnd.oma.drm.message" - MIME_TYPE_DM = "application/vnd.oma.drm.message" - MIME_TYPE_ODF = "application/vnd.oma.drm.content" - MIME_TYPE_SDCF = "application/vnd.oma.drm.content" - MIME_TYPE_UNKNOWN = "application/octet-stream" - - @staticmethod - def getOriginalMimeType(context: Any, file_path: str) -> str: - """获取原始 MIME 类型""" - return DrmUtils.MIME_TYPE_UNKNOWN - - @staticmethod - def getOriginalFileSuffix(context: Any, file_path: str) -> str: - """获取原始文件后缀""" - _, ext = os.path.splitext(file_path) if hasattr(os.path, 'splitext') else (file_path, "") - return ext - - @staticmethod - def isDrmFile(file_path: str) -> bool: - """检查是否为 DRM 文件""" - if not file_path: - return False - suffix = os.path.splitext(file_path)[1].lower() if hasattr(os.path, 'splitext') else "" - drm_extensions = {DrmUtils.EXTENSION_DCF, DrmUtils.EXTENSION_FL, - DrmUtils.EXTENSION_DM, DrmUtils.EXTENSION_ODF, - DrmUtils.EXTENSION_SDCF, DrmUtils.EXTENSION_FLD} - return suffix in drm_extensions - - @staticmethod - def isDrmMimeType(mime_type: str) -> bool: - """检查是否为 DRM MIME 类型""" - drm_mime_types = {DrmUtils.MIME_TYPE_DCF, DrmUtils.MIME_TYPE_FL, - DrmUtils.MIME_TYPE_DM, DrmUtils.MIME_TYPE_ODF, - DrmUtils.MIME_TYPE_SDCF} - return mime_type in drm_mime_types - - @staticmethod - def getDrmFileType(file_path: str) -> int: - """获取 DRM 文件类型""" - if not DrmUtils.isDrmFile(file_path): - return DrmStore.DrmObjectType.UNKNOWN - return DrmStore.DrmObjectType.CONTENT - - @staticmethod - def getSupportedMimeTypes() -> Set[str]: - """获取支持的 MIME 类型""" - return {DrmUtils.MIME_TYPE_DCF, DrmUtils.MIME_TYPE_FL, - DrmUtils.MIME_TYPE_DM, DrmUtils.MIME_TYPE_ODF, - DrmUtils.MIME_TYPE_SDCF} - - @staticmethod - def convertToDrmFile(input_path: str, output_path: str, - mime_type: str) -> DrmConvertedStatus: - """转换为 DRM 文件""" - with open(input_path, "rb") as f: - data = f.read() - status = DrmConvertedStatus(DrmConvertedStatus.STATUS_OK, data, mime_type) - status.setOriginalFilePath(input_path) - status.setConvertedFilePath(output_path) - return status - - -# ═══════════════════════════════════════════════════════════════ -# DrmManager (高层管理器) -# ═══════════════════════════════════════════════════════════════ - -class DrmManager(Stub): - """DrmManager — DRM 管理器""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._clients: Dict[int, DrmManagerClient] = {} - self._is_available: bool = True - self._support_infos: List[DrmSupportInfo] = [] - - @staticmethod - def getInstance(context: Any) -> DrmManager: - return DrmManager(context) - - def getDrmManagerClient(self) -> DrmManagerClient: - client = DrmManagerClient(self._context) - client_id = client.getUniqueId() - self._clients[client_id] = client - return client - - def releaseDrmManagerClient(self, client: DrmManagerClient): - client_id = client.getUniqueId() - if client_id in self._clients: - client.release() - del self._clients[client_id] - - def isAvailable(self) -> bool: - return self._is_available - - def setAvailable(self, available: bool): - self._is_available = available - - def getSupportInfos(self) -> List[DrmSupportInfo]: - return list(self._support_infos) - - def addSupportInfo(self, info: DrmSupportInfo): - self._support_infos.append(info) - - def removeSupportInfo(self, info: DrmSupportInfo): - self._support_infos = [i for i in self._support_infos if i is not info] - - def canHandle(self, path: str, mime_type: str) -> bool: - for client in self._clients.values(): - if client.canHandle(path, mime_type): - return True - return False - - def getAvailableClients(self) -> List[DrmManagerClient]: - return [c for c in self._clients.values() if c.isInitialized()] - - def release(self): - for client in list(self._clients.values()): - client.release() - self._clients.clear() - self._support_infos.clear() - - def toString(self) -> str: - return f"DrmManager(clients={len(self._clients)}, available={self._is_available})" \ No newline at end of file diff --git a/android_pyapi/android/gesture.py b/android_pyapi/android/gesture.py deleted file mode 100644 index fe22a03..0000000 --- a/android_pyapi/android/gesture.py +++ /dev/null @@ -1,545 +0,0 @@ -""" -Android gesture 包 — Gesture, GestureOverlayView, GesturePoint, GestureStroke, -GestureLibrary, GestureStore, Prediction, OrientedBoundingBox, GestureUtils -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import math -import sys -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "Gesture", - "GestureOverlayView", - "GesturePoint", - "GestureStroke", - "GestureLibrary", - "GestureStore", - "Prediction", - "OrientedBoundingBox", - "GestureUtils", -] - - -# ═══════════════════════════════════════════════════════════════ -# GesturePoint -# ═══════════════════════════════════════════════════════════════ - -class GesturePoint(Stub): - """GesturePoint — 手势点""" - - def __init__(self, x: float = 0.0, y: float = 0.0, timestamp: int = 0): - super().__init__() - self.x = x - self.y = y - self.timestamp = timestamp - - def toString(self) -> str: - return f"GesturePoint({self.x}, {self.y})" - - -# ═══════════════════════════════════════════════════════════════ -# OrientedBoundingBox -# ═══════════════════════════════════════════════════════════════ - -class OrientedBoundingBox(Stub): - """OrientedBoundingBox — 有向包围盒""" - - def __init__(self, center_x: float = 0.0, center_y: float = 0.0, - width: float = 0.0, height: float = 0.0, orientation: float = 0.0): - super().__init__() - self.centerX = center_x - self.centerY = center_y - self.width = width - self.height = height - self.orientation = orientation - - def toRect(self) -> Tuple[float, float, float, float]: - """转换为矩形""" - hw = self.width / 2.0 - hh = self.height / 2.0 - return (self.centerX - hw, self.centerY - hh, - self.centerX + hw, self.centerY + hh) - - -# ═══════════════════════════════════════════════════════════════ -# Prediction -# ═══════════════════════════════════════════════════════════════ - -class Prediction(Stub): - """Prediction — 预测结果""" - - def __init__(self, name: str = "", score: float = 0.0): - super().__init__() - self.name = name - self.score = score - - def toString(self) -> str: - return f"Prediction(name={self.name}, score={self.score})" - - -# ═══════════════════════════════════════════════════════════════ -# GestureStroke -# ═══════════════════════════════════════════════════════════════ - -class GestureStroke(Stub): - """GestureStroke — 手势笔划""" - - def __init__(self, points: Optional[List[GesturePoint]] = None): - super().__init__() - self._points = points or [] - self._path: Optional[Any] = None - self._length = self._compute_length() - self._bounding_box = OrientedBoundingBox() - - def _compute_length(self) -> float: - length = 0.0 - for i in range(1, len(self._points)): - dx = self._points[i].x - self._points[i - 1].x - dy = self._points[i].y - self._points[i - 1].y - length += math.sqrt(dx * dx + dy * dy) - return length - - def getLength(self) -> float: - """获取长度""" - return self._length - - def getPoints(self) -> List[GesturePoint]: - """获取点列表""" - return self._points.copy() - - def getPath(self) -> Optional[Any]: - """获取路径""" - return self._path - - def toPath(self, width: float = 1.0, height: float = 1.0, num_sample: int = 0) -> Any: - """转换为路径""" - return self._path - - def clearPath(self) -> None: - """清除路径""" - self._path = None - - def computeOrientedBoundingBox(self) -> OrientedBoundingBox: - """计算有向包围盒""" - return self._bounding_box - - -# ═══════════════════════════════════════════════════════════════ -# Gesture -# ═══════════════════════════════════════════════════════════════ - -class Gesture(Stub): - """Gesture — 手势""" - - def __init__(self): - super().__init__() - self._strokes: List[GestureStroke] = [] - self._id = 0 - self._bounding_box: Optional[Tuple[float, float, float, float]] = None - self._length = 0.0 - - def getStrokes(self) -> List[GestureStroke]: - """获取笔划列表""" - return self._strokes.copy() - - def getLength(self) -> float: - """获取手势长度""" - return sum(s.getLength() for s in self._strokes) - - def getBoundingBox(self) -> Tuple[float, float, float, float]: - """获取包围盒""" - if self._bounding_box: - return self._bounding_box - if not self._strokes: - return (0.0, 0.0, 0.0, 0.0) - min_x = min(p.x for s in self._strokes for p in s._points) if self._strokes else 0 - min_y = min(p.y for s in self._strokes for p in s._points) if self._strokes else 0 - max_x = max(p.x for s in self._strokes for p in s._points) if self._strokes else 0 - max_y = max(p.y for s in self._strokes for p in s._points) if self._strokes else 0 - return (min_x, min_y, max_x, max_y) - - def getID(self) -> int: - """获取 ID""" - return self._id - - def setID(self, id_: int) -> None: - """设置 ID""" - self._id = id_ - - def toPath(self) -> Optional[Any]: - """转换为路径""" - return None - - def toTempPath(self) -> Optional[Any]: - """转换为临时路径""" - return None - - def toBitmap(self) -> Optional[Any]: - """转换为位图""" - return None - - def getStrokesCount(self) -> int: - """获取笔划数量""" - return len(self._strokes) - - def getGestureStroke(self, index: int) -> Optional[GestureStroke]: - """获取指定索引的笔划""" - if 0 <= index < len(self._strokes): - return self._strokes[index] - return None - - def addStroke(self, stroke: GestureStroke) -> None: - """添加笔划""" - self._strokes.append(stroke) - - def toString(self) -> str: - return f"Gesture(id={self._id}, strokes={len(self._strokes)})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, Gesture): - return False - return self._id == other._id - - def hashCode(self) -> int: - return hash(self._id) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# GestureOverlayView -# ═══════════════════════════════════════════════════════════════ - -class GestureOverlayView(Stub): - """GestureOverlayView — 手势覆盖视图 (桌面存根)""" - - GESTURE_STROKE_TYPE_SINGLE = 0 - GESTURE_STROKE_TYPE_MULTIPLE = 1 - ORIENTATION_HORIZONTAL = 0 - ORIENTATION_VERTICAL = 1 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._gesture: Optional[Gesture] = None - self._gesture_color = 0xFFFFFF00 - self._uncertain_gesture_color = 0xFFFF0000 - self._gesture_stroke_width = 4.0 - self._gesture_stroke_type = GestureOverlayView.GESTURE_STROKE_TYPE_SINGLE - self._gesture_stroke_length_threshold = 50.0 - self._gesture_stroke_squareness_threshold = 0.5 - self._fade_enabled = True - self._fade_offset = 0 - self._fade_duration = 150 - self._events_interception_enabled = True - self._gesture_visible = True - self._gesture_listeners: List[Any] = [] - self._gesture_performed_listeners: List[Any] = [] - self._gesturing_listeners: List[Any] = [] - - def getGesture(self) -> Optional[Gesture]: - return self._gesture - - def setGesture(self, gesture: Gesture) -> None: - self._gesture = gesture - - def setGestureColor(self, color: int) -> None: - self._gesture_color = color - - def getGestureColor(self) -> int: - return self._gesture_color - - def setUncertainGestureColor(self, color: int) -> None: - self._uncertain_gesture_color = color - - def getUncertainGestureColor(self) -> int: - return self._uncertain_gesture_color - - def setGestureStrokeWidth(self, width: float) -> None: - self._gesture_stroke_width = width - - def getGestureStrokeWidth(self) -> float: - return self._gesture_stroke_width - - def setGestureStrokeType(self, type_: int) -> None: - self._gesture_stroke_type = type_ - - def getGestureStrokeType(self) -> int: - return self._gesture_stroke_type - - def setGestureStrokeLengthThreshold(self, threshold: float) -> None: - self._gesture_stroke_length_threshold = threshold - - def getGestureStrokeLengthThreshold(self) -> float: - return self._gesture_stroke_length_threshold - - def setGestureStrokeSquarenessThreshold(self, threshold: float) -> None: - self._gesture_stroke_squareness_threshold = threshold - - def getGestureStrokeSquarenessThreshold(self) -> float: - return self._gesture_stroke_squareness_threshold - - def setFadeEnabled(self, enabled: bool) -> None: - self._fade_enabled = enabled - - def isFadeEnabled(self) -> bool: - return self._fade_enabled - - def setFadeOffset(self, offset: int) -> None: - self._fade_offset = offset - - def getFadeOffset(self) -> int: - return self._fade_offset - - def setFadeDuration(self, duration: int) -> None: - self._fade_duration = duration - - def getFadeDuration(self) -> int: - return self._fade_duration - - def setEventsInterceptionEnabled(self, enabled: bool) -> None: - self._events_interception_enabled = enabled - - def isEventsInterceptionEnabled(self) -> bool: - return self._events_interception_enabled - - def setGestureVisible(self, visible: bool) -> None: - self._gesture_visible = visible - - def isGestureVisible(self) -> bool: - return self._gesture_visible - - def addOnGestureListener(self, listener: Any) -> None: - self._gesture_listeners.append(listener) - - def removeOnGestureListener(self, listener: Any) -> None: - if listener in self._gesture_listeners: - self._gesture_listeners.remove(listener) - - def removeAllOnGestureListeners(self) -> None: - self._gesture_listeners.clear() - - def addOnGesturePerformedListener(self, listener: Any) -> None: - self._gesture_performed_listeners.append(listener) - - def removeOnGesturePerformedListener(self, listener: Any) -> None: - if listener in self._gesture_performed_listeners: - self._gesture_performed_listeners.remove(listener) - - def removeAllOnGesturePerformedListeners(self) -> None: - self._gesture_performed_listeners.clear() - - def addOnGesturingListener(self, listener: Any) -> None: - self._gesturing_listeners.append(listener) - - def removeOnGesturingListener(self, listener: Any) -> None: - if listener in self._gesturing_listeners: - self._gesturing_listeners.remove(listener) - - def removeAllOnGesturingListeners(self) -> None: - self._gesturing_listeners.clear() - - def cancelGesture(self) -> None: - self._gesture = None - - def clear(self) -> None: - self._gesture = None - - -# ═══════════════════════════════════════════════════════════════ -# GestureLibrary -# ═══════════════════════════════════════════════════════════════ - -class GestureLibrary(Stub): - """GestureLibrary — 手势库 (桌面存根)""" - - ORIENTATION_INVARIANT = 0 - ORIENTATION_SENSITIVE = 1 - ORIENTATION_SEQUENTIAL_SENSITIVE = 2 - SEQUENCE_INVARIANT = 0 - SEQUENCE_SENSITIVE = 1 - - def __init__(self): - super().__init__() - self._gestures: Dict[str, List[Gesture]] = {} - self._read_only = False - self._orientation_style = GestureLibrary.ORIENTATION_SENSITIVE - self._sequence_type = GestureLibrary.SEQUENCE_SENSITIVE - - def store(self) -> bool: - """存储手势库""" - return True - - def load(self) -> bool: - """加载手势库""" - return True - - def getGestureEntries(self) -> List[str]: - """获取手势条目""" - return list(self._gestures.keys()) - - def getGestures(self, entry_name: str) -> List[Gesture]: - """获取手势列表""" - return self._gestures.get(entry_name, []) - - def recognize(self, gesture: Gesture) -> List[Prediction]: - """识别手势""" - return [] - - def removeGesture(self, entry_name: str, gesture: Gesture) -> None: - """移除手势""" - if entry_name in self._gestures and gesture in self._gestures[entry_name]: - self._gestures[entry_name].remove(gesture) - - def removeEntry(self, entry_name: str) -> None: - """移除条目""" - self._gestures.pop(entry_name, None) - - def addGesture(self, entry_name: str, gesture: Gesture) -> None: - """添加手势""" - if entry_name not in self._gestures: - self._gestures[entry_name] = [] - self._gestures[entry_name].append(gesture) - - def isReadOnly(self) -> bool: - """检查是否只读""" - return self._read_only - - def setOrientationStyle(self, style: int) -> None: - self._orientation_style = style - - def getOrientationStyle(self) -> int: - return self._orientation_style - - def setSequenceType(self, type_: int) -> None: - self._sequence_type = type_ - - def getSequenceType(self) -> int: - return self._sequence_type - - -# ═══════════════════════════════════════════════════════════════ -# GestureStore -# ═══════════════════════════════════════════════════════════════ - -class GestureStore(Stub): - """GestureStore — 手势存储 (桌面存根)""" - - def __init__(self): - super().__init__() - self._gestures: Dict[str, List[Gesture]] = {} - self._orientation_style = GestureLibrary.ORIENTATION_SENSITIVE - self._sequence_type = GestureLibrary.SEQUENCE_SENSITIVE - self._change_listener: Optional[Any] = None - - def addGesture(self, entry_name: str, gesture: Gesture) -> None: - if entry_name not in self._gestures: - self._gestures[entry_name] = [] - self._gestures[entry_name].append(gesture) - - def removeGesture(self, entry_name: str, gesture: Gesture) -> None: - if entry_name in self._gestures and gesture in self._gestures[entry_name]: - self._gestures[entry_name].remove(gesture) - - def removeEntry(self, entry_name: str) -> None: - self._gestures.pop(entry_name, None) - - def getGestureEntries(self) -> List[str]: - return list(self._gestures.keys()) - - def getGestures(self, entry_name: str) -> List[Gesture]: - return self._gestures.get(entry_name, []) - - def recognize(self, gesture: Gesture) -> List[Prediction]: - return [] - - def recognizeAsync(self, gesture: Gesture) -> None: - pass - - def setOrientationStyle(self, style: int) -> None: - self._orientation_style = style - - def getOrientationStyle(self) -> int: - return self._orientation_style - - def setSequenceType(self, type_: int) -> None: - self._sequence_type = type_ - - def getSequenceType(self) -> int: - return self._sequence_type - - def load(self, store_file: Any) -> None: - pass - - def save(self, store_file: Any) -> None: - pass - - def setChangeListener(self, listener: Any) -> None: - self._change_listener = listener - - def getChangeListener(self) -> Any: - return self._change_listener - - -# ═══════════════════════════════════════════════════════════════ -# GestureUtils -# ═══════════════════════════════════════════════════════════════ - -class GestureUtils(Stub): - """GestureUtils — 手势工具类""" - - def __init__(self): - super().__init__() - - @staticmethod - def copy(gesture: Gesture) -> Gesture: - """复制手势""" - new_gesture = Gesture() - for stroke in gesture.getStrokes(): - new_stroke = GestureStroke(stroke.getPoints()) - new_gesture.addStroke(new_stroke) - return new_gesture - - @staticmethod - def boundingBox(gesture: Gesture) -> OrientedBoundingBox: - """计算手势包围盒""" - bbox = gesture.getBoundingBox() - cx = (bbox[0] + bbox[2]) / 2.0 if bbox[2] != bbox[0] else 0.0 - cy = (bbox[1] + bbox[3]) / 2.0 if bbox[3] != bbox[1] else 0.0 - w = bbox[2] - bbox[0] - h = bbox[3] - bbox[1] - return OrientedBoundingBox(cx, cy, w, h, 0.0) - - @staticmethod - def boundingBox_stroke(stroke: GestureStroke) -> OrientedBoundingBox: - """计算笔划包围盒""" - return stroke.computeOrientedBoundingBox() - - @staticmethod - def spatialSampling(gesture: Gesture, num_samples: int) -> List[float]: - """空间采样""" - return [] - - @staticmethod - def temporalSampling(gesture: Gesture, num_samples: int) -> List[float]: - """时间采样""" - return [] - - @staticmethod - def computeOrientedBoundingBox(points: List[GesturePoint]) -> OrientedBoundingBox: - """计算有向包围盒""" - if not points: - return OrientedBoundingBox() - xs = [p.x for p in points] - ys = [p.y for p in points] - cx = sum(xs) / len(xs) - cy = sum(ys) / len(ys) - w = max(xs) - min(xs) - h = max(ys) - min(ys) - return OrientedBoundingBox(cx, cy, w, h, 0.0) \ No newline at end of file diff --git a/android_pyapi/android/gesture_ext.py b/android_pyapi/android/gesture_ext.py deleted file mode 100644 index 49495d4..0000000 --- a/android_pyapi/android/gesture_ext.py +++ /dev/null @@ -1,1158 +0,0 @@ -""" -Android Gesture 扩展组件 — GestureLibraries, GestureBuilderActivity, -GestureOverlayView (扩展), GestureStroke (扩展), GesturePoint (扩展), -各种手势识别器 (TapGesture, LongPressGesture, SwipeGesture 等) -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import math -import time -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError - -__all__ = [ - "GestureLibraries", "GestureBuilderActivity", - "GestureOverlayView", "GestureStroke", "GesturePoint", - "OrientationGesture", "FlingGesture", "SwipeGesture", - "PinchGesture", "TapGesture", "LongPressGesture", - "DoubleTapGesture", "ScrollGesture", "DragGesture", - "MultiFingerGesture", "GestureConfiguration", "GestureEvent", - "GestureRecognizer", "GesturePerformedListener", - "GestureDrawingListener", "GestureInputListener", - "GestureFeedback", "GestureFeedbackProvider", -] - - -# ═══════════════════════════════════════════════════════════════ -# 手势常量 -# ═══════════════════════════════════════════════════════════════ - -ORIENTATION_ANY = 0 -ORIENTATION_HORIZONTAL = 1 -ORIENTATION_VERTICAL = 2 -ORIENTATION_DIAGONAL_UP = 3 -ORIENTATION_DIAGONAL_DOWN = 4 -ORIENTATION_LEFT = 5 -ORIENTATION_RIGHT = 6 -ORIENTATION_UP = 7 -ORIENTATION_DOWN = 8 - -GESTURE_STATE_UNDEFINED = 0 -GESTURE_STATE_STARTED = 1 -GESTURE_STATE_IN_PROGRESS = 2 -GESTURE_STATE_COMPLETED = 3 -GESTURE_STATE_CANCELLED = 4 -GESTURE_STATE_FAILED = 5 - -GESTURE_EVENT_TYPE_TAP = 0 -GESTURE_EVENT_TYPE_DOUBLE_TAP = 1 -GESTURE_EVENT_TYPE_LONG_PRESS = 2 -GESTURE_EVENT_TYPE_SWIPE = 3 -GESTURE_EVENT_TYPE_FLING = 4 -GESTURE_EVENT_TYPE_PINCH = 5 -GESTURE_EVENT_TYPE_SCROLL = 6 -GESTURE_EVENT_TYPE_DRAG = 7 -GESTURE_EVENT_TYPE_ROTATION = 8 -GESTURE_EVENT_TYPE_MULTI_FINGER_TAP = 9 -GESTURE_EVENT_TYPE_ORIENTATION = 10 - -GESTURE_LIBRARY_FILE = 0 -GESTURE_LIBRARY_RESOURCE = 1 -GESTURE_LIBRARY_RAW = 2 -GESTURE_LIBRARY_PRIVATE = 3 - -GESTURE_DIRECTION_ANY = 0 -GESTURE_DIRECTION_LEFT = 1 -GESTURE_DIRECTION_RIGHT = 2 -GESTURE_DIRECTION_UP = 3 -GESTURE_DIRECTION_DOWN = 4 -GESTURE_DIRECTION_HORIZONTAL = 5 -GESTURE_DIRECTION_VERTICAL = 6 - -GESTURE_FEEDBACK_NONE = 0 -GESTURE_FEEDBACK_VIBRATE = 1 -GESTURE_FEEDBACK_SOUND = 2 -GESTURE_FEEDBACK_VISUAL = 4 -GESTURE_FEEDBACK_ALL = 7 - - -# ═══════════════════════════════════════════════════════════════ -# GesturePoint (扩展) -# ═══════════════════════════════════════════════════════════════ - -class GesturePoint(Stub): - """GesturePoint — 手势点 (完整版)""" - - def __init__(self, x: float = 0.0, y: float = 0.0, - timestamp: int = 0, pressure: float = 1.0, - size: float = 0.0, pointerId: int = 0): - super().__init__() - self._x = x - self._y = y - self._timestamp = timestamp - self._pressure = pressure - self._size = size - self._pointer_id = pointerId - - def getX(self) -> float: - return self._x - - def getY(self) -> float: - return self._y - - def getTimestamp(self) -> int: - return self._timestamp - - def getPressure(self) -> float: - return self._pressure - - def getSize(self) -> float: - return self._size - - def getPointerId(self) -> int: - return self._pointer_id - - def distance(self, other: "GesturePoint") -> float: - dx = self._x - other._x - dy = self._y - other._y - return math.sqrt(dx * dx + dy * dy) - - def velocity(self, other: "GesturePoint") -> float: - dt = self._timestamp - other._timestamp - if dt <= 0: - return 0.0 - dist = self.distance(other) - return dist / dt * 1000.0 - - def toString(self) -> str: - return f"GesturePoint({self._x:.1f}, {self._y:.1f})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, GesturePoint): - return False - return (self._x == other._x and self._y == other._y and - self._timestamp == other._timestamp) - - def hashCode(self) -> int: - return hash((self._x, self._y, self._timestamp)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# GestureStroke (扩展) -# ═══════════════════════════════════════════════════════════════ - -class GestureStroke(Stub): - """GestureStroke — 手势笔画 (完整版)""" - - def __init__(self, points: List[GesturePoint] = None): - super().__init__() - self._points = points or [] - self._length = 0.0 - self._bounding_box = (0, 0, 0, 0) - self._orientation = 0.0 - self._recalculate() - - def _recalculate(self): - if not self._points: - self._length = 0.0 - self._bounding_box = (0, 0, 0, 0) - return - min_x = min(p.getX() for p in self._points) - max_x = max(p.getX() for p in self._points) - min_y = min(p.getY() for p in self._points) - max_y = max(p.getY() for p in self._points) - self._bounding_box = (min_x, min_y, max_x - min_x, max_y - min_y) - length = 0.0 - for i in range(1, len(self._points)): - length += self._points[i - 1].distance(self._points[i]) - self._length = length - - def getPoints(self) -> List[GesturePoint]: - return list(self._points) - - def getLength(self) -> float: - return self._length - - def getBoundingBox(self) -> Tuple[float, float, float, float]: - return self._bounding_box - - def getBoundingBoxCenter(self) -> Tuple[float, float]: - x, y, w, h = self._bounding_box - return (x + w / 2, y + h / 2) - - def getOrientation(self) -> float: - return self._orientation - - def addPoint(self, point: GesturePoint): - self._points.append(point) - self._recalculate() - - def clear(self): - self._points.clear() - self._recalculate() - - def isLongEnough(self, minLength: float) -> bool: - return self._length >= minLength - - def isShortEnough(self, maxLength: float) -> bool: - return self._length <= maxLength - - def getDuration(self) -> int: - if len(self._points) < 2: - return 0 - return self._points[-1].getTimestamp() - self._points[0].getTimestamp() - - def toString(self) -> str: - return f"GestureStroke(points={len(self._points)}, length={self._length:.1f})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, GestureStroke): - return False - return (self._length == other._length and - self._bounding_box == other._bounding_box) - - def hashCode(self) -> int: - return hash(self._bounding_box) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# GestureConfiguration -# ═══════════════════════════════════════════════════════════════ - -class GestureConfiguration(Stub): - """GestureConfiguration — 手势配置""" - - def __init__(self): - super().__init__() - self._tap_timeout_ms = 300 - self._double_tap_timeout_ms = 400 - self._long_press_timeout_ms = 500 - self._swipe_min_distance = 50.0 - self._swipe_max_distance = 500.0 - self._fling_min_velocity = 200.0 - self._pinch_min_span = 20.0 - self._multi_finger_min_count = 2 - self._gesture_min_length = 10.0 - self._gesture_max_length = 500.0 - self._orientation_slop = 30.0 - - def getTapTimeoutMs(self) -> int: - return self._tap_timeout_ms - - def setTapTimeoutMs(self, timeout: int): - self._tap_timeout_ms = timeout - - def getDoubleTapTimeoutMs(self) -> int: - return self._double_tap_timeout_ms - - def setDoubleTapTimeoutMs(self, timeout: int): - self._double_tap_timeout_ms = timeout - - def getLongPressTimeoutMs(self) -> int: - return self._long_press_timeout_ms - - def setLongPressTimeoutMs(self, timeout: int): - self._long_press_timeout_ms = timeout - - def getSwipeMinDistance(self) -> float: - return self._swipe_min_distance - - def setSwipeMinDistance(self, distance: float): - self._swipe_min_distance = distance - - def getSwipeMaxDistance(self) -> float: - return self._swipe_max_distance - - def setSwipeMaxDistance(self, distance: float): - self._swipe_max_distance = distance - - def getFlingMinVelocity(self) -> float: - return self._fling_min_velocity - - def setFlingMinVelocity(self, velocity: float): - self._fling_min_velocity = velocity - - def getPinchMinSpan(self) -> float: - return self._pinch_min_span - - def setPinchMinSpan(self, span: float): - self._pinch_min_span = span - - def getMultiFingerMinCount(self) -> int: - return self._multi_finger_min_count - - def setMultiFingerMinCount(self, count: int): - self._multi_finger_min_count = count - - def getGestureMinLength(self) -> float: - return self._gesture_min_length - - def setGestureMinLength(self, length: float): - self._gesture_min_length = length - - def getGestureMaxLength(self) -> float: - return self._gesture_max_length - - def setGestureMaxLength(self, length: float): - self._gesture_max_length = length - - def getOrientationSlop(self) -> float: - return self._orientation_slop - - def setOrientationSlop(self, slop: float): - self._orientation_slop = slop - - def toString(self) -> str: - return f"GestureConfiguration(tap={self._tap_timeout_ms}ms)" - - -# ═══════════════════════════════════════════════════════════════ -# GestureEvent -# ═══════════════════════════════════════════════════════════════ - -class GestureEvent(Stub): - """GestureEvent — 手势事件""" - - def __init__(self, eventType: int = GESTURE_EVENT_TYPE_TAP, - position: Tuple[float, float] = (0.0, 0.0), - velocity: float = 0.0, direction: int = GESTURE_DIRECTION_ANY, - span: float = 0.0, fingerCount: int = 1, - stroke: Optional[GestureStroke] = None, - timestamp: int = 0, state: int = GESTURE_STATE_COMPLETED): - super().__init__() - self._event_type = eventType - self._x, self._y = position - self._velocity = velocity - self._direction = direction - self._span = span - self._finger_count = fingerCount - self._stroke = stroke - self._timestamp = timestamp or int(time.time() * 1000) - self._state = state - - def getEventType(self) -> int: - return self._event_type - - def getX(self) -> float: - return self._x - - def getY(self) -> float: - return self._y - - def getVelocity(self) -> float: - return self._velocity - - def getDirection(self) -> int: - return self._direction - - def getSpan(self) -> float: - return self._span - - def getFingerCount(self) -> int: - return self._finger_count - - def getStroke(self) -> Optional[GestureStroke]: - return self._stroke - - def getTimestamp(self) -> int: - return self._timestamp - - def getState(self) -> int: - return self._state - - def toString(self) -> str: - return (f"GestureEvent(type={self._event_type}, " - f"pos=({self._x:.0f},{self._y:.0f}))") - - def equals(self, other: Any) -> bool: - if not isinstance(other, GestureEvent): - return False - return (self._event_type == other._event_type and - self._timestamp == other._timestamp) - - def hashCode(self) -> int: - return hash((self._event_type, self._timestamp)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# 手势监听器 -# ═══════════════════════════════════════════════════════════════ - -class GesturePerformedListener(Stub): - """GesturePerformedListener — 手势执行监听器""" - - def onGesturePerformed(self, gesture: GestureStroke): - pass - - def toString(self) -> str: - return f"GesturePerformedListener@{hex(id(self))}" - - -class GestureDrawingListener(Stub): - """GestureDrawingListener — 手势绘制监听器""" - - def onGestureStarted(self, stroke: GestureStroke): - pass - - def onGestureUpdated(self, stroke: GestureStroke): - pass - - def onGestureCompleted(self, stroke: GestureStroke): - pass - - def onGestureCancelled(self, stroke: GestureStroke): - pass - - def toString(self) -> str: - return f"GestureDrawingListener@{hex(id(self))}" - - -class GestureInputListener(Stub): - """GestureInputListener — 手势输入监听器""" - - def onGestureEvent(self, event: GestureEvent): - pass - - def toString(self) -> str: - return f"GestureInputListener@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# GestureFeedback / GestureFeedbackProvider -# ═══════════════════════════════════════════════════════════════ - -class GestureFeedback(Stub): - """GestureFeedback — 手势反馈""" - - def __init__(self, type_: int = GESTURE_FEEDBACK_NONE, - intensity: float = 1.0, durationMs: int = 50): - super().__init__() - self._type = type_ - self._intensity = intensity - self._duration_ms = durationMs - - def getType(self) -> int: - return self._type - - def getIntensity(self) -> float: - return self._intensity - - def getDurationMs(self) -> int: - return self._duration_ms - - def toString(self) -> str: - return f"GestureFeedback(type={self._type})" - - -class GestureFeedbackProvider(Stub): - """GestureFeedbackProvider — 手势反馈提供器""" - - def __init__(self): - super().__init__() - self._vibrate_enabled = True - self._sound_enabled = False - self._visual_enabled = False - - def provideFeedback(self, feedback: GestureFeedback): - pass - - def isVibrateEnabled(self) -> bool: - return self._vibrate_enabled - - def setVibrateEnabled(self, enabled: bool): - self._vibrate_enabled = enabled - - def isSoundEnabled(self) -> bool: - return self._sound_enabled - - def setSoundEnabled(self, enabled: bool): - self._sound_enabled = enabled - - def isVisualEnabled(self) -> bool: - return self._visual_enabled - - def setVisualEnabled(self, enabled: bool): - self._visual_enabled = enabled - - def toString(self) -> str: - return f"GestureFeedbackProvider(vibrate={self._vibrate_enabled})" - - -# ═══════════════════════════════════════════════════════════════ -# GestureRecognizer -# ═══════════════════════════════════════════════════════════════ - -class GestureRecognizer(Stub): - """GestureRecognizer — 手势识别器基类""" - - def __init__(self): - super().__init__() - self._listeners: List[GestureInputListener] = [] - self._configuration = GestureConfiguration() - self._enabled = True - self._current_stroke: Optional[GestureStroke] = None - - def addListener(self, listener: GestureInputListener): - self._listeners.append(listener) - - def removeListener(self, listener: GestureInputListener): - self._listeners = [l for l in self._listeners if l is not listener] - - def setEnabled(self, enabled: bool): - self._enabled = enabled - - def isEnabled(self) -> bool: - return self._enabled - - def getConfiguration(self) -> GestureConfiguration: - return self._configuration - - def setConfiguration(self, config: GestureConfiguration): - self._configuration = config - - def reset(self): - self._current_stroke = None - - def processTouchEvent(self, x: float, y: float, - action: int, pointerId: int = 0, - pressure: float = 1.0) -> Optional[GestureEvent]: - return None - - def toString(self) -> str: - return f"GestureRecognizer@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# TapGesture / DoubleTapGesture / LongPressGesture -# ═══════════════════════════════════════════════════════════════ - -class TapGesture(GestureRecognizer): - """TapGesture — 点击手势识别器""" - - def __init__(self): - super().__init__() - self._touch_down_x = 0.0 - self._touch_down_y = 0.0 - self._touch_down_time = 0 - self._touch_down_id = -1 - - def processTouchEvent(self, x: float, y: float, - action: int, pointerId: int = 0, - pressure: float = 1.0) -> Optional[GestureEvent]: - if not self._enabled: - return None - now = int(time.time() * 1000) - if action == 0: # DOWN - self._touch_down_x = x - self._touch_down_y = y - self._touch_down_time = now - self._touch_down_id = pointerId - elif action == 1: # UP - if pointerId != self._touch_down_id: - return None - dt = now - self._touch_down_time - dx = x - self._touch_down_x - dy = y - self._touch_down_y - dist = math.sqrt(dx * dx + dy * dy) - if dt <= self._configuration.getTapTimeoutMs() and \ - dist <= 30.0: - return GestureEvent( - eventType=GESTURE_EVENT_TYPE_TAP, - position=(x, y), - fingerCount=1, - state=GESTURE_STATE_COMPLETED, - timestamp=now, - ) - return None - - -class DoubleTapGesture(GestureRecognizer): - """DoubleTapGesture — 双击手势识别器""" - - def __init__(self): - super().__init__() - self._first_tap_time = 0 - self._first_tap_x = 0.0 - self._first_tap_y = 0.0 - self._touch_down_count = 0 - self._touch_down_id = -1 - - def processTouchEvent(self, x: float, y: float, - action: int, pointerId: int = 0, - pressure: float = 1.0) -> Optional[GestureEvent]: - if not self._enabled: - return None - now = int(time.time() * 1000) - if action == 1: # UP - self._touch_down_count += 1 - if self._touch_down_count == 1: - self._first_tap_time = now - self._first_tap_x = x - self._first_tap_y = y - self._touch_down_id = pointerId - elif self._touch_down_count == 2: - dt = now - self._first_tap_time - dx = x - self._first_tap_x - dy = y - self._first_tap_y - dist = math.sqrt(dx * dx + dy * dy) - if dt <= self._configuration.getDoubleTapTimeoutMs() and \ - dist <= 40.0: - self._touch_down_count = 0 - return GestureEvent( - eventType=GESTURE_EVENT_TYPE_DOUBLE_TAP, - position=(x, y), - fingerCount=1, - state=GESTURE_STATE_COMPLETED, - timestamp=now, - ) - self._touch_down_count = 1 - self._first_tap_time = now - self._first_tap_x = x - self._first_tap_y = y - return None - - -class LongPressGesture(GestureRecognizer): - """LongPressGesture — 长按手势识别器""" - - def __init__(self): - super().__init__() - self._press_start_x = 0.0 - self._press_start_y = 0.0 - self._press_start_time = 0 - self._is_pressing = False - self._has_fired = False - - def processTouchEvent(self, x: float, y: float, - action: int, pointerId: int = 0, - pressure: float = 1.0) -> Optional[GestureEvent]: - if not self._enabled: - return None - now = int(time.time() * 1000) - if action == 0: - self._press_start_x = x - self._press_start_y = y - self._press_start_time = now - self._is_pressing = True - self._has_fired = False - elif action == 1: - if self._is_pressing and not self._has_fired: - dt = now - self._press_start_time - if dt >= self._configuration.getLongPressTimeoutMs(): - self._has_fired = True - self._is_pressing = False - return GestureEvent( - eventType=GESTURE_EVENT_TYPE_LONG_PRESS, - position=(self._press_start_x, self._press_start_y), - state=GESTURE_STATE_COMPLETED, - timestamp=now, - ) - self._is_pressing = False - return None - - -# ═══════════════════════════════════════════════════════════════ -# SwipeGesture / ScrollGesture / FlingGesture -# ═══════════════════════════════════════════════════════════════ - -class SwipeGesture(GestureRecognizer): - """SwipeGesture — 滑动手势识别器""" - - def __init__(self, direction: int = GESTURE_DIRECTION_ANY): - super().__init__() - self._direction = direction - self._start_x = 0.0 - self._start_y = 0.0 - self._start_time = 0 - self._is_tracking = False - - def processTouchEvent(self, x: float, y: float, - action: int, pointerId: int = 0, - pressure: float = 1.0) -> Optional[GestureEvent]: - if not self._enabled: - return None - now = int(time.time() * 1000) - if action == 0: - self._start_x = x - self._start_y = y - self._start_time = now - self._is_tracking = True - elif action == 1 and self._is_tracking: - dx = x - self._start_x - dy = y - self._start_y - dist = math.sqrt(dx * dx + dy * dy) - min_dist = self._configuration.getSwipeMinDistance() - max_dist = self._configuration.getSwipeMaxDistance() - if min_dist <= dist <= max_dist: - direction = self._compute_direction(dx, dy) - if self._direction == GESTURE_DIRECTION_ANY or \ - self._direction == direction: - self._is_tracking = False - return GestureEvent( - eventType=GESTURE_EVENT_TYPE_SWIPE, - position=(x, y), - direction=direction, - stroke=GestureStroke([ - GesturePoint(self._start_x, self._start_y, self._start_time), - GesturePoint(x, y, now), - ]), - state=GESTURE_STATE_COMPLETED, - timestamp=now, - ) - self._is_tracking = False - return None - - def _compute_direction(self, dx: float, dy: float) -> int: - angle = math.degrees(math.atan2(dy, dx)) - if -45 <= angle <= 45: - return GESTURE_DIRECTION_RIGHT - if 45 < angle <= 135: - return GESTURE_DIRECTION_DOWN - if -135 <= angle < -45: - return GESTURE_DIRECTION_UP - return GESTURE_DIRECTION_LEFT - - -class ScrollGesture(GestureRecognizer): - """ScrollGesture — 滚动手势识别器""" - - def __init__(self): - super().__init__() - self._start_x = 0.0 - self._start_y = 0.0 - self._last_x = 0.0 - self._last_y = 0.0 - self._is_scrolling = False - - def processTouchEvent(self, x: float, y: float, - action: int, pointerId: int = 0, - pressure: float = 1.0) -> Optional[GestureEvent]: - if not self._enabled: - return None - if action == 0: - self._start_x = x - self._start_y = y - self._last_x = x - self._last_y = y - self._is_scrolling = False - elif action == 2: # MOVE - dx = x - self._last_x - dy = y - self._last_y - dist = math.sqrt(dx * dx + dy * dy) - if dist > 5.0: - self._is_scrolling = True - self._last_x = x - self._last_y = y - return GestureEvent( - eventType=GESTURE_EVENT_TYPE_SCROLL, - position=(x, y), - stroke=GestureStroke([ - GesturePoint(self._start_x, self._start_y, 0), - GesturePoint(x, y, int(time.time() * 1000)), - ]), - state=GESTURE_STATE_IN_PROGRESS, - ) - elif action == 1 and self._is_scrolling: - self._is_scrolling = False - return None - - -class FlingGesture(GestureRecognizer): - """FlingGesture — 甩动手势识别器""" - - def __init__(self): - super().__init__() - self._start_x = 0.0 - self._start_y = 0.0 - self._start_time = 0 - self._is_tracking = False - - def processTouchEvent(self, x: float, y: float, - action: int, pointerId: int = 0, - pressure: float = 1.0) -> Optional[GestureEvent]: - if not self._enabled: - return None - now = int(time.time() * 1000) - if action == 0: - self._start_x = x - self._start_y = y - self._start_time = now - self._is_tracking = True - elif action == 1 and self._is_tracking: - dt = now - self._start_time - if dt <= 0: - return None - dx = x - self._start_x - dy = y - self._start_y - dist = math.sqrt(dx * dx + dy * dy) - velocity = dist / dt * 1000.0 - if velocity >= self._configuration.getFlingMinVelocity(): - self._is_tracking = False - return GestureEvent( - eventType=GESTURE_EVENT_TYPE_FLING, - position=(x, y), - velocity=velocity, - state=GESTURE_STATE_COMPLETED, - timestamp=now, - ) - self._is_tracking = False - return None - - -# ═══════════════════════════════════════════════════════════════ -# PinchGesture / DragGesture / MultiFingerGesture -# ═══════════════════════════════════════════════════════════════ - -class PinchGesture(GestureRecognizer): - """PinchGesture — 捏合手势识别器""" - - def __init__(self): - super().__init__() - self._initial_span = 0.0 - self._current_span = 0.0 - self._finger1_start: Optional[Tuple[float, float]] = None - self._finger2_start: Optional[Tuple[float, float]] = None - self._active = False - - def processTouchEvent(self, x: float, y: float, - action: int, pointerId: int = 0, - pressure: float = 1.0) -> Optional[GestureEvent]: - return None - - -class DragGesture(GestureRecognizer): - """DragGesture — 拖拽手势识别器""" - - def __init__(self): - super().__init__() - self._start_x = 0.0 - self._start_y = 0.0 - self._is_dragging = False - - def processTouchEvent(self, x: float, y: float, - action: int, pointerId: int = 0, - pressure: float = 1.0) -> Optional[GestureEvent]: - if not self._enabled: - return None - if action == 0: - self._start_x = x - self._start_y = y - self._is_dragging = False - elif action == 2: - dx = x - self._start_x - dy = y - self._start_y - dist = math.sqrt(dx * dx + dy * dy) - if dist > 15.0: - self._is_dragging = True - return GestureEvent( - eventType=GESTURE_EVENT_TYPE_DRAG, - position=(x, y), - state=GESTURE_STATE_IN_PROGRESS, - ) - elif action == 1 and self._is_dragging: - self._is_dragging = False - return None - - -class MultiFingerGesture(GestureRecognizer): - """MultiFingerGesture — 多指手势识别器""" - - def __init__(self, minFingers: int = 2): - super().__init__() - self._min_fingers = minFingers - self._active_pointers: Dict[int, Tuple[float, float]] = {} - - def processTouchEvent(self, x: float, y: float, - action: int, pointerId: int = 0, - pressure: float = 1.0) -> Optional[GestureEvent]: - if not self._enabled: - return None - if action == 0: - self._active_pointers[pointerId] = (x, y) - elif action == 1: - self._active_pointers.pop(pointerId, None) - if len(self._active_pointers) >= self._min_fingers: - return GestureEvent( - eventType=GESTURE_EVENT_TYPE_MULTI_FINGER_TAP, - position=(x, y), - fingerCount=len(self._active_pointers), - state=GESTURE_STATE_COMPLETED, - ) - elif action == 2: - self._active_pointers[pointerId] = (x, y) - return None - - -# ═══════════════════════════════════════════════════════════════ -# OrientationGesture -# ═══════════════════════════════════════════════════════════════ - -class OrientationGesture(GestureRecognizer): - """OrientationGesture — 方向手势识别器""" - - def __init__(self, orientation: int = ORIENTATION_ANY): - super().__init__() - self._orientation = orientation - self._start_x = 0.0 - self._start_y = 0.0 - self._is_tracking = False - - def processTouchEvent(self, x: float, y: float, - action: int, pointerId: int = 0, - pressure: float = 1.0) -> Optional[GestureEvent]: - if not self._enabled: - return None - if action == 0: - self._start_x = x - self._start_y = y - self._is_tracking = True - elif action == 1 and self._is_tracking: - dx = x - self._start_x - dy = y - self._start_y - orientation = self._compute_orientation(dx, dy) - if self._orientation == ORIENTATION_ANY or self._orientation == orientation: - self._is_tracking = False - return GestureEvent( - eventType=GESTURE_EVENT_TYPE_ORIENTATION, - position=(x, y), - state=GESTURE_STATE_COMPLETED, - ) - self._is_tracking = False - return None - - def _compute_orientation(self, dx: float, dy: float) -> int: - adx = abs(dx) - ady = abs(dy) - if adx < 10 and ady < 10: - return ORIENTATION_ANY - if adx > ady * 2: - return ORIENTATION_HORIZONTAL if dx > 0 else ORIENTATION_LEFT - elif ady > adx * 2: - return ORIENTATION_VERTICAL if dy > 0 else ORIENTATION_UP - elif dx > 0 and dy > 0: - return ORIENTATION_DIAGONAL_DOWN - elif dx > 0 and dy < 0: - return ORIENTATION_DIAGONAL_UP - elif dx < 0 and dy > 0: - return ORIENTATION_DIAGONAL_DOWN - return ORIENTATION_DIAGONAL_UP - - -# ═══════════════════════════════════════════════════════════════ -# GestureOverlayView (扩展) -# ═══════════════════════════════════════════════════════════════ - -class GestureOverlayView(Stub): - """GestureOverlayView — 手势叠加视图 (完整版)""" - - GESTURE_STROKE_TYPE_SINGLE = 0 - GESTURE_STROKE_TYPE_MULTIPLE = 1 - - ORIENTATION_HORIZONTAL = ORIENTATION_HORIZONTAL - ORIENTATION_VERTICAL = ORIENTATION_VERTICAL - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._current_stroke: Optional[GestureStroke] = None - self._gestures: List[GestureStroke] = [] - self._stroke_type = GestureOverlayView.GESTURE_STROKE_TYPE_SINGLE - self._gesture_color = 0xFFFFFF00 - self._gesture_length = 0.0 - self._is_gesture_visible = True - self._events_enabled = True - self._performed_listeners: List[GesturePerformedListener] = [] - self._drawing_listeners: List[GestureDrawingListener] = [] - self._gesture_recognizer = GestureRecognizer() - - def getCurrentStroke(self) -> Optional[GestureStroke]: - return self._current_stroke - - def getGestures(self) -> List[GestureStroke]: - return list(self._gestures) - - def setGestureColor(self, color: int): - self._gesture_color = color - - def getGestureColor(self) -> int: - return self._gesture_color - - def setGestureStrokeType(self, type_: int): - self._stroke_type = type_ - - def getGestureStrokeType(self) -> int: - return self._stroke_type - - def setGestureStrokeLengthThreshold(self, length: float): - self._gesture_length = length - - def getGestureStrokeLengthThreshold(self) -> float: - return self._gesture_length - - def setGestureVisible(self, visible: bool): - self._is_gesture_visible = visible - - def isGestureVisible(self) -> bool: - return self._is_gesture_visible - - def setEventsEnabled(self, enabled: bool): - self._events_enabled = enabled - - def isEventsEnabled(self) -> bool: - return self._events_enabled - - def addOnGesturePerformedListener(self, listener: GesturePerformedListener): - self._performed_listeners.append(listener) - - def removeOnGesturePerformedListener(self, listener: GesturePerformedListener): - self._performed_listeners = [l for l in self._performed_listeners if l is not listener] - - def addOnGestureDrawingListener(self, listener: GestureDrawingListener): - self._drawing_listeners.append(listener) - - def removeOnGestureDrawingListener(self, listener: GestureDrawingListener): - self._drawing_listeners = [l for l in self._drawing_listeners if l is not listener] - - def clear(self): - self._gestures.clear() - self._current_stroke = None - - def cancelGesture(self): - if self._current_stroke: - for l in self._drawing_listeners: - l.onGestureCancelled(self._current_stroke) - self._current_stroke = None - - def toString(self) -> str: - return f"GestureOverlayView(strokes={len(self._gestures)})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# GestureBuilderActivity -# ═══════════════════════════════════════════════════════════════ - -class GestureBuilderActivity(Stub): - """GestureBuilderActivity — 手势构建器活动""" - - def __init__(self): - super().__init__() - self._overlay = GestureOverlayView() - self._library: Optional["GestureLibraries"] = None - self._current_name: str = "" - - def getGestureOverlayView(self) -> GestureOverlayView: - return self._overlay - - def setGestureLibrary(self, library: "GestureLibraries"): - self._library = library - - def getGestureLibrary(self) -> Optional["GestureLibraries"]: - return self._library - - def setCurrentGestureName(self, name: str): - self._current_name = name - - def getCurrentGestureName(self) -> str: - return self._current_name - - def saveGesture(self): - if self._library and self._current_name and self._overlay.getCurrentStroke(): - stroke = self._overlay.getCurrentStroke() - self._library.store(self._current_name, stroke) - self._library.save() - - def toString(self) -> str: - return f"GestureBuilderActivity(name={self._current_name})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# GestureLibraries -# ═══════════════════════════════════════════════════════════════ - -class GestureLibraries(Stub): - """GestureLibraries — 手势库""" - - def __init__(self, storeType: int = GESTURE_LIBRARY_FILE, - path: str = "", loadOnInit: bool = True): - super().__init__() - self._store_type = storeType - self._path = path - self._gestures: Dict[str, List[GestureStroke]] = {} - self._loaded = False if loadOnInit else False - - def load(self) -> bool: - self._loaded = True - return True - - def save(self) -> bool: - return True - - def isLoaded(self) -> bool: - return self._loaded - - def getGestures(self, name: str) -> List[GestureStroke]: - return self._gestures.get(name, []) - - def getGestureNames(self) -> List[str]: - return list(self._gestures.keys()) - - def store(self, name: str, stroke: GestureStroke): - if name not in self._gestures: - self._gestures[name] = [] - self._gestures[name].append(stroke) - - def remove(self, name: str): - self._gestures.pop(name, None) - - def removeEntry(self, name: str, stroke: GestureStroke): - strokes = self._gestures.get(name) - if strokes: - try: - strokes.remove(stroke) - except ValueError: - pass - if not strokes: - self._gestures.pop(name, None) - - def getCount(self) -> int: - return sum(len(s) for s in self._gestures.values()) - - @staticmethod - def fromFile(path: str) -> "GestureLibraries": - return GestureLibraries(storeType=GESTURE_LIBRARY_FILE, path=path) - - @staticmethod - def fromResource(context: Any, resourceId: int) -> "GestureLibraries": - return GestureLibraries(storeType=GESTURE_LIBRARY_RESOURCE, path=str(resourceId)) - - @staticmethod - def fromRaw(context: Any, resourceId: int) -> "GestureLibraries": - return GestureLibraries(storeType=GESTURE_LIBRARY_RAW, path=str(resourceId)) - - @staticmethod - def fromPrivateFile(context: Any, name: str) -> "GestureLibraries": - return GestureLibraries(storeType=GESTURE_LIBRARY_PRIVATE, path=name) - - def toString(self) -> str: - return f"GestureLibraries(type={self._store_type}, gestures={self.getCount()})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/graphics.py b/android_pyapi/android/graphics.py deleted file mode 100644 index 86e4f98..0000000 --- a/android_pyapi/android/graphics.py +++ /dev/null @@ -1,3455 +0,0 @@ -""" -Android Graphics 组件 — Bitmap, Canvas, Paint, Color, Path, Rect 等 -方法签名完全对齐 Android SDK -""" - -import array -import math -import struct -import io -import os -import time -from enum import IntEnum -from typing import Any, Dict, List, Optional, Sequence, Tuple, Union - -from .._core import Stub, AndroidOnlyError, android_context, HAS_PIL, resolve_android_path - -# Re-export Typeface from text module -from .text import Typeface - - -# ═══════════════════════════════════════════════════════════════ -# Color -# ═══════════════════════════════════════════════════════════════ - -class Color(Stub): - """Color — 颜色工具类""" - - BLACK = 0xFF000000 - DKGRAY = 0xFF444444 - GRAY = 0xFF888888 - LTGRAY = 0xFFCCCCCC - WHITE = 0xFFFFFFFF - RED = 0xFFFF0000 - GREEN = 0xFF00FF00 - BLUE = 0xFF0000FF - YELLOW = 0xFFFFFF00 - CYAN = 0xFF00FFFF - MAGENTA = 0xFFFF00FF - TRANSPARENT = 0 - - @staticmethod - def alpha(color: int) -> int: - return (color >> 24) & 0xFF - - @staticmethod - def red(color: int) -> int: - return (color >> 16) & 0xFF - - @staticmethod - def green(color: int) -> int: - return (color >> 8) & 0xFF - - @staticmethod - def blue(color: int) -> int: - return color & 0xFF - - @staticmethod - def rgb(r: int, g: int, b: int) -> int: - return 0xFF000000 | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF) - - @staticmethod - def argb(a: int, r: int, g: int, b: int) -> int: - return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF) - - @staticmethod - def parseColor(colorString: str) -> int: - s = colorString.strip() - if s.startswith("#"): - s = s[1:] - if len(s) == 3: - s = "".join(c * 2 for c in s) - if len(s) == 6: - s = "FF" + s - return int(s, 16) & 0xFFFFFFFF - if s.startswith("0x"): - return int(s, 16) & 0xFFFFFFFF - name_map = { - "black": Color.BLACK, "white": Color.WHITE, "red": Color.RED, - "green": Color.GREEN, "blue": Color.BLUE, "yellow": Color.YELLOW, - "cyan": Color.CYAN, "magenta": Color.MAGENTA, "transparent": Color.TRANSPARENT, - "dkgray": Color.DKGRAY, "gray": Color.GRAY, "ltgray": Color.LTGRAY, - "darkgray": Color.DKGRAY, "lightgray": Color.LTGRAY, - } - return name_map.get(s.lower(), Color.BLACK) - - @staticmethod - def RGBToHSV(red: int, green: int, blue: int, hsv: List[float]): - r, g, b = red / 255.0, green / 255.0, blue / 255.0 - mx = max(r, g, b) - mn = min(r, g, b) - df = mx - mn - if df == 0: - h = 0.0 - elif mx == r: - h = ((g - b) / df) % 6.0 - elif mx == g: - h = (b - r) / df + 2.0 - else: - h = (r - g) / df + 4.0 - h = h * 60.0 - if h < 0: - h += 360.0 - s = 0.0 if mx == 0 else (df / mx) - v = mx - hsv[0] = h - hsv[1] = s - hsv[2] = v - - @staticmethod - def HSVToColor(hsv: List[float]) -> int: - return Color.HSVToColor(255, hsv) - - @staticmethod - def HSVToColor(alpha: int, hsv: List[float]) -> int: - h, s, v = hsv[0], hsv[1], hsv[2] - hi = int(h / 60.0) % 6 - f = (h / 60.0) - hi - p = v * (1.0 - s) - q = v * (1.0 - f * s) - t = v * (1.0 - (1.0 - f) * s) - if hi == 0: - r, g, b = v, t, p - elif hi == 1: - r, g, b = q, v, p - elif hi == 2: - r, g, b = p, v, t - elif hi == 3: - r, g, b = p, q, v - elif hi == 4: - r, g, b = t, p, v - else: - r, g, b = v, p, q - return Color.argb(alpha, int(r * 255 + 0.5), int(g * 255 + 0.5), int(b * 255 + 0.5)) - - @staticmethod - def ColorToHSV(color: int, hsv: List[float]): - Color.RGBToHSV(Color.red(color), Color.green(color), Color.blue(color), hsv) - - @staticmethod - def getHue(color: int) -> float: - hsv = [0.0, 0.0, 0.0] - Color.ColorToHSV(color, hsv) - return hsv[0] - - @staticmethod - def getSaturation(color: int) -> float: - hsv = [0.0, 0.0, 0.0] - Color.ColorToHSV(color, hsv) - return hsv[1] - - @staticmethod - def getBrightness(color: int) -> float: - hsv = [0.0, 0.0, 0.0] - Color.ColorToHSV(color, hsv) - return hsv[2] - - @staticmethod - def HSLToColor(hsl: List[float], alpha: float = 1.0) -> int: - h, s, l = hsl[0] / 360.0, hsl[1], hsl[2] - if s == 0: - r = g = b = l - else: - def hue2rgb(p, q, t): - if t < 0: - t += 1 - if t > 1: - t -= 1 - if t < 1.0 / 6.0: - return p + (q - p) * 6.0 * t - if t < 1.0 / 2.0: - return q - if t < 2.0 / 3.0: - return p + (q - p) * (2.0 / 3.0 - t) * 6.0 - return p - q = l * (1 + s) if l < 0.5 else l + s - l * s - p = 2 * l - q - r = hue2rgb(p, q, h + 1.0 / 3.0) - g = hue2rgb(p, q, h) - b = hue2rgb(p, q, h - 1.0 / 3.0) - a = max(0, min(255, int(alpha * 255))) - return Color.argb(a, int(r * 255), int(g * 255), int(b * 255)) - - @staticmethod - def ColorToHSL(color: int, hsl: List[float]): - r, g, b = Color.red(color) / 255.0, Color.green(color) / 255.0, Color.blue(color) / 255.0 - mx = max(r, g, b) - mn = min(r, g, b) - l = (mx + mn) / 2.0 - if mx == mn: - h = 0.0 - s = 0.0 - else: - d = mx - mn - s = d / (1 - abs(2 * l - 1)) - if mx == r: - h = ((g - b) / d) % 6.0 - elif mx == g: - h = (b - r) / d + 2.0 - else: - h = (r - g) / d + 4.0 - h = h * 60.0 - if h < 0: - h += 360.0 - hsl[0] = h - hsl[1] = s - hsl[2] = l - - -# ═══════════════════════════════════════════════════════════════ -# Rect -# ═══════════════════════════════════════════════════════════════ - -class Rect(Stub): - """Rect — 矩形 (int 坐标)""" - - def __init__(self, left: int = 0, top: int = 0, right: int = 0, bottom: int = 0): - super().__init__() - self.left = left - self.top = top - self.right = right - self.bottom = bottom - - def width(self) -> int: - return self.right - self.left - - def height(self) -> int: - return self.bottom - self.top - - def centerX(self) -> int: - return (self.left + self.right) // 2 - - def centerY(self) -> int: - return (self.top + self.bottom) // 2 - - def exactCenterX(self) -> float: - return (self.left + self.right) * 0.5 - - def exactCenterY(self) -> float: - return (self.top + self.bottom) * 0.5 - - def set(self, left: int, top: int, right: int, bottom: int): - self.left, self.top, self.right, self.bottom = left, top, right, bottom - - def offset(self, dx: int, dy: int): - self.left += dx - self.top += dy - self.right += dx - self.bottom += dy - - def offsetTo(self, newLeft: int, newTop: int): - self.right += newLeft - self.left - self.bottom += newTop - self.top - self.left = newLeft - self.top = newTop - - def inset(self, dx: int, dy: int): - self.left += dx - self.top += dy - self.right -= dx - self.bottom -= dy - - def contains(self, *args) -> bool: - if len(args) == 2: - x, y = args - return (self.left < self.right and self.top < self.bottom - and x >= self.left and x < self.right and y >= self.top and y < self.bottom) - elif len(args) == 4: - left, top, right, bottom = args - return (self.left < self.right and self.top < self.bottom - and self.left <= left and self.top <= top - and self.right >= right and self.bottom >= bottom) - return False - - def intersect(self, left: int, top: int, right: int, bottom: int) -> bool: - nL = max(self.left, left) - nT = max(self.top, top) - nR = min(self.right, right) - nB = min(self.bottom, bottom) - if nL < nR and nT < nB: - self.left, self.top, self.right, self.bottom = nL, nT, nR, nB - return True - return False - - def intersects(self, left: int, top: int, right: int, bottom: int) -> bool: - return self.left < right and self.right > left \ - and self.top < bottom and self.bottom > top - - @staticmethod - def intersects(a: "Rect", b: "Rect") -> bool: - return a.left < b.right and a.right > b.left \ - and a.top < b.bottom and a.bottom > b.top - - def union(self, left: int, top: int, right: int, bottom: int): - if self.isEmpty(): - self.set(left, top, right, bottom) - else: - self.left = min(self.left, left) - self.top = min(self.top, top) - self.right = max(self.right, right) - self.bottom = max(self.bottom, bottom) - - def sort(self): - if self.left > self.right: - self.left, self.right = self.right, self.left - if self.top > self.bottom: - self.top, self.bottom = self.bottom, self.top - - def isEmpty(self) -> bool: - return self.left >= self.right or self.top >= self.bottom - - def setEmpty(self): - self.left = self.top = self.right = self.bottom = 0 - - def toShortString(self) -> str: - return f"[{self.left},{self.top}][{self.right},{self.bottom}]" - - def flattenToString(self) -> str: - return f"{self.left} {self.top} {self.right} {self.bottom}" - - @staticmethod - def unflattenFromString(s: str) -> "Rect": - parts = s.split() - if len(parts) >= 4: - return Rect(int(parts[0]), int(parts[1]), int(parts[2]), int(parts[3])) - return Rect() - - def toString(self) -> str: - return f"Rect({self.left}, {self.top}, {self.right}, {self.bottom})" - - -# ═══════════════════════════════════════════════════════════════ -# RectF -# ═══════════════════════════════════════════════════════════════ - -class RectF(Stub): - """RectF — 矩形 (float 坐标)""" - - def __init__(self, left: float = 0.0, top: float = 0.0, right: float = 0.0, bottom: float = 0.0): - super().__init__() - self.left = left - self.top = top - self.right = right - self.bottom = bottom - - def width(self) -> float: - return self.right - self.left - - def height(self) -> float: - return self.bottom - self.top - - def centerX(self) -> float: - return (self.left + self.right) * 0.5 - - def centerY(self) -> float: - return (self.top + self.bottom) * 0.5 - - def set(self, left: float, top: float, right: float, bottom: float): - self.left, self.top, self.right, self.bottom = left, top, right, bottom - - def offset(self, dx: float, dy: float): - self.left += dx - self.top += dy - self.right += dx - self.bottom += dy - - def offsetTo(self, newLeft: float, newTop: float): - self.right += newLeft - self.left - self.bottom += newTop - self.top - self.left = newLeft - self.top = newTop - - def inset(self, dx: float, dy: float): - self.left += dx - self.top += dy - self.right -= dx - self.bottom -= dy - - def contains(self, x: float, y: float) -> bool: - return self.left < self.right and self.top < self.bottom \ - and x >= self.left and x < self.right and y >= self.top and y < self.bottom - - def intersect(self, left: float, top: float, right: float, bottom: float) -> bool: - nL = max(self.left, left) - nT = max(self.top, top) - nR = min(self.right, right) - nB = min(self.bottom, bottom) - if nL < nR and nT < nB: - self.left, self.top, self.right, self.bottom = nL, nT, nR, nB - return True - return False - - def intersects(self, left: float, top: float, right: float, bottom: float) -> bool: - return self.left < right and self.right > left \ - and self.top < bottom and self.bottom > top - - @staticmethod - def intersects(a: "RectF", b: "RectF") -> bool: - return a.left < b.right and a.right > b.left \ - and a.top < b.bottom and a.bottom > b.top - - def union(self, left: float, top: float, right: float, bottom: float): - if self.isEmpty(): - self.set(left, top, right, bottom) - else: - self.left = min(self.left, left) - self.top = min(self.top, top) - self.right = max(self.right, right) - self.bottom = max(self.bottom, bottom) - - def round(self, r: Rect): - r.left = int(self.left + 0.5) - r.top = int(self.top + 0.5) - r.right = int(self.right + 0.5) - r.bottom = int(self.bottom + 0.5) - - def roundOut(self, r: Rect): - r.left = int(math.floor(self.left)) - r.top = int(math.floor(self.top)) - r.right = int(math.ceil(self.right)) - r.bottom = int(math.ceil(self.bottom)) - - def sort(self): - if self.left > self.right: - self.left, self.right = self.right, self.left - if self.top > self.bottom: - self.top, self.bottom = self.bottom, self.top - - def isEmpty(self) -> bool: - return self.left >= self.right or self.top >= self.bottom - - def setEmpty(self): - self.left = self.top = self.right = self.bottom = 0.0 - - def toShortString(self) -> str: - return f"[{self.left:.1f},{self.top:.1f}][{self.right:.1f},{self.bottom:.1f}]" - - def toString(self) -> str: - return f"RectF({self.left}, {self.top}, {self.right}, {self.bottom})" - - -# ═══════════════════════════════════════════════════════════════ -# Point / PointF -# ═══════════════════════════════════════════════════════════════ - -class Point(Stub): - """Point — 整数坐标点""" - - def __init__(self, x: int = 0, y: int = 0): - super().__init__() - self.x = x - self.y = y - - def set(self, x: int, y: int): - self.x, self.y = x, y - - def offset(self, dx: int, dy: int): - self.x += dx - self.y += dy - - def equals(self, other) -> bool: - if isinstance(other, Point): - return self.x == other.x and self.y == other.y - return False - - def hashCode(self) -> int: - return self.x * 32713 + self.y - - def toString(self) -> str: - return f"Point({self.x}, {self.y})" - - -class PointF(Stub): - """PointF — 浮点坐标点""" - - def __init__(self, x: float = 0.0, y: float = 0.0): - super().__init__() - self.x = x - self.y = y - - def set(self, x: float, y: float): - self.x, self.y = x, y - - def offset(self, dx: float, dy: float): - self.x += dx - self.y += dy - - def equals(self, other) -> bool: - if isinstance(other, PointF): - return self.x == other.x and self.y == other.y - return False - - def hashCode(self) -> int: - return int(self.x * 32713 + self.y) - - def toString(self) -> str: - return f"PointF({self.x}, {self.y})" - - -# ═══════════════════════════════════════════════════════════════ -# Path -# ═══════════════════════════════════════════════════════════════ - -class Path(Stub): - """Path — 路径""" - - class Direction: - CW = 0 - CCW = 1 - - class FillType: - WINDING = 0 - EVEN_ODD = 1 - INVERSE_WINDING = 2 - INVERSE_EVEN_ODD = 3 - - class Op: - DIFFERENCE = 0 - INTERSECT = 1 - UNION = 2 - XOR = 3 - REVERSE_DIFFERENCE = 4 - - def __init__(self): - super().__init__() - self._segments: List[dict] = [] - self._fill_type = Path.FillType.WINDING - self._start_x = 0.0 - self._start_y = 0.0 - self._current_x = 0.0 - self._current_y = 0.0 - - def moveTo(self, x: float, y: float): - self._segments.append({"type": "move", "x": x, "y": y}) - self._current_x = x - self._current_y = y - - def lineTo(self, x: float, y: float): - self._segments.append({"type": "line", "x": x, "y": y}) - self._current_x = x - self._current_y = y - - def quadTo(self, x1: float, y1: float, x2: float, y2: float): - self._segments.append({"type": "quad", "x1": x1, "y1": y1, "x2": x2, "y2": y2}) - self._current_x = x2 - self._current_y = y2 - - def cubicTo(self, x1: float, y1: float, x2: float, y2: float, x3: float, y3: float): - self._segments.append({"type": "cubic", "x1": x1, "y1": y1, "x2": x2, "y2": y2, "x3": x3, "y3": y3}) - self._current_x = x3 - self._current_y = y3 - - def rMoveTo(self, dx: float, dy: float): - self.moveTo(self._current_x + dx, self._current_y + dy) - - def rLineTo(self, dx: float, dy: float): - self.lineTo(self._current_x + dx, self._current_y + dy) - - def rQuadTo(self, dx1: float, dy1: float, dx2: float, dy2: float): - self.quadTo(self._current_x + dx1, self._current_y + dy1, - self._current_x + dx2, self._current_y + dy2) - - def rCubicTo(self, dx1: float, dy1: float, dx2: float, dy2: float, dx3: float, dy3: float): - self.cubicTo(self._current_x + dx1, self._current_y + dy1, - self._current_x + dx2, self._current_y + dy2, - self._current_x + dx3, self._current_y + dy3) - - def addRect(self, left: float, top: float, right: float, bottom: float, - dir: int = Direction.CW): - self.moveTo(left, top) - self.lineTo(right, top) - self.lineTo(right, bottom) - self.lineTo(left, bottom) - self.close() - - def addRoundRect(self, rect: Union["RectF", tuple], rx: float, ry: float, - dir: int = Direction.CW): - if isinstance(rect, RectF): - l, t, r, b = rect.left, rect.top, rect.right, rect.bottom - else: - l, t, r, b = rect - self.moveTo(l + rx, t) - self.lineTo(r - rx, t) - self.quadTo(r, t, r, t + ry) - self.lineTo(r, b - ry) - self.quadTo(r, b, r - rx, b) - self.lineTo(l + rx, b) - self.quadTo(l, b, l, b - ry) - self.lineTo(l, t + ry) - self.quadTo(l, t, l + rx, t) - self.close() - - def addCircle(self, x: float, y: float, radius: float, dir: int = Direction.CW): - k = 0.5522847498 - self.moveTo(x + radius, y) - self.cubicTo(x + radius, y + radius * k, x + radius * k, y + radius, x, y + radius) - self.cubicTo(x - radius * k, y + radius, x - radius, y + radius * k, x - radius, y) - self.cubicTo(x - radius, y - radius * k, x - radius * k, y - radius, x, y - radius) - self.cubicTo(x + radius * k, y - radius, x + radius, y - radius * k, x + radius, y) - self.close() - - def addOval(self, rect: Union["RectF", tuple], dir: int = Direction.CW): - if isinstance(rect, RectF): - l, t, r, b = rect.left, rect.top, rect.right, rect.bottom - else: - l, t, r, b = rect - cx = (l + r) / 2.0 - cy = (t + b) / 2.0 - rx = (r - l) / 2.0 - ry = (b - t) / 2.0 - kx = rx * 0.5522847498 - ky = ry * 0.5522847498 - self.moveTo(cx + rx, cy) - self.cubicTo(cx + rx, cy + ky, cx + kx, cy + ry, cx, cy + ry) - self.cubicTo(cx - kx, cy + ry, cx - rx, cy + ky, cx - rx, cy) - self.cubicTo(cx - rx, cy - ky, cx - kx, cy - ry, cx, cy - ry) - self.cubicTo(cx + kx, cy - ry, cx + rx, cy - ky, cx + rx, cy) - self.close() - - def addArc(self, rect: Union["RectF", tuple], startAngle: float, sweepAngle: float): - if isinstance(rect, RectF): - l, t, r, b = rect.left, rect.top, rect.right, rect.bottom - else: - l, t, r, b = rect - cx = (l + r) / 2.0 - cy = (t + b) / 2.0 - rx = (r - l) / 2.0 - ry = (b - t) / 2.0 - start_rad = math.radians(startAngle) - end_rad = math.radians(startAngle + sweepAngle) - segments = 4 - da = (end_rad - start_rad) / segments - for i in range(segments + 1): - a = start_rad + i * da - px = cx + rx * math.cos(a) - py = cy + ry * math.sin(a) - if i == 0: - self.moveTo(px, py) - else: - self.lineTo(px, py) - - def addPath(self, path: "Path", dx: float = 0.0, dy: float = 0.0): - for seg in path._segments: - s = dict(seg) - if "x" in s: - s["x"] = s["x"] + dx - if "y" in s: - s["y"] = s["y"] + dy - if "x1" in s: - s["x1"] = s["x1"] + dx - if "y1" in s: - s["y1"] = s["y1"] + dy - if "x2" in s: - s["x2"] = s["x2"] + dx - if "y2" in s: - s["y2"] = s["y2"] + dy - if "x3" in s: - s["x3"] = s["x3"] + dx - if "y3" in s: - s["y3"] = s["y3"] + dy - self._segments.append(s) - self._current_x = path._current_x + dx - self._current_y = path._current_y + dy - - def arcTo(self, rect: Union["RectF", tuple], startAngle: float, sweepAngle: float, - forceMoveTo: bool = False): - if isinstance(rect, RectF): - l, t, r, b = rect.left, rect.top, rect.right, rect.bottom - else: - l, t, r, b = rect - cx = (l + r) / 2.0 - cy = (t + b) / 2.0 - rx = (r - l) / 2.0 - ry = (b - t) / 2.0 - start_rad = math.radians(startAngle) - end_rad = math.radians(startAngle + sweepAngle) - segments = 4 - da = (end_rad - start_rad) / segments - for i in range(segments + 1): - a = start_rad + i * da - px = cx + rx * math.cos(a) - py = cy + ry * math.sin(a) - if i == 0 and forceMoveTo: - self.moveTo(px, py) - elif i == 0: - self.lineTo(px, py) - else: - self.lineTo(px, py) - - def close(self): - self._segments.append({"type": "close"}) - - def reset(self): - self._segments.clear() - self._current_x = 0.0 - self._current_y = 0.0 - - def rewind(self): - self._segments.clear() - self._current_x = 0.0 - self._current_y = 0.0 - - def set(self, src: "Path"): - self._segments = list(src._segments) - self._fill_type = src._fill_type - self._current_x = src._current_x - self._current_y = src._current_y - - def computeBounds(self, exact: bool = False) -> RectF: - if not self._segments: - return RectF(0, 0, 0, 0) - min_x = float("inf") - min_y = float("inf") - max_x = float("-inf") - max_y = float("-inf") - for seg in self._segments: - for key in ("x", "x1", "x2", "x3"): - if key in seg: - if seg[key] < min_x: - min_x = seg[key] - if seg[key] > max_x: - max_x = seg[key] - for key in ("y", "y1", "y2", "y3"): - if key in seg: - if seg[key] < min_y: - min_y = seg[key] - if seg[key] > max_y: - max_y = seg[key] - if min_x == float("inf"): - return RectF(0, 0, 0, 0) - return RectF(min_x, min_y, max_x, max_y) - - def incReserve(self, extraPtCount: int): - pass - - def op(self, other: "Path", op: int) -> bool: - """对两个路径执行布尔运算。基于包围盒的近似实现。""" - if other is None: - return False - self_bounds = self.computeBounds() - other_bounds = other.computeBounds() - if op == Path.Op.UNION: - # 合并:将 other 的段追加到 self - self.addPath(other) - return True - if op == Path.Op.INTERSECT: - # 交集:仅当包围盒相交时保留相交区域 - if not RectF.intersects(self_bounds, other_bounds): - self.reset() - return False - # 用相交包围盒作为近似结果 - inter = RectF(self_bounds.left, self_bounds.top, - self_bounds.right, self_bounds.bottom) - inter.intersect(other_bounds.left, other_bounds.top, - other_bounds.right, other_bounds.bottom) - self.reset() - self.addRect(inter.left, inter.top, inter.right, inter.bottom) - return True - if op == Path.Op.DIFFERENCE: - # 差集:若包围盒不相交则 self 不变,否则标记(近似) - if not RectF.intersects(self_bounds, other_bounds): - return True - # 近似:无法精确计算,保留 self 不变 - return True - if op == Path.Op.REVERSE_DIFFERENCE: - # 反向差集:用 other 替换 self - self.set(other) - return True - if op == Path.Op.XOR: - # 异或:合并两者 - self.addPath(other) - return True - return False - - def isRect(self) -> bool: - if len(self._segments) != 5: - return False - types = [s["type"] for s in self._segments] - return types == ["move", "line", "line", "line", "close"] - - def isConvex(self) -> bool: - return True - - def setFillType(self, ft: int): - self._fill_type = ft - - def getFillType(self) -> int: - return self._fill_type - - def toggleReverseFillType(self): - mapping = { - Path.FillType.WINDING: Path.FillType.INVERSE_WINDING, - Path.FillType.EVEN_ODD: Path.FillType.INVERSE_EVEN_ODD, - Path.FillType.INVERSE_WINDING: Path.FillType.WINDING, - Path.FillType.INVERSE_EVEN_ODD: Path.FillType.EVEN_ODD, - } - self._fill_type = mapping.get(self._fill_type, Path.FillType.WINDING) - - -# ═══════════════════════════════════════════════════════════════ -# PathMeasure -# ═══════════════════════════════════════════════════════════════ - -class PathMeasure(Stub): - """PathMeasure — 路径测量""" - - def __init__(self, path: Path = None, forceClosed: bool = False): - super().__init__() - self._path = path - self._force_closed = forceClosed - self._contours: List[List[Tuple[float, float, float]]] = [] - self._contour_lengths: List[float] = [] - self._total_length: float = 0.0 - if path is not None: - self._flatten() - - def _flatten(self): - """将路径展平为线段列表,计算累积长度。""" - self._contours = [] - self._contour_lengths = [] - self._total_length = 0.0 - if not self._path: - return - segs = self._path._segments - cur_x = 0.0 - cur_y = 0.0 - start_x = 0.0 - start_y = 0.0 - contour: List[Tuple[float, float, float]] = [] - contour_len = 0.0 - for seg in segs: - t = seg.get("type") - if t == "move": - if contour: - self._contours.append(contour) - self._contour_lengths.append(contour_len) - self._total_length += contour_len - cur_x = seg.get("x", 0.0) - cur_y = seg.get("y", 0.0) - start_x = cur_x - start_y = cur_y - contour = [(cur_x, cur_y, 0.0)] - contour_len = 0.0 - elif t == "line": - nx = seg.get("x", cur_x) - ny = seg.get("y", cur_y) - d = math.hypot(nx - cur_x, ny - cur_y) - contour_len += d - contour.append((nx, ny, contour_len)) - cur_x = nx - cur_y = ny - elif t == "quad": - x1, y1 = seg.get("x1", cur_x), seg.get("y1", cur_y) - x2, y2 = seg.get("x2", cur_x), seg.get("y2", cur_y) - steps = 16 - prev_x, prev_y = cur_x, cur_y - for i in range(1, steps + 1): - tt = i / steps - mt = 1 - tt - nx = mt * mt * cur_x + 2 * mt * tt * x1 + tt * tt * x2 - ny = mt * mt * cur_y + 2 * mt * tt * y1 + tt * tt * y2 - d = math.hypot(nx - prev_x, ny - prev_y) - contour_len += d - contour.append((nx, ny, contour_len)) - prev_x, prev_y = nx, ny - cur_x = x2 - cur_y = y2 - elif t == "cubic": - x1, y1 = seg.get("x1", cur_x), seg.get("y1", cur_y) - x2, y2 = seg.get("x2", cur_x), seg.get("y2", cur_y) - x3, y3 = seg.get("x3", cur_x), seg.get("y3", cur_y) - steps = 24 - prev_x, prev_y = cur_x, cur_y - for i in range(1, steps + 1): - tt = i / steps - mt = 1 - tt - nx = mt ** 3 * cur_x + 3 * mt ** 2 * tt * x1 + 3 * mt * tt ** 2 * x2 + tt ** 3 * x3 - ny = mt ** 3 * cur_y + 3 * mt ** 2 * tt * y1 + 3 * mt * tt ** 2 * y2 + tt ** 3 * y3 - d = math.hypot(nx - prev_x, ny - prev_y) - contour_len += d - contour.append((nx, ny, contour_len)) - prev_x, prev_y = nx, ny - cur_x = x3 - cur_y = y3 - elif t == "close": - if contour: - d = math.hypot(start_x - cur_x, start_y - cur_y) - if d > 1e-10: - contour_len += d - contour.append((start_x, start_y, contour_len)) - cur_x = start_x - cur_y = start_y - if contour: - if self._force_closed and segs and segs[-1].get("type") != "close": - d = math.hypot(start_x - cur_x, start_y - cur_y) - if d > 1e-10: - contour_len += d - contour.append((start_x, start_y, contour_len)) - self._contours.append(contour) - self._contour_lengths.append(contour_len) - self._total_length += contour_len - - def setPath(self, path: Path, forceClosed: bool): - self._path = path - self._force_closed = forceClosed - self._contours = [] - self._contour_lengths = [] - self._total_length = 0.0 - if path is not None: - self._flatten() - - def getLength(self) -> float: - """返回当前轮廓的长度。""" - if self._contour_lengths: - return self._contour_lengths[0] - return 0.0 - - def getPosTan(self, distance: float, pos: List[float], tan: List[float]) -> bool: - """获取指定距离处的坐标和切线方向。""" - if not self._contours: - return False - contour = self._contours[0] - length = self._contour_lengths[0] - if length <= 0: - return False - # 钳制距离 - d = max(0.0, min(distance, length)) - # 二分查找 - lo, hi = 0, len(contour) - 1 - while lo < hi: - mid = (lo + hi) // 2 - if contour[mid][2] < d: - lo = mid + 1 - else: - hi = mid - idx = max(1, lo) - px, py, pd = contour[idx - 1] - nx, ny, nd = contour[idx] - seg_len = nd - pd - if seg_len < 1e-10: - t = 0.0 - else: - t = (d - pd) / seg_len - x = px + (nx - px) * t - y = py + (ny - py) * t - dx = nx - px - dy = ny - py - dl = math.hypot(dx, dy) - if dl < 1e-10: - tx, ty = 1.0, 0.0 - else: - tx, ty = dx / dl, dy / dl - if len(pos) >= 2: - pos[0] = x - pos[1] = y - if len(tan) >= 2: - tan[0] = tx - tan[1] = ty - return True - - def getMatrix(self, distance: float, matrix: "Matrix", flags: int) -> bool: - """获取指定距离处的变换矩阵(平移+旋转)。""" - pos = [0.0, 0.0] - tan = [0.0, 0.0] - if not self.getPosTan(distance, pos, tan): - return False - angle = math.degrees(math.atan2(tan[1], tan[0])) - matrix.reset() - matrix.postRotate(angle) - matrix.postTranslate(pos[0], pos[1]) - return True - - def getSegment(self, startD: float, stopD: float, dst: Path, startWithMoveTo: bool) -> bool: - """提取路径中 [startD, stopD] 区间的子路径。""" - if not self._contours: - return False - contour = self._contours[0] - length = self._contour_lengths[0] - if length <= 0: - return False - startD = max(0.0, min(startD, length)) - stopD = max(0.0, min(stopD, length)) - if startD >= stopD: - return False - first = True - for i in range(1, len(contour)): - px, py, pd = contour[i - 1] - nx, ny, nd = contour[i] - if nd < startD: - continue - if pd > stopD: - break - seg_len = nd - pd - if seg_len < 1e-10: - continue - # 计算线段两端在 [startD, stopD] 内的参数 - t0 = max(0.0, (startD - pd) / seg_len) - t1 = min(1.0, (stopD - pd) / seg_len) - x0 = px + (nx - px) * t0 - y0 = py + (ny - py) * t0 - x1 = px + (nx - px) * t1 - y1 = py + (ny - py) * t1 - if first and startWithMoveTo: - dst.moveTo(x0, y0) - else: - dst.lineTo(x0, y0) - dst.lineTo(x1, y1) - first = False - return True - - def isClosed(self) -> bool: - if not self._path or not self._path._segments: - return self._force_closed - if self._force_closed: - return True - return any(s.get("type") == "close" for s in self._path._segments) - - def nextContour(self) -> bool: - """移动到下一个轮廓。""" - if len(self._contours) > 1: - self._contours.pop(0) - self._contour_lengths.pop(0) - return True - return False - - -# ═══════════════════════════════════════════════════════════════ -# PorterDuff -# ═══════════════════════════════════════════════════════════════ - -class PorterDuff(Stub): - """PorterDuff — 图形混合模式""" - - class Mode: - CLEAR = 0 - SRC = 1 - DST = 2 - SRC_OVER = 3 - DST_OVER = 4 - SRC_IN = 5 - DST_IN = 6 - SRC_OUT = 7 - DST_OUT = 8 - SRC_ATOP = 9 - DST_ATOP = 10 - XOR = 11 - ADD = 12 - MULTIPLY = 13 - SCREEN = 14 - OVERLAY = 15 - DARKEN = 16 - LIGHTEN = 17 - - -# ═══════════════════════════════════════════════════════════════ -# ClipOp -# ═══════════════════════════════════════════════════════════════ - -class ClipOp(Stub): - """ClipOp — 裁剪操作""" - DIFFERENCE = 0 - INTERSECT = 1 - REPLACE = 2 - - -# ═══════════════════════════════════════════════════════════════ -# BlendMode -# ═══════════════════════════════════════════════════════════════ - -class BlendMode(Stub): - """BlendMode — Android 29+ 混合模式""" - CLEAR = 0 - SRC = 1 - DST = 2 - SRC_OVER = 3 - DST_OVER = 4 - SRC_IN = 5 - DST_IN = 6 - SRC_OUT = 7 - DST_OUT = 8 - SRC_ATOP = 9 - DST_ATOP = 10 - XOR = 11 - PLUS = 12 - MODULATE = 13 - SCREEN = 14 - OVERLAY = 15 - DARKEN = 16 - LIGHTEN = 17 - - -# ═══════════════════════════════════════════════════════════════ -# BlendModeColorFilter -# ═══════════════════════════════════════════════════════════════ - -class BlendModeColorFilter(Stub): - """BlendModeColorFilter — 混合模式颜色过滤器""" - - def __init__(self, color: int, mode): - super().__init__() - self._color = color - self._mode = mode - - def getColor(self) -> int: - return self._color - - def getMode(self): - return self._mode - - -# ═══════════════════════════════════════════════════════════════ -# ColorFilter -# ═══════════════════════════════════════════════════════════════ - -class ColorFilter(Stub): - """ColorFilter — 颜色过滤器的抽象基类""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# PorterDuffColorFilter -# ═══════════════════════════════════════════════════════════════ - -class PorterDuffColorFilter(ColorFilter): - """PorterDuffColorFilter — PorterDuff 颜色过滤器""" - - def __init__(self, color: int, mode): - super().__init__() - self._color = color - self._mode = mode - - def getColor(self) -> int: - return self._color - - def getMode(self): - return self._mode - - -# ═══════════════════════════════════════════════════════════════ -# LightingColorFilter -# ═══════════════════════════════════════════════════════════════ - -class LightingColorFilter(ColorFilter): - """LightingColorFilter — 光照颜色过滤器""" - - def __init__(self, mul: int, add: int): - super().__init__() - self._mul = mul - self._add = add - - def getMultiply(self) -> int: - return self._mul - - def getAdd(self) -> int: - return self._add - - -# ═══════════════════════════════════════════════════════════════ -# ComposeColorFilter -# ═══════════════════════════════════════════════════════════════ - -class ComposeColorFilter(ColorFilter): - """ComposeColorFilter — 组合颜色过滤器""" - - def __init__(self, cf1: ColorFilter, cf2: ColorFilter): - super().__init__() - self._cf1 = cf1 - self._cf2 = cf2 - - def getColorFilters(self) -> Tuple[ColorFilter, ColorFilter]: - return (self._cf1, self._cf2) - - -# ═══════════════════════════════════════════════════════════════ -# ColorMatrix -# ═══════════════════════════════════════════════════════════════ - -class ColorMatrix(Stub): - """ColorMatrix — 4x5 颜色变换矩阵""" - - def __init__(self, src: "ColorMatrix" = None): - super().__init__() - if src: - self._array = list(src._array) - else: - self._array = [1, 0, 0, 0, 0, - 0, 1, 0, 0, 0, - 0, 0, 1, 0, 0, - 0, 0, 0, 1, 0] - - def getArray(self) -> List[float]: - return self._array - - def set(self, src: "ColorMatrix"): - self._array = list(src._array) - - def reset(self): - self._array = [1, 0, 0, 0, 0, - 0, 1, 0, 0, 0, - 0, 0, 1, 0, 0, - 0, 0, 0, 1, 0] - - def setSaturation(self, sat: float): - R_lum = 0.213 - G_lum = 0.715 - B_lum = 0.072 - one_minus = 1.0 - sat - self._array = [ - R_lum * one_minus + sat, G_lum * one_minus, B_lum * one_minus, 0, 0, - R_lum * one_minus, G_lum * one_minus + sat, B_lum * one_minus, 0, 0, - R_lum * one_minus, G_lum * one_minus, B_lum * one_minus + sat, 0, 0, - 0, 0, 0, 1, 0, - ] - - def setRotate(self, axis: int, degrees: float): - rad = math.radians(degrees) - c = math.cos(rad) - s = math.sin(rad) - self.reset() - if axis == 0: - self._array[5] = c - self._array[6] = s - self._array[9] = -s - self._array[10] = c - elif axis == 1: - self._array[0] = c - self._array[2] = -s - self._array[8] = s - self._array[10] = c - elif axis == 2: - self._array[0] = c - self._array[1] = s - self._array[4] = -s - self._array[5] = c - - def setConcat(self, a: "ColorMatrix", b: "ColorMatrix"): - a_arr = a._array - b_arr = b._array - result = [0.0] * 20 - for i in range(4): - for j in range(5): - s = 0.0 - for k in range(4): - s += a_arr[i * 5 + k] * b_arr[k * 5 + j] - result[i * 5 + j] = s - self._array = result - - def preConcat(self, other: "ColorMatrix"): - new = ColorMatrix() - new.setConcat(self, other) - self._array = new._array - - def postConcat(self, other: "ColorMatrix"): - new = ColorMatrix() - new.setConcat(other, self) - self._array = new._array - - def setScale(self, rScale: float, gScale: float, bScale: float, aScale: float): - self._array = [ - rScale, 0, 0, 0, 0, - 0, gScale, 0, 0, 0, - 0, 0, bScale, 0, 0, - 0, 0, 0, aScale, 0, - ] - - -# ═══════════════════════════════════════════════════════════════ -# ColorMatrixColorFilter -# ═══════════════════════════════════════════════════════════════ - -class ColorMatrixColorFilter(ColorFilter): - """ColorMatrixColorFilter — 颜色矩阵过滤器""" - - def __init__(self, matrix: ColorMatrix): - super().__init__() - self._matrix = matrix - - def getMatrix(self) -> ColorMatrix: - return self._matrix - - -# ═══════════════════════════════════════════════════════════════ -# Matrix -# ═══════════════════════════════════════════════════════════════ - -class Matrix(Stub): - """Matrix — 3x3 变换矩阵""" - - IDENTITY_MATRIX: "Matrix" = None - - MPERSP_0 = 6 - MPERSP_1 = 7 - MPERSP_2 = 8 - MSCALE_X = 0 - MSCALE_Y = 4 - MSKEW_X = 1 - MSKEW_Y = 3 - MTRANS_X = 2 - MTRANS_Y = 5 - - def __init__(self, src: "Matrix" = None): - super().__init__() - if src: - self._values = list(src._values) - else: - self._values = [1, 0, 0, - 0, 1, 0, - 0, 0, 1] - - def getValues(self) -> List[float]: - return self._values - - def setValues(self, values: List[float]): - self._values = list(values) - - def reset(self): - self._values = [1, 0, 0, 0, 1, 0, 0, 0, 1] - - def isIdentity(self) -> bool: - return self._values == [1, 0, 0, 0, 1, 0, 0, 0, 1] - - def invert(self, inverse: "Matrix") -> bool: - v = self._values - det = v[0] * (v[4] * v[8] - v[5] * v[7]) \ - - v[1] * (v[3] * v[8] - v[5] * v[6]) \ - + v[2] * (v[3] * v[7] - v[4] * v[6]) - if abs(det) < 1e-10: - return False - inv_det = 1.0 / det - inv = [ - (v[4] * v[8] - v[5] * v[7]) * inv_det, - (v[2] * v[7] - v[1] * v[8]) * inv_det, - (v[1] * v[5] - v[2] * v[4]) * inv_det, - (v[5] * v[6] - v[3] * v[8]) * inv_det, - (v[0] * v[8] - v[2] * v[6]) * inv_det, - (v[2] * v[3] - v[0] * v[5]) * inv_det, - (v[3] * v[7] - v[4] * v[6]) * inv_det, - (v[1] * v[6] - v[0] * v[7]) * inv_det, - (v[0] * v[4] - v[1] * v[3]) * inv_det, - ] - inverse._values = inv - return True - - def setTranslate(self, dx: float, dy: float): - self._values = [1, 0, dx, 0, 1, dy, 0, 0, 1] - - def setScale(self, sx: float, sy: float, px: float = 0.0, py: float = 0.0): - self._values = [sx, 0, px - sx * px, 0, sy, py - sy * py, 0, 0, 1] - - def setRotate(self, degrees: float, px: float = 0.0, py: float = 0.0): - rad = math.radians(degrees) - c = math.cos(rad) - s = math.sin(rad) - tx = px - px * c + py * s - ty = py - px * s - py * c - self._values = [c, -s, tx, s, c, ty, 0, 0, 1] - - def setSkew(self, kx: float, ky: float, px: float = 0.0, py: float = 0.0): - self._values = [1, kx, -kx * px, ky, 1, -ky * py, 0, 0, 1] - - def setConcat(self, a: "Matrix", b: "Matrix"): - va, vb = a._values, b._values - self._values = [ - va[0] * vb[0] + va[1] * vb[3] + va[2] * vb[6], - va[0] * vb[1] + va[1] * vb[4] + va[2] * vb[7], - va[0] * vb[2] + va[1] * vb[5] + va[2] * vb[8], - va[3] * vb[0] + va[4] * vb[3] + va[5] * vb[6], - va[3] * vb[1] + va[4] * vb[4] + va[5] * vb[7], - va[3] * vb[2] + va[4] * vb[5] + va[5] * vb[8], - va[6] * vb[0] + va[7] * vb[3] + va[8] * vb[6], - va[6] * vb[1] + va[7] * vb[4] + va[8] * vb[7], - va[6] * vb[2] + va[7] * vb[5] + va[8] * vb[8], - ] - - def preConcat(self, other: "Matrix"): - new = Matrix() - new.setConcat(self, other) - self._values = new._values - - def postConcat(self, other: "Matrix"): - new = Matrix() - new.setConcat(other, self) - self._values = new._values - - def preTranslate(self, dx: float, dy: float): - m = Matrix() - m.setTranslate(dx, dy) - self.preConcat(m) - - def preScale(self, sx: float, sy: float, px: float = 0.0, py: float = 0.0): - m = Matrix() - m.setScale(sx, sy, px, py) - self.preConcat(m) - - def preRotate(self, degrees: float, px: float = 0.0, py: float = 0.0): - m = Matrix() - m.setRotate(degrees, px, py) - self.preConcat(m) - - def preSkew(self, kx: float, ky: float, px: float = 0.0, py: float = 0.0): - m = Matrix() - m.setSkew(kx, ky, px, py) - self.preConcat(m) - - def postTranslate(self, dx: float, dy: float): - m = Matrix() - m.setTranslate(dx, dy) - self.postConcat(m) - - def postScale(self, sx: float, sy: float, px: float = 0.0, py: float = 0.0): - m = Matrix() - m.setScale(sx, sy, px, py) - self.postConcat(m) - - def postRotate(self, degrees: float, px: float = 0.0, py: float = 0.0): - m = Matrix() - m.setRotate(degrees, px, py) - self.postConcat(m) - - def postSkew(self, kx: float, ky: float, px: float = 0.0, py: float = 0.0): - m = Matrix() - m.setSkew(kx, ky, px, py) - self.postConcat(m) - - def setRectToRect(self, src: RectF, dst: RectF, stf: int) -> bool: - if src.isEmpty() or dst.isEmpty(): - return False - sx = dst.width() / src.width() - sy = dst.height() / src.height() - tx = dst.left - src.left * sx - ty = dst.top - src.top * sy - self._values = [sx, 0, tx, 0, sy, ty, 0, 0, 1] - return True - - def mapRect(self, src: RectF, dst: RectF = None) -> RectF: - pts = [src.left, src.top, src.right, src.bottom] - self.mapPoints(pts) - if dst: - dst.set(pts[0], pts[1], pts[2], pts[3]) - return dst - return RectF(pts[0], pts[1], pts[2], pts[3]) - - def mapPoints(self, pts: List[float], offset: int = 0, count: int = -1): - if count < 0: - count = len(pts) - offset - v = self._values - for i in range(offset, offset + count, 2): - x = pts[i] - y = pts[i + 1] - pts[i] = x * v[0] + y * v[1] + v[2] - pts[i + 1] = x * v[3] + y * v[4] + v[5] - - def mapVectors(self, vecs: List[float]): - v = self._values - for i in range(0, len(vecs), 2): - x = vecs[i] - y = vecs[i + 1] - vecs[i] = x * v[0] + y * v[1] - vecs[i + 1] = x * v[3] + y * v[4] - - def mapRadius(self, radius: float) -> float: - v = self._values - sx = math.sqrt(v[0] * v[0] + v[3] * v[3]) - sy = math.sqrt(v[1] * v[1] + v[4] * v[4]) - return radius * max(sx, sy) - - def toString(self) -> str: - return f"Matrix({self._values})" - - -Matrix.IDENTITY_MATRIX = Matrix() - - -# ═══════════════════════════════════════════════════════════════ -# Shader -# ═══════════════════════════════════════════════════════════════ - -class Shader(Stub): - """Shader — 着色器基类""" - - class TileMode: - CLAMP = 0 - REPEAT = 1 - MIRROR = 2 - - def __init__(self): - super().__init__() - self._local_matrix = None - - def setLocalMatrix(self, matrix: Matrix): - self._local_matrix = matrix - - def getLocalMatrix(self) -> Optional[Matrix]: - return self._local_matrix - - -# ═══════════════════════════════════════════════════════════════ -# LinearGradient -# ═══════════════════════════════════════════════════════════════ - -class LinearGradient(Shader): - """LinearGradient — 线性渐变""" - - def __init__(self, x0: float, y0: float, x1: float, y1: float, - colors: Sequence[int], positions: Sequence[float], - tile: int = Shader.TileMode.CLAMP): - super().__init__() - self._x0, self._y0 = x0, y0 - self._x1, self._y1 = x1, y1 - self._colors = list(colors) - self._positions = list(positions) if positions else None - self._tile = tile - - -# ═══════════════════════════════════════════════════════════════ -# RadialGradient -# ═══════════════════════════════════════════════════════════════ - -class RadialGradient(Shader): - """RadialGradient — 径向渐变""" - - CENTER_COLOR = 0 - EDGE_COLOR = 1 - - def __init__(self, x: float, y: float, radius: float, - colors: Sequence[int], positions: Sequence[float], - tile: int = Shader.TileMode.CLAMP, - centerColor: int = Shader.TileMode.CLAMP): - """centerColor is ignored, kept for API compatibility""" - super().__init__() - self._x, self._y = x, y - self._radius = radius - self._colors = list(colors) - self._positions = list(positions) if positions else None - self._tile = tile - - def getX(self) -> float: - return self._x - - def getY(self) -> float: - return self._y - - def getRadius(self) -> float: - return self._radius - - -# ═══════════════════════════════════════════════════════════════ -# SweepGradient -# ═══════════════════════════════════════════════════════════════ - -class SweepGradient(Shader): - """SweepGradient — 扫描渐变""" - - def __init__(self, x: float, y: float, - colors: Sequence[int], positions: Sequence[float]): - super().__init__() - self._x, self._y = x, y - self._colors = list(colors) - self._positions = list(positions) if positions else None - - def getX(self) -> float: - return self._x - - def getY(self) -> float: - return self._y - - -# ═══════════════════════════════════════════════════════════════ -# BitmapShader -# ═══════════════════════════════════════════════════════════════ - -class BitmapShader(Shader): - """BitmapShader — 位图着色器""" - - def __init__(self, bitmap: "Bitmap", tileX: int, tileY: int): - super().__init__() - self._bitmap = bitmap - self._tile_x = tileX - self._tile_y = tileY - - def getBitmap(self) -> "Bitmap": - return self._bitmap - - -# ═══════════════════════════════════════════════════════════════ -# ComposeShader -# ═══════════════════════════════════════════════════════════════ - -class ComposeShader(Shader): - """ComposeShader — 组合着色器""" - - def __init__(self, shaderA: Shader, shaderB: Shader, mode): - super().__init__() - self._shader_a = shaderA - self._shader_b = shaderB - self._mode = mode - - -# ═══════════════════════════════════════════════════════════════ -# MaskFilter / BlurMaskFilter / EmbossMaskFilter -# ═══════════════════════════════════════════════════════════════ - -class MaskFilter(Stub): - """MaskFilter — 遮罩过滤器基类""" - pass - - -class BlurMaskFilter(MaskFilter): - """BlurMaskFilter — 模糊遮罩过滤器""" - - NORMAL = 0 - SOLID = 1 - OUTER = 2 - INNER = 3 - - def __init__(self, radius: float, style: int): - super().__init__() - self._radius = radius - self._style = style - - def getRadius(self) -> float: - return self._radius - - def getStyle(self) -> int: - return self._style - - -class EmbossMaskFilter(MaskFilter): - """EmbossMaskFilter — 浮雕遮罩过滤器""" - - def __init__(self, direction: List[float], ambient: float, specular: float, blurRadius: float): - super().__init__() - self._direction = list(direction) - self._ambient = ambient - self._specular = specular - self._blur_radius = blurRadius - - -# ═══════════════════════════════════════════════════════════════ -# PathEffect hierarchy -# ═══════════════════════════════════════════════════════════════ - -class PathEffect(Stub): - """PathEffect — 路径效果基类""" - pass - - -class ComposePathEffect(PathEffect): - """ComposePathEffect — 组合路径效果""" - - def __init__(self, outerpe: PathEffect, innerpe: PathEffect): - super().__init__() - self._outer = outerpe - self._inner = innerpe - - -class CornerPathEffect(PathEffect): - """CornerPathEffect — 圆角路径效果""" - - def __init__(self, radius: float): - super().__init__() - self._radius = radius - - -class DashPathEffect(PathEffect): - """DashPathEffect — 虚线路径效果""" - - def __init__(self, intervals: List[float], phase: float): - super().__init__() - self._intervals = list(intervals) - self._phase = phase - - -class DiscretePathEffect(PathEffect): - """DiscretePathEffect — 离散路径效果""" - - def __init__(self, segmentLength: float, deviation: float): - super().__init__() - self._segment_length = segmentLength - self._deviation = deviation - - -class PathDashPathEffect(PathEffect): - """PathDashPathEffect — 路径虚线效果""" - - ROTATE = 0 - MORPH = 1 - TRANSLATE = 2 - - def __init__(self, shape: Path, advance: float, phase: float, style: int): - super().__init__() - self._shape = shape - self._advance = advance - self._phase = phase - self._style = style - - -class SumPathEffect(PathEffect): - """SumPathEffect — 求和路径效果""" - - def __init__(self, first: PathEffect, second: PathEffect): - super().__init__() - self._first = first - self._second = second - - -# ═══════════════════════════════════════════════════════════════ -# Xfermode hierarchy -# ═══════════════════════════════════════════════════════════════ - -class Xfermode(Stub): - """Xfermode — 传输模式基类""" - pass - - -class PorterDuffXfermode(Xfermode): - """PorterDuffXfermode — PorterDuff 传输模式""" - - def __init__(self, mode): - super().__init__() - self._mode = mode - - def getMode(self): - return self._mode - - -class AvoidXfermode(Xfermode): - """AvoidXfermode — deprecated, kept for API compatibility""" - - TARGET = 0 - AVOID = 1 - - def __init__(self, opColor: int, tolerance: int, mode: int): - super().__init__() - self._op_color = opColor - self._tolerance = tolerance - self._mode = mode - - -class PixelXorXfermode(Xfermode): - """PixelXorXfermode — deprecated, kept for API compatibility""" - - def __init__(self, opColor: int): - super().__init__() - self._op_color = opColor - - -# ═══════════════════════════════════════════════════════════════ -# DrawFilter -# ═══════════════════════════════════════════════════════════════ - -class DrawFilter(Stub): - """DrawFilter — 绘制过滤器基类""" - pass - - -class PaintFlagsDrawFilter(DrawFilter): - """PaintFlagsDrawFilter — 画笔标志绘制过滤器""" - - def __init__(self, clearBits: int, setBits: int): - super().__init__() - self._clear_bits = clearBits - self._set_bits = setBits - - -# ═══════════════════════════════════════════════════════════════ -# Camera -# ═══════════════════════════════════════════════════════════════ - -class Camera(Stub): - """Camera — 3D 相机变换""" - - def __init__(self): - super().__init__() - self._location_x = 0.0 - self._location_y = 0.0 - self._location_z = -8.0 - self._rot_x = 0.0 - self._rot_y = 0.0 - self._rot_z = 0.0 - self._trans_x = 0.0 - self._trans_y = 0.0 - self._trans_z = 0.0 - self._stack: List[Tuple[float, float, float, float, float, float, float, float, float]] = [] - - def save(self): - """保存当前变换状态到栈。""" - self._stack.append((self._rot_x, self._rot_y, self._rot_z, - self._trans_x, self._trans_y, self._trans_z, - self._location_x, self._location_y, self._location_z)) - - def restore(self): - """从栈恢复上次保存的变换状态。""" - if self._stack: - (self._rot_x, self._rot_y, self._rot_z, - self._trans_x, self._trans_y, self._trans_z, - self._location_x, self._location_y, self._location_z) = self._stack.pop() - - def translate(self, x: float, y: float, z: float): - self._trans_x += x - self._trans_y += y - self._trans_z += z - - def rotateX(self, deg: float): - """绕 X 轴旋转(累积角度)。""" - self._rot_x += deg - - def rotateY(self, deg: float): - """绕 Y 轴旋转(累积角度)。""" - self._rot_y += deg - - def rotateZ(self, deg: float): - """绕 Z 轴旋转(累积角度)。""" - self._rot_z += deg - - def rotate(self, x: float, y: float, z: float): - """同时绕 X/Y/Z 轴旋转。""" - self._rot_x += x - self._rot_y += y - self._rot_z += z - - def _compute_matrix(self) -> List[float]: - """计算 4x4 变换矩阵并投影为 3x3 仿射矩阵。""" - rx = math.radians(self._rot_x) - ry = math.radians(self._rot_y) - rz = math.radians(self._rot_z) - cx, sx = math.cos(rx), math.sin(rx) - cy, sy = math.cos(ry), math.sin(ry) - cz, sz = math.cos(rz), math.sin(rz) - # 旋转矩阵 R = Rz * Ry * Rx - r00 = cz * cy - r01 = cz * sy * sx - sz * cx - r02 = cz * sy * cx + sz * sx - r10 = sz * cy - r11 = sz * sy * sx + cz * cx - r12 = sz * sy * cx - cz * sx - r20 = -sy - r21 = cy * sx - r22 = cy * cx - # 应用平移 - tx = self._trans_x - ty = self._trans_y - tz = self._trans_z - # 相机位置(z 越负越远) - cam_z = self._location_z - # 透视投影:z' = z + cam_z,当 cam_z != 0 时做透视除法 - # 简化为:将 3D 点投影到 2D - # 点 (x,y,z) -> (x*r00+y*r01+z*r02+tx, x*r10+y*r11+z*r12+ty, x*r20+y*r21+z*r22+tz) - # 透视:screen_x = x' * d / (d - z'), screen_y = y' * d / (d - z') - d = -cam_z # 相机到原点距离 - if abs(d) < 1e-10: - # 正交投影 - return [r00, r01, tx, r10, r11, ty, 0.0, 0.0, 1.0] - # 透视投影矩阵(近似):将 z 影响缩放到 x,y - # m[0]=r00, m[1]=r01, m[2]=tx, m[3]=r10, m[4]=r11, m[5]=ty - # 透视项:m[6]=r20/d, m[7]=r21/d, m[8]=1 - tz/d - return [r00, r01, tx, - r10, r11, ty, - r20 / d, r21 / d, 1.0 - tz / d] - - def getMatrix(self, matrix: Matrix): - """计算变换矩阵并写入给定的 Matrix。""" - matrix._values = self._compute_matrix() - - def applyToCanvas(self, canvas: "Canvas"): - """将相机变换应用到 Canvas。""" - m = Matrix() - self.getMatrix(m) - if hasattr(canvas, "concat"): - canvas.concat(m) - - def setLocation(self, x: float, y: float, z: float): - self._location_x = x - self._location_y = y - self._location_z = z - - def dotWithNormal(self, dx: float, dy: float, dz: float) -> float: - """计算向量与变换后 Z 轴法线的点积。""" - rx = math.radians(self._rot_x) - ry = math.radians(self._rot_y) - rz = math.radians(self._rot_z) - cy, sy = math.cos(ry), math.sin(ry) - cx, sx = math.cos(rx), math.sin(rx) - # 变换后的 Z 轴法线 - nz_x = sy - nz_y = -cy * sx - nz_z = cy * cx - return dx * nz_x + dy * nz_y + dz * nz_z - - -# ═══════════════════════════════════════════════════════════════ -# LayerRasterizer / Rasterizer -# ═══════════════════════════════════════════════════════════════ - -class Rasterizer(Stub): - """Rasterizer — 光栅化基类 (deprecated)""" - pass - - -class LayerRasterizer(Rasterizer): - """LayerRasterizer — 分层光栅化 (deprecated)""" - - def __init__(self): - super().__init__() - - def addLayer(self, paint: "Paint", dx: float = 0.0, dy: float = 0.0): - pass - - -# ═══════════════════════════════════════════════════════════════ -# NinePatch -# ═══════════════════════════════════════════════════════════════ - -class NinePatch(Stub): - """NinePatch — 九宫格图片""" - - def __init__(self, bitmap: "Bitmap", chunk: bytes): - super().__init__() - self._bitmap = bitmap - self._chunk = chunk if chunk else b"" - - def getBitmap(self) -> "Bitmap": - return self._bitmap - - def getWidth(self) -> int: - return self._bitmap.getWidth() if self._bitmap else 0 - - def getHeight(self) -> int: - return self._bitmap.getHeight() if self._bitmap else 0 - - @staticmethod - def isNinePatchChunk(chunk: bytes) -> bool: - return bool(chunk and len(chunk) > 0) - - -# ═══════════════════════════════════════════════════════════════ -# Picture / PictureDrawable -# ═══════════════════════════════════════════════════════════════ - -class Picture(Stub): - """Picture — 图画录制与回放""" - - def __init__(self): - super().__init__() - self._recorded = [] - - def beginRecording(self, width: int, height: int) -> "Canvas": - canvas = Canvas(width, height) - self._canvas = canvas - return canvas - - def endRecording(self): - self._canvas = None - - def draw(self, canvas: "Canvas"): - """回放录制的绘制命令到指定 Canvas。""" - for cmd in self._recorded: - method = cmd.get("method") - args = cmd.get("args", []) - if method and hasattr(canvas, method): - try: - getattr(canvas, method)(*args) - except Exception: - pass - - def getWidth(self) -> int: - return self._canvas.getWidth() if hasattr(self, "_canvas") and self._canvas else 0 - - def getHeight(self) -> int: - return self._canvas.getHeight() if hasattr(self, "_canvas") and self._canvas else 0 - - def createFromStream(self, stream) -> "Picture": - return Picture() - - def writeToStream(self, stream) -> bool: - return True - - -class PictureDrawable(Stub): - """PictureDrawable — 图画 Drawable""" - - def __init__(self, picture: Picture): - super().__init__() - self._picture = picture - - def getPicture(self) -> Picture: - return self._picture - - def setPicture(self, picture: Picture): - self._picture = picture - - def draw(self, canvas: "Canvas"): - """将 Picture 回放到指定 Canvas。""" - if self._picture is not None: - self._picture.draw(canvas) - - -# ═══════════════════════════════════════════════════════════════ -# SurfaceTexture -# ═══════════════════════════════════════════════════════════════ - -class SurfaceTexture(Stub): - """SurfaceTexture — 表面纹理""" - - def __init__(self, texName: int = 0): - super().__init__() - self._tex_name = texName - self._timestamp = 0 - self._buffer_width = 0 - self._buffer_height = 0 - self._attached = texName != 0 - self._released = False - - def getTimestamp(self) -> int: - return self._timestamp - - def updateTexImage(self): - self._timestamp = int(time.time() * 1e9) - - def release(self): - """释放 SurfaceTexture 资源。""" - self._released = True - self._attached = False - - def setDefaultBufferSize(self, width: int, height: int): - """设置默认缓冲区大小。""" - self._buffer_width = width - self._buffer_height = height - - def attachToGLContext(self, texName: int): - """将 SurfaceTexture 附加到 GL 纹理。""" - self._tex_name = texName - self._attached = True - self._released = False - - def detachFromGLContext(self): - """从 GL 纹理分离 SurfaceTexture。""" - self._attached = False - - -# ═══════════════════════════════════════════════════════════════ -# Region / RegionIterator -# ═══════════════════════════════════════════════════════════════ - -class Region(Stub): - """Region — 区域""" - - OP_DIFFERENCE = 0 - OP_INTERSECT = 1 - OP_UNION = 2 - OP_XOR = 3 - OP_REVERSE_DIFFERENCE = 4 - OP_REPLACE = 5 - - def __init__(self, *args): - super().__init__() - if len(args) == 4: - self._rect = Rect(args[0], args[1], args[2], args[3]) - elif len(args) == 1 and isinstance(args[0], Rect): - self._rect = Rect(args[0].left, args[0].top, args[0].right, args[0].bottom) - else: - self._rect = Rect() - - def set(self, *args) -> bool: - if len(args) == 4: - self._rect.set(args[0], args[1], args[2], args[3]) - elif len(args) == 1 and isinstance(args[0], Rect): - r = args[0] - self._rect.set(r.left, r.top, r.right, r.bottom) - return True - - def getBounds(self, r: Rect = None) -> Rect: - if r: - r.set(self._rect.left, self._rect.top, self._rect.right, self._rect.bottom) - return r - return Rect(self._rect.left, self._rect.top, self._rect.right, self._rect.bottom) - - def isEmpty(self) -> bool: - return self._rect.isEmpty() - - def contains(self, x: int, y: int) -> bool: - return self._rect.contains(x, y) - - def op(self, *args) -> bool: - """对区域执行布尔运算。支持 op(rect, op) / op(region, op) / op(left,top,right,bottom,op)。""" - if len(args) == 2: - operand, op_code = args - if isinstance(operand, Rect): - other = Rect(operand.left, operand.top, operand.right, operand.bottom) - elif isinstance(operand, Region): - other = Rect(operand._rect.left, operand._rect.top, - operand._rect.right, operand._rect.bottom) - else: - return False - elif len(args) == 5: - left, top, right, bottom, op_code = args - other = Rect(left, top, right, bottom) - else: - return False - self_r = self._rect - if op_code == Region.OP_INTERSECT: - nL = max(self_r.left, other.left) - nT = max(self_r.top, other.top) - nR = min(self_r.right, other.right) - nB = min(self_r.bottom, other.bottom) - if nL < nR and nT < nB: - self_r.set(nL, nT, nR, nB) - return True - self_r.setEmpty() - return False - if op_code == Region.OP_UNION: - if self_r.isEmpty(): - self_r.set(other.left, other.top, other.right, other.bottom) - else: - self_r.left = min(self_r.left, other.left) - self_r.top = min(self_r.top, other.top) - self_r.right = max(self_r.right, other.right) - self_r.bottom = max(self_r.bottom, other.bottom) - return True - if op_code == Region.OP_REPLACE: - self_r.set(other.left, other.top, other.right, other.bottom) - return True - if op_code == Region.OP_DIFFERENCE: - # 差集:若不相交则不变;若完全包含则清空;否则近似为不变 - if not Rect.intersects(self_r, other): - return True - if other.contains(self_r.left, self_r.top) and other.contains(self_r.right - 1, self_r.bottom - 1): - self_r.setEmpty() - return False - return True - if op_code == Region.OP_REVERSE_DIFFERENCE: - self_r.set(other.left, other.top, other.right, other.bottom) - return True - if op_code == Region.OP_XOR: - if not Rect.intersects(self_r, other): - self_r.union(other.left, other.top, other.right, other.bottom) - return True - return False - - def isRect(self) -> bool: - return True - - def isComplex(self) -> bool: - return False - - def toString(self) -> str: - return f"Region({self._rect})" - - -class RegionIterator(Stub): - """RegionIterator — 区域迭代器""" - - def __init__(self, region: Region): - super().__init__() - self._region = region - self._done = False - - def next(self, r: Rect) -> bool: - if self._done: - return False - r.set(self._region._rect.left, self._region._rect.top, - self._region._rect.right, self._region._rect.bottom) - self._done = True - return True - - -# ═══════════════════════════════════════════════════════════════ -# Paint -# ═══════════════════════════════════════════════════════════════ - -class Paint(Stub): - """Paint — 画笔""" - - class Style: - FILL = 0 - STROKE = 1 - FILL_AND_STROKE = 2 - - class Cap: - BUTT = 0 - ROUND = 1 - SQUARE = 2 - - class Join: - MITER = 0 - ROUND = 1 - BEVEL = 2 - - class Align: - LEFT = 0 - CENTER = 1 - RIGHT = 2 - - ANTI_ALIAS_FLAG = 0x01 - FILTER_BITMAP_FLAG = 0x02 - DITHER_FLAG = 0x04 - UNDERLINE_TEXT_FLAG = 0x08 - STRIKE_THRU_TEXT_FLAG = 0x10 - FAKE_BOLD_TEXT_FLAG = 0x20 - LINEAR_TEXT_FLAG = 0x40 - SUBPIXEL_TEXT_FLAG = 0x80 - DEV_KERN_TEXT_FLAG = 0x100 - - ALIGN_LEFT = 0 - ALIGN_CENTER = 1 - ALIGN_RIGHT = 2 - - def __init__(self, flags: int = 0): - super().__init__() - self._flags = flags - self._color = 0xFF000000 - self._alpha = 255 - self._style = Paint.Style.FILL - self._stroke_width = 0.0 - self._text_size = 12.0 - self._typeface = None - self._anti_alias = bool(flags & Paint.ANTI_ALIAS_FLAG) - self._color_filter = None - self._mask_filter = None - self._shader = None - self._path_effect = None - self._xfermode = None - self._filter_bitmap = bool(flags & Paint.FILTER_BITMAP_FLAG) - self._dither = bool(flags & Paint.DITHER_FLAG) - self._subpixel_text = bool(flags & Paint.SUBPIXEL_TEXT_FLAG) - self._underline_text = bool(flags & Paint.UNDERLINE_TEXT_FLAG) - self._strike_thru_text = bool(flags & Paint.STRIKE_THRU_TEXT_FLAG) - self._text_align = Paint.Align.LEFT - self._text_scale_x = 1.0 - self._text_skew_x = 0.0 - self._shadow_layer = None - - def setColor(self, color: int): - self._color = color - self._alpha = (color >> 24) & 0xFF - - def getColor(self) -> int: - return self._color - - def setARGB(self, a: int, r: int, g: int, b: int): - self.setColor(Color.argb(a, r, g, b)) - - def setAlpha(self, alpha: int): - self._alpha = max(0, min(255, alpha)) - self._color = (self._color & 0x00FFFFFF) | (self._alpha << 24) - - def getAlpha(self) -> int: - return self._alpha - - def setStyle(self, style: int): - self._style = style - - def getStyle(self) -> int: - return self._style - - def setStrokeWidth(self, width: float): - self._stroke_width = width - - def getStrokeWidth(self) -> float: - return self._stroke_width - - def setTextSize(self, textSize: float): - self._text_size = textSize - self._font_cache = None - - def getTextSize(self) -> float: - return self._text_size - - def _get_font(self): - """获取 PIL ImageFont 对象(带缓存)。""" - if not HAS_PIL: - return None - cache_key = (self._text_size, id(self._typeface) if self._typeface else None) - if getattr(self, "_font_cache_key", None) == cache_key and getattr(self, "_font_cache", None): - return self._font_cache - try: - from PIL import ImageFont - font_path = None - if self._typeface and hasattr(self._typeface, "_path") and self._typeface._path: - font_path = self._typeface._path - if font_path and os.path.exists(font_path): - self._font_cache = ImageFont.truetype(font_path, int(self._text_size)) - else: - self._font_cache = ImageFont.load_default() - self._font_cache_key = cache_key - return self._font_cache - except Exception: - return None - - def setTypeface(self, typeface: Optional[Typeface]): - self._typeface = typeface - - def getTypeface(self) -> Optional[Typeface]: - return self._typeface - - def setAntiAlias(self, aa: bool): - self._anti_alias = aa - - def isAntiAlias(self) -> bool: - return self._anti_alias - - def setColorFilter(self, filter: Optional[ColorFilter]): - self._color_filter = filter - - def getColorFilter(self) -> Optional[ColorFilter]: - return self._color_filter - - def setMaskFilter(self, maskfilter: Optional[MaskFilter]): - self._mask_filter = maskfilter - - def getMaskFilter(self) -> Optional[MaskFilter]: - return self._mask_filter - - def setShader(self, shader: Optional[Shader]): - self._shader = shader - - def getShader(self) -> Optional[Shader]: - return self._shader - - def setPathEffect(self, effect: Optional[PathEffect]): - self._path_effect = effect - - def getPathEffect(self) -> Optional[PathEffect]: - return self._path_effect - - def setXfermode(self, xfermode: Optional[Xfermode]): - self._xfermode = xfermode - - def getXfermode(self) -> Optional[Xfermode]: - return self._xfermode - - def setFlags(self, flags: int): - self._flags = flags - - def getFlags(self) -> int: - return self._flags - - def setFilterBitmap(self, filter: bool): - self._filter_bitmap = filter - - def isFilterBitmap(self) -> bool: - return self._filter_bitmap - - def setDither(self, dither: bool): - self._dither = dither - - def isDither(self) -> bool: - return self._dither - - def setSubpixelText(self, subpixel: bool): - self._subpixel_text = subpixel - - def isSubpixelText(self) -> bool: - return self._subpixel_text - - def setUnderlineText(self, underline: bool): - self._underline_text = underline - - def isUnderlineText(self) -> bool: - return self._underline_text - - def setStrikeThruText(self, strike: bool): - self._strike_thru_text = strike - - def isStrikeThruText(self) -> bool: - return self._strike_thru_text - - def setTextScaleX(self, scaleX: float): - self._text_scale_x = scaleX - - def getTextScaleX(self) -> float: - return self._text_scale_x - - def setTextSkewX(self, skewX: float): - self._text_skew_x = skewX - - def getTextSkewX(self) -> float: - return self._text_skew_x - - def setTextAlign(self, align: int): - self._text_align = align - - def getTextAlign(self) -> int: - return self._text_align - - def setTextLocales(self, locales): - pass - - def setShadowLayer(self, radius: float, dx: float, dy: float, color: int): - self._shadow_layer = (radius, dx, dy, color) - - def clearShadowLayer(self): - self._shadow_layer = None - - def measureText(self, text: str) -> float: - """测量文本宽度。优先使用 PIL ImageFont 获取精确宽度。""" - font = self._get_font() - if font is not None: - try: - if hasattr(font, "getlength"): - return float(font.getlength(text)) - bbox = font.getbbox(text) - return float(bbox[2] - bbox[0]) - except Exception: - pass - return len(text) * self._text_size * 0.5 - - def breakText(self, text: str, maxWidth: float, measuredWidth: List[float]) -> int: - """在最大宽度内截断文本,返回可容纳的字符数。""" - font = self._get_font() - width = 0.0 - count = 0 - for i, ch in enumerate(text): - if font is not None: - try: - cw = font.getlength(ch) if hasattr(font, "getlength") else (font.getbbox(ch)[2]) - except Exception: - cw = self._text_size * 0.5 - else: - cw = self._text_size * 0.5 - if width + cw > maxWidth: - break - width += cw - count += 1 - measuredWidth[0] = width - return count - - def getTextWidths(self, text: str, widths: List[float]) -> int: - """获取每个字符的宽度。""" - font = self._get_font() - for i, ch in enumerate(text): - if font is not None: - try: - widths[i] = float(font.getlength(ch)) if hasattr(font, "getlength") else float(font.getbbox(ch)[2]) - except Exception: - widths[i] = self._text_size * 0.5 - else: - widths[i] = self._text_size * 0.5 - return len(text) - - def getTextPath(self, text: str, start: int, end: int, x: float, y: float, path: Path): - path.moveTo(x, y) - for i in range(start, end): - path.lineTo(x + (i - start + 1) * self._text_size * 0.3, y) - - def getFontMetrics(self) -> Dict[str, float]: - """返回字体度量信息。优先使用 PIL ImageFont 获取真实度量。""" - size = self._text_size - font = self._get_font() - if font is not None: - try: - if hasattr(font, "getmetrics"): - ascent, descent = font.getmetrics() - return { - "ascent": -float(ascent), - "descent": float(descent), - "top": -float(ascent) - 2.0, - "bottom": float(descent) + 2.0, - "leading": 0.0, - } - except Exception: - pass - return { - "ascent": -size * 0.8, - "descent": size * 0.2, - "top": -size * 0.95, - "bottom": size * 0.25, - "leading": 0.0, - } - - def getFontMetricsInt(self) -> Dict[str, int]: - fm = self.getFontMetrics() - return {k: int(v) for k, v in fm.items()} - - def ascent(self) -> float: - return -self._text_size * 0.8 - - def descent(self) -> float: - return self._text_size * 0.2 - - def setStrokeCap(self, cap): - self._stroke_cap = cap - - def getStrokeCap(self): - return getattr(self, '_stroke_cap', 0) - - def setStrokeJoin(self, join): - self._stroke_join = join - - def getStrokeJoin(self): - return getattr(self, '_stroke_join', 0) - - def setStrokeMiter(self, miter): - self._stroke_miter = miter - - def getStrokeMiter(self): - return getattr(self, '_stroke_miter', 4.0) - - def getTextBounds(self, text, start, end, bounds): - """返回文本边界矩形。优先使用 PIL ImageFont 获取真实边界。""" - font = self._get_font() - if font is not None: - try: - sub = text[start:end] - bbox = font.getbbox(sub) - bounds.left = int(bbox[0]) - bounds.top = int(bbox[1]) - bounds.right = int(bbox[2]) - bounds.bottom = int(bbox[3]) - return - except Exception: - pass - if hasattr(self, '_text_size'): - w = len(text[start:end]) * self._text_size * 0.5 - h = self._text_size - else: - w = len(text[start:end]) * 12 - h = 24 - bounds.left = 0 - bounds.top = -int(h * 0.8) - bounds.right = int(w) - bounds.bottom = int(h * 0.2) - - def setBlendMode(self, blendmode): - self._blend_mode = blendmode - - def getBlendMode(self): - return getattr(self, '_blend_mode', None) - - def setLetterSpacing(self, letterSpacing): - self._letter_spacing = letterSpacing - - def getLetterSpacing(self): - return getattr(self, '_letter_spacing', 0.0) - - def setFontFeatureSettings(self, settings): - self._font_feature_settings = settings - - def getFontFeatureSettings(self): - return getattr(self, '_font_feature_settings', None) - - def setHinting(self, mode): - self._hinting = mode - - def getHinting(self): - return getattr(self, '_hinting', 2) # HINTING_ON - - def setFakeBoldText(self, fakeBoldText): - self._fake_bold_text = fakeBoldText - - def isFakeBoldText(self): - return getattr(self, '_fake_bold_text', False) - - def hasGlyph(self, string): - return len(string) > 0 - - -# ═══════════════════════════════════════════════════════════════ -# Bitmap -# ═══════════════════════════════════════════════════════════════ - -class Bitmap(Stub): - """Bitmap — 位图""" - - class Config: - ALPHA_8 = "ALPHA_8" - ARGB_4444 = "ARGB_4444" - ARGB_8888 = "ARGB_8888" - RGB_565 = "RGB_565" - RGBA_F16 = "RGBA_F16" - - class CompressFormat: - JPEG = "JPEG" - PNG = "PNG" - WEBP = "WEBP" - WEBP_LOSSY = "WEBP_LOSSY" - WEBP_LOSSLESS = "WEBP_LOSSLESS" - - def __init__(self, width: int, height: int, config: str = Config.ARGB_8888): - super().__init__() - self._width = width - self._height = height - self._config = config - self._recycled = False - self._has_alpha = config in (Bitmap.Config.ARGB_8888, Bitmap.Config.ARGB_4444, - Bitmap.Config.ALPHA_8, Bitmap.Config.RGBA_F16) - self._generation_id = 1 - - if HAS_PIL: - from PIL import Image - if config == Bitmap.Config.ALPHA_8: - mode = "L" - elif config == Bitmap.Config.RGB_565: - mode = "RGB" - else: - mode = "RGBA" - self._image = Image.new(mode, (width, height), (0, 0, 0, 0)) - else: - bpp = 4 if config == Bitmap.Config.ARGB_8888 else ( - 2 if config in (Bitmap.Config.ARGB_4444, Bitmap.Config.RGB_565) else 1 - ) - self._pixels = bytearray(width * height * bpp) - - @staticmethod - def createBitmap(width, height=None, config=None): - if isinstance(width, Bitmap): - src = width - dst = Bitmap(src._width, src._height, src._config) - if HAS_PIL and hasattr(src, "_image"): - from PIL import Image - dst._image = src._image.copy() - elif hasattr(src, "_pixels"): - dst._pixels = bytearray(src._pixels) - return dst - return Bitmap(width, height, config) - - @staticmethod - def createScaledBitmap(src: "Bitmap", dstWidth: int, dstHeight: int, - filter: bool = True) -> "Bitmap": - if HAS_PIL and hasattr(src, "_image"): - from PIL import Image - dst = Bitmap(dstWidth, dstHeight, src._config) - dst._image = src._image.resize((dstWidth, dstHeight), Image.LANCZOS if filter else Image.NEAREST) - return dst - dst = Bitmap(dstWidth, dstHeight, src._config) - return dst - - def copy(self, config: str, isMutable: bool) -> "Bitmap": - dst = Bitmap(self._width, self._height, config) - if HAS_PIL and hasattr(self, "_image"): - from PIL import Image - if config != self._config: - mode_map = {Bitmap.Config.ARGB_8888: "RGBA", Bitmap.Config.RGB_565: "RGB", - Bitmap.Config.ALPHA_8: "L"} - dst._image = self._image.convert(mode_map.get(config, "RGBA")) - else: - dst._image = self._image.copy() - elif hasattr(self, "_pixels"): - dst._pixels = bytearray(self._pixels) - return dst - - def compress(self, format: str, quality: int, stream) -> bool: - if self._recycled: - return False - if HAS_PIL and hasattr(self, "_image"): - pil_fmt = format.lower() - if pil_fmt == "jpeg": - pil_fmt = "jpeg" - elif pil_fmt == "webp": - pil_fmt = "webp" - else: - pil_fmt = "png" - if pil_fmt == "jpeg" and self._image.mode == "RGBA": - self._image = self._image.convert("RGB") - self._image.save(stream, format=pil_fmt.upper(), quality=quality) - return True - return False - - def recycle(self): - self._recycled = True - if HAS_PIL and hasattr(self, "_image"): - self._image = None - if hasattr(self, "_pixels"): - self._pixels = None - - def isRecycled(self) -> bool: - return self._recycled - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def getConfig(self) -> str: - return self._config - - def getRowBytes(self) -> int: - bpp = {"ALPHA_8": 1, "ARGB_4444": 2, "ARGB_8888": 4, "RGB_565": 2, "RGBA_F16": 8} - return self._width * bpp.get(self._config, 4) - - def getByteCount(self) -> int: - return self.getRowBytes() * self._height - - def getAllocationByteCount(self) -> int: - return self.getByteCount() - - def getPixel(self, x: int, y: int) -> int: - if self._recycled: - return 0 - if HAS_PIL and hasattr(self, "_image"): - p = self._image.getpixel((x, y)) - if self._image.mode == "RGBA": - return Color.argb(p[3], p[0], p[1], p[2]) - elif self._image.mode == "RGB": - return Color.rgb(p[0], p[1], p[2]) - elif self._image.mode == "L": - return Color.argb(255, p, p, p) - return 0 - - def getPixels(self, pixels: List[int], offset: int, stride: int, - x: int, y: int, width: int, height: int): - if self._recycled: - return - for row in range(height): - for col in range(width): - px = x + col - py = y + row - idx = offset + row * stride + col - if 0 <= idx < len(pixels) and 0 <= px < self._width and 0 <= py < self._height: - pixels[idx] = self.getPixel(px, py) - - def setPixel(self, x: int, y: int, color: int): - if self._recycled: - return - if HAS_PIL and hasattr(self, "_image"): - if self._image.mode == "RGBA": - self._image.putpixel((x, y), ( - Color.red(color), Color.green(color), - Color.blue(color), Color.alpha(color) - )) - elif self._image.mode == "RGB": - self._image.putpixel((x, y), ( - Color.red(color), Color.green(color), Color.blue(color) - )) - elif self._image.mode == "L": - self._image.putpixel((x, y), Color.red(color)) - else: - pass - - def setPixels(self, pixels: List[int], offset: int, stride: int, - x: int, y: int, width: int, height: int): - for row in range(height): - for col in range(width): - idx = offset + row * stride + col - if idx < len(pixels): - self.setPixel(x + col, y + row, pixels[idx]) - - def eraseColor(self, color: int): - if self._recycled: - return - if HAS_PIL and hasattr(self, "_image"): - from PIL import Image - w, h = self._image.size - if self._image.mode == "RGBA": - self._image = Image.new("RGBA", (w, h), - (Color.red(color), Color.green(color), - Color.blue(color), Color.alpha(color))) - elif self._image.mode == "RGB": - self._image = Image.new("RGB", (w, h), - (Color.red(color), Color.green(color), Color.blue(color))) - - def extractAlpha(self, paint: Paint = None) -> "Bitmap": - return Bitmap(self._width, self._height, Bitmap.Config.ALPHA_8) - - def sameAs(self, other: "Bitmap") -> bool: - """比较两个 Bitmap 的像素是否完全相同。""" - if not isinstance(other, Bitmap): - return False - if self._width != other._width or self._height != other._height: - return False - if self._config != other._config: - return False - if self._recycled or other._recycled: - return False - # 使用 PIL 比较像素 - if HAS_PIL and hasattr(self, "_image") and hasattr(other, "_image"): - try: - from PIL import ImageChops - diff = ImageChops.difference(self._image, other._image) - return diff.getbbox() is None - except Exception: - return list(self._image.getdata()) == list(other._image.getdata()) - # 回退到字节比较 - if hasattr(self, "_pixels") and hasattr(other, "_pixels"): - return bytes(self._pixels) == bytes(other._pixels) - return True - - def prepareToDraw(self): - """提示将位图数据上传到 GPU 缓存。桌面端为空操作。""" - pass - - def hasAlpha(self) -> bool: - return self._has_alpha - - def setHasAlpha(self, hasAlpha: bool): - self._has_alpha = hasAlpha - - def getGenerationId(self) -> int: - return self._generation_id - - def describeContents(self) -> int: - return 0 - - def writeToParcel(self, dest, flags: int): - pass - - def copyPixelsToBuffer(self, dst): - if HAS_PIL and hasattr(self, "_image"): - buf = self._image.tobytes() - dst.write(buf) - - def copyPixelsFromBuffer(self, src): - if HAS_PIL and hasattr(self, "_image"): - data = src.read() - import io as _io - buf = _io.BytesIO(data) - from PIL import Image - self._image = Image.open(buf) - self._image = self._image.resize((self._width, self._height)) - - def asMutable(self) -> "Bitmap": - return Bitmap.createBitmap(self) - - def toString(self) -> str: - return f"Bitmap({self._width}x{self._height}, config={self._config})" - - -# ═══════════════════════════════════════════════════════════════ -# BitmapFactory -# ═══════════════════════════════════════════════════════════════ - -class BitmapFactory(Stub): - """BitmapFactory — 位图工厂""" - - class Options(Stub): - """BitmapFactory.Options — 解码选项""" - - def __init__(self): - super().__init__() - self.inJustDecodeBounds = False - self.outWidth = 0 - self.outHeight = 0 - self.inSampleSize = 1 - self.inPreferredConfig = Bitmap.Config.ARGB_8888 - self.inDensity = 0 - self.inTargetDensity = 0 - self.inScaled = True - self.inMutable = False - self.outMimeType = "" - self.inPurgeable = False - self.inInputShareable = False - - @staticmethod - def decodeFile(pathName: str, opts: Options = None) -> Optional[Bitmap]: - resolved = resolve_android_path(pathName) - if not os.path.exists(resolved): - return None - if HAS_PIL: - from PIL import Image - try: - img = Image.open(resolved) - if opts and opts.inJustDecodeBounds: - opts.outWidth, opts.outHeight = img.size - opts.outMimeType = f"image/{img.format.lower() if img.format else 'png'}" - return None - w, h = img.size - bm = Bitmap(w, h, opts.inPreferredConfig if opts else Bitmap.Config.ARGB_8888) - if img.mode != "RGBA": - img = img.convert("RGBA") - bm._image = img - return bm - except Exception: - return None - return Bitmap(100, 100, Bitmap.Config.ARGB_8888) - - @staticmethod - def decodeResource(res, resId: int, opts: Options = None) -> Optional[Bitmap]: - """从资源解码位图。尝试通过 Resources 打开原始资源流。""" - # 尝试通过 Resources 获取资源路径或流 - if res is not None: - try: - # 尝试 openRawResource - stream = res.openRawResource(resId) - if stream is not None: - return BitmapFactory.decodeStream(stream, None, opts) - except Exception: - pass - try: - # 尝试通过资源名查找文件 - name = res.getResourceEntryName(resId) if hasattr(res, "getResourceEntryName") else None - if name and name != "unknown": - for ext in (".png", ".jpg", ".jpeg", ".webp", ".bmp"): - candidate = resolve_android_path("res/drawable/" + name + ext) - if os.path.exists(candidate): - return BitmapFactory.decodeFile(candidate, opts) - candidate = resolve_android_path("res/drawable-xxhdpi/" + name + ext) - if os.path.exists(candidate): - return BitmapFactory.decodeFile(candidate, opts) - except Exception: - pass - return Bitmap(100, 100, Bitmap.Config.ARGB_8888) - - @staticmethod - def decodeStream(stream, outPadding: "Rect" = None, opts: Options = None) -> Optional[Bitmap]: - if stream is None: - return None - if HAS_PIL: - from PIL import Image - try: - img = Image.open(stream) - if opts and opts.inJustDecodeBounds: - opts.outWidth, opts.outHeight = img.size - return None - w, h = img.size - bm = Bitmap(w, h, opts.inPreferredConfig if opts else Bitmap.Config.ARGB_8888) - if img.mode != "RGBA": - img = img.convert("RGBA") - bm._image = img - return bm - except Exception: - return None - return Bitmap(100, 100, Bitmap.Config.ARGB_8888) - - @staticmethod - def decodeByteArray(data: bytes, offset: int, length: int, opts: Options = None) -> Optional[Bitmap]: - if not data: - return None - if HAS_PIL: - from PIL import Image - import io as _io - try: - buf = _io.BytesIO(data[offset:offset + length]) - img = Image.open(buf) - if opts and opts.inJustDecodeBounds: - opts.outWidth, opts.outHeight = img.size - return None - w, h = img.size - bm = Bitmap(w, h, opts.inPreferredConfig if opts else Bitmap.Config.ARGB_8888) - if img.mode != "RGBA": - img = img.convert("RGBA") - bm._image = img - return bm - except Exception: - return None - return Bitmap(100, 100, Bitmap.Config.ARGB_8888) - - @staticmethod - def decodeResourceStream(res, value, stream, pad: "Rect", opts: Options = None) -> Optional[Bitmap]: - return BitmapFactory.decodeStream(stream, pad, opts) - - -# ═══════════════════════════════════════════════════════════════ -# Canvas -# ═══════════════════════════════════════════════════════════════ - -class Canvas(Stub): - """Canvas — 画布""" - - ALL_SAVE_FLAG = 0x1F - - def __init__(self, width=0, height=0): - super().__init__() - # 支持 Canvas(Bitmap) 重载 - if isinstance(width, Bitmap): - bitmap = width - self._bitmap = bitmap - self._width = bitmap.getWidth() - self._height = bitmap.getHeight() - if HAS_PIL: - from PIL import Image, ImageDraw - self._image = bitmap._image if hasattr(bitmap, '_image') and bitmap._image else None - self._draw = ImageDraw.Draw(self._image) if self._image else None - else: - self._pixels = None - self._save_count = 0 - self._matrix_stack = [Matrix()] - self._clip_stack = [Rect(0, 0, self._width, self._height)] - self._matrix = Matrix() - return - if HAS_PIL: - from PIL import Image, ImageDraw - self._width = width - self._height = height - if width > 0 and height > 0: - self._image = Image.new("RGBA", (width, height), (0, 0, 0, 0)) - self._draw = ImageDraw.Draw(self._image) - else: - self._image = None - self._draw = None - else: - self._width = width - self._height = height - self._pixels = None - self._bitmap = None - self._save_count = 0 - self._matrix_stack = [Matrix()] - self._clip_stack = [Rect(0, 0, width, height)] - self._matrix = Matrix() - - def setBitmap(self, bitmap: Bitmap): - self._bitmap = bitmap - self._width = bitmap.getWidth() - self._height = bitmap.getHeight() - if HAS_PIL: - from PIL import ImageDraw - if hasattr(bitmap, "_image") and bitmap._image: - self._image = bitmap._image - self._draw = ImageDraw.Draw(self._image) - - def getBitmap(self) -> Optional[Bitmap]: - return self._bitmap - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def save(self) -> int: - self._save_count += 1 - self._matrix_stack.append(Matrix(self._matrix)) - self._clip_stack.append(Rect( - self._clip_stack[-1].left, self._clip_stack[-1].top, - self._clip_stack[-1].right, self._clip_stack[-1].bottom - )) - return self._save_count - - def restore(self): - if len(self._matrix_stack) > 1: - self._matrix = self._matrix_stack.pop() - self._clip_stack.pop() - self._save_count -= 1 - - def saveLayer(self, bounds: RectF, paint: Paint = None, saveFlags: int = ALL_SAVE_FLAG) -> int: - return self.save() - - def getSaveCount(self) -> int: - return self._save_count - - def restoreToCount(self, saveCount: int): - while self._save_count > saveCount: - self.restore() - - def translate(self, dx: float, dy: float): - self._matrix.postTranslate(dx, dy) - - def scale(self, sx: float, sy: float, px: float = 0.0, py: float = 0.0): - self._matrix.postScale(sx, sy, px, py) - - def rotate(self, degrees: float, px: float = 0.0, py: float = 0.0): - self._matrix.postRotate(degrees, px, py) - - def skew(self, sx: float, sy: float): - self._matrix.postSkew(sx, sy) - - def concat(self, matrix: Matrix): - self._matrix.postConcat(matrix) - - def setMatrix(self, matrix: Matrix): - self._matrix = Matrix(matrix) - - def getMatrix(self, ctm: Matrix = None) -> Matrix: - if ctm: - ctm._values = list(self._matrix._values) - return ctm - return Matrix(self._matrix) - - def getLocalTransform(self, transform: Matrix = None) -> Matrix: - return self.getMatrix(transform) - - def getDeviceTransform(self, transform: Matrix = None) -> Matrix: - if transform: - transform.reset() - return transform - return Matrix() - - def clipRect(self, left: float, top: float, right: float, bottom: float, - op: int = ClipOp.INTERSECT): - r = self._clip_stack[-1] - if op == ClipOp.INTERSECT: - self._clip_stack[-1] = Rect( - max(r.left, int(left)), max(r.top, int(top)), - min(r.right, int(right)), min(r.bottom, int(bottom)) - ) - elif op == ClipOp.DIFFERENCE: - pass - - def clipPath(self, path: Path, op: int = ClipOp.INTERSECT): - bounds = path.computeBounds() - self.clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, op) - - def clipRegion(self, region: Region, op: int = ClipOp.INTERSECT): - r = region._rect - self.clipRect(float(r.left), float(r.top), float(r.right), float(r.bottom), op) - - def getClipBounds(self, bounds: Rect = None) -> Rect: - r = self._clip_stack[-1] - if bounds: - bounds.set(r.left, r.top, r.right, r.bottom) - return bounds - return Rect(r.left, r.top, r.right, r.bottom) - - def quickReject(self, *args) -> bool: - """判断给定矩形是否完全在裁剪区域之外。""" - clip = self._clip_stack[-1] - if len(args) == 1: - r = args[0] - if isinstance(r, Rect): - left, top, right, bottom = r.left, r.top, r.right, r.bottom - elif isinstance(r, RectF): - left, top, right, bottom = r.left, r.top, r.right, r.bottom - else: - return False - elif len(args) == 4: - left, top, right, bottom = args - else: - return False - return (right <= clip.left or left >= clip.right or - bottom <= clip.top or top >= clip.bottom) - - def drawColor(self, color: int): - if HAS_PIL and self._draw is not None: - r, g, b = Color.red(color), Color.green(color), Color.blue(color) - a = Color.alpha(color) - self._draw.rectangle([0, 0, self._width, self._height], - fill=(r, g, b, a)) - - def drawARGB(self, a: int, r: int, g: int, b: int): - self.drawColor(Color.argb(a, r, g, b)) - - def drawRGB(self, r: int, g: int, b: int): - self.drawARGB(255, r, g, b) - - def drawPaint(self, paint: Paint): - self.drawColor(paint.getColor()) - - def drawPoint(self, x: float, y: float, paint: Paint): - if HAS_PIL and self._draw is not None: - c = paint.getColor() - self._draw.point([(x, y)], fill=( - Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c) - )) - - def drawPoints(self, pts: List[float], offset: int = 0, count: int = -1, paint: Paint = None): - if paint is None: - paint = Paint() - if count < 0: - count = len(pts) - offset - for i in range(offset, offset + count, 2): - self.drawPoint(pts[i], pts[i + 1], paint) - - def drawLine(self, startX: float, startY: float, stopX: float, stopY: float, paint: Paint): - if HAS_PIL and self._draw is not None: - c = paint.getColor() - w = paint.getStrokeWidth() if paint.getStyle() != Paint.Style.FILL else 1 - self._draw.line([(startX, startY), (stopX, stopY)], - fill=(Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)), - width=max(1, int(w))) - - def drawLines(self, pts: List[float], offset: int = 0, count: int = -1, paint: Paint = None): - if paint is None: - paint = Paint() - if count < 0: - count = len(pts) - offset - for i in range(offset, offset + count, 4) if count >= 4 else []: - self.drawLine(pts[i], pts[i + 1], pts[i + 2], pts[i + 3], paint) - - def drawRect(self, rect: Union[Rect, RectF, tuple], paint: Paint): - if isinstance(rect, Rect): - l, t, r, b = rect.left, rect.top, rect.right, rect.bottom - elif isinstance(rect, RectF): - l, t, r, b = rect.left, rect.top, rect.right, rect.bottom - else: - l, t, r, b = rect - if HAS_PIL and self._draw is not None: - c = paint.getColor() - fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) - if paint.getStyle() == Paint.Style.FILL: - self._draw.rectangle([l, t, r, b], fill=fill_color) - elif paint.getStyle() == Paint.Style.STROKE: - w = max(1, int(paint.getStrokeWidth())) - self._draw.rectangle([l, t, r, b], outline=fill_color, width=w) - else: - self._draw.rectangle([l, t, r, b], fill=fill_color) - - def drawRoundRect(self, rect: Union[RectF, tuple], rx: float, ry: float, paint: Paint): - if isinstance(rect, RectF): - l, t, r, b = rect.left, rect.top, rect.right, rect.bottom - else: - l, t, r, b = rect - if HAS_PIL and self._draw is not None: - c = paint.getColor() - fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) - self._draw.rounded_rectangle( - [l, t, r, b], radius=rx, - fill=fill_color if paint.getStyle() != Paint.Style.STROKE else None, - outline=fill_color if paint.getStyle() != Paint.Style.FILL else None, - width=max(1, int(paint.getStrokeWidth())) if paint.getStyle() != Paint.Style.FILL else 1 - ) - - def drawCircle(self, cx: float, cy: float, radius: float, paint: Paint): - if HAS_PIL and self._draw is not None: - c = paint.getColor() - fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) - bbox = [cx - radius, cy - radius, cx + radius, cy + radius] - if paint.getStyle() == Paint.Style.FILL: - self._draw.ellipse(bbox, fill=fill_color) - elif paint.getStyle() == Paint.Style.STROKE: - w = max(1, int(paint.getStrokeWidth())) - self._draw.ellipse(bbox, outline=fill_color, width=w) - else: - self._draw.ellipse(bbox, fill=fill_color) - - def drawOval(self, rect: Union[RectF, tuple], paint: Paint): - if isinstance(rect, RectF): - l, t, r, b = rect.left, rect.top, rect.right, rect.bottom - else: - l, t, r, b = rect - if HAS_PIL and self._draw is not None: - c = paint.getColor() - fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) - self._draw.ellipse([l, t, r, b], fill=fill_color) - - def drawArc(self, rect: Union[RectF, tuple], startAngle: float, sweepAngle: float, - useCenter: bool, paint: Paint): - if isinstance(rect, RectF): - l, t, r, b = rect.left, rect.top, rect.right, rect.bottom - else: - l, t, r, b = rect - if HAS_PIL and self._draw is not None: - c = paint.getColor() - fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) - if not HAS_PIL: - return - from PIL import ImageDraw - self._draw.pieslice( - [l, t, r, b], start=startAngle, end=startAngle + sweepAngle, - fill=fill_color if useCenter or paint.getStyle() != Paint.Style.STROKE else None, - outline=fill_color if paint.getStyle() != Paint.Style.FILL else None, - width=max(1, int(paint.getStrokeWidth())) if paint.getStyle() != Paint.Style.FILL else 1 - ) - - def drawPath(self, path: Path, paint: Paint): - if HAS_PIL and self._draw is not None: - pil_path = [] - for seg in path._segments: - if seg["type"] == "move": - pil_path.append(("M", seg["x"], seg["y"])) - elif seg["type"] == "line": - pil_path.append(("L", seg["x"], seg["y"])) - elif seg["type"] == "close": - pil_path.append(("Z",)) - elif seg["type"] in ("quad", "cubic"): - pass - if pil_path: - from PIL import ImageDraw - c = paint.getColor() - fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) - try: - if paint.getStyle() == Paint.Style.FILL: - self._draw.polygon( - [(s[1], s[2]) for s in pil_path if s[0] in ("M", "L")], - fill=fill_color - ) - else: - w = max(1, int(paint.getStrokeWidth())) - if len(pil_path) > 1: - pts = [(s[1], s[2]) for s in pil_path if s[0] in ("M", "L")] - for i in range(len(pts) - 1): - self._draw.line([pts[i], pts[i + 1]], fill=fill_color, width=w) - except Exception: - pass - - def drawBitmap(self, *args): - """drawBitmap 重载: (bitmap,left,top,paint) / (bitmap,src,dst,paint) / (bitmap,matrix,paint)""" - if len(args) == 4: - bitmap, a, b, paint = args - if isinstance(a, (int, float)) and isinstance(b, (int, float)): - if HAS_PIL and self._draw is not None and hasattr(bitmap, "_image") and bitmap._image: - self._image.paste(bitmap._image, (int(a), int(b))) - elif HAS_PIL and self._image is not None: - pass - else: - src, dst = a, b - if HAS_PIL and self._image is not None and hasattr(bitmap, "_image") and bitmap._image: - if src: - cropped = bitmap._image.crop((src.left, src.top, src.right, src.bottom)) - resized = cropped.resize((int(dst.width()), int(dst.height()))) - self._image.paste(resized, (int(dst.left), int(dst.top))) - else: - resized = bitmap._image.resize((int(dst.width()), int(dst.height()))) - self._image.paste(resized, (int(dst.left), int(dst.top))) - elif len(args) == 3: - bitmap, matrix, paint = args - if HAS_PIL and self._image is not None and hasattr(bitmap, "_image") and bitmap._image: - try: - from PIL import Image - inv = Matrix() - if hasattr(matrix, "invert") and matrix.invert(inv): - v = inv.getValues() - elif hasattr(matrix, "getValues"): - v = matrix.getValues() - else: - v = [1, 0, 0, 0, 1, 0, 0, 0, 1] - transformed = bitmap._image.transform( - bitmap._image.size, Image.AFFINE, - (v[0], v[1], v[2], v[3], v[4], v[5])) - self._image.paste(transformed, (0, 0)) - except Exception: - pass - - def drawText(self, text: str, x: float, y: float, paint: Paint): - if HAS_PIL and self._draw is not None: - c = paint.getColor() - fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) - self._draw.text((x, y), text, fill=fill_color) - - def drawTextOnPath(self, text: str, path: Path, hOffset: float, vOffset: float, paint: Paint): - """沿路径绘制文本。使用 PathMeasure 逐字符放置。""" - if not text or not HAS_PIL or self._draw is None: - return - pm = PathMeasure(path, False) - total = pm.getLength() - if total <= 0: - return - c = paint.getColor() - fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) - dist = hOffset - pos = [0.0, 0.0] - tan = [0.0, 0.0] - for ch in text: - cw = paint.measureText(ch) - if dist > total: - break - if pm.getPosTan(dist, pos, tan): - angle = math.degrees(math.atan2(tan[1], tan[0])) - # 使用 PIL 旋转绘制每个字符 - try: - from PIL import Image as _PILImage - char_img = _PILImage.new("RGBA", (int(cw) + 4, int(paint._text_size) + 4), (0, 0, 0, 0)) - from PIL import ImageDraw as _PILDraw - cd = _PILDraw.Draw(char_img) - cd.text((0, 0), ch, fill=fill_color) - rotated = char_img.rotate(-angle, expand=True, resample=_PILImage.BICUBIC) - self._image.paste(rotated, (int(pos[0] + vOffset * tan[1]), int(pos[1] - vOffset * tan[0])), - rotated) - except Exception: - self._draw.text((pos[0], pos[1]), ch, fill=fill_color) - dist += cw - - def drawPosText(self, text: str, pos: List[float], paint: Paint): - if HAS_PIL and self._draw is not None: - c = paint.getColor() - fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) - for i, ch in enumerate(text): - if i * 2 + 1 < len(pos): - self._draw.text((pos[i * 2], pos[i * 2 + 1]), ch, fill=fill_color) - - def drawPicture(self, picture: Picture, dst: Union[Rect, RectF] = None): - """回放 Picture 中录制的绘制命令。""" - if picture is None: - return - recorded = getattr(picture, "_recorded", []) - if not recorded: - return - for cmd in recorded: - method = cmd.get("method") - args = cmd.get("args", []) - if method and hasattr(self, method): - try: - getattr(self, method)(*args) - except Exception: - pass - - def drawPatch(self, patch: NinePatch, dst: Rect, paint: Paint = None): - """绘制 NinePatch(拉伸位图)到目标矩形。""" - if patch is None or patch._bitmap is None: - return - bitmap = patch._bitmap - if dst is None: - self.drawBitmap(bitmap, 0, 0, paint) - return - # 简单缩放绘制 - if isinstance(dst, (Rect, RectF)): - dw = dst.right - dst.left - dh = dst.bottom - dst.top - else: - dw, dh = dst[2] - dst[0], dst[3] - dst[1] - scaled = Bitmap.createScaledBitmap(bitmap, int(dw), int(dh), True) - self.drawBitmap(scaled, dst.left if hasattr(dst, "left") else dst[0], - dst.top if hasattr(dst, "top") else dst[1], paint) - - def drawDoubleRoundRect(self, outer: RectF, outerRx: float, outerRy: float, - inner: RectF, innerRx: float, innerRy: float, paint: Paint): - """绘制双层圆角矩形(外层和内层)。""" - self.drawRoundRect(outer, outerRx, outerRy, paint) - self.drawRoundRect(inner, innerRx, innerRy, paint) - - def drawBitmapMesh(self, bitmap: Bitmap, meshWidth: int, meshHeight: int, - verts: List[float], vertOffset: int, colors: List[int], - colorOffset: int, paint: Paint = None): - pass - - def drawVertices(self, mode: int, vertexCount: int, verts: List[float], - vertOffset: int, texs: List[float], texOffset: int, - colors: List[int], colorOffset: int, indices: List[int], - indexOffset: int, indexCount: int, paint: Paint): - pass - - def isHardwareAccelerated(self) -> bool: - return False - - def isOpaque(self) -> bool: - return False - - def setDensity(self, density: int): - """设置画布密度(DPI)。""" - self._density = density - - def getDensity(self) -> int: - """返回画布密度(DPI),默认 0 表示未知。""" - return getattr(self, "_density", 0) - - def toString(self) -> str: - return f"Canvas({self._width}x{self._height})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - # Core - "Bitmap", "BitmapFactory", "Canvas", "Paint", "Color", "ColorFilter", - "PorterDuffColorFilter", "LightingColorFilter", "ComposeColorFilter", - "ColorMatrix", "ColorMatrixColorFilter", "Path", "PathMeasure", - "Rect", "RectF", "Point", "PointF", "Typeface", "Matrix", - - # Shader hierarchy - "Shader", "LinearGradient", "RadialGradient", "SweepGradient", - "BitmapShader", "ComposeShader", - - # MaskFilter hierarchy - "MaskFilter", "BlurMaskFilter", "EmbossMaskFilter", - - # PorterDuff - "PorterDuff", "PorterDuffXfermode", "Xfermode", - "AvoidXfermode", "PixelXorXfermode", - - # DrawFilter - "DrawFilter", "PaintFlagsDrawFilter", - - # PathEffect hierarchy - "PathEffect", "SumPathEffect", "CornerPathEffect", "DashPathEffect", - "DiscretePathEffect", "PathDashPathEffect", "ComposePathEffect", - - # Misc - "Camera", "SurfaceTexture", "Region", "RegionIterator", - "NinePatch", "Picture", "PictureDrawable", - "Rasterizer", "LayerRasterizer", - - # BlendMode - "BlendMode", "BlendModeColorFilter", - "ClipOp", -] \ No newline at end of file diff --git a/android_pyapi/android/graphics_drawable.py b/android_pyapi/android/graphics_drawable.py deleted file mode 100644 index ea929de..0000000 --- a/android_pyapi/android/graphics_drawable.py +++ /dev/null @@ -1,1128 +0,0 @@ -""" -Android Graphics Drawable 组件 — Drawable, BitmapDrawable, ColorDrawable 等 -方法签名完全对齐 Android SDK -""" - -import math -from typing import Any, Dict, List, Optional, Sequence, Tuple, Union - -from .._core import Stub, AndroidOnlyError, android_context, HAS_PIL, resolve_android_path -from .graphics import ( - Bitmap, Canvas, Color, ColorFilter, Paint, PorterDuff, PorterDuffColorFilter, - Rect, RectF, Matrix, Path, Shader, Rect, - BlendMode, BlendModeColorFilter, -) - - -# ═══════════════════════════════════════════════════════════════ -# ColorStateList -# ═══════════════════════════════════════════════════════════════ - -class ColorStateList(Stub): - """ColorStateList — 颜色状态列表""" - - def __init__(self, states: List[List[int]], colors: List[int]): - super().__init__() - self._states = list(states) - self._colors = list(colors) - self._default_color = colors[-1] if colors else 0 - - def getColorForState(self, stateSet: List[int], defaultColor: int = 0) -> int: - return self._default_color - - def getDefaultColor(self) -> int: - return self._default_color - - def isStateful(self) -> bool: - return len(self._states) > 0 - - def withAlpha(self, alpha: int) -> "ColorStateList": - new_colors = [] - for c in self._colors: - a = Color.alpha(c) - new_a = int(a * alpha / 255) - new_colors.append(Color.argb(new_a, Color.red(c), Color.green(c), Color.blue(c))) - return ColorStateList(self._states, new_colors) - - @staticmethod - def valueOf(color: int) -> "ColorStateList": - return ColorStateList([[]], [color]) - - def toString(self) -> str: - return f"ColorStateList({len(self._states)} states)" - - -# ═══════════════════════════════════════════════════════════════ -# Drawable -# ═══════════════════════════════════════════════════════════════ - -class Drawable(Stub): - """Drawable — 可绘制对象的抽象基类""" - - def __init__(self): - super().__init__() - self._bounds = Rect(0, 0, 0, 0) - self._alpha = 255 - self._color_filter = None - self._callback = None - self._visible = True - self._level = 0 - self._intrinsic_width = -1 - self._intrinsic_height = -1 - - def setBounds(self, left: int, top: int, right: int, bottom: int): - self._bounds.set(left, top, right, bottom) - - def setBounds(self, bounds: Rect): - self._bounds.set(bounds.left, bounds.top, bounds.right, bounds.bottom) - - def getBounds(self) -> Rect: - return self._bounds - - def copyBounds(self) -> Rect: - return Rect(self._bounds.left, self._bounds.top, - self._bounds.right, self._bounds.bottom) - - def copyBounds(self, out: Rect) -> Rect: - out.set(self._bounds.left, self._bounds.top, - self._bounds.right, self._bounds.bottom) - return out - - def getIntrinsicWidth(self) -> int: - return self._intrinsic_width - - def getIntrinsicHeight(self) -> int: - return self._intrinsic_height - - def setAlpha(self, alpha: int): - self._alpha = alpha - - def getAlpha(self) -> int: - return self._alpha - - def setColorFilter(self, colorFilter: Optional[ColorFilter]): - self._color_filter = colorFilter - - def getColorFilter(self) -> Optional[ColorFilter]: - return self._color_filter - - def getOpacity(self) -> int: - return 0 # PixelFormat.UNKNOWN - - def invalidateSelf(self): - if self._callback: - self._callback.invalidateDrawable(self) - - def setCallback(self, cb): - self._callback = cb - - def getCallback(self): - return self._callback - - def scheduleDrawable(self, what, who, when: int): - if self._callback: - self._callback.scheduleDrawable(what, who, when) - - def unscheduleDrawable(self, what, who): - if self._callback: - self._callback.unscheduleDrawable(what, who) - - def draw(self, canvas: Canvas): - pass - - def setVisible(self, visible: bool, restart: bool = False) -> bool: - old = self._visible - self._visible = visible - return old - - def isVisible(self) -> bool: - return self._visible - - def setLevel(self, level: int) -> bool: - self._level = level - return True - - def getLevel(self) -> int: - return self._level - - def getConstantState(self) -> Optional["DrawableConstantState"]: - return None - - def getCurrent(self) -> "Drawable": - return self - - def mutate(self) -> "Drawable": - return self - - def getMinimumWidth(self) -> int: - return 0 - - def getMinimumHeight(self) -> int: - return 0 - - def setDither(self, dither: bool): - pass - - def setFilterBitmap(self, filter: bool): - pass - - def setHotspot(self, x: float, y: float): - pass - - def setHotspotBounds(self, left: int, top: int, right: int, bottom: int): - pass - - def getHotspotBounds(self, outRect: Rect): - outRect.set(0, 0, 0, 0) - - def isStateful(self) -> bool: - return False - - def setState(self, stateSet: List[int]) -> bool: - return False - - def getState(self) -> List[int]: - return [] - - def jumpToCurrentState(self): - pass - - def clearColorFilter(self): - self._color_filter = None - - def getTransparentRegion(self): - from .graphics import Region - return Region(self._bounds) - - def onBoundsChange(self, bounds: Rect): - pass - - def onLevelChange(self, level: int) -> bool: - return False - - def onStateChange(self, stateSet: List[int]) -> bool: - return False - - def toString(self) -> str: - return f"{self.__class__.__name__}({self._bounds})" - - -class DrawableConstantState(Stub): - """DrawableConstantState — Drawable 常量状态""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# BitmapDrawable -# ═══════════════════════════════════════════════════════════════ - -class BitmapDrawable(Drawable): - """BitmapDrawable — 位图 Drawable""" - - def __init__(self, res=None, bitmap: Bitmap = None): - super().__init__() - self._bitmap = bitmap if bitmap else Bitmap(0, 0, Bitmap.Config.ARGB_8888) - self._paint = Paint() - self._target_density = 160 - self._gravity = 0x33 # Gravity.FILL - self._tile_mode_x = None - self._tile_mode_y = None - self._mutated = False - if self._bitmap: - self._intrinsic_width = self._bitmap.getWidth() - self._intrinsic_height = self._bitmap.getHeight() - - def getBitmap(self) -> Bitmap: - return self._bitmap - - def getPaint(self) -> Paint: - return self._paint - - def setAntiAlias(self, aa: bool): - self._paint.setAntiAlias(aa) - - def setFilterBitmap(self, filter: bool): - self._paint.setFilterBitmap(filter) - - def setDither(self, dither: bool): - self._paint.setDither(dither) - - def setTileModeX(self, mode): - self._tile_mode_x = mode - - def setTileModeY(self, mode): - self._tile_mode_y = mode - - def getTileModeX(self): - return self._tile_mode_x - - def getTileModeY(self): - return self._tile_mode_y - - def setGravity(self, gravity: int): - self._gravity = gravity - - def getGravity(self) -> int: - return self._gravity - - def setTargetDensity(self, density: int): - self._target_density = density - - def getTargetDensity(self) -> int: - return self._target_density - - def draw(self, canvas: Canvas): - if self._bitmap: - bounds = self._bounds - if HAS_PIL and hasattr(canvas, "_image") and hasattr(self._bitmap, "_image"): - bm_img = self._bitmap._image - if bm_img: - resized = bm_img.resize((bounds.width(), bounds.height())) - canvas._image.paste(resized, (bounds.left, bounds.top)) - - def getIntrinsicWidth(self) -> int: - return self._bitmap.getWidth() if self._bitmap else -1 - - def getIntrinsicHeight(self) -> int: - return self._bitmap.getHeight() if self._bitmap else -1 - - def getOpacity(self) -> int: - if self._bitmap: - return 1 if self._bitmap.hasAlpha() else -1 - return 0 - - def mutate(self) -> "BitmapDrawable": - if not self._mutated: - self._bitmap = Bitmap.createBitmap(self._bitmap) - self._paint = Paint() - self._mutated = True - return self - - -# ═══════════════════════════════════════════════════════════════ -# ColorDrawable -# ═══════════════════════════════════════════════════════════════ - -class ColorDrawable(Drawable): - """ColorDrawable — 纯色 Drawable""" - - def __init__(self, color: int = 0): - super().__init__() - self._color = color - self._paint = Paint() - self._paint.setColor(color) - - def getColor(self) -> int: - return self._color - - def setColor(self, color: int): - self._color = color - self._paint.setColor(color) - - def setAlpha(self, alpha: int): - self._paint.setAlpha(alpha) - - def getAlpha(self) -> int: - return self._paint.getAlpha() - - def draw(self, canvas: Canvas): - canvas.drawRect(self._bounds, self._paint) - - def getOpacity(self) -> int: - if Color.alpha(self._color) == 255: - return -1 # OPAQUE - return -3 # TRANSLUCENT - - def mutate(self) -> "ColorDrawable": - self._paint = Paint() - self._paint.setColor(self._color) - return self - - -# ═══════════════════════════════════════════════════════════════ -# GradientDrawable -# ═══════════════════════════════════════════════════════════════ - -class GradientDrawable(Drawable): - """GradientDrawable — 渐变 Drawable""" - - RECTANGLE = 0 - OVAL = 1 - LINE = 2 - RING = 3 - - class Orientation: - TOP_BOTTOM = 0 - TR_BL = 1 - RIGHT_LEFT = 2 - BR_TL = 3 - BOTTOM_TOP = 4 - BL_TR = 5 - LEFT_RIGHT = 6 - TL_BR = 7 - - def __init__(self, orientation: int = Orientation.TOP_BOTTOM, colors: List[int] = None): - super().__init__() - self._orientation = orientation - self._colors = colors if colors else [Color.BLACK, Color.WHITE] - self._shape = GradientDrawable.RECTANGLE - self._corner_radius = 0.0 - self._corner_radii = None - self._stroke_width = 0 - self._stroke_color = 0 - self._gradient_type = 0 # LINEAR_GRADIENT - self._gradient_center_x = 0.5 - self._gradient_center_y = 0.5 - self._use_level = False - self._width = -1 - self._height = -1 - self._color = None - - def setShape(self, shape: int): - self._shape = shape - - def getShape(self) -> int: - return self._shape - - def setSize(self, width: int, height: int): - self._width = width - self._height = height - - def setStroke(self, width: int, color: int, dashWidth: float = 0, dashGap: float = 0): - self._stroke_width = width - self._stroke_color = color - - def setCornerRadius(self, radius: float): - self._corner_radius = radius - self._corner_radii = None - - def setCornerRadii(self, radii: List[float]): - self._corner_radii = radii - self._corner_radius = radii[0] if radii else 0 - - def setColors(self, colors: List[int]): - self._colors = list(colors) - self._color = None - - def setColor(self, color: int): - self._color = color - self._colors = None - - def setGradientType(self, gradientType: int): - self._gradient_type = gradientType - - def setGradientCenter(self, x: float, y: float): - self._gradient_center_x = x - self._gradient_center_y = y - - def setOrientation(self, orientation: int): - self._orientation = orientation - - def setUseLevel(self, useLevel: bool): - self._use_level = useLevel - - def getIntrinsicWidth(self) -> int: - return self._width if self._width > 0 else super().getIntrinsicWidth() - - def getIntrinsicHeight(self) -> int: - return self._height if self._height > 0 else super().getIntrinsicHeight() - - def draw(self, canvas: Canvas): - bounds = self._bounds - if HAS_PIL and hasattr(canvas, "_draw") and canvas._draw: - if self._color is not None: - c = self._color - fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) - elif self._colors: - c = self._colors[0] - fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) - else: - fill_color = (0, 0, 0, 255) - - if self._shape == GradientDrawable.OVAL: - canvas._draw.ellipse([bounds.left, bounds.top, bounds.right, bounds.bottom], - fill=fill_color) - elif self._shape == GradientDrawable.RECTANGLE and self._corner_radius > 0: - canvas._draw.rounded_rectangle( - [bounds.left, bounds.top, bounds.right, bounds.bottom], - radius=self._corner_radius, fill=fill_color) - else: - canvas._draw.rectangle([bounds.left, bounds.top, bounds.right, bounds.bottom], - fill=fill_color) - - -# ═══════════════════════════════════════════════════════════════ -# LayerDrawable -# ═══════════════════════════════════════════════════════════════ - -class LayerDrawable(Drawable): - """LayerDrawable — 图层 Drawable""" - - def __init__(self, layers: List[Drawable]): - super().__init__() - self._layers = list(layers) - self._ids = list(range(len(layers))) - self._layer_insets = [Rect(0, 0, 0, 0) for _ in layers] - - def findDrawableByLayerId(self, id: int) -> Optional[Drawable]: - for i, lid in enumerate(self._ids): - if lid == id: - return self._layers[i] - return None - - def setId(self, index: int, id: int): - if 0 <= index < len(self._ids): - self._ids[index] = id - - def getNumberOfLayers(self) -> int: - return len(self._layers) - - def getDrawable(self, index: int) -> Optional[Drawable]: - if 0 <= index < len(self._layers): - return self._layers[index] - return None - - def setDrawableByLayerId(self, id: int, drawable: Drawable) -> bool: - for i, lid in enumerate(self._ids): - if lid == id: - self._layers[i] = drawable - return True - return False - - def setLayerInset(self, index: int, l: int, t: int, r: int, b: int): - if 0 <= index < len(self._layer_insets): - self._layer_insets[index].set(l, t, r, b) - - def setLayerInsetLeft(self, index: int, left: int): - if 0 <= index < len(self._layer_insets): - self._layer_insets[index].left = left - - def setLayerInsetTop(self, index: int, top: int): - if 0 <= index < len(self._layer_insets): - self._layer_insets[index].top = top - - def setLayerInsetRight(self, index: int, right: int): - if 0 <= index < len(self._layer_insets): - self._layer_insets[index].right = right - - def setLayerInsetBottom(self, index: int, bottom: int): - if 0 <= index < len(self._layer_insets): - self._layer_insets[index].bottom = bottom - - def draw(self, canvas: Canvas): - for i, layer in enumerate(self._layers): - ins = self._layer_insets[i] if i < len(self._layer_insets) else Rect(0, 0, 0, 0) - b = self._bounds - layer.setBounds(b.left + ins.left, b.top + ins.top, - b.right - ins.right, b.bottom - ins.bottom) - layer.draw(canvas) - - def mutate(self) -> "LayerDrawable": - return self - - def getOpacity(self) -> int: - return 0 - - def isStateful(self) -> bool: - return any(d.isStateful() for d in self._layers) - - def setVisible(self, visible: bool, restart: bool = False) -> bool: - old = super().setVisible(visible, restart) - for d in self._layers: - d.setVisible(visible, restart) - return old - - -# ═══════════════════════════════════════════════════════════════ -# StateListDrawable -# ═══════════════════════════════════════════════════════════════ - -class StateListDrawable(Drawable): - """StateListDrawable — 状态列表 Drawable""" - - def __init__(self): - super().__init__() - self._state_sets: List[List[int]] = [] - self._drawables: List[Drawable] = [] - self._current_index = -1 - - def addState(self, stateSet: List[int], drawable: Drawable): - self._state_sets.append(stateSet) - self._drawables.append(drawable) - - def selectDrawable(self, index: int) -> bool: - if 0 <= index < len(self._drawables): - self._current_index = index - return True - return False - - def getCurrent(self) -> Drawable: - if 0 <= self._current_index < len(self._drawables): - return self._drawables[self._current_index] - if self._drawables: - return self._drawables[0] - return self - - def draw(self, canvas: Canvas): - d = self.getCurrent() - d.setBounds(self._bounds) - d.draw(canvas) - - def isStateful(self) -> bool: - return True - - def setState(self, stateSet: List[int]) -> bool: - for i, ss in enumerate(self._state_sets): - if not ss or all(s in stateSet for s in ss): - self._current_index = i - return True - return False - - def mutate(self) -> "StateListDrawable": - self._drawables = [d.mutate() for d in self._drawables] - return self - - -# ═══════════════════════════════════════════════════════════════ -# LevelListDrawable -# ═══════════════════════════════════════════════════════════════ - -class LevelListDrawable(Drawable): - """LevelListDrawable — 级别列表 Drawable""" - - def __init__(self): - super().__init__() - self._levels: List[Tuple[int, int, Drawable]] = [] - self._current_index = -1 - - def addLevel(self, low: int, high: int, drawable: Drawable): - self._levels.append((low, high, drawable)) - - def onLevelChange(self, level: int) -> bool: - for i, (low, high, d) in enumerate(self._levels): - if low <= level <= high: - self._current_index = i - return True - return False - - def getCurrent(self) -> Drawable: - if 0 <= self._current_index < len(self._levels): - return self._levels[self._current_index][2] - if self._levels: - return self._levels[0][2] - return self - - def draw(self, canvas: Canvas): - d = self.getCurrent() - d.setBounds(self._bounds) - d.draw(canvas) - - -# ═══════════════════════════════════════════════════════════════ -# TransitionDrawable -# ═══════════════════════════════════════════════════════════════ - -class TransitionDrawable(Drawable): - """TransitionDrawable — 过渡 Drawable""" - - def __init__(self, layers: List[Drawable]): - super().__init__() - self._layers = list(layers) - self._transition_progress = 0.0 - self._reverse = False - - def startTransition(self, durationMillis: int): - self._transition_progress = 0.0 - self._reverse = False - - def resetTransition(self): - self._transition_progress = 0.0 - self._reverse = False - - def reverse(self, durationMillis: int): - self._transition_progress = 1.0 - self._reverse = True - - def startReverse(self, durationMillis: int = 0): - self._transition_progress = 1.0 - self._reverse = True - - def draw(self, canvas: Canvas): - for layer in self._layers: - layer.setBounds(self._bounds) - layer.draw(canvas) - - def getNumberOfLayers(self) -> int: - return len(self._layers) - - def getDrawable(self, index: int) -> Optional[Drawable]: - if 0 <= index < len(self._layers): - return self._layers[index] - return None - - def isCrossFadeEnabled(self) -> bool: - return True - - def setCrossFadeEnabled(self, enabled: bool): - pass - - -# ═══════════════════════════════════════════════════════════════ -# RotateDrawable -# ═══════════════════════════════════════════════════════════════ - -class RotateDrawable(Drawable): - """RotateDrawable — 旋转 Drawable""" - - def __init__(self): - super().__init__() - self._drawable = None - self._from_degrees = 0.0 - self._to_degrees = 360.0 - self._pivot_x = 0.5 - self._pivot_y = 0.5 - self._current_degrees = 0.0 - - def setDrawable(self, drawable: Drawable): - self._drawable = drawable - - def getDrawable(self) -> Optional[Drawable]: - return self._drawable - - def setFromDegrees(self, fromDegrees: float): - self._from_degrees = fromDegrees - - def setToDegrees(self, toDegrees: float): - self._to_degrees = toDegrees - - def setPivotX(self, pivotX: float): - self._pivot_x = pivotX - - def setPivotY(self, pivotY: float): - self._pivot_y = pivotY - - def draw(self, canvas: Canvas): - if self._drawable: - self._drawable.setBounds(self._bounds) - self._drawable.draw(canvas) - - -# ═══════════════════════════════════════════════════════════════ -# ScaleDrawable -# ═══════════════════════════════════════════════════════════════ - -class ScaleDrawable(Drawable): - """ScaleDrawable — 缩放 Drawable""" - - def __init__(self, drawable: Drawable = None, gravity: int = 0x33, scaleWidth: float = 0.0, - scaleHeight: float = 0.0): - super().__init__() - self._drawable = drawable - self._gravity = gravity - self._scale_width = scaleWidth - self._scale_height = scaleHeight - self._level = 0 - - def getDrawable(self) -> Optional[Drawable]: - return self._drawable - - def setLevel(self, level: int) -> bool: - self._level = level - if self._drawable: - self._drawable.setLevel(level) - return True - - def draw(self, canvas: Canvas): - if self._drawable: - self._drawable.setBounds(self._bounds) - self._drawable.draw(canvas) - - -# ═══════════════════════════════════════════════════════════════ -# ClipDrawable -# ═══════════════════════════════════════════════════════════════ - -class ClipDrawable(Drawable): - """ClipDrawable — 裁剪 Drawable""" - - HORIZONTAL = 1 - VERTICAL = 2 - - def __init__(self, drawable: Drawable = None, gravity: int = 0x33, orientation: int = HORIZONTAL): - super().__init__() - self._drawable = drawable - self._gravity = gravity - self._orientation = orientation - self._level = 0 - - def setLevel(self, level: int) -> bool: - self._level = level - if self._drawable: - self._drawable.setLevel(level) - return True - - def draw(self, canvas: Canvas): - if self._drawable: - self._drawable.setBounds(self._bounds) - self._drawable.draw(canvas) - - -# ═══════════════════════════════════════════════════════════════ -# InsetDrawable -# ═══════════════════════════════════════════════════════════════ - -class InsetDrawable(Drawable): - """InsetDrawable — 内嵌 Drawable""" - - def __init__(self, drawable: Drawable, inset: float): - super().__init__() - self._drawable = drawable - self._inset = int(inset) - self._inset_left = int(inset) - self._inset_top = int(inset) - self._inset_right = int(inset) - self._inset_bottom = int(inset) - - def getDrawable(self) -> Optional[Drawable]: - return self._drawable - - def getInset(self) -> int: - return self._inset - - def draw(self, canvas: Canvas): - if self._drawable: - b = self._bounds - self._drawable.setBounds( - b.left + self._inset_left, b.top + self._inset_top, - b.right - self._inset_right, b.bottom - self._inset_bottom - ) - self._drawable.draw(canvas) - - -# ═══════════════════════════════════════════════════════════════ -# ShapeDrawable -# ═══════════════════════════════════════════════════════════════ - -class ShapeDrawable(Drawable): - """ShapeDrawable — 形状 Drawable""" - - def __init__(self, shape: "Shape" = None): - super().__init__() - self._shape = shape - self._paint = Paint() - self._intrinsic_width = -1 - self._intrinsic_height = -1 - - def getShape(self): - return self._shape - - def setShape(self, shape): - self._shape = shape - - def getPaint(self) -> Paint: - return self._paint - - def setIntrinsicWidth(self, width: int): - self._intrinsic_width = width - - def setIntrinsicHeight(self, height: int): - self._intrinsic_height = height - - def getIntrinsicWidth(self) -> int: - return self._intrinsic_width if self._intrinsic_width > 0 else super().getIntrinsicWidth() - - def getIntrinsicHeight(self) -> int: - return self._intrinsic_height if self._intrinsic_height > 0 else super().getIntrinsicHeight() - - def draw(self, canvas: Canvas): - if self._shape: - self._shape.draw(canvas, self._paint) - - -class Shape(Stub): - """Shape — 形状基类""" - def draw(self, canvas: Canvas, paint: Paint): - pass - - -class ArcShape(Shape): - """ArcShape — 弧形""" - def __init__(self, startAngle: float, sweepAngle: float): - super().__init__() - self._start_angle = startAngle - self._sweep_angle = sweepAngle - - -class OvalShape(Shape): - """OvalShape — 椭圆""" - def draw(self, canvas: Canvas, paint: Paint): - if HAS_PIL and hasattr(canvas, "_draw"): - c = paint.getColor() - fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) - b = canvas.getClipBounds() - canvas._draw.ellipse([b.left, b.top, b.right, b.bottom], fill=fill_color) - - -class RectShape(Shape): - """RectShape — 矩形""" - def draw(self, canvas: Canvas, paint: Paint): - if HAS_PIL and hasattr(canvas, "_draw"): - c = paint.getColor() - fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) - b = canvas.getClipBounds() - canvas._draw.rectangle([b.left, b.top, b.right, b.bottom], fill=fill_color) - - -class RoundRectShape(Shape): - """RoundRectShape — 圆角矩形""" - def __init__(self, outerRadii: List[float], inset: RectF, innerRadii: List[float]): - super().__init__() - self._outer_radii = outerRadii - self._inset = inset - self._inner_radii = innerRadii - - def draw(self, canvas: Canvas, paint: Paint): - if HAS_PIL and hasattr(canvas, "_draw"): - c = paint.getColor() - fill_color = (Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)) - b = canvas.getClipBounds() - radius = self._outer_radii[0] if self._outer_radii else 0 - canvas._draw.rounded_rectangle([b.left, b.top, b.right, b.bottom], - radius=radius, fill=fill_color) - - -class PaintDrawable(ShapeDrawable): - """PaintDrawable — 画笔 Drawable (ShapeDrawable 子类)""" - - def __init__(self, color: int = 0): - super().__init__(RectShape()) - self.getPaint().setColor(color) - - def setCornerRadius(self, radius: float): - self.setShape(RoundRectShape([radius, radius, radius, radius], None, None)) - - -# ═══════════════════════════════════════════════════════════════ -# VectorDrawable -# ═══════════════════════════════════════════════════════════════ - -class VectorDrawable(Drawable): - """VectorDrawable — 矢量 Drawable""" - - def __init__(self): - super().__init__() - self._alpha = 255 - - def setAlpha(self, alpha: int): - self._alpha = alpha - - def getAlpha(self) -> int: - return self._alpha - - def draw(self, canvas: Canvas): - pass - - @staticmethod - def createFromXml(res, parser) -> "VectorDrawable": - return VectorDrawable() - - -# ═══════════════════════════════════════════════════════════════ -# RippleDrawable -# ═══════════════════════════════════════════════════════════════ - -class RippleDrawable(Drawable): - """RippleDrawable — 水波纹 Drawable (API 21+)""" - - def __init__(self, colorStates: ColorStateList, content: Drawable = None, - mask: Drawable = None): - super().__init__() - self._color_states = colorStates - self._content = content - self._mask = mask - self._radius = -1 - - def setRadius(self, radius: int): - self._radius = radius - - def getRadius(self) -> int: - return self._radius - - def draw(self, canvas: Canvas): - if self._content: - self._content.setBounds(self._bounds) - self._content.draw(canvas) - - def setColor(self, color: ColorStateList): - self._color_states = color - - def getColor(self) -> Optional[ColorStateList]: - return self._color_states - - def mutate(self) -> "RippleDrawable": - return self - - -# ═══════════════════════════════════════════════════════════════ -# AnimatedVectorDrawable -# ═══════════════════════════════════════════════════════════════ - -class AnimatedVectorDrawable(Drawable): - """AnimatedVectorDrawable — 动画矢量 Drawable (API 21+)""" - - def __init__(self): - super().__init__() - self._running = False - - def start(self): - self._running = True - - def stop(self): - self._running = False - - def isRunning(self) -> bool: - return self._running - - def reset(self): - self._running = False - - def draw(self, canvas: Canvas): - pass - - @staticmethod - def createFromXml(res, parser) -> "AnimatedVectorDrawable": - return AnimatedVectorDrawable() - - -# ═══════════════════════════════════════════════════════════════ -# AnimationDrawable -# ═══════════════════════════════════════════════════════════════ - -class AnimationDrawable(Drawable): - """AnimationDrawable — 帧动画 Drawable""" - - def __init__(self): - super().__init__() - self._frames: List[Tuple[Drawable, int]] = [] - self._current_frame = 0 - self._oneshot = False - self._running = False - - def addFrame(self, frame: Drawable, duration: int): - self._frames.append((frame, duration)) - - def getFrame(self, index: int) -> Optional[Drawable]: - if 0 <= index < len(self._frames): - return self._frames[index][0] - return None - - def getNumberOfFrames(self) -> int: - return len(self._frames) - - def getDuration(self, index: int) -> int: - if 0 <= index < len(self._frames): - return self._frames[index][1] - return 0 - - def setOneShot(self, oneshot: bool): - self._oneshot = oneshot - - def isOneShot(self) -> bool: - return self._oneshot - - def start(self): - self._running = True - - def stop(self): - self._running = False - - def isRunning(self) -> bool: - return self._running - - def run(self): - pass - - def selectDrawable(self, index: int) -> bool: - if 0 <= index < len(self._frames): - self._current_frame = index - return True - return False - - def getCurrent(self) -> Drawable: - if self._frames: - return self._frames[self._current_frame][0] - return self - - def draw(self, canvas: Canvas): - d = self.getCurrent() - d.setBounds(self._bounds) - d.draw(canvas) - - def setVisible(self, visible: bool, restart: bool = False) -> bool: - old = super().setVisible(visible, restart) - if visible and restart: - self._current_frame = 0 - return old - - -# ═══════════════════════════════════════════════════════════════ -# DrawableContainer -# ═══════════════════════════════════════════════════════════════ - -class DrawableContainer(Drawable): - """DrawableContainer — Drawable 容器""" - - def __init__(self): - super().__init__() - self._drawables: List[Drawable] = [] - self._current_index = 0 - - def selectDrawable(self, index: int) -> bool: - if 0 <= index < len(self._drawables): - self._current_index = index - return True - return False - - def getCurrent(self) -> Drawable: - if self._drawables: - return self._drawables[self._current_index] - return self - - def draw(self, canvas: Canvas): - d = self.getCurrent() - d.setBounds(self._bounds) - d.draw(canvas) - - def setAlpha(self, alpha: int): - for d in self._drawables: - d.setAlpha(alpha) - - def setColorFilter(self, cf: Optional[ColorFilter]): - for d in self._drawables: - d.setColorFilter(cf) - - def getOpacity(self) -> int: - return 0 - - def isStateful(self) -> bool: - return any(d.isStateful() for d in self._drawables) - - def mutate(self) -> "DrawableContainer": - return self - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Drawable", "BitmapDrawable", "ColorDrawable", "GradientDrawable", - "LayerDrawable", "StateListDrawable", "LevelListDrawable", - "TransitionDrawable", "RotateDrawable", "ScaleDrawable", - "ClipDrawable", "InsetDrawable", "ShapeDrawable", "PaintDrawable", - "VectorDrawable", "RippleDrawable", "AnimatedVectorDrawable", - "AnimationDrawable", "DrawableContainer", "DrawableConstantState", - "ColorStateList", - # Shapes - "Shape", "ArcShape", "OvalShape", "RectShape", "RoundRectShape", -] \ No newline at end of file diff --git a/android_pyapi/android/graphics_drawable_ext.py b/android_pyapi/android/graphics_drawable_ext.py deleted file mode 100644 index adad698..0000000 --- a/android_pyapi/android/graphics_drawable_ext.py +++ /dev/null @@ -1,668 +0,0 @@ -""" -Android graphics.drawable 扩展 — ShapeDrawable, GradientDrawable, LayerDrawable, -StateListDrawable, AnimatedVectorDrawable, RippleDrawable 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import sys -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "ShapeDrawable", - "PaintDrawable", - "GradientDrawable", - "StateListDrawable", - "LayerDrawable", - "LevelListDrawable", - "ScaleDrawable", - "ClipDrawable", - "RotateDrawable", - "TransitionDrawable", - "AnimatedStateListDrawable", - "AnimatedImageDrawable", - "AnimatedVectorDrawable", - "RippleDrawable", - "AnimatedRotateDrawable", - "InsetDrawable", - "NinePatchDrawable", -] - - -# ═══════════════════════════════════════════════════════════════ -# ShapeDrawable -# ═══════════════════════════════════════════════════════════════ - -class ShapeDrawable(Stub): - """ShapeDrawable — 形状可绘制对象 (桌面存根)""" - - def __init__(self, shape: Any = None): - super().__init__() - self._shape = shape - self._paint = None - self._shader_factory = None - self._bounds = (0, 0, 0, 0) - self._alpha = 255 - - def getShape(self) -> Any: - """获取形状""" - return self._shape - - def setShape(self, shape: Any) -> None: - """设置形状""" - self._shape = shape - - def getPaint(self) -> Any: - """获取画笔""" - return self._paint - - def setShaderFactory(self, factory: Any) -> None: - """设置着色器工厂""" - self._shader_factory = factory - - def getShaderFactory(self) -> Any: - """获取着色器工厂""" - return self._shader_factory - - def inflate(self, context: Any, attrs: Any) -> None: - """从 XML 填充""" - pass - - def setAlpha(self, alpha: int) -> None: - """设置透明度""" - self._alpha = alpha - - def getAlpha(self) -> int: - """获取透明度""" - return self._alpha - - def setBounds(self, left: int, top: int, right: int, bottom: int) -> None: - """设置边界""" - self._bounds = (left, top, right, bottom) - - def getIntrinsicWidth(self) -> int: - """获取内部宽度""" - if self._shape and hasattr(self._shape, 'getWidth'): - return self._shape.getWidth() - return 0 - - def getIntrinsicHeight(self) -> int: - """获取内部高度""" - if self._shape and hasattr(self._shape, 'getHeight'): - return self._shape.getHeight() - return 0 - - -class PaintDrawable(ShapeDrawable): - """PaintDrawable — 画笔可绘制对象""" - - def __init__(self, color: int = 0): - super().__init__() - self._color = color - - def setColor(self, color: int) -> None: - """设置颜色""" - self._color = color - - -# ═══════════════════════════════════════════════════════════════ -# GradientDrawable -# ═══════════════════════════════════════════════════════════════ - -class GradientDrawable(ShapeDrawable): - """GradientDrawable — 渐变可绘制对象""" - - RECTANGLE = 0 - OVAL = 1 - LINE = 2 - RING = 3 - LINEAR_GRADIENT = 0 - RADIAL_GRADIENT = 1 - SWEEP_GRADIENT = 2 - - def __init__(self, orientation: Any = None, colors: Optional[List[int]] = None): - super().__init__() - self._shape_type = GradientDrawable.RECTANGLE - self._gradient_type = GradientDrawable.LINEAR_GRADIENT - self._gradient_center = (0.5, 0.5) - self._corner_radius = 0.0 - self._corner_radii: Optional[List[float]] = None - self._stroke_width = 0 - self._stroke_color = 0 - self._color = 0 - self._colors = colors or [] - self._orientation = orientation - self._size = (0, 0) - - def setShape(self, shape_type: int) -> None: - """设置形状类型""" - self._shape_type = shape_type - - def setSize(self, width: int, height: int) -> None: - """设置尺寸""" - self._size = (width, height) - - def setGradientType(self, gradient_type: int) -> None: - """设置渐变类型""" - self._gradient_type = gradient_type - - def setGradientCenter(self, x: float, y: float) -> None: - """设置渐变中心""" - self._gradient_center = (x, y) - - def setCornerRadius(self, radius: float) -> None: - """设置圆角半径""" - self._corner_radius = radius - - def setCornerRadii(self, radii: List[float]) -> None: - """设置圆角半径组""" - self._corner_radii = radii - - def setStroke(self, width: int, color: int, dash_width: float = 0.0, - dash_gap: float = 0.0) -> None: - """设置描边""" - self._stroke_width = width - self._stroke_color = color - - def setColor(self, color: int) -> None: - """设置颜色""" - self._color = color - - def getColor(self) -> int: - """获取颜色""" - return self._color - - def setColors(self, colors: List[int]) -> None: - """设置颜色数组""" - self._colors = colors - - def getOrientation(self) -> Any: - """获取方向""" - return self._orientation - - def getIntrinsicWidth(self) -> int: - return self._size[0] - - def getIntrinsicHeight(self) -> int: - return self._size[1] - - -# ═══════════════════════════════════════════════════════════════ -# StateListDrawable -# ═══════════════════════════════════════════════════════════════ - -class StateListDrawable(ShapeDrawable): - """StateListDrawable — 状态列表可绘制对象""" - - def __init__(self): - super().__init__() - self._states: List[Tuple[List[int], Any]] = [] - self._enter_fade_duration = 0 - self._exit_fade_duration = 0 - - def addState(self, state_set: List[int], drawable: Any) -> None: - """添加状态""" - self._states.append((state_set, drawable)) - - def setState(self, state_set: List[int]) -> bool: - """设置当前状态""" - return True - - def getStateCount(self) -> int: - """获取状态数量""" - return len(self._states) - - def getStateSet(self, index: int) -> List[int]: - """获取指定索引的状态集""" - if 0 <= index < len(self._states): - return self._states[index][0] - return [] - - def getStateDrawable(self, index: int) -> Any: - """获取指定索引的可绘制对象""" - if 0 <= index < len(self._states): - return self._states[index][1] - return None - - def setEnterFadeDuration(self, duration: int) -> None: - """设置进入淡出时间""" - self._enter_fade_duration = duration - - def setExitFadeDuration(self, duration: int) -> None: - """设置退出淡出时间""" - self._exit_fade_duration = duration - - -# ═══════════════════════════════════════════════════════════════ -# LayerDrawable -# ═══════════════════════════════════════════════════════════════ - -class LayerDrawable(ShapeDrawable): - """LayerDrawable — 图层可绘制对象""" - - def __init__(self, layers: Optional[List[Any]] = None): - super().__init__() - self._layers: List[Any] = layers or [] - self._layer_insets: Dict[int, Tuple[int, int, int, int]] = {} - self._padding = (0, 0, 0, 0) - self._id_map: Dict[int, int] = {} - - def setLayerInset(self, index: int, left: int, top: int, right: int, bottom: int) -> None: - """设置图层嵌入""" - self._layer_insets[index] = (left, top, right, bottom) - - def setLayerInsetRelative(self, index: int, start: int, top: int, end: int, bottom: int) -> None: - """设置相对图层嵌入""" - self._layer_insets[index] = (start, top, end, bottom) - - def setDrawable(self, index: int, drawable: Any) -> None: - """设置可绘制对象""" - while len(self._layers) <= index: - self._layers.append(None) - self._layers[index] = drawable - - def setDrawableByLayerId(self, id_: int, drawable: Any) -> bool: - """按图层 ID 设置可绘制对象""" - if id_ in self._id_map: - idx = self._id_map[id_] - self._layers[idx] = drawable - return True - return False - - def findDrawableByLayerId(self, id_: int) -> Any: - """按图层 ID 查找可绘制对象""" - if id_ in self._id_map: - idx = self._id_map[id_] - if 0 <= idx < len(self._layers): - return self._layers[idx] - return None - - def getLayerCount(self) -> int: - """获取图层数量""" - return len(self._layers) - - def getNumberOfLayers(self) -> int: - """获取图层数量""" - return len(self._layers) - - def getId(self, index: int) -> int: - """获取图层 ID""" - for id_, idx in self._id_map.items(): - if idx == index: - return id_ - return 0 - - def setPadding(self, left: int, top: int, right: int, bottom: int) -> None: - """设置内边距""" - self._padding = (left, top, right, bottom) - - def setLayerGravity(self, index: int, gravity: int) -> None: - """设置图层重力""" - pass - - def setLayerWidth(self, index: int, width: int) -> None: - """设置图层宽度""" - pass - - def setLayerHeight(self, index: int, height: int) -> None: - """设置图层高度""" - pass - - def setLayerInsetLeft(self, index: int, left: int) -> None: - if index in self._layer_insets: - l, t, r, b = self._layer_insets[index] - self._layer_insets[index] = (left, t, r, b) - else: - self._layer_insets[index] = (left, 0, 0, 0) - - def setLayerInsetRight(self, index: int, right: int) -> None: - if index in self._layer_insets: - l, t, r, b = self._layer_insets[index] - self._layer_insets[index] = (l, t, right, b) - else: - self._layer_insets[index] = (0, 0, right, 0) - - def setLayerInsetTop(self, index: int, top: int) -> None: - if index in self._layer_insets: - l, t, r, b = self._layer_insets[index] - self._layer_insets[index] = (l, top, r, b) - else: - self._layer_insets[index] = (0, top, 0, 0) - - def setLayerInsetBottom(self, index: int, bottom: int) -> None: - if index in self._layer_insets: - l, t, r, b = self._layer_insets[index] - self._layer_insets[index] = (l, t, r, bottom) - else: - self._layer_insets[index] = (0, 0, 0, bottom) - - def setLayerInsetStart(self, index: int, start: int) -> None: - self.setLayerInsetLeft(index, start) - - def setLayerInsetEnd(self, index: int, end: int) -> None: - self.setLayerInsetRight(index, end) - - def mutate(self) -> "LayerDrawable": - """变异""" - return self - - -class LevelListDrawable(StateListDrawable): - """LevelListDrawable — 级别列表可绘制对象""" - - def __init__(self): - super().__init__() - - -# ═══════════════════════════════════════════════════════════════ -# ScaleDrawable / ClipDrawable / RotateDrawable -# ═══════════════════════════════════════════════════════════════ - -class ScaleDrawable(ShapeDrawable): - """ScaleDrawable — 缩放可绘制对象""" - - def __init__(self, drawable: Any = None, gravity: int = 0, - scale_width: float = 1.0, scale_height: float = 1.0): - super().__init__() - self._drawable = drawable - self._gravity = gravity - self._scale_width = scale_width - self._scale_height = scale_height - self._level = 0 - - def setScaleWidth(self, scale_width: float) -> None: - self._scale_width = scale_width - - def setScaleHeight(self, scale_height: float) -> None: - self._scale_height = scale_height - - def getScale(self) -> Tuple[float, float]: - return (self._scale_width, self._scale_height) - - def getLevel(self) -> int: - return self._level - - -class ClipDrawable(ShapeDrawable): - """ClipDrawable — 裁剪可绘制对象""" - - HORIZONTAL = 1 - VERTICAL = 2 - - def __init__(self, drawable: Any = None, gravity: int = 0, orientation: int = HORIZONTAL): - super().__init__() - self._drawable = drawable - self._gravity = gravity - self._orientation = orientation - - -class RotateDrawable(ShapeDrawable): - """RotateDrawable — 旋转可绘制对象""" - - def __init__(self, drawable: Any = None, gravity: int = 0): - super().__init__() - self._drawable = drawable - self._gravity = gravity - self._rotation = 0.0 - self._pivot_x = 0.5 - self._pivot_y = 0.5 - - def setRotation(self, rotation: float) -> None: - self._rotation = rotation - - -# ═══════════════════════════════════════════════════════════════ -# TransitionDrawable -# ═══════════════════════════════════════════════════════════════ - -class TransitionDrawable(LayerDrawable): - """TransitionDrawable — 过渡可绘制对象""" - - def __init__(self, layers: Optional[List[Any]] = None): - super().__init__(layers) - self._cross_fade_enabled = False - - def startTransition(self, duration_ms: int) -> None: - """开始过渡""" - pass - - def resetTransition(self) -> None: - """重置过渡""" - pass - - def reverseTransition(self, duration_ms: int) -> None: - """反向过渡""" - pass - - def crossFadeEnabled(self) -> bool: - """检查是否启用交叉淡入""" - return self._cross_fade_enabled - - def setCrossFadeEnabled(self, enabled: bool) -> None: - """设置交叉淡入""" - self._cross_fade_enabled = enabled - - -# ═══════════════════════════════════════════════════════════════ -# AnimatedStateListDrawable -# ═══════════════════════════════════════════════════════════════ - -class AnimatedStateListDrawable(StateListDrawable): - """AnimatedStateListDrawable — 动画状态列表可绘制对象""" - - def __init__(self): - super().__init__() - self._transitions: Dict[Tuple[int, int], Any] = {} - - def addState(self, state_set: List[int], drawable: Any, state_id: int) -> None: - """添加状态""" - self._states.append((state_set, drawable)) - - def addTransition(self, from_id: int, to_id: int, transition: Any) -> None: - """添加过渡""" - self._transitions[(from_id, to_id)] = transition - - -# ═══════════════════════════════════════════════════════════════ -# AnimatedImageDrawable -# ═══════════════════════════════════════════════════════════════ - -class AnimatedImageDrawable(ShapeDrawable): - """AnimatedImageDrawable — 动画图像可绘制对象""" - - LOOP_FINITE = 0 - LOOP_INFINITE = -1 - INFINITE = -1 - - def __init__(self): - super().__init__() - self._running = False - self._repeat_count = 0 - self._loop_behavior = AnimatedImageDrawable.LOOP_FINITE - - def start(self) -> None: - """开始动画""" - self._running = True - - def stop(self) -> None: - """停止动画""" - self._running = False - - def isRunning(self) -> bool: - """检查是否正在运行""" - return self._running - - def setRepeatCount(self, count: int) -> None: - """设置重复次数""" - self._repeat_count = count - - def getRepeatCount(self) -> int: - """获取重复次数""" - return self._repeat_count - - def setLoopBehavior(self, behavior: int) -> None: - """设置循环行为""" - self._loop_behavior = behavior - - def getLoopBehavior(self) -> int: - """获取循环行为""" - return self._loop_behavior - - -# ═══════════════════════════════════════════════════════════════ -# AnimatedVectorDrawable -# ═══════════════════════════════════════════════════════════════ - -class AnimatedVectorDrawable(ShapeDrawable): - """AnimatedVectorDrawable — 动画矢量可绘制对象""" - - def __init__(self): - super().__init__() - self._running = False - self._alpha = 255 - self._hotspot = (0, 0) - self._hotspot_bounds = (0, 0, 0, 0) - - def start(self) -> None: - """开始动画""" - self._running = True - - def stop(self) -> None: - """停止动画""" - self._running = False - - def reset(self) -> None: - """重置动画""" - self._running = False - - def isRunning(self) -> bool: - """检查是否正在运行""" - return self._running - - def setVisible(self, visible: bool, restart: bool) -> bool: - """设置可见性""" - return True - - def setAlpha(self, alpha: int) -> None: - """设置透明度""" - self._alpha = alpha - - def getAlpha(self) -> int: - """获取透明度""" - return self._alpha - - def setHotspot(self, x: float, y: float) -> None: - """设置热点""" - self._hotspot = (x, y) - - def setHotspotBounds(self, left: int, top: int, right: int, bottom: int) -> None: - """设置热点边界""" - self._hotspot_bounds = (left, top, right, bottom) - - def getIntrinsicWidth(self) -> int: - return 48 - - def getIntrinsicHeight(self) -> int: - return 48 - - -# ═══════════════════════════════════════════════════════════════ -# RippleDrawable -# ═══════════════════════════════════════════════════════════════ - -class RippleDrawable(ShapeDrawable): - """RippleDrawable — 水波纹可绘制对象""" - - RIPPLE_ENTERING = 0 - RIPPLE_EXITING = 1 - - def __init__(self, color: int = 0, content: Any = None, mask: Any = None): - super().__init__() - self._radius = 0 - self._color = color - self._effect_color = color - self._content = content - self._mask = mask - self._ripple_state = RippleDrawable.RIPPLE_ENTERING - - def setRadius(self, radius: int) -> None: - """设置半径""" - self._radius = radius - - def getRadius(self) -> int: - """获取半径""" - return self._radius - - def setColor(self, color: int) -> None: - """设置颜色""" - self._color = color - - def getColor(self) -> int: - """获取颜色""" - return self._color - - def setEffectColor(self, color: int) -> None: - """设置效果颜色""" - self._effect_color = color - - def getEffectColor(self) -> int: - """获取效果颜色""" - return self._effect_color - - -# ═══════════════════════════════════════════════════════════════ -# AnimatedRotateDrawable / InsetDrawable / NinePatchDrawable -# ═══════════════════════════════════════════════════════════════ - -class AnimatedRotateDrawable(ShapeDrawable): - """AnimatedRotateDrawable — 动画旋转可绘制对象""" - - def __init__(self, drawable: Any = None, gravity: int = 0): - super().__init__() - self._drawable = drawable - self._gravity = gravity - self._running = False - - def start(self) -> None: - self._running = True - - def stop(self) -> None: - self._running = False - - -class InsetDrawable(ShapeDrawable): - """InsetDrawable — 嵌入可绘制对象""" - - def __init__(self, drawable: Any = None, inset: int = 0): - super().__init__() - self._drawable = drawable - self._inset = inset - self._outset = inset - - def getInset(self) -> int: - """获取嵌入""" - return self._inset - - def getOutset(self) -> int: - """获取外延""" - return self._outset - - -class NinePatchDrawable(ShapeDrawable): - """NinePatchDrawable — 九宫格可绘制对象""" - - def __init__(self, nine_patch: Any = None): - super().__init__() - self._nine_patch = nine_patch - - def getIntrinsicWidth(self) -> int: - return 0 - - def getIntrinsicHeight(self) -> int: - return 0 \ No newline at end of file diff --git a/android_pyapi/android/graphics_drawable_shapes.py b/android_pyapi/android/graphics_drawable_shapes.py deleted file mode 100644 index 9bd442b..0000000 --- a/android_pyapi/android/graphics_drawable_shapes.py +++ /dev/null @@ -1,589 +0,0 @@ -""" -Android Graphics Drawable Shapes 组件 — Shape, ArcShape, OvalShape, PathShape, RectShape, RoundRectShape -方法签名完全对齐 Android SDK (android.graphics.drawable.shapes 包) -""" - -from __future__ import annotations - -import math -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# Shape -# ═══════════════════════════════════════════════════════════════ - -class Shape(Stub): - """Shape — 形状基类""" - - def __init__(self): - super().__init__() - self._width: float = 0.0 - self._height: float = 0.0 - self._alpha: int = 255 - self._visible: bool = True - self._dirty: bool = True - self._bounds: Tuple[float, float, float, float] = (0.0, 0.0, 0.0, 0.0) - - def getWidth(self) -> float: - return self._width - - def getHeight(self) -> float: - return self._height - - def getAlpha(self) -> int: - return self._alpha - - def setAlpha(self, alpha: int): - self._alpha = max(0, min(255, alpha)) - - def isVisible(self) -> bool: - return self._visible - - def setVisible(self, visible: bool): - self._visible = visible - - def resize(self, width: float, height: float): - """调整大小""" - self._width = width - self._height = height - self._bounds = (0.0, 0.0, width, height) - self._dirty = True - self.onResize(width, height) - - def onResize(self, width: float, height: float): - """大小变化回调""" - pass - - def getBounds(self) -> Tuple[float, float, float, float]: - return self._bounds - - def setBounds(self, left: float, top: float, right: float, bottom: float): - self._bounds = (left, top, right, bottom) - self._width = right - left - self._height = bottom - top - - def draw(self, canvas: Any, paint: Any): - """绘制形状 (由子类实现)""" - pass - - def hasAlpha(self) -> bool: - """检查是否有透明度""" - return self._alpha < 255 - - def clone(self) -> Shape: - """克隆形状""" - new_shape = self.__class__.__new__(self.__class__) - new_shape._width = self._width - new_shape._height = self._height - new_shape._alpha = self._alpha - new_shape._visible = self._visible - new_shape._bounds = self._bounds - return new_shape - - def isDirty(self) -> bool: - return self._dirty - - def setDirty(self, dirty: bool): - self._dirty = dirty - - def invalidate(self): - self._dirty = True - - def toString(self) -> str: - return f"Shape(w={self._width:.1f}, h={self._height:.1f})" - - -# ═══════════════════════════════════════════════════════════════ -# RectShape -# ═══════════════════════════════════════════════════════════════ - -class RectShape(Shape): - """RectShape — 矩形形状""" - - def __init__(self): - super().__init__() - self._corner_radius: Tuple[float, float, float, float] = (0.0, 0.0, 0.0, 0.0) - self._fill_color: int = 0xFF000000 - self._stroke_color: int = 0x00000000 - self._stroke_width: float = 0.0 - - def draw(self, canvas: Any, paint: Any): - """绘制矩形""" - pass - - def getCornerRadii(self) -> Tuple[float, float, float, float]: - return self._corner_radius - - def setCornerRadii(self, radii: Tuple[float, float, float, float]): - self._corner_radius = radii - - def getFillColor(self) -> int: - return self._fill_color - - def setFillColor(self, color: int): - self._fill_color = color - - def getStrokeColor(self) -> int: - return self._stroke_color - - def setStrokeColor(self, color: int): - self._stroke_color = color - - def getStrokeWidth(self) -> float: - return self._stroke_width - - def setStrokeWidth(self, width: float): - self._stroke_width = width - - def getCenterX(self) -> float: - return self._width / 2.0 - - def getCenterY(self) -> float: - return self._height / 2.0 - - def containsPoint(self, x: float, y: float) -> bool: - """检查是否包含点""" - return 0.0 <= x <= self._width and 0.0 <= y <= self._height - - def clone(self) -> RectShape: - r = RectShape() - r._width = self._width - r._height = self._height - r._alpha = self._alpha - r._corner_radius = self._corner_radius - r._fill_color = self._fill_color - r._stroke_color = self._stroke_color - r._stroke_width = self._stroke_width - r._bounds = self._bounds - return r - - def toString(self) -> str: - return f"RectShape(w={self._width:.1f}, h={self._height:.1f})" - - -# ═══════════════════════════════════════════════════════════════ -# OvalShape -# ═══════════════════════════════════════════════════════════════ - -class OvalShape(Shape): - """OvalShape — 椭圆形状""" - - def __init__(self): - super().__init__() - self._fill_color: int = 0xFF000000 - self._stroke_color: int = 0x00000000 - self._stroke_width: float = 0.0 - self._start_angle: float = 0.0 - self._sweep_angle: float = 360.0 - - def draw(self, canvas: Any, paint: Any): - """绘制椭圆""" - pass - - def getFillColor(self) -> int: - return self._fill_color - - def setFillColor(self, color: int): - self._fill_color = color - - def getStrokeColor(self) -> int: - return self._stroke_color - - def setStrokeColor(self, color: int): - self._stroke_color = color - - def getStrokeWidth(self) -> float: - return self._stroke_width - - def setStrokeWidth(self, width: float): - self._stroke_width = width - - def getCenterX(self) -> float: - return self._width / 2.0 - - def getCenterY(self) -> float: - return self._height / 2.0 - - def getRadiusX(self) -> float: - return self._width / 2.0 - - def getRadiusY(self) -> float: - return self._height / 2.0 - - def getStartAngle(self) -> float: - return self._start_angle - - def setStartAngle(self, angle: float): - self._start_angle = angle - - def getSweepAngle(self) -> float: - return self._sweep_angle - - def setSweepAngle(self, angle: float): - self._sweep_angle = angle - - def isCircle(self) -> bool: - """检查是否为圆形""" - return abs(self._width - self._height) < 0.001 - - def containsPoint(self, x: float, y: float) -> bool: - """检查是否包含点""" - cx = self.getCenterX() - cy = self.getCenterY() - rx = self.getRadiusX() - ry = self.getRadiusY() - if rx <= 0 or ry <= 0: - return False - dx = (x - cx) / rx - dy = (y - cy) / ry - return (dx * dx + dy * dy) <= 1.0 - - def clone(self) -> OvalShape: - o = OvalShape() - o._width = self._width - o._height = self._height - o._alpha = self._alpha - o._fill_color = self._fill_color - o._stroke_color = self._stroke_color - o._stroke_width = self._stroke_width - o._start_angle = self._start_angle - o._sweep_angle = self._sweep_angle - o._bounds = self._bounds - return o - - def toString(self) -> str: - return f"OvalShape(w={self._width:.1f}, h={self._height:.1f})" - - -# ═══════════════════════════════════════════════════════════════ -# ArcShape -# ═══════════════════════════════════════════════════════════════ - -class ArcShape(OvalShape): - """ArcShape — 圆弧形状""" - - def __init__(self, start_angle: float = 0.0, sweep_angle: float = 90.0): - super().__init__() - self._start_angle = start_angle - self._sweep_angle = sweep_angle - self._use_center: bool = False - - def draw(self, canvas: Any, paint: Any): - """绘制圆弧""" - pass - - def getStartAngle(self) -> float: - return self._start_angle - - def setStartAngle(self, angle: float): - self._start_angle = angle - - def getSweepAngle(self) -> float: - return self._sweep_angle - - def setSweepAngle(self, angle: float): - self._sweep_angle = angle - - def isUseCenter(self) -> bool: - return self._use_center - - def setUseCenter(self, use_center: bool): - self._use_center = use_center - - def getArcEndX(self) -> float: - cx = self.getCenterX() - cy = self.getCenterY() - rx = self.getRadiusX() - ry = self.getRadiusY() - angle_rad = math.radians(self._start_angle + self._sweep_angle) - return cx + rx * math.cos(angle_rad) - - def getArcEndY(self) -> float: - cx = self.getCenterX() - cy = self.getCenterY() - rx = self.getRadiusX() - ry = self.getRadiusY() - angle_rad = math.radians(self._start_angle + self._sweep_angle) - return cy + ry * math.sin(angle_rad) - - def isFullCircle(self) -> bool: - return abs(self._sweep_angle) >= 360.0 - - def clone(self) -> ArcShape: - a = ArcShape(self._start_angle, self._sweep_angle) - a._width = self._width - a._height = self._height - a._alpha = self._alpha - a._fill_color = self._fill_color - a._stroke_color = self._stroke_color - a._stroke_width = self._stroke_width - a._use_center = self._use_center - a._bounds = self._bounds - return a - - def toString(self) -> str: - return f"ArcShape(start={self._start_angle:.1f}, sweep={self._sweep_angle:.1f})" - - -# ═══════════════════════════════════════════════════════════════ -# PathShape -# ═══════════════════════════════════════════════════════════════ - -class PathShape(Shape): - """PathShape — 路径形状""" - - def __init__(self, path: Any = None, std_width: float = 1.0, std_height: float = 1.0): - super().__init__() - self._path = path - self._std_width = std_width - self._std_height = std_height - self._fill_color: int = 0xFF000000 - self._stroke_color: int = 0x00000000 - self._stroke_width: float = 0.0 - self._scale_x: float = 1.0 - self._scale_y: float = 1.0 - - def draw(self, canvas: Any, paint: Any): - """绘制路径""" - pass - - def getPath(self) -> Any: - return self._path - - def setPath(self, path: Any): - self._path = path - - def getStdWidth(self) -> float: - return self._std_width - - def getStdHeight(self) -> float: - return self._std_height - - def getFillColor(self) -> int: - return self._fill_color - - def setFillColor(self, color: int): - self._fill_color = color - - def getStrokeColor(self) -> int: - return self._stroke_color - - def setStrokeColor(self, color: int): - self._stroke_color = color - - def getStrokeWidth(self) -> float: - return self._stroke_width - - def setStrokeWidth(self, width: float): - self._stroke_width = width - - def onResize(self, width: float, height: float): - if self._std_width > 0 and self._std_height > 0: - self._scale_x = width / self._std_width - self._scale_y = height / self._std_height - - def getScaleX(self) -> float: - return self._scale_x - - def getScaleY(self) -> float: - return self._scale_y - - def containsPoint(self, x: float, y: float) -> bool: - """检查是否包含点""" - if self._path is None: - return False - # 使用简单矩形包含检查 - return 0.0 <= x <= self._width and 0.0 <= y <= self._height - - def clone(self) -> PathShape: - p = PathShape(self._path, self._std_width, self._std_height) - p._width = self._width - p._height = self._height - p._alpha = self._alpha - p._fill_color = self._fill_color - p._stroke_color = self._stroke_color - p._stroke_width = self._stroke_width - p._scale_x = self._scale_x - p._scale_y = self._scale_y - p._bounds = self._bounds - return p - - def toString(self) -> str: - return f"PathShape(std={self._std_width:.1f}x{self._std_height:.1f})" - - -# ═══════════════════════════════════════════════════════════════ -# RoundRectShape -# ═══════════════════════════════════════════════════════════════ - -class RoundRectShape(RectShape): - """RoundRectShape — 圆角矩形形状""" - - def __init__(self, outer_radii: Optional[Tuple[float, float, float, float]] = None, - inner_radii: Optional[Tuple[float, float, float, float]] = None, - inset: Optional[Tuple[float, float, float, float]] = None): - super().__init__() - self._outer_radii = outer_radii or (0.0, 0.0, 0.0, 0.0) - self._inner_radii = inner_radii or (0.0, 0.0, 0.0, 0.0) - self._inset = inset or (0.0, 0.0, 0.0, 0.0) - - def draw(self, canvas: Any, paint: Any): - """绘制圆角矩形""" - pass - - def getOuterRadii(self) -> Tuple[float, float, float, float]: - return self._outer_radii - - def setOuterRadii(self, radii: Tuple[float, float, float, float]): - self._outer_radii = radii - - def getInnerRadii(self) -> Tuple[float, float, float, float]: - return self._inner_radii - - def setInnerRadii(self, radii: Tuple[float, float, float, float]): - self._inner_radii = radii - - def getInset(self) -> Tuple[float, float, float, float]: - return self._inset - - def setInset(self, inset: Tuple[float, float, float, float]): - self._inset = inset - - def getOuterRect(self) -> Tuple[float, float, float, float]: - left, top, right, bottom = self._bounds - return (left + self._inset[0], top + self._inset[1], - right - self._inset[2], bottom - self._inset[3]) - - def getInnerRect(self) -> Tuple[float, float, float, float]: - outer = self.getOuterRect() - r = max(self._outer_radii) - return (outer[0] + r, outer[1] + r, - outer[2] - r, outer[3] - r) - - def hasInnerShape(self) -> bool: - """检查是否有内部形状""" - return any(r > 0 for r in self._inner_radii) - - def getCornerRadius(self) -> float: - """获取统一圆角半径 (所有角相同)""" - if all(abs(r - self._outer_radii[0]) < 0.001 for r in self._outer_radii): - return self._outer_radii[0] - return -1.0 - - def setCornerRadius(self, radius: float): - self._outer_radii = (radius, radius, radius, radius) - - def isRect(self) -> bool: - """检查是否为普通矩形""" - return all(r == 0.0 for r in self._outer_radii) - - def clone(self) -> RoundRectShape: - rr = RoundRectShape(self._outer_radii, self._inner_radii, self._inset) - rr._width = self._width - rr._height = self._height - rr._alpha = self._alpha - rr._fill_color = self._fill_color - rr._stroke_color = self._stroke_color - rr._stroke_width = self._stroke_width - rr._bounds = self._bounds - return rr - - def toString(self) -> str: - return f"RoundRectShape(radius={self._outer_radii[0]:.1f})" - - -# ═══════════════════════════════════════════════════════════════ -# ShapeUtils -# ═══════════════════════════════════════════════════════════════ - -class ShapeUtils(Stub): - """ShapeUtils — 形状工具类""" - - @staticmethod - def createRect(width: float, height: float) -> RectShape: - r = RectShape() - r.resize(width, height) - return r - - @staticmethod - def createOval(width: float, height: float) -> OvalShape: - o = OvalShape() - o.resize(width, height) - return o - - @staticmethod - def createCircle(radius: float) -> OvalShape: - o = OvalShape() - o.resize(radius * 2, radius * 2) - return o - - @staticmethod - def createArc(start_angle: float, sweep_angle: float, - width: float, height: float) -> ArcShape: - a = ArcShape(start_angle, sweep_angle) - a.resize(width, height) - return a - - @staticmethod - def createRoundRect(width: float, height: float, - corner_radius: float) -> RoundRectShape: - rr = RoundRectShape((corner_radius, corner_radius, - corner_radius, corner_radius)) - rr.resize(width, height) - return rr - - @staticmethod - def createPathShape(path: Any, std_width: float, std_height: float) -> PathShape: - return PathShape(path, std_width, std_height) - - @staticmethod - def createSquare(size: float) -> RectShape: - r = RectShape() - r.resize(size, size) - return r - - @staticmethod - def applyFill(shape: Shape, color: int): - if isinstance(shape, RectShape): - shape.setFillColor(color) - elif isinstance(shape, OvalShape): - shape.setFillColor(color) - elif isinstance(shape, PathShape): - shape.setFillColor(color) - - @staticmethod - def applyStroke(shape: Shape, color: int, width: float): - if isinstance(shape, RectShape): - shape.setStrokeColor(color) - shape.setStrokeWidth(width) - elif isinstance(shape, OvalShape): - shape.setStrokeColor(color) - shape.setStrokeWidth(width) - elif isinstance(shape, PathShape): - shape.setStrokeColor(color) - shape.setStrokeWidth(width) - - @staticmethod - def getShapeCenter(shape: Shape) -> Tuple[float, float]: - return (shape.getWidth() / 2.0, shape.getHeight() / 2.0) - - @staticmethod - def shapeEquals(a: Shape, b: Shape) -> bool: - if type(a) is not type(b): - return False - return (abs(a.getWidth() - b.getWidth()) < 0.001 and - abs(a.getHeight() - b.getHeight()) < 0.001 and - a.getAlpha() == b.getAlpha()) - - @staticmethod - def scaleShape(shape: Shape, scale_x: float, scale_y: float): - shape.resize(shape.getWidth() * scale_x, shape.getHeight() * scale_y) - - @staticmethod - def translateShape(shape: Shape, dx: float, dy: float): - b = shape.getBounds() - shape.setBounds(b[0] + dx, b[1] + dy, b[2] + dx, b[3] + dy) \ No newline at end of file diff --git a/android_pyapi/android/graphics_fonts.py b/android_pyapi/android/graphics_fonts.py deleted file mode 100644 index f32295f..0000000 --- a/android_pyapi/android/graphics_fonts.py +++ /dev/null @@ -1,298 +0,0 @@ -""" -Android graphics.fonts 包 — Font, FontFamily, FontStyle, FontVariationAxis, Typeface -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import sys -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "Font", - "FontFamily", - "FontStyle", - "FontVariationAxis", - "Typeface", -] - - -# ═══════════════════════════════════════════════════════════════ -# FontStyle -# ═══════════════════════════════════════════════════════════════ - -class FontStyle(Stub): - """FontStyle — 字体样式""" - - FONT_SLANT_UPRIGHT = 0 - FONT_SLANT_ITALIC = 1 - FONT_WIDTH_NORMAL = 100 - FONT_WIDTH_CONDENSED = 75 - FONT_WIDTH_EXTENDED = 125 - - def __init__(self, weight: int = 400, slant: int = FONT_SLANT_UPRIGHT, - width: int = FONT_WIDTH_NORMAL): - super().__init__() - self._weight = weight - self._slant = slant - self._width = width - - def getWeight(self) -> int: - """获取字重""" - return self._weight - - def getSlant(self) -> int: - """获取倾斜""" - return self._slant - - def getWidth(self) -> int: - """获取宽度""" - return self._width - - def toString(self) -> str: - return f"FontStyle(weight={self._weight}, slant={self._slant}, width={self._width})" - - -# ═══════════════════════════════════════════════════════════════ -# FontVariationAxis -# ═══════════════════════════════════════════════════════════════ - -class FontVariationAxis(Stub): - """FontVariationAxis — 字体变体轴""" - - def __init__(self, tag: str = "", style_value: float = 0.0): - super().__init__() - self._tag = tag - self._style_value = style_value - - def getTag(self) -> str: - """获取标签""" - return self._tag - - def getStyleValue(self) -> float: - """获取样式值""" - return self._style_value - - def toString(self) -> str: - return f"FontVariationAxis(tag={self._tag}, value={self._style_value})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, FontVariationAxis): - return False - return self._tag == other._tag and self._style_value == other._style_value - - def hashCode(self) -> int: - return hash((self._tag, self._style_value)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# Font -# ═══════════════════════════════════════════════════════════════ - -class Font(Stub): - """Font — 字体 (桌面存根,包装系统字体)""" - - _SYSTEM_FONT_DIRS = [ - "/usr/share/fonts", - "/System/Library/Fonts", - "C:\\Windows\\Fonts", - ] - - def __init__(self, file: Optional[str] = None, buffer: Optional[Any] = None, - weight: int = 400, italic: bool = False, ttc_index: int = 0, - axes: Optional[List[FontVariationAxis]] = None): - super().__init__() - self._file = file - self._buffer = buffer - self._weight = weight - self._italic = italic - self._ttc_index = ttc_index - self._axes = axes or [] - self._native_ptr = 0 - self._font_size = 12.0 - self._obb = None - self._source = file or "default" - - def getStyle(self) -> FontStyle: - """获取字体样式""" - return FontStyle(self._weight, FontStyle.FONT_SLANT_ITALIC if self._italic else FontStyle.FONT_SLANT_UPRIGHT) - - def getBuffer(self) -> Optional[Any]: - """获取缓冲区""" - return self._buffer - - def getNativePtr(self) -> int: - """获取原生指针""" - return self._native_ptr - - def getFontSize(self) -> float: - """获取字号""" - return self._font_size - - def getObb(self) -> Optional[Any]: - """获取 OBB""" - return self._obb - - def getSource(self) -> str: - """获取来源""" - return self._source - - def getTtcIndex(self) -> int: - """获取 TTC 索引""" - return self._ttc_index - - def getWeight(self) -> int: - """获取字重""" - return self._weight - - def getItalic(self) -> bool: - """获取斜体""" - return self._italic - - def getAxes(self) -> List[FontVariationAxis]: - """获取变体轴列表""" - return self._axes.copy() - - def getFile(self) -> Optional[str]: - """获取文件路径""" - return self._file - - @staticmethod - def buildNativeTypeface(font: "Font") -> int: - """构建原生字体类型""" - return 0 - - @staticmethod - def load(file: str, weight: int = 400, italic: bool = False, - ttc_index: int = 0) -> Optional["Font"]: - """从文件加载字体""" - if os.path.exists(file): - return Font(file=file, weight=weight, italic=italic, ttc_index=ttc_index) - return None - - def toString(self) -> str: - return f"Font(file={self._file}, weight={self._weight}, italic={self._italic})" - - -# ═══════════════════════════════════════════════════════════════ -# FontFamily -# ═══════════════════════════════════════════════════════════════ - -class FontFamily(Stub): - """FontFamily — 字体家族""" - - def __init__(self, fonts: Optional[List[Font]] = None): - super().__init__() - self._fonts = fonts or [] - - def addFont(self, font: Font) -> None: - """添加字体""" - self._fonts.append(font) - - def getFont(self, index: int) -> Optional[Font]: - """获取指定索引的字体""" - if 0 <= index < len(self._fonts): - return self._fonts[index] - return None - - def getSize(self) -> int: - """获取字体数量""" - return len(self._fonts) - - def toString(self) -> str: - return f"FontFamily(size={len(self._fonts)})" - - -# ═══════════════════════════════════════════════════════════════ -# Typeface (fonts package version) -# ═══════════════════════════════════════════════════════════════ - -class Typeface(Stub): - """Typeface — 字体类型 (图形字体包版本)""" - - DEFAULT: "Typeface" = None - DEFAULT_BOLD: "Typeface" = None - MONOSPACE: "Typeface" = None - SANS_SERIF: "Typeface" = None - SERIF: "Typeface" = None - - NORMAL = 0 - BOLD = 1 - ITALIC = 2 - BOLD_ITALIC = 3 - - def __init__(self, family_name: str = "sans-serif", style: int = NORMAL): - super().__init__() - self._family_name = family_name - self._style = style - self._weight = 400 - if style in (Typeface.BOLD, Typeface.BOLD_ITALIC): - self._weight = 700 - - @staticmethod - def defaultFromStyle(style: int) -> "Typeface": - """从样式获取默认字体""" - names = {0: "sans-serif", 1: "sans-serif", 2: "serif", 3: "serif"} - return Typeface(names.get(style, "sans-serif"), style) - - @staticmethod - def create(family_name: str, style: int) -> "Typeface": - """创建字体""" - return Typeface(family_name, style) - - @staticmethod - def load(file_path: str, style: int = NORMAL) -> "Typeface": - """从文件加载字体""" - return Typeface("custom", style) - - def getStyle(self) -> int: - """获取字体样式""" - return self._style - - def isBold(self) -> bool: - """检查是否粗体""" - return self._style in (Typeface.BOLD, Typeface.BOLD_ITALIC) - - def isItalic(self) -> bool: - """检查是否斜体""" - return self._style in (Typeface.ITALIC, Typeface.BOLD_ITALIC) - - def getWeight(self) -> int: - """获取字重""" - return self._weight - - @staticmethod - def getSystemFontMap() -> Dict[str, "Typeface"]: - """获取系统字体映射""" - return { - "sans-serif": Typeface.SANS_SERIF, - "serif": Typeface.SERIF, - "monospace": Typeface.MONOSPACE, - } - - @staticmethod - def getSystemFontSettings(weight: int, italic: bool) -> Optional[Font]: - """获取系统字体设置""" - return Font(weight=weight, italic=italic) - - @staticmethod - def setSystemFontSettings(font: Font) -> None: - """设置系统字体设置""" - pass - - def toString(self) -> str: - return f"Typeface({self._family_name}, style={self._style})" - - -# Initialize Typeface constants -Typeface.DEFAULT = Typeface("sans-serif", Typeface.NORMAL) -Typeface.DEFAULT_BOLD = Typeface("sans-serif", Typeface.BOLD) -Typeface.MONOSPACE = Typeface("monospace", Typeface.NORMAL) -Typeface.SANS_SERIF = Typeface("sans-serif", Typeface.NORMAL) -Typeface.SERIF = Typeface("serif", Typeface.NORMAL) \ No newline at end of file diff --git a/android_pyapi/android/graphics_pdf.py b/android_pyapi/android/graphics_pdf.py deleted file mode 100644 index ea06db5..0000000 --- a/android_pyapi/android/graphics_pdf.py +++ /dev/null @@ -1,335 +0,0 @@ -""" -Android Graphics PDF 组件 — PdfDocument, PdfRenderer, ParcelFileDescriptor -方法签名完全对齐 Android SDK -""" - -import os -import io -import tempfile -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError, android_context, HAS_PIL, HAS_PYMUPDF, HAS_PDF2IMAGE, resolve_android_path - - -# ═══════════════════════════════════════════════════════════════ -# ParcelFileDescriptor -# ═══════════════════════════════════════════════════════════════ - -class ParcelFileDescriptor(Stub): - """ParcelFileDescriptor — 文件描述符""" - - MODE_READ_ONLY = 0x10000000 - MODE_WRITE_ONLY = 0x20000000 - MODE_READ_WRITE = 0x30000000 - MODE_CREATE = 0x08000000 - MODE_TRUNCATE = 0x04000000 - MODE_APPEND = 0x02000000 - - def __init__(self, fd: int = -1, file_path: str = ""): - super().__init__() - self._fd = fd - self._file_path = file_path - self._closed = False - self._file_obj = None - if file_path and fd < 0: - resolved = resolve_android_path(file_path) - self._file_path = resolved - - @staticmethod - def open(file_path: str, mode: int) -> "ParcelFileDescriptor": - resolved = resolve_android_path(file_path) - fmode = "rb" - if mode & ParcelFileDescriptor.MODE_WRITE_ONLY: - fmode = "wb" - elif mode & ParcelFileDescriptor.MODE_READ_WRITE: - fmode = "r+b" - if mode & ParcelFileDescriptor.MODE_CREATE: - if not os.path.exists(os.path.dirname(resolved)): - os.makedirs(os.path.dirname(resolved), exist_ok=True) - if not os.path.exists(resolved): - open(resolved, "w").close() - return ParcelFileDescriptor(-1, file_path) - - @staticmethod - def parseFromFd(fd: int) -> "ParcelFileDescriptor": - return ParcelFileDescriptor(fd) - - @staticmethod - def parseFromString(path: str) -> "ParcelFileDescriptor": - resolved = resolve_android_path(path) - return ParcelFileDescriptor(-1, resolved) - - @staticmethod - def adoptFd(fd: int) -> "ParcelFileDescriptor": - return ParcelFileDescriptor(fd) - - @staticmethod - def createPipe() -> List["ParcelFileDescriptor"]: - r, w = os.pipe() - return [ParcelFileDescriptor(r), ParcelFileDescriptor(w)] - - def getFileDescriptor(self) -> int: - return self._fd - - def getStatSize(self) -> int: - if self._file_path and os.path.exists(self._file_path): - return os.path.getsize(self._file_path) - return 0 - - def getFd(self) -> int: - return self._fd - - def close(self): - if not self._closed: - self._closed = True - if self._file_obj: - self._file_obj.close() - elif self._fd >= 0: - try: - os.close(self._fd) - except OSError: - pass - - def getFilePath(self) -> str: - return self._file_path - - def toString(self) -> str: - return f"ParcelFileDescriptor(fd={self._fd})" - - -# ═══════════════════════════════════════════════════════════════ -# PdfDocument -# ═══════════════════════════════════════════════════════════════ - -class PdfDocument(Stub): - """PdfDocument — PDF 文档""" - - class Page(Stub): - """PdfDocument.Page — PDF 页面""" - - def __init__(self, page_number: int, width: int, height: int): - super().__init__() - self._page_number = page_number - self._width = width - self._height = height - self._canvas = None - - def getCanvas(self) -> "Canvas": - from .graphics import Canvas - if self._canvas is None: - self._canvas = Canvas(self._width, self._height) - return self._canvas - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def start(self): - pass - - def finish(self): - pass - - def __init__(self): - super().__init__() - self._pages: List[PdfDocument.Page] = [] - - def startPage(self, page_size: "Page") -> "Canvas": - width = page_size.getWidth() if hasattr(page_size, "getWidth") else 595 - height = page_size.getHeight() if hasattr(page_size, "getHeight") else 842 - page = PdfDocument.Page(len(self._pages), width, height) - self._pages.append(page) - return page.getCanvas() - - def finishPage(self, page: "Page"): - page.finish() - - def getPages(self) -> List["Page"]: - return list(self._pages) - - def writeTo(self, out_stream, close: bool = True): - if HAS_PYMUPDF: - import fitz - doc = fitz.open() - for p in self._pages: - page = doc.new_page(width=p._width, height=p._height) - if HAS_PIL and hasattr(p._canvas, "_image") and p._canvas._image: - img_bytes = io.BytesIO() - p._canvas._image.save(img_bytes, format="PNG") - img_bytes.seek(0) - rect = page.rect - page.insert_image(rect, stream=img_bytes.read()) - doc.save(out_stream) - doc.close() - elif HAS_PIL: - from PIL import Image - images = [] - for p in self._pages: - if hasattr(p._canvas, "_image") and p._canvas._image: - images.append(p._canvas._image.convert("RGB")) - if images: - images[0].save(out_stream, format="PDF", save_all=True, - append_images=images[1:]) - else: - blank = Image.new("RGB", (595, 842), (255, 255, 255)) - blank.save(out_stream, format="PDF") - else: - if close: - out_stream.close() - - def close(self): - self._pages.clear() - - def __enter__(self): - return self - - def __exit__(self, *args): - self.close() - - -# ═══════════════════════════════════════════════════════════════ -# PdfRenderer -# ═══════════════════════════════════════════════════════════════ - -class PdfRenderer(Stub): - """PdfRenderer — PDF 渲染器""" - - RENDER_MODE_FOR_DISPLAY = 1 - RENDER_MODE_FOR_PRINT = 2 - RENDER_MODE_FOR_ALL = 3 - - class Page(Stub): - """PdfRenderer.Page — PDF 渲染页面""" - - def __init__(self, renderer: "PdfRenderer", index: int): - super().__init__() - self._renderer = renderer - self._index = index - self._opened = True - if renderer._doc: - self._fitz_page = renderer._doc[index] - rect = self._fitz_page.rect - self._width = int(rect.width) - self._height = int(rect.height) - else: - self._fitz_page = None - self._width = 595 - self._height = 842 - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def getIndex(self) -> int: - return self._index - - def render(self, canvas: "Canvas", destLeft: int, destTop: int, - destRight: int, destBottom: int, flags: int = 0): - if not self._opened: - return - if HAS_PYMUPDF and self._fitz_page: - from .graphics import Bitmap - mat = fitz.Matrix(2, 2) # 2x zoom for quality - pix = self._fitz_page.get_pixmap(matrix=mat) - img_data = pix.tobytes("png") - if HAS_PIL and hasattr(canvas, "_image") and canvas._image: - from PIL import Image - import io as _io - img = Image.open(io.BytesIO(img_data)) - img = img.resize((destRight - destLeft, destBottom - destTop)) - canvas._image.paste(img, (destLeft, destTop)) - elif HAS_PDF2IMAGE and HAS_PIL and hasattr(canvas, "_image"): - from pdf2image import convert_from_bytes - if self._renderer._pdf_bytes: - images = convert_from_bytes( - self._renderer._pdf_bytes, - first_page=self._index + 1, - last_page=self._index + 1 - ) - if images: - img = images[0].resize((destRight - destLeft, destBottom - destTop)) - canvas._image.paste(img.convert("RGBA"), (destLeft, destTop)) - - def start(self): - self._opened = True - - def finish(self): - self._opened = False - - def close(self): - self._opened = False - - def __init__(self, pfd: ParcelFileDescriptor, closePfd: bool = True): - super().__init__() - self._pfd = pfd - self._close_pfd = closePfd - self._page_count = 0 - self._pdf_bytes = None - self._doc = None - - file_path = pfd.getFilePath() - if file_path and os.path.exists(file_path): - with open(file_path, "rb") as f: - self._pdf_bytes = f.read() - elif pfd._fd >= 0: - try: - self._pdf_bytes = os.read(pfd._fd, 10 * 1024 * 1024) - except OSError: - self._pdf_bytes = None - - if HAS_PYMUPDF and self._pdf_bytes: - import fitz - self._doc = fitz.open(stream=self._pdf_bytes, filetype="pdf") - self._page_count = len(self._doc) - elif self._pdf_bytes: - self._page_count = 1 # fallback - else: - self._page_count = 0 - - def openPage(self, index: int) -> "Page": - if 0 <= index < self._page_count: - return PdfRenderer.Page(self, index) - raise IndexError(f"Page index {index} out of range (0-{self._page_count - 1})") - - def close(self): - if self._doc and HAS_PYMUPDF: - self._doc.close() - self._doc = None - if self._close_pfd and self._pfd: - self._pfd.close() - self._pdf_bytes = None - - def getPageCount(self) -> int: - return self._page_count - - def shouldScale(self) -> bool: - return False - - def __enter__(self): - return self - - def __exit__(self, *args): - self.close() - - -# ═══════════════════════════════════════════════════════════════ -# PdfDocumentPage (alias for PdfDocument.Page) -# ═══════════════════════════════════════════════════════════════ - -PdfDocumentPage = PdfDocument.Page - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "PdfDocument", - "PdfDocumentPage", - "PdfRenderer", - "ParcelFileDescriptor", -] \ No newline at end of file diff --git a/android_pyapi/android/graphics_surface.py b/android_pyapi/android/graphics_surface.py deleted file mode 100644 index dd10992..0000000 --- a/android_pyapi/android/graphics_surface.py +++ /dev/null @@ -1,699 +0,0 @@ -""" -Android Graphics Surface 组件 — SurfaceTexture, SurfaceHolder, SurfaceView, GLSurfaceView -方法签名完全对齐 Android SDK (android.graphics.surface 包 和 android.view.Surface) -""" - -from __future__ import annotations - -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# SurfaceTexture -# ═══════════════════════════════════════════════════════════════ - -class SurfaceTexture(Stub): - """SurfaceTexture — 表面纹理""" - - # 纹理目标常量 - TEXTURE_TARGET_2D = 3553 - TEXTURE_TARGET_EXTERNAL = 36197 - - # Transform 矩阵常量 - TRANSFORM_MATRIX_SIZE = 16 - - def __init__(self, tex_name: int = 0, allow_high_performance: bool = True): - super().__init__() - self._tex_name = tex_name - self._tex_target: int = SurfaceTexture.TEXTURE_TARGET_EXTERNAL - self._timestamp: int = 0 - self._frame_available_listener: Optional[Callable] = None - self._frame_available_listener_handler: Any = None - self._default_buffer_size: Tuple[int, int] = (0, 0) - self._is_abandoned: bool = False - self._is_released: bool = False - self._transform_matrix: List[float] = [ - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0, - ] - self._buffer_count: int = 0 - self._frame_count: int = 0 - self._width: int = 0 - self._height: int = 0 - - def getTexName(self) -> int: - return self._tex_name - - def getTimestamp(self) -> int: - """获取时间戳 (纳秒)""" - return self._timestamp - - def updateTexImage(self): - """更新纹理图像""" - self._timestamp = int(time.time() * 1000000000) - self._frame_count += 1 - - def getTransformMatrix(self, matrix: Optional[List[float]] = None) -> List[float]: - """获取变换矩阵""" - if matrix is not None and len(matrix) >= 16: - for i in range(16): - matrix[i] = self._transform_matrix[i] - return list(self._transform_matrix) - - def setDefaultBufferSize(self, width: int, height: int): - """设置默认缓冲区大小""" - self._default_buffer_size = (width, height) - self._width = width - self._height = height - - def getDefaultBufferSize(self) -> Tuple[int, int]: - return self._default_buffer_size - - def setOnFrameAvailableListener(self, listener: Optional[Callable], - handler: Any = None): - """设置帧可用监听器""" - self._frame_available_listener = listener - self._frame_available_listener_handler = handler - if listener: - listener(self) - - def getOnFrameAvailableListener(self) -> Optional[Callable]: - return self._frame_available_listener - - def detachFromGLContext(self): - """从 GL 上下文分离""" - pass - - def attachToGLContext(self, tex_name: int): - """附加到 GL 上下文""" - self._tex_name = tex_name - - def release(self): - """释放表面纹理""" - self._is_released = True - self._frame_available_listener = None - - def releaseTexImage(self): - """释放纹理图像""" - pass - - def abandon(self): - """废弃表面纹理""" - self._is_abandoned = True - self._frame_available_listener = None - - def isReleased(self) -> bool: - return self._is_released - - def isAbandoned(self) -> bool: - return self._is_abandoned - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def getFrameCount(self) -> int: - return self._frame_count - - def getBufferCount(self) -> int: - return self._buffer_count - - def toString(self) -> str: - return f"SurfaceTexture(tex={self._tex_name}, {self._width}x{self._height})" - - -# ═══════════════════════════════════════════════════════════════ -# SurfaceHolder -# ═══════════════════════════════════════════════════════════════ - -class SurfaceHolder(Stub): - """SurfaceHolder — 表面持有者""" - - SURFACE_TYPE_NORMAL = 0 - SURFACE_TYPE_HARDWARE = 1 - SURFACE_TYPE_GPU = 2 - SURFACE_TYPE_PUSH_BUFFERS = 3 - - # 表面变更类型 - CHANGE_TYPE_NONE = 0 - CHANGE_TYPE_SIZE_CHANGED = 1 - CHANGE_TYPE_FORMAT_CHANGED = 2 - CHANGE_TYPE_SURFACE_CREATED = 3 - CHANGE_TYPE_SURFACE_DESTROYED = 4 - CHANGE_TYPE_SURFACE_REDRAW = 5 - - class Callback(Stub): - """SurfaceHolder.Callback — 表面回调""" - - def surfaceCreated(self, holder: SurfaceHolder): - pass - - def surfaceChanged(self, holder: SurfaceHolder, format: int, - width: int, height: int): - pass - - def surfaceDestroyed(self, holder: SurfaceHolder): - pass - - def surfaceRedrawNeeded(self, holder: SurfaceHolder): - pass - - class Callback2(Callback): - """SurfaceHolder.Callback2 — 表面回调 (扩展)""" - - def surfaceRedrawNeeded(self, holder: SurfaceHolder): - pass - - def __init__(self, surface: Any = None): - super().__init__() - self._surface = surface - self._is_creating: bool = False - self._type: int = SurfaceHolder.SURFACE_TYPE_NORMAL - self._width: int = 0 - self._height: int = 0 - self._format: int = 0 - self._callbacks: List[SurfaceHolder.Callback] = [] - self._callback2: Optional[SurfaceHolder.Callback2] = None - self._fixed_size: Tuple[int, int] = (-1, -1) - self._keep_screen_on: bool = False - - def getSurface(self) -> Any: - return self._surface - - def setSurface(self, surface: Any): - self._surface = surface - - def addCallback(self, callback: Callback): - if callback not in self._callbacks: - self._callbacks.append(callback) - - def removeCallback(self, callback: Callback): - self._callbacks = [c for c in self._callbacks if c is not callback] - - def getCallbacks(self) -> List[Callback]: - return list(self._callbacks) - - def setType(self, type_: int): - self._type = type_ - - def getType(self) -> int: - return self._type - - def setFixedSize(self, width: int, height: int): - self._fixed_size = (width, height) - self._width = width - self._height = height - - def getFixedSize(self) -> Tuple[int, int]: - return self._fixed_size - - def setSizeFromLayout(self): - """从布局获取大小""" - pass - - def isCreating(self) -> bool: - return self._is_creating - - def setCreating(self, creating: bool): - self._is_creating = creating - if creating: - for cb in self._callbacks: - cb.surfaceCreated(self) - - def setFormat(self, format: int): - self._format = format - - def getFormat(self) -> int: - return self._format - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def setKeepScreenOn(self, keep_on: bool): - self._keep_screen_on = keep_on - - def isKeepScreenOn(self) -> bool: - return self._keep_screen_on - - def lockCanvas(self) -> Any: - """锁定画布""" - return None - - def lockCanvas(Rect: Any) -> Any: - """锁定画布指定区域""" - return None - - def unlockCanvasAndPost(self, canvas: Any): - """解锁画布并提交""" - pass - - def notifySurfaceCreated(self): - for cb in self._callbacks: - cb.surfaceCreated(self) - - def notifySurfaceChanged(self, format: int, width: int, height: int): - for cb in self._callbacks: - cb.surfaceChanged(self, format, width, height) - - def notifySurfaceDestroyed(self): - for cb in self._callbacks: - cb.surfaceDestroyed(self) - - def toString(self) -> str: - return f"SurfaceHolder({self._width}x{self._height}, type={self._type})" - - -# ═══════════════════════════════════════════════════════════════ -# SurfaceView -# ═══════════════════════════════════════════════════════════════ - -class SurfaceView(Stub): - """SurfaceView — 表面视图""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._holder: Optional[SurfaceHolder] = SurfaceHolder() - self._is_visible: bool = False - self._is_created: bool = False - self._width: int = 0 - self._height: int = 0 - self._format: int = 0 - self._z_order: int = 0 - self._is_securable: bool = False - self._transparent_region: Optional[Any] = None - self._surface_frame: Optional[Any] = None - self._window_token: Any = None - - def getHolder(self) -> SurfaceHolder: - return self._holder - - def setZOrderOnTop(self, on_top: bool): - """设置 Z 轴顺序在顶层""" - self._z_order = 1 if on_top else 0 - - def setZOrderMediaOverlay(self, is_media_overlay: bool): - """设置媒体覆盖 Z 轴顺序""" - self._z_order = 2 if is_media_overlay else 0 - - def setVisibility(self, visible: bool): - self._is_visible = visible - - def isVisible(self) -> bool: - return self._is_visible - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def setSecure(self, secure: bool): - self._is_securable = secure - - def isSecure(self) -> bool: - return self._is_securable - - def getContext(self) -> Any: - return self._context - - def getWindowToken(self) -> Any: - return self._window_token - - def setWindowToken(self, token: Any): - self._window_token = token - - def onWindowVisibilityChanged(self, visibility: int): - """窗口可见性变化回调""" - pass - - def onMeasure(self, width_measure_spec: int, height_measure_spec: int): - """测量回调""" - pass - - def onLayout(self, changed: bool, left: int, top: int, - right: int, bottom: int): - """布局回调""" - self._width = right - left - self._height = bottom - top - if self._holder: - self._holder.notifySurfaceChanged(self._format, - self._width, self._height) - - def draw(self, canvas: Any): - """绘制""" - pass - - def toString(self) -> str: - return f"SurfaceView({self._width}x{self._height}, visible={self._is_visible})" - - -# ═══════════════════════════════════════════════════════════════ -# GLSurfaceView -# ═══════════════════════════════════════════════════════════════ - -class GLSurfaceView(SurfaceView): - """GLSurfaceView — OpenGL ES 表面视图""" - - RENDERMODE_CONTINUOUSLY = 1 - RENDERMODE_WHEN_DIRTY = 0 - - DEBUG_CHECK_GL_ERROR = 1 - DEBUG_LOG_GL_CALLS = 2 - - class Renderer(Stub): - """GLSurfaceView.Renderer — OpenGL 渲染器""" - - def onSurfaceCreated(self, gl: Any, config: Any): - pass - - def onSurfaceChanged(self, gl: Any, width: int, height: int): - pass - - def onDrawFrame(self, gl: Any): - pass - - def __init__(self, context: Any = None): - super().__init__(context) - self._renderer: Optional[GLSurfaceView.Renderer] = None - self._render_mode: int = GLSurfaceView.RENDERMODE_CONTINUOUSLY - self._egl_context_client_version: int = 2 - self._preserve_egl_context_on_pause: bool = False - self._debug_flags: int = 0 - self._is_running: bool = False - self._is_paused: bool = False - self._frame_count: int = 0 - self._request_render: bool = False - - def setRenderer(self, renderer: Renderer): - """设置渲染器""" - self._renderer = renderer - - def getRenderer(self) -> Optional[Renderer]: - return self._renderer - - def setRenderMode(self, mode: int): - """设置渲染模式""" - self._render_mode = mode - - def getRenderMode(self) -> int: - return self._render_mode - - def requestRender(self): - """请求渲染""" - self._request_render = True - if self._render_mode == GLSurfaceView.RENDERMODE_WHEN_DIRTY: - self._frame_count += 1 - - def queueEvent(self, r: Callable): - """队列事件""" - r() - - def onPause(self): - """暂停""" - self._is_paused = True - self._is_running = False - - def onResume(self): - """恢复""" - self._is_paused = False - self._is_running = True - - def setPreserveEGLContextOnPause(self, preserve: bool): - self._preserve_egl_context_on_pause = preserve - - def getPreserveEGLContextOnPause(self) -> bool: - return self._preserve_egl_context_on_pause - - def setEGLContextClientVersion(self, version: int): - self._egl_context_client_version = version - - def getEGLContextClientVersion(self) -> int: - return self._egl_context_client_version - - def setDebugFlags(self, flags: int): - self._debug_flags = flags - - def getDebugFlags(self) -> int: - return self._debug_flags - - def isRunning(self) -> bool: - return self._is_running - - def isPaused(self) -> bool: - return self._is_paused - - def getFrameCount(self) -> int: - return self._frame_count - - def onSurfaceCreated(self, gl: Any, config: Any): - if self._renderer: - self._renderer.onSurfaceCreated(gl, config) - - def onSurfaceChanged(self, gl: Any, width: int, height: int): - if self._renderer: - self._renderer.onSurfaceChanged(gl, width, height) - - def onDrawFrame(self, gl: Any): - if self._renderer: - self._renderer.onDrawFrame(gl) - - def destroyDrawingCache(self): - pass - - def toString(self) -> str: - return f"GLSurfaceView(renderMode={self._render_mode}, running={self._is_running})" - - -# ═══════════════════════════════════════════════════════════════ -# Surface (helper) -# ═══════════════════════════════════════════════════════════════ - -class Surface(Stub): - """Surface — 表面 (android.view.Surface)""" - - ROTATION_0 = 0 - ROTATION_90 = 1 - ROTATION_180 = 2 - ROTATION_270 = 3 - - SCALING_MODE_FREEZE = 0 - SCALING_MODE_SCALE_TO_WINDOW = 1 - SCALING_MODE_SCALE_CROP = 2 - SCALING_MODE_NO_SCALE_CROP = 3 - - def __init__(self, surface_texture: Optional[SurfaceTexture] = None): - super().__init__() - self._surface_texture = surface_texture - self._is_valid: bool = True - self._is_released: bool = False - self._width: int = 0 - self._height: int = 0 - self._format: int = 0 - self._name: str = "" - - def isValid(self) -> bool: - return self._is_valid and not self._is_released - - def isReleased(self) -> bool: - return self._is_released - - def release(self): - self._is_released = True - - def getSurfaceTexture(self) -> Optional[SurfaceTexture]: - return self._surface_texture - - def setSurfaceTexture(self, st: SurfaceTexture): - self._surface_texture = st - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def setWidth(self, width: int): - self._width = width - - def setHeight(self, height: int): - self._height = height - - def getFormat(self) -> int: - return self._format - - def setName(self, name: str): - self._name = name - - def getName(self) -> str: - return self._name - - def lockCanvas(self, in_out_dirty_rect: Any = None) -> Any: - """锁定画布""" - if self._is_released: - return None - return None - - def unlockCanvasAndPost(self, canvas: Any): - """解锁画布并提交""" - pass - - def toString(self) -> str: - return f"Surface(name={self._name}, valid={self._is_valid}, released={self._is_released})" - - -# ═══════════════════════════════════════════════════════════════ -# SurfaceControl (helper) -# ═══════════════════════════════════════════════════════════════ - -class SurfaceControl(Stub): - """SurfaceControl — 表面控制""" - - BUFFER_TRANSFORM_IDENTITY = 0 - BUFFER_TRANSFORM_MIRROR_HORIZONTAL = 1 - BUFFER_TRANSFORM_MIRROR_VERTICAL = 2 - BUFFER_TRANSFORM_ROTATE_90 = 4 - BUFFER_TRANSFORM_ROTATE_180 = 3 - BUFFER_TRANSFORM_ROTATE_270 = 7 - - def __init__(self, surface: Optional[Surface] = None): - super().__init__() - self._surface = surface - self._alpha: float = 1.0 - self._layer: int = 0 - self._z_order: int = 0 - self._is_visible: bool = True - self._transform: int = SurfaceControl.BUFFER_TRANSFORM_IDENTITY - self._crop: Tuple[int, int, int, int] = (0, 0, 0, 0) - - def getSurface(self) -> Optional[Surface]: - return self._surface - - def setSurface(self, surface: Surface): - self._surface = surface - - def setAlpha(self, alpha: float): - self._alpha = max(0.0, min(1.0, alpha)) - - def getAlpha(self) -> float: - return self._alpha - - def setLayer(self, layer: int): - self._layer = layer - - def getLayer(self) -> int: - return self._layer - - def setVisibility(self, visible: bool): - self._is_visible = visible - - def isVisible(self) -> bool: - return self._is_visible - - def setTransform(self, transform: int): - self._transform = transform - - def getTransform(self) -> int: - return self._transform - - def setCrop(self, left: int, top: int, right: int, bottom: int): - self._crop = (left, top, right, bottom) - - def getCrop(self) -> Tuple[int, int, int, int]: - return self._crop - - def release(self): - if self._surface: - self._surface.release() - - def toString(self) -> str: - return f"SurfaceControl(layer={self._layer}, alpha={self._alpha})" - - -# ═══════════════════════════════════════════════════════════════ -# SurfaceUtils -# ═══════════════════════════════════════════════════════════════ - -class SurfaceUtils(Stub): - """SurfaceUtils — 表面工具类""" - - @staticmethod - def createSurfaceFromTexture(texture: SurfaceTexture) -> Surface: - return Surface(texture) - - @staticmethod - def createSurfaceTexture(tex_name: int = 0) -> SurfaceTexture: - return SurfaceTexture(tex_name) - - @staticmethod - def createGLSurfaceView(context: Any) -> GLSurfaceView: - return GLSurfaceView(context) - - @staticmethod - def createSurfaceView(context: Any) -> SurfaceView: - return SurfaceView(context) - - @staticmethod - def createSurfaceHolder(surface: Any) -> SurfaceHolder: - return SurfaceHolder(surface) - - @staticmethod - def isSurfaceValid(surface: Surface) -> bool: - return surface is not None and surface.isValid() - - @staticmethod - def releaseSurface(surface: Surface): - if surface: - surface.release() - - @staticmethod - def releaseSurfaceTexture(texture: SurfaceTexture): - if texture: - texture.release() - - @staticmethod - def getDefaultRenderer(context: Any) -> GLSurfaceView.Renderer: - class DefaultRenderer(GLSurfaceView.Renderer): - def onSurfaceCreated(self, gl, config): - pass - def onSurfaceChanged(self, gl, width, height): - pass - def onDrawFrame(self, gl): - pass - return DefaultRenderer() - - @staticmethod - def createSimpleRenderer() -> GLSurfaceView.Renderer: - class SimpleRenderer(GLSurfaceView.Renderer): - def __init__(self): - super().__init__() - self._background_color: Tuple[float, float, float, float] = (0.0, 0.0, 0.0, 1.0) - - def onSurfaceCreated(self, gl, config): - pass - - def onSurfaceChanged(self, gl, width, height): - pass - - def onDrawFrame(self, gl): - pass - - def setBackgroundColor(self, r: float, g: float, b: float, a: float): - self._background_color = (r, g, b, a) - - return SimpleRenderer() - - @staticmethod - def getSurfaceRotationDegrees(rotation: int) -> int: - return rotation * 90 \ No newline at end of file diff --git a/android_pyapi/android/graphics_text.py b/android_pyapi/android/graphics_text.py deleted file mode 100644 index f59d88e..0000000 --- a/android_pyapi/android/graphics_text.py +++ /dev/null @@ -1,779 +0,0 @@ -""" -Android Graphics Text 组件 — TextRunShaper, LineBreaker, MeasuredText, PositionedGlyphs, TextLayout 等 -方法签名完全对齐 Android SDK (android.graphics.text 包) -""" - -from __future__ import annotations - -import math -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# PositionedGlyphs -# ═══════════════════════════════════════════════════════════════ - -class PositionedGlyphs(Stub): - """PositionedGlyphs — 定位字形""" - - def __init__(self): - super().__init__() - self._glyph_ids: List[int] = [] - self._positions_x: List[float] = [] - self._positions_y: List[float] = [] - self._advances_x: List[float] = [] - self._advances_y: List[float] = [] - self._width: float = 0.0 - self._height: float = 0.0 - self._ascent: float = 0.0 - self._descent: float = 0.0 - self._glyph_count: int = 0 - - def getGlyphCount(self) -> int: - return self._glyph_count - - def getGlyphId(self, index: int) -> int: - return self._glyph_ids[index] if 0 <= index < len(self._glyph_ids) else 0 - - def getGlyphX(self, index: int) -> float: - return self._positions_x[index] if 0 <= index < len(self._positions_x) else 0.0 - - def getGlyphY(self, index: int) -> float: - return self._positions_y[index] if 0 <= index < len(self._positions_y) else 0.0 - - def getGlyphAdvanceX(self, index: int) -> float: - return self._advances_x[index] if 0 <= index < len(self._advances_x) else 0.0 - - def getGlyphAdvanceY(self, index: int) -> float: - return self._advances_y[index] if 0 <= index < len(self._advances_y) else 0.0 - - def getWidth(self) -> float: - return self._width - - def getHeight(self) -> float: - return self._height - - def getAscent(self) -> float: - return self._ascent - - def getDescent(self) -> float: - return self._descent - - def addGlyph(self, glyph_id: int, x: float, y: float, - advance_x: float, advance_y: float): - self._glyph_ids.append(glyph_id) - self._positions_x.append(x) - self._positions_y.append(y) - self._advances_x.append(advance_x) - self._advances_y.append(advance_y) - self._glyph_count += 1 - - def setBounds(self, width: float, height: float): - self._width = width - self._height = height - - def setMetrics(self, ascent: float, descent: float): - self._ascent = ascent - self._descent = descent - - def clear(self): - self._glyph_ids.clear() - self._positions_x.clear() - self._positions_y.clear() - self._advances_x.clear() - self._advances_y.clear() - self._glyph_count = 0 - self._width = 0.0 - self._height = 0.0 - self._ascent = 0.0 - self._descent = 0.0 - - def toString(self) -> str: - return f"PositionedGlyphs(count={self._glyph_count}, w={self._width:.1f}, h={self._height:.1f})" - - -# ═══════════════════════════════════════════════════════════════ -# TextRunShaper -# ═══════════════════════════════════════════════════════════════ - -class TextRunShaper(Stub): - """TextRunShaper — 文本运行重塑器""" - - def __init__(self): - super().__init__() - - @staticmethod - def shapeTextRun(text: str, start: int, count: int, context_start: int, - context_count: int, x_offset: float, y_offset: float, - is_rtl: bool, paint: Any) -> PositionedGlyphs: - """重塑文本运行""" - glyphs = PositionedGlyphs() - for i in range(count): - if start + i < len(text): - char_code = ord(text[start + i]) - glyphs.addGlyph(char_code, x_offset + i * 12.0, y_offset, 12.0, 0.0) - glyphs.setBounds(count * 12.0, 16.0) - glyphs.setMetrics(-12.0, 3.0) - return glyphs - - @staticmethod - def shapeTextRunWithFont(text: str, start: int, count: int, - context_start: int, context_count: int, - x_offset: float, y_offset: float, - is_rtl: bool, paint: Any, - font: Any) -> PositionedGlyphs: - """使用指定字体重塑文本运行""" - return TextRunShaper.shapeTextRun(text, start, count, context_start, - context_count, x_offset, y_offset, - is_rtl, paint) - - @staticmethod - def shapeTextRunOnPath(text: str, start: int, count: int, - path: Any, paint: Any) -> PositionedGlyphs: - """沿路径重塑文本运行""" - glyphs = PositionedGlyphs() - for i in range(count): - if start + i < len(text): - char_code = ord(text[start + i]) - glyphs.addGlyph(char_code, i * 12.0, 0.0, 12.0, 0.0) - glyphs.setBounds(count * 12.0, 16.0) - glyphs.setMetrics(-12.0, 3.0) - return glyphs - - @staticmethod - def getGlyphBounds(glyphs: PositionedGlyphs, index: int) -> Tuple[float, float, float, float]: - """获取字形边界""" - x = glyphs.getGlyphX(index) - y = glyphs.getGlyphY(index) - return (x, y, x + 12.0, y + 16.0) - - -# ═══════════════════════════════════════════════════════════════ -# LineBreaker -# ═══════════════════════════════════════════════════════════════ - -class LineBreaker(Stub): - """LineBreaker — 换行器""" - - BREAK_STRATEGY_SIMPLE = 0 - BREAK_STRATEGY_HIGH_QUALITY = 1 - BREAK_STRATEGY_BALANCED = 2 - - HYPHENATION_FREQUENCY_NONE = 0 - HYPHENATION_FREQUENCY_NORMAL = 1 - HYPHENATION_FREQUENCY_FULL = 2 - - WORD_BREAK_NORMAL = 0 - WORD_BREAK_BREAK_ALL = 1 - WORD_BREAK_KEEP_ALL = 2 - - TAB_STRATEGY_NONE = 0 - TAB_STRATEGY_ORIGIN = 1 - TAB_STRATEGY_DESTINATION = 2 - - JUSTIFICATION_MODE_NONE = 0 - JUSTIFICATION_MODE_INTER_WORD = 1 - JUSTIFICATION_MODE_CHARACTER = 2 - - LINE_LIMIT_UNLIMITED = 0 - - class Result(Stub): - """LineBreaker.Result — 换行结果""" - - def __init__(self): - super().__init__() - self._line_counts: int = 0 - self._line_breaks: List[Tuple[int, float]] = [] - self._line_widths: List[float] = [] - self._line_ascent: List[float] = [] - self._line_descent: List[float] = [] - - def getLineCount(self) -> int: - return self._line_counts - - def getLineBreakOffset(self, line_index: int) -> int: - if 0 <= line_index < len(self._line_breaks): - return self._line_breaks[line_index][0] - return 0 - - def getLineWidth(self, line_index: int) -> float: - if 0 <= line_index < len(self._line_widths): - return self._line_widths[line_index] - return 0.0 - - def getLineAscent(self, line_index: int) -> float: - if 0 <= line_index < len(self._line_ascent): - return self._line_ascent[line_index] - return 0.0 - - def getLineDescent(self, line_index: int) -> float: - if 0 <= line_index < len(self._line_descent): - return self._line_descent[line_index] - return 0.0 - - def addLineBreak(self, offset: int, width: float, ascent: float, descent: float): - self._line_breaks.append((offset, width)) - self._line_widths.append(width) - self._line_ascent.append(ascent) - self._line_descent.append(descent) - self._line_counts += 1 - - def toString(self) -> str: - return f"LineBreaker.Result(lines={self._line_counts})" - - def __init__(self, strategy: int = BREAK_STRATEGY_SIMPLE, - hyphenation: int = HYPHENATION_FREQUENCY_NONE): - super().__init__() - self._strategy = strategy - self._hyphenation_frequency = hyphenation - self._justification_mode: int = LineBreaker.JUSTIFICATION_MODE_NONE - self._word_break: int = LineBreaker.WORD_BREAK_NORMAL - self._tab_strategy: int = LineBreaker.TAB_STRATEGY_NONE - self._line_limit: int = LineBreaker.LINE_LIMIT_UNLIMITED - - def getStrategy(self) -> int: - return self._strategy - - def setStrategy(self, strategy: int): - self._strategy = strategy - - def getHyphenationFrequency(self) -> int: - return self._hyphenation_frequency - - def setHyphenationFrequency(self, freq: int): - self._hyphenation_frequency = freq - - def setJustificationMode(self, mode: int): - self._justification_mode = mode - - def getJustificationMode(self) -> int: - return self._justification_mode - - def setWordBreak(self, word_break: int): - self._word_break = word_break - - def getWordBreak(self) -> int: - return self._word_break - - def setTabStrategy(self, strategy: int): - self._tab_strategy = strategy - - def getTabStrategy(self) -> int: - return self._tab_strategy - - def setLineLimit(self, limit: int): - self._line_limit = limit - - def getLineLimit(self) -> int: - return self._line_limit - - def computeLineBreaks(self, text: str, width: float, - paint: Any = None) -> Result: - """计算换行""" - result = LineBreaker.Result() - if not text: - return result - words = text.split(" ") - line_start = 0 - line_width = 0.0 - char_width = 12.0 # 粗略估计 - for word in words: - word_len = len(word) * char_width - if word_len + line_width > width and len(text[line_start:text.index(word)]) > 0: - break_offset = text.index(word, line_start) if line_start > 0 else len(word) - result.addLineBreak(break_offset, line_width, -12.0, 3.0) - line_start = break_offset - line_width = word_len - else: - line_width += word_len + char_width - if line_start < len(text): - result.addLineBreak(len(text), line_width, -12.0, 3.0) - return result - - def computeLineBreaksForMeasuredText(self, measured_text: MeasuredText, - width: float) -> Result: - """对度量文本计算换行""" - return self.computeLineBreaks(measured_text.getText(), width) - - def toString(self) -> str: - return f"LineBreaker(strategy={self._strategy}, hyphen={self._hyphenation_frequency})" - - -# ═══════════════════════════════════════════════════════════════ -# MeasuredText -# ═══════════════════════════════════════════════════════════════ - -class MeasuredText(Stub): - """MeasuredText — 度量文本""" - - def __init__(self, text: str = ""): - super().__init__() - self._text = text - self._widths: List[float] = [] - self._ascent: float = 0.0 - self._descent: float = 0.0 - self._height: float = 0.0 - self._glyph_count: int = 0 - self._char_spacing: float = 0.0 - self._word_spacing: float = 0.0 - self._scale_x: float = 1.0 - self._advances: List[float] = [] - self._clusters: List[int] = [] - for c in text: - self._widths.append(12.0) - self._advances.append(12.0) - self._clusters.append(1) - - def getText(self) -> str: - return self._text - - def getWidth(self, start: int = 0, end: int = -1) -> float: - if end < 0: - end = len(self._widths) - return sum(self._widths[start:end]) - - def getAscent(self) -> float: - return self._ascent - - def getDescent(self) -> float: - return self._descent - - def getHeight(self) -> float: - return self._height - - def getCharWidthAt(self, index: int) -> float: - if 0 <= index < len(self._widths): - return self._widths[index] - return 0.0 - - def setWidths(self, widths: List[float]): - self._widths = list(widths) - - def setMetrics(self, ascent: float, descent: float): - self._ascent = ascent - self._descent = descent - self._height = ascent - descent - - def getAdvance(self, index: int) -> float: - if 0 <= index < len(self._advances): - return self._advances[index] - return 0.0 - - def getCluster(self, index: int) -> int: - if 0 <= index < len(self._clusters): - return self._clusters[index] - return 1 - - def getGlyphCount(self) -> int: - return self._glyph_count - - def setGlyphCount(self, count: int): - self._glyph_count = count - - def getCharSpacing(self) -> float: - return self._char_spacing - - def getWordSpacing(self) -> float: - return self._word_spacing - - def getScaleX(self) -> float: - return self._scale_x - - def toString(self) -> str: - return f"MeasuredText(text={self._text[:20]}, w={self.getWidth():.1f})" - - -# ═══════════════════════════════════════════════════════════════ -# TextLayout -# ═══════════════════════════════════════════════════════════════ - -class TextLayout(Stub): - """TextLayout — 文本布局""" - - DIRECTION_LEFT_TO_RIGHT = 0 - DIRECTION_RIGHT_TO_LEFT = 1 - - ALIGN_NORMAL = 0 - ALIGN_CENTER = 1 - ALIGN_OPPOSITE = 2 - - TEXT_DIRECTION_FIRST_STRONG_LTR = 0 - TEXT_DIRECTION_FIRST_STRONG_RTL = 1 - TEXT_DIRECTION_ANY_LTR = 2 - TEXT_DIRECTION_ANY_RTL = 3 - TEXT_DIRECTION_LTR = 4 - TEXT_DIRECTION_RTL = 5 - TEXT_DIRECTION_LOCALE = 6 - TEXT_DIRECTION_FIRST_STRONG = 7 - - def __init__(self, text: str = "", width: float = 0.0, paint: Any = None): - super().__init__() - self._text = text - self._width = width - self._paint = paint - self._lines: List[TextLayout.LineInfo] = [] - self._alignment: int = TextLayout.ALIGN_NORMAL - self._direction: int = TextLayout.DIRECTION_LEFT_TO_RIGHT - self._text_direction: int = TextLayout.TEXT_DIRECTION_FIRST_STRONG_LTR - self._max_lines: int = 0 - self._include_padding: bool = True - self._spacing_mult: float = 1.0 - self._spacing_add: float = 0.0 - self._ellipsized_width: float = 0.0 - self._is_ellipsized: bool = False - - def getText(self) -> str: - return self._text - - def getWidth(self) -> float: - return self._width - - def getHeight(self) -> float: - if not self._lines: - return 0.0 - return sum(l.getHeight() for l in self._lines) - - def getLineCount(self) -> int: - return len(self._lines) - - def getLineInfo(self, line_index: int) -> Optional[TextLayout.LineInfo]: - if 0 <= line_index < len(self._lines): - return self._lines[line_index] - return None - - def getLineStart(self, line_index: int) -> int: - if 0 <= line_index < len(self._lines): - return self._lines[line_index].getStart() - return 0 - - def getLineEnd(self, line_index: int) -> int: - if 0 <= line_index < len(self._lines): - return self._lines[line_index].getEnd() - return 0 - - def getLineMax(self, line_index: int) -> float: - if 0 <= line_index < len(self._lines): - return self._lines[line_index].getMax() - return 0.0 - - def getLineAscent(self, line_index: int) -> float: - if 0 <= line_index < len(self._lines): - return self._lines[line_index].getAscent() - return 0.0 - - def getLineDescent(self, line_index: int) -> float: - if 0 <= line_index < len(self._lines): - return self._lines[line_index].getDescent() - return 0.0 - - def getMaxLines(self) -> int: - return self._max_lines - - def setMaxLines(self, max_lines: int): - self._max_lines = max_lines - - def getAlignment(self) -> int: - return self._alignment - - def setAlignment(self, alignment: int): - self._alignment = alignment - - def getDirection(self) -> int: - return self._direction - - def setDirection(self, direction: int): - self._direction = direction - - def getTextDirection(self) -> int: - return self._text_direction - - def setTextDirection(self, direction: int): - self._text_direction = direction - - def setIncludePadding(self, include: bool): - self._include_padding = include - - def isIncludePadding(self) -> bool: - return self._include_padding - - def setSpacingMultiplier(self, mult: float): - self._spacing_mult = mult - - def getSpacingMultiplier(self) -> float: - return self._spacing_mult - - def setSpacingAdd(self, add: float): - self._spacing_add = add - - def getSpacingAdd(self) -> float: - return self._spacing_add - - def isEllipsized(self) -> bool: - return self._is_ellipsized - - def setEllipsizedWidth(self, width: float): - self._ellipsized_width = width - - def getEllipsizedWidth(self) -> float: - return self._ellipsized_width - - def addLine(self, start: int, end: int, max_width: float, - ascent: float, descent: float): - line = TextLayout.LineInfo(start, end, max_width, ascent, descent) - self._lines.append(line) - - def clearLines(self): - self._lines.clear() - - def buildSimpleLayout(self, char_width: float = 12.0, line_height: float = 16.0): - """构建简单布局""" - self._lines.clear() - if not self._text: - return - chars_per_line = int(self._width / char_width) if char_width > 0 else 10 - if chars_per_line <= 0: - chars_per_line = 10 - start = 0 - while start < len(self._text): - end = min(start + chars_per_line, len(self._text)) - line_text = self._text[start:end] - line_width = len(line_text) * char_width - self.addLine(start, end, line_width, -12.0, 3.0) - start = end - - def toString(self) -> str: - return f"TextLayout(lines={len(self._lines)}, w={self._width:.1f})" - - # ═══════════════════════════════════════════════════════ - # TextLayout.LineInfo - # ═══════════════════════════════════════════════════════ - - class LineInfo(Stub): - """TextLayout.LineInfo — 行信息""" - - def __init__(self, start: int = 0, end: int = 0, - max_width: float = 0.0, ascent: float = 0.0, - descent: float = 0.0): - super().__init__() - self._start = start - self._end = end - self._max_width = max_width - self._ascent = ascent - self._descent = descent - self._height = ascent - descent if descent > 0 else 16.0 - self._direction: int = TextLayout.DIRECTION_LEFT_TO_RIGHT - self._is_ellipsized: bool = False - - def getStart(self) -> int: - return self._start - - def getEnd(self) -> int: - return self._end - - def getMax(self) -> float: - return self._max_width - - def getAscent(self) -> float: - return self._ascent - - def getDescent(self) -> float: - return self._descent - - def getHeight(self) -> float: - return self._height - - def getDirection(self) -> int: - return self._direction - - def setDirection(self, direction: int): - self._direction = direction - - def isEllipsized(self) -> bool: - return self._is_ellipsized - - def setEllipsized(self, ellipsized: bool): - self._is_ellipsized = ellipsized - - def toString(self) -> str: - return f"LineInfo({self._start}-{self._end}, w={self._max_width:.1f})" - - -# ═══════════════════════════════════════════════════════════════ -# FontShaper -# ═══════════════════════════════════════════════════════════════ - -class FontShaper(Stub): - """FontShaper — 字体重塑器""" - - def __init__(self, font: Any = None): - super().__init__() - self._font = font - self._variations: Dict[str, float] = {} - self._features: Dict[str, int] = {} - self._is_ready: bool = True - - def getFont(self) -> Any: - return self._font - - def setFont(self, font: Any): - self._font = font - - def setVariation(self, tag: str, value: float): - self._variations[tag] = value - - def getVariation(self, tag: str) -> Optional[float]: - return self._variations.get(tag) - - def getVariations(self) -> Dict[str, float]: - return dict(self._variations) - - def setFeature(self, tag: str, value: int): - self._features[tag] = value - - def getFeature(self, tag: str) -> int: - return self._features.get(tag, 0) - - def getFeatures(self) -> Dict[str, int]: - return dict(self._features) - - def shapeText(self, text: str, paint: Any = None) -> PositionedGlyphs: - """重塑文本""" - glyphs = PositionedGlyphs() - for i, c in enumerate(text): - glyphs.addGlyph(ord(c), i * 12.0, 0.0, 12.0, 0.0) - glyphs.setBounds(len(text) * 12.0, 16.0) - glyphs.setMetrics(-12.0, 3.0) - return glyphs - - def shapeTextRun(self, text: str, start: int, count: int, - context_start: int, context_count: int, - is_rtl: bool, paint: Any) -> PositionedGlyphs: - """重塑文本运行""" - glyphs = PositionedGlyphs() - for i in range(count): - idx = start + i - if idx < len(text): - glyphs.addGlyph(ord(text[idx]), i * 12.0, 0.0, 12.0, 0.0) - glyphs.setBounds(count * 12.0, 16.0) - glyphs.setMetrics(-12.0, 3.0) - return glyphs - - def getGlyphMetrics(self, glyph_id: int) -> Tuple[float, float, float, float]: - """获取字形度量""" - return (0.0, 0.0, 12.0, 16.0) - - def isReady(self) -> bool: - return self._is_ready - - def toString(self) -> str: - return f"FontShaper(variations={len(self._variations)})" - - -# ═══════════════════════════════════════════════════════════════ -# TextRunShaperResult (helper) -# ═══════════════════════════════════════════════════════════════ - -class TextRunShaperResult(Stub): - """TextRunShaperResult — 文本运行重塑结果""" - - def __init__(self): - super().__init__() - self._glyphs: PositionedGlyphs = PositionedGlyphs() - self._char_offset: int = 0 - self._char_count: int = 0 - self._is_rtl: bool = False - self._font_size: float = 12.0 - - def getGlyphs(self) -> PositionedGlyphs: - return self._glyphs - - def setGlyphs(self, glyphs: PositionedGlyphs): - self._glyphs = glyphs - - def getCharOffset(self) -> int: - return self._char_offset - - def setCharOffset(self, offset: int): - self._char_offset = offset - - def getCharCount(self) -> int: - return self._char_count - - def setCharCount(self, count: int): - self._char_count = count - - def isRtl(self) -> bool: - return self._is_rtl - - def setRtl(self, rtl: bool): - self._is_rtl = rtl - - def toString(self) -> str: - return f"TextRunShaperResult(glyphs={self._glyphs.getGlyphCount()})" - - -# ═══════════════════════════════════════════════════════════════ -# TextShaperUtils -# ═══════════════════════════════════════════════════════════════ - -class TextShaperUtils(Stub): - """TextShaperUtils — 文本重塑工具类""" - - @staticmethod - def estimateCharWidth(paint: Any = None) -> float: - return 12.0 - - @staticmethod - def estimateLineHeight(paint: Any = None) -> float: - return 16.0 - - @staticmethod - def measureText(text: str, paint: Any = None) -> float: - return len(text) * 12.0 - - @staticmethod - def wordWrap(text: str, max_width: float, char_width: float = 12.0) -> List[str]: - lines = [] - chars_per_line = int(max_width / char_width) if char_width > 0 else 10 - if chars_per_line <= 0: - chars_per_line = 10 - words = text.split(" ") - current_line = "" - for word in words: - if len(current_line) + len(word) + 1 <= chars_per_line: - if current_line: - current_line += " " - current_line += word - else: - if current_line: - lines.append(current_line) - current_line = word - if current_line: - lines.append(current_line) - return lines - - @staticmethod - def truncateText(text: str, max_width: float, char_width: float = 12.0) -> str: - max_chars = int(max_width / char_width) - if max_chars <= 0: - return "" - if len(text) <= max_chars: - return text - return text[:max_chars - 1] + "..." - - @staticmethod - def isRtlCharacter(c: str) -> bool: - cp = ord(c) - return (0x0590 <= cp <= 0x05FF or 0x0600 <= cp <= 0x06FF or - 0x0700 <= cp <= 0x074F or 0x07C0 <= cp <= 0x07FF or - 0xFB1D <= cp <= 0xFDFF or 0xFE70 <= cp <= 0xFEFF) - - @staticmethod - def getTextDirection(text: str) -> int: - for c in text[:100]: - if TextShaperUtils.isRtlCharacter(c): - return TextLayout.TEXT_DIRECTION_FIRST_STRONG_RTL - return TextLayout.TEXT_DIRECTION_FIRST_STRONG_LTR \ No newline at end of file diff --git a/android_pyapi/android/hardware.py b/android_pyapi/android/hardware.py deleted file mode 100644 index c0f54c6..0000000 --- a/android_pyapi/android/hardware.py +++ /dev/null @@ -1,746 +0,0 @@ -""" -Android Hardware 组件 — Sensor, SensorManager, SensorEvent, 等 -方法签名完全对齐 Android SDK -""" - -import math -import time -import random -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError, android_context, HAS_PIL - -# Linux 真实实现导入 -try: - from .._linux_impl import IS_LINUX, LinuxBluetooth, LinuxSensors, HAS_BLUETOOTHCTL, LinuxPower - _USE_LINUX = IS_LINUX -except ImportError: - try: - from .._linux_impl import IS_LINUX, LinuxBluetooth, LinuxSensors, HAS_BLUETOOTHCTL - _USE_LINUX = IS_LINUX - LinuxPower = None - except ImportError: - _USE_LINUX = False - LinuxPower = None - - -# ═══════════════════════════════════════════════════════════════ -# Sensor -# ═══════════════════════════════════════════════════════════════ - -class Sensor(Stub): - """Sensor — 传感器""" - - TYPE_ACCELEROMETER = 1 - TYPE_MAGNETIC_FIELD = 2 - TYPE_ORIENTATION = 3 - TYPE_GYROSCOPE = 4 - TYPE_LIGHT = 5 - TYPE_PRESSURE = 6 - TYPE_PROXIMITY = 8 - TYPE_GRAVITY = 9 - TYPE_LINEAR_ACCELERATION = 10 - TYPE_ROTATION_VECTOR = 11 - TYPE_RELATIVE_HUMIDITY = 12 - TYPE_AMBIENT_TEMPERATURE = 13 - TYPE_STEP_COUNTER = 19 - TYPE_STEP_DETECTOR = 18 - TYPE_HEART_RATE = 21 - TYPE_LOW_LATENCY_OFFBODY_DETECT = 34 - TYPE_ACCELEROMETER_LIMITED_AXES = 35 - TYPE_DEVICE_ORIENTATION = 27 - TYPE_STATIONARY_DETECT = 28 - - REPORTING_MODE_CONTINUOUS = 0 - REPORTING_MODE_ON_CHANGE = 1 - REPORTING_MODE_ONE_SHOT = 2 - REPORTING_MODE_SPECIAL_TRIGGER = 3 - - def __init__(self, type_: int, name: str = "Simulated Sensor", - vendor: str = "Android-pyApi", version: int = 1, - resolution: float = 1.0, max_range: float = 100.0, - power: float = 0.5, min_delay: int = 10000): - super().__init__() - self._type = type_ - self._name = name - self._vendor = vendor - self._version = version - self._resolution = resolution - self._max_range = max_range - self._power = power - self._min_delay = min_delay - - # Assign default values based on sensor type - type_defaults = { - Sensor.TYPE_ACCELEROMETER: ("Accelerometer", 0.01, 39.24, 3.0, 10000), - Sensor.TYPE_GYROSCOPE: ("Gyroscope", 0.001, 34.91, 6.1, 10000), - Sensor.TYPE_MAGNETIC_FIELD: ("Magnetic Field", 0.01, 2000.0, 4.0, 10000), - Sensor.TYPE_LIGHT: ("Light", 1.0, 40000.0, 0.5, 10000), - Sensor.TYPE_PROXIMITY: ("Proximity", 1.0, 10.0, 0.5, 10000), - Sensor.TYPE_PRESSURE: ("Pressure", 0.01, 1100.0, 1.0, 10000), - Sensor.TYPE_GRAVITY: ("Gravity", 0.01, 39.24, 3.0, 10000), - Sensor.TYPE_LINEAR_ACCELERATION: ("Linear Acceleration", 0.01, 39.24, 3.0, 10000), - Sensor.TYPE_ROTATION_VECTOR: ("Rotation Vector", 0.0001, 1.0, 5.0, 10000), - Sensor.TYPE_RELATIVE_HUMIDITY: ("Relative Humidity", 0.1, 100.0, 0.5, 10000), - Sensor.TYPE_AMBIENT_TEMPERATURE: ("Ambient Temperature", 0.01, 80.0, 0.5, 10000), - Sensor.TYPE_STEP_COUNTER: ("Step Counter", 1.0, 100000.0, 0.1, 100000), - Sensor.TYPE_STEP_DETECTOR: ("Step Detector", 1.0, 1.0, 0.1, 100000), - Sensor.TYPE_HEART_RATE: ("Heart Rate", 1.0, 220.0, 0.5, 100000), - } - - if type_ in type_defaults: - n, res, mr, p, md = type_defaults[type_] - self._name = name if name != "Simulated Sensor" else n - self._resolution = resolution if resolution != 1.0 else res - self._max_range = max_range if max_range != 100.0 else mr - self._power = power if power != 0.5 else p - self._min_delay = min_delay if min_delay != 10000 else md - - def getType(self) -> int: - return self._type - - def getName(self) -> str: - return self._name - - def getVendor(self) -> str: - return self._vendor - - def getVersion(self) -> int: - return self._version - - def getMaximumRange(self) -> float: - return self._max_range - - def getMinDelay(self) -> int: - return self._min_delay - - def getPower(self) -> float: - return self._power - - def getResolution(self) -> float: - return self._resolution - - def getFifoMaxEventCount(self) -> int: - return 0 - - def getFifoReservedEventCount(self) -> int: - return 0 - - def getReportingMode(self) -> int: - return Sensor.REPORTING_MODE_CONTINUOUS - - def isWakeUpSensor(self) -> bool: - return False - - def getStringType(self) -> str: - type_names = { - Sensor.TYPE_ACCELEROMETER: "android.sensor.accelerometer", - Sensor.TYPE_MAGNETIC_FIELD: "android.sensor.magnetic_field", - Sensor.TYPE_GYROSCOPE: "android.sensor.gyroscope", - Sensor.TYPE_LIGHT: "android.sensor.light", - Sensor.TYPE_PRESSURE: "android.sensor.pressure", - Sensor.TYPE_PROXIMITY: "android.sensor.proximity", - Sensor.TYPE_GRAVITY: "android.sensor.gravity", - Sensor.TYPE_LINEAR_ACCELERATION: "android.sensor.linear_acceleration", - Sensor.TYPE_ROTATION_VECTOR: "android.sensor.rotation_vector", - Sensor.TYPE_RELATIVE_HUMIDITY: "android.sensor.relative_humidity", - Sensor.TYPE_AMBIENT_TEMPERATURE: "android.sensor.ambient_temperature", - Sensor.TYPE_STEP_COUNTER: "android.sensor.step_counter", - Sensor.TYPE_STEP_DETECTOR: "android.sensor.step_detector", - Sensor.TYPE_HEART_RATE: "android.sensor.heart_rate", - } - return type_names.get(self._type, "android.sensor.unknown") - - def toString(self) -> str: - return f"{self._name} (type={self._type}, vendor={self._vendor}, ver={self._version})" - - -# ═══════════════════════════════════════════════════════════════ -# SensorEvent -# ═══════════════════════════════════════════════════════════════ - -class SensorEvent(Stub): - """SensorEvent — 传感器事件""" - - def __init__(self, sensor: Sensor, accuracy: int = 3): - super().__init__() - self.sensor = sensor - self.accuracy = accuracy - self.timestamp = int(time.time() * 1_000_000_000) # nanoseconds - self.values: List[float] = [] - - # Generate appropriate values based on sensor type - if sensor.getType() == Sensor.TYPE_ACCELEROMETER: - self.values = [random.uniform(-1, 1), random.uniform(-1, 1), - 9.8 + random.uniform(-0.5, 0.5)] - elif sensor.getType() == Sensor.TYPE_GYROSCOPE: - self.values = [random.uniform(-0.1, 0.1)] * 3 - elif sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD: - self.values = [random.uniform(-50, 50)] * 3 - elif sensor.getType() == Sensor.TYPE_LIGHT: - self.values = [random.uniform(100, 1000)] - elif sensor.getType() == Sensor.TYPE_PROXIMITY: - self.values = [5.0 if random.random() > 0.5 else 0.0] - elif sensor.getType() == Sensor.TYPE_PRESSURE: - self.values = [random.uniform(980, 1050)] - elif sensor.getType() in (Sensor.TYPE_GRAVITY, Sensor.TYPE_LINEAR_ACCELERATION): - self.values = [random.uniform(-1, 1)] * 3 - elif sensor.getType() == Sensor.TYPE_ROTATION_VECTOR: - self.values = [random.uniform(-1, 1)] * 5 - elif sensor.getType() == Sensor.TYPE_RELATIVE_HUMIDITY: - self.values = [random.uniform(30, 70)] - elif sensor.getType() == Sensor.TYPE_AMBIENT_TEMPERATURE: - self.values = [random.uniform(15, 35)] - elif sensor.getType() == Sensor.TYPE_STEP_COUNTER: - self.values = [float(random.randint(0, 10000))] - elif sensor.getType() == Sensor.TYPE_STEP_DETECTOR: - self.values = [1.0 if random.random() > 0.95 else 0.0] - elif sensor.getType() == Sensor.TYPE_HEART_RATE: - self.values = [random.uniform(60, 100)] - else: - self.values = [0.0] - - -# ═══════════════════════════════════════════════════════════════ -# SensorEventListener -# ═══════════════════════════════════════════════════════════════ - -class SensorEventListener(Stub): - """SensorEventListener — 传感器事件监听器""" - - def onSensorChanged(self, event: SensorEvent): - pass - - def onAccuracyChanged(self, sensor: Sensor, accuracy: int): - pass - - -# ═══════════════════════════════════════════════════════════════ -# SensorDelay -# ═══════════════════════════════════════════════════════════════ - -class SensorDelay(Stub): - """SensorDelay — 传感器采样延迟常量""" - FASTEST = 0 - GAME = 1 - UI = 2 - NORMAL = 3 - - -# ═══════════════════════════════════════════════════════════════ -# SensorManager -# ═══════════════════════════════════════════════════════════════ - -class SensorManager(Stub): - """SensorManager — 传感器管理器""" - - SENSOR_DELAY_FASTEST = 0 - SENSOR_DELAY_GAME = 1 - SENSOR_DELAY_UI = 2 - SENSOR_DELAY_NORMAL = 3 - - AXIS_X = 1 - AXIS_Y = 2 - AXIS_Z = 3 - AXIS_MINUS_X = 129 - AXIS_MINUS_Y = 130 - AXIS_MINUS_Z = 131 - - def __init__(self): - super().__init__() - self._sensors: Dict[int, Sensor] = {} - self._listeners: Dict[int, List[Tuple[SensorEventListener, Sensor, int]]] = {} - - # Create default sensors - for type_ in [ - Sensor.TYPE_ACCELEROMETER, Sensor.TYPE_GYROSCOPE, - Sensor.TYPE_MAGNETIC_FIELD, Sensor.TYPE_LIGHT, - Sensor.TYPE_PROXIMITY, Sensor.TYPE_PRESSURE, - Sensor.TYPE_GRAVITY, Sensor.TYPE_LINEAR_ACCELERATION, - Sensor.TYPE_ROTATION_VECTOR, Sensor.TYPE_RELATIVE_HUMIDITY, - Sensor.TYPE_AMBIENT_TEMPERATURE, Sensor.TYPE_STEP_COUNTER, - Sensor.TYPE_STEP_DETECTOR, Sensor.TYPE_HEART_RATE, - ]: - self._sensors[type_] = Sensor(type_) - - def getDefaultSensor(self, type_: int) -> Optional[Sensor]: - if _USE_LINUX: - sensor = self._build_linux_sensor(type_) - if sensor is not None: - return sensor - return self._sensors.get(type_) - - def _build_linux_sensor(self, type_: int) -> Optional[Sensor]: - """根据 Linux 真实传感器数据构造 Sensor 对象""" - try: - if type_ == Sensor.TYPE_AMBIENT_TEMPERATURE: - temps = LinuxSensors.get_temperatures() - if temps: - t = temps[0] - return Sensor(Sensor.TYPE_AMBIENT_TEMPERATURE, - name=t.get("name", "Linux Temperature"), - vendor="Linux", resolution=0.01, - max_range=125.0, power=0.0, min_delay=1000) - return None - if type_ == Sensor.TYPE_LIGHT: - light = LinuxSensors.get_light_level() - if light is not None: - return Sensor(Sensor.TYPE_LIGHT, name="Linux Light Sensor", - vendor="Linux", resolution=0.001, - max_range=40000.0, power=0.0, min_delay=1000) - return None - except Exception: - return None - return None - - def getSensorList(self, type_: int) -> List[Sensor]: - if _USE_LINUX: - return self._get_linux_sensor_list(type_) - if type_ == -1: # ALL - return list(self._sensors.values()) - sensor = self._sensors.get(type_) - return [sensor] if sensor else [] - - def _get_linux_sensor_list(self, type_: int) -> List[Sensor]: - """从 Linux 真实传感器数据生成传感器列表""" - sensors: List[Sensor] = [] - try: - if type_ == -1 or type_ == Sensor.TYPE_AMBIENT_TEMPERATURE: - for t in LinuxSensors.get_temperatures(): - sensors.append(Sensor( - Sensor.TYPE_AMBIENT_TEMPERATURE, - name=t.get("name", "Linux Temperature"), - vendor="Linux", resolution=0.01, - max_range=125.0, power=0.0, min_delay=1000)) - if type_ == -1 or type_ == Sensor.TYPE_LIGHT: - light = LinuxSensors.get_light_level() - if light is not None: - sensors.append(Sensor( - Sensor.TYPE_LIGHT, name="Linux Light Sensor", - vendor="Linux", resolution=0.001, - max_range=40000.0, power=0.0, min_delay=1000)) - # CPU 频率作为附加传感器 (映射到压力传感器类型以承载真实数据) - if type_ == -1 or type_ == Sensor.TYPE_PRESSURE: - for f in LinuxSensors.get_cpu_frequency(): - sensors.append(Sensor( - Sensor.TYPE_PRESSURE, - name=f"CPU{f.get('cpu', 0)} Frequency", - vendor="Linux", resolution=1.0, - max_range=10000.0, power=0.0, min_delay=1000)) - # 风扇转速作为附加传感器 (映射到相对湿度类型以承载真实数据) - if type_ == -1 or type_ == Sensor.TYPE_RELATIVE_HUMIDITY: - for fan in LinuxSensors.get_fan_speeds(): - sensors.append(Sensor( - Sensor.TYPE_RELATIVE_HUMIDITY, - name=f"Fan {fan.get('name', '')}", - vendor="Linux", resolution=1.0, - max_range=10000.0, power=0.0, min_delay=1000)) - except Exception: - pass - # 若未取到真实数据则回退到模拟传感器 - if not sensors: - if type_ == -1: - sensors = list(self._sensors.values()) - else: - sensor = self._sensors.get(type_) - if sensor: - sensors = [sensor] - return sensors - - def getSensors(self) -> List[Sensor]: - return list(self._sensors.values()) - - def registerListener(self, listener: SensorEventListener, sensor: Sensor, - samplingPeriodUs: int, maxReportLatencyUs: int = 0) -> bool: - if sensor.getType() not in self._sensors: - return False - if listener not in self._listeners: - self._listeners[id(listener)] = [] - self._listeners[id(listener)].append((listener, sensor, samplingPeriodUs)) - return True - - def unregisterListener(self, listener: SensorEventListener, sensor: Sensor = None) -> bool: - lid = id(listener) - if lid in self._listeners: - if sensor: - self._listeners[lid] = [ - (l, s, d) for l, s, d in self._listeners[lid] - if s.getType() != sensor.getType() - ] - if not self._listeners[lid]: - del self._listeners[lid] - else: - del self._listeners[lid] - return True - return False - - @staticmethod - def getOrientation(R: List[float], values: List[float]) -> List[float]: - if len(R) >= 9 and len(values) >= 3: - values[0] = math.degrees(math.atan2(R[1], R[4])) # azimuth - values[1] = math.degrees(math.asin(-R[7])) # pitch - values[2] = math.degrees(math.atan2(-R[6], R[8])) # roll - return values - - @staticmethod - def getRotationMatrix(R: List[float], I: List[float], - gravity: List[float], geomagnetic: List[float]) -> bool: - """根据重力和地磁向量计算旋转矩阵 R 和倾斜矩阵 I。 - 算法对齐 Android SensorManager.getRotationMatrix。""" - if len(R) < 9: - R.extend([0.0] * (9 - len(R))) - gx, gy, gz = gravity[0], gravity[1], gravity[2] - mx, my, mz = geomagnetic[0], geomagnetic[1], geomagnetic[2] - # 归一化重力向量 -> H - g_len = math.sqrt(gx * gx + gy * gy + gz * gz) - if g_len < 1e-10: - return False - hx = gx / g_len - hy = gy / g_len - hz = gz / g_len - # 叉积 A = H x M (东方向) - ax = hy * mz - hz * my - ay = hz * mx - hx * mz - az = hx * my - hy * mx - a_len = math.sqrt(ax * ax + ay * ay + az * az) - if a_len < 1e-10: - return False - ax /= a_len - ay /= a_len - az /= a_len - # 重新计算 M = A x H - ex = ay * hz - az * hy - ey = az * hx - ax * hz - ez = ax * hy - ay * hx - # 填充旋转矩阵 R (列优先,Android 格式) - R[0] = ax - R[1] = ay - R[2] = az - R[3] = ex - R[4] = ey - R[5] = ez - R[6] = hx - R[7] = hy - R[8] = hz - # 填充倾斜矩阵 I - if I is not None and len(I) >= 9: - inv_h = 1.0 / g_len - inv_m = 1.0 / math.sqrt(mx * mx + my * my + mz * mz) if (mx * mx + my * my + mz * mz) > 0 else 0 - I[0] = inv_h * hx - I[1] = inv_h * hy - I[2] = inv_h * hz - I[3] = inv_m * mx - I[4] = inv_m * my - I[5] = inv_m * mz - I[6] = inv_h * ex - I[7] = inv_h * ey - I[8] = inv_h * ez - return True - - @staticmethod - def getAltitude(p0: float, p: float) -> float: - return 44330.0 * (1.0 - math.pow(p / p0, 1.0 / 5.255)) - - @staticmethod - def getInclination(I: List[float]) -> float: - """根据倾斜矩阵 I 计算地磁倾角(弧度)。""" - if len(I) >= 9: - # I[5] = cos(el), I[2] = sin(el) - # 倾角 = atan2(I[2], I[5]) - return math.atan2(I[2], I[5]) - return 0.0 - - @staticmethod - def remapCoordinateSystem(inR: List[float], X: int, Y: int, - outR: List[float]) -> bool: - """根据指定的 X/Y 轴重映射坐标系。""" - if len(inR) < 9 or len(outR) < 9: - return False - # Android 轴常量 - AXIS_X = 1 - AXIS_Y = 2 - AXIS_Z = 3 - AXIS_MINUS_X = -1 - AXIS_MINUS_Y = -2 - AXIS_MINUS_Z = -3 - # 将轴常量映射到列索引和符号 - axis_map = { - AXIS_X: (0, 1), AXIS_Y: (1, 1), AXIS_Z: (2, 1), - AXIS_MINUS_X: (0, -1), AXIS_MINUS_Y: (1, -1), AXIS_MINUS_Z: (2, -1), - } - if X not in axis_map or Y not in axis_map: - return False - x_col, x_sign = axis_map[X] - y_col, y_sign = axis_map[Y] - # Z = X x Y - z_col = 3 - x_col - y_col - z_sign = x_sign * y_sign - # 如果 X 和 Y 不正交 - if abs(x_col - y_col) != 1 and not (x_col == 0 and y_col == 2) and not (x_col == 2 and y_col == 0): - z_sign = -z_sign - # 填充输出矩阵(列优先) - for row in range(3): - outR[row * 3 + 0] = inR[row * 3 + x_col] * x_sign - outR[row * 3 + 1] = inR[row * 3 + y_col] * y_sign - outR[row * 3 + 2] = inR[row * 3 + z_col] * z_sign - return True - - def getDeviceId(self) -> str: - """返回传感器设备 ID。尝试从 /sys 读取真实设备标识。""" - import os as _os - # 尝试从 /sys/class/sensor 读取 - try: - base = "/sys/class/sensor" - if _os.path.isdir(base): - names = _os.listdir(base) - if names: - return names[0] - except (OSError, IOError): - pass - # 尝试从 /sys/bus/iio/devices 读取 - try: - base = "/sys/bus/iio/devices" - if _os.path.isdir(base): - names = _os.listdir(base) - if names: - return names[0] - except (OSError, IOError): - pass - return "sensor_hub_0" - - -# ═══════════════════════════════════════════════════════════════ -# HardwareBuffer -# ═══════════════════════════════════════════════════════════════ - -class HardwareBuffer(Stub): - """HardwareBuffer — 硬件缓冲 (API 26+)""" - - USAGE_CPU_READ_OFTEN = 0x00000003 - USAGE_CPU_WRITE_OFTEN = 0x00000030 - USAGE_GPU_SAMPLED_IMAGE = 0x00000100 - USAGE_GPU_COLOR_OUTPUT = 0x00000200 - USAGE_GPU_DATA_BUFFER = 0x00001000 - USAGE_SENSOR_DIRECT_DATA = 0x00400000 - USAGE_PROTECTED_CONTENT = 0x00004000 - USAGE_VIDEO_ENCODE = 0x00010000 - USAGE_GPU_FRAMEBUFFER = 0x00020000 - USAGE_COMPOSER_OVERLAY = 0x00000800 - USAGE_FRONT_BUFFER_QUEUE = 0x00008000 - - BLOB = 0x21 - RGBA_8888 = 1 - RGBA_FP16 = 22 - RGBA_1010102 = 43 - RGBX_8888 = 2 - RGB_888 = 3 - RGB_565 = 4 - YCBCR_420_888 = 35 - - def __init__(self, width: int, height: int, format: int, layers: int, - usage: int, buffer_size: int = 0): - super().__init__() - self._width = width - self._height = height - self._format = format - self._layers = layers - self._usage = usage - self._buffer_size = buffer_size - self._closed = False - - @property - def width(self) -> int: - return self._width - - @property - def height(self) -> int: - return self._height - - @property - def format(self) -> int: - return self._format - - @property - def layers(self) -> int: - return self._layers - - @property - def usage(self) -> int: - return self._usage - - def destroy(self): - self.close() - - def close(self): - self._closed = True - - def isClosed(self) -> bool: - return self._closed - - def isEmpty(self) -> bool: - return self._width == 0 or self._height == 0 - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def getFormat(self) -> int: - return self._format - - def getLayers(self) -> int: - return self._layers - - def getUsage(self) -> int: - return self._usage - - def getStride(self) -> int: - return self._width - - -# ═══════════════════════════════════════════════════════════════ -# BatteryManager (Constants) -# ═══════════════════════════════════════════════════════════════ - -class BatteryManager(Stub): - """BatteryManager — 电池管理器常量 (实际实现在 android.os)""" - - BATTERY_PROPERTY_CHARGE_COUNTER = 1 - BATTERY_PROPERTY_CURRENT_NOW = 2 - BATTERY_PROPERTY_CURRENT_AVERAGE = 3 - BATTERY_PROPERTY_CAPACITY = 4 - BATTERY_PROPERTY_STATUS = 5 - BATTERY_PROPERTY_ENERGY_COUNTER = 6 - - BATTERY_STATUS_UNKNOWN = 1 - BATTERY_STATUS_CHARGING = 2 - BATTERY_STATUS_DISCHARGING = 3 - BATTERY_STATUS_NOT_CHARGING = 4 - BATTERY_STATUS_FULL = 5 - - BATTERY_HEALTH_UNKNOWN = 1 - BATTERY_HEALTH_GOOD = 2 - BATTERY_HEALTH_OVERHEAT = 3 - BATTERY_HEALTH_DEAD = 4 - BATTERY_HEALTH_OVER_VOLTAGE = 5 - BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6 - BATTERY_HEALTH_COLD = 7 - - BATTERY_PLUGGED_AC = 1 - BATTERY_PLUGGED_USB = 2 - BATTERY_PLUGGED_WIRELESS = 4 - - ACTION_CHARGING = "android.intent.action.ACTION_CHARGING" - ACTION_DISCHARGING = "android.intent.action.ACTION_DISCHARGING" - EXTRA_STATUS = "status" - EXTRA_HEALTH = "health" - EXTRA_LEVEL = "level" - EXTRA_SCALE = "scale" - EXTRA_PLUGGED = "plugged" - EXTRA_TEMPERATURE = "temperature" - EXTRA_VOLTAGE = "voltage" - EXTRA_TECHNOLOGY = "technology" - EXTRA_CHARGE_COUNTER = "charge_counter" - EXTRA_PRESENT = "present" - - def __init__(self): - super().__init__() - - def getIntProperty(self, id: int) -> int: - """返回电池整型属性。优先使用 LinuxPower 读取真实数据。""" - if _USE_LINUX and LinuxPower is not None: - try: - info = LinuxPower.get_battery_info() or {} - if id == BatteryManager.BATTERY_PROPERTY_CAPACITY: - level = info.get("level", -1) - return int(level) if isinstance(level, (int, float)) and level >= 0 else -1 - if id == BatteryManager.BATTERY_PROPERTY_STATUS: - status_map = { - "charging": BatteryManager.BATTERY_STATUS_CHARGING, - "discharging": BatteryManager.BATTERY_STATUS_DISCHARGING, - "not charging": BatteryManager.BATTERY_STATUS_NOT_CHARGING, - "full": BatteryManager.BATTERY_STATUS_FULL, - "unknown": BatteryManager.BATTERY_STATUS_UNKNOWN, - } - return status_map.get(str(info.get("status", "unknown")).lower(), - BatteryManager.BATTERY_STATUS_UNKNOWN) - if id == BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER: - voltage = info.get("voltage", 0) - return int(voltage * 1000) if isinstance(voltage, (int, float)) else 0 - except Exception: - pass - # 回退到默认值 - id_values = { - BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER: 3500, - BatteryManager.BATTERY_PROPERTY_CAPACITY: 85, - BatteryManager.BATTERY_PROPERTY_STATUS: BatteryManager.BATTERY_STATUS_DISCHARGING, - } - return id_values.get(id, 0) - - def getLongProperty(self, id: int) -> int: - """返回电池长整型属性。优先使用 LinuxPower 读取真实数据。""" - if _USE_LINUX and LinuxPower is not None: - try: - info = LinuxPower.get_battery_info() or {} - if id == BatteryManager.BATTERY_PROPERTY_CURRENT_NOW: - current = info.get("current_now", 0) - return int(current) if isinstance(current, (int, float)) else 0 - if id == BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER: - voltage = info.get("voltage", 0) - return int(voltage * 3600000) if isinstance(voltage, (int, float)) else 0 - except Exception: - pass - return self.getIntProperty(id) - - -# ═══════════════════════════════════════════════════════════════ -# BiometricManager (lightweight) -# ═══════════════════════════════════════════════════════════════ - -class BiometricManager(Stub): - """BiometricManager — 生物识别管理器 (轻量)""" - - BIOMETRIC_SUCCESS = 0 - BIOMETRIC_ERROR_HW_UNAVAILABLE = 1 - BIOMETRIC_ERROR_NONE_ENROLLED = 11 - BIOMETRIC_ERROR_NO_HARDWARE = 12 - BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15 - - def __init__(self): - super().__init__() - - def canAuthenticate(self) -> int: - return BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE - - -# ═══════════════════════════════════════════════════════════════ -# Flashlight support -# ═══════════════════════════════════════════════════════════════ - -class CameraFlashlight(Stub): - """CameraFlashlight — 手电筒支持""" - - def __init__(self): - super().__init__() - self._on = False - - def setTorchMode(self, cameraId: str, enabled: bool): - self._on = enabled - - def isTorchModeEnabled(self, cameraId: str) -> bool: - return self._on - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Sensor", "SensorManager", "SensorEvent", "SensorEventListener", - "SensorDelay", - "HardwareBuffer", - "BatteryManager", - "BiometricManager", - "CameraFlashlight", -] \ No newline at end of file diff --git a/android_pyapi/android/hardware_biometrics.py b/android_pyapi/android/hardware_biometrics.py deleted file mode 100644 index 8567b80..0000000 --- a/android_pyapi/android/hardware_biometrics.py +++ /dev/null @@ -1,291 +0,0 @@ -""" -Android Biometrics 组件 — BiometricManager, BiometricPrompt, 等 -方法签名完全对齐 Android SDK -""" - -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError, android_context - - -# ═══════════════════════════════════════════════════════════════ -# BiometricConstants -# ═══════════════════════════════════════════════════════════════ - -class BiometricConstants(Stub): - """BiometricConstants — 生物识别常量""" - - BIOMETRIC_SUCCESS = 0 - BIOMETRIC_ERROR_HW_UNAVAILABLE = 1 - BIOMETRIC_ERROR_VENDOR = 8 - BIOMETRIC_ERROR_LOCKOUT = 7 - BIOMETRIC_ERROR_LOCKOUT_PERMANENT = 9 - BIOMETRIC_ERROR_USER_CANCELED = 10 - BIOMETRIC_ERROR_NO_BIOMETRIC = 11 - BIOMETRIC_ERROR_HW_NOT_PRESENT = 12 - BIOMETRIC_ERROR_NEGATIVE_BUTTON = 13 - BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14 - BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15 - BIOMETRIC_ERROR_UNSUPPORTED = 65536 - BIOMETRIC_ERROR_CANCELED = 5 - BIOMETRIC_ERROR_TIMEOUT = 3 - BIOMETRIC_ERROR_NONE_ENROLLED = 11 - BIOMETRIC_ERROR_NO_HARDWARE = 12 - BIOMETRIC_STATUS_OK = 0 - - # Authenticator types - BIOMETRIC_STRONG = 0x0F - BIOMETRIC_WEAK = 0xFF - DEVICE_CREDENTIAL = 0x8000 - - -# ═══════════════════════════════════════════════════════════════ -# BiometricPrompt -# ═══════════════════════════════════════════════════════════════ - -class BiometricPrompt(Stub): - """BiometricPrompt — 生物识别提示 (API 28+)""" - - class AuthenticationCallback(Stub): - """BiometricPrompt.AuthenticationCallback — 认证回调""" - - def onAuthenticationError(self, errorCode: int, errString: str): - pass - - def onAuthenticationSucceeded(self, result: "BiometricPrompt.AuthenticationResult"): - pass - - def onAuthenticationFailed(self): - pass - - def onAuthenticationAcquired(self, acquireInfo: int): - pass - - class AuthenticationResult(Stub): - """BiometricPrompt.AuthenticationResult — 认证结果""" - - def __init__(self, cryptoObject: "BiometricPromptCryptoObject" = None, - authenticationType: int = BiometricConstants.BIOMETRIC_STRONG): - super().__init__() - self._crypto_object = cryptoObject - self._authentication_type = authenticationType - - def getCryptoObject(self) -> Optional["BiometricPromptCryptoObject"]: - return self._crypto_object - - def getAuthenticationType(self) -> int: - return self._authentication_type - - class PromptInfo(Stub): - """BiometricPrompt.PromptInfo — 提示信息""" - - class Builder(Stub): - """BiometricPrompt.PromptInfo.Builder — 提示信息构建器""" - - def __init__(self): - super().__init__() - self._title = "" - self._subtitle = "" - self._description = "" - self._device_credential_allowed = False - self._allowed_authenticators = 0 - self._confirmation_required = True - self._negative_button_text = "" - - def setTitle(self, title: str) -> "BiometricPrompt.PromptInfo.Builder": - self._title = title - return self - - def setSubtitle(self, subtitle: str) -> "BiometricPrompt.PromptInfo.Builder": - self._subtitle = subtitle - return self - - def setDescription(self, description: str) -> "BiometricPrompt.PromptInfo.Builder": - self._description = description - return self - - def setDeviceCredentialAllowed(self, allowed: bool) -> "BiometricPrompt.PromptInfo.Builder": - self._device_credential_allowed = allowed - return self - - def setAllowedAuthenticators(self, authenticators: int) -> "BiometricPrompt.PromptInfo.Builder": - self._allowed_authenticators = authenticators - return self - - def setConfirmationRequired(self, required: bool) -> "BiometricPrompt.PromptInfo.Builder": - self._confirmation_required = required - return self - - def setNegativeButtonText(self, text: str) -> "BiometricPrompt.PromptInfo.Builder": - self._negative_button_text = text - return self - - def build(self) -> "BiometricPrompt.PromptInfo": - info = BiometricPrompt.PromptInfo() - info._title = self._title - info._subtitle = self._subtitle - info._description = self._description - info._device_credential_allowed = self._device_credential_allowed - info._allowed_authenticators = self._allowed_authenticators - info._confirmation_required = self._confirmation_required - info._negative_button_text = self._negative_button_text - return info - - def __init__(self): - super().__init__() - self._title = "" - self._subtitle = "" - self._description = "" - self._device_credential_allowed = False - self._allowed_authenticators = 0 - self._confirmation_required = True - self._negative_button_text = "" - - def getTitle(self) -> str: - return self._title - - def getSubtitle(self) -> str: - return self._subtitle - - def getDescription(self) -> str: - return self._description - - def isDeviceCredentialAllowed(self) -> bool: - return self._device_credential_allowed - - def getAllowedAuthenticators(self) -> int: - return self._allowed_authenticators - - def isConfirmationRequired(self) -> bool: - return self._confirmation_required - - def getNegativeButtonText(self) -> str: - return self._negative_button_text - - def __init__(self, context, callback: AuthenticationCallback = None, - handler=None): - super().__init__() - self._context = context - self._callback = callback - self._cancelled = False - - def authenticate(self, crypto: "BiometricPromptCryptoObject" = None, - cancel: Any = None, executor=None, - callback: AuthenticationCallback = None): - self._cancelled = False - if callback: - self._callback = callback - if self._callback: - # Simulate failed authentication on desktop - self._callback.onAuthenticationError( - BiometricConstants.BIOMETRIC_ERROR_NO_HARDWARE, - "No biometric hardware available on this device" - ) - - def authenticate(self, promptInfo: PromptInfo, crypto: "BiometricPromptCryptoObject" = None): - self._cancelled = False - if self._callback: - self._callback.onAuthenticationError( - BiometricConstants.BIOMETRIC_ERROR_NO_HARDWARE, - "No biometric hardware available on this device" - ) - - def cancelAuthentication(self): - self._cancelled = True - if self._callback: - self._callback.onAuthenticationError( - BiometricConstants.BIOMETRIC_ERROR_CANCELED, - "Authentication cancelled" - ) - - def cancel(self): - self.cancelAuthentication() - - def isCancelled(self) -> bool: - return self._cancelled - - -# ═══════════════════════════════════════════════════════════════ -# BiometricPromptCryptoObject -# ═══════════════════════════════════════════════════════════════ - -class BiometricPromptCryptoObject(Stub): - """BiometricPromptCryptoObject — 加密对象""" - - def __init__(self, signature=None, cipher=None, mac=None): - super().__init__() - self._signature = signature - self._cipher = cipher - self._mac = mac - - def getSignature(self): - return self._signature - - def getCipher(self): - return self._cipher - - def getMac(self): - return self._mac - - -# ═══════════════════════════════════════════════════════════════ -# BiometricPromptAuthenticationResult -# ═══════════════════════════════════════════════════════════════ - -BiometricPromptAuthenticationResult = BiometricPrompt.AuthenticationResult - - -# ═══════════════════════════════════════════════════════════════ -# BiometricManager -# ═══════════════════════════════════════════════════════════════ - -class BiometricManager(Stub): - """BiometricManager — 生物识别管理器 (API 29+)""" - - BIOMETRIC_SUCCESS = BiometricConstants.BIOMETRIC_SUCCESS - BIOMETRIC_ERROR_HW_UNAVAILABLE = BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE - BIOMETRIC_ERROR_NONE_ENROLLED = BiometricConstants.BIOMETRIC_ERROR_NONE_ENROLLED - BIOMETRIC_ERROR_NO_HARDWARE = BiometricConstants.BIOMETRIC_ERROR_NO_HARDWARE - BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED - BIOMETRIC_ERROR_UNSUPPORTED = BiometricConstants.BIOMETRIC_ERROR_UNSUPPORTED - BIOMETRIC_STATUS_OK = BiometricConstants.BIOMETRIC_STATUS_OK - BIOMETRIC_ERROR_VENDOR = BiometricConstants.BIOMETRIC_ERROR_VENDOR - BIOMETRIC_ERROR_USER_CANCELED = BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED - BIOMETRIC_ERROR_CANCELED = BiometricConstants.BIOMETRIC_ERROR_CANCELED - BIOMETRIC_ERROR_TIMEOUT = BiometricConstants.BIOMETRIC_ERROR_TIMEOUT - BIOMETRIC_ERROR_LOCKOUT = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT - BIOMETRIC_ERROR_LOCKOUT_PERMANENT = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT - - # Authenticators - AUTHENTICATOR_BIOMETRIC_STRONG = BiometricConstants.BIOMETRIC_STRONG - AUTHENTICATOR_BIOMETRIC_WEAK = BiometricConstants.BIOMETRIC_WEAK - AUTHENTICATOR_DEVICE_CREDENTIAL = BiometricConstants.DEVICE_CREDENTIAL - - def __init__(self, context=None): - super().__init__() - self._context = context - - def canAuthenticate(self, authenticators: int = AUTHENTICATOR_BIOMETRIC_STRONG) -> int: - """Check if biometric authentication is available""" - return BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE - - @staticmethod - def fromContext(context) -> "BiometricManager": - """Get BiometricManager from context""" - return BiometricManager(context) - - def toString(self) -> str: - return "BiometricManager(desktop - no hardware)" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "BiometricManager", - "BiometricPrompt", - "BiometricPromptAuthenticationResult", - "BiometricPromptCryptoObject", - "BiometricConstants", -] \ No newline at end of file diff --git a/android_pyapi/android/hardware_camera2.py b/android_pyapi/android/hardware_camera2.py deleted file mode 100644 index 4025211..0000000 --- a/android_pyapi/android/hardware_camera2.py +++ /dev/null @@ -1,761 +0,0 @@ -""" -Android Camera2 组件 — CameraManager, CameraDevice, CaptureRequest 等 -方法签名完全对齐 Android SDK -""" - -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError, android_context, HAS_PIL - - -# ═══════════════════════════════════════════════════════════════ -# CameraMetadata -# ═══════════════════════════════════════════════════════════════ - -class CameraMetadata(Stub): - """CameraMetadata — 相机元数据基类""" - - # CONTROL_AE modes - CONTROL_AE_MODE_OFF = 0 - CONTROL_AE_MODE_ON = 1 - CONTROL_AE_MODE_ON_AUTO_FLASH = 2 - CONTROL_AE_MODE_ON_ALWAYS_FLASH = 3 - CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE = 4 - CONTROL_AE_MODE_EXTERNAL_FLASH = 5 - - # CONTROL_AF modes - CONTROL_AF_MODE_OFF = 0 - CONTROL_AF_MODE_AUTO = 1 - CONTROL_AF_MODE_MACRO = 2 - CONTROL_AF_MODE_CONTINUOUS_VIDEO = 3 - CONTROL_AF_MODE_CONTINUOUS_PICTURE = 4 - CONTROL_AF_MODE_EDOF = 5 - - # CONTROL_AWB modes - CONTROL_AWB_MODE_OFF = 0 - CONTROL_AWB_MODE_AUTO = 1 - CONTROL_AWB_MODE_INCANDESCENT = 2 - CONTROL_AWB_MODE_FLUORESCENT = 3 - CONTROL_AWB_MODE_WARM_FLUORESCENT = 4 - CONTROL_AWB_MODE_DAYLIGHT = 5 - CONTROL_AWB_MODE_CLOUDY_DAYLIGHT = 6 - CONTROL_AWB_MODE_TWILIGHT = 7 - CONTROL_AWB_MODE_SHADE = 8 - - # CONTROL modes - CONTROL_MODE_OFF = 0 - CONTROL_MODE_AUTO = 1 - CONTROL_MODE_USE_SCENE_MODE = 2 - CONTROL_MODE_OFF_KEEP_STATE = 3 - - # FLASH modes - FLASH_MODE_OFF = 0 - FLASH_MODE_SINGLE = 1 - FLASH_MODE_TORCH = 2 - - # STATISTICS_FACE_DETECT modes - STATISTICS_FACE_DETECT_MODE_OFF = 0 - STATISTICS_FACE_DETECT_MODE_SIMPLE = 1 - STATISTICS_FACE_DETECT_MODE_FULL = 2 - - # EDGE modes - EDGE_MODE_OFF = 0 - EDGE_MODE_FAST = 1 - EDGE_MODE_HIGH_QUALITY = 2 - EDGE_MODE_ZERO_SHUTTER_LAG = 3 - - # NOISE_REDUCTION modes - NOISE_REDUCTION_MODE_OFF = 0 - NOISE_REDUCTION_MODE_FAST = 1 - NOISE_REDUCTION_MODE_HIGH_QUALITY = 2 - NOISE_REDUCTION_MODE_MINIMAL = 3 - NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG = 4 - - # TONEMAP modes - TONEMAP_MODE_CONTRAST_CURVE = 0 - TONEMAP_MODE_FAST = 1 - TONEMAP_MODE_HIGH_QUALITY = 2 - TONEMAP_MODE_GAMMA_VALUE = 3 - TONEMAP_MODE_PRESET_CURVE = 4 - - # COLOR_CORRECTION modes - COLOR_CORRECTION_MODE_TRANSFORM_MATRIX = 0 - COLOR_CORRECTION_MODE_FAST = 1 - COLOR_CORRECTION_MODE_HIGH_QUALITY = 2 - - # COLOR_CORRECTION_ABERRATION modes - COLOR_CORRECTION_ABERRATION_MODE_OFF = 0 - COLOR_CORRECTION_ABERRATION_MODE_FAST = 1 - COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY = 2 - - # CONTROL_AE_PRECAPTURE_TRIGGER - CONTROL_AE_PRECAPTURE_TRIGGER_IDLE = 0 - CONTROL_AE_PRECAPTURE_TRIGGER_START = 1 - CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL = 2 - - # CONTROL_EFFECT modes - CONTROL_EFFECT_MODE_OFF = 0 - CONTROL_EFFECT_MODE_MONO = 1 - CONTROL_EFFECT_MODE_NEGATIVE = 2 - CONTROL_EFFECT_MODE_SOLARIZE = 3 - CONTROL_EFFECT_MODE_SEPIA = 4 - CONTROL_EFFECT_MODE_POSTERIZE = 5 - CONTROL_EFFECT_MODE_WHITEBOARD = 6 - CONTROL_EFFECT_MODE_BLACKBOARD = 7 - CONTROL_EFFECT_MODE_AQUA = 8 - - # CONTROL_SCENE modes - CONTROL_SCENE_MODE_DISABLED = 0 - CONTROL_SCENE_MODE_FACE_PRIORITY = 1 - CONTROL_SCENE_MODE_ACTION = 2 - CONTROL_SCENE_MODE_PORTRAIT = 3 - CONTROL_SCENE_MODE_LANDSCAPE = 4 - CONTROL_SCENE_MODE_NIGHT = 5 - CONTROL_SCENE_MODE_NIGHT_PORTRAIT = 6 - CONTROL_SCENE_MODE_THEATRE = 7 - CONTROL_SCENE_MODE_BEACH = 8 - CONTROL_SCENE_MODE_SNOW = 9 - CONTROL_SCENE_MODE_SUNSET = 10 - CONTROL_SCENE_MODE_STEADYPHOTO = 11 - CONTROL_SCENE_MODE_FIREWORKS = 12 - CONTROL_SCENE_MODE_SPORTS = 13 - CONTROL_SCENE_MODE_PARTY = 14 - CONTROL_SCENE_MODE_CANDLELIGHT = 15 - CONTROL_SCENE_MODE_BARCODE = 16 - CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17 - CONTROL_SCENE_MODE_HDR = 18 - - # CONTROL_VIDEO_STABILIZATION modes - CONTROL_VIDEO_STABILIZATION_MODE_OFF = 0 - CONTROL_VIDEO_STABILIZATION_MODE_ON = 1 - - # DISTORTION_CORRECTION modes - DISTORTION_CORRECTION_MODE_OFF = 0 - DISTORTION_CORRECTION_MODE_FAST = 1 - DISTORTION_CORRECTION_MODE_HIGH_QUALITY = 2 - - # HOT_PIXEL modes - HOT_PIXEL_MODE_OFF = 0 - HOT_PIXEL_MODE_FAST = 1 - HOT_PIXEL_MODE_HIGH_QUALITY = 2 - - # LENS_OPTICAL_STABILIZATION modes - LENS_OPTICAL_STABILIZATION_MODE_OFF = 0 - LENS_OPTICAL_STABILIZATION_MODE_ON = 1 - - # LENS_FACING - LENS_FACING_FRONT = 0 - LENS_FACING_BACK = 1 - LENS_FACING_EXTERNAL = 2 - - # INFO_SUPPORTED_HARDWARE_LEVEL - INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0 - INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1 - INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2 - INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3 - INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL = 4 - - -# ═══════════════════════════════════════════════════════════════ -# CameraCharacteristics -# ═══════════════════════════════════════════════════════════════ - -class CameraCharacteristics(CameraMetadata): - """CameraCharacteristics — 相机特性""" - - LENS_FACING = "android.lens.facing" - SENSOR_ORIENTATION = "android.sensor.orientation" - FLASH_INFO_AVAILABLE = "android.flash.info.available" - INFO_SUPPORTED_HARDWARE_LEVEL = "android.info.supportedHardwareLevel" - SCALER_STREAM_CONFIGURATION_MAP = "android.scaler.streamConfigurationMap" - CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES = "android.control.aeAvailableTargetFpsRanges" - CONTROL_AE_AVAILABLE_MODES = "android.control.aeAvailableModes" - CONTROL_AF_AVAILABLE_MODES = "android.control.afAvailableModes" - CONTROL_AVAILABLE_EFFECTS = "android.control.availableEffects" - CONTROL_AVAILABLE_SCENE_MODES = "android.control.availableSceneModes" - CONTROL_MAX_REGIONS_AE = "android.control.maxRegionsAe" - CONTROL_MAX_REGIONS_AF = "android.control.maxRegionsAf" - CONTROL_MAX_REGIONS_AWB = "android.control.maxRegionsAwb" - JPEG_AVAILABLE_THUMBNAIL_SIZES = "android.jpeg.availableThumbnailSizes" - JPEG_MAX_SIZE = "android.jpeg.maxSize" - SENSOR_INFO_PIXEL_ARRAY_SIZE = "android.sensor.info.pixelArraySize" - SENSOR_INFO_ACTIVE_ARRAY_SIZE = "android.sensor.info.activeArraySize" - FLASH_MODE = "android.flash.mode" - - def __init__(self, camera_id: str = "0"): - super().__init__() - self._properties: Dict[str, Any] = { - CameraCharacteristics.LENS_FACING: CameraCharacteristics.LENS_FACING_BACK, - CameraCharacteristics.SENSOR_ORIENTATION: 90, - CameraCharacteristics.FLASH_INFO_AVAILABLE: True, - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL: - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, - CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP: { - "output_sizes": [(1920, 1080), (1280, 720), (640, 480)], - "input_sizes": [(1920, 1080)], - }, - CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES: [(15, 30), (30, 30)], - CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES: [0, 1, 2, 3, 4], - CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES: [0, 1, 2, 3, 4, 5], - CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS: list(range(9)), - CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES: list(range(19)), - CameraCharacteristics.CONTROL_MAX_REGIONS_AE: 1, - CameraCharacteristics.CONTROL_MAX_REGIONS_AF: 1, - CameraCharacteristics.CONTROL_MAX_REGIONS_AWB: 0, - CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES: [(320, 240), (160, 120)], - CameraCharacteristics.JPEG_MAX_SIZE: 5000000, - CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE: (4032, 3024), - CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE: (4032, 3024), - } - - def get(self, key: str) -> Any: - return self._properties.get(key) - - def keys(self) -> Set[str]: - return set(self._properties.keys()) - - def query(self, key: str) -> Any: - return self._properties.get(key) - - -# ═══════════════════════════════════════════════════════════════ -# CameraId -# ═══════════════════════════════════════════════════════════════ - -class CameraId(Stub): - """CameraId — 相机ID封装""" - - def __init__(self, id_string: str): - super().__init__() - self._id = id_string - - def getId(self) -> str: - return self._id - - def toString(self) -> str: - return self._id - - def equals(self, other) -> bool: - if isinstance(other, CameraId): - return self._id == other._id - if isinstance(other, str): - return self._id == other - return False - - def hashCode(self) -> int: - return hash(self._id) - - -# ═══════════════════════════════════════════════════════════════ -# CameraCharacteristicsKey -# ═══════════════════════════════════════════════════════════════ - -class CameraCharacteristicsKey(Stub): - """CameraCharacteristicsKey — 相机特性键""" - - def __init__(self, name: str, native_type: type = object): - super().__init__() - self._name = name - self._native_type = native_type - - def getName(self) -> str: - return self._name - - def getNativeType(self) -> type: - return self._native_type - - def equals(self, other) -> bool: - if isinstance(other, CameraCharacteristicsKey): - return self._name == other._name - return False - - def hashCode(self) -> int: - return hash(self._name) - - def toString(self) -> str: - return f"CameraCharacteristicsKey({self._name})" - - -# ═══════════════════════════════════════════════════════════════ -# CaptureRequest -# ═══════════════════════════════════════════════════════════════ - -class CaptureRequest(CameraMetadata): - """CaptureRequest — 捕获请求""" - - CONTROL_AE_MODE = "android.control.aeMode" - CONTROL_AF_MODE = "android.control.afMode" - CONTROL_AWB_MODE = "android.control.awbMode" - CONTROL_MODE = "android.control.mode" - FLASH_MODE = "android.flash.mode" - SENSOR_EXPOSURE_TIME = "android.sensor.exposureTime" - SENSOR_SENSITIVITY = "android.sensor.sensitivity" - LENS_FOCUS_DISTANCE = "android.lens.focusDistance" - STATISTICS_FACE_DETECT_MODE = "android.statistics.faceDetectMode" - JPEG_QUALITY = "android.jpeg.quality" - JPEG_ORIENTATION = "android.jpeg.orientation" - JPEG_THUMBNAIL_QUALITY = "android.jpeg.thumbnailQuality" - JPEG_THUMBNAIL_SIZE = "android.jpeg.thumbnailSize" - SCALER_CROP_REGION = "android.scaler.cropRegion" - EDGE_MODE = "android.edge.mode" - NOISE_REDUCTION_MODE = "android.noiseReduction.mode" - TONEMAP_MODE = "android.tonemap.mode" - BLACK_LEVEL_LOCK = "android.blackLevel.lock" - CONTROL_AE_TARGET_FPS_RANGE = "android.control.aeTargetFpsRange" - CONTROL_AE_EXPOSURE_COMPENSATION = "android.control.aeExposureCompensation" - CONTROL_AE_LOCK = "android.control.aeLock" - CONTROL_AE_PRECAPTURE_TRIGGER = "android.control.aePrecaptureTrigger" - CONTROL_AWB_LOCK = "android.control.awbLock" - CONTROL_EFFECT_MODE = "android.control.effectMode" - CONTROL_SCENE_MODE = "android.control.sceneMode" - CONTROL_VIDEO_STABILIZATION_MODE = "android.control.videoStabilizationMode" - COLOR_CORRECTION_MODE = "android.colorCorrection.mode" - COLOR_CORRECTION_GAINS = "android.colorCorrection.gains" - COLOR_CORRECTION_TRANSFORM = "android.colorCorrection.transform" - COLOR_CORRECTION_ABERRATION_MODE = "android.colorCorrection.aberrationMode" - CONTROL_POST_RAW_SENSITIVITY_BOOST = "android.control.postRawSensitivityBoost" - DISTORTION_CORRECTION_MODE = "android.distortionCorrection.mode" - HOT_PIXEL_MODE = "android.hotPixel.mode" - LENS_OPTICAL_STABILIZATION_MODE = "android.lens.opticalStabilizationMode" - REQUEST_PIPELINE_MAX_DEPTH = "android.request.pipelineMaxDepth" - SENSOR_TIMESTAMP = "android.sensor.timestamp" - STATISTICS_LENS_SHADING_MAP_MODE = "android.statistics.lensShadingMapMode" - STATISTICS_OIS_DATA_MODE = "android.statistics.oisDataMode" - STATISTICS_SECTION = "android.statistics.section" - TONEMAP_CURVE = "android.tonemap.curve" - TONEMAP_GAMMA = "android.tonemap.gamma" - - class Builder(Stub): - """CaptureRequest.Builder — 捕获请求构建器""" - - def __init__(self, camera_device: "CameraDevice"): - super().__init__() - self._parameters: Dict[str, Any] = {} - self._tag = None - self._camera_device = camera_device - - def set(self, key: str, value: Any): - self._parameters[key] = value - - def get(self, key: str) -> Any: - return self._parameters.get(key) - - def setTag(self, tag: Any): - self._tag = tag - - def build(self) -> "CaptureRequest": - req = CaptureRequest() - req._parameters = dict(self._parameters) - req._tag = self._tag - return req - - def addTarget(self, surface): - pass - - def removeTarget(self, surface): - pass - - def __init__(self): - super().__init__() - self._parameters: Dict[str, Any] = { - CaptureRequest.CONTROL_AE_MODE: CameraMetadata.CONTROL_AE_MODE_ON, - CaptureRequest.CONTROL_AF_MODE: CameraMetadata.CONTROL_AF_MODE_AUTO, - CaptureRequest.CONTROL_AWB_MODE: CameraMetadata.CONTROL_AWB_MODE_AUTO, - CaptureRequest.CONTROL_MODE: CameraMetadata.CONTROL_MODE_AUTO, - CaptureRequest.FLASH_MODE: CameraMetadata.FLASH_MODE_OFF, - CaptureRequest.JPEG_QUALITY: 95, - CaptureRequest.JPEG_ORIENTATION: 0, - } - self._tag = None - self._targets: List[Any] = [] - - def get(self, key: str) -> Any: - return self._parameters.get(key) - - def keys(self) -> Set[str]: - return set(self._parameters.keys()) - - def getTag(self) -> Any: - return self._tag - - -# ═══════════════════════════════════════════════════════════════ -# CaptureResult -# ═══════════════════════════════════════════════════════════════ - -class CaptureResult(CameraMetadata): - """CaptureResult — 捕获结果""" - - # Same keys as CaptureRequest - CONTROL_AE_MODE = CaptureRequest.CONTROL_AE_MODE - CONTROL_AF_MODE = CaptureRequest.CONTROL_AF_MODE - CONTROL_AWB_MODE = CaptureRequest.CONTROL_AWB_MODE - CONTROL_MODE = CaptureRequest.CONTROL_MODE - FLASH_MODE = CaptureRequest.FLASH_MODE - SENSOR_EXPOSURE_TIME = CaptureRequest.SENSOR_EXPOSURE_TIME - SENSOR_SENSITIVITY = CaptureRequest.SENSOR_SENSITIVITY - LENS_FOCUS_DISTANCE = CaptureRequest.LENS_FOCUS_DISTANCE - STATISTICS_FACE_DETECT_MODE = CaptureRequest.STATISTICS_FACE_DETECT_MODE - JPEG_QUALITY = CaptureRequest.JPEG_QUALITY - JPEG_ORIENTATION = CaptureRequest.JPEG_ORIENTATION - JPEG_THUMBNAIL_QUALITY = CaptureRequest.JPEG_THUMBNAIL_QUALITY - JPEG_THUMBNAIL_SIZE = CaptureRequest.JPEG_THUMBNAIL_SIZE - SCALER_CROP_REGION = CaptureRequest.SCALER_CROP_REGION - EDGE_MODE = CaptureRequest.EDGE_MODE - NOISE_REDUCTION_MODE = CaptureRequest.NOISE_REDUCTION_MODE - TONEMAP_MODE = CaptureRequest.TONEMAP_MODE - BLACK_LEVEL_LOCK = CaptureRequest.BLACK_LEVEL_LOCK - CONTROL_AE_TARGET_FPS_RANGE = CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE - CONTROL_AE_EXPOSURE_COMPENSATION = CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION - CONTROL_AE_LOCK = CaptureRequest.CONTROL_AE_LOCK - CONTROL_AE_PRECAPTURE_TRIGGER = CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER - CONTROL_AWB_LOCK = CaptureRequest.CONTROL_AWB_LOCK - CONTROL_EFFECT_MODE = CaptureRequest.CONTROL_EFFECT_MODE - CONTROL_SCENE_MODE = CaptureRequest.CONTROL_SCENE_MODE - CONTROL_VIDEO_STABILIZATION_MODE = CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE - COLOR_CORRECTION_MODE = CaptureRequest.COLOR_CORRECTION_MODE - COLOR_CORRECTION_GAINS = CaptureRequest.COLOR_CORRECTION_GAINS - COLOR_CORRECTION_TRANSFORM = CaptureRequest.COLOR_CORRECTION_TRANSFORM - COLOR_CORRECTION_ABERRATION_MODE = CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE - CONTROL_POST_RAW_SENSITIVITY_BOOST = CaptureRequest.CONTROL_POST_RAW_SENSITIVITY_BOOST - DISTORTION_CORRECTION_MODE = CaptureRequest.DISTORTION_CORRECTION_MODE - HOT_PIXEL_MODE = CaptureRequest.HOT_PIXEL_MODE - LENS_OPTICAL_STABILIZATION_MODE = CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE - REQUEST_PIPELINE_MAX_DEPTH = CaptureRequest.REQUEST_PIPELINE_MAX_DEPTH - SENSOR_TIMESTAMP = CaptureRequest.SENSOR_TIMESTAMP - STATISTICS_LENS_SHADING_MAP_MODE = CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE - STATISTICS_OIS_DATA_MODE = CaptureRequest.STATISTICS_OIS_DATA_MODE - STATISTICS_SECTION = CaptureRequest.STATISTICS_SECTION - TONEMAP_CURVE = CaptureRequest.TONEMAP_CURVE - TONEMAP_GAMMA = CaptureRequest.TONEMAP_GAMMA - - def __init__(self, request: CaptureRequest = None): - super().__init__() - self._results: Dict[str, Any] = { - CaptureResult.CONTROL_AE_MODE: CameraMetadata.CONTROL_AE_MODE_ON, - CaptureResult.CONTROL_AF_MODE: CameraMetadata.CONTROL_AF_MODE_AUTO, - CaptureResult.CONTROL_AWB_MODE: CameraMetadata.CONTROL_AWB_MODE_AUTO, - CaptureResult.CONTROL_MODE: CameraMetadata.CONTROL_MODE_AUTO, - CaptureResult.FLASH_MODE: CameraMetadata.FLASH_MODE_OFF, - CaptureResult.SENSOR_EXPOSURE_TIME: 33333333, - CaptureResult.SENSOR_SENSITIVITY: 100, - CaptureResult.SENSOR_TIMESTAMP: int(time.time() * 1e9), - } - self._request = request - self._sequence_id = -1 - self._frame_number = -1 - - def get(self, key: str) -> Any: - return self._results.get(key) - - def keys(self) -> Set[str]: - return set(self._results.keys()) - - def getRequest(self) -> Optional[CaptureRequest]: - return self._request - - def getSequenceId(self) -> int: - return self._sequence_id - - def getFrameNumber(self) -> int: - return self._frame_number - - -# ═══════════════════════════════════════════════════════════════ -# TotalCaptureResult -# ═══════════════════════════════════════════════════════════════ - -class TotalCaptureResult(CaptureResult): - """TotalCaptureResult — 完整捕获结果""" - - def __init__(self, request: CaptureRequest = None, - partial_results: List[CaptureResult] = None): - super().__init__(request) - self._partial_results = partial_results or [] - - def getPartialResults(self) -> List[CaptureResult]: - return list(self._partial_results) - - -# ═══════════════════════════════════════════════════════════════ -# CameraCaptureSession -# ═══════════════════════════════════════════════════════════════ - -class CameraCaptureSession(Stub): - """CameraCaptureSession — 捕获会话""" - - class StateCallback(Stub): - """CameraCaptureSession.StateCallback — 会话状态回调""" - - def onConfigured(self, session: "CameraCaptureSession"): - pass - - def onConfigureFailed(self, session: "CameraCaptureSession"): - pass - - def onReady(self, session: "CameraCaptureSession"): - pass - - def onActive(self, session: "CameraCaptureSession"): - pass - - def onClosed(self, session: "CameraCaptureSession"): - pass - - def onSurfacePrepared(self, session: "CameraCaptureSession", surface): - pass - - class CaptureCallback(Stub): - """CameraCaptureSession.CaptureCallback — 捕获回调""" - - def onCaptureStarted(self, session: "CameraCaptureSession", request: CaptureRequest, - timestamp: int, frameNumber: int): - pass - - def onCaptureCompleted(self, session: "CameraCaptureSession", request: CaptureRequest, - result: TotalCaptureResult): - pass - - def onCaptureFailed(self, session: "CameraCaptureSession", request: CaptureRequest, - failure): - pass - - def onCaptureProgressed(self, session: "CameraCaptureSession", request: CaptureRequest, - result: CaptureResult): - pass - - def onCaptureSequenceCompleted(self, session: "CameraCaptureSession", - sequenceId: int, frameNumber: int): - pass - - def onCaptureSequenceAborted(self, session: "CameraCaptureSession", sequenceId: int): - pass - - def onCaptureBufferLost(self, session: "CameraCaptureSession", request: CaptureRequest, - target, frameNumber: int): - pass - - def __init__(self, device: "CameraDevice"): - super().__init__() - self._device = device - self._closed = False - self._repeating_request = None - self._state_callback = None - self._session_id = int(time.time() * 1000) - - def capture(self, request: CaptureRequest, callback: CaptureCallback = None, - handler=None) -> int: - seq_id = int(time.time() * 1000) - result = TotalCaptureResult(request) - if callback: - callback.onCaptureStarted(self, request, int(time.time() * 1e9), 0) - callback.onCaptureCompleted(self, request, result) - callback.onCaptureSequenceCompleted(self, seq_id, 0) - return seq_id - - def setRepeatingRequest(self, request: CaptureRequest, callback: CaptureCallback = None, - handler=None) -> int: - self._repeating_request = request - seq_id = int(time.time() * 1000) - result = TotalCaptureResult(request) - if callback: - callback.onCaptureStarted(self, request, int(time.time() * 1e9), 0) - callback.onCaptureCompleted(self, request, result) - return seq_id - - def stopRepeatingRequest(self) -> None: - self._repeating_request = None - - def abortCaptures(self) -> None: - pass - - def close(self): - self._closed = True - - def getDevice(self) -> "CameraDevice": - return self._device - - def isClosed(self) -> bool: - return self._closed - - -# ═══════════════════════════════════════════════════════════════ -# CameraDevice -# ═══════════════════════════════════════════════════════════════ - -class CameraDevice(Stub): - """CameraDevice — 相机设备""" - - STATE_OPENED = 0 - STATE_CLOSED = 3 - STATE_DISCONNECTED = 1 - STATE_ERROR = 2 - - class StateCallback(Stub): - """CameraDevice.StateCallback — 设备状态回调""" - - def onOpened(self, device: "CameraDevice"): - pass - - def onClosed(self, device: "CameraDevice"): - pass - - def onDisconnected(self, device: "CameraDevice"): - pass - - def onError(self, device: "CameraDevice", error: int): - pass - - def __init__(self, camera_id: str = "0"): - super().__init__() - self._id = camera_id - self._state = CameraDevice.STATE_OPENED - self._state_callback = None - self._sessions: List[CameraCaptureSession] = [] - - def getId(self) -> str: - return self._id - - def createCaptureRequest(self, templateType: int) -> CaptureRequest.Builder: - return CaptureRequest.Builder(self) - - def createCaptureSession(self, outputs: List[Any], - callback: CameraCaptureSession.StateCallback, - handler=None): - session = CameraCaptureSession(self) - session._state_callback = callback - self._sessions.append(session) - if callback: - callback.onConfigured(session) - return session - - def close(self): - self._state = CameraDevice.STATE_CLOSED - for session in self._sessions: - session.close() - - def getState(self) -> int: - return self._state - - def toString(self) -> str: - return f"CameraDevice@{self._id}" - - -# ═══════════════════════════════════════════════════════════════ -# CameraManager -# ═══════════════════════════════════════════════════════════════ - -class CameraManager(Stub): - """CameraManager — 相机管理器""" - - def __init__(self): - super().__init__() - self._cameras: Dict[str, CameraCharacteristics] = { - "0": CameraCharacteristics("0"), - } - self._open_devices: Dict[str, CameraDevice] = {} - self._availability_callbacks: List[Any] = [] - self._torch_callbacks: List[Any] = [] - - def getCameraIdList(self) -> List[str]: - return list(self._cameras.keys()) - - def getCameraCharacteristics(self, cameraId: str) -> CameraCharacteristics: - if cameraId not in self._cameras: - raise AndroidOnlyError(f"Camera {cameraId} not available") - return self._cameras[cameraId] - - def openCamera(self, cameraId: str, callback: CameraDevice.StateCallback, - handler=None): - if cameraId not in self._cameras: - if callback: - callback.onError(None, CameraDevice.STATE_ERROR) - return - device = CameraDevice(cameraId) - self._open_devices[cameraId] = device - if callback: - callback.onOpened(device) - - def registerAvailabilityCallback(self, callback): - self._availability_callbacks.append(callback) - - def unregisterAvailabilityCallback(self, callback): - if callback in self._availability_callbacks: - self._availability_callbacks.remove(callback) - - def registerTorchCallback(self, callback, handler=None): - self._torch_callbacks.append(callback) - - def unregisterTorchCallback(self, callback): - if callback in self._torch_callbacks: - self._torch_callbacks.remove(callback) - - def setTorchMode(self, cameraId: str, enabled: bool): - pass - - -# ═══════════════════════════════════════════════════════════════ -# CameraOfflineSession -# ═══════════════════════════════════════════════════════════════ - -class CameraOfflineSession(CameraCaptureSession): - """CameraOfflineSession — 离线会话 (API 30+)""" - - def __init__(self, device: CameraDevice): - super().__init__(device) - - def switchToOffline(self): - pass - - -# ═══════════════════════════════════════════════════════════════ -# CameraExtensionSession -# ═══════════════════════════════════════════════════════════════ - -class CameraExtensionSession(Stub): - """CameraExtensionSession — 扩展会话 (API 31+)""" - - class StateCallback(Stub): - def onConfigured(self, session: "CameraExtensionSession"): - pass - - def onConfigureFailed(self, session: "CameraExtensionSession"): - pass - - def onClosed(self, session: "CameraExtensionSession"): - pass - - class ExtensionCaptureCallback(Stub): - def onCaptureStarted(self, session: "CameraExtensionSession", request: CaptureRequest, - timestamp: int): - pass - - def onCaptureProcessStarted(self, session: "CameraExtensionSession", request: CaptureRequest): - pass - - def onCaptureFailed(self, session: "CameraExtensionSession", request: CaptureRequest): - pass - - def onCaptureResultAvailable(self, session: "CameraExtensionSession", - request: CaptureRequest, result: TotalCaptureResult): - pass - - def __init__(self): - super().__init__() - self._closed = False - - def close(self): - self._closed = True - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "CameraManager", "CameraDevice", "CameraCaptureSession", - "CaptureRequest", "CaptureResult", "CameraCharacteristics", - "CameraMetadata", "CameraId", "CameraCharacteristicsKey", - "TotalCaptureResult", "CameraOfflineSession", "CameraExtensionSession", -] \ No newline at end of file diff --git a/android_pyapi/android/hardware_camera2_params.py b/android_pyapi/android/hardware_camera2_params.py deleted file mode 100644 index c3da2f8..0000000 --- a/android_pyapi/android/hardware_camera2_params.py +++ /dev/null @@ -1,854 +0,0 @@ -""" -Android Hardware Camera2 Params 组件 — RggbChannelVector, BlackLevelPattern, StreamConfigurationMap 等 -方法签名完全对齐 Android SDK (android.hardware.camera2.params 包) -""" - -from __future__ import annotations - -import math -from typing import Any, Dict, List, Optional, Sequence, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# RggbChannelVector -# ═══════════════════════════════════════════════════════════════ - -class RggbChannelVector(Stub): - """RggbChannelVector — RGGB 通道向量""" - - COUNT = 4 - - def __init__(self, red: float, green_even: float, green_odd: float, blue: float): - super().__init__() - self._red = red - self._green_even = green_even - self._green_odd = green_odd - self._blue = blue - - def getRed(self) -> float: - return self._red - - def getGreenEven(self) -> float: - return self._green_even - - def getGreenOdd(self) -> float: - return self._green_odd - - def getBlue(self) -> float: - return self._blue - - def getComponent(self, i: int) -> float: - components = [self._red, self._green_even, self._green_odd, self._blue] - return components[i] - - def toFloatArray(self) -> List[float]: - return [self._red, self._green_even, self._green_odd, self._blue] - - def copyTo(self, destination: List[float], offset: int = 0): - for i, v in enumerate([self._red, self._green_even, self._green_odd, self._blue]): - destination[offset + i] = v - - def equals(self, other: Any) -> bool: - if isinstance(other, RggbChannelVector): - return (abs(self._red - other._red) < 1e-6 and - abs(self._green_even - other._green_even) < 1e-6 and - abs(self._green_odd - other._green_odd) < 1e-6 and - abs(self._blue - other._blue) < 1e-6) - return False - - def hashCode(self) -> int: - return hash((self._red, self._green_even, self._green_odd, self._blue)) & 0x7FFFFFFF - - def toString(self) -> str: - return f"RggbChannelVector(R={self._red:.3f}, Ge={self._green_even:.3f}, Go={self._green_odd:.3f}, B={self._blue:.3f})" - - -# ═══════════════════════════════════════════════════════════════ -# BlackLevelPattern -# ═══════════════════════════════════════════════════════════════ - -class BlackLevelPattern(Stub): - """BlackLevelPattern — 黑电平模式""" - - COUNT = 4 - - def __init__(self, red: int = 0, green_even: int = 0, - green_odd: int = 0, blue: int = 0): - super().__init__() - self._offsets = [red, green_even, green_odd, blue] - - def getOffsetForIndex(self, column: int, row: int) -> int: - idx = (row % 2) * 2 + (column % 2) - return self._offsets[idx] - - def copyTo(self, destination: List[int], offset: int = 0): - for i, v in enumerate(self._offsets): - destination[offset + i] = v - - def getRed(self) -> int: - return self._offsets[0] - - def getGreenEven(self) -> int: - return self._offsets[1] - - def getGreenOdd(self) -> int: - return self._offsets[2] - - def getBlue(self) -> int: - return self._offsets[3] - - def equals(self, other: Any) -> bool: - if isinstance(other, BlackLevelPattern): - return self._offsets == other._offsets - return False - - def hashCode(self) -> int: - return hash(tuple(self._offsets)) & 0x7FFFFFFF - - def toString(self) -> str: - return f"BlackLevelPattern(R={self._offsets[0]}, Ge={self._offsets[1]}, Go={self._offsets[2]}, B={self._offsets[3]})" - - -# ═══════════════════════════════════════════════════════════════ -# ColorSpaceTransform -# ═══════════════════════════════════════════════════════════════ - -class ColorSpaceTransform(Stub): - """ColorSpaceTransform — 色彩空间变换 (3x3 矩阵)""" - - COUNT = 9 - ELEMENTS_PER_ROW = 3 - ROWS = 3 - COLUMNS = 3 - - def __init__(self, elements: Optional[List[Rational]] = None): - super().__init__() - if elements is not None and len(elements) == 9: - self._elements = list(elements) - else: - self._elements = [Rational(1, 1) if i % 4 == 0 else Rational(0, 1) for i in range(9)] - - def getElement(self, column: int, row: int) -> Rational: - return self._elements[row * 3 + column] - - def setElement(self, column: int, row: int, element: Rational): - self._elements[row * 3 + column] = element - - def copyTo(self, destination: List[Rational], offset: int = 0): - for i, v in enumerate(self._elements): - destination[offset + i] = v - - def getElements(self) -> List[Rational]: - return list(self._elements) - - def transform(self, r: float, g: float, b: float) -> Tuple[float, float, float]: - e = self._elements - r_out = e[0].toFloat() * r + e[1].toFloat() * g + e[2].toFloat() * b - g_out = e[3].toFloat() * r + e[4].toFloat() * g + e[5].toFloat() * b - b_out = e[6].toFloat() * r + e[7].toFloat() * g + e[8].toFloat() * b - return (r_out, g_out, b_out) - - def equals(self, other: Any) -> bool: - if isinstance(other, ColorSpaceTransform): - return all(abs(a.toFloat() - b.toFloat()) < 1e-6 - for a, b in zip(self._elements, other._elements)) - return False - - def hashCode(self) -> int: - return hash(tuple(self._elements)) & 0x7FFFFFFF - - def toString(self) -> str: - return (f"ColorSpaceTransform([{self._elements[0]}, {self._elements[1]}, {self._elements[2]}," - f" {self._elements[3]}, {self._elements[4]}, {self._elements[5]}," - f" {self._elements[6]}, {self._elements[7]}, {self._elements[8]}])") - - -# ═══════════════════════════════════════════════════════════════ -# Rational -# ═══════════════════════════════════════════════════════════════ - -class Rational(Stub): - """Rational — 有理数""" - - def __init__(self, numerator: int = 0, denominator: int = 1): - super().__init__() - if denominator < 0: - numerator = -numerator - denominator = -denominator - gcd = math.gcd(abs(numerator), abs(denominator)) if denominator != 0 else 1 - self._numerator = numerator // gcd - self._denominator = denominator // gcd - - def getNumerator(self) -> int: - return self._numerator - - def getDenominator(self) -> int: - return self._denominator - - def toFloat(self) -> float: - if self._denominator == 0: - return float('inf') if self._numerator >= 0 else float('-inf') - return self._numerator / self._denominator - - def toDouble(self) -> float: - return self.toFloat() - - def equals(self, other: Any) -> bool: - if isinstance(other, Rational): - return (self._numerator * other._denominator == - other._numerator * self._denominator) - return False - - def hashCode(self) -> int: - return hash((self._numerator, self._denominator)) & 0x7FFFFFFF - - def toString(self) -> str: - return f"{self._numerator}/{self._denominator}" - - -# ═══════════════════════════════════════════════════════════════ -# StreamConfigurationMap -# ═══════════════════════════════════════════════════════════════ - -class StreamConfigurationMap(Stub): - """StreamConfigurationMap — 流配置映射""" - - def __init__(self): - super().__init__() - self._output_formats: Dict[int, List[Tuple[int, int]]] = {} - self._input_formats: Dict[int, List[Tuple[int, int]]] = {} - self._high_speed_video_sizes: Dict[int, List[Tuple[int, int]]] = {} - self._output_min_frame_durations: Dict[Tuple[int, int, int], int] = {} - self._output_stall_durations: Dict[Tuple[int, int, int], int] = {} - self._input_min_frame_durations: Dict[Tuple[int, int, int], int] = {} - self._input_stall_durations: Dict[Tuple[int, int, int], int] = {} - self._mutable: bool = True - - def getOutputFormats(self) -> List[int]: - return list(self._output_formats.keys()) - - def getInputFormats(self) -> List[int]: - return list(self._input_formats.keys()) - - def getOutputSizes(self, format: int) -> List[Tuple[int, int]]: - return list(self._output_formats.get(format, [])) - - def getInputSizes(self, format: int) -> List[Tuple[int, int]]: - return list(self._input_formats.get(format, [])) - - def getHighSpeedVideoSizes(self) -> List[Tuple[int, int]]: - result = set() - for sizes in self._high_speed_video_sizes.values(): - result.update(sizes) - return list(result) - - def getHighSpeedVideoFpsRangeFor(self, size: Tuple[int, int]) -> List[Tuple[int, int]]: - return [(30, 30), (60, 60), (120, 120)] - - def getHighSpeedVideoFpsRanges(self) -> List[Tuple[int, int]]: - return [(30, 30), (60, 60), (120, 120), (240, 240)] - - def getOutputMinFrameDuration(self, format: int, size: Tuple[int, int]) -> int: - return self._output_min_frame_durations.get((format, size[0], size[1]), 33333333) - - def getOutputStallDuration(self, format: int, size: Tuple[int, int]) -> int: - return self._output_stall_durations.get((format, size[0], size[1]), 0) - - def getInputMinFrameDuration(self, format: int, size: Tuple[int, int]) -> int: - return self._input_min_frame_durations.get((format, size[0], size[1]), 33333333) - - def getInputStallDuration(self, format: int, size: Tuple[int, int]) -> int: - return self._input_stall_durations.get((format, size[0], size[1]), 0) - - def isOutputSupportedFor(self, size: Tuple[int, int]) -> bool: - for sizes in self._output_formats.values(): - if size in sizes: - return True - return False - - def isValid(self) -> bool: - return len(self._output_formats) > 0 - - def setMutable(self, mutable: bool): - self._mutable = mutable - - def isMutable(self) -> bool: - return self._mutable - - def addOutputFormat(self, format: int, sizes: List[Tuple[int, int]]): - self._output_formats[format] = list(sizes) - - def addInputFormat(self, format: int, sizes: List[Tuple[int, int]]): - self._input_formats[format] = list(sizes) - - def toString(self) -> str: - return f"StreamConfigurationMap(outputFormats={len(self._output_formats)})" - - -# ═══════════════════════════════════════════════════════════════ -# TonemapCurve -# ═══════════════════════════════════════════════════════════════ - -class TonemapCurve(Stub): - """TonemapCurve — 色调映射曲线""" - - LEVEL_BLACK = 0 - LEVEL_WHITE = 1 - - CHANNEL_RED = 0 - CHANNEL_GREEN = 1 - CHANNEL_BLUE = 2 - - POINT_SIZE = 2 - - def __init__(self, red_curve: Optional[List[float]] = None, - green_curve: Optional[List[float]] = None, - blue_curve: Optional[List[float]] = None): - super().__init__() - self._curves = { - TonemapCurve.CHANNEL_RED: list(red_curve or [0.0, 0.0, 1.0, 1.0]), - TonemapCurve.CHANNEL_GREEN: list(green_curve or [0.0, 0.0, 1.0, 1.0]), - TonemapCurve.CHANNEL_BLUE: list(blue_curve or [0.0, 0.0, 1.0, 1.0]), - } - - def getPointCount(self, channel: int) -> int: - return len(self._curves.get(channel, [])) // 2 - - def getPoint(self, channel: int, index: int, out_point: List[float] = None) -> Tuple[float, float]: - curve = self._curves.get(channel, []) - if out_point is None: - out_point = [0.0, 0.0] - out_point[0] = curve[index * 2] - out_point[1] = curve[index * 2 + 1] - return (out_point[0], out_point[1]) - - def setPoint(self, channel: int, index: int, x: float, y: float): - curve = self._curves[channel] - while len(curve) < (index * 2 + 2): - curve.extend([0.0, 0.0]) - curve[index * 2] = x - curve[index * 2 + 1] = y - - def getCurve(self, channel: int) -> Optional[List[float]]: - return list(self._curves.get(channel, [])) - - def estimateCurve(self, channel: int, x: float) -> float: - curve = self._curves.get(channel, []) - if not curve or len(curve) < 4: - return x - for i in range(0, len(curve) - 2, 2): - x0, y0 = curve[i], curve[i + 1] - x1, y1 = curve[i + 2], curve[i + 3] - if x0 <= x <= x1 or (x1 <= x <= x0): - if abs(x1 - x0) < 1e-10: - return y0 - t = (x - x0) / (x1 - x0) - return y0 + t * (y1 - y0) - return curve[-1] if curve else x - - def equals(self, other: Any) -> bool: - if isinstance(other, TonemapCurve): - return all(abs(self._curves[c][i] - other._curves[c][i]) < 1e-6 - for c in range(3) for i in range(len(self._curves[c]))) - return False - - def hashCode(self) -> int: - return hash(tuple(tuple(v) for v in self._curves.values())) & 0x7FFFFFFF - - def toString(self) -> str: - return f"TonemapCurve(points={self.getPointCount(0)}, {self.getPointCount(1)}, {self.getPointCount(2)})" - - -# ═══════════════════════════════════════════════════════════════ -# LensShadingMap -# ═══════════════════════════════════════════════════════════════ - -class LensShadingMap(Stub): - """LensShadingMap — 镜头阴影映射""" - - def __init__(self, columns: int = 1, rows: int = 1): - super().__init__() - self._columns = columns - self._rows = rows - self._gain_map: Dict[Tuple[int, int, int], float] = {} - - def getGainFactor(self, channel: int, column: int, row: int) -> float: - return self._gain_map.get((channel, column, row), 1.0) - - def setGainFactor(self, channel: int, column: int, row: int, gain: float): - self._gain_map[(channel, column, row)] = gain - - def getGainFactorVector(self, column: int, row: int) -> RggbChannelVector: - r = self.getGainFactor(0, column, row) - ge = self.getGainFactor(1, column, row) - go = self.getGainFactor(2, column, row) - b = self.getGainFactor(3, column, row) - return RggbChannelVector(r, ge, go, b) - - def getColumnCount(self) -> int: - return self._columns - - def getRowCount(self) -> int: - return self._rows - - def copyGainFactors(self, destination: List[float], offset: int = 0): - for (ch, col, row), gain in self._gain_map.items(): - idx = offset + (row * self._columns + col) * 4 + ch - if idx < len(destination): - destination[idx] = gain - - def equals(self, other: Any) -> bool: - if isinstance(other, LensShadingMap): - return (self._columns == other._columns and self._rows == other._rows - and self._gain_map == other._gain_map) - return False - - def hashCode(self) -> int: - return hash((self._columns, self._rows, tuple(sorted(self._gain_map.items())))) & 0x7FFFFFFF - - def toString(self) -> str: - return f"LensShadingMap({self._columns}x{self._rows})" - - -# ═══════════════════════════════════════════════════════════════ -# MeteringRectangle -# ═══════════════════════════════════════════════════════════════ - -class MeteringRectangle(Stub): - """MeteringRectangle — 测光矩形""" - - def __init__(self, x: int = 0, y: int = 0, width: int = 0, - height: int = 0, metering_weight: int = 1): - super().__init__() - self._x = x - self._y = y - self._width = width - self._height = height - self._metering_weight = metering_weight - - def getX(self) -> int: - return self._x - - def getY(self) -> int: - return self._y - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def getMeteringWeight(self) -> int: - return self._metering_weight - - def getRect(self) -> Tuple[int, int, int, int]: - return (self._x, self._y, self._x + self._width, self._y + self._height) - - def equals(self, other: Any) -> bool: - if isinstance(other, MeteringRectangle): - return (self._x == other._x and self._y == other._y - and self._width == other._width - and self._height == other._height - and self._metering_weight == other._metering_weight) - return False - - def hashCode(self) -> int: - return hash((self._x, self._y, self._width, self._height, self._metering_weight)) & 0x7FFFFFFF - - def toString(self) -> str: - return f"MeteringRectangle({self._x},{self._y} {self._width}x{self._height} w={self._metering_weight})" - - -# ═══════════════════════════════════════════════════════════════ -# OutputConfiguration -# ═══════════════════════════════════════════════════════════════ - -class OutputConfiguration(Stub): - """OutputConfiguration — 输出配置""" - - SURFACE_TYPE_UNKNOWN = -1 - SURFACE_TYPE_SURFACE_VIEW = 0 - SURFACE_TYPE_SURFACE_TEXTURE = 1 - SURFACE_TYPE_IMAGE_READER = 2 - SURFACE_TYPE_MEDIA_CODEC = 3 - SURFACE_TYPE_MEDIA_RECORDER = 4 - - ROTATION_0 = 0 - ROTATION_90 = 1 - ROTATION_180 = 2 - ROTATION_270 = 3 - - def __init__(self, surface: Any = None, surface_type: int = SURFACE_TYPE_UNKNOWN): - super().__init__() - self._surface = surface - self._surface_type = surface_type - self._rotation: int = OutputConfiguration.ROTATION_0 - self._width: int = 0 - self._height: int = 0 - self._format: int = 0 - self._is_deferred: bool = False - self._physical_camera_id: str = "" - self._surface_set: List[Any] = [] - self._shared_surfaces: List[Any] = [] - self._capture_stage: int = 0 - - def getSurface(self) -> Any: - return self._surface - - def setSurface(self, surface: Any): - self._surface = surface - - def getSurfaceType(self) -> int: - return self._surface_type - - def setSurfaceType(self, type_: int): - self._surface_type = type_ - - def getRotation(self) -> int: - return self._rotation - - def setRotation(self, rotation: int): - self._rotation = rotation - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def getFormat(self) -> int: - return self._format - - def setDimensions(self, width: int, height: int): - self._width = width - self._height = height - - def setFormat(self, format: int): - self._format = format - - def isDeferred(self) -> bool: - return self._is_deferred - - def setDeferred(self, deferred: bool): - self._is_deferred = deferred - - def getPhysicalCameraId(self) -> str: - return self._physical_camera_id - - def setPhysicalCameraId(self, camera_id: str): - self._physical_camera_id = camera_id - - def addSurface(self, surface: Any): - self._surface_set.append(surface) - - def removeSurface(self, surface: Any): - self._surface_set = [s for s in self._surface_set if s is not surface] - - def getSurfaces(self) -> List[Any]: - return list(self._surface_set) - - def toString(self) -> str: - return f"OutputConfiguration({self._width}x{self._height}, rot={self._rotation})" - - -# ═══════════════════════════════════════════════════════════════ -# SessionConfiguration -# ═══════════════════════════════════════════════════════════════ - -class SessionConfiguration(Stub): - """SessionConfiguration — 会话配置""" - - SESSION_TYPE_REGULAR = 0 - SESSION_TYPE_VENDOR = 1 - SESSION_TYPE_HIGH_SPEED = 2 - SESSION_TYPE_LOW_LATENCY = 3 - SESSION_TYPE_OFFLINE_PROCESSING = 4 - - def __init__(self, session_type: int = SESSION_TYPE_REGULAR, - outputs: Optional[List[OutputConfiguration]] = None, - callback: Any = None): - super().__init__() - self._session_type = session_type - self._outputs = list(outputs or []) - self._callback = callback - self._input_config: Optional[OutputConfiguration] = None - self._session_params: Any = None - self._implementation: Any = None - self._color_space: int = 0 - self._is_global_session: bool = False - - def getSessionType(self) -> int: - return self._session_type - - def getOutputConfigurations(self) -> List[OutputConfiguration]: - return list(self._outputs) - - def addOutputConfiguration(self, output: OutputConfiguration): - self._outputs.append(output) - - def removeOutputConfiguration(self, output: OutputConfiguration): - self._outputs = [o for o in self._outputs if o is not output] - - def setInputConfiguration(self, input_config: OutputConfiguration): - self._input_config = input_config - - def getInputConfiguration(self) -> Optional[OutputConfiguration]: - return self._input_config - - def setSessionParameters(self, params: Any): - self._session_params = params - - def getSessionParameters(self) -> Any: - return self._session_params - - def setColorSpace(self, color_space: int): - self._color_space = color_space - - def getColorSpace(self) -> int: - return self._color_space - - def getCallback(self) -> Any: - return self._callback - - def toString(self) -> str: - return f"SessionConfiguration(type={self._session_type}, outputs={len(self._outputs)})" - - -# ═══════════════════════════════════════════════════════════════ -# MultiResolutionStreamConfigurationMap -# ═══════════════════════════════════════════════════════════════ - -class MultiResolutionStreamConfigurationMap(Stub): - """MultiResolutionStreamConfigurationMap — 多分辨率流配置映射""" - - def __init__(self): - super().__init__() - self._configs: Dict[int, List[OutputConfiguration]] = {} - - def getOutputConfigurations(self, format: int) -> List[OutputConfiguration]: - return list(self._configs.get(format, [])) - - def getFormats(self) -> List[int]: - return list(self._configs.keys()) - - def addConfiguration(self, format: int, config: OutputConfiguration): - if format not in self._configs: - self._configs[format] = [] - self._configs[format].append(config) - - def removeConfiguration(self, format: int, config: OutputConfiguration): - if format in self._configs: - self._configs[format] = [c for c in self._configs[format] if c is not config] - - def getMaxResolution(self, format: int) -> Optional[OutputConfiguration]: - configs = self._configs.get(format, []) - if not configs: - return None - return max(configs, key=lambda c: c.getWidth() * c.getHeight()) - - def toString(self) -> str: - return f"MultiResolutionStreamConfigurationMap(formats={len(self._configs)})" - - -# ═══════════════════════════════════════════════════════════════ -# OisSample -# ═══════════════════════════════════════════════════════════════ - -class OisSample(Stub): - """OisSample — OIS 样本数据""" - - def __init__(self, timestamp_ns: int = 0, x_shift: float = 0.0, y_shift: float = 0.0): - super().__init__() - self._timestamp_ns = timestamp_ns - self._x_shift = x_shift - self._y_shift = y_shift - - def getTimestampNanos(self) -> int: - return self._timestamp_ns - - def getXshift(self) -> float: - return self._x_shift - - def getYshift(self) -> float: - return self._y_shift - - def equals(self, other: Any) -> bool: - if isinstance(other, OisSample): - return (self._timestamp_ns == other._timestamp_ns - and abs(self._x_shift - other._x_shift) < 1e-6 - and abs(self._y_shift - other._y_shift) < 1e-6) - return False - - def hashCode(self) -> int: - return hash((self._timestamp_ns, self._x_shift, self._y_shift)) & 0x7FFFFFFF - - def toString(self) -> str: - return f"OisSample(ts={self._timestamp_ns}, x={self._x_shift:.3f}, y={self._y_shift:.3f})" - - -# ═══════════════════════════════════════════════════════════════ -# RecommendedStreamConfiguration -# ═══════════════════════════════════════════════════════════════ - -class RecommendedStreamConfiguration(Stub): - """RecommendedStreamConfiguration — 推荐流配置""" - - USECASE_PREVIEW = 0 - USECASE_RECORD = 1 - USECASE_STILL_CAPTURE = 2 - USECASE_VIDEO_SNAPSHOT = 3 - USECASE_ZSL = 4 - USECASE_RAW = 5 - USECASE_LOW_LATENCY_SNAPSHOT = 6 - USECASE_10BIT_OUTPUT = 7 - USECASE_ANALYSIS = 8 - USECASE_CUSTOM = 9 - USECASE_VENDOR_START = 0x1000 - - def __init__(self, width: int = 0, height: int = 0, format: int = 0, - input_output: bool = False, usecase: int = USECASE_PREVIEW): - super().__init__() - self._width = width - self._height = height - self._format = format - self._input_output = input_output - self._usecase = usecase - self._fps_range: Tuple[int, int] = (30, 30) - self._is_high_speed: bool = False - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def getFormat(self) -> int: - return self._format - - def isInput(self) -> bool: - return self._input_output - - def isOutput(self) -> bool: - return not self._input_output - - def getUsecase(self) -> int: - return self._usecase - - def getFpsRange(self) -> Tuple[int, int]: - return self._fps_range - - def setFpsRange(self, min_fps: int, max_fps: int): - self._fps_range = (min_fps, max_fps) - - def isHighSpeed(self) -> bool: - return self._is_high_speed - - def setHighSpeed(self, high_speed: bool): - self._is_high_speed = high_speed - - def toString(self) -> str: - return f"RecommendedStreamConfiguration({self._width}x{self._height}, fmt={self._format})" - - -# ═══════════════════════════════════════════════════════════════ -# RecommendedStreamConfigurationMap -# ═══════════════════════════════════════════════════════════════ - -class RecommendedStreamConfigurationMap(Stub): - """RecommendedStreamConfigurationMap — 推荐流配置映射""" - - def __init__(self): - super().__init__() - self._recommendations: Dict[int, List[RecommendedStreamConfiguration]] = {} - self._max_resolutions: Dict[int, Tuple[int, int]] = {} - - def getRecommendedStreamConfigurations(self, usecase: int) -> List[RecommendedStreamConfiguration]: - return list(self._recommendations.get(usecase, [])) - - def getAllUsecases(self) -> List[int]: - return list(self._recommendations.keys()) - - def addRecommendation(self, usecase: int, config: RecommendedStreamConfiguration): - if usecase not in self._recommendations: - self._recommendations[usecase] = [] - self._recommendations[usecase].append(config) - - def removeRecommendation(self, usecase: int, config: RecommendedStreamConfiguration): - if usecase in self._recommendations: - self._recommendations[usecase] = [ - c for c in self._recommendations[usecase] if c is not config - ] - - def getMaxResolution(self, usecase: int) -> Optional[Tuple[int, int]]: - return self._max_resolutions.get(usecase) - - def setMaxResolution(self, usecase: int, width: int, height: int): - self._max_resolutions[usecase] = (width, height) - - def toString(self) -> str: - return f"RecommendedStreamConfigurationMap(usecases={len(self._recommendations)})" - - -# ═══════════════════════════════════════════════════════════════ -# TotalCaptureResultExt — TotalCaptureResult 的扩展 -# ═══════════════════════════════════════════════════════════════ - -class TotalCaptureResultExt(Stub): - """TotalCaptureResultExt — TotalCaptureResult 的 params 扩展""" - - def __init__(self): - super().__init__() - self._results: Dict[str, Any] = {} - self._partial_results: List[Any] = [] - self._session_id: int = 0 - self._frame_number: int = 0 - self._sequence_id: int = 0 - self._af_trigger_id: int = 0 - self._ae_trigger_id: int = 0 - self._sensor_timestamp: int = 0 - self._physical_camera_results: Dict[str, Any] = {} - - def getResult(self, key: str) -> Optional[Any]: - return self._results.get(key) - - def setResult(self, key: str, value: Any): - self._results[key] = value - - def getAllResults(self) -> Dict[str, Any]: - return dict(self._results) - - def getPartialResults(self) -> List[Any]: - return list(self._partial_results) - - def addPartialResult(self, result: Any): - self._partial_results.append(result) - - def getSessionId(self) -> int: - return self._session_id - - def setSessionId(self, session_id: int): - self._session_id = session_id - - def getFrameNumber(self) -> int: - return self._frame_number - - def setFrameNumber(self, frame: int): - self._frame_number = frame - - def getSequenceId(self) -> int: - return self._sequence_id - - def setSequenceId(self, seq: int): - self._sequence_id = seq - - def getPhysicalCameraResult(self, camera_id: str) -> Optional[Any]: - return self._physical_camera_results.get(camera_id) - - def setPhysicalCameraResult(self, camera_id: str, result: Any): - self._physical_camera_results[camera_id] = result - - def toString(self) -> str: - return f"TotalCaptureResultExt(frame={self._frame_number}, seq={self._sequence_id})" \ No newline at end of file diff --git a/android_pyapi/android/hardware_consumerir.py b/android_pyapi/android/hardware_consumerir.py deleted file mode 100644 index 68ceaae..0000000 --- a/android_pyapi/android/hardware_consumerir.py +++ /dev/null @@ -1,93 +0,0 @@ -""" -Android hardware.consumerir 组件 — ConsumerIrManager, CarrierFrequencyRange 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import threading -from typing import Any, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# ConsumerIrManager -# ═══════════════════════════════════════════════════════════════ - -class ConsumerIrManager(Stub): - """ConsumerIrManager — 红外遥控管理器""" - - def __init__(self): - super().__init__() - self._has_emitter: bool = False - self._carrier_frequencies: List[ConsumerIrManager.CarrierFrequencyRange] = [ - ConsumerIrManager.CarrierFrequencyRange(30000, 60000), - ] - self._learned_ir: Optional[List[int]] = None - - class CarrierFrequencyRange(Stub): - """ConsumerIrManager.CarrierFrequencyRange — 载波频率范围""" - - def __init__(self, min_freq: int, max_freq: int): - super().__init__() - self._min_frequency = min_freq - self._max_frequency = max_freq - - def getMinFrequency(self) -> int: - return self._min_frequency - - def getMaxFrequency(self) -> int: - return self._max_frequency - - def toString(self) -> str: - return f"CarrierFrequencyRange{{min={self._min_frequency}, max={self._max_frequency}}}" - - def hashCode(self) -> int: - return hash((self._min_frequency, self._max_frequency)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, ConsumerIrManager.CarrierFrequencyRange): - return False - return (self._min_frequency == other._min_frequency and - self._max_frequency == other._max_frequency) - - # ── Main methods ── - - def hasIrEmitter(self) -> bool: - return self._has_emitter - - def transmit(self, carrierFrequency: int, pattern: List[int]): - if not self._has_emitter: - raise AndroidOnlyError("No IR emitter available on this device") - self._last_pattern = (carrierFrequency, pattern) - - def getCarrierFrequencyRange(self) -> List[CarrierFrequencyRange]: - return list(self._carrier_frequencies) - - # ── Learning (stub) ── - - def isLearnedIR(self) -> bool: - return self._learned_ir is not None - - def learnIR(self, timeout: int) -> Optional[List[int]]: - self._learned_ir = [9000, 4500, 560, 560, 560, 560, 560, 1690] - return self._learned_ir - - def cancelLearning(self): - self._learned_ir = None - - def toString(self) -> str: - return f"ConsumerIrManager{{hasEmitter={self._has_emitter}}}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "ConsumerIrManager", -] \ No newline at end of file diff --git a/android_pyapi/android/hardware_display.py b/android_pyapi/android/hardware_display.py deleted file mode 100644 index d199a51..0000000 --- a/android_pyapi/android/hardware_display.py +++ /dev/null @@ -1,299 +0,0 @@ -""" -Android hardware.display 组件 — DisplayManager, VirtualDisplay, WifiDisplay 等 -方法签名完全对齐 Android SDK -""" - -import time -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# DisplayManager -# ═══════════════════════════════════════════════════════════════ - -class DisplayManager(Stub): - """DisplayManager — 显示管理器""" - - VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 1 - VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 4 - VIRTUAL_DISPLAY_FLAG_PUBLIC_DISPLAY = 8 - VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2 - VIRTUAL_DISPLAY_FLAG_SECURE = 16 - VIRTUAL_DISPLAY_FLAG_SUPPORTS_HDR = 32 - - MATCH_DEFAULT_DISPLAY = -1 - MATCH_ALL_DISPLAYS = 0 - - # ═══════════════════════════════════════════════════════════ - # DisplayListener - # ═══════════════════════════════════════════════════════════ - - class DisplayListener(Stub): - """DisplayManager.DisplayListener — 显示监听器""" - - def onDisplayAdded(self, displayId: int): - pass - - def onDisplayRemoved(self, displayId: int): - pass - - def onDisplayChanged(self, displayId: int): - pass - - def __init__(self): - super().__init__() - self._displays: Dict[int, Display] = {} - self._listeners: Dict[int, DisplayManager.DisplayListener] = {} - self._next_display_id = 1 - # Create default display - self._displays[0] = Display(0, "Built-in Display") - - def getDisplay(self, displayId: int) -> Optional["Display"]: - return self._displays.get(displayId) - - def getDisplays(self) -> List["Display"]: - if DisplayManager.MATCH_ALL_DISPLAYS: - return list(self._displays.values()) - return [] - - def registerDisplayListener(self, listener: DisplayListener, handler: Any = None): - lid = id(listener) - self._listeners[lid] = listener - - def unregisterDisplayListener(self, listener: DisplayListener): - lid = id(listener) - self._listeners.pop(lid, None) - - def createVirtualDisplay(self, name: str, width: int, height: int, densityDpi: int, - surface: Any, flags: int, callback: Callable = None, - handler: Any = None) -> "VirtualDisplay": - display_id = self._next_display_id - self._next_display_id += 1 - vd = VirtualDisplay(display_id, name, width, height, densityDpi, surface, flags) - return vd - - def getStableDisplay(self) -> Optional["Display"]: - return self._displays.get(0) - - -# ═══════════════════════════════════════════════════════════════ -# Display -# ═══════════════════════════════════════════════════════════════ - -class Display(Stub): - """Display — 显示信息""" - - DEFAULT_DISPLAY = 0 - - STATE_UNKNOWN = 0 - STATE_OFF = 1 - STATE_ON = 2 - STATE_DOZE = 3 - STATE_DOZE_SUSPEND = 4 - - FLAG_PRESENTATION = 2 - FLAG_PRIVATE = 4 - FLAG_ROUND = 8 - FLAG_CAN_SHOW_WITH_INSETS = 16 - FLAG_SECURE = 32 - FLAG_SUPPORTS_HDR = 64 - - def __init__(self, displayId: int = 0, name: str = "Display", - width: int = 1920, height: int = 1080, - densityDpi: int = 420, refreshRate: float = 60.0): - super().__init__() - self._display_id = displayId - self._name = name - self._width = width - self._height = height - self._density_dpi = densityDpi - self._refresh_rate = refreshRate - self._state = Display.STATE_ON - self._flags = 0 - self._app_width = width - self._app_height = height - - def getDisplayId(self) -> int: - return self._display_id - - def getName(self) -> str: - return self._name - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def getRotation(self) -> int: - return 0 - - def getState(self) -> int: - return self._state - - def getRefreshRate(self) -> float: - return self._refresh_rate - - def isHdr(self) -> bool: - return bool(self._flags & Display.FLAG_SUPPORTS_HDR) - - def getFlags(self) -> int: - return self._flags - - def getMode(self) -> Any: - return None - - def getSupportedModes(self) -> List[Any]: - return [] - - def getSupportedHdrTypes(self) -> List[int]: - return [] - - def getAppWidth(self) -> int: - return self._app_width - - def getAppHeight(self) -> int: - return self._app_height - - def getDensityDpi(self) -> int: - return self._density_dpi - - def toString(self) -> str: - return f"Display(id={self._display_id}, name={self._name}, {self._width}x{self._height}," \ - f" dpi={self._density_dpi})" - - -# ═══════════════════════════════════════════════════════════════ -# VirtualDisplay -# ═══════════════════════════════════════════════════════════════ - -class VirtualDisplay(Stub): - """VirtualDisplay — 虚拟显示""" - - def __init__(self, displayId: int, name: str, width: int, height: int, - densityDpi: int, surface: Any, flags: int): - super().__init__() - self._display_id = displayId - self._name = name - self._width = width - self._height = height - self._density_dpi = densityDpi - self._surface = surface - self._flags = flags - self._display = Display(displayId, name, width, height, densityDpi) - self._released = False - - def setSurface(self, surface: Any): - self._surface = surface - - def resize(self, width: int, height: int, densityDpi: int): - self._width = width - self._height = height - self._density_dpi = densityDpi - - def release(self): - self._released = True - - def getDisplay(self) -> Display: - return self._display - - def getSurface(self) -> Any: - return self._surface - - def getFlags(self) -> int: - return self._flags - - def toString(self) -> str: - return f"VirtualDisplay(id={self._display_id}, name={self._name}," \ - f" {self._width}x{self._height}, released={self._released})" - - -# ═══════════════════════════════════════════════════════════════ -# WifiDisplayStatus -# ═══════════════════════════════════════════════════════════════ - -class WifiDisplayStatus(Stub): - """WifiDisplayStatus — Wi-Fi 显示状态""" - - FEATURE_STATE_DISABLED = 0 - FEATURE_STATE_ON = 1 - FEATURE_STATE_OFF = 2 - FEATURE_STATE_UNAVAILABLE = 3 - - def __init__(self): - super().__init__() - self._active_display: Optional[WifiDisplay] = None - self._scan_results: List[WifiDisplay] = [] - self._feature_state = WifiDisplayStatus.FEATURE_STATE_DISABLED - - def getActiveDisplay(self) -> Optional["WifiDisplay"]: - return self._active_display - - def getScanResults(self) -> List["WifiDisplay"]: - return self._scan_results - - def getFeatureState(self) -> int: - return self._feature_state - - def toString(self) -> str: - return f"WifiDisplayStatus(featureState={self._feature_state})" - - -# ═══════════════════════════════════════════════════════════════ -# WifiDisplay -# ═══════════════════════════════════════════════════════════════ - -class WifiDisplay(Stub): - """WifiDisplay — Wi-Fi 显示设备""" - - def __init__(self, deviceAddress: str = "", deviceName: str = "", - deviceAlias: str = "", available: bool = False, - remembered: bool = False): - super().__init__() - self._device_address = deviceAddress - self._device_name = deviceName - self._device_alias = deviceAlias - self._available = available - self._remembered = remembered - - def getDeviceAddress(self) -> str: - return self._device_address - - def getDeviceName(self) -> str: - return self._device_name - - def getDeviceAlias(self) -> str: - return self._device_alias - - def isAvailable(self) -> bool: - return self._available - - def isRemembered(self) -> bool: - return self._remembered - - def equals(self, other: Any) -> bool: - if not isinstance(other, WifiDisplay): - return False - return self._device_address == other._device_address - - def hashCode(self) -> int: - return hash(self._device_address) & 0x7FFFFFFF - - def toString(self) -> str: - return f"WifiDisplay(address={self._device_address}, name={self._device_name}," \ - f" available={self._available})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "DisplayManager", - "Display", - "VirtualDisplay", - "WifiDisplayStatus", - "WifiDisplay", -] \ No newline at end of file diff --git a/android_pyapi/android/hardware_fingerprint.py b/android_pyapi/android/hardware_fingerprint.py deleted file mode 100644 index b3fb524..0000000 --- a/android_pyapi/android/hardware_fingerprint.py +++ /dev/null @@ -1,179 +0,0 @@ -""" -Android hardware.fingerprint 组件 — FingerprintManager, Fingerprint 等 -方法签名完全对齐 Android SDK -""" - -from typing import Any, Callable, Dict, List, Optional - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# FingerprintManager -# ═══════════════════════════════════════════════════════════════ - -class FingerprintManager(Stub): - """FingerprintManager — 指纹管理器""" - - FINGERPRINT_ACQUIRED_GOOD = 0 - FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3 - FINGERPRINT_ACQUIRED_INSUFFICIENT = 2 - FINGERPRINT_ACQUIRED_PARTIAL = 1 - FINGERPRINT_ACQUIRED_TOO_FAST = 5 - FINGERPRINT_ACQUIRED_TOO_SLOW = 4 - - FINGERPRINT_ERROR_HW_UNAVAILABLE = 1 - FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2 - FINGERPRINT_ERROR_TIMEOUT = 3 - FINGERPRINT_ERROR_NO_SPACE = 4 - FINGERPRINT_ERROR_CANCELED = 5 - FINGERPRINT_ERROR_LOCKOUT = 7 - FINGERPRINT_ERROR_VENDOR = 8 - FINGERPRINT_ERROR_USER_CANCELED = 10 - FINGERPRINT_ERROR_BAD_CALIBRATION = 11 - FINGERPRINT_ERROR_POWER_PRESS = 12 - FINGERPRINT_ERROR_IDLE_NOT_ENROLLED = 13 - - # ═══════════════════════════════════════════════════════════ - # AuthenticationCallback - # ═══════════════════════════════════════════════════════════ - - class AuthenticationCallback(Stub): - """FingerprintManager.AuthenticationCallback — 认证回调""" - - def onAuthenticationError(self, errorCode: int, errString: str): - pass - - def onAuthenticationHelp(self, helpCode: int, helpString: str): - pass - - def onAuthenticationSucceeded(self, result: "FingerprintManager.AuthenticationResult"): - pass - - def onAuthenticationFailed(self): - pass - - def onAuthenticationAcquired(self, acquireInfo: int): - pass - - # ═══════════════════════════════════════════════════════════ - # AuthenticationResult - # ═══════════════════════════════════════════════════════════ - - class AuthenticationResult(Stub): - """FingerprintManager.AuthenticationResult — 认证结果""" - - def __init__(self, fingerprint: "Fingerprint" = None, - cryptoObject: "FingerprintManager.CryptoObject" = None): - super().__init__() - self._fingerprint = fingerprint - self._crypto_object = cryptoObject - - def getFingerprint(self) -> Optional["Fingerprint"]: - return self._fingerprint - - def getCryptoObject(self) -> Optional["FingerprintManager.CryptoObject"]: - return self._crypto_object - - # ═══════════════════════════════════════════════════════════ - # CryptoObject - # ═══════════════════════════════════════════════════════════ - - class CryptoObject(Stub): - """FingerprintManager.CryptoObject — 加密对象""" - - def __init__(self, cipher: Any = None, signature: Any = None, mac: Any = None): - super().__init__() - self._cipher = cipher - self._signature = signature - self._mac = mac - - def getCipher(self) -> Any: - return self._cipher - - def getSignature(self) -> Any: - return self._signature - - def getMac(self) -> Any: - return self._mac - - def __init__(self): - super().__init__() - self._enrolled_fingerprints: List[Fingerprint] = [] - self._callbacks: Dict[int, FingerprintManager.AuthenticationCallback] = {} - - def isHardwareDetected(self) -> bool: - return False - - def hasEnrolledFingerprints(self) -> bool: - return len(self._enrolled_fingerprints) > 0 - - def authenticate(self, crypto: Optional[CryptoObject] = None, - cancel: Any = None, flags: int = 0, - callback: Optional[AuthenticationCallback] = None, - handler: Any = None): - if callback: - callback.onAuthenticationError( - FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, - "Fingerprint hardware not available on desktop" - ) - - def enroll(self, token: Any, userId: int = 0, - callback: Optional[AuthenticationCallback] = None): - raise AndroidOnlyError("FingerprintManager.enroll() is not available on desktop") - - def remove(self, fingerprint: "Fingerprint", userId: int = 0): - if fingerprint in self._enrolled_fingerprints: - self._enrolled_fingerprints.remove(fingerprint) - - def rename(self, fingerprint: "Fingerprint", newName: str): - if fingerprint: - fingerprint._name = newName - - def getEnrolledFingerprints(self) -> List["Fingerprint"]: - return list(self._enrolled_fingerprints) - - -# ═══════════════════════════════════════════════════════════════ -# Fingerprint -# ═══════════════════════════════════════════════════════════════ - -class Fingerprint(Stub): - """Fingerprint — 指纹数据""" - - def __init__(self, name: str = "", fingerId: int = 0, groupId: int = 0, - deviceId: int = 0): - super().__init__() - self._name = name - self._finger_id = fingerId - self._group_id = groupId - self._device_id = deviceId - - def getName(self) -> str: - return self._name - - def getFingerId(self) -> int: - return self._finger_id - - def getGroupId(self) -> int: - return self._group_id - - def getDeviceId(self) -> int: - return self._device_id - - def toString(self) -> str: - return f"Fingerprint(name={self._name}, fingerId={self._finger_id})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "FingerprintManager", - "Fingerprint", -] \ No newline at end of file diff --git a/android_pyapi/android/hardware_hdmi.py b/android_pyapi/android/hardware_hdmi.py deleted file mode 100644 index 343fc63..0000000 --- a/android_pyapi/android/hardware_hdmi.py +++ /dev/null @@ -1,775 +0,0 @@ -""" -Android HDMI API — HdmiControlManager, HdmiDeviceInfo, HdmiPortInfo, etc. -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import sys -import time -import uuid -import threading -from typing import Any, Dict, List, Optional, Set, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -# DEVICE_* 常量 -DEVICE_TYPE_TV = 0 -DEVICE_TYPE_RECORDER = 1 -DEVICE_TYPE_TUNER = 2 -DEVICE_TYPE_PLAYBACK = 3 -DEVICE_TYPE_AUDIO_SYSTEM = 4 -DEVICE_TYPE_PURE_CEC_SWITCH = 5 -DEVICE_TYPE_VIDEO_PROCESSOR = 6 -DEVICE_TYPE_RESERVED = 7 -DEVICE_TYPE_INACTIVE = 15 -DEVICE_TYPE_CEC_AUTO = 16 - -# CONTROL_* 常量 -CONTROL_STATE_CHANGED_REASON_UNKNOWN = 0 -CONTROL_STATE_CHANGED_REASON_ENABLED = 1 -CONTROL_STATE_CHANGED_REASON_DISABLED = 2 -CONTROL_STATE_CHANGED_REASON_WAKE_UP = 3 -CONTROL_STATE_CHANGED_REASON_SLEEP = 4 -CONTROL_STATE_CHANGED_REASON_STANDBY = 5 -CONTROL_STATE_CHANGED_REASON_HDMI_CEC_ENABLED = 6 -CONTROL_STATE_CHANGED_REASON_HDMI_CEC_DISABLED = 7 -CONTROL_STATE_CHANGED_REASON_HDMI_MHL_ENABLED = 8 -CONTROL_STATE_CHANGED_REASON_HDMI_MHL_DISABLED = 9 -CONTROL_STATE_CHANGED_REASON_INVALID = -1 - -# CEC_* 常量 -CEC_VERSION_1_1 = 0x11 -CEC_VERSION_1_2 = 0x12 -CEC_VERSION_1_3 = 0x13 -CEC_VERSION_1_4 = 0x14 -CEC_VERSION_2_0 = 0x20 -CEC_VERSION_UNKNOWN = 0x00 - -# POWER_STATUS 常量 -POWER_STATUS_ON = 0 -POWER_STATUS_STANDBY = 1 -POWER_STATUS_TO_ON = 2 -POWER_STATUS_TO_STANDBY = 3 -POWER_STATUS_UNKNOWN = -1 - -# PORT_TYPE 常量 -PORT_TYPE_INPUT = 0 -PORT_TYPE_OUTPUT = 1 -PORT_TYPE_BIDIRECTIONAL = 2 -PORT_TYPE_ARC = 3 -PORT_TYPE_EARC = 4 - -# RESULT 常量 -RESULT_SUCCESS = 0 -RESULT_FAILURE = -1 -RESULT_TIMEOUT = -2 -RESULT_NOT_SUPPORTED = -3 -RESULT_BUSY = -4 -RESULT_ALREADY_IN_PROGRESS = -5 - -# CEC_MESSAGE_TYPE 常量 -CEC_MESSAGE_TYPE_FEATURE_ABORT = 0x00 -CEC_MESSAGE_TYPE_ACTIVE_SOURCE = 0x82 -CEC_MESSAGE_TYPE_IMAGE_VIEW_ON = 0x04 -CEC_MESSAGE_TYPE_TEXT_VIEW_ON = 0x0D -CEC_MESSAGE_TYPE_INACTIVE_SOURCE = 0x9D -CEC_MESSAGE_TYPE_REQUEST_ACTIVE_SOURCE = 0x85 -CEC_MESSAGE_TYPE_STANDBY = 0x36 -CEC_MESSAGE_TYPE_SET_MENU_LANGUAGE = 0x32 -CEC_MESSAGE_TYPE_USER_CONTROL_PRESSED = 0x44 -CEC_MESSAGE_TYPE_USER_CONTROL_RELEASED = 0x45 -CEC_MESSAGE_TYPE_GIVE_PHYSICAL_ADDRESS = 0x83 -CEC_MESSAGE_TYPE_REPORT_PHYSICAL_ADDRESS = 0x84 -CEC_MESSAGE_TYPE_GIVE_DEVICE_VENDOR_ID = 0x8C -CEC_MESSAGE_TYPE_DEVICE_VENDOR_ID = 0x87 -CEC_MESSAGE_TYPE_GIVE_OSD_NAME = 0x46 -CEC_MESSAGE_TYPE_SET_OSD_NAME = 0x47 -CEC_MESSAGE_TYPE_GIVE_FEATURES = 0x88 -CEC_MESSAGE_TYPE_REPORT_FEATURES = 0x89 -CEC_MESSAGE_TYPE_VENDOR_COMMAND = 0x89 -CEC_MESSAGE_TYPE_VENDOR_REMOTE_BUTTON_DOWN = 0x8A -CEC_MESSAGE_TYPE_VENDOR_REMOTE_BUTTON_UP = 0x8B - -# HDMI_RECORD_SOURCE 常量 -RECORD_SOURCE_OWN_SOURCE = 1 -RECORD_SOURCE_DIGITAL_SERVICE = 2 -RECORD_SOURCE_ANALOGUE_SERVICE = 3 -RECORD_SOURCE_EXTERNAL_PHYSICAL_ADDRESS = 4 -RECORD_SOURCE_EXTERNAL_PLUG = 5 -RECORD_SOURCE_PROGRAM_NUMBER = 6 - - -# ═══════════════════════════════════════════════════════════════ -# HdmiDeviceInfo -# ═══════════════════════════════════════════════════════════════ - -class HdmiDeviceInfo(Stub): - """HdmiDeviceInfo — HDMI 设备信息""" - - def __init__( - self, - logicalAddress: int = -1, - physicalAddress: int = 0xFFFF, - portId: int = -1, - deviceType: int = DEVICE_TYPE_INACTIVE, - vendorId: int = 0, - displayName: str = "", - ): - super().__init__() - self._logical_address = logicalAddress - self._physical_address = physicalAddress - self._port_id = portId - self._device_type = deviceType - self._vendor_id = vendorId - self._display_name = displayName - self._device_id = str(uuid.uuid4()) - self._osd_name: Optional[str] = None - self._cec_version = CEC_VERSION_2_0 - self._power_status = POWER_STATUS_ON - self._is_cec_device = True - self._is_mhl_device = False - self._is_arc_supported = False - self._is_earc_supported = False - self._is_audio_return_channel = False - self._physical_address_str: Optional[str] = None - self._adopter_id = 0 - self._logical_addresses: List[int] = [] - self._supports_cec = True - self._vendor_name: Optional[str] = None - self._menu_language: Optional[str] = None - self._features: List[int] = [] - self._is_active_source = False - self._discovery_time = time.time() - - def getLogicalAddress(self) -> int: - return self._logical_address - - def getPhysicalAddress(self) -> int: - return self._physical_address - - def getPhysicalAddressString(self) -> str: - if self._physical_address_str: - return self._physical_address_str - pa = self._physical_address - return f"{(pa >> 12) & 0xF}.{(pa >> 8) & 0xF}.{(pa >> 4) & 0xF}.{pa & 0xF}" - - def getPortId(self) -> int: - return self._port_id - - def getDeviceType(self) -> int: - return self._device_type - - def getDeviceTypeString(self) -> str: - type_names = { - DEVICE_TYPE_TV: "TV", - DEVICE_TYPE_RECORDER: "Recorder", - DEVICE_TYPE_TUNER: "Tuner", - DEVICE_TYPE_PLAYBACK: "Playback", - DEVICE_TYPE_AUDIO_SYSTEM: "AudioSystem", - DEVICE_TYPE_PURE_CEC_SWITCH: "Switch", - DEVICE_TYPE_VIDEO_PROCESSOR: "VideoProcessor", - DEVICE_TYPE_INACTIVE: "Inactive", - } - return type_names.get(self._device_type, "Unknown") - - def getVendorId(self) -> int: - return self._vendor_id - - def getDisplayName(self) -> str: - return self._display_name - - def getOsdName(self) -> Optional[str]: - return self._osd_name - - def setOsdName(self, name: str) -> None: - self._osd_name = name - - def getCecVersion(self) -> int: - return self._cec_version - - def getPowerStatus(self) -> int: - return self._power_status - - def setPowerStatus(self, status: int) -> None: - self._power_status = status - - def isCecDevice(self) -> bool: - return self._is_cec_device - - def isMhlDevice(self) -> bool: - return self._is_mhl_device - - def isArcSupported(self) -> bool: - return self._is_arc_supported - - def isEarcSupported(self) -> bool: - return self._is_earc_supported - - def isActiveSource(self) -> bool: - return self._is_active_source - - def setActiveSource(self, active: bool) -> None: - self._is_active_source = active - - def toString(self) -> str: - return ( - f"HdmiDeviceInfo(logical={self._logical_address}, " - f"physical={self.getPhysicalAddressString()}, " - f"type={self.getDeviceTypeString()}, " - f"name={self._display_name})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# HdmiPortInfo -# ═══════════════════════════════════════════════════════════════ - -class HdmiPortInfo(Stub): - """HdmiPortInfo — HDMI 端口信息""" - - def __init__(self, id: int = 0, type: int = PORT_TYPE_INPUT, address: int = 0x0000): - super().__init__() - self._id = id - self._type = type - self._physical_address = address - self._is_cec_supported = True - self._is_arc_supported = False - self._is_earc_supported = False - self._is_mhl_supported = False - self._supports_4k = False - self._supports_hdr = False - self._supports_dolby_vision = False - self._max_tmds_character_rate = 6000 - self._num_edid_blocks = 0 - self._edid: Optional[bytes] = None - self._port_name: Optional[str] = None - self._connected_device: Optional[HdmiDeviceInfo] = None - self._is_connected = False - - def getId(self) -> int: - return self._id - - def getType(self) -> int: - return self._type - - def getPhysicalAddress(self) -> int: - return self._physical_address - - def isCecSupported(self) -> bool: - return self._is_cec_supported - - def setCecSupported(self, supported: bool) -> None: - self._is_cec_supported = supported - - def isArcSupported(self) -> bool: - return self._is_arc_supported - - def setArcSupported(self, supported: bool) -> None: - self._is_arc_supported = supported - - def isEarcSupported(self) -> bool: - return self._is_earc_supported - - def setEarcSupported(self, supported: bool) -> None: - self._is_earc_supported = supported - - def isMhlSupported(self) -> bool: - return self._is_mhl_supported - - def supports4k(self) -> bool: - return self._supports_4k - - def setSupports4k(self, supported: bool) -> None: - self._supports_4k = supported - - def supportsHdr(self) -> bool: - return self._supports_hdr - - def supportsDolbyVision(self) -> bool: - return self._supports_dolby_vision - - def getMaxTmdsCharacterRate(self) -> int: - return self._max_tmds_character_rate - - def getPortName(self) -> Optional[str]: - return self._port_name - - def setPortName(self, name: str) -> None: - self._port_name = name - - def isConnected(self) -> bool: - return self._is_connected - - def setConnected(self, connected: bool, device: Optional[HdmiDeviceInfo] = None) -> None: - self._is_connected = connected - self._connected_device = device - - def getConnectedDevice(self) -> Optional[HdmiDeviceInfo]: - return self._connected_device - - def toString(self) -> str: - return ( - f"HdmiPortInfo(id={self._id}, type={self._type}, " - f"cec={self._is_cec_supported}, arc={self._is_arc_supported}, " - f"connected={self._is_connected})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# HdmiCecDeviceInfo -# ═══════════════════════════════════════════════════════════════ - -class HdmiCecDeviceInfo(Stub): - """HdmiCecDeviceInfo — CEC 设备信息""" - - def __init__(self): - super().__init__() - self._logical_address = -1 - self._physical_address = 0xFFFF - self._device_type = DEVICE_TYPE_INACTIVE - self._vendor_id = 0 - self._osd_name: Optional[str] = None - self._cec_version = CEC_VERSION_2_0 - self._language: Optional[str] = None - self._features: List[int] = [] - self._is_active = False - - def getLogicalAddress(self) -> int: - return self._logical_address - - def getPhysicalAddress(self) -> int: - return self._physical_address - - def getDeviceType(self) -> int: - return self._device_type - - def getVendorId(self) -> int: - return self._vendor_id - - def getOsdName(self) -> Optional[str]: - return self._osd_name - - def getCecVersion(self) -> int: - return self._cec_version - - def getLanguage(self) -> Optional[str]: - return self._language - - def getFeatures(self) -> List[int]: - return list(self._features) - - def isActive(self) -> bool: - return self._is_active - - def toString(self) -> str: - return ( - f"HdmiCecDeviceInfo(logical={self._logical_address}, " - f"type={self._device_type}, name={self._osd_name})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# HdmiRecordSources -# ═══════════════════════════════════════════════════════════════ - -class HdmiRecordSources(Stub): - """HdmiRecordSources — HDMI 录制源""" - - def __init__(self): - super().__init__() - - @staticmethod - def createDigitalSource(serviceId: int, source: int) -> Any: - return {"type": "digital", "serviceId": serviceId, "source": source} - - @staticmethod - def createAnalogueSource(serviceType: int, frequency: int, system: int) -> Any: - return {"type": "analogue", "serviceType": serviceType, "frequency": frequency, "system": system} - - @staticmethod - def createExternalPlug(plugNumber: int) -> Any: - return {"type": "external_plug", "plugNumber": plugNumber} - - @staticmethod - def createExternalPhysicalAddress(physicalAddress: int) -> Any: - return {"type": "external_physical", "physicalAddress": physicalAddress} - - @staticmethod - def createOwnSource() -> Any: - return {"type": "own"} - - @staticmethod - def createProgramNumber(programNumber: int) -> Any: - return {"type": "program", "programNumber": programNumber} - - def toString(self) -> str: - return "HdmiRecordSources" - - -# ═══════════════════════════════════════════════════════════════ -# HdmiRecordListener -# ═══════════════════════════════════════════════════════════════ - -class HdmiRecordListener(Stub): - """HdmiRecordListener — HDMI 录制监听器""" - - def __init__(self): - super().__init__() - - def onRecordStarted(self, source: Any, result: int) -> None: - pass - - def onRecordStopped(self, source: Any) -> None: - pass - - def onRecordScheduleStarted(self, source: Any, result: int) -> None: - pass - - def onRecordScheduleStopped(self, source: Any) -> None: - pass - - def onClearTimerStarted(self, source: Any, result: int) -> None: - pass - - def onClearTimerStopped(self, source: Any) -> None: - pass - - def onOneTouchRecordStarted(self, source: Any) -> None: - pass - - def onOneTouchRecordStopped(self, source: Any) -> None: - pass - - def onTimerRecordStarted(self, source: Any, result: int) -> None: - pass - - def onTimerRecordStopped(self, source: Any) -> None: - pass - - def toString(self) -> str: - return "HdmiRecordListener" - - -# ═══════════════════════════════════════════════════════════════ -# HdmiPlaybackClient -# ═══════════════════════════════════════════════════════════════ - -class HdmiPlaybackClient(Stub): - """HdmiPlaybackClient — HDMI 播放客户端""" - - def __init__(self, manager: "HdmiControlManager"): - super().__init__() - self._manager = manager - self._callback: Optional[Callable] = None - - def sendKeyEvent(self, keyCode: int, isPressed: bool) -> None: - pass - - def oneTouchPlay(self, callback: Optional[Callable] = None) -> None: - if callback: - callback(RESULT_SUCCESS) - - def queryDeviceType(self, callback: Optional[Callable] = None) -> None: - if callback: - callback(DEVICE_TYPE_PLAYBACK) - - def setCallback(self, callback: Callable) -> None: - self._callback = callback - - def clearCallback(self) -> None: - self._callback = None - - def toString(self) -> str: - return "HdmiPlaybackClient" - - -# ═══════════════════════════════════════════════════════════════ -# HdmiSwitchClient -# ═══════════════════════════════════════════════════════════════ - -class HdmiSwitchClient(Stub): - """HdmiSwitchClient — HDMI 切换客户端""" - - def __init__(self, manager: "HdmiControlManager"): - super().__init__() - self._manager = manager - self._callback: Optional[Callable] = None - - def selectDevice(self, logicalAddress: int, callback: Optional[Callable] = None) -> None: - if callback: - callback(RESULT_SUCCESS) - - def setCallback(self, callback: Callable) -> None: - self._callback = callback - - def clearCallback(self) -> None: - self._callback = None - - def getDeviceList(self) -> List[HdmiDeviceInfo]: - return self._manager.getDeviceList() - - def toString(self) -> str: - return "HdmiSwitchClient" - - -# ═══════════════════════════════════════════════════════════════ -# HdmiTvClient -# ═══════════════════════════════════════════════════════════════ - -class HdmiTvClient(Stub): - """HdmiTvClient — HDMI TV 客户端""" - - def __init__(self, manager: "HdmiControlManager"): - super().__init__() - self._manager = manager - self._callback: Optional[Callable] = None - self._record_listener: Optional[HdmiRecordListener] = None - - def deviceSelect(self, logicalAddress: int, callback: Optional[Callable] = None) -> None: - if callback: - callback(RESULT_SUCCESS) - - def sendStandby(self, deviceId: int, callback: Optional[Callable] = None) -> None: - if callback: - callback(RESULT_SUCCESS) - - def setSystemAudioMode(self, enabled: bool, callback: Optional[Callable] = None) -> None: - if callback: - callback(RESULT_SUCCESS) - - def setArcEnabled(self, enabled: bool) -> None: - pass - - def setEarcEnabled(self, enabled: bool) -> None: - pass - - def startOneTouchRecord(self, address: int, source: Any) -> None: - if self._record_listener: - self._record_listener.onOneTouchRecordStarted(source) - - def stopOneTouchRecord(self, address: int) -> None: - if self._record_listener: - self._record_listener.onOneTouchRecordStopped(address) - - def startTimerRecording(self, address: int, source: Any, duration: int) -> None: - if self._record_listener: - self._record_listener.onTimerRecordStarted(source, RESULT_SUCCESS) - - def clearTimerRecording(self, address: int, source: Any) -> None: - if self._record_listener: - self._record_listener.onClearTimerStarted(source, RESULT_SUCCESS) - - def setRecordListener(self, listener: HdmiRecordListener) -> None: - self._record_listener = listener - - def setCallback(self, callback: Callable) -> None: - self._callback = callback - - def clearCallback(self) -> None: - self._callback = None - - def startPlayback(self, address: int) -> None: - pass - - def stopPlayback(self, address: int) -> None: - pass - - def toString(self) -> str: - return "HdmiTvClient" - - -# ═══════════════════════════════════════════════════════════════ -# HdmiControlManager -# ═══════════════════════════════════════════════════════════════ - -class HdmiControlManager(Stub): - """HdmiControlManager — HDMI 控制管理器 (桌面存根)""" - - DEVICE_TYPE_TV = DEVICE_TYPE_TV - DEVICE_TYPE_RECORDER = DEVICE_TYPE_RECORDER - DEVICE_TYPE_TUNER = DEVICE_TYPE_TUNER - DEVICE_TYPE_PLAYBACK = DEVICE_TYPE_PLAYBACK - DEVICE_TYPE_AUDIO_SYSTEM = DEVICE_TYPE_AUDIO_SYSTEM - DEVICE_TYPE_PURE_CEC_SWITCH = DEVICE_TYPE_PURE_CEC_SWITCH - DEVICE_TYPE_VIDEO_PROCESSOR = DEVICE_TYPE_VIDEO_PROCESSOR - DEVICE_TYPE_RESERVED = DEVICE_TYPE_RESERVED - DEVICE_TYPE_INACTIVE = DEVICE_TYPE_INACTIVE - - CONTROL_STATE_CHANGED_REASON_UNKNOWN = CONTROL_STATE_CHANGED_REASON_UNKNOWN - CONTROL_STATE_CHANGED_REASON_ENABLED = CONTROL_STATE_CHANGED_REASON_ENABLED - CONTROL_STATE_CHANGED_REASON_DISABLED = CONTROL_STATE_CHANGED_REASON_DISABLED - CONTROL_STATE_CHANGED_REASON_WAKE_UP = CONTROL_STATE_CHANGED_REASON_WAKE_UP - CONTROL_STATE_CHANGED_REASON_SLEEP = CONTROL_STATE_CHANGED_REASON_SLEEP - CONTROL_STATE_CHANGED_REASON_STANDBY = CONTROL_STATE_CHANGED_REASON_STANDBY - - POWER_STATUS_ON = POWER_STATUS_ON - POWER_STATUS_STANDBY = POWER_STATUS_STANDBY - POWER_STATUS_TO_ON = POWER_STATUS_TO_ON - POWER_STATUS_TO_STANDBY = POWER_STATUS_TO_STANDBY - POWER_STATUS_UNKNOWN = POWER_STATUS_UNKNOWN - - RESULT_SUCCESS = RESULT_SUCCESS - RESULT_FAILURE = RESULT_FAILURE - RESULT_TIMEOUT = RESULT_TIMEOUT - RESULT_NOT_SUPPORTED = RESULT_NOT_SUPPORTED - RESULT_BUSY = RESULT_BUSY - - CEC_VERSION_1_4 = CEC_VERSION_1_4 - CEC_VERSION_2_0 = CEC_VERSION_2_0 - - _instance: Optional["HdmiControlManager"] = None - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._lock = threading.Lock() - self._devices: Dict[int, HdmiDeviceInfo] = {} - self._ports: Dict[int, HdmiPortInfo] = {} - self._is_available = True - self._hdmi_cec_enabled = True - self._hdmi_mhl_enabled = True - self._system_audio_mode = False - self._arc_enabled = False - self._earc_enabled = False - self._active_source: Optional[HdmiDeviceInfo] = None - self._playback_client: Optional[HdmiPlaybackClient] = None - self._tv_client: Optional[HdmiTvClient] = None - self._switch_client: Optional[HdmiSwitchClient] = None - self._hotplug_listeners: List[Callable] = [] - self._cec_message_listeners: List[Callable] = [] - self._control_state_listeners: List[Callable] = [] - - def getPlaybackClient(self) -> Optional[HdmiPlaybackClient]: - if self._playback_client is None: - self._playback_client = HdmiPlaybackClient(self) - return self._playback_client - - def getTvClient(self) -> Optional[HdmiTvClient]: - if self._tv_client is None: - self._tv_client = HdmiTvClient(self) - return self._tv_client - - def getSwitchClient(self) -> Optional[HdmiSwitchClient]: - if self._switch_client is None: - self._switch_client = HdmiSwitchClient(self) - return self._switch_client - - def getDeviceList(self) -> List[HdmiDeviceInfo]: - with self._lock: - return list(self._devices.values()) - - def getDeviceInfo(self, logicalAddress: int) -> Optional[HdmiDeviceInfo]: - with self._lock: - return self._devices.get(logicalAddress) - - def getPortInfo(self, portId: int) -> Optional[HdmiPortInfo]: - with self._lock: - return self._ports.get(portId) - - def getPorts(self) -> List[HdmiPortInfo]: - with self._lock: - return list(self._ports.values()) - - def addDevice(self, device: HdmiDeviceInfo) -> None: - with self._lock: - self._devices[device.getLogicalAddress()] = device - if device.isActiveSource(): - self._active_source = device - - def removeDevice(self, logicalAddress: int) -> Optional[HdmiDeviceInfo]: - with self._lock: - device = self._devices.pop(logicalAddress, None) - if device and self._active_source is device: - self._active_source = None - return device - - def clearDevices(self) -> None: - with self._lock: - self._devices.clear() - self._active_source = None - - def isCecEnabled(self) -> bool: - return self._hdmi_cec_enabled - - def setCecEnabled(self, enabled: bool) -> None: - self._hdmi_cec_enabled = enabled - - def isMhlEnabled(self) -> bool: - return self._hdmi_mhl_enabled - - def setMhlEnabled(self, enabled: bool) -> None: - self._hdmi_mhl_enabled = enabled - - def isSystemAudioMode(self) -> bool: - return self._system_audio_mode - - def setSystemAudioMode(self, mode: bool) -> None: - self._system_audio_mode = mode - - def isArcEnabled(self) -> bool: - return self._arc_enabled - - def setArcEnabled(self, enabled: bool) -> None: - self._arc_enabled = enabled - - def isEarcEnabled(self) -> bool: - return self._earc_enabled - - def setEarcEnabled(self, enabled: bool) -> None: - self._earc_enabled = enabled - - def getActiveSource(self) -> Optional[HdmiDeviceInfo]: - with self._lock: - return self._active_source - - def setActiveSource(self, device: HdmiDeviceInfo) -> None: - with self._lock: - self._active_source = device - - def addHotplugEventListener(self, listener: Callable) -> None: - if listener not in self._hotplug_listeners: - self._hotplug_listeners.append(listener) - - def removeHotplugEventListener(self, listener: Callable) -> None: - if listener in self._hotplug_listeners: - self._hotplug_listeners.remove(listener) - - def addCecMessageListener(self, listener: Callable) -> None: - if listener not in self._cec_message_listeners: - self._cec_message_listeners.append(listener) - - def removeCecMessageListener(self, listener: Callable) -> None: - if listener in self._cec_message_listeners: - self._cec_message_listeners.remove(listener) - - def addControlStateChangeListener(self, listener: Callable) -> None: - if listener not in self._control_state_listeners: - self._control_state_listeners.append(listener) - - def removeControlStateChangeListener(self, listener: Callable) -> None: - if listener in self._control_state_listeners: - self._control_state_listeners.remove(listener) - - def isAvailable(self) -> bool: - return self._is_available - - def sendCecMessage(self, message: Any) -> int: - return RESULT_SUCCESS - - def toString(self) -> str: - with self._lock: - return ( - f"HdmiControlManager(devices={len(self._devices)}, " - f"ports={len(self._ports)}, " - f"cec={self._hdmi_cec_enabled})" - ) \ No newline at end of file diff --git a/android_pyapi/android/hardware_input.py b/android_pyapi/android/hardware_input.py deleted file mode 100644 index 0410fec..0000000 --- a/android_pyapi/android/hardware_input.py +++ /dev/null @@ -1,254 +0,0 @@ -""" -Android hardware.input 组件 — InputManager, InputDevice, MotionRange 等 -方法签名完全对齐 Android SDK -""" - -import sys -from typing import Any, Callable, Dict, List, Optional - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# InputManager -# ═══════════════════════════════════════════════════════════════ - -class InputManager(Stub): - """InputManager — 输入管理器""" - - ACTION_QUERY_KEYBOARD_LAYOUTS = "android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" - - # ═══════════════════════════════════════════════════════════ - # InputDeviceListener - # ═══════════════════════════════════════════════════════════ - - class InputDeviceListener(Stub): - """InputManager.InputDeviceListener — 输入设备监听器""" - - def onInputDeviceAdded(self, deviceId: int): - pass - - def onInputDeviceRemoved(self, deviceId: int): - pass - - def onInputDeviceChanged(self, deviceId: int): - pass - - def __init__(self): - super().__init__() - self._devices: Dict[int, InputDevice] = {} - self._listeners: Dict[int, InputManager.InputDeviceListener] = {} - self._next_id = 1 - self._init_default_devices() - - def _init_default_devices(self): - # Keyboard - kb = InputDevice( - deviceId=0, name="Virtual Keyboard", - descriptor="keyboard_v1", sources=InputDevice.SOURCE_KEYBOARD, - vendorId=0x1, productId=0x1, - keyboardType=InputDevice.KEYBOARD_TYPE_ALPHABETIC, - hasMicrophone=False, isVirtual=True - ) - self._devices[0] = kb - - # Mouse - mouse = InputDevice( - deviceId=1, name="Virtual Mouse", - descriptor="mouse_v1", sources=InputDevice.SOURCE_MOUSE, - vendorId=0x2, productId=0x2, - keyboardType=InputDevice.KEYBOARD_TYPE_NONE, - hasMicrophone=False, isVirtual=True - ) - self._devices[1] = mouse - - # Trackpad - tp = InputDevice( - deviceId=2, name="Virtual Trackpad", - descriptor="trackpad_v1", sources=InputDevice.SOURCE_TOUCHPAD, - vendorId=0x3, productId=0x3, - keyboardType=InputDevice.KEYBOARD_TYPE_NONE, - hasMicrophone=False, isVirtual=False - ) - self._devices[2] = tp - - def getDevice(self, deviceId: int) -> Optional["InputDevice"]: - return self._devices.get(deviceId) - - def getDeviceIds(self) -> List[int]: - return list(self._devices.keys()) - - def registerInputDeviceListener(self, listener: InputDeviceListener, handler: Any = None): - lid = id(listener) - self._listeners[lid] = listener - - def unregisterInputDeviceListener(self, listener: InputDeviceListener): - lid = id(listener) - self._listeners.pop(lid, None) - - def getInputDevice(self, deviceId: int) -> Optional["InputDevice"]: - return self.getDevice(deviceId) - - def isInTabletMode(self) -> bool: - return False - - def getCurrentKeyboardLayoutForInputDevice(self, device: "InputDevice") -> Optional[str]: - return None - - def setKeyboardLayoutForInputDevice(self, device: "InputDevice", - keyboardLayoutDescriptor: str): - pass - - -# ═══════════════════════════════════════════════════════════════ -# InputDevice -# ═══════════════════════════════════════════════════════════════ - -class InputDevice(Stub): - """InputDevice — 输入设备""" - - KEYBOARD_TYPE_ALPHABETIC = 2 - KEYBOARD_TYPE_NON_ALPHABETIC = 1 - KEYBOARD_TYPE_NONE = 0 - - SOURCE_CLASS_JOYSTICK = 16 - SOURCE_CLASS_MASK = 0x000000FF - SOURCE_CLASS_NONE = 0x00000000 - SOURCE_CLASS_POINTER = 0x00000002 - SOURCE_CLASS_POSITION = 0x00000008 - SOURCE_CLASS_TRACKBALL = 0x00000004 - SOURCE_CLASS_BUTTON = 0x00000001 - - SOURCE_GAMEPAD = 0x00000401 - SOURCE_HDMI = 0x02000000 - SOURCE_JOYSTICK = 0x01000010 - SOURCE_KEYBOARD = 0x00000101 - SOURCE_MOUSE = 0x00002002 - SOURCE_MOUSE_RELATIVE = 0x00002000 - SOURCE_ROTARY_ENCODER = 0x00800000 - SOURCE_STYLUS = 0x00004002 - SOURCE_TOUCHPAD = 0x00100008 - SOURCE_TOUCHSCREEN = 0x00001002 - SOURCE_TOUCH_NAVIGATION = 0x00200008 - SOURCE_TRACKBALL = 0x00010004 - SOURCE_UNKNOWN = 0x00000000 - SOURCE_ANY = 0xFFFFFFFE - - # ═══════════════════════════════════════════════════════════ - # MotionRange - # ═══════════════════════════════════════════════════════════ - - class MotionRange(Stub): - """InputDevice.MotionRange — 运动范围""" - - def __init__(self, axis: int = 0, source: int = 0, - min: float = 0.0, max: float = 1.0, - flat: float = 0.0, fuzz: float = 0.0, - resolution: float = 0.0): - super().__init__() - self._axis = axis - self._source = source - self._min = min - self._max = max - self._flat = flat - self._fuzz = fuzz - self._resolution = resolution - - def getAxis(self) -> int: - return self._axis - - def getSource(self) -> int: - return self._source - - def getMin(self) -> float: - return self._min - - def getMax(self) -> float: - return self._max - - def getFlat(self) -> float: - return self._flat - - def getFuzz(self) -> float: - return self._fuzz - - def getRange(self) -> float: - return self._max - self._min - - def getResolution(self) -> float: - return self._resolution - - def toString(self) -> str: - return f"MotionRange(axis={self._axis}, source={self._source}," \ - f" range=[{self._min}, {self._max}])" - - def __init__(self, deviceId: int = 0, name: str = "", descriptor: str = "", - sources: int = 0, vendorId: int = 0, productId: int = 0, - controllerNumber: int = 0, keyboardType: int = KEYBOARD_TYPE_NONE, - hasMicrophone: bool = False, isVirtual: bool = False): - super().__init__() - self._device_id = deviceId - self._name = name - self._descriptor = descriptor - self._sources = sources - self._vendor_id = vendorId - self._product_id = productId - self._controller_number = controllerNumber - self._keyboard_type = keyboardType - self._has_microphone = hasMicrophone - self._is_virtual = isVirtual - self._motion_ranges: List[InputDevice.MotionRange] = [] - - def getId(self) -> int: - return self._device_id - - def getName(self) -> str: - return self._name - - def getDescriptor(self) -> str: - return self._descriptor - - def getSources(self) -> int: - return self._sources - - def getVendorId(self) -> int: - return self._vendor_id - - def getProductId(self) -> int: - return self._product_id - - def getControllerNumber(self) -> int: - return self._controller_number - - def getKeyCharacterMap(self) -> Any: - return None - - def getMotionRange(self, axis: int = -1, source: int = -1) -> List[MotionRange]: - results = [] - for mr in self._motion_ranges: - if axis >= 0 and mr.getAxis() != axis: - continue - if source >= 0 and mr.getSource() != source: - continue - results.append(mr) - return results - - def hasMicrophone(self) -> bool: - return self._has_microphone - - def isVirtual(self) -> bool: - return self._is_virtual - - def toString(self) -> str: - return f"InputDevice(id={self._device_id}, name={self._name}," \ - f" sources={self._sources:#x})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "InputManager", - "InputDevice", -] \ No newline at end of file diff --git a/android_pyapi/android/hardware_light.py b/android_pyapi/android/hardware_light.py deleted file mode 100644 index 4851f08..0000000 --- a/android_pyapi/android/hardware_light.py +++ /dev/null @@ -1,660 +0,0 @@ -""" -Android Lights API — LightsManager, Light, LightState, LightSession, LightsRequest -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import sys -import time -import uuid -import threading -from typing import Any, Dict, List, Optional, Set, Tuple, Callable -from enum import IntEnum - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -# LIGHT_ID_* 常量 -LIGHT_ID_BACKLIGHT = 0 -LIGHT_ID_KEYBOARD = 1 -LIGHT_ID_BUTTONS = 2 -LIGHT_ID_BATTERY = 3 -LIGHT_ID_NOTIFICATIONS = 4 -LIGHT_ID_ATTENTION = 5 -LIGHT_ID_BLUETOOTH = 6 -LIGHT_ID_WIFI = 7 -LIGHT_ID_MICROPHONE = 8 -LIGHT_ID_CAMERA = 9 -LIGHT_ID_COUNT = 10 -LIGHT_ID_MICROPHONE_MUTE = 11 -LIGHT_ID_CAMERA_MUTE = 12 -LIGHT_ID_POWER = 13 -LIGHT_ID_ADAPTIVE_BACKLIGHT = 14 -LIGHT_ID_CHARGING = 15 -LIGHT_ID_ERROR = 16 -LIGHT_ID_MANUAL_BACKLIGHT = 17 -LIGHT_ID_MAX = 31 - -# BRIGHTNESS_* 常量 -BRIGHTNESS_MIN = 0 -BRIGHTNESS_LOW = 25 -BRIGHTNESS_MEDIUM = 127 -BRIGHTNESS_HIGH = 200 -BRIGHTNESS_MAX = 255 -BRIGHTNESS_OFF = 0 -BRIGHTNESS_FULL = 255 -BRIGHTNESS_DEFAULT = 127 - -# LIGHT_TYPE_* 常量 -LIGHT_TYPE_MANUAL = 0 -LIGHT_TYPE_AUTOMATIC = 1 -LIGHT_TYPE_MICROPHONE = 2 -LIGHT_TYPE_CAMERA = 3 -LIGHT_TYPE_NOTIFICATION = 4 -LIGHT_TYPE_ATTENTION = 5 -LIGHT_TYPE_RGB = 6 -LIGHT_TYPE_MONOCHROME = 7 -LIGHT_TYPE_POWER = 8 -LIGHT_TYPE_BATTERY = 9 -LIGHT_TYPE_ADAPTIVE = 10 -LIGHT_TYPE_BLINKING = 11 - -# LIGHT_CAPABILITY 常量 -LIGHT_CAPABILITY_NONE = 0 -LIGHT_CAPABILITY_BRIGHTNESS = 1 -LIGHT_CAPABILITY_RGB = 2 -LIGHT_CAPABILITY_COLOR_TEMPERATURE = 4 -LIGHT_CAPABILITY_BLINKING = 8 -LIGHT_CAPABILITY_DIMMABLE = 16 -LIGHT_CAPABILITY_ADAPTIVE = 32 -LIGHT_CAPABILITY_MICROPHONE = 64 -LIGHT_CAPABILITY_CAMERA = 128 - -# FLASH_MODE 常量 -FLASH_MODE_NONE = 0 -FLASH_MODE_HARDWARE = 1 -FLASH_MODE_SOFTWARE = 2 -FLASH_MODE_TIMED = 3 -FLASH_MODE_INFINITE = 4 - -# LIGHT_STATE 常量 -LIGHT_STATE_ON = 1 -LIGHT_STATE_OFF = 0 -LIGHT_STATE_BLINKING = 2 -LIGHT_STATE_BREATHING = 3 -LIGHT_STATE_PULSING = 4 - - -# ═══════════════════════════════════════════════════════════════ -# LightState -# ═══════════════════════════════════════════════════════════════ - -class LightState(Stub): - """LightState — 灯光状态""" - - def __init__(self): - super().__init__() - self._color = 0 - self._brightness = 0 - self._brightness_mode = LIGHT_TYPE_MANUAL - self._flash_mode = FLASH_MODE_NONE - self._flash_on_ms = 0 - self._flash_off_ms = 0 - self._state = LIGHT_STATE_OFF - self._rgb_color = (0, 0, 0) - self._color_temperature = 6500 - self._is_hardware = False - self._pulse_duration = 0 - self._breathing_enabled = False - self._fade_in_time = 0 - self._fade_out_time = 0 - self._brightness_float = 0.0 - self._brightness_nits = 0.0 - - def getColor(self) -> int: - return self._color - - def setColor(self, color: int) -> None: - self._color = color - self._rgb_color = ((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF) - - def getBrightness(self) -> int: - return self._brightness - - def setBrightness(self, brightness: int) -> None: - self._brightness = max(0, min(255, brightness)) - self._brightness_float = self._brightness / 255.0 - - def getBrightnessMode(self) -> int: - return self._brightness_mode - - def setBrightnessMode(self, mode: int) -> None: - self._brightness_mode = mode - - def getFlashMode(self) -> int: - return self._flash_mode - - def setFlashMode(self, mode: int) -> None: - self._flash_mode = mode - - def getFlashOnMs(self) -> int: - return self._flash_on_ms - - def setFlashOnMs(self, onMs: int) -> None: - self._flash_on_ms = onMs - - def getFlashOffMs(self) -> int: - return self._flash_off_ms - - def setFlashOffMs(self, offMs: int) -> None: - self._flash_off_ms = offMs - - def getState(self) -> int: - return self._state - - def setState(self, state: int) -> None: - self._state = state - - def isOn(self) -> bool: - return self._state != LIGHT_STATE_OFF - - def getRgbColor(self) -> Tuple[int, int, int]: - return self._rgb_color - - def setRgbColor(self, r: int, g: int, b: int) -> None: - self._rgb_color = (r & 0xFF, g & 0xFF, b & 0xFF) - self._color = (r << 16) | (g << 8) | b - - def getColorTemperature(self) -> int: - return self._color_temperature - - def setColorTemperature(self, temperature: int) -> None: - self._color_temperature = max(1000, min(20000, temperature)) - - def isHardwareControlled(self) -> bool: - return self._is_hardware - - def setFadeInTime(self, ms: int) -> None: - self._fade_in_time = ms - - def setFadeOutTime(self, ms: int) -> None: - self._fade_out_time = ms - - def getBrightnessFloat(self) -> float: - return self._brightness_float - - def getBrightnessNits(self) -> float: - return self._brightness_nits - - def setBrightnessNits(self, nits: float) -> None: - self._brightness_nits = nits - - def equals(self, other: Any) -> bool: - if isinstance(other, LightState): - return (self._color == other._color and self._brightness == other._brightness) - return False - - def hashCode(self) -> int: - return hash((self._color, self._brightness)) & 0x7FFFFFFF - - def toString(self) -> str: - return ( - f"LightState(color=0x{self._color:06x}, " - f"brightness={self._brightness}, " - f"state={self._state})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# Light -# ═══════════════════════════════════════════════════════════════ - -class Light(Stub): - """Light — 灯光设备""" - - LIGHT_TYPE_MANUAL = LIGHT_TYPE_MANUAL - LIGHT_TYPE_AUTOMATIC = LIGHT_TYPE_AUTOMATIC - LIGHT_TYPE_MICROPHONE = LIGHT_TYPE_MICROPHONE - LIGHT_TYPE_CAMERA = LIGHT_TYPE_CAMERA - LIGHT_TYPE_NOTIFICATION = LIGHT_TYPE_NOTIFICATION - LIGHT_TYPE_ATTENTION = LIGHT_TYPE_ATTENTION - LIGHT_TYPE_RGB = LIGHT_TYPE_RGB - LIGHT_TYPE_ADAPTIVE = LIGHT_TYPE_ADAPTIVE - LIGHT_TYPE_BLINKING = LIGHT_TYPE_BLINKING - - def __init__(self, light_id: int = 0, light_type: int = LIGHT_TYPE_MANUAL): - super().__init__() - self._id = light_id - self._type = light_type - self._ordinal = light_id - self._capabilities = LIGHT_CAPABILITY_BRIGHTNESS - self._name: Optional[str] = None - self._is_available = True - self._default_brightness = BRIGHTNESS_DEFAULT - self._max_brightness = BRIGHTNESS_MAX - self._min_brightness = BRIGHTNESS_MIN - self._supports_rgb = False - self._supports_color_temperature = False - self._supports_blinking = False - self._supports_dimming = True - self._supports_adaptive = False - self._default_color = 0xFFFFFF - self._default_color_temperature = 6500 - self._blink_supported = False - self._pulse_supported = False - self._breathing_supported = False - self._fade_supported = False - self._current_state = LightState() - self._rgb_support = light_type == LIGHT_TYPE_RGB - self._adaptive_support = light_type == LIGHT_TYPE_ADAPTIVE - self._blink_support = light_type == LIGHT_TYPE_BLINKING - - def getId(self) -> int: - return self._id - - def getType(self) -> int: - return self._type - - def getOrdinal(self) -> int: - return self._ordinal - - def getCapabilities(self) -> int: - return self._capabilities - - def setCapabilities(self, caps: int) -> None: - self._capabilities = caps - - def getName(self) -> Optional[str]: - return self._name - - def setName(self, name: str) -> None: - self._name = name - - def isAvailable(self) -> bool: - return self._is_available - - def setAvailable(self, available: bool) -> None: - self._is_available = available - - def getDefaultBrightness(self) -> int: - return self._default_brightness - - def getMaxBrightness(self) -> int: - return self._max_brightness - - def getMinBrightness(self) -> int: - return self._min_brightness - - def supportsRgb(self) -> bool: - return self._supports_rgb - - def supportsColorTemperature(self) -> bool: - return self._supports_color_temperature - - def supportsBlinking(self) -> bool: - return self._supports_blinking - - def supportsDimming(self) -> bool: - return self._supports_dimming - - def supportsAdaptive(self) -> bool: - return self._supports_adaptive - - def getDefaultColor(self) -> int: - return self._default_color - - def getDefaultColorTemperature(self) -> int: - return self._default_color_temperature - - def getCurrentState(self) -> LightState: - return self._current_state - - def setCurrentState(self, state: LightState) -> None: - self._current_state = state - - def canSetBrightness(self) -> bool: - return self._capabilities & LIGHT_CAPABILITY_BRIGHTNESS != 0 - - def canSetColor(self) -> bool: - return self._capabilities & LIGHT_CAPABILITY_RGB != 0 - - def canSetColorTemperature(self) -> bool: - return self._capabilities & LIGHT_CAPABILITY_COLOR_TEMPERATURE != 0 - - def canBlink(self) -> bool: - return self._capabilities & LIGHT_CAPABILITY_BLINKING != 0 - - def isDimmable(self) -> bool: - return self._capabilities & LIGHT_CAPABILITY_DIMMABLE != 0 - - def isAdaptive(self) -> bool: - return self._capabilities & LIGHT_CAPABILITY_ADAPTIVE != 0 - - def equals(self, other: Any) -> bool: - if isinstance(other, Light): - return self._id == other._id - return False - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF - - def toString(self) -> str: - return f"Light(id={self._id}, type={self._type}, name={self._name})" - - -# ═══════════════════════════════════════════════════════════════ -# LightSession -# ═══════════════════════════════════════════════════════════════ - -class LightSession(Stub): - """LightSession — 灯光会话""" - - def __init__(self, light_id: int): - super().__init__() - self._session_id = str(uuid.uuid4()) - self._light_id = light_id - self._state = LightState() - self._is_active = False - self._created_at = time.time() - self._last_update = time.time() - self._priority = 0 - self._owner: Optional[str] = None - self._transient = False - self._timeout_ms = 0 - self._fade_duration_ms = 0 - - def getSessionId(self) -> str: - return self._session_id - - def getLightId(self) -> int: - return self._light_id - - def getState(self) -> LightState: - return self._state - - def setState(self, state: LightState) -> None: - self._state = state - self._last_update = time.time() - - def isActive(self) -> bool: - return self._is_active - - def setActive(self, active: bool) -> None: - self._is_active = active - - def getCreatedAt(self) -> float: - return self._created_at - - def getLastUpdate(self) -> float: - return self._last_update - - def getPriority(self) -> int: - return self._priority - - def setPriority(self, priority: int) -> None: - self._priority = priority - - def getOwner(self) -> Optional[str]: - return self._owner - - def setOwner(self, owner: str) -> None: - self._owner = owner - - def isTransient(self) -> bool: - return self._transient - - def setTransient(self, transient: bool) -> None: - self._transient = transient - - def getTimeoutMs(self) -> int: - return self._timeout_ms - - def setTimeoutMs(self, timeoutMs: int) -> None: - self._timeout_ms = timeoutMs - - def getFadeDurationMs(self) -> int: - return self._fade_duration_ms - - def setFadeDurationMs(self, ms: int) -> None: - self._fade_duration_ms = ms - - def toString(self) -> str: - return ( - f"LightSession(id={self._session_id[:8]}..., " - f"light={self._light_id}, " - f"active={self._is_active})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# LightsRequest -# ═══════════════════════════════════════════════════════════════ - -class LightsRequest(Stub): - """LightsRequest — 灯光请求""" - - def __init__(self): - super().__init__() - self._light_states: Dict[int, LightState] = {} - self._light_sessions: List[LightSession] = [] - self._request_id = str(uuid.uuid4()) - self._created_at = time.time() - self._priority = 0 - self._is_urgent = False - self._callback: Optional[Callable] = None - self._completed = False - - def addLightState(self, light_id: int, state: LightState) -> "LightsRequest": - self._light_states[light_id] = state - return self - - def addLightSession(self, session: LightSession) -> "LightsRequest": - self._light_sessions.append(session) - return self - - def removeLightState(self, light_id: int) -> None: - self._light_states.pop(light_id, None) - - def getLightStates(self) -> Dict[int, LightState]: - return dict(self._light_states) - - def getLightSessions(self) -> List[LightSession]: - return list(self._light_sessions) - - def getRequestId(self) -> str: - return self._request_id - - def getPriority(self) -> int: - return self._priority - - def setPriority(self, priority: int) -> None: - self._priority = priority - - def isUrgent(self) -> bool: - return self._is_urgent - - def setUrgent(self, urgent: bool) -> None: - self._is_urgent = urgent - - def isCompleted(self) -> bool: - return self._completed - - def setCompleted(self, completed: bool) -> None: - self._completed = completed - - def getLightCount(self) -> int: - return len(self._light_states) - - def toString(self) -> str: - return ( - f"LightsRequest(id={self._request_id[:8]}..., " - f"lights={len(self._light_states)}, " - f"sessions={len(self._light_sessions)})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# LightsManager -# ═══════════════════════════════════════════════════════════════ - -class LightsManager(Stub): - """LightsManager — 灯光管理器 (桌面存根)""" - - LIGHT_ID_BACKLIGHT = LIGHT_ID_BACKLIGHT - LIGHT_ID_KEYBOARD = LIGHT_ID_KEYBOARD - LIGHT_ID_BUTTONS = LIGHT_ID_BUTTONS - LIGHT_ID_BATTERY = LIGHT_ID_BATTERY - LIGHT_ID_NOTIFICATIONS = LIGHT_ID_NOTIFICATIONS - LIGHT_ID_ATTENTION = LIGHT_ID_ATTENTION - LIGHT_ID_BLUETOOTH = LIGHT_ID_BLUETOOTH - LIGHT_ID_WIFI = LIGHT_ID_WIFI - LIGHT_ID_MICROPHONE = LIGHT_ID_MICROPHONE - LIGHT_ID_CAMERA = LIGHT_ID_CAMERA - LIGHT_ID_MICROPHONE_MUTE = LIGHT_ID_MICROPHONE_MUTE - LIGHT_ID_CAMERA_MUTE = LIGHT_ID_CAMERA_MUTE - LIGHT_ID_POWER = LIGHT_ID_POWER - LIGHT_ID_ADAPTIVE_BACKLIGHT = LIGHT_ID_ADAPTIVE_BACKLIGHT - LIGHT_ID_CHARGING = LIGHT_ID_CHARGING - - BRIGHTNESS_MIN = BRIGHTNESS_MIN - BRIGHTNESS_LOW = BRIGHTNESS_LOW - BRIGHTNESS_MEDIUM = BRIGHTNESS_MEDIUM - BRIGHTNESS_HIGH = BRIGHTNESS_HIGH - BRIGHTNESS_MAX = BRIGHTNESS_MAX - BRIGHTNESS_OFF = BRIGHTNESS_OFF - BRIGHTNESS_FULL = BRIGHTNESS_FULL - BRIGHTNESS_DEFAULT = BRIGHTNESS_DEFAULT - - LIGHT_TYPE_MANUAL = LIGHT_TYPE_MANUAL - LIGHT_TYPE_AUTOMATIC = LIGHT_TYPE_AUTOMATIC - LIGHT_TYPE_MICROPHONE = LIGHT_TYPE_MICROPHONE - LIGHT_TYPE_CAMERA = LIGHT_TYPE_CAMERA - LIGHT_TYPE_NOTIFICATION = LIGHT_TYPE_NOTIFICATION - LIGHT_TYPE_ATTENTION = LIGHT_TYPE_ATTENTION - LIGHT_TYPE_RGB = LIGHT_TYPE_RGB - LIGHT_TYPE_ADAPTIVE = LIGHT_TYPE_ADAPTIVE - LIGHT_TYPE_BLINKING = LIGHT_TYPE_BLINKING - - _instance: Optional["LightsManager"] = None - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._lock = threading.Lock() - self._lights: Dict[int, Light] = {} - self._sessions: Dict[str, LightSession] = {} - self._active_requests: List[LightsRequest] = [] - self._default_brightness = 127 - self._max_brightness = 255 - self._min_brightness = 0 - self._lights_available = True - self._initialize_default_lights() - - def _initialize_default_lights(self) -> None: - default_lights = [ - (LIGHT_ID_BACKLIGHT, "backlight", LIGHT_TYPE_AUTOMATIC), - (LIGHT_ID_KEYBOARD, "keyboard", LIGHT_TYPE_MANUAL), - (LIGHT_ID_BUTTONS, "buttons", LIGHT_TYPE_MANUAL), - (LIGHT_ID_BATTERY, "battery", LIGHT_TYPE_BATTERY), - (LIGHT_ID_NOTIFICATIONS, "notifications", LIGHT_TYPE_NOTIFICATION), - (LIGHT_ID_ATTENTION, "attention", LIGHT_TYPE_ATTENTION), - (LIGHT_ID_BLUETOOTH, "bluetooth", LIGHT_TYPE_MANUAL), - (LIGHT_ID_WIFI, "wifi", LIGHT_TYPE_MANUAL), - (LIGHT_ID_MICROPHONE, "microphone", LIGHT_TYPE_MICROPHONE), - (LIGHT_ID_CAMERA, "camera", LIGHT_TYPE_CAMERA), - (LIGHT_ID_MICROPHONE_MUTE, "microphone_mute", LIGHT_TYPE_MICROPHONE), - (LIGHT_ID_CAMERA_MUTE, "camera_mute", LIGHT_TYPE_CAMERA), - (LIGHT_ID_POWER, "power", LIGHT_TYPE_POWER), - (LIGHT_ID_ADAPTIVE_BACKLIGHT, "adaptive_backlight", LIGHT_TYPE_ADAPTIVE), - (LIGHT_ID_CHARGING, "charging", LIGHT_TYPE_BATTERY), - ] - for lid, name, ltype in default_lights: - light = Light(lid, ltype) - light.setName(name) - self._lights[lid] = light - - def getLight(self, lightId: int) -> Optional[Light]: - with self._lock: - return self._lights.get(lightId) - - def getLights(self) -> List[Light]: - with self._lock: - return list(self._lights.values()) - - def openSession(self, lightId: int) -> LightSession: - session = LightSession(lightId) - with self._lock: - self._sessions[session.getSessionId()] = session - return session - - def closeSession(self, session: LightSession) -> None: - with self._lock: - sid = session.getSessionId() - if sid in self._sessions: - session.setActive(False) - del self._sessions[sid] - - def setLightState(self, lightId: int, state: LightState) -> None: - with self._lock: - light = self._lights.get(lightId) - if light: - light.setCurrentState(state) - - def setLightStates(self, states: Dict[int, LightState]) -> None: - with self._lock: - for lid, state in states.items(): - light = self._lights.get(lid) - if light: - light.setCurrentState(state) - - def submitRequest(self, request: LightsRequest) -> None: - with self._lock: - self._active_requests.append(request) - for lid, state in request.getLightStates().items(): - light = self._lights.get(lid) - if light: - light.setCurrentState(state) - for session in request.getLightSessions(): - session.setActive(True) - self._sessions[session.getSessionId()] = session - request.setCompleted(True) - - def getActiveRequests(self) -> List[LightsRequest]: - with self._lock: - return list(self._active_requests) - - def getActiveSessions(self, lightId: int) -> List[LightSession]: - with self._lock: - return [s for s in self._sessions.values() if s.getLightId() == lightId and s.isActive()] - - def getDefaultBrightness(self) -> int: - return self._default_brightness - - def setDefaultBrightness(self, brightness: int) -> None: - self._default_brightness = max(0, min(255, brightness)) - - def getMaxBrightness(self) -> int: - return self._max_brightness - - def getMinBrightness(self) -> int: - return self._min_brightness - - def isAvailable(self) -> bool: - return self._lights_available - - def toString(self) -> str: - with self._lock: - return ( - f"LightsManager(lights={len(self._lights)}, " - f"sessions={len(self._sessions)}, " - f"requests={len(self._active_requests)})" - ) \ No newline at end of file diff --git a/android_pyapi/android/hardware_location.py b/android_pyapi/android/hardware_location.py deleted file mode 100644 index 6ad2450..0000000 --- a/android_pyapi/android/hardware_location.py +++ /dev/null @@ -1,848 +0,0 @@ -""" -Android hardware.location 包 — ContextHubManager, ContextHubClient, -ContextHubInfo, NanoApp, NanoAppInstanceInfo, NanoAppFilter 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "ContextHubManager", "ContextHubClient", "ContextHubInfo", - "ContextHubTransaction", "ContextHubIntentEvent", - "ContextHubService", "ContextHubServiceCallback", - "NanoApp", "NanoAppInstanceInfo", "NanoAppFilter", - "NanoAppMessage", "NanoAppRpcService", "NanoAppStateInfo", - "ContextHubManagerCallback", -] - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -HUB_TYPE_PRIMARY = 0 -HUB_TYPE_SECONDARY = 1 -HUB_TYPE_VIRTUAL = 2 - -HUB_STATUS_OK = 0 -HUB_STATUS_ERROR = -1 -HUB_STATUS_TIMEOUT = -2 -HUB_STATUS_BUSY = -3 -HUB_STATUS_NOT_FOUND = -4 -HUB_STATUS_INVALID_PARAM = -5 -HUB_STATUS_UNSUPPORTED = -6 -HUB_STATUS_DISABLED = -7 -HUB_STATUS_RESTARTING = -8 -HUB_STATUS_LOW_POWER = -9 - -TRANSACTION_SUCCESS = 0 -TRANSACTION_FAILURE = -1 -TRANSACTION_PENDING = 1 -TRANSACTION_CANCELLED = -2 - -TRANSACTION_TYPE_LOAD_NANOAPP = 0 -TRANSACTION_TYPE_UNLOAD_NANOAPP = 1 -TRANSACTION_TYPE_QUERY_NANOAPPS = 2 -TRANSACTION_TYPE_QUERY_MEMORY = 3 -TRANSACTION_TYPE_RESET_HUB = 4 -TRANSACTION_TYPE_ENABLE_NANOAPP = 5 -TRANSACTION_TYPE_DISABLE_NANOAPP = 6 -TRANSACTION_TYPE_QUERY_NANOAPP_DETAILS = 7 -TRANSACTION_TYPE_CONFIGURE_HUB = 8 -TRANSACTION_TYPE_SET_NANOAPP_LOADED = 9 - -NANOAPP_STATE_NOT_LOADED = 0 -NANOAPP_STATE_LOADING = 1 -NANOAPP_STATE_LOADED = 2 -NANOAPP_STATE_UNLOADING = 3 -NANOAPP_STATE_RUNNING = 4 -NANOAPP_STATE_SUSPENDED = 5 -NANOAPP_STATE_ERROR = 6 -NANOAPP_STATE_STOPPED = 7 -NANOAPP_STATE_CRASHED = 8 - -NANOAPP_FLAG_ENABLED = 1 -NANOAPP_FLAG_DISABLED = 2 -NANOAPP_FLAG_SYSTEM = 4 -NANOAPP_FLAG_VENDOR = 8 -NANOAPP_FLAG_SIGNED = 16 -NANOAPP_FLAG_DEBUG = 32 - -NANOAPP_TYPE_ANY = -1 -NANOAPP_TYPE_SENSOR = 0 -NANOAPP_TYPE_AUDIO = 1 -NANOAPP_TYPE_LOCATION = 2 -NANOAPP_TYPE_ACTIVITY = 3 -NANOAPP_TYPE_CONTEXT = 4 -NANOAPP_TYPE_COMMUNICATION = 5 -NANOAPP_TYPE_SECURITY = 6 -NANOAPP_TYPE_CUSTOM = 100 - - -# ═══════════════════════════════════════════════════════════════ -# NanoApp -# ═══════════════════════════════════════════════════════════════ - -class NanoApp(Stub): - """NanoApp — Nano 应用程序""" - - def __init__(self, id: int = 0, name: str = "", vendor: str = "", - version: int = 1, size: int = 0, - type_: int = NANOAPP_TYPE_CUSTOM, - binary: bytes = b"", - flags: int = 0, description: str = ""): - super().__init__() - self._id = id - self._name = name - self._vendor = vendor - self._version = version - self._size = size if size > 0 else len(binary) - self._type = type_ - self._binary = binary - self._flags = flags - self._description = description - - def getId(self) -> int: - return self._id - - def getName(self) -> str: - return self._name - - def getVendor(self) -> str: - return self._vendor - - def getVersion(self) -> int: - return self._version - - def getSize(self) -> int: - return self._size - - def getType(self) -> int: - return self._type - - def getBinary(self) -> bytes: - return self._binary - - def getFlags(self) -> int: - return self._flags - - def getDescription(self) -> str: - return self._description - - def toString(self) -> str: - return f"NanoApp(id={self._id}, name={self._name}, version={self._version})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, NanoApp): - return False - return self._id == other._id - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# NanoAppMessage -# ═══════════════════════════════════════════════════════════════ - -class NanoAppMessage(Stub): - """NanoAppMessage — NanoApp 消息""" - - def __init__(self, nanoAppId: int = 0, messageType: int = 0, - data: bytes = b"", isReliable: bool = False, - isSequenced: bool = True): - super().__init__() - self._nano_app_id = nanoAppId - self._message_type = messageType - self._data = data - self._is_reliable = isReliable - self._is_sequenced = isSequenced - - def getNanoAppId(self) -> int: - return self._nano_app_id - - def getMessageType(self) -> int: - return self._message_type - - def getData(self) -> bytes: - return self._data - - def isReliable(self) -> bool: - return self._is_reliable - - def isSequenced(self) -> bool: - return self._is_sequenced - - def toString(self) -> str: - return f"NanoAppMessage(appId={self._nano_app_id}, type={self._message_type})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, NanoAppMessage): - return False - return self._nano_app_id == other._nano_app_id and self._message_type == other._message_type - - def hashCode(self) -> int: - return hash((self._nano_app_id, self._message_type)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# NanoAppInstanceInfo -# ═══════════════════════════════════════════════════════════════ - -class NanoAppInstanceInfo(Stub): - """NanoAppInstanceInfo — NanoApp 实例信息""" - - def __init__(self, id: int = 0, name: str = "", vendor: str = "", - version: int = 1, state: int = NANOAPP_STATE_NOT_LOADED, - type_: int = NANOAPP_TYPE_CUSTOM, - flags: int = 0, memUsage: int = 0, - handle: int = 0, isEnabled: bool = True): - super().__init__() - self._id = id - self._name = name - self._vendor = vendor - self._version = version - self._state = state - self._type = type_ - self._flags = flags - self._mem_usage = memUsage - self._handle = handle - self._is_enabled = isEnabled - - def getId(self) -> int: - return self._id - - def getName(self) -> str: - return self._name - - def getVendor(self) -> str: - return self._vendor - - def getVersion(self) -> int: - return self._version - - def getState(self) -> int: - return self._state - - def getType(self) -> int: - return self._type - - def getFlags(self) -> int: - return self._flags - - def getMemUsage(self) -> int: - return self._mem_usage - - def getHandle(self) -> int: - return self._handle - - def isEnabled(self) -> bool: - return self._is_enabled - - def toString(self) -> str: - return f"NanoAppInstanceInfo(id={self._id}, name={self._name}, state={self._state})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, NanoAppInstanceInfo): - return False - return self._id == other._id - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# NanoAppFilter -# ═══════════════════════════════════════════════════════════════ - -class NanoAppFilter(Stub): - """NanoAppFilter — NanoApp 过滤器""" - - def __init__(self, nanoAppId: int = -1, type_: int = NANOAPP_TYPE_ANY, - vendor: str = "", name: str = "", - minVersion: int = 0, maxVersion: int = -1, - flags: int = 0): - super().__init__() - self._nano_app_id = nanoAppId - self._type = type_ - self._vendor = vendor - self._name = name - self._min_version = minVersion - self._max_version = maxVersion - self._flags = flags - - def getNanoAppId(self) -> int: - return self._nano_app_id - - def getType(self) -> int: - return self._type - - def getVendor(self) -> str: - return self._vendor - - def getName(self) -> str: - return self._name - - def getMinVersion(self) -> int: - return self._min_version - - def getMaxVersion(self) -> int: - return self._max_version - - def getFlags(self) -> int: - return self._flags - - def matches(self, info: NanoAppInstanceInfo) -> bool: - if self._nano_app_id != -1 and info.getId() != self._nano_app_id: - return False - if self._type != NANOAPP_TYPE_ANY and info.getType() != self._type: - return False - if self._vendor and self._vendor != info.getVendor(): - return False - return True - - class Builder(Stub): - """NanoAppFilter.Builder — 过滤器构建器""" - - def __init__(self): - super().__init__() - self._nano_app_id = -1 - self._type = NANOAPP_TYPE_ANY - self._vendor = "" - self._name = "" - self._min_version = 0 - self._max_version = -1 - self._flags = 0 - - def setNanoAppId(self, id: int) -> "NanoAppFilter.Builder": - self._nano_app_id = id - return self - - def setType(self, type_: int) -> "NanoAppFilter.Builder": - self._type = type_ - return self - - def setVendor(self, vendor: str) -> "NanoAppFilter.Builder": - self._vendor = vendor - return self - - def build(self) -> "NanoAppFilter": - return NanoAppFilter( - nanoAppId=self._nano_app_id, type_=self._type, - vendor=self._vendor, name=self._name, - minVersion=self._min_version, maxVersion=self._max_version, - flags=self._flags, - ) - - def toString(self) -> str: - return f"NanoAppFilter(id={self._nano_app_id}, type={self._type})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, NanoAppFilter): - return False - return (self._nano_app_id == other._nano_app_id and - self._type == other._type) - - def hashCode(self) -> int: - return hash((self._nano_app_id, self._type)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# NanoAppRpcService -# ═══════════════════════════════════════════════════════════════ - -class NanoAppRpcService(Stub): - """NanoAppRpcService — NanoApp RPC 服务""" - - def __init__(self, id: int = 0, name: str = "", version: int = 1, - description: str = "", methods: List[str] = None): - super().__init__() - self._id = id - self._name = name - self._version = version - self._description = description - self._methods = methods or [] - - def getId(self) -> int: - return self._id - - def getName(self) -> str: - return self._name - - def getVersion(self) -> int: - return self._version - - def getDescription(self) -> str: - return self._description - - def getMethods(self) -> List[str]: - return list(self._methods) - - def toString(self) -> str: - return f"NanoAppRpcService(id={self._id}, name={self._name})" - - -# ═══════════════════════════════════════════════════════════════ -# NanoAppStateInfo -# ═══════════════════════════════════════════════════════════════ - -class NanoAppStateInfo(Stub): - """NanoAppStateInfo — NanoApp 状态信息""" - - def __init__(self, nanoAppId: int = 0, state: int = NANOAPP_STATE_NOT_LOADED, - isEnabled: bool = False, memUsage: int = 0, - uptimeMs: int = 0, lastError: int = 0, - errorMessage: str = ""): - super().__init__() - self._nano_app_id = nanoAppId - self._state = state - self._is_enabled = isEnabled - self._mem_usage = memUsage - self._uptime_ms = uptimeMs - self._last_error = lastError - self._error_message = errorMessage - - def getNanoAppId(self) -> int: - return self._nano_app_id - - def getState(self) -> int: - return self._state - - def isEnabled(self) -> bool: - return self._is_enabled - - def getMemUsage(self) -> int: - return self._mem_usage - - def getUptimeMs(self) -> int: - return self._uptime_ms - - def getLastError(self) -> int: - return self._last_error - - def getErrorMessage(self) -> str: - return self._error_message - - def toString(self) -> str: - return f"NanoAppStateInfo(id={self._nano_app_id}, state={self._state})" - - -# ═══════════════════════════════════════════════════════════════ -# ContextHubInfo -# ═══════════════════════════════════════════════════════════════ - -class ContextHubInfo(Stub): - """ContextHubInfo — 上下文 Hub 信息""" - - def __init__(self, id: int = 0, name: str = "", - vendor: str = "", version: int = 1, - type_: int = HUB_TYPE_PRIMARY, - maxSensors: int = 32, maxNanoApps: int = 16, - memorySize: int = 256 * 1024, - supportedSensors: List[int] = None, - isAvailable: bool = True): - super().__init__() - self._id = id - self._name = name - self._vendor = vendor - self._version = version - self._type = type_ - self._max_sensors = maxSensors - self._max_nano_apps = maxNanoApps - self._memory_size = memorySize - self._supported_sensors = supportedSensors or [] - self._is_available = isAvailable - - def getId(self) -> int: - return self._id - - def getName(self) -> str: - return self._name - - def getVendor(self) -> str: - return self._vendor - - def getVersion(self) -> int: - return self._version - - def getHubType(self) -> int: - return self._type - - def getMaxSensors(self) -> int: - return self._max_sensors - - def getMaxNanoApps(self) -> int: - return self._max_nano_apps - - def getMemorySize(self) -> int: - return self._memory_size - - def getSupportedSensors(self) -> List[int]: - return list(self._supported_sensors) - - def isAvailable(self) -> bool: - return self._is_available - - def toString(self) -> str: - return f"ContextHubInfo(id={self._id}, name={self._name})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, ContextHubInfo): - return False - return self._id == other._id - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# ContextHubTransaction -# ═══════════════════════════════════════════════════════════════ - -class ContextHubTransaction(Stub): - """ContextHubTransaction — ContextHub 事务""" - - def __init__(self, id: int = 0, type_: int = TRANSACTION_TYPE_QUERY_NANOAPPS, - status: int = TRANSACTION_PENDING, - result: Any = None): - super().__init__() - self._id = id - self._type = type_ - self._status = status - self._result = result - - def getId(self) -> int: - return self._id - - def getType(self) -> int: - return self._type - - def getStatus(self) -> int: - return self._status - - def getResult(self) -> Any: - return self._result - - def setResult(self, result: Any): - self._result = result - - def waitForResult(self, timeoutMs: int = 0) -> Any: - return self._result - - def cancel(self): - self._status = TRANSACTION_CANCELLED - - def toString(self) -> str: - return f"ContextHubTransaction(id={self._id}, type={self._type}, status={self._status})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, ContextHubTransaction): - return False - return self._id == other._id - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# ContextHubIntentEvent -# ═══════════════════════════════════════════════════════════════ - -class ContextHubIntentEvent(Stub): - """ContextHubIntentEvent — ContextHub 意图事件""" - - EVENT_HUB_RESTARTED = 0 - EVENT_HUB_ERROR = 1 - EVENT_NANOAPP_LOADED = 2 - EVENT_NANOAPP_UNLOADED = 3 - EVENT_NANOAPP_STARTED = 4 - EVENT_NANOAPP_STOPPED = 5 - EVENT_NANOAPP_ERROR = 6 - EVENT_NANOAPP_MESSAGE = 7 - EVENT_NANOAPP_CRASHED = 8 - - def __init__(self, eventType: int = EVENT_HUB_RESTARTED, - hubId: int = 0, nanoAppId: int = 0, - timestamp: int = 0, data: bytes = b""): - super().__init__() - self._event_type = eventType - self._hub_id = hubId - self._nano_app_id = nanoAppId - self._timestamp = timestamp - self._data = data - - def getEventType(self) -> int: - return self._event_type - - def getHubId(self) -> int: - return self._hub_id - - def getNanoAppId(self) -> int: - return self._nano_app_id - - def getTimestamp(self) -> int: - return self._timestamp - - def getData(self) -> bytes: - return self._data - - def toString(self) -> str: - return f"ContextHubIntentEvent(type={self._event_type}, hub={self._hub_id})" - - -# ═══════════════════════════════════════════════════════════════ -# ContextHubManagerCallback -# ═══════════════════════════════════════════════════════════════ - -class ContextHubManagerCallback(Stub): - """ContextHubManagerCallback — ContextHub 管理器回调""" - - def onHubStarted(self, hubInfo: ContextHubInfo): - pass - - def onHubStopped(self, hubInfo: ContextHubInfo): - pass - - def onHubError(self, hubInfo: ContextHubInfo, errorCode: int): - pass - - def onNanoAppLoaded(self, hubId: int, nanoAppId: int): - pass - - def onNanoAppUnloaded(self, hubId: int, nanoAppId: int): - pass - - def onNanoAppStarted(self, hubId: int, nanoAppId: int): - pass - - def onNanoAppStopped(self, hubId: int, nanoAppId: int): - pass - - def onNanoAppCrashed(self, hubId: int, nanoAppId: int): - pass - - def onNanoAppMessage(self, hubId: int, message: NanoAppMessage): - pass - - def toString(self) -> str: - return f"ContextHubManagerCallback@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# ContextHubClient -# ═══════════════════════════════════════════════════════════════ - -class ContextHubClient(Stub): - """ContextHubClient — ContextHub 客户端""" - - def __init__(self, hubInfo: Optional[ContextHubInfo] = None, - callback: Optional[ContextHubManagerCallback] = None): - super().__init__() - self._hub_info = hubInfo - self._callback = callback - - def getHubInfo(self) -> Optional[ContextHubInfo]: - return self._hub_info - - def sendMessageToNanoApp(self, nanoAppId: int, message: NanoAppMessage) -> int: - return TRANSACTION_SUCCESS - - def sendMessageToHub(self, message: NanoAppMessage) -> int: - return TRANSACTION_SUCCESS - - def requestNanoAppState(self, nanoAppId: int) -> Optional[NanoAppStateInfo]: - return None - - def enableNanoApp(self, nanoAppId: int) -> int: - return TRANSACTION_SUCCESS - - def disableNanoApp(self, nanoAppId: int) -> int: - return TRANSACTION_SUCCESS - - def close(self): - pass - - def setCallback(self, callback: ContextHubManagerCallback): - self._callback = callback - - def getCallback(self) -> Optional[ContextHubManagerCallback]: - return self._callback - - def toString(self) -> str: - return f"ContextHubClient(hub={self._hub_info})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# ContextHubService / ContextHubServiceCallback -# ═══════════════════════════════════════════════════════════════ - -class ContextHubService(Stub): - """ContextHubService — ContextHub 服务""" - - SERVICE_NAME = "contexthub" - - def __init__(self): - super().__init__() - - def startHub(self, hubInfo: ContextHubInfo) -> int: - return TRANSACTION_SUCCESS - - def stopHub(self, hubInfo: ContextHubInfo) -> int: - return TRANSACTION_SUCCESS - - def resetHub(self, hubInfo: ContextHubInfo) -> int: - return TRANSACTION_SUCCESS - - def configureHub(self, hubInfo: ContextHubInfo, config: bytes) -> int: - return TRANSACTION_SUCCESS - - def toString(self) -> str: - return "ContextHubService()" - - -class ContextHubServiceCallback(Stub): - """ContextHubServiceCallback — ContextHub 服务回调""" - - def onHubStarted(self, hubId: int): - pass - - def onHubStopped(self, hubId: int): - pass - - def onHubReset(self, hubId: int): - pass - - def onHubError(self, hubId: int, errorCode: int): - pass - - def toString(self) -> str: - return f"ContextHubServiceCallback@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# ContextHubManager -# ═══════════════════════════════════════════════════════════════ - -class ContextHubManager(Stub): - """ContextHubManager — ContextHub 管理器""" - - def __init__(self): - super().__init__() - self._hubs: Dict[int, ContextHubInfo] = {} - self._clients: Dict[int, ContextHubClient] = {} - self._callbacks: Dict[int, ContextHubManagerCallback] = {} - - def getHubs(self, filter_: Optional[NanoAppFilter] = None) -> List[ContextHubInfo]: - if filter_ is None: - return list(self._hubs.values()) - return [h for h in self._hubs.values() if h.getId() == filter_.getNanoAppId()] - - def createClient(self, hubInfo: ContextHubInfo, - callback: ContextHubManagerCallback) -> ContextHubClient: - client = ContextHubClient(hubInfo=hubInfo, callback=callback) - cid = id(client) - self._clients[cid] = client - if callback: - cb_id = id(callback) - self._callbacks[cb_id] = callback - return client - - def destroyClient(self, client: ContextHubClient): - cid = id(client) - self._clients.pop(cid, None) - cb = client.getCallback() - if cb: - self._callbacks.pop(id(cb), None) - - def registerCallback(self, hubInfo: ContextHubInfo, - callback: ContextHubManagerCallback): - cb_id = id(callback) - self._callbacks[cb_id] = callback - - def unregisterCallback(self, hubInfo: ContextHubInfo, - callback: ContextHubManagerCallback): - cb_id = id(callback) - self._callbacks.pop(cb_id, None) - - def loadNanoApp(self, hubInfo: ContextHubInfo, nanoApp: NanoApp) -> ContextHubTransaction: - tx = ContextHubTransaction(type_=TRANSACTION_TYPE_LOAD_NANOAPP) - tx.setResult(TRANSACTION_SUCCESS) - return tx - - def unloadNanoApp(self, hubInfo: ContextHubInfo, nanoAppId: int) -> ContextHubTransaction: - tx = ContextHubTransaction(type_=TRANSACTION_TYPE_UNLOAD_NANOAPP) - tx.setResult(TRANSACTION_SUCCESS) - return tx - - def queryNanoApps(self, hubInfo: ContextHubInfo) -> ContextHubTransaction: - tx = ContextHubTransaction(type_=TRANSACTION_TYPE_QUERY_NANOAPPS) - tx.setResult([]) - return tx - - def queryNanoAppDetails(self, hubInfo: ContextHubInfo, nanoAppId: int) -> ContextHubTransaction: - tx = ContextHubTransaction(type_=TRANSACTION_TYPE_QUERY_NANOAPP_DETAILS) - tx.setResult(NanoAppInstanceInfo(id=nanoAppId)) - return tx - - def queryHubMemory(self, hubInfo: ContextHubInfo) -> ContextHubTransaction: - tx = ContextHubTransaction(type_=TRANSACTION_TYPE_QUERY_MEMORY) - tx.setResult({"total": 262144, "free": 131072}) - return tx - - def resetHub(self, hubInfo: ContextHubInfo) -> ContextHubTransaction: - tx = ContextHubTransaction(type_=TRANSACTION_TYPE_RESET_HUB) - tx.setResult(TRANSACTION_SUCCESS) - return tx - - def enableNanoApp(self, hubInfo: ContextHubInfo, nanoAppId: int) -> int: - return TRANSACTION_SUCCESS - - def disableNanoApp(self, hubInfo: ContextHubInfo, nanoAppId: int) -> int: - return TRANSACTION_SUCCESS - - def findNanoAppByFilter(self, filter_: NanoAppFilter) -> List[NanoAppInstanceInfo]: - return [] - - def getNanoAppInstanceInfo(self, hubInfo: ContextHubInfo, - nanoAppId: int) -> Optional[NanoAppInstanceInfo]: - return None - - def addHub(self, hubInfo: ContextHubInfo): - self._hubs[hubInfo.getId()] = hubInfo - - def removeHub(self, hubId: int): - self._hubs.pop(hubId, None) - - def toString(self) -> str: - return f"ContextHubManager(hubs={len(self._hubs)})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/hardware_radio.py b/android_pyapi/android/hardware_radio.py deleted file mode 100644 index b365a41..0000000 --- a/android_pyapi/android/hardware_radio.py +++ /dev/null @@ -1,417 +0,0 @@ -""" -Android hardware.radio 组件 — RadioManager, RadioTuner, RadioMetadata 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import threading -from typing import Any, Callable, Dict, List, Optional, Set - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# RadioMetadata -# ═══════════════════════════════════════════════════════════════ - -class RadioMetadata(Stub): - """RadioMetadata — 收音机元数据""" - - METADATA_KEY_ART = 0x00000001 - METADATA_KEY_ARTIST = 0x00000002 - METADATA_KEY_TITLE = 0x00000003 - METADATA_KEY_ALBUM = 0x00000004 - METADATA_KEY_GENRE = 0x00000005 - METADATA_KEY_CLOCK = 0x00000006 - METADATA_KEY_ICON = 0x00000007 - METADATA_KEY_RDS_PI = 0x00000008 - METADATA_KEY_RDS_PS = 0x00000009 - METADATA_KEY_RDS_RT = 0x0000000A - METADATA_KEY_DAB_ENSEMBLE_NAME = 0x0000000B - METADATA_KEY_DAB_ECC = 0x0000000C - METADATA_KEY_DAB_IDENTIFIER = 0x0000000D - METADATA_KEY_DAB_SERVICE_NAME = 0x0000000E - - def __init__(self): - super().__init__() - self._data: Dict[int, Any] = {} - - def getString(self, key: int) -> Optional[str]: - val = self._data.get(key) - return str(val) if val is not None else None - - def getInt(self, key: int) -> int: - return int(self._data.get(key, 0)) - - def getLong(self, key: int) -> int: - return int(self._data.get(key, 0)) - - def getBitmap(self, key: int) -> Any: - return self._data.get(key) - - def getText(self, key: int) -> Optional[str]: - return self.getString(key) - - def containsKey(self, key: int) -> bool: - return key in self._data - - def keySet(self) -> Set[int]: - return set(self._data.keys()) - - def putString(self, key: int, value: str): - self._data[key] = value - - def putInt(self, key: int, value: int): - self._data[key] = value - - def toString(self) -> str: - return f"RadioMetadata{{{len(self._data)} entries}}" - - -# ═══════════════════════════════════════════════════════════════ -# RadioManager.ProgramInfo -# ═══════════════════════════════════════════════════════════════ - -class RadioManager: - """RadioManager — 收音机管理器(命名空间)""" - - STATUS_SUCCESS = 0 - STATUS_ERROR = -1 - STATUS_INVALID_ARGUMENTS = -2 - STATUS_INVALID_OPERATION = -3 - STATUS_NO_ENTRY = -4 - STATUS_PERMISSION_DENIED = -5 - STATUS_NOT_SUPPORTED = -6 - STATUS_NOT_INITIALIZED = -7 - STATUS_BUSY = -8 - STATUS_TIMED_OUT = -9 - STATUS_DEAD_OBJECT = -10 - - -class RadioManager_ProgramInfo(Stub): - """RadioManager.ProgramInfo — 节目信息""" - - INFO_TYPE_MIN = 0 - INFO_TYPE_MAX = 1 - - def __init__(self): - super().__init__() - self._metadata = RadioMetadata() - self._info_type: int = 0 - self._channel: int = 0 - self._tuning_frequency: int = 0 - self._program_type: int = 0 - self._digital: bool = False - self._emergency: bool = False - self._traffic: bool = False - self._music: bool = False - self._news: bool = False - self._talk: bool = False - self._sport: bool = False - self._weather: bool = False - self._classical: bool = False - - def getRadioMetadata(self) -> RadioMetadata: - return self._metadata - - def getInfoType(self) -> int: - return self._info_type - - def getChannel(self) -> int: - return self._channel - - def getTuningFrequency(self) -> int: - return self._tuning_frequency - - def getProgramType(self) -> int: - return self._program_type - - def isDigital(self) -> bool: - return self._digital - - def isEmergency(self) -> bool: - return self._emergency - - def isTraffic(self) -> bool: - return self._traffic - - def isMusic(self) -> bool: - return self._music - - def isNews(self) -> bool: - return self._news - - def isTalk(self) -> bool: - return self._talk - - def isSport(self) -> bool: - return self._sport - - def isWeather(self) -> bool: - return self._weather - - def isClassical(self) -> bool: - return self._classical - - def toString(self) -> str: - return f"ProgramInfo{{channel={self._channel}, digital={self._digital}}}" - - -RadioManager.ProgramInfo = RadioManager_ProgramInfo - - -# ═══════════════════════════════════════════════════════════════ -# RadioManager.ModuleProperties -# ═══════════════════════════════════════════════════════════════ - -class RadioManager_ModuleProperties(Stub): - """RadioManager.ModuleProperties — 模块属性""" - - def __init__(self, id_: int = 0, service_name: str = "radio", - class_id: int = 0, implementor: str = "Android-pyApi", - hal_version: int = 2, - supported_program_types: Optional[List[int]] = None, - supported_sound_bands: Optional[List[int]] = None, - supported_deemphasis: Optional[List[int]] = None, - supported_regions: Optional[List[int]] = None, - min_freq: int = 87500, max_freq: int = 108000, - freq_step: int = 100): - super().__init__() - self._id = id_ - self._service_name = service_name - self._class_id = class_id - self._implementor = implementor - self._hal_version = hal_version - self._supported_program_types = supported_program_types or [0, 1] - self._supported_sound_bands = supported_sound_bands or [0] - self._supported_deemphasis = supported_deemphasis or [50] - self._supported_regions = supported_regions or [0] - self._min_freq = min_freq - self._max_freq = max_freq - self._freq_step = freq_step - self._vendor_info: Dict[str, str] = {} - self._initialization_complete: bool = True - self._bg_scan_available: bool = True - self._program_info_available: bool = True - self._antenna_connected: bool = True - - def getId(self) -> int: - return self._id - - def getServiceName(self) -> str: - return self._service_name - - def getClassId(self) -> int: - return self._class_id - - def getImplementor(self) -> str: - return self._implementor - - def getHALVersion(self) -> int: - return self._hal_version - - def getSupportedProgramTypes(self) -> List[int]: - return list(self._supported_program_types) - - def getSupportedSoundBands(self) -> List[int]: - return list(self._supported_sound_bands) - - def getSupportedDeemphasis(self) -> List[int]: - return list(self._supported_deemphasis) - - def getSupportedRegions(self) -> List[int]: - return list(self._supported_regions) - - def getMinTuningFrequency(self) -> int: - return self._min_freq - - def getMaxTuningFrequency(self) -> int: - return self._max_freq - - def getTuningFrequencyStep(self) -> int: - return self._freq_step - - def getVendorInfo(self) -> Dict[str, str]: - return dict(self._vendor_info) - - def isInitializationComplete(self) -> bool: - return self._initialization_complete - - def isBgScanAvailable(self) -> bool: - return self._bg_scan_available - - def isProgramInfoAvailable(self) -> bool: - return self._program_info_available - - def isAntennaConnected(self) -> bool: - return self._antenna_connected - - def toString(self) -> str: - return f"ModuleProperties{{id={self._id}, name={self._service_name}}}" - - -RadioManager.ModuleProperties = RadioManager_ModuleProperties - - -# ═══════════════════════════════════════════════════════════════ -# RadioTuner -# ═══════════════════════════════════════════════════════════════ - -class RadioTuner(Stub): - """RadioTuner — 收音机调谐器""" - - DIRECTION_DOWN = 0 - DIRECTION_UP = 1 - - def __init__(self): - super().__init__() - self._frequency: int = 100000 # 100 MHz - self._muted: bool = False - self._configuration: Optional[Dict[str, Any]] = None - - def scan(self, direction: int, skipSubChannel: bool = False) -> int: - step = 100 if direction == self.DIRECTION_UP else -100 - self._frequency += step - return RadioManager.STATUS_SUCCESS - - def step(self, direction: int, skipSubChannel: bool = False) -> int: - return self.scan(direction, skipSubChannel) - - def tune(self, frequency: int, programType: int = -1) -> int: - self._frequency = frequency - return RadioManager.STATUS_SUCCESS - - def cancel(self) -> int: - return RadioManager.STATUS_SUCCESS - - def cancelAnnouncement(self) -> int: - return RadioManager.STATUS_SUCCESS - - def getConfiguration(self) -> Optional[Dict[str, Any]]: - return self._configuration - - def setConfiguration(self, configuration: Dict[str, Any]) -> int: - self._configuration = dict(configuration) - return RadioManager.STATUS_SUCCESS - - def getMute(self) -> bool: - return self._muted - - def setMute(self, mute: bool) -> int: - self._muted = mute - return RadioManager.STATUS_SUCCESS - - def getProgramInformation(self, fullInfo: bool = True) -> Optional[RadioManager.ProgramInfo]: - info = RadioManager.ProgramInfo() - info._tuning_frequency = self._frequency - return info - - def isAntennaConnected(self) -> bool: - return True - - def hasControl(self) -> bool: - return True - - def getParameters(self, keys: List[str]) -> Dict[str, str]: - return {} - - def setParameters(self, parameters: Dict[str, str]) -> int: - return RadioManager.STATUS_SUCCESS - - def close(self): - pass - - def toString(self) -> str: - return f"RadioTuner{{frequency={self._frequency}}}" - - -# ═══════════════════════════════════════════════════════════════ -# RadioManager (main class) -# ═══════════════════════════════════════════════════════════════ - -class RadioManager(Stub): - """RadioManager — 收音机管理器""" - - STATUS_SUCCESS = 0 - STATUS_ERROR = -1 - STATUS_INVALID_ARGUMENTS = -2 - STATUS_INVALID_OPERATION = -3 - STATUS_NO_ENTRY = -4 - STATUS_PERMISSION_DENIED = -5 - STATUS_NOT_SUPPORTED = -6 - STATUS_NOT_INITIALIZED = -7 - STATUS_BUSY = -8 - STATUS_TIMED_OUT = -9 - STATUS_DEAD_OBJECT = -10 - - def __init__(self): - super().__init__() - self._lock = threading.Lock() - self._modules: List[RadioManager.ModuleProperties] = [ - RadioManager.ModuleProperties(id_=0, service_name="FM Radio"), - ] - self._tuner: Optional[RadioTuner] = None - self._enabled: bool = False - self._callbacks: List[Any] = [] - - def listModules(self, modules: List[Any]) -> int: - modules.clear() - modules.extend(self._modules) - return self.STATUS_SUCCESS - - def open(self, moduleId: int, tunerCallback: Any) -> Optional[RadioTuner]: - self._tuner = RadioTuner() - return self._tuner - - def close(self, tuner: RadioTuner): - tuner.close() - self._tuner = None - - def getModuleProperties(self, moduleId: int) -> Optional[RadioManager.ModuleProperties]: - for m in self._modules: - if m.getId() == moduleId: - return m - return None - - def getParameters(self, moduleId: int, keys: List[str]) -> Dict[str, str]: - return {} - - def setParameters(self, moduleId: int, parameters: Dict[str, str]) -> int: - return self.STATUS_SUCCESS - - def isEnabled(self) -> bool: - return self._enabled - - def registerCallback(self, callback: Any, handler: Any = None) -> int: - if callback not in self._callbacks: - self._callbacks.append(callback) - return self.STATUS_SUCCESS - - def unregisterCallback(self, callback: Any) -> int: - if callback in self._callbacks: - self._callbacks.remove(callback) - return self.STATUS_SUCCESS - - def registerClient(self, packageName: str, callback: Any) -> int: - return self.STATUS_SUCCESS - - def unregisterClient(self, packageName: str) -> int: - return self.STATUS_SUCCESS - - def toString(self) -> str: - return f"RadioManager@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "RadioManager", - "RadioTuner", - "RadioMetadata", -] \ No newline at end of file diff --git a/android_pyapi/android/hardware_sensor_ext.py b/android_pyapi/android/hardware_sensor_ext.py deleted file mode 100644 index 651b3d8..0000000 --- a/android_pyapi/android/hardware_sensor_ext.py +++ /dev/null @@ -1,831 +0,0 @@ -""" -Android hardware sensor 扩展组件 — Sensor (扩展), SensorEvent, -SensorDirectChannel, SensorPrivacyManager, SensorOffload, -SensorRequest 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import math -import time -import random -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from .hardware import SensorManager as _BaseSensorManager -from .hardware import Sensor as _BaseSensor -from .hardware import SensorEvent as _BaseSensorEvent -from .hardware import SensorEventListener as _BaseSensorEventListener - -__all__ = [ - "Sensor", "SensorEvent", "SensorEventListener", - "SensorManager", "SensorDirectChannel", - "SensorDirectChannelConfig", "SensorPrivacyManager", - "SensorAvailability", "SensorAdditionalInfo", - "SensorDynamicSensorCallback", "SensorOffload", - "SensorOffloadSession", "SensorRequest", -] - - -# ═══════════════════════════════════════════════════════════════ -# 传感器常量 -# ═══════════════════════════════════════════════════════════════ - -TYPE_ACCELEROMETER = 1 -TYPE_MAGNETIC_FIELD = 2 -TYPE_ORIENTATION = 3 -TYPE_GYROSCOPE = 4 -TYPE_LIGHT = 5 -TYPE_PRESSURE = 6 -TYPE_TEMPERATURE = 7 -TYPE_PROXIMITY = 8 -TYPE_GRAVITY = 9 -TYPE_LINEAR_ACCELERATION = 10 -TYPE_ROTATION_VECTOR = 11 -TYPE_RELATIVE_HUMIDITY = 12 -TYPE_AMBIENT_TEMPERATURE = 13 -TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14 -TYPE_GAME_ROTATION_VECTOR = 15 -TYPE_GYROSCOPE_UNCALIBRATED = 16 -TYPE_SIGNIFICANT_MOTION = 17 -TYPE_STEP_DETECTOR = 18 -TYPE_STEP_COUNTER = 19 -TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20 -TYPE_HEART_RATE = 21 -TYPE_TILT_DETECTOR = 22 -TYPE_WAKE_GESTURE = 23 -TYPE_GLANCE_GESTURE = 24 -TYPE_PICK_UP_GESTURE = 25 -TYPE_WRIST_TILT_GESTURE = 26 -TYPE_DEVICE_ORIENTATION = 27 -TYPE_STATIONARY_DETECT = 28 -TYPE_POSE_6DOF = 28 -TYPE_STATIONARY_DETECT_EXT = 29 -TYPE_MOTION_DETECT = 30 -TYPE_HEART_BEAT = 31 -TYPE_LOW_LATENCY_OFFBODY_DETECT = 34 -TYPE_ACCELEROMETER_LIMITED_AXES = 35 -TYPE_GYROSCOPE_LIMITED_AXES = 36 -TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED = 37 -TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED = 38 -TYPE_HEAD_TRACKER = 39 -TYPE_ACCELEROMETER_UNCALIBRATED = 40 -TYPE_HINGE_ANGLE = 41 - -REPORTING_MODE_CONTINUOUS = 0 -REPORTING_MODE_ON_CHANGE = 1 -REPORTING_MODE_ONE_SHOT = 2 -REPORTING_MODE_SPECIAL_TRIGGER = 3 - -DIRECT_RATE_NONE = 0 -DIRECT_RATE_NORMAL = 1 -DIRECT_RATE_FAST = 2 -DIRECT_RATE_VERY_FAST = 3 - -DIRECT_CHANNEL_TYPE_HARDWARE = 1 -DIRECT_CHANNEL_TYPE_SOFTWARE = 2 -DIRECT_CHANNEL_SENSOR_NONE = 0 - -SENSOR_STATUS_NO_CONTACT = -1 -SENSOR_STATUS_UNRELIABLE = 0 -SENSOR_STATUS_ACCURACY_LOW = 1 -SENSOR_STATUS_ACCURACY_MEDIUM = 2 -SENSOR_STATUS_ACCURACY_HIGH = 3 - -AXIS_X = 1 -AXIS_Y = 2 -AXIS_Z = 3 -AXIS_MINUS_X = 4 -AXIS_MINUS_Y = 5 -AXIS_MINUS_Z = 6 - - -# ═══════════════════════════════════════════════════════════════ -# Sensor (扩展) -# ═══════════════════════════════════════════════════════════════ - -class Sensor(_BaseSensor): - """Sensor — 传感器 (完整版,继承 hardware.Sensor 的所有方法)""" - - TYPE_ACCELEROMETER = TYPE_ACCELEROMETER - TYPE_MAGNETIC_FIELD = TYPE_MAGNETIC_FIELD - TYPE_ORIENTATION = TYPE_ORIENTATION - TYPE_GYROSCOPE = TYPE_GYROSCOPE - TYPE_LIGHT = TYPE_LIGHT - TYPE_PRESSURE = TYPE_PRESSURE - TYPE_PROXIMITY = TYPE_PROXIMITY - TYPE_GRAVITY = TYPE_GRAVITY - TYPE_LINEAR_ACCELERATION = TYPE_LINEAR_ACCELERATION - TYPE_ROTATION_VECTOR = TYPE_ROTATION_VECTOR - TYPE_RELATIVE_HUMIDITY = TYPE_RELATIVE_HUMIDITY - TYPE_AMBIENT_TEMPERATURE = TYPE_AMBIENT_TEMPERATURE - TYPE_STEP_COUNTER = TYPE_STEP_COUNTER - TYPE_STEP_DETECTOR = TYPE_STEP_DETECTOR - TYPE_HEART_RATE = TYPE_HEART_RATE - TYPE_SIGNIFICANT_MOTION = TYPE_SIGNIFICANT_MOTION - TYPE_GAME_ROTATION_VECTOR = TYPE_GAME_ROTATION_VECTOR - TYPE_GEOMAGNETIC_ROTATION_VECTOR = TYPE_GEOMAGNETIC_ROTATION_VECTOR - TYPE_DEVICE_ORIENTATION = TYPE_DEVICE_ORIENTATION - TYPE_STATIONARY_DETECT = TYPE_STATIONARY_DETECT - TYPE_MOTION_DETECT = TYPE_MOTION_DETECT - TYPE_HEART_BEAT = TYPE_HEART_BEAT - TYPE_LOW_LATENCY_OFFBODY_DETECT = TYPE_LOW_LATENCY_OFFBODY_DETECT - TYPE_HINGE_ANGLE = TYPE_HINGE_ANGLE - TYPE_HEAD_TRACKER = TYPE_HEAD_TRACKER - - REPORTING_MODE_CONTINUOUS = REPORTING_MODE_CONTINUOUS - REPORTING_MODE_ON_CHANGE = REPORTING_MODE_ON_CHANGE - REPORTING_MODE_ONE_SHOT = REPORTING_MODE_ONE_SHOT - REPORTING_MODE_SPECIAL_TRIGGER = REPORTING_MODE_SPECIAL_TRIGGER - - SENSOR_STATUS_NO_CONTACT = SENSOR_STATUS_NO_CONTACT - SENSOR_STATUS_UNRELIABLE = SENSOR_STATUS_UNRELIABLE - SENSOR_STATUS_ACCURACY_LOW = SENSOR_STATUS_ACCURACY_LOW - SENSOR_STATUS_ACCURACY_MEDIUM = SENSOR_STATUS_ACCURACY_MEDIUM - SENSOR_STATUS_ACCURACY_HIGH = SENSOR_STATUS_ACCURACY_HIGH - - def __init__(self, type_: int = TYPE_ACCELEROMETER, - name: str = "Simulated Sensor", - vendor: str = "Android-pyApi", version: int = 1, - resolution: float = 1.0, max_range: float = 100.0, - power: float = 0.5, min_delay: int = 10000, - max_delay: int = 1000000, - reporting_mode: int = REPORTING_MODE_CONTINUOUS, - is_wake_up: bool = False, - is_dynamic: bool = False, - fifo_reserved_event_count: int = 0, - fifo_max_event_count: int = 0, - id_: int = 0): - super().__init__(type_, name, vendor, version, resolution, - max_range, power, min_delay) - self._type = type_ - self._name = name - self._vendor = vendor - self._version = version - self._resolution = resolution - self._max_range = max_range - self._power = power - self._min_delay = min_delay - self._max_delay = max_delay - self._reporting_mode = reporting_mode - self._is_wake_up = is_wake_up - self._is_dynamic = is_dynamic - self._fifo_reserved_event_count = fifo_reserved_event_count - self._fifo_max_event_count = fifo_max_event_count - self._id = id_ - self._string_type = "" - - def getType(self) -> int: - return self._type - - def getName(self) -> str: - return self._name - - def getVendor(self) -> str: - return self._vendor - - def getVersion(self) -> int: - return self._version - - def getMaximumRange(self) -> float: - return self._max_range - - def getResolution(self) -> float: - return self._resolution - - def getPower(self) -> float: - return self._power - - def getMinDelay(self) -> int: - return self._min_delay - - def getMaxDelay(self) -> int: - return self._max_delay - - def getReportingMode(self) -> int: - return self._reporting_mode - - def isWakeUpSensor(self) -> bool: - return self._is_wake_up - - def isDynamicSensor(self) -> bool: - return self._is_dynamic - - def getFifoReservedEventCount(self) -> int: - return self._fifo_reserved_event_count - - def getFifoMaxEventCount(self) -> int: - return self._fifo_max_event_count - - def getId(self) -> int: - return self._id - - def getStringType(self) -> str: - if self._string_type: - return self._string_type - type_names = { - 1: "android.sensor.accelerometer", 2: "android.sensor.magnetic_field", - 3: "android.sensor.orientation", 4: "android.sensor.gyroscope", - 5: "android.sensor.light", 6: "android.sensor.pressure", - 8: "android.sensor.proximity", 9: "android.sensor.gravity", - 10: "android.sensor.linear_acceleration", 11: "android.sensor.rotation_vector", - 12: "android.sensor.relative_humidity", 13: "android.sensor.ambient_temperature", - 19: "android.sensor.step_counter", 18: "android.sensor.step_detector", - 21: "android.sensor.heart_rate", - } - return type_names.get(self._type, f"android.sensor.type_{self._type}") - - def toString(self) -> str: - return (f"Sensor(name={self._name}, type={self._type}, " - f"vendor={self._vendor})") - - def equals(self, other: Any) -> bool: - if not isinstance(other, Sensor): - return False - return self._type == other._type and self._name == other._name - - def hashCode(self) -> int: - return hash((self._type, self._name)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# SensorEvent -# ═══════════════════════════════════════════════════════════════ - -class SensorEvent(_BaseSensorEvent): - """SensorEvent — 传感器事件 (继承 hardware.SensorEvent 的所有方法)""" - - def __init__(self, sensor: Optional[Sensor] = None, - values: List[float] = None, - accuracy: int = SENSOR_STATUS_ACCURACY_HIGH, - timestamp: int = 0): - if sensor is not None: - super().__init__(sensor, accuracy) - else: - Stub.__init__(self) - self.sensor = sensor - self.values = values or [0.0, 0.0, 0.0] - self.accuracy = accuracy - self.timestamp = timestamp or int(time.time() * 1000000) - - def getSensor(self) -> Optional[Sensor]: - return self.sensor - - def getValues(self) -> List[float]: - return list(self.values) - - def getAccuracy(self) -> int: - return self.accuracy - - def getTimestamp(self) -> int: - return self.timestamp - - def toString(self) -> str: - return (f"SensorEvent(sensor={self.sensor}, " - f"values={self.values}, accuracy={self.accuracy})") - - def equals(self, other: Any) -> bool: - if not isinstance(other, SensorEvent): - return False - return self.timestamp == other.timestamp - - def hashCode(self) -> int: - return self.timestamp & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# SensorEventListener -# ═══════════════════════════════════════════════════════════════ - -class SensorEventListener(_BaseSensorEventListener): - """SensorEventListener — 传感器事件监听器 (继承 hardware.SensorEventListener 的所有方法)""" - - def onSensorChanged(self, event: SensorEvent): - pass - - def onAccuracyChanged(self, sensor: Sensor, accuracy: int): - pass - - def toString(self) -> str: - return f"SensorEventListener@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# SensorManager (扩展) -# ═══════════════════════════════════════════════════════════════ - -class SensorManager(_BaseSensorManager): - """SensorManager — 传感器管理器 (完整版,继承 hardware.SensorManager)""" - - AXIS_X = AXIS_X - AXIS_Y = AXIS_Y - AXIS_Z = AXIS_Z - AXIS_MINUS_X = AXIS_MINUS_X - AXIS_MINUS_Y = AXIS_MINUS_Y - AXIS_MINUS_Z = AXIS_MINUS_Z - - SENSOR_DELAY_FASTEST = 0 - SENSOR_DELAY_GAME = 1 - SENSOR_DELAY_UI = 2 - SENSOR_DELAY_NORMAL = 3 - - DIRECT_RATE_NONE = DIRECT_RATE_NONE - DIRECT_RATE_NORMAL = DIRECT_RATE_NORMAL - DIRECT_RATE_FAST = DIRECT_RATE_FAST - DIRECT_RATE_VERY_FAST = DIRECT_RATE_VERY_FAST - - def __init__(self): - super().__init__() - self._sensors: Dict[int, Sensor] = {} - self._listeners: Dict[int, Tuple[SensorEventListener, Sensor, int]] = {} - self._dynamic_sensor_callbacks: Dict[int, SensorDynamicSensorCallback] = {} - self._privacy_manager = SensorPrivacyManager() - self._direct_channels: Dict[int, SensorDirectChannel] = {} - self._platform_sensors() - - def _platform_sensors(self): - types = [ - (Sensor.TYPE_ACCELEROMETER, "Accelerometer", 0.01, 39.24, 3.0, 10000), - (Sensor.TYPE_GYROSCOPE, "Gyroscope", 0.001, 34.91, 6.1, 10000), - (Sensor.TYPE_MAGNETIC_FIELD, "Magnetic Field", 0.01, 2000.0, 4.0, 10000), - (Sensor.TYPE_LIGHT, "Light", 1.0, 40000.0, 0.5, 10000), - (Sensor.TYPE_PROXIMITY, "Proximity", 1.0, 10.0, 0.5, 10000), - (Sensor.TYPE_PRESSURE, "Pressure", 0.01, 1100.0, 1.0, 10000), - (Sensor.TYPE_GRAVITY, "Gravity", 0.01, 39.24, 3.0, 10000), - (Sensor.TYPE_LINEAR_ACCELERATION, "Linear Acceleration", 0.01, 39.24, 3.0, 10000), - (Sensor.TYPE_ROTATION_VECTOR, "Rotation Vector", 0.0001, 1.0, 5.0, 10000), - (Sensor.TYPE_RELATIVE_HUMIDITY, "Relative Humidity", 0.1, 100.0, 0.5, 10000), - (Sensor.TYPE_AMBIENT_TEMPERATURE, "Ambient Temperature", 0.01, 80.0, 0.5, 10000), - (Sensor.TYPE_STEP_COUNTER, "Step Counter", 1.0, 100000.0, 0.1, 100000), - (Sensor.TYPE_STEP_DETECTOR, "Step Detector", 1.0, 1.0, 0.1, 100000), - (Sensor.TYPE_HEART_RATE, "Heart Rate", 1.0, 220.0, 0.5, 100000), - (Sensor.TYPE_SIGNIFICANT_MOTION, "Significant Motion", 1.0, 1.0, 0.1, 100000), - (Sensor.TYPE_DEVICE_ORIENTATION, "Device Orientation", 1.0, 360.0, 0.5, 10000), - (Sensor.TYPE_STATIONARY_DETECT, "Stationary Detect", 1.0, 1.0, 0.1, 100000), - (Sensor.TYPE_MOTION_DETECT, "Motion Detect", 1.0, 1.0, 0.1, 100000), - (Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT, "Off-Body Detect", 1.0, 1.0, 0.1, 100000), - (Sensor.TYPE_HINGE_ANGLE, "Hinge Angle", 0.1, 360.0, 0.1, 10000), - (Sensor.TYPE_HEAD_TRACKER, "Head Tracker", 0.01, 360.0, 1.0, 5000), - ] - for i, (t, n, r, mr, p, md) in enumerate(types): - self._sensors[t] = Sensor( - type_=t, name=n, resolution=r, max_range=mr, - power=p, min_delay=md, id_=i, - ) - - def getDefaultSensor(self, type_: int) -> Optional[Sensor]: - return self._sensors.get(type_) - - def getSensorList(self, type_: int) -> List[Sensor]: - if type_ == -1: - return list(self._sensors.values()) - return [s for s in self._sensors.values() if s.getType() == type_] - - def registerListener(self, listener: SensorEventListener, sensor: Sensor, - samplingPeriodUs: int = SENSOR_DELAY_NORMAL, - maxReportLatencyUs: int = 0) -> bool: - lid = id(listener) - self._listeners[lid] = (listener, sensor, samplingPeriodUs) - return True - - def unregisterListener(self, listener: SensorEventListener, sensor: Sensor = None): - lid = id(listener) - if sensor and lid in self._listeners: - _, reg_sensor, _ = self._listeners[lid] - if reg_sensor == sensor: - del self._listeners[lid] - else: - self._listeners.pop(lid, None) - - def registerDynamicSensorCallback(self, callback: SensorDynamicSensorCallback, - handler: Any = None): - lid = id(callback) - self._dynamic_sensor_callbacks[lid] = callback - - def unregisterDynamicSensorCallback(self, callback: SensorDynamicSensorCallback): - lid = id(callback) - self._dynamic_sensor_callbacks.pop(lid, None) - - def createDirectChannel(self, config: "SensorDirectChannelConfig") -> Optional["SensorDirectChannel"]: - channel = SensorDirectChannel(config=config, manager=self) - cid = id(channel) - self._direct_channels[cid] = channel - return channel - - def destroyDirectChannel(self, channel: "SensorDirectChannel"): - cid = id(channel) - self._direct_channels.pop(cid, None) - - def getSensorPrivacyManager(self) -> "SensorPrivacyManager": - return self._privacy_manager - - def flush(self, listener: SensorEventListener) -> bool: - return True - - def getDynamicSensorList(self, type_: int) -> List[Sensor]: - return [s for s in self._sensors.values() - if s.isDynamicSensor() and (type_ == -1 or s.getType() == type_)] - - def requestSensor(self, request: "SensorRequest") -> bool: - return True - - def getSensorAvailability(self, type_: int) -> "SensorAvailability": - return SensorAvailability(available=type_ in self._sensors) - - def createOffloadSession(self) -> "SensorOffloadSession": - return SensorOffloadSession() - - def getSensorOffload(self) -> "SensorOffload": - return SensorOffload() - - def toString(self) -> str: - return f"SensorManager(sensors={len(self._sensors)})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# SensorDirectChannelConfig -# ═══════════════════════════════════════════════════════════════ - -class SensorDirectChannelConfig(Stub): - """SensorDirectChannelConfig — 传感器直连通道配置""" - - def __init__(self, type_: int = DIRECT_CHANNEL_TYPE_SOFTWARE, - size: int = 4096, rate: int = DIRECT_RATE_NORMAL, - shared_mem_fd: int = -1): - super().__init__() - self._type = type_ - self._size = size - self._rate = rate - self._shared_mem_fd = shared_mem_fd - - def getType(self) -> int: - return self._type - - def getSize(self) -> int: - return self._size - - def getRate(self) -> int: - return self._rate - - def getSharedMemFd(self) -> int: - return self._shared_mem_fd - - def toString(self) -> str: - return f"SensorDirectChannelConfig(type={self._type}, size={self._size})" - - -# ═══════════════════════════════════════════════════════════════ -# SensorDirectChannel -# ═══════════════════════════════════════════════════════════════ - -class SensorDirectChannel(Stub): - """SensorDirectChannel — 传感器直连通道""" - - TYPE_HARDWARE = DIRECT_CHANNEL_TYPE_HARDWARE - TYPE_SOFTWARE = DIRECT_CHANNEL_TYPE_SOFTWARE - - RATE_NONE = DIRECT_RATE_NONE - RATE_NORMAL = DIRECT_RATE_NORMAL - RATE_FAST = DIRECT_RATE_FAST - RATE_VERY_FAST = DIRECT_RATE_VERY_FAST - - def __init__(self, config: Optional[SensorDirectChannelConfig] = None, - manager: Optional[SensorManager] = None): - super().__init__() - self._config = config - self._manager = manager - self._configured_sensors: List[Sensor] = [] - self._is_valid = True - - def isOpen(self) -> bool: - return self._is_valid - - def close(self): - self._is_valid = False - if self._manager: - self._manager.destroyDirectChannel(self) - - def configure(self, sensor: Sensor, rate: int) -> int: - if not self._is_valid: - return -1 - self._configured_sensors.append(sensor) - return 0 - - def getConfiguredSensors(self) -> List[Sensor]: - return list(self._configured_sensors) - - def toString(self) -> str: - return f"SensorDirectChannel(open={self._is_valid})" - - -# ═══════════════════════════════════════════════════════════════ -# SensorPrivacyManager -# ═══════════════════════════════════════════════════════════════ - -class SensorPrivacyManager(Stub): - """SensorPrivacyManager — 传感器隐私管理器""" - - SOURCE_MICROPHONE = 1 - SOURCE_CAMERA = 2 - SOURCE_LOCATION = 3 - SOURCE_ALL = -1 - - STATE_DISABLED = 0 - STATE_ENABLED = 1 - - def __init__(self): - super().__init__() - self._mic_muted = False - self._camera_disabled = False - self._listeners: Dict[int, Callable] = {} - - def isSensorPrivacyEnabled(self, source: int) -> bool: - if source == SensorPrivacyManager.SOURCE_MICROPHONE: - return self._mic_muted - if source == SensorPrivacyManager.SOURCE_CAMERA: - return self._camera_disabled - return False - - def setSensorPrivacy(self, source: int, enabled: bool): - if source == SensorPrivacyManager.SOURCE_MICROPHONE: - self._mic_muted = enabled - elif source == SensorPrivacyManager.SOURCE_CAMERA: - self._camera_disabled = enabled - elif source == SensorPrivacyManager.SOURCE_ALL: - self._mic_muted = enabled - self._camera_disabled = enabled - - def addSensorPrivacyListener(self, source: int, callback: Callable): - lid = id(callback) - self._listeners[lid] = callback - - def removeSensorPrivacyListener(self, source: int, callback: Callable): - lid = id(callback) - self._listeners.pop(lid, None) - - def toString(self) -> str: - return f"SensorPrivacyManager(micMuted={self._mic_muted})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# SensorAvailability -# ═══════════════════════════════════════════════════════════════ - -class SensorAvailability(Stub): - """SensorAvailability — 传感器可用性""" - - def __init__(self, available: bool = True, - type_: int = TYPE_ACCELEROMETER, - reason: str = ""): - super().__init__() - self._available = available - self._type = type_ - self._reason = reason - - def isAvailable(self) -> bool: - return self._available - - def getType(self) -> int: - return self._type - - def getReason(self) -> str: - return self._reason - - def toString(self) -> str: - return f"SensorAvailability(type={self._type}, available={self._available})" - - -# ═══════════════════════════════════════════════════════════════ -# SensorAdditionalInfo -# ═══════════════════════════════════════════════════════════════ - -class SensorAdditionalInfo(Stub): - """SensorAdditionalInfo — 传感器附加信息""" - - TYPE_FRAME_BEGIN = 0 - TYPE_FRAME_END = 1 - TYPE_UNTRACKED_DELAY = 65536 - TYPE_INTERNAL_TEMPERATURE = 65537 - TYPE_VEC3_CALIBRATION = 65538 - TYPE_SENSOR_PLACEMENT = 65539 - TYPE_SAMPLE_0 = 65540 - TYPE_LOCAL_GEOMAGNETIC_FIELD = 65541 - TYPE_LOCAL_GRAVITY = 65542 - TYPE_DOCK_STATE = 65543 - TYPE_HIGH_LEVEL_EVENT = 65544 - TYPE_MAGNETIC_FIELD_CALIBRATION = 65545 - - def __init__(self, sensor: Optional[Sensor] = None, - type_: int = TYPE_FRAME_BEGIN, - serialized: List[int] = None, - values: List[float] = None): - super().__init__() - self.sensor = sensor - self.type = type_ - self.serialized = serialized or [] - self.values = values or [] - - def getSensor(self) -> Optional[Sensor]: - return self.sensor - - def getType(self) -> int: - return self.type - - def getSerialized(self) -> List[int]: - return list(self.serialized) - - def getValues(self) -> List[float]: - return list(self.values) - - def toString(self) -> str: - return f"SensorAdditionalInfo(type={self.type})" - - -# ═══════════════════════════════════════════════════════════════ -# SensorDynamicSensorCallback -# ═══════════════════════════════════════════════════════════════ - -class SensorDynamicSensorCallback(Stub): - """SensorDynamicSensorCallback — 动态传感器回调""" - - def onDynamicSensorConnected(self, sensor: Sensor): - pass - - def onDynamicSensorDisconnected(self, sensor: Sensor): - pass - - def toString(self) -> str: - return f"SensorDynamicSensorCallback@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# SensorOffload / SensorOffloadSession -# ═══════════════════════════════════════════════════════════════ - -class SensorOffload(Stub): - """SensorOffload — 传感器卸载""" - - def __init__(self): - super().__init__() - - def createSession(self) -> "SensorOffloadSession": - return SensorOffloadSession() - - def getMaxSensorsPerSession(self) -> int: - return 16 - - def isSupported(self) -> bool: - return True - - def toString(self) -> str: - return "SensorOffload()" - - -class SensorOffloadSession(Stub): - """SensorOffloadSession — 传感器卸载会话""" - - STATE_IDLE = 0 - STATE_ACTIVE = 1 - STATE_SUSPENDED = 2 - STATE_ERROR = 3 - - def __init__(self): - super().__init__() - self._state = SensorOffloadSession.STATE_IDLE - self._sensors: List[Sensor] = [] - self._sampling_period_us = 0 - self._max_report_latency_us = 0 - - def getState(self) -> int: - return self._state - - def start(self, sensors: List[Sensor], samplingPeriodUs: int, - maxReportLatencyUs: int = 0): - self._sensors = list(sensors) - self._sampling_period_us = samplingPeriodUs - self._max_report_latency_us = maxReportLatencyUs - self._state = SensorOffloadSession.STATE_ACTIVE - - def stop(self): - self._state = SensorOffloadSession.STATE_IDLE - self._sensors.clear() - - def suspend(self): - self._state = SensorOffloadSession.STATE_SUSPENDED - - def resume(self): - if self._state == SensorOffloadSession.STATE_SUSPENDED: - self._state = SensorOffloadSession.STATE_ACTIVE - - def getSensors(self) -> List[Sensor]: - return list(self._sensors) - - def getSamplingPeriodUs(self) -> int: - return self._sampling_period_us - - def getMaxReportLatencyUs(self) -> int: - return self._max_report_latency_us - - def toString(self) -> str: - return f"SensorOffloadSession(state={self._state}, sensors={len(self._sensors)})" - - -# ═══════════════════════════════════════════════════════════════ -# SensorRequest / SensorRequest.Builder -# ═══════════════════════════════════════════════════════════════ - -class SensorRequest(Stub): - """SensorRequest — 传感器请求""" - - def __init__(self, sensor: Optional[Sensor] = None, - samplingPeriodUs: int = 100000, - maxReportLatencyUs: int = 0, - isWakeUp: bool = False): - super().__init__() - self._sensor = sensor - self._sampling_period_us = samplingPeriodUs - self._max_report_latency_us = maxReportLatencyUs - self._is_wake_up = isWakeUp - - def getSensor(self) -> Optional[Sensor]: - return self._sensor - - def getSamplingPeriodUs(self) -> int: - return self._sampling_period_us - - def getMaxReportLatencyUs(self) -> int: - return self._max_report_latency_us - - def isWakeUp(self) -> bool: - return self._is_wake_up - - class Builder(Stub): - """SensorRequest.Builder — 传感器请求构建器""" - - def __init__(self): - super().__init__() - self._sensor: Optional[Sensor] = None - self._sampling_period_us = 100000 - self._max_report_latency_us = 0 - self._is_wake_up = False - - def setSensor(self, sensor: Sensor) -> "SensorRequest.Builder": - self._sensor = sensor - return self - - def setSamplingPeriodUs(self, periodUs: int) -> "SensorRequest.Builder": - self._sampling_period_us = periodUs - return self - - def setMaxReportLatencyUs(self, latencyUs: int) -> "SensorRequest.Builder": - self._max_report_latency_us = latencyUs - return self - - def setWakeUp(self, wakeUp: bool) -> "SensorRequest.Builder": - self._is_wake_up = wakeUp - return self - - def build(self) -> "SensorRequest": - return SensorRequest( - sensor=self._sensor, samplingPeriodUs=self._sampling_period_us, - maxReportLatencyUs=self._max_report_latency_us, - isWakeUp=self._is_wake_up, - ) - - def toString(self) -> str: - return (f"SensorRequest(sensor={self._sensor}, " - f"period={self._sampling_period_us}us)") - - def equals(self, other: Any) -> bool: - if not isinstance(other, SensorRequest): - return False - return (self._sensor == other._sensor and - self._sampling_period_us == other._sampling_period_us) - - def hashCode(self) -> int: - return hash((id(self._sensor), self._sampling_period_us)) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/hardware_serial.py b/android_pyapi/android/hardware_serial.py deleted file mode 100644 index f4ac418..0000000 --- a/android_pyapi/android/hardware_serial.py +++ /dev/null @@ -1,513 +0,0 @@ -""" -Android hardware Serial 组件 — SerialManager, SerialPort, SerialUsb -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import io -import os -import time -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError - -__all__ = [ - "SerialManager", - "SerialPort", - "SerialUsb", -] - - -# ═══════════════════════════════════════════════════════════════ -# 串口常量 -# ═══════════════════════════════════════════════════════════════ - -SERIAL_PORT_MODE_READ = 0 -SERIAL_PORT_MODE_WRITE = 1 -SERIAL_PORT_MODE_READ_WRITE = 2 - -SERIAL_PARITY_NONE = 0 -SERIAL_PARITY_ODD = 1 -SERIAL_PARITY_EVEN = 2 -SERIAL_PARITY_MARK = 3 -SERIAL_PARITY_SPACE = 4 - -SERIAL_STOP_BITS_1 = 1 -SERIAL_STOP_BITS_1_5 = 3 -SERIAL_STOP_BITS_2 = 2 - -SERIAL_DATA_BITS_5 = 5 -SERIAL_DATA_BITS_6 = 6 -SERIAL_DATA_BITS_7 = 7 -SERIAL_DATA_BITS_8 = 8 - -SERIAL_FLOW_CONTROL_NONE = 0 -SERIAL_FLOW_CONTROL_RTS_CTS = 1 -SERIAL_FLOW_CONTROL_XON_XOFF = 2 -SERIAL_FLOW_CONTROL_DTR_DSR = 4 - -SERIAL_BAUD_300 = 300 -SERIAL_BAUD_1200 = 1200 -SERIAL_BAUD_2400 = 2400 -SERIAL_BAUD_4800 = 4800 -SERIAL_BAUD_9600 = 9600 -SERIAL_BAUD_19200 = 19200 -SERIAL_BAUD_38400 = 38400 -SERIAL_BAUD_57600 = 57600 -SERIAL_BAUD_115200 = 115200 -SERIAL_BAUD_230400 = 230400 -SERIAL_BAUD_460800 = 460800 -SERIAL_BAUD_921600 = 921600 - -SERIAL_ERROR_NONE = 0 -SERIAL_ERROR_PORT_NOT_FOUND = -1 -SERIAL_ERROR_PORT_IN_USE = -2 -SERIAL_ERROR_PERMISSION_DENIED = -3 -SERIAL_ERROR_IO_ERROR = -4 -SERIAL_ERROR_UNSUPPORTED = -5 -SERIAL_ERROR_TIMEOUT = -6 -SERIAL_ERROR_DISCONNECTED = -7 - - -# ═══════════════════════════════════════════════════════════════ -# SerialPort -# ═══════════════════════════════════════════════════════════════ - -class SerialPort(Stub): - """SerialPort — 串口""" - - def __init__(self, name: str = "", baudRate: int = SERIAL_BAUD_9600, - dataBits: int = SERIAL_DATA_BITS_8, - parity: int = SERIAL_PARITY_NONE, - stopBits: int = SERIAL_STOP_BITS_1, - flowControl: int = SERIAL_FLOW_CONTROL_NONE, - mode: int = SERIAL_PORT_MODE_READ_WRITE): - super().__init__() - self._name = name - self._baud_rate = baudRate - self._data_bits = dataBits - self._parity = parity - self._stop_bits = stopBits - self._flow_control = flowControl - self._mode = mode - self._is_open = False - self._fd: Optional[Any] = None - self._timeout_ms = 1000 - self._buffer_size = 4096 - self._read_buffer = b"" - self._write_buffer = b"" - - def getName(self) -> str: - return self._name - - def getBaudRate(self) -> int: - return self._baud_rate - - def setBaudRate(self, baudRate: int): - self._baud_rate = baudRate - - def getDataBits(self) -> int: - return self._data_bits - - def setDataBits(self, dataBits: int): - self._data_bits = dataBits - - def getParity(self) -> int: - return self._parity - - def setParity(self, parity: int): - self._parity = parity - - def getStopBits(self) -> int: - return self._stop_bits - - def setStopBits(self, stopBits: int): - self._stop_bits = stopBits - - def getFlowControl(self) -> int: - return self._flow_control - - def setFlowControl(self, flowControl: int): - self._flow_control = flowControl - - def getMode(self) -> int: - return self._mode - - def setTimeout(self, timeoutMs: int): - self._timeout_ms = timeoutMs - - def getTimeout(self) -> int: - return self._timeout_ms - - def isOpen(self) -> bool: - return self._is_open - - def open(self) -> int: - if self._is_open: - return SERIAL_ERROR_NONE - try: - self._fd = io.FileIO(self._name, "rb+" if self._mode == SERIAL_PORT_MODE_READ_WRITE - else "rb" if self._mode == SERIAL_PORT_MODE_READ - else "wb") - self._is_open = True - return SERIAL_ERROR_NONE - except (IOError, OSError) as e: - if "Permission denied" in str(e): - return SERIAL_ERROR_PERMISSION_DENIED - if "No such file" in str(e): - return SERIAL_ERROR_PORT_NOT_FOUND - return SERIAL_ERROR_IO_ERROR - - def close(self): - if self._fd: - try: - self._fd.close() - except Exception: - pass - self._fd = None - self._is_open = False - - def read(self, size: int = -1) -> Optional[bytes]: - if not self._is_open or not self._fd: - return None - try: - if self._mode == SERIAL_PORT_MODE_WRITE: - return b"" - if size < 0: - data = self._fd.read() - else: - data = self._fd.read(size) - return data if data else b"" - except Exception: - return None - - def write(self, data: bytes) -> int: - if not self._is_open or not self._fd: - return SERIAL_ERROR_PORT_NOT_FOUND - try: - if self._mode == SERIAL_PORT_MODE_READ: - return SERIAL_ERROR_PERMISSION_DENIED - written = self._fd.write(data) - return written if written else len(data) - except Exception: - return SERIAL_ERROR_IO_ERROR - - def flush(self) -> int: - if not self._is_open: - return SERIAL_ERROR_PORT_NOT_FOUND - try: - if hasattr(self._fd, 'flush'): - self._fd.flush() - return SERIAL_ERROR_NONE - except Exception: - return SERIAL_ERROR_IO_ERROR - - def getAvailableBytes(self) -> int: - if not self._is_open or not self._fd: - return 0 - try: - import os - if hasattr(self._fd, 'fileno'): - fd = self._fd.fileno() - import select - r, _, _ = select.select([fd], [], [], 0) - if r: - import struct - import fcntl - try: - buf = fcntl.ioctl(fd, 0x541B, struct.pack('I', 0)) # FIONREAD - return struct.unpack('I', buf)[0] - except Exception: - pass - except Exception: - pass - return 0 - - def setDTR(self, value: bool): - pass - - def setRTS(self, value: bool): - pass - - def getDCD(self) -> bool: - return False - - def getDSR(self) -> bool: - return False - - def getCTS(self) -> bool: - return False - - def getRI(self) -> bool: - return False - - def sendBreak(self, durationMs: int = 100): - pass - - def toString(self) -> str: - return (f"SerialPort(name={self._name}, baud={self._baud_rate}, " - f"open={self._is_open})") - - def equals(self, other: Any) -> bool: - if not isinstance(other, SerialPort): - return False - return self._name == other._name - - def hashCode(self) -> int: - return hash(self._name) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# SerialUsb -# ═══════════════════════════════════════════════════════════════ - -class SerialUsb(Stub): - """SerialUsb — USB 串口""" - - USB_VENDOR_FTDI = 0x0403 - USB_VENDOR_CP210X = 0x10C4 - USB_VENDOR_CH340 = 0x1A86 - USB_VENDOR_PL2303 = 0x067B - USB_VENDOR_ARDUINO = 0x2341 - USB_VENDOR_SILABS = 0x10C4 - - USB_PRODUCT_FTDI_FT232 = 0x6001 - USB_PRODUCT_CP2102 = 0xEA60 - USB_PRODUCT_CH340 = 0x7523 - USB_PRODUCT_PL2303 = 0x2303 - - def __init__(self, vendorId: int = 0, productId: int = 0, - serialNumber: str = "", manufacturer: str = "", - product: str = "", devicePort: str = "", - deviceNode: str = ""): - super().__init__() - self._vendor_id = vendorId - self._product_id = productId - self._serial_number = serialNumber - self._manufacturer = manufacturer - self._product = product - self._device_port = devicePort - self._device_node = deviceNode - self._port: Optional[SerialPort] = None - - def getVendorId(self) -> int: - return self._vendor_id - - def getProductId(self) -> int: - return self._product_id - - def getSerialNumber(self) -> str: - return self._serial_number - - def getManufacturer(self) -> str: - return self._manufacturer - - def getProduct(self) -> str: - return self._product - - def getDevicePort(self) -> str: - return self._device_port - - def getDeviceNode(self) -> str: - return self._device_node - - def createSerialPort(self, baudRate: int = SERIAL_BAUD_9600, - dataBits: int = SERIAL_DATA_BITS_8, - parity: int = SERIAL_PARITY_NONE, - stopBits: int = SERIAL_STOP_BITS_1, - flowControl: int = SERIAL_FLOW_CONTROL_NONE) -> Optional[SerialPort]: - if not self._device_node and not self._device_port: - return None - port_name = self._device_node or f"/dev/{self._device_port}" - port = SerialPort(name=port_name, baudRate=baud_rate, - dataBits=dataBits, parity=parity, - stopBits=stopBits, flowControl=flowControl) - self._port = port - return port - - def getSerialPort(self) -> Optional[SerialPort]: - return self._port - - def toString(self) -> str: - return (f"SerialUsb(vendor=0x{self._vendor_id:04X}, " - f"product=0x{self._product_id:04X})") - - def equals(self, other: Any) -> bool: - if not isinstance(other, SerialUsb): - return False - return (self._vendor_id == other._vendor_id and - self._product_id == other._product_id and - self._serial_number == other._serial_number) - - def hashCode(self) -> int: - return hash((self._vendor_id, self._product_id, self._serial_number)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# SerialManager -# ═══════════════════════════════════════════════════════════════ - -class SerialManager(Stub): - """SerialManager — 串口管理器""" - - ACTION_SERIAL_PORT_ADDED = "android.hardware.usb.action.USB_DEVICE_ATTACHED" - ACTION_SERIAL_PORT_REMOVED = "android.hardware.usb.action.USB_DEVICE_DETACHED" - EXTRA_SERIAL_PORT = "serial_port" - EXTRA_USB_DEVICE = "device" - - def __init__(self): - super().__init__() - self._ports: Dict[str, SerialPort] = {} - self._usb_devices: Dict[str, SerialUsb] = {} - self._listeners: Dict[int, Callable] = {} - - def getSerialPorts(self) -> List[SerialPort]: - return list(self._ports.values()) - - def getSerialPort(self, name: str) -> Optional[SerialPort]: - return self._ports.get(name) - - def openSerialPort(self, name: str, baudRate: int = SERIAL_BAUD_9600, - dataBits: int = SERIAL_DATA_BITS_8, - parity: int = SERIAL_PARITY_NONE, - stopBits: int = SERIAL_STOP_BITS_1, - flowControl: int = SERIAL_FLOW_CONTROL_NONE, - mode: int = SERIAL_PORT_MODE_READ_WRITE) -> Optional[SerialPort]: - if name in self._ports and self._ports[name].isOpen(): - return None - port = SerialPort(name=name, baudRate=baudRate, dataBits=dataBits, - parity=parity, stopBits=stopBits, - flowControl=flowControl, mode=mode) - err = port.open() - if err != SERIAL_ERROR_NONE: - return None - self._ports[name] = port - return port - - def closeSerialPort(self, name: str): - port = self._ports.get(name) - if port: - port.close() - self._ports.pop(name, None) - - def getUsbDevices(self) -> List[SerialUsb]: - return list(self._usb_devices.values()) - - def addUsbDevice(self, device: SerialUsb): - self._usb_devices[device.getDeviceNode() or device.getDevicePort()] = device - - def removeUsbDevice(self, device: SerialUsb): - key = device.getDeviceNode() or device.getDevicePort() - self._usb_devices.pop(key, None) - - def getAvailableDevices(self) -> List[str]: - """列出系统可用的串口设备路径""" - ports = [] - for base in ("/dev/ttyS", "/dev/ttyUSB", "/dev/ttyACM", - "/dev/ttyAMA", "/dev/ttyO", "/dev/cu.usbserial", - "/dev/tty.wchusbserial"): - for i in range(256): - path = f"{base}{i}" - if os.path.exists(path): - ports.append(path) - return ports + list(self._ports.keys()) - - def getSerialPortsForDevice(self, usbDevice: SerialUsb) -> List[SerialPort]: - return [p for n, p in self._ports.items() - if usbDevice.getDeviceNode() in n or usbDevice.getDevicePort() in n] - - def registerListener(self, listener: Callable): - lid = id(listener) - self._listeners[lid] = listener - - def unregisterListener(self, listener: Callable): - lid = id(listener) - self._listeners.pop(lid, None) - - def isSerialPortName(self, name: str) -> bool: - return name.startswith(("ttyS", "ttyUSB", "ttyACM", "ttyAMA")) - - def toString(self) -> str: - return f"SerialManager(ports={len(self._ports)}, usb={len(self._usb_devices)})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def getSerialPortCount(self) -> int: - return len(self._ports) - - def getUsbDeviceCount(self) -> int: - return len(self._usb_devices) - - def hasSerialPort(self, name: str) -> bool: - return name in self._ports - - def hasUsbDevice(self, vendorId: int, productId: int) -> bool: - for dev in self._usb_devices.values(): - if dev.getVendorId() == vendorId and dev.getProductId() == productId: - return True - return False - - def findUsbDeviceBySerial(self, serialNumber: str) -> Optional[SerialUsb]: - for dev in self._usb_devices.values(): - if dev.getSerialNumber() == serialNumber: - return dev - return None - - def findUsbDeviceByVendorProduct(self, vendorId: int, productId: int) -> List[SerialUsb]: - return [dev for dev in self._usb_devices.values() - if dev.getVendorId() == vendorId and dev.getProductId() == productId] - - def closeAllPorts(self) -> None: - for name, port in list(self._ports.items()): - port.close() - self._ports.pop(name, None) - - def getPortStatus(self, name: str) -> int: - port = self._ports.get(name) - if port is None: - return SERIAL_ERROR_PORT_NOT_FOUND - if not port.isOpen(): - return SERIAL_ERROR_PORT_NOT_FOUND - return SERIAL_ERROR_NONE - - def reconfigurePort(self, name: str, baudRate: int = 0, - dataBits: int = 0, parity: int = -1, - stopBits: int = 0, flowControl: int = -1) -> int: - port = self._ports.get(name) - if port is None: - return SERIAL_ERROR_PORT_NOT_FOUND - if baudRate > 0: - port.setBaudRate(baudRate) - if dataBits > 0: - port.setDataBits(dataBits) - if parity >= 0: - port.setParity(parity) - if stopBits > 0: - port.setStopBits(stopBits) - if flowControl >= 0: - port.setFlowControl(flowControl) - return SERIAL_ERROR_NONE - - def addSerialPort(self, port: SerialPort) -> None: - self._ports[port.getName()] = port - - def removeSerialPort(self, name: str) -> None: - self._ports.pop(name, None) - - def getSerialPortNames(self) -> List[str]: - return list(self._ports.keys()) - - def getUsbDeviceSerialNumbers(self) -> List[str]: - return [dev.getSerialNumber() for dev in self._usb_devices.values()] - - def clearAllDevices(self) -> None: - self._usb_devices.clear() - for port in self._ports.values(): - if port.isOpen(): - port.close() - self._ports.clear() \ No newline at end of file diff --git a/android_pyapi/android/hardware_thermal.py b/android_pyapi/android/hardware_thermal.py deleted file mode 100644 index 0dfe0ac..0000000 --- a/android_pyapi/android/hardware_thermal.py +++ /dev/null @@ -1,218 +0,0 @@ -""" -Android hardware.thermal 组件 — ThermalManager, CoolingDevice, Temperature 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import random -import threading -from typing import Any, Callable, Dict, List, Optional - -from .._core import Stub, AndroidOnlyError, HAS_PSUTIL -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# Temperature -# ═══════════════════════════════════════════════════════════════ - -class Temperature(Stub): - """Temperature — 温度数据""" - - TYPE_SKIN = 0 - TYPE_CPU = 1 - TYPE_GPU = 2 - TYPE_BATTERY = 3 - TYPE_NPU = 4 - TYPE_POWER_AMP = 5 - TYPE_MODEM = 6 - TYPE_AMBIENT = 7 - TYPE_USB_PORT = 8 - TYPE_DISPLAY = 9 - TYPE_BELT = 10 - TYPE_CAMERA = 11 - TYPE_TPU = 12 - TYPE_POGO = 13 - - def __init__(self, type_: int = TYPE_AMBIENT, name: str = "ambient", - value: float = 25.0, status: int = 0): - super().__init__() - self._type = type_ - self._name = name - self._value = value - self._status = status - - def getType(self) -> int: - return self._type - - def getName(self) -> str: - return self._name - - def getValue(self) -> float: - return self._value - - def getStatus(self) -> int: - return self._status - - def toString(self) -> str: - return f"Temperature{{type={self._type}, name={self._name}, value={self._value}, status={self._status}}}" - - -# ═══════════════════════════════════════════════════════════════ -# CoolingDevice -# ═══════════════════════════════════════════════════════════════ - -class CoolingDevice(Stub): - """CoolingDevice — 冷却设备""" - - def __init__(self, type_: int = 0, description: str = "cooling_fan", - current_level: int = 50): - super().__init__() - self._type = type_ - self._description = description - self._current_level = current_level - - def getType(self) -> int: - return self._type - - def getDescription(self) -> str: - return self._description - - def getCurrentLevel(self) -> int: - return self._current_level - - def toString(self) -> str: - return f"CoolingDevice{{type={self._type}, description={self._description}, level={self._current_level}}}" - - -# ═══════════════════════════════════════════════════════════════ -# ThermalStatus -# ═══════════════════════════════════════════════════════════════ - -class ThermalStatus(Stub): - """ThermalStatus — 热状态""" - - def __init__(self): - super().__init__() - self._status: int = ThermalManager.THERMAL_STATUS_NONE - self._temperatures: List[Temperature] = [] - - def getStatus(self) -> int: - return self._status - - def getType(self) -> int: - return 0 - - def getName(self) -> str: - return "thermal_status" - - def getTemperatures(self) -> List[Temperature]: - return list(self._temperatures) - - def toString(self) -> str: - return f"ThermalStatus{{status={self._status}}}" - - -# ═══════════════════════════════════════════════════════════════ -# ThermalManager -# ═══════════════════════════════════════════════════════════════ - -class ThermalManager(Stub): - """ThermalManager — 热管理器""" - - THERMAL_STATUS_NONE = 0 - THERMAL_STATUS_LIGHT = 1 - THERMAL_STATUS_MODERATE = 2 - THERMAL_STATUS_SEVERE = 3 - THERMAL_STATUS_CRITICAL = 4 - THERMAL_STATUS_EMERGENCY = 5 - THERMAL_STATUS_SHUTDOWN = 6 - - def __init__(self): - super().__init__() - self._lock = threading.Lock() - self._listeners: List[Callable[[int], None]] = [] - self._thermal_status = self.THERMAL_STATUS_NONE - self._status_threshold = self.THERMAL_STATUS_CRITICAL - - # Simulated temperatures - self._temperatures: List[Temperature] = [ - Temperature(Temperature.TYPE_CPU, "cpu0", 45.0, self.THERMAL_STATUS_NONE), - Temperature(Temperature.TYPE_GPU, "gpu0", 40.0, self.THERMAL_STATUS_NONE), - Temperature(Temperature.TYPE_BATTERY, "battery", 30.0, self.THERMAL_STATUS_NONE), - Temperature(Temperature.TYPE_AMBIENT, "ambient", 25.0, self.THERMAL_STATUS_NONE), - Temperature(Temperature.TYPE_SKIN, "skin", 32.0, self.THERMAL_STATUS_NONE), - Temperature(Temperature.TYPE_NPU, "npu0", 38.0, self.THERMAL_STATUS_NONE), - Temperature(Temperature.TYPE_MODEM, "modem", 35.0, self.THERMAL_STATUS_NONE), - Temperature(Temperature.TYPE_DISPLAY, "display", 36.0, self.THERMAL_STATUS_NONE), - ] - - # Try to get actual CPU temps via psutil - if HAS_PSUTIL: - import psutil - try: - temps = psutil.sensors_temperatures() - if temps: - for name, entries in temps.items(): - for entry in entries: - temp_type = getattr(Temperature, f"TYPE_{name.upper()}", Temperature.TYPE_AMBIENT) - self._temperatures.append( - Temperature(temp_type, entry.label or name, - entry.current, self.THERMAL_STATUS_NONE) - ) - except Exception: - pass - - # ── Thermal headroom ── - - def getThermalHeadroom(self, forecastSeconds: int) -> float: - """获取热余量百分比 (0.0 = 过温, 1.0 = 安全)""" - max_temp = max(t.getValue() for t in self._temperatures) if self._temperatures else 25.0 - headroom = max(0.0, 1.0 - (max_temp - 25.0) / 75.0) - return min(1.0, headroom) - - # ── Status ── - - def getThermalStatus(self) -> int: - return self._thermal_status - - def getThermalStatusThreshold(self) -> int: - return self._status_threshold - - # ── Listeners ── - - def addThermalStatusListener(self, listener: Callable[[int], None]): - with self._lock: - if listener not in self._listeners: - self._listeners.append(listener) - - def removeThermalStatusListener(self, listener: Callable[[int], None]): - with self._lock: - if listener in self._listeners: - self._listeners.remove(listener) - - def _notify_listeners(self, status: int): - with self._lock: - for listener in self._listeners: - listener(status) - - # ── Helper ── - - def toString(self) -> str: - return f"ThermalManager{{status={self._thermal_status}}}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "ThermalManager", - "CoolingDevice", - "ThermalStatus", - "Temperature", -] \ No newline at end of file diff --git a/android_pyapi/android/hardware_usb.py b/android_pyapi/android/hardware_usb.py deleted file mode 100644 index 3420944..0000000 --- a/android_pyapi/android/hardware_usb.py +++ /dev/null @@ -1,409 +0,0 @@ -from __future__ import annotations - -""" -Android hardware.usb 组件 — UsbManager, UsbDevice, UsbDeviceConnection 等 -方法签名完全对齐 Android SDK -""" - -from typing import Any, Dict, List, Optional, Set - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# UsbConstants -# ═══════════════════════════════════════════════════════════════ - -class UsbConstants(Stub): - """UsbConstants — USB 常量""" - - USB_DIR_IN = 0x80 - USB_DIR_OUT = 0 - - USB_TYPE_MASK = 0x60 - USB_TYPE_CLASS = 0x20 - USB_TYPE_VENDOR = 0x40 - USB_TYPE_STANDARD = 0x00 - - USB_ENDPOINT_XFER_CONTROL = 0 - USB_ENDPOINT_XFER_ISOC = 1 - USB_ENDPOINT_XFER_BULK = 2 - USB_ENDPOINT_XFER_INT = 3 - - USB_CLASS_APP_SPEC = 0xFE - USB_CLASS_AUDIO = 0x01 - USB_CLASS_CDC_DATA = 0x0A - USB_CLASS_COMM = 0x02 - USB_CLASS_CONTENT_SEC = 0x0D - USB_CLASS_CSCID = 0x0B - USB_CLASS_HID = 0x03 - USB_CLASS_HUB = 0x09 - USB_CLASS_MASS_STORAGE = 0x08 - USB_CLASS_MISC = 0xEF - USB_CLASS_PER_INTERFACE = 0x00 - USB_CLASS_PHYSICA = 0x05 - USB_CLASS_PRINTER = 0x07 - USB_CLASS_STILL_IMAGE = 0x06 - USB_CLASS_VENDOR_SPEC = 0xFF - USB_CLASS_VIDEO = 0x0E - USB_CLASS_WIRELESS = 0xE0 - - -# ═══════════════════════════════════════════════════════════════ -# UsbManager -# ═══════════════════════════════════════════════════════════════ - -class UsbManager(Stub): - """UsbManager — USB 管理器""" - - ACTION_USB_DEVICE_ATTACHED = "android.hardware.usb.action.USB_DEVICE_ATTACHED" - ACTION_USB_DEVICE_DETACHED = "android.hardware.usb.action.USB_DEVICE_DETACHED" - ACTION_USB_ACCESSORY_ATTACHED = "android.hardware.usb.action.USB_ACCESSORY_ATTACHED" - ACTION_USB_ACCESSORY_DETACHED = "android.hardware.usb.action.USB_ACCESSORY_DETACHED" - - EXTRA_DEVICE = "device" - EXTRA_ACCESSORY = "accessory" - EXTRA_PERMISSION_GRANTED = "permission" - - def __init__(self): - super().__init__() - self._devices: Dict[str, UsbDevice] = {} - self._permissions: Set[str] = set() - self._accessories: List[UsbAccessory] = [] - self._connections: Dict[str, UsbDeviceConnection] = {} - # Create some mock devices - self._devices["001"] = UsbDevice( - deviceId=1, deviceName="/dev/bus/usb/001/001", - vendorId=0x18D1, productId=0x4EE1, - deviceClass=UsbConstants.USB_CLASS_MISC, - deviceSubclass=0, deviceProtocol=0, - manufacturerName="Google", productName="Pixel Device", - version="3.1", serialNumber="ABC123" - ) - - def getDeviceList(self) -> Dict[str, UsbDevice]: - return dict(self._devices) - - def openDevice(self, device: "UsbDevice") -> Optional["UsbDeviceConnection"]: - if not device: - return None - conn = UsbDeviceConnection(device) - self._connections[str(device.getDeviceId())] = conn - return conn - - def hasPermission(self, device: "UsbDevice") -> bool: - return device.getDeviceName() in self._permissions - - def requestPermission(self, device: "UsbDevice"): - self._permissions.add(device.getDeviceName()) - - def grantPermission(self, device: "UsbDevice"): - self._permissions.add(device.getDeviceName()) - - def revokePermission(self, device: "UsbDevice"): - self._permissions.discard(device.getDeviceName()) - - def setCurrentFunction(self, function: str, usbDataUnlocked: bool = False): - pass - - def setUsbDataUnlocked(self, unlocked: bool): - pass - - def getCurrentAccessory(self) -> Optional["UsbAccessory"]: - return self._accessories[0] if self._accessories else None - - def openAccessory(self, accessory: "UsbAccessory") -> Optional["UsbDeviceConnection"]: - if not accessory: - return None - conn = UsbDeviceConnection(accessory) - return conn - - -# ═══════════════════════════════════════════════════════════════ -# UsbDevice -# ═══════════════════════════════════════════════════════════════ - -class UsbDevice(Stub): - """UsbDevice — USB 设备""" - - def __init__(self, deviceId: int = 0, deviceName: str = "", - vendorId: int = 0, productId: int = 0, - deviceClass: int = 0, deviceSubclass: int = 0, - deviceProtocol: int = 0, configurationCount: int = 1, - interfaceCount: int = 0, - manufacturerName: str = "", productName: str = "", - version: str = "", serialNumber: str = ""): - super().__init__() - self._device_id = deviceId - self._device_name = deviceName - self._vendor_id = vendorId - self._product_id = productId - self._device_class = deviceClass - self._device_subclass = deviceSubclass - self._device_protocol = deviceProtocol - self._configuration_count = configurationCount - self._interface_count = interfaceCount - self._manufacturer_name = manufacturerName - self._product_name = productName - self._version = version - self._serial_number = serialNumber - self._interfaces: List[UsbInterface] = [] - - def getDeviceId(self) -> int: - return self._device_id - - def getDeviceName(self) -> str: - return self._device_name - - def getVendorId(self) -> int: - return self._vendor_id - - def getProductId(self) -> int: - return self._product_id - - def getDeviceClass(self) -> int: - return self._device_class - - def getDeviceSubclass(self) -> int: - return self._device_subclass - - def getDeviceProtocol(self) -> int: - return self._device_protocol - - def getConfigurationCount(self) -> int: - return self._configuration_count - - def getInterfaceCount(self) -> int: - return len(self._interfaces) - - def getInterface(self, index: int) -> Optional["UsbInterface"]: - if 0 <= index < len(self._interfaces): - return self._interfaces[index] - return None - - def getInterfaces(self) -> List["UsbInterface"]: - return list(self._interfaces) - - def getManufacturerName(self) -> str: - return self._manufacturer_name - - def getProductName(self) -> str: - return self._product_name - - def getVersion(self) -> str: - return self._version - - def getSerialNumber(self) -> str: - return self._serial_number - - def describeContents(self) -> int: - return 0 - - def equals(self, other: Any) -> bool: - if not isinstance(other, UsbDevice): - return False - return self._device_id == other._device_id - - def hashCode(self) -> int: - return hash(self._device_id) & 0x7FFFFFFF - - def toString(self) -> str: - return f"UsbDevice(name={self._device_name}, vid={self._vendor_id:x}," \ - f" pid={self._product_id:x})" - - -# ═══════════════════════════════════════════════════════════════ -# UsbDeviceConnection -# ═══════════════════════════════════════════════════════════════ - -class UsbDeviceConnection(Stub): - """UsbDeviceConnection — USB 设备连接""" - - def __init__(self, device: Any): - super().__init__() - self._device = device - self._claimed_interfaces: Set[int] = set() - self._fd = -1 - - def claimInterface(self, intf: "UsbInterface", force: bool = False) -> bool: - self._claimed_interfaces.add(intf.getId()) - return True - - def releaseInterface(self, intf: "UsbInterface") -> bool: - self._claimed_interfaces.discard(intf.getId()) - return True - - def bulkTransfer(self, endpoint: "UsbEndpoint", buffer: bytes, - length: int, timeout: int) -> int: - return -1 - - def controlTransfer(self, requestType: int, request: int, - value: int, index: int, buffer: bytes, - length: int, timeout: int) -> int: - return -1 - - def getRawDescriptors(self) -> bytes: - return b"" - - def getFileDescriptor(self) -> int: - return self._fd - - def serial(self) -> Optional[str]: - if hasattr(self._device, 'getSerialNumber'): - return self._device.getSerialNumber() - return None - - def close(self): - self._claimed_interfaces.clear() - - def toString(self) -> str: - return f"UsbDeviceConnection(device={self._device})" - - -# ═══════════════════════════════════════════════════════════════ -# UsbInterface -# ═══════════════════════════════════════════════════════════════ - -class UsbInterface(Stub): - """UsbInterface — USB 接口""" - - def __init__(self, id: int = 0, alternateSetting: int = 0, - name: str = "", interfaceClass: int = 0, - interfaceSubclass: int = 0, interfaceProtocol: int = 0): - super().__init__() - self._id = id - self._alternate_setting = alternateSetting - self._name = name - self._interface_class = interfaceClass - self._interface_subclass = interfaceSubclass - self._interface_protocol = interfaceProtocol - self._endpoints: List[UsbEndpoint] = [] - - def getId(self) -> int: - return self._id - - def getAlternateSetting(self) -> int: - return self._alternate_setting - - def getName(self) -> str: - return self._name - - def getInterfaceClass(self) -> int: - return self._interface_class - - def getInterfaceSubclass(self) -> int: - return self._interface_subclass - - def getInterfaceProtocol(self) -> int: - return self._interface_protocol - - def getEndpointCount(self) -> int: - return len(self._endpoints) - - def getEndpoint(self, index: int) -> Optional["UsbEndpoint"]: - if 0 <= index < len(self._endpoints): - return self._endpoints[index] - return None - - def toString(self) -> str: - return f"UsbInterface(id={self._id}, name={self._name})" - - -# ═══════════════════════════════════════════════════════════════ -# UsbEndpoint -# ═══════════════════════════════════════════════════════════════ - -class UsbEndpoint(Stub): - """UsbEndpoint — USB 端点""" - - def __init__(self, address: int = 0, endpointNumber: int = 0, - attributes: int = 0, direction: int = 0, - maxPacketSize: int = 64, interval: int = 0, - type_: int = 0): - super().__init__() - self._address = address - self._endpoint_number = endpointNumber - self._attributes = attributes - self._direction = direction - self._max_packet_size = maxPacketSize - self._interval = interval - self._type = type_ - - def getAddress(self) -> int: - return self._address - - def getEndpointNumber(self) -> int: - return self._endpoint_number - - def getAttributes(self) -> int: - return self._attributes - - def getDirection(self) -> int: - return self._direction - - def getMaxPacketSize(self) -> int: - return self._max_packet_size - - def getInterval(self) -> int: - return self._interval - - def getType(self) -> int: - return self._type - - def toString(self) -> str: - return f"UsbEndpoint(addr={self._address:#04x}, type={self._type})" - - -# ═══════════════════════════════════════════════════════════════ -# UsbAccessory -# ═══════════════════════════════════════════════════════════════ - -class UsbAccessory(Stub): - """UsbAccessory — USB 附件""" - - def __init__(self, manufacturer: str = "", model: str = "", - description: str = "", version: str = "", - uri: str = "", serial: str = ""): - super().__init__() - self._manufacturer = manufacturer - self._model = model - self._description = description - self._version = version - self._uri = uri - self._serial = serial - - def getManufacturer(self) -> str: - return self._manufacturer - - def getModel(self) -> str: - return self._model - - def getDescription(self) -> str: - return self._description - - def getVersion(self) -> str: - return self._version - - def getUri(self) -> str: - return self._uri - - def getSerial(self) -> str: - return self._serial - - def toString(self) -> str: - return f"UsbAccessory(manufacturer={self._manufacturer}, model={self._model})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "UsbManager", - "UsbDevice", - "UsbDeviceConnection", - "UsbInterface", - "UsbEndpoint", - "UsbConstants", - "UsbAccessory", -] \ No newline at end of file diff --git a/android_pyapi/android/inputmethod.py b/android_pyapi/android/inputmethod.py deleted file mode 100644 index 553deef..0000000 --- a/android_pyapi/android/inputmethod.py +++ /dev/null @@ -1,804 +0,0 @@ -""" -Android InputMethod 组件 — InputMethodService, Keyboard, KeyboardView, Key 等 -方法签名完全对齐 Android SDK -""" - -import sys -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError, android_context - - -# ═══════════════════════════════════════════════════════════════ -# InputMethod -# ═══════════════════════════════════════════════════════════════ - -class InputMethod(Stub): - """InputMethod — 输入法接口""" - - def __init__(self): - super().__init__() - - def createSession(self, callback: Any) -> Optional["InputMethodSession"]: - """创建输入法会话""" - return None - - def startInput(self, input_connection: Any, editor_info: Any) -> None: - """开始输入""" - pass - - def restartInput(self, input_connection: Any, editor_info: Any) -> None: - """重启输入""" - pass - - def dispatchKeyEvent(self, event: Any) -> None: - """分发按键事件""" - pass - - def dispatchTrackballEvent(self, event: Any) -> None: - """分发轨迹球事件""" - pass - - def dispatchGenericMotionEvent(self, event: Any) -> None: - """分发通用运动事件""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# InputMethodSession -# ═══════════════════════════════════════════════════════════════ - -class InputMethodSession(Stub): - """InputMethodSession — 输入法会话接口""" - - def __init__(self): - super().__init__() - - def finishInput(self): - """结束输入""" - pass - - def updateSelection(self, old_sel_start: int, old_sel_end: int, - new_sel_start: int, new_sel_end: int, - candidates_start: int, candidates_end: int): - """更新选区""" - pass - - def updateCursor(self, old_cursor: Any, new_cursor: Any): - """更新光标""" - pass - - def displayCompletions(self, completions: List["CompletionInfo"]): - """显示补全""" - pass - - def appPrivateCommand(self, action: str, data: Any): - """应用私有命令""" - pass - - def viewClicked(self, focus_changed: bool): - """视图点击""" - pass - - def updateExtractedText(self, token: int, extracted_text: Any): - """更新提取文本""" - pass - - def dispatchKeyEvent(self, key_code: int, event: Any) -> None: - """分发按键事件""" - pass - - def dispatchTrackballEvent(self, event: Any) -> None: - pass - - def dispatchGenericMotionEvent(self, event: Any) -> None: - pass - - def toggleSoftInput(self, show_flags: int, hide_flags: int): - """切换软键盘""" - pass - - def notifyImeHidden(self): - """通知输入法隐藏""" - pass - - def removeImeSurface(self): - """移除输入法界面""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# AbstractInputMethodService -# ═══════════════════════════════════════════════════════════════ - -class AbstractInputMethodService(Stub): - """AbstractInputMethodService — 抽象输入法服务基类""" - - def __init__(self): - super().__init__() - self._input_method: Optional[InputMethod] = None - self._input_method_session: Optional[InputMethodSession] = None - - def onCreate(self): - """服务创建""" - pass - - def onDestroy(self): - """服务销毁""" - pass - - def onBind(self, intent: Any) -> Any: - """绑定""" - return None - - def onUnbind(self, intent: Any) -> bool: - """解绑""" - return False - - def onCreateInputMethod(self) -> InputMethod: - """创建输入法实例""" - return InputMethod() - - def onCreateInputMethodSession(self) -> InputMethodSession: - """创建输入法会话""" - return InputMethodSession() - - def getKeyDispatcherState(self) -> Any: - """获取按键调度状态""" - return None - - -# ═══════════════════════════════════════════════════════════════ -# InputMethodImpl -# ═══════════════════════════════════════════════════════════════ - -class InputMethodImpl(InputMethod): - """InputMethodImpl — 输入法实现""" - - def __init__(self): - super().__init__() - self._session: Optional[InputMethodSession] = None - - def createSession(self, callback: Any) -> Optional["InputMethodSession"]: - self._session = InputMethodSessionImpl() - return self._session - - def startInput(self, input_connection: Any, editor_info: Any): - sys.stderr.write("[InputMethodImpl] startInput\n") - - def restartInput(self, input_connection: Any, editor_info: Any): - sys.stderr.write("[InputMethodImpl] restartInput\n") - - def dispatchKeyEvent(self, event: Any): - sys.stderr.write("[InputMethodImpl] dispatchKeyEvent\n") - - def dispatchTrackballEvent(self, event: Any): - pass - - def dispatchGenericMotionEvent(self, event: Any): - pass - - -# ═══════════════════════════════════════════════════════════════ -# InputMethodSessionImpl -# ═══════════════════════════════════════════════════════════════ - -class InputMethodSessionImpl(InputMethodSession): - """InputMethodSessionImpl — 输入法会话实现""" - - def __init__(self): - super().__init__() - - def finishInput(self): - sys.stderr.write("[InputMethodSessionImpl] finishInput\n") - - def updateSelection(self, old_sel_start: int, old_sel_end: int, - new_sel_start: int, new_sel_end: int, - candidates_start: int, candidates_end: int): - pass - - def displayCompletions(self, completions: List["CompletionInfo"]): - pass - - def dispatchKeyEvent(self, key_code: int, event: Any): - pass - - -# ═══════════════════════════════════════════════════════════════ -# InputMethodService -# ═══════════════════════════════════════════════════════════════ - -class InputMethodService(AbstractInputMethodService): - """InputMethodService — 输入法服务""" - - SHOW_EXPLICIT = 0x0001 - SHOW_IMPLICIT = 0x0002 - - def __init__(self): - super().__init__() - self._input_view: Optional[Any] = None - self._candidates_view: Optional[Any] = None - self._input_connection: Optional[Any] = None - self._input_editor_info: Optional["EditorInfo"] = None - self._input_binding: Optional["InputBinding"] = None - self._input_view_shown: bool = False - self._fullscreen_mode: bool = False - self._extract_to_fullscreen_mode: bool = False - self._is_fullscreen: bool = False - self._keyboard: Optional["Keyboard"] = None - self._settings_observer: Any = None - self._candidates_view_shown: bool = False - self._show_input_requested: bool = False - self._token: Any = None - self._window_visible: bool = False - self._input_started: bool = False - - def onCreate(self): - """创建服务""" - sys.stderr.write("[InputMethodService] onCreate\n") - - def onCreateInputView(self) -> Optional[Any]: - """创建输入视图""" - return None - - def onCreateCandidatesView(self) -> Optional[Any]: - """创建候选视图""" - return None - - def onStartInputView(self, info: "EditorInfo", restarting: bool): - """开始输入视图""" - pass - - def onFinishInputView(self, finishing_input: bool): - """结束输入视图""" - pass - - def onStartInput(self, attribute: "EditorInfo", restarting: bool): - """开始输入""" - self._input_editor_info = attribute - self._input_started = True - - def onFinishInput(self): - """结束输入""" - self._input_started = False - - def onBindInput(self, binding: "InputBinding"): - """绑定输入""" - self._input_binding = binding - - def onUnbindInput(self) -> bool: - """解绑输入""" - self._input_binding = None - return False - - def onDestroy(self): - """销毁""" - self._input_view = None - self._candidates_view = None - self._input_connection = None - - def showWindow(self, show_flags: int): - """显示窗口""" - self._window_visible = True - sys.stderr.write(f"[InputMethodService] showWindow: flags={show_flags}\n") - - def hideWindow(self): - """隐藏窗口""" - self._window_visible = False - - def setInputView(self, view: Any): - """设置输入视图""" - self._input_view = view - - def setCandidatesView(self, view: Any): - """设置候选视图""" - self._candidates_view = view - - def updateInputViewShown(self): - """更新输入视图显示状态""" - pass - - def updateFullscreenMode(self): - """更新全屏模式""" - pass - - def updateExtractToFullscreenMode(self): - """更新提取全屏模式""" - pass - - def requestHideSelf(self, flags: int): - """请求隐藏自身""" - sys.stderr.write(f"[InputMethodService] requestHideSelf: flags={flags}\n") - - def switchInputMethod(self, id: str): - """切换输入法""" - sys.stderr.write(f"[InputMethodService] switchInputMethod: id={id}\n") - - def sendDefaultEditorAction(self, from_enter_key: bool): - """发送默认编辑器动作""" - pass - - def sendDownUpKeyEvents(self, key_event_code: int): - """发送按键按下和释放事件""" - pass - - def getCurrentInputBinding(self) -> Optional["InputBinding"]: - """获取当前输入绑定""" - return self._input_binding - - def getCurrentInputEditorInfo(self) -> Optional["EditorInfo"]: - """获取当前编辑器信息""" - return self._input_editor_info - - def getCurrentInputConnection(self) -> Optional["InputConnection"]: - """获取当前输入连接""" - return self._input_connection - - def getTextForImeAction(self, ime_options: int) -> str: - """获取IME动作的文本""" - action_map = { - 0x0001: "None", - 0x0002: "Go", - 0x0003: "Search", - 0x0004: "Send", - 0x0005: "Next", - 0x0006: "Done", - 0x0007: "Previous", - } - action = ime_options & 0x000000FF - return action_map.get(action, "Unknown") - - def doStartInput(self, input_connection: Any, editor_info: "EditorInfo", restarting: bool): - """执行启动输入""" - self._input_connection = input_connection - self._input_editor_info = editor_info - self.onStartInput(editor_info, restarting) - if self._window_visible: - self.onStartInputView(editor_info, restarting) - - def doFinishInput(self): - """执行结束输入""" - self.onFinishInput() - self.onFinishInputView(True) - - def onEvaluateInputViewShown(self) -> bool: - """评估是否显示输入视图""" - return self._input_view is not None - - def onEvaluateFullscreenMode(self) -> bool: - """评估是否全屏模式""" - return self._fullscreen_mode - - def onConfigureWindow(self, win: Any, is_fullscreen: bool, is_candidates: bool): - """配置窗口""" - pass - - def isInputViewShown(self) -> bool: - """输入视图是否已显示""" - return self._input_view_shown - - def updateWindow(self, show_input: bool = False): - """更新窗口""" - pass - - def setExtractView(self, view: Any): - """设置提取视图""" - pass - - def getExtractView(self) -> Optional[Any]: - """获取提取视图""" - return None - - def getInputView(self) -> Optional[Any]: - """获取输入视图""" - return self._input_view - - def getCandidatesView(self) -> Optional[Any]: - """获取候选视图""" - return self._candidates_view - - def isFullscreenMode(self) -> bool: - """是否全屏模式""" - return self._is_fullscreen - - def onDisplayCompletions(self, completions: List["CompletionInfo"]): - """显示补全""" - pass - - def onUpdateSelection(self, old_sel_start: int, old_sel_end: int, - new_sel_start: int, new_sel_end: int, - candidates_start: int, candidates_end: int): - """更新选区""" - pass - - def onViewClicked(self, focus_changed: bool): - """视图点击""" - pass - - def onWindowShown(self): - """窗口显示""" - self._window_visible = True - - def onWindowHidden(self): - """窗口隐藏""" - self._window_visible = False - - def setKeyboard(self, keyboard: "Keyboard"): - """设置键盘""" - self._keyboard = keyboard - - def getKeyboard(self) -> Optional["Keyboard"]: - """获取键盘""" - return self._keyboard - - -# ═══════════════════════════════════════════════════════════════ -# Key -# ═══════════════════════════════════════════════════════════════ - -class Key(Stub): - """Key — 键盘按键""" - - def __init__(self, row: "KeyboardRow" = None): - super().__init__() - self.codes: List[int] = [] - self.label: Optional[str] = None - self.icon: Any = None - self.sticky: bool = False - self.modifier: bool = False - self.repeatable: bool = False - self.edgeFlags: int = 0 - self.popupCharacters: Optional[str] = None - self.popupResId: int = 0 - self._x: int = 0 - self._y: int = 0 - self._width: int = 0 - self._height: int = 0 - self._gap: int = 0 - self._row: Optional["KeyboardRow"] = row - self._pressed: bool = False - self._on: bool = False - - def getCode(self) -> int: - """获取按键码""" - return self.codes[0] if self.codes else 0 - - def isPressed(self) -> bool: - """是否按下""" - return self._pressed - - def setPressed(self, pressed: bool): - """设置按下状态""" - self._pressed = pressed - - def isOn(self) -> bool: - """是否开启""" - return self._on - - def setOn(self, on_state: bool): - """设置开启状态""" - self._on = on_state - - def squaredDistance(self, x: int, y: int) -> int: - """计算距离平方""" - center_x = self._x + self._width // 2 - center_y = self._y + self._height // 2 - return (x - center_x) ** 2 + (y - center_y) ** 2 - - def hitTest(self, x: int, y: int) -> bool: - """命中测试""" - return (self._x <= x <= self._x + self._width and - self._y <= y <= self._y + self._height) - - def getX(self) -> int: - return self._x - - def getY(self) -> int: - return self._y - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def toString(self) -> str: - return f"Key(code={self.getCode()}, label={self.label})" - - -# ═══════════════════════════════════════════════════════════════ -# KeyboardRow -# ═══════════════════════════════════════════════════════════════ - -class KeyboardRow(Stub): - """KeyboardRow — 键盘行""" - - def __init__(self, keyboard: "Keyboard" = None): - super().__init__() - self._keys: List[Key] = [] - self._keyboard: Optional["Keyboard"] = keyboard - self._default_width: int = 0 - self._default_height: int = 0 - self._row_edge_flags: int = 0 - self._mode: str = "" - - def getKeys(self) -> List[Key]: - return list(self._keys) - - def addKey(self, key: Key): - self._keys.append(key) - - def getKeyboard(self) -> Optional["Keyboard"]: - return self._keyboard - - def getDefaultWidth(self) -> int: - return self._default_width - - def getDefaultHeight(self) -> int: - return self._default_height - - def toString(self) -> str: - return f"KeyboardRow(keys={len(self._keys)})" - - -# ═══════════════════════════════════════════════════════════════ -# Keyboard -# ═══════════════════════════════════════════════════════════════ - -class Keyboard(Stub): - """Keyboard — 键盘布局""" - - def __init__(self, context: Any = None, xml_layout_res_id: int = 0): - super().__init__() - self._keys: List[Key] = [] - self._rows: List[KeyboardRow] = [] - self._modifier_keys: List[Key] = [] - self._height: int = 0 - self._min_width: int = 0 - self._key_width: int = 0 - self._key_height: int = 0 - self._horizontal_gap: int = 0 - self._vertical_gap: int = 0 - self._shift_keys: List[Key] = [] - self._alt_keys: List[Key] = [] - - def getKeys(self) -> List[Key]: - return list(self._keys) - - def getHeight(self) -> int: - return self._height - - def getMinWidth(self) -> int: - return self._min_width - - def getKeyWidth(self) -> int: - return self._key_width - - def getKeyHeight(self) -> int: - return self._key_height - - def getHorizontalGap(self) -> int: - return self._horizontal_gap - - def getVerticalGap(self) -> int: - return self._vertical_gap - - def getModifierKeys(self) -> List[Key]: - return list(self._modifier_keys) - - def getNearestKeys(self, x: int, y: int) -> List[Key]: - """获取最近的按键""" - nearest: List[Key] = [] - for key in self._keys: - if key.hitTest(x, y): - nearest.append(key) - return nearest - - def setKeys(self, keys: List[Key]): - """设置按键列表""" - self._keys = keys - - def getRows(self) -> List[KeyboardRow]: - return list(self._rows) - - def setShifted(self, shift_state: bool): - """设置Shift状态""" - pass - - def isShifted(self) -> bool: - """是否Shift状态""" - return False - - def getShiftKeyIndex(self) -> int: - """获取Shift按键索引""" - for i, key in enumerate(self._keys): - if key.modifier: - return i - return -1 - - def setVerticalGap(self, gap: int): - self._vertical_gap = gap - - def setHorizontalGap(self, gap: int): - self._horizontal_gap = gap - - def setKeyWidth(self, width: int): - self._key_width = width - - def setKeyHeight(self, height: int): - self._key_height = height - - def toString(self) -> str: - return f"Keyboard(keys={len(self._keys)}, rows={len(self._rows)})" - - -# ═══════════════════════════════════════════════════════════════ -# KeyboardView -# ═══════════════════════════════════════════════════════════════ - -class KeyboardView(Stub): - """KeyboardView — 键盘视图""" - - class OnKeyboardActionListener(Stub): - """OnKeyboardActionListener — 键盘动作监听器""" - - def onKey(self, primary_code: int): - pass - - def onPress(self, primary_code: int): - pass - - def onRelease(self, primary_code: int): - pass - - def onText(self, text: str): - pass - - def swipeLeft(self): - pass - - def swipeRight(self): - pass - - def swipeDown(self): - pass - - def swipeUp(self): - pass - - def __init__(self, context: Any = None, attrs: Any = None): - super().__init__() - self._keyboard: Optional[Keyboard] = None - self._on_keyboard_action_listener: Optional["KeyboardView.OnKeyboardActionListener"] = None - self._preview_enabled: bool = True - self._proximity_correction_enabled: bool = True - self._vertical_correction: int = 0 - self._key_text_color: int = 0xFF000000 - self._key_text_size: int = 20 - self._shadow_color: int = 0x00000000 - self._shadow_radius: float = 0.0 - self._background_color: int = 0xFF888888 - - def setKeyboard(self, keyboard: Keyboard): - self._keyboard = keyboard - - def getKeyboard(self) -> Optional[Keyboard]: - return self._keyboard - - def onDraw(self, canvas: Any): - """绘制键盘""" - pass - - def setOnKeyboardActionListener(self, listener: "KeyboardView.OnKeyboardActionListener"): - self._on_keyboard_action_listener = listener - - def getOnKeyboardActionListener(self) -> Optional["KeyboardView.OnKeyboardActionListener"]: - return self._on_keyboard_action_listener - - def setPreviewEnabled(self, preview_enabled: bool): - self._preview_enabled = preview_enabled - - def isPreviewEnabled(self) -> bool: - return self._preview_enabled - - def setProximityCorrectionEnabled(self, enabled: bool): - self._proximity_correction_enabled = enabled - - def isProximityCorrectionEnabled(self) -> bool: - return self._proximity_correction_enabled - - def setVerticalCorrection(self, vertical_correction: int): - self._vertical_correction = vertical_correction - - def getVerticalCorrection(self) -> int: - return self._vertical_correction - - def setKeyTextColor(self, color: int): - self._key_text_color = color - - def setKeyTextSize(self, size: int): - self._key_text_size = size - - def setShadowColor(self, color: int): - self._shadow_color = color - - def setShadowRadius(self, radius: float): - self._shadow_radius = radius - - def setBackgroundColor(self, color: int): - self._background_color = color - - def invalidateAllKeys(self): - """使所有按键失效""" - pass - - def invalidateKey(self, key_index: int): - """使指定按键失效""" - pass - - def onClick(self, key: Key): - """点击按键""" - if self._on_keyboard_action_listener and key.codes: - self._on_keyboard_action_listener.onKey(key.codes[0]) - - def onLongPress(self, key: Key) -> bool: - """长按按键""" - if key.popupCharacters: - return True - return False - - def closing(self): - """关闭""" - pass - - def handleBack(self) -> bool: - """处理返回键""" - return False - - def setKeys(self, keys: List[Key]): - """设置按键""" - if self._keyboard: - self._keyboard.setKeys(keys) - - -# ═══════════════════════════════════════════════════════════════ -# KeyEventCallback -# ═══════════════════════════════════════════════════════════════ - -class KeyEventCallback(Stub): - """KeyEventCallback — 按键事件回调""" - - def onKeyDown(self, key_code: int, event: Any) -> bool: - """按键按下""" - return False - - def onKeyLongPress(self, key_code: int, event: Any) -> bool: - """按键长按""" - return False - - def onKeyUp(self, key_code: int, event: Any) -> bool: - """按键释放""" - return False - - def onKeyMultiple(self, key_code: int, count: int, event: Any) -> bool: - """按键重复""" - return False - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "InputMethodService", - "InputMethodImpl", - "InputMethodSessionImpl", - "AbstractInputMethodService", - "InputMethod", - "InputMethodSession", - "Keyboard", - "KeyboardView", - "Key", - "KeyEventCallback", - "KeyboardRow", -] \ No newline at end of file diff --git a/android_pyapi/android/inputmethodservice.py b/android_pyapi/android/inputmethodservice.py deleted file mode 100644 index ffb09b6..0000000 --- a/android_pyapi/android/inputmethodservice.py +++ /dev/null @@ -1,1078 +0,0 @@ -""" -Android InputMethod Service 组件 — InputMethodService, AbstractInputMethodService, Keyboard, Key, KeyboardView 等 -方法签名完全对齐 Android SDK (android.inputmethodservice 包) -""" - -from __future__ import annotations - -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -KEYCODE_UNKNOWN = 0 -KEYCODE_SOFT_LEFT = 1 -KEYCODE_SOFT_RIGHT = 2 -KEYCODE_HOME = 3 -KEYCODE_BACK = 4 -KEYCODE_CALL = 5 -KEYCODE_ENDCALL = 6 -KEYCODE_0 = 7 -KEYCODE_1 = 8 -KEYCODE_2 = 9 -KEYCODE_3 = 10 -KEYCODE_4 = 11 -KEYCODE_5 = 12 -KEYCODE_6 = 13 -KEYCODE_7 = 14 -KEYCODE_8 = 15 -KEYCODE_9 = 16 -KEYCODE_STAR = 17 -KEYCODE_POUND = 18 -KEYCODE_DPAD_UP = 19 -KEYCODE_DPAD_DOWN = 20 -KEYCODE_DPAD_LEFT = 21 -KEYCODE_DPAD_RIGHT = 22 -KEYCODE_DPAD_CENTER = 23 -KEYCODE_VOLUME_UP = 24 -KEYCODE_VOLUME_DOWN = 25 -KEYCODE_POWER = 26 -KEYCODE_CAMERA = 27 -KEYCODE_CLEAR = 28 -KEYCODE_A = 29 -KEYCODE_B = 30 -KEYCODE_C = 31 -KEYCODE_D = 32 -KEYCODE_E = 33 -KEYCODE_F = 34 -KEYCODE_G = 35 -KEYCODE_H = 36 -KEYCODE_I = 37 -KEYCODE_J = 38 -KEYCODE_K = 39 -KEYCODE_L = 40 -KEYCODE_M = 41 -KEYCODE_N = 42 -KEYCODE_O = 43 -KEYCODE_P = 44 -KEYCODE_Q = 45 -KEYCODE_R = 46 -KEYCODE_S = 47 -KEYCODE_T = 48 -KEYCODE_U = 49 -KEYCODE_V = 50 -KEYCODE_W = 51 -KEYCODE_X = 52 -KEYCODE_Y = 53 -KEYCODE_Z = 54 -KEYCODE_COMMA = 55 -KEYCODE_PERIOD = 56 -KEYCODE_ALT_LEFT = 57 -KEYCODE_ALT_RIGHT = 58 -KEYCODE_SHIFT_LEFT = 59 -KEYCODE_SHIFT_RIGHT = 60 -KEYCODE_TAB = 61 -KEYCODE_SPACE = 62 -KEYCODE_SYM = 63 -KEYCODE_EXPLORER = 64 -KEYCODE_ENVELOPE = 65 -KEYCODE_ENTER = 66 -KEYCODE_DEL = 67 -KEYCODE_GRAVE = 68 -KEYCODE_MINUS = 69 -KEYCODE_EQUALS = 70 -KEYCODE_LEFT_BRACKET = 71 -KEYCODE_RIGHT_BRACKET = 72 -KEYCODE_BACKSLASH = 73 -KEYCODE_SEMICOLON = 74 -KEYCODE_APOSTROPHE = 75 -KEYCODE_SLASH = 76 -KEYCODE_AT = 77 -KEYCODE_NUM = 78 -KEYCODE_HEADSETHOOK = 79 -KEYCODE_FOCUS = 80 -KEYCODE_PLUS = 81 -KEYCODE_MENU = 82 -KEYCODE_NOTIFICATION = 83 -KEYCODE_SEARCH = 84 -KEYCODE_MEDIA_PLAY_PAUSE = 85 -KEYCODE_MEDIA_STOP = 86 -KEYCODE_MEDIA_NEXT = 87 -KEYCODE_MEDIA_PREVIOUS = 88 -KEYCODE_MEDIA_REWIND = 89 -KEYCODE_MEDIA_FAST_FORWARD = 90 -KEYCODE_MUTE = 91 -KEYCODE_PAGE_UP = 92 -KEYCODE_PAGE_DOWN = 93 -KEYCODE_PICTSYMBOLS = 94 -KEYCODE_SWITCH_CHARSET = 95 -KEYCODE_BUTTON_A = 96 -KEYCODE_BUTTON_B = 97 -KEYCODE_BUTTON_C = 98 -KEYCODE_BUTTON_X = 99 -KEYCODE_BUTTON_Y = 100 -KEYCODE_BUTTON_Z = 101 -KEYCODE_BUTTON_L1 = 102 -KEYCODE_BUTTON_R1 = 103 -KEYCODE_BUTTON_L2 = 104 -KEYCODE_BUTTON_R2 = 105 -KEYCODE_BUTTON_THUMBL = 106 -KEYCODE_BUTTON_THUMBR = 107 -KEYCODE_BUTTON_START = 108 -KEYCODE_BUTTON_SELECT = 109 -KEYCODE_BUTTON_MODE = 110 -KEYCODE_ESCAPE = 111 -KEYCODE_FORWARD_DEL = 112 -KEYCODE_CTRL_LEFT = 113 -KEYCODE_CTRL_RIGHT = 114 -KEYCODE_CAPS_LOCK = 115 -KEYCODE_SCROLL_LOCK = 116 -KEYCODE_META_LEFT = 117 -KEYCODE_META_RIGHT = 118 -KEYCODE_FUNCTION = 119 -KEYCODE_SYSRQ = 120 -KEYCODE_BREAK = 121 -KEYCODE_MOVE_HOME = 122 -KEYCODE_MOVE_END = 123 -KEYCODE_INSERT = 124 -KEYCODE_FORWARD = 125 -KEYCODE_MEDIA_PLAY = 126 -KEYCODE_MEDIA_PAUSE = 127 -KEYCODE_MEDIA_CLOSE = 128 -KEYCODE_MEDIA_EJECT = 129 -KEYCODE_MEDIA_RECORD = 130 -KEYCODE_F1 = 131 -KEYCODE_F2 = 132 -KEYCODE_F3 = 133 -KEYCODE_F4 = 134 -KEYCODE_F5 = 135 -KEYCODE_F6 = 136 -KEYCODE_F7 = 137 -KEYCODE_F8 = 138 -KEYCODE_F9 = 139 -KEYCODE_F10 = 140 -KEYCODE_F11 = 141 -KEYCODE_F12 = 142 -KEYCODE_NUM_LOCK = 143 -KEYCODE_NUMPAD_0 = 144 -KEYCODE_NUMPAD_1 = 145 -KEYCODE_NUMPAD_2 = 146 -KEYCODE_NUMPAD_3 = 147 -KEYCODE_NUMPAD_4 = 148 -KEYCODE_NUMPAD_5 = 149 -KEYCODE_NUMPAD_6 = 150 -KEYCODE_NUMPAD_7 = 151 -KEYCODE_NUMPAD_8 = 152 -KEYCODE_NUMPAD_9 = 153 -KEYCODE_NUMPAD_DIVIDE = 154 -KEYCODE_NUMPAD_MULTIPLY = 155 -KEYCODE_NUMPAD_SUBTRACT = 156 -KEYCODE_NUMPAD_ADD = 157 -KEYCODE_NUMPAD_DOT = 158 -KEYCODE_NUMPAD_COMMA = 159 -KEYCODE_NUMPAD_ENTER = 160 -KEYCODE_NUMPAD_EQUALS = 161 -KEYCODE_NUMPAD_LEFT_PAREN = 162 -KEYCODE_NUMPAD_RIGHT_PAREN = 163 -KEYCODE_VOLUME_MUTE = 164 -KEYCODE_INFO = 165 -KEYCODE_CHANNEL_UP = 166 -KEYCODE_CHANNEL_DOWN = 167 -KEYCODE_ZOOM_IN = 168 -KEYCODE_ZOOM_OUT = 169 -KEYCODE_TV = 170 -KEYCODE_WINDOW = 171 -KEYCODE_GUIDE = 172 -KEYCODE_DVR = 173 -KEYCODE_BOOKMARK = 174 -KEYCODE_CAPTIONS = 175 -KEYCODE_SETTINGS = 176 -KEYCODE_TV_POWER = 177 -KEYCODE_TV_INPUT = 178 -KEYCODE_STB_POWER = 179 -KEYCODE_STB_INPUT = 180 -KEYCODE_AVR_POWER = 181 -KEYCODE_AVR_INPUT = 182 -KEYCODE_PROG_RED = 183 -KEYCODE_PROG_GREEN = 184 -KEYCODE_PROG_YELLOW = 185 -KEYCODE_PROG_BLUE = 186 -KEYCODE_APP_SWITCH = 187 -KEYCODE_BUTTON_1 = 188 -KEYCODE_BUTTON_2 = 189 -KEYCODE_BUTTON_3 = 190 -KEYCODE_BUTTON_4 = 191 -KEYCODE_BUTTON_5 = 192 -KEYCODE_BUTTON_6 = 193 -KEYCODE_BUTTON_7 = 194 -KEYCODE_BUTTON_8 = 195 -KEYCODE_BUTTON_9 = 196 -KEYCODE_BUTTON_10 = 197 -KEYCODE_BUTTON_11 = 198 -KEYCODE_BUTTON_12 = 199 -KEYCODE_BUTTON_13 = 200 -KEYCODE_BUTTON_14 = 201 -KEYCODE_BUTTON_15 = 202 -KEYCODE_BUTTON_16 = 203 -KEYCODE_LANGUAGE_SWITCH = 204 -KEYCODE_MANNER_MODE = 205 -KEYCODE_3D_MODE = 206 -KEYCODE_CONTACTS = 207 -KEYCODE_CALENDAR = 208 -KEYCODE_MUSIC = 209 -KEYCODE_CALCULATOR = 210 -KEYCODE_ZENKAKU_HANKAKU = 211 -KEYCODE_EISU = 212 -KEYCODE_MUHENKAN = 213 -KEYCODE_HENKAN = 214 -KEYCODE_KATAKANA_HIRAGANA = 215 -KEYCODE_YEN = 216 -KEYCODE_RO = 217 -KEYCODE_KANA = 218 -KEYCODE_ASSIST = 219 -KEYCODE_BRIGHTNESS_DOWN = 220 -KEYCODE_BRIGHTNESS_UP = 221 -KEYCODE_MEDIA_AUDIO_TRACK = 222 -KEYCODE_SLEEP = 223 -KEYCODE_WAKEUP = 224 -KEYCODE_PAIRING = 225 -KEYCODE_MEDIA_TOP_MENU = 226 -KEYCODE_11 = 227 -KEYCODE_12 = 228 -KEYCODE_LAST_CHANNEL = 229 -KEYCODE_TV_DATA_SERVICE = 230 -KEYCODE_VOICE_ASSIST = 231 -KEYCODE_TV_RADIO_SERVICE = 232 -KEYCODE_TV_TELETEXT = 233 -KEYCODE_TV_NUMBER_ENTRY = 234 -KEYCODE_TV_TERRESTRIAL_ANALOG = 235 -KEYCODE_TV_TERRESTRIAL_DIGITAL = 236 -KEYCODE_TV_SATELLITE = 237 -KEYCODE_TV_SATELLITE_BS = 238 -KEYCODE_TV_SATELLITE_CS = 239 -KEYCODE_TV_SATELLITE_SERVICE = 240 -KEYCODE_TV_NETWORK = 241 -KEYCODE_TV_ANTENNA_CABLE = 242 -KEYCODE_TV_INPUT_HDMI_1 = 243 -KEYCODE_TV_INPUT_HDMI_2 = 244 -KEYCODE_TV_INPUT_HDMI_3 = 245 -KEYCODE_TV_INPUT_HDMI_4 = 246 -KEYCODE_TV_INPUT_COMPOSITE_1 = 247 -KEYCODE_TV_INPUT_COMPOSITE_2 = 248 -KEYCODE_TV_INPUT_COMPONENT_1 = 249 -KEYCODE_TV_INPUT_COMPONENT_2 = 250 -KEYCODE_TV_INPUT_VGA_1 = 251 -KEYCODE_TV_AUDIO_DESCRIPTION = 252 -KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP = 253 -KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN = 254 -KEYCODE_TV_ZOOM_MODE = 255 -KEYCODE_TV_CONTENTS_MENU = 256 -KEYCODE_TV_MEDIA_CONTEXT_MENU = 257 -KEYCODE_TV_TIMER_PROGRAMMING = 258 -KEYCODE_HELP = 259 -KEYCODE_NAVIGATE_PREVIOUS = 260 -KEYCODE_NAVIGATE_NEXT = 261 -KEYCODE_NAVIGATE_IN = 262 -KEYCODE_NAVIGATE_OUT = 263 -KEYCODE_STEM_PRIMARY = 264 -KEYCODE_STEM_1 = 265 -KEYCODE_STEM_2 = 266 -KEYCODE_STEM_3 = 267 -KEYCODE_DPAD_UP_LEFT = 268 -KEYCODE_DPAD_DOWN_LEFT = 269 -KEYCODE_DPAD_UP_RIGHT = 270 -KEYCODE_DPAD_DOWN_RIGHT = 271 -KEYCODE_MEDIA_SKIP_FORWARD = 272 -KEYCODE_MEDIA_SKIP_BACKWARD = 273 -KEYCODE_MEDIA_STEP_FORWARD = 274 -KEYCODE_MEDIA_STEP_BACKWARD = 275 -KEYCODE_MEDIA_TOP_MENU_2 = 276 -KEYCODE_SOFT_SLEEP = 277 -KEYCODE_CUT = 278 -KEYCODE_COPY = 279 -KEYCODE_PASTE = 280 -KEYCODE_SYSTEM_NAVIGATION_UP = 281 -KEYCODE_SYSTEM_NAVIGATION_DOWN = 282 -KEYCODE_SYSTEM_NAVIGATION_LEFT = 283 -KEYCODE_SYSTEM_NAVIGATION_RIGHT = 284 -KEYCODE_ALL_APPS = 285 -KEYCODE_REFRESH = 286 -KEYCODE_THUMBS_UP = 287 -KEYCODE_THUMBS_DOWN = 288 -KEYCODE_PROFILE_SWITCH = 289 -KEYCODE_VIDEO_APP_1 = 290 -KEYCODE_VIDEO_APP_2 = 291 -KEYCODE_VIDEO_APP_3 = 292 -KEYCODE_VIDEO_APP_4 = 293 -KEYCODE_VIDEO_APP_5 = 294 -KEYCODE_VIDEO_APP_6 = 295 -KEYCODE_VIDEO_APP_7 = 296 -KEYCODE_VIDEO_APP_8 = 297 -KEYCODE_FEATURED_APP_1 = 298 -KEYCODE_FEATURED_APP_2 = 299 -KEYCODE_FEATURED_APP_3 = 300 -KEYCODE_FEATURED_APP_4 = 301 -KEYCODE_DEMO_APP_1 = 302 -KEYCODE_DEMO_APP_2 = 303 -KEYCODE_DEMO_APP_3 = 304 -KEYCODE_DEMO_APP_4 = 305 - - -# ═══════════════════════════════════════════════════════════════ -# Key -# ═══════════════════════════════════════════════════════════════ - -class Key(Stub): - """Key — 键盘按键""" - - KEY_FLAG_ALT = 1 - KEY_FLAG_SHIFT = 2 - KEY_FLAG_CAPS_LOCK = 4 - KEY_FLAG_CTRL = 8 - KEY_FLAG_META = 16 - KEY_FLAG_FUNCTION = 32 - KEY_FLAG_MODIFIER = 64 - KEY_FLAG_REPEAT = 128 - KEY_FLAG_LONGPRESS = 256 - KEY_FLAG_CLUSTER = 512 - KEY_FLAG_CANCELED = 1024 - KEY_FLAG_EDGE = 2048 - KEY_FLAG_GESTURE = 4096 - KEY_FLAG_VIRTUAL = 8192 - - KEY_TYPE_NORMAL = 0 - KEY_TYPE_MODIFIER = 1 - KEY_TYPE_SPACE = 2 - KEY_TYPE_DONE = 3 - KEY_TYPE_DELETE = 4 - KEY_TYPE_ACTION = 5 - KEY_TYPE_CANCEL = 6 - KEY_TYPE_MODE_CHANGE = 7 - KEY_TYPE_SHIFT = 8 - KEY_TYPE_ALT = 9 - KEY_TYPE_CTRL = 10 - KEY_TYPE_FUNCTION = 11 - KEY_TYPE_EMOJI = 12 - KEY_TYPE_LANGUAGE = 13 - - def __init__(self, key_code: int = KEYCODE_UNKNOWN, label: str = "", - type: int = KEY_TYPE_NORMAL): - super().__init__() - self._key_code = key_code - self._label = label - self._type = type - self._text: str = "" - self._icon: Any = None - self._x: int = 0 - self._y: int = 0 - self._width: int = 0 - self._height: int = 0 - self._gap: int = 0 - self._sticky: bool = False - self._modifier: bool = False - self._enabled: bool = True - self._repeating: bool = False - self._edge_flags: int = 0 - self._popup_characters: str = "" - self._secondary_labels: List[str] = [] - - def getKeyCode(self) -> int: - return self._key_code - - def setKeyCode(self, key_code: int): - self._key_code = key_code - - def getLabel(self) -> str: - return self._label - - def setLabel(self, label: str): - self._label = label - - def getType(self) -> int: - return self._type - - def setType(self, type_: int): - self._type = type_ - - def getText(self) -> str: - return self._text - - def setText(self, text: str): - self._text = text - - def getIcon(self) -> Any: - return self._icon - - def setIcon(self, icon: Any): - self._icon = icon - - def getX(self) -> int: - return self._x - - def setX(self, x: int): - self._x = x - - def getY(self) -> int: - return self._y - - def setY(self, y: int): - self._y = y - - def getWidth(self) -> int: - return self._width - - def setWidth(self, width: int): - self._width = width - - def getHeight(self) -> int: - return self._height - - def setHeight(self, height: int): - self._height = height - - def getGap(self) -> int: - return self._gap - - def setGap(self, gap: int): - self._gap = gap - - def isSticky(self) -> bool: - return self._sticky - - def setSticky(self, sticky: bool): - self._sticky = sticky - - def isModifier(self) -> bool: - return self._modifier - - def setModifier(self, modifier: bool): - self._modifier = modifier - - def isEnabled(self) -> bool: - return self._enabled - - def setEnabled(self, enabled: bool): - self._enabled = enabled - - def isRepeating(self) -> bool: - return self._repeating - - def setRepeating(self, repeating: bool): - self._repeating = repeating - - def getPopupCharacters(self) -> str: - return self._popup_characters - - def setPopupCharacters(self, chars: str): - self._popup_characters = chars - - def getSecondaryLabels(self) -> List[str]: - return list(self._secondary_labels) - - def setSecondaryLabels(self, labels: List[str]): - self._secondary_labels = list(labels) - - def isShiftKey(self) -> bool: - return self._type == Key.KEY_TYPE_SHIFT - - def isAltKey(self) -> bool: - return self._type == Key.KEY_TYPE_ALT - - def isCtrlKey(self) -> bool: - return self._type == Key.KEY_TYPE_CTRL - - def isFunctionKey(self) -> bool: - return self._type == Key.KEY_TYPE_FUNCTION - - def toString(self) -> str: - return f"Key(code={self._key_code}, label={self._label}, type={self._type})" - - -# ═══════════════════════════════════════════════════════════════ -# Keyboard -# ═══════════════════════════════════════════════════════════════ - -class Keyboard(Stub): - """Keyboard — 键盘布局""" - - KEYBOARD_TYPE_ALPHA = 0 - KEYBOARD_TYPE_NUMERIC = 1 - KEYBOARD_TYPE_PHONE = 2 - KEYBOARD_TYPE_SYMBOLS = 3 - KEYBOARD_TYPE_EMOJI = 4 - KEYBOARD_TYPE_URL = 5 - KEYBOARD_TYPE_EMAIL = 6 - KEYBOARD_TYPE_IME = 7 - - EDGE_LEFT = 1 - EDGE_RIGHT = 2 - EDGE_TOP = 4 - EDGE_BOTTOM = 8 - - def __init__(self, context: Any = None, layout_id: int = 0, - keyboard_type: int = KEYBOARD_TYPE_ALPHA): - super().__init__() - self._context = context - self._layout_id = layout_id - self._keyboard_type = keyboard_type - self._keys: List[Key] = [] - self._shift_keys: List[Key] = [] - self._alt_keys: List[Key] = [] - self._ctrl_keys: List[Key] = [] - self._function_keys: List[Key] = [] - self._modifier_keys: List[Key] = [] - self._key_width: int = 0 - self._key_height: int = 0 - self._horizontal_gap: int = 0 - self._vertical_gap: int = 0 - self._default_row_height: int = 0 - self._rows: int = 0 - self._columns: int = 0 - self._label: str = "" - self._is_shifted: bool = False - self._is_alt: bool = False - self._is_ctrl: bool = False - self._is_function: bool = False - - def getKeys(self) -> List[Key]: - return list(self._keys) - - def setKeys(self, keys: List[Key]): - self._keys = list(keys) - - def addKey(self, key: Key): - self._keys.append(key) - - def removeKey(self, key: Key): - self._keys = [k for k in self._keys if k is not key] - - def getKeyByCode(self, key_code: int) -> Optional[Key]: - for k in self._keys: - if k.getKeyCode() == key_code: - return k - return None - - def getShiftKeys(self) -> List[Key]: - return list(self._shift_keys) - - def getAltKeys(self) -> List[Key]: - return list(self._alt_keys) - - def getModifierKeys(self) -> List[Key]: - return list(self._modifier_keys) - - def getKeyWidth(self) -> int: - return self._key_width - - def setKeyWidth(self, width: int): - self._key_width = width - - def getKeyHeight(self) -> int: - return self._key_height - - def setKeyHeight(self, height: int): - self._key_height = height - - def getHorizontalGap(self) -> int: - return self._horizontal_gap - - def setHorizontalGap(self, gap: int): - self._horizontal_gap = gap - - def getVerticalGap(self) -> int: - return self._vertical_gap - - def setVerticalGap(self, gap: int): - self._vertical_gap = gap - - def getRows(self) -> int: - return self._rows - - def setRows(self, rows: int): - self._rows = rows - - def getColumns(self) -> int: - return self._columns - - def setColumns(self, cols: int): - self._columns = cols - - def getLabel(self) -> str: - return self._label - - def setLabel(self, label: str): - self._label = label - - def isShifted(self) -> bool: - return self._is_shifted - - def setShifted(self, shifted: bool): - self._is_shifted = shifted - - def getKeyboardType(self) -> int: - return self._keyboard_type - - def setKeyboardType(self, type_: int): - self._keyboard_type = type_ - - def toString(self) -> str: - return f"Keyboard(keys={len(self._keys)}, type={self._keyboard_type})" - - -# ═══════════════════════════════════════════════════════════════ -# KeyboardView -# ═══════════════════════════════════════════════════════════════ - -class KeyboardView(Stub): - """KeyboardView — 键盘视图""" - - def __init__(self, context: Any = None, keyboard: Keyboard = None): - super().__init__() - self._context = context - self._keyboard = keyboard - self._is_visible: bool = False - self._is_preview_enabled: bool = True - self._is_proximity_correction_enabled: bool = True - self._key_text_size: float = 20.0 - self._key_text_color: int = 0xFF000000 - self._background_color: int = 0xFFEEEEEE - self._preview_background_color: int = 0xFFCCCCCC - self._on_key_listener: Optional[Callable] = None - self._on_key_action_listener: Optional[Callable] = None - self._pressed_keys: Set[Key] = set() - self._keyboard_width: int = 0 - self._keyboard_height: int = 0 - - def setKeyboard(self, keyboard: Keyboard): - self._keyboard = keyboard - - def getKeyboard(self) -> Optional[Keyboard]: - return self._keyboard - - def setVisibility(self, visible: bool): - self._is_visible = visible - - def isVisible(self) -> bool: - return self._is_visible - - def setPreviewEnabled(self, enabled: bool): - self._is_preview_enabled = enabled - - def isPreviewEnabled(self) -> bool: - return self._is_preview_enabled - - def setProximityCorrectionEnabled(self, enabled: bool): - self._is_proximity_correction_enabled = enabled - - def isProximityCorrectionEnabled(self) -> bool: - return self._is_proximity_correction_enabled - - def setKeyTextSize(self, size: float): - self._key_text_size = size - - def getKeyTextSize(self) -> float: - return self._key_text_size - - def setKeyTextColor(self, color: int): - self._key_text_color = color - - def getKeyTextColor(self) -> int: - return self._key_text_color - - def setBackgroundColor(self, color: int): - self._background_color = color - - def getBackgroundColor(self) -> int: - return self._background_color - - def setOnKeyboardActionListener(self, listener: Callable): - self._on_key_listener = listener - - def setOnKeyListener(self, listener: Callable): - self._on_key_action_listener = listener - - def simulateKeyPress(self, key: Key): - """模拟按键按下""" - self._pressed_keys.add(key) - if self._on_key_listener: - self._on_key_listener.onKey(key.getKeyCode(), key) - - def simulateKeyRelease(self, key: Key): - """模拟按键释放""" - self._pressed_keys.discard(key) - - def isKeyPressed(self, key: Key) -> bool: - return key in self._pressed_keys - - def invalidate(self): - """重绘键盘""" - pass - - def setKeyboardWidth(self, width: int): - self._keyboard_width = width - - def setKeyboardHeight(self, height: int): - self._keyboard_height = height - - def getKeyboardWidth(self) -> int: - return self._keyboard_width - - def getKeyboardHeight(self) -> int: - return self._keyboard_height - - def clampY(self, y: int) -> int: - return max(0, min(y, self._keyboard_height)) - - def toString(self) -> str: - return f"KeyboardView(visible={self._is_visible})" - - -# ═══════════════════════════════════════════════════════════════ -# AbstractInputMethodService -# ═══════════════════════════════════════════════════════════════ - -class AbstractInputMethodService(Stub): - """AbstractInputMethodService — 抽象输入法服务基类""" - - SERVICE_INTERFACE = "android.inputmethodservice.AbstractInputMethodService" - SERVICE_META_DATA = "android.inputmethod" - - def __init__(self): - super().__init__() - self._keyboard_view: Optional[KeyboardView] = None - self._keyboard: Optional[Keyboard] = None - self._is_fullscreen: bool = False - self._is_extracted_view_visible: bool = False - self._is_input_view_visible: bool = False - self._is_candidates_view_visible: bool = False - self._input_view_started: bool = False - - def onCreate(self): - pass - - def onDestroy(self): - self._keyboard = None - self._keyboard_view = None - - def onCreateInputView(self) -> Optional[KeyboardView]: - return self._keyboard_view - - def onCreateCandidatesView(self) -> Any: - return None - - def onStartInputView(self, info: Any, restarting: bool): - self._is_input_view_visible = True - self._input_view_started = True - - def onFinishInputView(self, finishing_input: bool): - self._is_input_view_visible = False - - def onStartCandidatesView(self, info: Any, restarting: bool): - self._is_candidates_view_visible = True - - def onFinishCandidatesView(self, finishing_input: bool): - self._is_candidates_view_visible = False - - def onStartInput(self, input_connection: Any, editor_info: Any, restarting: bool): - pass - - def onFinishInput(self): - pass - - def setKeyboard(self, keyboard: Keyboard): - self._keyboard = keyboard - if self._keyboard_view: - self._keyboard_view.setKeyboard(keyboard) - - def getKeyboard(self) -> Optional[Keyboard]: - return self._keyboard - - def setKeyboardView(self, keyboard_view: KeyboardView): - self._keyboard_view = keyboard_view - - def getKeyboardView(self) -> Optional[KeyboardView]: - return self._keyboard_view - - def sendKeyChar(self, char: str): - pass - - def sendDownUpKeyEvents(self, key_event_code: int): - pass - - def switchToNextKeyboard(self): - pass - - def switchToPreviousKeyboard(self): - pass - - def hideWindow(self): - self._is_input_view_visible = False - self._is_candidates_view_visible = False - - def showWindow(self, show_flags: int): - pass - - def requestHideSelf(self, flags: int): - pass - - def requestShowSelf(self, flags: int): - pass - - def isFullscreen(self) -> bool: - return self._is_fullscreen - - def setFullscreen(self, fullscreen: bool): - self._is_fullscreen = fullscreen - - def toString(self) -> str: - return f"AbstractInputMethodService(fullscreen={self._is_fullscreen})" - - -# ═══════════════════════════════════════════════════════════════ -# InputMethodService -# ═══════════════════════════════════════════════════════════════ - -class InputMethodService(AbstractInputMethodService): - """InputMethodService — 输入法服务""" - - BACK_DISPOSITION_DEFAULT = 0 - BACK_DISPOSITION_WILL_DISMISS = 1 - BACK_DISPOSITION_WILL_NOT_DISMISS = 2 - - SHOW_INPUT_METHOD_PICKER = 1 - SHOW_EXPLICIT = 2 - SHOW_FORCED = 4 - - HIDE_EXPLICIT = 1 - HIDE_NOT_ALWAYS = 2 - HIDE_IMPLICIT_ONLY = 4 - HIDE_BY_COMMAND = 8 - - KEYGUARD_DISABLE_NONE = 0 - KEYGUARD_DISABLE_FEATURES_NONE = 0 - KEYGUARD_DISABLE_FEATURES_ALL = 0x7FFFFFFF - - def __init__(self): - super().__init__() - self._back_disposition: int = InputMethodService.BACK_DISPOSITION_DEFAULT - self._is_input_view_shown: bool = False - self._is_candidates_view_shown: bool = False - self._extract_view: Any = None - self._input_connection: Any = None - self._current_input_editor_info: Any = None - self._settings_observer: Any = None - - def onInitializeInterface(self): - pass - - def onBindInput(self): - pass - - def onUnbindInput(self): - pass - - def onStartInput(self, attribute: Any, restarting: bool): - super().onStartInput(attribute, attribute, restarting) - - def onFinishInput(self): - super().onFinishInput() - - def onDisplayCompletions(self, completions: List[Any]): - pass - - def onUpdateSelection(self, old_sel_start: int, old_sel_end: int, - new_sel_start: int, new_sel_end: int, - candidates_start: int, candidates_end: int): - pass - - def onUpdateExtractedText(self, token: int, extracted_text: Any): - pass - - def onExtractedTextClicked(self): - pass - - def onExtractingInputChanged(self, editor_info: Any): - pass - - def onConfigureWindow(self, win: Any, is_fullscreen: bool, is_obscured: bool): - pass - - def onEvaluateFullscreenMode(self) -> bool: - return self._is_fullscreen - - def onEvaluateInputViewShown(self) -> bool: - return True - - def onComputeInsets(self, out_insets: Any): - pass - - def onKeyDown(self, key_code: int, event: Any) -> bool: - return False - - def onKeyUp(self, key_code: int, event: Any) -> bool: - return False - - def onKeyLongPress(self, key_code: int, event: Any) -> bool: - return False - - def onKeyMultiple(self, key_code: int, count: int, event: Any) -> bool: - return False - - def onTrackballEvent(self, event: Any) -> bool: - return False - - def onGenericMotionEvent(self, event: Any) -> bool: - return False - - def onWindowShown(self): - pass - - def onWindowHidden(self): - pass - - def setBackDisposition(self, disposition: int): - self._back_disposition = disposition - - def getBackDisposition(self) -> int: - return self._back_disposition - - def switchInputMethod(self, id: str): - pass - - def changeInputMethodSubtype(self, subtype: Any): - pass - - def getCurrentInputBinding(self) -> Any: - return None - - def getCurrentInputConnection(self) -> Any: - return self._input_connection - - def getCurrentInputEditorInfo(self) -> Any: - return self._current_input_editor_info - - def getCurrentInputStarted(self) -> bool: - return self._input_view_started - - def getLayoutInflater(self) -> Any: - return None - - def getWindow(self) -> Any: - return None - - def isInputViewShown(self) -> bool: - return self._is_input_view_shown - - def setInputView(self, view: Any): - self._is_input_view_shown = view is not None - - def showStatusIcon(self, icon_id: int): - pass - - def hideStatusIcon(self): - pass - - def sendDefaultEditorAction(self, from_enter_key: bool): - pass - - def sendDownUpKeyEvents(self, key_event_code: int): - pass - - def sendKeyChar(self, char: str): - pass - - def updateFullscreenMode(self): - pass - - def updateInputViewShown(self): - pass - - def exposeCompletions(self, completions: List[Any]): - self.onDisplayCompletions(completions) - - def toString(self) -> str: - return f"InputMethodService(backDisposition={self._back_disposition})" - - -# ═══════════════════════════════════════════════════════════════ -# InputMethodServiceKeyboardHelper -# ═══════════════════════════════════════════════════════════════ - -class InputMethodServiceKeyboardHelper(Stub): - """InputMethodServiceKeyboardHelper — 输入法键盘辅助类""" - - @staticmethod - def createDefaultQWERTYKeyboard(context: Any) -> Keyboard: - kb = Keyboard(context, 0, Keyboard.KEYBOARD_TYPE_ALPHA) - row1_chars = "qwertyuiop" - row2_chars = "asdfghjkl" - row3_chars = "zxcvbnm" - for i, c in enumerate(row1_chars): - k = Key(ord(c.upper()), c, Key.KEY_TYPE_NORMAL) - k.setWidth(50) - k.setHeight(60) - k.setX(i * 55) - kb.addKey(k) - shift_key = Key(KEYCODE_SHIFT_LEFT, "Shift", Key.KEY_TYPE_SHIFT) - shift_key.setWidth(70) - shift_key.setHeight(60) - kb.addKey(shift_key) - space_key = Key(KEYCODE_SPACE, " ", Key.KEY_TYPE_SPACE) - space_key.setWidth(200) - space_key.setHeight(60) - kb.addKey(space_key) - del_key = Key(KEYCODE_DEL, "Del", Key.KEY_TYPE_DELETE) - del_key.setWidth(70) - del_key.setHeight(60) - kb.addKey(del_key) - enter_key = Key(KEYCODE_ENTER, "Enter", Key.KEY_TYPE_DONE) - enter_key.setWidth(80) - enter_key.setHeight(60) - kb.addKey(enter_key) - return kb - - @staticmethod - def createNumericKeyboard(context: Any) -> Keyboard: - kb = Keyboard(context, 0, Keyboard.KEYBOARD_TYPE_NUMERIC) - for i in range(10): - k = Key(KEYCODE_0 + i, str(i), Key.KEY_TYPE_NORMAL) - k.setWidth(60) - k.setHeight(60) - kb.addKey(k) - return kb - - @staticmethod - def createPhoneKeyboard(context: Any) -> Keyboard: - kb = Keyboard(context, 0, Keyboard.KEYBOARD_TYPE_PHONE) - layout = [["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"], ["*", "0", "#"]] - for row_idx, row in enumerate(layout): - for col_idx, c in enumerate(row): - k = Key(KEYCODE_0 + int(c) if c.isdigit() else ( - KEYCODE_STAR if c == "*" else KEYCODE_POUND), c, Key.KEY_TYPE_NORMAL) - k.setWidth(80) - k.setHeight(60) - k.setX(col_idx * 85) - k.setY(row_idx * 65) - kb.addKey(k) - return kb - - @staticmethod - def createSymbolKeyboard(context: Any) -> Keyboard: - kb = Keyboard(context, 0, Keyboard.KEYBOARD_TYPE_SYMBOLS) - symbols = "~!@#$%^&*()_+-=[]{}|;':\",./<>?" - for i, c in enumerate(symbols): - k = Key(KEYCODE_UNKNOWN, c, Key.KEY_TYPE_NORMAL) - k.setWidth(45) - k.setHeight(55) - kb.addKey(k) - return kb - - @staticmethod - def createKeyboardView(context: Any, keyboard: Keyboard) -> KeyboardView: - return KeyboardView(context, keyboard) \ No newline at end of file diff --git a/android_pyapi/android/location.py b/android_pyapi/android/location.py deleted file mode 100644 index fa791f7..0000000 --- a/android_pyapi/android/location.py +++ /dev/null @@ -1,1266 +0,0 @@ -""" -Android Location 组件 — Location, LocationManager, Geocoder 等 -方法签名完全对齐 Android SDK -""" - -import os -import sys -import math -import threading -from typing import Any, Dict, List, Optional, Set, Tuple, Union -from datetime import datetime - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) - -try: - from .._linux_impl import IS_LINUX, LinuxLocation - _USE_LINUX = IS_LINUX -except ImportError: - _USE_LINUX = False - - -# ═══════════════════════════════════════════════════════════════ -# Location -# ═══════════════════════════════════════════════════════════════ - -class Location(Stub): - """Location — 地理位置信息""" - - FORMAT_DEGREES = 0 - FORMAT_MINUTES = 1 - FORMAT_SECONDS = 2 - - def __init__(self, provider: str = ""): - super().__init__() - self._provider = provider - self._latitude: float = 0.0 - self._longitude: float = 0.0 - self._accuracy: float = 0.0 - self._time: int = 0 - self._elapsed_realtime_nanos: int = 0 - self._altitude: float = 0.0 - self._speed: float = 0.0 - self._bearing: float = 0.0 - self._extras: Optional[Dict[str, Any]] = None - - def setLatitude(self, latitude: float): - self._latitude = latitude - - def getLatitude(self) -> float: - return self._latitude - - def setLongitude(self, longitude: float): - self._longitude = longitude - - def getLongitude(self) -> float: - return self._longitude - - def setAccuracy(self, accuracy: float): - self._accuracy = accuracy - - def getAccuracy(self) -> float: - return self._accuracy - - def setTime(self, time: int): - self._time = time - - def getTime(self) -> int: - return self._time - - def setElapsedRealtimeNanos(self, nanos: int): - self._elapsed_realtime_nanos = nanos - - def getElapsedRealtimeNanos(self) -> int: - return self._elapsed_realtime_nanos - - def setAltitude(self, altitude: float): - self._altitude = altitude - - def getAltitude(self) -> float: - return self._altitude - - def setSpeed(self, speed: float): - self._speed = speed - - def getSpeed(self) -> float: - return self._speed - - def setBearing(self, bearing: float): - self._bearing = bearing - - def getBearing(self) -> float: - return self._bearing - - def setProvider(self, provider: str): - self._provider = provider - - def getProvider(self) -> str: - return self._provider - - def distanceTo(self, dest: "Location") -> float: - R = 6371000 - lat1 = math.radians(self._latitude) - lat2 = math.radians(dest._latitude) - dlat = math.radians(dest._latitude - self._latitude) - dlon = math.radians(dest._longitude - self._longitude) - a = (math.sin(dlat / 2) ** 2 + - math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2) - c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) - return R * c - - def bearingTo(self, dest: "Location") -> float: - lat1 = math.radians(self._latitude) - lat2 = math.radians(dest._latitude) - dlon = math.radians(dest._longitude - self._longitude) - y = math.sin(dlon) * math.cos(lat2) - x = (math.cos(lat1) * math.sin(lat2) - - math.sin(lat1) * math.cos(lat2) * math.cos(dlon)) - return (math.degrees(math.atan2(y, x)) + 360) % 360 - - @staticmethod - def convert(coordinate: Union[str, float], output_type: int = FORMAT_DEGREES) -> Union[str, float]: - if isinstance(coordinate, str): - return Location._parse_coordinate(coordinate) - return Location._format_coordinate(coordinate, output_type) - - @staticmethod - def _parse_coordinate(coord_str: str) -> float: - coord_str = coord_str.strip() - try: - return float(coord_str) - except ValueError: - pass - parts = coord_str.replace(",", ".").split() - if len(parts) >= 3: - try: - d = float(parts[0]) - m = float(parts[1]) - s = float(parts[2].rstrip("NSEW")) - result = d + m / 60.0 + s / 3600.0 - if coord_str.rstrip()[-1:] in ("S", "W"): - result = -result - return result - except (ValueError, IndexError): - return 0.0 - return 0.0 - - @staticmethod - def _format_coordinate(coord: float, output_type: int) -> str: - if output_type == Location.FORMAT_DEGREES: - return f"{coord:.6f}" - elif output_type == Location.FORMAT_MINUTES: - deg = int(coord) - minutes = (coord - deg) * 60 - return f"{deg}:{minutes:.3f}" - else: - deg = int(coord) - minutes = (coord - deg) * 60 - int_min = int(minutes) - secs = (minutes - int_min) * 60 - return f"{deg}:{int_min}:{secs:.2f}" - - def getExtras(self) -> Optional[Dict[str, Any]]: - return self._extras - - def setExtras(self, extras: Dict[str, Any]): - self._extras = extras - - def toString(self) -> str: - return (f"Location[provider={self._provider}, " - f"{self._latitude:.6f},{self._longitude:.6f}, " - f"acc={self._accuracy:.1f}]") - - def __repr__(self): - return self.toString() - - -# ═══════════════════════════════════════════════════════════════ -# Criteria -# ═══════════════════════════════════════════════════════════════ - -class Criteria(Stub): - """Criteria — 位置提供者筛选条件""" - - ACCURACY_FINE = 1 - ACCURACY_COARSE = 2 - POWER_LOW = 1 - POWER_MEDIUM = 2 - POWER_HIGH = 3 - NO_REQUIREMENT = 0 - - def __init__(self): - super().__init__() - self._accuracy = self.NO_REQUIREMENT - self._power_requirement = self.NO_REQUIREMENT - self._altitude_required = False - self._bearing_required = False - self._speed_required = False - self._cost_allowed = False - - def setAccuracy(self, accuracy: int): - self._accuracy = accuracy - - def getAccuracy(self) -> int: - return self._accuracy - - def setPowerRequirement(self, power: int): - self._power_requirement = power - - def getPowerRequirement(self) -> int: - return self._power_requirement - - def setAltitudeRequired(self, required: bool): - self._altitude_required = required - - def isAltitudeRequired(self) -> bool: - return self._altitude_required - - def setBearingRequired(self, required: bool): - self._bearing_required = required - - def isBearingRequired(self) -> bool: - return self._bearing_required - - def setSpeedRequired(self, required: bool): - self._speed_required = required - - def isSpeedRequired(self) -> bool: - return self._speed_required - - def setCostAllowed(self, allowed: bool): - self._cost_allowed = allowed - - def isCostAllowed(self) -> bool: - return self._cost_allowed - - def toString(self) -> str: - return f"Criteria(acc={self._accuracy}, power={self._power_requirement})" - - -# ═══════════════════════════════════════════════════════════════ -# LocationProvider -# ═══════════════════════════════════════════════════════════════ - -class LocationProvider(Stub): - """LocationProvider — 位置提供者""" - - AVAILABLE = 2 - TEMPORARILY_UNAVAILABLE = 1 - OUT_OF_SERVICE = 0 - - def __init__(self, name: str = ""): - super().__init__() - self._name = name - self._accuracy = Criteria.NO_REQUIREMENT - self._power_requirement = Criteria.NO_REQUIREMENT - self._has_monetary_cost = False - self._requires_cell = False - self._requires_network = False - self._requires_satellite = False - self._supports_altitude = False - self._supports_bearing = False - self._supports_speed = False - - def getName(self) -> str: - return self._name - - def getAccuracy(self) -> int: - return self._accuracy - - def getPowerRequirement(self) -> int: - return self._power_requirement - - def hasMonetaryCost(self) -> bool: - return self._has_monetary_cost - - def requiresCell(self) -> bool: - return self._requires_cell - - def requiresNetwork(self) -> bool: - return self._requires_network - - def requiresSatellite(self) -> bool: - return self._requires_satellite - - def supportsAltitude(self) -> bool: - return self._supports_altitude - - def supportsBearing(self) -> bool: - return self._supports_bearing - - def supportsSpeed(self) -> bool: - return self._supports_speed - - def getRequirements(self) -> int: - req = 0 - if self._requires_cell: - req |= 1 - if self._requires_network: - req |= 2 - if self._requires_satellite: - req |= 4 - return req - - def toString(self) -> str: - return f"LocationProvider(name={self._name})" - - -# ═══════════════════════════════════════════════════════════════ -# LocationListener -# ═══════════════════════════════════════════════════════════════ - -class LocationListener(Stub): - """LocationListener — 位置变化监听器""" - - def onLocationChanged(self, location: Location): - pass - - def onStatusChanged(self, provider: str, status: int, extras: Any): - pass - - def onProviderEnabled(self, provider: str): - pass - - def onProviderDisabled(self, provider: str): - pass - - -# ═══════════════════════════════════════════════════════════════ -# GpsSatellite -# ═══════════════════════════════════════════════════════════════ - -class GpsSatellite(Stub): - """GpsSatellite — GPS 卫星信息""" - - def __init__(self): - super().__init__() - self._prn: int = 0 - self._snr: float = 0.0 - self._elevation: float = 0.0 - self._azimuth: float = 0.0 - self._has_almanac: bool = False - self._has_ephemeris: bool = False - self._used_in_fix: bool = False - - def getPrn(self) -> int: - return self._prn - - def getSnr(self) -> float: - return self._snr - - def getElevation(self) -> float: - return self._elevation - - def getAzimuth(self) -> float: - return self._azimuth - - def hasAlmanac(self) -> bool: - return self._has_almanac - - def hasEphemeris(self) -> bool: - return self._has_ephemeris - - def usedInFix(self) -> bool: - return self._used_in_fix - - def toString(self) -> str: - return f"GpsSatellite(prn={self._prn}, snr={self._snr})" - - -# ═══════════════════════════════════════════════════════════════ -# GpsStatus -# ═══════════════════════════════════════════════════════════════ - -class GpsStatus(Stub): - """GpsStatus — GPS 状态""" - - GPS_EVENT_STARTED = 1 - GPS_EVENT_STOPPED = 2 - GPS_EVENT_FIRST_FIX = 3 - GPS_EVENT_SATELLITE_STATUS = 4 - - def __init__(self): - super().__init__() - self._satellites: List[GpsSatellite] = [] - self._max_satellites: int = 32 - self._time_to_first_fix: int = 0 - - def getSatellites(self) -> List[GpsSatellite]: - return list(self._satellites) - - def getMaxSatellites(self) -> int: - return self._max_satellites - - def getTimeToFirstFix(self) -> int: - return self._time_to_first_fix - - def setTimeToFirstFix(self, ttf: int): - self._time_to_first_fix = ttf - - -class GpsStatusListener(Stub): - """GpsStatusListener — GPS 状态监听器""" - - def onGpsStatusChanged(self, event: int): - pass - - -# ═══════════════════════════════════════════════════════════════ -# GnssClock / GnssMeasurement / GnssMeasurementsEvent / GnssNavigationMessage -# ═══════════════════════════════════════════════════════════════ - -class GnssClock(Stub): - """GnssClock — GNSS 时钟""" - - def __init__(self): - super().__init__() - self._time_nanos: int = 0 - self._time_uncertainty_nanos: float = 0.0 - self._leap_second: int = 0 - self._full_bias_nanos: int = 0 - self._bias_nanos: float = 0.0 - self._bias_uncertainty_nanos: float = 0.0 - self._drift_nanos_per_sec: float = 0.0 - self._drift_uncertainty_nanos_per_sec: float = 0.0 - self._hardware_clock_discontinuity_count: int = 0 - self._has_leap_second: bool = False - self._has_full_bias_nanos: bool = False - self._has_bias_nanos: bool = False - self._has_bias_uncertainty_nanos: bool = False - self._has_drift_nanos_per_sec: bool = False - self._has_drift_uncertainty_nanos_per_sec: bool = False - - def getTimeNanos(self) -> int: - return self._time_nanos - - def getTimeUncertaintyNanos(self) -> float: - return self._time_uncertainty_nanos - - def getLeapSecond(self) -> int: - return self._leap_second - - def getFullBiasNanos(self) -> int: - return self._full_bias_nanos - - def getBiasNanos(self) -> float: - return self._bias_nanos - - def getBiasUncertaintyNanos(self) -> float: - return self._bias_uncertainty_nanos - - def getDriftNanosPerSec(self) -> float: - return self._drift_nanos_per_sec - - def getDriftUncertaintyNanosPerSec(self) -> float: - return self._drift_uncertainty_nanos_per_sec - - def getHardwareClockDiscontinuityCount(self) -> int: - return self._hardware_clock_discontinuity_count - - -class GnssMeasurement(Stub): - """GnssMeasurement — GNSS 测量值""" - - STATE_UNKNOWN = 0 - STATE_CODE_LOCK = 1 - STATE_BIT_SYNC = 2 - STATE_SUBFRAME_SYNC = 3 - STATE_TOW_DECODED = 4 - STATE_MSEC_AMBIGUOUS = 5 - - def __init__(self): - super().__init__() - self._svid: int = 0 - self._constellation_type: int = 0 - self._time_offset_nanos: float = 0.0 - self._state: int = 0 - self._received_gps_tow_nanos: int = 0 - self._received_gps_tow_uncertainty_nanos: int = 0 - self._carrier_frequency_hz: float = 0.0 - self._carrier_phase: float = 0.0 - self._carrier_phase_uncertainty: float = 0.0 - self._multipath_indicator: int = 0 - self._snr_in_db: float = 0.0 - self._constellation_type_val: int = 0 - - def getSvid(self) -> int: - return self._svid - - def getConstellationType(self) -> int: - return self._constellation_type - - def getTimeOffsetNanos(self) -> float: - return self._time_offset_nanos - - def getState(self) -> int: - return self._state - - def getReceivedGpsTowNanos(self) -> int: - return self._received_gps_tow_nanos - - def getReceivedGpsTowUncertaintyNanos(self) -> int: - return self._received_gps_tow_uncertainty_nanos - - def getCarrierFrequencyHz(self) -> float: - return self._carrier_frequency_hz - - def getCarrierPhase(self) -> float: - return self._carrier_phase - - def getCarrierPhaseUncertainty(self) -> float: - return self._carrier_phase_uncertainty - - def getMultipathIndicator(self) -> int: - return self._multipath_indicator - - def getSnrInDb(self) -> float: - return self._snr_in_db - - -class GnssMeasurementsEvent(Stub): - """GnssMeasurementsEvent — GNSS 测量事件""" - - def __init__(self, clock: Optional[GnssClock] = None, - measurements: Optional[List[GnssMeasurement]] = None): - super().__init__() - self._clock = clock or GnssClock() - self._measurements = measurements or [] - - def getClock(self) -> GnssClock: - return self._clock - - def getMeasurements(self) -> List[GnssMeasurement]: - return list(self._measurements) - - -class GnssNavigationMessage(Stub): - """GnssNavigationMessage — GNSS 导航消息""" - - STATUS_PARITY_PASSED = 1 - STATUS_PARITY_REBUILT = 2 - TYPE_GPS_L1CA = 256 - TYPE_GPS_L2CA = 512 - TYPE_GPS_L5 = 768 - TYPE_GLO_L1CA = 1024 - TYPE_GLO_L2CA = 1280 - TYPE_BDS_D1 = 1792 - TYPE_BDS_D2 = 2048 - TYPE_GAL_I = 2560 - TYPE_GAL_F = 2816 - TYPE_QZS_L1CA = 3072 - TYPE_QZS_L5 = 3328 - - def __init__(self): - super().__init__() - self._type: int = 0 - self._svid: int = 0 - self._message_id: int = 0 - self._submessage_id: int = 0 - self._data: bytes = b"" - self._status: int = 0 - - def getType(self) -> int: - return self._type - - def getSvid(self) -> int: - return self._svid - - def getMessageId(self) -> int: - return self._message_id - - def getSubmessageId(self) -> int: - return self._submessage_id - - def getData(self) -> bytes: - return self._data - - def getStatus(self) -> int: - return self._status - - -# ═══════════════════════════════════════════════════════════════ -# GnssStatus -# ═══════════════════════════════════════════════════════════════ - -class GnssStatus(Stub): - """GnssStatus — GNSS 卫星状态""" - - GNSS_CONSTELLATION_GPS = 1 - GNSS_CONSTELLATION_SBAS = 2 - GNSS_CONSTELLATION_GLONASS = 3 - CONSTELLATION_QZSS = 4 - CONSTELLATION_BEIDOU = 5 - CONSTELLATION_GALILEO = 6 - CONSTELLATION_IRNSS = 7 - - def __init__(self): - super().__init__() - self._satellites: List[Dict[str, Any]] = [] - - def getSatelliteCount(self) -> int: - return len(self._satellites) - - def getSvid(self, index: int) -> int: - if 0 <= index < len(self._satellites): - return self._satellites[index].get("svid", 0) - return 0 - - def getConstellationType(self, index: int) -> int: - if 0 <= index < len(self._satellites): - return self._satellites[index].get("constellation", 0) - return 0 - - def getCarrierFrequencyHz(self, index: int) -> float: - if 0 <= index < len(self._satellites): - return self._satellites[index].get("carrier_freq", 0.0) - return 0.0 - - def getBasebandCn0DbHz(self, index: int) -> float: - """返回指定卫星的基带载噪比(dB-Hz)。""" - if 0 <= index < len(self._satellites): - return self._satellites[index].get("baseband_cn0", 0.0) - return 0.0 - - def getCn0DbHz(self, index: int) -> float: - if 0 <= index < len(self._satellites): - return self._satellites[index].get("cn0", 0.0) - return 0.0 - - def getAzimuthDegrees(self, index: int) -> float: - if 0 <= index < len(self._satellites): - return self._satellites[index].get("azimuth", 0.0) - return 0.0 - - def getElevationDegrees(self, index: int) -> float: - if 0 <= index < len(self._satellites): - return self._satellites[index].get("elevation", 0.0) - return 0.0 - - def hasAlmanac(self, index: int) -> bool: - if 0 <= index < len(self._satellites): - return self._satellites[index].get("has_almanac", False) - return False - - def hasEphemeris(self, index: int) -> bool: - if 0 <= index < len(self._satellites): - return self._satellites[index].get("has_ephemeris", False) - return False - - def usedInFix(self, index: int) -> bool: - if 0 <= index < len(self._satellites): - return self._satellites[index].get("used_in_fix", False) - return False - - -# ═══════════════════════════════════════════════════════════════ -# LocationRequest -# ═══════════════════════════════════════════════════════════════ - -class LocationRequest(Stub): - """LocationRequest — 位置请求""" - - QUALITY_HIGH_ACCURACY = 100 - QUALITY_BALANCED_POWER_ACCURACY = 102 - QUALITY_LOW_POWER = 104 - QUALITY_PASSIVE = 105 - - PRIORITY_HIGH_ACCURACY = 100 - PRIORITY_BALANCED_POWER_ACCURACY = 102 - PRIORITY_LOW_POWER = 104 - PRIORITY_NO_POWER = 105 - - def __init__(self): - super().__init__() - self._quality: int = self.QUALITY_BALANCED_POWER_ACCURACY - self._interval: int = 60000 - self._fastest_interval: int = 30000 - self._smallest_displacement: float = 0.0 - self._max_wait_time: int = 0 - self._duration_millis: int = 0 - self._max_updates: int = 0 # 0 means unlimited - self._priority: int = self.PRIORITY_BALANCED_POWER_ACCURACY - - def setQuality(self, quality: int): - self._quality = quality - - def getQuality(self) -> int: - return self._quality - - def setInterval(self, interval: int): - self._interval = interval - - def getInterval(self) -> int: - return self._interval - - def setFastestInterval(self, interval: int): - self._fastest_interval = interval - - def getFastestInterval(self) -> int: - return self._fastest_interval - - def setSmallestDisplacement(self, displacement: float): - self._smallest_displacement = displacement - - def getSmallestDisplacement(self) -> float: - return self._smallest_displacement - - def setMaxWaitTime(self, time: int): - self._max_wait_time = time - - def getMaxWaitTime(self) -> int: - return self._max_wait_time - - def setDurationMillis(self, duration: int): - self._duration_millis = duration - - def getDurationMillis(self) -> int: - return self._duration_millis - - def setMaxUpdates(self, updates: int): - self._max_updates = updates - - def getMaxUpdates(self) -> int: - return self._max_updates - - def setPriority(self, priority: int): - self._priority = priority - - def getPriority(self) -> int: - return self._priority - - def toString(self) -> str: - return f"LocationRequest(interval={self._interval}, priority={self._priority})" - - -# ═══════════════════════════════════════════════════════════════ -# LocationProviderProperties -# ═══════════════════════════════════════════════════════════════ - -class LocationProviderProperties(Stub): - """LocationProviderProperties — 提供者属性""" - - def __init__(self, requires_network: bool = False, - requires_satellite: bool = False, - requires_cell: bool = False, - has_monetary_cost: bool = False, - supports_altitude: bool = False, - supports_speed: bool = False, - supports_bearing: bool = False, - power_requirement: int = Criteria.NO_REQUIREMENT, - accuracy: int = Criteria.NO_REQUIREMENT): - super().__init__() - self.requiresNetwork = requires_network - self.requiresSatellite = requires_satellite - self.requiresCell = requires_cell - self.hasMonetaryCost = has_monetary_cost - self.supportsAltitude = supports_altitude - self.supportsSpeed = supports_speed - self.supportsBearing = supports_bearing - self.powerRequirement = power_requirement - self.accuracy = accuracy - - -# ═══════════════════════════════════════════════════════════════ -# SettingInjectorService -# ═══════════════════════════════════════════════════════════════ - -class SettingInjectorService(Stub): - """SettingInjectorService — 设置注入服务""" - - ACTION_INJECTED_SETTING = "android.location.InjectedSetting" - - def __init__(self, name: str = ""): - super().__init__() - self._name = name - - def onGetSummary(self) -> str: - return self._name - - def onGetEnabled(self) -> bool: - return True - - def toString(self) -> str: - return f"SettingInjectorService(name={self._name})" - - -# ═══════════════════════════════════════════════════════════════ -# Address -# ═══════════════════════════════════════════════════════════════ - -class Address(Stub): - """Address — 地理地址""" - - def __init__(self, locale: Any = None): - super().__init__() - self._address_lines: List[str] = [] - self._feature_name: str = "" - self._latitude: float = 0.0 - self._longitude: float = 0.0 - self._has_lat_lon: bool = False - self._admin_area: str = "" - self._sub_admin_area: str = "" - self._locality: str = "" - self._sub_locality: str = "" - self._premises: str = "" - self._thoroughfare: str = "" - self._sub_thoroughfare: str = "" - self._postal_code: str = "" - self._country_name: str = "" - self._country_code: str = "" - self._phone: str = "" - self._url: str = "" - self._extras: Optional[Dict[str, Any]] = None - - def getAddressLine(self, index: int) -> Optional[str]: - if 0 <= index < len(self._address_lines): - return self._address_lines[index] - return None - - def getMaxAddressLineIndex(self) -> int: - return max(0, len(self._address_lines) - 1) - - def getAdminArea(self) -> Optional[str]: - return self._admin_area or None - - def getSubAdminArea(self) -> Optional[str]: - return self._sub_admin_area or None - - def getLocality(self) -> Optional[str]: - return self._locality or None - - def getSubLocality(self) -> Optional[str]: - return self._sub_locality or None - - def getPremises(self) -> Optional[str]: - return self._premises or None - - def getThoroughfare(self) -> Optional[str]: - return self._thoroughfare or None - - def getSubThoroughfare(self) -> Optional[str]: - return self._sub_thoroughfare or None - - def getPostalCode(self) -> Optional[str]: - return self._postal_code or None - - def getCountryName(self) -> Optional[str]: - return self._country_name or None - - def getCountryCode(self) -> Optional[str]: - return self._country_code or None - - def getFeatureName(self) -> Optional[str]: - return self._feature_name or None - - def getLatitude(self) -> float: - return self._latitude - - def getLongitude(self) -> float: - return self._longitude - - def getPhone(self) -> Optional[str]: - return self._phone or None - - def getUrl(self) -> Optional[str]: - return self._url or None - - def setAddressLine(self, index: int, line: str): - while len(self._address_lines) <= index: - self._address_lines.append("") - self._address_lines[index] = line - - def setFeatureName(self, name: str): - self._feature_name = name - - def setAdminArea(self, area: str): - self._admin_area = area - - def setSubAdminArea(self, area: str): - self._sub_admin_area = area - - def setLocality(self, locality: str): - self._locality = locality - - def setSubLocality(self, sub_locality: str): - self._sub_locality = sub_locality - - def setPremises(self, premises: str): - self._premises = premises - - def setThoroughfare(self, thoroughfare: str): - self._thoroughfare = thoroughfare - - def setSubThoroughfare(self, sub_thoroughfare: str): - self._sub_thoroughfare = sub_thoroughfare - - def setPostalCode(self, postal_code: str): - self._postal_code = postal_code - - def setCountryName(self, country_name: str): - self._country_name = country_name - - def setCountryCode(self, country_code: str): - self._country_code = country_code - - def setPhone(self, phone: str): - self._phone = phone - - def setUrl(self, url: str): - self._url = url - - def setLatitude(self, latitude: float): - self._latitude = latitude - self._has_lat_lon = True - - def setLongitude(self, longitude: float): - self._longitude = longitude - self._has_lat_lon = True - - def hasLatitude(self) -> bool: - return self._has_lat_lon - - def hasLongitude(self) -> bool: - return self._has_lat_lon - - def toString(self) -> str: - return f"Address(locality={self._locality}, country={self._country_name})" - - -# ═══════════════════════════════════════════════════════════════ -# Geocoder -# ═══════════════════════════════════════════════════════════════ - -class Geocoder(Stub): - """Geocoder — 地理编码/逆地理编码""" - - # Nominatim (OpenStreetMap) 免费 API - _NOMINATIM_URL = "https://nominatim.openstreetmap.org" - _USER_AGENT = "Android-pyApi/1.0" - - def __init__(self, context: Any = None, locale: Any = None): - super().__init__() - self._context = context - self._locale = locale - - def _get_locale_str(self) -> str: - if self._locale is None: - return "en" - if hasattr(self._locale, "getLanguage"): - lang = self._locale.getLanguage() - country = self._locale.getCountry() - return f"{lang}-{country}" if country else lang - return str(self._locale) - - @staticmethod - def _http_get_json(url: str) -> Optional[Any]: - """使用 urllib 发送 GET 请求并解析 JSON。""" - try: - import urllib.request - import json - req = urllib.request.Request(url, headers={ - "User-Agent": Geocoder._USER_AGENT, - "Accept-Language": "en", - }) - with urllib.request.urlopen(req, timeout=10) as resp: - return json.loads(resp.read().decode("utf-8")) - except Exception: - return None - - def getFromLocation(self, latitude: float, longitude: float, - max_results: int = 1) -> List[Address]: - """逆地理编码:根据经纬度获取地址。优先使用 Nominatim API。""" - results: List[Address] = [] - # 尝试在线逆地理编码 - try: - url = (f"{self._NOMINATIM_URL}/reverse?" - f"lat={latitude}&lon={longitude}&format=jsonv2" - f"&addressdetails=1&limit={max_results}") - data = self._http_get_json(url) - if data and isinstance(data, dict) and "address" in data: - addr = self._parse_nominatim_reverse(data) - results.append(addr) - except Exception: - pass - # 回退到默认地址 - if not results: - addr = Address() - addr.setLatitude(latitude) - addr.setLongitude(longitude) - addr.setFeatureName("Unknown Location") - addr.setLocality("Unknown") - addr.setCountryName("Unknown") - results.append(addr) - return results[:max_results] if max_results > 0 else [] - - def _parse_nominatim_reverse(self, data: dict) -> Address: - """解析 Nominatim 逆地理编码响应。""" - addr = Address() - address = data.get("address", {}) - lat = data.get("lat") - lon = data.get("lon") - if lat is not None: - addr.setLatitude(float(lat)) - if lon is not None: - addr.setLongitude(float(lon)) - addr.setFeatureName(address.get("road") or address.get("building") - or data.get("name") or "Unknown") - addr.setLocality(address.get("city") or address.get("town") - or address.get("village") or address.get("county") or "Unknown") - addr.setAdminArea(address.get("state") or "") - addr.setSubAdminArea(address.get("state_district") or "") - addr.setCountryName(address.get("country") or "Unknown") - addr.setCountryCode(address.get("country_code", "").upper()) - addr.setPostalCode(address.get("postcode") or "") - addr.setAddressLine(0, data.get("display_name", "")) - return addr - - def getFromLocationName(self, location_name: str, max_results: int = 1, - lower_left_lat: Optional[float] = None, - lower_left_lon: Optional[float] = None, - upper_right_lat: Optional[float] = None, - upper_right_lon: Optional[float] = None) -> List[Address]: - """地理编码:根据地名获取经纬度。优先使用 Nominatim API。""" - results: List[Address] = [] - # 尝试在线地理编码 - try: - import urllib.parse - params = { - "q": location_name, - "format": "jsonv2", - "addressdetails": "1", - "limit": str(max_results), - } - # 添加边界框 - if all(v is not None for v in (lower_left_lat, lower_left_lon, - upper_right_lat, upper_right_lon)): - params["viewbox"] = f"{lower_left_lon},{upper_right_lat},{upper_right_lon},{lower_left_lat}" - params["bounded"] = "1" - query = urllib.parse.urlencode(params) - url = f"{self._NOMINATIM_URL}/search?{query}" - data = self._http_get_json(url) - if data and isinstance(data, list): - for item in data[:max_results]: - addr = self._parse_nominatim_search(item) - results.append(addr) - except Exception: - pass - # 回退到默认地址 - if not results: - addr = Address() - addr.setFeatureName(location_name) - addr.setLocality(location_name) - results.append(addr) - return results[:max_results] if max_results > 0 else [] - - def _parse_nominatim_search(self, item: dict) -> Address: - """解析 Nominatim 搜索响应。""" - addr = Address() - lat = item.get("lat") - lon = item.get("lon") - if lat is not None: - addr.setLatitude(float(lat)) - if lon is not None: - addr.setLongitude(float(lon)) - address = item.get("address", {}) - addr.setFeatureName(item.get("name") or address.get("road") or "Unknown") - addr.setLocality(address.get("city") or address.get("town") - or address.get("village") or address.get("county") or "Unknown") - addr.setAdminArea(address.get("state") or "") - addr.setCountryName(address.get("country") or "Unknown") - addr.setCountryCode(address.get("country_code", "").upper()) - addr.setPostalCode(address.get("postcode") or "") - addr.setAddressLine(0, item.get("display_name", "")) - return addr - - -# ═══════════════════════════════════════════════════════════════ -# LocationManager -# ═══════════════════════════════════════════════════════════════ - -class LocationManager(Stub): - """LocationManager — 位置管理器""" - - GPS_PROVIDER = "gps" - NETWORK_PROVIDER = "network" - PASSIVE_PROVIDER = "passive" - FUSED_PROVIDER = "fused" - - KEY_PROXIMITY_ENTERING = "entering" - KEY_PROXIMITY_DO_ENTER = "doEnter" - KEY_FLUSH_COMPLETE = "flushComplete" - KEY_LOCATION_CHANGED = "locationChanged" - KEY_PROXIMITY_ENTERING_DO_ENTER = "doEnter" - - PROVIDERS_CHANGED_ACTION = "android.location.PROVIDERS_CHANGED" - MODE_CHANGED_ACTION = "android.location.MODE_CHANGED" - - def __init__(self): - super().__init__() - self._listeners: Dict[str, List[LocationListener]] = {} - self._providers = { - self.GPS_PROVIDER: LocationProvider(self.GPS_PROVIDER), - self.NETWORK_PROVIDER: LocationProvider(self.NETWORK_PROVIDER), - self.PASSIVE_PROVIDER: LocationProvider(self.PASSIVE_PROVIDER), - self.FUSED_PROVIDER: LocationProvider(self.FUSED_PROVIDER), - } - self._last_known_locations: Dict[str, Location] = {} - self._enabled_providers = {self.GPS_PROVIDER, self.NETWORK_PROVIDER, - self.PASSIVE_PROVIDER, self.FUSED_PROVIDER} - self._test_providers: Dict[str, LocationProvider] = {} - self._test_locations: Dict[str, Location] = {} - self._proximity_alerts: List[Dict[str, Any]] = [] - - def getLastKnownLocation(self, provider: str) -> Optional[Location]: - if _USE_LINUX: - data = LinuxLocation.get_location() - loc = Location(provider or "network") - loc.setLatitude(data.get("latitude", 0.0)) - loc.setLongitude(data.get("longitude", 0.0)) - loc.setAccuracy(data.get("accuracy", 0)) - loc.setTime(int(datetime.now().timestamp() * 1000)) - loc.setProvider(provider or "network") - return loc - if provider in self._last_known_locations: - return self._last_known_locations[provider] - loc = Location(provider) - loc.setLatitude(37.7749) - loc.setLongitude(-122.4194) - loc.setTime(int(datetime.now().timestamp() * 1000)) - loc.setAccuracy(20.0) - self._last_known_locations[provider] = loc - return loc - - def requestLocationUpdates(self, provider_or_request: Any, min_time: Any = None, - min_distance: float = 0.0, listener_or_callback: Any = None, - looper: Any = None) -> None: - if isinstance(provider_or_request, str): - provider = provider_or_request - else: - provider = self.GPS_PROVIDER - if _USE_LINUX and listener_or_callback is not None: - data = LinuxLocation.get_location() - loc = Location(provider or "network") - loc.setLatitude(data.get("latitude", 0.0)) - loc.setLongitude(data.get("longitude", 0.0)) - loc.setAccuracy(data.get("accuracy", 0)) - loc.setTime(int(datetime.now().timestamp() * 1000)) - loc.setProvider(provider or "network") - try: - listener_or_callback.onLocationChanged(loc) - except Exception: - pass - if listener_or_callback is not None: - if provider not in self._listeners: - self._listeners[provider] = [] - if isinstance(listener_or_callback, LocationListener): - self._listeners[provider].append(listener_or_callback) - - def removeUpdates(self, listener: Any): - for provider in list(self._listeners.keys()): - self._listeners[provider] = [ - l for l in self._listeners[provider] if l is not listener - ] - if not self._listeners[provider]: - del self._listeners[provider] - - def isProviderEnabled(self, provider: str) -> bool: - if _USE_LINUX: - return provider == "network" - return provider in self._enabled_providers - - def getProviders(self, criteria: Optional[Criteria] = None, enabled_only: bool = False) -> List[str]: - if _USE_LINUX: - return ["network"] - providers = list(self._providers.keys()) - if enabled_only: - providers = [p for p in providers if p in self._enabled_providers] - return providers - - def getBestProvider(self, criteria: Criteria, enabled_only: bool = False) -> Optional[str]: - if criteria.getAccuracy() == Criteria.ACCURACY_FINE: - return self.GPS_PROVIDER - return self.NETWORK_PROVIDER - - def getAllProviders(self) -> List[str]: - return list(self._providers.keys()) - - def getProvider(self, name: str) -> Optional[LocationProvider]: - return self._providers.get(name) - - def addProximityAlert(self, latitude: float, longitude: float, - radius: float, expiration: int, intent: Any): - """添加接近警报。记录警报信息,在位置更新时检查是否进入/离开区域。""" - import time as _time - alert = { - "latitude": latitude, - "longitude": longitude, - "radius": radius, - "expiration": expiration, - "intent": intent, - "created_at": _time.time(), - "last_state": None, # None=未触发, True=在区域内, False=在区域外 - } - self._proximity_alerts.append(alert) - - def sendExtraCommand(self, provider: str, command: str, extras: Any) -> bool: - """向位置提供者发送额外命令。桌面端无实际效果,返回成功。""" - return True - - def clearTestProviderLocation(self, provider: str): - """清除测试提供者的位置数据。""" - self._test_locations.pop(provider, None) - - def setTestProviderLocation(self, provider: str, location: Location): - """设置测试提供者的位置。""" - self._test_locations[provider] = location - # 通知该提供者的监听器 - if provider in self._listeners: - for listener in self._listeners[provider]: - try: - listener.onLocationChanged(location) - except Exception: - pass - - def removeTestProvider(self, provider: str): - """移除测试提供者。""" - self._test_providers.pop(provider, None) - self._test_locations.pop(provider, None) - - def addTestProvider(self, name: str, requires_network: bool, requires_satellite: bool, - requires_cell: bool, has_monetary_cost: bool, supports_altitude: bool, - supports_speed: bool, supports_bearing: bool, power_requirement: int, - accuracy: int): - self._providers[name] = LocationProvider(name) - - def toString(self) -> str: - return f"LocationManager(providers={list(self._providers.keys())})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Location", - "LocationManager", - "LocationListener", - "LocationProvider", - "Criteria", - "Geocoder", - "Address", - "GpsStatus", - "GpsSatellite", - "GpsStatusListener", - "LocationRequest", - "LocationProviderProperties", - "SettingInjectorService", - "GnssStatus", - "GnssClock", - "GnssMeasurement", - "GnssMeasurementsEvent", - "GnssNavigationMessage", -] \ No newline at end of file diff --git a/android_pyapi/android/location_ext.py b/android_pyapi/android/location_ext.py deleted file mode 100644 index 978755c..0000000 --- a/android_pyapi/android/location_ext.py +++ /dev/null @@ -1,464 +0,0 @@ -""" -Android location 扩展组件 — LocationProvider, GpsStatus, LocationRequest, Criteria 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import math -import threading -from typing import Any, Callable, Dict, List, Optional, Set - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# GpsSatellite -# ═══════════════════════════════════════════════════════════════ - -class GpsSatellite(Stub): - """GpsSatellite — GPS 卫星""" - - def __init__(self, prn: int = 0, snr: float = 0.0, - elevation: float = 0.0, azimuth: float = 0.0, - has_almanac: bool = False, has_ephemeris: bool = False, - used_in_fix: bool = False): - super().__init__() - self._prn = prn - self._snr = snr - self._elevation = elevation - self._azimuth = azimuth - self._has_almanac = has_almanac - self._has_ephemeris = has_ephemeris - self._used_in_fix = used_in_fix - - def getPrn(self) -> int: - return self._prn - - def getSnr(self) -> float: - return self._snr - - def getElevation(self) -> float: - return self._elevation - - def getAzimuth(self) -> float: - return self._azimuth - - def hasAlmanac(self) -> bool: - return self._has_almanac - - def hasEphemeris(self) -> bool: - return self._has_ephemeris - - def usedInFix(self) -> bool: - return self._used_in_fix - - def toString(self) -> str: - return f"GpsSatellite{{prn={self._prn}, snr={self._snr}}}" - - -# ═══════════════════════════════════════════════════════════════ -# GpsStatus -# ═══════════════════════════════════════════════════════════════ - -class GpsStatus(Stub): - """GpsStatus — GPS 状态""" - - SATELITE_TYPE_GPS = 1 - SATELITE_TYPE_GLONASS = 2 - SATELITE_TYPE_BEIDOU = 3 - SATELITE_TYPE_GALILEO = 4 - SATELITE_TYPE_IRNSS = 5 - SATELITE_TYPE_QZSS = 6 - - def __init__(self): - super().__init__() - self._satellites: List[GpsSatellite] = [ - GpsSatellite(prn=1, snr=30.0, elevation=45.0, azimuth=120.0, - has_almanac=True, has_ephemeris=True, used_in_fix=True), - GpsSatellite(prn=3, snr=28.0, elevation=30.0, azimuth=200.0, - has_almanac=True, has_ephemeris=True, used_in_fix=True), - GpsSatellite(prn=7, snr=25.0, elevation=60.0, azimuth=80.0, - has_almanac=True, has_ephemeris=False, used_in_fix=False), - GpsSatellite(prn=11, snr=22.0, elevation=15.0, azimuth=300.0, - has_almanac=True, has_ephemeris=True, used_in_fix=True), - GpsSatellite(prn=17, snr=18.0, elevation=5.0, azimuth=180.0, - has_almanac=False, has_ephemeris=False, used_in_fix=False), - ] - self._max_satellites: int = 32 - self._time_to_first_fix: int = 30 # seconds - - def getSatellites(self) -> List[GpsSatellite]: - return list(self._satellites) - - def getMaxSatellites(self) -> int: - return self._max_satellites - - def getTimeToFirstFix(self) -> int: - return self._time_to_first_fix - - def setStatus(self, sv_status: Any, time_to_first_fix: int): - self._time_to_first_fix = time_to_first_fix - - def toString(self) -> str: - return f"GpsStatus{{satellites={len(self._satellites)}, tttf={self._time_to_first_fix}}}" - - -# ═══════════════════════════════════════════════════════════════ -# LocationRequest -# ═══════════════════════════════════════════════════════════════ - -class LocationRequest(Stub): - """LocationRequest — 位置请求""" - - QUALITY_HIGH_ACCURACY = 100 - QUALITY_BALANCED_POWER_ACCURACY = 102 - QUALITY_LOW_POWER = 104 - QUALITY_PASSIVE_ALLOWED = 105 - - def __init__(self): - super().__init__() - self._quality: int = self.QUALITY_BALANCED_POWER_ACCURACY - self._interval: int = 60000 # ms - self._fastest_interval: int = 30000 # ms - self._expiration_duration: int = -1 - self._expiration_time: int = -1 - self._num_updates: int = -1 # unlimited - self._smallest_displacement: float = 0.0 - self._max_wait_time: int = 0 - self._priority: int = self.QUALITY_BALANCED_POWER_ACCURACY - - @staticmethod - def create() -> "LocationRequest": - return LocationRequest() - - # ── Quality ── - - def getQuality(self) -> int: - return self._quality - - def setQuality(self, quality: int): - self._quality = quality - - # ── Priority ── - - def getPriority(self) -> int: - return self._priority - - def setPriority(self, priority: int): - self._priority = priority - - # ── Interval ── - - def setInterval(self, intervalMillis: int) -> "LocationRequest": - self._interval = intervalMillis - return self - - def getInterval(self) -> int: - return self._interval - - def setFastestInterval(self, intervalMillis: int) -> "LocationRequest": - self._fastest_interval = intervalMillis - return self - - def getFastestInterval(self) -> int: - return self._fastest_interval - - # ── Max wait ── - - def setMaxWaitTime(self, maxWaitMillis: int) -> "LocationRequest": - self._max_wait_time = maxWaitMillis - return self - - def getMaxWaitTime(self) -> int: - return self._max_wait_time - - # ── Expiration ── - - def setExpirationDuration(self, durationMillis: int) -> "LocationRequest": - self._expiration_duration = durationMillis - return self - - def getExpirationDuration(self) -> int: - return self._expiration_duration - - def setExpirationTime(self, timeMillis: int) -> "LocationRequest": - self._expiration_time = timeMillis - return self - - def getExpirationTime(self) -> int: - return self._expiration_time - - # ── Num updates ── - - def setNumUpdates(self, numUpdates: int) -> "LocationRequest": - self._num_updates = numUpdates - return self - - def getNumUpdates(self) -> int: - return self._num_updates - - # ── Displacement ── - - def setSmallestDisplacement(self, smallestDisplacementMeters: float) -> "LocationRequest": - self._smallest_displacement = smallestDisplacementMeters - return self - - def getSmallestDisplacement(self) -> float: - return self._smallest_displacement - - def toString(self) -> str: - return f"LocationRequest{{quality={self._quality}, interval={self._interval}}}" - - -# ═══════════════════════════════════════════════════════════════ -# LocationProviderProperties -# ═══════════════════════════════════════════════════════════════ - -class LocationProviderProperties(Stub): - """LocationProviderProperties — 位置提供者属性""" - - def __init__(self, requires_network: bool = False, requires_satellite: bool = True, - requires_cell: bool = False, has_monetary_cost: bool = False, - supports_altitude: bool = True, supports_speed: bool = True, - supports_bearing: bool = True, power_requirement: int = 2, - accuracy: int = 1): - super().__init__() - self._requires_network = requires_network - self._requires_satellite = requires_satellite - self._requires_cell = requires_cell - self._has_monetary_cost = has_monetary_cost - self._supports_altitude = supports_altitude - self._supports_speed = supports_speed - self._supports_bearing = supports_bearing - self._power_requirement = power_requirement - self._accuracy = accuracy - - def isRequiresNetwork(self) -> bool: - return self._requires_network - - def isRequiresSatellite(self) -> bool: - return self._requires_satellite - - def isRequiresCell(self) -> bool: - return self._requires_cell - - def isHasMonetaryCost(self) -> bool: - return self._has_monetary_cost - - def isSupportsAltitude(self) -> bool: - return self._supports_altitude - - def isSupportsSpeed(self) -> bool: - return self._supports_speed - - def isSupportsBearing(self) -> bool: - return self._supports_bearing - - def getPowerRequirement(self) -> int: - return self._power_requirement - - def getAccuracy(self) -> int: - return self._accuracy - - -# ═══════════════════════════════════════════════════════════════ -# LocationProvider -# ═══════════════════════════════════════════════════════════════ - -class LocationProvider(Stub): - """LocationProvider — 位置提供者""" - - AVAILABLE = 2 - TEMPORARILY_UNAVAILABLE = 1 - OUT_OF_SERVICE = 0 - - def __init__(self, name: str = "gps"): - super().__init__() - self._name = name - self._properties = LocationProviderProperties() - self._connected: bool = True - - def getName(self) -> str: - return self._name - - def meetsProviderCriteria(self, criteria: Any) -> bool: - return True - - def requiresNetwork(self) -> bool: - return self._properties.isRequiresNetwork() - - def requiresSatellite(self) -> bool: - return self._properties.isRequiresSatellite() - - def requiresCell(self) -> bool: - return self._properties.isRequiresCell() - - def hasMonetaryCost(self) -> bool: - return self._properties.isHasMonetaryCost() - - def supportsAltitude(self) -> bool: - return self._properties.isSupportsAltitude() - - def supportsSpeed(self) -> bool: - return self._properties.isSupportsSpeed() - - def supportsBearing(self) -> bool: - return self._properties.isSupportsBearing() - - def getPowerRequirement(self) -> int: - return self._properties.getPowerRequirement() - - def getAccuracy(self) -> int: - return self._properties.getAccuracy() - - def getGpsStatus(self) -> Optional[GpsStatus]: - return GpsStatus() - - def isEnabled(self) -> bool: - return True - - def sendExtraCommand(self, command: str, extras: Dict[str, Any]) -> bool: - return True - - def setListener(self, listener: Any): - pass - - def clearListener(self): - pass - - def getConnected(self) -> bool: - return self._connected - - def toString(self) -> str: - return f"LocationProvider{{name={self._name}, connected={self._connected}}}" - - -# ═══════════════════════════════════════════════════════════════ -# Criteria -# ═══════════════════════════════════════════════════════════════ - -class Criteria(Stub): - """Criteria — 位置提供者选择标准""" - - NO_REQUIREMENT = 0 - ACCURACY_FINE = 1 - ACCURACY_COARSE = 2 - POWER_LOW = 1 - POWER_MEDIUM = 2 - POWER_HIGH = 3 - - def __init__(self): - super().__init__() - self._accuracy: int = self.NO_REQUIREMENT - self._power_requirement: int = self.NO_REQUIREMENT - self._altitude_required: bool = False - self._bearing_required: bool = False - self._speed_required: bool = False - self._cost_allowed: bool = True - self._horizontal_accuracy: int = self.NO_REQUIREMENT - self._vertical_accuracy: int = self.NO_REQUIREMENT - self._bearing_accuracy: int = self.NO_REQUIREMENT - self._speed_accuracy: int = self.NO_REQUIREMENT - - def setAccuracy(self, accuracy: int): - self._accuracy = accuracy - - def getAccuracy(self) -> int: - return self._accuracy - - def setPowerRequirement(self, power: int): - self._power_requirement = power - - def getPowerRequirement(self) -> int: - return self._power_requirement - - def setAltitudeRequired(self, altitudeRequired: bool): - self._altitude_required = altitudeRequired - - def isAltitudeRequired(self) -> bool: - return self._altitude_required - - def setBearingRequired(self, bearingRequired: bool): - self._bearing_required = bearingRequired - - def isBearingRequired(self) -> bool: - return self._bearing_required - - def setSpeedRequired(self, speedRequired: bool): - self._speed_required = speedRequired - - def isSpeedRequired(self) -> bool: - return self._speed_required - - def setCostAllowed(self, costAllowed: bool): - self._cost_allowed = costAllowed - - def isCostAllowed(self) -> bool: - return self._cost_allowed - - def setHorizontalAccuracy(self, accuracy: int): - self._horizontal_accuracy = accuracy - - def getHorizontalAccuracy(self) -> int: - return self._horizontal_accuracy - - def setVerticalAccuracy(self, accuracy: int): - self._vertical_accuracy = accuracy - - def getVerticalAccuracy(self) -> int: - return self._vertical_accuracy - - def setBearingAccuracy(self, accuracy: int): - self._bearing_accuracy = accuracy - - def getBearingAccuracy(self) -> int: - return self._bearing_accuracy - - def setSpeedAccuracy(self, accuracy: int): - self._speed_accuracy = accuracy - - def getSpeedAccuracy(self) -> int: - return self._speed_accuracy - - def toString(self) -> str: - return f"Criteria{{accuracy={self._accuracy}, power={self._power_requirement}}}" - - -# ═══════════════════════════════════════════════════════════════ -# SettingInjectorService -# ═══════════════════════════════════════════════════════════════ - -class SettingInjectorService(Stub): - """SettingInjectorService — 设置注入服务""" - - def __init__(self): - super().__init__() - - def onGetStatus(self) -> int: - return 1 - - def onBind(self, intent: Any) -> Any: - return None - - def toString(self) -> str: - return f"SettingInjectorService@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "LocationProvider", - "LocationProviderProperties", - "GpsStatus", - "GpsSatellite", - "LocationRequest", - "Criteria", - "SettingInjectorService", -] \ No newline at end of file diff --git a/android_pyapi/android/location_geofence.py b/android_pyapi/android/location_geofence.py deleted file mode 100644 index 76eec9f..0000000 --- a/android_pyapi/android/location_geofence.py +++ /dev/null @@ -1,537 +0,0 @@ -""" -Android Location Geofence 组件 — Geofence, GeofencingEvent, GeofenceRequest 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import math -import time -import threading -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# Geofence 常量 -# ═══════════════════════════════════════════════════════════════ - -GEOFENCE_TRANSITION_ENTER = 1 -GEOFENCE_TRANSITION_EXIT = 2 -GEOFENCE_TRANSITION_DWELL = 4 - -GEOFENCE_ENTER = GEOFENCE_TRANSITION_ENTER -GEOFENCE_EXIT = GEOFENCE_TRANSITION_EXIT -GEOFENCE_DWELL = GEOFENCE_TRANSITION_DWELL - -GEOFENCE_RADIUS_INACCURACY = 1 -GEOFENCE_TRANSITION_UNKNOWN = -1 - -MONITORING_TYPE_PASSIVE = 0 -MONITORING_TYPE_ACTIVE = 1 -MONITORING_TYPE_PASSIVE_INTERVAL = 2 -MONITORING_TYPE_LOCATION = 3 -MONITORING_TYPE_FUSED_LOCATION = 4 - -MONITORING_TYPE_GEOFENCE = MONITORING_TYPE_ACTIVE - -GEOFENCE_REMOVE_BY_LIST = 1 -GEOFENCE_REMOVE_BY_SIGNATURE = 2 -GEOFENCE_REMOVE_ALL = 3 - -MONITORING_TYPE_GEOFENCE_ENTER = 1 -MONITORING_TYPE_GEOFENCE_EXIT = 2 -MONITORING_TYPE_GEOFENCE_DWELL = 3 - -GEOFENCE_AVAILABILITY_UNKNOWN = 0 -GEOFENCE_AVAILABILITY_AVAILABLE = 1 -GEOFENCE_AVAILABILITY_UNAVAILABLE = 2 - -GEOFENCE_ENTER_INIT = 0 -GEOFENCE_ENTER_FINAL = 1 -GEOFENCE_EXIT_INIT = 2 -GEOFENCE_EXIT_FINAL = 3 - - -# ═══════════════════════════════════════════════════════════════ -# Geofence -# ═══════════════════════════════════════════════════════════════ - -class Geofence(Stub): - """Geofence — 地理围栏""" - - def __init__(self, request_id: str = "", latitude: float = 0.0, - longitude: float = 0.0, radius: float = 100.0, - expiration_duration: int = -1, transition_types: int = 0): - super().__init__() - self._request_id = request_id - self._latitude = latitude - self._longitude = longitude - self._radius = max(0.0, radius) - self._expiration = expiration_duration - self._transitions = transition_types - self._monitoring_type = MONITORING_TYPE_PASSIVE - self._is_monitoring = False - - def getRequestId(self) -> str: - return self._request_id - - def getLatitude(self) -> float: - return self._latitude - - def getLongitude(self) -> float: - return self._longitude - - def getRadius(self) -> float: - return self._radius - - def getExpiration(self) -> int: - return self._expiration - - def getTransitions(self) -> int: - return self._transitions - - def setMonitoringType(self, monitor_type: int) -> None: - self._monitoring_type = monitor_type - - def getMonitoringType(self) -> int: - return self._monitoring_type - - def isExpired(self) -> bool: - if self._expiration < 0: - return False - return int(time.time() * 1000) > self._expiration - - def isMonitoring(self) -> bool: - return self._is_monitoring - - def setMonitoring(self, monitoring: bool) -> None: - self._is_monitoring = monitoring - - def containsLocation(self, latitude: float, longitude: float) -> bool: - """检查指定位置是否在此地理围栏内""" - dx = latitude - self._latitude - dy = longitude - self._longitude - distance = math.sqrt(dx * dx + dy * dy) * 111319.9 - return distance <= self._radius - - def toString(self) -> str: - return (f"Geofence(id={self._request_id}, " - f"lat={self._latitude:.4f}, lng={self._longitude:.4f}, " - f"radius={self._radius:.1f}m)") - - -class GeofenceBuilder(Stub): - """GeofenceBuilder — 地理围栏构建器""" - - def __init__(self): - super().__init__() - self._request_id: str = "" - self._latitude: float = 0.0 - self._longitude: float = 0.0 - self._radius: float = 100.0 - self._expiration_duration: int = -1 - self._transition_types: int = 0 - self._monitoring_type: int = MONITORING_TYPE_PASSIVE - self._loitering_delay: int = 0 - self._notification_responsiveness: int = 0 - self._last_transition: int = GEOFENCE_TRANSITION_UNKNOWN - - def setRequestId(self, request_id: str) -> "GeofenceBuilder": - self._request_id = request_id - return self - - def setLatitude(self, latitude: float) -> "GeofenceBuilder": - self._latitude = latitude - return self - - def setLongitude(self, longitude: float) -> "GeofenceBuilder": - self._longitude = longitude - return self - - def setRadius(self, radius: float) -> "GeofenceBuilder": - self._radius = max(0.0, radius) - return self - - def setExpirationDuration(self, duration_ms: int) -> "GeofenceBuilder": - self._expiration_duration = duration_ms - return self - - def setTransitionTypes(self, types: int) -> "GeofenceBuilder": - self._transition_types = types - return self - - def setMonitoringType(self, monitor_type: int) -> "GeofenceBuilder": - self._monitoring_type = monitor_type - return self - - def setLoiteringDelay(self, delay_ms: int) -> "GeofenceBuilder": - self._loitering_delay = delay_ms - return self - - def setNotificationResponsiveness(self, ms: int) -> "GeofenceBuilder": - self._notification_responsiveness = ms - return self - - def build(self) -> Geofence: - geofence = Geofence( - self._request_id, self._latitude, self._longitude, - self._radius, self._expiration_duration, self._transition_types - ) - geofence.setMonitoringType(self._monitoring_type) - return geofence - - def toString(self) -> str: - return (f"GeofenceBuilder(id={self._request_id}, " - f"lat={self._latitude:.4f}, lng={self._longitude:.4f})") - - -# ═══════════════════════════════════════════════════════════════ -# GeofencingEvent -# ═══════════════════════════════════════════════════════════════ - -class GeofencingEvent(Stub): - """GeofencingEvent — 地理围栏事件""" - - def __init__(self): - super().__init__() - self._triggering_geofences: List[Geofence] = [] - self._triggering_locations: List[Tuple[float, float]] = [] - self._transition_type: int = GEOFENCE_TRANSITION_UNKNOWN - self._error_code: int = 0 - self._initial_trigger: bool = False - self._event_time: int = 0 - - def getTriggeringGeofences(self) -> List[Geofence]: - return list(self._triggering_geofences) - - def setTriggeringGeofences(self, geofences: List[Geofence]) -> None: - self._triggering_geofences = list(geofences) - - def getTriggeringLocations(self) -> List[Tuple[float, float]]: - return list(self._triggering_locations) - - def addTriggeringLocation(self, latitude: float, longitude: float) -> None: - self._triggering_locations.append((latitude, longitude)) - - def getTransitionType(self) -> int: - return self._transition_type - - def setTransitionType(self, transition: int) -> None: - self._transition_type = transition - - def getErrorCode(self) -> int: - return self._error_code - - def setErrorCode(self, code: int) -> None: - self._error_code = code - - def isInitialTrigger(self) -> bool: - return self._initial_trigger - - def setInitialTrigger(self, initial: bool) -> None: - self._initial_trigger = initial - - def getEventTime(self) -> int: - return self._event_time - - def setEventTime(self, time_ms: int) -> None: - self._event_time = time_ms - - def hasError(self) -> bool: - return self._error_code != 0 - - def toString(self) -> str: - types = {1: "ENTER", 2: "EXIT", 4: "DWELL"} - return (f"GeofencingEvent(transition={types.get(self._transition_type, 'UNKNOWN')}, " - f"geofences={len(self._triggering_geofences)}, " - f"error={self._error_code})") - - -# ═══════════════════════════════════════════════════════════════ -# GeofenceRequest -# ═══════════════════════════════════════════════════════════════ - -class GeofenceRequest(Stub): - """GeofenceRequest — 地理围栏请求""" - - INITIAL_TRIGGER_ENTER = 1 - INITIAL_TRIGGER_EXIT = 2 - INITIAL_TRIGGER_DWELL = 4 - - def __init__(self): - super().__init__() - self._geofences: List[Geofence] = [] - self._initial_triggers: int = 0 - self._expiration_duration: int = -1 - self._loitering_delay: int = 0 - self._notification_responsiveness: int = 0 - - def getGeofences(self) -> List[Geofence]: - return list(self._geofences) - - def addGeofence(self, geofence: Geofence) -> None: - self._geofences.append(geofence) - - def setInitialTriggers(self, triggers: int) -> None: - self._initial_triggers = triggers - - def getInitialTriggers(self) -> int: - return self._initial_triggers - - def setExpirationDuration(self, duration: int) -> None: - self._expiration_duration = duration - - def getExpirationDuration(self) -> int: - return self._expiration_duration - - def setLoiteringDelay(self, delay: int) -> None: - self._loitering_delay = delay - - def getLoiteringDelay(self) -> int: - return self._loitering_delay - - def setNotificationResponsiveness(self, ms: int) -> None: - self._notification_responsiveness = ms - - def getNotificationResponsiveness(self) -> int: - return self._notification_responsiveness - - def toString(self) -> str: - return (f"GeofenceRequest(geofences={len(self._geofences)}, " - f"triggers={self._initial_triggers})") - - -# ═══════════════════════════════════════════════════════════════ -# GeofenceHardware -# ═══════════════════════════════════════════════════════════════ - -class GeofenceHardware(Stub): - """GeofenceHardware — 硬件地理围栏""" - - GEOFENCE_ENTERED = 0 - GEOFENCE_EXITED = 1 - GEOFENCE_UNCERTAIN = 2 - - MONITORING_TYPE_GEOFENCE = MONITORING_TYPE_GEOFENCE - - def __init__(self): - super().__init__() - self._geofences: Dict[int, Geofence] = {} - self._next_id: int = 1 - self._monitor_callback: Optional["GeofenceHardwareMonitorCallback"] = None - self._lock = threading.Lock() - - def addGeofence(self, fence_id: int, geofence: Geofence) -> bool: - with self._lock: - if fence_id < 0: - fence_id = self._next_id - self._next_id += 1 - self._geofences[fence_id] = geofence - return True - - def removeGeofence(self, fence_id: int) -> bool: - with self._lock: - return self._geofences.pop(fence_id, None) is not None - - def pauseGeofence(self, fence_id: int) -> bool: - return fence_id in self._geofences - - def resumeGeofence(self, fence_id: int, monitor_transition: int) -> bool: - return fence_id in self._geofences - - def getMonitoringTypes(self) -> List[int]: - return [MONITORING_TYPE_GEOFENCE] - - def getState(self, monitor_type: int) -> int: - return GEOFENCE_AVAILABILITY_AVAILABLE - - def setMonitorCallback(self, callback: Optional["GeofenceHardwareMonitorCallback"]) -> None: - self._monitor_callback = callback - - def toString(self) -> str: - return f"GeofenceHardware(geofences={len(self._geofences)})" - - -# ═══════════════════════════════════════════════════════════════ -# GeofenceHardwareRequest / Float / Circle -# ═══════════════════════════════════════════════════════════════ - -class GeofenceHardwareRequest(Stub): - """GeofenceHardwareRequest — 硬件地理围栏请求""" - - GEOFENCE_TYPE_CIRCLE = 0 - GEOFENCE_TYPE_FLOAT = 1 - - def __init__(self, latitude: float = 0.0, longitude: float = 0.0, - radius: float = 100.0, geofence_type: int = GEOFENCE_TYPE_CIRCLE): - super().__init__() - self._latitude = latitude - self._longitude = longitude - self._radius = radius - self._geofence_type = geofence_type - self._last_transition: int = GEOFENCE_TRANSITION_UNKNOWN - self._monitor_transition: int = GEOFENCE_TRANSITION_ENTER - self._unknown_timer: int = 30000 - self._monitor_notification: int = 5 - self._source_technologies: int = 0 - - def getLatitude(self) -> float: - return self._latitude - - def getLongitude(self) -> float: - return self._longitude - - def getRadius(self) -> float: - return self._radius - - def getGeofenceType(self) -> int: - return self._geofence_type - - def setLastTransition(self, transition: int) -> None: - self._last_transition = transition - - def getLastTransition(self) -> int: - return self._last_transition - - def setMonitorTransitions(self, transitions: int) -> None: - self._monitor_transition = transitions - - def getMonitorTransitions(self) -> int: - return self._monitor_transition - - def setUnknownTimer(self, timer_ms: int) -> None: - self._unknown_timer = timer_ms - - def getUnknownTimer(self) -> int: - return self._unknown_timer - - def setNotificationResponsiveness(self, ms: int) -> None: - self._monitor_notification = ms - - def getNotificationResponsiveness(self) -> int: - return self._monitor_notification - - def setSourceTechnologies(self, technologies: int) -> None: - self._source_technologies = technologies - - def getSourceTechnologies(self) -> int: - return self._source_technologies - - def toString(self) -> str: - return (f"GeofenceHardwareRequest(lat={self._latitude:.4f}, " - f"lng={self._longitude:.4f}, r={self._radius:.1f})") - - -class GeofenceHardwareRequestFloat(GeofenceHardwareRequest): - """GeofenceHardwareRequestFloat — 浮点型硬件围栏请求""" - - def __init__(self, latitude: float = 0.0, longitude: float = 0.0, - radius: float = 100.0): - super().__init__(latitude, longitude, radius, - GeofenceHardwareRequest.GEOFENCE_TYPE_FLOAT) - - def toString(self) -> str: - return (f"GeofenceHardwareRequestFloat(lat={self._latitude:.4f}, " - f"lng={self._longitude:.4f}, r={self._radius:.1f})") - - -class GeofenceHardwareRequestCircle(GeofenceHardwareRequest): - """GeofenceHardwareRequestCircle — 圆形硬件围栏请求""" - - def __init__(self, latitude: float = 0.0, longitude: float = 0.0, - radius: float = 100.0): - super().__init__(latitude, longitude, radius, - GeofenceHardwareRequest.GEOFENCE_TYPE_CIRCLE) - - def toString(self) -> str: - return (f"GeofenceHardwareRequestCircle(lat={self._latitude:.4f}, " - f"lng={self._longitude:.4f}, r={self._radius:.1f})") - - -# ═══════════════════════════════════════════════════════════════ -# GeofenceHardwareMonitorCallback -# ═══════════════════════════════════════════════════════════════ - -class GeofenceHardwareMonitorCallback(Stub): - """GeofenceHardwareMonitorCallback — 硬件围栏监控回调""" - - def onMonitoringSystemChange(self, monitor_type: int, - available: bool, - location: Optional[Any]) -> None: - pass - - def onGeofenceTransition(self, fence_id: int, - transition: int, - location: Any, - timestamp: int, - monitoring_type: int) -> None: - pass - - def onGeofenceExpiration(self, fence_id: int, - monitoring_type: int) -> None: - pass - - -# ═══════════════════════════════════════════════════════════════ -# GeofenceManager -# ═══════════════════════════════════════════════════════════════ - -class GeofenceManager(Stub): - """GeofenceManager — 地理围栏管理器""" - - def __init__(self): - super().__init__() - self._registered_geofences: Dict[str, Geofence] = {} - self._listeners: Dict[str, Callable] = {} - self._lock = threading.Lock() - self._monitoring = False - - def addGeofence(self, geofence: Geofence, listener: Callable) -> None: - with self._lock: - self._registered_geofences[geofence.getRequestId()] = geofence - self._listeners[geofence.getRequestId()] = listener - geofence.setMonitoring(True) - - def removeGeofence(self, request_id: str) -> None: - with self._lock: - geofence = self._registered_geofences.pop(request_id, None) - self._listeners.pop(request_id, None) - if geofence: - geofence.setMonitoring(False) - - def removeAllGeofences(self) -> None: - with self._lock: - for geofence in self._registered_geofences.values(): - geofence.setMonitoring(False) - self._registered_geofences.clear() - self._listeners.clear() - - def getRegisteredGeofences(self) -> List[Geofence]: - with self._lock: - return list(self._registered_geofences.values()) - - def isMonitoring(self) -> bool: - return self._monitoring - - def setMonitoring(self, monitoring: bool) -> None: - self._monitoring = monitoring - - def triggerTransition(self, geofence_id: str, transition: int, - latitude: float, longitude: float) -> None: - with self._lock: - if geofence_id in self._listeners: - event = GeofencingEvent() - event.setTransitionType(transition) - event.addTriggeringLocation(latitude, longitude) - if geofence_id in self._registered_geofences: - event.setTriggeringGeofences( - [self._registered_geofences[geofence_id]] - ) - self._listeners[geofence_id](event) - - def toString(self) -> str: - return (f"GeofenceManager(geofences={len(self._registered_geofences)}, " - f"monitoring={self._monitoring})") \ No newline at end of file diff --git a/android_pyapi/android/managers.py b/android_pyapi/android/managers.py deleted file mode 100644 index 624f9a2..0000000 --- a/android_pyapi/android/managers.py +++ /dev/null @@ -1,1773 +0,0 @@ -""" -Android System Manager API — 所有剩余系统 Manager 类集合 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import sys -import time -import json -import random -import socket -import uuid -import threading -from datetime import datetime, timedelta -from typing import Any, Dict, List, Optional, Set, Tuple, Callable, BinaryIO - -from .._core import Stub, AndroidOnlyError, _app_data_dir, _sdcard_root - -from ..java.lang import RuntimeException - -# Linux 真实实现导入 -try: - from .._linux_impl import IS_LINUX, LinuxNetwork, HAS_IP - _USE_LINUX = IS_LINUX -except ImportError: - _USE_LINUX = False - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# 全局常量 -# ═══════════════════════════════════════════════════════════════ - -INVALID_ID = -1 -MATCH_ALL = -1 - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityManager -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityManager(Stub): - """AccessibilityManager — 无障碍管理器 (桌面存根)""" - - FLAG_CONTENT_CONTROLS = 0x00000001 - FLAG_CONTENT_ICONS = 0x00000002 - FLAG_CONTENT_TEXT = 0x00000004 - FLAG_CONTENT_CONTENT_DESCRIPTION = 0x00000008 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._enabled = False - self._touch_exploration_enabled = False - self._high_text_contrast_enabled = False - self._animator_duration_scale = 1.0 - self._display_magnification_enabled = False - self._accessibility_volume = 0 - self._speech_rate = 1.0 - self._listeners: List[Any] = [] - - def isEnabled(self) -> bool: - return self._enabled - - def addAccessibilityStateChangeListener(self, listener: Any) -> bool: - if listener not in self._listeners: - self._listeners.append(listener) - return True - - def removeAccessibilityStateChangeListener(self, listener: Any) -> bool: - if listener in self._listeners: - self._listeners.remove(listener) - return True - - def isTouchExplorationEnabled(self) -> bool: - return self._touch_exploration_enabled - - def isHighTextContrastEnabled(self) -> bool: - return self._high_text_contrast_enabled - - def getAccessibilityServiceList(self) -> List[Any]: - return [] - - def addTouchExplorationStateChangeListener(self, listener: Any) -> None: - pass - - def removeTouchExplorationStateChangeListener(self, listener: Any) -> None: - pass - - def sendAccessibilityEvent(self, event: Any) -> None: - pass - - def interrupt(self) -> None: - pass - - def getEnabledAccessibilityServiceList(self, feedbackTypeFlags: int) -> List[Any]: - return [] - - def getInstalledAccessibilityServiceList(self) -> List[Any]: - return [] - - def toString(self) -> str: - return f"AccessibilityManager(enabled={self._enabled})" - - -# ═══════════════════════════════════════════════════════════════ -# AccountManager (扩展) -# ═══════════════════════════════════════════════════════════════ - -class AccountManager(Stub): - """AccountManager — 账户管理器 (扩展)""" - - KEY_ACCOUNTS = "accounts" - KEY_ACCOUNT_NAME = "accountName" - KEY_ACCOUNT_TYPE = "accountType" - KEY_AUTHTOKEN = "authtoken" - KEY_AUTH_FAILED_MESSAGE = "authFailedMessage" - KEY_AUTH_TOKEN_LABEL = "authTokenLabelKey" - KEY_BOOLEAN_RESULT = "booleanResult" - KEY_ERROR_CODE = "errorCode" - KEY_ERROR_MESSAGE = "errorMessage" - KEY_INTENT = "intent" - KEY_PASSWORD = "password" - KEY_USERDATA = "userdata" - KEY_CUSTOM_TOKEN_EXPIRY = "customTokenExpiry" - KEY_ACCOUNT_ACCESS_ID = "accountAccessId" - - ERROR_CODE_NETWORK_ERROR = 1 - ERROR_CODE_UNSUPPORTED_OPERATION = 2 - ERROR_CODE_INVALID_RESPONSE = 3 - ERROR_CODE_REMOTE_EXCEPTION = 4 - ERROR_CODE_MANAGEMENT_DISABLED = 5 - ERROR_CODE_BAD_ARGUMENTS = 6 - ERROR_CODE_BAD_REQUEST = 7 - ERROR_CODE_DENIED = 8 - ERROR_CODE_CANCELED = 9 - ERROR_CODE_INVALID_CREDENTIALS = 10 - ERROR_CODE_SESSION_EXPIRED = 11 - ERROR_CODE_USER_RESTRICTION = 12 - - _instance: Optional["AccountManager"] = None - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._accounts: List[Any] = [] - self._auth_tokens: Dict[str, str] = {} - self._user_data: Dict[str, Dict[str, str]] = {} - self._password_data: Dict[str, str] = {} - self._account_visibility: Dict[str, bool] = {} - self._lock = threading.Lock() - - @staticmethod - def get(context: Any) -> "AccountManager": - if AccountManager._instance is None: - AccountManager._instance = AccountManager(context) - return AccountManager._instance - - def getAccounts(self) -> List[Any]: - with self._lock: - return list(self._accounts) - - def getAccountsByType(self, type_: str) -> List[Any]: - with self._lock: - return [a for a in self._accounts if getattr(a, "type", None) == type_] - - def addAccountExplicitly(self, account: Any, password: Optional[str], userdata: Optional[Dict]) -> bool: - with self._lock: - if account not in self._accounts: - self._accounts.append(account) - name = getattr(account, "name", str(account)) - if password is not None: - self._password_data[name] = password - if userdata: - self._user_data[name] = dict(userdata) - return True - return False - - def removeAccountExplicitly(self, account: Any) -> bool: - with self._lock: - if account in self._accounts: - self._accounts.remove(account) - name = getattr(account, "name", str(account)) - self._password_data.pop(name, None) - self._user_data.pop(name, None) - return True - return False - - def getUserData(self, account: Any, key: str) -> Optional[str]: - with self._lock: - name = getattr(account, "name", str(account)) - return self._user_data.get(name, {}).get(key) - - def setUserData(self, account: Any, key: str, value: str) -> None: - with self._lock: - name = getattr(account, "name", str(account)) - if name not in self._user_data: - self._user_data[name] = {} - self._user_data[name][key] = value - - def getPassword(self, account: Any) -> Optional[str]: - with self._lock: - name = getattr(account, "name", str(account)) - return self._password_data.get(name) - - def setPassword(self, account: Any, password: str) -> None: - with self._lock: - name = getattr(account, "name", str(account)) - self._password_data[name] = password - - def clearPassword(self, account: Any) -> None: - with self._lock: - name = getattr(account, "name", str(account)) - self._password_data.pop(name, None) - - def getAuthToken(self, account: Any, authTokenType: str) -> Optional[str]: - with self._lock: - name = getattr(account, "name", str(account)) - return self._auth_tokens.get(f"{name}:{authTokenType}") - - def setAuthToken(self, account: Any, authTokenType: str, authToken: str) -> None: - with self._lock: - name = getattr(account, "name", str(account)) - self._auth_tokens[f"{name}:{authTokenType}"] = authToken - - def invalidateAuthToken(self, accountType: str, authToken: str) -> None: - with self._lock: - to_remove = [k for k, v in self._auth_tokens.items() if v == authToken] - for k in to_remove: - del self._auth_tokens[k] - - def hasFeatures(self, account: Any, features: List[str], callback: Any, handler: Any = None) -> None: - pass - - def getAccountVisibility(self, account: Any, packageName: str) -> int: - return 1 - - def toString(self) -> str: - return f"AccountManager(accounts={len(self._accounts)})" - - -# ═══════════════════════════════════════════════════════════════ -# AlarmManager -# ═══════════════════════════════════════════════════════════════ - -class AlarmManager(Stub): - """AlarmManager — 闹钟管理器""" - - RTC_WAKEUP = 0 - RTC = 1 - ELAPSED_REALTIME_WAKEUP = 2 - ELAPSED_REALTIME = 3 - INTERVAL_FIFTEEN_MINUTES = 900000 - INTERVAL_HALF_HOUR = 1800000 - INTERVAL_HOUR = 3600000 - INTERVAL_DAY = 86400000 - FLAG_ALLOW_WHILE_IDLE_UNSCREEN_ON = 0x00000008 - FLAG_ALLOW_WHILE_IDLE = 0x00000004 - FLAG_STANDALONE = 0x00000002 - FLAG_WAKE_FROM_IDLE = 0x00000001 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._alarms: List[Dict[str, Any]] = [] - self._lock = threading.Lock() - self._next_id = 1 - self._worker: Optional[threading.Thread] = None - self._running = True - - def set(self, type_: int, triggerAtMillis: int, operation: Any) -> None: - with self._lock: - self._alarms.append({ - "id": self._next_id, - "type": type_, - "trigger": triggerAtMillis, - "operation": operation, - "interval": 0, - "repeat": False, - "window": 0, - "allow_while_idle": False, - }) - self._next_id += 1 - - def setRepeating(self, type_: int, triggerAtMillis: int, intervalMillis: int, operation: Any) -> None: - with self._lock: - self._alarms.append({ - "id": self._next_id, - "type": type_, - "trigger": triggerAtMillis, - "operation": operation, - "interval": intervalMillis, - "repeat": True, - "window": 0, - "allow_while_idle": False, - }) - self._next_id += 1 - - def setWindow(self, type_: int, windowStartMillis: int, windowLengthMillis: int, operation: Any) -> None: - with self._lock: - self._alarms.append({ - "id": self._next_id, - "type": type_, - "trigger": windowStartMillis, - "operation": operation, - "interval": 0, - "repeat": False, - "window": windowLengthMillis, - "allow_while_idle": False, - }) - self._next_id += 1 - - def setExact(self, type_: int, triggerAtMillis: int, operation: Any) -> None: - self.set(type_, triggerAtMillis, operation) - - def setExactAndAllowWhileIdle(self, type_: int, triggerAtMillis: int, operation: Any) -> None: - self.set(type_, triggerAtMillis, operation) - - def setAlarmClock(self, info: Any, triggerAtMillis: int, operation: Any) -> None: - pass - - def cancel(self, operation: Any) -> None: - with self._lock: - self._alarms = [a for a in self._alarms if a.get("operation") != operation] - - def getNextAlarmClock(self) -> Optional[Any]: - return None - - def canScheduleExactAlarms(self) -> bool: - return True - - def toString(self) -> str: - return f"AlarmManager(alarms={len(self._alarms)})" - - -# ═══════════════════════════════════════════════════════════════ -# AppOpsManager -# ═══════════════════════════════════════════════════════════════ - -class AppOpsManager(Stub): - """AppOpsManager — 应用操作管理器""" - - MODE_ALLOWED = 0 - MODE_IGNORED = 1 - MODE_ERRORED = 2 - MODE_DEFAULT = 3 - MODE_FOREGROUND = 4 - - OPSTR_COARSE_LOCATION = "android:coarse_location" - OPSTR_FINE_LOCATION = "android:fine_location" - OPSTR_CAMERA = "android:camera" - OPSTR_RECORD_AUDIO = "android:record_audio" - OPSTR_READ_CONTACTS = "android:read_contacts" - OPSTR_WRITE_CONTACTS = "android:write_contacts" - OPSTR_READ_EXTERNAL_STORAGE = "android:read_external_storage" - OPSTR_WRITE_EXTERNAL_STORAGE = "android:write_external_storage" - OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications" - OPSTR_POST_NOTIFICATIONS = "android:post_notifications" - OPSTR_VIBRATE = "android:vibrate" - OPSTR_CALL_PHONE = "android:call_phone" - OPSTR_READ_SMS = "android:read_sms" - OPSTR_SEND_SMS = "android:send_sms" - OPSTR_RECEIVE_SMS = "android:receive_sms" - OPSTR_SYSTEM_ALERT_WINDOW = "android:system_alert_window" - OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages" - OPSTR_BLUETOOTH = "android:bluetooth" - OPSTR_BLUETOOTH_SCAN = "android:bluetooth_scan" - OPSTR_BLUETOOTH_ADVERTISE = "android:bluetooth_advertise" - OPSTR_BLUETOOTH_CONNECT = "android:bluetooth_connect" - OPSTR_NFC = "android:nfc" - OPSTR_NFC_PREFERRED_PAYMENT = "android:nfc_preferred_payment" - OPSTR_PICTURE_IN_PICTURE = "android:picture_in_picture" - OPSTR_FOREGROUND_SERVICE = "android:foreground_service" - OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage" - OPSTR_BODY_SENSORS = "android:body_sensors" - OPSTR_ACTIVITY_RECOGNITION = "android:activity_recognition" - OPSTR_USE_BIOMETRIC = "android:use_biometric" - OPSTR_USE_FINGERPRINT = "android:use_fingerprint" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._op_modes: Dict[str, Dict[str, int]] = {} - self._lock = threading.Lock() - - def checkOp(self, op: str, uid: int, packageName: str) -> int: - with self._lock: - if packageName not in self._op_modes: - return AppOpsManager.MODE_ALLOWED - return self._op_modes[packageName].get(op, AppOpsManager.MODE_ALLOWED) - - def noteOp(self, op: str, uid: int, packageName: str) -> int: - return self.checkOp(op, uid, packageName) - - def startOp(self, op: str, uid: int, packageName: str) -> int: - return self.checkOp(op, uid, packageName) - - def finishOp(self, op: str, uid: int, packageName: str) -> None: - pass - - def setMode(self, code: int, uid: int, packageName: str, mode: int) -> None: - with self._lock: - if packageName not in self._op_modes: - self._op_modes[packageName] = {} - self._op_modes[packageName][str(code)] = mode - - def toString(self) -> str: - return f"AppOpsManager(packages={len(self._op_modes)})" - - -# ═══════════════════════════════════════════════════════════════ -# ClipboardManager -# ═══════════════════════════════════════════════════════════════ - -class ClipboardManager(Stub): - """ClipboardManager — 剪贴板管理器""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._primary_clip: Optional[Any] = None - self._listeners: List[Callable] = [] - self._has_clipboard = True - self._clipboard_history: List[Any] = [] - self._max_history = 10 - - def setPrimaryClip(self, clip: Any) -> None: - self._primary_clip = clip - if clip is not None: - self._clipboard_history.append(clip) - if len(self._clipboard_history) > self._max_history: - self._clipboard_history.pop(0) - for listener in self._listeners: - try: - listener(clip) - except Exception: - pass - - def clearPrimaryClip(self) -> None: - if self._primary_clip is not None: - self._clipboard_history.append(None) - self._primary_clip = None - - def getPrimaryClip(self) -> Optional[Any]: - return self._primary_clip - - def hasPrimaryClip(self) -> bool: - return self._primary_clip is not None - - def hasClipboardText(self) -> bool: - if self._primary_clip is None: - return False - try: - return hasattr(self._primary_clip, "getText") and self._primary_clip.getText() is not None - except Exception: - return False - - def getText(self) -> Optional[str]: - if self._primary_clip is not None: - try: - return self._primary_clip.getText() - except Exception: - pass - return None - - def addPrimaryClipChangedListener(self, listener: Callable) -> None: - if listener not in self._listeners: - self._listeners.append(listener) - - def removePrimaryClipChangedListener(self, listener: Callable) -> None: - if listener in self._listeners: - self._listeners.remove(listener) - - def getClipboardHistory(self) -> List[Any]: - return list(self._clipboard_history) - - def toString(self) -> str: - return f"ClipboardManager(has_clip={self.hasPrimaryClip()})" - - -# ═══════════════════════════════════════════════════════════════ -# ConnectivityManager (扩展) -# ═══════════════════════════════════════════════════════════════ - -class ConnectivityManager(Stub): - """ConnectivityManager — 连接管理器 (扩展)""" - - TYPE_NONE = -1 - TYPE_MOBILE = 0 - TYPE_WIFI = 1 - TYPE_MOBILE_MMS = 2 - TYPE_MOBILE_SUPL = 3 - TYPE_MOBILE_DUN = 4 - TYPE_MOBILE_HIPRI = 5 - TYPE_WIMAX = 6 - TYPE_BLUETOOTH = 7 - TYPE_DUMMY = 8 - TYPE_ETHERNET = 9 - TYPE_VPN = 17 - TYPE_WIFI_P2P = 13 - - EXTRA_NETWORK_TYPE = "networkType" - EXTRA_IS_FAILOVER = "isFailover" - EXTRA_OTHER_NETWORK_INFO = "otherNetwork" - EXTRA_NO_CONNECTIVITY = "noConnectivity" - EXTRA_REASON = "reason" - EXTRA_EXTRA_INFO = "extraInfo" - EXTRA_NETWORK_INFO = "networkInfo" - EXTRA_NETWORK_REQUEST = "networkRequest" - EXTRA_NETWORK = "network" - - CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._active_network_info: Optional[Any] = None - self._network_preference = ConnectivityManager.TYPE_WIFI - self._background_data_setting = True - self._network_callbacks: List[Any] = [] - self._active_network: Optional[Any] = None - self._all_networks: List[Any] = [] - - def _get_linux_active_info(self): - """从 Linux 真实网络信息构造活跃 NetworkInfo""" - try: - from .net import NetworkInfo - interfaces = LinuxNetwork.get_interfaces() - # 有真实网络接口 (非环回) 且 operstate=up 即视为已连接 - connected = any( - (iface.get("ifname") or iface.get("name", "")) != "lo" - and iface.get("operstate", "").lower() == "up" - for iface in interfaces - ) - if not connected: - connected = LinuxNetwork.get_connectivity() - ip = LinuxNetwork.get_ip_address() - iface_name = "" - net_type = ConnectivityManager.TYPE_ETHERNET - for iface in interfaces: - name = iface.get("ifname") or iface.get("name", "") - state = iface.get("operstate", "") - if name and name != "lo" and state.lower() == "up": - iface_name = iface_name or name - if name and name.startswith(("wl", "wlan", "wlp")): - net_type = ConnectivityManager.TYPE_WIFI - iface_name = name - data = { - "connected": connected, - "ip": ip, - "interface": iface_name, - "type_name": "WIFI" if net_type == ConnectivityManager.TYPE_WIFI else "ETHERNET", - } - return NetworkInfo.from_linux_data(data, network_type=net_type) - except Exception: - return self._active_network_info - - def getActiveNetworkInfo(self) -> Optional[Any]: - if _USE_LINUX: - return self._get_linux_active_info() - return self._active_network_info - - def getNetworkInfo(self, networkType: int) -> Optional[Any]: - if _USE_LINUX: - info = self._get_linux_active_info() - if info is not None and info.getType() == networkType: - return info - return None - return None - - def getConnectivityStatus(self) -> bool: - """返回当前网络连通性 (True=已连接)""" - if _USE_LINUX: - return LinuxNetwork.get_connectivity() - return False - - def getAllNetworkInfo(self) -> List[Any]: - return self._all_networks - - def isActiveNetworkMetered(self) -> bool: - return True - - def getActiveNetwork(self) -> Optional[Any]: - return self._active_network - - def getAllNetworks(self) -> List[Any]: - return list(self._all_networks) - - def requestNetwork(self, request: Any, callback: Any) -> None: - pass - - def registerNetworkCallback(self, request: Any, callback: Any) -> None: - if callback not in self._network_callbacks: - self._network_callbacks.append(callback) - - def unregisterNetworkCallback(self, callback: Any) -> None: - if callback in self._network_callbacks: - self._network_callbacks.remove(callback) - - def getNetworkCapabilities(self, network: Any) -> Optional[Any]: - return None - - def getLinkProperties(self, network: Any) -> Optional[Any]: - return None - - def setNetworkPreference(self, preference: int) -> None: - self._network_preference = preference - - def getBackgroundDataSetting(self) -> bool: - return self._background_data_setting - - def setBackgroundDataSetting(self, allow: bool) -> None: - self._background_data_setting = allow - - def reportBadNetwork(self, network: Any) -> None: - pass - - def toString(self) -> str: - return f"ConnectivityManager(active={self._active_network_info is not None})" - - -# ═══════════════════════════════════════════════════════════════ -# DevicePolicyManager (扩展) -# ═══════════════════════════════════════════════════════════════ - -class DevicePolicyManager(Stub): - """DevicePolicyManager — 设备策略管理器 (扩展)""" - - ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN" - ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED" - ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED" - ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD" - ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION" - ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.PROVISION_MANAGED_PROFILE" - ACTION_SYSTEM_UPDATE_POLICY = "android.app.action.SYSTEM_UPDATE_POLICY" - - KEYGUARD_DISABLE_FEATURES_NONE = 0 - KEYGUARD_DISABLE_FEATURES_ALL = 0x7FFFFFFF - KEYGUARD_DISABLE_SECURE_CAMERA = 2 - KEYGUARD_DISABLE_FINGERPRINT = 32 - KEYGUARD_DISABLE_REMOTE_INPUT = 64 - KEYGUARD_DISABLE_FACE = 128 - KEYGUARD_DISABLE_IRIS = 256 - KEYGUARD_DISABLE_BIOMETRICS = 32 | 128 | 256 - KEYGUARD_DISABLE_SHORTCUTS = 512 - KEYGUARD_DISABLE_WIDGETS_ALL = 1048576 - - PASSWORD_QUALITY_UNSPECIFIED = 0 - PASSWORD_QUALITY_SOMETHING = 0x10000 - PASSWORD_QUALITY_NUMERIC = 0x20000 - PASSWORD_QUALITY_NUMERIC_COMPLEX = 0x30000 - PASSWORD_QUALITY_ALPHABETIC = 0x40000 - PASSWORD_QUALITY_ALPHANUMERIC = 0x50000 - PASSWORD_QUALITY_COMPLEX = 0x60000 - PASSWORD_QUALITY_MANAGED = 0x80000 - PASSWORD_QUALITY_BIOMETRIC_WEAK = 0x8000 - - RESET_PASSWORD_REQUIRE_ENTRY = 0x0001 - RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 0x0002 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._admin_receivers: List[Any] = [] - self._password_quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED - self._password_length = 0 - self._maximum_failed_passwords_for_wipe = 0 - self._maximum_time_to_lock = 0 - self._storage_encryption = False - self._camera_disabled = False - self._screen_capture_disabled = False - self._keyguard_disabled_features = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE - self._lock_task_packages: List[str] = [] - - def isDeviceOwnerApp(self, packageName: str) -> bool: - return False - - def isProfileOwnerApp(self, packageName: str) -> bool: - return False - - def getDeviceOwner(self) -> Optional[str]: - return None - - def getProfileOwner(self) -> Optional[Any]: - return None - - def setPasswordQuality(self, admin: Any, quality: int) -> None: - self._password_quality = quality - - def getPasswordQuality(self, admin: Any = None) -> int: - return self._password_quality - - def setPasswordMinimumLength(self, admin: Any, length: int) -> None: - self._password_length = length - - def getPasswordMinimumLength(self, admin: Any = None) -> int: - return self._password_length - - def resetPassword(self, password: str, flags: int = 0) -> bool: - return True - - def setMaximumFailedPasswordsForWipe(self, admin: Any, num: int) -> None: - self._maximum_failed_passwords_for_wipe = num - - def getMaximumFailedPasswordsForWipe(self, admin: Any = None) -> int: - return self._maximum_failed_passwords_for_wipe - - def setMaximumTimeToLock(self, admin: Any, timeMs: int) -> None: - self._maximum_time_to_lock = timeMs - - def getMaximumTimeToLock(self, admin: Any = None) -> int: - return self._maximum_time_to_lock - - def lockNow(self) -> None: - pass - - def wipeData(self, flags: int = 0) -> None: - raise RuntimeError("wipeData() would wipe device data") - - def setStorageEncryption(self, admin: Any, encrypt: bool) -> int: - self._storage_encryption = encrypt - return 1 - - def getStorageEncryption(self, admin: Any = None) -> bool: - return self._storage_encryption - - def setCameraDisabled(self, admin: Any, disabled: bool) -> None: - self._camera_disabled = disabled - - def getCameraDisabled(self, admin: Any = None) -> bool: - return self._camera_disabled - - def setScreenCaptureDisabled(self, admin: Any, disabled: bool) -> None: - self._screen_capture_disabled = disabled - - def getScreenCaptureDisabled(self, admin: Any = None) -> bool: - return self._screen_capture_disabled - - def setKeyguardDisabledFeatures(self, admin: Any, features: int) -> None: - self._keyguard_disabled_features = features - - def getKeyguardDisabledFeatures(self, admin: Any = None) -> int: - return self._keyguard_disabled_features - - def setLockTaskPackages(self, admin: Any, packages: List[str]) -> None: - self._lock_task_packages = list(packages) - - def getLockTaskPackages(self, admin: Any = None) -> List[str]: - return list(self._lock_task_packages) - - def isLockTaskPermitted(self, pkg: str) -> bool: - return pkg in self._lock_task_packages - - def toString(self) -> str: - return f"DevicePolicyManager(password_quality=0x{self._password_quality:x})" - - -# ═══════════════════════════════════════════════════════════════ -# DisplayManager (扩展) -# ═══════════════════════════════════════════════════════════════ - -class DisplayManager(Stub): - """DisplayManager — 显示管理器 (扩展)""" - - DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION" - VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 0x00000001 - VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 0x00000002 - VIRTUAL_DISPLAY_FLAG_PRESENTATION = 0x00000004 - VIRTUAL_DISPLAY_FLAG_SECURE = 0x00000008 - VIRTUAL_DISPLAY_FLAG_PUBLIC = 0x00000010 - VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH = 0x00000020 - VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 0x00000040 - VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_SURFACE = 0x00000080 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._displays: Dict[int, Any] = {} - self._display_listeners: List[Any] = [] - self._default_display: Optional[Any] = None - self._virtual_displays: Dict[str, Any] = {} - - def getDisplay(self, displayId: int) -> Optional[Any]: - return self._displays.get(displayId) - - def getDisplays(self, category: Optional[str] = None) -> List[Any]: - if category: - return [d for d in self._displays.values()] - return list(self._displays.values()) - - def getDefaultDisplay(self) -> Optional[Any]: - return self._default_display - - def registerDisplayListener(self, listener: Any, handler: Any = None) -> None: - if listener not in self._display_listeners: - self._display_listeners.append(listener) - - def unregisterDisplayListener(self, listener: Any) -> None: - if listener in self._display_listeners: - self._display_listeners.remove(listener) - - def createVirtualDisplay( - self, name: str, width: int, height: int, densityDpi: int, - surface: Any, flags: int = 0 - ) -> Optional[Any]: - return None - - def toString(self) -> str: - return f"DisplayManager(displays={len(self._displays)})" - - -# ═══════════════════════════════════════════════════════════════ -# DownloadManager -# ═══════════════════════════════════════════════════════════════ - -class DownloadManager(Stub): - """DownloadManager — 下载管理器""" - - ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE" - ACTION_NOTIFICATION_CLICKED = "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED" - ACTION_VIEW_DOWNLOADS = "android.intent.action.VIEW_DOWNLOADS" - EXTRA_DOWNLOAD_ID = "extraDownloadId" - EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS = "extraNotificationClickDownloadIds" - INTENT_EXTRAS_SORT_BY_SIZE = "android.app.DownloadManager.extraSortBySize" - - COLUMN_ID = "_id" - COLUMN_TITLE = "title" - COLUMN_DESCRIPTION = "description" - COLUMN_URI = "uri" - COLUMN_STATUS = "status" - COLUMN_REASON = "reason" - COLUMN_TOTAL_SIZE_BYTES = "total_size" - COLUMN_BYTES_DOWNLOADED_SO_FAR = "bytes_so_far" - COLUMN_MEDIA_TYPE = "media_type" - COLUMN_LOCAL_FILENAME = "local_filename" - COLUMN_LOCAL_URI = "local_uri" - COLUMN_LAST_MODIFIED_TIMESTAMP = "last_modified_timestamp" - - STATUS_PENDING = 1 - STATUS_RUNNING = 2 - STATUS_PAUSED = 3 - STATUS_SUCCESSFUL = 4 - STATUS_FAILED = 5 - STATUS_QUEUED = 6 - STATUS_WAITING = 7 - STATUS_WAITING_FOR_NETWORK = 8 - STATUS_WAITING_FOR_WIFI = 9 - - ERROR_UNKNOWN = 1000 - ERROR_DEVICE_NOT_FOUND = 1001 - ERROR_CANNOT_RESUME = 1002 - ERROR_FILE_ALREADY_EXISTS = 1003 - ERROR_FILE_ERROR = 1004 - ERROR_HTTP_DATA_ERROR = 1005 - ERROR_INSUFFICIENT_SPACE = 1006 - ERROR_TOO_MANY_REDIRECTS = 1007 - ERROR_UNHANDLED_HTTP_CODE = 1008 - ERROR_HTTP_406 = 1009 - - PAUSED_QUEUED_FOR_WIFI = 1 - PAUSED_WAITING_TO_RETRY = 2 - PAUSED_WAITING_FOR_NETWORK = 3 - PAUSED_UNKNOWN = 4 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._downloads: Dict[int, Dict[str, Any]] = {} - self._next_id = 1 - self._lock = threading.Lock() - - def enqueue(self, request: Any) -> int: - with self._lock: - download_id = self._next_id - self._next_id += 1 - self._downloads[download_id] = { - "id": download_id, - "status": DownloadManager.STATUS_PENDING, - "reason": 0, - "title": "", - "description": "", - "uri": "", - "total_size": -1, - "bytes_so_far": 0, - "local_filename": "", - } - return download_id - - def query(self, query: Any) -> Any: - return None - - def remove(self, varargs: List[int]) -> int: - removed = 0 - with self._lock: - for download_id in varargs: - if download_id in self._downloads: - del self._downloads[download_id] - removed += 1 - return removed - - def getUriForDownloadedFile(self, downloadId: int) -> Optional[str]: - return None - - def getMimeTypeForDownloadedFile(self, downloadId: int) -> Optional[str]: - return None - - def restartDownload(self, downloadId: int) -> bool: - return False - - def toString(self) -> str: - return f"DownloadManager(downloads={len(self._downloads)})" - - -# ═══════════════════════════════════════════════════════════════ -# DropBoxManager -# ═══════════════════════════════════════════════════════════════ - -class DropBoxManager(Stub): - """DropBoxManager — 日志收集管理器""" - - IS_EMPTY = 0 - IS_TEXT = 1 - IS_GZIPPED = 2 - IS_ENCRYPTED = 4 - - def __init__(self, context: Any = None): - super().__init__() - self._entries: Dict[str, List[Any]] = {} - self._max_entries = 100 - - def addText(self, tag: str, data: str) -> None: - if tag not in self._entries: - self._entries[tag] = [] - self._entries[tag].append(("text", data, time.time())) - if len(self._entries[tag]) > self._max_entries: - self._entries[tag].pop(0) - - def addData(self, tag: str, data: bytes, flags: int = 0) -> None: - if tag not in self._entries: - self._entries[tag] = [] - self._entries[tag].append(("data", data, time.time(), flags)) - if len(self._entries[tag]) > self._max_entries: - self._entries[tag].pop(0) - - def getNextEntry(self, tag: str, msec: int) -> Optional[Any]: - return None - - def isTagEnabled(self, tag: str) -> bool: - return True - - def dump(self, pw: Any) -> None: - pass - - def toString(self) -> str: - return f"DropBoxManager(entries={sum(len(v) for v in self._entries.values())})" - - -# ═══════════════════════════════════════════════════════════════ -# FocusManager -# ═══════════════════════════════════════════════════════════════ - -class FocusManager(Stub): - """FocusManager — 焦点管理器""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._focused_view: Optional[Any] = None - self._focus_cycle_enabled = True - self._focus_search_enabled = True - - def getFocusedView(self) -> Optional[Any]: - return self._focused_view - - def setFocusedView(self, view: Any) -> None: - self._focused_view = view - - def clearFocus(self) -> None: - self._focused_view = None - - def isFocusCycleEnabled(self) -> bool: - return self._focus_cycle_enabled - - def setFocusCycleEnabled(self, enabled: bool) -> None: - self._focus_cycle_enabled = enabled - - def requestFocus(self, view: Any, direction: int = 0) -> bool: - self._focused_view = view - return True - - def toString(self) -> str: - return f"FocusManager(focused={self._focused_view is not None})" - - -# ═══════════════════════════════════════════════════════════════ -# LauncherApps -# ═══════════════════════════════════════════════════════════════ - -class LauncherApps(Stub): - """LauncherApps — 启动器应用管理器""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._callbacks: List[Any] = [] - self._shortcuts: Dict[str, List[Any]] = {} - - def getActivityList(self, packageName: str, user: Any) -> List[Any]: - return [] - - def getApplicationList(self, user: Any) -> List[Any]: - return [] - - def startAppDetails(self, component: Any, user: Any, sourceBounds: Any, opts: Any) -> None: - pass - - def startMainActivity(self, component: Any, user: Any, sourceBounds: Any, opts: Any) -> None: - pass - - def getShortcutConfigActivityList(self, packageName: str, user: Any) -> List[Any]: - return [] - - def hasShortcutHostPermission(self) -> bool: - return True - - def getShortcuts(self, filter_: Any, user: Any) -> List[Any]: - return [] - - def pinShortcuts(self, packageName: str, shortcutIds: List[str], user: Any) -> bool: - return True - - def registerCallback(self, callback: Any) -> None: - if callback not in self._callbacks: - self._callbacks.append(callback) - - def unregisterCallback(self, callback: Any) -> None: - if callback in self._callbacks: - self._callbacks.remove(callback) - - def toString(self) -> str: - return f"LauncherApps(callbacks={len(self._callbacks)})" - - -# ═══════════════════════════════════════════════════════════════ -# MediaRouter -# ═══════════════════════════════════════════════════════════════ - -class MediaRouter(Stub): - """MediaRouter — 媒体路由管理器""" - - ROUTE_TYPE_LIVE_AUDIO = 1 - ROUTE_TYPE_LIVE_VIDEO = 2 - ROUTE_TYPE_REMOTE_DISPLAY = 4 - ROUTE_TYPE_USER = 8 - ROUTE_TYPE_ANY = 0x800000 - - UNSELECT_REASON_UNKNOWN = 0 - UNSELECT_REASON_ROUTE_CHANGED = 1 - UNSELECT_REASON_DISCONNECTED = 2 - UNSELECT_REASON_STOPPED = 3 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._routes: List[Any] = [] - self._selected_route: Optional[Any] = None - self._default_route: Optional[Any] = None - self._callbacks: List[Any] = [] - - def getDefaultRoute(self) -> Optional[Any]: - return self._default_route - - def getSelectedRoute(self, type_: int) -> Optional[Any]: - return self._selected_route - - def selectRoute(self, type_: int, route: Any) -> None: - self._selected_route = route - - def addCallback(self, types: int, cb: Any) -> None: - if cb not in self._callbacks: - self._callbacks.append(cb) - - def removeCallback(self, cb: Any) -> None: - if cb in self._callbacks: - self._callbacks.remove(cb) - - def getRoutes(self) -> List[Any]: - return list(self._routes) - - def isUserRouteAllowed(self) -> bool: - return True - - def toString(self) -> str: - return f"MediaRouter(routes={len(self._routes)})" - - -# ═══════════════════════════════════════════════════════════════ -# NetworkPolicyManager -# ═══════════════════════════════════════════════════════════════ - -class NetworkPolicyManager(Stub): - """NetworkPolicyManager — 网络策略管理器""" - - POLICY_NONE = 0 - POLICY_REJECT_METERED_BACKGROUND = 1 - POLICY_ALLOW_METERED_BACKGROUND = 4 - - RULE_ALLOW_ALL = 0 - RULE_REJECT_METERED = 1 - RULE_REJECT_ALL = 2 - RULE_TEMPORARY_ALLOW_METERED = 3 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._uid_policies: Dict[int, int] = {} - self._uid_rules: Dict[int, int] = {} - self._restrict_background = False - - def getUidPolicy(self, uid: int) -> int: - return self._uid_policies.get(uid, NetworkPolicyManager.POLICY_NONE) - - def setUidPolicy(self, uid: int, policy: int) -> None: - self._uid_policies[uid] = policy - - def getUidsWithPolicy(self, policy: int) -> List[int]: - return [uid for uid, p in self._uid_policies.items() if p == policy] - - def isUidNetworkingBlocked(self, uid: int, metered: bool) -> bool: - return False - - def registerListener(self, listener: Any) -> None: - pass - - def unregisterListener(self, listener: Any) -> None: - pass - - def toString(self) -> str: - return f"NetworkPolicyManager(uids={len(self._uid_policies)})" - - -# ═══════════════════════════════════════════════════════════════ -# NotificationManager (扩展) -# ═══════════════════════════════════════════════════════════════ - -class NotificationManagerExt(Stub): - """NotificationManager (Extension) — 额外通知管理器功能""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - - def getSnoozeIntentForChannel(self, channelId: str) -> Optional[Any]: - return None - - def matchesCallFilter(self, address: Any) -> bool: - return True - - def getActiveNotificationsFromBinder(self) -> List[Any]: - return [] - - def getHistoricalNotifications(self) -> List[Any]: - return [] - - def canNotifyAsPackage(self, pkg: str) -> bool: - return True - - def setNotificationDelegate(self, delegate: str) -> None: - pass - - def getNotificationDelegate(self) -> Optional[str]: - return None - - def updateNotificationChannelGroup(self, group: Any) -> None: - pass - - def toString(self) -> str: - return "NotificationManager(Extension)" - - -# ═══════════════════════════════════════════════════════════════ -# NsdManager (quick reference for net_nsd 模块) -# ═══════════════════════════════════════════════════════════════ - -class NsdManagerQuick(Stub): - """NsdManager (Quick Reference)""" - - PROTOCOL_DNS_SD = 0 - FAILURE_ALREADY_ACTIVE = 3 - FAILURE_INTERNAL_ERROR = 0 - FAILURE_MAX_LIMIT = 4 - FAILURE_SERVICE_NOT_FOUND = 1 - FAILURE_SERVICE_NOT_RUNNING = 2 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - - def discoverServices(self, serviceType: str, protocolType: int, listener: Any) -> None: - pass - - def stopServiceDiscovery(self, listener: Any) -> None: - pass - - def registerService(self, serviceInfo: Any, protocolType: int, listener: Any) -> None: - pass - - def unregisterService(self, listener: Any) -> None: - pass - - def resolveService(self, serviceInfo: Any, listener: Any) -> None: - pass - - def toString(self) -> str: - return "NsdManager" - - -# ═══════════════════════════════════════════════════════════════ -# RestrictionManager -# ═══════════════════════════════════════════════════════════════ - -class RestrictionManager(Stub): - """RestrictionManager — 限制管理器""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._restrictions: Dict[str, Any] = {} - self._lock = threading.Lock() - - def getApplicationRestrictions(self, packageName: str) -> Any: - return None - - def setApplicationRestrictions(self, packageName: str, restrictions: Any) -> None: - pass - - def hasRestrictionProvider(self) -> bool: - return False - - def getManifestRestrictions(self, packageName: str) -> List[Any]: - return [] - - def getRestrictionsProvider(self, packageName: str) -> Optional[Any]: - return None - - def notifyPermissionResponse(self, packageName: str, response: Any) -> None: - pass - - def toString(self) -> str: - return "RestrictionManager" - - -# ═══════════════════════════════════════════════════════════════ -# SubscriptionManager -# ═══════════════════════════════════════════════════════════════ - -class SubscriptionManager(Stub): - """SubscriptionManager — 订阅管理器""" - - DEFAULT_SUBSCRIPTION_ID = -1 - INVALID_SUBSCRIPTION_ID = -2 - ACTIVE_DATA_SUBSCRIPTION_ID = -3 - - EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX" - EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._subscriptions: List[Any] = [] - - def getActiveSubscriptionInfoList(self) -> List[Any]: - return list(self._subscriptions) - - def getActiveSubscriptionInfo(self, subId: int) -> Optional[Any]: - for sub in self._subscriptions: - if getattr(sub, "subscriptionId", None) == subId: - return sub - return None - - def getActiveSubscriptionInfoForSimSlotIndex(self, slotIndex: int) -> Optional[Any]: - for sub in self._subscriptions: - if getattr(sub, "simSlotIndex", None) == slotIndex: - return sub - return None - - def getDefaultSubscriptionId(self) -> int: - return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID - - def setDefaultSubscriptionId(self, subId: int) -> None: - pass - - def addOnSubscriptionsChangedListener(self, listener: Any) -> None: - pass - - def removeOnSubscriptionsChangedListener(self, listener: Any) -> None: - pass - - def getSlotIndex(self, subscriptionId: int) -> int: - return -1 - - def toString(self) -> str: - return f"SubscriptionManager(subscriptions={len(self._subscriptions)})" - - -# ═══════════════════════════════════════════════════════════════ -# WallpaperManager (扩展) -# ═══════════════════════════════════════════════════════════════ - -class WallpaperManagerExt(Stub): - """WallpaperManager (Extension)""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - - def getWallpaperIdForWhom(self, which: int) -> int: - return -1 - - def getWallpaperUserId(self) -> int: - return 0 - - def setWallpaperOffsets(self, windowToken: Any, xOffset: float, yOffset: float) -> None: - pass - - def clearWallpaperOffsets(self, windowToken: Any) -> None: - pass - - def hasResourceWallpaper(self, resid: int) -> bool: - return False - - def toString(self) -> str: - return "WallpaperManager(Extension)" - - -# ═══════════════════════════════════════════════════════════════ -# WifiP2pManager / WifiP2pDevice / WifiP2pGroup / WifiP2pConfig -# ═══════════════════════════════════════════════════════════════ - -class WifiP2pDevice(Stub): - """WifiP2pDevice — WiFi P2P 设备""" - - CONNECTED = 0 - INVITED = 1 - FAILED = 2 - AVAILABLE = 3 - UNAVAILABLE = 4 - - def __init__(self): - super().__init__() - self.deviceName: Optional[str] = None - self.deviceAddress: Optional[str] = None - self.primaryDeviceType: Optional[str] = None - self.secondaryDeviceType: Optional[str] = None - self.status = WifiP2pDevice.AVAILABLE - self.wpsConfigMethodsSupported = 0 - self.groupCapabilityFlags = 0 - self.deviceCapabilityFlags = 0 - self.serviceDiscoveryCapable = False - self.isGroupOwner = False - self._supports_wfd = False - self._interface_address: Optional[str] = None - - def isGroupOwner(self) -> bool: - return self.isGroupOwner - - def wpsDisplaySupported(self) -> bool: - return (self.wpsConfigMethodsSupported & 0x1000) != 0 - - def wpsKeypadSupported(self) -> bool: - return (self.wpsConfigMethodsSupported & 0x0100) != 0 - - def wpsPbcSupported(self) -> bool: - return (self.wpsConfigMethodsSupported & 0x0040) != 0 - - def equals(self, other: Any) -> bool: - if isinstance(other, WifiP2pDevice): - return self.deviceAddress == other.deviceAddress - return False - - def hashCode(self) -> int: - return hash(self.deviceAddress) & 0x7FFFFFFF - - def toString(self) -> str: - return f"WifiP2pDevice(name={self.deviceName}, addr={self.deviceAddress}, status={self.status})" - - -class WifiP2pGroup(Stub): - """WifiP2pGroup — WiFi P2P 组""" - - def __init__(self): - super().__init__() - self._network_name: Optional[str] = None - self._owner: Optional[WifiP2pDevice] = None - self._is_group_owner = False - self._passphrase: Optional[str] = None - self._interface: Optional[str] = None - self._clients: List[WifiP2pDevice] = [] - self._frequency = 0 - - def getNetworkName(self) -> Optional[str]: - return self._network_name - - def isGroupOwner(self) -> bool: - return self._is_group_owner - - def getOwner(self) -> Optional[WifiP2pDevice]: - return self._owner - - def getPassphrase(self) -> Optional[str]: - return self._passphrase - - def getInterface(self) -> Optional[str]: - return self._interface - - def getClientList(self) -> List[WifiP2pDevice]: - return list(self._clients) - - def getFrequency(self) -> int: - return self._frequency - - def addClient(self, device: WifiP2pDevice) -> None: - if device not in self._clients: - self._clients.append(device) - - def removeClient(self, device: WifiP2pDevice) -> None: - if device in self._clients: - self._clients.remove(device) - - def toString(self) -> str: - return f"WifiP2pGroup(net={self._network_name}, clients={len(self._clients)})" - - -class WifiP2pConfig(Stub): - """WifiP2pConfig — WiFi P2P 配置""" - - class Builder(Stub): - """WifiP2pConfig.Builder — 构建器""" - - def __init__(self): - super().__init__() - self._config = WifiP2pConfig() - - def setDeviceAddress(self, address: str) -> "WifiP2pConfig.Builder": - self._config._device_address = address - return self - - def setDeviceName(self, name: str) -> "WifiP2pConfig.Builder": - self._config._device_name = name - return self - - def setGroupOwnerIntent(self, intent: int) -> "WifiP2pConfig.Builder": - self._config._group_owner_intent = intent - return self - - def setNetworkName(self, name: str) -> "WifiP2pConfig.Builder": - self._config._network_name = name - return self - - def setPassphrase(self, passphrase: str) -> "WifiP2pConfig.Builder": - self._config._passphrase = passphrase - return self - - def enablePersistentMode(self, enabled: bool) -> "WifiP2pConfig.Builder": - self._config._persistent_mode = enabled - return self - - def build(self) -> "WifiP2pConfig": - return self._config - - def __init__(self): - super().__init__() - self._device_address: Optional[str] = None - self._device_name: Optional[str] = None - self._group_owner_intent = -1 - self._network_name: Optional[str] = None - self._passphrase: Optional[str] = None - self._persistent_mode = False - self._wps_config = 0 - self._net_id = -1 - - def getDeviceAddress(self) -> Optional[str]: - return self._device_address - - def getDeviceName(self) -> Optional[str]: - return self._device_name - - def getGroupOwnerIntent(self) -> int: - return self._group_owner_intent - - def isPersistentMode(self) -> bool: - return self._persistent_mode - - def toString(self) -> str: - return f"WifiP2pConfig(addr={self._device_address}, name={self._device_name})" - - -class WifiP2pManager(Stub): - """WifiP2pManager — WiFi P2P 管理器""" - - WIFI_P2P_STATE_ENABLED = 2 - WIFI_P2P_STATE_DISABLED = 1 - WIFI_P2P_PEERS_CHANGED_ACTION = "android.net.wifi.p2p.PEERS_CHANGED" - WIFI_P2P_STATE_CHANGED_ACTION = "android.net.wifi.p2p.STATE_CHANGED" - WIFI_P2P_CONNECTION_CHANGED_ACTION = "android.net.wifi.p2p.CONNECTION_STATE_CHANGE" - WIFI_P2P_THIS_DEVICE_CHANGED_ACTION = "android.net.wifi.p2p.THIS_DEVICE_CHANGED" - WIFI_P2P_DISCOVERY_CHANGED_ACTION = "android.net.wifi.p2p.DISCOVERY_STATE_CHANGE" - WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION = "android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED" - - BUSY = 2 - ERROR = 0 - P2P_UNSUPPORTED = 1 - NO_SERVICE_REQUESTS = 3 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._peers: List[WifiP2pDevice] = [] - self._services: List[Any] = [] - self._discovery_running = False - self._this_device: Optional[WifiP2pDevice] = None - self._groups: List[WifiP2pGroup] = [] - self._listeners: Dict[str, List[Any]] = {} - - def initialize(self, context: Any, looper: Any, listener: Any) -> "WifiP2pManager": - return self - - def discoverPeers(self, channel: Any, listener: Any) -> None: - self._discovery_running = True - if listener: - listener.onSuccess() - - def stopPeerDiscovery(self, channel: Any, listener: Any) -> None: - self._discovery_running = False - if listener: - listener.onSuccess() - - def requestPeers(self, channel: Any, listener: Any) -> None: - if listener: - listener.onPeersAvailable(self._peers) - - def connect(self, channel: Any, config: WifiP2pConfig, listener: Any) -> None: - if listener: - listener.onSuccess() - - def cancelConnect(self, channel: Any, listener: Any) -> None: - if listener: - listener.onSuccess() - - def createGroup(self, channel: Any, listener: Any) -> None: - if listener: - listener.onSuccess() - - def removeGroup(self, channel: Any, listener: Any) -> None: - if listener: - listener.onSuccess() - - def requestGroupInfo(self, channel: Any, listener: Any) -> None: - if listener and self._groups: - listener.onGroupInfoAvailable(self._groups[0] if self._groups else None) - - def requestConnectionInfo(self, channel: Any, listener: Any) -> None: - if listener: - listener.onConnectionInfoAvailable(None) - - def setDeviceName(self, channel: Any, deviceName: str, listener: Any) -> None: - if listener: - listener.onSuccess() - - def setWpsPbc(self, channel: Any, iface: str, listener: Any) -> None: - if listener: - listener.onSuccess() - - def setWpsPin(self, channel: Any, iface: str, pin: str, listener: Any) -> None: - if listener: - listener.onSuccess() - - def getAvailableServiceInfo(self, device: WifiP2pDevice) -> Optional[Any]: - return None - - def addServiceRequest(self, channel: Any, req: Any, listener: Any) -> None: - if listener: - listener.onSuccess() - - def removeServiceRequest(self, channel: Any, req: Any, listener: Any) -> None: - if listener: - listener.onSuccess() - - def setServiceResponseListener(self, channel: Any, listener: Any) -> None: - pass - - def clearServiceResponses(self, channel: Any, listener: Any) -> None: - if listener: - listener.onSuccess() - - def clearLocalServices(self, channel: Any, listener: Any) -> None: - if listener: - listener.onSuccess() - - def requestServiceInfo(self, channel: Any, device: WifiP2pDevice, listener: Any) -> None: - if listener: - listener.onServiceInfoAvailable(None) - - def getWifiP2pDeviceList(self) -> List[WifiP2pDevice]: - return list(self._peers) - - def isDiscoveryRunning(self) -> bool: - return self._discovery_running - - def toString(self) -> str: - return f"WifiP2pManager(peers={len(self._peers)}, discovery={self._discovery_running})" - - -# ═══════════════════════════════════════════════════════════════ -# WindowManager -# ═══════════════════════════════════════════════════════════════ - -class WindowManager(Stub): - """WindowManager — 窗口管理器""" - - # LayoutParams 类型 - TYPE_BASE_APPLICATION = 1 - TYPE_APPLICATION = 2 - TYPE_APPLICATION_STARTING = 3 - TYPE_APPLICATION_PANEL = 1000 - TYPE_APPLICATION_MEDIA = 1001 - TYPE_APPLICATION_SUB_PANEL = 1002 - TYPE_APPLICATION_ATTACHED_DIALOG = 1003 - TYPE_APPLICATION_OVERLAY = 1004 - TYPE_STATUS_BAR = 2000 - TYPE_SYSTEM_ALERT = 2003 - TYPE_SYSTEM_OVERLAY = 2006 - TYPE_TOAST = 2005 - TYPE_PHONE = 2002 - TYPE_PRIORITY_PHONE = 2007 - TYPE_SYSTEM_DIALOG = 2008 - TYPE_KEYGUARD = 2004 - TYPE_INPUT_METHOD = 2011 - TYPE_INPUT_METHOD_DIALOG = 2012 - TYPE_WALLPAPER = 2013 - TYPE_NAVIGATION_BAR = 2019 - TYPE_DREAM = 2024 - TYPE_ACCESSIBILITY_OVERLAY = 2032 - TYPE_VOLUME_OVERLAY = 2033 - TYPE_SECURE_SYSTEM_OVERLAY = 2034 - - # LayoutParams 标志 - FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001 - FLAG_DIM_BEHIND = 0x00000002 - FLAG_BLUR_BEHIND = 0x00000004 - FLAG_NOT_FOCUSABLE = 0x00000008 - FLAG_NOT_TOUCHABLE = 0x00000010 - FLAG_NOT_TOUCH_MODAL = 0x00000020 - FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040 - FLAG_KEEP_SCREEN_ON = 0x00000080 - FLAG_LAYOUT_IN_SCREEN = 0x00000100 - FLAG_LAYOUT_NO_LIMITS = 0x00000200 - FLAG_FULLSCREEN = 0x00000400 - FLAG_FORCE_NOT_FULLSCREEN = 0x00000800 - FLAG_DITHER = 0x00001000 - FLAG_SECURE = 0x00002000 - FLAG_SCALED = 0x00004000 - FLAG_IGNORE_CHEEK_PRESSES = 0x00008000 - FLAG_LAYOUT_INSET_DECOR = 0x00010000 - FLAG_ALT_FOCUSABLE_IM = 0x00020000 - FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000 - FLAG_SHOW_WHEN_LOCKED = 0x00080000 - FLAG_SHOW_WALLPAPER = 0x00100000 - FLAG_TURN_SCREEN_ON = 0x00200000 - FLAG_DISMISS_KEYGUARD = 0x00400000 - FLAG_SPLIT_TOUCH = 0x00800000 - FLAG_HARDWARE_ACCELERATED = 0x01000000 - FLAG_LOCAL_FOCUS_MODE = 0x10000000 - FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x20000000 - FLAG_TRANSLUCENT_STATUS = 0x04000000 - FLAG_TRANSLUCENT_NAVIGATION = 0x08000000 - - # 软输入模式 - SOFT_INPUT_STATE_UNSPECIFIED = 0 - SOFT_INPUT_STATE_UNCHANGED = 1 - SOFT_INPUT_STATE_HIDDEN = 2 - SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3 - SOFT_INPUT_STATE_VISIBLE = 4 - SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5 - SOFT_INPUT_ADJUST_RESIZE = 0x10 - SOFT_INPUT_ADJUST_PAN = 0x20 - SOFT_INPUT_ADJUST_NOTHING = 0x30 - SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00 - SOFT_INPUT_IS_FORWARD_NAVIGATION = 0x100 - - INVALID_DISPLAY = -1 - DOCKED_INVALID = -1 - DOCKED_LEFT = 1 - DOCKED_TOP = 2 - DOCKED_RIGHT = 3 - DOCKED_BOTTOM = 4 - - class LayoutParams(Stub): - """WindowManager.LayoutParams — 窗口布局参数""" - - MATCH_PARENT = -1 - WRAP_CONTENT = -2 - - def __init__(self, w: int = -1, h: int = -1, type_: int = 2, flags: int = 0): - super().__init__() - self.width = w - self.height = h - self.type = type_ - self.flags = flags - self.x = 0 - self.y = 0 - self.horizontalWeight = 0.0 - self.verticalWeight = 0.0 - self.horizontalMargin = 0.0 - self.verticalMargin = 0.0 - self.gravity = 0 - self.softInputMode = WindowManager.SOFT_INPUT_STATE_UNSPECIFIED - self.windowAnimations = -1 - self.alpha = 1.0 - self.dimAmount = 0.0 - self.screenOrientation = -1 - self.screenBrightness = -1.0 - self.buttonBrightness = -1.0 - self.token: Optional[Any] = None - self.packageName: Optional[str] = None - self.format = -1 - self.systemUiVisibility = 0 - self.subtreeSystemUiVisibility = 0 - self.preferredRefreshRate: float = 0.0 - self.colorMode = 0 - - def toString(self) -> str: - return f"LayoutParams({self.width}x{self.height}, type={self.type}, flags=0x{self.flags:x})" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._default_display: Optional[Any] = None - self._current_window_focus: Optional[Any] = None - self._window_views: List[Any] = [] - - def getDefaultDisplay(self) -> Optional[Any]: - return self._default_display - - def getCurrentWindowFocus(self) -> Optional[Any]: - return self._current_window_focus - - def addView(self, view: Any, params: "WindowManager.LayoutParams") -> None: - self._window_views.append((view, params)) - - def updateViewLayout(self, view: Any, params: "WindowManager.LayoutParams") -> None: - for i, (v, p) in enumerate(self._window_views): - if v is view: - self._window_views[i] = (view, params) - break - - def removeView(self, view: Any) -> None: - self._window_views = [(v, p) for v, p in self._window_views if v is not view] - - def removeViewImmediate(self, view: Any) -> None: - self.removeView(view) - - def getWindowViews(self) -> List[Any]: - return [v for v, _ in self._window_views] - - def toString(self) -> str: - return f"WindowManager(views={len(self._window_views)})" - - -WindowManager_LayoutParams = WindowManager.LayoutParams \ No newline at end of file diff --git a/android_pyapi/android/media.py b/android_pyapi/android/media.py deleted file mode 100644 index 5aca399..0000000 --- a/android_pyapi/android/media.py +++ /dev/null @@ -1,2525 +0,0 @@ -""" -Android Media 组件 — MediaPlayer, MediaRecorder, SoundPool, AudioManager 等 -方法签名完全对齐 Android SDK -""" - -import io -import json -import math -import os -import subprocess -import time -import struct -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError, android_context, HAS_PIL, HAS_PYMUPDF, resolve_android_path -from .graphics import PointF - -# Linux 真实实现导入 -try: - from .._linux_impl import IS_LINUX, LinuxAudio, HAS_AMIXER - _USE_LINUX = IS_LINUX -except ImportError: - _USE_LINUX = False - - -# ═══════════════════════════════════════════════════════════════ -# ffprobe / ffmpeg 辅助函数 -# ═══════════════════════════════════════════════════════════════ - -def _ffprobe_metadata(path: str) -> Optional[Dict[str, Any]]: - """用 ffprobe 获取媒体文件元数据,返回解析后的 JSON 字典。 - 失败时返回 None。""" - try: - result = subprocess.run( - ["ffprobe", "-v", "quiet", "-print_format", "json", - "-show_format", "-show_streams", path], - capture_output=True, text=True, timeout=30, - ) - if result.returncode != 0: - return None - return json.loads(result.stdout) - except (FileNotFoundError, subprocess.SubprocessError, - json.JSONDecodeError, OSError): - return None - - -def _ffprobe_duration_ms(path: str) -> int: - """用 ffprobe 获取时长(毫秒),失败返回 0。""" - data = _ffprobe_metadata(path) - if not data: - return 0 - try: - fmt = data.get("format", {}) - duration = fmt.get("duration") - if duration is not None: - return int(float(duration) * 1000) - # 回退:从流中找 - for stream in data.get("streams", []): - d = stream.get("duration") - if d is not None: - return int(float(d) * 1000) - except (ValueError, TypeError): - pass - return 0 - - -def _ffprobe_video_size(path: str) -> Tuple[int, int]: - """用 ffprobe 获取视频分辨率 (width, height),失败返回 (0, 0)。""" - data = _ffprobe_metadata(path) - if not data: - return (0, 0) - try: - for stream in data.get("streams", []): - if stream.get("codec_type") == "video": - w = int(stream.get("width", 0)) - h = int(stream.get("height", 0)) - if w > 0 and h > 0: - return (w, h) - except (ValueError, TypeError): - pass - return (0, 0) - - -def _ffmpeg_extract_frame(path: str, time_us: int = -1, - width: int = 0, height: int = 0) -> Optional[Any]: - """用 ffmpeg 提取指定时间点的视频帧,返回 PIL Image。 - time_us < 0 表示提取第一帧。失败返回 None。""" - if not HAS_PIL: - return None - try: - from PIL import Image - cmd = ["ffmpeg", "-v", "quiet"] - if time_us is not None and time_us >= 0: - cmd += ["-ss", str(time_us / 1_000_000.0)] - cmd += ["-i", path, "-frames:v", "1", "-f", "image2pipe", - "-vcodec", "png", "-"] - if width > 0 and height > 0: - # 在解码后缩放 - cmd = ["ffmpeg", "-v", "quiet"] - if time_us is not None and time_us >= 0: - cmd += ["-ss", str(time_us / 1_000_000.0)] - cmd += ["-i", path, "-frames:v", "1", "-vf", - f"scale={width}:{height}", "-f", "image2pipe", - "-vcodec", "png", "-"] - result = subprocess.run(cmd, capture_output=True, timeout=30) - if result.returncode != 0 or not result.stdout: - return None - return Image.open(io.BytesIO(result.stdout)) - except (FileNotFoundError, subprocess.SubprocessError, OSError): - return None - except Exception: - return None - - -def _ffmpeg_extract_cover(path: str) -> Optional[bytes]: - """用 ffmpeg 提取嵌入的封面图片,返回原始字节。 - 失败返回 None。""" - try: - # 尝试提取附加图片流(封面) - cmd = ["ffmpeg", "-v", "quiet", "-i", path, - "-map", "0:v", "-frames:v", "1", "-f", "image2pipe", - "-vcodec", "png", "-"] - result = subprocess.run(cmd, capture_output=True, timeout=30) - if result.returncode == 0 and result.stdout: - return result.stdout - return None - except (FileNotFoundError, subprocess.SubprocessError, OSError): - return None - except Exception: - return None - - -# ═══════════════════════════════════════════════════════════════ -# AudioAttributes -# ═══════════════════════════════════════════════════════════════ - -class AudioAttributes(Stub): - """AudioAttributes — 音频属性""" - - CONTENT_TYPE_UNKNOWN = 0 - CONTENT_TYPE_SPEECH = 1 - CONTENT_TYPE_MUSIC = 2 - CONTENT_TYPE_MOVIE = 3 - CONTENT_TYPE_SONIFICATION = 4 - - USAGE_UNKNOWN = 0 - USAGE_MEDIA = 1 - USAGE_VOICE_COMMUNICATION = 2 - USAGE_VOICE_COMMUNICATION_SIGNALLING = 3 - USAGE_ALARM = 4 - USAGE_NOTIFICATION = 5 - USAGE_NOTIFICATION_RINGTONE = 6 - USAGE_GAME = 14 - USAGE_ASSISTANCE_ACCESSIBILITY = 11 - USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12 - USAGE_ASSISTANCE_SONIFICATION = 13 - - FLAG_AUDIBILITY_ENFORCED = 1 - FLAG_SECURE = 2 - FLAG_SCO = 4 - FLAG_BEACON = 8 - FLAG_BYPASS_INTERRUPTION_POLICY = 16 - FLAG_BYPASS_MUTE = 32 - FLAG_LOW_LATENCY = 64 - FLAG_DEEP_BUFFER = 128 - - class Builder(Stub): - """AudioAttributes.Builder — 音频属性构建器""" - - def __init__(self): - super().__init__() - self._content_type = AudioAttributes.CONTENT_TYPE_UNKNOWN - self._usage = AudioAttributes.USAGE_UNKNOWN - self._flags = 0 - self._legacy_stream_type = -1 - - def setContentType(self, contentType: int) -> "AudioAttributes.Builder": - self._content_type = contentType - return self - - def setUsage(self, usage: int) -> "AudioAttributes.Builder": - self._usage = usage - return self - - def setFlags(self, flags: int) -> "AudioAttributes.Builder": - self._flags = flags - return self - - def setLegacyStreamType(self, streamType: int) -> "AudioAttributes.Builder": - self._legacy_stream_type = streamType - return self - - def build(self) -> "AudioAttributes": - attrs = AudioAttributes() - attrs._content_type = self._content_type - attrs._usage = self._usage - attrs._flags = self._flags - attrs._legacy_stream_type = self._legacy_stream_type - return attrs - - def __init__(self): - super().__init__() - self._content_type = AudioAttributes.CONTENT_TYPE_UNKNOWN - self._usage = AudioAttributes.USAGE_UNKNOWN - self._flags = 0 - self._legacy_stream_type = -1 - - def getContentType(self) -> int: - return self._content_type - - def getUsage(self) -> int: - return self._usage - - def getFlags(self) -> int: - return self._flags - - def getLegacyStreamType(self) -> int: - if self._legacy_stream_type >= 0: - return self._legacy_stream_type - usage_map = { - AudioAttributes.USAGE_MEDIA: 3, - AudioAttributes.USAGE_ALARM: 4, - AudioAttributes.USAGE_NOTIFICATION: 5, - AudioAttributes.USAGE_NOTIFICATION_RINGTONE: 2, - AudioAttributes.USAGE_VOICE_COMMUNICATION: 0, - AudioAttributes.USAGE_GAME: 3, - } - return usage_map.get(self._usage, 3) - - -# ═══════════════════════════════════════════════════════════════ -# AudioFormat -# ═══════════════════════════════════════════════════════════════ - -class AudioFormat(Stub): - """AudioFormat — 音频格式""" - - ENCODING_INVALID = 0 - ENCODING_DEFAULT = 1 - ENCODING_PCM_16BIT = 2 - ENCODING_PCM_8BIT = 3 - ENCODING_PCM_FLOAT = 4 - ENCODING_AC3 = 5 - ENCODING_E_AC3 = 6 - ENCODING_DTS = 7 - ENCODING_DTS_HD = 8 - ENCODING_MP3 = 9 - ENCODING_AAC = 10 - ENCODING_AAC_LC = 11 - ENCODING_AAC_HE_V1 = 12 - ENCODING_AAC_HE_V2 = 13 - ENCODING_AAC_ELD = 14 - ENCODING_AAC_XHE = 15 - ENCODING_AMRNB = 16 - ENCODING_AMRWB = 17 - ENCODING_OPUS = 18 - - CHANNEL_OUT_DEFAULT = 0x000 - CHANNEL_OUT_MONO = 0x004 - CHANNEL_OUT_STEREO = 0x00C - CHANNEL_OUT_QUAD = 0x01C - CHANNEL_OUT_SURROUND = 0x03C - CHANNEL_OUT_5POINT1 = 0x0FC - CHANNEL_OUT_7POINT1 = 0x3FC - - CHANNEL_IN_DEFAULT = 0x000 - CHANNEL_IN_MONO = 0x010 - CHANNEL_IN_STEREO = 0x020 - - def __init__(self): - super().__init__() - self._sample_rate = 44100 - self._encoding = AudioFormat.ENCODING_PCM_16BIT - self._channel_mask = AudioFormat.CHANNEL_OUT_STEREO - self._channel_count = 2 - - def getSampleRate(self) -> int: - return self._sample_rate - - def getEncoding(self) -> int: - return self._encoding - - def getChannelMask(self) -> int: - return self._channel_mask - - def getChannelCount(self) -> int: - return self._channel_count - - @staticmethod - def toChannelMask(channelCount: int) -> int: - return AudioFormat.CHANNEL_OUT_MONO if channelCount == 1 else AudioFormat.CHANNEL_OUT_STEREO - - -# ═══════════════════════════════════════════════════════════════ -# MediaPlayer -# ═══════════════════════════════════════════════════════════════ - -class MediaPlayer(Stub): - """MediaPlayer — 媒体播放器""" - - MEDIA_INFO_UNKNOWN = 1 - MEDIA_INFO_VIDEO_TRACK_LAGGING = 700 - MEDIA_INFO_VIDEO_RENDERING_START = 3 - MEDIA_INFO_BUFFERING_START = 701 - MEDIA_INFO_BUFFERING_END = 702 - MEDIA_INFO_BAD_INTERLEAVING = 800 - MEDIA_INFO_NOT_SEEKABLE = 801 - MEDIA_INFO_METADATA_UPDATE = 802 - MEDIA_INFO_AUDIO_NOT_PLAYING = 804 - MEDIA_INFO_VIDEO_NOT_PLAYING = 805 - MEDIA_INFO_SUBTITLE_TIMED_OUT = 902 - MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901 - - MEDIA_ERROR_UNKNOWN = 1 - MEDIA_ERROR_SERVER_DIED = 100 - MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200 - MEDIA_ERROR_TIMED_OUT = -110 - MEDIA_ERROR_IO = -1004 - MEDIA_ERROR_MALFORMED = -1007 - MEDIA_ERROR_UNSUPPORTED = -1010 - - # OnPreparedListener - class OnPreparedListener(Stub): - def onPrepared(self, mp: "MediaPlayer"): - pass - - # OnCompletionListener - class OnCompletionListener(Stub): - def onCompletion(self, mp: "MediaPlayer"): - pass - - # OnErrorListener - class OnErrorListener(Stub): - def onError(self, mp: "MediaPlayer", what: int, extra: int) -> bool: - return False - - # OnInfoListener - class OnInfoListener(Stub): - def onInfo(self, mp: "MediaPlayer", what: int, extra: int) -> bool: - return False - - # OnSeekCompleteListener - class OnSeekCompleteListener(Stub): - def onSeekComplete(self, mp: "MediaPlayer"): - pass - - # OnVideoSizeChangedListener - class OnVideoSizeChangedListener(Stub): - def onVideoSizeChanged(self, mp: "MediaPlayer", width: int, height: int): - pass - - def __init__(self): - super().__init__() - self._data_source = None - self._resolved_path = None - self._prepared = False - self._playing = False - self._looping = False - self._volume_left = 1.0 - self._volume_right = 1.0 - self._current_position = 0 - self._duration = 0 - self._video_width = 0 - self._video_height = 0 - self._screen_on = False - self._wake_mode = 0 - self._audio_attributes = None - self._display = None - self._surface = None - self._released = False - self._start_time = 0.0 # 用于模拟播放进度 - - # Listeners - self._on_prepared_listener = None - self._on_completion_listener = None - self._on_error_listener = None - self._on_info_listener = None - self._on_seek_complete_listener = None - self._on_video_size_changed_listener = None - - def setDataSource(self, path: str = None, uri=None, headers: Dict = None): - self._data_source = path or str(uri) if uri is not None else path - if self._data_source: - self._resolved_path = resolve_android_path(self._data_source) - - def _load_media_info(self): - """用 ffprobe 加载时长和分辨率信息(try/except 保护)。""" - if not self._resolved_path: - return - if not os.path.exists(self._resolved_path): - return - try: - duration = _ffprobe_duration_ms(self._resolved_path) - if duration > 0: - self._duration = duration - except Exception: - pass - try: - w, h = _ffprobe_video_size(self._resolved_path) - if w > 0 and h > 0: - self._video_width = w - self._video_height = h - if self._on_video_size_changed_listener: - self._on_video_size_changed_listener.onVideoSizeChanged( - self, w, h) - except Exception: - pass - - def prepare(self): - if self._released: - return - if self._resolved_path and not os.path.exists(self._resolved_path): - if self._on_error_listener: - self._on_error_listener.onError( - self, MediaPlayer.MEDIA_ERROR_UNKNOWN, -1004) - raise IOError(f"数据源文件不存在: {self._resolved_path}") - self._load_media_info() - self._prepared = True - - def prepareAsync(self): - if self._released: - return - if self._resolved_path and not os.path.exists(self._resolved_path): - if self._on_error_listener: - self._on_error_listener.onError( - self, MediaPlayer.MEDIA_ERROR_UNKNOWN, -1004) - return - self._load_media_info() - self._prepared = True - if self._on_prepared_listener: - self._on_prepared_listener.onPrepared(self) - - def start(self): - if self._released: - return - self._playing = True - self._start_time = time.time() - - def stop(self): - self._playing = False - - def pause(self): - if self._playing: - # 记录已播放的进度 - elapsed = int((time.time() - self._start_time) * 1000) - self._current_position = min( - self._current_position + elapsed, self._duration) - self._playing = False - - def reset(self): - self._playing = False - self._prepared = False - self._current_position = 0 - self._duration = 0 - self._video_width = 0 - self._video_height = 0 - self._data_source = None - self._resolved_path = None - - def release(self): - self._playing = False - self._prepared = False - self._released = True - self._data_source = None - self._resolved_path = None - self._on_prepared_listener = None - self._on_completion_listener = None - self._on_error_listener = None - self._on_info_listener = None - self._on_seek_complete_listener = None - self._on_video_size_changed_listener = None - - def seekTo(self, msec: int): - self._current_position = max(0, min(msec, self._duration)) - self._start_time = time.time() - if self._on_seek_complete_listener: - self._on_seek_complete_listener.onSeekComplete(self) - - def getCurrentPosition(self) -> int: - if self._playing and self._start_time > 0: - elapsed = int((time.time() - self._start_time) * 1000) - pos = self._current_position + elapsed - if self._duration > 0 and pos >= self._duration: - pos = self._duration - if self._looping: - self._current_position = 0 - self._start_time = time.time() - pos = 0 - else: - self._playing = False - if self._on_completion_listener: - self._on_completion_listener.onCompletion(self) - return pos - return self._current_position - - def getDuration(self) -> int: - if self._duration == 0 and self._resolved_path: - try: - self._duration = _ffprobe_duration_ms(self._resolved_path) - except Exception: - pass - return self._duration - - def isPlaying(self) -> bool: - return self._playing - - def isLooping(self) -> bool: - return self._looping - - def setLooping(self, looping: bool): - self._looping = looping - - def setVolume(self, leftVolume: float, rightVolume: float): - self._volume_left = leftVolume - self._volume_right = rightVolume - - def getVolume(self) -> Tuple[float, float]: - return (self._volume_left, self._volume_right) - - def setScreenOnWhilePlaying(self, screenOn: bool): - self._screen_on = screenOn - - def setWakeMode(self, context, mode: int): - self._wake_mode = mode - - def setAudioAttributes(self, attributes: AudioAttributes): - self._audio_attributes = attributes - - def setDisplay(self, surface_holder): - self._display = surface_holder - - def getVideoWidth(self) -> int: - if self._video_width == 0 and self._resolved_path: - try: - w, h = _ffprobe_video_size(self._resolved_path) - if w > 0: - self._video_width = w - self._video_height = h - except Exception: - pass - return self._video_width - - def getVideoHeight(self) -> int: - if self._video_height == 0 and self._resolved_path: - try: - w, h = _ffprobe_video_size(self._resolved_path) - if h > 0: - self._video_width = w - self._video_height = h - except Exception: - pass - return self._video_height - - def setOnPreparedListener(self, listener: "MediaPlayer.OnPreparedListener"): - self._on_prepared_listener = listener - - def setOnCompletionListener(self, listener: "MediaPlayer.OnCompletionListener"): - self._on_completion_listener = listener - - def setOnErrorListener(self, listener: "MediaPlayer.OnErrorListener"): - self._on_error_listener = listener - - def setOnInfoListener(self, listener: "MediaPlayer.OnInfoListener"): - self._on_info_listener = listener - - def setOnSeekCompleteListener(self, listener: "MediaPlayer.OnSeekCompleteListener"): - self._on_seek_complete_listener = listener - - def setOnVideoSizeChangedListener(self, listener: "MediaPlayer.OnVideoSizeChangedListener"): - self._on_video_size_changed_listener = listener - - @staticmethod - def create(context, resid: int = 0) -> "MediaPlayer": - mp = MediaPlayer() - mp._prepared = True - return mp - - -# ═══════════════════════════════════════════════════════════════ -# MediaRecorder -# ═══════════════════════════════════════════════════════════════ - -class MediaRecorder(Stub): - """MediaRecorder — 媒体录制器""" - - class AudioSource: - DEFAULT = 0 - MIC = 1 - VOICE_UPLINK = 2 - VOICE_DOWNLINK = 3 - VOICE_CALL = 4 - CAMCORDER = 5 - VOICE_RECOGNITION = 6 - VOICE_COMMUNICATION = 7 - REMOTE_SUBMIX = 8 - UNPROCESSED = 9 - - class VideoSource: - DEFAULT = 0 - CAMERA = 1 - SURFACE = 2 - - class OutputFormat: - DEFAULT = 0 - THREE_GPP = 1 - MPEG_4 = 2 - AMR_NB = 3 - AMR_WB = 4 - AAC_ADIF = 5 - AAC_ADTS = 6 - OUTPUT_FORMAT_RTP_AVP = 7 - MPEG_2_TS = 8 - WEBM = 9 - HEIF = 10 - OGG = 11 - - class AudioEncoder: - DEFAULT = 0 - AMR_NB = 1 - AMR_WB = 2 - AAC = 3 - HE_AAC = 4 - AAC_ELD = 5 - VORBIS = 6 - OPUS = 7 - LPCM = 8 - - class VideoEncoder: - DEFAULT = 0 - H_263 = 1 - H_264 = 2 - MPEG_4_SP = 3 - VP8 = 4 - HEVC = 5 - VP9 = 6 - AV1 = 7 - - def __init__(self): - super().__init__() - self._audio_source = 0 - self._video_source = 0 - self._output_format = 0 - self._audio_encoder = 0 - self._video_encoder = 0 - self._output_file = None - self._preview_display = None - self._recording = False - self._prepared = False - self._released = False - self._video_width = 0 - self._video_height = 0 - self._video_frame_rate = 0 - self._video_encoding_bit_rate = 0 - self._audio_encoding_bit_rate = 0 - self._max_duration = 0 - self._max_filesize = 0 - - def setAudioSource(self, audio_source: int): - self._audio_source = audio_source - - def setVideoSource(self, video_source: int): - self._video_source = video_source - - def setOutputFormat(self, output_format: int): - self._output_format = output_format - - def setAudioEncoder(self, audio_encoder: int): - self._audio_encoder = audio_encoder - - def setVideoEncoder(self, video_encoder: int): - self._video_encoder = video_encoder - - def setOutputFile(self, path: str): - self._output_file = path - - def setVideoSize(self, width: int, height: int): - self._video_width = width - self._video_height = height - - def setVideoFrameRate(self, rate: int): - self._video_frame_rate = rate - - def setVideoEncodingBitRate(self, bit_rate: int): - self._video_encoding_bit_rate = bit_rate - - def setAudioEncodingBitRate(self, bit_rate: int): - self._audio_encoding_bit_rate = bit_rate - - def setMaxDuration(self, max_duration_ms: int): - self._max_duration = max_duration_ms - - def setMaxFileSize(self, max_filesize_bytes: int): - self._max_filesize = max_filesize_bytes - - def setPreviewDisplay(self, surface_holder): - self._preview_display = surface_holder - - def prepare(self): - if self._released: - return - self._prepared = True - - def start(self): - if self._released or not self._prepared: - return - self._recording = True - - def stop(self): - self._recording = False - - def reset(self): - self._recording = False - self._prepared = False - self._audio_source = 0 - self._video_source = 0 - self._output_format = 0 - self._audio_encoder = 0 - self._video_encoder = 0 - self._output_file = None - self._video_width = 0 - self._video_height = 0 - self._video_frame_rate = 0 - self._video_encoding_bit_rate = 0 - - def release(self): - self._recording = False - self._prepared = False - self._released = True - self._output_file = None - self._preview_display = None - - def getMaxAmplitude(self) -> int: - if self._recording: - import random - return random.randint(0, 32767) - return 0 - - -# ═══════════════════════════════════════════════════════════════ -# SoundPool -# ═══════════════════════════════════════════════════════════════ - -class SoundPool(Stub): - """SoundPool — 音效池""" - - def __init__(self, maxStreams: int = 1, streamType: int = 3, srcQuality: int = 0): - super().__init__() - self._max_streams = maxStreams - self._stream_type = streamType - self._src_quality = srcQuality - self._sounds: Dict[int, str] = {} - self._playing: Dict[int, float] = {} - self._on_load_complete_listener = None - self._next_id = 1 - - def load(self, path: str, priority: int = 1) -> int: - sound_id = self._next_id - self._next_id += 1 - self._sounds[sound_id] = path - if self._on_load_complete_listener: - self._on_load_complete_listener.onLoadComplete(self, sound_id, 0) - return sound_id - - def load(self, context, resId: int, priority: int) -> int: - return self.load(str(resId), priority) - - def play(self, soundID: int, leftVolume: float, rightVolume: float, - priority: int, loop: int, rate: float) -> int: - stream_id = self._next_id - self._next_id += 1 - self._playing[stream_id] = 0.0 - return stream_id - - def pause(self, streamID: int): - pass - - def resume(self, streamID: int): - pass - - def autoPause(self): - pass - - def autoResume(self): - pass - - def release(self): - self._sounds.clear() - self._playing.clear() - - def setVolume(self, streamID: int, leftVolume: float, rightVolume: float): - pass - - def setRate(self, streamID: int, rate: float): - pass - - def setPriority(self, streamID: int, priority: int): - pass - - def setLoop(self, streamID: int, loop: int): - pass - - def setOnLoadCompleteListener(self, listener): - self._on_load_complete_listener = listener - - -# ═══════════════════════════════════════════════════════════════ -# AudioManager -# ═══════════════════════════════════════════════════════════════ - -class AudioManager(Stub): - """AudioManager — 音频管理器""" - - MODE_NORMAL = 0 - MODE_RINGTONE = 1 - MODE_IN_CALL = 2 - MODE_IN_COMMUNICATION = 3 - - STREAM_VOICE_CALL = 0 - STREAM_SYSTEM = 1 - STREAM_RING = 2 - STREAM_MUSIC = 3 - STREAM_ALARM = 4 - STREAM_NOTIFICATION = 5 - STREAM_BLUETOOTH_SCO = 6 - STREAM_DTMF = 8 - STREAM_TTS = 9 - STREAM_ACCESSIBILITY = 10 - - ADJUST_RAISE = 1 - ADJUST_LOWER = -1 - ADJUST_SAME = 0 - ADJUST_MUTE = -100 - ADJUST_UNMUTE = 100 - ADJUST_TOGGLE_MUTE = 101 - - FLAG_PLAY_SOUND = 1 - FLAG_SHOW_UI = 2 - FLAG_REMOVE_SOUND_AND_VIBRATE = 4 - FLAG_ALLOW_RINGER_MODES = 8 - FLAG_VIBRATE = 16 - FLAG_BYPASS_INTERRUPTION_POLICY = 32 - FLAG_BYPASS_MUTE = 64 - FLAG_LOW_LATENCY = 128 - - RINGER_MODE_NORMAL = 2 - RINGER_MODE_SILENT = 0 - RINGER_MODE_VIBRATE = 1 - - def __init__(self): - super().__init__() - self._mode = AudioManager.MODE_NORMAL - self._ringer_mode = AudioManager.RINGER_MODE_NORMAL - self._speakerphone_on = False - self._bluetooth_sco_on = False - self._wired_headset_on = False - self._stream_volumes = { - AudioManager.STREAM_VOICE_CALL: 4, - AudioManager.STREAM_SYSTEM: 7, - AudioManager.STREAM_RING: 5, - AudioManager.STREAM_MUSIC: 10, - AudioManager.STREAM_ALARM: 7, - AudioManager.STREAM_NOTIFICATION: 5, - } - self._parameters: Dict[str, str] = {} - - def getStreamVolume(self, streamType: int) -> int: - if _USE_LINUX: - # STREAM_MUSIC 及其它流类型映射到 Linux 主音量 - return LinuxAudio.get_volume() - return self._stream_volumes.get(streamType, 0) - - def getStreamMaxVolume(self, streamType: int) -> int: - if _USE_LINUX: - return 100 - return 15 - - def setStreamVolume(self, streamType: int, index: int, flags: int = 0): - if _USE_LINUX: - # STREAM_MUSIC 及其它流类型映射到 Linux 主音量 - LinuxAudio.set_volume(index) - return - if streamType in self._stream_volumes: - self._stream_volumes[streamType] = max(0, min(15, index)) - - def isMicrophoneMute(self) -> bool: - """麦克风是否静音""" - if _USE_LINUX: - return LinuxAudio.get_mute() - return False - - def setMicrophoneMute(self, on: bool): - """设置麦克风静音""" - if _USE_LINUX: - LinuxAudio.set_mute(on) - return - # 存根: 无操作 - - def adjustVolume(self, direction: int, flags: int = 0): - pass - - def adjustStreamVolume(self, streamType: int, direction: int, flags: int = 0): - current = self.getStreamVolume(streamType) - if direction == AudioManager.ADJUST_RAISE: - self.setStreamVolume(streamType, current + 1) - elif direction == AudioManager.ADJUST_LOWER: - self.setStreamVolume(streamType, current - 1) - - def getMode(self) -> int: - return self._mode - - def setMode(self, mode: int): - self._mode = mode - - def setSpeakerphoneOn(self, on: bool): - self._speakerphone_on = on - - def isSpeakerphoneOn(self) -> bool: - return self._speakerphone_on - - def isWiredHeadsetOn(self) -> bool: - return self._wired_headset_on - - def isBluetoothScoOn(self) -> bool: - return self._bluetooth_sco_on - - def setBluetoothScoOn(self, on: bool): - self._bluetooth_sco_on = on - - def setRingerMode(self, ringerMode: int): - self._ringer_mode = ringerMode - - def getRingerMode(self) -> int: - return self._ringer_mode - - def loadSoundEffects(self): - pass - - def unloadSoundEffects(self): - pass - - def getParameters(self, keys: str) -> str: - return ",".join(f"{k}={v}" for k, v in self._parameters.items() if k in keys) - - def setParameters(self, keyValuePairs: str): - for pair in keyValuePairs.split(";"): - if "=" in pair: - k, v = pair.split("=", 1) - self._parameters[k.strip()] = v.strip() - - -# ═══════════════════════════════════════════════════════════════ -# AudioTrack -# ═══════════════════════════════════════════════════════════════ - -class AudioTrack(Stub): - """AudioTrack — 音频轨道""" - - STATE_UNINITIALIZED = 0 - STATE_INITIALIZED = 1 - STATE_NO_STATIC_DATA = 2 - MODE_STATIC = 0 - MODE_STREAM = 1 - WRITE_BLOCKING = -1 - WRITE_NON_BLOCKING = 0 - SUCCESS = 0 - ERROR = -1 - ERROR_BAD_VALUE = -2 - ERROR_INVALID_OPERATION = -3 - ERROR_DEAD_OBJECT = -6 - ERROR_WAS_BLOCKED = -8 - - PERFORMANCE_MODE_NONE = 0 - PERFORMANCE_MODE_LOW_LATENCY = 1 - PERFORMANCE_MODE_POWER_SAVING = 2 - - def __init__(self, streamType: int, sampleRateInHz: int, channelConfig: int, - audioFormat: int, bufferSizeInBytes: int, mode: int): - super().__init__() - self._state = AudioTrack.STATE_INITIALIZED - self._playing = False - self._sample_rate = sampleRateInHz - self._channel_config = channelConfig - self._audio_format = audioFormat - self._buffer_size = bufferSizeInBytes - self._mode = mode - self._stream_type = streamType - self._volume = 1.0 - self._playback_rate = sampleRateInHz - self._buffer = bytearray(bufferSizeInBytes) - - def play(self): - self._playing = True - - def pause(self): - self._playing = False - - def stop(self): - self._playing = False - - def flush(self): - pass - - def release(self): - self._state = AudioTrack.STATE_UNINITIALIZED - self._playing = False - - def write(self, audioData, offsetInBytes: int = 0, sizeInBytes: int = -1, - writeMode: int = WRITE_BLOCKING) -> int: - if sizeInBytes < 0: - sizeInBytes = len(audioData) - offsetInBytes - if self._buffer and offsetInBytes + sizeInBytes <= len(self._buffer): - self._buffer[offsetInBytes:offsetInBytes + sizeInBytes] = audioData[offsetInBytes:offsetInBytes + sizeInBytes] - return sizeInBytes - - def getPlayState(self) -> int: - return 3 if self._playing else 1 - - def getState(self) -> int: - return self._state - - def getSampleRate(self) -> int: - return self._sample_rate - - def getPlaybackRate(self) -> int: - return self._playback_rate - - def setPlaybackRate(self, sampleRateInHz: int): - self._playback_rate = sampleRateInHz - - def setVolume(self, volume: float): - self._volume = max(0.0, min(1.0, volume)) - - def getVolume(self) -> float: - return self._volume - - def getAudioFormat(self) -> int: - return self._audio_format - - def getChannelConfiguration(self) -> int: - return self._channel_config - - def getChannelCount(self) -> int: - return 2 if self._channel_config == AudioFormat.CHANNEL_OUT_STEREO else 1 - - def getNotificationMarkerPosition(self) -> int: - return 0 - - def setNotificationMarkerPosition(self, marker: int): - pass - - def getPositionNotificationPeriod(self) -> int: - return 0 - - def setPositionNotificationPeriod(self, period: int): - pass - - @staticmethod - def getMinBufferSize(sampleRateInHz: int, channelConfig: int, audioFormat: int) -> int: - return sampleRateInHz * 2 * 2 # Conservative estimate - - @staticmethod - def getNativeOutputSampleRate(streamType: int) -> int: - return 44100 - - @staticmethod - def getMaxVolume() -> float: - return 1.0 - - @staticmethod - def getMinVolume() -> float: - return 0.0 - - -# ═══════════════════════════════════════════════════════════════ -# AudioRecord -# ═══════════════════════════════════════════════════════════════ - -class AudioRecord(Stub): - """AudioRecord — 音频录制器""" - - STATE_UNINITIALIZED = 0 - STATE_INITIALIZED = 1 - SUCCESS = 0 - ERROR = -1 - ERROR_BAD_VALUE = -2 - ERROR_INVALID_OPERATION = -3 - ERROR_DEAD_OBJECT = -6 - - def __init__(self, audioSource: int, sampleRateInHz: int, channelConfig: int, - audioFormat: int, bufferSizeInBytes: int): - super().__init__() - self._state = AudioRecord.STATE_INITIALIZED - self._recording = False - self._audio_source = audioSource - self._sample_rate = sampleRateInHz - self._channel_config = channelConfig - self._audio_format = audioFormat - self._buffer_size = bufferSizeInBytes - self._buffer = bytearray(bufferSizeInBytes) - - def startRecording(self): - self._recording = True - - def stop(self): - self._recording = False - - def release(self): - self._state = AudioRecord.STATE_UNINITIALIZED - self._recording = False - - def read(self, audioData, offsetInBytes: int, sizeInBytes: int) -> int: - return min(sizeInBytes, len(audioData) - offsetInBytes) - - def getState(self) -> int: - return self._state - - def getSampleRate(self) -> int: - return self._sample_rate - - def getRecordingState(self) -> int: - return 3 if self._recording else 1 - - def getAudioFormat(self) -> int: - return self._audio_format - - def getChannelConfiguration(self) -> int: - return self._channel_config - - def getChannelCount(self) -> int: - return 2 if self._channel_config == AudioFormat.CHANNEL_IN_STEREO else 1 - - @staticmethod - def getMinBufferSize(sampleRateInHz: int, channelConfig: int, audioFormat: int) -> int: - return sampleRateInHz * 2 * 2 - - def getAudioSource(self) -> int: - return self._audio_source - - -# ═══════════════════════════════════════════════════════════════ -# Ringtone -# ═══════════════════════════════════════════════════════════════ - -class Ringtone(Stub): - """Ringtone — 铃声""" - - def __init__(self, context=None): - super().__init__() - self._title = "" - self._stream_type = AudioManager.STREAM_RING - self._playing = False - self._volume = 1.0 - - def setStreamType(self, streamType: int): - self._stream_type = streamType - - def getStreamType(self) -> int: - return self._stream_type - - def setTitle(self, title: str): - self._title = title - - def getTitle(self, context=None) -> str: - return self._title - - def play(self): - self._playing = True - - def stop(self): - self._playing = False - - def isPlaying(self) -> bool: - return self._playing - - def setVolume(self, volume: float): - self._volume = max(0.0, min(1.0, volume)) - - def getVolume(self) -> float: - return self._volume - - def getAudioAttributes(self) -> AudioAttributes: - attrs = AudioAttributes() - if self._stream_type == AudioManager.STREAM_RING: - attrs._usage = AudioAttributes.USAGE_NOTIFICATION_RINGTONE - elif self._stream_type == AudioManager.STREAM_ALARM: - attrs._usage = AudioAttributes.USAGE_ALARM - else: - attrs._usage = AudioAttributes.USAGE_NOTIFICATION - return attrs - - -# ═══════════════════════════════════════════════════════════════ -# RingtoneManager -# ═══════════════════════════════════════════════════════════════ - -class RingtoneManager(Stub): - """RingtoneManager — 铃声管理器""" - - TYPE_RINGTONE = 1 - TYPE_NOTIFICATION = 2 - TYPE_ALARM = 4 - TYPE_ALL = 7 - - ACTION_RINGTONE_PICKER = "android.intent.action.RINGTONE_PICKER" - EXTRA_RINGTONE_TYPE = "android.intent.extra.ringtone.TYPE" - EXTRA_RINGTONE_TITLE = "android.intent.extra.ringtone.TITLE" - EXTRA_RINGTONE_EXISTING_URI = "android.intent.extra.ringtone.EXISTING_URI" - EXTRA_RINGTONE_DEFAULT_URI = "android.intent.extra.ringtone.DEFAULT_URI" - EXTRA_RINGTONE_SHOW_DEFAULT = "android.intent.extra.ringtone.SHOW_DEFAULT" - EXTRA_RINGTONE_INCLUDE_DRM = "android.intent.extra.ringtone.INCLUDE_DRM" - EXTRA_RINGTONE_PICKED_URI = "android.intent.extra.ringtone.PICKED_URI" - - def __init__(self, context): - super().__init__() - self._context = context - self._type = RingtoneManager.TYPE_RINGTONE - self._cursor = [] - self._cursor_position = -1 - - def setType(self, type_: int): - self._type = type_ - - def getCursor(self): - return self._cursor - - def getRingtoneUri(self, position: int): - return None - - def getRingtonePosition(self, uri) -> int: - return 0 - - @staticmethod - def getRingtone(context, uri) -> Ringtone: - return Ringtone(context) - - @staticmethod - def getActualDefaultRingtoneUri(context, type_: int): - return None - - @staticmethod - def setActualDefaultRingtoneUri(context, type_: int, ringtoneUri): - pass - - @staticmethod - def getDefaultUri(type_: int): - return None - - @staticmethod - def getValidRingtoneUri(context): - return None - - @staticmethod - def inferStreamType(type_: int) -> int: - if type_ == RingtoneManager.TYPE_RINGTONE: - return AudioManager.STREAM_RING - elif type_ == RingtoneManager.TYPE_NOTIFICATION: - return AudioManager.STREAM_NOTIFICATION - elif type_ == RingtoneManager.TYPE_ALARM: - return AudioManager.STREAM_ALARM - return AudioManager.STREAM_RING - - -# ═══════════════════════════════════════════════════════════════ -# MediaMetadataRetriever -# ═══════════════════════════════════════════════════════════════ - -class MediaMetadataRetriever(Stub): - """MediaMetadataRetriever — 媒体元数据检索器""" - - METADATA_KEY_ALBUM = "album" - METADATA_KEY_ARTIST = "artist" - METADATA_KEY_TITLE = "title" - METADATA_KEY_DURATION = "duration" - METADATA_KEY_CD_TRACK_NUMBER = "cd_track_number" - METADATA_KEY_COMPOSER = "composer" - METADATA_KEY_DATE = "date" - METADATA_KEY_GENRE = "genre" - METADATA_KEY_MIMETYPE = "mimetype" - METADATA_KEY_NUM_TRACKS = "num_tracks" - METADATA_KEY_WRITER = "writer" - METADATA_KEY_YEAR = "year" - METADATA_KEY_VIDEO_WIDTH = "video_width" - METADATA_KEY_VIDEO_HEIGHT = "video_height" - METADATA_KEY_HAS_AUDIO = "has_audio" - METADATA_KEY_HAS_VIDEO = "has_video" - METADATA_KEY_BITRATE = "bitrate" - METADATA_KEY_LOCATION = "location" - - # 提取帧选项 - OPTION_CLOSEST = 0x01 - OPTION_CLOSEST_SYNC = 0x02 - OPTION_NEXT_SYNC = 0x03 - OPTION_PREVIOUS_SYNC = 0x04 - - # ffprobe format_name 到 MIME 类型映射 - _MIME_MAP = { - "mov,mp4,m4a,3gp,3g2,mj2": "video/mp4", - "matroska,webm": "video/x-matroska", - "avi": "video/x-msvideo", - "flv": "video/x-flv", - "wav": "audio/wav", - "mp3": "audio/mpeg", - "aac": "audio/aac", - "ogg": "audio/ogg", - "flac": "audio/flac", - "asf": "video/x-ms-asf", - "mpeg": "video/mpeg", - "webm": "video/webm", - } - - def __init__(self): - super().__init__() - self._metadata: Dict[str, str] = {} - self._resolved_path: Optional[str] = None - self._ffprobe_data: Optional[Dict[str, Any]] = None - - def setDataSource(self, path: str, headers: Optional[Dict[str, str]] = None): - """设置数据源并用 ffprobe 加载元数据。""" - self._resolved_path = resolve_android_path(path) - self._metadata = {} - self._ffprobe_data = None - if not self._resolved_path or not os.path.exists(self._resolved_path): - return - # 用 ffprobe 获取真实元数据(try/except 保护) - try: - self._ffprobe_data = _ffprobe_metadata(self._resolved_path) - except Exception: - self._ffprobe_data = None - if self._ffprobe_data: - self._parse_ffprobe_metadata() - - def _parse_ffprobe_metadata(self): - """将 ffprobe 输出解析为 Android 风格的元数据键值对。""" - if not self._ffprobe_data: - return - data = self._ffprobe_data - fmt = data.get("format", {}) - tags = fmt.get("tags", {}) - streams = data.get("streams", []) - - # 找到视频流和音频流 - video_stream = None - audio_stream = None - for s in streams: - ct = s.get("codec_type", "") - if ct == "video" and video_stream is None: - video_stream = s - elif ct == "audio" and audio_stream is None: - audio_stream = s - - md = self._metadata - - # 时长(毫秒) - duration = fmt.get("duration") - if duration is None and video_stream: - duration = video_stream.get("duration") - if duration is None and audio_stream: - duration = audio_stream.get("duration") - if duration is not None: - try: - md[MediaMetadataRetriever.METADATA_KEY_DURATION] = str( - int(float(duration) * 1000)) - except (ValueError, TypeError): - pass - - # 标题 - title = tags.get("title") or tags.get("TITLE") - if title: - md[MediaMetadataRetriever.METADATA_KEY_TITLE] = title - - # 艺术家 - artist = tags.get("artist") or tags.get("ARTIST") - if artist: - md[MediaMetadataRetriever.METADATA_KEY_ARTIST] = artist - - # 专辑 - album = tags.get("album") or tags.get("ALBUM") - if album: - md[MediaMetadataRetriever.METADATA_KEY_ALBUM] = album - - # 作曲家 - composer = tags.get("composer") or tags.get("COMPOSER") - if composer: - md[MediaMetadataRetriever.METADATA_KEY_COMPOSER] = composer - - # 作者 - writer = tags.get("writer") or tags.get("WRITER") - if writer: - md[MediaMetadataRetriever.METADATA_KEY_WRITER] = writer - - # 流派 - genre = tags.get("genre") or tags.get("GENRE") - if genre: - md[MediaMetadataRetriever.METADATA_KEY_GENRE] = genre - - # 日期 / 年份 - date = tags.get("date") or tags.get("DATE") or tags.get("year") - if date: - md[MediaMetadataRetriever.METADATA_KEY_DATE] = str(date) - # 尝试提取年份 - year_str = str(date)[:4] - if year_str.isdigit(): - md[MediaMetadataRetriever.METADATA_KEY_YEAR] = year_str - - # 音轨号 - track = tags.get("track") or tags.get("TRACK") - if track: - md[MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER] = str(track) - - # 位置 - location = tags.get("location") or tags.get("LOCATION") - if location: - md[MediaMetadataRetriever.METADATA_KEY_LOCATION] = str(location) - - # MIME 类型 - format_name = fmt.get("format_name", "") - if format_name: - mime = MediaMetadataRetriever._MIME_MAP.get( - format_name, f"video/{format_name.split(',')[0]}") - md[MediaMetadataRetriever.METADATA_KEY_MIMETYPE] = mime - - # 轨道数 - if streams: - md[MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS] = str(len(streams)) - - # 比特率 - bit_rate = fmt.get("bit_rate") - if bit_rate: - md[MediaMetadataRetriever.METADATA_KEY_BITRATE] = str(bit_rate) - - # 视频信息 - if video_stream: - w = video_stream.get("width", 0) - h = video_stream.get("height", 0) - if w: - md[MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH] = str(w) - if h: - md[MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT] = str(h) - md[MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO] = "yes" - else: - md[MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO] = "no" - - # 音频信息 - if audio_stream: - md[MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO] = "yes" - else: - md[MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO] = "no" - - def extractMetadata(self, keyCode: str) -> Optional[str]: - return self._metadata.get(keyCode) - - def getEmbeddedPicture(self) -> Optional[bytes]: - """用 ffmpeg 提取嵌入的封面图片,返回原始字节数据。 - 失败返回 None。""" - if not self._resolved_path or not os.path.exists(self._resolved_path): - return None - try: - return _ffmpeg_extract_cover(self._resolved_path) - except Exception: - return None - - def getFrameAtTime(self, timeUs: int = -1, option: int = 0) -> Optional["Bitmap"]: - """用 ffmpeg 提取指定时间点的视频帧,返回 Bitmap(内含 PIL Image)。 - timeUs < 0 表示提取第一帧。失败返回 None。""" - if not self._resolved_path or not os.path.exists(self._resolved_path): - return None - try: - img = _ffmpeg_extract_frame(self._resolved_path, timeUs) - if img is not None: - from .graphics import Bitmap - w, h = img.size - bm = Bitmap(w, h, Bitmap.Config.ARGB_8888) - # 确保 RGBA 模式 - if img.mode != "RGBA": - img = img.convert("RGBA") - bm._image = img - return bm - except Exception: - pass - return None - - def getScaledFrameAtTime(self, timeUs: int, option: int, - width: int, height: int) -> Optional["Bitmap"]: - """用 ffmpeg 提取并缩放指定时间点的视频帧。""" - if not self._resolved_path or not os.path.exists(self._resolved_path): - return None - try: - img = _ffmpeg_extract_frame(self._resolved_path, timeUs, width, height) - if img is not None: - from .graphics import Bitmap - bm = Bitmap(width, height, Bitmap.Config.ARGB_8888) - if img.mode != "RGBA": - img = img.convert("RGBA") - bm._image = img - return bm - except Exception: - pass - return None - - def release(self): - self._metadata.clear() - self._resolved_path = None - self._ffprobe_data = None - - -# ═══════════════════════════════════════════════════════════════ -# MediaCodec -# ═══════════════════════════════════════════════════════════════ - -class MediaCodec(Stub): - """MediaCodec — 媒体编解码器""" - - BUFFER_FLAG_KEY_FRAME = 1 - BUFFER_FLAG_CODEC_CONFIG = 2 - BUFFER_FLAG_END_OF_STREAM = 4 - BUFFER_FLAG_PARTIAL_FRAME = 8 - BUFFER_FLAG_MUXER_DATA = 16 - - CONFIGURE_FLAG_ENCODE = 1 - CONFIGURE_FLAG_DECODE = 0 - - CRYPTO_MODE_UNENCRYPTED = 0 - CRYPTO_MODE_AES_CTR = 1 - CRYPTO_MODE_AES_WV = 2 - CRYPTO_MODE_AES_CBC = 3 - - INFO_TRY_AGAIN_LATER = -1 - INFO_OUTPUT_FORMAT_CHANGED = -2 - INFO_OUTPUT_BUFFERS_CHANGED = -3 - - MEDIA_CODEC_UNKNOWN = 0 - MEDIA_CODEC_DECODER = 1 - MEDIA_CODEC_ENCODER = 2 - - PARAMETER_KEY_SUSPEND = "suspend" - PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate" - PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync" - PARAMETER_KEY_DROP_BEFORE = "drop-before" - PARAMETER_KEY_DROP_AFTER = "drop-after" - PARAMETER_KEY_OFFSET_TIME = "offset-time" - PARAMETER_KEY_PRIORITY = "priority" - PARAMETER_KEY_OPERATING_RATE = "operating-rate" - PARAMETER_KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period" - PARAMETER_KEY_LOW_LATENCY = "low-latency" - - def __init__(self, name: str): - super().__init__() - self._name = name - self._configured = False - self._started = False - self._format = MediaFormat() - - @staticmethod - def createDecoderByType(mimeType: str) -> "MediaCodec": - return MediaCodec(f"decoder.{mimeType}") - - @staticmethod - def createEncoderByType(mimeType: str) -> "MediaCodec": - return MediaCodec(f"encoder.{mimeType}") - - def configure(self, format: "MediaFormat", surface, crypto, flags: int): - self._format = format - self._configured = True - - def start(self): - self._started = True - - def stop(self): - self._started = False - - def flush(self): - pass - - def release(self): - self._started = False - self._configured = False - - def getInputBuffers(self) -> List[Any]: - return [bytearray(4096)] - - def getOutputBuffers(self) -> List[Any]: - return [bytearray(4096)] - - def dequeueInputBuffer(self, timeoutUs: int) -> int: - return 0 - - def queueInputBuffer(self, index: int, offset: int, size: int, - presentationTimeUs: int, flags: int): - pass - - def dequeueOutputBuffer(self, info: "MediaCodec.BufferInfo", timeoutUs: int) -> int: - return MediaCodec.INFO_TRY_AGAIN_LATER - - def releaseOutputBuffer(self, index: int, render: bool): - pass - - def getOutputFormat(self) -> "MediaFormat": - return self._format - - def getInputImage(self, index: int): - return None - - def getOutputImage(self, index: int): - return None - - def getName(self) -> str: - return self._name - - def getCodecInfo(self): - return None - - -# ═══════════════════════════════════════════════════════════════ -# MediaCodecBufferInfo (nested class reference) -# ═══════════════════════════════════════════════════════════════ - -class MediaCodecBufferInfo(Stub): - """MediaCodec.BufferInfo — 编解码器缓冲信息""" - - def __init__(self): - super().__init__() - self.offset = 0 - self.size = 0 - self.presentationTimeUs = 0 - self.flags = 0 - - def set(self, offset: int, size: int, presentationTimeUs: int, flags: int): - self.offset = offset - self.size = size - self.presentationTimeUs = presentationTimeUs - self.flags = flags - - def duplicate(self) -> "MediaCodecBufferInfo": - info = MediaCodecBufferInfo() - info.set(self.offset, self.size, self.presentationTimeUs, self.flags) - return info - - -# ═══════════════════════════════════════════════════════════════ -# MediaFormat -# ═══════════════════════════════════════════════════════════════ - -class MediaFormat(Stub): - """MediaFormat — 媒体格式""" - - KEY_MIME = "mime" - KEY_WIDTH = "width" - KEY_HEIGHT = "height" - KEY_FRAME_RATE = "frame-rate" - KEY_BIT_RATE = "bitrate" - KEY_COLOR_FORMAT = "color-format" - KEY_I_FRAME_INTERVAL = "i-frame-interval" - KEY_CHANNEL_COUNT = "channel-count" - KEY_SAMPLE_RATE = "sample-rate" - KEY_DURATION = "durationUs" - KEY_MAX_INPUT_SIZE = "max-input-size" - KEY_AAC_PROFILE = "aac-profile" - KEY_LEVEL = "level" - KEY_PROFILE = "profile" - KEY_LANGUAGE = "language" - KEY_IS_AUTOSELECT = "is-autoselect" - KEY_IS_DEFAULT = "is-default" - KEY_IS_FORCED_SUBTITLE = "is-forced-subtitle" - KEY_PIXEL_ASPECT_RATIO_WIDTH = "pixel-aspect-ratio-width" - KEY_PIXEL_ASPECT_RATIO_HEIGHT = "pixel-aspect-ratio-height" - KEY_SLICE_HEIGHT = "slice-height" - KEY_STRIDE = "stride" - KEY_TEMPORAL_LAYERING = "temporal-layering" - KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after" - KEY_PRIORITY = "priority" - KEY_OPERATING_RATE = "operating-rate" - KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period" - KEY_LATENCY = "latency" - KEY_MAX_B_FRAMES = "max-bframes" - KEY_HDR10_PLUS_INFO = "hdr10-plus-info" - - MIMETYPE_AUDIO_AAC = "audio/mp4a-latm" - MIMETYPE_AUDIO_AMR_NB = "audio/3gpp" - MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb" - MIMETYPE_AUDIO_FLAC = "audio/flac" - MIMETYPE_AUDIO_MPEG = "audio/mpeg" - MIMETYPE_AUDIO_OPUS = "audio/opus" - MIMETYPE_AUDIO_RAW = "audio/raw" - MIMETYPE_AUDIO_VORBIS = "audio/vorbis" - MIMETYPE_VIDEO_AV1 = "video/av01" - MIMETYPE_VIDEO_AVC = "video/avc" - MIMETYPE_VIDEO_DOLBY_VISION = "video/dolby-vision" - MIMETYPE_VIDEO_H263 = "video/3gpp" - MIMETYPE_VIDEO_HEVC = "video/hevc" - MIMETYPE_VIDEO_MPEG2 = "video/mpeg2" - MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es" - MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8" - MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9" - MIMETYPE_VIDEO_RAW = "video/raw" - MIMETYPE_TEXT_CEA_608 = "text/cea-608" - MIMETYPE_TEXT_CEA_708 = "text/cea-708" - MIMETYPE_TEXT_VTT = "text/vtt" - MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic" - - def __init__(self): - super().__init__() - self._properties: Dict[str, Any] = {} - - def setString(self, key: str, value: str): - self._properties[key] = value - - def setInteger(self, key: str, value: int): - self._properties[key] = value - - def setFloat(self, key: str, value: float): - self._properties[key] = value - - def setByteBuffer(self, key: str, buf): - self._properties[key] = buf - - def setFeatureEnabled(self, feature: str, enabled: bool): - self._properties[feature] = enabled - - def getString(self, key: str) -> Optional[str]: - v = self._properties.get(key) - return str(v) if v is not None else None - - def getInteger(self, key: str, defaultValue: int = 0) -> int: - v = self._properties.get(key) - return int(v) if v is not None else defaultValue - - def getFloat(self, key: str, defaultValue: float = 0.0) -> float: - v = self._properties.get(key) - return float(v) if v is not None else defaultValue - - def getByteBuffer(self, key: str): - return self._properties.get(key) - - def containsKey(self, key: str) -> bool: - return key in self._properties - - def keys(self) -> List[str]: - return list(self._properties.keys()) - - @staticmethod - def createVideoFormat(mime: str, width: int, height: int) -> "MediaFormat": - fmt = MediaFormat() - fmt.setString(MediaFormat.KEY_MIME, mime) - fmt.setInteger(MediaFormat.KEY_WIDTH, width) - fmt.setInteger(MediaFormat.KEY_HEIGHT, height) - return fmt - - @staticmethod - def createAudioFormat(mime: str, sampleRate: int, channelCount: int) -> "MediaFormat": - fmt = MediaFormat() - fmt.setString(MediaFormat.KEY_MIME, mime) - fmt.setInteger(MediaFormat.KEY_SAMPLE_RATE, sampleRate) - fmt.setInteger(MediaFormat.KEY_CHANNEL_COUNT, channelCount) - return fmt - - @staticmethod - def createSubtitleFormat(mime: str, language: str) -> "MediaFormat": - fmt = MediaFormat() - fmt.setString(MediaFormat.KEY_MIME, mime) - fmt.setString(MediaFormat.KEY_LANGUAGE, language) - return fmt - - -# ═══════════════════════════════════════════════════════════════ -# MediaMuxer -# ═══════════════════════════════════════════════════════════════ - -class MediaMuxer(Stub): - """MediaMuxer — 媒体混合器""" - - OUTPUT_FORMAT_MPEG_4 = 0 - OUTPUT_FORMAT_WEBM = 1 - OUTPUT_FORMAT_THREE_GPP = 2 - OUTPUT_FORMAT_HEIF = 3 - OUTPUT_FORMAT_OGG = 4 - - def __init__(self, path: str, format: int): - super().__init__() - self._path = path - self._format = format - self._started = False - self._tracks: Dict[int, MediaFormat] = {} - self._next_track = 0 - - def addTrack(self, format: MediaFormat) -> int: - idx = self._next_track - self._next_track += 1 - self._tracks[idx] = format - return idx - - def start(self): - self._started = True - - def stop(self): - self._started = False - - def release(self): - self._started = False - self._tracks.clear() - - def writeSampleData(self, trackIndex: int, byteBuf, bufferInfo: MediaCodecBufferInfo): - pass - - def setOrientationHint(self, degrees: int): - pass - - def setLocation(self, latitude: float, longitude: float): - pass - - def getTrackFormat(self, index: int) -> Optional[MediaFormat]: - return self._tracks.get(index) - - -# ═══════════════════════════════════════════════════════════════ -# MediaExtractor -# ═══════════════════════════════════════════════════════════════ - -class MediaExtractor(Stub): - """MediaExtractor — 媒体提取器""" - - SEEK_TO_NEXT_SYNC = 0 - SEEK_TO_PREVIOUS_SYNC = 1 - SEEK_TO_CLOSEST_SYNC = 2 - SEEK_TO_CLOSEST_SYNC = 2 - SAMPLE_FLAG_SYNC = 1 - SAMPLE_FLAG_ENCRYPTED = 2 - - def __init__(self): - super().__init__() - self._tracks: List[MediaFormat] = [] - self._selected_tracks: Set[int] = set() - self._current_track = 0 - - def setDataSource(self, path: str): - pass - - def setDataSource(self, context, uri, headers: Dict[str, str] = None): - pass - - def getTrackCount(self) -> int: - return len(self._tracks) - - def getTrackFormat(self, index: int) -> Optional[MediaFormat]: - if 0 <= index < len(self._tracks): - return self._tracks[index] - return None - - def selectTrack(self, index: int): - self._selected_tracks.add(index) - - def unselectTrack(self, index: int): - self._selected_tracks.discard(index) - - def readSampleData(self, byteBuf, offset: int) -> int: - return 0 - - def getSampleTrackIndex(self) -> int: - return 0 - - def getSampleTime(self) -> int: - return 0 - - def advance(self) -> bool: - return False - - def release(self): - self._tracks.clear() - self._selected_tracks.clear() - - def seekTo(self, timeUs: int, mode: int): - pass - - def getSampleFlags(self) -> int: - return 0 - - def getCachedDuration(self) -> int: - return 0 - - def hasCachedReachedEndOfStream(self) -> bool: - return False - - def getSampleSize(self) -> int: - return 0 - - -# ═══════════════════════════════════════════════════════════════ -# FaceDetector / FaceDetectorFace -# ═══════════════════════════════════════════════════════════════ - -class FaceDetectorFace(Stub): - """FaceDetector.Face — 检测到的人脸""" - - CONFIDENCE_THRESHOLD = 0.4 - EULER_X = 0 - EULER_Y = 1 - EULER_Z = 2 - - def __init__(self): - super().__init__() - self._confidence = 0.0 - self._mid_point_x = 0.0 - self._mid_point_y = 0.0 - self._eyes_distance = 0.0 - self._pose_euler_x = 0.0 - self._pose_euler_y = 0.0 - self._pose_euler_z = 0.0 - - def confidence(self) -> float: - return self._confidence - - def getMidPoint(self, point: PointF): - point.set(self._mid_point_x, self._mid_point_y) - - def eyesDistance(self) -> float: - return self._eyes_distance - - def pose(self, euler: int) -> float: - if euler == FaceDetectorFace.EULER_X: - return self._pose_euler_x - elif euler == FaceDetectorFace.EULER_Y: - return self._pose_euler_y - elif euler == FaceDetectorFace.EULER_Z: - return self._pose_euler_z - return 0.0 - - -class FaceDetector(Stub): - """FaceDetector — 人脸检测器""" - - def __init__(self, width: int, height: int, maxFaces: int): - super().__init__() - self._width = width - self._height = height - self._max_faces = maxFaces - - def findFaces(self, bitmap: "Bitmap", faces: List[FaceDetectorFace]) -> int: - return 0 - - -# ═══════════════════════════════════════════════════════════════ -# ThumbnailUtils -# ═══════════════════════════════════════════════════════════════ - -class ThumbnailUtils(Stub): - """ThumbnailUtils — 缩略图工具""" - - OPTIONS_RECYCLE_INPUT = 256 - - @staticmethod - def createVideoThumbnail(filePath: str, kind: int) -> Optional["Bitmap"]: - if HAS_PIL: - from PIL import Image - from .graphics import Bitmap - img = Image.new("RGB", (320, 240), (32, 32, 32)) - bm = Bitmap(320, 240, Bitmap.Config.ARGB_8888) - bm._image = img - return bm - return None - - @staticmethod - def createImageThumbnail(filePath: str, kind: int) -> Optional["Bitmap"]: - resolved = resolve_android_path(filePath) - if not os.path.exists(resolved): - return None - if HAS_PIL: - from PIL import Image - from .graphics import Bitmap - img = Image.open(resolved) - img.thumbnail((320, 240), Image.LANCZOS) - bm = Bitmap(img.width, img.height, Bitmap.Config.ARGB_8888) - if img.mode != "RGBA": - img = img.convert("RGBA") - bm._image = img - return bm - return None - - @staticmethod - def extractThumbnail(source: "Bitmap", width: int, height: int, - options: int = 0) -> Optional["Bitmap"]: - if HAS_PIL and hasattr(source, "_image") and source._image: - from PIL import Image - img = source._image.copy() - img.thumbnail((width, height), Image.LANCZOS) - bm = Bitmap(img.width, img.height, source._config) - bm._image = img - if options & ThumbnailUtils.OPTIONS_RECYCLE_INPUT: - source.recycle() - return bm - return Bitmap.createScaledBitmap(source, width, height) - - -# ═══════════════════════════════════════════════════════════════ -# MediaScannerConnection / MediaScannerConnectionClient -# ═══════════════════════════════════════════════════════════════ - -class MediaScannerConnectionClient(Stub): - """MediaScannerConnection.MediaScannerConnectionClient — 扫描客户端接口""" - - def onMediaScannerConnected(self): - pass - - def onScanCompleted(self, path: str, uri): - pass - - -class MediaScannerConnection(Stub): - """MediaScannerConnection — 媒体扫描连接""" - - def __init__(self, context, client: MediaScannerConnectionClient = None): - super().__init__() - self._client = client - self._connected = False - - def connect(self): - self._connected = True - if self._client: - self._client.onMediaScannerConnected() - - def disconnect(self): - self._connected = False - - def isConnected(self) -> bool: - return self._connected - - def scanFile(self, path: str, mimeType: str): - if self._client: - self._client.onScanCompleted(path, None) - - @staticmethod - def scanFile(context, paths: List[str], mimeTypes: List[str], - callback: MediaScannerConnectionClient): - for path in paths: - if callback: - callback.onScanCompleted(path, None) - - -# ═══════════════════════════════════════════════════════════════ -# MediaActionSound -# ═══════════════════════════════════════════════════════════════ - -class MediaActionSound(Stub): - """MediaActionSound — 媒体操作音效""" - - SHUTTER_CLICK = 0 - FOCUS_COMPLETE = 1 - START_VIDEO_RECORDING = 2 - STOP_VIDEO_RECORDING = 3 - - def __init__(self): - super().__init__() - - def play(self, soundName: int): - pass - - def load(self, soundName: int): - pass - - def release(self): - pass - - -# ═══════════════════════════════════════════════════════════════ -# ToneGenerator -# ═══════════════════════════════════════════════════════════════ - -class ToneGenerator(Stub): - """ToneGenerator — 音调发生器""" - - TONE_DTMF_0 = 0 - TONE_DTMF_1 = 1 - TONE_DTMF_2 = 2 - TONE_DTMF_3 = 3 - TONE_DTMF_4 = 4 - TONE_DTMF_5 = 5 - TONE_DTMF_6 = 6 - TONE_DTMF_7 = 7 - TONE_DTMF_8 = 8 - TONE_DTMF_9 = 9 - TONE_DTMF_S = 10 - TONE_DTMF_P = 11 - TONE_DTMF_A = 12 - TONE_DTMF_B = 13 - TONE_DTMF_C = 14 - TONE_DTMF_D = 15 - TONE_SUP_CALL_WAITING = 16 - TONE_SUP_RINGTONE = 17 - TONE_PROP_BEEP = 24 - TONE_PROP_ACK = 25 - TONE_PROP_NACK = 26 - TONE_PROP_PROMPT = 27 - TONE_PROP_BEEP2 = 28 - TONE_SUP_INTERCEPT = 29 - TONE_SUP_INTERCEPT_ABBREV = 30 - TONE_SUP_CONGESTION = 31 - TONE_SUP_CONGESTION_ABBREV = 32 - TONE_SUP_CONFIRM = 33 - TONE_SUP_RADIO_ACK = 34 - TONE_SUP_RADIO_NOTAVAIL = 35 - TONE_SUP_ERROR = 36 - TONE_SUP_CALL_WAITING = 37 - TONE_SUP_RINGTONE = 38 - TONE_PROP_BEEP = 39 - TONE_PROP_ACK = 40 - TONE_PROP_NACK = 41 - TONE_PROP_PROMPT = 42 - TONE_PROP_BEEP2 = 43 - TONE_SUP_INTERCEPT = 44 - TONE_PROP_END_EMERGENCY = 45 - TONE_CDMA_PRESSHOLDKEY_LITE = 46 - TONE_CDMA_ALERT_SIGNAL_RELEASE = 47 - TONE_CDMA_NETWORK_USA_RINGBACK = 48 - TONE_CDMA_INTERCEPT = 49 - TONE_CDMA_ABBR_INTERCEPT = 50 - TONE_CDMA_REORDER = 51 - TONE_CDMA_ABBR_REORDER = 52 - TONE_CDMA_NETWORK_BUSY = 53 - TONE_CDMA_CONFIRM = 54 - TONE_CDMA_ANSWER = 55 - TONE_CDMA_NETWORK_CALLWAITING = 56 - TONE_CDMA_PRESSHOLDKEY_LITE = 57 - TONE_CDMA_ONE_MIN_BEEP = 58 - TONE_CDMA_SIGNAL_OFF = 59 - TONE_CDMA_SIGNAL_ON = 60 - TONE_CDMA_HIGH_LITE = 61 - TONE_CDMA_MED_LITE = 62 - TONE_CDMA_LOW_LITE = 63 - TONE_CDMA_LOW_PBX_LITE = 64 - TONE_CDMA_HIGH_PBX_LITE = 65 - TONE_CDMA_MED_PBX_LITE = 66 - TONE_CDMA_PBX_OFF = 67 - TONE_CDMA_PBX_ON = 68 - TONE_CDMA_ALERT_CALL_GUARD = 69 - TONE_CDMA_SOFT_ERROR_LITE = 70 - TONE_CDMA_ABBR_ALERT = 71 - TONE_CDMA_NETWORK_BUSY_ONE_SHOT = 72 - TONE_CDMA_NETWORK_BUSY = 73 - TONE_CDMA_NETWORK_BUSY_ONE_SHOT = 74 - TONE_CDMA_ABBR_ALERT = 75 - TONE_CDMA_SOFT_ERROR_LITE = 76 - TONE_CDMA_ALERT_CALL_GUARD = 77 - TONE_CDMA_LOW_PBX_LITE = 78 - TONE_CDMA_MED_PBX_LITE = 79 - TONE_CDMA_HIGH_PBX_LITE = 80 - TONE_CDMA_PBX_OFF = 81 - TONE_CDMA_PBX_ON = 82 - TONE_CDMA_PRESSHOLDKEY_LITE = 83 - TONE_CDMA_ONE_MIN_BEEP = 84 - TONE_CDMA_SIGNAL_OFF = 85 - TONE_CDMA_SIGNAL_ON = 86 - TONE_CDMA_HIGH_LITE = 87 - TONE_CDMA_MED_LITE = 88 - TONE_CDMA_LOW_LITE = 89 - TONE_CDMA_ALERT_AUTO_REDIAL = 90 - TONE_CDMA_ALERT_CALL_GUARD = 91 - TONE_PROP_BEEP = 92 - TONE_PROP_ACK = 93 - TONE_PROP_NACK = 94 - TONE_PROP_PROMPT = 95 - TONE_PROP_BEEP2 = 96 - TONE_SUP_INTERCEPT = 97 - TONE_SUP_INTERCEPT_ABBREV = 98 - TONE_SUP_CONGESTION = 99 - TONE_SUP_CONGESTION_ABBREV = 100 - TONE_SUP_CONFIRM = 101 - TONE_SUP_RADIO_ACK = 102 - TONE_SUP_RADIO_NOTAVAIL = 103 - TONE_SUP_ERROR = 104 - TONE_SUP_CALL_WAITING = 105 - TONE_SUP_RINGTONE = 106 - - MIN_VOLUME = 0 - MAX_VOLUME = 100 - - def __init__(self, streamType: int, volume: int): - super().__init__() - self._stream_type = streamType - self._volume = max(0, min(100, volume)) - - def startTone(self, toneType: int, durationMs: int = -1): - pass - - def stopTone(self): - pass - - def release(self): - pass - - def getAudioSessionId(self) -> int: - return 0 - - -# ═══════════════════════════════════════════════════════════════ -# ExifInterface -# ═══════════════════════════════════════════════════════════════ - -class ExifInterface(Stub): - """ExifInterface — EXIF 接口""" - - TAG_APERTURE = "FNumber" - TAG_DATETIME = "DateTime" - TAG_EXPOSURE_TIME = "ExposureTime" - TAG_FLASH = "Flash" - TAG_FOCAL_LENGTH = "FocalLength" - TAG_GPS_ALTITUDE = "GPSAltitude" - TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef" - TAG_GPS_DATESTAMP = "GPSDateStamp" - TAG_GPS_LATITUDE = "GPSLatitude" - TAG_GPS_LATITUDE_REF = "GPSLatitudeRef" - TAG_GPS_LONGITUDE = "GPSLongitude" - TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef" - TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod" - TAG_GPS_TIMESTAMP = "GPSTimeStamp" - TAG_IMAGE_LENGTH = "ImageLength" - TAG_IMAGE_WIDTH = "ImageWidth" - TAG_ISO = "ISOSpeedRatings" - TAG_MAKE = "Make" - TAG_MODEL = "Model" - TAG_ORIENTATION = "Orientation" - TAG_WHITE_BALANCE = "WhiteBalance" - - ORIENTATION_NORMAL = 1 - ORIENTATION_ROTATE_90 = 6 - ORIENTATION_ROTATE_180 = 3 - ORIENTATION_ROTATE_270 = 8 - ORIENTATION_FLIP_HORIZONTAL = 2 - ORIENTATION_FLIP_VERTICAL = 4 - ORIENTATION_TRANSPOSE = 5 - ORIENTATION_TRANSVERSE = 7 - ORIENTATION_UNDEFINED = 0 - - WHITEBALANCE_AUTO = 0 - WHITEBALANCE_MANUAL = 1 - - FLAG_NO_FLASH = 0 - FLAG_FIRED = 1 - FLAG_STROBE_RETURN_LIGHT_DETECTED = 2 - FLAG_STROBE_RETURN_LIGHT_NOT_DETECTED = 4 - FLAG_COMPULSORY_FIRE = 8 - FLAG_COMPULSORY_SUPPRESSION = 16 - FLAG_AUTO_MODE = 24 - FLAG_AUTO_MODE_RED_EYE = 25 - FLAG_NO_STROBE_FUNCTION = 32 - FLAG_RED_EYE_SUPPORT = 64 - - def __init__(self, filename: str): - super().__init__() - self._filename = resolve_android_path(filename) - self._attributes: Dict[str, str] = {} - self._pil_image = None - self._load_exif() - - def _load_exif(self): - """尝试从文件中读取 EXIF 数据""" - if not self._filename or not os.path.exists(self._filename): - return - if HAS_PIL: - from PIL import Image - from PIL import ExifTags - try: - img = Image.open(self._filename) - self._pil_image = img - exif_data = img._getexif() - if exif_data: - for tag_id, value in exif_data.items(): - tag_name = ExifTags.TAGS.get(tag_id, str(tag_id)) - self._attributes[str(tag_name)] = str(value) - except Exception: - self._attributes[self.TAG_MAKE] = "Unknown" - self._attributes[self.TAG_MODEL] = "Android Device" - self._attributes[self.TAG_ORIENTATION] = str(self.ORIENTATION_NORMAL) - self._attributes[self.TAG_IMAGE_WIDTH] = "1920" - self._attributes[self.TAG_IMAGE_LENGTH] = "1080" - else: - self._attributes[self.TAG_MAKE] = "Unknown" - self._attributes[self.TAG_MODEL] = "Android Device" - self._attributes[self.TAG_ORIENTATION] = str(self.ORIENTATION_NORMAL) - - def getAttribute(self, tag: str) -> Optional[str]: - return self._attributes.get(tag) - - def getAttributeInt(self, tag: str, defaultValue: int) -> int: - v = self._attributes.get(tag) - if v is not None: - try: - return int(v) - except (ValueError, TypeError): - return defaultValue - return defaultValue - - def getAttributeDouble(self, tag: str, defaultValue: float) -> float: - v = self._attributes.get(tag) - if v is not None: - try: - # Handle rational strings like "1/100" - if "/" in str(v): - num, den = str(v).split("/") - return float(num) / float(den) - return float(v) - except (ValueError, TypeError, ZeroDivisionError): - return defaultValue - return defaultValue - - def setAttribute(self, tag: str, value: str): - self._attributes[tag] = value - - def saveAttributes(self): - if not self._filename or not HAS_PIL: - return - from PIL import Image - try: - img = Image.open(self._filename) - exif_bytes = img.info.get("exif", b"") - img.save(self._filename, exif=exif_bytes) - except Exception: - pass - - def hasThumbnail(self) -> bool: - return False - - def getThumbnail(self) -> Optional[bytes]: - return None - - def getThumbnailRange(self) -> Optional[Tuple[int, int]]: - return None - - def getAttributeRange(self, tag: str) -> Optional[Tuple[int, int]]: - return None - - def setIsUri(self, isUri: bool): - pass - - def flipHorizontally(self): - if hasattr(self, '_pil_image') and self._pil_image: - from PIL import Image - self._pil_image = self._pil_image.transpose(Image.FLIP_LEFT_RIGHT) - self._save_from_pil() - - def flipVertically(self): - if hasattr(self, '_pil_image') and self._pil_image: - from PIL import Image - self._pil_image = self._pil_image.transpose(Image.FLIP_TOP_BOTTOM) - self._save_from_pil() - - def rotate(self, degrees): - if hasattr(self, '_pil_image') and self._pil_image: - from PIL import Image - self._pil_image = self._pil_image.rotate(degrees, expand=True) - self._save_from_pil() - # 更新方向属性 - self.setAttribute("Orientation", "1") # Normal after rotation - - def _save_from_pil(self): - """从 PIL 图像保存回文件""" - if hasattr(self, '_pil_image') and self._pil_image and hasattr(self, '_filename'): - self._pil_image.save(self._filename) - - -# ═══════════════════════════════════════════════════════════════ -# CamcorderProfile -# ═══════════════════════════════════════════════════════════════ - -class CamcorderProfile(Stub): - """CamcorderProfile — 摄像机配置""" - - QUALITY_LOW = 0 - QUALITY_HIGH = 1 - QUALITY_QCIF = 2 - QUALITY_CIF = 3 - QUALITY_480P = 4 - QUALITY_720P = 5 - QUALITY_1080P = 6 - QUALITY_QVGA = 7 - QUALITY_2160P = 8 - QUALITY_VGA = 9 - QUALITY_4KDCI = 10 - QUALITY_QHD = 11 - QUALITY_2K = 12 - - def __init__(self): - super().__init__() - self.quality = CamcorderProfile.QUALITY_HIGH - self.fileFormat = MediaRecorder.OutputFormat.MPEG_4 - self.duration = 30 - self.videoCodec = MediaRecorder.VideoEncoder.H_264 - self.videoBitRate = 20000000 - self.videoFrameRate = 30 - self.videoFrameWidth = 1920 - self.videoFrameHeight = 1080 - self.audioCodec = MediaRecorder.AudioEncoder.AAC - self.audioBitRate = 192000 - self.audioSampleRate = 44100 - self.audioChannels = 2 - - @staticmethod - def get(cameraId: int, quality: int) -> Optional["CamcorderProfile"]: - if quality in (CamcorderProfile.QUALITY_LOW, CamcorderProfile.QUALITY_HIGH): - profile = CamcorderProfile() - profile.quality = quality - if quality == CamcorderProfile.QUALITY_LOW: - profile.videoFrameWidth = 640 - profile.videoFrameHeight = 480 - profile.videoBitRate = 500000 - profile.audioBitRate = 96000 - return profile - return None - - @staticmethod - def hasProfile(cameraId: int, quality: int) -> bool: - return True - - @staticmethod - def getAll(cameraId: int) -> List[int]: - return [ - CamcorderProfile.QUALITY_LOW, - CamcorderProfile.QUALITY_HIGH, - CamcorderProfile.QUALITY_480P, - CamcorderProfile.QUALITY_720P, - CamcorderProfile.QUALITY_1080P, - ] - - -# ═══════════════════════════════════════════════════════════════ -# EncoderProfiles -# ═══════════════════════════════════════════════════════════════ - -class EncoderProfiles(Stub): - """EncoderProfiles — 编码器配置 (API 31+)""" - - class VideoProfile(Stub): - def __init__(self): - super().__init__() - self.codec = "" - self.bitrate = 0 - self.width = 0 - self.height = 0 - self.frameRate = 0 - self.profile = 0 - self.bitDepth = 8 - self.chromaSubsampling = 420 - self.hdrFormat = 0 - - class AudioProfile(Stub): - def __init__(self): - super().__init__() - self.codec = "" - self.bitrate = 0 - self.sampleRate = 0 - self.channels = 0 - self.profile = 0 - - def __init__(self): - super().__init__() - self._default_duration = 30 - self._recommended_file_format = MediaRecorder.OutputFormat.MPEG_4 - self._video_profiles: List[EncoderProfiles.VideoProfile] = [] - self._audio_profiles: List[EncoderProfiles.AudioProfile] = [] - - def getDefaultDuration(self) -> int: - return self._default_duration - - def getRecommendedFileFormat(self) -> int: - return self._recommended_file_format - - def getVideoProfiles(self) -> List["VideoProfile"]: - return list(self._video_profiles) - - def getAudioProfiles(self) -> List["AudioProfile"]: - return list(self._audio_profiles) - - @staticmethod - def get(cameraId: int, quality: int) -> Optional["EncoderProfiles"]: - profiles = EncoderProfiles() - - vp = EncoderProfiles.VideoProfile() - vp.codec = MediaFormat.MIMETYPE_VIDEO_AVC - vp.bitrate = 20000000 - vp.width = 1920 - vp.height = 1080 - vp.frameRate = 30 - vp.profile = 8 # High Profile - profiles._video_profiles.append(vp) - - ap = EncoderProfiles.AudioProfile() - ap.codec = MediaFormat.MIMETYPE_AUDIO_AAC - ap.bitrate = 192000 - ap.sampleRate = 44100 - ap.channels = 2 - ap.profile = 2 # AAC LC - profiles._audio_profiles.append(ap) - - return profiles - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "MediaPlayer", - "MediaRecorder", - "SoundPool", - "AudioManager", - "AudioTrack", - "AudioRecord", - "Ringtone", - "RingtoneManager", - "MediaMetadataRetriever", - "MediaCodec", - "MediaCodecBufferInfo", - "MediaFormat", - "MediaMuxer", - "MediaExtractor", - "FaceDetector", - "FaceDetectorFace", - "ThumbnailUtils", - "MediaScannerConnection", - "MediaScannerConnectionClient", - "AudioAttributes", - "AudioFormat", - "MediaActionSound", - "ToneGenerator", - "ExifInterface", - "CamcorderProfile", - "EncoderProfiles", -] \ No newline at end of file diff --git a/android_pyapi/android/media_async.py b/android_pyapi/android/media_async.py deleted file mode 100644 index 6a6028d..0000000 --- a/android_pyapi/android/media_async.py +++ /dev/null @@ -1,1621 +0,0 @@ -""" -Android media 异步组件 — MediaCodec, MediaExtractor, MediaMuxer, MediaPlayer 扩展等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import io -import math -import time -import struct -import threading -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union -from enum import IntEnum - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# MediaCodec 常量 -# ═══════════════════════════════════════════════════════════════ - -BUFFER_FLAG_KEY_FRAME = 1 -BUFFER_FLAG_CODEC_CONFIG = 2 -BUFFER_FLAG_END_OF_STREAM = 4 -BUFFER_FLAG_PARTIAL_FRAME = 8 -BUFFER_FLAG_SYNC_FRAME = 1 - -CRYPTO_MODE_UNENCRYPTED = 0 -CRYPTO_MODE_AES_CTR = 1 -CRYPTO_MODE_AES_WV = 2 -CRYPTO_MODE_AES_CBC = 3 - -INFO_TRY_AGAIN_LATER = -1 -INFO_OUTPUT_FORMAT_CHANGED = -2 -INFO_OUTPUT_BUFFERS_CHANGED = -3 - -MEDIA_CODEC_UNKNOWN = 0 -MEDIA_CODEC_DECODER = 1 -MEDIA_CODEC_ENCODER = 2 - -COLOR_FormatUnsupported = 0x7FFFFFFF -COLOR_FormatYUV420Planar = 19 -COLOR_FormatYUV420SemiPlanar = 21 -COLOR_FormatYUV420PackedPlanar = 20 -COLOR_FormatYUV420PackedSemiPlanar = 22 -COLOR_FormatYUV422Planar = 16 -COLOR_FormatYUV422SemiPlanar = 41 -COLOR_FormatYUV422PackedPlanar = 17 -COLOR_FormatYUV422PackedSemiPlanar = 18 -COLOR_FormatYUV444Planar = 31 -COLOR_FormatYUV444SemiPlanar = 42 -COLOR_FormatYUV444PackedPlanar = 32 -COLOR_FormatYUV444PackedSemiPlanar = 33 -COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7F000100 -COLOR_FormatSurface = 0x7F000789 -COLOR_FormatYUV420Flexible = 0x7F420888 -COLOR_Format24BitBGR = 5 -COLOR_Format32BitARGB8888 = 9 -COLOR_Format32BitBGRA8888 = 10 - -BITRATE_MODE_VBR = 0 -BITRATE_MODE_CBR = 1 -BITRATE_MODE_CQ = 2 - -CODEC_LEVEL_UNSPECIFIED = 0 -CODEC_PROFILE_UNSPECIFIED = 0 - -AUDIO_CHANNEL_OUT_MONO = 4 -AUDIO_CHANNEL_OUT_STEREO = 12 -AUDIO_CHANNEL_OUT_5POINT1 = 252 -AUDIO_CHANNEL_OUT_7POINT1 = 6396 - -AUDIO_CHANNEL_IN_MONO = 16 -AUDIO_CHANNEL_IN_STEREO = 12 - -PCM_ENCODING_PCM_16BIT = 2 -PCM_ENCODING_PCM_8BIT = 3 -PCM_ENCODING_PCM_FLOAT = 4 - -AUDIO_OUTPUT_FLAG_NONE = 0 -AUDIO_OUTPUT_FLAG_DIRECT = 1 -AUDIO_OUTPUT_FLAG_HW_AV_SYNC = 8 - -# MediaFormat KEY 常量 -KEY_MIME = "mime" -KEY_WIDTH = "width" -KEY_HEIGHT = "height" -KEY_FRAME_RATE = "frame-rate" -KEY_BIT_RATE = "bitrate" -KEY_COLOR_FORMAT = "color-format" -KEY_I_FRAME_INTERVAL = "i-frame-interval" -KEY_CHANNEL_COUNT = "channel-count" -KEY_SAMPLE_RATE = "sample-rate" -KEY_MAX_INPUT_SIZE = "max-input-size" -KEY_DURATION = "durationUs" -KEY_IS_ADTS = "is-adts" -KEY_CHANNEL_MASK = "channel-mask" -KEY_ENCODING = "encoding" -KEY_PCM_ENCODING = "pcm-encoding" -KEY_PROFILE = "profile" -KEY_LEVEL = "level" -KEY_LANGUAGE = "language" -KEY_PRIORITY = "priority" -KEY_OPERATING_RATE = "operating-rate" -KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period" -KEY_LATENCY = "latency" -KEY_MAX_FPS_TO_ENCODER = "max-fps-to-encoder" -KEY_MAX_PTS_GAP_TO_ENCODER = "max-pts-gap-to-encoder" -KEY_PREPEND_HEADER_TO_SYNC_FRAMES = "prepend-sps-pps-to-idr-frames" -KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after" -KEY_TEMPORAL_LAYERING = "temporal-layering" -KEY_HDR10_PLUS_INFO = "hdr10-plus-info" -KEY_PIXEL_ASPECT_RATIO_WIDTH = "pixel-aspect-ratio-width" -KEY_PIXEL_ASPECT_RATIO_HEIGHT = "pixel-aspect-ratio-height" -KEY_AAC_DRC_BOOST_FACTOR = "aac-drc-boost-factor" -KEY_AAC_DRC_ATTENUATION_FACTOR = "aac-drc-attenuation-factor" -KEY_AAC_DRC_HEAVY_COMPRESSION = "aac-drc-heavy-compression" -KEY_AAC_DRC_TARGET_REFERENCE_LEVEL = "aac-target-ref-level" -KEY_AAC_ENCODED_TARGET_LEVEL = "aac-encoded-target-level" -KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT = "aac-max-output-channel-count" -KEY_AAC_PROFILE = "aac-profile" -KEY_AAC_SBR_MODE = "aac-sbr-mode" -KEY_ENCODER_DELAY_FRAME_COUNT = "encoder-delay" -KEY_ENCODER_PADDING_FRAME_COUNT = "encoder-padding" -KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level" -KEY_MAX_BIT_RATE = "max-bitrate" -KEY_QUALITY = "quality" -KEY_SAMPLE_MIME_TYPE = "sample-mime-type" -KEY_TRACK_ID = "track-id" -KEY_IS_AUTOSELECT = "is-autoselect" -KEY_IS_DEFAULT = "is-default" -KEY_IS_FORCED_SUBTITLE = "is-forced-subtitle" -KEY_ROTATION = "rotation" -KEY_CAPTURE_RATE = "capture-rate" -KEY_FEATURE_ = "feature-" -KEY_HDR_STATIC_INFO = "hdr-static-info" -KEY_HDR10_PLUS_INFO = "hdr10-plus-info" -KEY_COLOR_TRANSFER_REQUEST = "color-transfer-request" -KEY_OFFSET_TIME = "offset-time" -KEY_PCM_ENCODING = "pcm-encoding" -KEY_SAMPLE_RATE = "sample-rate" -KEY_SUPPORTED_PCM_ENCODINGS = "supported-pcm-encodings" -KEY_SUPPORTED_PCM_USAGES = "supported-pcm-usages" -KEY_TRANSFER_TIMEOUT = "transfer-timeout" -KEY_VIDEO_QP_B_MAX = "video-qp-b-max" -KEY_VIDEO_QP_B_MIN = "video-qp-b-min" -KEY_VIDEO_QP_I_MAX = "video-qp-i-max" -KEY_VIDEO_QP_I_MIN = "video-qp-i-min" -KEY_VIDEO_QP_P_MAX = "video-qp-p-max" -KEY_VIDEO_QP_P_MIN = "video-qp-p-min" - -# MIME 类型 -MIMETYPE_VIDEO_AV1 = "video/av01" -MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9" -MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8" -MIMETYPE_VIDEO_H265 = "video/hevc" -MIMETYPE_VIDEO_H264 = "video/avc" -MIMETYPE_VIDEO_MPEG2 = "video/mpeg2" -MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es" -MIMETYPE_VIDEO_VC1 = "video/wvc1" -MIMETYPE_VIDEO_DIVX = "video/divx" -MIMETYPE_VIDEO_DIVX3 = "video/divx3" -MIMETYPE_VIDEO_FLV = "video/flv1" -MIMETYPE_VIDEO_H263 = "video/3gpp" -MIMETYPE_VIDEO_RAW = "video/raw" -MIMETYPE_VIDEO_DOLBY_VISION = "video/dolby-vision" -MIMETYPE_VIDEO_SCRAMBLED = "video/scrambled" -MIMETYPE_AUDIO_AAC = "audio/mp4a-latm" -MIMETYPE_AUDIO_MP3 = "audio/mpeg" -MIMETYPE_AUDIO_MPEG = "audio/mpeg" -MIMETYPE_AUDIO_AMR_NB = "audio/3gpp" -MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb" -MIMETYPE_AUDIO_FLAC = "audio/flac" -MIMETYPE_AUDIO_VORBIS = "audio/vorbis" -MIMETYPE_AUDIO_OPUS = "audio/opus" -MIMETYPE_AUDIO_RAW = "audio/raw" -MIMETYPE_AUDIO_ALAC = "audio/alac" -MIMETYPE_AUDIO_AC3 = "audio/ac3" -MIMETYPE_AUDIO_EAC3 = "audio/eac3" -MIMETYPE_AUDIO_WMA = "audio/x-ms-wma" -MIMETYPE_AUDIO_MSGSM = "audio/gsm" -MIMETYPE_TEXT_VTT = "text/vtt" -MIMETYPE_TEXT_CEA_608 = "text/cea-608" -MIMETYPE_TEXT_CEA_708 = "text/cea-708" -MIMETYPE_SUBTITLE_SRT = "application/x-subrip" -MIMETYPE_SUBTITLE_TTML = "application/ttml+xml" - - -# ═══════════════════════════════════════════════════════════════ -# MediaCodec.BufferInfo -# ═══════════════════════════════════════════════════════════════ - -class CodecBufferInfo(Stub): - """MediaCodec.BufferInfo — 编解码缓冲区信息""" - - BUFFER_FLAG_KEY_FRAME = BUFFER_FLAG_KEY_FRAME - BUFFER_FLAG_CODEC_CONFIG = BUFFER_FLAG_CODEC_CONFIG - BUFFER_FLAG_END_OF_STREAM = BUFFER_FLAG_END_OF_STREAM - BUFFER_FLAG_PARTIAL_FRAME = BUFFER_FLAG_PARTIAL_FRAME - BUFFER_FLAG_SYNC_FRAME = BUFFER_FLAG_SYNC_FRAME - - def __init__(self): - super().__init__() - self.offset: int = 0 - self.size: int = 0 - self.presentationTimeUs: int = 0 - self.flags: int = 0 - - def set(self, new_offset: int, new_size: int, new_time_us: int, new_flags: int) -> None: - self.offset = new_offset - self.size = new_size - self.presentationTimeUs = new_time_us - self.flags = new_flags - - def duplicate(self) -> "CodecBufferInfo": - info = CodecBufferInfo() - info.offset = self.offset - info.size = self.size - info.presentationTimeUs = self.presentationTimeUs - info.flags = self.flags - return info - - def toString(self) -> str: - return (f"BufferInfo(offset={self.offset}, size={self.size}, " - f"timeUs={self.presentationTimeUs}, flags={self.flags})") - - -# ═══════════════════════════════════════════════════════════════ -# MediaCodec.CryptoInfo -# ═══════════════════════════════════════════════════════════════ - -class CryptoInfo(Stub): - """CryptoInfo — 加密信息 (MediaCodec)""" - - def __init__(self): - super().__init__() - self.numSubSamples = 0 - self.numBytesOfClearData: List[int] = [] - self.numBytesOfEncryptedData: List[int] = [] - self.key: Optional[bytes] = None - self.iv: Optional[bytes] = None - self.mode: int = 0 - - class Pattern(Stub): - """Pattern — 加密模式""" - - def __init__(self, encrypt_blocks: int = 0, skip_blocks: int = 0): - super().__init__() - self.encryptBlocks = encrypt_blocks - self.skipBlocks = skip_blocks - - def toString(self) -> str: - return f"Pattern(encrypt={self.encryptBlocks}, skip={self.skipBlocks})" - - def hashCode(self) -> int: - return hash((self.encryptBlocks, self.skipBlocks)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, CryptoInfo.Pattern): - return False - return (self.encryptBlocks == other.encryptBlocks - and self.skipBlocks == other.skipBlocks) - - def setPattern(self, pattern: "CryptoInfo.Pattern"): - self._pattern = pattern - - def getPattern(self) -> Optional["CryptoInfo.Pattern"]: - return getattr(self, '_pattern', None) - - def toString(self) -> str: - return (f"CryptoInfo(subsamples={self.numSubSamples}, " - f"mode={self.mode})") - - -# ═══════════════════════════════════════════════════════════════ -# MediaCodec.CodecException -# ═══════════════════════════════════════════════════════════════ - -class CodecException(RuntimeException): - """MediaCodec.CodecException — 编解码器异常""" - - ERROR_RECOVERABLE = 1 - ERROR_INSUFFICIENT_RESOURCE = 2 - - def __init__(self, error_code: int = 0, diagnostic_info: str = "", - is_recoverable: bool = False, is_transient: bool = False): - super().__init__(f"CodecException: {diagnostic_info}") - self._error_code = error_code - self._diagnostic_info = diagnostic_info - self._is_recoverable = is_recoverable - self._is_transient = is_transient - - def getErrorCode(self) -> int: - return self._error_code - - def getDiagnosticInfo(self) -> str: - return self._diagnostic_info - - def isRecoverable(self) -> bool: - return self._is_recoverable - - def isTransient(self) -> bool: - return self._is_transient - - def toString(self) -> str: - return (f"CodecException(errorCode={self._error_code}, " - f"recoverable={self._is_recoverable}, " - f"transient={self._is_transient})") - - -# ═══════════════════════════════════════════════════════════════ -# MediaCodec -# ═══════════════════════════════════════════════════════════════ - -class MediaCodec(Stub): - """MediaCodec — 媒体编解码器""" - - # 内部类引用 - BufferInfo = CodecBufferInfo - CryptoInfo = CryptoInfo - CodecException = CodecException - - BUFFER_FLAG_KEY_FRAME = BUFFER_FLAG_KEY_FRAME - BUFFER_FLAG_CODEC_CONFIG = BUFFER_FLAG_CODEC_CONFIG - BUFFER_FLAG_END_OF_STREAM = BUFFER_FLAG_END_OF_STREAM - BUFFER_FLAG_PARTIAL_FRAME = BUFFER_FLAG_PARTIAL_FRAME - BUFFER_FLAG_SYNC_FRAME = BUFFER_FLAG_SYNC_FRAME - - CRYPTO_MODE_UNENCRYPTED = CRYPTO_MODE_UNENCRYPTED - CRYPTO_MODE_AES_CTR = CRYPTO_MODE_AES_CTR - CRYPTO_MODE_AES_WV = CRYPTO_MODE_AES_WV - CRYPTO_MODE_AES_CBC = CRYPTO_MODE_AES_CBC - - INFO_TRY_AGAIN_LATER = INFO_TRY_AGAIN_LATER - INFO_OUTPUT_FORMAT_CHANGED = INFO_OUTPUT_FORMAT_CHANGED - INFO_OUTPUT_BUFFERS_CHANGED = INFO_OUTPUT_BUFFERS_CHANGED - - CONFIGURE_FLAG_ENCODE = 1 - - def __init__(self, name: str = ""): - super().__init__() - self._name = name - self._configured = False - self._started = False - self._format: Optional["MediaFormat"] = None - self._input_buffers: Dict[int, Optional[bytes]] = {} - self._output_buffers: Dict[int, Optional[bytes]] = {} - self._input_buffer_count = 0 - self._output_buffer_count = 0 - self._input_lock = threading.Lock() - self._output_lock = threading.Lock() - self._callback: Optional[MediaCodec.Callback] = None - self._handler: Optional[Any] = None - - @staticmethod - def createByCodecName(name: str) -> "MediaCodec": - return MediaCodec(name) - - @staticmethod - def createDecoderByType(type_str: str) -> "MediaCodec": - return MediaCodec(f"decoder.{type_str}") - - @staticmethod - def createEncoderByType(type_str: str) -> "MediaCodec": - return MediaCodec(f"encoder.{type_str}") - - def configure(self, format: "MediaFormat", surface: Any = None, - crypto: Any = None, flags: int = 0) -> None: - self._format = format - self._configured = True - self._input_buffers.clear() - self._output_buffers.clear() - - def start(self) -> None: - if not self._configured: - raise RuntimeException("MediaCodec 未配置") - self._started = True - self._input_buffer_count = 8 - self._output_buffer_count = 8 - - def stop(self) -> None: - self._started = False - - def flush(self) -> None: - if not self._started: - return - with self._input_lock: - self._input_buffers.clear() - with self._output_lock: - self._output_buffers.clear() - - def release(self) -> None: - self.stop() - self._configured = False - self._format = None - - def getInputBuffers(self) -> List[Optional[bytes]]: - return [self._input_buffers.get(i) for i in range(self._input_buffer_count)] - - def getOutputBuffers(self) -> List[Optional[bytes]]: - return [self._output_buffers.get(i) for i in range(self._output_buffer_count)] - - def getInputBuffer(self, idx: int) -> Optional[bytes]: - with self._input_lock: - if idx not in self._input_buffers: - self._input_buffers[idx] = bytearray(1024 * 1024) - return self._input_buffers[idx] - - def getOutputBuffer(self, idx: int) -> Optional[bytes]: - with self._output_lock: - return self._output_buffers.get(idx) - - def queueInputBuffer(self, index: int, offset: int, size: int, - presentation_time_us: int, flags: int) -> None: - pass - - def queueSecureInputBuffer(self, index: int, offset: int, - info: CryptoInfo, - presentation_time_us: int, - flags: int) -> None: - pass - - def dequeueInputBuffer(self, timeout_us: int) -> int: - return 0 - - def dequeueOutputBuffer(self, info: CodecBufferInfo, timeout_us: int) -> int: - return INFO_TRY_AGAIN_LATER - - def releaseOutputBuffer(self, index: int, render: bool) -> None: - pass - - def getOutputFormat(self) -> Optional["MediaFormat"]: - return self._format - - def getCodecInfo(self) -> Optional["MediaCodecInfo"]: - return None - - def getName(self) -> str: - return self._name - - def setCallback(self, cb: Optional["MediaCodec.Callback"], - handler: Any = None) -> None: - self._callback = cb - self._handler = handler - - def createInputSurface(self) -> Any: - raise AndroidOnlyError("createInputSurface() 仅在 Android 上可用") - - def signalEndOfInputStream(self) -> None: - pass - - def getMetrics(self) -> Dict[str, Any]: - return {} - - def setVideoScalingMode(self, mode: int) -> None: - pass - - def getCodecName(self) -> str: - return self._name - - def isConfigured(self) -> bool: - return self._configured - - def isStarted(self) -> bool: - return self._started - - def toString(self) -> str: - return f"MediaCodec(name={self._name}, started={self._started})" - - -class MediaCodecCallback(Stub): - """MediaCodec.Callback — 编解码器回调""" - - def onInputBufferAvailable(self, codec: MediaCodec, index: int) -> None: - pass - - def onOutputBufferAvailable(self, codec: MediaCodec, index: int, - info: CodecBufferInfo) -> None: - pass - - def onError(self, codec: MediaCodec, e: CodecException) -> None: - pass - - def onOutputFormatChanged(self, codec: MediaCodec, format: "MediaFormat") -> None: - pass - - -MediaCodec.Callback = MediaCodecCallback - - -class MediaCodecInfo(Stub): - """MediaCodecInfo — 编解码器信息""" - - class CodecCapabilities(Stub): - """MediaCodecInfo.CodecCapabilities — 编解码能力""" - - FEATURE_AdaptivePlayback = "adaptive-playback" - FEATURE_SecurePlayback = "secure-playback" - FEATURE_TunneledPlayback = "tunneled-playback" - FEATURE_PartialFrame = "partial-frame" - - def __init__(self): - super().__init__() - self.colorFormats: List[int] = [] - self.profileLevels: List[Any] = [] - self.profileEntries: List[Any] = [] - self._flags: int = 0 - self._max_instances: int = 0 - self._bitrate_range: Optional[Tuple[int, int]] = None - self._width_range: Optional[Tuple[int, int]] = None - self._height_range: Optional[Tuple[int, int]] = None - self._frame_rate_range: Optional[Tuple[int, int]] = None - - def getColorFormats(self) -> List[int]: - return list(self.colorFormats) - - def getProfileLevels(self) -> List[Any]: - return list(self.profileLevels) - - def getSupportedFeatures(self) -> List[str]: - features = [] - return features - - def isFeatureSupported(self, name: str) -> bool: - return False - - def getVideoCapabilities(self) -> Optional[Any]: - return None - - def getAudioCapabilities(self) -> Optional[Any]: - return None - - def toString(self) -> str: - return (f"CodecCapabilities(formats={len(self.colorFormats)}, " - f"profiles={len(self.profileLevels)})") - - class CodecProfileLevel(Stub): - """MediaCodecInfo.CodecProfileLevel — 配置等级""" - - def __init__(self): - super().__init__() - self.profile: int = 0 - self.level: int = 0 - - def __init__(self, name: str = "", canonical_name: str = ""): - super().__init__() - self._name = name - self._canonical_name = canonical_name or name - self._is_encoder: bool = False - self._is_vendor: bool = False - self._capabilities: Optional[MediaCodecInfo.CodecCapabilities] = None - self._supported_types: List[str] = [] - - def getName(self) -> str: - return self._name - - def getCanonicalName(self) -> str: - return self._canonical_name - - def isEncoder(self) -> bool: - return self._is_encoder - - def setEncoder(self, is_enc: bool) -> None: - self._is_encoder = is_enc - - def isVendor(self) -> bool: - return self._is_vendor - - def getSupportedTypes(self) -> List[str]: - return list(self._supported_types) - - def getCapabilitiesForType(self, type_str: str) -> Optional[CodecCapabilities]: - if type_str in self._supported_types: - return self._capabilities - return None - - def toString(self) -> str: - return (f"MediaCodecInfo(name={self._name}, " - f"encoder={self._is_encoder}, " - f"types={len(self._supported_types)})") - - -class MediaCodecList(Stub): - """MediaCodecList — 编解码器列表""" - - REGULAR_CODECS = 0 - ALL_CODECS = 1 - - def __init__(self, kind: int = REGULAR_CODECS): - super().__init__() - self._kind = kind - self._codecs: List[MediaCodecInfo] = [] - - def getCodecInfos(self) -> List[MediaCodecInfo]: - return list(self._codecs) - - def findDecoderForFormat(self, format: "MediaFormat") -> Optional[str]: - return None - - def findEncoderForFormat(self, format: "MediaFormat") -> Optional[str]: - return None - - @staticmethod - def getCodecCount() -> int: - return 0 - - @staticmethod - def getCodecInfoAt(index: int) -> Optional[MediaCodecInfo]: - return None - - def toString(self) -> str: - return f"MediaCodecList(codecs={len(self._codecs)})" - - -# ═══════════════════════════════════════════════════════════════ -# MediaExtractor -# ═══════════════════════════════════════════════════════════════ - -class MediaExtractor(Stub): - """MediaExtractor — 媒体提取器""" - - SEEK_TO_PREVIOUS_SYNC = 0 - SEEK_TO_NEXT_SYNC = 1 - SEEK_TO_CLOSEST_SYNC = 2 - SAMPLE_FLAG_SYNC = 1 - SAMPLE_FLAG_ENCRYPTED = 2 - - def __init__(self): - super().__init__() - self._data_source: Optional[str] = None - self._fd: Optional[int] = None - self._track_count: int = 0 - self._selected_tracks: Set[int] = set() - self._current_track: int = -1 - self._sample_time: int = 0 - self._sample_flags: int = 0 - self._sample_size: int = 0 - self._sample_track_index: int = -1 - self._cached_info: Dict[int, "MediaFormat"] = {} - self._released = False - self._lock = threading.Lock() - - def setDataSource(self, path: str) -> None: - self._data_source = path - self._fd = None - self._parse_source() - - def setDataSource(self, path: str, headers: Dict[str, str] = None) -> None: - self._data_source = path - self._fd = None - self._headers = headers or {} - self._parse_source() - - def setDataSource(self, fd: int) -> None: - self._fd = fd - self._data_source = None - if fd > 0: - self._track_count = 1 - - def _parse_source(self) -> None: - if self._data_source and os.path.exists(self._data_source): - self._track_count = 1 - - def getTrackCount(self) -> int: - return self._track_count - - def getTrackFormat(self, index: int) -> Optional["MediaFormat"]: - return self._cached_info.get(index) - - def selectTrack(self, index: int) -> None: - with self._lock: - if 0 <= index < self._track_count: - self._selected_tracks.add(index) - - def unselectTrack(self, index: int) -> None: - with self._lock: - self._selected_tracks.discard(index) - - def getSampleTrackIndex(self) -> int: - return self._sample_track_index - - def readSampleData(self, byte_buf: Any, offset: int) -> int: - return -1 - - def getSampleTime(self) -> int: - return self._sample_time - - def getSampleFlags(self) -> int: - return self._sample_flags - - def getSampleSize(self) -> int: - return self._sample_size - - def advance(self) -> bool: - return False - - def seekTo(self, time_us: int, mode: int = SEEK_TO_CLOSEST_SYNC) -> None: - self._sample_time = time_us - - def release(self) -> None: - self._released = True - self._cached_info.clear() - self._selected_tracks.clear() - - def getCachedDuration(self) -> int: - return -1 - - def hasCacheReachedEndOfStream(self) -> bool: - return False - - def getSampleCryptoInfo(self, info: CryptoInfo) -> bool: - return False - - def toString(self) -> str: - return (f"MediaExtractor(source={self._data_source}, " - f"tracks={self._track_count})") - - -# ═══════════════════════════════════════════════════════════════ -# MediaMuxer -# ═══════════════════════════════════════════════════════════════ - -class MediaMuxer(Stub): - """MediaMuxer — 媒体复用器""" - - OUTPUT_FORMAT_MPEG4 = 0 - OUTPUT_FORMAT_WEBM = 1 - OUTPUT_FORMAT_3GPP = 2 - OUTPUT_FORMAT_HEIF = 3 - OUTPUT_FORMAT_OGG = 4 - - def __init__(self, path: str, format: int = OUTPUT_FORMAT_MPEG4): - super().__init__() - self._path = path - self._format = format - self._started = False - self._track_count = 0 - self._tracks: Dict[int, "MediaFormat"] = {} - self._next_track_index = 0 - self._released = False - - def addTrack(self, format: "MediaFormat") -> int: - idx = self._next_track_index - self._next_track_index += 1 - self._tracks[idx] = format - self._track_count = len(self._tracks) - return idx - - def start(self) -> None: - if self._started: - return - self._started = True - - def stop(self) -> None: - self._started = False - - def release(self) -> None: - self._released = True - self._tracks.clear() - self._started = False - - def writeSampleData(self, track_index: int, byte_buf: Any, - offset: int, buffer_info: CodecBufferInfo) -> None: - pass - - def setOrientationHint(self, degrees: int) -> None: - pass - - def setLocation(self, latitude: float, longitude: float) -> None: - pass - - def getTrackCount(self) -> int: - return self._track_count - - def getOutputFormat(self) -> int: - return self._format - - def getPath(self) -> str: - return self._path - - def isStarted(self) -> bool: - return self._started - - def toString(self) -> str: - return (f"MediaMuxer(path={self._path}, format={self._format}, " - f"tracks={self._track_count})") - - -# ═══════════════════════════════════════════════════════════════ -# MediaFormat -# ═══════════════════════════════════════════════════════════════ - -class MediaFormat(Stub): - """MediaFormat — 媒体格式""" - - # MIME 类型 - MIMETYPE_VIDEO_AV1 = MIMETYPE_VIDEO_AV1 - MIMETYPE_VIDEO_VP9 = MIMETYPE_VIDEO_VP9 - MIMETYPE_VIDEO_VP8 = MIMETYPE_VIDEO_VP8 - MIMETYPE_VIDEO_H265 = MIMETYPE_VIDEO_H265 - MIMETYPE_VIDEO_H264 = MIMETYPE_VIDEO_H264 - MIMETYPE_VIDEO_MPEG2 = MIMETYPE_VIDEO_MPEG2 - MIMETYPE_VIDEO_MPEG4 = MIMETYPE_VIDEO_MPEG4 - MIMETYPE_VIDEO_VC1 = MIMETYPE_VIDEO_VC1 - MIMETYPE_VIDEO_DIVX = MIMETYPE_VIDEO_DIVX - MIMETYPE_VIDEO_DIVX3 = MIMETYPE_VIDEO_DIVX3 - MIMETYPE_VIDEO_FLV = MIMETYPE_VIDEO_FLV - MIMETYPE_VIDEO_H263 = MIMETYPE_VIDEO_H263 - MIMETYPE_VIDEO_RAW = MIMETYPE_VIDEO_RAW - MIMETYPE_VIDEO_DOLBY_VISION = MIMETYPE_VIDEO_DOLBY_VISION - MIMETYPE_VIDEO_SCRAMBLED = MIMETYPE_VIDEO_SCRAMBLED - MIMETYPE_AUDIO_AAC = MIMETYPE_AUDIO_AAC - MIMETYPE_AUDIO_MP3 = MIMETYPE_AUDIO_MP3 - MIMETYPE_AUDIO_AMR_NB = MIMETYPE_AUDIO_AMR_NB - MIMETYPE_AUDIO_AMR_WB = MIMETYPE_AUDIO_AMR_WB - MIMETYPE_AUDIO_FLAC = MIMETYPE_AUDIO_FLAC - MIMETYPE_AUDIO_VORBIS = MIMETYPE_AUDIO_VORBIS - MIMETYPE_AUDIO_OPUS = MIMETYPE_AUDIO_OPUS - MIMETYPE_AUDIO_RAW = MIMETYPE_AUDIO_RAW - MIMETYPE_AUDIO_ALAC = MIMETYPE_AUDIO_ALAC - MIMETYPE_AUDIO_AC3 = MIMETYPE_AUDIO_AC3 - MIMETYPE_AUDIO_EAC3 = MIMETYPE_AUDIO_EAC3 - MIMETYPE_TEXT_VTT = MIMETYPE_TEXT_VTT - MIMETYPE_TEXT_CEA_608 = MIMETYPE_TEXT_CEA_608 - MIMETYPE_TEXT_CEA_708 = MIMETYPE_TEXT_CEA_708 - MIMETYPE_SUBTITLE_SRT = MIMETYPE_SUBTITLE_SRT - MIMETYPE_SUBTITLE_TTML = MIMETYPE_SUBTITLE_TTML - - # KEY 常量 - KEY_MIME = KEY_MIME - KEY_WIDTH = KEY_WIDTH - KEY_HEIGHT = KEY_HEIGHT - KEY_FRAME_RATE = KEY_FRAME_RATE - KEY_BIT_RATE = KEY_BIT_RATE - KEY_COLOR_FORMAT = KEY_COLOR_FORMAT - KEY_I_FRAME_INTERVAL = KEY_I_FRAME_INTERVAL - KEY_CHANNEL_COUNT = KEY_CHANNEL_COUNT - KEY_SAMPLE_RATE = KEY_SAMPLE_RATE - KEY_MAX_INPUT_SIZE = KEY_MAX_INPUT_SIZE - KEY_DURATION = KEY_DURATION - KEY_IS_ADTS = KEY_IS_ADTS - KEY_CHANNEL_MASK = KEY_CHANNEL_MASK - KEY_ENCODING = KEY_ENCODING - KEY_PROFILE = KEY_PROFILE - KEY_LEVEL = KEY_LEVEL - KEY_LANGUAGE = KEY_LANGUAGE - KEY_PRIORITY = KEY_PRIORITY - KEY_OPERATING_RATE = KEY_OPERATING_RATE - KEY_INTRA_REFRESH_PERIOD = KEY_INTRA_REFRESH_PERIOD - KEY_LATENCY = KEY_LATENCY - KEY_ROTATION = KEY_ROTATION - KEY_CAPTURE_RATE = KEY_CAPTURE_RATE - KEY_QUALITY = KEY_QUALITY - KEY_MAX_BIT_RATE = KEY_MAX_BIT_RATE - KEY_FLAC_COMPRESSION_LEVEL = KEY_FLAC_COMPRESSION_LEVEL - KEY_TRACK_ID = KEY_TRACK_ID - KEY_IS_AUTOSELECT = KEY_IS_AUTOSELECT - KEY_IS_DEFAULT = KEY_IS_DEFAULT - KEY_IS_FORCED_SUBTITLE = KEY_IS_FORCED_SUBTITLE - - # 色彩格式 - COLOR_FormatUnsupported = COLOR_FormatUnsupported - COLOR_FormatYUV420Planar = COLOR_FormatYUV420Planar - COLOR_FormatYUV420SemiPlanar = COLOR_FormatYUV420SemiPlanar - COLOR_FormatYUV420Flexible = COLOR_FormatYUV420Flexible - COLOR_FormatSurface = COLOR_FormatSurface - COLOR_Format32BitARGB8888 = COLOR_Format32BitARGB8888 - - def __init__(self): - super().__init__() - self._properties: Dict[str, Any] = {} - - @staticmethod - def createVideoFormat(mime: str, width: int, height: int) -> "MediaFormat": - fmt = MediaFormat() - fmt.setString(KEY_MIME, mime) - fmt.setInteger(KEY_WIDTH, width) - fmt.setInteger(KEY_HEIGHT, height) - return fmt - - @staticmethod - def createAudioFormat(mime: str, sample_rate: int, channel_count: int) -> "MediaFormat": - fmt = MediaFormat() - fmt.setString(KEY_MIME, mime) - fmt.setInteger(KEY_SAMPLE_RATE, sample_rate) - fmt.setInteger(KEY_CHANNEL_COUNT, channel_count) - return fmt - - @staticmethod - def createSubtitleFormat(mime: str, language: str) -> "MediaFormat": - fmt = MediaFormat() - fmt.setString(KEY_MIME, mime) - fmt.setString(KEY_LANGUAGE, language) - return fmt - - def containsKey(self, key: str) -> bool: - return key in self._properties - - def getInteger(self, key: str, default_value: int = 0) -> int: - val = self._properties.get(key, default_value) - return int(val) if isinstance(val, (int, float)) else default_value - - def getFloat(self, key: str, default_value: float = 0.0) -> float: - val = self._properties.get(key, default_value) - return float(val) if isinstance(val, (int, float)) else default_value - - def getLong(self, key: str, default_value: int = 0) -> int: - return self.getInteger(key, default_value) - - def getString(self, key: str, default_value: Optional[str] = None) -> Optional[str]: - val = self._properties.get(key, default_value) - return str(val) if val is not None else None - - def getByteBuffer(self, key: str) -> Optional[bytes]: - val = self._properties.get(key) - if isinstance(val, (bytes, bytearray)): - return bytes(val) - return None - - def setInteger(self, key: str, value: int) -> None: - self._properties[key] = value - - def setFloat(self, key: str, value: float) -> None: - self._properties[key] = value - - def setLong(self, key: str, value: int) -> None: - self._properties[key] = value - - def setString(self, key: str, value: str) -> None: - self._properties[key] = value - - def setByteBuffer(self, key: str, buf: bytes) -> None: - self._properties[key] = buf - - def getMap(self) -> Dict[str, Any]: - return dict(self._properties) - - def toString(self) -> str: - parts = [f"{k}={v}" for k, v in self._properties.items()] - return f"MediaFormat({', '.join(parts)})" - - -# ═══════════════════════════════════════════════════════════════ -# MediaCrypto -# ═══════════════════════════════════════════════════════════════ - -class MediaCrypto(Stub): - """MediaCrypto — 媒体加密""" - - def __init__(self, uuid: str = ""): - super().__init__() - self._uuid = uuid - self._init_data: Optional[bytes] = None - - def requiresSecureDecoderComponent(self, mime: str) -> bool: - return False - - def setMediaDrmSession(self, session_id: bytes) -> None: - pass - - def getUUID(self) -> str: - return self._uuid - - def toString(self) -> str: - return f"MediaCrypto(uuid={self._uuid})" - - -# ═══════════════════════════════════════════════════════════════ -# MediaDrm -# ═══════════════════════════════════════════════════════════════ - -class MediaDrm(Stub): - """MediaDrm — 数字版权管理""" - - KEY_TYPE_STREAMING = 1 - KEY_TYPE_OFFLINE = 2 - KEY_TYPE_RELEASE = 3 - - EVENT_PROVISION_REQUIRED = 1 - EVENT_KEY_REQUIRED = 2 - EVENT_KEY_EXPIRED = 3 - EVENT_VENDOR_DEFINED = 4 - EVENT_SESSION_RECLAIMED = 5 - - KEY_EXPIRED = 0 - KEY_STATUS_OK = 1 - KEY_STATUS_PENDING = 2 - KEY_STATUS_USABLE = 3 - KEY_STATUS_USABLE_IN_FUTURE = 4 - - def __init__(self, uuid: str = ""): - super().__init__() - self._uuid = uuid - self._sessions: Dict[str, bytes] = {} - self._event_listener: Optional[Callable] = None - self._expiration_update_listener: Optional[Callable] = None - self._key_status_listener: Optional[Callable] = None - - def openSession(self) -> Optional[bytes]: - import os - session_id = os.urandom(16) - self._sessions[session_id.hex()] = session_id - return session_id - - def closeSession(self, session_id: bytes) -> None: - key = session_id.hex() if isinstance(session_id, bytes) else session_id - self._sessions.pop(key, None) - - def getKeyRequest(self, session_id: bytes, init_data: bytes, - mime_type: str, key_type: int, - optional_parameters: Optional[Dict[str, str]] = None) -> Any: - return None - - def provideKeyResponse(self, session_id: bytes, response: bytes) -> Optional[bytes]: - return None - - def restoreKeys(self, session_id: bytes, key_set_id: bytes) -> None: - pass - - def removeKeys(self, session_id: bytes) -> None: - pass - - def queryKeyStatus(self, session_id: bytes) -> Optional[Dict[str, str]]: - return {"status": "usable"} - - def getProvisionRequest(self) -> Optional[Tuple[bytes, str]]: - return None - - def provideProvisionResponse(self, response: bytes) -> None: - pass - - def getSecureStops(self) -> List[bytes]: - return [] - - def getSecureStop(self, ssid: bytes) -> Optional[bytes]: - return None - - def releaseSecureStops(self, ssid: bytes) -> None: - pass - - def releaseAllSecureStops(self) -> None: - pass - - def getPropertyString(self, name: str) -> str: - return "" - - def setPropertyString(self, name: str, value: str) -> None: - pass - - def getPropertyByteArray(self, name: str) -> Optional[bytes]: - return None - - def setPropertyByteArray(self, name: str, value: bytes) -> None: - pass - - def getMetrics(self) -> Dict[str, Any]: - return {} - - def getMaxSessionCount(self) -> int: - return 128 - - def getOpenSessionCount(self) -> int: - return len(self._sessions) - - def getTotalSessionCount(self) -> int: - return len(self._sessions) - - def release(self) -> None: - self._sessions.clear() - - @staticmethod - def isCryptoSchemeSupported(uuid: str) -> bool: - return False - - def toString(self) -> str: - return f"MediaDrm(uuid={self._uuid})" - - -# ═══════════════════════════════════════════════════════════════ -# MediaDescription / MediaMetadata -# ═══════════════════════════════════════════════════════════════ - -class MediaDescription(Stub): - """MediaDescription — 媒体描述""" - - BT_FOLDER_TYPE_ALBUMS = 1 - BT_FOLDER_TYPE_ARTISTS = 2 - BT_FOLDER_TYPE_GENRES = 3 - BT_FOLDER_TYPE_PLAYLISTS = 4 - BT_FOLDER_TYPE_YEARS = 5 - EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE" - EXTRA_DOWNLOAD_STATUS = "android.media.extra.DOWNLOAD_STATUS" - - def __init__(self): - super().__init__() - self._media_id: str = "" - self._title: Optional[str] = None - self._subtitle: Optional[str] = None - self._description: Optional[str] = None - self._icon_uri: Optional[str] = None - self._extras: Dict[str, Any] = {} - self._media_uri: Optional[str] = None - self._flags: int = 0 - - def getMediaId(self) -> Optional[str]: - return self._media_id - - def getTitle(self) -> Optional[str]: - return self._title - - def getSubtitle(self) -> Optional[str]: - return self._subtitle - - def getDescription(self) -> Optional[str]: - return self._description - - def getIconUri(self) -> Optional[str]: - return self._icon_uri - - def getExtras(self) -> Optional[Dict[str, Any]]: - return dict(self._extras) if self._extras else None - - def getMediaUri(self) -> Optional[str]: - return self._media_uri - - def toString(self) -> str: - return (f"MediaDescription(id={self._media_id}, " - f"title={self._title})") - - -class MediaMetadata(Stub): - """MediaMetadata — 媒体元数据""" - - METADATA_KEY_ALBUM = "android.media.metadata.ALBUM" - METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART" - METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST" - METADATA_KEY_ART = "android.media.metadata.ART" - METADATA_KEY_ARTIST = "android.media.metadata.ARTIST" - METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR" - METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION" - METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER" - METADATA_KEY_DATE = "android.media.metadata.DATE" - METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER" - METADATA_KEY_DURATION = "android.media.metadata.DURATION" - METADATA_KEY_GENRE = "android.media.metadata.GENRE" - METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS" - METADATA_KEY_RATING = "android.media.metadata.RATING" - METADATA_KEY_TITLE = "android.media.metadata.TITLE" - METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER" - METADATA_KEY_WRITER = "android.media.metadata.WRITER" - METADATA_KEY_YEAR = "android.media.metadata.YEAR" - METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID" - METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE" - METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE" - METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION" - METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON" - METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING" - - def __init__(self): - super().__init__() - self._values: Dict[str, Any] = {} - - def containsKey(self, key: str) -> bool: - return key in self._values - - def getString(self, key: str) -> Optional[str]: - val = self._values.get(key) - return str(val) if val is not None else None - - def getLong(self, key: str) -> int: - return int(self._values.get(key, 0)) - - def getFloat(self, key: str) -> float: - return float(self._values.get(key, 0.0)) - - def getBitmap(self, key: str) -> Optional[Any]: - return None - - def putString(self, key: str, value: str) -> None: - self._values[key] = value - - def putLong(self, key: str, value: int) -> None: - self._values[key] = value - - def putFloat(self, key: str, value: float) -> None: - self._values[key] = value - - def putBitmap(self, key: str, bitmap: Any) -> None: - pass - - def size(self) -> int: - return len(self._values) - - def keySet(self) -> Set[str]: - return set(self._values.keys()) - - def toString(self) -> str: - return f"MediaMetadata(entries={len(self._values)})" - - -# ═══════════════════════════════════════════════════════════════ -# MediaMetadataRetriever -# ═══════════════════════════════════════════════════════════════ - -class MediaMetadataRetriever(Stub): - """MediaMetadataRetriever — 媒体元数据检索器""" - - OPTION_PREVIOUS_SYNC = 0 - OPTION_NEXT_SYNC = 1 - OPTION_CLOSEST_SYNC = 2 - OPTION_CLOSEST = 3 - - METADATA_KEY_ALBUM = 1 - METADATA_KEY_ALBUM_ARTIST = 13 - METADATA_KEY_ARTIST = 2 - METADATA_KEY_AUTHOR = 3 - METADATA_KEY_BITRATE = 20 - METADATA_KEY_CD_TRACK_NUMBER = 0 - METADATA_KEY_COMPILATION = 15 - METADATA_KEY_COMPOSER = 4 - METADATA_KEY_DATE = 5 - METADATA_KEY_DISC_NUMBER = 14 - METADATA_KEY_DURATION = 9 - METADATA_KEY_GENRE = 6 - METADATA_KEY_HAS_AUDIO = 16 - METADATA_KEY_HAS_VIDEO = 17 - METADATA_KEY_IS_DRM = 22 - METADATA_KEY_LOCATION = 23 - METADATA_KEY_MIMETYPE = 12 - METADATA_KEY_NUM_TRACKS = 10 - METADATA_KEY_TITLE = 7 - METADATA_KEY_VIDEO_HEIGHT = 19 - METADATA_KEY_VIDEO_WIDTH = 18 - METADATA_KEY_WRITER = 11 - METADATA_KEY_YEAR = 8 - - def __init__(self): - super().__init__() - self._data_source: Optional[str] = None - self._extracted: Dict[int, str] = {} - self._released = False - - def setDataSource(self, path: str) -> None: - self._data_source = path - self._extract() - - def setDataSource(self, path: str, headers: Dict[str, str]) -> None: - self._data_source = path - self._headers = headers - self._extract() - - def setDataSource(self, uri: Any, headers: Dict[str, str]) -> None: - self._data_source = str(uri) - self._extract() - - def setDataSource(self, fd: int) -> None: - self._data_source = f"fd:{fd}" - self._extract() - - def _extract(self) -> None: - if self._data_source and os.path.exists(str(self._data_source)): - self._extracted[METADATA_KEY_HAS_AUDIO] = "yes" - self._extracted[METADATA_KEY_HAS_VIDEO] = "yes" - - def extractMetadata(self, key_code: int) -> Optional[str]: - return self._extracted.get(key_code) - - def getEmbeddedPicture(self) -> Optional[bytes]: - return None - - def getFrameAtTime(self, time_us: int = -1, - option: int = OPTION_CLOSEST_SYNC) -> Optional[Any]: - return None - - def getScaledFrameAtTime(self, time_us: int, - option: int, - width: int, height: int) -> Optional[Any]: - return None - - def getPrimaryImage(self) -> Optional[Any]: - return None - - def release(self) -> None: - self._released = True - self._extracted.clear() - - def toString(self) -> str: - return f"MediaMetadataRetriever(source={self._data_source})" - - -# ═══════════════════════════════════════════════════════════════ -# MediaPlayer 扩展 -# ═══════════════════════════════════════════════════════════════ - -class MediaPlayer(Stub): - """MediaPlayer — 媒体播放器(扩展)""" - - MEDIA_ERROR_UNKNOWN = 1 - MEDIA_ERROR_SERVER_DIED = 100 - MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200 - MEDIA_ERROR_IO = -1004 - MEDIA_ERROR_MALFORMED = -1007 - MEDIA_ERROR_TIMED_OUT = -110 - MEDIA_ERROR_UNSUPPORTED = -1010 - - MEDIA_INFO_UNKNOWN = 1 - MEDIA_INFO_VIDEO_TRACK_LAGGING = 700 - MEDIA_INFO_VIDEO_RENDERING_START = 3 - MEDIA_INFO_BUFFERING_START = 701 - MEDIA_INFO_BUFFERING_END = 702 - MEDIA_INFO_BAD_INTERLEAVING = 800 - MEDIA_INFO_NOT_SEEKABLE = 801 - MEDIA_INFO_METADATA_UPDATE = 802 - MEDIA_INFO_AUDIO_NOT_PLAYING = 804 - MEDIA_INFO_VIDEO_NOT_PLAYING = 805 - MEDIA_INFO_SUBTITLE_TIMED_OUT = 902 - MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901 - - SEEK_CLOSEST = 3 - SEEK_CLOSEST_SYNC = 2 - SEEK_NEXT_SYNC = 1 - SEEK_PREVIOUS_SYNC = 0 - - VIDEO_SCALING_MODE_SCALE_TO_FIT = 1 - VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2 - - def __init__(self): - super().__init__() - self._data_source: Optional[str] = None - self._isplaying = False - self._isloop = False - self._volume: float = 1.0 - self._position: int = 0 - self._duration: int = 0 - self._isplayback = False - self._isprepared = False - self._on_completion: Optional[Callable] = None - self._on_error: Optional[Callable] = None - self._on_prepared: Optional[Callable] = None - self._on_info: Optional[Callable] = None - self._lock = threading.Lock() - self._surface: Optional[Any] = None - - @staticmethod - def create(context: Any, uri: Any) -> "MediaPlayer": - mp = MediaPlayer() - mp.setDataSource(str(uri)) - mp.prepare() - return mp - - def setDataSource(self, path: str) -> None: - self._data_source = path - - def setDataSource(self, path: str, headers: Dict[str, str]) -> None: - self._data_source = path - - def prepare(self) -> None: - self._isprepared = True - self._duration = 10000 - if self._on_prepared: - self._on_prepared(self) - - def prepareAsync(self) -> None: - self._isprepared = True - if self._on_prepared: - self._on_prepared(self) - - def start(self) -> None: - self._isplaying = True - self._isplayback = True - - def stop(self) -> None: - self._isplaying = False - self._isplayback = False - self._position = 0 - - def pause(self) -> None: - self._isplaying = False - - def seekTo(self, msec: int, mode: int = SEEK_CLOSEST) -> None: - self._position = max(0, msec) - - def getCurrentPosition(self) -> int: - return self._position - - def getDuration(self) -> int: - return self._duration - - def isPlaying(self) -> bool: - return self._isplaying - - def isLooping(self) -> bool: - return self._isloop - - def setLooping(self, looping: bool) -> None: - self._isloop = looping - - def setVolume(self, left_volume: float, right_volume: float) -> None: - self._volume = (left_volume + right_volume) / 2.0 - - def getVolume(self) -> float: - return self._volume - - def setSurface(self, surface: Any) -> None: - self._surface = surface - - def setDisplay(self, holder: Any) -> None: - pass - - def setScreenOnWhilePlaying(self, screen_on: bool) -> None: - pass - - def setWakeMode(self, context: Any, mode: int) -> None: - pass - - def setAudioStreamType(self, stream_type: int) -> None: - pass - - def reset(self) -> None: - self.stop() - self._isprepared = False - - def release(self) -> None: - self.reset() - - def getVideoWidth(self) -> int: - return 0 - - def getVideoHeight(self) -> int: - return 0 - - def setOnCompletionListener(self, listener: Optional[Callable]) -> None: - self._on_completion = listener - - def setOnErrorListener(self, listener: Optional[Callable]) -> None: - self._on_error = listener - - def setOnPreparedListener(self, listener: Optional[Callable]) -> None: - self._on_prepared = listener - - def setOnInfoListener(self, listener: Optional[Callable]) -> None: - self._on_info = listener - - def setOnBufferingUpdateListener(self, listener: Optional[Callable]) -> None: - pass - - def setOnSeekCompleteListener(self, listener: Optional[Callable]) -> None: - pass - - def setOnVideoSizeChangedListener(self, listener: Optional[Callable]) -> None: - pass - - def attachAuxEffect(self, effect_id: int) -> None: - pass - - def toString(self) -> str: - return (f"MediaPlayer(source={self._data_source}, " - f"playing={self._isplaying})") - - -# ═══════════════════════════════════════════════════════════════ -# MediaSync / MediaTimeProvider / TimedMetaData / WebVttParser -# ═══════════════════════════════════════════════════════════════ - -class MediaSync(Stub): - """MediaSync — 媒体同步器""" - - def __init__(self): - super().__init__() - self._surface: Optional[Any] = None - self._playback_params: Optional[Any] = None - self._callback: Optional[Callable] = None - self._timestamp: int = 0 - self._playback_rate: float = 1.0 - - def setSurface(self, surface: Any) -> None: - self._surface = surface - - def setCallback(self, cb: Optional[Callable], handler: Any = None) -> None: - self._callback = cb - - def getPlaybackParams(self) -> Any: - return self._playback_params - - def setPlaybackParams(self, params: Any) -> None: - self._playback_params = params - - def queueAudioBuffer(self, buffer_info: CodecBufferInfo, data: bytes, - offset: int, size: int, presentation_time_us: int) -> None: - pass - - def queueVideoBuffer(self, data: bytes, buffer_info: CodecBufferInfo, - presentation_time_us: int) -> None: - pass - - def render(self, presentation_time_us: int) -> bool: - return True - - def flush(self) -> None: - pass - - def getTimestamp(self) -> int: - return self._timestamp - - def release(self) -> None: - pass - - def toString(self) -> str: - return f"MediaSync(rate={self._playback_rate})" - - -class MediaTimeProvider(Stub): - """MediaTimeProvider — 媒体时间提供器""" - - def getTimeUs(self) -> int: - return int(time.time() * 1000000) - - -class TimedMetaData(Stub): - """TimedMetaData — 定时元数据""" - - def __init__(self, timestamp_us: int = 0, meta_data: bytes = b""): - super().__init__() - self._timestamp_us = timestamp_us - self._meta_data = meta_data - - def getTimestamp(self) -> int: - return self._timestamp_us - - def getMetaData(self) -> bytes: - return self._meta_data - - def toString(self) -> str: - return (f"TimedMetaData(timestamp={self._timestamp_us}, " - f"size={len(self._meta_data)})") - - -class WebVttParser(Stub): - """WebVttParser — WebVTT 字幕解析器""" - - def __init__(self): - super().__init__() - self._cues: List["WebVttCue"] = [] - - def parse(self, data: str) -> List["WebVttCue"]: - self._cues.clear() - lines = data.split("\n") - current_cue: Optional["WebVttCue"] = None - for line in lines: - line = line.strip() - if line.startswith("WEBVTT"): - continue - if line == "": - if current_cue is not None: - self._cues.append(current_cue) - current_cue = None - continue - if "-->" in line: - parts = line.split("-->") - start = self._parse_time(parts[0].strip()) - end = self._parse_time(parts[1].strip()) - current_cue = WebVttCue(start, end) - elif current_cue is not None: - current_cue.add_text(line) - if current_cue is not None: - self._cues.append(current_cue) - return list(self._cues) - - def _parse_time(self, time_str: str) -> int: - """将 HH:MM:SS.mmm 格式转换为微秒""" - if ":" in time_str: - parts = time_str.split(":") - if len(parts) == 3: - h, m, s = parts - else: - h = "0" - m, s = parts - sec_parts = s.split(".") - seconds = int(sec_parts[0]) - millis = int(sec_parts[1]) if len(sec_parts) > 1 else 0 - total_ms = int(h) * 3600000 + int(m) * 60000 + seconds * 1000 + millis - return total_ms * 1000 - return 0 - - def getCues(self) -> List["WebVttCue"]: - return list(self._cues) - - def clear(self) -> None: - self._cues.clear() - - -class WebVttCue(Stub): - """WebVttCue — WebVTT 字幕提示""" - - def __init__(self, start_us: int = 0, end_us: int = 0, text: str = ""): - super().__init__() - self._start_us = start_us - self._end_us = end_us - self._text = text - - def add_text(self, text: str) -> None: - if self._text: - self._text += "\n" + text - else: - self._text = text - - def getStartTime(self) -> int: - return self._start_us - - def getEndTime(self) -> int: - return self._end_us - - def getText(self) -> str: - return self._text - - def toString(self) -> str: - return (f"WebVttCue(start={self._start_us}, " - f"end={self._end_us}, text={self._text[:30]})") \ No newline at end of file diff --git a/android_pyapi/android/media_audio.py b/android_pyapi/android/media_audio.py deleted file mode 100644 index 9ea7d89..0000000 --- a/android_pyapi/android/media_audio.py +++ /dev/null @@ -1,1538 +0,0 @@ -""" -Android media 音频扩展组件 — AudioFormat, AudioTrack, AudioRecord, AudioDeviceInfo, -AudioRouting, AudioManager, AudioMix, AudioProfile, AudioProductStrategy 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import math -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "AudioFormat", "AudioAttributes", "AudioTrack", "AudioRecord", - "AudioDeviceInfo", "AudioDeviceCallback", "AudioRouting", - "AudioTimestamp", "AudioPresentation", "AudioPlaybackConfiguration", - "AudioRecordingConfiguration", "AudioManagerAudio", "AudioMixPort", - "AudioPort", "AudioPatch", "AudioGain", "AudioGainConfig", - "AudioMix", "AudioProfile", "AudioProductStrategy", "AudioVolumeGroup", -] - - -# ═══════════════════════════════════════════════════════════════ -# AudioFormat -# ═══════════════════════════════════════════════════════════════ - -class AudioFormat(Stub): - """AudioFormat — 音频格式""" - - ENCODING_INVALID = 0 - ENCODING_DEFAULT = 1 - ENCODING_PCM_16BIT = 2 - ENCODING_PCM_8BIT = 3 - ENCODING_PCM_FLOAT = 4 - ENCODING_AC3 = 5 - ENCODING_E_AC3 = 6 - ENCODING_DTS = 7 - ENCODING_DTS_HD = 8 - ENCODING_MP3 = 9 - ENCODING_AAC_LC = 10 - ENCODING_AAC_HE_V1 = 11 - ENCODING_AAC_HE_V2 = 12 - ENCODING_PCM_24BIT_PACKED = 13 - ENCODING_PCM_32BIT = 14 - ENCODING_PCM_16BIT_BUNDLED = 15 - ENCODING_PCM_24BIT = 16 - ENCODING_DOLBY_TRUEHD = 17 - ENCODING_DOLBY_MAT = 18 - ENCODING_IEC61937 = 19 - ENCODING_OPUS = 20 - ENCODING_AMRNB = 21 - ENCODING_AMRWB = 22 - ENCODING_FLAC = 23 - ENCODING_MP2 = 24 - ENCODING_AC4 = 25 - ENCODING_AC3_PLUS = 26 - ENCODING_E_AC3_JOC = 27 - ENCODING_DOLBY_MAT_21 = 28 - ENCODING_DSD = 29 - ENCODING_PCM_8_24_BIT = 30 - ENCODING_WMA = 31 - ENCODING_WMA_PRO = 32 - ENCODING_ALAC = 33 - ENCODING_APE = 34 - ENCODING_VORBIS = 35 - - CHANNEL_INVALID = 0 - CHANNEL_OUT_FRONT_LEFT = 0x4 - CHANNEL_OUT_FRONT_RIGHT = 0x8 - CHANNEL_OUT_FRONT_CENTER = 0x10 - CHANNEL_OUT_LOW_FREQUENCY = 0x20 - CHANNEL_OUT_BACK_LEFT = 0x40 - CHANNEL_OUT_BACK_RIGHT = 0x80 - CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x100 - CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200 - CHANNEL_OUT_BACK_CENTER = 0x400 - CHANNEL_OUT_SIDE_LEFT = 0x800 - CHANNEL_OUT_SIDE_RIGHT = 0x1000 - CHANNEL_OUT_TOP_CENTER = 0x2000 - CHANNEL_OUT_TOP_FRONT_LEFT = 0x4000 - CHANNEL_OUT_TOP_FRONT_RIGHT = 0x8000 - CHANNEL_OUT_TOP_FRONT_CENTER = 0x10000 - CHANNEL_OUT_TOP_BACK_LEFT = 0x20000 - CHANNEL_OUT_TOP_BACK_RIGHT = 0x40000 - CHANNEL_OUT_TOP_BACK_CENTER = 0x80000 - CHANNEL_OUT_MONO = 0x4 - CHANNEL_OUT_STEREO = 0xC - CHANNEL_OUT_QUAD = 0xCC - CHANNEL_OUT_SURROUND = 0x41C - CHANNEL_OUT_5POINT1 = 0xFC - CHANNEL_OUT_7POINT1 = 0x4FC - CHANNEL_OUT_5POINT1_SIDE = 0x8FC - CHANNEL_OUT_7POINT1_SURROUND = 0x18FC - - CHANNEL_IN_LEFT = 0x4 - CHANNEL_IN_RIGHT = 0x8 - CHANNEL_IN_FRONT = 0x10 - CHANNEL_IN_BACK = 0x40 - CHANNEL_IN_LEFT_PROCESSED = 0x20 - CHANNEL_IN_RIGHT_PROCESSED = 0x80 - CHANNEL_IN_FRONT_PROCESSED = 0x100 - CHANNEL_IN_BACK_PROCESSED = 0x200 - CHANNEL_IN_PRESSURE = 0x400 - CHANNEL_IN_X_AXIS = 0x800 - CHANNEL_IN_Y_AXIS = 0x1000 - CHANNEL_IN_Z_AXIS = 0x2000 - CHANNEL_IN_VOICE_UPLINK = 0x4000 - CHANNEL_IN_VOICE_DNLINK = 0x8000 - CHANNEL_IN_MONO = 0x10 - CHANNEL_IN_STEREO = 0xC - CHANNEL_IN_DEFAULT = 0x1 - - SAMPLE_RATE_UNSPECIFIED = 0 - - class Builder(Stub): - """AudioFormat.Builder — 音频格式构建器""" - - def __init__(self): - super().__init__() - self._encoding = AudioFormat.ENCODING_DEFAULT - self._sample_rate = AudioFormat.SAMPLE_RATE_UNSPECIFIED - self._channel_mask = AudioFormat.CHANNEL_INVALID - self._channel_index_mask = 0 - - def setEncoding(self, encoding: int) -> "AudioFormat.Builder": - self._encoding = encoding - return self - - def setSampleRate(self, sampleRate: int) -> "AudioFormat.Builder": - self._sample_rate = sampleRate - return self - - def setChannelMask(self, channelMask: int) -> "AudioFormat.Builder": - self._channel_mask = channelMask - return self - - def setChannelIndexMask(self, channelIndexMask: int) -> "AudioFormat.Builder": - self._channel_index_mask = channelIndexMask - return self - - def build(self) -> "AudioFormat": - fmt = AudioFormat() - fmt._encoding = self._encoding - fmt._sample_rate = self._sample_rate - fmt._channel_mask = self._channel_mask - fmt._channel_index_mask = self._channel_index_mask - return fmt - - def __init__(self): - super().__init__() - self._encoding = AudioFormat.ENCODING_DEFAULT - self._sample_rate = AudioFormat.SAMPLE_RATE_UNSPECIFIED - self._channel_mask = AudioFormat.CHANNEL_INVALID - self._channel_index_mask = 0 - - def getEncoding(self) -> int: - return self._encoding - - def getSampleRate(self) -> int: - return self._sample_rate - - def getChannelMask(self) -> int: - return self._channel_mask - - def getChannelIndexMask(self) -> int: - return self._channel_index_mask - - def getChannelCount(self) -> int: - mask = self._channel_mask - if mask == AudioFormat.CHANNEL_INVALID: - return 0 - return bin(mask).count("1") - - def toString(self) -> str: - return (f"AudioFormat(encoding={self._encoding}, " - f"sampleRate={self._sample_rate}, channels={self.getChannelCount()})") - - def equals(self, other: Any) -> bool: - if not isinstance(other, AudioFormat): - return False - return (self._encoding == other._encoding and - self._sample_rate == other._sample_rate and - self._channel_mask == other._channel_mask) - - def hashCode(self) -> int: - return hash((self._encoding, self._sample_rate, self._channel_mask)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioAttributes (扩展) -# ═══════════════════════════════════════════════════════════════ - -class AudioAttributes(Stub): - """AudioAttributes — 音频属性 (完整版)""" - - CONTENT_TYPE_UNKNOWN = 0 - CONTENT_TYPE_SPEECH = 1 - CONTENT_TYPE_MUSIC = 2 - CONTENT_TYPE_MOVIE = 3 - CONTENT_TYPE_SONIFICATION = 4 - - USAGE_UNKNOWN = 0 - USAGE_MEDIA = 1 - USAGE_VOICE_COMMUNICATION = 2 - USAGE_VOICE_COMMUNICATION_SIGNALLING = 3 - USAGE_ALARM = 4 - USAGE_NOTIFICATION = 5 - USAGE_NOTIFICATION_RINGTONE = 6 - USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7 - USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8 - USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9 - USAGE_NOTIFICATION_EVENT = 10 - USAGE_ASSISTANCE_ACCESSIBILITY = 11 - USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12 - USAGE_ASSISTANCE_SONIFICATION = 13 - USAGE_GAME = 14 - USAGE_VIRTUAL_SOURCE = 15 - USAGE_ASSISTANT = 16 - USAGE_CALL_ASSISTANT = 17 - USAGE_EMERGENCY = 18 - USAGE_SAFETY = 19 - USAGE_VEHICLE_STATUS = 20 - USAGE_ANNOUNCEMENT = 21 - - FLAG_AUDIBILITY_ENFORCED = 1 - FLAG_SECURE = 2 - FLAG_SCO = 4 - FLAG_BEACON = 8 - FLAG_BYPASS_INTERRUPTION_POLICY = 16 - FLAG_BYPASS_MUTE = 32 - FLAG_LOW_LATENCY = 64 - FLAG_DEEP_BUFFER = 128 - FLAG_NO_EXPANDABLE = 256 - FLAG_HW_AV_SYNC = 512 - FLAG_BYPASS_INTERRUPTION_POLICY_FLAG = 1024 - - ALLOW_CAPTURE_BY_ALL = 0 - ALLOW_CAPTURE_BY_SYSTEM = 1 - ALLOW_CAPTURE_BY_NONE = 2 - - class Builder(Stub): - """AudioAttributes.Builder — 音频属性构建器""" - - def __init__(self): - super().__init__() - self._content_type = AudioAttributes.CONTENT_TYPE_UNKNOWN - self._usage = AudioAttributes.USAGE_UNKNOWN - self._flags = 0 - self._legacy_stream_type = -1 - self._allowed_capture_policy = AudioAttributes.ALLOW_CAPTURE_BY_ALL - self._spatial_behavior = 0 - - def setContentType(self, contentType: int) -> "AudioAttributes.Builder": - self._content_type = contentType - return self - - def setUsage(self, usage: int) -> "AudioAttributes.Builder": - self._usage = usage - return self - - def setFlags(self, flags: int) -> "AudioAttributes.Builder": - self._flags = flags - return self - - def setLegacyStreamType(self, streamType: int) -> "AudioAttributes.Builder": - self._legacy_stream_type = streamType - return self - - def setAllowedCapturePolicy(self, capturePolicy: int) -> "AudioAttributes.Builder": - self._allowed_capture_policy = capturePolicy - return self - - def setSpatialBehavior(self, spatialBehavior: int) -> "AudioAttributes.Builder": - self._spatial_behavior = spatialBehavior - return self - - def build(self) -> "AudioAttributes": - attrs = AudioAttributes() - attrs._content_type = self._content_type - attrs._usage = self._usage - attrs._flags = self._flags - attrs._legacy_stream_type = self._legacy_stream_type - attrs._allowed_capture_policy = self._allowed_capture_policy - attrs._spatial_behavior = self._spatial_behavior - return attrs - - def __init__(self): - super().__init__() - self._content_type = AudioAttributes.CONTENT_TYPE_UNKNOWN - self._usage = AudioAttributes.USAGE_UNKNOWN - self._flags = 0 - self._legacy_stream_type = -1 - self._allowed_capture_policy = AudioAttributes.ALLOW_CAPTURE_BY_ALL - self._spatial_behavior = 0 - - def getContentType(self) -> int: - return self._content_type - - def getUsage(self) -> int: - return self._usage - - def getFlags(self) -> int: - return self._flags - - def getAllowedCapturePolicy(self) -> int: - return self._allowed_capture_policy - - def getSpatialBehavior(self) -> int: - return self._spatial_behavior - - def getVolumeControlStream(self) -> int: - if self._usage in (AudioAttributes.USAGE_VOICE_COMMUNICATION, - AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING): - return 0 - if self._usage == AudioAttributes.USAGE_ALARM: - return 4 - if self._usage in (AudioAttributes.USAGE_NOTIFICATION, - AudioAttributes.USAGE_NOTIFICATION_RINGTONE, - AudioAttributes.USAGE_NOTIFICATION_EVENT): - return 5 - if self._usage in (AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY, - AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, - AudioAttributes.USAGE_ASSISTANCE_SONIFICATION): - return 3 - return 3 - - toString = lambda self: f"AudioAttributes(usage={self._usage}, contentType={self._content_type})" - equals = lambda self, other: self is other if not isinstance(other, AudioAttributes) else (self._usage == other._usage and self._content_type == other._content_type) - hashCode = lambda self: hash((self._usage, self._content_type)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioTimestamp -# ═══════════════════════════════════════════════════════════════ - -class AudioTimestamp(Stub): - """AudioTimestamp — 音频时间戳""" - - TIMEBASE_MONOTONIC = 0 - TIMEBASE_BOOTTIME = 1 - - def __init__(self): - super().__init__() - self.framePosition: int = 0 - self.nanoTime: int = 0 - - def toString(self) -> str: - return f"AudioTimestamp(frame={self.framePosition}, nanoTime={self.nanoTime})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, AudioTimestamp): - return False - return self.framePosition == other.framePosition and self.nanoTime == other.nanoTime - - def hashCode(self) -> int: - return hash((self.framePosition, self.nanoTime)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioPresentation -# ═══════════════════════════════════════════════════════════════ - -class AudioPresentation(Stub): - """AudioPresentation — 音频呈现""" - - CONTENT_TYPE_UNKNOWN = 0 - CONTENT_TYPE_DIALOG = 1 - CONTENT_TYPE_MUSIC = 2 - CONTENT_TYPE_VOICEOVER = 3 - CONTENT_TYPE_NARRATION = 4 - CONTENT_TYPE_VISUALLY_IMPAIRED = 5 - CONTENT_TYPE_HEARING_IMPAIRED = 6 - CONTENT_TYPE_COMMENTARY = 7 - CONTENT_TYPE_SPOKEN_SUBTITLE = 8 - CONTENT_TYPE_EMERGENCY = 9 - CONTENT_TYPE_MAIN = 10 - CONTENT_TYPE_SUPPLEMENTARY = 11 - - MASTERED_FOR_STEREO = 0 - MASTERED_FOR_SURROUND = 1 - MASTERED_FOR_3D = 2 - MASTERED_FOR_HEADPHONE = 3 - - def __init__(self, presentationId: int = 0, programId: int = 0, - locale: str = "", contentType: int = CONTENT_TYPE_UNKNOWN, - masteredFor: int = MASTERED_FOR_STEREO, - labels: Dict[str, str] = None): - super().__init__() - self._presentation_id = presentationId - self._program_id = programId - self._locale = locale - self._content_type = contentType - self._mastered_for = masteredFor - self._labels = labels or {} - - def getPresentationId(self) -> int: - return self._presentation_id - - def getProgramId(self) -> int: - return self._program_id - - def getLocale(self) -> str: - return self._locale - - def getContentType(self) -> int: - return self._content_type - - def getMasteredFor(self) -> int: - return self._mastered_for - - def getLabels(self) -> Dict[str, str]: - return dict(self._labels) - - def toString(self) -> str: - return f"AudioPresentation(id={self._presentation_id}, locale={self._locale})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, AudioPresentation): - return False - return self._presentation_id == other._presentation_id - - def hashCode(self) -> int: - return self._presentation_id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioTrack -# ═══════════════════════════════════════════════════════════════ - -class AudioTrack(Stub): - """AudioTrack — 音频回放轨道""" - - MODE_STATIC = 0 - MODE_STREAM = 1 - - STATE_UNINITIALIZED = 0 - STATE_INITIALIZED = 1 - STATE_NO_STATIC_DATA = 2 - - WRITE_BLOCKING = 0 - SUCCESS = 0 - ERROR = -1 - ERROR_BAD_VALUE = -2 - ERROR_INVALID_OPERATION = -3 - ERROR_DEAD_OBJECT = -6 - ERROR_WOULD_BLOCK = -7 - - PERFORMANCE_MODE_NONE = 0 - PERFORMANCE_MODE_LOW_LATENCY = 1 - PERFORMANCE_MODE_POWER_SAVING = 2 - - class Builder(Stub): - """AudioTrack.Builder — 回放轨道构建器""" - - def __init__(self): - super().__init__() - self._audio_format = None - self._audio_attributes = None - self._buffer_size = 0 - self._session_id = 0 - self._transfer_mode = AudioTrack.MODE_STREAM - self._performance_mode = AudioTrack.PERFORMANCE_MODE_NONE - self._context = None - - def setAudioFormat(self, format_: AudioFormat) -> "AudioTrack.Builder": - self._audio_format = format_ - return self - - def setAudioAttributes(self, attributes: AudioAttributes) -> "AudioTrack.Builder": - self._audio_attributes = attributes - return self - - def setBufferSizeInBytes(self, bufferSize: int) -> "AudioTrack.Builder": - self._buffer_size = bufferSize - return self - - def setSessionId(self, sessionId: int) -> "AudioTrack.Builder": - self._session_id = sessionId - return self - - def setTransferMode(self, mode: int) -> "AudioTrack.Builder": - self._transfer_mode = mode - return self - - def setPerformanceMode(self, mode: int) -> "AudioTrack.Builder": - self._performance_mode = mode - return self - - def build(self) -> "AudioTrack": - return AudioTrack( - format_=self._audio_format, - attributes=self._audio_attributes, - bufferSize=self._buffer_size, - sessionId=self._session_id, - mode=self._transfer_mode, - performanceMode=self._performance_mode, - ) - - def __init__(self, format_: Any = None, attributes: Any = None, - bufferSize: int = 0, sessionId: int = 0, - mode: int = MODE_STREAM, performanceMode: int = PERFORMANCE_MODE_NONE, - sampleRate: int = 44100, channelConfig: int = AudioFormat.CHANNEL_OUT_STEREO, - audioFormat: int = AudioFormat.ENCODING_PCM_16BIT): - super().__init__() - self._state = AudioTrack.STATE_INITIALIZED - self._sample_rate = sampleRate - self._channel_config = channelConfig - self._audio_format = audioFormat - self._buffer_size = bufferSize if bufferSize > 0 else 4096 - self._mode = mode - self._session_id = sessionId - self._performance_mode = performanceMode - self._playback_rate = 1.0 - self._volume = 1.0 - self._position = 0 - self._is_playing = False - self._audio_attributes = attributes - self._format = format_ - self._timestamp = AudioTimestamp() - - def play(self): - self._is_playing = True - - def stop(self): - self._is_playing = False - - def pause(self): - self._is_playing = False - - def flush(self): - self._position = 0 - - def release(self): - self._state = AudioTrack.STATE_UNINITIALIZED - self._is_playing = False - - def write(self, audioData: Any, offsetInBytes: int = 0, sizeInBytes: int = 0) -> int: - if self._state != AudioTrack.STATE_INITIALIZED: - return AudioTrack.ERROR_INVALID_OPERATION - if isinstance(audioData, (bytes, bytearray)): - written = sizeInBytes if sizeInBytes > 0 else len(audioData) - self._position += written - return written - if hasattr(audioData, 'nbytes'): - written = sizeInBytes or audioData.nbytes - self._position += written - return written - return AudioTrack.ERROR_BAD_VALUE - - def getMinBufferSize(sampleRateInHz: int, channelConfig: int, audioFormat: int) -> int: - return 4096 - - def getNativeOutputSampleRate(streamType: int) -> int: - return 44100 - - def getSampleRate(self) -> int: - return self._sample_rate - - def getPlaybackRate(self) -> float: - return self._playback_rate - - def setPlaybackRate(self, rate: float): - self._playback_rate = rate - - def getVolume(self) -> float: - return self._volume - - def setVolume(self, volume: float): - self._volume = max(0.0, min(1.0, volume)) - - def getAudioFormat(self) -> int: - return self._audio_format - - def getChannelConfiguration(self) -> int: - return self._channel_config - - def getChannelCount(self) -> int: - return bin(self._channel_config).count("1") - - def getState(self) -> int: - return self._state - - def getPlayState(self) -> int: - return 3 if self._is_playing else 2 - - def getBufferSizeInFrames(self) -> int: - return self._buffer_size // 4 - - def getTimestamp(self, outTimestamp: AudioTimestamp) -> int: - base_time = time.time_ns() - outTimestamp.nanoTime = base_time - outTimestamp.framePosition = self._position - return AudioTrack.SUCCESS - - def getSessionId(self) -> int: - return self._session_id - - def getPerformanceMode(self) -> int: - return self._performance_mode - - def setPreferredDevice(self, deviceInfo: Any) -> bool: - return True - - def getPreferredDevice(self) -> Optional[Any]: - return None - - def toString(self) -> str: - return (f"AudioTrack(sampleRate={self._sample_rate}, " - f"playing={self._is_playing}, state={self._state})") - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioRecord -# ═══════════════════════════════════════════════════════════════ - -class AudioRecord(Stub): - """AudioRecord — 音频录制""" - - STATE_UNINITIALIZED = 0 - STATE_INITIALIZED = 1 - - SUCCESS = 0 - ERROR = -1 - ERROR_BAD_VALUE = -2 - ERROR_INVALID_OPERATION = -3 - ERROR_DEAD_OBJECT = -6 - - READ_BLOCKING = 0 - READ_NON_BLOCKING = 1 - - class Builder(Stub): - """AudioRecord.Builder — 录制构建器""" - - def __init__(self): - super().__init__() - self._audio_format = None - self._audio_attributes = None - self._buffer_size = 0 - self._session_id = 0 - self._context = None - - def setAudioFormat(self, format_: AudioFormat) -> "AudioRecord.Builder": - self._audio_format = format_ - return self - - def setAudioAttributes(self, attributes: AudioAttributes) -> "AudioRecord.Builder": - self._audio_attributes = attributes - return self - - def setBufferSizeInBytes(self, bufferSize: int) -> "AudioRecord.Builder": - self._buffer_size = bufferSize - return self - - def setSessionId(self, sessionId: int) -> "AudioRecord.Builder": - self._session_id = sessionId - return self - - def build(self) -> "AudioRecord": - return AudioRecord( - format_=self._audio_format, - attributes=self._audio_attributes, - bufferSize=self._buffer_size, - sessionId=self._session_id, - ) - - def __init__(self, format_: Any = None, attributes: Any = None, - bufferSize: int = 0, sessionId: int = 0, - sampleRate: int = 44100, channelConfig: int = AudioFormat.CHANNEL_IN_MONO, - audioFormat: int = AudioFormat.ENCODING_PCM_16BIT): - super().__init__() - self._state = AudioRecord.STATE_INITIALIZED - self._sample_rate = sampleRate - self._channel_config = channelConfig - self._audio_format = audioFormat - self._buffer_size = bufferSize if bufferSize > 0 else 4096 - self._session_id = sessionId - self._is_recording = False - self._position = 0 - self._format = format_ - self._audio_attributes = attributes - - def startRecording(self): - self._is_recording = True - - def stop(self): - self._is_recording = False - - def release(self): - self._state = AudioRecord.STATE_UNINITIALIZED - self._is_recording = False - - def read(self, audioData: Any, offsetInBytes: int = 0, sizeInBytes: int = 0) -> int: - if not self._is_recording: - return AudioRecord.ERROR_INVALID_OPERATION - return sizeInBytes if sizeInBytes > 0 else 0 - - def getMinBufferSize(sampleRateInHz: int, channelConfig: int, audioFormat: int) -> int: - return 4096 - - def getSampleRate(self) -> int: - return self._sample_rate - - def getAudioFormat(self) -> int: - return self._audio_format - - def getChannelConfiguration(self) -> int: - return self._channel_config - - def getChannelCount(self) -> int: - return bin(self._channel_config).count("1") - - def getState(self) -> int: - return self._state - - def getRecordingState(self) -> int: - return 3 if self._is_recording else 2 - - def getBufferSizeInFrames(self) -> int: - return self._buffer_size // 4 - - def getSessionId(self) -> int: - return self._session_id - - def setPreferredDevice(self, deviceInfo: Any) -> bool: - return True - - def getPreferredDevice(self) -> Optional[Any]: - return None - - def toString(self) -> str: - return f"AudioRecord(sampleRate={self._sample_rate}, recording={self._is_recording})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioDeviceInfo -# ═══════════════════════════════════════════════════════════════ - -class AudioDeviceInfo(Stub): - """AudioDeviceInfo — 音频设备信息""" - - TYPE_UNKNOWN = 0 - TYPE_BUILTIN_EARPIECE = 1 - TYPE_BUILTIN_SPEAKER = 2 - TYPE_WIRED_HEADSET = 3 - TYPE_WIRED_HEADPHONES = 4 - TYPE_LINE_ANALOG = 5 - TYPE_LINE_DIGITAL = 6 - TYPE_BLUETOOTH_SCO = 7 - TYPE_BLUETOOTH_A2DP = 8 - TYPE_HDMI = 9 - TYPE_HDMI_ARC = 10 - TYPE_USB_DEVICE = 11 - TYPE_USB_ACCESSORY = 12 - TYPE_DOCK = 13 - TYPE_FM = 14 - TYPE_BUILTIN_MIC = 15 - TYPE_FM_TUNER = 16 - TYPE_TV_TUNER = 17 - TYPE_TELEPHONY = 18 - TYPE_AUX_LINE = 19 - TYPE_IP = 20 - TYPE_BUS = 21 - TYPE_USB_HEADSET = 22 - TYPE_HEARING_AID = 23 - TYPE_BUILTIN_SPEAKER_SAFE = 24 - TYPE_REMOTE_SUBMIX = 25 - TYPE_BLE_HEADSET = 26 - TYPE_BLE_SPEAKER = 27 - TYPE_BLE_BROADCAST = 28 - - def __init__(self, id: int = 0, type_: int = TYPE_UNKNOWN, - productName: str = "", address: str = "", - isSource: bool = False, isSink: bool = False, - sampleRates: List[int] = None, - channelMasks: List[int] = None, - encodings: List[int] = None): - super().__init__() - self._id = id - self._type = type_ - self._product_name = productName - self._address = address - self._is_source = isSource - self._is_sink = isSink - self._sample_rates = sampleRates or [44100, 48000] - self._channel_masks = channelMasks or [AudioFormat.CHANNEL_OUT_STEREO] - self._encodings = encodings or [AudioFormat.ENCODING_PCM_16BIT] - - def getId(self) -> int: - return self._id - - def getType(self) -> int: - return self._type - - def getProductName(self) -> Any: - return self._product_name - - def getAddress(self) -> str: - return self._address - - def isSource(self) -> bool: - return self._is_source - - def isSink(self) -> bool: - return self._is_sink - - def getSampleRates(self) -> List[int]: - return list(self._sample_rates) - - def getChannelMasks(self) -> List[int]: - return list(self._channel_masks) - - def getEncodings(self) -> List[int]: - return list(self._encodings) - - def getChannelCounts(self) -> List[int]: - return [bin(m).count("1") for m in self._channel_masks] - - def toString(self) -> str: - type_names = { - 1: "BUILTIN_EARPIECE", 2: "BUILTIN_SPEAKER", 3: "WIRED_HEADSET", - 4: "WIRED_HEADPHONES", 8: "BTSCO", 9: "HDMI", 11: "USB_DEVICE", - } - return (f"AudioDeviceInfo(id={self._id}, " - f"type={type_names.get(self._type, str(self._type))})") - - def equals(self, other: Any) -> bool: - if not isinstance(other, AudioDeviceInfo): - return False - return self._id == other._id - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioDeviceCallback -# ═══════════════════════════════════════════════════════════════ - -class AudioDeviceCallback(Stub): - """AudioDeviceCallback — 音频设备回调""" - - def onAudioDevicesAdded(self, addedDevices: List[AudioDeviceInfo]): - pass - - def onAudioDevicesRemoved(self, removedDevices: List[AudioDeviceInfo]): - pass - - def toString(self) -> str: - return f"AudioDeviceCallback@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioRouting -# ═══════════════════════════════════════════════════════════════ - -class AudioRouting(Stub): - """AudioRouting — 音频路由接口""" - - def setPreferredDevice(self, deviceInfo: AudioDeviceInfo) -> bool: - raise NotImplementedError - - def getPreferredDevice(self) -> Optional[AudioDeviceInfo]: - return None - - def getDevices(self, flags: int) -> List[AudioDeviceInfo]: - return [] - - def addOnRoutingPrefChangedListener(self, listener: Any, handler: Any = None): - pass - - def removeOnRoutingPrefChangedListener(self, listener: Any): - pass - - def toString(self) -> str: - return f"AudioRouting@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioPlaybackConfiguration -# ═══════════════════════════════════════════════════════════════ - -class AudioPlaybackConfiguration(Stub): - """AudioPlaybackConfiguration — 音频回放配置""" - - PLAYER_STATE_UNKNOWN = -1 - PLAYER_STATE_IDLE = 0 - PLAYER_STATE_STARTED = 1 - PLAYER_STATE_PAUSED = 2 - PLAYER_STATE_STOPPED = 3 - - def __init__(self, id_: int = 0, usage: int = AudioAttributes.USAGE_MEDIA, - contentType: int = AudioAttributes.CONTENT_TYPE_MUSIC, - playerState: int = PLAYER_STATE_UNKNOWN): - super().__init__() - self._id = id_ - self._usage = usage - self._content_type = contentType - self._player_state = playerState - - def getId(self) -> int: - return self._id - - def getUsage(self) -> int: - return self._usage - - def getContentType(self) -> int: - return self._content_type - - def getPlayerState(self) -> int: - return self._player_state - - def toString(self) -> str: - return f"AudioPlaybackConfiguration(id={self._id}, state={self._player_state})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, AudioPlaybackConfiguration): - return False - return self._id == other._id - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioRecordingConfiguration -# ═══════════════════════════════════════════════════════════════ - -class AudioRecordingConfiguration(Stub): - """AudioRecordingConfiguration — 音频录制配置""" - - def __init__(self, id_: int = 0, clientUid: int = 0, - sessionId: int = 0, source: int = 0, - audioFormat: Optional[AudioFormat] = None): - super().__init__() - self._id = id_ - self._client_uid = clientUid - self._session_id = sessionId - self._source = source - self._audio_format = audioFormat or AudioFormat() - - def getId(self) -> int: - return self._id - - def getClientUid(self) -> int: - return self._client_uid - - def getSessionId(self) -> int: - return self._session_id - - def getSource(self) -> int: - return self._source - - def getAudioFormat(self) -> AudioFormat: - return self._audio_format - - def toString(self) -> str: - return f"AudioRecordingConfiguration(id={self._id}, source={self._source})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, AudioRecordingConfiguration): - return False - return self._id == other._id - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioManagerAudio (扩展音频管理器) -# ═══════════════════════════════════════════════════════════════ - -class AudioManagerAudio(Stub): - """AudioManager — 音频管理器 (扩展版)""" - - STREAM_VOICE_CALL = 0 - STREAM_SYSTEM = 1 - STREAM_RING = 2 - STREAM_MUSIC = 3 - STREAM_ALARM = 4 - STREAM_NOTIFICATION = 5 - STREAM_BLUETOOTH_SCO = 6 - STREAM_SYSTEM_ENFORCED = 7 - STREAM_DTMF = 8 - STREAM_TTS = 9 - STREAM_ACCESSIBILITY = 10 - - FLAG_SHOW_UI = 1 - FLAG_ALLOW_RINGER_MODES = 2 - FLAG_PLAY_SOUND = 4 - FLAG_REMOVE_SOUND_AND_VIBRATE = 8 - FLAG_VIBRATE = 16 - FLAG_BYPASS_INTERRUPTION_POLICY = 32 - FLAG_BYPASS_MUTE = 64 - FLAG_HDMI_SYSTEM_AUDIO_VOLUME = 128 - FLAG_FIXED_VOLUME = 256 - - MODE_INVALID = -2 - MODE_CURRENT = -1 - MODE_NORMAL = 0 - MODE_RINGTONE = 1 - MODE_IN_CALL = 2 - MODE_IN_COMMUNICATION = 3 - - DEVICE_OUT_EARPIECE = 0x1 - DEVICE_OUT_SPEAKER = 0x2 - DEVICE_OUT_WIRED_HEADSET = 0x4 - DEVICE_OUT_WIRED_HEADPHONE = 0x8 - DEVICE_OUT_BLUETOOTH_SCO = 0x10 - DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20 - DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40 - DEVICE_OUT_BLUETOOTH_A2DP = 0x80 - DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100 - DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200 - DEVICE_OUT_AUX_DIGITAL = 0x400 - DEVICE_OUT_HDMI = 0x800 - DEVICE_OUT_ANLG_DOCK_HEADSET = 0x1000 - DEVICE_OUT_DGTL_DOCK_HEADSET = 0x2000 - DEVICE_OUT_USB_ACCESSORY = 0x4000 - DEVICE_OUT_USB_DEVICE = 0x8000 - DEVICE_OUT_USB_HEADSET = 0x10000 - DEVICE_OUT_LINE = 0x20000 - DEVICE_OUT_HDMI_ARC = 0x40000 - DEVICE_OUT_HDMI_EARC = 0x80000 - DEVICE_OUT_BLE_HEADSET = 0x100000 - DEVICE_OUT_BLE_SPEAKER = 0x200000 - DEVICE_OUT_REMOTE_SUBMIX = 0x800000 - - DEVICE_IN_BUILTIN_MIC = 0x80000004 - DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x80000010 - DEVICE_IN_WIRED_HEADSET = 0x80000020 - DEVICE_IN_HDMI = 0x80000040 - DEVICE_IN_LINE = 0x80000080 - DEVICE_IN_USB_ACCESSORY = 0x80000100 - DEVICE_IN_USB_DEVICE = 0x80000200 - DEVICE_IN_BLUETOOTH_A2DP = 0x80000400 - DEVICE_IN_USB_HEADSET = 0x80000800 - DEVICE_IN_BLE_HEADSET = 0x80001000 - DEVICE_IN_REMOTE_SUBMIX = 0x80002000 - - FORMAT_DEFAULT = 0 - FORMAT_PCM_16_BIT = 1 - FORMAT_PCM_8_BIT = 2 - FORMAT_PCM_FLOAT = 4 - FORMAT_AC3 = 5 - FORMAT_E_AC3 = 6 - FORMAT_DTS = 7 - FORMAT_DTS_HD = 8 - - def __init__(self): - super().__init__() - self._volumes: Dict[int, int] = { - AudioManagerAudio.STREAM_MUSIC: 10, - AudioManagerAudio.STREAM_RING: 7, - AudioManagerAudio.STREAM_ALARM: 10, - AudioManagerAudio.STREAM_NOTIFICATION: 7, - AudioManagerAudio.STREAM_SYSTEM: 7, - AudioManagerAudio.STREAM_VOICE_CALL: 5, - } - self._muted: Dict[int, bool] = {} - self._mode = AudioManagerAudio.MODE_NORMAL - self._microphone_muted = False - self._speakerphone_on = False - self._master_volume = 1.0 - self._master_mute = False - self._device_callbacks: List[AudioDeviceCallback] = [] - self._devices: List[AudioDeviceInfo] = [] - self._routed_devices: Dict[int, int] = {} - - def getStreamVolume(self, streamType: int) -> int: - return self._volumes.get(streamType, 0) - - def setStreamVolume(self, streamType: int, index: int, flags: int = 0): - self._volumes[streamType] = max(0, min(index, self.getStreamMaxVolume(streamType))) - - def getStreamMaxVolume(self, streamType: int) -> int: - return 15 - - def getStreamMinVolume(self, streamType: int) -> int: - return 0 - - def isStreamMute(self, streamType: int) -> bool: - return self._muted.get(streamType, False) - - def setStreamMute(self, streamType: int, state: bool): - self._muted[streamType] = state - - def adjustVolume(self, direction: int, flags: int = 0): - pass - - def adjustStreamVolume(self, streamType: int, direction: int, flags: int = 0): - current = self.getStreamVolume(streamType) - if direction == 1: - self.setStreamVolume(streamType, current + 1, flags) - elif direction == -1: - self.setStreamVolume(streamType, current - 1, flags) - - def getMode(self) -> int: - return self._mode - - def setMode(self, mode: int): - self._mode = mode - - def isMicrophoneMute(self) -> bool: - return self._microphone_muted - - def setMicrophoneMute(self, on: bool): - self._microphone_muted = on - - def isSpeakerphoneOn(self) -> bool: - return self._speakerphone_on - - def setSpeakerphoneOn(self, on: bool): - self._speakerphone_on = on - - def getMasterVolume(self) -> float: - return self._master_volume - - def setMasterVolume(self, volume: float): - self._master_volume = max(0.0, min(1.0, volume)) - - def isMasterMute(self) -> bool: - return self._master_mute - - def setMasterMute(self, mute: bool): - self._master_mute = mute - - def getDevices(self, flags: int) -> List[AudioDeviceInfo]: - return list(self._devices) - - def setPreferredDeviceForStrategy(self, strategy: int, device: AudioDeviceInfo) -> bool: - self._routed_devices[strategy] = device.getId() if device else -1 - return True - - def registerAudioDeviceCallback(self, callback: AudioDeviceCallback, handler: Any = None): - self._device_callbacks.append(callback) - - def unregisterAudioDeviceCallback(self, callback: AudioDeviceCallback): - self._device_callbacks = [cb for cb in self._device_callbacks if cb is not callback] - - def getAudioPlaybackConfigurations(self) -> List[AudioPlaybackConfiguration]: - return [] - - def getAudioRecordingConfigurations(self) -> List[AudioRecordingConfiguration]: - return [] - - def generateAudioSessionId() -> int: - return int(time.time() * 1000) & 0x7FFFFFFF - - def toString(self) -> str: - return f"AudioManager(mode={self._mode}, masterVol={self._master_volume})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioMixPort / AudioPort / AudioPatch / AudioGain / AudioGainConfig -# ═══════════════════════════════════════════════════════════════ - -class AudioPort(Stub): - """AudioPort — 音频端口""" - - ROLE_NONE = 0 - ROLE_SOURCE = 1 - ROLE_SINK = 2 - - TYPE_NONE = 0 - TYPE_DEVICE = 1 - TYPE_SUBMIX = 2 - TYPE_SESSION = 3 - - def __init__(self, id_: int = 0, name: str = "", - role: int = ROLE_NONE, type_: int = TYPE_NONE, - gains: List["AudioGain"] = None): - super().__init__() - self._id = id_ - self._name = name - self._role = role - self._type = type_ - self._gains = gains or [] - - def getId(self) -> int: - return self._id - - def getName(self) -> str: - return self._name - - def getRole(self) -> int: - return self._role - - def type(self) -> int: - return self._type - - def gains(self) -> List["AudioGain"]: - return list(self._gains) - - def toString(self) -> str: - return f"AudioPort(id={self._id}, name={self._name})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, AudioPort): - return False - return self._id == other._id - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF - - -class AudioMixPort(AudioPort): - """AudioMixPort — 音频混合端口""" - - def __init__(self, id_: int = 0, name: str = "", - role: int = AudioPort.ROLE_NONE, - gains: List["AudioGain"] = None): - super().__init__(id_=id_, name=name, role=role, - type_=AudioPort.TYPE_SUBMIX, gains=gains) - - def toString(self) -> str: - return f"AudioMixPort(id={self._id}, name={self._name})" - - -class AudioGain(Stub): - """AudioGain — 音频增益""" - - MODE_JOINT = 1 - MODE_CHANNELS = 2 - MODE_RAMP = 4 - - def __init__(self, id_: int = 0, mode: int = MODE_JOINT, - channelMask: int = 0, minValue: int = 0, - maxValue: int = 100, defaultValue: int = 50, - stepValue: int = 1, minRampMs: int = 0, maxRampMs: int = 0): - super().__init__() - self._id = id_ - self._mode = mode - self._channel_mask = channelMask - self._min_value = minValue - self._max_value = maxValue - self._default_value = defaultValue - self._step_value = stepValue - self._min_ramp_ms = minRampMs - self._max_ramp_ms = maxRampMs - - def mode(self) -> int: - return self._mode - - def channelMask(self) -> int: - return self._channel_mask - - def minValue(self) -> int: - return self._min_value - - def maxValue(self) -> int: - return self._max_value - - def defaultValue(self) -> int: - return self._default_value - - def stepValue(self) -> int: - return self._step_value - - def toString(self) -> str: - return f"AudioGain(id={self._id}, range=[{self._min_value}, {self._max_value}])" - - def equals(self, other: Any) -> bool: - if not isinstance(other, AudioGain): - return False - return self._id == other._id - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF - - -class AudioGainConfig(Stub): - """AudioGainConfig — 音频增益配置""" - - def __init__(self, gain: Optional[AudioGain] = None, - mode: int = AudioGain.MODE_JOINT, - channelMask: int = 0, - values: List[int] = None, - rampDurationMs: int = 0): - super().__init__() - self._gain = gain - self._mode = mode - self._channel_mask = channelMask - self._values = values or [] - self._ramp_duration_ms = rampDurationMs - - def getGain(self) -> Optional[AudioGain]: - return self._gain - - def getMode(self) -> int: - return self._mode - - def getChannelMask(self) -> int: - return self._channel_mask - - def getValues(self) -> List[int]: - return list(self._values) - - def getRampDurationMs(self) -> int: - return self._ramp_duration_ms - - def toString(self) -> str: - return f"AudioGainConfig(mode={self._mode}, values={self._values})" - - -class AudioPatch(Stub): - """AudioPatch — 音频补丁""" - - def __init__(self, id_: int = 0, sources: List[AudioPort] = None, - sinks: List[AudioPort] = None): - super().__init__() - self._id = id_ - self._sources = sources or [] - self._sinks = sinks or [] - - def getId(self) -> int: - return self._id - - def getSources(self) -> List[AudioPort]: - return list(self._sources) - - def getSinks(self) -> List[AudioPort]: - return list(self._sinks) - - def toString(self) -> str: - return f"AudioPatch(id={self._id}, sources={len(self._sources)}, sinks={len(self._sinks)})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, AudioPatch): - return False - return self._id == other._id - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioMix -# ═══════════════════════════════════════════════════════════════ - -class AudioMix(Stub): - """AudioMix — 音频混合""" - - ROUTE_FLAG_RENDER = 1 - ROUTE_FLAG_LOOP_BACK = 2 - ROUTE_FLAG_CAPTURE = 4 - - CALLBACK_FLAG_NOTIFY_ACTIVITY = 1 - - def __init__(self, routeFlags: int = ROUTE_FLAG_RENDER, - callbackFlags: int = 0, - format_: Optional[AudioFormat] = None, - deviceType: int = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER): - super().__init__() - self._route_flags = routeFlags - self._callback_flags = callbackFlags - self._format = format_ or AudioFormat() - self._device_type = deviceType - - def getRouteFlags(self) -> int: - return self._route_flags - - def getCallbackFlags(self) -> int: - return self._callback_flags - - def getFormat(self) -> AudioFormat: - return self._format - - def getDeviceType(self) -> int: - return self._device_type - - def toString(self) -> str: - return f"AudioMix(routeFlags={self._route_flags})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, AudioMix): - return False - return self._route_flags == other._route_flags - - def hashCode(self) -> int: - return self._route_flags & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioProfile -# ═══════════════════════════════════════════════════════════════ - -class AudioProfile(Stub): - """AudioProfile — 音频配置文件""" - - def __init__(self, format_: int = AudioFormat.ENCODING_DEFAULT, - sampleRates: List[int] = None, - channelMasks: List[int] = None, - channelIndexMasks: List[int] = None): - super().__init__() - self._format = format_ - self._sample_rates = sampleRates or [44100, 48000] - self._channel_masks = channelMasks or [AudioFormat.CHANNEL_OUT_STEREO] - self._channel_index_masks = channelIndexMasks or [] - - def getFormat(self) -> int: - return self._format - - def getSampleRates(self) -> List[int]: - return list(self._sample_rates) - - def getChannelMasks(self) -> List[int]: - return list(self._channel_masks) - - def getChannelIndexMasks(self) -> List[int]: - return list(self._channel_index_masks) - - def toString(self) -> str: - return f"AudioProfile(format={self._format})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, AudioProfile): - return False - return self._format == other._format - - def hashCode(self) -> int: - return self._format & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioProductStrategy -# ═══════════════════════════════════════════════════════════════ - -class AudioProductStrategy(Stub): - """AudioProductStrategy — 音频产品策略""" - - def __init__(self, id_: int = 0, name: str = "", - toleratedFormats: List[int] = None, - volumeGroup: Optional["AudioVolumeGroup"] = None): - super().__init__() - self._id = id_ - self._name = name - self._tolerated_formats = toleratedFormats or [] - self._volume_group = volume_group - - def getId(self) -> int: - return self._id - - def getName(self) -> str: - return self._name - - def getToleratedFormats(self) -> List[int]: - return list(self._tolerated_formats) - - def getVolumeGroup(self) -> Optional["AudioVolumeGroup"]: - return self._volume_group - - def setVolumeGroup(self, group: "AudioVolumeGroup"): - self._volume_group = group - - def toString(self) -> str: - return f"AudioProductStrategy(id={self._id}, name={self._name})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, AudioProductStrategy): - return False - return self._id == other._id - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AudioVolumeGroup -# ═══════════════════════════════════════════════════════════════ - -class AudioVolumeGroup(Stub): - """AudioVolumeGroup — 音频音量组""" - - def __init__(self, id_: int = 0, name: str = "", - volumeIndexMin: int = 0, volumeIndexMax: int = 15): - super().__init__() - self._id = id_ - self._name = name - self._volume_index_min = volumeIndexMin - self._volume_index_max = volumeIndexMax - - def getId(self) -> int: - return self._id - - def getName(self) -> str: - return self._name - - def getMinVolumeIndex(self) -> int: - return self._volume_index_min - - def getMaxVolumeIndex(self) -> int: - return self._volume_index_max - - def toString(self) -> str: - return f"AudioVolumeGroup(id={self._id}, name={self._name})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, AudioVolumeGroup): - return False - return self._id == other._id - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/media_audiofx.py b/android_pyapi/android/media_audiofx.py deleted file mode 100644 index 45901e4..0000000 --- a/android_pyapi/android/media_audiofx.py +++ /dev/null @@ -1,547 +0,0 @@ -""" -Android media.audiofx 组件 — AudioEffect, Equalizer, BassBoost, Virtualizer 等 -方法签名完全对齐 Android SDK -""" - -import math -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# AudioEffect -# ═══════════════════════════════════════════════════════════════ - -class AudioEffect(Stub): - """AudioEffect — 音频效果基类""" - - EFFECT_SUCCESS = 0 - EFFECT_ERROR = -1 - EFFECT_ALREADY_EXISTS = -2 - EFFECT_NO_MORE_SESSION = -3 - EFFECT_REQUEST_INIT = -4 - EFFECT_INIT_FAILED = -5 - - EFFECT_INSERT = 1 - EFFECT_AUXILIARY = 2 - EFFECT_PRE_PROCESSING = 4 - EFFECT_POST_PROCESSING = 8 - - CONTENT_TYPE_MUSIC = 2 - CONTENT_TYPE_MOVIE = 3 - CONTENT_TYPE_GAME = 4 - CONTENT_TYPE_VOICE = 1 - - # ═══════════════════════════════════════════════════════════ - # Descriptor - # ═══════════════════════════════════════════════════════════ - - class Descriptor(Stub): - """AudioEffect.Descriptor — 效果描述符""" - - def __init__(self, type: str = "", uuid: str = "", - connectMode: str = "", name: str = "", - implementor: str = ""): - super().__init__() - self.type = type - self.uuid = uuid - self.connectMode = connectMode - self.name = name - self.implementor = implementor - - def toString(self) -> str: - return f"Descriptor(type={self.type}, uuid={self.uuid}, name={self.name})" - - def __init__(self, effectType: str = "", priority: int = 0, - audioSession: int = 0): - super().__init__() - self._enabled = False - self._effect_type = effectType - self._priority = priority - self._audio_session = audioSession - self._id = hash(self) & 0x7FFFFFFF - self._control_status_listener: Optional[Callable] = None - self._enable_status_listener: Optional[Callable] = None - - def getEnabled(self) -> bool: - return self._enabled - - def setEnabled(self, enabled: bool) -> int: - self._enabled = enabled - return AudioEffect.EFFECT_SUCCESS - - def getId(self) -> int: - return self._id - - def getDescriptor(self) -> Descriptor: - return AudioEffect.Descriptor( - type=self._effect_type, - uuid="", - connectMode="", - name=self.__class__.__name__, - implementor="Android-pyApi" - ) - - def release(self): - self._enabled = False - - def setControlStatusListener(self, listener: Callable): - self._control_status_listener = listener - - def setEnableStatusListener(self, listener: Callable): - self._enable_status_listener = listener - - -# ═══════════════════════════════════════════════════════════════ -# Equalizer -# ═══════════════════════════════════════════════════════════════ - -class Equalizer(AudioEffect): - """Equalizer — 均衡器""" - - PARAM_CURRENT_PRESET = 0 - PARAM_GET_BAND = 1 - PARAM_STRING_SIZE_MAX = 2 - PARAM_BAND_LEVEL = 3 - PARAM_LEVEL_RANGE = 4 - PARAM_NUM_BANDS = 5 - PARAM_BAND_FREQ_RANGE = 6 - PARAM_CENTER_FREQ = 7 - PARAM_GET_PRESET_NAME = 8 - PARAM_NUM_PRESETS = 9 - PARAM_PROPERTIES = 10 - - def __init__(self, priority: int = 0, audioSession: int = 0): - super().__init__("Equalizer", priority, audioSession) - self._num_bands = 5 - self._band_levels: Dict[int, int] = {} - self._presets = [ - "Normal", "Classical", "Dance", "Flat", "Folk", - "Heavy Metal", "Hip Hop", "Jazz", "Pop", "Rock" - ] - self._current_preset = 0 - self._band_freq_ranges: Dict[int, Tuple[int, int]] = { - 0: (60, 200), 1: (200, 600), 2: (600, 2400), - 3: (2400, 8000), 4: (8000, 16000) - } - - def getNumberOfBands(self) -> int: - return self._num_bands - - def getBand(self, frequency: int) -> int: - for band, (low, high) in self._band_freq_ranges.items(): - if low <= frequency <= high: - return band - return 0 - - def getBandLevel(self, band: int) -> int: - return self._band_levels.get(band, 0) - - def setBandLevel(self, band: int, level: int): - self._band_levels[band] = level - - def getBandLevelRange(self) -> List[int]: - return [-1500, 1500] - - def getCenterFreq(self, band: int) -> int: - if band in self._band_freq_ranges: - low, high = self._band_freq_ranges[band] - return (low + high) // 2 - return 0 - - def getNumberOfPresets(self) -> int: - return len(self._presets) - - def getPresetName(self, preset: int) -> str: - if 0 <= preset < len(self._presets): - return self._presets[preset] - return "Unknown" - - def usePreset(self, preset: int): - if 0 <= preset < len(self._presets): - self._current_preset = preset - - def getCurrentPreset(self) -> int: - return self._current_preset - - def getProperties(self) -> Any: - return None - - def setProperties(self, properties: Any): - pass - - def toString(self) -> str: - return f"Equalizer(preset={self._presets[self._current_preset]}," \ - f" bands={self._num_bands})" - - -# ═══════════════════════════════════════════════════════════════ -# BassBoost -# ═══════════════════════════════════════════════════════════════ - -class BassBoost(AudioEffect): - """BassBoost — 低音增强""" - - PARAM_STRENGTH = 1 - PARAM_STRENGTH_SUPPORTED = 2 - - def __init__(self, priority: int = 0, audioSession: int = 0): - super().__init__("BassBoost", priority, audioSession) - self._strength: int = 0 - - def setStrength(self, strength: int): - self._strength = max(0, min(1000, strength)) - - def getRoundedStrength(self) -> int: - return self._strength - - def getStrengthSupported(self) -> bool: - return True - - def toString(self) -> str: - return f"BassBoost(strength={self._strength})" - - -# ═══════════════════════════════════════════════════════════════ -# Virtualizer -# ═══════════════════════════════════════════════════════════════ - -class Virtualizer(AudioEffect): - """Virtualizer — 虚拟器 (空间音频)""" - - PARAM_STRENGTH = 1 - PARAM_STRENGTH_SUPPORTED = 2 - PARAM_VIRTUALIZATION_MODE = 3 - PARAM_FORCE_VIRTUALIZATION_MODE = 4 - - VIRTUALIZATION_MODE_AUTO = 0 - VIRTUALIZATION_MODE_OFF = 1 - VIRTUALIZATION_MODE_TRANSAURAL = 2 - VIRTUALIZATION_MODE_BINAURAL = 3 - - def __init__(self, priority: int = 0, audioSession: int = 0): - super().__init__("Virtualizer", priority, audioSession) - self._strength: int = 0 - self._virtualization_mode = Virtualizer.VIRTUALIZATION_MODE_AUTO - - def setStrength(self, strength: int): - self._strength = max(0, min(1000, strength)) - - def getRoundedStrength(self) -> int: - return self._strength - - def canVirtualize(self, channelMask: int, virtualizationMode: int) -> bool: - return True - - def getVirtualizationMode(self) -> int: - return self._virtualization_mode - - def toString(self) -> str: - return f"Virtualizer(strength={self._strength}, mode={self._virtualization_mode})" - - -# ═══════════════════════════════════════════════════════════════ -# PresetReverb -# ═══════════════════════════════════════════════════════════════ - -class PresetReverb(AudioEffect): - """PresetReverb — 预设混响""" - - PRESET_NONE = 0 - PRESET_SMALLROOM = 1 - PRESET_MEDIUMROOM = 2 - PRESET_LARGEROOM = 3 - PRESET_MEDIUMHALL = 4 - PRESET_LARGEHALL = 5 - PRESET_PLATE = 6 - - PARAM_PRESET = 0 - - def __init__(self, priority: int = 0, audioSession: int = 0): - super().__init__("PresetReverb", priority, audioSession) - self._preset = PresetReverb.PRESET_NONE - self._preset_names = { - PresetReverb.PRESET_NONE: "None", - PresetReverb.PRESET_SMALLROOM: "Small Room", - PresetReverb.PRESET_MEDIUMROOM: "Medium Room", - PresetReverb.PRESET_LARGEROOM: "Large Room", - PresetReverb.PRESET_MEDIUMHALL: "Medium Hall", - PresetReverb.PRESET_LARGEHALL: "Large Hall", - PresetReverb.PRESET_PLATE: "Plate", - } - - def setPreset(self, preset: int): - if preset in self._preset_names: - self._preset = preset - - def getPreset(self) -> int: - return self._preset - - def toString(self) -> str: - return f"PresetReverb(preset={self._preset_names.get(self._preset, 'Unknown')})" - - -# ═══════════════════════════════════════════════════════════════ -# EnvironmentalReverb -# ═══════════════════════════════════════════════════════════════ - -class EnvironmentalReverb(AudioEffect): - """EnvironmentalReverb — 环境混响""" - - def __init__(self, priority: int = 0, audioSession: int = 0): - super().__init__("EnvironmentalReverb", priority, audioSession) - self._decay_time = 1500 - self._decay_hf_ratio = 500 - self._density = 500 - self._diffusion = 500 - self._reverb_level = 0 - self._room_level = 0 - self._room_hf_level = 0 - self._reflections_delay = 20 - self._reflections_level = -500 - self._reverb_delay = 40 - - def setDecayTime(self, decayTime: int): - self._decay_time = decayTime - - def getDecayTime(self) -> int: - return self._decay_time - - def setDecayHFRatio(self, decayHFRatio: int): - self._decay_hf_ratio = decayHFRatio - - def getDecayHFRatio(self) -> int: - return self._decay_hf_ratio - - def setDensity(self, density: int): - self._density = density - - def getDensity(self) -> int: - return self._density - - def setDiffusion(self, diffusion: int): - self._diffusion = diffusion - - def getDiffusion(self) -> int: - return self._diffusion - - def setReverbLevel(self, reverbLevel: int): - self._reverb_level = reverbLevel - - def getReverbLevel(self) -> int: - return self._reverb_level - - def setRoomLevel(self, roomLevel: int): - self._room_level = roomLevel - - def getRoomLevel(self) -> int: - return self._room_level - - def setRoomHFLevel(self, roomHFLevel: int): - self._room_hf_level = roomHFLevel - - def getRoomHFLevel(self) -> int: - return self._room_hf_level - - def setReflectionsDelay(self, reflectionsDelay: int): - self._reflections_delay = reflectionsDelay - - def getReflectionsDelay(self) -> int: - return self._reflections_delay - - def setReflectionsLevel(self, reflectionsLevel: int): - self._reflections_level = reflectionsLevel - - def getReflectionsLevel(self) -> int: - return self._reflections_level - - def setReverbDelay(self, reverbDelay: int): - self._reverb_delay = reverbDelay - - def getReverbDelay(self) -> int: - return self._reverb_delay - - def toString(self) -> str: - return f"EnvironmentalReverb(decayTime={self._decay_time}," \ - f" density={self._density})" - - -# ═══════════════════════════════════════════════════════════════ -# LoudnessEnhancer -# ═══════════════════════════════════════════════════════════════ - -class LoudnessEnhancer(AudioEffect): - """LoudnessEnhancer — 响度增强器""" - - PARAM_TARGET_GAIN_MB = 0 - - def __init__(self, audioSession: int = 0): - super().__init__("LoudnessEnhancer", 0, audioSession) - self._target_gain = 0 - - def setTargetGain(self, gainMillibels: int): - self._target_gain = gainMillibels - - def getTargetGain(self) -> int: - return self._target_gain - - def toString(self) -> str: - return f"LoudnessEnhancer(targetGain={self._target_gain} mB)" - - -# ═══════════════════════════════════════════════════════════════ -# DynamicsProcessing -# ═══════════════════════════════════════════════════════════════ - -class DynamicsProcessing(AudioEffect): - """DynamicsProcessing — 动态处理""" - - class Stage(Stub): - """DynamicsProcessing.Stage — 处理阶段""" - - def __init__(self, enabled: bool = True): - super().__init__() - self._enabled = enabled - self._in_use = True - - def isEnabled(self) -> bool: - return self._enabled - - def setEnabled(self, enabled: bool): - self._enabled = enabled - - class EQBand(Stage): - """DynamicsProcessing.EQBand — EQ 频段""" - - def __init__(self, enabled: bool = True): - super().__init__(enabled) - self._gain = 0.0 - self._center_freq = 1000.0 - - def setGain(self, gain: float): - self._gain = gain - - def getGain(self) -> float: - return self._gain - - def setCenterFreq(self, freq: float): - self._center_freq = freq - - def getCenterFreq(self) -> float: - return self._center_freq - - class EQ(Stage): - """DynamicsProcessing.EQ — 均衡器""" - - def __init__(self, enabled: bool = True, bandCount: int = 6): - super().__init__(enabled) - self._bands = [EQBand() for _ in range(bandCount)] - - def getBand(self, band: int) -> "DynamicsProcessing.EQBand": - if 0 <= band < len(self._bands): - return self._bands[band] - raise IndexError(f"Band {band} out of range") - - class MBCBand(Stage): - """DynamicsProcessing.MBCBand — 多频段压缩频段""" - - def __init__(self, enabled: bool = True): - super().__init__(enabled) - self._gain = 0.0 - - def setGain(self, gain: float): - self._gain = gain - - def getGain(self) -> float: - return self._gain - - class MBC(Stage): - """DynamicsProcessing.MBC — 多频段压缩""" - - def __init__(self, enabled: bool = True, bandCount: int = 6): - super().__init__(enabled) - self._bands = [MBCBand() for _ in range(bandCount)] - - def getBand(self, band: int) -> "DynamicsProcessing.MBCBand": - if 0 <= band < len(self._bands): - return self._bands[band] - raise IndexError(f"Band {band} out of range") - - class Limiter(Stage): - """DynamicsProcessing.Limiter — 限制器""" - - def __init__(self, enabled: bool = True): - super().__init__(enabled) - - class Channel(Stub): - """DynamicsProcessing.Channel — 处理通道""" - - def __init__(self, stageCount: int = 1): - super().__init__() - self._stage_count = stageCount - - class Config(Stub): - """DynamicsProcessing.Config — 处理配置""" - - def __init__(self, channelCount: int = 1, preEqCount: int = 1, - mbcCount: int = 1, postEqCount: int = 1, - limiterCount: int = 1, prefEqBandCount: int = 6, - postEqBandCount: int = 6, mbcBandCount: int = 6): - super().__init__() - self._channels = [DynamicsProcessing.Channel(0) - for _ in range(channelCount)] - - def __init__(self, audioSession: int = 0): - super().__init__("DynamicsProcessing", 0, audioSession) - self._channel_count = 1 - self._post_constraints: List[Any] = [] - self._pre_eqs: List[DynamicsProcessing.EQ] = [DynamicsProcessing.EQ()] - self._post_eqs: List[DynamicsProcessing.EQ] = [DynamicsProcessing.EQ()] - self._mbcs: List[DynamicsProcessing.MBC] = [DynamicsProcessing.MBC()] - self._limiters: List[DynamicsProcessing.Limiter] = [DynamicsProcessing.Limiter()] - self._mbands: List[Any] = [] - self._channel_stage: List[Any] = [] - - def getChannelCount(self) -> int: - return self._channel_count - - def getPostConstraints(self) -> List[Any]: - return self._post_constraints - - def getPreEQs(self) -> List[EQ]: - return self._pre_eqs - - def getMbands(self) -> List[Any]: - return self._mbands - - def getPostEQs(self) -> List[EQ]: - return self._post_eqs - - def getLimiters(self) -> List[Limiter]: - return self._limiters - - def getMbc(self) -> List[MBC]: - return self._mbcs - - def getChannelStage(self) -> List[Any]: - return self._channel_stage - - def toString(self) -> str: - return f"DynamicsProcessing(channels={self._channel_count})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "AudioEffect", - "Equalizer", - "BassBoost", - "Virtualizer", - "PresetReverb", - "EnvironmentalReverb", - "LoudnessEnhancer", - "DynamicsProcessing", -] \ No newline at end of file diff --git a/android_pyapi/android/media_browse.py b/android_pyapi/android/media_browse.py deleted file mode 100644 index 686ab66..0000000 --- a/android_pyapi/android/media_browse.py +++ /dev/null @@ -1,310 +0,0 @@ -""" -Android media.browse 组件 — MediaBrowser, MediaItem, MediaDescription 等 -方法签名完全对齐 Android SDK -""" - -from typing import Any, Callable, Dict, List, Optional, Set - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# MediaBrowser -# ═══════════════════════════════════════════════════════════════ - -class MediaBrowser(Stub): - """MediaBrowser — 媒体浏览器""" - - EXTRA_PAGE = "android.media.browse.extra.PAGE" - EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE" - - # ═══════════════════════════════════════════════════════════ - # ConnectionCallback - # ═══════════════════════════════════════════════════════════ - - class ConnectionCallback(Stub): - """MediaBrowser.ConnectionCallback — 连接回调""" - - def onConnected(self): - pass - - def onConnectionSuspended(self): - pass - - def onConnectionFailed(self): - pass - - # ═══════════════════════════════════════════════════════════ - # SubscriptionCallback - # ═══════════════════════════════════════════════════════════ - - class SubscriptionCallback(Stub): - """MediaBrowser.SubscriptionCallback — 订阅回调""" - - def onChildrenLoaded(self, parentId: str, children: List["MediaBrowser.MediaItem"]): - pass - - def onError(self, parentId: str): - pass - - def onChildrenLoadedWithOptions(self, parentId: str, - children: List["MediaBrowser.MediaItem"], - options: Any): - pass - - # ═══════════════════════════════════════════════════════════ - # ItemCallback - # ═══════════════════════════════════════════════════════════ - - class ItemCallback(Stub): - """MediaBrowser.ItemCallback — 媒体项回调""" - - def onItemLoaded(self, item: "MediaBrowser.MediaItem"): - pass - - def onError(self, mediaId: str): - pass - - # ═══════════════════════════════════════════════════════════ - # MediaItem - # ═══════════════════════════════════════════════════════════ - - class MediaItem(Stub): - """MediaBrowser.MediaItem — 媒体项""" - - FLAG_BROWSABLE = 1 - FLAG_PLAYABLE = 2 - - def __init__(self, description: "MediaDescription" = None, flags: int = FLAG_PLAYABLE): - super().__init__() - self._description = description or MediaDescription() - self._flags = flags - - def getMediaId(self) -> Optional[str]: - return self._description.getMediaId() - - def getDescription(self) -> "MediaDescription": - return self._description - - def getFlags(self) -> int: - return self._flags - - def isBrowsable(self) -> bool: - return bool(self._flags & MediaBrowser.MediaItem.FLAG_BROWSABLE) - - def isPlayable(self) -> bool: - return bool(self._flags & MediaBrowser.MediaItem.FLAG_PLAYABLE) - - def toString(self) -> str: - return f"MediaItem(mediaId={self.getMediaId()}, flags={self._flags})" - - def __init__(self, context: Any = None, serviceComponent: Any = None, - connectionCallback: Optional[ConnectionCallback] = None, - rootHints: Any = None): - super().__init__() - self._connected = False - self._service_component = serviceComponent - self._root: str = "" - self._session_token: Any = None - self._connection_callback = connectionCallback - self._subscriptions: Dict[str, List[MediaBrowser.SubscriptionCallback]] = {} - self._media_tree: Dict[str, List[MediaBrowser.MediaItem]] = {} - self._item_cache: Dict[str, MediaBrowser.MediaItem] = {} - - def connect(self): - self._connected = True - self._root = "__ROOT__" - # Build a simple in-memory media tree - self._media_tree["__ROOT__"] = [ - MediaBrowser.MediaItem( - MediaDescription.Builder() - .setMediaId("__RECENT__") - .setTitle("Recently Played") - .setSubtitle("Your recently played media") - .build(), - flags=MediaBrowser.MediaItem.FLAG_BROWSABLE - ), - MediaBrowser.MediaItem( - MediaDescription.Builder() - .setMediaId("__ALL__") - .setTitle("All Music") - .setSubtitle("All your music library") - .build(), - flags=MediaBrowser.MediaItem.FLAG_BROWSABLE - ), - ] - self._media_tree["__RECENT__"] = [ - MediaBrowser.MediaItem( - MediaDescription.Builder() - .setMediaId("song_1") - .setTitle("Sample Song 1") - .setSubtitle("Artist 1") - .build(), - flags=MediaBrowser.MediaItem.FLAG_PLAYABLE - ), - ] - self._item_cache["song_1"] = self._media_tree["__RECENT__"][0] - - if self._connection_callback: - self._connection_callback.onConnected() - - def disconnect(self): - self._connected = False - self._subscriptions.clear() - if self._connection_callback: - self._connection_callback.onConnectionSuspended() - - def getConnected(self) -> bool: - return self._connected - - def getServiceComponent(self) -> Any: - return self._service_component - - def getRoot(self) -> str: - return self._root - - def isConnected(self) -> bool: - return self._connected - - def getSessionToken(self) -> Any: - return self._session_token - - def getItem(self, mediaId: str) -> Optional[MediaItem]: - return self._item_cache.get(mediaId) - - def subscribe(self, parentId: str, callback: SubscriptionCallback): - if parentId not in self._subscriptions: - self._subscriptions[parentId] = [] - self._subscriptions[parentId].append(callback) - - children = self._media_tree.get(parentId, []) - if children: - callback.onChildrenLoaded(parentId, children) - else: - callback.onError(parentId) - - def unsubscribe(self, parentId: str, callback: Optional[SubscriptionCallback] = None): - if parentId in self._subscriptions: - if callback: - self._subscriptions[parentId] = [ - cb for cb in self._subscriptions[parentId] - if cb is not callback - ] - if not self._subscriptions[parentId]: - del self._subscriptions[parentId] - else: - del self._subscriptions[parentId] - - -# ═══════════════════════════════════════════════════════════════ -# MediaDescription -# ═══════════════════════════════════════════════════════════════ - -class MediaDescription(Stub): - """MediaDescription — 媒体描述""" - - class Builder(Stub): - """MediaDescription.Builder — 媒体描述构建器""" - - def __init__(self): - super().__init__() - self._media_id: Optional[str] = None - self._title: Optional[str] = None - self._subtitle: Optional[str] = None - self._description: Optional[str] = None - self._icon_bitmap: Any = None - self._icon_uri: Optional[str] = None - self._extras: Any = None - self._media_uri: Optional[str] = None - - def setMediaId(self, mediaId: str) -> "MediaDescription.Builder": - self._media_id = mediaId - return self - - def setTitle(self, title: str) -> "MediaDescription.Builder": - self._title = title - return self - - def setSubtitle(self, subtitle: str) -> "MediaDescription.Builder": - self._subtitle = subtitle - return self - - def setDescription(self, description: str) -> "MediaDescription.Builder": - self._description = description - return self - - def setIconBitmap(self, icon: Any) -> "MediaDescription.Builder": - self._icon_bitmap = icon - return self - - def setIconUri(self, iconUri: str) -> "MediaDescription.Builder": - self._icon_uri = iconUri - return self - - def setExtras(self, extras: Any) -> "MediaDescription.Builder": - self._extras = extras - return self - - def setMediaUri(self, mediaUri: str) -> "MediaDescription.Builder": - self._media_uri = mediaUri - return self - - def build(self) -> "MediaDescription": - md = MediaDescription() - md._media_id = self._media_id - md._title = self._title - md._subtitle = self._subtitle - md._description = self._description - md._icon_bitmap = self._icon_bitmap - md._icon_uri = self._icon_uri - md._extras = self._extras - md._media_uri = self._media_uri - return md - - def __init__(self): - super().__init__() - self._media_id: Optional[str] = None - self._title: Optional[str] = None - self._subtitle: Optional[str] = None - self._description: Optional[str] = None - self._icon_bitmap: Any = None - self._icon_uri: Optional[str] = None - self._extras: Any = None - self._media_uri: Optional[str] = None - - def getMediaId(self) -> Optional[str]: - return self._media_id - - def getTitle(self) -> Optional[str]: - return self._title - - def getSubtitle(self) -> Optional[str]: - return self._subtitle - - def getDescription(self) -> Optional[str]: - return self._description - - def getIconBitmap(self) -> Any: - return self._icon_bitmap - - def getIconUri(self) -> Optional[str]: - return self._icon_uri - - def getExtras(self) -> Any: - return self._extras - - def getMediaUri(self) -> Optional[str]: - return self._media_uri - - def toString(self) -> str: - return f"MediaDescription(id={self._media_id}, title={self._title})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "MediaBrowser", - "MediaDescription", -] \ No newline at end of file diff --git a/android_pyapi/android/media_capture.py b/android_pyapi/android/media_capture.py deleted file mode 100644 index 8ac5bd5..0000000 --- a/android_pyapi/android/media_capture.py +++ /dev/null @@ -1,748 +0,0 @@ -""" -Android media.capture 组件 — CaptureSession, CaptureRequest, CaptureResult 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import math -import time -import threading -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# 捕获请求/结果常量 -# ═══════════════════════════════════════════════════════════════ - -REQUEST_ID = "request-id" -REQUEST_TYPE = "request-type" -RESULT_STATUS = "result-status" -RESULT_TIMESTAMP = "result-timestamp" -RESULT_FRAME_NUMBER = "result-frame-number" -ERROR_CAMERA_DEVICE = 1 -ERROR_CAMERA_DISABLED = 2 -ERROR_CAMERA_IN_USE = 3 -ERROR_CAMERA_SERVICE = 4 -ERROR_MAX_CAMERAS_IN_USE = 5 -ERROR_CAMERA_UNAVAILABLE = 1001 -ERROR_CAMERA_ACCESS_DENIED = 1002 - -CONTROL_AE_MODE_OFF = 0 -CONTROL_AE_MODE_ON = 1 -CONTROL_AE_MODE_ON_AUTO_FLASH = 2 -CONTROL_AE_MODE_ON_ALWAYS_FLASH = 3 -CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE = 4 -CONTROL_AE_MODE_ON_EXTERNAL_FLASH = 5 -CONTROL_AE_LOCK_OFF = 0 -CONTROL_AE_LOCK_ON = 1 -CONTROL_AE_PRECAPTURE_TRIGGER_IDLE = 0 -CONTROL_AE_PRECAPTURE_TRIGGER_START = 1 -CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL = 2 -CONTROL_AE_STATE_INACTIVE = 0 -CONTROL_AE_STATE_SEARCHING = 1 -CONTROL_AE_STATE_CONVERGED = 2 -CONTROL_AE_STATE_LOCKED = 3 -CONTROL_AE_STATE_FLASH_REQUIRED = 4 -CONTROL_AE_STATE_PRECAPTURE = 5 -CONTROL_AF_MODE_OFF = 0 -CONTROL_AF_MODE_AUTO = 1 -CONTROL_AF_MODE_MACRO = 2 -CONTROL_AF_MODE_CONTINUOUS_VIDEO = 3 -CONTROL_AF_MODE_CONTINUOUS_PICTURE = 4 -CONTROL_AF_MODE_EDOF = 5 -CONTROL_AF_TRIGGER_IDLE = 0 -CONTROL_AF_TRIGGER_START = 1 -CONTROL_AF_TRIGGER_CANCEL = 2 -CONTROL_AF_STATE_INACTIVE = 0 -CONTROL_AF_STATE_PASSIVE_SCAN = 1 -CONTROL_AF_STATE_PASSIVE_FOCUSED = 2 -CONTROL_AF_STATE_ACTIVE_SCAN = 3 -CONTROL_AF_STATE_FOCUSED_LOCKED = 4 -CONTROL_AF_STATE_NOT_FOCUSED_LOCKED = 5 -CONTROL_AF_STATE_PASSIVE_UNFOCUSED = 6 -CONTROL_AWB_MODE_OFF = 0 -CONTROL_AWB_MODE_AUTO = 1 -CONTROL_AWB_MODE_INCANDESCENT = 2 -CONTROL_AWB_MODE_FLUORESCENT = 3 -CONTROL_AWB_MODE_WARM_FLUORESCENT = 4 -CONTROL_AWB_MODE_DAYLIGHT = 5 -CONTROL_AWB_MODE_CLOUDY_DAYLIGHT = 6 -CONTROL_AWB_MODE_TWILIGHT = 7 -CONTROL_AWB_MODE_SHADE = 8 -CONTROL_AWB_LOCK_OFF = 0 -CONTROL_AWB_LOCK_ON = 1 -CONTROL_AWB_STATE_INACTIVE = 0 -CONTROL_AWB_STATE_SEARCHING = 1 -CONTROL_AWB_STATE_CONVERGED = 2 -CONTROL_AWB_STATE_LOCKED = 3 -CONTROL_CAPTURE_INTENT_PREVIEW = 1 -CONTROL_CAPTURE_INTENT_STILL_CAPTURE = 2 -CONTROL_CAPTURE_INTENT_VIDEO_RECORD = 3 -CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT = 4 -CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG = 5 -CONTROL_CAPTURE_INTENT_MANUAL = 6 -CONTROL_CAPTURE_INTENT_MOTION_TRACKING = 7 -CONTROL_EFFECT_MODE_OFF = 0 -CONTROL_EFFECT_MODE_MONO = 1 -CONTROL_EFFECT_MODE_NEGATIVE = 2 -CONTROL_EFFECT_MODE_SOLARIZE = 3 -CONTROL_EFFECT_MODE_SEPIA = 4 -CONTROL_EFFECT_MODE_POSTERIZE = 5 -CONTROL_EFFECT_MODE_WHITEBOARD = 6 -CONTROL_EFFECT_MODE_BLACKBOARD = 7 -CONTROL_EFFECT_MODE_AQUA = 8 -CONTROL_MODE_AUTO = 1 -CONTROL_MODE_OFF = 0 -CONTROL_MODE_USE_SCENE_MODE = 2 -CONTROL_SCENE_MODE_DISABLED = 0 -CONTROL_SCENE_MODE_FACE_PRIORITY = 1 -CONTROL_SCENE_MODE_ACTION = 2 -CONTROL_SCENE_MODE_PORTRAIT = 3 -CONTROL_SCENE_MODE_LANDSCAPE = 4 -CONTROL_SCENE_MODE_NIGHT = 5 -CONTROL_SCENE_MODE_NIGHT_PORTRAIT = 6 -CONTROL_SCENE_MODE_THEATRE = 7 -CONTROL_SCENE_MODE_BEACH = 8 -CONTROL_SCENE_MODE_SNOW = 9 -CONTROL_SCENE_MODE_SUNSET = 10 -CONTROL_SCENE_MODE_STEADYPHOTO = 11 -CONTROL_SCENE_MODE_FIREWORKS = 12 -CONTROL_SCENE_MODE_SPORTS = 13 -CONTROL_SCENE_MODE_PARTY = 14 -CONTROL_SCENE_MODE_CANDLELIGHT = 15 -CONTROL_SCENE_MODE_BARCODE = 16 -CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17 -CONTROL_SCENE_MODE_HDR = 18 -CONTROL_VIDEO_STABILIZATION_MODE_OFF = 0 -CONTROL_VIDEO_STABILIZATION_MODE_ON = 1 -CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW = 2 - -STATISTICS_FACE_DETECT_MODE_OFF = 0 -STATISTICS_FACE_DETECT_MODE_SIMPLE = 1 -STATISTICS_FACE_DETECT_MODE_FULL = 2 -STATISTICS_HOT_PIXEL_MAP_MODE_OFF = 0 -STATISTICS_HOT_PIXEL_MAP_MODE_ON = 1 -STATISTICS_LENS_SHADING_MAP_MODE_OFF = 0 -STATISTICS_LENS_SHADING_MAP_MODE_ON = 1 -STATISTICS_OIS_DATA_MODE_OFF = 0 -STATISTICS_OIS_DATA_MODE_ON = 1 -STATISTICS_SCENE_FLICKER_NONE = 0 -STATISTICS_SCENE_FLICKER_50HZ = 1 -STATISTICS_SCENE_FLICKER_60HZ = 2 - -FLASH_MODE_OFF = 0 -FLASH_MODE_SINGLE = 1 -FLASH_MODE_TORCH = 2 - -LENS_FACING_FRONT = 0 -LENS_FACING_BACK = 1 -LENS_FACING_EXTERNAL = 2 - -SENSOR_SENSITIVITY = "sensor-sensitivity" -SENSOR_EXPOSURE_TIME = "sensor-exposure-time" -SENSOR_FRAME_DURATION = "sensor-frame-duration" -SENSOR_TIMESTAMP = "sensor-timestamp" - -JPEG_QUALITY = "jpeg-quality" -JPEG_ORIENTATION = "jpeg-orientation" -JPEG_GPS_LOCATION = "jpeg-gps-location" -JPEG_THUMBNAIL_QUALITY = "jpeg-thumbnail-quality" -JPEG_THUMBNAIL_SIZE = "jpeg-thumbnail-size" - -NOISE_REDUCTION_MODE_OFF = 0 -NOISE_REDUCTION_MODE_FAST = 1 -NOISE_REDUCTION_MODE_HIGH_QUALITY = 2 -NOISE_REDUCTION_MODE_MINIMAL = 3 -NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG = 4 - -EDGE_MODE_OFF = 0 -EDGE_MODE_FAST = 1 -EDGE_MODE_HIGH_QUALITY = 2 -EDGE_MODE_ZERO_SHUTTER_LAG = 3 - -TONEMAP_MODE_CONTRAST_CURVE = 0 -TONEMAP_MODE_FAST = 1 -TONEMAP_MODE_HIGH_QUALITY = 2 -TONEMAP_MODE_GAMMA_VALUE = 3 -TONEMAP_MODE_PRESET_CURVE = 4 -TONEMAP_PRESET_CURVE_SRGB = 0 -TONEMAP_PRESET_CURVE_REC709 = 1 - -REQUEST_AVAILABLE_CAPABILITIES = "request-available-capabilities" -REQUEST_AVAILABLE_CHARACTERISTICS_KEYS = "request-available-characteristics-keys" -REQUEST_AVAILABLE_RESULT_KEYS = "request-available-result-keys" -REQUEST_AVAILABLE_SESSION_KEYS = "request-available-session-keys" -REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES = "request-available-dynamic-range-profiles" -REQUEST_AVAILABLE_RAW_MIN_DENOISE = "request-available-raw-min-denoise" -REQUEST_AVAILABLE_TEST_PATTERN_MODES = "request-available-test-pattern-modes" -REQUEST_PARTIAL_RESULT_COUNT = "request-partial-result-count" -REQUEST_PIPELINE_MAX_DEPTH = "request-pipeline-max-depth" - - -# ═══════════════════════════════════════════════════════════════ -# CaptureRequest -# ═══════════════════════════════════════════════════════════════ - -class CaptureRequest(Stub): - """CaptureRequest — 捕获请求""" - - class Builder(Stub): - """CaptureRequest.Builder — 捕获请求构建器""" - - def __init__(self): - super().__init__() - self._parameters: Dict[str, Any] = {} - self._tags: Dict[str, Any] = {} - self._surfaces: List[Any] = [] - self._repeating = False - - def set(self, key: str, value: Any) -> "CaptureRequest.Builder": - self._parameters[key] = value - return self - - def get(self, key: str) -> Optional[Any]: - return self._parameters.get(key) - - def setTag(self, key: str, value: Any) -> "CaptureRequest.Builder": - self._tags[key] = value - return self - - def getTag(self, key: str) -> Optional[Any]: - return self._tags.get(key) - - def addTarget(self, surface: Any) -> "CaptureRequest.Builder": - if surface not in self._surfaces: - self._surfaces.append(surface) - return self - - def removeTarget(self, surface: Any) -> "CaptureRequest.Builder": - if surface in self._surfaces: - self._surfaces.remove(surface) - return self - - def getTargets(self) -> List[Any]: - return list(self._surfaces) - - def isEmpty(self) -> bool: - return len(self._parameters) == 0 - - def build(self) -> "CaptureRequest": - req = CaptureRequest() - req._parameters = dict(self._parameters) - req._tags = dict(self._tags) - req._surfaces = list(self._surfaces) - req._repeating = self._repeating - return req - - def toString(self) -> str: - return (f"CaptureRequest.Builder(params={len(self._parameters)}, " - f"targets={len(self._surfaces)})") - - def __init__(self): - super().__init__() - self._parameters: Dict[str, Any] = {} - self._tags: Dict[str, Any] = {} - self._surfaces: List[Any] = [] - self._repeating = False - self._frame_number: int = 0 - self._request_id: int = 0 - - def get(self, key: str) -> Optional[Any]: - return self._parameters.get(key) - - def set(self, key: str, value: Any) -> None: - self._parameters[key] = value - - def getParameters(self) -> Dict[str, Any]: - return dict(self._parameters) - - def getTags(self) -> Dict[str, Any]: - return dict(self._tags) - - def getTargets(self) -> List[Any]: - return list(self._surfaces) - - def isRepeating(self) -> bool: - return self._repeating - - def setRepeating(self, repeating: bool) -> None: - self._repeating = repeating - - def getFrameNumber(self) -> int: - return self._frame_number - - def setFrameNumber(self, frame: int) -> None: - self._frame_number = frame - - def getRequestId(self) -> int: - return self._request_id - - def setRequestId(self, req_id: int) -> None: - self._request_id = req_id - - def toString(self) -> str: - return (f"CaptureRequest(id={self._request_id}, " - f"frame={self._frame_number}, " - f"params={len(self._parameters)})") - - -# ═══════════════════════════════════════════════════════════════ -# CaptureResult -# ═══════════════════════════════════════════════════════════════ - -class CaptureResult(Stub): - """CaptureResult — 捕获结果""" - - def __init__(self): - super().__init__() - self._results: Dict[str, Any] = {} - self._request: Optional[CaptureRequest] = None - self._sequence_id: int = 0 - self._frame_number: int = 0 - self._timestamp: int = 0 - self._partial_count: int = 0 - self._metadata: Dict[str, Any] = {} - - def get(self, key: str) -> Optional[Any]: - return self._results.get(key) - - def put(self, key: str, value: Any) -> None: - self._results[key] = value - - def getAll(self) -> Dict[str, Any]: - return dict(self._results) - - def getRequest(self) -> Optional[CaptureRequest]: - return self._request - - def setRequest(self, request: CaptureRequest) -> None: - self._request = request - - def getSequenceId(self) -> int: - return self._sequence_id - - def setSequenceId(self, seq_id: int) -> None: - self._sequence_id = seq_id - - def getFrameNumber(self) -> int: - return self._frame_number - - def setFrameNumber(self, frame: int) -> None: - self._frame_number = frame - - def getTimestamp(self) -> int: - return self._timestamp - - def setTimestamp(self, ts: int) -> None: - self._timestamp = ts - - def getMetadata(self, key: str) -> Any: - return self._metadata.get(key) - - def setMetadata(self, key: str, value: Any) -> None: - self._metadata[key] = value - - def getPartialCount(self) -> int: - return self._partial_count - - def setPartialCount(self, count: int) -> None: - self._partial_count = count - - def toString(self) -> str: - return (f"CaptureResult(frame={self._frame_number}, " - f"seq={self._sequence_id}, " - f"results={len(self._results)})") - - -# ═══════════════════════════════════════════════════════════════ -# TotalCaptureResult -# ═══════════════════════════════════════════════════════════════ - -class TotalCaptureResult(CaptureResult): - """TotalCaptureResult — 完整捕获结果(包含所有部分结果)""" - - def __init__(self): - super().__init__() - self._partial_results: List[CaptureResult] = [] - self._sensor_timestamp: int = 0 - self._physical_camera_results: Dict[str, CaptureResult] = {} - - def getPartialResults(self) -> List[CaptureResult]: - return list(self._partial_results) - - def addPartialResult(self, result: CaptureResult) -> None: - self._partial_results.append(result) - - def getPhysicalCameraResults(self) -> Dict[str, CaptureResult]: - return dict(self._physical_camera_results) - - def setPhysicalCameraResult(self, camera_id: str, result: CaptureResult) -> None: - self._physical_camera_results[camera_id] = result - - def toString(self) -> str: - return (f"TotalCaptureResult(frame={self.getFrameNumber()}, " - f"partials={len(self._partial_results)}, " - f"physical={len(self._physical_camera_results)})") - - -# ═══════════════════════════════════════════════════════════════ -# CaptureFailure -# ═══════════════════════════════════════════════════════════════ - -class CaptureFailure(Stub): - """CaptureFailure — 捕获失败信息""" - - REASON_ERROR = 0 - REASON_FLUSHED = 1 - REASON_DROPPED = 2 - - def __init__(self, request: Optional[CaptureRequest] = None, - reason: int = REASON_ERROR, - frame_number: int = 0, - sequence_id: int = 0, - is_image_capture: bool = False): - super().__init__() - self._request = request - self._reason = reason - self._frame_number = frame_number - self._sequence_id = sequence_id - self._is_image_capture = is_image_capture - self._physical_camera_id: Optional[str] = None - - def getRequest(self) -> Optional[CaptureRequest]: - return self._request - - def getReason(self) -> int: - return self._reason - - def getFrameNumber(self) -> int: - return self._frame_number - - def getSequenceId(self) -> int: - return self._sequence_id - - def isImageCapture(self) -> bool: - return self._is_image_capture - - def getPhysicalCameraId(self) -> Optional[str]: - return self._physical_camera_id - - def setPhysicalCameraId(self, camera_id: str) -> None: - self._physical_camera_id = camera_id - - def toString(self) -> str: - reasons = {0: "ERROR", 1: "FLUSHED", 2: "DROPPED"} - return (f"CaptureFailure(reason={reasons.get(self._reason, 'UNKNOWN')}, " - f"frame={self._frame_number})") - - -# ═══════════════════════════════════════════════════════════════ -# CaptureExtension -# ═══════════════════════════════════════════════════════════════ - -class CaptureExtension(Stub): - """CaptureExtension — 捕获扩展""" - - EXTENSION_NONE = 0 - EXTENSION_AUTO = 1 - EXTENSION_BEAUTY = 2 - EXTENSION_BOKEH = 3 - EXTENSION_HDR = 4 - EXTENSION_NIGHT = 5 - - def __init__(self, extension_type: int = EXTENSION_NONE): - super().__init__() - self._extension_type = extension_type - self._parameters: Dict[str, Any] = {} - self._enabled = False - - def getExtensionType(self) -> int: - return self._extension_type - - def setExtensionType(self, ext_type: int) -> None: - self._extension_type = ext_type - - def isEnabled(self) -> bool: - return self._enabled - - def setEnabled(self, enabled: bool) -> None: - self._enabled = enabled - - def setParameter(self, key: str, value: Any) -> None: - self._parameters[key] = value - - def getParameter(self, key: str) -> Optional[Any]: - return self._parameters.get(key) - - def toString(self) -> str: - types = {0: "NONE", 1: "AUTO", 2: "BEAUTY", 3: "BOKEH", 4: "HDR", 5: "NIGHT"} - return f"CaptureExtension(type={types.get(self._extension_type, 'UNKNOWN')})" - - -# ═══════════════════════════════════════════════════════════════ -# CaptureSession -# ═══════════════════════════════════════════════════════════════ - -class CaptureSession(Stub): - """CaptureSession — 捕获会话""" - - SESSION_ID = "session-id" - SESSION_CONFIGURATION = "session-configuration" - SESSION_STATE = "session-state" - - SESSION_STATE_OPEN = 0 - SESSION_STATE_READY = 1 - SESSION_STATE_ACTIVE = 2 - SESSION_STATE_CLOSED = 3 - - CAPTURE = "capture" - CAPTURE_BURST = "capture-burst" - SET_REPEATING_REQUEST = "set-repeating-request" - SET_REPEATING_BURST = "set-repeating-burst" - STOP_REPEATING = "stop-repeating" - ABORT_CAPTURES = "abort-captures" - - # 回调接口 - class CaptureCallback(Stub): - """CaptureSession.CaptureCallback — 捕获回调""" - - def onCaptureStarted(self, session: "CaptureSession", request: CaptureRequest, - timestamp: int, frame_number: int) -> None: - pass - - def onCaptureProgressed(self, session: "CaptureSession", request: CaptureRequest, - partial_result: CaptureResult) -> None: - pass - - def onCaptureCompleted(self, session: "CaptureSession", request: CaptureRequest, - result: TotalCaptureResult) -> None: - pass - - def onCaptureFailed(self, session: "CaptureSession", request: CaptureRequest, - failure: CaptureFailure) -> None: - pass - - def onCaptureSequenceCompleted(self, session: "CaptureSession", - sequence_id: int, frame_number: int) -> None: - pass - - def onCaptureSequenceAborted(self, session: "CaptureSession", - sequence_id: int) -> None: - pass - - def onCaptureBufferLost(self, session: "CaptureSession", - request: CaptureRequest, target: Any, - frame_number: int) -> None: - pass - - class StateCallback(Stub): - """CaptureSession.StateCallback — 会话状态回调""" - - def onConfigured(self, session: "CaptureSession") -> None: - pass - - def onConfigureFailed(self, session: "CaptureSession") -> None: - pass - - def onReady(self, session: "CaptureSession") -> None: - pass - - def onActive(self, session: "CaptureSession") -> None: - pass - - def onClosed(self, session: "CaptureSession") -> None: - pass - - def __init__(self): - super().__init__() - self._state = self.SESSION_STATE_CLOSED - self._state_callback: Optional[CaptureSession.StateCallback] = None - self._capture_callback: Optional[CaptureSession.CaptureCallback] = None - self._repeating_request: Optional[CaptureRequest] = None - self._output_config: Optional[Any] = None - self._input_config: Optional[Any] = None - self._session_id: int = 0 - self._next_sequence_id: int = 1 - self._lock = threading.Lock() - - def open(self, callback: StateCallback, handler: Any = None) -> None: - self._state_callback = callback - self._state = self.SESSION_STATE_OPEN - if callback: - callback.onConfigured(self) - - def capture(self, request: CaptureRequest, - callback: Optional[CaptureCallback] = None, - handler: Any = None) -> int: - with self._lock: - seq_id = self._next_sequence_id - self._next_sequence_id += 1 - if callback: - callback.onCaptureStarted(self, request, int(time.time() * 1e9), 0) - result = TotalCaptureResult() - result.setRequest(request) - result.setSequenceId(seq_id) - callback.onCaptureCompleted(self, request, result) - return seq_id - - def captureBurst(self, requests: List[CaptureRequest], - callback: Optional[CaptureCallback] = None, - handler: Any = None) -> int: - with self._lock: - seq_id = self._next_sequence_id - self._next_sequence_id += 1 - if callback: - for req in requests: - callback.onCaptureStarted(self, req, int(time.time() * 1e9), 0) - result = TotalCaptureResult() - result.setRequest(req) - result.setSequenceId(seq_id) - callback.onCaptureCompleted(self, req, result) - return seq_id - - def setRepeatingRequest(self, request: CaptureRequest, - callback: Optional[CaptureCallback] = None, - handler: Any = None) -> int: - with self._lock: - self._repeating_request = request - seq_id = self._next_sequence_id - self._next_sequence_id += 1 - return seq_id - - def setRepeatingBurst(self, requests: List[CaptureRequest], - callback: Optional[CaptureCallback] = None, - handler: Any = None) -> int: - with self._lock: - seq_id = self._next_sequence_id - self._next_sequence_id += 1 - return seq_id - - def stopRepeating(self) -> None: - with self._lock: - self._repeating_request = None - - def abortCaptures(self) -> None: - with self._lock: - self._repeating_request = None - - def close(self) -> None: - with self._lock: - self._state = self.SESSION_STATE_CLOSED - if self._state_callback: - self._state_callback.onClosed(self) - - def getState(self) -> int: - return self._state - - def isClosed(self) -> bool: - return self._state == self.SESSION_STATE_CLOSED - - def getSessionId(self) -> int: - return self._session_id - - def toString(self) -> str: - states = {0: "OPEN", 1: "READY", 2: "ACTIVE", 3: "CLOSED"} - return f"CaptureSession(state={states.get(self._state, 'UNKNOWN')})" - - -CaptureSession.CaptureCallback = CaptureSession.CaptureCallback -CaptureSession.StateCallback = CaptureSession.StateCallback - - -class CaptureSessionConfig(Stub): - """CaptureSessionConfig — 捕获会话配置""" - - def __init__(self): - super().__init__() - self._output_surfaces: List[Any] = [] - self._input_surface: Optional[Any] = None - self._session_params: Dict[str, Any] = {} - self._session_type: int = 0 - - def addOutputSurface(self, surface: Any) -> None: - if surface not in self._output_surfaces: - self._output_surfaces.append(surface) - - def removeOutputSurface(self, surface: Any) -> None: - if surface in self._output_surfaces: - self._output_surfaces.remove(surface) - - def getOutputSurfaces(self) -> List[Any]: - return list(self._output_surfaces) - - def setInputSurface(self, surface: Any) -> None: - self._input_surface = surface - - def getInputSurface(self) -> Optional[Any]: - return self._input_surface - - def setSessionParameter(self, key: str, value: Any) -> None: - self._session_params[key] = value - - def getSessionParameter(self, key: str) -> Optional[Any]: - return self._session_params.get(key) - - def toString(self) -> str: - return (f"CaptureSessionConfig(outputs={len(self._output_surfaces)}, " - f"input={'yes' if self._input_surface else 'no'})") - - -class CaptureSessionOutput(Stub): - """CaptureSessionOutput — 会话输出配置""" - - def __init__(self, surface: Any): - super().__init__() - self._surface = surface - self._physical_camera_id: Optional[str] = None - - def getSurface(self) -> Any: - return self._surface - - def setPhysicalCameraId(self, camera_id: str) -> None: - self._physical_camera_id = camera_id - - def getPhysicalCameraId(self) -> Optional[str]: - return self._physical_camera_id - - def toString(self) -> str: - return (f"CaptureSessionOutput(surface={self._surface}, " - f"physicalId={self._physical_camera_id})") - - -class CaptureSessionInput(Stub): - """CaptureSessionInput — 会话输入配置""" - - def __init__(self, surface: Any): - super().__init__() - self._surface = surface - - def getSurface(self) -> Any: - return self._surface - - def toString(self) -> str: - return f"CaptureSessionInput(surface={self._surface})" - - -class CaptureSessionParams(Stub): - """CaptureSessionParams — 会话参数配置""" - - def __init__(self): - super().__init__() - self._params: Dict[str, Any] = {} - - def set(self, key: str, value: Any) -> None: - self._params[key] = value - - def get(self, key: str) -> Optional[Any]: - return self._params.get(key) - - def getAll(self) -> Dict[str, Any]: - return dict(self._params) - - def toString(self) -> str: - return f"CaptureSessionParams(params={len(self._params)})" \ No newline at end of file diff --git a/android_pyapi/android/media_effect.py b/android_pyapi/android/media_effect.py deleted file mode 100644 index 8164ccc..0000000 --- a/android_pyapi/android/media_effect.py +++ /dev/null @@ -1,615 +0,0 @@ -""" -Android media.effect 组件 — Effect, EffectContext, EffectFactory, EffectUpdateListener -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import math -import threading -from typing import Any, Callable, Dict, List, Optional, Tuple, Union - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# Effect 常量 -# ═══════════════════════════════════════════════════════════════ - -EFFECT_AUTOFIX = "android.media.effect.effects.AutoFixEffect" -EFFECT_BACKDROPPER = "android.media.effect.effects.BackDropperEffect" -EFFECT_BITMAPOVERLAY = "android.media.effect.effects.BitmapOverlayEffect" -EFFECT_BLACKWHITE = "android.media.effect.effects.BlackWhiteEffect" -EFFECT_BRIGHTNESS = "android.media.effect.effects.BrightnessEffect" -EFFECT_CONTRAST = "android.media.effect.effects.ContrastEffect" -EFFECT_CROSSPROCESS = "android.media.effect.effects.CrossProcessEffect" -EFFECT_DOCUMENTARY = "android.media.effect.effects.DocumentaryEffect" -EFFECT_DUOTONE = "android.media.effect.effects.DuotoneEffect" -EFFECT_FILLLIGHT = "android.media.effect.effects.FillLightEffect" -EFFECT_FISHEYE = "android.media.effect.effects.FisheyeEffect" -EFFECT_FLIP = "android.media.effect.effects.FlipEffect" -EFFECT_GRAIN = "android.media.effect.effects.GrainEffect" -EFFECT_GRAYSCALE = "android.media.effect.effects.GrayscaleEffect" -EFFECT_LOMO = "android.media.effect.effects.LomoishEffect" -EFFECT_NEGATIVE = "android.media.effect.effects.NegativeEffect" -EFFECT_POSTERIZE = "android.media.effect.effects.PosterizeEffect" -EFFECT_REDEYE = "android.media.effect.effects.RedEyeEffect" -EFFECT_ROTATE = "android.media.effect.effects.RotateEffect" -EFFECT_SATURATE = "android.media.effect.effects.SaturateEffect" -EFFECT_SEPIA = "android.media.effect.effects.SepiaEffect" -EFFECT_SHARPEN = "android.media.effect.effects.SharpenEffect" -EFFECT_TEMPERATURE = "android.media.effect.effects.TemperatureEffect" -EFFECT_TINT = "android.media.effect.effects.TintEffect" -EFFECT_VIGNETTE = "android.media.effect.effects.VignetteEffect" - - -class EffectUpdateListener: - """EffectUpdateListener — 效果更新监听器""" - - def onEffectUpdated(self, effect: "Effect", info: Any) -> None: - """当效果状态更新时调用""" - pass - - -class Effect(Stub): - """Effect — 图像效果基类""" - - def __init__(self, effect_type: str = ""): - super().__init__() - self._type = effect_type - self._parameters: Dict[str, Any] = {} - self._update_listener: Optional[EffectUpdateListener] = None - self._listener_lock = threading.Lock() - - def apply(self, input_id: int, output_id: int) -> None: - """应用效果到输入表面,输出到输出表面""" - raise AndroidOnlyError("Effect.apply() 仅在 Android 设备上可用") - - def setParameter(self, param_key: str, value: Any) -> None: - """设置效果参数""" - self._parameters[param_key] = value - - def getParameter(self, param_key: str) -> Any: - """获取效果参数值""" - return self._parameters.get(param_key, None) - - def getName(self) -> str: - """获取效果名称""" - return self._type - - def setUpdateListener(self, listener: Optional[EffectUpdateListener]) -> None: - """设置更新监听器""" - with self._listener_lock: - self._update_listener = listener - - def getUpdateListener(self) -> Optional[EffectUpdateListener]: - """获取当前更新监听器""" - with self._listener_lock: - return self._update_listener - - def release(self) -> None: - """释放效果资源""" - self._parameters.clear() - self._update_listener = None - - def toString(self) -> str: - return f"Effect(type={self._type})" - - -class EffectContext(Stub): - """EffectContext — 效果上下文,管理 GL 资源和效果生命周期""" - - _instance_counter = 0 - _instances: Dict[int, "EffectContext"] = {} - _instances_lock = threading.Lock() - - def __init__(self): - super().__init__() - with EffectContext._instances_lock: - EffectContext._instance_counter += 1 - self._context_id = EffectContext._instance_counter - EffectContext._instances[self._context_id] = self - self._effects: Dict[str, Effect] = {} - self._effects_lock = threading.Lock() - self._released = False - - @staticmethod - def createWithCurrentGlContext() -> "EffectContext": - """使用当前 GL 上下文创建 EffectContext""" - ctx = EffectContext() - return ctx - - def getFactory(self) -> "EffectFactory": - """获取与此上下文关联的 EffectFactory""" - if self._released: - raise RuntimeException("EffectContext 已被释放") - return EffectFactory(self) - - def createEffect(self, effect_type: str) -> Effect: - """创建指定类型的效果""" - if self._released: - raise RuntimeException("EffectContext 已被释放") - factory = self.getFactory() - return factory.createEffect(effect_type) - - def getEffect(self, name: str) -> Optional[Effect]: - """获取已创建的指定名称的效果""" - with self._effects_lock: - return self._effects.get(name, None) - - def getAllEffects(self) -> List[Effect]: - """获取所有已创建的效果列表""" - with self._effects_lock: - return list(self._effects.values()) - - def release(self) -> None: - """释放 EffectContext 及其所有效果""" - if self._released: - return - self._released = True - with self._effects_lock: - for effect in self._effects.values(): - effect.release() - self._effects.clear() - with EffectContext._instances_lock: - EffectContext._instances.pop(self._context_id, None) - - def isReleased(self) -> bool: - """检查是否已释放""" - return self._released - - def getContextId(self) -> int: - return self._context_id - - def toString(self) -> str: - return f"EffectContext(id={self._context_id}, released={self._released})" - - -class EffectFactory(Stub): - """EffectFactory — 效果工厂,用于创建各种 Effect 实例""" - - # 效果类型常量 - EFFECT_AUTOFIX = EFFECT_AUTOFIX - EFFECT_BACKDROPPER = EFFECT_BACKDROPPER - EFFECT_BITMAPOVERLAY = EFFECT_BITMAPOVERLAY - EFFECT_BLACKWHITE = EFFECT_BLACKWHITE - EFFECT_BRIGHTNESS = EFFECT_BRIGHTNESS - EFFECT_CONTRAST = EFFECT_CONTRAST - EFFECT_CROSSPROCESS = EFFECT_CROSSPROCESS - EFFECT_DOCUMENTARY = EFFECT_DOCUMENTARY - EFFECT_DUOTONE = EFFECT_DUOTONE - EFFECT_FILLLIGHT = EFFECT_FILLLIGHT - EFFECT_FISHEYE = EFFECT_FISHEYE - EFFECT_FLIP = EFFECT_FLIP - EFFECT_GRAIN = EFFECT_GRAIN - EFFECT_GRAYSCALE = EFFECT_GRAYSCALE - EFFECT_LOMO = EFFECT_LOMO - EFFECT_NEGATIVE = EFFECT_NEGATIVE - EFFECT_POSTERIZE = EFFECT_POSTERIZE - EFFECT_REDEYE = EFFECT_REDEYE - EFFECT_ROTATE = EFFECT_ROTATE - EFFECT_SATURATE = EFFECT_SATURATE - EFFECT_SEPIA = EFFECT_SEPIA - EFFECT_SHARPEN = EFFECT_SHARPEN - EFFECT_TEMPERATURE = EFFECT_TEMPERATURE - EFFECT_TINT = EFFECT_TINT - EFFECT_VIGNETTE = EFFECT_VIGNETTE - - _EFFECT_NAMES: List[str] = [ - EFFECT_AUTOFIX, EFFECT_BACKDROPPER, EFFECT_BITMAPOVERLAY, - EFFECT_BLACKWHITE, EFFECT_BRIGHTNESS, EFFECT_CONTRAST, - EFFECT_CROSSPROCESS, EFFECT_DOCUMENTARY, EFFECT_DUOTONE, - EFFECT_FILLLIGHT, EFFECT_FISHEYE, EFFECT_FLIP, EFFECT_GRAIN, - EFFECT_GRAYSCALE, EFFECT_LOMO, EFFECT_NEGATIVE, EFFECT_POSTERIZE, - EFFECT_REDEYE, EFFECT_ROTATE, EFFECT_SATURATE, EFFECT_SEPIA, - EFFECT_SHARPEN, EFFECT_TEMPERATURE, EFFECT_TINT, EFFECT_VIGNETTE, - ] - - def __init__(self, context: EffectContext): - super().__init__() - self._context = context - - def createEffect(self, effect_type: str) -> Effect: - """创建指定类型的效果""" - if effect_type not in self._EFFECT_NAMES: - raise IllegalArgumentException(f"不支持的效果类型: {effect_type}") - effect = Effect(effect_type) - with self._context._effects_lock: - self._context._effects[effect_type] = effect - return effect - - def isEffectSupported(self, effect_type: str) -> bool: - """检查效果类型是否被支持""" - return effect_type in self._EFFECT_NAMES - - def getSupportedEffects(self) -> List[str]: - """获取所有支持的效果类型列表""" - return list(self._EFFECT_NAMES) - - def toString(self) -> str: - return f"EffectFactory(context_id={self._context.getContextId()})" - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -class AutoFixEffect(Effect): - """AutoFixEffect — 自动修复效果,调整对比度/亮度平衡""" - - def __init__(self): - super().__init__(EFFECT_AUTOFIX) - self._scale = 0.5 - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "scale": - self._scale = max(0.0, min(1.0, float(value))) - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "scale": - return self._scale - return super().getParameter(param_key) - - -class BackDropperEffect(Effect): - """BackDropperEffect — 背景替换效果""" - - def __init__(self): - super().__init__(EFFECT_BACKDROPPER) - self._source_id = 0 - self._background_id = 0 - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "source": - self._source_id = int(value) - elif param_key == "background": - self._background_id = int(value) - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "source": - return self._source_id - if param_key == "background": - return self._background_id - return super().getParameter(param_key) - - -class BitmapOverlayEffect(Effect): - """BitmapOverlayEffect — 位图覆盖效果""" - - def __init__(self): - super().__init__(EFFECT_BITMAPOVERLAY) - self._bitmap: Any = None - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "bitmap": - self._bitmap = value - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "bitmap": - return self._bitmap - return super().getParameter(param_key) - - -class BlackWhiteEffect(Effect): - """BlackWhiteEffect — 黑白效果""" - - def __init__(self): - super().__init__(EFFECT_BLACKWHITE) - self._black = 0.0 - self._white = 1.0 - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "black": - self._black = max(0.0, min(1.0, float(value))) - elif param_key == "white": - self._white = max(0.0, min(1.0, float(value))) - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "black": - return self._black - if param_key == "white": - return self._white - return super().getParameter(param_key) - - -class BrightnessEffect(Effect): - """BrightnessEffect — 亮度调整效果""" - - def __init__(self): - super().__init__(EFFECT_BRIGHTNESS) - self._brightness = 0.0 - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "brightness": - self._brightness = max(-1.0, min(1.0, float(value))) - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "brightness": - return self._brightness - return super().getParameter(param_key) - - -class ContrastEffect(Effect): - """ContrastEffect — 对比度调整效果""" - - def __init__(self): - super().__init__(EFFECT_CONTRAST) - self._contrast = 1.0 - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "contrast": - self._contrast = max(0.0, float(value)) - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "contrast": - return self._contrast - return super().getParameter(param_key) - - -class CrossProcessEffect(Effect): - """CrossProcessEffect — 交叉冲印效果""" - - def __init__(self): - super().__init__(EFFECT_CROSSPROCESS) - - -class DocumentaryEffect(Effect): - """DocumentaryEffect — 纪录片风格效果""" - - def __init__(self): - super().__init__(EFFECT_DOCUMENTARY) - - -class DuotoneEffect(Effect): - """DuotoneEffect — 双色调效果""" - - def __init__(self): - super().__init__(EFFECT_DUOTONE) - self._first_color = 0xFFFF0000 - self._second_color = 0xFF0000FF - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key in ("first_color", "second_color"): - setattr(self, f"_{param_key}", int(value) & 0xFFFFFFFF) - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "first_color": - return self._first_color - if param_key == "second_color": - return self._second_color - return super().getParameter(param_key) - - -class FillLightEffect(Effect): - """FillLightEffect — 填充光效果(人脸照明增强)""" - - def __init__(self): - super().__init__(EFFECT_FILLLIGHT) - self._strength = 0.0 - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "strength": - self._strength = max(0.0, min(1.0, float(value))) - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "strength": - return self._strength - return super().getParameter(param_key) - - -class FisheyeEffect(Effect): - """FisheyeEffect — 鱼眼效果""" - - def __init__(self): - super().__init__(EFFECT_FISHEYE) - self._scale = 0.5 - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "scale": - self._scale = max(0.0, min(1.0, float(value))) - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "scale": - return self._scale - return super().getParameter(param_key) - - -class FlipEffect(Effect): - """FlipEffect — 翻转效果""" - - def __init__(self): - super().__init__(EFFECT_FLIP) - self._horizontal = True - self._vertical = False - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "horizontal": - self._horizontal = bool(value) - elif param_key == "vertical": - self._vertical = bool(value) - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "horizontal": - return self._horizontal - if param_key == "vertical": - return self._vertical - return super().getParameter(param_key) - - -class GrainEffect(Effect): - """GrainEffect — 颗粒效果""" - - def __init__(self): - super().__init__(EFFECT_GRAIN) - self._intensity = 0.5 - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "intensity": - self._intensity = max(0.0, min(1.0, float(value))) - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "intensity": - return self._intensity - return super().getParameter(param_key) - - -class GrayscaleEffect(Effect): - """GrayscaleEffect — 灰度效果""" - - def __init__(self): - super().__init__(EFFECT_GRAYSCALE) - - -class LomoishEffect(Effect): - """LomoishEffect — LOMO 风格效果""" - - def __init__(self): - super().__init__(EFFECT_LOMO) - - -class NegativeEffect(Effect): - """NegativeEffect — 底片效果""" - - def __init__(self): - super().__init__(EFFECT_NEGATIVE) - - -class PosterizeEffect(Effect): - """PosterizeEffect — 色调分离效果""" - - def __init__(self): - super().__init__(EFFECT_POSTERIZE) - - -class RedEyeEffect(Effect): - """RedEyeEffect — 红眼消除效果""" - - def __init__(self): - super().__init__(EFFECT_REDEYE) - - -class RotateEffect(Effect): - """RotateEffect — 旋转效果""" - - def __init__(self): - super().__init__(EFFECT_ROTATE) - self._angle = 0.0 - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "angle": - self._angle = float(value) % 360.0 - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "angle": - return self._angle - return super().getParameter(param_key) - - -class SaturateEffect(Effect): - """SaturateEffect — 饱和度调整效果""" - - def __init__(self): - super().__init__(EFFECT_SATURATE) - self._scale = 1.0 - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "scale": - self._scale = max(0.0, float(value)) - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "scale": - return self._scale - return super().getParameter(param_key) - - -class SepiaEffect(Effect): - """SepiaEffect — 棕褐色效果""" - - def __init__(self): - super().__init__(EFFECT_SEPIA) - - -class SharpenEffect(Effect): - """SharpenEffect — 锐化效果""" - - def __init__(self): - super().__init__(EFFECT_SHARPEN) - self._scale = 0.5 - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "scale": - self._scale = max(0.0, float(value)) - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "scale": - return self._scale - return super().getParameter(param_key) - - -class TemperatureEffect(Effect): - """TemperatureEffect — 色温效果""" - - def __init__(self): - super().__init__(EFFECT_TEMPERATURE) - self._scale = 0.5 - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "scale": - self._scale = max(0.0, min(1.0, float(value))) - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "scale": - return self._scale - return super().getParameter(param_key) - - -class TintEffect(Effect): - """TintEffect — 色调效果""" - - def __init__(self): - super().__init__(EFFECT_TINT) - self._tint_color = 0xFF000000 - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "tint": - self._tint_color = int(value) & 0xFFFFFFFF - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "tint": - return self._tint_color - return super().getParameter(param_key) - - -class VignetteEffect(Effect): - """VignetteEffect — 暗角效果""" - - def __init__(self): - super().__init__(EFFECT_VIGNETTE) - self._scale = 0.5 - - def setParameter(self, param_key: str, value: Any) -> None: - if param_key == "scale": - self._scale = max(0.0, min(1.0, float(value))) - super().setParameter(param_key, value) - - def getParameter(self, param_key: str) -> Any: - if param_key == "scale": - return self._scale - return super().getParameter(param_key) \ No newline at end of file diff --git a/android_pyapi/android/media_face.py b/android_pyapi/android/media_face.py deleted file mode 100644 index a0c14a6..0000000 --- a/android_pyapi/android/media_face.py +++ /dev/null @@ -1,499 +0,0 @@ -""" -Android media.face 组件 — FaceDetector, Face 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import math -import threading -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -CONFIDENCE_THRESHOLD = 0.4 -EULER_X = 0 -EULER_Y = 1 -EULER_Z = 2 -ID_SCORE = 3 - -LANDMARK_X = 0 -LANDMARK_Y = 1 -LANDMARK_Z = 2 -LANDMARK_COUNT = 3 - -MAX_FACES = 64 -MIN_FACE_SIZE_PERCENT = 0.1 - -POINT_LEFT_EYE = 0 -POINT_RIGHT_EYE = 1 -POINT_NOSE = 2 -POINT_MOUTH = 3 - -FACE_LANDMARK_LEFT_EYE = 0 -FACE_LANDMARK_RIGHT_EYE = 1 -FACE_LANDMARK_NOSE_BRIDGE = 2 -FACE_LANDMARK_NOSE_BASE = 3 -FACE_LANDMARK_LEFT_EAR = 4 -FACE_LANDMARK_RIGHT_EAR = 5 -FACE_LANDMARK_LEFT_MOUTH = 6 -FACE_LANDMARK_RIGHT_MOUTH = 7 -FACE_LANDMARK_CHIN = 8 - - -class FaceLandmark: - """FaceLandmark — 人脸特征点""" - - def __init__(self, x: float = 0.0, y: float = 0.0, - z: float = 0.0, landmark_type: int = 0): - self.x = x - self.y = y - self.z = z - self.type = landmark_type - - def getX(self) -> float: - return self.x - - def getY(self) -> float: - return self.y - - def getZ(self) -> float: - return self.z - - def getType(self) -> int: - return self.type - - def toString(self) -> str: - return f"FaceLandmark(type={self.type}, x={self.x:.2f}, y={self.y:.2f}, z={self.z:.2f})" - - -class Face(Stub): - """Face — 检测到的人脸信息""" - - # 置信度常量 - CONFIDENCE_THRESHOLD = CONFIDENCE_THRESHOLD - - # 欧拉角常量 - EULER_X = EULER_X - EULER_Y = EULER_Y - EULER_Z = EULER_Z - - # ID 分数 - ID_SCORE = ID_SCORE - - # 特征点常量 - LANDMARK_X = LANDMARK_X - LANDMARK_Y = LANDMARK_Y - LANDMARK_Z = LANDMARK_Z - LANDMARK_COUNT = LANDMARK_COUNT - - # 人脸特征点类型 - POINT_LEFT_EYE = POINT_LEFT_EYE - POINT_RIGHT_EYE = POINT_RIGHT_EYE - POINT_NOSE = POINT_NOSE - POINT_MOUTH = POINT_MOUTH - - def __init__(self, confidence: float = 0.0, - midscore: float = 0.0, - euler_x: float = 0.0, euler_y: float = 0.0, euler_z: float = 0.0): - super().__init__() - self._confidence = max(0.0, min(1.0, confidence)) - self._midscore = midscore - self._euler_x = euler_x - self._euler_y = euler_y - self._euler_z = euler_z - self._landmarks: List[FaceLandmark] = [] - self._left_eye: Optional[Tuple[float, float]] = None - self._right_eye: Optional[Tuple[float, float]] = None - self._mouth: Optional[Tuple[float, float]] = None - self._id_score: float = 0.0 - self._face_id: int = -1 - self._bounds: Optional[Tuple[int, int, int, int]] = None - - def confidence(self) -> float: - """获取检测置信度 (0.0 - 1.0)""" - return self._confidence - - def getConfidence(self) -> float: - return self._confidence - - def getMidscore(self) -> float: - return self._midscore - - def eyesDistance(self) -> float: - """获取两眼之间的距离""" - if self._left_eye is None or self._right_eye is None: - return 0.0 - dx = self._left_eye[0] - self._right_eye[0] - dy = self._left_eye[1] - self._right_eye[1] - return math.sqrt(dx * dx + dy * dy) - - def pose(self, euler_type: int) -> float: - """获取人脸姿态欧拉角""" - if euler_type == EULER_X: - return self._euler_x - if euler_type == EULER_Y: - return self._euler_y - if euler_type == EULER_Z: - return self._euler_z - return 0.0 - - def getEulerX(self) -> float: - return self._euler_x - - def getEulerY(self) -> float: - return self._euler_y - - def getEulerZ(self) -> float: - return self._euler_z - - def getLeftEye(self) -> Optional[Tuple[float, float]]: - return self._left_eye - - def setLeftEye(self, x: float, y: float) -> None: - self._left_eye = (x, y) - - def getRightEye(self) -> Optional[Tuple[float, float]]: - return self._right_eye - - def setRightEye(self, x: float, y: float) -> None: - self._right_eye = (x, y) - - def getMouth(self) -> Optional[Tuple[float, float]]: - return self._mouth - - def setMouth(self, x: float, y: float) -> None: - self._mouth = (x, y) - - def getIdScore(self) -> float: - return self._id_score - - def setIdScore(self, score: float) -> None: - self._id_score = score - - def getFaceId(self) -> int: - return self._face_id - - def setFaceId(self, face_id: int) -> None: - self._face_id = face_id - - def getBounds(self) -> Optional[Tuple[int, int, int, int]]: - """获取人脸边界框 (left, top, right, bottom)""" - return self._bounds - - def setBounds(self, left: int, top: int, right: int, bottom: int) -> None: - self._bounds = (left, top, right, bottom) - - def getLandmarks(self) -> List[FaceLandmark]: - return list(self._landmarks) - - def getLandmarkCount(self) -> int: - return len(self._landmarks) - - def addLandmark(self, landmark: FaceLandmark) -> None: - self._landmarks.append(landmark) - - def toString(self) -> str: - return (f"Face(confidence={self._confidence:.3f}, " - f"euler=({self._euler_x:.1f},{self._euler_y:.1f},{self._euler_z:.1f}), " - f"landmarks={len(self._landmarks)})") - - -class FaceDetector(Stub): - """FaceDetector — 人脸检测器""" - - MAX_FACES = MAX_FACES - MIN_FACE_SIZE_PERCENT = MIN_FACE_SIZE_PERCENT - - # 人脸特征点类型 - POINT_LEFT_EYE = POINT_LEFT_EYE - POINT_RIGHT_EYE = POINT_RIGHT_EYE - POINT_NOSE = POINT_NOSE - POINT_MOUTH = POINT_MOUTH - - class Face(Face): - """FaceDetector.Face — 内部人脸类,继承自 Face""" - - def __init__(self, confidence: float = 0.0, - midscore: float = 0.0, - euler_x: float = 0.0, euler_y: float = 0.0, euler_z: float = 0.0): - super().__init__(confidence, midscore, euler_x, euler_y, euler_z) - - def runDetector(self, detector: "FaceDetector") -> bool: - """运行检测器以填充此 Face 的数据""" - return True - - @staticmethod - def getFaceFromArray(faces: List["Face"], index: int) -> Optional["Face"]: - if 0 <= index < len(faces): - return faces[index] - return None - - def __init__(self, width: int = 0, height: int = 0, max_faces: int = MAX_FACES): - super().__init__() - self._width = width - self._height = height - self._max_faces = min(max_faces, MAX_FACES) - self._detected_faces: List[Face] = [] - self._detection_lock = threading.Lock() - - def findFaces(self, bitmap: Any, faces: List[Face]) -> int: - """在位图中检测人脸并填充 faces 数组 - - Args: - bitmap: 位图对象(需要有 getPixels 方法) - faces: 预分配的 Face 数组 - - Returns: - 检测到的人脸数量 - """ - with self._detection_lock: - self._detected_faces.clear() - detected_count = self._run_detection(bitmap) - for i in range(min(detected_count, len(faces))): - faces[i] = self._detected_faces[i] - return min(detected_count, len(faces)) - - def _run_detection(self, bitmap: Any) -> int: - """内部检测实现 - - 桌面环境下返回模拟检测结果。 - """ - return 0 - - def getDetectedFaces(self) -> List[Face]: - """获取检测到的人脸列表""" - with self._detection_lock: - return list(self._detected_faces) - - def getFaceCount(self) -> int: - """获取检测到的人脸数量""" - with self._detection_lock: - return len(self._detected_faces) - - def setBitmapSize(self, width: int, height: int) -> None: - """设置位图尺寸""" - self._width = width - self._height = height - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def getMaxFaces(self) -> int: - return self._max_faces - - @staticmethod - def isFaceDetectionSupported() -> bool: - """检查是否支持人脸检测""" - return True - - def toString(self) -> str: - return (f"FaceDetector(width={self._width}, height={self._height}, " - f"max_faces={self._max_faces})") - - -class FaceDetectorBuilder(Stub): - """FaceDetectorBuilder — 人脸检测器构建器""" - - def __init__(self): - super().__init__() - self._width = 0 - self._height = 0 - self._max_faces = MAX_FACES - self._min_face_size = MIN_FACE_SIZE_PERCENT - self._fast_mode = False - - def setWidth(self, width: int) -> "FaceDetectorBuilder": - self._width = width - return self - - def setHeight(self, height: int) -> "FaceDetectorBuilder": - self._height = height - return self - - def setMaxFaces(self, max_faces: int) -> "FaceDetectorBuilder": - self._max_faces = min(max_faces, MAX_FACES) - return self - - def setMinFaceSize(self, size_percent: float) -> "FaceDetectorBuilder": - self._min_face_size = max(0.01, min(1.0, size_percent)) - return self - - def setFastMode(self, fast: bool) -> "FaceDetectorBuilder": - self._fast_mode = fast - return self - - def build(self) -> FaceDetector: - detector = FaceDetector(self._width, self._height, self._max_faces) - return detector - - def toString(self) -> str: - return (f"FaceDetectorBuilder(width={self._width}, height={self._height}, " - f"max_faces={self._max_faces})") - - -class FaceInfo(Stub): - """FaceInfo — 人脸详细信息""" - - def __init__(self, face: Optional[Face] = None): - super().__init__() - self._face = face - self._smile_score: float = 0.0 - self._left_eye_open_score: float = 0.0 - self._right_eye_open_score: float = 0.0 - self._blink_score: float = 0.0 - self._face_landmarks: Dict[int, Tuple[float, float, float]] = {} - - def getSmileScore(self) -> float: - return self._smile_score - - def setSmileScore(self, score: float) -> None: - self._smile_score = max(0.0, min(1.0, score)) - - def getLeftEyeOpenScore(self) -> float: - return self._left_eye_open_score - - def setLeftEyeOpenScore(self, score: float) -> None: - self._left_eye_open_score = max(0.0, min(1.0, score)) - - def getRightEyeOpenScore(self) -> float: - return self._right_eye_open_score - - def setRightEyeOpenScore(self, score: float) -> None: - self._right_eye_open_score = max(0.0, min(1.0, score)) - - def getBlinkScore(self) -> float: - return self._blink_score - - def setBlinkScore(self, score: float) -> None: - self._blink_score = max(0.0, min(1.0, score)) - - def toString(self) -> str: - return (f"FaceInfo(smile={self._smile_score:.2f}, " - f"blink={self._blink_score:.2f})") - - -class FaceTracker(Stub): - """FaceTracker — 人脸追踪器""" - - def __init__(self, max_faces: int = MAX_FACES): - super().__init__() - self._max_faces = max_faces - self._tracked_faces: Dict[int, Face] = {} - self._next_id = 1 - self._lock = threading.Lock() - - def track(self, detected_faces: List[Face]) -> Dict[int, Face]: - """追踪检测到的人脸,返回 id->Face 映射""" - with self._lock: - result: Dict[int, Face] = {} - for face in detected_faces: - face_id = self._assign_id(face) - result[face_id] = face - return result - - def _assign_id(self, face: Face) -> int: - """为新检测到的人脸分配 ID,或匹配已有 ID""" - best_id = self._next_id - for fid, tracked in self._tracked_faces.items(): - if self._match_faces(tracked, face): - best_id = fid - break - if best_id == self._next_id: - self._next_id += 1 - self._tracked_faces[best_id] = face - return best_id - - def _match_faces(self, tracked: Face, detected: Face) -> bool: - """判断两帧中的人脸是否为同一人""" - return False - - def reset(self) -> None: - """重置追踪器""" - with self._lock: - self._tracked_faces.clear() - self._next_id = 1 - - def getTrackedFaces(self) -> Dict[int, Face]: - with self._lock: - return dict(self._tracked_faces) - - def toString(self) -> str: - return f"FaceTracker(tracked={len(self._tracked_faces)})" - - -class FaceDetectorOptions(Stub): - """FaceDetectorOptions — 人脸检测器选项(MLKit兼容)""" - - PERFORMANCE_MODE_FAST = 1 - PERFORMANCE_MODE_ACCURATE = 2 - LANDMARK_MODE_NONE = 0 - LANDMARK_MODE_ALL = 1 - CONTOUR_MODE_NONE = 0 - CONTOUR_MODE_ALL = 1 - CLASSIFICATION_MODE_NONE = 0 - CLASSIFICATION_MODE_ALL = 1 - - def __init__(self): - super().__init__() - self._performance_mode = self.PERFORMANCE_MODE_FAST - self._landmark_mode = self.LANDMARK_MODE_NONE - self._contour_mode = self.CONTOUR_MODE_NONE - self._classification_mode = self.CLASSIFICATION_MODE_NONE - self._min_face_size = 0.1 - self._enable_tracking = False - - def setPerformanceMode(self, mode: int) -> "FaceDetectorOptions": - self._performance_mode = mode - return self - - def getPerformanceMode(self) -> int: - return self._performance_mode - - def setLandmarkMode(self, mode: int) -> "FaceDetectorOptions": - self._landmark_mode = mode - return self - - def getLandmarkMode(self) -> int: - return self._landmark_mode - - def setContourMode(self, mode: int) -> "FaceDetectorOptions": - self._contour_mode = mode - return self - - def getContourMode(self) -> int: - return self._contour_mode - - def setClassificationMode(self, mode: int) -> "FaceDetectorOptions": - self._classification_mode = mode - return self - - def getClassificationMode(self) -> int: - return self._classification_mode - - def setMinFaceSize(self, size: float) -> "FaceDetectorOptions": - self._min_face_size = max(0.01, min(1.0, size)) - return self - - def getMinFaceSize(self) -> float: - return self._min_face_size - - def enableTracking(self, enable: bool) -> "FaceDetectorOptions": - self._enable_tracking = enable - return self - - def isTrackingEnabled(self) -> bool: - return self._enable_tracking - - def toString(self) -> str: - return (f"FaceDetectorOptions(performance={self._performance_mode}, " - f"landmarks={self._landmark_mode})") \ No newline at end of file diff --git a/android_pyapi/android/media_image.py b/android_pyapi/android/media_image.py deleted file mode 100644 index a39b932..0000000 --- a/android_pyapi/android/media_image.py +++ /dev/null @@ -1,649 +0,0 @@ -""" -Android media.image 组件 — Image, ImageReader, ImageWriter 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import io -import math -import time -import struct -import threading -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# Image 格式常量 -# ═══════════════════════════════════════════════════════════════ - -FORMAT_NV21 = 17 -FORMAT_NV16 = 16 -FORMAT_YUY2 = 20 -FORMAT_JPEG = 256 -FORMAT_RAW_PRIVATE = 36 -FORMAT_RAW_SENSOR = 32 -FORMAT_RAW10 = 37 -FORMAT_RAW12 = 38 -FORMAT_RAW_DEPTH = 4098 -FORMAT_RAW_DEPTH10 = 4099 -FORMAT_YUV_420_888 = 35 -FORMAT_YUV_422_888 = 39 -FORMAT_YUV_444_888 = 40 -FORMAT_FLEX_RGB_888 = 41 -FORMAT_FLEX_RGBA_8888 = 42 -FORMAT_PRIVATE = 34 -FORMAT_UNKNOWN = 0 -FORMAT_RGB_565 = 4 -FORMAT_RGBA_8888 = 1 -FORMAT_RGBX_8888 = 2 -FORMAT_BGRA_8888 = 5 -FORMAT_Y8 = 538982489 -FORMAT_Y16 = 540422489 -FORMAT_DEPTH16 = 1144402265 -FORMAT_DEPTH_POINT_CLOUD = 257 -FORMAT_HEIC = 1212500294 - -TRANSFORM_NONE = 0 -TRANSFORM_FLIP_H = 1 -TRANSFORM_FLIP_V = 2 -TRANSFORM_ROTATE_90 = 4 -TRANSFORM_ROTATE_180 = 3 -TRANSFORM_ROTATE_270 = 7 - -SCALER_ROTATE_90 = 1 -SCALER_ROTATE_180 = 2 -SCALER_ROTATE_270 = 3 - - -class Image(Stub): - """Image — 图像数据容器""" - - class Plane(Stub): - """Image.Plane — 图像平面(YUV 各个平面)""" - - def __init__(self, buffer: Optional[bytes] = None, - row_stride: int = 0, pixel_stride: int = 0): - super().__init__() - self._buffer = buffer or b"" - self._row_stride = row_stride - self._pixel_stride = pixel_stride - - def getBuffer(self) -> Optional[bytes]: - return self._buffer - - def getRowStride(self) -> int: - return self._row_stride - - def getPixelStride(self) -> int: - return self._pixel_stride - - def setBuffer(self, buf: bytes) -> None: - self._buffer = buf - - def setRowStride(self, stride: int) -> None: - self._row_stride = stride - - def setPixelStride(self, stride: int) -> None: - self._pixel_stride = stride - - def toString(self) -> str: - return (f"Plane(rowStride={self._row_stride}, " - f"pixelStride={self._pixel_stride}, " - f"bufferSize={len(self._buffer) if self._buffer else 0})") - - # Image 格式常量 - FORMAT_NV21 = FORMAT_NV21 - FORMAT_NV16 = FORMAT_NV16 - FORMAT_YUY2 = FORMAT_YUY2 - FORMAT_JPEG = FORMAT_JPEG - FORMAT_RAW_PRIVATE = FORMAT_RAW_PRIVATE - FORMAT_RAW_SENSOR = FORMAT_RAW_SENSOR - FORMAT_RAW10 = FORMAT_RAW10 - FORMAT_RAW12 = FORMAT_RAW12 - FORMAT_RAW_DEPTH = FORMAT_RAW_DEPTH - FORMAT_RAW_DEPTH10 = FORMAT_RAW_DEPTH10 - FORMAT_YUV_420_888 = FORMAT_YUV_420_888 - FORMAT_YUV_422_888 = FORMAT_YUV_422_888 - FORMAT_YUV_444_888 = FORMAT_YUV_444_888 - FORMAT_FLEX_RGB_888 = FORMAT_FLEX_RGB_888 - FORMAT_FLEX_RGBA_8888 = FORMAT_FLEX_RGBA_8888 - FORMAT_PRIVATE = FORMAT_PRIVATE - FORMAT_UNKNOWN = FORMAT_UNKNOWN - FORMAT_RGB_565 = FORMAT_RGB_565 - FORMAT_RGBA_8888 = FORMAT_RGBA_8888 - FORMAT_RGBX_8888 = FORMAT_RGBX_8888 - FORMAT_BGRA_8888 = FORMAT_BGRA_8888 - FORMAT_Y8 = FORMAT_Y8 - FORMAT_Y16 = FORMAT_Y16 - FORMAT_DEPTH16 = FORMAT_DEPTH16 - FORMAT_DEPTH_POINT_CLOUD = FORMAT_DEPTH_POINT_CLOUD - FORMAT_HEIC = FORMAT_HEIC - - def __init__(self, width: int = 0, height: int = 0, - format: int = FORMAT_UNKNOWN, timestamp: int = 0): - super().__init__() - self._width = width - self._height = height - self._format = format - self._timestamp = timestamp - self._planes: List[Image.Plane] = [] - self._crop: Optional[Tuple[int, int, int, int]] = None - self._fence: Optional[Any] = None - self._hardware_buffer: Optional[Any] = None - self._released = False - self._owner: Optional[Any] = None - self._source: Optional[Any] = None - self._lock = threading.Lock() - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def getFormat(self) -> int: - return self._format - - def getTimestamp(self) -> int: - return self._timestamp - - def setTimestamp(self, timestamp: int) -> None: - self._timestamp = timestamp - - def getPlanes(self) -> List[Plane]: - return self._planes - - def getCropRect(self) -> Optional[Tuple[int, int, int, int]]: - return self._crop - - def setCropRect(self, crop: Tuple[int, int, int, int]) -> None: - self._crop = crop - - def getFence(self) -> Optional[Any]: - return self._fence - - def setFence(self, fence: Any) -> None: - self._fence = fence - - def getHardwareBuffer(self) -> Optional[Any]: - return self._hardware_buffer - - def close(self) -> None: - with self._lock: - if self._released: - return - self._released = True - self._planes.clear() - if self._source: - self._source._notify_image_closed(self) - - def isClosed(self) -> bool: - return self._released - - def toString(self) -> str: - return (f"Image(width={self._width}, height={self._height}, " - f"format={self._format}, planes={len(self._planes)}, " - f"timestamp={self._timestamp})") - - -# ═══════════════════════════════════════════════════════════════ -# ImageReader -# ═══════════════════════════════════════════════════════════════ - -class ImageReader(Stub): - """ImageReader — 图像读取器""" - - def __init__(self, width: int, height: int, format: int, - max_images: int = 1, usage: int = 0): - super().__init__() - self._width = width - self._height = height - self._format = format - self._max_images = max_images - self._usage = usage - self._images: List[Image] = [] - self._available_images: List[Image] = [] - self._on_image_available: Optional[Callable[[ImageReader], None]] = None - self._lock = threading.Lock() - self._released = False - self._surface: Optional[Any] = None - - @staticmethod - def newInstance(width: int, height: int, format: int, - max_images: int = 1, usage: int = 0) -> "ImageReader": - return ImageReader(width, height, format, max_images, usage) - - def acquireLatestImage(self) -> Optional[Image]: - with self._lock: - if not self._available_images: - return None - latest = self._available_images[-1] - self._available_images.clear() - return latest - - def acquireNextImage(self) -> Optional[Image]: - with self._lock: - if not self._available_images: - return None - return self._available_images.pop(0) - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def getFormat(self) -> int: - return self._format - - def getMaxImages(self) -> int: - return self._max_images - - def getUsage(self) -> int: - return self._usage - - def getSurface(self) -> Optional[Any]: - return self._surface - - def setOnImageAvailableListener(self, listener: Optional[Callable], - handler: Optional[Any] = None) -> None: - self._on_image_available = listener - - def getOnImageAvailableListener(self) -> Optional[Callable]: - return self._on_image_available - - def close(self) -> None: - with self._lock: - if self._released: - return - self._released = True - for img in self._images: - img.close() - self._images.clear() - self._available_images.clear() - - def isClosed(self) -> bool: - return self._released - - def _notify_image_closed(self, image: Image) -> None: - with self._lock: - if image in self._images: - self._images.remove(image) - - def _add_image(self, image: Image) -> None: - with self._lock: - image._source = self - self._images.append(image) - self._available_images.append(image) - if self._on_image_available: - self._on_image_available(self) - - def toString(self) -> str: - return (f"ImageReader(width={self._width}, height={self._height}, " - f"format={self._format}, maxImages={self._max_images})") - - -# ═══════════════════════════════════════════════════════════════ -# ImageWriter -# ═══════════════════════════════════════════════════════════════ - -class ImageWriter(Stub): - """ImageWriter — 图像写入器""" - - def __init__(self, surface: Any, max_images: int = 1): - super().__init__() - self._surface = surface - self._max_images = max_images - self._queued_images: List[Image] = [] - self._on_image_released: Optional[Callable] = None - self._lock = threading.Lock() - self._released = False - - @staticmethod - def newInstance(surface: Any, max_images: int = 1) -> "ImageWriter": - return ImageWriter(surface, max_images) - - def getSurface(self) -> Optional[Any]: - return self._surface - - def getMaxImages(self) -> int: - return self._max_images - - def queueInputImage(self, image: Image) -> None: - with self._lock: - if self._released: - raise RuntimeException("ImageWriter 已关闭") - self._queued_images.append(image) - if self._on_image_released: - self._on_image_released(self) - - def setOnImageReleasedListener(self, listener: Optional[Callable], - handler: Optional[Any] = None) -> None: - self._on_image_released = listener - - def getOnImageReleasedListener(self) -> Optional[Callable]: - return self._on_image_released - - def close(self) -> None: - with self._lock: - if self._released: - return - self._released = True - self._queued_images.clear() - - def isClosed(self) -> bool: - return self._released - - def toString(self) -> str: - return (f"ImageWriter(maxImages={self._max_images}, " - f"queued={len(self._queued_images)})") - - -# ═══════════════════════════════════════════════════════════════ -# MediaImage -# ═══════════════════════════════════════════════════════════════ - -class MediaImage(Image): - """MediaImage — 媒体图像(扩展 Image)""" - - def __init__(self, width: int = 0, height: int = 0, - format: int = FORMAT_UNKNOWN, timestamp: int = 0, - sequence_number: int = 0): - super().__init__(width, height, format, timestamp) - self._sequence_number = sequence_number - self._metadata: Dict[str, Any] = {} - self._transform: int = TRANSFORM_NONE - - def getSequenceNumber(self) -> int: - return self._sequence_number - - def setSequenceNumber(self, seq: int) -> None: - self._sequence_number = seq - - def getTransform(self) -> int: - return self._transform - - def setTransform(self, transform: int) -> None: - self._transform = transform - - def getMetadata(self, key: str) -> Any: - return self._metadata.get(key) - - def setMetadata(self, key: str, value: Any) -> None: - self._metadata[key] = value - - def getAllMetadata(self) -> Dict[str, Any]: - return dict(self._metadata) - - def toString(self) -> str: - return (f"MediaImage(width={self.getWidth()}, height={self.getHeight()}, " - f"format={self.getFormat()}, seq={self._sequence_number})") - - -# ═══════════════════════════════════════════════════════════════ -# SurfaceImage / ImageTransform -# ═══════════════════════════════════════════════════════════════ - -class SurfaceImage(Stub): - """SurfaceImage — 表面图像(用于 SurfaceView/TextureView 捕获)""" - - def __init__(self): - super().__init__() - self._image: Optional[Image] = None - self._transform: int = TRANSFORM_NONE - self._timestamp: int = 0 - self._lock = threading.Lock() - - def getImage(self) -> Optional[Image]: - with self._lock: - return self._image - - def setImage(self, image: Image) -> None: - with self._lock: - self._image = image - self._timestamp = image.getTimestamp() - - def getTransform(self) -> int: - return self._transform - - def setTransform(self, transform: int) -> None: - self._transform = transform - - def getTimestamp(self) -> int: - return self._timestamp - - def release(self) -> None: - with self._lock: - if self._image: - self._image.close() - self._image = None - - -class ImageTransform(Stub): - """ImageTransform — 图像变换""" - - SCALER_ROTATE_90 = SCALER_ROTATE_90 - SCALER_ROTATE_180 = SCALER_ROTATE_180 - SCALER_ROTATE_270 = SCALER_ROTATE_270 - TRANSFORM_NONE = TRANSFORM_NONE - TRANSFORM_FLIP_H = TRANSFORM_FLIP_H - TRANSFORM_FLIP_V = TRANSFORM_FLIP_V - TRANSFORM_ROTATE_90 = TRANSFORM_ROTATE_90 - TRANSFORM_ROTATE_180 = TRANSFORM_ROTATE_180 - TRANSFORM_ROTATE_270 = TRANSFORM_ROTATE_270 - - def __init__(self, rotation: int = 0, flip_h: bool = False, flip_v: bool = False): - super().__init__() - self._rotation = rotation - self._flip_h = flip_h - self._flip_v = flip_v - - def getRotation(self) -> int: - return self._rotation - - def setRotation(self, rotation: int) -> None: - self._rotation = rotation - - def isFlippedHorizontally(self) -> bool: - return self._flip_h - - def setFlipHorizontal(self, flip: bool) -> None: - self._flip_h = flip - - def isFlippedVertically(self) -> bool: - return self._flip_v - - def setFlipVertical(self, flip: bool) -> None: - self._flip_v = flip - - @staticmethod - def fromTransformBits(transform: int) -> "ImageTransform": - flip_h = bool(transform & TRANSFORM_FLIP_H) - flip_v = bool(transform & TRANSFORM_FLIP_V) - rotation = 0 - if transform & TRANSFORM_ROTATE_90: - rotation = 90 - elif transform & TRANSFORM_ROTATE_180: - rotation = 180 - elif transform & TRANSFORM_ROTATE_270: - rotation = 270 - return ImageTransform(rotation, flip_h, flip_v) - - def getTransformBits(self) -> int: - bits = 0 - if self._rotation == 90: - bits |= TRANSFORM_ROTATE_90 - elif self._rotation == 180: - bits |= TRANSFORM_ROTATE_180 - elif self._rotation == 270: - bits |= TRANSFORM_ROTATE_270 - if self._flip_h: - bits |= TRANSFORM_FLIP_H - if self._flip_v: - bits |= TRANSFORM_FLIP_V - return bits - - def toString(self) -> str: - return (f"ImageTransform(rotation={self._rotation}, " - f"flipH={self._flip_h}, flipV={self._flip_v})") - - -class ImageConfig(Stub): - """ImageConfig — 图像配置""" - - def __init__(self, width: int = 0, height: int = 0, - format: int = FORMAT_UNKNOWN, usage: int = 0): - super().__init__() - self._width = width - self._height = height - self._format = format - self._usage = usage - - def getWidth(self) -> int: - return self._width - - def setWidth(self, width: int) -> None: - self._width = width - - def getHeight(self) -> int: - return self._height - - def setHeight(self, height: int) -> None: - self._height = height - - def getFormat(self) -> int: - return self._format - - def setFormat(self, fmt: int) -> None: - self._format = fmt - - def getUsage(self) -> int: - return self._usage - - def setUsage(self, usage: int) -> None: - self._usage = usage - - def toString(self) -> str: - return (f"ImageConfig(width={self._width}, height={self._height}, " - f"format={self._format})") - - -class ImagePool(Stub): - """ImagePool — 图像池""" - - def __init__(self, max_images: int = 4): - super().__init__() - self._max_images = max_images - self._pool: List[Image] = [] - self._lock = threading.Lock() - - def acquireImage(self, width: int, height: int, format: int) -> Optional[Image]: - with self._lock: - for img in self._pool: - if (img.getWidth() == width and img.getHeight() == height - and img.getFormat() == format and not img.isClosed()): - self._pool.remove(img) - return img - if len(self._pool) < self._max_images: - img = Image(width, height, format) - return img - return None - - def releaseImage(self, image: Image) -> None: - with self._lock: - if len(self._pool) < self._max_images and not image.isClosed(): - self._pool.append(image) - - def close(self) -> None: - with self._lock: - for img in self._pool: - img.close() - self._pool.clear() - - def getAvailableCount(self) -> int: - with self._lock: - return len(self._pool) - - def getMaxImages(self) -> int: - return self._max_images - - def toString(self) -> str: - return f"ImagePool(max={self._max_images}, available={len(self._pool)})" - - -class ImageUtils(Stub): - """ImageUtils — 图像工具类""" - - @staticmethod - def getFormatName(format: int) -> str: - names = { - FORMAT_NV21: "NV21", - FORMAT_NV16: "NV16", - FORMAT_YUY2: "YUY2", - FORMAT_JPEG: "JPEG", - FORMAT_RAW_PRIVATE: "RAW_PRIVATE", - FORMAT_RAW_SENSOR: "RAW_SENSOR", - FORMAT_RAW10: "RAW10", - FORMAT_RAW12: "RAW12", - FORMAT_RAW_DEPTH: "RAW_DEPTH", - FORMAT_YUV_420_888: "YUV_420_888", - FORMAT_FLEX_RGB_888: "FLEX_RGB_888", - FORMAT_FLEX_RGBA_8888: "FLEX_RGBA_8888", - FORMAT_PRIVATE: "PRIVATE", - FORMAT_RGB_565: "RGB_565", - FORMAT_RGBA_8888: "RGBA_8888", - FORMAT_BGRA_8888: "BGRA_8888", - FORMAT_DEPTH16: "DEPTH16", - FORMAT_DEPTH_POINT_CLOUD: "DEPTH_POINT_CLOUD", - FORMAT_HEIC: "HEIC", - } - return names.get(format, f"UNKNOWN({format})") - - @staticmethod - def isYuvFormat(format: int) -> bool: - return format in (FORMAT_NV21, FORMAT_NV16, FORMAT_YUY2, - FORMAT_YUV_420_888, FORMAT_YUV_422_888, FORMAT_YUV_444_888) - - @staticmethod - def getPlaneCount(format: int) -> int: - if format in (FORMAT_NV21, FORMAT_NV16): - return 2 - if format in (FORMAT_YUV_420_888, FORMAT_YUV_422_888, FORMAT_YUV_444_888): - return 3 - if format in (FORMAT_RGBA_8888, FORMAT_RGBX_8888, FORMAT_BGRA_8888, - FORMAT_RGB_565, FORMAT_JPEG): - return 1 - if format == FORMAT_PRIVATE: - return 1 - return 1 - - @staticmethod - def getSizeInBytes(width: int, height: int, format: int) -> int: - bpp = { - FORMAT_RGBA_8888: 4, - FORMAT_RGBX_8888: 4, - FORMAT_BGRA_8888: 4, - FORMAT_RGB_565: 2, - FORMAT_YUY2: 2, - } - if format == FORMAT_JPEG: - return width * height * 3 - if format == FORMAT_NV21: - return width * height * 3 // 2 - if format == FORMAT_NV16: - return width * height * 2 - if format == FORMAT_YUV_420_888: - return width * height * 3 // 2 - if format in bpp: - return width * height * bpp[format] - return width * height * 4 - - @staticmethod - def isDepthFormat(format: int) -> bool: - return format in (FORMAT_DEPTH16, FORMAT_DEPTH_POINT_CLOUD, - FORMAT_RAW_DEPTH, FORMAT_RAW_DEPTH10) \ No newline at end of file diff --git a/android_pyapi/android/media_midi.py b/android_pyapi/android/media_midi.py deleted file mode 100644 index ad911af..0000000 --- a/android_pyapi/android/media_midi.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -Android media.midi 组件 — MidiManager, MidiDevice, MidiReceiver, MidiSender 等 -方法签名完全对齐 Android SDK -""" - -from typing import Any, Callable, Dict, List, Optional, Set - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# MidiManager -# ═══════════════════════════════════════════════════════════════ - -class MidiManager(Stub): - """MidiManager — MIDI 管理器""" - - def __init__(self): - super().__init__() - self._devices: Dict[int, MidiDevice] = {} - self._callbacks: Dict[int, Callable] = {} - self._next_device_id = 1 - self._init_default_devices() - - def _init_default_devices(self): - info = MidiDeviceInfo( - id=0, inputPortCount=1, outputPortCount=1, - type=MidiDeviceInfo.TYPE_VIRTUAL, - properties={MidiDeviceInfo.PROPERTY_NAME: "Virtual MIDI"} - ) - device = MidiDevice(info) - self._devices[0] = device - - def getDevices(self) -> List["MidiDeviceInfo"]: - return [d.getInfo() for d in self._devices.values()] - - def registerDeviceCallback(self, callback: Callable, handler: Any = None): - cid = id(callback) - self._callbacks[cid] = callback - - def unregisterDeviceCallback(self, callback: Callable): - cid = id(callback) - self._callbacks.pop(cid, None) - - def openDevice(self, deviceInfo: "MidiDeviceInfo", - callback: Callable, handler: Any = None): - device = self._devices.get(deviceInfo.getId()) - if device: - callback(device) - - def openBluetoothDevice(self, bluetoothDevice: Any) -> Optional["MidiDevice"]: - return None - - def getDefaultDevice(self) -> Optional["MidiDevice"]: - for d in self._devices.values(): - return d - return None - - def deviceConnected(self, info: "MidiDeviceInfo"): - pass - - def deviceDisconnected(self, info: "MidiDeviceInfo"): - pass - - -# ═══════════════════════════════════════════════════════════════ -# MidiDevice -# ═══════════════════════════════════════════════════════════════ - -class MidiDevice(Stub): - """MidiDevice — MIDI 设备""" - - # ═══════════════════════════════════════════════════════════ - # MidiConnection - # ═══════════════════════════════════════════════════════════ - - class MidiConnection(Stub): - """MidiDevice.MidiConnection — MIDI 连接""" - - def __init__(self): - super().__init__() - - def close(self): - pass - - def toString(self) -> str: - return "MidiConnection" - - def __init__(self, info: "MidiDeviceInfo" = None): - super().__init__() - self._info = info or MidiDeviceInfo() - self._input_ports: List[MidiInputPort] = [] - self._output_ports: List[MidiOutputPort] = [] - self._closed = False - - def getInfo(self) -> "MidiDeviceInfo": - return self._info - - def openInputPort(self, portNumber: int) -> Optional["MidiInputPort"]: - if self._closed: - return None - port = MidiInputPort(portNumber) - self._input_ports.append(port) - return port - - def openOutputPort(self, portNumber: int) -> Optional["MidiOutputPort"]: - if self._closed: - return None - port = MidiOutputPort(portNumber) - self._output_ports.append(port) - return port - - def close(self): - self._closed = True - - def toString(self) -> str: - return f"MidiDevice(info={self._info})" - - -# ═══════════════════════════════════════════════════════════════ -# MidiDeviceInfo -# ═══════════════════════════════════════════════════════════════ - -class MidiDeviceInfo(Stub): - """MidiDeviceInfo — MIDI 设备信息""" - - TYPE_USB = 1 - TYPE_BLUETOOTH = 2 - TYPE_VIRTUAL = 3 - - PROPERTY_ALSA_CARD = "alsa_card" - PROPERTY_ALSA_DEVICE = "alsa_device" - PROPERTY_MANUFACTURER = "manufacturer" - PROPERTY_NAME = "name" - PROPERTY_PRODUCT = "product" - PROPERTY_SERIAL_NUMBER = "serial_number" - PROPERTY_USB_DEVICE = "usb_device" - - def __init__(self, id: int = 0, inputPortCount: int = 0, - outputPortCount: int = 0, type_: int = TYPE_VIRTUAL, - properties: Optional[Dict[str, str]] = None): - super().__init__() - self._id = id - self._input_port_count = inputPortCount - self._output_port_count = outputPortCount - self._type = type_ - self._properties = properties or {} - - def getId(self) -> int: - return self._id - - def getProperties(self) -> Dict[str, str]: - return dict(self._properties) - - def getInputPortCount(self) -> int: - return self._input_port_count - - def getOutputPortCount(self) -> int: - return self._output_port_count - - def getType(self) -> int: - return self._type - - def equals(self, other: Any) -> bool: - if not isinstance(other, MidiDeviceInfo): - return False - return self._id == other._id - - def hashCode(self) -> int: - return hash(self._id) & 0x7FFFFFFF - - def toString(self) -> str: - return f"MidiDeviceInfo(id={self._id}, type={self._type}," \ - f" name={self._properties.get(MidiDeviceInfo.PROPERTY_NAME, '')})" - - -# ═══════════════════════════════════════════════════════════════ -# MidiReceiver -# ═══════════════════════════════════════════════════════════════ - -class MidiReceiver(Stub): - """MidiReceiver — MIDI 接收器""" - - def __init__(self, maxMessageSize: int = 1024): - super().__init__() - self._max_message_size = maxMessageSize - - def send(self, msg: bytes, offset: int, count: int): - self.onSend(msg, offset, count) - - def onSend(self, msg: bytes, offset: int, count: int): - pass - - def flush(self): - pass - - def getMaxMessageSize(self) -> int: - return self._max_message_size - - -# ═══════════════════════════════════════════════════════════════ -# MidiSender -# ═══════════════════════════════════════════════════════════════ - -class MidiSender(Stub): - """MidiSender — MIDI 发送器""" - - def __init__(self): - super().__init__() - self._receivers: List[MidiReceiver] = [] - - def send(self, msg: bytes, offset: int, count: int): - for r in self._receivers: - r.send(msg, offset, count) - - def onConnect(self, receiver: MidiReceiver): - pass - - def onDisconnect(self, receiver: MidiReceiver): - pass - - def connect(self, receiver: MidiReceiver): - self._receivers.append(receiver) - self.onConnect(receiver) - - def disconnect(self, receiver: MidiReceiver): - if receiver in self._receivers: - self._receivers.remove(receiver) - self.onDisconnect(receiver) - - -# ═══════════════════════════════════════════════════════════════ -# MidiInputPort -# ═══════════════════════════════════════════════════════════════ - -class MidiInputPort(MidiReceiver): - """MidiInputPort — MIDI 输入端口""" - - def __init__(self, portNumber: int = 0): - super().__init__() - self._port_number = portNumber - self._closed = False - - def send(self, msg: bytes, offset: int, count: int): - if not self._closed: - self.onSend(msg, offset, count) - - def onSend(self, msg: bytes, offset: int, count: int): - pass - - def flush(self): - pass - - def close(self): - self._closed = True - - -# ═══════════════════════════════════════════════════════════════ -# MidiOutputPort -# ═══════════════════════════════════════════════════════════════ - -class MidiOutputPort(MidiSender): - """MidiOutputPort — MIDI 输出端口""" - - def __init__(self, portNumber: int = 0): - super().__init__() - self._port_number = portNumber - - def send(self, msg: bytes, offset: int, count: int): - super().send(msg, offset, count) - - def onSend(self, msg: bytes, offset: int, count: int): - pass - - def getPortNumber(self) -> int: - return self._port_number - - -# ═══════════════════════════════════════════════════════════════ -# MidiEndpoint (stub) -# ═══════════════════════════════════════════════════════════════ - -class MidiEndpoint(Stub): - """MidiEndpoint — MIDI 端点""" - - def __init__(self): - super().__init__() - - -# ═══════════════════════════════════════════════════════════════ -# MidiDeviceService -# ═══════════════════════════════════════════════════════════════ - -class MidiDeviceService(Stub): - """MidiDeviceService — MIDI 设备服务""" - - def __init__(self): - super().__init__() - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "MidiManager", - "MidiDevice", - "MidiDeviceInfo", - "MidiReceiver", - "MidiSender", - "MidiInputPort", - "MidiOutputPort", - "MidiEndpoint", - "MidiDeviceService", -] \ No newline at end of file diff --git a/android_pyapi/android/media_projection.py b/android_pyapi/android/media_projection.py deleted file mode 100644 index a7b0794..0000000 --- a/android_pyapi/android/media_projection.py +++ /dev/null @@ -1,171 +0,0 @@ -""" -Android media.projection 组件 — MediaProjectionManager, MediaProjection, MediaProjectionInfo 等 -方法签名完全对齐 Android SDK -""" - -from typing import Any, Callable, Dict, List, Optional - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# MediaProjectionManager -# ═══════════════════════════════════════════════════════════════ - -class MediaProjectionManager(Stub): - """MediaProjectionManager — 媒体投影管理器""" - - def __init__(self): - super().__init__() - self._active_projections: Dict[int, "MediaProjection"] = {} - self._callbacks: Dict[int, Callable] = {} - - def createScreenCaptureIntent(self) -> Any: - return "SCREEN_CAPTURE_INTENT" - - def getMediaProjection(self, resultCode: int, data: Any) -> Optional["MediaProjection"]: - if resultCode == -1: # RESULT_OK - proj = MediaProjection() - self._active_projections[id(proj)] = proj - return proj - return None - - def getActiveProjectionInfo(self) -> Optional["MediaProjectionInfo"]: - for proj in self._active_projections.values(): - info = proj.getProjectionInfo() - if info: - return info - return None - - def getDisplayProjection(self, displayId: int) -> Optional["MediaProjection"]: - return None - - def addCallback(self, callback: Callable, handler: Any = None): - cid = id(callback) - self._callbacks[cid] = callback - - def removeCallback(self, callback: Callable): - cid = id(callback) - self._callbacks.pop(cid, None) - - def isMediaProjectionAllowed(self, packageName: str) -> bool: - return True - - -# ═══════════════════════════════════════════════════════════════ -# MediaProjection -# ═══════════════════════════════════════════════════════════════ - -class MediaProjection(Stub): - """MediaProjection — 媒体投影""" - - # ═══════════════════════════════════════════════════════════ - # Callback - # ═══════════════════════════════════════════════════════════ - - class Callback(Stub): - """MediaProjection.Callback — 投影回调""" - - def onStop(self): - pass - - def onCapturedContentResize(self, width: int, height: int): - pass - - def onCapturedContentVisibilityChanged(self, isVisible: bool): - pass - - def __init__(self): - super().__init__() - self._stopped = False - self._callbacks: List[MediaProjection.Callback] = [] - self._projection_info = MediaProjectionInfo( - packageName="com.android.systemui", - uid=1000, - isVirtualDisplay=False - ) - - def createVirtualDisplay(self, name: str, width: int, height: int, - densityDpi: int, flags: int, surface: Any, - callback: Callable = None, handler: Any = None) -> Any: - from .hardware_display import VirtualDisplay - vd = VirtualDisplay(99, name, width, height, densityDpi, surface, flags) - return vd - - def registerCallback(self, callback: Callback): - self._callbacks.append(callback) - - def unregisterCallback(self, callback: Callback): - if callback in self._callbacks: - self._callbacks.remove(callback) - - def stop(self): - self._stopped = True - for cb in self._callbacks: - cb.onStop() - - def getProjectionInfo(self) -> "MediaProjectionInfo": - return self._projection_info - - def getDisplay(self) -> Optional[Any]: - from .hardware_display import Display - return Display(displayId=0, name="Projection Display") - - def applyTransparentRegionOverlay(self, region: Any): - pass - - def removeTransparentRegionOverlay(self): - pass - - def toString(self) -> str: - return f"MediaProjection(stopped={self._stopped})" - - -# ═══════════════════════════════════════════════════════════════ -# MediaProjectionInfo -# ═══════════════════════════════════════════════════════════════ - -class MediaProjectionInfo(Stub): - """MediaProjectionInfo — 媒体投影信息""" - - def __init__(self, packageName: str = "", uid: int = 0, - isVirtualDisplay: bool = False): - super().__init__() - self._package_name = packageName - self._uid = uid - self._is_virtual_display = isVirtualDisplay - - def getProjectionType(self) -> int: - return 0 - - def getLaunchedFromPackage(self) -> str: - return self._package_name - - def getLaunchedFromUid(self) -> int: - return self._uid - - def isVirtualDisplay(self) -> bool: - return self._is_virtual_display - - def equals(self, other: Any) -> bool: - if not isinstance(other, MediaProjectionInfo): - return False - return (self._package_name == other._package_name - and self._uid == other._uid) - - def hashCode(self) -> int: - return hash((self._package_name, self._uid)) & 0x7FFFFFFF - - def toString(self) -> str: - return f"MediaProjectionInfo(package={self._package_name}, uid={self._uid})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "MediaProjectionManager", - "MediaProjection", - "MediaProjectionInfo", -] \ No newline at end of file diff --git a/android_pyapi/android/media_remoteblob.py b/android_pyapi/android/media_remoteblob.py deleted file mode 100644 index ffe4e9d..0000000 --- a/android_pyapi/android/media_remoteblob.py +++ /dev/null @@ -1,633 +0,0 @@ -""" -Android media.remoteblob 包 — RemoteBlobManager, RemoteBlob, RemoteBlobStore, -RemoteBlobReader, RemoteBlobQuery, RemoteBlobResult -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -from typing import Any, Dict, List, Optional, Tuple, Callable - -from .._core import Stub, AndroidOnlyError - -__all__ = [ - "RemoteBlobManager", - "RemoteBlob", - "RemoteBlobStore", - "RemoteBlobReader", - "RemoteBlobQuery", - "RemoteBlobResult", -] - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -QUERY_TYPE_ALL = 0 -QUERY_TYPE_BY_ID = 1 -QUERY_TYPE_BY_OWNER = 2 -QUERY_TYPE_BY_TAG = 3 -QUERY_TYPE_BY_TIMESTAMP = 4 -QUERY_TYPE_BY_SIZE = 5 -QUERY_TYPE_BY_MIME_TYPE = 6 - -STATUS_SUCCESS = 0 -STATUS_PENDING = 1 -STATUS_FAILED = 2 -STATUS_CANCELLED = 3 -STATUS_NOT_FOUND = 4 -STATUS_ACCESS_DENIED = 5 -STATUS_INSUFFICIENT_SPACE = 6 -STATUS_TIMEOUT = 7 -STATUS_INVALID_ARGUMENT = 8 -STATUS_EXPIRED = 9 - - -# ═══════════════════════════════════════════════════════════════ -# RemoteBlob -# ═══════════════════════════════════════════════════════════════ - -class RemoteBlob(Stub): - """RemoteBlob — 远程 Blob 数据块""" - - def __init__(self, id: str = "", owner: str = "", - tag: str = "", data: bytes = b"", - mimeType: str = "application/octet-stream", - size: int = 0, timestamp: int = 0, - metadata: Dict[str, str] = None, - expiryTimestamp: int = 0): - super().__init__() - self._id = id - self._owner = owner - self._tag = tag - self._data = data - self._mime_type = mimeType - self._size = size if size > 0 else len(data) - self._timestamp = timestamp - self._metadata = metadata or {} - self._expiry_timestamp = expiryTimestamp - - def getId(self) -> str: - return self._id - - def getOwner(self) -> str: - return self._owner - - def getTag(self) -> str: - return self._tag - - def getData(self) -> bytes: - return self._data - - def getMimeType(self) -> str: - return self._mime_type - - def getSize(self) -> int: - return self._size - - def getTimestamp(self) -> int: - return self._timestamp - - def getMetadata(self) -> Dict[str, str]: - return dict(self._metadata) - - def getExpiryTimestamp(self) -> int: - return self._expiry_timestamp - - def isExpired(self) -> bool: - import time - return 0 < self._expiry_timestamp < int(time.time() * 1000) - - def setMetadata(self, key: str, value: str): - self._metadata[key] = value - - def getMetadataValue(self, key: str) -> Optional[str]: - return self._metadata.get(key) - - class Builder(Stub): - """RemoteBlob.Builder — Blob 构建器""" - - def __init__(self): - super().__init__() - self._id = "" - self._owner = "" - self._tag = "" - self._data = b"" - self._mime_type = "application/octet-stream" - self._metadata: Dict[str, str] = {} - self._expiry_timestamp = 0 - - def setId(self, id: str) -> "RemoteBlob.Builder": - self._id = id - return self - - def setOwner(self, owner: str) -> "RemoteBlob.Builder": - self._owner = owner - return self - - def setTag(self, tag: str) -> "RemoteBlob.Builder": - self._tag = tag - return self - - def setData(self, data: bytes) -> "RemoteBlob.Builder": - self._data = data - return self - - def setMimeType(self, mimeType: str) -> "RemoteBlob.Builder": - self._mime_type = mimeType - return self - - def setMetadata(self, metadata: Dict[str, str]) -> "RemoteBlob.Builder": - self._metadata = metadata - return self - - def setExpiryTimestamp(self, expiryTimestamp: int) -> "RemoteBlob.Builder": - self._expiry_timestamp = expiryTimestamp - return self - - def build(self) -> "RemoteBlob": - return RemoteBlob( - id=self._id, owner=self._owner, tag=self._tag, - data=self._data, mimeType=self._mime_type, - metadata=self._metadata, - expiryTimestamp=self._expiry_timestamp, - ) - - def toString(self) -> str: - return f"RemoteBlob(id={self._id}, size={self._size}, tag={self._tag})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, RemoteBlob): - return False - return self._id == other._id - - def hashCode(self) -> int: - return hash(self._id) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# RemoteBlobQuery -# ═══════════════════════════════════════════════════════════════ - -class RemoteBlobQuery(Stub): - """RemoteBlobQuery — 远程 Blob 查询""" - - def __init__(self, queryType: int = QUERY_TYPE_ALL, - queryValue: str = "", - ownerFilter: str = "", - tagFilter: str = "", - mimeTypeFilter: str = "", - minSize: int = 0, maxSize: int = 0, - startTimestamp: int = 0, endTimestamp: int = 0, - offset: int = 0, limit: int = 100, - includeExpired: bool = False): - super().__init__() - self._query_type = queryType - self._query_value = queryValue - self._owner_filter = ownerFilter - self._tag_filter = tagFilter - self._mime_type_filter = mimeTypeFilter - self._min_size = minSize - self._max_size = maxSize - self._start_timestamp = startTimestamp - self._end_timestamp = endTimestamp - self._offset = offset - self._limit = limit - self._include_expired = includeExpired - - def getQueryType(self) -> int: - return self._query_type - - def getQueryValue(self) -> str: - return self._query_value - - def getOwnerFilter(self) -> str: - return self._owner_filter - - def getTagFilter(self) -> str: - return self._tag_filter - - def getMimeTypeFilter(self) -> str: - return self._mime_type_filter - - def getMinSize(self) -> int: - return self._min_size - - def getMaxSize(self) -> int: - return self._max_size - - def getStartTimestamp(self) -> int: - return self._start_timestamp - - def getEndTimestamp(self) -> int: - return self._end_timestamp - - def getOffset(self) -> int: - return self._offset - - def getLimit(self) -> int: - return self._limit - - def includeExpired(self) -> bool: - return self._include_expired - - class Builder(Stub): - """RemoteBlobQuery.Builder — 查询构建器""" - - def __init__(self): - super().__init__() - self._query_type = QUERY_TYPE_ALL - self._query_value = "" - self._owner_filter = "" - self._tag_filter = "" - self._mime_type_filter = "" - self._min_size = 0 - self._max_size = 0 - self._start_timestamp = 0 - self._end_timestamp = 0 - self._offset = 0 - self._limit = 100 - self._include_expired = False - - def setQueryType(self, queryType: int) -> "RemoteBlobQuery.Builder": - self._query_type = queryType - return self - - def setQueryValue(self, queryValue: str) -> "RemoteBlobQuery.Builder": - self._query_value = queryValue - return self - - def setOwnerFilter(self, owner: str) -> "RemoteBlobQuery.Builder": - self._owner_filter = owner - return self - - def setTagFilter(self, tag: str) -> "RemoteBlobQuery.Builder": - self._tag_filter = tag - return self - - def setMimeTypeFilter(self, mimeType: str) -> "RemoteBlobQuery.Builder": - self._mime_type_filter = mimeType - return self - - def setSizeRange(self, minSize: int, maxSize: int) -> "RemoteBlobQuery.Builder": - self._min_size = minSize - self._max_size = maxSize - return self - - def setTimestampRange(self, start: int, end: int) -> "RemoteBlobQuery.Builder": - self._start_timestamp = start - self._end_timestamp = end - return self - - def setOffset(self, offset: int) -> "RemoteBlobQuery.Builder": - self._offset = offset - return self - - def setLimit(self, limit: int) -> "RemoteBlobQuery.Builder": - self._limit = limit - return self - - def setIncludeExpired(self, include: bool) -> "RemoteBlobQuery.Builder": - self._include_expired = include - return self - - def build(self) -> "RemoteBlobQuery": - return RemoteBlobQuery( - queryType=self._query_type, queryValue=self._query_value, - ownerFilter=self._owner_filter, tagFilter=self._tag_filter, - mimeTypeFilter=self._mime_type_filter, - minSize=self._min_size, maxSize=self._max_size, - startTimestamp=self._start_timestamp, - endTimestamp=self._end_timestamp, - offset=self._offset, limit=self._limit, - includeExpired=self._include_expired, - ) - - def toString(self) -> str: - return f"RemoteBlobQuery(type={self._query_type}, limit={self._limit})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, RemoteBlobQuery): - return False - return (self._query_type == other._query_type and - self._query_value == other._query_value) - - def hashCode(self) -> int: - return hash((self._query_type, self._query_value)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# RemoteBlobResult -# ═══════════════════════════════════════════════════════════════ - -class RemoteBlobResult(Stub): - """RemoteBlobResult — 远程 Blob 查询结果""" - - def __init__(self, status: int = STATUS_SUCCESS, - blobs: List[RemoteBlob] = None, - totalCount: int = 0, errorMessage: str = ""): - super().__init__() - self._status = status - self._blobs = blobs or [] - self._total_count = totalCount if totalCount > 0 else len(self._blobs) - self._error_message = errorMessage - - def getStatus(self) -> int: - return self._status - - def getBlobs(self) -> List[RemoteBlob]: - return list(self._blobs) - - def getTotalCount(self) -> int: - return self._total_count - - def getErrorMessage(self) -> str: - return self._error_message - - def isSuccess(self) -> bool: - return self._status == STATUS_SUCCESS - - def toString(self) -> str: - return f"RemoteBlobResult(status={self._status}, count={len(self._blobs)})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, RemoteBlobResult): - return False - return self._status == other._status - - def hashCode(self) -> int: - return self._status & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# RemoteBlobStore -# ═══════════════════════════════════════════════════════════════ - -class RemoteBlobStore(Stub): - """RemoteBlobStore — 远程 Blob 存储""" - - def __init__(self, name: str = "default", - basePath: str = "", - maxCapacity: int = 100 * 1024 * 1024): - super().__init__() - self._name = name - self._base_path = basePath - self._max_capacity = maxCapacity - self._blobs: Dict[str, RemoteBlob] = {} - self._current_size = 0 - - def getName(self) -> str: - return self._name - - def getBasePath(self) -> str: - return self._base_path - - def getMaxCapacity(self) -> int: - return self._max_capacity - - def getCurrentSize(self) -> int: - return self._current_size - - def put(self, blob: RemoteBlob) -> int: - if blob.getId() in self._blobs: - old = self._blobs[blob.getId()] - self._current_size -= old.getSize() - self._blobs[blob.getId()] = blob - self._current_size += blob.getSize() - return STATUS_SUCCESS - - def get(self, id: str) -> Optional[RemoteBlob]: - return self._blobs.get(id) - - def remove(self, id: str) -> int: - blob = self._blobs.pop(id, None) - if blob: - self._current_size -= blob.getSize() - return STATUS_SUCCESS - return STATUS_NOT_FOUND - - def query(self, query: RemoteBlobQuery) -> RemoteBlobResult: - results = list(self._blobs.values()) - if query.getQueryType() == QUERY_TYPE_BY_ID and query.getQueryValue(): - results = [b for b in results if b.getId() == query.getQueryValue()] - elif query.getQueryType() == QUERY_TYPE_BY_OWNER and query.getOwnerFilter(): - results = [b for b in results if b.getOwner() == query.getOwnerFilter()] - elif query.getQueryType() == QUERY_TYPE_BY_TAG and query.getTagFilter(): - results = [b for b in results if b.getTag() == query.getTagFilter()] - elif query.getQueryType() == QUERY_TYPE_BY_MIME_TYPE and query.getMimeTypeFilter(): - results = [b for b in results if b.getMimeType() == query.getMimeTypeFilter()] - if not query.includeExpired(): - results = [b for b in results if not b.isExpired()] - offset = query.getOffset() - limit = query.getLimit() - total = len(results) - results = results[offset:offset + limit] - return RemoteBlobResult(status=STATUS_SUCCESS, blobs=results, totalCount=total) - - def clear(self): - self._blobs.clear() - self._current_size = 0 - - def contains(self, id: str) -> bool: - return id in self._blobs - - def getSize(self) -> int: - return len(self._blobs) - - def getCapacityRemaining(self) -> int: - return self._max_capacity - self._current_size - - def toString(self) -> str: - return f"RemoteBlobStore(name={self._name}, count={len(self._blobs)})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, RemoteBlobStore): - return False - return self._name == other._name - - def hashCode(self) -> int: - return hash(self._name) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# RemoteBlobReader -# ═══════════════════════════════════════════════════════════════ - -class RemoteBlobReader(Stub): - """RemoteBlobReader — 远程 Blob 读取器""" - - def __init__(self, store: Optional[RemoteBlobStore] = None, - blobId: str = "", bufferSize: int = 8192): - super().__init__() - self._store = store - self._blob_id = blobId - self._buffer_size = bufferSize - self._position = 0 - self._open = False - self._blob: Optional[RemoteBlob] = None - - def open(self) -> int: - if not self._store: - return STATUS_FAILED - self._blob = self._store.get(self._blob_id) - if not self._blob: - return STATUS_NOT_FOUND - self._position = 0 - self._open = True - return STATUS_SUCCESS - - def read(self, size: int = -1) -> Optional[bytes]: - if not self._open or not self._blob: - return None - data = self._blob.getData() - if self._position >= len(data): - return b"" - if size < 0: - result = data[self._position:] - self._position = len(data) - else: - result = data[self._position:self._position + size] - self._position += len(result) - return result - - def readFully(self) -> Optional[bytes]: - if not self._open or not self._blob: - return None - return self._blob.getData() - - def seek(self, position: int) -> int: - if not self._blob: - return STATUS_FAILED - if position < 0 or position > len(self._blob.getData()): - return STATUS_INVALID_ARGUMENT - self._position = position - return STATUS_SUCCESS - - def getPosition(self) -> int: - return self._position - - def getRemaining(self) -> int: - if not self._blob: - return 0 - return max(0, len(self._blob.getData()) - self._position) - - def close(self): - self._open = False - self._blob = None - self._position = 0 - - def isOpen(self) -> bool: - return self._open - - def toString(self) -> str: - return f"RemoteBlobReader(blob={self._blob_id}, open={self._open})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# RemoteBlobManager -# ═══════════════════════════════════════════════════════════════ - -class RemoteBlobManager(Stub): - """RemoteBlobManager — 远程 Blob 管理器""" - - def __init__(self): - super().__init__() - self._stores: Dict[str, RemoteBlobStore] = {} - self._default_store = RemoteBlobStore("default") - self._stores["default"] = self._default_store - self._listeners: Dict[int, Callable] = {} - - def createStore(self, name: str, basePath: str = "", - maxCapacity: int = 100 * 1024 * 1024) -> RemoteBlobStore: - if name in self._stores: - raise IllegalArgumentException(f"Store '{name}' already exists") - store = RemoteBlobStore(name=name, basePath=basePath, maxCapacity=maxCapacity) - self._stores[name] = store - return store - - def getStore(self, name: str) -> Optional[RemoteBlobStore]: - return self._stores.get(name) - - def deleteStore(self, name: str) -> int: - if name not in self._stores: - return STATUS_NOT_FOUND - self._stores[name].clear() - del self._stores[name] - return STATUS_SUCCESS - - def getDefaultStore(self) -> RemoteBlobStore: - return self._default_store - - def listStores(self) -> List[str]: - return list(self._stores.keys()) - - def openReader(self, storeName: str, blobId: str) -> Optional[RemoteBlobReader]: - store = self._stores.get(storeName) - if not store: - return None - reader = RemoteBlobReader(store=store, blobId=blobId) - status = reader.open() - return reader if status == STATUS_SUCCESS else None - - def put(self, blob: RemoteBlob, storeName: str = "default") -> int: - store = self._stores.get(storeName) - if not store: - return STATUS_NOT_FOUND - return store.put(blob) - - def get(self, blobId: str, storeName: str = "default") -> Optional[RemoteBlob]: - store = self._stores.get(storeName) - if not store: - return None - return store.get(blobId) - - def remove(self, blobId: str, storeName: str = "default") -> int: - store = self._stores.get(storeName) - if not store: - return STATUS_NOT_FOUND - return store.remove(blobId) - - def query(self, query: RemoteBlobQuery, storeName: str = "default") -> RemoteBlobResult: - store = self._stores.get(storeName) - if not store: - return RemoteBlobResult(status=STATUS_NOT_FOUND) - return store.query(query) - - def registerCallback(self, callback: Callable) -> int: - lid = id(callback) - self._listeners[lid] = callback - return STATUS_SUCCESS - - def unregisterCallback(self, callback: Callable) -> int: - lid = id(callback) - return STATUS_SUCCESS if self._listeners.pop(lid, None) else STATUS_NOT_FOUND - - def getStoreNames(self) -> List[str]: - return self.listStores() - - def getTotalSize(self) -> int: - return sum(s.getCurrentSize() for s in self._stores.values()) - - def getTotalCount(self) -> int: - return sum(s.getSize() for s in self._stores.values()) - - def clearAll(self): - for store in self._stores.values(): - store.clear() - - def toString(self) -> str: - return (f"RemoteBlobManager(stores={len(self._stores)}, " - f"totalBlobs={self.getTotalCount()})") - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/media_session.py b/android_pyapi/android/media_session.py deleted file mode 100644 index b848285..0000000 --- a/android_pyapi/android/media_session.py +++ /dev/null @@ -1,854 +0,0 @@ -""" -Android media.session 组件 — MediaSession, MediaController, PlaybackState, MediaMetadata 等 -方法签名完全对齐 Android SDK -""" - -import time -from typing import Any, Callable, Dict, List, Optional, Set, Union - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# MediaSession -# ═══════════════════════════════════════════════════════════════ - -class MediaSession(Stub): - """MediaSession — 媒体会话""" - - FLAG_HANDLES_MEDIA_BUTTONS = 1 - FLAG_HANDLES_TRANSPORT_CONTROLS = 2 - FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 4 - - # ═══════════════════════════════════════════════════════════ - # Callback - # ═══════════════════════════════════════════════════════════ - - class Callback(Stub): - """MediaSession.Callback — 会话回调""" - - def onPlay(self): - pass - - def onPause(self): - pass - - def onSkipToNext(self): - pass - - def onSkipToPrevious(self): - pass - - def onFastForward(self): - pass - - def onRewind(self): - pass - - def onStop(self): - pass - - def onSeekTo(self, pos: int): - pass - - def onSetRating(self, rating: "Rating"): - pass - - def onSetRepeatMode(self, repeatMode: int): - pass - - def onSetShuffleMode(self, shuffleMode: int): - pass - - def onCommand(self, command: str, extras: Any, cb: Any): - pass - - def onCustomAction(self, action: str, extras: Any): - pass - - def onMediaButtonEvent(self, mediaButtonIntent: Any) -> bool: - return False - - def onPlayFromMediaId(self, mediaId: str, extras: Any): - pass - - def onPlayFromSearch(self, query: str, extras: Any): - pass - - def onPlayFromUri(self, uri: str, extras: Any): - pass - - def onPrepare(self): - pass - - def onPrepareFromMediaId(self, mediaId: str, extras: Any): - pass - - def onPrepareFromSearch(self, query: str, extras: Any): - pass - - def onPrepareFromUri(self, uri: str, extras: Any): - pass - - def __init__(self, context: Any = None, tag: str = "", - token: "SessionToken" = None): - super().__init__() - self._tag = tag - self._token = token or SessionToken() - self._active = False - self._flags = 0 - self._session_activity: Any = None - self._callback: Optional[MediaSession.Callback] = None - self._metadata: Optional["MediaMetadata"] = None - self._playback_state: Optional["PlaybackState"] = None - self._queue: List["QueueItem"] = [] - self._queue_title: Optional[str] = None - self._extras: Any = None - self._rating_type = Rating.RATING_NONE - self._repeat_mode = PlaybackState.REPEAT_MODE_NONE - self._shuffle_mode = PlaybackState.SHUFFLE_MODE_NONE - self._captioning_enabled = False - self._controllers: List[MediaController] = [] - - def setSessionActivity(self, sessionActivity: Any): - self._session_activity = sessionActivity - - def sendSessionEvent(self, event: str, extras: Any): - for ctrl in self._controllers: - ctrl._callback.onSessionEvent(event, extras) - - def release(self): - self._active = False - self._controllers.clear() - - def getSessionToken(self) -> "SessionToken": - return self._token - - def setFlags(self, flags: int): - self._flags = flags - - def setPlaybackToLocal(self, volumeProvider: Any = None): - pass - - def setPlaybackToRemote(self, volumeProvider: Any): - pass - - def setActive(self, active: bool): - self._active = active - - def isActive(self) -> bool: - return self._active - - def setMediaMetadata(self, metadata: "MediaMetadata"): - self._metadata = metadata - for ctrl in self._controllers: - ctrl._callback.onMetadataChanged(metadata) - - def setPlaybackState(self, state: "PlaybackState"): - self._playback_state = state - for ctrl in self._controllers: - ctrl._callback.onPlaybackStateChanged(state) - - def setQueue(self, queue: List["QueueItem"]): - self._queue = queue - for ctrl in self._controllers: - ctrl._callback.onQueueChanged(queue) - - def setQueueTitle(self, title: str): - self._queue_title = title - for ctrl in self._controllers: - ctrl._callback.onQueueTitleChanged(title) - - def setExtras(self, extras: Any): - self._extras = extras - for ctrl in self._controllers: - ctrl._callback.onExtrasChanged(extras) - - def setMetadata(self, metadata: "MediaMetadata"): - self.setMediaMetadata(metadata) - - def setRatingType(self, ratingType: int): - self._rating_type = ratingType - - def setRepeatMode(self, repeatMode: int): - self._repeat_mode = repeatMode - for ctrl in self._controllers: - ctrl._callback.onRepeatModeChanged(repeatMode) - - def setShuffleMode(self, shuffleMode: int): - self._shuffle_mode = shuffleMode - for ctrl in self._controllers: - ctrl._callback.onShuffleModeChanged(shuffleMode) - - def setCaptioningEnabled(self, enabled: bool): - self._captioning_enabled = enabled - for ctrl in self._controllers: - ctrl._callback.onCaptioningEnabledChanged(enabled) - - def getController(self) -> "MediaController": - ctrl = MediaController(self) - self._controllers.append(ctrl) - return ctrl - - def getCallerPackageName(self) -> str: - return "com.android.systemui" - - def setCallback(self, callback: Callback, handler: Any = None): - self._callback = callback - - -# ═══════════════════════════════════════════════════════════════ -# MediaController -# ═══════════════════════════════════════════════════════════════ - -class MediaController(Stub): - """MediaController — 媒体控制器""" - - # ═══════════════════════════════════════════════════════════ - # Callback - # ═══════════════════════════════════════════════════════════ - - class Callback(Stub): - """MediaController.Callback — 控制器回调""" - - def onSessionReady(self): - pass - - def onSessionDestroyed(self): - pass - - def onSessionEvent(self, event: str, extras: Any): - pass - - def onPlaybackStateChanged(self, state: "PlaybackState"): - pass - - def onMetadataChanged(self, metadata: "MediaMetadata"): - pass - - def onQueueChanged(self, queue: List["QueueItem"]): - pass - - def onQueueTitleChanged(self, title: str): - pass - - def onExtrasChanged(self, extras: Any): - pass - - def onAudioInfoChanged(self, info: Any): - pass - - def onCaptioningEnabledChanged(self, enabled: bool): - pass - - def onRepeatModeChanged(self, repeatMode: int): - pass - - def onShuffleModeChanged(self, shuffleMode: int): - pass - - # ═══════════════════════════════════════════════════════════ - # TransportControls - # ═══════════════════════════════════════════════════════════ - - class TransportControls(Stub): - """MediaController.TransportControls — 传输控制""" - - def __init__(self, session: "MediaSession"): - super().__init__() - self._session = session - - def play(self): - if self._session._callback: - self._session._callback.onPlay() - - def pause(self): - if self._session._callback: - self._session._callback.onPause() - - def stop(self): - if self._session._callback: - self._session._callback.onStop() - - def fastForward(self): - if self._session._callback: - self._session._callback.onFastForward() - - def rewind(self): - if self._session._callback: - self._session._callback.onRewind() - - def skipToNext(self): - if self._session._callback: - self._session._callback.onSkipToNext() - - def skipToPrevious(self): - if self._session._callback: - self._session._callback.onSkipToPrevious() - - def seekTo(self, pos: int): - if self._session._callback: - self._session._callback.onSeekTo(pos) - - def setRating(self, rating: "Rating"): - if self._session._callback: - self._session._callback.onSetRating(rating) - - def setRepeatMode(self, repeatMode: int): - if self._session._callback: - self._session._callback.onSetRepeatMode(repeatMode) - - def setShuffleMode(self, shuffleMode: int): - if self._session._callback: - self._session._callback.onSetShuffleMode(shuffleMode) - - def sendCustomAction(self, action: str, extras: Any): - if self._session._callback: - self._session._callback.onCustomAction(action, extras) - - def playFromMediaId(self, mediaId: str, extras: Any): - if self._session._callback: - self._session._callback.onPlayFromMediaId(mediaId, extras) - - def playFromSearch(self, query: str, extras: Any): - if self._session._callback: - self._session._callback.onPlayFromSearch(query, extras) - - def playFromUri(self, uri: str, extras: Any): - if self._session._callback: - self._session._callback.onPlayFromUri(uri, extras) - - def prepare(self): - if self._session._callback: - self._session._callback.onPrepare() - - def prepareFromMediaId(self, mediaId: str, extras: Any): - if self._session._callback: - self._session._callback.onPrepareFromMediaId(mediaId, extras) - - def prepareFromSearch(self, query: str, extras: Any): - if self._session._callback: - self._session._callback.onPrepareFromSearch(query, extras) - - def prepareFromUri(self, uri: str, extras: Any): - if self._session._callback: - self._session._callback.onPrepareFromUri(uri, extras) - - def __init__(self, session: "MediaSession" = None): - super().__init__() - self._session = session - self._callback = MediaController.Callback() - self._transport_controls = MediaController.TransportControls(session) - - def getSessionToken(self) -> "SessionToken": - return self._session.getSessionToken() if self._session else SessionToken() - - def getTransportControls(self) -> TransportControls: - return self._transport_controls - - def getPlaybackState(self) -> Optional["PlaybackState"]: - return self._session._playback_state if self._session else None - - def getMetadata(self) -> Optional["MediaMetadata"]: - return self._session._metadata if self._session else None - - def getQueueTitle(self) -> Optional[str]: - return self._session._queue_title if self._session else None - - def getQueue(self) -> List["QueueItem"]: - return self._session._queue if self._session else [] - - def getRepeatMode(self) -> int: - return self._session._repeat_mode if self._session else PlaybackState.REPEAT_MODE_NONE - - def getShuffleMode(self) -> int: - return self._session._shuffle_mode if self._session else PlaybackState.SHUFFLE_MODE_NONE - - def getRatingType(self) -> int: - return self._session._rating_type if self._session else Rating.RATING_NONE - - def getExtras(self) -> Any: - return self._session._extras if self._session else None - - def getFlags(self) -> int: - return self._session._flags if self._session else 0 - - def getPackageName(self) -> str: - return self._session.getCallerPackageName() if self._session else "" - - def getSessionActivity(self) -> Any: - return self._session._session_activity if self._session else None - - def registerCallback(self, callback: Callback, handler: Any = None): - self._callback = callback - - def unregisterCallback(self, callback: Callback): - self._callback = MediaController.Callback() - - def sendCommand(self, command: str, extras: Any, cb: Any): - if self._session and self._session._callback: - self._session._callback.onCommand(command, extras, cb) - - def adjustVolume(self, direction: int, flags: int): - pass - - def setVolumeTo(self, value: int, flags: int): - pass - - def isSessionReady(self) -> bool: - return True - - -# ═══════════════════════════════════════════════════════════════ -# PlaybackState -# ═══════════════════════════════════════════════════════════════ - -class PlaybackState(Stub): - """PlaybackState — 播放状态""" - - STATE_NONE = 0 - STATE_STOPPED = 1 - STATE_PAUSED = 2 - STATE_PLAYING = 3 - STATE_FAST_FORWARDING = 4 - STATE_REWINDING = 5 - STATE_BUFFERING = 6 - STATE_ERROR = 7 - STATE_CONNECTING = 8 - STATE_SKIPPING_TO_PREVIOUS = 9 - STATE_SKIPPING_TO_NEXT = 10 - STATE_SKIPPING_TO_QUEUE_ITEM = 11 - - ACTION_PLAY = 1 << 0 - ACTION_PAUSE = 1 << 1 - ACTION_PLAY_PAUSE = 1 << 2 - ACTION_STOP = 1 << 3 - ACTION_SEEK_TO = 1 << 4 - ACTION_FAST_FORWARD = 1 << 5 - ACTION_REWIND = 1 << 6 - ACTION_SKIP_TO_NEXT = 1 << 7 - ACTION_SKIP_TO_PREVIOUS = 1 << 8 - ACTION_SET_RATING = 1 << 9 - ACTION_SET_REPEAT_MODE = 1 << 10 - ACTION_SET_SHUFFLE_MODE = 1 << 11 - ACTION_SET_CAPTIONING_ENABLED = 1 << 12 - ACTION_SKIP_TO_QUEUE_ITEM = 1 << 13 - ACTION_PLAY_FROM_MEDIA_ID = 1 << 14 - ACTION_PLAY_FROM_SEARCH = 1 << 15 - ACTION_PREPARE = 1 << 16 - ACTION_PREPARE_FROM_MEDIA_ID = 1 << 17 - ACTION_PREPARE_FROM_SEARCH = 1 << 18 - ACTION_PREPARE_FROM_URI = 1 << 19 - ACTION_PLAY_FROM_URI = 1 << 20 - - REPEAT_MODE_NONE = 0 - REPEAT_MODE_ONE = 1 - REPEAT_MODE_ALL = 2 - REPEAT_MODE_GROUP = 3 - - SHUFFLE_MODE_NONE = 0 - SHUFFLE_MODE_ALL = 1 - SHUFFLE_MODE_GROUP = 2 - - class Builder(Stub): - """PlaybackState.Builder — 播放状态构建器""" - - def __init__(self): - super().__init__() - self._state = PlaybackState.STATE_NONE - self._position = 0 - self._speed = 1.0 - self._actions = 0 - self._custom_actions: List[Any] = [] - self._active_queue_item_id = -1 - self._error_message: Optional[str] = None - self._extras: Any = None - self._buffered_position = 0 - - def setState(self, state: int, position: int, speed: float) -> "PlaybackState.Builder": - self._state = state - self._position = position - self._speed = speed - return self - - def setActions(self, actions: int) -> "PlaybackState.Builder": - self._actions = actions - return self - - def setCustomAction(self, action: str, name: str, icon: Any = None) -> "PlaybackState.Builder": - self._custom_actions.append((action, name, icon)) - return self - - def setActiveQueueItemId(self, id: int) -> "PlaybackState.Builder": - self._active_queue_item_id = id - return self - - def setErrorMessage(self, error: str) -> "PlaybackState.Builder": - self._error_message = error - return self - - def setExtras(self, extras: Any) -> "PlaybackState.Builder": - self._extras = extras - return self - - def setBufferedPosition(self, bufferedPosition: int) -> "PlaybackState.Builder": - self._buffered_position = bufferedPosition - return self - - def setPosition(self, position: int) -> "PlaybackState.Builder": - self._position = position - return self - - def setSpeed(self, speed: float) -> "PlaybackState.Builder": - self._speed = speed - return self - - def addCustomAction(self, action: "PlaybackState.CustomAction") -> "PlaybackState.Builder": - self._custom_actions.append(action) - return self - - def build(self) -> "PlaybackState": - ps = PlaybackState() - ps._state = self._state - ps._position = self._position - ps._speed = self._speed - ps._actions = self._actions - ps._custom_actions = self._custom_actions - ps._active_queue_item_id = self._active_queue_item_id - ps._error_message = self._error_message - ps._extras = self._extras - ps._buffered_position = self._buffered_position - return ps - - class CustomAction(Stub): - """PlaybackState.CustomAction — 自定义操作""" - - def __init__(self, action: str, name: str, icon: Any = None): - super().__init__() - self._action = action - self._name = name - self._icon = icon - - def getAction(self) -> str: - return self._action - - def getName(self) -> str: - return self._name - - def getIcon(self) -> Any: - return self._icon - - def __init__(self): - super().__init__() - self._state = PlaybackState.STATE_NONE - self._position = 0 - self._speed = 1.0 - self._actions = 0 - self._custom_actions: List[Any] = [] - self._active_queue_item_id = -1 - self._error_message: Optional[str] = None - self._extras: Any = None - self._buffered_position = 0 - self._last_position_update_time = int(time.time() * 1000) - - def getState(self) -> int: - return self._state - - def getPosition(self) -> int: - return self._position - - def getPlaybackSpeed(self) -> float: - return self._speed - - def getActions(self) -> int: - return self._actions - - def getCustomActions(self) -> List[Any]: - return self._custom_actions - - def getActiveQueueItemId(self) -> int: - return self._active_queue_item_id - - def getErrorMessage(self) -> Optional[str]: - return self._error_message - - def getExtras(self) -> Any: - return self._extras - - def getBufferedPosition(self) -> int: - return self._buffered_position - - def getLastPositionUpdateTime(self) -> int: - return self._last_position_update_time - - def toString(self) -> str: - state_names = { - PlaybackState.STATE_NONE: "NONE", - PlaybackState.STATE_STOPPED: "STOPPED", - PlaybackState.STATE_PAUSED: "PAUSED", - PlaybackState.STATE_PLAYING: "PLAYING", - } - return f"PlaybackState(state={state_names.get(self._state, str(self._state))}," \ - f" pos={self._position})" - - -# ═══════════════════════════════════════════════════════════════ -# MediaMetadata -# ═══════════════════════════════════════════════════════════════ - -class MediaMetadata(Stub): - """MediaMetadata — 媒体元数据""" - - METADATA_KEY_ALBUM = "android.media.metadata.ALBUM" - METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART" - METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST" - METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI" - METADATA_KEY_ART = "android.media.metadata.ART" - METADATA_KEY_ARTIST = "android.media.metadata.ARTIST" - METADATA_KEY_ART_URI = "android.media.metadata.ART_URI" - METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR" - METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION" - METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER" - METADATA_KEY_DATE = "android.media.metadata.DATE" - METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER" - METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION" - METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON" - METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI" - METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE" - METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE" - METADATA_KEY_DURATION = "android.media.metadata.DURATION" - METADATA_KEY_GENRE = "android.media.metadata.GENRE" - METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID" - METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS" - METADATA_KEY_RATING = "android.media.metadata.RATING" - METADATA_KEY_TITLE = "android.media.metadata.TITLE" - METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER" - METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING" - METADATA_KEY_WRITER = "android.media.metadata.WRITER" - METADATA_KEY_YEAR = "android.media.metadata.YEAR" - - class Builder(Stub): - """MediaMetadata.Builder — 元数据构建器""" - - def __init__(self): - super().__init__() - self._data: Dict[str, Any] = {} - - def putString(self, key: str, value: str) -> "MediaMetadata.Builder": - self._data[key] = value - return self - - def putLong(self, key: str, value: int) -> "MediaMetadata.Builder": - self._data[key] = value - return self - - def putBitmap(self, key: str, value: Any) -> "MediaMetadata.Builder": - self._data[key] = value - return self - - def putText(self, key: str, value: str) -> "MediaMetadata.Builder": - self._data[key] = value - return self - - def putRating(self, key: str, value: "Rating") -> "MediaMetadata.Builder": - self._data[key] = value - return self - - def build(self) -> "MediaMetadata": - mm = MediaMetadata() - mm._data = dict(self._data) - return mm - - def __init__(self): - super().__init__() - self._data: Dict[str, Any] = {} - - def getString(self, key: str) -> Optional[str]: - v = self._data.get(key) - return str(v) if v is not None else None - - def getLong(self, key: str) -> int: - v = self._data.get(key, 0) - try: - return int(v) - except (ValueError, TypeError): - return 0 - - def getBitmap(self, key: str) -> Any: - return self._data.get(key) - - def getText(self, key: str) -> Optional[str]: - return self.getString(key) - - def getDescription(self) -> "MediaDescription": - from .media_browse import MediaDescription - builder = MediaDescription.Builder() - title = self.getString(MediaMetadata.METADATA_KEY_TITLE) - if title: - builder.setTitle(title) - artist = self.getString(MediaMetadata.METADATA_KEY_ARTIST) - if artist: - builder.setSubtitle(artist) - media_id = self.getString(MediaMetadata.METADATA_KEY_MEDIA_ID) - if media_id: - builder.setMediaId(media_id) - return builder.build() - - def getRating(self, key: str) -> Optional["Rating"]: - v = self._data.get(key) - return v if isinstance(v, Rating) else None - - def getBitmapDescription(self) -> Any: - return None - - def containsKey(self, key: str) -> bool: - return key in self._data - - def keySet(self) -> Set[str]: - return set(self._data.keys()) - - def getMediaId(self) -> Optional[str]: - return self.getString(MediaMetadata.METADATA_KEY_MEDIA_ID) - - def toString(self) -> str: - title = self.getString(MediaMetadata.METADATA_KEY_TITLE) - return f"MediaMetadata(title={title})" - - -# ═══════════════════════════════════════════════════════════════ -# QueueItem -# ═══════════════════════════════════════════════════════════════ - -class QueueItem(Stub): - """QueueItem — 队列项""" - - def __init__(self, description: "MediaDescription" = None, queueId: int = 0): - super().__init__() - self._description = description - self._queue_id = queueId - - def getDescription(self) -> "MediaDescription": - return self._description - - def getQueueId(self) -> int: - return self._queue_id - - def toString(self) -> str: - return f"QueueItem(id={self._queue_id}, desc={self._description})" - - -# ═══════════════════════════════════════════════════════════════ -# Rating -# ═══════════════════════════════════════════════════════════════ - -class Rating(Stub): - """Rating — 评分""" - - RATING_NONE = 0 - RATING_HEART = 1 - RATING_THUMB_UP_DOWN = 2 - RATING_3_STARS = 3 - RATING_4_STARS = 4 - RATING_5_STARS = 5 - RATING_PERCENTAGE = 6 - - def __init__(self, ratingStyle: int = RATING_NONE): - super().__init__() - self._rating_style = ratingStyle - self._rated = False - self._heart = False - self._thumb_up = False - self._star_rating = 0.0 - self._percent_rating = 0.0 - - @staticmethod - def newHeartRating(hasHeart: bool) -> "Rating": - r = Rating(Rating.RATING_HEART) - r._rated = True - r._heart = hasHeart - return r - - @staticmethod - def newThumbRating(thumbUp: bool) -> "Rating": - r = Rating(Rating.RATING_THUMB_UP_DOWN) - r._rated = True - r._thumb_up = thumbUp - return r - - @staticmethod - def newStarRating(starRatingStyle: int, starRating: float) -> "Rating": - r = Rating(starRatingStyle) - r._rated = True - r._star_rating = starRating - return r - - @staticmethod - def newPercentageRating(percent: float) -> "Rating": - r = Rating(Rating.RATING_PERCENTAGE) - r._rated = True - r._percent_rating = percent - return r - - @staticmethod - def newUnratedRating(ratingStyle: int) -> "Rating": - return Rating(ratingStyle) - - def isRated(self) -> bool: - return self._rated - - def getRatingStyle(self) -> int: - return self._rating_style - - def hasHeart(self) -> bool: - return self._heart - - def isThumbUp(self) -> bool: - return self._thumb_up - - def getStarRating(self) -> float: - return self._star_rating - - def getPercentRating(self) -> float: - return self._percent_rating - - def toString(self) -> str: - return f"Rating(style={self._rating_style}, rated={self._rated})" - - -# ═══════════════════════════════════════════════════════════════ -# SessionToken -# ═══════════════════════════════════════════════════════════════ - -class SessionToken(Stub): - """SessionToken — 会话令牌""" - - TYPE_SESSION = 0 - - def __init__(self, type_: int = TYPE_SESSION): - super().__init__() - self._type = type_ - self._binder = None - - def getType(self) -> int: - return self._type - - def getSessionBinder(self) -> Any: - return self._binder - - def toString(self) -> str: - return f"SessionToken(type={self._type})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "MediaSession", - "MediaController", - "PlaybackState", - "MediaMetadata", - "QueueItem", - "Rating", - "SessionToken", -] \ No newline at end of file diff --git a/android_pyapi/android/media_tv.py b/android_pyapi/android/media_tv.py deleted file mode 100644 index 07b9882..0000000 --- a/android_pyapi/android/media_tv.py +++ /dev/null @@ -1,359 +0,0 @@ -""" -Android media.tv 组件 — TvInputManager, TvInputInfo, TvInputService 等 -方法签名完全对齐 Android SDK -""" - -from typing import Any, Callable, Dict, List, Optional - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# TvInputManager -# ═══════════════════════════════════════════════════════════════ - -class TvInputManager(Stub): - """TvInputManager — 电视输入管理器""" - - ACTION_VIEW_RECORDING_SCHEDULES = "android.tv.action.VIEW_RECORDING_SCHEDULES" - ACTION_WATCH_NEXT = "android.tv.action.WATCH_NEXT" - - TIME_SHIFT_STATUS_AVAILABLE = 0 - TIME_SHIFT_STATUS_UNAVAILABLE = 1 - TIME_SHIFT_STATUS_UNKNOWN = 2 - TIME_SHIFT_STATUS_UNSUPPORTED = 3 - - VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 8 - VIDEO_UNAVAILABLE_REASON_BUFFERING = 3 - VIDEO_UNAVAILABLE_REASON_CAS = 7 - VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE = 5 - VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 6 - VIDEO_UNAVAILABLE_REASON_PREEMPTED = 4 - VIDEO_UNAVAILABLE_REASON_TUNING = 1 - VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0 - VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 2 - - # ═══════════════════════════════════════════════════════════ - # TvInputCallback - # ═══════════════════════════════════════════════════════════ - - class TvInputCallback(Stub): - """TvInputManager.TvInputCallback — 电视输入回调""" - - def onInputAdded(self, inputId: str): - pass - - def onInputRemoved(self, inputId: str): - pass - - def onInputChanged(self, inputId: str): - pass - - def onInputStateChanged(self, inputId: str, state: int): - pass - - def onTvInputInfoUpdated(self, inputInfo: "TvInputInfo"): - pass - - def __init__(self): - super().__init__() - self._inputs: Dict[str, TvInputInfo] = {} - self._callbacks: Dict[int, TvInputManager.TvInputCallback] = {} - - def getTvInputList(self) -> List["TvInputInfo"]: - return list(self._inputs.values()) - - def getTvInputInfo(self, inputId: str) -> Optional["TvInputInfo"]: - return self._inputs.get(inputId) - - def getCurrentTvInputInfo(self) -> Optional["TvInputInfo"]: - for inp in self._inputs.values(): - return inp - return None - - def updateTvInputInfo(self, inputInfo: "TvInputInfo"): - if inputInfo: - self._inputs[inputInfo.getId()] = inputInfo - - def registerCallback(self, callback: TvInputCallback, handler: Any = None): - lid = id(callback) - self._callbacks[lid] = callback - - def unregisterCallback(self, callback: TvInputCallback): - lid = id(callback) - self._callbacks.pop(lid, None) - - def createRecordingSession(self, recordingService: str, inputId: str): - raise AndroidOnlyError("TvInputManager.createRecordingSession() is not available on desktop") - - def sendTvInputNotifyIntent(self, intent: Any, inputId: str): - pass - - def isParentalControlsEnabled(self) -> bool: - return False - - def isRatingBlocked(self, rating: str) -> bool: - return False - - def getBlockedRatings(self) -> List[str]: - return [] - - def getAvailableTvInputs(self) -> List[str]: - return list(self._inputs.keys()) - - def getTvContentRatingSystems(self) -> List[str]: - return ["US_TV", "US_MOVIE"] - - -# ═══════════════════════════════════════════════════════════════ -# TvInputInfo -# ═══════════════════════════════════════════════════════════════ - -class TvInputInfo(Stub): - """TvInputInfo — 电视输入信息""" - - TYPE_COMPONENT = 1005 - TYPE_COMPOSITE = 1004 - TYPE_HDMI = 1007 - TYPE_OTHER = 1001 - TYPE_SCART = 1003 - TYPE_SVIDEO = 1006 - TYPE_TUNER = 0 - TYPE_DISPLAY_LINK = 1008 - TYPE_VGA = 1002 - - def __init__(self, id: str = "", serviceInfo: Any = None, - parentId: str = "", type_: int = TYPE_TUNER, - hdmiDeviceInfo: Any = None, - isHidden: bool = False, canRecord: bool = False, - canPauseRecording: bool = False): - super().__init__() - self._id = id - self._service_info = serviceInfo - self._parent_id = parentId - self._type = type_ - self._hdmi_device_info = hdmiDeviceInfo - self._is_hidden = isHidden - self._can_record = canRecord - self._can_pause_recording = canPauseRecording - - def getId(self) -> str: - return self._id - - def getServiceInfo(self) -> Any: - return self._service_info - - def getParentId(self) -> str: - return self._parent_id - - def getType(self) -> int: - return self._type - - def getHdmiDeviceInfo(self) -> Any: - return self._hdmi_device_info - - def loadIcon(self, context: Any) -> Any: - return None - - def loadLogo(self, context: Any) -> Any: - return None - - def createIntentForSetupScreen(self) -> Any: - return None - - def isHidden(self) -> bool: - return self._is_hidden - - def canRecord(self) -> bool: - return self._can_record - - def canPauseRecording(self) -> bool: - return self._can_pause_recording - - def toString(self) -> str: - type_names = { - TvInputInfo.TYPE_TUNER: "TUNER", - TvInputInfo.TYPE_HDMI: "HDMI", - TvInputInfo.TYPE_COMPOSITE: "COMPOSITE", - TvInputInfo.TYPE_COMPONENT: "COMPONENT", - } - return f"TvInputInfo(id={self._id}, type={type_names.get(self._type, str(self._type))})" - - -# ═══════════════════════════════════════════════════════════════ -# TvInputService -# ═══════════════════════════════════════════════════════════════ - -class TvInputService(Stub): - """TvInputService — 电视输入服务""" - - RECORDING_ERROR_RECORDING_LOST = 0 - RECORDING_ERROR_TIMEOUT = 1 - RECORDING_ERROR_OTHER = 2 - - # ═══════════════════════════════════════════════════════════ - # Session - # ═══════════════════════════════════════════════════════════ - - class Session(Stub): - """TvInputService.Session — 电视会话""" - - def __init__(self): - super().__init__() - self._main = False - self._stream_volume = 1.0 - self._caption_enabled = False - - def onSetSurface(self, surface: Any) -> bool: - return True - - def onSurfaceChanged(self, format: int, width: int, height: int): - pass - - def onSurfaceReleased(self): - pass - - def onAppPrivateCommand(self, action: str, data: Any): - pass - - def onCreateOverlayView(self) -> Any: - return None - - def onRelayoutContent(self): - pass - - def onSetCaptionEnabled(self, enabled: bool): - self._caption_enabled = enabled - - def onSetStreamVolume(self, volume: float): - self._stream_volume = volume - - def onTimeShiftSetCurrentPositionRequest(self, positionMs: int): - pass - - def notifyTimeShiftStatusChanged(self, status: int): - pass - - def scheduleOverlayView(self, view: Any): - pass - - def timeShiftPlay(self, uri: str): - pass - - def timeShiftPause(self): - pass - - def timeShiftResume(self): - pass - - def timeShiftSeekTo(self, positionMs: int): - pass - - def timeShiftSetPlaybackParams(self, params: Any): - pass - - def timeShiftEnablePositionTracking(self, enable: bool): - pass - - def notifyVideoAvailable(self): - pass - - def notifyVideoUnavailable(self, reason: int): - pass - - def notifySessionEvent(self, eventType: str, eventArgs: Any): - pass - - def notifyTracksChanged(self, tracks: List[Any]): - pass - - def notifyTrackSelected(self, type_: str, trackId: str): - pass - - def notifyContentAllowed(self): - pass - - def notifyContentBlocked(self, rating: str): - pass - - def setMain(self, main: bool): - self._main = main - - def setStreamVolume(self, volume: float): - self._stream_volume = volume - - def __init__(self): - super().__init__() - self._sessions: List[TvInputService.Session] = [] - - def onCreateSession(self, inputId: str) -> Session: - session = TvInputService.Session() - self._sessions.append(session) - return session - - def onSetupFinished(self, inputId: str): - pass - - def sessionCreated(self, session: Session): - pass - - def notifyHardwareFailed(self, deviceId: int): - pass - - -# ═══════════════════════════════════════════════════════════════ -# TvRecordingClient / TvRecordingService (stubs) -# ═══════════════════════════════════════════════════════════════ - -class TvRecordingClient(Stub): - """TvRecordingClient — 电视录制客户端""" - - def __init__(self): - super().__init__() - - def startRecording(self, uri: str): - pass - - def stopRecording(self): - pass - - def pauseRecording(self): - pass - - def resumeRecording(self): - pass - - def release(self): - pass - - -class TvRecordingService(Stub): - """TvRecordingService — 电视录制服务""" - - def __init__(self): - super().__init__() - - def onStartRecording(self, inputId: str, sessionId: str): - pass - - def onStopRecording(self, sessionId: str): - pass - - def onPauseRecording(self, sessionId: str): - pass - - def onResumeRecording(self, sessionId: str): - pass - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "TvInputManager", - "TvInputInfo", - "TvInputService", - "TvRecordingClient", - "TvRecordingService", -] \ No newline at end of file diff --git a/android_pyapi/android/media_tv_ext.py b/android_pyapi/android/media_tv_ext.py deleted file mode 100644 index 35bbcaa..0000000 --- a/android_pyapi/android/media_tv_ext.py +++ /dev/null @@ -1,1500 +0,0 @@ -""" -Android media.tv 扩展组件 — TvContentRating, TvTrackInfo, TvView, TvStreamConfig, -TvContract, PreviewProgram, PreviewChannel 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "TvInputManager", - "TvContentRating", - "TvTrackInfo", - "TvInputInfo", - "TvInputService", - "TvRecordingClient", - "TvView", - "TvStreamConfig", - "TvContract", - "PreviewProgram", - "PreviewChannel", -] - - -# ═══════════════════════════════════════════════════════════════ -# TvContentRating -# ═══════════════════════════════════════════════════════════════ - -class TvContentRating(Stub): - """TvContentRating — 电视内容分级""" - - RATING_TV_G = "US_TV:US_TV_G" - RATING_TV_PG = "US_TV:US_TV_PG" - RATING_TV_14 = "US_TV:US_TV_14" - RATING_TV_MA = "US_TV:US_TV_MA" - RATING_TV_Y = "US_TV:US_TV_Y" - RATING_TV_Y7 = "US_TV:US_TV_Y7" - RATING_TV_Y7_FV = "US_TV:US_TV_Y7_FV" - RATING_MOVIE_G = "US_MOVIE:US_MOVIE_G" - RATING_MOVIE_PG = "US_MOVIE:US_MOVIE_PG" - RATING_MOVIE_PG13 = "US_MOVIE:US_MOVIE_PG13" - RATING_MOVIE_R = "US_MOVIE:US_MOVIE_R" - RATING_MOVIE_NC17 = "US_MOVIE:US_MOVIE_NC17" - - def __init__(self, ratingString: str = ""): - super().__init__() - self._rating_string = ratingString - parts = ratingString.split(":") if ratingString else [] - self._domain = parts[0] if len(parts) > 0 else "" - self._rating_system = parts[1] if len(parts) > 1 else "" - self._rating_value = parts[2] if len(parts) > 2 else "" - self._sub_ratings: List[str] = parts[3:] if len(parts) > 3 else [] - - @staticmethod - def create(ratingSystemDomain: str, ratingSystem: str, - ratedRating: str, subRatings: List[str] = None) -> "TvContentRating": - parts = [ratingSystemDomain, ratingSystem, ratedRating] - if subRatings: - parts.extend(subRatings) - return TvContentRating(":".join(parts)) - - @staticmethod - def createRatingSystemString(ratingSystemDomain: str, ratingSystem: str) -> str: - return f"{ratingSystemDomain}:{ratingSystem}" - - @staticmethod - def unflattenFromString(ratingString: str) -> "TvContentRating": - return TvContentRating(ratingString) - - def flattenToString(self) -> str: - return self._rating_string - - def getDomain(self) -> str: - return self._domain - - def getRatingSystem(self) -> str: - return self._rating_system - - def getMainRating(self) -> str: - return self._rating_value - - def getSubRatings(self) -> List[str]: - return list(self._sub_ratings) - - def getRatingSystemDomain(self) -> str: - return self._domain - - def contains(self, other: "TvContentRating") -> bool: - return self._rating_string == other._rating_string - - def toString(self) -> str: - return self._rating_string - - def equals(self, other: Any) -> bool: - if not isinstance(other, TvContentRating): - return False - return self._rating_string == other._rating_string - - def hashCode(self) -> int: - return hash(self._rating_string) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# TvTrackInfo -# ═══════════════════════════════════════════════════════════════ - -class TvTrackInfo(Stub): - """TvTrackInfo — 电视轨道信息""" - - TYPE_AUDIO = 0 - TYPE_VIDEO = 1 - TYPE_SUBTITLE = 2 - - def __init__(self, type_: int = TYPE_VIDEO, trackId: str = "", - language: str = "", description: str = "", - audioChannelCount: int = 0, audioSampleRate: int = 0, - videoWidth: int = 0, videoHeight: int = 0, - videoFrameRate: float = 0.0, videoPixelAspectRatioWidth: int = 0, - videoPixelAspectRatioHeight: int = 0, - videoActiveFormatDescription: int = 0, - subtitleType: int = 0, subtitleRoleFlags: int = 0, - encoding: str = ""): - super().__init__() - self._type = type_ - self._track_id = trackId - self._language = language - self._description = description - self._audio_channel_count = audioChannelCount - self._audio_sample_rate = audioSampleRate - self._video_width = videoWidth - self._video_height = videoHeight - self._video_frame_rate = videoFrameRate - self._video_pixel_aspect_ratio_width = videoPixelAspectRatioWidth - self._video_pixel_aspect_ratio_height = videoPixelAspectRatioHeight - self._video_active_format_description = videoActiveFormatDescription - self._subtitle_type = subtitleType - self._subtitle_role_flags = subtitleRoleFlags - self._encoding = encoding - - def getType(self) -> int: - return self._type - - def getTrackId(self) -> str: - return self._track_id - - def getLanguage(self) -> str: - return self._language - - def getDescription(self) -> str: - return self._description - - def getAudioChannelCount(self) -> int: - return self._audio_channel_count if self._type == TvTrackInfo.TYPE_AUDIO else 0 - - def getAudioSampleRate(self) -> int: - return self._audio_sample_rate if self._type == TvTrackInfo.TYPE_AUDIO else 0 - - def getVideoWidth(self) -> int: - return self._video_width if self._type == TvTrackInfo.TYPE_VIDEO else 0 - - def getVideoHeight(self) -> int: - return self._video_height if self._type == TvTrackInfo.TYPE_VIDEO else 0 - - def getVideoFrameRate(self) -> float: - return self._video_frame_rate if self._type == TvTrackInfo.TYPE_VIDEO else 0.0 - - def getVideoPixelAspectRatioWidth(self) -> int: - return self._video_pixel_aspect_ratio_width - - def getVideoPixelAspectRatioHeight(self) -> int: - return self._video_pixel_aspect_ratio_height - - def getVideoActiveFormatDescription(self) -> int: - return self._video_active_format_description - - def getSubtitleType(self) -> int: - return self._subtitle_type if self._type == TvTrackInfo.TYPE_SUBTITLE else 0 - - def getSubtitleRoleFlags(self) -> int: - return self._subtitle_role_flags - - def getEncoding(self) -> str: - return self._encoding - - class Builder(Stub): - """TvTrackInfo.Builder — 轨道信息构建器""" - - def __init__(self, type_: int): - super().__init__() - self._type = type_ - self._track_id = "" - self._language = "" - self._description = "" - self._audio_channel_count = 0 - self._audio_sample_rate = 0 - self._video_width = 0 - self._video_height = 0 - self._video_frame_rate = 0.0 - self._video_pixel_aspect_ratio_width = 0 - self._video_pixel_aspect_ratio_height = 0 - self._video_active_format_description = 0 - self._subtitle_type = 0 - self._subtitle_role_flags = 0 - self._encoding = "" - - def setTrackId(self, trackId: str) -> "TvTrackInfo.Builder": - self._track_id = trackId - return self - - def setLanguage(self, language: str) -> "TvTrackInfo.Builder": - self._language = language - return self - - def setDescription(self, description: str) -> "TvTrackInfo.Builder": - self._description = description - return self - - def setAudioChannelCount(self, audioChannelCount: int) -> "TvTrackInfo.Builder": - self._audio_channel_count = audioChannelCount - return self - - def setAudioSampleRate(self, audioSampleRate: int) -> "TvTrackInfo.Builder": - self._audio_sample_rate = audioSampleRate - return self - - def setVideoWidth(self, videoWidth: int) -> "TvTrackInfo.Builder": - self._video_width = videoWidth - return self - - def setVideoHeight(self, videoHeight: int) -> "TvTrackInfo.Builder": - self._video_height = videoHeight - return self - - def setVideoFrameRate(self, videoFrameRate: float) -> "TvTrackInfo.Builder": - self._video_frame_rate = videoFrameRate - return self - - def setVideoPixelAspectRatioWidth(self, aspectRatioWidth: int) -> "TvTrackInfo.Builder": - self._video_pixel_aspect_ratio_width = aspectRatioWidth - return self - - def setVideoPixelAspectRatioHeight(self, aspectRatioHeight: int) -> "TvTrackInfo.Builder": - self._video_pixel_aspect_ratio_height = aspectRatioHeight - return self - - def setVideoActiveFormatDescription(self, afd: int) -> "TvTrackInfo.Builder": - self._video_active_format_description = afd - return self - - def setSubtitleType(self, subtitleType: int) -> "TvTrackInfo.Builder": - self._subtitle_type = subtitleType - return self - - def setSubtitleRoleFlags(self, subtitleRoleFlags: int) -> "TvTrackInfo.Builder": - self._subtitle_role_flags = subtitleRoleFlags - return self - - def setEncoding(self, encoding: str) -> "TvTrackInfo.Builder": - self._encoding = encoding - return self - - def build(self) -> "TvTrackInfo": - return TvTrackInfo( - type_=self._type, trackId=self._track_id, - language=self._language, description=self._description, - audioChannelCount=self._audio_channel_count, - audioSampleRate=self._audio_sample_rate, - videoWidth=self._video_width, videoHeight=self._video_height, - videoFrameRate=self._video_frame_rate, - videoPixelAspectRatioWidth=self._video_pixel_aspect_ratio_width, - videoPixelAspectRatioHeight=self._video_pixel_aspect_ratio_height, - videoActiveFormatDescription=self._video_active_format_description, - subtitleType=self._subtitle_type, - subtitleRoleFlags=self._subtitle_role_flags, - encoding=self._encoding, - ) - - def toString(self) -> str: - type_names = {0: "AUDIO", 1: "VIDEO", 2: "SUBTITLE"} - return f"TvTrackInfo(id={self._track_id}, type={type_names.get(self._type, str(self._type))})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, TvTrackInfo): - return False - return self._track_id == other._track_id and self._type == other._type - - def hashCode(self) -> int: - return hash((self._track_id, self._type)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# TvInputInfo (扩展) -# ═══════════════════════════════════════════════════════════════ - -class TvInputInfo(Stub): - """TvInputInfo — 电视输入信息 (完整版)""" - - TYPE_COMPONENT = 1005 - TYPE_COMPOSITE = 1004 - TYPE_HDMI = 1007 - TYPE_OTHER = 1001 - TYPE_SCART = 1003 - TYPE_SVIDEO = 1006 - TYPE_TUNER = 0 - TYPE_DISPLAY_LINK = 1008 - TYPE_VGA = 1002 - - def __init__(self, id: str = "", serviceInfo: Any = None, - parentId: str = "", type_: int = TYPE_TUNER, - hdmiDeviceInfo: Any = None, - isHidden: bool = False, canRecord: bool = False, - canPauseRecording: bool = False, - passthroughMode: int = 0, tunerCount: int = 0): - super().__init__() - self._id = id - self._service_info = serviceInfo - self._parent_id = parentId - self._type = type_ - self._hdmi_device_info = hdmiDeviceInfo - self._is_hidden = isHidden - self._can_record = canRecord - self._can_pause_recording = canPauseRecording - self._passthrough_mode = passthroughMode - self._tuner_count = tunerCount - - def getId(self) -> str: - return self._id - - def getServiceInfo(self) -> Any: - return self._service_info - - def getParentId(self) -> str: - return self._parent_id - - def getType(self) -> int: - return self._type - - def getHdmiDeviceInfo(self) -> Any: - return self._hdmi_device_info - - def loadIcon(self, context: Any) -> Any: - return None - - def loadLogo(self, context: Any) -> Any: - return None - - def createIntentForSetupScreen(self) -> Any: - return None - - def isHidden(self) -> bool: - return self._is_hidden - - def canRecord(self) -> bool: - return self._can_record - - def canPauseRecording(self) -> bool: - return self._can_pause_recording - - def getPassthroughMode(self) -> int: - return self._passthrough_mode - - def getTunerCount(self) -> int: - return self._tuner_count - - def toString(self) -> str: - type_names = { - TvInputInfo.TYPE_TUNER: "TUNER", TvInputInfo.TYPE_HDMI: "HDMI", - TvInputInfo.TYPE_COMPOSITE: "COMPOSITE", TvInputInfo.TYPE_COMPONENT: "COMPONENT", - } - return f"TvInputInfo(id={self._id}, type={type_names.get(self._type, str(self._type))})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, TvInputInfo): - return False - return self._id == other._id - - def hashCode(self) -> int: - return hash(self._id) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# TvInputService (扩展) -# ═══════════════════════════════════════════════════════════════ - -class TvInputService(Stub): - """TvInputService — 电视输入服务 (完整版)""" - - RECORDING_ERROR_RECORDING_LOST = 0 - RECORDING_ERROR_TIMEOUT = 1 - RECORDING_ERROR_OTHER = 2 - RECORDING_ERROR_INSUFFICIENT_SPACE = 3 - RECORDING_ERROR_SERVICE_UNAVAILABLE = 4 - - class Session(Stub): - """TvInputService.Session — 电视会话""" - - def __init__(self): - super().__init__() - self._main = False - self._stream_volume = 1.0 - self._caption_enabled = False - self._surface: Any = None - self._overlay_view: Any = None - self._recording_enabled = False - - def onSetSurface(self, surface: Any) -> bool: - self._surface = surface - return True - - def onSurfaceChanged(self, format: int, width: int, height: int): - pass - - def onSurfaceReleased(self): - self._surface = None - - def onAppPrivateCommand(self, action: str, data: Any): - pass - - def onCreateOverlayView(self) -> Any: - return self._overlay_view - - def onRelayoutContent(self): - pass - - def onSetCaptionEnabled(self, enabled: bool): - self._caption_enabled = enabled - - def onSetStreamVolume(self, volume: float): - self._stream_volume = volume - - def onTimeShiftSetCurrentPositionRequest(self, positionMs: int): - pass - - def notifyTimeShiftStatusChanged(self, status: int): - pass - - def scheduleOverlayView(self, view: Any): - self._overlay_view = view - - def timeShiftPlay(self, uri: str): - pass - - def timeShiftPause(self): - pass - - def timeShiftResume(self): - pass - - def timeShiftSeekTo(self, positionMs: int): - pass - - def timeShiftSetPlaybackParams(self, params: Any): - pass - - def timeShiftEnablePositionTracking(self, enable: bool): - pass - - def notifyVideoAvailable(self): - pass - - def notifyVideoUnavailable(self, reason: int): - pass - - def notifySessionEvent(self, eventType: str, eventArgs: Any): - pass - - def notifyTracksChanged(self, tracks: List[Any]): - pass - - def notifyTrackSelected(self, type_: str, trackId: str): - pass - - def notifyContentAllowed(self): - pass - - def notifyContentBlocked(self, rating: str): - pass - - def setMain(self, main: bool): - self._main = main - - def setStreamVolume(self, volume: float): - self._stream_volume = volume - - def notifyRecordingStarted(self): - self._recording_enabled = True - - def notifyRecordingStopped(self): - self._recording_enabled = False - - def notifyRecordingPaused(self): - pass - - def notifyRecordingResumed(self): - pass - - def notifyRecordingError(self, errorCode: int): - pass - - def getSurface(self) -> Any: - return self._surface - - def isCaptionEnabled(self) -> bool: - return self._caption_enabled - - def getStreamVolume(self) -> float: - return self._stream_volume - - def toString(self) -> str: - return f"Session(main={self._main}, volume={self._stream_volume})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - class RecordingSession(Stub): - """TvInputService.RecordingSession — 录制会话""" - - def __init__(self): - super().__init__() - self._input_id = "" - self._recording_enabled = False - self._tune_uri: Any = None - - def onTune(self, uri: Any): - self._tune_uri = uri - - def onStartRecording(self, uri: Any): - self._recording_enabled = True - - def onStopRecording(self): - self._recording_enabled = False - - def onPauseRecording(self): - pass - - def onResumeRecording(self): - pass - - def notifyRecordingStarted(self): - pass - - def notifyRecordingStopped(self): - pass - - def notifyRecordingPaused(self): - pass - - def notifyRecordingResumed(self): - pass - - def notifyRecordingError(self, errorCode: int): - pass - - def notifyTuned(self, uri: Any): - pass - - def getTuneUri(self) -> Optional[Any]: - return self._tune_uri - - def toString(self) -> str: - return f"RecordingSession(recording={self._recording_enabled})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - class HardwareSession(Stub): - """TvInputService.HardwareSession — 硬件会话""" - - def __init__(self): - super().__init__() - self._session = TvInputService.Session() - - def onHardwareVideoAvailable(self): - pass - - def onHardwareVideoUnavailable(self, reason: int): - pass - - def toString(self) -> str: - return f"HardwareSession@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def __init__(self): - super().__init__() - self._sessions: List[TvInputService.Session] = [] - self._recording_sessions: List[TvInputService.RecordingSession] = [] - - def onCreateSession(self, inputId: str) -> Session: - session = TvInputService.Session() - self._sessions.append(session) - return session - - def onCreateRecordingSession(self, inputId: str) -> RecordingSession: - session = TvInputService.RecordingSession() - self._recording_sessions.append(session) - return session - - def onSetupFinished(self, inputId: str): - pass - - def sessionCreated(self, session: Session): - pass - - def notifyHardwareFailed(self, deviceId: int): - pass - - def getSessions(self) -> List[Session]: - return list(self._sessions) - - def getRecordingSessions(self) -> List[RecordingSession]: - return list(self._recording_sessions) - - def toString(self) -> str: - return f"TvInputService(sessions={len(self._sessions)})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# TvRecordingClient (扩展) -# ═══════════════════════════════════════════════════════════════ - -class TvRecordingClient(Stub): - """TvRecordingClient — 电视录制客户端""" - - def __init__(self, context: Any = None, inputId: str = "", - callback: Any = None): - super().__init__() - self._context = context - self._input_id = inputId - self._callback = callback - self._recording_uri: Optional[str] = None - self._is_recording = False - self._is_paused = False - - def startRecording(self, uri: str): - self._recording_uri = uri - self._is_recording = True - self._is_paused = False - - def stopRecording(self): - self._is_recording = False - self._is_paused = False - - def pauseRecording(self): - if self._is_recording: - self._is_paused = True - - def resumeRecording(self): - if self._is_paused: - self._is_paused = False - - def release(self): - self._recording_uri = None - self._is_recording = False - self._is_paused = False - - def isRecording(self) -> bool: - return self._is_recording - - def isPaused(self) -> bool: - return self._is_paused - - def getRecordingUri(self) -> Optional[str]: - return self._recording_uri - - def getInputId(self) -> str: - return self._input_id - - class Callback(Stub): - """TvRecordingClient.Callback — 录制回调""" - - def onRecordingStarted(self): - pass - - def onRecordingStopped(self): - pass - - def onRecordingPaused(self): - pass - - def onRecordingResumed(self): - pass - - def onError(self, errorCode: int): - pass - - def onTuned(self, channelUri: Any): - pass - - def onConnectionFailed(self, inputId: str): - pass - - def onDisconnected(self, inputId: str): - pass - - def toString(self) -> str: - return f"RecordingCallback@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def toString(self) -> str: - return f"TvRecordingClient(input={self._input_id}, recording={self._is_recording})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# TvView -# ═══════════════════════════════════════════════════════════════ - -class TvView(Stub): - """TvView — 电视视图""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._input_id: Optional[str] = None - self._callback: Optional[TvView.TvInputCallback] = None - self._stream_configs: List[TvStreamConfig] = [] - self._is_overlay = False - self._time_shift_enabled = False - - class TvInputCallback(Stub): - """TvView.TvInputCallback — 输入回调""" - - def onConnectionFailed(self, inputId: str): - pass - - def onDisconnected(self, inputId: str): - pass - - def onChannelRetuned(self, channelUri: Any): - pass - - def onTrackSelected(self, type_: int, trackId: str): - pass - - def onTracksChanged(self, tracks: List[Any]): - pass - - def onVideoAvailable(self, reason: int): - pass - - def onVideoUnavailable(self, reason: int): - pass - - def onContentAllowed(self): - pass - - def onContentBlocked(self, rating: str): - pass - - def onTimeShiftStatusChanged(self, status: int): - pass - - def toString(self) -> str: - return f"TvInputCallback@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def tune(self, inputId: str, channelUri: Any): - self._input_id = inputId - - def reset(self): - self._input_id = None - - def setCallback(self, callback: TvInputCallback): - self._callback = callback - - def setCaptionEnabled(self, enabled: bool): - pass - - def setStreamVolume(self, volume: float): - pass - - def selectTrack(self, type_: int, trackId: str): - pass - - def getSelectedTrack(self, type_: int) -> Optional[str]: - return None - - def getTracks(self, type_: int) -> List[Any]: - return [] - - def timeShiftPlay(self, uri: str): - self._time_shift_enabled = True - - def timeShiftPause(self): - pass - - def timeShiftResume(self): - pass - - def timeShiftSeekTo(self, positionMs: int): - pass - - def timeShiftSetPlaybackParams(self, params: Any): - pass - - def timeShiftEnablePositionTracking(self, enable: bool): - pass - - def timeShiftGetCurrentPosition(self) -> int: - return 0 - - def timeShiftGetStartPosition(self) -> int: - return 0 - - def sendAppPrivateCommand(self, action: str, data: Any): - pass - - def dispatchKeyEvent(self, event: Any) -> bool: - return False - - def dispatchUnhandledInputEvent(self, event: Any) -> bool: - return False - - def streamOverlay(self, isOverlay: bool): - self._is_overlay = isOverlay - - def getInputId(self) -> Optional[str]: - return self._input_id - - def toString(self) -> str: - return f"TvView(input={self._input_id})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# TvStreamConfig -# ═══════════════════════════════════════════════════════════════ - -class TvStreamConfig(Stub): - """TvStreamConfig — 电视流配置""" - - STREAM_TYPE_INDEPENDENT_VIDEO = 0 - STREAM_TYPE_INDEPENDENT_SOURCE = 1 - STREAM_TYPE_SESSION = 2 - - def __init__(self, streamId: int = 0, type_: int = STREAM_TYPE_INDEPENDENT_VIDEO, - maxWidth: int = 1920, maxHeight: int = 1080, - generation: int = 0): - super().__init__() - self._stream_id = streamId - self._type = type_ - self._max_width = maxWidth - self._max_height = maxHeight - self._generation = generation - - def getStreamId(self) -> int: - return self._stream_id - - def getType(self) -> int: - return self._type - - def getMaxWidth(self) -> int: - return self._max_width - - def getMaxHeight(self) -> int: - return self._max_height - - def getGeneration(self) -> int: - return self._generation - - class Builder(Stub): - """TvStreamConfig.Builder — 流配置构建器""" - - def __init__(self): - super().__init__() - self._stream_id = 0 - self._type = TvStreamConfig.STREAM_TYPE_INDEPENDENT_VIDEO - self._max_width = 1920 - self._max_height = 1080 - self._generation = 0 - - def setStreamId(self, streamId: int) -> "TvStreamConfig.Builder": - self._stream_id = streamId - return self - - def setType(self, type_: int) -> "TvStreamConfig.Builder": - self._type = type_ - return self - - def setMaxWidth(self, maxWidth: int) -> "TvStreamConfig.Builder": - self._max_width = maxWidth - return self - - def setMaxHeight(self, maxHeight: int) -> "TvStreamConfig.Builder": - self._max_height = maxHeight - return self - - def setGeneration(self, generation: int) -> "TvStreamConfig.Builder": - self._generation = generation - return self - - def build(self) -> "TvStreamConfig": - return TvStreamConfig( - streamId=self._stream_id, type_=self._type, - maxWidth=self._max_width, maxHeight=self._max_height, - generation=self._generation, - ) - - def toString(self) -> str: - return f"TvStreamConfig(id={self._stream_id}, type={self._type})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, TvStreamConfig): - return False - return self._stream_id == other._stream_id - - def hashCode(self) -> int: - return self._stream_id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# TvContract -# ═══════════════════════════════════════════════════════════════ - -class TvContract(Stub): - """TvContract — TV 内容提供者合约""" - - AUTHORITY = "android.media.tv" - CONTENT_URI_BASE = "content://android.media.tv" - - # ═══════════════════════════════════════════════════════════ - # Channels - # ═══════════════════════════════════════════════════════════ - - class Channels(Stub): - """TvContract.Channels — 频道表""" - - CONTENT_URI = "content://android.media.tv/channel" - CONTENT_URI_PASSTHROUGH = "content://android.media.tv/passthrough_channel" - - _ID = "_id" - COLUMN_BROWSABLE = "browsable" - COLUMN_DESCRIPTION = "description" - COLUMN_DISPLAY_NAME = "display_name" - COLUMN_DISPLAY_NUMBER = "display_number" - COLUMN_INPUT_ID = "input_id" - COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data" - COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1" - COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2" - COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3" - COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4" - COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id" - COLUMN_NETWORK_AFFILIATION = "network_affiliation" - COLUMN_ORIGINAL_NETWORK_ID = "original_network_id" - COLUMN_SEARCHABLE = "searchable" - COLUMN_SERVICE_ID = "service_id" - COLUMN_SERVICE_TYPE = "service_type" - COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id" - COLUMN_TYPE = "type" - COLUMN_VIDEO_FORMAT = "video_format" - COLUMN_VIDEO_HEIGHT = "video_height" - COLUMN_VIDEO_WIDTH = "video_width" - COLUMN_WATCH_NEXT_PROGRAM_COUNT = "watch_next_program_count" - - BROWSABLE_NO = 0 - BROWSABLE_YES = 1 - - TYPE_1SEG = "1SEG" - TYPE_ATSC_C = "ATSC_C" - TYPE_ATSC_M = "ATSC_M" - TYPE_ATSC_T = "ATSC_T" - TYPE_DTMB = "DTMB" - TYPE_DVB_C = "DVB_C" - TYPE_DVB_C2 = "DVB_C2" - TYPE_DVB_H = "DVB_H" - TYPE_DVB_S = "DVB_S" - TYPE_DVB_S2 = "DVB_S2" - TYPE_DVB_SH = "DVB_SH" - TYPE_DVB_T = "DVB_T" - TYPE_DVB_T2 = "DVB_T2" - TYPE_ISDB_C = "ISDB_C" - TYPE_ISDB_S = "ISDB_S" - TYPE_ISDB_T = "ISDB_T" - TYPE_ISDB_TB = "ISDB_TB" - TYPE_NTSC = "NTSC" - TYPE_OTHER = "OTHER" - TYPE_PAL = "PAL" - TYPE_PREVIEW = "PREVIEW" - TYPE_SECAM = "SECAM" - TYPE_S_DMB = "S_DMB" - TYPE_T_DMB = "T_DMB" - - VIDEO_FORMAT_1080I = "1080i" - VIDEO_FORMAT_1080P = "1080p" - VIDEO_FORMAT_2160P = "2160p" - VIDEO_FORMAT_240P = "240p" - VIDEO_FORMAT_360P = "360p" - VIDEO_FORMAT_4320P = "4320p" - VIDEO_FORMAT_480I = "480i" - VIDEO_FORMAT_480P = "480p" - VIDEO_FORMAT_576I = "576i" - VIDEO_FORMAT_576P = "576p" - VIDEO_FORMAT_720P = "720p" - - SERVICE_TYPE_AUDIO = "AUDIO" - SERVICE_TYPE_AUDIO_VIDEO = "AUDIO_VIDEO" - SERVICE_TYPE_OTHER = "OTHER" - - # ═══════════════════════════════════════════════════════════ - # Programs - # ═══════════════════════════════════════════════════════════ - - class Programs(Stub): - """TvContract.Programs — 节目表""" - - CONTENT_URI = "content://android.media.tv/program" - - _ID = "_id" - COLUMN_AUDIO_LANGUAGE = "audio_language" - COLUMN_BROADCAST_GENRE = "broadcast_genre" - COLUMN_CHANNEL_ID = "channel_id" - COLUMN_CONTENT_RATING = "content_rating" - COLUMN_DESCRIPTION = "description" - COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis" - COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number" - COLUMN_EPISODE_TITLE = "episode_title" - COLUMN_INTENT_URI = "intent_uri" - COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data" - COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1" - COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2" - COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3" - COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4" - COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id" - COLUMN_LONG_DESCRIPTION = "long_description" - COLUMN_POSTER_ART_URI = "poster_art_uri" - COLUMN_REVIEW_RATING_SCORE = "review_rating_score" - COLUMN_REVIEW_RATING_STYLE = "review_rating_style" - COLUMN_SEARCHABLE = "searchable" - COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number" - COLUMN_SEASON_TITLE = "season_title" - COLUMN_SHORT_DESCRIPTION = "short_description" - COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis" - COLUMN_THUMBNAIL_URI = "thumbnail_uri" - COLUMN_TITLE = "title" - COLUMN_VERSION_NUMBER = "version_number" - COLUMN_VIDEO_HEIGHT = "video_height" - COLUMN_VIDEO_WIDTH = "video_width" - - REVIEW_RATING_STYLE_PERCENTAGE = 0 - REVIEW_RATING_STYLE_STARS = 1 - REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 2 - - # ═══════════════════════════════════════════════════════════ - # RecordedPrograms - # ═══════════════════════════════════════════════════════════ - - class RecordedPrograms(Stub): - """TvContract.RecordedPrograms — 录制节目表""" - - CONTENT_URI = "content://android.media.tv/recorded_program" - - _ID = "_id" - COLUMN_AUDIO_LANGUAGE = "audio_language" - COLUMN_BROADCAST_GENRE = "broadcast_genre" - COLUMN_CHANNEL_ID = "channel_id" - COLUMN_CONTENT_RATING = "content_rating" - COLUMN_DESCRIPTION = "description" - COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis" - COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number" - COLUMN_EPISODE_TITLE = "episode_title" - COLUMN_INPUT_ID = "input_id" - COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data" - COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id" - COLUMN_LONG_DESCRIPTION = "long_description" - COLUMN_POSTER_ART_URI = "poster_art_uri" - COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis" - COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes" - COLUMN_RECORDING_DATA_URI = "recording_data_uri" - COLUMN_RECORDING_DURATION_MS = "recording_duration_ms" - COLUMN_REVIEW_RATING_SCORE = "review_rating_score" - COLUMN_REVIEW_RATING_STYLE = "review_rating_style" - COLUMN_SEARCHABLE = "searchable" - COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number" - COLUMN_SEASON_TITLE = "season_title" - COLUMN_SHORT_DESCRIPTION = "short_description" - COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis" - COLUMN_THUMBNAIL_URI = "thumbnail_uri" - COLUMN_TITLE = "title" - COLUMN_VERSION_NUMBER = "version_number" - - # ═══════════════════════════════════════════════════════════ - # WatchNextPrograms - # ═══════════════════════════════════════════════════════════ - - class WatchNextPrograms(Stub): - """TvContract.WatchNextPrograms — 待看节目表""" - - CONTENT_URI = "content://android.media.tv/watch_next_program" - - _ID = "_id" - COLUMN_AUDIO_LANGUAGE = "audio_language" - COLUMN_BROADCAST_GENRE = "broadcast_genre" - COLUMN_CHANNEL_ID = "channel_id" - COLUMN_CONTENT_RATING = "content_rating" - COLUMN_DESCRIPTION = "description" - COLUMN_DURATION_MILLIS = "duration_millis" - COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis" - COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number" - COLUMN_EPISODE_TITLE = "episode_title" - COLUMN_INTENT_URI = "intent_uri" - COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data" - COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id" - COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis" - COLUMN_LONG_DESCRIPTION = "long_description" - COLUMN_POSTER_ART_URI = "poster_art_uri" - COLUMN_REVIEW_RATING_SCORE = "review_rating_score" - COLUMN_REVIEW_RATING_STYLE = "review_rating_style" - COLUMN_SEARCHABLE = "searchable" - COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number" - COLUMN_SEASON_TITLE = "season_title" - COLUMN_SHORT_DESCRIPTION = "short_description" - COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis" - COLUMN_THUMBNAIL_URI = "thumbnail_uri" - COLUMN_TITLE = "title" - COLUMN_VERSION_NUMBER = "version_number" - COLUMN_WATCH_NEXT_TYPE = "watch_next_type" - - WATCH_NEXT_TYPE_CONTINUE = 0 - WATCH_NEXT_TYPE_NEXT = 1 - WATCH_NEXT_TYPE_NEW = 2 - WATCH_NEXT_TYPE_WATCHLIST = 3 - - # ═══════════════════════════════════════════════════════════ - # PreviewPrograms - # ═══════════════════════════════════════════════════════════ - - class PreviewPrograms(Stub): - """TvContract.PreviewPrograms — 预览节目表""" - - CONTENT_URI = "content://android.media.tv/preview_program" - - _ID = "_id" - COLUMN_AUDIO_LANGUAGE = "audio_language" - COLUMN_BROADCAST_GENRE = "broadcast_genre" - COLUMN_CHANNEL_ID = "channel_id" - COLUMN_CONTENT_RATING = "content_rating" - COLUMN_DESCRIPTION = "description" - COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis" - COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number" - COLUMN_EPISODE_TITLE = "episode_title" - COLUMN_INTENT_URI = "intent_uri" - COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data" - COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id" - COLUMN_ITEM_COUNT = "item_count" - COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis" - COLUMN_LONG_DESCRIPTION = "long_description" - COLUMN_POSTER_ART_URI = "poster_art_uri" - COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri" - COLUMN_REVIEW_RATING_SCORE = "review_rating_score" - COLUMN_REVIEW_RATING_STYLE = "review_rating_style" - COLUMN_SEARCHABLE = "searchable" - COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number" - COLUMN_SEASON_TITLE = "season_title" - COLUMN_SHORT_DESCRIPTION = "short_description" - COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis" - COLUMN_THUMBNAIL_URI = "thumbnail_uri" - COLUMN_TITLE = "title" - COLUMN_TYPE = "type" - COLUMN_VERSION_NUMBER = "version_number" - COLUMN_WEIGHT = "weight" - - ASPECT_RATIO_16_9 = 0 - ASPECT_RATIO_1_1 = 1 - ASPECT_RATIO_2_3 = 2 - ASPECT_RATIO_3_2 = 3 - ASPECT_RATIO_4_3 = 4 - ASPECT_RATIO_MOVIE = 5 - - TYPE_CLIP = "CLIP" - TYPE_EPISODE = "EPISODE" - TYPE_MOVIE = "MOVIE" - TYPE_PLAYLIST = "PLAYLIST" - TYPE_SERIES = "SERIES" - TYPE_TRAILER = "TRAILER" - TYPE_EVENT = "EVENT" - - def __init__(self): - super().__init__() - - def buildChannelUri(self, channelId: int) -> str: - return f"{TvContract.Channels.CONTENT_URI}/{channelId}" - - def buildProgramUri(self, programId: int) -> str: - return f"{TvContract.Programs.CONTENT_URI}/{programId}" - - def buildRecordedProgramUri(self, recordedProgramId: int) -> str: - return f"{TvContract.RecordedPrograms.CONTENT_URI}/{recordedProgramId}" - - def buildWatchNextProgramUri(self, watchNextProgramId: int) -> str: - return f"{TvContract.WatchNextPrograms.CONTENT_URI}/{watchNextProgramId}" - - def buildPreviewProgramUri(self, previewProgramId: int) -> str: - return f"{TvContract.PreviewPrograms.CONTENT_URI}/{previewProgramId}" - - def buildChannelsUriForInput(self, inputId: str) -> str: - return f"{TvContract.Channels.CONTENT_URI}?inputId={inputId}" - - def buildInputId(serviceComponentName: str) -> str: - return serviceComponentName - - def toString(self) -> str: - return f"TvContract@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# PreviewProgram -# ═══════════════════════════════════════════════════════════════ - -class PreviewProgram(Stub): - """PreviewProgram — 预览节目""" - - def __init__(self, id: int = 0, channelId: int = 0, title: str = "", - description: str = "", longDescription: str = "", - posterArtUri: str = "", thumbnailUri: str = "", - startTimeUtcMillis: int = 0, endTimeUtcMillis: int = 0, - weight: int = 0, type_: str = "CLIP"): - super().__init__() - self._id = id - self._channel_id = channelId - self._title = title - self._description = description - self._long_description = longDescription - self._poster_art_uri = posterArtUri - self._thumbnail_uri = thumbnailUri - self._start_time_utc_millis = startTimeUtcMillis - self._end_time_utc_millis = endTimeUtcMillis - self._weight = weight - self._type = type_ - - def getId(self) -> int: - return self._id - - def getChannelId(self) -> int: - return self._channel_id - - def getTitle(self) -> str: - return self._title - - def getDescription(self) -> str: - return self._description - - def getLongDescription(self) -> str: - return self._long_description - - def getPosterArtUri(self) -> str: - return self._poster_art_uri - - def getThumbnailUri(self) -> str: - return self._thumbnail_uri - - def getStartTimeUtcMillis(self) -> int: - return self._start_time_utc_millis - - def getEndTimeUtcMillis(self) -> int: - return self._end_time_utc_millis - - def getWeight(self) -> int: - return self._weight - - def getType(self) -> str: - return self._type - - def toString(self) -> str: - return f"PreviewProgram(id={self._id}, title={self._title})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, PreviewProgram): - return False - return self._id == other._id - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# PreviewChannel -# ═══════════════════════════════════════════════════════════════ - -class PreviewChannel(Stub): - """PreviewChannel — 预览频道""" - - def __init__(self, id: int = 0, type_: str = "PREVIEW", - displayName: str = "", description: str = "", - displayNumber: str = "", browsable: bool = True): - super().__init__() - self._id = id - self._type = type_ - self._display_name = displayName - self._description = description - self._display_number = displayNumber - self._browsable = browsable - - def getId(self) -> int: - return self._id - - def getType(self) -> str: - return self._type - - def getDisplayName(self) -> str: - return self._display_name - - def getDescription(self) -> str: - return self._description - - def getDisplayNumber(self) -> str: - return self._display_number - - def isBrowsable(self) -> bool: - return self._browsable - - def toString(self) -> str: - return f"PreviewChannel(id={self._id}, name={self._display_name})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, PreviewChannel): - return False - return self._id == other._id - - def hashCode(self) -> int: - return self._id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# TvInputManager (扩展完整版) -# ═══════════════════════════════════════════════════════════════ - -class TvInputManager(Stub): - """TvInputManager — 电视输入管理器 (完整版)""" - - ACTION_VIEW_RECORDING_SCHEDULES = "android.tv.action.VIEW_RECORDING_SCHEDULES" - ACTION_WATCH_NEXT = "android.tv.action.WATCH_NEXT" - ACTION_PREVIEW_PROGRAM_BROWSABLE = "android.tv.action.PREVIEW_PROGRAM_BROWSABLE" - - TIME_SHIFT_STATUS_AVAILABLE = 0 - TIME_SHIFT_STATUS_UNAVAILABLE = 1 - TIME_SHIFT_STATUS_UNKNOWN = 2 - TIME_SHIFT_STATUS_UNSUPPORTED = 3 - - VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 8 - VIDEO_UNAVAILABLE_REASON_BUFFERING = 3 - VIDEO_UNAVAILABLE_REASON_CAS = 7 - VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE = 5 - VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 6 - VIDEO_UNAVAILABLE_REASON_PREEMPTED = 4 - VIDEO_UNAVAILABLE_REASON_TUNING = 1 - VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0 - VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 2 - - INPUT_STATE_CONNECTED = 0 - INPUT_STATE_CONNECTED_STANDBY = 4 - INPUT_STATE_DISCONNECTED = 2 - INPUT_STATE_UNKNOWN = 1 - - class TvInputCallback(Stub): - """TvInputManager.TvInputCallback — 电视输入回调""" - - def onInputAdded(self, inputId: str): - pass - - def onInputRemoved(self, inputId: str): - pass - - def onInputChanged(self, inputId: str): - pass - - def onInputStateChanged(self, inputId: str, state: int): - pass - - def onTvInputInfoUpdated(self, inputInfo: "TvInputInfo"): - pass - - def toString(self) -> str: - return f"TvInputCallback@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def __init__(self): - super().__init__() - self._inputs: Dict[str, TvInputInfo] = {} - self._callbacks: Dict[int, TvInputManager.TvInputCallback] = {} - self._input_states: Dict[str, int] = {} - - def getTvInputList(self) -> List["TvInputInfo"]: - return list(self._inputs.values()) - - def getTvInputInfo(self, inputId: str) -> Optional["TvInputInfo"]: - return self._inputs.get(inputId) - - def getCurrentTvInputInfo(self) -> Optional["TvInputInfo"]: - for inp in self._inputs.values(): - return inp - return None - - def updateTvInputInfo(self, inputInfo: "TvInputInfo"): - if inputInfo: - self._inputs[inputInfo.getId()] = inputInfo - - def registerCallback(self, callback: TvInputCallback, handler: Any = None): - lid = id(callback) - self._callbacks[lid] = callback - - def unregisterCallback(self, callback: TvInputCallback): - lid = id(callback) - self._callbacks.pop(lid, None) - - def createRecordingSession(self, recordingService: str, inputId: str): - raise AndroidOnlyError("TvInputManager.createRecordingSession() is not available on desktop") - - def sendTvInputNotifyIntent(self, intent: Any, inputId: str): - pass - - def isParentalControlsEnabled(self) -> bool: - return False - - def isRatingBlocked(self, rating: str) -> bool: - return False - - def getBlockedRatings(self) -> List[str]: - return [] - - def getAvailableTvInputs(self) -> List[str]: - return list(self._inputs.keys()) - - def getTvContentRatingSystems(self) -> List[str]: - return ["US_TV", "US_MOVIE"] - - def getInputState(self, inputId: str) -> int: - return self._input_states.get(inputId, TvInputManager.INPUT_STATE_UNKNOWN) - - def setInputState(self, inputId: str, state: int): - self._input_states[inputId] = state - - def addInput(self, inputInfo: TvInputInfo): - if inputInfo: - self._inputs[inputInfo.getId()] = inputInfo - - def removeInput(self, inputId: str): - self._inputs.pop(inputId, None) - - def toString(self) -> str: - return f"TvInputManager(inputs={len(self._inputs)})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/mtp.py b/android_pyapi/android/mtp.py deleted file mode 100644 index c6c038e..0000000 --- a/android_pyapi/android/mtp.py +++ /dev/null @@ -1,476 +0,0 @@ -""" -Android Media Transfer Protocol API — MtpDevice, MtpStorageInfo, MtpObjectInfo, MtpConstants -方法签名完全对齐 Android SDK -""" - -import os as _os -import sys -import time -from datetime import datetime -from typing import Any, Dict, List, Optional, Set, Tuple, Callable, BinaryIO - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "MtpDevice", - "MtpStorageInfo", - "MtpObjectInfo", - "MtpConstants", - "MtpPropertyGroup", - "MtpDeviceInfo", - "MtpStorage", -] - - -# ═══════════════════════════════════════════════════════════════ -# MtpConstants — 50+ 格式常量 -# ═══════════════════════════════════════════════════════════════ - -class MtpConstants(Stub): - """MTP 协议常量""" - - # --- 格式常量 --- - FORMAT_UNDEFINED = 0x3000 - FORMAT_ASSOCIATION = 0x3001 - FORMAT_SCRIPT = 0x3002 - FORMAT_EXECUTABLE = 0x3003 - FORMAT_TEXT = 0x3004 - FORMAT_HTML = 0x3005 - FORMAT_DPOF = 0x3006 - FORMAT_AIFF = 0x3007 - FORMAT_WAV = 0x3008 - FORMAT_MP3 = 0x3009 - FORMAT_AVI = 0x300A - FORMAT_MPEG = 0x300B - FORMAT_ASF = 0x300C - FORMAT_DEF = 0x300D - FORMAT_EXIF_JPEG = 0x3801 - FORMAT_TIFF_EP = 0x3802 - FORMAT_FLASHPIX = 0x3803 - FORMAT_BMP = 0x3804 - FORMAT_CIFF = 0x3805 - FORMAT_GIF = 0x3807 - FORMAT_JFIF = 0x3808 - FORMAT_PCD = 0x3809 - FORMAT_PICT = 0x380A - FORMAT_PNG = 0x380B - FORMAT_TIFF = 0x380D - FORMAT_TIFF_IT = 0x380E - FORMAT_JP2 = 0x380F - FORMAT_JPX = 0x3810 - FORMAT_XML = 0x3811 - FORMAT_FIRMWARE = 0x3802 - FORMAT_WMA = 0xB901 - FORMAT_WMV = 0xB980 - FORMAT_WPL = 0xBA05 - FORMAT_FLAC = 0xB906 - FORMAT_OGG = 0xB902 - FORMAT_AAC = 0xB903 - FORMAT_MP4_CONTAINER = 0xB904 - FORMAT_MP2 = 0xB905 - FORMAT_3GP = 0xB907 - FORMAT_3GP_CONTAINER = 0xB908 - FORMAT_AVC = 0xB909 - FORMAT_HEVC = 0xB90A - FORMAT_DNG = 0xB90B - FORMAT_ARW = 0xB90C - FORMAT_CR2 = 0xB90D - FORMAT_RAF = 0xB90E - FORMAT_NEF = 0xB90F - FORMAT_ORF = 0xB910 - FORMAT_RW2 = 0xB911 - FORMAT_SRW = 0xB912 - - # --- 保护状态 --- - PROTECTION_STATUS_NONE = 0 - PROTECTION_STATUS_READ_ONLY = 0x8001 - PROTECTION_STATUS_PROTECTED = 0x8002 - - # --- 关联类型 --- - ASSOCIATION_TYPE_UNDEFINED = 0x0000 - ASSOCIATION_TYPE_GENERIC_FOLDER = 0x0001 - - # --- 属性格式常量 --- - PROP_FORMAT_UNDEFINED = 0x0000 - PROP_FORMAT_STRING = 0xFFFF - PROP_FORMAT_UINT8 = 0x0001 - PROP_FORMAT_UINT16 = 0x0002 - PROP_FORMAT_UINT32 = 0x0004 - PROP_FORMAT_UINT64 = 0x0008 - PROP_FORMAT_INT8 = 0x0009 - PROP_FORMAT_INT16 = 0x000A - PROP_FORMAT_INT32 = 0x000B - PROP_FORMAT_INT64 = 0x000C - PROP_FORMAT_BOOL = 0x4001 - PROP_FORMAT_DATE = 0x4002 - - # --- 操作码 --- - OPERATION_GET_DEVICE_INFO = 0x1001 - OPERATION_OPEN_SESSION = 0x1002 - OPERATION_CLOSE_SESSION = 0x1003 - OPERATION_GET_STORAGE_IDS = 0x1004 - OPERATION_GET_STORAGE_INFO = 0x1005 - OPERATION_GET_OBJECT_PROP_VALUE = 0x9802 - OPERATION_SET_OBJECT_PROP_VALUE = 0x9803 - OPERATION_GET_OBJECT_INFO = 0x1007 - OPERATION_GET_OBJECT = 0x1009 - OPERATION_GET_THUMB = 0x100A - OPERATION_DELETE_OBJECT = 0x100B - OPERATION_SEND_OBJECT_INFO = 0x100C - OPERATION_SEND_OBJECT = 0x100D - OPERATION_GET_OBJECT_REFERENCES = 0x1010 - OPERATION_SET_OBJECT_REFERENCES = 0x1011 - - def __init__(self): - super().__init__() - - -# ═══════════════════════════════════════════════════════════════ -# MtpDevice -# ═══════════════════════════════════════════════════════════════ - -class MtpDevice(Stub): - """MtpDevice — MTP 设备 (桌面存根)""" - - def __init__(self, device: Any = None): - super().__init__() - self._device = device - self._opened = False - - def open(self) -> bool: - """打开 MTP 设备""" - raise AndroidOnlyError("MtpDevice.open() 仅在 Android 设备上可用") - - def close(self): - """关闭 MTP 设备""" - self._opened = False - - def getDeviceInfo(self) -> "MtpDeviceInfo": - """获取设备信息""" - return MtpDeviceInfo() - - def getDeviceName(self) -> str: - """获取设备名称""" - return "MTP Device" - - def getObjectHandles( - self, storageId: int, format: int, objectHandle: int - ) -> List[int]: - """获取对象句柄列表""" - return [] - - def getObjectInfo(self, objectHandle: int) -> Optional["MtpObjectInfo"]: - """获取对象信息""" - return None - - def getObject(self, objectHandle: int, dataSize: int) -> Optional[bytes]: - """获取对象数据""" - return None - - def getThumbnail(self, objectHandle: int) -> Optional[bytes]: - """获取缩略图""" - return None - - def getStorageIds(self) -> List[int]: - """获取存储 ID 列表""" - return [0x00010001] - - def getStorageInfo(self, storageId: int) -> "MtpStorageInfo": - """获取存储信息""" - return MtpStorageInfo() - - def importFile(self, objectHandle: int, destPath: str) -> bool: - """导入文件""" - raise AndroidOnlyError("MtpDevice.importFile() 仅在 Android 设备上可用") - - def deleteObject(self, objectHandle: int) -> bool: - """删除对象""" - raise AndroidOnlyError("MtpDevice.deleteObject() 仅在 Android 设备上可用") - - def getParent(self, objectHandle: int) -> int: - """获取父对象句柄""" - return 0xFFFFFFFF - - def getObjectReferences(self, objectHandle: int) -> List[int]: - """获取对象引用列表""" - return [] - - def setObjectReferences(self, objectHandle: int, references: List[int]) -> bool: - """设置对象引用""" - raise AndroidOnlyError("MtpDevice.setObjectReferences() 仅在 Android 设备上可用") - - def sendObject(self, objectHandle: int, data: bytes) -> bool: - """发送对象""" - raise AndroidOnlyError("MtpDevice.sendObject() 仅在 Android 设备上可用") - - def sendObjectInfo( - self, storageId: int, parent: int, info: "MtpObjectInfo" - ) -> int: - """发送对象信息""" - raise AndroidOnlyError("MtpDevice.sendObjectInfo() 仅在 Android 设备上可用") - - def toString(self) -> str: - return f"MtpDevice(name={self.getDeviceName()})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# MtpStorageInfo -# ═══════════════════════════════════════════════════════════════ - -class MtpStorageInfo(Stub): - """MtpStorageInfo — MTP 存储信息""" - - def __init__(self): - super().__init__() - self._storage_id = 0x00010001 - self._max_capacity = 32 * 1024 * 1024 * 1024 # 32GB - self._free_space = 16 * 1024 * 1024 * 1024 # 16GB - self._description = "Internal Storage" - self._volume_identifier = "" - - def getStorageId(self) -> int: - return self._storage_id - - def getMaxCapacity(self) -> int: - return self._max_capacity - - def getFreeSpace(self) -> int: - return self._free_space - - def getDescription(self) -> str: - return self._description - - def getVolumeIdentifier(self) -> str: - return self._volume_identifier - - def toString(self) -> str: - return (f"MtpStorageInfo(id={self._storage_id}, " - f"free={self._free_space}B)") - - def equals(self, other) -> bool: - if not isinstance(other, MtpStorageInfo): - return False - return self._storage_id == other._storage_id - - def hashCode(self) -> int: - return self._storage_id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# MtpObjectInfo -# ═══════════════════════════════════════════════════════════════ - -class MtpObjectInfo(Stub): - """MtpObjectInfo — MTP 对象信息""" - - def __init__(self): - super().__init__() - self._object_handle = 0 - self._storage_id = 0 - self._format = MtpConstants.FORMAT_UNDEFINED - self._protection_status = MtpConstants.PROTECTION_STATUS_NONE - self._compressed_size = 0 - self._thumb_format = MtpConstants.FORMAT_UNDEFINED - self._thumb_compressed_size = 0 - self._thumb_pix_width = 0 - self._thumb_pix_height = 0 - self._image_pix_width = 0 - self._image_pix_height = 0 - self._image_pix_depth = 0 - self._parent = 0 - self._association_desc = 0 - self._association_type = 0 - self._sequence_number = 0 - self._name = "" - self._date_created = 0 - self._date_modified = 0 - self._keywords = "" - - def getObjectHandle(self) -> int: - return self._object_handle - - def getStorageId(self) -> int: - return self._storage_id - - def getFormat(self) -> int: - return self._format - - def getProtectionStatus(self) -> int: - return self._protection_status - - def getCompressedSize(self) -> int: - return self._compressed_size - - def getThumbFormat(self) -> int: - return self._thumb_format - - def getThumbCompressedSize(self) -> int: - return self._thumb_compressed_size - - def getThumbPixWidth(self) -> int: - return self._thumb_pix_width - - def getThumbPixHeight(self) -> int: - return self._thumb_pix_height - - def getImagePixWidth(self) -> int: - return self._image_pix_width - - def getImagePixHeight(self) -> int: - return self._image_pix_height - - def getImagePixDepth(self) -> int: - return self._image_pix_depth - - def getParent(self) -> int: - return self._parent - - def getAssociationDesc(self) -> int: - return self._association_desc - - def getAssociationType(self) -> int: - return self._association_type - - def getSequenceNumber(self) -> int: - return self._sequence_number - - def getName(self) -> str: - return self._name - - def getDateCreated(self) -> int: - return self._date_created - - def getDateModified(self) -> int: - return self._date_modified - - def getKeywords(self) -> str: - return self._keywords - - def toString(self) -> str: - return f"MtpObjectInfo(name={self._name}, handle={self._object_handle})" - - def equals(self, other) -> bool: - if not isinstance(other, MtpObjectInfo): - return False - return self._object_handle == other._object_handle - - def hashCode(self) -> int: - return self._object_handle & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# MtpPropertyGroup -# ═══════════════════════════════════════════════════════════════ - -class MtpPropertyGroup(Stub): - """MtpPropertyGroup — MTP 属性组""" - - def __init__(self): - super().__init__() - self._properties: Dict[int, Any] = {} - - def getPropertyValue(self, propertyCode: int) -> Optional[Any]: - return self._properties.get(propertyCode) - - def toString(self) -> str: - return f"MtpPropertyGroup(properties={len(self._properties)})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# MtpDeviceInfo -# ═══════════════════════════════════════════════════════════════ - -class MtpDeviceInfo(Stub): - """MtpDeviceInfo — MTP 设备信息""" - - def __init__(self): - super().__init__() - self._manufacturer = "Unknown" - self._model = "MTP Device" - self._device_version = "1.0" - self._serial_number = "00000000" - self._operations_supported: List[int] = [] - self._events_supported: List[int] = [] - self._device_properties_supported: List[int] = [] - - def getManufacturer(self) -> str: - return self._manufacturer - - def getModel(self) -> str: - return self._model - - def getDeviceVersion(self) -> str: - return self._device_version - - def getSerialNumber(self) -> str: - return self._serial_number - - def getOperationsSupported(self) -> List[int]: - return list(self._operations_supported) - - def getEventsSupported(self) -> List[int]: - return list(self._events_supported) - - def getDevicePropertySupported(self) -> List[int]: - return list(self._device_properties_supported) - - def toString(self) -> str: - return f"MtpDeviceInfo(manufacturer={self._manufacturer}, model={self._model})" - - def equals(self, other) -> bool: - if not isinstance(other, MtpDeviceInfo): - return False - return self._serial_number == other._serial_number - - def hashCode(self) -> int: - return hash(self._serial_number) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# MtpStorage -# ═══════════════════════════════════════════════════════════════ - -class MtpStorage(Stub): - """MtpStorage — MTP 存储""" - - def __init__(self, storageId: int = 0x00010001, path: str = ""): - super().__init__() - self._storage_id = storageId - self._path = path - self._description = "Storage" - - def getStorageId(self) -> int: - return self._storage_id - - def getPath(self) -> str: - return self._path - - def getDescription(self) -> str: - return self._description - - def toString(self) -> str: - return f"MtpStorage(id={self._storage_id}, path={self._path})" - - def equals(self, other) -> bool: - if not isinstance(other, MtpStorage): - return False - return self._storage_id == other._storage_id - - def hashCode(self) -> int: - return self._storage_id & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/net.py b/android_pyapi/android/net.py deleted file mode 100644 index 2bc46bd..0000000 --- a/android_pyapi/android/net.py +++ /dev/null @@ -1,1559 +0,0 @@ -""" -Android Network 组件 — ConnectivityManager, NetworkInfo, Uri_Builder 等 -方法签名完全对齐 Android SDK -""" - -import os -import sys -import socket -import struct -import threading -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union -from urllib.parse import urlparse, urlunparse, quote, unquote - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) - -# Linux 真实实现导入 -try: - from .._linux_impl import IS_LINUX, LinuxNetwork, HAS_IP - _USE_LINUX = IS_LINUX -except ImportError: - _USE_LINUX = False - - -# ═══════════════════════════════════════════════════════════════ -# Uri (re-export from content) -# ═══════════════════════════════════════════════════════════════ - -# Uri is defined in android.content; re-export the reference here. -from .content import Uri - -Uri_Builder = Uri.Builder - - -# ═══════════════════════════════════════════════════════════════ -# LocalSocket / LocalSocketAddress -# ═══════════════════════════════════════════════════════════════ - -class LocalSocketAddress(Stub): - """Android LocalSocketAddress""" - - class Namespace(Stub): - ABSTRACT = 0 - RESERVED = 1 - FILESYSTEM = 2 - - def __init__(self, name: str, namespace: int = Namespace.ABSTRACT): - super().__init__() - self._name = name - self._namespace = namespace - - def getName(self) -> str: - return self._name - - def getNamespace(self) -> int: - return self._namespace - - -class LocalSocket(Stub): - """Android LocalSocket — 基于 Unix domain socket 实现""" - - def __init__(self, sock_type: int = 1): # SOCK_STREAM - super().__init__() - self._sock: Optional[socket.socket] = None - self._sock_type = sock_type - self._input_stream: Optional[bytes] = b"" - self._output_stream: Optional[bytes] = b"" - self._closed = False - self._so_timeout: float = 0.0 - self._address: Optional[LocalSocketAddress] = None - - def connect(self, address: LocalSocketAddress, timeout: int = 0): - self._address = address - ns = address.getNamespace() - if ns == LocalSocketAddress.Namespace.ABSTRACT: - sock_path = "\0" + address.getName() - elif ns == LocalSocketAddress.Namespace.FILESYSTEM: - sock_path = address.getName() - else: - sock_path = os.path.join("/dev/socket", address.getName()) - try: - self._sock = socket.socket(socket.AF_UNIX, self._sock_type) - if timeout > 0: - self._sock.settimeout(timeout / 1000.0) - self._sock.connect(sock_path) - except (OSError, socket.error) as e: - raise AndroidOnlyError(f"LocalSocket.connect failed: {e}") - - def bind(self, address: LocalSocketAddress): - self._address = address - ns = address.getNamespace() - if ns == LocalSocketAddress.Namespace.ABSTRACT: - sock_path = "\0" + address.getName() - elif ns == LocalSocketAddress.Namespace.FILESYSTEM: - sock_path = address.getName() - else: - sock_path = os.path.join("/dev/socket", address.getName()) - try: - self._sock = socket.socket(socket.AF_UNIX, self._sock_type) - self._sock.bind(sock_path) - except (OSError, socket.error) as e: - raise AndroidOnlyError(f"LocalSocket.bind failed: {e}") - - def getInputStream(self): - return self._input_stream - - def getOutputStream(self): - return self._output_stream - - def close(self): - self._closed = True - if self._sock: - try: - self._sock.close() - except Exception: - pass - self._sock = None - - def setSoTimeout(self, timeout: int): - self._so_timeout = timeout / 1000.0 - if self._sock: - self._sock.settimeout(self._so_timeout) - - def getSoTimeout(self) -> int: - return int(self._so_timeout * 1000) - - def shutdownInput(self): - if self._sock: - try: - self._sock.shutdown(socket.SHUT_RD) - except Exception: - pass - - def shutdownOutput(self): - if self._sock: - try: - self._sock.shutdown(socket.SHUT_WR) - except Exception: - pass - - def isClosed(self) -> bool: - return self._closed - - def isConnected(self) -> bool: - return self._sock is not None - - def getRemoteSocketAddress(self) -> Optional[LocalSocketAddress]: - return self._address - - def toString(self) -> str: - return f"LocalSocket(address={self._address})" - - -# ═══════════════════════════════════════════════════════════════ -# NetworkUtils -# ═══════════════════════════════════════════════════════════════ - -class NetworkUtils(Stub): - """NetworkUtils — 网络工具""" - - @staticmethod - def getNetworkPartOfAddress(addr: str, prefix_len: int) -> str: - return addr - - @staticmethod - def intToInetAddress(host_address: int) -> str: - try: - return socket.inet_ntoa(struct.pack("!I", host_address & 0xFFFFFFFF)) - except Exception: - return "0.0.0.0" - - @staticmethod - def inetAddressToInt(addr: str) -> int: - try: - return struct.unpack("!I", socket.inet_aton(addr))[0] - except Exception: - return 0 - - @staticmethod - def isIPv6ULA(addr: str) -> bool: - return addr.startswith("fc") or addr.startswith("fd") - - @staticmethod - def isIPv6GlobalMulticast(addr: str) -> bool: - return addr.startswith("ff") - - @staticmethod - def prefixLengthToSubnetMask(prefix_len: int) -> int: - return (0xFFFFFFFF << (32 - prefix_len)) & 0xFFFFFFFF if prefix_len <= 32 else 0 - - -# ═══════════════════════════════════════════════════════════════ -# InetAddresses -# ═══════════════════════════════════════════════════════════════ - -class InetAddresses(Stub): - """InetAddresses — 地址解析工具""" - - @staticmethod - def isNumeric(address: str) -> bool: - try: - socket.inet_aton(address) - return True - except OSError: - return False - - @staticmethod - def parseNumericAddress(address: str) -> str: - return address - - @staticmethod - def getAddressFamily(address: str) -> int: - return 2 # AF_INET - - -# ═══════════════════════════════════════════════════════════════ -# NetworkSpecifier / MatchAllNetworkSpecifier -# ═══════════════════════════════════════════════════════════════ - -class NetworkSpecifier(Stub): - """NetworkSpecifier — 网络说明符基类""" - - def __init__(self): - super().__init__() - - def satisfiedBy(self, other: "NetworkSpecifier") -> bool: - return self is other - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self.__class__.__name__) & 0x7FFFFFFF - - -class MatchAllNetworkSpecifier(NetworkSpecifier): - """匹配所有网络的说明符""" - - def __init__(self): - super().__init__() - - def satisfiedBy(self, other: NetworkSpecifier) -> bool: - return True - - def toString(self) -> str: - return "MatchAllNetworkSpecifier" - - -class WifiAwareNetworkSpecifier(NetworkSpecifier): - """WiFi Aware 网络说明符""" - - def __init__(self, role: int, client_id: int, peer_id: int, peer_mac: Optional[str] = None): - super().__init__() - self._role = role - self._client_id = client_id - self._peer_id = peer_id - self._peer_mac = peer_mac - - def getRole(self) -> int: - return self._role - - def getClientId(self) -> int: - return self._client_id - - def toString(self) -> str: - return f"WifiAwareNetworkSpecifier(role={self._role}, client={self._client_id})" - - -class EthernetNetworkSpecifier(NetworkSpecifier): - """以太网网络说明符""" - - def __init__(self, data: Optional[str] = None): - super().__init__() - self._data = data - - def getData(self) -> Optional[str]: - return self._data - - def toString(self) -> str: - return f"EthernetNetworkSpecifier(data={self._data})" - - -class TelephonyNetworkSpecifier(NetworkSpecifier): - """电话网络说明符""" - - def __init__(self, subscription_id: int = 0): - super().__init__() - self._subscription_id = subscription_id - - def getSubscriptionId(self) -> int: - return self._subscription_id - - def toString(self) -> str: - return f"TelephonyNetworkSpecifier(subId={self._subscription_id})" - - -# ═══════════════════════════════════════════════════════════════ -# NetworkState -# ═══════════════════════════════════════════════════════════════ - -class NetworkState(Stub): - """NetworkState — 网络状态""" - - def __init__(self, network: Optional["Network"] = None, - network_info: Optional["NetworkInfo"] = None, - link_properties: Optional["LinkProperties"] = None, - network_capabilities: Optional["NetworkCapabilities"] = None, - subscriber_id: Optional[str] = None): - super().__init__() - self.network = network - self.network_info = network_info - self.link_properties = link_properties - self.network_capabilities = network_capabilities - self.subscriber_id = subscriber_id - - def getNetwork(self) -> Optional["Network"]: - return self.network - - def getNetworkInfo(self) -> Optional["NetworkInfo"]: - return self.network_info - - def getLinkProperties(self) -> Optional["LinkProperties"]: - return self.link_properties - - def getNetworkCapabilities(self) -> Optional["NetworkCapabilities"]: - return self.network_capabilities - - def toString(self) -> str: - return f"NetworkState(network={self.network})" - - -# ═══════════════════════════════════════════════════════════════ -# NetworkInfo -# ═══════════════════════════════════════════════════════════════ - -class NetworkInfo(Stub): - """NetworkInfo — 网络连接信息""" - - class DetailedState(Stub): - CONNECTED = "CONNECTED" - CONNECTING = "CONNECTING" - DISCONNECTED = "DISCONNECTED" - SUSPENDED = "SUSPENDED" - IDLE = "IDLE" - FAILED = "FAILED" - BLOCKED = "BLOCKED" - VERIFYING_POOR_CONNECTION = "VERIFYING_POOR_CONNECTION" - CAPTIVE_PORTAL_CHECK = "CAPTIVE_PORTAL_CHECK" - OBTAINING_IPADDR = "OBTAINING_IPADDR" - - class State(Stub): - CONNECTED = "CONNECTED" - CONNECTING = "CONNECTING" - DISCONNECTED = "DISCONNECTED" - SUSPENDED = "SUSPENDED" - UNKNOWN = "UNKNOWN" - - def __init__(self, type_: int = 0, subtype: int = 0, - state: str = "DISCONNECTED", detailed_state: str = "DISCONNECTED", - type_name: str = "", subtype_name: str = "", - extra_info: Optional[str] = None, reason: Optional[str] = None, - failover: bool = False, roaming: bool = False, - is_available: bool = True): - super().__init__() - self._type = type_ - self._subtype = subtype - self._state = state - self._detailed_state = detailed_state - self._type_name = type_name or self._default_type_name(type_) - self._subtype_name = subtype_name - self._extra_info = extra_info - self._reason = reason - self._failover = failover - self._roaming = roaming - self._is_available = is_available - self._is_connected = (state == "CONNECTED") - self._is_connecting = (state == "CONNECTING") - - def _default_type_name(self, type_: int) -> str: - names = { - 0: "MOBILE", 1: "WIFI", 6: "WIMAX", 7: "BLUETOOTH", - 9: "ETHERNET", 17: "VPN", - } - return names.get(type_, "UNKNOWN") - - @classmethod - def from_linux_data(cls, data: Optional[dict] = None, - network_type: int = 9) -> "NetworkInfo": - """从 Linux 真实网络数据构造 NetworkInfo - - data 字典可包含: ip, interface, connected, type_name 等 - """ - if data is None: - data = {} - connected = bool(data.get("connected", False)) - state = "CONNECTED" if connected else "DISCONNECTED" - detailed_state = "CONNECTED" if connected else "DISCONNECTED" - type_name = data.get("type_name") or cls._default_type_name_static(network_type) - extra_info = data.get("ip") or data.get("interface") - return cls( - type_=network_type, - state=state, - detailed_state=detailed_state, - type_name=type_name, - extra_info=extra_info, - is_available=True, - roaming=False, - ) - - @staticmethod - def _default_type_name_static(type_: int) -> str: - names = { - 0: "MOBILE", 1: "WIFI", 6: "WIMAX", 7: "BLUETOOTH", - 9: "ETHERNET", 17: "VPN", - } - return names.get(type_, "UNKNOWN") - - def getType(self) -> int: - return self._type - - def getSubtype(self) -> int: - return self._subtype - - def isConnected(self) -> bool: - return self._state == "CONNECTED" - - def isConnectedOrConnecting(self) -> bool: - return self._state in ("CONNECTED", "CONNECTING") - - def getState(self) -> str: - return self._state - - def getDetailedState(self) -> str: - return self._detailed_state - - def getTypeName(self) -> str: - return self._type_name - - def getSubtypeName(self) -> str: - return self._subtype_name - - def getExtraInfo(self) -> Optional[str]: - return self._extra_info - - def getReason(self) -> Optional[str]: - return self._reason - - def getFailover(self) -> bool: - return self._failover - - def getRoaming(self) -> bool: - return self._roaming - - def isAvailable(self) -> bool: - return self._is_available - - def isFailover(self) -> bool: - return self._failover - - def isRoaming(self) -> bool: - return self._roaming - - def toString(self) -> str: - return (f"NetworkInfo(type={self._type_name}[{self._type}], " - f"state={self._state}/{self._detailed_state}, " - f"roaming={self._roaming}, available={self._is_available})") - - def hashCode(self) -> int: - return self._type - - def equals(self, other) -> bool: - if isinstance(other, NetworkInfo): - return self._type == other._type and self._state == other._state - return False - - -# ═══════════════════════════════════════════════════════════════ -# Network -# ═══════════════════════════════════════════════════════════════ - -class Network(Stub): - """Network — 网络句柄""" - - def __init__(self, net_id: int = 0): - super().__init__() - self._net_id = net_id - - def getNetworkHandle(self) -> int: - return self._net_id - - def getNetworkCapabilities(self) -> Optional["NetworkCapabilities"]: - return None - - def getLinkProperties(self) -> Optional["LinkProperties"]: - return None - - def bindSocket(self, socket_obj: Any): - pass - - def bindSocketToNetwork(self, socket_obj: Any) -> bool: - return True - - def equals(self, other) -> bool: - if isinstance(other, Network): - return self._net_id == other._net_id - return False - - def hashCode(self) -> int: - return self._net_id - - def toString(self) -> str: - return f"Network({self._net_id})" - - -# ═══════════════════════════════════════════════════════════════ -# NetworkCapabilities -# ═══════════════════════════════════════════════════════════════ - -class NetworkCapabilities(Stub): - """NetworkCapabilities — 网络能力""" - - NET_CAPABILITY_INTERNET = 12 - NET_CAPABILITY_NOT_METERED = 11 - NET_CAPABILITY_NOT_ROAMING = 18 - NET_CAPABILITY_VALIDATED = 16 - NET_CAPABILITY_CAPTIVE_PORTAL = 17 - NET_CAPABILITY_NOT_VPN = 15 - NET_CAPABILITY_MMS = 0 - NET_CAPABILITY_SUPL = 1 - NET_CAPABILITY_DUN = 2 - NET_CAPABILITY_FOTA = 3 - NET_CAPABILITY_IMS = 4 - NET_CAPABILITY_CBS = 5 - NET_CAPABILITY_WIFI_P2P = 6 - NET_CAPABILITY_IA = 7 - NET_CAPABILITY_RCS = 8 - NET_CAPABILITY_XCAP = 9 - NET_CAPABILITY_EIMS = 10 - NET_CAPABILITY_NOT_CONGESTED = 13 - NET_CAPABILITY_NOT_RESTRICTED = 14 - NET_CAPABILITY_TRUSTED = 19 - NET_CAPABILITY_NOT_VCN_MANAGED = 20 - NET_CAPABILITY_PRIORITIZE_LATENCY = 21 - NET_CAPABILITY_PRIORITIZE_BANDWIDTH = 22 - - TRANSPORT_WIFI = 1 - TRANSPORT_CELLULAR = 0 - TRANSPORT_ETHERNET = 3 - TRANSPORT_BLUETOOTH = 2 - TRANSPORT_VPN = 4 - TRANSPORT_WIFI_AWARE = 5 - TRANSPORT_LOWPAN = 6 - TRANSPORT_TEST = 7 - - SIGNAL_STRENGTH_UNSPECIFIED = -2147483648 - - def __init__(self): - super().__init__() - self._capabilities: Set[int] = set() - self._transports: Set[int] = set() - self._link_upstream_bw = 0 - self._link_downstream_bw = 0 - self._signal_strength = self.SIGNAL_STRENGTH_UNSPECIFIED - self._network_specifier: Optional[NetworkSpecifier] = None - self._owner_uid = -1 - self._ssid: Optional[str] = None - self._administrator_uids: List[int] = [] - self._enterprise_ids: List[int] = [] - - def hasCapability(self, capability: int) -> bool: - return capability in self._capabilities - - def hasTransport(self, transport: int) -> bool: - return transport in self._transports - - def addCapability(self, capability: int) -> "NetworkCapabilities": - self._capabilities.add(capability) - return self - - def removeCapability(self, capability: int) -> "NetworkCapabilities": - self._capabilities.discard(capability) - return self - - def getCapabilities(self) -> List[int]: - return sorted(self._capabilities) - - def getLinkUpstreamBandwidthKbps(self) -> int: - return self._link_upstream_bw - - def getLinkDownstreamBandwidthKbps(self) -> int: - return self._link_downstream_bw - - def setLinkUpstreamBandwidthKbps(self, bw: int): - self._link_upstream_bw = bw - - def setLinkDownstreamBandwidthKbps(self, bw: int): - self._link_downstream_bw = bw - - def addTransportType(self, transport: int) -> "NetworkCapabilities": - self._transports.add(transport) - return self - - def removeTransportType(self, transport: int) -> "NetworkCapabilities": - self._transports.discard(transport) - return self - - def getTransportInfo(self) -> Optional[Any]: - return None - - def setNetworkSpecifier(self, specifier: NetworkSpecifier): - self._network_specifier = specifier - - def getNetworkSpecifier(self) -> Optional[NetworkSpecifier]: - return self._network_specifier - - def getSignalStrength(self) -> int: - return self._signal_strength - - def setSignalStrength(self, strength: int): - self._signal_strength = strength - - def getOwnerUid(self) -> int: - return self._owner_uid - - def setOwnerUid(self, uid: int): - self._owner_uid = uid - - def getSsid(self) -> Optional[str]: - return self._ssid - - def setSsid(self, ssid: str): - self._ssid = ssid - - def getAdministratorUids(self) -> List[int]: - return list(self._administrator_uids) - - def setAdministratorUids(self, uids: List[int]): - self._administrator_uids = list(uids) - - def equals(self, other) -> bool: - if isinstance(other, NetworkCapabilities): - return (self._capabilities == other._capabilities - and self._transports == other._transports) - return False - - def hashCode(self) -> int: - return hash(frozenset(self._capabilities)) ^ hash(frozenset(self._transports)) - - def toString(self) -> str: - caps = ",".join(str(c) for c in sorted(self._capabilities)) - trans = ",".join(str(t) for t in sorted(self._transports)) - return f"NetworkCapabilities(caps=[{caps}], transports=[{trans}])" - - -# ═══════════════════════════════════════════════════════════════ -# NetworkRequest -# ═══════════════════════════════════════════════════════════════ - -class NetworkRequest(Stub): - """NetworkRequest — 网络请求""" - - class Builder(Stub): - """Builder 模式""" - - def __init__(self): - super().__init__() - self._capabilities: Set[int] = set() - self._transports: Set[int] = set() - self._network_specifier: Optional[NetworkSpecifier] = None - self._clear_capabilities: Set[int] = set() - - def addCapability(self, capability: int) -> "NetworkRequest.Builder": - self._capabilities.add(capability) - return self - - def removeCapability(self, capability: int) -> "NetworkRequest.Builder": - self._capabilities.discard(capability) - self._clear_capabilities.add(capability) - return self - - def addTransportType(self, transport: int) -> "NetworkRequest.Builder": - self._transports.add(transport) - return self - - def removeTransportType(self, transport: int) -> "NetworkRequest.Builder": - self._transports.discard(transport) - return self - - def setNetworkSpecifier(self, specifier: NetworkSpecifier) -> "NetworkRequest.Builder": - self._network_specifier = specifier - return self - - def build(self) -> "NetworkRequest": - request = NetworkRequest() - request._capabilities = set(self._capabilities) - request._transports = set(self._transports) - request._network_specifier = self._network_specifier - request._request_id = id(request) - return request - - def __init__(self): - super().__init__() - self._capabilities: Set[int] = set() - self._transports: Set[int] = set() - self._network_specifier: Optional[NetworkSpecifier] = None - self._request_id = 0 - - def hasCapability(self, capability: int) -> bool: - return capability in self._capabilities - - def hasTransport(self, transport: int) -> bool: - return transport in self._transports - - def getCapabilities(self) -> Set[int]: - return set(self._capabilities) - - def getTransports(self) -> Set[int]: - return set(self._transports) - - def getNetworkSpecifier(self) -> Optional[NetworkSpecifier]: - return self._network_specifier - - def getRequestId(self) -> int: - return self._request_id - - def toString(self) -> str: - return f"NetworkRequest(id={self._request_id})" - - -# ═══════════════════════════════════════════════════════════════ -# NetworkCallback -# ═══════════════════════════════════════════════════════════════ - -class NetworkCallback(Stub): - """NetworkCallback — 网络回调""" - - def __init__(self, flags: int = 0): - super().__init__() - self._flags = flags - - def onAvailable(self, network: Network): - pass - - def onLost(self, network: Network): - pass - - def onCapabilitiesChanged(self, network: Network, capabilities: NetworkCapabilities): - pass - - def onLinkPropertiesChanged(self, network: Network, link_properties: "LinkProperties"): - pass - - def onNetworkSuspended(self, network: Network): - pass - - def onResumed(self, network: Network): - pass - - def onBlockedStatusChanged(self, network: Network, blocked: bool): - pass - - def onLosing(self, network: Network, max_ms_to_live: int): - pass - - def onPreCheck(self, network: Network) -> bool: - return True - - def onUnavailable(self): - pass - - -# ═══════════════════════════════════════════════════════════════ -# LinkAddress -# ═══════════════════════════════════════════════════════════════ - -class LinkAddress(Stub): - """LinkAddress — 链接地址""" - - def __init__(self, address: str = "0.0.0.0", - prefix_length: int = 24, - flags: int = 0, - scope: int = 0): - super().__init__() - self._address = address - self._prefix_length = prefix_length - self._flags = flags - self._scope = scope - self._is_ipv6 = ":" in address - - def getAddress(self) -> str: - return self._address - - def getPrefixLength(self) -> int: - return self._prefix_length - - def getFlags(self) -> int: - return self._flags - - def getScope(self) -> int: - return self._scope - - def isIpv6(self) -> bool: - return self._is_ipv6 - - def toString(self) -> str: - return f"{self._address}/{self._prefix_length}" - - def equals(self, other) -> bool: - if isinstance(other, LinkAddress): - return self._address == other._address - return False - - def hashCode(self) -> int: - return hash(self._address) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# RouteInfo -# ═══════════════════════════════════════════════════════════════ - -class RouteInfo(Stub): - """RouteInfo — 路由信息""" - - RTN_UNICAST = 1 - RTN_UNREACHABLE = 2 - RTN_BROADCAST = 3 - RTN_THROW = 4 - RTN_OTHER = 5 - - def __init__(self, destination: Optional[LinkAddress] = None, - gateway: str = "", - interface_name: str = "", - is_default: bool = False, - type_: int = RTN_UNICAST): - super().__init__() - self._destination = destination - self._gateway = gateway - self._interface_name = interface_name - self._is_default = is_default - self._type = type_ - - def getDestination(self) -> Optional[LinkAddress]: - return self._destination - - def getGateway(self) -> str: - return self._gateway - - def getInterface(self) -> str: - return self._interface_name - - def getType(self) -> int: - return self._type - - def isDefaultRoute(self) -> bool: - return self._is_default - - def isIPv4Default(self) -> bool: - return self._is_default and self._destination is not None and not self._destination.isIpv6() - - def isIPv6Default(self) -> bool: - return self._is_default and self._destination is not None and self._destination.isIpv6() - - def toString(self) -> str: - dest = str(self._destination) if self._destination else "0.0.0.0/0" - gw = self._gateway or "0.0.0.0" - return f"RouteInfo({dest} -> {gw} via {self._interface_name})" - - def equals(self, other) -> bool: - if isinstance(other, RouteInfo): - return (self._gateway == other._gateway - and self._interface_name == other._interface_name) - return False - - def hashCode(self) -> int: - return hash(self._gateway) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# LinkProperties -# ═══════════════════════════════════════════════════════════════ - -class LinkProperties(Stub): - """LinkProperties — 链接属性""" - - def __init__(self): - super().__init__() - self._link_addresses: List[LinkAddress] = [] - self._routes: List[RouteInfo] = [] - self._dns_servers: List[str] = [] - self._interface_name: str = "" - self._mtu: int = 1500 - self._http_proxy: Optional[ProxyInfo] = None - self._private_dns_server_name: str = "" - self._private_dns_active: bool = False - self._domains: str = "" - self._pcscf: List[str] = [] - - def getLinkAddresses(self) -> List[LinkAddress]: - return list(self._link_addresses) - - def getRoutes(self) -> List[RouteInfo]: - return list(self._routes) - - def getDnsServers(self) -> List[str]: - return list(self._dns_servers) - - def getInterfaceName(self) -> str: - return self._interface_name - - def setInterfaceName(self, name: str): - self._interface_name = name - - def addLinkAddress(self, address: LinkAddress): - if address not in self._link_addresses: - self._link_addresses.append(address) - - def removeLinkAddress(self, address: LinkAddress): - self._link_addresses = [a for a in self._link_addresses if a != address] - - def addRoute(self, route: RouteInfo): - if route not in self._routes: - self._routes.append(route) - - def removeRoute(self, route: RouteInfo): - self._routes = [r for r in self._routes if r != route] - - def addDnsServer(self, dns: str): - if dns not in self._dns_servers: - self._dns_servers.append(dns) - - def removeDnsServer(self, dns: str): - self._dns_servers = [d for d in self._dns_servers if d != dns] - - def getMtu(self) -> int: - return self._mtu - - def setMtu(self, mtu: int): - self._mtu = mtu - - def getHttpProxy(self) -> Optional["ProxyInfo"]: - return self._http_proxy - - def setHttpProxy(self, proxy: "ProxyInfo"): - self._http_proxy = proxy - - def getPrivateDnsServerName(self) -> str: - return self._private_dns_server_name - - def setPrivateDnsServerName(self, name: str): - self._private_dns_server_name = name - self._private_dns_active = bool(name) - - def isPrivateDnsActive(self) -> bool: - return self._private_dns_active - - def setLinkAddresses(self, addresses: List[LinkAddress]): - self._link_addresses = list(addresses) - - def setDnsServers(self, dns_servers: List[str]): - self._dns_servers = list(dns_servers) - - def getDomains(self) -> str: - return self._domains - - def setDomains(self, domains: str): - self._domains = domains - - def getPcscf(self) -> List[str]: - return list(self._pcscf) - - def addPcscf(self, addr: str): - if addr not in self._pcscf: - self._pcscf.append(addr) - - def setMtuV4(int_mtu: int): - pass - - def setMtuV6(int_mtu: int): - pass - - def clearLinkAddresses(self): - self._link_addresses.clear() - - def clearRoutes(self): - self._routes.clear() - - def clearDnsServers(self): - self._dns_servers.clear() - - def toString(self) -> str: - iface = self._interface_name or "none" - return f"LinkProperties(iface={iface}, mtu={self._mtu}, dns={self._dns_servers})" - - def equals(self, other) -> bool: - if isinstance(other, LinkProperties): - return (self._interface_name == other._interface_name - and self._mtu == other._mtu) - return False - - def hashCode(self) -> int: - return hash(self._interface_name) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# DnsResolver -# ═══════════════════════════════════════════════════════════════ - -class DnsResolver(Stub): - """DnsResolver — DNS 解析器""" - - CLASS_IN = 1 - TYPE_A = 1 - TYPE_AAAA = 28 - - @staticmethod - def getInstance() -> "DnsResolver": - return DnsResolver() - - def query(self, hostname: str, callback: Any, flags: int = 0): - try: - result = socket.getaddrinfo(hostname, None) - callback.onAnswer(None, 0, result) - except socket.gaierror: - callback.onAnswer(None, -1, None) - - -# ═══════════════════════════════════════════════════════════════ -# DhcpInfo -# ═══════════════════════════════════════════════════════════════ - -class DhcpInfo(Stub): - """DhcpInfo — DHCP 信息""" - - def __init__(self): - super().__init__() - self.ipAddress = 0 - self.gateway = 0 - self.netmask = 0 - self.dns1 = 0 - self.dns2 = 0 - self.serverAddress = 0 - self.leaseDuration = 0 - - def toString(self) -> str: - return (f"DhcpInfo(ip={self.ipAddress}, gw={self.gateway}, " - f"mask={self.netmask}, dns1={self.dns1}, dns2={self.dns2})") - - -# ═══════════════════════════════════════════════════════════════ -# SSLSessionCache -# ═══════════════════════════════════════════════════════════════ - -class SSLSessionCache(Stub): - """SSLSessionCache — SSL 会话缓存""" - - def __init__(self, context_or_dir: Any): - super().__init__() - self._dir: Optional[str] = None - if isinstance(context_or_dir, str): - self._dir = context_or_dir - else: - self._dir = os.path.join(_app_data_dir, "ssl_cache") - - def getDir(self) -> Optional[str]: - return self._dir - - -# ═══════════════════════════════════════════════════════════════ -# TrafficStats -# ═══════════════════════════════════════════════════════════════ - -class TrafficStats(Stub): - """TrafficStats — 流量统计""" - - UID_TETHERING = "UNSUPPORTED" - UNSUPPORTED = -1 - TAG_SYSTEM_UPDATE = 0 - - _cached_stats = None - _cache_time = 0 - - @staticmethod - def _read_net_dev(): - """读取 /proc/net/dev 获取网络流量""" - try: - with open("/proc/net/dev", "r") as f: - lines = f.readlines()[2:] # 跳过前两行表头 - total_rx = 0 - total_tx = 0 - for line in lines: - parts = line.split() - ifname = parts[0].rstrip(":") - if ifname == "lo": - continue - rx_bytes = int(parts[1]) - tx_bytes = int(parts[9]) - total_rx += rx_bytes - total_tx += tx_bytes - return {"rx_bytes": total_rx, "tx_bytes": total_tx, - "rx_packets": 0, "tx_packets": 0} - except Exception: - return {"rx_bytes": 0, "tx_bytes": 0, "rx_packets": 0, "tx_packets": 0} - - @staticmethod - def getMobileRxBytes(): - return 0 # 桌面无移动数据 - - @staticmethod - def getMobileTxBytes(): - return 0 - - @staticmethod - def getMobileRxPackets(): - return 0 - - @staticmethod - def getMobileTxPackets(): - return 0 - - @staticmethod - def getTotalRxBytes(): - return TrafficStats._read_net_dev()["rx_bytes"] - - @staticmethod - def getTotalTxBytes(): - return TrafficStats._read_net_dev()["tx_bytes"] - - @staticmethod - def getTotalRxPackets(): - return TrafficStats._read_net_dev()["rx_packets"] - - @staticmethod - def getTotalTxPackets(): - return TrafficStats._read_net_dev()["tx_packets"] - - @staticmethod - def getUidRxBytes(uid): - try: - with open(f"/proc/uid_stat/{uid}/tcp_rcv", "r") as f: - return int(f.read().strip()) - except Exception: - return 0 - - @staticmethod - def getUidTxBytes(uid): - try: - with open(f"/proc/uid_stat/{uid}/tcp_snd", "r") as f: - return int(f.read().strip()) - except Exception: - return 0 - - @staticmethod - def getUidRxPackets(uid): - return TrafficStats.getUidRxBytes(uid) - - @staticmethod - def getUidTxPackets(uid): - return TrafficStats.getUidTxBytes(uid) - - @staticmethod - def getThreadStatsTag(): - return getattr(TrafficStats, '_thread_tag', 0) - - @staticmethod - def setThreadStatsTag(tag): - TrafficStats._thread_tag = tag - - @staticmethod - def clearThreadStatsTag(): - TrafficStats._thread_tag = 0 - - @staticmethod - def getUidTcpTxBytes(uid: int) -> int: - return 0 - - @staticmethod - def getUidTcpRxBytes(uid: int) -> int: - return 0 - - @staticmethod - def getUidUdpTxBytes(uid: int) -> int: - return 0 - - @staticmethod - def getUidUdpRxBytes(uid: int) -> int: - return 0 - - @staticmethod - def setThreadStatsUid(uid: int): - pass - - @staticmethod - def clearThreadStatsUid(): - pass - - @staticmethod - def incrementOperationCount(uid: int, tag: int, count: int): - pass - - -# ═══════════════════════════════════════════════════════════════ -# Proxy / ProxyInfo -# ═══════════════════════════════════════════════════════════════ - -class Proxy(Stub): - """Proxy — 系统代理信息""" - - @staticmethod - def getDefaultHost() -> Optional[str]: - return None - - @staticmethod - def getDefaultPort() -> int: - return -1 - - -class ProxyInfo(Stub): - """ProxyInfo — 代理详细信息""" - - def __init__(self, host: str = "", port: int = 0, - pac_file_url: Optional[str] = None, - username: str = "", password: str = "", - exclusion_list: Optional[List[str]] = None): - super().__init__() - self._host = host - self._port = port - self._pac_file_url = pac_file_url - self._username = username - self._password = password - self._exclusion_list = list(exclusion_list) if exclusion_list else [] - self._valid = bool(host) or bool(pac_file_url) - - def getHost(self) -> str: - return self._host - - def getPort(self) -> int: - return self._port - - def getPacFileUrl(self) -> Optional[str]: - return self._pac_file_url - - def getUsername(self) -> str: - return self._username - - def getPassword(self) -> str: - return self._password - - def getExclusionList(self) -> List[str]: - return list(self._exclusion_list) - - def isValid(self) -> bool: - return self._valid - - def makeProxy(self, url: str): - return self - - def equals(self, other) -> bool: - if isinstance(other, ProxyInfo): - return self._host == other._host and self._port == other._port - return False - - def hashCode(self) -> int: - return hash(self._host) ^ self._port - - def toString(self) -> str: - pac = f" pac={self._pac_file_url}" if self._pac_file_url else "" - return f"ProxyInfo(host={self._host}:{self._port}{pac})" - - -# ═══════════════════════════════════════════════════════════════ -# ConnectivityManager -# ═══════════════════════════════════════════════════════════════ - -class ConnectivityManager(Stub): - """ConnectivityManager — 连接管理器""" - - TYPE_WIFI = 1 - TYPE_MOBILE = 0 - TYPE_BLUETOOTH = 7 - TYPE_ETHERNET = 9 - TYPE_VPN = 17 - TYPE_WIMAX = 6 - TYPE_DUMMY = 8 - TYPE_PROXY = 16 - - CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE" - - DEFAULT_NETWORK_PREFERENCE = 1 - - def __init__(self): - super().__init__() - self._networks: Dict[int, "Network"] = {} - self._callbacks: List[Tuple[NetworkCallback, Optional[NetworkRequest]]] = [] - self._active_network_info = NetworkInfo( - type_=self.TYPE_WIFI, state="CONNECTED", detailed_state="CONNECTED", - type_name="WIFI", is_available=True, - ) - self._net_id_counter = 0 - self._network_caps: Dict[int, NetworkCapabilities] = {} - self._link_props: Dict[int, LinkProperties] = {} - - def _get_linux_active_info(self) -> Optional[NetworkInfo]: - """从 Linux 真实网络信息构造活跃 NetworkInfo""" - try: - interfaces = LinuxNetwork.get_interfaces() - # 有真实网络接口 (非环回) 且 operstate=up 即视为已连接 - connected = any( - (iface.get("ifname") or iface.get("name", "")) != "lo" - and iface.get("operstate", "").lower() == "up" - for iface in interfaces - ) - # 若有活跃接口但 get_connectivity 为 False (外网不可达), 仍视为已连接 - if not connected: - connected = LinuxNetwork.get_connectivity() - ip = LinuxNetwork.get_ip_address() - iface_name = "" - for iface in interfaces: - name = iface.get("ifname") or iface.get("name", "") - state = iface.get("operstate", "") - if name and name != "lo" and state.lower() == "up": - iface_name = name - break - # 判断网络类型 (无线接口 -> WIFI, 否则 ETHERNET) - net_type = self.TYPE_ETHERNET - for iface in interfaces: - name = iface.get("ifname") or iface.get("name", "") - if name and name.startswith(("wl", "wlan", "wlp")): - net_type = self.TYPE_WIFI - iface_name = iface_name or name - break - data = { - "connected": connected, - "ip": ip, - "interface": iface_name, - "type_name": "WIFI" if net_type == self.TYPE_WIFI else "ETHERNET", - } - return NetworkInfo.from_linux_data(data, network_type=net_type) - except Exception: - return self._active_network_info - - def getActiveNetworkInfo(self) -> Optional[NetworkInfo]: - if _USE_LINUX: - return self._get_linux_active_info() - return self._active_network_info - - def getNetworkInfo(self, network_type: int) -> Optional[NetworkInfo]: - if _USE_LINUX: - info = self._get_linux_active_info() - if info is not None and info.getType() == network_type: - return info - # 请求的类型与活跃网络不同时, 返回断开状态 - return NetworkInfo( - type_=network_type, state="DISCONNECTED", - detailed_state="DISCONNECTED", is_available=False, - ) - network = self._networks.get(network_type) - if network: - return NetworkInfo(type_=network_type, state="CONNECTED", is_available=True) - return self._active_network_info - - def getConnectivityStatus(self) -> bool: - """返回当前网络连通性 (True=已连接)""" - if _USE_LINUX: - return LinuxNetwork.get_connectivity() - return True - - def getNetworkInfoForUid(self, network: Network, uid: int) -> Optional[NetworkInfo]: - return self._active_network_info - - def getAllNetworks(self) -> List[Network]: - if _USE_LINUX: - networks: List[Network] = [] - try: - interfaces = LinuxNetwork.get_interfaces() - idx = 0 - for iface in interfaces: - name = iface.get("ifname") or iface.get("name", "") - if not name or name == "lo": - continue - idx += 1 - net = Network(idx) - networks.append(net) - # 若无真实接口, 回退到内部缓存 - if networks: - return networks - except Exception: - pass - return list(self._networks.values()) - - def getActiveNetwork(self) -> Optional[Network]: - if _USE_LINUX: - try: - interfaces = LinuxNetwork.get_interfaces() - for iface in interfaces: - name = iface.get("ifname") or iface.get("name", "") - state = iface.get("operstate", "") - if name and name != "lo" and state.lower() == "up": - net = Network(1) - self._networks[1] = net - return net - # 回退: 若有外网连通性, 仍返回一个活跃 Network - if LinuxNetwork.get_connectivity(): - net = Network(1) - self._networks[1] = net - return net - except Exception: - pass - if self._networks: - nid = next(iter(self._networks)) - return self._networks[nid] - net = Network(1) - self._networks[1] = net - return net - - def getActiveNetworkForUid(self, uid: int) -> Optional[Network]: - return self.getActiveNetwork() - - def getNetworkCapabilities(self, network: Network) -> Optional[NetworkCapabilities]: - caps = self._network_caps.get(network.getNetworkHandle()) - if caps is None: - caps = NetworkCapabilities() - caps.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - caps.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - caps.addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) - caps.addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - self._network_caps[network.getNetworkHandle()] = caps - return caps - - def getLinkProperties(self, network: Network) -> Optional[LinkProperties]: - lp = self._link_props.get(network.getNetworkHandle()) - if lp is None: - lp = LinkProperties() - self._link_props[network.getNetworkHandle()] = lp - return lp - - def requestNetwork(self, request: NetworkRequest, callback: NetworkCallback, timeout_ms: int = 0, - handler: Any = None, network_specifier: int = 0) -> None: - self._callbacks.append((callback, request)) - net = self.getActiveNetwork() - if net: - try: - callback.onAvailable(net) - except Exception: - pass - - def registerDefaultNetworkCallback(self, callback: NetworkCallback, handler: Any = None) -> None: - self._callbacks.append((callback, None)) - net = self.getActiveNetwork() - if net: - try: - callback.onAvailable(net) - except Exception: - pass - - def registerNetworkCallback(self, request: NetworkRequest, callback: NetworkCallback, - handler: Any = None) -> None: - self._callbacks.append((callback, request)) - net = self.getActiveNetwork() - if net: - try: - callback.onAvailable(net) - except Exception: - pass - - def unregisterNetworkCallback(self, callback_or_request: Any): - if isinstance(callback_or_request, NetworkCallback): - self._callbacks = [ - (cb, req) for cb, req in self._callbacks - if cb is not callback_or_request - ] - elif isinstance(callback_or_request, NetworkRequest): - self._callbacks = [ - (cb, req) for cb, req in self._callbacks - if req is not callback_or_request - ] - - def bindProcessToNetwork(self, network: Optional[Network]) -> bool: - return True - - def getProcessDefaultNetwork(self) -> Optional[Network]: - return self.getActiveNetwork() - - @staticmethod - def setProcessDefaultNetwork(network: Optional[Network]) -> bool: - return True - - def getAllNetworkInfo(self) -> List[NetworkInfo]: - return [self._active_network_info] - - def getNetworkInfoForProcess(self) -> Optional[NetworkInfo]: - return self._active_network_info - - def isDefaultNetworkActive(self) -> bool: - return True - - def addDefaultNetworkActiveListener(self, listener: Any): - pass - - def removeDefaultNetworkActiveListener(self, listener: Any): - pass - - def reportBadNetwork(self, network: Network): - pass - - def setNetworkPreference(self, preference: int): - pass - - def getNetworkPreference(self) -> int: - return self.DEFAULT_NETWORK_PREFERENCE - - def startCaptivePortalApp(self, network: Network): - pass - - def setProcessDefaultNetworkForUid(self, uid: int) -> bool: - return True - - def registerNetworkAgent(self, agent: Any) -> int: - return 0 - - def toString(self) -> str: - return f"ConnectivityManager(networks={len(self._networks)})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Uri", - "Uri_Builder", - "ConnectivityManager", - "NetworkInfo", - "Network", - "NetworkCapabilities", - "NetworkRequest", - "NetworkCallback", - "LinkProperties", - "LinkAddress", - "RouteInfo", - "DnsResolver", - "DhcpInfo", - "InetAddresses", - "SSLSessionCache", - "TrafficStats", - "Proxy", - "ProxyInfo", - "NetworkSpecifier", - "MatchAllNetworkSpecifier", - "WifiAwareNetworkSpecifier", - "EthernetNetworkSpecifier", - "TelephonyNetworkSpecifier", - "NetworkState", - "LocalSocket", - "LocalSocketAddress", - "NetworkUtils", -] \ No newline at end of file diff --git a/android_pyapi/android/net_ethernet.py b/android_pyapi/android/net_ethernet.py deleted file mode 100644 index 1e55431..0000000 --- a/android_pyapi/android/net_ethernet.py +++ /dev/null @@ -1,551 +0,0 @@ -""" -Android net 以太网组件 — EthernetManager, EthernetNetworkSpecifier, -EthernetConfig, IpConfiguration, StaticIpConfiguration -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError - -__all__ = [ - "EthernetManager", - "EthernetNetworkSpecifier", - "EthernetNetworkUpdate", - "EthernetConfig", - "IpConfiguration", - "StaticIpConfiguration", -] - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -ETH_STATE_DISCONNECTED = 0 -ETH_STATE_CONNECTING = 1 -ETH_STATE_CONNECTED = 2 -ETH_STATE_SUSPENDED = 3 - -ETH_MODE_DHCP = 0 -ETH_MODE_STATIC = 1 - -ETH_LINK_MODE_AUTO = 0 -ETH_LINK_MODE_10_HALF = 1 -ETH_LINK_MODE_10_FULL = 2 -ETH_LINK_MODE_100_HALF = 3 -ETH_LINK_MODE_100_FULL = 4 -ETH_LINK_MODE_1000_HALF = 5 -ETH_LINK_MODE_1000_FULL = 6 -ETH_LINK_MODE_2500_FULL = 7 -ETH_LINK_MODE_5000_FULL = 8 -ETH_LINK_MODE_10000_FULL = 9 - -ETH_ERROR_NONE = 0 -ETH_ERROR_INTERFACE_NOT_FOUND = -1 -ETH_ERROR_CONFIG_FAILED = -2 -ETH_ERROR_LINK_DOWN = -3 -ETH_ERROR_NOT_SUPPORTED = -4 -ETH_ERROR_PERMISSION_DENIED = -5 - -ETH_CAPABILITY_WOL = 1 -ETH_CAPABILITY_VLAN = 2 -ETH_CAPABILITY_JUMBO_FRAMES = 4 -ETH_CAPABILITY_EEE = 8 -ETH_CAPABILITY_PTP = 16 - -ETH_DUPLEX_HALF = 0 -ETH_DUPLEX_FULL = 1 -ETH_DUPLEX_AUTO = 2 - -ETH_AUTH_NONE = 0 -ETH_AUTH_8021X = 1 -ETH_AUTH_MAB = 2 - - -# ═══════════════════════════════════════════════════════════════ -# IpConfiguration -# ═══════════════════════════════════════════════════════════════ - -class IpConfiguration(Stub): - """IpConfiguration — IP 配置""" - - class IpAssignment(Stub): - """IpConfiguration.IpAssignment — IP 分配方式""" - DHCP = 0 - STATIC = 1 - UNASSIGNED = 2 - - class ProxySettings(Stub): - """IpConfiguration.ProxySettings — 代理设置""" - NONE = 0 - STATIC = 1 - PAC = 2 - UNASSIGNED = 3 - - def __init__(self, ipAssignment: int = IpAssignment.DHCP, - proxySettings: int = ProxySettings.NONE, - staticConfig: Optional["StaticIpConfiguration"] = None): - super().__init__() - self._ip_assignment = ipAssignment - self._proxy_settings = proxySettings - self._static_config = staticConfig - - def getIpAssignment(self) -> int: - return self._ip_assignment - - def setIpAssignment(self, assignment: int): - self._ip_assignment = assignment - - def getProxySettings(self) -> int: - return self._proxy_settings - - def setProxySettings(self, settings: int): - self._proxy_settings = settings - - def getStaticIpConfiguration(self) -> Optional["StaticIpConfiguration"]: - return self._static_config - - def setStaticIpConfiguration(self, config: "StaticIpConfiguration"): - self._static_config = config - - def toString(self) -> str: - assignments = {0: "DHCP", 1: "STATIC", 2: "UNASSIGNED"} - return f"IpConfiguration(assignment={assignments.get(self._ip_assignment, str(self._ip_assignment))})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, IpConfiguration): - return False - return self._ip_assignment == other._ip_assignment - - def hashCode(self) -> int: - return self._ip_assignment & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# StaticIpConfiguration -# ═══════════════════════════════════════════════════════════════ - -class StaticIpConfiguration(Stub): - """StaticIpConfiguration — 静态 IP 配置""" - - def __init__(self, ipAddress: str = "192.168.1.100", - gateway: str = "192.168.1.1", - prefixLength: int = 24, - dnsServers: List[str] = None, - domains: str = ""): - super().__init__() - self._ip_address = ipAddress - self._gateway = gateway - self._prefix_length = prefixLength - self._dns_servers = dnsServers or ["8.8.8.8", "8.8.4.4"] - self._domains = domains - - def getIpAddress(self) -> str: - return self._ip_address - - def setIpAddress(self, ip: str): - self._ip_address = ip - - def getGateway(self) -> str: - return self._gateway - - def setGateway(self, gateway: str): - self._gateway = gateway - - def getPrefixLength(self) -> int: - return self._prefix_length - - def setPrefixLength(self, prefixLength: int): - self._prefix_length = prefixLength - - def getDnsServers(self) -> List[str]: - return list(self._dns_servers) - - def setDnsServers(self, dnsServers: List[str]): - self._dns_servers = list(dnsServers) - - def addDnsServer(self, server: str): - self._dns_servers.append(server) - - def getDomains(self) -> str: - return self._domains - - def setDomains(self, domains: str): - self._domains = domains - - class Builder(Stub): - """StaticIpConfiguration.Builder — 静态 IP 构建器""" - - def __init__(self): - super().__init__() - self._ip_address = "" - self._gateway = "" - self._prefix_length = 24 - self._dns_servers: List[str] = [] - self._domains = "" - - def setIpAddress(self, ip: str) -> "StaticIpConfiguration.Builder": - self._ip_address = ip - return self - - def setGateway(self, gateway: str) -> "StaticIpConfiguration.Builder": - self._gateway = gateway - return self - - def setPrefixLength(self, prefixLength: int) -> "StaticIpConfiguration.Builder": - self._prefix_length = prefixLength - return self - - def setDnsServers(self, dnsServers: List[str]) -> "StaticIpConfiguration.Builder": - self._dns_servers = list(dnsServers) - return self - - def setDomains(self, domains: str) -> "StaticIpConfiguration.Builder": - self._domains = domains - return self - - def build(self) -> "StaticIpConfiguration": - return StaticIpConfiguration( - ipAddress=self._ip_address, gateway=self._gateway, - prefixLength=self._prefix_length, - dnsServers=self._dns_servers, domains=self._domains, - ) - - def toString(self) -> str: - return f"StaticIpConfiguration(ip={self._ip_address}, gateway={self._gateway})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, StaticIpConfiguration): - return False - return self._ip_address == other._ip_address - - def hashCode(self) -> int: - return hash(self._ip_address) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# EthernetNetworkSpecifier -# ═══════════════════════════════════════════════════════════════ - -class EthernetNetworkSpecifier(Stub): - """EthernetNetworkSpecifier — 以太网网络指定器""" - - def __init__(self, interfaceName: str = ""): - super().__init__() - self._interface_name = interfaceName - - def getInterfaceName(self) -> str: - return self._interface_name - - def satisfiedBy(self, other: Optional["EthernetNetworkSpecifier"]) -> bool: - if other is None: - return False - return self._interface_name == other._interface_name - - def toString(self) -> str: - return f"EthernetNetworkSpecifier(iface={self._interface_name})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, EthernetNetworkSpecifier): - return False - return self._interface_name == other._interface_name - - def hashCode(self) -> int: - return hash(self._interface_name) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# EthernetNetworkUpdate -# ═══════════════════════════════════════════════════════════════ - -class EthernetNetworkUpdate(Stub): - """EthernetNetworkUpdate — 以太网网络更新""" - - def __init__(self, interfaceName: str = "", - state: int = ETH_STATE_DISCONNECTED, - config: Optional["EthernetConfig"] = None): - super().__init__() - self._interface_name = interfaceName - self._state = state - self._config = config - - def getInterfaceName(self) -> str: - return self._interface_name - - def getState(self) -> int: - return self._state - - def getConfig(self) -> Optional["EthernetConfig"]: - return self._config - - def toString(self) -> str: - return f"EthernetNetworkUpdate(iface={self._interface_name}, state={self._state})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, EthernetNetworkUpdate): - return False - return self._interface_name == other._interface_name - - def hashCode(self) -> int: - return hash(self._interface_name) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# EthernetConfig -# ═══════════════════════════════════════════════════════════════ - -class EthernetConfig(Stub): - """EthernetConfig — 以太网配置""" - - def __init__(self, ipConfig: Optional[IpConfiguration] = None, - linkMode: int = ETH_LINK_MODE_AUTO, - interfaceName: str = "", - mtu: int = 1500, - macAddress: str = ""): - super().__init__() - self._ip_config = ipConfig or IpConfiguration() - self._link_mode = linkMode - self._interface_name = interfaceName - self._mtu = mtu - self._mac_address = macAddress - - def getIpConfiguration(self) -> IpConfiguration: - return self._ip_config - - def setIpConfiguration(self, config: IpConfiguration): - self._ip_config = config - - def getLinkMode(self) -> int: - return self._link_mode - - def setLinkMode(self, mode: int): - self._link_mode = mode - - def getInterfaceName(self) -> str: - return self._interface_name - - def setInterfaceName(self, name: str): - self._interface_name = name - - def getMtu(self) -> int: - return self._mtu - - def setMtu(self, mtu: int): - self._mtu = mtu - - def getMacAddress(self) -> str: - return self._mac_address - - def setMacAddress(self, mac: str): - self._mac_address = mac - - class Builder(Stub): - """EthernetConfig.Builder — 以太网配置构建器""" - - def __init__(self): - super().__init__() - self._ip_config = IpConfiguration() - self._link_mode = ETH_LINK_MODE_AUTO - self._interface_name = "" - self._mtu = 1500 - self._mac_address = "" - - def setIpConfiguration(self, config: IpConfiguration) -> "EthernetConfig.Builder": - self._ip_config = config - return self - - def setLinkMode(self, mode: int) -> "EthernetConfig.Builder": - self._link_mode = mode - return self - - def setInterfaceName(self, name: str) -> "EthernetConfig.Builder": - self._interface_name = name - return self - - def setMtu(self, mtu: int) -> "EthernetConfig.Builder": - self._mtu = mtu - return self - - def setMacAddress(self, mac: str) -> "EthernetConfig.Builder": - self._mac_address = mac - return self - - def build(self) -> "EthernetConfig": - return EthernetConfig( - ipConfig=self._ip_config, linkMode=self._link_mode, - interfaceName=self._interface_name, mtu=self._mtu, - macAddress=self._mac_address, - ) - - def toString(self) -> str: - return f"EthernetConfig(iface={self._interface_name}, linkMode={self._link_mode})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, EthernetConfig): - return False - return self._interface_name == other._interface_name - - def hashCode(self) -> int: - return hash(self._interface_name) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# EthernetManager -# ═══════════════════════════════════════════════════════════════ - -class EthernetManager(Stub): - """EthernetManager — 以太网管理器""" - - ACTION_ETHERNET_STATE_CHANGED = "android.net.ethernet.ETHERNET_STATE_CHANGED" - EXTRA_ETHERNET_STATE = "ethernet_state" - EXTRA_INTERFACE_NAME = "interface_name" - EXTRA_LINK_MODE = "link_mode" - - STATE_DISABLED = 0 - STATE_ENABLED = 1 - - def __init__(self): - super().__init__() - self._enabled = False - self._interfaces: Dict[str, EthernetConfig] = {} - self._state = EthernetManager.STATE_DISABLED - self._listeners: Dict[int, Callable] = {} - - def isEnabled(self) -> bool: - return self._enabled - - def setEnabled(self, enabled: bool): - self._enabled = enabled - self._state = EthernetManager.STATE_ENABLED if enabled else EthernetManager.STATE_DISABLED - - def getState(self) -> int: - return self._state - - def getConfiguration(self, interfaceName: str) -> Optional["EthernetConfig"]: - return self._interfaces.get(interfaceName) - - def setConfiguration(self, interfaceName: str, config: "EthernetConfig"): - self._interfaces[interfaceName] = config - - def removeInterface(self, interfaceName: str): - self._interfaces.pop(interfaceName, None) - - def getInterfaceNames(self) -> List[str]: - return list(self._interfaces.keys()) - - def addInterface(self, interfaceName: str, config: Optional[EthernetConfig] = None): - self._interfaces[interfaceName] = config or EthernetConfig(interfaceName=interfaceName) - - def setLinkMode(self, interfaceName: str, mode: int): - config = self._interfaces.get(interfaceName) - if config: - config.setLinkMode(mode) - - def updateConfiguration(self, update: EthernetNetworkUpdate) -> bool: - iface = update.getInterfaceName() - config = update.getConfig() - if config: - self._interfaces[iface] = config - elif iface in self._interfaces: - pass - return True - - def registerEthernetEventListener(self, listener: Callable): - lid = id(listener) - self._listeners[lid] = listener - - def unregisterEthernetEventListener(self, listener: Callable): - lid = id(listener) - self._listeners.pop(lid, None) - - def getAvailableInterfaces(self) -> List[str]: - import socket - import netifaces - interfaces = [] - try: - import netifaces - for iface in netifaces.interfaces(): - if iface.startswith(("eth", "en", "em")): - interfaces.append(iface) - except ImportError: - import psutil - try: - import psutil - stats = psutil.net_if_stats() - for name in stats: - if name.startswith(("eth", "en", "em")): - interfaces.append(name) - except ImportError: - pass - return interfaces + list(self._interfaces.keys()) - - def isInterfaceAvailable(self, interfaceName: str) -> bool: - return interfaceName in self._interfaces - - def toString(self) -> str: - return f"EthernetManager(enabled={self._enabled}, interfaces={len(self._interfaces)})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def getInterfaceCount(self) -> int: - return len(self._interfaces) - - def hasInterface(self, interfaceName: str) -> bool: - return interfaceName in self._interfaces - - def getInterfaceNames(self) -> List[str]: - return list(self._interfaces.keys()) - - def getEnabledInterfaces(self) -> List[str]: - return [name for name, cfg in self._interfaces.items() - if cfg.getLinkMode() != ETH_STATE_DISCONNECTED] - - def getInterfaceConfig(self, interfaceName: str) -> Optional[EthernetConfig]: - return self._interfaces.get(interfaceName) - - def setInterfaceUp(self, interfaceName: str) -> bool: - config = self._interfaces.get(interfaceName) - if config: - config.setLinkMode(ETH_LINK_MODE_AUTO) - return True - return False - - def setInterfaceDown(self, interfaceName: str) -> bool: - config = self._interfaces.get(interfaceName) - if config: - config.setLinkMode(ETH_STATE_DISCONNECTED) - return True - return False - - def getLinkMode(self, interfaceName: str) -> int: - config = self._interfaces.get(interfaceName) - return config.getLinkMode() if config else ETH_STATE_DISCONNECTED - - def getMtu(self, interfaceName: str) -> int: - config = self._interfaces.get(interfaceName) - return config.getMtu() if config else 0 - - def setMtu(self, interfaceName: str, mtu: int) -> bool: - config = self._interfaces.get(interfaceName) - if config: - config.setMtu(mtu) - return True - return False - - def getMacAddress(self, interfaceName: str) -> str: - config = self._interfaces.get(interfaceName) - return config.getMacAddress() if config else "" - - def setMacAddress(self, interfaceName: str, mac: str) -> bool: - config = self._interfaces.get(interfaceName) - if config: - config.setMacAddress(mac) - return True - return False \ No newline at end of file diff --git a/android_pyapi/android/net_ext.py b/android_pyapi/android/net_ext.py deleted file mode 100644 index 0d3c499..0000000 --- a/android_pyapi/android/net_ext.py +++ /dev/null @@ -1,1447 +0,0 @@ -""" -Android net 网络扩展组件 — Network, NetworkCapabilities, NetworkRequest, -NetworkAgent, LinkProperties, TrafficStats, ProxyInfo, DhcpInfo, -ConnectivityDiagnosticsManager, ConnectivityManager 扩展等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import socket -import struct -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError -from .net import ConnectivityManager as _BaseConnectivityManager -from .net import DhcpInfo as _BaseDhcpInfo -from .net import LinkAddress as _BaseLinkAddress -from .net import LinkProperties as _BaseLinkProperties -from .net import Network as _BaseNetwork -from .net import NetworkCapabilities as _BaseNetworkCapabilities -from .net import NetworkInfo as _BaseNetworkInfo -from .net import NetworkRequest as _BaseNetworkRequest -from .net import NetworkSpecifier as _BaseNetworkSpecifier -from .net import ProxyInfo as _BaseProxyInfo -from .net import TrafficStats as _BaseTrafficStats -from .net import MatchAllNetworkSpecifier as _BaseMatchAllNetworkSpecifier - -# Linux 真实实现导入 -try: - from .._linux_impl import IS_LINUX, LinuxNetwork, HAS_IP - _USE_LINUX = IS_LINUX -except ImportError: - _USE_LINUX = False - -__all__ = [ - "Network", "NetworkCapabilities", "NetworkRequest", "NetworkSpecifier", - "MatchAllNetworkSpecifier", "NetworkAgent", "NetworkInfo", - "LinkProperties", "LinkAddress", "LinkQualityInfo", - "TrafficStats", "ProxyInfo", "DhcpInfo", "DhcpInfoInternal", - "WifiLinkQualityInfo", "EthernetLinkQualityInfo", "MobileLinkQualityInfo", - "NetworkBadging", "ConnectivityDiagnosticsManager", - "ConnectivityManager", -] - - -# ═══════════════════════════════════════════════════════════════ -# Network -# ═══════════════════════════════════════════════════════════════ - -class Network(_BaseNetwork): - """Network — 网络""" - - def __init__(self, netId: int = 0): - super().__init__() - self._net_id = netId - self._capabilities: Optional["NetworkCapabilities"] = None - self._link_properties: Optional["LinkProperties"] = None - - def getNetId(self) -> int: - return self._net_id - - def getNetworkCapabilities(self) -> Optional["NetworkCapabilities"]: - return self._capabilities - - def getLinkProperties(self) -> Optional["LinkProperties"]: - return self._link_properties - - def setCapabilities(self, caps: "NetworkCapabilities"): - self._capabilities = caps - - def setLinkProperties(self, props: "LinkProperties"): - self._link_properties = props - - def bindSocket(self, socketObj: Any): - """绑定 socket 到该网络""" - raise AndroidOnlyError("Network.bindSocket() is not available on desktop") - - def toString(self) -> str: - return f"Network(netId={self._net_id})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, Network): - return False - return self._net_id == other._net_id - - def hashCode(self) -> int: - return self._net_id & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# NetworkCapabilities -# ═══════════════════════════════════════════════════════════════ - -class NetworkCapabilities(_BaseNetworkCapabilities): - """NetworkCapabilities — 网络能力""" - - TRANSPORT_CELLULAR = 0 - TRANSPORT_WIFI = 1 - TRANSPORT_BLUETOOTH = 2 - TRANSPORT_ETHERNET = 3 - TRANSPORT_VPN = 4 - TRANSPORT_WIFI_AWARE = 5 - TRANSPORT_LOWPAN = 6 - TRANSPORT_TEST = 7 - TRANSPORT_USB = 8 - - NET_CAPABILITY_MMS = 0 - NET_CAPABILITY_SUPL = 1 - NET_CAPABILITY_DUN = 2 - NET_CAPABILITY_FOTA = 3 - NET_CAPABILITY_IMS = 4 - NET_CAPABILITY_CBS = 5 - NET_CAPABILITY_WIFI_P2P = 6 - NET_CAPABILITY_IA = 7 - NET_CAPABILITY_RCS = 8 - NET_CAPABILITY_XCAP = 9 - NET_CAPABILITY_EIMS = 10 - NET_CAPABILITY_NOT_METERED = 11 - NET_CAPABILITY_INTERNET = 12 - NET_CAPABILITY_NOT_RESTRICTED = 13 - NET_CAPABILITY_TRUSTED = 14 - NET_CAPABILITY_NOT_VPN = 15 - NET_CAPABILITY_VALIDATED = 16 - NET_CAPABILITY_CAPTIVE_PORTAL = 17 - NET_CAPABILITY_NOT_ROAMING = 18 - NET_CAPABILITY_FOREGROUND = 19 - NET_CAPABILITY_NOT_CONGESTED = 20 - NET_CAPABILITY_NOT_SUSPENDED = 21 - NET_CAPABILITY_OEM_PAID = 22 - NET_CAPABILITY_MCX = 23 - NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24 - NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25 - NET_CAPABILITY_HEAD_UNIT = 26 - NET_CAPABILITY_NOT_CARRIER_PRIVILEGED = 27 - NET_CAPABILITY_BIP = 28 - - SIGNAL_STRENGTH_UNSPECIFIED = -2147483648 - - def __init__(self): - super().__init__() - self._transport_types: Set[int] = set() - self._capabilities: Set[int] = set() - self._link_down_bandwidth_kbps = 0 - self._link_up_bandwidth_kbps = 0 - self._signal_strength = NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED - self._network_specifier: Optional["NetworkSpecifier"] = None - self._owner_uid = -1 - self._ssid: Optional[str] = None - - def addTransportType(self, transportType: int) -> "NetworkCapabilities": - self._transport_types.add(transportType) - return self - - def removeTransportType(self, transportType: int) -> "NetworkCapabilities": - self._transport_types.discard(transportType) - return self - - def hasTransport(self, transportType: int) -> bool: - return transportType in self._transport_types - - def addCapability(self, capability: int) -> "NetworkCapabilities": - self._capabilities.add(capability) - return self - - def removeCapability(self, capability: int) -> "NetworkCapabilities": - self._capabilities.discard(capability) - return self - - def hasCapability(self, capability: int) -> bool: - return capability in self._capabilities - - def hasAllCapabilities(self, capabilities: List[int]) -> bool: - return all(c in self._capabilities for c in capabilities) - - def getTransportTypes(self) -> List[int]: - return sorted(self._transport_types) - - def getCapabilities(self) -> List[int]: - return sorted(self._capabilities) - - def setLinkUpstreamBandwidthKbps(self, up: int) -> "NetworkCapabilities": - self._link_up_bandwidth_kbps = up - return self - - def setLinkDownstreamBandwidthKbps(self, down: int) -> "NetworkCapabilities": - self._link_down_bandwidth_kbps = down - return self - - def getLinkUpstreamBandwidthKbps(self) -> int: - return self._link_up_bandwidth_kbps - - def getLinkDownstreamBandwidthKbps(self) -> int: - return self._link_down_bandwidth_kbps - - def setSignalStrength(self, signalStrength: int) -> "NetworkCapabilities": - self._signal_strength = signalStrength - return self - - def getSignalStrength(self) -> int: - return self._signal_strength - - def setNetworkSpecifier(self, specifier: "NetworkSpecifier") -> "NetworkCapabilities": - self._network_specifier = specifier - return self - - def getNetworkSpecifier(self) -> Optional["NetworkSpecifier"]: - return self._network_specifier - - def setOwnerUid(self, uid: int) -> "NetworkCapabilities": - self._owner_uid = uid - return self - - def getOwnerUid(self) -> int: - return self._owner_uid - - def setSsid(self, ssid: str) -> "NetworkCapabilities": - self._ssid = ssid - return self - - def getSsid(self) -> Optional[str]: - return self._ssid - - def describeCapabilities(self) -> str: - names = { - 0: "MMS", 1: "SUPL", 2: "DUN", 3: "FOTA", 4: "IMS", - 5: "CBS", 6: "WIFI_P2P", 7: "IA", 8: "RCS", 9: "XCAP", - 10: "EIMS", 11: "NOT_METERED", 12: "INTERNET", - 13: "NOT_RESTRICTED", 14: "TRUSTED", 15: "NOT_VPN", - 16: "VALIDATED", 17: "CAPTIVE_PORTAL", 18: "NOT_ROAMING", - 19: "FOREGROUND", 20: "NOT_CONGESTED", 21: "NOT_SUSPENDED", - } - return ", ".join(names.get(c, str(c)) for c in sorted(self._capabilities)) - - def toString(self) -> str: - transports = { - 0: "CELLULAR", 1: "WIFI", 2: "BT", 3: "ETHERNET", - 4: "VPN", 5: "WIFI_AWARE", - } - trans_str = ", ".join(transports.get(t, str(t)) for t in sorted(self._transport_types)) - return f"NetworkCapabilities(transports=[{trans_str}], caps=[{self.describeCapabilities()}])" - - def equals(self, other: Any) -> bool: - if not isinstance(other, NetworkCapabilities): - return False - return (self._transport_types == other._transport_types and - self._capabilities == other._capabilities) - - def hashCode(self) -> int: - return hash((frozenset(self._transport_types), frozenset(self._capabilities))) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# NetworkSpecifier / MatchAllNetworkSpecifier -# ═══════════════════════════════════════════════════════════════ - -class NetworkSpecifier(_BaseNetworkSpecifier): - """NetworkSpecifier — 网络指定器""" - - def satisfiedBy(self, other: Optional["NetworkSpecifier"]) -> bool: - return False - - def toString(self) -> str: - return f"NetworkSpecifier@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -class MatchAllNetworkSpecifier(_BaseMatchAllNetworkSpecifier, NetworkSpecifier): - """MatchAllNetworkSpecifier — 匹配所有网络""" - - def satisfiedBy(self, other: Optional["NetworkSpecifier"]) -> bool: - return True - - def toString(self) -> str: - return "MatchAllNetworkSpecifier" - - -# ═══════════════════════════════════════════════════════════════ -# NetworkRequest -# ═══════════════════════════════════════════════════════════════ - -class NetworkRequest(_BaseNetworkRequest): - """NetworkRequest — 网络请求""" - - def __init__(self, capabilities: Optional[NetworkCapabilities] = None, - networkSpecifier: Optional[NetworkSpecifier] = None, - legacyType: int = -1, isRequest: bool = True): - super().__init__() - self._capabilities = capabilities or NetworkCapabilities() - self._network_specifier = networkSpecifier - self._legacy_type = legacyType - self._is_request = isRequest - - def getCapabilities(self) -> NetworkCapabilities: - return self._capabilities - - def getNetworkSpecifier(self) -> Optional[NetworkSpecifier]: - return self._network_specifier - - def getLegacyType(self) -> int: - return self._legacy_type - - def hasCapability(self, capability: int) -> bool: - return self._capabilities.hasCapability(capability) - - def hasTransport(self, transport: int) -> bool: - return self._capabilities.hasTransport(transport) - - class Builder(Stub): - """NetworkRequest.Builder — 请求构建器""" - - def __init__(self): - super().__init__() - self._capabilities = NetworkCapabilities() - self._network_specifier: Optional[NetworkSpecifier] = None - - def addTransportType(self, transportType: int) -> "NetworkRequest.Builder": - self._capabilities.addTransportType(transportType) - return self - - def removeTransportType(self, transportType: int) -> "NetworkRequest.Builder": - self._capabilities.removeTransportType(transportType) - return self - - def addCapability(self, capability: int) -> "NetworkRequest.Builder": - self._capabilities.addCapability(capability) - return self - - def removeCapability(self, capability: int) -> "NetworkRequest.Builder": - self._capabilities.removeCapability(capability) - return self - - def addCapabilities(self, capabilities: List[int]) -> "NetworkRequest.Builder": - for c in capabilities: - self._capabilities.addCapability(c) - return self - - def removeCapabilities(self, capabilities: List[int]) -> "NetworkRequest.Builder": - for c in capabilities: - self._capabilities.removeCapability(c) - return self - - def setNetworkSpecifier(self, specifier: NetworkSpecifier) -> "NetworkRequest.Builder": - self._network_specifier = specifier - return self - - def build(self) -> "NetworkRequest": - caps = self._capabilities - caps.setNetworkSpecifier(self._network_specifier) - return NetworkRequest(capabilities=caps, networkSpecifier=self._network_specifier) - - def toString(self) -> str: - return f"NetworkRequest(caps={self._capabilities})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, NetworkRequest): - return False - return self._capabilities == other._capabilities - - def hashCode(self) -> int: - return self._capabilities.hashCode() - - -# ═══════════════════════════════════════════════════════════════ -# NetworkAgent -# ═══════════════════════════════════════════════════════════════ - -class NetworkAgent(Stub): - """NetworkAgent — 网络代理""" - - def __init__(self, looper: Any = None, network: Optional[Network] = None, - capabilities: Optional[NetworkCapabilities] = None, - linkProperties: Optional["LinkProperties"] = None, - score: int = 0): - super().__init__() - self._network = network or Network() - self._capabilities = capabilities or NetworkCapabilities() - self._link_properties = linkProperties or LinkProperties() - self._score = score - self._validated = False - self._connected = False - self._callback: Optional[Callable] = None - - def getNetwork(self) -> Network: - return self._network - - def getCapabilities(self) -> NetworkCapabilities: - return self._capabilities - - def getLinkProperties(self) -> "LinkProperties": - return self._link_properties - - def getScore(self) -> int: - return self._score - - def setScore(self, score: int): - self._score = score - - def connect(self): - self._connected = True - - def disconnect(self): - self._connected = False - - def isConnected(self) -> bool: - return self._connected - - def isValidated(self) -> bool: - return self._validated - - def setValidated(self, validated: bool): - self._validated = validated - - def sendNetworkCapabilities(self, capabilities: NetworkCapabilities): - self._capabilities = capabilities - - def sendLinkProperties(self, linkProperties: "LinkProperties"): - self._link_properties = linkProperties - - def sendNetworkScore(self, score: int): - self._score = score - - def registerCallback(self, callback: Callable): - self._callback = callback - - def unregisterCallback(self, callback: Callable): - if self._callback is callback: - self._callback = None - - def toString(self) -> str: - return f"NetworkAgent(net={self._network}, score={self._score}, connected={self._connected})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, NetworkAgent): - return False - return self._network == other._network - - def hashCode(self) -> int: - return self._network.hashCode() - - -# ═══════════════════════════════════════════════════════════════ -# NetworkInfo (扩展) -# ═══════════════════════════════════════════════════════════════ - -class NetworkInfo(_BaseNetworkInfo): - """NetworkInfo — 网络信息 (完整版)""" - - TYPE_NONE = -1 - TYPE_MOBILE = 0 - TYPE_WIFI = 1 - TYPE_WIMAX = 6 - TYPE_ETHERNET = 9 - TYPE_BLUETOOTH = 7 - TYPE_VPN = 17 - TYPE_WIFI_P2P = 13 - TYPE_MOBILE_MMS = 2 - TYPE_MOBILE_SUPL = 3 - TYPE_MOBILE_DUN = 4 - TYPE_MOBILE_HIPRI = 5 - TYPE_MOBILE_FOTA = 10 - TYPE_MOBILE_IMS = 11 - TYPE_MOBILE_CBS = 12 - TYPE_MOBILE_IA = 14 - TYPE_MOBILE_EMERGENCY = 15 - TYPE_PROXY = 16 - - STATE_CONNECTING = 0 - STATE_CONNECTED = 1 - STATE_SUSPENDED = 2 - STATE_DISCONNECTING = 3 - STATE_DISCONNECTED = 4 - STATE_UNKNOWN = 5 - - DETAILED_STATE_IDLE = 0 - DETAILED_STATE_SCANNING = 1 - DETAILED_STATE_CONNECTING = 2 - DETAILED_STATE_AUTHENTICATING = 3 - DETAILED_STATE_OBTAINING_IPADDR = 4 - DETAILED_STATE_CONNECTED = 5 - DETAILED_STATE_SUSPENDED = 6 - DETAILED_STATE_DISCONNECTING = 7 - DETAILED_STATE_DISCONNECTED = 8 - DETAILED_STATE_FAILED = 9 - DETAILED_STATE_BLOCKED = 10 - DETAILED_STATE_VERIFYING_POOR_LINK = 11 - DETAILED_STATE_CAPTIVE_PORTAL_CHECK = 12 - - def __init__(self, type_: int = TYPE_NONE, subtype: int = 0, - state: int = STATE_DISCONNECTED, - detailedState: int = DETAILED_STATE_IDLE, - isAvailable: bool = True, - isRoaming: bool = False, - extraInfo: str = "", - isFailover: bool = False, - reason: str = ""): - super().__init__() - self._type = type_ - self._subtype = subtype - self._state = state - self._detailed_state = detailedState - self._is_available = isAvailable - self._is_roaming = isRoaming - self._extra_info = extraInfo - self._is_failover = isFailover - self._reason = reason - - def getType(self) -> int: - return self._type - - def getSubtype(self) -> int: - return self._subtype - - def getState(self) -> int: - return self._state - - def getDetailedState(self) -> int: - return self._detailed_state - - def isConnected(self) -> bool: - return self._state == NetworkInfo.STATE_CONNECTED - - def isConnectedOrConnecting(self) -> bool: - return self._state in (NetworkInfo.STATE_CONNECTED, NetworkInfo.STATE_CONNECTING) - - def isAvailable(self) -> bool: - return self._is_available - - def isRoaming(self) -> bool: - return self._is_roaming - - def isFailover(self) -> bool: - return self._is_failover - - def getExtraInfo(self) -> str: - return self._extra_info - - def getReason(self) -> str: - return self._reason - - def getTypeName(self) -> str: - names = { - -1: "NONE", 0: "MOBILE", 1: "WIFI", 6: "WIMAX", - 9: "ETHERNET", 7: "BLUETOOTH", 17: "VPN", - 13: "WIFI_P2P", 16: "PROXY", - } - return names.get(self._type, f"TYPE_{self._type}") - - def setDetailedState(self, detailedState: int, reason: str, extraInfo: str): - self._detailed_state = detailedState - self._reason = reason - self._extra_info = extraInfo - - def setState(self, state: int): - self._state = state - - def toString(self) -> str: - return (f"NetworkInfo(type={self.getTypeName()}, state={self._state}, " - f"connected={self.isConnected()})") - - def equals(self, other: Any) -> bool: - if not isinstance(other, NetworkInfo): - return False - return self._type == other._type and self._state == other._state - - def hashCode(self) -> int: - return hash((self._type, self._state)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# LinkAddress -# ═══════════════════════════════════════════════════════════════ - -class LinkAddress(_BaseLinkAddress): - """LinkAddress — 链路地址""" - - def __init__(self, address: str = "0.0.0.0", prefixLength: int = 24, - flags: int = 0, scope: int = 0): - super().__init__() - self._address = address - self._prefix_length = prefixLength - self._flags = flags - self._scope = scope - - def getAddress(self) -> str: - return self._address - - def getPrefixLength(self) -> int: - return self._prefix_length - - def getFlags(self) -> int: - return self._flags - - def getScope(self) -> int: - return self._scope - - def isGlobalPreferred(self) -> bool: - return self._scope == 0 - - def toString(self) -> str: - return f"{self._address}/{self._prefix_length}" - - def equals(self, other: Any) -> bool: - if not isinstance(other, LinkAddress): - return False - return self._address == other._address and self._prefix_length == other._prefix_length - - def hashCode(self) -> int: - return hash((self._address, self._prefix_length)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# LinkProperties -# ═══════════════════════════════════════════════════════════════ - -class LinkProperties(_BaseLinkProperties): - """LinkProperties — 链路属性""" - - def __init__(self): - super().__init__() - self._interface_name: str = "" - self._link_addresses: List[LinkAddress] = [] - self._dns_servers: List[str] = [] - self._domains: str = "" - self._mtu: int = 1500 - self._http_proxy: Optional["ProxyInfo"] = None - self._private_dns_server_name: str = "" - self._routes: List[str] = [] - - def setInterfaceName(self, name: str): - self._interface_name = name - - def getInterfaceName(self) -> str: - return self._interface_name - - def addLinkAddress(self, address: LinkAddress): - self._link_addresses.append(address) - - def removeLinkAddress(self, address: LinkAddress): - self._link_addresses = [a for a in self._link_addresses if a != address] - - def getLinkAddresses(self) -> List[LinkAddress]: - return list(self._link_addresses) - - def addDnsServer(self, dnsServer: str): - self._dns_servers.append(dnsServer) - - def removeDnsServer(self, dnsServer: str): - self._dns_servers = [d for d in self._dns_servers if d != dnsServer] - - def getDnsServers(self) -> List[str]: - return list(self._dns_servers) - - def setDomains(self, domains: str): - self._domains = domains - - def getDomains(self) -> str: - return self._domains - - def setMtu(self, mtu: int): - self._mtu = mtu - - def getMtu(self) -> int: - return self._mtu - - def setHttpProxy(self, proxy: "ProxyInfo"): - self._http_proxy = proxy - - def getHttpProxy(self) -> Optional["ProxyInfo"]: - return self._http_proxy - - def setPrivateDnsServerName(self, name: str): - self._private_dns_server_name = name - - def getPrivateDnsServerName(self) -> str: - return self._private_dns_server_name - - def addRoute(self, route: str): - self._routes.append(route) - - def removeRoute(self, route: str): - self._routes = [r for r in self._routes if r != route] - - def getRoutes(self) -> List[str]: - return list(self._routes) - - def clear(self): - self._link_addresses.clear() - self._dns_servers.clear() - self._routes.clear() - - def toString(self) -> str: - return (f"LinkProperties(iface={self._interface_name}, " - f"dns={self._dns_servers}, mtu={self._mtu})") - - def equals(self, other: Any) -> bool: - if not isinstance(other, LinkProperties): - return False - return self._interface_name == other._interface_name - - def hashCode(self) -> int: - return hash(self._interface_name) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# LinkQualityInfo -# ═══════════════════════════════════════════════════════════════ - -class LinkQualityInfo(Stub): - """LinkQualityInfo — 链路质量信息""" - - NORMALIZED_MIN = 0 - NORMALIZED_MAX = 100 - - def __init__(self): - super().__init__() - self._normalized_rssi = LinkQualityInfo.NORMALIZED_MIN - self._normalized_signal_strength = LinkQualityInfo.NORMALIZED_MIN - self._timestamp = 0 - self._theoretical_tx_rate = 0 - self._theoretical_rx_rate = 0 - - def getNormalizedRssi(self) -> int: - return self._normalized_rssi - - def setNormalizedRssi(self, rssi: int): - self._normalized_rssi = max(LinkQualityInfo.NORMALIZED_MIN, - min(LinkQualityInfo.NORMALIZED_MAX, rssi)) - - def getNormalizedSignalStrength(self) -> int: - return self._normalized_signal_strength - - def setNormalizedSignalStrength(self, strength: int): - self._normalized_signal_strength = max(LinkQualityInfo.NORMALIZED_MIN, - min(LinkQualityInfo.NORMALIZED_MAX, strength)) - - def getTimestamp(self) -> int: - return self._timestamp - - def setTimestamp(self, timestamp: int): - self._timestamp = timestamp - - def getTheoreticalTxRate(self) -> int: - return self._theoretical_tx_rate - - def setTheoreticalTxRate(self, rate: int): - self._theoretical_tx_rate = rate - - def getTheoreticalRxRate(self) -> int: - return self._theoretical_rx_rate - - def setTheoreticalRxRate(self, rate: int): - self._theoretical_rx_rate = rate - - def toString(self) -> str: - return f"LinkQualityInfo(rssi={self._normalized_rssi}, txRate={self._theoretical_tx_rate})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -class WifiLinkQualityInfo(LinkQualityInfo): - """WifiLinkQualityInfo — WiFi 链路质量""" - - def __init__(self): - super().__init__() - self._ssid: str = "" - self._bssid: str = "" - self._rssi: int = 0 - self._link_speed_mbps: int = 0 - self._frequency: int = 0 - - def getSsid(self) -> str: - return self._ssid - - def setSsid(self, ssid: str): - self._ssid = ssid - - def getBssid(self) -> str: - return self._bssid - - def setBssid(self, bssid: str): - self._bssid = bssid - - def getRssi(self) -> int: - return self._rssi - - def setRssi(self, rssi: int): - self._rssi = rssi - - def getLinkSpeedMbps(self) -> int: - return self._link_speed_mbps - - def setLinkSpeedMbps(self, speed: int): - self._link_speed_mbps = speed - - def getFrequency(self) -> int: - return self._frequency - - def setFrequency(self, freq: int): - self._frequency = freq - - def toString(self) -> str: - return f"WifiLinkQualityInfo(ssid={self._ssid}, rssi={self._rssi})" - - -class EthernetLinkQualityInfo(LinkQualityInfo): - """EthernetLinkQualityInfo — 以太网链路质量""" - - def __init__(self): - super().__init__() - self._cable_status: int = 0 - self._link_speed: int = 1000 - - def getCableStatus(self) -> int: - return self._cable_status - - def setCableStatus(self, status: int): - self._cable_status = status - - def getLinkSpeed(self) -> int: - return self._link_speed - - def setLinkSpeed(self, speed: int): - self._link_speed = speed - - def toString(self) -> str: - return f"EthernetLinkQualityInfo(speed={self._link_speed})" - - -class MobileLinkQualityInfo(LinkQualityInfo): - """MobileLinkQualityInfo — 移动网络链路质量""" - - def __init__(self): - super().__init__() - self._network_type: int = 0 - self._signal_dbm: int = 0 - self._sinr: int = 0 - self._rsrp: int = 0 - self._rsrq: int = 0 - self._rssnr: int = 0 - - def getNetworkType(self) -> int: - return self._network_type - - def setNetworkType(self, type_: int): - self._network_type = type_ - - def getSignalDbm(self) -> int: - return self._signal_dbm - - def setSignalDbm(self, dbm: int): - self._signal_dbm = dbm - - def getSinr(self) -> int: - return self._sinr - - def setSinr(self, sinr: int): - self._sinr = sinr - - def getRsrp(self) -> int: - return self._rsrp - - def setRsrp(self, rsrp: int): - self._rsrp = rsrp - - def getRsrq(self) -> int: - return self._rsrq - - def setRsrq(self, rsrq: int): - self._rsrq = rsrq - - def getRssnr(self) -> int: - return self._rssnr - - def setRssnr(self, rssnr: int): - self._rssnr = rssnr - - def toString(self) -> str: - return f"MobileLinkQualityInfo(type={self._network_type}, rsrp={self._rsrp})" - - -# ═══════════════════════════════════════════════════════════════ -# TrafficStats -# ═══════════════════════════════════════════════════════════════ - -class TrafficStats(_BaseTrafficStats): - """TrafficStats — 流量统计""" - - UID_REMOVED = -4 - UID_TETHERING = -5 - UNSUPPORTED = -1 - - @staticmethod - def getMobileTxPackets() -> int: - return 0 - - @staticmethod - def getMobileRxPackets() -> int: - return 0 - - @staticmethod - def getMobileTxBytes() -> int: - return 0 - - @staticmethod - def getMobileRxBytes() -> int: - return 0 - - @staticmethod - def getTotalTxPackets() -> int: - return 0 - - @staticmethod - def getTotalRxPackets() -> int: - return 0 - - @staticmethod - def getTotalTxBytes() -> int: - return 0 - - @staticmethod - def getTotalRxBytes() -> int: - return 0 - - @staticmethod - def getUidTxBytes(uid: int) -> int: - return 0 - - @staticmethod - def getUidRxBytes(uid: int) -> int: - return 0 - - @staticmethod - def getUidTxPackets(uid: int) -> int: - return 0 - - @staticmethod - def getUidRxPackets(uid: int) -> int: - return 0 - - @staticmethod - def getUidTcpTxBytes(uid: int) -> int: - return 0 - - @staticmethod - def getUidTcpRxBytes(uid: int) -> int: - return 0 - - @staticmethod - def getUidUdpTxBytes(uid: int) -> int: - return 0 - - @staticmethod - def getUidUdpRxBytes(uid: int) -> int: - return 0 - - @staticmethod - def getUidTcpSegmentsSent(uid: int) -> int: - return 0 - - @staticmethod - def getUidTcpSegmentsReceived(uid: int) -> int: - return 0 - - @staticmethod - def getUidUdpPacketsSent(uid: int) -> int: - return 0 - - @staticmethod - def getUidUdpPacketsReceived(uid: int) -> int: - return 0 - - def toString(self) -> str: - return "TrafficStats(desktop stub)" - - -# ═══════════════════════════════════════════════════════════════ -# ProxyInfo -# ═══════════════════════════════════════════════════════════════ - -class ProxyInfo(_BaseProxyInfo): - """ProxyInfo — 代理信息""" - - def __init__(self, host: str = "", port: int = 0, - exclusionList: List[str] = None, - pacFileUrl: str = ""): - super().__init__() - self._host = host - self._port = port - self._exclusion_list = exclusion_list or [] - self._pac_file_url = pacFileUrl - - def getHost(self) -> str: - return self._host - - def getPort(self) -> int: - return self._port - - def getExclusionList(self) -> List[str]: - return list(self._exclusion_list) - - def getPacFileUrl(self) -> str: - return self._pac_file_url - - def isValid(self) -> bool: - return bool(self._host) and self._port > 0 - - def toString(self) -> str: - return f"ProxyInfo(host={self._host}, port={self._port})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, ProxyInfo): - return False - return self._host == other._host and self._port == other._port - - def hashCode(self) -> int: - return hash((self._host, self._port)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# DhcpInfo / DhcpInfoInternal -# ═══════════════════════════════════════════════════════════════ - -class DhcpInfo(_BaseDhcpInfo): - """DhcpInfo — DHCP 信息""" - - def __init__(self): - super().__init__() - self.ipAddress: int = 0 - self.gateway: int = 0 - self.netmask: int = 0 - self.dns1: int = 0 - self.dns2: int = 0 - self.serverAddress: int = 0 - self.leaseDuration: int = 0 - - def toString(self) -> str: - def int_to_ip(ip: int) -> str: - return ".".join(str((ip >> (8 * i)) & 0xFF) for i in range(4)) - return (f"DhcpInfo(ip={int_to_ip(self.ipAddress)}, " - f"gateway={int_to_ip(self.gateway)}, " - f"dns1={int_to_ip(self.dns1)})") - - def equals(self, other: Any) -> bool: - if not isinstance(other, DhcpInfo): - return False - return self.ipAddress == other.ipAddress - - def hashCode(self) -> int: - return self.ipAddress & 0x7FFFFFFF - - -class DhcpInfoInternal(Stub): - """DhcpInfoInternal — DHCP 内部信息""" - - def __init__(self): - super().__init__() - self.ipAddress: str = "" - self.gateway: str = "" - self.netmask: str = "" - self.dns1: str = "" - self.dns2: str = "" - self.serverAddress: str = "" - self.leaseDuration: int = 0 - self.domain: str = "" - - def toString(self) -> str: - return f"DhcpInfoInternal(ip={self.ipAddress}, gateway={self.gateway})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, DhcpInfoInternal): - return False - return self.ipAddress == other.ipAddress - - def hashCode(self) -> int: - return hash(self.ipAddress) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# NetworkBadging -# ═══════════════════════════════════════════════════════════════ - -class NetworkBadging(Stub): - """NetworkBadging — 网络徽章""" - - BADGING_WIFI = 0 - BADGING_4G = 1 - BADGING_3G = 2 - BADGING_2G = 3 - BADGING_1X = 4 - BADGING_EHRPD = 5 - BADGING_HSPAP = 6 - BADGING_LTE = 7 - BADGING_LTE_CA = 8 - BADGING_NR = 9 - BADGING_ETHERNET = 10 - BADGING_WIMAX = 11 - - @staticmethod - def getBadging(transportType: int, subType: int = 0) -> int: - mapping = {1: NetworkBadging.BADGING_WIFI, 3: NetworkBadging.BADGING_ETHERNET} - return mapping.get(transportType, NetworkBadging.BADGING_4G) - - def toString(self) -> str: - return f"NetworkBadging@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# ConnectivityDiagnosticsManager -# ═══════════════════════════════════════════════════════════════ - -class ConnectivityDiagnosticsManager(Stub): - """ConnectivityDiagnosticsManager — 连接诊断管理器""" - - def __init__(self): - super().__init__() - self._callbacks: Dict[int, Callable] = {} - self._reports: Dict[int, "ConnectivityDiagnosticsManager_ConnectivityReport"] = {} - self._stall_reports: Dict[int, "ConnectivityDiagnosticsManager_DataStallReport"] = {} - - class ConnectivityReport(Stub): - """ConnectivityDiagnosticsManager.ConnectivityReport — 连接报告""" - - def __init__(self, network: Optional[Network] = None, - linkProperties: Optional[LinkProperties] = None, - networkCapabilities: Optional[NetworkCapabilities] = None, - timestamp: int = 0, - errorCode: int = 0, - errorMessage: str = ""): - super().__init__() - self._network = network - self._link_properties = linkProperties - self._network_capabilities = networkCapabilities - self._timestamp = timestamp - self._error_code = errorCode - self._error_message = errorMessage - - def getNetwork(self) -> Optional[Network]: - return self._network - - def getLinkProperties(self) -> Optional[LinkProperties]: - return self._link_properties - - def getNetworkCapabilities(self) -> Optional[NetworkCapabilities]: - return self._network_capabilities - - def getTimestamp(self) -> int: - return self._timestamp - - def getErrorCode(self) -> int: - return self._error_code - - def getErrorMessage(self) -> str: - return self._error_message - - def toString(self) -> str: - return f"ConnectivityReport(net={self._network}, error={self._error_code})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, ConnectivityDiagnosticsManager.ConnectivityReport): - return False - return self._network == other._network and self._timestamp == other._timestamp - - def hashCode(self) -> int: - return hash((id(self._network), self._timestamp)) & 0x7FFFFFFF - - class DataStallReport(Stub): - """ConnectivityDiagnosticsManager.DataStallReport — 数据卡顿报告""" - - REASON_NETWORK_VALIDATION = 0 - REASON_NETWORK_CONGESTION = 1 - REASON_NETWORK_PARTIAL_CONNECTIVITY = 2 - REASON_NETWORK_SUSPECTED = 3 - REASON_DNS_RESOLUTION_FAILURE = 4 - REASON_TCP_FAILURE = 5 - REASON_IP_REACHABILITY_LOSS = 6 - - DETECTION_METHOD_NONE = 0 - DETECTION_METHOD_DNS_EVENTS = 1 - DETECTION_METHOD_TCP_METRICS = 2 - DETECTION_METHOD_IP_METRICS = 3 - - def __init__(self, network: Optional[Network] = None, - detectionMethod: int = DETECTION_METHOD_NONE, - reason: int = REASON_NETWORK_VALIDATION, - timestamp: int = 0, - linkProperties: Optional[LinkProperties] = None, - networkCapabilities: Optional[NetworkCapabilities] = None): - super().__init__() - self._network = network - self._detection_method = detectionMethod - self._reason = reason - self._timestamp = timestamp - self._link_properties = linkProperties - self._network_capabilities = networkCapabilities - - def getNetwork(self) -> Optional[Network]: - return self._network - - def getDetectionMethod(self) -> int: - return self._detection_method - - def getReason(self) -> int: - return self._reason - - def getTimestamp(self) -> int: - return self._timestamp - - def getLinkProperties(self) -> Optional[LinkProperties]: - return self._link_properties - - def getNetworkCapabilities(self) -> Optional[NetworkCapabilities]: - return self._network_capabilities - - def toString(self) -> str: - return f"DataStallReport(net={self._network}, reason={self._reason})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, ConnectivityDiagnosticsManager.DataStallReport): - return False - return self._network == other._network and self._timestamp == other._timestamp - - def hashCode(self) -> int: - return hash((id(self._network), self._timestamp)) & 0x7FFFFFFF - - def registerConnectivityDiagnosticsCallback( - self, request: NetworkRequest, - callback: Callable, - handler: Any = None): - lid = id(callback) - self._callbacks[lid] = callback - - def unregisterConnectivityDiagnosticsCallback(self, callback: Callable): - lid = id(callback) - self._callbacks.pop(lid, None) - - def getConnectivityReport(self, network: Network) -> Optional[ConnectivityReport]: - return self._reports.get(network.getNetId()) - - def getDataStallReport(self, network: Network) -> Optional[DataStallReport]: - return self._stall_reports.get(network.getNetId()) - - def toString(self) -> str: - return f"ConnectivityDiagnosticsManager(callbacks={len(self._callbacks)})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# ConnectivityManager (扩展) -# ═══════════════════════════════════════════════════════════════ - -class ConnectivityManager(_BaseConnectivityManager): - """ConnectivityManager — 连接管理器 (扩展版)""" - - TYPE_NONE = -1 - TYPE_MOBILE = 0 - TYPE_WIFI = 1 - TYPE_MOBILE_MMS = 2 - TYPE_MOBILE_SUPL = 3 - TYPE_MOBILE_DUN = 4 - TYPE_MOBILE_HIPRI = 5 - TYPE_WIMAX = 6 - TYPE_BLUETOOTH = 7 - TYPE_DUMMY = 8 - TYPE_ETHERNET = 9 - TYPE_MOBILE_FOTA = 10 - TYPE_MOBILE_IMS = 11 - TYPE_MOBILE_CBS = 12 - TYPE_WIFI_P2P = 13 - TYPE_MOBILE_IA = 14 - TYPE_MOBILE_EMERGENCY = 15 - TYPE_PROXY = 16 - TYPE_VPN = 17 - - EXTRA_NETWORK = "android.net.extra.NETWORK" - EXTRA_OTHER_NETWORK = "android.net.extra.OTHER_NETWORK" - EXTRA_NO_CONNECTIVITY = "android.net.extra.NO_CONNECTIVITY" - EXTRA_REASON = "android.net.extra.REASON" - EXTRA_EXTRA_INFO = "android.net.extra.EXTRA_INFO" - EXTRA_NETWORK_INFO = "android.net.extra.NETWORK_INFO" - EXTRA_IS_FAILOVER = "android.net.extra.IS_FAILOVER" - EXTRA_NETWORK_TYPE = "android.net.extra.NETWORK_TYPE" - - ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL" - ACTION_CAPTIVE_PORTAL_TEST_COMPLETED = "android.net.conn.CAPTIVE_PORTAL_TEST_COMPLETED" - - def __init__(self): - super().__init__() - self._active_network: Optional[Network] = None - self._networks: Dict[int, Network] = {} - self._network_infos: Dict[int, NetworkInfo] = {} - self._callbacks: Dict[int, Any] = {} - self._diagnostics_manager = ConnectivityDiagnosticsManager() - - def getActiveNetwork(self) -> Optional[Network]: - return self._active_network - - def _get_linux_active_info(self): - """从 Linux 真实网络信息构造活跃 NetworkInfo""" - try: - from .net import NetworkInfo - interfaces = LinuxNetwork.get_interfaces() - # 有真实网络接口 (非环回) 且 operstate=up 即视为已连接 - connected = any( - (iface.get("ifname") or iface.get("name", "")) != "lo" - and iface.get("operstate", "").lower() == "up" - for iface in interfaces - ) - if not connected: - connected = LinuxNetwork.get_connectivity() - ip = LinuxNetwork.get_ip_address() - iface_name = "" - net_type = ConnectivityManager.TYPE_ETHERNET - for iface in interfaces: - name = iface.get("ifname") or iface.get("name", "") - state = iface.get("operstate", "") - if name and name != "lo" and state.lower() == "up": - iface_name = iface_name or name - if name and name.startswith(("wl", "wlan", "wlp")): - net_type = ConnectivityManager.TYPE_WIFI - iface_name = name - data = { - "connected": connected, - "ip": ip, - "interface": iface_name, - "type_name": "WIFI" if net_type == ConnectivityManager.TYPE_WIFI else "ETHERNET", - } - return NetworkInfo.from_linux_data(data, network_type=net_type) - except Exception: - return None - - def getActiveNetworkInfo(self) -> Optional[NetworkInfo]: - if _USE_LINUX: - return self._get_linux_active_info() - if self._active_network: - return self._network_infos.get(self._active_network.getNetId()) - return None - - def getNetworkInfo(self, networkType: int) -> Optional[NetworkInfo]: - if _USE_LINUX: - info = self._get_linux_active_info() - if info is not None and info.getType() == networkType: - return info - return None - for info in self._network_infos.values(): - if info.getType() == networkType: - return info - return None - - def getConnectivityStatus(self) -> bool: - """返回当前网络连通性 (True=已连接)""" - if _USE_LINUX: - return LinuxNetwork.get_connectivity() - return self._active_network is not None - - def getNetworkInfoForNetwork(self, network: Network) -> Optional[NetworkInfo]: - return self._network_infos.get(network.getNetId()) - - def getAllNetworks(self) -> List[Network]: - return list(self._networks.values()) - - def getAllNetworkInfo(self) -> List[NetworkInfo]: - return list(self._network_infos.values()) - - def getNetworkCapabilities(self, network: Network) -> Optional[NetworkCapabilities]: - return network.getNetworkCapabilities() - - def getLinkProperties(self, network: Network) -> Optional[LinkProperties]: - return network.getLinkProperties() - - def requestNetwork(self, request: NetworkRequest, callback: Any, - handler: Any = None, timeoutMs: int = 0, - flags: int = 0) -> "NetworkRequest": - lid = id(callback) - self._callbacks[lid] = callback - return request - - def unregisterNetworkCallback(self, callback: Any): - lid = id(callback) - self._callbacks.pop(lid, None) - - def registerNetworkCallback(self, request: NetworkRequest, callback: Any, - handler: Any = None): - lid = id(callback) - self._callbacks[lid] = callback - - def registerDefaultNetworkCallback(self, callback: Any, handler: Any = None): - lid = id(callback) - self._callbacks[lid] = callback - - def bindProcessToNetwork(self, network: Network) -> bool: - return True - - def getProcessDefaultNetwork(self) -> Optional[Network]: - return self._active_network - - def setProcessDefaultNetwork(self, network: Optional[Network]) -> bool: - self._active_network = network - return True - - def isDefaultNetworkActive(self) -> bool: - return self._active_network is not None - - def addDefaultNetworkActiveListener(self, callback: Callable): - pass - - def removeDefaultNetworkActiveListener(self, callback: Callable): - pass - - def reportInetCondition(self, networkType: int, percentage: int): - pass - - def getActiveLinkQualityInfo(self) -> Optional[LinkQualityInfo]: - return None - - def getLinkQualityInfo(self, networkType: int) -> Optional[LinkQualityInfo]: - return None - - def getAllLinkQualityInfo(self) -> List[LinkQualityInfo]: - return [] - - def getConnectivityDiagnosticsManager(self) -> ConnectivityDiagnosticsManager: - return self._diagnostics_manager - - def toString(self) -> str: - return f"ConnectivityManager(active={self._active_network})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/net_hotspot2.py b/android_pyapi/android/net_hotspot2.py deleted file mode 100644 index 5c7cfce..0000000 --- a/android_pyapi/android/net_hotspot2.py +++ /dev/null @@ -1,375 +0,0 @@ -""" -Android net.wifi.hotspot2 包 — PasspointConfiguration, HomeSp, PasspointManager, -OsuProvider, WifiPasspointInfo, ProvisionCallback -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import sys -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "PasspointConfiguration", - "HomeSp", - "PasspointManager", - "OsuProvider", - "WifiPasspointInfo", - "ProvisionCallback", -] - - -# ═══════════════════════════════════════════════════════════════ -# HomeSp -# ═══════════════════════════════════════════════════════════════ - -class HomeSp(Stub): - """HomeSp — 家庭服务提供商 (桌面存根)""" - - def __init__(self, fqdn: str = "", friendly_name: str = ""): - super().__init__() - self._fqdn = fqdn - self._friendly_name = friendly_name - self._friendly_name_list: List[str] = [friendly_name] if friendly_name else [] - self._match_all_ois: List[int] = [] - self._match_any_ois: List[int] = [] - self._other_home_provider_networks: List["HomeSp"] = [] - self._roaming_consortium_ois: List[int] = [] - self._icon_url = "" - self._icon: Any = None - - def getFqdn(self) -> str: - """获取 FQDN""" - return self._fqdn - - def getFriendlyName(self) -> str: - """获取友好名称""" - return self._friendly_name - - def getFriendlyNameList(self) -> List[str]: - """获取友好名称列表""" - return self._friendly_name_list.copy() - - def getMatchAllOis(self) -> List[int]: - """获取匹配所有 OI""" - return self._match_all_ois.copy() - - def getMatchAnyOis(self) -> List[int]: - """获取匹配任意 OI""" - return self._match_any_ois.copy() - - def getOtherHomeProviderNetworks(self) -> List["HomeSp"]: - """获取其他家庭提供商网络""" - return self._other_home_provider_networks.copy() - - def getRoamingConsortiumOis(self) -> List[int]: - """获取漫游联盟 OI""" - return self._roaming_consortium_ois.copy() - - def getIconUrl(self) -> str: - """获取图标 URL""" - return self._icon_url - - def getIcon(self) -> Any: - """获取图标""" - return self._icon - - def toString(self) -> str: - return f"HomeSp(fqdn={self._fqdn}, name={self._friendly_name})" - - -# ═══════════════════════════════════════════════════════════════ -# PasspointConfiguration -# ═══════════════════════════════════════════════════════════════ - -class PasspointConfiguration(Stub): - """PasspointConfiguration — Passpoint 配置 (桌面存根)""" - - def __init__(self): - super().__init__() - self._home_sp: Optional[HomeSp] = None - self._credential: Optional[Any] = None - self._update_identifier: Optional[int] = None - self._unique_id = "" - self._osu_provisioned = False - self._osu_provider_id = "" - self._service_friendly_name = "" - self._service_description = "" - self._service_icon_url = "" - self._user_intervention_required = False - self._create_time: Optional[int] = None - self._expiration_time: Optional[int] = None - - def getHomeSp(self) -> Optional[HomeSp]: - """获取家庭 SP""" - return self._home_sp - - def setHomeSp(self, home_sp: HomeSp) -> None: - """设置家庭 SP""" - self._home_sp = home_sp - - def getCredential(self) -> Optional[Any]: - """获取凭据""" - return self._credential - - def setCredential(self, credential: Any) -> None: - """设置凭据""" - self._credential = credential - - def getUpdateIdentifier(self) -> Optional[int]: - """获取更新标识符""" - return self._update_identifier - - def setUpdateIdentifier(self, identifier: Optional[int]) -> None: - """设置更新标识符""" - self._update_identifier = identifier - - def getUniqueId(self) -> str: - """获取唯一 ID""" - return self._unique_id - - def isOsuProvisioned(self) -> bool: - """检查是否为 OSU 配置""" - return self._osu_provisioned - - def getOsuProviderId(self) -> str: - """获取 OSU 提供商 ID""" - return self._osu_provider_id - - def setOsuProviderId(self, provider_id: str) -> None: - """设置 OSU 提供商 ID""" - self._osu_provider_id = provider_id - - def getServiceFriendlyName(self) -> str: - """获取服务友好名称""" - return self._service_friendly_name - - def setServiceFriendlyName(self, name: str) -> None: - """设置服务友好名称""" - self._service_friendly_name = name - - def getServiceDescription(self) -> str: - """获取服务描述""" - return self._service_description - - def setServiceDescription(self, description: str) -> None: - """设置服务描述""" - self._service_description = description - - def getServiceIconUrl(self) -> str: - """获取服务图标 URL""" - return self._service_icon_url - - def setServiceIconUrl(self, url: str) -> None: - """设置服务图标 URL""" - self._service_icon_url = url - - def isUserInterventionRequired(self) -> bool: - """检查是否需要用户干预""" - return self._user_intervention_required - - def setUserInterventionRequired(self, required: bool) -> None: - """设置用户干预""" - self._user_intervention_required = required - - def getCreateTime(self) -> Optional[int]: - """获取创建时间""" - return self._create_time - - def setCreateTime(self, time_millis: int) -> None: - """设置创建时间""" - self._create_time = time_millis - - def getExpirationTime(self) -> Optional[int]: - """获取过期时间""" - return self._expiration_time - - def setExpirationTime(self, time_millis: int) -> None: - """设置过期时间""" - self._expiration_time = time_millis - - def toString(self) -> str: - return f"PasspointConfiguration(fqdn={self._home_sp.getFqdn() if self._home_sp else 'none'})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, PasspointConfiguration): - return False - return self._unique_id == other._unique_id - - def hashCode(self) -> int: - return hash(self._unique_id) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# PasspointManager -# ═══════════════════════════════════════════════════════════════ - -class PasspointManager(Stub): - """PasspointManager — Passpoint 管理器 (桌面存根)""" - - _instance: Optional["PasspointManager"] = None - - def __init__(self): - super().__init__() - self._providers: Dict[str, PasspointConfiguration] = {} - self._matching_configs: List[Any] = [] - self._preferred_provider: Optional[str] = None - - @staticmethod - def getInstance() -> "PasspointManager": - if PasspointManager._instance is None: - PasspointManager._instance = PasspointManager() - return PasspointManager._instance - - def addOrUpdateProvider(self, config: PasspointConfiguration) -> None: - """添加或更新提供商""" - self._providers[config.getUniqueId()] = config - - def removeProvider(self, config: PasspointConfiguration) -> None: - """移除提供商""" - self._providers.pop(config.getUniqueId(), None) - - def getProviders(self) -> List[PasspointConfiguration]: - """获取提供商列表""" - return list(self._providers.values()) - - def getMatchingWifiConfigs(self, fqdn: str) -> List[Any]: - """获取匹配的 Wi-Fi 配置""" - return self._matching_configs - - def queryPasspointIcon(self, config: PasspointConfiguration, icon_url: str) -> None: - """查询 Passpoint 图标""" - pass - - def abortQueryPasspointIcon(self, icon_url: str) -> None: - """中止查询 Passpoint 图标""" - pass - - def setIcon(self, config: PasspointConfiguration, icon: Any) -> None: - """设置图标""" - pass - - def getAnqpOsuProviders(self, config: PasspointConfiguration) -> List["OsuProvider"]: - """获取 ANQP OSU 提供商""" - return [] - - def convertToWifiConfiguration(self, config: PasspointConfiguration) -> Optional[Any]: - """转换为 Wi-Fi 配置""" - return None - - def setPreferredProvider(self, config: PasspointConfiguration) -> None: - """设置首选提供商""" - self._preferred_provider = config.getUniqueId() - - def clearPreferredProvider(self) -> None: - """清除首选提供商""" - self._preferred_provider = None - - def getPasspointProviderWithPackage(self, package_name: str) -> Optional[PasspointConfiguration]: - """获取指定包名的 Passpoint 提供商""" - for config in self._providers.values(): - return config - return None - - -# ═══════════════════════════════════════════════════════════════ -# OsuProvider -# ═══════════════════════════════════════════════════════════════ - -class OsuProvider(Stub): - """OsuProvider — OSU 提供商 (桌面存根)""" - - OS_METHOD_SOAP_XML = 0 - OS_METHOD_OMADM = 1 - - def __init__(self, friendly_name: str = "", service_description: str = "", - server_uri: str = "", network_access_identifier: str = "", - method_list: Optional[List[int]] = None): - super().__init__() - self._friendly_name = friendly_name - self._service_description = service_description - self._server_uri = server_uri - self._network_access_identifier = network_access_identifier - self._method_list = method_list or [] - - def getFriendlyName(self) -> str: - """获取友好名称""" - return self._friendly_name - - def getServiceDescription(self) -> str: - """获取服务描述""" - return self._service_description - - def getServerUri(self) -> str: - """获取服务器 URI""" - return self._server_uri - - def getNetworkAccessIdentifier(self) -> str: - """获取网络接入标识符""" - return self._network_access_identifier - - def getMethodList(self) -> List[int]: - """获取方法列表""" - return self._method_list.copy() - - -# ═══════════════════════════════════════════════════════════════ -# WifiPasspointInfo -# ═══════════════════════════════════════════════════════════════ - -class WifiPasspointInfo(Stub): - """WifiPasspointInfo — Wi-Fi Passpoint 信息""" - - def __init__(self, fqdn: str = "", provider_friendly_name: str = ""): - super().__init__() - self._fqdn = fqdn - self._provider_friendly_name = provider_friendly_name - self._roaming_consortium_ids: List[int] = [] - - def getFqdn(self) -> str: - """获取 FQDN""" - return self._fqdn - - def getProviderFriendlyName(self) -> str: - """获取提供商友好名称""" - return self._provider_friendly_name - - def getRoamingConsortiumIds(self) -> List[int]: - """获取漫游联盟 ID""" - return self._roaming_consortium_ids.copy() - - def matchesFqdn(self, fqdn: str) -> bool: - """检查是否匹配 FQDN""" - return self._fqdn == fqdn - - def matchesRoamingConsortiumOis(self, ois: List[int]) -> bool: - """检查是否匹配漫游联盟 OI""" - return any(oi in self._roaming_consortium_ids for oi in ois) - - -# ═══════════════════════════════════════════════════════════════ -# ProvisionCallback -# ═══════════════════════════════════════════════════════════════ - -class ProvisionCallback(Stub): - """ProvisionCallback — 配置回调""" - - def __init__(self): - super().__init__() - - def onProvisionFailure(self, failure_code: int) -> None: - """配置失败回调""" - pass - - def onProvisionSuccess(self) -> None: - """配置成功回调""" - pass - - def onProvisionAbort(self) -> None: - """配置中止回调""" - pass \ No newline at end of file diff --git a/android_pyapi/android/net_http.py b/android_pyapi/android/net_http.py deleted file mode 100644 index e81f98a..0000000 --- a/android_pyapi/android/net_http.py +++ /dev/null @@ -1,643 +0,0 @@ -""" -Android HTTP API — AndroidHttpClient, HttpUrlConnection, HttpResponseCache, SSLCertificateSocketFactory, TimedText -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import sys -import io -import json -import time -import socket -import hashlib -import tempfile -import threading -import urllib.request -import urllib.error -import urllib.parse -import ssl as _ssl -from datetime import datetime -from typing import Any, Dict, List, Optional, Set, Tuple, Callable, BinaryIO -from http.client import HTTPResponse, HTTPConnection -from urllib.request import Request as _UrlRequest - -from .._core import Stub, AndroidOnlyError, _app_data_dir - -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -HTTP_GET = "GET" -HTTP_POST = "POST" -HTTP_PUT = "PUT" -HTTP_DELETE = "DELETE" -HTTP_HEAD = "HEAD" -HTTP_OPTIONS = "OPTIONS" -HTTP_PATCH = "PATCH" -HTTP_TRACE = "TRACE" - -HTTP_100_CONTINUE = 100 -HTTP_101_SWITCHING_PROTOCOLS = 101 -HTTP_200_OK = 200 -HTTP_201_CREATED = 201 -HTTP_202_ACCEPTED = 202 -HTTP_204_NO_CONTENT = 204 -HTTP_301_MOVED_PERMANENTLY = 301 -HTTP_302_FOUND = 302 -HTTP_304_NOT_MODIFIED = 304 -HTTP_307_TEMPORARY_REDIRECT = 307 -HTTP_400_BAD_REQUEST = 400 -HTTP_401_UNAUTHORIZED = 401 -HTTP_403_FORBIDDEN = 403 -HTTP_404_NOT_FOUND = 404 -HTTP_405_METHOD_NOT_ALLOWED = 405 -HTTP_408_REQUEST_TIMEOUT = 408 -HTTP_409_CONFLICT = 409 -HTTP_410_GONE = 410 -HTTP_429_TOO_MANY_REQUESTS = 429 -HTTP_500_INTERNAL_SERVER_ERROR = 500 -HTTP_502_BAD_GATEWAY = 502 -HTTP_503_SERVICE_UNAVAILABLE = 503 -HTTP_504_GATEWAY_TIMEOUT = 504 - -CHUNK_SIZE = 8192 -DEFAULT_CONNECT_TIMEOUT = 30000 -DEFAULT_READ_TIMEOUT = 30000 -MAX_REDIRECTS = 20 - - -# ═══════════════════════════════════════════════════════════════ -# SSLCertificateSocketFactory -# ═══════════════════════════════════════════════════════════════ - -class SSLCertificateSocketFactory(Stub): - """SSLCertificateSocketFactory — SSL 证书套接字工厂""" - - SSL_SECURITY_PROTOCOL_TLS = 1 - SSL_SECURITY_PROTOCOL_SSLv3 = 2 - SSL_SECURITY_PROTOCOL_DTLS = 3 - SSL_SECURITY_PROTOCOL_TLSv1 = 4 - SSL_SECURITY_PROTOCOL_TLSv1_1 = 5 - SSL_SECURITY_PROTOCOL_TLSv1_2 = 6 - SSL_SECURITY_PROTOCOL_TLSv1_3 = 7 - - def __init__(self, handshakeTimeoutMillis: int = 0): - super().__init__() - self._handshake_timeout = handshakeTimeoutMillis - self._security_protocol = SSLCertificateSocketFactory.SSL_SECURITY_PROTOCOL_TLS - self._trust_all = False - self._key_manager: Optional[Any] = None - self._trust_manager: Optional[Any] = None - self._secure_random: Optional[Any] = None - self._default_ssl_context = _ssl.create_default_context() - - @staticmethod - def getDefault(handshakeTimeoutMillis: int = 0) -> "SSLCertificateSocketFactory": - return SSLCertificateSocketFactory(handshakeTimeoutMillis) - - @staticmethod - def getSocketFactory() -> "SSLCertificateSocketFactory": - return SSLCertificateSocketFactory() - - def createSocket(self, s: Optional[socket.socket] = None, host: str = "", port: int = 443, autoClose: bool = True) -> socket.socket: - if s is None: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - context = _ssl.create_default_context() - if self._trust_all: - context.check_hostname = False - context.verify_mode = _ssl.CERT_NONE - return context.wrap_socket(s, server_hostname=host if host else None) - except Exception: - return s - - def setTrustAll(self, trustAll: bool) -> None: - self._trust_all = trustAll - - def setKeyManager(self, km: Any) -> None: - self._key_manager = km - - def setTrustManager(self, tm: Any) -> None: - self._trust_manager = tm - - def setSecureRandom(self, sr: Any) -> None: - self._secure_random = sr - - def getSecurityProtocol(self) -> int: - return self._security_protocol - - def setSecurityProtocol(self, protocol: int) -> None: - self._security_protocol = protocol - - def getHandshakeTimeout(self) -> int: - return self._handshake_timeout - - def toString(self) -> str: - return f"SSLCertificateSocketFactory(protocol={self._security_protocol})" - - -# ═══════════════════════════════════════════════════════════════ -# HttpResponseCache -# ═══════════════════════════════════════════════════════════════ - -class HttpResponseCache(Stub): - """HttpResponseCache — HTTP 响应缓存""" - - _instance: Optional["HttpResponseCache"] = None - - def __init__(self, directory: str, maxSize: int): - super().__init__() - self._directory = directory - self._max_size = maxSize - self._current_size = 0 - self._cache: Dict[str, Tuple[str, float, int]] = {} - self._hit_count = 0 - self._miss_count = 0 - self._network_count = 0 - self._write_success_count = 0 - self._write_abort_count = 0 - self._lock = threading.Lock() - os.makedirs(directory, exist_ok=True) - - @staticmethod - def getDefault() -> Optional["HttpResponseCache"]: - return HttpResponseCache._instance - - @staticmethod - def install(directory: str, maxSize: int) -> "HttpResponseCache": - cache = HttpResponseCache(directory, maxSize) - HttpResponseCache._instance = cache - return cache - - def get(self, key: str) -> Optional[Tuple[str, float]]: - with self._lock: - entry = self._cache.get(key) - if entry: - data, expiry, size = entry - if time.time() < expiry: - self._hit_count += 1 - return (data, expiry) - else: - del self._cache[key] - self._current_size -= size - self._miss_count += 1 - return None - self._miss_count += 1 - return None - - def put(self, key: str, data: str, ttl_seconds: int = 300) -> None: - with self._lock: - old = self._cache.get(key) - if old: - self._current_size -= old[2] - size = len(data.encode("utf-8")) - expires = time.time() + ttl_seconds - self._cache[key] = (data, expires, size) - self._current_size += size - self._write_success_count += 1 - self._evict_if_needed() - - def _evict_if_needed(self) -> None: - while self._current_size > self._max_size and self._cache: - oldest_key = min(self._cache.keys(), key=lambda k: self._cache[k][1]) - oldest = self._cache.pop(oldest_key) - self._current_size -= oldest[2] - self._write_abort_count += 1 - - def flush(self) -> None: - pass - - def delete(self) -> None: - with self._lock: - self._cache.clear() - self._current_size = 0 - HttpResponseCache._instance = None - - def maxSize(self) -> int: - return self._max_size - - def size(self) -> int: - with self._lock: - return self._current_size - - def hitCount(self) -> int: - return self._hit_count - - def missCount(self) -> int: - return self._miss_count - - def networkCount(self) -> int: - return self._network_count - - def writeSuccessCount(self) -> int: - return self._write_success_count - - def writeAbortCount(self) -> int: - return self._write_abort_count - - def isCached(self, key: str) -> bool: - with self._lock: - return key in self._cache - - def clear(self) -> None: - with self._lock: - self._cache.clear() - self._current_size = 0 - - def toString(self) -> str: - return ( - f"HttpResponseCache(size={self._current_size}/{self._max_size}, " - f"hits={self._hit_count}, misses={self._miss_count})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# TimedText -# ═══════════════════════════════════════════════════════════════ - -class TimedText(Stub): - """TimedText — 定时文本(字幕)""" - - def __init__(self, text: str = "", startMs: int = 0, endMs: int = 0): - super().__init__() - self._text = text - self._start_ms = startMs - self._end_ms = endMs - self._style: Optional[Dict[str, Any]] = None - self._subtitle_id = 0 - self._track_id = 0 - self._mime_type: Optional[str] = None - self._language: Optional[str] = None - self._flow = False - - def getText(self) -> str: - return self._text - - def setText(self, text: str) -> None: - self._text = text - - def getStartMs(self) -> int: - return self._start_ms - - def setStartMs(self, startMs: int) -> None: - self._start_ms = startMs - - def getEndMs(self) -> int: - return self._end_ms - - def setEndMs(self, endMs: int) -> None: - self._end_ms = endMs - - def getDurationMs(self) -> int: - return self._end_ms - self._start_ms - - def getStyle(self) -> Optional[Dict[str, Any]]: - return self._style - - def setStyle(self, style: Dict[str, Any]) -> None: - self._style = dict(style) - - def getLanguage(self) -> Optional[str]: - return self._language - - def setLanguage(self, lang: str) -> None: - self._language = lang - - def getMimeType(self) -> Optional[str]: - return self._mime_type - - def setMimeType(self, mimeType: str) -> None: - self._mime_type = mimeType - - def isFlow(self) -> bool: - return self._flow - - def setFlow(self, flow: bool) -> None: - self._flow = flow - - def toString(self) -> str: - return f"TimedText(text={self._text[:50]}, start={self._start_ms}, end={self._end_ms})" - - -# ═══════════════════════════════════════════════════════════════ -# HttpUrlConnection (扩展) -# ═══════════════════════════════════════════════════════════════ - -class HttpUrlConnection(Stub): - """HttpURLConnection (扩展) — HTTP 连接管理""" - - def __init__(self, url: str = ""): - super().__init__() - self._url = url - self._method = HTTP_GET - self._connect_timeout = DEFAULT_CONNECT_TIMEOUT - self._read_timeout = DEFAULT_READ_TIMEOUT - self._request_properties: Dict[str, str] = {} - self._follow_redirects = True - self._instance_follow_redirects = True - self._use_caches = True - self._allow_user_interaction = False - self._do_input = True - self._do_output = False - self._if_modified_since = 0 - self._fixed_length = -1 - self._fixed_stream_mode = False - self._chunk_length = 0 - self._response_code = -1 - self._response_message: Optional[str] = None - self._content_length = -1 - self._content_type: Optional[str] = None - self._content_encoding: Optional[str] = None - self._expiration: Optional[int] = None - self._date: Optional[int] = None - self._last_modified: Optional[int] = None - self._header_fields: Dict[str, List[str]] = {} - self._input_stream: Optional[BinaryIO] = None - self._output_stream: Optional[BinaryIO] = None - self._connected = False - self._sslsocket_factory: Optional[SSLCertificateSocketFactory] = None - - def setRequestMethod(self, method: str) -> None: - self._method = method.upper() - - def getRequestMethod(self) -> str: - return self._method - - def setConnectTimeout(self, timeoutMillis: int) -> None: - self._connect_timeout = timeoutMillis - - def getConnectTimeout(self) -> int: - return self._connect_timeout - - def setReadTimeout(self, timeoutMillis: int) -> None: - self._read_timeout = timeoutMillis - - def getReadTimeout(self) -> int: - return self._read_timeout - - def setRequestProperty(self, key: str, value: str) -> None: - self._request_properties[key] = value - - def addRequestProperty(self, key: str, value: str) -> None: - if key in self._request_properties: - self._request_properties[key] += ", " + value - else: - self._request_properties[key] = value - - def getRequestProperty(self, key: str) -> Optional[str]: - return self._request_properties.get(key) - - def getRequestProperties(self) -> Dict[str, str]: - return dict(self._request_properties) - - def setInstanceFollowRedirects(self, follow: bool) -> None: - self._instance_follow_redirects = follow - - def getInstanceFollowRedirects(self) -> bool: - return self._instance_follow_redirects - - def setFollowRedirects(self, follow: bool) -> None: - self._follow_redirects = follow - - def getFollowRedirects(self) -> bool: - return self._follow_redirects - - def setUseCaches(self, use: bool) -> None: - self._use_caches = use - - def getUseCaches(self) -> bool: - return self._use_caches - - def setAllowUserInteraction(self, allow: bool) -> None: - self._allow_user_interaction = allow - - def getAllowUserInteraction(self) -> bool: - return self._allow_user_interaction - - def setDoInput(self, do: bool) -> None: - self._do_input = do - - def getDoInput(self) -> bool: - return self._do_input - - def setDoOutput(self, do: bool) -> None: - self._do_output = do - - def getDoOutput(self) -> bool: - return self._do_output - - def setIfModifiedSince(self, ifModifiedSince: int) -> None: - self._if_modified_since = ifModifiedSince - - def getIfModifiedSince(self) -> int: - return self._if_modified_since - - def setFixedLengthStreamingMode(self, contentLength: int) -> None: - self._fixed_length = contentLength - self._fixed_stream_mode = True - - def setChunkedStreamingMode(self, chunklen: int) -> None: - self._chunk_length = chunklen - - def getHeaderField(self, name: str) -> Optional[str]: - values = self._header_fields.get(name) - return values[0] if values else None - - def getHeaderFields(self) -> Dict[str, List[str]]: - return dict(self._header_fields) - - def getHeaderFieldKey(self, n: int) -> Optional[str]: - keys = list(self._header_fields.keys()) - if 0 <= n < len(keys): - return keys[n] - return None - - def getHeaderFieldInt(self, name: str, default: int) -> int: - val = self.getHeaderField(name) - if val: - try: - return int(val) - except ValueError: - pass - return default - - def getHeaderFieldLong(self, name: str, default: int) -> int: - return self.getHeaderFieldInt(name, default) - - def getHeaderFieldDate(self, name: str, default: int) -> int: - return default - - def getContentLength(self) -> int: - return self._content_length - - def getContentLengthLong(self) -> int: - return self._content_length - - def getContentType(self) -> Optional[str]: - return self._content_type - - def getContentEncoding(self) -> Optional[str]: - return self._content_encoding - - def getExpiration(self) -> int: - return self._expiration or 0 - - def getDate(self) -> int: - return self._date or 0 - - def getLastModified(self) -> int: - return self._last_modified or 0 - - def getResponseCode(self) -> int: - if not self._connected: - self.connect() - return self._response_code - - def getResponseMessage(self) -> Optional[str]: - return self._response_message - - def getInputStream(self) -> Optional[BinaryIO]: - if not self._connected: - self.connect() - return self._input_stream - - def getOutputStream(self) -> Optional[BinaryIO]: - if not self._connected: - self.connect() - return self._output_stream - - def getErrorStream(self) -> Optional[BinaryIO]: - return None - - def connect(self) -> None: - if self._connected: - return - self._connected = True - try: - req = _UrlRequest(self._url, method=self._method.encode() if hasattr(self._method, 'encode') else self._method.encode()) - for k, v in self._request_properties.items(): - req.add_header(k, v) - self._response_code = 200 - self._response_message = "OK" - self._content_type = "text/plain" - self._content_length = 0 - except Exception as e: - self._response_code = 0 - self._response_message = str(e) - - def disconnect(self) -> None: - self._connected = False - self._input_stream = None - self._output_stream = None - - def getURL(self) -> str: - return self._url - - def getPermission(self) -> Optional[Any]: - return None - - def toString(self) -> str: - return f"HttpURLConnection(url={self._url}, method={self._method})" - - -# ═══════════════════════════════════════════════════════════════ -# AndroidHttpClient -# ═══════════════════════════════════════════════════════════════ - -class AndroidHttpClient(Stub): - """AndroidHttpClient — Android HTTP 客户端 (扩展)""" - - def __init__(self, userAgent: str = "AndroidHttpClient"): - super().__init__() - self._user_agent = userAgent - self._connect_timeout = DEFAULT_CONNECT_TIMEOUT - self._read_timeout = DEFAULT_READ_TIMEOUT - self._follow_redirects = True - self._compression_enabled = True - self._cookie_store: Dict[str, Dict[str, str]] = {} - self._auth_cache: Dict[str, Tuple[str, str]] = {} - self._lock = threading.Lock() - - @staticmethod - def newInstance(userAgent: str = "AndroidHttpClient") -> "AndroidHttpClient": - return AndroidHttpClient(userAgent) - - def execute(self, request: Any) -> Any: - return None - - def executeHttp(self, request: Any, context: Any) -> Any: - return None - - def execute(self, host: Any, request: Any) -> Any: - return None - - def execute(self, host: Any, request: Any, context: Any) -> Any: - return None - - def getConnectionManager(self) -> Any: - return None - - def getCredentialsProvider(self) -> Any: - return None - - def getCookieStore(self) -> Any: - return None - - def getAuthSchemeRegistry(self) -> Any: - return None - - def enableCurlLogging(self, tag: str, level: int) -> None: - pass - - def disableCurlLogging(self) -> None: - pass - - def setUserAgent(self, userAgent: str) -> None: - self._user_agent = userAgent - - def getConnectTimeout(self) -> int: - return self._connect_timeout - - def setConnectTimeout(self, timeout: int) -> None: - self._connect_timeout = timeout - - def getReadTimeout(self) -> int: - return self._read_timeout - - def setReadTimeout(self, timeout: int) -> None: - self._read_timeout = timeout - - def isCompressionEnabled(self) -> bool: - return self._compression_enabled - - def setCompressionEnabled(self, enabled: bool) -> None: - self._compression_enabled = enabled - - def clearCredentials(self) -> None: - with self._lock: - self._auth_cache.clear() - - def clearCookies(self) -> None: - with self._lock: - self._cookie_store.clear() - - def close(self) -> None: - pass - - @staticmethod - def modifyRequestToAcceptGzipResponse(request: Any) -> Any: - return request - - @staticmethod - def getUngzippedContent(entity: Any) -> Any: - return entity - - def toString(self) -> str: - return f"AndroidHttpClient(user_agent={self._user_agent})" \ No newline at end of file diff --git a/android_pyapi/android/net_nsd.py b/android_pyapi/android/net_nsd.py deleted file mode 100644 index 2b047dd..0000000 --- a/android_pyapi/android/net_nsd.py +++ /dev/null @@ -1,514 +0,0 @@ -""" -Android NsdManager API — NsdManager, NsdServiceInfo, DiscoveryListener, RegistrationListener, ResolveListener -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import sys -import socket -import uuid -import threading -from typing import Any, Dict, List, Optional, Set, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -# PROTOCOL_* 常量 -PROTOCOL_DNS_SD = 0 - -# FAILURE_* 常量 -FAILURE_ALREADY_ACTIVE = 3 -FAILURE_INTERNAL_ERROR = 0 -FAILURE_MAX_LIMIT = 4 -FAILURE_SERVICE_NOT_FOUND = 1 -FAILURE_SERVICE_NOT_RUNNING = 2 - -# DNS_* 常量 -DNS_A = 1 -DNS_AAAA = 28 -DNS_CNAME = 5 -DNS_HINFO = 13 -DNS_MX = 15 -DNS_NS = 2 -DNS_PTR = 12 -DNS_SOA = 6 -DNS_SRV = 33 -DNS_TXT = 16 -DNS_WKS = 11 -DNS_ANY = 255 - -# SERVICE_TYPE 常量 -SERVICE_TYPE_HTTP = "_http._tcp" -SERVICE_TYPE_HTTPS = "_https._tcp" -SERVICE_TYPE_FTP = "_ftp._tcp" -SERVICE_TYPE_SSH = "_ssh._tcp" -SERVICE_TYPE_SMTP = "_smtp._tcp" -SERVICE_TYPE_IMAP = "_imap._tcp" -SERVICE_TYPE_AIRPLAY = "_airplay._tcp" -SERVICE_TYPE_GOOGLECAST = "_googlecast._tcp" -SERVICE_TYPE_DEVICE_INFO = "_device-info._tcp" -SERVICE_TYPE_PRINTER = "_printer._tcp" -SERVICE_TYPE_SCANNER = "_scanner._tcp" - - -# ═══════════════════════════════════════════════════════════════ -# NsdServiceInfo -# ═══════════════════════════════════════════════════════════════ - -class NsdServiceInfo(Stub): - """NsdServiceInfo — NSD 服务信息""" - - def __init__(self): - super().__init__() - self._service_name: Optional[str] = None - self._service_type: Optional[str] = None - self._host: Optional[str] = None - self._port: int = 0 - self._txt_records: Dict[str, bytes] = {} - self._attributes: Dict[str, str] = {} - self._network: Optional[Any] = None - self._interface_index = 0 - self._service_id = str(uuid.uuid4()) - self._ttl = 120 - self._priority = 0 - self._weight = 0 - self._is_resolved = False - self._addresses: List[str] = [] - - def getServiceName(self) -> Optional[str]: - return self._service_name - - def setServiceName(self, serviceName: str) -> None: - self._service_name = serviceName - - def getServiceType(self) -> Optional[str]: - return self._service_type - - def setServiceType(self, serviceType: str) -> None: - self._service_type = serviceType - - def getHost(self) -> Optional[str]: - return self._host - - def setHost(self, host: str) -> None: - self._host = host - - def getPort(self) -> int: - return self._port - - def setPort(self, port: int) -> None: - self._port = port - - def getTxtRecords(self) -> Dict[str, bytes]: - return dict(self._txt_records) - - def setTxtRecords(self, txtRecords: Dict[str, bytes]) -> None: - self._txt_records = dict(txtRecords) - - def getAttributes(self) -> Dict[str, str]: - return dict(self._attributes) - - def setAttribute(self, key: str, value: str) -> None: - self._attributes[key] = value - - def removeAttribute(self, key: str) -> None: - self._attributes.pop(key, None) - - def getNetwork(self) -> Optional[Any]: - return self._network - - def setNetwork(self, network: Any) -> None: - self._network = network - - def getInterfaceIndex(self) -> int: - return self._interface_index - - def setInterfaceIndex(self, index: int) -> None: - self._interface_index = index - - def getHostAddresses(self) -> List[str]: - return list(self._addresses) - - def addHostAddress(self, address: str) -> None: - if address not in self._addresses: - self._addresses.append(address) - - def getServiceId(self) -> str: - return self._service_id - - def getTtl(self) -> int: - return self._ttl - - def setTtl(self, ttl: int) -> None: - self._ttl = ttl - - def getPriority(self) -> int: - return self._priority - - def setPriority(self, priority: int) -> None: - self._priority = priority - - def getWeight(self) -> int: - return self._weight - - def setWeight(self, weight: int) -> None: - self._weight = weight - - def isResolved(self) -> bool: - return self._is_resolved - - def setResolved(self, resolved: bool) -> None: - self._is_resolved = resolved - - def getHostAddress(self) -> Optional[str]: - if self._addresses: - return self._addresses[0] - return self._host - - def toString(self) -> str: - return ( - f"NsdServiceInfo(name={self._service_name}, " - f"type={self._service_type}, " - f"host={self._host}, " - f"port={self._port})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# NsdManager 内部 Listener 类 -# ═══════════════════════════════════════════════════════════════ - -class NsdManagerDiscoveryListener(Stub): - """NsdManager.DiscoveryListener — 服务发现监听器""" - - def __init__(self): - super().__init__() - - def onDiscoveryStarted(self, serviceType: str) -> None: - pass - - def onDiscoveryStopped(self, serviceType: str) -> None: - pass - - def onServiceFound(self, serviceInfo: "NsdServiceInfo") -> None: - pass - - def onServiceLost(self, serviceInfo: "NsdServiceInfo") -> None: - pass - - def onStartDiscoveryFailed(self, serviceType: str, errorCode: int) -> None: - pass - - def onStopDiscoveryFailed(self, serviceType: str, errorCode: int) -> None: - pass - - def onDiscoveryStopped(self, serviceType: str) -> None: - pass - - def toString(self) -> str: - return "NsdManager.DiscoveryListener" - - -class NsdManagerRegistrationListener(Stub): - """NsdManager.RegistrationListener — 服务注册监听器""" - - def __init__(self): - super().__init__() - - def onRegistrationFailed(self, serviceInfo: "NsdServiceInfo", errorCode: int) -> None: - pass - - def onUnregistrationFailed(self, serviceInfo: "NsdServiceInfo", errorCode: int) -> None: - pass - - def onServiceRegistered(self, serviceInfo: "NsdServiceInfo") -> None: - pass - - def onServiceUnregistered(self, serviceInfo: "NsdServiceInfo") -> None: - pass - - def toString(self) -> str: - return "NsdManager.RegistrationListener" - - -class NsdManagerResolveListener(Stub): - """NsdManager.ResolveListener — 服务解析监听器""" - - def __init__(self): - super().__init__() - - def onResolveFailed(self, serviceInfo: "NsdServiceInfo", errorCode: int) -> None: - pass - - def onServiceResolved(self, serviceInfo: "NsdServiceInfo") -> None: - pass - - def toString(self) -> str: - return "NsdManager.ResolveListener" - - -# ═══════════════════════════════════════════════════════════════ -# NsdManager -# ═══════════════════════════════════════════════════════════════ - -class NsdManager(Stub): - """NsdManager — 网络服务发现管理器 (桌面存根)""" - - PROTOCOL_DNS_SD = PROTOCOL_DNS_SD - - FAILURE_ALREADY_ACTIVE = FAILURE_ALREADY_ACTIVE - FAILURE_INTERNAL_ERROR = FAILURE_INTERNAL_ERROR - FAILURE_MAX_LIMIT = FAILURE_MAX_LIMIT - FAILURE_SERVICE_NOT_FOUND = FAILURE_SERVICE_NOT_FOUND - FAILURE_SERVICE_NOT_RUNNING = FAILURE_SERVICE_NOT_RUNNING - - DiscoveryListener = NsdManagerDiscoveryListener - RegistrationListener = NsdManagerRegistrationListener - ResolveListener = NsdManagerResolveListener - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._lock = threading.Lock() - self._registered_services: Dict[str, NsdServiceInfo] = {} - self._discovery_listeners: Dict[str, NsdManagerDiscoveryListener] = {} - self._discovery_active: Set[str] = set() - self._resolved_services: Dict[str, NsdServiceInfo] = {} - self._discovered_services: Dict[str, List[NsdServiceInfo]] = {} - self._network_interface: Optional[str] = None - self._socket_family = socket.AF_INET - self._discovery_timeout = 5000 - self._is_available = True - - def discoverServices( - self, serviceType: str, protocolType: int, listener: NsdManagerDiscoveryListener - ) -> None: - """发现指定类型的服务""" - with self._lock: - if serviceType in self._discovery_active: - listener.onStartDiscoveryFailed(serviceType, FAILURE_ALREADY_ACTIVE) - return - self._discovery_active.add(serviceType) - self._discovery_listeners[serviceType] = listener - listener.onDiscoveryStarted(serviceType) - # 模拟发现 - for srv_id, info in self._registered_services.items(): - if info.getServiceType() == serviceType: - listener.onServiceFound(info) - - def stopServiceDiscovery(self, listener: NsdManagerDiscoveryListener) -> None: - """停止服务发现""" - with self._lock: - to_remove = [] - for stype, lst in self._discovery_listeners.items(): - if lst is listener: - to_remove.append(stype) - for stype in to_remove: - self._discovery_active.discard(stype) - del self._discovery_listeners[stype] - listener.onDiscoveryStopped(stype) - - def registerService( - self, serviceInfo: NsdServiceInfo, protocolType: int, listener: NsdManagerRegistrationListener - ) -> None: - """注册本地服务""" - with self._lock: - srv_id = f"{serviceInfo.getServiceName()}:{serviceInfo.getServiceType()}" - if srv_id in self._registered_services: - listener.onRegistrationFailed(serviceInfo, FAILURE_ALREADY_ACTIVE) - return - self._registered_services[srv_id] = serviceInfo - listener.onServiceRegistered(serviceInfo) - - def unregisterService(self, listener: NsdManagerRegistrationListener) -> None: - """注销本地服务""" - with self._lock: - to_remove = [] - for srv_id, info in self._registered_services.items(): - to_remove.append(srv_id) - for srv_id in to_remove: - info = self._registered_services.pop(srv_id) - listener.onServiceUnregistered(info) - - def resolveService( - self, serviceInfo: NsdServiceInfo, listener: NsdManagerResolveListener - ) -> None: - """解析服务(获取主机和端口)""" - with self._lock: - srv_id = f"{serviceInfo.getServiceName()}:{serviceInfo.getServiceType()}" - if srv_id in self._registered_services: - resolved = self._registered_services[srv_id] - resolved.setResolved(True) - if not resolved.getHost(): - resolved.setHost("127.0.0.1") - if resolved.getPort() == 0: - resolved.setPort(8080) - self._resolved_services[srv_id] = resolved - listener.onServiceResolved(resolved) - else: - listener.onResolveFailed(serviceInfo, FAILURE_SERVICE_NOT_FOUND) - - def getDiscoveredServices(self, serviceType: str) -> List[NsdServiceInfo]: - """获取已发现的服务列表""" - with self._lock: - return [ - info for info in self._registered_services.values() - if info.getServiceType() == serviceType - ] - - def getRegisteredServices(self) -> List[NsdServiceInfo]: - """获取所有已注册的服务""" - with self._lock: - return list(self._registered_services.values()) - - def isDiscoveryActive(self, serviceType: str) -> bool: - """检查指定类型的发现是否活跃""" - with self._lock: - return serviceType in self._discovery_active - - def setNetworkInterface(self, iface: str) -> None: - """设置网络接口""" - self._network_interface = iface - - def getNetworkInterface(self) -> Optional[str]: - return self._network_interface - - def setDiscoveryTimeout(self, timeoutMs: int) -> None: - self._discovery_timeout = timeoutMs - - def getDiscoveryTimeout(self) -> int: - return self._discovery_timeout - - def isAvailable(self) -> bool: - return self._is_available - - def destroy(self) -> None: - with self._lock: - self._discovery_active.clear() - self._discovery_listeners.clear() - self._registered_services.clear() - self._resolved_services.clear() - self._is_available = False - - def toString(self) -> str: - with self._lock: - return ( - f"NsdManager(registered={len(self._registered_services)}, " - f"active_discoveries={len(self._discovery_active)})" - ) - - # ═══════════════════════════════════════════════════════════ - # NsdManager 补充功能 - # ═══════════════════════════════════════════════════════════ - - def getServiceInfo(self, serviceName: str) -> Optional[NsdServiceInfo]: - """通过名称获取已注册的服务信息""" - with self._lock: - for srv_id, info in self._registered_services.items(): - if info.getServiceName() == serviceName: - return info - return None - - def removeService(self, serviceName: str) -> bool: - """移除已注册的服务""" - with self._lock: - to_remove = [ - srv_id for srv_id, info in self._registered_services.items() - if info.getServiceName() == serviceName - ] - for srv_id in to_remove: - del self._registered_services[srv_id] - return len(to_remove) > 0 - - def getResolvedService(self, serviceName: str) -> Optional[NsdServiceInfo]: - """获取已解析的服务""" - with self._lock: - for srv_id, info in self._resolved_services.items(): - if info.getServiceName() == serviceName: - return info - return None - - def getResolvedServices(self) -> List[NsdServiceInfo]: - """获取所有已解析的服务""" - with self._lock: - return list(self._resolved_services.values()) - - def getAllDiscoveredServices(self) -> List[NsdServiceInfo]: - """获取所有已发现的服务""" - with self._lock: - return [ - info for info in self._registered_services.values() - if info.isResolved() - ] - - def getActiveDiscoveryCount(self) -> int: - """获取活跃发现数量""" - with self._lock: - return len(self._discovery_active) - - def getActiveDiscoveryTypes(self) -> List[str]: - """获取正在发现的服务类型列表""" - with self._lock: - return list(self._discovery_active) - - def createServiceInfo( - self, serviceName: str, serviceType: str, port: int = 0 - ) -> NsdServiceInfo: - """创建服务信息对象""" - info = NsdServiceInfo() - info.setServiceName(serviceName) - info.setServiceType(serviceType) - info.setPort(port) - info.setHost("127.0.0.1") - return info - - -class NsdServiceInfoBuilder(Stub): - """NsdServiceInfo.Builder — 服务信息构建器""" - - def __init__(self): - super().__init__() - self._info = NsdServiceInfo() - - def setServiceName(self, name: str) -> "NsdServiceInfoBuilder": - self._info.setServiceName(name) - return self - - def setServiceType(self, serviceType: str) -> "NsdServiceInfoBuilder": - self._info.setServiceType(serviceType) - return self - - def setHost(self, host: str) -> "NsdServiceInfoBuilder": - self._info.setHost(host) - return self - - def setPort(self, port: int) -> "NsdServiceInfoBuilder": - self._info.setPort(port) - return self - - def setTxtRecords(self, records: Dict[str, bytes]) -> "NsdServiceInfoBuilder": - self._info.setTxtRecords(records) - return self - - def addAttribute(self, key: str, value: str) -> "NsdServiceInfoBuilder": - self._info.setAttribute(key, value) - return self - - def setNetwork(self, network: Any) -> "NsdServiceInfoBuilder": - self._info.setNetwork(network) - return self - - def build(self) -> NsdServiceInfo: - return self._info - - def toString(self) -> str: - return f"NsdServiceInfoBuilder(name={self._info.getServiceName()})" \ No newline at end of file diff --git a/android_pyapi/android/net_rtp.py b/android_pyapi/android/net_rtp.py deleted file mode 100644 index fa194e3..0000000 --- a/android_pyapi/android/net_rtp.py +++ /dev/null @@ -1,246 +0,0 @@ -""" -Android net.rtp 包 — AudioGroup, AudioCodec, RtpStream, AudioStream -方法签名完全对齐 Android SDK (此包已废弃但仍是 SDK 的一部分) -""" - -from __future__ import annotations - -import sys -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "AudioGroup", - "AudioCodec", - "RtpStream", - "AudioStream", -] - - -# ═══════════════════════════════════════════════════════════════ -# AudioGroup -# ═══════════════════════════════════════════════════════════════ - -class AudioGroup(Stub): - """AudioGroup — 音频组 (桌面存根)""" - - ON_HOLD = 0 - MUTED = 1 - NORMAL = 2 - ECHO_SUPPRESSION = 3 - - def __init__(self): - super().__init__() - self._group_mode = AudioGroup.NORMAL - self._streams: List["AudioStream"] = [] - - def clear(self) -> None: - """清除所有流""" - self._streams.clear() - - def getGroupMode(self) -> int: - """获取组模式""" - return self._group_mode - - def setMode(self, mode: int) -> None: - """设置模式""" - self._group_mode = mode - - def sendDtmf(self, digit: int) -> None: - """发送 DTMF 音调""" - pass - - def getRtpStream(self, index: int) -> Optional["RtpStream"]: - """获取 RTP 流""" - if 0 <= index < len(self._streams): - return self._streams[index] - return None - - -# ═══════════════════════════════════════════════════════════════ -# AudioCodec -# ═══════════════════════════════════════════════════════════════ - -class AudioCodec(Stub): - """AudioCodec — 音频编解码器 (桌面存根)""" - - AMR: "AudioCodec" = None - PCMA: "AudioCodec" = None - PCMU: "AudioCodec" = None - GSM_EFR: "AudioCodec" = None - - MIN_FRAME_SIZE = 1 - MAX_FRAME_SIZE = 320 - FRAME_COUNT = 8 - CODEC_AMR = 0 - CODEC_PCMA = 1 - CODEC_PCMU = 2 - CODEC_GSM_EFR = 3 - - def __init__(self, type_: int = 0, codec: str = "", codec_spec: str = "", fmtp: str = ""): - super().__init__() - self._type = type_ - self._codec = codec - self._codec_spec = codec_spec - self._fmtp = fmtp - - @property - def type(self) -> int: - """获取类型""" - return self._type - - @property - def codec(self) -> str: - """获取编解码器名称""" - return self._codec - - @property - def codecSpec(self) -> str: - """获取编解码器规格""" - return self._codec_spec - - @property - def fmtp(self) -> str: - """获取 fmtp""" - return self._fmtp - - @staticmethod - def getCapabilities() -> List["AudioCodec"]: - """获取能力列表""" - return [AudioCodec.AMR, AudioCodec.PCMA, AudioCodec.PCMU, AudioCodec.GSM_EFR] - - @staticmethod - def getCodecCapabilities(codec_type: int) -> Optional["AudioCodec"]: - """获取指定类型的编解码器能力""" - codecs = { - AudioCodec.CODEC_AMR: AudioCodec.AMR, - AudioCodec.CODEC_PCMA: AudioCodec.PCMA, - AudioCodec.CODEC_PCMU: AudioCodec.PCMU, - AudioCodec.CODEC_GSM_EFR: AudioCodec.GSM_EFR, - } - return codecs.get(codec_type) - - @staticmethod - def getEncoder(codec_type: int) -> Optional["AudioCodec"]: - """获取编码器""" - return AudioCodec.getCodecCapabilities(codec_type) - - @staticmethod - def getDecoder(codec_type: int) -> Optional["AudioCodec"]: - """获取解码器""" - return AudioCodec.getCodecCapabilities(codec_type) - - def toString(self) -> str: - return f"AudioCodec(type={self._type}, codec={self._codec})" - - -# Initialize AudioCodec constants -AudioCodec.AMR = AudioCodec(AudioCodec.CODEC_AMR, "AMR", "AMR/8000", "") -AudioCodec.PCMA = AudioCodec(AudioCodec.CODEC_PCMA, "PCMA", "PCMA/8000", "") -AudioCodec.PCMU = AudioCodec(AudioCodec.CODEC_PCMU, "PCMU", "PCMU/8000", "") -AudioCodec.GSM_EFR = AudioCodec(AudioCodec.CODEC_GSM_EFR, "GSM-EFR", "GSM-EFR/8000", "") - - -# ═══════════════════════════════════════════════════════════════ -# RtpStream -# ═══════════════════════════════════════════════════════════════ - -class RtpStream(Stub): - """RtpStream — RTP 流 (桌面存根)""" - - MODE_NORMAL = 0 - MODE_SEND_ONLY = 1 - MODE_RECEIVE_ONLY = 2 - MODE_SEND_RECEIVE = 3 - MODE_HOLD = 4 - - def __init__(self, local_address: str = "127.0.0.1", local_port: int = 5004): - super().__init__() - self._local_address = local_address - self._local_port = local_port - self._remote_address = "" - self._remote_port = 0 - self._mode = RtpStream.MODE_NORMAL - self._released = False - - def getLocalAddress(self) -> str: - """获取本地地址""" - return self._local_address - - def getLocalPort(self) -> int: - """获取本地端口""" - return self._local_port - - def getRemoteAddress(self) -> str: - """获取远程地址""" - return self._remote_address - - def getRemotePort(self) -> int: - """获取远程端口""" - return self._remote_port - - def getMode(self) -> int: - """获取模式""" - return self._mode - - def setMode(self, mode: int) -> None: - """设置模式""" - self._mode = mode - - def associate(self, remote_address: str, remote_port: int) -> None: - """关联远程地址端口""" - self._remote_address = remote_address - self._remote_port = remote_port - - def release(self) -> None: - """释放资源""" - self._released = True - - -# ═══════════════════════════════════════════════════════════════ -# AudioStream -# ═══════════════════════════════════════════════════════════════ - -class AudioStream(RtpStream): - """AudioStream — 音频流 (桌面存根)""" - - def __init__(self, local_address: str = "127.0.0.1", local_port: int = 5006): - super().__init__(local_address, local_port) - self._codec: Optional[AudioCodec] = None - self._group: Optional[AudioGroup] = None - self._dtmf_type: int = -1 - - def getCodec(self) -> Optional[AudioCodec]: - """获取编解码器""" - return self._codec - - def setCodec(self, codec: AudioCodec) -> None: - """设置编解码器""" - self._codec = codec - - def getGroup(self) -> Optional[AudioGroup]: - """获取音频组""" - return self._group - - def setGroup(self, group: Optional[AudioGroup]) -> None: - """设置音频组""" - self._group = group - - def getDtmfType(self) -> int: - """获取 DTMF 类型""" - return self._dtmf_type - - def setDtmfType(self, type_: int) -> None: - """设置 DTMF 类型""" - self._dtmf_type = type_ - - def join(self, group: AudioGroup) -> None: - """加入音频组""" - self._group = group - - def release(self) -> None: - super().release() \ No newline at end of file diff --git a/android_pyapi/android/net_sip.py b/android_pyapi/android/net_sip.py deleted file mode 100644 index 4297908..0000000 --- a/android_pyapi/android/net_sip.py +++ /dev/null @@ -1,639 +0,0 @@ -""" -Android SIP API — SipManager, SipProfile, SipSession, SipAudioCall, SipErrorCode, SipSessionAdapter -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import sys -import time -import uuid -import socket -import threading -from typing import Any, Dict, List, Optional, Set, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# SipErrorCode -# ═══════════════════════════════════════════════════════════════ - -class SipErrorCode(Stub): - """SipErrorCode — SIP 错误码""" - - NO_ERROR = 0 - SOCKET_ERROR = -1 - SERVER_ERROR = -2 - TRANSACTION_ISSUE = -3 - TRANSACTION_TERMINATED = -4 - INVALID_REMOTE_URI = -5 - PEER_NOT_REACHABLE = -6 - CLIENT_ERROR = -7 - INVALID_CREDENTIALS = -8 - IN_PROGRESS = -9 - NOT_REACHABLE = -10 - TIME_OUT = -11 - DATA_USAGE_LIMIT = -12 - CROSS_DOMAIN_AUTHENTICATION = -13 - SERVER_UNREACHABLE = -14 - INTERNAL_SERVER_ERROR = -15 - - def __init__(self): - super().__init__() - - -# ═══════════════════════════════════════════════════════════════ -# SipProfile -# ═══════════════════════════════════════════════════════════════ - -class SipProfile(Stub): - """SipProfile — SIP 用户档案""" - - class Builder(Stub): - """SipProfile.Builder — 构建器""" - - def __init__(self, uriString: str): - super().__init__() - self._profile = SipProfile() - self._profile._uri_string = uriString - - def __init__(self, settings: "SipProfile"): - super().__init__() - self._profile = SipProfile() - self._profile._uri_string = settings.getUriString() - self._profile._display_name = settings.getDisplayName() - self._profile._auth_user = settings.getAuthUserName() - self._profile._password = settings.getPassword() - self._profile._server = settings.getServer() - self._profile._port = settings.getPort() - self._profile._protocol = settings.getProtocol() - self._profile._send_keepalive = settings.getSendKeepAlive() - self._profile._auto_registration = settings.isAutoRegistrationEnabled() - - def setDisplayName(self, displayName: str) -> "SipProfile.Builder": - self._profile._display_name = displayName - return self - - def setAuthUserName(self, authUserName: str) -> "SipProfile.Builder": - self._profile._auth_user = authUserName - return self - - def setPassword(self, password: str) -> "SipProfile.Builder": - self._profile._password = password - return self - - def setServer(self, server: str) -> "SipProfile.Builder": - self._profile._server = server - return self - - def setPort(self, port: int) -> "SipProfile.Builder": - self._profile._port = port - return self - - def setProtocol(self, protocol: str) -> "SipProfile.Builder": - self._profile._protocol = protocol - return self - - def setSendKeepAlive(self, enable: bool) -> "SipProfile.Builder": - self._profile._send_keepalive = enable - return self - - def setAutoRegistration(self, enable: bool) -> "SipProfile.Builder": - self._profile._auto_registration = enable - return self - - def setProfilePicture(self, icon: Any) -> "SipProfile.Builder": - self._profile._profile_picture = icon - return self - - def build(self) -> "SipProfile": - return self._profile - - def __init__(self): - super().__init__() - self._uri_string: Optional[str] = None - self._display_name: Optional[str] = None - self._auth_user: Optional[str] = None - self._password: Optional[str] = None - self._server: Optional[str] = None - self._port: int = 5060 - self._protocol: str = "UDP" - self._send_keepalive = False - self._auto_registration = True - self._profile_picture: Optional[Any] = None - self._profile_id = str(uuid.uuid4()) - self._local_ip: Optional[str] = None - self._registration_time = 3600 - self._is_registered = False - self._created_at = time.time() - - def getUriString(self) -> Optional[str]: - return self._uri_string - - def getDisplayName(self) -> Optional[str]: - return self._display_name - - def getAuthUserName(self) -> Optional[str]: - return self._auth_user - - def getPassword(self) -> Optional[str]: - return self._password - - def getServer(self) -> Optional[str]: - return self._server - - def getPort(self) -> int: - return self._port - - def getProtocol(self) -> str: - return self._protocol - - def getSendKeepAlive(self) -> bool: - return self._send_keepalive - - def isAutoRegistrationEnabled(self) -> bool: - return self._auto_registration - - def getProfilePicture(self) -> Optional[Any]: - return self._profile_picture - - def getProfileId(self) -> str: - return self._profile_id - - def getLocalIp(self) -> Optional[str]: - return self._local_ip - - def setLocalIp(self, ip: str) -> None: - self._local_ip = ip - - def isRegistered(self) -> bool: - return self._is_registered - - def getRegistrationTime(self) -> int: - return self._registration_time - - def setRegistrationTime(self, seconds: int) -> None: - self._registration_time = seconds - - def getCreatedAt(self) -> float: - return self._created_at - - def toString(self) -> str: - return f"SipProfile(uri={self._uri_string}, display={self._display_name}, server={self._server})" - - -# ═══════════════════════════════════════════════════════════════ -# SipSessionAdapter -# ═══════════════════════════════════════════════════════════════ - -class SipSessionAdapter(Stub): - """SipSessionAdapter — SIP 会话适配器""" - - def __init__(self): - super().__init__() - - def onCalling(self, session: "SipSession") -> None: - pass - - def onRinging(self, session: "SipSession", caller: "SipProfile", options: Any) -> None: - pass - - def onRingingBack(self, session: "SipSession") -> None: - pass - - def onCallEstablished(self, session: "SipSession", sessionDescription: str) -> None: - pass - - def onCallEnded(self, session: "SipSession") -> None: - pass - - def onCallBusy(self, session: "SipSession") -> None: - pass - - def onCallChangeFailed(self, session: "SipSession", errorCode: int, errorMessage: str) -> None: - pass - - def onError(self, session: "SipSession", errorCode: int, errorMessage: str) -> None: - pass - - def onRegistering(self, session: "SipSession") -> None: - pass - - def onRegistrationDone(self, session: "SipSession", duration: int) -> None: - pass - - def onRegistrationFailed( - self, session: "SipSession", errorCode: int, errorMessage: str - ) -> None: - pass - - def onRegistrationTimeout(self, session: "SipSession") -> None: - pass - - def toString(self) -> str: - return "SipSessionAdapter" - - -# ═══════════════════════════════════════════════════════════════ -# SipSession -# ═══════════════════════════════════════════════════════════════ - -class SipSession(Stub): - """SipSession — SIP 会话""" - - # 会话状态 - STATE_READY_TO_CALL = 0 - STATE_INCOMING_CALL = 1 - STATE_INCOMING_CALL_ANSWERING = 2 - STATE_OUTGOING_CALL = 3 - STATE_OUTGOING_CALL_RINGING = 4 - STATE_OUTGOING_CALL_CANCELING = 5 - STATE_IN_CALL = 6 - STATE_ENDED = 7 - STATE_DEREGISTER = 8 - STATE_REGISTERING = 9 - STATE_REGISTERED = 10 - STATE_UNREGISTERED = 11 - STATE_PENDING = 12 - STATE_INVALID = -1 - - def __init__(self): - super().__init__() - self._session_id = str(uuid.uuid4()) - self._local_profile: Optional[SipProfile] = None - self._peer_profile: Optional[SipProfile] = None - self._state = SipSession.STATE_READY_TO_CALL - self._listener: Optional[SipSessionAdapter] = None - self._call_id: Optional[str] = None - self._is_in_call = False - self._call_duration = 0 - self._start_time: Optional[float] = None - self._peer_session_description: Optional[str] = None - self._local_session_description: Optional[str] = None - self._credentials: Optional[Any] = None - self._error_code = SipErrorCode.NO_ERROR - self._error_message: Optional[str] = None - self._lock = threading.Lock() - - def getSessionId(self) -> str: - return self._session_id - - def getLocalProfile(self) -> Optional[SipProfile]: - return self._local_profile - - def getPeerProfile(self) -> Optional[SipProfile]: - return self._peer_profile - - def setListener(self, listener: SipSessionAdapter) -> None: - self._listener = listener - - def getState(self) -> int: - return self._state - - def setState(self, state: int) -> None: - self._state = state - - def isInCall(self) -> bool: - return self._is_in_call - - def getCallId(self) -> Optional[str]: - return self._call_id - - def getCallDuration(self) -> int: - return self._call_duration - - def getLocalSessionDescription(self) -> Optional[str]: - return self._local_session_description - - def setLocalSessionDescription(self, description: str) -> None: - self._local_session_description = description - - def getPeerSessionDescription(self) -> Optional[str]: - return self._peer_session_description - - def setPeerSessionDescription(self, description: str) -> None: - self._peer_session_description = description - - def getErrorCode(self) -> int: - return self._error_code - - def getErrorMessage(self) -> Optional[str]: - return self._error_message - - def makeCall(self, profile: SipProfile, sessionDescription: str, timeout: int = 30) -> None: - self._peer_profile = profile - self._state = SipSession.STATE_OUTGOING_CALL - self._start_time = time.time() - self._is_in_call = True - if self._listener: - self._listener.onCalling(self) - self._listener.onRingingBack(self) - self._listener.onCallEstablished(self, sessionDescription) - - def answerCall(self, sessionDescription: str, timeout: int = 30) -> None: - self._state = SipSession.STATE_IN_CALL - self._start_time = time.time() - self._is_in_call = True - if self._listener: - self._listener.onCallEstablished(self, sessionDescription) - - def endCall(self) -> None: - self._state = SipSession.STATE_ENDED - self._is_in_call = False - if self._start_time: - self._call_duration = int(time.time() - self._start_time) - if self._listener: - self._listener.onCallEnded(self) - - def changeCall(self, sessionDescription: str, timeout: int = 30) -> None: - if self._listener: - self._listener.onCallChangeFailed(self, SipErrorCode.IN_PROGRESS, "changeCall not supported") - - def register(self, duration: int = 3600) -> None: - self._state = SipSession.STATE_REGISTERING - if self._listener: - self._listener.onRegistering(self) - self._state = SipSession.STATE_REGISTERED - self._listener.onRegistrationDone(self, duration) - - def unregister(self) -> None: - self._state = SipSession.STATE_UNREGISTERED - - def isRegistered(self) -> bool: - return self._state == SipSession.STATE_REGISTERED - - def toString(self) -> str: - return f"SipSession(state={self._state}, in_call={self._is_in_call})" - - -# ═══════════════════════════════════════════════════════════════ -# SipAudioCall -# ═══════════════════════════════════════════════════════════════ - -class SipAudioCall(Stub): - """SipAudioCall — SIP 音频通话""" - - class Listener(Stub): - """SipAudioCall.Listener — 音频通话监听器""" - - def __init__(self): - super().__init__() - - def onReady(self, call: "SipAudioCall") -> None: - pass - - def onRinging(self, call: "SipAudioCall", caller: "SipProfile") -> None: - pass - - def onRingingBack(self, call: "SipAudioCall") -> None: - pass - - def onCallEstablished(self, call: "SipAudioCall") -> None: - pass - - def onCallEnded(self, call: "SipAudioCall") -> None: - pass - - def onCallBusy(self, call: "SipAudioCall") -> None: - pass - - def onCallHeld(self, call: "SipAudioCall") -> None: - pass - - def onError(self, call: "SipAudioCall", errorCode: int, errorMessage: str) -> None: - pass - - def onChanged(self, call: "SipAudioCall") -> None: - pass - - def toString(self) -> str: - return "SipAudioCall.Listener" - - def __init__(self, context: Any = None, profile: Optional[SipProfile] = None): - super().__init__() - self._context = context - self._profile = profile - self._session: Optional[SipSession] = None - self._listener: Optional["SipAudioCall.Listener"] = None - self._local_holder: Optional[Any] = None - self._is_muted = False - self._is_speaker_on = False - self._is_in_call = False - self._start_time: Optional[float] = None - self._call_duration = 0 - self._audio_state = 0 - - def setListener(self, listener: "SipAudioCall.Listener") -> None: - self._listener = listener - - def makeCall(self, profile: SipProfile, sessionDescription: str, timeout: int = 30) -> "SipSession": - session = SipSession() - session._local_profile = self._profile - session.makeCall(profile, sessionDescription, timeout) - self._session = session - self._is_in_call = True - self._start_time = time.time() - if self._listener: - self._listener.onCallEstablished(self) - return session - - def answerCall(self, sessionDescription: str, timeout: int = 30) -> None: - if self._session: - self._session.answerCall(sessionDescription, timeout) - self._is_in_call = True - self._start_time = time.time() - if self._listener: - self._listener.onCallEstablished(self) - - def endCall(self) -> None: - if self._session: - self._session.endCall() - self._is_in_call = False - if self._start_time: - self._call_duration = int(time.time() - self._start_time) - if self._listener: - self._listener.onCallEnded(self) - - def holdCall(self, timeout: int = 30) -> None: - if self._listener: - self._listener.onCallHeld(self) - - def continueCall(self, timeout: int = 30) -> None: - pass - - def toggleMute(self) -> None: - self._is_muted = not self._is_muted - - def isMuted(self) -> bool: - return self._is_muted - - def setSpeakerMode(self, speakerMode: bool) -> None: - self._is_speaker_on = speakerMode - - def isSpeakerOn(self) -> bool: - return self._is_speaker_on - - def isInCall(self) -> bool: - return self._is_in_call - - def getCallDuration(self) -> int: - return self._call_duration - - def getLocalProfile(self) -> Optional[SipProfile]: - return self._profile - - def getPeerProfile(self) -> Optional[SipProfile]: - if self._session: - return self._session.getPeerProfile() - return None - - def getSession(self) -> Optional[SipSession]: - return self._session - - def setLocalHolder(self, holder: Any) -> None: - self._local_holder = holder - - def toString(self) -> str: - return f"SipAudioCall(in_call={self._is_in_call}, muted={self._is_muted})" - - -# ═══════════════════════════════════════════════════════════════ -# SipManager -# ═══════════════════════════════════════════════════════════════ - -class SipManager(Stub): - """SipManager — SIP 管理器 (桌面存根)""" - - EXTRA_CALL_ID = "android.sip.extra.CALL_ID" - EXTRA_OFFER_SD = "android.sip.extra.OFFER_SD" - EXTRA_PROFILE = "android.sip.extra.PROFILE" - EXTRA_RESULT = "android.sip.extra.RESULT" - EXTRA_ERROR_CODE = "android.sip.extra.ERROR_CODE" - EXTRA_ERROR_MESSAGE = "android.sip.extra.ERROR_MESSAGE" - - INCOMING_CALL_INTENT_ACTION = "android.SipDemo.INCOMING_CALL" - - _instance: Optional["SipManager"] = None - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._profiles: Dict[str, SipProfile] = {} - self._sessions: Dict[str, SipSession] = {} - self._audio_calls: Dict[str, SipAudioCall] = {} - self._lock = threading.Lock() - self._is_available = True - self._is_api_supported = True - self._sip_network_type = 0 - - @staticmethod - def isApiSupported(context: Any) -> bool: - return True - - @staticmethod - def isVoipSupported(context: Any) -> bool: - return True - - @staticmethod - def isSipWifiOnly(context: Any) -> bool: - return False - - @staticmethod - def get(context: Any) -> "SipManager": - if SipManager._instance is None: - SipManager._instance = SipManager(context) - return SipManager._instance - - @staticmethod - def getSipNetworkType(context: Any) -> int: - return 0 - - def open(self, profile: SipProfile) -> None: - with self._lock: - key = profile.getUriString() or profile.getProfileId() - self._profiles[key] = profile - - def close(self, profile: SipProfile) -> None: - with self._lock: - key = profile.getUriString() or profile.getProfileId() - self._profiles.pop(key, None) - - def isOpened(self, uri: str) -> bool: - with self._lock: - return uri in self._profiles - - def register(self, profile: SipProfile, expiryTime: int, listener: SipSessionAdapter) -> None: - with self._lock: - key = profile.getUriString() or profile.getProfileId() - self._profiles[key] = profile - profile._is_registered = True - session = SipSession() - session._local_profile = profile - session._listener = listener - self._sessions[key] = session - listener.onRegistrationDone(session, expiryTime) - - def unregister(self, profile: SipProfile, listener: SipSessionAdapter) -> None: - with self._lock: - key = profile.getUriString() or profile.getProfileId() - self._profiles.pop(key, None) - session = self._sessions.pop(key, None) - if session: - session.setState(SipSession.STATE_UNREGISTERED) - listener.onRegistrationTimeout(session) - - def isRegistered(self, uri: str) -> bool: - with self._lock: - profile = self._profiles.get(uri) - return profile is not None and profile.isRegistered() - - def makeAudioCall( - self, localProfile: SipProfile, peerProfile: SipProfile, listener: SipAudioCall.Listener, - timeout: int = 30 - ) -> SipAudioCall: - call = SipAudioCall(self._context, localProfile) - call.setListener(listener) - call.makeCall(peerProfile, "", timeout) - with self._lock: - key = peerProfile.getUriString() or str(uuid.uuid4()) - self._audio_calls[key] = call - return call - - def makeAudioCall( - self, localUri: str, peerUri: str, listener: SipAudioCall.Listener, timeout: int = 30 - ) -> SipAudioCall: - local = SipProfile.Builder(localUri).build() - peer = SipProfile.Builder(peerUri).build() - return self.makeAudioCall(local, peer, listener, timeout) - - def takeAudioCall(self, incomingCallIntent: Any, listener: SipAudioCall.Listener) -> SipAudioCall: - call = SipAudioCall(self._context) - call.setListener(listener) - return call - - def getSessionFor(self, intent: Any) -> Optional[SipSession]: - return None - - def getProfiles(self) -> List[SipProfile]: - with self._lock: - return list(self._profiles.values()) - - def getActiveSessions(self) -> List[SipSession]: - with self._lock: - return list(self._sessions.values()) - - def getActiveCalls(self) -> List[SipAudioCall]: - with self._lock: - return list(self._audio_calls.values()) - - def toString(self) -> str: - return ( - f"SipManager(profiles={len(self._profiles)}, " - f"sessions={len(self._sessions)}, " - f"calls={len(self._audio_calls)})" - ) \ No newline at end of file diff --git a/android_pyapi/android/net_ssl.py b/android_pyapi/android/net_ssl.py deleted file mode 100644 index b493c0c..0000000 --- a/android_pyapi/android/net_ssl.py +++ /dev/null @@ -1,406 +0,0 @@ -""" -Android net.ssl 组件 — SSLSessionCache, SSLEngine, SSLSession, SSLContext, SSLParameters 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import ssl -import threading -import time as _time -from typing import Any, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# SSLSessionCache -# ═══════════════════════════════════════════════════════════════ - -class SSLSessionCache(Stub): - """SSLSessionCache — SSL 会话缓存""" - - def __init__(self, path: Optional[str] = None): - super().__init__() - self._path = path - self._cache: Dict[str, Any] = {} - self._lock = threading.Lock() - - def install(self, ssl_context: "SSLContext"): - pass - - @staticmethod - def get(ssl_session: Any) -> Optional["SSLSessionCache"]: - return None - - def toString(self) -> str: - return f"SSLSessionCache@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# SSLSession -# ═══════════════════════════════════════════════════════════════ - -class SSLSession(Stub): - """SSLSession — SSL 会话""" - - def __init__(self): - super().__init__() - self._creation_time: int = int(_time.time() * 1000) - self._last_accessed_time: int = self._creation_time - self._application_buffer_size: int = 16384 - self._packet_buffer_size: int = 16640 - self._cipher_suite: str = "TLS_AES_128_GCM_SHA256" - self._protocol: str = "TLSv1.3" - self._id: bytes = os.urandom(32) - self._peer_host: str = "" - self._peer_port: int = 443 - self._local_certificates: Optional[List[Any]] = None - self._peer_certificates: Optional[List[Any]] = None - self._local_principal: Any = None - self._peer_principal: Any = None - self._values: Dict[str, Any] = {} - self._session_context: Optional["SSLContext"] = None - - def getApplicationBufferSize(self) -> int: - return self._application_buffer_size - - def getCipherSuite(self) -> str: - return self._cipher_suite - - def getCreationTime(self) -> int: - return self._creation_time - - def getId(self) -> bytes: - return self._id - - def getLastAccessedTime(self) -> int: - return self._last_accessed_time - - def getLocalCertificates(self) -> Optional[List[Any]]: - return self._local_certificates - - def getLocalPrincipal(self) -> Any: - return self._local_principal - - def getPacketBufferSize(self) -> int: - return self._packet_buffer_size - - def getPeerCertificates(self) -> Optional[List[Any]]: - return self._peer_certificates - - def getPeerHost(self) -> str: - return self._peer_host - - def getPeerPort(self) -> int: - return self._peer_port - - def getPeerPrincipal(self) -> Any: - return self._peer_principal - - def getProtocol(self) -> str: - return self._protocol - - def getSessionContext(self) -> Optional["SSLContext"]: - return self._session_context - - def getValue(self, name: str) -> Any: - return self._values.get(name) - - def getValueNames(self) -> List[str]: - return list(self._values.keys()) - - def invalidate(self): - self._last_accessed_time = 0 - self._values.clear() - - def putValue(self, name: str, value: Any): - self._values[name] = value - - def removeValue(self, name: str): - self._values.pop(name, None) - - def toString(self) -> str: - return f"SSLSession{{protocol={self._protocol}, cipher={self._cipher_suite}}}" - - -# ═══════════════════════════════════════════════════════════════ -# SSLParameters -# ═══════════════════════════════════════════════════════════════ - -class SSLParameters(Stub): - """SSLParameters — SSL 参数""" - - def __init__(self): - super().__init__() - self._cipher_suites: List[str] = [] - self._protocols: List[str] = [] - self._want_client_auth: bool = False - self._need_client_auth: bool = False - self._algorithm_constraints: Any = None - self._endpoint_identification_algorithm: Optional[str] = None - self._server_names: List[Any] = [] - self._sni_matchers: List[Any] = [] - self._named_groups: List[str] = [] - - def getCipherSuites(self) -> List[str]: - return list(self._cipher_suites) - - def setCipherSuites(self, cipherSuites: List[str]): - self._cipher_suites = list(cipherSuites) - - def getProtocols(self) -> List[str]: - return list(self._protocols) - - def setProtocols(self, protocols: List[str]): - self._protocols = list(protocols) - - def getWantClientAuth(self) -> bool: - return self._want_client_auth - - def setWantClientAuth(self, wantClientAuth: bool): - self._want_client_auth = wantClientAuth - - def getNeedClientAuth(self) -> bool: - return self._need_client_auth - - def setNeedClientAuth(self, needClientAuth: bool): - self._need_client_auth = needClientAuth - - def getAlgorithmConstraints(self) -> Any: - return self._algorithm_constraints - - def setAlgorithmConstraints(self, constraints: Any): - self._algorithm_constraints = constraints - - def getEndpointIdentificationAlgorithm(self) -> Optional[str]: - return self._endpoint_identification_algorithm - - def setEndpointIdentificationAlgorithm(self, algorithm: Optional[str]): - self._endpoint_identification_algorithm = algorithm - - def getServerNames(self) -> List[Any]: - return list(self._server_names) - - def setServerNames(self, serverNames: List[Any]): - self._server_names = list(serverNames) - - def getSNIMatchers(self) -> List[Any]: - return list(self._sni_matchers) - - def setSNIMatchers(self, sniMatchers: List[Any]): - self._sni_matchers = list(sniMatchers) - - def getNamedGroups(self) -> List[str]: - return list(self._named_groups) - - def setNamedGroups(self, namedGroups: List[str]): - self._named_groups = list(namedGroups) - - def toString(self) -> str: - return f"SSLParameters@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# SSLContext -# ═══════════════════════════════════════════════════════════════ - -class SSLContext(Stub): - """SSLContext — SSL 上下文""" - - def __init__(self, protocol: str = "TLS"): - super().__init__() - self._protocol = protocol - self._ssl_context = ssl.create_default_context() - self._socket_factory: Any = None - self._server_socket_factory: Any = None - self._default_ssl_params: Optional[SSLParameters] = None - self._supported_ssl_params: Optional[SSLParameters] = None - - @staticmethod - def getInstance(protocol: str = "TLS") -> "SSLContext": - ctx = SSLContext(protocol) - ctx._default_ssl_params = SSLParameters() - ctx._supported_ssl_params = SSLParameters() - return ctx - - def init(self, keyManager: Any, trustManager: Any, random: Any): - pass - - def getSocketFactory(self) -> Any: - if not self._socket_factory: - self._socket_factory = self._ssl_context.wrap_socket - return self._socket_factory - - def getServerSocketFactory(self) -> Any: - if not self._server_socket_factory: - self._server_socket_factory = self._ssl_context.wrap_socket - return self._server_socket_factory - - def createSSLEngine(self) -> "SSLEngine": - return SSLEngine(self) - - def createSSLEngine(self, peerHost: str, peerPort: int) -> "SSLEngine": - return SSLEngine(self, peerHost, peerPort) - - def getProtocol(self) -> str: - return self._protocol - - def getSupportedSSLParameters(self) -> SSLParameters: - if not self._supported_ssl_params: - self._supported_ssl_params = SSLParameters() - return self._supported_ssl_params - - def getDefaultSSLParameters(self) -> SSLParameters: - if not self._default_ssl_params: - self._default_ssl_params = SSLParameters() - return self._default_ssl_params - - def toString(self) -> str: - return f"SSLContext{{protocol={self._protocol}}}" - - -# ═══════════════════════════════════════════════════════════════ -# SSLEngine -# ═══════════════════════════════════════════════════════════════ - -class SSLEngine(Stub): - """SSLEngine — SSL 引擎""" - - HANDSHAKE_STATUS_NOT_STARTED = 0 - HANDSHAKE_STATUS_IN_PROGRESS = 1 - HANDSHAKE_STATUS_NEED_WRAP = 2 - HANDSHAKE_STATUS_NEED_UNWRAP = 3 - HANDSHAKE_STATUS_FINISHED = 4 - HANDSHAKE_STATUS_NEED_TASK = 5 - - def __init__(self, context: SSLContext, peerHost: str = "", peerPort: int = 443): - super().__init__() - self._context = context - self._peer_host = peerHost - self._peer_port = peerPort - self._handshake_status = self.HANDSHAKE_STATUS_NOT_STARTED - self._use_client_mode: bool = True - self._need_client_auth: bool = False - self._want_client_auth: bool = False - self._enabled_cipher_suites: List[str] = [ - "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", - "TLS_CHACHA20_POLY1305_SHA256", - ] - self._enabled_protocols: List[str] = ["TLSv1.3", "TLSv1.2"] - self._session = SSLSession() - self._delegated_tasks: List[Any] = [] - self._application_protocol: Optional[str] = None - self._handshake_application_protocol: Optional[str] = None - self._hs_protocol_selector: Any = None - - def beginHandshake(self): - self._handshake_status = self.HANDSHAKE_STATUS_IN_PROGRESS - - def closeInbound(self): - pass - - def closeOutbound(self): - pass - - def getDelegatedTask(self) -> Optional[Any]: - if self._delegated_tasks: - return self._delegated_tasks.pop(0) - return None - - def getEnabledCipherSuites(self) -> List[str]: - return list(self._enabled_cipher_suites) - - def getEnabledProtocols(self) -> List[str]: - return list(self._enabled_protocols) - - def getHandshakeStatus(self) -> int: - return self._handshake_status - - def getNeedClientAuth(self) -> bool: - return self._need_client_auth - - def getSession(self) -> SSLSession: - return self._session - - def getSupportedCipherSuites(self) -> List[str]: - return list(self._enabled_cipher_suites) - - def getSupportedProtocols(self) -> List[str]: - return list(self._enabled_protocols) - - def getUseClientMode(self) -> bool: - return self._use_client_mode - - def getWantClientAuth(self) -> bool: - return self._want_client_auth - - def setEnabledCipherSuites(self, suites: List[str]): - self._enabled_cipher_suites = list(suites) - - def setEnabledProtocols(self, protocols: List[str]): - self._enabled_protocols = list(protocols) - - def setNeedClientAuth(self, needClientAuth: bool): - self._need_client_auth = needClientAuth - - def setUseClientMode(self, useClientMode: bool): - self._use_client_mode = useClientMode - - def setWantClientAuth(self, wantClientAuth: bool): - self._want_client_auth = wantClientAuth - - def setSSLParameters(self, params: SSLParameters): - if params.getCipherSuites(): - self._enabled_cipher_suites = params.getCipherSuites() - if params.getProtocols(): - self._enabled_protocols = params.getProtocols() - self._need_client_auth = params.getNeedClientAuth() - self._want_client_auth = params.getWantClientAuth() - - def getSSLParameters(self) -> SSLParameters: - params = SSLParameters() - params.setCipherSuites(self._enabled_cipher_suites) - params.setProtocols(self._enabled_protocols) - params.setNeedClientAuth(self._need_client_auth) - params.setWantClientAuth(self._want_client_auth) - return params - - def wrap(self, src: Any, dst: Any) -> Any: - return src - - def unwrap(self, src: Any, dst: Any) -> Any: - return src - - def getApplicationProtocol(self) -> Optional[str]: - return self._application_protocol - - def getHandshakeApplicationProtocol(self) -> Optional[str]: - return self._handshake_application_protocol - - def setHandshakeApplicationProtocolSelector(self, selector: Any): - self._hs_protocol_selector = selector - - def getHandshakeApplicationProtocolSelector(self) -> Any: - return self._hs_protocol_selector - - def toString(self) -> str: - return f"SSLEngine{{handshake={self._handshake_status}, client={self._use_client_mode}}}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "SSLSessionCache", - "SSLEngine", - "SSLSession", - "SSLContext", - "SSLParameters", -] \ No newline at end of file diff --git a/android_pyapi/android/net_vpn.py b/android_pyapi/android/net_vpn.py deleted file mode 100644 index 612e8fa..0000000 --- a/android_pyapi/android/net_vpn.py +++ /dev/null @@ -1,279 +0,0 @@ -""" -Android net.vpn 包 — VpnManager, VpnService, VpnService.Builder -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import sys -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "VpnManager", - "VpnService", - "VpnServiceBuilder", -] - - -# ═══════════════════════════════════════════════════════════════ -# VpnManager -# ═══════════════════════════════════════════════════════════════ - -class VpnManager(Stub): - """VpnManager — VPN 管理器 (桌面存根)""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._package = "" - self._addresses: List[str] = [] - self._routes: List[str] = [] - self._mtu = 1500 - self._session = "" - self._configured_packages: List[str] = [] - self._blocking = False - self._blocking_packages: List[str] = [] - self._network: Optional[Any] = None - self._http_proxy: Optional[Any] = None - self._underlying_networks: Optional[List[Any]] = None - - def setPackage(self, package_name: str) -> "VpnManager": - """设置包名""" - self._package = package_name - return self - - def addAddress(self, address: str, prefix_length: int) -> "VpnManager": - """添加地址""" - self._addresses.append(f"{address}/{prefix_length}") - return self - - def removeAddress(self, address: str, prefix_length: int) -> "VpnManager": - """移除地址""" - addr = f"{address}/{prefix_length}" - if addr in self._addresses: - self._addresses.remove(addr) - return self - - def setRoute(self, address: str, prefix_length: int) -> "VpnManager": - """设置路由""" - self._routes.append(f"{address}/{prefix_length}") - return self - - def setMtu(self, mtu: int) -> "VpnManager": - """设置 MTU""" - self._mtu = mtu - return self - - def setSession(self, session: str) -> "VpnManager": - """设置会话名称""" - self._session = session - return self - - def setConfiguredPackages(self, packages: List[str]) -> "VpnManager": - """设置已配置的包""" - self._configured_packages = packages - return self - - def setBlocking(self, blocking: bool) -> "VpnManager": - """设置阻塞模式""" - self._blocking = blocking - return self - - def setBlockingPackages(self, packages: List[str]) -> "VpnManager": - """设置阻塞包""" - self._blocking_packages = packages - return self - - def establish(self) -> Optional["VpnService"]: - """建立 VPN""" - return VpnService(self._context) - - def prepare(self, context: Any) -> Optional[Any]: - """准备 VPN""" - return None - - def getNetwork(self) -> Optional[Any]: - """获取网络""" - return self._network - - def setHttpProxy(self, proxy: Any) -> "VpnManager": - """设置 HTTP 代理""" - self._http_proxy = proxy - return self - - def setUnderlyingNetworks(self, networks: Optional[List[Any]]) -> "VpnManager": - """设置底层网络""" - self._underlying_networks = networks - return self - - @staticmethod - def isAlwaysOnVpnSupported(context: Any) -> bool: - """检查是否支持始终开启 VPN""" - return False - - @staticmethod - def setAlwaysOnVpn(context: Any, vpn_component: Any, lockdown: bool) -> None: - """设置始终开启 VPN""" - raise AndroidOnlyError("setAlwaysOnVpn 仅在 Android 设备上可用") - - @staticmethod - def getAlwaysOnVpnPackage(context: Any) -> Optional[str]: - """获取始终开启 VPN 包名""" - return None - - -# ═══════════════════════════════════════════════════════════════ -# VpnService -# ═══════════════════════════════════════════════════════════════ - -class VpnService(Stub): - """VpnService — VPN 服务 (桌面存根)""" - - SERVICE_INTERFACE = "android.net.VpnService" - SERVICE_META_DATA_SUPPORTS_ALWAYS_ON = "android.net.VpnService.SUPPORTS_ALWAYS_ON" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._underlying_networks: Optional[List[Any]] = None - self._tunnel: Optional[Any] = None - - def onStartCommand(self, intent: Any, flags: int, start_id: int) -> int: - """启动命令""" - return 0 - - def onBind(self, intent: Any) -> Optional[Any]: - """绑定""" - return None - - def onRevoke(self) -> None: - """撤销""" - pass - - @staticmethod - def prepare(context: Any) -> Optional[Any]: - """准备 VPN""" - return None - - @staticmethod - def protect(socket: Any) -> bool: - """保护套接字免受 VPN 路由影响""" - return True - - def setUnderlyingNetworks(self, networks: Optional[List[Any]]) -> None: - """设置底层网络""" - self._underlying_networks = networks - - def toString(self) -> str: - return "VpnService" - - -# ═══════════════════════════════════════════════════════════════ -# VpnService.Builder (aliased as VpnServiceBuilder) -# ═══════════════════════════════════════════════════════════════ - -class VpnServiceBuilder(Stub): - """VpnService.Builder — VPN 服务构建器 (桌面存根)""" - - def __init__(self, vpn_service: Optional[VpnService] = None): - super().__init__() - self._vpn_service = vpn_service or VpnService() - self._mtu = 1500 - self._addresses: List[Tuple[str, int]] = [] - self._routes: List[Tuple[str, int]] = [] - self._search_domains: List[str] = [] - self._dns_servers: List[str] = [] - self._session = "" - self._configured_packages: List[str] = [] - self._allowed_applications: List[str] = [] - self._disallowed_applications: List[str] = [] - self._blocking = False - self._http_proxy: Optional[Any] = None - self._underlying_networks: Optional[List[Any]] = None - self._metered = False - - def setMtu(self, mtu: int) -> "VpnServiceBuilder": - """设置 MTU""" - self._mtu = mtu - return self - - def addAddress(self, address: str, prefix_length: int) -> "VpnServiceBuilder": - """添加地址""" - self._addresses.append((address, prefix_length)) - return self - - def addRoute(self, address: str, prefix_length: int) -> "VpnServiceBuilder": - """添加路由""" - self._routes.append((address, prefix_length)) - return self - - def addSearchDomain(self, domain: str) -> "VpnServiceBuilder": - """添加搜索域""" - self._search_domains.append(domain) - return self - - def addDnsServer(self, address: str) -> "VpnServiceBuilder": - """添加 DNS 服务器""" - self._dns_servers.append(address) - return self - - def setSession(self, session: str) -> "VpnServiceBuilder": - """设置会话名称""" - self._session = session - return self - - def setConfiguredPackages(self, packages: List[str]) -> "VpnServiceBuilder": - """设置已配置的包""" - self._configured_packages = packages - return self - - def setAllowedApplications(self, packages: List[str]) -> "VpnServiceBuilder": - """设置允许的应用""" - self._allowed_applications = packages - return self - - def setDisallowedApplications(self, packages: List[str]) -> "VpnServiceBuilder": - """设置不允许的应用""" - self._disallowed_applications = packages - return self - - def setBlocking(self, blocking: bool) -> "VpnServiceBuilder": - """设置阻塞模式""" - self._blocking = blocking - return self - - def setHttpProxy(self, proxy: Any) -> "VpnServiceBuilder": - """设置 HTTP 代理""" - self._http_proxy = proxy - return self - - def setUnderlyingNetworks(self, networks: Optional[List[Any]]) -> "VpnServiceBuilder": - """设置底层网络""" - self._underlying_networks = networks - return self - - def establish(self) -> Optional[Any]: - """建立 VPN 隧道""" - return None - - def setMetered(self, metered: bool) -> "VpnServiceBuilder": - """设置计量连接""" - self._metered = metered - return self - - def addAllowedApplication(self, package: str) -> "VpnServiceBuilder": - """添加允许的应用""" - if package not in self._allowed_applications: - self._allowed_applications.append(package) - return self - - def addDisallowedApplication(self, package: str) -> "VpnServiceBuilder": - """添加不允许的应用""" - if package not in self._disallowed_applications: - self._disallowed_applications.append(package) - return self \ No newline at end of file diff --git a/android_pyapi/android/net_wifi.py b/android_pyapi/android/net_wifi.py deleted file mode 100644 index 216103c..0000000 --- a/android_pyapi/android/net_wifi.py +++ /dev/null @@ -1,1136 +0,0 @@ -""" -Android WiFi 组件 — WifiManager, WifiInfo, ScanResult 等 -方法签名完全对齐 Android SDK -""" - -import os -import sys -import json -import random -from typing import Any, Dict, List, Optional, Set, Tuple, Union -from datetime import datetime - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) - -# Linux 真实实现导入 -try: - from .._linux_impl import IS_LINUX, LinuxNetwork, HAS_IP - _USE_LINUX = IS_LINUX -except ImportError: - _USE_LINUX = False - - -# ═══════════════════════════════════════════════════════════════ -# SupplicantState -# ═══════════════════════════════════════════════════════════════ - -class SupplicantState(Stub): - """SupplicantState — wpa_supplicant 状态枚举""" - - ASSOCIATED = "ASSOCIATED" - ASSOCIATING = "ASSOCIATING" - COMPLETED = "COMPLETED" - DISCONNECTED = "DISCONNECTED" - DORMANT = "DORMANT" - FOUR_WAY_HANDSHAKE = "FOUR_WAY_HANDSHAKE" - GROUP_HANDSHAKE = "GROUP_HANDSHAKE" - INACTIVE = "INACTIVE" - INTERFACE_DISABLED = "INTERFACE_DISABLED" - INVALID = "INVALID" - SCANNING = "SCANNING" - UNINITIALIZED = "UNINITIALIZED" - - _VALID_STATES = [ - "ASSOCIATED", "ASSOCIATING", "COMPLETED", "DISCONNECTED", - "DORMANT", "FOUR_WAY_HANDSHAKE", "GROUP_HANDSHAKE", "INACTIVE", - "INTERFACE_DISABLED", "INVALID", "SCANNING", "UNINITIALIZED", - ] - - def __init__(self, state: str = "DISCONNECTED"): - super().__init__() - self._state = state if state in self._VALID_STATES else "INVALID" - - def getState(self) -> str: - return self._state - - def toString(self) -> str: - return self._state - - @staticmethod - def isValid(state_str: str) -> bool: - return state_str in SupplicantState._VALID_STATES - - def compareTo(self, other: "SupplicantState") -> int: - if self._state == other._state: - return 0 - return -1 if (self._VALID_STATES.index(self._state) - < self._VALID_STATES.index(other._state)) else 1 - - def equals(self, other) -> bool: - if isinstance(other, SupplicantState): - return self._state == other._state - return False - - def hashCode(self) -> int: - return hash(self._state) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# WifiSsid -# ═══════════════════════════════════════════════════════════════ - -class WifiSsid(Stub): - """WifiSsid — WiFi SSID 封装""" - - def __init__(self, ssid: str = ""): - super().__init__() - self._ssid = ssid - - @staticmethod - def createFromAsciiBytes(ssid_bytes: bytes) -> "WifiSsid": - try: - return WifiSsid(ssid_bytes.decode("utf-8")) - except UnicodeDecodeError: - return WifiSsid(repr(ssid_bytes)) - - @staticmethod - def createFromHex(hex_str: str) -> "WifiSsid": - try: - decoded = bytes.fromhex(hex_str).decode("utf-8", errors="replace") - return WifiSsid(decoded) - except Exception: - return WifiSsid(hex_str) - - def toString(self) -> str: - return self._ssid - - -# ═══════════════════════════════════════════════════════════════ -# WifiConfiguration -# ═══════════════════════════════════════════════════════════════ - -class WifiConfiguration(Stub): - """WifiConfiguration — WiFi 网络配置""" - - SECURITY_OPEN = 0 - SECURITY_WEP = 1 - SECURITY_PSK = 2 - SECURITY_EAP = 3 - SECURITY_SAE = 4 - SECURITY_OWE = 5 - - class KeyMgmt(Stub): - NONE = 0 - WPA_PSK = 1 - WPA_EAP = 2 - IEEE8021X = 3 - WPA2_PSK = 4 - OSEN = 5 - FT_PSK = 6 - FT_EAP = 7 - SAE = 8 - OWE = 9 - SUITE_B_192 = 10 - WPA3_SAE = 8 - WPA3_OWE = 9 - - class GroupCipher(Stub): - NONE = 0 - TKIP = 1 - CCMP = 2 - TKIP_CCMP = 3 - AES = 4 - SMS4 = 5 - GCMP_128 = 6 - GCMP_256 = 7 - BIP_GMAC_128 = 8 - BIP_GMAC_256 = 9 - BIP_CMAC_256 = 10 - - class PairwiseCipher(Stub): - NONE = 0 - TKIP = 1 - CCMP = 2 - NONE_ = 3 - GCMP_128 = 4 - GCMP_256 = 5 - SMS4 = 6 - - class AuthAlgorithm(Stub): - OPEN = 0 - SHARED = 1 - LEAP = 2 - - class Protocol(Stub): - RSN = 0 - WPA = 1 - OSEN = 2 - - class Status(Stub): - CURRENT = 0 - DISABLED = 1 - ENABLED = 2 - - def __init__(self): - super().__init__() - self.SSID: Optional[str] = None - self.BSSID: Optional[str] = None - self.preSharedKey: Optional[str] = None - self.status: int = WifiConfiguration.Status.DISABLED - self.networkId: int = -1 - self.allowedKeyManagement: Set[int] = set() - self.allowedGroupCiphers: Set[int] = set() - self.allowedPairwiseCiphers: Set[int] = set() - self.allowedAuthAlgorithms: Set[int] = set() - self.allowedProtocols: Set[int] = set() - self.hiddenSSID: bool = False - self.priority: int = 0 - self.wepKeys: List[Optional[str]] = [None, None, None, None] - self.wepTxKeyIndex: int = 0 - self.isEnterprise: bool = False - self._enterprise_config: Optional["WifiEnterpriseConfig"] = None - - def getNetworkId(self) -> int: - return self.networkId - - def toString(self) -> str: - ssid = self.SSID or "(none)" - return f"WifiConfiguration(SSID={ssid}, id={self.networkId})" - - def hashCode(self) -> int: - return hash(self.SSID) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# WifiEnterpriseConfig -# ═══════════════════════════════════════════════════════════════ - -class WifiEnterpriseConfig(Stub): - """WifiEnterpriseConfig — 企业级 WiFi 配置""" - - def __init__(self): - super().__init__() - self._identity: str = "" - self._password: str = "" - self._ca_cert: Optional[str] = None - self._client_cert: Optional[str] = None - self._subject_match: str = "" - self._alt_subject_match: str = "" - self._domain_suffix_match: str = "" - self._eap_method: int = -1 - self._phase2_method: int = -1 - - def setIdentity(self, identity: str): - self._identity = identity - - def getIdentity(self) -> str: - return self._identity - - def setPassword(self, password: str): - self._password = password - - def getPassword(self) -> str: - return self._password - - def setCaCertificate(self, cert: Any): - pass - - def getCaCertificate(self) -> Optional[Any]: - return None - - def setClientCertificate(self, cert: Any): - pass - - def getClientCertificate(self) -> Optional[Any]: - return None - - def setSubjectMatch(self, match: str): - self._subject_match = match - - def getSubjectMatch(self) -> str: - return self._subject_match - - def setDomainSuffixMatch(self, match: str): - self._domain_suffix_match = match - - def getDomainSuffixMatch(self) -> str: - return self._domain_suffix_match - - def toString(self) -> str: - return f"WifiEnterpriseConfig(identity={self._identity})" - - -# ═══════════════════════════════════════════════════════════════ -# WifiInfo -# ═══════════════════════════════════════════════════════════════ - -class WifiInfo(Stub): - """WifiInfo — WiFi 连接信息""" - - LINK_SPEED_UNITS = "Mbps" - STANDARD_LEGACY = 1 - STANDARD_11N = 4 - STANDARD_11AC = 5 - STANDARD_11AX = 6 - STANDARD_11BE = 7 - SECURITY_TYPE_OPEN = 0 - SECURITY_TYPE_WEP = 1 - SECURITY_TYPE_PSK = 2 - SECURITY_TYPE_EAP = 3 - SECURITY_TYPE_SAE = 4 - SECURITY_TYPE_OWE = 5 - SECURITY_TYPE_WAPI_PSK = 6 - SECURITY_TYPE_PASSPOINT_R1_R2 = 7 - SECURITY_TYPE_PASSPOINT_R3 = 8 - - INVALID_RSSI = -127 - DEFAULT_RSSI = -60 - - def __init__(self): - super().__init__() - self._ssid: str = "" - self._bssid: str = "00:00:00:00:00:00" - self._rssi: int = self.INVALID_RSSI - self._link_speed: int = 0 - self._frequency: int = 0 - self._ip_address: int = 0 - self._mac_address: str = "02:00:00:00:00:00" - self._network_id: int = -1 - self._supplicant_state: SupplicantState = SupplicantState(SupplicantState.DISCONNECTED) - self._wifi_standard: int = self.STANDARD_LEGACY - self._is_hidden_ssid: bool = False - self._tx_link_speed: int = 0 - self._rx_link_speed: int = 0 - self._max_rx_link_speed: int = 0 - self._max_tx_link_speed: int = 0 - self._security_type: int = self.SECURITY_TYPE_OPEN - self._rssi_timestamp: int = 0 - - def getSSID(self) -> str: - return self._ssid - - def getBSSID(self) -> str: - return self._bssid - - def getRssi(self) -> int: - return self._rssi - - def getLinkSpeed(self) -> int: - return self._link_speed - - def getFrequency(self) -> int: - return self._frequency - - def getIpAddress(self) -> int: - return self._ip_address - - def getMacAddress(self) -> str: - return self._mac_address - - def getNetworkId(self) -> int: - return self._network_id - - def getSupplicantState(self) -> SupplicantState: - return self._supplicant_state - - def getWifiStandard(self) -> int: - return self._wifi_standard - - def isHiddenSSID(self) -> bool: - return self._is_hidden_ssid - - def getTxLinkSpeedMbps(self) -> int: - return self._tx_link_speed - - def getRxLinkSpeedMbps(self) -> int: - return self._rx_link_speed - - def getMaxSupportedRxLinkSpeedMbps(self) -> int: - return self._max_rx_link_speed - - def getMaxSupportedTxLinkSpeedMbps(self) -> int: - return self._max_tx_link_speed - - def getSecurityType(self) -> int: - return self._security_type - - def getRssiTimestamp(self) -> int: - return self._rssi_timestamp - - def toString(self) -> str: - return (f"WifiInfo(SSID={self._ssid}, BSSID={self._bssid}, " - f"rssi={self._rssi}, speed={self._link_speed} {self.LINK_SPEED_UNITS})") - - def hashCode(self) -> int: - return hash(self._bssid) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# ScanResult -# ═══════════════════════════════════════════════════════════════ - -class ScanResult(Stub): - """ScanResult — WiFi 扫描结果""" - - WIFI_BAND_24_GHZ = 0 - WIFI_BAND_5_GHZ = 1 - WIFI_BAND_6_GHZ = 2 - CHANNEL_WIDTH_20MHZ = 0 - CHANNEL_WIDTH_40MHZ = 1 - CHANNEL_WIDTH_80MHZ = 2 - CHANNEL_WIDTH_160MHZ = 3 - CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4 - - UNSPECIFIED = -1 - - def __init__(self): - super().__init__() - self.SSID: str = "" - self.BSSID: str = "" - self.capabilities: str = "" - self.level: int = 0 - self.frequency: int = 0 - self.timestamp: int = 0 - self.channelWidth: int = self.CHANNEL_WIDTH_20MHZ - self.centerFreq0: int = 0 - self.centerFreq1: int = 0 - self.operatorFriendlyName: str = "" - self.venueName: str = "" - self.is80211mcResponder: bool = False - self.isPasspointNetwork: bool = False - self._wifi_standard: int = 0 - self._seen: int = 0 - self._distance: Optional[float] = None - self._distance_sd: Optional[float] = None - - def getSSID(self) -> str: - return self.SSID - - def getBSSID(self) -> str: - return self.BSSID - - def getCapabilities(self) -> str: - return self.capabilities - - def getLevel(self) -> int: - return self.level - - def getFrequency(self) -> int: - return self.frequency - - def getTimestamp(self) -> int: - return self.timestamp - - def getChannelWidth(self) -> int: - return self.channelWidth - - def getCenterFreq0(self) -> int: - return self.centerFreq0 - - def getCenterFreq1(self) -> int: - return self.centerFreq1 - - def getOperatorFriendlyName(self) -> str: - return self.operatorFriendlyName - - def getVenueName(self) -> str: - return self.venueName - - def is80211mcResponder(self) -> bool: - return self.is80211mcResponder - - def isPasspointNetwork(self) -> bool: - return self.isPasspointNetwork - - def getWifiStandard(self) -> int: - return self._wifi_standard - - def getDistance(self) -> Optional[float]: - return self._distance - - def getDistanceSd(self) -> Optional[float]: - return self._distance_sd - - def toString(self) -> str: - return f"ScanResult(SSID={self.SSID}, BSSID={self.BSSID}, level={self.level})" - - -# ═══════════════════════════════════════════════════════════════ -# WifiNetworkSpecifier -# ═══════════════════════════════════════════════════════════════ - -class WifiNetworkSpecifier(Stub): - """WifiNetworkSpecifier — WiFi 网络说明符""" - - def __init__(self, ssid: Optional[str] = None, bssid: Optional[str] = None, - band: int = 0, security_type: int = 0): - super().__init__() - self._ssid = ssid - self._bssid = bssid - self._band = band - self._security_type = security_type - - def getSsid(self) -> Optional[str]: - return self._ssid - - def getBssid(self) -> Optional[str]: - return self._bssid - - def getBand(self) -> int: - return self._band - - def toString(self) -> str: - return f"WifiNetworkSpecifier(SSID={self._ssid})" - - -# ═══════════════════════════════════════════════════════════════ -# WifiNetworkSuggestion -# ═══════════════════════════════════════════════════════════════ - -class WifiNetworkSuggestion(Stub): - """WifiNetworkSuggestion — WiFi 网络建议""" - - def __init__(self, ssid: str = "", bssid: Optional[str] = None, - is_hidden: bool = False, priority: int = 0, - wpa2_passphrase: Optional[str] = None, - wpa3_passphrase: Optional[str] = None, - is_owe: bool = False): - super().__init__() - self._ssid = ssid - self._bssid = bssid - self._is_hidden = is_hidden - self._priority = priority - self._wpa2_passphrase = wpa2_passphrase - self._wpa3_passphrase = wpa3_passphrase - self._is_owe = is_owe - - def getSsid(self) -> str: - return self._ssid - - def getBssid(self) -> Optional[str]: - return self._bssid - - def isHidden(self) -> bool: - return self._is_hidden - - def getPriority(self) -> int: - return self._priority - - def toString(self) -> str: - return f"WifiNetworkSuggestion(SSID={self._ssid})" - - -# ═══════════════════════════════════════════════════════════════ -# WifiManager helpers -# ═══════════════════════════════════════════════════════════════ - -class WifiManagerMulticastLock(Stub): - """WifiManager.MulticastLock""" - - def __init__(self, tag: str): - super().__init__() - self._tag = tag - self._held = False - self._ref_count = 0 - - def acquire(self): - self._held = True - self._ref_count += 1 - - def release(self): - if self._ref_count > 0: - self._ref_count -= 1 - if self._ref_count == 0: - self._held = False - - def isHeld(self) -> bool: - return self._held - - def toString(self) -> str: - return f"MulticastLock(tag={self._tag}, held={self._held})" - - -class WifiManagerLocalOnlyHotspotCallback(Stub): - """WifiManager.LocalOnlyHotspotCallback""" - - ERROR_NO_CHANNEL = 1 - ERROR_GENERIC = 2 - ERROR_INCOMPATIBLE_MODE = 3 - ERROR_TETHERING_DISALLOWED = 4 - - def onStarted(self, hotspot: Any): - pass - - def onStopped(self): - pass - - def onFailed(self, reason: int): - pass - - -class WifiManagerWpsCallback(Stub): - """WifiManager.WpsCallback""" - - def onStarted(self, pin: str): - pass - - def onSucceeded(self): - pass - - def onFailed(self, reason: int): - pass - - -# ═══════════════════════════════════════════════════════════════ -# WifiManager -# ═══════════════════════════════════════════════════════════════ - -class WifiManager(Stub): - """WifiManager — WiFi 管理器""" - - WIFI_STATE_DISABLING = 0 - WIFI_STATE_DISABLED = 1 - WIFI_STATE_ENABLING = 2 - WIFI_STATE_ENABLED = 3 - WIFI_STATE_UNKNOWN = 4 - - WIFI_MODE_FULL = 1 - WIFI_MODE_FULL_HIGH_PERF = 3 - WIFI_MODE_SCAN_ONLY = 2 - - ERROR_AUTHENTICATING = 1 - SAP_START_FAILURE_GENERAL = 0 - - ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK" - SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE" - SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.SUPPLICANT_STATE_CHANGED" - NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE" - SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS" - WIFI_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_STATE_CHANGED" - RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED" - NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED" - - def __init__(self): - super().__init__() - self._wifi_state = self.WIFI_STATE_ENABLED - self._wifi_enabled = True - self._connection_info: Optional[WifiInfo] = None - self._scan_results: List[ScanResult] = [] - self._configured_networks: List[WifiConfiguration] = [] - self._network_suggestions: List[WifiNetworkSuggestion] = [] - self._listeners: Dict[str, List[Any]] = {} - self._next_network_id = 0 - self._p2p_channels: List[int] = [] - - def getConnectionInfo(self) -> Optional[WifiInfo]: - if _USE_LINUX: - return self._get_linux_connection_info() - if self._connection_info is None: - info = WifiInfo() - info._ssid = '"mock_wifi"' - info._bssid = "00:1A:2B:3C:4D:5E" - info._rssi = -55 - info._link_speed = 1300 - info._frequency = 5180 - info._network_id = 0 - info._supplicant_state = SupplicantState(SupplicantState.COMPLETED) - self._connection_info = info - return self._connection_info - - def getWifiInfo(self) -> Optional[WifiInfo]: - """getWifiInfo — getConnectionInfo 的别名""" - return self.getConnectionInfo() - - def _get_linux_connection_info(self) -> WifiInfo: - """从 LinuxNetwork.get_wifi_info() 构造 WifiInfo""" - info = WifiInfo() - try: - data = LinuxNetwork.get_wifi_info() or {} - ssid = data.get("ssid", "") - info._ssid = f'"{ssid}"' if ssid else "" - info._bssid = data.get("bssid", "00:00:00:00:00:00") - # signal 通常是 0-100, 转换为 RSSI (dBm, 约 -100..0) - signal = data.get("signal", 0) - if isinstance(signal, (int, float)) and signal > 0: - info._rssi = int(-100 + (signal / 100.0) * 60) - else: - info._rssi = int(signal) if signal else WifiInfo.INVALID_RSSI - iface = data.get("interface", "") - if iface: - info._frequency = 2412 # 默认 2.4GHz - info._link_speed = data.get("link_speed", 0) or 0 - info._network_id = 0 - info._supplicant_state = SupplicantState(SupplicantState.COMPLETED) - except Exception: - pass - return info - - def getScanResults(self) -> List[ScanResult]: - if _USE_LINUX: - results = self._linux_scan_results() - if results is not None: - return results - return list(self._scan_results) - - def _linux_scan_results(self) -> Optional[List[ScanResult]]: - """通过 nmcli 获取 WiFi 扫描结果 (真实实现)""" - import subprocess - import re - try: - proc = subprocess.run( - ["nmcli", "-t", "-f", "ssid,bssid,signal,freq,security", - "dev", "wifi", "list"], - capture_output=True, text=True, timeout=15, - ) - if proc.returncode != 0: - return None - results: List[ScanResult] = [] - seen: Set[str] = set() - for line in proc.stdout.strip().split("\n"): - if not line: - continue - # nmcli -t 模式中值内的 : 被转义为 \:, 按非转义 : 分割 - parts = re.split(r'(? 4 else "" - results.append(sr) - return results - except Exception: - return None - - def startScan(self) -> bool: - return True - - def setWifiEnabled(self, enabled: bool) -> bool: - if _USE_LINUX: - import subprocess - import shutil - try: - cmd = ["nmcli", "radio", "wifi", "on" if enabled else "off"] - proc = subprocess.run(cmd, capture_output=True, text=True, timeout=5) - if proc.returncode != 0 and shutil.which("rfkill"): - # 回退到 rfkill - rfkill_cmd = (["rfkill", "unblock", "wifi"] if enabled - else ["rfkill", "block", "wifi"]) - subprocess.run(rfkill_cmd, capture_output=True, timeout=5) - except Exception: - pass - self._wifi_enabled = enabled - self._wifi_state = self.WIFI_STATE_ENABLED if enabled else self.WIFI_STATE_DISABLED - return True - - def isWifiEnabled(self) -> bool: - if _USE_LINUX: - return self._linux_has_wireless_interface() - return self._wifi_enabled - - def _linux_has_wireless_interface(self) -> bool: - """检查是否存在无线网络接口""" - try: - for iface in LinuxNetwork.get_interfaces(): - name = iface.get("ifname") or iface.get("name", "") - if name and name.startswith(("wl", "wlan", "wlp")): - return True - # 回退: 检查 /sys/class/net 下的无线接口 - import os as _os - for name in _os.listdir("/sys/class/net/") if _os.path.isdir("/sys/class/net") else []: - if name.startswith(("wl", "wlan", "wlp")): - return True - except Exception: - pass - return False - - def getWifiState(self) -> int: - return self._wifi_state - - def getConfiguredNetworks(self) -> List[WifiConfiguration]: - return list(self._configured_networks) - - def addNetwork(self, config: WifiConfiguration) -> int: - config.networkId = self._next_network_id - self._next_network_id += 1 - self._configured_networks.append(config) - return config.networkId - - def updateNetwork(self, config: WifiConfiguration) -> int: - for i, c in enumerate(self._configured_networks): - if c.networkId == config.networkId: - self._configured_networks[i] = config - return config.networkId - return -1 - - def removeNetwork(self, net_id: int) -> bool: - self._configured_networks = [ - c for c in self._configured_networks if c.networkId != net_id - ] - return True - - def enableNetwork(self, net_id: int, disable_others: bool = False) -> bool: - for c in self._configured_networks: - if c.networkId == net_id: - c.status = WifiConfiguration.Status.ENABLED - elif disable_others: - c.status = WifiConfiguration.Status.DISABLED - return True - - def disableNetwork(self, net_id: int) -> bool: - for c in self._configured_networks: - if c.networkId == net_id: - c.status = WifiConfiguration.Status.DISABLED - return True - - def disconnect(self) -> bool: - return True - - def reconnect(self) -> bool: - return True - - def reassociate(self) -> bool: - return True - - def is5GHzBandSupported(self) -> bool: - return True - - def is6GHzBandSupported(self) -> bool: - import sys - return sys.version_info >= (3, 10) - - def isEasyConnectSupported(self) -> bool: - return False - - def getP2pChannelList(self) -> List[int]: - return list(self._p2p_channels) - - def startLocalOnlyHotspot(self, callback: WifiManagerLocalOnlyHotspotCallback, handler: Any = None): - try: - callback.onStarted("MockHotspot") - except Exception: - pass - - def addNetworkSuggestions(self, suggestions: List[WifiNetworkSuggestion]) -> int: - self._network_suggestions.extend(suggestions) - return 0 # STATUS_SUGGESTION_SUCCESS - - def removeNetworkSuggestions(self, suggestions: List[WifiNetworkSuggestion]) -> int: - for s in suggestions: - self._network_suggestions = [x for x in self._network_suggestions if x is not s] - return 0 - - def createMulticastLock(self, tag: str) -> WifiManagerMulticastLock: - return WifiManagerMulticastLock(tag) - - def getDhcpInfo(self): - from .net import DhcpInfo - return DhcpInfo() - - def pingSupplicant(self) -> bool: - return True - - def startScanActive(self) -> bool: - return True - - def setScanAlwaysAvailable(self, enabled: bool): - pass - - def isScanAlwaysAvailable(self) -> bool: - return True - - def saveConfiguration(self) -> bool: - return True - - def toString(self) -> str: - return f"WifiManager(state={self._wifi_state})" - - -# ═══════════════════════════════════════════════════════════════ -# WifiP2pManager -# ═══════════════════════════════════════════════════════════════ - -class WifiP2pDevice(Stub): - """WifiP2pDevice — P2P 设备""" - - CONNECTED = 0 - INVITED = 1 - FAILED = 2 - AVAILABLE = 3 - UNAVAILABLE = 4 - - def __init__(self): - super().__init__() - self.deviceAddress: str = "" - self.deviceName: str = "" - self.primaryDeviceType: str = "" - self.secondaryDeviceType: str = "" - self.wpsConfigMethods: int = 0 - self.deviceCapability: int = 0 - self.groupCapability: int = 0 - self.status: int = self.UNAVAILABLE - self.GO_PORT: int = 0 - - def getDeviceAddress(self) -> str: - return self.deviceAddress - - def getDeviceName(self) -> str: - return self.deviceName - - def isServiceDiscoveryCapable(self) -> bool: - return True - - def isGroupOwner(self) -> bool: - return self.groupCapability > 0 - - def toString(self) -> str: - return f"WifiP2pDevice(name={self.deviceName}, addr={self.deviceAddress})" - - -class WifiP2pConfig(Stub): - """WifiP2pConfig — P2P 配置""" - - class Builder(Stub): - def __init__(self): - super().__init__() - self._device_address = "" - self._network_name = "" - self._passphrase = "" - self._group_owner_band = 0 - - def setDeviceAddress(self, addr: str) -> "WifiP2pConfig.Builder": - self._device_address = addr - return self - - def setNetworkName(self, name: str) -> "WifiP2pConfig.Builder": - self._network_name = name - return self - - def setPassphrase(self, passphrase: str) -> "WifiP2pConfig.Builder": - self._passphrase = passphrase - return self - - def setGroupOwnerBand(self, band: int) -> "WifiP2pConfig.Builder": - self._group_owner_band = band - return self - - def build(self) -> "WifiP2pConfig": - config = WifiP2pConfig() - config.deviceAddress = self._device_address - config.networkName = self._network_name - config.passphrase = self._passphrase - return config - - def __init__(self): - super().__init__() - self.deviceAddress: str = "" - self.networkName: str = "" - self.passphrase: str = "" - - -class WifiP2pGroup(Stub): - """WifiP2pGroup — P2P 群组""" - - def __init__(self): - super().__init__() - self._network_name: str = "" - self._is_group_owner: bool = False - self._owner: Optional[WifiP2pDevice] = None - self._clients: List[WifiP2pDevice] = [] - self._passphrase: str = "" - self._interface: str = "" - - def getNetworkName(self) -> str: - return self._network_name - - def isGroupOwner(self) -> bool: - return self._is_group_owner - - def getOwner(self) -> Optional[WifiP2pDevice]: - return self._owner - - def getClientList(self) -> List[WifiP2pDevice]: - return list(self._clients) - - def getPassphrase(self) -> str: - return self._passphrase - - def getInterface(self) -> str: - return self._interface - - def toString(self) -> str: - return f"WifiP2pGroup(name={self._network_name})" - - -class WifiP2pManager(Stub): - """WifiP2pManager — P2P 管理器""" - - WIFI_P2P_STATE_ENABLED_ACTION = "android.net.wifi.p2p.STATE_CHANGED" - WIFI_P2P_STATE_CHANGED_ACTION = "android.net.wifi.p2p.STATE_CHANGED" - WIFI_P2P_PEERS_CHANGED_ACTION = "android.net.wifi.p2p.PEERS_CHANGED" - WIFI_P2P_CONNECTION_CHANGED_ACTION = "android.net.wifi.p2p.CONNECTION_CHANGED" - WIFI_P2P_THIS_DEVICE_CHANGED_ACTION = "android.net.wifi.p2p.THIS_DEVICE_CHANGED" - WIFI_P2P_DISCOVERY_CHANGED_ACTION = "android.net.wifi.p2p.DISCOVERY_CHANGED" - - class Channel(Stub): - """P2P 通道""" - - def __init__(self): - super().__init__() - - def __init__(self): - super().__init__() - self._channel: Optional[WifiP2pManager.Channel] = None - self._peers: List[WifiP2pDevice] = [] - - def initialize(self, context: Any, looper: Any, listener: Any) -> "WifiP2pManager.Channel": - self._channel = WifiP2pManager.Channel() - if listener: - try: - listener.onSuccess() - except Exception: - pass - return self._channel - - def discoverPeers(self, channel: "WifiP2pManager.Channel", listener: Any): - if listener: - try: - listener.onSuccess() - except Exception: - pass - - def discoverServices(self, channel: "WifiP2pManager.Channel", listener: Any): - if listener: - try: - listener.onSuccess() - except Exception: - pass - - def requestPeers(self, channel: "WifiP2pManager.Channel", listener: Any): - if listener: - try: - listener.onPeersAvailable(self._peers) - except Exception: - pass - - def requestGroupInfo(self, channel: "WifiP2pManager.Channel", listener: Any): - if listener: - try: - listener.onGroupInfoAvailable(WifiP2pGroup()) - except Exception: - pass - - def createGroup(self, channel: "WifiP2pManager.Channel", listener: Any): - if listener: - try: - listener.onSuccess() - except Exception: - pass - - def removeGroup(self, channel: "WifiP2pManager.Channel", listener: Any): - if listener: - try: - listener.onSuccess() - except Exception: - pass - - def connect(self, channel: "WifiP2pManager.Channel", config: WifiP2pConfig, listener: Any): - if listener: - try: - listener.onSuccess() - except Exception: - pass - - def cancelConnect(self, channel: "WifiP2pManager.Channel", listener: Any): - if listener: - try: - listener.onSuccess() - except Exception: - pass - - def stopPeerDiscovery(self, channel: "WifiP2pManager.Channel", listener: Any): - if listener: - try: - listener.onSuccess() - except Exception: - pass - - def requestDeviceInfo(self, channel: "WifiP2pManager.Channel", device_address: str, listener: Any): - if listener: - try: - listener.onDeviceInfoAvailable(WifiP2pDevice()) - except Exception: - pass - - def setDeviceName(self, channel: "WifiP2pManager.Channel", device_name: str, listener: Any): - if listener: - try: - listener.onSuccess() - except Exception: - pass - - def setWfdEnabled(self, channel: "WifiP2pManager.Channel", enabled: bool, listener: Any): - pass - - def toString(self) -> str: - return f"WifiP2pManager(peers={len(self._peers)})" - - -# ═══════════════════════════════════════════════════════════════ -# WifiConfigurationKeyMgmt / WifiConfiguration... aliases -# ═══════════════════════════════════════════════════════════════ - -WifiConfigurationKeyMgmt = WifiConfiguration.KeyMgmt -WifiConfigurationGroupCipher = WifiConfiguration.GroupCipher -WifiConfigurationPairwiseCipher = WifiConfiguration.PairwiseCipher -WifiConfigurationAuthAlgorithm = WifiConfiguration.AuthAlgorithm -WifiConfigurationProtocol = WifiConfiguration.Protocol -WifiConfigurationStatus = WifiConfiguration.Status - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "WifiManager", - "WifiInfo", - "WifiConfiguration", - "WifiConfigurationKeyMgmt", - "WifiConfigurationGroupCipher", - "WifiConfigurationPairwiseCipher", - "WifiConfigurationAuthAlgorithm", - "WifiConfigurationProtocol", - "WifiConfigurationStatus", - "ScanResult", - "WifiSsid", - "SupplicantState", - "WifiEnterpriseConfig", - "WifiNetworkSpecifier", - "WifiNetworkSuggestion", - "WifiManagerLocalOnlyHotspotCallback", - "WifiManagerWpsCallback", - "WifiManagerMulticastLock", - "WifiP2pManager", - "WifiP2pDevice", - "WifiP2pConfig", - "WifiP2pGroup", -] \ No newline at end of file diff --git a/android_pyapi/android/net_wifi_aware.py b/android_pyapi/android/net_wifi_aware.py deleted file mode 100644 index 869e87d..0000000 --- a/android_pyapi/android/net_wifi_aware.py +++ /dev/null @@ -1,428 +0,0 @@ -""" -Android net.wifi.aware 组件 — WifiAwareManager, WifiAwareSession, PublishConfig, SubscribeConfig 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import threading -import uuid -from typing import Any, Callable, Dict, List, Optional, Set - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# Characteristics -# ═══════════════════════════════════════════════════════════════ - -class Characteristics(Stub): - """Characteristics — Wi-Fi Aware 特性""" - - def __init__(self): - super().__init__() - self._max_service_name_length: int = 255 - self._max_service_specific_info_length: int = 255 - self._max_match_filter_length: int = 255 - self._max_number_of_publish_service_sessions: int = 10 - self._max_number_of_subscribe_service_sessions: int = 10 - self._max_message_length: int = 2048 - self._max_peers: int = 10 - - def getMaxServiceNameLength(self) -> int: - return self._max_service_name_length - - def getMaxServiceSpecificInfoLength(self) -> int: - return self._max_service_specific_info_length - - def getMaxMatchFilterLength(self) -> int: - return self._max_match_filter_length - - def getMaxNumberOfPublishServiceSessions(self) -> int: - return self._max_number_of_publish_service_sessions - - def getMaxNumberOfSubscribeServiceSessions(self) -> int: - return self._max_number_of_subscribe_service_sessions - - def getMaxMessageLength(self) -> int: - return self._max_message_length - - def getMaxPeers(self) -> int: - return self._max_peers - - def toString(self) -> str: - return f"Characteristics@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# AttachCallback -# ═══════════════════════════════════════════════════════════════ - -class AttachCallback(Stub): - """AttachCallback — 连接回调""" - - def onAttached(self, session: "WifiAwareSession"): - pass - - def onAttachFailed(self): - pass - - -# ═══════════════════════════════════════════════════════════════ -# IdentityChangedListener -# ═══════════════════════════════════════════════════════════════ - -class IdentityChangedListener(Stub): - """IdentityChangedListener — 身份变更监听器""" - - def onIdentityChanged(self, macAddress: bytes): - pass - - -# ═══════════════════════════════════════════════════════════════ -# DiscoverySessionCallback -# ═══════════════════════════════════════════════════════════════ - -class DiscoverySessionCallback(Stub): - """DiscoverySessionCallback — 发现会话回调""" - - def onServiceDiscovered(self, peerHandle: Any, serviceSpecificInfo: bytes, - matchFilter: bytes): - pass - - def onServiceDiscoveredWithinRange(self, peerHandle: Any, - serviceSpecificInfo: bytes, - matchFilter: bytes, rangeMm: int): - pass - - def onMessageReceived(self, peerHandle: Any, message: bytes): - pass - - def onMessageSendSucceeded(self, messageId: int): - pass - - def onMessageSendFailed(self, messageId: int): - pass - - def onSessionConfigUpdated(self): - pass - - def onSessionConfigFailed(self): - pass - - def onSessionTerminated(self): - pass - - def onMatch(self, peerHandle: Any, serviceSpecificInfo: bytes, - matchFilter: bytes, rangeMm: int): - pass - - -# ═══════════════════════════════════════════════════════════════ -# PublishConfig -# ═══════════════════════════════════════════════════════════════ - -class PublishConfig(Stub): - """PublishConfig — 发布配置""" - - PUBLISH_TYPE_UNSOLICITED = 0 - PUBLISH_TYPE_SOLICITED = 1 - - def __init__(self, service_name: str = "", service_specific_info: bytes = b"", - match_filter: bytes = b"", terminate_notification_enabled: bool = True, - ranging_enabled: bool = False, publish_type: int = PUBLISH_TYPE_UNSOLICITED): - super().__init__() - self._service_name = service_name - self._service_specific_info = service_specific_info - self._match_filter = match_filter - self._terminate_notification_enabled = terminate_notification_enabled - self._ranging_enabled = ranging_enabled - self._publish_type = publish_type - - class Builder(Stub): - """PublishConfig.Builder — 构建器""" - - def __init__(self): - super().__init__() - self._service_name: str = "" - self._service_specific_info: bytes = b"" - self._match_filter: bytes = b"" - self._terminate_notification_enabled: bool = True - self._ranging_enabled: bool = False - self._publish_type: int = PublishConfig.PUBLISH_TYPE_UNSOLICITED - - def setServiceName(self, serviceName: str) -> "PublishConfig.Builder": - self._service_name = serviceName - return self - - def setServiceSpecificInfo(self, info: bytes) -> "PublishConfig.Builder": - self._service_specific_info = info - return self - - def setMatchFilter(self, matchFilter: bytes) -> "PublishConfig.Builder": - self._match_filter = matchFilter - return self - - def setTerminateNotificationEnabled(self, enabled: bool) -> "PublishConfig.Builder": - self._terminate_notification_enabled = enabled - return self - - def setRangingEnabled(self, enabled: bool) -> "PublishConfig.Builder": - self._ranging_enabled = enabled - return self - - def setPublishType(self, publishType: int) -> "PublishConfig.Builder": - self._publish_type = publishType - return self - - def build(self) -> "PublishConfig": - return PublishConfig( - self._service_name, self._service_specific_info, - self._match_filter, self._terminate_notification_enabled, - self._ranging_enabled, self._publish_type, - ) - - def toString(self) -> str: - return f"PublishConfig{{service={self._service_name}, type={self._publish_type}}}" - - -# ═══════════════════════════════════════════════════════════════ -# SubscribeConfig -# ═══════════════════════════════════════════════════════════════ - -class SubscribeConfig(Stub): - """SubscribeConfig — 订阅配置""" - - SUBSCRIBE_TYPE_PASSIVE = 0 - SUBSCRIBE_TYPE_ACTIVE = 1 - - def __init__(self, service_name: str = "", service_specific_info: bytes = b"", - match_filter: bytes = b"", terminate_notification_enabled: bool = True, - ranging_enabled: bool = False, subscribe_type: int = SUBSCRIBE_TYPE_PASSIVE): - super().__init__() - self._service_name = service_name - self._service_specific_info = service_specific_info - self._match_filter = match_filter - self._terminate_notification_enabled = terminate_notification_enabled - self._ranging_enabled = ranging_enabled - self._subscribe_type = subscribe_type - - class Builder(Stub): - """SubscribeConfig.Builder — 构建器""" - - def __init__(self): - super().__init__() - self._service_name: str = "" - self._service_specific_info: bytes = b"" - self._match_filter: bytes = b"" - self._terminate_notification_enabled: bool = True - self._ranging_enabled: bool = False - self._subscribe_type: int = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE - - def setServiceName(self, serviceName: str) -> "SubscribeConfig.Builder": - self._service_name = serviceName - return self - - def setServiceSpecificInfo(self, info: bytes) -> "SubscribeConfig.Builder": - self._service_specific_info = info - return self - - def setMatchFilter(self, matchFilter: bytes) -> "SubscribeConfig.Builder": - self._match_filter = matchFilter - return self - - def setTerminateNotificationEnabled(self, enabled: bool) -> "SubscribeConfig.Builder": - self._terminate_notification_enabled = enabled - return self - - def setRangingEnabled(self, enabled: bool) -> "SubscribeConfig.Builder": - self._ranging_enabled = enabled - return self - - def setSubscribeType(self, subscribeType: int) -> "SubscribeConfig.Builder": - self._subscribe_type = subscribeType - return self - - def build(self) -> "SubscribeConfig": - return SubscribeConfig( - self._service_name, self._service_specific_info, - self._match_filter, self._terminate_notification_enabled, - self._ranging_enabled, self._subscribe_type, - ) - - def toString(self) -> str: - return f"SubscribeConfig{{service={self._service_name}, type={self._subscribe_type}}}" - - -# ═══════════════════════════════════════════════════════════════ -# DiscoverySession -# ═══════════════════════════════════════════════════════════════ - -class DiscoverySession(Stub): - """DiscoverySession — 发现会话基类""" - - def __init__(self, service_name: str = ""): - super().__init__() - self._service_name = service_name - self._terminate_notification_enabled: bool = True - self._open: bool = True - - def sendMessage(self, peerHandle: Any, message: bytes, messageId: int): - pass - - def close(self): - self._open = False - - def setTerminateNotificationEnabled(self, enabled: bool): - self._terminate_notification_enabled = enabled - - def toString(self) -> str: - return f"DiscoverySession{{service={self._service_name}}}" - - -# ═══════════════════════════════════════════════════════════════ -# PublishDiscoverySession -# ═══════════════════════════════════════════════════════════════ - -class PublishDiscoverySession(DiscoverySession): - """PublishDiscoverySession — 发布发现会话""" - - def __init__(self, service_name: str = ""): - super().__init__(service_name) - - def updatePublishConfig(self, publishConfig: PublishConfig): - pass - - def toString(self) -> str: - return f"PublishDiscoverySession{{service={self._service_name}}}" - - -# ═══════════════════════════════════════════════════════════════ -# SubscribeDiscoverySession -# ═══════════════════════════════════════════════════════════════ - -class SubscribeDiscoverySession(DiscoverySession): - """SubscribeDiscoverySession — 订阅发现会话""" - - def __init__(self, service_name: str = ""): - super().__init__(service_name) - - def updateSubscribeConfig(self, subscribeConfig: SubscribeConfig): - pass - - def toString(self) -> str: - return f"SubscribeDiscoverySession{{service={self._service_name}}}" - - -# ═══════════════════════════════════════════════════════════════ -# WifiAwareSession -# ═══════════════════════════════════════════════════════════════ - -class WifiAwareSession(Stub): - """WifiAwareSession — Wi-Fi Aware 会话""" - - def __init__(self): - super().__init__() - self._open: bool = True - self._publish_sessions: List[PublishDiscoverySession] = [] - self._subscribe_sessions: List[SubscribeDiscoverySession] = [] - - def publish(self, publishConfig: PublishConfig, - callback: DiscoverySessionCallback, - handler: Any = None) -> PublishDiscoverySession: - session = PublishDiscoverySession(publishConfig._service_name) - self._publish_sessions.append(session) - return session - - def subscribe(self, subscribeConfig: SubscribeConfig, - callback: DiscoverySessionCallback, - handler: Any = None) -> SubscribeDiscoverySession: - session = SubscribeDiscoverySession(subscribeConfig._service_name) - self._subscribe_sessions.append(session) - return session - - def createNetworkSpecifier(self, role: int, service_name: str, - pmk: Optional[bytes] = None, - passphrase: Optional[str] = None) -> Any: - return None - - def createNetworkSpecifier(self, role: int, service_name: str, - peer_handle: Any, - pmk: Optional[bytes] = None, - passphrase: Optional[str] = None) -> Any: - return None - - def sendMessage(self, peerHandle: Any, message: bytes, messageId: int): - pass - - def close(self): - self._open = False - - def toString(self) -> str: - return f"WifiAwareSession@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# WifiAwareManager -# ═══════════════════════════════════════════════════════════════ - -class WifiAwareManager(Stub): - """WifiAwareManager — Wi-Fi Aware 管理器""" - - WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0 - WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1 - ACTION_WIFI_AWARE_STATE_CHANGED = "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED" - - def __init__(self): - super().__init__() - self._available: bool = True - self._sessions: List[WifiAwareSession] = [] - - # ── Attach ── - - def attach(self, callback: AttachCallback, handler: Any = None) -> None: - session = WifiAwareSession() - self._sessions.append(session) - callback.onAttached(session) - - def attach(self, attachCallback: AttachCallback, - identityChangedListener: IdentityChangedListener, - handler: Any = None) -> None: - session = WifiAwareSession() - self._sessions.append(session) - attachCallback.onAttached(session) - - # ── Characteristics ── - - def getCharacteristics(self) -> Characteristics: - return Characteristics() - - # ── Available ── - - def isAvailable(self) -> bool: - return self._available - - def toString(self) -> str: - return f"WifiAwareManager{{available={self._available}}}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "WifiAwareManager", - "WifiAwareSession", - "PublishConfig", - "SubscribeConfig", - "DiscoverySession", - "PublishDiscoverySession", - "SubscribeDiscoverySession", - "DiscoverySessionCallback", - "Characteristics", - "AttachCallback", - "IdentityChangedListener", -] \ No newline at end of file diff --git a/android_pyapi/android/net_wifi_rtt.py b/android_pyapi/android/net_wifi_rtt.py deleted file mode 100644 index 147435a..0000000 --- a/android_pyapi/android/net_wifi_rtt.py +++ /dev/null @@ -1,319 +0,0 @@ -""" -Android net.wifi.rtt 包 — RttManager, RangingRequest, RangingResult, -ResponderLocation, WifiRttManager -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import sys -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "RttManager", - "RangingResultCallback", - "RangingRequest", - "RangingResult", - "ResponderLocation", - "WifiRttManager", -] - - -# ═══════════════════════════════════════════════════════════════ -# RangingResultCallback -# ═══════════════════════════════════════════════════════════════ - -class RangingResultCallback(Stub): - """RangingResultCallback — 测距结果回调""" - - def __init__(self): - super().__init__() - - def onRangingResults(self, results: List["RangingResult"]) -> None: - """测距结果回调""" - pass - - def onRangingFailure(self, reason: int) -> None: - """测距失败回调""" - pass - - def onRangingAborted(self) -> None: - """测距中止回调""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# RangingRequest -# ═══════════════════════════════════════════════════════════════ - -class RangingRequest(Stub): - """RangingRequest — 测距请求""" - - class Builder(Stub): - """Builder — 测距请求构建器""" - - def __init__(self): - super().__init__() - self._access_points: List[Any] = [] - self._channel_width = 0 - self._preamble = 0 - - def addAccessPoint(self, access_point: Any) -> "RangingRequest.Builder": - """添加接入点""" - self._access_points.append(access_point) - return self - - def addAccessPoints(self, access_points: List[Any]) -> "RangingRequest.Builder": - """添加多个接入点""" - self._access_points.extend(access_points) - return self - - def setChannelWidth(self, channel_width: int) -> "RangingRequest.Builder": - """设置信道宽度""" - self._channel_width = channel_width - return self - - def setPreamble(self, preamble: int) -> "RangingRequest.Builder": - """设置前导码""" - self._preamble = preamble - return self - - def build(self) -> "RangingRequest": - """构建测距请求""" - req = RangingRequest() - req._access_points = self._access_points.copy() - req._channel_width = self._channel_width - req._preamble = self._preamble - return req - - def __init__(self): - super().__init__() - self._access_points: List[Any] = [] - self._channel_width = 0 - self._preamble = 0 - - -# ═══════════════════════════════════════════════════════════════ -# RangingResult -# ═══════════════════════════════════════════════════════════════ - -class RangingResult(Stub): - """RangingResult — 测距结果""" - - STATUS_SUCCESS = 0 - STATUS_FAIL = 1 - STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC = 2 - - def __init__(self, mac_address: str = "00:00:00:00:00:00", status: int = STATUS_SUCCESS, - rssi: int = 0, distance_mm: int = 0, distance_std_dev_mm: int = 0, - timestamp: int = 0, num_attempted: int = 0, num_successful: int = 0, - is_80211mc: bool = False): - super().__init__() - self._mac_address = mac_address - self._status = status - self._rssi = rssi - self._distance_mm = distance_mm - self._distance_std_dev_mm = distance_std_dev_mm - self._timestamp = timestamp - self._num_attempted = num_attempted - self._num_successful = num_successful - self._is_80211mc = is_80211mc - self._responder_location: Optional["ResponderLocation"] = None - self._unconfirmed_location: Optional["ResponderLocation"] = None - self._location_timestamp = 0 - - def getMacAddress(self) -> str: - """获取 MAC 地址""" - return self._mac_address - - def getStatus(self) -> int: - """获取状态""" - return self._status - - def getRssi(self) -> int: - """获取 RSSI""" - return self._rssi - - def getDistanceMm(self) -> int: - """获取距离(毫米)""" - return self._distance_mm - - def getDistanceStdDevMm(self) -> int: - """获取距离标准差(毫米)""" - return self._distance_std_dev_mm - - def getTimestamp(self) -> int: - """获取时间戳""" - return self._timestamp - - def getNumAttemptedMeasurements(self) -> int: - """获取尝试测量次数""" - return self._num_attempted - - def getNumSuccessfulMeasurements(self) -> int: - """获取成功测量次数""" - return self._num_successful - - def is80211mcMeasurement(self) -> bool: - """检查是否为 802.11mc 测量""" - return self._is_80211mc - - def getResponderLocation(self) -> Optional["ResponderLocation"]: - """获取响应者位置""" - return self._responder_location - - def getUnconfirmedLocation(self) -> Optional["ResponderLocation"]: - """获取未确认的位置""" - return self._unconfirmed_location - - def getLocationTimestamp(self) -> int: - """获取位置时间戳""" - return self._location_timestamp - - -# ═══════════════════════════════════════════════════════════════ -# ResponderLocation -# ═══════════════════════════════════════════════════════════════ - -class ResponderLocation(Stub): - """ResponderLocation — 响应者位置""" - - def __init__(self): - super().__init__() - self._lci = False - self._lcr = False - self._latitude = 0.0 - self._longitude = 0.0 - self._altitude = 0.0 - self._floor_number = 0 - self._height_above_floor = 0.0 - self._height_floor = 0.0 - self._expected_to_move = False - self._location_accuracy_micros = 0 - self._altitude_accuracy = 0.0 - - def isLci(self) -> bool: - """检查是否为 LCI""" - return self._lci - - def isLcr(self) -> bool: - """检查是否为 LCR""" - return self._lcr - - def getLatitude(self) -> float: - """获取纬度""" - return self._latitude - - def getLongitude(self) -> float: - """获取经度""" - return self._longitude - - def getAltitude(self) -> float: - """获取海拔""" - return self._altitude - - def getFloorNumber(self) -> int: - """获取楼层号""" - return self._floor_number - - def getHeightAboveFloorMeters(self) -> float: - """获取距离本层地面高度(米)""" - return self._height_above_floor - - def getHeightFloorMeters(self) -> float: - """获取本层高度(米)""" - return self._height_floor - - def getExpectedToMove(self) -> bool: - """检查是否预期移动""" - return self._expected_to_move - - def getLocationAccuracyMicros(self) -> int: - """获取位置精度(微秒)""" - return self._location_accuracy_micros - - def getAltitudeAccuracy(self) -> float: - """获取海拔精度""" - return self._altitude_accuracy - - def toString(self) -> str: - return f"ResponderLocation(lat={self._latitude}, lon={self._longitude})" - - -# ═══════════════════════════════════════════════════════════════ -# RttManager (original/deprecated) -# ═══════════════════════════════════════════════════════════════ - -class RttManager(Stub): - """RttManager — RTT 管理器(已废弃)(桌面存根)""" - - def __init__(self): - super().__init__() - self._callbacks: Dict[int, RangingResultCallback] = {} - - def startRanging(self, request: RangingRequest, callback: RangingResultCallback) -> None: - """开始测距""" - pass - - def stopRanging(self, request: RangingRequest) -> None: - """停止测距""" - pass - - def registerCallback(self, callback: RangingResultCallback) -> None: - """注册回调""" - callback_id = id(callback) - self._callbacks[callback_id] = callback - - def unregisterCallback(self, callback: RangingResultCallback) -> None: - """注销回调""" - callback_id = id(callback) - self._callbacks.pop(callback_id, None) - - -# ═══════════════════════════════════════════════════════════════ -# WifiRttManager (newer replacement API) -# ═══════════════════════════════════════════════════════════════ - -class WifiRttManager(Stub): - """WifiRttManager — Wi-Fi RTT 管理器 (桌面存根)""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._available = False - self._results: List[RangingResult] = [] - - def startRanging(self, request: RangingRequest, executor: Any, - callback: RangingResultCallback) -> None: - """开始测距""" - pass - - def cancelRanging(self, request: RangingRequest) -> None: - """取消测距""" - pass - - def registerRangingResultCallback(self, executor: Any, - callback: RangingResultCallback) -> None: - """注册测距结果回调""" - pass - - def unregisterRangingResultCallback(self, callback: RangingResultCallback) -> None: - """注销测距结果回调""" - pass - - def isAvailable(self) -> bool: - """检查 RTT 是否可用""" - return self._available - - def setRangingResultCallback(self, callback: RangingResultCallback) -> None: - """设置测距结果回调""" - pass - - def getRangingResults(self) -> List[RangingResult]: - """获取测距结果""" - return self._results.copy() \ No newline at end of file diff --git a/android_pyapi/android/nfc.py b/android_pyapi/android/nfc.py deleted file mode 100644 index a747fe9..0000000 --- a/android_pyapi/android/nfc.py +++ /dev/null @@ -1,390 +0,0 @@ -""" -Android NFC 组件 — NfcAdapter, NfcManager, Tag, NdefMessage 等 -方法签名完全对齐 Android SDK -""" - -import os -import sys -from typing import Any, Dict, List, Optional, Set, Tuple, Union - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) - - -# ═══════════════════════════════════════════════════════════════ -# NdefRecord -# ═══════════════════════════════════════════════════════════════ - -class NdefRecord(Stub): - """NdefRecord — NDEF 记录""" - - TNF_ABSOLUTE_URI = 3 - TNF_EMPTY = 0 - TNF_EXTERNAL_TYPE = 4 - TNF_MIME_MEDIA = 2 - TNF_UNCHANGED = 6 - TNF_UNKNOWN = 5 - TNF_WELL_KNOWN = 1 - - RTD_URI = "U" - RTD_TEXT = "T" - RTD_SMART_POSTER = "Sp" - RTD_ALTERNATIVE_CARRIER = "ac" - RTD_HANDOVER_CARRIER = "Hc" - RTD_HANDOVER_REQUEST = "Hr" - RTD_HANDOVER_SELECT = "Hs" - - def __init__(self, tnf: int = TNF_EMPTY, type_: bytes = b"", - id_: bytes = b"", payload: bytes = b""): - super().__init__() - self._tnf = tnf - self._type = type_ - self._id = id_ - self._payload = payload - - @staticmethod - def createUri(uri_spec: str) -> "NdefRecord": - return NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, - type_=uri_spec.encode("utf-8"), - payload=uri_spec.encode("utf-8")) - - @staticmethod - def createMime(mime_type: str, data: bytes) -> "NdefRecord": - return NdefRecord(NdefRecord.TNF_MIME_MEDIA, - type_=mime_type.encode("utf-8"), - payload=data) - - @staticmethod - def createExternal(domain: str, type_: str, data: bytes) -> "NdefRecord": - full_type = f"{domain}:{type_}" - return NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE, - type_=full_type.encode("utf-8"), - payload=data) - - @staticmethod - def createApplicationRecord(package_name: str) -> "NdefRecord": - return NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE, - type_=package_name.encode("utf-8"), - payload=package_name.encode("utf-8")) - - def getTnf(self) -> int: - return self._tnf - - def getType(self) -> bytes: - return self._type - - def getId(self) -> bytes: - return self._id - - def getPayload(self) -> bytes: - return self._payload - - def toByteArray(self) -> bytes: - import struct - tnf_byte = struct.pack("B", self._tnf & 0x07) - type_len = len(self._type) - id_len = len(self._id) - payload_len = len(self._payload) - header = struct.pack("BB", type_len, id_len) - return tnf_byte + header + struct.pack(">I", payload_len) + \ - self._type + self._id + self._payload - - def toString(self) -> str: - return f"NdefRecord(tnf={self._tnf}, type={self._type})" - - -# ═══════════════════════════════════════════════════════════════ -# NdefMessage -# ═══════════════════════════════════════════════════════════════ - -class NdefMessage(Stub): - """NdefMessage — NDEF 消息""" - - def __init__(self, records: Union[List[NdefRecord], bytes, Any]): - super().__init__() - if isinstance(records, list): - self._records = list(records) - elif isinstance(records, bytes): - self._records = [] - else: - self._records = [] - - def getRecords(self) -> List[NdefRecord]: - return list(self._records) - - def getByteArrayLength(self) -> int: - return sum(len(r.toByteArray()) for r in self._records) - - def toByteArray(self) -> bytes: - data = b"" - for r in self._records: - data += r.toByteArray() - return data - - -# ═══════════════════════════════════════════════════════════════ -# Tag -# ═══════════════════════════════════════════════════════════════ - -class Tag(Stub): - """Tag — NFC 标签""" - - def __init__(self, id_: bytes = b"", tech_list: Optional[List[str]] = None): - super().__init__() - self._id = id_ - self._tech_list = tech_list or [] - - def getId(self) -> bytes: - return self._id - - def getTechList(self) -> List[str]: - return list(self._tech_list) - - def getTechListParcelable(self) -> Any: - return TechListParcelable(self._tech_list) - - def toString(self) -> str: - return f"Tag(id={self._id.hex()}, techs={self._tech_list})" - - -class TechListParcelable(Stub): - """TechListParcelable — 技术列表包裹""" - - def __init__(self, tech_list: List[str]): - super().__init__() - self._tech_list = list(tech_list) - - def getTechList(self) -> List[str]: - return list(self._tech_list) - - def toString(self) -> str: - return f"TechListParcelable(techs={self._tech_list})" - - -# ═══════════════════════════════════════════════════════════════ -# NfcAdapter callback classes -# ═══════════════════════════════════════════════════════════════ - -class NfcAdapterCreateBeamUrisCallback(Stub): - def createBeamUris(self, event: "NfcEvent") -> List[Any]: - return [] - - -class NfcAdapterOnNdefPushCompleteCallback(Stub): - def onNdefPushComplete(self, event: "NfcEvent"): - pass - - -class NdefMessageCallback(Stub): - def createNdefMessage(self, event: "NfcEvent") -> Optional[NdefMessage]: - return None - - -# ═══════════════════════════════════════════════════════════════ -# NfcEvent -# ═══════════════════════════════════════════════════════════════ - -class NfcEvent(Stub): - """NfcEvent — NFC 事件""" - - def __init__(self, nfc_adapter: Any = None): - super().__init__() - self.nfcAdapter = nfc_adapter - - def getNfcAdapter(self) -> Any: - return self.nfcAdapter - - -# ═══════════════════════════════════════════════════════════════ -# NfcAntennaInfo / ApduServiceInfo / CardEmulation / NfcFCardEmulation -# ═══════════════════════════════════════════════════════════════ - -class NfcAntennaInfo(Stub): - """NfcAntennaInfo — NFC 天线信息""" - - def __init__(self, x: float = 0.0, y: float = 0.0): - super().__init__() - self._x = x - self._y = y - - def getX(self) -> float: - return self._x - - def getY(self) -> float: - return self._y - - -class ApduServiceInfo(Stub): - """ApduServiceInfo — APDU 服务信息""" - - def __init__(self, description: str = ""): - super().__init__() - self._description = description - - def getDescription(self) -> str: - return self._description - - -class CardEmulation(Stub): - """CardEmulation — 卡模拟""" - - CATEGORY_PAYMENT = "payment" - CATEGORY_OTHER = "other" - - SELECTION_TYPE_ASK_IF_CONFLICT = 1 - SELECTION_TYPE_PREFER_DEFAULT = 2 - SELECTION_TYPE_USER_PROMPTED = 0 - - -class NfcFCardEmulation(Stub): - """NfcFCardEmulation — NFC-F 卡模拟""" - - EXTRA_NFC_F_CARD_EMULATION_PARAMS = "android.nfc.extra.EXTRA_NFC_F_CARD_EMULATION_PARAMS" - - @staticmethod - def getInstance(nfc_adapter: Any) -> "NfcFCardEmulation": - return NfcFCardEmulation() - - def enableService(self, activity: Any, service: Any) -> bool: - return True - - def disableService(self, activity: Any, service: Any) -> bool: - return True - - def setSystemCodeForService(self, activity: Any, service: Any, system_code: str) -> bool: - return True - - -# ═══════════════════════════════════════════════════════════════ -# NfcManager -# ═══════════════════════════════════════════════════════════════ - -class NfcManager(Stub): - """NfcManager — NFC 管理器""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._default_adapter: Optional["NfcAdapter"] = None - - def getDefaultAdapter(self) -> Optional["NfcAdapter"]: - if self._default_adapter is None: - self._default_adapter = NfcAdapter() - return self._default_adapter - - -# ═══════════════════════════════════════════════════════════════ -# NfcAdapter -# ═══════════════════════════════════════════════════════════════ - -class NfcAdapter(Stub): - """NfcAdapter — NFC 适配器""" - - ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED" - ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED" - ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED" - - FLAG_READER_NFC_A = 1 - FLAG_READER_NFC_B = 2 - FLAG_READER_NFC_F = 4 - FLAG_READER_NFC_V = 8 - FLAG_READER_NFC_BARCODE = 16 - FLAG_READER_SKIP_NDEF_CHECK = 128 - FLAG_READER_NO_PLATFORM_SOUNDS = 256 - - FLAG_NDEF_PUSH_NO_CONFIRM = 1 - - _default_adapter: Optional["NfcAdapter"] = None - - def __init__(self): - super().__init__() - self._enabled = True - self._ndef_push_message: Optional[NdefMessage] = None - self._ndef_push_callback: Optional[NdefMessageCallback] = None - self._push_complete_callback: Optional[NfcAdapterOnNdefPushCompleteCallback] = None - self._beam_uri_callback: Optional[NfcAdapterCreateBeamUrisCallback] = None - - @staticmethod - def getDefaultAdapter(context: Any = None) -> Optional["NfcAdapter"]: - if NfcAdapter._default_adapter is None: - NfcAdapter._default_adapter = NfcAdapter() - return NfcAdapter._default_adapter - - def isEnabled(self) -> bool: - return self._enabled - - def enable(self) -> bool: - self._enabled = True - return True - - def disable(self) -> bool: - self._enabled = False - return True - - def setNdefPushMessage(self, message: NdefMessage, activity: Any, - vararg_activities: Any = None): - self._ndef_push_message = message - - def setNdefPushMessageCallback(self, callback: NdefMessageCallback, activity: Any, - vararg_activities: Any = None): - self._ndef_push_callback = callback - - def setOnNdefPushCompleteCallback(self, callback: NfcAdapterOnNdefPushCompleteCallback, - activity: Any, vararg_activities: Any = None): - self._push_complete_callback = callback - - def setBeamUrisCallback(self, callback: NfcAdapterCreateBeamUrisCallback, activity: Any): - self._beam_uri_callback = callback - - def enableForegroundDispatch(self, activity: Any, intent: Any, - filters: Any, tech_lists: Any): - pass - - def disableForegroundDispatch(self, activity: Any): - pass - - def enableReaderMode(self, activity: Any, callback: Any, flags: int, extras: Any = None): - pass - - def disableReaderMode(self, activity: Any): - pass - - def invokeBeam(self, activity: Any) -> bool: - return True - - def isSecureNfcSupported(self) -> bool: - return False - - def isSecureNfcEnabled(self) -> bool: - return False - - def enableSecureNfc(self, enable: bool) -> bool: - return True - - def toString(self) -> str: - return f"NfcAdapter(enabled={self._enabled})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "NfcAdapter", - "NfcManager", - "Tag", - "NdefMessage", - "NdefRecord", - "NfcEvent", - "NfcAdapterCreateBeamUrisCallback", - "NfcAdapterOnNdefPushCompleteCallback", - "NdefMessageCallback", - "TechListParcelable", - "NfcAntennaInfo", - "ApduServiceInfo", - "CardEmulation", - "NfcFCardEmulation", -] \ No newline at end of file diff --git a/android_pyapi/android/nfc_ext.py b/android_pyapi/android/nfc_ext.py deleted file mode 100644 index 721105c..0000000 --- a/android_pyapi/android/nfc_ext.py +++ /dev/null @@ -1,816 +0,0 @@ -""" -Android nfc 扩展 — NfcAdapter, NdefMessage, NdefRecord, Tag, Tech, -NfcA, NfcB, NfcF, NfcV, IsoDep, MifareClassic, MifareUltralight, -NdefFormatable, CardEmulation, HostApduService, OffHostApduService -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import sys -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "NfcAdapter", - "NdefMessage", - "NdefRecord", - "Tag", - "Tech", - "NfcA", - "NfcB", - "NfcF", - "NfcV", - "NfcBarcode", - "IsoDep", - "MifareClassic", - "MifareUltralight", - "NdefFormatable", - "CardEmulation", - "HostApduService", - "OffHostApduService", -] - - -# ═══════════════════════════════════════════════════════════════ -# NdefRecord -# ═══════════════════════════════════════════════════════════════ - -class NdefRecord(Stub): - """NdefRecord — NDEF 记录""" - - TNF_EMPTY = 0x00 - TNF_ABSOLUTE_URI = 0x03 - TNF_MEDIA = 0x02 - TNF_WELL_KNOWN = 0x01 - TNF_UNCHANGED = 0x06 - TNF_RESERVED = 0x07 - TNF_EXTERNAL_TYPE = 0x04 - - RTD_TEXT = b"T" - RTD_URI = b"U" - RTD_SMART_POSTER = b"Sp" - RTD_ALTERNATIVE_CARRIER = b"ac" - RTD_HANDOVER_CARRIER = b"Hc" - RTD_HANDOVER_REQUEST = b"Hr" - RTD_HANDOVER_SELECT = b"Hr" - - def __init__(self, tnf: int = TNF_EMPTY, type_: bytes = b"", - id_: bytes = b"", payload: bytes = b""): - super().__init__() - self._tnf = tnf - self._type = type_ - self._id = id_ - self._payload = payload - - @property - def tnf(self) -> int: - return self._tnf - - @property - def type(self) -> bytes: - return self._type - - @property - def id(self) -> bytes: - return self._id - - @property - def payload(self) -> bytes: - return self._payload - - def getTnf(self) -> int: - """获取 TNF""" - return self._tnf - - def getType(self) -> bytes: - """获取类型""" - return self._type - - def getId(self) -> bytes: - """获取 ID""" - return self._id - - def getPayload(self) -> bytes: - """获取载荷""" - return self._payload - - def toByteArray(self) -> bytes: - """转换为字节数组""" - return self._tnf.to_bytes(1, 'big') + self._type + self._id + self._payload - - def toString(self) -> str: - return f"NdefRecord(tnf={self._tnf}, type={self._type}, id={self._id}, payload={self._payload})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, NdefRecord): - return False - return (self._tnf == other._tnf and self._type == other._type - and self._id == other._id and self._payload == other._payload) - - def hashCode(self) -> int: - return hash((self._tnf, self._type, self._id, self._payload)) & 0x7FFFFFFF - - @staticmethod - def createUri(uri: str) -> "NdefRecord": - """创建 URI 记录""" - return NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, b"U", b"", uri.encode()) - - @staticmethod - def createMime(mime_type: str, data: bytes) -> "NdefRecord": - """创建 MIME 记录""" - return NdefRecord(NdefRecord.TNF_MEDIA, mime_type.encode(), b"", data) - - @staticmethod - def createApplication(package_name: str) -> "NdefRecord": - """创建应用记录""" - return NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE, - b"android.com:pkg", b"", package_name.encode()) - - @staticmethod - def createExternal(domain: str, type_: str, data: bytes) -> "NdefRecord": - """创建外部类型记录""" - return NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE, - f"{domain}:{type_}".encode(), b"", data) - - -# ═══════════════════════════════════════════════════════════════ -# NdefMessage -# ═══════════════════════════════════════════════════════════════ - -class NdefMessage(Stub): - """NdefMessage — NDEF 消息""" - - def __init__(self, records: Optional[List[NdefRecord]] = None): - super().__init__() - self._records = records or [] - - @staticmethod - def fromBytes(data: bytes) -> "NdefMessage": - """从字节数组创建消息""" - return NdefMessage() - - def getRecords(self) -> List[NdefRecord]: - """获取记录列表""" - return self._records.copy() - - def getByteArray(self) -> bytes: - """获取字节数组""" - return b"".join(r.toByteArray() for r in self._records) - - def toByteArray(self) -> bytes: - """转换为字节数组""" - return self.getByteArray() - - def toString(self) -> str: - return f"NdefMessage(records={len(self._records)})" - - -# ═══════════════════════════════════════════════════════════════ -# Tag -# ═══════════════════════════════════════════════════════════════ - -class Tag(Stub): - """Tag — NFC 标签 (桌面存根)""" - - def __init__(self, id_: bytes = b"", tech_list: Optional[List[str]] = None): - super().__init__() - self._id = id_ - self._tech_list = tech_list or [] - self._service_name = "" - - def getId(self) -> bytes: - """获取标签 ID""" - return self._id - - def getTechList(self) -> List[str]: - """获取技术列表""" - return self._tech_list.copy() - - def toString(self) -> str: - return f"Tag(id={self._id.hex()}, techs={self._tech_list})" - - def hashCode(self) -> int: - return hash(bytes(self._id)) & 0x7FFFFFFF - - def equals(self, other: Any) -> bool: - if not isinstance(other, Tag): - return False - return self._id == other._id - - def getServiceName(self) -> str: - """获取服务名称""" - return self._service_name - - def getTagService(self) -> Optional[Any]: - """获取标签服务""" - return None - - -# ═══════════════════════════════════════════════════════════════ -# Tech -# ═══════════════════════════════════════════════════════════════ - -class Tech(Stub): - """Tech — NFC 技术基类 (桌面存根)""" - - PRESENCE_CHECK_DELAY = 125 - - def __init__(self, tag: Optional[Tag] = None): - super().__init__() - self._tag = tag - self._connected = False - self._timeout = 500 - - @staticmethod - def get(tag: Tag, tech_class: Any) -> Optional["Tech"]: - """获取技术实例""" - return None - - def getTag(self) -> Optional[Tag]: - """获取标签""" - return self._tag - - def connect(self) -> None: - """连接""" - self._connected = True - - def reconnect(self) -> None: - """重新连接""" - pass - - def close(self) -> None: - """关闭""" - self._connected = False - - def getConnected(self) -> bool: - """检查是否已连接""" - return self._connected - - def isConnected(self) -> bool: - """检查是否已连接""" - return self._connected - - def getMaxTransceiveLength(self) -> int: - """获取最大收发长度""" - return 253 - - def setTimeout(self, timeout: int) -> None: - """设置超时""" - self._timeout = timeout - - def getTimeout(self) -> int: - """获取超时""" - return self._timeout - - def transceive(self, data: bytes) -> bytes: - """收发数据""" - return b"" - - -# ═══════════════════════════════════════════════════════════════ -# NfcA -# ═══════════════════════════════════════════════════════════════ - -class NfcA(Tech): - """NfcA — NFC-A 技术""" - - def __init__(self, tag: Optional[Tag] = None): - super().__init__(tag) - self._atqa = b"\x00\x00" - self._sak = 0 - - def getAtqa(self) -> bytes: - """获取 ATQA""" - return self._atqa - - def getSak(self) -> int: - """获取 SAK""" - return self._sak - - def getMaxTransceiveLength(self) -> int: - return 253 - - -# ═══════════════════════════════════════════════════════════════ -# NfcB -# ═══════════════════════════════════════════════════════════════ - -class NfcB(Tech): - """NfcB — NFC-B 技术""" - - def __init__(self, tag: Optional[Tag] = None): - super().__init__(tag) - self._application_data = b"" - self._protocol_info = b"" - - def getApplicationData(self) -> bytes: - """获取应用数据""" - return self._application_data - - def getProtocolInfo(self) -> bytes: - """获取协议信息""" - return self._protocol_info - - def getMaxTransceiveLength(self) -> int: - return 253 - - -# ═══════════════════════════════════════════════════════════════ -# NfcF -# ═══════════════════════════════════════════════════════════════ - -class NfcF(Tech): - """NfcF — NFC-F 技术""" - - def __init__(self, tag: Optional[Tag] = None): - super().__init__(tag) - self._manufacturer = 0 - self._sc = 0 - self._system_code = 0 - self._pmm = b"" - - def getManufacturer(self) -> int: - """获取制造商""" - return self._manufacturer - - def getSC(self) -> int: - """获取 SC""" - return self._sc - - def getSystemCode(self) -> int: - """获取系统代码""" - return self._system_code - - def getPMm(self) -> bytes: - """获取 PMm""" - return self._pmm - - def getMaxTransceiveLength(self) -> int: - return 253 - - -# ═══════════════════════════════════════════════════════════════ -# NfcV -# ═══════════════════════════════════════════════════════════════ - -class NfcV(Tech): - """NfcV — NFC-V 技术""" - - def __init__(self, tag: Optional[Tag] = None): - super().__init__(tag) - self._dsf_id = 0 - self._response_flags = 0 - self._identification = b"" - - def getDsfId(self) -> int: - """获取 DSF ID""" - return self._dsf_id - - def getResponseFlags(self) -> int: - """获取响应标志""" - return self._response_flags - - def getIdentification(self) -> bytes: - """获取标识""" - return self._identification - - -# ═══════════════════════════════════════════════════════════════ -# NfcBarcode -# ═══════════════════════════════════════════════════════════════ - -class NfcBarcode(Tech): - """NfcBarcode — NFC 条码技术""" - - def __init__(self, tag: Optional[Tag] = None): - super().__init__(tag) - - -# ═══════════════════════════════════════════════════════════════ -# IsoDep -# ═══════════════════════════════════════════════════════════════ - -class IsoDep(Tech): - """IsoDep — ISO-DEP 技术""" - - def __init__(self, tag: Optional[Tag] = None): - super().__init__(tag) - self._hi_layer_response = b"" - self._historical_bytes = b"" - self._extended_length_supported = False - - def getHiLayerResponse(self) -> bytes: - """获取高层响应""" - return self._hi_layer_response - - def getHistoricalBytes(self) -> bytes: - """获取历史字节""" - return self._historical_bytes - - def setTimeout(self, timeout: int) -> None: - self._timeout = timeout - - def getTimeout(self) -> int: - return self._timeout - - def getMaxTransceiveLength(self) -> int: - return 261 if self._extended_length_supported else 128 - - def isExtendedLengthApduSupported(self) -> bool: - """检查是否支持扩展长度 APDU""" - return self._extended_length_supported - - def transceive(self, data: bytes) -> bytes: - return b"\x90\x00" # SW1=0x90, SW2=0x00 = success - - -# ═══════════════════════════════════════════════════════════════ -# MifareClassic -# ═══════════════════════════════════════════════════════════════ - -class MifareClassic(Tech): - """MifareClassic — MIFARE Classic 技术""" - - TYPE_CLASSIC = 0 - TYPE_PLUS = 1 - TYPE_PRO = 2 - TYPE_UNKNOWN = -1 - SIZE_1K = 1024 - SIZE_2K = 2048 - SIZE_4K = 4096 - SIZE_MINI = 320 - KEY_DEFAULT = b"\xFF\xFF\xFF\xFF\xFF\xFF" - KEY_MIFARE_APPLICATION_DIRECTORY = b"\xA0\xA1\xA2\xA3\xA4\xA5" - KEY_NFC_FORUM = b"\xD3\xF7\xD3\xF7\xD3\xF7" - - def __init__(self, tag: Optional[Tag] = None): - super().__init__(tag) - self._type = MifareClassic.TYPE_CLASSIC - self._size = MifareClassic.SIZE_1K - - def authenticateSectorWithKeyA(self, sector_index: int, key: bytes) -> bool: - """使用密钥 A 认证扇区""" - return False - - def authenticateSectorWithKeyB(self, sector_index: int, key: bytes) -> bool: - """使用密钥 B 认证扇区""" - return False - - def readBlock(self, block_index: int) -> bytes: - """读取块""" - return b"\x00" * 16 - - def writeBlock(self, block_index: int, data: bytes) -> None: - """写入块""" - pass - - def readSector(self, sector_index: int) -> List[bytes]: - """读取扇区""" - return [b"\x00" * 16] * 4 - - def writeSector(self, sector_index: int, data: List[bytes]) -> None: - """写入扇区""" - pass - - @staticmethod - def sectorToBlock(sector_index: int) -> int: - """扇区索引转块索引""" - if sector_index < 32: - return sector_index * 4 - return 32 * 4 + (sector_index - 32) * 16 - - @staticmethod - def blockToSector(block_index: int) -> int: - """块索引转扇区索引""" - if block_index < 128: - return block_index // 4 - return 32 + (block_index - 128) // 16 - - @staticmethod - def getBlockCountInSector(sector_index: int) -> int: - """获取扇区中的块数量""" - return 4 if sector_index < 32 else 16 - - def isEmulated(self) -> bool: - """检查是否模拟""" - return False - - def getMaxTransceiveLength(self) -> int: - return 16 - - -# ═══════════════════════════════════════════════════════════════ -# MifareUltralight -# ═══════════════════════════════════════════════════════════════ - -class MifareUltralight(Tech): - """MifareUltralight — MIFARE Ultralight 技术""" - - TYPE_ULTRALIGHT = 1 - TYPE_ULTRALIGHT_C = 2 - TYPE_ULTRALIGHT_EV1 = 3 - TYPE_NTAG_2xx = 4 - TYPE_UNKNOWN = -1 - - def __init__(self, tag: Optional[Tag] = None): - super().__init__(tag) - self._type = MifareUltralight.TYPE_ULTRALIGHT - - def readPages(self, page_offset: int) -> bytes: - """读取页""" - return b"\x00" * 16 - - def writePage(self, page_offset: int, data: bytes) -> None: - """写入页""" - pass - - def setTimeout(self, timeout: int) -> None: - self._timeout = timeout - - def getTimeout(self) -> int: - return self._timeout - - def getMaxTransceiveLength(self) -> int: - return 16 - - -# ═══════════════════════════════════════════════════════════════ -# NdefFormatable -# ═══════════════════════════════════════════════════════════════ - -class NdefFormatable(Tech): - """NdefFormatable — NDEF 可格式化技术""" - - def __init__(self, tag: Optional[Tag] = None): - super().__init__(tag) - - def format(self, first_message: NdefMessage) -> None: - """格式化""" - pass - - def formatReadOnly(self, first_message: NdefMessage) -> None: - """格式化为只读""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# NfcAdapter -# ═══════════════════════════════════════════════════════════════ - -class NfcAdapter(Stub): - """NfcAdapter — NFC 适配器 (桌面存根)""" - - ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED" - ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED" - ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED" - ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED" - EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE" - STATE_OFF = 1 - STATE_TURNING_ON = 2 - STATE_ON = 3 - STATE_TURNING_OFF = 4 - EXTRA_TAG = "android.nfc.extra.TAG" - EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES" - EXTRA_ID = "android.nfc.extra.ID" - FLAG_READER_NFC_A = 0x01 - FLAG_READER_NFC_B = 0x02 - FLAG_READER_NFC_F = 0x04 - FLAG_READER_NFC_V = 0x08 - FLAG_READER_NFC_BARCODE = 0x10 - FLAG_READER_SKIP_NDEF_CHECK = 0x20 - FLAG_READER_NO_PLATFORM_SOUNDS = 0x40 - FLAG_READER_LISTEN_MODE = 0x80 - - _instance: Optional["NfcAdapter"] = None - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._enabled = True - self._ndef_push_enabled = False - self._secure_nfc_enabled = False - self._reader_mode_flags = 0 - self._tech_list: List[str] = [] - self._foreground_dispatch_active = False - self._foreground_reader_active = False - - @staticmethod - def getDefaultAdapter(context: Any) -> Optional["NfcAdapter"]: - """获取默认适配器""" - if NfcAdapter._instance is None: - NfcAdapter._instance = NfcAdapter(context) - return NfcAdapter._instance - - def isEnabled(self) -> bool: - """检查 NFC 是否启用""" - return self._enabled - - def enableForegroundDispatch(self, activity: Any, intent: Any, - filters: List[Any], tech_lists: List[List[str]]) -> None: - """启用前台分发""" - self._foreground_dispatch_active = True - - def disableForegroundDispatch(self, activity: Any) -> None: - """禁用前台分发""" - self._foreground_dispatch_active = False - - def enableForegroundReader(self, activity: Any, flags: int, - tech_lists: List[List[str]]) -> None: - """启用前台读取器""" - self._foreground_reader_active = True - self._reader_mode_flags = flags - - def disableForegroundReader(self, activity: Any) -> None: - """禁用前台读取器""" - self._foreground_reader_active = False - - def enableReaderMode(self, activity: Any, callback: Any, flags: int, - extras: Any = None) -> None: - """启用读取器模式""" - self._reader_mode_flags = flags - - def disableReaderMode(self, activity: Any) -> None: - """禁用读取器模式""" - self._reader_mode_flags = 0 - - def setNdefPushMessage(self, message: NdefMessage, activity: Any, - *activities: Any) -> None: - """设置 NDEF 推送消息""" - pass - - def setNdefPushMessageCallback(self, callback: Any, activity: Any, - *activities: Any) -> None: - """设置 NDEF 推送消息回调""" - pass - - def setOnNdefPushCompleteCallback(self, callback: Any, activity: Any, - *activities: Any) -> None: - """设置 NDEF 推送完成回调""" - pass - - def enableNdefPush(self, activity: Any) -> None: - """启用 NDEF 推送""" - self._ndef_push_enabled = True - - def disableNdefPush(self, activity: Any) -> None: - """禁用 NDEF 推送""" - self._ndef_push_enabled = False - - def isNdefPushEnabled(self) -> bool: - """检查 NDEF 推送是否启用""" - return self._ndef_push_enabled - - def createNdefMessage(self, message: NdefMessage) -> None: - """创建 NDEF 消息""" - pass - - def getNfcAdapterExtrasInterface(self) -> Optional[Any]: - """获取 NFC 适配器附加接口""" - return None - - def isSecureNfcSupported(self) -> bool: - """检查是否支持安全 NFC""" - return True - - def isSecureNfcEnabled(self) -> bool: - """检查安全 NFC 是否启用""" - return self._secure_nfc_enabled - - def enableSecureNfc(self, enabled: bool) -> bool: - """启用安全 NFC""" - self._secure_nfc_enabled = enabled - return True - - def disableSecureNfc(self) -> bool: - """禁用安全 NFC""" - self._secure_nfc_enabled = False - return True - - def getMaxTransceiveLength(self, tech: str) -> int: - """获取最大收发长度""" - return 253 - - def getTechList(self) -> List[str]: - """获取技术列表""" - return self._tech_list.copy() - - def setTechList(self, tech_list: List[str]) -> None: - """设置技术列表""" - self._tech_list = tech_list - - -# ═══════════════════════════════════════════════════════════════ -# CardEmulation -# ═══════════════════════════════════════════════════════════════ - -class CardEmulation(Stub): - """CardEmulation — 卡模拟 (桌面存根)""" - - CATEGORY_PAYMENT = "payment" - CATEGORY_OTHER = "other" - SELECTION_MODE_PREFER_DEFAULT = 0 - SELECTION_MODE_ALWAYS_ASK = 1 - - def __init__(self): - super().__init__() - self._aid_groups: Dict[str, List[Any]] = {} - self._selection_modes: Dict[str, int] = {} - - def isDefaultServiceForCategory(self, service_component: Any, category: str) -> bool: - """检查是否是类别的默认服务""" - return False - - def setPreferredService(self, service_component: Any) -> None: - """设置首选服务""" - pass - - def unsetPreferredService(self) -> None: - """取消设置首选服务""" - pass - - def getSelectionModes(self, category: str) -> int: - """获取选择模式""" - return self._selection_modes.get(category, CardEmulation.SELECTION_MODE_PREFER_DEFAULT) - - def setSelectionModeForCategory(self, category: str, mode: int) -> None: - """设置类别选择模式""" - self._selection_modes[category] = mode - - def getCategory(self, service_component: Any) -> str: - """获取类别""" - return CardEmulation.CATEGORY_OTHER - - def registerAidGroup(self, service_component: Any, aid_group: Any) -> bool: - """注册 AID 组""" - return False - - def unregisterAidGroup(self, service_component: Any, aid_group: Any) -> bool: - """注销 AID 组""" - return False - - def setAidGroup(self, service_component: Any, aid_group: Any) -> None: - """设置 AID 组""" - pass - - def getAidGroup(self, service_component: Any, category: str) -> Optional[Any]: - """获取 AID 组""" - return None - - def addAidGroup(self, service_component: Any, aid_group: Any) -> None: - """添加 AID 组""" - pass - - def removeAidGroup(self, service_component: Any, aid_group: Any) -> None: - """移除 AID 组""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# HostApduService -# ═══════════════════════════════════════════════════════════════ - -class HostApduService(Stub): - """HostApduService — 主机 APDU 服务 (桌面存根)""" - - SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_APDU_SERVICE" - SERVICE_META_DATA = "android.nfc.cardemulation.host_apdu_service" - DEACTIVATION_LINK_LOSS = 0 - DEACTIVATION_DESELECTED = 1 - - def __init__(self): - super().__init__() - - def processCommandApdu(self, command_apdu: bytes, extras: Any = None) -> bytes: - """处理命令 APDU""" - return b"\x90\x00" - - def onDeactivated(self, reason: int) -> None: - """去激活回调""" - pass - - def sendResponseApdu(self, response_apdu: bytes) -> None: - """发送响应 APDU""" - pass - - def notifyUnhandled(self) -> None: - """通知未处理""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# OffHostApduService -# ═══════════════════════════════════════════════════════════════ - -class OffHostApduService(Stub): - """OffHostApduService — 脱机主机 APDU 服务 (桌面存根)""" - - SERVICE_INTERFACE = "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE" - SERVICE_META_DATA = "android.nfc.cardemulation.off_host_apdu_service" - - def __init__(self): - super().__init__() \ No newline at end of file diff --git a/android_pyapi/android/opengl.py b/android_pyapi/android/opengl.py deleted file mode 100644 index ef85a1c..0000000 --- a/android_pyapi/android/opengl.py +++ /dev/null @@ -1,464 +0,0 @@ -""" -Android OpenGL 组件 — GLSurfaceView, GLU, GLUtils, EGL14 等 -方法签名完全对齐 Android SDK (android.opengl 包) -""" - -import math -from typing import Any, Callable, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# EGL 常量 -# ═══════════════════════════════════════════════════════════════ - -class EGL14(Stub): - """EGL14 — EGL 1.4 API 常量 (桌面仅提供常量)""" - - # EGL 布尔值 - EGL_TRUE = 1 - EGL_FALSE = 0 - - # EGL 错误码 - EGL_SUCCESS = 0x3000 - EGL_NOT_INITIALIZED = 0x3001 - EGL_BAD_ACCESS = 0x3002 - EGL_BAD_ALLOC = 0x3003 - EGL_BAD_ATTRIBUTE = 0x3004 - EGL_BAD_CONFIG = 0x3005 - EGL_BAD_CONTEXT = 0x3006 - EGL_BAD_CURRENT_SURFACE = 0x3007 - EGL_BAD_DISPLAY = 0x3008 - EGL_BAD_MATCH = 0x3009 - EGL_BAD_NATIVE_PIXMAP = 0x300A - EGL_BAD_NATIVE_WINDOW = 0x300B - EGL_BAD_PARAMETER = 0x300C - EGL_BAD_SURFACE = 0x300D - EGL_CONTEXT_LOST = 0x300E - - # EGL 配置属性 - EGL_BUFFER_SIZE = 0x3020 - EGL_ALPHA_SIZE = 0x3021 - EGL_BLUE_SIZE = 0x3022 - EGL_GREEN_SIZE = 0x3023 - EGL_RED_SIZE = 0x3024 - EGL_DEPTH_SIZE = 0x3025 - EGL_STENCIL_SIZE = 0x3026 - EGL_CONFIG_CAVEAT = 0x3027 - EGL_CONFIG_ID = 0x3028 - EGL_LEVEL = 0x3029 - EGL_MAX_PBUFFER_HEIGHT = 0x302A - EGL_MAX_PBUFFER_PIXELS = 0x302B - EGL_MAX_PBUFFER_WIDTH = 0x302C - EGL_NATIVE_RENDERABLE = 0x302D - EGL_NATIVE_VISUAL_ID = 0x302E - EGL_NATIVE_VISUAL_TYPE = 0x302F - EGL_PRESERVED_RESOURCES = 0x3030 - EGL_SAMPLES = 0x3031 - EGL_SAMPLE_BUFFERS = 0x3032 - EGL_SURFACE_TYPE = 0x3033 - EGL_TRANSPARENT_TYPE = 0x3034 - EGL_TRANSPARENT_BLUE_VALUE = 0x3035 - EGL_TRANSPARENT_GREEN_VALUE = 0x3036 - EGL_TRANSPARENT_RED_VALUE = 0x3037 - EGL_NONE = 0x3038 - EGL_BIND_TO_TEXTURE_RGB = 0x3039 - EGL_BIND_TO_TEXTURE_RGBA = 0x303A - EGL_MIN_SWAP_INTERVAL = 0x303B - EGL_MAX_SWAP_INTERVAL = 0x303C - - # EGL 表面属性 - EGL_WIDTH = 0x3057 - EGL_HEIGHT = 0x3056 - EGL_LARGEST_PBUFFER = 0x3058 - EGL_TEXTURE_FORMAT = 0x3080 - EGL_TEXTURE_TARGET = 0x3081 - EGL_MIPMAP_TEXTURE = 0x3082 - EGL_MIPMAP_LEVEL = 0x3083 - - # EGL 渲染上下文属性 - EGL_CONTEXT_CLIENT_TYPE = 0x3097 - EGL_CONTEXT_CLIENT_VERSION = 0x3098 - - # EGL 显示 - EGL_DEFAULT_DISPLAY = 0 - - # EGL 版本 - EGL_VERSION_1_4 = 1 - - # OpenGL ES 位标记 - EGL_OPENGL_ES_BIT = 0x0001 - EGL_OPENGL_ES2_BIT = 0x0004 - EGL_OPENGL_ES3_BIT = 0x0040 - - def __init__(self): - super().__init__() - - @staticmethod - def eglGetDisplay(display_id: Any) -> "EGLDisplay": - """获取 EGL 显示 (桌面仅返回存根)""" - raise AndroidOnlyError("EGL14.eglGetDisplay() 仅在 Android 设备上可用") - - @staticmethod - def eglInitialize(display: "EGLDisplay", major_minor: Optional[List[int]] = None) -> bool: - """初始化 EGL (桌面仅返回存根)""" - raise AndroidOnlyError("EGL14.eglInitialize() 仅在 Android 设备上可用") - - @staticmethod - def eglChooseConfig(display: "EGLDisplay", attrib_list: List[int], - configs: List[Any], config_size: int, - num_config: List[int]) -> bool: - """选择 EGL 配置 (桌面仅返回存根)""" - raise AndroidOnlyError("EGL14.eglChooseConfig() 仅在 Android 设备上可用") - - @staticmethod - def eglCreateContext(display: "EGLDisplay", config: "EGLConfig", - share_context: "EGLContext", - attrib_list: List[int]) -> "EGLContext": - """创建 EGL 上下文 (桌面仅返回存根)""" - raise AndroidOnlyError("EGL14.eglCreateContext() 仅在 Android 设备上可用") - - @staticmethod - def eglCreateWindowSurface(display: "EGLDisplay", config: "EGLConfig", - win: Any, attrib_list: List[int]) -> "EGLSurface": - """创建窗口表面 (桌面仅返回存根)""" - raise AndroidOnlyError("EGL14.eglCreateWindowSurface() 仅在 Android 设备上可用") - - @staticmethod - def eglMakeCurrent(display: "EGLDisplay", draw: "EGLSurface", - read: "EGLSurface", context: "EGLContext") -> bool: - """绑定 EGL 上下文 (桌面仅返回存根)""" - raise AndroidOnlyError("EGL14.eglMakeCurrent() 仅在 Android 设备上可用") - - @staticmethod - def eglSwapBuffers(display: "EGLDisplay", surface: "EGLSurface") -> bool: - """交换缓冲区 (桌面仅返回存根)""" - raise AndroidOnlyError("EGL14.eglSwapBuffers() 仅在 Android 设备上可用") - - @staticmethod - def eglDestroyContext(display: "EGLDisplay", context: "EGLContext") -> bool: - """销毁 EGL 上下文 (桌面仅返回存根)""" - raise AndroidOnlyError("EGL14.eglDestroyContext() 仅在 Android 设备上可用") - - @staticmethod - def eglDestroySurface(display: "EGLDisplay", surface: "EGLSurface") -> bool: - """销毁表面 (桌面仅返回存根)""" - raise AndroidOnlyError("EGL14.eglDestroySurface() 仅在 Android 设备上可用") - - @staticmethod - def eglTerminate(display: "EGLDisplay") -> bool: - """终止 EGL (桌面仅返回存根)""" - raise AndroidOnlyError("EGL14.eglTerminate() 仅在 Android 设备上可用") - - @staticmethod - def eglGetError() -> int: - """获取 EGL 错误码""" - return EGL14.EGL_SUCCESS - - @staticmethod - def eglQueryString(display: "EGLDisplay", name: int) -> str: - """查询 EGL 字符串""" - if name == 0x3029: # EGL_VERSION - return "1.4 (desktop stub)" - return "" - - def toString(self) -> str: - return "EGL14(desktop-stub)" - - -class EGLDisplay(Stub): - """EGLDisplay — EGL 显示连接""" - - def __init__(self): - super().__init__() - - def toString(self) -> str: - return f"EGLDisplay@{hex(id(self))}" - - def hashCode(self) -> int: - return hash(id(self)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -class EGLConfig(Stub): - """EGLConfig — EGL 帧缓冲配置""" - - def __init__(self): - super().__init__() - - def toString(self) -> str: - return f"EGLConfig@{hex(id(self))}" - - def hashCode(self) -> int: - return hash(id(self)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -class EGLContext(Stub): - """EGLContext — EGL 渲染上下文""" - - def __init__(self): - super().__init__() - - def toString(self) -> str: - return f"EGLContext@{hex(id(self))}" - - def hashCode(self) -> int: - return hash(id(self)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -class EGLSurface(Stub): - """EGLSurface — EGL 渲染表面""" - - def __init__(self): - super().__init__() - - def toString(self) -> str: - return f"EGLSurface@{hex(id(self))}" - - def hashCode(self) -> int: - return hash(id(self)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# GLSurfaceView -# ═══════════════════════════════════════════════════════════════ - -class GLSurfaceView(Stub): - """GLSurfaceView — OpenGL 渲染视图 (桌面存根)""" - - # 渲染模式 - RENDERMODE_CONTINUOUSLY = 1 - RENDERMODE_WHEN_DIRTY = 0 - - # 调试标志 - DEBUG_CHECK_GL_ERROR = 1 - DEBUG_LOG_GL_CALLS = 2 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._renderer: Optional[Any] = None - self._render_mode: int = GLSurfaceView.RENDERMODE_CONTINUOUSLY - self._egl_context_client_version: int = 2 - self._preserve_egl_context_on_pause: bool = False - self._debug_flags: int = 0 - self._paused: bool = False - self._queue: List[Callable] = [] - - def setRenderer(self, renderer: Any): - """设置渲染器""" - self._renderer = renderer - if renderer: - renderer.onSurfaceCreated(None, None) - renderer.onSurfaceChanged(None, 0, 0) - - def setRenderMode(self, render_mode: int): - """设置渲染模式""" - self._render_mode = render_mode - - def getRenderMode(self) -> int: - """获取渲染模式""" - return self._render_mode - - def onPause(self): - """暂停渲染""" - self._paused = True - - def onResume(self): - """恢复渲染""" - self._paused = False - - def queueEvent(self, r: Callable): - """将事件排入 GL 线程队列""" - self._queue.append(r) - # 立即执行 (桌面模拟) - try: - r() - except Exception: - pass - - def requestRender(self): - """请求渲染""" - if self._renderer and not self._paused: - self._renderer.onDrawFrame(None) - - def setEGLContextClientVersion(self, version: int): - """设置 EGL 客户端版本""" - self._egl_context_client_version = version - - def setPreserveEGLContextOnPause(self, preserve: bool): - """设置暂停时是否保留 EGL 上下文""" - self._preserve_egl_context_on_pause = preserve - - def setDebugFlags(self, debug_flags: int): - """设置调试标志""" - self._debug_flags = debug_flags - - def getPreserveEGLContextOnPause(self) -> bool: - return self._preserve_egl_context_on_pause - - def surfaceCreated(self, holder: Any): - """表面创建回调""" - pass - - def surfaceDestroyed(self, holder: Any): - """表面销毁回调""" - pass - - def surfaceChanged(self, holder: Any, format: int, w: int, h: int): - """表面变化回调""" - pass - - def toString(self) -> str: - return f"GLSurfaceView(renderMode={self._render_mode}, paused={self._paused})" - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# GLU -# ═══════════════════════════════════════════════════════════════ - -class GLU(Stub): - """GLU — OpenGL 实用工具函数""" - - def __init__(self): - super().__init__() - - @staticmethod - def gluLookAt(eye_x: float, eye_y: float, eye_z: float, - center_x: float, center_y: float, center_z: float, - up_x: float, up_y: float, up_z: float): - """定义视图变换 (桌面仅抛异常)""" - raise AndroidOnlyError("GLU.gluLookAt() 仅在 Android (OpenGL) 设备上可用") - - @staticmethod - def gluPerspective(fov_y: float, aspect: float, z_near: float, z_far: float): - """设置透视投影 (桌面仅抛异常)""" - raise AndroidOnlyError("GLU.gluPerspective() 仅在 Android (OpenGL) 设备上可用") - - @staticmethod - def gluOrtho2D(left: float, right: float, bottom: float, top: float): - """设置正交投影 (桌面仅抛异常)""" - raise AndroidOnlyError("GLU.gluOrtho2D() 仅在 Android (OpenGL) 设备上可用") - - @staticmethod - def gluProject(obj_x: float, obj_y: float, obj_z: float, - model: List[float], proj: List[float], - viewport: List[int]) -> Tuple[bool, float, float, float]: - """将对象坐标映射到窗口坐标""" - win_x = (obj_x * proj[0] + 1.0) * viewport[2] / 2.0 + viewport[0] - win_y = (obj_y * proj[5] + 1.0) * viewport[3] / 2.0 + viewport[1] - win_z = obj_z * proj[10] + proj[11] - return True, win_x, win_y, win_z - - @staticmethod - def gluUnProject(win_x: float, win_y: float, win_z: float, - model: List[float], proj: List[float], - viewport: List[int]) -> Tuple[bool, float, float, float]: - """将窗口坐标映射到对象坐标""" - obj_x = 2.0 * (win_x - viewport[0]) / viewport[2] - 1.0 - obj_y = 2.0 * (win_y - viewport[1]) / viewport[3] - 1.0 - obj_z = win_z * 2.0 - 1.0 - return True, obj_x, obj_y, obj_z - - @staticmethod - def gluErrorString(error: int) -> str: - """返回 OpenGL 错误描述字符串""" - errors = { - 0x0500: "GL_INVALID_ENUM", - 0x0501: "GL_INVALID_VALUE", - 0x0502: "GL_INVALID_OPERATION", - 0x0503: "GL_STACK_OVERFLOW", - 0x0504: "GL_STACK_UNDERFLOW", - 0x0505: "GL_OUT_OF_MEMORY", - 0x0506: "GL_INVALID_FRAMEBUFFER_OPERATION", - } - return errors.get(error, f"Unknown error 0x{error:04X}") - - def toString(self) -> str: - return "GLU(desktop-stub)" - - def hashCode(self) -> int: - return hash("android.opengl.GLU") & 0x7FFFFFFF - - def equals(self, other) -> bool: - return isinstance(other, GLU) - - -# ═══════════════════════════════════════════════════════════════ -# GLUtils -# ═══════════════════════════════════════════════════════════════ - -class GLUtils(Stub): - """GLUtils — OpenGL 工具函数""" - - def __init__(self): - super().__init__() - - @staticmethod - def getInternalFormat(bitmap: Any) -> int: - """获取位图的内部格式""" - return 0x1908 # GL_RGBA - - @staticmethod - def getType(bitmap: Any) -> int: - """获取位图数据类型""" - return 0x1401 # GL_UNSIGNED_BYTE - - @staticmethod - def texImage2D(target: int, level: int, internal_format: int, - bitmap: Any, border: int = 0): - """上传 2D 纹理图像""" - raise AndroidOnlyError("GLUtils.texImage2D() 仅在 Android (OpenGL) 设备上可用") - - @staticmethod - def texSubImage2D(target: int, level: int, xoffset: int, yoffset: int, - bitmap: Any, format: int = -1, type_: int = -1): - """更新 2D 纹理子图像""" - raise AndroidOnlyError("GLUtils.texSubImage2D() 仅在 Android (OpenGL) 设备上可用") - - @staticmethod - def toString(bitmap_or_buffer: Any) -> str: - return str(bitmap_or_buffer) - - def toString(self) -> str: - return "GLUtils(desktop-stub)" - - def hashCode(self) -> int: - return hash("android.opengl.GLUtils") & 0x7FFFFFFF - - def equals(self, other) -> bool: - return isinstance(other, GLUtils) - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "GLSurfaceView", - "GLU", - "GLUtils", - "EGL14", - "EGLConfig", - "EGLContext", - "EGLDisplay", - "EGLSurface", -] \ No newline at end of file diff --git a/android_pyapi/android/os.py b/android_pyapi/android/os.py deleted file mode 100644 index da6ab4d..0000000 --- a/android_pyapi/android/os.py +++ /dev/null @@ -1,2370 +0,0 @@ -""" -Android OS 组件 — Bundle, Handler, Looper, Message, Build 等 -方法签名完全对齐 Android SDK -""" - -import asyncio -import functools -import heapq -import os as _os -import sys -import threading -import time as _time -from datetime import datetime -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, TypeVar, Union - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, - HAS_PSUTIL, -) - -# Linux 真实实现导入 -try: - from .._linux_impl import IS_LINUX, LinuxPower, LinuxProcess, HAS_PSUTIL as _HAS_PSUTIL - _USE_LINUX = IS_LINUX -except ImportError: - _USE_LINUX = False - - -# ═══════════════════════════════════════════════════════════════ -# Build -# ═══════════════════════════════════════════════════════════════ - -class Build(Stub): - """Build — 设备信息""" - - class VERSION(Stub): - """版本信息""" - SDK_INT = 34 # Android 14 - RELEASE = "14" - INCREMENTAL = "20240301" - CODENAME = "REL" - SECURITY_PATCH = "2024-03-05" - BASE_OS = "" - PREVIEW_SDK_INT = 0 - RESOURCES_SDK_INT = 34 - MEDIA_PERFORMANCE_CLASS = 0 - - class VERSION_CODES(Stub): - """版本代码常量""" - BASE = 1 - BASE_1_1 = 2 - CUPCAKE = 3 - DONUT = 4 - ECLAIR = 5 - ECLAIR_0_1 = 6 - ECLAIR_MR1 = 7 - FROYO = 8 - GINGERBREAD = 9 - GINGERBREAD_MR1 = 10 - HONEYCOMB = 11 - HONEYCOMB_MR1 = 12 - HONEYCOMB_MR2 = 13 - ICE_CREAM_SANDWICH = 14 - ICE_CREAM_SANDWICH_MR1 = 15 - JELLY_BEAN = 16 - JELLY_BEAN_MR1 = 17 - JELLY_BEAN_MR2 = 18 - KITKAT = 19 - KITKAT_WATCH = 20 - LOLLIPOP = 21 - LOLLIPOP_MR1 = 22 - M = 23 - N = 24 - N_MR1 = 25 - O = 26 - O_MR1 = 27 - P = 28 - Q = 29 - R = 30 - S = 31 - S_V2 = 32 - TIRAMISU = 33 - UPSIDE_DOWN_CAKE = 34 - - VERSION = VERSION() - BOARD = "redfin" - BOOTLOADER = "r7.0-12345" - BRAND = "google" - CPU_ABI = "arm64-v8a" - CPU_ABI2 = "" - DEVICE = "redfin" - DISPLAY = "AP1A.240305.001" - FINGERPRINT = "google/redfin/redfin:14/AP1A.240305.001/12345678:user/release-keys" - HARDWARE = "redfin" - HOST = "build-test-01" - ID = "AP1A.240305.001" - MANUFACTURER = "Google" - MODEL = "Pixel 5" - ODM_SKU = None - PRODUCT = "redfin" - RADIO = "" - SERIAL = "unknown" - SKU = None - SOC_MANUFACTURER = "Qualcomm" - SOC_MODEL = "Snapdragon 765G" - SUPPORTED_32_BIT_ABIS = ["armeabi-v7a", "armeabi"] - SUPPORTED_64_BIT_ABIS = ["arm64-v8a"] - SUPPORTED_ABIS = ["arm64-v8a", "armeabi-v7a", "armeabi"] - TAGS = "release-keys" - TIME = int(_time.time() * 1000) - TYPE = "user" - USER = "build-user" - - @staticmethod - def getRadioVersion() -> str: - """返回基带/无线电版本。桌面端无无线电硬件,返回空字符串。""" - return Build.RADIO - - @staticmethod - def getSerial() -> str: - """返回设备序列号。尝试从 /sys 或 /proc 读取真实硬件序列号。""" - # 尝试从 /sys/class/android_usb 读取 - try: - for name in _os.listdir("/sys/class/android_usb"): - serial_path = f"/sys/class/android_usb/{name}/iSerial" - if _os.path.exists(serial_path): - with open(serial_path, "r") as f: - serial = f.read().strip() - if serial: - return serial - except (OSError, IOError): - pass - # 尝试从 /proc/cpuinfo 读取 Serial 字段 - try: - with open("/proc/cpuinfo", "r") as f: - for line in f: - if line.lower().startswith("serial"): - parts = line.split(":", 1) - if len(parts) == 2: - serial = parts[1].strip() - if serial and serial != "0" * len(serial): - return serial - except (OSError, IOError): - pass - # 尝试从 /sys/class/dmi/id/product_serial 读取 - try: - path = "/sys/class/dmi/id/product_serial" - if _os.path.exists(path): - with open(path, "r") as f: - serial = f.read().strip() - if serial: - return serial - except (OSError, IOError): - pass - return Build.SERIAL - - @staticmethod - def getFingerprint() -> str: - return Build.FINGERPRINT - - @classmethod - def _populate_from_linux(cls): - """从 LinuxProcess.get_system_info() 填充真实设备信息""" - try: - info = LinuxProcess.get_system_info() or {} - import platform as _platform - import socket as _socket - hostname = info.get("hostname") or _socket.gethostname() - system = info.get("platform") or _platform.system() - arch = info.get("arch") or _platform.machine() - kernel = info.get("kernel") or _platform.release() - cls.MODEL = hostname - cls.MANUFACTURER = "Linux" - cls.BRAND = system - cls.DEVICE = arch - cls.BOARD = arch - cls.HARDWARE = arch - cls.HOST = hostname - cls.PRODUCT = hostname - cls.DISPLAY = kernel - cls.ID = kernel - # FINGERPRINT: brand/device/device:version/id/build:user/release-keys - cls.FINGERPRINT = ( - f"{system}/{hostname}/{arch}:Linux/{kernel}/{hostname}:user/release-keys" - ) - # CPU ABI 用架构映射 - _abi_map = { - "x86_64": "x86_64", "amd64": "x86_64", - "aarch64": "arm64-v8a", "arm64": "arm64-v8a", - "x86": "x86", "i386": "x86", "i686": "x86", - "armv7l": "armeabi-v7a", "arm": "armeabi-v7a", - } - cls.CPU_ABI = _abi_map.get(arch, arch) - cls.SUPPORTED_ABIS = [cls.CPU_ABI] - cls.SUPPORTED_64_BIT_ABIS = [cls.CPU_ABI] if "64" in cls.CPU_ABI else [] - cls.SUPPORTED_32_BIT_ABIS = [cls.CPU_ABI] if "64" not in cls.CPU_ABI else [] - except Exception: - pass - - -# 在 Linux 上用真实系统信息填充 Build -if _USE_LINUX: - Build._populate_from_linux() - - -# ═══════════════════════════════════════════════════════════════ -# SystemClock -# ═══════════════════════════════════════════════════════════════ - -class SystemClock(Stub): - """SystemClock — 系统时钟""" - - @staticmethod - def elapsedRealtime() -> int: - """返回自启动以来的毫秒数""" - return int(_time.monotonic() * 1000) - - @staticmethod - def elapsedRealtimeNanos() -> int: - return int(_time.monotonic_ns()) - - @staticmethod - def uptimeMillis() -> int: - """返回自启动以来(不含深度睡眠)的毫秒数""" - return int(_time.monotonic() * 1000) - - @staticmethod - def currentThreadTimeMillis() -> int: - return int(_time.clock_gettime(_time.CLOCK_THREAD_CPUTIME_ID) * 1000) - - @staticmethod - def sleep(ms: int): - _time.sleep(ms / 1000.0) - - @staticmethod - def setCurrentTimeMillis(millis: int) -> bool: - """尝试设置系统时间。需要 root 权限,通常在桌面端会失败。""" - try: - import subprocess - seconds = millis / 1000.0 - # 尝试使用 date 命令设置时间 - result = subprocess.run( - ["date", "-s", f"@{int(seconds)}"], - capture_output=True, timeout=5) - return result.returncode == 0 - except Exception: - return False - - -# ═══════════════════════════════════════════════════════════════ -# Environment -# ═══════════════════════════════════════════════════════════════ - -class Environment(Stub): - """Environment — 环境目录""" - - MEDIA_BAD_REMOVAL = "bad_removal" - MEDIA_CHECKING = "checking" - MEDIA_EJECTING = "ejecting" - MEDIA_MOUNTED = "mounted" - MEDIA_MOUNTED_READ_ONLY = "mounted_ro" - MEDIA_NOFS = "nofs" - MEDIA_REMOVED = "removed" - MEDIA_SHARED = "shared" - MEDIA_UNKNOWN = "unknown" - MEDIA_UNMOUNTABLE = "unmountable" - MEDIA_UNMOUNTED = "unmounted" - - DIRECTORY_ALARMS = "Alarms" - DIRECTORY_DCIM = "DCIM" - DIRECTORY_DOCUMENTS = "Documents" - DIRECTORY_DOWNLOADS = "Download" - DIRECTORY_MOVIES = "Movies" - DIRECTORY_MUSIC = "Music" - DIRECTORY_NOTIFICATIONS = "Notifications" - DIRECTORY_PICTURES = "Pictures" - DIRECTORY_PODCASTS = "Podcasts" - DIRECTORY_RINGTONES = "Ringtones" - DIRECTORY_AUDIOBOOKS = "Audiobooks" - DIRECTORY_SCREENSHOTS = "Screenshots" - - @staticmethod - def getExternalStorageDirectory() -> str: - return _sdcard_root - - @staticmethod - def getExternalStoragePublicDirectory(type_: str) -> str: - path = _os.path.join(_sdcard_root, type_) - _os.makedirs(path, exist_ok=True) - return path - - @staticmethod - def getDataDirectory() -> str: - return _os.path.join(_app_data_dir, "data") - - @staticmethod - def getDownloadCacheDirectory() -> str: - return _os.path.join(_app_data_dir, "cache", "download") - - @staticmethod - def getRootDirectory() -> str: - return _os.path.abspath(_os.sep) - - @staticmethod - def getStorageDirectory(user_handle: Any = None) -> str: - return _sdcard_root - - @staticmethod - def getExternalStorageState(path: Optional[str] = None) -> str: - return Environment.MEDIA_MOUNTED - - @staticmethod - def isExternalStorageRemovable(path: Optional[str] = None) -> bool: - """判断外部存储是否可移除。通过检查 /proc/mounts 判断。""" - try: - check_path = path or _sdcard_root - with open("/proc/mounts", "r") as f: - for line in f: - parts = line.split() - if len(parts) >= 3 and check_path in parts[1]: - # 检查挂载选项中是否包含 SD 卡相关设备 - device = parts[0] - if "/sd" in device or "/mmc" in device or "/usb" in device: - return True - return False - except (OSError, IOError): - pass - return False - - @staticmethod - def isExternalStorageEmulated(path: Optional[str] = None) -> bool: - return True - - @staticmethod - def isExternalStorageLegacy(path: Optional[str] = None) -> bool: - return True - - -# ═══════════════════════════════════════════════════════════════ -# Process -# ═══════════════════════════════════════════════════════════════ - -class Process(Stub): - """Process — 进程信息""" - - # 进程优先级 - THREAD_PRIORITY_DEFAULT = 0 - THREAD_PRIORITY_LOWEST = -19 - THREAD_PRIORITY_BACKGROUND = 10 - THREAD_PRIORITY_FOREGROUND = -2 - THREAD_PRIORITY_DISPLAY = -4 - THREAD_PRIORITY_URGENT_DISPLAY = -8 - THREAD_PRIORITY_AUDIO = -16 - THREAD_PRIORITY_URGENT_AUDIO = -19 - THREAD_PRIORITY_MORE_FAVORABLE = -1 - THREAD_PRIORITY_LESS_FAVORABLE = 1 - - SIGNAL_KILL = 9 - SIGNAL_QUIT = 3 - SIGNAL_USR1 = 10 - SIGNAL_USR2 = 12 - - @staticmethod - def myTid() -> int: - return threading.get_ident() - - @staticmethod - def myUid() -> int: - """返回当前进程的真实 UID。""" - return _os.getuid() - - @staticmethod - def myPid() -> int: - return _os.getpid() - - @staticmethod - def killProcess(pid): - import signal - try: - _os.kill(pid, signal.SIGKILL) - except (OSError, ProcessLookupError): - pass - - @staticmethod - def sendSignal(pid, signal_num): - try: - _os.kill(pid, signal_num) - except (OSError, ProcessLookupError): - pass - - @staticmethod - def setThreadPriority(*args): - """设置线程优先级。支持 setThreadPriority(priority) 和 setThreadPriority(tid, priority)。""" - if len(args) == 1: - tid = 0 - priority = args[0] - elif len(args) == 2: - tid, priority = args - else: - return - try: - _os.setpriority(_os.PRIO_PROCESS, tid, priority) - except (OSError, PermissionError): - pass - - @staticmethod - def getElapsedCpuTime() -> int: - return int(_time.clock_gettime(_time.CLOCK_PROCESS_CPUTIME_ID) * 1000) - - @staticmethod - def supportsProcesses() -> bool: - return True - - -# ═══════════════════════════════════════════════════════════════ -# BaseBundle / PersistableBundle -# ═══════════════════════════════════════════════════════════════ - -class BaseBundle(Stub): - """BaseBundle — Bundle 基类""" - - def __init__(self): - super().__init__() - self._map: Dict[str, Any] = {} - self._class_loader = None - - def getString(self, key: str, default: Optional[str] = None) -> Optional[str]: - v = self._map.get(key, default) - return str(v) if v is not None else default - - def getInt(self, key: str, default: int = 0) -> int: - v = self._map.get(key, default) - try: - return int(v) if v is not None else default - except (ValueError, TypeError): - return default - - def getLong(self, key: str, default: int = 0) -> int: - return self.getInt(key, default) - - def getFloat(self, key: str, default: float = 0.0) -> float: - v = self._map.get(key, default) - try: - return float(v) if v is not None else default - except (ValueError, TypeError): - return default - - def getDouble(self, key: str, default: float = 0.0) -> float: - return self.getFloat(key, default) - - def getBoolean(self, key: str, default: bool = False) -> bool: - v = self._map.get(key, default) - if isinstance(v, bool): - return v - if isinstance(v, str): - return v.lower() in ("true", "1", "yes") - return bool(v) if v is not None else default - - def getByte(self, key: str, default: int = 0) -> int: - return self.getInt(key, default) - - def getChar(self, key: str, default: str = "") -> str: - return str(self._map.get(key, default)) - - def getShort(self, key: str, default: int = 0) -> int: - return self.getInt(key, default) - - def getCharSequence(self, key: str, default: Optional[str] = None) -> Optional[str]: - return self.getString(key, default) - - def getSerializable(self, key: str, default: Any = None) -> Any: - return self._map.get(key, default) - - def getStringArrayList(self, key: str) -> Optional[List[str]]: - v = self._map.get(key) - if isinstance(v, list): - return [str(x) for x in v] - return None - - def getIntegerArrayList(self, key: str) -> Optional[List[int]]: - v = self._map.get(key) - if isinstance(v, list): - return [int(x) for x in v if isinstance(x, (int, float))] - return None - - def containsKey(self, key: str) -> bool: - return key in self._map - - def isEmpty(self) -> bool: - return len(self._map) == 0 - - def size(self) -> int: - return len(self._map) - - def clear(self): - self._map.clear() - - def keySet(self) -> Set[str]: - return set(self._map.keys()) - - def hasKey(self, key: str) -> bool: - return key in self._map - - def toString(self) -> str: - return f"Bundle({self._map})" - - def __len__(self) -> int: - return len(self._map) - - def __contains__(self, key: str) -> bool: - return key in self._map - - def __getitem__(self, key: str): - return self._map[key] - - def __setitem__(self, key: str, value: Any): - self._map[key] = value - - def __delitem__(self, key: str): - self._map.pop(key, None) - - -class PersistableBundle(BaseBundle): - """PersistableBundle — 可持久化的 Bundle""" - - def __init__(self, capacity: int = 0): - super().__init__() - - def putPersistableBundle(self, key: str, value: "PersistableBundle"): - self._map[key] = value - - def getPersistableBundle(self, key: str) -> Optional["PersistableBundle"]: - v = self._map.get(key) - return v if isinstance(v, PersistableBundle) else None - - -# ═══════════════════════════════════════════════════════════════ -# Bundle -# ═══════════════════════════════════════════════════════════════ - -class Bundle(BaseBundle): - """Bundle — 键值对数据容器""" - - EMPTY: "Bundle" = None # 在类加载后初始化 - - def __init__(self, capacity: int = 0): - super().__init__() - self._capacity = capacity - - def putString(self, key: str, value: Optional[str]): - self._map[key] = value - - def putInt(self, key: str, value: int): - self._map[key] = value - - def putLong(self, key: str, value: int): - self._map[key] = value - - def putFloat(self, key: str, value: float): - self._map[key] = value - - def putDouble(self, key: str, value: float): - self._map[key] = value - - def putBoolean(self, key: str, value: bool): - self._map[key] = value - - def putByte(self, key: str, value: int): - self._map[key] = value - - def putChar(self, key: str, value: str): - self._map[key] = value - - def putShort(self, key: str, value: int): - self._map[key] = value - - def putCharSequence(self, key: str, value: Optional[str]): - self._map[key] = value - - def putSerializable(self, key: str, value: Any): - self._map[key] = value - - def putParcelable(self, key: str, value: Any): - self._map[key] = value - - def putStringArrayList(self, key: str, value: Optional[List[str]]): - self._map[key] = value - - def putIntegerArrayList(self, key: str, value: Optional[List[int]]): - self._map[key] = value - - # ---- 数组类型 ---- - def putIntArray(self, key: str, value: Optional[List[int]]): - self._map[key] = list(value) if value is not None else None - - def getIntArray(self, key: str) -> Optional[List[int]]: - v = self._map.get(key) - if isinstance(v, list): - return [int(x) for x in v] - return None - - def putStringArray(self, key: str, value: Optional[List[str]]): - self._map[key] = list(value) if value is not None else None - - def getStringArray(self, key: str) -> Optional[List[str]]: - v = self._map.get(key) - if isinstance(v, list): - return [str(x) for x in v] - return None - - def putLongArray(self, key: str, value: Optional[List[int]]): - self._map[key] = list(value) if value is not None else None - - def getLongArray(self, key: str) -> Optional[List[int]]: - v = self._map.get(key) - if isinstance(v, list): - return [int(x) for x in v] - return None - - def putBooleanArray(self, key: str, value: Optional[List[bool]]): - self._map[key] = list(value) if value is not None else None - - def getBooleanArray(self, key: str) -> Optional[List[bool]]: - v = self._map.get(key) - if isinstance(v, list): - return [bool(x) for x in v] - return None - - def putDoubleArray(self, key: str, value: Optional[List[float]]): - self._map[key] = list(value) if value is not None else None - - def getDoubleArray(self, key: str) -> Optional[List[float]]: - v = self._map.get(key) - if isinstance(v, list): - return [float(x) for x in v] - return None - - def putByteArray(self, key: str, value: Optional[List[int]]): - self._map[key] = list(value) if value is not None else None - - def getByteArray(self, key: str) -> Optional[List[int]]: - v = self._map.get(key) - if isinstance(v, list): - return [int(x) & 0xFF for x in v] - return None - - def putAll(self, bundle: "Bundle"): - if bundle: - self._map.update(bundle._map) - - def getParcelable(self, key: str, default: Any = None) -> Any: - return self._map.get(key, default) - - def getBundle(self, key: str) -> Optional["Bundle"]: - v = self._map.get(key) - return v if isinstance(v, Bundle) else None - - def putBundle(self, key: str, value: Optional["Bundle"]): - self._map[key] = value - - def describeContents(self) -> int: - return 0 - - def clone(self) -> "Bundle": - b = Bundle() - b._map = dict(self._map) - return b - - def deepCopy(self) -> "Bundle": - import copy - b = Bundle() - b._map = copy.deepcopy(self._map) - return b - - def filterValues(self): - pass - - def getPairValue(self) -> Optional[str]: - return None - - def setClassLoader(self, loader: Any): - self._class_loader = loader - - def getClassLoader(self) -> Any: - return self._class_loader - - @staticmethod - def forPair(key: str, value: str) -> "Bundle": - b = Bundle() - b.putString(key, value) - return b - - -Bundle.EMPTY = Bundle() - - -# ═══════════════════════════════════════════════════════════════ -# Parcel / Parcelable -# ═══════════════════════════════════════════════════════════════ - -class Parcelable(Stub): - """Parcelable 接口""" - - CONTENTS_FILE_DESCRIPTOR = "CONTENTS_FILE_DESCRIPTOR" - PARCELABLE_WRITE_RETURN_VALUE = 1 - PARCELABLE_ELIDE_DUPLICATES = 2 - - def describeContents(self) -> int: - return 0 - - def writeToParcel(self, dest: "Parcel", flags: int): - pass - - class Creator: - def createFromParcel(self, source: "Parcel"): - return None - def newArray(self, size: int): - return [None] * size - - class ClassLoaderCreator: - def createFromParcel(self, source: "Parcel", loader: Any): - return None - def newArray(self, size: int): - return [None] * size - - -class Parcel(Stub): - """Parcel — 序列化数据容器""" - - STRING_CREATOR = "STRING_CREATOR" - STRING_LIST_CREATOR = "STRING_LIST_CREATOR" - INT_CREATOR = "INT_CREATOR" - - def __init__(self): - super().__init__() - self._data: Dict[str, Any] = {} - self._position = 0 - self._size = 0 - - @staticmethod - def obtain() -> "Parcel": - return Parcel() - - def recycle(self): - self._data.clear() - self._position = 0 - self._size = 0 - - def writeInt(self, value: int): - self._data.setdefault("_ints", []).append(value) - self._size += 4 - - def writeLong(self, value: int): - self._data.setdefault("_longs", []).append(value) - self._size += 8 - - def writeFloat(self, value: float): - self._data.setdefault("_floats", []).append(value) - self._size += 4 - - def writeDouble(self, value: float): - self._data.setdefault("_doubles", []).append(value) - self._size += 8 - - def writeString(self, value: Optional[str]): - strs = self._data.setdefault("_strings", []) - strs.append(value) - self._size += len(value) + 4 if value else 4 - - def writeBoolean(self, value: bool): - self.writeInt(1 if value else 0) - - def writeByte(self, value: int): - self.writeInt(value & 0xFF) - - def writeParcelable(self, p: Parcelable, flags: int): - pass - - def writeStringArray(self, values: Optional[List[str]]): - self._data["_string_array"] = values - - def writeIntArray(self, values: Optional[List[int]]): - self._data["_int_array"] = values - - def writeLongArray(self, values: Optional[List[int]]): - self._data["_long_array"] = values - - def readInt(self) -> int: - ints = self._data.get("_ints", []) - if ints: - return ints.pop(0) - return 0 - - def readLong(self) -> int: - longs = self._data.get("_longs", []) - if longs: - return longs.pop(0) - return 0 - - def readFloat(self) -> float: - floats = self._data.get("_floats", []) - if floats: - return floats.pop(0) - return 0.0 - - def readDouble(self) -> float: - doubles = self._data.get("_doubles", []) - if doubles: - return doubles.pop(0) - return 0.0 - - def readString(self) -> Optional[str]: - strs = self._data.get("_strings", []) - if strs: - return strs.pop(0) - return None - - def readBoolean(self) -> bool: - return self.readInt() != 0 - - def readByte(self) -> int: - return self.readInt() & 0xFF - - def dataAvail(self) -> int: - return self._size - self._position - - def dataPosition(self) -> int: - return self._position - - def setDataPosition(self, pos: int): - self._position = pos - - def dataSize(self) -> int: - return self._size - - def dataCapacity(self) -> int: - return self._size - - def setDataCapacity(self, capacity: int): - pass - - def appendFrom(self, parcel: "Parcel", offset: int, length: int): - pass - - def marshall(self) -> bytes: - import json - return json.dumps(self._data).encode("utf-8") - - @staticmethod - def unmarshall(data: bytes) -> "Parcel": - import json - p = Parcel() - try: - p._data = json.loads(data.decode("utf-8")) - except Exception: - pass - return p - - def writeValue(self, v: Any): - self._data["_value"] = v - - def readValue(self) -> Any: - return self._data.get("_value") - - def hasFileDescriptors(self) -> bool: - return False - - def toString(self) -> str: - return f"Parcel({self._size} bytes)" - - -# ═══════════════════════════════════════════════════════════════ -# Message -# ═══════════════════════════════════════════════════════════════ - -class Message(Stub): - """Message — Handler 消息""" - - def __init__(self): - super().__init__() - self.what = 0 - self.arg1 = 0 - self.arg2 = 0 - self.obj: Any = None - self.data: Optional[Bundle] = None - self.replyTo: Optional["Messenger"] = None - self._target: Optional["Handler"] = None - self._callback: Optional[Callable] = None - self.when: int = 0 - self._sending = False - - @staticmethod - def obtain(handler: Optional["Handler"] = None, what: int = 0) -> "Message": - msg = Message() - if handler: - msg._target = handler - msg.what = what - return msg - - def sendToTarget(self): - if self._target: - self._target.sendMessage(self) - - def getTarget(self) -> Optional["Handler"]: - return self._target - - def setTarget(self, handler: "Handler"): - self._target = handler - - @property - def target(self) -> Optional["Handler"]: - return self._target - - @target.setter - def target(self, handler: "Handler"): - self._target = handler - - def getCallback(self) -> Optional[Callable]: - return self._callback - - def copyFrom(self, other: "Message"): - self.what = other.what - self.arg1 = other.arg1 - self.arg2 = other.arg2 - self.obj = other.obj - self.data = other.data - self.replyTo = other.replyTo - - def recycle(self): - self.what = 0 - self.arg1 = 0 - self.arg2 = 0 - self.obj = None - self.data = None - self._target = None - self._callback = None - - def isAsynchronous(self) -> bool: - return False - - def setAsynchronous(self, async_: bool): - pass - - def toString(self) -> str: - return f"Message(what={self.what}, arg1={self.arg1}, arg2={self.arg2})" - - -# ═══════════════════════════════════════════════════════════════ -# Looper -# ═══════════════════════════════════════════════════════════════ - -class Looper(Stub): - """Looper — 消息循环""" - - _main_looper: Optional["Looper"] = None - _thread_local = threading.local() - - def __init__(self, quit_allowed: bool = True): - super().__init__() - self._thread = threading.current_thread() - self._running = False - self._message_queue: List[Message] = [] - self._queue_lock = threading.Lock() - self._quit_allowed = quit_allowed - - @staticmethod - def prepare(): - """为当前线程准备一个 Looper""" - looper = Looper() - Looper._thread_local.looper = looper - return looper - - @staticmethod - def prepareMainLooper(): - if Looper._main_looper is None: - Looper._main_looper = Looper.prepare() - - @staticmethod - def getMainLooper() -> "Looper": - if Looper._main_looper is None: - Looper.prepareMainLooper() - return Looper._main_looper - - @staticmethod - def myLooper() -> Optional["Looper"]: - return getattr(Looper._thread_local, "looper", None) - - @staticmethod - def loop(): - """运行消息循环,处理到期的消息""" - looper = Looper.myLooper() - if looper is None: - raise RuntimeError("No Looper prepared") - looper._running = True - while looper._running: - msg = looper._next() - if msg is not None: - target = msg._target - if target is not None: - target.dispatchMessage(msg) - msg.recycle() - else: - # 无到期消息,短暂 sleep 避免忙等 - _time.sleep(0.001) - - def _next(self) -> Optional[Message]: - """非阻塞地取出一条到期消息(when <= now)""" - with self._queue_lock: - now = SystemClock.uptimeMillis() - for i, msg in enumerate(self._message_queue): - if msg.when <= now: - return self._message_queue.pop(i) - return None - - def _enqueue(self, msg: Message): - """将消息按 when 时间顺序入队""" - with self._queue_lock: - inserted = False - for i, m in enumerate(self._message_queue): - if msg.when < m.when: - self._message_queue.insert(i, msg) - inserted = True - break - if not inserted: - self._message_queue.append(msg) - - def _remove_callbacks(self, callback): - """从队列中移除匹配 callback 的消息""" - with self._queue_lock: - self._message_queue = [ - m for m in self._message_queue - if m._callback is not callback - ] - - def _remove_messages(self, what: int): - """从队列中移除匹配 what 的消息""" - with self._queue_lock: - self._message_queue = [ - m for m in self._message_queue - if m.what != what - ] - - def _has_messages(self, what: int) -> bool: - """检查队列中是否有匹配 what 的消息""" - with self._queue_lock: - return any(m.what == what for m in self._message_queue) - - def quit(self): - """退出消息循环""" - if not self._quit_allowed: - raise RuntimeError("Main Looper is not allowed to quit") - with self._queue_lock: - self._running = False - self._message_queue.clear() - - def quitSafely(self): - """安全退出,仅停止循环(已入队消息不再处理)""" - with self._queue_lock: - self._running = False - - def isCurrentThread(self) -> bool: - return threading.current_thread() == self._thread - - def getThread(self) -> threading.Thread: - return self._thread - - def getQueue(self): - return self._message_queue - - def dump(self, pw: Any, prefix: str): - """将 DropBox 条目转储到给定的 writer。""" - try: - if hasattr(pw, "write"): - pw.write(f"{prefix}DropBoxManager entries:\n") - for entry in self._entries: - pw.write(f"{prefix} [{entry.getTag()}] @ {entry.getTimeMillis()}: " - f"{entry.getText(1024)}\n") - elif hasattr(pw, "println"): - pw.println(f"{prefix}DropBoxManager entries:") - for entry in self._entries: - pw.println(f"{prefix} [{entry.getTag()}] @ {entry.getTimeMillis()}: " - f"{entry.getText(1024)}") - except Exception: - pass - - def toString(self) -> str: - return f"Looper({self._thread.name})" - - -# ═══════════════════════════════════════════════════════════════ -# Handler -# ═══════════════════════════════════════════════════════════════ - -class Handler(Stub): - """Handler — 消息处理器""" - - def __init__(self, looper: Optional[Looper] = None, callback: Optional[Callable] = None): - super().__init__() - self._looper = looper or Looper.getMainLooper() - self._callback = callback - self._async = False - - def handleMessage(self, msg: Message): - pass - - def dispatchMessage(self, msg: Message): - if msg._callback: - msg._callback() - elif self._callback: - self._callback(msg) - else: - self.handleMessage(msg) - - def post(self, r: Callable) -> bool: - msg = Message.obtain(self) - msg._callback = r - return self.sendMessage(msg) - - def postDelayed(self, r: Callable, delay_ms: int) -> bool: - msg = Message.obtain(self) - msg._callback = r - return self.sendMessageDelayed(msg, delay_ms) - - def postAtTime(self, r: Callable, uptime_ms: int) -> bool: - msg = Message.obtain(self) - msg._callback = r - msg.when = uptime_ms - return self.sendMessageAtTime(msg, uptime_ms) - - def sendMessage(self, msg: Message) -> bool: - msg._target = self - looper = self._looper - if looper is not None and looper._running: - looper._enqueue(msg) - return True - # 当前线程无运行中的 Looper,回退到同步执行(保持兼容性) - self.dispatchMessage(msg) - return True - - def sendEmptyMessage(self, what: int) -> bool: - msg = Message.obtain(self, what) - return self.sendMessage(msg) - - def sendMessageDelayed(self, msg: Message, delay_ms: int) -> bool: - msg._target = self - msg.when = SystemClock.uptimeMillis() + delay_ms - looper = self._looper - if looper is not None and looper._running: - looper._enqueue(msg) - return True - # 回退到同步执行 - self.dispatchMessage(msg) - return True - - def sendMessageAtTime(self, msg: Message, uptime_ms: int) -> bool: - msg._target = self - msg.when = uptime_ms - looper = self._looper - if looper is not None and looper._running: - looper._enqueue(msg) - return True - self.dispatchMessage(msg) - return True - - def sendMessageAtFrontOfQueue(self, msg: Message) -> bool: - msg._target = self - msg.when = 0 - looper = self._looper - if looper is not None and looper._running: - with looper._queue_lock: - looper._message_queue.insert(0, msg) - return True - self.dispatchMessage(msg) - return True - - def removeCallbacks(self, r: Callable): - looper = self._looper - if looper is not None: - looper._remove_callbacks(r) - - def removeMessages(self, what: int): - looper = self._looper - if looper is not None: - looper._remove_messages(what) - - def obtainMessage(self, what: int = 0) -> Message: - return Message.obtain(self, what) - - def hasMessages(self, what: int) -> bool: - looper = self._looper - if looper is not None: - return looper._has_messages(what) - return False - - def getLooper(self) -> Looper: - return self._looper - - def toString(self) -> str: - return f"Handler({self.__class__.__name__})" - - -# ═══════════════════════════════════════════════════════════════ -# HandlerThread -# ═══════════════════════════════════════════════════════════════ - -class HandlerThread(threading.Thread, Stub): - """HandlerThread — 带 Looper 的工作线程""" - - def __init__(self, name: str, priority: int = Process.THREAD_PRIORITY_DEFAULT): - super().__init__() - Stub.__init__(self) - self._name = name - self._priority = priority - self._looper: Optional[Looper] = None - self._started = threading.Event() - - def run(self): - Looper.prepare() - self._looper = Looper.myLooper() - self._started.set() - Looper.loop() - - def getLooper(self) -> Optional[Looper]: - self._started.wait() - return self._looper - - def getThreadId(self) -> int: - return self.ident or 0 - - def quit(self) -> bool: - if self._looper: - self._looper.quit() - return True - return False - - def quitSafely(self) -> bool: - return self.quit() - - -# ═══════════════════════════════════════════════════════════════ -# AsyncTask Executor (守护线程池) -# ═══════════════════════════════════════════════════════════════ - -class _AsyncTaskExecutor: - """守护线程池执行器 — 用于 AsyncTask 后台执行,不阻塞程序退出。 - - 使用 BoundedSemaphore 限制并发数,每个任务在独立的守护线程中运行。 - """ - - def __init__(self, max_workers: int = 4): - self._max_workers = max_workers - self._semaphore = threading.BoundedSemaphore(max_workers) - self._shutdown_flag = False - - def submit(self, fn, *args, **kwargs): - if self._shutdown_flag: - raise RuntimeError("Executor has been shut down") - self._semaphore.acquire() - - def _run(): - try: - fn(*args, **kwargs) - finally: - self._semaphore.release() - - t = threading.Thread(target=_run, daemon=True) - t.start() - - def execute(self, command): - self.submit(command) - - def shutdown(self, wait: bool = True): - self._shutdown_flag = True - - -# ═══════════════════════════════════════════════════════════════ -# AsyncTask -# ═══════════════════════════════════════════════════════════════ - -class AsyncTask(Stub): - """AsyncTask — 异步任务""" - - Status = type("Status", (), {"PENDING": 0, "RUNNING": 1, "FINISHED": 2}) - - SERIAL_EXECUTOR = None - THREAD_POOL_EXECUTOR = None - - def __init__(self): - super().__init__() - self._status = AsyncTask.Status.PENDING - self._result: Any = None - self._cancelled = False - self._handler = Handler(Looper.getMainLooper()) - - def doInBackground(self, *params) -> Any: - raise NotImplementedError - - def onPreExecute(self): - pass - - def onPostExecute(self, result: Any): - pass - - def onProgressUpdate(self, *values): - pass - - def onCancelled(self, result: Any = None): - pass - - def execute(self, *params) -> "AsyncTask": - return self.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, *params) - - def executeOnExecutor(self, exec: Any, *params) -> "AsyncTask": - self._status = AsyncTask.Status.RUNNING - self.onPreExecute() - exec.submit(self._run_background, params) - return self - - def _run_background(self, params): - """在后台线程中执行 doInBackground,完成后通过 Handler 回调主线程""" - try: - self._result = self.doInBackground(*params) - except Exception as e: - self._result = e - self._status = AsyncTask.Status.FINISHED - if self._cancelled: - self._handler.post(lambda: self.onCancelled(self._result)) - else: - self._handler.post(lambda: self.onPostExecute(self._result)) - - def get(self) -> Any: - return self._result - - def getStatus(self) -> int: - return self._status - - def isCancelled(self) -> bool: - return self._cancelled - - def cancel(self, may_interrupt_if_running: bool) -> bool: - self._cancelled = True - return True - - def publishProgress(self, *values): - self._handler.post(lambda: self.onProgressUpdate(*values)) - - -# 初始化默认执行器 -AsyncTask.THREAD_POOL_EXECUTOR = _AsyncTaskExecutor(max_workers=4) -AsyncTask.SERIAL_EXECUTOR = _AsyncTaskExecutor(max_workers=1) - - -# ═══════════════════════════════════════════════════════════════ -# CountDownTimer -# ═══════════════════════════════════════════════════════════════ - -class CountDownTimer(Stub): - """CountDownTimer — 倒计时""" - - def __init__(self, millis_in_future: int, count_down_interval: int): - super().__init__() - self._millis_in_future = millis_in_future - self._interval = count_down_interval - self._cancelled = False - - def onTick(self, millis_until_finished: int): - pass - - def onFinish(self): - pass - - def start(self) -> "CountDownTimer": - self._cancelled = False - remaining = self._millis_in_future - while remaining > 0 and not self._cancelled: - self.onTick(remaining) - _time.sleep(self._interval / 1000.0) - remaining -= self._interval - if not self._cancelled: - self.onFinish() - return self - - def cancel(self): - self._cancelled = True - - -# ═══════════════════════════════════════════════════════════════ -# Messenger -# ═══════════════════════════════════════════════════════════════ - -class Messenger(Stub): - """Messenger — 跨进程消息传递""" - - def __init__(self, handler: Handler): - super().__init__() - self._handler = handler - - def send(self, message: Message): - self._handler.sendMessage(message) - - def getBinder(self): - return None - - @staticmethod - def obtain(handler: Handler) -> "Messenger": - return Messenger(handler) - - -# ═══════════════════════════════════════════════════════════════ -# PowerManager / PowerManagerWakeLock -# ═══════════════════════════════════════════════════════════════ - -class PowerManager(Stub): - """PowerManager — 电源管理""" - - ACQUIRE_CAUSES_WAKEUP = 0x10000000 - DOZE_WAKE_LOCK = 0x00000040 - DRAW_WAKE_LOCK = 0x00000080 - FULL_WAKE_LOCK = 0x0000001A - LOCATION_HARDWARE_POWER_MODE = 0x00000080 - ON_AFTER_RELEASE = 0x20000000 - PARTIAL_WAKE_LOCK = 0x00000001 - PROXIMITY_SCREEN_OFF_WAKE_LOCK = 0x00000020 - SCREEN_BRIGHT_WAKE_LOCK = 0x0000000A - SCREEN_DIM_WAKE_LOCK = 0x00000006 - WAKE_LOCK_LEVEL_MASK = 0x0000FFFF - - def __init__(self): - super().__init__() - self._is_interactive = True - self._is_power_save = False - self._is_device_idle = False - - def newWakeLock(self, level_and_flags: int, tag: str) -> "PowerManagerWakeLock": - return PowerManagerWakeLock(level_and_flags, tag) - - def isInteractive(self) -> bool: - if _USE_LINUX: - return self.isScreenOn() - return self._is_interactive - - def isScreenOn(self) -> bool: - """屏幕是否点亮 — 在 Linux 上检查 DISPLAY 环境变量""" - if _USE_LINUX: - import os as _os_mod - display = _os_mod.environ.get("DISPLAY", "") - if display: - return True - # WAYLAND_DISPLAY 作为回退 - wayland = _os_mod.environ.get("WAYLAND_DISPLAY", "") - return bool(wayland) - return self._is_interactive - - def isPowerSaveMode(self) -> bool: - """检查是否处于省电模式。读取 /sys 中的电源相关状态。""" - try: - # 检查 CPU 调速器是否为 powersave - gov_path = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor" - if _os.path.exists(gov_path): - with open(gov_path, "r") as f: - governor = f.read().strip() - if governor == "powersave": - return True - except (OSError, IOError): - pass - return self._is_power_save - - def isDeviceIdleMode(self) -> bool: - return self._is_device_idle - - def isIgnoringBatteryOptimizations(self, package_name: str) -> bool: - return True - - def isSustainedPerformanceModeSupported(self) -> bool: - return False - - def reboot(self, reason: Optional[str] = None): - """重启设备。桌面端尝试执行 reboot 命令(通常需要 root)。""" - try: - import subprocess - cmd = ["reboot"] - if reason: - cmd.append(reason) - subprocess.run(cmd, capture_output=True, timeout=5) - except Exception: - sys.stderr.write(f"[PowerManager] reboot: {reason or ''}\n") - - def goToSleep(self, time_ms: int): - """使设备进入睡眠。桌面端无实际效果。""" - self._is_interactive = False - sys.stderr.write(f"[PowerManager] goToSleep at {time_ms}\n") - - def wakeUp(self, time_ms: int): - """唤醒设备。桌面端无实际效果。""" - self._is_interactive = True - sys.stderr.write(f"[PowerManager] wakeUp at {time_ms}\n") - - def userActivity(self, when: int, no_change_lights: bool = False): - """报告用户活动事件。桌面端无实际效果。""" - self._is_interactive = True - - -class PowerManagerWakeLock(Stub): - """PowerManagerWakeLock — 唤醒锁""" - - def __init__(self, level_and_flags: int, tag: str): - super().__init__() - self._level_and_flags = level_and_flags - self._tag = tag - self._held = False - self._work_source = None - self._history_tag = None - - def acquire(self, timeout_ms: int = 0): - self._held = True - sys.stderr.write(f"[PowerManager] acquire wakelock: {self._tag}\n") - - def acquireLocked(self): - self._held = True - - def release(self, flags: int = 0): - self._held = False - sys.stderr.write(f"[PowerManager] release wakelock: {self._tag}\n") - - def isHeld(self) -> bool: - return self._held - - def setWorkSource(self, work_source: Any): - self._work_source = work_source - - def setReferenceCounted(self, value: bool): - pass - - def setHistoryTag(self, tag: str): - self._history_tag = tag - - def toString(self) -> str: - return f"WakeLock({self._tag}, held={self._held})" - - -# ═══════════════════════════════════════════════════════════════ -# Vibrator / VibratorEffect -# ═══════════════════════════════════════════════════════════════ - -class Vibrator(Stub): - """Vibrator — 振动器""" - - def __init__(self): - super().__init__() - self._ff_device: Optional[str] = None - self._vibrating = False - - @staticmethod - def _find_ff_device() -> Optional[str]: - """查找 Linux 力反馈设备。""" - try: - for name in _os.listdir("/dev/input"): - if name.startswith("event"): - path = f"/dev/input/{name}" - if _os.access(path, _os.W_OK): - return path - except (OSError, IOError): - pass - # 检查 /sys/class/leds/vibrator - try: - if _os.path.exists("/sys/class/leds/vibrator"): - return "/sys/class/leds/vibrator" - except (OSError, IOError): - pass - return None - - def vibrate(self, *args): - """振动。支持 vibrate(milliseconds) 和 vibrate(pattern, repeat)。""" - if len(args) == 1 and isinstance(args[0], (int, float)): - milliseconds = int(args[0]) - self._vibrating = True - if self._ff_device is None: - self._ff_device = self._find_ff_device() - if self._ff_device and self._ff_device.startswith("/sys/class/leds"): - try: - with open(_os.path.join(self._ff_device, "brightness"), "w") as f: - f.write("1") - import threading - def _stop(): - _time.sleep(milliseconds / 1000.0) - try: - with open(_os.path.join(self._ff_device, "brightness"), "w") as f: - f.write("0") - except (OSError, IOError): - pass - self._vibrating = False - threading.Thread(target=_stop, daemon=True).start() - return - except (OSError, IOError): - pass - sys.stderr.write(f"[Vibrator] vibrate: {milliseconds}ms\n") - elif len(args) == 2 and isinstance(args[0], list): - pattern = args[0] - repeat = args[1] - if not pattern: - return - self._vibrating = True - import threading - def _run_pattern(): - idx = 0 - while True: - duration = pattern[idx % len(pattern)] - if idx % 2 == 0: - _time.sleep(duration / 1000.0) - else: - self.vibrate(duration) - _time.sleep(duration / 1000.0) - idx += 1 - if repeat < 0 or idx >= len(pattern): - if repeat < 0: - continue - break - self._vibrating = False - threading.Thread(target=_run_pattern, daemon=True).start() - sys.stderr.write(f"[Vibrator] vibrate pattern: {pattern}, repeat={repeat}\n") - - def cancel(self): - """取消振动。""" - self._vibrating = False - if self._ff_device and self._ff_device.startswith("/sys/class/leds"): - try: - with open(_os.path.join(self._ff_device, "brightness"), "w") as f: - f.write("0") - except (OSError, IOError): - pass - sys.stderr.write("[Vibrator] cancel\n") - - def hasVibrator(self) -> bool: - """检查是否有振动器硬件。""" - return self._find_ff_device() is not None - - def hasAmplitudeControl(self) -> bool: - """检查是否支持振幅控制。""" - try: - if _os.path.exists("/sys/class/leds/vibrator/max_brightness"): - with open("/sys/class/leds/vibrator/max_brightness", "r") as f: - return int(f.read().strip()) > 1 - except (OSError, IOError, ValueError): - pass - return False - - def areEffectsSupported(self, effect_ids: List[int]) -> List[int]: - return [0] * len(effect_ids) - - def areAllEffectsSupported(self, effect_ids: List[int]) -> List[int]: - return [0] * len(effect_ids) - - -class VibratorEffect(Stub): - """VibratorEffect — 振动效果""" - - EFFECT_CLICK = 0 - EFFECT_DOUBLE_CLICK = 1 - EFFECT_TICK = 2 - EFFECT_THUD = 3 - EFFECT_POP = 4 - EFFECT_HEAVY_CLICK = 5 - - def __init__(self, effect_id: int = 0): - super().__init__() - self._effect_id = effect_id - self._timings: Optional[List[int]] = None - self._amplitudes: Optional[List[int]] = None - self._repeat = -1 - - @staticmethod - def createOneShot(milliseconds: int, amplitude: int) -> "VibratorEffect": - """创建单次振动效果。""" - effect = VibratorEffect(0) - effect._timings = [0, milliseconds] - effect._amplitudes = [0, amplitude] - effect._repeat = -1 - return effect - - @staticmethod - def createWaveform(pattern: List[int], repeat: int) -> "VibratorEffect": - """创建波形振动效果。""" - effect = VibratorEffect(0) - effect._timings = list(pattern) - effect._amplitudes = [255 if i % 2 == 1 else 0 for i in range(len(pattern))] - effect._repeat = repeat - return effect - - @staticmethod - def createPredefined(effect_id: int) -> "VibratorEffect": - """创建预定义振动效果。""" - return VibratorEffect(effect_id) - - -# ═══════════════════════════════════════════════════════════════ -# BatteryManager -# ═══════════════════════════════════════════════════════════════ - -class BatteryManager(Stub): - """BatteryManager — 电池管理""" - - BATTERY_PROPERTY_CHARGE_COUNTER = 1 - BATTERY_PROPERTY_CURRENT_NOW = 2 - BATTERY_PROPERTY_CURRENT_AVERAGE = 3 - BATTERY_PROPERTY_CAPACITY = 4 - BATTERY_PROPERTY_STATUS = 5 - BATTERY_PROPERTY_ENERGY_COUNTER = 6 - BATTERY_PROPERTY_BATTERY_NOW = 7 - BATTERY_PROPERTY_BATTERY_LOW = 8 - - BATTERY_STATUS_UNKNOWN = 1 - BATTERY_STATUS_CHARGING = 2 - BATTERY_STATUS_DISCHARGING = 3 - BATTERY_STATUS_NOT_CHARGING = 4 - BATTERY_STATUS_FULL = 5 - - BATTERY_HEALTH_UNKNOWN = 1 - BATTERY_HEALTH_GOOD = 2 - BATTERY_HEALTH_OVERHEAT = 3 - BATTERY_HEALTH_DEAD = 4 - BATTERY_HEALTH_OVER_VOLTAGE = 5 - BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6 - BATTERY_HEALTH_COLD = 7 - - EXTRA_STATUS = "status" - EXTRA_HEALTH = "health" - EXTRA_PRESENT = "present" - EXTRA_LEVEL = "level" - EXTRA_SCALE = "scale" - EXTRA_ICON_SMALL = "icon-small" - EXTRA_PLUGGED = "plugged" - EXTRA_VOLTAGE = "voltage" - EXTRA_TEMPERATURE = "temperature" - EXTRA_TECHNOLOGY = "technology" - EXTRA_BATTERY_LOW = "battery_low" - EXTRA_CHARGE_COUNTER = "charge_counter" - - BATTERY_PLUGGED_AC = 1 - BATTERY_PLUGGED_USB = 2 - BATTERY_PLUGGED_WIRELESS = 4 - - def __init__(self): - super().__init__() - self._capacity = 85 - self._status = self.BATTERY_STATUS_CHARGING - self._health = self.BATTERY_HEALTH_GOOD - self._present = True - self._level = 85 - self._plugged = self.BATTERY_PLUGGED_AC - self._voltage = 4200 - self._temperature = 250 - self._technology = "Li-ion" - self._charge_counter = 3500000 - self._current_now = -500000 - self._current_avg = -450000 - self._energy_counter = 15000000 - if _USE_LINUX: - self._refresh_from_linux() - - _LINUX_STATUS_MAP = { - "charging": 2, # BATTERY_STATUS_CHARGING - "discharging": 3, # BATTERY_STATUS_DISCHARGING - "not charging": 4, # BATTERY_STATUS_NOT_CHARGING - "full": 5, # BATTERY_STATUS_FULL - "unknown": 1, # BATTERY_STATUS_UNKNOWN - } - - def _get_linux_battery_info(self) -> dict: - """从 LinuxPower 获取真实电池信息""" - try: - return LinuxPower.get_battery_info() or {} - except Exception: - return {} - - def _refresh_from_linux(self): - """用 Linux 真实电池信息刷新内部状态""" - info = self._get_linux_battery_info() - if not info: - return - self._present = bool(info.get("present", False)) - level = info.get("level", -1) - if self._present and isinstance(level, (int, float)) and level >= 0: - self._level = int(level) - self._capacity = int(level) - else: - # 无电池或电量未知 - self._level = -1 - self._capacity = -1 - status_str = str(info.get("status", "unknown")).lower() - self._status = self._LINUX_STATUS_MAP.get(status_str, self.BATTERY_STATUS_UNKNOWN) - voltage = info.get("voltage", 0) - if isinstance(voltage, (int, float)): - # LinuxPower 返回伏特, Android 期望毫伏 - self._voltage = int(voltage * 1000) - temperature = info.get("temperature", 0) - if isinstance(temperature, (int, float)): - # LinuxPower 返回摄氏度, Android 期望十分之一度 - self._temperature = int(temperature * 10) - self._technology = info.get("technology", "") or "Li-ion" - # 充电状态决定 plugged - if self._status == self.BATTERY_STATUS_CHARGING: - self._plugged = self.BATTERY_PLUGGED_AC - else: - self._plugged = 0 - - def getIntProperty(self, prop_id: int) -> int: - if _USE_LINUX: - self._refresh_from_linux() - if prop_id == self.BATTERY_PROPERTY_CAPACITY: - return self._capacity - if prop_id == self.BATTERY_PROPERTY_STATUS: - return self._status - if prop_id == self.BATTERY_PROPERTY_CHARGE_COUNTER: - return self._charge_counter - if prop_id == self.BATTERY_PROPERTY_CURRENT_NOW: - return self._current_now - if prop_id == self.BATTERY_PROPERTY_CURRENT_AVERAGE: - return self._current_avg - if prop_id == self.BATTERY_PROPERTY_ENERGY_COUNTER: - return self._energy_counter - return -1 - - def getLongProperty(self, prop_id: int) -> int: - return self.getIntProperty(prop_id) - - def isCharging(self) -> bool: - if _USE_LINUX: - self._refresh_from_linux() - return self._status == self.BATTERY_STATUS_CHARGING - - def isPresent(self) -> bool: - """电池是否存在""" - if _USE_LINUX: - self._refresh_from_linux() - return self._present - - def getLevel(self) -> int: - """电池电量 (0-100, -1 表示未知)""" - if _USE_LINUX: - self._refresh_from_linux() - return self._level - - def getVoltage(self) -> int: - """电池电压 (毫伏)""" - if _USE_LINUX: - self._refresh_from_linux() - return self._voltage - - def getTemperature(self) -> int: - """电池温度 (十分之一摄氏度)""" - if _USE_LINUX: - self._refresh_from_linux() - return self._temperature - - def getTechnology(self) -> str: - """电池技术""" - if _USE_LINUX: - self._refresh_from_linux() - return self._technology - - def getPlugType(self) -> int: - """充电类型""" - if _USE_LINUX: - self._refresh_from_linux() - return self._plugged - - def getStatus(self) -> int: - """电池状态""" - if _USE_LINUX: - self._refresh_from_linux() - return self._status - - def computeChargeTimeRemaining(self) -> int: - if _USE_LINUX: - # 无电池或已充满时返回 -1 - if not self._present or self._status == self.BATTERY_STATUS_FULL: - return -1 - return 1800000 # 30 minutes - - def toString(self) -> str: - return f"BatteryManager(level={self._level}%, charging={self.isCharging()})" - - -# ═══════════════════════════════════════════════════════════════ -# 异常类 -# ═══════════════════════════════════════════════════════════════ - -class NetworkOnMainThreadException(RuntimeError): - """网络操作在主线程上执行""" - pass - - -class TransactionTooLargeException(RuntimeError): - """事务数据太大""" - pass - - -class OperationCanceledException(RuntimeError): - """操作被取消""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# 其他工具类 -# ═══════════════════════════════════════════════════════════════ - -class ConditionVariable(Stub): - """ConditionVariable — 条件变量""" - - def __init__(self): - super().__init__() - self._cond = threading.Condition() - self._open = False - - def block(self, timeout_ms: int = 0): - with self._cond: - if timeout_ms <= 0: - while not self._open: - self._cond.wait() - else: - if not self._open: - self._cond.wait(timeout_ms / 1000.0) - - def open(self): - with self._cond: - self._open = True - self._cond.notify_all() - - def close(self): - with self._cond: - self._open = False - - -class Semaphore(Stub): - """Semaphore — 信号量""" - - def __init__(self, permits: int): - super().__init__() - self._sem = threading.Semaphore(permits) - - def acquire(self): - self._sem.acquire() - - def tryAcquire(self) -> bool: - return self._sem.acquire(blocking=False) - - def release(self): - self._sem.release() - - def availablePermits(self) -> int: - return self._sem._value - - -class TokenWatcher(Stub): - """TokenWatcher — 令牌监视器""" - - def __init__(self, handler: Handler, tag: str): - super().__init__() - self._handler = handler - self._tag = tag - self._tokens: Dict[int, str] = {} - - def acquire(self, token: Any, tag: str): - """获取令牌。""" - self._tokens[id(token)] = tag - if len(self._tokens) == 1: - self.acquired() - - def release(self, token: Any): - """释放令牌。""" - self._tokens.pop(id(token), None) - if not self._tokens: - self.released() - - def isAcquired(self) -> bool: - """检查是否有令牌被持有。""" - return len(self._tokens) > 0 - - def acquired(self): - """当第一个令牌被获取时调用。子类可重写。""" - pass - - def released(self): - """当所有令牌被释放时调用。子类可重写。""" - pass - - def dump(self): - """转储令牌信息。""" - for tid, tag in self._tokens.items(): - sys.stderr.write(f"[TokenWatcher] {self._tag}: token={tid}, tag={tag}\n") - - -class WorkSource(Stub): - """WorkSource — 工作来源""" - - def __init__(self, uid: int = 0): - super().__init__() - self._uids: List[int] = [uid] if uid else [] - - def add(self, uid: int): - if uid not in self._uids: - self._uids.append(uid) - - def remove(self, uid: int): - if uid in self._uids: - self._uids.remove(uid) - - def get(self, index: int) -> int: - if 0 <= index < len(self._uids): - return self._uids[index] - return 0 - - def size(self) -> int: - return len(self._uids) - - def clear(self): - self._uids.clear() - - def set(self, ws: "WorkSource"): - if ws: - self._uids = list(ws._uids) - - def toString(self) -> str: - return f"WorkSource({self._uids})" - - -class UserHandle(Stub): - """UserHandle — 用户句柄""" - - USER_ALL = -1 - USER_CURRENT = -2 - USER_CURRENT_OR_SELF = -3 - USER_NULL = -10000 - USER_OWNER = 0 - USER_SYSTEM = 0 - - def __init__(self, user_id: int = 0): - super().__init__() - self._user_id = user_id - - @staticmethod - def myUserId() -> int: - return 0 - - @staticmethod - def getUserHandleForUid(uid: int) -> "UserHandle": - return UserHandle(uid // 100000) - - @staticmethod - def getUserId(uid: int) -> int: - return uid // 100000 - - @staticmethod - def getUid(user_id: int, app_id: int) -> int: - return user_id * 100000 + app_id - - @staticmethod - def getAppId(uid: int) -> int: - return uid % 100000 - - def getIdentifier(self) -> int: - return self._user_id - - def hashCode(self) -> int: - return self._user_id - - def toString(self) -> str: - return f"UserHandle({self._user_id})" - - -class DropBoxManager(Stub): - """DropBoxManager — 系统日志收集""" - - IS_EMPTY = 0 - IS_TEXT = 1 - IS_GZIPPED = 2 - - class Entry(Stub): - def __init__(self, tag: str, milliseconds: int): - super().__init__() - self._tag = tag - self._millis = milliseconds - self._text = "" - - def getTag(self) -> str: - return self._tag - - def getTimeMillis(self) -> int: - return self._millis - - def getText(self, max_bytes: int) -> Optional[str]: - return self._text - - def getFlags(self, max_bytes: int) -> int: - return DropBoxManager.IS_TEXT - - def close(self): - pass - - def __init__(self): - super().__init__() - self._entries: List[DropBoxManager.Entry] = [] - - def addText(self, tag: str, text: str): - entry = DropBoxManager.Entry(tag, int(_time.time() * 1000)) - entry._text = text - self._entries.append(entry) - - def getNextEntry(self, tag: str, milliseconds: int) -> Optional[Entry]: - for entry in self._entries: - if entry.getTimeMillis() > milliseconds: - if not tag or entry.getTag() == tag: - return entry - return None - - def dump(self, pw: Any, prefix: str): - pass - - -class FileObserver(Stub): - """FileObserver — 文件监听""" - - ACCESS = 0x00000001 - MODIFY = 0x00000002 - ATTRIB = 0x00000004 - CLOSE_WRITE = 0x00000008 - CLOSE_NOWRITE = 0x00000010 - OPEN = 0x00000020 - MOVED_FROM = 0x00000040 - MOVED_TO = 0x00000080 - CREATE = 0x00000100 - DELETE = 0x00000200 - DELETE_SELF = 0x00000400 - MOVE_SELF = 0x00000800 - ALL_EVENTS = 0x00000FFF - - def __init__(self, path: str): - super().__init__() - self._path = path - self._watching = False - self._thread = None - self._snapshot: Dict[str, float] = {} - - def startWatching(self): - """开始监听文件变化。使用轮询方式检测文件系统事件。""" - if self._watching: - return - self._watching = True - self._snapshot = self._take_snapshot() - self._thread = threading.Thread(target=self._poll_loop, daemon=True) - self._thread.start() - - def _take_snapshot(self) -> Dict[str, float]: - """获取当前目录的文件快照(文件名 -> 修改时间)。""" - snapshot: Dict[str, float] = {} - try: - if _os.path.isdir(self._path): - for name in _os.listdir(self._path): - full = _os.path.join(self._path, name) - try: - snapshot[name] = _os.path.getmtime(full) - except OSError: - pass - elif _os.path.exists(self._path): - try: - snapshot[self._path] = _os.path.getmtime(self._path) - except OSError: - pass - except OSError: - pass - return snapshot - - def _poll_loop(self): - """轮询检测文件变化并触发事件。""" - while self._watching: - _time.sleep(0.5) - try: - current = self._take_snapshot() - # 检测新建和修改 - for name, mtime in current.items(): - if name not in self._snapshot: - self.onEvent(FileObserver.CREATE, name) - elif mtime != self._snapshot[name]: - self.onEvent(FileObserver.MODIFY, name) - # 检测删除 - for name in list(self._snapshot.keys()): - if name not in current: - self.onEvent(FileObserver.DELETE, name) - self._snapshot = current - except Exception: - pass - - def stopWatching(self): - """停止监听。""" - self._watching = False - if self._thread and self._thread.is_alive(): - self._thread.join(timeout=1.0) - self._thread = None - - def onEvent(self, event: int, path: Optional[str]): - """文件事件回调,子类可重写。""" - pass - - def finalize(self): - """清理资源。""" - self.stopWatching() - - -class FileUtils(Stub): - """FileUtils — 文件工具""" - - @staticmethod - def readFileToString(path): - try: - with open(path, "r", encoding="utf-8") as f: - return f.read() - except Exception: - return "" - - @staticmethod - def stringToFile(path, content): - try: - with open(path, "w", encoding="utf-8") as f: - f.write(content) - return True - except Exception: - return False - - @staticmethod - def copyFile(src, dst): - try: - import shutil - shutil.copy2(src, dst) - return True - except Exception: - return False - - @staticmethod - def deleteDirectory(path): - try: - import shutil - shutil.rmtree(path) - return True - except Exception: - return False - - @staticmethod - def deleteContents(path): - try: - import shutil - for entry in _os.listdir(path): - full = _os.path.join(path, entry) - if _os.path.isdir(full): - shutil.rmtree(full) - else: - _os.remove(full) - return True - except Exception: - return False - - @staticmethod - def deleteOlderFiles(dir_: Any, min_count: int, min_age_ms: int): - """删除目录中较旧的文件,但保留至少 min_count 个文件。 - min_age_ms 为文件的最小年龄(毫秒),低于此年龄的文件不会被删除。""" - try: - if not _os.path.isdir(dir_): - return - entries = [] - for name in _os.listdir(dir_): - full = _os.path.join(dir_, name) - if _os.path.isfile(full): - try: - mtime = _os.path.getmtime(full) - entries.append((full, mtime)) - except OSError: - pass - # 按修改时间排序(最新在前) - entries.sort(key=lambda x: x[1], reverse=True) - now = _time.time() - min_age_s = min_age_ms / 1000.0 - # 保留最新的 min_count 个文件,删除超过 min_age 的旧文件 - for i, (path, mtime) in enumerate(entries): - if i < min_count: - continue - if (now - mtime) >= min_age_s: - try: - _os.remove(path) - except OSError: - pass - except Exception: - pass - - -class PatternMatcher(Stub): - """PatternMatcher — 模式匹配器""" - - PATTERN_LITERAL = 0 - PATTERN_PREFIX = 1 - PATTERN_SIMPLE_GLOB = 2 - PATTERN_ADVANCED_GLOB = 3 - PATTERN_SUFFIX = 4 - - def __init__(self, pattern: str, type_: int): - super().__init__() - self._pattern = pattern - self._type = type_ - - def getPath(self) -> str: - return self._pattern - - def getType(self) -> int: - return self._type - - def match(self, str_: str) -> bool: - if self._type == self.PATTERN_LITERAL: - return str_ == self._pattern - if self._type == self.PATTERN_PREFIX: - return str_.startswith(self._pattern) - if self._type == self.PATTERN_SUFFIX: - return str_.endswith(self._pattern) - if self._type == self.PATTERN_SIMPLE_GLOB: - import fnmatch - return fnmatch.fnmatch(str_, self._pattern) - return str_ == self._pattern - - def toString(self) -> str: - return f"PatternMatcher({self._pattern}, type={self._type})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Bundle", - "BaseBundle", - "PersistableBundle", - "Handler", - "Looper", - "Message", - "HandlerThread", - "PowerManager", - "PowerManagerWakeLock", - "Vibrator", - "VibratorEffect", - "Build", - "SystemClock", - "Environment", - "Parcel", - "Parcelable", - "Process", - "AsyncTask", - "CountDownTimer", - "Messenger", - "BatteryManager", - "PatternMatcher", - "ConditionVariable", - "Semaphore", - "TokenWatcher", - "WorkSource", - "UserHandle", - "DropBoxManager", - "FileObserver", - "FileUtils", - "NetworkOnMainThreadException", - "TransactionTooLargeException", - "OperationCanceledException", -] \ No newline at end of file diff --git a/android_pyapi/android/os_health.py b/android_pyapi/android/os_health.py deleted file mode 100644 index 12ac297..0000000 --- a/android_pyapi/android/os_health.py +++ /dev/null @@ -1,215 +0,0 @@ -""" -Android os.health 组件 — HealthStats, HealthStatsWriter, UidHealthStats 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -from typing import Any, Dict, List, Optional - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# UidHealthStats -# ═══════════════════════════════════════════════════════════════ - -class UidHealthStats(Stub): - """UidHealthStats — UID 健康统计常量""" - - STATISTICS_SORTED_KEYS_COUNT = 0x10001 - - MEASUREMENT_REALTIME_BATTERY_MS = 0x20001 - MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS = 0x20002 - MEASUREMENT_FOREGROUND_APP_MS = 0x20003 - MEASUREMENT_BACKGROUND_PIDS_MEASUREMENT = 0x20004 - MEASUREMENT_REALTIME_BATTERY_MS_UNPLUGGED = 0x20005 - MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS_UNPLUGGED = 0x20006 - - TIMER_FLASHLIGHT = 0x30001 - TIMER_CAMERA = 0x30002 - TIMER_VIDEO = 0x30003 - TIMER_AUDIO = 0x30004 - TIMER_WIFI = 0x30005 - TIMER_BLUETOOTH = 0x30006 - TIMER_GPS = 0x30007 - TIMER_SENSORS = 0x30008 - TIMER_MOBILE_RADIO_ACTIVE = 0x30009 - TIMER_PARTIAL_WAKELOCK = 0x3000A - TIMER_WIFI_SCAN = 0x3000B - TIMER_WIFI_FULL_LOCK = 0x3000C - TIMER_WIFI_MULTICAST = 0x3000D - TIMER_WIFI_RADIO_ACTIVE = 0x3000E - TIMER_AUDIO_JACK = 0x3000F - TIMER_VIDEO_GPS = 0x30010 - TIMER_VIDEO_TURBO = 0x30011 - TIMER_VIDEO_LOCK = 0x30012 - - TIMER_OVERCOUNT = 0x40001 - - MEASUREMENT_OTHER_USER_ACTIVITY_COUNT = 0x50001 - MEASUREMENT_BLUETOOTH_ACTIVITY_COUNT = 0x50002 - MEASUREMENT_WIFI_ACTIVITY_COUNT = 0x50003 - MEASUREMENT_MOBILE_RADIO_ACTIVITY_COUNT = 0x50004 - - TIMER_WIFI_RADIO_ACTIVITY = 0x60001 - TIMER_BLUETOOTH_RADIO_ACTIVITY = 0x60002 - TIMER_MOBILE_RADIO_ACTIVITY = 0x60003 - - MEASUREMENT_WIFI_IDLE_MS = 0x70001 - MEASUREMENT_WIFI_RX_MS = 0x70002 - MEASUREMENT_WIFI_TX_MS = 0x70003 - MEASUREMENT_BLUETOOTH_IDLE_MS = 0x70004 - MEASUREMENT_BLUETOOTH_RX_MS = 0x70005 - MEASUREMENT_BLUETOOTH_TX_MS = 0x70006 - MEASUREMENT_MOBILE_RADIO_IDLE_MS = 0x70007 - MEASUREMENT_MOBILE_RADIO_RX_MS = 0x70008 - MEASUREMENT_MOBILE_RADIO_TX_MS = 0x70009 - - TIMER_WIFI_RADIO_SCAN = 0x80001 - TIMER_BLUETOOTH_RADIO_SCAN = 0x80002 - TIMER_MOBILE_RADIO_SCAN = 0x80003 - - MEASUREMENT_WIFI_RADIO_ACTIVE_MS = 0x90001 - MEASUREMENT_BLUETOOTH_RADIO_ACTIVE_MS = 0x90002 - MEASUREMENT_MOBILE_RADIO_ACTIVE_MS = 0x90003 - - -# ═══════════════════════════════════════════════════════════════ -# PidHealthStats -# ═══════════════════════════════════════════════════════════════ - -class PidHealthStats(Stub): - """PidHealthStats — 进程健康统计常量""" - - MEASUREMENT_WAKE_NESTING_COUNT = 0x20001 - MEASUREMENT_WAKE_SUM_MS = 0x20002 - MEASUREMENT_WAKE_START_MS = 0x20003 - - -# ═══════════════════════════════════════════════════════════════ -# ProcessHealthStats -# ═══════════════════════════════════════════════════════════════ - -class ProcessHealthStats(Stub): - """ProcessHealthStats — 进程健康统计常量""" - - MEASUREMENT_USER_TIME_MS = 0x20001 - MEASUREMENT_SYSTEM_TIME_MS = 0x20002 - MEASUREMENT_STARTS_COUNT = 0x20003 - MEASUREMENT_CRASHES_COUNT = 0x20004 - MEASUREMENT_ANR_COUNT = 0x20005 - MEASUREMENT_FOREGROUND_MS = 0x20006 - - -# ═══════════════════════════════════════════════════════════════ -# PackageHealthStats -# ═══════════════════════════════════════════════════════════════ - -class PackageHealthStats(Stub): - """PackageHealthStats — 包健康统计常量""" - - STATISTICS_SORTED_BY_TYPE = 0x10001 - MEASUREMENTS_WAKEUP_ALARMS_COUNT = 0x20001 - TIMERS_SERVICES = 0x30001 - STATISTICS_SERVICES = 0x40001 - - -# ═══════════════════════════════════════════════════════════════ -# ServiceHealthStats -# ═══════════════════════════════════════════════════════════════ - -class ServiceHealthStats(Stub): - """ServiceHealthStats — 服务健康统计常量""" - - MEASUREMENT_START_SERVICE_COUNT = 0x20001 - MEASUREMENT_LAUNCH_COUNT = 0x20002 - MEASUREMENT_CRASH_COUNT = 0x20003 - - -# ═══════════════════════════════════════════════════════════════ -# HealthStats -# ═══════════════════════════════════════════════════════════════ - -class HealthStats(Stub): - """HealthStats — 健康统计数据""" - - def __init__(self, data: Optional[Dict[int, Any]] = None): - super().__init__() - self._data: Dict[int, Any] = data or {} - - def getMeasurement(self, key: int) -> int: - return self._data.get(key, 0) - - def getTimer(self, key: int) -> Any: - return self._data.get(key) - - def getTimers(self, key: int) -> List[Any]: - return self._data.get(key, []) - - def getStats(self, key: int) -> "HealthStats": - return self._data.get(key, HealthStats()) - - def getStatsArray(self, key: int) -> List["HealthStats"]: - return self._data.get(key, []) - - def hasKey(self, key: int) -> bool: - return key in self._data - - def getKeys(self) -> List[int]: - return list(self._data.keys()) - - def toString(self) -> str: - return f"HealthStats{{{len(self._data)} entries}}" - - -# ═══════════════════════════════════════════════════════════════ -# HealthStatsWriter -# ═══════════════════════════════════════════════════════════════ - -class HealthStatsWriter(Stub): - """HealthStatsWriter — 健康统计写入器""" - - def __init__(self, keys: Optional[List[int]] = None): - super().__init__() - self._data: Dict[int, Any] = {} - - def addMeasurement(self, key: int, value: int): - self._data[key] = value - - def addTimer(self, key: int, count: int, time_ms: int): - self._data[key] = (count, time_ms) - - def addTimers(self, key: int, values: List[Any]): - self._data[key] = values - - def addStats(self, key: int, value: HealthStats): - self._data[key] = value - - def addStatsArray(self, key: int, values: List[HealthStats]): - self._data[key] = values - - def toHealthStats(self) -> HealthStats: - return HealthStats(dict(self._data)) - - def toString(self) -> str: - return f"HealthStatsWriter@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "HealthStats", - "HealthStatsWriter", - "UidHealthStats", - "PidHealthStats", - "ProcessHealthStats", - "PackageHealthStats", - "ServiceHealthStats", -] \ No newline at end of file diff --git a/android_pyapi/android/os_power.py b/android_pyapi/android/os_power.py deleted file mode 100644 index 5ba22e7..0000000 --- a/android_pyapi/android/os_power.py +++ /dev/null @@ -1,902 +0,0 @@ -""" -Android Power API — PowerManager, PowerProfile, BatteryStats, BatteryManager, BatterySaverController, etc. -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import sys -import time -import json -import platform -import threading -from datetime import datetime, timedelta -from typing import Any, Dict, List, Optional, Set, Tuple, Callable - -from .._core import Stub, AndroidOnlyError, _app_data_dir - -from ..java.lang import RuntimeException -from .os import BatteryManager as _BaseBatteryManager -from .os import PowerManager as _BasePowerManager - -# Linux 真实实现导入 -try: - from .._linux_impl import IS_LINUX, LinuxPower, LinuxProcess, HAS_PSUTIL as _HAS_PSUTIL - _USE_LINUX = IS_LINUX -except ImportError: - _USE_LINUX = False - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -# BATTERY_* 常量 -BATTERY_STATUS_UNKNOWN = 1 -BATTERY_STATUS_CHARGING = 2 -BATTERY_STATUS_DISCHARGING = 3 -BATTERY_STATUS_NOT_CHARGING = 4 -BATTERY_STATUS_FULL = 5 -BATTERY_STATUS_INVALID = 6 - -BATTERY_HEALTH_UNKNOWN = 1 -BATTERY_HEALTH_GOOD = 2 -BATTERY_HEALTH_OVERHEAT = 3 -BATTERY_HEALTH_DEAD = 4 -BATTERY_HEALTH_OVER_VOLTAGE = 5 -BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6 -BATTERY_HEALTH_COLD = 7 - -BATTERY_PLUGGED_AC = 1 -BATTERY_PLUGGED_USB = 2 -BATTERY_PLUGGED_WIRELESS = 4 -BATTERY_PLUGGED_DOCK = 8 - -BATTERY_PROPERTY_CAPACITY = 0 -BATTERY_PROPERTY_CHARGE_COUNTER = 1 -BATTERY_PROPERTY_CURRENT_NOW = 2 -BATTERY_PROPERTY_CURRENT_AVERAGE = 3 -BATTERY_PROPERTY_ENERGY_COUNTER = 4 -BATTERY_PROPERTY_STATUS = 5 -BATTERY_PROPERTY_TEMPERATURE = 6 -BATTERY_PROPERTY_VOLTAGE = 7 -BATTERY_PROPERTY_CHARGE_TIME_REMAINING = 8 -BATTERY_PROPERTY_DISCHARGE_TIME_REMAINING = 9 - -# POWER_* 常量 -POWER_SAVE_MODE_INIT_TRIGGER_TIMEOUT = 0 -POWER_SAVE_MODE_INIT_TRIGGER_DYNAMIC = 1 -POWER_SAVE_MODE_INIT_TRIGGER_PERCENTAGE = 2 -POWER_SAVE_MODE_INIT_TRIGGER_MANUAL = 3 - -SCREEN_BRIGHT_MODE_AUTOMATIC = 1 -SCREEN_BRIGHT_MODE_MANUAL = 0 - -# THERMAL_STATUS 常量 -THERMAL_STATUS_NONE = 0 -THERMAL_STATUS_LIGHT = 1 -THERMAL_STATUS_MODERATE = 2 -THERMAL_STATUS_SEVERE = 3 -THERMAL_STATUS_CRITICAL = 4 -THERMAL_STATUS_EMERGENCY = 5 -THERMAL_STATUS_SHUTDOWN = 6 - -# WAKE_LOCK 级别 -PARTIAL_WAKE_LOCK = 1 -FULL_WAKE_LOCK = 26 -SCREEN_DIM_WAKE_LOCK = 6 -SCREEN_BRIGHT_WAKE_LOCK = 10 -PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32 -DOZE_WAKE_LOCK = 64 -DRAW_WAKE_LOCK = 128 - -# ACQUIRE 标志 -ACQUIRE_CAUSES_WAKEUP = 0x10000000 -ON_AFTER_RELEASE = 0x20000000 -WAKE_LOCK_LEVEL_MASK = 0x0000FFFF - - -# ═══════════════════════════════════════════════════════════════ -# PowerSaveState -# ═══════════════════════════════════════════════════════════════ - -class PowerSaveState(Stub): - """PowerSaveState — 省电状态""" - - def __init__(self): - super().__init__() - self._global_battery_saver_enabled = False - self._location_mode = 0 - self._sound_trigger_mode = 0 - self._brightness_factor = 0.0 - self._is_interactive = True - self._screen_brightness_mode = SCREEN_BRIGHT_MODE_MANUAL - self._screen_dim_enabled = False - self._power_save_mode = False - self._dynamic_power_save_enabled = False - self._full_backup_enabled = True - self._data_saver_enabled = False - - def getGlobalBatterySaverEnabled(self) -> bool: - return self._global_battery_saver_enabled - - def getLocationMode(self) -> int: - return self._location_mode - - def getSoundTriggerMode(self) -> int: - return self._sound_trigger_mode - - def getBrightnessFactor(self) -> float: - return self._brightness_factor - - def isInteractive(self) -> bool: - return self._is_interactive - - def getScreenBrightnessMode(self) -> int: - return self._screen_brightness_mode - - def isScreenDimEnabled(self) -> bool: - return self._screen_dim_enabled - - def isPowerSaveMode(self) -> bool: - return self._power_save_mode - - def isDynamicPowerSaveEnabled(self) -> bool: - return self._dynamic_power_save_enabled - - def isFullBackupEnabled(self) -> bool: - return self._full_backup_enabled - - def isDataSaverEnabled(self) -> bool: - return self._data_saver_enabled - - def toString(self) -> str: - return ( - f"PowerSaveState(battery_saver={self._power_save_mode}, " - f"interactive={self._is_interactive})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# PowerProfile -# ═══════════════════════════════════════════════════════════════ - -class PowerProfile(Stub): - """PowerProfile — 电源配置""" - - POWER_WIFI_ON = "wifi.on" - POWER_WIFI_SCAN = "wifi.scan" - POWER_WIFI_ACTIVE = "wifi.active" - POWER_BLUETOOTH_ON = "bluetooth.on" - POWER_BLUETOOTH_ACTIVE = "bluetooth.active" - POWER_BLUETOOTH_AT_CMD = "bluetooth.at" - POWER_SCREEN_ON = "screen.on" - POWER_SCREEN_FULL = "screen.full" - POWER_CPU_IDLE = "cpu.idle" - POWER_CPU_AWAKE = "cpu.awake" - POWER_CPU_ACTIVE = "cpu.active" - POWER_AMBIENT_DISPLAY = "ambient.on" - POWER_GPS_ON = "gps.on" - POWER_CAMERA_ON = "camera.on" - POWER_FLASHLIGHT_ON = "flashlight.on" - POWER_MICROPHONE_ON = "microphone.on" - POWER_RADIO_ON = "radio.on" - POWER_RADIO_SCANNING = "radio.scanning" - POWER_RADIO_ACTIVE = "radio.active" - POWER_AUDIO_ON = "audio.on" - POWER_VIDEO_ON = "video.on" - POWER_VIBRATION_ON = "vibration.on" - POWER_NFC_ON = "nfc.on" - POWER_NFC_ACTIVE = "nfc.active" - POWER_USB_ON = "usb.on" - POWER_MODEM_CONTROLLER_SLEEP = "modem.controller.sleep" - POWER_MODEM_CONTROLLER_IDLE = "modem.controller.idle" - POWER_MODEM_CONTROLLER_RX = "modem.controller.rx" - POWER_MODEM_CONTROLLER_TX = "modem.controller.tx" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._profile_data: Dict[str, float] = { - PowerProfile.POWER_WIFI_ON: 0.1, - PowerProfile.POWER_WIFI_SCAN: 0.5, - PowerProfile.POWER_WIFI_ACTIVE: 1.0, - PowerProfile.POWER_SCREEN_ON: 0.5, - PowerProfile.POWER_SCREEN_FULL: 2.0, - PowerProfile.POWER_CPU_IDLE: 0.01, - PowerProfile.POWER_CPU_AWAKE: 0.1, - PowerProfile.POWER_CPU_ACTIVE: 1.0, - PowerProfile.POWER_GPS_ON: 1.5, - PowerProfile.POWER_BLUETOOTH_ON: 0.2, - PowerProfile.POWER_BLUETOOTH_ACTIVE: 0.5, - } - self._battery_capacity = 4000.0 - self._average_battery_lifespan = 24.0 - - def getAveragePower(self, type_: str) -> float: - return self._profile_data.get(type_, 0.0) - - def getAveragePowerOrDefault(self, type_: str, defaultValue: float) -> float: - return self._profile_data.get(type_, defaultValue) - - def getBatteryCapacity(self) -> float: - return self._battery_capacity - - def setBatteryCapacity(self, capacityMah: float) -> None: - self._battery_capacity = capacityMah - - def getAverageBatteryLifespan(self) -> float: - return self._average_battery_lifespan - - def toString(self) -> str: - return f"PowerProfile(battery={self._battery_capacity}mAh)" - - -# ═══════════════════════════════════════════════════════════════ -# BatteryStats -# ═══════════════════════════════════════════════════════════════ - -class BatteryStats(Stub): - """BatteryStats — 电池统计数据""" - - # 统计常量 - STATS_CURRENT = 0 - STATS_SINCE_CHARGED = 1 - STATS_SINCE_UNPLUGGED = 2 - STATS_UNPLUGGED = 3 - - # 电源使用者的类型 - AUDIO = 0 - VIDEO = 1 - CAMERA = 2 - FLASHLIGHT = 3 - GPS = 4 - WIFI = 5 - BLUETOOTH = 6 - PHONE = 7 - SCREEN = 8 - CPU = 9 - IDLE = 10 - WAKELOCK = 11 - DATA = 12 - AMBIENT_DISPLAY = 13 - NFC = 14 - RADIO = 15 - MODEM = 16 - SENSORS = 17 - VIBRATION = 18 - - def __init__(self): - super().__init__() - self._start_time = time.time() - self._stats: Dict[int, float] = {} - self._charge_counter = 0 - self._discharge_counter = 0 - self._charge_time_remaining = -1 - self._discharge_time_remaining = -1 - self._is_charging = False - self._battery_level = 100 - self._temperature = 25.0 - self._voltage = 3800 - self._capacity = 4000 - self._screen_on_time = 0 - self._wifi_on_time = 0 - self._bluetooth_on_time = 0 - self._gps_on_time = 0 - self._cpu_time = 0 - self._wake_lock_time = 0 - self._mobile_data_time = 0 - self._audio_time = 0 - self._video_time = 0 - self._camera_time = 0 - self._flashlight_time = 0 - self._ambient_display_time = 0 - self._nfc_time = 0 - self._radio_time = 0 - self._modem_time = 0 - self._sensors_time = 0 - self._vibration_time = 0 - self._full_wake_lock_time = 0 - self._partial_wake_lock_time = 0 - - def getBatteryLevel(self) -> int: - return self._battery_level - - def setBatteryLevel(self, level: int) -> None: - self._battery_level = max(0, min(100, level)) - - def isCharging(self) -> bool: - return self._is_charging - - def getTemperature(self) -> float: - return self._temperature - - def getVoltage(self) -> int: - return self._voltage - - def getCapacity(self) -> int: - return self._capacity - - def getScreenOnTime(self) -> int: - return self._screen_on_time - - def getWifiOnTime(self) -> int: - return self._wifi_on_time - - def getBluetoothOnTime(self) -> int: - return self._bluetooth_on_time - - def getGpsOnTime(self) -> int: - return self._gps_on_time - - def getCpuTime(self) -> int: - return self._cpu_time - - def getWakeLockTime(self) -> int: - return self._wake_lock_time - - def getMobileDataTime(self) -> int: - return self._mobile_data_time - - def getAudioTime(self) -> int: - return self._audio_time - - def getVideoTime(self) -> int: - return self._video_time - - def getCameraTime(self) -> int: - return self._camera_time - - def getFlashlightTime(self) -> int: - return self._flashlight_time - - def getAmbientDisplayTime(self) -> int: - return self._ambient_display_time - - def getNfcTime(self) -> int: - return self._nfc_time - - def getRadioTime(self) -> int: - return self._radio_time - - def getModemTime(self) -> int: - return self._modem_time - - def getSensorsTime(self) -> int: - return self._sensors_time - - def getVibrationTime(self) -> int: - return self._vibration_time - - def getUptime(self) -> int: - return int(time.time() - self._start_time) - - def getChargeTimeRemaining(self) -> int: - return self._charge_time_remaining - - def getDischargeTimeRemaining(self) -> int: - return self._discharge_time_remaining - - def computeBatteryLevelRemaining(self) -> float: - return float(self._battery_level) - - def getChargeCounter(self) -> int: - return self._charge_counter - - def getDischargeCounter(self) -> int: - return self._discharge_counter - - def toString(self) -> str: - return f"BatteryStats(level={self._battery_level}%, charging={self._is_charging})" - - -# ═══════════════════════════════════════════════════════════════ -# BatteryStatsManager -# ═══════════════════════════════════════════════════════════════ - -class BatteryStatsManager(Stub): - """BatteryStatsManager — 电池统计管理器""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._battery_stats = BatteryStats() - self._lock = threading.Lock() - - def getBatteryStats(self) -> BatteryStats: - with self._lock: - return self._battery_stats - - def reportCharge(self, chargeMah: int) -> None: - with self._lock: - self._battery_stats._charge_counter += chargeMah - - def reportDischarge(self, dischargeMah: int) -> None: - with self._lock: - self._battery_stats._discharge_counter += dischargeMah - - def resetStatistics(self) -> None: - with self._lock: - self._battery_stats = BatteryStats() - - def toString(self) -> str: - return f"BatteryStatsManager(stats={self._battery_stats})" - - -# ═══════════════════════════════════════════════════════════════ -# BatteryManager (扩展) -# ═══════════════════════════════════════════════════════════════ - -class BatteryManager(_BaseBatteryManager): - """BatteryManager (扩展) — 电池管理器 (继承 os.BatteryManager 的所有方法)""" - - EXTRA_STATUS = "status" - EXTRA_HEALTH = "health" - EXTRA_PRESENT = "present" - EXTRA_LEVEL = "level" - EXTRA_SCALE = "scale" - EXTRA_ICON_SMALL = "icon-small" - EXTRA_PLUGGED = "plugged" - EXTRA_TEMPERATURE = "temperature" - EXTRA_VOLTAGE = "voltage" - EXTRA_TECHNOLOGY = "technology" - EXTRA_CHARGE_COUNTER = "charge_counter" - EXTRA_CURRENT_NOW = "current_now" - EXTRA_CURRENT_AVERAGE = "current_average" - EXTRA_ENERGY_COUNTER = "energy_counter" - EXTRA_SEQUENCE = "sequence" - EXTRA_BATTERY_LOW = "battery_low" - EXTRA_MAX_CHARGING_CURRENT = "max_charging_current" - EXTRA_MAX_CHARGING_VOLTAGE = "max_charging_voltage" - EXTRA_CHARGE_TIME_REMAINING = "charge_time_remaining" - EXTRA_DISCHARGE_TIME_REMAINING = "discharge_time_remaining" - EXTRA_IS_CAPACITY_ESTIMATED = "is_capacity_estimated" - EXTRA_CAPACITY_PERCENTAGE = "capacity_percentage" - - ACTION_BATTERY_CHANGED = "android.intent.action.BATTERY_CHANGED" - ACTION_BATTERY_LOW = "android.intent.action.BATTERY_LOW" - ACTION_BATTERY_OKAY = "android.intent.action.BATTERY_OKAY" - ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED" - ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED" - ACTION_CHARGING = "android.intent.action.CHARGING" - ACTION_DISCHARGING = "android.intent.action.DISCHARGING" - - BATTERY_STATUS_UNKNOWN = BATTERY_STATUS_UNKNOWN - BATTERY_STATUS_CHARGING = BATTERY_STATUS_CHARGING - BATTERY_STATUS_DISCHARGING = BATTERY_STATUS_DISCHARGING - BATTERY_STATUS_NOT_CHARGING = BATTERY_STATUS_NOT_CHARGING - BATTERY_STATUS_FULL = BATTERY_STATUS_FULL - - BATTERY_HEALTH_UNKNOWN = BATTERY_HEALTH_UNKNOWN - BATTERY_HEALTH_GOOD = BATTERY_HEALTH_GOOD - BATTERY_HEALTH_OVERHEAT = BATTERY_HEALTH_OVERHEAT - BATTERY_HEALTH_DEAD = BATTERY_HEALTH_DEAD - BATTERY_HEALTH_OVER_VOLTAGE = BATTERY_HEALTH_OVER_VOLTAGE - BATTERY_HEALTH_UNSPECIFIED_FAILURE = BATTERY_HEALTH_UNSPECIFIED_FAILURE - BATTERY_HEALTH_COLD = BATTERY_HEALTH_COLD - - BATTERY_PLUGGED_AC = BATTERY_PLUGGED_AC - BATTERY_PLUGGED_USB = BATTERY_PLUGGED_USB - BATTERY_PLUGGED_WIRELESS = BATTERY_PLUGGED_WIRELESS - - BATTERY_PROPERTY_CAPACITY = BATTERY_PROPERTY_CAPACITY - BATTERY_PROPERTY_CHARGE_COUNTER = BATTERY_PROPERTY_CHARGE_COUNTER - BATTERY_PROPERTY_CURRENT_NOW = BATTERY_PROPERTY_CURRENT_NOW - BATTERY_PROPERTY_CURRENT_AVERAGE = BATTERY_PROPERTY_CURRENT_AVERAGE - BATTERY_PROPERTY_ENERGY_COUNTER = BATTERY_PROPERTY_ENERGY_COUNTER - BATTERY_PROPERTY_CHARGE_TIME_REMAINING = BATTERY_PROPERTY_CHARGE_TIME_REMAINING - BATTERY_PROPERTY_DISCHARGE_TIME_REMAINING = BATTERY_PROPERTY_DISCHARGE_TIME_REMAINING - - _instance: Optional["BatteryManager"] = None - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._battery_level = 100 - self._status = BATTERY_STATUS_FULL - self._health = BATTERY_HEALTH_GOOD - self._present = True - self._plugged = BATTERY_PLUGGED_AC - self._temperature = 25 - self._voltage = 3800 - self._technology = "Li-ion" - self._capacity = 4000 - self._charge_counter = 0 - self._current_now = 0 - self._current_average = 0 - self._energy_counter = 0 - self._max_charging_current = 3000 - self._max_charging_voltage = 4500 - self._charge_time_remaining = 0 - self._discharge_time_remaining = -1 - self._is_capacity_estimated = False - self._capacity_percentage = 100.0 - if _USE_LINUX: - self._refresh_from_linux() - - _LINUX_STATUS_MAP = { - "charging": BATTERY_STATUS_CHARGING, - "discharging": BATTERY_STATUS_DISCHARGING, - "not charging": BATTERY_STATUS_NOT_CHARGING, - "full": BATTERY_STATUS_FULL, - "unknown": BATTERY_STATUS_UNKNOWN, - } - - def _get_linux_battery_info(self) -> dict: - """从 LinuxPower 获取真实电池信息""" - try: - return LinuxPower.get_battery_info() or {} - except Exception: - return {} - - def _refresh_from_linux(self): - """用 Linux 真实电池信息刷新内部状态""" - info = self._get_linux_battery_info() - if not info: - return - self._present = bool(info.get("present", False)) - level = info.get("level", -1) - if self._present and isinstance(level, (int, float)) and level >= 0: - self._battery_level = int(level) - self._capacity_percentage = float(level) - # BATTERY_PROPERTY_CAPACITY 在 Android 中返回百分比 (0-100) - self._capacity = int(level) - else: - # 无电池或电量未知 - self._battery_level = -1 - self._capacity_percentage = 0.0 - self._capacity = -1 - status_str = str(info.get("status", "unknown")).lower() - self._status = self._LINUX_STATUS_MAP.get(status_str, BATTERY_STATUS_UNKNOWN) - voltage = info.get("voltage", 0) - if isinstance(voltage, (int, float)): - self._voltage = int(voltage * 1000) # V -> mV - temperature = info.get("temperature", 0) - if isinstance(temperature, (int, float)): - self._temperature = temperature # 摄氏度 - self._technology = info.get("technology", "") or "Li-ion" - if self._status == BATTERY_STATUS_CHARGING: - self._plugged = BATTERY_PLUGGED_AC - else: - self._plugged = 0 - - @staticmethod - def getInstance(context: Any) -> "BatteryManager": - if BatteryManager._instance is None: - BatteryManager._instance = BatteryManager(context) - return BatteryManager._instance - - def getIntProperty(self, id: int) -> int: - if _USE_LINUX: - self._refresh_from_linux() - properties = { - BATTERY_PROPERTY_CAPACITY: self._capacity, - BATTERY_PROPERTY_CHARGE_COUNTER: self._charge_counter, - BATTERY_PROPERTY_CURRENT_NOW: self._current_now, - BATTERY_PROPERTY_CURRENT_AVERAGE: self._current_average, - BATTERY_PROPERTY_ENERGY_COUNTER: self._energy_counter, - BATTERY_PROPERTY_CHARGE_TIME_REMAINING: self._charge_time_remaining, - BATTERY_PROPERTY_DISCHARGE_TIME_REMAINING: self._discharge_time_remaining, - BATTERY_PROPERTY_STATUS: self._status, - BATTERY_PROPERTY_TEMPERATURE: int(self._temperature), - BATTERY_PROPERTY_VOLTAGE: self._voltage, - } - return properties.get(id, 0) - - def getLongProperty(self, id: int) -> int: - return self.getIntProperty(id) - - def isCharging(self) -> bool: - if _USE_LINUX: - self._refresh_from_linux() - return self._status == BATTERY_STATUS_CHARGING or self._status == BATTERY_STATUS_FULL - - def getBatteryLevel(self) -> int: - if _USE_LINUX: - self._refresh_from_linux() - return self._battery_level - - def getInt(intent: Any, key: str, defaultValue: int) -> int: - return defaultValue - - def toString(self) -> str: - return ( - f"BatteryManager(level={self._battery_level}%, " - f"status={self._status}, " - f"charging={self.isCharging()})" - ) - - -# ═══════════════════════════════════════════════════════════════ -# BatterySaverController -# ═══════════════════════════════════════════════════════════════ - -class BatterySaverController(Stub): - """BatterySaverController — 电池节能控制器""" - - POLICY_MANUAL = 0 - POLICY_AUTOMATIC = 1 - POLICY_DYNAMIC = 2 - POLICY_PERCENTAGE = 3 - POLICY_TIMEOUT = 4 - POLICY_FULL = 5 - POLICY_ADAPTIVE = 6 - - STATE_OFF = 0 - STATE_ON = 1 - STATE_AUTO = 2 - STATE_MANUAL_ON = 3 - STATE_DYNAMIC_ON = 4 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._state = BatterySaverController.STATE_OFF - self._policy = BatterySaverController.POLICY_MANUAL - self._threshold = 15 - self._last_trigger_time: Optional[float] = None - self._trigger_reason = "" - self._listeners: List[Callable] = [] - - def setEnabled(self, enabled: bool) -> None: - self._state = BatterySaverController.STATE_ON if enabled else BatterySaverController.STATE_OFF - for listener in self._listeners: - try: - listener(enabled) - except Exception: - pass - - def isEnabled(self) -> bool: - return self._state == BatterySaverController.STATE_ON - - def getPolicy(self) -> int: - return self._policy - - def setPolicy(self, policy: int) -> None: - self._policy = policy - - def getThreshold(self) -> int: - return self._threshold - - def setThreshold(self, threshold: int) -> None: - self._threshold = max(0, min(100, threshold)) - - def getTriggerReason(self) -> str: - return self._trigger_reason - - def addListener(self, listener: Callable) -> None: - if listener not in self._listeners: - self._listeners.append(listener) - - def removeListener(self, listener: Callable) -> None: - if listener in self._listeners: - self._listeners.remove(listener) - - def toString(self) -> str: - return f"BatterySaverController(enabled={self.isEnabled()}, threshold={self._threshold}%)" - - -# ═══════════════════════════════════════════════════════════════ -# PowerManager (扩展) -# ═══════════════════════════════════════════════════════════════ - -class PowerManager(_BasePowerManager): - """PowerManager — 电源管理器 (扩展,继承 os.PowerManager 的所有方法)""" - - PARTIAL_WAKE_LOCK = PARTIAL_WAKE_LOCK - FULL_WAKE_LOCK = FULL_WAKE_LOCK - SCREEN_DIM_WAKE_LOCK = SCREEN_DIM_WAKE_LOCK - SCREEN_BRIGHT_WAKE_LOCK = SCREEN_BRIGHT_WAKE_LOCK - PROXIMITY_SCREEN_OFF_WAKE_LOCK = PROXIMITY_SCREEN_OFF_WAKE_LOCK - DOZE_WAKE_LOCK = DOZE_WAKE_LOCK - DRAW_WAKE_LOCK = DRAW_WAKE_LOCK - - ACQUIRE_CAUSES_WAKEUP = ACQUIRE_CAUSES_WAKEUP - ON_AFTER_RELEASE = ON_AFTER_RELEASE - - LOCATION_MODE_NO_CHANGE = 0 - LOCATION_MODE_GPS_DISABLED = 1 - LOCATION_MODE_FOREGROUND_ONLY = 2 - LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF = 3 - LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF = 4 - - SOUND_TRIGGER_MODE_NO_CHANGE = 0 - SOUND_TRIGGER_MODE_ALL_DISABLED = 1 - SOUND_TRIGGER_MODE_CRITICAL_ONLY = 2 - SOUND_TRIGGER_MODE_NEVER = 3 - - GO_TO_SLEEP_REASON_APPLICATION = 0 - GO_TO_SLEEP_REASON_DEVICE_ADMIN = 1 - GO_TO_SLEEP_REASON_TIMEOUT = 2 - GO_TO_SLEEP_REASON_LID_SWITCH = 3 - GO_TO_SLEEP_REASON_POWER_BUTTON = 4 - GO_TO_SLEEP_REASON_HDMI = 5 - GO_TO_SLEEP_REASON_SLEEP_GESTURE = 6 - GO_TO_SLEEP_REASON_ACCESSIBILITY = 7 - GO_TO_SLEEP_REASON_FORCE_SUSPEND = 8 - - WAKE_REASON_UNKNOWN = 0 - WAKE_REASON_POWER_BUTTON = 1 - WAKE_REASON_APPLICATION = 2 - WAKE_REASON_PLUGGED_IN = 3 - WAKE_REASON_GESTURE = 4 - WAKE_REASON_CAMERA_LAUNCH = 5 - WAKE_REASON_WAKE_KEY = 6 - WAKE_REASON_WAKE_MOTION = 7 - WAKE_REASON_HDMI = 8 - WAKE_REASON_DISPLAY_GROUP_REMOVED = 9 - WAKE_REASON_DREAM_MANAGER = 10 - WAKE_REASON_LID = 11 - WAKE_REASON_UNLOCK = 12 - WAKE_REASON_TAP = 13 - WAKE_REASON_LIFT = 14 - WAKE_REASON_PICK_UP = 15 - WAKE_REASON_DOUBLE_TAP = 16 - WAKE_REASON_SWIPE = 17 - WAKE_REASON_BACK_GESTURE = 18 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._is_interactive = True - self._is_power_save_mode = False - self._is_device_idle_mode = False - self._is_light_device_idle_mode = False - self._is_screen_on = True - self._screen_brightness = 255 - self._screen_brightness_mode = SCREEN_BRIGHT_MODE_AUTOMATIC - self._wake_locks: Dict[str, "PowerManager.WakeLock"] = {} - self._is_rebooting = False - self._shutdown_requested = False - self._thermal_status = THERMAL_STATUS_NONE - self._power_save_state = PowerSaveState() - self._current_wake_up_reason = PowerManager.WAKE_REASON_UNKNOWN - - def isInteractive(self) -> bool: - if _USE_LINUX: - return self.isScreenOn() - return self._is_interactive - - def isPowerSaveMode(self) -> bool: - return self._is_power_save_mode - - def isDeviceIdleMode(self) -> bool: - return self._is_device_idle_mode - - def isLightDeviceIdleMode(self) -> bool: - return self._is_light_device_idle_mode - - def isRebootingUserspaceSupported(self) -> bool: - return True - - def isScreenOn(self) -> bool: - if _USE_LINUX: - import os as _os_mod - display = _os_mod.environ.get("DISPLAY", "") - if display: - return True - wayland = _os_mod.environ.get("WAYLAND_DISPLAY", "") - return bool(wayland) - return self._is_screen_on - - def isWakeLockLevelSupported(self, level: int) -> bool: - return level in [ - PARTIAL_WAKE_LOCK, FULL_WAKE_LOCK, SCREEN_DIM_WAKE_LOCK, - SCREEN_BRIGHT_WAKE_LOCK, PROXIMITY_SCREEN_OFF_WAKE_LOCK, - DOZE_WAKE_LOCK, DRAW_WAKE_LOCK, - ] - - def newWakeLock(self, levelAndFlags: int, tag: str) -> "PowerManager.WakeLock": - wl = PowerManager.WakeLock(self, levelAndFlags, tag) - self._wake_locks[tag] = wl - return wl - - def goToSleep(self, time: int, reason: int, flags: int = 0) -> None: - self._is_interactive = False - self._is_screen_on = False - - def wakeUp(self, time: int, reason: int = WAKE_REASON_UNKNOWN) -> None: - self._is_interactive = True - self._is_screen_on = True - self._current_wake_up_reason = reason - - def userActivity(self, when: int, noChangeLights: bool) -> None: - pass - - def userActivity(self, when: int, event: int, flags: int = 0) -> None: - pass - - def getCurrentWakeUpReason(self) -> int: - return self._current_wake_up_reason - - def getThermalStatus(self) -> int: - return self._thermal_status - - def setThermalStatus(self, status: int) -> None: - self._thermal_status = status - - def getPowerSaveState(self, serviceType: int) -> PowerSaveState: - return self._power_save_state - - def reboot(self, reason: Optional[str] = None) -> None: - self._is_rebooting = True - raise AndroidOnlyError("PowerManager.reboot() 仅在 Android 设备上可用") - - def shutdown(self, reason: Optional[str] = None) -> None: - self._shutdown_requested = True - raise AndroidOnlyError("PowerManager.shutdown() 仅在 Android 设备上可用") - - def getCurrentBrightness(self) -> int: - return self._screen_brightness - - def setCurrentBrightness(self, brightness: int) -> None: - self._screen_brightness = max(0, min(255, brightness)) - - def getDefaultScreenBrightnessSetting(self) -> int: - return 127 - - def getMinimumBrightnessSetting(self) -> int: - return 1 - - def getMaximumBrightnessSetting(self) -> int: - return 255 - - def toString(self) -> str: - return ( - f"PowerManager(interactive={self._is_interactive}, " - f"powersave={self._is_power_save_mode}, " - f"screen_on={self._is_screen_on})" - ) - - # ═══════════════════════════════════════════════════════════ - # PowerManager.WakeLock - # ═══════════════════════════════════════════════════════════ - - class WakeLock(Stub): - """PowerManager.WakeLock — 唤醒锁""" - - def __init__(self, manager: "PowerManager", levelAndFlags: int, tag: str): - super().__init__() - self._manager = manager - self._level_and_flags = levelAndFlags - self._tag = tag - self._is_held = False - self._reference_counted = True - self._work_source: Optional[Any] = None - self._history_tag: Optional[str] = None - self._acquire_time: Optional[float] = None - - def acquire(self, timeoutMillis: int = -1) -> None: - self._is_held = True - self._acquire_time = time.time() - - def acquire(self, timeOut: int) -> None: - self._is_held = True - self._acquire_time = time.time() - - def release(self, flags: int = 0) -> None: - self._is_held = False - - def isHeld(self) -> bool: - return self._is_held - - def setReferenceCounted(self, value: bool) -> None: - self._reference_counted = value - - def setWorkSource(self, ws: Any) -> None: - self._work_source = ws - - def setHistoryTag(self, tag: str) -> None: - self._history_tag = tag - - def getTag(self) -> str: - return self._tag - - def getLevelAndFlags(self) -> int: - return self._level_and_flags - - def toString(self) -> str: - return f"WakeLock(tag={self._tag}, held={self._is_held})" \ No newline at end of file diff --git a/android_pyapi/android/os_storage.py b/android_pyapi/android/os_storage.py deleted file mode 100644 index 4da3fb7..0000000 --- a/android_pyapi/android/os_storage.py +++ /dev/null @@ -1,387 +0,0 @@ -""" -Android os.storage 组件 — StorageManager, StorageVolume, DiskInfo, VolumeInfo 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import shutil -import threading -import uuid -from typing import Any, Callable, Dict, List, Optional, Set - -from .._core import Stub, AndroidOnlyError, resolve_android_path -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# StorageVolume -# ═══════════════════════════════════════════════════════════════ - -class StorageVolume(Stub): - """StorageVolume — 存储卷""" - - STATE_MOUNTED = "mounted" - STATE_MOUNTED_READ_ONLY = "mounted_ro" - STATE_REMOVED = "removed" - STATE_UNMOUNTED = "unmounted" - STATE_CHECKING = "checking" - STATE_EJECTING = "ejecting" - STATE_MOUNTABLE = "mountable" - STATE_UNMOUNTABLE = "unmountable" - STATE_BAD_REMOVAL = "bad_removal" - STATE_DAMAGED = "damaged" - - STORAGE_TYPE_INTERNAL = 0 - STORAGE_TYPE_EXTERNAL = 1 - - def __init__(self, path: str = "/sdcard", description: str = "Internal shared storage", - primary: bool = True, removable: bool = False, - state: str = STATE_MOUNTED, max_file_size: int = 4 * 1024 * 1024 * 1024, - storage_type: int = STORAGE_TYPE_INTERNAL): - super().__init__() - self._path = path - self._description = description - self._primary = primary - self._removable = removable - self._state = state - self._max_file_size = max_file_size - self._uuid = str(uuid.uuid4()).replace("-", "")[:16] - self._user_label = description - self._storage_type = storage_type - - def getPath(self) -> str: - return self._path - - def getDescription(self, context: Any = None) -> str: - return self._description - - def isPrimary(self) -> bool: - return self._primary - - def isRemovable(self) -> bool: - return self._removable - - def getState(self) -> str: - return self._state - - def getMaxFileSize(self) -> int: - return self._max_file_size - - def getUuid(self) -> str: - return self._uuid - - def getUserLabel(self) -> str: - return self._user_label - - def getDirectory(self) -> Any: - from pathlib import Path - return Path(self._path) if os.path.isdir(self._path) else None - - def getStorageType(self) -> int: - return self._storage_type - - def createAccessIntent(self, directoryName: str) -> Any: - return None - - def toString(self) -> str: - return (f"StorageVolume{{path={self._path}, primary={self._primary}, " - f"state={self._state}, removable={self._removable}}}") - - -# ═══════════════════════════════════════════════════════════════ -# DiskInfo -# ═══════════════════════════════════════════════════════════════ - -class DiskInfo(Stub): - """DiskInfo — 磁盘信息""" - - def __init__(self, id_: str = "", label: str = "", size: int = 0, - is_sd: bool = False, is_usb: bool = False, - is_adoptable: bool = False): - super().__init__() - self._id = id_ - self._label = label - self._size = size - self._is_sd = is_sd - self._is_usb = is_usb - self._is_adoptable = is_adoptable - - def getId(self) -> str: - return self._id - - def getLabel(self) -> str: - return self._label - - def getSize(self) -> int: - return self._size - - def isSd(self) -> bool: - return self._is_sd - - def isUsb(self) -> bool: - return self._is_usb - - def isAdoptable(self) -> bool: - return self._is_adoptable - - def toString(self) -> str: - return (f"DiskInfo{{id={self._id}, label={self._label}, " - f"size={self._size}, sd={self._is_sd}, usb={self._is_usb}}}") - - -# ═══════════════════════════════════════════════════════════════ -# VolumeInfo -# ═══════════════════════════════════════════════════════════════ - -class VolumeInfo(Stub): - """VolumeInfo — 卷信息""" - - TYPE_PRIVATE = 1 - TYPE_PUBLIC = 2 - TYPE_EMULATED = 3 - TYPE_STUB = 4 - TYPE_OBB = 5 - - STATE_UNMOUNTED = 0 - STATE_CHECKING = 1 - STATE_MOUNTED = 2 - STATE_MOUNTED_READ_ONLY = 3 - STATE_FORMATTING = 4 - STATE_EJECTING = 5 - STATE_BAD_REMOVAL = 6 - STATE_DAMAGED = 7 - - def __init__(self, id_: str = "", type_: int = TYPE_EMULATED, - disk: Optional[DiskInfo] = None, partition: str = "", - path: str = "", internal_path: str = "", - fs_type: str = "ext4", fs_uuid: str = "", - fs_label: str = "", state: int = STATE_MOUNTED, - mount_user_id: int = 0): - super().__init__() - self._id = id_ - self._type = type_ - self._disk = disk - self._partition = partition - self._path = path - self._internal_path = internal_path - self._fs_type = fs_type - self._fs_uuid = fs_uuid - self._fs_label = fs_label - self._state = state - self._mount_user_id = mount_user_id - - def getId(self) -> str: - return self._id - - def getType(self) -> int: - return self._type - - def getDisk(self) -> Optional[DiskInfo]: - return self._disk - - def getPartition(self) -> str: - return self._partition - - def getPath(self) -> Optional[str]: - return self._path - - def getInternalPath(self) -> Optional[str]: - return self._internal_path - - def getFsType(self) -> str: - return self._fs_type - - def getFsUuid(self) -> str: - return self._fs_uuid - - def getFsLabel(self) -> str: - return self._fs_label - - def getState(self) -> int: - return self._state - - def getMountUserId(self) -> int: - return self._mount_user_id - - def toString(self) -> str: - return (f"VolumeInfo{{id={self._id}, type={self._type}, " - f"state={self._state}, path={self._path}}}") - - -# ═══════════════════════════════════════════════════════════════ -# VolumeRecord -# ═══════════════════════════════════════════════════════════════ - -class VolumeRecord(Stub): - """VolumeRecord — 卷记录""" - - def __init__(self, fs_uuid: str = "", part_guid: str = "", - type_: int = VolumeInfo.TYPE_PRIVATE, - fs_label: str = "", nickname: str = "", - created_millis: int = 0, last_seen_millis: int = 0, - approved_for_type: bool = False): - super().__init__() - self._fs_uuid = fs_uuid - self._part_guid = part_guid - self._type = type_ - self._fs_label = fs_label - self._nickname = nickname - self._created_millis = created_millis - self._last_seen_millis = last_seen_millis - self._approved_for_type = approved_for_type - self._pseudo_uuid = str(uuid.uuid4()) - - def getFsUuid(self) -> str: - return self._fs_uuid - - def getPartGuid(self) -> str: - return self._part_guid - - def getType(self) -> int: - return self._type - - def getFsLabel(self) -> str: - return self._fs_label - - def getNickname(self) -> str: - return self._nickname - - def getCreatedMillis(self) -> int: - return self._created_millis - - def getLastSeenMillis(self) -> int: - return self._last_seen_millis - - def isApprovedForType(self) -> bool: - return self._approved_for_type - - def getPseudoUuid(self) -> str: - return self._pseudo_uuid - - def toString(self) -> str: - return f"VolumeRecord{{uuid={self._fs_uuid}, type={self._type}, label={self._fs_label}}}" - - -# ═══════════════════════════════════════════════════════════════ -# StorageManager -# ═══════════════════════════════════════════════════════════════ - -class StorageManager(Stub): - """StorageManager — 存储管理器""" - - ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE" - ACTION_CLEAR_APP_CACHE = "android.os.storage.action.CLEAR_APP_CACHE" - UUID_DEFAULT = "primary_physical" - EXTRA_UUID = "android.os.storage.extra.UUID" - EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES" - - def __init__(self): - super().__init__() - self._lock = threading.Lock() - self._primary_volume = StorageVolume( - path="/sdcard", - description="Internal shared storage", - primary=True, - removable=False, - ) - self._volumes: List[StorageVolume] = [ - self._primary_volume, - StorageVolume( - path="/sdcard/external", - description="External SD Card", - primary=False, - removable=True, - state=StorageVolume.STATE_UNMOUNTED, - storage_type=StorageVolume.STORAGE_TYPE_EXTERNAL, - ), - ] - - # ── Volumes ── - - def getStorageVolumes(self) -> List[StorageVolume]: - with self._lock: - return list(self._volumes) - - def getVolumeList(self) -> List[StorageVolume]: - return self.getStorageVolumes() - - def getPrimaryStorageVolume(self) -> StorageVolume: - return self._primary_volume - - def getPrimaryStorageSize(self) -> int: - stat = shutil.disk_usage(os.path.expanduser("~")) - return stat.total - - def getBestFitPrimaryVolume(self) -> StorageVolume: - return self._primary_volume - - def getMountState(self, mountPoint: str) -> bool: - return os.path.ismount(mount_point) if os.path.exists(mount_point) else False - - # ── Mount / Unmount / Format ── - - def mount(self, volumeId: str) -> int: - return 0 - - def unmount(self, volumeId: str) -> int: - return 0 - - def format(self, volumeId: str) -> int: - return 0 - - # ── Allocation ── - - def allocateBytes(self, volumeUuid: str, bytes_: int, flags: int = 0) -> int: - stat = shutil.disk_usage(os.path.expanduser("~")) - if stat.free >= bytes_: - return 0 - return -1 - - def allocateBytesForUuid(self, volumeUuid: str, bytes_: int, flags: int = 0) -> int: - return self.allocateBytes(volumeUuid, bytes_, flags) - - def isAllocationSupported(self, volumeUuid: str) -> bool: - return True - - def cacheBehavior(self, uuid_: str) -> int: - return 0 - - # ── Permission ── - - def checkPermission(self, volumeUuid: str, pid: int, uid: int) -> int: - return 0 - - # ── OBB ── - - def isObbMounted(self, filename: str) -> bool: - return False - - def mountObb(self, filename: str, key: str, listener: Any) -> bool: - return True - - def unmountObb(self, filename: str, force: bool, listener: Any) -> bool: - return True - - def getMountedObbPath(self, filename: str) -> Optional[str]: - return None - - def toString(self) -> str: - return f"StorageManager@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "StorageManager", - "StorageVolume", - "DiskInfo", - "VolumeInfo", - "VolumeRecord", -] \ No newline at end of file diff --git a/android_pyapi/android/os_user.py b/android_pyapi/android/os_user.py deleted file mode 100644 index 2c666d2..0000000 --- a/android_pyapi/android/os_user.py +++ /dev/null @@ -1,408 +0,0 @@ -""" -Android os UserManager 组件 — UserManager, UserHandle, UserInfo 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import threading -import uuid -from typing import Any, Dict, List, Optional, Set - -from .._core import Stub, AndroidOnlyError, _sdcard_root -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# UserHandle -# ═══════════════════════════════════════════════════════════════ - -class UserHandle(Stub): - """UserHandle — 用户句柄""" - - MY_USER_ID = 0 - USER_OWNER = 0 - USER_CURRENT = -2 - USER_CURRENT_OR_SELF = -3 - USER_NULL = -10000 - USER_ALL = -1 - USER_SYSTEM = 0 - USER_SERIAL_NUMBER_SYSTEM = 0 - - PER_USER_RANGE = 100000 - - def __init__(self, userId: int = MY_USER_ID): - super().__init__() - self._userId = userId - - def getUserId(self) -> int: - return self._userId - - def getAppId(self, uid: int) -> int: - return uid % self.PER_USER_RANGE - - def getUserId(self, uid: int) -> int: - return uid // self.PER_USER_RANGE - - @staticmethod - def myUserId() -> int: - return 0 - - @staticmethod - def isIsolated(uid: int) -> bool: - return False - - @staticmethod - def isApp(uid: int) -> bool: - return 10000 <= (uid % 100000) < 20000 - - @staticmethod - def isSameUser(uid1: int, uid2: int) -> bool: - return (uid1 // 100000) == (uid2 // 100000) - - @staticmethod - def isSystem(uid: int) -> bool: - return uid == 1000 - - def toString(self) -> str: - return f"UserHandle{{{self._userId}}}" - - def hashCode(self) -> int: - return hash(self._userId) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, UserHandle): - return False - return self._userId == other._userId - - -# ═══════════════════════════════════════════════════════════════ -# UserInfo -# ═══════════════════════════════════════════════════════════════ - -class UserInfo(Stub): - """UserInfo — 用户信息""" - - FLAG_PRIMARY = 0x00000001 - FLAG_ADMIN = 0x00000002 - FLAG_GUEST = 0x00000004 - FLAG_RESTRICTED = 0x00000008 - FLAG_MANAGED_PROFILE = 0x00000010 - FLAG_PROFILE = 0x00000020 - FLAG_DEMO = 0x00000200 - FLAG_SYSTEM = 0x00001000 - - def __init__(self, id_: int = 0, name: str = "System User", - icon_path: str = "", flags: int = FLAG_PRIMARY | FLAG_ADMIN | FLAG_SYSTEM): - super().__init__() - self.id: int = id_ - self.name: str = name - self.iconPath: str = icon_path - self.flags: int = flags - - def isPrimary(self) -> bool: - return bool(self.flags & self.FLAG_PRIMARY) - - def isAdmin(self) -> bool: - return bool(self.flags & self.FLAG_ADMIN) - - def isGuest(self) -> bool: - return bool(self.flags & self.FLAG_GUEST) - - def isDemo(self) -> bool: - return bool(self.flags & self.FLAG_DEMO) - - def isManagedProfile(self) -> bool: - return bool(self.flags & self.FLAG_MANAGED_PROFILE) - - def isRestricted(self) -> bool: - return bool(self.flags & self.FLAG_RESTRICTED) - - def isSystemOnly(self) -> bool: - return bool(self.flags & self.FLAG_SYSTEM) - - def isProfile(self) -> bool: - return bool(self.flags & self.FLAG_PROFILE) - - def toString(self) -> str: - return f"UserInfo{{id={self.id}, name={self.name}, flags={self.flags}}}" - - def hashCode(self) -> int: - return hash(self.id) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, UserInfo): - return False - return self.id == other.id - - -# ═══════════════════════════════════════════════════════════════ -# UserManager -# ═══════════════════════════════════════════════════════════════ - -class UserManager(Stub): - """UserManager — 用户管理器""" - - USER_CREATION_FAILED_NOT_PERMITTED = -1 - USER_CREATION_FAILED_NO_MORE_USERS = -2 - - # DISALLOW_* restrictions - DISALLOW_CONFIG_WIFI = "no_config_wifi" - DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth" - DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks" - DISALLOW_CONFIG_TETHERING = "no_config_tethering" - DISALLOW_CONFIG_SMS = "no_config_sms" - DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials" - DISALLOW_CONFIG_VPN = "no_config_vpn" - DISALLOW_CONFIG_LOCALE = "no_config_locale" - DISALLOW_CONFIG_DATE_TIME = "no_config_date_time" - DISALLOW_CONFIG_BRIGHTNESS = "no_config_brightness" - DISALLOW_FACTORY_RESET = "no_factory_reset" - DISALLOW_APPS_CONTROL = "no_control_apps" - DISALLOW_CREATE_WINDOWS = "no_create_windows" - DISALLOW_CROSS_PROFILE_COPY_PASTE = "no_cross_profile_copy_paste" - DISALLOW_DEBUGGING_FEATURES = "no_debugging_features" - DISALLOW_INSTALL_APPS = "no_install_apps" - DISALLOW_UNINSTALL_APPS = "no_uninstall_apps" - DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts" - DISALLOW_OUTGOING_CALLS = "no_outgoing_calls" - DISALLOW_REMOVE_USER = "no_remove_user" - DISALLOW_SAFE_BOOT = "no_safe_boot" - DISALLOW_SET_USER_RESTRICTIONS = "no_set_user_restrictions" - DISALLOW_SHARE_LOCATION = "no_share_location" - DISALLOW_SMS = "no_sms" - DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone" - DISALLOW_UNMUTE_DEVICE = "no_unmute_device" - DISALLOW_ADJUST_VOLUME = "no_adjust_volume" - - KEY_RESTRICTIONS_PENDING = "restrictions_pending" - - PIN_VERIFICATION_FAILURE_INVALID = -3 - PIN_VERIFICATION_FAILURE_NOT_SET = -2 - PIN_VERIFICATION_SUCCESS = 0 - - MAX_SUPPORTED_USERS = 4 - - def __init__(self): - super().__init__() - self._lock = threading.Lock() - self._next_user_id = 10 - self._users: Dict[int, UserInfo] = {0: UserInfo(0, "System User")} - self._serial_numbers: Dict[int, int] = {0: 0} - self._next_serial = 1 - self._restrictions: Dict[str, Set[str]] = {} - self._quiet_mode: Set[int] = set() - - # ── User counting ── - - def getUserCount(self) -> int: - with self._lock: - return len(self._users) - - def getUsers(self) -> List[UserInfo]: - with self._lock: - return list(self._users.values()) - - def getSystemUser(self) -> UserInfo: - return self._users.get(0, UserInfo(0, "System User")) - - def getForegroundUser(self) -> UserHandle: - return UserHandle(0) - - def getMaxSupportedUsers(self) -> int: - return self.MAX_SUPPORTED_USERS - - def supportsMultipleUsers(self) -> bool: - return True - - # ── Serial numbers ── - - def getSerialNumberForUser(self, user: UserHandle) -> int: - return self._serial_numbers.get(user.getUserId(), -1) - - def getUserForSerialNumber(self, serialNumber: int) -> Optional[UserHandle]: - for uid, sn in self._serial_numbers.items(): - if sn == serialNumber: - return UserHandle(uid) - return None - - # ── Profiles ── - - def getUserProfiles(self) -> List[UserInfo]: - with self._lock: - return list(self._users.values()) - - def getProfileIds(self, userId: int, enabledOnly: bool = False) -> List[int]: - return [0, userId] if enabledOnly else [uid for uid in self._users] - - def getProfileIdsWithDisabled(self, userId: int) -> List[int]: - return self.getProfileIds(userId, enabledOnly=False) - - def getEnabledProfileIds(self, userId: int) -> List[int]: - return self.getProfileIds(userId, enabledOnly=True) - - def getUserProfileIds(self, userId: int) -> List[int]: - return self.getProfileIds(userId) - - def getProfileParent(self, userId: int) -> Optional[UserHandle]: - return None - - def getProfileParentId(self, userId: int) -> int: - return 0 - - # ── User state ── - - def isUserRunning(self, user: UserHandle) -> bool: - return True - - def isUserRunningOrStopped(self, user: UserHandle) -> bool: - return True - - def isUserUnlocked(self, user: UserHandle) -> bool: - return True - - def isUserUnlocked(self, userId: int) -> bool: - return True - - def isUserSwitcherEnabled(self) -> bool: - return True - - # ── User type checks ── - - def isManagedProfile(self, userId: int) -> bool: - with self._lock: - user = self._users.get(userId) - return user.isManagedProfile() if user else False - - def isManagedProfile(self) -> bool: - return False - - def isSystemUser(self) -> bool: - return True - - def isPrimaryUser(self) -> bool: - return True - - def isDemoUser(self) -> bool: - return False - - def isGuestUser(self) -> bool: - return False - - def isAdminUser(self) -> bool: - return True - - def isRestrictedProfile(self) -> bool: - return False - - def isLinkedUser(self) -> bool: - return False - - def isUserOfType(self, userHandle: UserHandle, userType: str) -> bool: - return userHandle.getUserId() == 0 - - # ── Restrictions ── - - def hasUserRestriction(self, restrictionKey: str, userHandle: UserHandle) -> bool: - uid = userHandle.getUserId() - with self._lock: - user_restrictions = self._restrictions.get(str(uid), set()) - return restrictionKey in user_restrictions - - def getUserRestrictions(self, userHandle: UserHandle) -> Dict[str, bool]: - uid = userHandle.getUserId() - with self._lock: - user_restrictions = self._restrictions.get(str(uid), set()) - return {r: True for r in user_restrictions} - - def setUserRestriction(self, restrictionKey: str, value: bool, userHandle: UserHandle): - uid = str(userHandle.getUserId()) - with self._lock: - if uid not in self._restrictions: - self._restrictions[uid] = set() - if value: - self._restrictions[uid].add(restrictionKey) - else: - self._restrictions[uid].discard(restrictionKey) - - # ── Quiet mode ── - - def isQuietModeEnabled(self, userHandle: UserHandle) -> bool: - return userHandle.getUserId() in self._quiet_mode - - def setQuietModeEnabled(self, userHandle: UserHandle, enable: bool) -> bool: - uid = userHandle.getUserId() - with self._lock: - if enable: - self._quiet_mode.add(uid) - else: - self._quiet_mode.discard(uid) - return True - - # ── User switching ── - - def canSwitchUsers(self) -> bool: - return True - - def switchUser(self, userHandle: UserHandle) -> bool: - return True - - def removeUser(self, userId: int) -> bool: - with self._lock: - if userId in self._users and userId != 0: - del self._users[userId] - self._serial_numbers.pop(userId, None) - return True - return False - - # ── User creation ── - - def createUser(self, name: str, flags: int = 0) -> UserInfo: - with self._lock: - uid = self._next_user_id - self._next_user_id += 1 - user = UserInfo(uid, name, flags=flags) - self._users[uid] = user - self._serial_numbers[uid] = self._next_serial - self._next_serial += 1 - return user - - def createProfile(self, name: str, profileFlags: int, userId: int) -> UserInfo: - return self.createUser(name, profileFlags) - - def createGuest(self, context: Any = None) -> UserInfo: - return self.createUser("Guest", UserInfo.FLAG_GUEST) - - # ── User name ── - - def setUserName(self, userId: int, name: str): - with self._lock: - if userId in self._users: - self._users[userId].name = name - - def getUserName(self) -> Optional[str]: - return "System User" - - # ── User info ── - - def getUserInfo(self, userId: int) -> Optional[UserInfo]: - with self._lock: - return self._users.get(userId) - - def toString(self) -> str: - return f"UserManager@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "UserManager", - "UserHandle", - "UserInfo", -] \ No newline at end of file diff --git a/android_pyapi/android/permission.py b/android_pyapi/android/permission.py deleted file mode 100644 index 1b0d9e8..0000000 --- a/android_pyapi/android/permission.py +++ /dev/null @@ -1,162 +0,0 @@ -""" -Android permission 组件 — PermissionManager, PermissionChecker 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import threading -from typing import Any, Callable, Dict, List, Optional, Set - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - - -# ═══════════════════════════════════════════════════════════════ -# PermissionChecker -# ═══════════════════════════════════════════════════════════════ - -class PermissionChecker(Stub): - """PermissionChecker — 权限检查器""" - - PERMISSION_GRANTED = 0 - PERMISSION_DENIED = -1 - PERMISSION_DENIED_APP_OP = -2 - PID_UNKNOWN = -1 - - @staticmethod - def checkPermission(context: Any, permission: str, pid: int, uid: int, - packageName: str) -> int: - return PermissionChecker.PERMISSION_GRANTED - - @staticmethod - def checkSelfPermission(context: Any, permission: str) -> int: - return PermissionChecker.PERMISSION_GRANTED - - -# ═══════════════════════════════════════════════════════════════ -# PermissionManager -# ═══════════════════════════════════════════════════════════════ - -class PermissionManager(Stub): - """PermissionManager — 权限管理器""" - - PERMISSION_GRANTED = 0 - PERMISSION_DENIED = -1 - - FLAG_PERMISSION_GRANTED_BY_DEFAULT = 1 << 5 - FLAG_PERMISSION_POLICY_FIXED = 1 << 4 - FLAG_PERMISSION_SYSTEM_FIXED = 1 << 3 - FLAG_PERMISSION_USER_FIXED = 1 << 2 - FLAG_PERMISSION_USER_SET = 1 << 0 - FLAG_PERMISSION_REVIEW_REQUIRED = 1 << 6 - FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 1 << 7 - FLAG_PERMISSION_REVOKED_COMPAT = 1 << 10 - FLAG_PERMISSION_ONE_TIME = 1 << 8 - FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED = 1 << 9 - FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED = 1 << 11 - FLAG_PERMISSION_AUTO_REVOKED = 1 << 12 - FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY = 1 << 13 - - def __init__(self): - super().__init__() - self._lock = threading.Lock() - self._granted_permissions: Dict[str, bool] = {} - self._permission_flags: Dict[str, int] = {} - self._listeners: List[Callable[[str], None]] = [] - - # ── Check ── - - def checkPermission(self, permission: str, pid: int = -1, uid: int = -1) -> int: - with self._lock: - if self._granted_permissions.get(permission, True): - return self.PERMISSION_GRANTED - return self.PERMISSION_DENIED - - def isRuntimePermission(self, permission: str) -> bool: - return True - - # ── Flags ── - - def getPermissionFlags(self, permission: str, packageName: str, user: Any = None) -> int: - with self._lock: - return self._permission_flags.get(permission, 0) - - def updatePermissionFlags(self, permission: str, packageName: str, - flagMask: int, flagValues: int, - user: Any = None) -> bool: - with self._lock: - current = self._permission_flags.get(permission, 0) - current = (current & ~flagMask) | (flagValues & flagMask) - self._permission_flags[permission] = current - return True - - # ── Grant / Revoke ── - - def grantPermission(self, permission: str, packageName: str, user: Any = None): - with self._lock: - self._granted_permissions[permission] = True - - def revokePermission(self, permission: str, packageName: str, user: Any = None): - with self._lock: - self._granted_permissions[permission] = False - - # ── Rationale ── - - def shouldShowRequestPermissionRationale(self, permission: str) -> bool: - return False - - # ── Listed ── - - def getGrantedPermissions(self) -> List[str]: - with self._lock: - return [p for p, g in self._granted_permissions.items() if g] - - def getDeniedPermissions(self) -> List[str]: - with self._lock: - return [p for p, g in self._granted_permissions.items() if not g] - - def getSplitPermissions(self) -> List[Any]: - return [] - - # ── Revocation ── - - def isPermissionRevokedByPolicy(self, permission: str, packageName: str) -> bool: - return False - - # ── Listeners ── - - def addOnPermissionsChangeListener(self, listener: Callable[[str], None]): - with self._lock: - if listener not in self._listeners: - self._listeners.append(listener) - - def removeOnPermissionsChangeListener(self, listener: Callable[[str], None]): - with self._lock: - if listener in self._listeners: - self._listeners.remove(listener) - - def _notify_listeners(self, permission: str): - with self._lock: - for listener in self._listeners: - listener(permission) - - # ── Misc ── - - def getPermissionControllerService(self) -> Any: - return None - - def toString(self) -> str: - return f"PermissionManager@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "PermissionManager", - "PermissionChecker", -] \ No newline at end of file diff --git a/android_pyapi/android/preference.py b/android_pyapi/android/preference.py deleted file mode 100644 index f666cc1..0000000 --- a/android_pyapi/android/preference.py +++ /dev/null @@ -1,654 +0,0 @@ -""" -Android Preference 组件 — Preference, PreferenceScreen, PreferenceManager 等 -方法签名完全对齐 Android SDK -""" - -import os -import sys -import json -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) - - -# ═══════════════════════════════════════════════════════════════ -# PreferenceChangeListener -# ═══════════════════════════════════════════════════════════════ - -class PreferenceChangeListener(Stub): - """OnPreferenceChangeListener — 偏好变化监听器""" - - def onPreferenceChange(self, preference: "Preference", new_value: Any) -> bool: - return True - - -class PreferenceOnPreferenceClickListener(Stub): - """OnPreferenceClickListener — 偏好点击监听器""" - - def onPreferenceClick(self, preference: "Preference") -> bool: - return True - - -# ═══════════════════════════════════════════════════════════════ -# PreferenceManager -# ═══════════════════════════════════════════════════════════════ - -class PreferenceManager(Stub): - """PreferenceManager — 偏好管理器""" - - KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values" - - def __init__(self, context: Any): - super().__init__() - self._context = context - self._shared_prefs_name: str = f"{_package_name}_preferences" - self._shared_prefs_mode: int = 0 - self._prefs_data: Dict[str, Any] = {} - self._prefs_path = os.path.join(_app_data_dir, "shared_prefs", - f"{self._shared_prefs_name}.json") - self._load() - - def _load(self): - os.makedirs(os.path.dirname(self._prefs_path), exist_ok=True) - if os.path.exists(self._prefs_path): - try: - with open(self._prefs_path, "r") as f: - self._prefs_data = json.load(f) - except (json.JSONDecodeError, OSError): - self._prefs_data = {} - else: - self._prefs_data = {} - - def _save(self): - os.makedirs(os.path.dirname(self._prefs_path), exist_ok=True) - with open(self._prefs_path, "w") as f: - json.dump(self._prefs_data, f) - - def getSharedPreferences(self) -> Any: - return self._prefs_data - - def setSharedPreferencesName(self, name: str): - self._shared_prefs_name = name - self._prefs_path = os.path.join(_app_data_dir, "shared_prefs", - f"{name}.json") - self._load() - - def setSharedPreferencesMode(self, mode: int): - self._shared_prefs_mode = mode - - @staticmethod - def getDefaultSharedPreferences(context: Any) -> Any: - mgr = PreferenceManager(context) - return mgr.getSharedPreferences() - - @staticmethod - def setDefaultValues(context: Any, res_id: int, read_again: bool): - pass - - @staticmethod - def setDefaultValues(context: Any, shared_prefs_name: str, - shared_prefs_mode: int, res_id: int, - read_again: bool): - pass - - def getPreference(self, key: str, default: Any = None) -> Any: - return self._prefs_data.get(key, default) - - def putPreference(self, key: str, value: Any): - self._prefs_data[key] = value - self._save() - - def removePreference(self, key: str): - if key in self._prefs_data: - del self._prefs_data[key] - self._save() - - -# ═══════════════════════════════════════════════════════════════ -# Preference -# ═══════════════════════════════════════════════════════════════ - -class Preference(Stub): - """Preference — 偏好设置项基类""" - - def __init__(self, context: Any = None, attrs: Any = None, - def_style_attr: int = 0): - super().__init__() - self._context = context - self._title: str = "" - self._summary: str = "" - self._key: Optional[str] = None - self._enabled: bool = True - self._default_value: Any = None - self._order: int = 0 - self._persistent: bool = True - self._icon: Any = None - self._dependents: List["Preference"] = [] - self._dependency_key: Optional[str] = None - self._preference_manager: Optional[PreferenceManager] = None - self._parent: Optional["PreferenceGroup"] = None - self._on_change_listener: Optional[PreferenceChangeListener] = None - self._on_click_listener: Optional[PreferenceOnPreferenceClickListener] = None - - def setTitle(self, title: str): - self._title = title - - def getTitle(self) -> str: - return self._title - - def setSummary(self, summary: str): - self._summary = summary - - def getSummary(self) -> str: - return self._summary - - def setKey(self, key: str): - self._key = key - - def getKey(self) -> Optional[str]: - return self._key - - def setEnabled(self, enabled: bool): - self._enabled = enabled - - def isEnabled(self) -> bool: - return self._enabled - - def setDefaultValue(self, value: Any): - self._default_value = value - - def setOnPreferenceClickListener(self, listener: PreferenceOnPreferenceClickListener): - self._on_click_listener = listener - - def setOnPreferenceChangeListener(self, listener: PreferenceChangeListener): - self._on_change_listener = listener - - def getSharedPreferences(self) -> Any: - if self._preference_manager: - return self._preference_manager.getSharedPreferences() - return {} - - def getEditor(self) -> Any: - if self._preference_manager: - return self._preference_manager - return None - - def shouldCommit(self) -> bool: - return True - - def shouldPersist(self) -> bool: - return self._persistent and self._key is not None - - def persistString(self, value: str) -> bool: - if self.shouldPersist() and self._preference_manager: - self._preference_manager.putPreference(self._key, value) - return True - return False - - def persistInt(self, value: int) -> bool: - if self.shouldPersist() and self._preference_manager: - self._preference_manager.putPreference(self._key, value) - return True - return False - - def persistBoolean(self, value: bool) -> bool: - if self.shouldPersist() and self._preference_manager: - self._preference_manager.putPreference(self._key, value) - return True - return False - - def persistFloat(self, value: float) -> bool: - if self.shouldPersist() and self._preference_manager: - self._preference_manager.putPreference(self._key, value) - return True - return False - - def persistLong(self, value: int) -> bool: - return self.persistInt(value) - - def getPersistedString(self, default_return: Optional[str] = None) -> Optional[str]: - if self.shouldPersist() and self._preference_manager: - val = self._preference_manager.getPreference(self._key, default_return) - return str(val) if val is not None else default_return - return default_return - - def getPersistedInt(self, default_return: int = 0) -> int: - if self.shouldPersist() and self._preference_manager: - return int(self._preference_manager.getPreference(self._key, default_return)) - return default_return - - def getPersistedBoolean(self, default_return: bool = False) -> bool: - if self.shouldPersist() and self._preference_manager: - val = self._preference_manager.getPreference(self._key, default_return) - if isinstance(val, bool): - return val - return str(val).lower() in ("true", "1", "yes") - return default_return - - def getPersistedFloat(self, default_return: float = 0.0) -> float: - if self.shouldPersist() and self._preference_manager: - return float(self._preference_manager.getPreference(self._key, default_return)) - return default_return - - def getPersistedLong(self, default_return: int = 0) -> int: - return self.getPersistedInt(default_return) - - def notifyChanged(self): - pass - - def notifyHierarchyChanged(self): - pass - - def onBindView(self, view: Any): - pass - - def onClick(self): - if self._on_click_listener: - self._on_click_listener.onPreferenceClick(self) - - def setOrder(self, order: int): - self._order = order - - def getOrder(self) -> int: - return self._order - - def setPersistent(self, persistent: bool): - self._persistent = persistent - - def isPersistent(self) -> bool: - return self._persistent - - def setDependency(self, dependency_key: str): - self._dependency_key = dependency_key - - def getDependency(self) -> Optional[str]: - return self._dependency_key - - def toString(self) -> str: - return f"Preference(key={self._key}, title={self._title})" - - def hashCode(self) -> int: - return hash(self._key) & 0x7FFFFFFF if self._key else 0 - - -# ═══════════════════════════════════════════════════════════════ -# PreferenceGroup -# ═══════════════════════════════════════════════════════════════ - -class PreferenceGroup(Preference): - """PreferenceGroup — 偏好组""" - - def __init__(self, context: Any = None, attrs: Any = None): - super().__init__(context, attrs) - self._preferences: List[Preference] = [] - - def addPreference(self, preference: Preference) -> bool: - if preference not in self._preferences: - self._preferences.append(preference) - preference._parent = self - return True - return False - - def removePreference(self, preference: Preference) -> bool: - if preference in self._preferences: - self._preferences.remove(preference) - preference._parent = None - return True - return False - - def removeAll(self): - for p in self._preferences: - p._parent = None - self._preferences.clear() - - def getPreference(self, index: int) -> Optional[Preference]: - if 0 <= index < len(self._preferences): - return self._preferences[index] - return None - - def getPreferenceCount(self) -> int: - return len(self._preferences) - - def findPreference(self, key: str) -> Optional[Preference]: - for p in self._preferences: - if p.getKey() == key: - return p - if isinstance(p, PreferenceGroup): - result = p.findPreference(key) - if result: - return result - return None - - def toString(self) -> str: - return f"PreferenceGroup(count={len(self._preferences)})" - - -# ═══════════════════════════════════════════════════════════════ -# PreferenceScreen -# ═══════════════════════════════════════════════════════════════ - -class PreferenceScreen(PreferenceGroup): - """PreferenceScreen — 根偏好屏幕""" - - def __init__(self, context: Any = None, attrs: Any = None): - super().__init__(context, attrs) - self._root: bool = True - - -# ═══════════════════════════════════════════════════════════════ -# PreferenceCategory -# ═══════════════════════════════════════════════════════════════ - -class PreferenceCategory(PreferenceGroup): - """PreferenceCategory — 偏好分类标题""" - - def __init__(self, context: Any = None, attrs: Any = None): - super().__init__(context, attrs) - - -# ═══════════════════════════════════════════════════════════════ -# TwoStatePreference -# ═══════════════════════════════════════════════════════════════ - -class TwoStatePreference(Preference): - """TwoStatePreference — 双状态偏好基类""" - - def __init__(self, context: Any = None, attrs: Any = None): - super().__init__(context, attrs) - self._checked: bool = False - self._summary_on: str = "" - self._summary_off: str = "" - self._disable_dependents_state: bool = False - - def isChecked(self) -> bool: - return self._checked - - def setChecked(self, checked: bool): - self._checked = checked - self.persistBoolean(checked) - - def setSummaryOn(self, summary: str): - self._summary_on = summary - - def setSummaryOff(self, summary: str): - self._summary_off = summary - - def setDisableDependentsState(self, state: bool): - self._disable_dependents_state = state - - -# ═══════════════════════════════════════════════════════════════ -# CheckBoxPreference -# ═══════════════════════════════════════════════════════════════ - -class CheckBoxPreference(TwoStatePreference): - """CheckBoxPreference — 复选框偏好""" - - def __init__(self, context: Any = None, attrs: Any = None): - super().__init__(context, attrs) - - -# ═══════════════════════════════════════════════════════════════ -# SwitchPreference -# ═══════════════════════════════════════════════════════════════ - -class SwitchPreference(TwoStatePreference): - """SwitchPreference — 开关偏好""" - - def __init__(self, context: Any = None, attrs: Any = None): - super().__init__(context, attrs) - self._switch_text_on: str = "" - self._switch_text_off: str = "" - - def setSwitchTextOn(self, text: str): - self._switch_text_on = text - - def setSwitchTextOff(self, text: str): - self._switch_text_off = text - - def getSwitchTextOn(self) -> str: - return self._switch_text_on - - def getSwitchTextOff(self) -> str: - return self._switch_text_off - - -# ═══════════════════════════════════════════════════════════════ -# DialogPreference -# ═══════════════════════════════════════════════════════════════ - -class DialogPreference(Preference): - """DialogPreference — 对话框偏好基类""" - - def __init__(self, context: Any = None, attrs: Any = None): - super().__init__(context, attrs) - self._dialog_title: str = "" - self._dialog_message: str = "" - self._dialog_icon: Any = None - self._positive_button_text: str = "" - self._negative_button_text: str = "" - - -# ═══════════════════════════════════════════════════════════════ -# EditTextPreference -# ═══════════════════════════════════════════════════════════════ - -class EditTextPreference(DialogPreference): - """EditTextPreference — 文本编辑偏好""" - - def __init__(self, context: Any = None, attrs: Any = None): - super().__init__(context, attrs) - self._text: str = "" - - def getText(self) -> str: - return self._text - - def setText(self, text: str): - self._text = text - self.persistString(text) - - -# ═══════════════════════════════════════════════════════════════ -# ListPreference -# ═══════════════════════════════════════════════════════════════ - -class ListPreference(DialogPreference): - """ListPreference — 列表偏好""" - - def __init__(self, context: Any = None, attrs: Any = None): - super().__init__(context, attrs) - self._entries: List[str] = [] - self._entry_values: List[str] = [] - self._value: str = "" - - def getEntries(self) -> List[str]: - return list(self._entries) - - def setEntries(self, entries: List[str]): - self._entries = list(entries) - - def getEntryValues(self) -> List[str]: - return list(self._entry_values) - - def setEntryValues(self, values: List[str]): - self._entry_values = list(values) - - def getValue(self) -> str: - return self._value - - def setValue(self, value: str): - self._value = value - self.persistString(value) - - def getEntry(self) -> Optional[str]: - try: - idx = self._entry_values.index(self._value) - if 0 <= idx < len(self._entries): - return self._entries[idx] - except ValueError: - pass - return None - - def getSummary(self) -> str: - entry = self.getEntry() - if entry: - return entry - return self._summary - - def setDefaultValue(self, value: Any): - super().setDefaultValue(value) - - -# ═══════════════════════════════════════════════════════════════ -# MultiSelectListPreference -# ═══════════════════════════════════════════════════════════════ - -class MultiSelectListPreference(DialogPreference): - """MultiSelectListPreference — 多选列表偏好""" - - def __init__(self, context: Any = None, attrs: Any = None): - super().__init__(context, attrs) - self._entries: List[str] = [] - self._entry_values: List[str] = [] - self._values: Set[str] = set() - - def getEntries(self) -> List[str]: - return list(self._entries) - - def setEntries(self, entries: List[str]): - self._entries = list(entries) - - def getEntryValues(self) -> List[str]: - return list(self._entry_values) - - def setEntryValues(self, values: List[str]): - self._entry_values = list(values) - - def getValues(self) -> Set[str]: - return set(self._values) - - def setValues(self, values: Set[str]): - self._values = set(values) - - -# ═══════════════════════════════════════════════════════════════ -# RingtonePreference / SeekBarPreference / DropDownPreference -# ═══════════════════════════════════════════════════════════════ - -class RingtonePreference(Preference): - """RingtonePreference — 铃声偏好""" - - def __init__(self, context: Any = None, attrs: Any = None): - super().__init__(context, attrs) - self._ringtone_type: int = 1 - self._show_default: bool = True - self._show_silent: bool = True - - -class SeekBarPreference(Preference): - """SeekBarPreference — 滑块偏好""" - - def __init__(self, context: Any = None, attrs: Any = None): - super().__init__(context, attrs) - self._min: int = 0 - self._max: int = 100 - self._current: int = 50 - self._show_progress: bool = True - self._adjustable: bool = True - - def getMin(self) -> int: - return self._min - - def setMin(self, min_val: int): - self._min = min_val - - def getMax(self) -> int: - return self._max - - def setMax(self, max_val: int): - self._max = max_val - - def getValue(self) -> int: - return self._current - - def setValue(self, value: int): - self._current = value - self.persistInt(value) - - -class DropDownPreference(ListPreference): - """DropDownPreference — 下拉列表偏好""" - - def __init__(self, context: Any = None, attrs: Any = None): - super().__init__(context, attrs) - - -# ═══════════════════════════════════════════════════════════════ -# PreferenceFragment / PreferenceFragmentCompat -# ═══════════════════════════════════════════════════════════════ - -class PreferenceFragment(Stub): - """PreferenceFragment — 偏好片段""" - - def __init__(self): - super().__init__() - self._preference_manager: Optional[PreferenceManager] = None - - def onCreate(self, savedInstanceState: Any): - pass - - def onCreatePreferences(self, savedInstanceState: Any, root_key: Optional[str]): - pass - - def addPreferencesFromResource(self, res_id: int): - pass - - def setPreferenceScreen(self, screen: PreferenceScreen): - pass - - def getPreferenceScreen(self) -> Optional[PreferenceScreen]: - return None - - def findPreference(self, key: str) -> Optional[Preference]: - screen = self.getPreferenceScreen() - if screen: - return screen.findPreference(key) - return None - - -class PreferenceFragmentCompat(PreferenceFragment): - """PreferenceFragmentCompat — 兼容偏好片段""" - - def __init__(self): - super().__init__() - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Preference", - "PreferenceScreen", - "PreferenceCategory", - "PreferenceManager", - "CheckBoxPreference", - "SwitchPreference", - "ListPreference", - "EditTextPreference", - "MultiSelectListPreference", - "PreferenceFragment", - "PreferenceFragmentCompat", - "PreferenceGroup", - "PreferenceChangeListener", - "PreferenceOnPreferenceClickListener", - "TwoStatePreference", - "DialogPreference", - "RingtonePreference", - "SeekBarPreference", - "DropDownPreference", -] \ No newline at end of file diff --git a/android_pyapi/android/print.py b/android_pyapi/android/print.py deleted file mode 100644 index 7dd47ae..0000000 --- a/android_pyapi/android/print.py +++ /dev/null @@ -1,581 +0,0 @@ -""" -Android Print 组件 — PrintManager, PrintJob, PrintDocumentAdapter, PdfPrint 等 -方法签名完全对齐 Android SDK (android.print 包) -""" - -import os -import time -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# PrintAttributes -# ═══════════════════════════════════════════════════════════════ - -class PrintAttributes(Stub): - """PrintAttributes — 打印属性""" - - # 颜色模式 - COLOR_MODE_MONOCHROME = 1 - COLOR_MODE_COLOR = 2 - - # 方向 - ORIENTATION_PORTRAIT = 1 - ORIENTATION_LANDSCAPE = 2 - - # 媒质尺寸 ID - MEDIA_SIZE_UNKNOWN = "unknown" - MEDIA_SIZE_A4 = "A4" - MEDIA_SIZE_A3 = "A3" - MEDIA_SIZE_LETTER = "letter" - MEDIA_SIZE_LEGAL = "legal" - MEDIA_SIZE_ISO_A4 = "iso_a4" - MEDIA_SIZE_ISO_A3 = "iso_a3" - MEDIA_SIZE_NA_LETTER = "na_letter" - - def __init__(self): - super().__init__() - self._media_size: Optional["PrintAttributes.MediaSize"] = None - self._resolution: Optional["PrintAttributes.Resolution"] = None - self._min_margins: Optional["PrintAttributes.Margins"] = None - self._color_mode: int = PrintAttributes.COLOR_MODE_COLOR - self._orientation: int = PrintAttributes.ORIENTATION_PORTRAIT - - def getMediaSize(self) -> Optional["MediaSize"]: - return self._media_size - - def getResolution(self) -> Optional["Resolution"]: - return self._resolution - - def getMinMargins(self) -> Optional["Margins"]: - return self._min_margins - - def getColorMode(self) -> int: - return self._color_mode - - def getOrientation(self) -> int: - return self._orientation - - def isPortrait(self) -> bool: - return self._orientation == PrintAttributes.ORIENTATION_PORTRAIT - - def toString(self) -> str: - return (f"PrintAttributes(colorMode={self._color_mode}, " - f"orientation={self._orientation})") - - def hashCode(self) -> int: - return hash((self._color_mode, self._orientation)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, PrintAttributes): - return False - return (self._color_mode == other._color_mode - and self._orientation == other._orientation) - - # ── 内部嵌套类 ── - - class MediaSize(Stub): - """MediaSize — 媒质尺寸""" - - def __init__(self, id: str, label: str, width_mils: int, height_mils: int): - super().__init__() - self._id = id - self._label = label - self._width_mils = width_mils - self._height_mils = height_mils - - def getId(self) -> str: - return self._id - - def getLabel(self, package_manager: Any = None) -> str: - return self._label - - def getWidthMils(self) -> int: - return self._width_mils - - def getHeightMils(self) -> int: - return self._height_mils - - def asPortrait(self) -> "PrintAttributes.MediaSize": - if self._width_mils > self._height_mils: - return PrintAttributes.MediaSize( - self._id, self._label, self._height_mils, self._width_mils) - return self - - def asLandscape(self) -> "PrintAttributes.MediaSize": - if self._width_mils < self._height_mils: - return PrintAttributes.MediaSize( - self._id, self._label, self._height_mils, self._width_mils) - return self - - def toString(self) -> str: - return f"MediaSize({self._id}: {self._width_mils}x{self._height_mils}mils)" - - def hashCode(self) -> int: - return hash(self._id) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, PrintAttributes.MediaSize): - return False - return self._id == other._id - - class Resolution(Stub): - """Resolution — 打印分辨率""" - - def __init__(self, id: str, label: str, horizontal_dpi: int, vertical_dpi: int): - super().__init__() - self._id = id - self._label = label - self._horizontal_dpi = horizontal_dpi - self._vertical_dpi = vertical_dpi - - def getId(self) -> str: - return self._id - - def getLabel(self) -> str: - return self._label - - def getHorizontalDpi(self) -> int: - return self._horizontal_dpi - - def getVerticalDpi(self) -> int: - return self._vertical_dpi - - def toString(self) -> str: - return f"Resolution({self._id}: {self._horizontal_dpi}x{self._vertical_dpi}dpi)" - - class Margins(Stub): - """Margins — 页边距""" - - NO_MARGINS: "PrintAttributes.Margins" = None - - def __init__(self, left_mils: int, top_mils: int, - right_mils: int, bottom_mils: int): - super().__init__() - self._left_mils = left_mils - self._top_mils = top_mils - self._right_mils = right_mils - self._bottom_mils = bottom_mils - - def getLeftMils(self) -> int: - return self._left_mils - - def getTopMils(self) -> int: - return self._top_mils - - def getRightMils(self) -> int: - return self._right_mils - - def getBottomMils(self) -> int: - return self._bottom_mils - - def toString(self) -> str: - return f"Margins({self._left_mils},{self._top_mils},{self._right_mils},{self._bottom_mils})" - - class Builder(Stub): - """Builder — PrintAttributes 构建器""" - - def __init__(self): - super().__init__() - self._attrs = PrintAttributes() - - def setMediaSize(self, media_size: "PrintAttributes.MediaSize") -> "PrintAttributes.Builder": - self._attrs._media_size = media_size - return self - - def setResolution(self, resolution: "PrintAttributes.Resolution") -> "PrintAttributes.Builder": - self._attrs._resolution = resolution - return self - - def setMinMargins(self, margins: "PrintAttributes.Margins") -> "PrintAttributes.Builder": - self._attrs._min_margins = margins - return self - - def setColorMode(self, color_mode: int) -> "PrintAttributes.Builder": - self._attrs._color_mode = color_mode - return self - - def setOrientation(self, orientation: int) -> "PrintAttributes.Builder": - self._attrs._orientation = orientation - return self - - def build(self) -> "PrintAttributes": - return self._attrs - - -PrintAttributes.Margins.NO_MARGINS = PrintAttributes.Margins(0, 0, 0, 0) - - -# ═══════════════════════════════════════════════════════════════ -# PrintJobInfo -# ═══════════════════════════════════════════════════════════════ - -class PrintJobInfo(Stub): - """PrintJobInfo — 打印任务信息""" - - # 状态常量 - STATE_CREATED = 1 - STATE_QUEUED = 2 - STATE_STARTED = 3 - STATE_BLOCKED = 4 - STATE_COMPLETED = 5 - STATE_FAILED = 6 - STATE_CANCELED = 7 - - def __init__(self): - super().__init__() - self._label: str = "" - self._id: Optional[Any] = None - self._state: int = PrintJobInfo.STATE_CREATED - self._attributes: Optional[PrintAttributes] = None - self._copies: int = 1 - self._creation_time: int = int(time.time() * 1000) - self._canceling: bool = False - self._pages: Optional[List[int]] = None - self._has_advanced_options: bool = False - - def getLabel(self) -> str: - return self._label - - def setLabel(self, label: str): - self._label = label - - def getId(self) -> Optional[Any]: - return self._id - - def getState(self) -> int: - return self._state - - def setState(self, state: int): - self._state = state - - def getAttributes(self) -> Optional[PrintAttributes]: - return self._attributes - - def setAttributes(self, attributes: PrintAttributes): - self._attributes = attributes - - def getCopies(self) -> int: - return self._copies - - def setCopies(self, copies: int): - self._copies = copies - - def getCreationTime(self) -> int: - return self._creation_time - - def isCanceling(self) -> bool: - return self._canceling - - def getPages(self) -> Optional[List[int]]: - return self._pages - - def setPages(self, pages: Optional[List[int]]): - self._pages = pages - - def hasAdvancedOptions(self) -> bool: - return self._has_advanced_options - - def toString(self) -> str: - return f"PrintJobInfo(label={self._label}, state={self._state}, copies={self._copies})" - - def hashCode(self) -> int: - return hash(self._id) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, PrintJobInfo): - return False - return self._id == other._id - - # ── Builder ── - - class Builder(Stub): - """Builder — PrintJobInfo 构建器""" - - def __init__(self, info: Optional["PrintJobInfo"] = None): - super().__init__() - self._info = PrintJobInfo() - if info: - self._info._label = info._label - self._info._id = info._id - self._info._attributes = info._attributes - self._info._copies = info._copies - - def setCopies(self, copies: int) -> "PrintJobInfo.Builder": - self._info._copies = copies - return self - - def setAttributes(self, attributes: PrintAttributes) -> "PrintJobInfo.Builder": - self._info._attributes = attributes - return self - - def setLabel(self, label: str) -> "PrintJobInfo.Builder": - self._info._label = label - return self - - def setPages(self, pages: Optional[List[int]]) -> "PrintJobInfo.Builder": - self._info._pages = pages - return self - - def build(self) -> "PrintJobInfo": - return self._info - - -# ═══════════════════════════════════════════════════════════════ -# PrintJob -# ═══════════════════════════════════════════════════════════════ - -class PrintJob(Stub): - """PrintJob — 打印任务""" - - def __init__(self, info: PrintJobInfo): - super().__init__() - self._info = info - self._cancelled = False - self._result: Optional[Any] = None - - def getId(self) -> Optional[Any]: - return self._info.getId() - - def getInfo(self) -> PrintJobInfo: - return self._info - - def isStarted(self) -> bool: - return self._info.getState() >= PrintJobInfo.STATE_STARTED - - def isBlocked(self) -> bool: - return self._info.getState() == PrintJobInfo.STATE_BLOCKED - - def isCompleted(self) -> bool: - return self._info.getState() == PrintJobInfo.STATE_COMPLETED - - def isFailed(self) -> bool: - return self._info.getState() == PrintJobInfo.STATE_FAILED - - def isCancelled(self) -> bool: - return self._cancelled - - def cancel(self): - """取消打印任务""" - self._cancelled = True - self._info.setState(PrintJobInfo.STATE_CANCELED) - - def restart(self): - """重新启动打印任务""" - self._cancelled = False - self._info.setState(PrintJobInfo.STATE_QUEUED) - - def setResult(self, result: Any): - self._result = result - - def getResult(self) -> Optional[Any]: - return self._result - - def toString(self) -> str: - return f"PrintJob(id={self.getId()}, state={self._info.getState()})" - - def hashCode(self) -> int: - return hash(self.getId()) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, PrintJob): - return False - return self.getId() == other.getId() - - -# ═══════════════════════════════════════════════════════════════ -# PrintDocumentAdapter -# ═══════════════════════════════════════════════════════════════ - -class PrintDocumentAdapter(Stub): - """PrintDocumentAdapter — 打印文档适配器""" - - EXTRA_PRINT_PREVIEW = "EXTRA_PRINT_PREVIEW" - - def __init__(self): - super().__init__() - - def onStart(self): - """打印开始回调""" - pass - - def onFinish(self): - """打印结束回调""" - pass - - def onLayout(self, old_attributes: PrintAttributes, new_attributes: PrintAttributes, - cancellation_signal: Any, callback: "PrintDocumentAdapterLayoutCallback", - metadata: Any): - """布局回调 - 桌面实现直接完成""" - if callback: - callback.onLayoutFinished(self, new_attributes) - - def onWrite(self, pages: List[int], destination: Any, - cancellation_signal: Any, - callback: "PrintDocumentAdapterWriteCallback"): - """写入回调 - 桌面实现直接完成""" - if callback: - callback.onWriteFinished(pages) - - -class PrintDocumentAdapterLayoutCallback(Stub): - """PrintDocumentAdapterLayoutCallback — 布局回调""" - - def __init__(self): - super().__init__() - - def onLayoutFinished(self, adapter: PrintDocumentAdapter, attributes: PrintAttributes): - """布局完成""" - pass - - def onLayoutFailed(self, error: Optional[str] = None): - """布局失败""" - pass - - def onLayoutCancelled(self): - """布局取消""" - pass - - -class PrintDocumentAdapterWriteCallback(Stub): - """PrintDocumentAdapterWriteCallback — 写入回调""" - - def __init__(self): - super().__init__() - - def onWriteFinished(self, pages: List[int]): - """写入完成""" - pass - - def onWriteFailed(self, error: Optional[str] = None): - """写入失败""" - pass - - def onWriteCancelled(self): - """写入取消""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# PdfPrint -# ═══════════════════════════════════════════════════════════════ - -class PdfPrint(Stub): - """PdfPrint — PDF 打印辅助类""" - - def __init__(self, attributes: Optional[PrintAttributes] = None): - super().__init__() - self._attributes = attributes or PrintAttributes() - - def getAttributes(self) -> PrintAttributes: - return self._attributes - - def setAttributes(self, attributes: PrintAttributes): - self._attributes = attributes - - def print(self, print_adapter: PrintDocumentAdapter, output_path: str) -> bool: - """执行 PDF 打印 (桌面实现: 写入文件)""" - try: - os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True) - # 在桌面环境, PDF 打印由适配器的 onWrite 实现处理 - callback = PrintDocumentAdapterWriteCallback() - print_adapter.onWrite([1], output_path, None, callback) - return True - except Exception: - return False - - def toString(self) -> str: - return f"PdfPrint(attributes={self._attributes})" - - def hashCode(self) -> int: - return hash(str(self._attributes)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, PdfPrint): - return False - return str(self._attributes) == str(other._attributes) - - -# ═══════════════════════════════════════════════════════════════ -# PrintManager -# ═══════════════════════════════════════════════════════════════ - -class PrintManager(Stub): - """PrintManager — 打印管理器""" - - # 打印任务 ID 生成 - _next_job_id = 1 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._print_jobs: Dict[str, PrintJob] = {} - - def getPrintJobs(self) -> List[PrintJob]: - """获取打印任务列表""" - return list(self._print_jobs.values()) - - def print(self, job_name: str, document_adapter: PrintDocumentAdapter, - attributes: Optional[PrintAttributes] = None) -> PrintJob: - """提交打印任务""" - info = PrintJobInfo() - info.setLabel(job_name) - info.setAttributes(attributes or PrintAttributes()) - info.setState(PrintJobInfo.STATE_QUEUED) - - job_id = f"print_job_{PrintManager._next_job_id}" - PrintManager._next_job_id += 1 - - job = PrintJob(info) - self._print_jobs[job_id] = job - - # 自动触发布局和写入 - cancellation_signal = type("CancellationSignal", (), {"isCanceled": lambda: False})() - document_adapter.onStart() - layout_cb = PrintDocumentAdapterLayoutCallback() - document_adapter.onLayout(None, info.getAttributes(), - cancellation_signal, layout_cb, None) - - info.setState(PrintJobInfo.STATE_COMPLETED) - document_adapter.onFinish() - - return job - - def getPrintJobById(self, job_id: str) -> Optional[PrintJob]: - """根据 ID 获取打印任务""" - return self._print_jobs.get(job_id) - - def cancelPrintJob(self, job_id: str) -> bool: - """取消打印任务""" - job = self._print_jobs.get(job_id) - if job: - job.cancel() - return True - return False - - def toString(self) -> str: - return f"PrintManager(jobs={len(self._print_jobs)})" - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "PrintManager", - "PrintJob", - "PrintJobInfo", - "PrintDocumentAdapter", - "PrintAttributes", - "PrintDocumentAdapterLayoutCallback", - "PrintDocumentAdapterWriteCallback", - "PdfPrint", -] \ No newline at end of file diff --git a/android_pyapi/android/printservice.py b/android_pyapi/android/printservice.py deleted file mode 100644 index d13a35f..0000000 --- a/android_pyapi/android/printservice.py +++ /dev/null @@ -1,917 +0,0 @@ -""" -Android Print Service 组件 — PrintService, PrintJob, PrinterDiscoverySession, PrinterId 等 -方法签名完全对齐 Android SDK (android.printservice 包) -""" - -from __future__ import annotations - -import os -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# PrinterId -# ═══════════════════════════════════════════════════════════════ - -class PrinterId(Stub): - """PrinterId — 打印机 ID""" - - def __init__(self, package_name: str = "", local_id: str = ""): - super().__init__() - self._package_name = package_name - self._local_id = local_id - - def getPackageName(self) -> str: - return self._package_name - - def setPackageName(self, pkg: str): - self._package_name = pkg - - def getLocalId(self) -> str: - return self._local_id - - def setLocalId(self, local_id: str): - self._local_id = local_id - - def toString(self) -> str: - return f"PrinterId(package={self._package_name}, localId={self._local_id})" - - def equals(self, other: Any) -> bool: - if isinstance(other, PrinterId): - return (self._package_name == other._package_name - and self._local_id == other._local_id) - return False - - def hashCode(self) -> int: - return hash((self._package_name, self._local_id)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# PrinterInfo -# ═══════════════════════════════════════════════════════════════ - -class PrinterInfo(Stub): - """PrinterInfo — 打印机信息""" - - STATUS_IDLE = 0 - STATUS_BUSY = 1 - STATUS_UNAVAILABLE = 2 - - STATUS_DISABLED = 3 - STATUS_LOW_ON_PAPER = 4 - STATUS_LOW_ON_INK = 5 - STATUS_LOW_ON_TONER = 6 - STATUS_WAITING = 7 - STATUS_PRINTING = 8 - STATUS_ERROR = 9 - - def __init__(self, printer_id: PrinterId = None, name: str = "", - status: int = STATUS_IDLE): - super().__init__() - self._id = printer_id or PrinterId() - self._name = name - self._status = status - self._description: str = "" - self._capabilities: Optional[PrinterCapabilities] = None - self._icon: Optional[bytes] = None - self._is_default: bool = False - self._printer_location: str = "" - self._printer_model: str = "" - self._printer_make_and_model: str = "" - - def getId(self) -> PrinterId: - return self._id - - def setId(self, printer_id: PrinterId): - self._id = printer_id - - def getName(self) -> str: - return self._name - - def setName(self, name: str): - self._name = name - - def getStatus(self) -> int: - return self._status - - def setStatus(self, status: int): - self._status = status - - def getDescription(self) -> str: - return self._description - - def setDescription(self, desc: str): - self._description = desc - - def getCapabilities(self) -> Optional[PrinterCapabilities]: - return self._capabilities - - def setCapabilities(self, caps: PrinterCapabilities): - self._capabilities = caps - - def getIcon(self) -> Optional[bytes]: - return self._icon - - def setIcon(self, icon: bytes): - self._icon = icon - - def isDefault(self) -> bool: - return self._is_default - - def setDefault(self, is_default: bool): - self._is_default = is_default - - def toString(self) -> str: - return f"PrinterInfo(name={self._name}, status={self._status})" - - -# ═══════════════════════════════════════════════════════════════ -# PrinterCapabilities -# ═══════════════════════════════════════════════════════════════ - -class PrinterCapabilities(Stub): - """PrinterCapabilities — 打印机能力""" - - def __init__(self): - super().__init__() - self._color_supported: bool = False - self._duplex_supported: bool = False - self._media_sizes: List[MediaSize] = [] - self._resolutions: List[Resolution] = [] - self._min_margins: Margins = Margins(0, 0, 0, 0) - self._paper_sizes: List[PaperSize] = [] - - def isColorSupported(self) -> bool: - return self._color_supported - - def setColorSupported(self, supported: bool): - self._color_supported = supported - - def isDuplexSupported(self) -> bool: - return self._duplex_supported - - def setDuplexSupported(self, supported: bool): - self._duplex_supported = supported - - def getMediaSizes(self) -> List[MediaSize]: - return list(self._media_sizes) - - def setMediaSizes(self, sizes: List[MediaSize]): - self._media_sizes = list(sizes) - - def getResolutions(self) -> List[Resolution]: - return list(self._resolutions) - - def setResolutions(self, resolutions: List[Resolution]): - self._resolutions = list(resolutions) - - def getMinMargins(self) -> Margins: - return self._min_margins - - def setMinMargins(self, margins: Margins): - self._min_margins = margins - - def toString(self) -> str: - return f"PrinterCapabilities(color={self._color_supported}, duplex={self._duplex_supported})" - - -# ═══════════════════════════════════════════════════════════════ -# MediaSize / Resolution / Margins / PaperSize -# ═══════════════════════════════════════════════════════════════ - -class MediaSize(Stub): - """MediaSize — 打印介质尺寸""" - - def __init__(self, id: str = "", label: str = "", width: int = 0, height: int = 0): - super().__init__() - self._id = id - self._label = label - self._width = width - self._height = height - - def getId(self) -> str: - return self._id - - def setId(self, id: str): - self._id = id - - def getLabel(self) -> str: - return self._label - - def setLabel(self, label: str): - self._label = label - - def getWidth(self) -> int: - return self._width - - def setWidth(self, width: int): - self._width = width - - def getHeight(self) -> int: - return self._height - - def setHeight(self, height: int): - self._height = height - - def toString(self) -> str: - return f"MediaSize(id={self._id}, {self._width}x{self._height})" - - -class Resolution(Stub): - """Resolution — 打印分辨率""" - - def __init__(self, id: str = "", label: str = "", - horizontal_dpi: int = 300, vertical_dpi: int = 300): - super().__init__() - self._id = id - self._label = label - self._horizontal_dpi = horizontal_dpi - self._vertical_dpi = vertical_dpi - - def getId(self) -> str: - return self._id - - def getLabel(self) -> str: - return self._label - - def getHorizontalDpi(self) -> int: - return self._horizontal_dpi - - def getVerticalDpi(self) -> int: - return self._vertical_dpi - - def toString(self) -> str: - return f"Resolution(id={self._id}, dpi={self._horizontal_dpi}x{self._vertical_dpi})" - - -class Margins(Stub): - """Margins — 打印边距""" - - def __init__(self, left: int = 0, top: int = 0, right: int = 0, bottom: int = 0): - super().__init__() - self._left = left - self._top = top - self._right = right - self._bottom = bottom - - def getLeft(self) -> int: - return self._left - - def getTop(self) -> int: - return self._top - - def getRight(self) -> int: - return self._right - - def getBottom(self) -> int: - return self._bottom - - def toString(self) -> str: - return f"Margins(l={self._left}, t={self._top}, r={self._right}, b={self._bottom})" - - -class PaperSize(Stub): - """PaperSize — 纸张尺寸""" - - A0 = 1 - A1 = 2 - A2 = 3 - A3 = 4 - A4 = 5 - A5 = 6 - LETTER = 7 - LEGAL = 8 - TABLOID = 9 - ENVELOPE = 10 - PHOTO = 11 - - def __init__(self, id: int = A4, label: str = "A4", - width: int = 210000, height: int = 297000): - super().__init__() - self._id = id - self._label = label - self._width = width - self._height = height - - def getId(self) -> int: - return self._id - - def getLabel(self) -> str: - return self._label - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def toString(self) -> str: - return f"PaperSize(id={self._id}, {self._label})" - - -# ═══════════════════════════════════════════════════════════════ -# PrintJobInfo -# ═══════════════════════════════════════════════════════════════ - -class PrintJobInfo(Stub): - """PrintJobInfo — 打印作业信息""" - - STATE_CREATED = 1 - STATE_QUEUED = 2 - STATE_STARTED = 3 - STATE_BLOCKED = 4 - STATE_COMPLETED = 5 - STATE_FAILED = 6 - STATE_CANCELED = 7 - - def __init__(self, job_id: int = 0, label: str = ""): - super().__init__() - self._job_id = job_id - self._label = label - self._state: int = PrintJobInfo.STATE_CREATED - self._printer: Optional[PrinterId] = None - self._creation_time: int = int(time.time() * 1000) - self._copies: int = 1 - self._page_count: int = 0 - self._media_size: Optional[MediaSize] = None - self._resolution: Optional[Resolution] = None - self._is_color: bool = True - self._is_duplex: bool = False - self._progress: float = 0.0 - self._status: int = 0 - self._status_string: str = "" - - def getJobId(self) -> int: - return self._job_id - - def setJobId(self, job_id: int): - self._job_id = job_id - - def getLabel(self) -> str: - return self._label - - def setLabel(self, label: str): - self._label = label - - def getState(self) -> int: - return self._state - - def setState(self, state: int): - self._state = state - - def getPrinterId(self) -> Optional[PrinterId]: - return self._printer - - def setPrinterId(self, printer_id: PrinterId): - self._printer = printer_id - - def getCreationTime(self) -> int: - return self._creation_time - - def setCreationTime(self, time_ms: int): - self._creation_time = time_ms - - def getCopies(self) -> int: - return self._copies - - def setCopies(self, copies: int): - self._copies = copies - - def getPageCount(self) -> int: - return self._page_count - - def setPageCount(self, count: int): - self._page_count = count - - def getMediaSize(self) -> Optional[MediaSize]: - return self._media_size - - def setMediaSize(self, size: MediaSize): - self._media_size = size - - def getResolution(self) -> Optional[Resolution]: - return self._resolution - - def setResolution(self, resolution: Resolution): - self._resolution = resolution - - def isColor(self) -> bool: - return self._is_color - - def setColor(self, color: bool): - self._is_color = color - - def isDuplex(self) -> bool: - return self._is_duplex - - def setDuplex(self, duplex: bool): - self._is_duplex = duplex - - def getProgress(self) -> float: - return self._progress - - def setProgress(self, progress: float): - self._progress = progress - - def getStatus(self) -> int: - return self._status - - def setStatus(self, status: int): - self._status = status - - def getStatusString(self) -> str: - return self._status_string - - def setStatusString(self, status_str: str): - self._status_string = status_str - - def toString(self) -> str: - return f"PrintJobInfo(id={self._job_id}, label={self._label}, state={self._state})" - - -# ═══════════════════════════════════════════════════════════════ -# PrintJob -# ═══════════════════════════════════════════════════════════════ - -class PrintJob(Stub): - """PrintJob — 打印作业""" - - def __init__(self, job_id: int = 0, label: str = ""): - super().__init__() - self._info = PrintJobInfo(job_id, label) - self._document: Optional[PrintDocument] = None - self._is_cancelled: bool = False - self._is_queued: bool = False - self._advanced_options: Dict[str, Any] = {} - - def getId(self) -> int: - return self._info.getJobId() - - def getInfo(self) -> PrintJobInfo: - return self._info - - def setInfo(self, info: PrintJobInfo): - self._info = info - - def getDocument(self) -> Optional[PrintDocument]: - return self._document - - def setDocument(self, document: PrintDocument): - self._document = document - - def isCancelled(self) -> bool: - return self._is_cancelled - - def isQueued(self) -> bool: - return self._is_queued - - def start(self): - """开始打印作业""" - self._info.setState(PrintJobInfo.STATE_STARTED) - - def block(self, reason: str): - """阻塞打印作业""" - self._info.setState(PrintJobInfo.STATE_BLOCKED) - self._info.setStatusString(reason) - - def complete(self): - """完成打印作业""" - self._info.setState(PrintJobInfo.STATE_COMPLETED) - self._info.setProgress(1.0) - - def fail(self, error: str): - """打印作业失败""" - self._info.setState(PrintJobInfo.STATE_FAILED) - self._info.setStatusString(error) - - def cancel(self): - """取消打印作业""" - self._info.setState(PrintJobInfo.STATE_CANCELED) - self._is_cancelled = True - - def setProgress(self, progress: float): - """设置进度""" - self._info.setProgress(max(0.0, min(1.0, progress))) - - def getAdvancedOptions(self) -> Dict[str, Any]: - return dict(self._advanced_options) - - def setAdvancedOption(self, key: str, value: Any): - self._advanced_options[key] = value - - def toString(self) -> str: - return f"PrintJob(id={self.getId()}, state={self._info.getState()})" - - -# ═══════════════════════════════════════════════════════════════ -# PrintDocument -# ═══════════════════════════════════════════════════════════════ - -class PrintDocument(Stub): - """PrintDocument — 打印文档""" - - def __init__(self, data: bytes = b"", mime_type: str = "application/pdf"): - super().__init__() - self._data = data - self._mime_type = mime_type - self._page_count: int = 0 - self._title: str = "" - self._content_uri: str = "" - self._file_path: str = "" - - def getData(self) -> bytes: - return self._data - - def setData(self, data: bytes): - self._data = data - - def getMimeType(self) -> str: - return self._mime_type - - def setMimeType(self, mime_type: str): - self._mime_type = mime_type - - def getPageCount(self) -> int: - return self._page_count - - def setPageCount(self, count: int): - self._page_count = count - - def getTitle(self) -> str: - return self._title - - def setTitle(self, title: str): - self._title = title - - def getContentUri(self) -> str: - return self._content_uri - - def setContentUri(self, uri: str): - self._content_uri = uri - - def getFilePath(self) -> str: - return self._file_path - - def setFilePath(self, file_path: str): - self._file_path = file_path - - def toString(self) -> str: - return f"PrintDocument(title={self._title}, pages={self._page_count})" - - -# ═══════════════════════════════════════════════════════════════ -# PrinterDiscoverySession -# ═══════════════════════════════════════════════════════════════ - -class PrinterDiscoverySession(Stub): - """PrinterDiscoverySession — 打印机发现会话""" - - def __init__(self): - super().__init__() - self._printers: Dict[PrinterId, PrinterInfo] = {} - self._tracked_printers: Set[PrinterId] = set() - self._is_discovering: bool = False - self._is_destroyed: bool = False - - def onStartPrinterDiscovery(self, priority_list: Optional[List[PrinterId]]): - """开始打印机发现""" - self._is_discovering = True - - def onStopPrinterDiscovery(self): - """停止打印机发现""" - self._is_discovering = False - - def onValidatePrinters(self, printer_ids: List[PrinterId]): - """验证打印机""" - pass - - def onStartPrinterStateTracking(self, printer_id: PrinterId): - """开始跟踪打印机状态""" - self._tracked_printers.add(printer_id) - - def onStopPrinterStateTracking(self, printer_id: PrinterId): - """停止跟踪打印机状态""" - self._tracked_printers.discard(printer_id) - - def onDestroy(self): - """销毁会话""" - self._is_destroyed = True - self._printers.clear() - self._tracked_printers.clear() - self._is_discovering = False - - def addPrinters(self, printers: List[PrinterInfo]): - """添加打印机""" - for p in printers: - self._printers[p.getId()] = p - - def removePrinters(self, printer_ids: List[PrinterId]): - """移除打印机""" - for pid in printer_ids: - self._printers.pop(pid, None) - - def getPrinters(self) -> List[PrinterInfo]: - """获取打印机列表""" - return list(self._printers.values()) - - def isDestroyed(self) -> bool: - return self._is_destroyed - - def isPrinterDiscoveryStarted(self) -> bool: - return self._is_discovering - - def getTrackedPrinters(self) -> Set[PrinterId]: - return set(self._tracked_printers) - - def toString(self) -> str: - return f"PrinterDiscoverySession(printers={len(self._printers)})" - - -# ═══════════════════════════════════════════════════════════════ -# CustomPrinterIconCallback -# ═══════════════════════════════════════════════════════════════ - -class CustomPrinterIconCallback(Stub): - """CustomPrinterIconCallback — 自定义打印机图标回调""" - - def __init__(self): - super().__init__() - self._icon_cache: Dict[PrinterId, bytes] = {} - self._callbacks: Dict[PrinterId, Callable] = {} - - def onCustomPrinterIconLoaded(self, printer_id: PrinterId, icon: bytes): - """自定义打印机图标加载回调""" - self._icon_cache[printer_id] = icon - - def requestCustomPrinterIcon(self, printer_id: PrinterId, - callback: Callable[[bytes], None]): - """请求自定义打印机图标""" - if printer_id in self._icon_cache: - callback(self._icon_cache[printer_id]) - else: - self._callbacks[printer_id] = callback - - def getCachedIcon(self, printer_id: PrinterId) -> Optional[bytes]: - return self._icon_cache.get(printer_id) - - def clearCache(self): - self._icon_cache.clear() - self._callbacks.clear() - - def toString(self) -> str: - return f"CustomPrinterIconCallback(cached={len(self._icon_cache)})" - - -# ═══════════════════════════════════════════════════════════════ -# PrintService -# ═══════════════════════════════════════════════════════════════ - -class PrintService(Stub): - """PrintService — 打印服务""" - - SERVICE_INTERFACE = "android.printservice.PrintService" - SERVICE_META_DATA = "android.printservice" - - EXTRA_PRINT_JOB_INFO = "android.printservice.extra.PRINT_JOB_INFO" - EXTRA_SELECT_PRINTER = "android.printservice.extra.SELECT_PRINTER" - EXTRA_PRINTER_ID = "android.printservice.extra.PRINTER_ID" - EXTRA_PRINTER_INFO = "android.printservice.extra.PRINTER_INFO" - EXTRA_PRINT_DOCUMENT_INFO = "android.printservice.extra.PRINT_DOCUMENT_INFO" - - def __init__(self): - super().__init__() - self._print_jobs: Dict[int, PrintJob] = {} - self._discovery_session: Optional[PrinterDiscoverySession] = None - self._icon_callbacks: List[CustomPrinterIconCallback] = [] - self._is_connected: bool = False - self._printer_infos: Dict[PrinterId, PrinterInfo] = {} - - def onCreatePrinterDiscoverySession(self) -> PrinterDiscoverySession: - """创建打印机发现会话""" - self._discovery_session = PrinterDiscoverySession() - return self._discovery_session - - def onPrintJobQueued(self, print_job: PrintJob): - """打印作业入队回调""" - self._print_jobs[print_job.getId()] = print_job - - def onCancelPrintJob(self, print_job: PrintJob): - """取消打印作业回调""" - print_job.cancel() - self._print_jobs.pop(print_job.getId(), None) - - def onConnected(self): - """连接回调""" - self._is_connected = True - - def onDisconnected(self): - """断开连接回调""" - self._is_connected = False - - def onRequestCancelPrintJob(self, print_job: PrintJob): - """请求取消打印作业""" - print_job.cancel() - - def getActivePrintJobs(self) -> List[PrintJob]: - """获取活动的打印作业""" - return [j for j in self._print_jobs.values() - if j.getInfo().getState() in (PrintJobInfo.STATE_QUEUED, - PrintJobInfo.STATE_STARTED, - PrintJobInfo.STATE_BLOCKED)] - - def getPrintJobs(self) -> List[PrintJob]: - return list(self._print_jobs.values()) - - def addPrintJob(self, print_job: PrintJob): - self._print_jobs[print_job.getId()] = print_job - - def removePrintJob(self, job_id: int): - self._print_jobs.pop(job_id, None) - - def getDiscoverySession(self) -> Optional[PrinterDiscoverySession]: - return self._discovery_session - - def addPrinterInfo(self, info: PrinterInfo): - self._printer_infos[info.getId()] = info - - def removePrinterInfo(self, printer_id: PrinterId): - self._printer_infos.pop(printer_id, None) - - def getPrinterInfos(self) -> List[PrinterInfo]: - return list(self._printer_infos.values()) - - def isConnected(self) -> bool: - return self._is_connected - - def generatePrintJobId(self) -> int: - return int(time.time() * 1000) % 100000 - - def toString(self) -> str: - return f"PrintService(jobs={len(self._print_jobs)})" - - -# ═══════════════════════════════════════════════════════════════ -# PrintServiceInfo -# ═══════════════════════════════════════════════════════════════ - -class PrintServiceInfo(Stub): - """PrintServiceInfo — 打印服务信息""" - - def __init__(self, package_name: str = "", service_name: str = ""): - super().__init__() - self._package_name = package_name - self._service_name = service_name - self._description: str = "" - self._is_enabled: bool = False - self._settings_activity: str = "" - self._add_printers_activity: str = "" - self._advanced_options_activity: str = "" - - def getPackageName(self) -> str: - return self._package_name - - def setPackageName(self, pkg: str): - self._package_name = pkg - - def getServiceName(self) -> str: - return self._service_name - - def setServiceName(self, name: str): - self._service_name = name - - def getDescription(self) -> str: - return self._description - - def setDescription(self, desc: str): - self._description = desc - - def isEnabled(self) -> bool: - return self._is_enabled - - def setEnabled(self, enabled: bool): - self._is_enabled = enabled - - def getSettingsActivity(self) -> str: - return self._settings_activity - - def setSettingsActivity(self, activity: str): - self._settings_activity = activity - - def toString(self) -> str: - return f"PrintServiceInfo(package={self._package_name})" - - -# ═══════════════════════════════════════════════════════════════ -# PrintServicePrinter -# ═══════════════════════════════════════════════════════════════ - -class PrintServicePrinter(Stub): - """PrintServicePrinter — 打印机辅助类""" - - TYPE_LOCAL = 0 - TYPE_NETWORK = 1 - TYPE_BLUETOOTH = 2 - TYPE_USB = 3 - TYPE_CLOUD = 4 - TYPE_VIRTUAL = 5 - - def __init__(self, printer_id: PrinterId = None, name: str = "", - printer_type: int = TYPE_LOCAL): - super().__init__() - self._id = printer_id or PrinterId() - self._name = name - self._printer_type = printer_type - self._uri: str = "" - self._ip_address: str = "" - self._port: int = 0 - self._location: str = "" - - def getId(self) -> PrinterId: - return self._id - - def setId(self, printer_id: PrinterId): - self._id = printer_id - - def getName(self) -> str: - return self._name - - def setName(self, name: str): - self._name = name - - def getPrinterType(self) -> int: - return self._printer_type - - def setPrinterType(self, type_: int): - self._printer_type = type_ - - def getUri(self) -> str: - return self._uri - - def setUri(self, uri: str): - self._uri = uri - - def getIpAddress(self) -> str: - return self._ip_address - - def setIpAddress(self, ip: str): - self._ip_address = ip - - def getPort(self) -> int: - return self._port - - def setPort(self, port: int): - self._port = port - - def getLocation(self) -> str: - return self._location - - def setLocation(self, location: str): - self._location = location - - def toString(self) -> str: - return f"PrintServicePrinter(name={self._name}, type={self._printer_type})" - - -# ═══════════════════════════════════════════════════════════════ -# PrintServiceUtils -# ═══════════════════════════════════════════════════════════════ - -class PrintServiceUtils(Stub): - """PrintServiceUtils — 打印服务工具类""" - - @staticmethod - def createPrinterId(package_name: str, local_id: str) -> PrinterId: - return PrinterId(package_name, local_id) - - @staticmethod - def createPrinterInfo(printer_id: PrinterId, name: str, - status: int) -> PrinterInfo: - return PrinterInfo(printer_id, name, status) - - @staticmethod - def createPrintJobInfo(job_id: int, label: str) -> PrintJobInfo: - return PrintJobInfo(job_id, label) - - @staticmethod - def createPrintJob(job_id: int, label: str) -> PrintJob: - return PrintJob(job_id, label) - - @staticmethod - def createPrintDocument(data: bytes, mime_type: str) -> PrintDocument: - return PrintDocument(data, mime_type) - - @staticmethod - def isServiceEnabled(package_name: str) -> bool: - return True - - @staticmethod - def getServiceList() -> List[PrintServiceInfo]: - return [] - - @staticmethod - def enableService(package_name: str, enabled: bool): - pass \ No newline at end of file diff --git a/android_pyapi/android/provider.py b/android_pyapi/android/provider.py deleted file mode 100644 index 56ed3ae..0000000 --- a/android_pyapi/android/provider.py +++ /dev/null @@ -1,688 +0,0 @@ -""" -Android Provider 组件 — Settings, MediaStore, ContactsContract 等 -方法签名完全对齐 Android SDK -""" - -import os -import sys -import json -from typing import Any, Dict, List, Optional, Set, Tuple, Union - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) - - -# ═══════════════════════════════════════════════════════════════ -# OpenableColumns -# ═══════════════════════════════════════════════════════════════ - -class OpenableColumns(Stub): - DISPLAY_NAME = "_display_name" - SIZE = "_size" - - -# ═══════════════════════════════════════════════════════════════ -# Settings -# ═══════════════════════════════════════════════════════════════ - -class _SettingsBase(Stub): - """Settings 基类 — 存储到 JSON 文件""" - - _DATA_DIR = os.path.join(_app_data_dir, "shared_prefs") - _cache: Dict[str, Dict[str, Any]] = {} - - @classmethod - def _get_path(cls, name: str) -> str: - os.makedirs(cls._DATA_DIR, exist_ok=True) - return os.path.join(cls._DATA_DIR, f"{name}.json") - - @classmethod - def _load(cls, name: str) -> Dict[str, Any]: - if name in cls._cache: - return cls._cache[name] - path = cls._get_path(name) - if os.path.exists(path): - try: - with open(path, "r") as f: - data = json.load(f) - cls._cache[name] = data - return data - except (json.JSONDecodeError, OSError): - pass - cls._cache[name] = {} - return cls._cache[name] - - @classmethod - def _save(cls, name: str): - path = cls._get_path(name) - try: - with open(path, "w") as f: - json.dump(cls._cache.get(name, {}), f) - except OSError: - pass - - @staticmethod - def putString(name: str, key: str, value: str) -> bool: - data = _SettingsBase._load(name) - data[key] = value - _SettingsBase._save(name) - return True - - @staticmethod - def getString(name: str, key: str, default_value: Optional[str] = None) -> Optional[str]: - data = _SettingsBase._load(name) - return data.get(key, default_value) - - @staticmethod - def putInt(name: str, key: str, value: int) -> bool: - data = _SettingsBase._load(name) - data[key] = value - _SettingsBase._save(name) - return True - - @staticmethod - def getInt(name: str, key: str, default_value: int = 0) -> int: - data = _SettingsBase._load(name) - return data.get(key, default_value) - - @staticmethod - def putLong(name: str, key: str, value: int) -> bool: - return _SettingsBase.putInt(name, key, value) - - @staticmethod - def getLong(name: str, key: str, default_value: int = 0) -> int: - return _SettingsBase.getInt(name, key, default_value) - - @staticmethod - def putFloat(name: str, key: str, value: float) -> bool: - data = _SettingsBase._load(name) - data[key] = value - _SettingsBase._save(name) - return True - - @staticmethod - def getFloat(name: str, key: str, default_value: float = 0.0) -> float: - data = _SettingsBase._load(name) - return data.get(key, default_value) - - @staticmethod - def putBoolean(name: str, key: str, value: bool) -> bool: - data = _SettingsBase._load(name) - data[key] = bool(value) - _SettingsBase._save(name) - return True - - @staticmethod - def getBoolean(name: str, key: str, default_value: bool = False) -> bool: - data = _SettingsBase._load(name) - val = data.get(key, default_value) - if isinstance(val, bool): - return val - if isinstance(val, str): - return val.lower() in ("true", "1", "yes") - if isinstance(val, (int, float)): - return bool(val) - return default_value - - -class SettingsSystem(_SettingsBase): - _NAME = "settings_system" - - @staticmethod - def putInt(key: str, value: int) -> bool: - return _SettingsBase.putInt(SettingsSystem._NAME, key, value) - - @staticmethod - def getInt(key: str, default_value: int = 0) -> int: - return _SettingsBase.getInt(SettingsSystem._NAME, key, default_value) - - @staticmethod - def putString(key: str, value: str) -> bool: - return _SettingsBase.putString(SettingsSystem._NAME, key, value) - - @staticmethod - def getString(key: str, default_value: Optional[str] = None) -> Optional[str]: - return _SettingsBase.getString(SettingsSystem._NAME, key, default_value) - - @staticmethod - def putFloat(key: str, value: float) -> bool: - return _SettingsBase.putFloat(SettingsSystem._NAME, key, value) - - @staticmethod - def getFloat(key: str, default_value: float = 0.0) -> float: - return _SettingsBase.getFloat(SettingsSystem._NAME, key, default_value) - - @staticmethod - def putLong(key: str, value: int) -> bool: - return _SettingsBase.putLong(SettingsSystem._NAME, key, value) - - @staticmethod - def getLong(key: str, default_value: int = 0) -> int: - return _SettingsBase.getLong(SettingsSystem._NAME, key, default_value) - - @staticmethod - def putBoolean(key: str, value: bool) -> bool: - return _SettingsBase.putBoolean(SettingsSystem._NAME, key, value) - - @staticmethod - def getBoolean(key: str, default_value: bool = False) -> bool: - return _SettingsBase.getBoolean(SettingsSystem._NAME, key, default_value) - - -class SettingsSecure(_SettingsBase): - _NAME = "settings_secure" - - @staticmethod - def putInt(key: str, value: int) -> bool: - return _SettingsBase.putInt(SettingsSecure._NAME, key, value) - - @staticmethod - def getInt(key: str, default_value: int = 0) -> int: - return _SettingsBase.getInt(SettingsSecure._NAME, key, default_value) - - @staticmethod - def putString(key: str, value: str) -> bool: - return _SettingsBase.putString(SettingsSecure._NAME, key, value) - - @staticmethod - def getString(key: str, default_value: Optional[str] = None) -> Optional[str]: - return _SettingsBase.getString(SettingsSecure._NAME, key, default_value) - - @staticmethod - def putFloat(key: str, value: float) -> bool: - return _SettingsBase.putFloat(SettingsSecure._NAME, key, value) - - @staticmethod - def getFloat(key: str, default_value: float = 0.0) -> float: - return _SettingsBase.getFloat(SettingsSecure._NAME, key, default_value) - - @staticmethod - def putLong(key: str, value: int) -> bool: - return _SettingsBase.putLong(SettingsSecure._NAME, key, value) - - @staticmethod - def getLong(key: str, default_value: int = 0) -> int: - return _SettingsBase.getLong(SettingsSecure._NAME, key, default_value) - - @staticmethod - def putBoolean(key: str, value: bool) -> bool: - return _SettingsBase.putBoolean(SettingsSecure._NAME, key, value) - - @staticmethod - def getBoolean(key: str, default_value: bool = False) -> bool: - return _SettingsBase.getBoolean(SettingsSecure._NAME, key, default_value) - - -class SettingsGlobal(_SettingsBase): - _NAME = "settings_global" - - @staticmethod - def putInt(key: str, value: int) -> bool: - return _SettingsBase.putInt(SettingsGlobal._NAME, key, value) - - @staticmethod - def getInt(key: str, default_value: int = 0) -> int: - return _SettingsBase.getInt(SettingsGlobal._NAME, key, default_value) - - @staticmethod - def putString(key: str, value: str) -> bool: - return _SettingsBase.putString(SettingsGlobal._NAME, key, value) - - @staticmethod - def getString(key: str, default_value: Optional[str] = None) -> Optional[str]: - return _SettingsBase.getString(SettingsGlobal._NAME, key, default_value) - - @staticmethod - def putFloat(key: str, value: float) -> bool: - return _SettingsBase.putFloat(SettingsGlobal._NAME, key, value) - - @staticmethod - def getFloat(key: str, default_value: float = 0.0) -> float: - return _SettingsBase.getFloat(SettingsGlobal._NAME, key, default_value) - - @staticmethod - def putLong(key: str, value: int) -> bool: - return _SettingsBase.putLong(SettingsGlobal._NAME, key, value) - - @staticmethod - def getLong(key: str, default_value: int = 0) -> int: - return _SettingsBase.getLong(SettingsGlobal._NAME, key, default_value) - - @staticmethod - def putBoolean(key: str, value: bool) -> bool: - return _SettingsBase.putBoolean(SettingsGlobal._NAME, key, value) - - @staticmethod - def getBoolean(key: str, default_value: bool = False) -> bool: - return _SettingsBase.getBoolean(SettingsGlobal._NAME, key, default_value) - - -class Settings(Stub): - """Settings — 系统设置""" - - System = SettingsSystem - Secure = SettingsSecure - Global = SettingsGlobal - - ACTION_SETTINGS = "android.settings.SETTINGS" - ACTION_APPLICATION_DETAILS_SETTINGS = "android.settings.APPLICATION_DETAILS_SETTINGS" - EXTRA_APP_PACKAGE = "android.intent.extra.PACKAGE_NAME" - - -# ═══════════════════════════════════════════════════════════════ -# MediaStore -# ═══════════════════════════════════════════════════════════════ - -class MediaStore(Stub): - """MediaStore — 媒体存储""" - - VOLUME_INTERNAL = "internal" - VOLUME_EXTERNAL = "external" - VOLUME_EXTERNAL_PRIMARY = "external_primary" - - class MediaColumns(Stub): - _ID = 0 - DATA = 1 - SIZE = 2 - DISPLAY_NAME = 3 - MIME_TYPE = 4 - TITLE = 5 - DATE_ADDED = 6 - DATE_MODIFIED = 7 - ORIENTATION = 8 - WIDTH = 9 - HEIGHT = 10 - - class Files(Stub): - @staticmethod - def getContentUri(volume_name: str) -> str: - return f"content://media/{volume_name}/file" - - class Images(Stub): - class Media(Stub): - EXTERNAL_CONTENT_URI = "content://media/external/images/media" - INTERNAL_CONTENT_URI = "content://media/internal/images/media" - - @staticmethod - def getContentUri(volume_name: str) -> str: - return f"content://media/{volume_name}/images/media" - - class Thumbnails(Stub): - EXTERNAL_CONTENT_URI = "content://media/external/images/thumbnails" - - class Video(Stub): - class Media(Stub): - EXTERNAL_CONTENT_URI = "content://media/external/video/media" - INTERNAL_CONTENT_URI = "content://media/internal/video/media" - - @staticmethod - def getContentUri(volume_name: str) -> str: - return f"content://media/{volume_name}/video/media" - - class Audio(Stub): - class Media(Stub): - EXTERNAL_CONTENT_URI = "content://media/external/audio/media" - INTERNAL_CONTENT_URI = "content://media/internal/audio/media" - - @staticmethod - def getContentUri(volume_name: str) -> str: - return f"content://media/{volume_name}/audio/media" - - @staticmethod - def getMediaUri(volume_name: str) -> str: - """返回指定卷的媒体 URI。""" - return f"content://media/{volume_name}" - - @staticmethod - def getVersion(context: Any, uri: str) -> str: - return "0" - - @staticmethod - def setVersion(context: Any, uri: str, version: str) -> bool: - return True - - -# ═══════════════════════════════════════════════════════════════ -# DocumentsContract -# ═══════════════════════════════════════════════════════════════ - -class DocumentsContract(Stub): - """DocumentsContract — 文档合约""" - - EXTRA_INITIAL_URI = "android.provider.extra.INITIAL_URI" - EXTRA_LOADING = "android.provider.extra.LOADING" - EXTRA_INFO = "android.provider.extra.INFO" - EXTRA_PROMPT = "android.provider.extra.PROMPT" - - @staticmethod - def buildDocumentUri(authority: str, document_id: str) -> str: - return f"content://{authority}/document/{document_id}" - - @staticmethod - def buildDocumentUriUsingTree(tree_uri: str, document_id: str) -> str: - return f"{tree_uri}/document/{document_id}" - - @staticmethod - def buildChildDocumentsUri(authority: str, parent_document_id: str) -> str: - return f"content://{authority}/document/{parent_document_id}/children" - - @staticmethod - def buildChildDocumentsUriUsingTree(tree_uri: str, parent_document_id: str) -> str: - return f"{tree_uri}/document/{parent_document_id}/children" - - @staticmethod - def getDocumentId(document_uri: str) -> str: - return document_uri.rsplit("/", 1)[-1] - - @staticmethod - def getTreeDocumentId(document_uri: str) -> str: - parts = document_uri.split("/") - if "document" in parts: - idx = parts.index("document") - if idx + 1 < len(parts): - return parts[idx + 1] - return "" - - @staticmethod - def getRootId(root_uri: str) -> str: - parts = root_uri.split("/") - if "root" in parts: - idx = parts.index("root") - if idx + 1 < len(parts): - return parts[idx + 1] - return "" - - -# ═══════════════════════════════════════════════════════════════ -# ContactsContract -# ═══════════════════════════════════════════════════════════════ - -class ContactsContract(Stub): - """ContactsContract — 联系人合约""" - - AUTHORITY = "com.android.contacts" - - class Contacts(Stub): - CONTENT_URI = "content://com.android.contacts/contacts" - CONTENT_FILTER_URI = "content://com.android.contacts/contacts/filter" - CONTENT_TYPE = "vnd.android.cursor.dir/contact" - CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact" - DISPLAY_NAME = "display_name" - PHOTO_URI = "photo_uri" - PHOTO_ID = "photo_id" - _ID = "_id" - HAS_PHONE_NUMBER = "has_phone_number" - LOOKUP_KEY = "lookup" - STARRED = "starred" - - class CommonDataKinds(Stub): - class Email(Stub): - CONTENT_URI = "content://com.android.contacts/data/emails" - CONTENT_ITEM_TYPE = "vnd.android.cursor.item/email_v2" - ADDRESS = "data1" - DISPLAY_NAME = "display_name" - TYPE = "data2" - TYPE_HOME = 1 - TYPE_WORK = 2 - TYPE_OTHER = 3 - TYPE_MOBILE = 4 - - class Phone(Stub): - CONTENT_URI = "content://com.android.contacts/data/phones" - CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone_v2" - NUMBER = "data1" - DISPLAY_NAME = "display_name" - TYPE = "data2" - TYPE_HOME = 1 - TYPE_MOBILE = 2 - TYPE_WORK = 3 - TYPE_FAX_WORK = 4 - TYPE_FAX_HOME = 5 - TYPE_PAGER = 6 - TYPE_OTHER = 7 - TYPE_CALLBACK = 8 - TYPE_CAR = 9 - TYPE_COMPANY_MAIN = 10 - TYPE_ISDN = 11 - TYPE_MAIN = 12 - TYPE_OTHER_FAX = 13 - TYPE_RADIO = 14 - TYPE_TELEX = 15 - TYPE_TTY_TDD = 16 - TYPE_WORK_MOBILE = 17 - TYPE_WORK_PAGER = 18 - TYPE_ASSISTANT = 19 - - class StructuredName(Stub): - CONTENT_ITEM_TYPE = "vnd.android.cursor.item/name" - DISPLAY_NAME = "data1" - GIVEN_NAME = "data2" - FAMILY_NAME = "data3" - PREFIX = "data4" - MIDDLE_NAME = "data5" - SUFFIX = "data6" - - class Organization(Stub): - CONTENT_ITEM_TYPE = "vnd.android.cursor.item/organization" - COMPANY = "data1" - TYPE = "data2" - TITLE = "data4" - TYPE_WORK = 1 - TYPE_OTHER = 2 - - class Data(Stub): - CONTENT_URI = "content://com.android.contacts/data" - CONTENT_TYPE = "vnd.android.cursor.dir/data" - CONTENT_ITEM_TYPE = "vnd.android.cursor.item/data" - MIMETYPE = "mimetype" - _ID = "_id" - DISPLAY_NAME = "display_name" - RAW_CONTACT_ID = "raw_contact_id" - CONTACT_ID = "contact_id" - - class RawContacts(Stub): - CONTENT_URI = "content://com.android.contacts/raw_contacts" - CONTENT_TYPE = "vnd.android.cursor.dir/raw_contact" - CONTENT_ITEM_TYPE = "vnd.android.cursor.item/raw_contact" - _ID = "_id" - CONTACT_ID = "contact_id" - ACCOUNT_NAME = "account_name" - ACCOUNT_TYPE = "account_type" - - -class ContactsContractContacts(ContactsContract.Contacts): - pass - - -class ContactsContractData(ContactsContract.Data): - pass - - -# ═══════════════════════════════════════════════════════════════ -# CallLog -# ═══════════════════════════════════════════════════════════════ - -class CallLog(Stub): - """CallLog — 通话记录""" - - class Calls(Stub): - CONTENT_URI = "content://call_log/calls" - _ID = "_id" - NUMBER = "number" - TYPE = "type" - TYPE_INCOMING = 1 - TYPE_OUTGOING = 2 - TYPE_MISSED = 3 - TYPE_VOICEMAIL = 4 - TYPE_REJECTED = 5 - TYPE_BLOCKED = 6 - TYPE_ANSWERED_EXTERNALLY = 7 - DATE = "date" - DURATION = "duration" - NEW = "new" - VOICEMAIL_URI = "voicemail_uri" - CACHED_NAME = "cached_name" - CACHED_NUMBER_LABEL = "cached_number_label" - GEOCODED_LOCATION = "geocoded_location" - COUNTRY_ISO = "country_iso" - - @staticmethod - def getLastOutgoingCall(context: Any) -> Optional[str]: - return None - - -# ═══════════════════════════════════════════════════════════════ -# CalendarContract -# ═══════════════════════════════════════════════════════════════ - -class CalendarContract(Stub): - """CalendarContract — 日历合约""" - - class Events(Stub): - CONTENT_URI = "content://com.android.calendar/events" - _ID = "_id" - TITLE = "title" - DESCRIPTION = "description" - EVENT_LOCATION = "event_location" - DTSTART = "dtstart" - DTEND = "dtend" - EVENT_TIMEZONE = "event_timezone" - DURATION = "duration" - ALL_DAY = "allDay" - CALENDAR_ID = "calendar_id" - ORGANIZER = "organizer" - STATUS = "eventStatus" - AVAILABILITY = "availability" - ACCESS_LEVEL = "accessLevel" - - class Attendees(Stub): - CONTENT_URI = "content://com.android.calendar/attendees" - - class Reminders(Stub): - CONTENT_URI = "content://com.android.calendar/reminders" - - -# ═══════════════════════════════════════════════════════════════ -# Browser / SearchRecentSuggestions / UserDictionary -# ═══════════════════════════════════════════════════════════════ - -class Browser(Stub): - """Browser — 浏览器书签""" - - class BookmarkColumns(Stub): - TITLE = "title" - URL = "url" - _ID = "_id" - BOOKMARK = "bookmark" - DATE = "date" - CREATED = "created" - VISITS = "visits" - - class SearchColumns(Stub): - URL = "url" - SEARCH = "search" - DATE = "date" - - HISTORY_PROJECTION = ["_id", "url", "title", "date", "visits", "bookmark", "created"] - SEARCHES_PROJECTION = ["_id", "search", "date"] - - -class SearchRecentSuggestions(Stub): - """SearchRecentSuggestions — 搜索建议""" - - def __init__(self, context: Any, authority: str, mode: int): - super().__init__() - self._authority = authority - self._mode = mode - - def saveRecentQuery(self, query_string: str, line2: str): - pass - - def clearHistory(self): - pass - - -class UserDictionary(Stub): - """UserDictionary — 用户词典""" - - class Words(Stub): - CONTENT_URI = "content://user_dictionary/words" - _ID = "_id" - WORD = "word" - FREQUENCY = "frequency" - LOCALE = "locale" - APP_ID = "app_id" - SHORTCUT = "shortcut" - - -# ═══════════════════════════════════════════════════════════════ -# SyncStateContract / LiveFolders -# ═══════════════════════════════════════════════════════════════ - -class SyncStateContract(Stub): - """SyncStateContract — 同步状态合约""" - - class Columns(Stub): - _ID = "_id" - ACCOUNT_NAME = "account_name" - ACCOUNT_TYPE = "account_type" - DATA = "data" - - class Helpers(Stub): - @staticmethod - def get(cr: Any, provider_uri: str, account: Any) -> Optional[bytes]: - return None - - @staticmethod - def set(cr: Any, provider_uri: str, account: Any, data: bytes): - pass - - @staticmethod - def insert(cr: Any, provider_uri: str, account: Any, data: bytes): - pass - - @staticmethod - def update(cr: Any, provider_uri: str, account: Any, data: bytes): - pass - - -class LiveFolders(Stub): - """LiveFolders — 实时文件夹""" - - NAME = "name" - DESCRIPTION = "description" - INTENT = "intent" - ICON_BITMAP = "icon_bitmap" - ICON_PACKAGE = "icon_package" - ICON_RESOURCE = "icon_resource" - DISPLAY_MODE = "display_mode" - DISPLAY_MODE_GRID = 1 - DISPLAY_MODE_LIST = 2 - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Settings", - "SettingsSystem", - "SettingsSecure", - "SettingsGlobal", - "CallLog", - "ContactsContract", - "ContactsContractContacts", - "ContactsContractData", - "MediaStore", - "DocumentsContract", - "CalendarContract", - "Browser", - "SearchRecentSuggestions", - "UserDictionary", - "SyncStateContract", - "LiveFolders", - "OpenableColumns", -] \ No newline at end of file diff --git a/android_pyapi/android/provider_calendar.py b/android_pyapi/android/provider_calendar.py deleted file mode 100644 index 4ce361e..0000000 --- a/android_pyapi/android/provider_calendar.py +++ /dev/null @@ -1,1379 +0,0 @@ -""" -Android Provider CalendarContract 扩展组件 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import json -import time -import datetime -import threading -from typing import Any, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# CalendarContract 常量 -# ═══════════════════════════════════════════════════════════════ - -# Calendars -CALENDAR_DISPLAY_NAME = "calendar_displayName" -CALENDAR_ACCOUNT_NAME = "account_name" -CALENDAR_ACCOUNT_TYPE = "account_type" -CALENDAR_OWNER_ACCOUNT = "calendar_ownerAccount" -CALENDAR_COLOR = "calendar_color" -CALENDAR_COLOR_KEY = "calendar_color_key" -CALENDAR_ACCESS_LEVEL = "calendar_access_level" -CALENDAR_VISIBLE = "calendar_visible" -CALENDAR_SYNC_EVENTS = "calendar_sync_events" -CALENDAR_TIMEZONE = "calendar_timezone" -CALENDAR_ALLOWED_REMINDERS = "calendar_allowedReminders" -CALENDAR_ALLOWED_AVAILABILITY = "calendar_allowedAvailability" -CALENDAR_ALLOWED_ATTENDEE_TYPES = "calendar_allowedAttendeeTypes" -CALENDAR_IS_PRIMARY = "calendar_isPrimary" -CALENDAR_MAX_REMINDERS = "calendar_maxReminders" -CALENDAR_DTSTART = "calendar_dtstart" -CALENDAR_CAN_ORGANIZER_RESPOND = "calendar_canOrganizerRespond" -CALENDAR_CAN_MODIFY_TIME_ZONE = "calendar_canModifyTimeZone" -CALENDAR_ALLOWED_REMINDERS = CALENDAR_ALLOWED_REMINDERS -CALENDAR_AVAILABLE_COLORS = "calendar_availableColors" -CALENDAR_COLORS_RAW = "calendar_colorsRaw" - -# Events -EVENT_ID = "_id" -EVENT_TITLE = "title" -EVENT_DESCRIPTION = "description" -EVENT_LOCATION = "eventLocation" -EVENT_DTSTART = "dtstart" -EVENT_DTEND = "dtend" -EVENT_DURATION = "duration" -EVENT_TIMEZONE = "eventTimezone" -EVENT_ALL_DAY = "allDay" -EVENT_CALENDAR_ID = "calendar_id" -EVENT_ORGANIZER = "organizer" -EVENT_STATUS = "eventStatus" -EVENT_AVAILABILITY = "availability" -EVENT_ACCESS_LEVEL = "accessLevel" -EVENT_RRULE = "rrule" -EVENT_RDATE = "rdate" -EVENT_EXRULE = "exrule" -EVENT_EXDATE = "exdate" -EVENT_ORIGINAL_ID = "original_id" -EVENT_ORIGINAL_SYNC_ID = "original_sync_id" -EVENT_ORIGINAL_INSTANCE_TIME = "originalInstanceTime" -EVENT_ORIGINAL_ALL_DAY = "originalAllDay" -EVENT_LAST_DATE = "lastDate" -EVENT_HAS_ALARM = "hasAlarm" -EVENT_HAS_EXTENDED_PROPERTIES = "hasExtendedProperties" -EVENT_EVENT_COLOR = "eventColor" -EVENT_EVENT_COLOR_KEY = "eventColor_key" -EVENT_CUSTOM_APP_PACKAGE = "customAppPackage" -EVENT_CUSTOM_APP_URI = "customAppUri" -EVENT_ORGANIZER_CAN_RESPOND = "organizerCanRespond" -EVENT_SELF_ATTENDEE_STATUS = "selfAttendeeStatus" -EVENT_UID_2445 = "uid2445" -EVENT_IS_ORGANIZER = "isOrganizer" -EVENT_CAN_INVITE_OTHERS = "canInviteOthers" -EVENT_ALLOWED_REMINDERS = "allowedReminders" - -# Attendees -ATTENDEE_ID = "_id" -ATTENDEE_EVENT_ID = "event_id" -ATTENDEE_NAME = "attendeeName" -ATTENDEE_EMAIL = "attendeeEmail" -ATTENDEE_RELATIONSHIP = "attendeeRelationship" -ATTENDEE_TYPE = "attendeeType" -ATTENDEE_STATUS = "attendeeStatus" -ATTENDEE_IDENTITY = "attendeeIdentity" -ATTENDEE_ID_NAMESPACE = "attendeeIdNamespace" - -# Reminders -REMINDER_ID = "_id" -REMINDER_EVENT_ID = "event_id" -REMINDER_MINUTES = "minutes" -REMINDER_METHOD = "method" - -# Instances -INSTANCE_ID = "_id" -INSTANCE_EVENT_ID = "event_id" -INSTANCE_BEGIN = "begin" -INSTANCE_END = "end" -INSTANCE_TITLE = "title" -INSTANCE_DESCRIPTION = "description" -INSTANCE_EVENT_LOCATION = "eventLocation" -INSTANCE_ALL_DAY = "allDay" -INSTANCE_CALENDAR_ID = "calendar_id" -INSTANCE_RRULE = "rrule" -INSTANCE_ORIGINAL_ID = "original_id" -INSTANCE_ORIGINAL_INSTANCE_TIME = "originalInstanceTime" -INSTANCE_ORIGINAL_SYNC_ID = "original_sync_id" -INSTANCE_EVENT_TIMEZONE = "eventTimezone" -INSTANCE_DTSTART = "dtstart" -INSTANCE_DTEND = "dtend" -INSTANCE_DURATION = "duration" -INSTANCE_STATUS = "eventStatus" -INSTANCE_AVAILABILITY = "availability" -INSTANCE_ACCESS_LEVEL = "accessLevel" -INSTANCE_HAS_ALARM = "hasAlarm" -INSTANCE_HAS_EXTENDED_PROPERTIES = "hasExtendedProperties" -INSTANCE_EVENT_COLOR = "eventColor" -INSTANCE_SELF_ATTENDEE_STATUS = "selfAttendeeStatus" -INSTANCE_START_DAY = "startDay" -INSTANCE_END_DAY = "endDay" -INSTANCE_START_MINUTE = "startMinute" -INSTANCE_END_MINUTE = "endMinute" - -# Colors -COLOR_ID = "_id" -COLOR_KEY = "color_key" -COLOR = "color" -COLOR_TYPE = "color_type" - -# Calendar Alerts -CALENDAR_ALERT_ID = "_id" -CALENDAR_ALERT_EVENT_ID = "event_id" -CALENDAR_ALERT_BEGIN = "begin" -CALENDAR_ALERT_END = "end" -CALENDAR_ALERT_ALARM_TIME = "alarmTime" -CALENDAR_ALERT_CREATION_TIME = "creationTime" -CALENDAR_ALERT_RECEIVED_TIME = "receivedTime" -CALENDAR_ALERT_NOTIFY_TIME = "notifyTime" -CALENDAR_ALERT_STATE = "state" -CALENDAR_ALERT_MINUTES = "minutes" -CALENDAR_ALERT_ACKNOWLEDGED = "acknowledged" -CALENDAR_ALERT_DISMISSED = "dismissed" -CALENDAR_ALERT_TYPE = "type" -CALENDAR_ALERT_ORIGIN = "origin" - -# Calendar Cache -CALENDAR_CACHE_KEY = "key" -CALENDAR_CACHE_VALUE = "value" - -# Event status -EVENT_STATUS_TENTATIVE = 0 -EVENT_STATUS_CONFIRMED = 1 -EVENT_STATUS_CANCELED = 2 - -# Availability -AVAILABILITY_BUSY = 0 -AVAILABILITY_FREE = 1 -AVAILABILITY_TENTATIVE = 2 - -# Access level -ACCESS_DEFAULT = 0 -ACCESS_CONFIDENTIAL = 1 -ACCESS_PRIVATE = 2 -ACCESS_PUBLIC = 3 -ACCESS_RESTRICTED = 4 - -# Attendee relationship -ATTENDEE_RELATIONSHIP_NONE = 0 -ATTENDEE_RELATIONSHIP_ORGANIZER = 1 -ATTENDEE_RELATIONSHIP_ATTENDEE = 2 -ATTENDEE_RELATIONSHIP_PERFORMER = 3 -ATTENDEE_RELATIONSHIP_SPEAKER = 4 - -# Attendee type -ATTENDEE_TYPE_NONE = 0 -ATTENDEE_TYPE_REQUIRED = 1 -ATTENDEE_TYPE_OPTIONAL = 2 - -# Attendee status -ATTENDEE_STATUS_NONE = 0 -ATTENDEE_STATUS_ACCEPTED = 1 -ATTENDEE_STATUS_DECLINED = 2 -ATTENDEE_STATUS_INVITED = 3 -ATTENDEE_STATUS_TENTATIVE = 4 - -# Reminder method -REMINDER_METHOD_DEFAULT = 0 -REMINDER_METHOD_ALERT = 1 -REMINDER_METHOD_EMAIL = 2 -REMINDER_METHOD_SMS = 3 -REMINDER_METHOD_ALARM = 4 - -# Color type -COLOR_TYPE_CALENDAR = 0 -COLOR_TYPE_EVENT = 1 - -# Calendar alert state -CALENDAR_ALERT_STATE_SCHEDULED = 0 -CALENDAR_ALERT_STATE_FIRED = 1 -CALENDAR_ALERT_STATE_DISMISSED = 2 - -# Calendar alert type -CALENDAR_ALERT_TYPE_ALARM = 0 -CALENDAR_ALERT_TYPE_REMINDER = 1 - -# Calendar alert origin -CALENDAR_ALERT_ORIGIN_SELF = 0 -CALENDAR_ALERT_ORIGIN_SYNC = 1 - -# Calendar visibility -CALENDAR_VISIBLE_TRUE = 1 -CALENDAR_VISIBLE_FALSE = 0 - -# Calendar sync -CALENDAR_SYNC_EVENTS_TRUE = 1 -CALENDAR_SYNC_EVENTS_FALSE = 0 - -# Calendar primary -CALENDAR_IS_PRIMARY_TRUE = 1 -CALENDAR_IS_PRIMARY_FALSE = 0 - -# Calendar can modify time zone -CALENDAR_CAN_MODIFY_TIME_ZONE_TRUE = 1 -CALENDAR_CAN_MODIFY_TIME_ZONE_FALSE = 0 - -# Calendar can organizer respond -CALENDAR_CAN_ORGANIZER_RESPOND_TRUE = 1 -CALENDAR_CAN_ORGANIZER_RESPOND_FALSE = 0 - -# Sync columns -CALENDAR_SYNC_ACCOUNT_NAME = "account_name" -CALENDAR_SYNC_ACCOUNT_TYPE = "account_type" -CALENDAR_SYNC_SOURCE_ID = "sync_id" -CALENDAR_SYNC_VERSION = "sync_version" -CALENDAR_SYNC_DIRTY = "sync_dirty" -CALENDAR_SYNC_MARK = "sync_mark" -CALENDAR_SYNC1 = "sync1" -CALENDAR_SYNC2 = "sync2" -CALENDAR_SYNC3 = "sync3" -CALENDAR_SYNC4 = "sync4" -CALENDAR_SYNC5 = "sync5" -CALENDAR_SYNC6 = "sync6" -CALENDAR_SYNC7 = "sync7" -CALENDAR_SYNC8 = "sync8" -CALENDAR_SYNC9 = "sync9" -CALENDAR_SYNC10 = "sync10" - -# Calendar keys for CalendarCache -CALENDAR_CACHE_KEY_TIMEZONE_TYPE = "timezoneType" -CALENDAR_CACHE_KEY_TIMEZONE_INSTANCES = "timezoneInstances" -CALENDAR_CACHE_KEY_TIMEZONE_INSTANCES_PREVIOUS = "timezoneInstancesPrevious" -CALENDAR_CACHE_KEY_TIMEZONE_INSTANCES_NEXT = "timezoneInstancesNext" - -# Calendar notification -CALENDAR_NOTIFICATION_ID = "_id" -CALENDAR_NOTIFICATION_EVENT_ID = "event_id" -CALENDAR_NOTIFICATION_ALARM_TIME = "alarmTime" -CALENDAR_NOTIFICATION_BEGIN = "begin" -CALENDAR_NOTIFICATION_END = "end" -CALENDAR_NOTIFICATION_TITLE = "title" -CALENDAR_NOTIFICATION_DESCRIPTION = "description" -CALENDAR_NOTIFICATION_LOCATION = "location" -CALENDAR_NOTIFICATION_STATE = "state" -CALENDAR_NOTIFICATION_MINUTES = "minutes" -CALENDAR_NOTIFICATION_VISIBILITY = "visibility" -CALENDAR_NOTIFICATION_PRIORITY = "priority" -CALENDAR_NOTIFICATION_GROUP_KEY = "groupKey" -CALENDAR_NOTIFICATION_SORT_KEY = "sortKey" - -# URI constants -CALENDAR_URI_CALENDARS = "content://com.android.calendar/calendars" -CALENDAR_URI_EVENTS = "content://com.android.calendar/events" -CALENDAR_URI_ATTENDEES = "content://com.android.calendar/attendees" -CALENDAR_URI_REMINDERS = "content://com.android.calendar/reminders" -CALENDAR_URI_INSTANCES = "content://com.android.calendar/instances/when" -CALENDAR_URI_INSTANCES_GROUP_BY_DAY = "content://com.android.calendar/instances/groupbyday" -CALENDAR_URI_COLORS = "content://com.android.calendar/colors" -CALENDAR_URI_CALENDAR_ALERTS = "content://com.android.calendar/calendar_alerts" -CALENDAR_URI_CALENDAR_CACHE = "content://com.android.calendar/calendar_cache" -CALENDAR_URI_SYNC_STATE = "content://com.android.calendar/syncstate" -CALENDAR_URI_CALENDAR_ENTITY = "content://com.android.calendar/calendar_entity" -CALENDAR_URI_EVENT_ENTITY = "content://com.android.calendar/event_entity" - - -# ═══════════════════════════════════════════════════════════════ -# CalendarContract 命名空间容器 -# ═══════════════════════════════════════════════════════════════ - -class CalendarContractBase(Stub): - """CalendarContract — 日历合约基类""" - pass - - -class SyncColumns(Stub): - """CalendarContract.SyncColumns — 同步列""" - ACCOUNT_NAME = CALENDAR_SYNC_ACCOUNT_NAME - ACCOUNT_TYPE = CALENDAR_SYNC_ACCOUNT_TYPE - SOURCE_ID = CALENDAR_SYNC_SOURCE_ID - VERSION = CALENDAR_SYNC_VERSION - DIRTY = CALENDAR_SYNC_DIRTY - SYNC1 = CALENDAR_SYNC1 - SYNC2 = CALENDAR_SYNC2 - SYNC3 = CALENDAR_SYNC3 - SYNC4 = CALENDAR_SYNC4 - SYNC5 = CALENDAR_SYNC5 - SYNC6 = CALENDAR_SYNC6 - SYNC7 = CALENDAR_SYNC7 - SYNC8 = CALENDAR_SYNC8 - SYNC9 = CALENDAR_SYNC9 - SYNC10 = CALENDAR_SYNC10 - - -class SyncState(Stub): - """CalendarContract.SyncState — 同步状态表""" - CONTENT_URI = CALENDAR_URI_SYNC_STATE - _ID = "_id" - ACCOUNT_NAME = "account_name" - ACCOUNT_TYPE = "account_type" - DATA = "data" - - -class Calendars(Stub): - """CalendarContract.Calendars — 日历表""" - CONTENT_URI = CALENDAR_URI_CALENDARS - _ID = "_id" - CALENDAR_DISPLAY_NAME = "calendar_displayName" - CALENDAR_ACCOUNT_NAME = "account_name" - CALENDAR_ACCOUNT_TYPE = "account_type" - CALENDAR_OWNER_ACCOUNT = "calendar_ownerAccount" - CALENDAR_COLOR = "calendar_color" - CALENDAR_COLOR_KEY = "calendar_color_key" - CALENDAR_ACCESS_LEVEL = "calendar_access_level" - CALENDAR_VISIBLE = "calendar_visible" - CALENDAR_SYNC_EVENTS = "calendar_sync_events" - CALENDAR_TIMEZONE = "calendar_timezone" - CALENDAR_ALLOWED_REMINDERS = "calendar_allowedReminders" - CALENDAR_ALLOWED_AVAILABILITY = "calendar_allowedAvailability" - CALENDAR_ALLOWED_ATTENDEE_TYPES = "calendar_allowedAttendeeTypes" - CALENDAR_IS_PRIMARY = "calendar_isPrimary" - CALENDAR_MAX_REMINDERS = "calendar_maxReminders" - CALENDAR_DTSTART = "calendar_dtstart" - CALENDAR_CAN_ORGANIZER_RESPOND = "calendar_canOrganizerRespond" - CALENDAR_CAN_MODIFY_TIME_ZONE = "calendar_canModifyTimeZone" - CALENDAR_AVAILABLE_COLORS = "calendar_availableColors" - CALENDAR_COLORS_RAW = "calendar_colorsRaw" - CALENDAR_ALLOWED_REMINDERS = "calendar_allowedReminders" - NAME = "calendar_displayName" - SYNC_EVENTS = "calendar_sync_events" - - VISIBLE = 1 - SYNC_EVENTS_YES = 1 - SYNC_EVENTS_NO = 0 - NO_UNDO_UPDATE_DELETION = 1 - - @staticmethod - def query(context: Any, projection: List[str], selection: str = None, - selection_args: List[str] = None, sort_order: str = None) -> List[Dict[str, Any]]: - """Query the calendars table.""" - return [] - - @staticmethod - def queryWithFactory(context: Any, projection: List[str], selection: str = None, - selection_args: List[str] = None, sort_order: str = None) -> Any: - """Query calendars with factory.""" - return None - - @staticmethod - def getCalendarColor(context: Any, calendar_id: int) -> int: - """Get calendar color.""" - return 0xFF1976D2 - - -class Events(Stub): - """CalendarContract.Events — 事件表""" - CONTENT_URI = CALENDAR_URI_EVENTS - _ID = "_id" - TITLE = "title" - DESCRIPTION = "description" - EVENT_LOCATION = "eventLocation" - DTSTART = "dtstart" - DTEND = "dtend" - DURATION = "duration" - EVENT_TIMEZONE = "eventTimezone" - ALL_DAY = "allDay" - CALENDAR_ID = "calendar_id" - ORGANIZER = "organizer" - STATUS = "eventStatus" - AVAILABILITY = "availability" - ACCESS_LEVEL = "accessLevel" - RRULE = "rrule" - RDATE = "rdate" - EXRULE = "exrule" - EXDATE = "exdate" - ORIGINAL_ID = "original_id" - ORIGINAL_SYNC_ID = "original_sync_id" - ORIGINAL_INSTANCE_TIME = "originalInstanceTime" - ORIGINAL_ALL_DAY = "originalAllDay" - LAST_DATE = "lastDate" - HAS_ALARM = "hasAlarm" - HAS_EXTENDED_PROPERTIES = "hasExtendedProperties" - EVENT_COLOR = "eventColor" - EVENT_COLOR_KEY = "eventColor_key" - CUSTOM_APP_PACKAGE = "customAppPackage" - CUSTOM_APP_URI = "customAppUri" - ORGANIZER_CAN_RESPOND = "organizerCanRespond" - SELF_ATTENDEE_STATUS = "selfAttendeeStatus" - UID_2445 = "uid2445" - IS_ORGANIZER = "isOrganizer" - CAN_INVITE_OTHERS = "canInviteOthers" - ALLOWED_REMINDERS = "allowedReminders" - - STATUS_TENTATIVE = 0 - STATUS_CONFIRMED = 1 - STATUS_CANCELED = 2 - - @staticmethod - def query(context: Any, projection: List[str], selection: str = None, - selection_args: List[str] = None, sort_order: str = None) -> List[Dict[str, Any]]: - """Query the events table.""" - return [] - - @staticmethod - def insert(context: Any, values: Dict[str, Any]) -> int: - """Insert a new event. Returns the event ID.""" - return -1 - - @staticmethod - def update(context: Any, values: Dict[str, Any], selection: str = None, - selection_args: List[str] = None) -> int: - """Update events. Returns number of rows affected.""" - return 0 - - @staticmethod - def delete(context: Any, selection: str = None, selection_args: List[str] = None) -> int: - """Delete events. Returns number of rows deleted.""" - return 0 - - @staticmethod - def queryEntity(context: Any, projection: List[str], selection: str = None, - selection_args: List[str] = None, sort_order: str = None) -> List[Any]: - """Query event entities.""" - return [] - - -class Attendees(Stub): - """CalendarContract.Attendees — 参与者表""" - CONTENT_URI = CALENDAR_URI_ATTENDEES - _ID = "_id" - EVENT_ID = "event_id" - ATTENDEE_NAME = "attendeeName" - ATTENDEE_EMAIL = "attendeeEmail" - ATTENDEE_RELATIONSHIP = "attendeeRelationship" - ATTENDEE_TYPE = "attendeeType" - ATTENDEE_STATUS = "attendeeStatus" - ATTENDEE_IDENTITY = "attendeeIdentity" - ATTENDEE_ID_NAMESPACE = "attendeeIdNamespace" - - RELATIONSHIP_NONE = 0 - RELATIONSHIP_ORGANIZER = 1 - RELATIONSHIP_ATTENDEE = 2 - RELATIONSHIP_PERFORMER = 3 - RELATIONSHIP_SPEAKER = 4 - - TYPE_NONE = 0 - TYPE_REQUIRED = 1 - TYPE_OPTIONAL = 2 - - STATUS_NONE = 0 - STATUS_ACCEPTED = 1 - STATUS_DECLINED = 2 - STATUS_INVITED = 3 - STATUS_TENTATIVE = 4 - - @staticmethod - def query(context: Any, event_id: int, projection: List[str] = None) -> List[Dict[str, Any]]: - """Query attendees for a specific event.""" - return [] - - -class Reminders(Stub): - """CalendarContract.Reminders — 提醒表""" - CONTENT_URI = CALENDAR_URI_REMINDERS - _ID = "_id" - EVENT_ID = "event_id" - MINUTES = "minutes" - METHOD = "method" - - METHOD_DEFAULT = 0 - METHOD_ALERT = 1 - METHOD_EMAIL = 2 - METHOD_SMS = 3 - METHOD_ALARM = 4 - - @staticmethod - def query(context: Any, event_id: int, projection: List[str] = None) -> List[Dict[str, Any]]: - """Query reminders for a specific event.""" - return [] - - -class Instances(Stub): - """CalendarContract.Instances — 实例表""" - CONTENT_URI = CALENDAR_URI_INSTANCES - CONTENT_BY_DAY_URI = CALENDAR_URI_INSTANCES_GROUP_BY_DAY - _ID = "_id" - EVENT_ID = "event_id" - BEGIN = "begin" - END = "end" - TITLE = "title" - DESCRIPTION = "description" - EVENT_LOCATION = "eventLocation" - ALL_DAY = "allDay" - CALENDAR_ID = "calendar_id" - RRULE = "rrule" - ORIGINAL_ID = "original_id" - ORIGINAL_INSTANCE_TIME = "originalInstanceTime" - ORIGINAL_SYNC_ID = "original_sync_id" - EVENT_TIMEZONE = "eventTimezone" - DTSTART = "dtstart" - DTEND = "dtend" - DURATION = "duration" - STATUS = "eventStatus" - AVAILABILITY = "availability" - ACCESS_LEVEL = "accessLevel" - HAS_ALARM = "hasAlarm" - HAS_EXTENDED_PROPERTIES = "hasExtendedProperties" - EVENT_COLOR = "eventColor" - SELF_ATTENDEE_STATUS = "selfAttendeeStatus" - START_DAY = "startDay" - END_DAY = "endDay" - START_MINUTE = "startMinute" - END_MINUTE = "endMinute" - - @staticmethod - def query(context: Any, projection: List[str], begin_time: int, end_time: int, - selection: str = None, selection_args: List[str] = None, - sort_order: str = None) -> List[Dict[str, Any]]: - """Query instances in a time range.""" - return [] - - -class Colors(Stub): - """CalendarContract.Colors — 颜色表""" - CONTENT_URI = CALENDAR_URI_COLORS - _ID = "_id" - COLOR_KEY = "color_key" - COLOR = "color" - COLOR_TYPE = "color_type" - - TYPE_CALENDAR = 0 - TYPE_EVENT = 1 - - @staticmethod - def query(context: Any, projection: List[str] = None) -> List[Dict[str, Any]]: - """Query available colors.""" - return [] - - -class CalendarEntity(Stub): - """CalendarContract.CalendarEntity — 日历实体""" - CONTENT_URI = CALENDAR_URI_CALENDAR_ENTITY - _ID = "_id" - CALENDAR_DISPLAY_NAME = "calendar_displayName" - CALENDAR_ACCOUNT_NAME = "account_name" - CALENDAR_ACCOUNT_TYPE = "account_type" - CALENDAR_OWNER_ACCOUNT = "calendar_ownerAccount" - CALENDAR_COLOR = "calendar_color" - CALENDAR_ACCESS_LEVEL = "calendar_access_level" - CALENDAR_VISIBLE = "calendar_visible" - CALENDAR_SYNC_EVENTS = "calendar_sync_events" - CALENDAR_TIMEZONE = "calendar_timezone" - CALENDAR_IS_PRIMARY = "calendar_isPrimary" - CALENDAR_DTSTART = "calendar_dtstart" - CALENDAR_CAN_ORGANIZER_RESPOND = "calendar_canOrganizerRespond" - CALENDAR_CAN_MODIFY_TIME_ZONE = "calendar_canModifyTimeZone" - CALENDAR_MAX_REMINDERS = "calendar_maxReminders" - CALENDAR_ALLOWED_REMINDERS = "calendar_allowedReminders" - - @staticmethod - def queryEntity(context: Any, projection: List[str], selection: str = None, - selection_args: List[str] = None, sort_order: str = None) -> List[Any]: - """Query calendar entities.""" - return [] - - -class EventEntity(Stub): - """CalendarContract.EventEntity — 事件实体""" - CONTENT_URI = CALENDAR_URI_EVENT_ENTITY - _ID = "_id" - TITLE = "title" - DESCRIPTION = "description" - EVENT_LOCATION = "eventLocation" - DTSTART = "dtstart" - DTEND = "dtend" - DURATION = "duration" - EVENT_TIMEZONE = "eventTimezone" - ALL_DAY = "allDay" - CALENDAR_ID = "calendar_id" - ORGANIZER = "organizer" - STATUS = "eventStatus" - AVAILABILITY = "availability" - ACCESS_LEVEL = "accessLevel" - RRULE = "rrule" - RDATE = "rdate" - EXRULE = "exrule" - EXDATE = "exdate" - ORIGINAL_ID = "original_id" - ORIGINAL_SYNC_ID = "original_sync_id" - ORIGINAL_INSTANCE_TIME = "originalInstanceTime" - ORIGINAL_ALL_DAY = "originalAllDay" - LAST_DATE = "lastDate" - HAS_ALARM = "hasAlarm" - HAS_EXTENDED_PROPERTIES = "hasExtendedProperties" - EVENT_COLOR = "eventColor" - CUSTOM_APP_PACKAGE = "customAppPackage" - CUSTOM_APP_URI = "customAppUri" - ORGANIZER_CAN_RESPOND = "organizerCanRespond" - SELF_ATTENDEE_STATUS = "selfAttendeeStatus" - UID_2445 = "uid2445" - IS_ORGANIZER = "isOrganizer" - CAN_INVITE_OTHERS = "canInviteOthers" - ALLOWED_REMINDERS = "allowedReminders" - - @staticmethod - def queryEntity(context: Any, projection: List[str], selection: str = None, - selection_args: List[str] = None, sort_order: str = None) -> List[Any]: - """Query event entities.""" - return [] - - -class CalendarCache(Stub): - """CalendarContract.CalendarCache — 日历缓存表""" - CONTENT_URI = CALENDAR_URI_CALENDAR_CACHE - KEY = "key" - VALUE = "value" - - TIMEZONE_TYPE = "timezoneType" - TIMEZONE_INSTANCES = "timezoneInstances" - TIMEZONE_INSTANCES_PREVIOUS = "timezoneInstancesPrevious" - TIMEZONE_INSTANCES_NEXT = "timezoneInstancesNext" - - TIMEZONE_TYPE_AUTO = "auto" - TIMEZONE_TYPE_USER = "user" - - @staticmethod - def getCacheKey(timezone_id: str, type_key: str) -> str: - """Build a cache key for timezone data.""" - return f"{timezone_id}_{type_key}" - - -class CalendarAlerts(Stub): - """CalendarContract.CalendarAlerts — 日历提醒表""" - CONTENT_URI = CALENDAR_URI_CALENDAR_ALERTS - _ID = "_id" - EVENT_ID = "event_id" - BEGIN = "begin" - END = "end" - ALARM_TIME = "alarmTime" - CREATION_TIME = "creationTime" - RECEIVED_TIME = "receivedTime" - NOTIFY_TIME = "notifyTime" - STATE = "state" - MINUTES = "minutes" - ACKNOWLEDGED = "acknowledged" - DISMISSED = "dismissed" - TYPE = "type" - ORIGIN = "origin" - - STATE_SCHEDULED = 0 - STATE_FIRED = 1 - STATE_DISMISSED = 2 - - TYPE_ALARM = 0 - TYPE_REMINDER = 1 - - ORIGIN_SELF = 0 - ORIGIN_SYNC = 1 - - @staticmethod - def query(context: Any, projection: List[str] = None) -> List[Dict[str, Any]]: - """Query calendar alerts.""" - return [] - - @staticmethod - def insert(context: Any, values: Dict[str, Any]) -> int: - """Insert a calendar alert. Returns the alert ID.""" - return -1 - - @staticmethod - def updateState(context: Any, alert_id: int, state: int): - """Update the state of a calendar alert.""" - pass - - -class CalendarNotification(Stub): - """CalendarContract.CalendarNotification — 日历通知表""" - CONTENT_URI = None # Virtual table, no direct content URI - _ID = "_id" - EVENT_ID = "event_id" - ALARM_TIME = "alarmTime" - BEGIN = "begin" - END = "end" - TITLE = "title" - DESCRIPTION = "description" - LOCATION = "location" - STATE = "state" - MINUTES = "minutes" - VISIBILITY = "visibility" - PRIORITY = "priority" - GROUP_KEY = "groupKey" - SORT_KEY = "sortKey" - - STATE_SHOWN = 0 - STATE_DISMISSED = 1 - - VISIBILITY_PRIVATE = 0 - VISIBILITY_PUBLIC = 1 - - PRIORITY_DEFAULT = 0 - PRIORITY_HIGH = 1 - PRIORITY_LOW = 2 - - @staticmethod - def notify(context: Any, event_id: int, alarm_time: int, begin: int, end: int): - """Trigger notification for a calendar event.""" - pass - - @staticmethod - def cancelNotification(context: Any, event_id: int): - """Cancel notification for a calendar event.""" - pass - - -class CalendarSyncColumns(SyncColumns): - """CalendarContract.CalendarSyncColumns — 日历同步列""" - CAL_SYNC1 = "cal_sync1" - CAL_SYNC2 = "cal_sync2" - CAL_SYNC3 = "cal_sync3" - CAL_SYNC4 = "cal_sync4" - CAL_SYNC5 = "cal_sync5" - CAL_SYNC6 = "cal_sync6" - CAL_SYNC7 = "cal_sync7" - CAL_SYNC8 = "cal_sync8" - CAL_SYNC9 = "cal_sync9" - CAL_SYNC10 = "cal_sync10" - - -# ═══════════════════════════════════════════════════════════════ -# CalendarContract 根类 -# ═══════════════════════════════════════════════════════════════ - -class CalendarContract(CalendarContractBase): - """CalendarContract — 日历合约(扩展版)""" - - Calendars = Calendars - Events = Events - Attendees = Attendees - Reminders = Reminders - Instances = Instances - Colors = Colors - CalendarEntity = CalendarEntity - EventEntity = EventEntity - CalendarCache = CalendarCache - CalendarAlerts = CalendarAlerts - CalendarNotification = CalendarNotification - CalendarSyncColumns = CalendarSyncColumns - SyncState = SyncState - - # URI aliases - CONTENT_URI_CALENDARS = CALENDAR_URI_CALENDARS - CONTENT_URI_EVENTS = CALENDAR_URI_EVENTS - CONTENT_URI_ATTENDEES = CALENDAR_URI_ATTENDEES - CONTENT_URI_REMINDERS = CALENDAR_URI_REMINDERS - CONTENT_URI_INSTANCES = CALENDAR_URI_INSTANCES - CONTENT_URI_INSTANCES_GROUP_BY_DAY = CALENDAR_URI_INSTANCES_GROUP_BY_DAY - CONTENT_URI_COLORS = CALENDAR_URI_COLORS - CONTENT_URI_CALENDAR_ALERTS = CALENDAR_URI_CALENDAR_ALERTS - CONTENT_URI_CALENDAR_CACHE = CALENDAR_URI_CALENDAR_CACHE - CONTENT_URI_SYNC_STATE = CALENDAR_URI_SYNC_STATE - CONTENT_URI_CALENDAR_ENTITY = CALENDAR_URI_CALENDAR_ENTITY - CONTENT_URI_EVENT_ENTITY = CALENDAR_URI_EVENT_ENTITY - - @staticmethod - def isValidEventType(context: Any, type_value: str) -> bool: - """Check if a mime type is a valid event type.""" - return type_value in ("vnd.android.cursor.item/event", "vnd.android.cursor.dir/event") - - -# ═══════════════════════════════════════════════════════════════ -# CalendarContract 便捷类(扩展别名) -# ═══════════════════════════════════════════════════════════════ - -class CalendarContractCalendars(Calendars): - """CalendarContract.Calendars 别名""" - pass - - -class CalendarContractEvents(Events): - """CalendarContract.Events 别名""" - pass - - -class CalendarContractAttendees(Attendees): - """CalendarContract.Attendees 别名""" - pass - - -class CalendarContractReminders(Reminders): - """CalendarContract.Reminders 别名""" - pass - - -class CalendarContractInstances(Instances): - """CalendarContract.Instances 别名""" - pass - - -class CalendarContractColors(Colors): - """CalendarContract.Colors 别名""" - pass - - -class CalendarContractCalendarEntity(CalendarEntity): - """CalendarContract.CalendarEntity 别名""" - pass - - -class CalendarContractEventEntity(EventEntity): - """CalendarContract.EventEntity 别名""" - pass - - -class CalendarContractCalendarCache(CalendarCache): - """CalendarContract.CalendarCache 别名""" - pass - - -class CalendarContractCalendarAlerts(CalendarAlerts): - """CalendarContract.CalendarAlerts 别名""" - pass - - -class CalendarContractCalendarNotification(CalendarNotification): - """CalendarContract.CalendarNotification 别名""" - pass - - -class CalendarContractCalendarSyncColumns(CalendarSyncColumns): - """CalendarContract.CalendarSyncColumns 别名""" - pass - - -class CalendarContractSyncState(SyncState): - """CalendarContract.SyncState 别名""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# CalendarEvent 辅助类 -# ═══════════════════════════════════════════════════════════════ - -class CalendarEvent(Stub): - """CalendarEvent — 事件对象封装""" - - def __init__(self, event_id: int = -1, title: str = "", description: str = "", - location: str = "", dtstart: int = 0, dtend: int = 0, - timezone: str = "UTC", all_day: bool = False, - calendar_id: int = -1, organizer: str = "", - status: int = EVENT_STATUS_CONFIRMED, - availability: int = AVAILABILITY_BUSY, - access_level: int = ACCESS_DEFAULT, - rrule: str = "", duration: str = ""): - super().__init__() - self._id = event_id - self._title = title - self._description = description - self._location = location - self._dtstart = dtstart - self._dtend = dtend - self._timezone = timezone - self._all_day = all_day - self._calendar_id = calendar_id - self._organizer = organizer - self._status = status - self._availability = availability - self._access_level = access_level - self._rrule = rrule - self._duration = duration - self._attendees: List[Dict[str, Any]] = [] - self._reminders: List[Dict[str, Any]] = [] - self._extended_properties: Dict[str, str] = {} - - def getId(self) -> int: - return self._id - - def getTitle(self) -> str: - return self._title - - def setTitle(self, title: str): - self._title = title - - def getDescription(self) -> str: - return self._description - - def setDescription(self, description: str): - self._description = description - - def getLocation(self) -> str: - return self._location - - def setLocation(self, location: str): - self._location = location - - def getDtStart(self) -> int: - return self._dtstart - - def setDtStart(self, dtstart: int): - self._dtstart = dtstart - - def getDtEnd(self) -> int: - return self._dtend - - def setDtEnd(self, dtend: int): - self._dtend = dtend - - def getTimezone(self) -> str: - return self._timezone - - def setTimezone(self, timezone: str): - self._timezone = timezone - - def isAllDay(self) -> bool: - return self._all_day - - def setAllDay(self, all_day: bool): - self._all_day = all_day - - def getCalendarId(self) -> int: - return self._calendar_id - - def setCalendarId(self, calendar_id: int): - self._calendar_id = calendar_id - - def getOrganizer(self) -> str: - return self._organizer - - def setOrganizer(self, organizer: str): - self._organizer = organizer - - def getStatus(self) -> int: - return self._status - - def setStatus(self, status: int): - self._status = status - - def getAvailability(self) -> int: - return self._availability - - def setAvailability(self, availability: int): - self._availability = availability - - def getAccessLevel(self) -> int: - return self._access_level - - def setAccessLevel(self, access_level: int): - self._access_level = access_level - - def getRrule(self) -> str: - return self._rrule - - def setRrule(self, rrule: str): - self._rrule = rrule - - def getDuration(self) -> str: - return self._duration - - def setDuration(self, duration: str): - self._duration = duration - - def addAttendee(self, name: str, email: str, relationship: int = ATTENDEE_RELATIONSHIP_ATTENDEE, - attendee_type: int = ATTENDEE_TYPE_REQUIRED, - status: int = ATTENDEE_STATUS_NONE): - """Add an attendee to this event.""" - self._attendees.append({ - "attendeeName": name, - "attendeeEmail": email, - "attendeeRelationship": relationship, - "attendeeType": attendee_type, - "attendeeStatus": status, - }) - - def getAttendees(self) -> List[Dict[str, Any]]: - return list(self._attendees) - - def addReminder(self, minutes: int, method: int = REMINDER_METHOD_DEFAULT): - """Add a reminder to this event.""" - self._reminders.append({ - "minutes": minutes, - "method": method, - }) - - def getReminders(self) -> List[Dict[str, Any]]: - return list(self._reminders) - - def putExtendedProperty(self, key: str, value: str): - """Set an extended property.""" - self._extended_properties[key] = value - - def getExtendedProperty(self, key: str) -> Optional[str]: - return self._extended_properties.get(key) - - def getExtendedProperties(self) -> Dict[str, str]: - return dict(self._extended_properties) - - def toContentValues(self) -> Dict[str, Any]: - """Convert event to content values dictionary.""" - values: Dict[str, Any] = {} - if self._title: - values["title"] = self._title - if self._description: - values["description"] = self._description - if self._location: - values["eventLocation"] = self._location - if self._dtstart: - values["dtstart"] = self._dtstart - if self._dtend: - values["dtend"] = self._dtend - if self._timezone: - values["eventTimezone"] = self._timezone - values["allDay"] = 1 if self._all_day else 0 - if self._calendar_id >= 0: - values["calendar_id"] = self._calendar_id - if self._organizer: - values["organizer"] = self._organizer - values["eventStatus"] = self._status - values["availability"] = self._availability - values["accessLevel"] = self._access_level - if self._rrule: - values["rrule"] = self._rrule - if self._duration: - values["duration"] = self._duration - return values - - @staticmethod - def fromContentValues(values: Dict[str, Any]) -> "CalendarEvent": - """Create an event from content values.""" - event = CalendarEvent() - event._id = values.get("_id", -1) - event._title = values.get("title", "") - event._description = values.get("description", "") - event._location = values.get("eventLocation", "") - event._dtstart = values.get("dtstart", 0) - event._dtend = values.get("dtend", 0) - event._timezone = values.get("eventTimezone", "UTC") - event._all_day = bool(values.get("allDay", 0)) - event._calendar_id = values.get("calendar_id", -1) - event._organizer = values.get("organizer", "") - event._status = values.get("eventStatus", EVENT_STATUS_CONFIRMED) - event._availability = values.get("availability", AVAILABILITY_BUSY) - event._access_level = values.get("accessLevel", ACCESS_DEFAULT) - event._rrule = values.get("rrule", "") - event._duration = values.get("duration", "") - return event - - def toString(self) -> str: - return f"CalendarEvent(id={self._id}, title='{self._title}')" - - -# ═══════════════════════════════════════════════════════════════ -# CalendarHelper 工具类 -# ═══════════════════════════════════════════════════════════════ - -class CalendarHelper(Stub): - """CalendarHelper — 日历操作辅助工具""" - - @staticmethod - def createEvent(context: Any, calendar_id: int, title: str, - dtstart: int, dtend: int, timezone: str = "UTC", - description: str = "", location: str = "", - all_day: bool = False) -> Optional[int]: - """Create a calendar event. Returns event ID or None.""" - return None - - @staticmethod - def updateEvent(context: Any, event_id: int, values: Dict[str, Any]) -> bool: - """Update a calendar event.""" - return False - - @staticmethod - def deleteEvent(context: Any, event_id: int) -> bool: - """Delete a calendar event.""" - return False - - @staticmethod - def getEvent(context: Any, event_id: int) -> Optional[CalendarEvent]: - """Get a calendar event by ID.""" - return None - - @staticmethod - def queryEvents(context: Any, calendar_id: Optional[int] = None, - start_time: Optional[int] = None, - end_time: Optional[int] = None) -> List[CalendarEvent]: - """Query calendar events with optional filters.""" - return [] - - @staticmethod - def addReminder(context: Any, event_id: int, minutes: int, - method: int = REMINDER_METHOD_DEFAULT) -> bool: - """Add a reminder to an event.""" - return False - - @staticmethod - def addAttendee(context: Any, event_id: int, email: str, - name: str = "") -> bool: - """Add an attendee to an event.""" - return False - - @staticmethod - def getDefaultCalendarId(context: Any) -> int: - """Get the default calendar ID.""" - return -1 - - @staticmethod - def queryCalendars(context: Any) -> List[Dict[str, Any]]: - """Query all available calendars.""" - return [] - - @staticmethod - def getCalendars(context: Any) -> List[Dict[str, Any]]: - """Get all calendars with sync enabled.""" - return [] - - @staticmethod - def createCalendar(context: Any, display_name: str, - account_name: str, account_type: str, - owner_account: str = "", timezone: str = "UTC") -> Optional[int]: - """Create a new calendar.""" - return None - - @staticmethod - def deleteCalendar(context: Any, calendar_id: int) -> bool: - """Delete a calendar.""" - return False - - -# ═══════════════════════════════════════════════════════════════ -# CalendarTimezoneHelper 工具类 -# ═══════════════════════════════════════════════════════════════ - -class CalendarTimezoneHelper(Stub): - """CalendarTimezoneHelper — 时区辅助工具""" - - TIMEZONE_IDS = [ - "UTC", "GMT", "America/New_York", "America/Chicago", - "America/Denver", "America/Los_Angeles", "America/Anchorage", - "Pacific/Honolulu", "Europe/London", "Europe/Paris", - "Europe/Berlin", "Europe/Moscow", "Europe/Istanbul", - "Asia/Dubai", "Asia/Kolkata", "Asia/Bangkok", - "Asia/Shanghai", "Asia/Tokyo", "Asia/Seoul", - "Australia/Sydney", "Pacific/Auckland", - ] - - @staticmethod - def getAvailableTimezoneIds() -> List[str]: - """Get list of available timezone IDs.""" - return list(CalendarTimezoneHelper.TIMEZONE_IDS) - - @staticmethod - def toDisplayName(timezone_id: str, locale: Any = None) -> str: - """Convert timezone ID to display name.""" - try: - import zoneinfo - tz = zoneinfo.ZoneInfo(timezone_id) - now = datetime.datetime.now(tz) - return f"{timezone_id} (UTC{now.strftime('%z')})" - except Exception: - return timezone_id - - @staticmethod - def toMilliseconds(days: int = 0, hours: int = 0, minutes: int = 0, - seconds: int = 0) -> int: - """Convert time duration to milliseconds.""" - total_seconds = (days * 86400) + (hours * 3600) + (minutes * 60) + seconds - return total_seconds * 1000 - - @staticmethod - def fromMilliseconds(ms: int) -> Dict[str, int]: - """Convert milliseconds to duration components.""" - total_seconds = ms // 1000 - days = total_seconds // 86400 - hours = (total_seconds % 86400) // 3600 - minutes = (total_seconds % 3600) // 60 - seconds = total_seconds % 60 - return {"days": days, "hours": hours, "minutes": minutes, "seconds": seconds} - - -# ═══════════════════════════════════════════════════════════════ -# CalendarRecurrenceRule 工具类 -# ═══════════════════════════════════════════════════════════════ - -class CalendarRecurrenceRule(Stub): - """CalendarRecurrenceRule — 重复规则解析工具""" - - FREQ_DAILY = "DAILY" - FREQ_WEEKLY = "WEEKLY" - FREQ_MONTHLY = "MONTHLY" - FREQ_YEARLY = "YEARLY" - - BY_DAY_SUNDAY = "SU" - BY_DAY_MONDAY = "MO" - BY_DAY_TUESDAY = "TU" - BY_DAY_WEDNESDAY = "WE" - BY_DAY_THURSDAY = "TH" - BY_DAY_FRIDAY = "FR" - BY_DAY_SATURDAY = "SA" - - def __init__(self, rrule: str = ""): - super().__init__() - self._freq: str = "" - self._interval: int = 1 - self._count: Optional[int] = None - self._until: Optional[int] = None - self._by_day: List[str] = [] - self._by_month_day: List[int] = [] - self._by_month: List[int] = [] - self._by_year_day: List[int] = [] - self._week_start: str = "MO" - self._raw = rrule - if rrule: - self._parse(rrule) - - def _parse(self, rrule: str): - """Parse an RRULE string.""" - parts = rrule.split(";") - for part in parts: - if "=" not in part: - continue - key, value = part.split("=", 1) - key = key.strip().upper() - value = value.strip() - if key == "FREQ": - self._freq = value - elif key == "INTERVAL": - self._interval = int(value) - elif key == "COUNT": - self._count = int(value) - elif key == "UNTIL": - try: - self._until = int(value) - except ValueError: - self._until = 0 - elif key == "BYDAY": - self._by_day = value.split(",") - elif key == "BYMONTHDAY": - self._by_month_day = [int(x) for x in value.split(",")] - elif key == "BYMONTH": - self._by_month = [int(x) for x in value.split(",")] - elif key == "BYYEARDAY": - self._by_year_day = [int(x) for x in value.split(",")] - elif key == "WKST": - self._week_start = value - - def getFrequency(self) -> str: - return self._freq - - def getInterval(self) -> int: - return self._interval - - def getCount(self) -> Optional[int]: - return self._count - - def getUntil(self) -> Optional[int]: - return self._until - - def getByDay(self) -> List[str]: - return list(self._by_day) - - def getByMonthDay(self) -> List[int]: - return list(self._by_month_day) - - def getByMonth(self) -> List[int]: - return list(self._by_month) - - def toString(self) -> str: - return self._raw - - @staticmethod - def createDaily(interval: int = 1, count: Optional[int] = None, - until: Optional[int] = None) -> "CalendarRecurrenceRule": - """Create a daily recurrence rule.""" - parts = [f"FREQ={CalendarRecurrenceRule.FREQ_DAILY}"] - if interval > 1: - parts.append(f"INTERVAL={interval}") - if count is not None: - parts.append(f"COUNT={count}") - if until is not None: - parts.append(f"UNTIL={until}") - return CalendarRecurrenceRule(";".join(parts)) - - @staticmethod - def createWeekly(interval: int = 1, by_day: Optional[List[str]] = None, - count: Optional[int] = None) -> "CalendarRecurrenceRule": - """Create a weekly recurrence rule.""" - parts = [f"FREQ={CalendarRecurrenceRule.FREQ_WEEKLY}"] - if interval > 1: - parts.append(f"INTERVAL={interval}") - if by_day: - parts.append(f"BYDAY={','.join(by_day)}") - if count is not None: - parts.append(f"COUNT={count}") - return CalendarRecurrenceRule(";".join(parts)) - - @staticmethod - def createMonthly(interval: int = 1, by_month_day: Optional[List[int]] = None, - count: Optional[int] = None) -> "CalendarRecurrenceRule": - """Create a monthly recurrence rule.""" - parts = [f"FREQ={CalendarRecurrenceRule.FREQ_MONTHLY}"] - if interval > 1: - parts.append(f"INTERVAL={interval}") - if by_month_day: - parts.append(f"BYMONTHDAY={','.join(str(d) for d in by_month_day)}") - if count is not None: - parts.append(f"COUNT={count}") - return CalendarRecurrenceRule(";".join(parts)) - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - # Constants - "CALENDAR_DISPLAY_NAME", "CALENDAR_ACCOUNT_NAME", "CALENDAR_ACCOUNT_TYPE", - "CALENDAR_OWNER_ACCOUNT", "CALENDAR_COLOR", "CALENDAR_COLOR_KEY", - "CALENDAR_ACCESS_LEVEL", "CALENDAR_VISIBLE", "CALENDAR_SYNC_EVENTS", - "CALENDAR_TIMEZONE", "CALENDAR_ALLOWED_REMINDERS", "CALENDAR_IS_PRIMARY", - "CALENDAR_MAX_REMINDERS", "CALENDAR_DTSTART", - "EVENT_ID", "EVENT_TITLE", "EVENT_DESCRIPTION", "EVENT_LOCATION", - "EVENT_DTSTART", "EVENT_DTEND", "EVENT_DURATION", "EVENT_TIMEZONE", - "EVENT_ALL_DAY", "EVENT_CALENDAR_ID", "EVENT_ORGANIZER", - "EVENT_STATUS", "EVENT_AVAILABILITY", "EVENT_ACCESS_LEVEL", - "EVENT_RRULE", "EVENT_RDATE", "EVENT_EXRULE", "EVENT_EXDATE", - "ATTENDEE_ID", "ATTENDEE_EVENT_ID", "ATTENDEE_NAME", "ATTENDEE_EMAIL", - "ATTENDEE_RELATIONSHIP", "ATTENDEE_TYPE", "ATTENDEE_STATUS", - "REMINDER_ID", "REMINDER_EVENT_ID", "REMINDER_MINUTES", "REMINDER_METHOD", - "INSTANCE_ID", "INSTANCE_BEGIN", "INSTANCE_END", - "AVAILABILITY_BUSY", "AVAILABILITY_FREE", "AVAILABILITY_TENTATIVE", - "ACCESS_DEFAULT", "ACCESS_CONFIDENTIAL", "ACCESS_PRIVATE", - "ACCESS_PUBLIC", "ACCESS_RESTRICTED", - "EVENT_STATUS_TENTATIVE", "EVENT_STATUS_CONFIRMED", "EVENT_STATUS_CANCELED", - "ATTENDEE_RELATIONSHIP_NONE", "ATTENDEE_RELATIONSHIP_ORGANIZER", - "ATTENDEE_RELATIONSHIP_ATTENDEE", "ATTENDEE_RELATIONSHIP_PERFORMER", - "ATTENDEE_RELATIONSHIP_SPEAKER", - "ATTENDEE_TYPE_NONE", "ATTENDEE_TYPE_REQUIRED", "ATTENDEE_TYPE_OPTIONAL", - "ATTENDEE_STATUS_NONE", "ATTENDEE_STATUS_ACCEPTED", "ATTENDEE_STATUS_DECLINED", - "ATTENDEE_STATUS_INVITED", "ATTENDEE_STATUS_TENTATIVE", - "REMINDER_METHOD_DEFAULT", "REMINDER_METHOD_ALERT", "REMINDER_METHOD_EMAIL", - "REMINDER_METHOD_SMS", "REMINDER_METHOD_ALARM", - # Classes - "CalendarContract", "Calendars", "Events", "Attendees", "Reminders", - "Instances", "Colors", "CalendarEntity", "EventEntity", "CalendarCache", - "CalendarAlerts", "CalendarNotification", "CalendarSyncColumns", "SyncState", - "CalendarContractBase", "SyncColumns", - "CalendarContractCalendars", "CalendarContractEvents", - "CalendarContractAttendees", "CalendarContractReminders", - "CalendarContractInstances", "CalendarContractColors", - "CalendarContractCalendarEntity", "CalendarContractEventEntity", - "CalendarContractCalendarCache", "CalendarContractCalendarAlerts", - "CalendarContractCalendarNotification", "CalendarContractCalendarSyncColumns", - "CalendarContractSyncState", - # Helpers - "CalendarEvent", "CalendarHelper", "CalendarTimezoneHelper", - "CalendarRecurrenceRule", -] \ No newline at end of file diff --git a/android_pyapi/android/provider_contacts.py b/android_pyapi/android/provider_contacts.py deleted file mode 100644 index b844d08..0000000 --- a/android_pyapi/android/provider_contacts.py +++ /dev/null @@ -1,776 +0,0 @@ -""" -Android Provider ContactsContract 扩展组件 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import json -import time -import threading -from typing import Any, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# 联系人常量 -# ═══════════════════════════════════════════════════════════════ - -TIMES_CONTACTED = "times_contacted" -LAST_TIME_CONTACTED = "last_time_contacted" -STARRED = "starred" -CUSTOM_RINGTONE = "custom_ringtone" -SEND_TO_VOICEMAIL = "send_to_voicemail" -CONTACT_PRESENCE = "contact_presence" -CONTACT_CHAT_CAPABILITY = "contact_chat_capability" -CONTACT_STATUS = "contact_status" -CONTACT_STATUS_TIMESTAMP = "contact_status_ts" -CONTACT_STATUS_RES_PACKAGE = "contact_status_res_package" -CONTACT_STATUS_LABEL = "contact_status_label" -CONTACT_STATUS_ICON = "contact_status_icon" - -CONTACT_ID = "_id" -CONTACT_LOOKUP_KEY = "lookup" -CONTACT_DISPLAY_NAME = "display_name" -CONTACT_PHOTO_ID = "photo_id" -CONTACT_PHOTO_URI = "photo_uri" -CONTACT_PHOTO_THUMBNAIL_URI = "photo_thumb_uri" -CONTACT_LAST_TIME_CONTACTED = LAST_TIME_CONTACTED -CONTACT_TIMES_CONTACTED = TIMES_CONTACTED -CONTACT_STARRED = STARRED -CONTACT_HAS_PHONE_NUMBER = "has_phone_number" -CONTACT_IS_USER_PROFILE = "is_user_profile" -CONTACT_IS_PRIVATE = "is_private" -CONTACT_NAME_RAW_CONTACT_ID = "name_raw_contact_id" -CONTACT_DISPLAY_NAME_SOURCE = "display_name_source" -CONTACT_LOOKUP_KEY = CONTACT_LOOKUP_KEY -CONTACT_SORT_KEY_PRIMARY = "sort_key" -CONTACT_SORT_KEY_ALTERNATIVE = "sort_key_alt" - -LOOKUP_KEY = CONTACT_LOOKUP_KEY -LOOKUP_ID = "_id" - -DATA_ID = "_id" -DATA_CONTACT_ID = "contact_id" -DATA_RAW_CONTACT_ID = "raw_contact_id" -DATA_MIMETYPE = "mimetype" -DATA_IS_PRIMARY = "is_primary" -DATA_IS_SUPER_PRIMARY = "is_super_primary" -DATA_DATA_VERSION = "data_version" -DATA_DELETED = "deleted" -DATA_SYNC1 = "data_sync1" -DATA_SYNC2 = "data_sync2" -DATA_SYNC3 = "data_sync3" -DATA_SYNC4 = "data_sync4" -DATA1 = "data1" -DATA2 = "data2" -DATA3 = "data3" -DATA4 = "data4" -DATA5 = "data5" -DATA6 = "data6" -DATA7 = "data7" -DATA8 = "data8" -DATA9 = "data9" -DATA10 = "data10" -DATA11 = "data11" -DATA12 = "data12" -DATA13 = "data13" -DATA14 = "data14" -DATA15 = "data15" - -RAW_CONTACT_ID = "_id" -RAW_CONTACT_CONTACT_ID = "contact_id" -RAW_CONTACT_ACCOUNT_NAME = "account_name" -RAW_CONTACT_ACCOUNT_TYPE = "account_type" -RAW_CONTACT_SOURCE_ID = "sourceid" -RAW_CONTACT_VERSION = "version" -RAW_CONTACT_DELETED = "deleted" -RAW_CONTACT_AGGREGATION_MODE = "aggregation_mode" -RAW_CONTACT_AGGREGATION_NEEDED = 0 -RAW_CONTACT_AGGREGATION_SUSPENDED = 1 -RAW_CONTACT_AGGREGATION_DEFAULT = 2 -RAW_CONTACT_SYNC1 = "sync1" -RAW_CONTACT_SYNC2 = "sync2" -RAW_CONTACT_SYNC3 = "sync3" -RAW_CONTACT_SYNC4 = "sync4" -RAW_CONTACT_METADATA = "metadata" - -# MIME 类型 -MIMETYPE_EMAIL = "vnd.android.cursor.item/email_v2" -MIMETYPE_PHONE = "vnd.android.cursor.item/phone_v2" -MIMETYPE_NAME = "vnd.android.cursor.item/name" -MIMETYPE_POSTAL = "vnd.android.cursor.item/postal-address_v2" -MIMETYPE_IM = "vnd.android.cursor.item/im" -MIMETYPE_NOTE = "vnd.android.cursor.item/note" -MIMETYPE_ORGANIZATION = "vnd.android.cursor.item/organization" -MIMETYPE_PHOTO = "vnd.android.cursor.item/photo" -MIMETYPE_WEBSITE = "vnd.android.cursor.item/website" -MIMETYPE_SIP_ADDRESS = "vnd.android.cursor.item/sip_address" -MIMETYPE_NICKNAME = "vnd.android.cursor.item/nickname" -MIMETYPE_GROUP_MEMBERSHIP = "vnd.android.cursor.item/group_membership" -MIMETYPE_EVENT = "vnd.android.cursor.item/contact_event" -MIMETYPE_RELATION = "vnd.android.cursor.item/relation" - -FULL_NAME_STYLE_UNDEFINED = 0 -FULL_NAME_STYLE_WESTERN = 1 -FULL_NAME_STYLE_EASTERN = 2 -FULL_NAME_STYLE_CHINESE = 3 -FULL_NAME_STYLE_JAPANESE = 4 -FULL_NAME_STYLE_KOREAN = 5 - -PHONETIC_NAME_STYLE_UNDEFINED = 0 -PHONETIC_NAME_STYLE_JAPANESE = 1 -PHONETIC_NAME_STYLE_KOREAN = 2 -PHONETIC_NAME_STYLE_PINYIN = 3 -PHONETIC_NAME_STYLE_ROMANJI = 4 - - -# ═══════════════════════════════════════════════════════════════ -# ContactsContract 命名空间容器 -# ═══════════════════════════════════════════════════════════════ - -class ContactsContractBase(Stub): - """ContactsContract — 联系人合同基类""" - pass - - -class SyncColumns(Stub): - """ContactsContract.SyncColumns — 同步列""" - ACCOUNT_NAME = RAW_CONTACT_ACCOUNT_NAME - ACCOUNT_TYPE = RAW_CONTACT_ACCOUNT_TYPE - SOURCE_ID = RAW_CONTACT_SOURCE_ID - VERSION = RAW_CONTACT_VERSION - DIRTY = "dirty" - SYNC1 = RAW_CONTACT_SYNC1 - SYNC2 = RAW_CONTACT_SYNC2 - SYNC3 = RAW_CONTACT_SYNC3 - SYNC4 = RAW_CONTACT_SYNC4 - - -class PresenceColumns(Stub): - """ContactsContract.PresenceColumns — 在线状态列""" - DATA_ID = "presence_data_id" - PROTOCOL = "protocol" - CUSTOM_PROTOCOL = "custom_protocol" - IM_HANDLE = "im_handle" - IM_ACCOUNT = "im_account" - STATUS = "mode" - STATUS_LABEL = "status_label" - STATUS_RES_PACKAGE = "status_res_package" - STATUS_ICON = "status_icon" - STATUS_TIMESTAMP = "status_ts" - - AVAILABLE = 5 - AWAY = 2 - BUSY = 4 - DO_NOT_DISTURB = 4 - IDLE = 3 - INVISIBLE = 1 - OFFLINE = 0 - - -class Presence(PresenceColumns): - """ContactsContract.Presence — 在线状态""" - pass - - -class FullNameStyle(Stub): - """ContactsContract.FullNameStyle — 全名风格""" - UNDEFINED = FULL_NAME_STYLE_UNDEFINED - WESTERN = FULL_NAME_STYLE_WESTERN - EASTERN = FULL_NAME_STYLE_EASTERN - CHINESE = FULL_NAME_STYLE_CHINESE - JAPANESE = FULL_NAME_STYLE_JAPANESE - KOREAN = FULL_NAME_STYLE_KOREAN - - -class PhoneticNameStyle(Stub): - """ContactsContract.PhoneticNameStyle — 拼音名称风格""" - UNDEFINED = PHONETIC_NAME_STYLE_UNDEFINED - JAPANESE = PHONETIC_NAME_STYLE_JAPANESE - KOREAN = PHONETIC_NAME_STYLE_KOREAN - PINYIN = PHONETIC_NAME_STYLE_PINYIN - - -# ═══════════════════════════════════════════════════════════════ -# ContactsContract.Contacts -# ═══════════════════════════════════════════════════════════════ - -class ContactData(Stub): - """内部联系人数据存储""" - def __init__(self): - self.id: int = 0 - self.lookup_key: str = "" - self.display_name: str = "" - self.photo_id: int = 0 - self.photo_uri: Optional[str] = None - self.photo_thumb_uri: Optional[str] = None - self.times_contacted: int = 0 - self.last_time_contacted: int = 0 - self.starred: bool = False - self.custom_ringtone: Optional[str] = None - self.send_to_voicemail: bool = False - self.has_phone_number: bool = False - self.is_user_profile: bool = False - self.name_raw_contact_id: int = 0 - self.display_name_source: int = 0 - self.sort_key: str = "" - self.sort_key_alt: str = "" - - -class AggregationSuggestions(Stub): - """ContactsContract.Contacts.AggregationSuggestions — 聚合建议""" - - class Builder(Stub): - """聚合建议构建器""" - - def __init__(self): - super().__init__() - self._contact_id: int = 0 - self._limit: int = 0 - self._name: Optional[str] = None - - def setContactId(self, contact_id: int) -> "AggregationSuggestions.Builder": - self._contact_id = contact_id - return self - - def setLimit(self, limit: int) -> "AggregationSuggestions.Builder": - self._limit = limit - return self - - def addNameParameter(self, name: str) -> "AggregationSuggestions.Builder": - self._name = name - return self - - def build(self) -> str: - return f"suggestions?contact_id={self._contact_id}&limit={self._limit}" - - CONTENT_DIRECTORY = "suggestions" - PARAMETER_MATCH_NAME = "name" - - -class Photo(Stub): - """ContactsContract.Contacts.Photo — 联系人照片""" - CONTENT_DIRECTORY = "photo" - PHOTO = "data15" - PHOTO_FILE_ID = "data14" - DISPLAY_PHOTO = "display_photo" - - -class Contacts(Stub): - """ContactsContract.Contacts — 联系人表""" - - CONTENT_URI = "content://com.android.contacts/contacts" - CONTENT_LOOKUP_URI = "content://com.android.contacts/contacts/lookup" - CONTENT_FILTER_URI = "content://com.android.contacts/contacts/filter" - CONTENT_STREQUENT_URI = "content://com.android.contacts/contacts/strequent" - CONTENT_FREQUENT_URI = "content://com.android.contacts/contacts/frequent" - CONTENT_STREQUENT_EMAIL_URI = "content://com.android.contacts/contacts/strequent/email" - CONTENT_GROUP_URI = "content://com.android.contacts/contacts/group" - - AggregationSuggestions = AggregationSuggestions - Photo = Photo - - # 列 - _ID = "_id" - LOOKUP_KEY = "lookup" - DISPLAY_NAME = "display_name" - PHOTO_ID = "photo_id" - PHOTO_URI = "photo_uri" - PHOTO_THUMBNAIL_URI = "photo_thumb_uri" - TIMES_CONTACTED = TIMES_CONTACTED - LAST_TIME_CONTACTED = LAST_TIME_CONTACTED - STARRED = STARRED - CUSTOM_RINGTONE = CUSTOM_RINGTONE - SEND_TO_VOICEMAIL = SEND_TO_VOICEMAIL - HAS_PHONE_NUMBER = "has_phone_number" - IS_USER_PROFILE = "is_user_profile" - NAME_RAW_CONTACT_ID = "name_raw_contact_id" - DISPLAY_NAME_SOURCE = "display_name_source" - SORT_KEY_PRIMARY = "sort_key" - SORT_KEY_ALTERNATIVE = "sort_key_alt" - CONTACT_LAST_UPDATED_TIMESTAMP = "contact_last_updated_timestamp" - CONTACT_PRESENCE = CONTACT_PRESENCE - CONTACT_CHAT_CAPABILITY = CONTACT_CHAT_CAPABILITY - CONTACT_STATUS = CONTACT_STATUS - CONTACT_STATUS_TIMESTAMP = CONTACT_STATUS_TIMESTAMP - - @staticmethod - def getLookupUri(contact_id: int, lookup_key: str) -> str: - return f"{Contacts.CONTENT_LOOKUP_URI}/{lookup_key}/{contact_id}" - - @staticmethod - def lookupContact(content_resolver: Any, lookup_uri: str) -> Optional[Dict[str, Any]]: - return None - - -# ═══════════════════════════════════════════════════════════════ -# ContactsContract.Data -# ═══════════════════════════════════════════════════════════════ - -class Data(Stub): - """ContactsContract.Data — 数据表""" - - CONTENT_URI = "content://com.android.contacts/data" - - _ID = DATA_ID - CONTACT_ID = DATA_CONTACT_ID - RAW_CONTACT_ID = DATA_RAW_CONTACT_ID - MIMETYPE = DATA_MIMETYPE - IS_PRIMARY = DATA_IS_PRIMARY - IS_SUPER_PRIMARY = DATA_IS_SUPER_PRIMARY - DATA_VERSION = DATA_DATA_VERSION - DELETED = DATA_DELETED - SYNC1 = DATA_SYNC1 - SYNC2 = DATA_SYNC2 - SYNC3 = DATA_SYNC3 - SYNC4 = DATA_SYNC4 - DATA1 = DATA1 - DATA2 = DATA2 - DATA3 = DATA3 - DATA4 = DATA4 - DATA5 = DATA5 - DATA6 = DATA6 - DATA7 = DATA7 - DATA8 = DATA8 - DATA9 = DATA9 - DATA10 = DATA10 - DATA11 = DATA11 - DATA12 = DATA12 - DATA13 = DATA13 - DATA14 = DATA14 - DATA15 = DATA15 - - # 偏好常量 - CONTACT_PREFERRED_ORDER = "contact_preferred_order" - CONTACT_PREFERRED_PHONE = "contact_preferred_phone" - CONTACT_PREFERRED_EMAIL = "contact_preferred_email" - CONTACT_PREFERRED_PHONE_ORDER = "contact_preferred_phone_order" - CONTACT_PREFERRED_EMAIL_ORDER = "contact_preferred_email_order" - - # 可见性 - VISIBILITY_VISIBLE = 0 - VISIBILITY_HIDDEN = 1 - VISIBILITY_DEFAULT = 0 - VISIBILITY = "visibility" - - @staticmethod - def getContactPreferred(data_kind: str, data_set: List[Dict]) -> Optional[Dict]: - if not data_set: - return None - primaries = [d for d in data_set if d.get("is_super_primary") or d.get("is_primary")] - if primaries: - return primaries[0] - return data_set[0] - - -# ═══════════════════════════════════════════════════════════════ -# ContactsContract.CommonDataKinds -# ═══════════════════════════════════════════════════════════════ - -class CommonDataKinds(Stub): - """ContactsContract.CommonDataKinds — 通用数据类型""" - - class Email(Stub): - CONTENT_ITEM_TYPE = MIMETYPE_EMAIL - TYPE_HOME = 1 - TYPE_WORK = 2 - TYPE_OTHER = 3 - TYPE_MOBILE = 4 - ADDRESS = DATA1 - DISPLAY_NAME = DATA4 - TYPE = DATA2 - LABEL = DATA3 - - class Phone(Stub): - CONTENT_ITEM_TYPE = MIMETYPE_PHONE - TYPE_HOME = 1 - TYPE_MOBILE = 2 - TYPE_WORK = 3 - TYPE_FAX_WORK = 4 - TYPE_FAX_HOME = 5 - TYPE_PAGER = 6 - TYPE_OTHER = 7 - TYPE_CALLBACK = 8 - TYPE_CAR = 9 - TYPE_COMPANY_MAIN = 10 - TYPE_ISDN = 11 - TYPE_MAIN = 12 - TYPE_OTHER_FAX = 13 - TYPE_RADIO = 14 - TYPE_TELEX = 15 - TYPE_TTY_TDD = 16 - TYPE_WORK_MOBILE = 17 - TYPE_WORK_PAGER = 18 - TYPE_ASSISTANT = 19 - TYPE_MMS = 20 - NUMBER = DATA1 - NUMBER_KEY = "data4" - TYPE = DATA2 - LABEL = DATA3 - - class StructuredName(Stub): - CONTENT_ITEM_TYPE = MIMETYPE_NAME - DISPLAY_NAME = DATA1 - GIVEN_NAME = DATA2 - FAMILY_NAME = DATA3 - PREFIX = DATA4 - MIDDLE_NAME = DATA5 - SUFFIX = DATA6 - PHONETIC_GIVEN_NAME = DATA7 - PHONETIC_FAMILY_NAME = DATA8 - PHONETIC_MIDDLE_NAME = DATA9 - FULL_NAME_STYLE = DATA10 - PHONETIC_NAME_STYLE = DATA11 - - class StructuredPostal(Stub): - CONTENT_ITEM_TYPE = MIMETYPE_POSTAL - FORMATTED_ADDRESS = DATA1 - STREET = DATA4 - POBOX = DATA5 - NEIGHBORHOOD = DATA6 - CITY = DATA7 - REGION = DATA8 - POSTCODE = DATA9 - COUNTRY = DATA10 - TYPE = DATA2 - LABEL = DATA3 - TYPE_HOME = 1 - TYPE_WORK = 2 - TYPE_OTHER = 3 - - class Im(Stub): - CONTENT_ITEM_TYPE = MIMETYPE_IM - PROTOCOL = DATA5 - CUSTOM_PROTOCOL = DATA6 - DATA = DATA1 - TYPE = DATA2 - - PROTOCOL_AIM = 0 - PROTOCOL_MSN = 1 - PROTOCOL_YAHOO = 2 - PROTOCOL_SKYPE = 3 - PROTOCOL_QQ = 4 - PROTOCOL_GTALK = 5 - PROTOCOL_ICQ = 6 - PROTOCOL_JABBER = 7 - PROTOCOL_CUSTOM = -1 - - class Organization(Stub): - CONTENT_ITEM_TYPE = MIMETYPE_ORGANIZATION - COMPANY = DATA1 - TYPE = DATA2 - LABEL = DATA3 - TITLE = DATA4 - DEPARTMENT = DATA5 - JOB_DESCRIPTION = DATA6 - SYMBOL = DATA7 - PHONETIC_NAME = DATA8 - OFFICE_LOCATION = DATA9 - PHONETIC_NAME_STYLE = DATA10 - TYPE_WORK = 1 - TYPE_OTHER = 2 - - class Photo(Stub): - CONTENT_ITEM_TYPE = MIMETYPE_PHOTO - PHOTO = DATA15 - PHOTO_FILE_ID = DATA14 - - class Note(Stub): - CONTENT_ITEM_TYPE = MIMETYPE_NOTE - NOTE = DATA1 - - class Website(Stub): - CONTENT_ITEM_TYPE = MIMETYPE_WEBSITE - URL = DATA1 - TYPE = DATA2 - TYPE_HOMEPAGE = 1 - TYPE_BLOG = 2 - TYPE_PROFILE = 3 - TYPE_HOME = 4 - TYPE_WORK = 5 - TYPE_FTP = 6 - TYPE_OTHER = 7 - - class Event(Stub): - CONTENT_ITEM_TYPE = MIMETYPE_EVENT - START_DATE = DATA1 - TYPE = DATA2 - TYPE_ANNIVERSARY = 1 - TYPE_OTHER = 2 - TYPE_BIRTHDAY = 3 - - class Relation(Stub): - CONTENT_ITEM_TYPE = MIMETYPE_RELATION - NAME = DATA1 - TYPE = DATA2 - TYPE_ASSISTANT = 1 - TYPE_BROTHER = 2 - TYPE_CHILD = 3 - TYPE_DOMESTIC_PARTNER = 4 - TYPE_FATHER = 5 - TYPE_FRIEND = 6 - TYPE_MANAGER = 7 - TYPE_MOTHER = 8 - TYPE_PARENT = 9 - TYPE_PARTNER = 10 - TYPE_REFERRED_BY = 11 - TYPE_RELATIVE = 12 - TYPE_SISTER = 13 - TYPE_SPOUSE = 14 - - class Nickname(Stub): - CONTENT_ITEM_TYPE = MIMETYPE_NICKNAME - NAME = DATA1 - TYPE = DATA2 - TYPE_DEFAULT = 1 - TYPE_OTHER_NAME = 2 - TYPE_MAIDEN_NAME = 3 - TYPE_SHORT_NAME = 4 - TYPE_INITIALS = 5 - - class SipAddress(Stub): - CONTENT_ITEM_TYPE = MIMETYPE_SIP_ADDRESS - SIP_ADDRESS = DATA1 - TYPE = DATA2 - TYPE_HOME = 1 - TYPE_WORK = 2 - - -# ═══════════════════════════════════════════════════════════════ -# ContactsContract.RawContacts -# ═══════════════════════════════════════════════════════════════ - -class RawContacts(Stub): - """ContactsContract.RawContacts — 原始联系人""" - - CONTENT_URI = "content://com.android.contacts/raw_contacts" - - _ID = RAW_CONTACT_ID - CONTACT_ID = RAW_CONTACT_CONTACT_ID - ACCOUNT_NAME = RAW_CONTACT_ACCOUNT_NAME - ACCOUNT_TYPE = RAW_CONTACT_ACCOUNT_TYPE - SOURCE_ID = RAW_CONTACT_SOURCE_ID - VERSION = RAW_CONTACT_VERSION - DELETED = RAW_CONTACT_DELETED - AGGREGATION_MODE = RAW_CONTACT_AGGREGATION_MODE - SYNC1 = RAW_CONTACT_SYNC1 - SYNC2 = RAW_CONTACT_SYNC2 - SYNC3 = RAW_CONTACT_SYNC3 - SYNC4 = RAW_CONTACT_SYNC4 - METADATA = RAW_CONTACT_METADATA - RAW_CONTACT_IS_USER_PROFILE = "raw_contact_is_user_profile" - RAW_CONTACT_IS_PRIVATE = "raw_contact_is_private" - DISPLAY_NAME_SOURCE = "display_name_source" - NAME_VERIFIED = "name_verified" - GEOLOCATION = "geolocation" - - AGGREGATION_MODE_DEFAULT = 0 - AGGREGATION_MODE_IMMEDIATE = 1 - AGGREGATION_MODE_SUSPENDED = 2 - AGGREGATION_MODE_DISABLED = 3 - - @staticmethod - def getContactId(raw_contact_id: int) -> int: - return 0 - - @staticmethod - def getLookupUri(raw_contact_id: int, lookup_key: str) -> str: - return f"content://com.android.contacts/raw_contacts/{raw_contact_id}" - - -# ═══════════════════════════════════════════════════════════════ -# ContactsContract.StatusUpdates / Profile / Settings -# ═══════════════════════════════════════════════════════════════ - -class StatusUpdates(PresenceColumns): - """ContactsContract.StatusUpdates — 状态更新""" - CONTENT_URI = "content://com.android.contacts/status_updates" - - -class Profile(Stub): - """ContactsContract.Profile — 用户个人资料""" - CONTENT_URI = "content://com.android.contacts/profile" - CONTENT_VCARD_URI = "content://com.android.contacts/profile/vcard" - CONTENT_RAW_CONTACTS_URI = "content://com.android.contacts/profile/raw_contacts" - MIN_ID = 1000000000 - MAX_ID = 2000000000 - - -class Settings(Stub): - """ContactsContract.Settings — 联系人设置""" - CONTENT_URI = "content://com.android.contacts/settings" - ACCOUNT_NAME = RAW_CONTACT_ACCOUNT_NAME - ACCOUNT_TYPE = RAW_CONTACT_ACCOUNT_TYPE - SHOULD_SYNC = "should_sync" - UNGROUPED_VISIBLE = "ungrouped_visible" - ANY_UNGROUPED = "any_ungrouped" - UNGROUPED_COUNT = "ungrouped_count" - UNGROUPED_WITH_PHONES = "ungrouped_with_phones" - - -# ═══════════════════════════════════════════════════════════════ -# ContactsContract.PhoneLookup / DisplayPhoto / DataUsageFeedback -# ═══════════════════════════════════════════════════════════════ - -class PhoneLookup(Stub): - """ContactsContract.PhoneLookup — 电话查找""" - CONTENT_URI = "content://com.android.contacts/phone_lookup" - CONTENT_FILTER_URI = "content://com.android.contacts/phone_lookup/filter" - QUERY_PARAMETER_SIP_ADDRESS = "sip" - NUMBER = "number" - TYPE = "type" - LABEL = "label" - NORMALIZED_NUMBER = "normalized_number" - CONTACT_ID = "contact_id" - LOOKUP_KEY = "lookup" - DISPLAY_NAME = "display_name" - PHOTO_ID = "photo_id" - PHOTO_URI = "photo_uri" - PHOTO_THUMBNAIL_URI = "photo_thumb_uri" - - -class DisplayPhoto(Stub): - """ContactsContract.DisplayPhoto — 显示照片""" - CONTENT_URI = "content://com.android.contacts/display_photo" - DISPLAY_MAX_DIM = "display_max_dim" - THUMBNAIL_MAX_DIM = "thumbnail_max_dim" - CONTENT_DIRECTORY = "display_photo" - - -class DataUsageFeedback(Stub): - """ContactsContract.DataUsageFeedback — 数据使用反馈""" - FEEDBACK_URI = "content://com.android.contacts/data/usagefeedback" - USAGE_TYPE = "type" - USAGE_TYPE_CALL = "call" - USAGE_TYPE_TEXT = "text" - USAGE_TYPE_EMAIL = "email" - USAGE_TYPE_LONG_TEXT = "long_text" - USAGE_TYPE_VIDEO_CALL = "video_call" - - -class QuickContact(Stub): - """ContactsContract.QuickContact — 快速联系人""" - - ACTION_QUICK_CONTACT = "android.provider.ContactsContract.QuickContact" - - MODE_SMALL = 0 - MODE_MEDIUM = 1 - MODE_LARGE = 2 - - EXTRA_EXCLUDE_MIMES = "android.provider.extra.EXCLUDE_MIMES" - EXTRA_MODE = "android.provider.extra.MODE" - EXTRA_PRIORITIZED_MIMETYPE = "android.provider.extra.PRIORITIZED_MIMETYPE" - - -# ═══════════════════════════════════════════════════════════════ -# ContactsContract 控制器 — 模拟 ContentProvider 的数据存储 -# ═══════════════════════════════════════════════════════════════ - -class ContactsContract(ContactsContractBase): - """ContactsContract — 联系人合同""" - - Contacts = Contacts - Data = Data - CommonDataKinds = CommonDataKinds - RawContacts = RawContacts - StatusUpdates = StatusUpdates - Profile = Profile - Settings = Settings - SyncColumns = SyncColumns - Presence = Presence - PhoneLookup = PhoneLookup - DisplayPhoto = DisplayPhoto - FullNameStyle = FullNameStyle - PhoneticNameStyle = PhoneticNameStyle - QuickContact = QuickContact - DataUsageFeedback = DataUsageFeedback - - AUTHORITY = "com.android.contacts" - CALLER_IS_SYNCADAPTER = "caller_is_syncadapter" - DEFERRED_SNIPPETING = "deferred_snippeting" - DEFERRED_SNIPPETING_QUERY = "deferred_snippeting_query" - DIRECTORY_CONTENT_URI = "content://com.android.contacts/directories" - LIMIT_PARAM_KEY = "limit" - PRIMARY_ACCOUNT_NAME = "name_forPrimaryAccount" - PRIMARY_ACCOUNT_TYPE = "type_forPrimaryAccount" - PROVIDER_INTERFACE = "android.content.ContactsContract" - REMOVE_DUPLICATE_ENTRIES = "remove_duplicate_entries" - STREQUENT_PHONE_ONLY = "strequent_phone_only" - - _store: Dict[str, List[Dict[str, Any]]] = { - "contacts": [], - "raw_contacts": [], - "data": [], - } - _lock = threading.Lock() - - @classmethod - def addContact(cls, display_name: str = "", - phone: str = "", email: str = "") -> int: - with cls._lock: - contact_id = len(cls._store["contacts"]) + 1 - contact = { - "_id": contact_id, - "display_name": display_name, - "has_phone_number": bool(phone), - "times_contacted": 0, - "last_time_contacted": 0, - "starred": False, - } - cls._store["contacts"].append(contact) - raw_id = len(cls._store["raw_contacts"]) + 1 - raw = { - "_id": raw_id, - "contact_id": contact_id, - "aggregation_mode": 0, - "deleted": False, - "account_name": "", - "account_type": "", - } - cls._store["raw_contacts"].append(raw) - if phone: - cls._store["data"].append({ - "_id": len(cls._store["data"]) + 1, - "raw_contact_id": raw_id, - "mimetype": MIMETYPE_PHONE, - "data1": phone, - "data2": 2, - "is_primary": True, - }) - if email: - cls._store["data"].append({ - "_id": len(cls._store["data"]) + 1, - "raw_contact_id": raw_id, - "mimetype": MIMETYPE_EMAIL, - "data1": email, - "data2": 1, - "is_primary": True, - }) - return contact_id - - @classmethod - def query(cls, table: str, filters: Optional[Dict] = None) -> List[Dict[str, Any]]: - with cls._lock: - results = list(cls._store.get(table, [])) - if filters: - filtered = [] - for item in results: - match = True - for k, v in filters.items(): - if item.get(k) != v: - match = False - break - if match: - filtered.append(item) - return filtered - return results - - @classmethod - def toString(cls) -> str: - return (f"ContactsContract(contacts={len(cls._store['contacts'])}, " - f"raw={len(cls._store['raw_contacts'])}, " - f"data={len(cls._store['data'])})") \ No newline at end of file diff --git a/android_pyapi/android/provider_settings.py b/android_pyapi/android/provider_settings.py deleted file mode 100644 index 21ad0d2..0000000 --- a/android_pyapi/android/provider_settings.py +++ /dev/null @@ -1,1660 +0,0 @@ -""" -Android Provider Settings 扩展组件 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import json -import sys -import time -from typing import Any, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# Settings 基类 - 桌面实现 -# ═══════════════════════════════════════════════════════════════ - -_SETTINGS_DATA_DIR = os.path.join( - os.path.expanduser("~"), "Android-compat", "app-data", "shared_prefs" -) - - -class _SettingsBase(Stub): - """Settings 基类 — 持久化存储到 JSON 文件""" - - _NAME = "" - _cache: Dict[str, Dict[str, Any]] = {} - - @classmethod - def _get_path(cls) -> str: - os.makedirs(_SETTINGS_DATA_DIR, exist_ok=True) - return os.path.join(_SETTINGS_DATA_DIR, f"{cls._NAME}.json") - - @classmethod - def _load(cls) -> Dict[str, Any]: - if cls._NAME in cls._cache: - return cls._cache[cls._NAME] - path = cls._get_path() - if os.path.exists(path): - try: - with open(path, "r") as f: - data = json.load(f) - cls._cache[cls._NAME] = data - return data - except (json.JSONDecodeError, OSError): - pass - cls._cache[cls._NAME] = {} - return cls._cache[cls._NAME] - - @classmethod - def _save(cls): - path = cls._get_path() - try: - with open(path, "w") as f: - json.dump(cls._cache.get(cls._NAME, {}), f) - except OSError: - pass - - @classmethod - def getString(cls, key: str, default_value: Optional[str] = None) -> Optional[str]: - data = cls._load() - return data.get(key, default_value) - - @classmethod - def putString(cls, key: str, value: str) -> bool: - data = cls._load() - data[key] = value - cls._save() - return True - - @classmethod - def getInt(cls, key: str, default_value: int = 0) -> int: - data = cls._load() - return data.get(key, default_value) - - @classmethod - def putInt(cls, key: str, value: int) -> bool: - data = cls._load() - data[key] = value - cls._save() - return True - - @classmethod - def getLong(cls, key: str, default_value: int = 0) -> int: - return cls.getInt(key, default_value) - - @classmethod - def putLong(cls, key: str, value: int) -> bool: - return cls.putInt(key, value) - - @classmethod - def getFloat(cls, key: str, default_value: float = 0.0) -> float: - data = cls._load() - return data.get(key, default_value) - - @classmethod - def putFloat(cls, key: str, value: float) -> bool: - data = cls._load() - data[key] = value - cls._save() - return True - - @classmethod - def hasKey(cls, key: str) -> bool: - data = cls._load() - return key in data - - @classmethod - def getAll(cls) -> Dict[str, Any]: - return dict(cls._load()) - - @classmethod - def clear(cls): - cls._cache[cls._NAME] = {} - cls._save() - - @classmethod - def remove(cls, key: str): - data = cls._load() - data.pop(key, None) - cls._save() - - -# ═══════════════════════════════════════════════════════════════ -# Settings.System — 系统设置 -# ═══════════════════════════════════════════════════════════════ - -SYSTEM_AIRPLANE_MODE_ON = "airplane_mode_on" -SYSTEM_AIRPLANE_MODE_RADIOS = "airplane_mode_radios" -SYSTEM_AIRPLANE_MODE_TOGGLEABLE_RADIOS = "airplane_mode_toggleable_radios" -SYSTEM_ALARM_ALERT = "alarm_alert" -SYSTEM_ALARM_ALERT_CACHE = "alarm_alert_cache" -SYSTEM_ANDROID_ID = "android_id" -SYSTEM_APPEND_FOR_LAST_AUDIBLE = "append_for_last_audible" -SYSTEM_AUTO_TIME = "auto_time" -SYSTEM_AUTO_TIME_ZONE = "auto_time_zone" -SYSTEM_BLUETOOTH_DISCOVERABILITY = "bluetooth_discoverability" -SYSTEM_BLUETOOTH_DISCOVERABILITY_TIMEOUT = "bluetooth_discoverability_timeout" -SYSTEM_BLUETOOTH_ON = "bluetooth_on" -SYSTEM_CAR_DOCK_SOUND = "car_dock_sound" -SYSTEM_CAR_UNDOCK_SOUND = "car_undock_sound" -SYSTEM_DESK_DOCK_SOUND = "desk_dock_sound" -SYSTEM_DESK_UNDOCK_SOUND = "desk_undock_sound" -SYSTEM_DOCK_SOUNDS_ENABLED = "dock_sounds_enabled" -SYSTEM_DTMF_TONE_WHEN_DIALING = "dtmf_tone" -SYSTEM_EGG_MODE = "egg_mode" -SYSTEM_END_BUTTON_BEHAVIOR = "end_button_behavior" -SYSTEM_FONT_SCALE = "font_scale" -SYSTEM_HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled" -SYSTEM_HEARING_AID = "hearing_aid" -SYSTEM_HOME_SOUND = "home_sound" -SYSTEM_HOME_UNDOCK_SOUND = "home_undock_sound" -SYSTEM_HTTP_PROXY = "http_proxy" -SYSTEM_LOCKSCREEN_SOUNDS_ENABLED = "lockscreen_sounds_enabled" -SYSTEM_LOCKSCREEN_DISABLED = "lock_screen_lock_after_timeout" -SYSTEM_LOCK_TO_APP_ENABLED = "lock_to_app_enabled" -SYSTEM_MODE_RINGER = "mode_ringer" -SYSTEM_MODE_RINGER_STREAMS_AFFECTED = "mode_ringer_streams_affected" -SYSTEM_MUTE_STREAMS_AFFECTED = "mute_streams_affected" -SYSTEM_NEXT_ALARM_FORMATTED = "next_alarm_formatted" -SYSTEM_NOTIFICATION_SOUND = "notification_sound" -SYSTEM_NOTIFICATION_SOUND_CACHE = "notification_sound_cache" -SYSTEM_POWER_SOUNDS_ENABLED = "power_sounds_enabled" -SYSTEM_RINGTONE = "ringtone" -SYSTEM_RINGTONE_CACHE = "ringtone_cache" -SYSTEM_SCREEN_BRIGHTNESS = "screen_brightness" -SYSTEM_SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode" -SYSTEM_SCREEN_BRIGHTNESS_MODE_MANUAL = 0 -SYSTEM_SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1 -SYSTEM_SCREEN_OFF_TIMEOUT = "screen_off_timeout" -SYSTEM_SETTINGS_CLASSNAME = "settings_classname" -SYSTEM_SETUP_WIZARD_HAS_RUN = "setup_wizard_has_run" -SYSTEM_SHOW_GTALK_SERVICE_STATUS = "show_gtalk_service_status" -SYSTEM_SHOW_PROCESSES = "show_processes" -SYSTEM_SHOW_WEB_SUGGESTIONS = "show_web_suggestions" -SYSTEM_SIP_RECEIVE_CALLS = "sip_receive_calls" -SYSTEM_SIP_CALL_OPTIONS = "sip_call_options" -SYSTEM_SOUND_EFFECTS_ENABLED = "sound_effects_enabled" -SYSTEM_STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in" -SYSTEM_TTY_MODE = "tty_mode" -SYSTEM_UNLOCK_SOUND = "unlock_sound" -SYSTEM_USER_ROTATION = "user_rotation" -SYSTEM_VIBRATE_ON = "vibrate_on" -SYSTEM_VOLUME_ALARM = "volume_alarm" -SYSTEM_VOLUME_BLUETOOTH_SCO = "volume_bluetooth_sco" -SYSTEM_VOLUME_DTMF = "volume_dtmf" -SYSTEM_VOLUME_MUSIC = "volume_music" -SYSTEM_VOLUME_NOTIFICATION = "volume_notification" -SYSTEM_VOLUME_RING = "volume_ring" -SYSTEM_VOLUME_SYSTEM = "volume_system" -SYSTEM_VOLUME_VOICE = "volume_voice" -SYSTEM_VOLUME_MASTER = "volume_master" -SYSTEM_VOLUME_MASTER_MUTE = "volume_master_mute" -SYSTEM_VOLUME_MICROPHONE_MUTE = "volume_microphone_mute" -SYSTEM_VOLUME_RING_MUTED = "volume_ring_muted" -SYSTEM_VOLUME_MUSIC_MUTED = "volume_music_muted" -SYSTEM_VOLUME_NOTIFICATION_MUTED = "volume_notification_muted" -SYSTEM_VOLUME_ALARM_MUTED = "volume_alarm_muted" -SYSTEM_VOLUME_SYSTEM_MUTED = "volume_system_muted" -SYSTEM_VOLUME_VOICE_MUTED = "volume_voice_muted" -SYSTEM_VOLUME_BLUETOOTH_SCO_MUTED = "volume_bluetooth_sco_muted" -SYSTEM_WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count" -SYSTEM_WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = "wifi_mobile_data_transition_wakelock_timeout_ms" -SYSTEM_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on" -SYSTEM_WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay" -SYSTEM_WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept" -SYSTEM_WIFI_ON = "wifi_on" -SYSTEM_WIFI_SLEEP_POLICY = "wifi_sleep_policy" -SYSTEM_WIFI_SLEEP_POLICY_DEFAULT = 0 -SYSTEM_WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1 -SYSTEM_WIFI_SLEEP_POLICY_NEVER = 2 -SYSTEM_WIFI_STATIC_DHCP_HOSTNAME = "wifi_static_dhcp_hostname" -SYSTEM_WIFI_STATIC_IP = "wifi_static_ip" -SYSTEM_WIFI_STATIC_GATEWAY = "wifi_static_gateway" -SYSTEM_WIFI_STATIC_NETMASK = "wifi_static_netmask" -SYSTEM_WIFI_STATIC_DNS1 = "wifi_static_dns1" -SYSTEM_WIFI_STATIC_DNS2 = "wifi_static_dns2" -SYSTEM_WIFI_WATCHDOG_ON = "wifi_watchdog_on" -SYSTEM_WINDOW_ANIMATION_SCALE = "window_animation_scale" -SYSTEM_TRANSITION_ANIMATION_SCALE = "transition_animation_scale" -SYSTEM_ANIMATOR_DURATION_SCALE = "animator_duration_scale" -SYSTEM_ACCELEROMETER_ROTATION = "accelerometer_rotation" -SYSTEM_SCREEN_BRIGHTNESS_MODE = SYSTEM_SCREEN_BRIGHTNESS_MODE -SYSTEM_HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY = "hide_rotation_lock_toggle_for_accessibility" -SYSTEM_VIBRATE_INPUT_DEVICES = "vibrate_input_devices" -SYSTEM_VOLUME_ADJUST_SOUNDS_ENABLED = "volume_adjust_sounds_enabled" -SYSTEM_MASTER_MONO = "master_mono" -SYSTEM_MASTER_BALANCE = "master_balance" -SYSTEM_NOTIFICATION_LIGHT_PULSE = "notification_light_pulse" -SYSTEM_NOTIFICATION_LIGHT_COLOR = "notification_light_color" -SYSTEM_NOTIFICATION_LIGHT_ON_TIME = "notification_light_on_time" -SYSTEM_NOTIFICATION_LIGHT_OFF_TIME = "notification_light_off_time" -SYSTEM_SWIPE_TO_SCREENSHOT_ENABLED = "swipe_to_screenshot_enabled" -SYSTEM_BATTERY_LIGHT_ENABLED = "battery_light_enabled" -SYSTEM_BATTERY_LIGHT_COLOR = "battery_light_color" -SYSTEM_BATTERY_LIGHT_ON_TIME = "battery_light_on_time" -SYSTEM_BATTERY_LIGHT_OFF_TIME = "battery_light_off_time" -SYSTEM_SCREEN_AUTO_BRIGHTNESS_ADJ = "screen_auto_brightness_adj" -SYSTEM_SCREEN_BRIGHTNESS_FLOAT = "screen_brightness_float" -SYSTEM_VOLUME_HUSH_GESTURE = "volume_hush_gesture" - - -class SettingsSystem(_SettingsBase): - """Settings.System — 系统设置表""" - - _NAME = "settings_system" - - # Airplane mode - AIRPLANE_MODE_ON = SYSTEM_AIRPLANE_MODE_ON - AIRPLANE_MODE_RADIOS = SYSTEM_AIRPLANE_MODE_RADIOS - AIRPLANE_MODE_TOGGLEABLE_RADIOS = SYSTEM_AIRPLANE_MODE_TOGGLEABLE_RADIOS - - # Volume - VOLUME_ALARM = SYSTEM_VOLUME_ALARM - VOLUME_BLUETOOTH_SCO = SYSTEM_VOLUME_BLUETOOTH_SCO - VOLUME_DTMF = SYSTEM_VOLUME_DTMF - VOLUME_MUSIC = SYSTEM_VOLUME_MUSIC - VOLUME_NOTIFICATION = SYSTEM_VOLUME_NOTIFICATION - VOLUME_RING = SYSTEM_VOLUME_RING - VOLUME_SYSTEM = SYSTEM_VOLUME_SYSTEM - VOLUME_VOICE = SYSTEM_VOLUME_VOICE - VOLUME_MASTER = SYSTEM_VOLUME_MASTER - VOLUME_MASTER_MUTE = SYSTEM_VOLUME_MASTER_MUTE - VOLUME_MICROPHONE_MUTE = SYSTEM_VOLUME_MICROPHONE_MUTE - - # Screen - SCREEN_BRIGHTNESS = SYSTEM_SCREEN_BRIGHTNESS - SCREEN_BRIGHTNESS_MODE = SYSTEM_SCREEN_BRIGHTNESS_MODE - SCREEN_BRIGHTNESS_MODE_MANUAL = SYSTEM_SCREEN_BRIGHTNESS_MODE_MANUAL - SCREEN_BRIGHTNESS_MODE_AUTOMATIC = SYSTEM_SCREEN_BRIGHTNESS_MODE_AUTOMATIC - SCREEN_OFF_TIMEOUT = SYSTEM_SCREEN_OFF_TIMEOUT - SCREEN_AUTO_BRIGHTNESS_ADJ = SYSTEM_SCREEN_AUTO_BRIGHTNESS_ADJ - ACCELEROMETER_ROTATION = SYSTEM_ACCELEROMETER_ROTATION - USER_ROTATION = SYSTEM_USER_ROTATION - HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY = SYSTEM_HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY - - # Sound - ALARM_ALERT = SYSTEM_ALARM_ALERT - NOTIFICATION_SOUND = SYSTEM_NOTIFICATION_SOUND - RINGTONE = SYSTEM_RINGTONE - DTMF_TONE_WHEN_DIALING = SYSTEM_DTMF_TONE_WHEN_DIALING - SOUND_EFFECTS_ENABLED = SYSTEM_SOUND_EFFECTS_ENABLED - HAPTIC_FEEDBACK_ENABLED = SYSTEM_HAPTIC_FEEDBACK_ENABLED - VIBRATE_ON = SYSTEM_VIBRATE_ON - MODE_RINGER = SYSTEM_MODE_RINGER - MODE_RINGER_STREAMS_AFFECTED = SYSTEM_MODE_RINGER_STREAMS_AFFECTED - MUTE_STREAMS_AFFECTED = SYSTEM_MUTE_STREAMS_AFFECTED - VIBRATE_INPUT_DEVICES = SYSTEM_VIBRATE_INPUT_DEVICES - VOLUME_ADJUST_SOUNDS_ENABLED = SYSTEM_VOLUME_ADJUST_SOUNDS_ENABLED - - # Dock - DOCK_SOUNDS_ENABLED = SYSTEM_DOCK_SOUNDS_ENABLED - CAR_DOCK_SOUND = SYSTEM_CAR_DOCK_SOUND - CAR_UNDOCK_SOUND = SYSTEM_CAR_UNDOCK_SOUND - DESK_DOCK_SOUND = SYSTEM_DESK_DOCK_SOUND - DESK_UNDOCK_SOUND = SYSTEM_DESK_UNDOCK_SOUND - POWER_SOUNDS_ENABLED = SYSTEM_POWER_SOUNDS_ENABLED - LOCKSCREEN_SOUNDS_ENABLED = SYSTEM_LOCKSCREEN_SOUNDS_ENABLED - UNLOCK_SOUND = SYSTEM_UNLOCK_SOUND - HOME_SOUND = SYSTEM_HOME_SOUND - HOME_UNDOCK_SOUND = SYSTEM_HOME_UNDOCK_SOUND - - # Time - AUTO_TIME = SYSTEM_AUTO_TIME - AUTO_TIME_ZONE = SYSTEM_AUTO_TIME_ZONE - NEXT_ALARM_FORMATTED = SYSTEM_NEXT_ALARM_FORMATTED - - # Bluetooth - BLUETOOTH_ON = SYSTEM_BLUETOOTH_ON - BLUETOOTH_DISCOVERABILITY = SYSTEM_BLUETOOTH_DISCOVERABILITY - BLUETOOTH_DISCOVERABILITY_TIMEOUT = SYSTEM_BLUETOOTH_DISCOVERABILITY_TIMEOUT - - # Wi-Fi - WIFI_ON = SYSTEM_WIFI_ON - WIFI_SLEEP_POLICY = SYSTEM_WIFI_SLEEP_POLICY - WIFI_SLEEP_POLICY_DEFAULT = SYSTEM_WIFI_SLEEP_POLICY_DEFAULT - WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = SYSTEM_WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED - WIFI_SLEEP_POLICY_NEVER = SYSTEM_WIFI_SLEEP_POLICY_NEVER - WIFI_MAX_DHCP_RETRY_COUNT = SYSTEM_WIFI_MAX_DHCP_RETRY_COUNT - WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = SYSTEM_WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS - WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = SYSTEM_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON - WIFI_NUM_OPEN_NETWORKS_KEPT = SYSTEM_WIFI_NUM_OPEN_NETWORKS_KEPT - WIFI_STATIC_DHCP_HOSTNAME = SYSTEM_WIFI_STATIC_DHCP_HOSTNAME - WIFI_STATIC_IP = SYSTEM_WIFI_STATIC_IP - WIFI_STATIC_GATEWAY = SYSTEM_WIFI_STATIC_GATEWAY - WIFI_STATIC_NETMASK = SYSTEM_WIFI_STATIC_NETMASK - WIFI_STATIC_DNS1 = SYSTEM_WIFI_STATIC_DNS1 - WIFI_STATIC_DNS2 = SYSTEM_WIFI_STATIC_DNS2 - - # Animation - WINDOW_ANIMATION_SCALE = SYSTEM_WINDOW_ANIMATION_SCALE - TRANSITION_ANIMATION_SCALE = SYSTEM_TRANSITION_ANIMATION_SCALE - ANIMATOR_DURATION_SCALE = SYSTEM_ANIMATOR_DURATION_SCALE - - # Text - FONT_SCALE = SYSTEM_FONT_SCALE - SHOW_WEB_SUGGESTIONS = SYSTEM_SHOW_WEB_SUGGESTIONS - - # Other - ANDROID_ID = SYSTEM_ANDROID_ID - EGG_MODE = SYSTEM_EGG_MODE - END_BUTTON_BEHAVIOR = SYSTEM_END_BUTTON_BEHAVIOR - HTTP_PROXY = SYSTEM_HTTP_PROXY - SETUP_WIZARD_HAS_RUN = SYSTEM_SETUP_WIZARD_HAS_RUN - SHOW_GTALK_SERVICE_STATUS = SYSTEM_SHOW_GTALK_SERVICE_STATUS - SHOW_PROCESSES = SYSTEM_SHOW_PROCESSES - STAY_ON_WHILE_PLUGGED_IN = SYSTEM_STAY_ON_WHILE_PLUGGED_IN - TTY_MODE = SYSTEM_TTY_MODE - SIP_RECEIVE_CALLS = SYSTEM_SIP_RECEIVE_CALLS - SIP_CALL_OPTIONS = SYSTEM_SIP_CALL_OPTIONS - HEARING_AID = SYSTEM_HEARING_AID - SETTINGS_CLASSNAME = SYSTEM_SETTINGS_CLASSNAME - LOCKSCREEN_DISABLED = SYSTEM_LOCKSCREEN_DISABLED - LOCK_TO_APP_ENABLED = SYSTEM_LOCK_TO_APP_ENABLED - ACCESSIBILITY_ENABLED = "accessibility_enabled" - MASTER_MONO = SYSTEM_MASTER_MONO - MASTER_BALANCE = SYSTEM_MASTER_BALANCE - NOTIFICATION_LIGHT_PULSE = SYSTEM_NOTIFICATION_LIGHT_PULSE - NOTIFICATION_LIGHT_COLOR = SYSTEM_NOTIFICATION_LIGHT_COLOR - NOTIFICATION_LIGHT_ON_TIME = SYSTEM_NOTIFICATION_LIGHT_ON_TIME - NOTIFICATION_LIGHT_OFF_TIME = SYSTEM_NOTIFICATION_LIGHT_OFF_TIME - SWIPE_TO_SCREENSHOT_ENABLED = SYSTEM_SWIPE_TO_SCREENSHOT_ENABLED - BATTERY_LIGHT_ENABLED = SYSTEM_BATTERY_LIGHT_ENABLED - BATTERY_LIGHT_COLOR = SYSTEM_BATTERY_LIGHT_COLOR - BATTERY_LIGHT_ON_TIME = SYSTEM_BATTERY_LIGHT_ON_TIME - BATTERY_LIGHT_OFF_TIME = SYSTEM_BATTERY_LIGHT_OFF_TIME - VOLUME_HUSH_GESTURE = SYSTEM_VOLUME_HUSH_GESTURE - - # Device provisioning - DEVICE_PROVISIONED = "device_provisioned" - - @classmethod - def getString(cls, key: str, default_value: Optional[str] = None) -> Optional[str]: - return super().getString(key, default_value) - - @classmethod - def putString(cls, key: str, value: str) -> bool: - return super().putString(key, value) - - @classmethod - def getInt(cls, key: str, default_value: int = 0) -> int: - return super().getInt(key, default_value) - - @classmethod - def putInt(cls, key: str, value: int) -> bool: - return super().putInt(key, value) - - @classmethod - def getFloat(cls, key: str, default_value: float = 0.0) -> float: - return super().getFloat(key, default_value) - - @classmethod - def putFloat(cls, key: str, value: float) -> bool: - return super().putFloat(key, value) - - @classmethod - def getLong(cls, key: str, default_value: int = 0) -> int: - return super().getLong(key, default_value) - - @classmethod - def putLong(cls, key: str, value: int) -> bool: - return super().putLong(key, value) - - -# ═══════════════════════════════════════════════════════════════ -# Settings.Secure — 安全设置 -# ═══════════════════════════════════════════════════════════════ - -SECURE_ADB_ENABLED = "adb_enabled" -SECURE_ALLOW_MOCK_LOCATION = "mock_location" -SECURE_ALLOW_MOCK_LOCATION = "mock_location" -SECURE_ANDROID_ID = "android_id" -SECURE_BACKGROUND_PROCESS_LIMIT = "background_process_limit" -SECURE_BLUETOOTH_HCI_LOG = "bluetooth_hci_log" -SECURE_BLUETOOTH_ON = "bluetooth_on" -SECURE_CAMERA_GESTURE_DISABLED = "camera_gesture_disabled" -SECURE_CAMERA_GESTURE_SOUND_ENABLED = "camera_gesture_sound_enabled" -SECURE_CAPTIVE_PORTAL_DETECTION_ENABLED = "captive_portal_detection_enabled" -SECURE_CAPTIVE_PORTAL_SERVER = "captive_portal_server" -SECURE_DEVELOPER_TOOLS_ENABLED = "developer_tools_enabled" -SECURE_DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled" -SECURE_DEVICE_PROVISIONED = "device_provisioned" -SECURE_DOZE_ENABLED = "doze_enabled" -SECURE_DOZE_PULSE_ON_PICK = "doze_pulse_on_pick" -SECURE_DOZE_PULSE_ON_LONG_PRESS = "doze_pulse_on_long_press" -SECURE_DOZE_PULSE_ON_TAP = "doze_pulse_on_tap" -SECURE_ENHANCED_VOICE_PRIVACY_ENABLED = "enhanced_voice_privacy_enabled" -SECURE_FACTORY_RESET = "factory_reset" -SECURE_GPS = "gps" -SECURE_GUEST_USER_ENABLED = "guest_user_enabled" -SECURE_HDCP_CHECKING = "hdcp_checking" -SECURE_HTTP_PROXY = "http_proxy" -SECURE_IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations" -SECURE_INSTALL_NON_MARKET_APPS = "install_non_market_apps" -SECURE_INSTANT_APPS_ENABLED = "instant_apps_enabled" -SECURE_KEYGUARD_DISABLE_LAYOUT = "keyguard_disable_layout" -SECURE_KEYGUARD_SLIDE_ENABLED = "keyguard_slide_enabled" -SECURE_LOCATION_MODE = "location_mode" -SECURE_LOCATION_MODE_HIGH_ACCURACY = 3 -SECURE_LOCATION_MODE_SENSORS_ONLY = 1 -SECURE_LOCATION_MODE_BATTERY_SAVING = 2 -SECURE_LOCATION_MODE_OFF = 0 -SECURE_LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed" -SECURE_LOCK_BIOMETRIC_WEAK_FALLBACK = "lock_biometric_weak_fallback" -SECURE_LOCK_PATTERN_ENABLED = "lock_pattern_autolock" -SECURE_LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern" -SECURE_LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled" -SECURE_LOCK_PIN_ENABLED = "lock_pin_enabled" -SECURE_LOCK_PASSWORD_ENABLED = "lock_password_enabled" -SECURE_LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications" -SECURE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = "lock_screen_allow_remote_input" -SECURE_LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications" -SECURE_LOCK_SCREEN_LOCK_AFTER_TIMEOUT = "lock_screen_lock_after_timeout" -SECURE_LOCK_SCREEN_OWNER_INFO = "lock_screen_owner_info" -SECURE_LOCK_SCREEN_OWNER_INFO_ENABLED = "lock_screen_owner_info_enabled" -SECURE_LOCK_SCREEN_WHEN_LOCKED = "lock_screen_when_locked" -SECURE_LOCK_TO_APP_EXIT_LOCKED = "lock_to_app_exit_locked" -SECURE_MANAGED_PROFILE_CONTACT_REMOTE_SEARCH = "managed_profile_contact_remote_search" -SECURE_MOUNT_FILESYSTEM = "mount_filesystem" -SECURE_MULTI_SIM_DATA_SUBSCRIPTION = "multi_sim_data_subscription" -SECURE_MULTI_SIM_SMS_SUBSCRIPTION = "multi_sim_sms_subscription" -SECURE_MULTI_SIM_VOICE_SUBSCRIPTION = "multi_sim_voice_subscription" -SECURE_NOTIFICATION_BADGING = "notification_badging" -SECURE_NOTIFICATION_BUBBLES = "notification_bubbles" -SECURE_NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners" -SECURE_NOTIFICATION_SHOW_MEDIA_PLAYER = "notification_show_media_player" -SECURE_NFC_ON = "nfc_on" -SECURE_NIGHT_DISPLAY_ACTIVATED = "night_display_activated" -SECURE_NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode" -SECURE_NIGHT_DISPLAY_COLOR_TEMPERATURE = "night_display_color_temperature" -SECURE_NIGHT_DISPLAY_CUSTOM_START_TIME = "night_display_custom_start_time" -SECURE_NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time" -SECURE_PACKAGE_VERIFIER_ENABLE = "package_verifier_enable" -SECURE_PACKAGE_VERIFIER_INCLUDE_ADB = "package_verifier_include_adb" -SECURE_PACKAGE_VERIFIER_TIMEOUT = "package_verifier_timeout" -SECURE_PARENTAL_CONTROL_ENABLED = "parental_control_enabled" -SECURE_PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update" -SECURE_PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url" -SECURE_RTT_ENABLED = "rtt_enabled" -SECURE_SCREENSAVER_COMPONENTS = "screensaver_components" -SECURE_SCREENSAVER_DEFAULTS = "screensaver_defaults" -SECURE_SCREENSAVER_ACTIVATE_ON_SLEEP = "screensaver_activate_on_sleep" -SECURE_SCREENSAVER_ACTIVATE_ON_DOCK = "screensaver_activate_on_dock" -SECURE_SCREENSAVER_ENABLED = "screensaver_enabled" -SECURE_SKIP_FIRST_USE_HINTS = "skip_first_use_hints" -SECURE_SLEEP_TIMEOUT = "sleep_timeout" -SECURE_SMS_DEFAULT_APPLICATION = "sms_default_application" -SECURE_THEME_MODE = "theme_mode" -SECURE_TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled" -SECURE_UI_NIGHT_MODE = "ui_night_mode" -SECURE_USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled" -SECURE_USER_SETUP_COMPLETE = "user_setup_complete" -SECURE_VERIFY_APPS = "verify_apps" -SECURE_VERIFY_APPS = "verify_apps" -SECURE_VOLUME_CONTROLLER_AS_MEDIA = "volume_controller_as_media" -SECURE_VR_DEVELOPER_MODE_ENABLED = "vr_developer_mode_enabled" -SECURE_VR_DISPLAY_MODE = "vr_display_mode" -SECURE_VR_LAUNCH_POLICY = "vr_launch_policy" -SECURE_WAKE_GESTURE_ENABLED = "wake_gesture_enabled" -SECURE_WIFI_AP_INTERNET_AVAILABLE = "wifi_ap_internet_available" -SECURE_WIFI_AUTO_CONNECT_TYPE = "wifi_auto_connect_type" -SCRUBBER_WIFI_IDLE_MS = "wifi_idle_ms" -SECURE_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on" -SECURE_WIFI_WATCHDOG_ON = "wifi_watchdog_on" -SECURE_WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED = "wifi_watchdog_poor_network_test_enabled" -SECURE_WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE = "wifi_watchdog_acceptable_packet_loss_percentage" -SECURE_WIFI_WATCHDOG_AP_COUNT = "wifi_watchdog_ap_count" -SECURE_WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS = "wifi_watchdog_background_check_delay_ms" -SECURE_WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED = "wifi_watchdog_background_check_enabled" -SECURE_WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS = "wifi_watchdog_background_check_timeout_ms" -SECURE_WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT = "wifi_watchdog_initial_ignored_ping_count" -SECURE_WIFI_WATCHDOG_PING_COUNT = "wifi_watchdog_ping_count" -SECURE_WIFI_WATCHDOG_PING_DELAY_MS = "wifi_watchdog_ping_delay_ms" -SECURE_WIFI_WATCHDOG_PING_TIMEOUT_MS = "wifi_watchdog_ping_timeout_ms" -SECURE_WIFI_WATCHDOG_WATCH_LIST = "wifi_watchdog_watch_list" -SECURE_WIFI_WATCHDOG_DNS_CHECK_COUNT = "wifi_watchdog_dns_check_count" -SECURE_WIFI_WATCHDOG_DNS_CHECK_TIMEOUT_MS = "wifi_watchdog_dns_check_timeout_ms" -SECURE_WIFI_WATCHDOG_MAX_AP_CHECKS = "wifi_watchdog_max_ap_checks" -SECURE_WIFI_WATCHDOG_TEST_SIZE_BYTES = "wifi_watchdog_test_size_bytes" -SECURE_WIFI_WATCHDOG_TRIGGER_DELAY_MS = "wifi_watchdog_trigger_delay_ms" -SECURE_WIFI_AUTO_CONNECT_TYPE = SECURE_WIFI_AUTO_CONNECT_TYPE - - -class SettingsSecure(_SettingsBase): - """Settings.Secure — 安全设置表""" - - _NAME = "settings_secure" - - # Development - ADB_ENABLED = SECURE_ADB_ENABLED - ALLOW_MOCK_LOCATION = SECURE_ALLOW_MOCK_LOCATION - DEVELOPMENT_SETTINGS_ENABLED = SECURE_DEVELOPMENT_SETTINGS_ENABLED - DEVELOPMENT_TOOLS_ENABLED = SECURE_DEVELOPER_TOOLS_ENABLED - BACKGROUND_PROCESS_LIMIT = SECURE_BACKGROUND_PROCESS_LIMIT - BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu" - FULL_BUGREPORT_ENABLED = "full_bugreport_enabled" - OEM_UNLOCK_ENABLED = "oem_unlock_enabled" - PROCESS_STATS_ENABLED = "process_stats_enabled" - STRICT_MODE_ENABLED = "strict_mode_enabled" - - # Security - INSTALL_NON_MARKET_APPS = SECURE_INSTALL_NON_MARKET_APPS - VERIFY_APPS = SECURE_VERIFY_APPS - PACKAGE_VERIFIER_ENABLE = SECURE_PACKAGE_VERIFIER_ENABLE - PACKAGE_VERIFIER_INCLUDE_ADB = SECURE_PACKAGE_VERIFIER_INCLUDE_ADB - PACKAGE_VERIFIER_TIMEOUT = SECURE_PACKAGE_VERIFIER_TIMEOUT - LOCK_PATTERN_ENABLED = SECURE_LOCK_PATTERN_ENABLED - LOCK_PATTERN_VISIBLE = SECURE_LOCK_PATTERN_VISIBLE - LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = SECURE_LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED - LOCK_PIN_ENABLED = SECURE_LOCK_PIN_ENABLED - LOCK_PASSWORD_ENABLED = SECURE_LOCK_PASSWORD_ENABLED - LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = SECURE_LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS - LOCK_SCREEN_ALLOW_REMOTE_INPUT = SECURE_LOCK_SCREEN_ALLOW_REMOTE_INPUT - LOCK_SCREEN_SHOW_NOTIFICATIONS = SECURE_LOCK_SCREEN_SHOW_NOTIFICATIONS - LOCK_SCREEN_LOCK_AFTER_TIMEOUT = SECURE_LOCK_SCREEN_LOCK_AFTER_TIMEOUT - LOCK_SCREEN_OWNER_INFO = SECURE_LOCK_SCREEN_OWNER_INFO - LOCK_SCREEN_OWNER_INFO_ENABLED = SECURE_LOCK_SCREEN_OWNER_INFO_ENABLED - LOCK_SCREEN_WHEN_LOCKED = SECURE_LOCK_SCREEN_WHEN_LOCKED - LOCK_TO_APP_EXIT_LOCKED = SECURE_LOCK_TO_APP_EXIT_LOCKED - KEYGUARD_DISABLE_LAYOUT = SECURE_KEYGUARD_DISABLE_LAYOUT - KEYGUARD_SLIDE_ENABLED = SECURE_KEYGUARD_SLIDE_ENABLED - LOCK_BIOMETRIC_WEAK_FALLBACK = SECURE_LOCK_BIOMETRIC_WEAK_FALLBACK - PARENTAL_CONTROL_ENABLED = SECURE_PARENTAL_CONTROL_ENABLED - PARENTAL_CONTROL_LAST_UPDATE = SECURE_PARENTAL_CONTROL_LAST_UPDATE - PARENTAL_CONTROL_REDIRECT_URL = SECURE_PARENTAL_CONTROL_REDIRECT_URL - - # Location - LOCATION_MODE = SECURE_LOCATION_MODE - LOCATION_MODE_HIGH_ACCURACY = SECURE_LOCATION_MODE_HIGH_ACCURACY - LOCATION_MODE_SENSORS_ONLY = SECURE_LOCATION_MODE_SENSORS_ONLY - LOCATION_MODE_BATTERY_SAVING = SECURE_LOCATION_MODE_BATTERY_SAVING - LOCATION_MODE_OFF = SECURE_LOCATION_MODE_OFF - LOCATION_PROVIDERS_ALLOWED = SECURE_LOCATION_PROVIDERS_ALLOWED - GPS = SECURE_GPS - - # Wi-Fi watchdog - WIFI_WATCHDOG_ON = SECURE_WIFI_WATCHDOG_ON - WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED = SECURE_WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED - WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE = SECURE_WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE - WIFI_WATCHDOG_AP_COUNT = SECURE_WIFI_WATCHDOG_AP_COUNT - WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS = SECURE_WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS - WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED = SECURE_WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED - WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS = SECURE_WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS - WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT = SECURE_WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT - WIFI_WATCHDOG_PING_COUNT = SECURE_WIFI_WATCHDOG_PING_COUNT - WIFI_WATCHDOG_PING_DELAY_MS = SECURE_WIFI_WATCHDOG_PING_DELAY_MS - WIFI_WATCHDOG_PING_TIMEOUT_MS = SECURE_WIFI_WATCHDOG_PING_TIMEOUT_MS - WIFI_WATCHDOG_WATCH_LIST = SECURE_WIFI_WATCHDOG_WATCH_LIST - WIFI_AUTO_CONNECT_TYPE = SECURE_WIFI_AUTO_CONNECT_TYPE - WIFI_IDLE_MS = SCRUBBER_WIFI_IDLE_MS - WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = SECURE_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON - WIFI_AP_INTERNET_AVAILABLE = SECURE_WIFI_AP_INTERNET_AVAILABLE - - # USB - USB_MASS_STORAGE_ENABLED = SECURE_USB_MASS_STORAGE_ENABLED - - # Accessibility - ACCESSIBILITY_ENABLED = "accessibility_enabled" - TOUCH_EXPLORATION_ENABLED = SECURE_TOUCH_EXPLORATION_ENABLED - ENHANCED_VOICE_PRIVACY_ENABLED = SECURE_ENHANCED_VOICE_PRIVACY_ENABLED - ACCESSIBILITY_SCRIPT_INJECTION = "accessibility_script_injection" - ACCESSIBILITY_SPEAK_PASSWORD = "accessibility_speak_password" - ACCESSIBILITY_WEB_CONTENT_KEY = "accessibility_web_content_key" - TOUCH_EXPLORATION_ENABLED = SECURE_TOUCH_EXPLORATION_ENABLED - NOTIFICATION_ENABLED_LISTENERS = SECURE_NOTIFICATION_ENABLED_LISTENERS - NOTIFICATION_BADGING = SECURE_NOTIFICATION_BADGING - NOTIFICATION_BUBBLES = SECURE_NOTIFICATION_BUBBLES - NOTIFICATION_SHOW_MEDIA_PLAYER = SECURE_NOTIFICATION_SHOW_MEDIA_PLAYER - - # Display - DOZE_ENABLED = SECURE_DOZE_ENABLED - DOZE_PULSE_ON_PICK = SECURE_DOZE_PULSE_ON_PICK - DOZE_PULSE_ON_LONG_PRESS = SECURE_DOZE_PULSE_ON_LONG_PRESS - DOZE_PULSE_ON_TAP = SECURE_DOZE_PULSE_ON_TAP - WAKE_GESTURE_ENABLED = SECURE_WAKE_GESTURE_ENABLED - CAMERA_GESTURE_DISABLED = SECURE_CAMERA_GESTURE_DISABLED - CAMERA_GESTURE_SOUND_ENABLED = SECURE_CAMERA_GESTURE_SOUND_ENABLED - NIGHT_DISPLAY_ACTIVATED = SECURE_NIGHT_DISPLAY_ACTIVATED - NIGHT_DISPLAY_AUTO_MODE = SECURE_NIGHT_DISPLAY_AUTO_MODE - NIGHT_DISPLAY_COLOR_TEMPERATURE = SECURE_NIGHT_DISPLAY_COLOR_TEMPERATURE - NIGHT_DISPLAY_CUSTOM_START_TIME = SECURE_NIGHT_DISPLAY_CUSTOM_START_TIME - NIGHT_DISPLAY_CUSTOM_END_TIME = SECURE_NIGHT_DISPLAY_CUSTOM_END_TIME - THEME_MODE = SECURE_THEME_MODE - UI_NIGHT_MODE = SECURE_UI_NIGHT_MODE - SCREENSAVER_ENABLED = SECURE_SCREENSAVER_ENABLED - SCREENSAVER_COMPONENTS = SECURE_SCREENSAVER_COMPONENTS - SCREENSAVER_DEFAULTS = SECURE_SCREENSAVER_DEFAULTS - SCREENSAVER_ACTIVATE_ON_SLEEP = SECURE_SCREENSAVER_ACTIVATE_ON_SLEEP - SCREENSAVER_ACTIVATE_ON_DOCK = SECURE_SCREENSAVER_ACTIVATE_ON_DOCK - SLEEP_TIMEOUT = SECURE_SLEEP_TIMEOUT - IMMERSIVE_MODE_CONFIRMATIONS = SECURE_IMMERSIVE_MODE_CONFIRMATIONS - - # Network - CAPTIVE_PORTAL_DETECTION_ENABLED = SECURE_CAPTIVE_PORTAL_DETECTION_ENABLED - CAPTIVE_PORTAL_SERVER = SECURE_CAPTIVE_PORTAL_SERVER - HTTP_PROXY = SECURE_HTTP_PROXY - RTT_ENABLED = SECURE_RTT_ENABLED - NFC_ON = SECURE_NFC_ON - BLUETOOTH_ON = SECURE_BLUETOOTH_ON - BLUETOOTH_HCI_LOG = SECURE_BLUETOOTH_HCI_LOG - MULTI_SIM_DATA_SUBSCRIPTION = SECURE_MULTI_SIM_DATA_SUBSCRIPTION - MULTI_SIM_SMS_SUBSCRIPTION = SECURE_MULTI_SIM_SMS_SUBSCRIPTION - MULTI_SIM_VOICE_SUBSCRIPTION = SECURE_MULTI_SIM_VOICE_SUBSCRIPTION - SMS_DEFAULT_APPLICATION = SECURE_SMS_DEFAULT_APPLICATION - - # Device - DEVICE_PROVISIONED = SECURE_DEVICE_PROVISIONED - ANDROID_ID = SECURE_ANDROID_ID - FACTORY_RESET = SECURE_FACTORY_RESET - USER_SETUP_COMPLETE = SECURE_USER_SETUP_COMPLETE - SKIP_FIRST_USE_HINTS = SECURE_SKIP_FIRST_USE_HINTS - GUEST_USER_ENABLED = SECURE_GUEST_USER_ENABLED - MANAGED_PROFILE_CONTACT_REMOTE_SEARCH = SECURE_MANAGED_PROFILE_CONTACT_REMOTE_SEARCH - MOUNT_FILESYSTEM = SECURE_MOUNT_FILESYSTEM - HDCP_CHECKING = SECURE_HDCP_CHECKING - INSTANT_APPS_ENABLED = SECURE_INSTANT_APPS_ENABLED - - # Volume - VOLUME_CONTROLLER_AS_MEDIA = SECURE_VOLUME_CONTROLLER_AS_MEDIA - - # VR - VR_DEVELOPER_MODE_ENABLED = SECURE_VR_DEVELOPER_MODE_ENABLED - VR_DISPLAY_MODE = SECURE_VR_DISPLAY_MODE - VR_LAUNCH_POLICY = SECURE_VR_LAUNCH_POLICY - - @classmethod - def getString(cls, key: str, default_value: Optional[str] = None) -> Optional[str]: - return super().getString(key, default_value) - - @classmethod - def putString(cls, key: str, value: str) -> bool: - return super().putString(key, value) - - @classmethod - def getInt(cls, key: str, default_value: int = 0) -> int: - return super().getInt(key, default_value) - - @classmethod - def putInt(cls, key: str, value: int) -> bool: - return super().putInt(key, value) - - @classmethod - def getFloat(cls, key: str, default_value: float = 0.0) -> float: - return super().getFloat(key, default_value) - - @classmethod - def putFloat(cls, key: str, value: float) -> bool: - return super().putFloat(key, value) - - @classmethod - def getLong(cls, key: str, default_value: int = 0) -> int: - return super().getLong(key, default_value) - - @classmethod - def putLong(cls, key: str, value: int) -> bool: - return super().putLong(key, value) - - -# ═══════════════════════════════════════════════════════════════ -# Settings.Global — 全局设置 -# ═══════════════════════════════════════════════════════════════ - -GLOBAL_AIRPLANE_MODE_ON = "airplane_mode_on" -GLOBAL_AIRPLANE_MODE_RADIOS = "airplane_mode_radios" -GLOBAL_AIRPLANE_MODE_TOGGLEABLE_RADIOS = "airplane_mode_toggleable_radios" -GLOBAL_ALARM_ALERT = "alarm_alert" -GLOBAL_ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED = "allow_user_switching_when_system_user_locked" -GLOBAL_ALWAYS_ON_DISPLAY_BRIGHTNESS = "always_on_display_brightness" -GLOBAL_ALWAYS_ON_DISPLAY_CONSTANT_BRIGHTNESS = "always_on_display_constant_brightness" -GLOBAL_ALWAYS_FINISH_ACTIVITIES = "always_finish_activities" -GLOBAL_ANIMATOR_DURATION_SCALE = "animator_duration_scale" -GLOBAL_APP_STANDBY_ENABLED = "app_standby_enabled" -GLOBAL_AUTO_TIME = "auto_time" -GLOBAL_AUTO_TIME_ZONE = "auto_time_zone" -GLOBAL_BATTERY_DISCHARGE_DURATION_THRESHOLD = "battery_discharge_duration_threshold" -GLOBAL_BATTERY_DISCHARGE_THRESHOLD = "battery_discharge_threshold" -GLOBAL_BLUETOOTH_CLASS_OF_DEVICE = "bluetooth_class_of_device" -GLOBAL_BLUETOOTH_DISABLED_PROFILES = "bluetooth_disabled_profiles" -GLOBAL_BLUETOOTH_INTEROPERABILITY_LIST = "bluetooth_interoperability_list" -GLOBAL_BLUETOOTH_ON = "bluetooth_on" -GLOBAL_BOOT_COUNT = "boot_count" -GLOBAL_BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu" -GLOBAL_CACHED_APPS_FREEZER_ENABLED = "cached_apps_freezer_enabled" -GLOBAL_CAPTIVE_PORTAL_DETECTION_ENABLED = "captive_portal_detection_enabled" -GLOBAL_CAPTIVE_PORTAL_SERVER = "captive_portal_server" -GLOBAL_CARRIER_APP_NAMES = "carrier_app_names" -GLOBAL_CARRIER_APP_WHITELIST = "carrier_app_whitelist" -GLOBAL_CDMA_CELL_BROADCAST_SMS = "cdma_cell_broadcast_sms" -GLOBAL_CDMA_ROAMING_MODE = "cdma_roaming_mode" -GLOBAL_CDMA_SUBSCRIPTION = "cdma_subscription" -GLOBAL_CELL_ON = "cell_on" -GLOBAL_COMPATIBILITY_MODE = "compatibility_mode" -GLOBAL_CONNECTIVITY_MANAGER_INITIALIZED = "connectivity_manager_initialized" -GLOBAL_CONTACTS_ACCOUNT_TYPE = "contacts_account_type" -GLOBAL_CONTACTS_DATABASE_UPDATE = "contacts_database_update" -GLOBAL_DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile" -GLOBAL_DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi" -GLOBAL_DATA_ROAMING = "data_roaming" -GLOBAL_DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS = "data_stall_alarm_non_aggressive_delay_in_ms" -GLOBAL_DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS = "data_stall_alarm_aggressive_delay_in_ms" -GLOBAL_DEBUG_APP = "debug_app" -GLOBAL_DEBUG_VIEW_ATTRIBUTES = "debug_view_attributes" -GLOBAL_DEFAULT_INSTALL_LOCATION = "default_install_location" -GLOBAL_DEFAULT_SIM_SETTINGS = "default_sim_settings" -GLOBAL_DEFAULT_WIFI_INTERFACE = "default_wifi_interface" -GLOBAL_DEVICE_DEMO_MODE = "device_demo_mode" -GLOBAL_DEVICE_IDLE_CONSTANTS = "device_idle_constants" -GLOBAL_DEVICE_NAME = "device_name" -GLOBAL_DEVICE_PROVISIONED = "device_provisioned" -GLOBAL_DEVICE_PROVISIONING_MOBILE_DATA_ENABLED = "device_provisioning_mobile_data_enabled" -GLOBAL_DEVELOPMENT_ENABLE_FREEFORM_WINDOW_SUPPORT = "development_enable_freeform_window_support" -GLOBAL_DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES = "development_force_resizable_activities" -GLOBAL_DEVELOPMENT_FORCE_RTL = "development_force_rtl" -GLOBAL_DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled" -GLOBAL_DISK_FREE_CHANGE_REPORTING_MINIMUM = "disk_free_change_reporting_minimum" -GLOBAL_DOCK_AUDIO_MEDIA_ENABLED = "dock_audio_media_enabled" -GLOBAL_DOCK_EVENT = "dock_event" -GLOBAL_DOWNLOAD_MAX_BYTES_OVER_MOBILE = "download_max_bytes_over_mobile" -GLOBAL_DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE = "download_recommended_max_bytes_over_mobile" -GLOBAL_DROPBOX_AGE_SECONDS = "dropbox_age_seconds" -GLOBAL_DROPBOX_MAX_FILES = "dropbox_max_files" -GLOBAL_DROPBOX_QUOTA_KB = "dropbox_quota_kb" -GLOBAL_DROPBOX_QUOTA_PERCENT = "dropbox_quota_percent" -GLOBAL_DROPBOX_RESERVE_PERCENT = "dropbox_reserve_percent" -GLOBAL_DROPBOX_TAG_PREFIX = "dropbox:" -GLOBAL_EMERGENCY_TONE = "emergency_tone" -GLOBAL_ENABLE_ACCESSIBILITY = "enable_accessibility" -GLOBAL_ENABLE_CELLULAR_ON_BOOT = "enable_cellular_on_boot" -GLOBAL_ENABLE_GPU_DEBUG_LAYERS = "enable_gpu_debug_layers" -GLOBAL_ENABLE_NETWORK_ACTIVITY = "enable_network_activity" -GLOBAL_ENCODED_SURROUND_OUTPUT = "encoded_surround_output" -GLOBAL_ENCODED_SURROUND_OUTPUT_ENABLED = "encoded_surround_output_enabled" -GLOBAL_EPHEMERAL_COOKIE_DURATION_SECONDS = "ephemeral_cookie_duration_seconds" -GLOBAL_FANCY_IME_ANIMATIONS = "fancy_ime_animations" -GLOBAL_FORCE_ALLOW_SCREEN_ON = "force_allow_screen_on" -GLOBAL_FORCE_ALLOW_SYSTEM_USER_ONLY = "force_allow_system_user_only" -GLOBAL_FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled" -GLOBAL_FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED = "forced_app_standby_for_small_battery_enabled" -GLOBAL_FORCED_APP_STANDBY_SMALL_BATTERY_THRESHOLD_MILLIS = "forced_app_standby_small_battery_threshold_millis" -GLOBAL_FSTRIM_MANDATORY_INTERVAL = "fstrim_mandatory_interval" -GLOBAL_GLOBAL_HTTP_PROXY_EXCLUSION_LIST = "global_http_proxy_exclusion_list" -GLOBAL_GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host" -GLOBAL_GLOBAL_HTTP_PROXY_PAC = "global_http_proxy_pac" -GLOBAL_GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port" -GLOBAL_GPRS_REGISTER_CHECK_PERIOD_MS = "gprs_register_check_period_ms" -GLOBAL_GUEST_USER_ENABLED = "guest_user_enabled" -GLOBAL_HDCP_CHECKING = "hdcp_checking" -GLOBAL_HEADS_UP_NOTIFICATIONS_ENABLED = "heads_up_notifications_enabled" -GLOBAL_HISTORY_DAYS = "history_days" -GLOBAL_HOTSPOT_APN = "hotspot_apn" -GLOBAL_HOTSPOT_APN_TYPE = "hotspot_apn_type" -GLOBAL_HOTSPOT_APN_AUTH_TYPE = "hotspot_apn_auth_type" -GLOBAL_HOTSPOT_APN_PROTOCOL = "hotspot_apn_protocol" -GLOBAL_HOTSPOT_APN_ROAMING_PROTOCOL = "hotspot_apn_roaming_protocol" -GLOBAL_HOTSPOT_APN_USER = "hotspot_apn_user" -GLOBAL_HOTSPOT_APN_PASS = "hotspot_apn_pass" -GLOBAL_HOTSPOT_CARRIER_ENABLED = "hotspot_carrier_enabled" -GLOBAL_HOTSPOT_ENABLED = "hotspot_enabled" -GLOBAL_HOTSPOT_SSID = "hotspot_ssid" -GLOBAL_HOTSPOT_SECURITY_TYPE = "hotspot_security_type" -GLOBAL_INET_CONDITION = "inet_condition" -GLOBAL_INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD = "installed_instant_app_min_cache_period" -GLOBAL_INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD = "installed_instant_app_max_cache_period" -GLOBAL_INSTANT_APP_DEXOPT_ENABLED = "instant_app_dexopt_enabled" -GLOBAL_INTENT_FIREWALL_MAIN_INTERACTION_FILTER = "intent_firewall_main_interaction_filter" -GLOBAL_INTENT_FIREWALL_COMPONENT_FILTER = "intent_firewall_component_filter" -GLOBAL_INTENT_FIREWALL_PACKAGE_FILTER = "intent_firewall_package_filter" -GLOBAL_INTENT_FIREWALL_CATEGORY_FILTER = "intent_firewall_category_filter" -GLOBAL_INTENT_FIREWALL_ACTION_FILTER = "intent_firewall_action_filter" -GLOBAL_INTENT_FIREWALL_TYPE = "intent_firewall_type" -GLOBAL_JOB_SCHEDULER_CONSTANTS = "job_scheduler_constants" -GLOBAL_KEEP_PROFILE_IN_BACKGROUND = "keep_profile_in_background" -GLOBAL_LID_BEHAVIOR = "lid_behavior" -GLOBAL_LOCK_SCREEN_WHEN_LOCKED = "lock_screen_when_locked" -GLOBAL_LOW_BATTERY_SOUND = "low_battery_sound" -GLOBAL_LOW_BATTERY_SOUND_TIMEOUT = "low_battery_sound_timeout" -GLOBAL_LOW_POWER_MODE = "low_power_mode" -GLOBAL_LOW_POWER_MODE_STICKY = "low_power_mode_sticky" -GLOBAL_LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_mode_trigger_level" -GLOBAL_MACHINE_UID = "machine_uid" -GLOBAL_MAX_NOTIFICATION_ENQUEUE_RATE = "max_notification_enqueue_rate" -GLOBAL_MEDIA_SCANNER_ON_BOOT = "media_scanner_on_boot" -GLOBAL_MOBILE_DATA = "mobile_data" -GLOBAL_MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on" -GLOBAL_MODE_RINGER = "mode_ringer" -GLOBAL_MULTI_SIM_DATA_CALL_SUBSCRIPTION = "multi_sim_data_call_subscription" -GLOBAL_NETSTATS_DEV_BUCKET_DURATION = "netstats_dev_bucket_duration" -GLOBAL_NETSTATS_DEV_DELETE_AGE = "netstats_dev_delete_age" -GLOBAL_NETSTATS_DEV_PERSIST_BYTES = "netstats_dev_persist_bytes" -GLOBAL_NETSTATS_DEV_ROTATE_AGE = "netstats_dev_rotate_age" -GLOBAL_NETSTATS_ENABLED = "netstats_enabled" -GLOBAL_NETSTATS_GLOBAL_ALERT_BYTES = "netstats_global_alert_bytes" -GLOBAL_NETSTATS_POLL_INTERVAL = "netstats_poll_interval" -GLOBAL_NETSTATS_SAMPLE_ENABLED = "netstats_sample_enabled" -GLOBAL_NETSTATS_TIME_CACHE_MAX_AGE = "netstats_time_cache_max_age" -GLOBAL_NETSTATS_UID_BUCKET_DURATION = "netstats_uid_bucket_duration" -GLOBAL_NETSTATS_UID_DELETE_AGE = "netstats_uid_delete_age" -GLOBAL_NETSTATS_UID_PERSIST_BYTES = "netstats_uid_persist_bytes" -GLOBAL_NETSTATS_UID_ROTATE_AGE = "netstats_uid_rotate_age" -GLOBAL_NETSTATS_UID_TAG_BUCKET_DURATION = "netstats_uid_tag_bucket_duration" -GLOBAL_NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age" -GLOBAL_NETSTATS_UID_TAG_PERSIST_BYTES = "netstats_uid_tag_persist_bytes" -GLOBAL_NETSTATS_UID_TAG_ROTATE_AGE = "netstats_uid_tag_rotate_age" -GLOBAL_NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi" -GLOBAL_NETWORK_METERED_MULTIPATH_PREFERENCE = "network_metered_multipath_preference" -GLOBAL_NETWORK_PREFERENCE = "network_preference" -GLOBAL_NETWORK_RECOMMENDATIONS_ENABLED = "network_recommendations_enabled" -GLOBAL_NETWORK_RECOMMENDATIONS_PACKAGE = "network_recommendations_package" -GLOBAL_NETWORK_SCORER_APP = "network_scorer_app" -GLOBAL_NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT = "network_switch_notification_daily_limit" -GLOBAL_NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS = "network_switch_notification_rate_limit_millis" -GLOBAL_NETWORK_WATCHLIST_ENABLED = "network_watchlist_enabled" -GLOBAL_NFC_ON = "nfc_on" -GLOBAL_NITZ_UPDATE_DIFF = "nitz_update_diff" -GLOBAL_NITZ_UPDATE_SPACING = "nitz_update_spacing" -GLOBAL_NOTIFICATION_BACKGROUNDING = "notification_backgrounding" -GLOBAL_NOTIFICATION_CHANNEL_BACKGROUNDING = "notification_channel_backgrounding" -GLOBAL_NOTIFICATION_MAX_CHANNEL_GROUPS = "notification_max_channel_groups" -GLOBAL_NOTIFICATION_MAX_CHANNELS = "notification_max_channels" -GLOBAL_NOTIFICATION_SNOOZE_MIN_TIME_SECONDS = "notification_snooze_min_time_seconds" -GLOBAL_NOTIFICATION_SNOOZE_MAX_TIME_SECONDS = "notification_snooze_max_time_seconds" -GLOBAL_NOTIFICATION_SNOOZE_OPTIONS = "notification_snooze_options" -GLOBAL_NOTIFICATION_TIMEOUT_IN_SECONDS = "notification_timeout_in_seconds" -GLOBAL_NR_NSA_NR_5G_ENABLED = "nr_nsa_nr_5g_enabled" -GLOBAL_NR_NSA_SUB6_5G_ENABLED = "nr_nsa_sub6_5g_enabled" -GLOBAL_NR_NSA_MMWAVE_5G_ENABLED = "nr_nsa_mmwave_5g_enabled" -GLOBAL_NR_NSA_SA_MODE = "nr_nsa_sa_mode" -GLOBAL_NR_SA_5G_ENABLED = "nr_sa_5g_enabled" -GLOBAL_NSD_ON = "nsd_on" -GLOBAL_NUM_SIM_EVENTS = "num_sim_events" -GLOBAL_OVERLAY_DISPLAY_DEVICES = "overlay_display_devices" -GLOBAL_PACKAGE_VERIFIER_DEFAULT_RESPONSE = "package_verifier_default_response" -GLOBAL_PACKAGE_VERIFIER_ENABLE = "package_verifier_enable" -GLOBAL_PACKAGE_VERIFIER_INCLUDE_ADB = "package_verifier_include_adb" -GLOBAL_PACKAGE_VERIFIER_SETTING = "package_verifier_setting" -GLOBAL_PACKAGE_VERIFIER_TIMEOUT = "package_verifier_timeout" -GLOBAL_PDP_WATCHDOG_ERROR_POLL_COUNT = "pdp_watchdog_error_poll_count" -GLOBAL_PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS = "pdp_watchdog_error_poll_interval_ms" -GLOBAL_PDP_WATCHDOG_LONG_POLL_INTERVAL_MS = "pdp_watchdog_long_poll_interval_ms" -GLOBAL_PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT = "pdp_watchdog_max_pdp_reset_fail_count" -GLOBAL_PDP_WATCHDOG_POLL_INTERVAL_MS = "pdp_watchdog_poll_interval_ms" -GLOBAL_PDP_WATCHDOG_TRIGGER_PACKET_COUNT = "pdp_watchdog_trigger_packet_count" -GLOBAL_POWER_MANAGER_CONSTANTS = "power_manager_constants" -GLOBAL_PREFERRED_NETWORK_MODE = "preferred_network_mode" -GLOBAL_PREPEND_APN = "prepend_apn" -GLOBAL_PREVIOUS_BOOT_COUNT = "previous_boot_count" -GLOBAL_PRIVATE_DNS_MODE = "private_dns_mode" -GLOBAL_PRIVATE_DNS_SPECIFIER = "private_dns_specifier" -GLOBAL_PROBLEM_DETECTED = "problem_detected" -GLOBAL_PROTECTED_NETWORKS = "protected_networks" -GLOBAL_RADIO_SCREEN_TIMEOUT = "radio_screen_timeout" -GLOBAL_RAM_SIZE = "ram_size" -GLOBAL_RECOVERY_UPDATE = "recovery_update" -GLOBAL_REMOTE_BUGREPORT_NOTIFICATION_ENABLED = "remote_bugreport_notification_enabled" -GLOBAL_REQUIRE_DEVICE_UNLOCK = "require_device_unlock" -GLOBAL_RESTART_BROADCAST_INTERVAL = "restart_broadcast_interval" -GLOBAL_ROAMING_SETTINGS = "roaming_settings" -GLOBAL_SAFE_BOOT_DISALLOWED = "safe_boot_disallowed" -GLOBAL_SAMPLING_PROFILER_MS = "sampling_profiler_ms" -GLOBAL_SCREEN_BRIGHTNESS = "screen_brightness" -GLOBAL_SCREEN_BRIGHTNESS_FLOAT = "screen_brightness_float" -GLOBAL_SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode" -GLOBAL_SCREEN_OFF_TIMEOUT = "screen_off_timeout" -GLOBAL_SCREEN_ON_POWER_BUTTON = "screen_on_power_button" -GLOBAL_SDCARD_BLACKLISTED_DEFAULT_SYNCABLE_TYPES = "sdcard_blacklisted_default_syncable_types" -GLOBAL_SELINUX_STATUS = "selinux_status" -GLOBAL_SELINUX_UPDATE = "selinux_update" -GLOBAL_SEND_ACTION_APP_ERROR = "send_action_app_error" -GLOBAL_SENSOR_PRIVACY = "sensor_privacy" -GLOBAL_SERVICE_AUTO_RESTART_DURATION = "service_auto_restart_duration" -GLOBAL_SERVICE_RESTART_DURATION = "service_restart_duration" -GLOBAL_SERVICE_RESTART_DURATION_LIMIT = "service_restart_duration_limit" -GLOBAL_SET_INSTALL_LOCATION = "set_install_location" -GLOBAL_SHARED_SYSTEM_USER_WHITELIST = "shared_system_user_whitelist" -GLOBAL_SHORTCUT_MANAGER_CONSTANTS = "shortcut_manager_constants" -GLOBAL_SHOW_HIDDEN_LAUNCHER_ICON_APPS = "show_hidden_launcher_icon_apps" -GLOBAL_SHOW_MEDIA_OUTPUT_SWITCHER = "show_media_output_switcher" -GLOBAL_SHOW_NOTIFICATION_CHANNEL_WARNINGS = "show_notification_channel_warnings" -GLOBAL_SHOW_PASSWORD = "show_password" -GLOBAL_SHOW_TEMPERATURE_WARNING = "show_temperature_warning" -GLOBAL_SHOW_ZEN_UPGRADE_NOTIFICATION = "show_zen_upgrade_notification" -GLOBAL_SIM_INSERTED_STATUS = "sim_inserted_status" -GLOBAL_SIM_OPERATOR_NUMERIC = "sim_operator_numeric" -GLOBAL_SKIP_RESTORE_AFTER_FACTORY_RESET = "skip_restore_after_factory_reset" -GLOBAL_SKIP_USER_SUGGESTION = "skip_user_suggestion" -GLOBAL_SLEEP = "sleep" -GLOBAL_SMART_USER_AUTO_LOCK = "smart_user_auto_lock" -GLOBAL_SMS_OUTGOING_CHECK_INTERVAL_MS = "sms_outgoing_check_interval_ms" -GLOBAL_SMS_OUTGOING_CHECK_MAX_COUNT = "sms_outgoing_check_max_count" -GLOBAL_SMS_SHORT_CODES = "sms_short_codes" -GLOBAL_SMS_SHORT_CODES_UPDATE_MODE = "sms_short_codes_update_mode" -GLOBAL_SS_SYSTEM_UID = "ss_system_uid" -GLOBAL_SS_USER_UID = "ss_user_uid" -GLOBAL_STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in" -GLOBAL_STORAGE_BENCHMARK_INTERVAL = "storage_benchmark_interval" -GLOBAL_STORAGE_SETTINGS_CLOBBER_THRESHOLD = "storage_settings_clobber_threshold" -GLOBAL_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED = "swipe_bottom_to_notification_enabled" -GLOBAL_SWITCH_HAS_BEEN_USED = "switch_has_been_used" -GLOBAL_SYNC_MANAGER_CONSTANTS = "sync_manager_constants" -GLOBAL_SYSTEM_MAX_ERRORS = "system_max_errors" -GLOBAL_SYSTEM_USER_HOME_URI = "system_user_home_uri" -GLOBAL_TCP_DEFAULT_INIT_RWND = "tcp_default_init_rwnd" -GLOBAL_TCP_OPTIMIZATION = "tcp_optimization" -GLOBAL_TCP_TIMEOUT = "tcp_timeout" -GLOBAL_TETHER_DUN_APN = "tether_dun_apn" -GLOBAL_TETHER_DUN_REQUIRED = "tether_dun_required" -GLOBAL_TETHER_OFFLOAD_DISABLED = "tether_offload_disabled" -GLOBAL_TETHER_SUPPORTED = "tether_supported" -GLOBAL_THEATER_MODE = "theater_mode" -GLOBAL_TIME_ONLINE = "time_online" -GLOBAL_TIMEOUT_TO_FORCE_SLEEP_MS = "timeout_to_force_sleep_ms" -GLOBAL_TITLE = "title" -GLOBAL_TPRONES = "tprones" -GLOBAL_TRANSITION_ANIMATION_SCALE = "transition_animation_scale" -GLOBAL_TRUSTED_SOUND = "trusted_sound" -GLOBAL_TTY_MODE = "tty_mode" -GLOBAL_TZINFO_UPDATE_CONTENT_URL = "tzinfo_update_content_url" -GLOBAL_TZINFO_UPDATE_METADATA_URL = "tzinfo_update_metadata_url" -GLOBAL_UART_ON_BOOT = "uart_on_boot" -GLOBAL_UBIFS = "ubifs" -GLOBAL_UID_CPUSET = "uid_cpuset" -GLOBAL_UNKNOWN_SOURCES_DEFAULT_REVERSED = "unknown_sources_default_reversed" -GLOBAL_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_DURATION = "unused_static_shared_lib_min_cache_duration" -GLOBAL_UPDATABLE_DRIVER_ALL_APPS = "updatable_driver_all_apps" -GLOBAL_UPDATABLE_DRIVER_PRODUCTION_OPT_IN = "updatable_driver_production_opt_in" -GLOBAL_UPDATABLE_DRIVER_PRODUCTION_DENYLIST = "updatable_driver_production_denylist" -GLOBAL_UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST = "updatable_driver_production_allowlist" -GLOBAL_UPDATABLE_DRIVER_PRERELEASE_OPT_IN = "updatable_driver_prerelease_opt_in" -GLOBAL_UPDATABLE_DRIVER_PROCESS_PACKAGES = "updatable_driver_process_packages" -GLOBAL_UPDATABLE_DRIVER_DENYLIST = "updatable_driver_denylist" -GLOBAL_USB_AUDIO_AUTOROUTING_DISABLED = "usb_audio_autorouting_disabled" -GLOBAL_USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled" -GLOBAL_USE_GOOGLE_MAIL = "use_google_mail" -GLOBAL_USER_SWITCHER = "user_switcher" -GLOBAL_VERIFY_APPS = "verify_apps" -GLOBAL_VOLUME_VIBRATE = "volume_vibrate" -GLOBAL_VOLUME_HUSH_GESTURE = "volume_hush_gesture" -GLOBAL_VR_DEVELOPER_MODE_ENABLED = "vr_developer_mode_enabled" -GLOBAL_VR_DISPLAY_MODE = "vr_display_mode" -GLOBAL_VR_LAUNCH_POLICY = "vr_launch_policy" -GLOBAL_WAIT_FOR_DEBUGGER = "wait_for_debugger" -GLOBAL_WALLPAPER_COMPONENT = "wallpaper_component" -GLOBAL_WIFI_BANDWIDTH_TEST_INTERVAL = "wifi_bandwidth_test_interval" -GLOBAL_WIFI_BANDWIDTH_TEST_TIMEOUT = "wifi_bandwidth_test_timeout" -GLOBAL_WIFI_BOUNCE_ACTIVITY_DELAY_MS = "wifi_bounce_activity_delay_ms" -GLOBAL_WIFI_COUNTRY_CODE = "wifi_country_code" -GLOBAL_WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN = "wifi_device_owner_configs_lockdown" -GLOBAL_WIFI_DISPLAY_CERTIFICATION_ON = "wifi_display_certification_on" -GLOBAL_WIFI_DISPLAY_ON = "wifi_display_on" -GLOBAL_WIFI_DISPLAY_WPS_CONFIG = "wifi_display_wps_config" -GLOBAL_WIFI_DRIVER_INTERFACE = "wifi_driver_interface" -GLOBAL_WIFI_DRIVER_OPERATION_MODE = "wifi_driver_operation_mode" -GLOBAL_WIFI_ENHANCED_AUTO_JOIN = "wifi_enhanced_auto_join" -GLOBAL_WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS = "wifi_ephemeral_out_of_range_timeout_ms" -GLOBAL_WIFI_FRAMEWORK_LOGGING_INTERVAL_SECONDS = "wifi_framework_logging_interval_seconds" -GLOBAL_WIFI_FRAMEWORK_SCAN_INTERVAL_MS = "wifi_framework_scan_interval_ms" -GLOBAL_WIFI_FRAMEWORK_SCAN_INTERVAL_MIN_MS = "wifi_framework_scan_interval_min_ms" -GLOBAL_WIFI_FRAMEWORK_SCAN_INTERVAL_MAX_MS = "wifi_framework_scan_interval_max_ms" -GLOBAL_WIFI_FRAMEWORK_ACTIVE_SCAN_INTERVAL_MS = "wifi_framework_active_scan_interval_ms" -GLOBAL_WIFI_FRAMEWORK_P2P_SCAN_INTERVAL_MS = "wifi_framework_p2p_scan_interval_ms" -GLOBAL_WIFI_FREQUENCY_BAND = "wifi_frequency_band" -GLOBAL_WIFI_HOTSPOT2_ENABLED = "wifi_hotspot2_enabled" -GLOBAL_WIFI_IDLE_MS = "wifi_idle_ms" -GLOBAL_WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count" -GLOBAL_WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = "wifi_mobile_data_transition_wakelock_timeout_ms" -GLOBAL_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on" -GLOBAL_WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay" -GLOBAL_WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept" -GLOBAL_WIFI_ON = "wifi_on" -GLOBAL_WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name" -GLOBAL_WIFI_SAVED_STATE = "wifi_saved_state" -GLOBAL_WIFI_SCAN_ALWAYS_AVAILABLE = "wifi_scan_always_available" -GLOBAL_WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS = "wifi_scan_interval_when_p2p_connected_ms" -GLOBAL_WIFI_SLEEP_POLICY = "wifi_sleep_policy" -GLOBAL_WIFI_SLEEP_POLICY_DEFAULT = 0 -GLOBAL_WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1 -GLOBAL_WIFI_SLEEP_POLICY_NEVER = 2 -GLOBAL_WIFI_SUPPLICANT_SCAN_INTERVAL_MS = "wifi_supplicant_scan_interval_ms" -GLOBAL_WIFI_SUSPEND_OPTIMIZATIONS_ENABLED = "wifi_suspend_optimizations_enabled" -GLOBAL_WIFI_VERBOSE_LOGGING_ENABLED = "wifi_verbose_logging_enabled" -GLOBAL_WIFI_WATCHDOG_ON = "wifi_watchdog_on" -GLOBAL_WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED = "wifi_watchdog_poor_network_test_enabled" -GLOBAL_WIMAX_ON = "wimax_on" -GLOBAL_WINDOW_ANIMATION_SCALE = "window_animation_scale" -GLOBAL_WINDOW_ORIENTATION_LISTENER_LOG = "window_orientation_listener_log" -GLOBAL_WIFI_ENHANCED_AUTO_JOIN = GLOBAL_WIFI_ENHANCED_AUTO_JOIN -GLOBAL_WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS = GLOBAL_WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS -GLOBAL_WIFI_FRAMEWORK_LOGGING_INTERVAL_SECONDS = GLOBAL_WIFI_FRAMEWORK_LOGGING_INTERVAL_SECONDS -GLOBAL_WIFI_FRAMEWORK_SCAN_INTERVAL_MS = GLOBAL_WIFI_FRAMEWORK_SCAN_INTERVAL_MS -GLOBAL_WIFI_FRAMEWORK_SCAN_INTERVAL_MIN_MS = GLOBAL_WIFI_FRAMEWORK_SCAN_INTERVAL_MIN_MS -GLOBAL_WIFI_FRAMEWORK_SCAN_INTERVAL_MAX_MS = GLOBAL_WIFI_FRAMEWORK_SCAN_INTERVAL_MAX_MS -GLOBAL_WIFI_FRAMEWORK_ACTIVE_SCAN_INTERVAL_MS = GLOBAL_WIFI_FRAMEWORK_ACTIVE_SCAN_INTERVAL_MS -GLOBAL_WIFI_FRAMEWORK_P2P_SCAN_INTERVAL_MS = GLOBAL_WIFI_FRAMEWORK_P2P_SCAN_INTERVAL_MS -GLOBAL_WIFI_FREQUENCY_BAND = GLOBAL_WIFI_FREQUENCY_BAND -GLOBAL_WIFI_HOTSPOT2_ENABLED = GLOBAL_WIFI_HOTSPOT2_ENABLED -GLOBAL_WIFI_IDLE_MS = GLOBAL_WIFI_IDLE_MS -GLOBAL_WIFI_MAX_DHCP_RETRY_COUNT = GLOBAL_WIFI_MAX_DHCP_RETRY_COUNT -GLOBAL_WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = GLOBAL_WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS -GLOBAL_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = GLOBAL_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON -GLOBAL_WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = GLOBAL_WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY -GLOBAL_WIFI_NUM_OPEN_NETWORKS_KEPT = GLOBAL_WIFI_NUM_OPEN_NETWORKS_KEPT -GLOBAL_WIFI_ON = GLOBAL_WIFI_ON -GLOBAL_WIFI_P2P_DEVICE_NAME = GLOBAL_WIFI_P2P_DEVICE_NAME -GLOBAL_WIFI_SAVED_STATE = GLOBAL_WIFI_SAVED_STATE -GLOBAL_WIFI_SCAN_ALWAYS_AVAILABLE = GLOBAL_WIFI_SCAN_ALWAYS_AVAILABLE -GLOBAL_WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS = GLOBAL_WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS -GLOBAL_WIFI_SLEEP_POLICY = GLOBAL_WIFI_SLEEP_POLICY -GLOBAL_WIFI_SLEEP_POLICY_DEFAULT = GLOBAL_WIFI_SLEEP_POLICY_DEFAULT -GLOBAL_WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = GLOBAL_WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED -GLOBAL_WIFI_SLEEP_POLICY_NEVER = GLOBAL_WIFI_SLEEP_POLICY_NEVER -GLOBAL_WIFI_SUPPLICANT_SCAN_INTERVAL_MS = GLOBAL_WIFI_SUPPLICANT_SCAN_INTERVAL_MS -GLOBAL_WIFI_SUSPEND_OPTIMIZATIONS_ENABLED = GLOBAL_WIFI_SUSPEND_OPTIMIZATIONS_ENABLED -GLOBAL_WIFI_VERBOSE_LOGGING_ENABLED = GLOBAL_WIFI_VERBOSE_LOGGING_ENABLED -GLOBAL_WIFI_WATCHDOG_ON = GLOBAL_WIFI_WATCHDOG_ON -GLOBAL_WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED = GLOBAL_WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED -GLOBAL_WIMAX_ON = GLOBAL_WIMAX_ON -GLOBAL_WINDOW_ANIMATION_SCALE = GLOBAL_WINDOW_ANIMATION_SCALE -GLOBAL_WINDOW_ORIENTATION_LISTENER_LOG = GLOBAL_WINDOW_ORIENTATION_LISTENER_LOG -GLOBAL_WIRELESS_CHARGING_STARTED_SOUND = "wireless_charging_started_sound" -GLOBAL_WOW_DISABLE = "wow_disable" -GLOBAL_WTF_IS_THIS = "wtf_is_this" -GLOBAL_ZEN_MODE = "zen_mode" -GLOBAL_ZEN_MODE_CONFIG_ETAG = "zen_mode_config_etag" -GLOBAL_ZEN_MODE_RINGER_LEVEL = "zen_mode_ringer_level" -GLOBAL_ZEN_MODE_DURATION = "zen_mode_duration" - - -class SettingsGlobal(_SettingsBase): - """Settings.Global — 全局设置表""" - - _NAME = "settings_global" - - # Airplane mode - AIRPLANE_MODE_ON = GLOBAL_AIRPLANE_MODE_ON - AIRPLANE_MODE_RADIOS = GLOBAL_AIRPLANE_MODE_RADIOS - AIRPLANE_MODE_TOGGLEABLE_RADIOS = GLOBAL_AIRPLANE_MODE_TOGGLEABLE_RADIOS - - # Wi-Fi - WIFI_ON = GLOBAL_WIFI_ON - WIFI_SLEEP_POLICY = GLOBAL_WIFI_SLEEP_POLICY - WIFI_SLEEP_POLICY_DEFAULT = GLOBAL_WIFI_SLEEP_POLICY_DEFAULT - WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = GLOBAL_WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED - WIFI_SLEEP_POLICY_NEVER = GLOBAL_WIFI_SLEEP_POLICY_NEVER - WIFI_MAX_DHCP_RETRY_COUNT = GLOBAL_WIFI_MAX_DHCP_RETRY_COUNT - WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = GLOBAL_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON - WIFI_NUM_OPEN_NETWORKS_KEPT = GLOBAL_WIFI_NUM_OPEN_NETWORKS_KEPT - WIFI_SCAN_ALWAYS_AVAILABLE = GLOBAL_WIFI_SCAN_ALWAYS_AVAILABLE - WIFI_COUNTRY_CODE = GLOBAL_WIFI_COUNTRY_CODE - WIFI_FREQUENCY_BAND = GLOBAL_WIFI_FREQUENCY_BAND - WIFI_ENHANCED_AUTO_JOIN = GLOBAL_WIFI_ENHANCED_AUTO_JOIN - WIFI_HOTSPOT2_ENABLED = GLOBAL_WIFI_HOTSPOT2_ENABLED - WIFI_VERBOSE_LOGGING_ENABLED = GLOBAL_WIFI_VERBOSE_LOGGING_ENABLED - WIFI_SUSPEND_OPTIMIZATIONS_ENABLED = GLOBAL_WIFI_SUSPEND_OPTIMIZATIONS_ENABLED - WIFI_WATCHDOG_ON = GLOBAL_WIFI_WATCHDOG_ON - WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS = GLOBAL_WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS - - # Bluetooth - BLUETOOTH_ON = GLOBAL_BLUETOOTH_ON - BLUETOOTH_CLASS_OF_DEVICE = GLOBAL_BLUETOOTH_CLASS_OF_DEVICE - BLUETOOTH_DISABLED_PROFILES = GLOBAL_BLUETOOTH_DISABLED_PROFILES - BLUETOOTH_INTEROPERABILITY_LIST = GLOBAL_BLUETOOTH_INTEROPERABILITY_LIST - - # Cellular / Mobile - MOBILE_DATA = GLOBAL_MOBILE_DATA - MOBILE_DATA_ALWAYS_ON = GLOBAL_MOBILE_DATA_ALWAYS_ON - DATA_ROAMING = GLOBAL_DATA_ROAMING - CELL_ON = GLOBAL_CELL_ON - PREFERRED_NETWORK_MODE = GLOBAL_PREFERRED_NETWORK_MODE - CDMA_ROAMING_MODE = GLOBAL_CDMA_ROAMING_MODE - CDMA_SUBSCRIPTION = GLOBAL_CDMA_SUBSCRIPTION - LTE_SERVICE_FORCED = "lte_service_forced" - NR_NSA_NR_5G_ENABLED = GLOBAL_NR_NSA_NR_5G_ENABLED - NR_NSA_SUB6_5G_ENABLED = GLOBAL_NR_NSA_SUB6_5G_ENABLED - NR_NSA_MMWAVE_5G_ENABLED = GLOBAL_NR_NSA_MMWAVE_5G_ENABLED - - # Hotspot - HOTSPOT_ENABLED = GLOBAL_HOTSPOT_ENABLED - HOTSPOT_SSID = GLOBAL_HOTSPOT_SSID - HOTSPOT_SECURITY_TYPE = GLOBAL_HOTSPOT_SECURITY_TYPE - HOTSPOT_APN = GLOBAL_HOTSPOT_APN - TETHER_SUPPORTED = GLOBAL_TETHER_SUPPORTED - TETHER_DUN_REQUIRED = GLOBAL_TETHER_DUN_REQUIRED - TETHER_DUN_APN = GLOBAL_TETHER_DUN_APN - - # Development - DEVELOPMENT_SETTINGS_ENABLED = GLOBAL_DEVELOPMENT_SETTINGS_ENABLED - DEVELOPMENT_ENABLE_FREEFORM_WINDOW_SUPPORT = GLOBAL_DEVELOPMENT_ENABLE_FREEFORM_WINDOW_SUPPORT - DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES = GLOBAL_DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES - DEVELOPMENT_FORCE_RTL = GLOBAL_DEVELOPMENT_FORCE_RTL - DEBUG_APP = GLOBAL_DEBUG_APP - DEBUG_VIEW_ATTRIBUTES = GLOBAL_DEBUG_VIEW_ATTRIBUTES - WAIT_FOR_DEBUGGER = GLOBAL_WAIT_FOR_DEBUGGER - ALWAYS_FINISH_ACTIVITIES = GLOBAL_ALWAYS_FINISH_ACTIVITIES - WINDOW_ANIMATION_SCALE = GLOBAL_WINDOW_ANIMATION_SCALE - TRANSITION_ANIMATION_SCALE = GLOBAL_TRANSITION_ANIMATION_SCALE - ANIMATOR_DURATION_SCALE = GLOBAL_ANIMATOR_DURATION_SCALE - SHOW_PASSWORD = GLOBAL_SHOW_PASSWORD - BUGREPORT_IN_POWER_MENU = GLOBAL_BUGREPORT_IN_POWER_MENU - STAY_ON_WHILE_PLUGGED_IN = GLOBAL_STAY_ON_WHILE_PLUGGED_IN - USB_MASS_STORAGE_ENABLED = GLOBAL_USB_MASS_STORAGE_ENABLED - ACCESSIBILITY_ENABLED = "accessibility_enabled" - SENSOR_PRIVACY = GLOBAL_SENSOR_PRIVACY - - # Display - SCREEN_BRIGHTNESS = GLOBAL_SCREEN_BRIGHTNESS - SCREEN_BRIGHTNESS_FLOAT = GLOBAL_SCREEN_BRIGHTNESS_FLOAT - SCREEN_BRIGHTNESS_MODE = GLOBAL_SCREEN_BRIGHTNESS_MODE - SCREEN_OFF_TIMEOUT = GLOBAL_SCREEN_OFF_TIMEOUT - THEATER_MODE = GLOBAL_THEATER_MODE - LOW_POWER_MODE = GLOBAL_LOW_POWER_MODE - LOW_POWER_MODE_STICKY = GLOBAL_LOW_POWER_MODE_STICKY - LOW_POWER_MODE_TRIGGER_LEVEL = GLOBAL_LOW_POWER_MODE_TRIGGER_LEVEL - DOZE_ENABLED = "doze_enabled" - DEVICE_DEMO_MODE = GLOBAL_DEVICE_DEMO_MODE - DEVICE_NAME = GLOBAL_DEVICE_NAME - ALWAYS_ON_DISPLAY_BRIGHTNESS = GLOBAL_ALWAYS_ON_DISPLAY_BRIGHTNESS - ALWAYS_ON_DISPLAY_CONSTANT_BRIGHTNESS = GLOBAL_ALWAYS_ON_DISPLAY_CONSTANT_BRIGHTNESS - - # Network - NETWORK_PREFERENCE = GLOBAL_NETWORK_PREFERENCE - NETWORK_SCORER_APP = GLOBAL_NETWORK_SCORER_APP - NETWORK_AVOID_BAD_WIFI = GLOBAL_NETWORK_AVOID_BAD_WIFI - NETWORK_METERED_MULTIPATH_PREFERENCE = GLOBAL_NETWORK_METERED_MULTIPATH_PREFERENCE - CAPTIVE_PORTAL_DETECTION_ENABLED = GLOBAL_CAPTIVE_PORTAL_DETECTION_ENABLED - CAPTIVE_PORTAL_SERVER = GLOBAL_CAPTIVE_PORTAL_SERVER - PRIVATE_DNS_MODE = GLOBAL_PRIVATE_DNS_MODE - PRIVATE_DNS_SPECIFIER = GLOBAL_PRIVATE_DNS_SPECIFIER - GLOBAL_HTTP_PROXY_HOST = GLOBAL_GLOBAL_HTTP_PROXY_HOST - GLOBAL_HTTP_PROXY_PORT = GLOBAL_GLOBAL_HTTP_PROXY_PORT - GLOBAL_HTTP_PROXY_PAC = GLOBAL_GLOBAL_HTTP_PROXY_PAC - GLOBAL_HTTP_PROXY_EXCLUSION_LIST = GLOBAL_GLOBAL_HTTP_PROXY_EXCLUSION_LIST - NFC_ON = GLOBAL_NFC_ON - NSD_ON = GLOBAL_NSD_ON - - # Device - DEVICE_PROVISIONED = GLOBAL_DEVICE_PROVISIONED - BOOT_COUNT = GLOBAL_BOOT_COUNT - DEVICE_PROVISIONING_MOBILE_DATA_ENABLED = GLOBAL_DEVICE_PROVISIONING_MOBILE_DATA_ENABLED - RAM_SIZE = GLOBAL_RAM_SIZE - MACHINE_UID = GLOBAL_MACHINE_UID - SELINUX_STATUS = GLOBAL_SELINUX_STATUS - - # Time - AUTO_TIME = GLOBAL_AUTO_TIME - AUTO_TIME_ZONE = GLOBAL_AUTO_TIME_ZONE - NITZ_UPDATE_DIFF = GLOBAL_NITZ_UPDATE_DIFF - NITZ_UPDATE_SPACING = GLOBAL_NITZ_UPDATE_SPACING - TIME_ONLINE = GLOBAL_TIME_ONLINE - - # Do not disturb / Zen - ZEN_MODE = GLOBAL_ZEN_MODE - ZEN_MODE_CONFIG_ETAG = GLOBAL_ZEN_MODE_CONFIG_ETAG - ZEN_MODE_RINGER_LEVEL = GLOBAL_ZEN_MODE_RINGER_LEVEL - ZEN_MODE_DURATION = GLOBAL_ZEN_MODE_DURATION - - # Audio - MODE_RINGER = GLOBAL_MODE_RINGER - ENCODED_SURROUND_OUTPUT = GLOBAL_ENCODED_SURROUND_OUTPUT - ENCODED_SURROUND_OUTPUT_ENABLED = GLOBAL_ENCODED_SURROUND_OUTPUT_ENABLED - DOCK_AUDIO_MEDIA_ENABLED = GLOBAL_DOCK_AUDIO_MEDIA_ENABLED - VOLUME_HUSH_GESTURE = GLOBAL_VOLUME_HUSH_GESTURE - - # Storage - DEFAULT_INSTALL_LOCATION = GLOBAL_DEFAULT_INSTALL_LOCATION - SET_INSTALL_LOCATION = GLOBAL_SET_INSTALL_LOCATION - STORAGE_BENCHMARK_INTERVAL = GLOBAL_STORAGE_BENCHMARK_INTERVAL - - # Package verifier - PACKAGE_VERIFIER_ENABLE = GLOBAL_PACKAGE_VERIFIER_ENABLE - PACKAGE_VERIFIER_INCLUDE_ADB = GLOBAL_PACKAGE_VERIFIER_INCLUDE_ADB - PACKAGE_VERIFIER_TIMEOUT = GLOBAL_PACKAGE_VERIFIER_TIMEOUT - PACKAGE_VERIFIER_DEFAULT_RESPONSE = GLOBAL_PACKAGE_VERIFIER_DEFAULT_RESPONSE - - # User - GUEST_USER_ENABLED = GLOBAL_GUEST_USER_ENABLED - USER_SWITCHER = GLOBAL_USER_SWITCHER - ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED = GLOBAL_ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED - KEEP_PROFILE_IN_BACKGROUND = GLOBAL_KEEP_PROFILE_IN_BACKGROUND - SKIP_USER_SUGGESTION = GLOBAL_SKIP_USER_SUGGESTION - - # Notifications - HEADS_UP_NOTIFICATIONS_ENABLED = GLOBAL_HEADS_UP_NOTIFICATIONS_ENABLED - MAX_NOTIFICATION_ENQUEUE_RATE = GLOBAL_MAX_NOTIFICATION_ENQUEUE_RATE - NOTIFICATION_SNOOZE_MIN_TIME_SECONDS = GLOBAL_NOTIFICATION_SNOOZE_MIN_TIME_SECONDS - NOTIFICATION_SNOOZE_MAX_TIME_SECONDS = GLOBAL_NOTIFICATION_SNOOZE_MAX_TIME_SECONDS - NOTIFICATION_TIMEOUT_IN_SECONDS = GLOBAL_NOTIFICATION_TIMEOUT_IN_SECONDS - SHOW_NOTIFICATION_CHANNEL_WARNINGS = GLOBAL_SHOW_NOTIFICATION_CHANNEL_WARNINGS - SHOW_MEDIA_OUTPUT_SWITCHER = GLOBAL_SHOW_MEDIA_OUTPUT_SWITCHER - NOTIFICATION_BACKGROUNDING = GLOBAL_NOTIFICATION_BACKGROUNDING - - # Wired/Wireless charging - WIRELESS_CHARGING_STARTED_SOUND = GLOBAL_WIRELESS_CHARGING_STARTED_SOUND - LOW_BATTERY_SOUND = GLOBAL_LOW_BATTERY_SOUND - LOW_BATTERY_SOUND_TIMEOUT = GLOBAL_LOW_BATTERY_SOUND_TIMEOUT - - # VR - VR_DEVELOPER_MODE_ENABLED = GLOBAL_VR_DEVELOPER_MODE_ENABLED - VR_DISPLAY_MODE = GLOBAL_VR_DISPLAY_MODE - VR_LAUNCH_POLICY = GLOBAL_VR_LAUNCH_POLICY - - @classmethod - def getString(cls, key: str, default_value: Optional[str] = None) -> Optional[str]: - return super().getString(key, default_value) - - @classmethod - def putString(cls, key: str, value: str) -> bool: - return super().putString(key, value) - - @classmethod - def getInt(cls, key: str, default_value: int = 0) -> int: - return super().getInt(key, default_value) - - @classmethod - def putInt(cls, key: str, value: int) -> bool: - return super().putInt(key, value) - - @classmethod - def getFloat(cls, key: str, default_value: float = 0.0) -> float: - return super().getFloat(key, default_value) - - @classmethod - def putFloat(cls, key: str, value: float) -> bool: - return super().putFloat(key, value) - - @classmethod - def getLong(cls, key: str, default_value: int = 0) -> int: - return super().getLong(key, default_value) - - @classmethod - def putLong(cls, key: str, value: int) -> bool: - return super().putLong(key, value) - - -# ═══════════════════════════════════════════════════════════════ -# Settings.NameValueTable — 名称值表接口 -# ═══════════════════════════════════════════════════════════════ - -class NameValueTable(Stub): - """Settings.NameValueTable — 名称-值表接口""" - - NAME = "name" - VALUE = "value" - - @staticmethod - def getUri(uri: Any) -> Any: - """Get the content URI for the name-value table.""" - return uri - - @staticmethod - def getString(resolver: Any, uri: Any, name: str) -> Optional[str]: - """Get a string from the name-value table.""" - return None - - @staticmethod - def putString(resolver: Any, uri: Any, name: str, value: str) -> bool: - """Put a string into the name-value table.""" - return False - - @staticmethod - def getInt(resolver: Any, uri: Any, name: str, default_value: int = 0) -> int: - """Get an integer from the name-value table.""" - return default_value - - @staticmethod - def putInt(resolver: Any, uri: Any, name: str, value: int) -> bool: - """Put an integer into the name-value table.""" - return False - - @staticmethod - def getFloat(resolver: Any, uri: Any, name: str, default_value: float = 0.0) -> float: - """Get a float from the name-value table.""" - return default_value - - @staticmethod - def putFloat(resolver: Any, uri: Any, name: str, value: float) -> bool: - """Put a float into the name-value table.""" - return False - - @staticmethod - def getLong(resolver: Any, uri: Any, name: str, default_value: int = 0) -> int: - """Get a long from the name-value table.""" - return default_value - - @staticmethod - def putLong(resolver: Any, uri: Any, name: str, value: int) -> bool: - """Put a long into the name-value table.""" - return False - - -# ═══════════════════════════════════════════════════════════════ -# Settings 根类 -# ═══════════════════════════════════════════════════════════════ - -class Settings(Stub): - """Settings — 系统设置(扩展版)""" - - System = SettingsSystem - Secure = SettingsSecure - Global = SettingsGlobal - NameValueTable = NameValueTable - - # Action constants - ACTION_SETTINGS = "android.settings.SETTINGS" - ACTION_APN_SETTINGS = "android.settings.APN_SETTINGS" - ACTION_APPLICATION_DETAILS_SETTINGS = "android.settings.APPLICATION_DETAILS_SETTINGS" - ACTION_APPLICATION_DEVELOPMENT_SETTINGS = "android.settings.APPLICATION_DEVELOPMENT_SETTINGS" - ACTION_APPLICATION_SETTINGS = "android.settings.APPLICATION_SETTINGS" - ACTION_BATTERY_SAVER_SETTINGS = "android.settings.BATTERY_SAVER_SETTINGS" - ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS" - ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS" - ACTION_CAST_SETTINGS = "android.settings.CAST_SETTINGS" - ACTION_CONDITION_PROVIDER_SETTINGS = "android.settings.CONDITION_PROVIDER_SETTINGS" - ACTION_DATA_ROAMING_SETTINGS = "android.settings.DATA_ROAMING_SETTINGS" - ACTION_DATE_SETTINGS = "android.settings.DATE_SETTINGS" - ACTION_DEVICE_INFO_SETTINGS = "android.settings.DEVICE_INFO_SETTINGS" - ACTION_DISPLAY_SETTINGS = "android.settings.DISPLAY_SETTINGS" - ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS" - ACTION_HARD_KEYBOARD_SETTINGS = "android.settings.HARD_KEYBOARD_SETTINGS" - ACTION_HOME_SETTINGS = "android.settings.HOME_SETTINGS" - ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS = "android.settings.IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS" - ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS = "android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS" - ACTION_INPUT_METHOD_SETTINGS = "android.settings.INPUT_METHOD_SETTINGS" - ACTION_INPUT_METHOD_SUBTYPE_SETTINGS = "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS" - ACTION_INTERNAL_STORAGE_SETTINGS = "android.settings.INTERNAL_STORAGE_SETTINGS" - ACTION_LOCALE_SETTINGS = "android.settings.LOCALE_SETTINGS" - ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS" - ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS" - ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS" - ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS" - ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.MANAGE_OVERLAY_PERMISSION" - ACTION_MANAGE_UNKNOWN_APP_SOURCES = "android.settings.MANAGE_UNKNOWN_APP_SOURCES" - ACTION_MANAGE_WRITE_SETTINGS = "android.settings.MANAGE_WRITE_SETTINGS" - ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS" - ACTION_NETWORK_OPERATOR_SETTINGS = "android.settings.NETWORK_OPERATOR_SETTINGS" - ACTION_NFCSHARING_SETTINGS = "android.settings.NFCSHARING_SETTINGS" - ACTION_NFC_SETTINGS = "android.settings.NFC_SETTINGS" - ACTION_NIGHT_DISPLAY_SETTINGS = "android.settings.NIGHT_DISPLAY_SETTINGS" - ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.NOTIFICATION_LISTENER_SETTINGS" - ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS" - ACTION_PRINT_SETTINGS = "android.settings.PRINT_SETTINGS" - ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS" - ACTION_QUICK_ACCESS_WALLET_SETTINGS = "android.settings.QUICK_ACCESS_WALLET_SETTINGS" - ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS" - ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" - ACTION_REQUEST_MANAGE_MEDIA = "android.settings.REQUEST_MANAGE_MEDIA" - ACTION_REQUEST_SET_AUTOFILL_SERVICE = "android.settings.REQUEST_SET_AUTOFILL_SERVICE" - ACTION_REQUEST_SCHEDULE_EXACT_ALARM = "android.settings.REQUEST_SCHEDULE_EXACT_ALARM" - ACTION_SEARCH_SETTINGS = "android.settings.SEARCH_SETTINGS" - ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS" - ACTION_SETTINGS = ACTION_SETTINGS - ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS" - ACTION_SHOW_REGULATORY_INFO = "android.settings.SHOW_REGULATORY_INFO" - ACTION_SOUND_SETTINGS = "android.settings.SOUND_SETTINGS" - ACTION_STORAGE_MANAGER_SETTINGS = "android.settings.STORAGE_MANAGER_SETTINGS" - ACTION_SYNC_SETTINGS = "android.settings.SYNC_SETTINGS" - ACTION_SYSTEM_UPDATE_SETTINGS = "android.settings.SYSTEM_UPDATE_SETTINGS" - ACTION_TETHER_PROVISIONING = "android.settings.TETHER_PROVISIONING" - ACTION_TRUSTED_CREDENTIALS_USER = "android.settings.TRUSTED_CREDENTIALS_USER" - ACTION_USAGE_ACCESS_SETTINGS = "android.settings.USAGE_ACCESS_SETTINGS" - ACTION_USER_DICTIONARY_SETTINGS = "android.settings.USER_DICTIONARY_SETTINGS" - ACTION_VIDEO_ACCESSIBILITY_SETTINGS = "android.settings.VIDEO_ACCESSIBILITY_SETTINGS" - ACTION_VOICE_CONTROL_AIRPLANE_MODE = "android.settings.VOICE_CONTROL_AIRPLANE_MODE" - ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE" - ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE" - ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS" - ACTION_VPN_SETTINGS = "android.settings.VPN_SETTINGS" - ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS" - ACTION_WEBVIEW_SETTINGS = "android.settings.WEBVIEW_SETTINGS" - ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS" - ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS" - ACTION_WIRELESS_SETTINGS = "android.settings.WIRELESS_SETTINGS" - ACTION_ZWITE_MIGRATE_SETTINGS = "android.settings.ZWITE_MIGRATE_SETTINGS" - - # Extra constants - EXTRA_ACCOUNT_TYPES = "account_types" - EXTRA_AUTHORITIES = "authorities" - EXTRA_BATTERY_SAVER_MODE_ENABLED = "extra_battery_saver_mode_enabled" - EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "extra_do_not_disturb_mode_enabled" - EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "extra_do_not_disturb_mode_minutes" - EXTRA_INPUT_METHOD_ID = "input_method_id" - EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGH_LIGHT = "extra_settings_embedded_deep_link_high_light" - EXTRA_SETTINGS_EMBEDDED_DEEP_LINK = "extra_settings_embedded_deep_link" - - # Intent constant for Settings - EXTRA_APP_PACKAGE = "android.intent.extra.PACKAGE_NAME" - - @staticmethod - def canDrawOverlays(context: Any) -> bool: - """Check if the app can draw overlays.""" - return True - - @staticmethod - def canWriteSettings(context: Any) -> bool: - """Check if the app can write system settings.""" - return True - - @staticmethod - def checkAndWriteSettings(context: Any, settings_key: str, value: Any) -> bool: - """Check and write a settings value if permitted.""" - return False - - @staticmethod - def getDefaultSettingsPackageName(context: Any) -> str: - """Get the default settings package name.""" - return "com.android.settings" - - -# ═══════════════════════════════════════════════════════════════ -# Settings Extras — 额外工具类 -# ═══════════════════════════════════════════════════════════════ - -class SettingsExtras: - """SettingsExtras — 设置辅助工具""" - - @staticmethod - def isAirplaneModeOn(context: Any) -> bool: - """Check if airplane mode is on.""" - return SettingsSystem.getInt(SettingsSystem.AIRPLANE_MODE_ON, 0) == 1 - - @staticmethod - def setAirplaneMode(context: Any, on: bool) -> bool: - """Set airplane mode on or off.""" - return SettingsSystem.putInt(SettingsSystem.AIRPLANE_MODE_ON, 1 if on else 0) - - @staticmethod - def isWifiOn(context: Any) -> bool: - """Check if Wi-Fi is on.""" - return SettingsSystem.getInt(SettingsSystem.WIFI_ON, 0) == 1 - - @staticmethod - def isBluetoothOn(context: Any) -> bool: - """Check if Bluetooth is on.""" - return SettingsSystem.getInt(SettingsSystem.BLUETOOTH_ON, 0) == 1 - - @staticmethod - def isAdbEnabled(context: Any) -> bool: - """Check if ADB is enabled.""" - return SettingsSecure.getInt(SettingsSecure.ADB_ENABLED, 0) == 1 - - @staticmethod - def isInstallNonMarketAppsAllowed(context: Any) -> bool: - """Check if installing apps from unknown sources is allowed.""" - return SettingsSecure.getInt(SettingsSecure.INSTALL_NON_MARKET_APPS, 0) == 1 - - @staticmethod - def getScreenBrightness(context: Any, default: int = 255) -> int: - """Get screen brightness value (0-255).""" - return SettingsSystem.getInt(SettingsSystem.SCREEN_BRIGHTNESS, default) - - @staticmethod - def setScreenBrightness(context: Any, value: int) -> bool: - """Set screen brightness (0-255).""" - return SettingsSystem.putInt(SettingsSystem.SCREEN_BRIGHTNESS, max(0, min(255, value))) - - @staticmethod - def getScreenOffTimeout(context: Any, default: int = 30000) -> int: - """Get screen off timeout in milliseconds.""" - return SettingsSystem.getInt(SettingsSystem.SCREEN_OFF_TIMEOUT, default) - - @staticmethod - def setScreenOffTimeout(context: Any, timeout_ms: int) -> bool: - """Set screen off timeout in milliseconds.""" - return SettingsSystem.putInt(SettingsSystem.SCREEN_OFF_TIMEOUT, timeout_ms) - - @staticmethod - def isDevelopmentSettingsEnabled(context: Any) -> bool: - """Check if development settings are enabled.""" - return SettingsSecure.getInt(SettingsSecure.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1 - - @staticmethod - def isMockLocationAllowed(context: Any) -> bool: - """Check if mock location is allowed.""" - return SettingsSecure.getInt(SettingsSecure.ALLOW_MOCK_LOCATION, 0) == 1 - - @staticmethod - def getDeviceName(context: Any, default: str = "") -> str: - """Get the device name.""" - value = SettingsGlobal.getString(SettingsGlobal.DEVICE_NAME) - return value if value is not None else default - - @staticmethod - def setDeviceName(context: Any, name: str) -> bool: - """Set the device name.""" - return SettingsGlobal.putString(SettingsGlobal.DEVICE_NAME, name) - - @staticmethod - def getZenMode(context: Any, default: int = 0) -> int: - """Get the current Zen/Do Not Disturb mode.""" - return SettingsGlobal.getInt(SettingsGlobal.ZEN_MODE, default) - - @staticmethod - def isDataRoamingEnabled(context: Any) -> bool: - """Check if data roaming is enabled.""" - return SettingsGlobal.getInt(SettingsGlobal.DATA_ROAMING, 0) == 1 - - @staticmethod - def isMobileDataEnabled(context: Any) -> bool: - """Check if mobile data is enabled.""" - return SettingsGlobal.getInt(SettingsGlobal.MOBILE_DATA, 1) == 1 - - @staticmethod - def getScreenBrightnessMode(context: Any) -> int: - """Get screen brightness mode (manual=0, auto=1).""" - return SettingsSystem.getInt(SettingsSystem.SCREEN_BRIGHTNESS_MODE, 0) - - @staticmethod - def setScreenBrightnessMode(context: Any, mode: int) -> bool: - """Set screen brightness mode (manual=0, auto=1).""" - return SettingsSystem.putInt(SettingsSystem.SCREEN_BRIGHTNESS_MODE, mode) - - @staticmethod - def getHttpProxy(context: Any) -> Optional[str]: - """Get the HTTP proxy setting.""" - return SettingsSystem.getString(SettingsSystem.HTTP_PROXY) - - @staticmethod - def setHttpProxy(context: Any, proxy: str) -> bool: - """Set the HTTP proxy.""" - return SettingsSystem.putString(SettingsSystem.HTTP_PROXY, proxy) - - @staticmethod - def getGlobalHttpProxy(context: Any) -> Dict[str, Any]: - """Get the global HTTP proxy settings.""" - return { - "host": SettingsGlobal.getString(SettingsGlobal.GLOBAL_HTTP_PROXY_HOST), - "port": SettingsGlobal.getInt(SettingsGlobal.GLOBAL_HTTP_PROXY_PORT, 0), - "pac": SettingsGlobal.getString(SettingsGlobal.GLOBAL_HTTP_PROXY_PAC), - "exclusion_list": SettingsGlobal.getString(SettingsGlobal.GLOBAL_HTTP_PROXY_EXCLUSION_LIST), - } - - @staticmethod - def isDeviceProvisioned(context: Any) -> bool: - """Check if the device has been provisioned.""" - return SettingsGlobal.getInt(SettingsGlobal.DEVICE_PROVISIONED, 0) == 1 - - @staticmethod - def getBootCount(context: Any) -> int: - """Get the number of times the device has been booted.""" - return SettingsGlobal.getInt(SettingsGlobal.BOOT_COUNT, 0) - - -# ═══════════════════════════════════════════════════════════════ -# SettingsProvider 辅助类 -# ═══════════════════════════════════════════════════════════════ - -class SettingsProvider(Stub): - """SettingsProvider — 设置提供者辅助类""" - - @staticmethod - def getStringForUser(resolver: Any, name: str, user_handle: Any, - default_value: Optional[str] = None) -> Optional[str]: - """Get a string setting for a specific user.""" - return default_value - - @staticmethod - def putStringForUser(resolver: Any, name: str, value: str, - user_handle: Any) -> bool: - """Put a string setting for a specific user.""" - return True - - @staticmethod - def getIntForUser(resolver: Any, name: str, user_handle: Any, - default_value: int = 0) -> int: - """Get an integer setting for a specific user.""" - return default_value - - @staticmethod - def putIntForUser(resolver: Any, name: str, value: int, - user_handle: Any) -> bool: - """Put an integer setting for a specific user.""" - return True - - @staticmethod - def getUriFor(name: str) -> str: - """Get the content URI for a named setting.""" - return f"content://settings/{name}" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - # Base - "_SettingsBase", - # System - "SettingsSystem", - "SYSTEM_AIRPLANE_MODE_ON", "SYSTEM_WIFI_ON", "SYSTEM_BLUETOOTH_ON", - "SYSTEM_SCREEN_BRIGHTNESS", "SYSTEM_SCREEN_OFF_TIMEOUT", - "SYSTEM_VOLUME_ALARM", "SYSTEM_VOLUME_MUSIC", "SYSTEM_VOLUME_RING", - "SYSTEM_VOLUME_NOTIFICATION", "SYSTEM_VOLUME_SYSTEM", - "SYSTEM_FONT_SCALE", "SYSTEM_HAPTIC_FEEDBACK_ENABLED", - "SYSTEM_SOUND_EFFECTS_ENABLED", "SYSTEM_DTMF_TONE_WHEN_DIALING", - "SYSTEM_ACCELEROMETER_ROTATION", "SYSTEM_USER_ROTATION", - "SYSTEM_WINDOW_ANIMATION_SCALE", "SYSTEM_TRANSITION_ANIMATION_SCALE", - "SYSTEM_ANIMATOR_DURATION_SCALE", - # Secure - "SettingsSecure", - "SECURE_ADB_ENABLED", "SECURE_ALLOW_MOCK_LOCATION", - "SECURE_INSTALL_NON_MARKET_APPS", "SECURE_ANDROID_ID", - "SECURE_DEVICE_PROVISIONED", "SECURE_LOCATION_MODE", - "SECURE_LOCK_PATTERN_ENABLED", "SECURE_USB_MASS_STORAGE_ENABLED", - # Global - "SettingsGlobal", - "GLOBAL_AIRPLANE_MODE_ON", "GLOBAL_WIFI_ON", "GLOBAL_BLUETOOTH_ON", - "GLOBAL_MOBILE_DATA", "GLOBAL_DATA_ROAMING", "GLOBAL_BOOT_COUNT", - "GLOBAL_DEVICE_PROVISIONED", "GLOBAL_DEVICE_NAME", - "GLOBAL_WINDOW_ANIMATION_SCALE", "GLOBAL_TRANSITION_ANIMATION_SCALE", - "GLOBAL_ANIMATOR_DURATION_SCALE", "GLOBAL_WIFI_SCAN_ALWAYS_AVAILABLE", - "GLOBAL_ZEN_MODE", "GLOBAL_STAY_ON_WHILE_PLUGGED_IN", - "GLOBAL_LOW_POWER_MODE", "GLOBAL_AUTO_TIME", "GLOBAL_AUTO_TIME_ZONE", - # Main - "Settings", "NameValueTable", - # Helpers - "SettingsExtras", "SettingsProvider", -] \ No newline at end of file diff --git a/android_pyapi/android/renderscript.py b/android_pyapi/android/renderscript.py deleted file mode 100644 index c86457b..0000000 --- a/android_pyapi/android/renderscript.py +++ /dev/null @@ -1,903 +0,0 @@ -from __future__ import annotations - -""" -Android RenderScript 组件 — RenderScript, Allocation, Element, Type, Script 等 -方法签名完全对齐 Android SDK (android.renderscript 包) -""" - -import math -from typing import Any, Callable, Dict, List, Optional, Tuple, Union - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# RenderScript -# ═══════════════════════════════════════════════════════════════ - -class RenderScript(Stub): - """RenderScript — RenderScript 运行时上下文 (桌面仅存根)""" - - # 优先级 - Priority_LOW = 0 - Priority_NORMAL = 1 - Priority_HIGH = 2 - - # 上下文类型 - ContextType_NORMAL = 0 - ContextType_DEBUG = 1 - ContextType_PROFILE = 2 - - def __init__(self): - super().__init__() - self._context_type = RenderScript.ContextType_NORMAL - self._error_handler: Optional[Callable] = None - self._message_handler: Optional[Callable] = None - self._priority = RenderScript.Priority_NORMAL - self._initialized = False - - @staticmethod - def create(context: Any = None) -> "RenderScript": - """创建 RenderScript 上下文""" - rs = RenderScript() - rs._initialized = True - return rs - - @staticmethod - def createMultiContext(context: Any = None, - context_type: int = ContextType_NORMAL, - flags: int = 0, - api_version: int = 0) -> "RenderScript": - """创建多上下文 RenderScript""" - rs = RenderScript() - rs._context_type = context_type - rs._initialized = True - return rs - - def setPriority(self, priority: int): - """设置优先级""" - self._priority = priority - - def getPriority(self) -> int: - return self._priority - - def setErrorHandler(self, handler: Callable): - """设置错误处理器""" - self._error_handler = handler - - def setMessageHandler(self, handler: Callable): - """设置消息处理器""" - self._message_handler = handler - - def finish(self): - """等待所有挂起渲染完成""" - pass - - def destroy(self): - """销毁 RenderScript 上下文""" - self._initialized = False - - def getApplicationContext(self) -> Any: - return None - - def getErrorHandler(self) -> Optional[Callable]: - return self._error_handler - - def getMessageHandler(self) -> Optional[Callable]: - return self._message_handler - - def isDestroyed(self) -> bool: - return not self._initialized - - def nvLoadAll(self): - pass - - def nvLoadFont(self, name: str) -> int: - return 0 - - def nvLoadProp(self, name: str) -> int: - return 0 - - def toString(self) -> str: - return f"RenderScript(priority={self._priority}, initialized={self._initialized})" - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# Element -# ═══════════════════════════════════════════════════════════════ - -class Element(Stub): - """Element — RenderScript 数据类型元素""" - - # 预定义数据类型 - F32: "Element" = None - F64: "Element" = None - I8: "Element" = None - I16: "Element" = None - I32: "Element" = None - I64: "Element" = None - U8: "Element" = None - U16: "Element" = None - U32: "Element" = None - U64: "Element" = None - BOOLEAN: "Element" = None - ALLOCATION: "Element" = None - A_8: "Element" = None - RGB_565: "Element" = None - RGBA_4444: "Element" = None - RGBA_8888: "Element" = None - F32_2: "Element" = None - F32_3: "Element" = None - F32_4: "Element" = None - F64_2: "Element" = None - F64_3: "Element" = None - F64_4: "Element" = None - I8_2: "Element" = None - I8_3: "Element" = None - I8_4: "Element" = None - I16_2: "Element" = None - I16_3: "Element" = None - I16_4: "Element" = None - I32_2: "Element" = None - I32_3: "Element" = None - I32_4: "Element" = None - I64_2: "Element" = None - I64_3: "Element" = None - I64_4: "Element" = None - U8_2: "Element" = None - U8_3: "Element" = None - U8_4: "Element" = None - U16_2: "Element" = None - U16_3: "Element" = None - U16_4: "Element" = None - U32_2: "Element" = None - U32_3: "Element" = None - U32_4: "Element" = None - U64_2: "Element" = None - U64_3: "Element" = None - U64_4: "Element" = None - - # 数据类型 - DataType = type("DataType", (), { - "NONE": 0, "FLOAT_32": 1, "FLOAT_64": 2, - "SIGNED_8": 3, "SIGNED_16": 4, "SIGNED_32": 5, "SIGNED_64": 6, - "UNSIGNED_8": 7, "UNSIGNED_16": 8, "UNSIGNED_32": 9, "UNSIGNED_64": 10, - "BOOLEAN": 11, "UNSIGNED_5_6_5": 12, "UNSIGNED_5_5_5_1": 13, - "UNSIGNED_4_4_4_4": 14, "MATRIX_4X4": 15, "MATRIX_3X3": 16, - "UNSIGNED_2_10_10_10": 17, "RS_ELEMENT": 18, - }) - - # 数据类型大小 - DataType = DataType() - - def __init__(self, rs: Optional[RenderScript] = None, data_type: int = 0, - vector_size: int = 1): - super().__init__() - self._rs = rs - self._data_type = data_type - self._vector_size = vector_size - self._element_size = self._compute_size() - - def _compute_size(self) -> int: - base_sizes = { - Element.DataType.FLOAT_32: 4, Element.DataType.FLOAT_64: 8, - Element.DataType.SIGNED_8: 1, Element.DataType.SIGNED_16: 2, - Element.DataType.SIGNED_32: 4, Element.DataType.SIGNED_64: 8, - Element.DataType.UNSIGNED_8: 1, Element.DataType.UNSIGNED_16: 2, - Element.DataType.UNSIGNED_32: 4, Element.DataType.UNSIGNED_64: 8, - Element.DataType.BOOLEAN: 1, - Element.DataType.UNSIGNED_5_6_5: 2, - Element.DataType.UNSIGNED_5_5_5_1: 2, - Element.DataType.UNSIGNED_4_4_4_4: 2, - Element.DataType.MATRIX_4X4: 64, - Element.DataType.MATRIX_3X3: 36, - Element.DataType.RS_ELEMENT: 4, - } - return base_sizes.get(self._data_type, 4) * self._vector_size - - def getBytesSize(self) -> int: - return self._element_size - - def getVectorSize(self) -> int: - return self._vector_size - - def getDataType(self) -> int: - return self._data_type - - def isCompatible(self, e: "Element") -> bool: - return self._data_type == e._data_type and self._vector_size == e._vector_size - - def getSubElement(self, index: int) -> Optional["Element"]: - return None - - def getSubElementCount(self) -> int: - return 0 - - def getSubElementArraySize(self, index: int) -> int: - return 0 - - def getSubElementOffsetBytes(self, index: int) -> int: - return 0 - - def toString(self) -> str: - return f"Element(dataType={self._data_type}, vectorSize={self._vector_size})" - - def hashCode(self) -> int: - return hash((self._data_type, self._vector_size)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, Element): - return False - return self._data_type == other._data_type and self._vector_size == other._vector_size - - @staticmethod - def BOOL(rs: RenderScript) -> "Element": - return Element(rs, Element.DataType.BOOLEAN, 1) - - @staticmethod - def U8(rs: RenderScript) -> "Element": - return Element(rs, Element.DataType.UNSIGNED_8, 1) - - @staticmethod - def I8(rs: RenderScript) -> "Element": - return Element(rs, Element.DataType.SIGNED_8, 1) - - @staticmethod - def U16(rs: RenderScript) -> "Element": - return Element(rs, Element.DataType.UNSIGNED_16, 1) - - @staticmethod - def I16(rs: RenderScript) -> "Element": - return Element(rs, Element.DataType.SIGNED_16, 1) - - @staticmethod - def U32(rs: RenderScript) -> "Element": - return Element(rs, Element.DataType.UNSIGNED_32, 1) - - @staticmethod - def I32(rs: RenderScript) -> "Element": - return Element(rs, Element.DataType.SIGNED_32, 1) - - @staticmethod - def U64(rs: RenderScript) -> "Element": - return Element(rs, Element.DataType.UNSIGNED_64, 1) - - @staticmethod - def I64(rs: RenderScript) -> "Element": - return Element(rs, Element.DataType.SIGNED_64, 1) - - @staticmethod - def F32(rs: RenderScript) -> "Element": - return Element(rs, Element.DataType.FLOAT_32, 1) - - @staticmethod - def F64(rs: RenderScript) -> "Element": - return Element(rs, Element.DataType.FLOAT_64, 1) - - @staticmethod - def createVector(rs: RenderScript, data_type: int, size: int) -> "Element": - return Element(rs, data_type, size) - - @staticmethod - def createPixel(rs: RenderScript, data_type: int, pixel_type: int) -> "Element": - return Element(rs, data_type, 4) - - -# ═══════════════════════════════════════════════════════════════ -# Type -# ═══════════════════════════════════════════════════════════════ - -class Type(Stub): - """Type — RenderScript 内存分配类型""" - - CubemapFace = type("CubemapFace", (), { - "POSITIVE_X": 0, "NEGATIVE_X": 1, - "POSITIVE_Y": 2, "NEGATIVE_Y": 3, - "POSITIVE_Z": 4, "NEGATIVE_Z": 5, - }) - - def __init__(self, rs: Optional[RenderScript] = None, - element: Optional[Element] = None, - dim_x: int = 0, dim_y: int = 0, dim_z: int = 0, - mipmaps: bool = False, faces: bool = False): - super().__init__() - self._rs = rs - self._element = element - self._dim_x = dim_x - self._dim_y = dim_y - self._dim_z = dim_z - self._mipmaps = mipmaps - self._faces = faces - self._yuv_format = 0 - - def getElement(self) -> Optional[Element]: - return self._element - - def getX(self) -> int: - return self._dim_x - - def getY(self) -> int: - return self._dim_y - - def getZ(self) -> int: - return self._dim_z - - def getCount(self) -> int: - return self._dim_x * max(self._dim_y, 1) * max(self._dim_z, 1) - - def hasMipmaps(self) -> bool: - return self._mipmaps - - def hasFaces(self) -> bool: - return self._faces - - def getYuvFormat(self) -> int: - return self._yuv_format - - def setYuvFormat(self, yuv_format: int): - self._yuv_format = yuv_format - - def toString(self) -> str: - return (f"Type(element={self._element}, dims={self._dim_x}x{self._dim_y}" - f"x{self._dim_z}, mipmaps={self._mipmaps})") - - def hashCode(self) -> int: - return hash((self._dim_x, self._dim_y, self._dim_z)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, Type): - return False - return (self._dim_x == other._dim_x and self._dim_y == other._dim_y - and self._dim_z == other._dim_z) - - # ── Builder ── - - class Builder(Stub): - """Builder — Type 构建器""" - - def __init__(self, rs: RenderScript, element: Element): - super().__init__() - self._rs = rs - self._element = element - self._dim_x = 0 - self._dim_y = 0 - self._dim_z = 0 - self._mipmaps = False - self._faces = False - self._yuv_format = 0 - - def setX(self, value: int) -> "Type.Builder": - self._dim_x = value - return self - - def setY(self, value: int) -> "Type.Builder": - self._dim_y = value - return self - - def setZ(self, value: int) -> "Type.Builder": - self._dim_z = value - return self - - def setMipmaps(self, enable: bool) -> "Type.Builder": - self._mipmaps = enable - return self - - def setFaces(self, enable: bool) -> "Type.Builder": - self._faces = enable - return self - - def setYuvFormat(self, yuv_format: int) -> "Type.Builder": - self._yuv_format = yuv_format - return self - - def create(self) -> Type: - t = Type(self._rs, self._element, self._dim_x, self._dim_y, - self._dim_z, self._mipmaps, self._faces) - t._yuv_format = self._yuv_format - return t - - -# ═══════════════════════════════════════════════════════════════ -# Allocation -# ═══════════════════════════════════════════════════════════════ - -class Allocation(Stub): - """Allocation — RenderScript 内存分配""" - - # 使用类型 - USAGE_SCRIPT = 0x0001 - USAGE_GRAPHICS_TEXTURE = 0x0002 - USAGE_GRAPHICS_VERTEX = 0x0004 - USAGE_GRAPHICS_CONSTANTS = 0x0008 - USAGE_GRAPHICS_RENDER_TARGET = 0x0010 - USAGE_IO_INPUT = 0x0020 - USAGE_IO_OUTPUT = 0x0040 - USAGE_SHARED = 0x0080 - - # Mipmap 控制 - MIPMAP_FULL = 0 - MIPMAP_ON_SYNC_TO_TEXTURE = 1 - - def __init__(self, rs: Optional[RenderScript] = None, - type_: Optional[Type] = None, - usage: int = USAGE_SCRIPT): - super().__init__() - self._rs = rs - self._type = type_ - self._usage = usage - self._data: Any = None - self._element_size = type_.getElement().getBytesSize() if type_ and type_.getElement() else 0 - - @staticmethod - def createTyped(rs: RenderScript, type_: Type, - usage: int = USAGE_SCRIPT) -> "Allocation": - """创建分配""" - alloc = Allocation(rs, type_, usage) - total_size = type_.getCount() * type_.getElement().getBytesSize() if type_.getElement() else 0 - alloc._data = bytearray(total_size) - return alloc - - @staticmethod - def createFromBitmap(rs: RenderScript, bitmap: Any, - usage: int = USAGE_SCRIPT) -> "Allocation": - """从位图创建分配""" - # 尝试获取位图尺寸 - width, height = 0, 0 - try: - width = bitmap.getWidth() if hasattr(bitmap, 'getWidth') else 0 - height = bitmap.getHeight() if hasattr(bitmap, 'getHeight') else 0 - except Exception: - pass - element = Element(rs, Element.DataType.UNSIGNED_8, 4) - type_ = Type(rs, element, width, height) - alloc = Allocation(rs, type_, usage) - alloc._data = bytearray(width * height * 4) - return alloc - - @staticmethod - def createFromBitmapBoxed(rs: RenderScript, bitmap: Any, - usage: int = USAGE_SCRIPT) -> "Allocation": - return Allocation.createFromBitmap(rs, bitmap, usage) - - def copyFrom(self, data: Any): - """从数组或位图复制数据""" - if isinstance(data, (bytes, bytearray)): - self._data = bytearray(data) - elif hasattr(data, '__iter__'): - import struct - try: - self._data = bytearray(struct.pack(f'<{len(list(data))}f', - *[float(v) for v in data])) - except Exception: - self._data = bytearray(len(list(data)) * 4) - else: - self._data = bytearray(1024) - - def copyTo(self, data: Any): - """将数据复制到目标""" - if isinstance(data, bytearray) and self._data: - data[:] = self._data - elif isinstance(data, list): - data.clear() - if self._data: - for i in range(0, len(self._data), 4): - data.append(int.from_bytes(self._data[i:i+4], 'little')) - - def resize(self, dim_x: int): - """重新调整分配大小""" - if self._type: - self._type._dim_x = dim_x - self._data = bytearray(self._type.getCount() * self._element_size) - - def getType(self) -> Optional[Type]: - return self._type - - def getUsage(self) -> int: - return self._usage - - def getElement(self) -> Optional[Element]: - return self._type.getElement() if self._type else None - - def syncAll(self, src_location: int): - pass - - def destroy(self): - self._data = None - - def toString(self) -> str: - return (f"Allocation(type={self._type}, usage={self._usage}, " - f"data_size={len(self._data) if self._data else 0})") - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# Matrix4f -# ═══════════════════════════════════════════════════════════════ - -class Matrix4f(Stub): - """Matrix4f — 4x4 浮点矩阵""" - - def __init__(self): - super().__init__() - self._matrix = [0.0] * 16 - self._matrix[0] = 1.0 # 单位矩阵 - self._matrix[5] = 1.0 - self._matrix[10] = 1.0 - self._matrix[15] = 1.0 - - def getArray(self) -> List[float]: - return list(self._matrix) - - def setArray(self, array: List[float]): - if len(array) >= 16: - self._matrix = list(array[:16]) - - def loadIdentity(self): - self._matrix = [0.0] * 16 - self._matrix[0] = 1.0 - self._matrix[5] = 1.0 - self._matrix[10] = 1.0 - self._matrix[15] = 1.0 - - def load(self, src: "Matrix4f"): - self._matrix = list(src._matrix) - - def loadRotate(self, rot: float, x: float, y: float, z: float): - angle_rad = math.radians(rot) - c = math.cos(angle_rad) - s = math.sin(angle_rad) - self.loadIdentity() - if x == 1 and y == 0 and z == 0: - self._matrix[5] = c - self._matrix[6] = -s - self._matrix[9] = s - self._matrix[10] = c - elif x == 0 and y == 1 and z == 0: - self._matrix[0] = c - self._matrix[2] = s - self._matrix[8] = -s - self._matrix[10] = c - elif x == 0 and y == 0 and z == 1: - self._matrix[0] = c - self._matrix[1] = -s - self._matrix[4] = s - self._matrix[5] = c - - def loadScale(self, x: float, y: float, z: float): - self.loadIdentity() - self._matrix[0] = x - self._matrix[5] = y - self._matrix[10] = z - - def loadTranslate(self, x: float, y: float, z: float): - self.loadIdentity() - self._matrix[12] = x - self._matrix[13] = y - self._matrix[14] = z - - def loadMultiply(self, lhs: "Matrix4f", rhs: "Matrix4f"): - for i in range(4): - for j in range(4): - self._matrix[i * 4 + j] = sum( - lhs._matrix[i * 4 + k] * rhs._matrix[k * 4 + j] - for k in range(4) - ) - - def multiply(self, rhs: "Matrix4f"): - result = [0.0] * 16 - for i in range(4): - for j in range(4): - result[i * 4 + j] = sum( - self._matrix[i * 4 + k] * rhs._matrix[k * 4 + j] - for k in range(4) - ) - self._matrix = result - - def transpose(self): - for i in range(4): - for j in range(i + 1, 4): - self._matrix[i * 4 + j], self._matrix[j * 4 + i] = \ - self._matrix[j * 4 + i], self._matrix[i * 4 + j] - - def toString(self) -> str: - rows = [] - for i in range(4): - row = self._matrix[i*4:(i+1)*4] - rows.append(f"[{', '.join(f'{v:.4f}' for v in row)}]") - return f"Matrix4f({', '.join(rows)})" - - def hashCode(self) -> int: - return hash(tuple(self._matrix)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, Matrix4f): - return False - return self._matrix == other._matrix - - -# ═══════════════════════════════════════════════════════════════ -# Matrix3f -# ═══════════════════════════════════════════════════════════════ - -class Matrix3f(Stub): - """Matrix3f — 3x3 浮点矩阵""" - - def __init__(self): - super().__init__() - self._matrix = [0.0] * 9 - self._matrix[0] = 1.0 - self._matrix[4] = 1.0 - self._matrix[8] = 1.0 - - def getArray(self) -> List[float]: - return list(self._matrix) - - def setArray(self, array: List[float]): - if len(array) >= 9: - self._matrix = list(array[:9]) - - def loadIdentity(self): - self._matrix = [0.0] * 9 - self._matrix[0] = 1.0 - self._matrix[4] = 1.0 - self._matrix[8] = 1.0 - - def load(self, src: "Matrix3f"): - self._matrix = list(src._matrix) - - def loadMultiply(self, lhs: "Matrix3f", rhs: "Matrix3f"): - for i in range(3): - for j in range(3): - self._matrix[i * 3 + j] = sum( - lhs._matrix[i * 3 + k] * rhs._matrix[k * 3 + j] - for k in range(3) - ) - - def multiply(self, rhs: "Matrix3f"): - result = [0.0] * 9 - for i in range(3): - for j in range(3): - result[i * 3 + j] = sum( - self._matrix[i * 3 + k] * rhs._matrix[k * 3 + j] - for k in range(3) - ) - self._matrix = result - - def transpose(self): - for i in range(3): - for j in range(i + 1, 3): - self._matrix[i * 3 + j], self._matrix[j * 3 + i] = \ - self._matrix[j * 3 + i], self._matrix[i * 3 + j] - - def toString(self) -> str: - rows = [] - for i in range(3): - row = self._matrix[i*3:(i+1)*3] - rows.append(f"[{', '.join(f'{v:.4f}' for v in row)}]") - return f"Matrix3f({', '.join(rows)})" - - def hashCode(self) -> int: - return hash(tuple(self._matrix)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, Matrix3f): - return False - return self._matrix == other._matrix - - -# ═══════════════════════════════════════════════════════════════ -# Script (base) -# ═══════════════════════════════════════════════════════════════ - -class Script(Stub): - """Script — RenderScript 脚本基类""" - - def __init__(self, rs: Optional[RenderScript] = None): - super().__init__() - self._rs = rs - self._kernel_groups: List[Any] = [] - - def setVar(self, index: int, value: Any): - """设置脚本变量""" - pass - - def getVar(self, index: int) -> Any: - return None - - def setTimeZone(self, timezone: str): - pass - - def forEach(self, slot: int, ains: List[Allocation], - aout: Allocation, vdata: Any = None): - """执行 forEach 内核""" - raise AndroidOnlyError("Script.forEach() 仅在 Android (RenderScript) 设备上可用") - - def invoke(self, slot: int): - """调用可调用函数""" - pass - - def bindAllocation(self, va: Allocation, slot: int): - """绑定分配""" - pass - - def destroy(self): - pass - - def toString(self) -> str: - return f"Script@{hex(id(self))}" - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# ScriptC -# ═══════════════════════════════════════════════════════════════ - -class ScriptC(Script): - """ScriptC — 编译的 RenderScript 内核脚本""" - - def __init__(self, rs: RenderScript, resource_id: int = 0): - super().__init__(rs) - self._resource_id = resource_id - - def toString(self) -> str: - return f"ScriptC(resourceId={self._resource_id})" - - -# ═══════════════════════════════════════════════════════════════ -# ScriptIntrinsicBlur -# ═══════════════════════════════════════════════════════════════ - -class ScriptIntrinsicBlur(Script): - """ScriptIntrinsicBlur — 高斯模糊内在函数""" - - def __init__(self, rs: RenderScript): - super().__init__(rs) - self._radius: float = 5.0 - self._input: Optional[Allocation] = None - - @staticmethod - def create(rs: RenderScript, e: Element) -> "ScriptIntrinsicBlur": - """创建模糊内在函数""" - return ScriptIntrinsicBlur(rs) - - def setRadius(self, radius: float): - """设置模糊半径""" - self._radius = max(0.0, min(radius, 25.0)) - - def setInput(self, alloc: Allocation): - """设置输入分配""" - self._input = alloc - - def forEach(self, aout: Allocation): - """执行模糊""" - if self._input and aout: - pass # 在桌面环境不执行实际模糊 - - def toString(self) -> str: - return f"ScriptIntrinsicBlur(radius={self._radius})" - - -# ═══════════════════════════════════════════════════════════════ -# ScriptIntrinsicConvolve3x3 -# ═══════════════════════════════════════════════════════════════ - -class ScriptIntrinsicConvolve3x3(Script): - """ScriptIntrinsicConvolve3x3 — 3x3 卷积内在函数""" - - def __init__(self, rs: RenderScript): - super().__init__(rs) - self._coefficients: List[float] = [0.0] * 9 - self._input: Optional[Allocation] = None - - @staticmethod - def create(rs: RenderScript, e: Element) -> "ScriptIntrinsicConvolve3x3": - """创建卷积内在函数""" - return ScriptIntrinsicConvolve3x3(rs) - - def setCoefficients(self, coeffs: List[float]): - """设置卷积系数""" - for i in range(min(len(coeffs), 9)): - self._coefficients[i] = coeffs[i] - - def setInput(self, alloc: Allocation): - """设置输入分配""" - self._input = alloc - - def forEach(self, aout: Allocation): - """执行卷积""" - pass - - def toString(self) -> str: - return f"ScriptIntrinsicConvolve3x3(coefficients={self._coefficients})" - - -# ═══════════════════════════════════════════════════════════════ -# 初始化 Element 静态实例 -# ═══════════════════════════════════════════════════════════════ - -def _init_elements(): - """初始化 Element 预定义实例(在模块导入时调用)""" - rs = RenderScript.create() - Element.F32 = Element.F32(rs) - Element.F64 = Element.F64(rs) - Element.I8 = Element.I8(rs) - Element.I16 = Element.I16(rs) - Element.I32 = Element.I32(rs) - Element.I64 = Element.I64(rs) - Element.U8 = Element.U8(rs) - Element.U16 = Element.U16(rs) - Element.U32 = Element.U32(rs) - Element.U64 = Element.U64(rs) - Element.BOOLEAN = Element.BOOL(rs) - Element.ALLOCATION = Element(rs, Element.DataType.RS_ELEMENT, 1) - Element.A_8 = Element.createPixel(rs, Element.DataType.UNSIGNED_8, 1) - Element.RGB_565 = Element.createPixel(rs, Element.DataType.UNSIGNED_5_6_5, 3) - Element.RGBA_4444 = Element.createPixel(rs, Element.DataType.UNSIGNED_4_4_4_4, 4) - Element.RGBA_8888 = Element.createPixel(rs, Element.DataType.UNSIGNED_8, 4) - Element.F32_2 = Element.createVector(rs, Element.DataType.FLOAT_32, 2) - Element.F32_3 = Element.createVector(rs, Element.DataType.FLOAT_32, 3) - Element.F32_4 = Element.createVector(rs, Element.DataType.FLOAT_32, 4) - Element.F64_2 = Element.createVector(rs, Element.DataType.FLOAT_64, 2) - Element.F64_3 = Element.createVector(rs, Element.DataType.FLOAT_64, 3) - Element.F64_4 = Element.createVector(rs, Element.DataType.FLOAT_64, 4) - Element.I8_2 = Element.createVector(rs, Element.DataType.SIGNED_8, 2) - Element.I8_3 = Element.createVector(rs, Element.DataType.SIGNED_8, 3) - Element.I8_4 = Element.createVector(rs, Element.DataType.SIGNED_8, 4) - Element.I16_2 = Element.createVector(rs, Element.DataType.SIGNED_16, 2) - Element.I16_3 = Element.createVector(rs, Element.DataType.SIGNED_16, 3) - Element.I16_4 = Element.createVector(rs, Element.DataType.SIGNED_16, 4) - Element.I32_2 = Element.createVector(rs, Element.DataType.SIGNED_32, 2) - Element.I32_3 = Element.createVector(rs, Element.DataType.SIGNED_32, 3) - Element.I32_4 = Element.createVector(rs, Element.DataType.SIGNED_32, 4) - Element.I64_2 = Element.createVector(rs, Element.DataType.SIGNED_64, 2) - Element.I64_3 = Element.createVector(rs, Element.DataType.SIGNED_64, 3) - Element.I64_4 = Element.createVector(rs, Element.DataType.SIGNED_64, 4) - Element.U8_2 = Element.createVector(rs, Element.DataType.UNSIGNED_8, 2) - Element.U8_3 = Element.createVector(rs, Element.DataType.UNSIGNED_8, 3) - Element.U8_4 = Element.createVector(rs, Element.DataType.UNSIGNED_8, 4) - Element.U16_2 = Element.createVector(rs, Element.DataType.UNSIGNED_16, 2) - Element.U16_3 = Element.createVector(rs, Element.DataType.UNSIGNED_16, 3) - Element.U16_4 = Element.createVector(rs, Element.DataType.UNSIGNED_16, 4) - Element.U32_2 = Element.createVector(rs, Element.DataType.UNSIGNED_32, 2) - Element.U32_3 = Element.createVector(rs, Element.DataType.UNSIGNED_32, 3) - Element.U32_4 = Element.createVector(rs, Element.DataType.UNSIGNED_32, 4) - Element.U64_2 = Element.createVector(rs, Element.DataType.UNSIGNED_64, 2) - Element.U64_3 = Element.createVector(rs, Element.DataType.UNSIGNED_64, 3) - Element.U64_4 = Element.createVector(rs, Element.DataType.UNSIGNED_64, 4) - - -_init_elements() - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "RenderScript", - "Allocation", - "Element", - "Type", - "Script", - "ScriptIntrinsicBlur", - "ScriptIntrinsicConvolve3x3", - "ScriptC", - "Matrix4f", - "Matrix3f", -] \ No newline at end of file diff --git a/android_pyapi/android/se.py b/android_pyapi/android/se.py deleted file mode 100644 index 00d9366..0000000 --- a/android_pyapi/android/se.py +++ /dev/null @@ -1,175 +0,0 @@ -""" -Android se 组件 — SecureElement, Session, Channel, Reader, OmapiConnection 等 -方法签名完全对齐 Android SDK (SecureElement API) -""" - -from typing import Any, Callable, Dict, List, Optional - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# SecureElement -# ═══════════════════════════════════════════════════════════════ - -class SecureElement(Stub): - """SecureElement — 安全元件""" - - SERVICE_INTERFACE = "android.se.SECURE_ELEMENT_SERVICE" - EXTRA_READER = "android.se.extra.READER" - EXTRA_READER_NAME = "android.se.extra.READER_NAME" - EXTRA_SLOT_ID = "android.se.extra.SLOT_ID" - EXTRA_CONNECT_RESPONSE = "android.se.extra.CONNECT_RESPONSE" - - def __init__(self): - super().__init__() - - -# ═══════════════════════════════════════════════════════════════ -# Reader -# ═══════════════════════════════════════════════════════════════ - -class Reader(Stub): - """Reader — 安全元件读取器""" - - def __init__(self, name: str = "eSE"): - super().__init__() - self._name = name - self._sessions: List[Session] = [] - - def getName(self) -> str: - return self._name - - def openSession(self) -> "Session": - session = Session(self) - self._sessions.append(session) - return session - - def closeSessions(self): - for s in self._sessions: - s.close() - self._sessions.clear() - - def isSecureElementPresent(self) -> bool: - return True - - def toString(self) -> str: - return f"Reader(name={self._name})" - - -# ═══════════════════════════════════════════════════════════════ -# Session -# ═══════════════════════════════════════════════════════════════ - -class Session(Stub): - """Session — 安全元件会话""" - - def __init__(self, reader: Optional[Reader] = None): - super().__init__() - self._reader = reader - self._channels: List[Channel] = [] - self._closed = False - - def openLogicalChannel(self, aid: bytes) -> Optional["Channel"]: - if self._closed: - return None - channel = Channel(self, logical=True) - self._channels.append(channel) - channel._select_response = bytes([0x90, 0x00]) # SW_OK - return channel - - def openBasicChannel(self, aid: bytes) -> Optional["Channel"]: - if self._closed: - return None - channel = Channel(self, logical=False) - self._channels.append(channel) - channel._select_response = bytes([0x90, 0x00]) # SW_OK - return channel - - def close(self): - for c in self._channels: - c.close() - self._channels.clear() - self._closed = True - - def getSessions(self) -> List["Session"]: - return [self] - - def getChannels(self) -> List["Channel"]: - return list(self._channels) - - def toString(self) -> str: - return f"Session(reader={self._reader}, channels={len(self._channels)})" - - -# ═══════════════════════════════════════════════════════════════ -# Channel -# ═══════════════════════════════════════════════════════════════ - -class Channel(Stub): - """Channel — 安全元件通道""" - - def __init__(self, session: Session, logical: bool = True): - super().__init__() - self._session = session - self._logical = logical - self._closed = False - self._select_response: Optional[bytes] = None - - def close(self): - self._closed = True - - def sendData(self, command: bytes) -> bytes: - if self._closed: - return bytes([0x6F, 0x00]) # No such EF - # Simulate successful APDU response - return bytes([0x90, 0x00]) - - def getSelectResponse(self) -> Optional[bytes]: - return self._select_response - - def getSession(self) -> Session: - return self._session - - def isBasicChannel(self) -> bool: - return not self._logical - - def isClosed(self) -> bool: - return self._closed - - def toString(self) -> str: - return f"Channel(logical={self._logical}, closed={self._closed})" - - -# ═══════════════════════════════════════════════════════════════ -# OmapiConnection -# ═══════════════════════════════════════════════════════════════ - -class OmapiConnection(Stub): - """OmapiConnection — OMAPI 连接入口""" - - def __init__(self): - super().__init__() - - @staticmethod - def getInstance(context: Any = None) -> "OmapiConnection": - return OmapiConnection() - - def getReaders(self) -> List[Reader]: - return [Reader("eSE")] - - def getReader(self, name: str) -> Optional[Reader]: - return Reader(name) - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "SecureElement", - "Reader", - "Session", - "Channel", - "OmapiConnection", -] \ No newline at end of file diff --git a/android_pyapi/android/security.py b/android_pyapi/android/security.py deleted file mode 100644 index f4a6431..0000000 --- a/android_pyapi/android/security.py +++ /dev/null @@ -1,182 +0,0 @@ -""" -Android Security 组件 — KeyChain, NetworkSecurityPolicy, AppUriAuthenticationPolicy 等 -方法签名完全对齐 Android SDK (android.security 包) -""" - -from typing import Any, Callable, Dict, List, Optional - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# KeyChain -# ═══════════════════════════════════════════════════════════════ - -class KeyChain(Stub): - """KeyChain — 系统级密钥链访问 (桌面环境不可用)""" - - ACTION_KEYCHAIN_CHANGED = "android.security.action.KEYCHAIN_CHANGED" - ACTION_KEY_ACCESS_CHANGED = "android.security.action.KEY_ACCESS_CHANGED" - EXTRA_KEY_ACCESSIBLE = "android.security.extra.KEY_ACCESSIBLE" - EXTRA_KEY_ALIAS = "android.security.extra.KEY_ALIAS" - EXTRA_RESULT = "android.security.extra.RESULT" - - def __init__(self): - super().__init__() - - @staticmethod - def createInstallIntent() -> Any: - """创建安装证书的 Intent (Android-only)""" - raise AndroidOnlyError("KeyChain.createInstallIntent() 仅在 Android 设备上可用") - - @staticmethod - def choosePrivateKeyAlias(activity: Any, response: Any, - keyTypes: Optional[List[str]] = None, - issuers: Optional[List[Any]] = None, - host: Optional[str] = None, - port: int = -1, - alias: Optional[str] = None): - """选择私钥别名 (Android-only)""" - raise AndroidOnlyError("KeyChain.choosePrivateKeyAlias() 仅在 Android 设备上可用") - - @staticmethod - def isKeyAlgorithmSupported(algorithm: str) -> bool: - """检查密钥算法是否支持""" - supported = {"RSA", "EC", "DSA", "AES", "HmacSHA256", "HmacSHA1"} - return algorithm in supported - - @staticmethod - def isBoundKeyAlgorithm(algorithm: str) -> bool: - """检查是否是绑定到硬件的密钥算法 (桌面环境不支持)""" - return False - - @staticmethod - def getPrivateKey(context: Any, alias: str) -> Any: - """获取私钥 (Android-only)""" - raise AndroidOnlyError("KeyChain.getPrivateKey() 仅在 Android 设备上可用") - - @staticmethod - def getCertificateChain(context: Any, alias: str) -> Optional[List[Any]]: - """获取证书链 (Android-only)""" - raise AndroidOnlyError("KeyChain.getCertificateChain() 仅在 Android 设备上可用") - - def toString(self) -> str: - return "KeyChain(desktop-stub)" - - def hashCode(self) -> int: - return hash("android.security.KeyChain") & 0x7FFFFFFF - - def equals(self, other) -> bool: - return isinstance(other, KeyChain) - - -# ═══════════════════════════════════════════════════════════════ -# AppUriAuthenticationPolicy -# ═══════════════════════════════════════════════════════════════ - -class AppUriAuthenticationPolicy(Stub): - """AppUriAuthenticationPolicy — URI 认证策略 (Android 11+)""" - - def __init__(self): - super().__init__() - self._rules: Dict[str, List[str]] = {} - - def addAppAndUriMapping(self, app: Any, uri: Any, alias: str): - """添加应用与 URI 映射""" - app_pkg = str(app) if app else "" - uri_str = str(uri) if uri else "" - if app_pkg not in self._rules: - self._rules[app_pkg] = [] - self._rules[app_pkg].append(uri_str) - - def getAppAndUriMappings(self) -> Dict[str, List[str]]: - """获取所有规则映射""" - return dict(self._rules) - - def toString(self) -> str: - return f"AppUriAuthenticationPolicy(rules={self._rules})" - - def hashCode(self) -> int: - return hash(str(self._rules)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, AppUriAuthenticationPolicy): - return False - return self._rules == other._rules - - -# ═══════════════════════════════════════════════════════════════ -# KeyChainAliasResponse -# ═══════════════════════════════════════════════════════════════ - -class KeyChainAliasResponse(Stub): - """KeyChainAliasResponse — 密钥别名选择响应""" - - def __init__(self, alias: Optional[str] = None): - super().__init__() - self._alias = alias - - def getAlias(self) -> Optional[str]: - """获取选定的密钥别名""" - return self._alias - - def toString(self) -> str: - return f"KeyChainAliasResponse(alias={self._alias})" - - def hashCode(self) -> int: - return hash(self._alias) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, KeyChainAliasResponse): - return False - return self._alias == other._alias - - -# ═══════════════════════════════════════════════════════════════ -# NetworkSecurityPolicy -# ═══════════════════════════════════════════════════════════════ - -class NetworkSecurityPolicy(Stub): - """NetworkSecurityPolicy — 网络安全策略""" - - _instance: Optional["NetworkSecurityPolicy"] = None - - def __init__(self): - super().__init__() - self._cleartext_permitted = True - - @staticmethod - def getInstance() -> "NetworkSecurityPolicy": - """获取全局 NetworkSecurityPolicy 实例""" - if NetworkSecurityPolicy._instance is None: - NetworkSecurityPolicy._instance = NetworkSecurityPolicy() - return NetworkSecurityPolicy._instance - - def isCleartextTrafficPermitted(self, host: Optional[str] = None) -> bool: - """检查是否允许明文流量 (桌面默认允许)""" - return self._cleartext_permitted - - def setCleartextTrafficPermitted(self, permitted: bool): - """设置是否允许明文流量""" - self._cleartext_permitted = permitted - - def toString(self) -> str: - return f"NetworkSecurityPolicy(cleartextPermitted={self._cleartext_permitted})" - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "KeyChain", - "AppUriAuthenticationPolicy", - "KeyChainAliasResponse", - "NetworkSecurityPolicy", -] \ No newline at end of file diff --git a/android_pyapi/android/service_autofill.py b/android_pyapi/android/service_autofill.py deleted file mode 100644 index 393a1f5..0000000 --- a/android_pyapi/android/service_autofill.py +++ /dev/null @@ -1,423 +0,0 @@ -from __future__ import annotations - -""" -Android Service Autofill 组件 — AutofillService, FillRequest, FillResponse, -SaveRequest, SaveInfo, Dataset, AutofillFieldClassificationService 等 -方法签名完全对齐 Android SDK (android.service.autofill 包) -""" - -import time -from typing import Any, Callable, Dict, List, Optional, Set - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# FillRequest -# ═══════════════════════════════════════════════════════════════ - -class FillRequest(Stub): - """FillRequest — 自动填充请求""" - - # 请求标志 - FLAG_MANUAL_REQUEST = 0x0001 - FLAG_COMPATIBILITY_MODE_REQUEST = 0x0002 - - def __init__(self, request_id: int = 0, flags: int = 0, - fill_contexts: Optional[List[Any]] = None): - super().__init__() - self._request_id = request_id - self._flags = flags - self._fill_contexts = fill_contexts or [] - self._client_state: Optional[Any] = None - - def getId(self) -> int: - """获取请求 ID""" - return self._request_id - - def getFlags(self) -> int: - """获取标志""" - return self._flags - - def getFillContexts(self) -> List[Any]: - """获取填充上下文""" - return list(self._fill_contexts) - - def getClientState(self) -> Optional[Any]: - return self._client_state - - def setClientState(self, state: Any): - self._client_state = state - - def toString(self) -> str: - return (f"FillRequest(id={self._request_id}, " - f"flags={self._flags}, contexts={len(self._fill_contexts)})") - - def hashCode(self) -> int: - return self._request_id & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, FillRequest): - return False - return self._request_id == other._request_id - - -# ═══════════════════════════════════════════════════════════════ -# Dataset -# ═══════════════════════════════════════════════════════════════ - -class Dataset(Stub): - """Dataset — 自动填充数据集""" - - def __init__(self): - super().__init__() - self._field_values: Dict[Any, Any] = {} - self._id: Optional[Any] = None - self._authentication: Optional[Any] = None - - def getFieldValues(self) -> Dict[Any, Any]: - return dict(self._field_values) - - def getId(self) -> Optional[Any]: - return self._id - - def setId(self, id: Any): - self._id = id - - def toString(self) -> str: - return f"Dataset(fields={len(self._field_values)}, id={self._id})" - - def hashCode(self) -> int: - return hash(str(self._id)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, Dataset): - return False - return self._id == other._id - - # ── Builder ── - - class Builder(Stub): - """Builder — Dataset 构建器""" - - def __init__(self): - super().__init__() - self._dataset = Dataset() - - def setId(self, id: Any) -> "Dataset.Builder": - self._dataset._id = id - return self - - def setValue(self, id: Any, value: Any) -> "Dataset.Builder": - self._dataset._field_values[id] = value - return self - - def setAuthentication(self, authentication: Any) -> "Dataset.Builder": - self._dataset._authentication = authentication - return self - - def build(self) -> Dataset: - return self._dataset - - -# ═══════════════════════════════════════════════════════════════ -# SaveInfo -# ═══════════════════════════════════════════════════════════════ - -class SaveInfo(Stub): - """SaveInfo — 保存信息""" - - # 保存类型 - SAVE_DATA_TYPE_EMAIL = 0x0001 - SAVE_DATA_TYPE_PASSWORD = 0x0002 - SAVE_DATA_TYPE_USERNAME = 0x0004 - SAVE_DATA_TYPE_CREDIT_CARD = 0x0008 - SAVE_DATA_TYPE_ADDRESS = 0x0010 - SAVE_DATA_TYPE_GENERIC = 0x0020 - SAVE_DATA_TYPE_PASSWORD_V2 = 0x0040 - SAVE_DATA_TYPE_SMS_2FA = 0x0080 - SAVE_DATA_TYPE_BIRTH_DATE = 0x0100 - SAVE_DATA_TYPE_EMAIL_ADDRESS = 0x0200 - SAVE_DATA_TYPE_PHONE = 0x0400 - SAVE_DATA_TYPE_USERNAME_V2 = 0x0800 - SAVE_DATA_TYPE_CARD_NUMBER = 0x1000 - SAVE_DATA_TYPE_CARD_EXPIRATION_DATE = 0x2000 - SAVE_DATA_TYPE_CARD_SECURITY_CODE = 0x4000 - SAVE_DATA_TYPE_FI_ACCOUNT_NUMBER = 0x8000 - SAVE_DATA_TYPE_FI_ROUTING_NUMBER = 0x10000 - SAVE_DATA_TYPE_PASSPORT = 0x20000 - SAVE_DATA_TYPE_DRIVERS_LICENSE = 0x40000 - SAVE_DATA_TYPE_SSN = 0x80000 - - # 标志 - FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 0x0001 - - def __init__(self): - super().__init__() - self._type: int = SaveInfo.SAVE_DATA_TYPE_GENERIC - self._required_ids: List[Any] = [] - self._optional_ids: List[Any] = [] - self._description: Optional[Any] = None - self._flags: int = 0 - self._trigger_ids: List[Any] = [] - self._validator: Optional[Any] = None - - def getType(self) -> int: - return self._type - - def getRequiredIds(self) -> List[Any]: - return list(self._required_ids) - - def getOptionalIds(self) -> List[Any]: - return list(self._optional_ids) - - def getDescription(self) -> Optional[Any]: - return self._description - - def getFlags(self) -> int: - return self._flags - - def toString(self) -> str: - return (f"SaveInfo(type={self._type}, required={len(self._required_ids)}, " - f"optional={len(self._optional_ids)})") - - def hashCode(self) -> int: - return hash((self._type, tuple(self._required_ids))) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, SaveInfo): - return False - return self._type == other._type - - # ── Builder ── - - class Builder(Stub): - """Builder — SaveInfo 构建器""" - - def __init__(self, type_: int, required_ids: List[Any]): - super().__init__() - self._save_info = SaveInfo() - self._save_info._type = type_ - self._save_info._required_ids = list(required_ids) - - def setOptionalIds(self, optional_ids: List[Any]) -> "SaveInfo.Builder": - self._save_info._optional_ids = list(optional_ids) - return self - - def setDescription(self, description: Any) -> "SaveInfo.Builder": - self._save_info._description = description - return self - - def setFlags(self, flags: int) -> "SaveInfo.Builder": - self._save_info._flags = flags - return self - - def setTriggerIds(self, trigger_ids: List[Any]) -> "SaveInfo.Builder": - self._save_info._trigger_ids = list(trigger_ids) - return self - - def setValidator(self, validator: Any) -> "SaveInfo.Builder": - self._save_info._validator = validator - return self - - def build(self) -> SaveInfo: - return self._save_info - - -# ═══════════════════════════════════════════════════════════════ -# FillResponse -# ═══════════════════════════════════════════════════════════════ - -class FillResponse(Stub): - """FillResponse — 自动填充响应""" - - def __init__(self): - super().__init__() - self._datasets: List[Dataset] = [] - self._save_info: Optional[SaveInfo] = None - self._client_state: Optional[Any] = None - self._authentication_result: Optional[Any] = None - - def getDatasets(self) -> List[Dataset]: - return list(self._datasets) - - def getSaveInfo(self) -> Optional[SaveInfo]: - return self._save_info - - def getClientState(self) -> Optional[Any]: - return self._client_state - - def toString(self) -> str: - return (f"FillResponse(datasets={len(self._datasets)}, " - f"saveInfo={'set' if self._save_info else 'none'})") - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - # ── Builder ── - - class Builder(Stub): - """Builder — FillResponse 构建器""" - - def __init__(self): - super().__init__() - self._response = FillResponse() - - def addDataset(self, dataset: Dataset) -> "FillResponse.Builder": - self._response._datasets.append(dataset) - return self - - def setDatasets(self, datasets: List[Dataset]) -> "FillResponse.Builder": - self._response._datasets = list(datasets) - return self - - def setSaveInfo(self, save_info: SaveInfo) -> "FillResponse.Builder": - self._response._save_info = save_info - return self - - def setClientState(self, client_state: Any) -> "FillResponse.Builder": - self._response._client_state = client_state - return self - - def setAuthenticationResult(self, result: Any) -> "FillResponse.Builder": - self._response._authentication_result = result - return self - - def build(self) -> FillResponse: - return self._response - - -# ═══════════════════════════════════════════════════════════════ -# SaveRequest -# ═══════════════════════════════════════════════════════════════ - -class SaveRequest(Stub): - """SaveRequest — 保存请求""" - - def __init__(self, request_id: int = 0, - client_state: Optional[Any] = None, - fill_contexts: Optional[List[Any]] = None): - super().__init__() - self._request_id = request_id - self._client_state = client_state - self._fill_contexts = fill_contexts or [] - - def getId(self) -> int: - return self._request_id - - def getClientState(self) -> Optional[Any]: - return self._client_state - - def getFillContexts(self) -> List[Any]: - return list(self._fill_contexts) - - def toString(self) -> str: - return (f"SaveRequest(id={self._request_id}, " - f"contexts={len(self._fill_contexts)})") - - def hashCode(self) -> int: - return self._request_id & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, SaveRequest): - return False - return self._request_id == other._request_id - - -# ═══════════════════════════════════════════════════════════════ -# AutofillService -# ═══════════════════════════════════════════════════════════════ - -class AutofillService(Stub): - """AutofillService — 自动填充服务""" - - SERVICE_INTERFACE = "android.service.autofill.AutofillService" - SERVICE_META_DATA = "android.autofill" - - def __init__(self): - super().__init__() - - def onFillRequest(self, request: FillRequest, - cancellation_signal: Any, - callback: Any): - """填充请求回调""" - response = FillResponse() - if callback and hasattr(callback, 'onSuccess'): - callback.onSuccess(response) - - def onSaveRequest(self, request: SaveRequest, callback: Any): - """保存请求回调""" - if callback and hasattr(callback, 'onSuccess'): - callback.onSuccess() - - def onConnected(self): - """服务已连接""" - pass - - def onDisconnected(self): - """服务已断开""" - pass - - def onUnbind(self, intent: Any) -> bool: - return False - - def toString(self) -> str: - return f"AutofillService@{hex(id(self))}" - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# AutofillFieldClassificationService -# ═══════════════════════════════════════════════════════════════ - -class AutofillFieldClassificationService(Stub): - """AutofillFieldClassificationService — 自动填充字段分类服务""" - - SERVICE_INTERFACE = ( - "android.service.autofill.AutofillFieldClassificationService" - ) - - def __init__(self): - super().__init__() - - def onConnected(self): - pass - - def onDisconnected(self): - pass - - def onClassificationRequest(self, request: Any, callback: Any): - if callback and hasattr(callback, 'onSuccess'): - callback.onSuccess(None) - - def toString(self) -> str: - return f"AutofillFieldClassificationService@{hex(id(self))}" - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "AutofillService", - "FillRequest", - "FillResponse", - "SaveRequest", - "SaveInfo", - "Dataset", - "AutofillFieldClassificationService", -] \ No newline at end of file diff --git a/android_pyapi/android/service_controls.py b/android_pyapi/android/service_controls.py deleted file mode 100644 index 8a1e45c..0000000 --- a/android_pyapi/android/service_controls.py +++ /dev/null @@ -1,350 +0,0 @@ -""" -Android Service Controls 组件 — ControlsProviderService, Control, DeviceTypes, -StatefulControl, StatelessControl 等 -方法签名完全对齐 Android SDK (android.service.controls 包) -""" - -from typing import Any, Callable, Dict, List, Optional, Set - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# DeviceTypes -# ═══════════════════════════════════════════════════════════════ - -class DeviceTypes(Stub): - """DeviceTypes — 设备类型常量""" - - TYPE_AC_HEATER = "AC_HEATER" - TYPE_AC_UNIT = "AC_UNIT" - TYPE_AIR_FRESHENER = "AIR_FRESHENER" - TYPE_AIR_PURIFIER = "AIR_PURIFIER" - TYPE_BLINDS = "BLINDS" - TYPE_CAMERA = "CAMERA" - TYPE_CLOSET = "CLOSET" - TYPE_COFFEE_MAKER = "COFFEE_MAKER" - TYPE_CURTAIN = "CURTAIN" - TYPE_DEHUMIDIFIER = "DEHUMIDIFIER" - TYPE_DISHWASHER = "DISHWASHER" - TYPE_DISPLAY = "DISPLAY" - TYPE_DOOR = "DOOR" - TYPE_DOORBELL = "DOORBELL" - TYPE_DRAWER = "DRAWER" - TYPE_DRYER = "DRYER" - TYPE_FAN = "FAN" - TYPE_GARAGE = "GARAGE" - TYPE_GATE = "GATE" - TYPE_HEATER = "HEATER" - TYPE_HOOD = "HOOD" - TYPE_HUMIDIFIER = "HUMIDIFIER" - TYPE_KETTLE = "KETTLE" - TYPE_LIGHT = "LIGHT" - TYPE_LIGHTBULB = "LIGHTBULB" - TYPE_LOCK = "LOCK" - TYPE_MICROWAVE = "MICROWAVE" - TYPE_MOP = "MOP" - TYPE_MOWER = "MOWER" - TYPE_MULTICOOKER = "MULTICOOKER" - TYPE_OUTLET = "OUTLET" - TYPE_OVEN = "OVEN" - TYPE_PERGOLLA = "PERGOLLA" - TYPE_RADIATOR = "RADIATOR" - TYPE_REFRIGERATOR = "REFRIGERATOR" - TYPE_REMOTE_STAND = "REMOTE_STAND" - TYPE_RICE_COOKER = "RICE_COOKER" - TYPE_ROUTINE = "ROUTINE" - TYPE_SCENE = "SCENE" - TYPE_SECURITY_SYSTEM = "SECURITY_SYSTEM" - TYPE_SET_TOP = "SET_TOP" - TYPE_SHOWER = "SHOWER" - TYPE_SHUTTER = "SHUTTER" - TYPE_SMOKE_DETECTOR = "SMOKE_DETECTOR" - TYPE_SOUNDBAR = "SOUNDBAR" - TYPE_SPRINKLER = "SPRINKLER" - TYPE_STANDMIXER = "STANDMIXER" - TYPE_THERMOSTAT = "THERMOSTAT" - TYPE_TV = "TV" - TYPE_VACUUM = "VACUUM" - TYPE_VALVE = "VALVE" - TYPE_WASHER = "WASHER" - TYPE_WATER_HEATER = "WATER_HEATER" - TYPE_WINDOW = "WINDOW" - TYPE_WINE_COOLER = "WINE_COOLER" - - def __init__(self): - super().__init__() - - -# ═══════════════════════════════════════════════════════════════ -# Control -# ═══════════════════════════════════════════════════════════════ - -class Control(Stub): - """Control — 设备控件基类""" - - # 状态 - STATUS_UNKNOWN = 0 - STATUS_OK = 1 - STATUS_NOT_RESPONDING = 2 - STATUS_ERROR = 3 - STATUS_FIRMWARE_UPDATE = 4 - - def __init__(self, control_id: str, title: str, - subtitle: str, device_type: str): - super().__init__() - self._control_id = control_id - self._title = title - self._subtitle = subtitle - self._device_type = device_type - self._status = Control.STATUS_OK - self._status_text: str = "" - self._custom_icon: Optional[Any] = None - self._custom_color: int = 0 - self._is_auth_required: bool = False - self._structure: str = "" - self._zone: str = "" - self._is_favorite: bool = False - self._device_types_list: List[str] = [device_type] - self._parent_id: Optional[str] = None - - def getControlId(self) -> str: - """获取控件 ID""" - return self._control_id - - def getTitle(self) -> str: - """获取标题""" - return self._title - - def getSubtitle(self) -> str: - """获取副标题""" - return self._subtitle - - def getDeviceType(self) -> str: - """获取设备类型""" - return self._device_type - - def getStatus(self) -> int: - return self._status - - def setStatus(self, status: int): - self._status = status - - def getStatusText(self) -> str: - return self._status_text - - def setStatusText(self, status_text: str): - self._status_text = status_text - - def getStructure(self) -> str: - return self._structure - - def setStructure(self, structure: str): - self._structure = structure - - def getZone(self) -> str: - return self._zone - - def setZone(self, zone: str): - self._zone = zone - - def isFavorite(self) -> bool: - return self._is_favorite - - def setFavorite(self, favorite: bool): - self._is_favorite = favorite - - def toString(self) -> str: - return (f"Control(id={self._control_id}, title={self._title}, " - f"type={self._device_type}, status={self._status})") - - def hashCode(self) -> int: - return hash(self._control_id) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, Control): - return False - return self._control_id == other._control_id - - -# ═══════════════════════════════════════════════════════════════ -# StatefulControl -# ═══════════════════════════════════════════════════════════════ - -class StatefulControl(Control): - """StatefulControl — 有状态控件""" - - def __init__(self, control_id: str, title: str, - subtitle: str, device_type: str): - super().__init__(control_id, title, subtitle, device_type) - self._current_value: Any = None - self._min_value: Optional[float] = None - self._max_value: Optional[float] = None - self._step: Optional[float] = None - self._unit: Optional[str] = None - self._is_on: bool = False - self._mode: str = "" - - def setStatus(self, status: int) -> "StatefulControl": - """设置状态""" - self._status = status - return self - - def setStatusText(self, status_text: str) -> "StatefulControl": - """设置状态文本""" - self._status_text = status_text - return self - - def getCurrentValue(self) -> Any: - return self._current_value - - def setCurrentValue(self, value: Any) -> "StatefulControl": - self._current_value = value - return self - - def getMinValue(self) -> Optional[float]: - return self._min_value - - def setMinValue(self, min_value: float) -> "StatefulControl": - self._min_value = min_value - return self - - def getMaxValue(self) -> Optional[float]: - return self._max_value - - def setMaxValue(self, max_value: float) -> "StatefulControl": - self._max_value = max_value - return self - - def getStep(self) -> Optional[float]: - return self._step - - def setStep(self, step: float) -> "StatefulControl": - self._step = step - return self - - def isOn(self) -> bool: - return self._is_on - - def setOn(self, is_on: bool) -> "StatefulControl": - self._is_on = is_on - return self - - def getMode(self) -> str: - return self._mode - - def setMode(self, mode: str) -> "StatefulControl": - self._mode = mode - return self - - def toString(self) -> str: - return (f"StatefulControl(id={self._control_id}, title={self._title}, " - f"type={self._device_type}, on={self._is_on}, " - f"value={self._current_value})") - - -# ═══════════════════════════════════════════════════════════════ -# StatelessControl -# ═══════════════════════════════════════════════════════════════ - -class StatelessControl(Control): - """StatelessControl — 无状态控件""" - - def __init__(self, control_id: str, title: str, - subtitle: str, device_type: str): - super().__init__(control_id, title, subtitle, device_type) - self._action_text: str = "" - - def getActionText(self) -> str: - return self._action_text - - def setActionText(self, action_text: str) -> "StatelessControl": - self._action_text = action_text - return self - - def toString(self) -> str: - return (f"StatelessControl(id={self._control_id}, " - f"title={self._title}, type={self._device_type})") - - -# ═══════════════════════════════════════════════════════════════ -# ControlsProviderService -# ═══════════════════════════════════════════════════════════════ - -class ControlsProviderService(Stub): - """ControlsProviderService — 控件提供者服务""" - - SERVICE_INTERFACE = "android.service.controls.ControlsProviderService" - - # 控件类型 - CONTROL_TYPE_TOGGLE = 1 - CONTROL_TYPE_RANGE = 2 - CONTROL_TYPE_STATELESS = 3 - CONTROL_TYPE_BUTTON = 4 - - def __init__(self): - super().__init__() - self._controls: Dict[str, Control] = {} - self._publishers: Dict[str, Any] = {} - self._subscribers: Dict[str, Any] = {} - - def onCreatePublisherFor(self, control_ids: List[str]) -> Any: - """为指定控件创建发布者""" - publisher = type("Publisher", (), { - "onUnsubscribe": lambda: None, - "onRequest": lambda cid: None, - })() - for cid in control_ids: - self._publishers[cid] = publisher - return publisher - - def onGetSubscriberFor(self, control_ids: List[str]) -> Any: - """获取控件订阅者""" - subscriber = type("Subscriber", (), { - "onSubscribe": lambda cid: None, - "onUnsubscribe": lambda cid: None, - })() - for cid in control_ids: - self._subscribers[cid] = subscriber - return subscriber - - def publishControls(self, control_ids: List[str], controls: List[Control]): - """发布控件状态""" - for control in controls: - self._controls[control.getControlId()] = control - - def addControl(self, control: Control): - """添加控件""" - self._controls[control.getControlId()] = control - - def removeControl(self, control_id: str): - """移除控件""" - self._controls.pop(control_id, None) - - def getControl(self, control_id: str) -> Optional[Control]: - return self._controls.get(control_id) - - def getControls(self) -> List[Control]: - return list(self._controls.values()) - - def toString(self) -> str: - return f"ControlsProviderService(controls={len(self._controls)})" - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "ControlsProviderService", - "Control", - "DeviceTypes", - "StatefulControl", - "StatelessControl", -] \ No newline at end of file diff --git a/android_pyapi/android/service_dreams.py b/android_pyapi/android/service_dreams.py deleted file mode 100644 index 0248fa2..0000000 --- a/android_pyapi/android/service_dreams.py +++ /dev/null @@ -1,140 +0,0 @@ -""" -Android Service Dreams 组件 — DreamService 等 -方法签名完全对齐 Android SDK (android.service.dreams 包) -""" - -from typing import Any, Callable, Dict, List, Optional - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# DreamService -# ═══════════════════════════════════════════════════════════════ - -class DreamService(Stub): - """DreamService — 屏保服务基类""" - - # 服务接口 - SERVICE_INTERFACE = "android.service.dreams.DreamService" - SERVICE_META_DATA = "android.service.dream" - - # 退出原因 - EXIT_CAUSE_UNKNOWN = 0 - EXIT_CAUSE_TIMEOUT = 1 - EXIT_CAUSE_USER = 2 - EXIT_CAUSE_DREAM_FINISHED = 3 - - def __init__(self): - super().__init__() - self._fullscreen: bool = False - self._interactive: bool = False - self._dreaming: bool = False - self._window: Optional[Any] = None - self._content_view: Optional[Any] = None - self._exit_cause: int = DreamService.EXIT_CAUSE_UNKNOWN - self._created: bool = False - self._screen_bright: bool = True - self._doze_component: Optional[Any] = None - self._doze_mode: bool = False - self._debug: bool = False - - def setContentView(self, view: Any): - """设置内容视图""" - self._content_view = view - - def getContentView(self) -> Optional[Any]: - return self._content_view - - def setFullscreen(self, fullscreen: bool): - """设置全屏模式""" - self._fullscreen = fullscreen - - def isFullscreen(self) -> bool: - """检查是否全屏""" - return self._fullscreen - - def isInteractive(self) -> bool: - """检查是否可交互""" - return self._interactive - - def setInteractive(self, interactive: bool): - """设置可交互""" - self._interactive = interactive - - def getWindow(self) -> Optional[Any]: - """获取窗口""" - return self._window - - def startDream(self): - """开始屏保""" - self._dreaming = True - - def awaken(self): - """唤醒/结束屏保""" - self._dreaming = False - - def onDreamingStarted(self): - """屏保开始时回调""" - self._dreaming = True - - def onDreamingStopped(self): - """屏保停止时回调""" - self._dreaming = False - - def onCreate(self): - """创建时回调""" - self._created = True - - def onDestroy(self): - """销毁时回调""" - self._created = False - self._dreaming = False - - def onAttachedToWindow(self): - """附加到窗口时回调""" - pass - - def onDetachedFromWindow(self): - """从窗口分离时回调""" - pass - - def isDreaming(self) -> bool: - """检查是否正在运行屏保""" - return self._dreaming - - def getExitCause(self) -> int: - return self._exit_cause - - def setExitCause(self, cause: int): - self._exit_cause = cause - - def onWakeUp(self): - """唤醒回调""" - self.awaken() - - def canDoze(self) -> bool: - return self._doze_mode - - def setDozeComponent(self, component: Any): - self._doze_component = component - - def toString(self) -> str: - return (f"DreamService(dreaming={self._dreaming}, " - f"fullscreen={self._fullscreen}, " - f"interactive={self._interactive})") - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "DreamService", -] \ No newline at end of file diff --git a/android_pyapi/android/service_media.py b/android_pyapi/android/service_media.py deleted file mode 100644 index a974e71..0000000 --- a/android_pyapi/android/service_media.py +++ /dev/null @@ -1,812 +0,0 @@ -""" -Android Media Service 组件 — MediaBrowserService, MediaSessionService, BrowserRoot, Result 等 -方法签名完全对齐 Android SDK (android.service.media 包) -""" - -from __future__ import annotations - -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# Result -# ═══════════════════════════════════════════════════════════════ - -class Result(Stub): - """Result — 异步操作结果""" - - FLAG_OPTION_NOT_KNOWN = 0 - FLAG_OPTION_FOUND = 1 - FLAG_OPTION_NOT_FOUND = 2 - FLAG_OPTION_ERROR = 4 - - def __init__(self, result_type: Any = None): - super().__init__() - self._result_type = result_type - self._result: Any = None - self._flags: int = Result.FLAG_OPTION_NOT_KNOWN - self._is_done: bool = False - self._is_sent: bool = False - self._error: Optional[str] = None - - def getResult(self) -> Any: - return self._result - - def setResult(self, result: Any): - self._result = result - - def getFlags(self) -> int: - return self._flags - - def setFlags(self, flags: int): - self._flags = flags - - def isDone(self) -> bool: - return self._is_done - - def isSent(self) -> bool: - return self._is_sent - - def getError(self) -> Optional[str]: - return self._error - - def sendResult(self, result: Any): - """发送结果""" - self._result = result - self._is_done = True - self._is_sent = True - - def sendError(self, error: str): - """发送错误""" - self._error = error - self._is_done = True - self._is_sent = True - self._flags = Result.FLAG_OPTION_ERROR - - def onResultSent(self, result: Any, flags: int = 0): - """结果已发送回调""" - self._result = result - self._flags = flags - self._is_done = True - - def toString(self) -> str: - return f"Result(done={self._is_done}, sent={self._is_sent})" - - -# ═══════════════════════════════════════════════════════════════ -# BrowserRoot -# ═══════════════════════════════════════════════════════════════ - -class BrowserRoot(Stub): - """BrowserRoot — 媒体浏览根节点""" - - EXTRA_RECENT = "android.service.media.extra.RECENT" - EXTRA_OFFLINE = "android.service.media.extra.OFFLINE" - EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED" - EXTRA_EMPTY_ROOT_ALLOWED = "android.service.media.extra.EMPTY_ROOT_ALLOWED" - EXTRA_RECYCLERVIEW = "android.service.media.extra.RECYCLERVIEW" - - def __init__(self, root_id: str = "", extras: Any = None): - super().__init__() - self._root_id = root_id - self._extras = extras - self._title: str = "" - self._is_visible: bool = True - self._is_playable: bool = False - self._is_browsable: bool = True - self._children: List[MediaBrowserService.BrowserRoot] = [] - self._icon_uri: str = "" - self._subtitle: str = "" - self._description: str = "" - - def getRootId(self) -> str: - return self._root_id - - def setRootId(self, root_id: str): - self._root_id = root_id - - def getExtras(self) -> Any: - return self._extras - - def setExtras(self, extras: Any): - self._extras = extras - - def getTitle(self) -> str: - return self._title - - def setTitle(self, title: str): - self._title = title - - def isVisible(self) -> bool: - return self._is_visible - - def setVisible(self, visible: bool): - self._is_visible = visible - - def isPlayable(self) -> bool: - return self._is_playable - - def setPlayable(self, playable: bool): - self._is_playable = playable - - def isBrowsable(self) -> bool: - return self._is_browsable - - def setBrowsable(self, browsable: bool): - self._is_browsable = browsable - - def getIconUri(self) -> str: - return self._icon_uri - - def setIconUri(self, uri: str): - self._icon_uri = uri - - def getSubtitle(self) -> str: - return self._subtitle - - def setSubtitle(self, subtitle: str): - self._subtitle = subtitle - - def getDescription(self) -> str: - return self._description - - def setDescription(self, desc: str): - self._description = desc - - def toString(self) -> str: - return f"BrowserRoot(rootId={self._root_id})" - - -# ═══════════════════════════════════════════════════════════════ -# MediaBrowserService -# ═══════════════════════════════════════════════════════════════ - -class MediaBrowserService(Stub): - """MediaBrowserService — 媒体浏览服务""" - - SERVICE_INTERFACE = "android.media.browse.MediaBrowserService" - SERVICE_META_DATA = "android.media.browse" - - KEY_MEDIA_ITEM = "media_item" - KEY_SEARCH_RESULTS = "search_results" - KEY_ERROR = "error" - KEY_MEDIA_ROOT = "media_root" - KEY_RECENT = "recent" - KEY_OFFLINE = "offline" - KEY_SUGGESTED = "suggested" - KEY_EMPTY_ROOT_ALLOWED = "empty_root_allowed" - KEY_PAGE = "page" - KEY_PAGE_SIZE = "page_size" - - # ═══════════════════════════════════════════════════════ - # BrowserRoot (inner class) - # ═══════════════════════════════════════════════════════ - - class BrowserRoot(Stub): - """MediaBrowserService.BrowserRoot — 浏览根节点""" - - EXTRA_RECENT = "android.service.media.extra.RECENT" - EXTRA_OFFLINE = "android.service.media.extra.OFFLINE" - EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED" - EXTRA_EMPTY_ROOT_ALLOWED = "android.service.media.extra.EMPTY_ROOT_ALLOWED" - - def __init__(self, root_id: str = "", extras: Any = None): - super().__init__() - self._root_id = root_id - self._extras = extras - - def getRootId(self) -> str: - return self._root_id - - def getExtras(self) -> Any: - return self._extras - - # ═══════════════════════════════════════════════════════ - # Result (inner class) - # ═══════════════════════════════════════════════════════ - - class Result(Stub): - """MediaBrowserService.Result — 服务结果""" - - FLAG_OPTION_NOT_KNOWN = 0 - FLAG_OPTION_FOUND = 1 - FLAG_OPTION_NOT_FOUND = 2 - FLAG_OPTION_ERROR = 4 - - def __init__(self): - super().__init__() - self._result: Any = None - self._flags: int = MediaBrowserService.Result.FLAG_OPTION_NOT_KNOWN - self._is_done: bool = False - self._error: Optional[str] = None - - def getResult(self) -> Any: - return self._result - - def getFlags(self) -> int: - return self._flags - - def isDone(self) -> bool: - return self._is_done - - def sendResult(self, result: Any, flags: int = 0): - self._result = result - self._flags = flags - self._is_done = True - - def sendError(self, error: str): - self._error = error - self._flags = MediaBrowserService.Result.FLAG_OPTION_ERROR - self._is_done = True - - def onResultSent(self, result: Any, flags: int = 0): - self._result = result - self._flags = flags - self._is_done = True - - def __init__(self): - super().__init__() - self._root: Optional[MediaBrowserService.BrowserRoot] = None - self._session: Optional[MediaSessionService.MediaSession] = None - self._is_connected: bool = False - self._media_items: Dict[str, Any] = {} - self._children_cache: Dict[str, List[Any]] = {} - self._search_results: Dict[str, List[Any]] = {} - self._package_name: str = "" - self._on_load_children_callbacks: Dict[str, Callable] = {} - self._on_load_item_callbacks: Dict[str, Callable] = {} - - def onGetRoot(self, client_package_name: str, client_uid: int, - root_hints: Any = None) -> Optional[BrowserRoot]: - """获取根节点回调""" - return self._root - - def onLoadChildren(self, parent_id: str, result: Result): - """加载子节点回调""" - children = self._children_cache.get(parent_id, []) - result.sendResult(children) - - def onLoadChildrenWithOptions(self, parent_id: str, result: Result, - options: Any = None): - """加载子节点 (带选项) 回调""" - self.onLoadChildren(parent_id, result) - - def onLoadItem(self, item_id: str, result: Result): - """加载媒体项回调""" - item = self._media_items.get(item_id) - if item: - result.sendResult(item) - else: - result.sendError(f"Item not found: {item_id}") - - def onSearch(self, query: str, extras: Any, result: Result): - """搜索回调""" - results = self._search_results.get(query, []) - result.sendResult(results) - - def onCustomAction(self, action: str, extras: Any, result: Result): - """自定义动作回调""" - result.sendResult(None) - - def setRoot(self, root: BrowserRoot): - self._root = root - - def getRoot(self) -> Optional[BrowserRoot]: - return self._root - - def setSession(self, session: MediaSessionService.MediaSession): - self._session = session - - def getSession(self) -> Optional[MediaSessionService.MediaSession]: - return self._session - - def setMediaItems(self, items: Dict[str, Any]): - self._media_items = dict(items) - - def addMediaItem(self, item_id: str, item: Any): - self._media_items[item_id] = item - - def setChildren(self, parent_id: str, children: List[Any]): - self._children_cache[parent_id] = list(children) - - def addChild(self, parent_id: str, child: Any): - if parent_id not in self._children_cache: - self._children_cache[parent_id] = [] - self._children_cache[parent_id].append(child) - - def setSearchResults(self, query: str, results: List[Any]): - self._search_results[query] = list(results) - - def isConnected(self) -> bool: - return self._is_connected - - def setConnected(self, connected: bool): - self._is_connected = connected - - def getPackageName(self) -> str: - return self._package_name - - def setPackageName(self, pkg: str): - self._package_name = pkg - - def notifyChildrenChanged(self, parent_id: str): - """通知子节点已变更""" - pass - - def notifyChildrenChangedWithOptions(self, parent_id: str, options: Any = None): - """通知子节点已变更 (带选项)""" - pass - - def toString(self) -> str: - return f"MediaBrowserService(root={self._root})" - - -# ═══════════════════════════════════════════════════════════════ -# MediaSessionService -# ═══════════════════════════════════════════════════════════════ - -class MediaSessionService(Stub): - """MediaSessionService — 媒体会话服务""" - - SERVICE_INTERFACE = "android.media.session.MediaSessionService" - SERVICE_META_DATA = "android.media.session" - - KEY_MEDIA_SESSION = "media_session" - KEY_MEDIA_SESSION_TOKEN = "media_session_token" - KEY_MEDIA_CONTROLLER = "media_controller" - KEY_PLAYBACK_STATE = "playback_state" - KEY_METADATA = "metadata" - KEY_QUEUE = "queue" - KEY_QUEUE_TITLE = "queue_title" - KEY_ACTIONS = "actions" - KEY_ERROR = "error" - KEY_EXTRAS = "extras" - KEY_RATING = "rating" - KEY_REPEAT_MODE = "repeat_mode" - KEY_SHUFFLE_MODE = "shuffle_mode" - - # ═══════════════════════════════════════════════════════ - # MediaSession - # ═══════════════════════════════════════════════════════ - - class MediaSession(Stub): - """MediaSessionService.MediaSession — 媒体会话""" - - FLAG_HANDLES_MEDIA_BUTTONS = 1 - FLAG_HANDLES_TRANSPORT_CONTROLS = 2 - FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 4 - - PLAYBACK_STATE_NONE = 0 - PLAYBACK_STATE_STOPPED = 1 - PLAYBACK_STATE_PAUSED = 2 - PLAYBACK_STATE_PLAYING = 3 - PLAYBACK_STATE_FAST_FORWARDING = 4 - PLAYBACK_STATE_REWINDING = 5 - PLAYBACK_STATE_BUFFERING = 6 - PLAYBACK_STATE_ERROR = 7 - PLAYBACK_STATE_SKIPPING_TO_PREVIOUS = 8 - PLAYBACK_STATE_SKIPPING_TO_NEXT = 9 - PLAYBACK_STATE_SKIPPING_TO_QUEUE_ITEM = 10 - PLAYBACK_STATE_CONNECTING = 11 - - REPEAT_MODE_NONE = 0 - REPEAT_MODE_ONE = 1 - REPEAT_MODE_ALL = 2 - REPEAT_MODE_GROUP = 3 - - SHUFFLE_MODE_NONE = 0 - SHUFFLE_MODE_ALL = 1 - SHUFFLE_MODE_GROUP = 2 - - ACTION_PLAY = 1 - ACTION_PAUSE = 2 - ACTION_PLAY_PAUSE = 4 - ACTION_STOP = 8 - ACTION_SEEK_TO = 16 - ACTION_FAST_FORWARD = 32 - ACTION_REWIND = 64 - ACTION_SKIP_TO_NEXT = 128 - ACTION_SKIP_TO_PREVIOUS = 256 - ACTION_SKIP_TO_QUEUE_ITEM = 512 - ACTION_SET_RATING = 1024 - ACTION_SET_REPEAT_MODE = 2048 - ACTION_SET_SHUFFLE_MODE = 4096 - ACTION_SET_CAPTIONING_ENABLED = 8192 - ACTION_PLAY_FROM_MEDIA_ID = 16384 - ACTION_PLAY_FROM_SEARCH = 32768 - ACTION_PLAY_FROM_URI = 65536 - ACTION_PREPARE = 131072 - ACTION_PREPARE_FROM_MEDIA_ID = 262144 - ACTION_PREPARE_FROM_SEARCH = 524288 - ACTION_PREPARE_FROM_URI = 1048576 - - class Callback(Stub): - """MediaSessionService.MediaSession.Callback — 会话回调""" - - def onPlay(self): - pass - - def onPause(self): - pass - - def onStop(self): - pass - - def onSeekTo(self, pos: int): - pass - - def onFastForward(self): - pass - - def onRewind(self): - pass - - def onSkipToNext(self): - pass - - def onSkipToPrevious(self): - pass - - def onSkipToQueueItem(self, id: int): - pass - - def onSetRating(self, rating: Any): - pass - - def onSetRepeatMode(self, repeat_mode: int): - pass - - def onSetShuffleMode(self, shuffle_mode: int): - pass - - def onPlayFromMediaId(self, media_id: str, extras: Any): - pass - - def onPlayFromSearch(self, query: str, extras: Any): - pass - - def onPlayFromUri(self, uri: str, extras: Any): - pass - - def onPrepare(self): - pass - - def onPrepareFromMediaId(self, media_id: str, extras: Any): - pass - - def onPrepareFromSearch(self, query: str, extras: Any): - pass - - def onPrepareFromUri(self, uri: str, extras: Any): - pass - - def onCommand(self, command: str, extras: Any, cb: Any): - pass - - def onCustomAction(self, action: str, extras: Any): - pass - - def onMediaButtonEvent(self, intent: Any) -> bool: - return False - - def __init__(self, tag: str = ""): - super().__init__() - self._tag = tag - self._callback: Optional[MediaSessionService.MediaSession.Callback] = None - self._playback_state: int = MediaSessionService.MediaSession.PLAYBACK_STATE_NONE - self._metadata: Any = None - self._queue: List[Any] = [] - self._queue_title: str = "" - self._actions: int = 0 - self._repeat_mode: int = MediaSessionService.MediaSession.REPEAT_MODE_NONE - self._shuffle_mode: int = MediaSessionService.MediaSession.SHUFFLE_MODE_NONE - self._active: bool = False - self._flags: int = 0 - self._position: int = 0 - self._duration: int = 0 - self._error: Optional[str] = None - self._extras: Dict[str, Any] = {} - - def setCallback(self, callback: Callback): - self._callback = callback - - def getCallback(self) -> Optional[Callback]: - return self._callback - - def setActive(self, active: bool): - self._active = active - - def isActive(self) -> bool: - return self._active - - def setFlags(self, flags: int): - self._flags = flags - - def getFlags(self) -> int: - return self._flags - - def setPlaybackState(self, state: int): - self._playback_state = state - - def getPlaybackState(self) -> int: - return self._playback_state - - def setMetadata(self, metadata: Any): - self._metadata = metadata - - def getMetadata(self) -> Any: - return self._metadata - - def setQueue(self, queue: List[Any]): - self._queue = list(queue) - - def getQueue(self) -> List[Any]: - return list(self._queue) - - def setQueueTitle(self, title: str): - self._queue_title = title - - def getQueueTitle(self) -> str: - return self._queue_title - - def setActions(self, actions: int): - self._actions = actions - - def getActions(self) -> int: - return self._actions - - def setRepeatMode(self, mode: int): - self._repeat_mode = mode - - def getRepeatMode(self) -> int: - return self._repeat_mode - - def setShuffleMode(self, mode: int): - self._shuffle_mode = mode - - def getShuffleMode(self) -> int: - return self._shuffle_mode - - def getTag(self) -> str: - return self._tag - - def setPosition(self, pos: int): - self._position = pos - - def getPosition(self) -> int: - return self._position - - def setDuration(self, duration: int): - self._duration = duration - - def getDuration(self) -> int: - return self._duration - - def setError(self, error: str): - self._error = error - - def getError(self) -> Optional[str]: - return self._error - - def getExtras(self) -> Dict[str, Any]: - return dict(self._extras) - - def setExtras(self, extras: Dict[str, Any]): - self._extras = dict(extras) - - def isPlaying(self) -> bool: - return self._playback_state == MediaSessionService.MediaSession.PLAYBACK_STATE_PLAYING - - def toString(self) -> str: - return f"MediaSession(tag={self._tag}, state={self._playback_state})" - - def __init__(self): - super().__init__() - self._sessions: Dict[str, MediaSessionService.MediaSession] = {} - self._browser_service: Optional[MediaBrowserService] = None - self._is_running: bool = False - - def onCreate(self): - """创建服务""" - self._is_running = True - - def onDestroy(self): - """销毁服务""" - self._is_running = False - for session in self._sessions.values(): - session.setActive(False) - self._sessions.clear() - - def onGetSession(self, controller_info: Any) -> Optional[MediaSession]: - """获取会话回调""" - if self._sessions: - return next(iter(self._sessions.values())) - return None - - def getSessions(self) -> List[MediaSession]: - return list(self._sessions.values()) - - def addSession(self, tag: str, session: MediaSession): - self._sessions[tag] = session - - def removeSession(self, tag: str): - self._sessions.pop(tag, None) - - def getSession(self, tag: str) -> Optional[MediaSession]: - return self._sessions.get(tag) - - def setBrowserService(self, service: MediaBrowserService): - self._browser_service = service - - def getBrowserService(self) -> Optional[MediaBrowserService]: - return self._browser_service - - def isRunning(self) -> bool: - return self._is_running - - def toString(self) -> str: - return f"MediaSessionService(sessions={len(self._sessions)})" - - -# ═══════════════════════════════════════════════════════════════ -# MediaSessionServiceCallback -# ═══════════════════════════════════════════════════════════════ - -class MediaSessionServiceCallback(Stub): - """MediaSessionServiceCallback — 媒体会话服务回调""" - - def onSessionCreated(self, session: MediaSessionService.MediaSession): - pass - - def onSessionDestroyed(self, session: MediaSessionService.MediaSession): - pass - - def onSessionActive(self, session: MediaSessionService.MediaSession): - pass - - def onSessionInactive(self, session: MediaSessionService.MediaSession): - pass - - def onPlaybackStateChanged(self, session: MediaSessionService.MediaSession, state: int): - pass - - def onMetadataChanged(self, session: MediaSessionService.MediaSession, metadata: Any): - pass - - -# ═══════════════════════════════════════════════════════════════ -# MediaSessionUtils -# ═══════════════════════════════════════════════════════════════ - -class MediaSessionUtils(Stub): - """MediaSessionUtils — 媒体会话工具类""" - - @staticmethod - def createMediaSession(tag: str) -> MediaSessionService.MediaSession: - return MediaSessionService.MediaSession(tag) - - @staticmethod - def createBrowserRoot(root_id: str, extras: Any = None) -> BrowserRoot: - return BrowserRoot(root_id, extras) - - @staticmethod - def createServiceResult() -> MediaBrowserService.Result: - return MediaBrowserService.Result() - - @staticmethod - def getPlaybackStateName(state: int) -> str: - names = { - MediaSessionService.MediaSession.PLAYBACK_STATE_NONE: "NONE", - MediaSessionService.MediaSession.PLAYBACK_STATE_STOPPED: "STOPPED", - MediaSessionService.MediaSession.PLAYBACK_STATE_PAUSED: "PAUSED", - MediaSessionService.MediaSession.PLAYBACK_STATE_PLAYING: "PLAYING", - MediaSessionService.MediaSession.PLAYBACK_STATE_FAST_FORWARDING: "FAST_FORWARDING", - MediaSessionService.MediaSession.PLAYBACK_STATE_REWINDING: "REWINDING", - MediaSessionService.MediaSession.PLAYBACK_STATE_BUFFERING: "BUFFERING", - MediaSessionService.MediaSession.PLAYBACK_STATE_ERROR: "ERROR", - } - return names.get(state, f"UNKNOWN({state})") - - @staticmethod - def isActiveSession(session: MediaSessionService.MediaSession) -> bool: - return session is not None and session.isActive() - - @staticmethod - def getSupportedActions(session: MediaSessionService.MediaSession) -> int: - return session.getActions() if session else 0 - - -# ═══════════════════════════════════════════════════════════════ -# MediaSessionServiceConfig -# ═══════════════════════════════════════════════════════════════ - -class MediaSessionServiceConfig(Stub): - """MediaSessionServiceConfig — 媒体会话服务配置""" - - def __init__(self): - super().__init__() - self._max_sessions: int = 10 - self._allow_multiple_sessions: bool = True - self._notification_enabled: bool = True - self._auto_play_on_start: bool = False - self._enable_media_buttons: bool = True - self._enable_queue: bool = True - - def getMaxSessions(self) -> int: - return self._max_sessions - - def setMaxSessions(self, max_sessions: int): - self._max_sessions = max_sessions - - def isAllowMultipleSessions(self) -> bool: - return self._allow_multiple_sessions - - def setAllowMultipleSessions(self, allow: bool): - self._allow_multiple_sessions = allow - - def isNotificationEnabled(self) -> bool: - return self._notification_enabled - - def setNotificationEnabled(self, enabled: bool): - self._notification_enabled = enabled - - def isAutoPlayOnStart(self) -> bool: - return self._auto_play_on_start - - def setAutoPlayOnStart(self, auto_play: bool): - self._auto_play_on_start = auto_play - - def isMediaButtonsEnabled(self) -> bool: - return self._enable_media_buttons - - def setMediaButtonsEnabled(self, enabled: bool): - self._enable_media_buttons = enabled - - def isQueueEnabled(self) -> bool: - return self._enable_queue - - def setQueueEnabled(self, enabled: bool): - self._enable_queue = enabled - - def toString(self) -> str: - return f"MediaSessionServiceConfig(maxSessions={self._max_sessions})" - - -# ═══════════════════════════════════════════════════════════════ -# MediaSessionServiceHelper -# ═══════════════════════════════════════════════════════════════ - -class MediaSessionServiceHelper(Stub): - """MediaSessionServiceHelper — 媒体会话服务辅助类""" - - @staticmethod - def requestMediaSessionService(context: Any, service: MediaSessionService): - """请求媒体会话服务""" - service.onCreate() - - @staticmethod - def releaseMediaSessionService(service: MediaSessionService): - """释放媒体会话服务""" - service.onDestroy() - - @staticmethod - def sendMediaButton(context: Any, session: MediaSessionService.MediaSession, - key_code: int): - """发送媒体按键""" - cb = session.getCallback() - if cb: - cb.onMediaButtonEvent(None) - - @staticmethod - def createDefaultSession(tag: str = "default") -> MediaSessionService.MediaSession: - session = MediaSessionService.MediaSession(tag) - session.setCallback(MediaSessionService.MediaSession.Callback()) - return session - - @staticmethod - def playbackStateToString(state: int) -> str: - return MediaSessionUtils.getPlaybackStateName(state) \ No newline at end of file diff --git a/android_pyapi/android/service_notification.py b/android_pyapi/android/service_notification.py deleted file mode 100644 index f397be8..0000000 --- a/android_pyapi/android/service_notification.py +++ /dev/null @@ -1,378 +0,0 @@ -""" -Android Service Notification 组件 — NotificationListenerService, StatusBarNotification, RankingMap 等 -方法签名完全对齐 Android SDK (android.service.notification 包) -""" - -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# StatusBarNotification -# ═══════════════════════════════════════════════════════════════ - -class StatusBarNotification(Stub): - """StatusBarNotification — 状态栏通知""" - - def __init__(self, pkg: str = "", op_pkg: str = "", - id: int = 0, tag: Optional[str] = None, - uid: int = 10001, pid: int = 1000, - user: Any = None, notification: Any = None, - post_time: int = 0): - super().__init__() - self._pkg = pkg - self._op_pkg = op_pkg or pkg - self._id = id - self._tag = tag - self._uid = uid - self._pid = pid - self._user = user - self._notification = notification - self._post_time = post_time or int(time.time() * 1000) - self._is_clearable = True - self._is_ongoing = False - self._key = f"{pkg}|{id}|{tag or ''}" - self._override_group_key: Optional[str] = None - self._override_public = False - self._initial_source_id = 0 - self._initial_source_uid = uid - self._initial_source_pkg = pkg - - def getPackageName(self) -> str: - """获取通知所属包名""" - return self._pkg - - def getOpPkg(self) -> str: - return self._op_pkg - - def getNotification(self) -> Any: - """获取 Notification 对象""" - return self._notification - - def getId(self) -> int: - """获取通知 ID""" - return self._id - - def getTag(self) -> Optional[str]: - """获取通知标签""" - return self._tag - - def getPostTime(self) -> int: - """获取发送时间 (毫秒)""" - return self._post_time - - def getUid(self) -> int: - return self._uid - - def getPid(self) -> int: - return self._pid - - def getUser(self) -> Any: - return self._user - - def getKey(self) -> str: - return self._key - - def isClearable(self) -> bool: - """是否可以清除""" - return self._is_clearable - - def isOngoing(self) -> bool: - """是否是持续通知""" - return self._is_ongoing - - def setClearable(self, clearable: bool): - self._is_clearable = clearable - - def setOngoing(self, ongoing: bool): - self._is_ongoing = ongoing - - def getOverrideGroupKey(self) -> Optional[str]: - return self._override_group_key - - def setOverrideGroupKey(self, override_group_key: Optional[str]): - self._override_group_key = override_group_key - - def isGroup(self) -> bool: - return self._override_group_key is not None - - def clone(self) -> "StatusBarNotification": - """克隆通知""" - clone = StatusBarNotification( - pkg=self._pkg, op_pkg=self._op_pkg, id=self._id, - tag=self._tag, uid=self._uid, pid=self._pid, - user=self._user, notification=self._notification, - post_time=self._post_time, - ) - clone._is_clearable = self._is_clearable - clone._is_ongoing = self._is_ongoing - clone._key = self._key - clone._override_group_key = self._override_group_key - return clone - - def toString(self) -> str: - return (f"StatusBarNotification(pkg={self._pkg}, id={self._id}, " - f"tag={self._tag}, postTime={self._post_time})") - - def hashCode(self) -> int: - return hash(self._key) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, StatusBarNotification): - return False - return self._key == other._key - - -# ═══════════════════════════════════════════════════════════════ -# NotificationListenerService -# ═══════════════════════════════════════════════════════════════ - -class NotificationListenerService(Stub): - """NotificationListenerService — 通知监听服务""" - - # 取消原因 - REASON_LISTENER_CANCEL = 1 - REASON_CLICK = 2 - REASON_CANCEL = 3 - REASON_DELETE = 4 - REASON_PACKAGE_CHANGED = 5 - REASON_USER_STOPPED = 6 - REASON_PACKACE_BANNED = 7 - REASON_APP_CANCEL = 8 - REASON_APP_CANCEL_ALL = 9 - REASON_CHANNEL_BANNED = 10 - REASON_SNOOZED = 11 - REASON_TIMEOUT = 12 - REASON_ERROR = 13 - REASON_GROUP_SUMMARY_CANCELED = 14 - REASON_ASSISTANT_CANCEL = 15 - - # 中断过滤器 - INTERRUPTION_FILTER_NONE = 0 - INTERRUPTION_FILTER_PRIORITY = 1 - INTERRUPTION_FILTER_ALARMS = 2 - INTERRUPTION_FILTER_ALL = 3 - INTERRUPTION_FILTER_UNKNOWN = 4 - - # 提示 - HINT_HOST_DISABLE_CALL_EFFECTS = 1 - HINT_HOST_DISABLE_EFFECTS = 2 - HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 3 - - # SUW 组件 - TRAMPOLINE_ACTIVITY_CLASS = ( - "com.android.settings.Settings$NotificationAppListActivity" - ) - - def __init__(self): - super().__init__() - self._active_notifications: Dict[str, StatusBarNotification] = {} - self._keys_list: List[str] = [] - self._interruption_filter: int = ( - NotificationListenerService.INTERRUPTION_FILTER_ALL - ) - self._ranking_map: Optional["NotificationListenerService.RankingMap"] = None - self._is_listener_bound = False - - @staticmethod - def requestRebind(component_name: Any): - """请求重新绑定通知监听器""" - pass - - @staticmethod - def requestUnbind(component_name: Any): - """请求解绑通知监听器""" - pass - - def onNotificationPosted(self, sbn: StatusBarNotification, - ranking_map: Optional["RankingMap"] = None): - """通知被发送时回调""" - if sbn: - self._active_notifications[sbn.getKey()] = sbn - self._keys_list = list(self._active_notifications.keys()) - if ranking_map: - self._ranking_map = ranking_map - - def onNotificationRemoved(self, sbn: StatusBarNotification, - ranking_map: Optional["RankingMap"] = None, - reason: int = REASON_LISTENER_CANCEL): - """通知被移除时回调""" - if sbn: - key = sbn.getKey() - self._active_notifications.pop(key, None) - if key in self._keys_list: - self._keys_list.remove(key) - - def onListenerConnected(self): - """监听器已连接""" - self._is_listener_bound = True - - def onListenerDisconnected(self): - """监听器已断开""" - self._is_listener_bound = False - - def onNotificationRemovedReason(self, sbn: StatusBarNotification, - ranking_map: "RankingMap", - reason: int): - """通知被移除 (带原因)""" - self.onNotificationRemoved(sbn, ranking_map, reason) - - def onNotificationPostedWithRankingMap(self, sbn: StatusBarNotification, - ranking_map: "RankingMap"): - """通知被发送 (带排名映射)""" - self.onNotificationPosted(sbn, ranking_map) - - def getActiveNotifications(self) -> List[StatusBarNotification]: - """获取活动通知列表""" - return list(self._active_notifications.values()) - - def getActiveNotificationsWithRankingMap(self) -> Tuple[ - List[StatusBarNotification], - Optional["RankingMap"]]: - """获取活动通知及排名映射""" - return list(self._active_notifications.values()), self._ranking_map - - def getCurrentInterruptionFilter(self) -> int: - """获取当前中断过滤器""" - return self._interruption_filter - - def setInterruptionFilter(self, interruption_filter: int): - """设置中断过滤器""" - self._interruption_filter = interruption_filter - - def cancelNotification(self, key: str): - """取消指定通知""" - self._active_notifications.pop(key, None) - if key in self._keys_list: - self._keys_list.remove(key) - - def cancelAllNotifications(self): - """取消所有通知""" - self._active_notifications.clear() - self._keys_list.clear() - - def requestRebind(self, component_name: Any): - """请求重新绑定""" - pass - - def requestUnbind(self): - """请求解绑""" - pass - - def registerAsSystemService(self, context: Any, component_name: Any, - user: int): - pass - - def unregisterAsSystemService(self): - pass - - def isBound(self) -> bool: - return self._is_listener_bound - - def toString(self) -> str: - return (f"NotificationListenerService(" - f"notifications={len(self._active_notifications)}, " - f"filter={self._interruption_filter})") - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - # ── RankingMap ── - - class RankingMap(Stub): - """RankingMap — 通知排名映射""" - - def __init__(self): - super().__init__() - self._rankings: Dict[str, "NotificationListenerService.Ranking"] = {} - - def getOrderedKeys(self) -> List[str]: - """获取有序键列表""" - return list(self._rankings.keys()) - - def getRanking(self, key: str) -> Optional["NotificationListenerService.Ranking"]: - """获取指定通知的排名信息""" - return self._rankings.get(key) - - def getRawRanking(self, key: str) -> Optional[Any]: - return self._rankings.get(key) - - def toString(self) -> str: - return f"RankingMap(keys={len(self._rankings)})" - - def hashCode(self) -> int: - return hash(tuple(self._rankings.keys())) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, NotificationListenerService.RankingMap): - return False - return self._rankings == other._rankings - - class Ranking(Stub): - """Ranking — 通知排名""" - - def __init__(self): - super().__init__() - self._key: str = "" - self._rank: int = 0 - self._is_ambient: bool = False - self._matches_interruption_filter: bool = True - self._suppressed_visual_effects: int = 0 - self._importance: int = 3 # IMPORTANCE_DEFAULT - self._importance_explanation: Optional[str] = None - self._group_key: Optional[str] = None - self._override_group_key: Optional[str] = None - self._channel: Optional[Any] = None - self._is_suspended: bool = False - - def getKey(self) -> str: - return self._key - - def getRank(self) -> int: - return self._rank - - def isAmbient(self) -> bool: - return self._is_ambient - - def matchesInterruptionFilter(self) -> bool: - return self._matches_interruption_filter - - def getSuppressedVisualEffects(self) -> int: - return self._suppressed_visual_effects - - def getImportance(self) -> int: - return self._importance - - def getImportanceExplanation(self) -> Optional[str]: - return self._importance_explanation - - def getGroupKey(self) -> Optional[str]: - return self._group_key - - def getOverrideGroupKey(self) -> Optional[str]: - return self._override_group_key - - def getChannel(self) -> Optional[Any]: - return self._channel - - def isSuspended(self) -> bool: - return self._is_suspended - - def toString(self) -> str: - return (f"Ranking(key={self._key}, rank={self._rank}, " - f"importance={self._importance})") - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "NotificationListenerService", - "StatusBarNotification", -] \ No newline at end of file diff --git a/android_pyapi/android/service_quicksettings.py b/android_pyapi/android/service_quicksettings.py deleted file mode 100644 index eeccab8..0000000 --- a/android_pyapi/android/service_quicksettings.py +++ /dev/null @@ -1,214 +0,0 @@ -""" -Android Service QuickSettings 组件 — TileService, Tile 等 -方法签名完全对齐 Android SDK (android.service.quicksettings 包) -""" - -import time -from typing import Any, Callable, Dict, List, Optional - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# Tile -# ═══════════════════════════════════════════════════════════════ - -class Tile(Stub): - """Tile — 快捷设置磁贴""" - - # 状态常量 - STATE_ACTIVE = 1 - STATE_INACTIVE = 2 - STATE_UNAVAILABLE = 0 - - def __init__(self): - super().__init__() - self._state: int = Tile.STATE_INACTIVE - self._label: str = "" - self._subtitle: str = "" - self._content_description: str = "" - self._icon: Optional[Any] = None - self._icon_res_id: int = 0 - self._meta_data: Optional[Dict[str, Any]] = None - self._last_updated: int = 0 - - def getState(self) -> int: - """获取磁贴状态""" - return self._state - - def setState(self, state: int): - """设置磁贴状态""" - self._state = state - self._last_updated = int(time.time() * 1000) - - def getLabel(self) -> str: - """获取标签""" - return self._label - - def setLabel(self, label: str): - """设置标签""" - self._label = label - self._last_updated = int(time.time() * 1000) - - def getSubtitle(self) -> str: - """获取副标题""" - return self._subtitle - - def setSubtitle(self, subtitle: str): - """设置副标题""" - self._subtitle = subtitle - self._last_updated = int(time.time() * 1000) - - def getContentDescription(self) -> str: - """获取内容描述""" - return self._content_description - - def setContentDescription(self, content_description: str): - """设置内容描述""" - self._content_description = content_description - - def getIcon(self) -> Optional[Any]: - """获取图标""" - return self._icon - - def setIcon(self, icon: Any): - """设置图标""" - self._icon = icon - self._last_updated = int(time.time() * 1000) - - def updateTile(self): - """更新磁贴 (通知系统刷新)""" - self._last_updated = int(time.time() * 1000) - - def getMetaData(self) -> Optional[Dict[str, Any]]: - return self._meta_data - - def setMetaData(self, meta_data: Dict[str, Any]): - self._meta_data = meta_data - - def toString(self) -> str: - state_names = { - Tile.STATE_ACTIVE: "ACTIVE", - Tile.STATE_INACTIVE: "INACTIVE", - Tile.STATE_UNAVAILABLE: "UNAVAILABLE", - } - return (f"Tile(label={self._label}, state=" - f"{state_names.get(self._state, self._state)})") - - def hashCode(self) -> int: - return hash((self._label, self._state)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, Tile): - return False - return (self._label == other._label and self._state == other._state) - - -# ═══════════════════════════════════════════════════════════════ -# TileService -# ═══════════════════════════════════════════════════════════════ - -class TileService(Stub): - """TileService — 快捷设置磁贴服务""" - - # 动作 - ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE" - ACTION_REQUEST_LISTENING = ( - "android.service.quicksettings.action.REQUEST_LISTENING" - ) - META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE" - - def __init__(self): - super().__init__() - self._qs_tile: Optional[Tile] = None - self._locked: bool = False - self._secure: bool = False - self._listening: bool = False - self._tile_added: bool = False - self._tile_removed: bool = False - - @staticmethod - def requestListeningState(context: Any, component: Any): - """请求监听状态 (Android-only)""" - pass - - def isLocked(self) -> bool: - """检查设备是否锁定""" - return self._locked - - def isSecure(self) -> bool: - """检查设备是否安全锁定""" - return self._secure - - def startActivity(self, intent: Any): - """启动 Activity (Android-only)""" - raise AndroidOnlyError( - "TileService.startActivity() 仅在 Android 设备上可用" - ) - - def showDialog(self, dialog: Any): - """显示对话框""" - pass - - def getQsTile(self) -> Optional[Tile]: - """获取快速设置磁贴""" - return self._qs_tile - - def setQsTile(self, tile: Tile): - self._qs_tile = tile - - def unlockAndRun(self, runnable: Callable): - """解锁并运行""" - runnable() - - def onTileAdded(self): - """磁贴被添加时回调""" - self._tile_added = True - - def onTileRemoved(self): - """磁贴被移除时回调""" - self._tile_removed = True - - def onStartListening(self): - """开始监听时回调""" - self._listening = True - - def onStopListening(self): - """停止监听时回调""" - self._listening = False - - def onClick(self): - """被点击时回调""" - if self._qs_tile: - current_state = self._qs_tile.getState() - if current_state == Tile.STATE_ACTIVE: - self._qs_tile.setState(Tile.STATE_INACTIVE) - elif current_state == Tile.STATE_INACTIVE: - self._qs_tile.setState(Tile.STATE_ACTIVE) - - def onDestroy(self): - """销毁时回调""" - pass - - def isListening(self) -> bool: - return self._listening - - def toString(self) -> str: - return (f"TileService(locked={self._locked}, secure={self._secure}, " - f"listening={self._listening})") - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "TileService", - "Tile", -] \ No newline at end of file diff --git a/android_pyapi/android/service_voice.py b/android_pyapi/android/service_voice.py deleted file mode 100644 index d6d2cc3..0000000 --- a/android_pyapi/android/service_voice.py +++ /dev/null @@ -1,869 +0,0 @@ -""" -Android Service Voice 组件 — VoiceInteractionService, VoiceInteractionSession, VoiceInteractor 等 -方法签名完全对齐 Android SDK (android.service.voice 包) -""" - -from __future__ import annotations - -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# 常量定义 -# ═══════════════════════════════════════════════════════════════ - -KEY_ACTION = "action" -KEY_ACTION_ARG = "action_arg" -KEY_ARGUMENTS = "arguments" -KEY_CONFIDENCE_SCORE = "confidence_score" -KEY_ERROR = "error" -KEY_FEATURE_ID = "feature_id" -KEY_ID = "id" -KEY_LANGUAGE = "language" -KEY_LOCALE = "locale" -KEY_MESSAGE = "message" -KEY_PACKAGE_NAME = "package_name" -KEY_POWER_SOURCE = "power_source" -KEY_PROMPT = "prompt" -KEY_RESULT = "result" -KEY_SESSION_ID = "session_id" -KEY_STATUS = "status" -KEY_TIMEOUT_MS = "timeout_ms" -KEY_VOLUME = "volume" -KEY_WAKE_WORD = "wake_word" -KEY_IS_DEMO = "is_demo" -KEY_IS_TRUSTED = "is_trusted" -KEY_IS_VALID = "is_valid" -KEY_RECOGNITION_RESULT = "recognition_result" - -SHOW_SOURCE_FULL_SCREEN = 0 -SHOW_SOURCE_DOCKED = 1 -SHOW_SOURCE_PEEK = 2 -SHOW_SOURCE_ASSISTANT = 3 -SHOW_SOURCE_VOICE_SEARCH = 4 -SHOW_SOURCE_SHORTCUT = 5 -SHOW_SOURCE_NOTIFICATION = 6 -SHOW_SOURCE_WIDGET = 7 - -VOICE_INTERACTION_ACTION = "android.service.voice.action.VOICE_INTERACTION" -VOICE_INTERACTION_META_DATA = "android.service.voice.voice_interaction" -VOICE_INTERACTION_SERVICE = "android.service.voice.VoiceInteractionService" - -SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService" -SESSION_SERVICE_INTERFACE = "android.service.voice.VoiceInteractionSessionService" - -FLAG_SHOW_WITH_SCREEN_ON = 1 -FLAG_SHOW_WITH_SCREEN_OFF = 2 -FLAG_SHOW_WHEN_LOCKED = 4 -FLAG_SHOW_AS_SYSTEM_OVERLAY = 8 -FLAG_EXCLUSIVE = 16 - -VOICE_ACTIVITY_STATE_UNKNOWN = 0 -VOICE_ACTIVITY_STATE_LISTENING = 1 -VOICE_ACTIVITY_STATE_PROCESSING = 2 -VOICE_ACTIVITY_STATE_RESPONDING = 3 -VOICE_ACTIVITY_STATE_INACTIVE = 4 -VOICE_ACTIVITY_STATE_ERROR = 5 - - -# ═══════════════════════════════════════════════════════════════ -# VoiceInteractionService -# ═══════════════════════════════════════════════════════════════ - -class VoiceInteractionService(Stub): - """VoiceInteractionService — 语音交互服务""" - - def __init__(self): - super().__init__() - self._is_connected: bool = False - self._session: Optional[VoiceInteractionSession] = None - self._available_voice_actions: List[str] = [] - self._supported_languages: List[str] = [] - self._active_session: bool = False - self._disabled_until: int = 0 - self._feature_id: str = "" - self._package_name: str = "" - - def onReady(self): - """服务就绪回调""" - self._is_connected = True - - def onConnected(self): - """已连接回调""" - self._is_connected = True - - def onDisconnected(self): - """断开连接回调""" - self._is_connected = False - - def onShutdown(self): - """关闭回调""" - self._is_connected = False - self._session = None - - def setAvailableVoiceActions(self, actions: List[str]): - """设置可用语音动作""" - self._available_voice_actions = list(actions) - - def getAvailableVoiceActions(self) -> List[str]: - return list(self._available_voice_actions) - - def setSupportedLanguages(self, languages: List[str]): - self._supported_languages = list(languages) - - def getSupportedLanguages(self) -> List[str]: - return list(self._supported_languages) - - def isConnected(self) -> bool: - return self._is_connected - - def getActiveSession(self) -> Optional[VoiceInteractionSession]: - return self._session - - def setActiveSession(self, session: Optional[VoiceInteractionSession]): - self._session = session - self._active_session = session is not None - - def isActiveSession(self) -> bool: - return self._active_session - - def showSession(self, args: Dict[str, Any] = None, flags: int = 0): - """显示语音会话""" - if self._session: - self._session.show(args or {}, flags) - - def finish(self): - """结束服务""" - self._is_connected = False - if self._session: - self._session.finish() - self._session = None - - def createSession(self, service_context: Any = None, - session_id: int = 0) -> VoiceInteractionSession: - """创建语音交互会话""" - session = VoiceInteractionSession(service_context) - session.setSessionId(session_id) - self._session = session - return session - - def getFeatureId(self) -> str: - return self._feature_id - - def setFeatureId(self, feature_id: str): - self._feature_id = feature_id - - def getPackageName(self) -> str: - return self._package_name - - def setPackageName(self, pkg: str): - self._package_name = pkg - - def isDisabled(self) -> bool: - return self._disabled_until > int(time.time() * 1000) - - def toString(self) -> str: - return f"VoiceInteractionService(connected={self._is_connected})" - - -# ═══════════════════════════════════════════════════════════════ -# VoiceInteractionSession -# ═══════════════════════════════════════════════════════════════ - -class VoiceInteractionSession(Stub): - """VoiceInteractionSession — 语音交互会话""" - - STATE_UNKNOWN = 0 - STATE_LISTENING = 1 - STATE_PROCESSING = 2 - STATE_RESPONDING = 3 - STATE_INACTIVE = 4 - STATE_ERROR = 5 - STATE_FINISHED = 6 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._session_id: int = 0 - self._state: int = VoiceInteractionSession.STATE_UNKNOWN - self._interactor: Optional[VoiceInteractor] = None - self._current_arguments: Dict[str, Any] = {} - self._is_active: bool = False - self._is_finished: bool = False - self._prompt: str = "" - self._confidence_score: float = 0.0 - self._recognition_result: Any = None - self._current_action: str = "" - - def setSessionId(self, session_id: int): - self._session_id = session_id - - def getSessionId(self) -> int: - return self._session_id - - def getState(self) -> int: - return self._state - - def setState(self, state: int): - self._state = state - - def getInteractor(self) -> Optional[VoiceInteractor]: - return self._interactor - - def setInteractor(self, interactor: VoiceInteractor): - self._interactor = interactor - - def show(self, args: Dict[str, Any], flags: int): - """显示会话""" - self._current_arguments = args - self._is_active = True - self._state = VoiceInteractionSession.STATE_LISTENING - self.onShow(args, flags) - - def hide(self): - """隐藏会话""" - self._is_active = False - self._state = VoiceInteractionSession.STATE_INACTIVE - self.onHide() - - def finish(self): - """结束会话""" - self._is_finished = True - self._state = VoiceInteractionSession.STATE_FINISHED - self._is_active = False - self.onFinish() - - def setPrompt(self, prompt: str): - self._prompt = prompt - - def getPrompt(self) -> str: - return self._prompt - - def setConfidenceScore(self, score: float): - self._confidence_score = score - - def getConfidenceScore(self) -> float: - return self._confidence_score - - def setRecognitionResult(self, result: Any): - self._recognition_result = result - - def getRecognitionResult(self) -> Any: - return self._recognition_result - - def setCurrentAction(self, action: str): - self._current_action = action - - def getCurrentAction(self) -> str: - return self._current_action - - def isActive(self) -> bool: - return self._is_active - - def isFinished(self) -> bool: - return self._is_finished - - def startListening(self, prompt: str = ""): - """开始监听""" - self._state = VoiceInteractionSession.STATE_LISTENING - self._prompt = prompt - - def stopListening(self): - """停止监听""" - if self._state == VoiceInteractionSession.STATE_LISTENING: - self._state = VoiceInteractionSession.STATE_PROCESSING - - def processVoiceInput(self, input_data: Any): - """处理语音输入""" - self._state = VoiceInteractionSession.STATE_PROCESSING - self._recognition_result = input_data - - def respond(self, response: Any): - """响应""" - self._state = VoiceInteractionSession.STATE_RESPONDING - - def onShow(self, args: Dict[str, Any], flags: int): - """会话显示回调""" - pass - - def onHide(self): - """会话隐藏回调""" - pass - - def onFinish(self): - """会话结束回调""" - pass - - def onListeningStarted(self, prompt: str): - """开始监听回调""" - pass - - def onListeningFinished(self): - """监听结束回调""" - pass - - def onRecognitionResult(self, result: Any): - """识别结果回调""" - pass - - def onConfidenceScoreChanged(self, score: float): - """置信度变化回调""" - pass - - def onRequestConfirmation(self, prompt: Any): - """请求确认回调""" - if self._interactor: - self._interactor.onRequestConfirmation(prompt) - - def onRequestSelect(self, prompt: Any, options: List[Any]): - """请求选择回调""" - if self._interactor: - self._interactor.onRequestSelect(prompt, options) - - def onRequestComplete(self, result: Any): - """请求完成回调""" - if self._interactor: - self._interactor.onRequestComplete(result) - - def onRequestError(self, error: Any): - """请求错误回调""" - if self._interactor: - self._interactor.onRequestError(error) - - def toString(self) -> str: - return f"VoiceInteractionSession(id={self._session_id}, state={self._state})" - - -# ═══════════════════════════════════════════════════════════════ -# VoiceInteractionManager -# ═══════════════════════════════════════════════════════════════ - -class VoiceInteractionManager(Stub): - """VoiceInteractionManager — 语音交互管理器""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._active_service: Optional[VoiceInteractionService] = None - self._supported_actions: List[str] = [] - self._is_available: bool = True - - @staticmethod - def getInstance(context: Any) -> Optional[VoiceInteractionManager]: - return VoiceInteractionManager(context) - - def getActiveService(self) -> Optional[VoiceInteractionService]: - return self._active_service - - def setActiveService(self, service: VoiceInteractionService): - self._active_service = service - - def isAvailable(self) -> bool: - return self._is_available - - def setAvailable(self, available: bool): - self._is_available = available - - def getSupportedActions(self) -> List[str]: - return list(self._supported_actions) - - def setSupportedActions(self, actions: List[str]): - self._supported_actions = list(actions) - - def startVoiceActivity(self, voice_session: VoiceInteractionSession): - """启动语音活动""" - voice_session.setState(VoiceInteractionSession.STATE_LISTENING) - - def setVoiceActivityState(self, state: int): - """设置语音活动状态""" - pass - - def showSession(self, service: VoiceInteractionService, args: Dict[str, Any], - flags: int): - """显示会话""" - service.showSession(args, flags) - - def hideSession(self, service: VoiceInteractionService): - """隐藏会话""" - session = service.getActiveSession() - if session: - session.hide() - - def finishSession(self, service: VoiceInteractionService): - """结束会话""" - session = service.getActiveSession() - if session: - session.finish() - - def getSessionToken(self) -> Any: - return None - - def toString(self) -> str: - return f"VoiceInteractionManager(available={self._is_available})" - - -# ═══════════════════════════════════════════════════════════════ -# VoiceInteractor -# ═══════════════════════════════════════════════════════════════ - -class VoiceInteractor(Stub): - """VoiceInteractor — 语音交互器""" - - REQUEST_CONFIRMATION = "confirmation" - REQUEST_SELECT = "select" - REQUEST_COMPLETE = "complete" - REQUEST_ERROR = "error" - REQUEST_ABORT = "abort" - REQUEST_COMMAND = "command" - - def __init__(self): - super().__init__() - self._requests: List[VoiceInteractor.Request] = [] - self._active_request: Optional[VoiceInteractor.Request] = None - self._is_available: bool = True - - def submitRequest(self, request: VoiceInteractor.Request): - """提交请求""" - self._requests.append(request) - self._active_request = request - - def cancelRequest(self, request: VoiceInteractor.Request): - """取消请求""" - self._requests = [r for r in self._requests if r is not request] - if self._active_request is request: - self._active_request = None - - def getActiveRequest(self) -> Optional[VoiceInteractor.Request]: - return self._active_request - - def getPendingRequests(self) -> List[VoiceInteractor.Request]: - return list(self._requests) - - def isAvailable(self) -> bool: - return self._is_available - - def setAvailable(self, available: bool): - self._is_available = available - - def onRequestConfirmation(self, prompt: Any): - """确认请求回调""" - request = VoiceInteractor.ConfirmationRequest(prompt) - self.submitRequest(request) - - def onRequestSelect(self, prompt: Any, options: List[Any]): - """选择请求回调""" - request = VoiceInteractor.SelectRequest(prompt, options) - self.submitRequest(request) - - def onRequestComplete(self, result: Any): - """完成请求回调""" - request = VoiceInteractor.CompleteRequest(result) - self.submitRequest(request) - - def onRequestError(self, error: Any): - """错误请求回调""" - request = VoiceInteractor.ErrorRequest(error) - self.submitRequest(request) - - def destroy(self): - """销毁""" - self._requests.clear() - self._active_request = None - - def toString(self) -> str: - return f"VoiceInteractor(requests={len(self._requests)})" - - # ═══════════════════════════════════════════════════════ - # VoiceInteractor.Request 基类 - # ═══════════════════════════════════════════════════════ - - class Request(Stub): - """VoiceInteractor.Request — 请求基类""" - - def __init__(self, request_type: str = ""): - super().__init__() - self._request_type = request_type - self._is_active: bool = True - self._timeout_ms: int = 30000 - self._created_at: int = int(time.time() * 1000) - self._result: Any = None - - def getRequestType(self) -> str: - return self._request_type - - def isActive(self) -> bool: - return self._is_active - - def setActive(self, active: bool): - self._is_active = active - - def getTimeoutMs(self) -> int: - return self._timeout_ms - - def setTimeoutMs(self, timeout: int): - self._timeout_ms = timeout - - def getCreatedAt(self) -> int: - return self._created_at - - def getResult(self) -> Any: - return self._result - - def setResult(self, result: Any): - self._result = result - - def isExpired(self) -> bool: - return (int(time.time() * 1000) - self._created_at) > self._timeout_ms - - def onCancelled(self): - self._is_active = False - - # ═══════════════════════════════════════════════════════ - # ConfirmationRequest - # ═══════════════════════════════════════════════════════ - - class ConfirmationRequest(Request): - """VoiceInteractor.ConfirmationRequest — 确认请求""" - - def __init__(self, prompt: Any = None, args: Dict[str, Any] = None): - super().__init__(VoiceInteractor.REQUEST_CONFIRMATION) - self._prompt = prompt - self._args = args or {} - - def getPrompt(self) -> Any: - return self._prompt - - def getArgs(self) -> Dict[str, Any]: - return dict(self._args) - - def onConfirmationResult(self, confirmed: bool, result: Any = None): - """确认结果回调""" - self.setResult({"confirmed": confirmed, "data": result}) - - # ═══════════════════════════════════════════════════════ - # SelectRequest - # ═══════════════════════════════════════════════════════ - - class SelectRequest(Request): - """VoiceInteractor.SelectRequest — 选择请求""" - - def __init__(self, prompt: Any = None, options: List[Any] = None): - super().__init__(VoiceInteractor.REQUEST_SELECT) - self._prompt = prompt - self._options = options or [] - self._selected_index: int = -1 - - def getPrompt(self) -> Any: - return self._prompt - - def getOptions(self) -> List[Any]: - return list(self._options) - - def getSelectedIndex(self) -> int: - return self._selected_index - - def onSelectResult(self, index: int, result: Any = None): - """选择结果回调""" - self._selected_index = index - self.setResult({"index": index, "data": result}) - - # ═══════════════════════════════════════════════════════ - # CompleteRequest - # ═══════════════════════════════════════════════════════ - - class CompleteRequest(Request): - """VoiceInteractor.CompleteRequest — 完成请求""" - - def __init__(self, result: Any = None): - super().__init__(VoiceInteractor.REQUEST_COMPLETE) - self.setResult(result) - - def onCompleteResult(self, result: Any = None): - """完成结果回调""" - self.setResult(result) - - # ═══════════════════════════════════════════════════════ - # ErrorRequest - # ═══════════════════════════════════════════════════════ - - class ErrorRequest(Request): - """VoiceInteractor.ErrorRequest — 错误请求""" - - def __init__(self, error: Any = None): - super().__init__(VoiceInteractor.REQUEST_ERROR) - self._error = error - - def getError(self) -> Any: - return self._error - - def onErrorResult(self, error: Any = None): - """错误结果回调""" - self.setResult({"error": error or self._error}) - - -# ═══════════════════════════════════════════════════════════════ -# VoiceInteractionSessionService -# ═══════════════════════════════════════════════════════════════ - -class VoiceInteractionSessionService(Stub): - """VoiceInteractionSessionService — 语音交互会话服务""" - - SERVICE_INTERFACE = "android.service.voice.VoiceInteractionSessionService" - SERVICE_META_DATA = "android.voice_interaction" - - def __init__(self): - super().__init__() - self._service: Optional[VoiceInteractionService] = None - self._current_session: Optional[VoiceInteractionSession] = None - self._session_count: int = 0 - self._is_running: bool = False - - def onNewSession(self, args: Dict[str, Any]) -> VoiceInteractionSession: - """创建新会话""" - self._session_count += 1 - session = VoiceInteractionSession() - session.setSessionId(self._session_count) - session.show(args, 0) - self._current_session = session - return session - - def onSessionFinished(self, session: VoiceInteractionSession): - """会话结束回调""" - if self._current_session is session: - self._current_session = None - session.finish() - - def setVoiceInteractionService(self, service: VoiceInteractionService): - self._service = service - - def getVoiceInteractionService(self) -> Optional[VoiceInteractionService]: - return self._service - - def getCurrentSession(self) -> Optional[VoiceInteractionSession]: - return self._current_session - - def isRunning(self) -> bool: - return self._is_running - - def setRunning(self, running: bool): - self._is_running = running - - def onDestroy(self): - """销毁""" - if self._current_session: - self._current_session.finish() - self._current_session = None - self._is_running = False - - def toString(self) -> str: - return f"VoiceInteractionSessionService(running={self._is_running})" - - -# ═══════════════════════════════════════════════════════════════ -# VoiceInteractionSessionCallback -# ═══════════════════════════════════════════════════════════════ - -class VoiceInteractionSessionCallback(Stub): - """VoiceInteractionSessionCallback — 语音交互会话回调""" - - def onSessionStarted(self, session: VoiceInteractionSession): - pass - - def onSessionFinished(self, session: VoiceInteractionSession): - pass - - def onSessionHidden(self, session: VoiceInteractionSession): - pass - - def onSessionShown(self, session: VoiceInteractionSession): - pass - - def onResultChanged(self, session: VoiceInteractionSession, result: Any): - pass - - -# ═══════════════════════════════════════════════════════════════ -# VoiceInteractionUtils -# ═══════════════════════════════════════════════════════════════ - -class VoiceInteractionUtils(Stub): - """VoiceInteractionUtils — 语音交互工具类""" - - @staticmethod - def isVoiceInteractionAvailable(context: Any) -> bool: - return True - - @staticmethod - def getActiveServiceComponentName(context: Any) -> Any: - return None - - @staticmethod - def getSupportedVoiceActions(context: Any) -> List[str]: - return [] - - @staticmethod - def setVoiceActionEnabled(context: Any, action: str, enabled: bool): - pass - - @staticmethod - def requestVoiceInteractionService(context: Any, service: VoiceInteractionService): - pass - - @staticmethod - def createVoiceInteractor(context: Any) -> VoiceInteractor: - return VoiceInteractor() - - @staticmethod - def getVoiceInteractionServiceInfo(context: Any) -> Any: - return None - - @staticmethod - def isVoiceInteractionService(context: Any, pkg: str) -> bool: - return True - - -# ═══════════════════════════════════════════════════════════════ -# Keyphrase (hotword detection helpers) -# ═══════════════════════════════════════════════════════════════ - -class Keyphrase(Stub): - """Keyphrase — 唤醒词/关键词""" - - def __init__(self, phrase: str = "", id: int = 0): - super().__init__() - self._phrase = phrase - self._id = id - self._locale: str = "" - self._confidence_level: float = 0.0 - self._is_enabled: bool = True - - def getPhrase(self) -> str: - return self._phrase - - def setPhrase(self, phrase: str): - self._phrase = phrase - - def getId(self) -> int: - return self._id - - def setId(self, id: int): - self._id = id - - def getLocale(self) -> str: - return self._locale - - def setLocale(self, locale: str): - self._locale = locale - - def getConfidenceLevel(self) -> float: - return self._confidence_level - - def setConfidenceLevel(self, level: float): - self._confidence_level = level - - def isEnabled(self) -> bool: - return self._is_enabled - - def setEnabled(self, enabled: bool): - self._is_enabled = enabled - - def toString(self) -> str: - return f"Keyphrase(phrase={self._phrase})" - - -# ═══════════════════════════════════════════════════════════════ -# VoiceInteractionSessionActivity (helper) -# ═══════════════════════════════════════════════════════════════ - -class VoiceInteractionSessionActivity(Stub): - """VoiceInteractionSessionActivity — 语音交互会话活动""" - - def __init__(self): - super().__init__() - self._session: Optional[VoiceInteractionSession] = None - self._activity_state: int = 0 - - def setSession(self, session: VoiceInteractionSession): - self._session = session - - def getSession(self) -> Optional[VoiceInteractionSession]: - return self._session - - def getActivityState(self) -> int: - return self._activity_state - - def setActivityState(self, state: int): - self._activity_state = state - - def finishActivity(self): - self._activity_state = VoiceInteractionSession.STATE_FINISHED - - def toString(self) -> str: - return f"VoiceInteractionSessionActivity(state={self._activity_state})" - - -# ═══════════════════════════════════════════════════════════════ -# AlwaysOnHotwordDetector (helper) -# ═══════════════════════════════════════════════════════════════ - -class AlwaysOnHotwordDetector(Stub): - """AlwaysOnHotwordDetector — 始终开启的唤醒词检测器""" - - STATE_NOT_READY = 0 - STATE_READY = 1 - STATE_DETECTING = 2 - STATE_PAUSED = 3 - STATE_ERROR = 4 - STATE_DISABLED = 5 - - RECOGNITION_FLAG_NONE = 0 - RECOGNITION_FLAG_ALLOW_MULTIPLE = 1 - RECOGNITION_FLAG_CAPTURE_TRIGGER = 2 - RECOGNITION_FLAG_TRIGGER_AFTER_PREVIOUS = 4 - - def __init__(self): - super().__init__() - self._state: int = AlwaysOnHotwordDetector.STATE_NOT_READY - self._keyphrases: List[Keyphrase] = [] - self._is_detecting: bool = False - self._detection_callback: Optional[Callable] = None - - def getState(self) -> int: - return self._state - - def setState(self, state: int): - self._state = state - - def getKeyphrases(self) -> List[Keyphrase]: - return list(self._keyphrases) - - def addKeyphrase(self, keyphrase: Keyphrase): - self._keyphrases.append(keyphrase) - - def startRecognition(self, recognition_flags: int = 0): - """开始识别""" - self._is_detecting = True - self._state = AlwaysOnHotwordDetector.STATE_DETECTING - - def stopRecognition(self): - """停止识别""" - self._is_detecting = False - self._state = AlwaysOnHotwordDetector.STATE_READY - - def isDetecting(self) -> bool: - return self._is_detecting - - def setDetectionCallback(self, callback: Callable): - self._detection_callback = callback - - def destroy(self): - self._is_detecting = False - self._state = AlwaysOnHotwordDetector.STATE_NOT_READY - self._keyphrases.clear() - - def toString(self) -> str: - return f"AlwaysOnHotwordDetector(state={self._state})" \ No newline at end of file diff --git a/android_pyapi/android/service_vr.py b/android_pyapi/android/service_vr.py deleted file mode 100644 index 23c6e1e..0000000 --- a/android_pyapi/android/service_vr.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -Android Service VR 组件 — VrListenerService 等 -方法签名完全对齐 Android SDK (android.service.vr 包) -""" - -from typing import Any, Callable, List, Optional - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# VrListenerService -# ═══════════════════════════════════════════════════════════════ - -class VrListenerService(Stub): - """VrListenerService — VR 监听服务 (已弃用, 仅存根)""" - - SERVICE_INTERFACE = "android.service.vr.VrListenerService" - - def __init__(self): - super().__init__() - self._current_vr_activity: Optional[str] = None - self._is_listening: bool = False - - def onCurrentVrActivityChanged(self, component: Any): - """当前 VR Activity 变化时回调""" - if component: - self._current_vr_activity = str(component) - - def getCurrentVrActivity(self) -> Optional[str]: - """获取当前 VR Activity""" - return self._current_vr_activity - - def onConnected(self): - """服务连接回调""" - self._is_listening = True - - def onDisconnected(self): - """服务断开回调""" - self._is_listening = False - - def isListening(self) -> bool: - return self._is_listening - - def toString(self) -> str: - return (f"VrListenerService(" - f"activity={self._current_vr_activity})") - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "VrListenerService", -] \ No newline at end of file diff --git a/android_pyapi/android/service_wallpaper.py b/android_pyapi/android/service_wallpaper.py deleted file mode 100644 index 4908029..0000000 --- a/android_pyapi/android/service_wallpaper.py +++ /dev/null @@ -1,264 +0,0 @@ -""" -Android Service Wallpaper 组件 — WallpaperService, Engine, SurfaceHolder 等 -方法签名完全对齐 Android SDK (android.service.wallpaper 包) -""" - -import time -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# SurfaceHolder -# ═══════════════════════════════════════════════════════════════ - -class SurfaceHolder(Stub): - """SurfaceHolder — 表面持有者 (桌面存根)""" - - # 表面类型 - SURFACE_TYPE_NORMAL = 0 - SURFACE_TYPE_HARDWARE = 1 - SURFACE_TYPE_GPU = 2 - SURFACE_TYPE_PUSH_BUFFERS = 3 - - def __init__(self): - super().__init__() - self._surface: Optional[Any] = None - self._surface_frame: Optional[Any] = None - self._surface_type: int = SurfaceHolder.SURFACE_TYPE_NORMAL - self._fixed_size: Optional[Tuple[int, int]] = None - self._format: int = 0 # PixelFormat.UNKNOWN - self._callbacks: List[Any] = [] - - def getSurface(self) -> Any: - """获取 Surface""" - return self._surface - - def getSurfaceFrame(self) -> Any: - return self._surface_frame - - def isCreating(self) -> bool: - return False - - def setType(self, surface_type: int): - self._surface_type = surface_type - - def getType(self) -> int: - return self._surface_type - - def setFormat(self, format: int): - self._format = format - - def getFormat(self) -> int: - return self._format - - def setFixedSize(self, width: int, height: int): - self._fixed_size = (width, height) - - def setSizeFromLayout(self): - pass - - def addCallback(self, callback: Any): - if callback not in self._callbacks: - self._callbacks.append(callback) - - def removeCallback(self, callback: Any): - if callback in self._callbacks: - self._callbacks.remove(callback) - - def notifySurfaceCreated(self): - for cb in self._callbacks: - if hasattr(cb, 'surfaceCreated'): - cb.surfaceCreated(self) - - def notifySurfaceChanged(self, format: int, width: int, height: int): - for cb in self._callbacks: - if hasattr(cb, 'surfaceChanged'): - cb.surfaceChanged(self, format, width, height) - - def notifySurfaceDestroyed(self): - for cb in self._callbacks: - if hasattr(cb, 'surfaceDestroyed'): - cb.surfaceDestroyed(self) - - def lockCanvas(self) -> Any: - """锁定画布""" - return None - - def lockCanvas(self, dirty: Any) -> Any: - """锁定画布 (指定脏区域)""" - return None - - def unlockCanvasAndPost(self, canvas: Any): - """解锁并提交画布""" - pass - - def toString(self) -> str: - size_str = (f"{self._fixed_size[0]}x{self._fixed_size[1]}" - if self._fixed_size else "dynamic") - return f"SurfaceHolder(size={size_str}, callbacks={len(self._callbacks)})" - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# WallpaperService -# ═══════════════════════════════════════════════════════════════ - -class WallpaperService(Stub): - """WallpaperService — 动态壁纸服务基类""" - - # 服务接口 - SERVICE_INTERFACE = "android.service.wallpaper.WallpaperService" - ACTION_LIVE_WALLPAPER_CHOOSER = ( - "android.service.wallpaper.LIVE_WALLPAPER_CHOOSER" - ) - - # 元数据 - META_DATA_WALLPAPER = "android.service.wallpaper" - - def __init__(self): - super().__init__() - - def onCreate(self): - """创建壁纸服务""" - pass - - def onCreateEngine(self) -> "WallpaperService.Engine": - """创建壁纸引擎""" - return WallpaperService.Engine(self) - - def onDestroy(self): - """销毁壁纸服务""" - pass - - def toString(self) -> str: - return f"WallpaperService@{hex(id(self))}" - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - # ── Engine ── - - class Engine(Stub): - """Engine — 壁纸引擎""" - - def __init__(self, service: "WallpaperService" = None): - super().__init__() - self._service = service - self._is_visible: bool = True - self._is_created: bool = False - self._surface_holder = SurfaceHolder() - self._surface_holder.addCallback(self) - self._offset: Tuple[float, float, float, float, float] = (0, 0, 0, 0, 0) - self._x_ppw: float = 1.0 - self._y_ppw: float = 1.0 - self._desired_width: int = 0 - self._desired_height: int = 0 - self._touch_events_enabled: bool = False - self._offset_notifications_enabled: bool = False - self._draw_count: int = 0 - - def onCreate(self, surface_holder: SurfaceHolder): - """创建引擎""" - self._is_created = True - - def onSurfaceCreated(self, holder: SurfaceHolder): - """表面创建时回调""" - pass - - def onSurfaceDestroyed(self, holder: SurfaceHolder): - """表面销毁时回调""" - pass - - def onSurfaceChanged(self, holder: SurfaceHolder, - format: int, width: int, height: int): - """表面尺寸变化时回调""" - pass - - def onDraw(self, canvas: Any): - """绘制壁纸""" - self._draw_count += 1 - - def onOffsetsChanged(self, x_offset: float, y_offset: float, - x_step: float, y_step: float, - x_pixel_offset: int, y_pixel_offset: int): - """偏移量变化回调""" - self._offset = (x_offset, y_offset, x_step, y_step) - - def onTouchEvent(self, motion_event: Any): - """触摸事件回调""" - pass - - def onVisibilityChanged(self, visible: bool): - """可见性变化回调""" - self._is_visible = visible - - def setOffsetNotifications(self, x_ppw: float, y_ppw: float): - """设置偏移通知""" - self._x_ppw = x_ppw - self._y_ppw = y_ppw - self._offset_notifications_enabled = True - - def getSurfaceHolder(self) -> SurfaceHolder: - """获取表面持有者""" - return self._surface_holder - - def getDesiredMinimumWidth(self) -> int: - return self._desired_width - - def getDesiredMinimumHeight(self) -> int: - return self._desired_height - - def isVisible(self) -> bool: - return self._is_visible - - def isPreview(self) -> bool: - return False - - def setTouchEventsEnabled(self, enabled: bool): - self._touch_events_enabled = enabled - - def setCreateSurfaceFromSurfaceControlResult(self, result: Any): - pass - - def surfaceCreated(self, holder: SurfaceHolder): - """SurfaceHolder.Callback — 表面创建""" - self.onSurfaceCreated(holder) - - def surfaceChanged(self, holder: SurfaceHolder, - format: int, width: int, height: int): - """SurfaceHolder.Callback — 表面变化""" - self.onSurfaceChanged(holder, format, width, height) - - def surfaceDestroyed(self, holder: SurfaceHolder): - """SurfaceHolder.Callback — 表面销毁""" - self.onSurfaceDestroyed(holder) - - def toString(self) -> str: - return (f"Engine(visible={self._is_visible}, " - f"draws={self._draw_count})") - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "WallpaperService", - "SurfaceHolder", -] \ No newline at end of file diff --git a/android_pyapi/android/sip.py b/android_pyapi/android/sip.py deleted file mode 100644 index 275d4f0..0000000 --- a/android_pyapi/android/sip.py +++ /dev/null @@ -1,519 +0,0 @@ -""" -Android SIP API — SipManager, SipProfile, SipSession, SipAudioCall -(已废弃但存在于 SDK 中) -方法签名完全对齐 Android SDK -""" - -import sys -from typing import Any, Dict, List, Optional, Set, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "SipManager", - "SipProfile", - "SipSession", - "SipAudioCall", -] - - -# ═══════════════════════════════════════════════════════════════ -# SipManager -# ═══════════════════════════════════════════════════════════════ - -class SipManager(Stub): - """SipManager — SIP 会话管理器 (桌面存根, 已废弃)""" - - EXTRA_CALL_ID = "android.sip.extra.CALL_ID" - EXTRA_OFFER_SD = "android.sip.extra.OFFER_SD" - EXTRA_PEER_PROFILE = "android.sip.extra.PEER_PROFILE" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._opened = False - self._registered = False - self._session: Optional["SipSession"] = None - self._listener: Optional[Callable] = None - - @staticmethod - def isSipWifiOnly(context: Any) -> bool: - """检查 SIP 是否仅限 WiFi""" - return False - - @staticmethod - def isVoipSupported(context: Any) -> bool: - """检查是否支持 VoIP""" - return True - - @staticmethod - def isApiSupported(context: Any) -> bool: - """检查 SIP API 是否支持""" - return True - - @staticmethod - def newInstance(context: Any) -> "SipManager": - """创建新实例""" - return SipManager(context) - - def open(self, localProfile: "SipProfile") -> "SipSession": - """打开 SIP 会话""" - self._opened = True - self._session = SipSession(localProfile) - return self._session - - def setRegistrationListener( - self, localProfileUri: str, listener: "SipSession.Listener" - ): - """设置注册监听器""" - if self._session: - self._session.setListener(listener) - - def close(self, localProfileUri: str): - """关闭 SIP 会话""" - self._opened = False - self._registered = False - self._session = None - - def getSessionFor(self, intent: Any) -> Optional["SipSession"]: - """获取 Intent 对应的 SIP 会话""" - return self._session - - def register( - self, localProfile: "SipProfile", expiryTime: int, listener: "SipSession.Listener" - ): - """注册 SIP""" - raise AndroidOnlyError("SipManager.register() 仅在 Android 设备上可用") - - def unregister( - self, localProfile: "SipProfile", listener: "SipSession.Listener" - ): - """注销 SIP""" - raise AndroidOnlyError("SipManager.unregister() 仅在 Android 设备上可用") - - def isOpened(self, localProfileUri: str) -> bool: - """检查是否已打开""" - return self._opened - - def isRegistered(self, localProfileUri: str) -> bool: - """检查是否已注册""" - return self._registered - - def createSipSessionIfNeeded(self, localProfile: "SipProfile") -> "SipSession": - """如有必要则创建 SIP 会话""" - if self._session is None: - self._session = SipSession(localProfile) - return self._session - - def createSipProfile(self, intent: Any) -> Optional["SipProfile"]: - """从 Intent 创建 SIP 配置文件""" - return None - - def toString(self) -> str: - return f"SipManager(opened={self._opened}, registered={self._registered})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# SipProfile -# ═══════════════════════════════════════════════════════════════ - -class SipProfile(Stub): - """SipProfile — SIP 配置文件""" - - # --- Protocol constants --- - PROTOCOL_UDP = "UDP" - PROTOCOL_TCP = "TCP" - PROTOCOL_TLS = "TLS" - - class Builder(Stub): - """SipProfile.Builder — SIP 配置文件构建器""" - - def __init__(self, uri: str = ""): - super().__init__() - self._profile = SipProfile() - self._profile._uri = uri - - def setProfileName(self, name: str) -> "SipProfile.Builder": - self._profile._profile_name = name - return self - - def setUserName(self, userName: str) -> "SipProfile.Builder": - self._profile._user_name = userName - return self - - def setDomain(self, domain: str) -> "SipProfile.Builder": - self._profile._domain = domain - return self - - def setPassword(self, password: str) -> "SipProfile.Builder": - self._profile._password = password - return self - - def setPort(self, port: int) -> "SipProfile.Builder": - self._profile._port = port - return self - - def setProtocol(self, protocol: str) -> "SipProfile.Builder": - self._profile._protocol = protocol - return self - - def setSendKeepAlive(self, enabled: bool) -> "SipProfile.Builder": - self._profile._send_keep_alive = enabled - return self - - def setAutoRegistration(self, enabled: bool) -> "SipProfile.Builder": - self._profile._auto_registration = enabled - return self - - def setDisplayName(self, displayName: str) -> "SipProfile.Builder": - self._profile._display_name = displayName - return self - - def setAuthUserName(self, authUserName: str) -> "SipProfile.Builder": - self._profile._auth_user_name = authUserName - return self - - def build(self) -> "SipProfile": - return self._profile - - def toString(self) -> str: - return f"SipProfile.Builder(name={self._profile._profile_name})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def __init__(self): - super().__init__() - self._uri = "" - self._profile_name = "" - self._user_name = "" - self._domain = "" - self._display_name = "" - self._password = "" - self._protocol = self.PROTOCOL_UDP - self._port = 5060 - self._send_keep_alive = False - self._auto_registration = True - self._auth_user_name = "" - - def getProfileName(self) -> str: - return self._profile_name - - def getUserName(self) -> str: - return self._user_name - - def getDomain(self) -> str: - return self._domain - - def getDisplayName(self) -> str: - return self._display_name - - def getPassword(self) -> str: - return self._password - - def getProtocol(self) -> str: - return self._protocol - - def getPort(self) -> int: - return self._port - - def getSendKeepAlive(self) -> bool: - return self._send_keep_alive - - def isAutoRegistration(self) -> bool: - return self._auto_registration - - def getUri(self) -> str: - return self._uri - - def toString(self) -> str: - return (f"SipProfile(name={self._profile_name}, " - f"user={self._user_name}@{self._domain})") - - def equals(self, other) -> bool: - if not isinstance(other, SipProfile): - return False - return self._uri == other._uri - - def hashCode(self) -> int: - return hash(self._uri) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# SipSession -# ═══════════════════════════════════════════════════════════════ - -class SipSession(Stub): - """SipSession — SIP 会话""" - - # --- State constants --- - STATE_READY_TO_CALL = 0 - STATE_INCOMING_CALL = 1 - STATE_INCOMING_CALL_ANSWERING = 2 - STATE_IN_CALL = 3 - STATE_OUTGOING_CALL = 4 - STATE_OUTGOING_CALL_RINGING = 5 - STATE_OUTGOING_CALL_CANCELING = 6 - STATE_PINGING = 7 - STATE_DEREGISTERING = 8 - STATE_CLOSED = 9 - - def __init__(self, profile: "SipProfile" = None): - super().__init__() - self._profile = profile - self._listener: Optional["SipSession.Listener"] = None - self._state = self.STATE_CLOSED - self._call_id = "" - self._peer_profile: Optional[SipProfile] = None - self._in_call = False - - def setListener(self, listener: "SipSession.Listener"): - """设置会话监听器""" - self._listener = listener - - def register(self, duration: int): - """注册 SIP""" - raise AndroidOnlyError("SipSession.register() 仅在 Android 设备上可用") - - def unregister(self): - """注销 SIP""" - raise AndroidOnlyError("SipSession.unregister() 仅在 Android 设备上可用") - - def makeAudioCall( - self, localProfile: "SipProfile", peerProfile: "SipProfile" - ): - """发起音频呼叫""" - raise AndroidOnlyError("SipSession.makeAudioCall() 仅在 Android 设备上可用") - - def answerCall(self, callId: str, sessionDescription: int): - """接听呼叫""" - raise AndroidOnlyError("SipSession.answerCall() 仅在 Android 设备上可用") - - def endCall(self): - """结束呼叫""" - raise AndroidOnlyError("SipSession.endCall() 仅在 Android 设备上可用") - - def changeCall(self, callId: str, sessionDescription: int): - """更改呼叫""" - raise AndroidOnlyError("SipSession.changeCall() 仅在 Android 设备上可用") - - def getCallId(self) -> str: - return self._call_id - - def getPeerProfile(self) -> Optional["SipProfile"]: - return self._peer_profile - - def getState(self) -> int: - return self._state - - def isInCall(self) -> bool: - return self._in_call - - def getLocalProfile(self) -> Optional["SipProfile"]: - return self._profile - - # ═══════════════════════════════════════════════════════════ - # Listener - # ═══════════════════════════════════════════════════════════ - - class Listener(Stub): - """SipSession.Listener — SIP 会话监听器""" - - def __init__(self): - super().__init__() - - def onCalling(self, session: "SipSession"): - """呼叫中回调""" - pass - - def onRinging(self, session: "SipSession", caller: "SipProfile"): - """响铃回调""" - pass - - def onCallEstablished(self, session: "SipSession", sessionDescription: str): - """呼叫建立回调""" - pass - - def onCallEnded(self, session: "SipSession"): - """呼叫结束回调""" - pass - - def onCallBusy(self, session: "SipSession"): - """呼叫忙碌回调""" - pass - - def onError(self, session: "SipSession", errorCode: int, errorMessage: str): - """错误回调""" - pass - - def onCallChangeFailed(self, session: "SipSession", errorCode: int, errorMessage: str): - """呼叫变更失败回调""" - pass - - def onRegistrationDone(self, session: "SipSession", duration: int): - """注册成功回调""" - pass - - def onRegistrationFailed( - self, session: "SipSession", errorCode: int, errorMessage: str - ): - """注册失败回调""" - pass - - def onRegistrationTimeout(self, session: "SipSession"): - """注册超时回调""" - pass - - def toString(self) -> str: - return f"SipSession.Listener@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def toString(self) -> str: - return f"SipSession(state={self._state})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# SipAudioCall -# ═══════════════════════════════════════════════════════════════ - -class SipAudioCall(Stub): - """SipAudioCall — SIP 音频呼叫""" - - def __init__(self, context: Any = None, localProfile: "SipProfile" = None): - super().__init__() - self._context = context - self._local_profile = localProfile - self._listener: Optional["SipAudioCall.Listener"] = None - self._muted = False - self._holding = False - self._in_call = False - - def setListener(self, listener: "SipAudioCall.Listener"): - """设置音频呼叫监听器""" - self._listener = listener - - def startAudio(self): - """开始音频""" - raise AndroidOnlyError("SipAudioCall.startAudio() 仅在 Android 设备上可用") - - def stopAudio(self): - """停止音频""" - raise AndroidOnlyError("SipAudioCall.stopAudio() 仅在 Android 设备上可用") - - def close(self): - """关闭音频呼叫""" - self._in_call = False - self._holding = False - - def isMuted(self) -> bool: - return self._muted - - def toggleMute(self): - """切换静音""" - self._muted = not self._muted - - def hold(self, hold: int): - """保持呼叫""" - self._holding = (hold != 0) - - def continueCall(self, sessionDescription: int): - """继续呼叫""" - raise AndroidOnlyError("SipAudioCall.continueCall() 仅在 Android 设备上可用") - - def makeCall( - self, localProfile: "SipProfile", peerProfile: "SipProfile", timeout: int - ): - """发起呼叫""" - raise AndroidOnlyError("SipAudioCall.makeCall() 仅在 Android 设备上可用") - - def answerCall(self, sessionDescription: int): - """接听呼叫""" - raise AndroidOnlyError("SipAudioCall.answerCall() 仅在 Android 设备上可用") - - def sendDtmf(self, code: int): - """发送 DTMF""" - pass - - # ═══════════════════════════════════════════════════════════ - # Listener - # ═══════════════════════════════════════════════════════════ - - class Listener(Stub): - """SipAudioCall.Listener — 音频呼叫监听器""" - - def __init__(self): - super().__init__() - - def onReadyToCall(self, call: "SipAudioCall"): - """准备呼叫回调""" - pass - - def onCallEstablished(self, call: "SipAudioCall", sessionDescription: str): - """呼叫建立回调""" - pass - - def onCallEnded(self, call: "SipAudioCall"): - """呼叫结束回调""" - pass - - def onCallHeld(self, call: "SipAudioCall"): - """呼叫保持回调""" - pass - - def onCallChanged(self, call: "SipAudioCall"): - """呼叫变更回调""" - pass - - def onError(self, call: "SipAudioCall", errorCode: int, errorMessage: str): - """错误回调""" - pass - - def onRingingBack(self, call: "SipAudioCall"): - """回铃回调""" - pass - - def onRinging(self, call: "SipAudioCall", caller: "SipProfile"): - """响铃回调""" - pass - - def toString(self) -> str: - return f"SipAudioCall.Listener@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def toString(self) -> str: - return f"SipAudioCall(inCall={self._in_call})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/speech.py b/android_pyapi/android/speech.py deleted file mode 100644 index 1429ae5..0000000 --- a/android_pyapi/android/speech.py +++ /dev/null @@ -1,370 +0,0 @@ -""" -Android Speech API — SpeechRecognizer, RecognitionListener, RecognitionService, RecognizerIntent -方法签名完全对齐 Android SDK -""" - -import sys -import threading -import time -from typing import Any, Dict, List, Optional, Set, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "SpeechRecognizer", - "RecognitionListener", - "RecognitionService", - "RecognizerIntent", - "RecognizerResultsIntent", -] - - -# ═══════════════════════════════════════════════════════════════ -# SpeechRecognizer -# ═══════════════════════════════════════════════════════════════ - -class SpeechRecognizer(Stub): - """SpeechRecognizer — 语音识别器 (桌面麦克风模拟)""" - - RESULTS_RECOGNITION = "results_recognition" - CONFIDENCE_SCORES = "confidence_scores" - - ERROR_NETWORK_TIMEOUT = 1 - ERROR_NETWORK = 2 - ERROR_AUDIO = 3 - ERROR_SERVER = 4 - ERROR_CLIENT = 5 - ERROR_SPEECH_TIMEOUT = 6 - ERROR_NO_MATCH = 7 - ERROR_RECOGNIZER_BUSY = 8 - ERROR_INSUFFICIENT_PERMISSIONS = 9 - ERROR_TOO_MANY_REQUESTS = 10 - ERROR_LANGUAGE_NOT_SUPPORTED = 11 - ERROR_LANGUAGE_UNAVAILABLE = 12 - ERROR_SERVER_DISCONNECTED = 13 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._listener: Optional["RecognitionListener"] = None - self._is_listening = False - self._destroyed = False - self._simulate_timer: Optional[threading.Timer] = None - - @staticmethod - def createSpeechRecognizer(context: Any) -> "SpeechRecognizer": - """创建语音识别器""" - return SpeechRecognizer(context) - - @staticmethod - def isRecognitionAvailable(context: Any) -> bool: - """检查语音识别是否可用""" - try: - import speech_recognition as sr - return True - except ImportError: - try: - import whisper # noqa: F401 - return True - except ImportError: - pass - return False - - def setRecognitionListener(self, listener: "RecognitionListener"): - """设置识别监听器""" - self._listener = listener - - def startListening(self, intent: Any): - """开始语音识别""" - if self._destroyed: - return - - self._is_listening = True - - # 使用简单识别模拟 - if self._listener: - self._listener.onReadyForSpeech(None) - self._listener.onBeginningOfSpeech() - - # 模拟麦克风音量变化 - for i in range(3): - self._listener.onRmsChanged(5.0 + i * 2.0) - time.sleep(0.05) - - self._listener.onBufferReceived(bytearray(1024)) - self._listener.onEndOfSpeech() - - # 模拟识别结果 - results = {self.RESULTS_RECOGNITION: ["hello world"], self.CONFIDENCE_SCORES: [0.95]} - self._listener.onResults(results) - - def stopListening(self): - """停止语音识别""" - self._is_listening = False - if self._simulate_timer: - self._simulate_timer.cancel() - self._simulate_timer = None - - def cancel(self): - """取消语音识别""" - self._is_listening = False - if self._simulate_timer: - self._simulate_timer.cancel() - self._simulate_timer = None - if self._listener: - self._listener.onError(self.ERROR_NO_MATCH) - - def destroy(self): - """销毁语音识别器""" - self._destroyed = True - self._is_listening = False - if self._simulate_timer: - self._simulate_timer.cancel() - self._simulate_timer = None - self._listener = None - - def toString(self) -> str: - return f"SpeechRecognizer(listening={self._is_listening})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# RecognitionListener -# ═══════════════════════════════════════════════════════════════ - -class RecognitionListener(Stub): - """RecognitionListener — 语音识别监听器""" - - def __init__(self): - super().__init__() - - def onReadyForSpeech(self, params: Any): - """准备开始语音识别回调""" - pass - - def onBeginningOfSpeech(self): - """开始说话回调""" - pass - - def onRmsChanged(self, rmsdB: float): - """音量变化回调""" - pass - - def onBufferReceived(self, buffer: bytearray): - """接收音频缓冲区回调""" - pass - - def onEndOfSpeech(self): - """说话结束回调""" - pass - - def onError(self, error: int): - """错误回调""" - pass - - def onResults(self, results: Dict[str, Any]): - """识别结果回调""" - pass - - def onPartialResults(self, partialResults: Dict[str, Any]): - """部分识别结果回调""" - pass - - def onSegmentResults(self, segmentResults: Dict[str, Any]): - """分段结果回调""" - pass - - def onEndOfSegmentedSession(self, sessionId: int): - """分段会话结束回调""" - pass - - def onLanguageDetection(self, language: str): - """语言检测回调""" - pass - - def onEvent(self, eventType: int, params: Any): - """事件回调""" - pass - - def toString(self) -> str: - return f"RecognitionListener@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# RecognitionService -# ═══════════════════════════════════════════════════════════════ - -class RecognitionService(Stub): - """RecognitionService — 语音识别服务""" - - SERVICE_INTERFACE = "android.speech.RecognitionService" - SERVICE_META_DATA = "android.speech" - - def __init__(self): - super().__init__() - self._callback: Optional["RecognitionService.Callback"] = None - - def onStartListening(self, intent: Any, listener: "RecognitionService.Callback"): - """开始监听回调""" - pass - - def onCancel(self, listener: "RecognitionService.Callback"): - """取消回调""" - pass - - def onStopListening(self, listener: "RecognitionService.Callback"): - """停止监听回调""" - pass - - def onDestroy(self): - """销毁回调""" - pass - - def oneShotListening(self, intent: Any, listener: "RecognitionService.Callback"): - """一次性监听""" - if listener: - listener.readyForSpeech(None) - listener.beginningOfSpeech() - listener.endOfSpeech() - results = {SpeechRecognizer.RESULTS_RECOGNITION: ["hello"]} - listener.results(results) - - def getCurrentCallback(self) -> Optional["RecognitionService.Callback"]: - return self._callback - - # ═══════════════════════════════════════════════════════════ - # Callback - # ═══════════════════════════════════════════════════════════ - - class Callback(Stub): - """RecognitionService.Callback — 识别服务回调""" - - def __init__(self): - super().__init__() - - def results(self, results: Dict[str, Any]): - """返回识别结果""" - pass - - def partialResults(self, partialResults: Dict[str, Any]): - """返回部分结果""" - pass - - def error(self, error: int): - """返回错误""" - pass - - def beginningOfSpeech(self): - """开始说话回调""" - pass - - def bufferReceived(self, buffer: bytearray): - """接收音频缓冲区回调""" - pass - - def endOfSpeech(self): - """说话结束回调""" - pass - - def readyForSpeech(self, params: Any): - """准备开始语音识别""" - pass - - def rmsChanged(self, rmsdB: float): - """音量变化回调""" - pass - - def toString(self) -> str: - return f"RecognitionService.Callback@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def toString(self) -> str: - return f"RecognitionService@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# RecognizerIntent -# ═══════════════════════════════════════════════════════════════ - -class RecognizerIntent(Stub): - """RecognizerIntent — 识别器 Intent 常量""" - - ACTION_RECOGNIZE_SPEECH = "android.speech.action.RECOGNIZE_SPEECH" - ACTION_WEB_SEARCH = "android.speech.action.WEB_SEARCH" - ACTION_VOICE_SEARCH_HANDS_FREE = "android.speech.action.VOICE_SEARCH_HANDS_FREE" - ACTION_GET_LANGUAGE_DETAILS = "android.speech.action.GET_LANGUAGE_DETAILS" - - EXTRA_LANGUAGE_MODEL = "android.speech.extra.LANGUAGE_MODEL" - EXTRA_LANGUAGE = "android.speech.extra.LANGUAGE" - EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS" - EXTRA_PARTIAL_RESULTS = "android.speech.extra.PARTIAL_RESULTS" - EXTRA_CALLING_PACKAGE = "android.speech.extra.CALLING_PACKAGE" - EXTRA_PROMPT = "android.speech.extra.PROMPT" - EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS = "android.speech.extra.SPEECH_INPUT_MINIMUM_LENGTH_MILLIS" - EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extra.SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS" - EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extra.SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS" - EXTRA_CONFIDENCE_SCORES = "android.speech.extra.CONFIDENCE_SCORES" - EXTRA_RESULTS = "android.speech.extra.RESULTS" - EXTRA_SECURE = "android.speech.extra.SECURE" - EXTRA_SUPPORTED_LANGUAGES = "android.speech.extra.SUPPORTED_LANGUAGES" - EXTRA_LANGUAGE_PREFERENCE = "android.speech.extra.LANGUAGE_PREFERENCE" - EXTRA_ONLY_RETURN_LANGUAGE_PREFERENCE = "android.speech.extra.ONLY_RETURN_LANGUAGE_PREFERENCE" - EXTRA_LANGUAGE_DETAILS = "android.speech.extra.LANGUAGE_DETAILS" - - LANGUAGE_MODEL_FREE_FORM = "free_form" - LANGUAGE_MODEL_WEB_SEARCH = "web_search" - - RESULT_AUDIO_ERROR = 1 - RESULT_CLIENT_ERROR = 2 - RESULT_NETWORK_ERROR = 3 - RESULT_SERVER_ERROR = 4 - RESULT_NO_MATCH = 5 - - DETAIL_META_DATA = "android.speech.DETAIL_META_DATA" - - def __init__(self): - super().__init__() - - -# ═══════════════════════════════════════════════════════════════ -# RecognizerResultsIntent -# ═══════════════════════════════════════════════════════════════ - -class RecognizerResultsIntent(Stub): - """RecognizerResultsIntent — 识别结果 Intent 常量""" - - ACTION_VOICE_SEARCH_RESULTS = "android.speech.action.VOICE_SEARCH_RESULTS" - EXTRA_VOICE_SEARCH_RESULT_STRINGS = "android.speech.extra.VOICE_SEARCH_RESULT_STRINGS" - EXTRA_VOICE_SEARCH_RESULT_URLS = "android.speech.extra.VOICE_SEARCH_RESULT_URLS" - EXTRA_VOICE_SEARCH_RESULT_HTML = "android.speech.extra.VOICE_SEARCH_RESULT_HTML" - EXTRA_VOICE_SEARCH_RESULT_HTML_BASE_URLS = "android.speech.extra.VOICE_SEARCH_RESULT_HTML_BASE_URLS" - URI_SCHEME_INLINE = "inline" - - def __init__(self): - super().__init__() \ No newline at end of file diff --git a/android_pyapi/android/speech_tts.py b/android_pyapi/android/speech_tts.py deleted file mode 100644 index 60c8dcd..0000000 --- a/android_pyapi/android/speech_tts.py +++ /dev/null @@ -1,401 +0,0 @@ -""" -Android Speech/TTS 组件 — TextToSpeech, Voice, SynthesisCallback 等 -方法签名完全对齐 Android SDK -""" - -import os -import sys -import locale as _locale -from typing import Any, Dict, List, Optional, Set, Tuple, Union - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) - - -# ═══════════════════════════════════════════════════════════════ -# TextToSpeech.OnInitListener -# ═══════════════════════════════════════════════════════════════ - -class TextToSpeechOnInitListener(Stub): - """OnInitListener — 初始化监听器""" - - def onInit(self, status: int): - pass - - -# ═══════════════════════════════════════════════════════════════ -# UtteranceProgressListener -# ═══════════════════════════════════════════════════════════════ - -class UtteranceProgressListener(Stub): - """UtteranceProgressListener — 发音进度监听器""" - - def onStart(self, utterance_id: str): - pass - - def onDone(self, utterance_id: str): - pass - - def onError(self, utterance_id: str, error_code: int = -1): - pass - - def onStop(self, utterance_id: str, interrupted: bool = False): - pass - - def onBeginSynthesis(self, utterance_id: str, sample_rate_in_hz: int, - audio_format: int, channel_count: int): - pass - - def onRangeStart(self, utterance_id: str, start: int, end: int, frame: int): - pass - - -class TextToSpeechOnUtteranceCompletedListener(Stub): - """OnUtteranceCompletedListener — 旧版发音完成监听器""" - - def onUtteranceCompleted(self, utterance_id: str): - pass - - -# ═══════════════════════════════════════════════════════════════ -# SynthesisRequest -# ═══════════════════════════════════════════════════════════════ - -class SynthesisRequest(Stub): - """SynthesisRequest — 合成请求""" - - def __init__(self): - super().__init__() - self._text: str = "" - self._speech_rate: float = 1.0 - self._voice: Optional["Voice"] = None - self._calling_uid: int = 0 - self._country: str = "" - self._language: str = "" - self._variant: str = "" - self._params: Dict[str, str] = {} - - def getText(self) -> str: - return self._text - - def getSpeechRate(self) -> float: - return self._speech_rate - - def getVoice(self) -> Optional["Voice"]: - return self._voice - - def getCallingUid(self) -> int: - return self._calling_uid - - def getCountry(self) -> str: - return self._country - - def getLanguage(self) -> str: - return self._language - - def getVariant(self) -> str: - return self._variant - - def getParams(self) -> Dict[str, str]: - return dict(self._params) - - -# ═══════════════════════════════════════════════════════════════ -# SynthesisCallback -# ═══════════════════════════════════════════════════════════════ - -class SynthesisCallback(Stub): - """SynthesisCallback — 合成回调""" - - def start(self, sample_rate_in_hz: int, audio_format: int, channel_count: int) -> int: - return 0 - - def getMaxBufferSize(self) -> int: - return 8192 - - def getChannelCount(self) -> int: - return 1 - - def getAudioFormat(self) -> int: - return 1 - - def getSampleRate(self) -> int: - return 22050 - - def availableBuffer(self, buffer_size: int) -> int: - return buffer_size - - def audioAvailable(self, audio: bytes) -> int: - return len(audio) - - def done(self) -> int: - return 0 - - def error(self) -> int: - return -1 - - def hasStarted(self) -> bool: - return False - - def hasFinished(self) -> bool: - return False - - -# ═══════════════════════════════════════════════════════════════ -# Voice -# ═══════════════════════════════════════════════════════════════ - -class Voice(Stub): - """Voice — 语音""" - - QUALITY_DEFAULT = -1 - QUALITY_VERY_LOW = 100 - QUALITY_LOW = 200 - QUALITY_NORMAL = 300 - QUALITY_HIGH = 400 - QUALITY_VERY_HIGH = 500 - - LATENCY_DEFAULT = -1 - LATENCY_VERY_LOW = 100 - LATENCY_LOW = 200 - LATENCY_NORMAL = 300 - LATENCY_HIGH = 400 - LATENCY_VERY_HIGH = 500 - - def __init__(self, name: str = "", locale: Any = None, - quality: int = QUALITY_NORMAL, latency: int = LATENCY_NORMAL, - requires_network: bool = False, - features: Optional[List[str]] = None): - super().__init__() - self._name = name - self._locale = locale or _locale.getdefaultlocale()[0] - self._quality = quality - self._latency = latency - self._requires_network = requires_network - self._features = features or [] - - def getName(self) -> str: - return self._name - - def getLocale(self) -> Any: - return self._locale - - def getQuality(self) -> int: - return self._quality - - def getLatency(self) -> int: - return self._latency - - def isNetworkConnectionRequired(self) -> bool: - return self._requires_network - - def getFeatures(self) -> List[str]: - return list(self._features) - - def toString(self) -> str: - return f"Voice(name={self._name}, locale={self._locale})" - - -# ═══════════════════════════════════════════════════════════════ -# TextToSpeech.Engine -# ═══════════════════════════════════════════════════════════════ - -class TextToSpeechEngine(Stub): - """TextToSpeech.Engine — TTS 引擎元数据""" - - ACTION_CHECK_TTS_DATA = "android.speech.tts.engine.CHECK_TTS_DATA" - ACTION_INSTALL_TTS_DATA = "android.speech.tts.engine.INSTALL_TTS_DATA" - ACTION_GET_SAMPLE_TEXT = "android.speech.tts.engine.GET_SAMPLE_TEXT" - - KEY_PARAM_STREAM = "streamType" - KEY_PARAM_VOLUME = "volume" - KEY_PARAM_PAN = "pan" - KEY_PARAM_UTTERANCE_ID = "utteranceId" - KEY_PARAM_SESSION_ID = "sessionId" - - KEY_FEATURE_NETWORK_SYNTHESIS = "networkSynthesis" - KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedSynthesis" - KEY_FEATURE_NOT_INSTALLED = "notInstalled" - - DEFAULT_STREAM = 3 - DEFAULT_LANG = "eng" - DEFAULT_COUNTRY = "USA" - DEFAULT_VARIANT = "" - - @staticmethod - def getLocaleLang(lang: str) -> str: - return lang[:3] if len(lang) >= 3 else lang - - @staticmethod - def getLocaleCountry(lang: str) -> str: - return lang[3:6] if len(lang) >= 6 else "" - - @staticmethod - def getLocaleVariant(lang: str) -> str: - return lang[6:] if len(lang) > 6 else "" - - -Engine = TextToSpeechEngine - - -# ═══════════════════════════════════════════════════════════════ -# TextToSpeech -# ═══════════════════════════════════════════════════════════════ - -class TextToSpeech(Stub): - """TextToSpeech — 文字转语音""" - - SUCCESS = 0 - ERROR = -1 - STOPPED = -2 - - QUEUE_FLUSH = 0 - QUEUE_ADD = 1 - - LANG_COUNTRY_AVAILABLE = 1 - LANG_AVAILABLE = 0 - LANG_COUNTRY_VAR_AVAILABLE = 2 - LANG_MISSING_DATA = -1 - LANG_NOT_SUPPORTED = -2 - - Engine = TextToSpeechEngine - - def __init__(self, context: Any = None, listener: Optional[TextToSpeechOnInitListener] = None): - super().__init__() - self._context = context - self._listener = listener - self._utterance_progress_listener: Optional[UtteranceProgressListener] = None - self._language: str = "" - self._country: str = "" - self._pitch: float = 1.0 - self._speech_rate: float = 1.0 - self._voice: Optional[Voice] = None - self._voices: List[Voice] = [ - Voice("en-us-1", "en_US", Voice.QUALITY_HIGH, Voice.LATENCY_NORMAL), - Voice("en-gb-1", "en_GB", Voice.QUALITY_NORMAL, Voice.LATENCY_NORMAL), - Voice("zh-cn-1", "zh_CN", Voice.QUALITY_HIGH, Voice.LATENCY_NORMAL), - ] - self._engine_package: str = "" - self._initialized = False - if listener: - try: - listener.onInit(self.SUCCESS) - except Exception: - pass - self._initialized = True - - def speak(self, text: str, queue_mode: int = QUEUE_FLUSH, - params: Any = None, utterance_id: str = "") -> int: - return self.SUCCESS - - def stop(self) -> int: - return self.SUCCESS - - def shutdown(self): - self._initialized = False - - def setLanguage(self, language: str, country: str = "", variant: str = "") -> int: - self._language = language - self._country = country - return self.LANG_AVAILABLE - - def getLanguage(self) -> Tuple[str, str, str]: - return (self._language, self._country, "") - - def isLanguageAvailable(self, language: str, country: str = "", - variant: str = "") -> int: - return self.LANG_AVAILABLE - - def setOnUtteranceProgressListener(self, listener: UtteranceProgressListener): - self._utterance_progress_listener = listener - - def setOnUtteranceCompletedListener(self, listener: TextToSpeechOnUtteranceCompletedListener): - pass - - def setPitch(self, pitch: float) -> int: - self._pitch = pitch - return self.SUCCESS - - def setSpeechRate(self, rate: float) -> int: - self._speech_rate = rate - return self.SUCCESS - - def getVoice(self) -> Optional[Voice]: - return self._voice - - def setVoice(self, voice: Voice) -> int: - self._voice = voice - return self.SUCCESS - - def getVoices(self) -> Set[Voice]: - return set(self._voices) - - def addSpeech(self, text: str, filename: str) -> int: - return self.SUCCESS - - def addEyes(self, text: str, filename: str) -> int: - return self.SUCCESS - - def synthesizeToFile(self, text: str, params: Any, filename: str) -> int: - return self.SUCCESS - - def setEngineByPackageName(self, package_name: str): - self._engine_package = package_name - - def getDefaultEngine(self) -> Optional[str]: - return "com.android.tts" - - def getEngines(self) -> List["EngineInfo"]: - return [] - - def getFeatures(self, locale: Any) -> Set[str]: - return set() - - def areDefaultsEnforced(self) -> bool: - return False - - def setQueueMode(self, queue_mode: int): - pass - - def isSpeaking(self) -> bool: - return False - - def toString(self) -> str: - return f"TextToSpeech(lang={self._language})" - - -# ═══════════════════════════════════════════════════════════════ -# EngineInfo -# ═══════════════════════════════════════════════════════════════ - -class EngineInfo(Stub): - """EngineInfo — 引擎信息""" - - def __init__(self): - super().__init__() - self.name: str = "" - self.label: str = "" - self.icon: int = 0 - self.priority: int = 0 - self.system: bool = False - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "TextToSpeech", - "TextToSpeechEngine", - "TextToSpeechOnInitListener", - "TextToSpeechOnUtteranceCompletedListener", - "SynthesisCallback", - "SynthesisRequest", - "Voice", - "UtteranceProgressListener", - "Engine", - "EngineInfo", -] \ No newline at end of file diff --git a/android_pyapi/android/system.py b/android_pyapi/android/system.py deleted file mode 100644 index 8e2eae6..0000000 --- a/android_pyapi/android/system.py +++ /dev/null @@ -1,592 +0,0 @@ -""" -Android System 组件 — Os, StructStat, StructUtsname, OsConstants 等 -方法签名完全对齐 Android SDK (android.system 包) -""" - -import os as _os -import stat as _stat -import platform -import errno -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# OsConstants -# ═══════════════════════════════════════════════════════════════ - -class OsConstants(Stub): - """OsConstants — 系统调用常量,映射自 os/stat 模块""" - - # 文件访问常量 - R_OK = _os.R_OK - W_OK = _os.W_OK - X_OK = _os.X_OK - F_OK = _os.F_OK - - # 文件权限常量 - S_IRWXU = _stat.S_IRWXU - S_IRUSR = _stat.S_IRUSR - S_IWUSR = _stat.S_IWUSR - S_IXUSR = _stat.S_IXUSR - S_IRWXG = _stat.S_IRWXG - S_IRGRP = _stat.S_IRGRP - S_IWGRP = _stat.S_IWGRP - S_IXGRP = _stat.S_IXGRP - S_IRWXO = _stat.S_IRWXO - S_IROTH = _stat.S_IROTH - S_IWOTH = _stat.S_IWOTH - S_IXOTH = _stat.S_IXOTH - S_ISUID = _stat.S_ISUID - S_ISGID = _stat.S_ISGID - S_ISVTX = _stat.S_ISVTX - - # 文件类型常量 - S_IFMT = _stat.S_IFMT - S_IFSOCK = _stat.S_IFSOCK - S_IFLNK = _stat.S_IFLNK - S_IFREG = _stat.S_IFREG - S_IFBLK = _stat.S_IFBLK - S_IFDIR = _stat.S_IFDIR - S_IFCHR = _stat.S_IFCHR - S_IFIFO = _stat.S_IFIFO - - # 标准文件描述符 - STDIN_FILENO = 0 - STDOUT_FILENO = 1 - STDERR_FILENO = 2 - - # 信号常量 - SIGHUP = 1 - SIGINT = 2 - SIGQUIT = 3 - SIGILL = 4 - SIGTRAP = 5 - SIGABRT = 6 - SIGBUS = 7 - SIGFPE = 8 - SIGKILL = 9 - SIGUSR1 = 10 - SIGSEGV = 11 - SIGUSR2 = 12 - SIGPIPE = 13 - SIGALRM = 14 - SIGTERM = 15 - SIGCHLD = 17 - SIGCONT = 18 - SIGSTOP = 19 - SIGTSTP = 20 - SIGTTIN = 21 - SIGTTOU = 22 - - # errno 常量 (使用 getattr 处理 Windows 上不存在的常量) - EPERM = errno.EPERM - ENOENT = errno.ENOENT - ESRCH = errno.ESRCH - EINTR = errno.EINTR - EIO = errno.EIO - ENXIO = errno.ENXIO - E2BIG = errno.E2BIG - ENOEXEC = errno.ENOEXEC - EBADF = errno.EBADF - ECHILD = errno.ECHILD - EAGAIN = errno.EAGAIN - ENOMEM = errno.ENOMEM - EACCES = errno.EACCES - EFAULT = errno.EFAULT - ENOTBLK = getattr(errno, 'ENOTBLK', 15) # Windows 不支持 - EBUSY = errno.EBUSY - EEXIST = errno.EEXIST - EXDEV = errno.EXDEV - ENODEV = errno.ENODEV - ENOTDIR = errno.ENOTDIR - EISDIR = errno.EISDIR - EINVAL = errno.EINVAL - ENFILE = errno.ENFILE - EMFILE = errno.EMFILE - ENOTTY = errno.ENOTTY - ETXTBSY = getattr(errno, 'ETXTBSY', 139) - EFBIG = errno.EFBIG - ENOSPC = errno.ENOSPC - ESPIPE = errno.ESPIPE - EROFS = errno.EROFS - EMLINK = errno.EMLINK - EPIPE = errno.EPIPE - EDOM = errno.EDOM - ERANGE = errno.ERANGE - EDEADLK = errno.EDEADLK - ENAMETOOLONG = errno.ENAMETOOLONG - ENOLCK = errno.ENOLCK - ENOSYS = errno.ENOSYS - ENOTEMPTY = errno.ENOTEMPTY - ELOOP = errno.ELOOP - ENOMSG = errno.ENOMSG - EIDRM = errno.EIDRM - ENOSTR = getattr(errno, 'ENOSTR', 125) - ENODATA = getattr(errno, 'ENODATA', 120) - ETIME = getattr(errno, 'ETIME', 137) - ENOSR = getattr(errno, 'ENOSR', 124) - EREMOTE = getattr(errno, 'EREMOTE', 10071) - ENOLINK = getattr(errno, 'ENOLINK', 121) - EPROTO = errno.EPROTO - EMULTIHOP = getattr(errno, 'EMULTIHOP', 10072) # Windows 不支持 - EBADMSG = getattr(errno, 'EBADMSG', 104) - EOVERFLOW = getattr(errno, 'EOVERFLOW', 132) - EILSEQ = errno.EILSEQ - ENOTSOCK = errno.ENOTSOCK - EDESTADDRREQ = errno.EDESTADDRREQ - EMSGSIZE = errno.EMSGSIZE - EPROTOTYPE = errno.EPROTOTYPE - ENOPROTOOPT = errno.ENOPROTOOPT - EPROTONOSUPPORT = errno.EPROTONOSUPPORT - ESOCKTNOSUPPORT = getattr(errno, 'ESOCKTNOSUPPORT', 10044) - EOPNOTSUPP = getattr(errno, 'EOPNOTSUPP', 10045) - EPFNOSUPPORT = getattr(errno, 'EPFNOSUPPORT', 10046) - EAFNOSUPPORT = getattr(errno, 'EAFNOSUPPORT', 10047) - EADDRINUSE = errno.EADDRINUSE - EADDRNOTAVAIL = errno.EADDRNOTAVAIL - ENETDOWN = errno.ENETDOWN - ENETUNREACH = errno.ENETUNREACH - ENETRESET = errno.ENETRESET - ECONNABORTED = errno.ECONNABORTED - ECONNRESET = errno.ECONNRESET - ENOBUFS = errno.ENOBUFS - EISCONN = errno.EISCONN - ENOTCONN = errno.ENOTCONN - ESHUTDOWN = getattr(errno, 'ESHUTDOWN', 10058) - ETOOMANYREFS = getattr(errno, 'ETOOMANYREFS', 10059) - ETIMEDOUT = errno.ETIMEDOUT - ECONNREFUSED = errno.ECONNREFUSED - EHOSTDOWN = getattr(errno, 'EHOSTDOWN', 10064) - EHOSTUNREACH = errno.EHOSTUNREACH - EALREADY = errno.EALREADY - EINPROGRESS = errno.EINPROGRESS - ESTALE = getattr(errno, 'ESTALE', 10070) - EUCLEAN = getattr(errno, 'EUCLEAN', 117) # Windows 不支持 - ENOTNAM = getattr(errno, 'ENOTNAM', 118) # Windows 不支持 - ENAVAIL = getattr(errno, 'ENAVAIL', 119) # Windows 不支持 - EISNAM = getattr(errno, 'EISNAM', 120) # Windows 不支持 - EREMOTEIO = getattr(errno, 'EREMOTEIO', 121) # Windows 不支持 - EDQUOT = getattr(errno, 'EDQUOT', 10069) - ENOMEDIUM = getattr(errno, 'ENOMEDIUM', 123) # Windows 不支持 - EMEDIUMTYPE = getattr(errno, 'EMEDIUMTYPE', 124) # Windows 不支持 - ECANCELED = getattr(errno, 'ECANCELED', 105) - ENOKEY = getattr(errno, 'ENOKEY', 126) # Windows 不支持 - EKEYEXPIRED = getattr(errno, 'EKEYEXPIRED', 127) # Windows 不支持 - EKEYREVOKED = getattr(errno, 'EKEYREVOKED', 128) # Windows 不支持 - EKEYREJECTED = getattr(errno, 'EKEYREJECTED', 129) # Windows 不支持 - EOWNERDEAD = getattr(errno, 'EOWNERDEAD', 133) - ENOTRECOVERABLE = getattr(errno, 'ENOTRECOVERABLE', 127) - ERFKILL = getattr(errno, 'ERFKILL', 132) # Windows 不支持 - EHWPOISON = getattr(errno, 'EHWPOISON', 1001) # Windows 不支持 - - def __init__(self): - super().__init__() - - -# ═══════════════════════════════════════════════════════════════ -# StructStat -# ═══════════════════════════════════════════════════════════════ - -class StructStat(Stub): - """StructStat — stat() 返回的文件状态结构体""" - - def __init__(self, st_mode: int = 0, st_ino: int = 0, st_dev: int = 0, - st_nlink: int = 0, st_uid: int = 0, st_gid: int = 0, - st_size: int = 0, st_atime: int = 0, st_mtime: int = 0, - st_ctime: int = 0, st_blksize: int = 4096, - st_blocks: int = 0, st_rdev: int = 0): - super().__init__() - self.st_mode = st_mode - self.st_ino = st_ino - self.st_dev = st_dev - self.st_nlink = st_nlink - self.st_uid = st_uid - self.st_gid = st_gid - self.st_size = st_size - self.st_atime = st_atime - self.st_mtime = st_mtime - self.st_ctime = st_ctime - self.st_blksize = st_blksize - self.st_blocks = st_blocks - self.st_rdev = st_rdev - self.st_atime_ns = st_atime * 1_000_000_000 - self.st_mtime_ns = st_mtime * 1_000_000_000 - self.st_ctime_ns = st_ctime * 1_000_000_000 - - @staticmethod - def from_os_stat(st: _os.stat_result) -> "StructStat": - """从 Python os.stat_result 创建 StructStat""" - return StructStat( - st_mode=st.st_mode, - st_ino=st.st_ino, - st_dev=st.st_dev, - st_nlink=st.st_nlink, - st_uid=st.st_uid, - st_gid=st.st_gid, - st_size=st.st_size, - st_atime=int(st.st_atime), - st_mtime=int(st.st_mtime), - st_ctime=int(st.st_ctime), - st_blksize=st.st_blksize, - st_blocks=st.st_blocks, - st_rdev=st.st_rdev, - ) - - def toString(self) -> str: - return (f"StructStat(st_mode={oct(self.st_mode)}, st_size={self.st_size}, " - f"st_mtime={self.st_mtime}, st_uid={self.st_uid}, st_gid={self.st_gid})") - - def hashCode(self) -> int: - return hash((self.st_dev, self.st_ino, self.st_mode)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, StructStat): - return False - return (self.st_dev == other.st_dev and self.st_ino == other.st_ino - and self.st_mode == other.st_mode and self.st_size == other.st_size) - - -# ═══════════════════════════════════════════════════════════════ -# StructUtsname -# ═══════════════════════════════════════════════════════════════ - -class StructUtsname(Stub): - """StructUtsname — uname() 返回的系统信息结构体""" - - def __init__(self, sysname: str = "", nodename: str = "", - release: str = "", version: str = "", machine: str = ""): - super().__init__() - self.sysname = sysname or platform.system() - self.nodename = nodename or platform.node() - self.release = release or platform.release() - self.version = version or platform.version() - self.machine = machine or platform.machine() - - @staticmethod - def from_platform() -> "StructUtsname": - """从 Python platform 模块创建 StructUtsname""" - uname = platform.uname() - return StructUtsname( - sysname=uname.system, - nodename=uname.node, - release=uname.release, - version=uname.version, - machine=uname.machine, - ) - - def toString(self) -> str: - return (f"StructUtsname(sysname={self.sysname}, nodename={self.nodename}, " - f"release={self.release}, version={self.version}, machine={self.machine})") - - def hashCode(self) -> int: - return hash((self.sysname, self.nodename, self.release)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, StructUtsname): - return False - return (self.sysname == other.sysname and self.nodename == other.nodename - and self.release == other.release) - - -# ═══════════════════════════════════════════════════════════════ -# Os -# ═══════════════════════════════════════════════════════════════ - -class Os(Stub): - """Os — 系统调用包装器,代理到 Python os 模块""" - - def __init__(self): - super().__init__() - - # ── 文件操作 ── - - @staticmethod - def stat(path: str) -> StructStat: - """获取文件状态""" - st = _os.stat(path) - return StructStat.from_os_stat(st) - - @staticmethod - def lstat(path: str) -> StructStat: - """获取符号链接自身状态""" - st = _os.lstat(path) - return StructStat.from_os_stat(st) - - @staticmethod - def chmod(path: str, mode: int): - """修改文件权限""" - _os.chmod(path, mode) - - @staticmethod - def chown(path: str, uid: int, gid: int): - """修改文件所有者""" - _os.chown(path, uid, gid) - - @staticmethod - def link(oldpath: str, newpath: str): - """创建硬链接""" - _os.link(oldpath, newpath) - - @staticmethod - def symlink(target: str, linkpath: str): - """创建符号链接""" - _os.symlink(target, linkpath) - - @staticmethod - def readlink(path: str) -> str: - """读取符号链接目标""" - return _os.readlink(path) - - @staticmethod - def unlink(pathname: str): - """删除文件(解除链接)""" - _os.unlink(pathname) - - @staticmethod - def remove(path: str): - """删除文件""" - _os.remove(path) - - @staticmethod - def rename(oldpath: str, newpath: str): - """重命名文件""" - _os.rename(oldpath, newpath) - - @staticmethod - def mkdir(path: str, mode: int = 0o777): - """创建目录""" - _os.mkdir(path, mode) - - @staticmethod - def rmdir(path: str): - """删除空目录""" - _os.rmdir(path) - - @staticmethod - def listdir(path: str) -> List[str]: - """列出目录内容""" - return _os.listdir(path) - - @staticmethod - def truncate(path: str, length: int): - """截断文件到指定长度""" - with open(path, 'ab') as f: - f.truncate(length) - - @staticmethod - def access(path: str, mode: int) -> bool: - """检查文件访问权限""" - return _os.access(path, mode) - - # ── 文件描述符操作 ── - - @staticmethod - def dup(oldfd: int) -> int: - """复制文件描述符""" - return _os.dup(oldfd) - - @staticmethod - def dup2(oldfd: int, newfd: int): - """复制文件描述符到指定值""" - _os.dup2(oldfd, newfd) - - @staticmethod - def close(fd: int): - """关闭文件描述符""" - _os.close(fd) - - @staticmethod - def read(fd: int, count: int) -> bytes: - """从文件描述符读取""" - return _os.read(fd, count) - - @staticmethod - def write(fd: int, data: bytes) -> int: - """写入文件描述符""" - return _os.write(fd, data) - - @staticmethod - def lseek(fd: int, offset: int, whence: int) -> int: - """移动文件读写位置""" - return _os.lseek(fd, offset, whence) - - @staticmethod - def fsync(fd: int): - """同步文件描述符""" - _os.fsync(fd) - - @staticmethod - def fstat(fd: int) -> StructStat: - """获取文件描述符对应文件状态""" - st = _os.fstat(fd) - return StructStat.from_os_stat(st) - - @staticmethod - def isatty(fd: int) -> bool: - """判断文件描述符是否为终端""" - return _os.isatty(fd) - - # ── 进程相关 ── - - @staticmethod - def getpid() -> int: - """获取当前进程 ID""" - return _os.getpid() - - @staticmethod - def getppid() -> int: - """获取父进程 ID""" - return _os.getppid() - - @staticmethod - def getuid() -> int: - """获取当前用户 ID""" - return _os.getuid() - - @staticmethod - def getgid() -> int: - """获取当前组 ID""" - return _os.getgid() - - @staticmethod - def geteuid() -> int: - """获取有效用户 ID""" - return _os.geteuid() - - @staticmethod - def getegid() -> int: - """获取有效组 ID""" - return _os.getegid() - - @staticmethod - def setuid(uid: int): - """设置用户 ID""" - _os.setuid(uid) - - @staticmethod - def setgid(gid: int): - """设置组 ID""" - _os.setgid(gid) - - @staticmethod - def setsid() -> int: - """创建新会话""" - return _os.setsid() - - @staticmethod - def umask(mask: int) -> int: - """设置文件创建掩码""" - return _os.umask(mask) - - @staticmethod - def kill(pid: int, sig: int): - """发送信号给进程""" - _os.kill(pid, sig) - - @staticmethod - def getenv(name: str, default: Optional[str] = None) -> Optional[str]: - """获取环境变量""" - return _os.environ.get(name, default) - - @staticmethod - def setenv(name: str, value: str, overwrite: bool = True): - """设置环境变量""" - if overwrite or name not in _os.environ: - _os.environ[name] = value - - @staticmethod - def unsetenv(name: str): - """删除环境变量""" - _os.environ.pop(name, None) - - @staticmethod - def environ() -> Dict[str, str]: - """获取环境变量字典""" - return dict(_os.environ) - - # ── 系统信息 ── - - @staticmethod - def uname() -> StructUtsname: - """获取系统信息""" - return StructUtsname.from_platform() - - @staticmethod - def sysconf(name: str) -> int: - """获取系统配置值""" - conf_names = { - "SC_CLK_TCK": 100, - "SC_PAGE_SIZE": 4096, - "SC_NPROCESSORS_CONF": _os.cpu_count() or 1, - "SC_NPROCESSORS_ONLN": _os.cpu_count() or 1, - } - return conf_names.get(name, -1) - - @staticmethod - def getpagesize() -> int: - """获取内存页大小""" - return 4096 - - @staticmethod - def getloadavg() -> List[float]: - """获取系统负载平均值""" - try: - return list(_os.getloadavg()) - except AttributeError: - return [0.0, 0.0, 0.0] - - # ── 管道与 I/O ── - - @staticmethod - def pipe() -> Tuple[int, int]: - """创建管道""" - return _os.pipe() - - @staticmethod - def mkfifo(path: str, mode: int = 0o666): - """创建命名管道 (FIFO)""" - _os.mkfifo(path, mode) - - @staticmethod - def tmpfile() -> str: - """创建临时文件路径""" - import tempfile - return tempfile.mktemp() - - # ── 时间 ── - - @staticmethod - def time() -> int: - """获取当前时间戳 (秒)""" - import time - return int(time.time()) - - # ── 其他 ── - - @staticmethod - def strerror(errno: int) -> str: - """将错误码转为错误描述""" - return _os.strerror(errno) - - def toString(self) -> str: - return f"Os(sysname={platform.system()}, release={platform.release()})" - - def hashCode(self) -> int: - return hash("android.system.Os") & 0x7FFFFFFF - - def equals(self, other) -> bool: - return isinstance(other, Os) - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Os", - "StructStat", - "StructUtsname", - "OsConstants", -] \ No newline at end of file diff --git a/android_pyapi/android/telecom.py b/android_pyapi/android/telecom.py deleted file mode 100644 index f25dab4..0000000 --- a/android_pyapi/android/telecom.py +++ /dev/null @@ -1,737 +0,0 @@ -""" -Android Telecom API — TelecomManager, Call, CallAudioState, DisconnectCause, PhoneAccount, Connection -方法签名完全对齐 Android SDK -""" - -import sys -import time -from typing import Any, Dict, List, Optional, Set, Tuple, Callable - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "TelecomManager", - "Call", - "CallAudioState", - "DisconnectCause", - "PhoneAccount", - "Connection", -] - - -# ═══════════════════════════════════════════════════════════════ -# TelecomManager -# ═══════════════════════════════════════════════════════════════ - -class TelecomManager(Stub): - """TelecomManager — 电信管理器 (桌面存根)""" - - ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER" - ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS" - ACTION_SETTINGS = "android.telecom.action.SETTINGS" - ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION" - ACTION_CALL_FORWARDING = "android.telecom.action.CALL_FORWARDING" - ACTION_DEFAULT_CALL_SCREENING_APP = "android.telecom.action.DEFAULT_CALL_SCREENING_APP" - ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED" - ACTION_NEW_UNKNOWN_CALL = "android.telecom.action.NEW_UNKNOWN_CALL" - ACTION_TTY_PREFERRED = "android.telecom.action.TTY_PREFERRED" - ACTION_TTY_ENABLED = "android.telecom.action.TTY_ENABLED" - ACTION_TTY_DISABLED = "android.telecom.action.TTY_DISABLED" - ACTION_CONNECTION_SERVICE_CONFIGURE = "android.telecom.action.CONNECTION_SERVICE_CONFIGURE" - ACTION_CALL_DIAGNOSTIC_STATE_CHANGED = "android.telecom.action.CALL_DIAGNOSTIC_STATE_CHANGED" - DURATION_SHORT = "short" - DURATION_LONG = "long" - EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER" - EXTRA_CALL_CREATED_TIME_MILLIS = "android.telecom.extra.CALL_CREATED_TIME_MILLIS" - EXTRA_CALL_TECHNOLOGY_TYPE = "android.telecom.extra.CALL_TECHNOLOGY_TYPE" - EXTRA_CALL_NETWORK_TYPE = "android.telecom.extra.CALL_NETWORK_TYPE" - EXTRA_CALL_SUBSTATE = "android.telecom.extra.CALL_SUBSTATE" - EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME" - EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE" - EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE" - EXTRA_DISCONNECT_CAUSE_DESCRIPTION = "android.telecom.extra.DISCONNECT_CAUSE_DESCRIPTION" - EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE" - EXTRA_PRIORITY = "android.telecom.extra.PRIORITY" - EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE" - EXTRA_HANDOVER_FROM = "android.telecom.extra.HANDOVER_FROM" - EXTRA_HANDOVER_TO = "android.telecom.extra.HANDOVER_TO" - EXTRA_IS_HANDOVER = "android.telecom.extra.IS_HANDOVER" - TTY_MODE_OFF = 0 - TTY_MODE_FULL = 1 - TTY_MODE_HCO = 2 - TTY_MODE_VCO = 3 - PRESENTATION_ALLOWED = 1 - PRESENTATION_RESTRICTED = 2 - PRESENTATION_UNKNOWN = 3 - PRESENTATION_PAYPHONE = 4 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._default_dialer = "com.android.dialer" - self._phone_accounts: Dict[str, Any] = {} - - def getDefaultDialerPackage(self) -> str: - """获取默认拨号器包名""" - return self._default_dialer - - def getCurrentTtyMode(self) -> int: - """获取当前 TTY 模式""" - return self.TTY_MODE_OFF - - def getCallCapablePhoneAccounts(self) -> List[Any]: - """获取可通话的电话账户""" - return list(self._phone_accounts.values()) - - def getPhoneAccount(self, accountHandle: Any) -> Optional[Any]: - """获取指定电话账户""" - return self._phone_accounts.get(str(accountHandle)) - - def getPhoneAccountList(self) -> List[Any]: - """获取电话账户列表""" - return list(self._phone_accounts.values()) - - def getSimConfig(self, accountHandle: Any) -> Optional[Any]: - """获取 SIM 配置""" - return None - - def registerPhoneAccount(self, account: "PhoneAccount"): - """注册电话账户""" - self._phone_accounts[str(account.getAccountHandle())] = account - - def unregisterPhoneAccount(self, accountHandle: Any): - """注销电话账户""" - self._phone_accounts.pop(str(accountHandle), None) - - def clearPhoneAccounts(self): - """清除所有电话账户""" - self._phone_accounts.clear() - - def clearAccounts(self): - """清除所有账户""" - self._phone_accounts.clear() - - def showInCallScreen(self, showDialpad: bool): - """显示通话界面""" - pass - - def acceptRingingCall(self): - """接听响铃呼叫""" - pass - - def silenceRinger(self): - """静音铃声""" - pass - - def isInCall(self) -> bool: - """检查是否正在通话""" - return False - - def isInManagedCall(self) -> bool: - """检查是否在托管通话中""" - return False - - def addNewIncomingCall(self, phoneAccount: "PhoneAccount", extras: Any = None): - """添加新的来电""" - pass - - def addNewUnknownCall(self, phoneAccount: "PhoneAccount", extras: Any = None): - """添加新的未知呼叫""" - pass - - def handleCreateConferenceComplete( - self, callId: str, response: Any - ): - """处理创建会议完成""" - pass - - def transferCall(self, callId: str, targetPhoneAccount: Any): - """转移呼叫""" - pass - - def setUserSelectedOutgoingPhoneAccount( - self, accountHandle: Any - ): - """设置用户选择的呼出电话账户""" - pass - - def toString(self) -> str: - return f"TelecomManager@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# Call -# ═══════════════════════════════════════════════════════════════ - -class Call(Stub): - """Call — 通话 (桌面存根)""" - - STATE_ACTIVE = 1 - STATE_HOLDING = 2 - STATE_DIALING = 3 - STATE_RINGING = 4 - STATE_CONNECTING = 5 - STATE_DISCONNECTED = 6 - STATE_DISCONNECTING = 7 - STATE_SELECT_PHONE_ACCOUNT = 8 - STATE_NEW = 9 - STATE_PULLING_CALL = 10 - - def __init__(self): - super().__init__() - self._state = self.STATE_NEW - self._details: Optional["Call.Details"] = None - self._conferenceable_calls: List["Call"] = [] - self._callbacks: List[Callable] = [] - self._listeners: List[Callable] = [] - - def getState(self) -> int: - return self._state - - def getDetails(self) -> Optional["Call.Details"]: - return self._details - - def getConferenceableCalls(self) -> List["Call"]: - return list(self._conferenceable_calls) - - def registerCallback(self, callback: Callable): - if callback not in self._callbacks: - self._callbacks.append(callback) - - def unregisterCallback(self, callback: Callable): - if callback in self._callbacks: - self._callbacks.remove(callback) - - def addListener(self, listener: Callable): - if listener not in self._listeners: - self._listeners.append(listener) - - def removeListener(self, listener: Callable): - if listener in self._listeners: - self._listeners.remove(listener) - - def answer(self, videoState: int = 0): - """接听通话""" - self._state = self.STATE_ACTIVE - - def reject(self, rejectWithMessage: bool = False, textMessage: str = ""): - """拒绝通话""" - self._state = self.STATE_DISCONNECTED - - def disconnect(self): - """挂断通话""" - self._state = self.STATE_DISCONNECTED - - def hold(self): - """保持通话""" - self._state = self.STATE_HOLDING - - def unhold(self): - """取消保持""" - self._state = self.STATE_ACTIVE - - def playDtmfTone(self, digit: str): - """播放 DTMF 音""" - pass - - def stopDtmfTone(self): - """停止 DTMF 音""" - pass - - def postDialWait(self, remaining: str): - """后拨号等待""" - pass - - def mergeConference(self): - """合并会议""" - pass - - def splitFromConference(self): - """从会议分离""" - pass - - def sendCallEvent(self, event: str, extras: Any = None): - """发送通话事件""" - pass - - def putExtra(self, key: str, value: Any): - """设置额外信息""" - pass - - # ═══════════════════════════════════════════════════════════ - # Details - # ═══════════════════════════════════════════════════════════ - - class Details(Stub): - """Call.Details — 通话详细信息""" - - DIRECTION_INCOMING = 1 - DIRECTION_OUTGOING = 2 - - CAPABILITY_HOLD = 1 - CAPABILITY_SUPPORT_HOLD = 2 - CAPABILITY_MERGE_CONFERENCE = 4 - CAPABILITY_SWAP_CONFERENCE = 8 - CAPABILITY_RESPOND_VIA_TEXT = 16 - CAPABILITY_MUTE = 32 - CAPABILITY_MANAGE_CONFERENCE = 64 - CAPABILITY_SUPPORTS_VT_LOCAL = 128 - CAPABILITY_SUPPORTS_VT_REMOTE = 256 - CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL = 512 - CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 1024 - CAPABILITY_SUPPORTS_DOWNGRADE_VT_TO_AUDIO = 2048 - CAPABILITY_SUPPORTS_VIDEO_CALLING = 4096 - CAPABILITY_SUPPORTS_VT_LOCAL_RX = 8192 - CAPABILITY_SUPPORTS_VT_LOCAL_TX = 16384 - CAPABILITY_SUPPORTS_VT_REMOTE_RX = 32768 - CAPABILITY_SUPPORTS_VT_REMOTE_TX = 65536 - CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 131072 - CAPABILITY_CAN_PAUSE_VIDEO = 262144 - CAPABILITY_SELF_MANAGED = 524288 - CAPABILITY_SUPPORTS_VOIP = 1048576 - CAPABILITY_ADD_PARTICIPANT = 2097152 - CAPABILITY_CAN_UPGRADE_TO_VIDEO = 4194304 - - PROPERTY_HIGH_DEF_AUDIO = 1 - PROPERTY_WIFI = 2 - PROPERTY_EMERGENCY_CALLBACK = 4 - PROPERTY_IS_EXTERNAL_CALL = 8 - PROPERTY_HAS_CDMA_VOICE_PRIVACY = 16 - PROPERTY_SELF_MANAGED = 32 - PROPERTY_ASSISTED_DIALING = 64 - PROPERTY_NETWORK_IDENTIFIED = 128 - PROPERTY_CALL_AUTO_FORWARDED = 256 - PROPERTY_CALL_TYPE_UNKNOWN = 512 - PROPERTY_IS_VOIP_AUDIO_MODE = 1024 - PROPERTY_IS_DOWNGRADED_CALL = 2048 - PROPERTY_ENTITLEMENT_CHANNEL = 4096 - PROPERTY_PULLED_CALL = 8192 - PROPERTY_CROSS_SIM = 16384 - PROPERTY_VIDEO_CALL = 32768 - PROPERTY_RTT = 65536 - - def __init__(self): - super().__init__() - self._handle: Any = None - self._gateway_info: Any = None - self._caller_display_name: str = "" - self._call_direction = self.DIRECTION_INCOMING - self._account_handle: Any = None - self._call_capabilities = 0 - self._call_properties = 0 - self._caller_info: Any = None - self._creation_time_millis = 0 - self._connect_time_millis = 0 - self._disconnect_cause: Any = None - self._video_state = 0 - self._intent_extras: Any = None - self._extras: Any = None - - def getHandle(self) -> Optional[Any]: - return self._handle - - def getGatewayInfo(self) -> Optional[Any]: - return self._gateway_info - - def getCallerDisplayName(self) -> str: - return self._caller_display_name - - def getCallDirection(self) -> int: - return self._call_direction - - def getAccountHandle(self) -> Optional[Any]: - return self._account_handle - - def getCallCapabilities(self) -> int: - return self._call_capabilities - - def getCallProperties(self) -> int: - return self._call_properties - - def getCallerInfo(self) -> Optional[Any]: - return self._caller_info - - def getCreationTimeMillis(self) -> int: - return self._creation_time_millis - - def getConnectTimeMillis(self) -> int: - return self._connect_time_millis - - def getDisconnectCause(self) -> Optional["DisconnectCause"]: - return self._disconnect_cause - - def getVideoState(self) -> int: - return self._video_state - - def getIntentExtras(self) -> Optional[Any]: - return self._intent_extras - - def getExtras(self) -> Optional[Any]: - return self._extras - - def toString(self) -> str: - return f"Call.Details(handle={self._handle})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def toString(self) -> str: - return f"Call(state={self._state})" - - def equals(self, other) -> bool: - if not isinstance(other, Call): - return False - return self._state == other._state and self._details == other._details - - def hashCode(self) -> int: - return self._state & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# CallAudioState -# ═══════════════════════════════════════════════════════════════ - -class CallAudioState(Stub): - """CallAudioState — 通话音频状态""" - - ROUTE_EARPIECE = 1 - ROUTE_SPEAKER = 2 - ROUTE_BLUETOOTH = 4 - ROUTE_WIRED_HEADSET = 8 - ROUTE_WIRED_OR_EARPIECE = 9 - - def __init__(self): - super().__init__() - self._route = self.ROUTE_EARPIECE - self._supported_route_mask = ( - self.ROUTE_EARPIECE | self.ROUTE_SPEAKER | self.ROUTE_WIRED_HEADSET - ) - - def getRoute(self) -> int: - return self._route - - def getSupportedRouteMask(self) -> int: - return self._supported_route_mask - - def toString(self) -> str: - return f"CallAudioState(route={self._route})" - - def equals(self, other) -> bool: - if not isinstance(other, CallAudioState): - return False - return self._route == other._route - - def hashCode(self) -> int: - return self._route & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# DisconnectCause -# ═══════════════════════════════════════════════════════════════ - -class DisconnectCause(Stub): - """DisconnectCause — 断连原因""" - - LOCAL = 1 - REMOTE = 2 - BUSY = 3 - CANCELED = 4 - NO_ANSWER = 5 - MISSED = 6 - ERROR = 7 - REJECTED = 8 - NUMBER_UNREACHABLE = 9 - RESTRICTED = 10 - OTHER = 11 - ANSWERED_ELSEWHERE = 12 - CALL_PULLED = 13 - - def __init__(self, causeCode: int = OTHER): - super().__init__() - self._cause_code = causeCode - self._reason: str = "" - self._description: str = "" - - def getCode(self) -> int: - return self._cause_code - - def getReason(self) -> str: - return self._reason - - def getDescription(self) -> str: - return self._description - - def toString(self) -> str: - return f"DisconnectCause(code={self._cause_code})" - - def equals(self, other) -> bool: - if not isinstance(other, DisconnectCause): - return False - return self._cause_code == other._cause_code - - def hashCode(self) -> int: - return self._cause_code & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# PhoneAccount -# ═══════════════════════════════════════════════════════════════ - -class PhoneAccount(Stub): - """PhoneAccount — 电话账户""" - - CAP_HOLD = 1 - CAP_ADD_CALL = 2 - CAP_MULTI_ENDPOINT = 4 - CAP_CONFERENCE = 8 - CAP_VIDEO_CALLING = 16 - CAP_CALL_COMPOSER = 32 - CAP_CONNECTION_MANAGER = 64 - - EXTRA_LOG_SELF_MANAGED_CALLS = "android.telecom.extra.LOG_SELF_MANAGED_CALLS" - EXTRA_ADD_SELF_MANAGED_CALLS = "android.telecom.extra.ADD_SELF_MANAGED_CALLS" - - SCREENING_ALLOW = 0 - SCREENING_STOP = 1 - SCREENING_SILENCE = 2 - SCREENING_DISALLOW = 3 - - SCHEME_SIP = "sip" - SCHEME_TEL = "tel" - SCHEME_VOICEMAIL = "voicemail" - - class Builder(Stub): - """PhoneAccount.Builder — 电话账户构建器""" - - def __init__(self, accountHandle: Any, label: str): - super().__init__() - self._account = PhoneAccount() - self._account._account_handle = accountHandle - self._account._label = label - - def setCapabilities(self, capabilities: int) -> "PhoneAccount.Builder": - self._account._capabilities = capabilities - return self - - def setAddress(self, address: Any) -> "PhoneAccount.Builder": - self._account._address = address - return self - - def setSubscriptionAddress(self, address: Any) -> "PhoneAccount.Builder": - self._account._subscription_address = address - return self - - def setSupportedUriSchemes( - self, uriSchemes: List[str] - ) -> "PhoneAccount.Builder": - self._account._supported_uri_schemes = list(uriSchemes) - return self - - def setIcon(self, icon: Any) -> "PhoneAccount.Builder": - self._account._icon = icon - return self - - def setHighlightColor(self, color: int) -> "PhoneAccount.Builder": - self._account._highlight_color = color - return self - - def setShortDescription(self, description: str) -> "PhoneAccount.Builder": - self._account._short_description = description - return self - - def addSupportedUriScheme( - self, uriScheme: str - ) -> "PhoneAccount.Builder": - if uriScheme not in self._account._supported_uri_schemes: - self._account._supported_uri_schemes.append(uriScheme) - return self - - def setExtras(self, extras: Any) -> "PhoneAccount.Builder": - self._account._extras = extras - return self - - def build(self) -> "PhoneAccount": - return self._account - - def toString(self) -> str: - return f"PhoneAccount.Builder(label={self._account._label})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def __init__(self): - super().__init__() - self._account_handle: Any = None - self._label: str = "" - self._capabilities: int = 0 - self._address: Any = None - self._subscription_address: Any = None - self._supported_uri_schemes: List[str] = [self.SCHEME_TEL] - self._icon: Any = None - self._highlight_color: int = 0 - self._short_description: str = "" - self._extras: Any = None - - def getAccountHandle(self) -> Optional[Any]: - return self._account_handle - - def getLabel(self) -> str: - return self._label - - def getCapabilities(self) -> int: - return self._capabilities - - def getAddress(self) -> Optional[Any]: - return self._address - - def getSubscriptionAddress(self) -> Optional[Any]: - return self._subscription_address - - def getSupportedUriSchemes(self) -> List[str]: - return list(self._supported_uri_schemes) - - def getIcon(self) -> Optional[Any]: - return self._icon - - def getHighlightColor(self) -> int: - return self._highlight_color - - def getShortDescription(self) -> str: - return self._short_description - - def getExtras(self) -> Optional[Any]: - return self._extras - - def toString(self) -> str: - return f"PhoneAccount(label={self._label})" - - def equals(self, other) -> bool: - if not isinstance(other, PhoneAccount): - return False - return self._account_handle == other._account_handle - - def hashCode(self) -> int: - return hash(str(self._account_handle)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# Connection -# ═══════════════════════════════════════════════════════════════ - -class Connection(Stub): - """Connection — 通话连接 (桌面存根)""" - - STATE_INITIALIZING = 0 - STATE_ACTIVE = 1 - STATE_DIALING = 2 - STATE_RINGING = 3 - STATE_HOLDING = 4 - STATE_DISCONNECTED = 5 - STATE_PULLING_CALL = 6 - - CAPABILITY_HOLD = 1 - CAPABILITY_SUPPORT_HOLD = 2 - CAPABILITY_MERGE_CONFERENCE = 4 - CAPABILITY_SWAP_CONFERENCE = 8 - CAPABILITY_RESPOND_VIA_TEXT = 16 - CAPABILITY_MUTE = 32 - CAPABILITY_MANAGE_CONFERENCE = 64 - CAPABILITY_SUPPORTS_VT_LOCAL = 128 - CAPABILITY_SUPPORTS_VT_REMOTE = 256 - CAPABILITY_SUPPORTS_VIDEO_CALLING = 4096 - CAPABILITY_SELF_MANAGED = 524288 - CAPABILITY_SUPPORTS_VOIP = 1048576 - CAPABILITY_ADD_PARTICIPANT = 2097152 - - PROPERTY_SELF_MANAGED = 32 - PROPERTY_IS_VOIP_AUDIO_MODE = 1024 - PROPERTY_HIGH_DEF_AUDIO = 1 - PROPERTY_WIFI = 2 - - def __init__(self): - super().__init__() - self._state = self.STATE_INITIALIZING - self._video_state = 0 - self._connection_capabilities = 0 - self._connection_properties = 0 - self._audio_mode_is_voip = False - self._conferenceable_connections: List["Connection"] = [] - self._extras: Dict[str, Any] = {} - - def setVideoState(self, videoState: int): - self._video_state = videoState - - def setActive(self): - self._state = self.STATE_ACTIVE - - def setRinging(self): - self._state = self.STATE_RINGING - - def setDialing(self): - self._state = self.STATE_DIALING - - def setHolding(self): - self._state = self.STATE_HOLDING - - def setDisconnected(self, disconnectCause: "DisconnectCause"): - self._state = self.STATE_DISCONNECTED - - def setPostDialWait(self, remaining: str): - pass - - def setConferenceableConnections( - self, conferenceable: List["Connection"] - ): - self._conferenceable_connections = list(conferenceable) - - def setAudioModeIsVoip(self, isVoip: bool): - self._audio_mode_is_voip = isVoip - - def setConnectionCapabilities(self, capabilities: int): - self._connection_capabilities = capabilities - - def setConnectionProperties(self, properties: int): - self._connection_properties = properties - - def putExtra(self, key: str, value: Any): - self._extras[key] = value - - def onShowIncomingCallUi(self): - pass - - def onCallAudioStateChanged(self, audioState: "CallAudioState"): - pass - - def destroy(self): - self._state = self.STATE_DISCONNECTED - - def onCallFilteringCompleted(self, completion: Any): - pass - - def onSilence(self): - pass - - def toString(self) -> str: - return f"Connection(state={self._state})" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/telephony.py b/android_pyapi/android/telephony.py deleted file mode 100644 index ce54f90..0000000 --- a/android_pyapi/android/telephony.py +++ /dev/null @@ -1,709 +0,0 @@ -""" -Android Telephony 组件 — TelephonyManager, CellInfo, PhoneStateListener 等 -方法签名完全对齐 Android SDK -""" - -import os -import sys -import random -from typing import Any, Dict, List, Optional, Set, Tuple, Union - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) - -try: - from .._linux_impl import IS_LINUX, LinuxNetwork, LinuxProcess, LinuxLocation - _USE_LINUX = IS_LINUX -except ImportError: - _USE_LINUX = False - - -# ═══════════════════════════════════════════════════════════════ -# CellIdentity 基类集合 -# ═══════════════════════════════════════════════════════════════ - -class CellIdentity(Stub): - def __init__(self): - super().__init__() - - -class CellIdentityGsm(CellIdentity): - def __init__(self, mcc: int = 0, mnc: int = 0, lac: int = 0, cid: int = 0, arfcn: int = 0): - super().__init__() - self._mcc = mcc - self._mnc = mnc - self._lac = lac - self._cid = cid - self._arfcn = arfcn - - def getMcc(self) -> int: return self._mcc - def getMnc(self) -> int: return self._mnc - def getLac(self) -> int: return self._lac - def getCid(self) -> int: return self._cid - def getArfcn(self) -> int: return self._arfcn - - -class CellIdentityCdma(CellIdentity): - def __init__(self, network_id: int = 0, system_id: int = 0, basestation_id: int = 0, - latitude: int = 0, longitude: int = 0): - super().__init__() - self._network_id = network_id - self._system_id = system_id - self._basestation_id = basestation_id - self._latitude = latitude - self._longitude = longitude - - def getNetworkId(self) -> int: return self._network_id - def getSystemId(self) -> int: return self._system_id - def getBasestationId(self) -> int: return self._basestation_id - def getLatitude(self) -> int: return self._latitude - def getLongitude(self) -> int: return self._longitude - - -class CellIdentityLte(CellIdentity): - def __init__(self, mcc: int = 0, mnc: int = 0, ci: int = 0, pci: int = 0, tac: int = 0): - super().__init__() - self._mcc = mcc; self._mnc = mnc - self._ci = ci; self._pci = pci; self._tac = tac - - def getMcc(self) -> int: return self._mcc - def getMnc(self) -> int: return self._mnc - def getCi(self) -> int: return self._ci - def getPci(self) -> int: return self._pci - def getTac(self) -> int: return self._tac - - -class CellIdentityWcdma(CellIdentity): - def __init__(self, mcc: int = 0, mnc: int = 0, lac: int = 0, cid: int = 0, psc: int = 0): - super().__init__() - self._mcc = mcc; self._mnc = mnc - self._lac = lac; self._cid = cid; self._psc = psc - - def getMcc(self) -> int: return self._mcc - def getMnc(self) -> int: return self._mnc - def getLac(self) -> int: return self._lac - def getCid(self) -> int: return self._cid - def getPsc(self) -> int: return self._psc - - -class CellIdentityNr(CellIdentity): - def __init__(self, mcc: int = 0, mnc: int = 0, nci: int = 0, pci: int = 0, tac: int = 0): - super().__init__() - self._mcc = mcc; self._mnc = mnc - self._nci = nci; self._pci = pci; self._tac = tac - - def getMcc(self) -> int: return self._mcc - def getMnc(self) -> int: return self._mnc - def getNci(self) -> int: return self._nci - def getPci(self) -> int: return self._pci - def getTac(self) -> int: return self._tac - - -# ═══════════════════════════════════════════════════════════════ -# CellSignalStrength 基类集合 -# ═══════════════════════════════════════════════════════════════ - -class CellSignalStrength(Stub): - SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0 - SIGNAL_STRENGTH_POOR = 1 - SIGNAL_STRENGTH_MODERATE = 2 - SIGNAL_STRENGTH_GOOD = 3 - SIGNAL_STRENGTH_GREAT = 4 - - def getLevel(self) -> int: - return self.SIGNAL_STRENGTH_NONE_OR_UNKNOWN - - def getAsuLevel(self) -> int: - return 0 - - def getDbm(self) -> int: - return 0 - - -class CellSignalStrengthGsm(CellSignalStrength): - def __init__(self, rssi: int = 0, ber: int = 0, ta: int = 0): - super().__init__() - self._rssi = rssi; self._ber = ber; self._ta = ta - - def getRssi(self) -> int: return self._rssi - def getDbm(self) -> int: return self._rssi - 113 if self._rssi > 0 else 0 - - -class CellSignalStrengthCdma(CellSignalStrength): - def __init__(self, dbm: int = 0, ecio: int = 0, evdo_dbm: int = 0, evdo_ecio: int = 0, - evdo_snr: int = 0): - super().__init__() - self._dbm = dbm; self._ecio = ecio - self._evdo_dbm = evdo_dbm; self._evdo_ecio = evdo_ecio; self._evdo_snr = evdo_snr - - def getDbm(self) -> int: return self._dbm - - -class CellSignalStrengthLte(CellSignalStrength): - def __init__(self, rssi: int = 0, rsrp: int = 0, rsrq: int = 0, rssnr: int = 0, - cqi: int = 0, ta: int = 0): - super().__init__() - self._rssi = rssi; self._rsrp = rsrp; self._rsrq = rsrq - self._rssnr = rssnr; self._cqi = cqi; self._ta = ta - - def getRsrp(self) -> int: return self._rsrp - def getRsrq(self) -> int: return self._rsrq - def getRssnr(self) -> int: return self._rssnr - def getDbm(self) -> int: return self._rsrp - - -class CellSignalStrengthWcdma(CellSignalStrength): - def __init__(self, rscp: int = 0, ecno: int = 0): - super().__init__() - self._rscp = rscp; self._ecno = ecno - - def getRscp(self) -> int: return self._rscp - def getEcno(self) -> int: return self._ecno - def getDbm(self) -> int: return self._rscp - 113 if self._rscp > 0 else 0 - - -class CellSignalStrengthNr(CellSignalStrength): - def __init__(self, ssRsrp: int = 0, ssRsrq: int = 0, ssSinr: int = 0, csiRsrp: int = 0): - super().__init__() - self._ssRsrp = ssRsrp; self._ssRsrq = ssRsrq - self._ssSinr = ssSinr; self._csiRsrp = csiRsrp - - def getSsRsrp(self) -> int: return self._ssRsrp - def getDbm(self) -> int: return self._ssRsrp - - -# ═══════════════════════════════════════════════════════════════ -# CellInfo 基类集合 -# ═══════════════════════════════════════════════════════════════ - -class CellInfo(Stub): - UNAVAILABLE = 0x7FFFFFFF - TIMESTAMP_TYPE_UNKNOWN = 0 - TIMESTAMP_TYPE_GSM = 1 - TIMESTAMP_TYPE_UMTS = 2 - TIMESTAMP_TYPE_CDMA = 3 - TIMESTAMP_TYPE_LTE = 4 - TIMESTAMP_TYPE_WCDMA = 5 - TIMESTAMP_TYPE_NR = 6 - - def __init__(self): - super().__init__() - self._registered = False - self._timestamp = 0 - self._connection_status = 0 - - def isRegistered(self) -> bool: return self._registered - def getTimeStamp(self) -> int: return self._timestamp - def getCellConnectionStatus(self) -> int: return self._connection_status - - -class CellInfoGsm(CellInfo): - def __init__(self): - super().__init__() - self._identity = CellIdentityGsm() - self._signal_strength = CellSignalStrengthGsm() - - def getCellIdentity(self) -> CellIdentityGsm: return self._identity - def getCellSignalStrength(self) -> CellSignalStrengthGsm: return self._signal_strength - - -class CellInfoCdma(CellInfo): - def __init__(self): - super().__init__() - self._identity = CellIdentityCdma() - self._signal_strength = CellSignalStrengthCdma() - - def getCellIdentity(self) -> CellIdentityCdma: return self._identity - def getCellSignalStrength(self) -> CellSignalStrengthCdma: return self._signal_strength - - -class CellInfoLte(CellInfo): - def __init__(self): - super().__init__() - self._identity = CellIdentityLte() - self._signal_strength = CellSignalStrengthLte() - - def getCellIdentity(self) -> CellIdentityLte: return self._identity - def getCellSignalStrength(self) -> CellSignalStrengthLte: return self._signal_strength - - -class CellInfoWcdma(CellInfo): - def __init__(self): - super().__init__() - self._identity = CellIdentityWcdma() - self._signal_strength = CellSignalStrengthWcdma() - - def getCellIdentity(self) -> CellIdentityWcdma: return self._identity - def getCellSignalStrength(self) -> CellSignalStrengthWcdma: return self._signal_strength - - -class CellInfoNr(CellInfo): - def __init__(self): - super().__init__() - self._identity = CellIdentityNr() - self._signal_strength = CellSignalStrengthNr() - - def getCellIdentity(self) -> CellIdentityNr: return self._identity - def getCellSignalStrength(self) -> CellSignalStrengthNr: return self._signal_strength - - -# ═══════════════════════════════════════════════════════════════ -# PhoneStateListener -# ═══════════════════════════════════════════════════════════════ - -class PhoneStateListener(Stub): - LISTEN_NONE = 0 - LISTEN_SERVICE_STATE = 1 - LISTEN_SIGNAL_STRENGTHS = 2 - LISTEN_MESSAGE_WAITING_INDICATOR = 4 - LISTEN_CALL_FORWARDING_INDICATOR = 8 - LISTEN_CELL_LOCATION = 16 - LISTEN_CALL_STATE = 32 - LISTEN_DATA_CONNECTION_STATE = 64 - LISTEN_DATA_ACTIVITY = 128 - LISTEN_SIGNAL_STRENGTH = 256 - LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 512 - LISTEN_CELL_INFO = 1024 - LISTEN_PRECISE_CALL_STATE = 2048 - LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096 - LISTEN_DATA_CONNECTION_REAL_TIME_INFO = 8192 - LISTEN_OEM_HOOK_RAW_EVENT = 32768 - LISTEN_SRVCC_STATE_CHANGED = 65536 - LISTEN_CARRIER_NETWORK_CHANGE = 131072 - LISTEN_VOICE_ACTIVATION_STATE = 262144 - LISTEN_DATA_ACTIVATION_STATE = 524288 - LISTEN_USER_MOBILE_DATA_STATE = 1048576 - LISTEN_DISPLAY_INFO_CHANGED = 2097152 - LISTEN_PHONE_CAPABILITY_CHANGE = 4194304 - LISTEN_ACTIVE_PHONE_TYPE_CHANGE = 8388608 - LISTEN_EMERGENCY_NUMBER_LIST = 16777216 - LISTEN_EMERGENCY_NUMBER_SOURCE = 33554432 - LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864 - LISTEN_BARRING_INFO_CHANGED = 134217728 - LISTEN_ALL = 2147483647 - - def __init__(self, looper: Any = None): - super().__init__() - self._looper = looper - - def onServiceStateChanged(self, service_state: "ServiceState"): - pass - - def onSignalStrengthsChanged(self, signal_strength: "SignalStrength"): - pass - - def onMessageWaitingIndicatorChanged(self, mwi: bool): - pass - - def onCallForwardingIndicatorChanged(self, cfi: bool): - pass - - def onCellLocationChanged(self, location: Any): - pass - - def onCallStateChanged(self, state: int, phone_number: str): - pass - - def onDataConnectionStateChanged(self, state: int, network_type: int = 0): - pass - - def onDataActivity(self, direction: int): - pass - - def onSignalStrengthChanged(self, asu: int): - pass - - def onCellInfoChanged(self, cell_info: List[CellInfo]): - pass - - -# ═══════════════════════════════════════════════════════════════ -# ServiceState / SignalStrength -# ═══════════════════════════════════════════════════════════════ - -class ServiceState(Stub): - STATE_IN_SERVICE = 0 - STATE_OUT_OF_SERVICE = 1 - STATE_EMERGENCY_ONLY = 2 - STATE_POWER_OFF = 3 - - def __init__(self): - super().__init__() - self._state = self.STATE_OUT_OF_SERVICE - self._roaming = False - self._operator_alpha_long: str = "" - self._operator_alpha_short: str = "" - self._operator_numeric: str = "" - self._is_manual_selection = False - self._cdma_default_roaming_indicator = -1 - self._cdma_eri_icon_index = -1 - self._cdma_eri_icon_mode = -1 - self._cdma_roaming_indicator = -1 - self._channel_number = 0 - - def getState(self) -> int: return self._state - def setState(self, state: int): self._state = state - def getRoaming(self) -> bool: return self._roaming - def setRoaming(self, roaming: bool): self._roaming = roaming - def getOperatorAlphaLong(self) -> str: return self._operator_alpha_long - def getOperatorAlphaShort(self) -> str: return self._operator_alpha_short - def getOperatorNumeric(self) -> str: return self._operator_numeric - def getIsManualSelection(self) -> bool: return self._is_manual_selection - def getCdmaDefaultRoamingIndicator(self) -> int: return self._cdma_default_roaming_indicator - def getCdmaEriIconIndex(self) -> int: return self._cdma_eri_icon_index - def getCdmaEriIconMode(self) -> int: return self._cdma_eri_icon_mode - def getCdmaRoamingIndicator(self) -> int: return self._cdma_roaming_indicator - def getChannelNumber(self) -> int: return self._channel_number - def getNetworkRegistrationInfo(self, domain: int, transport_type: int) -> Any: - return None - def toString(self) -> str: - return f"ServiceState(state={self._state}, roaming={self._roaming})" - - -class SignalStrength(Stub): - NUM_SIGNAL_STRENGTH_BINS = 5 - SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0 - SIGNAL_STRENGTH_POOR = 1 - SIGNAL_STRENGTH_MODERATE = 2 - SIGNAL_STRENGTH_GOOD = 3 - SIGNAL_STRENGTH_GREAT = 4 - - def __init__(self): - super().__init__() - self._gsm_signal_strength = 0 - self._cdma_dbm = 0 - self._cdma_ecio = 0 - self._evdo_dbm = 0 - self._evdo_ecio = 0 - self._evdo_snr = 0 - self._lte_signal_strength = 0 - self._td_scdma_rscp = 0 - - def getGsmSignalStrength(self) -> int: return self._gsm_signal_strength - def getCdmaDbm(self) -> int: return self._cdma_dbm - def getCdmaEcio(self) -> int: return self._cdma_ecio - def getEvdoDbm(self) -> int: return self._evdo_dbm - def getEvdoEcio(self) -> int: return self._evdo_ecio - def getEvdoSnr(self) -> int: return self._evdo_snr - def getLteSignalStrength(self) -> int: return self._lte_signal_strength - def getLevel(self) -> int: return self.SIGNAL_STRENGTH_NONE_OR_UNKNOWN - def getAsuLevel(self) -> int: return 0 - def getDbm(self) -> int: return 0 - - -# ═══════════════════════════════════════════════════════════════ -# SmsManager -# ═══════════════════════════════════════════════════════════════ - -class SmsManager(Stub): - RESULT_ERROR_GENERIC_FAILURE = 1 - RESULT_ERROR_RADIO_OFF = 2 - RESULT_ERROR_NULL_PDU = 3 - RESULT_ERROR_NO_SERVICE = 4 - RESULT_ERROR_LIMIT_EXCEEDED = 5 - RESULT_ERROR_FDN_CHECK_FAILURE = 6 - RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 7 - RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 8 - RESULT_RIL_GENERIC_FAILURE = 9 - RESULT_RIL_RADIO_NOT_AVAILABLE = 10 - STATUS_ON_ICC_FREE = 0 - STATUS_ON_ICC_READ = 1 - STATUS_ON_ICC_UNREAD = 3 - STATUS_ON_ICC_SENT = 5 - STATUS_ON_ICC_UNSENT = 7 - - _instance: Optional["SmsManager"] = None - - def __init__(self): - super().__init__() - - @staticmethod - def getDefault() -> "SmsManager": - if SmsManager._instance is None: - SmsManager._instance = SmsManager() - return SmsManager._instance - - def sendTextMessage(self, dest_address: str, sc_address: Optional[str], - text: str, sent_intent: Any, delivery_intent: Any): - pass - - def sendMultipartTextMessage(self, dest_address: str, sc_address: Optional[str], - parts: List[str], sent_intents: Optional[List[Any]], - delivery_intents: Optional[List[Any]]): - pass - - def sendDataMessage(self, dest_address: str, sc_address: Optional[str], - dest_port: int, data: bytes, sent_intent: Any, - delivery_intent: Any): - pass - - def sendMultimediaMessage(self, context: Any, content_uri: str, - location_url: Optional[str], - config_overrides: Optional[Any], - sent_intent: Any): - pass - - -# ═══════════════════════════════════════════════════════════════ -# PhoneNumberUtils -# ═══════════════════════════════════════════════════════════════ - -class PhoneNumberUtils(Stub): - - FORMAT_JAPAN = 2 - FORMAT_NANP = 1 - FORMAT_UNKNOWN = 0 - - @staticmethod - def formatNumber(source: str) -> str: - return source - - @staticmethod - def isGlobalPhoneNumber(phone_number: str) -> bool: - return bool(phone_number and len(phone_number) >= 7) - - @staticmethod - def compare(a: str, b: str) -> bool: - return a.replace("-", "").replace(" ", "") == b.replace("-", "").replace(" ", "") - - @staticmethod - def formatNumberToE164(phone_number: str, default_country_iso: str) -> Optional[str]: - return phone_number - - @staticmethod - def formatNumberToRFC3966(phone_number: str, default_country_iso: str) -> Optional[str]: - return phone_number - - @staticmethod - def isEmergencyNumber(phone_number: str) -> bool: - return phone_number in ("911", "112", "999", "000", "110", "119", "120", "122") - - @staticmethod - def isVoiceMailNumber(phone_number: str) -> bool: - return False - - @staticmethod - def convertKeypadLettersToDigits(input_: str) -> str: - mapping = {"A": "2", "B": "2", "C": "2", "D": "3", "E": "3", - "F": "3", "G": "4", "H": "4", "I": "4", "J": "5", - "K": "5", "L": "5", "M": "6", "N": "6", "O": "6", - "P": "7", "Q": "7", "R": "7", "S": "7", "T": "8", - "U": "8", "V": "8", "W": "9", "X": "9", "Y": "9", "Z": "9"} - result = "" - for c in input_.upper(): - result += mapping.get(c, c) - return result - - @staticmethod - def toCallerIDMinMatch(phone_number: str) -> str: - return phone_number[-7:] if len(phone_number) >= 7 else phone_number - - -# ═══════════════════════════════════════════════════════════════ -# TelephonyManager -# ═══════════════════════════════════════════════════════════════ - -class TelephonyManager(Stub): - """TelephonyManager — 电话管理器""" - - PHONE_TYPE_NONE = 0 - PHONE_TYPE_GSM = 1 - PHONE_TYPE_CDMA = 2 - PHONE_TYPE_SIP = 3 - - NETWORK_TYPE_UNKNOWN = 0 - NETWORK_TYPE_GPRS = 1 - NETWORK_TYPE_EDGE = 2 - NETWORK_TYPE_UMTS = 3 - NETWORK_TYPE_CDMA = 4 - NETWORK_TYPE_EVDO_0 = 5 - NETWORK_TYPE_EVDO_A = 6 - NETWORK_TYPE_1xRTT = 7 - NETWORK_TYPE_HSDPA = 8 - NETWORK_TYPE_HSUPA = 9 - NETWORK_TYPE_HSPA = 10 - NETWORK_TYPE_IDEN = 11 - NETWORK_TYPE_EVDO_B = 12 - NETWORK_TYPE_LTE = 13 - NETWORK_TYPE_EHRPD = 14 - NETWORK_TYPE_HSPAP = 15 - NETWORK_TYPE_GSM = 16 - NETWORK_TYPE_TD_SCDMA = 17 - NETWORK_TYPE_IWLAN = 18 - NETWORK_TYPE_LTE_CA = 19 - NETWORK_TYPE_NR = 20 - - SIM_STATE_UNKNOWN = 0 - SIM_STATE_ABSENT = 1 - SIM_STATE_PIN_REQUIRED = 2 - SIM_STATE_PUK_REQUIRED = 3 - SIM_STATE_NETWORK_LOCKED = 4 - SIM_STATE_READY = 5 - SIM_STATE_NOT_READY = 6 - SIM_STATE_PERM_DISABLED = 7 - - DATA_DISCONNECTED = 0 - DATA_CONNECTING = 1 - DATA_CONNECTED = 2 - DATA_SUSPENDED = 3 - - CALL_STATE_IDLE = 0 - CALL_STATE_RINGING = 1 - CALL_STATE_OFFHOOK = 2 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - - def getNetworkOperatorName(self) -> str: - if _USE_LINUX: - loc = LinuxLocation.get_location() - return loc.get("isp", "") or "" - return "MockOperator" - - def getNetworkOperator(self) -> str: - return "310260" - - def getNetworkCountryIso(self) -> str: - return "us" - - def getNetworkType(self) -> int: - if _USE_LINUX: - if LinuxNetwork.get_connectivity(): - return self.NETWORK_TYPE_IWLAN - return self.NETWORK_TYPE_UNKNOWN - return self.NETWORK_TYPE_LTE - - def getPhoneType(self) -> int: - if _USE_LINUX: - return self.PHONE_TYPE_NONE - return self.PHONE_TYPE_GSM - - def getDataNetworkType(self) -> int: - return self.NETWORK_TYPE_LTE - - def getVoiceNetworkType(self) -> int: - return self.NETWORK_TYPE_LTE - - def getSimOperator(self) -> str: - if _USE_LINUX: - return "" - return "310260" - - def getSimOperatorName(self) -> str: - return "MockSIM" - - def getSimCountryIso(self) -> str: - return "us" - - def getSimSerialNumber(self) -> Optional[str]: - if _USE_LINUX: - return "" - return "89014103211118510720" - - def getSubscriberId(self) -> Optional[str]: - if _USE_LINUX: - return "" - return "310260123456789" - - def getDeviceId(self) -> Optional[str]: - if _USE_LINUX: - return LinuxNetwork.get_mac_address() or None - return "000000000000000" - - def getDeviceSoftwareVersion(self) -> Optional[str]: - if _USE_LINUX: - info = LinuxProcess.get_system_info() - return info.get("kernel", "") - return "00.00.00.00" - - def getLine1Number(self) -> Optional[str]: - if _USE_LINUX: - return "" - return "15551234567" - - def getGroupIdLevel1(self) -> Optional[str]: - return "" - - def getIccAuthentication(self, app_type: int, aid: str, data: bytes) -> Optional[bytes]: - return None - - def getVoiceMailNumber(self) -> Optional[str]: - return "15551234567" - - def getVoiceMailAlphaTag(self) -> str: - return "Voicemail" - - def isNetworkRoaming(self) -> bool: - return False - - def hasIccCard(self) -> bool: - return True - - def getSimState(self) -> int: - return self.SIM_STATE_READY - - def getCellLocation(self) -> Any: - # 桌面无基站,Linux 下返回 None - if _USE_LINUX: - return None - return None - - def getAllCellInfo(self) -> Optional[List[CellInfo]]: - return [] - - def getMmsUserAgent(self) -> str: - return "Android-Mms/1.0" - - def getMmsUAProfUrl(self) -> str: - return "http://www.example.com/uaprof.xml" - - def listen(self, listener, events): - if not hasattr(self, '_listeners'): - self._listeners = [] - self._listeners.append((listener, events)) - - def getDataState(self) -> int: - return self.DATA_CONNECTED - - def getDataActivity(self) -> int: - return 0 # DATA_ACTIVITY_NONE - - def getCallState(self) -> int: - return self.CALL_STATE_IDLE - - def toString(self) -> str: - return "TelephonyManager()" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "TelephonyManager", - "CellInfo", - "CellInfoGsm", - "CellInfoCdma", - "CellInfoLte", - "CellInfoWcdma", - "CellInfoNr", - "CellIdentityGsm", - "CellIdentityCdma", - "CellIdentityLte", - "CellIdentityWcdma", - "CellIdentityNr", - "CellSignalStrengthGsm", - "CellSignalStrengthCdma", - "CellSignalStrengthLte", - "CellSignalStrengthWcdma", - "CellSignalStrengthNr", - "PhoneStateListener", - "PhoneNumberUtils", - "ServiceState", - "SignalStrength", - "SmsManager", -] \ No newline at end of file diff --git a/android_pyapi/android/telephony_cdma.py b/android_pyapi/android/telephony_cdma.py deleted file mode 100644 index 8b7b54b..0000000 --- a/android_pyapi/android/telephony_cdma.py +++ /dev/null @@ -1,714 +0,0 @@ -""" -Android Telephony CDMA 组件 — CdmaCellLocation, SignalStrength (CDMA) -方法签名完全对齐 Android SDK (android.telephony.cdma 包) -""" - -from __future__ import annotations - -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# CdmaCellLocation -# ═══════════════════════════════════════════════════════════════ - -class CdmaCellLocation(Stub): - """CdmaCellLocation — CDMA 小区位置""" - - INVALID = -1 - - def __init__(self): - super().__init__() - self._base_station_id: int = CdmaCellLocation.INVALID - self._base_station_latitude: int = CdmaCellLocation.INVALID - self._base_station_longitude: int = CdmaCellLocation.INVALID - self._system_id: int = CdmaCellLocation.INVALID - self._network_id: int = CdmaCellLocation.INVALID - self._pseudo_network_id: int = CdmaCellLocation.INVALID - self._pseudo_base_station_id: int = CdmaCellLocation.INVALID - self._lac: int = CdmaCellLocation.INVALID - self._cid: int = CdmaCellLocation.INVALID - - def getBaseStationId(self) -> int: - return self._base_station_id - - def setBaseStationId(self, bsid: int): - self._base_station_id = bsid - - def getBaseStationLatitude(self) -> int: - return self._base_station_latitude - - def setBaseStationLatitude(self, latitude: int): - self._base_station_latitude = latitude - - def getBaseStationLongitude(self) -> int: - return self._base_station_longitude - - def setBaseStationLongitude(self, longitude: int): - self._base_station_longitude = longitude - - def getSystemId(self) -> int: - return self._system_id - - def setSystemId(self, sid: int): - self._system_id = sid - - def getNetworkId(self) -> int: - return self._network_id - - def setNetworkId(self, nid: int): - self._network_id = nid - - def getLac(self) -> int: - """获取位置区码 (仅 1xRTT)""" - return self._lac - - def setLac(self, lac: int): - self._lac = lac - - def getCid(self) -> int: - """获取小区 ID (仅 1xRTT)""" - return self._cid - - def setCid(self, cid: int): - self._cid = cid - - def isEmpty(self) -> bool: - """检查位置是否为空""" - return (self._base_station_id == CdmaCellLocation.INVALID - and self._base_station_latitude == CdmaCellLocation.INVALID - and self._base_station_longitude == CdmaCellLocation.INVALID - and self._system_id == CdmaCellLocation.INVALID - and self._network_id == CdmaCellLocation.INVALID) - - def equals(self, other: Any) -> bool: - if isinstance(other, CdmaCellLocation): - return (self._base_station_id == other._base_station_id - and self._base_station_latitude == other._base_station_latitude - and self._base_station_longitude == other._base_station_longitude - and self._system_id == other._system_id - and self._network_id == other._network_id) - return False - - def hashCode(self) -> int: - return hash((self._base_station_id, self._base_station_latitude, - self._base_station_longitude, self._system_id, - self._network_id)) & 0x7FFFFFFF - - def fillInNotifierBundle(self, bundle: Any): - """填充通知包""" - if bundle is not None: - bundle.putInt("base_station_id", self._base_station_id) - bundle.putInt("base_station_latitude", self._base_station_latitude) - bundle.putInt("base_station_longitude", self._base_station_longitude) - bundle.putInt("system_id", self._system_id) - bundle.putInt("network_id", self._network_id) - - def toString(self) -> str: - return f"CdmaCellLocation(bsid={self._base_station_id}, sid={self._system_id}, nid={self._network_id})" - - -# ═══════════════════════════════════════════════════════════════ -# SignalStrength — CDMA -# ═══════════════════════════════════════════════════════════════ - -class SignalStrength(Stub): - """SignalStrength — CDMA 信号强度""" - - SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0 - SIGNAL_STRENGTH_POOR = 1 - SIGNAL_STRENGTH_MODERATE = 2 - SIGNAL_STRENGTH_GOOD = 3 - SIGNAL_STRENGTH_GREAT = 4 - - INVALID = -1 - MAX_DBM = -25 - MIN_DBM = -120 - - CDMA_RSSI_MAX = -75 - CDMA_RSSI_MIN = -120 - CDMA_ECIO_MAX = 0 - CDMA_ECIO_MIN = -30 - EVDO_DBM_MAX = -60 - EVDO_DBM_MIN = -120 - EVDO_ECIO_MAX = 0 - EVDO_ECIO_MIN = -20 - EVDO_SNR_MAX = 10 - EVDO_SNR_MIN = 0 - - def __init__(self, cdma_dbm: int = 0, cdma_ecio: int = 0, - evdo_dbm: int = 0, evdo_ecio: int = 0, evdo_snr: int = 0): - super().__init__() - self._cdma_dbm = cdma_dbm - self._cdma_ecio = cdma_ecio - self._evdo_dbm = evdo_dbm - self._evdo_ecio = evdo_ecio - self._evdo_snr = evdo_snr - self._is_gsm: bool = False - self._lte_rssi: int = 0 - self._lte_rsrp: int = 0 - self._lte_rsrq: int = 0 - self._lte_rssnr: int = 0 - self._td_scdma_rscp: int = 0 - self._wcdma_rscp: int = 0 - self._wcdma_ecno: int = 0 - - def getCdmaDbm(self) -> int: - """获取 CDMA dBm""" - return self._cdma_dbm - - def setCdmaDbm(self, dbm: int): - self._cdma_dbm = dbm - - def getCdmaEcio(self) -> int: - """获取 CDMA Ec/Io (0.5 dB 单位)""" - return self._cdma_ecio - - def setCdmaEcio(self, ecio: int): - self._cdma_ecio = ecio - - def getEvdoDbm(self) -> int: - """获取 EVDO dBm""" - return self._evdo_dbm - - def setEvdoDbm(self, dbm: int): - self._evdo_dbm = dbm - - def getEvdoEcio(self) -> int: - """获取 EVDO Ec/Io (0.5 dB 单位)""" - return self._evdo_ecio - - def setEvdoEcio(self, ecio: int): - self._evdo_ecio = ecio - - def getEvdoSnr(self) -> int: - """获取 EVDO SNR""" - return self._evdo_snr - - def setEvdoSnr(self, snr: int): - self._evdo_snr = snr - - def getDbm(self) -> int: - """获取整体 dBm""" - if self._evdo_dbm > 0: - return self._evdo_dbm - return self._cdma_dbm - - def getLevel(self) -> int: - """获取信号等级 (0-4)""" - dbm = self.getDbm() - if dbm >= -85: - return SignalStrength.SIGNAL_STRENGTH_GREAT - elif dbm >= -95: - return SignalStrength.SIGNAL_STRENGTH_GOOD - elif dbm >= -105: - return SignalStrength.SIGNAL_STRENGTH_MODERATE - elif dbm >= -115: - return SignalStrength.SIGNAL_STRENGTH_POOR - else: - return SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN - - def getAsuLevel(self) -> int: - """获取 ASU 等级""" - dbm = self.getDbm() - if dbm <= -120: - return 0 - if dbm >= -25: - return 96 - return int((dbm + 120) * (96.0 / 95.0)) - - def getEvdoLevel(self) -> int: - """获取 EVDO 等级""" - evdo_dbm = self._evdo_dbm - if evdo_dbm >= -65: - return SignalStrength.SIGNAL_STRENGTH_GREAT - elif evdo_dbm >= -75: - return SignalStrength.SIGNAL_STRENGTH_GOOD - elif evdo_dbm >= -90: - return SignalStrength.SIGNAL_STRENGTH_MODERATE - elif evdo_dbm >= -105: - return SignalStrength.SIGNAL_STRENGTH_POOR - else: - return SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN - - def getCdmaLevel(self) -> int: - """获取 CDMA 等级""" - cdma_dbm = self._cdma_dbm - if cdma_dbm >= -75: - return SignalStrength.SIGNAL_STRENGTH_GREAT - elif cdma_dbm >= -85: - return SignalStrength.SIGNAL_STRENGTH_GOOD - elif cdma_dbm >= -95: - return SignalStrength.SIGNAL_STRENGTH_MODERATE - elif cdma_dbm >= -105: - return SignalStrength.SIGNAL_STRENGTH_POOR - else: - return SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN - - def isGsm(self) -> bool: - return self._is_gsm - - def setGsm(self, gsm: bool): - self._is_gsm = gsm - - def getCdmaRssi(self) -> int: - """获取 CDMA RSSI (dBm)""" - return self._cdma_dbm - - def getCdmaEcioDb(self) -> float: - """获取 CDMA Ec/Io (dB)""" - return self._cdma_ecio / 2.0 - - def getEvdoEcioDb(self) -> float: - """获取 EVDO Ec/Io (dB)""" - return self._evdo_ecio / 2.0 - - def isValid(self) -> bool: - """检查是否有效""" - return self._cdma_dbm > SignalStrength.INVALID - - def toString(self) -> str: - return f"CDMA_SignalStrength(cdmaDbm={self._cdma_dbm}, evdoDbm={self._evdo_dbm})" - - -# ═══════════════════════════════════════════════════════════════ -# CDMA SMS -# ═══════════════════════════════════════════════════════════════ - -class CdmaSmsMessage(Stub): - """CdmaSmsMessage — CDMA 短信""" - - ENCODING_7BIT_ASCII = 2 - ENCODING_UNICODE = 4 - ENCODING_SHIFT_JIS = 5 - ENCODING_KOREAN = 6 - ENCODING_LATIN = 8 - ENCODING_LATIN_HEBREW = 9 - ENCODING_GSM_7BIT = 10 - ENCODING_GSM_DCS = 11 - - PRIORITY_NORMAL = 0 - PRIORITY_INTERACTIVE = 1 - PRIORITY_URGENT = 2 - PRIORITY_EMERGENCY = 3 - - PRIVACY_NORMAL = 0 - PRIVACY_RESTRICTED = 1 - PRIVACY_CONFIDENTIAL = 2 - PRIVACY_SECRET = 3 - - def __init__(self): - super().__init__() - self._message_body: str = "" - self._originating_address: str = "" - self._destination_address: str = "" - self._encoding: int = CdmaSmsMessage.ENCODING_7BIT_ASCII - self._priority: int = CdmaSmsMessage.PRIORITY_NORMAL - self._privacy: int = CdmaSmsMessage.PRIVACY_NORMAL - self._timestamp: int = 0 - self._bearer_data: bytes = b"" - self._service_category: int = 0 - self._language: str = "" - self._is_urgent: bool = False - - def getMessageBody(self) -> str: - return self._message_body - - def setMessageBody(self, body: str): - self._message_body = body - - def getOriginatingAddress(self) -> str: - return self._originating_address - - def setOriginatingAddress(self, addr: str): - self._originating_address = addr - - def getDestinationAddress(self) -> str: - return self._destination_address - - def setDestinationAddress(self, addr: str): - self._destination_address = addr - - def getEncoding(self) -> int: - return self._encoding - - def setEncoding(self, encoding: int): - self._encoding = encoding - - def getPriority(self) -> int: - return self._priority - - def setPriority(self, priority: int): - self._priority = priority - - def getPrivacy(self) -> int: - return self._privacy - - def setPrivacy(self, privacy: int): - self._privacy = privacy - - def getTimestamp(self) -> int: - return self._timestamp - - def setTimestamp(self, ts: int): - self._timestamp = ts - - def getBearerData(self) -> bytes: - return self._bearer_data - - def setBearerData(self, data: bytes): - self._bearer_data = data - - def toString(self) -> str: - return f"CdmaSmsMessage(from={self._originating_address}, body={self._message_body[:20]})" - - -# ═══════════════════════════════════════════════════════════════ -# CDMA 网络常量 -# ═══════════════════════════════════════════════════════════════ - -class CdmaNetworkType(Stub): - """CdmaNetworkType — CDMA 网络类型常量""" - - NETWORK_TYPE_CDMA = 4 - NETWORK_TYPE_EVDO_0 = 5 - NETWORK_TYPE_EVDO_A = 6 - NETWORK_TYPE_1xRTT = 7 - NETWORK_TYPE_EVDO_B = 12 - NETWORK_TYPE_EHRPD = 14 - - @staticmethod - def isCdma(network_type: int) -> bool: - return network_type in (CdmaNetworkType.NETWORK_TYPE_CDMA, - CdmaNetworkType.NETWORK_TYPE_EVDO_0, - CdmaNetworkType.NETWORK_TYPE_EVDO_A, - CdmaNetworkType.NETWORK_TYPE_1xRTT, - CdmaNetworkType.NETWORK_TYPE_EVDO_B, - CdmaNetworkType.NETWORK_TYPE_EHRPD) - - @staticmethod - def toString(network_type: int) -> str: - names = { - CdmaNetworkType.NETWORK_TYPE_CDMA: "CDMA", - CdmaNetworkType.NETWORK_TYPE_EVDO_0: "EVDO rev.0", - CdmaNetworkType.NETWORK_TYPE_EVDO_A: "EVDO rev.A", - CdmaNetworkType.NETWORK_TYPE_1xRTT: "1xRTT", - CdmaNetworkType.NETWORK_TYPE_EVDO_B: "EVDO rev.B", - CdmaNetworkType.NETWORK_TYPE_EHRPD: "eHRPD", - } - return names.get(network_type, f"UNKNOWN({network_type})") - - @staticmethod - def getRatName(network_type: int) -> str: - return CdmaNetworkType.toString(network_type) - - -# ═══════════════════════════════════════════════════════════════ -# CDMA 补充业务 -# ═══════════════════════════════════════════════════════════════ - -class CdmaServiceState(Stub): - """CdmaServiceState — CDMA 服务状态""" - - STATE_IN_SERVICE = 0 - STATE_OUT_OF_SERVICE = 1 - STATE_EMERGENCY_ONLY = 2 - STATE_POWER_OFF = 3 - - def __init__(self): - super().__init__() - self._state: int = CdmaServiceState.STATE_OUT_OF_SERVICE - self._is_roaming: bool = False - self._cdma_default_roaming_indicator: int = -1 - self._cdma_eri_icon_index: int = -1 - self._cdma_eri_icon_mode: int = -1 - self._cdma_roaming_indicator: int = -1 - self._system_id: int = -1 - self._network_id: int = -1 - self._base_station_id: int = -1 - self._base_station_latitude: int = -1 - self._base_station_longitude: int = -1 - self._operator_alpha_long: str = "" - self._operator_alpha_short: str = "" - self._operator_numeric: str = "" - self._is_manual_selection: bool = False - - def getState(self) -> int: - return self._state - - def setState(self, state: int): - self._state = state - - def getRoaming(self) -> bool: - return self._is_roaming - - def setRoaming(self, roaming: bool): - self._is_roaming = roaming - - def getCdmaDefaultRoamingIndicator(self) -> int: - return self._cdma_default_roaming_indicator - - def setCdmaDefaultRoamingIndicator(self, indicator: int): - self._cdma_default_roaming_indicator = indicator - - def getCdmaEriIconIndex(self) -> int: - return self._cdma_eri_icon_index - - def setCdmaEriIconIndex(self, index: int): - self._cdma_eri_icon_index = index - - def getCdmaEriIconMode(self) -> int: - return self._cdma_eri_icon_mode - - def setCdmaEriIconMode(self, mode: int): - self._cdma_eri_icon_mode = mode - - def getCdmaRoamingIndicator(self) -> int: - return self._cdma_roaming_indicator - - def setCdmaRoamingIndicator(self, indicator: int): - self._cdma_roaming_indicator = indicator - - def getSystemId(self) -> int: - return self._system_id - - def setSystemId(self, sid: int): - self._system_id = sid - - def getNetworkId(self) -> int: - return self._network_id - - def setNetworkId(self, nid: int): - self._network_id = nid - - def getOperatorAlphaLong(self) -> str: - return self._operator_alpha_long - - def setOperatorAlphaLong(self, op: str): - self._operator_alpha_long = op - - def getOperatorAlphaShort(self) -> str: - return self._operator_alpha_short - - def setOperatorAlphaShort(self, op: str): - self._operator_alpha_short = op - - def getOperatorNumeric(self) -> str: - return self._operator_numeric - - def setOperatorNumeric(self, op: str): - self._operator_numeric = op - - def toString(self) -> str: - return f"CdmaServiceState(state={self._state}, roaming={self._is_roaming})" - - -# ═══════════════════════════════════════════════════════════════ -# CDMA ERI (Enhanced Roaming Indicator) -# ═══════════════════════════════════════════════════════════════ - -class CdmaEriInfo(Stub): - """CdmaEriInfo — CDMA ERI 信息""" - - ERI_ON = 0 - ERI_OFF = 1 - ERI_FLASH = 2 - - ROAMING_INDICATOR_NOT_ROAMING = 0 - ROAMING_INDICATOR_ROAMING = 1 - ROAMING_INDICATOR_SAMPLING = 2 - ROAMING_INDICATOR_AFFILIATED = 3 - ROAMING_INDICATOR_NOT_AFFILIATED = 4 - ROAMING_INDICATOR_FLASH = 5 - - def __init__(self): - super().__init__() - self._icon_index: int = -1 - self._icon_mode: int = CdmaEriInfo.ERI_OFF - self._roaming_indicator: int = CdmaEriInfo.ROAMING_INDICATOR_NOT_ROAMING - self._eri_text: str = "" - self._alert_show: bool = False - self._call_guard: bool = False - - def getIconIndex(self) -> int: - return self._icon_index - - def setIconIndex(self, index: int): - self._icon_index = index - - def getIconMode(self) -> int: - return self._icon_mode - - def setIconMode(self, mode: int): - self._icon_mode = mode - - def getRoamingIndicator(self) -> int: - return self._roaming_indicator - - def setRoamingIndicator(self, indicator: int): - self._roaming_indicator = indicator - - def getEriText(self) -> str: - return self._eri_text - - def setEriText(self, text: str): - self._eri_text = text - - def isAlertShow(self) -> bool: - return self._alert_show - - def setAlertShow(self, show: bool): - self._alert_show = show - - def isCallGuard(self) -> bool: - return self._call_guard - - def setCallGuard(self, guard: bool): - self._call_guard = guard - - def isRoaming(self) -> bool: - return self._roaming_indicator != CdmaEriInfo.ROAMING_INDICATOR_NOT_ROAMING - - def toString(self) -> str: - return f"CdmaEriInfo(icon={self._icon_index}, roaming={self._roaming_indicator})" - - -# ═══════════════════════════════════════════════════════════════ -# CDMA 语音/数据状态 -# ═══════════════════════════════════════════════════════════════ - -class CdmaCallState(Stub): - """CdmaCallState — CDMA 通话状态""" - - CALL_STATE_IDLE = 0 - CALL_STATE_ACTIVE = 1 - CALL_STATE_ALERTING = 2 - CALL_STATE_DIALING = 3 - CALL_STATE_INCOMING = 4 - CALL_STATE_HOLDING = 5 - CALL_STATE_DISCONNECTING = 6 - CALL_STATE_DISCONNECTED = 7 - CALL_STATE_CONFERENCE = 8 - - CALL_TYPE_VOICE = 0 - CALL_TYPE_DATA = 1 - CALL_TYPE_FAX = 2 - CALL_TYPE_UNKNOWN = 3 - - @staticmethod - def toString(state: int) -> str: - names = { - CdmaCallState.CALL_STATE_IDLE: "IDLE", - CdmaCallState.CALL_STATE_ACTIVE: "ACTIVE", - CdmaCallState.CALL_STATE_ALERTING: "ALERTING", - CdmaCallState.CALL_STATE_DIALING: "DIALING", - CdmaCallState.CALL_STATE_INCOMING: "INCOMING", - CdmaCallState.CALL_STATE_DISCONNECTED: "DISCONNECTED", - } - return names.get(state, f"UNKNOWN({state})") - - -# ═══════════════════════════════════════════════════════════════ -# CDMA Data Connection -# ═══════════════════════════════════════════════════════════════ - -class CdmaDataConnectionState(Stub): - """CdmaDataConnectionState — CDMA 数据连接状态""" - - DATA_STATE_DISCONNECTED = 0 - DATA_STATE_CONNECTING = 1 - DATA_STATE_CONNECTED = 2 - DATA_STATE_SUSPENDED = 3 - DATA_STATE_DORMANT = 4 - DATA_STATE_AUTHENTICATING = 5 - - DATA_ACTIVITY_NONE = 0 - DATA_ACTIVITY_IN = 1 - DATA_ACTIVITY_OUT = 2 - DATA_ACTIVITY_INOUT = 3 - DATA_ACTIVITY_DORMANT = 4 - - @staticmethod - def toString(state: int) -> str: - names = { - CdmaDataConnectionState.DATA_STATE_DISCONNECTED: "DISCONNECTED", - CdmaDataConnectionState.DATA_STATE_CONNECTING: "CONNECTING", - CdmaDataConnectionState.DATA_STATE_CONNECTED: "CONNECTED", - CdmaDataConnectionState.DATA_STATE_SUSPENDED: "SUSPENDED", - CdmaDataConnectionState.DATA_STATE_DORMANT: "DORMANT", - } - return names.get(state, f"UNKNOWN({state})") - - @staticmethod - def dataActivityToString(activity: int) -> str: - names = { - CdmaDataConnectionState.DATA_ACTIVITY_NONE: "NONE", - CdmaDataConnectionState.DATA_ACTIVITY_IN: "IN", - CdmaDataConnectionState.DATA_ACTIVITY_OUT: "OUT", - CdmaDataConnectionState.DATA_ACTIVITY_INOUT: "INOUT", - CdmaDataConnectionState.DATA_ACTIVITY_DORMANT: "DORMANT", - } - return names.get(activity, f"UNKNOWN({activity})") - - -# ═══════════════════════════════════════════════════════════════ -# CDMA 网络注册 -# ═══════════════════════════════════════════════════════════════ - -class CdmaRegistrationState(Stub): - """CdmaRegistrationState — CDMA 注册状态""" - - REG_STATE_NOT_REGISTERED = 0 - REG_STATE_REGISTERED = 1 - REG_STATE_REGISTERING = 2 - REG_STATE_DENIED = 3 - REG_STATE_UNKNOWN = 4 - REG_STATE_ROAMING = 5 - REG_STATE_HOME = 6 - - def __init__(self, state: int = REG_STATE_UNKNOWN): - super().__init__() - self._state = state - self._system_id: int = -1 - self._network_id: int = -1 - self._base_id: int = -1 - self._zone_id: int = -1 - - def getState(self) -> int: - return self._state - - def setState(self, state: int): - self._state = state - - def getSystemId(self) -> int: - return self._system_id - - def setSystemId(self, sid: int): - self._system_id = sid - - def getNetworkId(self) -> int: - return self._network_id - - def setNetworkId(self, nid: int): - self._network_id = nid - - def getBaseId(self) -> int: - return self._base_id - - def setBaseId(self, base_id: int): - self._base_id = base_id - - def isRegistered(self) -> bool: - return self._state in (CdmaRegistrationState.REG_STATE_REGISTERED, - CdmaRegistrationState.REG_STATE_HOME, - CdmaRegistrationState.REG_STATE_ROAMING) - - def isRoaming(self) -> bool: - return self._state == CdmaRegistrationState.REG_STATE_ROAMING - - def toString(self) -> str: - return f"CdmaRegistrationState(state={self._state})" \ No newline at end of file diff --git a/android_pyapi/android/telephony_data.py b/android_pyapi/android/telephony_data.py deleted file mode 100644 index 6fd4206..0000000 --- a/android_pyapi/android/telephony_data.py +++ /dev/null @@ -1,1200 +0,0 @@ -""" -Android Telephony Data 组件 — TelephonyManager.Data, DataConnection, ApnSetting, DataProfile 等 -方法签名完全对齐 Android SDK (android.telephony.data 包) -""" - -from __future__ import annotations - -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# ApnSetting -# ═══════════════════════════════════════════════════════════════ - -class ApnSetting(Stub): - """ApnSetting — APN 设置""" - - TYPE_DEFAULT = 1 << 0 - TYPE_MMS = 1 << 1 - TYPE_SUPL = 1 << 2 - TYPE_DUN = 1 << 3 - TYPE_HIPRI = 1 << 4 - TYPE_FOTA = 1 << 5 - TYPE_IMS = 1 << 6 - TYPE_CBS = 1 << 7 - TYPE_IA = 1 << 8 - TYPE_EMERGENCY = 1 << 9 - TYPE_MCX = 1 << 10 - TYPE_XCAP = 1 << 11 - TYPE_BIP = 1 << 12 - TYPE_VSIM = 1 << 13 - TYPE_ENTERPRISE = 1 << 14 - TYPE_ALL = 0xFFFFFFFF - - PROTOCOL_IP = "IP" - PROTOCOL_IPV6 = "IPV6" - PROTOCOL_IPV4V6 = "IPV4V6" - PROTOCOL_PPP = "PPP" - PROTOCOL_NON_IP = "NON-IP" - PROTOCOL_UNSTRUCTURED = "UNSTRUCTURED" - - AUTH_TYPE_NONE = 0 - AUTH_TYPE_PAP = 1 - AUTH_TYPE_CHAP = 2 - AUTH_TYPE_PAP_OR_CHAP = 3 - - MVNO_TYPE_IMSI = 0 - MVNO_TYPE_GID = 1 - MVNO_TYPE_SPN = 2 - MVNO_TYPE_PNN = 3 - MVNO_TYPE_ACQ = 4 - MVNO_TYPE_ICCID = 5 - MVNO_TYPE_PLMN = 6 - MVNO_TYPE_SIP = 7 - MVNO_TYPE_NONE = -1 - - def __init__(self, apn: str = "", numeric: str = "", - carrier: str = "", mcc: str = "", mnc: str = "", - user: str = "", password: str = ""): - super().__init__() - self._apn = apn - self._numeric = numeric - self._carrier = carrier - self._mcc = mcc - self._mnc = mnc - self._user = user - self._password = password - self._proxy: str = "" - self._port: int = 0 - self._server: str = "" - self._mmsc: str = "" - self._mms_proxy: str = "" - self._mms_port: int = 0 - self._types: int = ApnSetting.TYPE_DEFAULT - self._protocol: str = ApnSetting.PROTOCOL_IP - self._roaming_protocol: str = ApnSetting.PROTOCOL_IP - self._auth_type: int = ApnSetting.AUTH_TYPE_NONE - self._carrier_enabled: bool = True - self._bearer: int = 0 - self._bearer_bitmask: int = 0 - self._roaming_bearer_bitmask: int = 0 - self._mtu: int = 0 - self._mvno_type: int = ApnSetting.MVNO_TYPE_NONE - self._mvno_match_data: str = "" - self._edited: int = 0 - self._id: int = 0 - self._network_type_bitmask: int = 0 - self._entry_name: str = "" - self._entry_for_apn: str = "" - self._always_on: bool = False - self._skip_464xlat: bool = False - self._persistent: bool = True - - def getApn(self) -> str: - return self._apn - - def setApn(self, apn: str): - self._apn = apn - - def getNumeric(self) -> str: - return self._numeric - - def setNumeric(self, numeric: str): - self._numeric = numeric - - def getCarrier(self) -> str: - return self._carrier - - def setCarrier(self, carrier: str): - self._carrier = carrier - - def getMcc(self) -> str: - return self._mcc - - def setMcc(self, mcc: str): - self._mcc = mcc - - def getMnc(self) -> str: - return self._mnc - - def setMnc(self, mnc: str): - self._mnc = mnc - - def getUser(self) -> str: - return self._user - - def setUser(self, user: str): - self._user = user - - def getPassword(self) -> str: - return self._password - - def setPassword(self, password: str): - self._password = password - - def getProxy(self) -> str: - return self._proxy - - def setProxy(self, proxy: str): - self._proxy = proxy - - def getPort(self) -> int: - return self._port - - def setPort(self, port: int): - self._port = port - - def getServer(self) -> str: - return self._server - - def setServer(self, server: str): - self._server = server - - def getMmsc(self) -> str: - return self._mmsc - - def setMmsc(self, mmsc: str): - self._mmsc = mmsc - - def getMmsProxy(self) -> str: - return self._mms_proxy - - def setMmsProxy(self, proxy: str): - self._mms_proxy = proxy - - def getMmsPort(self) -> int: - return self._mms_port - - def setMmsPort(self, port: int): - self._mms_port = port - - def getTypes(self) -> int: - return self._types - - def setTypes(self, types: int): - self._types = types - - def getProtocol(self) -> str: - return self._protocol - - def setProtocol(self, protocol: str): - self._protocol = protocol - - def getRoamingProtocol(self) -> str: - return self._roaming_protocol - - def setRoamingProtocol(self, protocol: str): - self._roaming_protocol = protocol - - def getAuthType(self) -> int: - return self._auth_type - - def setAuthType(self, auth_type: int): - self._auth_type = auth_type - - def isCarrierEnabled(self) -> bool: - return self._carrier_enabled - - def setCarrierEnabled(self, enabled: bool): - self._carrier_enabled = enabled - - def getBearer(self) -> int: - return self._bearer - - def setBearer(self, bearer: int): - self._bearer = bearer - - def hasType(self, type_: int) -> bool: - return bool(self._types & type_) - - def isAlwaysOn(self) -> bool: - return self._always_on - - def setAlwaysOn(self, always_on: bool): - self._always_on = always_on - - def getMtu(self) -> int: - return self._mtu - - def setMtu(self, mtu: int): - self._mtu = mtu - - def getMvnoType(self) -> int: - return self._mvno_type - - def setMvnoType(self, mvno_type: int): - self._mvno_type = mvno_type - - def getMvnoMatchData(self) -> str: - return self._mvno_match_data - - def setMvnoMatchData(self, data: str): - self._mvno_match_data = data - - def getId(self) -> int: - return self._id - - def setId(self, id_: int): - self._id = id_ - - def getEntryName(self) -> str: - return self._entry_name - - def setEntryName(self, name: str): - self._entry_name = name - - def toString(self) -> str: - return f"ApnSetting(apn={self._apn}, carrier={self._carrier})" - - class Builder(Stub): - """ApnSetting.Builder — APN 构建器""" - - def __init__(self): - super().__init__() - self._apn = "" - self._numeric = "" - self._carrier = "" - self._mcc = "" - self._mnc = "" - self._user = "" - self._password = "" - self._proxy = "" - self._port = 0 - self._server = "" - self._mmsc = "" - self._mms_proxy = "" - self._mms_port = 0 - self._types = ApnSetting.TYPE_DEFAULT - self._protocol = ApnSetting.PROTOCOL_IP - self._roaming_protocol = ApnSetting.PROTOCOL_IP - self._auth_type = ApnSetting.AUTH_TYPE_NONE - self._carrier_enabled = True - self._bearer = 0 - self._mvno_type = ApnSetting.MVNO_TYPE_NONE - self._mvno_match_data = "" - self._always_on = False - self._mtu = 0 - - def setApn(self, apn: str) -> ApnSetting.Builder: - self._apn = apn; return self - - def setNumeric(self, numeric: str) -> ApnSetting.Builder: - self._numeric = numeric; return self - - def setCarrier(self, carrier: str) -> ApnSetting.Builder: - self._carrier = carrier; return self - - def setMcc(self, mcc: str) -> ApnSetting.Builder: - self._mcc = mcc; return self - - def setMnc(self, mnc: str) -> ApnSetting.Builder: - self._mnc = mnc; return self - - def setUser(self, user: str) -> ApnSetting.Builder: - self._user = user; return self - - def setPassword(self, password: str) -> ApnSetting.Builder: - self._password = password; return self - - def setProxy(self, proxy: str) -> ApnSetting.Builder: - self._proxy = proxy; return self - - def setPort(self, port: int) -> ApnSetting.Builder: - self._port = port; return self - - def setMmsc(self, mmsc: str) -> ApnSetting.Builder: - self._mmsc = mmsc; return self - - def setMmsProxy(self, proxy: str) -> ApnSetting.Builder: - self._mms_proxy = proxy; return self - - def setMmsPort(self, port: int) -> ApnSetting.Builder: - self._mms_port = port; return self - - def setTypes(self, types: int) -> ApnSetting.Builder: - self._types = types; return self - - def setProtocol(self, protocol: str) -> ApnSetting.Builder: - self._protocol = protocol; return self - - def setRoamingProtocol(self, protocol: str) -> ApnSetting.Builder: - self._roaming_protocol = protocol; return self - - def setAuthType(self, auth_type: int) -> ApnSetting.Builder: - self._auth_type = auth_type; return self - - def setCarrierEnabled(self, enabled: bool) -> ApnSetting.Builder: - self._carrier_enabled = enabled; return self - - def setAlwaysOn(self, always_on: bool) -> ApnSetting.Builder: - self._always_on = always_on; return self - - def build(self) -> ApnSetting: - apn = ApnSetting(self._apn, self._numeric, self._carrier, - self._mcc, self._mnc, self._user, self._password) - apn.setProxy(self._proxy) - apn.setPort(self._port) - apn.setMmsc(self._mmsc) - apn.setMmsProxy(self._mms_proxy) - apn.setMmsPort(self._mms_port) - apn.setTypes(self._types) - apn.setProtocol(self._protocol) - apn.setRoamingProtocol(self._roaming_protocol) - apn.setAuthType(self._auth_type) - apn.setCarrierEnabled(self._carrier_enabled) - apn.setAlwaysOn(self._always_on) - return apn - - -# ═══════════════════════════════════════════════════════════════ -# DataProfile -# ═══════════════════════════════════════════════════════════════ - -class DataProfile(Stub): - """DataProfile — 数据配置文件""" - - TYPE_COMMON = 0 - TYPE_3GPP = 1 - TYPE_3GPP2 = 2 - TYPE_IMS = 3 - TYPE_FOTA = 4 - TYPE_CBS = 5 - TYPE_IA = 6 - TYPE_EMERGENCY = 7 - TYPE_MCX = 8 - TYPE_XCAP = 9 - TYPE_ENTERPRISE = 10 - - PROTOCOL_IPV4 = 0 - PROTOCOL_IPV6 = 1 - PROTOCOL_IPV4V6 = 2 - PROTOCOL_PPP = 3 - PROTOCOL_UNKNOWN = -1 - - def __init__(self, profile_id: int = 0, apn: str = "", - protocol: int = PROTOCOL_IPV4): - super().__init__() - self._profile_id = profile_id - self._apn = apn - self._protocol = protocol - self._roaming_protocol: int = DataProfile.PROTOCOL_IPV4 - self._auth_type: int = 0 - self._user: str = "" - self._password: str = "" - self._type: int = DataProfile.TYPE_COMMON - self._max_connections: int = 0 - self._wait_time: int = 0 - self._enabled: bool = True - self._supported_apn_types_bitmask: int = 0 - self._bearer_bitmask: int = 0 - self._mtu: int = 0 - self._persistent: bool = True - self._preferred: bool = False - - def getProfileId(self) -> int: - return self._profile_id - - def setProfileId(self, profile_id: int): - self._profile_id = profile_id - - def getApn(self) -> str: - return self._apn - - def setApn(self, apn: str): - self._apn = apn - - def getProtocol(self) -> int: - return self._protocol - - def setProtocol(self, protocol: int): - self._protocol = protocol - - def getRoamingProtocol(self) -> int: - return self._roaming_protocol - - def setRoamingProtocol(self, protocol: int): - self._roaming_protocol = protocol - - def getAuthType(self) -> int: - return self._auth_type - - def setAuthType(self, auth_type: int): - self._auth_type = auth_type - - def getUser(self) -> str: - return self._user - - def setUser(self, user: str): - self._user = user - - def getPassword(self) -> str: - return self._password - - def setPassword(self, password: str): - self._password = password - - def getType(self) -> int: - return self._type - - def setType(self, type_: int): - self._type = type_ - - def getMaxConnections(self) -> int: - return self._max_connections - - def setMaxConnections(self, max_conn: int): - self._max_connections = max_conn - - def getWaitTime(self) -> int: - return self._wait_time - - def setWaitTime(self, wait: int): - self._wait_time = wait - - def isEnabled(self) -> bool: - return self._enabled - - def setEnabled(self, enabled: bool): - self._enabled = enabled - - def isPersistent(self) -> bool: - return self._persistent - - def setPersistent(self, persistent: bool): - self._persistent = persistent - - def isPreferred(self) -> bool: - return self._preferred - - def setPreferred(self, preferred: bool): - self._preferred = preferred - - def getMtu(self) -> int: - return self._mtu - - def setMtu(self, mtu: int): - self._mtu = mtu - - def getSupportedApnTypesBitmask(self) -> int: - return self._supported_apn_types_bitmask - - def setSupportedApnTypesBitmask(self, bitmask: int): - self._supported_apn_types_bitmask = bitmask - - def toString(self) -> str: - return f"DataProfile(id={self._profile_id}, apn={self._apn})" - - -# ═══════════════════════════════════════════════════════════════ -# DataConnection -# ═══════════════════════════════════════════════════════════════ - -class DataConnection(Stub): - """DataConnection — 数据连接""" - - STATE_DISCONNECTED = 0 - STATE_CONNECTING = 1 - STATE_CONNECTED = 2 - STATE_DISCONNECTING = 3 - STATE_SUSPENDED = 4 - STATE_UNKNOWN = -1 - - LINK_PROPERTIES_IFACE_NAME = "iface" - LINK_PROPERTIES_DNS1 = "dns1" - LINK_PROPERTIES_DNS2 = "dns2" - LINK_PROPERTIES_GATEWAY = "gateway" - LINK_PROPERTIES_MTU = "mtu" - LINK_PROPERTIES_ADDRESS = "address" - LINK_PROPERTIES_PREFIX_LENGTH = "prefix_length" - LINK_PROPERTIES_HTTP_PROXY = "http_proxy" - LINK_PROPERTIES_PCSCF = "pcscf" - - def __init__(self, cid: int = 0): - super().__init__() - self._cid = cid - self._state: int = DataConnection.STATE_DISCONNECTED - self._apn: str = "" - self._apn_type: int = ApnSetting.TYPE_DEFAULT - self._network_type: int = 0 - self._link_properties: Dict[str, Any] = {} - self._data_profile: Optional[DataProfile] = None - self._is_internet: bool = False - self._fail_cause: int = 0 - self._suggested_retry_time: int = 0 - self._time_connected: int = 0 - self._bearer_id: int = 0 - self._rat: int = 0 - self._is_roaming: bool = False - - def getCid(self) -> int: - return self._cid - - def setCid(self, cid: int): - self._cid = cid - - def getState(self) -> int: - return self._state - - def setState(self, state: int): - self._state = state - - def getApn(self) -> str: - return self._apn - - def setApn(self, apn: str): - self._apn = apn - - def getApnType(self) -> int: - return self._apn_type - - def setApnType(self, apn_type: int): - self._apn_type = apn_type - - def getNetworkType(self) -> int: - return self._network_type - - def setNetworkType(self, network_type: int): - self._network_type = network_type - - def getLinkProperties(self) -> Dict[str, Any]: - return dict(self._link_properties) - - def setLinkProperties(self, props: Dict[str, Any]): - self._link_properties = dict(props) - - def getDataProfile(self) -> Optional[DataProfile]: - return self._data_profile - - def setDataProfile(self, profile: DataProfile): - self._data_profile = profile - - def isInternet(self) -> bool: - return self._is_internet - - def setInternet(self, internet: bool): - self._is_internet = internet - - def getFailCause(self) -> int: - return self._fail_cause - - def setFailCause(self, cause: int): - self._fail_cause = cause - - def getSuggestedRetryTime(self) -> int: - return self._suggested_retry_time - - def setSuggestedRetryTime(self, time_ms: int): - self._suggested_retry_time = time_ms - - def getTimeConnected(self) -> int: - return self._time_connected - - def setTimeConnected(self, time_ms: int): - self._time_connected = time_ms - - def getBearerId(self) -> int: - return self._bearer_id - - def setBearerId(self, bearer_id: int): - self._bearer_id = bearer_id - - def isRoaming(self) -> bool: - return self._is_roaming - - def setRoaming(self, roaming: bool): - self._is_roaming = roaming - - def isConnected(self) -> bool: - return self._state == DataConnection.STATE_CONNECTED - - def toString(self) -> str: - return f"DataConnection(cid={self._cid}, state={self._state}, apn={self._apn})" - - -# ═══════════════════════════════════════════════════════════════ -# DataSpecificRegistrationInfo -# ═══════════════════════════════════════════════════════════════ - -class DataSpecificRegistrationInfo(Stub): - """DataSpecificRegistrationInfo — 数据特定注册信息""" - - LVC_AVAILABLE = 0 - LVC_NOT_AVAILABLE = 1 - LVC_UNKNOWN = 2 - - def __init__(self): - super().__init__() - self._lvc_state: int = DataSpecificRegistrationInfo.LVC_UNKNOWN - self._is_dc_over_nr: bool = False - self._is_enhanced_voice_roaming: bool = False - self._is_endc_available: bool = False - self._max_data_connectivity: int = 0 - self._nr_available: bool = False - self._en_dc_available: bool = False - self._voice_over_nr: bool = False - self._is_roaming: bool = False - self._registration_network_type: int = 0 - self._voice_registration_network_type: int = 0 - self._available_services: Set[int] = set() - self._voice_registration_info: Any = None - self._nr_voice_available: bool = False - self._lte_voice_available: bool = False - - def getLvcState(self) -> int: - return self._lvc_state - - def setLvcState(self, state: int): - self._lvc_state = state - - def isDcOverNr(self) -> bool: - return self._is_dc_over_nr - - def setDcOverNr(self, dc_over_nr: bool): - self._is_dc_over_nr = dc_over_nr - - def isEnhancedVoiceRoaming(self) -> bool: - return self._is_enhanced_voice_roaming - - def setEnhancedVoiceRoaming(self, roaming: bool): - self._is_enhanced_voice_roaming = roaming - - def isEndcAvailable(self) -> bool: - return self._is_endc_available - - def setEndcAvailable(self, available: bool): - self._is_endc_available = available - - def getMaxDataConnectivity(self) -> int: - return self._max_data_connectivity - - def setMaxDataConnectivity(self, max_: int): - self._max_data_connectivity = max_ - - def isNrAvailable(self) -> bool: - return self._nr_available - - def setNrAvailable(self, available: bool): - self._nr_available = available - - def isEnDcAvailable(self) -> bool: - return self._en_dc_available - - def setEnDcAvailable(self, available: bool): - self._en_dc_available = available - - def isVoiceOverNr(self) -> bool: - return self._voice_over_nr - - def setVoiceOverNr(self, voice_over_nr: bool): - self._voice_over_nr = voice_over_nr - - def getRegistrationNetworkType(self) -> int: - return self._registration_network_type - - def setRegistrationNetworkType(self, type_: int): - self._registration_network_type = type_ - - def getVoiceRegistrationNetworkType(self) -> int: - return self._voice_registration_network_type - - def setVoiceRegistrationNetworkType(self, type_: int): - self._voice_registration_network_type = type_ - - def getAvailableServices(self) -> Set[int]: - return set(self._available_services) - - def setAvailableServices(self, services: Set[int]): - self._available_services = set(services) - - def toString(self) -> str: - return f"DataSpecificRegistrationInfo(nr={self._nr_available}, dcNr={self._is_dc_over_nr})" - - -# ═══════════════════════════════════════════════════════════════ -# NetworkSlicingConfig -# ═══════════════════════════════════════════════════════════════ - -class NetworkSlicingConfig(Stub): - """NetworkSlicingConfig — 网络切片配置""" - - def __init__(self): - super().__init__() - self._ursp_rules: List[UrspRule] = [] - self._nssais: List[Nssai] = [] - self._configured_s_nssais: List[Any] = [] - self._requested_s_nssais: List[Any] = [] - self._allowed_s_nssais: List[Any] = [] - self._default_s_nssai: Any = None - self._mapped_hplmn_s_nssais: List[Any] = [] - self._rejected_s_nssais: List[Any] = [] - - def getUrspRules(self) -> List[UrspRule]: - return list(self._ursp_rules) - - def setUrspRules(self, rules: List[UrspRule]): - self._ursp_rules = list(rules) - - def getNssais(self) -> List[Nssai]: - return list(self._nssais) - - def setNssais(self, nssais: List[Nssai]): - self._nssais = list(nssais) - - def getConfiguredSNssais(self) -> List[Any]: - return list(self._configured_s_nssais) - - def setConfiguredSNssais(self, nssais: List[Any]): - self._configured_s_nssais = list(nssais) - - def getRequestedSNssais(self) -> List[Any]: - return list(self._requested_s_nssais) - - def setRequestedSNssais(self, nssais: List[Any]): - self._requested_s_nssais = list(nssais) - - def addUrspRule(self, rule: UrspRule): - self._ursp_rules.append(rule) - - def addNssai(self, nssai: Nssai): - self._nssais.append(nssai) - - def toString(self) -> str: - return f"NetworkSlicingConfig(ursp={len(self._ursp_rules)}, nssais={len(self._nssais)})" - - -# ═══════════════════════════════════════════════════════════════ -# UrspRule -# ═══════════════════════════════════════════════════════════════ - -class UrspRule(Stub): - """UrspRule — URSP (UE Route Selection Policy) 规则""" - - def __init__(self, precedence: int = 0, rule_id: int = 0): - super().__init__() - self._precedence = precedence - self._rule_id = rule_id - self._traffic_descriptors: List[TrafficDescriptor] = [] - self._route_selection_descriptors: List[RouteSelectionDescriptor] = [] - - def getPrecedence(self) -> int: - return self._precedence - - def setPrecedence(self, precedence: int): - self._precedence = precedence - - def getRuleId(self) -> int: - return self._rule_id - - def setRuleId(self, rule_id: int): - self._rule_id = rule_id - - def getTrafficDescriptors(self) -> List[TrafficDescriptor]: - return list(self._traffic_descriptors) - - def setTrafficDescriptors(self, descriptors: List[TrafficDescriptor]): - self._traffic_descriptors = list(descriptors) - - def getRouteSelectionDescriptors(self) -> List[RouteSelectionDescriptor]: - return list(self._route_selection_descriptors) - - def setRouteSelectionDescriptors(self, descriptors: List[RouteSelectionDescriptor]): - self._route_selection_descriptors = list(descriptors) - - def addTrafficDescriptor(self, descriptor: TrafficDescriptor): - self._traffic_descriptors.append(descriptor) - - def addRouteSelectionDescriptor(self, descriptor: RouteSelectionDescriptor): - self._route_selection_descriptors.append(descriptor) - - def toString(self) -> str: - return f"UrspRule(id={self._rule_id}, precedence={self._precedence})" - - -# ═══════════════════════════════════════════════════════════════ -# TrafficDescriptor -# ═══════════════════════════════════════════════════════════════ - -class TrafficDescriptor(Stub): - """TrafficDescriptor — 流量描述符""" - - def __init__(self): - super().__init__() - self._dnn: str = "" - self._os_app_id: str = "" - self._fqdn: str = "" - self._ip_descriptors: List[Any] = [] - self._domain_descriptors: List[Any] = [] - self._connection_capabilities: List[int] = [] - - def getDnn(self) -> str: - return self._dnn - - def setDnn(self, dnn: str): - self._dnn = dnn - - def getOsAppId(self) -> str: - return self._os_app_id - - def setOsAppId(self, app_id: str): - self._os_app_id = app_id - - def getFqdn(self) -> str: - return self._fqdn - - def setFqdn(self, fqdn: str): - self._fqdn = fqdn - - def getIpDescriptors(self) -> List[Any]: - return list(self._ip_descriptors) - - def setIpDescriptors(self, descriptors: List[Any]): - self._ip_descriptors = list(descriptors) - - def getDomainDescriptors(self) -> List[Any]: - return list(self._domain_descriptors) - - def setDomainDescriptors(self, descriptors: List[Any]): - self._domain_descriptors = list(descriptors) - - def getConnectionCapabilities(self) -> List[int]: - return list(self._connection_capabilities) - - def setConnectionCapabilities(self, caps: List[int]): - self._connection_capabilities = list(caps) - - def toString(self) -> str: - return f"TrafficDescriptor(dnn={self._dnn})" - - -# ═══════════════════════════════════════════════════════════════ -# RouteSelectionDescriptor -# ═══════════════════════════════════════════════════════════════ - -class RouteSelectionDescriptor(Stub): - """RouteSelectionDescriptor — 路由选择描述符""" - - SSC_MODE_1 = 1 - SSC_MODE_2 = 2 - SSC_MODE_3 = 3 - SSC_MODE_UNKNOWN = 0 - - def __init__(self): - super().__init__() - self._ssc_mode: int = RouteSelectionDescriptor.SSC_MODE_UNKNOWN - self._s_nssai: Optional[Any] = None - self._dnn: str = "" - self._route_selection_info: bytes = b"" - self._precedence: int = 0 - self._pdu_session_type: int = 0 - self._access_type: int = 0 - self._data_network_name: str = "" - - def getSscMode(self) -> int: - return self._ssc_mode - - def setSscMode(self, mode: int): - self._ssc_mode = mode - - def getSNssai(self) -> Optional[Any]: - return self._s_nssai - - def setSNssai(self, nssai: Any): - self._s_nssai = nssai - - def getDnn(self) -> str: - return self._dnn - - def setDnn(self, dnn: str): - self._dnn = dnn - - def getPrecedence(self) -> int: - return self._precedence - - def setPrecedence(self, prec: int): - self._precedence = prec - - def getPduSessionType(self) -> int: - return self._pdu_session_type - - def setPduSessionType(self, type_: int): - self._pdu_session_type = type_ - - def toString(self) -> str: - return f"RouteSelectionDescriptor(sscMode={self._ssc_mode}, dnn={self._dnn})" - - -# ═══════════════════════════════════════════════════════════════ -# Nssai -# ═══════════════════════════════════════════════════════════════ - -class Nssai(Stub): - """Nssai — 网络切片选择辅助信息""" - - SST_EMBB = 1 - SST_URLLC = 2 - SST_MIOT = 3 - SST_V2X = 4 - - def __init__(self, sst: int = 0, sd: int = 0): - super().__init__() - self._sst = sst - self._sd = sd - self._sd_string: str = "" - self._mapped_hplmn_sst: int = 0 - self._mapped_hplmn_sd: int = 0 - - def getSst(self) -> int: - return self._sst - - def setSst(self, sst: int): - self._sst = sst - - def getSd(self) -> int: - return self._sd - - def setSd(self, sd: int): - self._sd = sd - - def getSdString(self) -> str: - return self._sd_string - - def setSdString(self, sd_str: str): - self._sd_string = sd_str - - def getMappedHplmnSst(self) -> int: - return self._mapped_hplmn_sst - - def setMappedHplmnSst(self, sst: int): - self._mapped_hplmn_sst = sst - - def getMappedHplmnSd(self) -> int: - return self._mapped_hplmn_sd - - def setMappedHplmnSd(self, sd: int): - self._mapped_hplmn_sd = sd - - def toString(self) -> str: - return f"Nssai(sst={self._sst}, sd={self._sd})" - - -# ═══════════════════════════════════════════════════════════════ -# TelephonyManager.Data -# ═══════════════════════════════════════════════════════════════ - -class TelephonyManager_Data(Stub): - """TelephonyManager.Data — TelephonyManager 数据相关子类""" - - DATA_DISCONNECTED = 0 - DATA_CONNECTING = 1 - DATA_CONNECTED = 2 - DATA_SUSPENDED = 3 - DATA_HANDOVER_IN_PROGRESS = 4 - DATA_UNKNOWN = -1 - - DATA_ACTIVITY_NONE = 0 - DATA_ACTIVITY_IN = 1 - DATA_ACTIVITY_OUT = 2 - DATA_ACTIVITY_INOUT = 3 - DATA_ACTIVITY_DORMANT = 4 - - def __init__(self, telephony_manager: Any = None): - super().__init__() - self._manager = telephony_manager - self._data_connections: Dict[int, DataConnection] = {} - self._data_state: int = TelephonyManager_Data.DATA_DISCONNECTED - - def getDataState(self) -> int: - return self._data_state - - def setDataState(self, state: int): - self._data_state = state - - def getDataActivity(self) -> int: - return TelephonyManager_Data.DATA_ACTIVITY_NONE - - def getDataConnections(self) -> List[DataConnection]: - return list(self._data_connections.values()) - - def getDataConnection(self, cid: int) -> Optional[DataConnection]: - return self._data_connections.get(cid) - - def addDataConnection(self, conn: DataConnection): - self._data_connections[conn.getCid()] = conn - - def removeDataConnection(self, cid: int): - self._data_connections.pop(cid, None) - - def isDataEnabled(self) -> bool: - return True - - def isDataRoamingEnabled(self) -> bool: - return False - - def getDataNetworkType(self) -> int: - return 0 - - def toString(self) -> str: - return f"TelephonyManager.Data(state={self._data_state})" - - -# ═══════════════════════════════════════════════════════════════ -# DataFailCause -# ═══════════════════════════════════════════════════════════════ - -class DataFailCause(Stub): - """DataFailCause — 数据连接失败原因码""" - - NONE = 0 - OPERATOR_BARRED = 8 - INSUFFICIENT_RESOURCES = 26 - MISSING_UKNOWN_APN = 27 - UNKNOWN_PDP_ADDRESS_TYPE = 28 - USER_AUTHENTICATION = 29 - ACTIVATION_REJECT_GGSN = 30 - ACTIVATION_REJECT_UNSPECIFIED = 31 - SERVICE_OPTION_NOT_SUPPORTED = 32 - SERVICE_OPTION_NOT_SUBSCRIBED = 33 - SERVICE_OPTION_OUT_OF_ORDER = 34 - NSAPI_IN_USE = 35 - REGULAR_DEACTIVATION = 36 - QOS_NOT_ACCEPTED = 37 - NETWORK_FAILURE = 38 - REACTIVATION_REQUESTED = 39 - PDP_DUPLICATE = 40 - PDP_AUTH_FAILURE = 41 - MAX_ACTIVE_PDP_CONTEXT_REACHED = 65 - UNSUPPORTED_APN_IN_CURRENT_PLMN = 66 - INVALID_TRANSACTION_ID = 81 - MESSAGE_INCORRECT_SEMANTIC = 83 - INVALID_MANDATORY_INFO = 84 - MESSAGE_TYPE_UNSUPPORTED = 97 - MSG_TYPE_NONCOMPATIBLE_STATE = 98 - UNKNOWN_INFO_ELEMENT = 99 - CONDITIONAL_IE_ERROR = 100 - PROTOCOL_ERRORS = 111 - APN_TYPE_CONFLICT = 112 - INVALID_PCO = 113 - INTERNAL_RETRY = 114 - PDP_ACTIVATE_MAX_RETRY_FAILED = 115 - RADIO_ACCESS_BEARER_FAILURE = 116 - RADIO_ACCESS_BEARER_SETUP_FAILURE = 117 - RADIO_ACCESS_BEARER_RELEASE_FAILURE = 118 - PDP_MODIFY_FAILURE = 119 - MAX_ACTIVE_NETWORK_TYPE = 120 - ONLY_SINGLE_BEARER_ALLOWED = 121 - ERROR_UNSPECIFIED = 65535 - - @staticmethod - def toString(cause: int) -> str: - names = {v: k for k, v in DataFailCause.__dict__.items() - if isinstance(v, int) and not k.startswith("_")} - return names.get(cause, f"UNKNOWN({cause})") - - -# ═══════════════════════════════════════════════════════════════ -# DataServiceProvider -# ═══════════════════════════════════════════════════════════════ - -class DataServiceProvider(Stub): - """DataServiceProvider — 数据服务提供商""" - - def __init__(self): - super().__init__() - self._name: str = "" - self._plmn: str = "" - self._is_roaming: bool = False - self._is_registered: bool = False - - def getName(self) -> str: - return self._name - - def setName(self, name: str): - self._name = name - - def getPlmn(self) -> str: - return self._plmn - - def setPlmn(self, plmn: str): - self._plmn = plmn - - def isRoaming(self) -> bool: - return self._is_roaming - - def setRoaming(self, roaming: bool): - self._is_roaming = roaming - - def isRegistered(self) -> bool: - return self._is_registered - - def setRegistered(self, registered: bool): - self._is_registered = registered - - def toString(self) -> str: - return f"DataServiceProvider(name={self._name}, plmn={self._plmn})" - - -# ═══════════════════════════════════════════════════════════════ -# DataRegistrationState (helper) -# ═══════════════════════════════════════════════════════════════ - -class DataRegistrationState(Stub): - """DataRegistrationState — 数据注册状态""" - - REGISTRATION_STATE_NOT_REGISTERED = 0 - REGISTRATION_STATE_REGISTERED_HOME = 1 - REGISTRATION_STATE_REGISTERED_ROAMING = 2 - REGISTRATION_STATE_REGISTERED_NOT_KNOWN = 3 - REGISTRATION_STATE_DENIED = 4 - REGISTRATION_STATE_UNKNOWN = 5 - REGISTRATION_STATE_SCANNING = 6 - - def __init__(self, state: int = REGISTRATION_STATE_UNKNOWN): - super().__init__() - self._state = state - self._domain: int = 0 - self._transport_type: int = 0 - self._specific_info: Optional[DataSpecificRegistrationInfo] = None - self._rat: int = 0 - self._reason_for_denial: int = 0 - - def getState(self) -> int: - return self._state - - def setState(self, state: int): - self._state = state - - def getDomain(self) -> int: - return self._domain - - def setDomain(self, domain: int): - self._domain = domain - - def getTransportType(self) -> int: - return self._transport_type - - def setTransportType(self, transport: int): - self._transport_type = transport - - def getSpecificInfo(self) -> Optional[DataSpecificRegistrationInfo]: - return self._specific_info - - def setSpecificInfo(self, info: DataSpecificRegistrationInfo): - self._specific_info = info - - def getRat(self) -> int: - return self._rat - - def setRat(self, rat: int): - self._rat = rat - - def isRegistered(self) -> bool: - return self._state in (DataRegistrationState.REGISTRATION_STATE_REGISTERED_HOME, - DataRegistrationState.REGISTRATION_STATE_REGISTERED_ROAMING) - - def toString(self) -> str: - return f"DataRegistrationState(state={self._state})" \ No newline at end of file diff --git a/android_pyapi/android/telephony_euicc.py b/android_pyapi/android/telephony_euicc.py deleted file mode 100644 index 8313a82..0000000 --- a/android_pyapi/android/telephony_euicc.py +++ /dev/null @@ -1,1181 +0,0 @@ -""" -Android Telephony EUICC 组件 — EuiccManager, EuiccCardManager, EuiccInfo, DownloadableSubscription 等 -方法签名完全对齐 Android SDK (android.telephony.euicc 包) -""" - -from __future__ import annotations - -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# EuiccManager -# ═══════════════════════════════════════════════════════════════ - -class EuiccManager(Stub): - """EuiccManager — eUICC 管理器""" - - ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS" - ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION" - ACTION_RESOLVE_ERROR = "android.telephony.euicc.action.RESOLVE_ERROR" - ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED" - ACTION_DELETE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED" - ACTION_RENAME_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED" - ACTION_START_RESOLUTION = "android.telephony.euicc.action.START_RESOLUTION" - - EXTRA_ENABLE_SUBSCRIPTION = "android.telephony.euicc.extra.ENABLE_SUBSCRIPTION" - EXTRA_SUBSCRIPTION_ID = "android.telephony.euicc.extra.SUBSCRIPTION_ID" - EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION" - EXTRA_RESOLUTION_INTENT = "android.telephony.euicc.extra.RESOLUTION_INTENT" - EXTRA_RESOLUTION_CALLBACK_INTENT = "android.telephony.euicc.extra.RESOLUTION_CALLBACK_INTENT" - EXTRA_USE_QR_SCANNER = "android.telephony.euicc.extra.USE_QR_SCANNER" - EXTRA_FORCE_COMMIT = "android.telephony.euicc.extra.FORCE_COMMIT" - EXTRA_CONSENT = "android.telephony.euicc.extra.CONSENT" - EXTRA_SUBSCRIPTION_ID_FOR_RESOLVING = "android.telephony.euicc.extra.SUBSCRIPTION_ID_FOR_RESOLVING" - EXTRA_RESOLVING_ACTION = "android.telephony.euicc.extra.RESOLVING_ACTION" - EXTRA_ACTIVE_SLOT = "android.telephony.euicc.extra.ACTIVE_SLOT" - EXTRA_SIM_SLOT_INDEX = "android.telephony.euicc.extra.SIM_SLOT_INDEX" - EXTRA_PHYSICAL_SLOT_ID = "android.telephony.euicc.extra.PHYSICAL_SLOT_ID" - - EMBEDDED_SUBSCRIPTION_RESULT_OK = 0 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR = 1 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_BUSY = 2 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_INVALID_ACTIVATION_CODE = 3 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_INVALID_CONFIRMATION_CODE = 4 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_INVALID_RESPONSE = 5 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_INVALID_MATCHING_ID = 6 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_INVALID_SMDP_ADDRESS = 7 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_NETWORK = 8 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_UNSUPPORTED = 9 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_PROFILES_LIMIT_EXCEEDED = 10 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_DOWNLOAD_IN_PROGRESS = 11 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_INVALID_PORT = 12 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_CARRIER_LOCKED = 13 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_INVALID_PORT_INDEX = 14 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_INVALID_ADDRESS = 15 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_INTERNAL = 16 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_SIM_NOT_READY = 17 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_CARRIER_PRIVILEGE = 18 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_APDU = 19 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_CONFIRMATION_CODE_MISMATCH = 20 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_OPERATION_BUSY = 21 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_INVALID_SLOT_ID = 22 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_NOT_FOUND = 23 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_DISALLOWED = 24 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_NOT_ENABLED = 25 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_MULTI_SIM_NOT_SUPPORTED = 26 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_SUBSCRIPTION_NOT_FOUND = 27 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_SUBSCRIPTION_ALREADY_EXISTS = 28 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_RENAME_NOT_ALLOWED = 29 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_DELETE_NOT_ALLOWED = 30 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_REFRESH_NOT_ALLOWED = 31 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_OUT_OF_MEMORY = 32 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_REJECTED = 33 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_FRAME_TABLE_FULL = 34 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_APPLICATION_NOT_FOUND = 35 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_MISMATCH = 36 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_VERSION_NOT_SUPPORTED = 37 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_POLICY = 38 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_AUTH = 39 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_KEY = 40 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_SIGNATURE = 41 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_CERTIFICATE = 42 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_CERTIFICATE_EXPIRED = 43 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_CERTIFICATE_REVOKED = 44 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_IDENTITY_MISMATCH = 45 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_PROFILE_NOT_AVAILABLE = 46 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_PROFILE_ALREADY_INSTALLED = 47 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_PROFILE_NOT_INSTALLED = 48 - EMBEDDED_SUBSCRIPTION_RESULT_ERROR_EUICC_PROFILE_INVALID = 49 - - # eUICC 操作 - OPERATION_DOWNLOAD = 0 - OPERATION_DELETE = 1 - OPERATION_SWITCH = 2 - OPERATION_RENAME = 3 - OPERATION_REFRESH = 4 - OPERATION_RESOLVE = 5 - OPERATION_GET_DEFAULT = 6 - OPERATION_GET_EID = 7 - OPERATION_GET_EUICC_INFO = 8 - OPERATION_GET_EUICC_CHALLENGE = 9 - OPERATION_GET_PROFILE = 10 - OPERATION_GET_SUBSCRIPTION = 11 - OPERATION_LIST_SUBSCRIPTIONS = 12 - OPERATION_SET_NICKNAME = 13 - OPERATION_ERASE = 14 - OPERATION_UPDATE = 15 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._eid: str = "" - self._euicc_info: Optional[EuiccInfo] = None - self._subscriptions: List[EuiccSubscriptionInfo] = [] - self._enabled: bool = False - self._slot_index: int = 0 - - def isEnabled(self) -> bool: - """检查 eUICC 是否启用""" - return self._enabled - - def setEnabled(self, enabled: bool): - self._enabled = enabled - - def getEid(self) -> str: - """获取 eUICC ID (EID)""" - return self._eid - - def setEid(self, eid: str): - self._eid = eid - - def getEuiccInfo(self) -> Optional[EuiccInfo]: - """获取 eUICC 信息""" - return self._euicc_info - - def setEuiccInfo(self, info: EuiccInfo): - self._euicc_info = info - - def getEuiccCardManager(self) -> Optional[EuiccCardManager]: - """获取 eUICC 卡管理器""" - return EuiccCardManager(self._context) - - def getSlotIndex(self) -> int: - return self._slot_index - - def setSlotIndex(self, index: int): - self._slot_index = index - - def getContext(self) -> Any: - return self._context - - def downloadSubscription(self, subscription: DownloadableSubscription, - switch_to_downloaded: bool = True, - callback: Any = None) -> int: - """下载订阅""" - return EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK - - def startResolution(self, result: int, callback_intent: Any, - resolution_intent: Any): - """启动解析流程""" - pass - - def listSubscriptions(self) -> List[EuiccSubscriptionInfo]: - """列出所有订阅""" - return list(self._subscriptions) - - def addSubscription(self, sub: EuiccSubscriptionInfo): - self._subscriptions.append(sub) - - def removeSubscription(self, sub_id: int): - self._subscriptions = [s for s in self._subscriptions if s.getSubscriptionId() != sub_id] - - def getDefaultDownloadableSubscription(self) -> Optional[DownloadableSubscription]: - """获取默认可下载订阅""" - return None - - def createForCardId(self, card_id: int) -> EuiccManager: - """为指定卡 ID 创建 eUICC 管理器""" - return self - - def switchToSubscription(self, sub_id: int, callback: Any = None): - """切换到指定订阅""" - pass - - def deleteSubscription(self, sub_id: int, callback: Any = None): - """删除指定订阅""" - pass - - def renameSubscription(self, sub_id: int, new_name: str): - """重命名订阅""" - for s in self._subscriptions: - if s.getSubscriptionId() == sub_id: - s.setNickname(new_name) - break - - def eraseSubscriptions(self, callback: Any = None): - """擦除所有订阅""" - self._subscriptions.clear() - - def refreshSubscriptions(self, callback: Any = None): - """刷新订阅列表""" - pass - - def retainSubscriptionsForFactoryReset(self, callback: Any = None): - """保留订阅以备恢复出厂设置""" - pass - - def isSimPortAvailable(self, port_index: int) -> bool: - return True - - def toString(self) -> str: - return f"EuiccManager(enabled={self._enabled}, eid={self._eid[:8] if self._eid else ''}...)" - - -# ═══════════════════════════════════════════════════════════════ -# EuiccCardManager -# ═══════════════════════════════════════════════════════════════ - -class EuiccCardManager(Stub): - """EuiccCardManager — eUICC 卡管理器""" - - RESULT_OK = 0 - RESULT_UNKNOWN_ERROR = 1 - RESULT_INVALID_ARGUMENTS = 2 - RESULT_INTERNAL_ERROR = 3 - RESULT_SESSION_CLOSED = 4 - RESULT_NO_PENDING_COMMANDS = 5 - RESULT_COMMAND_FAILED = 6 - RESULT_APDU_ERROR = 7 - RESULT_CARD_NOT_ACCESSIBLE = 8 - RESULT_CARD_REMOVED = 9 - RESULT_CARD_RESET = 10 - RESULT_TIMEOUT = 11 - RESULT_CARD_IO_ERROR = 12 - RESULT_CARD_AUTH_ERROR = 13 - RESULT_PROFILE_NOT_AVAILABLE = 14 - RESULT_PROFILE_ALREADY_INSTALLED = 15 - RESULT_PROFILE_NOT_INSTALLED = 16 - RESULT_PROFILE_INSTALL_ERROR = 17 - RESULT_PROFILE_DELETE_ERROR = 18 - RESULT_PROFILE_EUICC_MISMATCH = 19 - RESULT_PROFILE_VERSION_MISMATCH = 20 - RESULT_PROFILE_CERTIFICATE_ERROR = 21 - RESULT_PROFILE_SIGNATURE_ERROR = 22 - RESULT_PROFILE_INTEGRITY_ERROR = 23 - RESULT_PROFILE_EXPIRED = 24 - RESULT_PROFILE_REVOKED = 25 - RESULT_PROFILE_POLICY_ERROR = 26 - RESULT_PROFILE_AUTH_ERROR = 27 - RESULT_PROFILE_KEY_ERROR = 28 - RESULT_PROFILE_MEMORY_ERROR = 29 - RESULT_PROFILE_UNSUPPORTED = 30 - RESULT_PROFILE_CONFLICT = 31 - RESULT_RAT_NOT_ALLOWED = 32 - RESULT_POLICY_RULES_NOT_SATISFIED = 33 - RESULT_NOT_ENABLED = 34 - RESULT_MULTISIM_NOT_SUPPORTED = 35 - RESULT_CARRIER_LOCKED = 36 - RESULT_OPERATION_BUSY = 37 - RESULT_CARD_ALREADY_ACTIVE = 38 - RESULT_CARD_INACTIVE = 39 - - class ResultCallback(Stub): - """EuiccCardManager.ResultCallback — eUICC 卡操作结果回调""" - - def onComplete(self, result: int, data: Any = None): - pass - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._card_id: str = "" - self._eid: str = "" - - def getContext(self) -> Any: - return self._context - - def getCardId(self) -> str: - return self._card_id - - def setCardId(self, card_id: str): - self._card_id = card_id - - def getEid(self) -> str: - return self._eid - - def setEid(self, eid: str): - self._eid = eid - - def requestEvents(self, callback: ResultCallback): - """请求 eUICC 事件""" - pass - - def requestEvents(self, port_index: int, callback: ResultCallback): - pass - - def listNotifications(self, callback: ResultCallback): - """列出通知""" - pass - - def listNotifications(self, port_index: int, callback: ResultCallback): - pass - - def retrieveNotification(self, seq_number: int, callback: ResultCallback): - """检索通知""" - pass - - def retrieveNotificationList(self, seq_numbers: List[int], callback: ResultCallback): - """检索通知列表""" - pass - - def prepareDownload(self, subscription: DownloadableSubscription, - callback: ResultCallback): - """准备下载""" - pass - - def prepareDownload(self, port_index: int, subscription: DownloadableSubscription, - callback: ResultCallback): - pass - - def loadBoundProfilePackage(self, bound_profile_package: bytes, - callback: ResultCallback): - """加载绑定配置文件包""" - pass - - def loadBoundProfilePackage(self, port_index: int, bound_profile_package: bytes, - callback: ResultCallback): - pass - - def getProfile(self, iccid: str, callback: ResultCallback): - """获取配置文件""" - pass - - def getProfile(self, port_index: int, iccid: str, callback: ResultCallback): - pass - - def listProfiles(self, callback: ResultCallback): - """列出配置文件""" - pass - - def listProfiles(self, port_index: int, callback: ResultCallback): - pass - - def setNickname(self, iccid: str, nickname: str, callback: ResultCallback): - """设置昵称""" - pass - - def setNickname(self, port_index: int, iccid: str, nickname: str, callback: ResultCallback): - pass - - def deleteProfile(self, iccid: str, callback: ResultCallback): - """删除配置文件""" - pass - - def deleteProfile(self, port_index: int, iccid: str, callback: ResultCallback): - pass - - def enableProfile(self, iccid: str, callback: ResultCallback): - """启用配置文件""" - pass - - def enableProfile(self, port_index: int, iccid: str, callback: ResultCallback): - pass - - def disableProfile(self, iccid: str, callback: ResultCallback): - """禁用配置文件""" - pass - - def disableProfile(self, port_index: int, iccid: str, callback: ResultCallback): - pass - - def resetMemory(self, reset_options: int, callback: ResultCallback): - """重置 eUICC 内存""" - pass - - def resetMemory(self, port_index: int, reset_options: int, callback: ResultCallback): - pass - - def setTestMode(self, test_mode: bool, callback: ResultCallback): - """设置测试模式""" - pass - - def getEuiccChallenge(self, callback: ResultCallback): - """获取 eUICC 挑战""" - pass - - def getEuiccChallenge(self, port_index: int, callback: ResultCallback): - pass - - def getEuiccInfo(self, callback: ResultCallback): - """获取 eUICC 信息""" - pass - - def getEuiccInfo(self, port_index: int, callback: ResultCallback): - pass - - def listSubscriptions(self, callback: ResultCallback): - """列出订阅""" - pass - - def toString(self) -> str: - return f"EuiccCardManager(eid={self._eid[:8] if self._eid else ''}...)" - - -# ═══════════════════════════════════════════════════════════════ -# EuiccInfo -# ═══════════════════════════════════════════════════════════════ - -class EuiccInfo(Stub): - """EuiccInfo — eUICC 信息""" - - def __init__(self, gsid: str = "", svn: str = "", - pp_version: str = "", - global_platform_version: str = ""): - super().__init__() - self._gsid = gsid - self._svn = svn - self._pp_version = pp_version - self._global_platform_version = global_platform_version - self._euicc_firmware_version: str = "" - self._euicc_hardware_version: str = "" - self._oid: str = "" - self._eid: str = "" - self._profile_version: str = "" - self._ext_card_resource: str = "" - self._default_smdp_address: str = "" - - def getGsid(self) -> str: - return self._gsid - - def setGsid(self, gsid: str): - self._gsid = gsid - - def getSvn(self) -> str: - return self._svn - - def setSvn(self, svn: str): - self._svn = svn - - def getPpVersion(self) -> str: - return self._pp_version - - def setPpVersion(self, pp_version: str): - self._pp_version = pp_version - - def getGlobalPlatformVersion(self) -> str: - return self._global_platform_version - - def setGlobalPlatformVersion(self, version: str): - self._global_platform_version = version - - def getEuiccFirmwareVersion(self) -> str: - return self._euicc_firmware_version - - def setEuiccFirmwareVersion(self, version: str): - self._euicc_firmware_version = version - - def getEuiccHardwareVersion(self) -> str: - return self._euicc_hardware_version - - def setEuiccHardwareVersion(self, version: str): - self._euicc_hardware_version = version - - def getOid(self) -> str: - return self._oid - - def setOid(self, oid: str): - self._oid = oid - - def getEid(self) -> str: - return self._eid - - def setEid(self, eid: str): - self._eid = eid - - def getProfileVersion(self) -> str: - return self._profile_version - - def setProfileVersion(self, version: str): - self._profile_version = version - - def getExtCardResource(self) -> str: - return self._ext_card_resource - - def setExtCardResource(self, resource: str): - self._ext_card_resource = resource - - def getDefaultSmdpAddress(self) -> str: - return self._default_smdp_address - - def setDefaultSmdpAddress(self, address: str): - self._default_smdp_address = address - - def toString(self) -> str: - return f"EuiccInfo(gsid={self._gsid}, svn={self._svn})" - - -# ═══════════════════════════════════════════════════════════════ -# DownloadableSubscription -# ═══════════════════════════════════════════════════════════════ - -class DownloadableSubscription(Stub): - """DownloadableSubscription — 可下载订阅""" - - def __init__(self, encoded_activation_code: str = ""): - super().__init__() - self._encoded_activation_code = encoded_activation_code - self._confirmation_code: str = "" - self._carrier_name: str = "" - self._access_rule: Optional[EuiccRulesAuthTable] = None - self._matching_id: str = "" - self._smdp_address: str = "" - self._profile_class: int = 0 - self._iccid: str = "" - self._nickname: str = "" - - def getEncodedActivationCode(self) -> str: - return self._encoded_activation_code - - def setEncodedActivationCode(self, code: str): - self._encoded_activation_code = code - - def getConfirmationCode(self) -> str: - return self._confirmation_code - - def setConfirmationCode(self, code: str): - self._confirmation_code = code - - def getCarrierName(self) -> str: - return self._carrier_name - - def setCarrierName(self, name: str): - self._carrier_name = name - - def getAccessRule(self) -> Optional[EuiccRulesAuthTable]: - return self._access_rule - - def setAccessRule(self, rule: EuiccRulesAuthTable): - self._access_rule = rule - - def getMatchingId(self) -> str: - return self._matching_id - - def setMatchingId(self, matching_id: str): - self._matching_id = matching_id - - def getSmdpAddress(self) -> str: - return self._smdp_address - - def setSmdpAddress(self, address: str): - self._smdp_address = address - - def getProfileClass(self) -> int: - return self._profile_class - - def setProfileClass(self, profile_class: int): - self._profile_class = profile_class - - def getIccid(self) -> str: - return self._iccid - - def setIccid(self, iccid: str): - self._iccid = iccid - - def getNickname(self) -> str: - return self._nickname - - def setNickname(self, nickname: str): - self._nickname = nickname - - @staticmethod - def forActivationCode(encoded_activation_code: str) -> DownloadableSubscription: - return DownloadableSubscription(encoded_activation_code) - - def toString(self) -> str: - return f"DownloadableSubscription(carrier={self._carrier_name})" - - -# ═══════════════════════════════════════════════════════════════ -# EuiccNotification -# ═══════════════════════════════════════════════════════════════ - -class EuiccNotification(Stub): - """EuiccNotification — eUICC 通知""" - - EVENT_INSTALL = 1 - EVENT_DELETE = 2 - EVENT_ENABLE = 4 - EVENT_DISABLE = 8 - EVENT_REFRESH = 16 - EVENT_POLICY_RULES_CHANGE = 32 - EVENT_MEMORY_RESET = 64 - EVENT_PROFILE_METADATA_UPDATED = 128 - EVENT_NOTIFICATIONS_ENABLED = 256 - EVENT_ALL = 511 - - ALL_EVENTS = EVENT_INSTALL | EVENT_DELETE | EVENT_ENABLE | EVENT_DISABLE | EVENT_REFRESH | EVENT_POLICY_RULES_CHANGE | EVENT_MEMORY_RESET | EVENT_PROFILE_METADATA_UPDATED | EVENT_NOTIFICATIONS_ENABLED - - def __init__(self, seq_number: int = 0, target_address: str = "", - event: int = 0, timestamp: int = 0): - super().__init__() - self._seq_number = seq_number - self._target_address = target_address - self._event = event - self._timestamp = timestamp - self._iccid: str = "" - self._displacement: Optional[bytes] = None - self._notification_data: Optional[bytes] = None - - def getSeqNumber(self) -> int: - return self._seq_number - - def setSeqNumber(self, seq: int): - self._seq_number = seq - - def getTargetAddress(self) -> str: - return self._target_address - - def setTargetAddress(self, address: str): - self._target_address = address - - def getEvent(self) -> int: - return self._event - - def setEvent(self, event: int): - self._event = event - - def getTimestamp(self) -> int: - return self._timestamp - - def setTimestamp(self, ts: int): - self._timestamp = ts - - def getIccid(self) -> str: - return self._iccid - - def setIccid(self, iccid: str): - self._iccid = iccid - - def getDisplacement(self) -> Optional[bytes]: - return self._displacement - - def setDisplacement(self, data: bytes): - self._displacement = data - - def getNotificationData(self) -> Optional[bytes]: - return self._notification_data - - def setNotificationData(self, data: bytes): - self._notification_data = data - - def toString(self) -> str: - return f"EuiccNotification(seq={self._seq_number}, event={self._event})" - - -# ═══════════════════════════════════════════════════════════════ -# EuiccRulesAuthTable -# ═══════════════════════════════════════════════════════════════ - -class EuiccRulesAuthTable(Stub): - """EuiccRulesAuthTable — eUICC 规则授权表""" - - RULE_TYPE_ACCESS_RULES = 0 - RULE_TYPE_POLICY_RULES = 1 - RULE_TYPE_USAGE_RULES = 2 - - POLICY_RULE_FLAG_CONSENT_REQUIRED = 1 - POLICY_RULE_FLAG_NOTIFY_REQUIRED = 2 - POLICY_RULE_FLAG_DELETE_BLOCKED = 4 - POLICY_RULE_FLAG_DISABLE_BLOCKED = 8 - - class RulesAuthTableEntry(Stub): - """EuiccRulesAuthTable.RulesAuthTableEntry — 规则授权表条目""" - - def __init__(self): - super().__init__() - self._certificate_hash: str = "" - self._rule_type: int = 0 - self._flags: int = 0 - self._signature: str = "" - self._pkid: str = "" - self._valid_from: int = 0 - self._valid_until: int = 0 - - def getCertificateHash(self) -> str: - return self._certificate_hash - - def setCertificateHash(self, hash_val: str): - self._certificate_hash = hash_val - - def getRuleType(self) -> int: - return self._rule_type - - def setRuleType(self, rule_type: int): - self._rule_type = rule_type - - def getFlags(self) -> int: - return self._flags - - def setFlags(self, flags: int): - self._flags = flags - - def getSignature(self) -> str: - return self._signature - - def setSignature(self, sig: str): - self._signature = sig - - def getPkid(self) -> str: - return self._pkid - - def setPkid(self, pkid: str): - self._pkid = pkid - - def hasFlags(self, flag: int) -> bool: - return bool(self._flags & flag) - - def isValidFor(self, timestamp: int) -> bool: - return self._valid_from <= timestamp < self._valid_until - - def toString(self) -> str: - return f"RulesAuthTableEntry(type={self._rule_type})" - - def __init__(self): - super().__init__() - self._entries: List[EuiccRulesAuthTable.RulesAuthTableEntry] = [] - - def getEntries(self) -> List[RulesAuthTableEntry]: - return list(self._entries) - - def addEntry(self, entry: RulesAuthTableEntry): - self._entries.append(entry) - - def removeEntry(self, entry: RulesAuthTableEntry): - self._entries = [e for e in self._entries if e is not entry] - - def findEntryByCertHash(self, cert_hash: str) -> Optional[RulesAuthTableEntry]: - for e in self._entries: - if e.getCertificateHash() == cert_hash: - return e - return None - - def findEntryByPkid(self, pkid: str) -> Optional[RulesAuthTableEntry]: - for e in self._entries: - if e.getPkid() == pkid: - return e - return None - - def getSize(self) -> int: - return len(self._entries) - - def isEmpty(self) -> bool: - return len(self._entries) == 0 - - def toString(self) -> str: - return f"EuiccRulesAuthTable(entries={len(self._entries)})" - - -# ═══════════════════════════════════════════════════════════════ -# EuiccSubscriptionInfo -# ═══════════════════════════════════════════════════════════════ - -class EuiccSubscriptionInfo(Stub): - """EuiccSubscriptionInfo — eUICC 订阅信息""" - - SUBSCRIPTION_TYPE_PROFILE = 0 - SUBSCRIPTION_TYPE_RESERVED = 1 - SUBSCRIPTION_TYPE_OPERATIONAL = 2 - SUBSCRIPTION_TYPE_TEST = 3 - SUBSCRIPTION_TYPE_PROVISIONING = 4 - SUBSCRIPTION_TYPE_UNKNOWN = -1 - - def __init__(self, subscription_id: int = 0, iccid: str = "", - nickname: str = ""): - super().__init__() - self._subscription_id = subscription_id - self._iccid = iccid - self._nickname = nickname - self._carrier_name: str = "" - self._subscription_type: int = EuiccSubscriptionInfo.SUBSCRIPTION_TYPE_PROFILE - self._is_embedded: bool = True - self._is_removable: bool = False - self._is_active: bool = False - self._profile_class: int = 0 - self._port_index: int = 0 - self._logical_slot_index: int = 0 - self._eid: str = "" - self._access_rules: Optional[EuiccRulesAuthTable] = None - - def getSubscriptionId(self) -> int: - return self._subscription_id - - def setSubscriptionId(self, sub_id: int): - self._subscription_id = sub_id - - def getIccid(self) -> str: - return self._iccid - - def setIccid(self, iccid: str): - self._iccid = iccid - - def getNickname(self) -> str: - return self._nickname - - def setNickname(self, nickname: str): - self._nickname = nickname - - def getCarrierName(self) -> str: - return self._carrier_name - - def setCarrierName(self, name: str): - self._carrier_name = name - - def getSubscriptionType(self) -> int: - return self._subscription_type - - def setSubscriptionType(self, sub_type: int): - self._subscription_type = sub_type - - def isEmbedded(self) -> bool: - return self._is_embedded - - def setEmbedded(self, embedded: bool): - self._is_embedded = embedded - - def isRemovable(self) -> bool: - return self._is_removable - - def setRemovable(self, removable: bool): - self._is_removable = removable - - def isActive(self) -> bool: - return self._is_active - - def setActive(self, active: bool): - self._is_active = active - - def getProfileClass(self) -> int: - return self._profile_class - - def setProfileClass(self, profile_class: int): - self._profile_class = profile_class - - def getPortIndex(self) -> int: - return self._port_index - - def setPortIndex(self, port: int): - self._port_index = port - - def getLogicalSlotIndex(self) -> int: - return self._logical_slot_index - - def setLogicalSlotIndex(self, index: int): - self._logical_slot_index = index - - def getEid(self) -> str: - return self._eid - - def setEid(self, eid: str): - self._eid = eid - - def getAccessRules(self) -> Optional[EuiccRulesAuthTable]: - return self._access_rules - - def setAccessRules(self, rules: EuiccRulesAuthTable): - self._access_rules = rules - - def toString(self) -> str: - return f"EuiccSubscriptionInfo(id={self._subscription_id}, carrier={self._carrier_name})" - - -# ═══════════════════════════════════════════════════════════════ -# EuiccProfileInfo -# ═══════════════════════════════════════════════════════════════ - -class EuiccProfileInfo(Stub): - """EuiccProfileInfo — eUICC 配置文件信息""" - - PROFILE_CLASS_OPERATIONAL = 0 - PROFILE_CLASS_TEST = 1 - PROFILE_CLASS_PROVISIONING = 2 - PROFILE_CLASS_OPERATIONAL_V2 = 3 - PROFILE_CLASS_UNKNOWN = -1 - - STATE_DISABLED = 0 - STATE_ENABLED = 1 - - def __init__(self, iccid: str = ""): - super().__init__() - self._iccid = iccid - self._nickname: str = "" - self._carrier_name: str = "" - self._profile_class: int = EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL - self._state: int = EuiccProfileInfo.STATE_DISABLED - self._service_provider_name: str = "" - self._profile_name: str = "" - self._profile_owner: str = "" - self._carrier_privilege_rules: List[EuiccRulesAuthTable.RulesAuthTableEntry] = [] - self._is_visible: bool = True - self._policy_rules: Optional[EuiccRulesAuthTable] = None - self._icon: Optional[bytes] = None - - def getIccid(self) -> str: - return self._iccid - - def setIccid(self, iccid: str): - self._iccid = iccid - - def getNickname(self) -> str: - return self._nickname - - def setNickname(self, nickname: str): - self._nickname = nickname - - def getCarrierName(self) -> str: - return self._carrier_name - - def setCarrierName(self, name: str): - self._carrier_name = name - - def getProfileClass(self) -> int: - return self._profile_class - - def setProfileClass(self, profile_class: int): - self._profile_class = profile_class - - def getState(self) -> int: - return self._state - - def setState(self, state: int): - self._state = state - - def getServiceProviderName(self) -> str: - return self._service_provider_name - - def setServiceProviderName(self, name: str): - self._service_provider_name = name - - def getProfileName(self) -> str: - return self._profile_name - - def setProfileName(self, name: str): - self._profile_name = name - - def getCarrierPrivilegeRules(self) -> List[EuiccRulesAuthTable.RulesAuthTableEntry]: - return list(self._carrier_privilege_rules) - - def setCarrierPrivilegeRules(self, rules: List[EuiccRulesAuthTable.RulesAuthTableEntry]): - self._carrier_privilege_rules = list(rules) - - def isVisible(self) -> bool: - return self._is_visible - - def setVisible(self, visible: bool): - self._is_visible = visible - - def getPolicyRules(self) -> Optional[EuiccRulesAuthTable]: - return self._policy_rules - - def setPolicyRules(self, rules: EuiccRulesAuthTable): - self._policy_rules = rules - - def isEnabled(self) -> bool: - return self._state == EuiccProfileInfo.STATE_ENABLED - - def toString(self) -> str: - return f"EuiccProfileInfo(iccid={self._iccid}, carrier={self._carrier_name})" - - -# ═══════════════════════════════════════════════════════════════ -# EuiccActions (helper) -# ═══════════════════════════════════════════════════════════════ - -class EuiccActions(Stub): - """EuiccActions — eUICC 操作辅助类""" - - @staticmethod - def createDownloadAction(subscription: DownloadableSubscription, - switch_on_download: bool = True) -> Any: - """创建下载操作意图""" - return None - - @staticmethod - def createDeleteAction(subscription_id: int) -> Any: - """创建删除操作意图""" - return None - - @staticmethod - def createSwitchAction(subscription_id: int) -> Any: - """创建切换操作意图""" - return None - - @staticmethod - def createRenameAction(subscription_id: int, new_name: str) -> Any: - """创建重命名操作意图""" - return None - - @staticmethod - def createEraseAction() -> Any: - """创建擦除操作意图""" - return None - - @staticmethod - def encodeActivationCode(matching_id: str, smdp_address: str) -> str: - """编码激活码""" - return f"1$^{matching_id}${smdp_address}$" - - @staticmethod - def decodeActivationCode(activation_code: str) -> Optional[Tuple[str, str]]: - """解码激活码""" - try: - parts = activation_code.split("$") - if len(parts) >= 3: - return parts[1], parts[2] - except Exception: - pass - return None - - @staticmethod - def isValidActivationCode(activation_code: str) -> bool: - """验证激活码是否有效""" - return activation_code.startswith("1$") and "$" in activation_code - - -# ═══════════════════════════════════════════════════════════════ -# EuiccChannel (helper) -# ═══════════════════════════════════════════════════════════════ - -class EuiccChannel(Stub): - """EuiccChannel — eUICC 通道辅助类""" - - CHANNEL_TYPE_APDU = 0 - CHANNEL_TYPE_HTTP = 1 - CHANNEL_TYPE_HTTPS = 2 - - def __init__(self, channel_type: int = CHANNEL_TYPE_APDU): - super().__init__() - self._channel_type = channel_type - self._is_open: bool = False - self._buffer: bytes = b"" - - def getChannelType(self) -> int: - return self._channel_type - - def isOpen(self) -> bool: - return self._is_open - - def open(self): - self._is_open = True - - def close(self): - self._is_open = False - self._buffer = b"" - - def send(self, data: bytes) -> bytes: - self._buffer = data - return b"" - - def receive(self) -> bytes: - return self._buffer - - def toString(self) -> str: - return f"EuiccChannel(type={self._channel_type}, open={self._is_open})" - - -# ═══════════════════════════════════════════════════════════════ -# EuiccPortIndex (helper) -# ═══════════════════════════════════════════════════════════════ - -class EuiccPortIndex(Stub): - """EuiccPortIndex — eUICC 端口索引""" - - PORT_INDEX_DEFAULT = 0 - PORT_INDEX_FIRST = 0 - PORT_INDEX_SECOND = 1 - - def __init__(self, port_index: int = PORT_INDEX_DEFAULT): - super().__init__() - self._port_index = port_index - - def getPortIndex(self) -> int: - return self._port_index - - def setPortIndex(self, index: int): - self._port_index = index - - def toString(self) -> str: - return f"EuiccPortIndex({self._port_index})" - - -# ═══════════════════════════════════════════════════════════════ -# EuiccResetOption (helper) -# ═══════════════════════════════════════════════════════════════ - -class EuiccResetOption(Stub): - """EuiccResetOption — eUICC 重置选项""" - - RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1 - RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES = 2 - RESET_OPTION_RESET_TO_DEFAULT = 3 - RESET_OPTION_DELETE_ALL_PROFILES = 4 - - def __init__(self, option: int = RESET_OPTION_DELETE_OPERATIONAL_PROFILES): - super().__init__() - self._option = option - - def getOption(self) -> int: - return self._option - - def setOption(self, option: int): - self._option = option - - def toString(self) -> str: - return f"EuiccResetOption({self._option})" - - -# ═══════════════════════════════════════════════════════════════ -# EuiccMemorySize (helper) -# ═══════════════════════════════════════════════════════════════ - -class EuiccMemorySize(Stub): - """EuiccMemorySize — eUICC 内存大小""" - - def __init__(self, free_bytes: int = 0, total_bytes: int = 0): - super().__init__() - self._free_bytes = free_bytes - self._total_bytes = total_bytes - self._used_bytes = total_bytes - free_bytes - - def getFreeBytes(self) -> int: - return self._free_bytes - - def getTotalBytes(self) -> int: - return self._total_bytes - - def getUsedBytes(self) -> int: - return self._used_bytes - - def getUsedPercentage(self) -> float: - if self._total_bytes == 0: - return 0.0 - return (self._used_bytes / self._total_bytes) * 100.0 - - def toString(self) -> str: - return f"EuiccMemorySize(free={self._free_bytes}, total={self._total_bytes})" - - -# ═══════════════════════════════════════════════════════════════ -# EuiccSmdpAddress (helper) -# ═══════════════════════════════════════════════════════════════ - -class EuiccSmdpAddress(Stub): - """EuiccSmdpAddress — SMDP 地址辅助类""" - - def __init__(self, address: str = ""): - super().__init__() - self._address = address - self._is_default: bool = False - - def getAddress(self) -> str: - return self._address - - def setAddress(self, address: str): - self._address = address - - def isDefault(self) -> bool: - return self._is_default - - def setDefault(self, is_default: bool): - self._is_default = is_default - - @staticmethod - def getDefaultAddress() -> EuiccSmdpAddress: - return EuiccSmdpAddress("default-smdp.gsma.com") - - def toString(self) -> str: - return f"EuiccSmdpAddress({self._address})" \ No newline at end of file diff --git a/android_pyapi/android/telephony_gsm.py b/android_pyapi/android/telephony_gsm.py deleted file mode 100644 index f150fb5..0000000 --- a/android_pyapi/android/telephony_gsm.py +++ /dev/null @@ -1,1082 +0,0 @@ -""" -Android Telephony GSM 组件 — GsmCellLocation, GsmSignalStrength, SmsManager (GSM扩展) -方法签名完全对齐 Android SDK (android.telephony.gsm 包) -""" - -from __future__ import annotations - -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# GsmCellLocation -# ═══════════════════════════════════════════════════════════════ - -class GsmCellLocation(Stub): - """GsmCellLocation — GSM 小区位置""" - - def __init__(self): - super().__init__() - self._lac: int = -1 - self._cid: int = -1 - self._psc: int = -1 - self._uarfcn: int = -1 - self._pci: int = -1 - self._arfcn: int = -1 - self._bsic: int = -1 - self._rssi: int = 0 - self._timing_advance: int = 0 - - def getLac(self) -> int: - """获取位置区码 (LAC)""" - return self._lac - - def setLac(self, lac: int): - self._lac = lac - - def getCid(self) -> int: - """获取小区 ID (CID)""" - return self._cid - - def setCid(self, cid: int): - self._cid = cid - - def getPsc(self) -> int: - """获取主扰码 (PSC)""" - return self._psc - - def setPsc(self, psc: int): - self._psc = psc - - def getUarfcn(self) -> int: - """获取 UARFCN""" - return self._uarfcn - - def setUarfcn(self, uarfcn: int): - self._uarfcn = uarfcn - - def getPci(self) -> int: - """获取物理小区 ID (PCI)""" - return self._pci - - def setPci(self, pci: int): - self._pci = pci - - def getArfcn(self) -> int: - """获取 ARFCN""" - return self._arfcn - - def setArfcn(self, arfcn: int): - self._arfcn = arfcn - - def getBsic(self) -> int: - """获取基站识别码 (BSIC)""" - return self._bsic - - def setBsic(self, bsic: int): - self._bsic = bsic - - def getRssi(self) -> int: - return self._rssi - - def setRssi(self, rssi: int): - self._rssi = rssi - - def getTimingAdvance(self) -> int: - """获取时间提前量 (TA)""" - return self._timing_advance - - def setTimingAdvance(self, ta: int): - self._timing_advance = ta - - def isGprs(self) -> bool: - """检查是否 GPRS""" - return self._cid >= 0 - - def toString(self) -> str: - return f"GsmCellLocation(lac={self._lac}, cid={self._cid}, psc={self._psc})" - - def equals(self, other: Any) -> bool: - if isinstance(other, GsmCellLocation): - return (self._lac == other._lac and self._cid == other._cid - and self._psc == other._psc) - return False - - def hashCode(self) -> int: - return hash((self._lac, self._cid, self._psc)) & 0x7FFFFFFF - - def fillInNotifierBundle(self, bundle: Any): - """填充通知包""" - if bundle is not None: - bundle.putInt("lac", self._lac) - bundle.putInt("cid", self._cid) - bundle.putInt("psc", self._psc) - - -# ═══════════════════════════════════════════════════════════════ -# GsmSignalStrength -# ═══════════════════════════════════════════════════════════════ - -class GsmSignalStrength(Stub): - """GsmSignalStrength — GSM 信号强度""" - - SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0 - SIGNAL_STRENGTH_POOR = 1 - SIGNAL_STRENGTH_MODERATE = 2 - SIGNAL_STRENGTH_GOOD = 3 - SIGNAL_STRENGTH_GREAT = 4 - - INVALID = 99 - MAX_RSSI = 31 - MIN_RSSI = 0 - - def __init__(self, rssi: int = 0, ber: int = -1, ta: int = -1): - super().__init__() - self._rssi = rssi - self._ber = ber - self._ta = ta - self._signal_strength: int = GsmSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN - self._bit_error_rate: int = -1 - self._is_gsm: bool = True - - def getRssi(self) -> int: - """获取 RSSI""" - return self._rssi - - def setRssi(self, rssi: int): - self._rssi = rssi - - def getBer(self) -> int: - """获取误码率 (BER)""" - return self._ber - - def setBer(self, ber: int): - self._ber = ber - - def getTimingAdvance(self) -> int: - """获取时间提前量""" - return self._ta - - def setTimingAdvance(self, ta: int): - self._ta = ta - - def getSignalStrength(self) -> int: - """获取信号强度等级""" - return self._signal_strength - - def setSignalStrength(self, strength: int): - self._signal_strength = strength - - def getBitErrorRate(self) -> int: - return self._bit_error_rate - - def setBitErrorRate(self, ber: int): - self._bit_error_rate = ber - - def getDbm(self) -> int: - """获取 dBm 值""" - if self._rssi <= 0 or self._rssi == 99: - return -113 - return -113 + 2 * self._rssi - - def getLevel(self) -> int: - """获取信号等级 (0-4)""" - dbm = self.getDbm() - if dbm >= -85: - return GsmSignalStrength.SIGNAL_STRENGTH_GREAT - elif dbm >= -95: - return GsmSignalStrength.SIGNAL_STRENGTH_GOOD - elif dbm >= -105: - return GsmSignalStrength.SIGNAL_STRENGTH_MODERATE - elif dbm >= -115: - return GsmSignalStrength.SIGNAL_STRENGTH_POOR - else: - return GsmSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN - - def getAsuLevel(self) -> int: - """获取 ASU 等级""" - return self._rssi - - def isGsm(self) -> bool: - return self._is_gsm - - def toString(self) -> str: - return f"GsmSignalStrength(rssi={self._rssi}, dbm={self.getDbm()}, level={self.getLevel()})" - - -# ═══════════════════════════════════════════════════════════════ -# SmsManager — GSM 扩展 -# ═══════════════════════════════════════════════════════════════ - -class SmsManager(Stub): - """SmsManager — GSM SMS 管理器""" - - RESULT_ERROR_GENERIC_FAILURE = 1 - RESULT_ERROR_RADIO_OFF = 2 - RESULT_ERROR_NULL_PDU = 3 - RESULT_ERROR_NO_SERVICE = 4 - RESULT_ERROR_LIMIT_EXCEEDED = 5 - RESULT_ERROR_FDN_CHECK_FAILURE = 6 - RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 7 - RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 8 - RESULT_RIL_GENERIC_FAILURE = 9 - RESULT_RIL_RADIO_NOT_AVAILABLE = 10 - RESULT_RIL_NETWORK_FAILURE = 11 - RESULT_RIL_SMS_SEND_FAILURE = 12 - RESULT_RIL_SMS_DELIVERY_FAILURE = 13 - RESULT_RIL_CANCELLED = 14 - RESULT_RIL_MODEM_RESET = 15 - RESULT_RIL_SIM_ABSENT = 16 - RESULT_RIL_INVALID_SMSC_ADDRESS = 17 - RESULT_RIL_SIM_SMS_FAILURE = 18 - RESULT_RIL_NO_MEMORY = 19 - RESULT_RIL_SIM_PUK_REQUIRED = 20 - RESULT_RIL_SIM_PIN_REQUIRED = 21 - RESULT_RIL_RADIO_NOT_READY = 22 - RESULT_RIL_ENCODING_ERROR = 23 - RESULT_RIL_INVALID_STATE = 24 - RESULT_RIL_MODEM_ERROR = 25 - RESULT_RIL_INTERNAL_ERROR = 26 - RESULT_RIL_REQUEST_NOT_SUPPORTED = 27 - RESULT_RIL_INVALID_PARAMETER = 28 - RESULT_RIL_SIM_ERROR = 29 - RESULT_RIL_SIM_NOT_READY = 30 - RESULT_RIL_NETWORK_REJECT = 31 - RESULT_RIL_NETWORK_TIMEOUT = 32 - RESULT_RIL_NETWORK_CONGESTION = 33 - RESULT_RIL_NO_ACKNOWLEDGMENT = 34 - RESULT_RIL_SMSC_ADDRESS_NOT_SET = 35 - RESULT_RIL_FDN_CHECK_FAILURE = 36 - RESULT_RIL_RESET_CANCELLED = 37 - RESULT_RIL_SUBSCRIPTION_NOT_AVAILABLE = 38 - RESULT_RIL_SIM_SMS_PROVIDER_ERROR = 39 - RESULT_RIL_SMS_FORMAT_ERROR = 40 - RESULT_RIL_CODING_ERROR = 41 - RESULT_RIL_SMS_CAUSE_CODE_ERROR = 42 - RESULT_RIL_SMS_TRANSPORT_ERROR = 43 - - STATUS_ON_ICC_FREE = 0 - STATUS_ON_ICC_READ = 1 - STATUS_ON_ICC_UNREAD = 3 - STATUS_ON_ICC_SENT = 5 - STATUS_ON_ICC_UNSENT = 7 - - SMS_TYPE_INCOMING = 0 - SMS_TYPE_OUTGOING = 1 - SMS_TYPE_STATUS_REPORT = 2 - SMS_TYPE_CELL_BROADCAST = 3 - SMS_TYPE_WAP_PUSH = 4 - - # SMS 编码方式 - ENCODING_7BIT = 0 - ENCODING_8BIT = 1 - ENCODING_16BIT = 2 - ENCODING_UNKNOWN = -1 - - # 消息有效期 - VALIDITY_PERIOD_DEFAULT = -1 - VALIDITY_PERIOD_NO_EXPIRY = 0 - VALIDITY_PERIOD_1_HOUR = 1 - VALIDITY_PERIOD_6_HOURS = 2 - VALIDITY_PERIOD_24_HOURS = 3 - VALIDITY_PERIOD_MAX = 4 - - _instance: Optional[SmsManager] = None - - def __init__(self): - super().__init__() - self._smsc_address: str = "" - self._default_sms_app: bool = True - self._is_sim_ready: bool = True - - @staticmethod - def getDefault() -> SmsManager: - """获取默认 SmsManager 实例""" - if SmsManager._instance is None: - SmsManager._instance = SmsManager() - return SmsManager._instance - - def sendTextMessage(self, dest_address: str, sc_address: Optional[str], - text: str, sent_intent: Any, delivery_intent: Any): - """发送文本短信""" - pass - - def sendMultipartTextMessage(self, dest_address: str, sc_address: Optional[str], - parts: List[str], sent_intents: Optional[List[Any]], - delivery_intents: Optional[List[Any]]): - """发送多段文本短信""" - pass - - def sendDataMessage(self, dest_address: str, sc_address: Optional[str], - dest_port: int, data: bytes, sent_intent: Any, - delivery_intent: Any): - """发送数据短信""" - pass - - def sendMultimediaMessage(self, context: Any, content_uri: str, - location_url: Optional[str], - config_overrides: Optional[Any], - sent_intent: Any): - """发送彩信""" - pass - - def downloadMultimediaMessage(self, context: Any, content_uri: str, - config_overrides: Optional[Any], - downloaded_intent: Any): - """下载彩信""" - pass - - def copyMessageToIcc(self, status: int, pdu: bytes, smsc: bytes) -> bool: - """复制短信到 ICC""" - return True - - def deleteMessageFromIcc(self, message_index: int) -> bool: - """从 ICC 删除短信""" - return True - - def updateMessageOnIcc(self, message_index: int, status: int, pdu: bytes) -> bool: - """更新 ICC 上的短信""" - return True - - def copyMessageToSim(self, status: int, pdu: bytes, smsc: bytes) -> bool: - """复制短信到 SIM 卡""" - return True - - def deleteMessageFromSim(self, message_index: int) -> bool: - """从 SIM 卡删除短信""" - return True - - def updateMessageOnSim(self, message_index: int, status: int, pdu: bytes) -> bool: - """更新 SIM 卡上的短信""" - return True - - def getAllMessagesFromIcc(self) -> List[Any]: - """从 ICC 获取所有短信""" - return [] - - def getAllMessagesFromSim(self) -> List[Any]: - """从 SIM 卡获取所有短信""" - return [] - - def getSmscAddress(self) -> str: - """获取 SMSC 地址""" - return self._smsc_address - - def setSmscAddress(self, address: str): - """设置 SMSC 地址""" - self._smsc_address = address - - def getDefaultSmsSubscriptionId(self) -> int: - """获取默认短信订阅 ID""" - return 0 - - def isDefaultSmsApp(self) -> bool: - """检查是否为默认短信应用""" - return self._default_sms_app - - def createAppSpecificSmsToken(self, prefixes: List[str]) -> str: - """创建应用特定短信令牌""" - return f"sms_token_{int(time.time())}" - - @staticmethod - def getDefaultSmsSubscriptionId() -> int: - return 0 - - @staticmethod - def getSmsManagerForSubscriptionId(sub_id: int) -> SmsManager: - """获取指定订阅的 SmsManager""" - return SmsManager() - - def sendTextMessageWithSelfPermissions(self, dest_address: str, sc_address: Optional[str], - text: str, sent_intent: Any, - delivery_intent: Any): - """使用自身权限发送文本短信""" - self.sendTextMessage(dest_address, sc_address, text, sent_intent, delivery_intent) - - def injectSmsPdu(self, pdu: bytes, format: str, result: Any): - """注入短信 PDU""" - pass - - def toString(self) -> str: - return "SmsManager(default)" - - -# ═══════════════════════════════════════════════════════════════ -# GSM 短信状态与事件 -# ═══════════════════════════════════════════════════════════════ - -class SmsMessage(Stub): - """SmsMessage — GSM 短信""" - - ENCODING_7BIT = 0 - ENCODING_8BIT = 1 - ENCODING_16BIT = 2 - ENCODING_UNKNOWN = -1 - - MAX_USER_DATA_BYTES = 140 - MAX_USER_DATA_SEPTETS = 160 - MAX_USER_DATA_SEPTETS_WITH_HEADER = 153 - - # 消息类型 - MESSAGE_TYPE_INBOX = 0 - MESSAGE_TYPE_SENT = 1 - MESSAGE_TYPE_DRAFT = 2 - MESSAGE_TYPE_OUTBOX = 3 - MESSAGE_TYPE_FAILED = 4 - MESSAGE_TYPE_QUEUED = 5 - - def __init__(self): - super().__init__() - self._message_body: str = "" - self._originating_address: str = "" - self._service_center: str = "" - self._timestamp: int = 0 - self._timestamp_sent: int = 0 - self._protocol_identifier: int = 0 - self._status: int = -1 - self._message_type: int = SmsMessage.MESSAGE_TYPE_INBOX - self._index_on_icc: int = -1 - self._pdu: bytes = b"" - self._is_status_report: bool = False - self._is_replace: bool = False - self._is_cphs: bool = False - self._email_body: str = "" - self._email_from: str = "" - self._pseudo_subject: str = "" - self._user_data: bytes = b"" - self._user_data_header: bytes = b"" - - def getMessageBody(self) -> str: - return self._message_body - - def setMessageBody(self, body: str): - self._message_body = body - - def getOriginatingAddress(self) -> str: - return self._originating_address - - def setOriginatingAddress(self, address: str): - self._originating_address = address - - def getServiceCenterAddress(self) -> str: - return self._service_center - - def setServiceCenterAddress(self, sc: str): - self._service_center = sc - - def getTimestampMillis(self) -> int: - return self._timestamp - - def setTimestampMillis(self, ts: int): - self._timestamp = ts - - def getStatus(self) -> int: - return self._status - - def setStatus(self, status: int): - self._status = status - - def getProtocolIdentifier(self) -> int: - return self._protocol_identifier - - def setProtocolIdentifier(self, pid: int): - self._protocol_identifier = pid - - def getIndexOnIcc(self) -> int: - return self._index_on_icc - - def setIndexOnIcc(self, index: int): - self._index_on_icc = index - - def isStatusReportMessage(self) -> bool: - return self._is_status_report - - def setStatusReportMessage(self, report: bool): - self._is_status_report = report - - def isReplaceMessage(self) -> bool: - return self._is_replace - - def isCphsMessage(self) -> bool: - return self._is_cphs - - def getEmailBody(self) -> str: - return self._email_body - - def getEmailFrom(self) -> str: - return self._email_from - - def getPseudoSubject(self) -> str: - return self._pseudo_subject - - def getUserData(self) -> bytes: - return self._user_data - - def getUserDataHeader(self) -> bytes: - return self._user_data_header - - def getPdu(self) -> bytes: - return self._pdu - - def setPdu(self, pdu: bytes): - self._pdu = pdu - - @staticmethod - def getSubmitPdu(sc_address: str, dest_address: str, message: str, - status_report_requested: bool) -> Tuple[bytes, bytes]: - """获取提交 PDU""" - return (b"", b"") - - @staticmethod - def getSubmitPduForData(dest_address: str, sc_address: str, - dest_port: int, data: bytes, - status_report_requested: bool) -> Tuple[bytes, bytes]: - """获取数据提交 PDU""" - return (b"", b"") - - @staticmethod - def createFromPdu(pdu: bytes, format: str = "3gpp") -> Optional[SmsMessage]: - """从 PDU 创建短信""" - return SmsMessage() - - @staticmethod - def calculateLength(message: str, use7bitOnly: bool = False) -> Any: - """计算消息长度""" - class LengthResult(Stub): - def __init__(self): - super().__init__() - self.msg_count = 1 - self.remaining_septets = 0 - self.code_unit_size = 1 - return LengthResult() - - def toString(self) -> str: - return f"SmsMessage(from={self._originating_address}, body={self._message_body[:20]})" - - -# ═══════════════════════════════════════════════════════════════ -# GSM CellBroadcast -# ═══════════════════════════════════════════════════════════════ - -class CellBroadcastMessage(Stub): - """CellBroadcastMessage — GSM 小区广播消息""" - - GEOGRAPHICAL_SCOPE_PLMN = 0 - GEOGRAPHICAL_SCOPE_CELL = 1 - GEOGRAPHICAL_SCOPE_LA = 2 - GEOGRAPHICAL_SCOPE_CELL_WIDE = 3 - - def __init__(self): - super().__init__() - self._message_body: str = "" - self._channel_id: int = 0 - self._serial_number: int = 0 - self._message_code: int = 0 - self._language_code: str = "" - self._geographical_scope: int = 0 - self._update_number: int = 0 - self._is_emergency: bool = False - self._is_etws: bool = False - self._is_cmas: bool = False - self._service_category: int = 0 - self._plmn: str = "" - self._lac: int = -1 - self._cid: int = -1 - - def getMessageBody(self) -> str: - return self._message_body - - def setMessageBody(self, body: str): - self._message_body = body - - def getChannelId(self) -> int: - return self._channel_id - - def setChannelId(self, channel_id: int): - self._channel_id = channel_id - - def getSerialNumber(self) -> int: - return self._serial_number - - def setSerialNumber(self, sn: int): - self._serial_number = sn - - def getMessageCode(self) -> int: - return self._message_code - - def setMessageCode(self, code: int): - self._message_code = code - - def getLanguageCode(self) -> str: - return self._language_code - - def setLanguageCode(self, lang: str): - self._language_code = lang - - def getGeographicalScope(self) -> int: - return self._geographical_scope - - def setGeographicalScope(self, scope: int): - self._geographical_scope = scope - - def isEmergencyMessage(self) -> bool: - return self._is_emergency - - def setEmergencyMessage(self, emergency: bool): - self._is_emergency = emergency - - def isEtwsMessage(self) -> bool: - return self._is_etws - - def setEtwsMessage(self, etws: bool): - self._is_etws = etws - - def isCmasMessage(self) -> bool: - return self._is_cmas - - def setCmasMessage(self, cmas: bool): - self._is_cmas = cmas - - def toString(self) -> str: - return f"CellBroadcastMessage(channel={self._channel_id}, body={self._message_body[:20]})" - - -# ═══════════════════════════════════════════════════════════════ -# GSM ServiceState (GSM-specific) -# ═══════════════════════════════════════════════════════════════ - -class GsmServiceState(Stub): - """GsmServiceState — GSM 服务状态""" - - STATE_IN_SERVICE = 0 - STATE_OUT_OF_SERVICE = 1 - STATE_EMERGENCY_ONLY = 2 - STATE_POWER_OFF = 3 - STATE_RADIO_OFF = 3 - - ROAMING_TYPE_NOT_ROAMING = 0 - ROAMING_TYPE_DOMESTIC = 1 - ROAMING_TYPE_INTERNATIONAL = 2 - - def __init__(self): - super().__init__() - self._state: int = GsmServiceState.STATE_OUT_OF_SERVICE - self._operator_alpha_long: str = "" - self._operator_alpha_short: str = "" - self._operator_numeric: str = "" - self._is_manual_network_selection: bool = False - self._roaming: bool = False - self._roaming_type: int = GsmServiceState.ROAMING_TYPE_NOT_ROAMING - self._emergency_only: bool = False - self._voice_registered: bool = False - self._data_registered: bool = False - self._voice_network_type: int = 0 - self._data_network_type: int = 0 - self._css_indicator: bool = False - self._network_id: str = "" - self._plmn: str = "" - self._lac: int = -1 - self._cid: int = -1 - - def getState(self) -> int: - return self._state - - def setState(self, state: int): - self._state = state - - def getOperatorAlphaLong(self) -> str: - return self._operator_alpha_long - - def setOperatorAlphaLong(self, op: str): - self._operator_alpha_long = op - - def getOperatorAlphaShort(self) -> str: - return self._operator_alpha_short - - def setOperatorAlphaShort(self, op: str): - self._operator_alpha_short = op - - def getOperatorNumeric(self) -> str: - return self._operator_numeric - - def setOperatorNumeric(self, op: str): - self._operator_numeric = op - - def getIsManualNetworkSelection(self) -> bool: - return self._is_manual_network_selection - - def setIsManualNetworkSelection(self, manual: bool): - self._is_manual_network_selection = manual - - def getRoaming(self) -> bool: - return self._roaming - - def setRoaming(self, roaming: bool): - self._roaming = roaming - - def getRoamingType(self) -> int: - return self._roaming_type - - def setRoamingType(self, roaming_type: int): - self._roaming_type = roaming_type - - def isEmergencyOnly(self) -> bool: - return self._emergency_only - - def setEmergencyOnly(self, emergency: bool): - self._emergency_only = emergency - - def isVoiceRegistered(self) -> bool: - return self._voice_registered - - def setVoiceRegistered(self, registered: bool): - self._voice_registered = registered - - def isDataRegistered(self) -> bool: - return self._data_registered - - def setDataRegistered(self, registered: bool): - self._data_registered = registered - - def getVoiceNetworkType(self) -> int: - return self._voice_network_type - - def setVoiceNetworkType(self, type_: int): - self._voice_network_type = type_ - - def getDataNetworkType(self) -> int: - return self._data_network_type - - def setDataNetworkType(self, type_: int): - self._data_network_type = type_ - - def toString(self) -> str: - return f"GsmServiceState(state={self._state}, op={self._operator_alpha_long})" - - -# ═══════════════════════════════════════════════════════════════ -# GSM GPRS (数据相关) -# ═══════════════════════════════════════════════════════════════ - -class GprsCellLocation(Stub): - """GprsCellLocation — GPRS 小区位置""" - - def __init__(self, lac: int = -1, cid: int = -1): - super().__init__() - self._lac = lac - self._cid = cid - - def getLac(self) -> int: - return self._lac - - def setLac(self, lac: int): - self._lac = lac - - def getCid(self) -> int: - return self._cid - - def setCid(self, cid: int): - self._cid = cid - - def toString(self) -> str: - return f"GprsCellLocation(lac={self._lac}, cid={self._cid})" - - -# ═══════════════════════════════════════════════════════════════ -# GSM SMS Utils -# ═══════════════════════════════════════════════════════════════ - -class SmsUtils(Stub): - """SmsUtils — GSM 短信工具类""" - - @staticmethod - def getSmsFormat() -> str: - """获取短信格式""" - return "3gpp" - - @staticmethod - def is7bitOnly(message: str) -> bool: - """检查是否仅为 7 位字符""" - for c in message: - if ord(c) > 127: - return False - return True - - @staticmethod - def countSeptets(message: str, use7bitOnly: bool = False) -> int: - """计算 7 位编码消息的字节数""" - if use7bitOnly or SmsUtils.is7bitOnly(message): - return len(message) - return len(message.encode("utf-16-be")) // 2 - - @staticmethod - def countGsmBytes(message: str) -> int: - """计算 GSM 字节数""" - if SmsUtils.is7bitOnly(message): - return len(message) - return len(message.encode("utf-16-be")) - - @staticmethod - def splitMessage(text: str) -> List[str]: - """分割长消息""" - if len(text) <= 160: - return [text] - parts = [] - for i in range(0, len(text), 153): - parts.append(text[i:i + 153]) - return parts - - @staticmethod - def getMaxMessageLength() -> int: - """获取最大消息长度""" - return 160 - - @staticmethod - def getMaxUserDataBytes() -> int: - return 140 - - @staticmethod - def encodePhoneNumber(number: str) -> str: - """编码电话号码""" - if number.startswith("+"): - number = number[1:] - return number - - @staticmethod - def decodePhoneNumber(encoded: str) -> str: - """解码电话号码""" - return encoded - - @staticmethod - def isValidSmsAddress(address: str) -> bool: - """验证短信地址是否有效""" - return len(address) > 0 and len(address) <= 20 - - @staticmethod - def getSmsPduType(pdu: bytes) -> int: - """获取短信 PDU 类型""" - if len(pdu) > 0: - return pdu[0] & 0x03 - return -1 - - -# ═══════════════════════════════════════════════════════════════ -# GSM 网络相关常量 -# ═══════════════════════════════════════════════════════════════ - -class GsmNetworkType(Stub): - """GsmNetworkType — GSM 网络类型常量""" - - NETWORK_TYPE_UNKNOWN = 0 - NETWORK_TYPE_GPRS = 1 - NETWORK_TYPE_EDGE = 2 - NETWORK_TYPE_UMTS = 3 - NETWORK_TYPE_HSDPA = 8 - NETWORK_TYPE_HSUPA = 9 - NETWORK_TYPE_HSPA = 10 - NETWORK_TYPE_HSPAP = 15 - NETWORK_TYPE_LTE = 13 - NETWORK_TYPE_NR = 20 - - @staticmethod - def isGsm(network_type: int) -> bool: - return network_type in (GsmNetworkType.NETWORK_TYPE_GPRS, - GsmNetworkType.NETWORK_TYPE_EDGE) - - @staticmethod - def isUmts(network_type: int) -> bool: - return network_type in (GsmNetworkType.NETWORK_TYPE_UMTS, - GsmNetworkType.NETWORK_TYPE_HSDPA, - GsmNetworkType.NETWORK_TYPE_HSUPA, - GsmNetworkType.NETWORK_TYPE_HSPA, - GsmNetworkType.NETWORK_TYPE_HSPAP) - - @staticmethod - def toString(network_type: int) -> str: - names = { - GsmNetworkType.NETWORK_TYPE_UNKNOWN: "UNKNOWN", - GsmNetworkType.NETWORK_TYPE_GPRS: "GPRS", - GsmNetworkType.NETWORK_TYPE_EDGE: "EDGE", - GsmNetworkType.NETWORK_TYPE_UMTS: "UMTS", - GsmNetworkType.NETWORK_TYPE_HSDPA: "HSDPA", - GsmNetworkType.NETWORK_TYPE_HSUPA: "HSUPA", - GsmNetworkType.NETWORK_TYPE_HSPA: "HSPA", - GsmNetworkType.NETWORK_TYPE_HSPAP: "HSPA+", - GsmNetworkType.NETWORK_TYPE_LTE: "LTE", - GsmNetworkType.NETWORK_TYPE_NR: "NR", - } - return names.get(network_type, f"UNKNOWN({network_type})") - - -# ═══════════════════════════════════════════════════════════════ -# GSM SIM 操作 -# ═══════════════════════════════════════════════════════════════ - -class SimUtils(Stub): - """SimUtils — SIM 工具类""" - - @staticmethod - def getSimSerialNumber() -> str: - """获取 SIM 序列号""" - return "89014103211118510720" - - @staticmethod - def getSimOperator() -> str: - """获取 SIM 运营商""" - return "310410" - - @staticmethod - def getSimOperatorName() -> str: - """获取 SIM 运营商名称""" - return "AT&T" - - @staticmethod - def getSubscriberId() -> str: - """获取订阅者 ID (IMSI)""" - return "310410123456789" - - @staticmethod - def getDeviceId() -> str: - """获取设备 ID (IMEI)""" - return "356938035643809" - - @staticmethod - def getDeviceSoftwareVersion() -> str: - """获取设备软件版本""" - return "01" - - @staticmethod - def getVoiceMailNumber() -> str: - """获取语音信箱号码""" - return "+1-555-123-4567" - - @staticmethod - def getVoiceMailAlphaTag() -> str: - """获取语音信箱标签""" - return "Voicemail" - - @staticmethod - def hasIccCard() -> bool: - """检查是否有 ICC 卡""" - return True - - @staticmethod - def getSimState() -> int: - """获取 SIM 状态""" - return 5 # SIM_STATE_READY - - @staticmethod - def isNetworkRoaming() -> bool: - """检查是否网络漫游""" - return False - - @staticmethod - def getNetworkOperator() -> str: - """获取网络运营商""" - return "310410" - - @staticmethod - def getNetworkOperatorName() -> str: - """获取网络运营商名称""" - return "AT&T" - - @staticmethod - def getNetworkCountryIso() -> str: - """获取网络国家 ISO 代码""" - return "us" - - @staticmethod - def getSimCountryIso() -> str: - """获取 SIM 国家 ISO 代码""" - return "us" - - -# ═══════════════════════════════════════════════════════════════ -# GSM 通话相关 -# ═══════════════════════════════════════════════════════════════ - -class GsmCallState(Stub): - """GsmCallState — GSM 通话状态常量""" - - CALL_STATE_IDLE = 0 - CALL_STATE_RINGING = 1 - CALL_STATE_OFFHOOK = 2 - - CALL_STATE_ACTIVE = 3 - CALL_STATE_HOLDING = 4 - CALL_STATE_DIALING = 5 - CALL_STATE_ALERTING = 6 - CALL_STATE_INCOMING = 7 - CALL_STATE_WAITING = 8 - CALL_STATE_DISCONNECTED = 9 - CALL_STATE_DISCONNECTING = 10 - - @staticmethod - def toString(state: int) -> str: - names = { - GsmCallState.CALL_STATE_IDLE: "IDLE", - GsmCallState.CALL_STATE_RINGING: "RINGING", - GsmCallState.CALL_STATE_OFFHOOK: "OFFHOOK", - } - return names.get(state, f"UNKNOWN({state})") - - -# ═══════════════════════════════════════════════════════════════ -# GSM Supplementary Service -# ═══════════════════════════════════════════════════════════════ - -class SupplementaryService(Stub): - """SupplementaryService — GSM 补充服务""" - - SERVICE_CLASS_NONE = 0 - SERVICE_CLASS_VOICE = 1 - SERVICE_CLASS_DATA = 2 - SERVICE_CLASS_FAX = 4 - SERVICE_CLASS_SMS = 8 - SERVICE_CLASS_DATA_SYNC = 16 - SERVICE_CLASS_DATA_ASYNC = 32 - SERVICE_CLASS_PACKET = 64 - SERVICE_CLASS_PAD = 128 - SERVICE_CLASS_MAX = 255 - - CF_REASON_UNCONDITIONAL = 0 - CF_REASON_BUSY = 1 - CF_REASON_NO_REPLY = 2 - CF_REASON_NOT_REACHABLE = 3 - CF_REASON_ALL = 4 - CF_REASON_ALL_CONDITIONAL = 5 - - CB_SERVICE_ALL = 0 - CB_SERVICE_OUTGOING = 1 - CB_SERVICE_INCOMING = 2 - - def __init__(self): - super().__init__() - self._service_class: int = SupplementaryService.SERVICE_CLASS_NONE - self._cf_reason: int = SupplementaryService.CF_REASON_UNCONDITIONAL - self._cb_service: int = SupplementaryService.CB_SERVICE_ALL - - def getServiceClass(self) -> int: - return self._service_class - - def setServiceClass(self, service_class: int): - self._service_class = service_class - - def getCallForwardReason(self) -> int: - return self._cf_reason - - def setCallForwardReason(self, reason: int): - self._cf_reason = reason - - def getCallBarringService(self) -> int: - return self._cb_service - - def setCallBarringService(self, service: int): - self._cb_service = service - - def toString(self) -> str: - return f"SupplementaryService(class={self._service_class})" \ No newline at end of file diff --git a/android_pyapi/android/telephony_ims.py b/android_pyapi/android/telephony_ims.py deleted file mode 100644 index 6dc636f..0000000 --- a/android_pyapi/android/telephony_ims.py +++ /dev/null @@ -1,1545 +0,0 @@ -""" -Android Telephony IMS 组件 — ImsManager, ImsService, ImsCall, ImsCallSession, MmTelFeature 等 -方法签名完全对齐 Android SDK (android.telephony.ims 包) -""" - -from __future__ import annotations - -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# IMS 常量 -# ═══════════════════════════════════════════════════════════════ - -IMS_SERVICE_CLASS_NONE = 0 -IMS_SERVICE_CLASS_VOICE = 1 -IMS_SERVICE_CLASS_VIDEO = 2 -IMS_SERVICE_CLASS_SMS = 4 -IMS_SERVICE_CLASS_UT = 8 - -IMS_REGISTRATION_STATE_NOT_REGISTERED = 0 -IMS_REGISTRATION_STATE_REGISTERING = 1 -IMS_REGISTRATION_STATE_REGISTERED = 2 - -IMS_REGISTRATION_TECH_NONE = 0 -IMS_REGISTRATION_TECH_LTE = 1 -IMS_REGISTRATION_TECH_IWLAN = 2 -IMS_REGISTRATION_TECH_NR = 3 - -IMS_CALL_STATE_IDLE = 0 -IMS_CALL_STATE_ACTIVE = 1 -IMS_CALL_STATE_HOLDING = 2 -IMS_CALL_STATE_DIALING = 3 -IMS_CALL_STATE_ALERTING = 4 -IMS_CALL_STATE_INCOMING = 5 -IMS_CALL_STATE_WAITING = 6 -IMS_CALL_STATE_DISCONNECTED = 7 -IMS_CALL_STATE_DISCONNECTING = 8 - -IMS_CALL_STATE_UNKNOWN = -1 -IMS_CALL_STATE_CONFERENCE = 9 -IMS_CALL_STATE_REFER = 10 -IMS_CALL_STATE_UPDATE = 11 - -IMS_FAILURE_REASON_UNKNOWN = 0 -IMS_FAILURE_REASON_NETWORK = 1 -IMS_FAILURE_REASON_SERVER = 2 -IMS_FAILURE_REASON_TIMEOUT = 3 -IMS_FAILURE_REASON_INTERNAL = 4 -IMS_FAILURE_REASON_BUSY = 5 -IMS_FAILURE_REASON_NOT_FOUND = 6 -IMS_FAILURE_REASON_FORBIDDEN = 7 -IMS_FAILURE_REASON_SERVICE_UNAVAILABLE = 8 -IMS_FAILURE_REASON_REQUEST_TIMEOUT = 9 -IMS_FAILURE_REASON_TEMPORARILY_UNAVAILABLE = 10 -IMS_FAILURE_REASON_MEDIA_NO_ANSWER = 11 -IMS_FAILURE_REASON_MEDIA_REJECTED = 12 -IMS_FAILURE_REASON_MEDIA_CANCELLED = 13 -IMS_FAILURE_REASON_MEDIA_INSUFFICIENT_BANDWIDTH = 14 -IMS_FAILURE_REASON_CODE_TEMPORARY_FAILURE = 15 -IMS_FAILURE_REASON_CODE_CONGESTION = 16 -IMS_FAILURE_REASON_CODE_REQUESTED_TERMINATED = 17 -IMS_FAILURE_REASON_CODE_BEARER_NOT_AVAILABLE = 18 -IMS_FAILURE_REASON_CODE_INSUFFICIENT_RESOURCES = 19 -IMS_FAILURE_REASON_CODE_REJECTED = 20 -IMS_FAILURE_REASON_CODE_UNSPECIFIED = 21 - -IMS_MEDIA_TYPE_AUDIO = 1 -IMS_MEDIA_TYPE_VIDEO = 2 -IMS_MEDIA_TYPE_TEXT = 3 - -IMS_SERVICE_TYPE_VOLTE = 0 -IMS_SERVICE_TYPE_VIDEO = 1 -IMS_SERVICE_TYPE_WFC = 2 -IMS_SERVICE_TYPE_SMS = 3 -IMS_SERVICE_TYPE_UT = 4 - - -# ═══════════════════════════════════════════════════════════════ -# ImsReasonInfo -# ═══════════════════════════════════════════════════════════════ - -class ImsReasonInfo(Stub): - """ImsReasonInfo — IMS 原因信息""" - - CODE_USER_TERMINATED = 0 - CODE_USER_TERMINATED_BY_REMOTE = 1 - CODE_USER_TRIGGERED_END_OF_CALL = 2 - CODE_USER_ANSWERED_ELSEWHERE = 3 - CODE_USER_IGNORE = 4 - CODE_USER_DECLINE = 5 - CODE_USER_NOANSWER = 6 - CODE_NETWORK_ERROR = 100 - CODE_SIP_REDIRECTED = 200 - CODE_SIP_BAD_REQUEST = 400 - CODE_SIP_FORBIDDEN = 403 - CODE_SIP_NOT_FOUND = 404 - CODE_SIP_REQUEST_TIMEOUT = 408 - CODE_SIP_TEMPORARILY_UNAVAILABLE = 480 - CODE_SIP_BUSY = 486 - CODE_SIP_REQUEST_TERMINATED = 487 - CODE_SIP_SERVER_INTERNAL_ERROR = 500 - CODE_SIP_SERVICE_UNAVAILABLE = 503 - CODE_SIP_SERVER_TIMEOUT = 504 - CODE_SIP_VERSION_NOT_SUPPORTED = 505 - CODE_SIP_BUSY_EVERYWHERE = 600 - CODE_SIP_DECLINE = 603 - CODE_SIP_DOES_NOT_EXIST_ANYWHERE = 604 - CODE_SIP_NOT_ACCEPTABLE = 606 - CODE_MEDIA_INIT_FAILED = 700 - CODE_MEDIA_NO_DATA = 701 - CODE_MEDIA_NOT_ACCEPTABLE = 702 - CODE_MEDIA_UNSUPPORTED = 703 - CODE_MEDIA_SESSION_TERMINATED = 704 - CODE_MEDIA_NO_SESSION = 705 - CODE_MEDIA_RTP_TIMEOUT = 706 - CODE_MEDIA_RTCP_TIMEOUT = 707 - CODE_MEDIA_RESOURCE_LIMIT = 708 - CODE_MEDIA_INSUFFICIENT_BANDWIDTH = 709 - CODE_MEDIA_LATE_MEDIA = 710 - CODE_MEDIA_UNKNOWN = 711 - CODE_SIP_ALTERNATE_EMERGENCY_CALL = 712 - CODE_LOCAL_ILLEGAL_ARGUMENT = 800 - CODE_LOCAL_ILLEGAL_STATE = 801 - CODE_LOCAL_INTERNAL_ERROR = 802 - CODE_LOCAL_IMS_SERVICE_DOWN = 803 - CODE_LOCAL_NO_PENDING_CALL = 804 - CODE_LOCAL_SERVICE_NOT_RUNNING = 805 - CODE_LOCAL_SERVICE_NOT_READY = 806 - CODE_LOCAL_SERVICE_TERMINATED = 807 - CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 808 - - EXTRA_CODE_CALL_RETRY_NEEDED = 1000 - EXTRA_CODE_CALL_RETRY_SIP = 1001 - EXTRA_CODE_CALL_RETRY_NORMAL = 1002 - EXTRA_CODE_CALL_RETRY_EMERGENCY = 1003 - - def __init__(self, code: int = CODE_USER_TERMINATED, - extra_code: int = 0, message: str = ""): - super().__init__() - self._code = code - self._extra_code = extra_code - self._message = message - - def getCode(self) -> int: - return self._code - - def getExtraCode(self) -> int: - return self._extra_code - - def getExtraMessage(self) -> str: - return self._message - - def toString(self) -> str: - return f"ImsReasonInfo(code={self._code}, extra={self._extra_code}, msg={self._message})" - - -# ═══════════════════════════════════════════════════════════════ -# ImsCallProfile -# ═══════════════════════════════════════════════════════════════ - -class ImsCallProfile(Stub): - """ImsCallProfile — IMS 呼叫配置文件""" - - SERVICE_TYPE_NORMAL = 0 - SERVICE_TYPE_EMERGENCY = 1 - - CALL_TYPE_VOICE = 1 - CALL_TYPE_VT = 2 - CALL_TYPE_VT_TX = 3 - CALL_TYPE_VT_RX = 4 - CALL_TYPE_VT_NODIR = 5 - CALL_TYPE_SMS = 6 - - DIALSTRING_NORMAL = 0 - DIALSTRING_SS_CONF = 1 - DIALSTRING_USSD = 2 - - EXTRA_CALL_MODE_CHANGEABLE = "call_mode_changeable" - EXTRA_CONFERENCE_ALLOWED = "conference_allowed" - EXTRA_ADDITIONAL_CALL_INFO = "additional_call_info" - EXTRA_CHILD_NETWORK_TYPE = "child_network_type" - EXTRA_CNAPN = "cnapn" - EXTRA_DIALSTRING = "dialstring" - EXTRA_OI = "oi" - EXTRA_OIR = "oir" - EXTRA_CNAP = "cnap" - EXTRA_USSD = "ussd" - EXTRA_CALL_RAT = "call_rat" - EXTRA_CALL_RAT_TYPE = "call_rat_type" - EXTRA_RTT_STATE = "rtt_state" - EXTRA_PRIORITY = "priority" - EXTRA_PEER_CONNECTION_TYPE = "peer_connection_type" - EXTRA_IS_PULL = "is_pull" - EXTRA_ADD_IMS_HANDOVER = "add_ims_handover" - - def __init__(self, service_type: int = SERVICE_TYPE_NORMAL, - call_type: int = CALL_TYPE_VOICE): - super().__init__() - self._service_type = service_type - self._call_type = call_type - self._media_profiles: List[ImsStreamMediaProfile] = [] - self._extras: Dict[str, Any] = {} - - def getServiceType(self) -> int: - return self._service_type - - def setServiceType(self, service_type: int): - self._service_type = service_type - - def getCallType(self) -> int: - return self._call_type - - def setCallType(self, call_type: int): - self._call_type = call_type - - def getMediaProfile(self) -> Optional[ImsStreamMediaProfile]: - if self._media_profiles: - return self._media_profiles[0] - return None - - def setMediaProfile(self, profile: ImsStreamMediaProfile): - if self._media_profiles: - self._media_profiles[0] = profile - else: - self._media_profiles.append(profile) - - def getMediaProfiles(self) -> List[ImsStreamMediaProfile]: - return list(self._media_profiles) - - def setMediaProfiles(self, profiles: List[ImsStreamMediaProfile]): - self._media_profiles = list(profiles) - - def getExtra(self, key: str) -> Optional[Any]: - return self._extras.get(key) - - def setExtra(self, key: str, value: Any): - self._extras[key] = value - - def toString(self) -> str: - return f"ImsCallProfile(type={self._call_type}, service={self._service_type})" - - -# ═══════════════════════════════════════════════════════════════ -# ImsStreamMediaProfile -# ═══════════════════════════════════════════════════════════════ - -class ImsStreamMediaProfile(Stub): - """ImsStreamMediaProfile — IMS 流媒体配置文件""" - - DIRECTION_INVALID = -1 - DIRECTION_INACTIVE = 0 - DIRECTION_RECEIVE = 1 - DIRECTION_SEND = 2 - DIRECTION_SEND_RECEIVE = 3 - - RTT_MODE_DISABLED = 0 - RTT_MODE_ENABLED = 1 - RTT_MODE_FULL = 2 - - AUDIO_QUALITY_NONE = 0 - AUDIO_QUALITY_AMR = 1 - AUDIO_QUALITY_AMR_WB = 2 - AUDIO_QUALITY_QCELP13K = 3 - AUDIO_QUALITY_EVRC = 4 - AUDIO_QUALITY_EVRC_B = 5 - AUDIO_QUALITY_EVRC_WB = 6 - AUDIO_QUALITY_EVRC_NW = 7 - AUDIO_QUALITY_GSM_EFR = 8 - AUDIO_QUALITY_GSM_FR = 9 - AUDIO_QUALITY_GSM_HR = 10 - AUDIO_QUALITY_G722 = 11 - AUDIO_QUALITY_G723 = 12 - AUDIO_QUALITY_G711 = 13 - AUDIO_QUALITY_G729 = 14 - AUDIO_QUALITY_EVS_NB = 15 - AUDIO_QUALITY_EVS_WB = 16 - AUDIO_QUALITY_EVS_SWB = 17 - AUDIO_QUALITY_EVS_FB = 18 - - VIDEO_QUALITY_NONE = 0 - VIDEO_QUALITY_QCIF = 1 - VIDEO_QUALITY_QVGA = 2 - VIDEO_QUALITY_CIF = 3 - VIDEO_QUALITY_VGA = 4 - VIDEO_QUALITY_720P = 5 - VIDEO_QUALITY_1080P = 6 - VIDEO_QUALITY_4K = 7 - - def __init__(self): - super().__init__() - self._audio_quality = ImsStreamMediaProfile.AUDIO_QUALITY_NONE - self._video_quality = ImsStreamMediaProfile.VIDEO_QUALITY_NONE - self._audio_direction = ImsStreamMediaProfile.DIRECTION_INVALID - self._video_direction = ImsStreamMediaProfile.DIRECTION_INVALID - self._rtt_mode = ImsStreamMediaProfile.RTT_MODE_DISABLED - self._audio_codec: str = "" - self._video_codec: str = "" - self._audio_bitrate: int = 0 - self._video_bitrate: int = 0 - self._audio_sample_rate: int = 0 - - def getAudioQuality(self) -> int: - return self._audio_quality - - def setAudioQuality(self, quality: int): - self._audio_quality = quality - - def getVideoQuality(self) -> int: - return self._video_quality - - def setVideoQuality(self, quality: int): - self._video_quality = quality - - def getAudioDirection(self) -> int: - return self._audio_direction - - def setAudioDirection(self, direction: int): - self._audio_direction = direction - - def getVideoDirection(self) -> int: - return self._video_direction - - def setVideoDirection(self, direction: int): - self._video_direction = direction - - def getRttMode(self) -> int: - return self._rtt_mode - - def setRttMode(self, mode: int): - self._rtt_mode = mode - - def getAudioCodec(self) -> str: - return self._audio_codec - - def setAudioCodec(self, codec: str): - self._audio_codec = codec - - def getVideoCodec(self) -> str: - return self._video_codec - - def setVideoCodec(self, codec: str): - self._video_codec = codec - - def isRttCall(self) -> bool: - return self._rtt_mode != ImsStreamMediaProfile.RTT_MODE_DISABLED - - def toString(self) -> str: - return f"ImsStreamMediaProfile(audio={self._audio_quality}, video={self._video_quality})" - - -# ═══════════════════════════════════════════════════════════════ -# ImsCallSession -# ═══════════════════════════════════════════════════════════════ - -class ImsCallSession(Stub): - """ImsCallSession — IMS 呼叫会话""" - - STATE_IDLE = 0 - STATE_NEGOTIATION = 1 - STATE_ESTABLISHING = 2 - STATE_ESTABLISHED = 3 - STATE_TERMINATING = 4 - STATE_TERMINATED = 5 - STATE_MODIFYING = 6 - STATE_HOLDING = 7 - STATE_RESUMING = 8 - STATE_TRANSFERRING = 9 - STATE_CONFERENCE = 10 - STATE_REFER = 11 - STATE_UPDATE = 12 - - class Listener(Stub): - """ImsCallSession.Listener — 呼叫会话监听器""" - - def onCallStateChanged(self, session: ImsCallSession, state: int): - pass - - def onCallProfileChanged(self, session: ImsCallSession, profile: ImsCallProfile): - pass - - def onCallQualityChanged(self, session: ImsCallSession, quality: int, video_quality: int): - pass - - def onCallSessionHeld(self, session: ImsCallSession, held: bool): - pass - - def onCallSessionHoldFailed(self, session: ImsCallSession, reason: ImsReasonInfo): - pass - - def onCallSessionHoldReceived(self, session: ImsCallSession, profile: ImsCallProfile): - pass - - def onCallSessionResumeFailed(self, session: ImsCallSession, reason: ImsReasonInfo): - pass - - def onCallSessionResumeReceived(self, session: ImsCallSession, profile: ImsCallProfile): - pass - - def onCallSessionMergeStarted(self, session: ImsCallSession, - new_session: ImsCallSession, profile: ImsCallProfile): - pass - - def onCallSessionMergeComplete(self, session: ImsCallSession): - pass - - def onCallSessionMergeFailed(self, session: ImsCallSession, reason: ImsReasonInfo): - pass - - def onCallSessionUpdated(self, session: ImsCallSession, profile: ImsCallProfile): - pass - - def onCallSessionUpdateFailed(self, session: ImsCallSession, reason: ImsReasonInfo): - pass - - def onCallSessionUpdateReceived(self, session: ImsCallSession, profile: ImsCallProfile): - pass - - def onCallSessionTtyModeReceived(self, session: ImsCallSession, mode: int): - pass - - def onCallSessionHandover(self, session: ImsCallSession, - src_tech: int, target_tech: int, reason: ImsReasonInfo): - pass - - def onCallSessionHandoverFailed(self, session: ImsCallSession, - src_tech: int, target_tech: int, - reason: ImsReasonInfo): - pass - - def onCallSessionMayHandover(self, session: ImsCallSession, src_tech: int, target_tech: int): - pass - - def onCallSessionDisconnected(self, session: ImsCallSession, reason: ImsReasonInfo): - pass - - def onCallSessionInitiated(self, session: ImsCallSession, profile: ImsCallProfile): - pass - - def onCallSessionRttModifyRequestReceived(self, session: ImsCallSession, profile: ImsCallProfile): - pass - - def onCallSessionRttModifyResponseReceived(self, session: ImsCallSession, status: int): - pass - - def onCallSessionRttMessageReceived(self, session: ImsCallSession, message: str): - pass - - def onCallSessionMultipartyStateChanged(self, session: ImsCallSession, is_multi_party: bool): - pass - - def onCallSessionConferenceStateUpdated(self, session: ImsCallSession, state: Any): - pass - - def onCallSessionTerminated(self, session: ImsCallSession, reason: ImsReasonInfo): - pass - - def __init__(self, context: Any = None, profile: ImsCallProfile = None): - super().__init__() - self._context = context - self._profile = profile or ImsCallProfile() - self._state = ImsCallSession.STATE_IDLE - self._listener: Optional[ImsCallSession.Listener] = None - self._call_id: str = "" - self._callee: str = "" - self._is_multiparty: bool = False - self._media_profile: Optional[ImsStreamMediaProfile] = None - - def setListener(self, listener: ImsCallSession.Listener): - self._listener = listener - - def getListener(self) -> Optional[ImsCallSession.Listener]: - return self._listener - - def getCallId(self) -> str: - return self._call_id - - def setCallId(self, call_id: str): - self._call_id = call_id - - def getCallee(self) -> str: - return self._callee - - def setCallee(self, callee: str): - self._callee = callee - - def getState(self) -> int: - return self._state - - def setState(self, state: int): - self._state = state - - def getCallProfile(self) -> ImsCallProfile: - return self._profile - - def getLocalCallProfile(self) -> ImsCallProfile: - return self._profile - - def getRemoteCallProfile(self) -> ImsCallProfile: - return self._profile - - def getMediaProfile(self) -> Optional[ImsStreamMediaProfile]: - return self._media_profile - - def isMultiparty(self) -> bool: - return self._is_multiparty - - def start(self, callee: str, profile: ImsCallProfile): - self._callee = callee - if profile: - self._profile = profile - self._state = ImsCallSession.STATE_ESTABLISHING - self._call_id = f"ims_call_{int(time.time() * 1000)}" - if self._listener: - self._listener.onCallStateChanged(self, self._state) - self._listener.onCallSessionInitiated(self, self._profile) - - def accept(self, call_type: int, profile: ImsStreamMediaProfile): - self._state = ImsCallSession.STATE_ESTABLISHED - self._media_profile = profile - if self._listener: - self._listener.onCallStateChanged(self, self._state) - self._listener.onCallProfileChanged(self, self._profile) - - def reject(self, reason: int): - self._state = ImsCallSession.STATE_TERMINATING - if self._listener: - reason_info = ImsReasonInfo(reason) - self._listener.onCallSessionTerminated(self, reason_info) - - def terminate(self, reason: int = 0): - self._state = ImsCallSession.STATE_TERMINATED - if self._listener: - reason_info = ImsReasonInfo(reason) - self._listener.onCallSessionTerminated(self, reason_info) - self._listener.onCallStateChanged(self, self._state) - - def hold(self): - self._state = ImsCallSession.STATE_HOLDING - if self._listener: - self._listener.onCallStateChanged(self, self._state) - self._listener.onCallSessionHeld(self, True) - - def resume(self): - self._state = ImsCallSession.STATE_ESTABLISHED - if self._listener: - self._listener.onCallStateChanged(self, self._state) - self._listener.onCallSessionResumeReceived(self, self._profile) - - def merge(self): - if self._listener: - self._listener.onCallSessionMergeStarted(self, ImsCallSession(), self._profile) - - def sendDtmf(self, c: str, result: Any = None): - pass - - def update(self, profile: ImsCallProfile, media_profile: ImsStreamMediaProfile): - if self._listener: - self._listener.onCallSessionUpdated(self, profile) - - def sendRttMessage(self, message: str): - if self._listener: - self._listener.onCallSessionRttMessageReceived(self, message) - - def sendRttModifyRequest(self, profile: ImsCallProfile): - if self._listener: - self._listener.onCallSessionRttModifyRequestReceived(self, profile) - - def sendRttModifyResponse(self, response: int): - if self._listener: - self._listener.onCallSessionRttModifyResponseReceived(self, response) - - def close(self): - self._state = ImsCallSession.STATE_TERMINATED - self._listener = None - - def toString(self) -> str: - return f"ImsCallSession(callId={self._call_id}, state={self._state})" - - -# ═══════════════════════════════════════════════════════════════ -# ImsCall -# ═══════════════════════════════════════════════════════════════ - -class ImsCall(Stub): - """ImsCall — IMS 呼叫""" - - class Listener(Stub): - """ImsCall.Listener — IMS 呼叫监听器""" - - def onCallProgressing(self, call: ImsCall, session: ImsCallSession, state: Any): - pass - - def onCallStarted(self, call: ImsCall, session: ImsCallSession): - pass - - def onCallUpdated(self, call: ImsCall, session: ImsCallSession): - pass - - def onCallUpdateFailed(self, call: ImsCall, session: ImsCallSession, reason: ImsReasonInfo): - pass - - def onCallHeld(self, call: ImsCall, session: ImsCallSession): - pass - - def onCallHoldFailed(self, call: ImsCall, session: ImsCallSession, reason: ImsReasonInfo): - pass - - def onCallResumed(self, call: ImsCall, session: ImsCallSession): - pass - - def onCallResumeFailed(self, call: ImsCall, session: ImsCallSession, reason: ImsReasonInfo): - pass - - def onCallMerged(self, call: ImsCall, session: ImsCallSession, merge_session: ImsCallSession): - pass - - def onCallMergeFailed(self, call: ImsCall, session: ImsCallSession, reason: ImsReasonInfo): - pass - - def onCallBusy(self, call: ImsCall, session: ImsCallSession): - pass - - def onCallDisconnected(self, call: ImsCall, session: ImsCallSession, reason: ImsReasonInfo): - pass - - def onCallSuppServiceReceived(self, call: ImsCall, session: ImsCallSession, - service: Any): - pass - - def onCallHandover(self, call: ImsCall, session: ImsCallSession, src_tech: int, - target_tech: int, reason: ImsReasonInfo): - pass - - def onCallHandoverFailed(self, call: ImsCall, session: ImsCallSession, - src_tech: int, target_tech: int, - reason: ImsReasonInfo): - pass - - def onCallRttMessageReceived(self, call: ImsCall, message: str): - pass - - def onCallRttModifyRequestReceived(self, call: ImsCall, profile: ImsCallProfile): - pass - - def onCallRttModifyResponseReceived(self, call: ImsCall, status: int): - pass - - def onCallConferenceStateUpdated(self, call: ImsCall, state: Any): - pass - - def onCallSessionEvent(self, call: ImsCall, session: ImsCallSession, event: Any): - pass - - def __init__(self, context: Any = None, profile: ImsCallProfile = None): - super().__init__() - self._context = context - self._profile = profile or ImsCallProfile() - self._session: Optional[ImsCallSession] = None - self._listener: Optional[ImsCall.Listener] = None - self._is_multiparty: bool = False - self._state: int = ImsCallSession.STATE_IDLE - - def setListener(self, listener: ImsCall.Listener): - self._listener = listener - - def getListener(self) -> Optional[ImsCall.Listener]: - return self._listener - - def getSession(self) -> Optional[ImsCallSession]: - return self._session - - def getCallProfile(self) -> ImsCallProfile: - return self._profile - - def getLocalCallProfile(self) -> ImsCallProfile: - return self._profile - - def getRemoteCallProfile(self) -> ImsCallProfile: - return self._profile - - def isMultiparty(self) -> bool: - return self._is_multiparty - - def getState(self) -> int: - return self._state - - def close(self): - if self._session: - self._session.close() - self._session = None - self._listener = None - - def toString(self) -> str: - return f"ImsCall(state={self._state}, multi={self._is_multiparty})" - - -# ═══════════════════════════════════════════════════════════════ -# ImsConfig -# ═══════════════════════════════════════════════════════════════ - -class ImsConfig(Stub): - """ImsConfig — IMS 配置""" - - CONFIG_START = 0 - CONFIG_END = 1 - - # VoLTE 配置 - VO_CONFIG_START = 100 - VO_CONFIG_END = 200 - - # 功能开关 - FEATURE_TYPE_VOLTE = 0 - FEATURE_TYPE_VIDEO = 1 - FEATURE_TYPE_UT = 2 - FEATURE_TYPE_UT_OVER_VOLTE = 3 - FEATURE_TYPE_UT_OVER_WIFI = 4 - FEATURE_TYPE_UNKNOWN = -1 - - # 操作结果 - OPERATION_SUCCESS = 0 - OPERATION_FAILED = 1 - OPERATION_NOT_SUPPORTED = 2 - OPERATION_TIMEOUT = 3 - OPERATION_INVALID_PARAM = 4 - - # VoLTE 设置 - VO_SETTING_VOLTE_ENABLED = 101 - VO_SETTING_VIDEO_ENABLED = 102 - VO_SETTING_WFC_ENABLED = 103 - VO_SETTING_WFC_MODE = 104 - VO_SETTING_LBO_PCSCF_ADDRESS = 105 - VO_SETTING_DOMAIN_NAME = 106 - VO_SETTING_SIP_INVITE_TIMEOUT = 107 - VO_SETTING_RTT_ENABLED = 108 - VO_SETTING_AMR_WB_ENABLED = 109 - VO_SETTING_EVS_ENABLED = 110 - VO_SETTING_AMR_NB_ENABLED = 111 - VO_SETTING_GSM_WB_ENABLED = 112 - VO_SETTING_GSM_NB_ENABLED = 113 - VO_SETTING_PCSCF_DISCOVERY_ENABLED = 114 - VO_SETTING_PCSCF_ADDRESS = 115 - VO_SETTING_SIP_SERVER_ADDRESS = 116 - VO_SETTING_PUBLIC_USER_IDENTITY = 117 - VO_SETTING_PRIVATE_USER_IDENTITY = 118 - VO_SETTING_HOME_NETWORK_DOMAIN = 119 - VO_SETTING_AM_CODEC = 120 - VO_SETTING_VIDEO_CODEC = 121 - VO_SETTING_VT_ENABLED = 122 - VO_SETTING_LTE_TO_WIFI_HANDOVER_ENABLED = 123 - VO_SETTING_WIFI_TO_LTE_HANDOVER_ENABLED = 124 - VO_SETTING_LTE_TO_NR_HANDOVER_ENABLED = 125 - VO_SETTING_NR_TO_LTE_HANDOVER_ENABLED = 126 - - class ConfigCallback(Stub): - """ImsConfig.ConfigCallback — 配置更改回调""" - - def onConfigChanged(self, item: int, value: str): - pass - - def onGetFeatureValueResponse(self, feature: int, network: int, value: int, status: int): - pass - - def onSetFeatureValueResponse(self, feature: int, network: int, value: int, status: int): - pass - - def onGetConfigResponse(self, item: int, value: str, status: int): - pass - - def onSetConfigResponse(self, item: int, status: int): - pass - - def __init__(self): - super().__init__() - self._config: Dict[int, str] = {} - self._feature_values: Dict[Tuple[int, int], int] = {} - self._callback: Optional[ImsConfig.ConfigCallback] = None - - def setConfig(self, item: int, value: str) -> int: - self._config[item] = value - return ImsConfig.OPERATION_SUCCESS - - def getConfig(self, item: int) -> str: - return self._config.get(item, "") - - def getFeatureValue(self, feature: int, network: int) -> int: - return self._feature_values.get((feature, network), 0) - - def setFeatureValue(self, feature: int, network: int, value: int) -> int: - self._feature_values[(feature, network)] = value - return ImsConfig.OPERATION_SUCCESS - - def setConfigCallback(self, callback: ConfigCallback): - self._callback = callback - - def getConfigCallback(self) -> Optional[ConfigCallback]: - return self._callback - - def toString(self) -> str: - return f"ImsConfig(settings={len(self._config)})" - - -# ═══════════════════════════════════════════════════════════════ -# ImsRegistration -# ═══════════════════════════════════════════════════════════════ - -class ImsRegistration(Stub): - """ImsRegistration — IMS 注册信息""" - - def __init__(self): - super().__init__() - self._registrant_id: int = 0 - self._registration_state: int = IMS_REGISTRATION_STATE_NOT_REGISTERED - self._registration_technology: int = IMS_REGISTRATION_TECH_NONE - self._sip_uri: str = "" - self._feature_tags: List[str] = [] - self._supported_media_types: List[int] = [] - - def getRegistrantId(self) -> int: - return self._registrant_id - - def setRegistrantId(self, rid: int): - self._registrant_id = rid - - def getRegistrationState(self) -> int: - return self._registration_state - - def setRegistrationState(self, state: int): - self._registration_state = state - - def getRegistrationTechnology(self) -> int: - return self._registration_technology - - def setRegistrationTechnology(self, tech: int): - self._registration_technology = tech - - def getSipUri(self) -> str: - return self._sip_uri - - def setSipUri(self, uri: str): - self._sip_uri = uri - - def getFeatureTags(self) -> List[str]: - return list(self._feature_tags) - - def setFeatureTags(self, tags: List[str]): - self._feature_tags = list(tags) - - def getSupportedMediaTypes(self) -> List[int]: - return list(self._supported_media_types) - - def setSupportedMediaTypes(self, types: List[int]): - self._supported_media_types = list(types) - - def isRegistered(self) -> bool: - return self._registration_state == IMS_REGISTRATION_STATE_REGISTERED - - def toString(self) -> str: - return f"ImsRegistration(state={self._registration_state}, tech={self._registration_technology})" - - -# ═══════════════════════════════════════════════════════════════ -# ImsManager -# ═══════════════════════════════════════════════════════════════ - -class ImsManager(Stub): - """ImsManager — IMS 管理器""" - - ACTION_IMS_SERVICE_UP = "android.telephony.ims.action.IMS_SERVICE_UP" - ACTION_IMS_SERVICE_DOWN = "android.telephony.ims.action.IMS_SERVICE_DOWN" - ACTION_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.IMS_REGISTRATION_ERROR" - - EXTRA_PHONE_ID = "android.telephony.ims.extra.PHONE_ID" - EXTRA_SUBSCRIPTION_ID = "android.telephony.ims.extra.SUBSCRIPTION_ID" - - def __init__(self, context: Any = None, phone_id: int = 0): - super().__init__() - self._context = context - self._phone_id = phone_id - self._registration: Optional[ImsRegistration] = None - self._config: Optional[ImsConfig] = None - self._mmtel_features: Dict[int, MmTelFeature] = {} - self._registration_callbacks: List[ImsRegistrationCallback] = [] - - @staticmethod - def getInstance(context: Any, phone_id: int) -> ImsManager: - return ImsManager(context, phone_id) - - def getImsService(self) -> Optional[ImsService]: - return ImsService(self._context) - - def getConfig(self) -> Optional[ImsConfig]: - if self._config is None: - self._config = ImsConfig() - return self._config - - def getRegistration(self) -> Optional[ImsRegistration]: - return self._registration - - def setRegistration(self, registration: ImsRegistration): - self._registration = registration - - def getPhoneId(self) -> int: - return self._phone_id - - def getContext(self) -> Any: - return self._context - - def isServiceSupported(self) -> bool: - return True - - def isServiceReady(self) -> bool: - return self._registration is not None and self._registration.isRegistered() - - def getMmTelFeature(self, sub_id: int) -> Optional[MmTelFeature]: - return self._mmtel_features.get(sub_id) - - def createMmTelFeature(self, sub_id: int) -> MmTelFeature: - feature = MmTelFeature(self._context, sub_id) - self._mmtel_features[sub_id] = feature - return feature - - def addRegistrationCallback(self, callback: ImsRegistrationCallback): - if callback not in self._registration_callbacks: - self._registration_callbacks.append(callback) - - def removeRegistrationCallback(self, callback: ImsRegistrationCallback): - self._registration_callbacks = [c for c in self._registration_callbacks if c is not callback] - - def getRegistrationCallbacks(self) -> List[ImsRegistrationCallback]: - return list(self._registration_callbacks) - - def notifyRegistrationChange(self): - for cb in self._registration_callbacks: - cb.onRegistered(self._registration) - - def close(self): - self._config = None - self._registration = None - self._registration_callbacks.clear() - self._mmtel_features.clear() - - def toString(self) -> str: - return f"ImsManager(phoneId={self._phone_id})" - - -# ═══════════════════════════════════════════════════════════════ -# ImsRegistrationCallback -# ═══════════════════════════════════════════════════════════════ - -class ImsRegistrationCallback(Stub): - """ImsRegistrationCallback — IMS 注册回调""" - - def onRegistered(self, registration: ImsRegistration): - pass - - def onRegistering(self, registration: ImsRegistration): - pass - - def onDeregistered(self, registration: ImsRegistration, reason: ImsReasonInfo): - pass - - def onTechnologyChanged(self, registration: ImsRegistration): - pass - - def onSubscriberAssociatedUriChanged(self, uris: List[str]): - pass - - -# ═══════════════════════════════════════════════════════════════ -# ImsService -# ═══════════════════════════════════════════════════════════════ - -class ImsService(Stub): - """ImsService — IMS 服务""" - - CAPABILITY_TYPE_VOLTE = 0 - CAPABILITY_TYPE_VIDEO = 1 - CAPABILITY_TYPE_UT = 2 - CAPABILITY_TYPE_SMS = 3 - CAPABILITY_TYPE_MMTEL = 4 - CAPABILITY_TYPE_RCS = 5 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._features: Dict[int, Any] = {} - self._is_ready: bool = False - self._capabilities: Set[int] = set() - - def getContext(self) -> Any: - return self._context - - def isReady(self) -> bool: - return self._is_ready - - def setReady(self, ready: bool): - self._is_ready = ready - - def getCapabilities(self) -> Set[int]: - return set(self._capabilities) - - def setCapabilities(self, caps: Set[int]): - self._capabilities = set(caps) - - def hasCapability(self, capability: int) -> bool: - return capability in self._capabilities - - def addCapability(self, capability: int): - self._capabilities.add(capability) - - def removeCapability(self, capability: int): - self._capabilities.discard(capability) - - def getFeature(self, feature_type: int) -> Optional[Any]: - return self._features.get(feature_type) - - def addFeature(self, feature_type: int, feature: Any): - self._features[feature_type] = feature - - def createCallSession(self, profile: ImsCallProfile) -> Optional[ImsCallSession]: - return ImsCallSession(self._context, profile) - - def close(self): - self._features.clear() - self._capabilities.clear() - self._is_ready = False - - def toString(self) -> str: - return f"ImsService(ready={self._is_ready}, caps={len(self._capabilities)})" - - -# ═══════════════════════════════════════════════════════════════ -# MmTelFeature -# ═══════════════════════════════════════════════════════════════ - -class MmTelFeature(Stub): - """MmTelFeature — MMTel 功能""" - - # MMTel 功能类型 - MMTEL_CAPABILITY_TYPE_VOICE = 0 - MMTEL_CAPABILITY_TYPE_VIDEO = 1 - MMTEL_CAPABILITY_TYPE_UT = 2 - MMTEL_CAPABILITY_TYPE_SMS = 3 - MMTEL_CAPABILITY_TYPE_NONE = 4 - - # 网络类型 - NETWORK_TYPE_LTE = 0 - NETWORK_TYPE_IWLAN = 1 - NETWORK_TYPE_NR = 2 - - # 功能可用性 - CAPABILITY_TYPE_UNKNOWN = -1 - CAPABILITY_TYPE_NONE = 0 - CAPABILITY_TYPE_VOICE = 1 - CAPABILITY_TYPE_VIDEO = 2 - CAPABILITY_TYPE_UT = 4 - CAPABILITY_TYPE_SMS = 8 - CAPABILITY_TYPE_ALL = 15 - - class Listener(Stub): - """MmTelFeature.Listener — MMTel 功能监听器""" - - def onIncomingCall(self, call_session: ImsCallSession, call_id: str, extras: Any): - pass - - def onIncomingCallAnswered(self, call_session: ImsCallSession, call_id: str, extras: Any): - pass - - def onRejectedCall(self, call_id: str, reason: ImsReasonInfo): - pass - - def onVoiceMessageCountChanged(self, count: int): - pass - - def __init__(self, context: Any = None, sub_id: int = 0): - super().__init__() - self._context = context - self._sub_id = sub_id - self._listener: Optional[MmTelFeature.Listener] = None - self._mmtel_capabilities: Set[int] = set() - self._is_available: bool = False - self._network_type: int = MmTelFeature.NETWORK_TYPE_LTE - self._call_sessions: Dict[str, ImsCallSession] = {} - - def setListener(self, listener: MmTelFeature.Listener): - self._listener = listener - - def getListener(self) -> Optional[MmTelFeature.Listener]: - return self._listener - - def getSubscriptionId(self) -> int: - return self._sub_id - - def isAvailable(self) -> bool: - return self._is_available - - def setAvailable(self, available: bool): - self._is_available = available - - def getNetworkType(self) -> int: - return self._network_type - - def setNetworkType(self, network_type: int): - self._network_type = network_type - - def getMmTelCapabilities(self) -> Set[int]: - return set(self._mmtel_capabilities) - - def setMmTelCapabilities(self, caps: Set[int]): - self._mmtel_capabilities = set(caps) - - def hasCapability(self, capability: int) -> bool: - return capability in self._mmtel_capabilities - - def createCallSession(self, profile: ImsCallProfile) -> ImsCallSession: - session = ImsCallSession(self._context, profile) - session.setCallId(f"mmtel_call_{len(self._call_sessions)}") - self._call_sessions[session.getCallId()] = session - return session - - def endCallSession(self, call_id: str): - self._call_sessions.pop(call_id, None) - - def getCallSessions(self) -> List[ImsCallSession]: - return list(self._call_sessions.values()) - - def toString(self) -> str: - return f"MmTelFeature(subId={self._sub_id}, available={self._is_available})" - - -# ═══════════════════════════════════════════════════════════════ -# MmTelManager -# ═══════════════════════════════════════════════════════════════ - -class MmTelManager(Stub): - """MmTelManager — MMTel 管理器""" - - CAPABILITY_TYPE_VOICE = 0 - CAPABILITY_TYPE_VIDEO = 1 - CAPABILITY_TYPE_UT = 2 - CAPABILITY_TYPE_SMS = 3 - CAPABILITY_TYPE_NONE = 4 - - WIFI_CALLING_SETTING_ENABLED = 0 - WIFI_CALLING_SETTING_DISABLED = 1 - WIFI_CALLING_SETTING_UNKNOWN = 2 - - class CapabilityCallback(Stub): - """MmTelManager.CapabilityCallback — 能力回调""" - - def onCapabilitiesChanged(self, capabilities: Set[int]): - pass - - class RegistrationCallback(Stub): - """MmTelManager.RegistrationCallback — 注册回调""" - - def onRegistered(self, registration: ImsRegistration): - pass - - def onRegistering(self, registration: ImsRegistration): - pass - - def onUnregistered(self, reason: ImsReasonInfo): - pass - - def onTechnologyChanged(self, registration: ImsRegistration): - pass - - def onSubscriberAssociatedUriChanged(self, uris: List[str]): - pass - - def __init__(self, context: Any = None, sub_id: int = 0): - super().__init__() - self._context = context - self._sub_id = sub_id - self._mmtel_feature: Optional[MmTelFeature] = None - self._capability_callbacks: List[MmTelManager.CapabilityCallback] = [] - self._registration_callbacks: List[MmTelManager.RegistrationCallback] = [] - - @staticmethod - def getInstance(context: Any, sub_id: int) -> Optional[MmTelManager]: - return MmTelManager(context, sub_id) - - def getSubscriptionId(self) -> int: - return self._sub_id - - def getMmTelFeature(self) -> Optional[MmTelFeature]: - return self._mmtel_feature - - def setMmTelFeature(self, feature: MmTelFeature): - self._mmtel_feature = feature - - def addCapabilityCallback(self, callback: CapabilityCallback): - if callback not in self._capability_callbacks: - self._capability_callbacks.append(callback) - - def removeCapabilityCallback(self, callback: CapabilityCallback): - self._capability_callbacks = [c for c in self._capability_callbacks if c is not callback] - - def addRegistrationCallback(self, callback: RegistrationCallback): - if callback not in self._registration_callbacks: - self._registration_callbacks.append(callback) - - def removeRegistrationCallback(self, callback: RegistrationCallback): - self._registration_callbacks = [c for c in self._registration_callbacks if c is not callback] - - def isMmTelCapabilitySupported(self, capability: int, transport_type: int) -> bool: - return True - - def isMmTelCapabilityEnabled(self, capability: int, transport_type: int) -> bool: - return False - - def setMmTelCapabilityEnabled(self, capability: int, transport_type: int, enabled: bool): - pass - - def isAdvancedCallingSettingEnabled(self) -> bool: - return False - - def isVoLteSettingEnabled(self) -> bool: - return False - - def isVtSettingEnabled(self) -> bool: - return False - - def isWiFiCallingSettingEnabled(self) -> bool: - return False - - def setVoLteSettingEnabled(self, enabled: bool): - pass - - def setVtSettingEnabled(self, enabled: bool): - pass - - def setWiFiCallingSettingEnabled(self, enabled: bool): - pass - - def sendRttMessage(self, message: str): - pass - - def sendRttModifyRequest(self, profile: ImsCallProfile): - pass - - def sendRttModifyResponse(self, response: int): - pass - - def toString(self) -> str: - return f"MmTelManager(subId={self._sub_id})" - - -# ═══════════════════════════════════════════════════════════════ -# ImsSsInfo -# ═══════════════════════════════════════════════════════════════ - -class ImsSsInfo(Stub): - """ImsSsInfo — IMS 补充业务信息""" - - NOT_REGISTERED = 0 - DISABLED = 0 - ENABLED = 1 - REGISTERED = 1 - - SERVICE_TYPE_CFU = 0 - SERVICE_TYPE_CFB = 1 - SERVICE_TYPE_CFNRY = 2 - SERVICE_TYPE_CFNRC = 3 - SERVICE_TYPE_CF_ALL = 4 - SERVICE_TYPE_CF_ALL_CONDITIONAL = 5 - SERVICE_TYPE_CLIP = 6 - SERVICE_TYPE_CLIR = 7 - SERVICE_TYPE_COLP = 8 - SERVICE_TYPE_COLR = 9 - SERVICE_TYPE_CW = 10 - SERVICE_TYPE_HOLD = 11 - SERVICE_TYPE_ECT = 12 - SERVICE_TYPE_BAOC = 13 - SERVICE_TYPE_BAOIC = 14 - SERVICE_TYPE_BAOIC_EXC_HOME = 15 - SERVICE_TYPE_BIC_ROAM = 16 - SERVICE_TYPE_CB_ALL = 17 - SERVICE_TYPE_CB_ALL_OUTGOING = 18 - SERVICE_TYPE_CB_ALL_INCOMING = 19 - - def __init__(self, status: int = NOT_REGISTERED, provision_status: int = 0): - super().__init__() - self._status = status - self._provision_status = provision_status - self._call_forwarding_info: Optional[ImsCallForwardInfo] = None - - def getStatus(self) -> int: - return self._status - - def setStatus(self, status: int): - self._status = status - - def getProvisionStatus(self) -> int: - return self._provision_status - - def setProvisionStatus(self, status: int): - self._provision_status = status - - def isProvisioned(self) -> bool: - return self._provision_status == 1 - - def isRegistered(self) -> bool: - return self._status == ImsSsInfo.REGISTERED - - def getCallForwardInfo(self) -> Optional[ImsCallForwardInfo]: - return self._call_forwarding_info - - def setCallForwardInfo(self, info: ImsCallForwardInfo): - self._call_forwarding_info = info - - def toString(self) -> str: - return f"ImsSsInfo(status={self._status})" - - -# ═══════════════════════════════════════════════════════════════ -# ImsCallForwardInfo -# ═══════════════════════════════════════════════════════════════ - -class ImsCallForwardInfo(Stub): - """ImsCallForwardInfo — IMS 呼叫前转信息""" - - CDIV_CF_UNCONDITIONAL = 0 - CDIV_CF_BUSY = 1 - CDIV_CF_NO_REPLY = 2 - CDIV_CF_NOT_REACHABLE = 3 - CDIV_CF_ALL = 4 - CDIV_CF_ALL_CONDITIONAL = 5 - - def __init__(self, condition: int = CDIV_CF_UNCONDITIONAL, - status: int = 0, number: str = "", - time_seconds: int = 20): - super().__init__() - self._condition = condition - self._status = status - self._number = number - self._time_seconds = time_seconds - - def getCondition(self) -> int: - return self._condition - - def setCondition(self, condition: int): - self._condition = condition - - def getStatus(self) -> int: - return self._status - - def setStatus(self, status: int): - self._status = status - - def getNumber(self) -> str: - return self._number - - def setNumber(self, number: str): - self._number = number - - def getTimeSeconds(self) -> int: - return self._time_seconds - - def setTimeSeconds(self, seconds: int): - self._time_seconds = seconds - - def toString(self) -> str: - return f"ImsCallForwardInfo(condition={self._condition}, number={self._number})" - - -# ═══════════════════════════════════════════════════════════════ -# ImsUt -# ═══════════════════════════════════════════════════════════════ - -class ImsUt(Stub): - """ImsUt — IMS 补充服务""" - - OPERATION_SUCCESS = 0 - OPERATION_FAILED = 1 - OPERATION_NOT_SUPPORTED = 2 - OPERATION_TIMEOUT = 3 - OPERATION_NETWORK_ERROR = 4 - OPERATION_INVALID_PARAM = 5 - - class Listener(Stub): - """ImsUt.Listener — 补充服务监听器""" - - def onUtConfigurationUpdated(self, result: int): - pass - - def onUtConfigurationUpdateFailed(self, result: int): - pass - - def onUtConfigurationQueried(self, result: int, ss_info: List[ImsSsInfo]): - pass - - def onUtConfigurationQueryFailed(self, result: int): - pass - - def onUtConfigurationCallBarringQueried(self, result: int, ss_info: List[ImsSsInfo]): - pass - - def onUtConfigurationCallForwardQueried(self, result: int, ss_info: List[ImsSsInfo]): - pass - - def onUtConfigurationCallWaitingQueried(self, result: int, ss_info: List[ImsSsInfo]): - pass - - def __init__(self): - super().__init__() - self._listener: Optional[ImsUt.Listener] = None - - def setListener(self, listener: ImsUt.Listener): - self._listener = listener - - def getListener(self) -> Optional[ImsUt.Listener]: - return self._listener - - def queryCallBarring(self, cb_type: int): - pass - - def queryCallForward(self, condition: int): - pass - - def queryCallWaiting(self): - pass - - def updateCallBarring(self, cb_type: int, enabled: bool, - password: str = "", service_class: int = 0): - pass - - def updateCallForward(self, condition: int, enabled: bool, - number: str = "", time_seconds: int = 20): - pass - - def updateCallWaiting(self, enabled: bool, service_class: int = 0): - pass - - def close(self): - self._listener = None - - -# ═══════════════════════════════════════════════════════════════ -# ImsCallSessionCallback -# ═══════════════════════════════════════════════════════════════ - -class ImsCallSessionCallback(Stub): - """ImsCallSessionCallback — IMS 呼叫会话回调""" - - def onCallStateChanged(self, state: int): - pass - - def onCallProfileChanged(self, profile: ImsCallProfile): - pass - - def onCallQualityChanged(self, quality: int, video_quality: int): - pass - - def onCallSessionHeld(self, held: bool): - pass - - def onCallSessionHoldFailed(self, reason: ImsReasonInfo): - pass - - def onCallSessionResumed(self, resume: bool): - pass - - def onCallSessionResumeFailed(self, reason: ImsReasonInfo): - pass - - def onCallSessionMergeStarted(self, session: ImsCallSession, profile: ImsCallProfile): - pass - - def onCallSessionMergeComplete(self, session: ImsCallSession): - pass - - def onCallSessionMergeFailed(self, reason: ImsReasonInfo): - pass - - def onCallSessionUpdated(self, profile: ImsCallProfile): - pass - - def onCallSessionUpdateFailed(self, reason: ImsReasonInfo): - pass - - def onCallSessionTerminated(self, reason: ImsReasonInfo): - pass - - def onCallSessionInitiated(self, profile: ImsCallProfile): - pass - - def onCallSessionHandover(self, src_tech: int, target_tech: int, reason: ImsReasonInfo): - pass - - def onCallSessionHandoverFailed(self, src_tech: int, target_tech: int, reason: ImsReasonInfo): - pass - - def onCallSessionMayHandover(self, src_tech: int, target_tech: int): - pass - - def onCallSessionRttMessageReceived(self, message: str): - pass - - def onCallSessionRttModifyRequestReceived(self, profile: ImsCallProfile): - pass - - def onCallSessionRttModifyResponseReceived(self, status: int): - pass - - -# ═══════════════════════════════════════════════════════════════ -# ImsStateCallback -# ═══════════════════════════════════════════════════════════════ - -class ImsStateCallback(Stub): - """ImsStateCallback — IMS 状态回调""" - - def onServiceAvailable(self): - pass - - def onServiceUnavailable(self): - pass - - def onUnavailable(self, reason: int): - pass - - def onConnected(self): - pass - - def onDisconnected(self, reason: int): - pass - - def onResume(self): - pass - - def onSuspend(self): - pass - - def onFeatureStateChanged(self, feature_state: int, network: int): - pass - - def onRegistrationStateChanged(self, state: int): - pass \ No newline at end of file diff --git a/android_pyapi/android/telephony_mbms.py b/android_pyapi/android/telephony_mbms.py deleted file mode 100644 index e73db86..0000000 --- a/android_pyapi/android/telephony_mbms.py +++ /dev/null @@ -1,1180 +0,0 @@ -""" -Android Telephony MBMS 组件 — MbmsDownloadManager, MbmsStreamingManager, DownloadRequest, StreamingService 等 -方法签名完全对齐 Android SDK (android.telephony.mbms 包) -""" - -from __future__ import annotations - -import os -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# MbmsErrors -# ═══════════════════════════════════════════════════════════════ - -class MbmsErrors(Stub): - """MbmsErrors — MBMS 错误代码基类""" - - SUCCESS = 0 - ERROR_NO_UNIQUE_MIDDLEWARE = 1 - ERROR_MIDDLEWARE_NOT_BOUND = 2 - ERROR_MIDDLEWARE_LOST = 3 - ERROR_MIDDLEWARE_NOT_YET_READY = 4 - ERROR_OUT_OF_MEMORY = 5 - ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE = 6 - ERROR_MIDDLEWARE_PERMANENTLY_UNAVAILABLE = 7 - ERROR_MIDDLEWARE_UNABLE_TO_CREATE_SESSION = 8 - ERROR_MIDDLEWARE_UNABLE_TO_CREATE_DATA_SESSION = 9 - ERROR_MIDDLEWARE_UNABLE_TO_CREATE_DOWNLOAD = 10 - ERROR_MIDDLEWARE_UNABLE_TO_CREATE_STREAMING = 11 - ERROR_MIDDLEWARE_INTERNAL_ERROR = 12 - ERROR_MIDDLEWARE_NOT_SUPPORTED = 13 - ERROR_MIDDLEWARE_INVALID_INPUT = 14 - ERROR_MIDDLEWARE_INVALID_SERVICE_ID = 15 - ERROR_MIDDLEWARE_INVALID_FILE_PATH = 16 - ERROR_MIDDLEWARE_DUPLICATE_SERVICE = 17 - ERROR_MIDDLEWARE_SERVICE_NOT_FOUND = 18 - ERROR_MIDDLEWARE_SERVICE_ALREADY_RUNNING = 19 - ERROR_MIDDLEWARE_SERVICE_NOT_RUNNING = 20 - ERROR_MIDDLEWARE_INVALID_SAI = 21 - ERROR_MIDDLEWARE_INVALID_CELL_ID = 22 - ERROR_MIDDLEWARE_UNABLE_TO_FETCH_PREFIX = 23 - ERROR_MIDDLEWARE_TEMP_FILE_EXPIRED = 24 - ERROR_MIDDLEWARE_FILE_ALREADY_COMPLETE = 25 - ERROR_MIDDLEWARE_FILE_ALREADY_IN_PROGRESS = 26 - ERROR_MIDDLEWARE_STALE_SERVICE_ANNOUNCEMENT = 27 - ERROR_MIDDLEWARE_MISSING_FILE_INFO = 28 - ERROR_MIDDLEWARE_NO_EXPIRATION = 29 - ERROR_MIDDLEWARE_UNKNOWN_FILE_INFO = 30 - ERROR_MIDDLEWARE_UNKNOWN_SIGNAL = 31 - ERROR_MIDDLEWARE_INSUFFICIENT_STORAGE = 32 - ERROR_MIDDLEWARE_INSUFFICIENT_MEMORY = 33 - ERROR_MIDDLEWARE_UNABLE_TO_CLOSE_FILE = 34 - ERROR_MIDDLEWARE_UNABLE_TO_OPEN_FILE = 35 - ERROR_MIDDLEWARE_IO_ERROR = 36 - ERROR_MIDDLEWARE_CONCURRENT_SERVICE_LIMIT = 37 - ERROR_MIDDLEWARE_INVALID_DOWNLOAD_ID = 38 - - class InitializationErrors(Stub): - """MbmsErrors.InitializationErrors — 初始化错误""" - ERROR_DUPLICATE_INITIALIZE = 101 - ERROR_APP_PERMISSIONS_NOT_GRANTED = 102 - ERROR_UNABLE_TO_INITIALIZE = 103 - ERROR_INTERNAL = 104 - ERROR_MIDDLEWARE_NOT_BOUND = 105 - - class DownloadErrors(Stub): - """MbmsErrors.DownloadErrors — 下载错误""" - ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 201 - ERROR_UNKNOWN_DOWNLOAD_REQUEST = 202 - ERROR_DOWNLOAD_NOT_FOUND = 203 - ERROR_DOWNLOAD_ALREADY_EXISTS = 204 - ERROR_DOWNLOAD_INACTIVE = 205 - ERROR_FILE_UNAVAILABLE = 206 - ERROR_SERVICE_ID_NOT_DOWNLOAD = 207 - ERROR_MALFORMED_FILE_URI = 208 - ERROR_STALE_FILE_INFO = 209 - ERROR_TEMP_FILE_ROOT_NOT_SET = 210 - ERROR_UNKNOWN_FILE_INFO = 211 - ERROR_NO_DOWNLOAD_FOR_FILE = 212 - - class StreamingErrors(Stub): - """MbmsErrors.StreamingErrors — 流错误""" - ERROR_STREAMING_NOT_ALLOWED = 301 - ERROR_STREAMING_SERVICE_NOT_STARTED = 302 - ERROR_STREAMING_SERVICE_ALREADY_STARTED = 303 - ERROR_STREAMING_SERVICE_NOT_VISIBLE = 304 - ERROR_STREAMING_MALFORMED_SERVICE_ANNOUNCEMENT = 305 - ERROR_STREAMING_DUPLICATE_SERVICE_ANNOUNCEMENT = 306 - ERROR_STREAMING_SERVICE_ANNOUNCEMENT_EXPIRED = 307 - ERROR_STREAMING_RADIO_RESOURCE_REJECTED = 308 - ERROR_STREAMING_RADIO_RESOURCE_NOT_AVAILABLE = 309 - ERROR_STREAMING_INVALID_SAI_LIST = 310 - ERROR_STREAMING_UNABLE_TO_UPDATE_SUBFLOW = 311 - - class NameTypeErrors(Stub): - """MbmsErrors.NameTypeErrors — 名称类型错误""" - ERROR_NAME_SYSTEM_ERROR = 401 - ERROR_NAME_INVALID_FORMAT = 402 - ERROR_NAME_UNSUPPORTED_CHARACTER_ENCODING = 403 - ERROR_NAME_CONTAINS_INVALID_CHARACTERS = 404 - - class GeneralErrors(Stub): - """MbmsErrors.GeneralErrors — 通用错误""" - ERROR_MIDDLEWARE_NOT_BOUND = 501 - ERROR_OUT_OF_MEMORY = 502 - ERROR_MIDDLEWARE_LOST = 503 - ERROR_MIDDLEWARE_NOT_YET_READY = 504 - ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE = 505 - ERROR_MIDDLEWARE_UNABLE_TO_CREATE_SESSION = 506 - - -# ═══════════════════════════════════════════════════════════════ -# DownloadRequest -# ═══════════════════════════════════════════════════════════════ - -class DownloadRequest(Stub): - """DownloadRequest — MBMS 下载请求""" - - # 下载请求常量 - DOWNLOAD_RESULT_SUCCESS = 0 - DOWNLOAD_RESULT_ERROR = 1 - DOWNLOAD_RESULT_ERROR_MEDIA_NOT_FOUND = 2 - DOWNLOAD_RESULT_ERROR_MEDIA_UNAVAILABLE = 3 - DOWNLOAD_RESULT_ERROR_MEDIA_FAILED = 4 - DOWNLOAD_RESULT_ERROR_SERVICE_UNAVAILABLE = 5 - DOWNLOAD_RESULT_ERROR_INSUFFICIENT_STORAGE = 6 - DOWNLOAD_RESULT_ERROR_IO_ERROR = 7 - DOWNLOAD_RESULT_ERROR_FILE_NOT_FOUND = 8 - DOWNLOAD_RESULT_ERROR_OUT_OF_MEMORY = 9 - DOWNLOAD_RESULT_ERROR_RADIO_OFF = 10 - DOWNLOAD_RESULT_ERROR_INTERRUPTED = 11 - DOWNLOAD_RESULT_ERROR_TIMEOUT = 12 - DOWNLOAD_RESULT_ERROR_CANCELLED = 13 - DOWNLOAD_RESULT_ERROR_DOWNLOAD_NOT_STARTED = 14 - - # 下载状态 - DOWNLOAD_STATUS_INACTIVE = 0 - DOWNLOAD_STATUS_ACTIVE = 1 - DOWNLOAD_STATUS_PENDING = 2 - DOWNLOAD_STATUS_PAUSED = 3 - DOWNLOAD_STATUS_COMPLETED = 4 - DOWNLOAD_STATUS_CANCELED = 5 - DOWNLOAD_STATUS_FAILED = 6 - - def __init__(self, service_id: str = "", file_uri: str = "", - source: Any = None, destination: Any = None, - app_intent: Any = None): - super().__init__() - self._service_id = service_id - self._file_uri = file_uri - self._source = source - self._destination = destination - self._app_intent = app_intent - self._subscription_id: int = 0 - self._sai: List[int] = [] - self._app_name: str = "" - self._app_version: str = "" - self._storage_type: int = 0 - self._hash: str = "" - self._request_id: int = 0 - - def getServiceId(self) -> str: - return self._service_id - - def getFileUri(self) -> str: - return self._file_uri - - def getSource(self) -> Any: - return self._source - - def getDestination(self) -> Any: - return self._destination - - def getIntent(self) -> Any: - return self._app_intent - - def getSubscriptionId(self) -> int: - return self._subscription_id - - def setSubscriptionId(self, sub_id: int): - self._subscription_id = sub_id - - def getSai(self) -> List[int]: - return list(self._sai) - - def setSai(self, sai: List[int]): - self._sai = list(sai) - - def getAppName(self) -> str: - return self._app_name - - def setAppName(self, name: str): - self._app_name = name - - def getAppVersion(self) -> str: - return self._app_version - - def setAppVersion(self, version: str): - self._app_version = version - - def getStorageType(self) -> int: - return self._storage_type - - def setStorageType(self, storage_type: int): - self._storage_type = storage_type - - def getHash(self) -> str: - return self._hash - - def getRequestId(self) -> int: - return self._request_id - - def setRequestId(self, request_id: int): - self._request_id = request_id - - def toString(self) -> str: - return f"DownloadRequest(serviceId={self._service_id}, fileUri={self._file_uri})" - - class Builder(Stub): - """DownloadRequest.Builder — 下载请求构建器""" - - def __init__(self, service_id: str, file_uri: str): - super().__init__() - self._service_id = service_id - self._file_uri = file_uri - self._source: Any = None - self._destination: Any = None - self._app_intent: Any = None - self._subscription_id: int = 0 - self._sai: List[int] = [] - self._app_name: str = "" - self._app_version: str = "" - self._storage_type: int = 0 - - def setServiceId(self, service_id: str) -> DownloadRequest.Builder: - self._service_id = service_id - return self - - def setFileUri(self, file_uri: str) -> DownloadRequest.Builder: - self._file_uri = file_uri - return self - - def setSource(self, source: Any) -> DownloadRequest.Builder: - self._source = source - return self - - def setDestination(self, destination: Any) -> DownloadRequest.Builder: - self._destination = destination - return self - - def setIntent(self, intent: Any) -> DownloadRequest.Builder: - self._app_intent = intent - return self - - def setSubscriptionId(self, sub_id: int) -> DownloadRequest.Builder: - self._subscription_id = sub_id - return self - - def setSai(self, sai: List[int]) -> DownloadRequest.Builder: - self._sai = list(sai) - return self - - def setAppName(self, name: str) -> DownloadRequest.Builder: - self._app_name = name - return self - - def setAppVersion(self, version: str) -> DownloadRequest.Builder: - self._app_version = version - return self - - def setStorageType(self, storage_type: int) -> DownloadRequest.Builder: - self._storage_type = storage_type - return self - - def build(self) -> DownloadRequest: - req = DownloadRequest(self._service_id, self._file_uri, - self._source, self._destination, - self._app_intent) - req.setSubscriptionId(self._subscription_id) - req.setSai(self._sai) - req.setAppName(self._app_name) - req.setAppVersion(self._app_version) - req.setStorageType(self._storage_type) - return req - - -# ═══════════════════════════════════════════════════════════════ -# FileInfo -# ═══════════════════════════════════════════════════════════════ - -class FileInfo(Stub): - """FileInfo — MBMS 文件信息""" - - def __init__(self, uri: str = "", mime_type: str = "", - file_size: int = 0, name: str = ""): - super().__init__() - self._uri = uri - self._mime_type = mime_type - self._file_size = file_size - self._name = name - - def getUri(self) -> str: - return self._uri - - def getMimeType(self) -> str: - return self._mime_type - - def getFileSize(self) -> int: - return self._file_size - - def getName(self) -> str: - return self._name - - def toString(self) -> str: - return f"FileInfo(uri={self._uri}, mimeType={self._mime_type}, size={self._file_size})" - - -# ═══════════════════════════════════════════════════════════════ -# DownloadStatusListener -# ═══════════════════════════════════════════════════════════════ - -class DownloadStatusListener(Stub): - """DownloadStatusListener — MBMS 下载状态监听器""" - - def onStatusChanged(self, request: DownloadRequest, info: FileInfo, status: int): - """下载状态改变回调""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# FileServiceInfo -# ═══════════════════════════════════════════════════════════════ - -class FileServiceInfo(Stub): - """FileServiceInfo — MBMS 文件服务信息""" - - def __init__(self, service_id: str = "", service_class: str = "", - service_announcement: Any = None): - super().__init__() - self._service_id = service_id - self._service_class = service_class - self._service_announcement = service_announcement - self._sais: List[int] = [] - self._cells: List[int] = [] - self._files: List[FileInfo] = [] - - def getServiceId(self) -> str: - return self._service_id - - def getServiceClass(self) -> str: - return self._service_class - - def getServiceAnnouncement(self) -> Any: - return self._service_announcement - - def getSai(self) -> List[int]: - return list(self._sais) - - def getCells(self) -> List[int]: - return list(self._cells) - - def getFiles(self) -> List[FileInfo]: - return list(self._files) - - def toString(self) -> str: - return f"FileServiceInfo(serviceId={self._service_id}, class={self._service_class})" - - -# ═══════════════════════════════════════════════════════════════ -# StreamingService -# ═══════════════════════════════════════════════════════════════ - -class StreamingService(Stub): - """StreamingService — MBMS 流媒体服务""" - - # 流媒体服务状态 - STATE_STOPPED = 0 - STATE_STARTED = 1 - STATE_STALLED = 2 - STATE_PENDING = 3 - STATE_ERROR = 4 - - # 流媒体服务广播标志 - BROADCAST_METHOD_UNICAST = 0 - BROADCAST_METHOD_MULTICAST = 1 - BROADCAST_METHOD_BROADCAST = 2 - - # 流媒体服务信息 - STREAMING_SERVICE_STATE_STOPPED = 0 - STREAMING_SERVICE_STATE_STARTED = 1 - STREAMING_SERVICE_STATE_STALLED = 2 - STREAMING_SERVICE_STATE_PENDING = 3 - STREAMING_SERVICE_STATE_ERROR = 4 - - REASON_NONE = 0 - REASON_BY_USER_REQUEST = 1 - REASON_BY_MIDDLEWARE = 2 - REASON_BY_RADIO_RESOURCE = 3 - REASON_BY_OUT_OF_MEMORY = 4 - REASON_BY_SIGNAL_LOST = 5 - REASON_BY_TEMPORARILY_UNAVAILABLE = 6 - REASON_BY_PERMANENTLY_UNAVAILABLE = 7 - REASON_BY_SERVICE_SHUTDOWN = 8 - REASON_BY_SERVICE_NOT_VISIBLE = 9 - REASON_BY_RADIO_NOT_AVAILABLE = 10 - - def __init__(self, service_id: str = "", service_class: str = ""): - super().__init__() - self._service_id = service_id - self._service_class = service_class - self._state = StreamingService.STATE_STOPPED - self._broadcast_method = StreamingService.BROADCAST_METHOD_MULTICAST - self._callback: Optional[StreamingServiceCallback] = None - self._audio_description: Any = None - self._video_description: Any = None - self._subtitle_descriptions: List[Any] = [] - self._estimated_bitrate: int = 0 - - def getServiceId(self) -> str: - return self._service_id - - def getServiceClass(self) -> str: - return self._service_class - - def getState(self) -> int: - return self._state - - def setState(self, state: int): - self._state = state - - def getBroadcastMethod(self) -> int: - return self._broadcast_method - - def setBroadcastMethod(self, method: int): - self._broadcast_method = method - - def getAudioDescription(self) -> Any: - return self._audio_description - - def setAudioDescription(self, desc: Any): - self._audio_description = desc - - def getVideoDescription(self) -> Any: - return self._video_description - - def setVideoDescription(self, desc: Any): - self._video_description = desc - - def getSubtitleDescriptions(self) -> List[Any]: - return list(self._subtitle_descriptions) - - def setSubtitleDescriptions(self, descs: List[Any]): - self._subtitle_descriptions = list(descs) - - def getEstimatedBitrate(self) -> int: - return self._estimated_bitrate - - def setEstimatedBitrate(self, bitrate: int): - self._estimated_bitrate = bitrate - - def setCallback(self, callback: StreamingServiceCallback): - self._callback = callback - - def getCallback(self) -> Optional[StreamingServiceCallback]: - return self._callback - - def start(self): - """启动流媒体服务""" - self._state = StreamingService.STATE_STARTED - if self._callback: - self._callback.onStreamStateUpdated(self._state, - StreamingService.REASON_BY_USER_REQUEST) - - def stop(self): - """停止流媒体服务""" - self._state = StreamingService.STATE_STOPPED - if self._callback: - self._callback.onStreamStateUpdated(self._state, - StreamingService.REASON_BY_USER_REQUEST) - - def close(self): - """关闭流媒体服务""" - self._state = StreamingService.STATE_STOPPED - self._callback = None - - def toString(self) -> str: - return f"StreamingService(serviceId={self._service_id}, state={self._state})" - - -# ═══════════════════════════════════════════════════════════════ -# StreamingServiceCallback -# ═══════════════════════════════════════════════════════════════ - -class StreamingServiceCallback(Stub): - """StreamingServiceCallback — MBMS 流媒体服务回调""" - - SIGNAL_STRENGTH_UNKNOWN = 0 - SIGNAL_STRENGTH_POOR = 1 - SIGNAL_STRENGTH_MODERATE = 2 - SIGNAL_STRENGTH_GOOD = 3 - SIGNAL_STRENGTH_GREAT = 4 - - def onStreamStateUpdated(self, state: int, reason: int): - """流状态更新回调""" - pass - - def onMediaDescriptionUpdated(self): - """媒体描述更新回调""" - pass - - def onBroadcastSignalStrengthUpdated(self, signal_strength: int): - """广播信号强度更新回调""" - pass - - def onStreamMethodUpdated(self, broadcast_method: int): - """流方法更新回调""" - pass - - def onError(self, error_code: int, message: str = ""): - """错误回调""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# DownloadProgressListener -# ═══════════════════════════════════════════════════════════════ - -class DownloadProgressListener(Stub): - """DownloadProgressListener — MBMS 下载进度监听器""" - - def onProgressUpdated(self, request: DownloadRequest, info: FileInfo, - current_bytes: int, total_bytes: int, current_files: int, - total_files: int): - """下载进度更新回调""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# DownloadRequestListener -# ═══════════════════════════════════════════════════════════════ - -class DownloadRequestListener(Stub): - """DownloadRequestListener — MBMS 下载请求监听器""" - - def onProgressUpdated(self, request: DownloadRequest, info: FileInfo, - current_bytes: int, total_bytes: int, current_files: int, - total_files: int): - pass - - def onDownloadStateChanged(self, request: DownloadRequest, state: int): - pass - - -# ═══════════════════════════════════════════════════════════════ -# MbmsDownloadSession (pre-API 30) -# ═══════════════════════════════════════════════════════════════ - -class MbmsDownloadSession(Stub): - """MbmsDownloadSession — MBMS 下载会话 (旧版 API)""" - - EXTRA_MBMS_DOWNLOAD_RESULT = "android.telephony.mbms.extra.MBMS_DOWNLOAD_RESULT" - EXTRA_MBMS_DOWNLOAD_REQUEST = "android.telephony.mbms.extra.MBMS_DOWNLOAD_REQUEST" - EXTRA_MBMS_FILE_INFO = "android.telephony.mbms.extra.MBMS_FILE_INFO" - EXTRA_MBMS_COMPLETED_FILES = "android.telephony.mbms.extra.MBMS_COMPLETED_FILES" - - RESULT_SUCCESSFUL_DOWNLOAD = 0 - RESULT_EXPIRED_CHARGES = 1 - RESULT_FILE_UNAVAILABLE = 2 - RESULT_IO_ERROR = 3 - RESULT_OUT_OF_STORAGE = 4 - RESULT_SERVICE_NOT_DOWNLOAD = 5 - RESULT_DOWNLOAD_NOT_STARTED = 6 - RESULT_FILE_NOT_FOUND = 7 - RESULT_FILE_IN_PROGRESS = 8 - RESULT_FILE_ALREADY_DOWNLOADED = 9 - RESULT_MALFORMED_URI = 10 - RESULT_CANCELLED = 11 - RESULT_INTERRUPTED = 12 - RESULT_TIMEOUT = 13 - RESULT_SERVICE_UNAVAILABLE = 14 - RESULT_MEDIA_NOT_FOUND = 15 - RESULT_MEDIA_FAILED = 16 - RESULT_ERROR_RADIO_OFF = 17 - RESULT_ERROR_OUT_OF_MEMORY = 18 - - _instance_map: Dict[int, MbmsDownloadSession] = {} - - def __init__(self, context: Any = None, subscription_id: int = 0, - callback: Any = None): - super().__init__() - self._context = context - self._subscription_id = subscription_id - self._callback = callback - self._middleware_bound = False - self._download_requests: Dict[int, DownloadRequest] = {} - self._file_services: Dict[str, FileServiceInfo] = {} - self._temp_root: str = "" - - @staticmethod - def create(context: Any, callback: Any, - subscription_id: int = 0) -> MbmsDownloadSession: - session = MbmsDownloadSession(context, subscription_id, callback) - MbmsDownloadSession._instance_map[subscription_id] = session - return session - - def requestUpdate(self, file_service_list: List[FileServiceInfo]): - """请求更新文件服务列表""" - pass - - def download(self, request: DownloadRequest): - """开始下载""" - request_id = int(time.time() * 1000) - request.setRequestId(request_id) - self._download_requests[request_id] = request - - def cancelDownload(self, download_request: DownloadRequest): - """取消下载""" - rid = download_request.getRequestId() - if rid in self._download_requests: - del self._download_requests[rid] - - def getCurrentPendingDownloadRequests(self) -> List[DownloadRequest]: - """获取当前待处理的下载请求""" - return list(self._download_requests.values()) - - def getTempFileRoot(self) -> str: - """获取临时文件根目录""" - return self._temp_root - - def setTempFileRoot(self, temp_root: str, storage_type: int): - """设置临时文件根目录""" - self._temp_root = temp_root - - def getMiddlewareVersion(self) -> int: - """获取中间件版本""" - return 0 - - def getSubscriptionId(self) -> int: - return self._subscription_id - - def close(self): - """关闭会话""" - self._download_requests.clear() - self._file_services.clear() - - @staticmethod - def getDownloadStatus(context: Any, subscription_id: int, - download_request: DownloadRequest) -> int: - return DownloadRequest.DOWNLOAD_STATUS_INACTIVE - - def getFileServices(self) -> Dict[str, FileServiceInfo]: - return dict(self._file_services) - - def getName(self) -> str: - return "MbmsDownloadSession" - - def toString(self) -> str: - return f"MbmsDownloadSession(subId={self._subscription_id})" - - -# ═══════════════════════════════════════════════════════════════ -# MbmsStreamingSession (pre-API 30) -# ═══════════════════════════════════════════════════════════════ - -class MbmsStreamingSession(Stub): - """MbmsStreamingSession — MBMS 流媒体会话 (旧版 API)""" - - EXTRA_MBMS_STREAMING_SERVICE = "android.telephony.mbms.extra.MBMS_STREAMING_SERVICE" - EXTRA_MBMS_STREAMING_SERVICE_INFO = "android.telephony.mbms.extra.MBMS_STREAMING_SERVICE_INFO" - - _instance_map: Dict[int, MbmsStreamingSession] = {} - - def __init__(self, context: Any = None, subscription_id: int = 0, - callback: Any = None): - super().__init__() - self._context = context - self._subscription_id = subscription_id - self._callback = callback - self._middleware_bound = False - self._streaming_services: Dict[str, StreamingService] = {} - - @staticmethod - def create(context: Any, callback: Any, - subscription_id: int = 0) -> MbmsStreamingSession: - session = MbmsStreamingSession(context, subscription_id, callback) - MbmsStreamingSession._instance_map[subscription_id] = session - return session - - def startStreaming(self, service_info: FileServiceInfo, - callback: StreamingServiceCallback) -> StreamingService: - """开始流媒体""" - service = StreamingService(service_info.getServiceId(), - service_info.getServiceClass()) - service.setCallback(callback) - self._streaming_services[service_info.getServiceId()] = service - return service - - def stopStreaming(self, streaming_service: StreamingService): - """停止流媒体""" - sid = streaming_service.getServiceId() - if sid in self._streaming_services: - streaming_service.stop() - del self._streaming_services[sid] - - def requestUpdate(self, file_service_list: List[FileServiceInfo]): - """请求更新服务列表""" - pass - - def getCurrentStreamingServices(self) -> List[StreamingService]: - """获取当前流媒体服务""" - return list(self._streaming_services.values()) - - def getMiddlewareVersion(self) -> int: - return 0 - - def getSubscriptionId(self) -> int: - return self._subscription_id - - def close(self): - """关闭会话""" - for service in self._streaming_services.values(): - service.close() - self._streaming_services.clear() - - def getName(self) -> str: - return "MbmsStreamingSession" - - def toString(self) -> str: - return f"MbmsStreamingSession(subId={self._subscription_id})" - - -# ═══════════════════════════════════════════════════════════════ -# MbmsDownloadManager (API 30+) -# ═══════════════════════════════════════════════════════════════ - -class MbmsDownloadManager(Stub): - """MbmsDownloadManager — MBMS 下载管理器 (API 30+)""" - - EXTRA_MBMS_DOWNLOAD_RESULT = "android.telephony.mbms.extra.MBMS_DOWNLOAD_RESULT" - EXTRA_MBMS_DOWNLOAD_REQUEST = "android.telephony.mbms.extra.MBMS_DOWNLOAD_REQUEST" - EXTRA_MBMS_FILE_INFO = "android.telephony.mbms.extra.MBMS_FILE_INFO" - EXTRA_MBMS_COMPLETED_FILES = "android.telephony.mbms.extra.MBMS_COMPLETED_FILES" - - # 下载结果码 - DOWNLOAD_RESULT_SUCCESS = 0 - DOWNLOAD_RESULT_ERROR = 1 - DOWNLOAD_RESULT_ERROR_MEDIA_NOT_FOUND = 2 - DOWNLOAD_RESULT_ERROR_MEDIA_UNAVAILABLE = 3 - DOWNLOAD_RESULT_ERROR_MEDIA_FAILED = 4 - DOWNLOAD_RESULT_ERROR_SERVICE_UNAVAILABLE = 5 - DOWNLOAD_RESULT_ERROR_INSUFFICIENT_STORAGE = 6 - DOWNLOAD_RESULT_ERROR_IO_ERROR = 7 - DOWNLOAD_RESULT_ERROR_FILE_NOT_FOUND = 8 - DOWNLOAD_RESULT_ERROR_OUT_OF_MEMORY = 9 - DOWNLOAD_RESULT_ERROR_RADIO_OFF = 10 - DOWNLOAD_RESULT_ERROR_INTERRUPTED = 11 - DOWNLOAD_RESULT_ERROR_TIMEOUT = 12 - DOWNLOAD_RESULT_ERROR_CANCELLED = 13 - DOWNLOAD_RESULT_ERROR_DOWNLOAD_NOT_STARTED = 14 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._download_requests: Dict[int, DownloadRequest] = {} - self._file_services: Dict[str, FileServiceInfo] = {} - self._listeners: List[DownloadStatusListener] = [] - self._progress_listeners: List[DownloadProgressListener] = [] - - @staticmethod - def getService(context: Any, sub_id: int = 0) -> Optional[MbmsDownloadManager]: - return MbmsDownloadManager(context) - - def addDownloadRequest(self, download_request: DownloadRequest): - """添加下载请求""" - request_id = len(self._download_requests) + 1 - download_request.setRequestId(request_id) - self._download_requests[request_id] = download_request - - def removeDownloadRequest(self, download_request: DownloadRequest): - """移除下载请求""" - rid = download_request.getRequestId() - self._download_requests.pop(rid, None) - - def getCurrentDownloadRequests(self) -> List[DownloadRequest]: - """获取当前所有下载请求""" - return list(self._download_requests.values()) - - def getPendingDownloadRequests(self) -> List[DownloadRequest]: - """获取待处理的下载请求""" - return [r for r in self._download_requests.values() - if r.getHash() == ""] - - def getCompletedDownloadRequests(self) -> List[DownloadRequest]: - """获取已完成的下载请求""" - return [r for r in self._download_requests.values() - if r.getHash() != ""] - - def addStatusListener(self, listener: DownloadStatusListener): - """添加状态监听器""" - if listener not in self._listeners: - self._listeners.append(listener) - - def removeStatusListener(self, listener: DownloadStatusListener): - """移除状态监听器""" - self._listeners = [l for l in self._listeners if l is not listener] - - def addProgressListener(self, listener: DownloadProgressListener): - """添加进度监听器""" - if listener not in self._progress_listeners: - self._progress_listeners.append(listener) - - def removeProgressListener(self, listener: DownloadProgressListener): - """移除进度监听器""" - self._progress_listeners = [l for l in self._progress_listeners - if l is not listener] - - def getMaxDownloadRequests(self) -> int: - """获取最大下载请求数""" - return 100 - - def getFileServices(self) -> Dict[str, FileServiceInfo]: - return dict(self._file_services) - - def setTempFileRoot(self, temp_root: str, storage_type: int): - """设置临时文件根目录""" - pass - - def getTempFileRoot(self) -> str: - return "" - - def getMiddlewareVersion(self) -> int: - return 0 - - def close(self): - """关闭下载管理器""" - self._download_requests.clear() - self._listeners.clear() - self._progress_listeners.clear() - - def toString(self) -> str: - return f"MbmsDownloadManager(requests={len(self._download_requests)})" - - -# ═══════════════════════════════════════════════════════════════ -# MbmsStreamingManager (API 30+) -# ═══════════════════════════════════════════════════════════════ - -class MbmsStreamingManager(Stub): - """MbmsStreamingManager — MBMS 流媒体管理器 (API 30+)""" - - EXTRA_MBMS_STREAMING_SERVICE_INFO = "android.telephony.mbms.extra.MBMS_STREAMING_SERVICE_INFO" - - STREAMING_SERVICE_STATE_STOPPED = 0 - STREAMING_SERVICE_STATE_STARTED = 1 - STREAMING_SERVICE_STATE_STALLED = 2 - STREAMING_SERVICE_STATE_PENDING = 3 - STREAMING_SERVICE_STATE_ERROR = 4 - - BROADCAST_METHOD_UNICAST = 0 - BROADCAST_METHOD_MULTICAST = 1 - BROADCAST_METHOD_BROADCAST = 2 - - REASON_BY_USER_REQUEST = 1 - REASON_BY_MIDDLEWARE = 2 - REASON_BY_RADIO_RESOURCE = 3 - REASON_BY_OUT_OF_MEMORY = 4 - REASON_BY_SIGNAL_LOST = 5 - REASON_BY_TEMPORARILY_UNAVAILABLE = 6 - REASON_BY_PERMANENTLY_UNAVAILABLE = 7 - REASON_BY_SERVICE_SHUTDOWN = 8 - REASON_BY_SERVICE_NOT_VISIBLE = 9 - REASON_BY_RADIO_NOT_AVAILABLE = 10 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._streaming_services: Dict[str, StreamingService] = {} - self._service_info_map: Dict[str, FileServiceInfo] = {} - - @staticmethod - def getService(context: Any, sub_id: int = 0) -> Optional[MbmsStreamingManager]: - return MbmsStreamingManager(context) - - def startStreaming(self, service_info: FileServiceInfo, - callback: StreamingServiceCallback) -> StreamingService: - """启动流媒体服务""" - service = StreamingService(service_info.getServiceId(), - service_info.getServiceClass()) - service.setCallback(callback) - self._streaming_services[service_info.getServiceId()] = service - self._service_info_map[service_info.getServiceId()] = service_info - return service - - def stopStreaming(self, streaming_service: StreamingService): - """停止流媒体服务""" - sid = streaming_service.getServiceId() - if sid in self._streaming_services: - streaming_service.stop() - del self._streaming_services[sid] - - def getCurrentStreamingServices(self) -> List[StreamingService]: - return list(self._streaming_services.values()) - - def getServiceInfoForStreamingService(self, service: StreamingService) -> Optional[FileServiceInfo]: - return self._service_info_map.get(service.getServiceId()) - - def getMaxStreamingServices(self) -> int: - return 10 - - def getMiddlewareVersion(self) -> int: - return 0 - - def close(self): - """关闭流媒体管理器""" - for service in self._streaming_services.values(): - service.close() - self._streaming_services.clear() - self._service_info_map.clear() - - def toString(self) -> str: - return f"MbmsStreamingManager(services={len(self._streaming_services)})" - - -# ═══════════════════════════════════════════════════════════════ -# MbmsUtils -# ═══════════════════════════════════════════════════════════════ - -class MbmsUtils(Stub): - """MbmsUtils — MBMS 工具类""" - - @staticmethod - def getCellBroadcastServiceInfo(context: Any, - service_id: str, - subscription_id: int) -> Optional[FileServiceInfo]: - """获取小区广播服务信息""" - return None - - @staticmethod - def isFileService(service_announcement: Any) -> bool: - """判断是否为文件服务""" - return True - - @staticmethod - def isStreamingService(service_announcement: Any) -> bool: - """判断是否为流媒体服务""" - return True - - @staticmethod - def getServiceClassFromServiceAnnouncement(service_announcement: Any) -> Optional[str]: - """从服务公告中获取服务类""" - return None - - @staticmethod - def getServiceIdFromServiceAnnouncement(service_announcement: Any) -> Optional[str]: - """从服务公告中获取服务 ID""" - return None - - @staticmethod - def getFileInfoFromFileService(service_info: FileServiceInfo) -> List[FileInfo]: - """从文件服务信息中获取文件信息列表""" - return service_info.getFiles() - - @staticmethod - def createDownloadRequest(service_id: str, file_uri: str, - source: Any, destination: Any) -> DownloadRequest: - """创建下载请求""" - return DownloadRequest(service_id, file_uri, source, destination) - - -# ═══════════════════════════════════════════════════════════════ -# MbmsGroupCallService -# ═══════════════════════════════════════════════════════════════ - -class MbmsGroupCallService(Stub): - """MbmsGroupCallService — MBMS 组呼服务""" - - REASON_BY_USER_REQUEST = 1 - REASON_BY_MIDDLEWARE = 2 - REASON_BY_RADIO_RESOURCE = 3 - REASON_BY_OUT_OF_MEMORY = 4 - REASON_BY_SIGNAL_LOST = 5 - REASON_BY_TEMPORARILY_UNAVAILABLE = 6 - REASON_BY_PERMANENTLY_UNAVAILABLE = 7 - REASON_BY_SERVICE_SHUTDOWN = 8 - REASON_BY_SERVICE_NOT_VISIBLE = 9 - REASON_BY_RADIO_NOT_AVAILABLE = 10 - - STATE_STOPPED = 0 - STATE_STARTED = 1 - STATE_STALLED = 2 - STATE_PENDING = 3 - STATE_ERROR = 4 - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._group_calls: Dict[str, Any] = {} - - @staticmethod - def getService(context: Any, sub_id: int = 0) -> Optional[MbmsGroupCallService]: - return MbmsGroupCallService(context) - - def startGroupCall(self, service_id: str, sai_list: List[int], - frequency: int, tmgi: Any, callback: Any): - """启动组呼""" - pass - - def stopGroupCall(self, service_id: str): - """停止组呼""" - self._group_calls.pop(service_id, None) - - def getCurrentGroupCalls(self) -> List[Any]: - return list(self._group_calls.values()) - - def close(self): - self._group_calls.clear() - - def toString(self) -> str: - return f"MbmsGroupCallService(calls={len(self._group_calls)})" - - -# ═══════════════════════════════════════════════════════════════ -# MbmsDownloadSessionCallback -# ═══════════════════════════════════════════════════════════════ - -class MbmsDownloadSessionCallback(Stub): - """MbmsDownloadSessionCallback — MBMS 下载会话回调""" - - def onError(self, error_code: int, message: str = ""): - pass - - def onFileServicesUpdated(self, services: List[FileServiceInfo]): - pass - - def onMiddlewareReady(self): - pass - - -# ═══════════════════════════════════════════════════════════════ -# MbmsStreamingSessionCallback -# ═══════════════════════════════════════════════════════════════ - -class MbmsStreamingSessionCallback(Stub): - """MbmsStreamingSessionCallback — MBMS 流媒体会话回调""" - - def onError(self, error_code: int, message: str = ""): - pass - - def onStreamingServicesUpdated(self, services: List[FileServiceInfo]): - pass - - def onMiddlewareReady(self): - pass - - -# ═══════════════════════════════════════════════════════════════ -# ServiceAnnouncement -# ═══════════════════════════════════════════════════════════════ - -class ServiceAnnouncement(Stub): - """ServiceAnnouncement — MBMS 服务公告""" - - def __init__(self, service_id: str = "", service_class: str = "", - announcement_data: bytes = b""): - super().__init__() - self._service_id = service_id - self._service_class = service_class - self._announcement_data = announcement_data - self._sais: List[int] = [] - self._cells: List[int] = [] - self._expiry_time: int = 0 - - def getServiceId(self) -> str: - return self._service_id - - def getServiceClass(self) -> str: - return self._service_class - - def getAnnouncementData(self) -> bytes: - return self._announcement_data - - def getSais(self) -> List[int]: - return list(self._sais) - - def setSais(self, sais: List[int]): - self._sais = list(sais) - - def getCells(self) -> List[int]: - return list(self._cells) - - def setCells(self, cells: List[int]): - self._cells = list(cells) - - def getExpiryTime(self) -> int: - return self._expiry_time - - def setExpiryTime(self, expiry: int): - self._expiry_time = expiry - - def toString(self) -> str: - return f"ServiceAnnouncement(serviceId={self._service_id})" - - -# ═══════════════════════════════════════════════════════════════ -# InternalDownloadSession (helper) -# ═══════════════════════════════════════════════════════════════ - -class InternalDownloadSession(Stub): - """InternalDownloadSession — 内部下载会话辅助类""" - - def __init__(self): - super().__init__() - self._session_id: int = 0 - self._active: bool = False - self._downloads: Dict[int, DownloadRequest] = {} - - def getSessionId(self) -> int: - return self._session_id - - def setSessionId(self, session_id: int): - self._session_id = session_id - - def isActive(self) -> bool: - return self._active - - def setActive(self, active: bool): - self._active = active - - def addDownload(self, request: DownloadRequest): - self._downloads[request.getRequestId()] = request - - def removeDownload(self, request_id: int): - self._downloads.pop(request_id, None) - - def getDownloads(self) -> List[DownloadRequest]: - return list(self._downloads.values()) - - def clear(self): - self._downloads.clear() - self._active = False - - def toString(self) -> str: - return f"InternalDownloadSession(id={self._session_id}, active={self._active})" - - -# ═══════════════════════════════════════════════════════════════ -# MbmsResourceUtils -# ═══════════════════════════════════════════════════════════════ - -class MbmsResourceUtils(Stub): - """MbmsResourceUtils — MBMS 资源工具""" - - @staticmethod - def getServiceIdList(services: List[FileServiceInfo]) -> List[str]: - return [s.getServiceId() for s in services] - - @staticmethod - def getServiceNameList(services: List[FileServiceInfo]) -> List[str]: - return [s.getServiceClass() for s in services] - - @staticmethod - def findServiceById(services: List[FileServiceInfo], - service_id: str) -> Optional[FileServiceInfo]: - for s in services: - if s.getServiceId() == service_id: - return s - return None - - @staticmethod - def mergeServiceLists(a: List[FileServiceInfo], - b: List[FileServiceInfo]) -> List[FileServiceInfo]: - merged = {s.getServiceId(): s for s in a} - for s in b: - merged[s.getServiceId()] = s - return list(merged.values()) \ No newline at end of file diff --git a/android_pyapi/android/test.py b/android_pyapi/android/test.py deleted file mode 100644 index e730422..0000000 --- a/android_pyapi/android/test.py +++ /dev/null @@ -1,485 +0,0 @@ -""" -Android Test 组件 — InstrumentationTestRunner, InstrumentationTestCase, -ActivityInstrumentationTestCase2, ActivityUnitTestCase, ServiceTestCase, -ProviderTestCase2, ApplicationTestCase, Instrumentation 等 -方法签名完全对齐 Android SDK (android.test 包) -""" - -import os -import sys -import time -import threading -from typing import Any, Callable, Dict, List, Optional, Tuple, Type - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# Instrumentation -# ═══════════════════════════════════════════════════════════════ - -class Instrumentation(Stub): - """Instrumentation — 测试仪器 (桌面版)""" - - # 报告标志 - REPORT_KEY_IDENTIFIER = "id" - REPORT_KEY_STREAM_RESULTS = "stream" - - def __init__(self): - super().__init__() - self._context: Optional[Any] = None - self._target_context: Optional[Any] = None - self._started = False - self._finished = False - self._automatic_performance_snapshots = False - self._instr_result: Dict[str, Any] = {} - - def onCreate(self, arguments: Any): - """创建仪器""" - self._started = True - - def onStart(self): - """启动仪器""" - pass - - def onDestroy(self): - """销毁仪器""" - self._finished = True - - def getContext(self) -> Optional[Any]: - """获取上下文""" - return self._context - - def getTargetContext(self) -> Optional[Any]: - """获取目标上下文""" - return self._target_context - - def setContext(self, context: Any): - self._context = context - - def setTargetContext(self, target_context: Any): - self._target_context = target_context - - def startActivitySync(self, intent: Any) -> Any: - """同步启动 Activity""" - return None - - def sendKeySync(self, key_event: Any): - """发送按键事件""" - pass - - def sendCharacterSync(self, character: int): - """发送字符事件""" - pass - - def sendStringSync(self, text: str): - """发送字符串""" - pass - - def sendPointerSync(self, motion_event: Any): - """发送指针事件""" - pass - - def sendTrackballEventSync(self, motion_event: Any): - """发送轨迹球事件""" - pass - - def runOnMainSync(self, runner: Callable): - """在主线程上同步运行""" - runner() - - def waitForIdle(self): - """等待空闲""" - pass - - def waitForIdleSync(self): - """同步等待空闲""" - pass - - def callActivityOnCreate(self, activity: Any, icicle: Any): - """调用 Activity.onCreate""" - if hasattr(activity, 'onCreate'): - activity.onCreate(icicle) - - def callActivityOnStart(self, activity: Any): - """调用 Activity.onStart""" - if hasattr(activity, 'onStart'): - activity.onStart() - - def callActivityOnResume(self, activity: Any): - """调用 Activity.onResume""" - if hasattr(activity, 'onResume'): - activity.onResume() - - def callActivityOnPause(self, activity: Any): - """调用 Activity.onPause""" - if hasattr(activity, 'onPause'): - activity.onPause() - - def callActivityOnStop(self, activity: Any): - """调用 Activity.onStop""" - if hasattr(activity, 'onStop'): - activity.onStop() - - def callActivityOnDestroy(self, activity: Any): - """调用 Activity.onDestroy""" - if hasattr(activity, 'onDestroy'): - activity.onDestroy() - - def callActivityOnRestart(self, activity: Any): - """调用 Activity.onRestart""" - if hasattr(activity, 'onRestart'): - activity.onRestart() - - def isProfiling(self) -> bool: - return False - - def startProfiling(self): - pass - - def stopProfiling(self): - pass - - def setInTouchMode(self, in_touch: bool): - pass - - def finish(self, result_code: int, results: Any = None): - """完成测试""" - self._finished = True - if results: - self._instr_result.update(results) - - def getComponentName(self) -> Any: - return None - - def getUiAutomation(self) -> Any: - return None - - def toString(self) -> str: - return (f"Instrumentation(started={self._started}, " - f"finished={self._finished})") - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# InstrumentationTestRunner -# ═══════════════════════════════════════════════════════════════ - -class InstrumentationTestRunner(Stub): - """InstrumentationTestRunner — 仪器化测试运行器""" - - REPORT_KEY_NAME = "name" - REPORT_KEY_CLASS = "class" - REPORT_KEY_TEST = "test" - REPORT_KEY_NUM_TOTAL = "numtests" - REPORT_KEY_NUM_CURRENT = "current" - REPORT_KEY_NUM_ITERATIONS = "numiterations" - - def __init__(self): - super().__init__() - self._instrumentation = Instrumentation() - self._test_results: List[Dict[str, Any]] = [] - - def onCreate(self, arguments: Any): - """创建测试运行器""" - self._instrumentation.onCreate(arguments) - - def onStart(self): - """开始测试""" - self._instrumentation.onStart() - - def onDestroy(self): - """销毁""" - self._instrumentation.onDestroy() - - def getInstrumentation(self) -> Instrumentation: - return self._instrumentation - - def runTest(self, test_case: Any): - """运行测试用例""" - try: - if hasattr(test_case, 'setUp'): - test_case.setUp() - if hasattr(test_case, 'runTest'): - test_case.runTest() - if hasattr(test_case, 'tearDown'): - test_case.tearDown() - self._test_results.append({"success": True, "test": str(test_case)}) - except Exception as e: - self._test_results.append({ - "success": False, "test": str(test_case), "error": str(e), - }) - - def getTestResults(self) -> List[Dict[str, Any]]: - return list(self._test_results) - - def toString(self) -> str: - return (f"InstrumentationTestRunner(" - f"results={len(self._test_results)})") - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - -# ═══════════════════════════════════════════════════════════════ -# InstrumentationTestCase -# ═══════════════════════════════════════════════════════════════ - -class InstrumentationTestCase(Stub): - """InstrumentationTestCase — 仪器化测试用例基类""" - - def __init__(self): - super().__init__() - self._instrumentation: Optional[Instrumentation] = None - self._test_name: str = "" - - def injectInstrumentation(self, instrumentation: Instrumentation): - """注入仪器""" - self._instrumentation = instrumentation - - def getInstrumentation(self) -> Optional[Instrumentation]: - return self._instrumentation - - def setUp(self): - """测试前准备""" - pass - - def tearDown(self): - """测试后清理""" - pass - - def runTest(self): - """执行测试""" - pass - - def sendKeys(self, *keys: str): - """发送按键""" - pass - - def touchDown(self, view: Any, x: float, y: float): - """触摸按下""" - pass - - def touchUp(self, view: Any, x: float, y: float): - """触摸抬起""" - pass - - def touchAndHold(self, view: Any, x: float, y: float): - """触摸并按住""" - pass - - def click(self, view: Any): - """点击""" - pass - - def typeText(self, view: Any, text: str): - """输入文本""" - pass - - def assertActivityRequiresFeature(self, activity_class: Any, - feature: str) -> bool: - return True - - def toString(self) -> str: - return f"InstrumentationTestCase(name={self._test_name})" - - def hashCode(self) -> int: - return hash(self._test_name) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, InstrumentationTestCase): - return False - return self._test_name == other._test_name - - -# ═══════════════════════════════════════════════════════════════ -# ActivityInstrumentationTestCase2 -# ═══════════════════════════════════════════════════════════════ - -class ActivityInstrumentationTestCase2(InstrumentationTestCase): - """ActivityInstrumentationTestCase2 — Activity 测试用例""" - - def __init__(self, pkg: str, activity_class: Type): - super().__init__() - self._pkg = pkg - self._activity_class = activity_class - self._activity: Optional[Any] = None - - def setActivityIntent(self, intent: Any): - """设置 Activity Intent""" - pass - - def getActivity(self) -> Optional[Any]: - """获取 Activity 实例""" - return self._activity - - def testActivityCreated(self) -> bool: - return self._activity is not None - - def testActivityResumed(self) -> bool: - return self._activity is not None - - def testActivityStarted(self) -> bool: - return self._activity is not None - - def toString(self) -> str: - return (f"ActivityInstrumentationTestCase2(" - f"pkg={self._pkg}, cls={self._activity_class})") - - -# ═══════════════════════════════════════════════════════════════ -# ActivityUnitTestCase -# ═══════════════════════════════════════════════════════════════ - -class ActivityUnitTestCase(InstrumentationTestCase): - """ActivityUnitTestCase — Activity 单元测试""" - - def __init__(self, activity_class: Type): - super().__init__() - self._activity_class = activity_class - self._activity: Optional[Any] = None - - def setActivityContext(self, context: Any): - """设置 Activity 上下文""" - if self._activity and hasattr(self._activity, 'setContext'): - self._activity.setContext(context) - - def setApplication(self, application: Any): - """设置 Application""" - if self._activity and hasattr(self._activity, 'setApplication'): - self._activity.setApplication(application) - - def getActivity(self) -> Optional[Any]: - return self._activity - - def startActivity(self, intent: Any, bundle: Any = None, - last_non_config_instance: Any = None) -> Any: - """启动 Activity""" - return self._activity - - def toString(self) -> str: - return f"ActivityUnitTestCase(cls={self._activity_class})" - - -# ═══════════════════════════════════════════════════════════════ -# ServiceTestCase -# ═══════════════════════════════════════════════════════════════ - -class ServiceTestCase(InstrumentationTestCase): - """ServiceTestCase — Service 测试用""" - - def __init__(self, service_class: Type): - super().__init__() - self._service_class = service_class - self._service: Optional[Any] = None - self._service_binder: Optional[Any] = None - self._service_created = False - - def getService(self) -> Optional[Any]: - return self._service - - def bindService(self, intent: Any) -> Any: - """绑定 Service""" - return self._service_binder - - def bindServiceNotNeeded(self): - """Service 不需要绑定""" - pass - - def setService(self, service: Any): - self._service = service - - def testServiceCreated(self) -> bool: - return self._service_created - - def testServiceBound(self) -> bool: - return self._service_binder is not None - - def testServiceStarted(self) -> bool: - return self._service is not None - - def testServiceStopped(self) -> bool: - return not self._service_created - - def toString(self) -> str: - return f"ServiceTestCase(cls={self._service_class})" - - -# ═══════════════════════════════════════════════════════════════ -# ProviderTestCase2 -# ═══════════════════════════════════════════════════════════════ - -class ProviderTestCase2(InstrumentationTestCase): - """ProviderTestCase2 — ContentProvider 测试用例""" - - def __init__(self, provider_class: Type, provider_authority: str): - super().__init__() - self._provider_class = provider_class - self._provider_authority = provider_authority - self._provider: Optional[Any] = None - self._resolver: Optional[Any] = None - - def getProvider(self) -> Optional[Any]: - return self._provider - - def getMockContentResolver(self) -> Optional[Any]: - return self._resolver - - def setProvider(self, provider: Any): - self._provider = provider - - def setMockContentResolver(self, resolver: Any): - self._resolver = resolver - - def toString(self) -> str: - return (f"ProviderTestCase2(cls={self._provider_class}, " - f"authority={self._provider_authority})") - - -# ═══════════════════════════════════════════════════════════════ -# ApplicationTestCase -# ═══════════════════════════════════════════════════════════════ - -class ApplicationTestCase(InstrumentationTestCase): - """ApplicationTestCase — Application 测试用例""" - - def __init__(self, application_class: Type): - super().__init__() - self._application_class = application_class - self._application: Optional[Any] = None - - def getApplication(self) -> Optional[Any]: - return self._application - - def setApplication(self, application: Any): - self._application = application - - def testApplicationCreated(self) -> bool: - return self._application is not None - - def toString(self) -> str: - return f"ApplicationTestCase(cls={self._application_class})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "InstrumentationTestRunner", - "InstrumentationTestCase", - "ActivityInstrumentationTestCase2", - "ActivityUnitTestCase", - "ServiceTestCase", - "ProviderTestCase2", - "ApplicationTestCase", - "Instrumentation", -] \ No newline at end of file diff --git a/android_pyapi/android/text.py b/android_pyapi/android/text.py deleted file mode 100644 index d4ac87d..0000000 --- a/android_pyapi/android/text.py +++ /dev/null @@ -1,1167 +0,0 @@ -""" -Android Text 组件 — TextUtils, SpannableString, Html, Layout 等 -方法签名完全对齐 Android SDK -""" - -import html as _html -import re as _re -import sys -from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, Union -from urllib.parse import quote_plus as _quote_plus, unquote_plus as _unquote_plus - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, -) - - -# ═══════════════════════════════════════════════════════════════ -# Typeface -# ═══════════════════════════════════════════════════════════════ - -class Typeface(Stub): - """Typeface — 字体""" - - NORMAL = 0 - BOLD = 1 - ITALIC = 2 - BOLD_ITALIC = 3 - - DEFAULT: "Typeface" = None - DEFAULT_BOLD: "Typeface" = None - MONOSPACE: "Typeface" = None - SANS_SERIF: "Typeface" = None - SERIF: "Typeface" = None - - def __init__(self, family_name: str = "", style: int = NORMAL): - super().__init__() - self._family_name = family_name - self._style = style - self._weight = 400 if style in (self.NORMAL,) else 700 - - @staticmethod - def defaultFromStyle(style: int) -> "Typeface": - tf = Typeface("sans-serif", style) - return tf - - @staticmethod - def create(family_name: str, style: int) -> "Typeface": - return Typeface(family_name, style) - - @staticmethod - def createFromFile(path: str) -> "Typeface": - return Typeface("custom") - - def getStyle(self) -> int: - return self._style - - def getWeight(self) -> int: - return self._weight - - def isBold(self) -> bool: - return self._style in (self.BOLD, self.BOLD_ITALIC) - - def isItalic(self) -> bool: - return self._style in (self.ITALIC, self.BOLD_ITALIC) - - def toString(self) -> str: - return f"Typeface({self._family_name}, style={self._style})" - - -# Initialize Typeface constants -Typeface.DEFAULT = Typeface("sans-serif", Typeface.NORMAL) -Typeface.DEFAULT_BOLD = Typeface("sans-serif", Typeface.BOLD) -Typeface.MONOSPACE = Typeface("monospace", Typeface.NORMAL) -Typeface.SANS_SERIF = Typeface("sans-serif", Typeface.NORMAL) -Typeface.SERIF = Typeface("serif", Typeface.NORMAL) - - -# ═══════════════════════════════════════════════════════════════ -# TextPaint -# ═══════════════════════════════════════════════════════════════ - -class TextPaint(Stub): - """TextPaint — 文本绘制画笔""" - - def __init__(self, flags: int = 0): - super().__init__() - self.bgColor = 0 - self.bgColorSet = False - self.linkColor = 0xFF0000FF # blue - self.underlineWidth = 0.0 - self.baselineShift = 0 - self.bulletRadius = 0.0 - self.bulletGap = 0 - self.bulletColor = 0 - self.color = 0xFF000000 - self.textSize = 14.0 - self.typeface: Optional[Typeface] = Typeface.DEFAULT - self.flags = flags - self.antiAlias = True - self.style = Typeface.NORMAL - self.strokeWidth = 0.0 - self.strokeCap = "butt" - self.strokeJoin = "miter" - self.shadowDx = 0.0 - self.shadowDy = 0.0 - self.shadowRadius = 0.0 - self.shadowColor = 0 - self.elevation = 0.0 - self.letterSpacing = 0.0 - self.fontFeatureSettings = "" - self.fontVariationSettings = "" - - def setUnderlineText(self, underline: bool): - if underline: - self.underlineWidth = 1.0 - else: - self.underlineWidth = 0.0 - - def getColor(self) -> int: - return self.color - - def setColor(self, color: int): - self.color = color - - def setARGB(self, a: int, r: int, g: int, b: int): - self.color = ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF) - - def getTextSize(self) -> float: - return self.textSize - - def setTextSize(self, size: float): - self.textSize = size - - def getTypeface(self) -> Optional[Typeface]: - return self.typeface - - def setTypeface(self, tf: Typeface): - self.typeface = tf - - def setAntiAlias(self, aa: bool): - self.antiAlias = aa - - def measureText(self, text: str) -> float: - return len(text) * self.textSize * 0.5 - - def toString(self) -> str: - return f"TextPaint(size={self.textSize}, color={self.color})" - - -# ═══════════════════════════════════════════════════════════════ -# TextUtils -# ═══════════════════════════════════════════════════════════════ - -class TextUtils(Stub): - """TextUtils — 文本工具""" - - CAP_MODE_SENTENCES = 0 - CAP_MODE_WORDS = 1 - CAP_MODE_CHARACTERS = 2 - - @staticmethod - def isEmpty(str_: Optional[str]) -> bool: - return str_ is None or len(str_) == 0 - - @staticmethod - def isBlank(str_: Optional[str]) -> bool: - return str_ is None or str_.strip() == "" - - @staticmethod - def equals(a: Optional[str], b: Optional[str]) -> bool: - return a == b - - @staticmethod - def join(delimiter: str, tokens: Sequence[str]) -> str: - return delimiter.join(tokens) - - @staticmethod - def split(text: str, delimiter: str) -> List[str]: - if text is None or len(text) == 0: - return [] - return _re.split(delimiter, text) - - @staticmethod - def htmlEncode(s: str) -> str: - return _html.escape(s, quote=True) - - @staticmethod - def htmlDecode(s: str) -> str: - return _html.unescape(s) - - @staticmethod - def concat(*texts: Any) -> str: - parts = [] - for t in texts: - if t is not None: - parts.append(str(t)) - return "".join(parts) - - @staticmethod - def indexOf(s: Any, needle: Any, start: int = 0) -> int: - if s is None: - return -1 - s = str(s) - needle = str(needle) - if start < 0: - start = 0 - return s.find(needle, start) - - @staticmethod - def lastIndexOf(s: Any, needle: Any, last: Optional[int] = None) -> int: - if s is None: - return -1 - s = str(s) - needle = str(needle) - if last is None: - return s.rfind(needle) - if last < 0: - return -1 - return s.rfind(needle, 0, last + 1) - - @staticmethod - def urlEncode(s: str) -> str: - if s is None: - return "" - return _quote_plus(str(s)) - - @staticmethod - def urlDecode(s: str) -> str: - if s is None: - return "" - return _unquote_plus(str(s)) - - @staticmethod - def getReverse(s: str, start: int, end: int) -> str: - return s[start:end][::-1] - - @staticmethod - def substring(text: str, start: int, end: int) -> str: - return text[start:end] - - @staticmethod - def delimitedListToStringArray(text: str, delimiter: str, delimiter2: str = "") -> List[str]: - if not text: - return [] - return [t.strip() for t in text.split(delimiter)] - - @staticmethod - def commaEllipsize(text: str, max_width: int, paint: Any, - one_origin: bool = False, ellipsize: bool = False) -> str: - if len(text) <= max_width: - return text - return text[:max_width] + "..." - - @staticmethod - def ellipsize(text: str, paint: Any, avail: float, - where: Any, ellipsis: str = "...") -> str: - if paint and hasattr(paint, 'measureText'): - text_width = paint.measureText(text) - if text_width <= avail: - return text - max_chars = int(avail / 7) if avail else len(text) // 2 - if max_chars >= len(text): - return text - if str(where) == "END": - return text[:max_chars] + ellipsis - elif str(where) == "START": - return ellipsis + text[-max_chars:] - elif str(where) == "MIDDLE": - half = max_chars // 2 - return text[:half] + ellipsis + text[-(max_chars - half):] - return text[:max_chars] + ellipsis - - @staticmethod - def isDigitsOnly(s: Any) -> bool: - if s is None: - return False - s = str(s) - if len(s) == 0: - return False - return s.isdigit() - - @staticmethod - def isGraphical(s: str) -> bool: - return bool(s and s.isprintable()) - - @staticmethod - def capitalize(s: str) -> str: - return s.capitalize() - - @staticmethod - def trim(s: str) -> str: - return s.strip() - - @staticmethod - def getTrimmedLength(s: str) -> int: - return len(s.strip()) - - @staticmethod - def stringOrEmpty(s: Optional[str]) -> str: - return s or "" - - @staticmethod - def copySpansFrom(source: Any, start: int, end: int, kind: type, - dest: Any, dest_offset: int = 0): - pass - - @staticmethod - def removeEmptySpans(spans: List, source: Any, kind: type) -> List: - return [s for s in spans if s is not None] - - @staticmethod - def expandTemplate(template: str, values: List[str]) -> str: - result = template - for i, v in enumerate(values): - result = result.replace("$" + str(i + 1), v) - return result - - -# ═══════════════════════════════════════════════════════════════ -# Span 相关接口和基类 -# ═══════════════════════════════════════════════════════════════ - -class Spanned(Stub): - """Spanned 接口 — 带标记的字符串""" - - SPAN_POINT_MARK = 0 - SPAN_MARK_MARK = 1 - SPAN_POINT_POINT = 2 - SPAN_MARK_POINT = 3 - SPAN_PARAGRAPH = 3 - SPAN_INCLUSIVE_EXCLUSIVE = SPAN_MARK_POINT - SPAN_INCLUSIVE_INCLUSIVE = SPAN_MARK_MARK - SPAN_EXCLUSIVE_EXCLUSIVE = SPAN_POINT_POINT - SPAN_EXCLUSIVE_INCLUSIVE = SPAN_POINT_MARK - SPAN_COMPOSING = 256 - SPAN_USER = 0x01000000 - SPAN_USER_SHIFT = 24 - SPAN_PRIORITY = 0x0000FF00 - SPAN_PRIORITY_SHIFT = 8 - SPAN_INTERMEDIATE = 0x00100000 - - def __init__(self, source: str = ""): - super().__init__() - self._source = source - self._spans: Dict[Tuple[type, int], Tuple[int, int, int, Any]] = {} # (type, id) -> (start, end, flags, span_obj) - - def setSpan(self, span_obj: Any, start: int, end: int, flags: int): - span_id = id(span_obj) - span_type = type(span_obj) - self._spans[(span_type, span_id)] = (start, end, flags, span_obj) - - def removeSpan(self, span_obj: Any): - span_id = id(span_obj) - span_type = type(span_obj) - self._spans.pop((span_type, span_id), None) - - def getSpans(self, start: int, end: int, span_type: Optional[type] = None) -> List[Any]: - collected = [] - for (st, sid), (s, e, flags, obj) in self._spans.items(): - if span_type is None or st == span_type: - if s < end and e > start: - collected.append((s, obj)) - collected.sort(key=lambda t: t[0]) - return [obj for _, obj in collected] - - def getSpanStart(self, span_obj: Any) -> int: - span_id = id(span_obj) - span_type = type(span_obj) - data = self._spans.get((span_type, span_id)) - return data[0] if data else -1 - - def getSpanEnd(self, span_obj: Any) -> int: - span_id = id(span_obj) - span_type = type(span_obj) - data = self._spans.get((span_type, span_id)) - return data[1] if data else -1 - - def getSpanFlags(self, span_obj: Any) -> int: - span_id = id(span_obj) - span_type = type(span_obj) - data = self._spans.get((span_type, span_id)) - return data[2] if data else 0 - - def nextSpanTransition(self, start: int, end: int, span_type: Optional[type] = None) -> int: - result = end - for (st, sid), (s, e, flags, obj) in self._spans.items(): - if span_type is not None and st != span_type: - continue - if start < s < result: - result = s - if start < e < result: - result = e - return result - - def toString(self) -> str: - return self._source - - def __len__(self) -> int: - return len(self._source) - - def __getitem__(self, index): - return self._source[index] - - def __str__(self): - return self._source - - def length(self) -> int: - return len(self._source) - - -class Editable(Spanned): - """Editable — 可编辑文本接口""" - - def __init__(self, source: str = ""): - super().__init__(source) - - def append(self, text: Any) -> "Editable": - self._source += str(text) - return self - - def insert(self, where: int, text: Any): - self._source = self._source[:where] + str(text) + self._source[where:] - - def replace(self, start: int, end: int, text: Any): - self._source = self._source[:start] + str(text) + self._source[end:] - - def delete(self, start: int, end: int): - self._source = self._source[:start] + self._source[end:] - - def clear(self): - self._source = "" - - def clearSpans(self): - self._spans.clear() - - def getChars(self, start: int, end: int, dest: List[str], destoff: int): - for i, c in enumerate(self._source[start:end]): - if destoff + i < len(dest): - dest[destoff + i] = c - - -# ═══════════════════════════════════════════════════════════════ -# SpannableString -# ═══════════════════════════════════════════════════════════════ - -class SpannableString(Spanned): - """SpannableString — 不可变但可标记的字符串""" - - def __init__(self, source: str): - super().__init__(source) - self._source = source - - def setSpan(self, span_obj: Any, start: int, end: int, flags: int): - super().setSpan(span_obj, start, end, flags) - - def removeSpan(self, span_obj: Any): - super().removeSpan(span_obj) - - def toString(self) -> str: - return self._source - - def __str__(self): - return self._source - - @staticmethod - def valueOf(source: str) -> "SpannableString": - return SpannableString(source) - - def length(self) -> int: - return len(self._source) - - def charAt(self, index: int) -> str: - return self._source[index] - - def subSequence(self, start: int, end: int) -> str: - return self._source[start:end] - - -# ═══════════════════════════════════════════════════════════════ -# SpannableStringBuilder -# ═══════════════════════════════════════════════════════════════ - -class SpannableStringBuilder(Editable): - """SpannableStringBuilder — 可变可标记字符串构建器""" - - def __init__(self, source: Any = None): - if source is None: - super().__init__("") - elif isinstance(source, str): - super().__init__(source) - elif isinstance(source, Spanned): - super().__init__(source.toString()) - else: - super().__init__(str(source)) - - def append(self, text: Any, start: Optional[int] = None, - end: Optional[int] = None) -> "SpannableStringBuilder": - if text is None: - return self - if start is None and end is None: - self._source += str(text) - else: - s = 0 if start is None else start - text_str = str(text) - e = len(text_str) if end is None else end - self._source += text_str[s:e] - return self - - def insert(self, where: int, text: Any) -> "SpannableStringBuilder": - super().insert(where, text) - return self - - def replace(self, start: int, end: int, text: Any) -> "SpannableStringBuilder": - super().replace(start, end, text) - return self - - def delete(self, start: int, end: int) -> "SpannableStringBuilder": - super().delete(start, end) - return self - - def setSpan(self, span_obj: Any, start: int, end: int, flags: int): - super().setSpan(span_obj, start, end, flags) - - def clear(self): - super().clear() - - def clearSpans(self): - super().clearSpans() - - def length(self) -> int: - return len(self._source) - - def charAt(self, index: int) -> str: - return self._source[index] if 0 <= index < len(self._source) else "" - - def subSequence(self, start: int, end: int) -> str: - return self._source[start:end] - - def toString(self) -> str: - return self._source - - def __str__(self): - return self._source - - def __iadd__(self, other: Any) -> "SpannableStringBuilder": - self.append(str(other)) - return self - - -# ═══════════════════════════════════════════════════════════════ -# TextWatcher -# ═══════════════════════════════════════════════════════════════ - -class TextWatcher(Stub): - """TextWatcher — 文本变化监听器""" - - def beforeTextChanged(self, s: str, start: int, count: int, after: int): - pass - - def onTextChanged(self, s: str, start: int, before: int, count: int): - pass - - def afterTextChanged(self, s: Any): - pass - - -# ═══════════════════════════════════════════════════════════════ -# InputFilter / LengthFilter -# ═══════════════════════════════════════════════════════════════ - -class InputFilter(Stub): - """InputFilter — 输入过滤器接口""" - - class LengthFilter(Stub): - """LengthFilter — 长度限制过滤器""" - - def __init__(self, max_length: int): - super().__init__() - self._max_length = max_length - - def filter(self, source: str, start: int, end: int, dest: Spanned, - dstart: int, dend: int) -> Optional[str]: - keep = self._max_length - (len(dest.toString()) - (dend - dstart)) - if keep <= 0: - return "" - if keep >= (end - start): - return None # keep original - return source[start:start + keep] - - def getMax(self) -> int: - return self._max_length - - @staticmethod - def allCaps() -> "InputFilter": - f = InputFilter() - f._transform = lambda s: s.upper() - return f - - def filter(self, source: str, start: int, end: int, dest: Spanned, - dstart: int, dend: int) -> Optional[str]: - return None - - -# ═══════════════════════════════════════════════════════════════ -# Html -# ═══════════════════════════════════════════════════════════════ - -class Html(Stub): - """Html — HTML 文本处理""" - - FROM_HTML_MODE_COMPACT = 0 - FROM_HTML_MODE_LEGACY = 1 - FROM_HTML_OPTION_USE_CSS_COLORS = 256 - FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE = 0x00000001 - FROM_HTML_SEPARATOR_LINE_BREAK_DIV = 0x00000002 - FROM_HTML_SEPARATOR_LINE_BREAK_HEADING = 0x00000004 - FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM = 0x00000008 - FROM_HTML_SEPARATOR_LINE_BREAK_LIST = 0x00000020 - FROM_HTML_SEPARATOR_LINE_BREAK_TABLE = 0x00000040 - TO_HTML_PARAGRAPH_LINES_INDIVIDUAL = 0x00000001 - TO_HTML_PARAGRAPH_LINE_CONSECUTIVE = 0x00000002 - - _TAG_RE = _re.compile(r"<[^>]*>") - _BR_RE = _re.compile(r"", _re.IGNORECASE) - _P_RE = _re.compile(r"

|]*>", _re.IGNORECASE) - - @staticmethod - def fromHtml(html_text: str, flags: int = 0) -> str: - """将 HTML 转为纯文本""" - text = html_text - text = Html._BR_RE.sub("\n", text) - text = Html._P_RE.sub("\n\n", text) - text = Html._TAG_RE.sub("", text) - text = _html.unescape(text) - return text.strip() - - @staticmethod - def toHtml(text: str, option: int = 0) -> str: - """将纯文本转为 HTML""" - paragraphs = text.split("\n") - html_parts = [] - for p in paragraphs: - escaped = _html.escape(p) - html_parts.append(f"

{escaped}

") - return "".join(html_parts) - - @staticmethod - def fromHtml(html_text: str, image_getter: Any, tag_handler: Any) -> str: - return Html.fromHtml(html_text) - - @staticmethod - def toHtmlWithOptions(text: str, options: int) -> str: - return Html.toHtml(text) - - class ImageGetter: - def getDrawable(self, source: str): - return None - - class TagHandler: - def handleTag(self, opening: bool, tag: str, output: Editable, xml_reader: Any): - pass - - -# ═══════════════════════════════════════════════════════════════ -# CharacterStyle / 各 Span 实现 -# ═══════════════════════════════════════════════════════════════ - -class CharacterStyle(Stub): - """CharacterStyle — 字符样式基类""" - - def underLine(self): - pass - - def updateDrawState(self, tp: TextPaint): - pass - - def getUnderline(self) -> bool: - return False - - @staticmethod - def wrap(cs: "CharacterStyle") -> "CharacterStyle": - return cs - - -class StyleSpan(CharacterStyle): - """StyleSpan — 字体样式""" - - def __init__(self, style: int): - super().__init__() - self._style = style - - def getStyle(self) -> int: - return self._style - - def updateDrawState(self, tp: TextPaint): - tp.style = self._style - - -class TypefaceSpan(CharacterStyle): - """TypefaceSpan — 字体族""" - - def __init__(self, family: str): - super().__init__() - self._family = family - - def getFamily(self) -> str: - return self._family - - def updateDrawState(self, tp: TextPaint): - if self._family == "monospace": - tp.typeface = Typeface.MONOSPACE - elif self._family == "serif": - tp.typeface = Typeface.SERIF - else: - tp.typeface = Typeface.DEFAULT - - -class RelativeSizeSpan(CharacterStyle): - """RelativeSizeSpan — 相对大小""" - - def __init__(self, size: float): - super().__init__() - self._size = size - - def getSizeChange(self) -> float: - return self._size - - def updateDrawState(self, tp: TextPaint): - tp.textSize *= self._size - - -class ForegroundColorSpan(CharacterStyle): - """ForegroundColorSpan — 前景色""" - - def __init__(self, color: int): - super().__init__() - self._color = color - - def getForegroundColor(self) -> int: - return self._color - - def updateDrawState(self, tp: TextPaint): - tp.color = self._color - - -class BackgroundColorSpan(CharacterStyle): - """BackgroundColorSpan — 背景色""" - - def __init__(self, color: int): - super().__init__() - self._color = color - - def getBackgroundColor(self) -> int: - return self._color - - def updateDrawState(self, tp: TextPaint): - tp.bgColor = self._color - tp.bgColorSet = True - - -class UnderlineSpan(CharacterStyle): - """UnderlineSpan — 下划线""" - - def updateDrawState(self, tp: TextPaint): - tp.setUnderlineText(True) - - -class StrikethroughSpan(CharacterStyle): - """StrikethroughSpan — 删除线""" - - def updateDrawState(self, tp: TextPaint): - pass # desktop 无直接等价 - - -class SubscriptSpan(CharacterStyle): - """SubscriptSpan — 下标""" - - def updateDrawState(self, tp: TextPaint): - tp.baselineShift = -tp.textSize / 4 - - -class SuperscriptSpan(CharacterStyle): - """SuperscriptSpan — 上标""" - - def updateDrawState(self, tp: TextPaint): - tp.baselineShift = tp.textSize / 2 - - -class ImageSpan(CharacterStyle): - """ImageSpan — 图片""" - - ALIGN_BOTTOM = 0 - ALIGN_BASELINE = 1 - ALIGN_CENTER = 2 - ALIGN_TOP = 3 - - def __init__(self, drawable: Any, source: str = "", align: int = ALIGN_BOTTOM): - super().__init__() - self._drawable = drawable - self._source = source - self._align = align - - def getDrawable(self): - return self._drawable - - def getSource(self) -> str: - return self._source - - def getAlignment(self) -> int: - return self._align - - -class ClickableSpan(CharacterStyle): - """ClickableSpan — 可点击文本""" - - def __init__(self): - super().__init__() - - def onClick(self, view: Any): - pass - - def updateDrawState(self, tp: TextPaint): - tp.color = tp.linkColor - tp.setUnderlineText(True) - - -class URLSpan(ClickableSpan): - """URLSpan — URL 链接""" - - def __init__(self, url: str): - super().__init__() - self._url = url - - def getURL(self) -> str: - return self._url - - def onClick(self, view: Any): - sys.stderr.write(f"[URLSpan] onClick: {self._url}\n") - - def updateDrawState(self, tp: TextPaint): - super().updateDrawState(tp) - - -# ═══════════════════════════════════════════════════════════════ -# NoCopySpan / ParagraphStyle 接口 -# ═══════════════════════════════════════════════════════════════ - -class NoCopySpan(Stub): - """NoCopySpan — 标记不在拷贝时复制的 span""" - pass - -class NoCopySpanConcise(NoCopySpan): - """NoCopySpan.Concrete — 具体实现""" - pass - - -class ParagraphStyle(Stub): - """ParagraphStyle — 段落样式接口""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# Ellipsize / TruncateAt 枚举 -# ═══════════════════════════════════════════════════════════════ - -class Ellipsize(Stub): - """Ellipsize 常量""" - END = "END" - MIDDLE = "MIDDLE" - MARQUEE = "MARQUEE" - START = "START" - NONE = "NONE" - - -class TruncateAt(Stub): - """TruncateAt 常量""" - END = "END" - MIDDLE = "MIDDLE" - MARQUEE = "MARQUEE" - START = "START" - - -# ═══════════════════════════════════════════════════════════════ -# Align -# ═══════════════════════════════════════════════════════════════ - -class Align(Stub): - """Layout.Alignment 枚举""" - ALIGN_NORMAL = 0 - ALIGN_OPPOSITE = 1 - ALIGN_CENTER = 2 - - -# ═══════════════════════════════════════════════════════════════ -# TextDirectionHeuristic / TextDirectionHeuristics -# ═══════════════════════════════════════════════════════════════ - -class TextDirectionHeuristic(Stub): - """TextDirectionHeuristic — 文本方向启发式""" - pass - - -class TextDirectionHeuristics(Stub): - """TextDirectionHeuristics — 文本方向启发式常量""" - - FIRSTSTRONG_LTR = TextDirectionHeuristic() - FIRSTSTRONG_RTL = TextDirectionHeuristic() - ANYRTL_LTR = TextDirectionHeuristic() - LTR = TextDirectionHeuristic() - RTL = TextDirectionHeuristic() - LOCALE = TextDirectionHeuristic() - - -# ═══════════════════════════════════════════════════════════════ -# LoginFilter -# ═══════════════════════════════════════════════════════════════ - -class LoginFilter(InputFilter): - """LoginFilter — 登录名输入过滤器""" - - def __init__(self, append_ok: bool = True): - super().__init__() - self._append_ok = append_ok - - def isAllowed(self, c: str) -> bool: - return c.isalnum() or c in "@._-" - - def filter(self, source: str, start: int, end: int, dest: Spanned, - dstart: int, dend: int) -> Optional[str]: - filtered = [] - for c in source[start:end]: - if self.isAllowed(c): - filtered.append(c) - elif self._append_ok: - pass - result = "".join(filtered) - return result if result != source else None - - class UsernameFilterGMail(InputFilter): - def filter(self, source, start, end, dest, dstart, dend): - result = [] - for c in source[start:end]: - if c.isalnum() or c in "@._-": - result.append(c) - return "".join(result) or None - - class PasswordFilterGMail(InputFilter): - def filter(self, source, start, end, dest, dstart, dend): - return None - - -# ═══════════════════════════════════════════════════════════════ -# Method / MovementMethod -# ═══════════════════════════════════════════════════════════════ - -class Method(Stub): - """Method — 输入法接口""" - pass - - -class MovementMethod(Stub): - """MovementMethod — 移动方式接口""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# Layout / StaticLayout / DynamicLayout / BoringLayout -# ═══════════════════════════════════════════════════════════════ - -class Layout(Stub): - """Layout — 布局基类""" - - Alignment = Align - - BREAK_STRATEGY_SIMPLE = 0 - BREAK_STRATEGY_HIGH_QUALITY = 1 - BREAK_STRATEGY_BALANCED = 2 - - HYPHENATION_FREQUENCY_NONE = 0 - HYPHENATION_FREQUENCY_NORMAL = 1 - HYPHENATION_FREQUENCY_FULL = 2 - - JUSTIFICATION_MODE_NONE = 0 - JUSTIFICATION_MODE_INTER_WORD = 1 - - def __init__(self, text: str, paint: TextPaint, width: int, alignment: int, - spacing_mult: float, spacing_add: float): - super().__init__() - self._text = text - self._paint = paint - self._width = width - self._alignment = alignment - self._spacing_mult = spacing_mult - self._spacing_add = spacing_add - self._line_count = max(1, len(text) // max(1, width // 10) + 1) - - def getLineCount(self) -> int: - return self._line_count - - def getLineBounds(self, line: int, bounds: Optional[Any]) -> int: - line_height = int(self._paint.textSize * self._spacing_mult + self._spacing_add) - return line * line_height - - def getPrimaryHorizontal(self, offset: int) -> float: - line = 0 - return (offset % max(1, self._width)) * self._paint.measureText("x") - - def getSecondaryHorizontal(self, offset: int) -> float: - return 0.0 - - def getLineTop(self, line: int) -> int: - line_height = int(self._paint.textSize * self._spacing_mult + self._spacing_add) - return line * line_height - - def getLineBottom(self, line: int) -> int: - line_height = int(self._paint.textSize * self._spacing_mult + self._spacing_add) - return (line + 1) * line_height - - def getLineStart(self, line: int) -> int: - chars_per_line = max(1, self._width // max(1, int(self._paint.measureText("x")))) - return line * chars_per_line - - def getLineEnd(self, line: int) -> int: - chars_per_line = max(1, self._width // max(1, int(self._paint.measureText("x")))) - return min((line + 1) * chars_per_line, len(self._text)) - - def getText(self) -> str: - return self._text - - def getPaint(self) -> TextPaint: - return self._paint - - def getWidth(self) -> int: - return self._width - - def getAlignment(self) -> int: - return self._alignment - - def getSpacingMultiplier(self) -> float: - return self._spacing_mult - - def getSpacingAdd(self) -> float: - return self._spacing_add - - def isRtlCharAt(self, offset: int) -> bool: - return False - - def getParagraphDirection(self, line: int) -> int: - return 1 - - def draw(self, canvas: Any): - pass - - -class StaticLayout(Layout): - """StaticLayout — 静态布局""" - - def __init__(self, source: str, paint: TextPaint, width: int, alignment: int, - spacing_mult: float, spacing_add: float, include_pad: bool): - super().__init__(source, paint, width, alignment, spacing_mult, spacing_add) - self._include_pad = include_pad - - -class DynamicLayout(Layout): - """DynamicLayout — 动态布局""" - - def __init__(self, base: Any, paint: TextPaint, width: int, alignment: int, - spacing_mult: float, spacing_add: float, include_pad: bool, - ellipsize: Any = None, ellipsize_width: int = 0): - super().__init__(str(base), paint, width, alignment, spacing_mult, spacing_add) - self._include_pad = include_pad - - -class BoringLayout(Layout): - """BoringLayout — 单行布局""" - - def __init__(self, source: str, paint: TextPaint, width: int, alignment: int, - spacing_mult: float, spacing_add: float, include_pad: bool, - ellipsize: Any = None, ellipsize_width: int = 0): - super().__init__(source, paint, width, alignment, spacing_mult, spacing_add) - self._include_pad = include_pad - self._line_count = 1 if source else 0 - - class Metrics(Stub): - """Metrics for BoringLayout""" - width = 0 - height = 0 - - @staticmethod - def isBoring(text: str, paint: TextPaint) -> Optional["BoringLayout.Metrics"]: - m = BoringLayout.Metrics() - m.width = int(paint.measureText(text)) - m.height = int(paint.textSize) - return m if text else None - - @staticmethod - def make(source: str, paint: TextPaint, width: int, alignment: int, - spacing_mult: float, spacing_add: float, metrics: "BoringLayout.Metrics", - include_pad: bool) -> "BoringLayout": - return BoringLayout(source, paint, width, alignment, spacing_mult, spacing_add, include_pad) - - -class ClipRectangle(Stub): - """ClipRectangle — 裁剪矩形""" - def __init__(self, left: int = 0, top: int = 0, right: int = 0, bottom: int = 0): - super().__init__() - self.left = left - self.top = top - self.right = right - self.bottom = bottom - - -# ═══════════════════════════════════════════════════════════════ -# Spannable (接口别名) -# ═══════════════════════════════════════════════════════════════ - -class Spannable(Spanned): - """Spannable — 可标记接口""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "TextUtils", - "SpannableString", - "SpannableStringBuilder", - "Spannable", - "Editable", - "Spanned", - "TextWatcher", - "InputFilter", - "Html", - "Layout", - "StaticLayout", - "DynamicLayout", - "BoringLayout", - "ClipRectangle", - "TextPaint", - "TextDirectionHeuristic", - "TextDirectionHeuristics", - "Align", - "Ellipsize", - "TruncateAt", - "LoginFilter", - "Method", - "MovementMethod", - "NoCopySpan", - "ParagraphStyle", - "CharacterStyle", - "StyleSpan", - "TypefaceSpan", - "RelativeSizeSpan", - "ForegroundColorSpan", - "BackgroundColorSpan", - "UnderlineSpan", - "StrikethroughSpan", - "SubscriptSpan", - "SuperscriptSpan", - "ImageSpan", - "ClickableSpan", - "URLSpan", - "Typeface", -] \ No newline at end of file diff --git a/android_pyapi/android/text_format.py b/android_pyapi/android/text_format.py deleted file mode 100644 index 37bbf43..0000000 --- a/android_pyapi/android/text_format.py +++ /dev/null @@ -1,456 +0,0 @@ -""" -Android text.format 包 — DateFormat, Time, Formatter, DateUtils 等 -方法签名完全对齐 Android SDK;使用 Python datetime 模块实现真实逻辑 -""" - -from __future__ import annotations - -import datetime -import math -import re -import time as _time -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "DateFormat", - "Time", - "Formatter", - "DateUtils", -] - - -# ═══════════════════════════════════════════════════════════════ -# DateFormat -# ═══════════════════════════════════════════════════════════════ - -class DateFormat(Stub): - """DateFormat — 日期格式化工具 (使用 Python datetime 实现)""" - - DAY_OF_WEEK = 1 - DAY_OF_MONTH = 2 - DAY_OF_YEAR = 3 - HOUR_OF_DAY = 4 - MINUTE = 5 - SECOND = 6 - MONTH = 7 - YEAR = 8 - - _PATTERNS = { - "short": "%m/%d/%y", - "medium": "%b %d, %Y", - "long": "%B %d, %Y", - "full": "%A, %B %d, %Y", - "time_short": "%I:%M %p", - "time_medium": "%I:%M:%S %p", - "time_long": "%I:%M:%S %p %Z", - } - - def __init__(self): - super().__init__() - - @staticmethod - def getDateFormat(context: Any) -> "DateFormat": - """获取日期格式""" - return DateFormat() - - @staticmethod - def getTimeFormat(context: Any) -> "DateFormat": - """获取时间格式""" - return DateFormat() - - @staticmethod - def getDateTimeFormat(context: Any) -> "DateFormat": - """获取日期时间格式""" - return DateFormat() - - @staticmethod - def is24HourFormat(context: Any) -> bool: - """检查是否使用 24 小时制""" - return False - - @staticmethod - def format(date_str: str, in_format: str = "%Y-%m-%d", - out_format: str = "%b %d, %Y") -> str: - """格式化日期字符串""" - try: - dt = datetime.datetime.strptime(date_str, in_format) - return dt.strftime(out_format) - except (ValueError, TypeError): - return date_str - - @staticmethod - def formatDateRange(context: Any, start_millis: int, end_millis: int, - flags: int = 0) -> str: - """格式化日期范围""" - start_dt = datetime.datetime.fromtimestamp(start_millis / 1000.0) - end_dt = datetime.datetime.fromtimestamp(end_millis / 1000.0) - if start_dt.date() == end_dt.date(): - return start_dt.strftime("%b %d, %Y") - return f"{start_dt.strftime('%b %d')} - {end_dt.strftime('%b %d, %Y')}" - - @staticmethod - def getBestDateTimePattern(pattern: str, locale: Any) -> str: - """获取最佳日期时间模式""" - return pattern - - @staticmethod - def getLongDateFormat(context: Any) -> "DateFormat": - return DateFormat() - - @staticmethod - def getMediumDateFormat(context: Any) -> "DateFormat": - return DateFormat() - - @staticmethod - def getShortDateFormat(context: Any) -> "DateFormat": - return DateFormat() - - @staticmethod - def getTimePeriodString(time_period: int) -> str: - """获取时间段字符串""" - periods = { - 0: "morning", - 1: "afternoon", - 2: "evening", - 3: "night", - } - return periods.get(time_period, "unknown") - - @staticmethod - def formatElapsedTime(elapsed_millis: int) -> str: - """格式化经过的时间""" - seconds = elapsed_millis // 1000 - minutes = seconds // 60 - hours = minutes // 60 - seconds = seconds % 60 - minutes = minutes % 60 - if hours > 0: - return f"{hours}:{minutes:02d}:{seconds:02d}" - return f"{minutes}:{seconds:02d}" - - -# ═══════════════════════════════════════════════════════════════ -# Time -# ═══════════════════════════════════════════════════════════════ - -class Time(Stub): - """Time — 时间类 (使用 Python datetime 实现)""" - - TIMEZONE_UTC = "UTC" - EPOCH_JULIAN_DAY = 2440588 - MONDAY = 2 - SUNDAY = 1 - JANUARY = 0 - DECEMBER = 11 - - def __init__(self, timezone: str = "UTC"): - super().__init__() - self._dt = datetime.datetime.utcnow() - self.timezone = timezone - self.allDay = False - self.julianDay = self._to_julian(self._dt) - self.weekNumber = 0 - self.gmtoff = 0 - self.hour = self._dt.hour - self.minute = self._dt.minute - self.second = self._dt.second - self.weekDay = self._dt.isoweekday() - self.yearDay = self._dt.timetuple().tm_yday - self.month = self._dt.month - 1 # 0-based - self.monthDay = self._dt.day - self.year = self._dt.year - self.isDst = 0 - - @staticmethod - def _to_julian(dt: datetime.datetime) -> int: - """将 datetime 转换为儒略日""" - timestamp = dt.timestamp() - return int(Time.EPOCH_JULIAN_DAY + timestamp / 86400.0) - - @staticmethod - def _from_julian(jd: int) -> datetime.datetime: - """将儒略日转换为 datetime""" - timestamp = (jd - Time.EPOCH_JULIAN_DAY) * 86400.0 - return datetime.datetime.fromtimestamp(timestamp) - - def setToNow(self) -> None: - """设置当前时间""" - self._dt = datetime.datetime.now() - self._update_fields() - - def set(self, second: int, minute: int, hour: int, monthDay: int, - month: int, year: int) -> None: - """设置时间""" - self._dt = datetime.datetime(year, month + 1, monthDay, hour, minute, second) - self._update_fields() - - def setTimezone(self, timezone: str) -> None: - """设置时区""" - self.timezone = timezone - - def normalize(self, ignoreDst: bool = False) -> int: - """标准化时间,返回毫秒""" - self._update_fields() - return int(self._dt.timestamp() * 1000) - - def format(self, pattern: str = "%Y-%m-%d %H:%M:%S") -> str: - """格式化输出""" - return self._dt.strftime(pattern) - - def toString(self) -> str: - return self._dt.isoformat() - - def parse(self, s: str) -> bool: - """解析时间字符串""" - formats = [ - "%Y-%m-%d %H:%M:%S", - "%Y-%m-%dT%H:%M:%S", - "%Y-%m-%d", - "%m/%d/%Y %H:%M:%S", - "%m/%d/%Y", - ] - for fmt in formats: - try: - self._dt = datetime.datetime.strptime(s, fmt) - self._update_fields() - return True - except ValueError: - continue - return False - - def parse3339(self, s: str) -> bool: - """解析 RFC 3339 时间字符串""" - try: - self._dt = datetime.datetime.fromisoformat(s.replace("Z", "+00:00")) - self._update_fields() - return True - except (ValueError, TypeError): - return False - - def switchTimezone(self, timezone: str) -> None: - """切换时区""" - self.timezone = timezone - - def toMillis(self) -> int: - """转换为毫秒""" - return int(self._dt.timestamp() * 1000) - - def setJulianDay(self, julianDay: int) -> None: - """设置儒略日""" - self.julianDay = julianDay - self._dt = self._from_julian(julianDay) - self._update_fields() - - @staticmethod - def getJulianDay(millis: int, gmtoff: int) -> int: - """从毫秒获取儒略日""" - dt = datetime.datetime.fromtimestamp(millis / 1000.0) - return Time._to_julian(dt) - - def getWeekNumber(self) -> int: - """获取周数""" - return self._dt.isocalendar()[1] - - def _update_fields(self) -> None: - """更新所有字段""" - self.hour = self._dt.hour - self.minute = self._dt.minute - self.second = self._dt.second - self.weekDay = self._dt.isoweekday() - self.yearDay = self._dt.timetuple().tm_yday - self.month = self._dt.month - 1 - self.monthDay = self._dt.day - self.year = self._dt.year - self.julianDay = self._to_julian(self._dt) - self.weekNumber = self.getWeekNumber() - - -# ═══════════════════════════════════════════════════════════════ -# Formatter -# ═══════════════════════════════════════════════════════════════ - -class Formatter(Stub): - """Formatter — 格式化工具 (使用真实实现)""" - - def __init__(self): - super().__init__() - - @staticmethod - def formatFileSize(context: Any, size_bytes: int) -> str: - """格式化文件大小""" - if size_bytes < 0: - return "0 B" - units = ["B", "KB", "MB", "GB", "TB"] - unit_idx = 0 - size_f = float(size_bytes) - while size_f >= 1024 and unit_idx < len(units) - 1: - size_f /= 1024.0 - unit_idx += 1 - if unit_idx == 0: - return f"{int(size_f)} {units[unit_idx]}" - return f"{size_f:.1f} {units[unit_idx]}" - - @staticmethod - def formatShortFileSize(context: Any, size_bytes: int) -> str: - """格式化短文件大小""" - return Formatter.formatFileSize(context, size_bytes) - - @staticmethod - def formatIpAddress(addr: int) -> str: - """格式化 IP 地址""" - return f"{(addr >> 24) & 0xFF}.{(addr >> 16) & 0xFF}.{(addr >> 8) & 0xFF}.{addr & 0xFF}" - - @staticmethod - def formatDateFromDB(context: Any, date_str: str) -> str: - """格式化数据库日期""" - return DateFormat.format(date_str, "%Y-%m-%d", "%b %d, %Y") - - @staticmethod - def formatShortDateFromDB(context: Any, date_str: str) -> str: - """格式化短数据库日期""" - return DateFormat.format(date_str, "%Y-%m-%d", "%m/%d/%y") - - -# ═══════════════════════════════════════════════════════════════ -# DateUtils -# ═══════════════════════════════════════════════════════════════ - -class DateUtils(Stub): - """DateUtils — 日期工具类 (使用 Python datetime 实现)""" - - DAY_IN_MILLIS = 86400000 - HOUR_IN_MILLIS = 3600000 - MINUTE_IN_MILLIS = 60000 - SECOND_IN_MILLIS = 1000 - WEEK_IN_MILLIS = 604800000 - YEAR_IN_MILLIS = 31536000000 - - FORMAT_SHOW_TIME = 0x00001 - FORMAT_SHOW_DATE = 0x00002 - FORMAT_SHOW_YEAR = 0x00004 - FORMAT_NO_MIDNIGHT = 0x00008 - FORMAT_SHOW_WEEKDAY = 0x00010 - FORMAT_UTC = 0x00020 - FORMAT_ABBREV_MONTH = 0x00040 - FORMAT_ABBREV_WEEKDAY = 0x00080 - FORMAT_NO_NOON = 0x00100 - FORMAT_CAP_AMPM = 0x00200 - FORMAT_NO_YEAR = 0x00400 - FORMAT_NUMERIC_DATE = 0x00800 - FORMAT_ABBREV_ALL = 0x01000 - FORMAT_12HOUR = 0x02000 - FORMAT_24HOUR = 0x04000 - - _WEEKDAYS = ["", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] - _WEEKDAYS_ABBREV = ["", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] - _MONTHS = ["January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December"] - _MONTHS_ABBREV = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] - - def __init__(self): - super().__init__() - - @staticmethod - def formatDateTime(context: Any, millis: int, flags: int) -> str: - """格式化日期时间""" - dt = datetime.datetime.fromtimestamp(millis / 1000.0) - parts = [] - if flags & DateUtils.FORMAT_SHOW_WEEKDAY: - abbrev = bool(flags & DateUtils.FORMAT_ABBREV_WEEKDAY) - wd = DateUtils._WEEKDAYS_ABBREV[dt.isoweekday()] if abbrev else DateUtils._WEEKDAYS[dt.isoweekday()] - parts.append(wd) - if flags & DateUtils.FORMAT_SHOW_DATE: - abbrev = bool(flags & DateUtils.FORMAT_ABBREV_MONTH) - if flags & DateUtils.FORMAT_NUMERIC_DATE: - parts.append(f"{dt.month}/{dt.day}") - else: - mn = DateUtils._MONTHS_ABBREV[dt.month - 1] if abbrev else DateUtils._MONTHS[dt.month - 1] - parts.append(f"{mn} {dt.day}") - if flags & DateUtils.FORMAT_SHOW_YEAR: - parts.append(str(dt.year)) - if flags & DateUtils.FORMAT_SHOW_TIME: - if flags & DateUtils.FORMAT_24HOUR: - parts.append(dt.strftime("%H:%M")) - else: - parts.append(dt.strftime("%I:%M %p")) - return " ".join(parts) - - @staticmethod - def formatDateRange(context: Any, start_millis: int, end_millis: int, - flags: int = 0) -> str: - """格式化日期范围""" - return DateFormat.formatDateRange(context, start_millis, end_millis, flags) - - @staticmethod - def formatElapsedTime(now: int, past: int) -> str: - """格式化经过的时间""" - diff = now - past - return DateFormat.formatElapsedTime(diff) - - @staticmethod - def getRelativeTimeSpanString(time_millis: int, now_millis: int, - min_resolution: int = MINUTE_IN_MILLIS, - flags: int = 0) -> str: - """获取相对时间字符串""" - diff = now_millis - time_millis - if diff < 0: - return "just now" - if diff < DateUtils.MINUTE_IN_MILLIS: - return "just now" - if diff < DateUtils.HOUR_IN_MILLIS: - minutes = int(diff / DateUtils.MINUTE_IN_MILLIS) - return f"{minutes} minute{'s' if minutes > 1 else ''} ago" - if diff < DateUtils.DAY_IN_MILLIS: - hours = int(diff / DateUtils.HOUR_IN_MILLIS) - return f"{hours} hour{'s' if hours > 1 else ''} ago" - if diff < DateUtils.WEEK_IN_MILLIS: - days = int(diff / DateUtils.DAY_IN_MILLIS) - return f"{days} day{'s' if days > 1 else ''} ago" - if diff < DateUtils.YEAR_IN_MILLIS: - weeks = int(diff / DateUtils.WEEK_IN_MILLIS) - return f"{weeks} week{'s' if weeks > 1 else ''} ago" - years = int(diff / DateUtils.YEAR_IN_MILLIS) - return f"{years} year{'s' if years > 1 else ''} ago" - - @staticmethod - def getRelativeDateTimeString(time_millis: int, now_millis: int, - min_resolution: int, transition_resolution: int, - flags: int) -> str: - """获取相对日期时间字符串""" - return DateUtils.getRelativeTimeSpanString(time_millis, now_millis, min_resolution, flags) - - @staticmethod - def getDayOfWeekString(day_of_week: int, flags: int) -> str: - """获取星期字符串""" - abbrev = bool(flags & DateUtils.FORMAT_ABBREV_WEEKDAY) - if 1 <= day_of_week <= 7: - return DateUtils._WEEKDAYS_ABBREV[day_of_week] if abbrev else DateUtils._WEEKDAYS[day_of_week] - return "" - - @staticmethod - def getMonthString(month: int, flags: int) -> str: - """获取月份字符串""" - abbrev = bool(flags & DateUtils.FORMAT_ABBREV_MONTH) - if 0 <= month <= 11: - return DateUtils._MONTHS_ABBREV[month] if abbrev else DateUtils._MONTHS[month] - return "" - - @staticmethod - def isToday(millis: int) -> bool: - """检查是否是今天""" - today = datetime.date.today() - dt = datetime.datetime.fromtimestamp(millis / 1000.0) - return dt.date() == today - - @staticmethod - def sameDay(millis1: int, millis2: int) -> bool: - """检查是否是同一天""" - dt1 = datetime.datetime.fromtimestamp(millis1 / 1000.0) - dt2 = datetime.datetime.fromtimestamp(millis2 / 1000.0) - return dt1.date() == dt2.date() \ No newline at end of file diff --git a/android_pyapi/android/text_method.py b/android_pyapi/android/text_method.py deleted file mode 100644 index e262474..0000000 --- a/android_pyapi/android/text_method.py +++ /dev/null @@ -1,676 +0,0 @@ -""" -Android text.method 包 — KeyListener, MovementMethod, TransformationMethod 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import sys -import time -from typing import Any, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "KeyListener", - "MetaKeyKeyListener", - "NumberKeyListener", - "DigitsKeyListener", - "TextKeyListener", - "MovementMethod", - "ArrowKeyMovementMethod", - "LinkMovementMethod", - "Touch", - "BaseMovementMethod", - "ScrollingMovementMethod", - "WordIterator", - "PasswordTransformationMethod", - "TransformationMethod", - "TransformationMethod2", - "SingleLineTransformationMethod", - "HideReturnsTransformationMethod", - "TimeDisplayTruncationMethod", - "DateTimeKeyListener", - "DateKeyListener", - "TimeKeyListener", -] - - -# ═══════════════════════════════════════════════════════════════ -# KeyListener -# ═══════════════════════════════════════════════════════════════ - -class KeyListener(Stub): - """KeyListener — 按键监听器接口 (桌面存根)""" - - META_SHIFT_MASK = 0x1 - META_ALT_MASK = 0x2 - META_SYM_MASK = 0x4 - META_CTRL_MASK = 0x1000 - META_META_MASK = 0x10000 - META_CAPS_LOCK = 0x100000 - META_ALT_LOCK = 0x200000 - META_SYM_LOCK = 0x400000 - META_SELECTING = 0x800000 - META_SHIFTING = 0x1000000 - - def __init__(self): - super().__init__() - self._input_type = 0 - self._meta_state = 0 - - def clearMetaKeyState(self, view: Any, content: Any, states: int) -> None: - """清除元键状态""" - self._meta_state = self._meta_state & ~states - - def getInputType(self) -> int: - """获取输入类型""" - return self._input_type - - def onKeyDown(self, view: Any, content: Any, keyCode: int, event: Any) -> bool: - """按键按下事件""" - return False - - def onKeyUp(self, view: Any, content: Any, keyCode: int, event: Any) -> bool: - """按键释放事件""" - return False - - def onKeyOther(self, view: Any, content: Any, event: Any) -> bool: - """其他按键事件""" - return False - - @staticmethod - def metaStateHasNoModifiers(metaState: int) -> bool: - """检查元状态是否没有修饰键""" - return (metaState & 0x0000FFFF) == 0 - - @staticmethod - def metaStateHasModifiers(metaState: int, modifiers: int) -> bool: - """检查元状态是否有指定修饰键""" - return (metaState & modifiers) != 0 - - @staticmethod - def metaStateFilterDirectionLocked(metaState: int) -> int: - """过滤方向锁定""" - return metaState & ~(KeyListener.META_CAPS_LOCK | KeyListener.META_ALT_LOCK | KeyListener.META_SYM_LOCK) - - def __repr__(self) -> str: - return f"KeyListener(inputType={self._input_type})" - - -# ═══════════════════════════════════════════════════════════════ -# MetaKeyKeyListener -# ═══════════════════════════════════════════════════════════════ - -class MetaKeyKeyListener(KeyListener): - """MetaKeyKeyListener — 元键按键监听器 (桌面存根)""" - - def __init__(self): - super().__init__() - self._locked_meta: Dict[int, int] = {} - - def clearMetaKeyState(self, view: Any, content: Any, states: int) -> None: - """清除元键状态""" - self._meta_state = self._meta_state & ~states - for state_bit in [KeyListener.META_CAPS_LOCK, KeyListener.META_ALT_LOCK, KeyListener.META_SYM_LOCK]: - if states & state_bit: - self._locked_meta.pop(state_bit, None) - - @staticmethod - def resetMetaState(content: Any) -> None: - """重置元状态""" - if hasattr(content, '_meta_state'): - content._meta_state = 0 - - def onKeyDown(self, view: Any, content: Any, keyCode: int, event: Any) -> bool: - """按键按下事件处理""" - return super().onKeyDown(view, content, keyCode, event) - - def onKeyUp(self, view: Any, content: Any, keyCode: int, event: Any) -> bool: - """按键释放事件处理""" - return super().onKeyUp(view, content, keyCode, event) - - @staticmethod - def adjustMetaAfterKeypress(content: Any) -> None: - """按键后调整元状态""" - pass - - def handleKeyDown(self, view: Any, content: Any, keyCode: int, event: Any) -> bool: - """处理按键按下 (内部)""" - return self.onKeyDown(view, content, keyCode, event) - - def handleKeyUp(self, view: Any, content: Any, keyCode: int, event: Any) -> bool: - """处理按键释放 (内部)""" - return self.onKeyUp(view, content, keyCode, event) - - @staticmethod - def getMetaState(content: Any) -> int: - """获取元状态""" - return getattr(content, '_meta_state', 0) - - @staticmethod - def getMetaState_ex(content: Any, meta: int) -> bool: - """检查特定元状态""" - state = getattr(content, '_meta_state', 0) - return (state & meta) != 0 - - @staticmethod - def isMetaTracker(content: Any) -> bool: - """检查是否是元追踪器""" - return hasattr(content, '_meta_state') - - @staticmethod - def isSelectingMetaTracker(content: Any) -> bool: - """检查是否是选择元追踪器""" - return hasattr(content, '_meta_state') and (getattr(content, '_meta_state', 0) & KeyListener.META_SELECTING) != 0 - - @staticmethod - def peekMetaState(content: Any) -> int: - """窥视元状态""" - return getattr(content, '_meta_state', 0) - - def resetLockedMeta(self, content: Any) -> None: - """重置锁定元键""" - self._locked_meta.clear() - - def clearLockedMeta(self, content: Any) -> None: - """清除锁定元键""" - self._locked_meta.clear() - - @staticmethod - def releaseAllMetaKeys(content: Any) -> None: - """释放所有元键""" - if hasattr(content, '_meta_state'): - content._meta_state = 0 - - def toString(self) -> str: - return f"MetaKeyKeyListener(metaState={self._meta_state}, locked={self._locked_meta})" - - -# ═══════════════════════════════════════════════════════════════ -# NumberKeyListener -# ═══════════════════════════════════════════════════════════════ - -class NumberKeyListener(MetaKeyKeyListener): - """NumberKeyListener — 数字按键监听器 (桌面存根)""" - - _ACCEPTED_CHARS: str = "" - - def __init__(self): - super().__init__() - self._input_type = 0x0002 # TYPE_CLASS_NUMBER - - def getAcceptedChars(self) -> List[str]: - """获取接受的字符列表""" - return list(self._ACCEPTED_CHARS) - - def filter(self, source: str, start: int, end: int, dest: str, dstart: int, dend: int) -> str: - """过滤输入,只保留接受的字符""" - result = "" - for i in range(start, end): - ch = source[i] if i < len(source) else '' - if ch in self._ACCEPTED_CHARS: - result += ch - return result if result != source[start:end] else "" - - def lookup(self, key: int, event: Any) -> Optional[str]: - """查找按键映射的字符""" - key_map = { - 7: '0', 8: '1', 9: '2', 10: '3', 11: '4', - 12: '5', 13: '6', 14: '7', 15: '8', 16: '9', - } - return key_map.get(key) - - def onKeyDown(self, view: Any, content: Any, keyCode: int, event: Any) -> bool: - """按键按下事件""" - char = self.lookup(keyCode, event) - if char and char in self._ACCEPTED_CHARS: - return True - return False - - -# ═══════════════════════════════════════════════════════════════ -# DigitsKeyListener -# ═══════════════════════════════════════════════════════════════ - -class DigitsKeyListener(NumberKeyListener): - """DigitsKeyListener — 数字按键监听器 (桌面存根)""" - - _instances: Dict[str, "DigitsKeyListener"] = {} - - def __init__(self, accepted: str = "0123456789"): - super().__init__() - self._ACCEPTED_CHARS = accepted - self._input_type = 0x0002 - - @staticmethod - def getInstance(locale: Any = None, sign: bool = False, decimal: bool = False) -> "DigitsKeyListener": - """获取实例""" - key = f"{locale}_{sign}_{decimal}" - if key not in DigitsKeyListener._instances: - accepted = "0123456789" - if sign: - accepted += "-" - if decimal: - accepted += "." - DigitsKeyListener._instances[key] = DigitsKeyListener(accepted) - return DigitsKeyListener._instances[key] - - def getAcceptedChars(self) -> List[str]: - return list(self._ACCEPTED_CHARS) - - def filter(self, source: str, start: int, end: int, dest: str, dstart: int, dend: int) -> str: - return super().filter(source, start, end, dest, dstart, dend) - - -# ═══════════════════════════════════════════════════════════════ -# TextKeyListener -# ═══════════════════════════════════════════════════════════════ - -class TextKeyListener(MetaKeyKeyListener): - """TextKeyListener — 文本按键监听器 (桌面存根)""" - - CAP_MODE_CHARACTERS = 0x1 - CAP_MODE_WORDS = 0x2 - CAP_MODE_SENTENCES = 0x4 - - _instance: Optional["TextKeyListener"] = None - - def __init__(self): - super().__init__() - self._input_type = 0x0001 # TYPE_CLASS_TEXT - self._auto_cap_mode = 0 - - @staticmethod - def getInstance() -> "TextKeyListener": - """获取单例实例""" - if TextKeyListener._instance is None: - TextKeyListener._instance = TextKeyListener() - return TextKeyListener._instance - - def clear(self, view: Any, content: Any) -> None: - """清除内容""" - if hasattr(content, 'clear'): - content.clear() - - def release(self, view: Any) -> None: - """释放资源""" - pass - - def onKeyDown(self, view: Any, content: Any, keyCode: int, event: Any) -> bool: - return True - - def onKeyUp(self, view: Any, content: Any, keyCode: int, event: Any) -> bool: - return True - - def toString(self) -> str: - return f"TextKeyListener(capMode={self._auto_cap_mode})" - - -# ═══════════════════════════════════════════════════════════════ -# MovementMethod -# ═══════════════════════════════════════════════════════════════ - -class MovementMethod(Stub): - """MovementMethod — 移动方法接口 (桌面存根)""" - - def __init__(self): - super().__init__() - self._movement_meta_state = 0 - - def initialize(self, view: Any, content: Any) -> None: - """初始化""" - pass - - def onKeyDown(self, view: Any, content: Any, keyCode: int, event: Any) -> bool: - """按键按下""" - return False - - def onKeyUp(self, view: Any, content: Any, keyCode: int, event: Any) -> bool: - """按键释放""" - return False - - def onTakeFocus(self, view: Any, content: Any, direction: int) -> bool: - """获取焦点""" - return False - - def canSelectArbitrarily(self) -> bool: - """能否任意选择""" - return False - - def dispatchTouchEvent(self, view: Any, content: Any, event: Any) -> bool: - """分发触摸事件""" - return False - - def onGenericMotionEvent(self, view: Any, content: Any, event: Any) -> bool: - """通用运动事件""" - return False - - def getMovementMetaState(self) -> int: - """获取移动元状态""" - return self._movement_meta_state - - def setMovementMetaState(self, state: int) -> None: - """设置移动元状态""" - self._movement_meta_state = state - - def toString(self) -> str: - return f"{self.__class__.__name__}@MovementMethod" - - -# ═══════════════════════════════════════════════════════════════ -# ArrowKeyMovementMethod -# ═══════════════════════════════════════════════════════════════ - -class ArrowKeyMovementMethod(MovementMethod): - """ArrowKeyMovementMethod — 箭头键移动方法 (桌面存根)""" - - _instance: Optional["ArrowKeyMovementMethod"] = None - - def __init__(self): - super().__init__() - - @staticmethod - def getInstance() -> "ArrowKeyMovementMethod": - if ArrowKeyMovementMethod._instance is None: - ArrowKeyMovementMethod._instance = ArrowKeyMovementMethod() - return ArrowKeyMovementMethod._instance - - def onKeyDown(self, view: Any, content: Any, keyCode: int, event: Any) -> bool: - return True - - def canSelectArbitrarily(self) -> bool: - return True - - -# ═══════════════════════════════════════════════════════════════ -# LinkMovementMethod -# ═══════════════════════════════════════════════════════════════ - -class LinkMovementMethod(MovementMethod): - """LinkMovementMethod — 链接移动方法 (桌面存根)""" - - _instance: Optional["LinkMovementMethod"] = None - - def __init__(self): - super().__init__() - - @staticmethod - def getInstance() -> "LinkMovementMethod": - if LinkMovementMethod._instance is None: - LinkMovementMethod._instance = LinkMovementMethod() - return LinkMovementMethod._instance - - def onKeyDown(self, view: Any, content: Any, keyCode: int, event: Any) -> bool: - return True - - def canSelectArbitrarily(self) -> bool: - return False - - -# ═══════════════════════════════════════════════════════════════ -# Touch -# ═══════════════════════════════════════════════════════════════ - -class Touch(Stub): - """Touch — 触摸工具类 (桌面存根)""" - - def __init__(self): - super().__init__() - self._initial_scroll_x = 0 - self._initial_scroll_y = 0 - - @staticmethod - def getInitialScrollX(widget: Any, content: Any) -> int: - """获取初始滚动 X""" - return getattr(widget, '_initial_scroll_x', 0) - - @staticmethod - def getInitialScrollY(widget: Any, content: Any) -> int: - """获取初始滚动 Y""" - return getattr(widget, '_initial_scroll_y', 0) - - @staticmethod - def scrollTo(widget: Any, content: Any, x: int, y: int) -> None: - """滚动到指定位置""" - if hasattr(widget, '_scroll_x'): - widget._scroll_x = x - if hasattr(widget, '_scroll_y'): - widget._scroll_y = y - - -# ═══════════════════════════════════════════════════════════════ -# BaseMovementMethod -# ═══════════════════════════════════════════════════════════════ - -class BaseMovementMethod(MovementMethod): - """BaseMovementMethod — 基本移动方法 (桌面存根)""" - - def __init__(self): - super().__init__() - - def onKeyDown(self, view: Any, content: Any, keyCode: int, event: Any) -> bool: - return False - - def canSelectArbitrarily(self) -> bool: - return False - - -# ═══════════════════════════════════════════════════════════════ -# ScrollingMovementMethod -# ═══════════════════════════════════════════════════════════════ - -class ScrollingMovementMethod(BaseMovementMethod): - """ScrollingMovementMethod — 滚动移动方法 (桌面存根)""" - - _instance: Optional["ScrollingMovementMethod"] = None - - def __init__(self): - super().__init__() - - @staticmethod - def getInstance() -> "ScrollingMovementMethod": - if ScrollingMovementMethod._instance is None: - ScrollingMovementMethod._instance = ScrollingMovementMethod() - return ScrollingMovementMethod._instance - - def onKeyDown(self, view: Any, content: Any, keyCode: int, event: Any) -> bool: - return True - - -# ═══════════════════════════════════════════════════════════════ -# WordIterator -# ═══════════════════════════════════════════════════════════════ - -class WordIterator(Stub): - """WordIterator — 单词迭代器 (桌面存根)""" - - def __init__(self, locale: Optional[Any] = None): - super().__init__() - self._locale = locale - self._text = "" - self._cursor = 0 - - def setText(self, text: str, start: int, end: int) -> None: - """设置文本""" - self._text = text[start:end] if end <= len(text) else text[start:] - self._cursor = 0 - - def next(self) -> int: - """下一个单词边界""" - self._cursor += 1 - return self._cursor - - def prev(self) -> int: - """上一个单词边界""" - self._cursor -= 1 - return max(self._cursor, 0) - - def getEnd(self) -> int: - """获取结束位置""" - return len(self._text) - - def getBegin(self) -> int: - """获取开始位置""" - return 0 - - def isBoundary(self, offset: int) -> bool: - """检查是否是单词边界""" - return True - - -# ═══════════════════════════════════════════════════════════════ -# TransformationMethod / PasswordTransformationMethod -# ═══════════════════════════════════════════════════════════════ - -class TransformationMethod(Stub): - """TransformationMethod — 变换方法接口 (桌面存根)""" - - def __init__(self): - super().__init__() - - def getTransformation(self, source: Any, view: Any) -> Any: - """获取变换后的文本""" - return source - - def onFocusChanged(self, view: Any, source: Any, focused: bool, direction: int, previouslyFocused: Any) -> None: - """焦点变化事件""" - pass - - -class TransformationMethod2(TransformationMethod): - """TransformationMethod2 — 带长度变化通知的变换方法""" - - def __init__(self): - super().__init__() - self._length_changes_allowed = True - - def setLengthChangesAllowed(self, allowLengthChanges: bool) -> None: - """设置是否允许长度变化""" - self._length_changes_allowed = allowLengthChanges - - -class PasswordTransformationMethod(TransformationMethod): - """PasswordTransformationMethod — 密码变换方法 (桌面存根)""" - - _instance: Optional["PasswordTransformationMethod"] = None - - def __init__(self): - super().__init__() - - @staticmethod - def getInstance() -> "PasswordTransformationMethod": - if PasswordTransformationMethod._instance is None: - PasswordTransformationMethod._instance = PasswordTransformationMethod() - return PasswordTransformationMethod._instance - - def getTransformation(self, source: Any, view: Any) -> Any: - """将文本转换为掩码字符""" - text = str(source) if source else "" - return "\u2022" * len(text) # bullet character - - def onFocusChanged(self, view: Any, source: Any, focused: bool, direction: int, previouslyFocused: Any) -> None: - pass - - -class SingleLineTransformationMethod(TransformationMethod): - """SingleLineTransformationMethod — 单行变换方法 (桌面存根)""" - - _instance: Optional["SingleLineTransformationMethod"] = None - - def __init__(self): - super().__init__() - - @staticmethod - def getInstance() -> "SingleLineTransformationMethod": - if SingleLineTransformationMethod._instance is None: - SingleLineTransformationMethod._instance = SingleLineTransformationMethod() - return SingleLineTransformationMethod._instance - - def getTransformation(self, source: Any, view: Any) -> Any: - """移除换行符""" - text = str(source) if source else "" - return text.replace("\n", " ").replace("\r", "") - - -class HideReturnsTransformationMethod(SingleLineTransformationMethod): - """HideReturnsTransformationMethod — 隐藏回车变换方法 (桌面存根)""" - - _instance: Optional["HideReturnsTransformationMethod"] = None - - def __init__(self): - super().__init__() - - @staticmethod - def getInstance() -> "HideReturnsTransformationMethod": - if HideReturnsTransformationMethod._instance is None: - HideReturnsTransformationMethod._instance = HideReturnsTransformationMethod() - return HideReturnsTransformationMethod._instance - - def getTransformation(self, source: Any, view: Any) -> Any: - text = str(source) if source else "" - return text.replace("\r", "") - - -class TimeDisplayTruncationMethod(TransformationMethod): - """TimeDisplayTruncationMethod — 时间显示截断方法 (桌面存根)""" - - def __init__(self, max_digits: int = 4): - super().__init__() - self._max_digits = max_digits - - def getTransformation(self, source: Any, view: Any) -> Any: - text = str(source) if source else "" - if len(text) > self._max_digits: - return text[:self._max_digits] - return text - - -# ═══════════════════════════════════════════════════════════════ -# DateTimeKeyListener / DateKeyListener / TimeKeyListener -# ═══════════════════════════════════════════════════════════════ - -class DateTimeKeyListener(NumberKeyListener): - """DateTimeKeyListener — 日期时间按键监听器 (桌面存根)""" - - def __init__(self, locale: Optional[Any] = None): - super().__init__() - self._locale = locale - self._ACCEPTED_CHARS = "0123456789/: " - - -class DateKeyListener(NumberKeyListener): - """DateKeyListener — 日期按键监听器 (桌面存根)""" - - _instance: Optional["DateKeyListener"] = None - - def __init__(self, locale: Optional[Any] = None): - super().__init__() - self._locale = locale - self._ACCEPTED_CHARS = "0123456789/" - - @staticmethod - def getInstance(locale: Any = None) -> "DateKeyListener": - if DateKeyListener._instance is None: - DateKeyListener._instance = DateKeyListener(locale) - return DateKeyListener._instance - - -class TimeKeyListener(NumberKeyListener): - """TimeKeyListener — 时间按键监听器 (桌面存根)""" - - _instance: Optional["TimeKeyListener"] = None - - def __init__(self, locale: Optional[Any] = None): - super().__init__() - self._locale = locale - self._ACCEPTED_CHARS = "0123456789:" - - @staticmethod - def getInstance(locale: Any = None) -> "TimeKeyListener": - if TimeKeyListener._instance is None: - TimeKeyListener._instance = TimeKeyListener(locale) - return TimeKeyListener._instance \ No newline at end of file diff --git a/android_pyapi/android/text_style.py b/android_pyapi/android/text_style.py deleted file mode 100644 index adf620f..0000000 --- a/android_pyapi/android/text_style.py +++ /dev/null @@ -1,974 +0,0 @@ -""" -Android text.style 包 — CharacterStyle, AlignmentSpan, BulletSpan, URLSpan 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import sys -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "CharacterStyle", - "MetricAffectingSpan", - "BackgroundColorSpan", - "ForegroundColorSpan", - "StyleSpan", - "TypefaceSpan", - "AbsoluteSizeSpan", - "RelativeSizeSpan", - "ScaleXSpan", - "StrikethroughSpan", - "UnderlineSpan", - "SubscriptSpan", - "SuperscriptSpan", - "URLSpan", - "ClickableSpan", - "ImageSpan", - "AlignmentSpan", - "BulletSpan", - "QuoteSpan", - "LeadingMarginSpan", - "LeadingMarginSpan2", - "LineBackgroundSpan", - "LineHeightSpan", - "TabStopSpan", - "WrapTogetherSpan", - "ParagraphStyle", - "DrawableMarginSpan", - "IconMarginSpan", - "EasyEditSpan", - "SpellCheckSpan", - "SuggestionSpan", - "TextAppearanceSpan", - "LocaleSpan", - "ReplacementSpan", - "DynamicDrawableSpan", - "MaskFilterSpan", - "RasterizerSpan", - "ShadowStyleSpan", - "TtsSpan", -] - - -# ═══════════════════════════════════════════════════════════════ -# CharacterStyle -# ═══════════════════════════════════════════════════════════════ - -class CharacterStyle(Stub): - """CharacterStyle — 字符样式基类 (桌面存根)""" - - def __init__(self): - super().__init__() - - def setSpan(self, text: Any, start: int, end: int, flags: int) -> None: - """设置 span 范围""" - pass - - def getUnderlying(self) -> "CharacterStyle": - """获取底层样式""" - return self - - @staticmethod - def wrap(cs: "CharacterStyle") -> "CharacterStyle": - """包装样式""" - return cs - - def updateDrawState(self, tp: Any) -> None: - """更新绘制状态""" - pass - - def toString(self) -> str: - return f"{self.__class__.__name__}@CharacterStyle" - - -class MetricAffectingSpan(CharacterStyle): - """MetricAffectingSpan — 影响度量的 Span""" - - def __init__(self): - super().__init__() - - def updateMeasureState(self, tp: Any) -> None: - """更新度量状态""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# BackgroundColorSpan -# ═══════════════════════════════════════════════════════════════ - -class BackgroundColorSpan(CharacterStyle): - """BackgroundColorSpan — 背景颜色 Span""" - - def __init__(self, color: int = 0): - super().__init__() - self._color = color - - def setColor(self, color: int) -> None: - """设置背景颜色""" - self._color = color - - def getBackgroundColor(self) -> int: - """获取背景颜色""" - return self._color - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'bgColor'): - tp.bgColor = self._color - - -# ═══════════════════════════════════════════════════════════════ -# ForegroundColorSpan -# ═══════════════════════════════════════════════════════════════ - -class ForegroundColorSpan(CharacterStyle): - """ForegroundColorSpan — 前景颜色 Span""" - - def __init__(self, color: int = 0): - super().__init__() - self._color = color - - def setColor(self, color: int) -> None: - """设置前景颜色""" - self._color = color - - def getForegroundColor(self) -> int: - """获取前景颜色""" - return self._color - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'color'): - tp.color = self._color - - -# ═══════════════════════════════════════════════════════════════ -# StyleSpan -# ═══════════════════════════════════════════════════════════════ - -class StyleSpan(MetricAffectingSpan): - """StyleSpan — 字体样式 Span""" - - NORMAL = 0 - BOLD = 1 - ITALIC = 2 - BOLD_ITALIC = 3 - - def __init__(self, style: int = NORMAL): - super().__init__() - self._style = style - - def getStyle(self) -> int: - """获取字体样式""" - return self._style - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'style'): - tp.style = self._style - - def updateMeasureState(self, tp: Any) -> None: - if hasattr(tp, 'style'): - tp.style = self._style - - -# ═══════════════════════════════════════════════════════════════ -# TypefaceSpan -# ═══════════════════════════════════════════════════════════════ - -class TypefaceSpan(MetricAffectingSpan): - """TypefaceSpan — 字体 Span""" - - def __init__(self, family: str = "sans-serif"): - super().__init__() - self._family = family - - def getFamily(self) -> str: - """获取字体名称""" - return self._family - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'typeface'): - tp.typeface = self._family - - def updateMeasureState(self, tp: Any) -> None: - if hasattr(tp, 'typeface'): - tp.typeface = self._family - - -# ═══════════════════════════════════════════════════════════════ -# AbsoluteSizeSpan -# ═══════════════════════════════════════════════════════════════ - -class AbsoluteSizeSpan(MetricAffectingSpan): - """AbsoluteSizeSpan — 绝对字号 Span""" - - def __init__(self, size: int = 0, dip: bool = False): - super().__init__() - self._size = size - self._dip = dip - - def getSize(self) -> int: - """获取字号""" - return self._size - - def getDip(self) -> bool: - """检查是否使用 dip 单位""" - return self._dip - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'textSize'): - tp.textSize = float(self._size) - - def updateMeasureState(self, tp: Any) -> None: - if hasattr(tp, 'textSize'): - tp.textSize = float(self._size) - - -# ═══════════════════════════════════════════════════════════════ -# RelativeSizeSpan -# ═══════════════════════════════════════════════════════════════ - -class RelativeSizeSpan(MetricAffectingSpan): - """RelativeSizeSpan — 相对字号 Span""" - - def __init__(self, proportion: float = 1.0): - super().__init__() - self._proportion = proportion - - def getSizeChange(self) -> float: - """获取字号变化比例""" - return self._proportion - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'textSize') and hasattr(tp, '_baseTextSize'): - tp.textSize = tp._baseTextSize * self._proportion - - def updateMeasureState(self, tp: Any) -> None: - if hasattr(tp, 'textSize') and hasattr(tp, '_baseTextSize'): - tp.textSize = tp._baseTextSize * self._proportion - - -# ═══════════════════════════════════════════════════════════════ -# ScaleXSpan -# ═══════════════════════════════════════════════════════════════ - -class ScaleXSpan(MetricAffectingSpan): - """ScaleXSpan — X 轴缩放 Span""" - - def __init__(self, scale_x: float = 1.0): - super().__init__() - self._scale_x = scale_x - - def getScaleX(self) -> float: - """获取 X 轴缩放比例""" - return self._scale_x - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'textScaleX'): - tp.textScaleX = self._scale_x - - def updateMeasureState(self, tp: Any) -> None: - if hasattr(tp, 'textScaleX'): - tp.textScaleX = self._scale_x - - -# ═══════════════════════════════════════════════════════════════ -# StrikethroughSpan / UnderlineSpan / SubscriptSpan / SuperscriptSpan -# ═══════════════════════════════════════════════════════════════ - -class StrikethroughSpan(CharacterStyle): - """StrikethroughSpan — 删除线 Span""" - - def __init__(self): - super().__init__() - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'strikethru'): - tp.strikethru = True - - -class UnderlineSpan(CharacterStyle): - """UnderlineSpan — 下划线 Span""" - - def __init__(self): - super().__init__() - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'underline'): - tp.underline = True - - -class SubscriptSpan(MetricAffectingSpan): - """SubscriptSpan — 下标 Span""" - - def __init__(self): - super().__init__() - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'subscript'): - tp.subscript = True - - -class SuperscriptSpan(MetricAffectingSpan): - """SuperscriptSpan — 上标 Span""" - - def __init__(self): - super().__init__() - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'superscript'): - tp.superscript = True - - -# ═══════════════════════════════════════════════════════════════ -# URLSpan / ClickableSpan -# ═══════════════════════════════════════════════════════════════ - -class URLSpan(CharacterStyle): - """URLSpan — 超链接 Span""" - - def __init__(self, url: str = ""): - super().__init__() - self._url = url - - def getURL(self) -> str: - """获取 URL""" - return self._url - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'color'): - tp.color = 0xFF0000FF # blue - if hasattr(tp, 'underline'): - tp.underline = True - - def onClick(self, view: Any) -> None: - """点击事件""" - pass - - -class ClickableSpan(CharacterStyle): - """ClickableSpan — 可点击 Span""" - - def __init__(self): - super().__init__() - - def onClick(self, view: Any) -> None: - """点击事件""" - pass - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'color'): - tp.color = 0xFF0000FF - if hasattr(tp, 'underline'): - tp.underline = True - - -# ═══════════════════════════════════════════════════════════════ -# ImageSpan -# ═══════════════════════════════════════════════════════════════ - -class ImageSpan(CharacterStyle): - """ImageSpan — 图片 Span""" - - def __init__(self, drawable: Any = None, source: str = "", vertical_alignment: int = 0): - super().__init__() - self._drawable = drawable - self._source = source - self._vertical_alignment = vertical_alignment - - def getDrawable(self) -> Any: - """获取 Drawable""" - return self._drawable - - def getSource(self) -> str: - """获取资源源""" - return self._source - - def getContentUri(self) -> Optional[str]: - """获取内容 URI""" - return None - - def draw(self, canvas: Any, text: str, x: int, y: int, top: int, bottom: int) -> None: - """绘制图片""" - if self._drawable and hasattr(self._drawable, 'draw'): - self._drawable.draw(canvas) - - def toString(self) -> str: - return f"ImageSpan(source={self._source})" - - -# ═══════════════════════════════════════════════════════════════ -# AlignmentSpan -# ═══════════════════════════════════════════════════════════════ - -class AlignmentSpan(CharacterStyle): - """AlignmentSpan — 对齐 Span""" - - class Standard(CharacterStyle): - """标准对齐实现""" - - def __init__(self, alignment: int): - super().__init__() - self._alignment = alignment - - def getAlignment(self) -> int: - return self._alignment - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'alignment'): - tp.alignment = self._alignment - - def __init__(self, alignment: int = 0): - super().__init__() - self._alignment = alignment - - def getAlignment(self) -> int: - """获取对齐方式""" - return self._alignment - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'alignment'): - tp.alignment = self._alignment - - -# ═══════════════════════════════════════════════════════════════ -# BulletSpan -# ═══════════════════════════════════════════════════════════════ - -class BulletSpan(CharacterStyle): - """BulletSpan — 项目符号 Span""" - - STANDARD_GAP_WIDTH = 2 - - def __init__(self, leading_margin: int = 0, gap_width: int = STANDARD_GAP_WIDTH, - bullet_radius: int = 4, color: int = 0xFF000000): - super().__init__() - self._leading_margin = leading_margin - self._gap_width = gap_width - self._bullet_radius = bullet_radius - self._color = color - - def getLeadingMargin(self) -> int: - """获取前导边距""" - return self._leading_margin - - def getGapWidth(self) -> int: - """获取间隙宽度""" - return self._gap_width - - def getBulletRadius(self) -> int: - """获取项目符号半径""" - return self._bullet_radius - - def getColor(self) -> int: - """获取颜色""" - return self._color - - def updateDrawState(self, tp: Any) -> None: - pass - - def drawLeadingMargin(self, canvas: Any, paint: Any, x: int, dir_: int, - top: int, baseline: int, bottom: int, text: str, - start: int, end: int, first: bool, margin: int) -> None: - """绘制前导边距中的项目符号""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# QuoteSpan -# ═══════════════════════════════════════════════════════════════ - -class QuoteSpan(CharacterStyle): - """QuoteSpan — 引用 Span""" - - STANDARD_GAP_WIDTH = 2 - STANDARD_COLOR = 0xFF0000FF - - def __init__(self, color: int = STANDARD_COLOR, gap_width: int = STANDARD_GAP_WIDTH, stripe_width: int = 2): - super().__init__() - self._color = color - self._gap_width = gap_width - self._stripe_width = stripe_width - - def getColor(self) -> int: - """获取引用颜色""" - return self._color - - def getGapWidth(self) -> int: - """获取间隙宽度""" - return self._gap_width - - def getStripeWidth(self) -> int: - """获取条纹宽度""" - return self._stripe_width - - def updateDrawState(self, tp: Any) -> None: - pass - - def drawLeadingMargin(self, canvas: Any, paint: Any, x: int, dir_: int, - top: int, baseline: int, bottom: int, text: str, - start: int, end: int, first: bool, margin: int) -> None: - """绘制引用条纹""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# LeadingMarginSpan / LeadingMarginSpan2 -# ═══════════════════════════════════════════════════════════════ - -class LeadingMarginSpan(CharacterStyle): - """LeadingMarginSpan — 前导边距 Span""" - - def __init__(self): - super().__init__() - - def getLeadingMargin(self, first: bool) -> int: - """获取前导边距""" - return 0 - - def drawLeadingMargin(self, canvas: Any, paint: Any, x: int, dir_: int, - top: int, baseline: int, bottom: int, text: str, - start: int, end: int, first: bool, margin: int) -> None: - """绘制前导边距""" - pass - - -class LeadingMarginSpan2(LeadingMarginSpan): - """LeadingMarginSpan2 — 支持行数限制的前导边距 Span""" - - def __init__(self): - super().__init__() - - def getLeadingMarginLineCount(self) -> int: - """获取前导边距行数""" - return 1 - - -# ═══════════════════════════════════════════════════════════════ -# LineBackgroundSpan / LineHeightSpan -# ═══════════════════════════════════════════════════════════════ - -class LineBackgroundSpan(CharacterStyle): - """LineBackgroundSpan — 行背景 Span""" - - def __init__(self): - super().__init__() - - def drawBackground(self, canvas: Any, paint: Any, left: int, right: int, - top: int, baseline: int, bottom: int, text: str, - start: int, end: int, line_num: int) -> None: - """绘制行背景""" - pass - - -class LineHeightSpan(CharacterStyle): - """LineHeightSpan — 行高 Span""" - - class WithDensity(CharacterStyle): - """带密度参数的行高实现""" - - def __init__(self): - super().__init__() - - def chooseHeight(self, text: str, start: int, end: int, spanstart: int, - spanend: int, height: List[int], fontMetrics: Any) -> None: - pass - - def __init__(self): - super().__init__() - - def chooseHeight(self, text: str, start: int, end: int, spanstart: int, - spanend: int, height: List[int], fontMetrics: Any) -> None: - """选择行高""" - pass - - @staticmethod - def increaseHeight(height: List[int], increase: int) -> None: - """增加高度""" - if height: - height[0] = height[0] + increase - - -# ═══════════════════════════════════════════════════════════════ -# TabStopSpan / WrapTogetherSpan / ParagraphStyle -# ═══════════════════════════════════════════════════════════════ - -class TabStopSpan(CharacterStyle): - """TabStopSpan — 制表符停靠 Span""" - - class Standard(CharacterStyle): - """标准制表符停靠实现""" - - def __init__(self, tab_stop: int = 40): - super().__init__() - self._tab_stop = tab_stop - - def getTabStop(self) -> int: - return self._tab_stop - - def __init__(self, tab_stop: int = 40): - super().__init__() - self._tab_stop = tab_stop - - def getTabStop(self) -> int: - """获取制表符停靠位置""" - return self._tab_stop - - -class WrapTogetherSpan(CharacterStyle): - """WrapTogetherSpan — 一起换行 Span""" - - def __init__(self): - super().__init__() - - -class ParagraphStyle(Stub): - """ParagraphStyle — 段落样式标记接口""" - - def __init__(self): - super().__init__() - - -# ═══════════════════════════════════════════════════════════════ -# DrawableMarginSpan / IconMarginSpan -# ═══════════════════════════════════════════════════════════════ - -class DrawableMarginSpan(CharacterStyle): - """DrawableMarginSpan — 可绘制边距 Span""" - - def __init__(self, drawable: Any = None, pad: int = 0): - super().__init__() - self._drawable = drawable - self._pad = pad - - -class IconMarginSpan(CharacterStyle): - """IconMarginSpan — 图标边距 Span""" - - def __init__(self, bitmap: Any = None, pad: int = 0): - super().__init__() - self._bitmap = bitmap - self._pad = pad - - -# ═══════════════════════════════════════════════════════════════ -# EasyEditSpan / SpellCheckSpan -# ═══════════════════════════════════════════════════════════════ - -class EasyEditSpan(CharacterStyle): - """EasyEditSpan — 简易编辑 Span""" - - def __init__(self): - super().__init__() - - -class SpellCheckSpan(CharacterStyle): - """SpellCheckSpan — 拼写检查 Span""" - - def __init__(self, spell_checking: bool = True): - super().__init__() - self._spell_checking = spell_checking - - def isSpellCheckEnabled(self) -> bool: - """检查拼写检查是否启用""" - return self._spell_checking - - def setSpellCheckEnabled(self, enabled: bool) -> None: - """设置拼写检查启用""" - self._spell_checking = enabled - - -# ═══════════════════════════════════════════════════════════════ -# SuggestionSpan -# ═══════════════════════════════════════════════════════════════ - -class SuggestionSpan(CharacterStyle): - """SuggestionSpan — 建议 Span""" - - FLAG_EASY_CORRECT = 0x0001 - FLAG_MISSPELLED = 0x0002 - FLAG_AUTO_CORRECTION = 0x0004 - FLAG_GRAMMAR_ERROR = 0x0008 - FLAG_PERSONAL_NAME = 0x0010 - FLAG_COMPATIBILITY = 0x0020 - SUGGESTIONS_MAX_SIZE = 5 - - def __init__(self, locale: Optional[Any] = None, suggestions: Optional[List[str]] = None, - flags: int = 0, underline_color: int = 0): - super().__init__() - self._locale = locale - self._suggestions = suggestions or [] - self._flags = flags - self._underline_color = underline_color - - def getLocale(self) -> Optional[Any]: - """获取区域设置""" - return self._locale - - def getSuggestions(self) -> List[str]: - """获取建议列表""" - return self._suggestions.copy() - - def getUnderlineColor(self) -> int: - """获取下划线颜色""" - return self._underline_color - - def setUnderlineColor(self, color: int) -> None: - """设置下划线颜色""" - self._underline_color = color - - def getFlags(self) -> int: - """获取标志""" - return self._flags - - def setFlags(self, flags: int) -> None: - """设置标志""" - self._flags = flags - - @staticmethod - def getSuggestionsFromContext(context: Any, locale: Any, query: str, - max_results: int = SUGGESTIONS_MAX_SIZE) -> List[str]: - """从上下文中获取建议""" - return [] - - -# ═══════════════════════════════════════════════════════════════ -# TextAppearanceSpan -# ═══════════════════════════════════════════════════════════════ - -class TextAppearanceSpan(MetricAffectingSpan): - """TextAppearanceSpan — 文字外观 Span""" - - def __init__(self, context: Any = None, appearance_id: int = 0, - family: str = "sans-serif", text_size: Optional[int] = None, - text_color: Optional[int] = None, link_text_color: Optional[int] = None): - super().__init__() - self._family = family - self._text_size = text_size - self._text_color = text_color - self._link_text_color = link_text_color - - def getFamily(self) -> str: - """获取字体名称""" - return self._family - - def getTextSize(self) -> Optional[int]: - """获取字号""" - return self._text_size - - def getTextColor(self) -> Optional[int]: - """获取文字颜色""" - return self._text_color - - def getTextColorLink(self) -> Optional[int]: - """获取链接文字颜色""" - return self._link_text_color - - def getLinkTextColor(self) -> Optional[int]: - """获取链接文字颜色""" - return self._link_text_color - - def updateDrawState(self, tp: Any) -> None: - if self._text_color is not None and hasattr(tp, 'color'): - tp.color = self._text_color - if self._text_size is not None and hasattr(tp, 'textSize'): - tp.textSize = float(self._text_size) - if hasattr(tp, 'typeface'): - tp.typeface = self._family - - def updateMeasureState(self, tp: Any) -> None: - if self._text_size is not None and hasattr(tp, 'textSize'): - tp.textSize = float(self._text_size) - - -# ═══════════════════════════════════════════════════════════════ -# LocaleSpan -# ═══════════════════════════════════════════════════════════════ - -class LocaleSpan(MetricAffectingSpan): - """LocaleSpan — 区域设置 Span""" - - def __init__(self, locale: Any = None): - super().__init__() - self._locale = locale - - def getLocale(self) -> Any: - """获取区域设置""" - return self._locale - - def updateDrawState(self, tp: Any) -> None: - pass - - def updateMeasureState(self, tp: Any) -> None: - pass - - -# ═══════════════════════════════════════════════════════════════ -# ReplacementSpan -# ═══════════════════════════════════════════════════════════════ - -class ReplacementSpan(CharacterStyle): - """ReplacementSpan — 替换 Span""" - - def __init__(self): - super().__init__() - - def draw(self, canvas: Any, text: str, start: int, end: int, x: float, - top: int, y: float, bottom: int, paint: Any) -> None: - """绘制替换内容""" - pass - - def getSize(self, paint: Any, text: str, start: int, end: int, - fontMetrics: Optional[Any]) -> int: - """获取尺寸""" - return 0 - - def updateMeasureState(self, tp: Any) -> None: - pass - - -# ═══════════════════════════════════════════════════════════════ -# DynamicDrawableSpan -# ═══════════════════════════════════════════════════════════════ - -class DynamicDrawableSpan(ReplacementSpan): - """DynamicDrawableSpan — 动态可绘制 Span""" - - ALIGN_BOTTOM = 0 - ALIGN_BASELINE = 1 - ALIGN_CENTER = 2 - - def __init__(self, vertical_alignment: int = ALIGN_BOTTOM): - super().__init__() - self._vertical_alignment = vertical_alignment - - def getDrawable(self) -> Any: - """获取可绘制对象""" - return None - - def draw(self, canvas: Any, text: str, start: int, end: int, x: float, - top: int, y: float, bottom: int, paint: Any) -> None: - drawable = self.getDrawable() - if drawable: - pass - - def getSize(self, paint: Any, text: str, start: int, end: int, - fontMetrics: Optional[Any]) -> int: - drawable = self.getDrawable() - if drawable and hasattr(drawable, 'getIntrinsicWidth'): - return drawable.getIntrinsicWidth() - return 0 - - -# ═══════════════════════════════════════════════════════════════ -# MaskFilterSpan / RasterizerSpan / ShadowStyleSpan -# ═══════════════════════════════════════════════════════════════ - -class MaskFilterSpan(CharacterStyle): - """MaskFilterSpan — 遮罩滤镜 Span""" - - def __init__(self, filter_: Any = None): - super().__init__() - self._filter = filter_ - - def getMaskFilter(self) -> Any: - """获取遮罩滤镜""" - return self._filter - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'maskFilter'): - tp.maskFilter = self._filter - - -class RasterizerSpan(CharacterStyle): - """RasterizerSpan — 光栅化 Span""" - - def __init__(self, rasterizer: Any = None): - super().__init__() - self._rasterizer = rasterizer - - def getRasterizer(self) -> Any: - """获取光栅化器""" - return self._rasterizer - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'rasterizer'): - tp.rasterizer = self._rasterizer - - -class ShadowStyleSpan(CharacterStyle): - """ShadowStyleSpan — 阴影样式 Span""" - - def __init__(self, radius: float = 1.0, dx: float = 0, dy: float = 0, color: int = 0): - super().__init__() - self._radius = radius - self._dx = dx - self._dy = dy - self._color = color - - def updateDrawState(self, tp: Any) -> None: - if hasattr(tp, 'setShadowLayer'): - tp.setShadowLayer(self._radius, self._dx, self._dy, self._color) - - -# ═══════════════════════════════════════════════════════════════ -# TtsSpan -# ═══════════════════════════════════════════════════════════════ - -class TtsSpan(CharacterStyle): - """TtsSpan — 文本转语音 Span""" - - TYPE_DATE = "android.type.date" - TYPE_DELIMITER = "android.type.delimiter" - TYPE_DIGITS = "android.type.digits" - TYPE_ELECTRONIC = "android.type.electronic" - TYPE_FRACTION = "android.type.fraction" - TYPE_MEASURE = "android.type.measure" - TYPE_MONEY = "android.type.money" - TYPE_NUMBER = "android.type.number" - TYPE_ORDINAL = "android.type.ordinal" - TYPE_PERCENTAGE = "android.type.percentage" - TYPE_TELEPHONE = "android.type.telephone" - TYPE_TEXT = "android.type.text" - TYPE_TIME = "android.type.time" - TYPE_VERBATIM = "android.type.verbatim" - - ARG_TEXT = "android.arg.text" - ARG_QUANTITY = "android.arg.quantity" - ARG_MINUTES = "android.arg.minutes" - ARG_HOURS = "android.arg.hours" - ARG_DAY = "android.arg.day" - ARG_MONTH = "android.arg.month" - ARG_YEAR = "android.arg.year" - ARG_WEEKDAY = "android.arg.weekday" - ARG_COUNTRY_CODE = "android.arg.country_code" - ARG_NUMBER_PARTS = "android.arg.number_parts" - ARG_NUMBER_TYPE = "android.arg.number_type" - ARG_NUMBER_GROUP = "android.arg.number_group" - ARG_MONEY = "android.arg.money" - ARG_CURRENCY = "android.arg.currency" - ARG_AMOUNT = "android.arg.amount" - ARG_FRACTION_DENOMINATOR = "android.arg.fraction_denominator" - ARG_FRACTION_NUMERATOR = "android.arg.fraction_numerator" - ARG_FRACTION_TYPE = "android.arg.fraction_type" - ARG_INTEGER_PART = "android.arg.integer_part" - - def __init__(self, type_: str = TYPE_TEXT, args: Optional[Dict[str, str]] = None): - super().__init__() - self._type = type_ - self._args = args or {} - - def getType(self) -> str: - """获取 TTS 类型""" - return self._type - - def getArgs(self) -> Dict[str, str]: - """获取参数""" - return self._args.copy() - - def toString(self) -> str: - return f"TtsSpan(type={self._type}, args={self._args})" \ No newline at end of file diff --git a/android_pyapi/android/text_util.py b/android_pyapi/android/text_util.py deleted file mode 100644 index c2f4d4a..0000000 --- a/android_pyapi/android/text_util.py +++ /dev/null @@ -1,297 +0,0 @@ -""" -Android text.util 包 — Linkify, Rfc822Token, Rfc822Tokenizer, Selection, LinkSpec -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import re -from typing import Any, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "Linkify", - "Rfc822Token", - "Rfc822Tokenizer", - "Selection", - "LinkSpec", -] - - -# ═══════════════════════════════════════════════════════════════ -# Linkify -# ═══════════════════════════════════════════════════════════════ - -class Linkify(Stub): - """Linkify — 自动链接工具 (使用正则表达式检测 URL/邮件/电话)""" - - ALL = 0xFFFFFFFF - PHONE_NUMBERS = 0x1 - WEB_URLS = 0x2 - EMAIL_ADDRESSES = 0x4 - MAP_ADDRESSES = 0x8 - - _WEB_URL_PATTERN = re.compile( - r'(https?://|www\.)[a-zA-Z0-9\-\.]+\.\w{2,}(/\S*)?' - ) - _EMAIL_PATTERN = re.compile( - r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' - ) - _PHONE_PATTERN = re.compile( - r'(\+?\d{1,3}[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}' - ) - - def __init__(self): - super().__init__() - - @staticmethod - def addLinks(text: Any, mask: int = ALL) -> bool: - """向文本中添加链接""" - if hasattr(text, '_links'): - text._links = [] - s = str(text) - if mask & Linkify.WEB_URLS: - for m in Linkify._WEB_URL_PATTERN.finditer(s): - spec = LinkSpec() - spec.url = m.group() - spec.start = m.start() - spec.end = m.end() - text._links.append(spec) - if mask & Linkify.EMAIL_ADDRESSES: - for m in Linkify._EMAIL_PATTERN.finditer(s): - spec = LinkSpec() - spec.url = f"mailto:{m.group()}" - spec.start = m.start() - spec.end = m.end() - text._links.append(spec) - if mask & Linkify.PHONE_NUMBERS: - for m in Linkify._PHONE_PATTERN.finditer(s): - spec = LinkSpec() - spec.url = f"tel:{m.group()}" - spec.start = m.start() - spec.end = m.end() - text._links.append(spec) - return len(text._links) > 0 - return False - - @staticmethod - def addLinks_ext(text: Any, pattern: Any, scheme: str, - linkMatchFilter: Any = None, urlFilter: Any = None) -> bool: - """使用自定义模式添加链接""" - if hasattr(text, '_links'): - text._links = [] - s = str(text) - if isinstance(pattern, str): - compiled = re.compile(pattern) - else: - compiled = pattern - for m in compiled.finditer(s): - spec = LinkSpec() - spec.url = f"{scheme}:{m.group()}" - spec.start = m.start() - spec.end = m.end() - text._links.append(spec) - return len(text._links) > 0 - return False - - @staticmethod - def removeLinks(text: Any) -> None: - """移除文本中的链接""" - if hasattr(text, '_links'): - text._links = [] - - @staticmethod - def applyLinks(text: Any, mask: int) -> bool: - """应用链接""" - return Linkify.addLinks(text, mask) - - def toString(self) -> str: - return f"Linkify()" - - -# ═══════════════════════════════════════════════════════════════ -# Rfc822Token -# ═══════════════════════════════════════════════════════════════ - -class Rfc822Token(Stub): - """Rfc822Token — RFC 822 电子邮件令牌""" - - def __init__(self, name: str = "", address: str = "", comment: str = ""): - super().__init__() - self._name = name - self._address = address - self._comment = comment - - def getName(self) -> str: - """获取名称""" - return self._name - - def getAddress(self) -> str: - """获取地址""" - return self._address - - def getComment(self) -> str: - """获取注释""" - return self._comment - - def setAddress(self, address: str) -> None: - """设置地址""" - self._address = address - - def setComment(self, comment: str) -> None: - """设置注释""" - self._comment = comment - - def setName(self, name: str) -> None: - """设置名称""" - self._name = name - - def toString(self) -> str: - """返回完整的 RFC 822 字符串""" - parts = [] - if self._name: - parts.append(f"{self._name} <{self._address}>") - else: - parts.append(self._address) - if self._comment: - parts.append(f"({self._comment})") - return " ".join(parts) - - def equals(self, other: Any) -> bool: - if not isinstance(other, Rfc822Token): - return False - return (self._name == other._name and - self._address == other._address and - self._comment == other._comment) - - def hashCode(self) -> int: - return hash((self._name, self._address, self._comment)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# Rfc822Tokenizer -# ═══════════════════════════════════════════════════════════════ - -class Rfc822Tokenizer(Stub): - """Rfc822Tokenizer — RFC 822 分词器""" - - def __init__(self): - super().__init__() - - @staticmethod - def tokenize(text: str) -> List[Rfc822Token]: - """分词""" - tokens = [] - pattern = re.compile(r'([^<,]+)?<([^>]+)>\s*\(([^)]*)\)|([^<,]+)?<([^>]+)>|([^@,\s]+@[^,\s]+)') - for m in pattern.finditer(text): - if m.group(1) or m.group(2): - name = (m.group(1) or "").strip() - addr = (m.group(2) or m.group(5) or "").strip() - comment = (m.group(3) or "").strip() - tokens.append(Rfc822Token(name, addr, comment)) - elif m.group(6): - tokens.append(Rfc822Token("", m.group(6).strip(), "")) - return tokens - - @staticmethod - def getTokenStart(text: str, cursor: int) -> int: - """获取令牌起始位置""" - return cursor - - @staticmethod - def getTokenEnd(text: str, cursor: int) -> int: - """获取令牌结束位置""" - return cursor - - @staticmethod - def advance(text: str, cursor: int) -> int: - """前进光标""" - return cursor + 1 - - -# ═══════════════════════════════════════════════════════════════ -# Selection -# ═══════════════════════════════════════════════════════════════ - -class Selection(Stub): - """Selection — 文本选择工具""" - - def __init__(self): - super().__init__() - - @staticmethod - def setSelection(content: Any, start: int, stop: Optional[int] = None) -> None: - """设置选择范围""" - if hasattr(content, '_selection_start'): - content._selection_start = start - content._selection_end = stop if stop is not None else start - if hasattr(content, '_selection'): - content._selection = (start, stop if stop is not None else start) - - @staticmethod - def extendSelection(content: Any, index: int) -> None: - """扩展选择范围""" - if hasattr(content, '_selection_end'): - content._selection_end = index - - @staticmethod - def extendUp(content: Any, view: Any) -> bool: - """向上扩展选择""" - return False - - @staticmethod - def extendDown(content: Any, view: Any) -> bool: - """向下扩展选择""" - return False - - @staticmethod - def extendLeft(content: Any, view: Any) -> bool: - """向左扩展选择""" - return False - - @staticmethod - def extendRight(content: Any, view: Any) -> bool: - """向右扩展选择""" - return False - - @staticmethod - def moveUp(content: Any, view: Any) -> bool: - """向上移动选择""" - return False - - @staticmethod - def moveDown(content: Any, view: Any) -> bool: - """向下移动选择""" - return False - - @staticmethod - def moveLeft(content: Any, view: Any) -> bool: - """向左移动选择""" - return False - - @staticmethod - def moveRight(content: Any, view: Any) -> bool: - """向右移动选择""" - return False - - -# ═══════════════════════════════════════════════════════════════ -# LinkSpec -# ═══════════════════════════════════════════════════════════════ - -class LinkSpec(Stub): - """LinkSpec — 链接规范""" - - def __init__(self): - super().__init__() - self.url: str = "" - self.start: int = 0 - self.end: int = 0 - self.frameworkAddedSpan: bool = False - - def toString(self) -> str: - return f"LinkSpec(url={self.url}, start={self.start}, end={self.end})" \ No newline at end of file diff --git a/android_pyapi/android/transition.py b/android_pyapi/android/transition.py deleted file mode 100644 index f7064cc..0000000 --- a/android_pyapi/android/transition.py +++ /dev/null @@ -1,504 +0,0 @@ -""" -Android Transition 组件 — Transition, TransitionManager, Scene, Fade, Slide, Explode 等 -方法签名完全对齐 Android SDK (android.transition 包) -""" - -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# Scene -# ═══════════════════════════════════════════════════════════════ - -class Scene(Stub): - """Scene — 转场场景""" - - def __init__(self, root: Any = None, layout: Any = None): - super().__init__() - self._root = root - self._layout = layout - self._entered = False - - @staticmethod - def getSceneForLayout(root: Any, layout_id: int, context: Any) -> "Scene": - """从布局资源创建场景""" - return Scene(root, layout_id) - - def getRoot(self) -> Any: - return self._root - - def enter(self): - """进入场景""" - self._entered = True - - def exit(self): - """退出场景""" - self._entered = False - - def isEntered(self) -> bool: - return self._entered - - def setEnterAction(self, action: Callable): - """设置进入动作""" - self._enter_action = action - - def setExitAction(self, action: Callable): - """设置退出动作""" - self._exit_action = action - - def toString(self) -> str: - return f"Scene(root={self._root}, entered={self._entered})" - - def hashCode(self) -> int: - return hash(id(self._root)) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, Scene): - return False - return self._root is other._root - - -# ═══════════════════════════════════════════════════════════════ -# Transition -# ═══════════════════════════════════════════════════════════════ - -class Transition(Stub): - """Transition — 转场动画基类""" - - # 匹配模式 - MATCH_VIEW_NAME = 0 - MATCH_ID = 1 - MATCH_INSTANCE = 2 - MATCH_ITEM_ID = 3 - - def __init__(self): - super().__init__() - self._duration: int = 300 - self._start_delay: int = 0 - self._interpolator: Optional[Any] = None - self._targets: Set[Any] = set() - self._target_ids: Set[int] = set() - self._target_names: Set[str] = set() - self._target_types: Set[type] = set() - self._exclude_targets: Set[Any] = set() - self._exclude_ids: Set[int] = set() - self._exclude_names: Set[str] = set() - self._exclude_types: Set[type] = set() - self._listeners: List[Any] = [] - self._name: str = "" - self._epicenter_callback: Optional[Callable] = None - self._path_motion: Optional[Any] = None - self._propagation: Optional[Any] = None - self._paused: bool = False - self._started: bool = False - self._ended: bool = False - - def setDuration(self, duration_ms: int) -> "Transition": - """设置持续时间 (毫秒)""" - self._duration = max(0, duration_ms) - return self - - def getDuration(self) -> int: - return self._duration - - def setStartDelay(self, start_delay: int) -> "Transition": - """设置起始延迟 (毫秒)""" - self._start_delay = max(0, start_delay) - return self - - def getStartDelay(self) -> int: - return self._start_delay - - def setInterpolator(self, interpolator: Any) -> "Transition": - """设置插值器""" - self._interpolator = interpolator - return self - - def getInterpolator(self) -> Any: - return self._interpolator - - def addTarget(self, target: Any) -> "Transition": - """添加动画目标""" - if isinstance(target, int): - self._target_ids.add(target) - elif isinstance(target, str): - self._target_names.add(target) - elif isinstance(target, type): - self._target_types.add(target) - else: - self._targets.add(target) - return self - - def removeTarget(self, target: Any) -> "Transition": - """移除动画目标""" - if isinstance(target, int): - self._target_ids.discard(target) - elif isinstance(target, str): - self._target_names.discard(target) - elif isinstance(target, type): - self._target_types.discard(target) - else: - self._targets.discard(target) - return self - - def getTargets(self) -> List[Any]: - return list(self._targets) - - def excludeTarget(self, target: Any, exclude: bool = True) -> "Transition": - """排除目标""" - if exclude: - if isinstance(target, int): - self._exclude_ids.add(target) - elif isinstance(target, str): - self._exclude_names.add(target) - elif isinstance(target, type): - self._exclude_types.add(target) - else: - self._exclude_targets.add(target) - else: - if isinstance(target, int): - self._exclude_ids.discard(target) - elif isinstance(target, str): - self._exclude_names.discard(target) - elif isinstance(target, type): - self._exclude_types.discard(target) - else: - self._exclude_targets.discard(target) - return self - - def addListener(self, listener: Any) -> "Transition": - """添加转场监听器""" - if listener not in self._listeners: - self._listeners.append(listener) - return self - - def removeListener(self, listener: Any) -> "Transition": - """移除转场监听器""" - if listener in self._listeners: - self._listeners.remove(listener) - return self - - def getListeners(self) -> List[Any]: - return list(self._listeners) - - def setName(self, name: str) -> "Transition": - self._name = name - return self - - def getName(self) -> str: - return self._name - - def setEpicenterCallback(self, callback: Callable) -> "Transition": - self._epicenter_callback = callback - return self - - def getEpicenterCallback(self) -> Optional[Callable]: - return self._epicenter_callback - - def setPathMotion(self, path_motion: Any) -> "Transition": - self._path_motion = path_motion - return self - - def getPathMotion(self) -> Any: - return self._path_motion - - def setPropagation(self, propagation: Any) -> "Transition": - self._propagation = propagation - return self - - def getPropagation(self) -> Any: - return self._propagation - - def isPaused(self) -> bool: - return self._paused - - def pause(self): - """暂停转场""" - self._paused = True - - def resume(self): - """恢复转场""" - self._paused = False - - def cancel(self): - """取消转场""" - self._started = False - self._ended = True - - def end(self): - """结束转场""" - self._ended = True - - def toString(self) -> str: - return (f"{self.__class__.__name__}(duration={self._duration}, " - f"startDelay={self._start_delay}, targets={len(self._targets)})") - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def equals(self, other) -> bool: - return self is other - - def captureStartValues(self, view: Any): - """捕获视图起始值""" - pass - - def captureEndValues(self, view: Any): - """捕获视图结束值""" - pass - - def createAnimator(self, view: Any, start_values: Any, - end_values: Any) -> Optional[Any]: - """创建动画器""" - return None - - -# ═══════════════════════════════════════════════════════════════ -# TransitionSet -# ═══════════════════════════════════════════════════════════════ - -class TransitionSet(Transition): - """TransitionSet — 转场合集""" - - ORDERING_TOGETHER = 0 - ORDERING_SEQUENTIAL = 1 - - def __init__(self): - super().__init__() - self._ordering = TransitionSet.ORDERING_TOGETHER - self._transitions: List[Transition] = [] - - def setOrdering(self, ordering: int) -> "TransitionSet": - """设置播放顺序""" - self._ordering = ordering - return self - - def getOrdering(self) -> int: - return self._ordering - - def addTransition(self, transition: Transition) -> "TransitionSet": - """添加子转场""" - self._transitions.append(transition) - return self - - def removeTransition(self, transition: Transition) -> "TransitionSet": - """移除子转场""" - if transition in self._transitions: - self._transitions.remove(transition) - return self - - def getTransitionAt(self, index: int) -> Optional[Transition]: - """获取指定索引的子转场""" - if 0 <= index < len(self._transitions): - return self._transitions[index] - return None - - def getTransitionCount(self) -> int: - return len(self._transitions) - - def toString(self) -> str: - return (f"TransitionSet(ordering={self._ordering}, " - f"count={len(self._transitions)})") - - -# ═══════════════════════════════════════════════════════════════ -# AutoTransition -# ═══════════════════════════════════════════════════════════════ - -class AutoTransition(TransitionSet): - """AutoTransition — 自动过渡转场""" - - def __init__(self): - super().__init__() - self.setOrdering(TransitionSet.ORDERING_TOGETHER) - self._duration = 300 - - def toString(self) -> str: - return f"AutoTransition(duration={self._duration})" - - -# ═══════════════════════════════════════════════════════════════ -# Fade -# ═══════════════════════════════════════════════════════════════ - -class Fade(Transition): - """Fade — 淡入淡出转场""" - - IN = 0x00000001 - OUT = 0x00000002 - - def __init__(self, fading_mode: int = IN | OUT): - super().__init__() - self._fading_mode = fading_mode - - def getFadingMode(self) -> int: - return self._fading_mode - - def setFadingMode(self, fading_mode: int): - self._fading_mode = fading_mode - - def toString(self) -> str: - return f"Fade(mode={self._fading_mode}, duration={self._duration})" - - -# ═══════════════════════════════════════════════════════════════ -# ChangeBounds -# ═══════════════════════════════════════════════════════════════ - -class ChangeBounds(Transition): - """ChangeBounds — 位置变化转场""" - - def __init__(self): - super().__init__() - self._resize_clip: bool = False - self._reparent: bool = False - self._reparent_with_overlay: bool = True - - def setResizeClip(self, resize_clip: bool): - self._resize_clip = resize_clip - - def getResizeClip(self) -> bool: - return self._resize_clip - - def setReparent(self, reparent: bool): - self._reparent = reparent - - def getReparent(self) -> bool: - return self._reparent - - def setReparentWithOverlay(self, reparent_with_overlay: bool): - self._reparent_with_overlay = reparent_with_overlay - - def getReparentWithOverlay(self) -> bool: - return self._reparent_with_overlay - - def toString(self) -> str: - return f"ChangeBounds(duration={self._duration}, resizeClip={self._resize_clip})" - - -# ═══════════════════════════════════════════════════════════════ -# ChangeTransform -# ═══════════════════════════════════════════════════════════════ - -class ChangeTransform(Transition): - """ChangeTransform — 变换属性转场""" - - def __init__(self): - super().__init__() - self._reparent: bool = False - self._reparent_with_overlay: bool = True - - def setReparent(self, reparent: bool): - self._reparent = reparent - - def getReparent(self) -> bool: - return self._reparent - - def setReparentWithOverlay(self, reparent_with_overlay: bool): - self._reparent_with_overlay = reparent_with_overlay - - def getReparentWithOverlay(self) -> bool: - return self._reparent_with_overlay - - def toString(self) -> str: - return f"ChangeTransform(duration={self._duration})" - - -# ═══════════════════════════════════════════════════════════════ -# Slide -# ═══════════════════════════════════════════════════════════════ - -class Slide(Transition): - """Slide — 滑动转场""" - - TOP = 0x00000001 - BOTTOM = 0x00000002 - START = 0x00000003 - END = 0x00000004 - RIGHT = 0x00000005 - LEFT = 0x00000006 - - def __init__(self, slide_edge: int = BOTTOM): - super().__init__() - self._slide_edge = slide_edge - - def getSlideEdge(self) -> int: - return self._slide_edge - - def setSlideEdge(self, slide_edge: int): - self._slide_edge = slide_edge - - def toString(self) -> str: - return f"Slide(edge={self._slide_edge}, duration={self._duration})" - - -# ═══════════════════════════════════════════════════════════════ -# Explode -# ═══════════════════════════════════════════════════════════════ - -class Explode(Transition): - """Explode — 爆炸/散开转场""" - - def __init__(self): - super().__init__() - - def toString(self) -> str: - return f"Explode(duration={self._duration})" - - -# ═══════════════════════════════════════════════════════════════ -# TransitionManager -# ═══════════════════════════════════════════════════════════════ - -class TransitionManager(Stub): - """TransitionManager — 转场管理器""" - - def __init__(self): - super().__init__() - - @staticmethod - def beginDelayedTransition(scene_root: Any, - transition: Optional[Transition] = None): - """开始延迟转场""" - pass - - @staticmethod - def endTransitions(scene_root: Any): - """结束转场""" - pass - - @staticmethod - def go(scene: Scene, transition: Optional[Transition] = None): - """执行场景转场""" - if scene: - scene.enter() - - def toString(self) -> str: - return "TransitionManager(desktop-stub)" - - def hashCode(self) -> int: - return hash("android.transition.TransitionManager") & 0x7FFFFFFF - - def equals(self, other) -> bool: - return isinstance(other, TransitionManager) - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Transition", - "TransitionManager", - "AutoTransition", - "Fade", - "ChangeBounds", - "ChangeTransform", - "Slide", - "Explode", - "TransitionSet", - "Scene", -] \ No newline at end of file diff --git a/android_pyapi/android/util.py b/android_pyapi/android/util.py deleted file mode 100644 index 9f2a5f7..0000000 --- a/android_pyapi/android/util.py +++ /dev/null @@ -1,1111 +0,0 @@ -""" -Android Util 组件 — Log, SparseArray, LruCache, DisplayMetrics 等 -方法签名完全对齐 Android SDK -""" - -import math -import re as _re -import struct -import sys -from datetime import datetime -from typing import Any, Callable, Dict, Generic, List, Optional, Set, Tuple, TypeVar, Union - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, -) - - -# ═══════════════════════════════════════════════════════════════ -# Log -# ═══════════════════════════════════════════════════════════════ - -class Log(Stub): - """Log — 日志输出""" - - ASSERT = 7 - DEBUG = 3 - ERROR = 6 - INFO = 4 - VERBOSE = 2 - WARN = 5 - - _min_log_level = VERBOSE - - @staticmethod - def v(tag: str, msg: str) -> int: - if Log._min_log_level <= Log.VERBOSE: - sys.stderr.write(f"V/{tag}: {msg}\n") - return 0 - - @staticmethod - def d(tag: str, msg: str) -> int: - if Log._min_log_level <= Log.DEBUG: - sys.stderr.write(f"D/{tag}: {msg}\n") - return 0 - - @staticmethod - def i(tag: str, msg: str) -> int: - if Log._min_log_level <= Log.INFO: - sys.stderr.write(f"I/{tag}: {msg}\n") - return 0 - - @staticmethod - def w(tag: str, msg: str = None, tr: Exception = None) -> int: - if Log._min_log_level <= Log.WARN: - # 支持 w(tag, Throwable) 调用形式 - if msg is None and tr is not None: - msg = Log.getStackTraceString(tr) - elif tr is not None: - msg = f"{msg}\n{Log.getStackTraceString(tr)}" - sys.stderr.write(f"W/{tag}: {msg}\n") - return 0 - - @staticmethod - def e(tag: str, msg: str = None, tr: Exception = None) -> int: - if Log._min_log_level <= Log.ERROR: - # 支持 e(tag, Throwable) 调用形式 - if msg is None and tr is not None: - msg = Log.getStackTraceString(tr) - elif tr is not None: - msg = f"{msg}\n{Log.getStackTraceString(tr)}" - sys.stderr.write(f"E/{tag}: {msg}\n") - return 0 - - @staticmethod - def wtf(tag: str, msg=None, tr: Exception = None) -> int: - """What a Terrible Failure。支持以下调用形式: - - wtf(tag, msg) - - wtf(tag, Throwable) - - wtf(tag, msg, Throwable) - """ - # 如果 msg 是异常对象,则当作 tr 处理 - if isinstance(msg, BaseException): - tr = msg - msg = None - if msg is None and tr is not None: - msg = Log.getStackTraceString(tr) - elif tr is not None: - msg = f"{msg}\n{Log.getStackTraceString(tr)}" - if msg is None: - msg = "" - sys.stderr.write(f"WTF/{tag}: {msg}\n") - return 0 - - @staticmethod - def println(log_level: int, tag: str, msg: str) -> int: - level_map = { - Log.VERBOSE: "V", - Log.DEBUG: "D", - Log.INFO: "I", - Log.WARN: "W", - Log.ERROR: "E", - Log.ASSERT: "A", - } - prefix = level_map.get(log_level, "?") - sys.stderr.write(f"{prefix}/{tag}: {msg}\n") - return 0 - - @staticmethod - def isLoggable(tag: str, level: int) -> bool: - return level >= Log._min_log_level - - @staticmethod - def getStackTraceString(tr: Exception) -> str: - import traceback - if tr is None: - return "" - if isinstance(tr, str): - return tr - return "".join(traceback.format_exception(type(tr), tr, tr.__traceback__)) - - @staticmethod - def setMinLogLevel(level: int): - Log._min_log_level = level - - -# ═══════════════════════════════════════════════════════════════ -# SparseArray -# ═══════════════════════════════════════════════════════════════ - -T = TypeVar("T") - -class SparseArray(Generic[T], Stub): - """SparseArray — 稀疏数组(int 键映射)""" - - def __init__(self, initial_capacity: int = 10): - super().__init__() - self._keys: List[int] = [] - self._values: List[Optional[T]] = [] - self._size = 0 - - def put(self, key: int, value: T): - """如果 key 已存在则替换""" - try: - idx = self._keys.index(key) - self._values[idx] = value - except ValueError: - self._keys.append(key) - self._values.append(value) - self._size += 1 - - def get(self, key: int, default: Optional[T] = None) -> Optional[T]: - try: - idx = self._keys.index(key) - return self._values[idx] - except ValueError: - return default - - def delete(self, key: int): - try: - idx = self._keys.index(key) - self._keys.pop(idx) - self._values.pop(idx) - self._size -= 1 - except ValueError: - pass - - def remove(self, key: int): - self.delete(key) - - def removeAt(self, index: int): - if 0 <= index < self._size: - self._keys.pop(index) - self._values.pop(index) - self._size -= 1 - - def size(self) -> int: - return self._size - - def keyAt(self, index: int) -> int: - if 0 <= index < self._size: - return self._keys[index] - return 0 - - def valueAt(self, index: int) -> Optional[T]: - if 0 <= index < self._size: - return self._values[index] - return None - - def setValueAt(self, index: int, value: T): - if 0 <= index < self._size: - self._values[index] = value - - def indexOfKey(self, key: int) -> int: - try: - return self._keys.index(key) - except ValueError: - return -1 - - def indexOfValue(self, value: T) -> int: - try: - return self._values.index(value) - except ValueError: - return -1 - - def clear(self): - self._keys.clear() - self._values.clear() - self._size = 0 - - def append(self, key: int, value: T): - self.put(key, value) - - def containsKey(self, key: int) -> bool: - return key in self._keys - - def putAll(self, other: "SparseArray[T]"): - for i in range(other.size()): - self.put(other.keyAt(i), other.valueAt(i)) - - def clone(self) -> "SparseArray[T]": - sa = SparseArray[T]() - sa._keys = list(self._keys) - sa._values = list(self._values) - sa._size = self._size - return sa - - def toString(self) -> str: - pairs = ", ".join( - f"{k}={v}" for k, v in zip(self._keys, self._values) - ) - return f"SparseArray{{{pairs}}}" - - def __getitem__(self, key: int) -> Optional[T]: - return self.get(key) - - def __setitem__(self, key: int, value: T): - self.put(key, value) - - def __delitem__(self, key: int): - self.delete(key) - - def __len__(self) -> int: - return self._size - - def __contains__(self, key: int) -> bool: - return key in self._keys - - -class SparseIntArray(Stub): - """SparseIntArray — int 到 int 的映射""" - - def __init__(self, initial_capacity: int = 10): - super().__init__() - self._keys: List[int] = [] - self._values: List[int] = [] - - def put(self, key: int, value: int): - try: - idx = self._keys.index(key) - self._values[idx] = value - except ValueError: - self._keys.append(key) - self._values.append(value) - - def get(self, key: int, default: int = 0) -> int: - try: - idx = self._keys.index(key) - return self._values[idx] - except ValueError: - return default - - def delete(self, key: int): - try: - idx = self._keys.index(key) - self._keys.pop(idx) - self._values.pop(idx) - except ValueError: - pass - - def removeAt(self, index: int): - if 0 <= index < len(self._keys): - self._keys.pop(index) - self._values.pop(index) - - def size(self) -> int: - return len(self._keys) - - def keyAt(self, index: int) -> int: - if 0 <= index < len(self._keys): - return self._keys[index] - return 0 - - def valueAt(self, index: int) -> int: - if 0 <= index < len(self._values): - return self._values[index] - return 0 - - def indexOfKey(self, key: int) -> int: - try: - return self._keys.index(key) - except ValueError: - return -1 - - def indexOfValue(self, value: int) -> int: - try: - return self._values.index(value) - except ValueError: - return -1 - - def clear(self): - self._keys.clear() - self._values.clear() - - def append(self, key: int, value: int): - self.put(key, value) - - def toString(self) -> str: - pairs = ", ".join(f"{k}={v}" for k, v in zip(self._keys, self._values)) - return f"SparseIntArray{{{pairs}}}" - - -class SparseBooleanArray(Stub): - """SparseBooleanArray — int 到 bool 的映射""" - - def __init__(self, initial_capacity: int = 10): - super().__init__() - self._keys: List[int] = [] - self._values: List[bool] = [] - - def put(self, key: int, value: bool): - try: - idx = self._keys.index(key) - self._values[idx] = value - except ValueError: - self._keys.append(key) - self._values.append(value) - - def get(self, key: int, default: bool = False) -> bool: - try: - idx = self._keys.index(key) - return self._values[idx] - except ValueError: - return default - - def delete(self, key: int): - try: - idx = self._keys.index(key) - self._keys.pop(idx) - self._values.pop(idx) - except ValueError: - pass - - def size(self) -> int: - return len(self._keys) - - def keyAt(self, index: int) -> int: - if 0 <= index < len(self._keys): - return self._keys[index] - return 0 - - def valueAt(self, index: int) -> bool: - if 0 <= index < len(self._values): - return self._values[index] - return False - - def indexOfKey(self, key: int) -> int: - try: - return self._keys.index(key) - except ValueError: - return -1 - - def indexOfValue(self, value: bool) -> int: - try: - return self._values.index(value) - except ValueError: - return -1 - - def clear(self): - self._keys.clear() - self._values.clear() - - def append(self, key: int, value: bool): - self.put(key, value) - - def toString(self) -> str: - pairs = ", ".join(f"{k}={v}" for k, v in zip(self._keys, self._values)) - return f"SparseBooleanArray{{{pairs}}}" - - -# ═══════════════════════════════════════════════════════════════ -# LruCache -# ═══════════════════════════════════════════════════════════════ - -class LruCache(Stub): - """LruCache — LRU 缓存""" - - def __init__(self, max_size: int): - super().__init__() - self._max_size = max_size - self._size = 0 - self._map: Dict[Any, Any] = {} - - def get(self, key: Any) -> Optional[Any]: - return self._map.get(key) - - def put(self, key: Any, value: Any) -> Optional[Any]: - old = self._map.get(key) - self._map[key] = value - self._size += self._safe_size_of(key, value) - if old is not None: - self._size -= self._safe_size_of(key, old) - self._trim_to_size(self._max_size) - return old - - def remove(self, key: Any) -> Optional[Any]: - value = self._map.pop(key, None) - if value is not None: - self._size -= self._safe_size_of(key, value) - return value - - def size(self) -> int: - return self._size - - def maxSize(self) -> int: - return self._max_size - - def trimToSize(self, max_size: int): - self._trim_to_size(max_size) - - def _trim_to_size(self, max_size: int): - while self._size > max_size and self._map: - # Remove oldest (first key) - oldest_key = next(iter(self._map)) - self.remove(oldest_key) - - def snapshot(self) -> Dict[Any, Any]: - return dict(self._map) - - def evictAll(self): - self._map.clear() - self._size = 0 - - def _safe_size_of(self, key: Any, value: Any) -> int: - try: - return self.sizeOf(key, value) - except Exception: - return 1 - - def sizeOf(self, key: Any, value: Any) -> int: - return 1 - - def entryRemoved(self, evicted: bool, key: Any, old_value: Any, new_value: Optional[Any]): - pass - - def create(self, key: Any) -> Optional[Any]: - return None - - def getCount(self) -> int: - return len(self._map) - - def putCount(self) -> int: - return len(self._map) - - def evictionCount(self) -> int: - return 0 - - def hitCount(self) -> int: - return 0 - - def missCount(self) -> int: - return 0 - - def toString(self) -> str: - return f"LruCache(maxSize={self._max_size}, size={self._size})" - - -# ═══════════════════════════════════════════════════════════════ -# DisplayMetrics -# ═══════════════════════════════════════════════════════════════ - -class DisplayMetrics(Stub): - """DisplayMetrics — 屏幕显示度量""" - - DENSITY_LOW = 120 - DENSITY_MEDIUM = 160 - DENSITY_TV = 213 - DENSITY_HIGH = 240 - DENSITY_XHIGH = 320 - DENSITY_XXHIGH = 480 - DENSITY_XXXHIGH = 640 - DENSITY_DEFAULT = DENSITY_MEDIUM - DENSITY_DEVICE_STABLE = DENSITY_XXXHIGH - - def __init__(self): - super().__init__() - self.widthPixels = 1080 - self.heightPixels = 1920 - self.density = 2.75 - self.densityDpi = 440 - self.scaledDensity = 2.75 - self.xdpi = 440.0 - self.ydpi = 440.0 - self.noncompatWidthPixels = 1080 - self.noncompatHeightPixels = 1920 - self.noncompatDensity = 2.75 - self.noncompatDensityDpi = 440 - self.noncompatScaledDensity = 2.75 - self.noncompatXdpi = 440.0 - self.noncompatYdpi = 440.0 - - def setTo(self, other: "DisplayMetrics"): - self.widthPixels = other.widthPixels - self.heightPixels = other.heightPixels - self.density = other.density - self.densityDpi = other.densityDpi - self.scaledDensity = other.scaledDensity - self.xdpi = other.xdpi - self.ydpi = other.ydpi - - def setToDefaults(self): - self.__init__() - - def toString(self) -> str: - return (f"DisplayMetrics({self.widthPixels}x{self.heightPixels}, " - f"density={self.density}, dpi={self.densityDpi})") - - -# ═══════════════════════════════════════════════════════════════ -# TypedValue -# ═══════════════════════════════════════════════════════════════ - -class TypedValue(Stub): - """TypedValue — 类型化值的容器""" - - COMPLEX_UNIT_PX = 0 - COMPLEX_UNIT_DIP = 1 - COMPLEX_UNIT_SP = 2 - COMPLEX_UNIT_PT = 3 - COMPLEX_UNIT_IN = 4 - COMPLEX_UNIT_MM = 5 - COMPLEX_UNIT_FRACTION = 6 - COMPLEX_UNIT_FRACTION_PARENT = 7 - - COMPLEX_UNIT_SHIFT = 0 - COMPLEX_UNIT_MASK = 0xF - - COMPLEX_RADIX_SHIFT = 4 - COMPLEX_RADIX_MASK = 0x3 - - COMPLEX_MANTISSA_SHIFT = 8 - COMPLEX_MANTISSA_MASK = 0x00ffffff - - # 复数值的基数乘数(与 AOSP TypedValue.RADIX_MULTS 一致) - _RADIX_MULTS = ( - 0.00390625, # 1/256, radix 0 - 3.0517578e-5, # 1/32768, radix 1 - 1.1920929e-7, # 1/8388608, radix 2 - 4.6566129e-10, # 1/2147483648, radix 3 - ) - - TYPE_NULL = 0x00 - TYPE_REFERENCE = 0x01 - TYPE_ATTRIBUTE = 0x02 - TYPE_STRING = 0x03 - TYPE_FLOAT = 0x04 - TYPE_DIMENSION = 0x05 - TYPE_FRACTION = 0x06 - TYPE_FIRST_INT = 0x10 - TYPE_INT_DEC = 0x10 - TYPE_INT_HEX = 0x11 - TYPE_INT_BOOLEAN = 0x12 - TYPE_INT_COLOR_ARGB8 = 0x1C - TYPE_INT_COLOR_RGB8 = 0x1D - TYPE_INT_COLOR_ARGB4 = 0x1E - TYPE_INT_COLOR_RGB4 = 0x1F - TYPE_LAST_COLOR_INT = 0x1F - TYPE_LAST_INT = 0x1F - - DATA_NULL_UNDEFINED = 0 - DATA_NULL_EMPTY = 1 - - def __init__(self): - super().__init__() - self.type = self.TYPE_NULL - self.data = 0 - self.assetCookie = 0 - self.resourceId = 0 - self.changingConfigurations = 0 - self.string = None - self.density = 0 - - @staticmethod - def applyDimension(unit: int, value: float, metrics: DisplayMetrics) -> float: - if unit == TypedValue.COMPLEX_UNIT_PX: - return value - if unit == TypedValue.COMPLEX_UNIT_DIP: - return value * metrics.density - if unit == TypedValue.COMPLEX_UNIT_SP: - return value * metrics.scaledDensity - if unit == TypedValue.COMPLEX_UNIT_PT: - return value * metrics.xdpi * (1.0 / 72) - if unit == TypedValue.COMPLEX_UNIT_IN: - return value * metrics.xdpi - if unit == TypedValue.COMPLEX_UNIT_MM: - return value * metrics.xdpi * (1.0 / 25.4) - return value - - @staticmethod - def complexToDimension(data: int, metrics: DisplayMetrics) -> float: - """将复数维度值转换为像素值。 - 复数值格式:bits 0-3=unit, bits 4-5=radix, bits 8-31=mantissa(有符号24位)""" - unit = (data >> TypedValue.COMPLEX_UNIT_SHIFT) & TypedValue.COMPLEX_UNIT_MASK - radix = (data >> TypedValue.COMPLEX_RADIX_SHIFT) & TypedValue.COMPLEX_RADIX_MASK - mantissa = (data >> TypedValue.COMPLEX_MANTISSA_SHIFT) & TypedValue.COMPLEX_MANTISSA_MASK - # 24 位有符号数符号扩展 - if mantissa & 0x800000: - mantissa = mantissa - 0x1000000 - value = mantissa * TypedValue._RADIX_MULTS[radix] - return TypedValue.applyDimension(unit, value, metrics) - - @staticmethod - def complexToFloat(data: int) -> float: - """将复数值转换为浮点数(与 AOSP complexToFloat 一致)。""" - radix = (data >> TypedValue.COMPLEX_RADIX_SHIFT) & TypedValue.COMPLEX_RADIX_MASK - # mantissa 保持在原始位位置(左移 8 位),与 AOSP 一致 - mantissa = data & (TypedValue.COMPLEX_MANTISSA_MASK << TypedValue.COMPLEX_MANTISSA_SHIFT) - # 处理符号(Java int 是有符号的,bit 31 为符号位) - if mantissa & 0x80000000: - mantissa = mantissa - 0x100000000 - return float(mantissa) * TypedValue._RADIX_MULTS[radix] - - @staticmethod - def complexToDimensionPixelOffset(data: int, metrics: DisplayMetrics) -> int: - return int(TypedValue.complexToDimension(data, metrics)) - - @staticmethod - def complexToDimensionPixelSize(data: int, metrics: DisplayMetrics) -> int: - val = TypedValue.complexToDimension(data, metrics) - if val >= 0: - return int(val + 0.5) - return int(val - 0.5) - - def getFloat(self) -> float: - """将 data 字段按 IEEE 754 单精度浮点数解读(与 Float.intBitsToFloat 一致)。""" - bits = self.data & 0xFFFFFFFF - return struct.unpack('f', struct.pack('I', bits))[0] - - def toString(self) -> str: - return f"TypedValue(type={self.type}, data={self.data})" - - -# ═══════════════════════════════════════════════════════════════ -# Pair -# ═══════════════════════════════════════════════════════════════ - -class Pair(Stub): - """Pair — 有序对""" - - def __init__(self, first: Any, second: Any): - super().__init__() - self.first = first - self.second = second - - @staticmethod - def create(a: Any, b: Any) -> "Pair": - return Pair(a, b) - - def hashCode(self) -> int: - return hash((self.first, self.second)) - - def equals(self, other: Any) -> bool: - if not isinstance(other, Pair): - return False - return self.first == other.first and self.second == other.second - - def toString(self) -> str: - return f"Pair({self.first}, {self.second})" - - def __eq__(self, other): - return self.equals(other) - - def __hash__(self): - return self.hashCode() - - def __repr__(self): - return self.toString() - - def __iter__(self): - return iter((self.first, self.second)) - - -# ═══════════════════════════════════════════════════════════════ -# Size / SizeF -# ═══════════════════════════════════════════════════════════════ - -class Size(Stub): - """Size — 整数尺寸""" - - def __init__(self, width: int, height: int): - super().__init__() - self._width = width - self._height = height - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def equals(self, other: Any) -> bool: - if not isinstance(other, Size): - return False - return self._width == other._width and self._height == other._height - - def hashCode(self) -> int: - return (self._height << 16) ^ self._width - - def toString(self) -> str: - return f"{self._width}x{self._height}" - - @staticmethod - def parseSize(string: str) -> "Size": - parts = string.split("x") - if len(parts) == 2: - return Size(int(parts[0]), int(parts[1])) - raise ValueError(f"Invalid size: {string}") - - def __eq__(self, other): - return self.equals(other) - - def __hash__(self): - return self.hashCode() - - def __repr__(self): - return self.toString() - - -class SizeF(Stub): - """SizeF — 浮点数尺寸""" - - def __init__(self, width: float, height: float): - super().__init__() - self._width = width - self._height = height - - def getWidth(self) -> float: - return self._width - - def getHeight(self) -> float: - return self._height - - def equals(self, other: Any) -> bool: - if not isinstance(other, SizeF): - return False - return abs(self._width - other._width) < 0.001 and abs(self._height - other._height) < 0.001 - - def hashCode(self) -> int: - return hash((self._width, self._height)) - - def toString(self) -> str: - return f"{self._width:.1f}x{self._height:.1f}" - - @staticmethod - def parseSizeF(string: str) -> "SizeF": - parts = string.split("x") - if len(parts) == 2: - return SizeF(float(parts[0]), float(parts[1])) - raise ValueError(f"Invalid size: {string}") - - def __eq__(self, other): - return self.equals(other) - - def __hash__(self): - return self.hashCode() - - def __repr__(self): - return self.toString() - - -# ═══════════════════════════════════════════════════════════════ -# StateSet -# ═══════════════════════════════════════════════════════════════ - -class StateSet(Stub): - """StateSet — 状态集合""" - - VIEW_STATE_WINDOW_FOCUSED = 1 - VIEW_STATE_SELECTED = 1 << 1 - VIEW_STATE_FOCUSED = 1 << 2 - VIEW_STATE_ENABLED = 1 << 3 - VIEW_STATE_PRESSED = 1 << 4 - VIEW_STATE_ACTIVATED = 1 << 5 - VIEW_STATE_ACCELERATED = 1 << 6 - VIEW_STATE_HOVERED = 1 << 7 - VIEW_STATE_DRAG_CAN_ACCEPT = 1 << 8 - VIEW_STATE_DRAG_HOVERED = 1 << 9 - - WILD_CARD = 0x00000000 - - @staticmethod - def isWildCard(state_spec_or_mask: Any) -> bool: - if isinstance(state_spec_or_mask, list): - return len(state_spec_or_mask) == 0 - return state_spec_or_mask == StateSet.WILD_CARD - - @staticmethod - def trimStateSet(states: List[int], new_size: int) -> List[int]: - return states[:new_size] - - @staticmethod - def dump(states: List[int]) -> str: - if not states: - return "[]" - return "[" + ", ".join(str(s) for s in states) + "]" - - -# ═══════════════════════════════════════════════════════════════ -# MathUtils -# ═══════════════════════════════════════════════════════════════ - -class MathUtils(Stub): - """MathUtils — 数学工具""" - - DEG_TO_RAD = math.pi / 180.0 - RAD_TO_DEG = 180.0 / math.pi - - @staticmethod - def abs(v: float) -> float: - return abs(v) - - @staticmethod - def constrain(amount: float, low: float, high: float) -> float: - return max(low, min(high, amount)) - - @staticmethod - def constrainInt(amount: int, low: int, high: int) -> int: - return max(low, min(high, amount)) - - @staticmethod - def lerp(start: float, stop: float, amount: float) -> float: - return start + (stop - start) * amount - - @staticmethod - def lerpInv(a: float, b: float, value: float) -> float: - if a != b: - return (value - a) / (b - a) - return 0.0 - - @staticmethod - def norm(start: float, stop: float, value: float) -> float: - return MathUtils.lerpInv(start, stop, value) - - @staticmethod - def map(min_start: float, min_stop: float, max_start: float, max_stop: float, value: float) -> float: - return max_start + (value - min_start) * (max_stop - max_start) / (min_stop - min_start) - - @staticmethod - def dist(x1: float, y1: float, x2: float, y2: float) -> float: - return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) - - @staticmethod - def max(var1: float, var2: float) -> float: - return max(var1, var2) - - @staticmethod - def min(var1: float, var2: float) -> float: - return min(var1, var2) - - @staticmethod - def degrees(radians: float) -> float: - return math.degrees(radians) - - @staticmethod - def radians(degrees: float) -> float: - return math.radians(degrees) - - @staticmethod - def exp(a: float) -> float: - return math.exp(a) - - @staticmethod - def log(a: float) -> float: - return math.log(a) - - @staticmethod - def pow(a: float, b: float) -> float: - return a ** b - - @staticmethod - def sqrt(a: float) -> float: - return math.sqrt(a) - - @staticmethod - def sin(angle: float) -> float: - return math.sin(angle) - - @staticmethod - def cos(angle: float) -> float: - return math.cos(angle) - - @staticmethod - def tan(angle: float) -> float: - return math.tan(angle) - - @staticmethod - def atan2(a: float, b: float) -> float: - return math.atan2(a, b) - - @staticmethod - def random() -> float: - import random - return random.random() - - @staticmethod - def isNaN(v: float) -> bool: - return math.isnan(v) - - @staticmethod - def nanToZero(value: float) -> float: - return 0.0 if math.isnan(value) else value - - -# ═══════════════════════════════════════════════════════════════ -# Pattern (re) -# ═══════════════════════════════════════════════════════════════ - -class Pattern(Stub): - """Pattern — 正则表达式模式""" - - CASE_INSENSITIVE = 2 - MULTILINE = 8 - DOTALL = 32 - UNICODE_CASE = 64 - CANON_EQ = 128 - UNIX_LINES = 1 - LITERAL = 16 - COMMENTS = 4 - - def __init__(self, pattern: str, flags: int = 0): - super().__init__() - self._pattern_str = pattern - self._flags = flags - re_flags = 0 - if flags & self.CASE_INSENSITIVE: - re_flags |= _re.IGNORECASE - if flags & self.MULTILINE: - re_flags |= _re.MULTILINE - if flags & self.DOTALL: - re_flags |= _re.DOTALL - if flags & self.UNICODE_CASE: - re_flags |= _re.UNICODE - if flags & self.UNIX_LINES: - pass # no re equivalent - self._compiled = _re.compile(pattern, re_flags) - - @staticmethod - def compile(pattern: str, flags: int = 0) -> "Pattern": - return Pattern(pattern, flags) - - def matcher(self, input_str: str) -> "Matcher": - return Matcher(self._compiled, input_str) - - def split(self, input_str: str, limit: int = 0) -> List[str]: - if limit < 0: - return self._compiled.split(input_str, maxsplit=0) - return self._compiled.split(input_str, maxsplit=limit - 1 if limit > 0 else 0) - - def pattern(self) -> str: - return self._pattern_str - - def flags(self) -> int: - return self._flags - - @staticmethod - def matches(regex: str, input_str: str) -> bool: - return bool(_re.fullmatch(regex, input_str)) - - @staticmethod - def quote(s: str) -> str: - return _re.escape(s) - - def toString(self) -> str: - return self._pattern_str - - -class Matcher(Stub): - """Matcher — 正则匹配器""" - - def __init__(self, compiled_pattern: "Pattern", input_str: str): - super().__init__() - self._pattern = compiled_pattern - self._input = input_str - self._region_start = 0 - self._region_end = len(input_str) - self._match: Optional[Any] = None - - def matches(self) -> bool: - self._match = self._pattern.fullmatch(self._input, - self._region_start, self._region_end) - return self._match is not None - - def find(self, start: int = 0) -> bool: - self._match = self._pattern.search(self._input, start or self._region_start, - self._region_end) - return self._match is not None - - def lookingAt(self) -> bool: - self._match = self._pattern.match(self._input, self._region_start, - self._region_end) - return self._match is not None - - def group(self, group: int = 0) -> Optional[str]: - if self._match: - return self._match.group(group) - return None - - def groupCount(self) -> int: - if self._match: - return len(self._match.groups()) - return 0 - - def start(self, group: int = 0) -> int: - if self._match: - return self._match.start(group) - return -1 - - def end(self, group: int = 0) -> int: - if self._match: - return self._match.end(group) - return -1 - - def replaceAll(self, replacement: str) -> str: - return self._pattern.sub(replacement, self._input) - - def replaceFirst(self, replacement: str) -> str: - return self._pattern.sub(replacement, self._input, count=1) - - def region(self, start: int, end: int) -> "Matcher": - self._region_start = start - self._region_end = end - return self - - def regionStart(self) -> int: - return self._region_start - - def regionEnd(self) -> int: - return self._region_end - - def reset(self, input_str: Optional[str] = None) -> "Matcher": - if input_str is not None: - self._input = input_str - self._match = None - return self - - def hitEnd(self) -> bool: - return False - - def requireEnd(self) -> bool: - return False - - def usePattern(self, new_pattern: "Pattern") -> "Matcher": - self._pattern = new_pattern - return self - - def appendReplacement(self, sb: Any, replacement: str): - pass - - def appendTail(self, sb: Any): - pass - - def toString(self) -> str: - return f"Matcher({self._pattern.pattern()})" - - -# ═══════════════════════════════════════════════════════════════ -# TYPED_VALUE 常量 -# ═══════════════════════════════════════════════════════════════ - -TYPED_VALUE = TypedValue # 别名 - - -# ═══════════════════════════════════════════════════════════════ -# Pair 反向兼容 (android.util.Pair) -# ═══════════════════════════════════════════════════════════════ - -# Pair 已在上面定义 - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Log", - "SparseArray", - "SparseIntArray", - "SparseBooleanArray", - "LruCache", - "DisplayMetrics", - "TypedValue", - "Pair", - "Size", - "SizeF", - "StateSet", - "MathUtils", - "Pattern", - "Matcher", - "TYPED_VALUE", -] \ No newline at end of file diff --git a/android_pyapi/android/uwb.py b/android_pyapi/android/uwb.py deleted file mode 100644 index 5b9cde9..0000000 --- a/android_pyapi/android/uwb.py +++ /dev/null @@ -1,420 +0,0 @@ -""" -Android uwb 组件 — UwbManager, RangingResult, RangingParameters, UwbAddress 等 -方法签名完全对齐 Android SDK (UWB API) -""" - -from typing import Any, Callable, Dict, List, Optional - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# UwbConstants -# ═══════════════════════════════════════════════════════════════ - -class UwbConstants(Stub): - """UwbConstants — UWB 常量""" - - STATUS_OK = 0 - STATUS_FAILURE = 1 - STATUS_INVALID_PARAMETERS = 2 - STATUS_SESSION_DOES_NOT_EXIST = 3 - STATUS_MAX_RANGING_ROUND_RETRY = 4 - STATUS_RANGING_TX_FAILED = 5 - STATUS_SESSION_STATE_CHANGE_EXCEPTION = 6 - STATUS_UNKNOWN_ERROR = 255 - - CONFIG_ID_0 = 0 - CONFIG_ID_1 = 1 - CONFIG_ID_2 = 2 - CONFIG_ID_3 = 3 - - MEASUREMENT_REPORT_TYPE_DEFERRED = 0 - MEASUREMENT_REPORT_TYPE_NTF = 1 - - RANGING_UPDATE_RATE_AUTO = 0 - RANGING_UPDATE_RATE_FREQUENCY_1_HZ = 1 - RANGING_UPDATE_RATE_FREQUENCY_2_HZ = 2 - RANGING_UPDATE_RATE_FREQUENCY_5_HZ = 5 - RANGING_UPDATE_RATE_FREQUENCY_10_HZ = 10 - RANGING_UPDATE_RATE_FREQUENCY_20_HZ = 20 - RANGING_UPDATE_RATE_FREQUENCY_100_HZ = 100 - RANGING_UPDATE_RATE_FREQUENCY_250_HZ = 250 - - -# ═══════════════════════════════════════════════════════════════ -# UwbAddress -# ═══════════════════════════════════════════════════════════════ - -class UwbAddress(Stub): - """UwbAddress — UWB 地址""" - - SHORT_MAC_ADDRESS = 0 - EXTENDED_MAC_ADDRESS = 1 - - def __init__(self, address: bytes = b"", type_: int = SHORT_MAC_ADDRESS): - super().__init__() - self._address = address - self._type = type_ - - def getAddress(self) -> bytes: - return self._address - - def getType(self) -> int: - return self._type - - def toString(self) -> str: - return f"UwbAddress(type={self._type}, addr={self._address.hex()})" - - -# ═══════════════════════════════════════════════════════════════ -# AngleOfArrival -# ═══════════════════════════════════════════════════════════════ - -class AngleOfArrival(Stub): - """AngleOfArrival — 到达角""" - - STATUS_SUCCESS = 0 - STATUS_FAILURE = 1 - - def __init__(self, azimuth: float = 0.0, elevation: float = 0.0, - azimuthConfidence: float = 0.0, elevationConfidence: float = 0.0, - status: int = STATUS_FAILURE): - super().__init__() - self._azimuth = azimuth - self._elevation = elevation - self._azimuth_confidence = azimuthConfidence - self._elevation_confidence = elevationConfidence - self._status = status - - def getAzimuth(self) -> float: - return self._azimuth - - def getElevation(self) -> float: - return self._elevation - - def getAzimuthConfidence(self) -> float: - return self._azimuth_confidence - - def getElevationConfidence(self) -> float: - return self._elevation_confidence - - -# ═══════════════════════════════════════════════════════════════ -# DistanceMeasurement -# ═══════════════════════════════════════════════════════════════ - -class DistanceMeasurement(Stub): - """DistanceMeasurement — 距离测量""" - - STATUS_SUCCESS = 0 - STATUS_FAILURE = 1 - - def __init__(self, meters: float = 0.0, confidence: float = 0.0, - standardDeviation: float = 0.0, status: int = STATUS_FAILURE): - super().__init__() - self._meters = meters - self._confidence = confidence - self._standard_deviation = standardDeviation - self._status = status - - def getMeters(self) -> float: - return self._meters - - def getConfidence(self) -> float: - return self._confidence - - def getStandardDeviation(self) -> float: - return self._standard_deviation - - -# ═══════════════════════════════════════════════════════════════ -# RangingResult -# ═══════════════════════════════════════════════════════════════ - -class RangingResult(Stub): - """RangingResult — 测距结果""" - - def __init__(self, uwbDevice: Any = None, remoteDevice: Any = None, - status: int = 0, nofRounds: int = 0, nofMeasurements: int = 0, - elapsedRealtimeNanos: int = 0, - distanceMeasurement: Optional[DistanceMeasurement] = None, - angleOfArrival: Optional[AngleOfArrival] = None, - rssi: float = -90.0): - super().__init__() - self._uwb_device = uwbDevice - self._remote_device = remoteDevice - self._status = status - self._nof_rounds = nofRounds - self._nof_measurements = nofMeasurements - self._elapsed_realtime_nanos = elapsedRealtimeNanos - self._distance_measurement = distanceMeasurement - self._angle_of_arrival = angleOfArrival - self._rssi = rssi - self._distance_reported = distanceMeasurement is not None - self._aoa_reported = angleOfArrival is not None - - def getUwbDevice(self) -> Any: - return self._uwb_device - - def getRemoteDevice(self) -> Any: - return self._remote_device - - def getStatus(self) -> int: - return self._status - - def getNofRounds(self) -> int: - return self._nof_rounds - - def getNofMeasurements(self) -> int: - return self._nof_measurements - - def getElapsedRealtimeNanos(self) -> int: - return self._elapsed_realtime_nanos - - def getDistanceMeasurement(self) -> Optional[DistanceMeasurement]: - return self._distance_measurement - - def getAngleOfArrival(self) -> Optional[AngleOfArrival]: - return self._angle_of_arrival - - def getAngleOfArrivalAzimuth(self) -> float: - if self._angle_of_arrival: - return self._angle_of_arrival.getAzimuth() - return 0.0 - - def getAngleOfArrivalElevation(self) -> float: - if self._angle_of_arrival: - return self._angle_of_arrival.getElevation() - return 0.0 - - def getAngleOfArrivalAzimuthConfidence(self) -> float: - if self._angle_of_arrival: - return self._angle_of_arrival.getAzimuthConfidence() - return 0.0 - - def getAngleOfArrivalElevationConfidence(self) -> float: - if self._angle_of_arrival: - return self._angle_of_arrival.getElevationConfidence() - return 0.0 - - def getRssi(self) -> float: - return self._rssi - - def isDistanceMeasurementReported(self) -> bool: - return self._distance_reported - - def isAngleOfArrivalReported(self) -> bool: - return self._aoa_reported - - -# ═══════════════════════════════════════════════════════════════ -# RangingParameters -# ═══════════════════════════════════════════════════════════════ - -class RangingParameters(Stub): - """RangingParameters — 测距参数""" - - class Builder(Stub): - """RangingParameters.Builder — 测距参数构建器""" - - def __init__(self): - super().__init__() - self._uwb_config_id = 0 - self._session_id = 0 - self._sub_session_id = 0 - self._ranging_update_rate = 1 - self._slot_duration = 0 - self._measurement_report_type = 0 - self._number_of_controlee = 1 - self._destination_addresses: List[UwbAddress] = [] - self._device_configs: List[Any] = [] - self._vendor_specific_data: List[Any] = [] - - def setUwbConfigId(self, configId: int) -> "RangingParameters.Builder": - self._uwb_config_id = configId - return self - - def setSessionId(self, sessionId: int) -> "RangingParameters.Builder": - self._session_id = sessionId - return self - - def setSubSessionId(self, subSessionId: int) -> "RangingParameters.Builder": - self._sub_session_id = subSessionId - return self - - def setRangingUpdateRate(self, rate: int) -> "RangingParameters.Builder": - self._ranging_update_rate = rate - return self - - def setSlotDuration(self, duration: int) -> "RangingParameters.Builder": - self._slot_duration = duration - return self - - def setMeasurementReportType(self, reportType: int) -> "RangingParameters.Builder": - self._measurement_report_type = reportType - return self - - def setNumberOfControlee(self, count: int) -> "RangingParameters.Builder": - self._number_of_controlee = count - return self - - def setDestinationAddresses(self, addrs: List[UwbAddress]) -> "RangingParameters.Builder": - self._destination_addresses = addrs - return self - - def setDeviceConfigs(self, configs: List[Any]) -> "RangingParameters.Builder": - self._device_configs = configs - return self - - def setVendorSpecificData(self, data: List[Any]) -> "RangingParameters.Builder": - self._vendor_specific_data = data - return self - - def build(self) -> "RangingParameters": - rp = RangingParameters() - rp._uwb_config_id = self._uwb_config_id - rp._session_id = self._session_id - rp._sub_session_id = self._sub_session_id - rp._ranging_update_rate = self._ranging_update_rate - rp._slot_duration = self._slot_duration - rp._measurement_report_type = self._measurement_report_type - rp._number_of_controlee = self._number_of_controlee - rp._destination_addresses = self._destination_addresses - rp._device_configs = self._device_configs - rp._vendor_specific_data = self._vendor_specific_data - return rp - - def __init__(self): - super().__init__() - - -# ═══════════════════════════════════════════════════════════════ -# UwbConfiguration / RangingConfig (stubs) -# ═══════════════════════════════════════════════════════════════ - -class UwbConfiguration(Stub): - """UwbConfiguration — UWB 配置""" - - def __init__(self): - super().__init__() - - -class RangingConfig(Stub): - """RangingConfig — 测距配置""" - - def __init__(self): - super().__init__() - - -# ═══════════════════════════════════════════════════════════════ -# RangingSession -# ═══════════════════════════════════════════════════════════════ - -class RangingSession(Stub): - """RangingSession — 测距会话""" - - def __init__(self): - super().__init__() - self._open = False - - def open(self): - self._open = True - - def close(self): - self._open = False - - def reconfigure(self, params: RangingParameters): - pass - - -# ═══════════════════════════════════════════════════════════════ -# UwbManager -# ═══════════════════════════════════════════════════════════════ - -class UwbManager(Stub): - """UwbManager — UWB 管理器""" - - # ═══════════════════════════════════════════════════════════ - # UwbCallback - # ═══════════════════════════════════════════════════════════ - - class UwbCallback(Stub): - """UwbManager.UwbCallback — UWB 回调""" - - def onRangingResult(self, sessionId: int, result: RangingResult): - pass - - def onRangingFailure(self, sessionId: int, reason: int): - pass - - def onControleeResult(self, sessionId: int, status: int): - pass - - _instance: Optional["UwbManager"] = None - - def __init__(self): - super().__init__() - - @staticmethod - def getInstance(context: Any = None) -> "UwbManager": - if UwbManager._instance is None: - UwbManager._instance = UwbManager() - return UwbManager._instance - - def open(self, sessionId: int) -> RangingSession: - session = RangingSession() - session.open() - return session - - def close(self, sessionId: int): - pass - - def range(self, sessionId: int, params: RangingParameters, - callback: UwbCallback): - if callback: - result = RangingResult( - status=UwbConstants.STATUS_OK, - distanceMeasurement=DistanceMeasurement( - meters=1.5, confidence=0.8, - standardDeviation=0.1, status=DistanceMeasurement.STATUS_SUCCESS - ), - rssi=-65.0 - ) - callback.onRangingResult(sessionId, result) - - def stopRange(self, sessionId: int): - pass - - def addControlee(self, sessionId: int, address: UwbAddress): - pass - - def removeControlee(self, sessionId: int, address: UwbAddress): - pass - - def setMeasurementData(self, sessionId: int, data: bytes): - pass - - def sendMeasurementData(self, sessionId: int, data: bytes): - pass - - def getUwbDevice(self) -> Any: - return None - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "UwbManager", - "UwbConstants", - "UwbAddress", - "AngleOfArrival", - "DistanceMeasurement", - "RangingResult", - "RangingParameters", - "UwbConfiguration", - "RangingConfig", - "RangingSession", -] \ No newline at end of file diff --git a/android_pyapi/android/view.py b/android_pyapi/android/view.py deleted file mode 100644 index 5e0b481..0000000 --- a/android_pyapi/android/view.py +++ /dev/null @@ -1,4207 +0,0 @@ -""" -Android View 组件 — View, ViewGroup, MotionEvent, KeyEvent, GestureDetector 等 -方法签名完全对齐 Android SDK -""" - -import math -import time -import weakref -from typing import Any, Callable, Dict, List, Optional, Tuple, Union - -from .._core import Stub, AndroidOnlyError, android_context, HAS_PYAUTOGUI - -try: - from .._linux_impl import IS_LINUX, LinuxDisplay, LinuxUIAutomation, HAS_XDOTOOL - _USE_LINUX = IS_LINUX -except ImportError: - _USE_LINUX = False - - -# ═══════════════════════════════════════════════════════════════ -# Gravity -# ═══════════════════════════════════════════════════════════════ - -class Gravity: - """Gravity — 布局重力常量""" - NO_GRAVITY = 0x0000 - TOP = 0x0030 - BOTTOM = 0x0050 - LEFT = 0x0003 - RIGHT = 0x0005 - CENTER = 0x0011 - CENTER_HORIZONTAL = 0x0001 - CENTER_VERTICAL = 0x0010 - FILL = 0x0077 - FILL_HORIZONTAL = 0x0007 - FILL_VERTICAL = 0x0070 - CLIP_HORIZONTAL = 0x0008 - CLIP_VERTICAL = 0x0080 - START = 0x00800001 - END = 0x00800005 - RELATIVE_LAYOUT_DIRECTION = 0x00800000 - - @staticmethod - def getAbsoluteGravity(gravity: int, layout_direction: int) -> int: - from ..os import View - if layout_direction == View.LAYOUT_DIRECTION_RTL: - if gravity & Gravity.START: - gravity = (gravity & ~Gravity.START) | Gravity.RIGHT - if gravity & Gravity.END: - gravity = (gravity & ~Gravity.END) | Gravity.LEFT - else: - if gravity & Gravity.START: - gravity = (gravity & ~Gravity.START) | Gravity.LEFT - if gravity & Gravity.END: - gravity = (gravity & ~Gravity.END) | Gravity.RIGHT - return gravity - - @staticmethod - def apply(gravity: int, w: int, h: int, container: "Rect") -> int: - return 0 - - -Rect = None - - -def _set_rect_class(): - global Rect - if Rect is None: - class _Rect: - def __init__(self, l=0, t=0, r=0, b=0): - self.left = l - self.top = t - self.right = r - self.bottom = b - def width(self): return self.right - self.left - def height(self): return self.bottom - self.top - def set(self, l, t, r, b): - self.left, self.top, self.right, self.bottom = l, t, r, b - def isEmpty(self): return self.width() <= 0 or self.height() <= 0 - def toString(self): return f"Rect({self.left}, {self.top} - {self.right}, {self.bottom})" - Rect = _Rect - - -_set_rect_class() - - -# ═══════════════════════════════════════════════════════════════ -# View -# ═══════════════════════════════════════════════════════════════ - -class View(Stub): - """View — 所有 UI 组件的基类""" - - # 可见性常量 - VISIBLE = 0 - INVISIBLE = 4 - GONE = 8 - - # 测量常量 - MEASURED_SIZE_STATE_MASK = 0xFF000000 - MEASURED_STATE_MASK = 0xFF000000 - MEASURED_HEIGHT_STATE_SHIFT = 16 - MEASURED_WIDTH_STATE_SHIFT = 0 - - # 测量模式 - EXACTLY = 0x40000000 - AT_MOST = 0x80000000 # -2147483648 - UNSPECIFIED = 0 - - # 滚动轴 - SCROLL_AXIS_NONE = 0 - SCROLL_AXIS_HORIZONTAL = 1 - SCROLL_AXIS_VERTICAL = 2 - - # 布局方向 - LAYOUT_DIRECTION_LTR = 0 - LAYOUT_DIRECTION_RTL = 1 - - # 绘制缓存 - DRAWING_CACHE_QUALITY_LOW = 0x00040000 - DRAWING_CACHE_QUALITY_HIGH = 0x00080000 - DRAWING_CACHE_QUALITY_AUTO = 0 - - # 焦点方向 - FOCUS_BACKWARD = 0x00000001 - FOCUS_FORWARD = 0x00000002 - FOCUS_LEFT = 0x00000011 - FOCUS_UP = 0x00000021 - FOCUS_RIGHT = 0x00000042 - FOCUS_DOWN = 0x00000082 - - # Keep screen on - KEEP_SCREEN_ON = 0x02000000 - - # 状态 - STATUS_BAR_HIDDEN = 0x00000001 - STATUS_BAR_VISIBLE = 0x00000000 - NAVIGATION_BAR_HIDDEN = 0x00000002 - NAVIGATION_BAR_VISIBLE = 0x00000000 - - # 系统UI可见性 - SYSTEM_UI_FLAG_VISIBLE = 0 - SYSTEM_UI_FLAG_LOW_PROFILE = 0x00000001 - SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002 - SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004 - SYSTEM_UI_FLAG_LAYOUT_STABLE = 0x00000100 - SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200 - SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400 - SYSTEM_UI_FLAG_IMMERSIVE = 0x00000800 - SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 0x00001000 - - _next_id = 0x10000000 - _id_lock = None - - def __init__(self, context=None): - super().__init__() - if context is None: - context = android_context - self._context = context - self._id = View.NO_ID - self._tag: Any = None - self._layout_params: Optional["ViewGroup.LayoutParams"] = None - self._visibility = View.VISIBLE - self._enabled = True - self._clickable = False - self._focusable = False - self._selected = False - self._activated = False - self._x: float = 0.0 - self._y: float = 0.0 - self._translation_x: float = 0.0 - self._translation_y: float = 0.0 - self._rotation: float = 0.0 - self._scale_x: float = 1.0 - self._scale_y: float = 1.0 - self._alpha: float = 1.0 - self._pivot_x: float = 0.0 - self._pivot_y: float = 0.0 - self._measured_width: int = 0 - self._measured_height: int = 0 - self._left: int = 0 - self._top: int = 0 - self._right: int = 0 - self._bottom: int = 0 - self._padding_left: int = 0 - self._padding_top: int = 0 - self._padding_right: int = 0 - self._padding_bottom: int = 0 - self._parent: Optional["ViewGroup"] = None - self._background_color: Optional[int] = None - self._background_drawable: Any = None - self._background_resource: int = 0 - self._on_click_listener: Optional[Callable] = None - self._on_long_click_listener: Optional[Callable] = None - self._on_touch_listener: Optional[Callable] = None - self._on_key_listener: Optional[Callable] = None - self._callbacks: Dict[Any, Tuple[Callable, float]] = {} - self._scroll_x: int = 0 - self._scroll_y: int = 0 - self._will_not_draw = False - self._layer_type = View.LAYER_TYPE_NONE - self._layout_direction = View.LAYOUT_DIRECTION_LTR - self._min_width: int = 0 - self._min_height: int = 0 - self._elevation: float = 0.0 - self._clickable = False - self._long_clickable = False - self._context_clickable = False - self._children: List["View"] = [] - - # ─── 常量 ────────────────────────────────────────────────── - - NO_ID = -1 - DRAWING_CACHE_QUALITY_LOW = 0x00040000 - DRAWING_CACHE_QUALITY_HIGH = 0x00080000 - DRAWING_CACHE_QUALITY_AUTO = 0 - LAYER_TYPE_NONE = 0 - LAYER_TYPE_SOFTWARE = 1 - LAYER_TYPE_HARDWARE = 2 - FADING_EDGE_NONE = 0 - FADING_EDGE_HORIZONTAL = 0x00001000 - FADING_EDGE_VERTICAL = 0x00002000 - OVER_SCROLL_ALWAYS = 0 - OVER_SCROLL_IF_CONTENT_SCROLLS = 1 - OVER_SCROLL_NEVER = 2 - - # ─── id / tag ────────────────────────────────────────────── - - def setId(self, id_: int): - self._id = id_ - - def getId(self) -> int: - return self._id - - def setTag(self, tag: Any): - self._tag = tag - - def getTag(self) -> Any: - return self._tag - - # ─── layout params ───────────────────────────────────────── - - def setLayoutParams(self, params: "ViewGroup.LayoutParams"): - self._layout_params = params - - def getLayoutParams(self) -> Optional["ViewGroup.LayoutParams"]: - return self._layout_params - - # ─── visibility ──────────────────────────────────────────── - - def setVisibility(self, visibility: int): - self._visibility = visibility - - def getVisibility(self) -> int: - return self._visibility - - def isShown(self) -> bool: - return self._visibility == View.VISIBLE - - # ─── position / size ─────────────────────────────────────── - - def getX(self) -> float: - return self._x - - def setX(self, x: float): - self._x = x - - def getY(self) -> float: - return self._y - - def setY(self, y: float): - self._y = y - - def getWidth(self) -> int: - return self._right - self._left - - def getHeight(self) -> int: - return self._bottom - self._top - - def getLeft(self) -> int: - return self._left - - def getTop(self) -> int: - return self._top - - def getRight(self) -> int: - return self._right - - def getBottom(self) -> int: - return self._bottom - - def getMeasuredWidth(self) -> int: - return self._measured_width - - def getMeasuredHeight(self) -> int: - return self._measured_height - - def setFrame(self, left: int, top: int, right: int, bottom: int) -> bool: - changed = (left != self._left or top != self._top or - right != self._right or bottom != self._bottom) - self._left, self._top, self._right, self._bottom = left, top, right, bottom - return changed - - def layout(self, l: int, t: int, r: int, b: int): - old_l, old_t, old_r, old_b = self._left, self._top, self._right, self._bottom - changed = self.setFrame(l, t, r, b) - if changed: - self.onLayout(changed, l - old_l, t - old_t, r - old_r, b - old_b) - - def onLayout(self, changed: bool, left: int, top: int, right: int, bottom: int): - pass - - def measure(self, width_measure_spec: int, height_measure_spec: int): - self.onMeasure(width_measure_spec, height_measure_spec) - - def onMeasure(self, width_measure_spec: int, height_measure_spec: int): - w = View.getDefaultSize(self.getSuggestedMinimumWidth(), width_measure_spec) - h = View.getDefaultSize(self.getSuggestedMinimumHeight(), height_measure_spec) - self.setMeasuredDimension(w, h) - - def setMeasuredDimension(self, measured_width: int, measured_height: int): - self._measured_width = measured_width - self._measured_height = measured_height - - def getSuggestedMinimumWidth(self) -> int: - return self._min_width - - def getSuggestedMinimumHeight(self) -> int: - return self._min_height - - def getMinimumWidth(self) -> int: - return self._min_width - - def getMinimumHeight(self) -> int: - return self._min_height - - def setMinimumWidth(self, min_w: int): - self._min_width = min_w - - def setMinimumHeight(self, min_h: int): - self._min_height = min_h - - def draw(self, canvas: Any): - self.onDraw(canvas) - - def onDraw(self, canvas: Any): - pass - - # ─── listeners ───────────────────────────────────────────── - - def setOnClickListener(self, listener: Optional[Callable]): - self._on_click_listener = listener - - def setOnLongClickListener(self, listener: Optional[Callable]) -> None: - self._on_long_click_listener = listener - - def setOnTouchListener(self, listener: Optional[Callable]) -> None: - self._on_touch_listener = listener - - def setOnKeyListener(self, listener: Optional[Callable]) -> None: - self._on_key_listener = listener - - def callOnClick(self) -> bool: - if self._on_click_listener: - self._on_click_listener(self) - return True - return False - - def performClick(self) -> bool: - return self.callOnClick() - - def screenshot(self, path=None): - """截屏""" - if _USE_LINUX: - return LinuxDisplay.screenshot(path) - return None - - def performLongClick(self) -> bool: - if self._on_long_click_listener: - self._on_long_click_listener(self) - return True - return False - - # ─── post / delayed ──────────────────────────────────────── - - def post(self, action: Callable) -> bool: - action() - return True - - def postDelayed(self, action: Callable, delay_millis: int) -> bool: - import threading - timer = threading.Timer(delay_millis / 1000.0, action) - timer.daemon = True - timer.start() - self._callbacks[action] = (action, time.time() + delay_millis / 1000.0) - return True - - def removeCallbacks(self, action: Optional[Callable] = None): - if action is None: - self._callbacks.clear() - else: - self._callbacks.pop(action, None) - - # ─── invalidate / requestLayout ──────────────────────────── - - def invalidate(self): - pass - - def requestLayout(self): - pass - - def requestFocus(self, direction: int = FOCUS_FORWARD) -> bool: - if self._visibility != View.VISIBLE or not self._enabled: - return False - return True - - # ─── padding ─────────────────────────────────────────────── - - def setPadding(self, left: int, top: int, right: int, bottom: int): - self._padding_left = left - self._padding_top = top - self._padding_right = right - self._padding_bottom = bottom - - def getPaddingLeft(self) -> int: - return self._padding_left - - def getPaddingTop(self) -> int: - return self._padding_top - - def getPaddingRight(self) -> int: - return self._padding_right - - def getPaddingBottom(self) -> int: - return self._padding_bottom - - def getPaddingStart(self) -> int: - return self._padding_left - - def getPaddingEnd(self) -> int: - return self._padding_right - - # ─── background ──────────────────────────────────────────── - - def setBackgroundColor(self, color: int): - self._background_color = color - self._background_drawable = None - - def setBackground(self, background: Any): - self._background_drawable = background - - def setBackgroundDrawable(self, background: Any): - self._background_drawable = background - - def setBackgroundResource(self, resid: int): - self._background_resource = resid - - def getBackground(self) -> Any: - return self._background_drawable - - # ─── transform properties ────────────────────────────────── - - def setAlpha(self, alpha: float): - self._alpha = alpha - - def getAlpha(self) -> float: - return self._alpha - - def setRotation(self, rotation: float): - self._rotation = rotation - - def getRotation(self) -> float: - return self._rotation - - def setScaleX(self, scale_x: float): - self._scale_x = scale_x - - def getScaleX(self) -> float: - return self._scale_x - - def setScaleY(self, scale_y: float): - self._scale_y = scale_y - - def getScaleY(self) -> float: - return self._scale_y - - def setTranslationX(self, translation_x: float): - self._translation_x = translation_x - - def getTranslationX(self) -> float: - return self._translation_x - - def setTranslationY(self, translation_y: float): - self._translation_y = translation_y - - def getTranslationY(self) -> float: - return self._translation_y - - def getPivotX(self) -> float: - return self._pivot_x - - def setPivotX(self, pivot_x: float): - self._pivot_x = pivot_x - - def getPivotY(self) -> float: - return self._pivot_y - - def setPivotY(self, pivot_y: float): - self._pivot_y = pivot_y - - def setElevation(self, elevation: float): - self._elevation = elevation - - def getElevation(self) -> float: - return self._elevation - - def animate(self) -> "ViewPropertyAnimator": - return ViewPropertyAnimator(self) - - # ─── parent / root ───────────────────────────────────────── - - def getParent(self) -> Optional["ViewParent"]: - return self._parent - - def getRootView(self) -> "View": - v = self - while v._parent is not None: - v = v._parent - return v - - # ─── hierarchy ───────────────────────────────────────────── - - def findViewById(self, id_: int) -> Optional["View"]: - if self._id == id_: - return self - if hasattr(self, "_children"): - for child in self._children: - result = child.findViewById(id_) - if result is not None: - return result - return None - - def bringToFront(self): - if self._parent is not None: - self._parent.bringChildToFront(self) - - # ─── enable / focusable / clickable ──────────────────────── - - def setEnabled(self, enabled: bool): - self._enabled = enabled - - def isEnabled(self) -> bool: - return self._enabled - - def setFocusable(self, focusable: bool): - self._focusable = focusable - - def isFocusable(self) -> bool: - return self._focusable - - def setClickable(self, clickable: bool): - self._clickable = clickable - - def isClickable(self) -> bool: - return self._clickable - - def setLongClickable(self, long_clickable: bool): - self._long_clickable = long_clickable - - def isLongClickable(self) -> bool: - return self._long_clickable - - def setContextClickable(self, context_clickable: bool): - self._context_clickable = context_clickable - - def isContextClickable(self) -> bool: - return self._context_clickable - - def setSelected(self, selected: bool): - self._selected = selected - - def isSelected(self) -> bool: - return self._selected - - def setActivated(self, activated: bool): - self._activated = activated - - def isActivated(self) -> bool: - return self._activated - - # ─── context / resources ─────────────────────────────────── - - def getContext(self): - return self._context - - def getResources(self): - if hasattr(self._context, "getResources"): - return self._context.getResources() - return None - - # ─── scroll ──────────────────────────────────────────────── - - def setScrollX(self, value: int): - self._scroll_x = value - - def setScrollY(self, value: int): - self._scroll_y = value - - def getScrollX(self) -> int: - return self._scroll_x - - def getScrollY(self) -> int: - return self._scroll_y - - def scrollTo(self, x: int, y: int): - self._scroll_x = x - self._scroll_y = y - - def scrollBy(self, x: int, y: int): - self._scroll_x += x - self._scroll_y += y - - def computeScroll(self): - pass - - # ─── drawing / layer ─────────────────────────────────────── - - def setWillNotDraw(self, will_not_draw: bool): - self._will_not_draw = will_not_draw - - def willNotDraw(self) -> bool: - return self._will_not_draw - - def setLayerType(self, layer_type: int, paint: Any = None): - self._layer_type = layer_type - - def getLayerType(self) -> int: - return self._layer_type - - def buildLayer(self): - pass - - def setDrawingCacheQuality(self, quality: int): - pass - - def getDrawingCacheQuality(self) -> int: - return View.DRAWING_CACHE_QUALITY_AUTO - - def getDrawingCache(self, auto_scale: bool = True) -> Any: - return None - - def buildDrawingCache(self, auto_scale: bool = True): - pass - - def destroyDrawingCache(self): - pass - - # ─── layout direction ────────────────────────────────────── - - def setLayoutDirection(self, layout_direction: int): - self._layout_direction = layout_direction - - def getLayoutDirection(self) -> int: - return self._layout_direction - - def setTextDirection(self, text_direction: int): - pass - - def getTextDirection(self) -> int: - return 0 - - def setTextAlignment(self, text_alignment: int): - pass - - def getTextAlignment(self) -> int: - return 0 - - # ─── hit / focus ─────────────────────────────────────────── - - def onTouchEvent(self, event: "MotionEvent") -> bool: - return self._clickable or self._long_clickable - - def onTrackballEvent(self, event: "MotionEvent") -> bool: - return False - - def onKeyDown(self, key_code: int, event: "KeyEvent") -> bool: - return False - - def onKeyUp(self, key_code: int, event: "KeyEvent") -> bool: - return False - - def onKeyLongPress(self, key_code: int, event: "KeyEvent") -> bool: - return False - - def onKeyMultiple(self, key_code: int, repeat_count: int, event: "KeyEvent") -> bool: - return False - - def dispatchTouchEvent(self, event: "MotionEvent") -> bool: - if self._on_touch_listener is not None: - handled = self._on_touch_listener(self, event) - if handled: - return True - return self.onTouchEvent(event) - - def dispatchKeyEvent(self, event: "KeyEvent") -> bool: - if self._on_key_listener is not None: - handled = self._on_key_listener(self, event.getKeyCode(), event) - if handled: - return True - if event.getAction() == KeyEvent.ACTION_DOWN: - return self.onKeyDown(event.getKeyCode(), event) - elif event.getAction() == KeyEvent.ACTION_UP: - return self.onKeyUp(event.getKeyCode(), event) - return False - - def dispatchGenericMotionEvent(self, event: "MotionEvent") -> bool: - return False - - def hasFocus(self) -> bool: - return False - - def isFocused(self) -> bool: - return self.hasFocus() - - def clearFocus(self): - pass - - def onWindowFocusChanged(self, has_window_focus: bool): - pass - - def hasWindowFocus(self) -> bool: - return True - - def getWindowToken(self) -> Any: - return None - - def getWindowVisibility(self) -> int: - return View.VISIBLE - - def getWindowAttachCount(self) -> int: - return 1 - - def onAttachedToWindow(self): - pass - - def onDetachedFromWindow(self): - pass - - def isAttachedToWindow(self) -> bool: - return self._parent is not None - - # ─── Haptic ──────────────────────────────────────────────── - - def performHapticFeedback(self, feedback_constant: int) -> bool: - return False - - HapticFeedbackConstants = None - - # ─── static helpers ──────────────────────────────────────── - - @staticmethod - def generateViewId() -> int: - View._next_id += 1 - return View._next_id - - @staticmethod - def getDefaultSize(size: int, measure_spec: int) -> int: - spec_mode = View.MeasureSpec.getMode(measure_spec) - spec_size = View.MeasureSpec.getSize(measure_spec) - if spec_mode == View.UNSPECIFIED: - return size - else: - return spec_size - - @staticmethod - def combineMeasuredStates(cur_state: int, new_state: int) -> int: - return cur_state | new_state - - @staticmethod - def resolveSize(size: int, measure_spec: int) -> int: - return View.getDefaultSize(size, measure_spec) - - @staticmethod - def resolveSizeAndState(size: int, measure_spec: int, child_measured_state: int) -> int: - result = View.resolveSize(size, measure_spec) - return result | (child_measured_state & View.MEASURED_STATE_MASK) - - @staticmethod - def inflate(context, resource, root): - return View(context) - - # ─── 事件处理 (扩展) ─────────────────────────────────────── - - def onGenericMotionEvent(self, event) -> bool: - return False - - def onHoverEvent(self, event) -> bool: - return False - - def onDragEvent(self, event) -> bool: - return False - - def onSizeChanged(self, w, h, oldw, oldh): - pass - - def onFocusChanged(self, gainFocus, direction, previouslyFocusedRect): - pass - - def onScrollChanged(self, l, t, oldl, oldt): - pass - - def onVisibilityChanged(self, changedView, visibility): - pass - - # ─── 监听器设置 (扩展) ───────────────────────────────────── - - def setOnGenericMotionListener(self, listener): - self._on_generic_motion_listener = listener - return self - - def setOnHoverListener(self, listener): - self._on_hover_listener = listener - return self - - def setOnDragListener(self, listener): - self._on_drag_listener = listener - return self - - def setOnFocusChangeListener(self, listener): - self._on_focus_change_listener = listener - return self - - def setOnContextClickListener(self, listener): - self._on_context_click_listener = listener - return self - - def addOnLayoutChangeListener(self, listener): - if not hasattr(self, '_layout_change_listeners'): - self._layout_change_listeners = [] - self._layout_change_listeners.append(listener) - - def removeOnLayoutChangeListener(self, listener): - if hasattr(self, '_layout_change_listeners'): - try: - self._layout_change_listeners.remove(listener) - except ValueError: - pass - - def setOnApplyWindowInsetsListener(self, listener): - self._on_apply_window_insets_listener = listener - return self - - # ─── 位置和尺寸 (扩展) ───────────────────────────────────── - - def getLocationOnScreen(self, location): - location[0] = self._left if hasattr(self, '_left') else 0 - location[1] = self._top if hasattr(self, '_top') else 0 - - def getLocationInWindow(self, location): - self.getLocationOnScreen(location) - - def offsetLeftAndRight(self, offset): - if hasattr(self, '_left'): - self._left += offset - - def offsetTopAndBottom(self, offset): - if hasattr(self, '_top'): - self._top += offset - - def getHitRect(self, out_rect): - out_rect.left = self._left if hasattr(self, '_left') else 0 - out_rect.top = self._top if hasattr(self, '_top') else 0 - out_rect.right = out_rect.left + (self._width if hasattr(self, '_width') else 0) - out_rect.bottom = out_rect.top + (self._height if hasattr(self, '_height') else 0) - - def getDrawingRect(self, out_rect): - out_rect.left = 0 - out_rect.top = 0 - out_rect.right = self._width if hasattr(self, '_width') else 0 - out_rect.bottom = self._height if hasattr(self, '_height') else 0 - - # ─── 其他扩展方法 ────────────────────────────────────────── - - def setTooltipText(self, text): - self._tooltip_text = text - - def getTooltipText(self): - return getattr(self, '_tooltip_text', None) - - def setTranslationZ(self, translationZ): - self._translation_z = translationZ - - def getTranslationZ(self): - return getattr(self, '_translation_z', 0.0) - - def setZ(self, z): - self._z = z - - def getZ(self): - return getattr(self, '_z', 0.0) - - def setTransitionName(self, name): - self._transition_name = name - - def getTransitionName(self): - return getattr(self, '_transition_name', None) - - def setClipToOutline(self, clipToOutline): - self._clip_to_outline = clipToOutline - - def getClipToOutline(self): - return getattr(self, '_clip_to_outline', False) - - def setFitsSystemWindows(self, fitSystemWindows): - self._fits_system_windows = fitSystemWindows - - def getFitsSystemWindows(self): - return getattr(self, '_fits_system_windows', False) - - def playSoundEffect(self, soundConstant): - pass - - def setSaveEnabled(self, enabled): - self._save_enabled = enabled - - def isSaveEnabled(self): - return getattr(self, '_save_enabled', True) - - def setSystemUiVisibility(self, visibility): - self._system_ui_visibility = visibility - - def getSystemUiVisibility(self): - return getattr(self, '_system_ui_visibility', 0) - - def requestApplyInsets(self): - pass - - def onApplyWindowInsets(self, insets): - return insets - - def setOnCreateContextMenuListener(self, listener): - self._on_create_context_menu_listener = listener - return self - - def getBaseline(self): - return -1 - - def setFocusableInTouchMode(self, focusable): - self._focusable_in_touch_mode = focusable - - def isFocusableInTouchMode(self): - return getattr(self, '_focusable_in_touch_mode', False) - - def setRotationX(self, rotationX): - self._rotation_x = rotationX - - def getRotationX(self): - return getattr(self, '_rotation_x', 0.0) - - def setRotationY(self, rotationY): - self._rotation_y = rotationY - - def getRotationY(self): - return getattr(self, '_rotation_y', 0.0) - - def setCameraDistance(self, distance): - self._camera_distance = distance - - def getCameraDistance(self): - return getattr(self, '_camera_distance', 0.0) - - def setForeground(self, foreground): - self._foreground = foreground - - def getForeground(self): - return getattr(self, '_foreground', None) - - def setBackgroundTintList(self, tint): - self._background_tint = tint - - def setBackgroundTintMode(self, tintMode): - self._background_tint_mode = tintMode - - def setScrollbarFadingEnabled(self, fadeScrollbars): - self._fade_scrollbars = fadeScrollbars - - def isScrollbarFadingEnabled(self): - return getattr(self, '_fade_scrollbars', True) - - def setVerticalScrollBarEnabled(self, enabled): - self._vertical_scrollbar = enabled - - def isVerticalScrollBarEnabled(self): - return getattr(self, '_vertical_scrollbar', True) - - def setHorizontalScrollBarEnabled(self, enabled): - self._horizontal_scrollbar = enabled - - def isHorizontalScrollBarEnabled(self): - return getattr(self, '_horizontal_scrollbar', True) - - def setLabelFor(self, id): - self._label_for = id - - def getLabelFor(self): - return getattr(self, '_label_for', 0) - - def cancelLongPress(self): - pass - - def setPaddingRelative(self, start, top, end, bottom): - self._padding_start = start - self._padding_top = top - self._padding_end = end - self._padding_bottom = bottom - - def setSoundEffectsEnabled(self, soundEffectsEnabled): - self._sound_effects = soundEffectsEnabled - - def isSoundEffectsEnabled(self): - return getattr(self, '_sound_effects', True) - - def setHapticFeedbackEnabled(self, hapticFeedbackEnabled): - self._haptic_feedback = hapticFeedbackEnabled - - def isHapticFeedbackEnabled(self): - return getattr(self, '_haptic_feedback', True) - - def refreshDrawableState(self): - pass - - def getDrawableState(self): - return [] - - def requestRectangleOnScreen(self, rectangle): - return False - - def setAllCaps(self, allCaps): - self._all_caps = allCaps - - -class MeasureSpec: - """MeasureSpec — 测量规格包装""" - EXACTLY = View.EXACTLY - AT_MOST = 0x80000000 # -2147483648 - UNSPECIFIED = 0 - MODE_SHIFT = 30 - MODE_MASK = 0xC0000000 - - @staticmethod - def makeMeasureSpec(size: int, mode: int) -> int: - return (size & ~MeasureSpec.MODE_MASK) | (mode & MeasureSpec.MODE_MASK) - - @staticmethod - def getMode(measure_spec: int) -> int: - return measure_spec & MeasureSpec.MODE_MASK - - @staticmethod - def getSize(measure_spec: int) -> int: - return measure_spec & ~MeasureSpec.MODE_MASK - - @staticmethod - def toString(measure_spec: int) -> str: - mode = MeasureSpec.getMode(measure_spec) - size = MeasureSpec.getSize(measure_spec) - mode_str = "UNSPECIFIED" - if mode == MeasureSpec.EXACTLY: - mode_str = "EXACTLY" - elif mode == MeasureSpec.AT_MOST: - mode_str = "AT_MOST" - return f"MeasureSpec: {mode_str} {size}" - - -View.MeasureSpec = MeasureSpec - - -class ViewPropertyAnimator: - """ViewPropertyAnimator — View 属性动画""" - - def __init__(self, view: View): - self._view = view - self._duration: int = 300 - self._interpolator: Any = None - - def setDuration(self, duration: int) -> "ViewPropertyAnimator": - self._duration = duration - return self - - def alpha(self, value: float) -> "ViewPropertyAnimator": - self._view.setAlpha(value) - return self - - def translationX(self, value: float) -> "ViewPropertyAnimator": - self._view.setTranslationX(value) - return self - - def translationY(self, value: float) -> "ViewPropertyAnimator": - self._view.setTranslationY(value) - return self - - def rotation(self, value: float) -> "ViewPropertyAnimator": - self._view.setRotation(value) - return self - - def scaleX(self, value: float) -> "ViewPropertyAnimator": - self._view.setScaleX(value) - return self - - def scaleY(self, value: float) -> "ViewPropertyAnimator": - self._view.setScaleY(value) - return self - - def x(self, value: float) -> "ViewPropertyAnimator": - self._view.setX(value) - return self - - def y(self, value: float) -> "ViewPropertyAnimator": - self._view.setY(value) - return self - - def start(self): - pass - - def cancel(self): - pass - - def setInterpolator(self, interpolator: Any) -> "ViewPropertyAnimator": - self._interpolator = interpolator - return self - - def withStartAction(self, runnable: Callable) -> "ViewPropertyAnimator": - return self - - def withEndAction(self, runnable: Callable) -> "ViewPropertyAnimator": - return self - - -# ═══════════════════════════════════════════════════════════════ -# ViewParent / ViewManager -# ═══════════════════════════════════════════════════════════════ - -class ViewParent(Stub): - """ViewParent — 视图父接口""" - - def requestLayout(self): - pass - - def invalidateChild(self, child: View, r: Any): - pass - - def requestChildFocus(self, child: View, focused: View): - pass - - def clearChildFocus(self, child: View): - pass - - def getParent(self) -> Optional["ViewParent"]: - return None - - def bringChildToFront(self, child: View): - pass - - def focusSearch(self, child: View, direction: int) -> Optional[View]: - return None - - def requestChildRectangleOnScreen(self, child: View, rectangle: Any, immediate: bool) -> bool: - return False - - def getChildVisibleRect(self, child: View, r: Any, offset: Any) -> bool: - return False - - def requestTransparentRegion(self, child: View): - pass - - def onStartNestedScroll(self, child: View, target: View, nested_scroll_axes: int) -> bool: - return False - - def onStopNestedScroll(self, target: View): - pass - - def onNestedScroll(self, target: View, dx_consumed: int, dy_consumed: int, - dx_unconsumed: int, dy_unconsumed: int): - pass - - def onNestedPreScroll(self, target: View, dx: int, dy: int, consumed: List[int]): - pass - - def onNestedFling(self, target: View, velocity_x: float, velocity_y: float, consumed: bool) -> bool: - return False - - def onNestedPreFling(self, target: View, velocity_x: float, velocity_y: float) -> bool: - return False - - def isNestedScrollingEnabled(self) -> bool: - return False - - def setNestedScrollingEnabled(self, enabled: bool): - pass - - def notifySubtreeAccessibilityStateChanged(self, child: View, source: View, change_type: int): - pass - - -class ViewManager(Stub): - """ViewManager — 视图管理接口""" - - def addView(self, view: View, params: "ViewGroup.LayoutParams"): - pass - - def updateViewLayout(self, view: View, params: "ViewGroup.LayoutParams"): - pass - - def removeView(self, view: View): - pass - - -# ═══════════════════════════════════════════════════════════════ -# ViewGroup -# ═══════════════════════════════════════════════════════════════ - -class ViewGroup(View, ViewParent, ViewManager): - """ViewGroup — 可包含子视图的容器""" - - CLIP_TO_PADDING_MASK = 0x00000001 - CLIP_CHILDREN_MASK = 0x00000002 - FLAG_CLIP_CHILDREN = 0x00000001 - FLAG_CLIP_TO_PADDING = 0x00000002 - FLAG_USE_CHILD_DRAWING_ORDER = 0x00000400 - - def __init__(self, context=None): - super().__init__(context) - self._children: List[View] = [] - self._clip_children = True - self._clip_to_padding = True - self._layout_mode = 0 - self._child_drawing_order_reversed = False - self._persistent_drawing_cache = False - self._always_drawn_with_cache = True - self._animate_on_set_layout = True - self._add_stateset = True - self._layout_animations = False - - # ─── child management ────────────────────────────────────── - - def addView(self, child: View, index: Optional[int] = -1, - params: Optional["ViewGroup.LayoutParams"] = None): - """addView — 支持 addView(child), addView(child, params), addView(child, index), addView(child, index, params)""" - # 智能 参数分派:第二个参数可能是 index(int) 或 params(LayoutParams) - if isinstance(index, ViewGroup.LayoutParams): - params = index - index = -1 - if params is None: - params = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT) - if index < 0 or index >= len(self._children): - self._children.append(child) - else: - self._children.insert(index, child) - child._parent = self - child.setLayoutParams(params) - child.onAttachedToWindow() - - def removeView(self, view: View): - if view in self._children: - self._children.remove(view) - view._parent = None - view.onDetachedFromWindow() - - def removeViewAt(self, index: int): - if 0 <= index < len(self._children): - self.removeView(self._children[index]) - - def removeAllViews(self): - for child in list(self._children): - child._parent = None - child.onDetachedFromWindow() - self._children.clear() - - def getChildCount(self) -> int: - return len(self._children) - - def getChildAt(self, index: int) -> Optional[View]: - if 0 <= index < len(self._children): - return self._children[index] - return None - - def indexOfChild(self, child: View) -> int: - try: - return self._children.index(child) - except ValueError: - return -1 - - def bringChildToFront(self, child: View): - if child in self._children: - self._children.remove(child) - self._children.append(child) - - def requestChildFocus(self, child: View, focused: View): - pass - - def onInterceptTouchEvent(self, event: "MotionEvent") -> bool: - return False - - def onInterceptHoverEvent(self, event: "MotionEvent") -> bool: - return False - - def addChildrenForAccessibility(self, children: List[View]): - for child in self._children: - children.append(child) - - def setClipChildren(self, clip_children: bool): - self._clip_children = clip_children - - def getClipChildren(self) -> bool: - return self._clip_children - - def setClipToPadding(self, clip_to_padding: bool): - self._clip_to_padding = clip_to_padding - - def getClipToPadding(self) -> bool: - return self._clip_to_padding - - def setLayoutAnimation(self, layout_animation: Any): - pass - - def getLayoutAnimation(self) -> Any: - return None - - def setLayoutMode(self, layout_mode: int): - self._layout_mode = layout_mode - - def getLayoutMode(self) -> int: - return self._layout_mode - - def setPersistentDrawingCache(self, drawing_cache_to_keep: int): - self._persistent_drawing_cache = bool(drawing_cache_to_keep) - - def setAddStatesFromChildren(self, add_states: bool): - self._add_stateset = add_states - - def addStatesFromChildren(self) -> bool: - return self._add_stateset - - def setChildDrawingOrderCallback(self, callback: Any): - pass - - def setChildrenDrawingOrderEnabled(self, enabled: bool): - self._child_drawing_order_reversed = enabled - - def isChildrenDrawingOrderEnabled(self) -> bool: - return self._child_drawing_order_reversed - - def setChildrenDrawnWithCacheEnabled(self, enabled: bool): - self._always_drawn_with_cache = enabled - - def isChildrenDrawnWithCacheEnabled(self) -> bool: - return self._always_drawn_with_cache - - def setMotionEventSplittingEnabled(self, split: bool): - pass - - def isMotionEventSplittingEnabled(self) -> bool: - return True - - # ─── layout transition ──────────────────────────────────── - - def setLayoutTransition(self, transition: Any): - self._layout_transition = transition - - def getLayoutTransition(self) -> Any: - return getattr(self, '_layout_transition', None) - - # ─── focus / touch blocking ─────────────────────────────── - - def setTouchscreenBlocksFocus(self, blocks: bool): - self._touchscreen_blocks_focus = blocks - - def isTouchscreenBlocksFocus(self) -> bool: - return getattr(self, '_touchscreen_blocks_focus', False) - - # ─── child visible rect ─────────────────────────────────── - - def getChildVisibleRect(self, child: View, r: Any, offset: Any) -> bool: - if r is not None and hasattr(r, 'set'): - r.set(child.getLeft(), child.getTop(), - child.getRight(), child.getBottom()) - if offset is not None and hasattr(offset, 'set'): - offset.set(0, 0) - return True - - def setTransitionGroup(self, is_transition_group: bool): - pass - - def isTransitionGroup(self) -> bool: - return False - - def startLayoutAnimation(self): - pass - - def scheduleLayoutAnimation(self): - pass - - def gatherTransparentRegion(self, region: Any) -> bool: - return False - - def requestTransparentRegion(self, child: View): - pass - - def onChildVisibilityChanged(self, child: View, old_visibility: int, new_visibility: int): - pass - - def focusSearch(self, focused: View, direction: int) -> Optional[View]: - return None - - def focusableViewAvailable(self, v: View): - pass - - def showContextMenuForChild(self, original_view: View) -> bool: - return False - - def createContextMenu(self, menu: Any): - pass - - def showContextMenu(self) -> bool: - return False - - def startActionModeForChild(self, original_view: View, action_mode: Any) -> Optional[Any]: - return None - - # ─── LayoutParams ────────────────────────────────────────── - - class LayoutParams: - """ViewGroup.LayoutParams — 布局参数""" - - MATCH_PARENT = -1 - WRAP_CONTENT = -2 - - def __init__(self, width: int = WRAP_CONTENT, height: int = WRAP_CONTENT): - self.width = width - self.height = height - - @staticmethod - def getSize(size: int) -> int: - return size - - # ─── MarginLayoutParams ──────────────────────────────────── - - class MarginLayoutParams(LayoutParams): - """ViewGroup.MarginLayoutParams — 带边距的布局参数""" - - def __init__(self, width: int = -2, height: int = -2): - super().__init__(width, height) - self.leftMargin = 0 - self.topMargin = 0 - self.rightMargin = 0 - self.bottomMargin = 0 - self.startMargin = 0 - self.endMargin = 0 - - def setMargins(self, left: int, top: int, right: int, bottom: int): - self.leftMargin = left - self.topMargin = top - self.rightMargin = right - self.bottomMargin = bottom - - def getMarginLeft(self) -> int: - return self.leftMargin - - def getMarginTop(self) -> int: - return self.topMargin - - def getMarginRight(self) -> int: - return self.rightMargin - - def getMarginBottom(self) -> int: - return self.bottomMargin - - def getMarginStart(self) -> int: - return self.startMargin if self.startMargin != 0 else self.leftMargin - - def getMarginEnd(self) -> int: - return self.endMargin if self.endMargin != 0 else self.rightMargin - - def setMarginStart(self, start: int): - self.startMargin = start - - def setMarginEnd(self, end: int): - self.endMargin = end - - -# ═══════════════════════════════════════════════════════════════ -# WindowManager -# ═══════════════════════════════════════════════════════════════ - -class WindowManager(ViewManager): - """WindowManager — 窗口管理器""" - - class LayoutParams(ViewGroup.LayoutParams): - """WindowManager.LayoutParams — 窗口布局参数""" - - # 窗口类型 - TYPE_FIRST_APPLICATION = 1 - TYPE_APPLICATION = 2 - TYPE_APPLICATION_STARTING = 3 - TYPE_APPLICATION_PANEL = 1000 - TYPE_APPLICATION_MEDIA = 1001 - TYPE_APPLICATION_SUB_PANEL = 1002 - TYPE_APPLICATION_ATTACHED_DIALOG = 1003 - TYPE_APPLICATION_OVERLAY = 1004 - - # 标志位 - FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001 - FLAG_DIM_BEHIND = 0x00000002 - FLAG_BLUR_BEHIND = 0x00000004 - FLAG_NOT_FOCUSABLE = 0x00000008 - FLAG_NOT_TOUCHABLE = 0x00000010 - FLAG_NOT_TOUCH_MODAL = 0x00000020 - FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040 - FLAG_KEEP_SCREEN_ON = 0x00000080 - FLAG_LAYOUT_IN_SCREEN = 0x00000100 - FLAG_LAYOUT_NO_LIMITS = 0x00000200 - FLAG_FULLSCREEN = 0x00000400 - FLAG_FORCE_NOT_FULLSCREEN = 0x00000800 - FLAG_DITHER = 0x00001000 - FLAG_SECURE = 0x00002000 - FLAG_SCALED = 0x00004000 - FLAG_IGNORE_CHEEK_PRESSES = 0x00008000 - FLAG_LAYOUT_INSET_DECOR = 0x00010000 - FLAG_ALT_FOCUSABLE_IM = 0x00020000 - FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000 - FLAG_SHOW_WHEN_LOCKED = 0x00080000 - FLAG_SHOW_WALLPAPER = 0x00100000 - FLAG_TURN_SCREEN_ON = 0x00200000 - FLAG_DISMISS_KEYGUARD = 0x00400000 - FLAG_SPLIT_TOUCH = 0x00800000 - FLAG_HARDWARE_ACCELERATED = 0x01000000 - FLAG_LOCAL_FOCUS_MODE = 0x02000000 - FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x04000000 - FLAG_TRANSLUCENT_STATUS = 0x04000000 - FLAG_TRANSLUCENT_NAVIGATION = 0x08000000 - - # 软输入模式 - SOFT_INPUT_STATE_UNSPECIFIED = 0 - SOFT_INPUT_STATE_UNCHANGED = 1 - SOFT_INPUT_STATE_HIDDEN = 2 - SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3 - SOFT_INPUT_STATE_VISIBLE = 4 - SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5 - SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00 - SOFT_INPUT_ADJUST_RESIZE = 0x10 - SOFT_INPUT_ADJUST_PAN = 0x20 - SOFT_INPUT_ADJUST_NOTHING = 0x30 - SOFT_INPUT_IS_FORWARD_NAVIGATION = 0x100 - - # 屏幕方向 - SCREEN_ORIENTATION_UNSPECIFIED = -1 - SCREEN_ORIENTATION_LANDSCAPE = 0 - SCREEN_ORIENTATION_PORTRAIT = 1 - SCREEN_ORIENTATION_USER = 2 - SCREEN_ORIENTATION_BEHIND = 3 - SCREEN_ORIENTATION_SENSOR = 4 - SCREEN_ORIENTATION_NOSENSOR = 5 - SCREEN_ORIENTATION_SENSOR_LANDSCAPE = 6 - SCREEN_ORIENTATION_SENSOR_PORTRAIT = 7 - SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8 - SCREEN_ORIENTATION_REVERSE_PORTRAIT = 9 - SCREEN_ORIENTATION_FULL_SENSOR = 10 - SCREEN_ORIENTATION_USER_LANDSCAPE = 11 - SCREEN_ORIENTATION_USER_PORTRAIT = 12 - SCREEN_ORIENTATION_FULL_USER = 13 - SCREEN_ORIENTATION_LOCKED = 14 - - # 重力 - gravity = Gravity.NO_GRAVITY - x = 0 - y = 0 - horizontalMargin = 0.0 - verticalMargin = 0.0 - - def __init__(self, w: int = ViewGroup.LayoutParams.WRAP_CONTENT, - h: int = ViewGroup.LayoutParams.WRAP_CONTENT): - super().__init__(w, h) - self.type = WindowManager.LayoutParams.TYPE_APPLICATION - self.flags = 0 - self.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED - self.screenOrientation = WindowManager.LayoutParams.SCREEN_ORIENTATION_UNSPECIFIED - self.gravity = Gravity.NO_GRAVITY - self.x = 0 - self.y = 0 - self.token: Any = None - self.windowAnimations: int = 0 - self.packageName: str = "" - self.screenBrightness = -1.0 - self.buttonBrightness = -1.0 - self.rotationAnimation = 0 - self.preferredDisplayModeId = 0 - self.colorMode = 0 - self.format = -1 - - def __init__(self): - super().__init__() - self._views: Dict[View, WindowManager.LayoutParams] = {} - - def addView(self, view: View, params: LayoutParams): - self._views[view] = params - - def updateViewLayout(self, view: View, params: LayoutParams): - if view in self._views: - self._views[view] = params - - def removeView(self, view: View): - self._views.pop(view, None) - - def removeViewImmediate(self, view: View): - self._views.pop(view, None) - - def getDefaultDisplay(self) -> "Display": - return Display() - - -# ═══════════════════════════════════════════════════════════════ -# MotionEvent -# ═══════════════════════════════════════════════════════════════ - -class MotionEvent(Stub): - """MotionEvent — 触摸事件""" - - # 动作常量 - ACTION_DOWN = 0 - ACTION_UP = 1 - ACTION_MOVE = 2 - ACTION_CANCEL = 3 - ACTION_OUTSIDE = 4 - ACTION_POINTER_DOWN = 5 - ACTION_POINTER_UP = 6 - ACTION_HOVER_MOVE = 7 - ACTION_HOVER_ENTER = 8 - ACTION_HOVER_EXIT = 9 - ACTION_SCROLL = 11 - ACTION_BUTTON_PRESS = 12 - ACTION_BUTTON_RELEASE = 13 - - # 动作掩码 - ACTION_MASK = 0xFF - ACTION_POINTER_INDEX_MASK = 0xFF00 - ACTION_POINTER_INDEX_SHIFT = 8 - - # 按钮 - BUTTON_PRIMARY = 1 - BUTTON_SECONDARY = 2 - BUTTON_TERTIARY = 4 - BUTTON_BACK = 8 - BUTTON_FORWARD = 16 - BUTTON_STYLUS_PRIMARY = 32 - BUTTON_STYLUS_SECONDARY = 64 - - # 边缘 - EDGE_LEFT = 1 - EDGE_TOP = 2 - EDGE_RIGHT = 4 - EDGE_BOTTOM = 8 - - # 坐标分类 - AXIS_X = 0 - AXIS_Y = 1 - AXIS_PRESSURE = 2 - AXIS_SIZE = 3 - AXIS_TOUCH_MAJOR = 4 - AXIS_TOUCH_MINOR = 5 - AXIS_TOOL_MAJOR = 6 - AXIS_TOOL_MINOR = 7 - AXIS_ORIENTATION = 8 - AXIS_VSCROLL = 9 - AXIS_HSCROLL = 10 - AXIS_Z = 11 - AXIS_RX = 12 - AXIS_RY = 13 - AXIS_RZ = 14 - AXIS_HAT_X = 15 - AXIS_HAT_Y = 16 - AXIS_LTRIGGER = 17 - AXIS_RTRIGGER = 18 - AXIS_THROTTLE = 19 - AXIS_RUDDER = 20 - AXIS_WHEEL = 21 - AXIS_GAS = 22 - AXIS_BRAKE = 23 - AXIS_DISTANCE = 24 - AXIS_TILT = 25 - AXIS_SCROLL = 26 - AXIS_RELATIVE_X = 27 - AXIS_RELATIVE_Y = 28 - AXIS_GENERIC_1 = 32 - AXIS_GENERIC_2 = 33 - AXIS_GENERIC_3 = 34 - AXIS_GENERIC_4 = 35 - AXIS_GENERIC_5 = 36 - AXIS_GENERIC_6 = 37 - AXIS_GENERIC_7 = 38 - AXIS_GENERIC_8 = 39 - AXIS_GENERIC_9 = 40 - AXIS_GENERIC_10 = 41 - AXIS_GENERIC_11 = 42 - AXIS_GENERIC_12 = 43 - AXIS_GENERIC_13 = 44 - AXIS_GENERIC_14 = 45 - AXIS_GENERIC_15 = 46 - AXIS_GENERIC_16 = 47 - - # 输入类型 - TOOL_TYPE_UNKNOWN = 0 - TOOL_TYPE_FINGER = 1 - TOOL_TYPE_STYLUS = 2 - TOOL_TYPE_MOUSE = 3 - TOOL_TYPE_ERASER = 4 - - INVALID_POINTER_ID = -1 - - def __init__(self, action: int = 0, x: float = 0.0, y: float = 0.0, - event_time: int = 0, pointer_count: int = 1, - meta_state: int = 0, button_state: int = 0): - super().__init__() - self._action = action - self._x = x - self._y = y - self._raw_x = x - self._raw_y = y - self._event_time = event_time if event_time else int(time.time() * 1000) - self._down_time = self._event_time - self._pointer_count = pointer_count - self._meta_state = meta_state - self._button_state = button_state - self._pointers: List[Dict] = [] - for i in range(pointer_count): - self._pointers.append({ - "id": i, - "x": x, - "y": y, - "pressure": 1.0, - "size": 0.5, - "tool_type": MotionEvent.TOOL_TYPE_FINGER, - }) - - @classmethod - def obtain(cls, down_time: int, event_time: int, action: int, - x: float, y: float, meta_state: int = 0) -> "MotionEvent": - return cls(action=action, x=x, y=y, event_time=event_time) - - @classmethod - def obtainNoHistory(cls, me: "MotionEvent") -> "MotionEvent": - return cls(action=me._action, x=me._x, y=me._y, - event_time=me._event_time, pointer_count=me._pointer_count) - - def recycle(self): - pass - - def getAction(self) -> int: - return self._action - - def getActionMasked(self) -> int: - return self._action & MotionEvent.ACTION_MASK - - def getActionIndex(self) -> int: - return (self._action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT - - def getX(self, pointer_index: int = 0) -> float: - if 0 <= pointer_index < len(self._pointers): - return self._pointers[pointer_index]["x"] - return self._x - - def getY(self, pointer_index: int = 0) -> float: - if 0 <= pointer_index < len(self._pointers): - return self._pointers[pointer_index]["y"] - return self._y - - def getRawX(self) -> float: - return self._raw_x - - def getRawY(self) -> float: - return self._raw_y - - def getPointerCount(self) -> int: - return self._pointer_count - - def getPointerId(self, pointer_index: int) -> int: - if 0 <= pointer_index < len(self._pointers): - return self._pointers[pointer_index]["id"] - return pointer_index - - def findPointerIndex(self, pointer_id: int) -> int: - for i, p in enumerate(self._pointers): - if p["id"] == pointer_id: - return i - return -1 - - def getPressure(self, pointer_index: int = 0) -> float: - if 0 <= pointer_index < len(self._pointers): - return self._pointers[pointer_index]["pressure"] - return 1.0 - - def getSize(self, pointer_index: int = 0) -> float: - if 0 <= pointer_index < len(self._pointers): - return self._pointers[pointer_index]["size"] - return 0.5 - - def getEventTime(self) -> int: - return self._event_time - - def getDownTime(self) -> int: - return self._down_time - - def getMetaState(self) -> int: - return self._meta_state - - def getButtonState(self) -> int: - return self._button_state - - def getToolType(self, pointer_index: int = 0) -> int: - if 0 <= pointer_index < len(self._pointers): - return self._pointers[pointer_index]["tool_type"] - return MotionEvent.TOOL_TYPE_FINGER - - def setLocation(self, x: float, y: float): - self._x = x - self._y = y - - def offsetLocation(self, dx: float, dy: float): - self._x += dx - self._y += dy - - def transform(self, matrix: Any): - pass - - def isLongPress(self) -> bool: - return (self._event_time - self._down_time) > 500 - - def getHistoricalSize(self, pos: int, pointer: int = 0) -> float: - return 0.5 - - # ─── historical coordinates ─────────────────────────────── - - def getHistorySize(self) -> int: - return len(getattr(self, '_history', [])) - - def getHistoricalX(self, pointer_index: int = 0, pos: int = 0) -> float: - history = getattr(self, '_history', []) - if 0 <= pos < len(history): - return history[pos].get('x', self.getX(pointer_index)) - return self.getX(pointer_index) - - def getHistoricalY(self, pointer_index: int = 0, pos: int = 0) -> float: - history = getattr(self, '_history', []) - if 0 <= pos < len(history): - return history[pos].get('y', self.getY(pointer_index)) - return self.getY(pointer_index) - - def getHistoricalPressure(self, pointer_index: int = 0, pos: int = 0) -> float: - history = getattr(self, '_history', []) - if 0 <= pos < len(history): - return history[pos].get('pressure', 1.0) - return 1.0 - - def getHistoricalEventTime(self, pos: int = 0) -> int: - history = getattr(self, '_history', []) - if 0 <= pos < len(history): - return history[pos].get('event_time', self._event_time) - return self._event_time - - # ─── edge flags ─────────────────────────────────────────── - - def getEdgeFlags(self) -> int: - return getattr(self, '_edge_flags', 0) - - def setEdgeFlags(self, edge_flags: int): - self._edge_flags = edge_flags - - # ─── action mutation ────────────────────────────────────── - - def setAction(self, action: int): - self._action = action - - # ─── batch ──────────────────────────────────────────────── - - def addBatch(self, event_time: int, x: float, y: float, - pressure: float = 1.0, size: float = 0.5, - meta_state: int = 0): - if not hasattr(self, '_history'): - self._history = [] - self._history.append({ - 'event_time': self._event_time, - 'x': self._x, - 'y': self._y, - 'pressure': self.getPressure(0), - 'size': self.getSize(0), - 'meta_state': self._meta_state, - }) - self._x = x - self._y = y - self._raw_x = x - self._raw_y = y - self._event_time = event_time - self._meta_state = meta_state - if self._pointers: - self._pointers[0]['x'] = x - self._pointers[0]['y'] = y - self._pointers[0]['pressure'] = pressure - self._pointers[0]['size'] = size - - -# ═══════════════════════════════════════════════════════════════ -# KeyEvent -# ═══════════════════════════════════════════════════════════════ - -class KeyEvent(Stub): - """KeyEvent — 按键事件""" - - # 动作 - ACTION_DOWN = 0 - ACTION_UP = 1 - ACTION_MULTIPLE = 2 - - # 键码 - KEYCODE_UNKNOWN = 0 - KEYCODE_SOFT_LEFT = 1 - KEYCODE_SOFT_RIGHT = 2 - KEYCODE_HOME = 3 - KEYCODE_BACK = 4 - KEYCODE_CALL = 5 - KEYCODE_ENDCALL = 6 - KEYCODE_0 = 7 - KEYCODE_1 = 8 - KEYCODE_2 = 9 - KEYCODE_3 = 10 - KEYCODE_4 = 11 - KEYCODE_5 = 12 - KEYCODE_6 = 13 - KEYCODE_7 = 14 - KEYCODE_8 = 15 - KEYCODE_9 = 16 - KEYCODE_STAR = 17 - KEYCODE_POUND = 18 - KEYCODE_DPAD_UP = 19 - KEYCODE_DPAD_DOWN = 20 - KEYCODE_DPAD_LEFT = 21 - KEYCODE_DPAD_RIGHT = 22 - KEYCODE_DPAD_CENTER = 23 - KEYCODE_VOLUME_UP = 24 - KEYCODE_VOLUME_DOWN = 25 - KEYCODE_POWER = 26 - KEYCODE_CAMERA = 27 - KEYCODE_CLEAR = 28 - KEYCODE_A = 29 - KEYCODE_B = 30 - KEYCODE_C = 31 - KEYCODE_D = 32 - KEYCODE_E = 33 - KEYCODE_F = 34 - KEYCODE_G = 35 - KEYCODE_H = 36 - KEYCODE_I = 37 - KEYCODE_J = 38 - KEYCODE_K = 39 - KEYCODE_L = 40 - KEYCODE_M = 41 - KEYCODE_N = 42 - KEYCODE_O = 43 - KEYCODE_P = 44 - KEYCODE_Q = 45 - KEYCODE_R = 46 - KEYCODE_S = 47 - KEYCODE_T = 48 - KEYCODE_U = 49 - KEYCODE_V = 50 - KEYCODE_W = 51 - KEYCODE_X = 52 - KEYCODE_Y = 53 - KEYCODE_Z = 54 - KEYCODE_COMMA = 55 - KEYCODE_PERIOD = 56 - KEYCODE_ALT_LEFT = 57 - KEYCODE_ALT_RIGHT = 58 - KEYCODE_SHIFT_LEFT = 59 - KEYCODE_SHIFT_RIGHT = 60 - KEYCODE_TAB = 61 - KEYCODE_SPACE = 62 - KEYCODE_SYM = 63 - KEYCODE_EXPLORER = 64 - KEYCODE_ENVELOPE = 65 - KEYCODE_ENTER = 66 - KEYCODE_DEL = 67 - KEYCODE_GRAVE = 68 - KEYCODE_MINUS = 69 - KEYCODE_EQUALS = 70 - KEYCODE_LEFT_BRACKET = 71 - KEYCODE_RIGHT_BRACKET = 72 - KEYCODE_BACKSLASH = 73 - KEYCODE_SEMICOLON = 74 - KEYCODE_APOSTROPHE = 75 - KEYCODE_SLASH = 76 - KEYCODE_AT = 77 - KEYCODE_NUM = 78 - KEYCODE_HEADSETHOOK = 79 - KEYCODE_FOCUS = 80 - KEYCODE_PLUS = 81 - KEYCODE_MENU = 82 - KEYCODE_NOTIFICATION = 83 - KEYCODE_SEARCH = 84 - KEYCODE_MEDIA_PLAY_PAUSE = 85 - KEYCODE_MEDIA_STOP = 86 - KEYCODE_MEDIA_NEXT = 87 - KEYCODE_MEDIA_PREVIOUS = 88 - KEYCODE_MEDIA_REWIND = 89 - KEYCODE_MEDIA_FAST_FORWARD = 90 - KEYCODE_MUTE = 91 - KEYCODE_PAGE_UP = 92 - KEYCODE_PAGE_DOWN = 93 - KEYCODE_PICTSYMBOLS = 94 - KEYCODE_SWITCH_CHARSET = 95 - KEYCODE_BUTTON_A = 96 - KEYCODE_BUTTON_B = 97 - KEYCODE_BUTTON_C = 98 - KEYCODE_BUTTON_X = 99 - KEYCODE_BUTTON_Y = 100 - KEYCODE_BUTTON_Z = 101 - KEYCODE_BUTTON_L1 = 102 - KEYCODE_BUTTON_R1 = 103 - KEYCODE_BUTTON_L2 = 104 - KEYCODE_BUTTON_R2 = 105 - KEYCODE_BUTTON_THUMBL = 106 - KEYCODE_BUTTON_THUMBR = 107 - KEYCODE_BUTTON_START = 108 - KEYCODE_BUTTON_SELECT = 109 - KEYCODE_BUTTON_MODE = 110 - KEYCODE_ESCAPE = 111 - KEYCODE_FORWARD_DEL = 112 - KEYCODE_CTRL_LEFT = 113 - KEYCODE_CTRL_RIGHT = 114 - KEYCODE_CAPS_LOCK = 115 - KEYCODE_SCROLL_LOCK = 116 - KEYCODE_META_LEFT = 117 - KEYCODE_META_RIGHT = 118 - KEYCODE_FUNCTION = 119 - KEYCODE_SYSRQ = 120 - KEYCODE_BREAK = 121 - KEYCODE_MOVE_HOME = 122 - KEYCODE_MOVE_END = 123 - KEYCODE_INSERT = 124 - KEYCODE_FORWARD = 125 - KEYCODE_MEDIA_PLAY = 126 - KEYCODE_MEDIA_PAUSE = 127 - KEYCODE_MEDIA_CLOSE = 128 - KEYCODE_MEDIA_EJECT = 129 - KEYCODE_MEDIA_RECORD = 130 - KEYCODE_F1 = 131 - KEYCODE_F2 = 132 - KEYCODE_F3 = 133 - KEYCODE_F4 = 134 - KEYCODE_F5 = 135 - KEYCODE_F6 = 136 - KEYCODE_F7 = 137 - KEYCODE_F8 = 138 - KEYCODE_F9 = 139 - KEYCODE_F10 = 140 - KEYCODE_F11 = 141 - KEYCODE_F12 = 142 - KEYCODE_NUM_LOCK = 143 - KEYCODE_NUMPAD_0 = 144 - KEYCODE_NUMPAD_1 = 145 - KEYCODE_NUMPAD_2 = 146 - KEYCODE_NUMPAD_3 = 147 - KEYCODE_NUMPAD_4 = 148 - KEYCODE_NUMPAD_5 = 149 - KEYCODE_NUMPAD_6 = 150 - KEYCODE_NUMPAD_7 = 151 - KEYCODE_NUMPAD_8 = 152 - KEYCODE_NUMPAD_9 = 153 - KEYCODE_NUMPAD_DIVIDE = 154 - KEYCODE_NUMPAD_MULTIPLY = 155 - KEYCODE_NUMPAD_SUBTRACT = 156 - KEYCODE_NUMPAD_ADD = 157 - KEYCODE_NUMPAD_DOT = 158 - KEYCODE_NUMPAD_COMMA = 159 - KEYCODE_NUMPAD_ENTER = 160 - KEYCODE_NUMPAD_EQUALS = 161 - KEYCODE_NUMPAD_LEFT_PAREN = 162 - KEYCODE_NUMPAD_RIGHT_PAREN = 163 - KEYCODE_META_CTRL_LEFT = 164 - KEYCODE_META_CTRL_RIGHT = 165 - KEYCODE_META_ALT_LEFT = 166 - KEYCODE_META_ALT_RIGHT = 167 - KEYCODE_META_SHIFT_LEFT = 168 - KEYCODE_META_SHIFT_RIGHT = 169 - KEYCODE_META_SYM = 170 - - # 元键掩码 - META_ALT_ON = 0x02 - META_ALT_LEFT_ON = 0x10 - META_ALT_RIGHT_ON = 0x20 - META_SHIFT_ON = 0x01 - META_SHIFT_LEFT_ON = 0x40 - META_SHIFT_RIGHT_ON = 0x80 - META_SYM_ON = 0x04 - META_FUNCTION_ON = 0x08 - META_CTRL_ON = 0x1000 - META_CTRL_LEFT_ON = 0x2000 - META_CTRL_RIGHT_ON = 0x4000 - META_META_ON = 0x10000 - META_META_LEFT_ON = 0x20000 - META_META_RIGHT_ON = 0x40000 - META_CAPS_LOCK_ON = 0x100000 - META_NUM_LOCK_ON = 0x200000 - META_SCROLL_LOCK_ON = 0x400000 - - def __init__(self, action: int = ACTION_DOWN, key_code: int = KEYCODE_UNKNOWN, - repeat_count: int = 0, event_time: int = 0, - meta_state: int = 0): - super().__init__() - self._action = action - self._key_code = key_code - self._repeat_count = repeat_count - self._event_time = event_time if event_time else int(time.time() * 1000) - self._down_time = self._event_time - self._meta_state = meta_state - self._device_id = 0 - self._source = 0x101 # InputDevice.SOURCE_KEYBOARD - self._flags = 0 - self._display_id = 0 - - getAction = lambda self: self._action - getKeyCode = lambda self: self._key_code - getRepeatCount = lambda self: self._repeat_count - getEventTime = lambda self: self._event_time - getDownTime = lambda self: self._down_time - getMetaState = lambda self: self._meta_state - getDeviceId = lambda self: self._device_id - getSource = lambda self: self._source - getFlags = lambda self: self._flags - getDisplayId = lambda self: self._display_id - - def isLongPress(self) -> bool: - return self._repeat_count > 0 - - def dispatch(self, receiver: View, on_receive: Callable) -> bool: - return on_receive(receiver, self) - - def isAltPressed(self) -> bool: - return bool(self._meta_state & KeyEvent.META_ALT_ON) - - def isShiftPressed(self) -> bool: - return bool(self._meta_state & KeyEvent.META_SHIFT_ON) - - def isCtrlPressed(self) -> bool: - return bool(self._meta_state & KeyEvent.META_CTRL_ON) - - def isMetaPressed(self) -> bool: - return bool(self._meta_state & KeyEvent.META_META_ON) - - def isSymPressed(self) -> bool: - return bool(self._meta_state & KeyEvent.META_SYM_ON) - - def isFunctionPressed(self) -> bool: - return bool(self._meta_state & KeyEvent.META_FUNCTION_ON) - - def isCapsLockOn(self) -> bool: - return bool(self._meta_state & KeyEvent.META_CAPS_LOCK_ON) - - def isNumLockOn(self) -> bool: - return bool(self._meta_state & KeyEvent.META_NUM_LOCK_ON) - - def isScrollLockOn(self) -> bool: - return bool(self._meta_state & KeyEvent.META_SCROLL_LOCK_ON) - - @staticmethod - def changeTimeRepeat(event: "KeyEvent", event_time: int, repeat_count: int) -> "KeyEvent": - return KeyEvent( - action=event._action, - key_code=event._key_code, - repeat_count=repeat_count, - event_time=event_time, - meta_state=event._meta_state, - ) - - @staticmethod - def changeAction(event: "KeyEvent", action: int) -> "KeyEvent": - return KeyEvent( - action=action, - key_code=event._key_code, - repeat_count=event._repeat_count, - event_time=event._event_time, - meta_state=event._meta_state, - ) - - def getCharacters(self): - return getattr(self, '_characters', None) - - def isSystem(self): - return False - - def isPrintingKey(self): - return False - - def isModifierKey(self, keyCode): - return keyCode in (KEYCODE_SHIFT_LEFT, KEYCODE_SHIFT_RIGHT, KEYCODE_ALT_LEFT, KEYCODE_ALT_RIGHT, KEYCODE_CTRL_LEFT, KEYCODE_CTRL_RIGHT, KEYCODE_META_LEFT, KEYCODE_META_RIGHT) - - def startTracking(self): - self._tracking = True - - def isTracking(self): - return getattr(self, '_tracking', False) - - @staticmethod - def keyCodeToString(keyCode): - return f"KEYCODE_{keyCode}" - - -# ═══════════════════════════════════════════════════════════════ -# GestureDetector -# ═══════════════════════════════════════════════════════════════ - -class GestureDetector(Stub): - """GestureDetector — 手势检测器""" - - class SimpleOnGestureListener: - """SimpleOnGestureListener — 简单手势监听器""" - - def onDown(self, e: MotionEvent) -> bool: - return False - - def onShowPress(self, e: MotionEvent): - pass - - def onSingleTapUp(self, e: MotionEvent) -> bool: - return False - - def onScroll(self, e1: MotionEvent, e2: MotionEvent, - distance_x: float, distance_y: float) -> bool: - return False - - def onLongPress(self, e: MotionEvent): - pass - - def onFling(self, e1: MotionEvent, e2: MotionEvent, - velocity_x: float, velocity_y: float) -> bool: - return False - - def onSingleTapConfirmed(self, e: MotionEvent) -> bool: - return False - - def onDoubleTap(self, e: MotionEvent) -> bool: - return False - - def onDoubleTapEvent(self, e: MotionEvent) -> bool: - return False - - def onContextClick(self, e: MotionEvent) -> bool: - return False - - class OnGestureListener: - def onDown(self, e: MotionEvent) -> bool: - return False - def onShowPress(self, e: MotionEvent): - pass - def onSingleTapUp(self, e: MotionEvent) -> bool: - return False - def onScroll(self, e1: MotionEvent, e2: MotionEvent, - distance_x: float, distance_y: float) -> bool: - return False - def onLongPress(self, e: MotionEvent): - pass - def onFling(self, e1: MotionEvent, e2: MotionEvent, - velocity_x: float, velocity_y: float) -> bool: - return False - - class OnDoubleTapListener: - def onSingleTapConfirmed(self, e: MotionEvent) -> bool: - return False - def onDoubleTap(self, e: MotionEvent) -> bool: - return False - def onDoubleTapEvent(self, e: MotionEvent) -> bool: - return False - - def __init__(self, context=None, listener: Optional["GestureDetector.OnGestureListener"] = None, - handler: Any = None): - super().__init__() - self._listener = listener - self._double_tap_listener: Optional["GestureDetector.OnDoubleTapListener"] = None - self._is_longpress_enabled = True - self._context = context - self._current_down_event: Optional[MotionEvent] = None - self._previous_down_event: Optional[MotionEvent] = None - self._in_long_press = False - self._long_press_timeout = 500 - self._touch_slop = 8 - - def setOnDoubleTapListener(self, listener: Optional["GestureDetector.OnDoubleTapListener"]): - self._double_tap_listener = listener - - def setIsLongpressEnabled(self, enabled: bool): - self._is_longpress_enabled = enabled - - def isLongpressEnabled(self) -> bool: - return self._is_longpress_enabled - - def onTouchEvent(self, ev: MotionEvent) -> bool: - action = ev.getAction() - if action == MotionEvent.ACTION_DOWN: - self._in_long_press = False - self._current_down_event = ev - if self._listener: - return self._listener.onDown(ev) - elif action == MotionEvent.ACTION_MOVE: - pass - elif action == MotionEvent.ACTION_UP: - if self._listener: - return self._listener.onSingleTapUp(ev) - elif action == MotionEvent.ACTION_CANCEL: - pass - return False - - def setContextClickListener(self, listener: Callable): - pass - - def setContextClickable(self, context_clickable: bool): - pass - - def isContextClickable(self) -> bool: - return False - - -# ═══════════════════════════════════════════════════════════════ -# ScaleGestureDetector -# ═══════════════════════════════════════════════════════════════ - -class ScaleGestureDetector(Stub): - """ScaleGestureDetector — 缩放手势检测器""" - - class SimpleOnScaleGestureListener: - """SimpleOnScaleGestureListener — 简单缩放手势监听器""" - - def onScaleBegin(self, detector: "ScaleGestureDetector") -> bool: - return True - - def onScale(self, detector: "ScaleGestureDetector") -> bool: - return False - - def onScaleEnd(self, detector: "ScaleGestureDetector"): - pass - - class OnScaleGestureListener: - def onScaleBegin(self, detector: "ScaleGestureDetector") -> bool: - return True - def onScale(self, detector: "ScaleGestureDetector") -> bool: - return False - def onScaleEnd(self, detector: "ScaleGestureDetector"): - pass - - def __init__(self, context=None, listener: Optional[OnScaleGestureListener] = None): - super().__init__() - self._listener = listener - self._scale_factor = 1.0 - self._current_span = 0.0 - self._current_span_x = 0.0 - self._current_span_y = 0.0 - self._focus_x = 0.0 - self._focus_y = 0.0 - self._previous_span = 0.0 - self._time_delta = 0 - self._in_progress = False - - def getScaleFactor(self) -> float: - return self._scale_factor - - def getCurrentSpan(self) -> float: - return self._current_span - - def getCurrentSpanX(self) -> float: - return self._current_span_x - - def getCurrentSpanY(self) -> float: - return self._current_span_y - - def getPreviousSpan(self) -> float: - return self._previous_span - - def getPreviousSpanX(self) -> float: - return self._current_span_x - - def getPreviousSpanY(self) -> float: - return self._current_span_y - - def getFocusX(self) -> float: - return self._focus_x - - def getFocusY(self) -> float: - return self._focus_y - - def getTimeDelta(self) -> int: - return self._time_delta - - def getEventTime(self) -> int: - return int(time.time() * 1000) - - def isInProgress(self) -> bool: - return self._in_progress - - def onTouchEvent(self, event: MotionEvent) -> bool: - action = event.getActionMasked() - if action == MotionEvent.ACTION_DOWN: - self._in_progress = False - elif action == MotionEvent.ACTION_POINTER_DOWN: - if event.getPointerCount() >= 2: - self._in_progress = True - x1, y1 = event.getX(0), event.getY(0) - x2, y2 = event.getX(1), event.getY(1) - self._current_span = math.hypot(x2 - x1, y2 - y1) - self._focus_x = (x1 + x2) / 2.0 - self._focus_y = (y1 + y2) / 2.0 - if self._listener: - self._listener.onScaleBegin(self) - return True - elif action == MotionEvent.ACTION_MOVE: - if self._in_progress and event.getPointerCount() >= 2: - x1, y1 = event.getX(0), event.getY(0) - x2, y2 = event.getX(1), event.getY(1) - new_span = math.hypot(x2 - x1, y2 - y1) - self._previous_span = self._current_span - self._current_span = new_span - if self._previous_span > 0: - self._scale_factor = self._current_span / self._previous_span - self._focus_x = (x1 + x2) / 2.0 - self._focus_y = (y1 + y2) / 2.0 - if self._listener: - self._listener.onScale(self) - return True - elif action in (MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL): - if self._in_progress and self._listener: - self._listener.onScaleEnd(self) - self._in_progress = False - return False - - -# ═══════════════════════════════════════════════════════════════ -# VelocityTracker -# ═══════════════════════════════════════════════════════════════ - -class VelocityTracker(Stub): - """VelocityTracker — 速度追踪器""" - - def __init__(self): - super().__init__() - self._movements: List[Tuple[float, float, float]] = [] # (x, y, time) - self._x_velocity: float = 0.0 - self._y_velocity: float = 0.0 - - @staticmethod - def obtain() -> "VelocityTracker": - return VelocityTracker() - - @staticmethod - def obtainUnit(max_units: int, generator_id: int) -> "VelocityTracker": - return VelocityTracker() - - def addMovement(self, event: MotionEvent): - self._movements.append((event.getRawX(), event.getRawY(), - event.getEventTime() / 1000.0)) - if len(self._movements) > 100: - self._movements = self._movements[-50:] - - def computeCurrentVelocity(self, units: int, max_velocity: float = float("inf")): - if len(self._movements) < 2: - self._x_velocity = 0.0 - self._y_velocity = 0.0 - return - recent = self._movements[-4:] if len(self._movements) >= 4 else self._movements - first_x, first_y, first_t = recent[0] - last_x, last_y, last_t = recent[-1] - dt = last_t - first_t - if dt > 0: - unit_scale = units / 1000.0 - self._x_velocity = (last_x - first_x) / dt * unit_scale - self._y_velocity = (last_y - first_y) / dt * unit_scale - self._x_velocity = max(-max_velocity, min(max_velocity, self._x_velocity)) - self._y_velocity = max(-max_velocity, min(max_velocity, self._y_velocity)) - else: - self._x_velocity = 0.0 - self._y_velocity = 0.0 - - def getXVelocity(self, id_: int = -1) -> float: - return self._x_velocity - - def getYVelocity(self, id_: int = -1) -> float: - return self._y_velocity - - def recycle(self): - self._movements.clear() - self._x_velocity = 0.0 - self._y_velocity = 0.0 - - def clear(self): - self._movements.clear() - - def getEstimator(self) -> Any: - return None - - -# ═══════════════════════════════════════════════════════════════ -# DragEvent -# ═══════════════════════════════════════════════════════════════ - -class DragEvent(Stub): - """DragEvent — 拖放事件""" - - ACTION_DRAG_STARTED = 1 - ACTION_DRAG_LOCATION = 2 - ACTION_DROP = 3 - ACTION_DRAG_ENTERED = 4 - ACTION_DRAG_EXITED = 5 - ACTION_DRAG_ENDED = 6 - - def __init__(self, action: int = 0, x: float = 0.0, y: float = 0.0, - clip_data: Any = None, result: bool = False): - super().__init__() - self._action = action - self._x = x - self._y = y - self._clip_data = clip_data - self._result = result - self._local_state: Any = None - - def getAction(self) -> int: - return self._action - - def getX(self) -> float: - return self._x - - def getY(self) -> float: - return self._y - - def getClipData(self) -> Any: - return self._clip_data - - def getResult(self) -> bool: - return self._result - - def getLocalState(self) -> Any: - return self._local_state - - def setLocalState(self, state: Any): - self._local_state = state - - def toString(self) -> str: - action_names = { - 1: "ACTION_DRAG_STARTED", - 2: "ACTION_DRAG_LOCATION", - 3: "ACTION_DROP", - 4: "ACTION_DRAG_ENTERED", - 5: "ACTION_DRAG_EXITED", - 6: "ACTION_DRAG_ENDED", - } - name = action_names.get(self._action, f"UNKNOWN({self._action})") - return f"DragEvent(action={name}, x={self._x}, y={self._y})" - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityDelegate -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityDelegate(Stub): - """AccessibilityDelegate — 辅助功能委托""" - - def sendAccessibilityEvent(self, host: View, event_type: int): - pass - - def sendAccessibilityEventUnchecked(self, host: View, event: Any): - pass - - def dispatchPopulateAccessibilityEvent(self, host: View, event: Any) -> bool: - return False - - def onPopulateAccessibilityEvent(self, host: View, event: Any): - pass - - def onInitializeAccessibilityEvent(self, host: View, event: Any): - pass - - def onInitializeAccessibilityNodeInfo(self, host: View, info: Any): - pass - - def onRequestSendAccessibilityEvent(self, host: ViewGroup, child: View, event: Any) -> bool: - return True - - def getAccessibilityNodeProvider(self, host: View) -> Optional[Any]: - return None - - def performAccessibilityAction(self, host: View, action: int, arguments: Any) -> bool: - return False - - -# ═══════════════════════════════════════════════════════════════ -# ViewTreeObserver -# ═══════════════════════════════════════════════════════════════ - -class ViewTreeObserver(Stub): - """ViewTreeObserver — 视图树观察者""" - - class OnGlobalLayoutListener: - def onGlobalLayout(self): - pass - - class OnGlobalFocusChangeListener: - def onGlobalFocusChanged(self, old_focus: View, new_focus: View): - pass - - class OnScrollChangedListener: - def onScrollChanged(self): - pass - - class OnTouchModeChangeListener: - def onTouchModeChanged(self, is_in_touch_mode: bool): - pass - - class OnWindowAttachListener: - def onWindowAttached(self): - pass - def onWindowDetached(self): - pass - - class OnWindowFocusChangeListener: - def onWindowFocusChanged(self, has_focus: bool): - pass - - class OnDrawListener: - def onDraw(self): - pass - - class OnPreDrawListener: - def onPreDraw(self) -> bool: - return True - - class OnWindowShownListener: - def onWindowShown(self): - pass - - def __init__(self): - super().__init__() - self._global_layout_listeners: List[ViewTreeObserver.OnGlobalLayoutListener] = [] - self._scroll_changed_listeners: List[ViewTreeObserver.OnScrollChangedListener] = [] - self._pre_draw_listeners: List[ViewTreeObserver.OnPreDrawListener] = [] - self._draw_listeners: List[ViewTreeObserver.OnDrawListener] = [] - self._touch_mode_listeners: List[ViewTreeObserver.OnTouchModeChangeListener] = [] - self._window_focus_listeners: List[ViewTreeObserver.OnWindowFocusChangeListener] = [] - self._window_attach_listeners: List[ViewTreeObserver.OnWindowAttachListener] = [] - self._alive = True - - def addOnGlobalLayoutListener(self, listener: OnGlobalLayoutListener): - self._global_layout_listeners.append(listener) - - def removeOnGlobalLayoutListener(self, listener: OnGlobalLayoutListener): - if listener in self._global_layout_listeners: - self._global_layout_listeners.remove(listener) - - def addOnScrollChangedListener(self, listener: OnScrollChangedListener): - self._scroll_changed_listeners.append(listener) - - def removeOnScrollChangedListener(self, listener: OnScrollChangedListener): - if listener in self._scroll_changed_listeners: - self._scroll_changed_listeners.remove(listener) - - def addOnPreDrawListener(self, listener: OnPreDrawListener): - self._pre_draw_listeners.append(listener) - - def removeOnPreDrawListener(self, listener: OnPreDrawListener): - if listener in self._pre_draw_listeners: - self._pre_draw_listeners.remove(listener) - - def addOnDrawListener(self, listener: OnDrawListener): - self._draw_listeners.append(listener) - - def removeOnDrawListener(self, listener: OnDrawListener): - if listener in self._draw_listeners: - self._draw_listeners.remove(listener) - - def addOnGlobalFocusChangeListener(self, listener: OnGlobalFocusChangeListener): - pass - - def removeOnGlobalFocusChangeListener(self, listener: OnGlobalFocusChangeListener): - pass - - def addOnTouchModeChangeListener(self, listener: OnTouchModeChangeListener): - self._touch_mode_listeners.append(listener) - - def removeOnTouchModeChangeListener(self, listener: OnTouchModeChangeListener): - if listener in self._touch_mode_listeners: - self._touch_mode_listeners.remove(listener) - - def addOnWindowAttachListener(self, listener: OnWindowAttachListener): - self._window_attach_listeners.append(listener) - - def removeOnWindowAttachListener(self, listener: OnWindowAttachListener): - if listener in self._window_attach_listeners: - self._window_attach_listeners.remove(listener) - - def addOnWindowFocusChangeListener(self, listener: OnWindowFocusChangeListener): - self._window_focus_listeners.append(listener) - - def removeOnWindowFocusChangeListener(self, listener: OnWindowFocusChangeListener): - if listener in self._window_focus_listeners: - self._window_focus_listeners.remove(listener) - - def addOnWindowShownListener(self, listener: OnWindowShownListener): - pass - - def removeOnWindowShownListener(self, listener: OnWindowShownListener): - pass - - def dispatchOnGlobalLayout(self): - for listener in list(self._global_layout_listeners): - listener.onGlobalLayout() - - def dispatchOnScrollChanged(self): - for listener in list(self._scroll_changed_listeners): - listener.onScrollChanged() - - def isAlive(self) -> bool: - return self._alive - - def kill(self): - self._alive = False - self._global_layout_listeners.clear() - self._scroll_changed_listeners.clear() - self._pre_draw_listeners.clear() - self._draw_listeners.clear() - - -# ═══════════════════════════════════════════════════════════════ -# ViewConfiguration -# ═══════════════════════════════════════════════════════════════ - -class ViewConfiguration(Stub): - """ViewConfiguration — 视图配置常量""" - - def __init__(self, context=None): - super().__init__() - if context is None: - context = android_context - self._context = context - - @staticmethod - def get(context=None) -> "ViewConfiguration": - return ViewConfiguration(context) - - @staticmethod - def getLongPressTimeout() -> int: - return 500 - - @staticmethod - def getTapTimeout() -> int: - return 100 - - @staticmethod - def getJumpTapTimeout() -> int: - return 500 - - @staticmethod - def getDoubleTapTimeout() -> int: - return 300 - - @staticmethod - def getEdgeSlop() -> int: - return 12 - - @staticmethod - def getTouchSlop() -> int: - return 8 - - @staticmethod - def getScaledTouchSlop(self) -> int: - return 8 - - @staticmethod - def getScaledEdgeSlop(self) -> int: - return 12 - - @staticmethod - def getScaledFadingEdgeLength(self) -> int: - return 12 - - @staticmethod - def getScaledMinimumFlingVelocity(self) -> int: - return 50 - - @staticmethod - def getScaledMaximumFlingVelocity(self) -> int: - return 8000 - - @staticmethod - def getScaledPagingTouchSlop(self) -> int: - return 16 - - @staticmethod - def getScaledOverflingDistance(self) -> int: - return 32 - - @staticmethod - def getScaledOverscrollDistance(self) -> int: - return 32 - - @staticmethod - def getScaledScrollFactor(self) -> int: - return 64 - - @staticmethod - def getScrollFriction() -> float: - return 0.015 - - @staticmethod - def getMaximumDrawingCacheSize() -> int: - return 480 * 800 * 4 - - @staticmethod - def getWindowTouchSlop() -> int: - return 16 - - @staticmethod - def getGlobalActionKeySlop() -> int: - return 12 - - def getScaledWindowTouchSlop(self) -> int: - return 16 - - -# ═══════════════════════════════════════════════════════════════ -# SurfaceView -# ═══════════════════════════════════════════════════════════════ - -class SurfaceView(View): - """SurfaceView — 表面视图,用于直接绘制""" - - def __init__(self, context=None): - super().__init__(context) - self._surface = Surface() - self._surface_holder = SurfaceHolder() - - def getHolder(self) -> "SurfaceHolder": - return self._surface_holder - - def getSurfaceControl(self) -> Any: - return None - - def setVisibility(self, visibility: int): - super().setVisibility(visibility) - - def setSecure(self, is_secure: bool): - pass - - def setZOrderMediaOverlay(self, is_media_overlay: bool): - pass - - def setZOrderOnTop(self, on_top: bool): - pass - - def gatherTransparentRegion(self, region: Any) -> bool: - return False - - def draw(self, canvas: Any): - pass - - -class SurfaceHolder(Stub): - """SurfaceHolder — Surface 持有者""" - - class Callback: - def surfaceCreated(self, holder: "SurfaceHolder"): - pass - def surfaceChanged(self, holder: "SurfaceHolder", format: int, width: int, height: int): - pass - def surfaceDestroyed(self, holder: "SurfaceHolder"): - pass - - class Callback2(Callback): - def surfaceRedrawNeeded(self, holder: "SurfaceHolder"): - pass - - SURFACE_TYPE_NORMAL = 0 - SURFACE_TYPE_HARDWARE = 1 - SURFACE_TYPE_GPU = 2 - SURFACE_TYPE_PUSH_BUFFERS = 3 - - def __init__(self): - super().__init__() - self._surface = Surface() - self._callbacks: List[SurfaceHolder.Callback] = [] - self._type = SurfaceHolder.SURFACE_TYPE_NORMAL - self._fixed_size: Optional[Tuple[int, int]] = None - self._format = -1 - - def getSurface(self) -> "Surface": - return self._surface - - def addCallback(self, callback: Callback): - self._callbacks.append(callback) - - def removeCallback(self, callback: Callback): - if callback in self._callbacks: - self._callbacks.remove(callback) - - def isCreating(self) -> bool: - return False - - def setType(self, type_: int): - self._type = type_ - - def setFixedSize(self, width: int, height: int): - self._fixed_size = (width, height) - - def setSizeFromLayout(self): - self._fixed_size = None - - def setFormat(self, format: int): - self._format = format - - def lockCanvas(self, *args): - """锁定画布并返回 Canvas 对象""" - if hasattr(self, '_surface') and self._surface: - from .graphics import Canvas, Bitmap - if hasattr(self._surface, '_bitmap') and self._surface._bitmap: - return Canvas(self._surface._bitmap) - from .graphics import Canvas, Bitmap - return Canvas(Bitmap(100, 100)) - - def unlockCanvasAndPost(self, canvas): - """解锁画布并提交""" - pass # 桌面环境无需实际提交 - - -# ═══════════════════════════════════════════════════════════════ -# TextureView -# ═══════════════════════════════════════════════════════════════ - -class TextureView(View): - """TextureView — 纹理视图""" - - def __init__(self, context=None): - super().__init__(context) - self._surface_texture = SurfaceTexture() - self._layer = None - self._update_listener: Optional[Callable] = None - self._surface_texture_listener: Optional[Callable] = None - - class SurfaceTextureListener: - def onSurfaceTextureAvailable(self, surface: "SurfaceTexture", width: int, height: int): - pass - def onSurfaceTextureSizeChanged(self, surface: "SurfaceTexture", width: int, height: int): - pass - def onSurfaceTextureDestroyed(self, surface: "SurfaceTexture") -> bool: - return True - def onSurfaceTextureUpdated(self, surface: "SurfaceTexture"): - pass - - def isOpaque(self) -> bool: - return False - - def setOpaque(self, opaque: bool): - pass - - def getSurfaceTexture(self) -> "SurfaceTexture": - return self._surface_texture - - def setSurfaceTexture(self, surface_texture: "SurfaceTexture"): - self._surface_texture = surface_texture - - def setSurfaceTextureListener(self, listener: SurfaceTextureListener): - self._surface_texture_listener = listener - - def getSurfaceTextureListener(self) -> Optional[SurfaceTextureListener]: - return self._surface_texture_listener - - def buildLayer(self): - pass - - def setLayerPaint(self, paint: Any): - pass - - def getLayerPaint(self) -> Any: - return None - - def setTransform(self, transform: Any): - pass - - def getTransform(self, transform: Any) -> Any: - return transform - - def draw(self, canvas: Any): - pass - - def lockCanvas(self) -> Any: - return None - - def unlockCanvasAndPost(self, canvas: Any): - pass - - -class SurfaceTexture(Stub): - """SurfaceTexture — 表面纹理""" - - def __init__(self, tex_name: int = 0, allow_legacy: bool = True): - super().__init__() - self._tex_name = tex_name - self._timestamp = 0 - self._frame_available_listener: Optional[Callable] = None - self._default_buffer_size: Optional[Tuple[int, int]] = None - - def setOnFrameAvailableListener(self, listener: Callable, handler: Any = None): - self._frame_available_listener = listener - - def getTimestamp(self) -> int: - return self._timestamp - - def updateTexImage(self): - pass - - def release(self): - pass - - def setDefaultBufferSize(self, width: int, height: int): - self._default_buffer_size = (width, height) - - def getTransformMatrix(self, mtx: List[float]): - for i in range(16): - mtx[i] = 1.0 if i % 5 == 0 else 0.0 - - -# ═══════════════════════════════════════════════════════════════ -# MenuInflater -# ═══════════════════════════════════════════════════════════════ - -class MenuInflater(Stub): - """MenuInflater — 菜单填充器(桌面环境桩实现)""" - - def __init__(self, context=None): - super().__init__() - self._context = context - - def inflate(self, menu_res: int, menu: Any): - return menu - - def toString(self) -> str: - return f"MenuInflater@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# Window -# ═══════════════════════════════════════════════════════════════ - -class Window(Stub): - """Window — 抽象窗口基类""" - - # 特性ID - FEATURES_DEFAULT = -1 - FEATURE_OPTIONS_PANEL = 0 - FEATURE_NO_TITLE = 1 - FEATURE_PROGRESS = 2 - FEATURE_LEFT_ICON = 3 - FEATURE_RIGHT_ICON = 4 - FEATURE_INDETERMINATE_PROGRESS = 5 - FEATURE_CONTEXT_MENU = 6 - FEATURE_CUSTOM_TITLE = 7 - FEATURE_ACTION_BAR = 8 - FEATURE_ACTION_BAR_OVERLAY = 9 - FEATURE_ACTION_MODE_OVERLAY = 10 - FEATURE_SWIPE_TO_DISMISS = 11 - FEATURE_CONTENT_TRANSITIONS = 12 - FEATURE_WATCH = 13 - - # 本地特性 - ID_ANDROID_CONTENT = 0x01020002 - - DECOR_CAPTION_SHORTCUT_DURATION_MS = 1000 - - # 标志 - NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME = "navigationBarBackground" - STATUS_BAR_BACKGROUND_TRANSITION_NAME = "statusBarBackground" - - def __init__(self, context=None): - super().__init__() - if context is None: - context = android_context - self._context = context - self._features: Dict[int, bool] = {} - self._title: str = "" - self._decor_view: Optional[View] = None - self._callback: Optional["Window.Callback"] = None - self._window_manager: Optional[WindowManager] = None - self._soft_input_mode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED - self._flags: Dict[int, bool] = {} - self._container: Optional[Window] = None - self._local_features: List[int] = [] - - class Callback: - def dispatchKeyEvent(self, event: KeyEvent) -> bool: - return False - def dispatchTouchEvent(self, event: MotionEvent) -> bool: - return False - def dispatchTrackballEvent(self, event: MotionEvent) -> bool: - return False - def dispatchGenericMotionEvent(self, event: MotionEvent) -> bool: - return False - def dispatchPopulateAccessibilityEvent(self, event: Any) -> bool: - return False - def onCreatePanelView(self, feature_id: int) -> Optional[View]: - return None - def onCreatePanelMenu(self, feature_id: int, menu: Any) -> bool: - return False - def onPreparePanel(self, feature_id: int, view: View, menu: Any) -> bool: - return False - def onMenuOpened(self, feature_id: int, menu: Any): - pass - def onMenuItemSelected(self, feature_id: int, item: Any) -> bool: - return False - def onWindowAttributesChanged(self, attrs: Any): - pass - def onContentChanged(self): - pass - def onWindowFocusChanged(self, has_focus: bool): - pass - def onAttachedToWindow(self): - pass - def onDetachedFromWindow(self): - pass - def onPanelClosed(self, feature_id: int, menu: Any): - pass - def onSearchRequested(self) -> bool: - return False - def onSearchRequested(self, search_event: Any) -> bool: - return False - def onActionModeStarted(self, mode: Any): - pass - def onActionModeFinished(self, mode: Any): - pass - def onWindowStartingActionMode(self, callback: Any) -> Optional[Any]: - return None - def onWindowStartingActionMode(self, callback: Any, type_: int) -> Optional[Any]: - return None - - def setContentView(self, layout_res_id: int): - pass - - def setContentView(self, view: View): - pass - - def setContentView(self, view: View, params: ViewGroup.LayoutParams): - pass - - def addContentView(self, view: View, params: ViewGroup.LayoutParams): - pass - - def setTitle(self, title: str): - self._title = title - - def getTitle(self) -> str: - return self._title - - def setFeatureInt(self, feature_id: int, value: int): - self._features[feature_id] = True - - def requestFeature(self, feature_id: int) -> bool: - self._features[feature_id] = True - return True - - def hasFeature(self, feature_id: int) -> bool: - return self._features.get(feature_id, False) - - def setFlags(self, flags: int, mask: int): - for flag_bit in range(32): - if mask & (1 << flag_bit): - self._flags[1 << flag_bit] = bool(flags & (1 << flag_bit)) - - def addFlags(self, flags: int): - for flag_bit in range(32): - if flags & (1 << flag_bit): - self._flags[1 << flag_bit] = True - - def clearFlags(self, flags: int): - for flag_bit in range(32): - if flags & (1 << flag_bit): - self._flags[1 << flag_bit] = False - - def setSoftInputMode(self, soft_input_mode: int): - self._soft_input_mode = soft_input_mode - - def getSoftInputMode(self) -> int: - return self._soft_input_mode - - def setLayout(self, width: int, height: int): - pass - - def findViewById(self, id_: int) -> Optional[View]: - if self._decor_view: - return self._decor_view.findViewById(id_) - return None - - def getDecorView(self) -> Optional[View]: - return self._decor_view - - def setDecorView(self, view: View): - self._decor_view = view - - def getWindowManager(self) -> Optional[WindowManager]: - return self._window_manager - - def setWindowManager(self, wm: WindowManager, app_token: Any = None, app_name: str = ""): - self._window_manager = wm - - def setCallback(self, callback: Callback): - self._callback = callback - - def getCallback(self) -> Optional[Callback]: - return self._callback - - def superDispatchTouchEvent(self, event: MotionEvent) -> bool: - if self._decor_view: - return self._decor_view.dispatchTouchEvent(event) - return False - - def superDispatchKeyEvent(self, event: KeyEvent) -> bool: - if self._decor_view: - return self._decor_view.dispatchKeyEvent(event) - return False - - def superDispatchTrackballEvent(self, event: MotionEvent) -> bool: - return False - - def superDispatchGenericMotionEvent(self, event: MotionEvent) -> bool: - return False - - def takeSurface(self, callback: Any): - pass - - def takeInputQueue(self, callback: Any): - pass - - def isActive(self) -> bool: - return True - - def isFloating(self) -> bool: - return False - - def setBackgroundDrawable(self, drawable: Any): - pass - - def setBackgroundDrawableResource(self, res_id: int): - pass - - def setDimAmount(self, amount: float): - pass - - def getLayoutInflater(self): - return None - - def setWindowAnimations(self, res_id: int): - pass - - def getWindowStyle(self) -> Any: - return None - - def setContainer(self, container: "Window"): - self._container = container - - def getContainer(self) -> Optional["Window"]: - return self._container - - def setLocalFeatures(self, local_features: int): - pass - - def makeActive(self): - pass - - def getContext(self): - return self._context - - def getAttributes(self) -> WindowManager.LayoutParams: - params = WindowManager.LayoutParams() - params.softInputMode = self._soft_input_mode - return params - - def setAttributes(self, attrs: WindowManager.LayoutParams): - pass - - def reconfigure(self, attrs: WindowManager.LayoutParams): - pass - - def onConfigurationChanged(self, new_config: Any): - pass - - def getCurrentFocus(self) -> Optional[View]: - return None - - def getVolumeControlStream(self) -> int: - return -1 - - def setVolumeControlStream(self, stream_type: int): - pass - - def getMediaController(self) -> Any: - return None - - def setMediaController(self, controller: Any): - pass - - def openGlEsVersion(self) -> int: - return 2 - - def isShortcutKey(self, key_code: int, event: KeyEvent) -> bool: - return False - - def setStatusBarColor(self, color: int): - pass - - def setNavigationBarColor(self, color: int): - pass - - def setStatusBarVisibility(self, visible: bool): - pass - - def setNavigationBarVisibility(self, visible: bool): - pass - - def getStatusBarColor(self) -> int: - return 0 - - def getNavigationBarColor(self) -> int: - return 0 - - -# ═══════════════════════════════════════════════════════════════ -# WindowInsets -# ═══════════════════════════════════════════════════════════════ - -class WindowInsets(Stub): - """WindowInsets — 窗口插入区域""" - - def __init__(self, left: int = 0, top: int = 0, right: int = 0, bottom: int = 0): - super().__init__() - self._system_window_insets = Rect(left, top, right, bottom) - self._stable_insets = Rect(0, 0, 0, 0) - self._display_cutout_insets = Rect(0, 0, 0, 0) - self._consumed = False - - def getSystemWindowInsetLeft(self) -> int: - return self._system_window_insets.left - - def getSystemWindowInsetTop(self) -> int: - return self._system_window_insets.top - - def getSystemWindowInsetRight(self) -> int: - return self._system_window_insets.right - - def getSystemWindowInsetBottom(self) -> int: - return self._system_window_insets.bottom - - def getSystemWindowInsets(self) -> Rect: - return self._system_window_insets - - def getStableInsetTop(self) -> int: - return self._stable_insets.top - - def getStableInsetLeft(self) -> int: - return self._stable_insets.left - - def getStableInsetRight(self) -> int: - return self._stable_insets.right - - def getStableInsetBottom(self) -> int: - return self._stable_insets.bottom - - def getDisplayCutoutInsets(self) -> Rect: - return self._display_cutout_insets - - def consumeDisplayCutout(self) -> "WindowInsets": - return WindowInsets() - - def consumeSystemWindowInsets(self) -> "WindowInsets": - return WindowInsets() - - def replaceSystemWindowInsets(self, left: int, top: int, right: int, bottom: int) -> "WindowInsets": - new_ins = WindowInsets(left, top, right, bottom) - new_ins._stable_insets = self._stable_insets - return new_ins - - def isConsumed(self) -> bool: - return self._consumed - - def hasSystemWindowInsets(self) -> bool: - return (self._system_window_insets.left > 0 or self._system_window_insets.top > 0 or - self._system_window_insets.right > 0 or self._system_window_insets.bottom > 0) - - def hasInsets(self) -> bool: - return self.hasSystemWindowInsets() - - def isRound(self) -> bool: - return False - - def getDisplayCutout(self) -> Any: - return None - - def consumeStableInsets(self) -> "WindowInsets": - return WindowInsets() - - def toString(self) -> str: - return f"WindowInsets(system={self._system_window_insets})" - - -# ═══════════════════════════════════════════════════════════════ -# WindowInsetsController -# ═══════════════════════════════════════════════════════════════ - -class WindowInsetsController(Stub): - """WindowInsetsController — 窗口插入控制器""" - - BEHAVIOR_SHOW_BARS_BY_TOUCH = 0 - BEHAVIOR_SHOW_BARS_BY_SWIPE = 1 - BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2 - - APPEARANCE_OPAQUE_STATUS_BARS = 0x00000001 - APPEARANCE_OPAQUE_NAVIGATION_BARS = 0x00000002 - APPEARANCE_LOW_PROFILE_BARS = 0x00000004 - APPEARANCE_LIGHT_STATUS_BARS = 0x00000008 - APPEARANCE_LIGHT_NAVIGATION_BARS = 0x00000010 - - def __init__(self): - super().__init__() - self._appearance = 0 - self._behavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH - - def show(self, types: int): - pass - - def hide(self, types: int): - pass - - def setSystemBarsAppearance(self, appearance: int, mask: int): - self._appearance = (self._appearance & ~mask) | (appearance & mask) - - def getSystemBarsAppearance(self) -> int: - return self._appearance - - def setSystemBarsBehavior(self, behavior: int): - self._behavior = behavior - - def getSystemBarsBehavior(self) -> int: - return self._behavior - - def setAnimationsDisabled(self, disabled: bool): - pass - - -# ═══════════════════════════════════════════════════════════════ -# Display -# ═══════════════════════════════════════════════════════════════ - -class Display(Stub): - """Display — 显示信息""" - - DEFAULT_DISPLAY = 0 - - ROTATION_0 = 0 - ROTATION_90 = 1 - ROTATION_180 = 2 - ROTATION_270 = 3 - - FLAG_PRESENTATION = 0x00000008 - FLAG_PRIVATE = 0x00000004 - FLAG_SECURE = 0x00000002 - FLAG_SUPPORTS_PROTECTED_BUFFERS = 0x00000001 - STATE_DOZE = 3 - STATE_DOZE_SUSPEND = 4 - STATE_OFF = 1 - STATE_ON = 2 - STATE_UNKNOWN = 0 - - def __init__(self, display_id: int = DEFAULT_DISPLAY): - super().__init__() - self._display_id = display_id - self._width = 1080 - self._height = 1920 - self._rotation = Display.ROTATION_0 - self._refresh_rate = 60.0 - self._density = 2.0 - self._density_dpi = 320 - self._state = Display.STATE_ON - self._flags = 0 - self._name = "Built-in Screen" - - def getDisplayId(self) -> int: - return self._display_id - - def getWidth(self) -> int: - if _USE_LINUX: - return LinuxDisplay.get_screen_size()[0] - return self._width - - def getHeight(self) -> int: - if _USE_LINUX: - return LinuxDisplay.get_screen_size()[1] - return self._height - - def getRotation(self) -> int: - return self._rotation - - def getRefreshRate(self) -> float: - return self._refresh_rate - - def getName(self) -> str: - return self._name - - def getState(self) -> int: - return self._state - - def getFlags(self) -> int: - return self._flags - - def isHdr(self) -> bool: - return False - - def isWideColorGamut(self) -> bool: - return False - - def getSize(self, out_size: Any = None): - if out_size is not None: - out_size.width = self._width - out_size.height = self._height - return None - - def getRealSize(self, out_size: Any = None): - if out_size is not None: - out_size.width = self._width - out_size.height = self._height - return None - - def getMetrics(self, metrics: Any = None): - if metrics is not None: - metrics.widthPixels = self._width - metrics.heightPixels = self._height - metrics.density = self._density - metrics.densityDpi = self._density_dpi - metrics.scaledDensity = self._density - metrics.xdpi = 320.0 - metrics.ydpi = 320.0 - return None - - def getRealMetrics(self, metrics: Any = None): - return self.getMetrics(metrics) - - def getOrientation(self) -> int: - return self._rotation - - def getDisplayAdjustments(self) -> Any: - return None - - def getPixelFormat(self) -> int: - return 1 # RGBA_8888 - - def isValid(self) -> bool: - return True - - def toString(self) -> str: - return f"Display({self._display_id}, {self._width}x{self._height})" - - -# ═══════════════════════════════════════════════════════════════ -# Surface -# ═══════════════════════════════════════════════════════════════ - -class Surface(Stub): - """Surface — 表面,用于绘制""" - - ROTATION_0 = 0 - ROTATION_90 = 1 - ROTATION_180 = 2 - ROTATION_270 = 3 - - CHANGE_FRAME_RATE_ALWAYS = 1 - CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS = 0 - - def __init__(self, surface_control: Any = None): - super().__init__() - self._is_valid = True - self._is_locked = False - self._width = 1080 - self._height = 1920 - self._hardware_accelerated = False - - def isValid(self) -> bool: - return self._is_valid - - def isConsumerRunningBehind(self) -> bool: - return False - - def lockCanvas(self, dirty_rect: Any = None) -> Any: - if not self._is_valid: - return None - self._is_locked = True - return None # 返回空 Canvas - - def lockHardwareCanvas(self) -> Any: - if not self._is_valid: - return None - self._is_locked = True - self._hardware_accelerated = True - return None - - def unlockCanvasAndPost(self, canvas: Any): - self._is_locked = False - - def getSurfaceControl(self) -> Any: - return None - - def release(self): - self._is_valid = False - self._is_locked = False - - def destroy(self): - self.release() - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def setFrameRate(self, frame_rate: float, compatibility: int): - pass - - def toString(self) -> str: - return f"Surface(valid={self._is_valid}, {self._width}x{self._height})" - - -# ═══════════════════════════════════════════════════════════════ -# Choreographer -# ═══════════════════════════════════════════════════════════════ - -class Choreographer(Stub): - """Choreographer — 帧渲染协调器""" - - CALLBACK_INPUT = 0 - CALLBACK_ANIMATION = 1 - CALLBACK_INSETS_ANIMATION = 2 - CALLBACK_TRAVERSAL = 3 - CALLBACK_COMMIT = 4 - - _instance: Optional["Choreographer"] = None - - class FrameCallback: - def doFrame(self, frame_time_nanos: int): - pass - - class FrameTimeline: - pass - - def __init__(self): - super().__init__() - - @staticmethod - def getInstance() -> "Choreographer": - if Choreographer._instance is None: - Choreographer._instance = Choreographer() - return Choreographer._instance - - def postFrameCallback(self, callback: "FrameCallback"): - callback.doFrame(int(time.time() * 1_000_000_000)) - - def postFrameCallbackDelayed(self, callback: "FrameCallback", delay_millis: int): - callback.doFrame(int((time.time() + delay_millis / 1000.0) * 1_000_000_000)) - - def removeFrameCallback(self, callback: "FrameCallback"): - pass - - def postCallback(self, callback_type: int, action: Callable, token: Any): - action() - - def postCallbackDelayed(self, callback_type: int, action: Callable, token: Any, delay_millis: int): - import threading - timer = threading.Timer(delay_millis / 1000.0, action) - timer.daemon = True - timer.start() - - def removeCallbacks(self, callback_type: int, action: Callable, token: Any): - pass - - def getFrameTimeNanos(self) -> int: - return int(time.time() * 1_000_000_000) - - def getLastFrameTimeNanos(self) -> int: - return int(time.time() * 1_000_000_000) - 16_666_666 - - -# ═══════════════════════════════════════════════════════════════ -# ActionMode -# ═══════════════════════════════════════════════════════════════ - -class ActionMode(Stub): - """ActionMode — 操作模式""" - - TYPE_FLOATING = 1 - TYPE_PRIMARY = 0 - TYPE_TOOLBAR = 2 - - class Callback: - def onCreateActionMode(self, mode: "ActionMode", menu: Any) -> bool: - return False - def onPrepareActionMode(self, mode: "ActionMode", menu: Any) -> bool: - return False - def onActionItemClicked(self, mode: "ActionMode", item: Any) -> bool: - return False - def onDestroyActionMode(self, mode: "ActionMode"): - pass - - class Callback2(Callback): - def onGetContentRect(self, mode: "ActionMode", view: View, out_rect: Any): - pass - - def __init__(self): - super().__init__() - self._tag: Any = None - self._title: str = "" - self._subtitle: str = "" - self._title_optional_hint: str = "" - self._subtitle_optional_hint: str = "" - self._type = ActionMode.TYPE_PRIMARY - self._menu: Any = None - - def setTitle(self, title: str): - self._title = title - - def getTitle(self) -> str: - return self._title - - def setSubtitle(self, subtitle: str): - self._subtitle = subtitle - - def getSubtitle(self) -> str: - return self._subtitle - - def setTitleOptionalHint(self, hint: bool): - self._title_optional_hint = hint - - def getTitleOptionalHint(self) -> bool: - return bool(self._title_optional_hint) - - def setTag(self, tag: Any): - self._tag = tag - - def getTag(self) -> Any: - return self._tag - - def getMenu(self): - return self._menu - - def setType(self, type_: int): - self._type = type_ - - def getType(self) -> int: - return self._type - - def invalidate(self): - pass - - def invalidateContentRect(self): - pass - - def finish(self): - pass - - def getCustomView(self) -> Optional[View]: - return None - - def setCustomView(self, view: View): - pass - - def setImageDrawable(self, drawable: Any): - pass - - def getMenuInflater(self): - return None - - -# ═══════════════════════════════════════════════════════════════ -# ContextThemeWrapper -# ═══════════════════════════════════════════════════════════════ - -class ContextThemeWrapper(Stub): - """ContextThemeWrapper — 上下文主题包装器""" - - def __init__(self, base: Any = None, theme_res_id: int = 0): - super().__init__() - self._base = base if base is not None else android_context - self._theme_res_id = theme_res_id - self._theme: Any = None - - def attachBaseContext(self, new_base: Any): - self._base = new_base - - def getBaseContext(self): - return self._base - - def setTheme(self, resid: int): - self._theme_res_id = resid - - def getThemeResId(self) -> int: - return self._theme_res_id - - def getTheme(self) -> Any: - return self._theme - - def getResources(self): - if hasattr(self._base, "getResources"): - return self._base.getResources() - return None - - def getSystemService(self, name: str) -> Any: - if hasattr(self._base, "getSystemService"): - return self._base.getSystemService(name) - return None - - def getPackageName(self) -> str: - if hasattr(self._base, "getPackageName"): - return self._base.getPackageName() - return "" - - def getApplicationContext(self): - return self._base - - -# ═══════════════════════════════════════════════════════════════ -# ViewOutlineProvider -# ═══════════════════════════════════════════════════════════════ - -class ViewOutlineProvider(Stub): - """ViewOutlineProvider — 视图轮廓提供者""" - - BACKGROUND = None - BOUNDS = None - PADDED_BOUNDS = None - - def getOutline(self, view: View, outline: Any): - pass - - # 预定义实例 - @staticmethod - def _init_constants(): - if ViewOutlineProvider.BACKGROUND is None: - class _Background(ViewOutlineProvider): - def getOutline(self, view, outline): - if hasattr(outline, "setRect"): - outline.setRect(0, 0, view.getWidth(), view.getHeight()) - ViewOutlineProvider.BACKGROUND = _Background() - - if ViewOutlineProvider.BOUNDS is None: - class _Bounds(ViewOutlineProvider): - def getOutline(self, view, outline): - if hasattr(outline, "setRect"): - outline.setRect(0, 0, view.getWidth(), view.getHeight()) - ViewOutlineProvider.BOUNDS = _Bounds() - - if ViewOutlineProvider.PADDED_BOUNDS is None: - class _PaddedBounds(ViewOutlineProvider): - def getOutline(self, view, outline): - if hasattr(outline, "setRect"): - outline.setRect( - view.getPaddingLeft(), view.getPaddingTop(), - view.getWidth() - view.getPaddingRight(), - view.getHeight() - view.getPaddingBottom(), - ) - ViewOutlineProvider.PADDED_BOUNDS = _PaddedBounds() - - -ViewOutlineProvider._init_constants() - -# ═══════════════════════════════════════════════════════════════ -# RenderNode -# ═══════════════════════════════════════════════════════════════ - -class RenderNode(Stub): - """RenderNode — 渲染节点""" - - STATUS_DISPLAY_LIST_INFLATED = 1 - STATUS_DISPLAY_LIST_READY = 2 - STATUS_DISPLAY_LIST_PREPARE = 3 - STATUS_DISPLAY_LIST_UNUSED = 0 - - def __init__(self, name: str = ""): - super().__init__() - self._name = name - self._alpha = 1.0 - self._translation_x = 0.0 - self._translation_y = 0.0 - self._translation_z = 0.0 - self._elevation = 0.0 - self._rotation = 0.0 - self._rotation_x = 0.0 - self._rotation_y = 0.0 - self._scale_x = 1.0 - self._scale_y = 1.0 - self._pivot_x = 0.0 - self._pivot_y = 0.0 - self._left = 0 - self._top = 0 - self._right = 0 - self._bottom = 0 - self._clip_to_bounds = False - self._clip_bounds: Optional[Rect] = None - self._has_display_list = False - self._status = RenderNode.STATUS_DISPLAY_LIST_UNUSED - - def setPosition(self, left: int, top: int, right: int, bottom: int) -> bool: - old_left, old_top, old_right, old_bottom = self._left, self._top, self._right, self._bottom - self._left, self._top, self._right, self._bottom = left, top, right, bottom - return (old_left != left or old_top != top or old_right != right or old_bottom != bottom) - - def getLeft(self) -> int: - return self._left - def getTop(self) -> int: - return self._top - def getRight(self) -> int: - return self._right - def getBottom(self) -> int: - return self._bottom - - def setAlpha(self, alpha: float) -> bool: - changed = self._alpha != alpha - self._alpha = alpha - return changed - - def getAlpha(self) -> float: - return self._alpha - - def setTranslationX(self, tx: float) -> bool: - changed = self._translation_x != tx - self._translation_x = tx - return changed - - def getTranslationX(self) -> float: - return self._translation_x - - def setTranslationY(self, ty: float) -> bool: - changed = self._translation_y != ty - self._translation_y = ty - return changed - - def getTranslationY(self) -> float: - return self._translation_y - - def setElevation(self, elevation: float) -> bool: - changed = self._elevation != elevation - self._elevation = elevation - return changed - - def getElevation(self) -> float: - return self._elevation - - def setRotation(self, rotation: float) -> bool: - changed = self._rotation != rotation - self._rotation = rotation - return changed - - def getRotation(self) -> float: - return self._rotation - - def setScaleX(self, scale_x: float) -> bool: - changed = self._scale_x != scale_x - self._scale_x = scale_x - return changed - - def getScaleX(self) -> float: - return self._scale_x - - def setScaleY(self, scale_y: float) -> bool: - changed = self._scale_y != scale_y - self._scale_y = scale_y - return changed - - def getScaleY(self) -> float: - return self._scale_y - - def setPivotX(self, pivot_x: float) -> bool: - changed = self._pivot_x != pivot_x - self._pivot_x = pivot_x - return changed - - def getPivotX(self) -> float: - return self._pivot_x - - def setPivotY(self, pivot_y: float) -> bool: - changed = self._pivot_y != pivot_y - self._pivot_y = pivot_y - return changed - - def getPivotY(self) -> float: - return self._pivot_y - - def setClipToBounds(self, clip_to_bounds: bool) -> bool: - changed = self._clip_to_bounds != clip_to_bounds - self._clip_to_bounds = clip_to_bounds - return changed - - def getClipToBounds(self) -> bool: - return self._clip_to_bounds - - def setClipBounds(self, clip_rect: Optional[Rect]): - self._clip_bounds = clip_rect - - def getClipBounds(self) -> Optional[Rect]: - return self._clip_bounds - - def setHasDisplayList(self, has_display_list: bool): - self._has_display_list = has_display_list - - def hasDisplayList(self) -> bool: - return self._has_display_list - - def getName(self) -> str: - return self._name - - def discardDisplayList(self): - self._has_display_list = False - self._status = RenderNode.STATUS_DISPLAY_LIST_UNUSED - - def start(self) -> Any: - return None - - def end(self): - pass - - def offsetLeftAndRight(self, offset: int): - self._left += offset - self._right += offset - - def offsetTopAndBottom(self, offset: int): - self._top += offset - self._bottom += offset - - def getMatrix(self, out_matrix: Any): - pass - - def getInverseMatrix(self, out_matrix: Any): - pass - - def useLighting(self) -> bool: - return False - - def setUseLighting(self, use_lighting: bool): - pass - - def setLayerType(self, layer_type: int): - pass - - def getLayerType(self) -> int: - return 0 - - -# ═══════════════════════════════════════════════════════════════ -# ScrollCaptureCallback -# ═══════════════════════════════════════════════════════════════ - -class ScrollCaptureCallback(Stub): - """ScrollCaptureCallback — 滚动捕获回调""" - - def onScrollCaptureSearch(self, l: int, t: int, r: int, b: int) -> Optional[Any]: - return None - - def onScrollCaptureStart(self, session: Any) -> Optional[Any]: - return None - - def onScrollCaptureImageRequest(self, session: Any, request: Any) -> int: - return 0 - - def onScrollCaptureEnd(self, session: Any): - pass - - -# ═══════════════════════════════════════════════════════════════ -# KeyboardShortcutGroup -# ═══════════════════════════════════════════════════════════════ - -class KeyboardShortcutGroup(Stub): - """KeyboardShortcutGroup — 键盘快捷键分组""" - - def __init__(self, label: str, items: Optional[List[Any]] = None): - super().__init__() - self._label = label - self._items: List[Any] = items if items is not None else [] - self._system_group = False - - def getLabel(self) -> str: - return self._label - - def getItems(self) -> List[Any]: - return list(self._items) - - def addItem(self, item: Any): - self._items.append(item) - - def setSystemGroup(self, is_system: bool): - self._system_group = is_system - - def isSystemGroup(self) -> bool: - return self._system_group - - -# ═══════════════════════════════════════════════════════════════ -# SoundEffectConstants -# ═══════════════════════════════════════════════════════════════ - -class SoundEffectConstants: - """SoundEffectConstants — 音效常量""" - - CLICK = 0 - NAVIGATION_LEFT = 1 - NAVIGATION_UP = 2 - NAVIGATION_RIGHT = 3 - NAVIGATION_DOWN = 4 - NAVIGATION_REPEAT_LEFT = 5 - NAVIGATION_REPEAT_UP = 6 - NAVIGATION_REPEAT_RIGHT = 7 - NAVIGATION_REPEAT_DOWN = 8 - - @staticmethod - def getDirectionForNavigation(gravity: int) -> int: - return SoundEffectConstants.NAVIGATION_UP - - -# ═══════════════════════════════════════════════════════════════ -# FocusFinder -# ═══════════════════════════════════════════════════════════════ - -class FocusFinder(Stub): - """FocusFinder — 焦点查找器""" - - _instance: Optional["FocusFinder"] = None - - def __init__(self): - super().__init__() - - @staticmethod - def getInstance() -> "FocusFinder": - if FocusFinder._instance is None: - FocusFinder._instance = FocusFinder() - return FocusFinder._instance - - def findNextFocus(self, root: ViewGroup, focused: View, direction: int) -> Optional[View]: - return None - - def findNearestTouchable(self, root: ViewGroup, x: int, y: int, direction: int, - deltas: List[int]) -> Optional[View]: - return None - - -# ═══════════════════════════════════════════════════════════════ -# ViewGroup override for children list -# ═══════════════════════════════════════════════════════════════ - -# Ensure ViewGroup._children is separate from View._children -ViewGroup._children = [] # Will be overridden in __init__ - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "View", - "ViewGroup", - "ViewParent", - "ViewManager", - "WindowManager", - "MotionEvent", - "KeyEvent", - "GestureDetector", - "ScaleGestureDetector", - "VelocityTracker", - "DragEvent", - "AccessibilityDelegate", - "ViewTreeObserver", - "ViewConfiguration", - "SurfaceView", - "TextureView", - "Window", - "WindowInsets", - "WindowInsetsController", - "Display", - "Surface", - "Choreographer", - "ActionMode", - "ContextThemeWrapper", - "Gravity", - "ViewOutlineProvider", - "RenderNode", - "ScrollCaptureCallback", - "KeyboardShortcutGroup", - "MeasureSpec", - "ViewPropertyAnimator", - "SurfaceHolder", - "SurfaceTexture", - "FocusFinder", - "SoundEffectConstants", - "Rect", -] \ No newline at end of file diff --git a/android_pyapi/android/view_accessibility.py b/android_pyapi/android/view_accessibility.py deleted file mode 100644 index 7e306bc..0000000 --- a/android_pyapi/android/view_accessibility.py +++ /dev/null @@ -1,1488 +0,0 @@ -""" -Android View Accessibility 组件 — AccessibilityEvent, AccessibilityNodeInfo, -AccessibilityService, AccessibilityManager 等 -方法签名完全对齐 Android SDK -""" - -import sys -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError, android_context - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityEvent -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityEvent(Stub): - """AccessibilityEvent — 无障碍事件""" - - # 事件类型 - TYPE_VIEW_CLICKED = 0x00000001 - TYPE_VIEW_LONG_CLICKED = 0x00000002 - TYPE_VIEW_SELECTED = 0x00000004 - TYPE_VIEW_FOCUSED = 0x00000008 - TYPE_VIEW_TEXT_CHANGED = 0x00000010 - TYPE_WINDOW_STATE_CHANGED = 0x00000020 - TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040 - TYPE_VIEW_HOVER_ENTER = 0x00000080 - TYPE_VIEW_HOVER_EXIT = 0x00000100 - TYPE_TOUCH_EXPLORATION_GESTURE_START = 0x00000200 - TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400 - TYPE_WINDOW_CONTENT_CHANGED = 0x00000800 - TYPE_VIEW_SCROLLED = 0x00001000 - TYPE_VIEW_TEXT_SELECTION_CHANGED = 0x00002000 - TYPE_ANNOUNCEMENT = 0x00004000 - TYPE_VIEW_ACCESSIBILITY_FOCUSED = 0x00008000 - TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 0x00010000 - TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 0x00020000 - TYPE_GESTURE_DETECTION_START = 0x00040000 - TYPE_GESTURE_DETECTION_END = 0x00080000 - TYPE_TOUCH_INTERACTION_START = 0x00100000 - TYPE_TOUCH_INTERACTION_END = 0x00200000 - TYPE_WINDOWS_CHANGED = 0x00400000 - TYPE_VIEW_CONTEXT_CLICKED = 0x00800000 - TYPE_ASSIST_READING_CONTENT = 0x01000000 - - # 内容变化类型 - CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000001 - CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 0x00000002 - CONTENT_CHANGE_TYPE_SUBTREE = 0x00000004 - CONTENT_CHANGE_TYPE_TEXT = 0x00000010 - CONTENT_CHANGE_TYPE_UNDEFINED = 0x00000000 - CONTENT_CHANGE_TYPE_PANE_TITLE = 0x00000020 - CONTENT_CHANGE_TYPE_DRAG_STARTED = 0x00000040 - CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 0x00000080 - CONTENT_CHANGE_TYPE_DRAG_DROPPED = 0x00000100 - CONTENT_CHANGE_TYPE_CLEAR_FOCUS = 0x00000200 - CONTENT_CHANGE_TYPE_ADDED = 0x00000400 - CONTENT_CHANGE_TYPE_REMOVED = 0x00000800 - CONTENT_CHANGE_TYPE_VISIBILITY_CHANGED = 0x00001000 - CONTENT_CHANGE_TYPE_PANE_APPEARED = 0x00002000 - CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 0x00004000 - CONTENT_CHANGE_TYPE_ERROR = 0x00008000 - - # 窗口变化类型 - WINDOWS_CHANGE_ADDED = 0x00000001 - WINDOWS_CHANGE_REMOVED = 0x00000002 - WINDOWS_CHANGE_TITLE = 0x00000004 - WINDOWS_CHANGE_BOUNDS = 0x00000008 - WINDOWS_CHANGE_LAYER = 0x00000010 - WINDOWS_CHANGE_ACTIVE = 0x00000020 - WINDOWS_CHANGE_FOCUSED = 0x00000040 - WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED = 0x00000080 - WINDOWS_CHANGE_PARENT = 0x00000100 - WINDOWS_CHANGE_CHILDREN = 0x00000200 - WINDOWS_CHANGE_PIP = 0x00000400 - - MAX_TEXT_LENGTH = 500 - - # 预留回收池 - _pool: List["AccessibilityEvent"] = [] - _pool_max = 10 - - def __init__(self): - super().__init__() - self._event_type: int = 0 - self._source: Optional[Any] = None - self._source_node_id: int = -1 - self._class_name: Optional[str] = None - self._package_name: Optional[str] = None - self._content_description: Optional[str] = None - self._text: List[str] = [] - self._action: int = 0 - self._from_index: int = -1 - self._to_index: int = -1 - self._item_count: int = 0 - self._current_item_index: int = 0 - self._enabled: bool = True - self._password: bool = False - self._checked: bool = False - self._full_package_name: Optional[str] = None - self._before_text: Optional[str] = None - self._movement_granularity: int = 0 - self._added_count: int = 0 - self._removed_count: int = 0 - self._content_change_types: int = 0 - self._scroll_delta_x: float = 0.0 - self._scroll_delta_y: float = 0.0 - self._max_scroll_x: float = 0.0 - self._max_scroll_y: float = 0.0 - self._event_time: int = 0 - self._parcelable_data: Dict[str, Any] = {} - self._record_count: int = 0 - - @staticmethod - def obtain(event: Optional["AccessibilityEvent"] = None) -> "AccessibilityEvent": - if AccessibilityEvent._pool: - result = AccessibilityEvent._pool.pop() - else: - result = AccessibilityEvent() - if event is not None: - result._event_type = event._event_type - result._source = event._source - result._class_name = event._class_name - result._package_name = event._package_name - result._content_description = event._content_description - result._text = list(event._text) - result._action = event._action - return result - - def recycle(self): - self._event_type = 0 - self._source = None - self._source_node_id = -1 - self._class_name = None - self._package_name = None - self._content_description = None - self._text.clear() - self._action = 0 - self._from_index = -1 - self._to_index = -1 - self._item_count = 0 - if len(AccessibilityEvent._pool) < AccessibilityEvent._pool_max: - AccessibilityEvent._pool.append(self) - - def getEventType(self) -> int: - return self._event_type - - def setEventType(self, event_type: int): - self._event_type = event_type - - def getSource(self) -> Optional[Any]: - return self._source - - def setSource(self, source: Optional[Any]): - self._source = source - - def getClassName(self) -> Optional[str]: - return self._class_name - - def setClassName(self, class_name: Optional[str]): - self._class_name = class_name - - def getPackageName(self) -> Optional[str]: - return self._package_name - - def setPackageName(self, package_name: Optional[str]): - self._package_name = package_name - - def getContentDescription(self) -> Optional[str]: - return self._content_description - - def setContentDescription(self, content_description: Optional[str]): - self._content_description = content_description - - def getText(self) -> List[str]: - return list(self._text) - - def getAction(self) -> int: - return self._action - - def setAction(self, action: int): - self._action = action - - def setFromIndex(self, from_index: int): - self._from_index = from_index - - def getFromIndex(self) -> int: - return self._from_index - - def setToIndex(self, to_index: int): - self._to_index = to_index - - def getToIndex(self) -> int: - return self._to_index - - def setItemCount(self, item_count: int): - self._item_count = item_count - - def getItemCount(self) -> int: - return self._item_count - - def setCurrentItemIndex(self, index: int): - self._current_item_index = index - - def getCurrentItemIndex(self) -> int: - return self._current_item_index - - def isEnabled(self) -> bool: - return self._enabled - - def setEnabled(self, enabled: bool): - self._enabled = enabled - - def isPassword(self) -> bool: - return self._password - - def setPassword(self, password: bool): - self._password = password - - def isChecked(self) -> bool: - return self._checked - - def setChecked(self, checked: bool): - self._checked = checked - - def getBeforeText(self) -> Optional[str]: - return self._before_text - - def setBeforeText(self, before_text: Optional[str]): - self._before_text = before_text - - def getMovementGranularity(self) -> int: - return self._movement_granularity - - def setMovementGranularity(self, granularity: int): - self._movement_granularity = granularity - - def getAddedCount(self) -> int: - return self._added_count - - def setAddedCount(self, added_count: int): - self._added_count = added_count - - def getRemovedCount(self) -> int: - return self._removed_count - - def setRemovedCount(self, removed_count: int): - self._removed_count = removed_count - - def getContentChangeTypes(self) -> int: - return self._content_change_types - - def setContentChangeTypes(self, change_types: int): - self._content_change_types = change_types - - def getScrollDeltaX(self) -> float: - return self._scroll_delta_x - - def setScrollDeltaX(self, delta_x: float): - self._scroll_delta_x = delta_x - - def getScrollDeltaY(self) -> float: - return self._scroll_delta_y - - def setScrollDeltaY(self, delta_y: float): - self._scroll_delta_y = delta_y - - def getMaxScrollX(self) -> float: - return self._max_scroll_x - - def setMaxScrollX(self, max_x: float): - self._max_scroll_x = max_x - - def getMaxScrollY(self) -> float: - return self._max_scroll_y - - def setMaxScrollY(self, max_y: float): - self._max_scroll_y = max_y - - def getEventTime(self) -> int: - return self._event_time - - def setEventTime(self, event_time: int): - self._event_time = event_time - - def getParcelableData(self) -> Any: - return self._parcelable_data - - def setParcelableData(self, data: Any): - if isinstance(data, dict): - self._parcelable_data = data - else: - self._parcelable_data = {"data": data} - - def getRecordCount(self) -> int: - return self._record_count - - def appendRecord(self, record: "AccessibilityRecord"): - self._record_count += 1 - - def getRecord(self, index: int) -> Optional["AccessibilityRecord"]: - return None - - def toString(self) -> str: - return (f"AccessibilityEvent(type={hex(self._event_type)}, " - f"className={self._class_name}, " - f"packageName={self._package_name}, " - f"text={self._text})") - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityRecord -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityRecord(Stub): - """AccessibilityRecord — 无障碍记录""" - - def __init__(self): - super().__init__() - self._source: Optional[Any] = None - self._source_node_id: int = -1 - self._class_name: Optional[str] = None - self._package_name: Optional[str] = None - self._content_description: Optional[str] = None - self._text: List[str] = [] - self._from_index: int = -1 - self._to_index: int = -1 - self._item_count: int = 0 - self._current_item_index: int = 0 - self._scrollable: bool = False - self._scroll_x: int = 0 - self._scroll_y: int = 0 - self._max_scroll_x: int = 0 - self._max_scroll_y: int = 0 - self._enabled: bool = True - self._password: bool = False - self._checked: bool = False - self._full_package_name: Optional[str] = None - self._before_text: Optional[str] = None - self._removed_count: int = 0 - self._added_count: int = 0 - - def setSource(self, source: Optional[Any]): - self._source = source - - def getSource(self) -> Optional[Any]: - return self._source - - def setClassName(self, class_name: Optional[str]): - self._class_name = class_name - - def getClassName(self) -> Optional[str]: - return self._class_name - - def setContentDescription(self, content_description: Optional[str]): - self._content_description = content_description - - def getContentDescription(self) -> Optional[str]: - return self._content_description - - def setPackageName(self, package_name: Optional[str]): - self._package_name = package_name - - def getPackageName(self) -> Optional[str]: - return self._package_name - - def getText(self) -> List[str]: - return list(self._text) - - def setFromIndex(self, from_index: int): - self._from_index = from_index - - def getFromIndex(self) -> int: - return self._from_index - - def setToIndex(self, to_index: int): - self._to_index = to_index - - def getToIndex(self) -> int: - return self._to_index - - def setItemCount(self, item_count: int): - self._item_count = item_count - - def getItemCount(self) -> int: - return self._item_count - - def setScrollable(self, scrollable: bool): - self._scrollable = scrollable - - def getScrollable(self) -> bool: - return self._scrollable - - def setScrollX(self, scroll_x: int): - self._scroll_x = scroll_x - - def getScrollX(self) -> int: - return self._scroll_x - - def setScrollY(self, scroll_y: int): - self._scroll_y = scroll_y - - def getScrollY(self) -> int: - return self._scroll_y - - def setMaxScrollX(self, max_scroll_x: int): - self._max_scroll_x = max_scroll_x - - def getMaxScrollX(self) -> int: - return self._max_scroll_x - - def setMaxScrollY(self, max_scroll_y: int): - self._max_scroll_y = max_scroll_y - - def getMaxScrollY(self) -> int: - return self._max_scroll_y - - def isEnabled(self) -> bool: - return self._enabled - - def setEnabled(self, enabled: bool): - self._enabled = enabled - - def isPassword(self) -> bool: - return self._password - - def setPassword(self, password: bool): - self._password = password - - def isChecked(self) -> bool: - return self._checked - - def setChecked(self, checked: bool): - self._checked = checked - - def getBeforeText(self) -> Optional[str]: - return self._before_text - - def setBeforeText(self, before_text: Optional[str]): - self._before_text = before_text - - def getAddedCount(self) -> int: - return self._added_count - - def setAddedCount(self, added_count: int): - self._added_count = added_count - - def getRemovedCount(self) -> int: - return self._removed_count - - def setRemovedCount(self, removed_count: int): - self._removed_count = removed_count - - def toString(self) -> str: - return (f"AccessibilityRecord(className={self._class_name}, " - f"packageName={self._package_name})") - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityNodeInfo -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityNodeInfo(Stub): - """AccessibilityNodeInfo — 无障碍节点信息""" - - FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000002 - FLAG_REPORT_VIEW_IDS = 0x00000010 - FLAG_PREFETCH_PREDECESSORS = 0x00000001 - FLAG_PREFETCH_SIBLINGS = 0x00000002 - FLAG_PREFETCH_DESCENDANTS = 0x00000004 - - ACTION_CLICK = 0x00000010 - ACTION_LONG_CLICK = 0x00000020 - ACTION_FOCUS = 0x00000040 - ACTION_CLEAR_FOCUS = 0x00000080 - ACTION_SELECT = 0x00000100 - ACTION_CLEAR_SELECTION = 0x00000200 - ACTION_ACCESSIBILITY_FOCUS = 0x00000400 - ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000800 - ACTION_SCROLL_FORWARD = 0x00001000 - ACTION_SCROLL_BACKWARD = 0x00002000 - ACTION_SCROLL_TO_POSITION = 0x00004000 - ACTION_SET_SELECTION = 0x00008000 - ACTION_COPY = 0x00010000 - ACTION_PASTE = 0x00020000 - ACTION_CUT = 0x00030000 - ACTION_SET_TEXT = 0x00040000 - ACTION_EXPAND = 0x00080000 - ACTION_COLLAPSE = 0x00100000 - ACTION_DISMISS = 0x00200000 - ACTION_MOVE_WINDOW = 0x00400000 - ACTION_SHOW_ON_SCREEN = 0x00800000 - ACTION_CONTEXT_CLICK = 0x01000000 - ACTION_SCROLL_UP = 0x02000000 - ACTION_SCROLL_DOWN = 0x04000000 - ACTION_SCROLL_LEFT = 0x08000000 - ACTION_SCROLL_RIGHT = 0x10000000 - ACTION_PAGE_UP = 0x20000000 - ACTION_PAGE_DOWN = 0x40000000 - ACTION_PAGE_LEFT = 0x80000000 - ACTION_PAGE_RIGHT = 0x100000000 - - # 移动粒度 - MOVEMENT_GRANULARITY_CHARACTER = 1 - MOVEMENT_GRANULARITY_WORD = 2 - MOVEMENT_GRANULARITY_LINE = 4 - MOVEMENT_GRANULARITY_PARAGRAPH = 8 - MOVEMENT_GRANULARITY_PAGE = 16 - - def __init__(self): - super().__init__() - self._children: List["AccessibilityNodeInfo"] = [] - self._actions: int = 0 - self._clickable: bool = False - self._focusable: bool = False - self._long_clickable: bool = False - self._scrollable: bool = False - self._content_description: Optional[str] = None - self._text: Optional[str] = None - self._class_name: Optional[str] = None - self._package_name: Optional[str] = None - self._parent: Optional["AccessibilityNodeInfo"] = None - self._view_id: Optional[str] = None - self._enabled: bool = True - self._password: bool = False - self._checked: bool = False - self._checkable: bool = False - self._selected: bool = False - self._focused: bool = False - self._visible_to_user: bool = True - self._accessibility_focused: bool = False - self._can_open_popup: bool = False - self._dismissable: bool = False - self._editable: bool = False - self._multi_line: bool = False - self._important_for_accessibility: bool = True - self._pane_title: Optional[str] = None - self._hint_text: Optional[str] = None - self._tooltip_text: Optional[str] = None - self._error: Optional[str] = None - self._min_text_length: int = -1 - self._max_text_length: int = -1 - self._child_count: int = 0 - self._bounds_in_parent: Optional[Tuple[int, int, int, int]] = None - self._bounds_in_screen: Optional[Tuple[int, int, int, int]] = None - - def findAccessibilityNodeInfosByText(self, text: str) -> List["AccessibilityNodeInfo"]: - result: List["AccessibilityNodeInfo"] = [] - if self._text and text and text.lower() in self._text.lower(): - result.append(self) - for child in self._children: - result.extend(child.findAccessibilityNodeInfosByText(text)) - return result - - def findAccessibilityNodeInfosByViewId(self, view_id: str) -> List["AccessibilityNodeInfo"]: - result: List["AccessibilityNodeInfo"] = [] - if self._view_id == view_id: - result.append(self) - for child in self._children: - result.extend(child.findAccessibilityNodeInfosByViewId(view_id)) - return result - - def performAction(self, action: int) -> bool: - if action & self._actions: - sys.stderr.write(f"[AccessibilityNodeInfo] performAction: {hex(action)}\n") - return True - return False - - def setClickable(self, clickable: bool): - self._clickable = clickable - - def isClickable(self) -> bool: - return self._clickable - - def setFocusable(self, focusable: bool): - self._focusable = focusable - - def isFocusable(self) -> bool: - return self._focusable - - def setLongClickable(self, long_clickable: bool): - self._long_clickable = long_clickable - - def isLongClickable(self) -> bool: - return self._long_clickable - - def setScrollable(self, scrollable: bool): - self._scrollable = scrollable - - def isScrollable(self) -> bool: - return self._scrollable - - def setContentDescription(self, content_description: Optional[str]): - self._content_description = content_description - - def getContentDescription(self) -> Optional[str]: - return self._content_description - - def setText(self, text: Optional[str]): - self._text = text - - def getText(self) -> Optional[str]: - return self._text - - def setClassName(self, class_name: Optional[str]): - self._class_name = class_name - - def getClassName(self) -> Optional[str]: - return self._class_name - - def setPackageName(self, package_name: Optional[str]): - self._package_name = package_name - - def getPackageName(self) -> Optional[str]: - return self._package_name - - def getChild(self, index: int) -> Optional["AccessibilityNodeInfo"]: - if 0 <= index < len(self._children): - return self._children[index] - return None - - def setParent(self, parent: Optional["AccessibilityNodeInfo"]): - self._parent = parent - - def getParent(self) -> Optional["AccessibilityNodeInfo"]: - return self._parent - - def getChildCount(self) -> int: - return len(self._children) - - def addChild(self, child: "AccessibilityNodeInfo"): - self._children.append(child) - child._parent = self - - def addAction(self, action: int): - self._actions |= action - - def setActions(self, actions: int): - self._actions = actions - - def getActions(self) -> int: - return self._actions - - def setViewIdResourceName(self, view_id: str): - self._view_id = view_id - - def getViewIdResourceName(self) -> Optional[str]: - return self._view_id - - def isEnabled(self) -> bool: - return self._enabled - - def setEnabled(self, enabled: bool): - self._enabled = enabled - - def isPassword(self) -> bool: - return self._password - - def setPassword(self, password: bool): - self._password = password - - def isChecked(self) -> bool: - return self._checked - - def setChecked(self, checked: bool): - self._checked = checked - - def isCheckable(self) -> bool: - return self._checkable - - def setCheckable(self, checkable: bool): - self._checkable = checkable - - def isSelected(self) -> bool: - return self._selected - - def setSelected(self, selected: bool): - self._selected = selected - - def isFocused(self) -> bool: - return self._focused - - def setFocused(self, focused: bool): - self._focused = focused - - def isVisibleToUser(self) -> bool: - return self._visible_to_user - - def setVisibleToUser(self, visible: bool): - self._visible_to_user = visible - - def isAccessibilityFocused(self) -> bool: - return self._accessibility_focused - - def setAccessibilityFocused(self, focused: bool): - self._accessibility_focused = focused - - def isEditable(self) -> bool: - return self._editable - - def setEditable(self, editable: bool): - self._editable = editable - - def isMultiLine(self) -> bool: - return self._multi_line - - def setMultiLine(self, multi_line: bool): - self._multi_line = multi_line - - def isImportantForAccessibility(self) -> bool: - return self._important_for_accessibility - - def setImportantForAccessibility(self, important: bool): - self._important_for_accessibility = important - - def setPaneTitle(self, pane_title: Optional[str]): - self._pane_title = pane_title - - def getPaneTitle(self) -> Optional[str]: - return self._pane_title - - def setHintText(self, hint_text: Optional[str]): - self._hint_text = hint_text - - def getHintText(self) -> Optional[str]: - return self._hint_text - - def setTooltipText(self, tooltip_text: Optional[str]): - self._tooltip_text = tooltip_text - - def getTooltipText(self) -> Optional[str]: - return self._tooltip_text - - def setError(self, error: Optional[str]): - self._error = error - - def getError(self) -> Optional[str]: - return self._error - - def setBoundsInParent(self, left: int, top: int, right: int, bottom: int): - self._bounds_in_parent = (left, top, right, bottom) - - def getBoundsInParent(self) -> Optional[Tuple[int, int, int, int]]: - return self._bounds_in_parent - - def setBoundsInScreen(self, left: int, top: int, right: int, bottom: int): - self._bounds_in_screen = (left, top, right, bottom) - - def getBoundsInScreen(self) -> Optional[Tuple[int, int, int, int]]: - return self._bounds_in_screen - - def getWindowId(self) -> int: - return -1 - - def recycle(self): - self._children.clear() - self._parent = None - self._actions = 0 - - def toString(self) -> str: - return (f"AccessibilityNodeInfo(className={self._class_name}, " - f"text={self._text}, " - f"contentDescription={self._content_description})") - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityNodeProvider -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityNodeProvider(Stub): - """AccessibilityNodeProvider — 无障碍节点提供者""" - - HOST_VIEW_ID = -1 - - def __init__(self): - super().__init__() - - def createAccessibilityNodeInfo(self, virtual_view_id: int) -> AccessibilityNodeInfo: - raise AndroidOnlyError("AccessibilityNodeProvider.createAccessibilityNodeInfo") - - def addExtraDataToAccessibilityNodeInfo(self, virtual_view_id: int, - info: AccessibilityNodeInfo, - extra_data_key: str, - arguments: Optional[Any]): - pass - - def performAction(self, virtual_view_id: int, action: int, - arguments: Optional[Any]) -> bool: - return False - - def findFocus(self, focus: int) -> Optional[AccessibilityNodeInfo]: - return None - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityServiceInfo -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityServiceInfo(Stub): - """AccessibilityServiceInfo — 无障碍服务信息""" - - FEEDBACK_GENERIC = 16 - FEEDBACK_SPOKEN = 1 - FEEDBACK_HAPTIC = 2 - FEEDBACK_AUDIBLE = 4 - FEEDBACK_VISUAL = 8 - FEEDBACK_BRAILLE = 32 - FEEDBACK_ALL_MASK = -1 - - FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x00000001 - FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000002 - FLAG_REPORT_VIEW_IDS = 0x00000010 - FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000020 - FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 0x00000040 - FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000080 - FLAG_REQUEST_ACCESSIBILITY_BUTTON = 0x00000100 - FLAG_REQUEST_SHORTCUT_WARNING_DIALOG = 0x00000200 - - CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1 - CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2 - CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4 - CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 8 - CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16 - CAPABILITY_CAN_PERFORM_GESTURES = 32 - - def __init__(self): - super().__init__() - self.eventTypes: int = 0 - self.feedbackType: int = 0 - self.flags: int = 0 - self.notificationTimeout: int = 0 - self.filterTouchExploration: bool = False - self.packageNames: Optional[List[str]] = None - self.descriptionResId: int = 0 - self.description: Optional[str] = None - self.settingsActivityName: Optional[str] = None - self.capabilities: int = 0 - self.interactiveUiTimeout: int = 0 - self.nonInteractiveUiTimeout: int = 0 - - def getCapabilityInfo(self, capability: int) -> Optional[str]: - cap_map = { - self.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT: "CAN_RETRIEVE_WINDOW_CONTENT", - self.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION: "CAN_REQUEST_TOUCH_EXPLORATION", - self.CAPABILITY_CAN_CONTROL_MAGNIFICATION: "CAN_CONTROL_MAGNIFICATION", - self.CAPABILITY_CAN_PERFORM_GESTURES: "CAN_PERFORM_GESTURES", - } - return cap_map.get(capability) - - def toString(self) -> str: - return (f"AccessibilityServiceInfo(eventTypes={hex(self.eventTypes)}, " - f"feedbackType={self.feedbackType}, " - f"flags={hex(self.flags)})") - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityWindowInfo -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityWindowInfo(Stub): - """AccessibilityWindowInfo — 无障碍窗口信息""" - - TYPE_APPLICATION = 1 - TYPE_INPUT_METHOD = 2 - TYPE_SYSTEM = 3 - TYPE_ACCESSIBILITY_OVERLAY = 4 - TYPE_SPLIT_SCREEN_DIVIDER = 5 - - UNDEFINED = -1 - ACTIVE = 0x00000001 - FOCUSED = 0x00000002 - ACCESSIBILITY_FOCUSED = 0x00000004 - - def __init__(self): - super().__init__() - self._type: int = TYPE_SYSTEM - self._bounds_in_screen: Tuple[int, int, int, int] = (0, 0, 0, 0) - self._root: Optional[AccessibilityNodeInfo] = None - self._title: Optional[str] = None - self._anchor: Optional[Any] = None - self._parent: Optional["AccessibilityWindowInfo"] = None - self._children: List["AccessibilityWindowInfo"] = [] - self._id: int = -1 - self._layer: int = 0 - self._active: bool = False - self._focused: bool = False - self._accessibility_focused: bool = False - self._picture_in_picture: bool = False - - @staticmethod - def obtain() -> "AccessibilityWindowInfo": - return AccessibilityWindowInfo() - - def recycle(self): - self._root = None - self._children.clear() - self._parent = None - - def getType(self) -> int: - return self._type - - def setType(self, window_type: int): - self._type = window_type - - def getBoundsInScreen(self) -> Tuple[int, int, int, int]: - return self._bounds_in_screen - - def setBoundsInScreen(self, left: int, top: int, right: int, bottom: int): - self._bounds_in_screen = (left, top, right, bottom) - - def getRoot(self) -> Optional[AccessibilityNodeInfo]: - return self._root - - def setRoot(self, root: Optional[AccessibilityNodeInfo]): - self._root = root - - def getTitle(self) -> Optional[str]: - return self._title - - def setTitle(self, title: Optional[str]): - self._title = title - - def getAnchor(self) -> Optional[Any]: - return self._anchor - - def setAnchor(self, anchor: Optional[Any]): - self._anchor = anchor - - def getId(self) -> int: - return self._id - - def setId(self, window_id: int): - self._id = window_id - - def getLayer(self) -> int: - return self._layer - - def setLayer(self, layer: int): - self._layer = layer - - def getParent(self) -> Optional["AccessibilityWindowInfo"]: - return self._parent - - def setParent(self, parent: Optional["AccessibilityWindowInfo"]): - self._parent = parent - - def getChild(self, index: int) -> Optional["AccessibilityWindowInfo"]: - if 0 <= index < len(self._children): - return self._children[index] - return None - - def getChildCount(self) -> int: - return len(self._children) - - def addChild(self, child: "AccessibilityWindowInfo"): - self._children.append(child) - - def isActive(self) -> bool: - return self._active - - def setActive(self, active: bool): - self._active = active - - def isFocused(self) -> bool: - return self._focused - - def setFocused(self, focused: bool): - self._focused = focused - - def isAccessibilityFocused(self) -> bool: - return self._accessibility_focused - - def setAccessibilityFocused(self, focused: bool): - self._accessibility_focused = focused - - def isInPictureInPictureMode(self) -> bool: - return self._picture_in_picture - - def setPictureInPictureMode(self, pip: bool): - self._picture_in_picture = pip - - def toString(self) -> str: - type_names = {1: "APPLICATION", 2: "INPUT_METHOD", 3: "SYSTEM", - 4: "ACCESSIBILITY_OVERLAY", 5: "SPLIT_SCREEN_DIVIDER"} - type_name = type_names.get(self._type, "UNKNOWN") - return f"AccessibilityWindowInfo(id={self._id}, type={type_name}, title={self._title})" - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityService -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityService(Stub): - """AccessibilityService — 无障碍服务基类""" - - GLOBAL_ACTION_BACK = 1 - GLOBAL_ACTION_HOME = 2 - GLOBAL_ACTION_RECENTS = 3 - GLOBAL_ACTION_NOTIFICATIONS = 4 - GLOBAL_ACTION_QUICK_SETTINGS = 5 - GLOBAL_ACTION_POWER_DIALOG = 6 - GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7 - GLOBAL_ACTION_LOCK_SCREEN = 8 - GLOBAL_ACTION_TAKE_SCREENSHOT = 9 - GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10 - GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 11 - GLOBAL_ACTION_ACCESSIBILITY_BUTTON = 12 - GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 13 - GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT_CHOOSER = 14 - GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 15 - - def __init__(self): - super().__init__() - self._service_info: Optional[AccessibilityServiceInfo] = None - self._root_node: Optional[AccessibilityNodeInfo] = None - self._windows: List[AccessibilityWindowInfo] = [] - - def onAccessibilityEvent(self, event: AccessibilityEvent): - raise AndroidOnlyError("AccessibilityService.onAccessibilityEvent: not implemented") - - def onInterrupt(self): - raise AndroidOnlyError("AccessibilityService.onInterrupt: not implemented") - - def onServiceConnected(self): - pass - - def performGlobalAction(self, action: int) -> bool: - action_names = { - self.GLOBAL_ACTION_BACK: "BACK", - self.GLOBAL_ACTION_HOME: "HOME", - self.GLOBAL_ACTION_RECENTS: "RECENTS", - self.GLOBAL_ACTION_NOTIFICATIONS: "NOTIFICATIONS", - self.GLOBAL_ACTION_QUICK_SETTINGS: "QUICK_SETTINGS", - } - sys.stderr.write(f"[AccessibilityService] performGlobalAction: " - f"{action_names.get(action, hex(action))}\n") - return True - - def getRootInActiveWindow(self) -> Optional[AccessibilityNodeInfo]: - return self._root_node - - def findFocus(self, focus_type: int) -> Optional[AccessibilityNodeInfo]: - return self._root_node - - def getServiceInfo(self) -> Optional[AccessibilityServiceInfo]: - return self._service_info - - def setServiceInfo(self, info: AccessibilityServiceInfo): - self._service_info = info - - def getWindows(self) -> List[AccessibilityWindowInfo]: - return list(self._windows) - - def getWindowsOnScreen(self) -> List[AccessibilityWindowInfo]: - return [w for w in self._windows if w.isActive()] - - def disableSelf(self): - sys.stderr.write("[AccessibilityService] disableSelf\n") - - def onKeyEvent(self, event: Any) -> bool: - return False - - def onGesture(self, gesture_id: int) -> bool: - return False - - def onMotionEvent(self, event: Any) -> bool: - return False - - def onTouchExplorationStateChanged(self, enabled: bool): - pass - - def onMagnificationChanged(self, region: Any, scale: float): - pass - - def onSoftKeyboardShowModeChanged(self, show_mode: int): - pass - - def dispatchKeyEventToInputMethod(self, event: Any) -> bool: - return False - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityManager -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityManager(Stub): - """AccessibilityManager — 无障碍管理器""" - - def __init__(self): - super().__init__() - self._enabled: bool = False - self._touch_exploration_enabled: bool = False - self._state_listeners: List[Callable] = [] - self._touch_exploration_listeners: List[Callable] = [] - self._accessibility_service_list: List[AccessibilityServiceInfo] = [] - self._request_preparers: List[Any] = [] - - def isEnabled(self) -> bool: - return self._enabled - - def setEnabled(self, enabled: bool): - old = self._enabled - self._enabled = enabled - if old != enabled: - for listener in list(self._state_listeners): - try: - listener.onAccessibilityStateChanged(enabled) - except Exception: - pass - - def isTouchExplorationEnabled(self) -> bool: - return self._touch_exploration_enabled - - def addAccessibilityStateChangeListener(self, listener: Callable) -> bool: - if listener not in self._state_listeners: - self._state_listeners.append(listener) - return True - return False - - def removeAccessibilityStateChangeListener(self, listener: Callable) -> bool: - if listener in self._state_listeners: - self._state_listeners.remove(listener) - return True - return False - - def addTouchExplorationStateChangeListener(self, listener: Callable) -> bool: - if listener not in self._touch_exploration_listeners: - self._touch_exploration_listeners.append(listener) - return True - return False - - def removeTouchExplorationStateChangeListener(self, listener: Callable) -> bool: - if listener in self._touch_exploration_listeners: - self._touch_exploration_listeners.remove(listener) - return True - return False - - def getAccessibilityServiceList(self) -> List[AccessibilityServiceInfo]: - return list(self._accessibility_service_list) - - def addAccessibilityRequestPreparer(self, preparer: Any): - if preparer not in self._request_preparers: - self._request_preparers.append(preparer) - - def removeAccessibilityRequestPreparer(self, preparer: Any): - if preparer in self._request_preparers: - self._request_preparers.remove(preparer) - - def sendAccessibilityEvent(self, event: AccessibilityEvent): - sys.stderr.write(f"[AccessibilityManager] sendEvent: type={hex(event.getEventType())}\n") - - def interrupt(self): - sys.stderr.write("[AccessibilityManager] interrupt\n") - - def getEnabledAccessibilityServiceList(self, feedback_type_flags: int) -> List[AccessibilityServiceInfo]: - return [s for s in self._accessibility_service_list - if s.feedbackType & feedback_type_flags] - - def getInstalledAccessibilityServiceList(self) -> List[AccessibilityServiceInfo]: - return list(self._accessibility_service_list) - - def addAccessibilityStateChangeListener(self, listener: Callable) -> bool: - if listener not in self._state_listeners: - self._state_listeners.append(listener) - return True - return False - - def removeAccessibilityStateChangeListener(self, listener: Callable) -> bool: - if listener in self._state_listeners: - self._state_listeners.remove(listener) - return True - return False - - def toString(self) -> str: - return f"AccessibilityManager(enabled={self._enabled})" - - -# ═══════════════════════════════════════════════════════════════ -# CaptioningManager -# ═══════════════════════════════════════════════════════════════ - -class CaptioningManager(Stub): - """CaptioningManager — 字幕管理器""" - - class CaptionStyle(Stub): - """CaptionStyle — 字幕样式""" - - EDGE_TYPE_NONE = 0 - EDGE_TYPE_OUTLINE = 1 - EDGE_TYPE_DROP_SHADOW = 2 - EDGE_TYPE_RAISED = 3 - EDGE_TYPE_DEPRESSED = 4 - - def __init__(self): - super().__init__() - self._foreground_color: int = 0xFFFFFFFF - self._background_color: int = 0xFF000000 - self._edge_color: int = 0xFF000000 - self._edge_type: int = EDGE_TYPE_NONE - self._typeface: Optional[Any] = None - self._font_scale: float = 1.0 - self._window_color: int = 0xFF000000 - - @staticmethod - def getCustomStyle(css: Any) -> "CaptioningManager.CaptionStyle": - return CaptioningManager.CaptionStyle() - - def getForegroundColor(self) -> int: - return self._foreground_color - - def getBackgroundColor(self) -> int: - return self._background_color - - def getEdgeColor(self) -> int: - return self._edge_color - - def getEdgeType(self) -> int: - return self._edge_type - - def getTypeface(self) -> Optional[Any]: - return self._typeface - - def hasForegroundColor(self) -> bool: - return True - - def hasBackgroundColor(self) -> bool: - return True - - def hasEdgeColor(self) -> bool: - return True - - def hasEdgeType(self) -> bool: - return True - - def hasWindowColor(self) -> bool: - return True - - def getWindowColor(self) -> int: - return self._window_color - - def __init__(self): - super().__init__() - self._enabled: bool = False - self._locale: Optional[str] = None - self._font_scale: float = 1.0 - self._user_style: Optional["CaptioningManager.CaptionStyle"] = None - - def isCaptioningEnabled(self) -> bool: - return self._enabled - - def getLocale(self) -> Optional[str]: - return self._locale - - def getFontScale(self) -> float: - return self._font_scale - - def getUserStyle(self) -> "CaptioningManager.CaptionStyle": - if self._user_style is None: - self._user_style = CaptioningManager.CaptionStyle() - return self._user_style - - def addCaptioningChangeListener(self, listener: Any): - pass - - def removeCaptioningChangeListener(self, listener: Any): - pass - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityServiceGestureResult -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityServiceGestureResult(Stub): - """AccessibilityServiceGestureResult — 手势结果""" - - def __init__(self, completed: bool): - super().__init__() - self._completed: bool = completed - - def isCompleted(self) -> bool: - return self._completed - - -# ═══════════════════════════════════════════════════════════════ -# TouchExplorationStateChangeListener -# ═══════════════════════════════════════════════════════════════ - -class TouchExplorationStateChangeListener(Stub): - """TouchExplorationStateChangeListener — 触摸探索状态变更监听器""" - - def onTouchExplorationStateChanged(self, enabled: bool): - pass - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityButtonController -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityButtonController(Stub): - """AccessibilityButtonController — 无障碍按钮控制器""" - - class AccessibilityButtonCallback(Stub): - """AccessibilityButtonCallback — 无障碍按钮回调""" - - def onAvailabilityChanged(self, available: bool): - pass - - def onClicked(self, controller: "AccessibilityButtonController"): - pass - - def __init__(self): - super().__init__() - self._callback: Optional["AccessibilityButtonController.AccessibilityButtonCallback"] = None - - def isAccessibilityButtonAvailable(self) -> bool: - return False - - def registerAccessibilityButtonCallback(self, callback: "AccessibilityButtonController.AccessibilityButtonCallback"): - self._callback = callback - - def unregisterAccessibilityButtonCallback(self, callback: "AccessibilityButtonController.AccessibilityButtonCallback"): - if self._callback == callback: - self._callback = None - - def performAccessibilityButtonAction(self) -> bool: - if self._callback: - self._callback.onClicked(self) - return True - return False - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityButtonControllerCallback -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityButtonControllerCallback(Stub): - """AccessibilityButtonControllerCallback — 别名""" - - def onAvailabilityChanged(self, available: bool): - pass - - def onClicked(self, controller: AccessibilityButtonController): - pass - - -# ═══════════════════════════════════════════════════════════════ -# MagnificationConfig -# ═══════════════════════════════════════════════════════════════ - -class MagnificationConfig(Stub): - """MagnificationConfig — 放大配置""" - - SCALE = 0 - CENTER_X = 1 - CENTER_Y = 2 - ALL = -1 - - def __init__(self): - super().__init__() - self._scale: float = 1.0 - self._center_x: float = 0.0 - self._center_y: float = 0.0 - self._mode: int = 0 - self._frame_left: float = 0.0 - self._frame_top: float = 0.0 - self._frame_right: float = 0.0 - self._frame_bottom: float = 0.0 - - def getScale(self) -> float: - return self._scale - - def setScale(self, scale: float): - self._scale = scale - - def getCenterX(self) -> float: - return self._center_x - - def setCenterX(self, center_x: float): - self._center_x = center_x - - def getCenterY(self) -> float: - return self._center_y - - def setCenterY(self, center_y: float): - self._center_y = center_y - - def getMode(self) -> int: - return self._mode - - def setMode(self, mode: int): - self._mode = mode - - def toString(self) -> str: - return (f"MagnificationConfig(scale={self._scale}, " - f"center=({self._center_x}, {self._center_y}))") - - -# ═══════════════════════════════════════════════════════════════ -# MagnificationController -# ═══════════════════════════════════════════════════════════════ - -class MagnificationController(Stub): - """MagnificationController — 放大控制器""" - - class OnMagnificationChangedListener(Stub): - def onMagnificationChanged(self, controller: "MagnificationController", - region: Any, scale: float, center_x: float, center_y: float): - pass - - def __init__(self): - super().__init__() - self._listeners: List["MagnificationController.OnMagnificationChangedListener"] = [] - self._scale: float = 1.0 - self._center_x: float = 0.0 - self._center_y: float = 0.0 - self._magnification_region: Any = None - - def getScale(self) -> float: - return self._scale - - def getCenterX(self) -> float: - return self._center_x - - def getCenterY(self) -> float: - return self._center_y - - def getMagnificationRegion(self) -> Any: - return self._magnification_region - - def setScale(self, scale: float, animate: bool = False) -> bool: - self._scale = scale - self._notifyListeners() - return True - - def setCenter(self, center_x: float, center_y: float, animate: bool = False) -> bool: - self._center_x = center_x - self._center_y = center_y - self._notifyListeners() - return True - - def addListener(self, listener: "MagnificationController.OnMagnificationChangedListener"): - if listener not in self._listeners: - self._listeners.append(listener) - - def removeListener(self, listener: "MagnificationController.OnMagnificationChangedListener"): - if listener in self._listeners: - self._listeners.remove(listener) - - def _notifyListeners(self): - for listener in list(self._listeners): - try: - listener.onMagnificationChanged(self, self._magnification_region, - self._scale, self._center_x, self._center_y) - except Exception: - pass - - def isMagnifying(self) -> bool: - return abs(self._scale - 1.0) > 0.01 - - def reset(self, animate: bool = False) -> bool: - if self.isMagnifying(): - self._scale = 1.0 - self._center_x = 0.0 - self._center_y = 0.0 - self._notifyListeners() - return True - return False - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "AccessibilityEvent", "AccessibilityRecord", - "AccessibilityNodeInfo", "AccessibilityNodeProvider", - "AccessibilityService", "AccessibilityServiceInfo", - "AccessibilityWindowInfo", - "CaptioningManager", - "AccessibilityManager", - "AccessibilityServiceGestureResult", - "TouchExplorationStateChangeListener", - "AccessibilityButtonController", - "AccessibilityButtonControllerCallback", - "MagnificationConfig", - "MagnificationController", -] \ No newline at end of file diff --git a/android_pyapi/android/view_accessibility_ext.py b/android_pyapi/android/view_accessibility_ext.py deleted file mode 100644 index 0e725a5..0000000 --- a/android_pyapi/android/view_accessibility_ext.py +++ /dev/null @@ -1,744 +0,0 @@ -""" -Android View Accessibility 扩展组件 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import time -import threading -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# 无障碍事件常量 -# ═══════════════════════════════════════════════════════════════ - -TYPE_VIEW_CLICKED = 0x00000001 -TYPE_VIEW_LONG_CLICKED = 0x00000002 -TYPE_VIEW_SELECTED = 0x00000004 -TYPE_VIEW_FOCUSED = 0x00000008 -TYPE_VIEW_TEXT_CHANGED = 0x00000010 -TYPE_WINDOW_STATE_CHANGED = 0x00000020 -TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040 -TYPE_VIEW_HOVER_ENTER = 0x00000080 -TYPE_VIEW_HOVER_EXIT = 0x00000100 -TYPE_TOUCH_EXPLORATION_GESTURE_START = 0x00000200 -TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400 -TYPE_WINDOW_CONTENT_CHANGED = 0x00000800 -TYPE_VIEW_SCROLLED = 0x00001000 -TYPE_VIEW_TEXT_SELECTION_CHANGED = 0x00002000 -TYPE_ANNOUNCEMENT = 0x00004000 -TYPE_VIEW_ACCESSIBILITY_FOCUSED = 0x00008000 -TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 0x00010000 -TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 0x00020000 -TYPE_GESTURE_DETECTION_START = 0x00040000 -TYPE_GESTURE_DETECTION_END = 0x00080000 -TYPE_TOUCH_INTERACTION_START = 0x00100000 -TYPE_TOUCH_INTERACTION_END = 0x00200000 -TYPE_WINDOWS_CHANGED = 0x00400000 -TYPE_VIEW_CONTEXT_CLICKED = 0x00800000 -TYPE_ASSIST_READING_CONTEXT = 0x01000000 - -CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000001 -CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 0x00000002 -CONTENT_CHANGE_TYPE_SUBTREE = 0x00000004 -CONTENT_CHANGE_TYPE_TEXT = 0x00000008 -CONTENT_CHANGE_TYPE_UNDEFINED = 0x00000010 - -WINDOWS_CHANGE_ADDED = 0x00000001 -WINDOWS_CHANGE_REMOVED = 0x00000002 -WINDOWS_CHANGE_TITLE = 0x00000004 -WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED = 0x00000008 -WINDOWS_CHANGE_ACTIVE = 0x00000010 -WINDOWS_CHANGE_PARENT = 0x00000020 -WINDOWS_CHANGE_CHILDREN = 0x00000040 -WINDOWS_CHANGE_BOUNDS = 0x00000080 -WINDOWS_CHANGE_LAYER = 0x00000100 - -WINDOW_TYPE_ACCESSIBILITY_OVERLAY = 4 -WINDOW_TYPE_APPLICATION = 1 -WINDOW_TYPE_INPUT_METHOD = 2 -WINDOW_TYPE_SPLIT_SCREEN_DIVIDER = 5 -WINDOW_TYPE_SYSTEM = 3 - -FLAG_REPORT_VIEW_IDS = 0x10 -FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x80 -FLAG_REQUEST_FILTER_KEY_EVENTS = 0x20 -FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x40 -FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 0x08 -FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x02 -FLAG_REQUEST_ACCESSIBILITY_BUTTON = 0x100 - -CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1 -CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2 -CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4 -CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 8 - -CAPTIONING_TYPE_UNKNOWN = -1 -CAPTIONING_TYPE_FEATURE = 0 -CAPTIONING_TYPE_COUNTRY = 1 -CAPTIONING_TYPE_SPOKEN = 2 -CAPTIONING_TYPE_UNKNOWN_CHARACTER = 3 -CAPTIONING_TYPE_MUSIC = 4 -CAPTIONING_TYPE_SFX = 5 -CAPTIONING_TYPE_VOICE = 6 - -CAPTIONING_PRESET_INVALID = -1 -CAPTIONING_PRESET_CUSTOM = 0 -CAPTIONING_PRESET_WHITE_ON_BLACK = 1 -CAPTIONING_PRESET_BLACK_ON_WHITE = 2 -CAPTIONING_PRESET_YELLOW_ON_BLACK = 3 -CAPTIONING_PRESET_YELLOW_ON_BLUE = 4 -CAPTIONING_PRESET_CUSTOM_BLACK_ON_WHITE = 5 -CAPTIONING_PRESET_CUSTOM_WHITE_ON_BLACK = 6 -CAPTIONING_PRESET_CUSTOM_YELLOW_ON_BLACK = 7 -CAPTIONING_PRESET_CUSTOM_YELLOW_ON_BLUE = 8 - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityEvent -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityRecord(Stub): - """AccessibilityRecord — 无障碍记录""" - - def __init__(self): - super().__init__() - self._source_view: Optional[Any] = None - self._source_node_id: int = -1 - self._source_window_id: int = -1 - self._event_time: int = 0 - self._package_name: Optional[str] = None - self._class_name: Optional[str] = None - self._text: List[str] = [] - self._content_description: Optional[str] = None - self._item_count: int = -1 - self._current_item_index: int = -1 - self._from_index: int = -1 - self._to_index: int = -1 - self._scrollable: bool = False - self._password: bool = False - self._checked: bool = False - self._enabled: bool = True - self._clickable: bool = False - self._focusable: bool = False - self._focused: bool = False - self._long_clickable: bool = False - self._selected: bool = False - self._pass_through: bool = False - self._before_text: Optional[str] = None - self._removed_count: int = 0 - self._added_count: int = 0 - self._max_scroll_x: int = -1 - self._max_scroll_y: int = -1 - self._scroll_x: int = -1 - self._scroll_y: int = -1 - - def setSource(self, view: Any) -> None: - self._source_view = view - - def getSource(self) -> Optional[Any]: - return self._source_view - - def setSourceNodeId(self, node_id: int) -> None: - self._source_node_id = node_id - - def getSourceNodeId(self) -> int: - return self._source_node_id - - def setWindowId(self, window_id: int) -> None: - self._source_window_id = window_id - - def getWindowId(self) -> int: - return self._source_window_id - - def setEventTime(self, time: int) -> None: - self._event_time = time - - def getEventTime(self) -> int: - return self._event_time if self._event_time else int(time.time() * 1000) - - def setPackageName(self, name: str) -> None: - self._package_name = name - - def getPackageName(self) -> Optional[str]: - return self._package_name - - def setClassName(self, name: str) -> None: - self._class_name = name - - def getClassName(self) -> Optional[str]: - return self._class_name - - def setText(self, text: List[str]) -> None: - self._text = list(text) - - def getText(self) -> List[str]: - return list(self._text) - - def setContentDescription(self, desc: str) -> None: - self._content_description = desc - - def getContentDescription(self) -> Optional[str]: - return self._content_description - - def setItemCount(self, count: int) -> None: - self._item_count = count - - def getItemCount(self) -> int: - return self._item_count - - def setCurrentItemIndex(self, index: int) -> None: - self._current_item_index = index - - def getCurrentItemIndex(self) -> int: - return self._current_item_index - - def setFromIndex(self, index: int) -> None: - self._from_index = index - - def getFromIndex(self) -> int: - return self._from_index - - def setToIndex(self, index: int) -> None: - self._to_index = index - - def getToIndex(self) -> int: - return self._to_index - - def setScrollable(self, scrollable: bool) -> None: - self._scrollable = scrollable - - def isScrollable(self) -> bool: - return self._scrollable - - def setPassword(self, password: bool) -> None: - self._password = password - - def isPassword(self) -> bool: - return self._password - - def setChecked(self, checked: bool) -> None: - self._checked = checked - - def isChecked(self) -> bool: - return self._checked - - def setEnabled(self, enabled: bool) -> None: - self._enabled = enabled - - def isEnabled(self) -> bool: - return self._enabled - - def setClickable(self, clickable: bool) -> None: - self._clickable = clickable - - def isClickable(self) -> bool: - return self._clickable - - def setFocusable(self, focusable: bool) -> None: - self._focusable = focusable - - def isFocusable(self) -> bool: - return self._focusable - - def setFocused(self, focused: bool) -> None: - self._focused = focused - - def isFocused(self) -> bool: - return self._focused - - def setLongClickable(self, long_clickable: bool) -> None: - self._long_clickable = long_clickable - - def isLongClickable(self) -> bool: - return self._long_clickable - - def setSelected(self, selected: bool) -> None: - self._selected = selected - - def isSelected(self) -> bool: - return self._selected - - def setBeforeText(self, text: str) -> None: - self._before_text = text - - def getBeforeText(self) -> Optional[str]: - return self._before_text - - def setRemovedCount(self, count: int) -> None: - self._removed_count = count - - def getRemovedCount(self) -> int: - return self._removed_count - - def setAddedCount(self, count: int) -> None: - self._added_count = count - - def getAddedCount(self) -> int: - return self._added_count - - def setMaxScrollX(self, max_x: int) -> None: - self._max_scroll_x = max_x - - def getMaxScrollX(self) -> int: - return self._max_scroll_x - - def setMaxScrollY(self, max_y: int) -> None: - self._max_scroll_y = max_y - - def getMaxScrollY(self) -> int: - return self._max_scroll_y - - def setScrollX(self, x: int) -> None: - self._scroll_x = x - - def getScrollX(self) -> int: - return self._scroll_x - - def setScrollY(self, y: int) -> None: - self._scroll_y = y - - def getScrollY(self) -> int: - return self._scroll_y - - def toString(self) -> str: - return (f"AccessibilityRecord(class={self._class_name}, " - f"text={self._text})") - - -class AccessibilityEvent(AccessibilityRecord): - """AccessibilityEvent — 无障碍事件""" - - INVALID_POSITION = -1 - MAX_TEXT_LENGTH = 500 - - def __init__(self, event_type: int = 0): - super().__init__() - self._event_type = event_type - self._event_type_int = event_type - self._movement_granularity: int = 0 - self._action: int = 0 - self._content_change_types: int = 0 - self._window_changes: int = 0 - - @staticmethod - def obtain(event_type: int = 0) -> "AccessibilityEvent": - return AccessibilityEvent(event_type) - - @staticmethod - def obtain(event: "AccessibilityEvent") -> "AccessibilityEvent": - e = AccessibilityEvent(event.getEventType()) - return e - - def getEventType(self) -> int: - return self._event_type - - def setEventType(self, event_type: int) -> None: - self._event_type = event_type - - def setMovementGranularity(self, granularity: int) -> None: - self._movement_granularity = granularity - - def getMovementGranularity(self) -> int: - return self._movement_granularity - - def setAction(self, action: int) -> None: - self._action = action - - def getAction(self) -> int: - return self._action - - def setContentChangeTypes(self, types: int) -> None: - self._content_change_types = types - - def getContentChangeTypes(self) -> int: - return self._content_change_types - - def setWindowChanges(self, changes: int) -> None: - self._window_changes = changes - - def getWindowChanges(self) -> int: - return self._window_changes - - def recycle(self) -> None: - pass - - def toString(self) -> str: - return f"AccessibilityEvent(type={self._event_type})" - - -class AccessibilityWindowInfo(Stub): - """AccessibilityWindowInfo — 无障碍窗口信息""" - - TYPE_ACCESSIBILITY_OVERLAY = WINDOW_TYPE_ACCESSIBILITY_OVERLAY - TYPE_APPLICATION = WINDOW_TYPE_APPLICATION - TYPE_INPUT_METHOD = WINDOW_TYPE_INPUT_METHOD - TYPE_SPLIT_SCREEN_DIVIDER = WINDOW_TYPE_SPLIT_SCREEN_DIVIDER - TYPE_SYSTEM = WINDOW_TYPE_SYSTEM - - UNDEFINED = -1 - ACTIVE = 0 - INACTIVE = 1 - - def __init__(self): - super().__init__() - self._type: int = self.TYPE_APPLICATION - self._id: int = -1 - self._layer: int = 0 - self._is_active = False - self._is_focused = False - self._is_accessibility_focused = False - self._title: Optional[str] = None - self._root: Optional[Any] = None - self._parent: Optional["AccessibilityWindowInfo"] = None - self._bounds: Tuple[int, int, int, int] = (0, 0, 0, 0) - self._anchor: Optional[Any] = None - self._picture_in_picture = False - self._first_child: Optional["AccessibilityWindowInfo"] = None - self._last_child: Optional["AccessibilityWindowInfo"] = None - self._child_count: int = 0 - - def getType(self) -> int: - return self._type - - def setType(self, type_val: int) -> None: - self._type = type_val - - def getId(self) -> int: - return self._id - - def setId(self, id_val: int) -> None: - self._id = id_val - - def setLayer(self, layer: int) -> None: - self._layer = layer - - def getLayer(self) -> int: - return self._layer - - def isActive(self) -> bool: - return self._is_active - - def setActive(self, active: bool) -> None: - self._is_active = active - - def isFocused(self) -> bool: - return self._is_focused - - def setFocused(self, focused: bool) -> None: - self._is_focused = focused - - def isAccessibilityFocused(self) -> bool: - return self._is_accessibility_focused - - def setAccessibilityFocused(self, focused: bool) -> None: - self._is_accessibility_focused = focused - - def getTitle(self) -> Optional[str]: - return self._title - - def setTitle(self, title: str) -> None: - self._title = title - - def setBoundsInScreen(self, bounds: Tuple[int, int, int, int]) -> None: - self._bounds = bounds - - def getBoundsInScreen(self) -> Tuple[int, int, int, int]: - return self._bounds - - def setAnchorId(self, anchor: Any) -> None: - self._anchor = anchor - - def getAnchorId(self) -> Optional[Any]: - return self._anchor - - def isInPictureInPictureMode(self) -> bool: - return self._picture_in_picture - - def setPictureInPictureMode(self, pip: bool) -> None: - self._picture_in_picture = pip - - def getRoot(self) -> Optional[Any]: - return self._root - - def setRoot(self, root: Any) -> None: - self._root = root - - def getParent(self) -> Optional["AccessibilityWindowInfo"]: - return self._parent - - def getChild(self, index: int) -> Optional["AccessibilityWindowInfo"]: - if index == 0: - return self._first_child - return None - - def getChildCount(self) -> int: - return self._child_count - - @staticmethod - def obtain() -> "AccessibilityWindowInfo": - return AccessibilityWindowInfo() - - @staticmethod - def obtain(info: "AccessibilityWindowInfo") -> "AccessibilityWindowInfo": - return AccessibilityWindowInfo() - - def recycle(self) -> None: - pass - - def toString(self) -> str: - types = {1: "APPLICATION", 2: "INPUT_METHOD", 3: "SYSTEM", 4: "OVERLAY"} - return (f"AccessibilityWindowInfo(type={types.get(self._type, 'UNKNOWN')}, " - f"id={self._id})") - - -# ═══════════════════════════════════════════════════════════════ -# AccessibilityManager 扩展 -# ═══════════════════════════════════════════════════════════════ - -class AccessibilityManager(Stub): - """AccessibilityManager — 无障碍管理器扩展""" - - FLAG_REPORT_VIEW_IDS = FLAG_REPORT_VIEW_IDS - FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY - FLAG_REQUEST_FILTER_KEY_EVENTS = FLAG_REQUEST_FILTER_KEY_EVENTS - FLAG_REQUEST_TOUCH_EXPLORATION_MODE = FLAG_REQUEST_TOUCH_EXPLORATION_MODE - FLAG_RETRIEVE_INTERACTIVE_WINDOWS = FLAG_RETRIEVE_INTERACTIVE_WINDOWS - FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = FLAG_INCLUDE_NOT_IMPORTANT_VIEWS - FLAG_REQUEST_ACCESSIBILITY_BUTTON = FLAG_REQUEST_ACCESSIBILITY_BUTTON - - class AccessibilityStateChangeListener(Stub): - """AccessibilityManager.AccessibilityStateChangeListener""" - def onAccessibilityStateChanged(self, enabled: bool) -> None: - pass - - class TouchExplorationStateChangeListener(Stub): - """AccessibilityManager.TouchExplorationStateChangeListener""" - def onTouchExplorationStateChanged(self, enabled: bool) -> None: - pass - - class HighTextContrastChangeListener(Stub): - def onHighTextContrastStateChanged(self, enabled: bool) -> None: - pass - - def __init__(self): - super().__init__() - self._enabled = False - self._touch_exploration_enabled = False - self._high_text_contrast_enabled = False - self._state_listeners: List[AccessibilityStateChangeListener] = [] - self._touch_listeners: List[TouchExplorationStateChangeListener] = [] - self._contrast_listeners: List[Any] = [] - self._accessibility_button_available = False - - def isEnabled(self) -> bool: - return self._enabled - - def setEnabled(self, enabled: bool) -> None: - old = self._enabled - self._enabled = enabled - if old != enabled: - for listener in list(self._state_listeners): - listener.onAccessibilityStateChanged(enabled) - - def isTouchExplorationEnabled(self) -> bool: - return self._touch_exploration_enabled - - def setTouchExplorationEnabled(self, enabled: bool) -> None: - old = self._touch_exploration_enabled - self._touch_exploration_enabled = enabled - if old != enabled: - for listener in list(self._touch_listeners): - listener.onTouchExplorationStateChanged(enabled) - - def isHighTextContrastEnabled(self) -> bool: - return self._high_text_contrast_enabled - - def addAccessibilityStateChangeListener(self, listener: AccessibilityStateChangeListener) -> None: - if listener not in self._state_listeners: - self._state_listeners.append(listener) - - def removeAccessibilityStateChangeListener(self, listener: AccessibilityStateChangeListener) -> None: - if listener in self._state_listeners: - self._state_listeners.remove(listener) - - def addTouchExplorationStateChangeListener(self, listener: TouchExplorationStateChangeListener) -> None: - if listener not in self._touch_listeners: - self._touch_listeners.append(listener) - - def removeTouchExplorationStateChangeListener(self, listener: TouchExplorationStateChangeListener) -> None: - if listener in self._touch_listeners: - self._touch_listeners.remove(listener) - - def addHighTextContrastStateChangeListener(self, listener: Any) -> None: - if listener not in self._contrast_listeners: - self._contrast_listeners.append(listener) - - def removeHighTextContrastStateChangeListener(self, listener: Any) -> None: - if listener in self._contrast_listeners: - self._contrast_listeners.remove(listener) - - def setAccessibilityButtonAvailable(self, available: bool) -> None: - self._accessibility_button_available = available - - def isAccessibilityButtonAvailable(self) -> bool: - return self._accessibility_button_available - - def sendAccessibilityEvent(self, event: AccessibilityEvent) -> None: - pass - - def interrupt(self) -> None: - pass - - def getInstalledAccessibilityServiceList(self) -> List[Any]: - return [] - - def getEnabledAccessibilityServiceList(self) -> List[Any]: - return [] - - def toString(self) -> str: - return (f"AccessibilityManager(enabled={self._enabled}, " - f"touchExploration={self._touch_exploration_enabled})") - - -# ═══════════════════════════════════════════════════════════════ -# CaptioningManager -# ═══════════════════════════════════════════════════════════════ - -class CaptioningManager(Stub): - """CaptioningManager — 字幕管理""" - - CAPTIONING_TYPE_UNKNOWN = CAPTIONING_TYPE_UNKNOWN - CAPTIONING_TYPE_FEATURE = CAPTIONING_TYPE_FEATURE - CAPTIONING_TYPE_COUNTRY = CAPTIONING_TYPE_COUNTRY - CAPTIONING_TYPE_SPOKEN = CAPTIONING_TYPE_SPOKEN - CAPTIONING_TYPE_UNKNOWN_CHARACTER = CAPTIONING_TYPE_UNKNOWN_CHARACTER - CAPTIONING_TYPE_MUSIC = CAPTIONING_TYPE_MUSIC - CAPTIONING_TYPE_SFX = CAPTIONING_TYPE_SFX - CAPTIONING_TYPE_VOICE = CAPTIONING_TYPE_VOICE - - class CaptionStyle(Stub): - """CaptioningManager.CaptionStyle — 字幕风格""" - - EDGE_TYPE_NONE = 0 - EDGE_TYPE_OUTLINE = 1 - EDGE_TYPE_DROP_SHADOW = 2 - EDGE_TYPE_RAISED = 3 - EDGE_TYPE_DEPRESSED = 4 - - PRESET_CUSTOM = CAPTIONING_PRESET_CUSTOM - PRESET_WHITE_ON_BLACK = CAPTIONING_PRESET_WHITE_ON_BLACK - PRESET_BLACK_ON_WHITE = CAPTIONING_PRESET_BLACK_ON_WHITE - PRESET_YELLOW_ON_BLACK = CAPTIONING_PRESET_YELLOW_ON_BLACK - PRESET_YELLOW_ON_BLUE = CAPTIONING_PRESET_YELLOW_ON_BLUE - - def __init__(self): - super().__init__() - self._foreground_color: int = 0xFFFFFFFF - self._background_color: int = 0xFF000000 - self._edge_color: int = 0xFF000000 - self._edge_type: int = self.EDGE_TYPE_NONE - self._window_color: int = 0xFF000000 - self._foreground_size: int = 1 - self._preset: int = CAPTIONING_PRESET_WHITE_ON_BLACK - - def getForegroundColor(self) -> int: - return self._foreground_color - - def getBackgroundColor(self) -> int: - return self._background_color - - def getEdgeColor(self) -> int: - return self._edge_color - - def getEdgeType(self) -> int: - return self._edge_type - - def getWindowColor(self) -> int: - return self._window_color - - def getForegroundSize(self) -> int: - return self._foreground_size - - def getPreset(self) -> int: - return self._preset - - def hasBackgroundColor(self) -> bool: - return (self._background_color >> 24) > 0 - - def hasForegroundColor(self) -> bool: - return (self._foreground_color >> 24) > 0 - - def hasEdgeType(self) -> bool: - return self._edge_type != self.EDGE_TYPE_NONE - - def hasWindowColor(self) -> bool: - return (self._window_color >> 24) > 0 - - @staticmethod - def getCustomStyle(style_id: int) -> "CaptioningManager.CaptionStyle": - return CaptioningManager.CaptionStyle() - - @staticmethod - def getPresetStyle(preset: int) -> "CaptioningManager.CaptionStyle": - return CaptioningManager.CaptionStyle() - - def applyStyle(self, style: "CaptioningManager.CaptionStyle") -> None: - pass - - def toString(self) -> str: - return f"CaptionStyle(preset={self._preset})" - - class CaptioningChangeListener(Stub): - """CaptioningManager.CaptioningChangeListener""" - - def onEnabledChanged(self, enabled: bool) -> None: - pass - - def onUserStyleChanged(self, style: "CaptioningManager.CaptionStyle") -> None: - pass - - def onLocaleChanged(self, locale: Any) -> None: - pass - - def onFontScaleChanged(self, scale: float) -> None: - pass - - def __init__(self): - super().__init__() - self._enabled = False - self._user_style: Optional[CaptioningManager.CaptionStyle] = None - self._font_scale: float = 1.0 - self._locale: Optional[Any] = None - self._listeners: List[CaptioningManager.CaptioningChangeListener] = [] - - def isEnabled(self) -> bool: - return self._enabled - - def getFontScale(self) -> float: - return self._font_scale - - def getLocale(self) -> Optional[Any]: - return self._locale - - def getUserStyle(self) -> CaptionStyle: - return self._user_style or CaptioningManager.CaptionStyle() - - def addCaptioningChangeListener(self, listener: CaptioningChangeListener) -> None: - if listener not in self._listeners: - self._listeners.append(listener) - - def removeCaptioningChangeListener(self, listener: CaptioningChangeListener) -> None: - if listener in self._listeners: - self._listeners.remove(listener) - - def toString(self) -> str: - return f"CaptioningManager(enabled={self._enabled})" - - -IllegalArgumentException = type("IllegalStateException", (RuntimeException,), {}) \ No newline at end of file diff --git a/android_pyapi/android/view_animation.py b/android_pyapi/android/view_animation.py deleted file mode 100644 index 1c703ea..0000000 --- a/android_pyapi/android/view_animation.py +++ /dev/null @@ -1,935 +0,0 @@ -""" -Android View Animation 组件 — Animation, AnimationSet, Interpolators, -AnimationUtils, Transformation 等 -方法签名完全对齐 Android SDK -""" - -import math -import sys -import time -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError, android_context - - -# ═══════════════════════════════════════════════════════════════ -# Interpolator -# ═══════════════════════════════════════════════════════════════ - -class Interpolator(Stub): - """Interpolator — 插值器接口""" - - def getInterpolation(self, input: float) -> float: - return input - - -# ═══════════════════════════════════════════════════════════════ -# AccelerateInterpolator -# ═══════════════════════════════════════════════════════════════ - -class AccelerateInterpolator(Interpolator): - """AccelerateInterpolator — 加速插值器(先慢后快)""" - - def __init__(self, factor: float = 1.0): - super().__init__() - self._factor = factor - self._double_factor = 2.0 * factor - - def getInterpolation(self, input: float) -> float: - if self._factor == 1.0: - return input * input - return input ** (self._double_factor) - - -# ═══════════════════════════════════════════════════════════════ -# DecelerateInterpolator -# ═══════════════════════════════════════════════════════════════ - -class DecelerateInterpolator(Interpolator): - """DecelerateInterpolator — 减速插值器(先快后慢)""" - - def __init__(self, factor: float = 1.0): - super().__init__() - self._factor = factor - - def getInterpolation(self, input: float) -> float: - if self._factor == 1.0: - return 1.0 - (1.0 - input) * (1.0 - input) - return 1.0 - (1.0 - input) ** (2.0 * self._factor) - - -# ═══════════════════════════════════════════════════════════════ -# AccelerateDecelerateInterpolator -# ═══════════════════════════════════════════════════════════════ - -class AccelerateDecelerateInterpolator(Interpolator): - """AccelerateDecelerateInterpolator — 加速减速插值器""" - - def getInterpolation(self, input: float) -> float: - return float(math.cos((input + 1) * math.pi) / 2.0 + 0.5) - - -# ═══════════════════════════════════════════════════════════════ -# LinearInterpolator -# ═══════════════════════════════════════════════════════════════ - -class LinearInterpolator(Interpolator): - """LinearInterpolator — 线性插值器""" - - def getInterpolation(self, input: float) -> float: - return input - - -# ═══════════════════════════════════════════════════════════════ -# OvershootInterpolator -# ═══════════════════════════════════════════════════════════════ - -class OvershootInterpolator(Interpolator): - """OvershootInterpolator — 超出插值器(越过目标再回弹)""" - - def __init__(self, tension: float = 2.0): - super().__init__() - self._tension = tension - - def getInterpolation(self, input: float) -> float: - input -= 1.0 - return input * input * ((self._tension + 1) * input + self._tension) + 1.0 - - -# ═══════════════════════════════════════════════════════════════ -# AnticipateInterpolator -# ═══════════════════════════════════════════════════════════════ - -class AnticipateInterpolator(Interpolator): - """AnticipateInterpolator — 预判插值器(先反向后正向前进)""" - - def __init__(self, tension: float = 2.0): - super().__init__() - self._tension = tension - - def getInterpolation(self, input: float) -> float: - return input * input * ((self._tension + 1) * input - self._tension) - - -# ═══════════════════════════════════════════════════════════════ -# AnticipateOvershootInterpolator -# ═══════════════════════════════════════════════════════════════ - -class AnticipateOvershootInterpolator(Interpolator): - """AnticipateOvershootInterpolator — 预判超出插值器""" - - def __init__(self, tension: float = 2.0, extra_tension: float = 1.5): - super().__init__() - self._tension = tension - self._extra_tension = extra_tension - - def getInterpolation(self, input: float) -> float: - if input < 0.5: - t = input * 2.0 - return 0.5 * t * t * ((self._tension + 1) * t - self._tension) - else: - t = input * 2.0 - 2.0 - return 0.5 * (t * t * ((self._tension + 1) * t + self._tension) + 2.0) - - -# ═══════════════════════════════════════════════════════════════ -# BounceInterpolator -# ═══════════════════════════════════════════════════════════════ - -class BounceInterpolator(Interpolator): - """BounceInterpolator — 弹跳插值器""" - - @staticmethod - def _bounce(t: float) -> float: - t2 = t * t - return t2 * 8.0 - - def getInterpolation(self, input: float) -> float: - if input < 0.3535: - return BounceInterpolator._bounce(input) - elif input < 0.7408: - return BounceInterpolator._bounce(input - 0.54719) + 0.7 - elif input < 0.9644: - return BounceInterpolator._bounce(input - 0.8526) + 0.9 - else: - return BounceInterpolator._bounce(input - 1.0435) + 0.95 - - -# ═══════════════════════════════════════════════════════════════ -# CycleInterpolator -# ═══════════════════════════════════════════════════════════════ - -class CycleInterpolator(Interpolator): - """CycleInterpolator — 周期插值器(正弦循环)""" - - def __init__(self, cycles: float = 1.0): - super().__init__() - self._cycles = cycles - - def getInterpolation(self, input: float) -> float: - return float(math.sin(2.0 * math.pi * self._cycles * input)) - - -# ═══════════════════════════════════════════════════════════════ -# PathInterpolator -# ═══════════════════════════════════════════════════════════════ - -class PathInterpolator(Interpolator): - """PathInterpolator — 路径插值器""" - - def __init__(self, control_x1: float = 0.0, control_y1: float = 0.0, - control_x2: float = 1.0, control_y2: float = 1.0): - super().__init__() - self._control_x1 = control_x1 - self._control_y1 = control_y1 - self._control_x2 = control_x2 - self._control_y2 = control_y2 - - def getInterpolation(self, input: float) -> float: - return self._cubic_bezier(input) - - def _cubic_bezier(self, t: float) -> float: - u = 1.0 - t - return (3.0 * u * u * t * self._control_y1 + - 3.0 * u * t * t * self._control_y2 + - t * t * t) - - -# ═══════════════════════════════════════════════════════════════ -# Transformation -# ═══════════════════════════════════════════════════════════════ - -class Transformation(Stub): - """Transformation — 动画变换矩阵""" - - TYPE_IDENTITY = 0 - TYPE_ALPHA = 1 - TYPE_BOTH = 2 - - def __init__(self): - super().__init__() - self._alpha: float = 1.0 - self._matrix: Any = None # 占位,实际 Android 为 Matrix 对象 - self._transformation_type: int = Transformation.TYPE_IDENTITY - - def getAlpha(self) -> float: - return self._alpha - - def setAlpha(self, alpha: float): - self._alpha = alpha - - def getMatrix(self) -> Any: - return self._matrix - - def setMatrix(self, matrix: Any): - self._matrix = matrix - - def setTransformationType(self, transformation_type: int): - self._transformation_type = transformation_type - - def getTransformationType(self) -> int: - return self._transformation_type - - def clear(self): - self._alpha = 1.0 - self._matrix = None - self._transformation_type = Transformation.TYPE_IDENTITY - - def compose(self, other: "Transformation"): - if other is None: - return - self._alpha *= other._alpha - if self._matrix is None: - self._matrix = other._matrix - elif other._matrix is not None: - pass # 占位:矩阵乘法 - self._transformation_type = max(self._transformation_type, - other._transformation_type) - - def toString(self) -> str: - return (f"Transformation(alpha={self._alpha}, " - f"type={self._transformation_type})") - - -# ═══════════════════════════════════════════════════════════════ -# Animation -# ═══════════════════════════════════════════════════════════════ - -class Animation(Stub): - """Animation — 动画基类""" - - INFINITE = -1 - RESTART = 1 - REVERSE = 2 - - ABSOLUTE = 0 - RELATIVE_TO_SELF = 1 - RELATIVE_TO_PARENT = 2 - - START_ON_FIRST_FRAME = -1 - - class AnimationListener(Stub): - """AnimationListener — 动画监听器""" - - def onAnimationStart(self, animation: "Animation"): - pass - - def onAnimationEnd(self, animation: "Animation"): - pass - - def onAnimationRepeat(self, animation: "Animation"): - pass - - def __init__(self): - super().__init__() - self._duration: int = 0 - self._start_offset: int = 0 - self._repeat_count: int = 0 - self._repeat_mode: int = Animation.RESTART - self._fill_before: bool = False - self._fill_after: bool = False - self._fill_enabled: bool = False - self._z_adjustment: int = 0 - self._interpolator: Optional[Interpolator] = None - self._listener: Optional["Animation.AnimationListener"] = None - self._start_time: Optional[int] = None - self._started: bool = False - self._ended: bool = False - self._cycle: float = 0.0 - self._current_repeat: int = 0 - self._initialized: bool = False - self._scale_factor: float = 1.0 - self._paused: bool = False - self._more: bool = True - - def start(self): - self._start_time = self._current_time_millis() - self._started = True - self._ended = False - self._current_repeat = 0 - self._more = True - if self._listener: - self._listener.onAnimationStart(self) - - def startNow(self): - self.start() - - def cancel(self): - self._ended = True - self._more = False - - def reset(self): - self._start_time = None - self._started = False - self._ended = False - self._current_repeat = 0 - self._cycle = 0.0 - self._more = True - self._initialized = False - - def setDuration(self, duration_millis: int): - self._duration = duration_millis - - def getDuration(self) -> int: - return self._duration - - def setStartOffset(self, start_offset: int): - self._start_offset = start_offset - - def getStartOffset(self) -> int: - return self._start_offset - - def setRepeatCount(self, repeat_count: int): - self._repeat_count = repeat_count - - def getRepeatCount(self) -> int: - return self._repeat_count - - def setRepeatMode(self, repeat_mode: int): - self._repeat_mode = repeat_mode - - def getRepeatMode(self) -> int: - return self._repeat_mode - - def setInterpolator(self, interpolator: Optional[Interpolator]): - self._interpolator = interpolator - - def getInterpolator(self) -> Optional[Interpolator]: - return self._interpolator - - def setAnimationListener(self, listener: "Animation.AnimationListener"): - self._listener = listener - - def getAnimationListener(self) -> Optional["Animation.AnimationListener"]: - return self._listener - - def getTransformation(self, current_time: int, - out_transformation: Transformation) -> bool: - if self._start_time is None: - self._start_time = current_time - - elapsed = current_time - self._start_time - self._start_offset - if elapsed < 0: - out_transformation.clear() - return True - - duration = self._duration - if duration == 0: - duration = 1 - - normalized = elapsed / duration - - if normalized >= 1.0: - if self._current_repeat < self._repeat_count or self._repeat_count == self.INFINITE: - if self._repeat_mode == Animation.REVERSE: - normalized = 0.0 - else: - normalized = 0.0 - self._current_repeat += 1 - if self._listener: - self._listener.onAnimationRepeat(self) - else: - normalized = 1.0 - self._ended = True - self._more = False - self._apply_transformation(1.0, out_transformation) - if self._listener: - self._listener.onAnimationEnd(self) - return False - - if normalized > 1.0: - normalized = 1.0 - - if self._interpolator: - interpolated = self._interpolator.getInterpolation(normalized) - else: - interpolated = normalized - - self._cycle = normalized - self._apply_transformation(interpolated, out_transformation) - return self._more - - def _apply_transformation(self, interpolated_time: float, t: Transformation): - pass - - def hasEnded(self) -> bool: - return self._ended - - def hasStarted(self) -> bool: - return self._started - - def setFillBefore(self, fill_before: bool): - self._fill_before = fill_before - - def getFillBefore(self) -> bool: - return self._fill_before - - def setFillAfter(self, fill_after: bool): - self._fill_after = fill_after - - def getFillAfter(self) -> bool: - return self._fill_after - - def setFillEnabled(self, fill_enabled: bool): - self._fill_enabled = fill_enabled - - def getFillEnabled(self) -> bool: - return self._fill_enabled - - def setZAdjustment(self, z_adjustment: int): - self._z_adjustment = z_adjustment - - def getZAdjustment(self) -> int: - return self._z_adjustment - - def willChangeBounds(self) -> bool: - return True - - def willChangeTransformationMatrix(self) -> bool: - return True - - def initialize(self, width: int, height: int, parent_width: int, parent_height: int): - self._initialized = True - - def restrictDuration(self, duration_millis: int): - if self._duration > duration_millis: - self._duration = duration_millis - - def scaleCurrentDuration(self, scale: float): - self._duration = int(self._duration * scale) - - @staticmethod - def _current_time_millis() -> int: - return int(time.time() * 1000) - - def toString(self) -> str: - return (f"Animation(duration={self._duration}, " - f"repeatCount={self._repeat_count}, " - f"started={self._started})") - - -# ═══════════════════════════════════════════════════════════════ -# AlphaAnimation -# ═══════════════════════════════════════════════════════════════ - -class AlphaAnimation(Animation): - """AlphaAnimation — 透明度动画""" - - def __init__(self, from_alpha: float, to_alpha: float): - super().__init__() - self._from_alpha = from_alpha - self._to_alpha = to_alpha - - def _apply_transformation(self, interpolated_time: float, t: Transformation): - alpha = self._from_alpha + (self._to_alpha - self._from_alpha) * interpolated_time - t.setAlpha(alpha) - - def getFromAlpha(self) -> float: - return self._from_alpha - - def getToAlpha(self) -> float: - return self._to_alpha - - -# ═══════════════════════════════════════════════════════════════ -# ScaleAnimation -# ═══════════════════════════════════════════════════════════════ - -class ScaleAnimation(Animation): - """ScaleAnimation — 缩放动画""" - - def __init__(self, from_x: float, to_x: float, from_y: float, to_y: float, - pivot_x: float = 0.0, pivot_y: float = 0.0, - pivot_x_type: int = Animation.ABSOLUTE, - pivot_y_type: int = Animation.ABSOLUTE): - super().__init__() - self._from_x = from_x - self._to_x = to_x - self._from_y = from_y - self._to_y = to_y - self._pivot_x = pivot_x - self._pivot_y = pivot_y - self._pivot_x_type = pivot_x_type - self._pivot_y_type = pivot_y_type - - def _apply_transformation(self, interpolated_time: float, t: Transformation): - sx = self._from_x + (self._to_x - self._from_x) * interpolated_time - sy = self._from_y + (self._to_y - self._from_y) * interpolated_time - # 占位:Matrix 的缩放操作 - if t.getMatrix() is not None: - pass - - def getFromX(self) -> float: - return self._from_x - - def getToX(self) -> float: - return self._to_x - - def getFromY(self) -> float: - return self._from_y - - def getToY(self) -> float: - return self._to_y - - def getPivotX(self) -> float: - return self._pivot_x - - def getPivotY(self) -> float: - return self._pivot_y - - -# ═══════════════════════════════════════════════════════════════ -# TranslateAnimation -# ═══════════════════════════════════════════════════════════════ - -class TranslateAnimation(Animation): - """TranslateAnimation — 平移动画""" - - def __init__(self, from_x: float, to_x: float, from_y: float, to_y: float, - from_x_type: int = Animation.ABSOLUTE, - to_x_type: int = Animation.ABSOLUTE, - from_y_type: int = Animation.ABSOLUTE, - to_y_type: int = Animation.ABSOLUTE): - super().__init__() - self._from_x = from_x - self._to_x = to_x - self._from_y = from_y - self._to_y = to_y - self._from_x_type = from_x_type - self._to_x_type = to_x_type - self._from_y_type = from_y_type - self._to_y_type = to_y_type - - def _apply_transformation(self, interpolated_time: float, t: Transformation): - dx = self._from_x + (self._to_x - self._from_x) * interpolated_time - dy = self._from_y + (self._to_y - self._from_y) * interpolated_time - if t.getMatrix() is not None: - pass - - def getFromX(self) -> float: - return self._from_x - - def getToX(self) -> float: - return self._to_x - - def getFromY(self) -> float: - return self._from_y - - def getToY(self) -> float: - return self._to_y - - -# ═══════════════════════════════════════════════════════════════ -# RotateAnimation -# ═══════════════════════════════════════════════════════════════ - -class RotateAnimation(Animation): - """RotateAnimation — 旋转动画""" - - def __init__(self, from_degrees: float, to_degrees: float, - pivot_x: float = 0.0, pivot_y: float = 0.0, - pivot_x_type: int = Animation.ABSOLUTE, - pivot_y_type: int = Animation.ABSOLUTE): - super().__init__() - self._from_degrees = from_degrees - self._to_degrees = to_degrees - self._pivot_x = pivot_x - self._pivot_y = pivot_y - self._pivot_x_type = pivot_x_type - self._pivot_y_type = pivot_y_type - - def _apply_transformation(self, interpolated_time: float, t: Transformation): - degrees = self._from_degrees + (self._to_degrees - self._from_degrees) * interpolated_time - if t.getMatrix() is not None: - pass - - def getFromDegrees(self) -> float: - return self._from_degrees - - def getToDegrees(self) -> float: - return self._to_degrees - - def getPivotX(self) -> float: - return self._pivot_x - - def getPivotY(self) -> float: - return self._pivot_y - - -# ═══════════════════════════════════════════════════════════════ -# AnimationSet -# ═══════════════════════════════════════════════════════════════ - -class AnimationSet(Animation): - """AnimationSet — 动画集合""" - - def __init__(self, share_interpolator: bool = True): - super().__init__() - self._share_interpolator = share_interpolator - self._animations: List[Animation] = [] - - def addAnimation(self, animation: Animation): - self._animations.append(animation) - if self._share_interpolator and self._interpolator: - animation.setInterpolator(self._interpolator) - - def getAnimations(self) -> List[Animation]: - return list(self._animations) - - def setDuration(self, duration_millis: int): - super().setDuration(duration_millis) - for anim in self._animations: - anim.setDuration(duration_millis) - - def setFillAfter(self, fill_after: bool): - super().setFillAfter(fill_after) - for anim in self._animations: - anim.setFillAfter(fill_after) - - def setFillBefore(self, fill_before: bool): - super().setFillBefore(fill_before) - for anim in self._animations: - anim.setFillBefore(fill_before) - - def setRepeatMode(self, repeat_mode: int): - super().setRepeatMode(repeat_mode) - for anim in self._animations: - anim.setRepeatMode(repeat_mode) - - def setRepeatCount(self, repeat_count: int): - super().setRepeatCount(repeat_count) - for anim in self._animations: - anim.setRepeatCount(repeat_count) - - def setStartOffset(self, start_offset: int): - super().setStartOffset(start_offset) - for anim in self._animations: - anim.setStartOffset(start_offset) - - def setInterpolator(self, interpolator: Optional[Interpolator]): - super().setInterpolator(interpolator) - if self._share_interpolator: - for anim in self._animations: - anim.setInterpolator(interpolator) - - def start(self): - super().start() - for anim in self._animations: - anim.start() - - def reset(self): - super().reset() - for anim in self._animations: - anim.reset() - - def cancel(self): - super().cancel() - for anim in self._animations: - anim.cancel() - - def getTransformation(self, current_time: int, - out_transformation: Transformation) -> bool: - more = False - for anim in self._animations: - local_t = Transformation() - if anim.getTransformation(current_time, local_t): - more = True - out_transformation.compose(local_t) - return more - - def initialize(self, width: int, height: int, parent_width: int, parent_height: int): - super().initialize(width, height, parent_width, parent_height) - for anim in self._animations: - anim.initialize(width, height, parent_width, parent_height) - - def willChangeBounds(self) -> bool: - for anim in self._animations: - if anim.willChangeBounds(): - return True - return False - - def willChangeTransformationMatrix(self) -> bool: - for anim in self._animations: - if anim.willChangeTransformationMatrix(): - return True - return False - - def getAnimations(self) -> List[Animation]: - return list(self._animations) - - def restrictDuration(self, duration_millis: int): - super().restrictDuration(duration_millis) - for anim in self._animations: - anim.restrictDuration(duration_millis) - - def scaleCurrentDuration(self, scale: float): - super().scaleCurrentDuration(scale) - for anim in self._animations: - anim.scaleCurrentDuration(scale) - - -# ═══════════════════════════════════════════════════════════════ -# Animation.AnimationListener (已定义在 Animation 类中) -# ═══════════════════════════════════════════════════════════════ - -# Animation.AnimationListener is defined inside the Animation class above - - -# ═══════════════════════════════════════════════════════════════ -# AnimationUtils -# ═══════════════════════════════════════════════════════════════ - -class AnimationUtils(Stub): - """AnimationUtils — 动画工具类""" - - @staticmethod - def loadAnimation(context: Any, id: int) -> Animation: - """加载动画资源(占位实现)""" - sys.stderr.write(f"[AnimationUtils] loadAnimation: id={id}\n") - return Animation() - - @staticmethod - def makeInAnimation(context: Any, from_left: bool) -> Animation: - """创建进入动画""" - anim = AlphaAnimation(0.0, 1.0) - anim.setDuration(300) - return anim - - @staticmethod - def makeOutAnimation(context: Any, to_left: bool) -> Animation: - """创建退出动画""" - anim = AlphaAnimation(1.0, 0.0) - anim.setDuration(300) - return anim - - @staticmethod - def makeInChildBottomAnimation(context: Any) -> Animation: - """创建子视图从底部进入动画""" - anim = TranslateAnimation(0, 0, 1.0, 0.0, - from_x_type=Animation.RELATIVE_TO_SELF, - to_x_type=Animation.RELATIVE_TO_SELF, - from_y_type=Animation.RELATIVE_TO_SELF, - to_y_type=Animation.RELATIVE_TO_SELF) - anim.setDuration(300) - return anim - - @staticmethod - def currentAnimationTimeMillis() -> int: - return int(time.time() * 1000) - - -# ═══════════════════════════════════════════════════════════════ -# LayoutAnimationController -# ═══════════════════════════════════════════════════════════════ - -class LayoutAnimationController(Stub): - """LayoutAnimationController — 布局动画控制器""" - - ORDER_NORMAL = 0 - ORDER_REVERSE = 1 - ORDER_RANDOM = 2 - - def __init__(self, animation: Optional[Animation] = None): - super().__init__() - self._animation: Optional[Animation] = animation - self._delay: float = 0.5 - self._order: int = LayoutAnimationController.ORDER_NORMAL - self._initialized: bool = False - self._max_delay: int = 0 - - def setAnimation(self, animation: Animation): - self._animation = animation - - def getAnimation(self) -> Optional[Animation]: - return self._animation - - def setOrder(self, order: int): - self._order = order - - def getOrder(self) -> int: - return self._order - - def setDelay(self, delay: float): - self._delay = delay - - def getDelay(self) -> float: - return self._delay - - def getDelayForView(self, view: Any) -> int: - if self._order == LayoutAnimationController.ORDER_NORMAL: - return 0 - elif self._order == LayoutAnimationController.ORDER_REVERSE: - return 0 - elif self._order == LayoutAnimationController.ORDER_RANDOM: - import random - return int(random.random() * self._max_delay) - return 0 - - def willOverlap(self) -> bool: - return self._delay < 1.0 - - def start(self): - self._initialized = True - - def isDone(self) -> bool: - return True - - def getAnimationForView(self, view: Any) -> Optional[Animation]: - return self._animation - - def setInitialized(self, initialized: bool): - self._initialized = initialized - - -# ═══════════════════════════════════════════════════════════════ -# GridLayoutAnimationController -# ═══════════════════════════════════════════════════════════════ - -class GridLayoutAnimationController(LayoutAnimationController): - """GridLayoutAnimationController — 网格布局动画控制器""" - - DIRECTION_LEFT_TO_RIGHT = 0x0 - DIRECTION_RIGHT_TO_LEFT = 0x1 - DIRECTION_TOP_TO_BOTTOM = 0x2 - DIRECTION_BOTTOM_TO_TOP = 0x3 - DIRECTION_HORIZONTAL_MASK = 0x1 - DIRECTION_VERTICAL_MASK = 0x2 - - def __init__(self, animation: Optional[Animation] = None): - super().__init__(animation) - self._column_delay: float = 0.5 - self._row_delay: float = 0.5 - self._direction: int = GridLayoutAnimationController.DIRECTION_LEFT_TO_RIGHT - self._columns: int = 1 - self._rows: int = 1 - - def setColumnDelay(self, column_delay: float): - self._column_delay = column_delay - - def getColumnDelay(self) -> float: - return self._column_delay - - def setRowDelay(self, row_delay: float): - self._row_delay = row_delay - - def getRowDelay(self) -> float: - return self._row_delay - - def setDirection(self, direction: int): - self._direction = direction - - def getDirection(self) -> int: - return self._direction - - def setColumns(self, columns: int): - self._columns = columns - - def getColumns(self) -> int: - return self._columns - - def setRows(self, rows: int): - self._rows = rows - - def getRows(self) -> int: - return self._rows - - def getDelayForView(self, view: Any) -> int: - duration = 0 - if self._animation: - duration = self._animation.getDuration() - return int(duration * self._column_delay) - - def willOverlap(self) -> bool: - return self._column_delay < 1.0 or self._row_delay < 1.0 - - def getAnimationForView(self, view: Any) -> Optional[Animation]: - return self._animation - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "Animation", - "AnimationSet", - "AlphaAnimation", - "ScaleAnimation", - "TranslateAnimation", - "RotateAnimation", - "AnimationUtils", - "Interpolator", - "AccelerateInterpolator", - "DecelerateInterpolator", - "AccelerateDecelerateInterpolator", - "LinearInterpolator", - "OvershootInterpolator", - "AnticipateInterpolator", - "AnticipateOvershootInterpolator", - "BounceInterpolator", - "CycleInterpolator", - "PathInterpolator", - "Transformation", - "LayoutAnimationController", - "GridLayoutAnimationController", -] \ No newline at end of file diff --git a/android_pyapi/android/view_animator.py b/android_pyapi/android/view_animator.py deleted file mode 100644 index b7a3816..0000000 --- a/android_pyapi/android/view_animator.py +++ /dev/null @@ -1,887 +0,0 @@ -""" -Android View Animator 组件 — LayoutAnimationController, AnimationUtils, -AnimationSet, Interpolator 系列, RotateAnimation, ScaleAnimation, -TranslateAnimation, AlphaAnimation, GridLayoutAnimationController 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import math -import time -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError - -__all__ = [ - "LayoutAnimationController", "AnimationUtils", "AnimationSet", - "AnimationScale", "AnimationInterpolator", "CycleInterpolator", - "AccelerateInterpolator", "DecelerateInterpolator", - "AccelerateDecelerateInterpolator", "AnticipateInterpolator", - "OvershootInterpolator", "AnticipateOvershootInterpolator", - "BounceInterpolator", "LinearInterpolator", "PathInterpolator", - "RotateAnimation", "ScaleAnimation", "TranslateAnimation", - "AlphaAnimation", "AnimationDescription", - "GridLayoutAnimationController", -] - - -# ═══════════════════════════════════════════════════════════════ -# 常量 -# ═══════════════════════════════════════════════════════════════ - -INFINITE = -1 -RESTART = 1 -REVERSE = 2 - -ZORDER_NORMAL = 0 -ZORDER_TOP = 1 -ZORDER_BOTTOM = -1 - -RELATIVE_TO_SELF = 0 -RELATIVE_TO_PARENT = 1 - -FILL_AFTER = 1 -FILL_BEFORE = 2 -FILL_BOTH = 3 - -START_ON_FIRST_FRAME = 4 -ABSOLUTE_TO_SELF = 8 - -ANIMATION_STARTED = 0 -ANIMATION_RUNNING = 1 -ANIMATION_FILLED = 2 -ANIMATION_ENDED = 3 -ANIMATION_CANCELLED = 4 - - -# ═══════════════════════════════════════════════════════════════ -# AnimationInterpolator -# ═══════════════════════════════════════════════════════════════ - -class AnimationInterpolator(Stub): - """AnimationInterpolator — 动画插值器基类""" - - def getInterpolation(self, input: float) -> float: - return input - - def toString(self) -> str: - return f"{self.__class__.__name__}@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -class LinearInterpolator(AnimationInterpolator): - """LinearInterpolator — 线性插值器""" - - def getInterpolation(self, input: float) -> float: - return input - - def toString(self) -> str: - return "LinearInterpolator" - - -class AccelerateInterpolator(AnimationInterpolator): - """AccelerateInterpolator — 加速插值器""" - - def __init__(self, factor: float = 1.0): - super().__init__() - self._factor = factor - self._double_factor = 2.0 * factor - - def getInterpolation(self, input: float) -> float: - if self._factor == 1.0: - return input * input - return pow(input, self._double_factor) - - def toString(self) -> str: - return f"AccelerateInterpolator(factor={self._factor})" - - -class DecelerateInterpolator(AnimationInterpolator): - """DecelerateInterpolator — 减速插值器""" - - def __init__(self, factor: float = 1.0): - super().__init__() - self._factor = factor - - def getInterpolation(self, input: float) -> float: - if self._factor == 1.0: - return 1.0 - (1.0 - input) * (1.0 - input) - return 1.0 - pow(1.0 - input, 2.0 * self._factor) - - def toString(self) -> str: - return f"DecelerateInterpolator(factor={self._factor})" - - -class AccelerateDecelerateInterpolator(AnimationInterpolator): - """AccelerateDecelerateInterpolator — 加速减速插值器""" - - def getInterpolation(self, input: float) -> float: - return float(math.cos((input + 1.0) * math.pi) / 2.0 + 0.5) - - def toString(self) -> str: - return "AccelerateDecelerateInterpolator" - - -class AnticipateInterpolator(AnimationInterpolator): - """AnticipateInterpolator — 预期插值器""" - - def __init__(self, tension: float = 2.0): - super().__init__() - self._tension = tension - - def getInterpolation(self, input: float) -> float: - return input * input * ((self._tension + 1.0) * input - self._tension) - - def toString(self) -> str: - return f"AnticipateInterpolator(tension={self._tension})" - - -class OvershootInterpolator(AnimationInterpolator): - """OvershootInterpolator — 超调插值器""" - - def __init__(self, tension: float = 2.0): - super().__init__() - self._tension = tension - - def getInterpolation(self, input: float) -> float: - x = input - 1.0 - return x * x * ((self._tension + 1.0) * x + self._tension) + 1.0 - - def toString(self) -> str: - return f"OvershootInterpolator(tension={self._tension})" - - -class AnticipateOvershootInterpolator(AnimationInterpolator): - """AnticipateOvershootInterpolator — 预期超调插值器""" - - def __init__(self, tension: float = 2.0, extraTension: float = 1.5): - super().__init__() - self._tension = tension - self._extra_tension = extraTension - - def getInterpolation(self, input: float) -> float: - if input < 0.5: - x = 2.0 * input - return 0.5 * x * x * ((self._tension + 1.0) * x - self._tension) - else: - x = 2.0 * input - 2.0 - t = self._tension * self._extra_tension - return 0.5 * (x * x * ((t + 1.0) * x + t) + 2.0) - - def toString(self) -> str: - return f"AnticipateOvershootInterpolator(tension={self._tension})" - - -class BounceInterpolator(AnimationInterpolator): - """BounceInterpolator — 弹跳插值器""" - - def getInterpolation(self, input: float) -> float: - x = input - for i in range(4): - duration = pow(2.0, -i) * 0.6 - if x < duration: - scale = 0.5 / duration - return scale * x * (duration - x) * 8.0 + (1.0 - 1.0 / pow(2.0, i)) - x -= duration - return 0.0 - - def toString(self) -> str: - return "BounceInterpolator" - - -class CycleInterpolator(AnimationInterpolator): - """CycleInterpolator — 周期插值器""" - - def __init__(self, cycles: float = 1.0): - super().__init__() - self._cycles = cycles - - def getInterpolation(self, input: float) -> float: - return float(math.sin(2.0 * math.pi * self._cycles * input)) - - def toString(self) -> str: - return f"CycleInterpolator(cycles={self._cycles})" - - -class PathInterpolator(AnimationInterpolator): - """PathInterpolator — 路径插值器""" - - def __init__(self, controlX1: float = 0.0, controlY1: float = 0.0, - controlX2: float = 1.0, controlY2: float = 1.0): - super().__init__() - self._cx1 = controlX1 - self._cy1 = controlY1 - self._cx2 = controlX2 - self._cy2 = controlY2 - - def getInterpolation(self, input: float) -> float: - t = input - return (1.0 - t) * (1.0 - t) * (1.0 - t) * 0.0 + \ - 3.0 * (1.0 - t) * (1.0 - t) * t * self._cy1 + \ - 3.0 * (1.0 - t) * t * t * self._cy2 + \ - t * t * t * 1.0 - - def toString(self) -> str: - return f"PathInterpolator({self._cx1}, {self._cy1}, {self._cx2}, {self._cy2})" - - -# ═══════════════════════════════════════════════════════════════ -# AnimationScale -# ═══════════════════════════════════════════════════════════════ - -class AnimationScale(Stub): - """AnimationScale — 动画缩放比例""" - - def __init__(self, scale: float = 1.0): - super().__init__() - self._scale = scale - - def getScale(self) -> float: - return self._scale - - def setScale(self, scale: float): - self._scale = max(0.0, scale) - - def toString(self) -> str: - return f"AnimationScale(scale={self._scale})" - - -# ═══════════════════════════════════════════════════════════════ -# AnimationSet -# ═══════════════════════════════════════════════════════════════ - -class AnimationSet(Stub): - """AnimationSet — 动画集合""" - - def __init__(self, shareInterpolator: bool = True): - super().__init__() - self._share_interpolator = shareInterpolator - self._animations: List[Any] = [] - self._interpolator: Optional[AnimationInterpolator] = None - self._duration: int = 0 - self._fill_after = False - self._fill_before = False - self._repeat_count = 0 - self._repeat_mode = RESTART - self._start_offset = 0 - self._start_time = -1 - - def addAnimation(self, animation: Any): - self._animations.append(animation) - - def getAnimations(self) -> List[Any]: - return list(self._animations) - - def setDuration(self, duration: int): - self._duration = duration - for anim in self._animations: - if hasattr(anim, 'setDuration'): - anim.setDuration(duration) - - def getDuration(self) -> int: - return self._duration - - def setInterpolator(self, interpolator: AnimationInterpolator): - self._interpolator = interpolator - if self._share_interpolator: - for anim in self._animations: - if hasattr(anim, 'setInterpolator'): - anim.setInterpolator(interpolator) - - def getInterpolator(self) -> Optional[AnimationInterpolator]: - return self._interpolator - - def setFillAfter(self, fill: bool): - self._fill_after = fill - - def isFillAfter(self) -> bool: - return self._fill_after - - def setFillBefore(self, fill: bool): - self._fill_before = fill - - def isFillBefore(self) -> bool: - return self._fill_before - - def setRepeatCount(self, count: int): - self._repeat_count = count - - def getRepeatCount(self) -> int: - return self._repeat_count - - def setRepeatMode(self, mode: int): - self._repeat_mode = mode - - def getRepeatMode(self) -> int: - return self._repeat_mode - - def setStartOffset(self, offset: int): - self._start_offset = offset - - def getStartOffset(self) -> int: - return self._start_offset - - def start(self): - self._start_time = int(time.time() * 1000) - - def cancel(self): - self._start_time = -1 - - def isRunning(self) -> bool: - return self._start_time >= 0 - - def hasStarted(self) -> bool: - return self._start_time >= 0 - - def toString(self) -> str: - return f"AnimationSet(animations={len(self._animations)}, duration={self._duration})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# AnimationDescription -# ═══════════════════════════════════════════════════════════════ - -class AnimationDescription(Stub): - """AnimationDescription — 动画描述""" - - def __init__(self, name: str = "", duration: int = 300, - interpolator: Optional[AnimationInterpolator] = None, - fillAfter: bool = False, repeatCount: int = 0): - super().__init__() - self._name = name - self._duration = duration - self._interpolator = interpolator or LinearInterpolator() - self._fill_after = fillAfter - self._repeat_count = repeatCount - - def getName(self) -> str: - return self._name - - def getDuration(self) -> int: - return self._duration - - def getInterpolator(self) -> AnimationInterpolator: - return self._interpolator - - def isFillAfter(self) -> bool: - return self._fill_after - - def getRepeatCount(self) -> int: - return self._repeat_count - - def toString(self) -> str: - return f"AnimationDescription(name={self._name}, duration={self._duration})" - - -# ═══════════════════════════════════════════════════════════════ -# RotateAnimation -# ═══════════════════════════════════════════════════════════════ - -class RotateAnimation(Stub): - """RotateAnimation — 旋转动画""" - - def __init__(self, fromDegrees: float = 0.0, toDegrees: float = 360.0, - pivotX: float = 0.0, pivotY: float = 0.0, - pivotXType: int = RELATIVE_TO_SELF, - pivotYType: int = RELATIVE_TO_SELF): - super().__init__() - self._from_degrees = fromDegrees - self._to_degrees = toDegrees - self._pivot_x = pivotX - self._pivot_y = pivotY - self._pivot_x_type = pivotXType - self._pivot_y_type = pivotYType - self._duration = 300 - self._interpolator: Optional[AnimationInterpolator] = None - self._repeat_count = 0 - self._repeat_mode = RESTART - self._fill_after = False - self._fill_before = False - self._start_offset = 0 - self._start_time = -1 - - def getFromDegrees(self) -> float: - return self._from_degrees - - def getToDegrees(self) -> float: - return self._to_degrees - - def getPivotX(self) -> float: - return self._pivot_x - - def getPivotY(self) -> float: - return self._pivot_y - - def getPivotXType(self) -> int: - return self._pivot_x_type - - def getPivotYType(self) -> int: - return self._pivot_y_type - - def setDuration(self, duration: int): - self._duration = duration - - def getDuration(self) -> int: - return self._duration - - def setInterpolator(self, interpolator: AnimationInterpolator): - self._interpolator = interpolator - - def getInterpolator(self) -> Optional[AnimationInterpolator]: - return self._interpolator - - def setRepeatCount(self, count: int): - self._repeat_count = count - - def getRepeatCount(self) -> int: - return self._repeat_count - - def setRepeatMode(self, mode: int): - self._repeat_mode = mode - - def getRepeatMode(self) -> int: - return self._repeat_mode - - def setFillAfter(self, fill: bool): - self._fill_after = fill - - def isFillAfter(self) -> bool: - return self._fill_after - - def setFillBefore(self, fill: bool): - self._fill_before = fill - - def isFillBefore(self) -> bool: - return self._fill_before - - def setStartOffset(self, offset: int): - self._start_offset = offset - - def start(self): - self._start_time = int(time.time() * 1000) - - def cancel(self): - self._start_time = -1 - - def toString(self) -> str: - return (f"RotateAnimation(from={self._from_degrees}, to={self._to_degrees}, " - f"duration={self._duration})") - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# ScaleAnimation -# ═══════════════════════════════════════════════════════════════ - -class ScaleAnimation(Stub): - """ScaleAnimation — 缩放动画""" - - def __init__(self, fromX: float = 0.0, toX: float = 1.0, - fromY: float = 0.0, toY: float = 1.0, - pivotX: float = 0.0, pivotY: float = 0.0, - pivotXType: int = RELATIVE_TO_SELF, - pivotYType: int = RELATIVE_TO_SELF): - super().__init__() - self._from_x = fromX - self._to_x = toX - self._from_y = fromY - self._to_y = toY - self._pivot_x = pivotX - self._pivot_y = pivotY - self._pivot_x_type = pivotXType - self._pivot_y_type = pivotYType - self._duration = 300 - self._interpolator: Optional[AnimationInterpolator] = None - self._repeat_count = 0 - self._fill_after = False - self._start_time = -1 - - def getFromX(self) -> float: - return self._from_x - - def getToX(self) -> float: - return self._to_x - - def getFromY(self) -> float: - return self._from_y - - def getToY(self) -> float: - return self._to_y - - def getPivotX(self) -> float: - return self._pivot_x - - def getPivotY(self) -> float: - return self._pivot_y - - def setDuration(self, duration: int): - self._duration = duration - - def getDuration(self) -> int: - return self._duration - - def setInterpolator(self, interpolator: AnimationInterpolator): - self._interpolator = interpolator - - def getInterpolator(self) -> Optional[AnimationInterpolator]: - return self._interpolator - - def setRepeatCount(self, count: int): - self._repeat_count = count - - def setFillAfter(self, fill: bool): - self._fill_after = fill - - def start(self): - self._start_time = int(time.time() * 1000) - - def cancel(self): - self._start_time = -1 - - def toString(self) -> str: - return (f"ScaleAnimation(x={self._from_x}->{self._to_x}, " - f"y={self._from_y}->{self._to_y})") - - -# ═══════════════════════════════════════════════════════════════ -# TranslateAnimation (扩展) -# ═══════════════════════════════════════════════════════════════ - -class TranslateAnimation(Stub): - """TranslateAnimation — 位移动画 (完整版)""" - - def __init__(self, fromX: float = 0.0, toX: float = 0.0, - fromY: float = 0.0, toY: float = 0.0, - fromXType: int = RELATIVE_TO_SELF, - toXType: int = RELATIVE_TO_SELF, - fromYType: int = RELATIVE_TO_SELF, - toYType: int = RELATIVE_TO_SELF): - super().__init__() - self._from_x = fromX - self._to_x = toX - self._from_y = fromY - self._to_y = toY - self._from_x_type = fromXType - self._to_x_type = toXType - self._from_y_type = fromYType - self._to_y_type = toYType - self._duration = 300 - self._interpolator: Optional[AnimationInterpolator] = None - self._repeat_count = 0 - self._fill_after = False - self._start_time = -1 - - def getFromX(self) -> float: - return self._from_x - - def getToX(self) -> float: - return self._to_x - - def getFromY(self) -> float: - return self._from_y - - def getToY(self) -> float: - return self._to_y - - def setDuration(self, duration: int): - self._duration = duration - - def getDuration(self) -> int: - return self._duration - - def setInterpolator(self, interpolator: AnimationInterpolator): - self._interpolator = interpolator - - def setRepeatCount(self, count: int): - self._repeat_count = count - - def setFillAfter(self, fill: bool): - self._fill_after = fill - - def start(self): - self._start_time = int(time.time() * 1000) - - def cancel(self): - self._start_time = -1 - - def hasStarted(self) -> bool: - return self._start_time >= 0 - - def toString(self) -> str: - return (f"TranslateAnimation(x={self._from_x}->{self._to_x}, " - f"y={self._from_y}->{self._to_y})") - - -# ═══════════════════════════════════════════════════════════════ -# AlphaAnimation (扩展) -# ═══════════════════════════════════════════════════════════════ - -class AlphaAnimation(Stub): - """AlphaAnimation — 透明度动画 (完整版)""" - - def __init__(self, fromAlpha: float = 0.0, toAlpha: float = 1.0): - super().__init__() - self._from_alpha = fromAlpha - self._to_alpha = toAlpha - self._duration = 300 - self._interpolator: Optional[AnimationInterpolator] = None - self._repeat_count = 0 - self._fill_after = False - self._start_time = -1 - - def getFromAlpha(self) -> float: - return self._from_alpha - - def getToAlpha(self) -> float: - return self._to_alpha - - def setDuration(self, duration: int): - self._duration = duration - - def getDuration(self) -> int: - return self._duration - - def setInterpolator(self, interpolator: AnimationInterpolator): - self._interpolator = interpolator - - def getInterpolator(self) -> Optional[AnimationInterpolator]: - return self._interpolator - - def setRepeatCount(self, count: int): - self._repeat_count = count - - def setFillAfter(self, fill: bool): - self._fill_after = fill - - def start(self): - self._start_time = int(time.time() * 1000) - - def cancel(self): - self._start_time = -1 - - def hasStarted(self) -> bool: - return self._start_time >= 0 - - def toString(self) -> str: - return f"AlphaAnimation(from={self._from_alpha}, to={self._to_alpha})" - - -# ═══════════════════════════════════════════════════════════════ -# LayoutAnimationController -# ═══════════════════════════════════════════════════════════════ - -class LayoutAnimationController(Stub): - """LayoutAnimationController — 布局动画控制器""" - - ORDER_NORMAL = 0 - ORDER_REVERSE = 1 - ORDER_RANDOM = 2 - - def __init__(self, animation: Optional[AnimationDescription] = None, - delay: float = 0.5, order: int = ORDER_NORMAL): - super().__init__() - self._animation = animation - self._delay = delay - self._order = order - self._interpolator: Optional[AnimationInterpolator] = None - - def getAnimation(self) -> Optional[AnimationDescription]: - return self._animation - - def setAnimation(self, animation: AnimationDescription): - self._animation = animation - - def getDelay(self) -> float: - return self._delay - - def setDelay(self, delay: float): - self._delay = delay - - def getOrder(self) -> int: - return self._order - - def setOrder(self, order: int): - self._order = order - - def setInterpolator(self, interpolator: AnimationInterpolator): - self._interpolator = interpolator - - def getInterpolator(self) -> Optional[AnimationInterpolator]: - return self._interpolator - - def isEnabled(self) -> bool: - return self._animation is not None - - def toString(self) -> str: - return f"LayoutAnimationController(delay={self._delay})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# GridLayoutAnimationController -# ═══════════════════════════════════════════════════════════════ - -class GridLayoutAnimationController(LayoutAnimationController): - """GridLayoutAnimationController — 网格布局动画控制器""" - - DIRECTION_LEFT_TO_RIGHT = 0 - DIRECTION_RIGHT_TO_LEFT = 1 - DIRECTION_TOP_TO_BOTTOM = 2 - DIRECTION_BOTTOM_TO_TOP = 3 - - DIRECTION_HORIZONTAL_MASK = 1 - DIRECTION_VERTICAL_MASK = 2 - - PRIORITY_NONE = 0 - PRIORITY_COLUMN = 1 - PRIORITY_ROW = 2 - - def __init__(self, animation: Optional[AnimationDescription] = None, - columnDelay: float = 0.5, rowDelay: float = 0.5, - direction: int = DIRECTION_LEFT_TO_RIGHT, - directionPriority: int = PRIORITY_NONE): - super().__init__(animation=animation, delay=0.5) - self._column_delay = columnDelay - self._row_delay = rowDelay - self._direction = direction - self._direction_priority = directionPriority - - def getColumnDelay(self) -> float: - return self._column_delay - - def setColumnDelay(self, delay: float): - self._column_delay = delay - - def getRowDelay(self) -> float: - return self._row_delay - - def setRowDelay(self, delay: float): - self._row_delay = delay - - def getDirection(self) -> int: - return self._direction - - def setDirection(self, direction: int): - self._direction = direction - - def getDirectionPriority(self) -> int: - return self._direction_priority - - def setDirectionPriority(self, priority: int): - self._direction_priority = priority - - def toString(self) -> str: - return f"GridLayoutAnimationController(colDelay={self._column_delay})" - - -# ═══════════════════════════════════════════════════════════════ -# AnimationUtils -# ═══════════════════════════════════════════════════════════════ - -class AnimationUtils(Stub): - """AnimationUtils — 动画工具类""" - - @staticmethod - def currentAnimationTimeMillis() -> int: - return int(time.time() * 1000) - - @staticmethod - def loadAnimation(context: Any, id: int) -> Optional[AnimationDescription]: - return None - - @staticmethod - def loadInterpolator(context: Any, id: int) -> Optional[AnimationInterpolator]: - return None - - @staticmethod - def makeInAnimation(context: Any, fromLeft: bool = True) -> TranslateAnimation: - x = -1.0 if fromLeft else 1.0 - anim = TranslateAnimation(fromX=x, toX=0.0, fromXType=RELATIVE_TO_PARENT, - toXType=RELATIVE_TO_PARENT) - anim.setDuration(300) - anim.setInterpolator(DecelerateInterpolator()) - return anim - - @staticmethod - def makeOutAnimation(context: Any, toRight: bool = True) -> TranslateAnimation: - x = 1.0 if toRight else -1.0 - anim = TranslateAnimation(fromX=0.0, toX=x, fromXType=RELATIVE_TO_PARENT, - toXType=RELATIVE_TO_PARENT) - anim.setDuration(300) - anim.setInterpolator(AccelerateInterpolator()) - return anim - - @staticmethod - def makeInChildBottomAnimation(context: Any) -> TranslateAnimation: - anim = TranslateAnimation(fromX=0.0, toX=0.0, - fromY=1.0, toY=0.0, - fromYType=RELATIVE_TO_PARENT, - toYType=RELATIVE_TO_PARENT) - anim.setDuration(300) - anim.setInterpolator(DecelerateInterpolator()) - return anim - - @staticmethod - def createScaleUpAnimation(context: Any) -> ScaleAnimation: - anim = ScaleAnimation(fromX=0.0, toX=1.0, fromY=0.0, toY=1.0, - pivotX=0.5, pivotY=0.5, - pivotXType=RELATIVE_TO_SELF, - pivotYType=RELATIVE_TO_SELF) - anim.setDuration(300) - anim.setInterpolator(AccelerateDecelerateInterpolator()) - return anim - - @staticmethod - def createShrinkDownAnimation(context: Any) -> ScaleAnimation: - anim = ScaleAnimation(fromX=1.0, toX=0.0, fromY=1.0, toY=0.0, - pivotX=0.5, pivotY=0.5, - pivotXType=RELATIVE_TO_SELF, - pivotYType=RELATIVE_TO_SELF) - anim.setDuration(300) - anim.setInterpolator(AccelerateDecelerateInterpolator()) - return anim - - @staticmethod - def createFadeInAnimation(context: Any) -> AlphaAnimation: - anim = AlphaAnimation(fromAlpha=0.0, toAlpha=1.0) - anim.setDuration(300) - return anim - - @staticmethod - def createFadeOutAnimation(context: Any) -> AlphaAnimation: - anim = AlphaAnimation(fromAlpha=1.0, toAlpha=0.0) - anim.setDuration(300) - return anim - - @staticmethod - def createRotateAnimation(context: Any, fromDeg: float = 0.0, - toDeg: float = 360.0) -> RotateAnimation: - anim = RotateAnimation(fromDegrees=fromDeg, toDegrees=toDeg, - pivotX=0.5, pivotY=0.5) - anim.setDuration(500) - return anim - - def toString(self) -> str: - return "AnimationUtils" \ No newline at end of file diff --git a/android_pyapi/android/view_autofill.py b/android_pyapi/android/view_autofill.py deleted file mode 100644 index b721ea9..0000000 --- a/android_pyapi/android/view_autofill.py +++ /dev/null @@ -1,313 +0,0 @@ -""" -Android view.autofill 组件 — AutofillManager, AutofillValue, AutofillId 等 -方法签名完全对齐 Android SDK -""" - -from typing import Any, Callable, Dict, List, Optional - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# AutofillManager -# ═══════════════════════════════════════════════════════════════ - -class AutofillManager(Stub): - """AutofillManager — 自动填充管理器""" - - # ═══════════════════════════════════════════════════════════ - # AutofillCallback - # ═══════════════════════════════════════════════════════════ - - class AutofillCallback(Stub): - """AutofillManager.AutofillCallback — 自动填充回调""" - - def onAutofillEvent(self, view: Any, event: int): - pass - - def onAutofillUnavailableEvent(self, event: int): - pass - - def onAuthenticationShowing(self): - pass - - def __init__(self): - super().__init__() - self._enabled = False - self._callbacks: List[AutofillManager.AutofillCallback] = [] - - def isEnabled(self) -> bool: - return self._enabled - - def hasEnabledAutofillServices(self) -> bool: - return False - - def isAutofillSupported(self) -> bool: - return False - - def disableAutofillServices(self): - self._enabled = False - - def notifyValueChanged(self, view: Any): - pass - - def commit(self): - pass - - def cancel(self): - pass - - def requestAutofill(self, view: Any): - pass - - def getAutofillServiceComponentName(self) -> Any: - return None - - def getAutofillServiceLabel(self) -> str: - return "" - - def getAvailableFieldClassificationAlgorithms(self) -> List[str]: - return [] - - def getDefaultFieldClassificationAlgorithm(self) -> Optional[str]: - return None - - def getFillEventHistory(self) -> Optional["FillEventHistory"]: - return None - - def onAuthenticationResult(self, authenticationResult: Any): - pass - - def onAuthenticationFailed(self): - pass - - -# ═══════════════════════════════════════════════════════════════ -# AutofillValue -# ═══════════════════════════════════════════════════════════════ - -class AutofillValue(Stub): - """AutofillValue — 自动填充值""" - - def __init__(self): - super().__init__() - self._text_value: Optional[str] = None - self._date_value: Optional[int] = None - self._toggle_value: Optional[bool] = None - self._list_value: Optional[int] = None - self._is_text = False - self._is_date = False - self._is_toggle = False - self._is_list = False - - @staticmethod - def forText(value: str) -> "AutofillValue": - v = AutofillValue() - v._text_value = value - v._is_text = True - return v - - @staticmethod - def forDate(value: int) -> "AutofillValue": - v = AutofillValue() - v._date_value = value - v._is_date = True - return v - - @staticmethod - def forToggle(value: bool) -> "AutofillValue": - v = AutofillValue() - v._toggle_value = value - v._is_toggle = True - return v - - @staticmethod - def forList(value: int) -> "AutofillValue": - v = AutofillValue() - v._list_value = value - v._is_list = True - return v - - def getTextValue(self) -> Optional[str]: - return self._text_value - - def getDateValue(self) -> Optional[int]: - return self._date_value - - def getToggleValue(self) -> Optional[bool]: - return self._toggle_value - - def getListValue(self) -> Optional[int]: - return self._list_value - - def isText(self) -> bool: - return self._is_text - - def isDate(self) -> bool: - return self._is_date - - def isToggle(self) -> bool: - return self._is_toggle - - def isList(self) -> bool: - return self._is_list - - def equals(self, other: Any) -> bool: - if not isinstance(other, AutofillValue): - return False - return (self._text_value == other._text_value - and self._date_value == other._date_value - and self._toggle_value == other._toggle_value - and self._list_value == other._list_value) - - def hashCode(self) -> int: - return hash((self._text_value, self._date_value, - self._toggle_value, self._list_value)) & 0x7FFFFFFF - - def toString(self) -> str: - if self._is_text: - return f"AutofillValue(text={self._text_value})" - if self._is_date: - return f"AutofillValue(date={self._date_value})" - if self._is_toggle: - return f"AutofillValue(toggle={self._toggle_value})" - if self._is_list: - return f"AutofillValue(list={self._list_value})" - return "AutofillValue(empty)" - - -# ═══════════════════════════════════════════════════════════════ -# AutofillId -# ═══════════════════════════════════════════════════════════════ - -class AutofillId(Stub): - """AutofillId — 自动填充 ID""" - - FLAG_AUTOFILL_SESSION_SUPPORTS_INDEXING = 1 - FLAG_AUTOFILL_SESSION_SUPPORTS_AUGMENTED_AUTOFILL = 2 - - def __init__(self, viewId: int = 0, sessionId: int = 0, - virtualChildId: int = -1, virtualIntId: int = -1): - super().__init__() - self._view_id = viewId - self._session_id = sessionId - self._virtual_child_id = virtualChildId - self._virtual_int_id = virtualIntId - - def getViewId(self) -> int: - return self._view_id - - def getSessionId(self) -> int: - return self._session_id - - def getVirtualChildId(self) -> int: - return self._virtual_child_id - - def getVirtualIntId(self) -> int: - return self._virtual_int_id - - def equals(self, other: Any) -> bool: - if not isinstance(other, AutofillId): - return False - return (self._view_id == other._view_id - and self._session_id == other._session_id - and self._virtual_child_id == other._virtual_child_id) - - def hashCode(self) -> int: - return hash((self._view_id, self._session_id, - self._virtual_child_id)) & 0x7FFFFFFF - - def toString(self) -> str: - return f"AutofillId(viewId={self._view_id}, sessionId={self._session_id})" - - -# ═══════════════════════════════════════════════════════════════ -# FillEventHistory -# ═══════════════════════════════════════════════════════════════ - -class FillEventHistory(Stub): - """FillEventHistory — 填充事件历史""" - - def __init__(self): - super().__init__() - self._events: List[Any] = [] - self._client_state: Any = None - - def getEvents(self) -> List[Any]: - return self._events - - def getClientState(self) -> Any: - return self._client_state - - -# ═══════════════════════════════════════════════════════════════ -# FillContext -# ═══════════════════════════════════════════════════════════════ - -class FillContext(Stub): - """FillContext — 填充上下文""" - - def __init__(self, requestId: int = 0, autofillId: Optional[AutofillId] = None, - focusedId: Optional[AutofillId] = None, - focusedVirtualId: int = -1, extras: Any = None, - autofillValue: Optional[AutofillValue] = None, - visibility: Optional[int] = None, - options: Optional[List[Any]] = None): - super().__init__() - self._request_id = requestId - self._autofill_id = autofillId - self._focused_id = focusedId - self._focused_virtual_id = focusedVirtualId - self._extras = extras - self._autofill_value = autofillValue - self._visibility = visibility - self._options = options - - def getRequestId(self) -> int: - return self._request_id - - def getAutofillId(self) -> Optional[AutofillId]: - return self._autofill_id - - def getFocusedId(self) -> Optional[AutofillId]: - return self._focused_id - - def getFocusedVirtualId(self) -> int: - return self._focused_virtual_id - - def getExtras(self) -> Any: - return self._extras - - def getAutofillValue(self) -> Optional[AutofillValue]: - return self._autofill_value - - def getVisibility(self) -> Optional[int]: - return self._visibility - - def getOptions(self) -> Optional[List[Any]]: - return self._options - - -# ═══════════════════════════════════════════════════════════════ -# AugmentedAutofillManager (stub) -# ═══════════════════════════════════════════════════════════════ - -class AugmentedAutofillManager(Stub): - """AugmentedAutofillManager — 增强自动填充管理器""" - - def __init__(self): - super().__init__() - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "AutofillManager", - "AutofillValue", - "AutofillId", - "FillEventHistory", - "FillContext", - "AugmentedAutofillManager", -] \ No newline at end of file diff --git a/android_pyapi/android/view_dragndrop.py b/android_pyapi/android/view_dragndrop.py deleted file mode 100644 index 8a20ef6..0000000 --- a/android_pyapi/android/view_dragndrop.py +++ /dev/null @@ -1,248 +0,0 @@ -""" -Android DragAndDrop 组件 — DragAndDropPermissions, ClipData, ClipDescription -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -from typing import Any, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# MIME 类型常量 -# ═══════════════════════════════════════════════════════════════ - -MIMETYPE_TEXT_PLAIN = "text/plain" -MIMETYPE_TEXT_HTML = "text/html" -MIMETYPE_TEXT_URILIST = "text/uri-list" -MIMETYPE_TEXT_INTENT = "text/vnd.android.intent" -MIMETYPE_IMAGE_PNG = "image/png" -MIMETYPE_IMAGE_JPEG = "image/jpeg" -MIMETYPE_IMAGE_GIF = "image/gif" -MIMETYPE_IMAGE_WEBP = "image/webp" -MIMETYPE_IMAGE_BMP = "image/bmp" -MIMETYPE_VIDEO_MP4 = "video/mp4" -MIMETYPE_VIDEO_3GP = "video/3gpp" -MIMETYPE_AUDIO_MP3 = "audio/mpeg" -MIMETYPE_AUDIO_AAC = "audio/mp4a-latm" -MIMETYPE_APPLICATION_PDF = "application/pdf" -MIMETYPE_APPLICATION_ZIP = "application/zip" -MIMETYPE_APPLICATION_JSON = "application/json" -MIMETYPE_APPLICATION_XML = "application/xml" - - -# ═══════════════════════════════════════════════════════════════ -# ClipDescription -# ═══════════════════════════════════════════════════════════════ - -class ClipDescription(Stub): - """ClipDescription — 剪贴板数据描述""" - - MIMETYPE_TEXT_PLAIN = MIMETYPE_TEXT_PLAIN - MIMETYPE_TEXT_HTML = MIMETYPE_TEXT_HTML - MIMETYPE_TEXT_URILIST = MIMETYPE_TEXT_URILIST - MIMETYPE_TEXT_INTENT = MIMETYPE_TEXT_INTENT - - def __init__(self, label: str, mime_types: List[str]): - super().__init__() - self._label = label - self._mime_types: List[str] = list(mime_types) - self._extras: Optional[Any] = None - self._timestamp: int = 0 - - @staticmethod - def compareMimeTypes(mime_type1: str, mime_type2: str) -> bool: - """比较两个 MIME 类型是否匹配""" - if mime_type1 == mime_type2: - return True - if mime_type1 == "*/*" or mime_type2 == "*/*": - return True - t1 = mime_type1.split("/")[0] if "/" in mime_type1 else "" - t2 = mime_type2.split("/")[0] if "/" in mime_type2 else "" - if t1 and t1 == t2: - if mime_type1.endswith("/*") or mime_type2.endswith("/*"): - return True - return False - - def getLabel(self) -> str: - return self._label - - def getMimeType(self, index: int) -> str: - if 0 <= index < len(self._mime_types): - return self._mime_types[index] - return "" - - def getMimeTypeCount(self) -> int: - return len(self._mime_types) - - def hasMimeType(self, mime_type: str) -> bool: - for mt in self._mime_types: - if ClipDescription.compareMimeTypes(mt, mime_type): - return True - return False - - def filterMimeTypes(self, mime_type: str) -> List[str]: - result = [] - for mt in self._mime_types: - if ClipDescription.compareMimeTypes(mt, mime_type): - result.append(mt) - return result - - def setExtras(self, extras: Any) -> None: - self._extras = extras - - def getExtras(self) -> Optional[Any]: - return self._extras - - def getTimestamp(self) -> int: - return self._timestamp - - def setTimestamp(self, ts: int) -> None: - self._timestamp = ts - - def toString(self) -> str: - return (f"ClipDescription(label={self._label}, " - f"mimeTypes={self._mime_types})") - - -# ═══════════════════════════════════════════════════════════════ -# ClipData.Item -# ═══════════════════════════════════════════════════════════════ - -class ClipItem(Stub): - """ClipData.Item — 剪贴板数据项""" - - def __init__(self, text: Optional[str] = None, - intent: Optional[Any] = None, - uri: Optional[str] = None, - html_text: Optional[str] = None): - super().__init__() - self._text = text - self._intent = intent - self._uri = uri - self._html_text = html_text - - def getText(self) -> Optional[str]: - return self._text - - def getIntent(self) -> Optional[Any]: - return self._intent - - def getUri(self) -> Optional[str]: - return self._uri - - def getHtmlText(self) -> Optional[str]: - return self._html_text - - def coerceToText(self, context: Any) -> str: - if self._text: - return self._text - if self._uri: - return str(self._uri) - if self._intent: - return str(self._intent) - return "" - - def coerceToHtmlText(self, context: Any) -> Optional[str]: - return self._html_text - - def coerceToStyledText(self, context: Any) -> Optional[str]: - return self.coerceToText(context) - - def toString(self) -> str: - return (f"ClipData.Item(text={self._text}, " - f"uri={self._uri})") - - -# ═══════════════════════════════════════════════════════════════ -# ClipData -# ═══════════════════════════════════════════════════════════════ - -class ClipData(Stub): - """ClipData — 剪贴板数据""" - - Item = ClipItem - - def __init__(self, description: ClipDescription, item: ClipItem): - super().__init__() - self._description = description - self._items: List[ClipItem] = [item] - - @staticmethod - def newPlainText(label: str, text: str) -> "ClipData": - desc = ClipDescription(label, [MIMETYPE_TEXT_PLAIN]) - item = ClipItem(text=text) - return ClipData(desc, item) - - @staticmethod - def newHtmlText(label: str, text: str, html_text: str) -> "ClipData": - desc = ClipDescription(label, [MIMETYPE_TEXT_HTML, MIMETYPE_TEXT_PLAIN]) - item = ClipItem(text=text, html_text=html_text) - return ClipData(desc, item) - - @staticmethod - def newIntent(label: str, intent: Any) -> "ClipData": - desc = ClipDescription(label, [MIMETYPE_TEXT_INTENT]) - item = ClipItem(intent=intent) - return ClipData(desc, item) - - @staticmethod - def newUri(cr: Any, label: str, uri: str) -> "ClipData": - mime_type = MIMETYPE_TEXT_URILIST - desc = ClipDescription(label, [mime_type]) - item = ClipItem(uri=uri) - return ClipData(desc, item) - - @staticmethod - def newRawUri(label: str, uri: str) -> "ClipData": - desc = ClipDescription(label, [MIMETYPE_TEXT_URILIST]) - item = ClipItem(uri=uri) - return ClipData(desc, item) - - def getDescription(self) -> ClipDescription: - return self._description - - def addItem(self, item: ClipItem) -> None: - self._items.append(item) - - def getItemAt(self, index: int) -> Optional[ClipItem]: - if 0 <= index < len(self._items): - return self._items[index] - return None - - def getItemCount(self) -> int: - return len(self._items) - - def toString(self) -> str: - return (f"ClipData(desc={self._description}, " - f"items={len(self._items)})") - - -# ═══════════════════════════════════════════════════════════════ -# DragAndDropPermissions -# ═══════════════════════════════════════════════════════════════ - -class DragAndDropPermissions(Stub): - """DragAndDropPermissions — 拖放权限""" - - def __init__(self): - super().__init__() - self._granted_uris: List[str] = [] - self._released = False - - def release(self) -> None: - self._released = True - self._granted_uris.clear() - - def isReleased(self) -> bool: - return self._released - - def getGrantedUris(self) -> List[str]: - return list(self._granted_uris) - - def toString(self) -> str: - return (f"DragAndDropPermissions(uris={len(self._granted_uris)}, " - f"released={self._released})") \ No newline at end of file diff --git a/android_pyapi/android/view_ext.py b/android_pyapi/android/view_ext.py deleted file mode 100644 index c942ad1..0000000 --- a/android_pyapi/android/view_ext.py +++ /dev/null @@ -1,2520 +0,0 @@ -""" -Android View 扩展组件 — ViewOutlineProvider, ViewPropertyAnimator, ViewStructure 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import math -import time -import weakref -import threading -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException -from .view import KeyEvent as _BaseKeyEvent -from .view import ViewGroup as _BaseViewGroup -from .view import View as _BaseView -from .view import ViewOutlineProvider as _BaseViewOutlineProvider -from .view import ViewPropertyAnimator as _BaseViewPropertyAnimator -from .view import ViewTreeObserver as _BaseViewTreeObserver -from .view import ViewConfiguration as _BaseViewConfiguration -from .view import ViewParent as _BaseViewParent -from .view import WindowInsets as _BaseWindowInsets -from .view import Display as _BaseDisplay -from .view import DragEvent as _BaseDragEvent -from .view import GestureDetector as _BaseGestureDetector -from .view import ScaleGestureDetector as _BaseScaleGestureDetector -from .view import SoundEffectConstants as _BaseSoundEffectConstants -from .view import Surface as _BaseSurface -from .view import SurfaceView as _BaseSurfaceView -from .view import TextureView as _BaseTextureView -from .view import VelocityTracker as _BaseVelocityTracker -from .view import WindowInsetsController as _BaseWindowInsetsController - - -# ═══════════════════════════════════════════════════════════════ -# ViewOutlineProvider -# ═══════════════════════════════════════════════════════════════ - -class ViewOutlineProvider(_BaseViewOutlineProvider): - """ViewOutlineProvider — 视图轮廓提供者 (继承 view.ViewOutlineProvider 的所有方法)""" - - BACKGROUND = None - BOUNDS = None - PADDED_BOUNDS = None - - def __init__(self): - super().__init__() - - def getOutline(self, view: Any, outline: Any) -> None: - """获取视图轮廓""" - pass - - -ViewOutlineProvider.BACKGROUND = ViewOutlineProvider() -ViewOutlineProvider.BOUNDS = ViewOutlineProvider() -ViewOutlineProvider.PADDED_BOUNDS = ViewOutlineProvider() - - -class Outline(Stub): - """Outline — 轮廓描述""" - - MODE_CONVEX_PATH = 2 - MODE_EMPTY = 0 - MODE_ROUND_RECT = 1 - - def __init__(self): - super().__init__() - self._alpha: float = 1.0 - self._mode: int = self.MODE_EMPTY - self._rect: Tuple[float, float, float, float] = (0, 0, 0, 0) - self._radius: float = 0.0 - - def setAlpha(self, alpha: float) -> None: - self._alpha = max(0.0, min(1.0, alpha)) - - def getAlpha(self) -> float: - return self._alpha - - def setEmpty(self) -> None: - self._mode = self.MODE_EMPTY - - def isEmpty(self) -> bool: - return self._mode == self.MODE_EMPTY - - def setRoundRect(self, left: float, top: float, right: float, bottom: float, - radius: float) -> None: - self._mode = self.MODE_ROUND_RECT - self._rect = (left, top, right, bottom) - self._radius = radius - - def setOval(self, left: float, top: float, right: float, bottom: float) -> None: - self._mode = self.MODE_ROUND_RECT - self._rect = (left, top, right, bottom) - self._radius = min(right - left, bottom - top) / 2.0 - - def setConvexPath(self, path: Any) -> None: - self._mode = self.MODE_CONVEX_PATH - - def getRect(self) -> Optional[Tuple[float, float, float, float]]: - if self._mode == self.MODE_EMPTY: - return None - return self._rect - - def getRadius(self) -> float: - return self._radius - - def canClip(self) -> bool: - return self._mode in (self.MODE_ROUND_RECT, self.MODE_CONVEX_PATH) - - def toString(self) -> str: - return (f"Outline(mode={self._mode}, alpha={self._alpha}, " - f"radius={self._radius})") - - -# ═══════════════════════════════════════════════════════════════ -# ViewPropertyAnimator -# ═══════════════════════════════════════════════════════════════ - -class ViewPropertyAnimator(_BaseViewPropertyAnimator): - """ViewPropertyAnimator — 视图属性动画器 (继承 view.ViewPropertyAnimator 的所有方法)""" - - def __init__(self, view: Any = None): - super().__init__() - self._view = weakref.ref(view) if view is not None else None - self._duration: int = 300 - self._start_delay: int = 0 - self._interpolator: Optional[Any] = None - self._listener: Optional[Callable] = None - self._translation_x: Optional[float] = None - self._translation_y: Optional[float] = None - self._translation_z: Optional[float] = None - self._scale_x: Optional[float] = None - self._scale_y: Optional[float] = None - self._rotation: Optional[float] = None - self._rotation_x: Optional[float] = None - self._rotation_y: Optional[float] = None - self._alpha: Optional[float] = None - self._x: Optional[float] = None - self._y: Optional[float] = None - self._width: Optional[int] = None - self._height: Optional[int] = None - self._running = False - - def translationX(self, value: float) -> "ViewPropertyAnimator": - self._translation_x = value - return self - - def translationY(self, value: float) -> "ViewPropertyAnimator": - self._translation_y = value - return self - - def translationZ(self, value: float) -> "ViewPropertyAnimator": - self._translation_z = value - return self - - def rotation(self, value: float) -> "ViewPropertyAnimator": - self._rotation = value - return self - - def rotationX(self, value: float) -> "ViewPropertyAnimator": - self._rotation_x = value - return self - - def rotationY(self, value: float) -> "ViewPropertyAnimator": - self._rotation_y = value - return self - - def scaleX(self, value: float) -> "ViewPropertyAnimator": - self._scale_x = value - return self - - def scaleY(self, value: float) -> "ViewPropertyAnimator": - self._scale_y = value - return self - - def alpha(self, value: float) -> "ViewPropertyAnimator": - self._alpha = max(0.0, min(1.0, value)) - return self - - def x(self, value: float) -> "ViewPropertyAnimator": - self._x = value - return self - - def y(self, value: float) -> "ViewPropertyAnimator": - self._y = value - return self - - def width(self, value: int) -> "ViewPropertyAnimator": - self._width = value - return self - - def height(self, value: int) -> "ViewPropertyAnimator": - self._height = value - return self - - def setDuration(self, duration: int) -> "ViewPropertyAnimator": - self._duration = max(0, duration) - return self - - def getDuration(self) -> int: - return self._duration - - def setStartDelay(self, delay: int) -> "ViewPropertyAnimator": - self._start_delay = max(0, delay) - return self - - def getStartDelay(self) -> int: - return self._start_delay - - def setInterpolator(self, interpolator: Any) -> "ViewPropertyAnimator": - self._interpolator = interpolator - return self - - def setListener(self, listener: Optional[Callable]) -> "ViewPropertyAnimator": - self._listener = listener - return self - - def withEndAction(self, action: Callable) -> "ViewPropertyAnimator": - self._end_action = action - return self - - def withStartAction(self, action: Callable) -> "ViewPropertyAnimator": - self._start_action = action - return self - - def start(self) -> None: - if self._running: - return - self._running = True - if self._start_action: - self._start_action() - if self._listener: - self._listener.onAnimationStart(self) - if self._alpha is not None and self._view: - v = self._view() - if v: - v.setAlpha(self._alpha) - if self._listener: - self._listener.onAnimationEnd(self) - if self._end_action: - self._end_action() - - def cancel(self) -> None: - self._running = False - if self._listener: - self._listener.onAnimationCancel(self) - - def isRunning(self) -> bool: - return self._running - - def toString(self) -> str: - return (f"ViewPropertyAnimator(duration={self._duration}, " - f"running={self._running})") - - -# ═══════════════════════════════════════════════════════════════ -# ViewStructure -# ═══════════════════════════════════════════════════════════════ - -class ViewStructure(Stub): - """ViewStructure — 视图结构(用于 Assist/autofill)""" - - def __init__(self): - super().__init__() - self._text: Optional[str] = None - self._hint: Optional[str] = None - self._className: Optional[str] = None - self._contentDescription: Optional[str] = None - self._id: Optional[int] = None - self._id_package: Optional[str] = None - self._id_type: Optional[str] = None - self._id_entry: Optional[str] = None - self._web_domain: Optional[str] = None - self._input_type: int = 0 - self._alpha: float = 1.0 - self._elevation: float = 0.0 - self._width: int = 0 - self._height: int = 0 - self._left: int = 0 - self._top: int = 0 - self._scroll_x: int = 0 - self._scroll_y: int = 0 - self._children: List[ViewStructure] = [] - self._html_info: Optional[Any] = None - self._data_set: Optional[Any] = None - - def setText(self, text: str) -> None: - self._text = text - - def getText(self) -> Optional[str]: - return self._text - - def setHint(self, hint: str) -> None: - self._hint = hint - - def getHint(self) -> Optional[str]: - return self._hint - - def setClassName(self, class_name: str) -> None: - self._className = class_name - - def getClassName(self) -> Optional[str]: - return self._className - - def setContentDescription(self, desc: str) -> None: - self._contentDescription = desc - - def getContentDescription(self) -> Optional[str]: - return self._contentDescription - - def setId(self, id_val: int, package: str, type_name: str, entry: str) -> None: - self._id = id_val - self._id_package = package - self._id_type = type_name - self._id_entry = entry - - def getAutoFillId(self) -> Optional[int]: - return self._id - - def setWebDomain(self, domain: str) -> None: - self._web_domain = domain - - def getWebDomain(self) -> Optional[str]: - return self._web_domain - - def setInputType(self, input_type: int) -> None: - self._input_type = input_type - - def getInputType(self) -> int: - return self._input_type - - def setAlpha(self, alpha: float) -> None: - self._alpha = max(0.0, min(1.0, alpha)) - - def getAlpha(self) -> float: - return self._alpha - - def setElevation(self, elevation: float) -> None: - self._elevation = elevation - - def getElevation(self) -> float: - return self._elevation - - def setDimensions(self, width: int, height: int) -> None: - self._width = width - self._height = height - - def setDimens(self, left: int, top: int, scroll_x: int, scroll_y: int) -> None: - self._left = left - self._top = top - self._scroll_x = scroll_x - self._scroll_y = scroll_y - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def getLeft(self) -> int: - return self._left - - def getTop(self) -> int: - return self._top - - def addChild(self, num: int) -> ViewStructure: - for _ in range(num): - child = ViewStructure() - self._children.append(child) - return self._children[0] if self._children else ViewStructure() - - def getChildCount(self) -> int: - return len(self._children) - - def getChildAt(self, index: int) -> Optional[ViewStructure]: - if 0 <= index < len(self._children): - return self._children[index] - return None - - def setAutofillHints(self, hints: List[str]) -> None: - pass - - def setAutofillType(self, type_val: int) -> None: - pass - - def setAutofillValue(self, value: Any) -> None: - pass - - def setDataIsSensitive(self, sensitive: bool) -> None: - pass - - def toString(self) -> str: - return (f"ViewStructure(class={self._className}, " - f"children={len(self._children)})") - - -# ═══════════════════════════════════════════════════════════════ -# ViewTreeObserver -# ═══════════════════════════════════════════════════════════════ - -class ViewTreeObserver(_BaseViewTreeObserver): - """ViewTreeObserver — 视图树观察者 (继承 view.ViewTreeObserver 的所有方法)""" - - def __init__(self): - super().__init__() - self._global_layout_listeners: List[Callable] = [] - self._global_focus_listeners: List[Callable] = [] - self._pre_draw_listeners: List[Callable] = [] - self._scroll_listeners: List[Callable] = [] - self._touch_mode_listeners: List[Callable] = [] - self._window_focus_listeners: List[Callable] = [] - self._window_attach_listeners: List[Callable] = [] - self._draw_listeners: List[Callable] = [] - self._lock = threading.Lock() - - def addOnGlobalLayoutListener(self, listener: Callable) -> None: - with self._lock: - if listener not in self._global_layout_listeners: - self._global_layout_listeners.append(listener) - - def removeOnGlobalLayoutListener(self, listener: Callable) -> None: - with self._lock: - if listener in self._global_layout_listeners: - self._global_layout_listeners.remove(listener) - - def addOnGlobalFocusChangeListener(self, listener: Callable) -> None: - with self._lock: - if listener not in self._global_focus_listeners: - self._global_focus_listeners.append(listener) - - def removeOnGlobalFocusChangeListener(self, listener: Callable) -> None: - with self._lock: - if listener in self._global_focus_listeners: - self._global_focus_listeners.remove(listener) - - def addOnPreDrawListener(self, listener: Callable) -> None: - with self._lock: - if listener not in self._pre_draw_listeners: - self._pre_draw_listeners.append(listener) - - def removeOnPreDrawListener(self, listener: Callable) -> None: - with self._lock: - if listener in self._pre_draw_listeners: - self._pre_draw_listeners.remove(listener) - - def addOnScrollChangedListener(self, listener: Callable) -> None: - with self._lock: - if listener not in self._scroll_listeners: - self._scroll_listeners.append(listener) - - def removeOnScrollChangedListener(self, listener: Callable) -> None: - with self._lock: - if listener in self._scroll_listeners: - self._scroll_listeners.remove(listener) - - def addOnTouchModeChangeListener(self, listener: Callable) -> None: - with self._lock: - if listener not in self._touch_mode_listeners: - self._touch_mode_listeners.append(listener) - - def removeOnTouchModeChangeListener(self, listener: Callable) -> None: - with self._lock: - if listener in self._touch_mode_listeners: - self._touch_mode_listeners.remove(listener) - - def addOnWindowFocusChangeListener(self, listener: Callable) -> None: - with self._lock: - if listener not in self._window_focus_listeners: - self._window_focus_listeners.append(listener) - - def removeOnWindowFocusChangeListener(self, listener: Callable) -> None: - with self._lock: - if listener in self._window_focus_listeners: - self._window_focus_listeners.remove(listener) - - def addOnWindowAttachListener(self, listener: Callable) -> None: - with self._lock: - if listener not in self._window_attach_listeners: - self._window_attach_listeners.append(listener) - - def removeOnWindowAttachListener(self, listener: Callable) -> None: - with self._lock: - if listener in self._window_attach_listeners: - self._window_attach_listeners.remove(listener) - - def addOnDrawListener(self, listener: Callable) -> None: - with self._lock: - if listener not in self._draw_listeners: - self._draw_listeners.append(listener) - - def removeOnDrawListener(self, listener: Callable) -> None: - with self._lock: - if listener in self._draw_listeners: - self._draw_listeners.remove(listener) - - def dispatchOnGlobalLayout(self) -> None: - with self._lock: - listeners = list(self._global_layout_listeners) - for listener in listeners: - listener() - - def isAlive(self) -> bool: - return True - - def toString(self) -> str: - return (f"ViewTreeObserver(layoutListeners={len(self._global_layout_listeners)}, " - f"preDrawListeners={len(self._pre_draw_listeners)})") - - -# ═══════════════════════════════════════════════════════════════ -# ViewDebug -# ═══════════════════════════════════════════════════════════════ - -class ViewDebug(Stub): - """ViewDebug — 视图调试工具""" - - TRACE_HIERARCHY = 0 - TRACE_CAPTURE = 1 - - @staticmethod - def dump(view: Any, out: Any) -> None: - pass - - @staticmethod - def dumpCapturedView(view: Any, out: Any, prefix: str) -> None: - pass - - @staticmethod - def trace(view: Any, type_val: int, out: Any) -> None: - pass - - @staticmethod - def startRecyclerTracing() -> None: - pass - - @staticmethod - def stopRecyclerTracing() -> None: - pass - - class RecyclerTraceable(Stub): - def getTraceInfo(self) -> str: - return "" - - -# ═══════════════════════════════════════════════════════════════ -# ViewConfiguration -# ═══════════════════════════════════════════════════════════════ - -class ViewConfiguration(_BaseViewConfiguration): - """ViewConfiguration — 视图配置 (继承 view.ViewConfiguration 的所有方法)""" - - def __init__(self): - super().__init__() - self._scaled_touch_slop: int = 8 - self._scaled_double_tap_slop: int = 100 - self._scaled_edge_slop: int = 12 - self._scaled_fading_edge_length: int = 12 - self._scaled_minimum_fling_velocity: int = 50 - self._scaled_maximum_fling_velocity: int = 4000 - self._scaled_scroll_bar_size: int = 10 - self._scaled_over_scroll_distance: int = 0 - self._scaled_paging_touch_slop: int = 16 - self._scaled_drawing_cache_size: int = 480 - - @staticmethod - def get(context: Any) -> "ViewConfiguration": - return ViewConfiguration() - - def getScaledTouchSlop(self) -> int: - return self._scaled_touch_slop - - def getScaledDoubleTapSlop(self) -> int: - return self._scaled_double_tap_slop - - def getScaledEdgeSlop(self) -> int: - return self._scaled_edge_slop - - def getScaledFadingEdgeLength(self) -> int: - return self._scaled_fading_edge_length - - def getScaledMinimumFlingVelocity(self) -> int: - return self._scaled_minimum_fling_velocity - - def getScaledMaximumFlingVelocity(self) -> int: - return self._scaled_maximum_fling_velocity - - def getScaledScrollBarSize(self) -> int: - return self._scaled_scroll_bar_size - - def getScaledOverScrollDistance(self) -> int: - return self._scaled_over_scroll_distance - - def getScaledPagingTouchSlop(self) -> int: - return self._scaled_paging_touch_slop - - def getScaledDrawingCacheSize(self) -> int: - return self._scaled_drawing_cache_size - - @staticmethod - def getLongPressTimeout() -> int: - return 500 - - @staticmethod - def getTapTimeout() -> int: - return 100 - - @staticmethod - def getDoubleTapTimeout() -> int: - return 300 - - @staticmethod - def getKeyRepeatTimeout() -> int: - return 400 - - @staticmethod - def getKeyRepeatDelay() -> int: - return 50 - - @staticmethod - def getScrollDefaultDelay() -> int: - return 40 - - @staticmethod - def getScrollFriction() -> float: - return 0.015 - - def toString(self) -> str: - return "ViewConfiguration()" - - -# ═══════════════════════════════════════════════════════════════ -# ViewParent (扩展) -# ═══════════════════════════════════════════════════════════════ - -class ViewParent(_BaseViewParent): - """ViewParent — 视图父容器接口扩展 (继承 view.ViewParent 的所有方法)""" - - def requestLayout(self) -> None: - pass - - def isLayoutRequested(self) -> bool: - return False - - def requestTransparentRegion(self, child: Any) -> None: - pass - - def invalidateChild(self, child: Any, r: Any) -> None: - pass - - def invalidateChildInParent(self, location: Any, r: Any) -> Optional[Any]: - return None - - def getParent(self) -> Optional["ViewParent"]: - return None - - def requestChildFocus(self, child: Any, focused: Any) -> None: - pass - - def recomputeViewAttributes(self, child: Any) -> None: - pass - - def clearChildFocus(self, child: Any) -> None: - pass - - def getChildVisibleRect(self, child: Any, r: Any, offset: Any) -> bool: - return True - - def createSnapshot(self, out: Any, child: Any) -> None: - pass - - def onStartTemporaryDetach(self) -> None: - pass - - def onFinishTemporaryDetach(self) -> None: - pass - - def bringChildToFront(self, child: Any) -> None: - pass - - def focusSearch(self, child: Any, direction: int) -> Optional[Any]: - return None - - def focusableViewAvailable(self, v: Any) -> None: - pass - - def showContextMenuForChild(self, original_view: Any) -> bool: - return False - - def actionModeStartingForChild(self, original_view: Any) -> Optional[Any]: - return None - - def requestSendAccessibilityEvent(self, child: Any, event: Any) -> bool: - return True - - def childHasTransientStateChanged(self, child: Any, has_state: bool) -> None: - pass - - def requestChildRectangleOnScreen(self, child: Any, rectangle: Any, - immediate: bool) -> bool: - return True - - def canResolveLayoutDirection(self) -> bool: - return True - - def isLayoutDirectionResolved(self) -> bool: - return True - - def getLayoutDirection(self) -> int: - return 0 - - def toString(self) -> str: - return "ViewParent()" - - -# ═══════════════════════════════════════════════════════════════ -# ViewGroup (扩展) -# ═══════════════════════════════════════════════════════════════ - -class ViewGroup(_BaseViewGroup): - """ViewGroup — 视图组扩展 (继承 view.ViewGroup 的所有方法)""" - - CLIP_TO_PADDING_MASK = 0 - FOCUS_AFTER = 1 - FOCUS_BEFORE = 2 - FOCUS_BLOCK = 3 - - LAYOUT_MODE_CLIP_BOUNDS = 0 - LAYOUT_MODE_OPTICAL_BOUNDS = 1 - - PERSISTENT_NO_CACHE = 0 - PERSISTENT_ANIMATION_CACHE = 1 - PERSISTENT_SCROLLING_CACHE = 2 - PERSISTENT_ALL_CACHES = 3 - - class LayoutParams(Stub): - """ViewGroup.LayoutParams — 布局参数""" - - FILL_PARENT = -1 - MATCH_PARENT = -1 - WRAP_CONTENT = -2 - - def __init__(self, width: int = WRAP_CONTENT, height: int = WRAP_CONTENT): - super().__init__() - self.width = width - self.height = height - - def resolveLayoutDirection(self, layout_direction: int) -> None: - pass - - def toString(self) -> str: - return f"LayoutParams({self.width}x{self.height})" - - class MarginLayoutParams(LayoutParams): - """ViewGroup.MarginLayoutParams — 带边距的布局参数""" - - def __init__(self, width: int = -2, - height: int = -2): - super().__init__(width, height) - self.leftMargin: int = 0 - self.topMargin: int = 0 - self.rightMargin: int = 0 - self.bottomMargin: int = 0 - self.marginStart: int = 0 - self.marginEnd: int = 0 - - def setMargins(self, left: int, top: int, right: int, bottom: int) -> None: - self.leftMargin = left - self.topMargin = top - self.rightMargin = right - self.bottomMargin = bottom - - def getMarginLeft(self) -> int: - return self.leftMargin - - def getMarginTop(self) -> int: - return self.topMargin - - def getMarginRight(self) -> int: - return self.rightMargin - - def getMarginBottom(self) -> int: - return self.bottomMargin - - def setMarginStart(self, start: int) -> None: - self.marginStart = start - - def setMarginEnd(self, end: int) -> None: - self.marginEnd = end - - def getMarginStart(self) -> int: - return self.marginStart - - def getMarginEnd(self) -> int: - return self.marginEnd - - def toString(self) -> str: - return (f"MarginLayoutParams({self.width}x{self.height}, " - f"margins=({self.leftMargin},{self.topMargin}," - f"{self.rightMargin},{self.bottomMargin}))") - - def __init__(self, context=None): - super().__init__(context) - if not hasattr(self, '_children'): - self._children: List[Any] = [] - if not hasattr(self, '_clip_children'): - self._clip_children = True - if not hasattr(self, '_clip_to_padding'): - self._clip_to_padding = True - self._layout_mode = self.LAYOUT_MODE_CLIP_BOUNDS - self._persistent_caching = self.PERSISTENT_NO_CACHE - self._touch_mode = False - if not hasattr(self, '_motion_event_splitting'): - self._motion_event_splitting = True - self._transient_child: Optional[Any] = None - self._add_stateset_focus = False - self._child_drawable_state: Optional[Any] = None - - def addView(self, child: Any, index: int = -1) -> None: - if index < 0 or index >= len(self._children): - self._children.append(child) - else: - self._children.insert(index, child) - - def removeView(self, child: Any) -> None: - if child in self._children: - self._children.remove(child) - - def removeViewAt(self, index: int) -> None: - if 0 <= index < len(self._children): - self._children.pop(index) - - def removeAllViews(self) -> None: - self._children.clear() - - def getChildCount(self) -> int: - return len(self._children) - - def getChildAt(self, index: int) -> Optional[Any]: - if 0 <= index < len(self._children): - return self._children[index] - return None - - def indexOfChild(self, child: Any) -> int: - try: - return self._children.index(child) - except ValueError: - return -1 - - def bringChildToFront(self, child: Any) -> None: - if child in self._children: - self._children.remove(child) - self._children.append(child) - - def setClipChildren(self, clip: bool) -> None: - self._clip_children = clip - - def getClipChildren(self) -> bool: - return self._clip_children - - def setClipToPadding(self, clip: bool) -> None: - self._clip_to_padding = clip - - def getClipToPadding(self) -> bool: - return self._clip_to_padding - - def setLayoutMode(self, mode: int) -> None: - self._layout_mode = mode - - def getLayoutMode(self) -> int: - return self._layout_mode - - def setPersistentDrawingCache(self, cache: int) -> None: - self._persistent_caching = cache - - def getPersistentDrawingCache(self) -> int: - return self._persistent_caching - - def setAddStatesFromChildren(self, add: bool) -> None: - self._add_stateset_focus = add - - def addStatesFromChildren(self) -> bool: - return self._add_stateset_focus - - def setMotionEventSplittingEnabled(self, split: bool) -> None: - self._motion_event_splitting = split - - def isMotionEventSplittingEnabled(self) -> bool: - return self._motion_event_splitting - - def setTouchMode(self, touch: bool) -> None: - self._touch_mode = touch - - def isTouchMode(self) -> bool: - return self._touch_mode - - def onLayout(self, changed: bool, l: int, t: int, r: int, b: int) -> None: - pass - - def onInterceptTouchEvent(self, ev: Any) -> bool: - return False - - def requestDisallowInterceptTouchEvent(self, disallow: bool) -> None: - pass - - def getChildDrawingOrder(self, child_count: int, i: int) -> int: - return i - - def setChildrenDrawingOrderEnabled(self, enabled: bool) -> None: - pass - - def isChildrenDrawingOrderEnabled(self) -> bool: - return False - - def toString(self) -> str: - return f"ViewGroup(children={len(self._children)})" - - -class ViewGroupOverlay(Stub): - """ViewGroupOverlay — 视图组覆盖层""" - - def __init__(self, host: Any = None): - super().__init__() - self._host = host - self._drawables: List[Any] = [] - - def add(self, drawable: Any) -> None: - if drawable not in self._drawables: - self._drawables.append(drawable) - - def remove(self, drawable: Any) -> None: - if drawable in self._drawables: - self._drawables.remove(drawable) - - def clear(self) -> None: - self._drawables.clear() - - def toString(self) -> str: - return f"ViewGroupOverlay(size={len(self._drawables)})" - - -class ViewOverlay(Stub): - """ViewOverlay — 视图覆盖层""" - - def __init__(self, host: Any = None): - super().__init__() - self._host = host - self._drawables: List[Any] = [] - - def add(self, drawable: Any) -> None: - if drawable not in self._drawables: - self._drawables.append(drawable) - - def remove(self, drawable: Any) -> None: - if drawable in self._drawables: - self._drawables.remove(drawable) - - def clear(self) -> None: - self._drawables.clear() - - def getHost(self) -> Optional[Any]: - return self._host - - def toString(self) -> str: - return f"ViewOverlay(size={len(self._drawables)})" - - -# ═══════════════════════════════════════════════════════════════ -# WindowInsets / DisplayCutout / Display -# ═══════════════════════════════════════════════════════════════ - -class WindowInsets(_BaseWindowInsets): - """WindowInsets — 窗口插入区域 (继承 view.WindowInsets 的所有方法)""" - - def __init__(self): - super().__init__() - self._system_window_insets: Tuple[int, int, int, int] = (0, 0, 0, 0) - self._stable_insets: Tuple[int, int, int, int] = (0, 0, 0, 0) - self._display_cutout_insets: Tuple[int, int, int, int] = (0, 0, 0, 0) - self._mandatory_system_gesture_insets: Tuple[int, int, int, int] = (0, 0, 0, 0) - self._tappable_element_insets: Tuple[int, int, int, int] = (0, 0, 0, 0) - self._system_gesture_insets: Tuple[int, int, int, int] = (0, 0, 0, 0) - self._consume_stable = False - self._consume_system_windows = False - self._display_cutout: Optional[DisplayCutout] = None - self._rounded_corners: List[Any] = [] - self._privacy_indicators: Optional[Tuple[int, int, int, int]] = None - - def getSystemWindowInsetLeft(self) -> int: - return self._system_window_insets[0] - - def getSystemWindowInsetTop(self) -> int: - return self._system_window_insets[1] - - def getSystemWindowInsetRight(self) -> int: - return self._system_window_insets[2] - - def getSystemWindowInsetBottom(self) -> int: - return self._system_window_insets[3] - - def getSystemWindowInsets(self) -> Tuple[int, int, int, int]: - return self._system_window_insets - - def getStableInsetLeft(self) -> int: - return self._stable_insets[0] - - def getStableInsetTop(self) -> int: - return self._stable_insets[1] - - def getStableInsetRight(self) -> int: - return self._stable_insets[2] - - def getStableInsetBottom(self) -> int: - return self._stable_insets[3] - - def getDisplayCutout(self) -> Optional[DisplayCutout]: - return self._display_cutout - - def getDisplayCutoutInsets(self) -> Tuple[int, int, int, int]: - return self._display_cutout_insets - - def getMandatorySystemGestureInsets(self) -> Tuple[int, int, int, int]: - return self._mandatory_system_gesture_insets - - def getTappableElementInsets(self) -> Tuple[int, int, int, int]: - return self._tappable_element_insets - - def getSystemGestureInsets(self) -> Tuple[int, int, int, int]: - return self._system_gesture_insets - - def consumeSystemWindowInsets(self) -> WindowInsets: - insets = WindowInsets() - insets._system_window_insets = (0, 0, 0, 0) - return insets - - def consumeStableInsets(self) -> WindowInsets: - insets = WindowInsets() - insets._stable_insets = (0, 0, 0, 0) - return insets - - def isConsumed(self) -> bool: - return self._consume_system_windows - - def hasInsets(self) -> bool: - return (max(self._system_window_insets) > 0 or - max(self._stable_insets) > 0) - - def hasSystemWindowInsets(self) -> bool: - return max(self._system_window_insets) > 0 - - def hasStableInsets(self) -> bool: - return max(self._stable_insets) > 0 - - def hasDisplayCutout(self) -> bool: - return self._display_cutout is not None - - def toString(self) -> str: - return (f"WindowInsets(sys={self._system_window_insets}, " - f"stable={self._stable_insets})") - - -class DisplayCutout(Stub): - """DisplayCutout — 屏幕缺口信息""" - - def __init__(self): - super().__init__() - self._safe_insets: Tuple[int, int, int, int] = (0, 0, 0, 0) - self._bound_left: Optional[Any] = None - self._bound_top: Optional[Any] = None - self._bound_right: Optional[Any] = None - self._bound_bottom: Optional[Any] = None - self._waterfall_insets: Tuple[int, int, int, int] = (0, 0, 0, 0) - - def getSafeInsetLeft(self) -> int: - return self._safe_insets[0] - - def getSafeInsetTop(self) -> int: - return self._safe_insets[1] - - def getSafeInsetRight(self) -> int: - return self._safe_insets[2] - - def getSafeInsetBottom(self) -> int: - return self._safe_insets[3] - - def getBoundingRectLeft(self) -> Optional[Any]: - return self._bound_left - - def getBoundingRectTop(self) -> Optional[Any]: - return self._bound_top - - def getBoundingRectRight(self) -> Optional[Any]: - return self._bound_right - - def getBoundingRectBottom(self) -> Optional[Any]: - return self._bound_bottom - - def getWaterfallInsets(self) -> Tuple[int, int, int, int]: - return self._waterfall_insets - - def toString(self) -> str: - return f"DisplayCutout(safe={self._safe_insets})" - - -class Display(_BaseDisplay): - """Display — 屏幕显示信息 (继承 view.Display 的所有方法)""" - - DEFAULT_DISPLAY = 0 - FLAG_PRESENTATION = 1 - FLAG_SECURE = 2 - FLAG_SUPPORTS_PROTECTED_BUFFERS = 4 - - STATE_OFF = 0 - STATE_ON = 1 - STATE_DOZE = 2 - STATE_DOZE_SUSPEND = 3 - STATE_SUSPENDED = 4 - STATE_UNKNOWN = -1 - - def __init__(self, display_id: int = DEFAULT_DISPLAY): - super().__init__() - self._display_id = display_id - self._width: int = 1080 - self._height: int = 1920 - self._refresh_rate: float = 60.0 - self._rotation: int = 0 - self._state: int = STATE_ON - self._name: str = "Internal Display" - self._flags: int = 0 - self._density: float = 2.0 - self._dpi_x: float = 480.0 - self._dpi_y: float = 480.0 - self._app_width: int = 1080 - self._app_height: int = 1920 - self._smallest_width: int = 1080 - self._largest_width: int = 1920 - self._hdr_capabilities: Optional[Any] = None - self._mode: Optional[Any] = None - self._supported_modes: List[Any] = [] - - def getDisplayId(self) -> int: - return self._display_id - - def getName(self) -> str: - return self._name - - def getFlags(self) -> int: - return self._flags - - def getHeight(self) -> int: - return self._height - - def getWidth(self) -> int: - return self._width - - def getRotation(self) -> int: - return self._rotation - - def getState(self) -> int: - return self._state - - def getRefreshRate(self) -> float: - return self._refresh_rate - - def getDensity(self) -> float: - return self._density - - def getDpiX(self) -> float: - return self._dpi_x - - def getDpiY(self) -> float: - return self._dpi_y - - def getAppWidth(self) -> int: - return self._app_width - - def getAppHeight(self) -> int: - return self._app_height - - def getSmallestWidth(self) -> int: - return self._smallest_width - - def getLargestWidth(self) -> int: - return self._largest_width - - def getSupportedModes(self) -> List[Any]: - return list(self._supported_modes) - - def getMode(self) -> Optional[Any]: - return self._mode - - def isHdr(self) -> bool: - return False - - def getHdrCapabilities(self) -> Optional[Any]: - return self._hdr_capabilities - - def isValid(self) -> bool: - return True - - def toString(self) -> str: - return (f"Display(id={self._display_id}, " - f"{self._width}x{self._height}, " - f"rotation={self._rotation})") - - -# ═══════════════════════════════════════════════════════════════ -# DragEvent / GestureDetector / KeyEvent / KeyCharacterMap -# ═══════════════════════════════════════════════════════════════ - -DRAG_EVENT_ACTION_DRAG_STARTED = 1 -DRAG_EVENT_ACTION_DRAG_ENTERED = 2 -DRAG_EVENT_ACTION_DRAG_LOCATION = 3 -DRAG_EVENT_ACTION_DRAG_EXITED = 4 -DRAG_EVENT_ACTION_DROP = 5 -DRAG_EVENT_ACTION_DRAG_ENDED = 6 - -GESTURE_STATE_NONE = 0 -GESTURE_STATE_STARTED = 1 -GESTURE_STATE_IN_PROGRESS = 2 -GESTURE_STATE_FINISHED = 3 - -ACTION_DOWN = 0 -ACTION_UP = 1 -ACTION_MOVE = 2 -ACTION_CANCEL = 3 -ACTION_OUTSIDE = 4 -ACTION_POINTER_DOWN = 5 -ACTION_POINTER_UP = 6 -ACTION_HOVER_ENTER = 7 -ACTION_HOVER_EXIT = 8 -ACTION_HOVER_MOVE = 9 -ACTION_SCROLL = 10 -ACTION_POINTER_INDEX_MASK = 0xFF00 -ACTION_POINTER_INDEX_SHIFT = 8 - -META_ALT_MASK = 2 -META_ALT_LEFT_ON = 16 -META_ALT_RIGHT_ON = 32 -META_ALT_ON = 2 -META_CTRL_MASK = 4096 -META_CTRL_LEFT_ON = 8192 -META_CTRL_RIGHT_ON = 16384 -META_CTRL_ON = 4096 -META_FUNCTION_ON = 8 -META_META_MASK = 65536 -META_META_LEFT_ON = 131072 -META_META_RIGHT_ON = 262144 -META_META_ON = 65536 -META_SHIFT_MASK = 1 -META_SHIFT_LEFT_ON = 64 -META_SHIFT_RIGHT_ON = 128 -META_SHIFT_ON = 1 -META_SYM_ON = 4 -META_CAPS_LOCK_ON = 1048576 -META_NUM_LOCK_ON = 2097152 -META_SCROLL_LOCK_ON = 4194304 - -KEYCODE_UNKNOWN = 0 -KEYCODE_SOFT_LEFT = 1 -KEYCODE_SOFT_RIGHT = 2 -KEYCODE_HOME = 3 -KEYCODE_BACK = 4 -KEYCODE_CALL = 5 -KEYCODE_ENDCALL = 6 -KEYCODE_0 = 7 -KEYCODE_1 = 8 -KEYCODE_2 = 9 -KEYCODE_3 = 10 -KEYCODE_4 = 11 -KEYCODE_5 = 12 -KEYCODE_6 = 13 -KEYCODE_7 = 14 -KEYCODE_8 = 15 -KEYCODE_9 = 16 -KEYCODE_STAR = 17 -KEYCODE_POUND = 18 -KEYCODE_DPAD_UP = 19 -KEYCODE_DPAD_DOWN = 20 -KEYCODE_DPAD_LEFT = 21 -KEYCODE_DPAD_RIGHT = 22 -KEYCODE_DPAD_CENTER = 23 -KEYCODE_VOLUME_UP = 24 -KEYCODE_VOLUME_DOWN = 25 -KEYCODE_POWER = 26 -KEYCODE_CAMERA = 27 -KEYCODE_CLEAR = 28 -KEYCODE_A = 29 -KEYCODE_B = 30 -KEYCODE_C = 31 -KEYCODE_D = 32 -KEYCODE_E = 33 -KEYCODE_F = 34 -KEYCODE_G = 35 -KEYCODE_H = 36 -KEYCODE_I = 37 -KEYCODE_J = 38 -KEYCODE_K = 39 -KEYCODE_L = 40 -KEYCODE_M = 41 -KEYCODE_N = 42 -KEYCODE_O = 43 -KEYCODE_P = 44 -KEYCODE_Q = 45 -KEYCODE_R = 46 -KEYCODE_S = 47 -KEYCODE_T = 48 -KEYCODE_U = 49 -KEYCODE_V = 50 -KEYCODE_W = 51 -KEYCODE_X = 52 -KEYCODE_Y = 53 -KEYCODE_Z = 54 -KEYCODE_COMMA = 55 -KEYCODE_PERIOD = 56 -KEYCODE_ALT_LEFT = 57 -KEYCODE_ALT_RIGHT = 58 -KEYCODE_SHIFT_LEFT = 59 -KEYCODE_SHIFT_RIGHT = 60 -KEYCODE_TAB = 61 -KEYCODE_SPACE = 62 -KEYCODE_SYM = 63 -KEYCODE_EXPLORER = 64 -KEYCODE_ENVELOPE = 65 -KEYCODE_ENTER = 66 -KEYCODE_DEL = 67 -KEYCODE_GRAVE = 68 -KEYCODE_MINUS = 69 -KEYCODE_EQUALS = 70 -KEYCODE_LEFT_BRACKET = 71 -KEYCODE_RIGHT_BRACKET = 72 -KEYCODE_BACKSLASH = 73 -KEYCODE_SEMICOLON = 74 -KEYCODE_APOSTROPHE = 75 -KEYCODE_SLASH = 76 -KEYCODE_AT = 77 -KEYCODE_NUM = 78 -KEYCODE_HEADSETHOOK = 79 -KEYCODE_FOCUS = 80 -KEYCODE_PLUS = 81 -KEYCODE_MENU = 82 -KEYCODE_NOTIFICATION = 83 -KEYCODE_SEARCH = 84 -KEYCODE_MEDIA_PLAY_PAUSE = 85 -KEYCODE_MEDIA_STOP = 86 -KEYCODE_MEDIA_NEXT = 87 -KEYCODE_MEDIA_PREVIOUS = 88 -KEYCODE_MEDIA_REWIND = 89 -KEYCODE_MEDIA_FAST_FORWARD = 90 -KEYCODE_MUTE = 91 -KEYCODE_PAGE_UP = 92 -KEYCODE_PAGE_DOWN = 93 -KEYCODE_PICTSYMBOLS = 94 -KEYCODE_SWITCH_CHARSET = 95 -KEYCODE_BUTTON_A = 96 -KEYCODE_BUTTON_B = 97 -KEYCODE_BUTTON_C = 98 -KEYCODE_BUTTON_X = 99 -KEYCODE_BUTTON_Y = 100 -KEYCODE_BUTTON_Z = 101 -KEYCODE_BUTTON_L1 = 102 -KEYCODE_BUTTON_R1 = 103 -KEYCODE_BUTTON_L2 = 104 -KEYCODE_BUTTON_R2 = 105 -KEYCODE_BUTTON_THUMBL = 106 -KEYCODE_BUTTON_THUMBR = 107 -KEYCODE_BUTTON_START = 108 -KEYCODE_BUTTON_SELECT = 109 -KEYCODE_BUTTON_MODE = 110 -KEYCODE_ESCAPE = 111 -KEYCODE_FORWARD_DEL = 112 -KEYCODE_CTRL_LEFT = 113 -KEYCODE_CTRL_RIGHT = 114 -KEYCODE_CAPS_LOCK = 115 -KEYCODE_SCROLL_LOCK = 116 -KEYCODE_META_LEFT = 117 -KEYCODE_META_RIGHT = 118 -KEYCODE_FUNCTION = 119 -KEYCODE_SYSRQ = 120 -KEYCODE_BREAK = 121 -KEYCODE_MOVE_HOME = 122 -KEYCODE_MOVE_END = 123 -KEYCODE_INSERT = 124 -KEYCODE_FORWARD = 125 -KEYCODE_MEDIA_PLAY = 126 -KEYCODE_MEDIA_PAUSE = 127 -KEYCODE_MEDIA_CLOSE = 128 -KEYCODE_MEDIA_EJECT = 129 -KEYCODE_MEDIA_RECORD = 130 -KEYCODE_F1 = 131 -KEYCODE_F2 = 132 -KEYCODE_F3 = 133 -KEYCODE_F4 = 134 -KEYCODE_F5 = 135 -KEYCODE_F6 = 136 -KEYCODE_F7 = 137 -KEYCODE_F8 = 138 -KEYCODE_F9 = 139 -KEYCODE_F10 = 140 -KEYCODE_F11 = 141 -KEYCODE_F12 = 142 -KEYCODE_NUM_LOCK = 143 -KEYCODE_NUMPAD_0 = 144 -KEYCODE_NUMPAD_1 = 145 -KEYCODE_NUMPAD_2 = 146 -KEYCODE_NUMPAD_3 = 147 -KEYCODE_NUMPAD_4 = 148 -KEYCODE_NUMPAD_5 = 149 -KEYCODE_NUMPAD_6 = 150 -KEYCODE_NUMPAD_7 = 151 -KEYCODE_NUMPAD_8 = 152 -KEYCODE_NUMPAD_9 = 153 -KEYCODE_NUMPAD_DIVIDE = 154 -KEYCODE_NUMPAD_MULTIPLY = 155 -KEYCODE_NUMPAD_SUBTRACT = 156 -KEYCODE_NUMPAD_ADD = 157 -KEYCODE_NUMPAD_DOT = 158 -KEYCODE_NUMPAD_COMMA = 159 -KEYCODE_NUMPAD_ENTER = 160 -KEYCODE_NUMPAD_EQUALS = 161 -KEYCODE_NUMPAD_LEFT_PAREN = 162 -KEYCODE_NUMPAD_RIGHT_PAREN = 163 -KEYCODE_VOLUME_MUTE = 164 -KEYCODE_INFO = 165 -KEYCODE_CHANNEL_UP = 166 -KEYCODE_CHANNEL_DOWN = 167 -KEYCODE_ZOOM_IN = 168 -KEYCODE_ZOOM_OUT = 169 - -WINDOW_INSETS_TYPE_SYSTEM_BARS = 0 -WINDOW_INSETS_TYPE_STATUS_BARS = 1 -WINDOW_INSETS_TYPE_NAVIGATION_BARS = 2 -WINDOW_INSETS_TYPE_CAPTION_BAR = 3 -WINDOW_INSETS_TYPE_IME = 4 -WINDOW_INSETS_TYPE_TAPPABLE_ELEMENT = 5 -WINDOW_INSETS_TYPE_MANDATORY_SYSTEM_GESTURES = 6 -WINDOW_INSETS_TYPE_SYSTEM_GESTURES = 7 -WINDOW_INSETS_TYPE_DISPLAY_CUTOUT = 8 -WINDOW_INSETS_TYPE_WATERFALL = 9 -WINDOW_INSETS_TYPE_PRIVACY_INDICATORS = 10 - -WINDOW_INSETS_ANIMATION_TYPE_NONE = 0 -WINDOW_INSETS_ANIMATION_TYPE_ALPHA = 1 -WINDOW_INSETS_ANIMATION_TYPE_TRANSLATION = 2 -WINDOW_INSETS_ANIMATION_TYPE_ALPHA_AND_TRANSLATION = 3 - - -class DragEvent(_BaseDragEvent): - """DragEvent — 拖放事件 (继承 view.DragEvent 的所有方法)""" - - ACTION_DRAG_STARTED = DRAG_EVENT_ACTION_DRAG_STARTED - ACTION_DRAG_ENTERED = DRAG_EVENT_ACTION_DRAG_ENTERED - ACTION_DRAG_LOCATION = DRAG_EVENT_ACTION_DRAG_LOCATION - ACTION_DRAG_EXITED = DRAG_EVENT_ACTION_DRAG_EXITED - ACTION_DROP = DRAG_EVENT_ACTION_DROP - ACTION_DRAG_ENDED = DRAG_EVENT_ACTION_DRAG_ENDED - - def __init__(self, action: int = 0): - super().__init__() - self._action = action - self._x: float = 0.0 - self._y: float = 0.0 - self._clip_data: Optional[Any] = None - self._result: Optional[Any] = None - self._local_state: Optional[Any] = None - self._drag_surface: Optional[Any] = None - - def getAction(self) -> int: - return self._action - - def getX(self) -> float: - return self._x - - def getY(self) -> float: - return self._y - - def getClipData(self) -> Optional[Any]: - return self._clip_data - - def getResult(self) -> Optional[Any]: - return self._result - - def getLocalState(self) -> Optional[Any]: - return self._local_state - - def toString(self) -> str: - actions = {1: "DRAG_STARTED", 2: "DRAG_ENTERED", 3: "DRAG_LOCATION", - 4: "DRAG_EXITED", 5: "DROP", 6: "DRAG_ENDED"} - return f"DragEvent(action={actions.get(self._action, 'UNKNOWN')})" - - -class GestureDetector(_BaseGestureDetector): - """GestureDetector — 手势检测器 (继承 view.GestureDetector 的所有方法)""" - - class SimpleOnGestureListener(Stub): - """GestureDetector.SimpleOnGestureListener — 简单手势监听器""" - - def onDown(self, e: Any) -> bool: - return False - - def onShowPress(self, e: Any) -> None: - pass - - def onSingleTapUp(self, e: Any) -> bool: - return False - - def onSingleTapConfirmed(self, e: Any) -> bool: - return False - - def onDoubleTap(self, e: Any) -> bool: - return False - - def onDoubleTapEvent(self, e: Any) -> bool: - return False - - def onLongPress(self, e: Any) -> None: - pass - - def onScroll(self, e1: Any, e2: Any, distance_x: float, distance_y: float) -> bool: - return False - - def onFling(self, e1: Any, e2: Any, velocity_x: float, velocity_y: float) -> bool: - return False - - def onContextClick(self, e: Any) -> bool: - return False - - class OnGestureListener(Stub): - def onDown(self, e: Any) -> bool: - return False - def onShowPress(self, e: Any) -> None: - pass - def onSingleTapUp(self, e: Any) -> bool: - return False - def onScroll(self, e1: Any, e2: Any, dx: float, dy: float) -> bool: - return False - def onLongPress(self, e: Any) -> None: - pass - def onFling(self, e1: Any, e2: Any, vx: float, vy: float) -> bool: - return False - - class OnDoubleTapListener(Stub): - def onSingleTapConfirmed(self, e: Any) -> bool: - return False - def onDoubleTap(self, e: Any) -> bool: - return False - def onDoubleTapEvent(self, e: Any) -> bool: - return False - def onContextClick(self, e: Any) -> bool: - return False - - def __init__(self, context: Any = None, listener: Optional[Any] = None): - super().__init__() - self._listener = listener - self._double_tap_listener: Optional[Any] = None - self._is_double_tapping = False - self._touch_slop: int = 8 - self._double_tap_slop: int = 100 - self._double_tap_timeout: int = 300 - self._longpress_timeout: int = 500 - self._last_tap_x: float = 0 - self._last_tap_y: float = 0 - self._last_tap_time: int = 0 - - def setOnDoubleTapListener(self, listener: Optional[Any]) -> None: - self._double_tap_listener = listener - - def setContextClickListener(self, listener: Optional[Any]) -> None: - pass - - def setIsLongpressEnabled(self, enabled: bool) -> None: - pass - - def isLongpressEnabled(self) -> bool: - return True - - def onTouchEvent(self, ev: Any) -> bool: - return True - - def toString(self) -> str: - return f"GestureDetector(listener={'yes' if self._listener else 'no'})" - - -class KeyEvent(_BaseKeyEvent): - """KeyEvent — 按键事件 (扩展版,继承 view.KeyEvent 的所有常量)""" - - ACTION_DOWN = 0 - ACTION_UP = 1 - ACTION_MULTIPLE = 2 - - FLAG_WOKE_HERE = 1 - FLAG_SOFT_KEYBOARD = 2 - FLAG_KEEP_TOUCH_MODE = 4 - FLAG_FROM_SYSTEM = 8 - FLAG_EDITOR_ACTION = 16 - FLAG_CANCELED = 32 - FLAG_VIRTUAL_HARD_KEY = 64 - FLAG_LONG_PRESS = 128 - FLAG_CANCELED_LONG_PRESS = 256 - FLAG_TRACKING = 512 - FLAG_FALLBACK = 1024 - - def __init__(self, action: int = ACTION_DOWN, code: int = KEYCODE_UNKNOWN): - super().__init__() - self._action = action - self._code = code - self._repeat: int = 0 - self._meta_state: int = 0 - self._device_id: int = 0 - self._scan_code: int = 0 - self._flags: int = 0 - self._down_time: int = 0 - self._event_time: int = 0 - self._display_id: int = 0 - - def getAction(self) -> int: - return self._action - - def getKeyCode(self) -> int: - return self._code - - def getRepeatCount(self) -> int: - return self._repeat - - def getMetaState(self) -> int: - return self._meta_state - - def getDeviceId(self) -> int: - return self._device_id - - def getScanCode(self) -> int: - return self._scan_code - - def getFlags(self) -> int: - return self._flags - - def getDownTime(self) -> int: - return self._down_time - - def getEventTime(self) -> int: - return self._event_time - - def getDisplayId(self) -> int: - return self._display_id - - def isLongPress(self) -> bool: - return bool(self._flags & self.FLAG_LONG_PRESS) - - def isCanceled(self) -> bool: - return bool(self._flags & self.FLAG_CANCELED) - - @staticmethod - def isModifierKey(key_code: int) -> bool: - return key_code in (KEYCODE_ALT_LEFT, KEYCODE_ALT_RIGHT, - KEYCODE_SHIFT_LEFT, KEYCODE_SHIFT_RIGHT, - KEYCODE_CTRL_LEFT, KEYCODE_CTRL_RIGHT, - KEYCODE_META_LEFT, KEYCODE_META_RIGHT, - KEYCODE_SYM, KEYCODE_FUNCTION) - - @staticmethod - def keyCodeFromString(symbolic_name: str) -> int: - mapping = { - "KEYCODE_A": KEYCODE_A, "KEYCODE_B": KEYCODE_B, - "KEYCODE_C": KEYCODE_C, "KEYCODE_D": KEYCODE_D, - "KEYCODE_E": KEYCODE_E, "KEYCODE_F": KEYCODE_F, - "KEYCODE_G": KEYCODE_G, "KEYCODE_H": KEYCODE_H, - "KEYCODE_I": KEYCODE_I, "KEYCODE_J": KEYCODE_J, - "KEYCODE_K": KEYCODE_K, "KEYCODE_L": KEYCODE_L, - "KEYCODE_M": KEYCODE_M, "KEYCODE_N": KEYCODE_N, - "KEYCODE_O": KEYCODE_O, "KEYCODE_P": KEYCODE_P, - "KEYCODE_Q": KEYCODE_Q, "KEYCODE_R": KEYCODE_R, - "KEYCODE_S": KEYCODE_S, "KEYCODE_T": KEYCODE_T, - "KEYCODE_U": KEYCODE_U, "KEYCODE_V": KEYCODE_V, - "KEYCODE_W": KEYCODE_W, "KEYCODE_X": KEYCODE_X, - "KEYCODE_Y": KEYCODE_Y, "KEYCODE_Z": KEYCODE_Z, - "KEYCODE_0": KEYCODE_0, "KEYCODE_1": KEYCODE_1, - "KEYCODE_2": KEYCODE_2, "KEYCODE_3": KEYCODE_3, - "KEYCODE_4": KEYCODE_4, "KEYCODE_5": KEYCODE_5, - "KEYCODE_6": KEYCODE_6, "KEYCODE_7": KEYCODE_7, - "KEYCODE_8": KEYCODE_8, "KEYCODE_9": KEYCODE_9, - "KEYCODE_HOME": KEYCODE_HOME, "KEYCODE_BACK": KEYCODE_BACK, - "KEYCODE_MENU": KEYCODE_MENU, "KEYCODE_SEARCH": KEYCODE_SEARCH, - "KEYCODE_ENTER": KEYCODE_ENTER, "KEYCODE_DEL": KEYCODE_DEL, - "KEYCODE_SPACE": KEYCODE_SPACE, "KEYCODE_TAB": KEYCODE_TAB, - } - return mapping.get(symbolic_name, KEYCODE_UNKNOWN) - - @staticmethod - def keyCodeToString(key_code: int) -> str: - reverse = {v: k for k, v in { - "KEYCODE_A": KEYCODE_A, "KEYCODE_B": KEYCODE_B, - "KEYCODE_HOME": KEYCODE_HOME, "KEYCODE_BACK": KEYCODE_BACK, - }.items()} - return reverse.get(key_code, f"KEYCODE_{key_code}") - - def toString(self) -> str: - return (f"KeyEvent(action={self._action}, code={self._code}, " - f"repeat={self._repeat})") - - -class KeyCharacterMap(Stub): - """KeyCharacterMap — 按键字符映射""" - - ALPHA = 0 - FULL = 1 - NUMERIC = 2 - PREDICTIVE = 3 - SPECIAL_FUNCTION = 4 - VIRTUAL = 5 - - BUILT_IN_KEYBOARD = 0 - NUMERIC_MODIFIER_MASK = 1 - MODIFIER_BEHAVIOR_CHORDED = 0 - MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED = 1 - - def __init__(self): - super().__init__() - - def get(self, key_code: int, meta_state: int) -> int: - return 0 - - def getNumber(self, key_code: int) -> int: - if KEYCODE_0 <= key_code <= KEYCODE_9: - return ord("0") + (key_code - KEYCODE_0) - return 0 - - def getMatch(self, key_code: int, chars: List[int], - meta_state: int = 0) -> int: - return 0 - - def getDisplayLabel(self, key_code: int) -> int: - return key_code - - def getKeyboardType(self) -> int: - return self.QWERTY - - @staticmethod - def deviceHasKey(key_code: int) -> bool: - return True - - @staticmethod - def deviceHasKeys(key_codes: List[int]) -> List[bool]: - return [True] * len(key_codes) - - def toString(self) -> str: - return "KeyCharacterMap()" - - -class KeyDispatcherState(Stub): - """KeyDispatcherState — 按键调度状态""" - - def __init__(self): - super().__init__() - self._state: Dict[int, Any] = {} - - def reset(self) -> None: - self._state.clear() - - def startTracking(self, event: KeyEvent, target: Any) -> None: - key = event.getKeyCode() - self._state[key] = {"target": target, "started": True} - - def isTracking(self, event: KeyEvent) -> bool: - key = event.getKeyCode() - return key in self._state and self._state[key].get("started", False) - - def performedLongPress(self, event: KeyEvent) -> None: - key = event.getKeyCode() - if key in self._state: - self._state[key]["longpress"] = True - - def handleUpEvent(self, event: KeyEvent) -> bool: - key = event.getKeyCode() - if key in self._state: - del self._state[key] - return True - return False - - def toString(self) -> str: - return f"KeyDispatcherState(tracking={len(self._state)})" - - -# ═══════════════════════════════════════════════════════════════ -# LayoutInflater / Menu / MenuItem / SubMenu / ContextMenu -# ═══════════════════════════════════════════════════════════════ - -class LayoutInflater(Stub): - """LayoutInflater — 布局填充器""" - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - - @staticmethod - def from_context(context: Any) -> "LayoutInflater": - return LayoutInflater(context) - - def inflate(self, resource: Any, root: Any, attach_to_root: bool = False) -> Optional[Any]: - return None - - def getContext(self) -> Optional[Any]: - return self._context - - def cloneInContext(self, new_context: Any) -> "LayoutInflater": - return LayoutInflater(new_context) - - def setFactory(self, factory: Any) -> None: - pass - - def getFactory(self) -> Optional[Any]: - return None - - def setFactory2(self, factory: Any) -> None: - pass - - def getFactory2(self) -> Optional[Any]: - return None - - def setFilters(self, filters: List[Any]) -> None: - pass - - def toString(self) -> str: - return f"LayoutInflater(context={self._context})" - - -class Menu(Stub): - """Menu — 菜单接口""" - - FIRST = 1 - NONE = 0 - CATEGORY_MASK = 0xFFFF - CATEGORY_SHIFT = 16 - FLAG_PERFORM_NO_CLOSE = 1 - FLAG_ALWAYS_PERFORM_CLOSE = 2 - USER_MASK = 0x0000FFFF - USER_SHIFT = 0 - CATEGORY_CONTAINER = 0x00010000 - CATEGORY_SYSTEM = 0x00020000 - CATEGORY_SECONDARY = 0x00030000 - CATEGORY_ALTERNATIVE = 0x00040000 - CATEGORY_APP = 0x00100000 - - def add(self, title: str) -> "MenuItem": - return MenuItem(self, 0, 0, 0, title) - - def add(self, group_id: int, item_id: int, order: int, title: str) -> "MenuItem": - return MenuItem(self, group_id, item_id, order, title) - - def addIntentOptions(self, group_id: int, item_id: int, order: int, - caller: Any, specific: Any, intent: Any, - flags: int, out_specific_items: List[Any]) -> int: - return 0 - - def addSubMenu(self, title: str) -> "SubMenu": - return SubMenu(self) - - def addSubMenu(self, group_id: int, item_id: int, order: int, - title: str) -> "SubMenu": - return SubMenu(self) - - def clear(self) -> None: - pass - - def removeGroup(self, group_id: int) -> None: - pass - - def removeItem(self, id_val: int) -> None: - pass - - def setGroupCheckable(self, group: int, checkable: bool, - exclusive: bool) -> None: - pass - - def setGroupEnabled(self, group: int, enabled: bool) -> None: - pass - - def setGroupVisible(self, group: int, visible: bool) -> None: - pass - - def findItem(self, id_val: int) -> Optional["MenuItem"]: - return None - - def size(self) -> int: - return 0 - - def getItem(self, index: int) -> "MenuItem": - return MenuItem(self, 0, 0, 0, "") - - def close(self) -> None: - pass - - def performIdentifierAction(self, id_val: int, flags: int) -> bool: - return True - - def performShortcut(self, key_code: int, event: Any, flags: int) -> bool: - return True - - def isShortcutKey(self, key_code: int, event: Any) -> bool: - return False - - def hasVisibleItems(self) -> bool: - return False - - def onMenuAction(self, id_val: int) -> bool: - return True - - def toString(self) -> str: - return "Menu()" - - -class MenuItem(Stub): - """MenuItem — 菜单项""" - - SHOW_AS_ACTION_ALWAYS = 2 - SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8 - SHOW_AS_ACTION_IF_ROOM = 1 - SHOW_AS_ACTION_NEVER = 0 - SHOW_AS_ACTION_WITH_TEXT = 4 - - def __init__(self, menu: Menu = None, group_id: int = 0, - item_id: int = 0, order: int = 0, title: str = ""): - super().__init__() - self._menu = menu - self._item_id = item_id - self._group_id = group_id - self._order = order - self._title = title - self._title_condensed: Optional[str] = None - self._icon: Optional[Any] = None - self._intent: Optional[Any] = None - self._checked = False - self._checkable = False - self._visible = True - self._enabled = True - self._action_view: Optional[Any] = None - self._on_click: Optional[Callable] = None - self._show_as_action = 0 - - def getItemId(self) -> int: - return self._item_id - - def getGroupId(self) -> int: - return self._group_id - - def getOrder(self) -> int: - return self._order - - def getTitle(self) -> str: - return self._title - - def setTitle(self, title: str) -> "MenuItem": - self._title = title - return self - - def getTitleCondensed(self) -> Optional[str]: - return self._title_condensed - - def setTitleCondensed(self, title: str) -> "MenuItem": - self._title_condensed = title - return self - - def getIcon(self) -> Optional[Any]: - return self._icon - - def setIcon(self, icon: Any) -> "MenuItem": - self._icon = icon - return self - - def getIntent(self) -> Optional[Any]: - return self._intent - - def setIntent(self, intent: Any) -> "MenuItem": - self._intent = intent - return self - - def isChecked(self) -> bool: - return self._checked - - def setChecked(self, checked: bool) -> "MenuItem": - self._checked = checked - return self - - def isCheckable(self) -> bool: - return self._checkable - - def setCheckable(self, checkable: bool) -> "MenuItem": - self._checkable = checkable - return self - - def isVisible(self) -> bool: - return self._visible - - def setVisible(self, visible: bool) -> "MenuItem": - self._visible = visible - return self - - def isEnabled(self) -> bool: - return self._enabled - - def setEnabled(self, enabled: bool) -> "MenuItem": - self._enabled = enabled - return self - - def setOnMenuItemClickListener(self, click: Callable) -> "MenuItem": - self._on_click = click - return self - - def getOnMenuItemClickListener(self) -> Optional[Callable]: - return self._on_click - - def setActionView(self, view: Any) -> "MenuItem": - self._action_view = view - return self - - def setActionView(self, layout_res_id: int) -> "MenuItem": - return self - - def getActionView(self) -> Optional[Any]: - return self._action_view - - def setShowAsAction(self, action_enum: int) -> None: - self._show_as_action = action_enum - - def getShowAsAction(self) -> int: - return self._show_as_action - - def expandActionView(self) -> bool: - return True - - def collapseActionView(self) -> bool: - return True - - def isActionViewExpanded(self) -> bool: - return False - - def setOnActionExpandListener(self, listener: Any) -> "MenuItem": - return self - - def hasSubMenu(self) -> bool: - return False - - def getSubMenu(self) -> Optional["SubMenu"]: - return None - - def toString(self) -> str: - return f"MenuItem(id={self._item_id}, title={self._title})" - - -class SubMenu(Menu): - """SubMenu — 子菜单""" - - def __init__(self, parent: Optional[Menu] = None): - super().__init__() - self._parent = parent - - def setHeaderTitle(self, title: str) -> "SubMenu": - return self - - def setHeaderIcon(self, icon: Any) -> "SubMenu": - return self - - def setHeaderView(self, view: Any) -> "SubMenu": - return self - - def clearHeader(self) -> None: - pass - - def setIcon(self, icon: Any) -> "SubMenu": - return self - - def getItem(self) -> MenuItem: - return MenuItem(self, 0, 0, 0, "") - - def toString(self) -> str: - return "SubMenu()" - - -class ContextMenu(Menu): - """ContextMenu — 上下文菜单""" - - class ContextMenuInfo(Stub): - """ContextMenu.ContextMenuInfo — 上下文菜单信息接口""" - pass - - def __init__(self): - super().__init__() - - def setHeaderTitle(self, title: str) -> "ContextMenu": - return self - - def setHeaderIcon(self, icon: Any) -> "ContextMenu": - return self - - def setHeaderView(self, view: Any) -> "ContextMenu": - return self - - def clearHeader(self) -> None: - pass - - def toString(self) -> str: - return "ContextMenu()" - - -# ═══════════════════════════════════════════════════════════════ -# ScaleGestureDetector / SoundEffectConstants / Surface / etc. -# ═══════════════════════════════════════════════════════════════ - -class ScaleGestureDetector(_BaseScaleGestureDetector): - """ScaleGestureDetector — 缩放手势检测器 (继承 view.ScaleGestureDetector 的所有方法)""" - - class SimpleOnScaleGestureListener(Stub): - def onScale(self, detector: "ScaleGestureDetector") -> bool: - return False - def onScaleBegin(self, detector: "ScaleGestureDetector") -> bool: - return True - def onScaleEnd(self, detector: "ScaleGestureDetector") -> None: - pass - - class OnScaleGestureListener(Stub): - def onScale(self, detector: "ScaleGestureDetector") -> bool: - return False - def onScaleBegin(self, detector: "ScaleGestureDetector") -> bool: - return True - def onScaleEnd(self, detector: "ScaleGestureDetector") -> None: - pass - - def __init__(self, context: Any = None, listener: Optional[Any] = None): - super().__init__() - self._listener = listener - self._scale_factor: float = 1.0 - self._focus_x: float = 0.0 - self._focus_y: float = 0.0 - self._current_span: float = 0.0 - self._previous_span: float = 0.0 - self._time_delta: int = 0 - self._in_progress = False - self._quick_scale_enabled = False - - def getScaleFactor(self) -> float: - return self._scale_factor - - def getFocusX(self) -> float: - return self._focus_x - - def getFocusY(self) -> float: - return self._focus_y - - def getCurrentSpan(self) -> float: - return self._current_span - - def getPreviousSpan(self) -> float: - return self._previous_span - - def getCurrentSpanX(self) -> float: - return self._current_span - - def getCurrentSpanY(self) -> float: - return self._current_span - - def getPreviousSpanX(self) -> float: - return self._previous_span - - def getPreviousSpanY(self) -> float: - return self._previous_span - - def getTimeDelta(self) -> int: - return self._time_delta - - def getEventTime(self) -> int: - return 0 - - def isInProgress(self) -> bool: - return self._in_progress - - def setQuickScaleEnabled(self, enabled: bool) -> None: - self._quick_scale_enabled = enabled - - def isQuickScaleEnabled(self) -> bool: - return self._quick_scale_enabled - - def onTouchEvent(self, ev: Any) -> bool: - return True - - def toString(self) -> str: - return (f"ScaleGestureDetector(scale={self._scale_factor:.2f}, " - f"inProgress={self._in_progress})") - - -class SoundEffectConstants(_BaseSoundEffectConstants): - """SoundEffectConstants — 音效常量 (继承 view.SoundEffectConstants 的所有方法)""" - - CLICK = 0 - NAVIGATION_UP = 1 - NAVIGATION_DOWN = 2 - NAVIGATION_LEFT = 3 - NAVIGATION_RIGHT = 4 - NAVIGATION_REPEAT_UP = 5 - NAVIGATION_REPEAT_DOWN = 6 - NAVIGATION_REPEAT_LEFT = 7 - NAVIGATION_REPEAT_RIGHT = 8 - - @staticmethod - def getContantForFocusDirection(direction: int) -> int: - return SoundEffectConstants.NAVIGATION_UP - - -class Surface(_BaseSurface): - """Surface — 表面 (继承 view.Surface 的所有方法)""" - - ROTATION_0 = 0 - ROTATION_90 = 1 - ROTATION_180 = 2 - ROTATION_270 = 3 - - def __init__(self, surface_control: Optional["SurfaceControl"] = None): - super().__init__() - self._surface_control = surface_control - self._is_valid = True - self._width: int = 0 - self._height: int = 0 - - def isValid(self) -> bool: - return self._is_valid - - def lockCanvas(self, dirty_rect: Optional[Any] = None) -> Optional[Any]: - return None - - def unlockCanvasAndPost(self, canvas: Any) -> None: - pass - - def getSurfaceControl(self) -> Optional["SurfaceControl"]: - return self._surface_control - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def toString(self) -> str: - return f"Surface(valid={self._is_valid})" - - -class SurfaceControl(Stub): - """SurfaceControl — 表面控制""" - - BUFFER_TRANSFORM_IDENTITY = 0 - BUFFER_TRANSFORM_MIRROR_H = 1 - BUFFER_TRANSFORM_MIRROR_V = 2 - BUFFER_TRANSFORM_ROTATE_90 = 4 - BUFFER_TRANSFORM_ROTATE_180 = 3 - BUFFER_TRANSFORM_ROTATE_270 = 7 - - def __init__(self): - super().__init__() - self._surface: Optional[Surface] = None - self._visible = False - self._alpha: float = 1.0 - self._layer: int = 0 - - def createSurface(self) -> Surface: - self._surface = Surface(self) - return self._surface - - def setAlpha(self, alpha: float) -> None: - self._alpha = max(0.0, min(1.0, alpha)) - - def setLayer(self, layer: int) -> None: - self._layer = layer - - def setVisible(self, visible: bool) -> None: - self._visible = visible - - def release(self) -> None: - self._surface = None - - def toString(self) -> str: - return f"SurfaceControl(layer={self._layer}, visible={self._visible})" - - -class SurfaceView(_BaseSurfaceView): - """SurfaceView — 表面视图扩展 (继承 view.SurfaceView 的所有方法)""" - - def __init__(self, context: Any = None): - super().__init__() - self._surface: Optional[Surface] = None - self._surface_holder: Optional[Any] = None - self._callback: Optional[Callable] = None - - def getHolder(self) -> Optional[Any]: - return self._surface_holder - - def getSurfaceControl(self) -> Optional[SurfaceControl]: - return None - - def setSecure(self, secure: bool) -> None: - pass - - def setZOrderOnTop(self, on_top: bool) -> None: - pass - - def setZOrderMediaOverlay(self, is_media_overlay: bool) -> None: - pass - - def toString(self) -> str: - return "SurfaceView()" - - -class TextureView(_BaseTextureView): - """TextureView — 纹理视图扩展 (继承 view.TextureView 的所有方法)""" - - def __init__(self, context: Any = None): - super().__init__() - self._surface_texture: Optional[Any] = None - self._callback: Optional[Callable] = None - self._update_listener: Optional[Callable] = None - - def getSurfaceTexture(self) -> Optional[Any]: - return self._surface_texture - - def setSurfaceTextureListener(self, listener: Optional[Callable]) -> None: - self._update_listener = listener - - def isAvailable(self) -> bool: - return self._surface_texture is not None - - def setOpaque(self, opaque: bool) -> None: - pass - - def getBitmap(self) -> Optional[Any]: - return None - - def toString(self) -> str: - return "TextureView()" - - -class TouchDelegate(Stub): - """TouchDelegate — 触摸委托""" - - def __init__(self, bounds: Any, delegate_view: Any): - super().__init__() - self._bounds = bounds - self._delegate_view = delegate_view - self._touch_slop: int = 8 - - def onTouchEvent(self, event: Any) -> bool: - return False - - def toString(self) -> str: - return f"TouchDelegate(delegate={self._delegate_view})" - - -class VelocityTracker(_BaseVelocityTracker): - """VelocityTracker — 速度追踪器 (继承 view.VelocityTracker 的所有方法)""" - - def __init__(self, strategy: str = ""): - super().__init__() - self._positions: List[Tuple[float, float, int]] = [] - self._strategy = strategy - - @staticmethod - def obtain(strategy: str = "") -> "VelocityTracker": - return VelocityTracker(strategy) - - def addMovement(self, event: Any) -> None: - pass - - def computeCurrentVelocity(self, units: int, max_velocity: float = 10000.0) -> None: - pass - - def getXVelocity(self) -> float: - return 0.0 - - def getYVelocity(self) -> float: - return 0.0 - - def getXVelocity(self, id_val: int) -> float: - return 0.0 - - def getYVelocity(self, id_val: int) -> float: - return 0.0 - - def clear(self) -> None: - self._positions.clear() - - def recycle(self) -> None: - self._positions.clear() - - def toString(self) -> str: - return f"VelocityTracker(strategy={self._strategy})" - - -class WindowInsetsAnimation(Stub): - """WindowInsetsAnimation — 窗口插入动画""" - - def __init__(self, type_mask: int = 0, interpolated_fraction: float = 0.0): - super().__init__() - self._type_mask = type_mask - self._interpolated_fraction = interpolated_fraction - self._duration: int = 0 - self._interpolator: Optional[Any] = None - - def getTypeMask(self) -> int: - return self._type_mask - - def getInterpolatedFraction(self) -> float: - return self._interpolated_fraction - - def getDurationMillis(self) -> float: - return float(self._duration) - - def setDuration(self, duration: int) -> None: - self._duration = duration - - def setInterpolator(self, interpolator: Any) -> None: - self._interpolator = interpolator - - def getInterpolator(self) -> Optional[Any]: - return self._interpolator - - class Callback(Stub): - """WindowInsetsAnimation.Callback — 动画回调""" - - DISPATCH_MODE_STOP = 0 - DISPATCH_MODE_CONTINUE_ON_SUBTREE = 1 - - def __init__(self, dispatch_mode: int = DISPATCH_MODE_CONTINUE_ON_SUBTREE): - super().__init__() - self._dispatch_mode = dispatch_mode - - def onPrepare(self, animation: "WindowInsetsAnimation") -> None: - pass - - def onStart(self, animation: "WindowInsetsAnimation", - bounds: WindowInsetsAnimation.Bounds) -> None: - pass - - def onProgress(self, insets: WindowInsets, - running_animations: List["WindowInsetsAnimation"]) -> WindowInsets: - return insets - - def onEnd(self, animation: "WindowInsetsAnimation") -> None: - pass - - class Bounds(Stub): - """WindowInsetsAnimation.Bounds — 动画边界""" - - def __init__(self, lower_bound: WindowInsets, upper_bound: WindowInsets): - super().__init__() - self._lower_bound = lower_bound - self._upper_bound = upper_bound - - def getLowerBound(self) -> WindowInsets: - return self._lower_bound - - def getUpperBound(self) -> WindowInsets: - return self._upper_bound - - def inset(self, insets: WindowInsets) -> "Bounds": - return self - - def toString(self) -> str: - return "WindowInsetsAnimation.Bounds()" - - def toString(self) -> str: - return (f"WindowInsetsAnimation(typeMask={self._type_mask}, " - f"fraction={self._interpolated_fraction})") - - -class WindowInsetsController(_BaseWindowInsetsController): - """WindowInsetsController — 窗口插入控制器 (继承 view.WindowInsetsController 的所有方法)""" - - APPEARANCE_OPAQUE_STATUS_BARS = 1 - APPEARANCE_OPAQUE_NAVIGATION_BARS = 2 - APPEARANCE_LOW_PROFILE_BARS = 4 - APPEARANCE_LIGHT_STATUS_BARS = 8 - APPEARANCE_LIGHT_NAVIGATION_BARS = 16 - APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS = 32 - APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS = 64 - - BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 0 - BEHAVIOR_DEFAULT = 1 - BEHAVIOR_SHOW_BARS_BY_TOUCH = 2 - BEHAVIOR_SHOW_BARS_BY_SWIPE = 3 - - def __init__(self): - super().__init__() - self._appearance: int = 0 - self._behavior: int = self.BEHAVIOR_DEFAULT - self._show_types: int = 0 - self._hide_types: int = 0 - - def show(self, types: int) -> None: - self._show_types |= types - - def hide(self, types: int) -> None: - self._hide_types |= types - - def setAppearance(self, appearance: int) -> None: - self._appearance = appearance - - def getAppearance(self) -> int: - return self._appearance - - def setBehavior(self, behavior: int) -> None: - self._behavior = behavior - - def getBehavior(self) -> int: - return self._behavior - - def setSystemBarsAppearance(self, appearance: int, mask: int) -> None: - self._appearance = (self._appearance & ~mask) | (appearance & mask) - - def getSystemBarsAppearance(self) -> int: - return self._appearance - - def toString(self) -> str: - return (f"WindowInsetsController(show={self._show_types}, " - f"hide={self._hide_types})") \ No newline at end of file diff --git a/android_pyapi/android/view_gesture.py b/android_pyapi/android/view_gesture.py deleted file mode 100644 index e798fdc..0000000 --- a/android_pyapi/android/view_gesture.py +++ /dev/null @@ -1,647 +0,0 @@ -""" -Android View Gesture 扩展组件 — GestureBuilder, GestureLibrary, GestureStore 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import math -import time -import json -import os -import threading -from typing import Any, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# GesturePoint -# ═══════════════════════════════════════════════════════════════ - -class GesturePoint(Stub): - """GesturePoint — 手势点""" - - def __init__(self, x: float = 0.0, y: float = 0.0, - pressure: float = 1.0, size: float = 1.0, - timestamp: int = 0): - super().__init__() - self.x = x - self.y = y - self.pressure = pressure - self.size = size - self.timestamp = timestamp or int(time.time() * 1000) - - def getX(self) -> float: - return self.x - - def getY(self) -> float: - return self.y - - def getPressure(self) -> float: - return self.pressure - - def getSize(self) -> float: - return self.size - - def getTimestamp(self) -> int: - return self.timestamp - - def distance(self, other: "GesturePoint") -> float: - dx = self.x - other.x - dy = self.y - other.y - return math.sqrt(dx * dx + dy * dy) - - def toString(self) -> str: - return (f"GesturePoint({self.x:.1f}, {self.y:.1f}, " - f"p={self.pressure:.2f})") - - -# ═══════════════════════════════════════════════════════════════ -# GestureStroke -# ═══════════════════════════════════════════════════════════════ - -class GestureStroke(Stub): - """GestureStroke — 手势笔画""" - - def __init__(self, points: Optional[List[GesturePoint]] = None): - super().__init__() - self._points: List[GesturePoint] = list(points) if points else [] - self._length: float = -1.0 - self._bounding_box: Optional[Tuple[float, float, float, float]] = None - self._straightness: Optional[float] = None - self._lock = threading.Lock() - - def getPoints(self) -> List[GesturePoint]: - return list(self._points) - - def getPointCount(self) -> int: - return len(self._points) - - def getLength(self) -> float: - with self._lock: - if self._length < 0: - self._length = self._compute_length() - return self._length - - def _compute_length(self) -> float: - length = 0.0 - for i in range(1, len(self._points)): - length += self._points[i].distance(self._points[i - 1]) - return length - - def getBoundingBox(self) -> Tuple[float, float, float, float]: - with self._lock: - if self._bounding_box is None: - self._bounding_box = self._compute_bounding_box() - return self._bounding_box - - def _compute_bounding_box(self) -> Tuple[float, float, float, float]: - if not self._points: - return (0.0, 0.0, 0.0, 0.0) - min_x = min(p.x for p in self._points) - max_x = max(p.x for p in self._points) - min_y = min(p.y for p in self._points) - max_y = max(p.y for p in self._points) - return (min_x, min_y, max_x, max_y) - - def getStraightness(self) -> float: - with self._lock: - if self._straightness is None: - self._straightness = self._compute_straightness() - return self._straightness - - def _compute_straightness(self) -> float: - if len(self._points) < 2: - return 1.0 - dx = self._points[-1].x - self._points[0].x - dy = self._points[-1].y - self._points[0].y - straight_dist = math.sqrt(dx * dx + dy * dy) - actual_dist = self.getLength() - if actual_dist == 0: - return 1.0 - return straight_dist / actual_dist - - def computeOrientedBoundingBox(self) -> "OrientedBoundingBox": - return OrientedBoundingBox.computeFromStroke(self) - - def addPoint(self, point: GesturePoint) -> None: - with self._lock: - self._points.append(point) - self._length = -1.0 - self._bounding_box = None - - def clear(self) -> None: - with self._lock: - self._points.clear() - self._length = -1.0 - self._bounding_box = None - - def toString(self) -> str: - return f"GestureStroke(points={len(self._points)}, length={self.getLength():.1f})" - - -# ═══════════════════════════════════════════════════════════════ -# OrientedBoundingBox -# ═══════════════════════════════════════════════════════════════ - -class OrientedBoundingBox(Stub): - """OrientedBoundingBox — 定向边界框""" - - def __init__(self, center_x: float = 0.0, center_y: float = 0.0, - width: float = 0.0, height: float = 0.0, - orientation: float = 0.0): - super().__init__() - self.centerX = center_x - self.centerY = center_y - self.width = width - self.height = height - self.orientation = orientation - - @staticmethod - def computeFromStroke(stroke: GestureStroke) -> "OrientedBoundingBox": - points = stroke.getPoints() - if len(points) < 2: - return OrientedBoundingBox() - - sum_x = sum(p.x for p in points) - sum_y = sum(p.y for p in points) - n = len(points) - center_x = sum_x / n - center_y = sum_y / n - - variance_x = sum((p.x - center_x) ** 2 for p in points) / n - variance_y = sum((p.y - center_y) ** 2 for p in points) / n - covariance = sum((p.x - center_x) * (p.y - center_y) for p in points) / n - - if variance_x + variance_y == 0: - return OrientedBoundingBox(center_x, center_y, 1, 1, 0) - - theta = 0.5 * math.atan2(2 * covariance, variance_x - variance_y) - cos_t = math.cos(theta) - sin_t = math.sin(theta) - - max_proj1 = max_proj2 = float("-inf") - min_proj1 = min_proj2 = float("inf") - - for p in points: - proj1 = (p.x - center_x) * cos_t + (p.y - center_y) * sin_t - proj2 = -(p.x - center_x) * sin_t + (p.y - center_y) * cos_t - max_proj1 = max(max_proj1, proj1) - min_proj1 = min(min_proj1, proj1) - max_proj2 = max(max_proj2, proj2) - min_proj2 = min(min_proj2, proj2) - - width = max_proj1 - min_proj1 - height = max_proj2 - min_proj2 - orientation = math.degrees(theta) - - return OrientedBoundingBox(center_x, center_y, width, height, orientation) - - def getCenterX(self) -> float: - return self.centerX - - def getCenterY(self) -> float: - return self.centerY - - def getWidth(self) -> float: - return self.width - - def getHeight(self) -> float: - return self.height - - def getOrientation(self) -> float: - return self.orientation - - def toString(self) -> str: - return (f"OrientedBoundingBox(center=({self.centerX:.1f},{self.centerY:.1f}), " - f"size=({self.width:.1f}x{self.height:.1f}), " - f"angle={self.orientation:.1f})") - - -# ═══════════════════════════════════════════════════════════════ -# GestureUtils -# ═══════════════════════════════════════════════════════════════ - -class GestureUtils(Stub): - """GestureUtils — 手势工具""" - - MINIMUM_STROKE_LENGTH = 50.0 - MAXIMUM_STROKE_LENGTH = 10000.0 - - @staticmethod - def spatialSampling(gesture: "Gesture", num_samples: int = 16) -> List[float]: - """空间采样 — 将手势转换为固定长度的向量""" - result = [] - strokes = gesture.getStrokes() - if not strokes: - return [0.0] * (num_samples * 2) - - all_points = [] - for stroke in strokes: - all_points.extend(stroke.getPoints()) - - if len(all_points) < 2: - return [0.0] * (num_samples * 2) - - total_length = sum( - all_points[i].distance(all_points[i - 1]) - for i in range(1, len(all_points)) - ) - - if total_length == 0: - return [all_points[0].x, all_points[0].y] * num_samples - - step = total_length / (num_samples - 1) - current_dist = 0.0 - target_dist = 0.0 - result.append(all_points[0].x) - result.append(all_points[0].y) - - for i in range(1, len(all_points)): - seg_length = all_points[i].distance(all_points[i - 1]) - if seg_length == 0: - continue - current_dist += seg_length - while target_dist < current_dist and len(result) < num_samples * 2: - ratio = 1.0 - (current_dist - target_dist) / seg_length - x = all_points[i - 1].x + ratio * (all_points[i].x - all_points[i - 1].x) - y = all_points[i - 1].y + ratio * (all_points[i].y - all_points[i - 1].y) - result.append(x) - result.append(y) - target_dist += step - - while len(result) < num_samples * 2: - result.append(all_points[-1].x) - result.append(all_points[-1].y) - - return result[:num_samples * 2] - - @staticmethod - def temporalSampling(gesture: "Gesture", num_samples: int = 16) -> List[float]: - return GestureUtils.spatialSampling(gesture, num_samples) - - @staticmethod - def computeOrientedBoundingBox(stroke: GestureStroke) -> OrientedBoundingBox: - return OrientedBoundingBox.computeFromStroke(stroke) - - @staticmethod - def normalize(points: List[float]) -> List[float]: - if not points: - return [] - xs = points[0::2] - ys = points[1::2] - min_x = min(xs) - max_x = max(xs) - min_y = min(ys) - max_y = max(ys) - range_x = max_x - min_x if max_x != min_x else 1 - range_y = max_y - min_y if max_y != min_y else 1 - - normalized = [] - for i in range(0, len(points), 2): - normalized.append((points[i] - min_x) / range_x) - normalized.append((points[i + 1] - min_y) / range_y) - return normalized - - @staticmethod - def scale(points: List[float], width: float, height: float) -> List[float]: - normalized = GestureUtils.normalize(points) - scaled = [] - for i in range(0, len(normalized), 2): - scaled.append(normalized[i] * width) - scaled.append(normalized[i + 1] * height) - return scaled - - @staticmethod - def totalLength(points: List[float]) -> float: - length = 0.0 - for i in range(2, len(points), 2): - dx = points[i] - points[i - 2] - dy = points[i + 1] - points[i - 1] - length += math.sqrt(dx * dx + dy * dy) - return length - - @staticmethod - def cosineDistance(vector1: List[float], vector2: List[float]) -> float: - if len(vector1) != len(vector2) or not vector1: - return 1.0 - dot = sum(a * b for a, b in zip(vector1, vector2)) - norm1 = math.sqrt(sum(a * a for a in vector1)) - norm2 = math.sqrt(sum(b * b for b in vector2)) - if norm1 == 0 or norm2 == 0: - return 1.0 - return 1.0 - dot / (norm1 * norm2) - - @staticmethod - def euclideanDistance(vector1: List[float], vector2: List[float]) -> float: - if len(vector1) != len(vector2): - return float("inf") - return math.sqrt(sum((a - b) ** 2 for a, b in zip(vector1, vector2))) - - -# ═══════════════════════════════════════════════════════════════ -# Gesture -# ═══════════════════════════════════════════════════════════════ - -class Gesture(Stub): - """Gesture — 手势""" - - def __init__(self): - super().__init__() - self._strokes: List[GestureStroke] = [] - self._id: int = 0 - self._name: str = "" - self._lock = threading.Lock() - - def addStroke(self, stroke: GestureStroke) -> None: - with self._lock: - self._strokes.append(stroke) - - def getStrokes(self) -> List[GestureStroke]: - return list(self._strokes) - - def getStrokeCount(self) -> int: - return len(self._strokes) - - def getLength(self) -> float: - total = 0.0 - for stroke in self._strokes: - total += stroke.getLength() - return total - - def getBoundingBox(self) -> Tuple[float, float, float, float]: - if not self._strokes: - return (0, 0, 0, 0) - min_x = min(s.getBoundingBox()[0] for s in self._strokes) - min_y = min(s.getBoundingBox()[1] for s in self._strokes) - max_x = max(s.getBoundingBox()[2] for s in self._strokes) - max_y = max(s.getBoundingBox()[3] for s in self._strokes) - return (min_x, min_y, max_x, max_y) - - def getID(self) -> int: - return self._id - - def setID(self, id_val: int) -> None: - self._id = id_val - - def getName(self) -> str: - return self._name - - def setName(self, name: str) -> None: - self._name = name - - def toJson(self) -> str: - data = { - "id": self._id, - "name": self._name, - "strokes": [] - } - for stroke in self._strokes: - stroke_data = { - "points": [ - {"x": p.x, "y": p.y, "pressure": p.pressure, - "size": p.size, "timestamp": p.timestamp} - for p in stroke.getPoints() - ] - } - data["strokes"].append(stroke_data) - return json.dumps(data) - - @staticmethod - def fromJson(json_str: str) -> "Gesture": - data = json.loads(json_str) - gesture = Gesture() - gesture.setID(data.get("id", 0)) - gesture.setName(data.get("name", "")) - for stroke_data in data.get("strokes", []): - points = [ - GesturePoint(p["x"], p["y"], p.get("pressure", 1.0), - p.get("size", 1.0), p.get("timestamp", 0)) - for p in stroke_data.get("points", []) - ] - gesture.addStroke(GestureStroke(points)) - return gesture - - def toString(self) -> str: - return f"Gesture(name={self._name}, strokes={len(self._strokes)})" - - -# ═══════════════════════════════════════════════════════════════ -# Prediction -# ═══════════════════════════════════════════════════════════════ - -class Prediction(Stub): - """Prediction — 手势识别预测结果""" - - def __init__(self, name: str = "", score: float = 0.0): - super().__init__() - self.name = name - self.score = score - - def getName(self) -> str: - return self.name - - def getScore(self) -> float: - return self.score - - def toString(self) -> str: - return f"Prediction(name={self.name}, score={self.score:.3f})" - - -# ═══════════════════════════════════════════════════════════════ -# GestureLibrary / GestureStore -# ═══════════════════════════════════════════════════════════════ - -class GestureLibrary(Stub): - """GestureLibrary — 手势库扩展""" - - SEQUENCE_MINIMUM_LENGTH = 2 - - def __init__(self): - super().__init__() - self._gestures: Dict[str, List[Gesture]] = {} - self._lock = threading.Lock() - - def getGestureEntries(self) -> Set[str]: - with self._lock: - return set(self._gestures.keys()) - - def getGestures(self, entry_name: str) -> List[Gesture]: - with self._lock: - return list(self._gestures.get(entry_name, [])) - - def recognize(self, gesture: Gesture) -> List[Prediction]: - """识别手势,返回预测结果列表""" - results: List[Prediction] = [] - with self._lock: - for name, gestures in self._gestures.items(): - for g in gestures: - score = self._compute_similarity(gesture, g) - results.append(Prediction(name, score)) - results.sort(key=lambda p: p.score, reverse=True) - return results[:10] - - def _compute_similarity(self, g1: Gesture, g2: Gesture) -> float: - """计算两个手势之间的相似度 (0.0 - 1.0)""" - try: - v1 = GestureUtils.spatialSampling(g1, 16) - v2 = GestureUtils.spatialSampling(g2, 16) - distance = GestureUtils.euclideanDistance(v1, v2) - max_dist = math.sqrt(len(v1)) - similarity = 1.0 - (distance / max_dist) - return max(0.0, min(1.0, similarity)) - except Exception: - return 0.0 - - def addGesture(self, entry_name: str, gesture: Gesture) -> None: - with self._lock: - if entry_name not in self._gestures: - self._gestures[entry_name] = [] - gesture.setName(entry_name) - self._gestures[entry_name].append(gesture) - - def removeGesture(self, entry_name: str, gesture: Gesture) -> None: - with self._lock: - if entry_name in self._gestures: - if gesture in self._gestures[entry_name]: - self._gestures[entry_name].remove(gesture) - - def removeEntry(self, entry_name: str) -> None: - with self._lock: - self._gestures.pop(entry_name, None) - - def getGestureCount(self) -> int: - count = 0 - with self._lock: - for gestures in self._gestures.values(): - count += len(gestures) - return count - - def getEntryCount(self) -> int: - return len(self._gestures) - - def save(self, path: str) -> bool: - try: - data = {} - with self._lock: - for name, gestures in self._gestures.items(): - data[name] = [g.toJson() for g in gestures] - with open(path, "w") as f: - json.dump(data, f) - return True - except Exception: - return False - - def load(self, path: str) -> bool: - try: - if not os.path.exists(path): - return False - with open(path, "r") as f: - data = json.load(f) - with self._lock: - self._gestures.clear() - for name, gesture_jsons in data.items(): - self._gestures[name] = [ - Gesture.fromJson(gj) for gj in gesture_jsons - ] - return True - except Exception: - return False - - def clear(self) -> None: - with self._lock: - self._gestures.clear() - - def isReadOnly(self) -> bool: - return False - - def toString(self) -> str: - return f"GestureLibrary(entries={len(self._gestures)})" - - -class GestureStore(GestureLibrary): - """GestureStore — 手势存储扩展""" - - SEQUENCE_TYPE_INVARIANT = 1 - SEQUENCE_TYPE_SENSITIVE = 2 - ORIENTATION_INVARIANT = 1 - ORIENTATION_SENSITIVE = 2 - - def __init__(self): - super().__init__() - self._sequence_type = self.SEQUENCE_TYPE_SENSITIVE - self._orientation_style = self.ORIENTATION_INVARIANT - - def setSequenceType(self, type_val: int) -> None: - self._sequence_type = type_val - - def getSequenceType(self) -> int: - return self._sequence_type - - def setOrientationStyle(self, style: int) -> None: - self._orientation_style = style - - def getOrientationStyle(self) -> int: - return self._orientation_style - - def toString(self) -> str: - return f"GestureStore(entries={self.getEntryCount()})" - - -class GestureBuilder(Stub): - """GestureBuilder — 手势构建器""" - - MIN_LENGTH = 10.0 - MAX_LENGTH = 500.0 - - def __init__(self): - super().__init__() - self._current_gesture: Optional[Gesture] = None - self._current_stroke: Optional[GestureStroke] = None - self._current_points: List[GesturePoint] = [] - - def newGesture(self) -> Gesture: - gesture = Gesture() - self._current_gesture = gesture - self._current_stroke = None - self._current_points = [] - return gesture - - def startStroke(self, x: float, y: float, pressure: float = 1.0) -> None: - self._current_points = [] - point = GesturePoint(x, y, pressure, 1.0, int(time.time() * 1000)) - self._current_points.append(point) - - def moveStroke(self, x: float, y: float, pressure: float = 1.0) -> None: - point = GesturePoint(x, y, pressure, 1.0, int(time.time() * 1000)) - self._current_points.append(point) - - def endStroke(self) -> GestureStroke: - stroke = GestureStroke(self._current_points) - self._current_stroke = stroke - if self._current_gesture: - self._current_gesture.addStroke(stroke) - self._current_points = [] - return stroke - - def cancelStroke(self) -> None: - self._current_points = [] - self._current_stroke = None - - def getCurrentGesture(self) -> Optional[Gesture]: - return self._current_gesture - - def getCurrentStroke(self) -> Optional[GestureStroke]: - return self._current_stroke - - def setOrientationStyle(self, style: int) -> None: - pass - - def setSequenceType(self, type_val: int) -> None: - pass - - def toString(self) -> str: - return (f"GestureBuilder(gesture={'yes' if self._current_gesture else 'no'}, " - f"strokePoints={len(self._current_points)})") \ No newline at end of file diff --git a/android_pyapi/android/view_haptic.py b/android_pyapi/android/view_haptic.py deleted file mode 100644 index d8b3a81..0000000 --- a/android_pyapi/android/view_haptic.py +++ /dev/null @@ -1,381 +0,0 @@ -""" -Android View Haptic 组件 — HapticFeedbackConstants, View 触觉扩展 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import threading -from typing import Any, Callable, Dict, List, Optional - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# HapticFeedbackConstants -# ═══════════════════════════════════════════════════════════════ - -class HapticFeedbackConstants(Stub): - """HapticFeedbackConstants — 触觉反馈常量""" - - LONG_PRESS = 0 - TEXT_HANDLE_MOVE = 12 - VIRTUAL_KEY = 1 - VIRTUAL_KEY_RELEASE = 2 - CLOCK_TICK = 4 - KEYBOARD_PRESS = 3 - KEYBOARD_RELEASE = 13 - KEYBOARD_TAP = 3 - CONFIRM = 5 - REJECT = 6 - GESTURE_START = 7 - GESTURE_END = 8 - EDGE_REJECT = 9 - DRAG_CROSS = 10 - SEGMENT_FREQUENT_TICK = 11 - SEGMENT_TICK = 14 - SEGMENT_TICK_VERY_SMALL = 15 - SEGMENT_TICK_SMALL = 16 - SEGMENT_TICK_MEDIUM = 17 - SEGMENT_TICK_LARGE = 18 - ENTRY_BUMP = 19 - EXIT_BUMP = 20 - REORDER = 21 - CONTEXT_CLICK = 22 - - FLAG_IGNORE_VIEW_SETTING = 1 - FLAG_IGNORE_GLOBAL_SETTING = 2 - - _is_enabled = True - _global_enabled = True - _lock = threading.Lock() - - def __init__(self): - super().__init__() - - @classmethod - def isEnabled(cls) -> bool: - with cls._lock: - return cls._is_enabled and cls._global_enabled - - @classmethod - def setEnabled(cls, enabled: bool) -> None: - with cls._lock: - cls._is_enabled = enabled - - @classmethod - def setGlobalEnabled(cls, enabled: bool) -> None: - with cls._lock: - cls._global_enabled = enabled - - @classmethod - def isGlobalEnabled(cls) -> bool: - with cls._lock: - return cls._global_enabled - - @classmethod - def toString(cls) -> str: - return (f"HapticFeedbackConstants(enabled={cls._is_enabled}, " - f"global={cls._global_enabled})") - - @classmethod - def getFeedbackConstantName(cls, feedback_constant: int) -> str: - names = { - 0: "LONG_PRESS", 12: "TEXT_HANDLE_MOVE", 1: "VIRTUAL_KEY", - 2: "VIRTUAL_KEY_RELEASE", 4: "CLOCK_TICK", 3: "KEYBOARD_PRESS", - 13: "KEYBOARD_RELEASE", 5: "CONFIRM", 6: "REJECT", - 7: "GESTURE_START", 8: "GESTURE_END", 9: "EDGE_REJECT", - 10: "DRAG_CROSS", 11: "SEGMENT_FREQUENT_TICK", - 14: "SEGMENT_TICK", 15: "SEGMENT_TICK_VERY_SMALL", - 16: "SEGMENT_TICK_SMALL", 17: "SEGMENT_TICK_MEDIUM", - 18: "SEGMENT_TICK_LARGE", 19: "ENTRY_BUMP", 20: "EXIT_BUMP", - 21: "REORDER", 22: "CONTEXT_CLICK", - } - return names.get(feedback_constant, f"UNKNOWN({feedback_constant})") - - -# ═══════════════════════════════════════════════════════════════ -# View Haptic 方法扩展 -# ═══════════════════════════════════════════════════════════════ - -class ViewHapticExtensions(Stub): - """ViewHapticExtensions — 视图触觉扩展方法""" - - def __init__(self, view: Any = None): - super().__init__() - self._view = view - self._haptic_enabled = True - self._haptic_feedback_enabled = True - self._exclude_gesture_region = False - self._lock = threading.Lock() - - def performHapticFeedback(self, feedback_constant: int, - flags: int = 0) -> bool: - """执行触觉反馈""" - if not self._haptic_enabled: - return False - if flags & HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING: - pass - if flags & HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING: - pass - if not HapticFeedbackConstants.isEnabled(): - return False - return self._do_haptic(feedback_constant) - - def _do_haptic(self, feedback_constant: int) -> bool: - """实际执行触觉反馈效果 (桌面模拟)""" - return True - - def setHapticFeedbackEnabled(self, enabled: bool) -> None: - with self._lock: - self._haptic_feedback_enabled = enabled - - def isHapticFeedbackEnabled(self) -> bool: - return self._haptic_feedback_enabled - - def setHapticEnabled(self, enabled: bool) -> None: - with self._lock: - self._haptic_enabled = enabled - - def isHapticEnabled(self) -> bool: - return self._haptic_enabled - - def setExcludeFromExcludeGestureRegion(self, exclude: bool) -> None: - self._exclude_gesture_region = exclude - - def isExcludedFromExcludeGestureRegion(self) -> bool: - return self._exclude_gesture_region - - def toString(self) -> str: - return (f"ViewHapticExtensions(enabled={self._haptic_enabled}, " - f"feedback={self._haptic_feedback_enabled})") - - -# ═══════════════════════════════════════════════════════════════ -# HapticPlayer (触觉播放器) -# ═══════════════════════════════════════════════════════════════ - -class HapticPlayer(Stub): - """HapticPlayer — 触觉播放器""" - - PLAY_STATUS_IDLE = 0 - PLAY_STATUS_PLAYING = 1 - PLAY_STATUS_PAUSED = 2 - PLAY_STATUS_STOPPED = 3 - - def __init__(self): - super().__init__() - self._status = self.PLAY_STATUS_IDLE - self._duration: int = 0 - self._amplitude: float = 1.0 - self._frequency: float = 150.0 - self._on_completion: Optional[Callable] = None - self._lock = threading.Lock() - - def play(self) -> None: - with self._lock: - self._status = self.PLAY_STATUS_PLAYING - - def pause(self) -> None: - with self._lock: - self._status = self.PLAY_STATUS_PAUSED - - def stop(self) -> None: - with self._lock: - self._status = self.PLAY_STATUS_STOPPED - if self._on_completion: - self._on_completion() - - def setDuration(self, duration_ms: int) -> None: - self._duration = max(0, duration_ms) - - def getDuration(self) -> int: - return self._duration - - def setAmplitude(self, amplitude: float) -> None: - self._amplitude = max(0.0, min(1.0, amplitude)) - - def getAmplitude(self) -> float: - return self._amplitude - - def setFrequency(self, frequency_hz: float) -> None: - self._frequency = max(20.0, min(1000.0, frequency_hz)) - - def getFrequency(self) -> float: - return self._frequency - - def setOnCompletionListener(self, listener: Optional[Callable]) -> None: - self._on_completion = listener - - def getStatus(self) -> int: - return self._status - - def isPlaying(self) -> bool: - return self._status == self.PLAY_STATUS_PLAYING - - def toString(self) -> str: - return (f"HapticPlayer(duration={self._duration}ms, " - f"amplitude={self._amplitude:.2f}, " - f"freq={self._frequency:.0f}Hz)") - - -# ═══════════════════════════════════════════════════════════════ -# HapticFeedbackProvider -# ═══════════════════════════════════════════════════════════════ - -class HapticFeedbackProvider(Stub): - """HapticFeedbackProvider — 触觉反馈提供者""" - - def __init__(self): - super().__init__() - self._players: Dict[int, HapticPlayer] = {} - self._default_duration: Dict[int, int] = { - HapticFeedbackConstants.LONG_PRESS: 50, - HapticFeedbackConstants.VIRTUAL_KEY: 10, - HapticFeedbackConstants.KEYBOARD_TAP: 5, - HapticFeedbackConstants.CONFIRM: 30, - HapticFeedbackConstants.REJECT: 40, - HapticFeedbackConstants.GESTURE_START: 20, - HapticFeedbackConstants.GESTURE_END: 15, - HapticFeedbackConstants.CLOCK_TICK: 5, - } - self._enabled = True - - def getPlayer(self, feedback_constant: int) -> HapticPlayer: - if feedback_constant not in self._players: - player = HapticPlayer() - duration = self._default_duration.get(feedback_constant, 20) - player.setDuration(duration) - self._players[feedback_constant] = player - return self._players[feedback_constant] - - def perform(self, feedback_constant: int, flags: int = 0) -> bool: - if not self._enabled: - return False - player = self.getPlayer(feedback_constant) - player.play() - return True - - def cancel(self, feedback_constant: int) -> None: - if feedback_constant in self._players: - self._players[feedback_constant].stop() - - def cancelAll(self) -> None: - for player in self._players.values(): - player.stop() - - def setEnabled(self, enabled: bool) -> None: - self._enabled = enabled - - def isEnabled(self) -> bool: - return self._enabled - - def toString(self) -> str: - return f"HapticFeedbackProvider(players={len(self._players)})" - - -# ═══════════════════════════════════════════════════════════════ -# HapticPattern / HapticComposer -# ═══════════════════════════════════════════════════════════════ - -class HapticPattern(Stub): - """HapticPattern — 触觉模式""" - - def __init__(self): - super().__init__() - self._segments: List[HapticPattern.Segment] = [] - - class Segment(Stub): - """HapticPattern.Segment — 触觉片段""" - - TYPE_PAUSE = 0 - TYPE_VIBRATION = 1 - - def __init__(self, type_val: int = TYPE_VIBRATION, - duration_ms: int = 0, amplitude: float = 1.0): - super().__init__() - self._type = type_val - self._duration = duration_ms - self._amplitude = amplitude - - def getType(self) -> int: - return self._type - - def getDuration(self) -> int: - return self._duration - - def getAmplitude(self) -> float: - return self._amplitude - - def toString(self) -> str: - return (f"Segment(type={'VIBRATION' if self._type == 1 else 'PAUSE'}, " - f"duration={self._duration}, amplitude={self._amplitude:.2f})") - - def addSegment(self, segment: Segment) -> None: - self._segments.append(segment) - - def addVibration(self, duration_ms: int, amplitude: float = 1.0) -> None: - self._segments.append(self.Segment(self.Segment.TYPE_VIBRATION, duration_ms, amplitude)) - - def addPause(self, duration_ms: int) -> None: - self._segments.append(self.Segment(self.Segment.TYPE_PAUSE, duration_ms, 0.0)) - - def getSegments(self) -> List[Segment]: - return list(self._segments) - - def getTotalDuration(self) -> int: - return sum(s.getDuration() for s in self._segments) - - def clear(self) -> None: - self._segments.clear() - - def toString(self) -> str: - return f"HapticPattern(segments={len(self._segments)}, total={self.getTotalDuration()}ms)" - - -class HapticComposer(Stub): - """HapticComposer — 触觉合成器""" - - def __init__(self): - super().__init__() - self._pattern = HapticPattern() - - def addContinuous(self, duration_ms: int, amplitude: float = 1.0) -> "HapticComposer": - self._pattern.addVibration(duration_ms, amplitude) - return self - - def addPause(self, duration_ms: int) -> "HapticComposer": - self._pattern.addPause(duration_ms) - return self - - def addRamp(self, duration_ms: int, start_amplitude: float, - end_amplitude: float, steps: int = 10) -> "HapticComposer": - step_duration = duration_ms // max(1, steps) - for i in range(steps): - amp = start_amplitude + (end_amplitude - start_amplitude) * (i / max(1, steps - 1)) - self._pattern.addVibration(step_duration, amp) - return self - - def addPulse(self, duration_ms: int, amplitude: float = 1.0, - count: int = 1, interval_ms: int = 100) -> "HapticComposer": - for i in range(count): - if i > 0: - self._pattern.addPause(interval_ms) - self._pattern.addVibration(duration_ms, amplitude) - return self - - def addClick(self) -> "HapticComposer": - return self.addPulse(10, 1.0, 1, 0) - - def addTick(self) -> "HapticComposer": - return self.addPulse(5, 0.5, 1, 0) - - def build(self) -> HapticPattern: - return self._pattern - - def reset(self) -> None: - self._pattern = HapticPattern() - - def toString(self) -> str: - return f"HapticComposer(pattern={self._pattern})" \ No newline at end of file diff --git a/android_pyapi/android/view_inputmethod.py b/android_pyapi/android/view_inputmethod.py deleted file mode 100644 index 4da8dab..0000000 --- a/android_pyapi/android/view_inputmethod.py +++ /dev/null @@ -1,859 +0,0 @@ -""" -Android View InputMethod 组件 — InputMethodManager, EditorInfo, -InputConnection, ExtractedText 等 -方法签名完全对齐 Android SDK -""" - -import sys -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError, android_context - - -# ═══════════════════════════════════════════════════════════════ -# InputMethodManager -# ═══════════════════════════════════════════════════════════════ - -class InputMethodManager(Stub): - """InputMethodManager — 输入法管理器""" - - SHOW_IMPLICIT = 0x0001 - SHOW_FORCED = 0x0002 - HIDE_IMPLICIT_ONLY = 0x0001 - HIDE_NOT_ALWAYS = 0x0002 - RESULT_SHOWN = 2 - RESULT_HIDDEN = 3 - RESULT_UNCHANGED_SHOWN = 4 - RESULT_UNCHANGED_HIDDEN = 5 - - def __init__(self): - super().__init__() - self._active: bool = False - self._current_input_connection: Optional["InputConnection"] = None - self._is_accepting_text: bool = True - self._fullscreen_mode: bool = False - self._cursor_x: int = 0 - self._cursor_y: int = 0 - self._selection_start: int = -1 - self._selection_end: int = -1 - self._composing_start: int = -1 - self._composing_end: int = -1 - - def showSoftInput(self, view: Any, flags: int, - result_receiver: Any = None) -> bool: - self._active = True - sys.stderr.write(f"[InputMethodManager] showSoftInput: flags={flags}\n") - return True - - def hideSoftInputFromWindow(self, window_token: Any, flags: int, - result_receiver: Any = None) -> bool: - self._active = False - sys.stderr.write(f"[InputMethodManager] hideSoftInputFromWindow: flags={flags}\n") - return True - - def toggleSoftInput(self, show_flags: int, hide_flags: int): - self._active = not self._active - sys.stderr.write(f"[InputMethodManager] toggleSoftInput: " - f"show={show_flags}, hide={hide_flags}\n") - - def restartInput(self, view: Any): - sys.stderr.write("[InputMethodManager] restartInput\n") - - def startInput(self, view: Any) -> bool: - sys.stderr.write("[InputMethodManager] startInput\n") - return True - - def updateSelection(self, view: Any, sel_start: int, sel_end: int, - composing_start: int, composing_end: int): - self._selection_start = sel_start - self._selection_end = sel_end - self._composing_start = composing_start - self._composing_end = composing_end - - def updateCursorAnchorInfo(self, view: Any, cursor_anchor_info: "CursorAnchorInfo"): - self._cursor_x = cursor_anchor_info.getInsertionMarkerBaseline() - self._cursor_y = cursor_anchor_info.getInsertionMarkerTop() - - def displayCompletions(self, view: Any, completions: List["CompletionInfo"]): - sys.stderr.write(f"[InputMethodManager] displayCompletions: " - f"{len(completions) if completions else 0} items\n") - - def sendKeyChar(self, key_char: str): - sys.stderr.write(f"[InputMethodManager] sendKeyChar: '{key_char}'\n") - - def clearMetaKeyStates(self, view: Any, states: int): - sys.stderr.write(f"[InputMethodManager] clearMetaKeyStates: {states}\n") - - def isAcceptingText(self) -> bool: - return self._is_accepting_text - - def isActive(self) -> bool: - return self._active - - def isFullscreenMode(self) -> bool: - return self._fullscreen_mode - - def setFullscreenMode(self, fullscreen: bool): - self._fullscreen_mode = fullscreen - - def getCurrentInputMethodSubtype(self) -> Optional["InputMethodSubtype"]: - return None - - def getShortcutInputMethodsAndSubtypes(self) -> List[Any]: - return [] - - def showStatusIcon(self, icon: Any): - pass - - def hideStatusIcon(self): - pass - - def registerSuggestionSpansForNotification(self, spans: List[Any], sender: Any): - pass - - def notifySuggestionPicked(self, suggestion: Any): - pass - - def windowDismissed(self, window_token: Any): - self._active = False - - def toString(self) -> str: - return f"InputMethodManager(active={self._active})" - - -# ═══════════════════════════════════════════════════════════════ -# EditorInfo -# ═══════════════════════════════════════════════════════════════ - -class EditorInfo(Stub): - """EditorInfo — 编辑器信息""" - - TYPE_CLASS_TEXT = 0x0001 - TYPE_CLASS_NUMBER = 0x0002 - TYPE_CLASS_PHONE = 0x0004 - TYPE_CLASS_DATETIME = 0x0008 - - TYPE_TEXT_VARIATION_NORMAL = 0x0000 - TYPE_TEXT_VARIATION_URI = 0x0010 - TYPE_TEXT_VARIATION_EMAIL_ADDRESS = 0x0020 - TYPE_TEXT_VARIATION_EMAIL_SUBJECT = 0x0030 - TYPE_TEXT_VARIATION_SHORT_MESSAGE = 0x0040 - TYPE_TEXT_VARIATION_LONG_MESSAGE = 0x0050 - TYPE_TEXT_VARIATION_PERSON_NAME = 0x0060 - TYPE_TEXT_VARIATION_POSTAL_ADDRESS = 0x0070 - TYPE_TEXT_VARIATION_PASSWORD = 0x0080 - TYPE_TEXT_VARIATION_VISIBLE_PASSWORD = 0x0090 - TYPE_TEXT_VARIATION_WEB_EDIT_TEXT = 0x00A0 - TYPE_TEXT_VARIATION_FILTER = 0x00B0 - TYPE_TEXT_VARIATION_PHONETIC = 0x00C0 - TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS = 0x00D0 - TYPE_TEXT_VARIATION_WEB_PASSWORD = 0x00E0 - - TYPE_NUMBER_VARIATION_NORMAL = 0x0000 - TYPE_NUMBER_VARIATION_PASSWORD = 0x0010 - - TYPE_DATETIME_VARIATION_NORMAL = 0x0000 - TYPE_DATETIME_VARIATION_DATE = 0x0010 - TYPE_DATETIME_VARIATION_TIME = 0x0020 - - TYPE_NULL = 0x0000 - - IME_ACTION_NONE = 0x0001 - IME_ACTION_GO = 0x0002 - IME_ACTION_SEARCH = 0x0003 - IME_ACTION_SEND = 0x0004 - IME_ACTION_NEXT = 0x0005 - IME_ACTION_DONE = 0x0006 - IME_ACTION_PREVIOUS = 0x0007 - - IME_FLAG_NO_EXTRACT_UI = 0x10000001 - IME_FLAG_NO_ACCESSORY_ACTION = 0x20000000 - IME_FLAG_NO_ENTER_ACTION = 0x40000000 - IME_FLAG_NAVIGATE_NEXT = 0x08000000 - IME_FLAG_NAVIGATE_PREVIOUS = 0x04000000 - IME_FLAG_NO_FULLSCREEN = 0x02000000 - IME_FLAG_FORCE_ASCII = 0x80000000 - - IME_MASK_ACTION = 0x000000FF - - def __init__(self): - super().__init__() - self.inputType: int = EditorInfo.TYPE_CLASS_TEXT - self.imeOptions: int = EditorInfo.IME_ACTION_NONE - self.actionLabel: Optional[str] = None - self.actionId: int = 0 - self.initialSelStart: int = -1 - self.initialSelEnd: int = -1 - self.initialCapsMode: int = 0 - self.hintText: Optional[str] = None - self.label: Optional[str] = None - self.packageName: Optional[str] = None - self.fieldId: int = 0 - self.fieldName: Optional[str] = None - self.extras: Optional[Any] = None - - def toString(self) -> str: - return (f"EditorInfo(inputType={hex(self.inputType)}, " - f"imeOptions={hex(self.imeOptions)}, " - f"actionLabel={self.actionLabel})") - - def dump(self, printer: Any, prefix: str): - sys.stderr.write(f"{prefix}EditorInfo: inputType={hex(self.inputType)}\n") - - -# ═══════════════════════════════════════════════════════════════ -# InputConnection -# ═══════════════════════════════════════════════════════════════ - -class InputConnection(Stub): - """InputConnection — 输入连接接口""" - - def __init__(self): - super().__init__() - self._text: str = "" - self._selection_start: int = 0 - self._selection_end: int = 0 - self._composing_start: int = -1 - self._composing_end: int = -1 - self._batch_edit_count: int = 0 - - def getTextBeforeCursor(self, length: int, flags: int) -> Optional[str]: - end = self._selection_start - start = max(0, end - length) - return self._text[start:end] - - def getTextAfterCursor(self, length: int, flags: int) -> Optional[str]: - start = self._selection_end - end = min(len(self._text), start + length) - return self._text[start:end] - - def getExtractedText(self, request: "ExtractedTextRequest", - flags: int) -> Optional["ExtractedText"]: - result = ExtractedText() - result.text = self._text - result.startOffset = 0 - result.selectionStart = self._selection_start - result.selectionEnd = self._selection_end - result.flags = 0 - return result - - def commitText(self, text: Any, new_cursor_position: int) -> bool: - if isinstance(text, str): - committed = text - else: - committed = str(text) if text else "" - - before = self._text[:self._selection_start] - after = self._text[self._selection_end:] - self._text = before + committed + after - - if new_cursor_position > 0: - self._selection_start = len(before) + new_cursor_position - 1 - else: - self._selection_start = len(before) + len(committed) + new_cursor_position - self._selection_end = self._selection_start - self._composing_start = -1 - self._composing_end = -1 - return True - - def setComposingText(self, text: Any, new_cursor_position: int) -> bool: - if self._composing_start < 0: - self._composing_start = self._selection_start - self._composing_end = self._selection_end - - before = self._text[:self._composing_start] - after = self._text[self._composing_end:] - - if isinstance(text, str): - composing = text - else: - composing = str(text) if text else "" - - self._text = before + composing + after - self._composing_end = self._composing_start + len(composing) - self._selection_start = self._composing_end + new_cursor_position - self._selection_end = self._selection_start - return True - - def setSelection(self, start: int, end: int) -> bool: - self._selection_start = max(0, min(start, len(self._text))) - self._selection_end = max(0, min(end, len(self._text))) - return True - - def deleteSurroundingText(self, before_length: int, after_length: int) -> bool: - start = max(0, self._selection_start - before_length) - end = min(len(self._text), self._selection_end + after_length) - self._text = self._text[:start] + self._text[end:] - new_cursor = max(0, self._selection_start - before_length) - self._selection_start = new_cursor - self._selection_end = new_cursor - return True - - def commitCompletion(self, completion: "CompletionInfo") -> bool: - sys.stderr.write(f"[InputConnection] commitCompletion: " - f"{completion.getText()}\n") - return True - - def commitCorrection(self, correction: "CorrectionInfo") -> bool: - sys.stderr.write(f"[InputConnection] commitCorrection: " - f"{correction.getNewText()}\n") - return True - - def getSelectedText(self, flags: int) -> Optional[str]: - if self._selection_start == self._selection_end: - return "" - return self._text[self._selection_start:self._selection_end] - - def finishComposingText(self) -> bool: - self._composing_start = -1 - self._composing_end = -1 - return True - - def beginBatchEdit(self) -> bool: - self._batch_edit_count += 1 - return True - - def endBatchEdit(self) -> bool: - if self._batch_edit_count > 0: - self._batch_edit_count -= 1 - return True - - def sendKeyEvent(self, event: Any) -> bool: - sys.stderr.write(f"[InputConnection] sendKeyEvent\n") - return True - - def clearMetaKeyStates(self, states: int) -> bool: - return True - - def performEditorAction(self, action_code: int) -> bool: - sys.stderr.write(f"[InputConnection] performEditorAction: {action_code}\n") - return True - - def performContextMenuAction(self, id: int) -> bool: - sys.stderr.write(f"[InputConnection] performContextMenuAction: {id}\n") - return True - - def commitContent(self, input_content: "InputContentInfo", - flags: int, opts: Optional[Any] = None) -> bool: - sys.stderr.write("[InputConnection] commitContent\n") - return True - - def requestCursorUpdates(self, cursor_update_mode: int) -> bool: - return True - - def closeConnection(self): - pass - - def reportFullscreenMode(self, enabled: bool): - pass - - def getSurroundingText(self, before_length: int, after_length: int, - flags: int) -> Optional["SurroundingText"]: - start = max(0, self._selection_start - before_length) - end = min(len(self._text), self._selection_end + after_length) - return SurroundingText( - text=self._text[start:end], - selection_start=self._selection_start - start, - selection_end=self._selection_end - start, - offset=start, - ) - - def takeSnapshot(self) -> Optional[Any]: - """获取输入快照""" - return None - - def setComposingRegion(self, start: int, end: int) -> bool: - self._composing_start = start - self._composing_end = end - return True - - def getCursorCapsMode(self, req_modes: int) -> int: - return 0 - - def getTextBeforeCursor(self, length: int, flags: int) -> Optional[str]: - end = self._selection_start - start = max(0, end - length) - return self._text[start:end] - - def getTextAfterCursor(self, length: int, flags: int) -> Optional[str]: - start = self._selection_end - end = min(len(self._text), start + length) - return self._text[start:end] - - -# ═══════════════════════════════════════════════════════════════ -# ExtractedText -# ═══════════════════════════════════════════════════════════════ - -class ExtractedText(Stub): - """ExtractedText — 提取的文本""" - - FLAG_SINGLE_LINE = 0x0001 - FLAG_SELECTING = 0x0002 - - def __init__(self): - super().__init__() - self.text: Optional[str] = None - self.startOffset: int = 0 - self.partialStartOffset: int = -1 - self.partialEndOffset: int = -1 - self.selectionStart: int = -1 - self.selectionEnd: int = -1 - self.flags: int = 0 - - def toString(self) -> str: - return (f"ExtractedText(startOffset={self.startOffset}, " - f"text={self.text}, " - f"selection=({self.selectionStart}, {self.selectionEnd}))") - - -# ═══════════════════════════════════════════════════════════════ -# ExtractedTextRequest -# ═══════════════════════════════════════════════════════════════ - -class ExtractedTextRequest(Stub): - """ExtractedTextRequest — 提取文本请求""" - - def __init__(self): - super().__init__() - self.token: int = 0 - self.flags: int = 0 - self.hintMaxLines: int = 0 - self.hintMaxChars: int = 0 - - -# ═══════════════════════════════════════════════════════════════ -# InputBinding -# ═══════════════════════════════════════════════════════════════ - -class InputBinding(Stub): - """InputBinding — 输入绑定""" - - def __init__(self, connection: Optional["InputConnection"] = None, - uid: int = -1, pid: int = -1, - private_ime_options: str = ""): - super().__init__() - self._connection: Optional["InputConnection"] = connection - self._uid: int = uid - self._pid: int = pid - self._private_ime_options: str = private_ime_options - - def getConnection(self) -> Optional["InputConnection"]: - return self._connection - - def getUid(self) -> int: - return self._uid - - def getPid(self) -> int: - return self._pid - - def getPrivateImeOptions(self) -> str: - return self._private_ime_options - - def toString(self) -> str: - return (f"InputBinding(uid={self._uid}, pid={self._pid}, " - f"connection={self._connection})") - - -# ═══════════════════════════════════════════════════════════════ -# InputMethodSubtype -# ═══════════════════════════════════════════════════════════════ - -class InputMethodSubtype(Stub): - """InputMethodSubtype — 输入法子类型""" - - SUBTYPE_ID_NONE = -1 - - def __init__(self, locale: str = "", mode: str = "", - extra_value: str = "", is_auxiliary: bool = False, - overrides_ime_theme: bool = False, - subtype_id: int = SUBTYPE_ID_NONE): - super().__init__() - self._locale: str = locale - self._mode: str = mode - self._extra_value: str = extra_value - self._is_auxiliary: bool = is_auxiliary - self._overrides_ime_theme: bool = overrides_ime_theme - self._subtype_id: int = subtype_id - self._display_name_res: int = 0 - self._icon_res: int = 0 - - def getLocale(self) -> str: - return self._locale - - def getMode(self) -> str: - return self._mode - - def getExtraValue(self) -> str: - return self._extra_value - - def getSubtypeId(self) -> int: - return self._subtype_id - - def containsExtraValueKey(self, key: str) -> bool: - if not self._extra_value: - return False - pairs = self._extra_value.split(",") - for pair in pairs: - kv = pair.split("=", 1) - if len(kv) == 2 and kv[0].strip() == key: - return True - return False - - def getDisplayName(self, context: Any, package_name: str, - app_info: Any) -> str: - return self._locale or self._mode - - def isAuxiliary(self) -> bool: - return self._is_auxiliary - - def overridesImeTheme(self) -> bool: - return self._overrides_ime_theme - - def hashCode(self) -> int: - return hash((self._locale, self._mode, self._extra_value)) & 0x7FFFFFFF - - def equals(self, other: Any) -> bool: - if isinstance(other, InputMethodSubtype): - return (self._locale == other._locale and - self._mode == other._mode and - self._extra_value == other._extra_value) - return False - - def toString(self) -> str: - return (f"InputMethodSubtype(locale={self._locale}, " - f"mode={self._mode})") - - -# ═══════════════════════════════════════════════════════════════ -# CursorAnchorInfo -# ═══════════════════════════════════════════════════════════════ - -class CursorAnchorInfo(Stub): - """CursorAnchorInfo — 光标锚点信息""" - - FLAG_HAS_VISIBLE_REGION = 0x01 - FLAG_IS_RTL = 0x02 - - def __init__(self): - super().__init__() - self._insertion_marker_baseline: float = 0.0 - self._insertion_marker_top: float = 0.0 - self._insertion_marker_bottom: float = 0.0 - self._matrix: Optional[Any] = None - self._character_bounds: Dict[int, Tuple[float, float, float, float]] = {} - self._character_bounds_flags: Dict[int, int] = {} - self._composing_text: Optional[str] = None - self._composing_text_start: int = -1 - - def getInsertionMarkerBaseline(self) -> float: - return self._insertion_marker_baseline - - def setInsertionMarkerBaseline(self, baseline: float): - self._insertion_marker_baseline = baseline - - def getInsertionMarkerTop(self) -> float: - return self._insertion_marker_top - - def setInsertionMarkerTop(self, top: float): - self._insertion_marker_top = top - - def getInsertionMarkerBottom(self) -> float: - return self._insertion_marker_bottom - - def setInsertionMarkerBottom(self, bottom: float): - self._insertion_marker_bottom = bottom - - def getMatrix(self) -> Optional[Any]: - return self._matrix - - def setMatrix(self, matrix: Any): - self._matrix = matrix - - def getCharacterBounds(self, index: int) -> Optional[Tuple[float, float, float, float]]: - return self._character_bounds.get(index) - - def setCharacterBounds(self, index: int, left: float, top: float, - right: float, bottom: float, flags: int): - self._character_bounds[index] = (left, top, right, bottom) - self._character_bounds_flags[index] = flags - - def getCharacterBoundsFlags(self, index: int) -> int: - return self._character_bounds_flags.get(index, 0) - - def getComposingText(self) -> Optional[str]: - return self._composing_text - - def getComposingTextStart(self) -> int: - return self._composing_text_start - - def toString(self) -> str: - return (f"CursorAnchorInfo(baseline={self._insertion_marker_baseline}, " - f"top={self._insertion_marker_top})") - - -# ═══════════════════════════════════════════════════════════════ -# CorrectionInfo -# ═══════════════════════════════════════════════════════════════ - -class CorrectionInfo(Stub): - """CorrectionInfo — 校正信息""" - - def __init__(self, old_text: str = "", new_text: str = ""): - super().__init__() - self._old_text: str = old_text - self._new_text: str = new_text - - def getOldText(self) -> str: - return self._old_text - - def getNewText(self) -> str: - return self._new_text - - def toString(self) -> str: - return f"CorrectionInfo('{self._old_text}' -> '{self._new_text}')" - - -# ═══════════════════════════════════════════════════════════════ -# CompletionInfo -# ═══════════════════════════════════════════════════════════════ - -class CompletionInfo(Stub): - """CompletionInfo — 补全信息""" - - def __init__(self, id: int = 0, position: int = 0, - text: str = "", label: str = ""): - super().__init__() - self._id: int = id - self._position: int = position - self._text: str = text - self._label: str = label - - def getId(self) -> int: - return self._id - - def getPosition(self) -> int: - return self._position - - def getText(self) -> str: - return self._text - - def getLabel(self) -> str: - return self._label - - def toString(self) -> str: - return f"CompletionInfo(id={self._id}, text='{self._text}')" - - -# ═══════════════════════════════════════════════════════════════ -# SurroundingText -# ═══════════════════════════════════════════════════════════════ - -class SurroundingText(Stub): - """SurroundingText — 周围文本""" - - def __init__(self, text: str = "", selection_start: int = 0, - selection_end: int = 0, offset: int = 0): - super().__init__() - self._text: str = text - self._selection_start: int = selection_start - self._selection_end: int = selection_end - self._offset: int = offset - - def getText(self) -> str: - return self._text - - def getSelectionStart(self) -> int: - return self._selection_start - - def getSelectionEnd(self) -> int: - return self._selection_end - - def getOffset(self) -> int: - return self._offset - - def toString(self) -> str: - return (f"SurroundingText(text='{self._text}', " - f"selection=({self._selection_start}, {self._selection_end}), " - f"offset={self._offset})") - - -# ═══════════════════════════════════════════════════════════════ -# InputContentInfo -# ═══════════════════════════════════════════════════════════════ - -class InputContentInfo(Stub): - """InputContentInfo — 输入内容信息""" - - def __init__(self, uri: Any = None, description: Any = None, - link_uri: Any = None): - super().__init__() - self._uri: Any = uri - self._description: Any = description - self._link_uri: Any = link_uri - - def getUri(self) -> Any: - return self._uri - - def getDescription(self) -> Any: - return self._description - - def getLinkUri(self) -> Any: - return self._link_uri - - def requestPermission(self): - pass - - def releasePermission(self): - pass - - def toString(self) -> str: - return (f"InputContentInfo(uri={self._uri}, " - f"description={self._description})") - - -# ═══════════════════════════════════════════════════════════════ -# InputConnectionWrapper -# ═══════════════════════════════════════════════════════════════ - -class InputConnectionWrapper(InputConnection): - """InputConnectionWrapper — 输入连接包装器""" - - def __init__(self, target: InputConnection, mutable: bool = True): - super().__init__() - self._target: InputConnection = target - self._mutable: bool = mutable - - def setTarget(self, target: InputConnection): - if self._mutable: - self._target = target - - def getTarget(self) -> InputConnection: - return self._target - - def getTextBeforeCursor(self, length: int, flags: int) -> Optional[str]: - return self._target.getTextBeforeCursor(length, flags) - - def getTextAfterCursor(self, length: int, flags: int) -> Optional[str]: - return self._target.getTextAfterCursor(length, flags) - - def getSelectedText(self, flags: int) -> Optional[str]: - return self._target.getSelectedText(flags) - - def getSurroundingText(self, before_length: int, after_length: int, - flags: int) -> Optional["SurroundingText"]: - return self._target.getSurroundingText(before_length, after_length, flags) - - def getCursorCapsMode(self, req_modes: int) -> int: - return self._target.getCursorCapsMode(req_modes) - - def getExtractedText(self, request: "ExtractedTextRequest", - flags: int) -> Optional["ExtractedText"]: - return self._target.getExtractedText(request, flags) - - def commitText(self, text: Any, new_cursor_position: int) -> bool: - return self._target.commitText(text, new_cursor_position) - - def commitCompletion(self, completion: "CompletionInfo") -> bool: - return self._target.commitCompletion(completion) - - def commitCorrection(self, correction: "CorrectionInfo") -> bool: - return self._target.commitCorrection(correction) - - def commitContent(self, input_content: "InputContentInfo", - flags: int, opts: Optional[Any] = None) -> bool: - return self._target.commitContent(input_content, flags, opts) - - def setComposingText(self, text: Any, new_cursor_position: int) -> bool: - return self._target.setComposingText(text, new_cursor_position) - - def setComposingRegion(self, start: int, end: int) -> bool: - return self._target.setComposingRegion(start, end) - - def finishComposingText(self) -> bool: - return self._target.finishComposingText() - - def setSelection(self, start: int, end: int) -> bool: - return self._target.setSelection(start, end) - - def performEditorAction(self, action_code: int) -> bool: - return self._target.performEditorAction(action_code) - - def performContextMenuAction(self, id: int) -> bool: - return self._target.performContextMenuAction(id) - - def beginBatchEdit(self) -> bool: - return self._target.beginBatchEdit() - - def endBatchEdit(self) -> bool: - return self._target.endBatchEdit() - - def sendKeyEvent(self, event: Any) -> bool: - return self._target.sendKeyEvent(event) - - def clearMetaKeyStates(self, states: int) -> bool: - return self._target.clearMetaKeyStates(states) - - def deleteSurroundingText(self, before_length: int, after_length: int) -> bool: - return self._target.deleteSurroundingText(before_length, after_length) - - def requestCursorUpdates(self, cursor_update_mode: int) -> bool: - return self._target.requestCursorUpdates(cursor_update_mode) - - def closeConnection(self): - self._target.closeConnection() - - def reportFullscreenMode(self, enabled: bool): - self._target.reportFullscreenMode(enabled) - - -# ═══════════════════════════════════════════════════════════════ -# BaseInputConnection -# ═══════════════════════════════════════════════════════════════ - -class BaseInputConnection(InputConnection): - """BaseInputConnection — 输入连接基类""" - - def __init__(self, view: Any, full_editor: bool = True): - super().__init__() - self._view = view - self._full_editor = full_editor - self._text: str = "" - self._selection_start: int = 0 - self._selection_end: int = 0 - self._composing_start: int = -1 - self._composing_end: int = -1 - - def getEditable(self) -> Optional[Any]: - return None - - def reportFullscreenMode(self, enabled: bool): - pass - - def requestCursorUpdates(self, cursor_update_mode: int) -> bool: - return self._full_editor - - def closeConnection(self): - pass - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "InputMethodManager", - "EditorInfo", - "InputConnection", - "ExtractedText", - "ExtractedTextRequest", - "InputBinding", - "InputMethodSubtype", - "CursorAnchorInfo", - "CorrectionInfo", - "CompletionInfo", - "SurroundingText", - "InputContentInfo", - "InputConnectionWrapper", - "BaseInputConnection", -] \ No newline at end of file diff --git a/android_pyapi/android/view_textclassifier.py b/android_pyapi/android/view_textclassifier.py deleted file mode 100644 index dbc19e2..0000000 --- a/android_pyapi/android/view_textclassifier.py +++ /dev/null @@ -1,426 +0,0 @@ -""" -Android view.textclassifier 组件 — TextClassifier, TextClassification, TextLinks, TextSelection 等 -方法签名完全对齐 Android SDK -""" - -import re -from typing import Any, Callable, Dict, List, Optional, Set, Tuple - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# EntityConfig -# ═══════════════════════════════════════════════════════════════ - -class EntityConfig(Stub): - """EntityConfig — 实体配置""" - - class Builder(Stub): - """EntityConfig.Builder — 实体配置构建器""" - - def __init__(self): - super().__init__() - self._include_types: Optional[Set[str]] = None - self._exclude_types: Optional[Set[str]] = None - self._include_from_textclassifier = True - - def includeTypesFromTextClassifier(self) -> "EntityConfig.Builder": - self._include_from_textclassifier = True - return self - - def excludeTypes(self, varargs: List[str]) -> "EntityConfig.Builder": - self._exclude_types = set(varargs) - return self - - def includeTypes(self, varargs: List[str]) -> "EntityConfig.Builder": - self._include_types = set(varargs) - return self - - def build(self) -> "EntityConfig": - config = EntityConfig() - config._include_types = self._include_types - config._exclude_types = self._exclude_types - config._include_from_textclassifier = self._include_from_textclassifier - return config - - def getEntityTypes(self) -> Optional[List[str]]: - if self._include_types: - return list(self._include_types) - return None - - def __init__(self): - super().__init__() - self._include_types: Optional[Set[str]] = None - self._exclude_types: Optional[Set[str]] = None - self._include_from_textclassifier = True - - def getEntityTypes(self) -> Optional[List[str]]: - if self._include_types: - return list(self._include_types) - return None - - -# ═══════════════════════════════════════════════════════════════ -# TextClassifier -# ═══════════════════════════════════════════════════════════════ - -class TextClassifier(Stub): - """TextClassifier — 文本分类器""" - - TYPE_UNKNOWN = "unknown" - TYPE_ADDRESS = "address" - TYPE_DATE = "date" - TYPE_DATE_TIME = "datetime" - TYPE_DURATION = "duration" - TYPE_EMAIL = "email" - TYPE_FLIGHT_NUMBER = "flight" - TYPE_IDENTIFIER = "identifier" - TYPE_LINK = "link" - TYPE_LIST = "list" - TYPE_LOCATION = "location" - TYPE_MEASUREMENT = "measurement" - TYPE_MONEY = "money" - TYPE_OTHER = "other" - TYPE_PERIODIC = "periodic" - TYPE_PHONE = "phone" - TYPE_PRODUCT = "product" - TYPE_RECIPE = "recipe" - TYPE_TRACK_AIRLINE = "track_airline" - TYPE_TRACK_FLIGHT = "track_flight" - TYPE_TRACK_FOOD = "track_food" - TYPE_TRACK_SPORT = "track_sport" - TYPE_TRACK_WEATHER = "track_weather" - TYPE_URL = "url" - TYPE_VEHICLE = "vehicle" - - WIDGET_TYPE_CLIPBOARD = "clipboard" - WIDGET_TYPE_CUT_COPY = "cutcopy" - WIDGET_TYPE_EDIT_TEXT = "edittext" - WIDGET_TYPE_LINKIFY = "linkify" - WIDGET_TYPE_NOTIFICATION = "notification" - WIDGET_TYPE_OTHER = "other" - WIDGET_TYPE_SELECTION = "selection" - WIDGET_TYPE_TEXT = "text" - WIDGET_TYPE_UNSELECTED_TEXT = "unselected_text" - - # ═══════════════════════════════════════════════════════════ - # Request - # ═══════════════════════════════════════════════════════════ - - class Request(Stub): - """TextClassifier.Request — 分类请求""" - - def __init__(self): - super().__init__() - self._calling_package_name: Optional[str] = None - self._text: str = "" - self._start_index: int = 0 - self._end_index: int = 0 - self._extras: Any = None - self._default_locales: Any = None - self._dark_launch_allowed = False - - def getCallingPackageName(self) -> Optional[str]: - return self._calling_package_name - - def getText(self) -> str: - return self._text - - def getStartIndex(self) -> int: - return self._start_index - - def getEndIndex(self) -> int: - return self._end_index - - def getExtras(self) -> Any: - return self._extras - - def getDefaultLocales(self) -> Any: - return self._default_locales - - def isDarkLaunchAllowed(self) -> bool: - return self._dark_launch_allowed - - def __init__(self): - super().__init__() - - def classifyText(self, request: Request) -> "TextClassification": - text = request.getText() - start = request.getStartIndex() - end = request.getEndIndex() - if end > len(text): - end = len(text) - selected = text[start:end] if start < end else text - - entity_type = TextClassifier.TYPE_UNKNOWN - confidence = 0.0 - - # Basic regex-based classification - if re.match(r'^https?://', selected): - entity_type = TextClassifier.TYPE_URL - confidence = 0.95 - elif re.match(r'^[\w.+-]+@[\w-]+\.[\w.-]+$', selected): - entity_type = TextClassifier.TYPE_EMAIL - confidence = 0.95 - elif re.match(r'^\+?[\d\s\-\(\)]{7,15}$', selected): - entity_type = TextClassifier.TYPE_PHONE - confidence = 0.85 - elif re.match(r'^\d{1,4}[-/]\d{1,2}[-/]\d{1,4}$', selected): - entity_type = TextClassifier.TYPE_DATE - confidence = 0.80 - elif re.match(r'^\d{1,2}:\d{2}(\s?[AP]M)?$', selected, re.IGNORECASE): - entity_type = TextClassifier.TYPE_DATE_TIME - confidence = 0.80 - - tc = TextClassification() - tc._text = selected - tc._entity_type = entity_type - tc._confidence_score = confidence - tc._entity_count = 1 - return tc - - def suggestConversationActions(self, request: Request) -> List["ConversationAction"]: - return [] - - def detectLanguage(self, text: str) -> "TextClassification": - tc = TextClassification() - tc._text = text - tc._entity_type = TextClassifier.TYPE_UNKNOWN - return tc - - def onSelectionEvent(self, event: Any): - pass - - def onTextChanged(self, text: Any): - pass - - def onTextSelectionAction(self, action: Any): - pass - - -# ═══════════════════════════════════════════════════════════════ -# TextClassification -# ═══════════════════════════════════════════════════════════════ - -class TextClassification(Stub): - """TextClassification — 文本分类结果""" - - def __init__(self): - super().__init__() - self._text: str = "" - self._entity_type: str = TextClassifier.TYPE_UNKNOWN - self._confidence_score: float = 0.0 - self._entity_count: int = 0 - self._icon: Any = None - self._label: str = "" - self._actions: List[Any] = [] - self._extras: Any = None - self._id: Optional[str] = None - - def getText(self) -> str: - return self._text - - def getEntityCount(self) -> int: - return self._entity_count - - def getEntity(self, index: int) -> Optional[str]: - if index == 0: - return self._entity_type - return None - - def getEntityType(self) -> str: - return self._entity_type - - def getConfidenceScore(self, entityType: str) -> float: - if entityType == self._entity_type: - return self._confidence_score - return 0.0 - - def getIcon(self) -> Any: - return self._icon - - def getLabel(self) -> str: - return self._label - - def getActions(self) -> List[Any]: - return self._actions - - def getExtras(self) -> Any: - return self._extras - - def getId(self) -> Optional[str]: - return self._id - - def toString(self) -> str: - return f"TextClassification(type={self._entity_type}," \ - f" confidence={self._confidence_score:.2f})" - - -# ═══════════════════════════════════════════════════════════════ -# TextLinks -# ═══════════════════════════════════════════════════════════════ - -class TextLinks(Stub): - """TextLinks — 文本链接""" - - def __init__(self): - super().__init__() - self._links: List[Any] = [] - self._extras: Any = None - - def getLinks(self) -> List[Any]: - return self._links - - def getEntityCount(self) -> int: - return len(self._links) - - def getEntity(self, index: int) -> Optional[str]: - if 0 <= index < len(self._links): - return self._links[index] - return None - - def getEntityType(self) -> str: - return TextClassifier.TYPE_LINK - - def getConfidenceScore(self, entityType: str) -> float: - return 0.0 - - def getExtras(self) -> Any: - return self._extras - - -# ═══════════════════════════════════════════════════════════════ -# TextSelection -# ═══════════════════════════════════════════════════════════════ - -class TextSelection(Stub): - """TextSelection — 文本选择结果""" - - def __init__(self): - super().__init__() - self._start_index: int = 0 - self._end_index: int = 0 - self._entity_type: str = TextClassifier.TYPE_UNKNOWN - self._text: str = "" - self._entity_count: int = 0 - self._confidence_score: float = 0.0 - self._extras: Any = None - self._id: Optional[str] = None - - def getSelectionStartIndex(self) -> int: - return self._start_index - - def getSelectionEndIndex(self) -> int: - return self._end_index - - def getEntityType(self) -> str: - return self._entity_type - - def getText(self) -> str: - return self._text - - def getEntityCount(self) -> int: - return self._entity_count - - def getEntity(self, index: int) -> Optional[str]: - if index == 0: - return self._entity_type - return None - - def getConfidenceScore(self, entityType: str) -> float: - if entityType == self._entity_type: - return self._confidence_score - return 0.0 - - def getExtras(self) -> Any: - return self._extras - - def getId(self) -> Optional[str]: - return self._id - - -# ═══════════════════════════════════════════════════════════════ -# ConversationAction -# ═══════════════════════════════════════════════════════════════ - -class ConversationAction(Stub): - """ConversationAction — 对话操作""" - - TYPE_CALL = "call" - TYPE_CREATE_REMINDER = "create_reminder" - TYPE_OPEN_URL = "open_url" - TYPE_SEND_EMAIL = "send_email" - TYPE_SEND_SMS = "send_sms" - TYPE_SHARE_LOCATION = "share_location" - TYPE_TEXT_REPLY = "text_reply" - TYPE_VIEW_CALENDAR = "view_calendar" - TYPE_VIEW_MAP = "view_map" - - def __init__(self, text: str = "", confidenceScore: float = 0.0, - action: str = "", extras: Any = None, - icon: Any = None, label: str = ""): - super().__init__() - self._text = text - self._confidence_score = confidenceScore - self._action = action - self._extras = extras - self._icon = icon - self._label = label - - def getText(self) -> str: - return self._text - - def getConfidenceScore(self) -> float: - return self._confidence_score - - def getAction(self) -> str: - return self._action - - def getExtras(self) -> Any: - return self._extras - - def getIcon(self) -> Any: - return self._icon - - def getLabel(self) -> str: - return self._label - - -# ═══════════════════════════════════════════════════════════════ -# TextClassificationContext -# ═══════════════════════════════════════════════════════════════ - -class TextClassificationContext(Stub): - """TextClassificationContext — 文本分类上下文""" - - def __init__(self, packageName: str = "", widgetType: str = "", - widgetVersion: str = ""): - super().__init__() - self._package_name = packageName - self._widget_type = widgetType - self._widget_version = widgetVersion - - def getPackageName(self) -> str: - return self._package_name - - def getWidgetType(self) -> str: - return self._widget_type - - def getWidgetVersion(self) -> str: - return self._widget_version - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "TextClassifier", - "TextClassification", - "TextLinks", - "TextSelection", - "ConversationAction", - "TextClassificationContext", - "EntityConfig", -] \ No newline at end of file diff --git a/android_pyapi/android/view_textservice.py b/android_pyapi/android/view_textservice.py deleted file mode 100644 index 1f9eed7..0000000 --- a/android_pyapi/android/view_textservice.py +++ /dev/null @@ -1,395 +0,0 @@ -""" -Android view.textservice 包 — TextServicesManager, SpellCheckerSession, -SuggestionsInfo, TextInfo, SentenceSuggestionsInfo, SpellCheckerSubtype -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import sys -from typing import Any, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "TextServicesManager", - "SpellCheckerSession", - "SpellCheckerSessionListener", - "SuggestionsInfo", - "TextInfo", - "SentenceSuggestionsInfo", - "SpellCheckerSubtype", -] - - -# ═══════════════════════════════════════════════════════════════ -# TextServicesManager -# ═══════════════════════════════════════════════════════════════ - -class TextServicesManager(Stub): - """TextServicesManager — 文本服务管理器 (桌面存根)""" - - SPELL_CHECKER_SERVICE = "textservices" - EXTRA_SUBTYPE_ID = "android.intent.extra.SUBTYPE_ID" - EXTRA_SPELL_CHECKER = "android.intent.extra.SPELL_CHECKER" - EXTRA_RESULT = "android.intent.extra.RESULT" - - _instance: Optional["TextServicesManager"] = None - - def __init__(self): - super().__init__() - self._enabled_spell_checkers: List[Any] = [] - self._current_spell_checker: Optional[Any] = None - self._current_subtype: Optional["SpellCheckerSubtype"] = None - self._spell_checker_enabled = True - - @staticmethod - def getInstance() -> "TextServicesManager": - """获取实例""" - if TextServicesManager._instance is None: - TextServicesManager._instance = TextServicesManager() - return TextServicesManager._instance - - def getSpellCheckerService(self, subtype: Any, language: str) -> Optional[Any]: - """获取拼写检查器服务""" - return None - - def getCurrentSpellChecker(self) -> Optional[Any]: - """获取当前拼写检查器""" - return self._current_spell_checker - - def getCurrentSubtype(self) -> Optional["SpellCheckerSubtype"]: - """获取当前子类型""" - return self._current_subtype - - def getEnabledSpellCheckers(self) -> List[Any]: - """获取启用的拼写检查器列表""" - return self._enabled_spell_checkers.copy() - - def newSpellCheckerSession(self, locale: Any, listener: "SpellCheckerSessionListener", - grammar: bool = False) -> Optional["SpellCheckerSession"]: - """创建新的拼写检查器会话""" - return SpellCheckerSession(self, locale, listener) - - def isSpellCheckerEnabled(self) -> bool: - """检查拼写检查器是否启用""" - return self._spell_checker_enabled - - def getEnabledSubtypes(self, spell_checker: Any) -> List["SpellCheckerSubtype"]: - """获取启用的子类型""" - return [] - - def setSpellCheckerSubtype(self, subtype: "SpellCheckerSubtype") -> None: - """设置拼写检查器子类型""" - self._current_subtype = subtype - - def getSpellCheckerSubtype(self, spell_checker: Any, subtype_id: int) -> Optional["SpellCheckerSubtype"]: - """获取拼写检查器子类型""" - return None - - -# ═══════════════════════════════════════════════════════════════ -# SpellCheckerSession -# ═══════════════════════════════════════════════════════════════ - -class SpellCheckerSessionListener(Stub): - """SpellCheckerSessionListener — 拼写检查器会话监听器""" - - def __init__(self): - super().__init__() - - def onGetSuggestions(self, results: List["SuggestionsInfo"]) -> None: - """获取建议回调""" - pass - - def onGetSentenceSuggestions(self, results: List["SentenceSuggestionsInfo"]) -> None: - """获取句子建议回调""" - pass - - -class SpellCheckerSession(Stub): - """SpellCheckerSession — 拼写检查器会话 (桌面存根)""" - - def __init__(self, manager: TextServicesManager, locale: Any, - listener: SpellCheckerSessionListener): - super().__init__() - self._manager = manager - self._locale = locale - self._listener = listener - self._disconnected = False - self._active_subtype: Optional["SpellCheckerSubtype"] = None - self._subtype_offset = 0 - self._session_locale = str(locale) if locale else "" - - def getSpellChecker(self) -> Optional[Any]: - """获取拼写检查器""" - return self._manager.getCurrentSpellChecker() - - def createSuggestions(self, text_info: "TextInfo", suggestions_limit: int) -> List["SuggestionsInfo"]: - """创建建议""" - return [] - - def getActiveSubtype(self) -> Optional["SpellCheckerSubtype"]: - """获取活动子类型""" - return self._active_subtype - - def getWordSuggestions(self, text_info: "TextInfo", suggestions_limit: int) -> List["SuggestionsInfo"]: - """获取单词建议""" - return [] - - def cancel(self) -> None: - """取消""" - self._disconnected = True - - def close(self) -> None: - """关闭""" - self._disconnected = True - - def onStart(self) -> None: - """开始""" - pass - - def onStop(self) -> None: - """停止""" - pass - - def isSessionDisconnected(self) -> bool: - """检查会话是否断开""" - return self._disconnected - - def getSpellCheckerSessionSubtypeOffset(self) -> int: - """获取拼写检查器会话子类型偏移""" - return self._subtype_offset - - def setSpellCheckerSessionSubtypeOffset(self, offset: int) -> None: - """设置拼写检查器会话子类型偏移""" - self._subtype_offset = offset - - def getSpellCheckerSessionLocale(self) -> str: - """获取拼写检查器会话区域设置""" - return self._session_locale - - -# ═══════════════════════════════════════════════════════════════ -# SuggestionsInfo -# ═══════════════════════════════════════════════════════════════ - -class SuggestionsInfo(Stub): - """SuggestionsInfo — 建议信息""" - - RESULT_ATTR_IN_THE_DICTIONARY = 0x0001 - RESULT_ATTR_LOOKS_LIKE_TYPO = 0x0002 - RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = 0x0004 - RESULT_ATTR_RECOMMENDED_SUGGESTION = 0x0008 - RESULT_ATTR_SUGGESTED_WITH_EXPERT_MODE = 0x0010 - - def __init__(self, suggestions: Optional[List[str]] = None, cookie: int = 0, - sequence: int = 0, suggestions_attrs: Optional[List[int]] = None): - super().__init__() - self._suggestions = suggestions or [] - self._cookie = cookie - self._sequence = sequence - self._suggestions_attrs = suggestions_attrs or [] - - def getSuggestionsCount(self) -> int: - """获取建议数量""" - return len(self._suggestions) - - def getSuggestionsAt(self, index: int) -> Optional[str]: - """获取指定索引的建议""" - if 0 <= index < len(self._suggestions): - return self._suggestions[index] - return None - - def getCookie(self) -> int: - """获取 Cookie""" - return self._cookie - - def getSequence(self) -> int: - """获取序列""" - return self._sequence - - def getSuggestionInfo(self, index: int) -> Tuple[Optional[str], int]: - """获取建议信息和属性""" - if 0 <= index < len(self._suggestions): - attr = self._suggestions_attrs[index] if index < len(self._suggestions_attrs) else 0 - return (self._suggestions[index], attr) - return (None, 0) - - def setCookieAndSequence(self, cookie: int, sequence: int) -> None: - """设置 Cookie 和序列""" - self._cookie = cookie - self._sequence = sequence - - -# ═══════════════════════════════════════════════════════════════ -# TextInfo -# ═══════════════════════════════════════════════════════════════ - -class TextInfo(Stub): - """TextInfo — 文本信息""" - - def __init__(self, text: str = "", cookie: int = 0, sequence: int = 0, - input_start: int = 0, input_end: int = 0, cursor_offset: int = 0): - super().__init__() - self._text = text - self._cookie = cookie - self._sequence = sequence - self._input_start = input_start - self._input_end = input_end - self._cursor_offset = cursor_offset - - def getText(self) -> str: - """获取文本""" - return self._text - - def getCookie(self) -> int: - """获取 Cookie""" - return self._cookie - - def getSequence(self) -> int: - """获取序列""" - return self._sequence - - def getInputStart(self) -> int: - """获取输入起始位置""" - return self._input_start - - def getInputEnd(self) -> int: - """获取输入结束位置""" - return self._input_end - - def getCursorOffset(self) -> int: - """获取光标偏移""" - return self._cursor_offset - - def toString(self) -> str: - return f"TextInfo(text={self._text}, cookie={self._cookie})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, TextInfo): - return False - return (self._text == other._text and self._cookie == other._cookie and - self._sequence == other._sequence) - - def hashCode(self) -> int: - return hash((self._text, self._cookie, self._sequence)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# SentenceSuggestionsInfo -# ═══════════════════════════════════════════════════════════════ - -class SentenceSuggestionsInfo(Stub): - """SentenceSuggestionsInfo — 句子建议信息""" - - def __init__(self, suggestions_infos: Optional[List[SuggestionsInfo]] = None, - offsets: Optional[List[int]] = None, lengths: Optional[List[int]] = None): - super().__init__() - self._suggestions_infos = suggestions_infos or [] - self._offsets = offsets or [] - self._lengths = lengths or [] - - def getSuggestionsCount(self) -> int: - """获取建议数量""" - return len(self._suggestions_infos) - - def getSuggestionsAt(self, index: int) -> Optional[SuggestionsInfo]: - """获取指定索引的建议""" - if 0 <= index < len(self._suggestions_infos): - return self._suggestions_infos[index] - return None - - def getOffsetAt(self, index: int) -> int: - """获取指定索引的偏移""" - if 0 <= index < len(self._offsets): - return self._offsets[index] - return 0 - - def getLengthAt(self, index: int) -> int: - """获取指定索引的长度""" - if 0 <= index < len(self._lengths): - return self._lengths[index] - return 0 - - -# ═══════════════════════════════════════════════════════════════ -# SpellCheckerSubtype -# ═══════════════════════════════════════════════════════════════ - -class SpellCheckerSubtype(Stub): - """SpellCheckerSubtype — 拼写检查器子类型""" - - MODE_ANDROID = "android" - MODE_DEFAULT = "default" - SUBTYPE_ID_NONE = -1 - - def __init__(self, locale: str = "", extra_value: str = "", - name_override: str = "", subtype_id: int = SUBTYPE_ID_NONE, - subtype_mode: str = MODE_DEFAULT): - super().__init__() - self._locale = locale - self._extra_value = extra_value - self._name_override = name_override - self._subtype_id = subtype_id - self._subtype_mode = subtype_mode - self._auto_selected = False - - def getLocale(self) -> str: - """获取区域设置""" - return self._locale - - def containsExtraValue(self, key: str) -> bool: - """检查是否包含额外值""" - return key in self._extra_value - - def getDisplayName(self, context: Any, package_name: str, type_name: str) -> str: - """获取显示名称""" - if self._name_override: - return self._name_override - return self._locale - - def getExtraValue(self) -> str: - """获取额外值""" - return self._extra_value - - def hashCode(self) -> int: - return hash((self._locale, self._extra_value, self._subtype_id)) & 0x7FFFFFFF - - def equals(self, other: Any) -> bool: - if not isinstance(other, SpellCheckerSubtype): - return False - return (self._locale == other._locale and - self._extra_value == other._extra_value and - self._subtype_id == other._subtype_id) - - def getNameOverride(self) -> str: - """获取名称覆盖""" - return self._name_override - - def getSubtypeId(self) -> int: - """获取子类型 ID""" - return self._subtype_id - - def getSubtypeLocale(self) -> str: - """获取子类型区域设置""" - return self._locale - - def getSubtypeExtraValue(self) -> str: - """获取子类型额外值""" - return self._extra_value - - def getSubtypeNameOverride(self) -> str: - """获取子类型名称覆盖""" - return self._name_override - - def getSubtypeMode(self) -> str: - """获取子类型模式""" - return self._subtype_mode - - def isAutomaticallySelected(self) -> bool: - """检查是否自动选择""" - return self._auto_selected \ No newline at end of file diff --git a/android_pyapi/android/view_translation.py b/android_pyapi/android/view_translation.py deleted file mode 100644 index 2a0c6fd..0000000 --- a/android_pyapi/android/view_translation.py +++ /dev/null @@ -1,435 +0,0 @@ -""" -Android view.translation 组件 — TranslationManager, TranslationRequest, TranslationResponse 等 -方法签名完全对齐 Android SDK -""" - -from typing import Any, Callable, Dict, List, Optional - -from .._core import Stub, AndroidOnlyError - - -# ═══════════════════════════════════════════════════════════════ -# TranslationSpec -# ═══════════════════════════════════════════════════════════════ - -class TranslationSpec(Stub): - """TranslationSpec — 翻译规格""" - - DATA_TYPE_TEXT = 0 - DATA_TYPE_UI = 1 - - def __init__(self): - super().__init__() - self._locale: Optional[str] = None - self._data_type = TranslationSpec.DATA_TYPE_TEXT - - def setLocale(self, locale: str) -> "TranslationSpec": - self._locale = locale - return self - - def setDataType(self, dataType: int) -> "TranslationSpec": - self._data_type = dataType - return self - - def getLocale(self) -> Optional[str]: - return self._locale - - def getDataType(self) -> int: - return self._data_type - - -# ═══════════════════════════════════════════════════════════════ -# TranslationContext -# ═══════════════════════════════════════════════════════════════ - -class TranslationContext(Stub): - """TranslationContext — 翻译上下文""" - - def __init__(self, sourceLocale: str = "", targetLocale: str = "", - translationFlags: int = 0): - super().__init__() - self._source_locale = sourceLocale - self._target_locale = targetLocale - self._translation_flags = translationFlags - - def getSourceLocale(self) -> str: - return self._source_locale - - def getTargetLocale(self) -> str: - return self._target_locale - - def getTranslationFlags(self) -> int: - return self._translation_flags - - -# ═══════════════════════════════════════════════════════════════ -# TranslationCapability -# ═══════════════════════════════════════════════════════════════ - -class TranslationCapability(Stub): - """TranslationCapability — 翻译能力""" - - STATE_AVAILABLE = 0 - STATE_UNAVAILABLE = 1 - STATE_CAPABLE = 2 - STATE_NOT_CAPABLE = 3 - STATE_DOWNLOADING = 4 - STATE_NOT_ALLOWED = 5 - - def __init__(self, state: int = STATE_UNAVAILABLE, - sourceLocale: str = "", targetLocale: str = "", - uiTranslationEnabled: bool = False, - textTranslationEnabled: bool = False, - translationService: str = "desktop"): - super().__init__() - self._state = state - self._source_locale = sourceLocale - self._target_locale = targetLocale - self._ui_translation_enabled = uiTranslationEnabled - self._text_translation_enabled = textTranslationEnabled - self._translation_service = translationService - - def getState(self) -> int: - return self._state - - def getSourceLocale(self) -> str: - return self._source_locale - - def getTargetLocale(self) -> str: - return self._target_locale - - def isUiTranslationEnabled(self) -> bool: - return self._ui_translation_enabled - - def isTextTranslationEnabled(self) -> bool: - return self._text_translation_enabled - - def getTranslationService(self) -> str: - return self._translation_service - - -# ═══════════════════════════════════════════════════════════════ -# TranslationResult -# ═══════════════════════════════════════════════════════════════ - -class TranslationResult(Stub): - """TranslationResult — 翻译结果""" - - def __init__(self, translatedText: str = "", sourceText: str = "", - translationStatus: int = 0): - super().__init__() - self._translated_text = translatedText - self._source_text = sourceText - self._translation_status = translationStatus - - def getTranslatedText(self) -> str: - return self._translated_text - - def getSourceText(self) -> str: - return self._source_text - - def getTranslationStatus(self) -> int: - return self._translation_status - - -# ═══════════════════════════════════════════════════════════════ -# TranslationRequest -# ═══════════════════════════════════════════════════════════════ - -class TranslationRequest(Stub): - """TranslationRequest — 翻译请求""" - - class Builder(Stub): - """TranslationRequest.Builder — 翻译请求构建器""" - - def __init__(self): - super().__init__() - self._source_text: str = "" - self._source_locale: str = "" - self._target_locale: str = "" - self._translation_flags: int = 0 - self._ui_translation_result: Any = None - self._request_id: int = 0 - self._translation_config: Any = None - - def setSourceText(self, text: str) -> "TranslationRequest.Builder": - self._source_text = text - return self - - def setSourceLocale(self, locale: str) -> "TranslationRequest.Builder": - self._source_locale = locale - return self - - def setTargetLocale(self, locale: str) -> "TranslationRequest.Builder": - self._target_locale = locale - return self - - def setTranslationFlags(self, flags: int) -> "TranslationRequest.Builder": - self._translation_flags = flags - return self - - def setUiTranslationResult(self, result: Any) -> "TranslationRequest.Builder": - self._ui_translation_result = result - return self - - def setRequestId(self, id: int) -> "TranslationRequest.Builder": - self._request_id = id - return self - - def setTranslationConfig(self, config: Any) -> "TranslationRequest.Builder": - self._translation_config = config - return self - - def build(self) -> "TranslationRequest": - req = TranslationRequest() - req._source_text = self._source_text - req._source_locale = self._source_locale - req._target_locale = self._target_locale - req._translation_flags = self._translation_flags - req._ui_translation_result = self._ui_translation_result - req._request_id = self._request_id - req._translation_config = self._translation_config - return req - - def __init__(self): - super().__init__() - self._source_text: str = "" - self._source_locale: str = "" - self._target_locale: str = "" - self._request_id: int = 0 - self._translation_flags: int = 0 - self._ui_translation_result: Any = None - self._translation_config: Any = None - - def getSourceText(self) -> str: - return self._source_text - - def getSourceLocale(self) -> str: - return self._source_locale - - def getTargetLocale(self) -> str: - return self._target_locale - - def getRequestId(self) -> int: - return self._request_id - - def getTranslationFlags(self) -> int: - return self._translation_flags - - -# ═══════════════════════════════════════════════════════════════ -# TranslationResponse -# ═══════════════════════════════════════════════════════════════ - -class TranslationResponse(Stub): - """TranslationResponse — 翻译响应""" - - class Builder(Stub): - """TranslationResponse.Builder — 翻译响应构建器""" - - def __init__(self): - super().__init__() - self._translation_result: Optional[TranslationResult] = None - self._status: int = 0 - - def setTranslationResult(self, result: TranslationResult) -> "TranslationResponse.Builder": - self._translation_result = result - return self - - def setStatus(self, status: int) -> "TranslationResponse.Builder": - self._status = status - return self - - def build(self) -> "TranslationResponse": - resp = TranslationResponse() - resp._translation_result = self._translation_result - resp._status = self._status - return resp - - def __init__(self): - super().__init__() - self._translation_result: Optional[TranslationResult] = None - self._status: int = 0 - - def getTranslationResult(self) -> Optional[TranslationResult]: - return self._translation_result - - def getStatus(self) -> int: - return self._status - - -# ═══════════════════════════════════════════════════════════════ -# Translator -# ═══════════════════════════════════════════════════════════════ - -class Translator(Stub): - """Translator — 翻译器""" - - def __init__(self): - super().__init__() - self._supported_locales: List[str] = ["en", "zh", "ja", "ko", "fr", "de", "es"] - - def translate(self, text: str, sourceLocale: str, - targetLocale: str) -> str: - # Basic stub: return original text with a prefix - if sourceLocale == targetLocale: - return text - return f"[{sourceLocale}->{targetLocale}] {text}" - - def destroy(self): - pass - - def getSupportedLocales(self) -> List[str]: - return list(self._supported_locales) - - -# ═══════════════════════════════════════════════════════════════ -# TranslationManager -# ═══════════════════════════════════════════════════════════════ - -class TranslationManager(Stub): - """TranslationManager — 翻译管理器""" - - EXTRA_CAPABILITIES_FROM_FRAMEWORK = "android.view.translation.extra.CAPABILITIES_FROM_FRAMEWORK" - EXTRA_TRANSLATION_REQUEST_RESULT = "android.view.translation.extra.TRANSLATION_REQUEST_RESULT" - - def __init__(self): - super().__init__() - self._translators: Dict[str, Translator] = {} - - def startTranslation(self, request: TranslationRequest) -> TranslationResponse: - builder = TranslationResponse.Builder() - try: - source = request.getSourceLocale() - target = request.getTargetLocale() - text = request.getSourceText() - - translator = self._get_or_create_translator() - translated = translator.translate(text, source, target) - - result = TranslationResult( - translatedText=translated, - sourceText=text, - translationStatus=0 - ) - builder.setTranslationResult(result) - builder.setStatus(0) - except Exception as e: - builder.setStatus(1) - return builder.build() - - def createOnDeviceTranslation(self) -> Translator: - translator = Translator() - return translator - - def getTranslationCapabilities(self, sourceLocale: str, - targetLocale: str) -> List[TranslationCapability]: - return [ - TranslationCapability( - state=TranslationCapability.STATE_AVAILABLE, - sourceLocale=sourceLocale, - targetLocale=targetLocale, - textTranslationEnabled=True - ) - ] - - def onTranslationRequest(self, request: TranslationRequest): - pass - - def isTranslationSupported(self) -> bool: - return True - - def isOnDeviceTranslationSupported(self) -> bool: - return True - - def isULTRADeviceSupported(self) -> bool: - return False - - def _get_or_create_translator(self) -> Translator: - return Translator() - - -# ═══════════════════════════════════════════════════════════════ -# ViewTranslation / ViewTranslationRequest / ViewTranslationResponse (stubs) -# ═══════════════════════════════════════════════════════════════ - -class ViewTranslation(Stub): - """ViewTranslation — 视图翻译""" - - def __init__(self, autofillId: Any = None, value: str = "", - translationResult: Optional[TranslationResult] = None): - super().__init__() - self._autofill_id = autofillId - self._value = value - self._translation_result = translationResult - - def getAutofillId(self) -> Any: - return self._autofill_id - - def getValue(self) -> str: - return self._value - - def getTranslationResult(self) -> Optional[TranslationResult]: - return self._translation_result - - def setTranslationResult(self, result: TranslationResult): - self._translation_result = result - - -class ViewTranslationRequest(Stub): - """ViewTranslationRequest — 视图翻译请求""" - - def __init__(self, autofillId: Any = None, value: str = "", - requestId: int = 0): - super().__init__() - self._autofill_id = autofillId - self._value = value - self._request_id = requestId - - def getAutofillId(self) -> Any: - return self._autofill_id - - def getValue(self) -> str: - return self._value - - def getRequestId(self) -> int: - return self._request_id - - -class ViewTranslationResponse(Stub): - """ViewTranslationResponse — 视图翻译响应""" - - def __init__(self, autofillId: Any = None, value: str = "", - extras: Any = None): - super().__init__() - self._autofill_id = autofillId - self._value = value - self._extras = extras - - def getAutofillId(self) -> Any: - return self._autofill_id - - def getValue(self) -> str: - return self._value - - def getExtras(self) -> Any: - return self._extras - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "TranslationManager", - "TranslationRequest", - "TranslationResponse", - "TranslationResult", - "TranslationCapability", - "Translator", - "TranslationContext", - "TranslationSpec", - "ViewTranslation", - "ViewTranslationRequest", - "ViewTranslationResponse", -] \ No newline at end of file diff --git a/android_pyapi/android/view_window.py b/android_pyapi/android/view_window.py deleted file mode 100644 index fff87e2..0000000 --- a/android_pyapi/android/view_window.py +++ /dev/null @@ -1,969 +0,0 @@ -""" -Android Window 组件 — Window, WindowManager, WindowManager.LayoutParams, -PhoneWindow, Window.Callback, WindowMetrics, SplashScreen, -WindowContext, WindowManagerGlobal 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -from typing import Any, Callable, Dict, List, Optional, Tuple - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "Window", "WindowManager", "PhoneWindow", "WindowMetrics", - "WindowMetricsCalculator", "SplashScreen", "SplashScreenView", - "SplashScreenManager", "WindowContext", "WindowContextController", - "WindowHideScrollCaptureTarget", "WindowLayoutBuilder", - "WindowLayoutObserver", "WindowManagerGlobal", "WindowManagerPolicy", - "WindowProviderService", -] - - -# ═══════════════════════════════════════════════════════════════ -# 窗口常量 -# ═══════════════════════════════════════════════════════════════ - -TYPE_BASE_APPLICATION = 1 -TYPE_APPLICATION = 2 -TYPE_APPLICATION_STARTING = 3 -TYPE_APPLICATION_PANEL = 1000 -TYPE_APPLICATION_MEDIA = 1001 -TYPE_APPLICATION_SUB_PANEL = 1002 -TYPE_APPLICATION_ABOVE_SUB_PANEL = 1003 -TYPE_APPLICATION_ATTACHED_DIALOG = 1004 -TYPE_STATUS_BAR = 2000 -TYPE_SEARCH_BAR = 2001 -TYPE_PHONE = 2002 -TYPE_SYSTEM_ALERT = 2003 -TYPE_KEYGUARD = 2004 -TYPE_TOAST = 2005 -TYPE_SYSTEM_OVERLAY = 2006 -TYPE_PRIORITY_PHONE = 2007 -TYPE_SYSTEM_DIALOG = 2008 -TYPE_KEYGUARD_DIALOG = 2009 -TYPE_SYSTEM_ERROR = 2010 -TYPE_INPUT_METHOD = 2011 -TYPE_INPUT_METHOD_DIALOG = 2012 -TYPE_WALLPAPER = 2013 -TYPE_STATUS_BAR_PANEL = 2014 -TYPE_SECURE_SYSTEM_OVERLAY = 2015 -TYPE_DRAG = 2016 -TYPE_STATUS_BAR_SUBSCEEN = 2017 -TYPE_POINTER = 2018 -TYPE_NAVIGATION_BAR = 2019 -TYPE_VOLUME_OVERLAY = 2020 -TYPE_BOOT_PROGRESS = 2021 -TYPE_NAVIGATION_BAR_PANEL = 2024 -TYPE_APPLICATION_OVERLAY = 2025 -TYPE_UNIVERSE_BACKGROUND = 2026 -TYPE_DISPLAY_OVERLAY = 2027 -TYPE_MAGNIFICATION_OVERLAY = 2028 -TYPE_INPUT_CONSUMER = 2029 -TYPE_ACCESSIBILITY_OVERLAY = 2030 -TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY = 2031 -TYPE_NOTIFICATION_SHADE = 2032 -TYPE_STATUS_BAR_ADDITIONAL_INFO = 2033 -TYPE_KEYGUARD_SCRIM = 2034 - -FLAG_NOT_FOCUSABLE = 0x00000008 -FLAG_NOT_TOUCHABLE = 0x00000010 -FLAG_NOT_TOUCH_MODAL = 0x00000020 -FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040 -FLAG_KEEP_SCREEN_ON = 0x00000080 -FLAG_LAYOUT_IN_SCREEN = 0x00000100 -FLAG_LAYOUT_NO_LIMITS = 0x00000200 -FLAG_FULLSCREEN = 0x00000400 -FLAG_FORCE_NOT_FULLSCREEN = 0x00000800 -FLAG_DITHER = 0x00001000 -FLAG_SECURE = 0x00002000 -FLAG_SCALED = 0x00004000 -FLAG_IGNORE_CHEEK_PRESSES = 0x00008000 -FLAG_LAYOUT_INSET_DECOR = 0x00010000 -FLAG_ALT_FOCUSABLE_IM = 0x00020000 -FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000 -FLAG_SHOW_WHEN_LOCKED = 0x00080000 -FLAG_SHOW_WALLPAPER = 0x00100000 -FLAG_TURN_SCREEN_ON = 0x00200000 -FLAG_DISMISS_KEYGUARD = 0x00400000 -FLAG_SPLIT_TOUCH = 0x01000000 -FLAG_HARDWARE_ACCELERATED = 0x10000000 -FLAG_LOCAL_FOCUS_MODE = 0x10000000 -FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000 -FLAG_TRANSLUCENT_STATUS = 0x04000000 -FLAG_TRANSLUCENT_NAVIGATION = 0x08000000 -FLAG_BLUR_BEHIND = 0x00000004 -FLAG_DIM_BEHIND = 0x00000002 - -FEATURE_NO_TITLE = 1 -FEATURE_INDETERMINATE_PROGRESS = 5 -FEATURE_PROGRESS = 2 -FEATURE_CUSTOM_TITLE = 7 -FEATURE_LEFT_ICON = 3 -FEATURE_RIGHT_ICON = 4 -FEATURE_ACTION_BAR = 8 -FEATURE_ACTION_BAR_OVERLAY = 9 -FEATURE_ACTION_MODE_OVERLAY = 10 -FEATURE_CONTENT_TRANSITIONS = 12 -FEATURE_ACTIVITY_TRANSITIONS = 13 - -SOFT_INPUT_ADJUST_NOTHING = 0x00000030 -SOFT_INPUT_ADJUST_PAN = 0x00000020 -SOFT_INPUT_ADJUST_RESIZE = 0x00000010 -SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00000000 -SOFT_INPUT_STATE_ALWAYS_HIDDEN = 0x00000003 -SOFT_INPUT_STATE_ALWAYS_VISIBLE = 0x00000005 -SOFT_INPUT_STATE_HIDDEN = 0x00000002 -SOFT_INPUT_STATE_UNCHANGED = 0x00000001 -SOFT_INPUT_STATE_VISIBLE = 0x00000004 -SOFT_INPUT_MASK_ADJUST = 0x000000F0 -SOFT_INPUT_MASK_STATE = 0x0000000F -SOFT_INPUT_IS_FORWARD_NAVIGATION = 0x10000000 - -ROTATION_0 = 0 -ROTATION_90 = 1 -ROTATION_180 = 2 -ROTATION_270 = 3 - -LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0 -LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES = 1 -LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2 -LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS = 3 - - -# ═══════════════════════════════════════════════════════════════ -# WindowManager.LayoutParams -# ═══════════════════════════════════════════════════════════════ - -class WindowManager: - """WindowManager — 窗口管理器接口""" - - class LayoutParams(Stub): - """WindowManager.LayoutParams — 窗口布局参数""" - - TYPE_BASE_APPLICATION = TYPE_BASE_APPLICATION - TYPE_APPLICATION = TYPE_APPLICATION - TYPE_APPLICATION_PANEL = TYPE_APPLICATION_PANEL - TYPE_APPLICATION_MEDIA = TYPE_APPLICATION_MEDIA - TYPE_APPLICATION_SUB_PANEL = TYPE_APPLICATION_SUB_PANEL - TYPE_STATUS_BAR = TYPE_STATUS_BAR - TYPE_SYSTEM_ALERT = TYPE_SYSTEM_ALERT - TYPE_KEYGUARD = TYPE_KEYGUARD - TYPE_TOAST = TYPE_TOAST - TYPE_SYSTEM_OVERLAY = TYPE_SYSTEM_OVERLAY - TYPE_INPUT_METHOD = TYPE_INPUT_METHOD - TYPE_WALLPAPER = TYPE_WALLPAPER - TYPE_APPLICATION_OVERLAY = TYPE_APPLICATION_OVERLAY - TYPE_ACCESSIBILITY_OVERLAY = TYPE_ACCESSIBILITY_OVERLAY - - FLAG_NOT_FOCUSABLE = FLAG_NOT_FOCUSABLE - FLAG_NOT_TOUCHABLE = FLAG_NOT_TOUCHABLE - FLAG_KEEP_SCREEN_ON = FLAG_KEEP_SCREEN_ON - FLAG_FULLSCREEN = FLAG_FULLSCREEN - FLAG_SECURE = FLAG_SECURE - FLAG_SHOW_WHEN_LOCKED = FLAG_SHOW_WHEN_LOCKED - FLAG_TURN_SCREEN_ON = FLAG_TURN_SCREEN_ON - FLAG_DISMISS_KEYGUARD = FLAG_DISMISS_KEYGUARD - FLAG_TRANSLUCENT_STATUS = FLAG_TRANSLUCENT_STATUS - FLAG_TRANSLUCENT_NAVIGATION = FLAG_TRANSLUCENT_NAVIGATION - FLAG_HARDWARE_ACCELERATED = FLAG_HARDWARE_ACCELERATED - FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS - - SOFT_INPUT_ADJUST_NOTHING = SOFT_INPUT_ADJUST_NOTHING - SOFT_INPUT_ADJUST_PAN = SOFT_INPUT_ADJUST_PAN - SOFT_INPUT_ADJUST_RESIZE = SOFT_INPUT_ADJUST_RESIZE - SOFT_INPUT_MASK_ADJUST = SOFT_INPUT_MASK_ADJUST - SOFT_INPUT_MASK_STATE = SOFT_INPUT_MASK_STATE - - def __init__(self, w: int = -1, h: int = -1, - type_: int = TYPE_APPLICATION, - flags: int = 0, format: int = -1): - super().__init__() - self.width = w - self.height = h - self.type = type_ - self.flags = flags - self.format = format - self.x = 0 - self.y = 0 - self.horizontalMargin = 0.0 - self.verticalMargin = 0.0 - self.alpha = 1.0 - self.dimAmount = 0.0 - self.screenOrientation = ROTATION_0 - self.softInputMode = SOFT_INPUT_ADJUST_NOTHING - self.gravity = 0 - self.windowAnimations = 0 - self.token: Any = None - self.packageName: str = "" - self.title: str = "" - self.windowManager: Any = None - self.buttonBrightness = -1.0 - self.screenBrightness = -1.0 - self.rotationAnimation = 0 - self.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT - self.preferredDisplayModeId = 0 - self.colorMode = 0 - self.fitInsetsTypes = 0 - self.fitInsetsSides = 0 - self.wideColorGamut = False - self.captionMode = 0 - - def setTitle(self, title: str): - self.title = title - - def getTitle(self) -> str: - return self.title - - def setGravity(self, gravity: int): - self.gravity = gravity - - def getGravity(self) -> int: - return self.gravity - - def setSoftInputMode(self, mode: int): - self.softInputMode = mode - - def getSoftInputMode(self) -> int: - return self.softInputMode - - def setFlags(self, flags: int, mask: int): - self.flags = (self.flags & ~mask) | (flags & mask) - - def getFlags(self) -> int: - return self.flags - - def toString(self) -> str: - return (f"LayoutParams({self.width}x{self.height}, " - f"type={self.type}, flags=0x{self.flags:08x})") - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def __init__(self): - super().__init__() - - def addView(self, view: Any, params: LayoutParams): - raise AndroidOnlyError("WindowManager.addView() is not available on desktop") - - def updateViewLayout(self, view: Any, params: LayoutParams): - raise AndroidOnlyError("WindowManager.updateViewLayout() is not available on desktop") - - def removeView(self, view: Any): - raise AndroidOnlyError("WindowManager.removeView() is not available on desktop") - - def removeViewImmediate(self, view: Any): - raise AndroidOnlyError("WindowManager.removeViewImmediate() is not available on desktop") - - def getDefaultDisplay(self) -> Any: - return None - - def getCurrentWindowMetrics(self) -> "WindowMetrics": - return WindowMetrics(width=1920, height=1080) - - def getMaximumWindowMetrics(self) -> "WindowMetrics": - return WindowMetrics(width=1920, height=1080) - - def toString(self) -> str: - return "WindowManager" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# Window.Callback -# ═══════════════════════════════════════════════════════════════ - -class Window: - """Window — 窗口基类""" - - class Callback(Stub): - """Window.Callback — 窗口回调""" - - def onWindowAttributesChanged(self, attrs: Any): - pass - - def onWindowFocusChanged(self, hasFocus: bool): - pass - - def dispatchKeyEvent(self, event: Any) -> bool: - return False - - def dispatchTouchEvent(self, event: Any) -> bool: - return False - - def dispatchTrackballEvent(self, event: Any) -> bool: - return False - - def dispatchGenericMotionEvent(self, event: Any) -> bool: - return False - - def onContentChanged(self): - pass - - def toString(self) -> str: - return f"Window.Callback@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - class OnFrameMetricsAvailableListener(Stub): - """Window.OnFrameMetricsAvailableListener — 帧指标监听器""" - - def onFrameMetricsAvailable(self, window: "Window", frameMetrics: Any, - dropCountSinceLastInvocation: int): - pass - - def toString(self) -> str: - return f"OnFrameMetricsAvailableListener@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - class OnRestrictedCaptionAreaChangedListener(Stub): - """Window.OnRestrictedCaptionAreaChangedListener — 受限标题区域变化监听器""" - - def onRestrictedCaptionAreaChanged(self, rect: Any): - pass - - def toString(self) -> str: - return f"OnRestrictedCaptionAreaChangedListener@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - def __init__(self, context: Any = None): - super().__init__() - self._context = context - self._callback: Optional[Window.Callback] = None - self._window_manager = WindowManager() - self._features: Dict[int, bool] = {} - self._local_features: Dict[int, bool] = {} - self._decor: Any = None - self._container: Any = None - self._attributes = WindowManager.LayoutParams() - self._is_active = False - - def getContext(self) -> Any: - return self._context - - def getCallback(self) -> Optional[Callback]: - return self._callback - - def setCallback(self, callback: Callback): - self._callback = callback - - def getWindowManager(self) -> WindowManager: - return self._window_manager - - def setWindowManager(self, wm: WindowManager, pm: Any, appName: str = ""): - self._window_manager = wm - - def getAttributes(self) -> WindowManager.LayoutParams: - return self._attributes - - def setAttributes(self, attrs: WindowManager.LayoutParams): - self._attributes = attrs - - def addFlags(self, flags: int): - self._attributes.flags |= flags - - def clearFlags(self, flags: int): - self._attributes.flags &= ~flags - - def setFlags(self, flags: int, mask: int): - self._attributes.setFlags(flags, mask) - - def setGravity(self, gravity: int): - self._attributes.setGravity(gravity) - - def setSoftInputMode(self, mode: int): - self._attributes.setSoftInputMode(mode) - - def setTitle(self, title: str): - self._attributes.setTitle(title) - - def setType(self, type_: int): - self._attributes.type = type_ - - def setFormat(self, format: int): - self._attributes.format = format - - def setWindowAnimations(self, resId: int): - self._attributes.windowAnimations = resId - - def requestFeature(self, featureId: int) -> bool: - self._features[featureId] = True - return True - - def hasFeature(self, featureId: int) -> bool: - return self._features.get(featureId, False) - - def isActive(self) -> bool: - return self._is_active - - def setContainer(self, container: Any): - self._container = container - - def getContainer(self) -> Any: - return self._container - - def makeActive(self): - self._is_active = True - - def makeInactive(self): - self._is_active = False - - def getDecorView(self) -> Any: - return self._decor - - def setDecorView(self, view: Any): - self._decor = view - - def injectInputEvent(self, event: Any): - pass - - def superDispatchKeyEvent(self, event: Any) -> bool: - return False - - def superDispatchTouchEvent(self, event: Any) -> bool: - return False - - def superDispatchTrackballEvent(self, event: Any) -> bool: - return False - - def superDispatchGenericMotionEvent(self, event: Any) -> bool: - return False - - def toString(self) -> str: - return f"Window(active={self._is_active})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# PhoneWindow -# ═══════════════════════════════════════════════════════════════ - -class PhoneWindow(Window): - """PhoneWindow — 电话窗口""" - - def __init__(self, context: Any = None): - super().__init__(context=context) - self._is_phone = True - self._status_bar_color = 0 - self._navigation_bar_color = 0 - self._is_status_bar_visible = True - self._is_navigation_bar_visible = True - - def setStatusBarColor(self, color: int): - self._status_bar_color = color - - def getStatusBarColor(self) -> int: - return self._status_bar_color - - def setNavigationBarColor(self, color: int): - self._navigation_bar_color = color - - def getNavigationBarColor(self) -> int: - return self._navigation_bar_color - - def setStatusBarVisibility(self, visible: bool): - self._is_status_bar_visible = visible - - def setNavigationBarVisibility(self, visible: bool): - self._is_navigation_bar_visible = visible - - def isStatusBarVisible(self) -> bool: - return self._is_status_bar_visible - - def isNavigationBarVisible(self) -> bool: - return self._is_navigation_bar_visible - - def toString(self) -> str: - return f"PhoneWindow(statusBar={self._is_status_bar_visible})" - - -# ═══════════════════════════════════════════════════════════════ -# WindowContentFrameStats / WindowFrameStats -# ═══════════════════════════════════════════════════════════════ - -class WindowContentFrameStats(Stub): - """WindowContentFrameStats — 窗口内容帧统计""" - - def __init__(self): - super().__init__() - self._frames_count = 0 - self._dropped_count = 0 - self._total_duration_ns = 0 - - def getFramesCount(self) -> int: - return self._frames_count - - def getDroppedCount(self) -> int: - return self._dropped_count - - def getTotalDurationNanos(self) -> int: - return self._total_duration_ns - - def toString(self) -> str: - return f"WindowContentFrameStats(frames={self._frames_count}, dropped={self._dropped_count})" - - -class WindowFrameStats(Stub): - """WindowFrameStats — 窗口帧统计""" - - def __init__(self): - super().__init__() - self._frames_count = 0 - self._jank_count = 0 - self._percentile_95 = 0.0 - self._percentile_99 = 0.0 - - def getFramesCount(self) -> int: - return self._frames_count - - def getJankCount(self) -> int: - return self._jank_count - - def getP95DurationMillis(self) -> float: - return self._percentile_95 - - def getP99DurationMillis(self) -> float: - return self._percentile_99 - - def toString(self) -> str: - return f"WindowFrameStats(frames={self._frames_count}, jank={self._jank_count})" - - -# ═══════════════════════════════════════════════════════════════ -# WindowMetrics -# ═══════════════════════════════════════════════════════════════ - -class WindowMetrics(Stub): - """WindowMetrics — 窗口指标""" - - def __init__(self, width: int = 0, height: int = 0, - densityDpi: int = 320, density: float = 2.0, - scaledDensity: float = 2.0, xdpi: float = 320.0, - ydpi: float = 320.0): - super().__init__() - self._width = width - self._height = height - self._density_dpi = densityDpi - self._density = density - self._scaled_density = scaledDensity - self._xdpi = xdpi - self._ydpi = ydpi - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def getDensityDpi(self) -> int: - return self._density_dpi - - def getDensity(self) -> float: - return self._density - - def getScaledDensity(self) -> float: - return self._scaled_density - - def getXdpi(self) -> float: - return self._xdpi - - def getYdpi(self) -> float: - return self._ydpi - - def getBounds(self) -> Any: - return (0, 0, self._width, self._height) - - def toString(self) -> str: - return f"WindowMetrics({self._width}x{self._height})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, WindowMetrics): - return False - return self._width == other._width and self._height == other._height - - def hashCode(self) -> int: - return hash((self._width, self._height)) & 0x7FFFFFFF - - -class WindowMetricsCalculator(Stub): - """WindowMetricsCalculator — 窗口指标计算器""" - - @staticmethod - def getOrCreate() -> "WindowMetricsCalculator": - return WindowMetricsCalculator() - - def computeCurrentWindowMetrics(self, context: Any) -> WindowMetrics: - return WindowMetrics(width=1920, height=1080) - - def computeMaximumWindowMetrics(self, context: Any) -> WindowMetrics: - return WindowMetrics(width=1920, height=1080) - - def toString(self) -> str: - return "WindowMetricsCalculator" - - -class WindowMetricsCalculatorDecorator(Stub): - """WindowMetricsCalculatorDecorator — 窗口指标装饰器""" - - def __init__(self, base: Optional[WindowMetricsCalculator] = None): - super().__init__() - self._base = base - - def computeCurrentWindowMetrics(self, context: Any) -> WindowMetrics: - if self._base: - return self._base.computeCurrentWindowMetrics(context) - return WindowMetrics(width=1920, height=1080) - - def computeMaximumWindowMetrics(self, context: Any) -> WindowMetrics: - if self._base: - return self._base.computeMaximumWindowMetrics(context) - return WindowMetrics(width=1920, height=1080) - - -# ═══════════════════════════════════════════════════════════════ -# SplashScreen / SplashScreenView / SplashScreenManager -# ═══════════════════════════════════════════════════════════════ - -class SplashScreen(Stub): - """SplashScreen — 启动屏""" - - SPLASH_SCREEN_STYLE_SOLID_COLOR = 0 - SPLASH_SCREEN_STYLE_ICON = 1 - SPLASH_SCREEN_STYLE_ANIMATED_ICON = 2 - - def __init__(self): - super().__init__() - self._icon: Any = None - self._background_color: int = 0 - self._animation_duration: int = 0 - self._style = SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR - - def setSplashScreenIcon(self, icon: Any): - self._icon = icon - - def getSplashScreenIcon(self) -> Any: - return self._icon - - def setBackgroundColor(self, color: int): - self._background_color = color - - def getBackgroundColor(self) -> int: - return self._background_color - - def setAnimationDuration(self, duration: int): - self._animation_duration = duration - - def getAnimationDuration(self) -> int: - return self._animation_duration - - def setStyle(self, style: int): - self._style = style - - def getStyle(self) -> int: - return self._style - - def toString(self) -> str: - return f"SplashScreen(style={self._style})" - - -class SplashScreenView(Stub): - """SplashScreenView — 启动屏视图""" - - def __init__(self): - super().__init__() - self._visible = False - self._icon: Any = None - self._branding_image: Any = None - - def isVisible(self) -> bool: - return self._visible - - def setVisible(self, visible: bool): - self._visible = visible - - def getIconView(self) -> Any: - return self._icon - - def getBrandingImageView(self) -> Any: - return self._branding_image - - def remove(self): - self._visible = False - - def toString(self) -> str: - return f"SplashScreenView(visible={self._visible})" - - -class SplashScreenManager(Stub): - """SplashScreenManager — 启动屏管理器""" - - def __init__(self): - super().__init__() - self._splash_screens: Dict[Any, SplashScreen] = {} - - def getSplashScreen(self, activity: Any) -> Optional[SplashScreen]: - return self._splash_screens.get(id(activity)) - - def installSplashScreen(self, activity: Any) -> SplashScreen: - screen = SplashScreen() - self._splash_screens[id(activity)] = screen - return screen - - def uninstallSplashScreen(self, activity: Any): - self._splash_screens.pop(id(activity), None) - - def toString(self) -> str: - return f"SplashScreenManager(screens={len(self._splash_screens)})" - - -# ═══════════════════════════════════════════════════════════════ -# WindowContext / WindowContextController -# ═══════════════════════════════════════════════════════════════ - -class WindowContext(Stub): - """WindowContext — 窗口上下文""" - - def __init__(self, context: Any = None, windowType: int = TYPE_APPLICATION): - super().__init__() - self._base_context = context - self._window_type = windowType - self._resources: Any = None - - def getBaseContext(self) -> Any: - return self._base_context - - def getWindowType(self) -> int: - return self._window_type - - def getResources(self) -> Any: - return self._resources - - def setResources(self, resources: Any): - self._resources = resources - - def toString(self) -> str: - return f"WindowContext(type={self._window_type})" - - -class WindowContextController(Stub): - """WindowContextController — 窗口上下文控制器""" - - def __init__(self, windowContext: Optional[WindowContext] = None): - super().__init__() - self._window_context = windowContext - - def attachToDisplay(self, displayId: int): - pass - - def detach(self): - pass - - def toString(self) -> str: - return f"WindowContextController(ctx={self._window_context})" - - -# ═══════════════════════════════════════════════════════════════ -# WindowHideScrollCaptureTarget -# ═══════════════════════════════════════════════════════════════ - -class WindowHideScrollCaptureTarget(Stub): - """WindowHideScrollCaptureTarget — 窗口隐藏滚动捕获目标""" - - def __init__(self, view: Any = None): - super().__init__() - self._view = view - - def getView(self) -> Any: - return self._view - - def toString(self) -> str: - return f"WindowHideScrollCaptureTarget(view={self._view})" - - -# ═══════════════════════════════════════════════════════════════ -# WindowLayoutBuilder / WindowLayoutObserver -# ═══════════════════════════════════════════════════════════════ - -class WindowLayoutBuilder(Stub): - """WindowLayoutBuilder — 窗口布局构建器""" - - def __init__(self): - super().__init__() - self._width = 0 - self._height = 0 - self._gravity = 0 - self._x = 0 - self._y = 0 - self._horizontal_margin = 0.0 - self._vertical_margin = 0.0 - - def setWidth(self, width: int) -> "WindowLayoutBuilder": - self._width = width - return self - - def setHeight(self, height: int) -> "WindowLayoutBuilder": - self._height = height - return self - - def setGravity(self, gravity: int) -> "WindowLayoutBuilder": - self._gravity = gravity - return self - - def setPosition(self, x: int, y: int) -> "WindowLayoutBuilder": - self._x = x - self._y = y - return self - - def build(self) -> WindowManager.LayoutParams: - params = WindowManager.LayoutParams(w=self._width, h=self._height) - params.gravity = self._gravity - params.x = self._x - params.y = self._y - return params - - def toString(self) -> str: - return f"WindowLayoutBuilder({self._width}x{self._height})" - - -class WindowLayoutObserver(Stub): - """WindowLayoutObserver — 窗口布局观察者""" - - def onLayoutChanged(self, layout: WindowManager.LayoutParams): - pass - - def toString(self) -> str: - return f"WindowLayoutObserver@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# WindowManagerGlobal / WindowManagerPolicy / WindowProviderService -# ═══════════════════════════════════════════════════════════════ - -class WindowManagerGlobal(Stub): - """WindowManagerGlobal — 全局窗口管理器""" - - _instance: Optional["WindowManagerGlobal"] = None - - @staticmethod - def getInstance() -> "WindowManagerGlobal": - if WindowManagerGlobal._instance is None: - WindowManagerGlobal._instance = WindowManagerGlobal() - return WindowManagerGlobal._instance - - def __init__(self): - super().__init__() - self._views: Dict[int, Any] = {} - self._params: Dict[int, WindowManager.LayoutParams] = {} - self._next_id = 0 - - def addView(self, view: Any, params: WindowManager.LayoutParams): - vid = id(view) - self._views[vid] = view - self._params[vid] = params - - def updateViewLayout(self, view: Any, params: WindowManager.LayoutParams): - vid = id(view) - if vid in self._params: - self._params[vid] = params - - def removeView(self, view: Any): - vid = id(view) - self._views.pop(vid, None) - self._params.pop(vid, None) - - def getWindowViews(self) -> List[Any]: - return list(self._views.values()) - - def getViewCount(self) -> int: - return len(self._views) - - def toString(self) -> str: - return f"WindowManagerGlobal(views={len(self._views)})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -class WindowManagerPolicy(Stub): - """WindowManagerPolicy — 窗口管理器策略""" - - WINDOW_MODE_FULLSCREEN = 0 - WINDOW_MODE_FREEFORM = 1 - WINDOW_MODE_SPLIT_SCREEN = 2 - WINDOW_MODE_PINNED = 3 - - def __init__(self): - super().__init__() - - def isKeyguardLocked(self) -> bool: - return False - - def isKeyguardSecure(self) -> bool: - return False - - def isScreenOn(self) -> bool: - return True - - def toString(self) -> str: - return "WindowManagerPolicy" - - -class WindowProviderService(Stub): - """WindowProviderService — 窗口提供者服务""" - - SERVICE_INTERFACE = "android.window.WindowProviderService" - - def __init__(self): - super().__init__() - - def onCreateWindowProvider(self): - pass - - def onWindowProviderAttached(self): - pass - - def onWindowProviderDetached(self): - pass - - def toString(self) -> str: - return f"WindowProviderService@{hex(id(self))}" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/android/webkit.py b/android_pyapi/android/webkit.py deleted file mode 100644 index e8000c7..0000000 --- a/android_pyapi/android/webkit.py +++ /dev/null @@ -1,992 +0,0 @@ -""" -Android WebKit 组件 — WebView, WebSettings, WebViewClient, WebChromeClient 等 -方法签名完全对齐 Android SDK -""" - -import os -import sys -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union - -from .._core import ( - Stub, AndroidOnlyError, android_context, - resolve_android_path, to_android_path, - _sdcard_root, _app_data_dir, _package_name, -) - - -# ═══════════════════════════════════════════════════════════════ -# WebResourceRequest / WebResourceResponse / WebResourceError -# ═══════════════════════════════════════════════════════════════ - -class WebResourceRequest(Stub): - """WebResourceRequest — 资源请求""" - - def __init__(self, url: str = "", is_for_main_frame: bool = False, - is_redirect: bool = False, method: str = "GET", - request_headers: Optional[Dict[str, str]] = None): - super().__init__() - self._url = url - self._is_for_main_frame = is_for_main_frame - self._is_redirect = is_redirect - self._method = method - self._request_headers = request_headers or {} - - def getUrl(self) -> str: - return self._url - - def isForMainFrame(self) -> bool: - return self._is_for_main_frame - - def isRedirect(self) -> bool: - return self._is_redirect - - def getMethod(self) -> str: - return self._method - - def getRequestHeaders(self) -> Dict[str, str]: - return dict(self._request_headers) - - def toString(self) -> str: - return f"WebResourceRequest(url={self._url})" - - -class WebResourceResponse(Stub): - """WebResourceResponse — 资源响应""" - - def __init__(self, mime_type: str = "", encoding: str = "", - data: Any = None, status_code: int = 200, - reason_phrase: str = "OK", - response_headers: Optional[Dict[str, str]] = None): - super().__init__() - self._mime_type = mime_type - self._encoding = encoding - self._data = data - self._status_code = status_code - self._reason_phrase = reason_phrase - self._response_headers = response_headers or {} - - def getMimeType(self) -> str: - return self._mime_type - - def getEncoding(self) -> str: - return self._encoding - - def getData(self) -> Any: - return self._data - - def getStatusCode(self) -> int: - return self._status_code - - def getReasonPhrase(self) -> str: - return self._reason_phrase - - def getResponseHeaders(self) -> Dict[str, str]: - return dict(self._response_headers) - - -class WebResourceError(Stub): - """WebResourceError — 资源错误""" - - ERROR_UNKNOWN = -1 - ERROR_HOST_LOOKUP = -2 - ERROR_UNSUPPORTED_AUTH_SCHEME = -3 - ERROR_AUTHENTICATION = -4 - ERROR_PROXY_AUTHENTICATION = -5 - ERROR_CONNECT = -6 - ERROR_IO = -7 - ERROR_TIMEOUT = -8 - ERROR_REDIRECT_LOOP = -9 - ERROR_UNSUPPORTED_SCHEME = -10 - ERROR_FAILED_SSL_HANDSHAKE = -11 - ERROR_BAD_URL = -12 - ERROR_FILE = -13 - ERROR_FILE_NOT_FOUND = -14 - ERROR_TOO_MANY_REQUESTS = -15 - - def __init__(self, error_code: int = ERROR_UNKNOWN, description: str = ""): - super().__init__() - self._error_code = error_code - self._description = description - - def getErrorCode(self) -> int: - return self._error_code - - def getDescription(self) -> str: - return self._description - - -# ═══════════════════════════════════════════════════════════════ -# WebMessage / WebMessagePort -# ═══════════════════════════════════════════════════════════════ - -class WebMessage(Stub): - """WebMessage — Web 消息""" - - def __init__(self, data: str = ""): - super().__init__() - self._data = data - - def getData(self) -> str: - return self._data - - -class WebMessagePort(Stub): - """WebMessagePort — Web 消息端口""" - - def postMessage(self, message: WebMessage): - pass - - def close(self): - pass - - def setWebMessageCallback(self, callback: Any): - pass - - -# ═══════════════════════════════════════════════════════════════ -# JavascriptInterface -# ═══════════════════════════════════════════════════════════════ - -class JavascriptInterface(Stub): - """JavascriptInterface — JavaScript 接口注解""" - - def __init__(self, value: str = ""): - super().__init__() - self._value = value - - def getValue(self) -> str: - return self._value - - -# ═══════════════════════════════════════════════════════════════ -# JsResult / JsPromptResult -# ═══════════════════════════════════════════════════════════════ - -class JsResult(Stub): - """JsResult — JS 对话框结果""" - - def __init__(self): - super().__init__() - self._confirmed = False - - def confirm(self): - self._confirmed = True - - def cancel(self): - self._confirmed = False - - -class JsPromptResult(JsResult): - """JsPromptResult — JS 提示结果""" - - def __init__(self): - super().__init__() - self._string_result: str = "" - - def confirm(self, result: str = ""): - self._confirmed = True - self._string_result = result - - def getStringResult(self) -> str: - return self._string_result - - -# ═══════════════════════════════════════════════════════════════ -# HttpAuthHandler / SslErrorHandler -# ═══════════════════════════════════════════════════════════════ - -class HttpAuthHandler(Stub): - """HttpAuthHandler — HTTP 认证处理器""" - - def __init__(self): - super().__init__() - self._proceeded = False - self._cancelled = False - - def proceed(self, username: str, password: str): - self._proceeded = True - - def cancel(self): - self._cancelled = True - - def useHttpAuthUsernamePassword(self) -> bool: - return False - - -class SslErrorHandler(Stub): - """SslErrorHandler — SSL 错误处理器""" - - def __init__(self): - super().__init__() - self._proceeded = False - self._cancelled = False - - def proceed(self): - self._proceeded = True - - def cancel(self): - self._cancelled = True - - -# ═══════════════════════════════════════════════════════════════ -# PermissionRequest -# ═══════════════════════════════════════════════════════════════ - -class PermissionRequest(Stub): - """PermissionRequest — 权限请求""" - - RESOURCE_VIDEO_CAPTURE = "android.webkit.resource.VIDEO_CAPTURE" - RESOURCE_AUDIO_CAPTURE = "android.webkit.resource.AUDIO_CAPTURE" - RESOURCE_PROTECTED_MEDIA_ID = "android.webkit.resource.PROTECTED_MEDIA_ID" - RESOURCE_MIDI_SYSEX = "android.webkit.resource.MIDI_SYSEX" - - def __init__(self): - super().__init__() - self._origin: Any = None - self._resources: List[str] = [] - self._granted_resources: List[str] = [] - - def getOrigin(self) -> Any: - return self._origin - - def getResources(self) -> List[str]: - return list(self._resources) - - def getGrantedResources(self) -> List[str]: - return list(self._granted_resources) - - def grant(self, resources: List[str]): - self._granted_resources = list(resources) - - def deny(self): - self._granted_resources.clear() - - -# ═══════════════════════════════════════════════════════════════ -# GeolocationPermissions / WebStorage -# ═══════════════════════════════════════════════════════════════ - -class GeolocationPermissions(Stub): - """GeolocationPermissions — 地理位置权限""" - - @staticmethod - def getInstance() -> "GeolocationPermissions": - return GeolocationPermissions() - - def allow(self, origin: str): - pass - - def clear(self, origin: str): - pass - - def clearAll(self): - pass - - def getAllowed(self, origin: str, callback: Any): - pass - - def getOrigins(self, callback: Any): - pass - - -class WebStorage(Stub): - """WebStorage — Web 存储""" - - @staticmethod - def getInstance() -> "WebStorage": - return WebStorage() - - def deleteAllData(self): - pass - - def deleteOrigin(self, origin: str): - pass - - def getOrigins(self, callback: Any): - pass - - def getQuotaForOrigin(self, origin: str, callback: Any): - pass - - def getUsageForOrigin(self, origin: str, callback: Any): - pass - - -# ═══════════════════════════════════════════════════════════════ -# WebBackForwardList / WebHistoryItem -# ═══════════════════════════════════════════════════════════════ - -class WebHistoryItem(Stub): - """WebHistoryItem — 历史项""" - - def __init__(self): - super().__init__() - self._url: str = "" - self._original_url: str = "" - self._title: str = "" - self._favicon: Any = None - - def getUrl(self) -> str: - return self._url - - def getOriginalUrl(self) -> str: - return self._original_url - - def getTitle(self) -> str: - return self._title - - def getFavicon(self) -> Any: - return self._favicon - - def toString(self) -> str: - return f"WebHistoryItem(url={self._url})" - - -class WebBackForwardList(Stub): - """WebBackForwardList — 前进后退列表""" - - def __init__(self): - super().__init__() - self._items: List[WebHistoryItem] = [] - self._current_index: int = -1 - - def getCurrentItem(self) -> Optional[WebHistoryItem]: - if 0 <= self._current_index < len(self._items): - return self._items[self._current_index] - return None - - def getCurrentIndex(self) -> int: - return self._current_index - - def getItemAtIndex(self, index: int) -> Optional[WebHistoryItem]: - if 0 <= index < len(self._items): - return self._items[index] - return None - - def getSize(self) -> int: - return len(self._items) - - -# ═══════════════════════════════════════════════════════════════ -# CookieManager / CookieSyncManager -# ═══════════════════════════════════════════════════════════════ - -class CookieManager(Stub): - """CookieManager — Cookie 管理器""" - - _instance: Optional["CookieManager"] = None - - def __init__(self): - super().__init__() - self._accept_cookies: bool = True - self._accept_third_party: bool = True - self._cookies: Dict[str, str] = {} - - @staticmethod - def getInstance() -> "CookieManager": - if CookieManager._instance is None: - CookieManager._instance = CookieManager() - return CookieManager._instance - - def setAcceptCookies(self, accept: bool): - self._accept_cookies = accept - - def acceptCookies(self) -> bool: - return self._accept_cookies - - def setCookie(self, url: str, value: str): - if self._accept_cookies: - self._cookies[url] = value - - def getCookie(self, url: str) -> Optional[str]: - return self._cookies.get(url) - - def setAcceptThirdPartyCookies(self, webview: Any, accept: bool): - self._accept_third_party = accept - - def acceptThirdPartyCookies(self) -> bool: - return self._accept_third_party - - def removeSessionCookie(self): - pass - - def removeAllCookies(self): - self._cookies.clear() - - def removeAllCookiesSync(self): - self._cookies.clear() - - def removeSessionCookies(self): - pass - - def flush(self): - pass - - def hasCookies(self) -> bool: - return bool(self._cookies) - - def removeExpiredCookie(self): - pass - - -class CookieSyncManager(Stub): - """CookieSyncManager — Cookie 同步管理器""" - - _instance: Optional["CookieSyncManager"] = None - - @staticmethod - def createInstance(context: Any) -> "CookieSyncManager": - if CookieSyncManager._instance is None: - CookieSyncManager._instance = CookieSyncManager() - return CookieSyncManager._instance - - @staticmethod - def getInstance() -> "CookieSyncManager": - if CookieSyncManager._instance is None: - CookieSyncManager._instance = CookieSyncManager() - return CookieSyncManager._instance - - def sync(self): - pass - - def startSync(self): - pass - - def stopSync(self): - pass - - def resetSync(self): - pass - - -# ═══════════════════════════════════════════════════════════════ -# WebSettings -# ═══════════════════════════════════════════════════════════════ - -class WebSettings(Stub): - """WebSettings — WebView 设置""" - - LOAD_DEFAULT = -1 - LOAD_CACHE_ELSE_NETWORK = 1 - LOAD_NO_CACHE = 2 - LOAD_CACHE_ONLY = 3 - - MIXED_CONTENT_ALWAYS_ALLOW = 0 - MIXED_CONTENT_NEVER_ALLOW = 1 - MIXED_CONTENT_COMPATIBILITY_MODE = 2 - - class LayoutAlgorithm(Stub): - NORMAL = 0 - SINGLE_COLUMN = 1 - NARROW_COLUMNS = 2 - TEXT_AUTOSIZING = 3 - - def __init__(self): - super().__init__() - self._js_enabled: bool = True - self._dom_storage_enabled: bool = True - self._allow_file_access: bool = True - self._allow_content_access: bool = True - self._allow_file_access_from_urls: bool = False - self._allow_universal_access_from_urls: bool = False - self._built_in_zoom_controls: bool = True - self._display_zoom_controls: bool = True - self._support_zoom: bool = True - self._load_with_overview_mode: bool = False - self._use_wide_viewport: bool = False - self._user_agent_string: str = "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36" - self._default_text_encoding: str = "UTF-8" - self._cache_mode: int = self.LOAD_DEFAULT - self._app_cache_enabled: bool = False - self._app_cache_path: str = "" - self._database_enabled: bool = False - self._geolocation_enabled: bool = False - self._js_can_open_windows: bool = False - self._loads_images_automatically: bool = True - self._block_network_image: bool = False - self._block_network_loads: bool = False - self._minimum_font_size: int = 8 - self._default_font_size: int = 16 - self._text_zoom: int = 100 - self._layout_algorithm: int = WebSettings.LayoutAlgorithm.NORMAL - self._mixed_content_mode: int = self.MIXED_CONTENT_NEVER_ALLOW - self._offscreen_pre_raster: bool = False - self._safe_browsing_enabled: bool = True - self._javascript_can_open_windows_automatically: bool = False - - def setJavaScriptEnabled(self, enabled: bool): - self._js_enabled = enabled - - def setDomStorageEnabled(self, enabled: bool): - self._dom_storage_enabled = enabled - - def setAllowFileAccess(self, allow: bool): - self._allow_file_access = allow - - def setAllowContentAccess(self, allow: bool): - self._allow_content_access = allow - - def setAllowFileAccessFromFileURLs(self, allow: bool): - self._allow_file_access_from_urls = allow - - def setAllowUniversalAccessFromFileURLs(self, allow: bool): - self._allow_universal_access_from_urls = allow - - def setBuiltInZoomControls(self, enabled: bool): - self._built_in_zoom_controls = enabled - - def setDisplayZoomControls(self, enabled: bool): - self._display_zoom_controls = enabled - - def setSupportZoom(self, support: bool): - self._support_zoom = support - - def setLoadWithOverviewMode(self, overview: bool): - self._load_with_overview_mode = overview - - def setUseWideViewPort(self, use: bool): - self._use_wide_viewport = use - - def setUserAgentString(self, ua: str): - self._user_agent_string = ua - - def getUserAgentString(self) -> str: - return self._user_agent_string - - def setDefaultTextEncodingName(self, encoding: str): - self._default_text_encoding = encoding - - def getDefaultTextEncodingName(self) -> str: - return self._default_text_encoding - - def setCacheMode(self, mode: int): - self._cache_mode = mode - - def getCacheMode(self) -> int: - return self._cache_mode - - def setAppCacheEnabled(self, enabled: bool): - self._app_cache_enabled = enabled - - def setAppCachePath(self, path: str): - self._app_cache_path = path - - def setDatabaseEnabled(self, enabled: bool): - self._database_enabled = enabled - - def setGeolocationEnabled(self, enabled: bool): - self._geolocation_enabled = enabled - - def setJavaScriptCanOpenWindowsAutomatically(self, auto: bool): - self._javascript_can_open_windows_automatically = auto - - def setLoadsImagesAutomatically(self, auto: bool): - self._loads_images_automatically = auto - - def setBlockNetworkImage(self, block: bool): - self._block_network_image = block - - def setBlockNetworkLoads(self, block: bool): - self._block_network_loads = block - - def setMinimumFontSize(self, size: int): - self._minimum_font_size = size - - def getMinimumFontSize(self) -> int: - return self._minimum_font_size - - def setDefaultFontSize(self, size: int): - self._default_font_size = size - - def getDefaultFontSize(self) -> int: - return self._default_font_size - - def setTextZoom(self, zoom: int): - self._text_zoom = zoom - - def getTextZoom(self) -> int: - return self._text_zoom - - def setLayoutAlgorithm(self, algo: int): - self._layout_algorithm = algo - - def getLayoutAlgorithm(self) -> int: - return self._layout_algorithm - - def setMixedContentMode(self, mode: int): - self._mixed_content_mode = mode - - def getMixedContentMode(self) -> int: - return self._mixed_content_mode - - def setOffscreenPreRaster(self, enabled: bool): - self._offscreen_pre_raster = enabled - - def getOffscreenPreRaster(self) -> bool: - return self._offscreen_pre_raster - - def setSafeBrowsingEnabled(self, enabled: bool): - self._safe_browsing_enabled = enabled - - def getSafeBrowsingEnabled(self) -> bool: - return self._safe_browsing_enabled - - def toString(self) -> str: - return f"WebSettings(js={self._js_enabled})" - - -# ═══════════════════════════════════════════════════════════════ -# WebViewClient -# ═══════════════════════════════════════════════════════════════ - -class WebViewClient(Stub): - """WebViewClient — WebView 客户端""" - - def onPageStarted(self, view: "WebView", url: str, favicon: Any): - pass - - def onPageFinished(self, view: "WebView", url: str): - pass - - def onLoadResource(self, view: "WebView", url: str): - pass - - def shouldOverrideUrlLoading(self, view: "WebView", request: Any) -> bool: - return False - - def shouldOverrideUrlLoading(self, view: "WebView", url: str) -> bool: - return False - - def shouldInterceptRequest(self, view: "WebView", request: WebResourceRequest) -> Optional[WebResourceResponse]: - return None - - def shouldInterceptRequest(self, view: "WebView", url: str) -> Optional[WebResourceResponse]: - return None - - def onReceivedHttpError(self, view: "WebView", request: WebResourceRequest, - error_response: WebResourceResponse): - pass - - def onReceivedSslError(self, view: "WebView", handler: SslErrorHandler, error: Any): - pass - - def onReceivedClientCertRequest(self, view: "WebView", request: Any): - pass - - def onReceivedHttpAuthRequest(self, view: "WebView", handler: HttpAuthHandler, - host: str, realm: str): - pass - - def onFormResubmission(self, view: "WebView", dont_resend: Any, resend: Any): - pass - - def doUpdateVisitedHistory(self, view: "WebView", url: str, is_reload: bool): - pass - - def onScaleChanged(self, view: "WebView", old_scale: float, new_scale: float): - pass - - def onUnhandledKeyEvent(self, view: "WebView", event: Any): - pass - - def shouldOverrideKeyEvent(self, view: "WebView", event: Any) -> bool: - return False - - -# ═══════════════════════════════════════════════════════════════ -# WebChromeClient -# ═══════════════════════════════════════════════════════════════ - -class WebChromeClient(Stub): - """WebChromeClient — WebView Chrome 客户端""" - - def onProgressChanged(self, view: "WebView", new_progress: int): - pass - - def onReceivedTitle(self, view: "WebView", title: str): - pass - - def onReceivedIcon(self, view: "WebView", icon: Any): - pass - - def onReceivedTouchIconUrl(self, view: "WebView", url: str, precomposed: bool): - pass - - def onShowCustomView(self, view: Any, callback: Any): - pass - - def onHideCustomView(self): - pass - - def onCreateWindow(self, view: "WebView", is_dialog: bool, - is_user_gesture: bool, result_msg: Any) -> bool: - return False - - def onRequestFocus(self, view: "WebView"): - pass - - def onCloseWindow(self, window: "WebView"): - pass - - def onJsAlert(self, view: "WebView", url: str, message: str, - result: JsResult) -> bool: - return False - - def onJsConfirm(self, view: "WebView", url: str, message: str, - result: JsResult) -> bool: - return False - - def onJsPrompt(self, view: "WebView", url: str, message: str, - default_value: str, result: JsPromptResult) -> bool: - return False - - def onJsBeforeUnload(self, view: "WebView", url: str, message: str, - result: JsResult) -> bool: - return False - - def onExceededDatabaseQuota(self, url: str, database_identifier: str, - quota: int, estimated_database_size: int, - total_quota: int, quota_updater: Any): - pass - - def onReachedMaxAppCacheSize(self, space_needed: int, total_quota: int, - quota_updater: Any): - pass - - def onGeolocationPermissionsShowPrompt(self, origin: str, - callback: Any): - pass - - def onGeolocationPermissionsHidePrompt(self): - pass - - def onPermissionRequest(self, request: PermissionRequest): - pass - - def onPermissionRequestCanceled(self, request: PermissionRequest): - pass - - def onConsoleMessage(self, message: Any) -> bool: - return True - - -# ═══════════════════════════════════════════════════════════════ -# WebView -# ═══════════════════════════════════════════════════════════════ - -class WebView(Stub): - """WebView — Web 视图""" - - _instance_count = 0 - - def __init__(self, context: Any = None, attrs: Any = None): - super().__init__() - WebView._instance_count += 1 - self._context = context - self._settings = WebSettings() - self._webview_client: Optional[WebViewClient] = None - self._chrome_client: Optional[WebChromeClient] = None - self._current_url: str = "" - self._original_url: str = "" - self._title: str = "" - self._progress: int = 100 - self._content_height: int = 0 - self._scale: float = 1.0 - self._background_color: int = 0xFFFFFFFF - self._back_forward_list = WebBackForwardList() - self._javascript_interfaces: Dict[str, Any] = {} - self._paused: bool = False - self._timers_paused: bool = False - self._network_available: bool = True - - def loadUrl(self, url: str, additional_headers: Optional[Dict[str, str]] = None): - self._current_url = url - self._original_url = url - self._title = url - self._progress = 0 - if self._webview_client: - try: - self._webview_client.onPageStarted(self, url, None) - self._webview_client.onPageFinished(self, url) - except Exception: - pass - self._progress = 100 - - def loadData(self, data: str, mime_type: str = "text/html", - encoding: str = "utf-8"): - self._current_url = "data:text/html;charset=utf-8," - self._progress = 100 - - def loadDataWithBaseURL(self, base_url: str, data: str, - mime_type: str, encoding: str, - history_url: str): - self._current_url = history_url or base_url - self._progress = 100 - - def setWebViewClient(self, client: WebViewClient): - self._webview_client = client - - def setWebChromeClient(self, client: WebChromeClient): - self._chrome_client = client - - def getSettings(self) -> WebSettings: - return self._settings - - def goBack(self): - pass - - def goForward(self): - pass - - def canGoBack(self) -> bool: - return False - - def canGoForward(self) -> bool: - return False - - def goBackOrForward(self, steps: int): - pass - - def reload(self): - pass - - def stopLoading(self): - pass - - def clearHistory(self): - pass - - def clearCache(self, include_disk_files: bool): - pass - - def clearView(self): - pass - - def destroy(self): - WebView._instance_count -= 1 - - def getUrl(self) -> Optional[str]: - return self._current_url or None - - def getTitle(self) -> Optional[str]: - return self._title or None - - def getProgress(self) -> int: - return self._progress - - def getContentHeight(self) -> int: - return self._content_height - - def getScale(self) -> float: - return self._scale - - def addJavascriptInterface(self, obj: Any, interface_name: str): - self._javascript_interfaces[interface_name] = obj - - def removeJavascriptInterface(self, interface_name: str): - if interface_name in self._javascript_interfaces: - del self._javascript_interfaces[interface_name] - - def evaluateJavascript(self, script: str, callback: Any = None): - pass - - def setInitialScale(self, scale_in_percent: int): - self._scale = scale_in_percent / 100.0 - - def setBackgroundColor(self, color: int): - self._background_color = color - - def scrollTo(self, x: int, y: int): - pass - - def scrollBy(self, x: int, y: int): - pass - - def pageUp(self, top: bool) -> bool: - return True - - def pageDown(self, bottom: bool) -> bool: - return True - - def findNext(self, forward: bool): - pass - - def findAll(self, find: str) -> int: - return 0 - - def findAllAsync(self, find: str): - pass - - def clearMatches(self): - pass - - def zoomIn(self): - self._scale *= 1.25 - - def zoomOut(self): - self._scale /= 1.25 - - def setNetworkAvailable(self, available: bool): - self._network_available = available - - def pauseTimers(self): - self._timers_paused = True - - def resumeTimers(self): - self._timers_paused = False - - def onPause(self): - self._paused = True - - def onResume(self): - self._paused = False - - def onWindowFocusChanged(self, has_focus: bool): - pass - - def setLayoutParams(self, params: Any): - pass - - def requestFocus(self, direction: int = 0): - pass - - def getOriginalUrl(self) -> Optional[str]: - return self._original_url or None - - def canZoomIn(self) -> bool: - return True - - def canZoomOut(self) -> bool: - return self._scale > 0.25 - - def toString(self) -> str: - return f"WebView(url={self._current_url})" - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - "WebView", - "WebSettings", - "WebViewClient", - "WebChromeClient", - "WebResourceRequest", - "WebResourceResponse", - "WebResourceError", - "WebMessage", - "WebMessagePort", - "JavascriptInterface", - "CookieManager", - "CookieSyncManager", - "JsResult", - "JsPromptResult", - "HttpAuthHandler", - "SslErrorHandler", - "PermissionRequest", - "GeolocationPermissions", - "WebStorage", - "WebBackForwardList", - "WebHistoryItem", -] \ No newline at end of file diff --git a/android_pyapi/android/webkit_ext.py b/android_pyapi/android/webkit_ext.py deleted file mode 100644 index 1878d76..0000000 --- a/android_pyapi/android/webkit_ext.py +++ /dev/null @@ -1,1554 +0,0 @@ -""" -Android WebKit 扩展组件 — WebView, WebSettings, WebViewClient, WebChromeClient 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import os -import json -import threading -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# WebView 常量 -# ═══════════════════════════════════════════════════════════════ - -LOAD_DEFAULT = 0 -LOAD_CACHE_ONLY = 1 -LOAD_CACHE_ELSE_NETWORK = 2 -LOAD_NO_CACHE = 3 -LOAD_NORMAL = 0 - -ERROR_UNKNOWN = -1 -ERROR_HOST_LOOKUP = -2 -ERROR_UNSUPPORTED_AUTH_SCHEME = -3 -ERROR_AUTHENTICATION = -4 -ERROR_PROXY_AUTHENTICATION = -5 -ERROR_CONNECT = -6 -ERROR_IO = -7 -ERROR_TIMEOUT = -8 -ERROR_REDIRECT_LOOP = -9 -ERROR_UNSUPPORTED_SCHEME = -10 -ERROR_FAILED_SSL_HANDSHAKE = -11 -ERROR_BAD_URL = -12 -ERROR_FILE = -13 -ERROR_FILE_NOT_FOUND = -14 -ERROR_TOO_MANY_REQUESTS = -15 - -NAVIGATION_TYPE_LINK_CLICKED = 0 -NAVIGATION_TYPE_FORM_SUBMITTED = 1 -NAVIGATION_TYPE_BACK_FORWARD = 2 -NAVIGATION_TYPE_RELOAD = 3 -NAVIGATION_TYPE_FORM_RESUBMITTED = 4 -NAVIGATION_TYPE_OTHER = -1 - -OVER_SCROLL_NEVER = 2 -OVER_SCROLL_ALWAYS = 0 -OVER_SCROLL_IF_CONTENT_SCROLLS = 1 - -RENDERER_PRIORITY_WAIVED = 0 -RENDERER_PRIORITY_BOUND = 1 -RENDERER_PRIORITY_IMPORTANT = 2 - -SCHEME_HTTP = "http" -SCHEME_HTTPS = "https" -SCHEME_FILE = "file" -SCHEME_ABOUT = "about" -SCHEME_DATA = "data" -SCHEME_JAVASCRIPT = "javascript" - -WEB_CHROME_CLIENT_GET_DEFAULT_VIDEO_POSTER = 1 -WEB_CHROME_CLIENT_GET_VISITED_HISTORY = 2 - -WEB_RESOURCE_ERROR_STYLE_BOX = 0 -WEB_RESOURCE_ERROR_STYLE_GENERIC = 1 - - -# ═══════════════════════════════════════════════════════════════ -# WebResourceRequest / WebResourceResponse / WebResourceError -# ═══════════════════════════════════════════════════════════════ - -class WebResourceRequest(Stub): - """WebResourceRequest — 资源请求""" - - def __init__(self, url: str = "", is_for_main_frame: bool = False, - is_redirect: bool = False, method: str = "GET", - request_headers: Optional[Dict[str, str]] = None, - has_user_gesture: bool = False, - is_main_frame: bool = False): - super().__init__() - self._url = url - self._is_for_main_frame = is_for_main_frame - self._is_redirect = is_redirect - self._method = method - self._request_headers = request_headers or {} - self._has_user_gesture = has_user_gesture - self._is_main_frame = is_main_frame - - def getUrl(self) -> str: - return self._url - - def isForMainFrame(self) -> bool: - return self._is_for_main_frame - - def isRedirect(self) -> bool: - return self._is_redirect - - def getMethod(self) -> str: - return self._method - - def getRequestHeaders(self) -> Dict[str, str]: - return dict(self._request_headers) - - def hasUserGesture(self) -> bool: - return self._has_user_gesture - - def isMainFrame(self) -> bool: - return self._is_main_frame - - def toString(self) -> str: - return f"WebResourceRequest(url={self._url}, method={self._method})" - - -class WebResourceResponse(Stub): - """WebResourceResponse — 资源响应""" - - def __init__(self, mime_type: str = "", encoding: str = "", - data: Any = None, status_code: int = 200, - reason_phrase: str = "OK", - response_headers: Optional[Dict[str, str]] = None): - super().__init__() - self._mime_type = mime_type - self._encoding = encoding - self._data = data - self._status_code = status_code - self._reason_phrase = reason_phrase - self._response_headers = response_headers or {} - - def getMimeType(self) -> str: - return self._mime_type - - def getEncoding(self) -> str: - return self._encoding - - def getData(self) -> Any: - return self._data - - def getStatusCode(self) -> int: - return self._status_code - - def getReasonPhrase(self) -> str: - return self._reason_phrase - - def getResponseHeaders(self) -> Dict[str, str]: - return dict(self._response_headers) - - def setMimeType(self, mime_type: str) -> None: - self._mime_type = mime_type - - def setEncoding(self, encoding: str) -> None: - self._encoding = encoding - - def setData(self, data: Any) -> None: - self._data = data - - def setStatusCodeAndReasonPhrase(self, status_code: int, reason: str) -> None: - self._status_code = status_code - self._reason_phrase = reason - - def setResponseHeaders(self, headers: Dict[str, str]) -> None: - self._response_headers = dict(headers) - - def toString(self) -> str: - return (f"WebResourceResponse(status={self._status_code}, " - f"mime={self._mime_type})") - - -class WebResourceError(Stub): - """WebResourceError — 资源错误""" - - ERROR_UNKNOWN = ERROR_UNKNOWN - ERROR_HOST_LOOKUP = ERROR_HOST_LOOKUP - ERROR_UNSUPPORTED_AUTH_SCHEME = ERROR_UNSUPPORTED_AUTH_SCHEME - ERROR_AUTHENTICATION = ERROR_AUTHENTICATION - ERROR_PROXY_AUTHENTICATION = ERROR_PROXY_AUTHENTICATION - ERROR_CONNECT = ERROR_CONNECT - ERROR_IO = ERROR_IO - ERROR_TIMEOUT = ERROR_TIMEOUT - ERROR_REDIRECT_LOOP = ERROR_REDIRECT_LOOP - ERROR_UNSUPPORTED_SCHEME = ERROR_UNSUPPORTED_SCHEME - ERROR_FAILED_SSL_HANDSHAKE = ERROR_FAILED_SSL_HANDSHAKE - ERROR_BAD_URL = ERROR_BAD_URL - ERROR_FILE = ERROR_FILE - ERROR_FILE_NOT_FOUND = ERROR_FILE_NOT_FOUND - ERROR_TOO_MANY_REQUESTS = ERROR_TOO_MANY_REQUESTS - - def __init__(self, error_code: int = ERROR_UNKNOWN, description: str = ""): - super().__init__() - self._error_code = error_code - self._description = description - - def getErrorCode(self) -> int: - return self._error_code - - def getDescription(self) -> str: - return self._description - - def toString(self) -> str: - return f"WebResourceError(code={self._error_code}, desc={self._description})" - - -# ═══════════════════════════════════════════════════════════════ -# WebMessage / WebMessagePort / WebMessageListener -# ═══════════════════════════════════════════════════════════════ - -class WebMessage(Stub): - """WebMessage — Web消息""" - - def __init__(self, data: str = "", ports: Optional[List["WebMessagePort"]] = None): - super().__init__() - self._data = data - self._ports = list(ports) if ports else [] - - def getData(self) -> str: - return self._data - - def getPorts(self) -> List["WebMessagePort"]: - return list(self._ports) - - def toString(self) -> str: - return f"WebMessage(data={self._data[:50]})" - - -class WebMessagePort(Stub): - """WebMessagePort — Web消息端口""" - - def __init__(self): - super().__init__() - self._callback: Optional[Callable] = None - self._closed = False - - def postMessage(self, message: WebMessage) -> None: - if self._closed: - raise RuntimeException("WebMessagePort 已关闭") - if self._callback: - self._callback(message) - - def close(self) -> None: - self._closed = True - self._callback = None - - def setWebMessageCallback(self, callback: Optional[Callable]) -> None: - self._callback = callback - - def isClosed(self) -> bool: - return self._closed - - def toString(self) -> str: - return f"WebMessagePort(closed={self._closed})" - - -class WebMessageListener(Stub): - """WebMessageListener — Web消息监听器""" - - def onPostMessage(self, view: Any, message: WebMessage, - source_origin: str, is_main_frame: bool, - js_ports: List[WebMessagePort]) -> None: - pass - - -# ═══════════════════════════════════════════════════════════════ -# WebStorage -# ═══════════════════════════════════════════════════════════════ - -class WebStorage(Stub): - """WebStorage — Web存储""" - - class UsageInfo(Stub): - """WebStorage.UsageInfo — 使用信息""" - def __init__(self, origin: str = "", usage: int = 0, quota: int = 0): - super().__init__() - self._origin = origin - self._usage = usage - self._quota = quota - - def getOrigin(self) -> str: - return self._origin - - def getUsage(self) -> int: - return self._usage - - def getQuota(self) -> int: - return self._quota - - def toString(self) -> str: - return f"UsageInfo(origin={self._origin}, usage={self._usage})" - - def __init__(self): - super().__init__() - self._storage: Dict[str, int] = {} - self._lock = threading.Lock() - - def getOrigins(self, callback: Callable) -> None: - with self._lock: - callback(list(self._storage.keys())) - - def getUsageForOrigin(self, origin: str, callback: Callable) -> None: - with self._lock: - callback(self._storage.get(origin, 0)) - - def getQuotaForOrigin(self, origin: str, callback: Callable) -> None: - with self._lock: - callback(self._storage.get(origin, 5 * 1024 * 1024)) - - def setQuotaForOrigin(self, origin: str, quota: int) -> None: - with self._lock: - self._storage[origin] = quota - - def deleteOrigin(self, origin: str) -> None: - with self._lock: - self._storage.pop(origin, None) - - def deleteAllData(self) -> None: - with self._lock: - self._storage.clear() - - def toString(self) -> str: - return f"WebStorage(origins={len(self._storage)})" - - -# ═══════════════════════════════════════════════════════════════ -# WebView 扩展 -# ═══════════════════════════════════════════════════════════════ - -class WebView(Stub): - """WebView — Web视图扩展""" - - RENDERER_PRIORITY_WAIVED = RENDERER_PRIORITY_WAIVED - RENDERER_PRIORITY_BOUND = RENDERER_PRIORITY_BOUND - RENDERER_PRIORITY_IMPORTANT = RENDERER_PRIORITY_IMPORTANT - - SCHEME_HTTP = SCHEME_HTTP - SCHEME_HTTPS = SCHEME_HTTPS - SCHEME_FILE = SCHEME_FILE - SCHEME_ABOUT = SCHEME_ABOUT - SCHEME_DATA = SCHEME_DATA - SCHEME_JAVASCRIPT = SCHEME_JAVASCRIPT - - LOAD_DEFAULT = LOAD_DEFAULT - LOAD_CACHE_ONLY = LOAD_CACHE_ONLY - LOAD_CACHE_ELSE_NETWORK = LOAD_CACHE_ELSE_NETWORK - LOAD_NO_CACHE = LOAD_NO_CACHE - LOAD_NORMAL = LOAD_NORMAL - - OVER_SCROLL_NEVER = OVER_SCROLL_NEVER - OVER_SCROLL_ALWAYS = OVER_SCROLL_ALWAYS - OVER_SCROLL_IF_CONTENT_SCROLLS = OVER_SCROLL_IF_CONTENT_SCROLLS - - class WebViewTransport(Stub): - """WebView.WebViewTransport — WebView传输""" - def __init__(self): - super().__init__() - self._webview: Optional[WebView] = None - - def setWebView(self, webview: "WebView") -> None: - self._webview = webview - - def getWebView(self) -> Optional["WebView"]: - return self._webview - - def __init__(self, context: Any = None): - super().__init__() - self._url: Optional[str] = None - self._content: Optional[str] = None - self._settings: Optional["WebSettings"] = None - self._client: Optional["WebViewClient"] = None - self._chrome_client: Optional["WebChromeClient"] = None - self._web_message_listeners: Dict[str, WebMessageListener] = {} - self._renderer_priority: int = self.RENDERER_PRIORITY_BOUND - self._renderer_priority_overridden = False - self._lock = threading.Lock() - - def loadUrl(self, url: str, additional_headers: Optional[Dict[str, str]] = None) -> None: - self._url = url - self._content = None - if self._client: - self._client.onPageStarted(self, url, None) - - def loadData(self, data: str, mime_type: str = "text/html", - encoding: str = "utf-8") -> None: - self._content = data - self._url = "data:text/html;charset=utf-8," + data[:100] - - def loadDataWithBaseURL(self, base_url: str, data: str, - mime_type: str, encoding: str, - history_url: Optional[str]) -> None: - self._content = data - self._url = base_url - - def postUrl(self, url: str, post_data: bytes) -> None: - self._url = url - - def getUrl(self) -> Optional[str]: - return self._url - - def getTitle(self) -> str: - return "" - - def getSettings(self) -> "WebSettings": - if self._settings is None: - self._settings = WebSettings(self) - return self._settings - - def setWebViewClient(self, client: "WebViewClient") -> None: - self._client = client - - def getWebViewClient(self) -> Optional["WebViewClient"]: - return self._client - - def setWebChromeClient(self, client: "WebChromeClient") -> None: - self._chrome_client = client - - def getWebChromeClient(self) -> Optional["WebChromeClient"]: - return self._chrome_client - - def setWebMessageListener(self, js_object_name: str, - listener: WebMessageListener) -> None: - with self._lock: - self._web_message_listeners[js_object_name] = listener - - def addJavascriptInterface(self, obj: Any, name: str) -> None: - pass - - def removeJavascriptInterface(self, name: str) -> None: - pass - - def evaluateJavascript(self, script: str, callback: Optional[Callable] = None) -> None: - pass - - def evaluateJavascriptForTests(self, script: str, callback: Optional[Callable] = None) -> None: - pass - - def canGoBack(self) -> bool: - return False - - def canGoForward(self) -> bool: - return False - - def goBack(self) -> None: - pass - - def goForward(self) -> None: - pass - - def reload(self) -> None: - if self._url: - self.loadUrl(self._url) - - def stopLoading(self) -> None: - pass - - def clearHistory(self) -> None: - pass - - def clearCache(self, include_disk: bool) -> None: - pass - - def clearFormData(self) -> None: - pass - - def destroy(self) -> None: - pass - - def onPause(self) -> None: - pass - - def onResume(self) -> None: - pass - - def isPaused(self) -> bool: - return False - - def pageUp(self, top: bool) -> bool: - return True - - def pageDown(self, bottom: bool) -> bool: - return True - - def setInitialScale(self, scale: int) -> None: - pass - - def getScale(self) -> float: - return 1.0 - - def setRendererPriorityPolicy(self, priority: int, overridden: bool) -> None: - self._renderer_priority = priority - self._renderer_priority_overridden = overridden - - def getRendererRequestedPriority(self) -> int: - return self._renderer_priority - - def getRendererPriorityWaivedWhenNotVisible(self) -> bool: - return self._renderer_priority_overridden - - def setNetworkAvailable(self, available: bool) -> None: - pass - - def setLayoutParams(self, params: Any) -> None: - pass - - def toString(self) -> str: - return f"WebView(url={self._url})" - - -# ═══════════════════════════════════════════════════════════════ -# WebSettings 扩展 -# ═══════════════════════════════════════════════════════════════ - -class WebSettings(Stub): - """WebSettings — Web设置扩展""" - - LOAD_DEFAULT = LOAD_DEFAULT - LOAD_CACHE_ONLY = LOAD_CACHE_ONLY - LOAD_CACHE_ELSE_NETWORK = LOAD_CACHE_ELSE_NETWORK - LOAD_NO_CACHE = LOAD_NO_CACHE - LOAD_NORMAL = LOAD_NORMAL - - MIXED_CONTENT_ALWAYS_ALLOW = 0 - MIXED_CONTENT_NEVER_ALLOW = 1 - MIXED_CONTENT_COMPATIBILITY_MODE = 2 - - LAYOUT_ALGORITHM_NORMAL = 0 - LAYOUT_ALGORITHM_SINGLE_COLUMN = 1 - LAYOUT_ALGORITHM_NARROW_COLUMNS = 2 - LAYOUT_ALGORITHM_TEXT_AUTOSIZING = 3 - - PLUGIN_STATE_ON = 0 - PLUGIN_STATE_ON_DEMAND = 1 - PLUGIN_STATE_OFF = 2 - - RENDER_GOOGLE_SUIT = 0 - RENDER_PRIORITY_NORMAL = 0 - RENDER_PRIORITY_HIGH = 1 - RENDER_PRIORITY_LOW = 2 - - TEXT_AUTOSIZING_TEXT_SIZE_INCREMENT = 0 - - USER_AGENT_DESKTOP = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" - USER_AGENT_MOBILE = "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36" - - def __init__(self, webview: Optional[WebView] = None): - super().__init__() - self._webview = webview - self._java_script_enabled = False - self._java_script_can_open_windows = False - self._allow_file_access = True - self._allow_file_access_from_file_urls = True - self._allow_universal_access_from_file_urls = False - self._built_in_zoom_controls = True - self._display_zoom_controls = True - self._support_zoom = True - self._load_with_overview_mode = False - self._use_wide_viewport = True - self._dom_storage_enabled = True - self._database_enabled = True - self._app_cache_enabled = True - self._cache_mode = LOAD_DEFAULT - self._user_agent_string: Optional[str] = None - self._default_font_size: int = 16 - self._default_fixed_font_size: int = 13 - self._minimum_font_size: int = 8 - self._minimum_logical_font_size: int = 8 - self._text_zoom: int = 100 - self._standard_font_family: str = "sans-serif" - self._serif_font_family: str = "serif" - self._sans_serif_font_family: str = "sans-serif" - self._fixed_font_family: str = "monospace" - self._cursive_font_family: str = "cursive" - self._fantasy_font_family: str = "fantasy" - self._default_text_encoding: str = "UTF-8" - self._layout_algorithm = self.LAYOUT_ALGORITHM_NORMAL - self._block_network_image = False - self._block_network_loads = False - self._accept_cookies = True - self._mixed_content_mode = self.MIXED_CONTENT_NEVER_ALLOW - self._offscreen_prefetch_rss = False - self._safe_browsing_enabled = True - self._save_form_data = True - self._save_password = False - self._need_initial_focus = True - self._support_multiple_windows = False - self._allow_content_access = True - self._loads_images_automatically = True - self._javascript_enabled = False - self._media_playback_requires_user_gesture = True - - def setJavaScriptEnabled(self, enabled: bool) -> None: - self._javascript_enabled = enabled - - def getJavaScriptEnabled(self) -> bool: - return self._javascript_enabled - - def setJavaScriptCanOpenWindowsAutomatically(self, can: bool) -> None: - self._java_script_can_open_windows = can - - def getJavaScriptCanOpenWindowsAutomatically(self) -> bool: - return self._java_script_can_open_windows - - def setAllowFileAccess(self, allow: bool) -> None: - self._allow_file_access = allow - - def getAllowFileAccess(self) -> bool: - return self._allow_file_access - - def setAllowFileAccessFromFileURLs(self, allow: bool) -> None: - self._allow_file_access_from_file_urls = allow - - def getAllowFileAccessFromFileURLs(self) -> bool: - return self._allow_file_access_from_file_urls - - def setAllowUniversalAccessFromFileURLs(self, allow: bool) -> None: - self._allow_universal_access_from_file_urls = allow - - def getAllowUniversalAccessFromFileURLs(self) -> bool: - return self._allow_universal_access_from_file_urls - - def setBuiltInZoomControls(self, enabled: bool) -> None: - self._built_in_zoom_controls = enabled - - def getBuiltInZoomControls(self) -> bool: - return self._built_in_zoom_controls - - def setDisplayZoomControls(self, enabled: bool) -> None: - self._display_zoom_controls = enabled - - def getDisplayZoomControls(self) -> bool: - return self._display_zoom_controls - - def setSupportZoom(self, support: bool) -> None: - self._support_zoom = support - - def supportZoom(self) -> bool: - return self._support_zoom - - def setLoadWithOverviewMode(self, overview: bool) -> None: - self._load_with_overview_mode = overview - - def getLoadWithOverviewMode(self) -> bool: - return self._load_with_overview_mode - - def setUseWideViewPort(self, use: bool) -> None: - self._use_wide_viewport = use - - def getUseWideViewPort(self) -> bool: - return self._use_wide_viewport - - def setDomStorageEnabled(self, enabled: bool) -> None: - self._dom_storage_enabled = enabled - - def getDomStorageEnabled(self) -> bool: - return self._dom_storage_enabled - - def setDatabaseEnabled(self, enabled: bool) -> None: - self._database_enabled = enabled - - def getDatabaseEnabled(self) -> bool: - return self._database_enabled - - def setAppCacheEnabled(self, enabled: bool) -> None: - self._app_cache_enabled = enabled - - def getAppCacheEnabled(self) -> bool: - return self._app_cache_enabled - - def setCacheMode(self, mode: int) -> None: - self._cache_mode = mode - - def getCacheMode(self) -> int: - return self._cache_mode - - def setUserAgentString(self, ua: Optional[str]) -> None: - self._user_agent_string = ua - - def getUserAgentString(self) -> str: - return self._user_agent_string or self.USER_AGENT_MOBILE - - def setDefaultFontSize(self, size: int) -> None: - self._default_font_size = max(1, size) - - def getDefaultFontSize(self) -> int: - return self._default_font_size - - def setDefaultFixedFontSize(self, size: int) -> None: - self._default_fixed_font_size = max(1, size) - - def getDefaultFixedFontSize(self) -> int: - return self._default_fixed_font_size - - def setMinimumFontSize(self, size: int) -> None: - self._minimum_font_size = max(1, size) - - def getMinimumFontSize(self) -> int: - return self._minimum_font_size - - def setTextZoom(self, text_zoom: int) -> None: - self._text_zoom = max(50, min(200, text_zoom)) - - def getTextZoom(self) -> int: - return self._text_zoom - - def setStandardFontFamily(self, font: str) -> None: - self._standard_font_family = font - - def getStandardFontFamily(self) -> str: - return self._standard_font_family - - def setSerifFontFamily(self, font: str) -> None: - self._serif_font_family = font - - def getSerifFontFamily(self) -> str: - return self._serif_font_family - - def setSansSerifFontFamily(self, font: str) -> None: - self._sans_serif_font_family = font - - def getSansSerifFontFamily(self) -> str: - return self._sans_serif_font_family - - def setFixedFontFamily(self, font: str) -> None: - self._fixed_font_family = font - - def getFixedFontFamily(self) -> str: - return self._fixed_font_family - - def setCursiveFontFamily(self, font: str) -> None: - self._cursive_font_family = font - - def getCursiveFontFamily(self) -> str: - return self._cursive_font_family - - def setFantasyFontFamily(self, font: str) -> None: - self._fantasy_font_family = font - - def getFantasyFontFamily(self) -> str: - return self._fantasy_font_family - - def setDefaultTextEncodingName(self, encoding: str) -> None: - self._default_text_encoding = encoding - - def getDefaultTextEncodingName(self) -> str: - return self._default_text_encoding - - def setLayoutAlgorithm(self, algorithm: int) -> None: - self._layout_algorithm = algorithm - - def getLayoutAlgorithm(self) -> int: - return self._layout_algorithm - - def setBlockNetworkImage(self, block: bool) -> None: - self._block_network_image = block - - def getBlockNetworkImage(self) -> bool: - return self._block_network_image - - def setBlockNetworkLoads(self, block: bool) -> None: - self._block_network_loads = block - - def getBlockNetworkLoads(self) -> bool: - return self._block_network_loads - - def setMixedContentMode(self, mode: int) -> None: - self._mixed_content_mode = mode - - def getMixedContentMode(self) -> int: - return self._mixed_content_mode - - def setLoadsImagesAutomatically(self, enabled: bool) -> None: - self._loads_images_automatically = enabled - - def getLoadsImagesAutomatically(self) -> bool: - return self._loads_images_automatically - - def setMediaPlaybackRequiresUserGesture(self, requires: bool) -> None: - self._media_playback_requires_user_gesture = requires - - def getMediaPlaybackRequiresUserGesture(self) -> bool: - return self._media_playback_requires_user_gesture - - def setSupportMultipleWindows(self, support: bool) -> None: - self._support_multiple_windows = support - - def supportMultipleWindows(self) -> bool: - return self._support_multiple_windows - - def setSaveFormData(self, save: bool) -> None: - self._save_form_data = save - - def getSaveFormData(self) -> bool: - return self._save_form_data - - def setSavePassword(self, save: bool) -> None: - self._save_password = save - - def getSavePassword(self) -> bool: - return self._save_password - - def setNeedInitialFocus(self, need: bool) -> None: - self._need_initial_focus = need - - def getNeedInitialFocus(self) -> bool: - return self._need_initial_focus - - def setSafeBrowsingEnabled(self, enabled: bool) -> None: - self._safe_browsing_enabled = enabled - - def getSafeBrowsingEnabled(self) -> bool: - return self._safe_browsing_enabled - - def toString(self) -> str: - return (f"WebSettings(js={self._javascript_enabled}, " - f"zoom={self._support_zoom})") - - -# ═══════════════════════════════════════════════════════════════ -# WebViewClient 扩展 -# ═══════════════════════════════════════════════════════════════ - -class WebViewClient(Stub): - """WebViewClient — Web视图客户端扩展""" - - def __init__(self): - super().__init__() - - def shouldOverrideUrlLoading(self, view: WebView, request: WebResourceRequest) -> bool: - return False - - def shouldOverrideKeyEvent(self, view: WebView, event: Any) -> bool: - return False - - def onPageStarted(self, view: WebView, url: str, favicon: Any) -> None: - pass - - def onPageFinished(self, view: WebView, url: str) -> None: - pass - - def onLoadResource(self, view: WebView, url: str) -> None: - pass - - def onPageCommitVisible(self, view: WebView, url: str) -> None: - pass - - def shouldInterceptRequest(self, view: WebView, request: WebResourceRequest) -> Optional[WebResourceResponse]: - return None - - def onReceivedError(self, view: WebView, request: WebResourceRequest, - error: WebResourceError) -> None: - pass - - def onReceivedHttpError(self, view: WebView, request: WebResourceRequest, - error_response: WebResourceResponse) -> None: - pass - - def onReceivedHttpAuthRequest(self, view: WebView, handler: Any, - host: str, realm: str) -> None: - pass - - def onReceivedLoginRequest(self, view: WebView, realm: str, - account: str, args: str) -> None: - pass - - def onReceivedSslError(self, view: WebView, handler: Any, - error: Any) -> None: - pass - - def onReceivedClientCertRequest(self, view: WebView, - request: Any) -> None: - pass - - def onFormResubmission(self, view: WebView, dont_resend: Any, - resend: Any) -> None: - pass - - def doUpdateVisitedHistory(self, view: WebView, url: str, - is_reloa: bool) -> None: - pass - - def onUnhandledKeyEvent(self, view: WebView, event: Any) -> None: - pass - - def onScaleChanged(self, view: WebView, old_scale: float, new_scale: float) -> None: - pass - - def onRenderProcessGone(self, view: WebView, detail: Any) -> bool: - return True - - def onUnhandledInputEvent(self, view: WebView, event: Any) -> None: - pass - - def safebrowsingOnBackForwardTransitioned(self, view: WebView, - safe: bool) -> None: - pass - - def toString(self) -> str: - return "WebViewClient()" - - -# ═══════════════════════════════════════════════════════════════ -# WebChromeClient 扩展 -# ═══════════════════════════════════════════════════════════════ - -class WebChromeClient(Stub): - """WebChromeClient — Web Chrome客户端扩展""" - - GET_DEFAULT_VIDEO_POSTER = WEB_CHROME_CLIENT_GET_DEFAULT_VIDEO_POSTER - GET_VISITED_HISTORY = WEB_CHROME_CLIENT_GET_VISITED_HISTORY - - def __init__(self): - super().__init__() - - def onProgressChanged(self, view: WebView, new_progress: int) -> None: - pass - - def onReceivedTitle(self, view: WebView, title: str) -> None: - pass - - def onReceivedIcon(self, view: WebView, icon: Any) -> None: - pass - - def onReceivedTouchIconUrl(self, view: WebView, url: str, - precomposed: bool) -> None: - pass - - def onShowCustomView(self, view: Any, callback: Any) -> None: - pass - - def onHideCustomView(self) -> None: - pass - - def onCreateWindow(self, view: WebView, is_dialog: bool, - is_user_gesture: bool, - result_msg: Any) -> bool: - return False - - def onRequestFocus(self, view: WebView) -> None: - pass - - def onCloseWindow(self, window: WebView) -> None: - pass - - def onJsAlert(self, view: WebView, url: str, message: str, - result: Any) -> bool: - return False - - def onJsConfirm(self, view: WebView, url: str, message: str, - result: Any) -> bool: - return False - - def onJsPrompt(self, view: WebView, url: str, message: str, - default_value: str, result: Any) -> bool: - return False - - def onJsBeforeUnload(self, view: WebView, url: str, message: str, - result: Any) -> bool: - return False - - def onConsoleMessage(self, console_message: Any) -> bool: - return True - - def getVideoLoadingProgressView(self) -> Optional[Any]: - return None - - def getDefaultVideoPoster(self) -> Optional[Any]: - return None - - def onShowFileChooser(self, view: WebView, callback: Any, - params: Any) -> bool: - return False - - def onPermissionRequest(self, request: Any) -> None: - pass - - def onPermissionRequestCanceled(self, request: Any) -> None: - pass - - def onGeolocationPermissionsShowPrompt(self, origin: str, - callback: Any) -> None: - pass - - def onGeolocationPermissionsHidePrompt(self) -> None: - pass - - def onJsTimeout(self) -> bool: - return True - - def onExceededDatabaseQuota(self, url: str, database_id: str, - quota: int, estimated: int, - quota_updater: Any) -> None: - pass - - def onReachedMaxAppCacheSize(self, space_needed: int, total: int, - quota_updater: Any) -> None: - pass - - def onConsoleMessage(self, source_id: str, message: str, - line_number: int, msg_type: int) -> bool: - return True - - def toString(self) -> str: - return "WebChromeClient()" - - -# ═══════════════════════════════════════════════════════════════ -# CookieManager 扩展 -# ═══════════════════════════════════════════════════════════════ - -class CookieManager(Stub): - """CookieManager — Cookie管理器扩展""" - - _instance: Optional["CookieManager"] = None - _lock = threading.Lock() - - def __init__(self): - super().__init__() - self._cookies: Dict[str, Dict[str, str]] = {} - self._accept_cookie = True - - @staticmethod - def getInstance() -> "CookieManager": - with CookieManager._lock: - if CookieManager._instance is None: - CookieManager._instance = CookieManager() - return CookieManager._instance - - def setAcceptCookie(self, accept: bool) -> None: - self._accept_cookie = accept - - def acceptCookie(self) -> bool: - return self._accept_cookie - - def setAcceptThirdPartyCookies(self, webview: WebView, accept: bool) -> None: - pass - - def acceptThirdPartyCookies(self, webview: WebView) -> bool: - return False - - def setCookie(self, url: str, value: str) -> None: - domain = self._extract_domain(url) - if domain not in self._cookies: - self._cookies[domain] = {} - if "=" in value: - key, val = value.split("=", 1) - self._cookies[domain][key.strip()] = val.strip() - - def getCookie(self, url: str) -> Optional[str]: - domain = self._extract_domain(url) - if domain in self._cookies: - parts = [f"{k}={v}" for k, v in self._cookies[domain].items()] - return "; ".join(parts) - return None - - def removeSessionCookies(self) -> None: - self._cookies.clear() - - def removeAllCookies(self) -> None: - self._cookies.clear() - - def removeExpiredCookie(self) -> None: - pass - - def hasCookies(self) -> bool: - return bool(self._cookies) - - def _extract_domain(self, url: str) -> str: - if "://" in url: - url = url.split("://", 1)[1] - return url.split("/")[0].split(":")[0] - - def flush(self) -> None: - pass - - def toString(self) -> str: - return f"CookieManager(domains={len(self._cookies)})" - - -# ═══════════════════════════════════════════════════════════════ -# GeolocationPermissions / JsResult / JsPromptResult -# ═══════════════════════════════════════════════════════════════ - -class GeolocationPermissions(Stub): - """GeolocationPermissions — 地理位置权限""" - - _origins: Dict[str, bool] = {} - _lock = threading.Lock() - - @classmethod - def getInstance(cls) -> "GeolocationPermissions": - return cls() - - def getOrigins(self, callback: Callable) -> None: - with self._lock: - callback(list(self._origins.keys())) - - def getAllowed(self, origin: str, callback: Callable) -> None: - with self._lock: - callback(origin, self._origins.get(origin, False), False) - - def clear(self, origin: str) -> None: - with self._lock: - self._origins.pop(origin, None) - - def clearAll(self) -> None: - with self._lock: - self._origins.clear() - - def allow(self, origin: str, allow: bool) -> None: - with self._lock: - self._origins[origin] = allow - - -class JsResult(Stub): - """JsResult — JavaScript 对话框结果""" - - def __init__(self): - super().__init__() - self._result: Optional[bool] = None - - def confirm(self) -> None: - self._result = True - - def cancel(self) -> None: - self._result = False - - def getResult(self) -> Optional[bool]: - return self._result - - def toString(self) -> str: - return f"JsResult(result={self._result})" - - -class JsPromptResult(JsResult): - """JsPromptResult — JavaScript 提示框结果""" - - def __init__(self): - super().__init__() - self._string_result: str = "" - - def confirm(self, result: str) -> None: - super().confirm() - self._string_result = result - - def getStringResult(self) -> str: - return self._string_result - - def toString(self) -> str: - return f"JsPromptResult(result={self._result}, string={self._string_result})" - - -class HttpAuthHandler(Stub): - """HttpAuthHandler — HTTP 认证处理器""" - - def __init__(self): - super().__init__() - self._handled = False - - def proceed(self, username: str, password: str) -> None: - self._handled = True - - def cancel(self) -> None: - self._handled = True - - def useHttpAuthUsernamePassword(self, use: bool) -> None: - pass - - def isHandled(self) -> bool: - return self._handled - - def toString(self) -> str: - return f"HttpAuthHandler(handled={self._handled})" - - -class SslErrorHandler(Stub): - """SslErrorHandler — SSL 错误处理器""" - - def __init__(self): - super().__init__() - self._handled = False - - def proceed(self) -> None: - self._handled = True - - def cancel(self) -> None: - self._handled = True - - def isHandled(self) -> bool: - return self._handled - - def toString(self) -> str: - return f"SslErrorHandler(handled={self._handled})" - - -class ClientCertRequest(Stub): - """ClientCertRequest — 客户端证书请求""" - - def __init__(self): - super().__init__() - self._handled = False - - def getHost(self) -> str: - return "" - - def getPort(self) -> int: - return 443 - - def getKeyTypes(self) -> List[str]: - return ["RSA"] - - def getPrincipals(self) -> List[str]: - return [] - - def cancel(self) -> None: - self._handled = True - - def proceed(self, private_key: Any, chain: List[Any]) -> None: - self._handled = True - - def ignore(self) -> None: - self._handled = True - - def isHandled(self) -> bool: - return self._handled - - -class PermissionRequest(Stub): - """PermissionRequest — 权限请求""" - - RESOURCE_VIDEO_CAPTURE = "android.webkit.resource.VIDEO_CAPTURE" - RESOURCE_AUDIO_CAPTURE = "android.webkit.resource.AUDIO_CAPTURE" - RESOURCE_PROTECTED_MEDIA_ID = "android.webkit.resource.PROTECTED_MEDIA_ID" - RESOURCE_MIDI_SYSEX = "android.webkit.resource.MIDI_SYSEX" - - def __init__(self): - super().__init__() - self._origin: Optional[str] = None - self._resources: List[str] = [] - self._granted = False - - def getOrigin(self) -> str: - return self._origin or "" - - def getResources(self) -> List[str]: - return list(self._resources) - - def grant(self, resources: List[str]) -> None: - self._granted = True - - def deny(self) -> None: - self._granted = False - - def isGranted(self) -> bool: - return self._granted - - def toString(self) -> str: - return f"PermissionRequest(origin={self._origin}, granted={self._granted})" - - -class ConsoleMessage(Stub): - """ConsoleMessage — 控制台消息""" - - MessageLevel = type("MessageLevel", (), { - "TIP": 0, "LOG": 1, "WARNING": 2, "ERROR": 3, "DEBUG": 4 - }) - - def __init__(self, message: str = "", source_id: str = "", - line_number: int = 0, msg_level: int = MessageLevel.LOG): - super().__init__() - self._message = message - self._source_id = source_id - self._line_number = line_number - self._msg_level = msg_level - - def message(self) -> str: - return self._message - - def sourceId(self) -> str: - return self._source_id - - def lineNumber(self) -> int: - return self._line_number - - def messageLevel(self) -> int: - return self._msg_level - - def toString(self) -> str: - return f"ConsoleMessage({self._message[:50]})" - - -class ValueCallback(Stub): - """ValueCallback — 值回调接口""" - - def onReceiveValue(self, value: Any) -> None: - pass - - -# ═══════════════════════════════════════════════════════════════ -# WebBackForwardList / WebHistoryItem / WebIconDatabase / etc. -# ═══════════════════════════════════════════════════════════════ - -class WebBackForwardList(Stub): - """WebBackForwardList — 后退/前进列表""" - - def __init__(self): - super().__init__() - self._items: List["WebHistoryItem"] = [] - self._current_index: int = -1 - - def getCurrentItem(self) -> Optional["WebHistoryItem"]: - if 0 <= self._current_index < len(self._items): - return self._items[self._current_index] - return None - - def getCurrentIndex(self) -> int: - return self._current_index - - def getItemAtIndex(self, index: int) -> Optional["WebHistoryItem"]: - if 0 <= index < len(self._items): - return self._items[index] - return None - - def getSize(self) -> int: - return len(self._items) - - def toString(self) -> str: - return f"WebBackForwardList(size={len(self._items)}, current={self._current_index})" - - -class WebHistoryItem(Stub): - """WebHistoryItem — Web历史条目""" - - def __init__(self, url: str = "", title: str = "", - favicon: Any = None): - super().__init__() - self._url = url - self._title = title - self._favicon = favicon - - def getUrl(self) -> str: - return self._url - - def getTitle(self) -> str: - return self._title - - def getFavicon(self) -> Optional[Any]: - return self._favicon - - def setUrl(self, url: str) -> None: - self._url = url - - def setTitle(self, title: str) -> None: - self._title = title - - def setFavicon(self, favicon: Any) -> None: - self._favicon = favicon - - def toString(self) -> str: - return f"WebHistoryItem(url={self._url}, title={self._title})" - - -class WebIconDatabase(Stub): - """WebIconDatabase — Web图标数据库""" - - _instance: Optional["WebIconDatabase"] = None - - @staticmethod - def getInstance() -> "WebIconDatabase": - if WebIconDatabase._instance is None: - WebIconDatabase._instance = WebIconDatabase() - return WebIconDatabase._instance - - def open(self, path: str) -> None: - pass - - def close(self) -> None: - pass - - def removeAllIcons(self) -> None: - pass - - def requestIconForPageUrl(self, url: str, callback: Callable) -> None: - callback(url, None) - - def retainIconForPageUrl(self, url: str) -> None: - pass - - def releaseIconForPageUrl(self, url: str) -> None: - pass - - def getIconForPageUrl(self, url: str) -> Optional[Any]: - return None - - def toString(self) -> str: - return "WebIconDatabase()" - - -class WebViewDatabase(Stub): - """WebViewDatabase — WebView数据库""" - - _instance: Optional["WebViewDatabase"] = None - - @staticmethod - def getInstance(context: Any) -> "WebViewDatabase": - if WebViewDatabase._instance is None: - WebViewDatabase._instance = WebViewDatabase() - return WebViewDatabase._instance - - def hasFormData(self) -> bool: - return False - - def clearFormData(self) -> None: - pass - - def hasUsernamePassword(self) -> bool: - return False - - def clearUsernamePassword(self) -> None: - pass - - def hasHttpAuthUsernamePassword(self) -> bool: - return False - - def clearHttpAuthUsernamePassword(self) -> None: - pass - - def toString(self) -> str: - return "WebViewDatabase()" - - -class WebViewFragment(Stub): - """WebViewFragment — WebView片段""" - - def __init__(self): - super().__init__() - self._webview: Optional[WebView] = None - - def onCreateView(self, inflater: Any, container: Any, - savedInstanceState: Any) -> Optional[WebView]: - self._webview = WebView() - return self._webview - - def onPause(self) -> None: - if self._webview: - self._webview.onPause() - - def onResume(self) -> None: - if self._webview: - self._webview.onResume() - - def onDestroyView(self) -> None: - if self._webview: - self._webview.destroy() - - def getWebView(self) -> Optional[WebView]: - return self._webview - - def toString(self) -> str: - return f"WebViewFragment(webview={'yes' if self._webview else 'no'})" - - -class DateSorter(Stub): - """DateSorter — 日期排序器""" - - DAY_COUNT = 5 - - def __init__(self, context: Any): - super().__init__() - self._bounds: List[int] = [0, 0, 0, 0, 0] - self._labels: List[str] = ["Today", "Yesterday", "Earlier"] - - def getBounds(self) -> List[int]: - return list(self._bounds) - - def getLabel(self, index: int) -> str: - if 0 <= index < len(self._labels): - return self._labels[index] - return "" - - def toString(self) -> str: - return "DateSorter()" - - -class FindActionModeCallback(Stub): - """FindActionModeCallback — 查找操作模式回调""" - - def __init__(self, context: Any = None): - super().__init__() - self._search_text: str = "" - self._global_search_count: int = 0 - self._match_count: int = 0 - - def finishActionMode(self) -> None: - pass - - def setText(self, text: str) -> None: - self._search_text = text - - def getText(self) -> str: - return self._search_text - - def setGlobalSearchCount(self, count: int) -> None: - self._global_search_count = count - - def getGlobalSearchCount(self) -> int: - return self._global_search_count - - def setMatchCount(self, count: int) -> None: - self._match_count = count - - def getMatchCount(self) -> int: - return self._match_count - - def toString(self) -> str: - return (f"FindActionModeCallback(search={self._search_text}, " - f"matches={self._match_count})") - - -class JsDialogHelper(Stub): - """JsDialogHelper — JavaScript 对话框助手""" - - ALERT = 1 - CONFIRM = 2 - PROMPT = 3 - UNLOAD = 4 - - def __init__(self, result: JsResult, default_value: str = "", - dialog_type: int = ALERT, message: str = ""): - super().__init__() - self._result = result - self._default_value = default_value - self._dialog_type = dialog_type - self._message = message - - def getDialogType(self) -> int: - return self._dialog_type - - def getMessage(self) -> str: - return self._message - - def getDefaultValue(self) -> str: - return self._default_value - - def invokeCallback(self, result: Any) -> None: - if self._result: - self._result.confirm() - - def cancelDialog(self) -> None: - if self._result: - self._result.cancel() - - def toString(self) -> str: - types = {1: "ALERT", 2: "CONFIRM", 3: "PROMPT", 4: "UNLOAD"} - return f"JsDialogHelper(type={types.get(self._dialog_type, 'UNKNOWN')})" \ No newline at end of file diff --git a/android_pyapi/android/widget.py b/android_pyapi/android/widget.py deleted file mode 100644 index 6d1cbaa..0000000 --- a/android_pyapi/android/widget.py +++ /dev/null @@ -1,4891 +0,0 @@ -""" -Android Widget 组件 — TextView, Button, RecyclerView, ListView, WebView 等 -方法签名完全对齐 Android SDK -""" - -import math -import time -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError, android_context -from .view import ( - View, ViewGroup, ViewTreeObserver, - MotionEvent, KeyEvent, Gravity, SurfaceView, - ViewGroup as VG, -) - -try: - from .._linux_impl import IS_LINUX, LinuxNotify, LinuxClipboard - _USE_LINUX = IS_LINUX -except ImportError: - _USE_LINUX = False - - -# ═══════════════════════════════════════════════════════════════ -# TextView -# ═══════════════════════════════════════════════════════════════ - -class TextView(View): - """TextView — 文本显示视图""" - - # 自动链接 - AUTO_LINK_NONE = 0x00 - AUTO_LINK_WEB_URLS = 0x01 - AUTO_LINK_EMAIL_ADDRESSES = 0x02 - AUTO_LINK_PHONE_NUMBERS = 0x04 - AUTO_LINK_MAP_ADDRESSES = 0x08 - AUTO_LINK_ALL = 0x0F - - # Buffer类型 - BUFFER_TYPE_NORMAL = 0 - BUFFER_TYPE_SPANNABLE = 1 - BUFFER_TYPE_EDITABLE = 2 - - # 截断位置 - TRUNCATE_AT_END = 0 - TRUNCATE_AT_MIDDLE = 1 - TRUNCATE_AT_START = 2 - - # 文本方向 - TEXT_DIRECTION_INHERIT = 0 - TEXT_DIRECTION_FIRST_STRONG = 1 - TEXT_DIRECTION_ANY_RTL = 2 - TEXT_DIRECTION_LTR = 3 - TEXT_DIRECTION_RTL = 4 - TEXT_DIRECTION_LOCALE = 5 - TEXT_DIRECTION_FIRST_STRONG_LTR = 6 - TEXT_DIRECTION_FIRST_STRONG_RTL = 7 - - # 文本对齐 - TEXT_ALIGNMENT_INHERIT = 0 - TEXT_ALIGNMENT_GRAVITY = 1 - TEXT_ALIGNMENT_TEXT_START = 2 - TEXT_ALIGNMENT_TEXT_END = 3 - TEXT_ALIGNMENT_CENTER = 4 - TEXT_ALIGNMENT_VIEW_START = 5 - TEXT_ALIGNMENT_VIEW_END = 6 - - def __init__(self, context=None): - super().__init__(context) - self._text: str = "" - self._hint: str = "" - self._text_size: float = 14.0 - self._text_color: int = 0xFF000000 - self._hint_text_color: int = 0xFF808080 - self._link_text_color: int = 0xFFFF0000 - self._highlight_color: int = 0x556633CC - self._gravity = Gravity.NO_GRAVITY - self._typeface: Any = None - self._input_type: int = 0 - self._lines: int = 0 - self._max_lines: int = 0 - self._min_lines: int = 0 - self._single_line: bool = False - self._ellipsize: Optional[str] = None # "end", "start", "middle", "marquee" - self._movement_method: Any = None - self._auto_link_mask: int = TextView.AUTO_LINK_NONE - self._transformation_method: Optional[Callable] = None - self._shadow_radius: float = 0.0 - self._shadow_dx: float = 0.0 - self._shadow_dy: float = 0.0 - self._shadow_color: int = 0 - self._line_spacing_extra: float = 0.0 - self._line_spacing_mult: float = 1.0 - self._compound_drawables: List[Any] = [None, None, None, None] - self._compound_drawable_padding: int = 0 - self._include_font_padding: bool = True - self._elegant_text_height: bool = False - self._letter_spacing: float = 0.0 - self._font_feature_settings: str = "" - self._key_listener: Any = None - self._filters: List[Any] = [] - self._select_all_on_focus: bool = False - self._horizontally_scrolling: bool = False - self._on_editor_action_listener: Optional[Callable] = None - self._paint: Any = None - self._line_count: int = 1 - self._layout: Any = None - self._links_clickable: bool = False - self._cursor_visible: bool = True - self._editable: bool = False - self._selection_start: int = 0 - self._selection_end: int = 0 - self._scroll_horizontally: bool = False - - # ─── text ────────────────────────────────────────────────── - - def setText(self, text: str, buffer_type: int = BUFFER_TYPE_NORMAL): - self._text = str(text) if text is not None else "" - self._line_count = self._text.count("\n") + 1 if self._text else 0 - - def getText(self) -> str: - return self._text - - def append(self, text: str): - self._text += str(text) if text is not None else "" - self._line_count = self._text.count("\n") + 1 if self._text else 0 - - # ─── text size / color ───────────────────────────────────── - - def setTextSize(self, size: float, unit: int = 2): # unit=2 means SP - self._text_size = size - - def getTextSize(self) -> float: - return self._text_size - - def setTextColor(self, color: int): - self._text_color = color - - def getCurrentTextColor(self) -> int: - return self._text_color - - def setHintTextColor(self, color: int): - self._hint_text_color = color - - def getHintTextColor(self) -> int: - return self._hint_text_color - - def setLinkTextColor(self, color: int): - self._link_text_color = color - - def getLinkTextColor(self) -> int: - return self._link_text_color - - def setHighlightColor(self, color: int): - self._highlight_color = color - - def getHighlightColor(self) -> int: - return self._highlight_color - - # ─── gravity ────────────────────────────────────────────── - - def setGravity(self, gravity: int): - self._gravity = gravity - - def getGravity(self) -> int: - return self._gravity - - # ─── typeface ────────────────────────────────────────────── - - def setTypeface(self, typeface: Any, style: int = 0): - self._typeface = typeface - - def getTypeface(self) -> Any: - return self._typeface - - # ─── hint ────────────────────────────────────────────────── - - def setHint(self, hint: str): - self._hint = str(hint) if hint is not None else "" - - def getHint(self) -> str: - return self._hint - - # ─── input type ──────────────────────────────────────────── - - def setInputType(self, input_type: int): - self._input_type = input_type - - def getInputType(self) -> int: - return self._input_type - - # ─── lines ───────────────────────────────────────────────── - - def setLines(self, lines: int): - self._lines = lines - - def setMaxLines(self, max_lines: int): - self._max_lines = max_lines - - def getMaxLines(self) -> int: - return self._max_lines - - def setMinLines(self, min_lines: int): - self._min_lines = min_lines - - def getMinLines(self) -> int: - return self._min_lines - - def getLineCount(self) -> int: - return self._line_count - - def setSingleLine(self, single_line: bool = True): - self._single_line = single_line - - # ─── ellipsize ───────────────────────────────────────────── - - def setEllipsize(self, where: Optional[str]): - self._ellipsize = where - - def getEllipsize(self) -> Optional[str]: - return self._ellipsize - - # ─── movement method ────────────────────────────────────── - - def setMovementMethod(self, movement: Any): - self._movement_method = movement - - def getMovementMethod(self) -> Any: - return self._movement_method - - # ─── auto link ───────────────────────────────────────────── - - def setAutoLinkMask(self, mask: int): - self._auto_link_mask = mask - - def getAutoLinkMask(self) -> int: - return self._auto_link_mask - - # ─── transformation ─────────────────────────────────────── - - def setTransformationMethod(self, method: Optional[Callable]): - self._transformation_method = method - - def getTransformationMethod(self) -> Optional[Callable]: - return self._transformation_method - - # ─── shadow ──────────────────────────────────────────────── - - def setShadowLayer(self, radius: float, dx: float, dy: float, color: int): - self._shadow_radius = radius - self._shadow_dx = dx - self._shadow_dy = dy - self._shadow_color = color - - # ─── line spacing ───────────────────────────────────────── - - def setLineSpacing(self, extra: float, multiplier: float): - self._line_spacing_extra = extra - self._line_spacing_mult = multiplier - - # ─── compound drawables ─────────────────────────────────── - - def setCompoundDrawables(self, left: Any, top: Any, right: Any, bottom: Any): - self._compound_drawables = [left, top, right, bottom] - - def setCompoundDrawablesWithIntrinsicBounds(self, left: Any, top: Any, - right: Any, bottom: Any): - self._compound_drawables = [left, top, right, bottom] - - def setCompoundDrawablePadding(self, pad: int): - self._compound_drawable_padding = pad - - def getCompoundDrawablePadding(self) -> int: - return self._compound_drawable_padding - - def getCompoundDrawables(self) -> List[Any]: - return list(self._compound_drawables) - - # ─── editor action ──────────────────────────────────────── - - def setOnEditorActionListener(self, listener: Optional[Callable]): - self._on_editor_action_listener = listener - - # ─── key listener ───────────────────────────────────────── - - def setKeyListener(self, key_listener: Any): - self._key_listener = key_listener - - def getKeyListener(self) -> Any: - return self._key_listener - - # ─── filters ─────────────────────────────────────────────── - - def setFilters(self, filters: List[Any]): - self._filters = list(filters) - - def getFilters(self) -> List[Any]: - return list(self._filters) - - # ─── misc ────────────────────────────────────────────────── - - def setSelectAllOnFocus(self, select_all_on_focus: bool): - self._select_all_on_focus = select_all_on_focus - - def setHorizontallyScrolling(self, horizontally_scrolling: bool): - self._horizontally_scrolling = horizontally_scrolling - - def setIncludeFontPadding(self, include_font_padding: bool): - self._include_font_padding = include_font_padding - - def setElegantTextHeight(self, elegant: bool): - self._elegant_text_height = elegant - - def setLetterSpacing(self, letter_spacing: float): - self._letter_spacing = letter_spacing - - def getLetterSpacing(self) -> float: - return self._letter_spacing - - def setFontFeatureSettings(self, settings: str): - self._font_feature_settings = settings - - def getFontFeatureSettings(self) -> str: - return self._font_feature_settings - - def getPaint(self) -> Any: - return self._paint - - def getLayout(self) -> Any: - return self._layout - - def setLinksClickable(self, links_clickable: bool): - self._links_clickable = links_clickable - - def getLinksClickable(self) -> bool: - return self._links_clickable - - def setCursorVisible(self, visible: bool): - self._cursor_visible = visible - - def isCursorVisible(self) -> bool: - return self._cursor_visible - - def setTextCursorDrawable(self, drawable: Any): - pass - - def setFreezesText(self, freezes_text: bool): - pass - - def getFreezesText(self) -> bool: - return False - - def setEditableFactory(self, factory: Any): - pass - - def setSpannableFactory(self, factory: Any): - pass - - def getTextSize(self) -> float: - return self._text_size - - def setBackgroundColor(self, color: int): - super().setBackgroundColor(color) - - def setPadding(self, left: int, top: int, right: int, bottom: int): - super().setPadding(left, top, right, bottom) - - def setPaddingRelative(self, start: int, top: int, end: int, bottom: int): - self._padding_left = start - self._padding_top = top - self._padding_right = end - self._padding_bottom = bottom - - # ─── text color getter ─────────────────────────────────── - - def getTextColor(self) -> int: - return self._text_color - - # ─── selection action mode ─────────────────────────────── - - def setCustomSelectionActionModeCallback(self, callback: Any): - self._custom_selection_action_mode_callback = callback - - def getCustomSelectionActionModeCallback(self) -> Any: - return getattr(self, '_custom_selection_action_mode_callback', None) - - # ─── break / justification strategy ────────────────────── - - def setBreakStrategy(self, break_strategy: int): - self._break_strategy = break_strategy - - def getBreakStrategy(self) -> int: - return getattr(self, '_break_strategy', 0) - - def setJustificationMode(self, justification_mode: int): - self._justification_mode = justification_mode - - def getJustificationMode(self) -> int: - return getattr(self, '_justification_mode', 0) - - def onMeasure(self, width_measure_spec: int, height_measure_spec: int): - from .view import MeasureSpec - w = MeasureSpec.getSize(width_measure_spec) - h = MeasureSpec.getSize(height_measure_spec) - if MeasureSpec.getMode(width_measure_spec) == MeasureSpec.AT_MOST: - text_len = len(self._text) * 4 - w = min(w, max(100, text_len)) - if MeasureSpec.getMode(height_measure_spec) == MeasureSpec.AT_MOST: - text_lines = self._line_count - h = min(h, max(20, text_lines * int(self._text_size) + 10)) - self.setMeasuredDimension(w, h) - - def onDraw(self, canvas: Any): - pass - - -# ═══════════════════════════════════════════════════════════════ -# EditText -# ═══════════════════════════════════════════════════════════════ - -class EditText(TextView): - """EditText — 可编辑文本""" - - def __init__(self, context=None): - super().__init__(context) - self._editable = True - self._selection_start = 0 - self._selection_end = 0 - - def setSelection(self, start: int, stop: Optional[int] = None): - if stop is None: - self._selection_start = start - self._selection_end = start - else: - self._selection_start = start - self._selection_end = stop - - def getSelectionStart(self) -> int: - return self._selection_start - - def getSelectionEnd(self) -> int: - return self._selection_end - - def selectAll(self): - self._selection_start = 0 - self._selection_end = len(self._text) - - def extendSelection(self, index: int): - self._selection_end = index - - def setText(self, text: str, buffer_type: int = TextView.BUFFER_TYPE_EDITABLE): - super().setText(text, buffer_type) - - def getEditableText(self): - return self._text - - -# ═══════════════════════════════════════════════════════════════ -# Button -# ═══════════════════════════════════════════════════════════════ - -class Button(TextView): - """Button — 按钮""" - - def __init__(self, context=None): - super().__init__(context) - self._clickable = True - self._focusable = True - - def onTouchEvent(self, event: MotionEvent) -> bool: - if event.getAction() == MotionEvent.ACTION_UP: - self.performClick() - return super().onTouchEvent(event) - - -class CheckBox(Button): - """CheckBox — 复选框""" - - def __init__(self, context=None): - super().__init__(context) - self._checked = False - - def setChecked(self, checked: bool): - self._checked = checked - - def isChecked(self) -> bool: - return self._checked - - def toggle(self): - self._checked = not self._checked - - def setOnCheckedChangeListener(self, listener: Optional[Callable]): - self._on_checked_change_listener = listener - - def getOnCheckedChangeListener(self) -> Optional[Callable]: - return getattr(self, '_on_checked_change_listener', None) - - def performClick(self) -> bool: - self.toggle() - return super().performClick() - - -class RadioButton(Button): - """RadioButton — 单选按钮""" - - def __init__(self, context=None): - super().__init__(context) - self._checked = False - - def setChecked(self, checked: bool): - self._checked = checked - - def isChecked(self) -> bool: - return self._checked - - def toggle(self): - self._checked = not self._checked - - def performClick(self) -> bool: - self.toggle() - return super().performClick() - - -class ToggleButton(Button): - """ToggleButton — 开关按钮""" - - def __init__(self, context=None): - super().__init__(context) - self._checked = False - self._text_on = "ON" - self._text_off = "OFF" - self._disabled_alpha = 0.5 - - def setChecked(self, checked: bool): - self._checked = checked - if self._checked: - self.setText(self._text_on) - else: - self.setText(self._text_off) - - def isChecked(self) -> bool: - return self._checked - - def toggle(self): - self.setChecked(not self._checked) - - def setTextOn(self, text_on: str): - self._text_on = text_on - if self._checked: - self.setText(text_on) - - def getTextOn(self) -> str: - return self._text_on - - def setTextOff(self, text_off: str): - self._text_off = text_off - if not self._checked: - self.setText(text_off) - - def getTextOff(self) -> str: - return self._text_off - - def performClick(self) -> bool: - self.toggle() - return super().performClick() - - -class Switch(Button): - """Switch — 开关组件""" - - def __init__(self, context=None): - super().__init__(context) - self._checked = False - self._show_text = True - self._split_track = False - self._thumb_text_padding: int = 0 - self._switch_min_width: int = 0 - self._switch_padding: int = 0 - self._thumb_drawable: Any = None - self._track_drawable: Any = None - self._text_on = "ON" - self._text_off = "OFF" - self._thumb_width = 40 - self._switch_width = 100 - - def isChecked(self) -> bool: - return self._checked - - def setChecked(self, checked: bool): - self._checked = checked - - def toggle(self): - self._checked = not self._checked - - def setShowText(self, show_text: bool): - self._show_text = show_text - - def getShowText(self) -> bool: - return self._show_text - - def setSplitTrack(self, split_track: bool): - self._split_track = split_track - - def getSplitTrack(self) -> bool: - return self._split_track - - def setThumbTextPadding(self, padding: int): - self._thumb_text_padding = padding - - def getThumbTextPadding(self) -> int: - return self._thumb_text_padding - - def setSwitchMinWidth(self, min_width: int): - self._switch_min_width = min_width - - def getSwitchMinWidth(self) -> int: - return self._switch_min_width - - def setSwitchPadding(self, padding: int): - self._switch_padding = padding - - def getSwitchPadding(self) -> int: - return self._switch_padding - - def getThumbDrawable(self) -> Any: - return self._thumb_drawable - - def setThumbDrawable(self, drawable: Any): - self._thumb_drawable = drawable - - def getTrackDrawable(self) -> Any: - return self._track_drawable - - def setTrackDrawable(self, drawable: Any): - self._track_drawable = drawable - - def setTextOn(self, text_on: str): - self._text_on = text_on - - def getTextOn(self) -> str: - return self._text_on - - def setTextOff(self, text_off: str): - self._text_off = text_off - - def getTextOff(self) -> str: - return self._text_off - - # ─── checked change listener ───────────────────────────── - - def setOnCheckedChangeListener(self, listener: Optional[Callable]): - self._on_checked_change_listener = listener - - def getOnCheckedChangeListener(self) -> Optional[Callable]: - return getattr(self, '_on_checked_change_listener', None) - - # ─── switch text appearance ────────────────────────────── - - def setSwitchTextAppearance(self, context: Any, resid: int): - self._switch_text_appearance = resid - - def getSwitchTextAppearance(self) -> int: - return getattr(self, '_switch_text_appearance', 0) - - def performClick(self) -> bool: - self.toggle() - return super().performClick() - - def onMeasure(self, width_measure_spec: int, height_measure_spec: int): - from .view import MeasureSpec - w = MeasureSpec.getSize(width_measure_spec) - h = MeasureSpec.getSize(height_measure_spec) - self.setMeasuredDimension(w, max(30, h)) - - -# ═══════════════════════════════════════════════════════════════ -# ImageView -# ═══════════════════════════════════════════════════════════════ - -class ImageView(View): - """ImageView — 图片视图""" - - # ScaleType常量 - ScaleType = type("ScaleType", (), { - "MATRIX": 0, - "FIT_XY": 1, - "FIT_START": 2, - "FIT_CENTER": 3, - "FIT_END": 4, - "CENTER": 5, - "CENTER_CROP": 6, - "CENTER_INSIDE": 7, - }) - - def __init__(self, context=None): - super().__init__(context) - self._drawable: Any = None - self._image_resource: int = 0 - self._uri: Any = None - self._scale_type: int = ImageView.ScaleType.FIT_CENTER - self._max_width: int = 0 - self._max_height: int = 0 - self._adjust_view_bounds: bool = False - self._crop_to_padding: bool = False - self._baseline: int = 0 - self._baseline_align_bottom: bool = False - self._image_matrix: Any = None - self._image_bitmap: Any = None - - def setImageDrawable(self, drawable: Any): - self._drawable = drawable - - def getDrawable(self) -> Any: - return self._drawable - - def setImageResource(self, res_id: int): - self._image_resource = res_id - - def setImageURI(self, uri: Any): - self._uri = uri - - def setImageBitmap(self, bitmap: Any): - self._image_bitmap = bitmap - - def getImageBitmap(self) -> Any: - return self._image_bitmap - - def setScaleType(self, scale_type: int): - self._scale_type = scale_type - - def getScaleType(self) -> int: - return self._scale_type - - def setMaxWidth(self, max_width: int): - self._max_width = max_width - - def setMaxHeight(self, max_height: int): - self._max_height = max_height - - def setAdjustViewBounds(self, adjust: bool): - self._adjust_view_bounds = adjust - - def getAdjustViewBounds(self) -> bool: - return self._adjust_view_bounds - - def setCropToPadding(self, crop: bool): - self._crop_to_padding = crop - - def getCropToPadding(self) -> bool: - return self._crop_to_padding - - # ─── color filter ───────────────────────────────────────── - - def setColorFilter(self, color_filter: Any): - self._color_filter = color_filter - - def getColorFilter(self) -> Any: - return getattr(self, '_color_filter', None) - - def clearColorFilter(self): - self._color_filter = None - - def setBaseline(self, baseline: int): - self._baseline = baseline - - def getBaseline(self) -> int: - return self._baseline - - def setBaselineAlignBottom(self, aligned: bool): - self._baseline_align_bottom = aligned - - def getBaselineAlignBottom(self) -> bool: - return self._baseline_align_bottom - - def setImageMatrix(self, matrix: Any): - self._image_matrix = matrix - - def getImageMatrix(self) -> Any: - return self._image_matrix - - def invalidateDrawable(self, dr: Any): - pass - - def onMeasure(self, width_measure_spec: int, height_measure_spec: int): - from .view import MeasureSpec - w = MeasureSpec.getSize(width_measure_spec) - h = MeasureSpec.getSize(height_measure_spec) - if w == 0: - w = 100 - if h == 0: - h = 100 - self.setMeasuredDimension(w, h) - - -class ImageButton(ImageView): - """ImageButton — 图片按钮""" - - def __init__(self, context=None): - super().__init__(context) - self._clickable = True - self._focusable = True - - def onTouchEvent(self, event: MotionEvent) -> bool: - if event.getAction() == MotionEvent.ACTION_UP: - self.performClick() - return super().onTouchEvent(event) - - -# ═══════════════════════════════════════════════════════════════ -# ProgressBar -# ═══════════════════════════════════════════════════════════════ - -class ProgressBar(View): - """ProgressBar — 进度条""" - - def __init__(self, context=None, attrs: Any = None, def_style_attr: int = 0, - def_style_res: int = 0): - super().__init__(context) - self._progress: int = 0 - self._max: int = 100 - self._indeterminate: bool = False - self._indeterminate_drawable: Any = None - self._progress_drawable: Any = None - self._secondary_progress: int = 0 - self._min: int = 0 - self._interpolator: Any = None - self._animation: Any = None - - def setProgress(self, progress: int): - self._progress = max(self._min, min(self._max, progress)) - - def getProgress(self) -> int: - return self._progress - - def setMax(self, max_val: int): - self._max = max_val - if self._progress > self._max: - self._progress = self._max - - def getMax(self) -> int: - return self._max - - def setMin(self, min_val: int): - self._min = min_val - - def getMin(self) -> int: - return self._min - - def incrementProgressBy(self, diff: int): - self.setProgress(self._progress + diff) - - def setIndeterminate(self, indeterminate: bool): - self._indeterminate = indeterminate - - def isIndeterminate(self) -> bool: - return self._indeterminate - - def getIndeterminateDrawable(self) -> Any: - return self._indeterminate_drawable - - def setIndeterminateDrawable(self, drawable: Any): - self._indeterminate_drawable = drawable - - def setProgressDrawable(self, drawable: Any): - self._progress_drawable = drawable - - def getProgressDrawable(self) -> Any: - return self._progress_drawable - - def setSecondaryProgress(self, secondary_progress: int): - self._secondary_progress = max(self._min, min(self._max, secondary_progress)) - - def getSecondaryProgress(self) -> int: - return self._secondary_progress - - def setInterpolator(self, interpolator: Any): - self._interpolator = interpolator - - def getInterpolator(self) -> Any: - return self._interpolator - - def setAnimation(self, animation: Any): - self._animation = animation - - def onMeasure(self, width_measure_spec: int, height_measure_spec: int): - from .view import MeasureSpec - w = MeasureSpec.getSize(width_measure_spec) - h = MeasureSpec.getSize(height_measure_spec) - if h == 0: - h = 20 - self.setMeasuredDimension(w, h) - - -class SeekBar(ProgressBar): - """SeekBar — 可拖动进度条""" - - def __init__(self, context=None, attrs: Any = None, - def_style_attr: int = 0, def_style_res: int = 0): - super().__init__(context, attrs, def_style_attr, def_style_res) - self._on_seek_bar_change_listener: Optional[Callable] = None - - class OnSeekBarChangeListener: - def onProgressChanged(self, seek_bar: "SeekBar", progress: int, from_user: bool): - pass - def onStartTrackingTouch(self, seek_bar: "SeekBar"): - pass - def onStopTrackingTouch(self, seek_bar: "SeekBar"): - pass - - def setOnSeekBarChangeListener(self, listener: Optional[OnSeekBarChangeListener]): - self._on_seek_bar_change_listener = listener - - def getOnSeekBarChangeListener(self) -> Optional[OnSeekBarChangeListener]: - return self._on_seek_bar_change_listener - - def setThumb(self, thumb: Any): - pass - - def getThumb(self) -> Any: - return None - - def setThumbOffset(self, thumb_offset: int): - pass - - def getThumbOffset(self) -> int: - return 0 - - def setKeyProgressIncrement(self, increment: int): - pass - - def getKeyProgressIncrement(self) -> int: - return 1 - - -class RatingBar(ProgressBar): - """RatingBar — 评分条""" - - def __init__(self, context=None, attrs: Any = None, - def_style_attr: int = 0, def_style_res: int = 0): - super().__init__(context, attrs, def_style_attr, def_style_res) - self._num_stars: int = 5 - self._rating: float = 0.0 - self._step_size: float = 0.5 - self._is_indicator: bool = False - self._on_rating_bar_change_listener: Optional[Callable] = None - - class OnRatingBarChangeListener: - def onRatingChanged(self, rating_bar: "RatingBar", rating: float, from_user: bool): - pass - - def setOnRatingBarChangeListener(self, listener: Optional[OnRatingBarChangeListener]): - self._on_rating_bar_change_listener = listener - - def getOnRatingBarChangeListener(self) -> Optional[OnRatingBarChangeListener]: - return self._on_rating_bar_change_listener - - def setNumStars(self, num_stars: int): - self._num_stars = max(1, num_stars) - - def getNumStars(self) -> int: - return self._num_stars - - def setRating(self, rating: float): - self._rating = max(0.0, min(float(self._num_stars), rating)) - - def getRating(self) -> float: - return self._rating - - def setStepSize(self, step_size: float): - self._step_size = max(0.1, step_size) - - def getStepSize(self) -> float: - return self._step_size - - def setIsIndicator(self, is_indicator: bool): - self._is_indicator = is_indicator - - def isIndicator(self) -> bool: - return self._is_indicator - - -# ═══════════════════════════════════════════════════════════════ -# AdapterView -# ═══════════════════════════════════════════════════════════════ - -class AdapterView(ViewGroup): - """AdapterView — 适配器视图基类""" - - CHOICE_MODE_NONE = 0 - CHOICE_MODE_SINGLE = 1 - CHOICE_MODE_MULTIPLE = 2 - CHOICE_MODE_MULTIPLE_MODAL = 3 - - INVALID_POSITION = -1 - INVALID_ROW_ID = -9223372036854775808 # Long.MIN_VALUE - - def __init__(self, context=None): - super().__init__(context) - self._adapter: Any = None - self._on_item_click_listener: Optional[Callable] = None - self._on_item_long_click_listener: Optional[Callable] = None - self._on_item_selected_listener: Optional[Callable] = None - self._choice_mode = AdapterView.CHOICE_MODE_NONE - self._checked_item_positions: Dict[int, bool] = {} - self._checked_id_positions: Dict[int, bool] = {} - self._selected_position = AdapterView.INVALID_POSITION - self._empty_view: Optional[View] = None - self._selection_notifier: Any = None - - def setAdapter(self, adapter: Any): - self._adapter = adapter - - def getAdapter(self) -> Any: - return self._adapter - - def setOnItemClickListener(self, listener: Optional[Callable]): - self._on_item_click_listener = listener - - def getOnItemClickListener(self) -> Optional[Callable]: - return self._on_item_click_listener - - def setOnItemLongClickListener(self, listener: Optional[Callable]): - self._on_item_long_click_listener = listener - - def getOnItemLongClickListener(self) -> Optional[Callable]: - return self._on_item_long_click_listener - - def setOnItemSelectedListener(self, listener: Optional[Callable]): - self._on_item_selected_listener = listener - - def getOnItemSelectedListener(self) -> Optional[Callable]: - return self._on_item_selected_listener - - def getSelectedItemPosition(self) -> int: - return self._selected_position - - def getSelectedItemId(self) -> int: - return 0 - - def getSelectedItem(self) -> Any: - if self._adapter is not None and 0 <= self._selected_position < self._adapter.getCount(): - return self._adapter.getItem(self._selected_position) - return None - - def setSelection(self, position: int): - self._selected_position = position - - def setEmptyView(self, empty_view: View): - self._empty_view = empty_view - - def getEmptyView(self) -> Optional[View]: - return self._empty_view - - def setChoiceMode(self, choice_mode: int): - self._choice_mode = choice_mode - - def getChoiceMode(self) -> int: - return self._choice_mode - - def setItemChecked(self, position: int, checked: bool): - if self._choice_mode == AdapterView.CHOICE_MODE_SINGLE: - self._checked_item_positions.clear() - self._checked_item_positions[position] = checked - elif self._choice_mode == AdapterView.CHOICE_MODE_MULTIPLE: - self._checked_item_positions[position] = checked - - def isItemChecked(self, position: int) -> bool: - return self._checked_item_positions.get(position, False) - - def getCheckedItemPositions(self) -> Dict[int, bool]: - return dict(self._checked_item_positions) - - def getCheckedItemCount(self) -> int: - return len(self._checked_item_positions) - - def getCheckedItemIds(self) -> List[int]: - return list(self._checked_id_positions.keys()) - - def performItemClick(self, view: View, position: int, id_: int) -> bool: - if self._on_item_click_listener: - self._on_item_click_listener(self, view, position, id_) - return True - return False - - -# ═══════════════════════════════════════════════════════════════ -# ListView -# ═══════════════════════════════════════════════════════════════ - -class ListView(AdapterView): - """ListView — 列表视图""" - - CHOICE_MODE_NONE = 0 - CHOICE_MODE_SINGLE = 1 - CHOICE_MODE_MULTIPLE = 2 - CHOICE_MODE_MULTIPLE_MODAL = 3 - - TRANSCRIPT_MODE_DISABLED = 0 - TRANSCRIPT_MODE_NORMAL = 1 - TRANSCRIPT_MODE_ALWAYS_SCROLL = 2 - - def __init__(self, context=None): - super().__init__(context) - self._divider: Any = None - self._divider_height: int = 0 - self._footer_divider: Any = None - self._header_divider: Any = None - self._footer_views: List[View] = [] - self._header_views: List[View] = [] - self._transcript_mode = ListView.TRANSCRIPT_MODE_DISABLED - self._items_can_focus = True - self._smooth_scroll_position = 0 - self._stack_from_bottom = False - self._selection_division = 0 - - def setAdapter(self, adapter: Any): - super().setAdapter(adapter) - - def getAdapter(self) -> Any: - return super().getAdapter() - - def setSelection(self, position: int): - super().setSelection(position) - - def smoothScrollToPosition(self, position: int): - self._smooth_scroll_position = position - - def setDivider(self, divider: Any): - self._divider = divider - - def setDividerHeight(self, height: int): - self._divider_height = height - - def getDivider(self) -> Any: - return self._divider - - def getDividerHeight(self) -> int: - return self._divider_height - - def addFooterView(self, v: View, data: Any = None, is_selectable: bool = True): - self._footer_views.append(v) - - def removeFooterView(self, v: View) -> bool: - if v in self._footer_views: - self._footer_views.remove(v) - return True - return False - - def addHeaderView(self, v: View, data: Any = None, is_selectable: bool = True): - self._header_views.append(v) - - def removeHeaderView(self, v: View) -> bool: - if v in self._header_views: - self._header_views.remove(v) - return True - return False - - def getHeaderViewsCount(self) -> int: - return len(self._header_views) - - def getFooterViewsCount(self) -> int: - return len(self._footer_views) - - def setTranscriptMode(self, mode: int): - self._transcript_mode = mode - - def getTranscriptMode(self) -> int: - return self._transcript_mode - - def setItemsCanFocus(self, items_can_focus: bool): - self._items_can_focus = items_can_focus - - def getItemsCanFocus(self) -> bool: - return self._items_can_focus - - def setStackFromBottom(self, stack_from_bottom: bool): - self._stack_from_bottom = stack_from_bottom - - def isStackFromBottom(self) -> bool: - return self._stack_from_bottom - - def getCheckItemIds(self) -> List[int]: - return list(self._checked_id_positions.keys()) - - def clearChoices(self): - self._checked_item_positions.clear() - self._checked_id_positions.clear() - - -class GridView(AdapterView): - """GridView — 网格视图""" - - def __init__(self, context=None): - super().__init__(context) - self._num_columns: int = 1 - self._column_width: int = 0 - self._horizontal_spacing: int = 0 - self._vertical_spacing: int = 0 - self._stretch_mode: int = 0 # NO_STRETCH - self._gravity = Gravity.LEFT - - STRETCH_NO_STRETCH = 0 - STRETCH_STRETCH_COLUMN_WIDTH = 1 - STRETCH_STRETCH_SPACING = 2 - STRETCH_STRETCH_SPACING_UNIFORM = 3 - STRETCH_STRETCH_COLUMN_WIDTH_UNIFORM = 4 - - def setNumColumns(self, num_columns: int): - self._num_columns = max(1, num_columns) - - def getNumColumns(self) -> int: - return self._num_columns - - def setColumnWidth(self, column_width: int): - self._column_width = column_width - - def getColumnWidth(self) -> int: - return self._column_width - - def setHorizontalSpacing(self, horizontal_spacing: int): - self._horizontal_spacing = horizontal_spacing - - def getHorizontalSpacing(self) -> int: - return self._horizontal_spacing - - def setVerticalSpacing(self, vertical_spacing: int): - self._vertical_spacing = vertical_spacing - - def getVerticalSpacing(self) -> int: - return self._vertical_spacing - - def setStretchMode(self, stretch_mode: int): - self._stretch_mode = stretch_mode - - def getStretchMode(self) -> int: - return self._stretch_mode - - def setGravity(self, gravity: int): - self._gravity = gravity - - def getGravity(self) -> int: - return self._gravity - - def setAdapter(self, adapter: Any): - super().setAdapter(AdapterView) - - -# ═══════════════════════════════════════════════════════════════ -# RecyclerView -# ═══════════════════════════════════════════════════════════════ - -class RecyclerView(ViewGroup): - """RecyclerView — 可回收视图列表""" - - HORIZONTAL = 0 - VERTICAL = 1 - NO_POSITION = -1 - INVALID_TYPE = -1 - TOUCH_SLOP_DEFAULT = 0 - TOUCH_SLOP_PAGING = 1 - - def __init__(self, context=None): - super().__init__(context) - self._adapter: Optional["RecyclerView.Adapter"] = None - self._layout_manager: Optional["RecyclerView.LayoutManager"] = None - self._item_decorations: List[Any] = [] - self._recycler: Any = None - self._child_attached_listener: Optional[Callable] = None - self._child_detached_listener: Optional[Callable] = None - self._item_animator: Any = None - self._on_scroll_listener: Optional[Callable] = None - self._nested_scrolling_enabled = True - self._scroll_state = RecyclerView.SCROLL_STATE_IDLE - self._layout_frozen = False - self._suppress_layout = False - self._clip_to_padding = True - - SCROLL_STATE_IDLE = 0 - SCROLL_STATE_DRAGGING = 1 - SCROLL_STATE_SETTLING = 2 - - # ─── Adapter ───────────────────────────────────────────── - - class Adapter(Stub): - """RecyclerView.Adapter — 适配器""" - - def __init__(self): - super().__init__() - self._observable: Any = None - self._has_stable_ids = False - self._state_restoration_policy = 0 - - def onCreateViewHolder(self, parent: "RecyclerView", view_type: int) -> "RecyclerView.ViewHolder": - raise NotImplementedError - - def onBindViewHolder(self, holder: "RecyclerView.ViewHolder", position: int): - raise NotImplementedError - - def getItemCount(self) -> int: - return 0 - - def getItemViewType(self, position: int) -> int: - return 0 - - def getItemId(self, position: int) -> int: - return position - - def hasStableIds(self) -> bool: - return self._has_stable_ids - - def setHasStableIds(self, has_stable_ids: bool): - self._has_stable_ids = has_stable_ids - - def onAttachedToRecyclerView(self, recycler_view: "RecyclerView"): - pass - - def onDetachedFromRecyclerView(self, recycler_view: "RecyclerView"): - pass - - def onViewAttachedToWindow(self, holder: "RecyclerView.ViewHolder"): - pass - - def onViewDetachedFromWindow(self, holder: "RecyclerView.ViewHolder"): - pass - - def onViewRecycled(self, holder: "RecyclerView.ViewHolder"): - pass - - def onFailedToRecycleView(self, holder: "RecyclerView.ViewHolder") -> bool: - return False - - def notifyDataSetChanged(self): - for observer in getattr(self, '_observers', []): - observer.onChanged() - - def notifyItemChanged(self, position, payload=None): - for observer in getattr(self, '_observers', []): - observer.onItemRangeChanged(position, 1, payload) - - def notifyItemInserted(self, position): - for observer in getattr(self, '_observers', []): - observer.onItemRangeInserted(position, 1) - - def notifyItemRemoved(self, position): - for observer in getattr(self, '_observers', []): - observer.onItemRangeRemoved(position, 1) - - def notifyItemMoved(self, fromPosition, toPosition): - for observer in getattr(self, '_observers', []): - observer.onItemRangeMoved(fromPosition, toPosition, 1) - - def notifyItemRangeChanged(self, positionStart, itemCount, payload=None): - for observer in getattr(self, '_observers', []): - observer.onItemRangeChanged(positionStart, itemCount, payload) - - def notifyItemRangeInserted(self, positionStart, itemCount): - for observer in getattr(self, '_observers', []): - observer.onItemRangeInserted(positionStart, itemCount) - - def notifyItemRangeRemoved(self, positionStart, itemCount): - for observer in getattr(self, '_observers', []): - observer.onItemRangeRemoved(positionStart, itemCount) - - def registerAdapterDataObserver(self, observer): - if not hasattr(self, '_observers'): - self._observers = [] - self._observers.append(observer) - - def unregisterAdapterDataObserver(self, observer): - if hasattr(self, '_observers'): - try: - self._observers.remove(observer) - except ValueError: - pass - - def getStateRestorationPolicy(self) -> int: - return 0 - - # ─── ViewHolder ─────────────────────────────────────────── - - class ViewHolder(Stub): - """RecyclerView.ViewHolder — 视图持有者""" - - FLAG_BOUND = 1 - FLAG_NOT_RECYCLABLE = 2 - FLAG_RETURNED_FROM_SCRAP = 4 - FLAG_CHANGED = 8 - FLAG_IGNORE = 16 - FLAG_TMP_DETACHED = 32 - FLAG_UPDATE = 64 - FLAG_ADAPTER_POSITION_UNKNOWN = 128 - FLAG_ADAPTER_FULLUPDATE = 256 - FLAG_MOVED = 512 - FLAG_APPEARED_IN_PRE_LAYOUT = 1024 - - def __init__(self, item_view: View): - super().__init__() - self.itemView = item_view - self._position = RecyclerView.NO_POSITION - self._old_position = RecyclerView.NO_POSITION - self._item_id = RecyclerView.NO_ID - self._item_view_type = RecyclerView.INVALID_TYPE - self._flags = 0 - self._pre_layout_position = RecyclerView.NO_POSITION - self._is_recyclable = True - self._scrap_container: Optional[RecyclerView] = None - self._shadowed_holder: Optional["RecyclerView.ViewHolder"] = None - self._shadowing_holder: Optional["RecyclerView.ViewHolder"] = None - - NO_ID = -1 - - def getAdapterPosition(self) -> int: - return self._position - - def getLayoutPosition(self) -> int: - return self._position - - def getOldPosition(self) -> int: - return self._old_position - - def getItemId(self) -> int: - return self._item_id - - def getItemViewType(self) -> int: - return self._item_view_type - - def setIsRecyclable(self, recyclable: bool): - self._is_recyclable = recyclable - - def isRecyclable(self) -> bool: - return self._is_recyclable - - def getBindingAdapterPosition(self) -> int: - return self._position - - # ─── LayoutManager ──────────────────────────────────────── - - class LayoutManager(Stub): - """RecyclerView.LayoutManager — 布局管理器""" - - def __init__(self): - super().__init__() - self._recycler_view: Optional["RecyclerView"] = None - self._child_count = 0 - self._width = 0 - self._height = 0 - self._auto_measure = True - self._item_prefetch_enabled = True - - def onLayoutChildren(self, recycler: Any, state: Any): - pass - - def getChildCount(self) -> int: - return self._child_count - - def getPosition(self, view: View) -> int: - if isinstance(view, View): - return 0 - return RecyclerView.NO_POSITION - - def scrollToPosition(self, position: int): - pass - - def smoothScrollToPosition(self, recycler_view: "RecyclerView", state: Any, position: int): - pass - - def onAdapterChanged(self, old_adapter: Any, new_adapter: Any): - pass - - def generateDefaultLayoutParams(self) -> ViewGroup.LayoutParams: - return ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT) - - def getWidth(self) -> int: - return self._width - - def getHeight(self) -> int: - return self._height - - def setAutoMeasureEnabled(self, enabled: bool): - self._auto_measure = enabled - - def isAutoMeasureEnabled(self) -> bool: - return self._auto_measure - - def requestLayout(self): - pass - - def requestSimpleAnimationsInNextLayout(self): - pass - - def assertNotInLayoutOrScroll(self, tag: str = ""): - pass - - def getPaddingLeft(self) -> int: - return 0 - - def getPaddingTop(self) -> int: - return 0 - - def getPaddingRight(self) -> int: - return 0 - - def getPaddingBottom(self) -> int: - return 0 - - # ─── ItemDecoration ────────────────────────────────────── - - class ItemDecoration(Stub): - """RecyclerView.ItemDecoration — 条目装饰""" - - def onDraw(self, c: Any, parent: "RecyclerView", state: Any): - pass - - def onDrawOver(self, c: Any, parent: "RecyclerView", state: Any): - pass - - def getItemOffsets(self, out_rect: Any, view: View, parent: "RecyclerView", state: Any): - if hasattr(out_rect, "set"): - out_rect.set(0, 0, 0, 0) - - # ─── RecycledViewPool ───────────────────────────────────── - - class RecycledViewPool(Stub): - """RecyclerView.RecycledViewPool — 回收视图池""" - - def __init__(self): - super().__init__() - self._max_recycled_views = 5 - - def getRecycledView(self, view_type: int) -> Optional["RecyclerView.ViewHolder"]: - return None - - def putRecycledView(self, scrap: "RecyclerView.ViewHolder"): - pass - - def setMaxRecycledViews(self, view_type: int, max_count: int): - self._max_recycled_views = max_count - - def clear(self): - pass - - # ─── Recycler ───────────────────────────────────────────── - - class Recycler(Stub): - """RecyclerView.Recycler — 回收器""" - - def __init__(self): - super().__init__() - self._view_cache: Dict[int, List["RecyclerView.ViewHolder"]] = {} - - def getViewForPosition(self, position: int) -> View: - raise AndroidOnlyError("RecyclerView.Recycler.getViewForPosition") - - def recycleView(self, view: View): - pass - - def getRecycledViewPool(self) -> "RecyclerView.RecycledViewPool": - return RecyclerView.RecycledViewPool() - - def setViewCacheSize(self, view_count: int): - pass - - def clear(self): - self._view_cache.clear() - - # ─── RecyclerView Instance Methods ─────────────────────── - - def setAdapter(self, adapter: Optional["RecyclerView.Adapter"]): - self._adapter = adapter - - def getAdapter(self) -> Optional["RecyclerView.Adapter"]: - return self._adapter - - def setLayoutManager(self, layout_manager: Optional["RecyclerView.LayoutManager"]): - self._layout_manager = layout_manager - if layout_manager: - layout_manager._recycler_view = self - - def getLayoutManager(self) -> Optional["RecyclerView.LayoutManager"]: - return self._layout_manager - - def addItemDecoration(self, decoration: "RecyclerView.ItemDecoration", index: int = -1): - if index < 0 or index >= len(self._item_decorations): - self._item_decorations.append(decoration) - else: - self._item_decorations.insert(index, decoration) - - def removeItemDecoration(self, decoration: "RecyclerView.ItemDecoration"): - if decoration in self._item_decorations: - self._item_decorations.remove(decoration) - - def getItemDecorationAt(self, index: int) -> "RecyclerView.ItemDecoration": - return self._item_decorations[index] - - def getItemDecorationCount(self) -> int: - return len(self._item_decorations) - - def swapAdapter(self, adapter: Optional["RecyclerView.Adapter"], remove_and_recycle: bool): - self._adapter = adapter - - def getRecycledViewPool(self) -> "RecyclerView.RecycledViewPool": - return RecyclerView.RecycledViewPool() - - def setRecycledViewPool(self, pool: "RecyclerView.RecycledViewPool"): - pass - - def getRecycler(self) -> "RecyclerView.Recycler": - return RecyclerView.Recycler() - - def setItemAnimator(self, animator: Any): - self._item_animator = animator - - def getItemAnimator(self) -> Any: - return self._item_animator - - def setOnScrollListener(self, listener: Optional[Callable]): - self._on_scroll_listener = listener - - def addOnScrollListener(self, listener: Callable): - pass - - def removeOnScrollListener(self, listener: Callable): - pass - - def clearOnScrollListeners(self): - pass - - def addOnChildAttachStateChangeListener(self, listener: Callable): - self._child_attached_listener = listener - - def removeOnChildAttachStateChangeListener(self, listener: Callable): - pass - - def setHasFixedSize(self, has_fixed_size: bool): - pass - - def hasFixedSize(self) -> bool: - return False - - def setNestedScrollingEnabled(self, enabled: bool): - self._nested_scrolling_enabled = enabled - - def isNestedScrollingEnabled(self) -> bool: - return self._nested_scrolling_enabled - - def startNestedScroll(self, axes: int) -> bool: - return False - - def stopNestedScroll(self): - pass - - def hasNestedScrollingParent(self) -> bool: - return False - - def dispatchNestedPreScroll(self, dx: int, dy: int, consumed: List[int], offset: List[int]) -> bool: - return False - - def dispatchNestedScroll(self, dx_consumed: int, dy_consumed: int, - dx_unconsumed: int, dy_unconsumed: int, - offset: List[int]) -> bool: - return False - - def dispatchNestedPreFling(self, velocity_x: float, velocity_y: float) -> bool: - return False - - def dispatchNestedFling(self, velocity_x: float, velocity_y: float, consumed: bool) -> bool: - return False - - def stopScroll(self): - pass - - def getScrollState(self) -> int: - return self._scroll_state - - def setLayoutFrozen(self, frozen: bool): - self._layout_frozen = frozen - - def isLayoutFrozen(self) -> bool: - return self._layout_frozen - - def smoothScrollBy(self, dx: int, dy: int): - pass - - def scrollBy(self, x: int, y: int): - pass - - def computeHorizontalScrollOffset(self) -> int: - return 0 - - def computeHorizontalScrollRange(self) -> int: - return 0 - - def computeVerticalScrollOffset(self) -> int: - return 0 - - def computeVerticalScrollRange(self) -> int: - return 0 - - def findViewHolderForAdapterPosition(self, position: int) -> Optional["RecyclerView.ViewHolder"]: - return None - - def findViewHolderForLayoutPosition(self, position: int) -> Optional["RecyclerView.ViewHolder"]: - return None - - def findViewHolderForItemId(self, id_: int) -> Optional["RecyclerView.ViewHolder"]: - return None - - NO_ID = -1 - - -# 为 __all__ 导出创建别名 -RecyclerViewAdapter = RecyclerView.Adapter -RecyclerViewViewHolder = RecyclerView.ViewHolder -RecyclerViewLayoutManager = RecyclerView.LayoutManager - - -class LinearLayoutManager(RecyclerView.LayoutManager): - """LinearLayoutManager — 线性布局管理器""" - - HORIZONTAL = 0 - VERTICAL = 1 - - def __init__(self, context: Any = None, orientation: int = VERTICAL, reverse_layout: bool = False): - super().__init__() - self._orientation = orientation - self._reverse_layout = reverse_layout - self._stack_from_end = False - self._item_count = 0 - - def setOrientation(self, orientation: int): - self._orientation = orientation - - def getOrientation(self) -> int: - return self._orientation - - def setReverseLayout(self, reverse_layout: bool): - self._reverse_layout = reverse_layout - - def getReverseLayout(self) -> bool: - return self._reverse_layout - - def setStackFromEnd(self, stack_from_end: bool): - self._stack_from_end = stack_from_end - - def getStackFromEnd(self) -> bool: - return self._stack_from_end - - def generateDefaultLayoutParams(self) -> ViewGroup.LayoutParams: - return ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT) - - def onLayoutChildren(self, recycler: Any, state: Any): - pass - - -class GridLayoutManager(LinearLayoutManager): - """GridLayoutManager — 网格布局管理器""" - - def __init__(self, context: Any = None, span_count: int = 1, - orientation: int = LinearLayoutManager.VERTICAL, - reverse_layout: bool = False): - super().__init__(context, orientation, reverse_layout) - self._span_count = max(1, span_count) - - class SpanSizeLookup(Stub): - """GridLayoutManager.SpanSizeLookup — 跨度大小查找""" - - def __init__(self): - super().__init__() - self._span_index_cache = {} - - def getSpanSize(self, position: int) -> int: - return 1 - - def getSpanIndex(self, position: int, span_count: int) -> int: - return position % span_count - - def getSpanGroupIndex(self, position: int, span_count: int) -> int: - return position // span_count - - def invalidateSpanIndexCache(self): - self._span_index_cache.clear() - - def setSpanCount(self, span_count: int): - self._span_count = max(1, span_count) - - def getSpanCount(self) -> int: - return self._span_count - - def setSpanSizeLookup(self, span_size_lookup: SpanSizeLookup): - pass - - def getSpanSizeLookup(self) -> SpanSizeLookup: - return GridLayoutManager.SpanSizeLookup() - - -class StaggeredGridLayoutManager(RecyclerView.LayoutManager): - """StaggeredGridLayoutManager — 瀑布流布局管理器""" - - HORIZONTAL = 0 - VERTICAL = 1 - - GAP_HANDLING_NONE = 0 - GAP_HANDLING_LAZY = 1 - GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS = 2 - - def __init__(self, span_count: int = 1, orientation: int = VERTICAL): - super().__init__() - self._span_count = max(1, span_count) - self._orientation = orientation - self._gap_strategy = StaggeredGridLayoutManager.GAP_HANDLING_LAZY - - def setSpanCount(self, span_count: int): - self._span_count = max(1, span_count) - - def getSpanCount(self) -> int: - return self._span_count - - def setOrientation(self, orientation: int): - self._orientation = orientation - - def getOrientation(self) -> int: - return self._orientation - - def setGapStrategy(self, gap_strategy: int): - self._gap_strategy = gap_strategy - - def getGapStrategy(self) -> int: - return self._gap_strategy - - -# ═══════════════════════════════════════════════════════════════ -# LinearLayout -# ═══════════════════════════════════════════════════════════════ - -class LinearLayout(ViewGroup): - """LinearLayout — 线性布局""" - - HORIZONTAL = 0 - VERTICAL = 1 - SHOW_DIVIDER_NONE = 0 - SHOW_DIVIDER_BEGINNING = 1 - SHOW_DIVIDER_MIDDLE = 2 - SHOW_DIVIDER_END = 4 - - def __init__(self, context=None): - super().__init__(context) - self._orientation = LinearLayout.VERTICAL - self._gravity = Gravity.NO_GRAVITY - self._weight_sum: float = 0.0 - self._baseline_aligned = True - self._baseline_aligned_child_index = -1 - self._divider: Any = None - self._divider_padding: int = 0 - self._divider_show: int = LinearLayout.SHOW_DIVIDER_NONE - self._total_length: int = 0 - - def setOrientation(self, orientation: int): - self._orientation = orientation - - def getOrientation(self) -> int: - return self._orientation - - def setGravity(self, gravity: int): - self._gravity = gravity - - def getGravity(self) -> int: - return self._gravity - - def setWeightSum(self, weight_sum: float): - self._weight_sum = weight_sum - - def getWeightSum(self) -> float: - return self._weight_sum - - def setBaselineAligned(self, baseline_aligned: bool): - self._baseline_aligned = baseline_aligned - - def isBaselineAligned(self) -> bool: - return self._baseline_aligned - - def setDividerDrawable(self, divider: Any): - self._divider = divider - - def setShowDividers(self, show_dividers: int): - self._divider_show = show_dividers - - def getShowDividers(self) -> int: - return self._divider_show - - def getDividerDrawable(self) -> Any: - return self._divider - - def setDividerPadding(self, padding: int): - self._divider_padding = padding - - def getDividerPadding(self) -> int: - return self._divider_padding - - def getBaseline(self) -> int: - return -1 - - def onMeasure(self, width_measure_spec: int, height_measure_spec: int): - from .view import MeasureSpec - w = MeasureSpec.getSize(width_measure_spec) - h = MeasureSpec.getSize(height_measure_spec) - self.setMeasuredDimension(w, h) - - def onLayout(self, changed: bool, l: int, t: int, r: int, b: int): - pass - - class LayoutParams(ViewGroup.MarginLayoutParams): - """LinearLayout.LayoutParams — 线性布局参数""" - - def __init__(self, width: int = ViewGroup.LayoutParams.WRAP_CONTENT, - height: int = ViewGroup.LayoutParams.WRAP_CONTENT, - weight: float = 0.0): - super().__init__(width, height) - self.weight = weight - self.gravity = -1 - - def setLayoutWeight(self, weight: float): - self.weight = weight - - -# ═══════════════════════════════════════════════════════════════ -# RelativeLayout -# ═══════════════════════════════════════════════════════════════ - -class RelativeLayout(ViewGroup): - """RelativeLayout — 相对布局""" - - TRUE = -1 - FALSE = 0 - - # 规则常量 - ALIGN_PARENT_LEFT = 0 - ALIGN_PARENT_TOP = 1 - ALIGN_PARENT_RIGHT = 2 - ALIGN_PARENT_BOTTOM = 3 - ALIGN_PARENT_START = 4 - ALIGN_PARENT_END = 5 - CENTER_IN_PARENT = 6 - CENTER_HORIZONTAL = 7 - CENTER_VERTICAL = 8 - ABOVE = 9 - BELOW = 10 - LEFT_OF = 11 - RIGHT_OF = 12 - ALIGN_LEFT = 13 - ALIGN_TOP = 14 - ALIGN_RIGHT = 15 - ALIGN_BOTTOM = 16 - ALIGN_BASELINE = 17 - ALIGN_WITH_PARENT_IF_MISSING = 18 - START_OF = 19 - END_OF = 20 - ALIGN_START = 21 - ALIGN_END = 22 - - def __init__(self, context=None): - super().__init__(context) - self._gravity = Gravity.NO_GRAVITY - self._ignore_gravity_of_view_ids: List[int] = [] - self._has_left_weight = False - self._has_right_weight = False - self._has_top_weight = False - self._has_bottom_weight = False - - def setGravity(self, gravity: int): - self._gravity = gravity - - def getGravity(self) -> int: - return self._gravity - - def setHorizontalGravity(self, horizontal_gravity: int): - pass - - def setVerticalGravity(self, vertical_gravity: int): - pass - - def onMeasure(self, width_measure_spec: int, height_measure_spec: int): - from .view import MeasureSpec - w = MeasureSpec.getSize(width_measure_spec) - h = MeasureSpec.getSize(height_measure_spec) - self.setMeasuredDimension(w, h) - - def onLayout(self, changed: bool, l: int, t: int, r: int, b: int): - pass - - class LayoutParams(ViewGroup.MarginLayoutParams): - """RelativeLayout.LayoutParams — 相对布局参数""" - - def __init__(self, width: int = ViewGroup.LayoutParams.WRAP_CONTENT, - height: int = ViewGroup.LayoutParams.WRAP_CONTENT): - super().__init__(width, height) - self._rules: Dict[int, int] = {} - self.align_with_parent = False - - def addRule(self, verb: int, subject: int = -1): - self._rules[verb] = subject - - def removeRule(self, verb: int): - self._rules.pop(verb, None) - - def getRule(self, verb: int) -> int: - return self._rules.get(verb, 0) - - def getRules(self) -> Dict[int, int]: - return dict(self._rules) - - def addLayoutRules(self, layout_rules: int): - pass - - def removeLayoutRule(self, layout_rule: int): - pass - - -# ═══════════════════════════════════════════════════════════════ -# FrameLayout -# ═══════════════════════════════════════════════════════════════ - -class FrameLayout(ViewGroup): - """FrameLayout — 帧布局""" - - def __init__(self, context=None): - super().__init__(context) - self._foreground: Any = None - self._foreground_gravity = Gravity.FILL - self._measure_all_children = False - self._foreground_bounds_padding: int = 0 - self._foreground_in_padding = False - - def setForeground(self, drawable: Any): - self._foreground = drawable - - def getForeground(self) -> Any: - return self._foreground - - def setForegroundGravity(self, gravity: int): - self._foreground_gravity = gravity - - def getForegroundGravity(self) -> int: - return self._foreground_gravity - - def setMeasureAllChildren(self, measure_all: bool): - self._measure_all_children = measure_all - - def getMeasureAllChildren(self) -> bool: - return self._measure_all_children - - def setForegroundBoundsPadding(self, padding: int): - self._foreground_bounds_padding = padding - - def onMeasure(self, width_measure_spec: int, height_measure_spec: int): - from .view import MeasureSpec - w = MeasureSpec.getSize(width_measure_spec) - h = MeasureSpec.getSize(height_measure_spec) - if self._measure_all_children: - for child in self._children: - child.measure(width_measure_spec, height_measure_spec) - self.setMeasuredDimension(w, h) - - def onLayout(self, changed: bool, l: int, t: int, r: int, b: int): - pass - - def gatherTransparentRegion(self, region: Any) -> bool: - return False - - -# ═══════════════════════════════════════════════════════════════ -# TableLayout / TableRow -# ═══════════════════════════════════════════════════════════════ - -class TableLayout(LinearLayout): - """TableLayout — 表格布局""" - - def __init__(self, context=None): - super().__init__(context) - self._stretchable_columns: List[bool] = [] - self._shrinkable_columns: List[bool] = [] - self._collapsed_columns: List[bool] = [] - self._max_columns: int = 0 - - def setStretchAllColumns(self, stretch_all: bool): - pass - - def setShrinkAllColumns(self, shrink_all: bool): - pass - - def setColumnStretchable(self, column_index: int, stretchable: bool): - pass - - def setColumnShrinkable(self, column_index: int, shrinkable: bool): - pass - - def setColumnCollapsed(self, column_index: int, collapsed: bool): - pass - - def isColumnStretchable(self, column_index: int) -> bool: - return False - - def isColumnShrinkable(self, column_index: int) -> bool: - return False - - def isColumnCollapsed(self, column_index: int) -> bool: - return False - - -class TableRow(LinearLayout): - """TableRow — 表格行""" - - def __init__(self, context=None): - super().__init__(context) - self._layout_params: Dict[int, Any] = {} - - class LayoutParams(LinearLayout.LayoutParams): - """TableRow.LayoutParams — 表格行布局参数""" - - def __init__(self, width: int = ViewGroup.LayoutParams.WRAP_CONTENT, - height: int = ViewGroup.LayoutParams.WRAP_CONTENT): - super().__init__(width, height) - self.column = -1 - self.span = 1 - - -# ═══════════════════════════════════════════════════════════════ -# ScrollView / HorizontalScrollView -# ═══════════════════════════════════════════════════════════════ - -class ScrollView(FrameLayout): - """ScrollView — 滚动视图""" - - def __init__(self, context=None): - super().__init__(context) - self._fill_viewport = False - self._smooth_scrolling_enabled = True - self._max_scroll_amount = 0 - self._scroll_position = 0 - self._is_layout_after_scroll = False - - def setFillViewport(self, fill_viewport: bool): - self._fill_viewport = fill_viewport - - def isFillViewport(self) -> bool: - return self._fill_viewport - - def setSmoothScrollingEnabled(self, enabled: bool): - self._smooth_scrolling_enabled = enabled - - def isSmoothScrollingEnabled(self) -> bool: - return self._smooth_scrolling_enabled - - def fullScroll(self, direction: int) -> bool: - return True - - def arrowScroll(self, direction: int) -> bool: - return True - - def smoothScrollTo(self, x, y): - self._scroll_x = x - self._scroll_y = y - - def smoothScrollBy(self, dx, dy): - self._scroll_x = getattr(self, '_scroll_x', 0) + dx - self._scroll_y = getattr(self, '_scroll_y', 0) + dy - - def pageScroll(self, direction: int) -> bool: - return True - - def getMaxScrollAmount(self) -> int: - return self._max_scroll_amount - - def computeScrollDeltaToGetChildRectOnScreen(self, rect: Any) -> int: - return 0 - - def isLayoutAfterScroll(self) -> bool: - return self._is_layout_after_scroll - - -class HorizontalScrollView(FrameLayout): - """HorizontalScrollView — 水平滚动视图""" - - def __init__(self, context=None): - super().__init__(context) - self._fill_viewport = False - - def setFillViewport(self, fill_viewport: bool): - self._fill_viewport = fill_viewport - - def isFillViewport(self) -> bool: - return self._fill_viewport - - def fullScroll(self, direction: int) -> bool: - return True - - def arrowScroll(self, direction: int) -> bool: - return True - - def smoothScrollTo(self, x, y): - self._scroll_x = x - self._scroll_y = y - - def smoothScrollBy(self, dx, dy): - self._scroll_x = getattr(self, '_scroll_x', 0) + dx - self._scroll_y = getattr(self, '_scroll_y', 0) + dy - - def pageScroll(self, direction: int) -> bool: - return True - - -# ═══════════════════════════════════════════════════════════════ -# WebView -# ═══════════════════════════════════════════════════════════════ - -class WebView(ViewGroup): - """WebView — 网页视图""" - - # 布局模式 - NORMAL = 0 - SMALL_SCREEN = 1 - LARGE_SCREEN = 2 - - def __init__(self, context=None): - super().__init__(context) - self._settings: "WebSettings" = WebSettings() - self._web_view_client: Optional[Callable] = None - self._web_chrome_client: Optional[Callable] = None - self._can_go_back: bool = False - self._can_go_forward: bool = False - self._current_url: str = "" - self._history: List[str] = [] - self._current_index: int = -1 - self._download_listener: Optional[Callable] = None - self._find_listener: Optional[Callable] = None - self._picture_listener: Optional[Callable] = None - - def loadUrl(self, url: str, additional_headers: Optional[Dict[str, str]] = None): - self._current_url = url - self._history = self._history[:self._current_index + 1] - self._history.append(url) - self._current_index = len(self._history) - 1 - self._can_go_back = self._current_index > 0 - self._can_go_forward = False - - def loadData(self, data: str, mime_type: str = "text/html", - encoding: str = "utf-8"): - self._current_url = "about:blank" - - def loadDataWithBaseURL(self, base_url: str, data: str, mime_type: str, - encoding: str, history_url: str): - self._current_url = base_url or "about:blank" - - def getSettings(self) -> "WebSettings": - return self._settings - - def setWebViewClient(self, client: Any): - self._web_view_client = client - - def getWebViewClient(self) -> Any: - return self._web_view_client - - def setWebChromeClient(self, client: Any): - self._web_chrome_client = client - - def getWebChromeClient(self) -> Any: - return self._web_chrome_client - - def goBack(self): - if self._can_go_back: - self._current_index -= 1 - self._current_url = self._history[self._current_index] - self._can_go_back = self._current_index > 0 - self._can_go_forward = True - - def goForward(self): - if self._can_go_forward: - self._current_index += 1 - self._current_url = self._history[self._current_index] - self._can_go_forward = self._current_index < len(self._history) - 1 - self._can_go_back = True - - def reload(self): - pass - - def canGoBack(self) -> bool: - return self._can_go_back - - def canGoForward(self) -> bool: - return self._can_go_forward - - def clearHistory(self): - self._history.clear() - self._current_index = -1 - self._can_go_back = False - self._can_go_forward = False - - def destroy(self): - pass - - def onPause(self): - pass - - def onResume(self): - pass - - def pauseTimers(self): - pass - - def resumeTimers(self): - pass - - def getUrl(self) -> Optional[str]: - return self._current_url if self._current_url else None - - def getTitle(self) -> Optional[str]: - return None - - def evaluateJavascript(self, script: str, callback: Optional[Callable] = None): - if callback: - callback("") - - def addJavascriptInterface(self, obj: Any, name: str): - pass - - def removeJavascriptInterface(self, name: str): - pass - - def canGoBackOrForward(self, steps: int) -> bool: - target = self._current_index + steps - return 0 <= target < len(self._history) - - def goBackOrForward(self, steps: int): - target = self._current_index + steps - if 0 <= target < len(self._history): - self._current_index = target - self._current_url = self._history[target] - self._can_go_back = target > 0 - self._can_go_forward = target < len(self._history) - 1 - - def setDownloadListener(self, listener: Optional[Callable]): - self._download_listener = listener - - def setFindListener(self, listener: Optional[Callable]): - self._find_listener = listener - - class WebViewTransport: - def setWebView(self, web_view: "WebView"): - pass - def getWebView(self) -> Optional["WebView"]: - return None - - class HitTestResult: - UNKNOWN_TYPE = 0 - ANCHOR_TYPE = 1 - EDIT_TEXT_TYPE = 2 - IMAGE_TYPE = 3 - SRC_ANCHOR_TYPE = 4 - SRC_IMAGE_ANCHOR_TYPE = 5 - PHONE_TYPE = 6 - GEO_TYPE = 7 - EMAIL_TYPE = 8 - - def __init__(self): - self._type = WebView.HitTestResult.UNKNOWN_TYPE - self._extra = "" - - def getType(self) -> int: - return self._type - def getExtra(self) -> str: - return self._extra - - def getHitTestResult(self) -> HitTestResult: - return WebView.HitTestResult() - - def findAllAsync(self, find_str: str): - pass - - def findNext(self, forward: bool): - pass - - def clearMatches(self): - pass - - def zoomIn(self) -> bool: - return False - - def zoomOut(self) -> bool: - return False - - def setInitialScale(self, scale_in_percent: int): - pass - - def getScale(self) -> float: - return 1.0 - - def setPictureListener(self, listener: Optional[Callable]): - self._picture_listener = listener - - -class WebSettings(Stub): - """WebSettings — WebView 设置""" - - # 渲染模式 - RENDER_GOES_BROWSER = 0 - RENDER_GOES_SURFACE_VIEW = 1 - RENDER_GOES_DISPLAY_LIST = 2 - RENDER_GOES_GPU = 3 - RENDER_GOES_SURFACE = 4 - - # 布局算法 - LAYOUT_ALGORITHM_NORMAL = 0 - LAYOUT_ALGORITHM_SINGLE_COLUMN = 1 - LAYOUT_ALGORITHM_NARROW_COLUMNS = 2 - LAYOUT_ALGORITHM_TEXT_AUTOSIZING = 3 - - def __init__(self): - super().__init__() - self._javascript_enabled = False - self._dom_storage_enabled = False - self._allow_file_access = True - self._built_in_zoom_controls = True - self._display_zoom_controls = True - self._load_with_overview_mode = False - self._support_zoom = True - self._use_wide_view_port = False - self._database_enabled = False - self._allow_content_access = True - self._cache_mode = -1 # LOAD_DEFAULT - self._user_agent_string: str = "Mozilla/5.0 (Linux; Android 13)" - self._default_font_size = 16 - self._minimum_font_size = 8 - self._text_zoom = 100 - self._standard_font_family = "sans-serif" - self._serif_font_family = "serif" - self._sans_serif_font_family = "sans-serif" - self._fixed_font_family = "monospace" - self._cursive_font_family = "cursive" - self._fantasy_font_family = "fantasy" - self._default_encoding = "utf-8" - self._layout_algorithm = WebSettings.LAYOUT_ALGORITHM_NORMAL - self._block_network_image = False - self._block_network_loads = False - self._mixed_content_mode = 0 # MIXED_CONTENT_ALWAYS_ALLOW - self._offscreen_pre_raster = False - self._safe_browsing_enabled = True - - def setJavaScriptEnabled(self, enabled: bool): - self._javascript_enabled = enabled - - def getJavaScriptEnabled(self) -> bool: - return self._javascript_enabled - - def setDomStorageEnabled(self, enabled: bool): - self._dom_storage_enabled = enabled - - def getDomStorageEnabled(self) -> bool: - return self._dom_storage_enabled - - def setAllowFileAccess(self, allow: bool): - self._allow_file_access = allow - - def getAllowFileAccess(self) -> bool: - return self._allow_file_access - - def setBuiltInZoomControls(self, enabled: bool): - self._built_in_zoom_controls = enabled - - def getBuiltInZoomControls(self) -> bool: - return self._built_in_zoom_controls - - def setDisplayZoomControls(self, enabled: bool): - self._display_zoom_controls = enabled - - def getDisplayZoomControls(self) -> bool: - return self._display_zoom_controls - - def setSupportZoom(self, support: bool): - self._support_zoom = support - - def supportZoom(self) -> bool: - return self._support_zoom - - def setLoadWithOverviewMode(self, overview: bool): - self._load_with_overview_mode = overview - - def getLoadWithOverviewMode(self) -> bool: - return self._load_with_overview_mode - - def setUseWideViewPort(self, use: bool): - self._use_wide_view_port = use - - def getUseWideViewPort(self) -> bool: - return self._use_wide_view_port - - def setDatabaseEnabled(self, enabled: bool): - self._database_enabled = enabled - - def getDatabaseEnabled(self) -> bool: - return self._database_enabled - - def setAllowContentAccess(self, allow: bool): - self._allow_content_access = allow - - def getAllowContentAccess(self) -> bool: - return self._allow_content_access - - def setCacheMode(self, mode: int): - self._cache_mode = mode - - def getCacheMode(self) -> int: - return self._cache_mode - - LOAD_DEFAULT = -1 - LOAD_CACHE_ELSE_NETWORK = 1 - LOAD_NO_CACHE = 2 - LOAD_CACHE_ONLY = 3 - - def setUserAgentString(self, ua: str): - self._user_agent_string = ua - - def getUserAgentString(self) -> str: - return self._user_agent_string - - def setDefaultFontSize(self, size: int): - self._default_font_size = size - - def getDefaultFontSize(self) -> int: - return self._default_font_size - - def setMinimumFontSize(self, size: int): - self._minimum_font_size = size - - def getMinimumFontSize(self) -> int: - return self._minimum_font_size - - def setTextZoom(self, text_zoom: int): - self._text_zoom = text_zoom - - def getTextZoom(self) -> int: - return self._text_zoom - - def setStandardFontFamily(self, font: str): - self._standard_font_family = font - - def setSerifFontFamily(self, font: str): - self._serif_font_family = font - - def setSansSerifFontFamily(self, font: str): - self._sans_serif_font_family = font - - def setFixedFontFamily(self, font: str): - self._fixed_font_family = font - - def setCursiveFontFamily(self, font: str): - self._cursive_font_family = font - - def setFantasyFontFamily(self, font: str): - self._fantasy_font_family = font - - def setDefaultTextEncodingName(self, encoding: str): - self._default_encoding = encoding - - def getDefaultTextEncodingName(self) -> str: - return self._default_encoding - - def setLayoutAlgorithm(self, algorithm: int): - self._layout_algorithm = algorithm - - def getLayoutAlgorithm(self) -> int: - return self._layout_algorithm - - def setBlockNetworkImage(self, block: bool): - self._block_network_image = block - - def getBlockNetworkImage(self) -> bool: - return self._block_network_image - - def setBlockNetworkLoads(self, block: bool): - self._block_network_loads = block - - def getBlockNetworkLoads(self) -> bool: - return self._block_network_loads - - def setMixedContentMode(self, mode: int): - self._mixed_content_mode = mode - - def getMixedContentMode(self) -> int: - return self._mixed_content_mode - - MIXED_CONTENT_ALWAYS_ALLOW = 0 - MIXED_CONTENT_NEVER_ALLOW = 1 - MIXED_CONTENT_COMPATIBILITY_MODE = 2 - - def setOffscreenPreRaster(self, enabled: bool): - self._offscreen_pre_raster = enabled - - def getOffscreenPreRaster(self) -> bool: - return self._offscreen_pre_raster - - def setSafeBrowsingEnabled(self, enabled: bool): - self._safe_browsing_enabled = enabled - - def getSafeBrowsingEnabled(self) -> bool: - return self._safe_browsing_enabled - - def setNeedInitialFocus(self, flag: bool): - pass - - -# ═══════════════════════════════════════════════════════════════ -# ViewAnimator / ViewFlipper / ViewSwitcher etc. -# ═══════════════════════════════════════════════════════════════ - -class ViewAnimator(FrameLayout): - """ViewAnimator — 视图动画容器""" - - def __init__(self, context=None): - super().__init__(context) - self._which_child: int = 0 - self._animate_first_view: bool = False - self._in_animation: Any = None - self._out_animation: Any = None - - def setDisplayedChild(self, which_child: int): - if 0 <= which_child < self.getChildCount(): - self._which_child = which_child - - def getDisplayedChild(self) -> int: - return self._which_child - - def showNext(self): - if self._which_child < self.getChildCount() - 1: - self._which_child += 1 - - def showPrevious(self): - if self._which_child > 0: - self._which_child -= 1 - - def setInAnimation(self, in_animation: Any): - self._in_animation = in_animation - - def getInAnimation(self) -> Any: - return self._in_animation - - def setOutAnimation(self, out_animation: Any): - self._out_animation = out_animation - - def getOutAnimation(self) -> Any: - return self._out_animation - - def setAnimateFirstView(self, animate: bool): - self._animate_first_view = animate - - def getAnimateFirstView(self) -> bool: - return self._animate_first_view - - def getBaseline(self) -> int: - return -1 - - -class ViewFlipper(ViewAnimator): - """ViewFlipper — 视图翻转器""" - - def __init__(self, context=None): - super().__init__(context) - self._flip_interval: int = 3000 - self._auto_start: bool = False - self._running: bool = False - self._flip_timer: Any = None - - def setFlipInterval(self, milliseconds: int): - self._flip_interval = milliseconds - - def getFlipInterval(self) -> int: - return self._flip_interval - - def setAutoStart(self, auto_start: bool): - self._auto_start = auto_start - - def isAutoStart(self) -> bool: - return self._auto_start - - def startFlipping(self): - self._running = True - - def stopFlipping(self): - self._running = False - - def isFlipping(self) -> bool: - return self._running - - def onDetachedFromWindow(self): - super().onDetachedFromWindow() - self._running = False - - def onAttachedToWindow(self): - super().onAttachedToWindow() - if self._auto_start: - self._running = True - - -class ViewSwitcher(ViewAnimator): - """ViewSwitcher — 视图切换器""" - - def __init__(self, context=None): - super().__init__(context) - self._factory: Optional[Callable] = None - - class ViewFactory: - def makeView(self) -> View: - return View() - - def setFactory(self, factory: ViewFactory): - self._factory = factory - - def getCurrentView(self) -> Optional[View]: - return self.getChildAt(self._which_child) - - def getNextView(self) -> Optional[View]: - next_index = self._which_child + 1 - if next_index < self.getChildCount(): - return self.getChildAt(next_index) - return None - - def setInAnimation(self, in_animation: Any): - super().setInAnimation(in_animation) - - def setOutAnimation(self, out_animation: Any): - super().setOutAnimation(out_animation) - - -class ImageSwitcher(ViewSwitcher): - """ImageSwitcher — 图片切换器""" - - def setImageDrawable(self, drawable: Any): - pass - - def setImageResource(self, resid: int): - pass - - def setImageURI(self, uri: Any): - pass - - def setImageBitmap(self, bitmap: Any): - pass - - -class TextSwitcher(ViewSwitcher): - """TextSwitcher — 文本切换器""" - - def setCurrentText(self, text: str): - if self.getChildCount() > 0: - child = self.getChildAt(0) - if hasattr(child, "setText"): - child.setText(text) - - def setText(self, text: str): - self.setCurrentText(text) - - -class AdapterViewAnimator(AdapterView): - """AdapterViewAnimator — 适配器视图动画器""" - - def __init__(self, context=None): - super().__init__(context) - self._in_animation: Any = None - self._out_animation: Any = None - self._displayed_child: int = 0 - self._loop_views: bool = False - - def setDisplayedChild(self, which_child: int): - self._displayed_child = which_child - - def getDisplayedChild(self) -> int: - return self._displayed_child - - def showNext(self): - self._displayed_child = (self._displayed_child + 1) % max(1, self.getCount()) - - def showPrevious(self): - last = max(1, self.getCount()) - self._displayed_child = (self._displayed_child - 1) % last - - def setInAnimation(self, in_animation: Any): - self._in_animation = in_animation - - def getInAnimation(self) -> Any: - return self._in_animation - - def setOutAnimation(self, out_animation: Any): - self._out_animation = out_animation - - def getOutAnimation(self) -> Any: - return self._out_animation - - def getCurrentView(self) -> Optional[View]: - return self.getChildAt(self._displayed_child) - - def getBaseline(self) -> int: - return -1 - - def getCount(self) -> int: - if self._adapter: - return self._adapter.getCount() - return 0 - - -class AdapterViewFlipper(AdapterViewAnimator): - """AdapterViewFlipper — 适配器视图翻转器""" - - def __init__(self, context=None): - super().__init__(context) - self._flip_interval: int = 10000 - self._auto_start: bool = False - self._running: bool = False - - def setFlipInterval(self, milliseconds: int): - self._flip_interval = milliseconds - - def getFlipInterval(self) -> int: - return self._flip_interval - - def startFlipping(self): - self._running = True - - def stopFlipping(self): - self._running = False - - def isFlipping(self) -> bool: - return self._running - - def setAutoStart(self, auto_start: bool): - self._auto_start = auto_start - - def isAutoStart(self) -> bool: - return self._auto_start - - -class StackView(AdapterViewAnimator): - """StackView — 栈视图""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# Adapters -# ═══════════════════════════════════════════════════════════════ - -class BaseAdapter(Stub): - """BaseAdapter — 基础适配器""" - - def __init__(self): - super().__init__() - self._data_set_observable: Any = None - - def getCount(self) -> int: - return 0 - - def getItem(self, position: int) -> Any: - return None - - def getItemId(self, position: int) -> int: - return position - - def getView(self, position: int, convert_view: Optional[View], parent: Optional[ViewGroup]) -> View: - return View() - - def getItemViewType(self, position: int) -> int: - return 0 - - def getViewTypeCount(self) -> int: - return 1 - - def hasStableIds(self) -> bool: - return False - - def isEmpty(self) -> bool: - return self.getCount() == 0 - - def areAllItemsEnabled(self) -> bool: - return True - - def isEnabled(self, position: int) -> bool: - return True - - def getDropDownView(self, position: int, convert_view: Optional[View], - parent: Optional[ViewGroup]) -> View: - return self.getView(position, convert_view, parent) - - def notifyDataSetChanged(self): - for observer in getattr(self, '_observers', []): - observer.onChanged() - - def notifyDataSetInvalidated(self): - for observer in getattr(self, '_observers', []): - observer.onInvalidated() - - def registerDataSetObserver(self, observer): - if not hasattr(self, '_observers'): - self._observers = [] - self._observers.append(observer) - - def unregisterDataSetObserver(self, observer): - if hasattr(self, '_observers'): - try: - self._observers.remove(observer) - except ValueError: - pass - - -class ArrayAdapter(BaseAdapter): - """ArrayAdapter — 数组适配器""" - - def __init__(self, context: Any, resource: int, objects: Optional[List[Any]] = None): - super().__init__() - self._context = context - self._resource = resource - self._drop_down_resource = resource - self._objects: List[Any] = list(objects) if objects is not None else [] - self._field_id = 0 - self._notify_on_change = True - - def getCount(self) -> int: - return len(self._objects) - - def getItem(self, position: int) -> Any: - if 0 <= position < len(self._objects): - return self._objects[position] - return None - - def getPosition(self, item: Any) -> int: - try: - return self._objects.index(item) - except ValueError: - return -1 - - def getView(self, position: int, convert_view: Optional[View], - parent: Optional[ViewGroup]) -> View: - if convert_view is not None: - tv = convert_view - else: - tv = TextView(self._context) - if hasattr(tv, "setText"): - tv.setText(str(self.getItem(position) if self.getItem(position) is not None else "")) - return tv - - def add(self, obj: Any): - self._objects.append(obj) - if self._notify_on_change: - self.notifyDataSetChanged() - - def addAll(self, *items: Any): - for item in items: - self._objects.append(item) - if self._notify_on_change: - self.notifyDataSetChanged() - - def insert(self, obj: Any, index: int): - self._objects.insert(index, obj) - if self._notify_on_change: - self.notifyDataSetChanged() - - def remove(self, obj: Any): - if obj in self._objects: - self._objects.remove(obj) - if self._notify_on_change: - self.notifyDataSetChanged() - - def clear(self): - self._objects.clear() - if self._notify_on_change: - self.notifyDataSetChanged() - - def sort(self, comparator: Callable): - self._objects.sort(key=comparator) - if self._notify_on_change: - self.notifyDataSetChanged() - - def setNotifyOnChange(self, notify_on_change: bool): - self._notify_on_change = notify_on_change - - def getContext(self): - return self._context - - def getCount(self) -> int: - return len(self._objects) - - def setDropDownViewResource(self, resource: int): - self._drop_down_resource = resource - - def getDropDownViewResource(self) -> int: - return self._drop_down_resource - - -class SimpleAdapter(BaseAdapter): - """SimpleAdapter — 简单适配器""" - - def __init__(self, context: Any, data: List[Dict[str, Any]], resource: int, - from_cols: List[str], to_ids: List[int]): - super().__init__() - self._context = context - self._data = data - self._resource = resource - self._from = from_cols - self._to = to_ids - self._drop_down_resource = resource - - def getCount(self) -> int: - return len(self._data) - - def getItem(self, position: int) -> Any: - if 0 <= position < len(self._data): - return self._data[position] - return None - - def getView(self, position: int, convert_view: Optional[View], - parent: Optional[ViewGroup]) -> View: - if convert_view is not None: - v = convert_view - else: - v = View(self._context) - return v - - def setDropDownViewResource(self, resource: int): - self._drop_down_resource = resource - - -class CursorAdapter(BaseAdapter): - """CursorAdapter — 游标适配器""" - - FLAG_AUTO_REQUERY = 0x01 - FLAG_REGISTER_CONTENT_OBSERVER = 0x02 - - def __init__(self, context: Any, c: Any, flags: int = FLAG_AUTO_REQUERY): - super().__init__() - self._context = context - self._cursor = c - self._flags = flags - - def getCount(self) -> int: - return 0 - - def getItem(self, position: int) -> Any: - return None - - def getView(self, position: int, convert_view: Optional[View], - parent: Optional[ViewGroup]) -> View: - return View(self._context) - - def bindView(self, view: View, context: Any, cursor: Any): - pass - - def newView(self, context: Any, cursor: Any, parent: ViewGroup) -> View: - return View(context) - - def getCursor(self) -> Any: - return self._cursor - - def changeCursor(self, cursor: Any): - self._cursor = cursor - self.notifyDataSetChanged() - - def swapCursor(self, new_cursor: Any) -> Any: - old = self._cursor - self._cursor = new_cursor - self.notifyDataSetChanged() - return old - - -# ═══════════════════════════════════════════════════════════════ -# Spinner -# ═══════════════════════════════════════════════════════════════ - -class Spinner(AdapterView): - """Spinner — 下拉选择器""" - - MODE_DIALOG = 0 - MODE_DROPDOWN = 1 - - def __init__(self, context=None, mode: int = MODE_DROPDOWN): - super().__init__(context) - self._mode = mode - self._prompt: str = "" - self._popup: Any = None - self._on_item_selected_listener: Optional[Callable] = None - - class OnItemSelectedListener: - def onItemSelected(self, parent: AdapterView, view: View, position: int, id_: int): - pass - def onNothingSelected(self, parent: AdapterView): - pass - - def setOnItemSelectedListener(self, listener: Optional[OnItemSelectedListener]): - self._on_item_selected_listener = listener - - def getOnItemSelectedListener(self) -> Optional[OnItemSelectedListener]: - return self._on_item_selected_listener - - def setSelection(self, position: int, animate: bool = True): - super().setSelection(position) - - def getSelectedItem(self) -> Any: - return super().getSelectedItem() - - def getSelectedItemPosition(self) -> int: - return self._selected_position - - def getSelectedView(self) -> Optional[View]: - return None - - def setPrompt(self, prompt: str): - self._prompt = prompt - - def getPrompt(self) -> str: - return self._prompt - - def setPopupBackgroundDrawable(self, drawable: Any): - pass - - def getPopupBackgroundDrawable(self) -> Any: - return None - - def performClick(self) -> bool: - return True - - -# ═══════════════════════════════════════════════════════════════ -# AutoCompleteTextView -# ═══════════════════════════════════════════════════════════════ - -class AutoCompleteTextView(EditText): - """AutoCompleteTextView — 自动完成文本""" - - def __init__(self, context=None): - super().__init__(context) - self._threshold: int = 2 - self._completion_hint: str = "" - self._drop_down_anchor: Optional[View] = None - self._drop_down_horizontal_offset: int = 0 - self._drop_down_vertical_offset: int = 0 - self._drop_down_width: int = 0 - self._drop_down_height: int = 0 - self._on_item_click_listener: Optional[Callable] = None - self._on_dismiss_listener: Optional[Callable] = None - self._validator: Optional[Callable] = None - - def setThreshold(self, threshold: int): - self._threshold = max(1, threshold) - - def getThreshold(self) -> int: - return self._threshold - - def setAdapter(self, adapter: Any): - self._adapter = adapter - - def getAdapter(self) -> Any: - return self._adapter - - def setOnItemClickListener(self, listener: Optional[Callable]): - self._on_item_click_listener = listener - - def getOnItemClickListener(self) -> Optional[Callable]: - return self._on_item_click_listener - - def setCompletionHint(self, hint: str): - self._completion_hint = hint - - def getCompletionHint(self) -> str: - return self._completion_hint - - def setDropDownAnchor(self, anchor_id: int): - pass - - def setDropDownHorizontalOffset(self, offset: int): - self._drop_down_horizontal_offset = offset - - def getDropDownHorizontalOffset(self) -> int: - return self._drop_down_horizontal_offset - - def setDropDownVerticalOffset(self, offset: int): - self._drop_down_vertical_offset = offset - - def getDropDownVerticalOffset(self) -> int: - return self._drop_down_vertical_offset - - def setDropDownWidth(self, width: int): - self._drop_down_width = width - - def getDropDownWidth(self) -> int: - return self._drop_down_width - - def showDropDown(self): - pass - - def dismissDropDown(self): - pass - - def isDropDownShowing(self) -> bool: - return False - - def enoughToFilter(self) -> bool: - return len(self._text) >= self._threshold - - def setValidator(self, validator: Optional[Callable]): - self._validator = validator - - def getValidator(self) -> Optional[Callable]: - return self._validator - - def performValidation(self): - pass - - def setOnDismissListener(self, listener: Optional[Callable]): - self._on_dismiss_listener = listener - - def performFiltering(self, text: str, key_code: int): - pass - - def onFilterComplete(self, count: int): - pass - - -class MultiAutoCompleteTextView(AutoCompleteTextView): - """MultiAutoCompleteTextView — 多值自动完成文本""" - - def __init__(self, context=None): - super().__init__(context) - self._tokenizer: Optional[Callable] = None - - class CommaTokenizer: - def tokenize(self, text: str) -> List[str]: - return text.split(",") - def findTokenStart(self, text: str, cursor: int) -> int: - return text.rfind(",", 0, cursor) + 1 - def findTokenEnd(self, text: str, cursor: int) -> int: - next_comma = text.find(",", cursor) - return next_comma if next_comma >= 0 else len(text) - def terminateToken(self, text: str) -> str: - return text + "," - - def setTokenizer(self, tokenizer: Any): - self._tokenizer = tokenizer - - def getTokenizer(self) -> Any: - return self._tokenizer - - -# ═══════════════════════════════════════════════════════════════ -# DatePicker / TimePicker / CalendarView / NumberPicker -# ═══════════════════════════════════════════════════════════════ - -class DatePicker(FrameLayout): - """DatePicker — 日期选择器""" - - def __init__(self, context=None): - super().__init__(context) - self._year: int = 2024 - self._month: int = 0 # 0-based - self._day_of_month: int = 1 - self._min_date: int = 0 - self._max_date: int = 0 - self._spinners_shown: bool = True - self._calendar_view_shown: bool = False - self._on_date_changed_listener: Optional[Callable] = None - self._calendar_view: Optional[CalendarView] = None - - class OnDateChangedListener: - def onDateChanged(self, view: "DatePicker", year: int, month_of_year: int, day_of_month: int): - pass - - def init(self, year: int, month: int, day_of_month: int, - listener: Optional[OnDateChangedListener] = None): - self._year = year - self._month = month - self._day_of_month = day_of_month - self._on_date_changed_listener = listener - if listener: - listener.onDateChanged(self, year, month, day_of_month) - - def getYear(self) -> int: - return self._year - - def getMonth(self) -> int: - return self._month - - def getDayOfMonth(self) -> int: - return self._day_of_month - - def setMinDate(self, min_date: int): - self._min_date = min_date - - def getMinDate(self) -> int: - return self._min_date - - def setMaxDate(self, max_date: int): - self._max_date = max_date - - def getMaxDate(self) -> int: - return self._max_date - - def setSpinnersShown(self, shown: bool): - self._spinners_shown = shown - - def setCalendarViewShown(self, shown: bool): - self._calendar_view_shown = shown - - def getCalendarView(self) -> Optional["CalendarView"]: - return self._calendar_view - - def setOnDateChangedListener(self, listener: Optional[OnDateChangedListener]): - self._on_date_changed_listener = listener - - def updateDate(self, year: int, month: int, day_of_month: int): - self._year = year - self._month = month - self._day_of_month = day_of_month - if self._on_date_changed_listener: - self._on_date_changed_listener.onDateChanged(self, year, month, day_of_month) - - -class TimePicker(FrameLayout): - """TimePicker — 时间选择器""" - - MODE_CLOCK = 0 - MODE_SPINNER = 1 - - def __init__(self, context=None, mode: int = MODE_CLOCK): - super().__init__(context) - self._current_hour: int = 12 - self._current_minute: int = 0 - self._is24_hour_view: Optional[bool] = None # None = system default - self._mode = mode - self._on_time_changed_listener: Optional[Callable] = None - - class OnTimeChangedListener: - def onTimeChanged(self, view: "TimePicker", hour_of_day: int, minute: int): - pass - - def setOnTimeChangedListener(self, listener: Optional[OnTimeChangedListener]): - self._on_time_changed_listener = listener - - def getOnTimeChangedListener(self) -> Optional[OnTimeChangedListener]: - return self._on_time_changed_listener - - def getCurrentHour(self) -> int: - return self._current_hour - - def setCurrentHour(self, current_hour: int): - self._current_hour = current_hour % 24 - if self._on_time_changed_listener: - self._on_time_changed_listener.onTimeChanged(self, self._current_hour, self._current_minute) - - def getCurrentMinute(self) -> int: - return self._current_minute - - def setCurrentMinute(self, current_minute: int): - self._current_minute = current_minute % 60 - if self._on_time_changed_listener: - self._on_time_changed_listener.onTimeChanged(self, self._current_hour, self._current_minute) - - def setIs24HourView(self, is24_hour_view: Optional[bool]): - self._is24_hour_view = is24_hour_view - - def is24HourView(self) -> bool: - if self._is24_hour_view is not None: - return self._is24_hour_view - return False - - def getMode(self) -> int: - return self._mode - - def setHour(self, hour: int): - self.setCurrentHour(hour) - - def getHour(self) -> int: - return self.getCurrentHour() - - def setMinute(self, minute: int): - self.setCurrentMinute(minute) - - def getMinute(self) -> int: - return self.getCurrentMinute() - - -class CalendarView(FrameLayout): - """CalendarView — 日历视图""" - - def __init__(self, context=None): - super().__init__(context) - self._date: int = 0 - self._min_date: int = 0 - self._max_date: int = 0 - self._first_day_of_week: int = 1 # Sunday=1 - self._week_separator: bool = False - self._on_date_change_listener: Optional[Callable] = None - self._shown_week_count: int = 6 - - class OnDateChangeListener: - def onSelectedDayChange(self, view: "CalendarView", year: int, month: int, day_of_month: int): - pass - - def setOnDateChangeListener(self, listener: Optional[OnDateChangeListener]): - self._on_date_change_listener = listener - - def getDate(self) -> int: - return self._date - - def setDate(self, date: int, animate: bool = True, center: bool = True): - self._date = date - - def getMinDate(self) -> int: - return self._min_date - - def setMinDate(self, min_date: int): - self._min_date = min_date - - def getMaxDate(self) -> int: - return self._max_date - - def setMaxDate(self, max_date: int): - self._max_date = max_date - - def getFirstDayOfWeek(self) -> int: - return self._first_day_of_week - - def setFirstDayOfWeek(self, first_day_of_week: int): - self._first_day_of_week = first_day_of_week - - def setWeekSeparatorDrawable(self, drawable: Any): - self._week_separator = True - - def getShownWeekCount(self) -> int: - return self._shown_week_count - - def setShownWeekCount(self, count: int): - self._shown_week_count = count - - -class NumberPicker(ViewGroup): - """NumberPicker — 数字选择器""" - - def __init__(self, context=None): - super().__init__(context) - self._min_value: int = 0 - self._max_value: int = 100 - self._value: int = 0 - self._displayed_values: Optional[List[str]] = None - self._on_value_change_listener: Optional[Callable] = None - self._on_scroll_listener: Optional[Callable] = None - self._wrap_selector_wheel: bool = True - self._selection_divider: bool = True - - class OnValueChangeListener: - def onValueChange(self, picker: "NumberPicker", old_val: int, new_val: int): - pass - - class OnScrollListener: - def onScrollStateChange(self, picker: "NumberPicker", state: int): - pass - - SCROLL_STATE_IDLE = 0 - SCROLL_STATE_TOUCH_SCROLL = 1 - SCROLL_STATE_FLING = 2 - - def setMinValue(self, min_value: int): - self._min_value = min_value - if self._value < self._min_value: - self._value = self._min_value - - def getMinValue(self) -> int: - return self._min_value - - def setMaxValue(self, max_value: int): - self._max_value = max_value - if self._value > self._max_value: - self._value = self._max_value - - def getMaxValue(self) -> int: - return self._max_value - - def setValue(self, value: int): - old_value = self._value - self._value = max(self._min_value, min(self._max_value, value)) - if self._on_value_change_listener and old_value != self._value: - self._on_value_change_listener.onValueChange(self, old_value, self._value) - - def getValue(self) -> int: - return self._value - - def setDisplayedValues(self, displayed_values: Optional[List[str]]): - self._displayed_values = displayed_values - - def getDisplayedValues(self) -> Optional[List[str]]: - return self._displayed_values - - def setOnValueChangedListener(self, listener: Optional[OnValueChangeListener]): - self._on_value_change_listener = listener - - def setOnScrollListener(self, listener: Optional[OnScrollListener]): - self._on_scroll_listener = listener - - def setWrapSelectorWheel(self, wrap: bool): - self._wrap_selector_wheel = wrap - - def getWrapSelectorWheel(self) -> bool: - return self._wrap_selector_wheel - - def setSelectionDivider(self, divider: Any): - self._selection_divider = True - - -# ═══════════════════════════════════════════════════════════════ -# VideoView / MediaController -# ═══════════════════════════════════════════════════════════════ - -class VideoView(SurfaceView): - """VideoView — 视频播放视图""" - - def __init__(self, context=None): - super().__init__(context) - self._video_path: Optional[str] = None - self._uri: Any = None - self._media_controller: Optional["MediaController"] = None - self._on_prepared_listener: Optional[Callable] = None - self._on_completion_listener: Optional[Callable] = None - self._on_error_listener: Optional[Callable] = None - self._is_playing: bool = False - self._current_position: int = 0 - self._duration: int = 0 - self._can_seek_back: bool = True - self._can_seek_forward: bool = True - self._can_pause: bool = True - self._video_width: int = 0 - self._video_height: int = 0 - - def setVideoPath(self, path: str): - self._video_path = path - - def setVideoURI(self, uri: Any): - self._uri = uri - - def setMediaController(self, controller: "MediaController"): - self._media_controller = controller - - def setOnPreparedListener(self, listener: Optional[Callable]): - self._on_prepared_listener = listener - - def setOnCompletionListener(self, listener: Optional[Callable]): - self._on_completion_listener = listener - - def setOnErrorListener(self, listener: Optional[Callable]): - self._on_error_listener = listener - - def start(self): - self._is_playing = True - - def stopPlayback(self): - self._is_playing = False - self._current_position = 0 - - def pause(self): - self._is_playing = False - - def resume(self): - self._is_playing = True - - def seekTo(self, msec: int): - self._current_position = max(0, min(msec, self._duration)) - - def getCurrentPosition(self) -> int: - return self._current_position - - def getDuration(self) -> int: - return self._duration - - def isPlaying(self) -> bool: - return self._is_playing - - def canSeekForward(self) -> bool: - return self._can_seek_forward - - def canSeekBackward(self) -> bool: - return self._can_seek_back - - def canPause(self) -> bool: - return self._can_pause - - def getBufferPercentage(self) -> int: - return 100 - - def resolveAdjustedSize(self, desired_size: int, measure_spec: int) -> int: - from .view import MeasureSpec - return MeasureSpec.getSize(measure_spec) - - def setVideoWidth(self, video_width: int): - self._video_width = video_width - - def setVideoHeight(self, video_height: int): - self._video_height = video_height - - @property - def videoWidth(self) -> int: - return self._video_width - - @property - def videoHeight(self) -> int: - return self._video_height - - -class MediaController(FrameLayout): - """MediaController — 媒体控制器""" - - def __init__(self, context=None, target: Optional[VideoView] = None): - super().__init__(context) - self._target = target - self._anchor_view: Optional[View] = None - self._is_showing: bool = False - self._set_up_listener: Optional[Callable] = None - - def setMediaPlayer(self, player: "MediaController.MediaPlayerControl"): - pass - - def setAnchorView(self, view: View): - self._anchor_view = view - - def show(self, timeout: int = 3000): - self._is_showing = True - - def hide(self): - self._is_showing = False - - def isShowing(self) -> bool: - return self._is_showing - - def setEnabled(self, enabled: bool): - super().setEnabled(enabled) - - def setOnPrepareListener(self, listener: Optional[Callable]): - self._set_up_listener = listener - - class MediaPlayerControl: - def start(self): - pass - def pause(self): - pass - def getDuration(self) -> int: - return 0 - def getCurrentPosition(self) -> int: - return 0 - def seekTo(self, pos: int): - pass - def isPlaying(self) -> bool: - return False - def getBufferPercentage(self) -> int: - return 0 - def canPause(self) -> bool: - return False - def canSeekBackward(self) -> bool: - return False - def canSeekForward(self) -> bool: - return False - def getAudioSessionId(self) -> int: - return 0 - - -# ═══════════════════════════════════════════════════════════════ -# Toast -# ═══════════════════════════════════════════════════════════════ - -class Toast(Stub): - """Toast — 提示消息""" - - LENGTH_SHORT = 0 - LENGTH_LONG = 1 - - def __init__(self, context=None): - super().__init__() - if context is None: - context = android_context - self._context = context - self._duration = Toast.LENGTH_SHORT - self._gravity = Gravity.NO_GRAVITY - self._x_offset: int = 0 - self._y_offset: int = 0 - self._view: Optional[View] = None - self._text: str = "" - self._horizontal_margin: float = 0.0 - self._vertical_margin: float = 0.0 - self._next_view: Optional[View] = None - - @staticmethod - def makeText(context: Any, text: str, duration: int) -> "Toast": - toast = Toast(context) - toast._text = text - toast._duration = duration - return toast - - def show(self): - if _USE_LINUX: - LinuxNotify.send("Toast", self._text) - else: - print(f"[Toast] {self._text}") - - def cancel(self): - if _USE_LINUX: - # 桌面通知自动消失,无需操作 - return - pass - - def setGravity(self, gravity: int, x_offset: int, y_offset: int): - self._gravity = gravity - self._x_offset = x_offset - self._y_offset = y_offset - - def setDuration(self, duration: int): - self._duration = duration - - def getDuration(self) -> int: - return self._duration - - def setView(self, view: View): - self._view = view - - def getView(self) -> Optional[View]: - return self._view - - def setMargin(self, horizontal_margin: float, vertical_margin: float): - self._horizontal_margin = horizontal_margin - self._vertical_margin = vertical_margin - - def getHorizontalMargin(self) -> float: - return self._horizontal_margin - - def getVerticalMargin(self) -> float: - return self._vertical_margin - - def setText(self, text: str): - self._text = text - - def setNextAnnouncementView(self, view: View): - self._next_view = view - - def getNextAnnouncementView(self) -> Optional[View]: - return self._next_view - - def isTouchable(self) -> bool: - return False - - -# ═══════════════════════════════════════════════════════════════ -# PopupMenu / PopupWindow -# ═══════════════════════════════════════════════════════════════ - -class PopupMenu(Stub): - """PopupMenu — 弹出菜单""" - - def __init__(self, context: Any, anchor: View, gravity: int = Gravity.NO_GRAVITY): - super().__init__() - self._context = context - self._anchor = anchor - self._gravity = gravity - self._menu: "PopupMenu.Menu" = PopupMenu.Menu() - self._on_menu_item_click_listener: Optional[Callable] = None - self._on_dismiss_listener: Optional[Callable] = None - - class Menu(Stub): - """PopupMenu.Menu — 弹出菜单项列表""" - - def __init__(self): - super().__init__() - self._items: List[Dict] = [] - - def add(self, title: str) -> Any: - item_id = len(self._items) - self._items.append({"id": item_id, "title": title, "enabled": True}) - return item_id - - def findItem(self, id_: int) -> Optional[Dict]: - for item in self._items: - if item["id"] == id_: - return item - return None - - def removeItem(self, id_: int): - for i, item in enumerate(self._items): - if item["id"] == id_: - self._items.pop(i) - break - - def size(self) -> int: - return len(self._items) - - def getItem(self, index: int) -> Optional[Dict]: - if 0 <= index < len(self._items): - return self._items[index] - return None - - def clear(self): - self._items.clear() - - def setOnMenuItemClickListener(self, listener: Optional[Callable]): - pass - - def getMenu(self) -> "PopupMenu.Menu": - return self._menu - - def inflate(self, menu_res_id: int): - pass - - def show(self): - pass - - def dismiss(self): - pass - - def setOnMenuItemClickListener(self, listener: Optional[Callable]): - self._on_menu_item_click_listener = listener - - def setOnDismissListener(self, listener: Optional[Callable]): - self._on_dismiss_listener = listener - - def getDragToOpenListener(self) -> Optional[Callable]: - return None - - def setForceShowIcon(self, force_show: bool): - pass - - -class PopupWindow(Stub): - """PopupWindow — 弹出窗口""" - - def __init__(self, content_view: Optional[View] = None, - width: int = ViewGroup.LayoutParams.WRAP_CONTENT, - height: int = ViewGroup.LayoutParams.WRAP_CONTENT, - focusable: bool = True): - super().__init__() - self._content_view = content_view - self._width = width - self._height = height - self._focusable = focusable - self._is_showing: bool = False - self._outside_touchable: bool = False - self._touchable: bool = True - self._background_drawable: Any = None - self._animation_style: int = -1 - self._elevation: float = 0.0 - self._input_method_mode: int = -1 - self._soft_input_mode: int = -1 - self._on_dismiss_listener: Optional[Callable] = None - self._window_layout_type: int = 1000 # TYPE_APPLICATION_PANEL - self._overlap_anchor: bool = True - self._clip_to_screen: bool = True - self._split_enabled: bool = False - self._width_mode: int = 0 - self._height_mode: int = 0 - - def showAsDropDown(self, anchor: View, x_off: int = 0, y_off: int = 0, - gravity: int = Gravity.NO_GRAVITY): - self._is_showing = True - - def showAtLocation(self, parent: View, gravity: int, x: int, y: int): - self._is_showing = True - - def dismiss(self): - self._is_showing = False - if self._on_dismiss_listener: - self._on_dismiss_listener() - - def isShowing(self) -> bool: - return self._is_showing - - def setContentView(self, content_view: View): - self._content_view = content_view - - def getContentView(self) -> Optional[View]: - return self._content_view - - def setWidth(self, width: int): - self._width = width - - def getWidth(self) -> int: - return self._width - - def setHeight(self, height: int): - self._height = height - - def getHeight(self) -> int: - return self._height - - def setFocusable(self, focusable: bool): - self._focusable = focusable - - def isFocusable(self) -> bool: - return self._focusable - - def setOutsideTouchable(self, touchable: bool): - self._outside_touchable = touchable - - def isOutsideTouchable(self) -> bool: - return self._outside_touchable - - def setTouchable(self, touchable: bool): - self._touchable = touchable - - def isTouchable(self) -> bool: - return self._touchable - - def setBackgroundDrawable(self, background: Any): - self._background_drawable = background - - def getBackground(self) -> Any: - return self._background_drawable - - def setAnimationStyle(self, anim_style: int): - self._animation_style = anim_style - - def getAnimationStyle(self) -> int: - return self._animation_style - - def setElevation(self, elevation: float): - self._elevation = elevation - - def getElevation(self) -> float: - return self._elevation - - def setOnDismissListener(self, listener: Optional[Callable]): - self._on_dismiss_listener = listener - - def setTouchInterceptor(self, interceptor: Optional[Callable]): - pass - - def setClippingEnabled(self, enabled: bool): - self._clip_to_screen = enabled - - def isClippingEnabled(self) -> bool: - return self._clip_to_screen - - def setSplitTouchEnabled(self, enabled: bool): - self._split_enabled = enabled - - def isSplitTouchEnabled(self) -> bool: - return self._split_enabled - - def setWindowLayoutType(self, layout_type: int): - self._window_layout_type = layout_type - - def getWindowLayoutType(self) -> int: - return self._window_layout_type - - def setSoftInputMode(self, mode: int): - self._soft_input_mode = mode - - def getSoftInputMode(self) -> int: - return self._soft_input_mode - - def setInputMethodMode(self, mode: int): - self._input_method_mode = mode - - def getInputMethodMode(self) -> int: - return self._input_method_mode - - INPUT_METHOD_FROM_FOCUSABLE = 0 - INPUT_METHOD_NEEDED = 1 - INPUT_METHOD_NOT_NEEDED = 2 - - -# ═══════════════════════════════════════════════════════════════ -# Toolbar -# ═══════════════════════════════════════════════════════════════ - -class Toolbar(ViewGroup): - """Toolbar — 工具栏""" - - def __init__(self, context=None): - super().__init__(context) - self._title: str = "" - self._subtitle: str = "" - self._navigation_icon: Any = None - self._navigation_content_description: str = "" - self._logo: Any = None - self._logo_description: str = "" - self._title_text_color: int = 0 - self._subtitle_text_color: int = 0 - self._collapse_icon: Any = None - self._menu: Any = None - self._on_menu_item_click_listener: Optional[Callable] = None - self._navigation_on_click_listener: Optional[Callable] = None - self._popup_theme: int = 0 - self._content_inset_start: int = 16 - self._content_inset_end: int = 16 - self._title_margin_start: int = 0 - - def setTitle(self, title: str): - self._title = title - - def getTitle(self) -> str: - return self._title - - def setSubtitle(self, subtitle: str): - self._subtitle = subtitle - - def getSubtitle(self) -> str: - return self._subtitle - - def setNavigationIcon(self, icon: Any): - self._navigation_icon = icon - - def getNavigationIcon(self) -> Any: - return self._navigation_icon - - def setNavigationContentDescription(self, description: str): - self._navigation_content_description = description - - def getNavigationContentDescription(self) -> str: - return self._navigation_content_description - - def setLogo(self, logo: Any): - self._logo = logo - - def getLogo(self) -> Any: - return self._logo - - def setLogoDescription(self, description: str): - self._logo_description = description - - def getLogoDescription(self) -> str: - return self._logo_description - - def setTitleTextColor(self, color: int): - self._title_text_color = color - - def setSubtitleTextColor(self, color: int): - self._subtitle_text_color = color - - def inflateMenu(self, menu_res_id: int): - pass - - def getMenu(self) -> Any: - return self._menu - - def setOnMenuItemClickListener(self, listener: Optional[Callable]): - self._on_menu_item_click_listener = listener - - def setNavigationOnClickListener(self, listener: Optional[Callable]): - self._navigation_on_click_listener = listener - - def setCollapseIcon(self, icon: Any): - self._collapse_icon = icon - - def getCollapseIcon(self) -> Any: - return self._collapse_icon - - def setPopupTheme(self, res_id: int): - self._popup_theme = res_id - - def getPopupTheme(self) -> int: - return self._popup_theme - - def setContentInsetsRelative(self, content_inset_start: int, content_inset_end: int): - self._content_inset_start = content_inset_start - self._content_inset_end = content_inset_end - - def getContentInsetStart(self) -> int: - return self._content_inset_start - - def getContentInsetEnd(self) -> int: - return self._content_inset_end - - def collapseActionView(self): - pass - - def dismissPopupMenus(self): - pass - - def isOverflowMenuShowing(self) -> bool: - return False - - def showOverflowMenu(self) -> bool: - return False - - def hideOverflowMenu(self) -> bool: - return False - - def getOverflowIcon(self) -> Any: - return None - - def setOverflowIcon(self, icon: Any): - pass - - -class ActionMenuView(LinearLayout): - """ActionMenuView — 操作菜单视图""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# TabHost / TabWidget -# ═══════════════════════════════════════════════════════════════ - -class TabHost(FrameLayout): - """TabHost — 标签主机""" - - def __init__(self, context=None): - super().__init__(context) - self._tab_widget: Optional["TabWidget"] = None - self._tab_content: Optional[FrameLayout] = None - self._tabs: List["TabHost.TabSpec"] = [] - self._current_tab: int = 0 - self._on_tab_changed_listener: Optional[Callable] = None - self._local_activity_manager: Any = None - - class TabSpec(Stub): - """TabHost.TabSpec — 标签规格""" - - def __init__(self, tag: str): - super().__init__() - self._tag = tag - self._indicator: Any = None - self._content: Any = None - self._content_view: Optional[View] = None - - def setIndicator(self, indicator: Any) -> "TabHost.TabSpec": - self._indicator = indicator - return self - - def setContent(self, content: Any) -> "TabHost.TabSpec": - self._content = content - return self - - def setContent(self, view_id: int) -> "TabHost.TabSpec": - return self - - def setContent(self, view: View) -> "TabHost.TabSpec": - self._content_view = view - return self - - def getTag(self) -> str: - return self._tag - - def getIndicator(self) -> Any: - return self._indicator - - class OnTabChangeListener: - def onTabChanged(self, tab_id: str): - pass - - def setup(self): - pass - - def newTabSpec(self, tag: str) -> "TabHost.TabSpec": - spec = TabHost.TabSpec(tag) - self._tabs.append(spec) - return spec - - def addTab(self, tab_spec: "TabHost.TabSpec"): - if tab_spec not in self._tabs: - self._tabs.append(tab_spec) - - def setCurrentTab(self, index: int): - if 0 <= index < len(self._tabs): - self._current_tab = index - - def setCurrentTabByTag(self, tag: str): - for i, tab in enumerate(self._tabs): - if tab.getTag() == tag: - self._current_tab = i - break - - def getCurrentTab(self) -> int: - return self._current_tab - - def getCurrentTabTag(self) -> str: - if 0 <= self._current_tab < len(self._tabs): - return self._tabs[self._current_tab].getTag() - return "" - - def getTabWidget(self) -> "TabWidget": - return self._tab_widget - - def getTabContentView(self) -> FrameLayout: - return self._tab_content - - def setOnTabChangedListener(self, listener: Optional[OnTabChangeListener]): - self._on_tab_changed_listener = listener - - def clearAllTabs(self): - self._tabs.clear() - self._current_tab = 0 - - -class TabWidget(LinearLayout): - """TabWidget — 标签部件""" - - def __init__(self, context=None): - super().__init__(context) - self._strip: Any = None - self._left_strip: Any = None - self._right_strip: Any = None - self._selected_tab: int = -1 - self._draw_full_strip: bool = False - - def setStripEnabled(self, strip_enabled: bool): - self._draw_full_strip = strip_enabled - - def isStripEnabled(self) -> bool: - return self._draw_full_strip - - def setLeftStripDrawable(self, drawable: Any): - self._left_strip = drawable - - def setRightStripDrawable(self, drawable: Any): - self._right_strip = drawable - - def setDividerDrawable(self, drawable: Any): - pass - - def focusCurrentTab(self, index: int): - self._selected_tab = index - - def isEnabled(self) -> bool: - return True - - -# ═══════════════════════════════════════════════════════════════ -# ExpandableListView -# ═══════════════════════════════════════════════════════════════ - -class ExpandableListView(ListView): - """ExpandableListView — 可展开列表视图""" - - PACKED_POSITION_TYPE_GROUP = 0 - PACKED_POSITION_TYPE_CHILD = 1 - PACKED_POSITION_TYPE_NULL = 2 - PACKED_POSITION_VALUE_NULL = 0xFFFFFFFF - - def __init__(self, context=None): - super().__init__(context) - self._expandable_adapter: Optional["ExpandableListAdapter"] = None - self._on_group_collapse_listener: Optional[Callable] = None - self._on_group_expand_listener: Optional[Callable] = None - self._on_child_click_listener: Optional[Callable] = None - self._on_group_click_listener: Optional[Callable] = None - - def setAdapter(self, adapter: "ExpandableListAdapter"): - self._expandable_adapter = adapter - self._adapter = adapter - - def getExpandableListAdapter(self) -> Optional["ExpandableListAdapter"]: - return self._expandable_adapter - - def setOnGroupCollapseListener(self, listener: Optional[Callable]): - self._on_group_collapse_listener = listener - - def setOnGroupExpandListener(self, listener: Optional[Callable]): - self._on_group_expand_listener = listener - - def setOnChildClickListener(self, listener: Optional[Callable]): - self._on_child_click_listener = listener - - def setOnGroupClickListener(self, listener: Optional[Callable]): - self._on_group_click_listener = listener - - def expandGroup(self, group_pos: int, animate: bool = True) -> bool: - return True - - def collapseGroup(self, group_pos: int) -> bool: - return True - - def setOnItemClickListener(self, listener: Optional[Callable]): - self._on_item_click_listener = listener - - def getOnItemClickListener(self) -> Optional[Callable]: - return self._on_item_click_listener - - def setOnItemLongClickListener(self, listener: Optional[Callable]): - self._on_item_long_click_listener = listener - - def getOnItemLongClickListener(self) -> Optional[Callable]: - return self._on_item_long_click_listener - - def getPackedPositionType(self, packed_position: int) -> int: - if packed_position == ExpandableListView.PACKED_POSITION_VALUE_NULL: - return ExpandableListView.PACKED_POSITION_TYPE_NULL - return ExpandableListView.PACKED_POSITION_TYPE_GROUP - - def getPackedPositionGroup(self, packed_position: int) -> int: - return packed_position & 0xFFFF - - def getPackedPositionChild(self, packed_position: int) -> int: - return (packed_position >> 16) & 0xFFFF - - @staticmethod - def getPackedPositionForChild(group_position: int, child_position: int) -> int: - return (child_position << 16) | group_position - - @staticmethod - def getPackedPositionForGroup(group_position: int) -> int: - return group_position - - def isGroupExpanded(self, group_position: int) -> bool: - return False - - def setSelectedGroup(self, group_position: int): - pass - - def setSelectedChild(self, group_position: int, child_position: int, should_expand_group: bool): - pass - - def smoothScrollToPosition(self, position: int): - pass - - -# ═══════════════════════════════════════════════════════════════ -# ExpandableListAdapter -# ═══════════════════════════════════════════════════════════════ - -class ExpandableListAdapter(Stub): - """ExpandableListAdapter — 可扩展列表适配器接口""" - - def __init__(self): - super().__init__() - - def getGroupCount(self) -> int: - raise AndroidOnlyError("ExpandableListAdapter.getGroupCount: not implemented") - - def getChildrenCount(self, group_position: int) -> int: - raise AndroidOnlyError("ExpandableListAdapter.getChildrenCount: not implemented") - - def getGroup(self, group_position: int) -> Any: - raise AndroidOnlyError("ExpandableListAdapter.getGroup: not implemented") - - def getChild(self, group_position: int, child_position: int) -> Any: - raise AndroidOnlyError("ExpandableListAdapter.getChild: not implemented") - - def getGroupId(self, group_position: int) -> int: - raise AndroidOnlyError("ExpandableListAdapter.getGroupId: not implemented") - - def getChildId(self, group_position: int, child_position: int) -> int: - raise AndroidOnlyError("ExpandableListAdapter.getChildId: not implemented") - - def hasStableIds(self) -> bool: - return False - - def getGroupView(self, group_position: int, is_expanded: bool, - convert_view: Optional[Any], parent: Optional[Any]) -> Any: - raise AndroidOnlyError("ExpandableListAdapter.getGroupView: not implemented") - - def getChildView(self, group_position: int, child_position: int, - is_last_child: bool, convert_view: Optional[Any], - parent: Optional[Any]) -> Any: - raise AndroidOnlyError("ExpandableListAdapter.getChildView: not implemented") - - def isChildSelectable(self, group_position: int, child_position: int) -> bool: - return True - - def areAllItemsEnabled(self) -> bool: - return True - - def isEmpty(self) -> bool: - return self.getGroupCount() == 0 - - def onGroupCollapsed(self, group_position: int): - pass - - def onGroupExpanded(self, group_position: int): - pass - - def getCombinedGroupId(self, group_id: int) -> int: - return group_id - - def getCombinedChildId(self, group_id: int, child_id: int) -> int: - return (group_id << 32) | child_id - - -# ═══════════════════════════════════════════════════════════════ -# SimpleExpandableListAdapter -# ═══════════════════════════════════════════════════════════════ - -class SimpleExpandableListAdapter(ExpandableListAdapter, BaseAdapter): - """SimpleExpandableListAdapter — 简单的可展开列表适配器""" - - def __init__(self, context: Any, - group_data: List[Dict[str, Any]], - group_layout: int, - group_from: List[str], - group_to: List[int], - child_data: List[List[Dict[str, Any]]], - child_layout: int, - child_from: List[str], - child_to: List[int]): - super().__init__() - self._context = context - self._group_data = group_data - self._group_layout = group_layout - self._group_from = group_from - self._group_to = group_to - self._child_data = child_data - self._child_layout = child_layout - self._child_from = child_from - self._child_to = child_to - - def getGroupCount(self) -> int: - return len(self._group_data) - - def getChildrenCount(self, group_position: int) -> int: - if 0 <= group_position < len(self._child_data): - return len(self._child_data[group_position]) - return 0 - - def getGroup(self, group_position: int) -> Any: - if 0 <= group_position < len(self._group_data): - return self._group_data[group_position] - return None - - def getChild(self, group_position: int, child_position: int) -> Any: - if 0 <= group_position < len(self._child_data): - children = self._child_data[group_position] - if 0 <= child_position < len(children): - return children[child_position] - return None - - def getGroupId(self, group_position: int) -> int: - return group_position - - def getChildId(self, group_position: int, child_position: int) -> int: - return child_position - - def getGroupView(self, group_position: int, is_expanded: bool, - convert_view: Optional[Any], parent: Optional[Any]) -> Any: - if convert_view is not None: - view = convert_view - else: - view = TextView(self._context) - view.setText(str(self.getGroup(group_position))) - return view - - def getChildView(self, group_position: int, child_position: int, - is_last_child: bool, convert_view: Optional[Any], - parent: Optional[Any]) -> Any: - if convert_view is not None: - view = convert_view - else: - view = TextView(self._context) - child = self.getChild(group_position, child_position) - view.setText(str(child) if child else "") - return view - - def isChildSelectable(self, group_position: int, child_position: int) -> bool: - return True - - def getCount(self) -> int: - return self.getGroupCount() - - def getItem(self, position: int) -> Any: - return self.getGroup(position) - - def getItemId(self, position: int) -> int: - return position - - def getView(self, position: int, convert_view: Optional[Any], - parent: Optional[Any]) -> Any: - return self.getGroupView(position, False, convert_view, parent) - - -# ═══════════════════════════════════════════════════════════════ -# BaseExpandableListAdapter -# ═══════════════════════════════════════════════════════════════ - -class BaseExpandableListAdapter(ExpandableListAdapter, BaseAdapter): - """BaseExpandableListAdapter — 可展开列表适配器基类""" - - def __init__(self): - super().__init__() - - def getGroupCount(self) -> int: - return 0 - - def getChildrenCount(self, group_position: int) -> int: - return 0 - - def getGroup(self, group_position: int) -> Any: - return None - - def getChild(self, group_position: int, child_position: int) -> Any: - return None - - def getGroupId(self, group_position: int) -> int: - return group_position - - def getChildId(self, group_position: int, child_position: int) -> int: - return child_position - - def getGroupView(self, group_position: int, is_expanded: bool, - convert_view: Optional[Any], parent: Optional[Any]) -> Any: - if convert_view is not None: - view = convert_view - else: - view = TextView(android_context) - view.setText(f"Group {group_position}") - return view - - def getChildView(self, group_position: int, child_position: int, - is_last_child: bool, convert_view: Optional[Any], - parent: Optional[Any]) -> Any: - if convert_view is not None: - view = convert_view - else: - view = TextView(android_context) - view.setText(f"Child {group_position}:{child_position}") - return view - - def isChildSelectable(self, group_position: int, child_position: int) -> bool: - return True - - def areAllItemsEnabled(self) -> bool: - return True - - def onGroupCollapsed(self, group_position: int): - pass - - def onGroupExpanded(self, group_position: int): - pass - - def notifyDataSetChanged(self): - pass - - def notifyDataSetInvalidated(self): - pass - - def getCount(self) -> int: - return self.getGroupCount() - - def getItem(self, position: int) -> Any: - return self.getGroup(position) - - def getItemId(self, position: int) -> int: - return position - - def getView(self, position: int, convert_view: Optional[Any], - parent: Optional[Any]) -> Any: - return self.getGroupView(position, False, convert_view, parent) - - -# ═══════════════════════════════════════════════════════════════ -# SearchView -# ═══════════════════════════════════════════════════════════════ - -class SearchView(LinearLayout): - """SearchView — 搜索视图""" - - def __init__(self, context=None): - super().__init__(context) - self._query: str = "" - self._query_hint: Optional[str] = None - self._iconified: bool = True - self._iconified_by_default: bool = True - self._submit_button_enabled: bool = False - self._query_refinement_enabled: bool = False - self._searchable_info: Any = None - self._on_query_text_listener: Optional[Callable] = None - self._on_close_listener: Optional[Callable] = None - self._on_suggestion_listener: Optional[Callable] = None - self._on_query_text_focus_change_listener: Optional[Callable] = None - self._suggestion_adapter: Optional["CursorAdapter"] = None - self._max_width: int = sys.maxsize - self._on_search_click_listener: Optional[Callable] = None - - class OnQueryTextListener: - def onQueryTextSubmit(self, query: str) -> bool: - return False - - def onQueryTextChange(self, new_text: str) -> bool: - return False - - class OnCloseListener: - def onClose(self) -> bool: - return False - - class OnSuggestionListener: - def onSuggestionSelect(self, position: int) -> bool: - return False - - def onSuggestionClick(self, position: int) -> bool: - return False - - def setQuery(self, query: str, submit: bool = False): - self._query = query - if self._on_query_text_listener: - self._on_query_text_listener.onQueryTextChange(query) - if submit and self._on_query_text_listener: - self._on_query_text_listener.onQueryTextSubmit(query) - - def getQuery(self) -> str: - return self._query - - def setQueryHint(self, hint: Optional[str]): - self._query_hint = hint - - def getQueryHint(self) -> Optional[str]: - return self._query_hint - - def setIconified(self, iconified: bool): - self._iconified = iconified - - def isIconified(self) -> bool: - return self._iconified - - def setIconifiedByDefault(self, iconified: bool): - self._iconified_by_default = iconified - - def isIconifiedByDefault(self) -> bool: - return self._iconified_by_default - - def setSubmitButtonEnabled(self, enabled: bool): - self._submit_button_enabled = enabled - - def isSubmitButtonEnabled(self) -> bool: - return self._submit_button_enabled - - def setQueryRefinementEnabled(self, enable: bool): - self._query_refinement_enabled = enable - - def isQueryRefinementEnabled(self) -> bool: - return self._query_refinement_enabled - - def setOnQueryTextListener(self, listener: Optional["SearchView.OnQueryTextListener"]): - self._on_query_text_listener = listener - - def getOnQueryTextListener(self) -> Optional["SearchView.OnQueryTextListener"]: - return self._on_query_text_listener - - def setOnCloseListener(self, listener: Optional["SearchView.OnCloseListener"]): - self._on_close_listener = listener - - def setOnSuggestionListener(self, listener: Optional["SearchView.OnSuggestionListener"]): - self._on_suggestion_listener = listener - - def setOnSearchClickListener(self, listener: Optional[Callable]): - self._on_search_click_listener = listener - - def setSuggestionsAdapter(self, adapter: Optional["CursorAdapter"]): - self._suggestion_adapter = adapter - - def getSuggestionsAdapter(self) -> Optional["CursorAdapter"]: - return self._suggestion_adapter - - def setMaxWidth(self, max_width: int): - self._max_width = max_width - - def getMaxWidth(self) -> int: - return self._max_width - - def onActionViewExpanded(self): - self._iconified = False - - def onActionViewCollapsed(self): - self._iconified = True - - def onClose(self) -> bool: - if self._on_close_listener: - return self._on_close_listener.onClose() - self._iconified = self._iconified_by_default - return True - - def clearFocus(self): - pass - - -# ═══════════════════════════════════════════════════════════════ -# ZoomControls -# ═══════════════════════════════════════════════════════════════ - -class ZoomControls(LinearLayout): - """ZoomControls — 缩放控件""" - - def __init__(self, context=None): - super().__init__(context) - self._zoom_in_enabled: bool = True - self._zoom_out_enabled: bool = True - self._zoom_speed: int = 300 - self._on_zoom_in_listener: Optional[Callable] = None - self._on_zoom_out_listener: Optional[Callable] = None - - def show(self): - self.setVisibility(View.VISIBLE) - - def hide(self): - self.setVisibility(View.GONE) - - def setIsZoomInEnabled(self, is_enabled: bool): - self._zoom_in_enabled = is_enabled - - def setIsZoomOutEnabled(self, is_enabled: bool): - self._zoom_out_enabled = is_enabled - - def setOnZoomInClickListener(self, listener: Optional[Callable]): - self._on_zoom_in_listener = listener - - def setOnZoomOutClickListener(self, listener: Optional[Callable]): - self._on_zoom_out_listener = listener - - def setZoomSpeed(self, speed: int): - self._zoom_speed = speed - - def getZoomSpeed(self) -> int: - return self._zoom_speed - - def hasFocus(self) -> bool: - return False - - -# ═══════════════════════════════════════════════════════════════ -# DigitalClock -# ═══════════════════════════════════════════════════════════════ - -class DigitalClock(TextView): - """DigitalClock — 数字时钟(API 14+ 已弃用)""" - - def __init__(self, context=None): - super().__init__(context) - self._format: str = "%H:%M:%S" - self._update_interval: int = 1000 - self._running: bool = False - self._timer: Optional[Any] = None - - def start(self): - self._running = True - - def stop(self): - self._running = False - - def setFormat(self, fmt: str): - self._format = fmt - - def getFormat(self) -> str: - return self._format - - def onTimeChanged(self): - now = datetime.now().strftime(self._format) if hasattr(datetime, 'now') else "00:00:00" - self.setText(now) - - -# ═══════════════════════════════════════════════════════════════ -# AnalogClock -# ═══════════════════════════════════════════════════════════════ - -class AnalogClock(View): - """AnalogClock — 模拟时钟(API 14+ 已弃用)""" - - def __init__(self, context=None): - super().__init__(context) - self._dial: Optional[Any] = None - self._hand_hour: Optional[Any] = None - self._hand_minute: Optional[Any] = None - self._running: bool = False - self._update_interval: int = 1000 - - def start(self): - self._running = True - self.invalidate() - - def stop(self): - self._running = False - - def onDraw(self, canvas: Any): - super().onDraw(canvas) - - def setDialDrawable(self, drawable: Any): - self._dial = drawable - - def setHourDrawable(self, drawable: Any): - self._hand_hour = drawable - - def setMinuteDrawable(self, drawable: Any): - self._hand_minute = drawable - - -# ═══════════════════════════════════════════════════════════════ -# Chronometer -# ═══════════════════════════════════════════════════════════════ - -class Chronometer(TextView): - """Chronometer — 计时器""" - - def __init__(self, context=None): - super().__init__(context) - self._base: int = 0 - self._format: Optional[str] = None - self._running: bool = False - self._visible: bool = True - self._on_chronometer_tick_listener: Optional[Callable] = None - self._count_down: bool = False - self._elapsed: float = 0.0 - - def start(self): - self._running = True - - def stop(self): - self._running = False - - def setBase(self, base: int): - self._base = base - - def getBase(self) -> int: - return self._base - - def setFormat(self, fmt: str): - self._format = fmt - - def getFormat(self) -> Optional[str]: - return self._format - - def setOnChronometerTickListener(self, listener: Optional[Callable]): - self._on_chronometer_tick_listener = listener - - def getOnChronometerTickListener(self) -> Optional[Callable]: - return self._on_chronometer_tick_listener - - def isCountDown(self) -> bool: - return self._count_down - - def setCountDown(self, count_down: bool): - self._count_down = count_down - - def getElapsedRealtime(self) -> float: - return self._elapsed - - def setVisibility(self, visibility: int): - self._visible = (visibility == View.VISIBLE) - super().setVisibility(visibility) - - def onDraw(self, canvas: Any): - super().onDraw(canvas) - if self._running and self._on_chronometer_tick_listener: - try: - self._on_chronometer_tick_listener(self) - except Exception: - pass - - -# ═══════════════════════════════════════════════════════════════ -# __all__ -# ═══════════════════════════════════════════════════════════════ - -__all__ = [ - # TextView hierarchy - "TextView", "EditText", "Button", "ImageButton", "CheckBox", - "RadioButton", "ToggleButton", "Switch", - # ImageView - "ImageView", - # Progress - "ProgressBar", "SeekBar", "RatingBar", - # AdapterView hierarchy - "AdapterView", "ListView", "GridView", "ExpandableListView", - "Spinner", "AutoCompleteTextView", "MultiAutoCompleteTextView", - # RecyclerView - "RecyclerView", "RecyclerViewAdapter", "RecyclerViewViewHolder", - "RecyclerViewLayoutManager", "LinearLayoutManager", - "GridLayoutManager", "StaggeredGridLayoutManager", - # Layouts - "LinearLayout", "RelativeLayout", "FrameLayout", - "TableLayout", "TableRow", - # Scrolling - "ScrollView", "HorizontalScrollView", - # WebView - "WebView", "WebSettings", - # Adapters - "BaseAdapter", "ArrayAdapter", "SimpleAdapter", "CursorAdapter", - "ExpandableListAdapter", "SimpleExpandableListAdapter", - "BaseExpandableListAdapter", - # ViewAnimator - "ViewAnimator", "ViewFlipper", "ViewSwitcher", - "ImageSwitcher", "TextSwitcher", - "AdapterViewAnimator", "StackView", "AdapterViewFlipper", - # Date/Time - "DatePicker", "TimePicker", "CalendarView", "NumberPicker", - "DigitalClock", "AnalogClock", "Chronometer", - # Media - "VideoView", "MediaController", - # Toast/Popup - "Toast", "PopupMenu", "PopupWindow", - # Toolbar - "Toolbar", "ActionMenuView", - # Tabs - "TabHost", "TabWidget", - # Search - "SearchView", - # Zoom - "ZoomControls", -] \ No newline at end of file diff --git a/android_pyapi/android/widget_adapter.py b/android_pyapi/android/widget_adapter.py deleted file mode 100644 index 10e30c4..0000000 --- a/android_pyapi/android/widget_adapter.py +++ /dev/null @@ -1,1145 +0,0 @@ -""" -Android Widget Adapter 组件 — ListAdapter, SpinnerAdapter, ArrayAdapter (扩展), -CursorAdapter (扩展), RecyclerView.Adapter (扩展), RecyclerView.ViewHolder, -PagerAdapter, FragmentPagerAdapter, PagerTitleStrip 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -from typing import Any, Callable, Dict, Generic, List, Optional, Tuple, TypeVar - -from .._core import Stub, AndroidOnlyError - -T = TypeVar("T") - -__all__ = [ - "ListAdapter", "SpinnerAdapter", "WrapperListAdapter", - "HeaderViewListAdapter", "ArrayAdapter", "CursorAdapter", - "SimpleCursorAdapter", "ResourceCursorAdapter", "SimpleAdapter", - "BaseAdapter", "RecyclerView", - "PagerAdapter", "FragmentPagerAdapter", "FragmentStatePagerAdapter", - "PagerTitleStrip", "PagerTabStrip", -] - - -# ═══════════════════════════════════════════════════════════════ -# 适配器常量 -# ═══════════════════════════════════════════════════════════════ - -ADAPTER_IGNORE_ITEM_VIEW_TYPE = -1 -ADAPTER_NO_SELECTION = -1 -ADAPTER_INVALID_POSITION = -1 -ADAPTER_INVALID_ROW_ID = -1 -ADAPTER_HAS_STABLE_IDS = True -ADAPTER_NO_STABLE_IDS = False - -PAGER_POSITION_UNCHANGED = -1 -PAGER_POSITION_NONE = -2 - - -# ═══════════════════════════════════════════════════════════════ -# BaseAdapter (扩展) -# ═══════════════════════════════════════════════════════════════ - -class BaseAdapter(Stub): - """BaseAdapter — 基础适配器 (完整版)""" - - def __init__(self): - super().__init__() - self._observers: List[Any] = [] - - def getCount(self) -> int: - return 0 - - def getItem(self, position: int) -> Optional[Any]: - return None - - def getItemId(self, position: int) -> int: - return position - - def getView(self, position: int, convertView: Any, parent: Any) -> Any: - return None - - def getItemViewType(self, position: int) -> int: - return ADAPTER_IGNORE_ITEM_VIEW_TYPE - - def getViewTypeCount(self) -> int: - return 1 - - def hasStableIds(self) -> bool: - return False - - def isEmpty(self) -> bool: - return self.getCount() == 0 - - def registerDataSetObserver(self, observer: Any): - self._observers.append(observer) - - def unregisterDataSetObserver(self, observer: Any): - self._observers = [o for o in self._observers if o is not observer] - - def notifyDataSetChanged(self): - for o in self._observers: - if hasattr(o, 'onChanged'): - o.onChanged() - - def notifyDataSetInvalidated(self): - for o in self._observers: - if hasattr(o, 'onInvalidated'): - o.onInvalidated() - - def areAllItemsEnabled(self) -> bool: - return True - - def isEnabled(self, position: int) -> bool: - return True - - def getDropDownView(self, position: int, convertView: Any, parent: Any) -> Any: - return self.getView(position, convertView, parent) - - def toString(self) -> str: - return f"BaseAdapter(count={self.getCount()})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# ListAdapter -# ═══════════════════════════════════════════════════════════════ - -class ListAdapter(Stub): - """ListAdapter — 列表适配器接口""" - - def getCount(self) -> int: - return 0 - - def getItem(self, position: int) -> Optional[Any]: - return None - - def getItemId(self, position: int) -> int: - return position - - def getView(self, position: int, convertView: Any, parent: Any) -> Any: - return None - - def isEmpty(self) -> bool: - return self.getCount() == 0 - - def registerDataSetObserver(self, observer: Any): - pass - - def unregisterDataSetObserver(self, observer: Any): - pass - - def areAllItemsEnabled(self) -> bool: - return True - - def isEnabled(self, position: int) -> bool: - return True - - def getItemViewType(self, position: int) -> int: - return ADAPTER_IGNORE_ITEM_VIEW_TYPE - - def getViewTypeCount(self) -> int: - return 1 - - def hasStableIds(self) -> bool: - return False - - def toString(self) -> str: - return f"ListAdapter@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# SpinnerAdapter -# ═══════════════════════════════════════════════════════════════ - -class SpinnerAdapter(Stub): - """SpinnerAdapter — 下拉适配器接口""" - - def getCount(self) -> int: - return 0 - - def getItem(self, position: int) -> Optional[Any]: - return None - - def getItemId(self, position: int) -> int: - return position - - def getView(self, position: int, convertView: Any, parent: Any) -> Any: - return None - - def getDropDownView(self, position: int, convertView: Any, parent: Any) -> Any: - return self.getView(position, convertView, parent) - - def toString(self) -> str: - return f"SpinnerAdapter@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# WrapperListAdapter -# ═══════════════════════════════════════════════════════════════ - -class WrapperListAdapter(Stub): - """WrapperListAdapter — 包装列表适配器""" - - def getWrappedAdapter(self) -> Optional[ListAdapter]: - return None - - def getCount(self) -> int: - wrapped = self.getWrappedAdapter() - return wrapped.getCount() if wrapped else 0 - - def getItem(self, position: int) -> Optional[Any]: - wrapped = self.getWrappedAdapter() - return wrapped.getItem(position) if wrapped else None - - def getItemId(self, position: int) -> int: - wrapped = self.getWrappedAdapter() - return wrapped.getItemId(position) if wrapped else position - - def toString(self) -> str: - return f"WrapperListAdapter@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# HeaderViewListAdapter -# ═══════════════════════════════════════════════════════════════ - -class HeaderViewListAdapter(WrapperListAdapter): - """HeaderViewListAdapter — 页眉视图列表适配器""" - - def __init__(self, wrappedAdapter: Optional[ListAdapter] = None): - super().__init__() - self._wrapped_adapter = wrappedAdapter - self._headers: List[Any] = [] - self._footers: List[Any] = [] - - def getWrappedAdapter(self) -> Optional[ListAdapter]: - return self._wrapped_adapter - - def addHeader(self, view: Any, data: Any = None, isSelectable: bool = True): - self._headers.append((view, data, isSelectable)) - - def addFooter(self, view: Any, data: Any = None, isSelectable: bool = True): - self._footers.append((view, data, isSelectable)) - - def removeHeader(self, view: Any) -> bool: - for i, (v, _, _) in enumerate(self._headers): - if v is view: - self._headers.pop(i) - return True - return False - - def removeFooter(self, view: Any) -> bool: - for i, (v, _, _) in enumerate(self._footers): - if v is view: - self._footers.pop(i) - return True - return False - - def getHeadersCount(self) -> int: - return len(self._headers) - - def getFootersCount(self) -> int: - return len(self._footers) - - def getCount(self) -> int: - count = len(self._headers) + len(self._footers) - if self._wrapped_adapter: - count += self._wrapped_adapter.getCount() - return count - - def getItem(self, position: int) -> Optional[Any]: - if position < len(self._headers): - return self._headers[position][1] - offset = position - len(self._headers) - if self._wrapped_adapter and offset < self._wrapped_adapter.getCount(): - return self._wrapped_adapter.getItem(offset) - footer_offset = offset - (self._wrapped_adapter.getCount() if self._wrapped_adapter else 0) - if 0 <= footer_offset < len(self._footers): - return self._footers[footer_offset][1] - return None - - def getView(self, position: int, convertView: Any, parent: Any) -> Any: - if position < len(self._headers): - return self._headers[position][0] - offset = position - len(self._headers) - if self._wrapped_adapter and offset < self._wrapped_adapter.getCount(): - return self._wrapped_adapter.getView(offset, convertView, parent) - footer_offset = offset - (self._wrapped_adapter.getCount() if self._wrapped_adapter else 0) - if 0 <= footer_offset < len(self._footers): - return self._footers[footer_offset][0] - return None - - def toString(self) -> str: - return f"HeaderViewListAdapter(headers={len(self._headers)}, footers={len(self._footers)})" - - -# ═══════════════════════════════════════════════════════════════ -# ArrayAdapter (扩展) -# ═══════════════════════════════════════════════════════════════ - -class ArrayAdapter(BaseAdapter): - """ArrayAdapter — 数组适配器 (完整版)""" - - def __init__(self, context: Any = None, resource: int = 0, - textViewResourceId: int = 0, objects: List[Any] = None): - super().__init__() - self._context = context - self._resource = resource - self._text_view_resource_id = textViewResourceId - self._objects: List[Any] = list(objects) if objects else [] - self._drop_down_resource = resource - self._field_id = 0 - self._filter: Optional[Any] = None - - def add(self, obj: Any): - self._objects.append(obj) - self.notifyDataSetChanged() - - def addAll(self, *objects: Any): - for obj in objects: - self._objects.append(obj) - self.notifyDataSetChanged() - - def insert(self, obj: Any, index: int): - self._objects.insert(index, obj) - self.notifyDataSetChanged() - - def remove(self, obj: Any): - if obj in self._objects: - self._objects.remove(obj) - self.notifyDataSetChanged() - - def clear(self): - self._objects.clear() - self.notifyDataSetChanged() - - def sort(self, comparator: Callable[[Any, Any], int]): - self._objects.sort(key=lambda x: x) - self.notifyDataSetChanged() - - def getCount(self) -> int: - return len(self._objects) - - def getItem(self, position: int) -> Optional[Any]: - if 0 <= position < len(self._objects): - return self._objects[position] - return None - - def getPosition(self, item: Any) -> int: - try: - return self._objects.index(item) - except ValueError: - return ADAPTER_INVALID_POSITION - - def setDropDownViewResource(self, resource: int): - self._drop_down_resource = resource - - def getDropDownViewResource(self) -> int: - return self._drop_down_resource - - def getContext(self) -> Any: - return self._context - - def getFilter(self) -> Optional[Any]: - return self._filter - - def createFromResource(context: Any, textArrayResId: int, - textViewResId: int) -> "ArrayAdapter": - return ArrayAdapter(context=context, resource=textViewResId) - - def toString(self) -> str: - return f"ArrayAdapter(count={len(self._objects)})" - - -# ═══════════════════════════════════════════════════════════════ -# CursorAdapter (扩展) -# ═══════════════════════════════════════════════════════════════ - -class CursorAdapter(BaseAdapter): - """CursorAdapter — 游标适配器 (完整版)""" - - FLAG_AUTO_REQUERY = 0x01 - FLAG_REGISTER_CONTENT_OBSERVER = 0x02 - - def __init__(self, context: Any = None, cursor: Any = None, - flags: int = FLAG_AUTO_REQUERY): - super().__init__() - self._context = context - self._cursor = cursor - self._flags = flags - self._row_id_column = "_id" - self._count = 0 - if cursor: - self._count = len(cursor) if hasattr(cursor, '__len__') else 0 - - def getCursor(self) -> Any: - return self._cursor - - def setCursor(self, cursor: Any): - self._cursor = cursor - self._count = len(cursor) if cursor and hasattr(cursor, '__len__') else 0 - self.notifyDataSetChanged() - - def getCount(self) -> int: - return self._count - - def getItem(self, position: int) -> Optional[Any]: - if self._cursor and 0 <= position < self._count: - if hasattr(self._cursor, '__getitem__'): - return self._cursor[position] - return self._cursor - return None - - def getItemId(self, position: int) -> int: - if self._cursor and 0 <= position < self._count: - item = self.getItem(position) - if item and hasattr(item, 'get'): - return int(item.get(self._row_id_column, position)) - return position - - def getView(self, position: int, convertView: Any, parent: Any) -> Any: - return self.newView(self._context, self._cursor, parent) if not convertView else convertView - - def newView(self, context: Any, cursor: Any, parent: Any) -> Any: - return None - - def bindView(self, view: Any, context: Any, cursor: Any): - pass - - def changeCursor(self, cursor: Any): - self.setCursor(cursor) - - def getFilterQueryProvider(self) -> Optional[Any]: - return None - - def setFilterQueryProvider(self, provider: Any): - pass - - def runQueryOnBackgroundThread(self, constraint: str) -> Any: - return self._cursor - - def toString(self) -> str: - return f"CursorAdapter(count={self._count})" - - -# ═══════════════════════════════════════════════════════════════ -# SimpleCursorAdapter (扩展) -# ═══════════════════════════════════════════════════════════════ - -class SimpleCursorAdapter(CursorAdapter): - """SimpleCursorAdapter — 简单游标适配器 (完整版)""" - - def __init__(self, context: Any = None, layout: int = 0, - cursor: Any = None, fromColumns: List[str] = None, - toViews: List[int] = None, flags: int = 0): - super().__init__(context=context, cursor=cursor, flags=flags) - self._layout = layout - self._from_columns = fromColumns or [] - self._to_views = toViews or [] - - def getLayout(self) -> int: - return self._layout - - def setViewText(self, view: Any, text: str): - pass - - def setViewImage(self, view: Any, value: str): - pass - - def getStringConversionColumn(self) -> int: - return 0 - - def setStringConversionColumn(self, column: int): - pass - - def getViewBinder(self) -> Optional[Any]: - return None - - def setViewBinder(self, viewBinder: Any): - pass - - def toString(self) -> str: - return f"SimpleCursorAdapter(layout={self._layout})" - - -# ═══════════════════════════════════════════════════════════════ -# ResourceCursorAdapter (扩展) -# ═══════════════════════════════════════════════════════════════ - -class ResourceCursorAdapter(CursorAdapter): - """ResourceCursorAdapter — 资源游标适配器 (完整版)""" - - def __init__(self, context: Any = None, layout: int = 0, - cursor: Any = None, flags: int = 0, - dropDownLayout: int = 0): - super().__init__(context=context, cursor=cursor, flags=flags) - self._layout = layout - self._drop_down_layout = dropDownLayout or layout - - def setDropDownViewResource(self, dropDownLayout: int): - self._drop_down_layout = dropDownLayout - - def getDropDownView(self, position: int, convertView: Any, parent: Any) -> Any: - return self.newView(self._context, self._cursor, parent) - - def toString(self) -> str: - return f"ResourceCursorAdapter(layout={self._layout})" - - -# ═══════════════════════════════════════════════════════════════ -# SimpleAdapter (扩展) -# ═══════════════════════════════════════════════════════════════ - -class SimpleAdapter(BaseAdapter): - """SimpleAdapter — 简单适配器 (完整版)""" - - def __init__(self, context: Any = None, data: List[Dict[str, Any]] = None, - resource: int = 0, from_: List[str] = None, - to: List[int] = None): - super().__init__() - self._context = context - self._data = data or [] - self._resource = resource - self._from_keys = from_ or [] - self._to_ids = to or [] - self._drop_down_resource = resource - - def getCount(self) -> int: - return len(self._data) - - def getItem(self, position: int) -> Optional[Dict[str, Any]]: - if 0 <= position < len(self._data): - return self._data[position] - return None - - def getItemId(self, position: int) -> int: - return position - - def getView(self, position: int, convertView: Any, parent: Any) -> Any: - return convertView - - def setDropDownViewResource(self, resource: int): - self._drop_down_resource = resource - - def getDropDownView(self, position: int, convertView: Any, parent: Any) -> Any: - return convertView - - def getContext(self) -> Any: - return self._context - - def getData(self) -> List[Dict[str, Any]]: - return list(self._data) - - def setData(self, data: List[Dict[str, Any]]): - self._data = list(data) - self.notifyDataSetChanged() - - def toString(self) -> str: - return f"SimpleAdapter(count={len(self._data)})" - - -# ═══════════════════════════════════════════════════════════════ -# RecyclerView -# ═══════════════════════════════════════════════════════════════ - -class RecyclerView(Stub): - """RecyclerView — 循环视图""" - - HORIZONTAL = 0 - VERTICAL = 1 - NO_POSITION = -1 - NO_ID = -1 - INVALID_TYPE = -1 - - # ═══════════════════════════════════════════════════════════ - # RecyclerView.Adapter (扩展) - # ═══════════════════════════════════════════════════════════ - - class Adapter(BaseAdapter): - """RecyclerView.Adapter — RecyclerView 适配器 (完整版)""" - - def __init__(self): - super().__init__() - self._has_stable_ids = False - self._state_restoration_policy = 0 - self._observers: List[RecyclerView.AdapterDataObserver] = [] - - def onCreateViewHolder(self, parent: Any, viewType: int) -> "RecyclerView.ViewHolder": - raise NotImplementedError - - def onBindViewHolder(self, holder: "RecyclerView.ViewHolder", position: int): - raise NotImplementedError - - def getItemCount(self) -> int: - return 0 - - def getItemViewType(self, position: int) -> int: - return ADAPTER_IGNORE_ITEM_VIEW_TYPE - - def setHasStableIds(self, hasStableIds: bool): - self._has_stable_ids = hasStableIds - - def hasStableIds(self) -> bool: - return self._has_stable_ids - - def onAttachedToRecyclerView(self, recyclerView: "RecyclerView"): - pass - - def onDetachedFromRecyclerView(self, recyclerView: "RecyclerView"): - pass - - def onViewAttachedToWindow(self, holder: "RecyclerView.ViewHolder"): - pass - - def onViewDetachedFromWindow(self, holder: "RecyclerView.ViewHolder"): - pass - - def onViewRecycled(self, holder: "RecyclerView.ViewHolder"): - pass - - def onFailedToRecycleView(self, holder: "RecyclerView.ViewHolder") -> bool: - return False - - def registerAdapterDataObserver(self, observer: "RecyclerView.AdapterDataObserver"): - self._observers.append(observer) - - def unregisterAdapterDataObserver(self, observer: "RecyclerView.AdapterDataObserver"): - self._observers = [o for o in self._observers if o is not observer] - - def notifyDataSetChanged(self): - for o in self._observers: - o.onChanged() - - def notifyItemChanged(self, position: int): - for o in self._observers: - o.onItemRangeChanged(position, 1) - - def notifyItemRangeChanged(self, positionStart: int, itemCount: int): - for o in self._observers: - o.onItemRangeChanged(positionStart, itemCount) - - def notifyItemInserted(self, position: int): - for o in self._observers: - o.onItemRangeInserted(position, 1) - - def notifyItemRangeInserted(self, positionStart: int, itemCount: int): - for o in self._observers: - o.onItemRangeInserted(positionStart, itemCount) - - def notifyItemRemoved(self, position: int): - for o in self._observers: - o.onItemRangeRemoved(position, 1) - - def notifyItemRangeRemoved(self, positionStart: int, itemCount: int): - for o in self._observers: - o.onItemRangeRemoved(positionStart, itemCount) - - def notifyItemMoved(self, fromPosition: int, toPosition: int): - for o in self._observers: - o.onItemRangeMoved(fromPosition, toPosition, 1) - - def getItemId(self, position: int) -> int: - return position - - def toString(self) -> str: - return f"RecyclerView.Adapter(items={self.getItemCount()})" - - # ═══════════════════════════════════════════════════════════ - # RecyclerView.ViewHolder (扩展) - # ═══════════════════════════════════════════════════════════ - - class ViewHolder(Stub): - """RecyclerView.ViewHolder — 视图持有者 (完整版)""" - - def __init__(self, itemView: Any = None): - super().__init__() - self.itemView = itemView - self._adapter_position = RecyclerView.NO_POSITION - self._old_position = RecyclerView.NO_POSITION - self._item_id = RecyclerView.NO_ID - self._item_view_type = RecyclerView.INVALID_TYPE - self._is_recyclable = True - self._is_bound = False - - def getAdapterPosition(self) -> int: - return self._adapter_position - - def getOldPosition(self) -> int: - return self._old_position - - def getItemId(self) -> int: - return self._item_id - - def getItemViewType(self) -> int: - return self._item_view_type - - def setIsRecyclable(self, recyclable: bool): - self._is_recyclable = recyclable - - def isRecyclable(self) -> bool: - return self._is_recyclable - - def setIsBound(self, bound: bool): - self._is_bound = bound - - def isBound(self) -> bool: - return self._is_bound - - def toString(self) -> str: - return f"ViewHolder(pos={self._adapter_position})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - # ═══════════════════════════════════════════════════════════ - # RecyclerView.LayoutManager (扩展) - # ═══════════════════════════════════════════════════════════ - - class LayoutManager(Stub): - """RecyclerView.LayoutManager — 布局管理器 (完整版)""" - - def __init__(self): - super().__init__() - self._child_count = 0 - self._item_count = 0 - self._auto_measure = True - - def generateDefaultLayoutParams(self) -> Any: - return None - - def onLayoutChildren(self, recycler: Any, state: Any): - pass - - def getChildCount(self) -> int: - return self._child_count - - def getItemCount(self) -> int: - return self._item_count - - def setAutoMeasureEnabled(self, enabled: bool): - self._auto_measure = enabled - - def isAutoMeasureEnabled(self) -> bool: - return self._auto_measure - - def canScrollHorizontally(self) -> bool: - return False - - def canScrollVertically(self) -> bool: - return False - - def scrollToPosition(self, position: int): - pass - - def smoothScrollToPosition(self, view: Any, state: Any, position: int): - pass - - def computeVerticalScrollOffset(self, state: Any) -> int: - return 0 - - def computeVerticalScrollExtent(self, state: Any) -> int: - return 0 - - def computeVerticalScrollRange(self, state: Any) -> int: - return 0 - - def computeHorizontalScrollOffset(self, state: Any) -> int: - return 0 - - def computeHorizontalScrollExtent(self, state: Any) -> int: - return 0 - - def computeHorizontalScrollRange(self, state: Any) -> int: - return 0 - - def toString(self) -> str: - return f"LayoutManager(children={self._child_count})" - - # ═══════════════════════════════════════════════════════════ - # RecyclerView.AdapterDataObserver - # ═══════════════════════════════════════════════════════════ - - class AdapterDataObserver(Stub): - """RecyclerView.AdapterDataObserver — 适配器数据观察者""" - - def onChanged(self): - pass - - def onItemRangeChanged(self, positionStart: int, itemCount: int): - pass - - def onItemRangeInserted(self, positionStart: int, itemCount: int): - pass - - def onItemRangeRemoved(self, positionStart: int, itemCount: int): - pass - - def onItemRangeMoved(self, fromPosition: int, toPosition: int, itemCount: int): - pass - - def toString(self) -> str: - return f"AdapterDataObserver@{hex(id(self))}" - - # ═══════════════════════════════════════════════════════════ - # RecyclerView.ItemDecoration - # ═══════════════════════════════════════════════════════════ - - class ItemDecoration(Stub): - """RecyclerView.ItemDecoration — 项目装饰""" - - def onDraw(self, canvas: Any, parent: "RecyclerView", state: Any): - pass - - def onDrawOver(self, canvas: Any, parent: "RecyclerView", state: Any): - pass - - def getItemOffsets(self, outRect: Any, view: Any, - parent: "RecyclerView", state: Any): - outRect.set(0, 0, 0, 0) - - def toString(self) -> str: - return f"ItemDecoration@{hex(id(self))}" - - # ═══════════════════════════════════════════════════════════ - # RecyclerView.ItemAnimator - # ═══════════════════════════════════════════════════════════ - - class ItemAnimator(Stub): - """RecyclerView.ItemAnimator — 项目动画器""" - - def animateDisappearance(self, holder: ViewHolder, preInfo: Any, - postInfo: Any) -> bool: - return False - - def animateAppearance(self, holder: ViewHolder, preInfo: Any, - postInfo: Any) -> bool: - return False - - def animatePersistence(self, holder: ViewHolder, preInfo: Any, - postInfo: Any) -> bool: - return False - - def animateChange(self, oldHolder: ViewHolder, newHolder: ViewHolder, - preInfo: Any, postInfo: Any) -> bool: - return False - - def endAnimation(self, holder: ViewHolder): - pass - - def endAnimations(self): - pass - - def isRunning(self) -> bool: - return False - - def dispatchAnimationsFinished(self): - pass - - def toString(self) -> str: - return f"ItemAnimator@{hex(id(self))}" - - # ═══════════════════════════════════════════════════════════ - # RecyclerView.SmoothScroller - # ═══════════════════════════════════════════════════════════ - - class SmoothScroller(Stub): - """RecyclerView.SmoothScroller — 平滑滚动器""" - - def start(self, recyclerView: "RecyclerView", layoutManager: LayoutManager, - targetPosition: int): - pass - - def stop(self): - pass - - def isRunning(self) -> bool: - return False - - def getTargetPosition(self) -> int: - return RecyclerView.NO_POSITION - - def toString(self) -> str: - return f"SmoothScroller@{hex(id(self))}" - - # ═══════════════════════════════════════════════════════════ - # RecyclerView.OnScrollListener - # ═══════════════════════════════════════════════════════════ - - class OnScrollListener(Stub): - """RecyclerView.OnScrollListener — 滚动监听器""" - - SCROLL_STATE_IDLE = 0 - SCROLL_STATE_DRAGGING = 1 - SCROLL_STATE_SETTLING = 2 - - def onScrollStateChanged(self, recyclerView: "RecyclerView", newState: int): - pass - - def onScrolled(self, recyclerView: "RecyclerView", dx: int, dy: int): - pass - - def toString(self) -> str: - return f"OnScrollListener@{hex(id(self))}" - - def __init__(self): - super().__init__() - self._adapter: Optional[RecyclerView.Adapter] = None - self._layout_manager: Optional[RecyclerView.LayoutManager] = None - self._item_animator: Optional[RecyclerView.ItemAnimator] = None - self._scroll_listeners: List[RecyclerView.OnScrollListener] = [] - self._item_decoration: Optional[RecyclerView.ItemDecoration] = None - - def setAdapter(self, adapter: Adapter): - self._adapter = adapter - - def getAdapter(self) -> Optional[Adapter]: - return self._adapter - - def setLayoutManager(self, layoutManager: LayoutManager): - self._layout_manager = layoutManager - - def getLayoutManager(self) -> Optional[LayoutManager]: - return self._layout_manager - - def setItemAnimator(self, animator: ItemAnimator): - self._item_animator = animator - - def getItemAnimator(self) -> Optional[ItemAnimator]: - return self._item_animator - - def addItemDecoration(self, decoration: ItemDecoration, index: int = -1): - self._item_decoration = decoration - - def addOnScrollListener(self, listener: OnScrollListener): - self._scroll_listeners.append(listener) - - def removeOnScrollListener(self, listener: OnScrollListener): - self._scroll_listeners = [l for l in self._scroll_listeners if l is not listener] - - def clearOnScrollListeners(self): - self._scroll_listeners.clear() - - def scrollToPosition(self, position: int): - pass - - def smoothScrollToPosition(self, position: int): - pass - - def toString(self) -> str: - return f"RecyclerView(adapter={'set' if self._adapter else 'null'})" - - -# ═══════════════════════════════════════════════════════════════ -# PagerAdapter -# ═══════════════════════════════════════════════════════════════ - -class PagerAdapter(Stub): - """PagerAdapter — 页面适配器""" - - POSITION_UNCHANGED = PAGER_POSITION_UNCHANGED - POSITION_NONE = PAGER_POSITION_NONE - - def __init__(self): - super().__init__() - self._observers: List[Any] = [] - - def getCount(self) -> int: - return 0 - - def instantiateItem(self, container: Any, position: int) -> Any: - return None - - def destroyItem(self, container: Any, position: int, obj: Any): - pass - - def isViewFromObject(self, view: Any, obj: Any) -> bool: - return False - - def getPageTitle(self, position: int) -> Optional[str]: - return None - - def getPageWidth(self, position: int) -> float: - return 1.0 - - def getItemPosition(self, obj: Any) -> int: - return PagerAdapter.POSITION_UNCHANGED - - def startUpdate(self, container: Any): - pass - - def finishUpdate(self, container: Any): - pass - - def registerDataSetObserver(self, observer: Any): - self._observers.append(observer) - - def unregisterDataSetObserver(self, observer: Any): - self._observers = [o for o in self._observers if o is not observer] - - def notifyDataSetChanged(self): - for o in self._observers: - if hasattr(o, 'onChanged'): - o.onChanged() - - def toString(self) -> str: - return f"PagerAdapter(count={self.getCount()})" - - -# ═══════════════════════════════════════════════════════════════ -# FragmentPagerAdapter -# ═══════════════════════════════════════════════════════════════ - -class FragmentPagerAdapter(PagerAdapter): - """FragmentPagerAdapter — Fragment 页面适配器""" - - BEHAVIOR_SET_USER_VISIBLE_HINT = 0 - BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1 - - def __init__(self, fm: Any = None, behavior: int = BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT): - super().__init__() - self._fm = fm - self._behavior = behavior - self._fragments: Dict[int, Any] = {} - - def getItem(self, position: int) -> Any: - raise NotImplementedError - - def instantiateItem(self, container: Any, position: int) -> Any: - fragment = self.getItem(position) - self._fragments[position] = fragment - return fragment - - def destroyItem(self, container: Any, position: int, obj: Any): - self._fragments.pop(position, None) - - def isViewFromObject(self, view: Any, obj: Any) -> bool: - if hasattr(obj, 'getView'): - return obj.getView() is view - return False - - def getPageTitle(self, position: int) -> Optional[str]: - return None - - def getItemId(self, position: int) -> int: - return position - - def toString(self) -> str: - return f"FragmentPagerAdapter(count={self.getCount()})" - - -# ═══════════════════════════════════════════════════════════════ -# FragmentStatePagerAdapter -# ═══════════════════════════════════════════════════════════════ - -class FragmentStatePagerAdapter(FragmentPagerAdapter): - """FragmentStatePagerAdapter — Fragment 状态页面适配器""" - - def __init__(self, fm: Any = None, behavior: int = FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT): - super().__init__(fm=fm, behavior=behavior) - self._saved_states: Dict[int, Any] = {} - - def saveState(self, position: int) -> Any: - state = self._saved_states.get(position) - self._fragments.pop(position, None) - return state - - def restoreState(self, state: Any, loader: Any): - pass - - def instantiateItem(self, container: Any, position: int) -> Any: - return super().instantiateItem(container, position) - - def destroyItem(self, container: Any, position: int, obj: Any): - state = self.saveState(position) - if state: - self._saved_states[position] = state - super().destroyItem(container, position, obj) - - def toString(self) -> str: - return f"FragmentStatePagerAdapter(count={self.getCount()})" - - -# ═══════════════════════════════════════════════════════════════ -# PagerTitleStrip / PagerTabStrip -# ═══════════════════════════════════════════════════════════════ - -class PagerTitleStrip(Stub): - """PagerTitleStrip — 页面标题条""" - - def __init__(self, context: Any = None): - super().__init__() - self._text_spacing = 16 - self._nonprimary_alpha = 0.6 - self._current_page = 0 - self._adapter: Optional[PagerAdapter] = None - - def setAdapter(self, adapter: PagerAdapter): - self._adapter = adapter - - def getAdapter(self) -> Optional[PagerAdapter]: - return self._adapter - - def setTextSpacing(self, spacingPixels: int): - self._text_spacing = spacingPixels - - def getTextSpacing(self) -> int: - return self._text_spacing - - def updateTitle(self, position: int, adapter: PagerAdapter): - self._current_page = position - - def requestLayout(self): - pass - - def toString(self) -> str: - return f"PagerTitleStrip(page={self._current_page})" - - -class PagerTabStrip(PagerTitleStrip): - """PagerTabStrip — 页面标签条""" - - def __init__(self, context: Any = None): - super().__init__(context=context) - self._tab_indicator_color = 0 - self._tab_spacing = 8 - self._draw_full_underline = False - self._underline_height = 2 - self._tab_padding = 16 - - def setTabIndicatorColor(self, color: int): - self._tab_indicator_color = color - - def getTabIndicatorColor(self) -> int: - return self._tab_indicator_color - - def setDrawFullUnderline(self, draw: bool): - self._draw_full_underline = draw - - def isDrawFullUnderline(self) -> bool: - return self._draw_full_underline - - def setUnderlineHeight(self, height: int): - self._underline_height = height - - def getUnderlineHeight(self) -> int: - return self._underline_height - - def setTabPadding(self, padding: int): - self._tab_padding = padding - - def getTabPadding(self) -> int: - return self._tab_padding - - def toString(self) -> str: - return f"PagerTabStrip(page={self._current_page})" \ No newline at end of file diff --git a/android_pyapi/android/widget_ext.py b/android_pyapi/android/widget_ext.py deleted file mode 100644 index 699021f..0000000 --- a/android_pyapi/android/widget_ext.py +++ /dev/null @@ -1,2232 +0,0 @@ -""" -Android Widget 扩展组件 — AbsoluteLayout, AdapterView, ProgressBar, SeekBar, Layouts 等 -方法签名完全对齐 Android SDK -""" - -from __future__ import annotations - -import math -import time -import threading -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union - -from .._core import Stub, AndroidOnlyError -from .widget import AdapterView as _BaseAdapterView -from .widget import AutoCompleteTextView as _BaseAutoCompleteTextView -from .widget import ProgressBar as _BaseProgressBar -from .widget import Chronometer as _BaseChronometer -from .widget import TableLayout as _BaseTableLayout -from .widget import TableRow as _BaseTableRow -from .widget import LinearLayout as _BaseLinearLayout -from .widget import FrameLayout as _BaseFrameLayout -from .widget import RelativeLayout as _BaseRelativeLayout -from .widget import NumberPicker as _BaseNumberPicker -from .widget import SearchView as _BaseSearchView -from .widget import TabHost as _BaseTabHost -from .widget import TabWidget as _BaseTabWidget -from .widget import ViewAnimator as _BaseViewAnimator -from .widget import ImageButton as _BaseImageButton -from .widget import ZoomControls as _BaseZoomControls -from .widget import DigitalClock as _BaseDigitalClock -from .widget import AnalogClock as _BaseAnalogClock -from .widget import ToggleButton as _BaseToggleButton -from .widget import RadioButton as _BaseRadioButton -from .widget import SeekBar as _BaseSeekBar -from .widget import RatingBar as _BaseRatingBar -from .widget import ViewSwitcher as _BaseViewSwitcher -from .widget import TextSwitcher as _BaseTextSwitcher -from .widget import ImageSwitcher as _BaseImageSwitcher -from .widget import BaseAdapter as _BaseBaseAdapter -from .widget import ArrayAdapter as _BaseArrayAdapter -from .widget import SimpleAdapter as _BaseSimpleAdapter -from .widget import CursorAdapter as _BaseCursorAdapter -from ..java.lang import RuntimeException - - -# ═══════════════════════════════════════════════════════════════ -# 布局常量 -# ═══════════════════════════════════════════════════════════════ - -VISIBLE = 0 -INVISIBLE = 4 -GONE = 8 - -MATCH_PARENT = -1 -WRAP_CONTENT = -2 - -FILL_PARENT = -1 - -HORIZONTAL = 0 -VERTICAL = 1 - -LEFT = 0 -RIGHT = 1 -CENTER = 2 -TOP = 3 -BOTTOM = 4 - -NO_ID = -1 - - -# ═══════════════════════════════════════════════════════════════ -# AbsoluteLayout -# ═══════════════════════════════════════════════════════════════ - -class AbsoluteLayout(Stub): - """AbsoluteLayout — 绝对布局""" - - class LayoutParams(Stub): - """AbsoluteLayout.LayoutParams — 绝对布局参数""" - - def __init__(self, width: int = WRAP_CONTENT, height: int = WRAP_CONTENT, - x: int = 0, y: int = 0): - super().__init__() - self.width = width - self.height = height - self.x = x - self.y = y - - def toString(self) -> str: - return (f"AbsoluteLayout.LayoutParams({self.width}x{self.height} " - f"@{self.x},{self.y})") - - def __init__(self, context: Any = None): - super().__init__() - self._children: List[Any] = [] - - def addView(self, child: Any, x: int = 0, y: int = 0) -> None: - self._children.append(child) - - def toString(self) -> str: - return f"AbsoluteLayout(children={len(self._children)})" - - -# ═══════════════════════════════════════════════════════════════ -# Adapter / AdapterView / Filter / Filterable -# ═══════════════════════════════════════════════════════════════ - -class Adapter(Stub): - """Adapter — 适配器接口""" - - IGNORE_ITEM_VIEW_TYPE = -1 - NO_SELECTION = -2147483648 - - def __init__(self): - super().__init__() - self._data_set_observers: List[Callable] = [] - - def registerDataSetObserver(self, observer: Callable) -> None: - if observer not in self._data_set_observers: - self._data_set_observers.append(observer) - - def unregisterDataSetObserver(self, observer: Callable) -> None: - if observer in self._data_set_observers: - self._data_set_observers.remove(observer) - - def notifyDataSetChanged(self) -> None: - for obs in list(self._data_set_observers): - obs() - - def notifyDataSetInvalidated(self) -> None: - for obs in list(self._data_set_observers): - obs() - - def getCount(self) -> int: - return 0 - - def getItem(self, position: int) -> Optional[Any]: - return None - - def getItemId(self, position: int) -> int: - return position - - def getItemViewType(self, position: int) -> int: - return Adapter.IGNORE_ITEM_VIEW_TYPE - - def getViewTypeCount(self) -> int: - return 1 - - def hasStableIds(self) -> bool: - return False - - def isEmpty(self) -> bool: - return self.getCount() == 0 - - def getView(self, position: int, convert_view: Optional[Any], - parent: Any) -> Optional[Any]: - return None - - def getDropDownView(self, position: int, convert_view: Optional[Any], - parent: Any) -> Optional[Any]: - return self.getView(position, convert_view, parent) - - def toString(self) -> str: - return f"Adapter(count={self.getCount()})" - - -class AdapterView(_BaseAdapterView): - """AdapterView — 适配器视图""" - - ITEM_VIEW_TYPE_IGNORE = -1 - ITEM_VIEW_TYPE_HEADER_OR_FOOTER = -2 - - INVALID_POSITION = -1 - INVALID_ROW_ID = -9223372036854775808 - INVALID_TYPE = -1 - - CHOICE_MODE_NONE = 0 - CHOICE_MODE_SINGLE = 1 - CHOICE_MODE_MULTIPLE = 2 - CHOICE_MODE_MODAL = 3 - - def __init__(self, context: Any = None): - super().__init__() - self._adapter: Optional[Adapter] = None - self._choice_mode: int = self.CHOICE_MODE_NONE - self._checked_positions: Set[int] = set() - self._selected_position: int = self.INVALID_POSITION - self._item_click_listener: Optional[Callable] = None - self._item_long_click_listener: Optional[Callable] = None - self._item_selected_listener: Optional[Callable] = None - self._empty_view: Optional[Any] = None - self._first_visible_position: int = 0 - self._last_visible_position: int = 0 - self._lock = threading.Lock() - - def setAdapter(self, adapter: Optional[Adapter]) -> None: - self._adapter = adapter - - def getAdapter(self) -> Optional[Adapter]: - return self._adapter - - def setOnItemClickListener(self, listener: Optional[Callable]) -> None: - self._item_click_listener = listener - - def getOnItemClickListener(self) -> Optional[Callable]: - return self._item_click_listener - - def setOnItemLongClickListener(self, listener: Optional[Callable]) -> None: - self._item_long_click_listener = listener - - def setOnItemSelectedListener(self, listener: Optional[Callable]) -> None: - self._item_selected_listener = listener - - def getSelectedItemPosition(self) -> int: - return self._selected_position - - def getSelectedItemId(self) -> int: - if self._adapter and self._selected_position != self.INVALID_POSITION: - return self._adapter.getItemId(self._selected_position) - return self.INVALID_ROW_ID - - def getSelectedView(self) -> Optional[Any]: - return None - - def getSelectedItem(self) -> Optional[Any]: - if self._adapter and self._selected_position != self.INVALID_POSITION: - return self._adapter.getItem(self._selected_position) - return None - - def setSelection(self, position: int) -> None: - self._selected_position = position - - def getFirstVisiblePosition(self) -> int: - return self._first_visible_position - - def getLastVisiblePosition(self) -> int: - return self._last_visible_position - - def getCount(self) -> int: - return self._adapter.getCount() if self._adapter else 0 - - def getChildAt(self, index: int) -> Optional[Any]: - return None - - def getChildCount(self) -> int: - return 0 - - def setChoiceMode(self, mode: int) -> None: - self._choice_mode = mode - - def getChoiceMode(self) -> int: - return self._choice_mode - - def setItemChecked(self, position: int, checked: bool) -> None: - with self._lock: - if checked: - if self._choice_mode == self.CHOICE_MODE_SINGLE: - self._checked_positions.clear() - self._checked_positions.add(position) - else: - self._checked_positions.discard(position) - - def isItemChecked(self, position: int) -> bool: - return position in self._checked_positions - - def getCheckedItemPosition(self) -> int: - if self._checked_positions: - return next(iter(self._checked_positions)) - return self.INVALID_POSITION - - def getCheckedItemPositions(self) -> Set[int]: - return set(self._checked_positions) - - def getCheckedItemIds(self) -> Set[int]: - ids = set() - for pos in self._checked_positions: - if self._adapter: - ids.add(self._adapter.getItemId(pos)) - return ids - - def getCheckItemCount(self) -> int: - return len(self._checked_positions) - - def setEmptyView(self, view: Any) -> None: - self._empty_view = view - - def getEmptyView(self) -> Optional[Any]: - return self._empty_view - - def performItemClick(self, view: Any, position: int, id_val: int) -> bool: - if self._item_click_listener: - self._item_click_listener(self, view, position, id_val) - return True - return False - - def toString(self) -> str: - return (f"AdapterView(adapter={'yes' if self._adapter else 'no'}, " - f"count={self.getCount()})") - - -class Filter(Stub): - """Filter — 过滤器""" - - class FilterResults(Stub): - """Filter.FilterResults — 过滤结果""" - - def __init__(self): - super().__init__() - self.values: Optional[Any] = None - self.count: int = 0 - - def __init__(self): - super().__init__() - self._filter_listener: Optional[Callable] = None - - def filter(self, constraint: str) -> None: - results = self.performFiltering(constraint) - self.publishResults(constraint, results) - - def filter(self, constraint: str, listener: Optional[Callable]) -> None: - self._filter_listener = listener - self.filter(constraint) - - def performFiltering(self, constraint: str) -> FilterResults: - return Filter.FilterResults() - - def publishResults(self, constraint: str, results: FilterResults) -> None: - pass - - def convertResultToString(self, result_value: Any) -> str: - return str(result_value) if result_value is not None else "" - - def toString(self) -> str: - return "Filter()" - - -class Filterable(Stub): - """Filterable — 可过滤接口""" - - def getFilter(self) -> Filter: - return Filter() - - -class ThemedSpinnerAdapter(Stub): - """ThemedSpinnerAdapter — 主题化 Spinner 适配器""" - - def setDropDownViewTheme(self, theme: Any) -> None: - pass - - def getDropDownViewTheme(self) -> Optional[Any]: - return None - - -# ═══════════════════════════════════════════════════════════════ -# AutoCompleteTextView -# ═══════════════════════════════════════════════════════════════ - -class AutoCompleteTextView(_BaseAutoCompleteTextView): - """AutoCompleteTextView — 自动完成文本视图""" - - THRESHOLD_DEFAULT = 2 - - def __init__(self, context: Any = None): - super().__init__() - self._threshold = self.THRESHOLD_DEFAULT - self._adapter: Optional[Adapter] = None - self._listener: Optional[Callable] = None - self._validator: Optional[Callable] = None - self._completion_hint: Optional[str] = None - self._drop_down_width: int = WRAP_CONTENT - self._drop_down_height: int = WRAP_CONTENT - self._drop_down_anchor: Optional[Any] = None - self._drop_down_horizontal_offset: int = 0 - self._drop_down_vertical_offset: int = 0 - self._showing = False - - def setThreshold(self, threshold: int) -> None: - self._threshold = max(1, threshold) - - def getThreshold(self) -> int: - return self._threshold - - def setAdapter(self, adapter: Adapter) -> None: - self._adapter = adapter - - def getAdapter(self) -> Optional[Adapter]: - return self._adapter - - def setOnItemClickListener(self, listener: Optional[Callable]) -> None: - self._listener = listener - - def setOnItemSelectedListener(self, listener: Optional[Callable]) -> None: - pass - - def showDropDown(self) -> None: - self._showing = True - - def dismissDropDown(self) -> None: - self._showing = False - - def isShowing(self) -> bool: - return self._showing - - def setDropDownWidth(self, width: int) -> None: - self._drop_down_width = width - - def getDropDownWidth(self) -> int: - return self._drop_down_width - - def setDropDownHeight(self, height: int) -> None: - self._drop_down_height = height - - def getDropDownHeight(self) -> int: - return self._drop_down_height - - def setDropDownAnchor(self, anchor: Any) -> None: - self._drop_down_anchor = anchor - - def getDropDownAnchor(self) -> Optional[Any]: - return self._drop_down_anchor - - def setDropDownHorizontalOffset(self, offset: int) -> None: - self._drop_down_horizontal_offset = offset - - def getDropDownHorizontalOffset(self) -> int: - return self._drop_down_horizontal_offset - - def setDropDownVerticalOffset(self, offset: int) -> None: - self._drop_down_vertical_offset = offset - - def getDropDownVerticalOffset(self) -> int: - return self._drop_down_vertical_offset - - def setCompletionHint(self, hint: str) -> None: - self._completion_hint = hint - - def getCompletionHint(self) -> Optional[str]: - return self._completion_hint - - def setValidator(self, validator: Optional[Callable]) -> None: - self._validator = validator - - def getValidator(self) -> Optional[Callable]: - return self._validator - - def performValidation(self) -> None: - if self._validator: - self._validator() - - def enoughToFilter(self) -> bool: - return True - - def toString(self) -> str: - return f"AutoCompleteTextView(threshold={self._threshold})" - - -# ═══════════════════════════════════════════════════════════════ -# ProgressBar / SeekBar / AbsSeekBar / RatingBar -# ═══════════════════════════════════════════════════════════════ - -class ProgressBar(_BaseProgressBar): - """ProgressBar — 进度条扩展""" - - STYLE_HORIZONTAL = 0x00000001 - STYLE_SPINNER = 0x00000000 - - def __init__(self, context: Any = None, style: int = STYLE_SPINNER): - super().__init__() - self._min: int = 0 - self._max: int = 100 - self._progress: int = 0 - self._secondary_progress: int = 0 - self._indeterminate = (style == self.STYLE_SPINNER) - self._indeterminate_drawable: Optional[Any] = None - self._progress_drawable: Optional[Any] = None - self._on_progress_listener: Optional[Callable] = None - self._mirror = False - - def setMin(self, min_val: int) -> None: - self._min = min_val - if self._progress < min_val: - self._progress = min_val - - def getMin(self) -> int: - return self._min - - def setMax(self, max_val: int) -> None: - self._max = max_val - if self._progress > max_val: - self._progress = max_val - - def getMax(self) -> int: - return self._max - - def setProgress(self, progress: int) -> None: - self._progress = max(self._min, min(self._max, progress)) - if self._on_progress_listener: - self._on_progress_listener(self, self._progress) - - def getProgress(self) -> int: - return self._progress - - def setSecondaryProgress(self, progress: int) -> None: - self._secondary_progress = max(self._min, min(self._max, progress)) - - def getSecondaryProgress(self) -> int: - return self._secondary_progress - - def incrementProgressBy(self, diff: int) -> None: - self.setProgress(self._progress + diff) - - def incrementSecondaryProgressBy(self, diff: int) -> None: - self._secondary_progress = max(self._min, min(self._max, self._secondary_progress + diff)) - - def setIndeterminate(self, indeterminate: bool) -> None: - self._indeterminate = indeterminate - - def isIndeterminate(self) -> bool: - return self._indeterminate - - def setIndeterminateDrawable(self, drawable: Any) -> None: - self._indeterminate_drawable = drawable - - def getIndeterminateDrawable(self) -> Optional[Any]: - return self._indeterminate_drawable - - def setProgressDrawable(self, drawable: Any) -> None: - self._progress_drawable = drawable - - def getProgressDrawable(self) -> Optional[Any]: - return self._progress_drawable - - def setOnProgressBarChangeListener(self, listener: Optional[Callable]) -> None: - self._on_progress_listener = listener - - def setMirrorForRtl(self, mirror: bool) -> None: - self._mirror = mirror - - def getMirrorForRtl(self) -> bool: - return self._mirror - - def getProgressScale(self) -> float: - if self._max <= self._min: - return 0.0 - return (self._progress - self._min) / (self._max - self._min) - - def toString(self) -> str: - return (f"ProgressBar(min={self._min}, max={self._max}, " - f"progress={self._progress}, " - f"indeterminate={self._indeterminate})") - - -class AbsSeekBar(ProgressBar): - """AbsSeekBar — 抽象进度条""" - - def __init__(self, context: Any = None): - super().__init__(context, ProgressBar.STYLE_HORIZONTAL) - self._thumb: Optional[Any] = None - self._thumb_offset: int = 0 - self._key_progress_increment: int = 1 - self._is_dragging = False - self._on_seek_bar_change: Optional[Callable] = None - self._split_track: bool = False - self._tick_mark: Optional[Any] = None - - def setThumb(self, thumb: Any) -> None: - self._thumb = thumb - - def getThumb(self) -> Optional[Any]: - return self._thumb - - def setThumbOffset(self, offset: int) -> None: - self._thumb_offset = offset - - def getThumbOffset(self) -> int: - return self._thumb_offset - - def setKeyProgressIncrement(self, increment: int) -> None: - self._key_progress_increment = max(1, increment) - - def getKeyProgressIncrement(self) -> int: - return self._key_progress_increment - - def setSplitTrack(self, split: bool) -> None: - self._split_track = split - - def getSplitTrack(self) -> bool: - return self._split_track - - def setTickMark(self, mark: Any) -> None: - self._tick_mark = mark - - def getTickMark(self) -> Optional[Any]: - return self._tick_mark - - def setOnSeekBarChangeListener(self, listener: Optional[Callable]) -> None: - self._on_seek_bar_change = listener - - -class SeekBar(_BaseSeekBar, AbsSeekBar): - """SeekBar — 拖动条""" - - def __init__(self, context: Any = None): - AbsSeekBar.__init__(self, context) - self._on_seek_bar_change: Optional[Callable] = None - - def setOnSeekBarChangeListener(self, listener: Optional[Callable]) -> None: - self._on_seek_bar_change = listener - - def getOnSeekBarChangeListener(self) -> Optional[Callable]: - return self._on_seek_bar_change - - def toString(self) -> str: - return f"SeekBar(progress={self.getProgress()})" - - -class RatingBar(_BaseRatingBar, ProgressBar): - """RatingBar — 评分条""" - - def __init__(self, context: Any = None, num_stars: int = 5, - step_size: float = 0.5): - ProgressBar.__init__(self, context, ProgressBar.STYLE_HORIZONTAL) - self._num_stars = num_stars - self._step_size = step_size - self._rating: float = 0.0 - self._is_indicator = False - self._on_rating_change: Optional[Callable] = None - self.setMax(num_stars) - - def setNumStars(self, num: int) -> None: - self._num_stars = num - self.setMax(num) - - def getNumStars(self) -> int: - return self._num_stars - - def setRating(self, rating: float) -> None: - self._rating = max(0.0, min(float(self._num_stars), rating)) - self.setProgress(int(self._rating)) - - def getRating(self) -> float: - return self._rating - - def setStepSize(self, size: float) -> None: - self._step_size = max(0.1, size) - - def getStepSize(self) -> float: - return self._step_size - - def setIsIndicator(self, indicator: bool) -> None: - self._is_indicator = indicator - - def isIndicator(self) -> bool: - return self._is_indicator - - def setOnRatingBarChangeListener(self, listener: Optional[Callable]) -> None: - self._on_rating_change = listener - - def getOnRatingBarChangeListener(self) -> Optional[Callable]: - return self._on_rating_change - - def toString(self) -> str: - return f"RatingBar(rating={self._rating}/{self._num_stars})" - - -# ═══════════════════════════════════════════════════════════════ -# Chronometer -# ═══════════════════════════════════════════════════════════════ - -class Chronometer(_BaseChronometer): - """Chronometer — 计时器""" - - FORMAT_ELAPSED_TIME = 1 - FORMAT_SECONDS = 2 - FORMAT_MINUTES = 3 - - def __init__(self, context: Any = None): - super().__init__() - self._base: int = 0 - self._elapsed: int = 0 - self._running = False - self._format: str = "%H:%M:%S" - self._on_tick: Optional[Callable] = None - self._lock = threading.Lock() - - def setBase(self, base: int) -> None: - self._base = base - - def getBase(self) -> int: - return self._base - - def start(self) -> None: - with self._lock: - self._running = True - - def stop(self) -> None: - with self._lock: - self._running = False - self._elapsed = int(time.time() * 1000) - self._base - - def setFormat(self, format_str: str) -> None: - self._format = format_str - - def getFormat(self) -> str: - return self._format - - def setOnChronometerTickListener(self, listener: Optional[Callable]) -> None: - self._on_tick = listener - - def getOnChronometerTickListener(self) -> Optional[Callable]: - return self._on_tick - - def isRunning(self) -> bool: - return self._running - - def getElapsedTime(self) -> int: - if self._running: - return int(time.time() * 1000) - self._base - return self._elapsed - - def toString(self) -> str: - return f"Chronometer(running={self._running})" - - -# ═══════════════════════════════════════════════════════════════ -# CompoundButton / ToggleButton / RadioButton / RadioGroup -# ═══════════════════════════════════════════════════════════════ - -class CompoundButton(Stub): - """CompoundButton — 复合按钮""" - - def __init__(self, context: Any = None): - super().__init__() - self._checked = False - self._on_check_listener: Optional[Callable] = None - self._button_drawable: Optional[Any] = None - - def toggle(self) -> None: - self.setChecked(not self._checked) - - def setChecked(self, checked: bool) -> None: - if self._checked != checked: - self._checked = checked - if self._on_check_listener: - self._on_check_listener(self, checked) - - def isChecked(self) -> bool: - return self._checked - - def setOnCheckedChangeListener(self, listener: Optional[Callable]) -> None: - self._on_check_listener = listener - - def getOnCheckedChangeListener(self) -> Optional[Callable]: - return self._on_check_listener - - def setButtonDrawable(self, drawable: Any) -> None: - self._button_drawable = drawable - - def getButtonDrawable(self) -> Optional[Any]: - return self._button_drawable - - def toString(self) -> str: - return f"CompoundButton(checked={self._checked})" - - -class ToggleButton(_BaseToggleButton, CompoundButton): - """ToggleButton — 开关按钮""" - - def __init__(self, context: Any = None): - super().__init__(context) - CompoundButton.__init__(self, context) - self._text_on: str = "ON" - self._text_off: str = "OFF" - - def setTextOn(self, text: str) -> None: - self._text_on = text - - def getTextOn(self) -> str: - return self._text_on - - def setTextOff(self, text: str) -> None: - self._text_off = text - - def getTextOff(self) -> str: - return self._text_off - - def toString(self) -> str: - return f"ToggleButton(checked={self.isChecked()})" - - -class RadioButton(_BaseRadioButton, CompoundButton): - """RadioButton — 单选按钮""" - - def __init__(self, context: Any = None): - super().__init__(context) - CompoundButton.__init__(self, context) - - def toggle(self) -> None: - if not self.isChecked(): - super().toggle() - - def toString(self) -> str: - return f"RadioButton(checked={self.isChecked()})" - - -class RadioGroup(Stub): - """RadioGroup — 单选按钮组""" - - class LayoutParams(Stub): - """RadioGroup.LayoutParams — 单选按钮组布局参数""" - - def __init__(self, width: int = WRAP_CONTENT, height: int = WRAP_CONTENT): - super().__init__() - self.width = width - self.height = height - - def toString(self) -> str: - return f"RadioGroup.LayoutParams({self.width}x{self.height})" - - def __init__(self, context: Any = None): - super().__init__() - self._checked_id: int = NO_ID - self._on_check_listener: Optional[Callable] = None - self._children: List[RadioButton] = [] - self._protect_from_checked_change = False - - def setOnCheckedChangeListener(self, listener: Optional[Callable]) -> None: - self._on_check_listener = listener - - def getOnCheckedChangeListener(self) -> Optional[Callable]: - return self._on_check_listener - - def check(self, id_val: int) -> None: - self._checked_id = id_val - if self._on_check_listener: - self._on_check_listener(self, id_val) - - def getCheckedRadioButtonId(self) -> int: - return self._checked_id - - def clearCheck(self) -> None: - self._checked_id = NO_ID - - def addView(self, child: RadioButton, index: int = -1) -> None: - if index < 0 or index >= len(self._children): - self._children.append(child) - else: - self._children.insert(index, child) - - def getChildCount(self) -> int: - return len(self._children) - - def toString(self) -> str: - return f"RadioGroup(checkedId={self._checked_id})" - - -# ═══════════════════════════════════════════════════════════════ -# TableLayout / TableRow -# ═══════════════════════════════════════════════════════════════ - -class TableLayout(_BaseTableLayout): - """TableLayout — 表格布局""" - - def __init__(self, context: Any = None): - super().__init__() - self._rows: List[TableRow] = [] - self._columns_stretchable: Set[int] = set() - self._columns_collapsible: Set[int] = set() - self._shrink_allowed = False - self._stretch_allowed = False - - def addView(self, child: Any, index: int = -1) -> None: - pass - - def setColumnStretchable(self, column_index: int, stretchable: bool) -> None: - if stretchable: - self._columns_stretchable.add(column_index) - else: - self._columns_stretchable.discard(column_index) - - def setColumnCollapsible(self, column_index: int, collapsible: bool) -> None: - if collapsible: - self._columns_collapsible.add(column_index) - else: - self._columns_collapsible.discard(column_index) - - def setShrinkAllColumns(self, shrink: bool) -> None: - self._shrink_allowed = shrink - - def setStretchAllColumns(self, stretch: bool) -> None: - self._stretch_allowed = stretch - - def toString(self) -> str: - return f"TableLayout(rows={len(self._rows)})" - - -class TableRow(_BaseTableRow): - """TableRow — 表格行""" - - class LayoutParams(Stub): - """TableRow.LayoutParams — 行布局参数""" - - def __init__(self, width: int = MATCH_PARENT, height: int = WRAP_CONTENT): - super().__init__() - self.width = width - self.height = height - self.column: int = -1 - self.span: int = 1 - - def toString(self) -> str: - return f"TableRow.LayoutParams(col={self.column}, span={self.span})" - - def __init__(self, context: Any = None): - super().__init__() - self._children: List[Any] = [] - - def addView(self, child: Any, index: int = -1) -> None: - if index < 0 or index >= len(self._children): - self._children.append(child) - else: - self._children.insert(index, child) - - def getChildCount(self) -> int: - return len(self._children) - - def getVirtualChildAt(self, index: int) -> Optional[Any]: - if 0 <= index < len(self._children): - return self._children[index] - return None - - def toString(self) -> str: - return f"TableRow(children={len(self._children)})" - - -# ═══════════════════════════════════════════════════════════════ -# GridLayout -# ═══════════════════════════════════════════════════════════════ - -class GridLayout(Stub): - """GridLayout — 网格布局""" - - class Alignment(Stub): - """GridLayout.Alignment — 对齐方式""" - - def __init__(self, name: str = ""): - super().__init__() - self._name = name - - def toString(self) -> str: - return f"Alignment({self._name})" - - FILL = Alignment("FILL") - CENTER = Alignment("CENTER") - LEFT = Alignment("LEFT") - RIGHT = Alignment("RIGHT") - TOP = Alignment("TOP") - BOTTOM = Alignment("BOTTOM") - BASELINE = Alignment("BASELINE") - START = Alignment("START") - END = Alignment("END") - - ALIGN_BOUNDS = 0 - ALIGN_MARGINS = 1 - - HORIZONTAL = 0 - VERTICAL = 1 - - UNDEFINED = -2147483648 - - class Spec(Stub): - """GridLayout.Spec — 网格规格""" - - def __init__(self, start: int = -2147483648, span: int = 1, - alignment: Optional["GridLayout.Alignment"] = None): - super().__init__() - self.start = start - self.span = span - self.alignment = alignment - - def toString(self) -> str: - return f"Spec(start={self.start}, span={self.span})" - - class LayoutParams(Stub): - """GridLayout.LayoutParams — 网格布局参数""" - - def __init__(self): - super().__init__() - self.width = WRAP_CONTENT - self.height = WRAP_CONTENT - self.columnSpec = GridLayout.Spec(GridLayout.UNDEFINED, 1, GridLayout.FILL) - self.rowSpec = GridLayout.Spec(GridLayout.UNDEFINED, 1, GridLayout.FILL) - self.setMargins(0, 0, 0, 0) - self.leftMargin = 0 - self.topMargin = 0 - self.rightMargin = 0 - self.bottomMargin = 0 - - def setMargins(self, left: int, top: int, right: int, bottom: int) -> None: - self.leftMargin = left - self.topMargin = top - self.rightMargin = right - self.bottomMargin = bottom - - def setGravity(self, gravity: int) -> None: - pass - - def toString(self) -> str: - return (f"GridLayout.LayoutParams(col={self.columnSpec}, " - f"row={self.rowSpec})") - - @staticmethod - def spec(start: int = UNDEFINED, span: int = 1, - alignment: Optional[Alignment] = None) -> Spec: - return GridLayout.Spec(start, span, alignment) - - def __init__(self, context: Any = None): - super().__init__() - self._orientation = self.HORIZONTAL - self._column_count: int = 2 - self._row_count: int = 2 - self._alignment_mode = self.ALIGN_BOUNDS - self._use_default_margins = False - self._children: List[Any] = [] - - def setOrientation(self, orientation: int) -> None: - self._orientation = orientation - - def getOrientation(self) -> int: - return self._orientation - - def setColumnCount(self, count: int) -> None: - self._column_count = max(0, count) - - def getColumnCount(self) -> int: - return self._column_count - - def setRowCount(self, count: int) -> None: - self._row_count = max(0, count) - - def getRowCount(self) -> int: - return self._row_count - - def setAlignmentMode(self, mode: int) -> None: - self._alignment_mode = mode - - def getAlignmentMode(self) -> int: - return self._alignment_mode - - def setUseDefaultMargins(self, use: bool) -> None: - self._use_default_margins = use - - def getUseDefaultMargins(self) -> bool: - return self._use_default_margins - - def toString(self) -> str: - return f"GridLayout({self._column_count}x{self._row_count})" - - -# ═══════════════════════════════════════════════════════════════ -# LinearLayout / FrameLayout / RelativeLayout -# ═══════════════════════════════════════════════════════════════ - -class LinearLayout(_BaseLinearLayout): - """LinearLayout — 线性布局""" - - HORIZONTAL = 0 - VERTICAL = 1 - SHOW_DIVIDER_NONE = 0 - SHOW_DIVIDER_BEGINNING = 1 - SHOW_DIVIDER_MIDDLE = 2 - SHOW_DIVIDER_END = 4 - - class LayoutParams(Stub): - """LinearLayout.LayoutParams — 线性布局参数""" - - def __init__(self, width: int = MATCH_PARENT, height: int = WRAP_CONTENT, - weight: float = 0.0): - super().__init__() - self.width = width - self.height = height - self.weight = weight - self.gravity = -1 - self.leftMargin = 0 - self.topMargin = 0 - self.rightMargin = 0 - self.bottomMargin = 0 - - def setMargins(self, left: int, top: int, right: int, bottom: int) -> None: - self.leftMargin = left - self.topMargin = top - self.rightMargin = right - self.bottomMargin = bottom - - def toString(self) -> str: - return (f"LinearLayout.LayoutParams({self.width}x{self.height}, " - f"weight={self.weight})") - - def __init__(self, context: Any = None): - super().__init__() - self._orientation = self.HORIZONTAL - self._weight_sum: float = 0.0 - self._baseline_aligned = True - self._divider: Optional[Any] = None - self._divider_padding: int = 0 - self._show_dividers = self.SHOW_DIVIDER_NONE - self._gravity: int = -1 - - def setOrientation(self, orientation: int) -> None: - self._orientation = orientation - - def getOrientation(self) -> int: - return self._orientation - - def setWeightSum(self, weight_sum: float) -> None: - self._weight_sum = weight_sum - - def getWeightSum(self) -> float: - return self._weight_sum - - def setBaselineAligned(self, aligned: bool) -> None: - self._baseline_aligned = aligned - - def isBaselineAligned(self) -> bool: - return self._baseline_aligned - - def setDividerDrawable(self, divider: Any) -> None: - self._divider = divider - - def getDividerDrawable(self) -> Optional[Any]: - return self._divider - - def setDividerPadding(self, padding: int) -> None: - self._divider_padding = padding - - def getDividerPadding(self) -> int: - return self._divider_padding - - def setShowDividers(self, show: int) -> None: - self._show_dividers = show - - def getShowDividers(self) -> int: - return self._show_dividers - - def setGravity(self, gravity: int) -> None: - self._gravity = gravity - - def getGravity(self) -> int: - return self._gravity - - def toString(self) -> str: - return f"LinearLayout(orientation={'H' if self._orientation == 0 else 'V'})" - - -class FrameLayout(_BaseFrameLayout): - """FrameLayout — 帧布局""" - - class LayoutParams(Stub): - """FrameLayout.LayoutParams — 帧布局参数""" - - UNSPECIFIED_GRAVITY = -1 - - def __init__(self, width: int = MATCH_PARENT, height: int = MATCH_PARENT, - gravity: int = UNSPECIFIED_GRAVITY): - super().__init__() - self.width = width - self.height = height - self.gravity = gravity - self.leftMargin = 0 - self.topMargin = 0 - self.rightMargin = 0 - self.bottomMargin = 0 - - def setMargins(self, left: int, top: int, right: int, bottom: int) -> None: - self.leftMargin = left - self.topMargin = top - self.rightMargin = right - self.bottomMargin = bottom - - def toString(self) -> str: - return (f"FrameLayout.LayoutParams({self.width}x{self.height}, " - f"gravity={self.gravity})") - - def __init__(self, context: Any = None): - super().__init__() - self._foreground: Optional[Any] = None - self._foreground_gravity: int = 0 - self._measure_all_children = False - - def setForeground(self, drawable: Any) -> None: - self._foreground = drawable - - def getForeground(self) -> Optional[Any]: - return self._foreground - - def setForegroundGravity(self, gravity: int) -> None: - self._foreground_gravity = gravity - - def getForegroundGravity(self) -> int: - return self._foreground_gravity - - def setMeasureAllChildren(self, measure: bool) -> None: - self._measure_all_children = measure - - def getMeasureAllChildren(self) -> bool: - return self._measure_all_children - - def toString(self) -> str: - return "FrameLayout()" - - -class RelativeLayout(_BaseRelativeLayout): - """RelativeLayout — 相对布局""" - - TRUE = -1 - FALSE = -2 - - ALIGN_PARENT_LEFT = 1 - ALIGN_PARENT_TOP = 2 - ALIGN_PARENT_RIGHT = 4 - ALIGN_PARENT_BOTTOM = 8 - ALIGN_PARENT_START = 16 - ALIGN_PARENT_END = 32 - CENTER_IN_PARENT = 64 - CENTER_HORIZONTAL = 128 - CENTER_VERTICAL = 256 - ABOVE = 512 - BELOW = 1024 - LEFT_OF = 2048 - RIGHT_OF = 4096 - ALIGN_LEFT = 8192 - ALIGN_TOP = 16384 - ALIGN_RIGHT = 32768 - ALIGN_BOTTOM = 65536 - ALIGN_BASELINE = 131072 - ALIGN_START = 262144 - ALIGN_END = 524288 - START_OF = 1048576 - END_OF = 2097152 - LAYOUT_DIRECTION = 4194304 - - class LayoutParams(Stub): - """RelativeLayout.LayoutParams — 相对布局参数""" - - def __init__(self, width: int = WRAP_CONTENT, height: int = WRAP_CONTENT): - super().__init__() - self.width = width - self.height = height - self.leftMargin = 0 - self.topMargin = 0 - self.rightMargin = 0 - self.bottomMargin = 0 - self.marginStart = 0 - self.marginEnd = 0 - self._rules: Dict[int, int] = {} - self._align_with_parent = False - - def addRule(self, verb: int, subject: int = -1) -> None: - self._rules[verb] = subject - - def removeRule(self, verb: int) -> None: - self._rules.pop(verb, None) - - def getRule(self, verb: int) -> int: - return self._rules.get(verb, 0) - - def getRules(self) -> Dict[int, int]: - return dict(self._rules) - - def setMargins(self, left: int, top: int, right: int, bottom: int) -> None: - self.leftMargin = left - self.topMargin = top - self.rightMargin = right - self.bottomMargin = bottom - - def setMarginStart(self, start: int) -> None: - self.marginStart = start - - def setMarginEnd(self, end: int) -> None: - self.marginEnd = end - - def toString(self) -> str: - return (f"RelativeLayout.LayoutParams({self.width}x{self.height}, " - f"rules={len(self._rules)})") - - def __init__(self, context: Any = None): - super().__init__() - self._gravity: int = -1 - self._horizontal_gravity: int = -1 - self._vertical_gravity: int = -1 - self._ignore_gravity = False - - def setGravity(self, gravity: int) -> None: - self._gravity = gravity - - def getGravity(self) -> int: - return self._gravity - - def setHorizontalGravity(self, gravity: int) -> None: - self._horizontal_gravity = gravity - - def setVerticalGravity(self, gravity: int) -> None: - self._vertical_gravity = gravity - - def setIgnoreGravity(self, view_id: int) -> None: - self._ignore_gravity = True - - def getIgnoreGravity(self) -> int: - return 0 - - def toString(self) -> str: - return "RelativeLayout()" - - -# ═══════════════════════════════════════════════════════════════ -# NumberPicker / SearchView / TabHost / TabWidget -# ═══════════════════════════════════════════════════════════════ - -class NumberPicker(_BaseNumberPicker): - """NumberPicker — 数字选择器""" - - def __init__(self, context: Any = None): - super().__init__() - self._min_value: int = 0 - self._max_value: int = 100 - self._value: int = 0 - self._wrap_selector_wheel = True - self._on_value_change: Optional[Callable] = None - self._on_scroll: Optional[Callable] = None - self._displayed_values: Optional[List[str]] = None - self._descendant_focus = False - - def setMinValue(self, value: int) -> None: - self._min_value = value - if self._value < value: - self._value = value - - def getMinValue(self) -> int: - return self._min_value - - def setMaxValue(self, value: int) -> None: - self._max_value = value - if self._value > value: - self._value = value - - def getMaxValue(self) -> int: - return self._max_value - - def setValue(self, value: int) -> None: - self._value = max(self._min_value, min(self._max_value, value)) - if self._on_value_change: - self._on_value_change(self, self._value, self._value) - - def getValue(self) -> int: - return self._value - - def setWrapSelectorWheel(self, wrap: bool) -> None: - self._wrap_selector_wheel = wrap - - def getWrapSelectorWheel(self) -> bool: - return self._wrap_selector_wheel - - def setDisplayedValues(self, values: Optional[List[str]]) -> None: - self._displayed_values = values - - def getDisplayedValues(self) -> Optional[List[str]]: - return self._displayed_values - - def setOnValueChangedListener(self, listener: Optional[Callable]) -> None: - self._on_value_change = listener - - def setOnScrollListener(self, listener: Optional[Callable]) -> None: - self._on_scroll = listener - - def toString(self) -> str: - return f"NumberPicker(value={self._value}, range=[{self._min_value}-{self._max_value}])" - - -class SearchView(_BaseSearchView): - """SearchView — 搜索视图扩展""" - - ICONIFIED = 0 - EXPANDED = 1 - COLLAPSED = 2 - - def __init__(self, context: Any = None): - super().__init__() - self._query: str = "" - self._query_hint: Optional[str] = None - self._iconified = True - self._iconified_by_default = True - self._submit_button_enabled = True - self._query_refinement_enabled = False - self._searchable_info: Optional[Any] = None - self._on_query: Optional[Callable] = None - self._on_query_text: Optional[Callable] = None - self._on_close: Optional[Callable] = None - self._on_suggestion: Optional[Callable] = None - - def setQuery(self, query: str, submit: bool = False) -> None: - self._query = query - if submit and self._on_query: - self._on_query(query) - - def getQuery(self) -> str: - return self._query - - def setQueryHint(self, hint: Optional[str]) -> None: - self._query_hint = hint - - def getQueryHint(self) -> Optional[str]: - return self._query_hint - - def setIconified(self, iconified: bool) -> None: - self._iconified = iconified - - def isIconified(self) -> bool: - return self._iconified - - def setIconifiedByDefault(self, iconified: bool) -> None: - self._iconified_by_default = iconified - - def isIconifiedByDefault(self) -> bool: - return self._iconified_by_default - - def setSubmitButtonEnabled(self, enabled: bool) -> None: - self._submit_button_enabled = enabled - - def isSubmitButtonEnabled(self) -> bool: - return self._submit_button_enabled - - def setQueryRefinementEnabled(self, enabled: bool) -> None: - self._query_refinement_enabled = enabled - - def isQueryRefinementEnabled(self) -> bool: - return self._query_refinement_enabled - - def setOnQueryTextListener(self, listener: Optional[Callable]) -> None: - self._on_query_text = listener - - def setOnCloseListener(self, listener: Optional[Callable]) -> None: - self._on_close = listener - - def setOnSuggestionListener(self, listener: Optional[Callable]) -> None: - self._on_suggestion = listener - - def setSearchableInfo(self, info: Any) -> None: - self._searchable_info = info - - def onActionViewExpanded(self) -> None: - self._iconified = False - - def onActionViewCollapsed(self) -> None: - self._iconified = True - - def toString(self) -> str: - return f"SearchView(query={self._query})" - - -class TabHost(_BaseTabHost): - """TabHost — 标签宿主""" - - class TabSpec(Stub): - """TabHost.TabSpec — 标签规格""" - - def __init__(self, tag: str = ""): - super().__init__() - self._tag = tag - self._indicator: Optional[Any] = None - self._content: Optional[Any] = None - - def setIndicator(self, indicator: Any) -> "TabHost.TabSpec": - self._indicator = indicator - return self - - def setContent(self, content: Any) -> "TabHost.TabSpec": - self._content = content - return self - - def getTag(self) -> str: - return self._tag - - def toString(self) -> str: - return f"TabSpec(tag={self._tag})" - - def __init__(self, context: Any = None): - super().__init__() - self._tabs: List[TabHost.TabSpec] = [] - self._current_tab: int = 0 - - def newTabSpec(self, tag: str) -> TabSpec: - return TabHost.TabSpec(tag) - - def addTab(self, spec: TabSpec) -> None: - self._tabs.append(spec) - - def setCurrentTab(self, index: int) -> None: - if 0 <= index < len(self._tabs): - self._current_tab = index - - def setCurrentTabByTag(self, tag: str) -> None: - for i, spec in enumerate(self._tabs): - if spec.getTag() == tag: - self._current_tab = i - break - - def getCurrentTab(self) -> int: - return self._current_tab - - def getCurrentTabTag(self) -> str: - if 0 <= self._current_tab < len(self._tabs): - return self._tabs[self._current_tab].getTag() - return "" - - def getTabWidget(self) -> Optional["TabWidget"]: - return None - - def getTabContentView(self) -> Optional[Any]: - return None - - def getTabCount(self) -> int: - return len(self._tabs) - - def toString(self) -> str: - return f"TabHost(tabs={len(self._tabs)}, current={self._current_tab})" - - -class TabWidget(_BaseTabWidget): - """TabWidget — 标签小部件""" - - def __init__(self, context: Any = None): - super().__init__() - self._tabs: List[Any] = [] - self._current_tab: int = 0 - self._strip_enabled = False - self._strip_left: int = 0 - self._strip_top: int = 0 - self._strip_right: int = 0 - self._strip_bottom: int = 0 - - def setCurrentTab(self, index: int) -> None: - self._current_tab = max(0, min(index, len(self._tabs) - 1)) - - def getCurrentTab(self) -> int: - return self._current_tab - - def setStripEnabled(self, enabled: bool) -> None: - self._strip_enabled = enabled - - def isStripEnabled(self) -> bool: - return self._strip_enabled - - def getTabCount(self) -> int: - return len(self._tabs) - - def toString(self) -> str: - return f"TabWidget(tabs={len(self._tabs)})" - - -# ═══════════════════════════════════════════════════════════════ -# TextSwitcher / ViewSwitcher / ViewAnimator / ImageSwitcher -# ═══════════════════════════════════════════════════════════════ - -class ViewAnimator(_BaseViewAnimator): - """ViewAnimator — 视图动画器""" - - def __init__(self, context: Any = None): - super().__init__() - self._children: List[Any] = [] - self._displayed_child: int = 0 - self._in_animation: Optional[Any] = None - self._out_animation: Optional[Any] = None - self._animate_first_view = False - - def addView(self, child: Any) -> None: - self._children.append(child) - - def removeView(self, child: Any) -> None: - if child in self._children: - self._children.remove(child) - - def removeAllViews(self) -> None: - self._children.clear() - - def getChildCount(self) -> int: - return len(self._children) - - def getCurrentView(self) -> Optional[Any]: - if 0 <= self._displayed_child < len(self._children): - return self._children[self._displayed_child] - return None - - def getDisplayedChild(self) -> int: - return self._displayed_child - - def setDisplayedChild(self, index: int) -> None: - if 0 <= index < len(self._children): - self._displayed_child = index - - def showNext(self) -> None: - if self._children: - self._displayed_child = (self._displayed_child + 1) % len(self._children) - - def showPrevious(self) -> None: - if self._children: - self._displayed_child = (self._displayed_child - 1) % len(self._children) - - def setInAnimation(self, animation: Any) -> None: - self._in_animation = animation - - def getInAnimation(self) -> Optional[Any]: - return self._in_animation - - def setOutAnimation(self, animation: Any) -> None: - self._out_animation = animation - - def getOutAnimation(self) -> Optional[Any]: - return self._out_animation - - def setAnimateFirstView(self, animate: bool) -> None: - self._animate_first_view = animate - - def getAnimateFirstView(self) -> bool: - return self._animate_first_view - - def toString(self) -> str: - return f"ViewAnimator(children={len(self._children)}, displayed={self._displayed_child})" - - -class ViewSwitcher(_BaseViewSwitcher, ViewAnimator): - """ViewSwitcher — 视图切换器""" - - class ViewFactory(Stub): - """ViewSwitcher.ViewFactory — 视图工厂""" - def makeView(self) -> Any: - return None - - def __init__(self, context: Any = None): - super().__init__(context) - ViewAnimator.__init__(self, context) - self._factory: Optional[ViewSwitcher.ViewFactory] = None - - def setFactory(self, factory: ViewFactory) -> None: - self._factory = factory - - def getNextView(self) -> Optional[Any]: - next_idx = (self.getDisplayedChild() + 1) % max(1, self.getChildCount()) - if 0 <= next_idx < self.getChildCount(): - return None - return None - - def reset(self) -> None: - self.removeAllViews() - - def toString(self) -> str: - return f"ViewSwitcher()" - - -class TextSwitcher(_BaseTextSwitcher, ViewSwitcher): - """TextSwitcher — 文本切换器""" - - def __init__(self, context: Any = None): - super().__init__(context) - ViewSwitcher.__init__(self, context) - self._factory = None - - def setCurrentText(self, text: str) -> None: - pass - - def getCurrentText(self) -> str: - return "" - - def toString(self) -> str: - return "TextSwitcher()" - - -class ImageSwitcher(_BaseImageSwitcher, ViewSwitcher): - """ImageSwitcher — 图片切换器""" - - def __init__(self, context: Any = None): - super().__init__(context) - ViewSwitcher.__init__(self, context) - - def setImageResource(self, res_id: int) -> None: - pass - - def setImageDrawable(self, drawable: Any) -> None: - pass - - def setImageURI(self, uri: Any) -> None: - pass - - def toString(self) -> str: - return "ImageSwitcher()" - - -class ImageButton(_BaseImageButton): - """ImageButton — 图片按钮""" - - def __init__(self, context: Any = None): - super().__init__() - self._adjust_view_bounds = False - self._scale_type: Optional[Any] = None - - def setAdjustViewBounds(self, adjust: bool) -> None: - self._adjust_view_bounds = adjust - - def getAdjustViewBounds(self) -> bool: - return self._adjust_view_bounds - - def setScaleType(self, scale_type: Any) -> None: - self._scale_type = scale_type - - def getScaleType(self) -> Optional[Any]: - return self._scale_type - - def toString(self) -> str: - return "ImageButton()" - - -class ZoomButton(ImageButton): - """ZoomButton — 缩放按钮""" - - def __init__(self, context: Any = None): - super().__init__(context) - self._zoom_in = False - self._zoom_speed: int = 300 - self._on_click: Optional[Callable] = None - - def setZoomIn(self, zoom_in: bool) -> None: - self._zoom_in = zoom_in - - def isZoomIn(self) -> bool: - return self._zoom_in - - def setZoomSpeed(self, speed: int) -> None: - self._zoom_speed = speed - - def getZoomSpeed(self) -> int: - return self._zoom_speed - - def setOnZoomListener(self, listener: Optional[Callable]) -> None: - self._on_click = listener - - def toString(self) -> str: - return f"ZoomButton(zoomIn={self._zoom_in})" - - -class ZoomControls(_BaseZoomControls): - """ZoomControls — 缩放控件""" - - def __init__(self, context: Any = None): - super().__init__() - self._zoom_in_enabled = True - self._zoom_out_enabled = True - self._on_zoom_in: Optional[Callable] = None - self._on_zoom_out: Optional[Callable] = None - self._show_zoom_controls = True - - def setOnZoomInClickListener(self, listener: Optional[Callable]) -> None: - self._on_zoom_in = listener - - def setOnZoomOutClickListener(self, listener: Optional[Callable]) -> None: - self._on_zoom_out = listener - - def setZoomInEnabled(self, enabled: bool) -> None: - self._zoom_in_enabled = enabled - - def setZoomOutEnabled(self, enabled: bool) -> None: - self._zoom_out_enabled = enabled - - def show(self) -> None: - self._show_zoom_controls = True - - def hide(self) -> None: - self._show_zoom_controls = False - - def setIsZoomInEnabled(self, enabled: bool) -> None: - self._zoom_in_enabled = enabled - - def setIsZoomOutEnabled(self, enabled: bool) -> None: - self._zoom_out_enabled = enabled - - def hasFocus(self) -> bool: - return False - - def toString(self) -> str: - return "ZoomControls()" - - -# ═══════════════════════════════════════════════════════════════ -# DigitalClock / TextClock / AnalogClock -# ═══════════════════════════════════════════════════════════════ - -class DigitalClock(_BaseDigitalClock): - """DigitalClock — 数字时钟""" - - def __init__(self, context: Any = None): - super().__init__() - self._format: str = "%H:%M:%S" - self._on_tick: Optional[Callable] = None - - def setFormat(self, format_str: str) -> None: - self._format = format_str - - def getFormat(self) -> str: - return self._format - - def getTime(self) -> str: - import datetime - return datetime.datetime.now().strftime(self._format) - - def setOnTickListener(self, listener: Optional[Callable]) -> None: - self._on_tick = listener - - def toString(self) -> str: - return f"DigitalClock(time={self.getTime()})" - - -class TextClock(Stub): - """TextClock — 文本时钟""" - - def __init__(self, context: Any = None): - super().__init__() - self._format_12_hour: str = "hh:mm:ss a" - self._format_24_hour: str = "HH:mm:ss" - self._time_zone: Optional[str] = None - - def setFormat12Hour(self, format_str: str) -> None: - self._format_12_hour = format_str - - def getFormat12Hour(self) -> str: - return self._format_12_hour - - def setFormat24Hour(self, format_str: str) -> None: - self._format_24_hour = format_str - - def getFormat24Hour(self) -> str: - return self._format_24_hour - - def setTimeZone(self, tz: str) -> None: - self._time_zone = tz - - def getTimeZone(self) -> Optional[str]: - return self._time_zone - - def is24HourModeEnabled(self) -> bool: - return True - - def getText(self) -> str: - import datetime - if self._time_zone: - import pytz - now = datetime.datetime.now(pytz.timezone(self._time_zone)) - else: - now = datetime.datetime.now() - return now.strftime(self._format_24_hour) - - def toString(self) -> str: - return f"TextClock(time={self.getText()})" - - -class AnalogClock(_BaseAnalogClock): - """AnalogClock — 模拟时钟""" - - def __init__(self, context: Any = None): - super().__init__() - self._dial: Optional[Any] = None - self._hour_hand: Optional[Any] = None - self._minute_hand: Optional[Any] = None - self._second_hand: Optional[Any] = None - - def toString(self) -> str: - return "AnalogClock()" - - -# ═══════════════════════════════════════════════════════════════ -# QuickContactBadge -# ═══════════════════════════════════════════════════════════════ - -class QuickContactBadge(Stub): - """QuickContactBadge — 快速联系人徽章""" - - MODE_SMALL = 0 - MODE_MEDIUM = 1 - MODE_LARGE = 2 - - def __init__(self, context: Any = None): - super().__init__() - self._contact_uri: Optional[str] = None - self._mode = self.MODE_MEDIUM - self._on_click: Optional[Callable] = None - - def assignContactUri(self, uri: str) -> None: - self._contact_uri = uri - - def assignContactFromEmail(self, email: str, lazy: bool = False) -> None: - self._contact_uri = f"mailto:{email}" - - def assignContactFromPhone(self, phone: str, lazy: bool = False) -> None: - self._contact_uri = f"tel:{phone}" - - def setMode(self, mode: int) -> None: - self._mode = mode - - def getMode(self) -> int: - return self._mode - - def setOnClickListener(self, listener: Optional[Callable]) -> None: - self._on_click = listener - - def toString(self) -> str: - return f"QuickContactBadge(contact={self._contact_uri})" - - -# ═══════════════════════════════════════════════════════════════ -# 各种 Adapter 实现 -# ═══════════════════════════════════════════════════════════════ - -class BaseAdapter(_BaseBaseAdapter, Adapter): - """BaseAdapter — 基础适配器""" - - def __init__(self): - super().__init__() - self._data_set_observers: List[Callable] = [] - - def registerDataSetObserver(self, observer: Callable) -> None: - if observer not in self._data_set_observers: - self._data_set_observers.append(observer) - - def unregisterDataSetObserver(self, observer: Callable) -> None: - if observer in self._data_set_observers: - self._data_set_observers.remove(observer) - - def notifyDataSetChanged(self) -> None: - for obs in list(self._data_set_observers): - obs() - - def notifyDataSetInvalidated(self) -> None: - self.notifyDataSetChanged() - - def areAllItemsEnabled(self) -> bool: - return True - - def isEnabled(self, position: int) -> bool: - return True - - def getItemViewType(self, position: int) -> int: - return Adapter.IGNORE_ITEM_VIEW_TYPE - - def getViewTypeCount(self) -> int: - return 1 - - def hasStableIds(self) -> bool: - return False - - def isEmpty(self) -> bool: - return self.getCount() == 0 - - -class ArrayAdapter(_BaseArrayAdapter, BaseAdapter): - """ArrayAdapter — 数组适配器""" - - def __init__(self, context: Any = None, resource: int = 0, - objects: Optional[List[Any]] = None): - BaseAdapter.__init__(self) - self._context = context - self._resource = resource - self._drop_down_resource: int = resource - self._objects: List[Any] = list(objects) if objects else [] - self._lock = threading.Lock() - - def add(self, obj: Any) -> None: - with self._lock: - self._objects.append(obj) - self.notifyDataSetChanged() - - def addAll(self, collection: List[Any]) -> None: - with self._lock: - self._objects.extend(collection) - self.notifyDataSetChanged() - - def insert(self, obj: Any, index: int) -> None: - with self._lock: - self._objects.insert(index, obj) - self.notifyDataSetChanged() - - def remove(self, obj: Any) -> None: - with self._lock: - if obj in self._objects: - self._objects.remove(obj) - self.notifyDataSetChanged() - - def clear(self) -> None: - with self._lock: - self._objects.clear() - self.notifyDataSetChanged() - - def sort(self, comparator: Callable) -> None: - with self._lock: - self._objects.sort(key=comparator) - self.notifyDataSetChanged() - - def getCount(self) -> int: - return len(self._objects) - - def getItem(self, position: int) -> Optional[Any]: - if 0 <= position < len(self._objects): - return self._objects[position] - return None - - def getPosition(self, item: Any) -> int: - try: - return self._objects.index(item) - except ValueError: - return -1 - - def setDropDownViewResource(self, resource: int) -> None: - self._drop_down_resource = resource - - def getDropDownViewResource(self) -> int: - return self._drop_down_resource - - def toString(self) -> str: - return f"ArrayAdapter(items={len(self._objects)})" - - -class SimpleAdapter(_BaseSimpleAdapter, BaseAdapter): - """SimpleAdapter — 简单适配器""" - - def __init__(self, context: Any = None, - data: Optional[List[Dict[str, Any]]] = None, - resource: int = 0, - from_keys: Optional[List[str]] = None, - to_ids: Optional[List[int]] = None): - BaseAdapter.__init__(self) - self._context = context - self._data: List[Dict[str, Any]] = data or [] - self._resource = resource - self._from_keys: List[str] = list(from_keys) if from_keys else [] - self._to_ids: List[int] = list(to_ids) if to_ids else [] - self._drop_down_resource: int = resource - self._view_binder: Optional[Callable] = None - - def getCount(self) -> int: - return len(self._data) - - def getItem(self, position: int) -> Optional[Dict[str, Any]]: - if 0 <= position < len(self._data): - return self._data[position] - return None - - def getViewBinder(self) -> Optional[Callable]: - return self._view_binder - - def setViewBinder(self, binder: Optional[Callable]) -> None: - self._view_binder = binder - - def setDropDownViewResource(self, resource: int) -> None: - self._drop_down_resource = resource - - def getDropDownViewResource(self) -> int: - return self._drop_down_resource - - def toString(self) -> str: - return f"SimpleAdapter(items={len(self._data)})" - - -class CursorAdapter(_BaseCursorAdapter, BaseAdapter): - """CursorAdapter — 游标适配器""" - - FLAG_AUTO_REQUERY = 1 - FLAG_REGISTER_CONTENT_OBSERVER = 2 - - def __init__(self, context: Any = None, cursor: Any = None, - flags: int = FLAG_AUTO_REQUERY): - BaseAdapter.__init__(self) - self._context = context - self._cursor = cursor - self._flags = flags - self._row_id_column: str = "_id" - self._lock = threading.Lock() - - def getCursor(self) -> Optional[Any]: - return self._cursor - - def setCursor(self, cursor: Any) -> None: - self._cursor = cursor - self.notifyDataSetChanged() - - def getCount(self) -> int: - return 0 - - def getItem(self, position: int) -> Optional[Any]: - return None - - def getItemId(self, position: int) -> int: - return position - - def changeCursor(self, cursor: Any) -> Optional[Any]: - old = self._cursor - self._cursor = cursor - self.notifyDataSetChanged() - return old - - def swapCursor(self, cursor: Any) -> Optional[Any]: - old = self._cursor - self._cursor = cursor - self.notifyDataSetChanged() - return old - - def toString(self) -> str: - return f"CursorAdapter(cursor={'yes' if self._cursor else 'no'})" - - -class ResourceCursorAdapter(CursorAdapter): - """ResourceCursorAdapter — 资源游标适配器""" - - def __init__(self, context: Any = None, layout: int = 0, - cursor: Any = None, flags: int = CursorAdapter.FLAG_AUTO_REQUERY): - super().__init__(context, cursor, flags) - self._layout = layout - self._drop_down_layout: int = layout - - def setDropDownViewResource(self, resource: int) -> None: - self._drop_down_layout = resource - - def getDropDownViewResource(self) -> int: - return self._drop_down_layout - - def toString(self) -> str: - return "ResourceCursorAdapter()" - - -class SimpleCursorAdapter(ResourceCursorAdapter): - """SimpleCursorAdapter — 简单游标适配器""" - - def __init__(self, context: Any = None, layout: int = 0, - cursor: Any = None, from_columns: Optional[List[str]] = None, - to_views: Optional[List[int]] = None, - flags: int = CursorAdapter.FLAG_AUTO_REQUERY): - super().__init__(context, layout, cursor, flags) - self._from_columns: List[str] = list(from_columns) if from_columns else [] - self._to_views: List[int] = list(to_views) if to_views else [] - self._view_binder: Optional[Callable] = None - self._cursor_to_column_map: Optional[Dict[str, int]] = None - - def getViewBinder(self) -> Optional[Callable]: - return self._view_binder - - def setViewBinder(self, binder: Optional[Callable]) -> None: - self._view_binder = binder - - def setCursorToStringConverter(self, converter: Optional[Callable]) -> None: - pass - - def toString(self) -> str: - return "SimpleCursorAdapter()" - - -class HeaderViewListAdapter(Stub): - """HeaderViewListAdapter — 头部视图列表适配器包装""" - - def __init__(self, wrapped_adapter: Optional[Adapter] = None): - super().__init__() - self._wrapped: Optional[Adapter] = wrapped_adapter - self._headers: List[Any] = [] - self._footers: List[Any] = [] - - def addHeaderView(self, view: Any, data: Any = None, is_selectable: bool = True) -> None: - self._headers.append(view) - - def addFooterView(self, view: Any, data: Any = None, is_selectable: bool = True) -> None: - self._footers.append(view) - - def removeHeaderView(self, view: Any) -> bool: - if view in self._headers: - self._headers.remove(view) - return True - return False - - def removeFooterView(self, view: Any) -> bool: - if view in self._footers: - self._footers.remove(view) - return True - return False - - def getCount(self) -> int: - base = self._wrapped.getCount() if self._wrapped else 0 - return base + len(self._headers) + len(self._footers) - - def getWrappedAdapter(self) -> Optional[Adapter]: - return self._wrapped - - def getHeadersCount(self) -> int: - return len(self._headers) - - def getFootersCount(self) -> int: - return len(self._footers) - - def isEmpty(self) -> bool: - return self.getCount() == 0 - - def toString(self) -> str: - return (f"HeaderViewListAdapter(headers={len(self._headers)}, " - f"footers={len(self._footers)})") - - -class WrapperListAdapter(Adapter): - """WrapperListAdapter — 包装列表适配器""" - - def __init__(self, wrapped: Optional[Adapter] = None): - super().__init__() - self._wrapped = wrapped - - def getWrappedAdapter(self) -> Optional[Adapter]: - return self._wrapped - - def getCount(self) -> int: - return self._wrapped.getCount() if self._wrapped else 0 - - def getItem(self, position: int) -> Optional[Any]: - return self._wrapped.getItem(position) if self._wrapped else None - - def getItemId(self, position: int) -> int: - return self._wrapped.getItemId(position) if self._wrapped else position - - def getView(self, position: int, convert_view: Optional[Any], - parent: Any) -> Optional[Any]: - return self._wrapped.getView(position, convert_view, parent) if self._wrapped else None - - def toString(self) -> str: - return "WrapperListAdapter()" \ No newline at end of file diff --git a/android_pyapi/dalvik/__init__.py b/android_pyapi/dalvik/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/android_pyapi/dalvik/system.py b/android_pyapi/dalvik/system.py deleted file mode 100644 index 009c3b6..0000000 --- a/android_pyapi/dalvik/system.py +++ /dev/null @@ -1,721 +0,0 @@ -""" -Android dalvik.system APIs implemented in Python. -dalvik.system package - DexClassLoader, PathClassLoader, DexFile, VMRuntime, VMDebug, etc. -Desktop stub implementation. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -import sys as _sys -import os as _os -from typing import List, Optional - -__all__ = [ - "DexClassLoader", "PathClassLoader", "BaseDexClassLoader", - "DexFile", "InMemoryDexClassLoader", - "VMRuntime", "VMDebug", - "CloseGuard", "PotentialDeadlockError", - "BlockGuard", -] - - -# ============================================================ -# BaseDexClassLoader -# ============================================================ - -class BaseDexClassLoader(Stub): - """ - dalvik.system.BaseDexClassLoader - A class loader that loads classes from DEX files. - """ - - def __init__(self, dex_path: str = "", optimized_dir: str = None, - library_search_path: str = None, parent: Any = None): - super().__init__() - self._dex_path = dex_path - self._optimized_dir = optimized_dir - self._library_search_path = library_search_path - self._parent = parent or type.__subclasses__ - self._loaded_classes = set() - self._assertion_status = {} - self._default_assertion_status = False - - def loadClass(self, name: str, resolve: bool = True) -> Optional[type]: - """Loads a class by name.""" - # Try to find the class in the Python path - parts = name.split(".") - module_name = ".".join(parts[:-1]) - class_name = parts[-1] - try: - if module_name: - module = __import__(module_name, fromlist=[class_name]) - cls = getattr(module, class_name, None) - else: - cls = globals().get(class_name) or __builtins__.get(class_name) - if cls: - self._loaded_classes.add(name) - return cls - return None - except (ImportError, AttributeError): - return None - - def findClass(self, name: str) -> Optional[type]: - """Finds a class by name.""" - return self.loadClass(name) - - def findResource(self, name: str) -> Optional[str]: - """Finds a resource by name.""" - path = _os.path.join(self._dex_path or "", name) - if _os.path.exists(path): - return path - return None - - def findResources(self, name: str) -> List[str]: - """Finds all resources by name.""" - result = [] - r = self.findResource(name) - if r: - result.append(r) - return result - - def getParent(self): - return self._parent - - def getPackage(self, name: str): - """Gets a package by name.""" - return None - - def setClassAssertionStatus(self, class_name: str, enabled: bool): - self._assertion_status[class_name] = enabled - - def setDefaultAssertionStatus(self, enabled: bool): - self._default_assertion_status = enabled - - def setPackageAssertionStatus(self, package_name: str, enabled: bool): - pass - - def clearAssertionStatus(self): - self._assertion_status = {} - self._default_assertion_status = False - - def toString(self) -> str: - return f"BaseDexClassLoader@{hex(id(self))}[dex={self._dex_path}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# DexClassLoader -# ============================================================ - -class DexClassLoader(BaseDexClassLoader): - """ - dalvik.system.DexClassLoader - A class loader that loads classes from .jar and .apk files. - """ - - def __init__(self, dex_path: str = "", optimized_dir: str = None, - library_search_path: str = None, parent: Any = None): - super().__init__(dex_path, optimized_dir, library_search_path, parent) - - def loadClass(self, name: str, resolve: bool = True) -> Optional[type]: - return super().loadClass(name, resolve) - - def findClass(self, name: str) -> Optional[type]: - return super().findClass(name) - - def getParent(self): - return super().getParent() - - def getResourceAsStream(self, name: str): - """Returns an input stream for reading a resource.""" - path = self.findResource(name) - if path: - return open(path, 'rb') - return None - - def getResources(self, name: str): - """Returns an enumeration of URLs for a resource.""" - return iter(self.findResources(name)) - - def toString(self) -> str: - return f"DexClassLoader@{hex(id(self))}[dex={self._dex_path}]" - - -# ============================================================ -# PathClassLoader -# ============================================================ - -class PathClassLoader(BaseDexClassLoader): - """ - dalvik.system.PathClassLoader - A class loader for loading classes from the file system. - """ - - def __init__(self, dex_path: str = "", parent: Any = None, - library_search_path: str = None): - super().__init__(dex_path, None, library_search_path, parent) - - def loadClass(self, name: str, resolve: bool = True) -> Optional[type]: - return super().loadClass(name, resolve) - - def findClass(self, name: str) -> Optional[type]: - return super().findClass(name) - - def getParent(self): - return super().getParent() - - def toString(self) -> str: - return f"PathClassLoader@{hex(id(self))}[dex={self._dex_path}]" - - -# ============================================================ -# InMemoryDexClassLoader -# ============================================================ - -class InMemoryDexClassLoader(BaseDexClassLoader): - """ - dalvik.system.InMemoryDexClassLoader - A class loader that loads classes from in-memory DEX buffers. - """ - - def __init__(self, dex_buffers: List, parent: Any = None): - super().__init__("", None, None, parent) - self._dex_buffers = dex_buffers - - def loadClass(self, name: str, resolve: bool = True) -> Optional[type]: - return super().loadClass(name, resolve) - - def findClass(self, name: str) -> Optional[type]: - return super().findClass(name) - - def toString(self) -> str: - return f"InMemoryDexClassLoader@{hex(id(self))}[buffers={len(self._dex_buffers)}]" - - -# ============================================================ -# DexFile -# ============================================================ - -class DexFile(Stub): - """ - dalvik.system.DexFile - Represents a single DEX file. - """ - - def __init__(self, file_name: str = None): - super().__init__() - self._file_name = file_name - self._closed = False - - @staticmethod - def loadDex(source_path: str, output_path: str, flags: int = 0) -> "DexFile": - return DexFile(source_path) - - def loadClass(self, name: str, loader: Any = None) -> Optional[type]: - """Loads a class from this DEX file.""" - parts = name.split(".") - try: - module = __import__(".".join(parts[:-1]), fromlist=[parts[-1]]) - return getattr(module, parts[-1], None) - except (ImportError, AttributeError): - return None - - def loadClassBinaryName(self, name: str, loader: Any = None) -> Optional[type]: - """Loads a class using binary (internal) name format.""" - java_name = name.replace("/", ".") - return self.loadClass(java_name, loader) - - def getName(self) -> str: - return self._file_name or "" - - def close(self): - self._closed = True - - def entries(self): - """Returns an enumeration of class names.""" - return iter([]) - - def getEntryCount(self) -> int: - return 0 - - @staticmethod - def isDexOptNeeded(source_path: str) -> bool: - return False - - def toString(self) -> str: - return f"DexFile@{hex(id(self))}[file={self._file_name}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# VMRuntime -# ============================================================ - -class VMRuntime(Stub): - """ - dalvik.system.VMRuntime - Provides access to Dalvik/ART VM runtime features. - """ - - _runtime = None - - def __init__(self): - super().__init__() - self._target_sdk_version = 0 - self._jit_compiler_filter = "speed" - self._large_heap_class = None - self._large_heap_size = 0 - self._native_allocation_count = 0 - self._disabled_compat_changes = [] - - @staticmethod - def getRuntime() -> "VMRuntime": - if VMRuntime._runtime is None: - VMRuntime._runtime = VMRuntime() - return VMRuntime._runtime - - def is64Bit(self) -> bool: - import struct - return struct.calcsize("P") == 8 - - def setTargetSdkVersion(self, version: int): - self._target_sdk_version = version - - def getTargetSdkVersion(self) -> int: - return self._target_sdk_version - - def startJitCompilation(self): - pass - - def endJitCompilation(self): - pass - - def concurrentGC(self, enable: bool): - pass - - def enableGcLogging(self, enable: bool): - pass - - def vmInstructionSet(self) -> str: - import platform - machine = platform.machine() - if machine in ("x86_64", "amd64"): - return "x86_64" - elif machine in ("i386", "i686"): - return "x86" - elif machine in ("aarch64", "arm64"): - return "arm64" - elif machine.startswith("arm"): - return "arm" - return machine - - def isVm64(self) -> bool: - return self.is64Bit() - - def getExprOpts(self) -> str: - return "" - - def setExprOpts(self, opts: str): - pass - - def registerAppInfo(self, app_name: str, app_data_dir: str): - pass - - def getDetachThreadTimeout(self) -> int: - return 0 - - def setDetachThreadTimeout(self, timeout_ms: int): - pass - - def setDisabledCompatChanges(self, changes: List[int]): - self._disabled_compat_changes = list(changes) - - def isBootClassPathOnDisk(self) -> bool: - return True - - def isJavaDebuggable(self) -> bool: - return False - - def getJitCompilerFilter(self) -> str: - return self._jit_compiler_filter - - def setJitCompilerFilter(self, filter_: str): - self._jit_compiler_filter = filter_ - - def getLargeHeapClass(self) -> str: - return self._large_heap_class - - def setLargeHeapClass(self, class_name: str): - self._large_heap_class = class_name - - def getLargeHeapSize(self) -> int: - return self._large_heap_size - - def setLargeHeapSize(self, size: int): - self._large_heap_size = size - - def registerNativeAllocation(self, bytes_: int): - self._native_allocation_count += bytes_ - - def registerNativeFree(self, bytes_: int): - self._native_allocation_count -= bytes_ - - def toString(self) -> str: - return f"VMRuntime@{hex(id(self))}[64bit={self.is64Bit()}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# VMDebug -# ============================================================ - -class VMDebug(Stub): - """ - dalvik.system.VMDebug - Provides debug features for the VM. - """ - TRACE_COUNT_ALLOCS: int = 1 - TRACE_COUNT_ALLOCS_SYSTEM_ONLY: int = 2 - TRACE_COUNT_ALLOCS_ALL: int = 3 - - def __init__(self): - super().__init__() - self._alloc_count = 0 - self._alloc_size = 0 - self._freed_count = 0 - self._freed_size = 0 - - @staticmethod - def startCountingAllocations(): - pass - - @staticmethod - def stopCountingAllocations(): - pass - - @staticmethod - def getGlobalAllocCount() -> int: - return 0 - - @staticmethod - def getGlobalAllocSize() -> int: - return 0 - - @staticmethod - def getGlobalFreedCount() -> int: - return 0 - - @staticmethod - def getGlobalFreedSize() -> int: - return 0 - - @staticmethod - def getLoadedClasses() -> int: - return 0 - - @staticmethod - def printLoadedClasses(count: int): - pass - - @staticmethod - def isDebuggerConnected() -> bool: - return False - - @staticmethod - def isDebuggingEnabled() -> bool: - return False - - @staticmethod - def resetAllocCount(): - pass - - @staticmethod - def getThreadAllocCount() -> int: - return 0 - - @staticmethod - def getThreadAllocSize() -> int: - return 0 - - @staticmethod - def threadCpuTimeNanos() -> int: - import time - return int(time.time() * 1e9) - - @staticmethod - def startEmulatorTracing(): - pass - - @staticmethod - def stopEmulatorTracing(): - pass - - @staticmethod - def startMethodTracing(filename: str = None, buffer_size: int = 0, flags: int = 0, sampling_interval: int = 0): - pass - - @staticmethod - def stopMethodTracing(): - pass - - @staticmethod - def getMethodTracingMode() -> int: - return 0 - - @staticmethod - def getRuntimeStat(stat_name: str) -> str: - return "" - - @staticmethod - def getRuntimeStatValue(stat_name: str, value: int) -> int: - return 0 - - @staticmethod - def resolveClassLinkageError(cls: type): - pass - - @staticmethod - def crash(): - raise RuntimeError("VMDebug.crash() called") - - @staticmethod - def dumpReferenceTables(): - pass - - @staticmethod - def setAllocTrackerStackDepth(depth: int): - pass - - @staticmethod - def attachAgent(agent: str, options: str = None, class_loader: Any = None): - pass - - @staticmethod - def startAllocCounting(): - pass - - @staticmethod - def stopAllocCounting(): - pass - - @staticmethod - def getGlobalExternalAllocCount() -> int: - return 0 - - @staticmethod - def getGlobalExternalAllocSize() -> int: - return 0 - - @staticmethod - def getGlobalExternalFreedCount() -> int: - return 0 - - @staticmethod - def getGlobalExternalFreedSize() -> int: - return 0 - - @staticmethod - def setAllocationLimit(limit: int): - pass - - @staticmethod - def setGlobalCpuLimit(limit: int): - pass - - @staticmethod - def setThreadCpuLimit(limit: int): - pass - - @staticmethod - def setMethodTracingPriority(priority: int): - pass - - def toString(self) -> str: - return f"VMDebug@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# CloseGuard -# ============================================================ - -class CloseGuard(Stub): - """ - dalvik.system.CloseGuard - Reports when resources are not closed properly. - """ - REPORTER = None - - def __init__(self): - super().__init__() - self._open = False - self._reported = False - - @staticmethod - def get() -> "CloseGuard": - return CloseGuard() - - def open(self, closer: str): - """Marks this guard as open.""" - self._open = True - - def close(self): - """Marks this guard as closed.""" - self._open = False - - def warnIfOpen(self): - """Warns if the guard is still open.""" - if self._open and not self._reported: - self._reported = True - - @staticmethod - def setEnabled(enabled: bool): - pass - - @staticmethod - def getReporter(): - return CloseGuard.REPORTER - - @staticmethod - def setReporter(reporter): - CloseGuard.REPORTER = reporter - - def toString(self) -> str: - return f"CloseGuard@{hex(id(self))}[open={self._open}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# PotentialDeadlockError -# ============================================================ - -class PotentialDeadlockError(Exception, Stub): - """ - dalvik.system.PotentialDeadlockError - Thrown when a potential deadlock is detected. - """ - def __init__(self, message: str = None): - super().__init__(message or "Potential deadlock detected") - self._message = message - - def toString(self) -> str: - return f"PotentialDeadlockError: {self._message}" - - def equals(self, other) -> bool: - return isinstance(other, PotentialDeadlockError) and self._message == other._message - - def hashCode(self) -> int: - return hash(self._message) & 0x7FFFFFFF - - -# ============================================================ -# BlockGuard -# ============================================================ - -class BlockGuard(Stub): - """ - dalvik.system.BlockGuard - Mechanism to detect and prevent blocking operations on the main thread. - """ - LAX_POLICY = None - - class Policy(Stub): - """ - dalvik.system.BlockGuard.Policy - Interface for block guard policies. - """ - def onReadFromDisk(self): pass - def onWriteToDisk(self): pass - def onNetwork(self): pass - def onExplicitSuspend(self): pass - def getPolicyMask(self) -> int: return 0 - def onFileSystemAccess(self): pass - def onUnbufferedIO(self): pass - - def toString(self) -> str: - return "BlockGuard.Policy" - - def equals(self, other) -> bool: - return isinstance(other, BlockGuard.Policy) - - def hashCode(self) -> int: - return hash("BlockGuard.Policy") & 0x7FFFFFFF - - class Operational(Policy): - """ - dalvik.system.BlockGuard.Operational - Operational policy for block guard. - """ - def toString(self) -> str: - return "BlockGuard.Operational" - - def equals(self, other) -> bool: - return isinstance(other, BlockGuard.Operational) - - def hashCode(self) -> int: - return hash("BlockGuard.Operational") & 0x7FFFFFFF - - _delegate = None - - def __init__(self): - super().__init__() - - @staticmethod - def setDelegate(delegate): - BlockGuard._delegate = delegate - - @staticmethod - def isOperational() -> bool: - return BlockGuard._delegate is not None - - @staticmethod - def initDelegate(): - """Initializes the delegate as a BlockGuard.Operational.""" - BlockGuard._delegate = BlockGuard.Operational() - - @staticmethod - def getThreadPolicy(): - """Returns the policy for the current thread.""" - return BlockGuard.LAX_POLICY or BlockGuard.Policy() - - @staticmethod - def setThreadPolicy(policy: Policy): - pass - - def toString(self) -> str: - return f"BlockGuard@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -BlockGuard.LAX_POLICY = BlockGuard.Policy() \ No newline at end of file diff --git a/android_pyapi/java/__init__.py b/android_pyapi/java/__init__.py deleted file mode 100644 index 9efbd1e..0000000 --- a/android_pyapi/java/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -""" -Java standard library APIs implemented in Python. -Android-pyApi: desktop Python translation of Android Java APIs. -""" \ No newline at end of file diff --git a/android_pyapi/java/beans.py b/android_pyapi/java/beans.py deleted file mode 100644 index 9f083c5..0000000 --- a/android_pyapi/java/beans.py +++ /dev/null @@ -1,806 +0,0 @@ -""" -Java java.beans standard library APIs implemented in Python. -java.beans package - BeanInfo, Introspector, PropertyChange, etc. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -from .lang import RuntimeException -import typing as _typing - -__all__ = [ - "BeanInfo", "IntrospectionException", "Introspector", - "BeanDescriptor", "EventSetDescriptor", "MethodDescriptor", - "ParameterDescriptor", - "PropertyChangeListener", "PropertyChangeSupport", - "PropertyChangeEvent", "PropertyChangeListenerProxy", - "PropertyDescriptor", "SimpleBeanInfo", "FeatureDescriptor", - "PropertyEditor", "PropertyEditorManager", "PropertyEditorSupport", - "VetoableChangeListener", "VetoableChangeSupport", - "IndexedPropertyChangeEvent", "IndexedPropertyDescriptor", - "EventHandler", "ExceptionListener", - "AppletInitializer", "Beans", "Customizer", "DesignMode", "Visibility", -] - - -# ============================================================ -# Exceptions -# ============================================================ - -class IntrospectionException(Exception): - """java.beans.IntrospectionException""" - def __init__(self, message): - super().__init__(message) - - -# ============================================================ -# FeatureDescriptor -# ============================================================ - -class FeatureDescriptor(Stub): - """java.beans.FeatureDescriptor""" - def __init__(self): - super().__init__() - self._name = None - self._display_name = None - self._short_description = None - self._preferred = False - self._expert = False - self._hidden = False - self._attributes = {} - self._bound = False - self._constrained = False - - def getName(self): - return self._name - - def setName(self, name): - self._name = name - - def getDisplayName(self): - return self._display_name or self._name or "" - - def setDisplayName(self, name): - self._display_name = name - - def getShortDescription(self): - return self._short_description or self.getDisplayName() - - def setShortDescription(self, text): - self._short_description = text - - def isPreferred(self): - return self._preferred - - def setPreferred(self, preferred): - self._preferred = preferred - - def isExpert(self): - return self._expert - - def setExpert(self, expert): - self._expert = expert - - def isHidden(self): - return self._hidden - - def setHidden(self, hidden): - self._hidden = hidden - - def setValue(self, attribute_name, value): - self._attributes[attribute_name] = value - - def getValue(self, attribute_name): - return self._attributes.get(attribute_name) - - def attributeNames(self): - return list(self._attributes.keys()) - - -# ============================================================ -# BeanDescriptor -# ============================================================ - -class BeanDescriptor(FeatureDescriptor): - """java.beans.BeanDescriptor""" - def __init__(self, bean_class, customizer_class=None): - super().__init__() - self._bean_class = bean_class - self._customizer_class = customizer_class - - def getBeanClass(self): - return self._bean_class - - def getCustomizerClass(self): - return self._customizer_class - - -# ============================================================ -# EventSetDescriptor -# ============================================================ - -class EventSetDescriptor(FeatureDescriptor): - """java.beans.EventSetDescriptor""" - def __init__(self, source_class, event_set_name, listener_type, - listener_methods, add_listener_method=None, - remove_listener_method=None): - super().__init__() - self._source_class = source_class - self._event_set_name = event_set_name - self._listener_type = listener_type - self._listener_methods = listener_methods or [] - self._add_listener_method = add_listener_method - self._remove_listener_method = remove_listener_method - self._unicast = False - self._in_default_event_set = False - - def getListenerType(self): - return self._listener_type - - def getListenerMethods(self): - return self._listener_methods - - def getAddListenerMethod(self): - return self._add_listener_method - - def getRemoveListenerMethod(self): - return self._remove_listener_method - - def isUnicast(self): - return self._unicast - - def setUnicast(self, unicast): - self._unicast = unicast - - def isInDefaultEventSet(self): - return self._in_default_event_set - - def setInDefaultEventSet(self, in_default): - self._in_default_event_set = in_default - - -# ============================================================ -# MethodDescriptor / ParameterDescriptor -# ============================================================ - -class MethodDescriptor(FeatureDescriptor): - """java.beans.MethodDescriptor""" - def __init__(self, method, parameter_descriptors=None): - super().__init__() - self._method = method - self._parameter_descriptors = parameter_descriptors or [] - - def getMethod(self): - return self._method - - def getParameterDescriptors(self): - return self._parameter_descriptors - - -class ParameterDescriptor(FeatureDescriptor): - """java.beans.ParameterDescriptor""" - pass - - -# ============================================================ -# PropertyDescriptor -# ============================================================ - -class PropertyDescriptor(FeatureDescriptor): - """java.beans.PropertyDescriptor""" - def __init__(self, property_name, bean_class, - getter_name=None, setter_name=None): - super().__init__() - self._property_name = property_name - self._bean_class = bean_class - self._getter_name = getter_name - self._setter_name = setter_name - self._property_type = None - self._read_method = None - self._write_method = None - self._bound = False - self._constrained = False - self._property_editor_class = None - - def getPropertyType(self): - return self._property_type - - def setPropertyType(self, prop_type): - self._property_type = prop_type - - def getReadMethod(self): - return self._read_method - - def setReadMethod(self, method): - self._read_method = method - - def getWriteMethod(self): - return self._write_method - - def setWriteMethod(self, method): - self._write_method = method - - def isBound(self): - return self._bound - - def setBound(self, bound): - self._bound = bound - - def isConstrained(self): - return self._constrained - - def setConstrained(self, constrained): - self._constrained = constrained - - def getPropertyEditorClass(self): - return self._property_editor_class - - def setPropertyEditorClass(self, cls): - self._property_editor_class = cls - - -class IndexedPropertyDescriptor(PropertyDescriptor): - """java.beans.IndexedPropertyDescriptor""" - def __init__(self, property_name, bean_class, - getter_name=None, setter_name=None, - indexed_getter_name=None, indexed_setter_name=None): - super().__init__(property_name, bean_class, getter_name, setter_name) - self._indexed_read_method = None - self._indexed_write_method = None - self._indexed_property_type = None - - def getIndexedReadMethod(self): - return self._indexed_read_method - - def setIndexedReadMethod(self, method): - self._indexed_read_method = method - - def getIndexedWriteMethod(self): - return self._indexed_write_method - - def setIndexedWriteMethod(self, method): - self._indexed_write_method = method - - def getIndexedPropertyType(self): - return self._indexed_property_type - - def setIndexedPropertyType(self, prop_type): - self._indexed_property_type = prop_type - - -# ============================================================ -# Introspector -# ============================================================ - -class Introspector(Stub): - """java.beans.Introspector""" - @staticmethod - def getBeanInfo(bean_class, stop_class=None, flags=None): - bean_descriptor = BeanDescriptor(bean_class) - bean_info = SimpleBeanInfo() - - props = [] - methods = [] - events = [] - - for name in dir(bean_class): - if name.startswith('_'): - continue - attr = getattr(bean_class, name, None) - if callable(attr): - methods.append(MethodDescriptor(attr)) - if name.startswith('get') and not name.startswith('getClass'): - prop_name = name[3].lower() + name[4:] - pd = PropertyDescriptor(prop_name, bean_class, getter_name=name) - props.append(pd) - - bean_info._property_descriptors = props - bean_info._method_descriptors = methods - bean_info._event_set_descriptors = events - bean_info._bean_descriptor = bean_descriptor - return bean_info - - @staticmethod - def decapitalize(name): - if len(name) == 0: - return name - if len(name) > 1 and name[1].islower(): - return name[0].lower() + name[1:] - return name.lower() - - @staticmethod - def flushCaches(): - pass - - @staticmethod - def flushFromCaches(cls): - pass - - USE_ALL_BEANINFO = 1 - IGNORE_IMMEDIATE_BEANINFO = 2 - IGNORE_ALL_BEANINFO = 3 - - -# ============================================================ -# SimpleBeanInfo / BeanInfo -# ============================================================ - -class BeanInfo(Stub): - """java.beans.BeanInfo""" - ICON_COLOR_16x16 = 1 - ICON_COLOR_32x32 = 2 - ICON_MONO_16x16 = 3 - ICON_MONO_32x32 = 4 - - def getBeanDescriptor(self): - return None - - def getEventSetDescriptors(self): - return [] - - def getDefaultEventIndex(self): - return -1 - - def getMethodDescriptors(self): - return [] - - def getDefaultMethodIndex(self): - return -1 - - def getPropertyDescriptors(self): - return [] - - def getDefaultPropertyIndex(self): - return -1 - - def getIcon(self, icon_kind): - return None - - def getAdditionalBeanInfo(self): - return None - - -class SimpleBeanInfo(BeanInfo): - """java.beans.SimpleBeanInfo""" - def __init__(self): - super().__init__() - self._bean_descriptor = None - self._property_descriptors = [] - self._method_descriptors = [] - self._event_set_descriptors = [] - self._default_property_index = -1 - self._default_event_index = -1 - self._default_method_index = -1 - - def getBeanDescriptor(self): - return self._bean_descriptor - - def getPropertyDescriptors(self): - return self._property_descriptors - - def getMethodDescriptors(self): - return self._method_descriptors - - def getEventSetDescriptors(self): - return self._event_set_descriptors - - def getDefaultPropertyIndex(self): - return self._default_property_index - - def getDefaultEventIndex(self): - return self._default_event_index - - def getIcon(self, icon_kind): - return None - - def loadBeanInfo(self, bean_info_class): - return self - - -# ============================================================ -# PropertyChangeEvent / IndexedPropertyChangeEvent -# ============================================================ - -class PropertyChangeEvent(Stub): - """java.beans.PropertyChangeEvent""" - def __init__(self, source, property_name, old_value, new_value): - super().__init__() - self._source = source - self._property_name = property_name - self._old_value = old_value - self._new_value = new_value - self._propagation_id = None - - def getPropertyName(self): - return self._property_name - - def getOldValue(self): - return self._old_value - - def getNewValue(self): - return self._new_value - - def setPropagationId(self, propagation_id): - self._propagation_id = propagation_id - - def getPropagationId(self): - return self._propagation_id - - def toString(self): - return (f"PropertyChangeEvent[property={self._property_name}, " - f"old={self._old_value}, new={self._new_value}]") - - -class IndexedPropertyChangeEvent(PropertyChangeEvent): - """java.beans.IndexedPropertyChangeEvent""" - def __init__(self, source, property_name, old_value, new_value, index): - super().__init__(source, property_name, old_value, new_value) - self._index = index - - def getIndex(self): - return self._index - - -# ============================================================ -# Listener Interfaces -# ============================================================ - -class PropertyChangeListener(Stub): - """java.beans.PropertyChangeListener""" - def propertyChange(self, evt): - pass - - -class PropertyChangeListenerProxy(PropertyChangeListener): - """java.beans.PropertyChangeListenerProxy""" - def __init__(self, property_name, listener): - super().__init__() - self._property_name = property_name - self._listener = listener - - def propertyChange(self, evt): - if self._listener: - self._listener(evt) - - def getPropertyName(self): - return self._property_name - - def getListener(self): - return self._listener - - -class VetoableChangeListener(Stub): - """java.beans.VetoableChangeListener""" - def vetoableChange(self, evt): - pass - - -# ============================================================ -# PropertyChangeSupport -# ============================================================ - -class PropertyChangeSupport(Stub): - """java.beans.PropertyChangeSupport""" - def __init__(self, source_bean): - super().__init__() - self._source = source_bean - self._listeners = [] - - def addPropertyChangeListener(self, listener, property_name=None): - if property_name is not None: - self._listeners.append(PropertyChangeListenerProxy(property_name, listener)) - else: - self._listeners.append(listener) - - def removePropertyChangeListener(self, listener, property_name=None): - if property_name is not None: - self._listeners = [ - l for l in self._listeners - if not (isinstance(l, PropertyChangeListenerProxy) - and l.getListener() == listener - and l.getPropertyName() == property_name) - ] - else: - self._listeners = [ - l for l in self._listeners - if l != listener - ] - - def getPropertyChangeListeners(self, property_name=None): - if property_name is not None: - return [l for l in self._listeners - if isinstance(l, PropertyChangeListenerProxy) - and l.getPropertyName() == property_name - or not isinstance(l, PropertyChangeListenerProxy)] - return self._listeners - - def firePropertyChange(self, property_name_or_evt, old_value=None, new_value=None): - if isinstance(property_name_or_evt, PropertyChangeEvent): - evt = property_name_or_evt - else: - evt = PropertyChangeEvent(self._source, property_name_or_evt, - old_value, new_value) - for listener in self._listeners: - if isinstance(listener, PropertyChangeListenerProxy): - if listener.getPropertyName() == evt.getPropertyName(): - listener.propertyChange(evt) - else: - listener.propertyChange(evt) - - def hasListeners(self, property_name): - for listener in self._listeners: - if isinstance(listener, PropertyChangeListenerProxy): - if listener.getPropertyName() == property_name: - return True - return False - - -# ============================================================ -# VetoableChangeSupport -# ============================================================ - -class VetoableChangeSupport(Stub): - """java.beans.VetoableChangeSupport""" - def __init__(self, source_bean): - super().__init__() - self._source = source_bean - self._listeners = [] - - def addVetoableChangeListener(self, listener, property_name=None): - if property_name is not None: - self._listeners.append(PropertyChangeListenerProxy(property_name, listener)) - else: - self._listeners.append(listener) - - def removeVetoableChangeListener(self, listener, property_name=None): - if property_name is not None: - self._listeners = [ - l for l in self._listeners - if not (isinstance(l, PropertyChangeListenerProxy) - and l.getListener() == listener - and l.getPropertyName() == property_name) - ] - else: - self._listeners = [l for l in self._listeners if l != listener] - - def fireVetoableChange(self, property_name_or_evt, old_value=None, new_value=None): - if isinstance(property_name_or_evt, PropertyChangeEvent): - evt = property_name_or_evt - else: - evt = PropertyChangeEvent(self._source, property_name_or_evt, - old_value, new_value) - for listener in self._listeners: - if isinstance(listener, PropertyChangeListenerProxy): - if listener.getPropertyName() == evt.getPropertyName(): - listener.propertyChange(evt) - else: - listener.propertyChange(evt) - - def hasListeners(self, property_name): - for listener in self._listeners: - if isinstance(listener, PropertyChangeListenerProxy): - if listener.getPropertyName() == property_name: - return True - return False - - -# ============================================================ -# PropertyEditor -# ============================================================ - -class PropertyEditor(Stub): - """java.beans.PropertyEditor""" - def setValue(self, value): - pass - - def getValue(self): - return None - - def isPaintable(self): - return False - - def paintValue(self, gfx, box): - pass - - def getJavaInitializationString(self): - return "" - - def getAsText(self): - return "" - - def setAsText(self, text): - pass - - def getTags(self): - return None - - def getCustomEditor(self): - return None - - def supportsCustomEditor(self): - return False - - def addPropertyChangeListener(self, listener): - pass - - def removePropertyChangeListener(self, listener): - pass - - -class PropertyEditorSupport(PropertyEditor): - """java.beans.PropertyEditorSupport""" - def __init__(self, source=None): - super().__init__() - self._source = source - self._value = None - self._listeners = [] - - def setValue(self, value): - self._value = value - self._fire_property_change() - - def getValue(self): - return self._value - - def getAsText(self): - if self._value is None: - return "null" - return str(self._value) - - def setAsText(self, text): - self._value = text - self._fire_property_change() - - def getSource(self): - return self._source - - def addPropertyChangeListener(self, listener): - self._listeners.append(listener) - - def removePropertyChangeListener(self, listener): - if listener in self._listeners: - self._listeners.remove(listener) - - def _fire_property_change(self): - evt = PropertyChangeEvent(self._source, "", None, self._value) - for listener in self._listeners: - listener.propertyChange(evt) - - -class PropertyEditorManager(Stub): - """java.beans.PropertyEditorManager""" - _editors = {} - - @staticmethod - def findEditor(target_type): - return PropertyEditorSupport() - - @staticmethod - def registerEditor(target_type, editor_class): - PropertyEditorManager._editors[target_type] = editor_class - - @staticmethod - def getEditorSearchPath(): - return [] - - @staticmethod - def setEditorSearchPath(path): - pass - - -# ============================================================ -# EventHandler -# ============================================================ - -class EventHandler(Stub): - """java.beans.EventHandler""" - def __init__(self, target, action, event_property_name, listener_method_name): - super().__init__() - self._target = target - self._action = action - self._event_property_name = event_property_name - self._listener_method_name = listener_method_name - - @staticmethod - def create(listener_interface, target, action, event_property_name=None, - listener_method_name=None): - return target - - def invoke(self, proxy, method, arguments): - if self._target and self._action: - action_fn = getattr(self._target, self._action, None) - if action_fn: - return action_fn() - - -# ============================================================ -# ExceptionListener -# ============================================================ - -class ExceptionListener(Stub): - """java.beans.ExceptionListener""" - def exceptionThrown(self, e): - pass - - -# ============================================================ -# AppletInitializer -# ============================================================ - -class AppletInitializer(Stub): - """java.beans.AppletInitializer""" - def initialize(self, applet_bean, bean_context): - pass - - def activate(self, applet_bean): - pass - - -# ============================================================ -# Beans / Customizer / DesignMode / Visibility -# ============================================================ - -class Beans(Stub): - """java.beans.Beans""" - @staticmethod - def instantiate(cls_loader, bean_name, bean_context=None, bean_arg=None): - return None - - @staticmethod - def designTime(): - return False - - @staticmethod - def isDesignTime(): - return False - - @staticmethod - def setDesignTime(is_design_time): - pass - - @staticmethod - def isGuiAvailable(): - return False - - @staticmethod - def setGuiAvailable(is_gui_available): - pass - - -class Customizer(Stub): - """java.beans.Customizer""" - def setObject(self, bean): - pass - - def addPropertyChangeListener(self, listener): - pass - - def removePropertyChangeListener(self, listener): - pass - - -class DesignMode(Stub): - """java.beans.DesignMode""" - PROPERTYNAME = "designTime" - - def isDesignTime(self): - return False - - def setDesignTime(self, design_time): - pass - - -class Visibility(Stub): - """java.beans.Visibility""" - def needsGui(self): - return False - - def dontUseGui(self): - pass - - def okToUseGui(self): - pass - - def avoidingGui(self): - return True \ No newline at end of file diff --git a/android_pyapi/java/crypto.py b/android_pyapi/java/crypto.py deleted file mode 100644 index fe80f49..0000000 --- a/android_pyapi/java/crypto.py +++ /dev/null @@ -1,628 +0,0 @@ -""" -Java crypto standard library APIs implemented in Python. -javax.crypto package - Cipher, KeyGenerator, Mac, SecretKey, etc. -""" - -from .._core import Stub, AndroidOnlyError -from .lang import RuntimeException -import os as _os - -# Try to import cryptography library -try: - from cryptography.hazmat.primitives.ciphers import Cipher as _Cipher - from cryptography.hazmat.primitives.ciphers import algorithms as _algorithms - from cryptography.hazmat.primitives.ciphers import modes as _modes - from cryptography.hazmat.primitives import hashes as _hashes - from cryptography.hazmat.primitives.asymmetric import rsa as _rsa - from cryptography.hazmat.primitives.asymmetric import padding as _padding - from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC as _PBKDF2 - from cryptography.hazmat.backends import default_backend as _crypto_backend - HAS_CRYPTOGRAPHY = True -except ImportError: - HAS_CRYPTOGRAPHY = False - -from .security import ( - GeneralSecurityException, InvalidKeyException, - NoSuchAlgorithmException, NoSuchProviderException, - Key, SecretKeySpec, AlgorithmParameters, AlgorithmParametersSpi, - AlgorithmParameterGenerator, AlgorithmParameterGeneratorSpi, - Provider, SecureRandom, -) - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) -AEADBadTagException = type("AEADBadTagException", (GeneralSecurityException,), {}) -BadPaddingException = type("BadPaddingException", (GeneralSecurityException,), {}) -IllegalBlockSizeException = type("IllegalBlockSizeException", (GeneralSecurityException,), {}) -NoSuchPaddingException = type("NoSuchPaddingException", (GeneralSecurityException,), {}) -ShortBufferException = type("ShortBufferException", (GeneralSecurityException,), {}) -ExemptionMechanismException = type("ExemptionMechanismException", (GeneralSecurityException,), {}) - -__all__ = [ - "Cipher", "CipherSpi", - "KeyGenerator", "KeyGeneratorSpi", - "SecretKey", "SecretKeyFactory", "SecretKeyFactorySpi", - "Mac", "MacSpi", - "IvParameterSpec", "PBEParameterSpec", "GCMParameterSpec", - "OAEPParameterSpec", "PSource", "MGF1ParameterSpec", - "DHParameterSpec", "DSAParameterSpec", - "ECParameterSpec", "ECGenParameterSpec", "ECPoint", - "ECField", "ECFieldF2m", "ECFieldFp", "EllipticCurve", - "AlgorithmParameters", "AlgorithmParameterGenerator", - "AlgorithmParametersSpi", - "AEADBadTagException", "BadPaddingException", - "IllegalBlockSizeException", "NoSuchPaddingException", - "ShortBufferException", - "ExemptionMechanism", "ExemptionMechanismException", - "ExemptionMechanismSpi", -] - - -# ============================================================ -# SecretKey -# ============================================================ - -class SecretKey(Key): - def __init__(self, key_bytes=None, algorithm=""): - super().__init__() - self._key = key_bytes or b"" - self._algorithm = algorithm - - def getAlgorithm(self): - return self._algorithm - - def getFormat(self): - return "RAW" - - def getEncoded(self): - return self._key - - -# ============================================================ -# Cipher -# ============================================================ - -class Cipher(Stub): - ENCRYPT_MODE = 1 - DECRYPT_MODE = 2 - WRAP_MODE = 3 - UNWRAP_MODE = 4 - PUBLIC_KEY = 1 - PRIVATE_KEY = 2 - SECRET_KEY = 3 - - def __init__(self, transformation): - super().__init__() - self._transformation = transformation - self._opmode = 0 - self._key = None - self._iv = None - self._provider = Provider("AndroidOpenSSL", 1.0, "Android OpenSSL provider") - - @staticmethod - def getInstance(transformation, provider=None): - return Cipher(transformation) - - def init(self, opmode, key_or_cert, params=None, random=None): - self._opmode = opmode - self._key = key_or_cert - if isinstance(params, IvParameterSpec): - self._iv = params.getIV() - elif isinstance(params, GCMParameterSpec): - self._iv = params.getIV() - - def doFinal(self, input_data=None): - if input_data is None: - return b"" - if HAS_CRYPTOGRAPHY and self._key is not None: - try: - algo = self._transformation.split("/")[0].upper() - key_bytes = self._key.getEncoded() if hasattr(self._key, "getEncoded") else bytes(self._key) - iv = self._iv or b"\x00" * 16 - if algo == "AES": - if len(iv) == 12: - mode = _modes.GCM(iv) - else: - mode = _modes.CBC(iv) - cipher = _Cipher(_algorithms.AES(key_bytes), mode, backend=_crypto_backend()) - elif algo == "DES": - cipher = _Cipher(_algorithms.TripleDES(key_bytes), _modes.CBC(iv), backend=_crypto_backend()) - else: - return self._basic_cipher(input_data) - if self._opmode == Cipher.ENCRYPT_MODE: - encryptor = cipher.encryptor() - result = encryptor.update(input_data) + encryptor.finalize() - return result - else: - decryptor = cipher.decryptor() - result = decryptor.update(input_data) + decryptor.finalize() - return result - except Exception: - return self._basic_cipher(input_data) - else: - return self._basic_cipher(input_data) - - def _basic_cipher(self, data): - if self._opmode == Cipher.ENCRYPT_MODE: - if self._key is not None: - key_bytes = self._key.getEncoded() if hasattr(self._key, "getEncoded") else bytes(self._key) - return bytes([(b ^ key_bytes[i % len(key_bytes)]) & 0xFF for i, b in enumerate(data)]) - return data - else: - if self._key is not None: - key_bytes = self._key.getEncoded() if hasattr(self._key, "getEncoded") else bytes(self._key) - return bytes([(b ^ key_bytes[i % len(key_bytes)]) & 0xFF for i, b in enumerate(data)]) - return data - - def update(self, input_data): - return self._basic_cipher(input_data) - - def getBlockSize(self): - algo = self._transformation.split("/")[0].upper() - if algo == "AES": - return 16 - if algo == "DES" or algo == "DESede": - return 8 - return 1 - - def getOutputSize(self, input_len): - bs = self.getBlockSize() - if bs > 1: - return input_len + bs - (input_len % bs) - return input_len - - def getIV(self): - return self._iv - - def getParameters(self): - if self._iv: - return AlgorithmParameters(self._transformation.split("/")[0]) - return None - - def getAlgorithm(self): - return self._transformation - - def getProvider(self): - return self._provider - - def getExemptionMechanism(self): - return None - - def unwrap(self, wrapped_key, wrapped_key_algorithm, wrapped_key_type): - return SecretKey(wrapped_key, wrapped_key_algorithm) - - def wrap(self, key): - key_bytes = key.getEncoded() if hasattr(key, "getEncoded") else b"" - return self.doFinal(key_bytes) - - @staticmethod - def maxAllowedKeyLength(transformation): - return 2147483647 - - -class CipherSpi(Stub): - pass - - -# ============================================================ -# KeyGenerator -# ============================================================ - -class KeyGenerator(Stub): - def __init__(self, algorithm): - super().__init__() - self._algorithm = algorithm - self._key_size = 128 - self._secure_random = None - - @staticmethod - def getInstance(algorithm, provider=None): - return KeyGenerator(algorithm) - - def init(self, size_or_random, random=None): - if isinstance(size_or_random, int): - self._key_size = size_or_random - self._secure_random = random - else: - self._secure_random = size_or_random - - def generateKey(self): - key_bytes = _os.urandom(self._key_size // 8) - return SecretKey(key_bytes, self._algorithm) - - def getAlgorithm(self): - return self._algorithm - - def getProvider(self): - return Provider("AndroidOpenSSL", 1.0, "Android OpenSSL provider") - - -class KeyGeneratorSpi(Stub): - pass - - -# ============================================================ -# SecretKeyFactory -# ============================================================ - -class SecretKeyFactory(Stub): - def __init__(self, algorithm): - super().__init__() - self._algorithm = algorithm - - @staticmethod - def getInstance(algorithm, provider=None): - return SecretKeyFactory(algorithm) - - def generateSecret(self, key_spec): - if isinstance(key_spec, SecretKeySpec): - return SecretKey(key_spec.getEncoded(), self._algorithm) - return SecretKey(b"", self._algorithm) - - def getKeySpec(self, secret_key, key_spec_cls): - return SecretKeySpec(secret_key.getEncoded(), self._algorithm) - - def translateKey(self, key): - return key - - def getAlgorithm(self): - return self._algorithm - - -class SecretKeyFactorySpi(Stub): - pass - - -# ============================================================ -# Mac -# ============================================================ - -class Mac(Stub): - def __init__(self, algorithm): - super().__init__() - self._algorithm = algorithm - self._key = None - self._state = "UNINITIALIZED" - self._data = b"" - - @staticmethod - def getInstance(algorithm, provider=None): - return Mac(algorithm) - - def init(self, key, params=None): - self._key = key - self._state = "INITIALIZED" - self._data = b"" - - def update(self, input_data, offset=0, length=-1): - if length < 0: - self._data += bytes(input_data) - else: - self._data += bytes(input_data[offset:offset+length]) - - def doFinal(self, input_data=None): - if input_data is not None: - self._data += bytes(input_data) - if HAS_CRYPTOGRAPHY and self._key is not None: - try: - key_bytes = self._key.getEncoded() if hasattr(self._key, "getEncoded") else bytes(self._key) - algo = self._algorithm.lower().replace("-", "") - if "sha256" in algo or "sha-256" in self._algorithm: - h = _hashlib.sha256(key_bytes + self._data) - elif "sha1" in algo or "sha-1" in self._algorithm: - h = _hashlib.sha1(key_bytes + self._data) - elif "md5" in algo: - h = _hashlib.md5(key_bytes + self._data) - else: - h = _hashlib.sha256(key_bytes + self._data) - result = h.digest() - self._data = b"" - return result - except Exception: - pass - import hashlib as _hashlib - key_bytes = self._key.getEncoded() if self._key and hasattr(self._key, "getEncoded") else b"key" - h = _hashlib.sha256(key_bytes + self._data) - result = h.digest() - self._data = b"" - return result - - def reset(self): - self._data = b"" - - def getAlgorithm(self): - return self._algorithm - - def getMacLength(self): - return 32 - - def getProvider(self): - return Provider("AndroidOpenSSL", 1.0, "Android OpenSSL provider") - - -class MacSpi(Stub): - pass - - -# ============================================================ -# Parameter specs -# ============================================================ - -class IvParameterSpec(Stub): - def __init__(self, iv): - super().__init__() - self._iv = bytes(iv) - - def getIV(self): - return self._iv - - -class PBEParameterSpec(Stub): - def __init__(self, salt, iteration_count): - super().__init__() - self._salt = bytes(salt) - self._iteration_count = iteration_count - - def getSalt(self): - return self._salt - - def getIterationCount(self): - return self._iteration_count - - -class GCMParameterSpec(Stub): - def __init__(self, t_len, src): - super().__init__() - self._t_len = t_len - self._iv = bytes(src) - - def getTLen(self): - return self._t_len - - def getIV(self): - return self._iv - - -class OAEPParameterSpec(Stub): - def __init__(self, md_name, mgf_name, mgf_spec, p_src): - super().__init__() - self._md_name = md_name - self._mgf_name = mgf_name - self._mgf_spec = mgf_spec - self._p_src = p_src - - def getDigestAlgorithm(self): - return self._md_name - - def getMGFAlgorithm(self): - return self._mgf_name - - def getMGFParameters(self): - return self._mgf_spec - - def getPSource(self): - return self._p_src - - -class PSource(Stub): - class PSpecified(Stub): - DEFAULT = None - - def __init__(self, p): - super().__init__() - self._p = p - - def getValue(self): - return self._p - - def __init__(self, p_src_id): - super().__init__() - self._p_src_id = p_src_id - - def getAlgorithm(self): - return self._p_src_id - - -PSource.PSpecified.DEFAULT = PSource.PSpecified(b"") - - -class MGF1ParameterSpec(Stub): - SHA1 = None - SHA256 = None - SHA384 = None - SHA512 = None - - def __init__(self, md_name): - super().__init__() - self._md_name = md_name - - def getDigestAlgorithm(self): - return self._md_name - - def toString(self): - return f"MGF1 {self._md_name}" - - -MGF1ParameterSpec.SHA1 = MGF1ParameterSpec("SHA-1") -MGF1ParameterSpec.SHA256 = MGF1ParameterSpec("SHA-256") -MGF1ParameterSpec.SHA384 = MGF1ParameterSpec("SHA-384") -MGF1ParameterSpec.SHA512 = MGF1ParameterSpec("SHA-512") - - -# ============================================================ -# DH / DSA / EC parameter specs (stubs) -# ============================================================ - -class DHParameterSpec(Stub): - def __init__(self, p, g, l=0): - super().__init__() - self._p = p - self._g = g - self._l = l - - def getP(self): - return self._p - - def getG(self): - return self._g - - def getL(self): - return self._l - - -class DSAParameterSpec(Stub): - def __init__(self, p, q, g): - super().__init__() - self._p = p - self._q = q - self._g = g - - def getP(self): - return self._p - - def getQ(self): - return self._q - - def getG(self): - return self._g - - -class ECPoint(Stub): - def __init__(self, affine_x, affine_y): - super().__init__() - self._affine_x = affine_x - self._affine_y = affine_y - - def getAffineX(self): - return self._affine_x - - def getAffineY(self): - return self._affine_y - - def equals(self, other): - if isinstance(other, ECPoint): - return self._affine_x == other._affine_x and self._affine_y == other._affine_y - return False - - def hashCode(self): - return hash((self._affine_x, self._affine_y)) - - -class ECField(Stub): - def getFieldSize(self): - raise NotImplementedError - - -class ECFieldFp(ECField): - def __init__(self, p): - super().__init__() - self._p = p - - def getP(self): - return self._p - - def getFieldSize(self): - return self._p.bit_length() - - -class ECFieldF2m(ECField): - def __init__(self, m, ks=None): - super().__init__() - self._m = m - self._ks = ks - - def getM(self): - return self._m - - def getMidTermsOfReductionPolynomial(self): - return self._ks - - def getFieldSize(self): - return self._m - - -class EllipticCurve(Stub): - def __init__(self, field, a, b, seed=None): - super().__init__() - self._field = field - self._a = a - self._b = b - self._seed = seed - - def getField(self): - return self._field - - def getA(self): - return self._a - - def getB(self): - return self._b - - def getSeed(self): - return self._seed - - -class ECParameterSpec(Stub): - def __init__(self, curve, generator, order, cofactor, seed=None): - super().__init__() - self._curve = curve - self._generator = generator - self._order = order - self._cofactor = cofactor - - def getCurve(self): - return self._curve - - def getGenerator(self): - return self._generator - - def getOrder(self): - return self._order - - def getCofactor(self): - return self._cofactor - - -class ECGenParameterSpec(Stub): - def __init__(self, name): - super().__init__() - self._name = name - - def getName(self): - return self._name - - -# ============================================================ -# ExemptionMechanism -# ============================================================ - -class ExemptionMechanism(Stub): - def __init__(self, name): - super().__init__() - self._name = name - self._state = False - - @staticmethod - def getInstance(name, provider=None): - return ExemptionMechanism(name) - - def init(self, key): - self._state = False - - def isCryptoAllowed(self, key): - return True - - def genExemptionBlob(self, data=None): - return b"" - - def getName(self): - return self._name - - def getOutputSize(self, input_len): - return input_len - - def storeMechanism(self, mechanism): - pass - - -class ExemptionMechanismSpi(Stub): - pass \ No newline at end of file diff --git a/android_pyapi/java/io.py b/android_pyapi/java/io.py deleted file mode 100644 index 02974ff..0000000 --- a/android_pyapi/java/io.py +++ /dev/null @@ -1,2120 +0,0 @@ -""" -Java I/O standard library APIs implemented in Python. -java.io package - File, streams, readers, writers, and exceptions. -""" - -from .._core import Stub, AndroidOnlyError -import io as _pyio -import os as _os -import sys as _sys -import errno as _errno -import stat as _stat -import threading as _threading -from datetime import datetime as _datetime -from collections import OrderedDict as _OrderedDict -from pathlib import Path as _PathLibPath - -__all__ = [ - "Serializable", "Externalizable", "ObjectStreamField", "ObjectStreamConstants", - "FileFilter", "FilenameFilter", "File", "FileDescriptor", - "FileInputStream", "FileOutputStream", - "BufferedInputStream", "BufferedOutputStream", - "BufferedReader", "BufferedWriter", - "InputStreamReader", "OutputStreamWriter", - "FileReader", "FileWriter", "PrintWriter", - "ByteArrayInputStream", "ByteArrayOutputStream", - "StringReader", "StringWriter", - "DataInputStream", "DataOutputStream", - "ObjectInputStream", "ObjectOutputStream", - "FilterInputStream", "FilterOutputStream", - "PipedInputStream", "PipedOutputStream", - "PushbackInputStream", "SequenceInputStream", - "RandomAccessFile", - "Closeable", "Flushable", - "IOError", "IOException", "FileNotFoundException", - "EOFException", "CharConversionException", - "ObjectStreamException", "SyncFailedException", - "UTFDataFormatException", "WriteAbortedException", - "UnsupportedEncodingException", -] - - -# ============================================================ -# Marker / mixin interfaces -# ============================================================ - -class Closeable(Stub): - def close(self): - raise NotImplementedError - -class Flushable(Stub): - def flush(self): - raise NotImplementedError - -class Serializable(Stub): - pass - -class Externalizable(Stub): - pass - -class ObjectStreamConstants: - STREAM_MAGIC = 0xACED - STREAM_VERSION = 5 - TC_BASE = 0x70 - TC_NULL = 0x70 - TC_REFERENCE = 0x71 - TC_CLASS = 0x72 - TC_OBJECT = 0x73 - TC_STRING = 0x74 - TC_ARRAY = 0x75 - TC_ENUM = 0x7E - TC_CLASSDESC = 0x72 - TC_PROXYCLASSDESC = 0x7D - TC_BLOCKDATA = 0x77 - TC_ENDBLOCKDATA = 0x78 - TC_RESET = 0x79 - TC_EXCEPTION = 0x7A - TC_LONGSTRING = 0x7C - baseWireHandle = 0x7E0000 - -class ObjectStreamField(Stub): - def __init__(self, name, typ): - self._name = name - self._type = typ - - def getName(self): - return self._name - - def getType(self): - return self._type - - def toString(self): - return f"{self._type} {self._name}" - - -# ============================================================ -# FileFilter / FilenameFilter -# ============================================================ - -class FileFilter(Stub): - def accept(self, pathname): - raise NotImplementedError - -class FilenameFilter(Stub): - def accept(self, dir, name): - raise NotImplementedError - - -# ============================================================ -# FileDescriptor -# ============================================================ - -class FileDescriptor(Stub): - _valid_fd = -1 - - def __init__(self, fd=None): - super().__init__() - if fd is not None: - self._valid_fd = fd - - def valid(self): - return self._valid_fd >= 0 - - def sync(self): - if self._valid_fd >= 0: - try: - _os.fsync(self._valid_fd) - except OSError: - pass - - def hashCode(self): - return self._valid_fd - - @staticmethod - def _from_fileno(fd): - return FileDescriptor(fd) - - -# ============================================================ -# Exception classes -# ============================================================ - -class IOException(Exception): - def __init__(self, message=None, cause=None): - super().__init__(message) - self._cause = cause - - def getCause(self): - return self._cause - -class FileNotFoundException(IOException): - pass - -class EOFException(IOException): - pass - -class CharConversionException(IOException): - pass - -class ObjectStreamException(IOException): - pass - -class SyncFailedException(IOException): - pass - -class UTFDataFormatException(IOException): - pass - -class WriteAbortedException(ObjectStreamException): - def __init__(self, message=None, cause=None): - super().__init__(message) - self.detail = cause - -class UnsupportedEncodingException(IOException): - pass - -class IOError(Exception): - def __init__(self, message=None, cause=None): - super().__init__(message) - self._cause = cause - - def getCause(self): - return self._cause - - -# ============================================================ -# File -# ============================================================ - -class File(Stub): - separator = _os.sep - separatorChar = _os.sep - pathSeparator = _os.pathsep - pathSeparatorChar = _os.pathsep - - def __init__(self, *args): - super().__init__() - if len(args) == 1: - path = args[0] - if isinstance(path, File): - self._path = path._path - else: - self._path = str(path) - elif len(args) == 2: - parent, child = args - if parent is None: - self._path = str(child) - else: - parent_str = parent._path if isinstance(parent, File) else str(parent) - child_str = str(child) - if child_str.startswith(self.separator): - self._path = child_str - else: - self._path = _os.path.join(parent_str, child_str) - else: - self._path = "" - self._path = self._path or "" - - def _resolve(self): - return _os.path.realpath(self._path) if self._path else "" - - def exists(self): - return _os.path.exists(self._path) - - def isFile(self): - return _os.path.isfile(self._path) - - def isDirectory(self): - return _os.path.isdir(self._path) - - def getName(self): - return _os.path.basename(self._path) - - def getPath(self): - return self._path - - def getAbsolutePath(self): - return _os.path.abspath(self._path) - - def getCanonicalPath(self): - try: - return _os.path.realpath(self._path) - except Exception: - return self.getAbsolutePath() - - def getParent(self): - p = _os.path.dirname(self._path) - return p if p else None - - def getParentFile(self): - p = self.getParent() - return File(p) if p else None - - def length(self): - try: - return _os.path.getsize(self._path) - except OSError: - return 0 - - def lastModified(self): - try: - return int(_os.path.getmtime(self._path) * 1000) - except OSError: - return 0 - - def createNewFile(self): - try: - f = _os.open(self._path, _os.O_CREAT | _os.O_EXCL, 0o644) - _os.close(f) - return True - except OSError: - return False - - def delete(self): - try: - if self.isDirectory(): - _os.rmdir(self._path) - else: - _os.remove(self._path) - return True - except OSError: - return False - - def mkdir(self): - try: - _os.mkdir(self._path) - return True - except OSError: - return False - - def mkdirs(self): - try: - _os.makedirs(self._path, exist_ok=True) - return True - except OSError: - return False - - def renameTo(self, dest): - try: - dest_path = dest._path if isinstance(dest, File) else str(dest) - _os.rename(self._path, dest_path) - return True - except OSError: - return False - - def list(self): - try: - return _os.listdir(self._path) - except OSError: - return None - - def listFiles(self, filter=None): - try: - names = _os.listdir(self._path) - files = [File(self._path, n) for n in names] - if filter: - return [f for f in files if filter.accept(f)] - return files - except OSError: - return None - - @staticmethod - def listRoots(): - if _sys.platform == "win32": - import string - return [File(d + ":\\") for d in string.ascii_uppercase if _os.path.exists(d + ":\\")] - return [File("/")] - - def canRead(self): - return _os.access(self._path, _os.R_OK) - - def canWrite(self): - return _os.access(self._path, _os.W_OK) - - def canExecute(self): - return _os.access(self._path, _os.X_OK) - - def setReadable(self, readable, ownerOnly=True): - try: - if readable: - if ownerOnly: - _os.chmod(self._path, _stat.S_IRUSR | (_stat.S_IRGRP if _os.stat(self._path).st_gid else 0)) - else: - _os.chmod(self._path, _stat.S_IRUSR | _stat.S_IRGRP | _stat.S_IROTH) - else: - mode = _os.stat(self._path).st_mode - if ownerOnly: - mode &= ~_stat.S_IRUSR - else: - mode &= ~(_stat.S_IRUSR | _stat.S_IRGRP | _stat.S_IROTH) - _os.chmod(self._path, mode) - return True - except OSError: - return False - - def setWritable(self, writable, ownerOnly=True): - try: - if writable: - if ownerOnly: - _os.chmod(self._path, _stat.S_IWUSR) - else: - _os.chmod(self._path, _stat.S_IWUSR | _stat.S_IWGRP | _stat.S_IWOTH) - else: - mode = _os.stat(self._path).st_mode - if ownerOnly: - mode &= ~_stat.S_IWUSR - else: - mode &= ~(_stat.S_IWUSR | _stat.S_IWGRP | _stat.S_IWOTH) - _os.chmod(self._path, mode) - return True - except OSError: - return False - - def setExecutable(self, executable, ownerOnly=True): - try: - if executable: - if ownerOnly: - _os.chmod(self._path, _stat.S_IXUSR) - else: - _os.chmod(self._path, _stat.S_IXUSR | _stat.S_IXGRP | _stat.S_IXOTH) - else: - mode = _os.stat(self._path).st_mode - if ownerOnly: - mode &= ~_stat.S_IXUSR - else: - mode &= ~(_stat.S_IXUSR | _stat.S_IXGRP | _stat.S_IXOTH) - _os.chmod(self._path, mode) - return True - except OSError: - return False - - def isHidden(self): - return _os.path.basename(self._path).startswith(".") - - def toURL(self): - from urllib.parse import quote - return f"file:{quote(self.getAbsolutePath())}" - - def toURI(self): - return self.toURL() - - def getTotalSpace(self): - try: - st = _os.statvfs(self._path) - return st.f_frsize * st.f_blocks - except OSError: - return 0 - - def getFreeSpace(self): - try: - st = _os.statvfs(self._path) - return st.f_frsize * st.f_bfree - except OSError: - return 0 - - def getUsableSpace(self): - try: - st = _os.statvfs(self._path) - return st.f_frsize * st.f_bavail - except OSError: - return 0 - - def compareTo(self, other) -> int: - if isinstance(other, File): - if self._path < other._path: - return -1 - elif self._path > other._path: - return 1 - return 0 - return -1 - - def equals(self, other): - if isinstance(other, File): - return self._path == other._path - return False - - def hashCode(self): - return hash(self._path) & 0x7FFFFFFF - - def toString(self): - return self._path - - def toPath(self): - return _PathLibPath(self._path) - - @staticmethod - def createTempFile(prefix, suffix=None, directory=None): - import tempfile - if directory: - fd, path = tempfile.mkstemp(prefix=prefix, suffix=suffix or ".tmp", dir=directory) - else: - fd, path = tempfile.mkstemp(prefix=prefix, suffix=suffix or ".tmp") - _os.close(fd) - return File(path) - - def deleteOnExit(self): - import atexit - atexit.register(lambda: self.delete() if self.exists() else None) - - def setLastModified(self, time): - try: - _os.utime(self._path, (time / 1000, time / 1000)) - return True - except Exception: - return False - - def setReadOnly(self): - try: - _os.chmod(self._path, 0o444) - return True - except Exception: - return False - - def isAbsolute(self): - return _os.path.isabs(self._path) - - def getAbsoluteFile(self): - return File(self.getAbsolutePath()) - - def getCanonicalFile(self): - return File(self.getCanonicalPath()) - - def __str__(self): - return self._path - - def __repr__(self): - return f"File({self._path!r})" - - -# ============================================================ -# FileInputStream -# ============================================================ - -class FileInputStream(Closeable): - def __init__(self, file): - super().__init__() - if isinstance(file, File): - path = file.getPath() - else: - path = str(file) - self._path = path - self._fd = None - self._channel = None - self._closed = False - try: - self._fd = _os.open(path, _os.O_RDONLY) - except OSError as e: - raise FileNotFoundException(str(e)) - - def read(self, b=None, off=None, length=None): - if self._closed: - raise IOException("Stream closed") - if b is None: - data = _os.read(self._fd, 1) - return data[0] if data else -1 - if isinstance(b, (bytes, bytearray)): - if off is None: - off = 0 - if length is None: - length = len(b) - off - data = _os.read(self._fd, length) - if not data: - return -1 - n = len(data) - if isinstance(b, bytearray): - b[off:off+n] = data - else: - ba = bytearray(b) - ba[off:off+n] = data - b[:] = bytes(ba) - return n - return -1 - - def skip(self, n): - try: - pos = _os.lseek(self._fd, 0, _os.SEEK_CUR) - end = _os.lseek(self._fd, 0, _os.SEEK_END) - _os.lseek(self._fd, pos, _os.SEEK_SET) - actual = min(n, end - pos) - _os.lseek(self._fd, actual, _os.SEEK_CUR) - return actual - except OSError: - return 0 - - def available(self): - if self._closed: - return 0 - try: - pos = _os.lseek(self._fd, 0, _os.SEEK_CUR) - end = _os.lseek(self._fd, 0, _os.SEEK_END) - _os.lseek(self._fd, pos, _os.SEEK_SET) - return end - pos - except OSError: - return 0 - - def close(self): - if not self._closed: - self._closed = True - if self._fd is not None: - _os.close(self._fd) - self._fd = None - - def getFD(self): - if self._closed: - raise IOException("Stream closed") - return FileDescriptor(self._fd) - - def getChannel(self): - if self._closed: - raise IOException("Stream closed") - if self._channel is None: - import io as _pyio_mod - self._channel = _pyio_mod.FileIO(self._fd, closefd=False) - return self._channel - - def __del__(self): - self.close() - - -# ============================================================ -# FileOutputStream -# ============================================================ - -class FileOutputStream(Closeable, Flushable): - def __init__(self, file, append=False): - super().__init__() - if isinstance(file, File): - path = file.getPath() - elif isinstance(file, FileDescriptor): - self._fd = file._valid_fd - self._path = None - self._closed = False - self._channel = None - return - else: - path = str(file) - self._path = path - self._closed = False - self._channel = None - try: - flags = _os.O_WRONLY | _os.O_CREAT - if append: - flags |= _os.O_APPEND - else: - flags |= _os.O_TRUNC - self._fd = _os.open(path, flags, 0o644) - except OSError as e: - raise FileNotFoundException(str(e)) - - def write(self, b=None, off=None, length=None): - if self._closed: - raise IOException("Stream closed") - if b is None: - return - if isinstance(b, int): - _os.write(self._fd, bytes([b])) - elif isinstance(b, (bytes, bytearray)): - if off is None: - data = bytes(b) - else: - data = bytes(b[off:off+length if length else (len(b)-off)]) - _os.write(self._fd, data) - else: - if off is None: - data = bytes(b) - else: - data = bytes(b[off:off+length if length else (len(b)-off)]) - _os.write(self._fd, data) - - def close(self): - if not self._closed: - self._closed = True - if self._fd is not None: - _os.close(self._fd) - self._fd = None - - def getFD(self): - if self._closed: - raise IOException("Stream closed") - return FileDescriptor(self._fd) - - def getChannel(self): - if self._closed: - raise IOException("Stream closed") - if self._channel is None: - import io as _pyio_mod - self._channel = _pyio_mod.FileIO(self._fd, closefd=False, mode='ab') - return self._channel - - def flush(self): - if self._fd is not None: - try: - _os.fsync(self._fd) - except OSError: - pass - - def __del__(self): - self.close() - - -# ============================================================ -# FilterInputStream / FilterOutputStream (abstract wrappers) -# ============================================================ - -class FilterInputStream(Closeable): - def __init__(self, inp): - super().__init__() - self._in = inp - - def read(self, *args): - return self._in.read(*args) - - def skip(self, n): - return self._in.skip(n) - - def available(self): - return self._in.available() - - def close(self): - self._in.close() - - def mark(self, readlimit): - pass - - def reset(self): - raise IOException("mark/reset not supported") - - def markSupported(self): - return False - - -class FilterOutputStream(Closeable, Flushable): - def __init__(self, out): - super().__init__() - self._out = out - - def write(self, *args): - self._out.write(*args) - - def close(self): - self._out.close() - - def flush(self): - self._out.flush() - - -# ============================================================ -# BufferedInputStream -# ============================================================ - -class BufferedInputStream(FilterInputStream): - def __init__(self, inp, size=8192): - super().__init__(inp) - self._buffer = bytearray() - self._buf_size = size - self._pos = 0 - self._count = 0 - - def read(self, b=None, off=None, length=None): - if b is None: - if self._pos >= self._count: - self._fill() - if self._pos >= self._count: - return -1 - val = self._buffer[self._pos] - self._pos += 1 - return val - if off is None: - off = 0 - if length is None: - length = len(b) - total = 0 - while total < length: - if self._pos >= self._count: - if not self._fill(): - break - chunk = min(length - total, self._count - self._pos) - if isinstance(b, bytearray): - b[off+total:off+total+chunk] = self._buffer[self._pos:self._pos+chunk] - else: - ba = bytearray(b) - ba[off+total:off+total+chunk] = self._buffer[self._pos:self._pos+chunk] - b[:] = bytes(ba) - self._pos += chunk - total += chunk - return total if total > 0 else -1 - - def _fill(self): - tmp = bytearray(self._buf_size) - n = self._in.read(tmp, 0, self._buf_size) - if n is None or n == -1: - return False - if isinstance(n, (bytes, bytearray)): - # defensive: underlying stream returned raw data instead of a count - tmp = bytearray(n) - n = len(tmp) - if not isinstance(n, int) or n <= 0: - return False - self._buffer = bytearray(tmp[:n]) - self._pos = 0 - self._count = n - return True - - def available(self): - buf_avail = self._count - self._pos - if buf_avail < 0: - buf_avail = 0 - try: - underlying = self._in.available() - if underlying and underlying > 0: - return buf_avail + underlying - except Exception: - pass - return buf_avail - - def mark(self, readlimit): - self._mark_pos = self._pos - self._mark_buffer = bytes(self._buffer) - self._mark_count = self._count - self._mark_limit = readlimit - - def reset(self): - if not hasattr(self, '_mark_pos'): - raise IOException("Resetting to invalid mark") - self._buffer = bytearray(self._mark_buffer) - self._count = self._mark_count - self._pos = self._mark_pos - - def markSupported(self): - return True - - -# ============================================================ -# BufferedOutputStream -# ============================================================ - -class BufferedOutputStream(FilterOutputStream): - def __init__(self, out, size=8192): - super().__init__(out) - self._buf = bytearray() - self._buf_size = size - - def write(self, b=None, off=None, length=None): - if b is None: - return - if isinstance(b, int): - self._buf.append(b & 0xFF) - else: - if off is not None and length is not None: - data = b[off:off+length] - else: - data = b - self._buf.extend(data) - if len(self._buf) >= self._buf_size: - self.flush() - - def flush(self): - if self._buf: - self._out.write(bytes(self._buf)) - self._buf.clear() - self._out.flush() - - -# ============================================================ -# InputStreamReader -# ============================================================ - -class InputStreamReader(Closeable): - def __init__(self, inp, charset=None): - super().__init__() - self._in = inp - self._charset = charset or "utf-8" - self._buffer = None - - def read(self, cbuf=None, off=None, length=None): - if cbuf is None: - data = self._in.read() - if data is None or data == -1: - return -1 - return data & 0xFF - if off is None: - off = 0 - if length is None: - length = len(cbuf) - raw = self._in.read(bytearray(length), 0, length) - if raw is None or raw == -1: - return -1 - n = len(raw) if isinstance(raw, (bytes, bytearray)) else raw - if isinstance(raw, int): - return raw - decoded = raw.decode(self._charset, errors='replace') - for i, ch in enumerate(decoded): - if off + i < len(cbuf): - cbuf[off + i] = ch - return len(decoded) - - def getEncoding(self): - return self._charset - - def close(self): - self._in.close() - - -# ============================================================ -# OutputStreamWriter -# ============================================================ - -class OutputStreamWriter(Closeable, Flushable): - def __init__(self, out, charset=None): - super().__init__() - self._out = out - self._charset = charset or "utf-8" - - def write(self, s, off=None, length=None): - if isinstance(s, str): - if off is not None and length is not None: - s = s[off:off+length] - self._out.write(s.encode(self._charset)) - elif isinstance(s, (list, tuple)): - chars = "".join(s) - if off is not None and length is not None: - chars = chars[off:off+length] - self._out.write(chars.encode(self._charset)) - - def flush(self): - self._out.flush() - - def close(self): - self._out.close() - - def getEncoding(self): - return self._charset - - -# ============================================================ -# FileReader -# ============================================================ - -class FileReader(InputStreamReader): - def __init__(self, file): - if isinstance(file, File): - path = file.getPath() - else: - path = str(file) - inp = FileInputStream(path) - super().__init__(inp, "utf-8") - self._file_path = path - - -# ============================================================ -# FileWriter -# ============================================================ - -class FileWriter(OutputStreamWriter): - def __init__(self, file, append=False): - if isinstance(file, File): - path = file.getPath() - append = append - else: - path = str(file) - out = FileOutputStream(path, append) - super().__init__(out, "utf-8") - self._file_path = path - - -# ============================================================ -# BufferedReader -# ============================================================ - -class BufferedReader(Closeable): - def __init__(self, reader, size=8192): - super().__init__() - self._reader = reader - self._buf_size = size - self._buffer = "" - self._pos = 0 - self._closed = False - - def read(self, cbuf=None, off=None, length=None): - if self._closed: - raise IOException("Stream closed") - if cbuf is None: - if self._pos >= len(self._buffer): - self._fill() - if self._pos >= len(self._buffer): - return -1 - ch = ord(self._buffer[self._pos]) - self._pos += 1 - return ch - if off is None: - off = 0 - if length is None: - length = len(cbuf) - total = 0 - while total < length: - if self._pos >= len(self._buffer): - if not self._fill(): - break - chunk = min(length - total, len(self._buffer) - self._pos) - for i in range(chunk): - cbuf[off + total + i] = self._buffer[self._pos + i] - self._pos += chunk - total += chunk - return total if total > 0 else -1 - - def _fill(self): - if hasattr(self._reader, '_in'): - raw = self._reader._in.read(bytearray(self._buf_size), 0, self._buf_size) - if raw is None or raw == -1: - return False - data = bytes(raw) if isinstance(raw, int) else bytes(raw) - self._buffer = data.decode(self._reader.getEncoding() if hasattr(self._reader, 'getEncoding') else "utf-8", errors='replace') - else: - raw = [] - for _ in range(self._buf_size): - ch = self._reader.read() - if ch == -1: - break - raw.append(chr(ch)) - self._buffer = "".join(raw) - self._pos = 0 - return len(self._buffer) > 0 - - def readLine(self): - if self._closed: - raise IOException("Stream closed") - result = [] - while True: - if self._pos >= len(self._buffer): - if not self._fill(): - break - while self._pos < len(self._buffer): - ch = self._buffer[self._pos] - self._pos += 1 - if ch == "\n": - return "".join(result) - if ch == "\r": - if self._pos < len(self._buffer) and self._buffer[self._pos] == "\n": - self._pos += 1 - return "".join(result) - result.append(ch) - return "".join(result) if result else None - - def ready(self): - return self._pos < len(self._buffer) - - def mark(self, readAheadLimit): - self._mark_pos = self._pos - self._mark_buffer = self._buffer - - def reset(self): - if hasattr(self, '_mark_pos'): - self._pos = self._mark_pos - self._buffer = self._mark_buffer - else: - raise IOException("Mark not set") - - def markSupported(self): - return True - - def skip(self, n): - skipped = 0 - while skipped < n: - if self._pos >= len(self._buffer): - if not self._fill(): - break - chunk = min(n - skipped, len(self._buffer) - self._pos) - self._pos += chunk - skipped += chunk - return skipped - - def close(self): - self._closed = True - self._reader.close() - - def lines(self): - line = self.readLine() - while line is not None: - yield line - line = self.readLine() - - -# ============================================================ -# BufferedWriter -# ============================================================ - -class BufferedWriter(Closeable, Flushable): - def __init__(self, writer, size=8192): - super().__init__() - self._writer = writer - self._buf_size = size - self._buffer = [] - - def write(self, s, off=None, length=None): - if isinstance(s, str): - if off is not None and length is not None: - s = s[off:off+length] - self._buffer.append(s) - elif isinstance(s, (list, tuple)): - chars = "".join(s) - if off is not None and length is not None: - chars = chars[off:off+length] - self._buffer.append(chars) - elif isinstance(s, int): - self._buffer.append(chr(s)) - if sum(len(x) for x in self._buffer) >= self._buf_size: - self.flush() - - def newLine(self): - self._buffer.append("\n") - - def append(self, csq=None, start=None, end=None): - if csq is None: - return self - if isinstance(csq, int): - self._buffer.append(chr(csq)) - else: - s = str(csq) - if start is not None and end is not None: - s = s[start:end] - elif start is not None: - s = s[start:] - self._buffer.append(s) - if sum(len(x) for x in self._buffer) >= self._buf_size: - self.flush() - return self - - def check(self): - if getattr(self._writer, '_closed', False): - raise IOException("Stream closed") - - def flush(self): - if self._buffer: - data = "".join(self._buffer) - self._writer.write(data) - self._buffer.clear() - self._writer.flush() - - def close(self): - self.flush() - self._writer.close() - - -# ============================================================ -# PrintWriter -# ============================================================ - -class PrintWriter(Closeable, Flushable): - def __init__(self, out, autoFlush=False): - super().__init__() - if isinstance(out, _pyio.TextIOBase): - self._out = out - else: - self._out = out - self._auto_flush = autoFlush - self._error = False - - def _write_str(self, s): - s = str(s) - if hasattr(self._out, 'write'): - self._out.write(s) - else: - self._out.write(s.encode('utf-8')) - - def print_bool(self, b): - self._write_str(str(b)) - if self._auto_flush: - self.flush() - - def print_char(self, c): - self._write_str(str(c)) - if self._auto_flush: - self.flush() - - def print_char_array(self, c): - self._write_str("".join(c)) - if self._auto_flush: - self.flush() - - def print_double(self, d): - self._write_str(str(d)) - if self._auto_flush: - self.flush() - - def print_float(self, f): - self._write_str(str(f)) - if self._auto_flush: - self.flush() - - def print_int(self, i): - self._write_str(str(i)) - if self._auto_flush: - self.flush() - - def print_long(self, l): - self._write_str(str(l)) - if self._auto_flush: - self.flush() - - def print_obj(self, obj): - self._write_str(str(obj)) - if self._auto_flush: - self.flush() - - def print_str(self, s): - self._write_str(str(s) if s is not None else "null") - if self._auto_flush: - self.flush() - - print = print_str - print_boolean = print_bool - - def println(self, s=None): - if s is None: - self._write_str("\n") - else: - self._write_str(str(s) + "\n") - if self._auto_flush: - self.flush() - - def println_bool(self, b): - self._write_str(str(b) + "\n") - if self._auto_flush: - self.flush() - - def println_char(self, c): - self._write_str(str(c) + "\n") - if self._auto_flush: - self.flush() - - def println_char_array(self, c): - self._write_str("".join(c) + "\n") - if self._auto_flush: - self.flush() - - def println_double(self, d): - self._write_str(str(d) + "\n") - if self._auto_flush: - self.flush() - - def println_float(self, f): - self._write_str(str(f) + "\n") - if self._auto_flush: - self.flush() - - def println_int(self, i): - self._write_str(str(i) + "\n") - if self._auto_flush: - self.flush() - - def println_long(self, l): - self._write_str(str(l) + "\n") - if self._auto_flush: - self.flush() - - def println_obj(self, obj): - self._write_str(str(obj) + "\n") - if self._auto_flush: - self.flush() - - def write(self, s, off=None, length=None): - if isinstance(s, str): - if off is not None and length is not None: - s = s[off:off+length] - self._write_str(s) - else: - self._write_str(str(s)) - - def append(self, csq=None, start=None, end=None): - if csq is None: - csq = "null" - if isinstance(csq, int): - self._write_str(chr(csq)) - else: - s = str(csq) - if start is not None and end is not None: - s = s[start:end] - elif start is not None: - s = s[start:] - self._write_str(s) - return self - - def format(self, fmt, *args): - s = str(fmt) % args if args else str(fmt) - self._write_str(s) - return self - - def printf(self, fmt, *args): - return self.format(fmt, *args) - - def flush(self): - if hasattr(self._out, 'flush'): - self._out.flush() - - def close(self): - self.flush() - if hasattr(self._out, 'close'): - self._out.close() - - def checkError(self): - return self._error - - def setError(self): - self._error = True - - -# ============================================================ -# ByteArrayInputStream -# ============================================================ - -class ByteArrayInputStream(Closeable): - def __init__(self, buf): - super().__init__() - self._buf = bytes(buf) - self._pos = 0 - self._count = len(self._buf) - self._mark = 0 - self._closed = False - - def read(self, b=None, off=None, length=None): - if self._closed: - raise IOException("Stream closed") - if b is None: - if self._pos >= self._count: - return -1 - val = self._buf[self._pos] & 0xFF - self._pos += 1 - return val - if off is None: - off = 0 - if length is None: - length = len(b) - avail = self._count - self._pos - if avail <= 0: - return -1 - n = min(length, avail) - if isinstance(b, bytearray): - b[off:off+n] = self._buf[self._pos:self._pos+n] - else: - ba = bytearray(b) - ba[off:off+n] = self._buf[self._pos:self._pos+n] - b[:] = bytes(ba) - self._pos += n - return n - - def skip(self, n): - if self._closed: - return 0 - avail = self._count - self._pos - actual = min(n, avail) - self._pos += actual - return actual - - def available(self): - if self._closed: - return 0 - return self._count - self._pos - - def close(self): - self._closed = True - - def mark(self, readAheadLimit): - self._mark = self._pos - - def reset(self): - self._pos = self._mark - - def markSupported(self): - return True - - -# ============================================================ -# ByteArrayOutputStream -# ============================================================ - -class ByteArrayOutputStream(Closeable, Flushable): - def __init__(self, size=32): - super().__init__() - self._buf = bytearray() - self._closed = False - - def write(self, b=None, off=None, length=None): - if self._closed: - raise IOException("Stream closed") - if b is None: - return - if isinstance(b, int): - self._buf.append(b & 0xFF) - elif isinstance(b, (bytes, bytearray)): - if off is not None and length is not None: - self._buf.extend(b[off:off+length]) - else: - self._buf.extend(b) - - def toByteArray(self): - return bytes(self._buf) - - def writeTo(self, out): - if self._closed: - raise IOException("Stream closed") - out.write(bytes(self._buf)) - - def size(self): - return len(self._buf) - - def toString(self, charset=None): - if charset: - return bytes(self._buf).decode(charset, errors='replace') - return bytes(self._buf).decode('utf-8', errors='replace') - - def reset(self): - self._buf.clear() - - def close(self): - self._closed = True - - def flush(self): - pass - - -# ============================================================ -# StringReader -# ============================================================ - -class StringReader(Closeable): - def __init__(self, s): - super().__init__() - self._str = s - self._pos = 0 - self._length = len(s) - self._mark = 0 - self._closed = False - - def read(self, cbuf=None, off=None, length=None): - if self._closed: - raise IOException("Stream closed") - if cbuf is None: - if self._pos >= self._length: - return -1 - val = ord(self._str[self._pos]) - self._pos += 1 - return val - if off is None: - off = 0 - if length is None: - length = len(cbuf) - avail = self._length - self._pos - if avail <= 0: - return -1 - n = min(length, avail) - for i in range(n): - cbuf[off + i] = self._str[self._pos + i] - self._pos += n - return n - - def skip(self, n): - avail = self._length - self._pos - actual = min(n, avail) - self._pos += actual - return max(actual, 0) - - def ready(self): - return self._pos < self._length - - def mark(self, readAheadLimit): - self._mark = self._pos - - def reset(self): - self._pos = self._mark - - def markSupported(self): - return True - - def close(self): - self._closed = True - - -# ============================================================ -# StringWriter -# ============================================================ - -class StringWriter(Closeable, Flushable): - def __init__(self): - super().__init__() - self._buffer = [] - self._closed = False - - def write(self, s, off=None, length=None): - if self._closed: - raise IOException("Stream closed") - if isinstance(s, str): - if off is not None and length is not None: - s = s[off:off+length] - self._buffer.append(s) - elif isinstance(s, int): - self._buffer.append(chr(s)) - elif isinstance(s, (list, tuple)): - chars = "".join(s) - if off is not None and length is not None: - chars = chars[off:off+length] - self._buffer.append(chars) - - def flush(self): - pass - - def close(self): - self._closed = True - - def toString(self): - return "".join(self._buffer) - - def getBuffer(self): - return "".join(self._buffer) - - -# ============================================================ -# DataInputStream -# ============================================================ - -class DataInputStream(Closeable): - def __init__(self, inp): - super().__init__() - self._in = inp - - def read(self, b=None): - if b is None: - return self._in.read() - return self._in.read(b) - - def readBoolean(self): - v = self._in.read() - if v == -1: - raise EOFException() - return bool(v) - - def readByte(self): - v = self._in.read() - if v == -1: - raise EOFException() - return (v & 0xFF) if v <= 127 else (v - 256) - - def readChar(self): - hi = self._in.read() - lo = self._in.read() - if hi == -1 or lo == -1: - raise EOFException() - return chr((hi << 8) | lo) - - def readDouble(self): - return _struct.unpack(">d", self._read_bytes(8))[0] - - def readFloat(self): - return _struct.unpack(">f", self._read_bytes(4))[0] - - def readFully(self, b, off=0, length=None): - if length is None: - length = len(b) - total = 0 - while total < length: - n = self._in.read(b, off + total, length - total) - if n == -1: - raise EOFException() - total += n - - def readInt(self): - return _struct.unpack(">i", self._read_bytes(4))[0] - - def readLong(self): - return _struct.unpack(">q", self._read_bytes(8))[0] - - def readShort(self): - v = _struct.unpack(">h", self._read_bytes(2))[0] - return v - - def readUTF(self): - length = self.readUnsignedShort() - data = self._read_bytes(length) - return data.decode("utf-8") - - def readUnsignedByte(self): - v = self._in.read() - if v == -1: - raise EOFException() - return v & 0xFF - - def readUnsignedShort(self): - hi = self._in.read() - lo = self._in.read() - if hi == -1 or lo == -1: - raise EOFException() - return ((hi & 0xFF) << 8) | (lo & 0xFF) - - def skipBytes(self, n): - return self._in.skip(n) - - def _read_bytes(self, n): - buf = bytearray(n) - total = 0 - while total < n: - r = self._in.read(buf, total, n - total) - if r == -1: - raise EOFException() - total += r - return bytes(buf) - - def close(self): - self._in.close() - - -# ============================================================ -# DataOutputStream -# ============================================================ - -import struct as _struct - -class DataOutputStream(Closeable, Flushable): - def __init__(self, out): - super().__init__() - self._out = out - self._written = 0 - - def write(self, b=None, off=None, length=None): - if b is not None: - self._out.write(b, off, length) - self._written += length if length else (len(b) if isinstance(b, (bytes, bytearray)) else 1) - - def writeBoolean(self, v): - self._out.write(int(v)) - - def writeByte(self, v): - self._out.write(v & 0xFF) - - def writeBytes(self, s): - for ch in str(s): - self._out.write(ord(ch) & 0xFF) - - def writeChar(self, v): - self._out.write((v >> 8) & 0xFF) - self._out.write(v & 0xFF) - - def writeChars(self, s): - for ch in str(s): - self.writeChar(ord(ch)) - - def writeDouble(self, v): - self._write_struct(">d", v) - - def writeFloat(self, v): - self._write_struct(">f", v) - - def writeInt(self, v): - self._write_struct(">i", v) - - def writeLong(self, v): - self._write_struct(">q", v) - - def writeShort(self, v): - self._write_struct(">h", v) - - def writeUTF(self, s): - encoded = str(s).encode("utf-8") - self.writeShort(len(encoded)) - for b in encoded: - self._out.write(b) - - def size(self): - return self._written - - def _write_struct(self, fmt, v): - data = _struct.pack(fmt, v) - for b in data: - self._out.write(b) - - def flush(self): - self._out.flush() - - def close(self): - self._out.close() - - -# ============================================================ -# ObjectInputStream / ObjectOutputStream (stub) -# ============================================================ - -class ObjectInputStream(Closeable): - def __init__(self, inp): - super().__init__() - self._in = inp - - def readObject(self): - raise AndroidOnlyError("ObjectInputStream.readObject()") - - def readInt(self): - return DataInputStream(self._in).readInt() - - def close(self): - self._in.close() - - -class ObjectOutputStream(Closeable, Flushable): - def __init__(self, out): - super().__init__() - self._out = out - - def writeObject(self, obj): - raise AndroidOnlyError("ObjectOutputStream.writeObject()") - - def writeInt(self, v): - DataOutputStream(self._out).writeInt(v) - - def flush(self): - self._out.flush() - - def close(self): - self._out.close() - - -# ============================================================ -# PipedInputStream / PipedOutputStream -# ============================================================ - -class PipedInputStream(Closeable): - def __init__(self, src=None, pipe_size=1024): - super().__init__() - self._buffer = bytearray() - self._pipe_size = pipe_size - self._closed = False - self._lock = _threading.RLock() - self._data_available = _threading.Condition(self._lock) - if src: - src.connect(self) - - def connect(self, src): - src._sink = self - - def receive(self, b): - with self._lock: - self._buffer.append(b & 0xFF) - self._data_available.notify() - - def read(self, b=None, off=None, length=None): - with self._lock: - while len(self._buffer) == 0 and not self._closed: - self._data_available.wait(timeout=1) - if self._closed and len(self._buffer) == 0: - return -1 - if b is None: - if len(self._buffer) == 0: - return -1 - return self._buffer.pop(0) - if off is None: - off = 0 - if length is None: - length = len(b) - n = min(length, len(self._buffer)) - for i in range(n): - if isinstance(b, bytearray): - b[off + i] = self._buffer.pop(0) - else: - b[off + i] = self._buffer.pop(0) - return n - - def close(self): - with self._lock: - self._closed = True - self._data_available.notify_all() - - -class PipedOutputStream(Closeable): - def __init__(self, sink=None): - super().__init__() - self._sink = sink - - def connect(self, sink): - self._sink = sink - - def write(self, b, off=None, length=None): - if self._sink is None: - raise IOException("Pipe not connected") - if isinstance(b, int): - self._sink.receive(b) - else: - if off is not None and length is not None: - for i in range(off, off + length): - self._sink.receive(b[i]) - else: - for byte in b: - self._sink.receive(byte) - - def flush(self): - pass - - def close(self): - if self._sink: - self._sink.close() - - -# ============================================================ -# PushbackInputStream -# ============================================================ - -class PushbackInputStream(FilterInputStream): - def __init__(self, inp, size=1): - super().__init__(inp) - self._pushback_buf = bytearray() - self._max_size = size - - def read(self, b=None, off=None, length=None): - if b is None: - if self._pushback_buf: - return self._pushback_buf.pop(0) - return self._in.read() - if off is None: - off = 0 - if length is None: - length = len(b) - total = 0 - while self._pushback_buf and total < length: - if isinstance(b, bytearray): - b[off + total] = self._pushback_buf.pop(0) - else: - ba = bytearray(b) - ba[off + total] = self._pushback_buf.pop(0) - b[:] = bytes(ba) - total += 1 - if total < length: - n = self._in.read(b, off + total, length - total) - if n > 0: - total += n - return total if total > 0 else -1 - - def unread(self, b): - if isinstance(b, int): - if len(self._pushback_buf) >= self._max_size: - raise IOException("Pushback buffer overflow") - self._pushback_buf.append(b & 0xFF) - else: - for byte_val in b: - self.unread(byte_val) - - def close(self): - self._pushback_buf.clear() - self._in.close() - - -# ============================================================ -# SequenceInputStream -# ============================================================ - -def _is_enumeration(obj) -> bool: - """True if obj really exposes hasMoreElements/nextElement (not via Stub).""" - for cls in type(obj).__mro__: - if ('hasMoreElements' in cls.__dict__ - and 'nextElement' in cls.__dict__): - return True - return False - - -class SequenceInputStream(Closeable): - def __init__(self, *args): - super().__init__() - self._streams = [] - self._current_idx = 0 - self._closed = False - if len(args) == 0: - return - if len(args) == 1: - streams = args[0] - if isinstance(streams, (list, tuple)): - self._streams = list(streams) - elif _is_enumeration(streams): - while streams.hasMoreElements(): - self._streams.append(streams.nextElement()) - else: - # single stream - self._streams = [streams] - else: - # SequenceInputStream(s1, s2, ...) - self._streams = list(args) - - def _current(self): - while self._current_idx < len(self._streams): - inp = self._streams[self._current_idx] - if inp is None: - self._current_idx += 1 - continue - return inp - return None - - def _advance(self): - if self._current_idx < len(self._streams): - cur = self._streams[self._current_idx] - if cur is not None: - try: - cur.close() - except Exception: - pass - self._current_idx += 1 - - def read(self, b=None, off=None, length=None): - if self._closed: - raise IOException("Stream closed") - if b is None: - while True: - inp = self._current() - if inp is None: - return -1 - result = inp.read() - if result != -1: - return result - self._advance() - if off is None: - off = 0 - if length is None: - length = len(b) - total = 0 - while total < length: - inp = self._current() - if inp is None: - break - n = inp.read(b, off + total, length - total) - if n is None or n == -1: - self._advance() - continue - if n == 0: - self._advance() - continue - total += n - return total if total > 0 else -1 - - def available(self): - inp = self._current() - if inp is None: - return 0 - try: - return inp.available() - except Exception: - return 0 - - def close(self): - self._closed = True - for s in self._streams: - if s is not None: - try: - s.close() - except Exception: - pass - - -# ============================================================ -# RandomAccessFile -# ============================================================ - -class RandomAccessFile(Closeable, Flushable): - def __init__(self, file, mode="r"): - super().__init__() - if isinstance(file, File): - path = file.getPath() - else: - path = str(file) - self._path = path - self._mode = mode - self._closed = False - py_modes = {"r": "rb", "rw": "r+b", "rws": "r+b", "rwd": "r+b"} - py_mode = py_modes.get(mode, "r+b") - try: - if "w" in mode and mode != "r": - self._file = open(path, py_mode) - else: - self._file = open(path, py_mode) - except FileNotFoundError as e: - if "w" in mode: - self._file = open(path, "w+b") - else: - raise FileNotFoundException(str(e)) - - def seek(self, pos): - self._file.seek(pos) - - def getFilePointer(self): - return self._file.tell() - - def length(self): - pos = self._file.tell() - self._file.seek(0, 2) - end = self._file.tell() - self._file.seek(pos) - return end - - def read(self, b=None, off=None, length=None): - if b is None: - data = self._file.read(1) - return data[0] if data else -1 - if off is None: - off = 0 - if length is None: - length = len(b) - data = self._file.read(length) - if not data: - return -1 - n = len(data) - if isinstance(b, bytearray): - b[off:off+n] = data - else: - ba = bytearray(b) - ba[off:off+n] = data - b[:] = bytes(ba) - return n - - def readBoolean(self): - v = self.read() - if v == -1: - raise EOFException() - return bool(v) - - def readByte(self): - v = self.read() - if v == -1: - raise EOFException() - return (v & 0xFF) if v <= 127 else (v - 256) - - def readChar(self): - hi = self.read() - lo = self.read() - if hi == -1 or lo == -1: - raise EOFException() - return chr((hi << 8) | lo) - - def readDouble(self): - data = self._read_bytes(8) - return _struct.unpack(">d", data)[0] - - def readFloat(self): - data = self._read_bytes(4) - return _struct.unpack(">f", data)[0] - - def readInt(self): - data = self._read_bytes(4) - return _struct.unpack(">i", data)[0] - - def readLine(self): - result = [] - while True: - ch = self.read() - if ch == -1: - break - if ch == 0x0A: - break - if ch == 0x0D: - next_ch = self.read() - if next_ch != 0x0A: - self.seek(self.getFilePointer() - 1) - break - result.append(chr(ch)) - return "".join(result) if result else None - - def readLong(self): - data = self._read_bytes(8) - return _struct.unpack(">q", data)[0] - - def readShort(self): - data = self._read_bytes(2) - return _struct.unpack(">h", data)[0] - - def readUnsignedByte(self): - v = self.read() - if v == -1: - raise EOFException() - return v & 0xFF - - def readUnsignedShort(self): - hi = self.read() - lo = self.read() - if hi == -1 or lo == -1: - raise EOFException() - return ((hi & 0xFF) << 8) | (lo & 0xFF) - - def readUTF(self): - length = self.readUnsignedShort() - data = self._read_bytes(length) - return data.decode("utf-8") - - def write(self, b=None, off=None, length=None): - if b is None: - return - if isinstance(b, int): - self._file.write(bytes([b & 0xFF])) - else: - if off is not None and length is not None: - self._file.write(bytes(b[off:off+length])) - else: - self._file.write(bytes(b)) - - def writeBoolean(self, v): - self.write(int(v)) - - def writeByte(self, v): - self.write(v & 0xFF) - - def writeBytes(self, s): - for ch in str(s): - self.write(ord(ch) & 0xFF) - - def writeChar(self, v): - self.write((v >> 8) & 0xFF) - self.write(v & 0xFF) - - def writeChars(self, s): - for ch in str(s): - self.writeChar(ord(ch)) - - def writeDouble(self, v): - data = _struct.pack(">d", v) - self._file.write(data) - - def writeFloat(self, v): - data = _struct.pack(">f", v) - self._file.write(data) - - def writeInt(self, v): - data = _struct.pack(">i", v) - self._file.write(data) - - def writeLong(self, v): - data = _struct.pack(">q", v) - self._file.write(data) - - def writeShort(self, v): - data = _struct.pack(">h", v) - self._file.write(data) - - def writeUTF(self, s): - encoded = str(s).encode("utf-8") - self.writeShort(len(encoded)) - self._file.write(encoded) - - def _read_bytes(self, n): - data = self._file.read(n) - if len(data) < n: - raise EOFException() - return data - - def setLength(self, new_length): - pos = self.getFilePointer() - self._file.truncate(new_length) - if pos > new_length: - self.seek(new_length) - - def close(self): - if not self._closed: - self._closed = True - self._file.close() - - def getChannel(self): - return self._file - - def flush(self): - self._file.flush() - - def __del__(self): - self.close() \ No newline at end of file diff --git a/android_pyapi/java/io_ext.py b/android_pyapi/java/io_ext.py deleted file mode 100644 index 99c2874..0000000 --- a/android_pyapi/java/io_ext.py +++ /dev/null @@ -1,1414 +0,0 @@ -""" -Java java.io extended standard library APIs implemented in Python. -Extends java.io with ObjectStreamField, ObjectStreamConstants, ObjectInput, etc. -""" - -from __future__ import annotations - -from .._core import Stub -from ..java.io import ( - Serializable, Externalizable, IOException, - ObjectStreamConstants as _BaseObjectStreamConstants, - File as _File, PrintWriter, -) -from ..java.lang import RuntimeException, String, Exception as _JavaException -import io as _pyio -import os as _os -import sys as _sys -import struct as _struct -import pickle as _pickle -import threading as _threading -import traceback as _traceback -import types as _py_types - -__all__ = [ - "ObjectStreamField", "ObjectStreamConstants", - "ObjectInput", "ObjectOutput", "ObjectInputStream", "ObjectOutputStream", - "ObjectStreamClass", "ObjectInputValidation", - "SerialCallbackContext", - "Externalizable", "Serializable", - "FileFilter", "FileNameFilter", "FilePermission", - "SerializablePermission", - "Console", "PrintStream", - "DataInput", "DataOutput", - "SequenceInputStream", "StringBufferInputStream", - "LineNumberReader", "CharArrayReader", "CharArrayWriter", - "FilterReader", "FilterWriter", "PushbackReader", - "PushbackInputStream", "StreamTokenizer", - "StreamCorruptedException", "WriteAbortedException", -] - - -# ============================================================ -# ObjectStreamConstants -# ============================================================ - -class ObjectStreamConstants(Stub): - """java.io.ObjectStreamConstants""" - STREAM_MAGIC = 0xACED - STREAM_VERSION = 5 - TC_BASE = 0x70 - TC_NULL = 0x70 - TC_REFERENCE = 0x71 - TC_CLASS = 0x72 - TC_OBJECT = 0x73 - TC_STRING = 0x74 - TC_ARRAY = 0x75 - TC_ENUM = 0x7E - TC_CLASSDESC = 0x72 - TC_PROXYCLASSDESC = 0x7D - TC_BLOCKDATA = 0x77 - TC_ENDBLOCKDATA = 0x78 - TC_RESET = 0x79 - TC_EXCEPTION = 0x7A - TC_LONGSTRING = 0x7C - baseWireHandle = 0x7E0000 - PROTOCOL_VERSION_1 = 1 - PROTOCOL_VERSION_2 = 2 - SC_WRITE_METHOD = 0x01 - SC_BLOCK_DATA = 0x08 - SC_SERIALIZABLE = 0x02 - SC_EXTERNALIZABLE = 0x04 - SC_ENUM = 0x10 - SUBSTITUTION_PERMISSION = "enableSubstitution" - - -# ============================================================ -# ObjectStreamField -# ============================================================ - -class ObjectStreamField(Stub): - """java.io.ObjectStreamField""" - def __init__(self, name, field_type, unshared=False): - super().__init__() - self._name = name - self._type = field_type - self._unshared = unshared - - def getName(self): - return self._name - - def getType(self): - return self._type - - def getTypeString(self): - return self._type.__name__ if hasattr(self._type, '__name__') else str(self._type) - - def getTypeCode(self): - return self.getTypeString()[0].upper() - - def isUnshared(self): - return self._unshared - - def isPrimitive(self): - return self._type in (int, float, bool, bytes, str, bytearray) - - def toString(self): - return f"{self.getTypeString()} {self._name}" - - -# ============================================================ -# ObjectStreamClass -# ============================================================ - -class ObjectStreamClass(Stub): - """java.io.ObjectStreamClass""" - _descriptors = {} - - def __init__(self, cls=None): - super().__init__() - self._cls = cls - self._name = cls.__name__ if cls else "" - self._serial_version_uid = hash(cls) if cls else 0 - self._fields = [] - self._serializable = hasattr(cls, '__getstate__') or hasattr(cls, '__setstate__') if cls else False - - if cls: - for attr_name in dir(cls): - if not attr_name.startswith('_'): - attr_type = type(getattr(cls, attr_name, None)) - self._fields.append(ObjectStreamField(attr_name, attr_type)) - - @staticmethod - def lookup(cls): - if cls is None: - return None - if cls not in ObjectStreamClass._descriptors: - ObjectStreamClass._descriptors[cls] = ObjectStreamClass(cls) - return ObjectStreamClass._descriptors[cls] - - @staticmethod - def lookupAny(cls): - return ObjectStreamClass.lookup(cls) - - def getName(self): - return self._name - - def getSerialVersionUID(self): - return self._serial_version_uid - - def getFields(self): - return self._fields - - def getField(self, name): - for f in self._fields: - if f.getName() == name: - return f - return None - - def forClass(self): - return self._cls - - def isSerializable(self): - return self._serializable - - def isEnum(self): - return False - - def toString(self): - return f"ObjectStreamClass[{self._name}]" - - -# ============================================================ -# ObjectInputValidation -# ============================================================ - -class ObjectInputValidation(Stub): - """java.io.ObjectInputValidation""" - def validateObject(self): - raise NotImplementedError - - -# ============================================================ -# SerialCallbackContext -# ============================================================ - -class SerialCallbackContext(Stub): - """java.io.SerialCallbackContext""" - def __init__(self, obj, desc): - super().__init__() - self._obj = obj - self._desc = desc - self._used = False - - def getObj(self): - return self._obj - - def getDesc(self): - return self._desc - - def setUsed(self): - self._used = True - - def isUsed(self): - return self._used - - -# ============================================================ -# ObjectInput / ObjectOutput interfaces -# ============================================================ - -class DataInput(Stub): - """java.io.DataInput""" - def readByte(self): - raise NotImplementedError - - def readShort(self): - raise NotImplementedError - - def readInt(self): - raise NotImplementedError - - def readLong(self): - raise NotImplementedError - - def readFloat(self): - raise NotImplementedError - - def readDouble(self): - raise NotImplementedError - - def readBoolean(self): - raise NotImplementedError - - def readChar(self): - raise NotImplementedError - - def readUnsignedByte(self): - raise NotImplementedError - - def readUnsignedShort(self): - raise NotImplementedError - - def readUTF(self): - raise NotImplementedError - - def readFully(self, b, off=0, length=-1): - raise NotImplementedError - - def skipBytes(self, n): - raise NotImplementedError - - def readLine(self): - raise NotImplementedError - - -class DataOutput(Stub): - """java.io.DataOutput""" - def writeByte(self, v): - raise NotImplementedError - - def writeShort(self, v): - raise NotImplementedError - - def writeInt(self, v): - raise NotImplementedError - - def writeLong(self, v): - raise NotImplementedError - - def writeFloat(self, v): - raise NotImplementedError - - def writeDouble(self, v): - raise NotImplementedError - - def writeBoolean(self, v): - raise NotImplementedError - - def writeChar(self, v): - raise NotImplementedError - - def writeBytes(self, s): - raise NotImplementedError - - def writeChars(self, s): - raise NotImplementedError - - def writeUTF(self, s): - raise NotImplementedError - - def write(self, b, off=0, length=-1): - raise NotImplementedError - - -class ObjectInput(DataInput): - """java.io.ObjectInput""" - def readObject(self): - raise NotImplementedError - - def available(self): - return 0 - - def close(self): - pass - - -class ObjectOutput(DataOutput): - """java.io.ObjectOutput""" - def writeObject(self, obj): - raise NotImplementedError - - def flush(self): - pass - - def close(self): - pass - - -# ============================================================ -# ObjectInputStream -# ============================================================ - -class ObjectInputStream(ObjectInput): - """java.io.ObjectInputStream""" - def __init__(self, inp): - super().__init__() - self._input = inp - self._data = inp.read() if hasattr(inp, 'read') else b"" - self._pos = 0 - self._handles = {} - self._next_handle = ObjectStreamConstants.baseWireHandle - self._validation_list = [] - self._resolved = {} - - def readObject(self): - if self._pos >= len(self._data): - raise StreamCorruptedException("No data") - try: - obj = _pickle.loads(self._data[self._pos:]) - return obj - except Exception: - return None - - def readByte(self): - val = self._data[self._pos] if self._pos < len(self._data) else 0 - self._pos += 1 - return val - - def readInt(self): - if self._pos + 4 > len(self._data): - raise StreamCorruptedException() - val = _struct.unpack_from(">i", self._data, self._pos)[0] - self._pos += 4 - return val - - def readShort(self): - if self._pos + 2 > len(self._data): - raise StreamCorruptedException() - val = _struct.unpack_from(">h", self._data, self._pos)[0] - self._pos += 2 - return val - - def readLong(self): - if self._pos + 8 > len(self._data): - raise StreamCorruptedException() - val = _struct.unpack_from(">q", self._data, self._pos)[0] - self._pos += 8 - return val - - def readFloat(self): - if self._pos + 4 > len(self._data): - raise StreamCorruptedException() - val = _struct.unpack_from(">f", self._data, self._pos)[0] - self._pos += 4 - return val - - def readDouble(self): - if self._pos + 8 > len(self._data): - raise StreamCorruptedException() - val = _struct.unpack_from(">d", self._data, self._pos)[0] - self._pos += 8 - return val - - def readBoolean(self): - return self.readByte() != 0 - - def readChar(self): - if self._pos + 2 > len(self._data): - raise StreamCorruptedException() - val = _struct.unpack_from(">H", self._data, self._pos)[0] - self._pos += 2 - return chr(val) - - def readUnsignedByte(self): - return self.readByte() & 0xFF - - def readUnsignedShort(self): - return self.readShort() & 0xFFFF - - def readUTF(self): - length = self.readUnsignedShort() - if self._pos + length > len(self._data): - raise StreamCorruptedException() - val = self._data[self._pos:self._pos + length].decode("utf-8") - self._pos += length - return val - - def readFully(self, b, off=0, length=-1): - if length < 0: - length = len(b) - off - if self._pos + length > len(self._data): - raise StreamCorruptedException() - for i in range(length): - b[off + i] = self._data[self._pos + i] - self._pos += length - - def skipBytes(self, n): - actual = min(n, len(self._data) - self._pos) - self._pos += actual - return actual - - def readLine(self): - result = [] - while self._pos < len(self._data): - c = chr(self._data[self._pos]) - self._pos += 1 - if c == "\n": - break - if c == "\r": - if self._pos < len(self._data) and chr(self._data[self._pos]) == "\n": - self._pos += 1 - break - result.append(c) - return "".join(result) - - def available(self): - return len(self._data) - self._pos - - def close(self): - self._input = None - self._data = b"" - self._pos = 0 - - def defaultReadObject(self): - return self.readObject() - - def readFields(self): - return self.readObject() - - def registerValidation(self, obj, prio): - self._validation_list.append((obj, prio)) - - def resolveClass(self, desc): - return desc.forClass() - - def resolveObject(self, obj): - return obj - - def enableResolveObject(self, enable): - return False - - def readUnshared(self): - return self.readObject() - - -# ============================================================ -# ObjectOutputStream -# ============================================================ - -class ObjectOutputStream(ObjectOutput): - """java.io.ObjectOutputStream""" - def __init__(self, out): - super().__init__() - self._output = out - self._buffer = bytearray() - self._handles = {} - self._next_handle = ObjectStreamConstants.baseWireHandle - self._block_data_mode = False - self._extended_block_data_mode = False - - def writeObject(self, obj): - data = _pickle.dumps(obj) - self._buffer.extend(data) - self._flush_buffer() - - def writeByte(self, v): - self._buffer.extend(_struct.pack(">b", v if isinstance(v, int) else ord(v))) - - def writeInt(self, v): - self._buffer.extend(_struct.pack(">i", int(v))) - - def writeShort(self, v): - self._buffer.extend(_struct.pack(">h", int(v))) - - def writeLong(self, v): - self._buffer.extend(_struct.pack(">q", int(v))) - - def writeFloat(self, v): - self._buffer.extend(_struct.pack(">f", float(v))) - - def writeDouble(self, v): - self._buffer.extend(_struct.pack(">d", float(v))) - - def writeBoolean(self, v): - self._buffer.extend(_struct.pack(">?", bool(v))) - - def writeChar(self, v): - if isinstance(v, str): - v = ord(v) - self._buffer.extend(_struct.pack(">H", int(v))) - - def writeBytes(self, s): - if isinstance(s, str): - self._buffer.extend(s.encode()) - elif isinstance(s, bytes): - self._buffer.extend(s) - elif isinstance(s, bytearray): - self._buffer.extend(s) - - def writeChars(self, s): - for c in s: - self._buffer.extend(_struct.pack(">H", ord(c))) - - def writeUTF(self, s): - encoded = s.encode("utf-8") - self.writeShort(len(encoded)) - self._buffer.extend(encoded) - - def write(self, b, off=0, length=-1): - if isinstance(b, int): - self._buffer.append(b & 0xFF) - elif isinstance(b, bytes): - if length < 0: - length = len(b) - off - self._buffer.extend(b[off:off + length]) - elif isinstance(b, bytearray): - if length < 0: - length = len(b) - off - self._buffer.extend(b[off:off + length]) - - def flush(self): - self._flush_buffer() - if hasattr(self._output, 'flush'): - self._output.flush() - - def close(self): - self.flush() - if hasattr(self._output, 'close'): - self._output.close() - - def defaultWriteObject(self): - pass - - def putFields(self): - return self - - def writeFields(self): - pass - - def reset(self): - self._handles = {} - self._next_handle = ObjectStreamConstants.baseWireHandle - self._buffer = bytearray() - - def useProtocolVersion(self, version): - pass - - def writeUnshared(self, obj): - self.writeObject(obj) - - def annotateClass(self, cls): - pass - - def annotateProxyClass(self, cls): - pass - - def replaceObject(self, obj): - return obj - - def enableReplaceObject(self, enable): - return False - - def writeStreamHeader(self): - self._buffer.extend(_struct.pack(">HH", - ObjectStreamConstants.STREAM_MAGIC, - ObjectStreamConstants.STREAM_VERSION)) - - def _flush_buffer(self): - if self._buffer: - data = bytes(self._buffer) - if hasattr(self._output, 'write'): - self._output.write(data) - self._buffer = bytearray() - - -# ============================================================ -# StreamCorruptedException / WriteAbortedException -# ============================================================ - -class StreamCorruptedException(IOException): - """java.io.StreamCorruptedException""" - def __init__(self, message=None): - super().__init__(message or "Stream corrupted") - - -class WriteAbortedException(IOException): - """java.io.WriteAbortedException""" - def __init__(self, message, exception): - super().__init__(message) - self._exception = exception - - def getException(self): - return self._exception - - -# ============================================================ -# FileFilter / FileNameFilter -# ============================================================ - -class FileFilter(Stub): - """java.io.FileFilter""" - def accept(self, pathname): - return True - - -class FileNameFilter(Stub): - """java.io.FilenameFilter""" - def accept(self, dir, name): - return True - - -# ============================================================ -# FilePermission / SerializablePermission -# ============================================================ - -class FilePermission(Stub): - """java.io.FilePermission""" - def __init__(self, path, actions): - super().__init__() - self._path = path - self._actions = actions - - def getName(self): - return self._path - - def getActions(self): - return self._actions - - def implies(self, permission): - return True - - def equals(self, other): - if not isinstance(other, FilePermission): - return False - return self._path == other._path and self._actions == other._actions - - def hashCode(self): - return hash((self._path, self._actions)) - - -class SerializablePermission(Stub): - """java.io.SerializablePermission""" - def __init__(self, name, actions=None): - super().__init__() - self._name = name - self._actions = actions or "" - - def getName(self): - return self._name - - def getActions(self): - return self._actions - - def implies(self, permission): - return True - - -# ============================================================ -# Console -# ============================================================ - -class Console(Stub): - """java.io.Console""" - def __init__(self): - super().__init__() - - @staticmethod - def console(): - return Console() - - def readLine(self, fmt=None, *args): - if fmt: - print(fmt % args, end="") - return input() - - def readPassword(self, fmt=None, *args): - import getpass - if fmt: - print(fmt % args, end="") - return getpass.getpass("") - - def format(self, fmt, *args): - _sys.stdout.write(fmt % args) - _sys.stdout.flush() - return self - - def printf(self, format, *args): - return self.format(format, *args) - - def flush(self): - _sys.stdout.flush() - - def reader(self): - return _pyio.BufferedReader(_pyio.FileIO(_sys.stdin.fileno())) - - def writer(self): - return _pyio.TextIOWrapper(_pyio.BufferedWriter( - _pyio.FileIO(_sys.stdout.fileno(), "w") - )) - - -# ============================================================ -# PrintStream -# ============================================================ - -class PrintStream(Stub): - """java.io.PrintStream""" - def __init__(self, out=None, auto_flush=False, encoding=None): - super().__init__() - self._out = out or _sys.stdout - self._auto_flush = auto_flush - self._encoding = encoding or "utf-8" - self._error = False - - def print(self, obj=""): - _sys.stdout.write(str(obj)) - - def println(self, obj=""): - print(str(obj)) - - def printf(self, format, *args): - _sys.stdout.write(format % args) - if self._auto_flush: - _sys.stdout.flush() - return self - - def format(self, format_, *args): - return self.printf(format_, *args) - - def write(self, b, off=0, length=-1): - if isinstance(b, int): - self._out.write(bytes([b])) - elif isinstance(b, bytes): - if length < 0: - self._out.write(b[off:]) - else: - self._out.write(b[off:off + length]) - elif isinstance(b, str): - self._out.write(b) - if self._auto_flush: - self._out.flush() - - def flush(self): - self._out.flush() - - def close(self): - self.flush() - - def checkError(self): - return self._error - - def setError(self): - self._error = True - - def clearError(self): - self._error = False - - def append(self, c): - if isinstance(c, str): - self._out.write(c) - else: - self._out.write(str(c)) - return self - - -# ============================================================ -# SequenceInputStream -# ============================================================ - -class SequenceInputStream(Stub): - """java.io.SequenceInputStream""" - def __init__(self, *args): - super().__init__() - if len(args) == 1: - self._enumerator = iter(args[0]) - self._current = next(self._enumerator, None) - elif len(args) == 2: - self._enumerator = iter(args) - self._current = args[0] - else: - self._enumerator = iter([]) - self._current = None - - def read(self, b=None, off=0, length=-1): - while self._current is not None: - if hasattr(self._current, 'read'): - if b is None: - data = self._current.read() - else: - if length < 0: - data = self._current.read(len(b) - off) - else: - data = self._current.read(min(length, len(b) - off)) - if data: - if b is not None: - for i, byte in enumerate(data): - b[off + i] = byte - return len(data) - self._current = next(self._enumerator, None) - else: - self._current = next(self._enumerator, None) - return -1 - - def close(self): - self._current = None - - -# ============================================================ -# StringBufferInputStream -# ============================================================ - -class StringBufferInputStream(Stub): - """java.io.StringBufferInputStream""" - def __init__(self, s): - super().__init__() - self._string = s - self._pos = 0 - self._count = len(s) - - def read(self, b=None, off=0, length=-1): - if self._pos >= self._count: - return -1 - if b is None: - val = ord(self._string[self._pos]) if self._pos < self._count else -1 - self._pos += 1 - return val - if length < 0: - length = len(b) - off - available = self._count - self._pos - to_read = min(available, length) - for i in range(to_read): - b[off + i] = ord(self._string[self._pos + i]) - self._pos += to_read - return to_read - - def available(self): - return self._count - self._pos - - def reset(self): - self._pos = 0 - - -# ============================================================ -# LineNumberReader -# ============================================================ - -class LineNumberReader(Stub): - """java.io.LineNumberReader""" - def __init__(self, reader, sz=8192): - super().__init__() - self._reader = reader - self._line_number = 0 - self._buffer = "" - self._pos = 0 - self._marked_pos = 0 - self._marked_line = 0 - self._sz = sz - - def readLine(self): - result = [] - while self._pos < len(self._buffer) or self._fill_buffer(): - if self._pos < len(self._buffer): - c = self._buffer[self._pos] - self._pos += 1 - if c == "\n": - self._line_number += 1 - return "".join(result) - if c == "\r": - self._line_number += 1 - if self._pos < len(self._buffer) and self._buffer[self._pos] == "\n": - self._pos += 1 - return "".join(result) - result.append(c) - else: - break - if not result: - return None - return "".join(result) - - def getLineNumber(self): - return self._line_number - - def setLineNumber(self, line_number): - self._line_number = line_number - - def read(self, cbuf=None, off=0, length=-1): - if cbuf is None: - if self._pos < len(self._buffer) or self._fill_buffer(): - if self._pos < len(self._buffer): - c = self._buffer[self._pos] - self._pos += 1 - return ord(c) - return -1 - count = 0 - while count < (length if length >= 0 else len(cbuf) - off): - if self._pos >= len(self._buffer) and not self._fill_buffer(): - break - cbuf[off + count] = self._buffer[self._pos] - self._pos += 1 - count += 1 - return count if count > 0 else -1 - - def close(self): - if self._reader and hasattr(self._reader, 'close'): - self._reader.close() - - def mark(self, read_ahead_limit): - self._marked_pos = self._pos - self._marked_line = self._line_number - - def reset(self): - self._pos = self._marked_pos - self._line_number = self._marked_line - - def skip(self, n): - actual = 0 - while actual < n: - if self._pos >= len(self._buffer) and not self._fill_buffer(): - break - self._pos += 1 - actual += 1 - return actual - - def ready(self): - return self._pos < len(self._buffer) - - def _fill_buffer(self): - if self._reader and hasattr(self._reader, 'read'): - data = self._reader.read(self._sz) - if isinstance(data, str): - self._buffer = data - elif isinstance(data, bytes): - self._buffer = data.decode('utf-8') - else: - self._buffer = "" - self._pos = 0 - return len(self._buffer) > 0 - return False - - -# ============================================================ -# CharArrayReader / CharArrayWriter -# ============================================================ - -class CharArrayReader(Stub): - """java.io.CharArrayReader""" - def __init__(self, buf, offset=0, length=None): - super().__init__() - self._buf = list(buf) - if length is not None: - self._buf = self._buf[offset:offset + length] - else: - self._buf = self._buf[offset:] - self._pos = 0 - self._marked_pos = 0 - - def read(self, cbuf=None, off=0, length=-1): - if self._pos >= len(self._buf): - return -1 - if cbuf is None: - val = ord(self._buf[self._pos]) if isinstance(self._buf[self._pos], str) else self._buf[self._pos] - self._pos += 1 - return val - if length < 0: - length = len(cbuf) - off - available = len(self._buf) - self._pos - to_read = min(available, length) - for i in range(to_read): - cbuf[off + i] = ( - ord(self._buf[self._pos + i]) - if isinstance(self._buf[self._pos + i], str) - else self._buf[self._pos + i] - ) - self._pos += to_read - return to_read - - def close(self): - self._buf = [] - - def mark(self, read_ahead_limit): - self._marked_pos = self._pos - - def reset(self): - self._pos = self._marked_pos - - def skip(self, n): - actual = min(n, len(self._buf) - self._pos) - self._pos += actual - return actual - - def ready(self): - return self._pos < len(self._buf) - - -class CharArrayWriter(Stub): - """java.io.CharArrayWriter""" - def __init__(self, initial_size=32): - super().__init__() - self._buf = [] - - def write(self, c, off=0, length=-1): - if isinstance(c, str): - self._buf.append(c) - elif isinstance(c, (list, tuple)): - if length < 0: - length = len(c) - off - self._buf.extend(c[off:off + length]) - elif isinstance(c, int): - self._buf.append(chr(c)) - - def writeTo(self, out): - if hasattr(out, 'write'): - out.write(self.toString()) - - def toString(self): - return "".join(str(x) for x in self._buf) - - def size(self): - return len(self._buf) - - def toCharArray(self): - return list(self.toString()) - - def flush(self): - pass - - def close(self): - pass - - def reset(self): - self._buf = [] - - -# ============================================================ -# FilterReader / FilterWriter -# ============================================================ - -class FilterReader(Stub): - """java.io.FilterReader""" - def __init__(self, reader): - super().__init__() - self._reader = reader - - def read(self, cbuf=None, off=0, length=-1): - if self._reader: - return self._reader.read(cbuf, off, length) - return -1 - - def close(self): - if self._reader: - self._reader.close() - - def mark(self, read_limit): - if self._reader and hasattr(self._reader, 'mark'): - self._reader.mark(read_limit) - - def reset(self): - if self._reader and hasattr(self._reader, 'reset'): - self._reader.reset() - - def ready(self): - if self._reader and hasattr(self._reader, 'ready'): - return self._reader.ready() - return False - - def skip(self, n): - if self._reader and hasattr(self._reader, 'skip'): - return self._reader.skip(n) - return 0 - - -class FilterWriter(Stub): - """java.io.FilterWriter""" - def __init__(self, writer): - super().__init__() - self._writer = writer - - def write(self, c, off=0, length=-1): - if self._writer: - if isinstance(c, str): - self._writer.write(c) - elif isinstance(c, int): - self._writer.write(chr(c)) - - def flush(self): - if self._writer: - self._writer.flush() - - def close(self): - if self._writer: - self._writer.close() - - -# ============================================================ -# PushbackReader -# ============================================================ - -class PushbackReader(FilterReader): - """java.io.PushbackReader""" - def __init__(self, reader, size=1): - super().__init__(reader) - self._pushback_buf = [] - self._pushback_size = size - - def read(self, cbuf=None, off=0, length=-1): - if self._pushback_buf: - ch = self._pushback_buf.pop() - if cbuf is None: - return ord(ch) if isinstance(ch, str) else ch - cbuf[off] = ord(ch) if isinstance(ch, str) else ch - return 1 - return super().read(cbuf, off, length) - - def unread(self, c): - if len(self._pushback_buf) >= self._pushback_size: - raise IOException("Pushback buffer full") - if isinstance(c, (list, tuple)): - for ch in reversed(c): - self._pushback_buf.append(ch) - else: - self._pushback_buf.append(c) - - -# ============================================================ -# PushbackInputStream -# ============================================================ - -class PushbackInputStream(Stub): - """java.io.PushbackInputStream""" - def __init__(self, inp, size=1): - super().__init__() - self._input = inp - self._pushback_buf = bytearray() - self._pushback_size = size - - def read(self, b=None, off=0, length=-1): - if self._pushback_buf and b is None: - return self._pushback_buf.pop(0) - if b is not None: - count = 0 - while count < (length if length >= 0 else len(b) - off): - if self._pushback_buf: - b[off + count] = self._pushback_buf.pop(0) - elif self._input: - val = self._input.read(1) - if val == -1 or val == b"" or val is None: - break - b[off + count] = val[0] if isinstance(val, bytes) else val - else: - break - count += 1 - return count if count > 0 else -1 - if self._pushback_buf: - return self._pushback_buf.pop(0) - if self._input: - val = self._input.read(1) - if val is not None and val != -1: - return val[0] if isinstance(val, bytes) else val - return -1 - - def unread(self, b, off=0, length=-1): - if isinstance(b, int): - if len(self._pushback_buf) >= self._pushback_size: - raise IOException("Pushback buffer full") - self._pushback_buf.insert(0, b & 0xFF) - elif isinstance(b, (bytes, bytearray)): - if length < 0: - length = len(b) - off - if len(self._pushback_buf) + length > self._pushback_size: - raise IOException("Pushback buffer full") - for i in range(length - 1, -1, -1): - self._pushback_buf.insert(0, b[off + i]) - - def available(self): - return len(self._pushback_buf) + (self._input.available() if self._input else 0) - - def close(self): - if self._input: - self._input.close() - self._pushback_buf = bytearray() - - -# ============================================================ -# StreamTokenizer -# ============================================================ - -class StreamTokenizer(Stub): - """java.io.StreamTokenizer""" - def __init__(self, reader=None, inp=None): - super().__init__() - if reader: - self._reader = reader - elif inp: - import io - self._reader = io.TextIOWrapper(io.BufferedReader(inp)) - else: - self._reader = None - - self.ttype = 0 - self.sval = None - self.nval = 0.0 - - self._reset_syntax() - - def _reset_syntax(self): - self._ordinary_chars = set() - self._word_chars = set() - self._whitespace_chars = set(range(0x20)) - self._comment_char = None - self._quote_char = None - self._parse_numbers = True - self._lower_case_mode = False - self._slash_slash_comments = False - self._slash_star_comments = False - self._eol_is_significant = False - self._lower_case_mode = False - self._pushback = None - - def resetSyntax(self): - self._reset_syntax() - - def wordChars(self, low, high): - for c in range(low, high + 1): - self._word_chars.add(c) - - def whitespaceChars(self, low, high): - for c in range(low, high + 1): - self._whitespace_chars.add(c) - - def ordinaryChars(self, low, high): - for c in range(low, high + 1): - self._ordinary_chars.add(c) - - def ordinaryChar(self, ch): - self._ordinary_chars.add(ch) - - def commentChar(self, ch): - self._comment_char = ch - - def quoteChar(self, ch): - self._quote_char = ch - - def parseNumbers(self): - self._parse_numbers = True - - def eolIsSignificant(self, flag): - self._eol_is_significant = flag - - def slashSlashComments(self, flag): - self._slash_slash_comments = flag - - def slashStarComments(self, flag): - self._slash_star_comments = flag - - def lowerCaseMode(self, flag): - self._lower_case_mode = flag - - def pushBack(self): - self._pushback = self.ttype - - def lineno(self): - return 1 - - def toString(self): - if self.ttype == -1: - return "EOF" - if self.ttype == -3: - return f"WORD={self.sval}" - if self.ttype == -2: - return f"NUMBER={self.nval}" - return f"CHAR={chr(self.ttype) if self.ttype < 256 else '?'}" - - def nextToken(self): - if self._pushback is not None: - pb = self._pushback - self._pushback = None - self.ttype = pb - return self.ttype - - if self._reader is None: - self.ttype = -1 # TT_EOF - return -1 - - ch = self._read_char() - if ch is None: - self.ttype = -1 - return -1 - - # Check for newlines - if ch == "\n": - if self._eol_is_significant: - self.ttype = 10 # TT_EOL - return 10 - return self.nextToken() - - # Whitespace - if ord(ch) in self._whitespace_chars: - return self.nextToken() - - # Comment char - if self._comment_char is not None and ord(ch) == self._comment_char: - while ch is not None and ch != "\n": - ch = self._read_char() - return self.nextToken() - - # Quote char - if self._quote_char is not None and ord(ch) == self._quote_char: - buf = [] - while True: - ch = self._read_char() - if ch is None or ord(ch) == self._quote_char: - break - buf.append(ch) - self.sval = "".join(buf) - if self._lower_case_mode: - self.sval = self.sval.lower() - self.ttype = ord(self._quote_char) - return self.ttype - - # Number - if self._parse_numbers and (ch.isdigit() or ch in ('-', '.')): - buf = [ch] - if ch in ('-', '.'): - next_ch = self._peek_char() - if next_ch is not None and (next_ch.isdigit() or next_ch == '.'): - buf.append(self._read_char()) - while True: - ch = self._read_char() - if ch is None or not (ch.isdigit() or ch == '.' or ch in ('e', 'E', '-', '+')): - if ch is not None: - self._unread_char(ch) - break - buf.append(ch) - num_str = "".join(buf) - try: - self.nval = float(num_str) - except ValueError: - self.nval = 0.0 - self.ttype = -2 # TT_NUMBER - return -2 - - # Word chars - if ord(ch) in self._word_chars: - buf = [ch] - while True: - next_ch = self._peek_char() - if next_ch is not None and ord(next_ch) in self._word_chars: - buf.append(self._read_char()) - else: - break - self.sval = "".join(buf) - if self._lower_case_mode: - self.sval = self.sval.lower() - self.ttype = -3 # TT_WORD - return -3 - - # Ordinary char - self.ttype = ord(ch) - return ord(ch) - - def _read_char(self): - if self._reader is None: - return None - try: - if hasattr(self._reader, 'read'): - c = self._reader.read(1) - if c and isinstance(c, bytes): - c = c.decode('utf-8') - return c if c else None - return None - except Exception: - return None - - def _peek_char(self): - if self._reader is None: - return None - try: - c = self._reader.read(1) - if c and isinstance(c, bytes): - c = c.decode('utf-8') - if c: - self._unread_char(c) - return c if c else None - except Exception: - return None - - def _unread_char(self, c): - if hasattr(self._reader, 'seek'): - self._reader.seek(self._reader.tell() - 1) - - TT_EOF = -1 - TT_EOL = 10 - TT_NUMBER = -2 - TT_WORD = -3 \ No newline at end of file diff --git a/android_pyapi/java/io_file.py b/android_pyapi/java/io_file.py deleted file mode 100644 index 5e4b7d6..0000000 --- a/android_pyapi/java/io_file.py +++ /dev/null @@ -1,1451 +0,0 @@ -""" -java.io 文件扩展组件 — File扩展, FileSystem, UnixFileSystem, -FileDescriptor扩展, FileInputStream扩展, FileChannel扩展, -FilePermission扩展, FileVisitor, FileAlreadyExistsException等 -方法签名完全对齐 Java SDK -""" - -from __future__ import annotations - -import errno -import io -import os -import stat -import struct -import sys -import time -from typing import (Any, Callable, Dict, IO, Iterator, List, Optional, - Set, Tuple, Union) - -from .._core import Stub, AndroidOnlyError - -__all__ = [ - "FileSystem", "UnixFileSystem", "WinNTFileSystem", - "FileStatus", "FileAccess", "FileAccessModes", - "FileDescriptor", "FileInputStream", "FileOutputStream", - "RandomAccessFile", "FileLock", "FileChannel", - "FilePermission", "FileFilter", "FileNameFilter", - "FileSystemProvider", "FileTreeWalker", "FileTypeDetector", - "FileVisitOption", "FileVisitResult", "FileVisitor", - "SimpleFileVisitor", - "FileAlreadyExistsException", "FileSystemAlreadyExistsException", - "FileSystemException", "FileSystemLoopException", - "FileSystemNotFoundException", "AccessDeniedException", - "AtomicMoveNotSupportedException", "DirectoryIteratorException", - "DirectoryNotEmptyException", "FileSystemOperationException", - "InvalidPathException", "ProviderMismatchException", - "ProviderNotFoundException", "ReadOnlyFileSystemException", - "UserPrincipalLookupService", "UserPrincipal", "GroupPrincipal", - "FileAttribute", "FileAttributeView", "FileStore", - "FileStoreAttributeView", "FileSystems", -] - - -# ═══════════════════════════════════════════════════════════════ -# 文件系统常量 -# ═══════════════════════════════════════════════════════════════ - -ACCESS_READ = 0x01 -ACCESS_WRITE = 0x02 -ACCESS_EXECUTE = 0x04 - -CREATE_FILE = 0x01 -CREATE_DIRECTORY = 0x02 -CREATE_LINK = 0x04 -CREATE_SYMBOLIC_LINK = 0x08 - -DELETE_FILE = 0x01 -DELETE_DIRECTORY = 0x02 -DELETE_ON_CLOSE = 0x04 - -OPEN_READ = 0x01 -OPEN_WRITE = 0x02 -OPEN_APPEND = 0x04 -OPEN_TRUNCATE = 0x08 -OPEN_CREATE = 0x10 -OPEN_CREATE_NEW = 0x20 -OPEN_DELETE_ON_CLOSE = 0x40 -OPEN_SPARSE = 0x80 -OPEN_SYNC = 0x100 -OPEN_DSYNC = 0x200 - -COPY_FILE = 0x01 -COPY_DIRECTORY = 0x02 -COPY_SYMBOLIC_LINK = 0x04 -COPY_ATTRIBUTES = 0x08 -COPY_REPLACE_EXISTING = 0x10 -COPY_COPY_ATTRIBUTES = 0x08 -COPY_NO_FOLLOW_LINKS = 0x04 - -MOVE_FILE = 0x01 -MOVE_DIRECTORY = 0x02 -MOVE_REPLACE_EXISTING = 0x04 -MOVE_ATOMIC = 0x08 - -LINK_SYMBOLIC = 0x01 -LINK_HARD = 0x02 - -WALK_FILE_TREE = 0x01 -WALK_FOLLOW_LINKS = 0x02 -WALK_MAX_DEPTH_UNLIMITED = -1 - -FILE_TYPE_UNKNOWN = 0 -FILE_TYPE_REGULAR = 1 -FILE_TYPE_DIRECTORY = 2 -FILE_TYPE_SYMBOLIC_LINK = 3 -FILE_TYPE_CHARACTER_DEVICE = 4 -FILE_TYPE_BLOCK_DEVICE = 5 -FILE_TYPE_FIFO = 6 -FILE_TYPE_SOCKET = 7 - -FILE_ATTRIBUTE_READ_ONLY = 0x01 -FILE_ATTRIBUTE_HIDDEN = 0x02 -FILE_ATTRIBUTE_SYSTEM = 0x04 -FILE_ATTRIBUTE_DIRECTORY = 0x08 -FILE_ATTRIBUTE_ARCHIVE = 0x10 -FILE_ATTRIBUTE_NORMAL = 0x20 -FILE_ATTRIBUTE_TEMPORARY = 0x40 -FILE_ATTRIBUTE_SPARSE = 0x80 -FILE_ATTRIBUTE_REPARSE = 0x100 -FILE_ATTRIBUTE_COMPRESSED = 0x200 -FILE_ATTRIBUTE_OFFLINE = 0x400 -FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x800 -FILE_ATTRIBUTE_ENCRYPTED = 0x1000 -FILE_ATTRIBUTE_VIRTUAL = 0x2000 -FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x4000 -FILE_ATTRIBUTE_NO_SCRUB = 0x8000 -FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000 -FILE_ATTRIBUTE_PINNED = 0x80000 -FILE_ATTRIBUTE_UNPINNED = 0x100000 -FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000 - - -# ═══════════════════════════════════════════════════════════════ -# 异常类 -# ═══════════════════════════════════════════════════════════════ - -class FileSystemException(OSError): - """FileSystemException — 文件系统异常""" - - def __init__(self, file_: str = "", other: str = "", - reason: str = ""): - self._file = file_ - self._other = other - self._reason = reason - msg = f"{file_}" - if other: - msg += f" -> {other}" - if reason: - msg += f": {reason}" - super().__init__(msg) - - def getFile(self) -> str: - return self._file - - def getOtherFile(self) -> str: - return self._other - - def getReason(self) -> str: - return self._reason - - -class FileAlreadyExistsException(FileSystemException): - """FileAlreadyExistsException — 文件已存在""" - pass - - -class FileSystemAlreadyExistsException(FileSystemException): - """FileSystemAlreadyExistsException — 文件系统已存在""" - pass - - -class FileSystemLoopException(FileSystemException): - """FileSystemLoopException — 文件系统循环""" - pass - - -class FileSystemNotFoundException(FileSystemException): - """FileSystemNotFoundException — 文件系统未找到""" - pass - - -class AccessDeniedException(FileSystemException): - """AccessDeniedException — 访问被拒绝""" - pass - - -class AtomicMoveNotSupportedException(FileSystemException): - """AtomicMoveNotSupportedException — 原子移动不支持""" - pass - - -class DirectoryIteratorException(FileSystemException): - """DirectoryIteratorException — 目录迭代异常""" - pass - - -class DirectoryNotEmptyException(FileSystemException): - """DirectoryNotEmptyException — 目录非空""" - pass - - -class FileSystemOperationException(FileSystemException): - """FileSystemOperationException — 文件系统操作异常""" - pass - - -class InvalidPathException(FileSystemException): - """InvalidPathException — 路径无效""" - pass - - -class ProviderMismatchException(FileSystemException): - """ProviderMismatchException — 提供器不匹配""" - pass - - -class ProviderNotFoundException(FileSystemException): - """ProviderNotFoundException — 提供器未找到""" - pass - - -class ReadOnlyFileSystemException(FileSystemException): - """ReadOnlyFileSystemException — 只读文件系统""" - pass - - -# ═══════════════════════════════════════════════════════════════ -# FileStatus / FileAccess / FileAccessModes -# ═══════════════════════════════════════════════════════════════ - -class FileStatus(Stub): - """FileStatus — 文件状态""" - - def __init__(self, type_: int = FILE_TYPE_UNKNOWN, - size: int = 0, lastModified: int = 0, - lastAccess: int = 0, creationTime: int = 0, - isSymbolicLink: bool = False, - device: int = 0, inode: int = 0, - mode: int = 0, uid: int = 0, gid: int = 0): - super().__init__() - self._type = type_ - self._size = size - self._last_modified = lastModified - self._last_access = lastAccess - self._creation_time = creationTime - self._is_symlink = isSymbolicLink - self._device = device - self._inode = inode - self._mode = mode - self._uid = uid - self._gid = gid - - def getType(self) -> int: - return self._type - - def getSize(self) -> int: - return self._size - - def getLastModified(self) -> int: - return self._last_modified - - def getLastAccess(self) -> int: - return self._last_access - - def getCreationTime(self) -> int: - return self._creation_time - - def isSymbolicLink(self) -> bool: - return self._is_symlink - - def getDevice(self) -> int: - return self._device - - def getInode(self) -> int: - return self._inode - - def getMode(self) -> int: - return self._mode - - def getUid(self) -> int: - return self._uid - - def getGid(self) -> int: - return self._gid - - def toString(self) -> str: - return f"FileStatus(type={self._type}, size={self._size})" - - -class FileAccess(Stub): - """FileAccess — 文件访问""" - - def __init__(self, canRead: bool = True, canWrite: bool = True, - canExecute: bool = False): - super().__init__() - self._can_read = canRead - self._can_write = canWrite - self._can_execute = canExecute - - def canRead(self) -> bool: - return self._can_read - - def canWrite(self) -> bool: - return self._can_write - - def canExecute(self) -> bool: - return self._can_execute - - def toString(self) -> str: - return f"FileAccess(r={self._can_read}, w={self._can_write}, x={self._can_execute})" - - -class FileAccessModes(Stub): - """FileAccessModes — 文件访问模式""" - - def __init__(self, read: bool = False, write: bool = False, - append: bool = False, truncate: bool = False): - super().__init__() - self._read = read - self._write = write - self._append = append - self._truncate = truncate - - def isRead(self) -> bool: - return self._read - - def isWrite(self) -> bool: - return self._write - - def isAppend(self) -> bool: - return self._append - - def isTruncate(self) -> bool: - return self._truncate - - def toString(self) -> str: - return f"FileAccessModes(r={self._read}, w={self._write})" - - -# ═══════════════════════════════════════════════════════════════ -# FileDescriptor (扩展) -# ═══════════════════════════════════════════════════════════════ - -class FileDescriptor(Stub): - """FileDescriptor — 文件描述符 (完整版)""" - - _in: Optional["FileDescriptor"] = None - _out: Optional["FileDescriptor"] = None - _err: Optional["FileDescriptor"] = None - - @staticmethod - def in_() -> "FileDescriptor": - if FileDescriptor._in is None: - fd = FileDescriptor() - fd._fd = 0 - FileDescriptor._in = fd - return FileDescriptor._in - - @staticmethod - def out() -> "FileDescriptor": - if FileDescriptor._out is None: - fd = FileDescriptor() - fd._fd = 1 - FileDescriptor._out = fd - return FileDescriptor._out - - @staticmethod - def err() -> "FileDescriptor": - if FileDescriptor._err is None: - fd = FileDescriptor() - fd._fd = 2 - FileDescriptor._err = fd - return FileDescriptor._err - - def __init__(self): - super().__init__() - self._fd: int = -1 - self._closed = False - self._readonly = False - - def valid(self) -> bool: - return self._fd >= 0 and not self._closed - - def getInt(self) -> int: - return self._fd - - def setInt(self, fd: int): - self._fd = fd - self._closed = False - - def close(self): - if self.valid(): - try: - os.close(self._fd) - except Exception: - pass - self._closed = True - self._fd = -1 - - def isClosed(self) -> bool: - return self._closed - - def sync(self): - if self.valid(): - try: - os.fsync(self._fd) - except Exception: - pass - - def toString(self) -> str: - return f"FileDescriptor(fd={self._fd})" - - def equals(self, other: Any) -> bool: - if not isinstance(other, FileDescriptor): - return False - return self._fd == other._fd - - def hashCode(self) -> int: - return self._fd & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# FileInputStream (扩展) -# ═══════════════════════════════════════════════════════════════ - -class FileInputStream(Stub): - """FileInputStream — 文件输入流 (完整版)""" - - def __init__(self, file: Any): - super().__init__() - if isinstance(file, int): - self._fd = FileDescriptor() - self._fd.setInt(file) - self._native_fd = file - elif isinstance(file, FileDescriptor): - self._fd = file - self._native_fd = file.getInt() - elif isinstance(file, str): - self._fd = FileDescriptor() - self._native_fd = os.open(file, os.O_RDONLY) - self._fd.setInt(self._native_fd) - else: - path = str(file) if hasattr(file, 'getPath') else str(file) - self._fd = FileDescriptor() - self._native_fd = os.open(path, os.O_RDONLY) - self._fd.setInt(self._native_fd) - self._closed = False - self._pos = 0 - - def read(self) -> int: - if self._closed or self._native_fd < 0: - return -1 - try: - data = os.read(self._native_fd, 1) - if not data: - return -1 - self._pos += 1 - return data[0] - except Exception: - return -1 - - def readBytes(self, b: bytearray, off: int, len_: int) -> int: - if self._closed or self._native_fd < 0: - return -1 - try: - data = os.read(self._native_fd, len_) - if not data: - return -1 - actual = len(data) - b[off:off + actual] = data - self._pos += actual - return actual - except Exception: - return -1 - - def available(self) -> int: - return 0 - - def skip(self, n: int) -> int: - if self._closed or self._native_fd < 0: - return 0 - try: - os.lseek(self._native_fd, n, os.SEEK_CUR) - self._pos += n - return n - except Exception: - return 0 - - def close(self): - if not self._closed: - self._closed = True - if self._native_fd >= 0: - try: - os.close(self._native_fd) - except Exception: - pass - self._fd.close() - - def getFD(self) -> Optional[FileDescriptor]: - return self._fd if self._fd.valid() else None - - def getChannel(self) -> "FileChannel": - return FileChannel(fd=self._native_fd, mode="r") - - def isClosed(self) -> bool: - return self._closed - - def toString(self) -> str: - return f"FileInputStream(fd={self._native_fd})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# FileOutputStream (扩展) -# ═══════════════════════════════════════════════════════════════ - -class FileOutputStream(Stub): - """FileOutputStream — 文件输出流 (完整版)""" - - def __init__(self, file: Any, append: bool = False): - super().__init__() - flags = os.O_WRONLY | os.O_CREAT - if append: - flags |= os.O_APPEND - else: - flags |= os.O_TRUNC - - if isinstance(file, int): - self._fd = FileDescriptor() - self._fd.setInt(file) - self._native_fd = file - elif isinstance(file, FileDescriptor): - self._fd = file - self._native_fd = file.getInt() - elif isinstance(file, str): - self._fd = FileDescriptor() - self._native_fd = os.open(file, flags, 0o644) - self._fd.setInt(self._native_fd) - else: - path = str(file) if hasattr(file, 'getPath') else str(file) - self._fd = FileDescriptor() - self._native_fd = os.open(path, flags, 0o644) - self._fd.setInt(self._native_fd) - self._closed = False - self._append_mode = append - - def write(self, b: Union[int, bytes, bytearray]): - if self._closed or self._native_fd < 0: - return - if isinstance(b, int): - os.write(self._native_fd, bytes([b])) - else: - os.write(self._native_fd, bytes(b)) - - def writeBytes(self, b: bytes, off: int, len_: int): - if self._closed or self._native_fd < 0: - return - os.write(self._native_fd, b[off:off + len_]) - - def flush(self): - pass - - def close(self): - if not self._closed: - self._closed = True - if self._native_fd >= 0: - try: - os.close(self._native_fd) - except Exception: - pass - self._fd.close() - - def getFD(self) -> Optional[FileDescriptor]: - return self._fd if self._fd.valid() else None - - def getChannel(self) -> "FileChannel": - return FileChannel(fd=self._native_fd, mode="rw") - - def isClosed(self) -> bool: - return self._closed - - def toString(self) -> str: - return f"FileOutputStream(fd={self._native_fd})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# RandomAccessFile -# ═══════════════════════════════════════════════════════════════ - -class RandomAccessFile(Stub): - """RandomAccessFile — 随机访问文件""" - - def __init__(self, file: Any, mode: str = "r"): - super().__init__() - self._mode = mode - if isinstance(file, str): - self._path = file - elif hasattr(file, 'getPath'): - self._path = file.getPath() - else: - self._path = str(file) - - if "r" in mode and "w" in mode: - flags = os.O_RDWR | os.O_CREAT - elif "w" in mode: - flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC - elif "rws" in mode: - flags = os.O_RDWR | os.O_SYNC - elif "rwd" in mode: - flags = os.O_RDWR | os.O_DSYNC - else: - flags = os.O_RDONLY - - self._fd = os.open(self._path, flags, 0o644) - self._file_descriptor = FileDescriptor() - self._file_descriptor.setInt(self._fd) - self._closed = False - - def read(self) -> int: - if self._closed: - return -1 - try: - data = os.read(self._fd, 1) - return data[0] if data else -1 - except Exception: - return -1 - - def readBytes(self, b: bytearray, off: int, len_: int) -> int: - if self._closed: - return -1 - try: - data = os.read(self._fd, len_) - if not data: - return -1 - b[off:off + len(data)] = data - return len(data) - except Exception: - return -1 - - def write(self, b: Union[int, bytes, bytearray]): - if self._closed: - return - if isinstance(b, int): - os.write(self._fd, bytes([b])) - else: - os.write(self._fd, bytes(b)) - - def writeBytes(self, b: bytes, off: int, len_: int): - if not self._closed: - os.write(self._fd, b[off:off + len_]) - - def getFilePointer(self) -> int: - return os.lseek(self._fd, 0, os.SEEK_CUR) - - def seek(self, pos: int): - os.lseek(self._fd, pos, os.SEEK_SET) - - def length(self) -> int: - return os.lseek(self._fd, 0, os.SEEK_END) - - def setLength(self, newLength: int): - os.truncate(self._fd, newLength) - - def skipBytes(self, n: int) -> int: - try: - current = os.lseek(self._fd, 0, os.SEEK_CUR) - file_size = os.lseek(self._fd, 0, os.SEEK_END) - os.lseek(self._fd, current, os.SEEK_SET) - actual = min(n, file_size - current) - os.lseek(self._fd, actual, os.SEEK_CUR) - return max(0, actual) - except Exception: - return 0 - - def close(self): - if not self._closed: - self._closed = True - try: - os.close(self._fd) - except Exception: - pass - - def getFD(self) -> FileDescriptor: - return self._file_descriptor - - def getChannel(self) -> "FileChannel": - return FileChannel(fd=self._fd, mode=self._mode) - - def readLine(self) -> Optional[str]: - if self._closed: - return None - result = [] - while True: - try: - b = os.read(self._fd, 1) - if not b: - break - c = b[0] - if c == ord('\n'): - break - if c == ord('\r'): - b2 = os.read(self._fd, 1) - if b2 and b2[0] != ord('\n'): - os.lseek(self._fd, -1, os.SEEK_CUR) - break - result.append(chr(c)) - except Exception: - break - return "".join(result) if result else None - - def toString(self) -> str: - return f"RandomAccessFile(path={self._path}, mode={self._mode})" - - -# ═══════════════════════════════════════════════════════════════ -# FileLock -# ═══════════════════════════════════════════════════════════════ - -class FileLock(Stub): - """FileLock — 文件锁""" - - def __init__(self, channel: "FileChannel", position: int = 0, - size: int = 0, shared: bool = False): - super().__init__() - self._channel = channel - self._position = position - self._size = size - self._shared = shared - self._valid = True - - def position(self) -> int: - return self._position - - def size(self) -> int: - return self._size - - def isShared(self) -> bool: - return self._shared - - def isValid(self) -> bool: - return self._valid - - def release(self): - self._valid = False - - def close(self): - self.release() - - def channel(self) -> "FileChannel": - return self._channel - - def toString(self) -> str: - return f"FileLock(pos={self._position}, shared={self._shared})" - - -# ═══════════════════════════════════════════════════════════════ -# FileChannel (扩展) -# ═══════════════════════════════════════════════════════════════ - -class FileChannel(Stub): - """FileChannel — 文件通道 (完整版)""" - - def __init__(self, fd: int = -1, mode: str = "r", - path: str = ""): - super().__init__() - self._fd = fd - self._mode = mode - self._path = path - self._open = fd >= 0 - self._position = 0 - - def read(self, dst: bytearray, offset: int = 0, length: int = 0) -> int: - if not self._open: - return -1 - if length <= 0: - length = len(dst) - offset - try: - data = os.read(self._fd, length) - if not data: - return -1 - dst[offset:offset + len(data)] = data - self._position += len(data) - return len(data) - except Exception: - return -1 - - def write(self, src: Union[bytes, bytearray], offset: int = 0, length: int = 0) -> int: - if not self._open: - return 0 - if length <= 0: - data = bytes(src) - else: - data = bytes(src[offset:offset + length]) - try: - written = os.write(self._fd, data) - self._position += written - return written - except Exception: - return 0 - - def position(self) -> int: - return self._position - - def setPosition(self, newPosition: int) -> "FileChannel": - if self._open: - os.lseek(self._fd, newPosition, os.SEEK_SET) - self._position = newPosition - return self - - def size(self) -> int: - if not self._open: - return 0 - current = os.lseek(self._fd, 0, os.SEEK_CUR) - size_ = os.lseek(self._fd, 0, os.SEEK_END) - os.lseek(self._fd, current, os.SEEK_SET) - return size_ - - def truncate(self, newSize: int) -> "FileChannel": - if self._open: - os.truncate(self._fd, newSize) - return self - - def force(self, metaData: bool): - if self._open: - os.fsync(self._fd) - - def close(self): - if self._open: - self._open = False - try: - os.close(self._fd) - except Exception: - pass - - def isOpen(self) -> bool: - return self._open - - def lock(self, position: int = 0, size: int = 0, shared: bool = False) -> FileLock: - return FileLock(channel=self, position=position, size=size, shared=shared) - - def tryLock(self, position: int = 0, size: int = 0, shared: bool = False) -> Optional[FileLock]: - return FileLock(channel=self, position=position, size=size, shared=shared) - - def transferTo(self, position: int, count: int, target: "FileChannel") -> int: - return 0 - - def transferFrom(self, src: "FileChannel", position: int, count: int) -> int: - return 0 - - def map(self, mode: int, position: int, size: int) -> Any: - return None - - def toString(self) -> str: - return f"FileChannel(fd={self._fd}, open={self._open})" - - -# ═══════════════════════════════════════════════════════════════ -# FilePermission (扩展) -# ═══════════════════════════════════════════════════════════════ - -class FilePermission(Stub): - """FilePermission — 文件权限""" - - def __init__(self, path: str = "", actions: str = ""): - super().__init__() - self._path = path - self._actions = actions - self._action_set: Set[str] = set(actions.split(",")) if actions else set() - - def getPath(self) -> str: - return self._path - - def getActions(self) -> str: - return self._actions - - def implies(self, permission: "FilePermission") -> bool: - return self._action_set.issuperset(permission._action_set) - - def impliesByPath(self, path: str) -> bool: - return self._path == path - - def toString(self) -> str: - return f'FilePermission("{self._path}", "{self._actions}")' - - def equals(self, other: Any) -> bool: - if not isinstance(other, FilePermission): - return False - return self._path == other._path and self._actions == other._actions - - def hashCode(self) -> int: - return hash((self._path, self._actions)) & 0x7FFFFFFF - - -# ═══════════════════════════════════════════════════════════════ -# FileFilter / FileNameFilter -# ═══════════════════════════════════════════════════════════════ - -class FileFilter(Stub): - """FileFilter — 文件过滤器""" - - def accept(self, pathname: Any) -> bool: - return True - - def toString(self) -> str: - return f"FileFilter@{hex(id(self))}" - - -class FileNameFilter(Stub): - """FileNameFilter — 文件名过滤器""" - - def accept(self, dir_: Any, name: str) -> bool: - return True - - def toString(self) -> str: - return f"FileNameFilter@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# FileVisitOption / FileVisitResult / FileVisitor / SimpleFileVisitor -# ═══════════════════════════════════════════════════════════════ - -class FileVisitOption(Stub): - """FileVisitOption — 文件访问选项""" - FOLLOW_LINKS = 0 - - def __init__(self, value: int = FOLLOW_LINKS): - super().__init__() - self._value = value - - def getValue(self) -> int: - return self._value - - -class FileVisitResult(Stub): - """FileVisitResult — 文件访问结果""" - CONTINUE = 0 - TERMINATE = 1 - SKIP_SUBTREE = 2 - SKIP_SIBLINGS = 3 - - def __init__(self, value: int = CONTINUE): - super().__init__() - self._value = value - - def getValue(self) -> int: - return self._value - - -class FileVisitor(Stub): - """FileVisitor — 文件访问器接口""" - - def preVisitDirectory(self, dir_: Any, attrs: Any) -> FileVisitResult: - return FileVisitResult(FileVisitResult.CONTINUE) - - def visitFile(self, file_: Any, attrs: Any) -> FileVisitResult: - return FileVisitResult(FileVisitResult.CONTINUE) - - def visitFileFailed(self, file_: Any, exc: Exception) -> FileVisitResult: - return FileVisitResult(FileVisitResult.CONTINUE) - - def postVisitDirectory(self, dir_: Any, exc: Optional[Exception]) -> FileVisitResult: - return FileVisitResult(FileVisitResult.CONTINUE) - - def toString(self) -> str: - return f"FileVisitor@{hex(id(self))}" - - -class SimpleFileVisitor(FileVisitor): - """SimpleFileVisitor — 简单文件访问器""" - - def preVisitDirectory(self, dir_: Any, attrs: Any) -> FileVisitResult: - return FileVisitResult(FileVisitResult.CONTINUE) - - def visitFile(self, file_: Any, attrs: Any) -> FileVisitResult: - return FileVisitResult(FileVisitResult.CONTINUE) - - def visitFileFailed(self, file_: Any, exc: Exception) -> FileVisitResult: - return FileVisitResult(FileVisitResult.CONTINUE) - - def postVisitDirectory(self, dir_: Any, exc: Optional[Exception]) -> FileVisitResult: - return FileVisitResult(FileVisitResult.CONTINUE) - - def toString(self) -> str: - return f"SimpleFileVisitor@{hex(id(self))}" - - -# ═══════════════════════════════════════════════════════════════ -# FileTreeWalker / FileTypeDetector / FileSystemProvider -# ═══════════════════════════════════════════════════════════════ - -class FileTreeWalker(Stub): - """FileTreeWalker — 文件树遍历器""" - - def __init__(self, visitor: Optional[FileVisitor] = None, - options: List[FileVisitOption] = None, - maxDepth: int = WALK_MAX_DEPTH_UNLIMITED): - super().__init__() - self._visitor = visitor or SimpleFileVisitor() - self._options = options or [] - self._max_depth = maxDepth - - def walk(self, start: Any): - self._walk_recursive(str(start), 0) - - def _walk_recursive(self, path: str, depth: int): - if self._max_depth >= 0 and depth > self._max_depth: - return - if not os.path.exists(path): - return - if os.path.isdir(path): - result = self._visitor.preVisitDirectory(path, None) - if result.getValue() == FileVisitResult.TERMINATE: - return - if result.getValue() != FileVisitResult.SKIP_SUBTREE: - try: - for entry in sorted(os.listdir(path)): - child = os.path.join(path, entry) - self._walk_recursive(child, depth + 1) - except Exception as e: - self._visitor.visitFileFailed(path, e) - self._visitor.postVisitDirectory(path, None) - else: - try: - self._visitor.visitFile(path, None) - except Exception as e: - self._visitor.visitFileFailed(path, e) - - def toString(self) -> str: - return f"FileTreeWalker(maxDepth={self._max_depth})" - - -class FileTypeDetector(Stub): - """FileTypeDetector — 文件类型检测器""" - - def probeContentType(self, path: Any) -> Optional[str]: - return None - - def toString(self) -> str: - return f"FileTypeDetector@{hex(id(self))}" - - -class FileSystemProvider(Stub): - """FileSystemProvider — 文件系统提供器""" - - SCHEME_FILE = "file" - - def __init__(self, scheme: str = SCHEME_FILE): - super().__init__() - self._scheme = scheme - - def getScheme(self) -> str: - return self._scheme - - def newFileSystem(self, uri: Any, env: Dict[str, Any] = None) -> Any: - return None - - def getFileSystem(self, uri: Any) -> Any: - return None - - def getPath(self, uri: Any) -> str: - return str(uri) - - def newFileChannel(self, path: Any, options: Set[str] = None, - attrs: List[Any] = None) -> Optional[FileChannel]: - return None - - def newInputStream(self, path: Any, options: Set[str] = None) -> Optional[FileInputStream]: - return None - - def newOutputStream(self, path: Any, options: Set[str] = None) -> Optional[FileOutputStream]: - return None - - def toString(self) -> str: - return f"FileSystemProvider(scheme={self._scheme})" - - -# ═══════════════════════════════════════════════════════════════ -# FileStore / FileStoreAttributeView / FileAttribute / FileAttributeView -# ═══════════════════════════════════════════════════════════════ - -class FileAttribute(Stub): - """FileAttribute — 文件属性""" - - def __init__(self, name: str = "", value: Any = None): - super().__init__() - self._name = name - self._value = value - - def name(self) -> str: - return self._name - - def value(self) -> Any: - return self._value - - def toString(self) -> str: - return f"FileAttribute(name={self._name})" - - -class FileAttributeView(Stub): - """FileAttributeView — 文件属性视图基类""" - - def name(self) -> str: - return "basic" - - def toString(self) -> str: - return f"FileAttributeView(name={self.name()})" - - -class FileStoreAttributeView(Stub): - """FileStoreAttributeView — 文件存储属性视图基类""" - - def name(self) -> str: - return "basic" - - def toString(self) -> str: - return f"FileStoreAttributeView(name={self.name()})" - - -class FileStore(Stub): - """FileStore — 文件存储""" - - def __init__(self, path: str = "/", - type_: str = "ext4", - isReadOnly: bool = False): - super().__init__() - self._path = path - self._type = type_ - self._is_read_only = isReadOnly - - def name(self) -> str: - return self._path - - def type(self) -> str: - return self._type - - def isReadOnly(self) -> bool: - return self._is_read_only - - def getTotalSpace(self) -> int: - try: - st = os.statvfs(self._path) - return st.f_frsize * st.f_blocks - except Exception: - return 0 - - def getUsableSpace(self) -> int: - try: - st = os.statvfs(self._path) - return st.f_frsize * st.f_bavail - except Exception: - return 0 - - def getUnallocatedSpace(self) -> int: - try: - st = os.statvfs(self._path) - return st.f_frsize * st.f_bfree - except Exception: - return 0 - - def getFileStoreAttributeView(self, viewType: Any) -> Optional[FileStoreAttributeView]: - return None - - def supportsFileAttributeView(self, viewType: Any) -> bool: - return True - - def toString(self) -> str: - return f"FileStore(path={self._path}, type={self._type})" - - -# ═══════════════════════════════════════════════════════════════ -# FileSystem (扩展) / UnixFileSystem / WinNTFileSystem -# ═══════════════════════════════════════════════════════════════ - -class FileSystem(Stub): - """FileSystem — 文件系统 (扩展版)""" - - def __init__(self): - super().__init__() - self._separator = os.sep - self._path_separator = os.pathsep - self._normalized_separator = "/" if os.sep == "/" else "\\" - - def getSeparator(self) -> str: - return self._separator - - def getPathSeparator(self) -> str: - return self._path_separator - - def normalize(self, path: str) -> str: - return os.path.normpath(path) - - def resolve(self, parent: str, child: str) -> str: - return os.path.join(parent, child) - - def getLength(self, path: str) -> int: - try: - return os.path.getsize(path) - except Exception: - return 0 - - def getLastModifiedTime(self, path: str) -> int: - try: - return int(os.path.getmtime(path) * 1000) - except Exception: - return 0 - - def getLastAccessTime(self, path: str) -> int: - try: - st = os.stat(path) - return int(st.st_atime * 1000) - except Exception: - return 0 - - def getCreationTime(self, path: str) -> int: - try: - st = os.stat(path) - if hasattr(st, 'st_birthtime'): - return int(st.st_birthtime * 1000) - return int(st.st_ctime * 1000) - except Exception: - return 0 - - def isDirectory(self, path: str) -> bool: - return os.path.isdir(path) - - def isFile(self, path: str) -> bool: - return os.path.isfile(path) - - def exists(self, path: str) -> bool: - return os.path.exists(path) - - def canRead(self, path: str) -> bool: - return os.access(path, os.R_OK) - - def canWrite(self, path: str) -> bool: - return os.access(path, os.W_OK) - - def canExecute(self, path: str) -> bool: - return os.access(path, os.X_OK) - - def isAbsolute(self, path: str) -> bool: - return os.path.isabs(path) - - def list(self, path: str) -> List[str]: - try: - return os.listdir(path) - except Exception: - return [] - - def mkdir(self, path: str) -> bool: - try: - os.mkdir(path) - return True - except Exception: - return False - - def mkdirs(self, path: str) -> bool: - try: - os.makedirs(path, exist_ok=True) - return True - except Exception: - return False - - def rename(self, src: str, dst: str) -> bool: - try: - os.rename(src, dst) - return True - except Exception: - return False - - def delete(self, path: str) -> bool: - try: - if os.path.isdir(path): - os.rmdir(path) - else: - os.unlink(path) - return True - except Exception: - return False - - def getStatus(self, path: str) -> Optional[FileStatus]: - try: - st = os.stat(path) - return FileStatus( - type_=FILE_TYPE_DIRECTORY if stat.S_ISDIR(st.st_mode) - else FILE_TYPE_REGULAR if stat.S_ISREG(st.st_mode) - else FILE_TYPE_SYMBOLIC_LINK if stat.S_ISLNK(st.st_mode) - else FILE_TYPE_UNKNOWN, - size=st.st_size, - lastModified=int(st.st_mtime * 1000), - lastAccess=int(st.st_atime * 1000), - creationTime=int(st.st_ctime * 1000), - isSymbolicLink=os.path.islink(path), - device=st.st_dev, - inode=st.st_ino, - mode=st.st_mode, - uid=st.st_uid, - gid=st.st_gid, - ) - except Exception: - return None - - def getSpace(self, path: str) -> Tuple[int, int, int]: - try: - st = os.statvfs(path) - total = st.f_frsize * st.f_blocks - free = st.f_frsize * st.f_bfree - available = st.f_frsize * st.f_bavail - return (total, free, available) - except Exception: - return (0, 0, 0) - - def toString(self) -> str: - return f"FileSystem(sep={self._separator})" - - def equals(self, other: Any) -> bool: - return self is other - - def hashCode(self) -> int: - return id(self) & 0x7FFFFFFF - - -class UnixFileSystem(FileSystem): - """UnixFileSystem — Unix 文件系统""" - - def __init__(self): - super().__init__() - self._separator = "/" - self._path_separator = ":" - - def normalize(self, path: str) -> str: - return os.path.normpath(path) - - def resolve(self, parent: str, child: str) -> str: - if not parent: - return child - if not child: - return parent - return f"{parent.rstrip('/')}/{child.lstrip('/')}" - - def toString(self) -> str: - return "UnixFileSystem" - - -class WinNTFileSystem(FileSystem): - """WinNTFileSystem — Windows NT 文件系统""" - - def __init__(self): - super().__init__() - self._separator = "\\" - self._path_separator = ";" - self._normalized_separator = "\\" - - def normalize(self, path: str) -> str: - return path.replace("/", "\\") - - def resolve(self, parent: str, child: str) -> str: - if not parent: - return child - if not child: - return parent - sep = "\\" - parent_clean = parent.rstrip(sep) - child_clean = child.lstrip(sep) - return f"{parent_clean}{sep}{child_clean}" - - def toString(self) -> str: - return "WinNTFileSystem" - - -# ═══════════════════════════════════════════════════════════════ -# UserPrincipal / GroupPrincipal / UserPrincipalLookupService -# ═══════════════════════════════════════════════════════════════ - -class UserPrincipal(Stub): - """UserPrincipal — 用户主体""" - - def __init__(self, name: str = ""): - super().__init__() - self._name = name - - def getName(self) -> str: - return self._name - - def toString(self) -> str: - return self._name - - def equals(self, other: Any) -> bool: - if not isinstance(other, UserPrincipal): - return False - return self._name == other._name - - def hashCode(self) -> int: - return hash(self._name) & 0x7FFFFFFF - - -class GroupPrincipal(UserPrincipal): - """GroupPrincipal — 组主体""" - - def __init__(self, name: str = ""): - super().__init__(name=name) - - def toString(self) -> str: - return f"Group({self._name})" - - -class UserPrincipalLookupService(Stub): - """UserPrincipalLookupService — 用户主体查找服务""" - - def lookupPrincipalByName(self, name: str) -> UserPrincipal: - return UserPrincipal(name=name) - - def lookupPrincipalByGroupName(self, name: str) -> GroupPrincipal: - return GroupPrincipal(name=name) - - def toString(self) -> str: - return "UserPrincipalLookupService" - - -# ═══════════════════════════════════════════════════════════════ -# FileSystems (工厂) -# ═══════════════════════════════════════════════════════════════ - -class FileSystems(Stub): - """FileSystems — 文件系统工厂""" - - _default: Optional[FileSystem] = None - _unix_default: Optional[UnixFileSystem] = None - - @staticmethod - def getDefault() -> FileSystem: - if sys.platform == "win32": - if FileSystems._default is None: - FileSystems._default = WinNTFileSystem() - return FileSystems._default - else: - if FileSystems._unix_default is None: - FileSystems._unix_default = UnixFileSystem() - return FileSystems._unix_default - - @staticmethod - def newFileSystem(uri: Any, env: Dict[str, Any] = None) -> Any: - return None - - @staticmethod - def newFileSystemFromPath(path: Any, loader: Any = None) -> Any: - return None - - def toString(self) -> str: - return "FileSystems" \ No newline at end of file diff --git a/android_pyapi/java/lang.py b/android_pyapi/java/lang.py deleted file mode 100644 index d296fe2..0000000 --- a/android_pyapi/java/lang.py +++ /dev/null @@ -1,2128 +0,0 @@ -""" -Java lang standard library APIs implemented in Python. -java.lang package - String, Integer, Math, Thread, System, Object, etc. -""" - -from .._core import Stub, AndroidOnlyError -import re as _re -import math as _py_math -import sys as _sys -import os as _os -import platform -import time as _time -import threading as _threading -import inspect as _inspect -import math -import random -import time -from datetime import datetime as _datetime - -__all__ = [ - "Object", "Throwable", "Exception", "RuntimeException", "Error", - "NullPointerException", "IllegalArgumentException", "IllegalStateException", - "IndexOutOfBoundsException", "ArrayIndexOutOfBoundsException", - "StringIndexOutOfBoundsException", "ClassCastException", - "UnsupportedOperationException", "ArithmeticException", - "NumberFormatException", "SecurityException", - "ConcurrentModificationException", "NegativeArraySizeException", - "NoSuchFieldException", "NoSuchMethodException", "InterruptedException", - "CloneNotSupportedException", "IllegalMonitorStateException", - "IllegalThreadStateException", "EnumConstantNotPresentException", - "TypeNotPresentException", - "ClassNotFoundException", "IllegalAccessException", "InstantiationException", - "InvocationTargetException", "NoSuchElementException", - "AssertionError", "OutOfMemoryError", "StackOverflowError", - "NoClassDefFoundError", "UnsatisfiedLinkError", "LinkageError", - "VerifyError", "AbstractMethodError", "IncompatibleClassChangeError", - "ExceptionInInitializerError", - "String", "Integer", "Long", "Short", "Byte", "Float", "Double", - "Boolean", "Character", "Math", "StrictMath", "System", - "Thread", "ThreadLocal", "StringBuilder", "StringBuffer", - "Class", "Void", "VoidType", - "Stub", "Comparable", "CharSequence", "Iterable", "Appendable", - "Readable", "AutoCloseable", "Cloneable", "Runnable", - "StackTraceElement", "Number", "SuppressWarnings", "Deprecated", - "Override", "FunctionalInterface", -] - - -# ============================================================ -# Interfaces -# ============================================================ - -class Comparable(Stub): - def compareTo(self, other): - raise NotImplementedError - -class Iterable(Stub): - def iterator(self): - raise NotImplementedError - -class AutoCloseable(Stub): - def close(self): - raise NotImplementedError - -class Cloneable(Stub): - def clone(self): - return self.__class__() - -class CharSequence(Stub): - def length(self): - raise NotImplementedError - def charAt(self, index): - raise NotImplementedError - def subSequence(self, start, end): - raise NotImplementedError - def toString(self): - raise NotImplementedError - -class Appendable(Stub): - def append(self, c): - raise NotImplementedError - -class Readable(Stub): - def read(self, cb): - raise NotImplementedError - -class Runnable(Stub): - def run(self): - pass - - -# ============================================================ -# Throwable hierarchy -# ============================================================ - -class Throwable(Exception, Stub): - def __init__(self, message=None, cause=None): - super().__init__(message or "") - self._detail_message = str(message) if message else None - self._cause = cause - self._stack_trace = [] - self._suppressed = [] - self._fill_in_stack() - - def _fill_in_stack(self): - import traceback - stack = traceback.extract_stack() - self._stack_trace = [ - StackTraceElement( - frame.filename, frame.name, frame.filename, frame.lineno, False - ) - for frame in stack[:-1] - ] - - def getMessage(self): - return self._detail_message - - def getLocalizedMessage(self): - return self._detail_message - - def getCause(self): - return self._cause - - def initCause(self, cause): - self._cause = cause - return self - - def toString(self): - msg = self._detail_message or "" - return f"{self.__class__.__name__}: {msg}" if msg else self.__class__.__name__ - - def printStackTrace(self, file=None): - import traceback - if file is None: - file = _sys.stderr - traceback.print_exc(file=file) - - def fillInStackTrace(self): - self._fill_in_stack() - return self - - def getStackTrace(self): - return self._stack_trace - - def setStackTrace(self, stack_trace): - self._stack_trace = stack_trace - - def addSuppressed(self, exception): - self._suppressed.append(exception) - - def getSuppressed(self): - return self._suppressed - - def __str__(self): - return self.toString() - - -class Exception(Throwable): - pass - -class RuntimeException(Exception): - pass - -class Error(Throwable): - pass - - -class NullPointerException(RuntimeException): - pass - -class IllegalArgumentException(RuntimeException): - pass - -class IllegalStateException(RuntimeException): - pass - -class IndexOutOfBoundsException(RuntimeException): - pass - -class ArrayIndexOutOfBoundsException(IndexOutOfBoundsException): - pass - -class StringIndexOutOfBoundsException(IndexOutOfBoundsException): - pass - -class ClassCastException(RuntimeException): - pass - -class UnsupportedOperationException(RuntimeException): - pass - -class ArithmeticException(RuntimeException): - pass - -class NumberFormatException(IllegalArgumentException): - pass - -class SecurityException(RuntimeException): - pass - -class ConcurrentModificationException(RuntimeException): - pass - -class NegativeArraySizeException(RuntimeException): - pass - -class NoSuchFieldException(Exception): - pass - -class NoSuchMethodException(Exception): - pass - -class InterruptedException(Exception): - pass - -class CloneNotSupportedException(Exception): - pass - -class IllegalMonitorStateException(RuntimeException): - pass - -class IllegalThreadStateException(IllegalArgumentException): - pass - -class EnumConstantNotPresentException(RuntimeException): - def __init__(self, e, constant): - super().__init__(f"{e}.{constant}") - self.enumType = e - self.constantName = constant - -class TypeNotPresentException(RuntimeException): - def __init__(self, typeName, cause=None): - super().__init__(f"Type {typeName} not present", cause) - self.typeName = typeName - - -# ===== 缺失的 Exception 子类 ===== -class ClassNotFoundException(Exception): - def __init__(self, message="", cause=None): - super().__init__(message, cause) - self.ex = message - -class IllegalAccessException(Exception): - def __init__(self, message=""): - super().__init__(message) - -class InstantiationException(Exception): - def __init__(self, message=""): - super().__init__(message) - -class InvocationTargetException(Exception): - def __init__(self, target=None, cause=None): - if target is not None and cause is None: - super().__init__(str(target)) - self.target = target - else: - super().__init__(str(target) if target else "", cause) - self.target = target - -class NoSuchElementException(RuntimeException): - def __init__(self, message=""): - super().__init__(message) - -# ===== 缺失的 Error 类 ===== -class AssertionError(Error): - def __init__(self, message=None): - if message is None: - super().__init__("Assertion failed") - else: - super().__init__(str(message)) - -class OutOfMemoryError(Error): - def __init__(self, message=""): - super().__init__(message) - -class StackOverflowError(Error): - def __init__(self, message=""): - super().__init__(message) - -class NoClassDefFoundError(Error): - def __init__(self, message=""): - super().__init__(message) - -class UnsatisfiedLinkError(Error): - def __init__(self, message=""): - super().__init__(message) - -class LinkageError(Error): - def __init__(self, message=""): - super().__init__(message) - -class VerifyError(LinkageError): - def __init__(self, message=""): - super().__init__(message) - -class AbstractMethodError(LinkageError): - def __init__(self, message=""): - super().__init__(message) - -class IncompatibleClassChangeError(LinkageError): - def __init__(self, message=""): - super().__init__(message) - -class ExceptionInInitializerError(LinkageError): - def __init__(self, message="", cause=None): - super().__init__(message) - self._cause = cause - self.exception = cause - - -# ============================================================ -# StackTraceElement -# ============================================================ - -class StackTraceElement(Stub): - def __init__(self, declaring_class, method_name, file_name, line_number, native_method): - super().__init__() - self._class = declaring_class - self._method = method_name - self._file = file_name - self._line = line_number - self._native = native_method - - def getClassName(self): - return self._class - - def getMethodName(self): - return self._method - - def getFileName(self): - return self._file - - def getLineNumber(self): - return self._line - - def isNativeMethod(self): - return self._native - - def toString(self): - return f"{self._class}.{self._method}({self._file}:{self._line})" - - -# ============================================================ -# Object -# ============================================================ - -class Object(Stub): - def __init__(self): - super().__init__() - - def getClass(self): - return self.__class__ - - def hashCode(self): - return hash(self) & 0x7FFFFFFF - - def equals(self, other): - return self is other - - def toString(self): - return f"{self.__class__.__name__}@{hex(id(self))}" - - def notify(self): - pass - - def notifyAll(self): - pass - - def wait(self, timeout=None): - pass - - def clone(self): - return self.__class__() - - def finalize(self): - pass - - -# ============================================================ -# String -# ============================================================ - -class String(CharSequence): - def __init__(self, value=None): - super().__init__() - if value is None: - self._str = "" - elif isinstance(value, bytes): - self._str = value.decode("utf-8", errors="replace") - else: - self._str = str(value) - - def charAt(self, index): - return self._str[index] - - def codePointAt(self, index): - return ord(self._str[index]) - - def codePointBefore(self, index): - return ord(self._str[index - 1]) if index > 0 else -1 - - def codePointCount(self, beginIndex, endIndex): - return endIndex - beginIndex - - def compareTo(self, other): - if isinstance(other, String): - return (self._str > other._str) - (self._str < other._str) - return -1 - - def compareToIgnoreCase(self, other): - if isinstance(other, String): - return (self._str.lower() > other._str.lower()) - (self._str.lower() < other._str.lower()) - return -1 - - def concat(self, s): - return String(self._str + str(s)) - - def contains(self, s): - return str(s) in self._str - - def contentEquals(self, cs): - return self._str == str(cs) - - def endsWith(self, suffix): - return self._str.endswith(suffix) - - def equals(self, other): - if isinstance(other, String): - return self._str == other._str - return False - - def equalsIgnoreCase(self, other): - if isinstance(other, String): - return self._str.lower() == other._str.lower() - return False - - def getBytes(self, charset="UTF-8"): - return self._str.encode(charset, errors="replace") - - def hashCode(self) -> int: - h = 0 - for i in range(len(self._str)): - h = 31 * h + ord(self._str[i]) - return h & 0x7FFFFFFF - - def indexOf(self, ch, fromIndex=0): - if isinstance(ch, int): - ch = chr(ch) - return self._str.index(str(ch), fromIndex) if str(ch) in self._str[fromIndex:] else -1 - - def intern(self): - return self - - def isEmpty(self): - return len(self._str) == 0 - - def lastIndexOf(self, ch, fromIndex=None): - if isinstance(ch, int): - ch = chr(ch) - if fromIndex is None: - fromIndex = len(self._str) - 1 - search_in = self._str[:fromIndex + 1] - return search_in.rfind(str(ch)) if str(ch) in search_in else -1 - - def length(self): - return len(self._str) - - def matches(self, regex): - return bool(_re.fullmatch(regex, self._str)) - - def offsetByCodePoints(self, index, codePointOffset): - return index + codePointOffset - - def regionMatches(self, ignoreCase, toffset, other, ooffset, length): - if ignoreCase: - return self._str[toffset:toffset+length].lower() == other._str[ooffset:ooffset+length].lower() - return self._str[toffset:toffset+length] == other._str[ooffset:ooffset+length] - - def replace(self, oldChar, newChar): - if isinstance(oldChar, int): - oldChar = chr(oldChar) - if isinstance(newChar, int): - newChar = chr(newChar) - return String(self._str.replace(oldChar, newChar)) - - def replaceAll(self, regex, replacement): - return String(_re.sub(regex, replacement, self._str)) - - def replaceFirst(self, regex, replacement): - return String(_re.sub(regex, replacement, self._str, count=1)) - - def split(self, regex, limit=0): - if limit <= 0: - parts = _re.split(regex, self._str) - if limit == 0: - while parts and parts[-1] == "": - parts.pop() - else: - parts = _re.split(regex, self._str, maxsplit=limit - 1) - while len(parts) < limit: - parts.append("") - return [String(p) for p in parts] - - def startsWith(self, prefix): - return self._str.startswith(prefix) - - def substring(self, beginIndex, endIndex=None): - if endIndex is None: - return String(self._str[beginIndex:]) - return String(self._str[beginIndex:endIndex]) - - def toCharArray(self): - return list(self._str) - - def toLowerCase(self): - return String(self._str.lower()) - - def toUpperCase(self): - return String(self._str.upper()) - - def trim(self): - return String(self._str.strip()) - - def strip(self): - return String(self._str.strip()) - - def stripLeading(self): - return String(self._str.lstrip()) - - def stripTrailing(self): - return String(self._str.rstrip()) - - def isBlank(self): - return self._str.strip() == "" - - def repeat(self, count): - return String(self._str * count) - - def lines(self): - from .util import ArrayList - result = ArrayList() - for line in self._str.splitlines(): - result.add(String(line)) - return result - - def subSequence(self, start, end): - return self.substring(start, end) - - def getChars(self, srcBegin, srcEnd, dst, dstBegin): - chars = self._str[srcBegin:srcEnd] - for i, c in enumerate(chars): - dst[dstBegin + i] = c - - def chars(self): - return [ord(c) for c in self._str] - - def formatted(self, *args): - return String(self._str.format(*args)) - - def translateEscapes(self): - result = [] - s = self._str - i = 0 - n = len(s) - while i < n: - c = s[i] - if c == '\\' and i + 1 < n: - nxt = s[i + 1] - if nxt == 'b': - result.append('\b'); i += 2 - elif nxt == 't': - result.append('\t'); i += 2 - elif nxt == 'n': - result.append('\n'); i += 2 - elif nxt == 'f': - result.append('\f'); i += 2 - elif nxt == 'r': - result.append('\r'); i += 2 - elif nxt == 's': - result.append(' '); i += 2 - elif nxt == '"': - result.append('"'); i += 2 - elif nxt == "'": - result.append("'"); i += 2 - elif nxt == '\\': - result.append('\\'); i += 2 - elif nxt in '01234567': - octal = nxt - i += 2 - max_digits = 2 if nxt in '0123' else 1 - while len(octal) < max_digits + 1 and i < n and s[i] in '01234567': - octal += s[i] - i += 1 - result.append(chr(int(octal, 8))) - elif nxt == '\n': - i += 2 - elif nxt == '\r': - i += 2 - if i < n and s[i] == '\n': - i += 1 - else: - result.append(nxt); i += 2 - else: - result.append(c); i += 1 - return String(''.join(result)) - - @staticmethod - def valueOf(obj): - return String(str(obj)) - - @staticmethod - def copyValueOf(data, offset=0, count=None): - if count is None: - return String("".join(data)) - return String("".join(data[offset:offset+count])) - - @staticmethod - def format(fmt, *args): - return String(str(fmt) % args if args else str(fmt)) - - @staticmethod - def join(delimiter, *elements): - if hasattr(elements[0], '__iter__') and not isinstance(elements[0], (str, bytes)): - elements = elements[0] - return String(str(delimiter).join(str(s) for s in elements)) - - def toString(self): - return self._str - - def __str__(self): - return self._str - - def __repr__(self): - return f"String({self._str!r})" - - def __len__(self): - return len(self._str) - - def __getitem__(self, idx): - return self._str[idx] - - def __add__(self, other): - return String(self._str + str(other)) - - def __eq__(self, other): - if isinstance(other, String): - return self._str == other._str - return NotImplemented - - -# ============================================================ -# Number -# ============================================================ - -class Number(Stub): - def intValue(self): - raise NotImplementedError - def longValue(self): - raise NotImplementedError - def floatValue(self): - raise NotImplementedError - def doubleValue(self): - raise NotImplementedError - def byteValue(self): - return self.intValue() & 0xFF - def shortValue(self): - return self.intValue() & 0xFFFF - - -# ============================================================ -# Integer -# ============================================================ - -class Integer(Number, Comparable): - MAX_VALUE = 2147483647 - MIN_VALUE = -2147483648 - SIZE = 32 - BYTES = 4 - TYPE = int - - def __init__(self, value=0): - super().__init__() - if isinstance(value, str): - self._value = Integer.parseInt(value) - else: - self._value = int(value) - - @staticmethod - def parseInt(s, radix=10): - if isinstance(s, String): - s = str(s) - if radix == 10: - return int(str(s)) - return int(str(s), radix) - - @staticmethod - def toString(i, radix=10): - if radix == 10: - return String(str(i)) - if radix == 2: - return String(bin(i)[2:]) - if radix == 8: - return String(oct(i)[2:]) - if radix == 16: - return String(hex(i)[2:]) - return String(str(i)) - - @staticmethod - def toBinaryString(i): - if i >= 0: - return String(bin(i)[2:]) - return String(bin(i & 0xFFFFFFFF)[2:]) - - @staticmethod - def toHexString(i): - if i >= 0: - return String(hex(i)[2:]) - return String(hex(i & 0xFFFFFFFF)[2:]) - - @staticmethod - def toOctalString(i): - if i >= 0: - return String(oct(i)[2:]) - return String(oct(i & 0xFFFFFFFF)[2:]) - - @staticmethod - def valueOf(i, radix=10): - if isinstance(i, str): - return Integer(Integer.parseInt(i, radix)) - return Integer(i) - - def intValue(self): - return self._value - - def compareTo(self, other): - if isinstance(other, Integer): - return (self._value > other._value) - (self._value < other._value) - return -1 - - def equals(self, other): - if isinstance(other, Integer): - return self._value == other._value - return False - - def byteValue(self): - return self._value & 0xFF - - def shortValue(self): - return self._value & 0xFFFF - - def longValue(self): - return self._value - - def floatValue(self): - return float(self._value) - - def doubleValue(self): - return float(self._value) - - @staticmethod - def bitCount(i): - return bin(i & 0xFFFFFFFF).count("1") - - @staticmethod - def highestOneBit(i): - if i == 0: - return 0 - return 1 << (i.bit_length() - 1) - - @staticmethod - def lowestOneBit(i): - return i & -i - - @staticmethod - def numberOfLeadingZeros(i): - if i == 0: - return 32 - return 32 - i.bit_length() - - @staticmethod - def numberOfTrailingZeros(i): - if i == 0: - return 32 - return (i & -i).bit_length() - 1 - - @staticmethod - def reverse(i): - v = i & 0xFFFFFFFF - result = 0 - for _ in range(32): - result = (result << 1) | (v & 1) - v >>= 1 - return result - - @staticmethod - def reverseBytes(i): - v = i & 0xFFFFFFFF - return ((v >> 24) & 0xFF) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | ((v << 24) & 0xFF000000) - - @staticmethod - def rotateLeft(i, distance): - v = i & 0xFFFFFFFF - d = distance & 31 - return ((v << d) | (v >> (32 - d))) & 0xFFFFFFFF - - @staticmethod - def rotateRight(i, distance): - v = i & 0xFFFFFFFF - d = distance & 31 - return ((v >> d) | (v << (32 - d))) & 0xFFFFFFFF - - @staticmethod - def signum(i): - if i > 0: - return 1 - if i < 0: - return -1 - return 0 - - @staticmethod - def decode(nm): - if nm.startswith("0x") or nm.startswith("0X"): - return Integer(int(nm, 16)) - if nm.startswith("0") and len(nm) > 1: - return Integer(int(nm, 8)) - if nm.startswith("#"): - return Integer(int(nm[1:], 16)) - return Integer(int(nm)) - - @staticmethod - def getInteger(nm, val=None): - env_val = _os.environ.get(nm) - if env_val is not None: - return Integer(int(env_val)) - return Integer(val) if val is not None else None - - @staticmethod - def compare(x, y): - return -1 if x < y else (1 if x > y else 0) - - @staticmethod - def compareUnsigned(x, y): - ux = x & 0xFFFFFFFF if x < 0 else x - uy = y & 0xFFFFFFFF if y < 0 else y - return -1 if ux < uy else (1 if ux > uy else 0) - - @staticmethod - def divideUnsigned(dividend, divisor): - ux = dividend & 0xFFFFFFFF if dividend < 0 else dividend - uy = divisor & 0xFFFFFFFF if divisor < 0 else divisor - return ux // uy - - @staticmethod - def remainderUnsigned(dividend, divisor): - ux = dividend & 0xFFFFFFFF if dividend < 0 else dividend - uy = divisor & 0xFFFFFFFF if divisor < 0 else divisor - return ux % uy - - @staticmethod - def parseUnsignedInt(s, radix=10): - return int(s, radix) - - @staticmethod - def toUnsignedString(i, radix=10): - if i < 0: - i = i & 0xFFFFFFFF - if radix == 10: - return str(i) - elif radix == 16: - return format(i, 'x') - elif radix == 2: - return format(i, 'b') - elif radix == 8: - return format(i, 'o') - return str(i) - - @staticmethod - def toUnsignedLong(i): - return i & 0xFFFFFFFF - - @staticmethod - def sum(a, b): - return a + b - - @staticmethod - def max(a, b): - return a if a > b else b - - @staticmethod - def min(a, b): - return a if a < b else b - - def toString(self): - return String(str(self._value)) - - def __str__(self): - return str(self._value) - - def __int__(self): - return self._value - - -# ============================================================ -# Long -# ============================================================ - -class Long(Number, Comparable): - MAX_VALUE = 9223372036854775807 - MIN_VALUE = -9223372036854775808 - SIZE = 64 - BYTES = 8 - TYPE = int - - def __init__(self, value=0): - super().__init__() - if isinstance(value, str): - self._value = Long.parseLong(value) - else: - self._value = int(value) - - @staticmethod - def parseLong(s, radix=10): - if isinstance(s, String): - s = str(s) - if radix == 10: - return int(str(s)) - return int(str(s), radix) - - @staticmethod - def toString(i, radix=10): - if radix == 10: - return String(str(i)) - return String(str(i)) - - @staticmethod - def toBinaryString(i): - if i >= 0: - return String(bin(i)[2:]) - return String(bin(i & 0xFFFFFFFFFFFFFFFF)[2:]) - - @staticmethod - def toHexString(i): - if i >= 0: - return String(hex(i)[2:]) - return String(hex(i & 0xFFFFFFFFFFFFFFFF)[2:]) - - @staticmethod - def toOctalString(i): - if i >= 0: - return String(oct(i)[2:]) - return String(oct(i & 0xFFFFFFFFFFFFFFFF)[2:]) - - @staticmethod - def valueOf(i, radix=10): - if isinstance(i, str): - return Long(Long.parseLong(i, radix)) - return Long(i) - - def longValue(self): - return self._value - - def intValue(self): - return int(self._value) - - def floatValue(self): - return float(self._value) - - def doubleValue(self): - return float(self._value) - - def compareTo(self, other): - if isinstance(other, Long): - return (self._value > other._value) - (self._value < other._value) - return -1 - - def equals(self, other): - if isinstance(other, Long): - return self._value == other._value - return False - - def toString(self): - return String(str(self._value)) - - def __str__(self): - return str(self._value) - - -# ============================================================ -# Short, Byte -# ============================================================ - -class Short(Number, Comparable): - MAX_VALUE = 32767 - MIN_VALUE = -32768 - SIZE = 16 - BYTES = 2 - TYPE = int - - def __init__(self, value=0): - super().__init__() - if isinstance(value, str): - self._value = Short.parseShort(value) - else: - self._value = int(value) & 0xFFFF - - @staticmethod - def parseShort(s, radix=10): - return int(str(s), radix) - - @staticmethod - def valueOf(i, radix=10): - if isinstance(i, str): - return Short(Short.parseShort(i, radix)) - return Short(i) - - def shortValue(self): - return self._value - - def intValue(self): - return self._value - - def longValue(self): - return self._value - - def floatValue(self): - return float(self._value) - - def doubleValue(self): - return float(self._value) - - def compareTo(self, other): - if isinstance(other, Short): - return (self._value > other._value) - (self._value < other._value) - return -1 - - def equals(self, other): - if isinstance(other, Short): - return self._value == other._value - return False - - def toString(self): - return String(str(self._value)) - - -class Byte(Number, Comparable): - MAX_VALUE = 127 - MIN_VALUE = -128 - SIZE = 8 - BYTES = 1 - TYPE = int - - def __init__(self, value=0): - super().__init__() - if isinstance(value, str): - self._value = Byte.parseByte(value) - else: - self._value = int(value) & 0xFF - if self._value > 127: - self._value -= 256 - - @staticmethod - def parseByte(s, radix=10): - return int(str(s), radix) - - @staticmethod - def valueOf(i, radix=10): - if isinstance(i, str): - return Byte(Byte.parseByte(i, radix)) - return Byte(i) - - def byteValue(self): - return self._value - - def intValue(self): - return self._value - - def longValue(self): - return self._value - - def compareTo(self, other): - if isinstance(other, Byte): - return (self._value > other._value) - (self._value < other._value) - return -1 - - def equals(self, other): - if isinstance(other, Byte): - return self._value == other._value - return False - - def toString(self): - return String(str(self._value)) - - -# ============================================================ -# Float -# ============================================================ - -class Float(Number, Comparable): - MAX_VALUE = 3.4028235e38 - MIN_VALUE = 1.4e-45 - MIN_NORMAL = 1.17549435e-38 - MAX_EXPONENT = 127 - MIN_EXPONENT = -126 - SIZE = 32 - BYTES = 4 - TYPE = float - NaN = float("nan") - POSITIVE_INFINITY = float("inf") - NEGATIVE_INFINITY = float("-inf") - - def __init__(self, value=0.0): - super().__init__() - if isinstance(value, str): - self._value = Float.parseFloat(value) - else: - self._value = float(value) - - @staticmethod - def parseFloat(s): - if isinstance(s, String): - s = str(s) - s = str(s).strip() - if s == "NaN": - return float("nan") - if s == "Infinity" or s == "Inf": - return float("inf") - if s == "-Infinity" or s == "-Inf": - return float("-inf") - return float(s) - - @staticmethod - def toString(f): - return String(str(f)) - - @staticmethod - def valueOf(f): - if isinstance(f, str): - return Float(Float.parseFloat(f)) - return Float(f) - - def floatValue(self): - return self._value - - def intValue(self): - return int(self._value) - - def longValue(self): - return int(self._value) - - def doubleValue(self): - return float(self._value) - - def compareTo(self, other): - if isinstance(other, Float): - a, b = self._value, other._value - if _py_math.isnan(a) and _py_math.isnan(b): - return 0 - if _py_math.isnan(a): - return 1 - if _py_math.isnan(b): - return -1 - return (a > b) - (a < b) - return -1 - - def isNaN(self): - return _py_math.isnan(self._value) - - def isInfinite(self): - return _py_math.isinf(self._value) - - def equals(self, other): - if isinstance(other, Float): - if _py_math.isnan(self._value) and _py_math.isnan(other._value): - return True - return self._value == other._value - return False - - def hashCode(self): - return _struct.pack(">f", self._value) - - def toString(self): - return String(str(self._value)) - - def __str__(self): - return str(self._value) - -import struct as _struct - - -# ============================================================ -# Double -# ============================================================ - -class Double(Number, Comparable): - MAX_VALUE = 1.7976931348623157e308 - MIN_VALUE = 4.9e-324 - MIN_NORMAL = 2.2250738585072014e-308 - MAX_EXPONENT = 1023 - MIN_EXPONENT = -1022 - SIZE = 64 - BYTES = 8 - TYPE = float - NaN = float("nan") - POSITIVE_INFINITY = float("inf") - NEGATIVE_INFINITY = float("-inf") - - def __init__(self, value=0.0): - super().__init__() - if isinstance(value, str): - self._value = Double.parseDouble(value) - else: - self._value = float(value) - - @staticmethod - def parseDouble(s): - if isinstance(s, String): - s = str(s) - s = str(s).strip() - if s == "NaN": - return float("nan") - if s == "Infinity" or s == "Inf": - return float("inf") - if s == "-Infinity" or s == "-Inf": - return float("-inf") - return float(s) - - @staticmethod - def toString(d): - return String(str(d)) - - @staticmethod - def valueOf(d): - if isinstance(d, str): - return Double(Double.parseDouble(d)) - return Double(d) - - def doubleValue(self): - return self._value - - def intValue(self): - return int(self._value) - - def longValue(self): - return int(self._value) - - def floatValue(self): - return float(self._value) - - def compareTo(self, other): - if isinstance(other, Double): - a, b = self._value, other._value - if _py_math.isnan(a) and _py_math.isnan(b): - return 0 - if _py_math.isnan(a): - return 1 - if _py_math.isnan(b): - return -1 - return (a > b) - (a < b) - return -1 - - def isNaN(self): - return _py_math.isnan(self._value) - - def isInfinite(self): - return _py_math.isinf(self._value) - - def equals(self, other): - if isinstance(other, Double): - if _py_math.isnan(self._value) and _py_math.isnan(other._value): - return True - return self._value == other._value - return False - - def toString(self): - return String(str(self._value)) - - def __str__(self): - return str(self._value) - - -# ============================================================ -# Boolean -# ============================================================ - -class Boolean(Stub): - TRUE = "true" - FALSE = "false" - TYPE = bool - - def __init__(self, value=False): - super().__init__() - if isinstance(value, str): - self._value = Boolean.parseBoolean(value) - elif isinstance(value, bool): - self._value = value - else: - self._value = bool(value) - - @staticmethod - def parseBoolean(s): - return str(s).lower() == "true" - - @staticmethod - def toString(b): - return String(str(b).lower()) - - @staticmethod - def valueOf(b): - if isinstance(b, str): - return Boolean(Boolean.parseBoolean(b)) - return Boolean(b) - - def booleanValue(self): - return self._value - - @staticmethod - def logicalAnd(a, b): - return a and b - - @staticmethod - def logicalOr(a, b): - return a or b - - @staticmethod - def logicalXor(a, b): - return a ^ b - - def compareTo(self, other): - if isinstance(other, Boolean): - return (self._value > other._value) - (self._value < other._value) - return -1 - - def equals(self, other): - if isinstance(other, Boolean): - return self._value == other._value - return False - - def toString(self): - return String("true" if self._value else "false") - - def __str__(self): - return "true" if self._value else "false" - - def __bool__(self): - return self._value - - -# ============================================================ -# Character -# ============================================================ - -class Character(Stub): - MAX_VALUE = 65535 - MIN_VALUE = 0 - SIZE = 16 - BYTES = 2 - TYPE = str - - def __init__(self, value=""): - super().__init__() - if isinstance(value, int): - self._value = chr(value) - else: - self._value = str(value)[0] if value else "" - - def charValue(self): - return self._value - - @staticmethod - def isDigit(ch): - return str(ch).isdigit() - - @staticmethod - def isLetter(ch): - return str(ch).isalpha() - - @staticmethod - def isLetterOrDigit(ch): - return str(ch).isalnum() - - @staticmethod - def isLowerCase(ch): - return str(ch).islower() - - @staticmethod - def isUpperCase(ch): - return str(ch).isupper() - - @staticmethod - def isWhitespace(ch): - return str(ch).isspace() - - @staticmethod - def isSpaceChar(ch): - return str(ch) in " \t\n\r\f\u000B" - - @staticmethod - def isISOControl(ch): - c = ord(ch) if isinstance(ch, str) else ch - return c < 0x20 or c == 0x7F - - @staticmethod - def isJavaIdentifierPart(ch): - c = str(ch) - return c.isalnum() or c == "_" or c == "$" - - @staticmethod - def isJavaIdentifierStart(ch): - c = str(ch) - return c.isalpha() or c == "_" or c == "$" - - @staticmethod - def isJavaLetter(ch): - return Character.isJavaIdentifierStart(ch) - - @staticmethod - def isJavaLetterOrDigit(ch): - return Character.isJavaIdentifierPart(ch) - - @staticmethod - def toLowerCase(ch): - return str(ch).lower() - - @staticmethod - def toUpperCase(ch): - return str(ch).upper() - - @staticmethod - def toTitleCase(ch): - return str(ch).upper() - - @staticmethod - def getNumericValue(ch): - s = str(ch) - if s.isdigit(): - return int(s) - if "a" <= s <= "z": - return ord(s) - ord("a") + 10 - if "A" <= s <= "Z": - return ord(s) - ord("A") + 10 - return -1 - - @staticmethod - def digit(ch, radix): - val = Character.getNumericValue(ch) - return val if val < radix else -1 - - @staticmethod - def forDigit(digit, radix): - if digit >= radix: - return "\0" - return str(digit) if digit < 10 else chr(ord("a") + digit - 10) - - @staticmethod - def getType(ch): - return 0 - - @staticmethod - def getDirectionality(ch): - return 0 - - @staticmethod - def getName(ch): - return f"CHARACTER {ord(ch)}" - - @staticmethod - def charCount(codePoint): - return 1 if codePoint < 0x10000 else 2 - - @staticmethod - def isBmpCodePoint(cp): - return cp <= 0xFFFF - - @staticmethod - def isHighSurrogate(ch): - c = ord(ch) if isinstance(ch, str) else ch - return 0xD800 <= c <= 0xDBFF - - @staticmethod - def isLowSurrogate(ch): - c = ord(ch) if isinstance(ch, str) else ch - return 0xDC00 <= c <= 0xDFFF - - @staticmethod - def isSupplementaryCodePoint(cp): - return 0x10000 - - -class Math: - """java.lang.Math — 数学运算""" - E = 2.718281828459045 - PI = 3.141592653589793 - - @staticmethod - def abs(a): return abs(a) - @staticmethod - def ceil(a): return int(math.ceil(a)) - @staticmethod - def floor(a): return int(math.floor(a)) - @staticmethod - def round(a: float) -> int: - return int(math.floor(a + 0.5)) - @staticmethod - def min(a, b): return a if a < b else b - @staticmethod - def max(a, b): return a if a > b else b - @staticmethod - def sqrt(a): return math.sqrt(a) - @staticmethod - def cbrt(a): return math.cbrt(a) - @staticmethod - def pow(a, b): return math.pow(a, b) - @staticmethod - def exp(a): return math.exp(a) - @staticmethod - def log(a): return math.log(a) - @staticmethod - def log10(a): return math.log10(a) - @staticmethod - def sin(a): return math.sin(a) - @staticmethod - def cos(a): return math.cos(a) - @staticmethod - def tan(a): return math.tan(a) - @staticmethod - def asin(a): return math.asin(a) - @staticmethod - def acos(a): return math.acos(a) - @staticmethod - def atan(a): return math.atan(a) - @staticmethod - def atan2(y, x): return math.atan2(y, x) - @staticmethod - def toDegrees(r): return math.degrees(r) - @staticmethod - def toRadians(d): return math.radians(d) - @staticmethod - def hypot(x, y): return math.hypot(x, y) - @staticmethod - def sinh(x): return math.sinh(x) - @staticmethod - def cosh(x): return math.cosh(x) - @staticmethod - def tanh(x): return math.tanh(x) - @staticmethod - def signum(x): return 0.0 if x == 0 else (1.0 if x > 0 else -1.0) - @staticmethod - def random(): return random.random() - @staticmethod - def addExact(x, y): - result = x + y - if (x > 0 and y > 0 and result < 0) or (x < 0 and y < 0 and result > 0): - raise ArithmeticError("integer overflow") - return result - @staticmethod - def copySign(m, s): return abs(m) * (1 if s >= 0 else -1) - @staticmethod - def floorDiv(x, y): - return math.floor(x / y) if y != 0 else 0 - @staticmethod - def floorMod(x, y): - return x - Math.floorDiv(x, y) * y - @staticmethod - def subtractExact(x, y): - result = x - y - if isinstance(x, int) and isinstance(y, int): - if result > 0 and x < 0 and y > 0: - raise ArithmeticException("integer overflow") - if result < 0 and x > 0 and y < 0: - raise ArithmeticException("integer overflow") - return result - @staticmethod - def multiplyExact(x, y): - result = x * y - if isinstance(x, int) and isinstance(y, int): - if result // y != x: - raise ArithmeticException("integer overflow") - return result - @staticmethod - def incrementExact(a): - if a == 2147483647: - raise ArithmeticException("integer overflow") - return a + 1 - @staticmethod - def decrementExact(a): - if a == -2147483648: - raise ArithmeticException("integer overflow") - return a - 1 - @staticmethod - def negateExact(a): - if a == -2147483648: - raise ArithmeticException("integer overflow") - return -a - @staticmethod - def toIntExact(value): - if value < -2147483648 or value > 2147483647: - raise ArithmeticException("integer overflow") - return int(value) - @staticmethod - def log1p(x): - return math.log1p(x) - @staticmethod - def expm1(x): - return math.expm1(x) - @staticmethod - def ulp(x): - return math.ulp(x) if hasattr(math, 'ulp') else abs(x) * 2.220446049250313e-16 - @staticmethod - def getExponent(x): - return math.frexp(x)[1] - 1 - @staticmethod - def nextAfter(start, direction): - return math.nextafter(start, direction) - @staticmethod - def nextUp(x): - return math.nextafter(x, math.inf) - @staticmethod - def nextDown(x): - return math.nextafter(x, -math.inf) - @staticmethod - def rint(x): - return math.rint(x) if hasattr(math, 'rint') else float(math.floor(x + 0.5)) - @staticmethod - def IEEEremainder(x, y): - return math.remainder(x, y) if hasattr(math, 'remainder') else x - y * math.floor(x / y) - @staticmethod - def compareUnsigned(x, y): - ux = x & 0xFFFFFFFF if x < 0 else x - uy = y & 0xFFFFFFFF if y < 0 else y - return -1 if ux < uy else (1 if ux > uy else 0) - @staticmethod - def divideUnsigned(dividend, divisor): - ux = dividend & 0xFFFFFFFF if dividend < 0 else dividend - uy = divisor & 0xFFFFFFFF if divisor < 0 else divisor - return ux // uy - @staticmethod - def remainderUnsigned(dividend, divisor): - ux = dividend & 0xFFFFFFFF if dividend < 0 else dividend - uy = divisor & 0xFFFFFFFF if divisor < 0 else divisor - return ux % uy - @staticmethod - def fma(a, b, c): - return a * b + c - - -class StrictMath(Math): - """java.lang.StrictMath""" - pass - - -class _Console: - """java.io.Console 简化实现,供 System.console() 使用""" - def readLine(self, fmt=None, *args): - if fmt is not None: - try: - _sys.stdout.write(str(fmt) % args if args else str(fmt)) - except Exception: - _sys.stdout.write(str(fmt)) - _sys.stdout.flush() - line = _sys.stdin.readline() - if line == "": - return None - return line.rstrip("\r\n") - - def readPassword(self, fmt=None, *args): - import getpass - if fmt is not None: - try: - _sys.stderr.write(str(fmt) % args if args else str(fmt)) - except Exception: - _sys.stderr.write(str(fmt)) - _sys.stderr.flush() - pwd = getpass.getpass("") - return list(pwd) if pwd is not None else None - - def printf(self, fmt, *args): - try: - _sys.stdout.write(str(fmt) % args if args else str(fmt)) - except Exception: - _sys.stdout.write(str(fmt)) - return self - - def format(self, fmt, *args): - try: - _sys.stdout.write(str(fmt) % args if args else str(fmt)) - except Exception: - _sys.stdout.write(str(fmt)) - return self - - def flush(self): - _sys.stdout.flush() - - def writer(self): - return _sys.stdout - - def reader(self): - return _sys.stdin - - -class System: - """java.lang.System""" - _console = None - @staticmethod - def currentTimeMillis(): return int(time.time() * 1000) - @staticmethod - def nanoTime(): return time.perf_counter_ns() - @staticmethod - def arraycopy(src, srcPos, dest, destPos, length): - if isinstance(src, (list, bytearray)): - dest[destPos:destPos+length] = src[srcPos:srcPos+length] - else: - raise TypeError("Unsupported type for arraycopy") - @staticmethod - def identityHashCode(obj): return id(obj) & 0x7FFFFFFF - @staticmethod - def gc(): pass - @staticmethod - def exit(status): raise SystemExit(status) - @staticmethod - def getProperty(key, default=None): - import os - return os.environ.get(key, default) - @staticmethod - def setProperty(key, value): - import os - os.environ[key] = value - return value - @staticmethod - def clearProperty(key): - return _os.environ.pop(key, None) - @staticmethod - def getenv(name=None): - if name is None: - return dict(_os.environ) - return _os.environ.get(name) - @staticmethod - def console(): - if System._console is None: - System._console = _Console() - return System._console - @staticmethod - def setIn(in_stream): - System.in_ = in_stream - @staticmethod - def setOut(out_stream): - System.out = out_stream - @staticmethod - def setErr(err_stream): - System.err = err_stream - @staticmethod - def lineSeparator(): return _os.linesep - @staticmethod - def getProperties(): - return { - "java.version": "1.8.0", - "java.vendor": "Android-pyApi", - "java.vendor.url": "https://gitee.com/shuiyu1123/Android-pyApi", - "java.home": _os.environ.get("JAVA_HOME", ""), - "java.class.version": "52.0", - "os.name": platform.system(), - "os.arch": platform.machine(), - "os.version": platform.release(), - "file.separator": _os.sep, - "path.separator": _os.pathsep, - "line.separator": _os.linesep, - "user.name": _os.environ.get("USER", _os.environ.get("USERNAME", "")), - "user.home": _os.path.expanduser("~"), - "user.dir": _os.getcwd(), - } - @staticmethod - def setProperties(props): - System._properties = props - @staticmethod - def mapLibraryName(libname): - if platform.system() == "Windows": - return libname + ".dll" - elif platform.system() == "Darwin": - return "lib" + libname + ".dylib" - else: - return "lib" + libname + ".so" - @staticmethod - def getLogger(name): - import logging - return logging.getLogger(name) - in_ = _sys.stdin - out = _sys.stdout - err = _sys.stderr - - -class Thread(Runnable): - """java.lang.Thread""" - MIN_PRIORITY = 1 - NORM_PRIORITY = 5 - MAX_PRIORITY = 10 - - # Thread.State 常量(java.lang.Thread.State) - NEW = "NEW" - RUNNABLE = "RUNNABLE" - BLOCKED = "BLOCKED" - WAITING = "WAITING" - TIMED_WAITING = "TIMED_WAITING" - TERMINATED = "TERMINATED" - - # 按 Python 线程 ident 缓存 Thread 对象,供 currentThread() 复用 - _thread_cache = {} - _default_uncaught_exception_handler = None - - def __init__(self, target=None, name=None): - super().__init__() - self._target = target - self._name = name or f"Thread-{id(self)}" - self._alive = False - self._daemon = False - self._interrupted = False - self._priority = Thread.NORM_PRIORITY - self._state = Thread.NEW - self._py_thread = None - self._id = _threading.get_ident() - - def start(self): - self._alive = True - self._state = Thread.RUNNABLE - self._py_thread = _threading.Thread( - target=self._run_wrapper, daemon=self._daemon, name=self._name - ) - self._py_thread.start() - self._id = self._py_thread.ident - - def _run_wrapper(self): - tid = _threading.get_ident() - self._id = tid - Thread._thread_cache[tid] = self - try: - self.run() - except (SystemExit, KeyboardInterrupt): - raise - except BaseException as e: - # 注意:模块内 Exception 已被 java.lang.Exception 覆盖, - # 这里用 BaseException 捕获 Python 原生异常并转发给未捕获异常处理器 - handler = Thread._default_uncaught_exception_handler - if handler is not None: - try: - handler.uncaughtException(self, e) - except BaseException: - pass - finally: - self._alive = False - self._state = Thread.TERMINATED - Thread._thread_cache.pop(tid, None) - - def run(self): - if self._target: - self._target() - - def join(self, millis=0): - if self._py_thread is not None: - if millis and millis > 0: - self._py_thread.join(millis / 1000.0) - else: - self._py_thread.join() - - def interrupt(self): - self._interrupted = True - - def isInterrupted(self): - return self._interrupted - - @staticmethod - def interrupted(): - tid = _threading.get_ident() - t = Thread._thread_cache.get(tid) - if t is not None: - status = t._interrupted - t._interrupted = False - return status - return False - - def isAlive(self): - if self._py_thread is not None: - return self._py_thread.is_alive() - return self._alive - - def getState(self): - if self._py_thread is not None: - if self._py_thread.is_alive(): - return Thread.RUNNABLE - return Thread.TERMINATED - return self._state - - def getPriority(self): - return self._priority - - def setPriority(self, priority): - if Thread.MIN_PRIORITY <= priority <= Thread.MAX_PRIORITY: - self._priority = priority - - def getId(self): - return self._id - - def getName(self): return self._name - def setName(self, n): self._name = n - def setDaemon(self, d): self._daemon = d - def isDaemon(self): return self._daemon - - @staticmethod - def currentThread(): - tid = _threading.get_ident() - t = Thread._thread_cache.get(tid) - if t is not None: - return t - # 未缓存的线程(如主线程):创建并缓存一个代表对象 - try: - is_main = (tid == _threading.main_thread().ident) - except Exception: - is_main = False - t = Thread(name="main" if is_main else f"Thread-{tid}") - t._state = Thread.RUNNABLE - t._alive = True - t._id = tid - Thread._thread_cache[tid] = t - return t - - @staticmethod - def sleep(millis): - _time.sleep(millis / 1000.0) - - @staticmethod - def yield_(): - _time.sleep(0) - - @staticmethod - def setDefaultUncaughtExceptionHandler(handler): - Thread._default_uncaught_exception_handler = handler - - @staticmethod - def getDefaultUncaughtExceptionHandler(): - return Thread._default_uncaught_exception_handler - - def getUncaughtExceptionHandler(self): - return getattr(self, '_uncaught_handler', Thread._default_uncaught_exception_handler) - - def setUncaughtExceptionHandler(self, handler): - self._uncaught_handler = handler - - @staticmethod - def getAllStackTraces(): - import traceback - result = {} - # 只返回当前线程 - result[Thread.currentThread()] = traceback.extract_stack() - return result - - def getStackTrace(self): - import traceback - return traceback.extract_stack() - - @staticmethod - def enumerate(array): - # 返回活动线程数 - return 0 - - @staticmethod - def activeCount(): - import threading - return threading.active_count() - - def getContextClassLoader(self): - return getattr(self, '_context_classloader', None) - - def setContextClassLoader(self, cl): - self._context_classloader = cl - - def getThreadGroup(self): - return getattr(self, '_thread_group', None) - - @staticmethod - def holdLock(obj): - return False - - @staticmethod - def dumpStack(): - import traceback - traceback.print_stack() - - @staticmethod - def onSpinWait(): - pass - - def isVirtual(self): - return False - - @staticmethod - def ofVirtual(): - """创建虚拟线程 (Java 21+)""" - t = Thread(target=lambda: None) - t._virtual = True - return t - - -class ThreadLocal(Stub): - """java.lang.ThreadLocal""" - def __init__(self): - super().__init__() - self._data = {} - - def get(self): - tid = _threading.get_ident() - return self._data.get(tid, None) - def set(self, value): - self._data[_threading.get_ident()] = value - def remove(self): - self._data.pop(_threading.get_ident(), None) - - -class StringBuilder(Stub): - """java.lang.StringBuilder — 可变字符序列""" - def __init__(self, s=""): - super().__init__() - self._buf = list(str(s)) - - def append(self, obj): - if isinstance(obj, bool): - self._buf.append("true" if obj else "false") - else: - self._buf.append(str(obj)) - return self - - def insert(self, offset, obj): - self._buf[offset:offset] = list(str(obj)) - return self - - def delete(self, start, end): - del self._buf[start:end] - return self - - def deleteCharAt(self, index): - del self._buf[index] - return self - - def replace(self, start, end, s): - self._buf[start:end] = list(s) - return self - - def reverse(self): - self._buf.reverse() - return self - - def toString(self): return "".join(self._buf) - def length(self): return len(self._buf) - def capacity(self): return len(self._buf) + 16 - def charAt(self, i): return self._buf[i] - def setCharAt(self, i, c): self._buf[i] = c - def substring(self, s, e=None): - return "".join(self._buf[s:e]) - def indexOf(self, s, f=0): - return self.toString().find(s, f) - def lastIndexOf(self, s, f=None): - return self.toString().rfind(s) - - def ensureCapacity(self, minimumCapacity): - pass # Python list 自动扩容 - - def trimToSize(self): - pass - - def setLength(self, newLength): - current = len(self._buf) - if newLength < current: - del self._buf[newLength:] - else: - self._buf.extend([" "] * (newLength - current)) - - def subSequence(self, start, end): - return "".join(self._buf[start:end]) - - def getChars(self, srcBegin, srcEnd, dst, dstBegin): - for i in range(srcBegin, srcEnd): - dst[dstBegin + i - srcBegin] = self._buf[i] - - def codePointAt(self, index): - return ord(self._buf[index]) - - def codePointBefore(self, index): - return ord(self._buf[index - 1]) - - def codePointCount(self, beginIndex, endIndex): - return endIndex - beginIndex - - -class StringBuffer(StringBuilder): - """java.lang.StringBuffer — 线程安全的可变字符序列""" - pass - - -class Class(Stub): - """java.lang.Class""" - def __init__(self, cls): - super().__init__() - self._cls = cls - def getName(self): return self._cls.__name__ - def getSimpleName(self): return self._cls.__name__ - def getCanonicalName(self): return self._cls.__name__ - def isInterface(self): return isinstance(self._cls, type) - def isArray(self): return self._cls is list - def isPrimitive(self): return self._cls in (int, float, bool, str, bytes) - def getSuperclass(self): return Class(self._cls.__base__) if self._cls.__base__ else None - def cast(self, obj): return obj - - -class Void(Stub): - """java.lang.Void""" - TYPE = None - - -class VoidType(Stub): - """java.lang.Void (inner type)""" - pass - - -# 注解类 -class SuppressWarnings(Stub): - pass -class Deprecated(Stub): - pass -class Override(Stub): - pass -class FunctionalInterface(Stub): - pass \ No newline at end of file diff --git a/android_pyapi/java/lang_annotation.py b/android_pyapi/java/lang_annotation.py deleted file mode 100644 index 7fe12cf..0000000 --- a/android_pyapi/java/lang_annotation.py +++ /dev/null @@ -1,303 +0,0 @@ -""" -Java java.lang.annotation APIs implemented in Python. -java.lang.annotation package - Annotation, Retention, Target, Documented, Inherited, etc. -Desktop wrapper classes mirroring the Java annotation framework. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -from ..java.lang import Error - -__all__ = [ - "Annotation", "Documented", "Inherited", "Repeatable", "Retention", - "RetentionPolicy", "Target", "ElementType", "Native", - "IncompleteAnnotationException", "AnnotationFormatError", -] - - -# ============================================================ -# RetentionPolicy enum -# ============================================================ - -class RetentionPolicy(Stub): - """ - java.lang.annotation.RetentionPolicy - Enum-like class for annotation retention policies. - """ - SOURCE: "RetentionPolicy" = None - CLASS: "RetentionPolicy" = None - RUNTIME: "RetentionPolicy" = None - - def __init__(self, name: str): - super().__init__() - self._name = name - - def name(self) -> str: - return self._name - - def toString(self) -> str: - return self._name - - def equals(self, other) -> bool: - if isinstance(other, RetentionPolicy): - return self._name == other._name - return False - - def hashCode(self) -> int: - return hash(self._name) & 0x7FFFFFFF - - def ordinal(self) -> int: - return ["SOURCE", "CLASS", "RUNTIME"].index(self._name) - - -RetentionPolicy.SOURCE = RetentionPolicy("SOURCE") -RetentionPolicy.CLASS = RetentionPolicy("CLASS") -RetentionPolicy.RUNTIME = RetentionPolicy("RUNTIME") - - -# ============================================================ -# ElementType enum -# ============================================================ - -class ElementType(Stub): - """ - java.lang.annotation.ElementType - Enum-like class for annotation target element types. - """ - TYPE: "ElementType" = None - FIELD: "ElementType" = None - METHOD: "ElementType" = None - PARAMETER: "ElementType" = None - CONSTRUCTOR: "ElementType" = None - LOCAL_VARIABLE: "ElementType" = None - ANNOTATION_TYPE: "ElementType" = None - PACKAGE: "ElementType" = None - TYPE_PARAMETER: "ElementType" = None - TYPE_USE: "ElementType" = None - MODULE: "ElementType" = None - RECORD_COMPONENT: "ElementType" = None - - def __init__(self, name: str): - super().__init__() - self._name = name - - def name(self) -> str: - return self._name - - def toString(self) -> str: - return self._name - - def equals(self, other) -> bool: - if isinstance(other, ElementType): - return self._name == other._name - return False - - def hashCode(self) -> int: - return hash(self._name) & 0x7FFFFFFF - - def ordinal(self) -> int: - return [ - "TYPE", "FIELD", "METHOD", "PARAMETER", "CONSTRUCTOR", - "LOCAL_VARIABLE", "ANNOTATION_TYPE", "PACKAGE", - "TYPE_PARAMETER", "TYPE_USE", "MODULE", "RECORD_COMPONENT", - ].index(self._name) - - -ElementType.TYPE = ElementType("TYPE") -ElementType.FIELD = ElementType("FIELD") -ElementType.METHOD = ElementType("METHOD") -ElementType.PARAMETER = ElementType("PARAMETER") -ElementType.CONSTRUCTOR = ElementType("CONSTRUCTOR") -ElementType.LOCAL_VARIABLE = ElementType("LOCAL_VARIABLE") -ElementType.ANNOTATION_TYPE = ElementType("ANNOTATION_TYPE") -ElementType.PACKAGE = ElementType("PACKAGE") -ElementType.TYPE_PARAMETER = ElementType("TYPE_PARAMETER") -ElementType.TYPE_USE = ElementType("TYPE_USE") -ElementType.MODULE = ElementType("MODULE") -ElementType.RECORD_COMPONENT = ElementType("RECORD_COMPONENT") - - -# ============================================================ -# Annotation (interface) -# ============================================================ - -class Annotation(Stub): - """ - java.lang.annotation.Annotation - The common interface for all annotation types. - """ - - def __init__(self): - super().__init__() - - def annotationType(self) -> type: - """Returns the annotation type of this annotation.""" - return self.__class__ - - def toString(self) -> str: - return f"@{self.__class__.__name__}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self.__class__.__name__) & 0x7FFFFFFF - - -# ============================================================ -# Documented -# ============================================================ - -class Documented(Annotation): - """ - java.lang.annotation.Documented - Indicates that annotations with a type are to be documented by javadoc. - """ - def __init__(self): - super().__init__() - - -# ============================================================ -# Inherited -# ============================================================ - -class Inherited(Annotation): - """ - java.lang.annotation.Inherited - Indicates that an annotation type is automatically inherited. - """ - def __init__(self): - super().__init__() - - -# ============================================================ -# Repeatable -# ============================================================ - -class Repeatable(Annotation): - """ - java.lang.annotation.Repeatable - Indicates that the annotation type can be applied more than once. - """ - def __init__(self, value: type = None): - super().__init__() - self._value = value - - def value(self) -> type: - return self._value - - def toString(self) -> str: - return f"@Repeatable(value={self._value})" if self._value else "@Repeatable" - - -# ============================================================ -# Retention -# ============================================================ - -class Retention(Annotation): - """ - java.lang.annotation.Retention - Indicates how long annotations with the annotated type are to be retained. - """ - def __init__(self, value: RetentionPolicy = RetentionPolicy.CLASS): - super().__init__() - self._value = value - - def value(self) -> RetentionPolicy: - return self._value - - def toString(self) -> str: - return f"@Retention(value={self._value})" - - -# ============================================================ -# Target -# ============================================================ - -class Target(Annotation): - """ - java.lang.annotation.Target - Indicates the contexts in which an annotation type is applicable. - """ - def __init__(self, value=None): - super().__init__() - self._value = value if value is not None else [] - - def value(self): - """Returns the element types this annotation can be applied to.""" - return self._value - - def toString(self) -> str: - return f"@Target(value={self._value})" - - -# ============================================================ -# Native -# ============================================================ - -class Native(Annotation): - """ - java.lang.annotation.Native - Indicates that a field defining a constant value may be referenced from native code. - """ - def __init__(self): - super().__init__() - - -# ============================================================ -# IncompleteAnnotationException -# ============================================================ - -class IncompleteAnnotationException(Exception, Stub): - """ - java.lang.annotation.IncompleteAnnotationException - Thrown when a program attempts to access an annotation with missing elements. - """ - def __init__(self, annotation_type: type = None, element_name: str = None): - super().__init__(f"Annotation {annotation_type} is missing element '{element_name}'") - self._annotation_type = annotation_type - self._element_name = element_name - - def annotationType(self) -> type: - return self._annotation_type - - def elementName(self) -> str: - return self._element_name - - def toString(self) -> str: - return f"IncompleteAnnotationException: {self._annotation_type} missing '{self._element_name}'" - - def equals(self, other) -> bool: - if not isinstance(other, IncompleteAnnotationException): - return False - return self._annotation_type == other._annotation_type and self._element_name == other._element_name - - def hashCode(self) -> int: - return hash((str(self._annotation_type), self._element_name)) & 0x7FFFFFFF - - -# ============================================================ -# AnnotationFormatError -# ============================================================ - -class AnnotationFormatError(Error, Stub): - """ - java.lang.annotation.AnnotationFormatError - Thrown when the annotation parser attempts to read an annotation from a class file - and determines that the annotation is malformed. - """ - def __init__(self, message: str = None, cause: Exception = None): - super().__init__(message, cause) - self._message = message - - def toString(self) -> str: - return f"AnnotationFormatError: {self._message}" - - def equals(self, other) -> bool: - if not isinstance(other, AnnotationFormatError): - return False - return self._message == other._message - - def hashCode(self) -> int: - return hash(self._message) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/java/lang_invoke.py b/android_pyapi/java/lang_invoke.py deleted file mode 100644 index c4af1c0..0000000 --- a/android_pyapi/java/lang_invoke.py +++ /dev/null @@ -1,766 +0,0 @@ -""" -Java java.lang.invoke standard library APIs implemented in Python. -java.lang.invoke package - MethodHandle, MethodType, MethodHandles, CallSite, VarHandle, etc. -""" - -from __future__ import annotations - -from .._core import Stub -from ..java.lang import RuntimeException, Exception as _JavaException -import typing as _typing -import types as _py_types -import inspect as _inspect -import functools as _functools - -__all__ = [ - "MethodHandle", "MethodType", "MethodHandles", "MethodHandles_Lookup", - "CallSite", "ConstantCallSite", "MutableCallSite", "VolatileCallSite", - "SwitchPoint", "VarHandle", "VarHandle_AccessMode", - "MethodHandleInfo", "MethodHandleProxies", - "SerializedLambda", "LambdaMetafactory", "StringConcatFactory", - "REF_GET_FIELD", "REF_GET_STATIC", "REF_PUT_FIELD", - "REF_PUT_STATIC", "REF_INVOKE_VIRTUAL", "REF_INVOKE_STATIC", - "REF_INVOKE_SPECIAL", "REF_NEW_INVOKE_SPECIAL", - "REF_INVOKE_INTERFACE", - "LT_GLOBAL", "LT_NEW", -] - -# ============================================================ -# Reference Constants -# ============================================================ - -REF_GET_FIELD = 1 -REF_GET_STATIC = 2 -REF_PUT_FIELD = 3 -REF_PUT_STATIC = 4 -REF_INVOKE_VIRTUAL = 5 -REF_INVOKE_STATIC = 6 -REF_INVOKE_SPECIAL = 7 -REF_NEW_INVOKE_SPECIAL = 8 -REF_INVOKE_INTERFACE = 9 - -LT_GLOBAL = "global" -LT_NEW = "new" - - -# ============================================================ -# MethodType -# ============================================================ - -class MethodType(Stub): - """java.lang.invoke.MethodType""" - def __init__(self, return_type, param_types=None): - super().__init__() - self._return_type = return_type - self._param_types = list(param_types or []) - - def returnType(self): - return self._return_type - - def parameterCount(self): - return len(self._param_types) - - def parameterType(self, index): - return self._param_types[index] - - def parameterList(self): - return list(self._param_types) - - def parameterArray(self): - return list(self._param_types) - - def appendParameterTypes(self, *types): - return MethodType(self._return_type, self._param_types + list(types)) - - def changeReturnType(self, return_type): - return MethodType(return_type, self._param_types) - - def changeParameterType(self, index, param_type): - new_params = list(self._param_types) - new_params[index] = param_type - return MethodType(self._return_type, new_params) - - def dropParameterTypes(self, start, end): - new_params = self._param_types[:start] + self._param_types[end:] - return MethodType(self._return_type, new_params) - - def insertParameterTypes(self, index, *types): - new_params = self._param_types[:index] + list(types) + self._param_types[index:] - return MethodType(self._return_type, new_params) - - def genericMethodType(self, object_count, final_array=False): - return MethodType(object, [object] * object_count) - - @staticmethod - def methodType(return_type, *param_types): - return MethodType(return_type, list(param_types)) - - @staticmethod - def genericMethodType(object_count, final_array=False): - return MethodType(object, [object] * object_count) - - def toString(self): - params = ", ".join(str(t) for t in self._param_types) - return f"({params}) -> {self._return_type}" - - def equals(self, other): - if not isinstance(other, MethodType): - return False - return (self._return_type == other._return_type - and self._param_types == other._param_types) - - def hashCode(self): - return hash((self._return_type, tuple(self._param_types))) - - def wrap(self): - return self - - def unwrap(self): - return self - - -# ============================================================ -# MethodHandle -# ============================================================ - -class MethodHandle(Stub): - """java.lang.invoke.MethodHandle""" - def __init__(self, method_type=None, target=None): - super().__init__() - self._type = method_type or MethodType(object, []) - self._target = target - - def type(self): - return self._type - - def invoke(self, *args): - if self._target is None: - return None - return self._target(*args) - - def invokeExact(self, *args): - return self.invoke(*args) - - def invokeWithArguments(self, *args): - return self.invoke(*args) - - def asType(self, new_type): - return self._target - - def asCollector(self, array_type, array_length): - return self - - def asSpreader(self, array_type, array_length): - return self - - def bindTo(self, argument): - bound_target = None - if self._target is not None: - bound_target = _functools.partial(self._target, argument) - return MethodHandle(self._type.dropParameterTypes(0, 1), bound_target) - - def toString(self): - return f"MethodHandle{self._type}" - - -# ============================================================ -# MethodHandles / Lookup -# ============================================================ - -class MethodHandles_Lookup(Stub): - """java.lang.invoke.MethodHandles.Lookup""" - def __init__(self, lookup_class=None): - super().__init__() - self._lookup_class = lookup_class - - def lookupClass(self): - return self._lookup_class - - def findStatic(self, refc, name, type_): - return MethodHandle(type_) - - def findVirtual(self, refc, name, type_): - return MethodHandle(type_) - - def findConstructor(self, refc, type_): - return MethodHandle(type_) - - def findSpecial(self, refc, name, type_, special_caller): - return MethodHandle(type_) - - def findGetter(self, refc, name, type_): - return MethodHandle(type_) - - def findSetter(self, refc, name, type_): - return MethodHandle(type_) - - def findStaticGetter(self, refc, name, type_): - return MethodHandle(type_) - - def findStaticSetter(self, refc, name, type_): - return MethodHandle(type_) - - def bind(self, receiver, name, type_): - return MethodHandle(type_) - - def unreflect(self, method): - return MethodHandle(MethodType(object, [])) - - def unreflectConstructor(self, constructor): - return MethodHandle(MethodType(object, [])) - - def unreflectGetter(self, field): - return MethodHandle(MethodType(object, [])) - - def unreflectSetter(self, field): - return MethodHandle(MethodType(object, [])) - - def dropLookupMode(self, mode_to_drop): - return MethodHandles_Lookup(self._lookup_class) - - def toString(self): - return f"Lookup[{self._lookup_class}]" - - PACKAGE = 8 - PRIVATE = 4 - PROTECTED = 2 - PUBLIC = 1 - - -class MethodHandles(Stub): - """java.lang.invoke.MethodHandles""" - @staticmethod - def lookup(): - return MethodHandles_Lookup() - - @staticmethod - def publicLookup(): - return MethodHandles_Lookup(object) - - @staticmethod - def arrayElementGetter(array_class): - return MethodHandle(MethodType(object, [array_class, int])) - - @staticmethod - def arrayElementSetter(array_class): - return MethodHandle(MethodType(None, [array_class, int, object])) - - @staticmethod - def identity(type_): - return MethodHandle(MethodType(type_, [type_])) - - @staticmethod - def constant(type_, value): - return MethodHandle(MethodType(type_, [])) - - @staticmethod - def zero(type_): - return MethodHandle(MethodType(type_, [])) - - @staticmethod - def filterReturnValue(target, filter_): - return target - - @staticmethod - def filterArguments(target, pos, *filters): - return target - - @staticmethod - def collectArguments(target, pos, filter_): - return target - - @staticmethod - def foldArguments(target, combiner): - return target - - @staticmethod - def guardWithTest(test, target, fallback): - class Guarded(MethodHandle): - def invoke(self, *args): - if test.invoke(*args): - return target.invoke(*args) - return fallback.invoke(*args) - return Guarded(MethodType(object, [])) - - @staticmethod - def throwException(return_type, ex_type): - return MethodHandle(MethodType(return_type, [ex_type])) - - @staticmethod - def spreadInvoker(type_, leading_arg_count): - return MethodHandle(type_) - - @staticmethod - def explicitCastArguments(target, new_type): - return target - - @staticmethod - def permuteArguments(target, new_type, *reorder): - return target - - @staticmethod - def catchException(target, ex_type, handler): - return target - - @staticmethod - def tryFinally(target, cleanup): - return target - - @staticmethod - def loop(loop_handles): - return MethodHandle(MethodType(object, [])) - - -# ============================================================ -# CallSite / ConstantCallSite / MutableCallSite / VolatileCallSite -# ============================================================ - -class CallSite(Stub): - """java.lang.invoke.CallSite""" - def __init__(self, target=None): - super().__init__() - self._target = target - - def type(self): - if self._target: - return self._target.type() - return MethodType(object, []) - - def target(self): - return self._target - - def setTarget(self, new_target): - self._target = new_target - - def dynamicInvoker(self): - return self._target - - -class ConstantCallSite(CallSite): - """java.lang.invoke.ConstantCallSite""" - def __init__(self, target): - super().__init__(target) - - def setTarget(self, new_target): - raise UnsupportedOperationException("ConstantCallSite target is immutable") - - -class MutableCallSite(CallSite): - """java.lang.invoke.MutableCallSite""" - def __init__(self, target_or_type): - if isinstance(target_or_type, MethodType): - super().__init__(MethodHandle(target_or_type)) - else: - super().__init__(target_or_type) - - def syncAll(self, sites): - pass - - def setTarget(self, new_target): - if hasattr(self, '_target'): - self._target = new_target - - -class VolatileCallSite(MutableCallSite): - """java.lang.invoke.VolatileCallSite""" - def __init__(self, target_or_type): - super().__init__(target_or_type) - - def setTarget(self, new_target): - super().setTarget(new_target) - - def target(self): - return self._target - - -# ============================================================ -# SwitchPoint -# ============================================================ - -class SwitchPoint(Stub): - """java.lang.invoke.SwitchPoint""" - def __init__(self): - super().__init__() - self._has_been_invalidated = False - - def hasBeenInvalidated(self): - return self._has_been_invalidated - - def invalidateAll(self): - self._has_been_invalidated = True - - @staticmethod - def guardWithTest(switch_point, target, fallback): - class GuardedSwitch(MethodHandle): - def invoke(self, *args): - if switch_point.hasBeenInvalidated(): - return fallback.invoke(*args) - return target.invoke(*args) - return GuardedSwitch(MethodType(object, [])) - - -# ============================================================ -# VarHandle -# ============================================================ - -class VarHandle_AccessMode(Stub): - """java.lang.invoke.VarHandle.AccessMode""" - def __init__(self, name): - super().__init__() - self._name = name - - def name(self): - return self._name - - def toString(self): - return self._name - - # Access mode constants - GET = None - SET = None - GET_VOLATILE = None - SET_VOLATILE = None - GET_ACQUIRE = None - SET_RELEASE = None - GET_OPAQUE = None - SET_OPAQUE = None - COMPARE_AND_SET = None - COMPARE_AND_EXCHANGE = None - COMPARE_AND_EXCHANGE_ACQUIRE = None - COMPARE_AND_EXCHANGE_RELEASE = None - WEAK_COMPARE_AND_SET = None - WEAK_COMPARE_AND_SET_ACQUIRE = None - WEAK_COMPARE_AND_SET_RELEASE = None - GET_AND_SET = None - GET_AND_SET_ACQUIRE = None - GET_AND_SET_RELEASE = None - GET_AND_ADD = None - GET_AND_ADD_ACQUIRE = None - GET_AND_ADD_RELEASE = None - GET_AND_BITWISE_OR = None - GET_AND_BITWISE_OR_ACQUIRE = None - GET_AND_BITWISE_OR_RELEASE = None - GET_AND_BITWISE_AND = None - GET_AND_BITWISE_AND_ACQUIRE = None - GET_AND_BITWISE_AND_RELEASE = None - GET_AND_BITWISE_XOR = None - GET_AND_BITWISE_XOR_ACQUIRE = None - GET_AND_BITWISE_XOR_RELEASE = None - GET = None - - -class VarHandle(Stub): - """java.lang.invoke.VarHandle""" - def __init__(self, var_type=None, coordinate_types=None): - super().__init__() - self._var_type = var_type or object - self._coordinate_types = list(coordinate_types or []) - self._access_modes = {} - - def get(self, *coordinates): - return None - - def set(self, *coordinates_and_value): - pass - - def getVolatile(self, *coordinates): - return self.get(*coordinates) - - def setVolatile(self, *coordinates_and_value): - self.set(*coordinates_and_value) - - def getAcquire(self, *coordinates): - return self.get(*coordinates) - - def setRelease(self, *coordinates_and_value): - self.set(*coordinates_and_value) - - def getOpaque(self, *coordinates): - return self.get(*coordinates) - - def setOpaque(self, *coordinates_and_value): - self.set(*coordinates_and_value) - - def compareAndSet(self, *args): - return True - - def compareAndExchange(self, *args): - return args[-2] - - def compareAndExchangeAcquire(self, *args): - return self.compareAndExchange(*args) - - def compareAndExchangeRelease(self, *args): - return self.compareAndExchange(*args) - - def weakCompareAndSet(self, *args): - return True - - def weakCompareAndSetAcquire(self, *args): - return True - - def weakCompareAndSetRelease(self, *args): - return True - - def getAndSet(self, *coordinates_and_value): - return None - - def getAndSetAcquire(self, *coordinates_and_value): - return self.getAndSet(*coordinates_and_value) - - def getAndSetRelease(self, *coordinates_and_value): - return self.getAndSet(*coordinates_and_value) - - def getAndAdd(self, *coordinates_and_value): - return 0 - - def getAndAddAcquire(self, *coordinates_and_value): - return self.getAndAdd(*coordinates_and_value) - - def getAndAddRelease(self, *coordinates_and_value): - return self.getAndAdd(*coordinates_and_value) - - def getAndBitwiseOr(self, *coordinates_and_value): - return 0 - - def getAndBitwiseAnd(self, *coordinates_and_value): - return 0 - - def getAndBitwiseXor(self, *coordinates_and_value): - return 0 - - def varType(self): - return self._var_type - - def coordinateTypes(self): - return self._coordinate_types - - def toMethodHandle(self, access_mode): - return MethodHandle(MethodType(self._var_type, self._coordinate_types)) - - def getAccessModes(self): - return list(self._access_modes.values()) - - def isAccessModeSupported(self, access_mode): - return True - - AccessMode = VarHandle_AccessMode - - def toString(self): - return f"VarHandle[{self._var_type}]" - - -# ============================================================ -# Initialize VarHandle_AccessMode constants -# ============================================================ - -VarHandle_AccessMode.GET = VarHandle_AccessMode("GET") -VarHandle_AccessMode.SET = VarHandle_AccessMode("SET") -VarHandle_AccessMode.GET_VOLATILE = VarHandle_AccessMode("GET_VOLATILE") -VarHandle_AccessMode.SET_VOLATILE = VarHandle_AccessMode("SET_VOLATILE") -VarHandle_AccessMode.GET_ACQUIRE = VarHandle_AccessMode("GET_ACQUIRE") -VarHandle_AccessMode.SET_RELEASE = VarHandle_AccessMode("SET_RELEASE") -VarHandle_AccessMode.GET_OPAQUE = VarHandle_AccessMode("GET_OPAQUE") -VarHandle_AccessMode.SET_OPAQUE = VarHandle_AccessMode("SET_OPAQUE") -VarHandle_AccessMode.COMPARE_AND_SET = VarHandle_AccessMode("COMPARE_AND_SET") -VarHandle_AccessMode.COMPARE_AND_EXCHANGE = VarHandle_AccessMode("COMPARE_AND_EXCHANGE") -VarHandle_AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE = VarHandle_AccessMode("COMPARE_AND_EXCHANGE_ACQUIRE") -VarHandle_AccessMode.COMPARE_AND_EXCHANGE_RELEASE = VarHandle_AccessMode("COMPARE_AND_EXCHANGE_RELEASE") -VarHandle_AccessMode.WEAK_COMPARE_AND_SET = VarHandle_AccessMode("WEAK_COMPARE_AND_SET") -VarHandle_AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE = VarHandle_AccessMode("WEAK_COMPARE_AND_SET_ACQUIRE") -VarHandle_AccessMode.WEAK_COMPARE_AND_SET_RELEASE = VarHandle_AccessMode("WEAK_COMPARE_AND_SET_RELEASE") -VarHandle_AccessMode.GET_AND_SET = VarHandle_AccessMode("GET_AND_SET") -VarHandle_AccessMode.GET_AND_SET_ACQUIRE = VarHandle_AccessMode("GET_AND_SET_ACQUIRE") -VarHandle_AccessMode.GET_AND_SET_RELEASE = VarHandle_AccessMode("GET_AND_SET_RELEASE") -VarHandle_AccessMode.GET_AND_ADD = VarHandle_AccessMode("GET_AND_ADD") -VarHandle_AccessMode.GET_AND_ADD_ACQUIRE = VarHandle_AccessMode("GET_AND_ADD_ACQUIRE") -VarHandle_AccessMode.GET_AND_ADD_RELEASE = VarHandle_AccessMode("GET_AND_ADD_RELEASE") -VarHandle_AccessMode.GET_AND_BITWISE_OR = VarHandle_AccessMode("GET_AND_BITWISE_OR") -VarHandle_AccessMode.GET_AND_BITWISE_OR_ACQUIRE = VarHandle_AccessMode("GET_AND_BITWISE_OR_ACQUIRE") -VarHandle_AccessMode.GET_AND_BITWISE_OR_RELEASE = VarHandle_AccessMode("GET_AND_BITWISE_OR_RELEASE") -VarHandle_AccessMode.GET_AND_BITWISE_AND = VarHandle_AccessMode("GET_AND_BITWISE_AND") -VarHandle_AccessMode.GET_AND_BITWISE_AND_ACQUIRE = VarHandle_AccessMode("GET_AND_BITWISE_AND_ACQUIRE") -VarHandle_AccessMode.GET_AND_BITWISE_AND_RELEASE = VarHandle_AccessMode("GET_AND_BITWISE_AND_RELEASE") -VarHandle_AccessMode.GET_AND_BITWISE_XOR = VarHandle_AccessMode("GET_AND_BITWISE_XOR") -VarHandle_AccessMode.GET_AND_BITWISE_XOR_ACQUIRE = VarHandle_AccessMode("GET_AND_BITWISE_XOR_ACQUIRE") -VarHandle_AccessMode.GET_AND_BITWISE_XOR_RELEASE = VarHandle_AccessMode("GET_AND_BITWISE_XOR_RELEASE") - - -# ============================================================ -# MethodHandleInfo -# ============================================================ - -class MethodHandleInfo(Stub): - """java.lang.invoke.MethodHandleInfo""" - def __init__(self, reference_kind=0, declaring_class=None, name="", method_type=None): - super().__init__() - self._reference_kind = reference_kind - self._declaring_class = declaring_class - self._name = name - self._method_type = method_type or MethodType(object, []) - - def getReferenceKind(self): - return self._reference_kind - - def getDeclaringClass(self): - return self._declaring_class - - def getName(self): - return self._name - - def getMethodType(self): - return self._method_type - - def isVarArgs(self): - return False - - def reflectAs(self, expected_type, lookup): - return None - - def toString(self): - return (f"MethodHandleInfo[{self._name}, kind={self._reference_kind}, " - f"class={self._declaring_class}, type={self._method_type}]") - - # Reference kind constants - REF_getField = REF_GET_FIELD - REF_getStatic = REF_GET_STATIC - REF_putField = REF_PUT_FIELD - REF_putStatic = REF_PUT_STATIC - REF_invokeVirtual = REF_INVOKE_VIRTUAL - REF_invokeStatic = REF_INVOKE_STATIC - REF_invokeSpecial = REF_INVOKE_SPECIAL - REF_newInvokeSpecial = REF_NEW_INVOKE_SPECIAL - REF_invokeInterface = REF_INVOKE_INTERFACE - - -# ============================================================ -# MethodHandleProxies -# ============================================================ - -class MethodHandleProxies(Stub): - """java.lang.invoke.MethodHandleProxies""" - @staticmethod - def asInterfaceInstance(iface, target): - return target - - @staticmethod - def isWrapperClass(cls): - return False - - @staticmethod - def wrapperInstanceTarget(proxy): - return None - - @staticmethod - def wrapperInstanceType(proxy): - return None - - -# ============================================================ -# SerializedLambda -# ============================================================ - -class SerializedLambda(Stub): - """java.lang.invoke.SerializedLambda""" - def __init__(self): - super().__init__() - self._capturing_class = "" - self._functional_interface_class_name = "" - self._functional_interface_method_name = "" - self._functional_interface_method_signature = "" - self._impl_class = "" - self._impl_method_name = "" - self._impl_method_signature = "" - self._impl_method_kind = 0 - self._captured_args = [] - self._instantiated_method_type = "" - self._captured_arg_count = 0 - - def getCapturingClass(self): - return self._capturing_class - - def getFunctionalInterfaceClassName(self): - return self._functional_interface_class_name - - def getFunctionalInterfaceMethodName(self): - return self._functional_interface_method_name - - def getFunctionalInterfaceMethodSignature(self): - return self._functional_interface_method_signature - - def getImplClass(self): - return self._impl_class - - def getImplMethodName(self): - return self._impl_method_name - - def getImplMethodSignature(self): - return self._impl_method_signature - - def getImplMethodKind(self): - return self._impl_method_kind - - def getCapturedArgCount(self): - return self._captured_arg_count - - def getCapturedArg(self, i): - return self._captured_args[i] if i < len(self._captured_args) else None - - def getInstantiatedMethodType(self): - return self._instantiated_method_type - - -# ============================================================ -# LambdaMetafactory -# ============================================================ - -class LambdaMetafactory(Stub): - """java.lang.invoke.LambdaMetafactory""" - FLAG_SERIALIZABLE = 1 - FLAG_MARKERS = 2 - FLAG_BRIDGES = 4 - - @staticmethod - def metafactory(caller, invoked_name, invoked_type, sam_method_type, - impl_method, instantiated_method_type, *args): - return MethodHandle(instantiated_method_type) - - @staticmethod - def altMetafactory(caller, invoked_name, invoked_type, *args): - return MethodHandle(MethodType(object, [])) - - -# ============================================================ -# StringConcatFactory -# ============================================================ - -class StringConcatFactory(Stub): - """java.lang.invoke.StringConcatFactory""" - @staticmethod - def makeConcatWithConstants(lookup, name, concat_type, recipe, *constants): - return MethodHandle(concat_type) - - @staticmethod - def makeConcat(lookup, name, concat_type): - class ConcatHandle(MethodHandle): - def invoke(self, *args): - return "".join(str(a) for a in args) - return ConcatHandle(concat_type, lambda *args: "".join(str(a) for a in args)) - - -# ============================================================ -# UnsupportedOperationException -# ============================================================ - -class UnsupportedOperationException(RuntimeException): - """java.lang.UnsupportedOperationException""" - pass - - -# ============================================================ -# Alias for MethodHandles.Lookup -# ============================================================ - -MethodHandles_Lookup = MethodHandles_Lookup -MethodHandles.Lookup = MethodHandles_Lookup -VarHandle.AccessMode = VarHandle_AccessMode \ No newline at end of file diff --git a/android_pyapi/java/lang_management.py b/android_pyapi/java/lang_management.py deleted file mode 100644 index f947ef3..0000000 --- a/android_pyapi/java/lang_management.py +++ /dev/null @@ -1,830 +0,0 @@ -""" -Java java.lang.management standard library APIs implemented in Python. -java.lang.management package - ManagementFactory, MXBeans, ThreadInfo, etc. -""" - -from __future__ import annotations - -from .._core import Stub -from ..java.lang import RuntimeException, StackTraceElement -import typing as _typing -import time as _time -import os as _os -import sys as _sys -import threading as _threading -import platform as _platform - -__all__ = [ - "ManagementFactory", "MemoryMXBean", "MemoryUsage", "MemoryType", - "MemoryPoolMXBean", "MemoryManagerMXBean", "GarbageCollectorMXBean", - "ThreadMXBean", "RuntimeMXBean", "ClassLoadingMXBean", - "OperatingSystemMXBean", "CompilationMXBean", - "PlatformLoggingMXBean", "PlatformManagedObject", - "ManagementPermission", "LockInfo", "MonitorInfo", "ThreadInfo", -] - - -# ============================================================ -# ManagementPermission -# ============================================================ - -class ManagementPermission(Stub): - """java.lang.management.ManagementPermission""" - def __init__(self, name, actions=None): - super().__init__() - self._name = name - self._actions = actions - - def getName(self): - return self._name - - def getActions(self): - return self._actions or "" - - def implies(self, permission): - return True - - -# ============================================================ -# MemoryType -# ============================================================ - -class MemoryType(Stub): - """java.lang.management.MemoryType""" - def __init__(self, name): - super().__init__() - self._name = name - - def toString(self): - return self._name - - HEAP = None - NON_HEAP = None - - -MemoryType.HEAP = MemoryType("HEAP") -MemoryType.NON_HEAP = MemoryType("NON_HEAP") - - -# ============================================================ -# MemoryUsage -# ============================================================ - -class MemoryUsage(Stub): - """java.lang.management.MemoryUsage""" - def __init__(self, init, used, committed, max_val): - super().__init__() - self._init = init - self._used = used - self._committed = committed - self._max = max_val - - def getInit(self): - return self._init - - def getUsed(self): - return self._used - - def getCommitted(self): - return self._committed - - def getMax(self): - return self._max - - def toString(self): - return (f"init={self._init} used={self._used} " - f"committed={self._committed} max={self._max}") - - -# ============================================================ -# LockInfo -# ============================================================ - -class LockInfo(Stub): - """java.lang.management.LockInfo""" - def __init__(self, class_name, identity_hash_code): - super().__init__() - self._class_name = class_name - self._identity_hash_code = identity_hash_code - - def getClassName(self): - return self._class_name - - def getIdentityHashCode(self): - return self._identity_hash_code - - def toString(self): - return f"{self._class_name}@{self._identity_hash_code:x}" - - -# ============================================================ -# MonitorInfo -# ============================================================ - -class MonitorInfo(LockInfo): - """java.lang.management.MonitorInfo""" - def __init__(self, class_name, identity_hash_code, - locked_stack_depth, locked_stack_frame): - super().__init__(class_name, identity_hash_code) - self._locked_stack_depth = locked_stack_depth - self._locked_stack_frame = locked_stack_frame - - def getLockedStackDepth(self): - return self._locked_stack_depth - - def getLockedStackFrame(self): - return self._locked_stack_frame - - def toString(self): - return (f"MonitorInfo[{self._class_name}@{self._identity_hash_code:x}, " - f"depth={self._locked_stack_depth}]") - - -# ============================================================ -# ThreadInfo -# ============================================================ - -class ThreadInfo(Stub): - """java.lang.management.ThreadInfo""" - def __init__(self, thread_id=0, thread_name="", thread_state="RUNNABLE", - blocked_count=0, blocked_time=-1, waited_count=0, waited_time=-1, - lock_name=None, lock_owner_id=-1, lock_owner_name=None, - in_native=False, suspended=False, stack_trace=None, - locked_monitors=None, locked_synchronizers=None, - daemon=False, priority=5): - super().__init__() - self._thread_id = thread_id - self._thread_name = thread_name - self._thread_state = thread_state - self._blocked_count = blocked_count - self._blocked_time = blocked_time - self._waited_count = waited_count - self._waited_time = waited_time - self._lock_name = lock_name - self._lock_owner_id = lock_owner_id - self._lock_owner_name = lock_owner_name - self._in_native = in_native - self._suspended = suspended - self._stack_trace = stack_trace or [] - self._locked_monitors = locked_monitors or [] - self._locked_synchronizers = locked_synchronizers or [] - self._daemon = daemon - self._priority = priority - - def getThreadId(self): - return self._thread_id - - def getThreadName(self): - return self._thread_name - - def getThreadState(self): - return self._thread_state - - def getBlockedCount(self): - return self._blocked_count - - def getBlockedTime(self): - return self._blocked_time - - def getWaitedCount(self): - return self._waited_count - - def getWaitedTime(self): - return self._waited_time - - def getLockName(self): - return self._lock_name - - def getLockOwnerId(self): - return self._lock_owner_id - - def getLockOwnerName(self): - return self._lock_owner_name - - def isInNative(self): - return self._in_native - - def isSuspended(self): - return self._suspended - - def getStackTrace(self): - return self._stack_trace - - def getLockedMonitors(self): - return self._locked_monitors - - def getLockedSynchronizers(self): - return self._locked_synchronizers - - def isDaemon(self): - return self._daemon - - def getPriority(self): - return self._priority - - def toString(self): - return (f"ThreadInfo[id={self._thread_id}, name={self._thread_name}, " - f"state={self._thread_state}]") - - -# ============================================================ -# PlatformManagedObject -# ============================================================ - -class PlatformManagedObject(Stub): - """java.lang.management.PlatformManagedObject""" - def getObjectName(self): - return None - - -# ============================================================ -# MemoryMXBean -# ============================================================ - -class MemoryMXBean(PlatformManagedObject): - """java.lang.management.MemoryMXBean""" - def __init__(self): - super().__init__() - import psutil as _psutil - self._psutil_available = False - try: - import psutil - self._psutil = psutil - self._psutil_available = True - except ImportError: - self._psutil_available = False - - def getHeapMemoryUsage(self): - if self._psutil_available: - mem = self._psutil.virtual_memory() - return MemoryUsage(mem.total, mem.used, mem.available, mem.total) - return MemoryUsage(0, 0, 0, 0) - - def getNonHeapMemoryUsage(self): - return MemoryUsage(0, 0, 0, 0) - - def getObjectPendingFinalizationCount(self): - return 0 - - def isVerbose(self): - return False - - def setVerbose(self, value): - pass - - def gc(self): - import gc - gc.collect() - - -# ============================================================ -# MemoryPoolMXBean -# ============================================================ - -class MemoryPoolMXBean(PlatformManagedObject): - """java.lang.management.MemoryPoolMXBean""" - def __init__(self, name="", pool_type=MemoryType.HEAP): - super().__init__() - self._name = name - self._type = pool_type - self._usage = MemoryUsage(0, 0, 0, 0) - self._peak_usage = MemoryUsage(0, 0, 0, 0) - self._collection_usage = None - self._valid = True - self._threshold = 0 - self._collection_threshold = 0 - self._threshold_supported = False - self._collection_threshold_supported = False - - def getName(self): - return self._name - - def getType(self): - return self._type - - def getUsage(self): - import psutil - try: - mem = psutil.virtual_memory() - self._usage = MemoryUsage(mem.total, mem.used, mem.available, mem.total) - except ImportError: - pass - return self._usage - - def getPeakUsage(self): - return self._peak_usage - - def resetPeakUsage(self): - self._peak_usage = MemoryUsage(0, 0, 0, 0) - - def isValid(self): - return self._valid - - def getCollectionUsage(self): - return self._collection_usage - - def getUsageThreshold(self): - return self._threshold - - def setUsageThreshold(self, threshold): - self._threshold = threshold - self._threshold_supported = True - - def isUsageThresholdSupported(self): - return self._threshold_supported - - def isUsageThresholdExceeded(self): - return self._usage.getUsed() > self._threshold if self._threshold > 0 else False - - def getUsageThresholdCount(self): - return 0 - - def getCollectionUsageThreshold(self): - return self._collection_threshold - - def setCollectionUsageThreshold(self, threshold): - self._collection_threshold = threshold - self._collection_threshold_supported = True - - def isCollectionUsageThresholdSupported(self): - return self._collection_threshold_supported - - def isCollectionUsageThresholdExceeded(self): - return False - - def getCollectionUsageThresholdCount(self): - return 0 - - def getMemoryManagerNames(self): - return [] - - def toString(self): - return f"MemoryPoolMXBean[name={self._name}, type={self._type}]" - - -# ============================================================ -# MemoryManagerMXBean -# ============================================================ - -class MemoryManagerMXBean(PlatformManagedObject): - """java.lang.management.MemoryManagerMXBean""" - def __init__(self, name=""): - super().__init__() - self._name = name - self._valid = True - self._pool_names = [] - - def getName(self): - return self._name - - def isValid(self): - return self._valid - - def getMemoryPoolNames(self): - return self._pool_names - - def toString(self): - return f"MemoryManagerMXBean[name={self._name}]" - - -# ============================================================ -# GarbageCollectorMXBean -# ============================================================ - -class GarbageCollectorMXBean(MemoryManagerMXBean): - """java.lang.management.GarbageCollectorMXBean""" - def __init__(self, name=""): - super().__init__(name) - self._collection_count = 0 - self._collection_time = 0 - - def getCollectionCount(self): - return self._collection_count - - def getCollectionTime(self): - return self._collection_time - - def toString(self): - return (f"GarbageCollectorMXBean[name={self._name}, " - f"collections={self._collection_count}, " - f"time={self._collection_time}ms]") - - -# ============================================================ -# ThreadMXBean -# ============================================================ - -class ThreadMXBean(PlatformManagedObject): - """java.lang.management.ThreadMXBean""" - def __init__(self): - super().__init__() - self._thread_contention_monitoring_enabled = False - self._thread_cpu_time_enabled = False - self._thread_allocated_memory_enabled = False - - def getThreadCount(self): - return _threading.active_count() - - def getPeakThreadCount(self): - return _threading.active_count() - - def getTotalStartedThreadCount(self): - return _threading.active_count() - - def getDaemonThreadCount(self): - return 0 - - def getAllThreadIds(self): - return [t.ident for t in _threading.enumerate() if t.ident is not None] - - def getThreadInfo(self, thread_id_or_ids, max_depth=None): - if isinstance(thread_id_or_ids, (list, tuple)): - return [self._build_thread_info(tid) for tid in thread_id_or_ids] - return self._build_thread_info(thread_id_or_ids) - - def _build_thread_info(self, thread_id): - for t in _threading.enumerate(): - if t.ident == thread_id: - return ThreadInfo( - thread_id=thread_id, - thread_name=t.name or "", - thread_state=t.name or "RUNNABLE", - ) - return ThreadInfo(thread_id=thread_id, thread_name="") - - def isThreadContentionMonitoringSupported(self): - return True - - def isThreadContentionMonitoringEnabled(self): - return self._thread_contention_monitoring_enabled - - def setThreadContentionMonitoringEnabled(self, enable): - self._thread_contention_monitoring_enabled = enable - - def isCurrentThreadCpuTimeSupported(self): - return True - - def isThreadCpuTimeEnabled(self): - return self._thread_cpu_time_enabled - - def setThreadCpuTimeEnabled(self, enable): - self._thread_cpu_time_enabled = enable - - def getCurrentThreadCpuTime(self): - return 0 - - def getCurrentThreadUserTime(self): - return 0 - - def getThreadCpuTime(self, thread_id): - return 0 - - def getThreadUserTime(self, thread_id): - return 0 - - def isObjectMonitorUsageSupported(self): - return True - - def isSynchronizerUsageSupported(self): - return True - - def findMonitorDeadlockedThreads(self): - return None - - def findDeadlockedThreads(self): - return None - - def resetPeakThreadCount(self): - pass - - def getThreadAllocatedBytes(self, thread_id_or_ids): - if isinstance(thread_id_or_ids, (list, tuple)): - return [0] * len(thread_id_or_ids) - return 0 - - def isThreadAllocatedMemorySupported(self): - return True - - def isThreadAllocatedMemoryEnabled(self): - return self._thread_allocated_memory_enabled - - def setThreadAllocatedMemoryEnabled(self, enable): - self._thread_allocated_memory_enabled = enable - - def getTotalThreadAllocatedBytes(self): - return 0 - - -# ============================================================ -# RuntimeMXBean -# ============================================================ - -class RuntimeMXBean(PlatformManagedObject): - """java.lang.management.RuntimeMXBean""" - def __init__(self): - super().__init__() - self._start_time = _time.time() * 1000 - - def getName(self): - return f"{_os.getpid()}@{_platform.node()}" - - getVmName = lambda self: _platform.python_implementation() - getVmVersion = lambda self: _platform.python_version() - getVmVendor = lambda self: "Python Software Foundation" - getSpecName = lambda self: "Python Virtual Machine Specification" - getSpecVersion = lambda self: _sys.version - getSpecVendor = lambda self: "Python Software Foundation" - - def getManagementSpecVersion(self): - return "1.0" - - def getStartTime(self): - return self._start_time - - def getUptime(self): - return (_time.time() * 1000) - self._start_time - - def getInputArguments(self): - return _sys.argv[1:] - - def getClassPath(self): - return _sys.path - - def getBootClassPath(self): - return "" - - def getLibraryPath(self): - return _os.environ.get("PATH", "") - - def getSystemProperties(self): - return { - "java.version": _platform.python_version(), - "java.vendor": "Python", - "java.vm.version": _platform.python_version(), - "os.name": _platform.system(), - "os.arch": _platform.machine(), - "os.version": _platform.release(), - "user.dir": _os.getcwd(), - "user.home": _os.path.expanduser("~"), - "user.name": _os.environ.get("USER", ""), - "line.separator": "\n", - "file.separator": _os.sep, - "path.separator": _os.pathsep, - } - - def isBootClassPathSupported(self): - return False - - -# ============================================================ -# ClassLoadingMXBean -# ============================================================ - -class ClassLoadingMXBean(PlatformManagedObject): - """java.lang.management.ClassLoadingMXBean""" - def __init__(self): - super().__init__() - self._verbose = False - - def getTotalLoadedClassCount(self): - return 0 - - def getLoadedClassCount(self): - return 0 - - def getUnloadedClassCount(self): - return 0 - - def isVerbose(self): - return self._verbose - - def setVerbose(self, value): - self._verbose = value - - -# ============================================================ -# OperatingSystemMXBean -# ============================================================ - -class OperatingSystemMXBean(PlatformManagedObject): - """java.lang.management.OperatingSystemMXBean""" - def __init__(self): - super().__init__() - self._available = False - try: - import psutil - self._psutil = psutil - self._available = True - except ImportError: - self._available = False - - def getArch(self): - return _platform.machine() - - def getAvailableProcessors(self): - return _os.cpu_count() or 1 - - def getSystemLoadAverage(self): - if self._available: - return self._psutil.getloadavg()[0] - return -1.0 - - def getName(self): - return _platform.system() - - def getVersion(self): - return _platform.release() - - def getProcessCpuTime(self): - return 0 - - def getCommittedVirtualMemorySize(self): - return 0 - - def getTotalSwapSpaceSize(self): - if self._available: - return self._psutil.swap_memory().total - return 0 - - def getFreeSwapSpaceSize(self): - if self._available: - return self._psutil.swap_memory().free - return 0 - - def getProcessCpuLoad(self): - return 0.0 - - def getSystemCpuLoad(self): - if self._available: - return self._psutil.cpu_percent(interval=0) / 100.0 - return 0.0 - - def getTotalPhysicalMemorySize(self): - if self._available: - return self._psutil.virtual_memory().total - return 0 - - def getFreePhysicalMemorySize(self): - if self._available: - return self._psutil.virtual_memory().available - return 0 - - def getTotalDiskSpace(self): - return 0 - - def getFreeDiskSpace(self): - return 0 - - def getSystemUptime(self): - return _time.time() - _time.monotonic() + _time.monotonic() - - -# ============================================================ -# CompilationMXBean -# ============================================================ - -class CompilationMXBean(PlatformManagedObject): - """java.lang.management.CompilationMXBean""" - def getName(self): - return "CPython" - - def isCompilationTimeMonitoringSupported(self): - return False - - def getTotalCompilationTime(self): - return 0 - - -# ============================================================ -# PlatformLoggingMXBean -# ============================================================ - -class PlatformLoggingMXBean(PlatformManagedObject): - """java.lang.management.PlatformLoggingMXBean""" - def getLoggerLevel(self, logger_name): - return "" - - def setLoggerLevel(self, logger_name, level_name): - pass - - def getParentLogger(self, logger_name): - return "" - - def getLoggerNames(self): - return [] - - -# ============================================================ -# ManagementFactory -# ============================================================ - -class ManagementFactory(Stub): - """java.lang.management.ManagementFactory""" - CLASS_LOADING_MXBEAN_NAME = "java.lang:type=ClassLoading" - COMPILATION_MXBEAN_NAME = "java.lang:type=Compilation" - GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE = "java.lang:type=GarbageCollector" - MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE = "java.lang:type=MemoryManager" - MEMORY_MXBEAN_NAME = "java.lang:type=Memory" - MEMORY_POOL_MXBEAN_DOMAIN_TYPE = "java.lang:type=MemoryPool" - OPERATING_SYSTEM_MXBEAN_NAME = "java.lang:type=OperatingSystem" - RUNTIME_MXBEAN_NAME = "java.lang:type=Runtime" - THREAD_MXBEAN_NAME = "java.lang:type=Threading" - LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging" - - _memory_mxbean = None - _runtime_mxbean = None - _thread_mxbean = None - _class_loading_mxbean = None - _os_mxbean = None - _compilation_mxbean = None - _platform_logging_mxbean = None - _memory_pools = None - _memory_managers = None - _gc_mxbeans = None - - @staticmethod - def getMemoryMXBean(): - if ManagementFactory._memory_mxbean is None: - ManagementFactory._memory_mxbean = MemoryMXBean() - return ManagementFactory._memory_mxbean - - @staticmethod - def getRuntimeMXBean(): - if ManagementFactory._runtime_mxbean is None: - ManagementFactory._runtime_mxbean = RuntimeMXBean() - return ManagementFactory._runtime_mxbean - - @staticmethod - def getThreadMXBean(): - if ManagementFactory._thread_mxbean is None: - ManagementFactory._thread_mxbean = ThreadMXBean() - return ManagementFactory._thread_mxbean - - @staticmethod - def getClassLoadingMXBean(): - if ManagementFactory._class_loading_mxbean is None: - ManagementFactory._class_loading_mxbean = ClassLoadingMXBean() - return ManagementFactory._class_loading_mxbean - - @staticmethod - def getOperatingSystemMXBean(): - if ManagementFactory._os_mxbean is None: - ManagementFactory._os_mxbean = OperatingSystemMXBean() - return ManagementFactory._os_mxbean - - @staticmethod - def getCompilationMXBean(): - if ManagementFactory._compilation_mxbean is None: - ManagementFactory._compilation_mxbean = CompilationMXBean() - return ManagementFactory._compilation_mxbean - - @staticmethod - def getPlatformLoggingMXBean(): - if ManagementFactory._platform_logging_mxbean is None: - ManagementFactory._platform_logging_mxbean = PlatformLoggingMXBean() - return ManagementFactory._platform_logging_mxbean - - @staticmethod - def getMemoryPoolMXBeans(): - if ManagementFactory._memory_pools is None: - ManagementFactory._memory_pools = [ - MemoryPoolMXBean("Code Cache", MemoryType.NON_HEAP), - MemoryPoolMXBean("Metaspace", MemoryType.NON_HEAP), - MemoryPoolMXBean("Compressed Class Space", MemoryType.NON_HEAP), - MemoryPoolMXBean("PS Eden Space", MemoryType.HEAP), - MemoryPoolMXBean("PS Survivor Space", MemoryType.HEAP), - MemoryPoolMXBean("PS Old Gen", MemoryType.HEAP), - ] - return ManagementFactory._memory_pools - - @staticmethod - def getMemoryManagerMXBeans(): - if ManagementFactory._memory_managers is None: - ManagementFactory._memory_managers = [ - MemoryManagerMXBean("CodeCacheManager"), - MemoryManagerMXBean("Metaspace Manager"), - MemoryManagerMXBean("PS Scavenge"), - MemoryManagerMXBean("PS MarkSweep"), - ] - return ManagementFactory._memory_managers - - @staticmethod - def getGarbageCollectorMXBeans(): - if ManagementFactory._gc_mxbeans is None: - ManagementFactory._gc_mxbeans = [ - GarbageCollectorMXBean("PS Scavenge"), - GarbageCollectorMXBean("PS MarkSweep"), - ] - return ManagementFactory._gc_mxbeans - - @staticmethod - def getPlatformMXBeans(interface_class): - return [] - - @staticmethod - def newPlatformMXBeanProxy(server_connection, mxbean_name, interface_class): - return None - - @staticmethod - def getPlatformManagedObject(object_name): - return None \ No newline at end of file diff --git a/android_pyapi/java/lang_ref.py b/android_pyapi/java/lang_ref.py deleted file mode 100644 index d8294ad..0000000 --- a/android_pyapi/java/lang_ref.py +++ /dev/null @@ -1,386 +0,0 @@ -""" -Java java.lang.ref reference APIs implemented in Python. -java.lang.ref package - Reference, ReferenceQueue, SoftReference, WeakReference, PhantomReference, Finalizer. -Wraps Python weakref module. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -import weakref as _weakref -import threading as _threading -import gc as _gc - -__all__ = [ - "Reference", "SoftReference", "WeakReference", "PhantomReference", - "ReferenceQueue", "Finalizer", "FinalizerReference", "FinalizationWatchdog", -] - - -# ============================================================ -# ReferenceQueue -# ============================================================ - -class ReferenceQueue(Stub): - """ - java.lang.ref.ReferenceQueue - A queue that References are appended to when they are enqueued. - """ - - def __init__(self): - super().__init__() - self._queue: list = [] - self._lock = _threading.Lock() - self._cond = _threading.Condition(self._lock) - - def poll(self): - """ - Polls to see if a reference object is available. - Returns the reference if available, None otherwise. - """ - with self._lock: - if self._queue: - return self._queue.pop(0) - return None - - def remove(self, timeout: int = 0): - """ - Removes the next reference object from this queue, blocking until one is available - or the timeout expires. - Args: - timeout: timeout in milliseconds (0 = indefinite) - Returns: - The reference, or None if timeout expired. - """ - with self._lock: - if self._queue: - return self._queue.pop(0) - if timeout == 0: - self._cond.wait() - if self._queue: - return self._queue.pop(0) - return None - else: - self._cond.wait(timeout / 1000.0) - if self._queue: - return self._queue.pop(0) - return None - - def enqueue(self, ref: "Reference") -> bool: - """ - Enqueues a reference onto this queue. - Returns True if successful. - """ - with self._lock: - self._queue.append(ref) - self._cond.notify() - return True - - def __len__(self): - return len(self._queue) - - -# ============================================================ -# Reference (abstract base) -# ============================================================ - -class Reference(Stub): - """ - java.lang.ref.Reference - Abstract base class for reference objects. - """ - - def __init__(self, referent=None, queue: ReferenceQueue = None): - super().__init__() - self._referent = referent - self._queue = queue - self._enqueued = False - # Underlying weakref to track referent lifetime - self._weak = None - if referent is not None: - self._weak = _weakref.ref( - referent, - self._on_referent_collected - ) - - def _on_referent_collected(self, weakref_obj): - """Called when the referent is garbage collected.""" - if self._queue is not None and not self._enqueued: - self._enqueued = True - self._queue.enqueue(self) - - def get(self): - """ - Returns this reference object's referent. - If the referent has been garbage collected, returns None. - """ - if self._weak is not None: - return self._weak() - return self._referent - - def clear(self): - """Clears this reference object.""" - self._referent = None - if self._weak is not None: - self._weak = None - - def isEnqueued(self) -> bool: - """Returns whether this reference object has been enqueued.""" - return self._enqueued - - def enqueue(self) -> bool: - """Enqueues this reference object.""" - if self._queue is not None and not self._enqueued: - self._enqueued = True - return self._queue.enqueue(self) - return False - - def clone(self): - """Creates a shallow copy of this reference.""" - return self.__class__(self.get(), self._queue) - - def toString(self) -> str: - return f"{self.__class__.__name__}@{hex(id(self))}[referent={self.get()}]" - - def equals(self, other) -> bool: - if not isinstance(other, Reference): - return False - return self.get() is other.get() - - def hashCode(self) -> int: - r = self.get() - return hash(r) & 0x7FFFFFFF if r is not None else 0 - - -# ============================================================ -# SoftReference -# ============================================================ - -class SoftReference(Reference): - """ - java.lang.ref.SoftReference - Soft reference objects, cleared at the discretion of the GC. - """ - - def __init__(self, referent=None, queue: ReferenceQueue = None): - super().__init__(referent, queue) - self._clock = 0 - - def get(self): - """Returns the referent; on desktop, soft references are more resilient.""" - return super().get() - - def enqueue(self) -> bool: - return super().enqueue() - - def clear(self): - super().clear() - - def toString(self) -> str: - return f"SoftReference@{hex(id(self))}[referent={self.get()}]" - - -# ============================================================ -# WeakReference -# ============================================================ - -class WeakReference(Reference): - """ - java.lang.ref.WeakReference - Weak reference objects, cleared automatically by the GC. - """ - - def __init__(self, referent=None, queue: ReferenceQueue = None): - super().__init__(referent, queue) - - def toString(self) -> str: - return f"WeakReference@{hex(id(self))}[referent={self.get()}]" - - -# ============================================================ -# PhantomReference -# ============================================================ - -class PhantomReference(Reference): - """ - java.lang.ref.PhantomReference - Phantom reference objects. get() always returns None. - """ - - def __init__(self, referent=None, queue: ReferenceQueue = None): - super().__init__(referent, queue) - - def get(self): - """ - Phantom references always return None. - The referent is phantom-reachable and thus not accessible. - """ - return None - - def toString(self) -> str: - return f"PhantomReference@{hex(id(self))}[referent=phantom]" - - -# ============================================================ -# FinalizerReference -# ============================================================ - -class FinalizerReference(Reference): - """ - java.lang.ref.FinalizerReference - Represents a finalizer reference used by the VM's finalization system. - """ - - def __init__(self, referent=None, queue: ReferenceQueue = None): - super().__init__(referent, queue) - self._finalized = False - self._finalize_method = None - if referent is not None and hasattr(referent, "finalize"): - self._finalize_method = getattr(referent, "finalize", None) - - def finalize(self): - """Invokes the referent's finalize method if it exists.""" - if not self._finalized and self._finalize_method is not None: - try: - self._finalize_method() - except Exception: - pass - finally: - self._finalized = True - - def isFinalized(self) -> bool: - return self._finalized - - def toString(self) -> str: - return f"FinalizerReference@{hex(id(self))}[finalized={self._finalized}]" - - -# ============================================================ -# FinalizationWatchdog -# ============================================================ - -class FinalizationWatchdog(Stub): - """ - java.lang.ref.FinalizationWatchdog - A watchdog thread that ensures finalizations proceed. - Desktop stub implementation. - """ - - _instance = None - _running = False - _thread = None - - def __init__(self): - super().__init__() - self._timeout = 10000 # 10 seconds - - @classmethod - def getInstance(cls): - if cls._instance is None: - cls._instance = FinalizationWatchdog() - return cls._instance - - def start(self): - """Starts the watchdog thread.""" - if not self._running: - self._running = True - self._thread = _threading.Thread(target=self._run, daemon=True) - self._thread.start() - - def stop(self): - """Stops the watchdog thread.""" - self._running = False - - def _run(self): - """Watchdog loop.""" - while self._running: - _gc.collect() - import time - time.sleep(1) - - def setEnabled(self, enabled: bool): - if enabled: - self.start() - else: - self.stop() - - def toString(self) -> str: - return f"FinalizationWatchdog@{hex(id(self))}[running={self._running}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Finalizer -# ============================================================ - -class Finalizer(Stub): - """ - java.lang.ref.Finalizer - Manages object finalization. Provides methods to run finalization. - """ - - _instance = None - - def __init__(self): - super().__init__() - - @classmethod - def runFinalization(cls): - """ - Runs the finalization methods of any objects pending finalization. - Equivalent to calling System.runFinalization(). - """ - _gc.collect() - for obj in _gc.get_objects(): - if hasattr(obj, "finalize"): - try: - obj.finalize() - except Exception: - pass - - @classmethod - def runFinalizersOnExit(cls, value: bool): - """ - Sets whether finalization is enabled on exit. - Deprecated in Java. Desktop stub. - """ - if value: - import atexit - atexit.register(cls._finalize_all) - - @classmethod - def _finalize_all(cls): - """Finalize all objects on exit.""" - for obj in _gc.get_objects(): - if hasattr(obj, "finalize"): - try: - obj.finalize() - except Exception: - pass - - @classmethod - def finalizeAllEnqueued(cls): - """ - Finalizes all objects that have been enqueued for finalization. - """ - _gc.collect() - for obj in _gc.get_objects(): - if hasattr(obj, "finalize"): - try: - obj.finalize() - except Exception: - pass - - def toString(self) -> str: - return f"Finalizer@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/java/lang_reflect.py b/android_pyapi/java/lang_reflect.py deleted file mode 100644 index 0df5774..0000000 --- a/android_pyapi/java/lang_reflect.py +++ /dev/null @@ -1,968 +0,0 @@ -""" -Java java.lang.reflect APIs implemented in Python. -java.lang.reflect package - Field, Method, Constructor, Array, Modifier, Proxy, etc. -Wraps Python inspect module. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -import inspect as _inspect -import types as _types -from typing import Any, List, Optional, Tuple - -__all__ = [ - "Field", "Method", "Constructor", "Array", "Modifier", - "InvocationHandler", "Proxy", "Parameter", - "AccessibleObject", "Executable", - "GenericArrayType", "ParameterizedType", "TypeVariable", - "WildcardType", "GenericDeclaration", - "AnnotatedType", "AnnotatedArrayType", "AnnotatedParameterizedType", - "AnnotatedTypeVariable", "AnnotatedWildcardType", -] - - -# ============================================================ -# Modifier -# ============================================================ - -class Modifier(Stub): - """ - java.lang.reflect.Modifier - Provides static methods and constants for decoding class and member access modifiers. - Uses actual JVM modifier bit values. - """ - PUBLIC: int = 0x0001 - PRIVATE: int = 0x0002 - PROTECTED: int = 0x0004 - STATIC: int = 0x0008 - FINAL: int = 0x0010 - SYNCHRONIZED: int = 0x0020 - VOLATILE: int = 0x0040 - TRANSIENT: int = 0x0080 - NATIVE: int = 0x0100 - INTERFACE: int = 0x0200 - ABSTRACT: int = 0x0400 - STRICT: int = 0x0800 - - _MODIFIER_NAMES = { - 0x0001: "public", 0x0002: "private", 0x0004: "protected", - 0x0008: "static", 0x0010: "final", 0x0020: "synchronized", - 0x0040: "volatile", 0x0080: "transient", 0x0100: "native", - 0x0200: "interface", 0x0400: "abstract", 0x0800: "strictfp", - } - - def __init__(self): - super().__init__() - - @staticmethod - def isPublic(mod: int) -> bool: - return (mod & Modifier.PUBLIC) != 0 - - @staticmethod - def isPrivate(mod: int) -> bool: - return (mod & Modifier.PRIVATE) != 0 - - @staticmethod - def isProtected(mod: int) -> bool: - return (mod & Modifier.PROTECTED) != 0 - - @staticmethod - def isStatic(mod: int) -> bool: - return (mod & Modifier.STATIC) != 0 - - @staticmethod - def isFinal(mod: int) -> bool: - return (mod & Modifier.FINAL) != 0 - - @staticmethod - def isSynchronized(mod: int) -> bool: - return (mod & Modifier.SYNCHRONIZED) != 0 - - @staticmethod - def isVolatile(mod: int) -> bool: - return (mod & Modifier.VOLATILE) != 0 - - @staticmethod - def isTransient(mod: int) -> bool: - return (mod & Modifier.TRANSIENT) != 0 - - @staticmethod - def isNative(mod: int) -> bool: - return (mod & Modifier.NATIVE) != 0 - - @staticmethod - def isInterface(mod: int) -> bool: - return (mod & Modifier.INTERFACE) != 0 - - @staticmethod - def isAbstract(mod: int) -> bool: - return (mod & Modifier.ABSTRACT) != 0 - - @staticmethod - def isStrict(mod: int) -> bool: - return (mod & Modifier.STRICT) != 0 - - @staticmethod - def toString(mod: int) -> str: - parts = [] - for bit, name in sorted(Modifier._MODIFIER_NAMES.items()): - if mod & bit: - parts.append(name) - return " ".join(parts) - - @staticmethod - def classModifiers() -> int: - return Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE | \ - Modifier.ABSTRACT | Modifier.STATIC | Modifier.FINAL | Modifier.STRICT - - @staticmethod - def constructorModifiers() -> int: - return Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE - - @staticmethod - def fieldModifiers() -> int: - return Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE | \ - Modifier.STATIC | Modifier.FINAL | Modifier.TRANSIENT | Modifier.VOLATILE - - @staticmethod - def interfaceModifiers() -> int: - return Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE | \ - Modifier.ABSTRACT | Modifier.STATIC | Modifier.STRICT - - @staticmethod - def methodModifiers() -> int: - return Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE | \ - Modifier.ABSTRACT | Modifier.STATIC | Modifier.FINAL | \ - Modifier.SYNCHRONIZED | Modifier.NATIVE | Modifier.STRICT - - @staticmethod - def parameterModifiers() -> int: - return Modifier.FINAL - - -# ============================================================ -# AccessibleObject -# ============================================================ - -class AccessibleObject(Stub): - """ - java.lang.reflect.AccessibleObject - Base class for Field, Method, and Constructor. - """ - - def __init__(self): - super().__init__() - self._accessible = False - - def setAccessible(self, flag: bool): - """Sets the accessible flag for this object.""" - self._accessible = flag - - def isAccessible(self) -> bool: - """Returns the value of the accessible flag.""" - return self._accessible - - def getAnnotation(self, annotation_class: type) -> Optional[Stub]: - """Returns the annotation for this element (if present).""" - return None - - def getDeclaredAnnotations(self) -> List: - """Returns all annotations directly present on this element.""" - return [] - - def getAnnotations(self) -> List: - """Returns all annotations on this element.""" - return [] - - def getAnnotationsByType(self, annotation_class: type) -> List: - """Returns annotations of the specified type on this element.""" - return [] - - def isAnnotationPresent(self, annotation_class: type) -> bool: - """Returns True if the specified annotation is present.""" - return False - - def toString(self) -> str: - return f"{self.__class__.__name__}@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Field -# ============================================================ - -class Field(AccessibleObject): - """ - java.lang.reflect.Field - Provides information about, and dynamic access to, a single field of a class. - """ - - def __init__(self, declaring_class: type = None, name: str = None, - field_type: type = None, modifiers: int = 0): - super().__init__() - self._declaring_class = declaring_class - self._name = name - self._field_type = field_type - self._modifiers = modifiers - self._generic_type = field_type - - def getName(self) -> str: - return self._name or "" - - def getType(self) -> type: - return self._field_type or object - - def getModifiers(self) -> int: - return self._modifiers - - def get(self, obj: Any) -> Any: - """Returns the value of the field on the given object.""" - if self._name is None: - return None - if obj is None and Modifier.isStatic(self._modifiers): - # Static field access on the class - if self._declaring_class and hasattr(self._declaring_class, self._name): - return getattr(self._declaring_class, self._name) - return None - if obj is not None and hasattr(obj, self._name): - return getattr(obj, self._name) - return None - - def set(self, obj: Any, value: Any): - """Sets the field on the given object to the given value.""" - if self._name is None: - return - if obj is None and Modifier.isStatic(self._modifiers): - if self._declaring_class: - setattr(self._declaring_class, self._name, value) - return - if obj is not None and hasattr(obj, self._name): - setattr(obj, self._name, value) - - def setAccessible(self, flag: bool): - self._accessible = flag - - def getDeclaringClass(self) -> type: - return self._declaring_class - - def getGenericType(self): - return self._generic_type - - def getAnnotation(self, annotation_class: type): - return None - - def getDeclaredAnnotations(self) -> List: - return [] - - def isAccessible(self) -> bool: - return self._accessible - - def isSynthetic(self) -> bool: - return False - - def toGenericString(self) -> str: - return f"{Modifier.toString(self._modifiers)} {self._field_type.__name__ if self._field_type else '?'} {self._name}" - - def getBoolean(self, obj: Any) -> bool: - val = self.get(obj) - return bool(val) if val is not None else False - - def getByte(self, obj: Any) -> int: - val = self.get(obj) - return int(val) if val is not None else 0 - - def getChar(self, obj: Any) -> str: - val = self.get(obj) - return str(val)[0] if val else '\0' - - def getShort(self, obj: Any) -> int: - val = self.get(obj) - return int(val) if val is not None else 0 - - def getInt(self, obj: Any) -> int: - val = self.get(obj) - return int(val) if val is not None else 0 - - def getLong(self, obj: Any) -> int: - val = self.get(obj) - return int(val) if val is not None else 0 - - def getFloat(self, obj: Any) -> float: - val = self.get(obj) - return float(val) if val is not None else 0.0 - - def getDouble(self, obj: Any) -> float: - val = self.get(obj) - return float(val) if val is not None else 0.0 - - def setBoolean(self, obj: Any, value: bool): - self.set(obj, value) - - def setByte(self, obj: Any, value: int): - self.set(obj, value) - - def setChar(self, obj: Any, value: str): - self.set(obj, value) - - def setShort(self, obj: Any, value: int): - self.set(obj, value) - - def setInt(self, obj: Any, value: int): - self.set(obj, value) - - def setLong(self, obj: Any, value: int): - self.set(obj, value) - - def setFloat(self, obj: Any, value: float): - self.set(obj, value) - - def setDouble(self, obj: Any, value: float): - self.set(obj, value) - - def toString(self) -> str: - return self.toGenericString() - - def equals(self, other) -> bool: - if not isinstance(other, Field): - return False - return (self._declaring_class == other._declaring_class and - self._name == other._name and - self._field_type == other._field_type) - - def hashCode(self) -> int: - return hash((self._declaring_class, self._name)) & 0x7FFFFFFF - - -# ============================================================ -# Executable (abstract base for Method & Constructor) -# ============================================================ - -class Executable(AccessibleObject): - """ - java.lang.reflect.Executable - Shared base for Method and Constructor. - """ - - def __init__(self, declaring_class: type = None, name: str = None, - parameter_types: List[type] = None, exception_types: List[type] = None, - modifiers: int = 0): - super().__init__() - self._declaring_class = declaring_class - self._name = name - self._parameter_types = parameter_types or [] - self._exception_types = exception_types or [] - self._modifiers = modifiers - - def getParameterTypes(self) -> List[type]: - return list(self._parameter_types) - - def getParameterCount(self) -> int: - return len(self._parameter_types) - - def getExceptionTypes(self) -> List[type]: - return list(self._exception_types) - - def getModifiers(self) -> int: - return self._modifiers - - def getDeclaringClass(self) -> type: - return self._declaring_class - - def getName(self) -> str: - return self._name or "" - - def getAnnotation(self, annotation_class: type): - return None - - def getDeclaredAnnotations(self) -> List: - return [] - - def getParameterAnnotations(self) -> List[List]: - return [[] for _ in self._parameter_types] - - def isVarArgs(self) -> bool: - return False - - def isSynthetic(self) -> bool: - return False - - def toGenericString(self) -> str: - return f"{Modifier.toString(self._modifiers)} {self._name}({', '.join(t.__name__ for t in self._parameter_types)})" - - def toString(self) -> str: - return self.toGenericString() - - def equals(self, other) -> bool: - if not isinstance(other, Executable): - return False - return (self._declaring_class == other._declaring_class and - self._name == other._name and - self._parameter_types == other._parameter_types) - - def hashCode(self) -> int: - return hash((self._declaring_class, self._name)) & 0x7FFFFFFF - - -# ============================================================ -# Method -# ============================================================ - -class Method(Executable): - """ - java.lang.reflect.Method - Provides information about, and access to, a single method of a class. - """ - - def __init__(self, declaring_class: type = None, name: str = None, - return_type: type = None, parameter_types: List[type] = None, - exception_types: List[type] = None, modifiers: int = 0, - impl_func: callable = None): - super().__init__(declaring_class, name, parameter_types, exception_types, modifiers) - self._return_type = return_type - self._impl_func = impl_func - self._generic_return_type = return_type - self._generic_parameter_types = parameter_types or [] - - def getReturnType(self) -> type: - return self._return_type or type(None) - - def getGenericReturnType(self): - return self._generic_return_type - - def getGenericParameterTypes(self) -> List: - return list(self._generic_parameter_types) - - def invoke(self, obj: Any, *args) -> Any: - """ - Invokes the underlying method on the given object with the given args. - """ - if self._impl_func is not None: - return self._impl_func(obj, *args) - - if self._name is None: - return None - - # Try to call the method on the object - if obj is not None and hasattr(obj, self._name): - method = getattr(obj, self._name) - if callable(method): - return method(*args) - - # Static call on class - if self._declaring_class and hasattr(self._declaring_class, self._name): - method = getattr(self._declaring_class, self._name) - if callable(method): - return method(*args) - - return None - - def getDefaultValue(self): - """Returns the default value for the annotation member.""" - return None - - def isBridge(self) -> bool: - return False - - def toGenericString(self) -> str: - ret = self._return_type.__name__ if self._return_type else "void" - params = ', '.join(t.__name__ for t in self._parameter_types) - return f"{Modifier.toString(self._modifiers)} {ret} {self._name}({params})" - - def toString(self) -> str: - return self.toGenericString() - - def equals(self, other) -> bool: - if not isinstance(other, Method): - return False - return (self._declaring_class == other._declaring_class and - self._name == other._name and - self._parameter_types == other._parameter_types) - - def hashCode(self) -> int: - return hash((self._declaring_class, self._name)) & 0x7FFFFFFF - - -# ============================================================ -# Constructor -# ============================================================ - -class Constructor(Executable): - """ - java.lang.reflect.Constructor - Provides information about, and access to, a single constructor of a class. - """ - - def __init__(self, declaring_class: type = None, - parameter_types: List[type] = None, - exception_types: List[type] = None, - modifiers: int = 0): - super().__init__(declaring_class, declaring_class.__name__ if declaring_class else "", - parameter_types, exception_types, modifiers) - - def newInstance(self, *args) -> Any: - """Creates a new instance of the declaring class.""" - if self._declaring_class is None: - raise AndroidOnlyError("No declaring class for constructor") - try: - return self._declaring_class(*args) - except Exception as e: - raise AndroidOnlyError(f"Cannot instantiate {self._declaring_class}: {e}") - - def toGenericString(self) -> str: - params = ', '.join(t.__name__ for t in self._parameter_types) - return f"{Modifier.toString(self._modifiers)} {self._name}({params})" - - def toString(self) -> str: - return self.toGenericString() - - def equals(self, other) -> bool: - if not isinstance(other, Constructor): - return False - return (self._declaring_class == other._declaring_class and - self._parameter_types == other._parameter_types) - - def hashCode(self) -> int: - return hash((self._declaring_class, tuple(self._parameter_types))) & 0x7FFFFFFF - - -# ============================================================ -# Array -# ============================================================ - -class Array(Stub): - """ - java.lang.reflect.Array - Provides static methods to dynamically create and access arrays. - """ - - def __init__(self): - super().__init__() - - @staticmethod - def newInstance(component_type: type, *dimensions) -> Any: - """Creates a new array with the specified component type and dimensions.""" - if len(dimensions) == 1: - return [None] * dimensions[0] - # Multi-dimensional - result = [] - for _ in range(dimensions[0]): - result.append(Array.newInstance(component_type, *dimensions[1:])) - return result - - @staticmethod - def getLength(array: Any) -> int: - """Returns the length of the specified array.""" - return len(array) - - @staticmethod - def get(array: Any, index: int) -> Any: - """Returns the element at the specified index.""" - return array[index] - - @staticmethod - def set(array: Any, index: int, value: Any): - """Sets the element at the specified index.""" - array[index] = value - - @staticmethod - def getBoolean(array: Any, index: int) -> bool: - return bool(array[index]) - - @staticmethod - def getByte(array: Any, index: int) -> int: - return int(array[index]) - - @staticmethod - def getChar(array: Any, index: int) -> str: - return str(array[index]) - - @staticmethod - def getShort(array: Any, index: int) -> int: - return int(array[index]) - - @staticmethod - def getInt(array: Any, index: int) -> int: - return int(array[index]) - - @staticmethod - def getLong(array: Any, index: int) -> int: - return int(array[index]) - - @staticmethod - def getFloat(array: Any, index: int) -> float: - return float(array[index]) - - @staticmethod - def getDouble(array: Any, index: int) -> float: - return float(array[index]) - - @staticmethod - def setBoolean(array: Any, index: int, value: bool): - array[index] = value - - @staticmethod - def setByte(array: Any, index: int, value: int): - array[index] = value - - @staticmethod - def setChar(array: Any, index: int, value: str): - array[index] = value - - @staticmethod - def setShort(array: Any, index: int, value: int): - array[index] = value - - @staticmethod - def setInt(array: Any, index: int, value: int): - array[index] = value - - @staticmethod - def setLong(array: Any, index: int, value: int): - array[index] = value - - @staticmethod - def setFloat(array: Any, index: int, value: float): - array[index] = value - - @staticmethod - def setDouble(array: Any, index: int, value: float): - array[index] = value - - -# ============================================================ -# Parameter -# ============================================================ - -class Parameter(AccessibleObject): - """ - java.lang.reflect.Parameter - Information about a method parameter. - """ - - def __init__(self, name: str = None, param_type: type = None, - modifiers: int = 0, is_implicit: bool = False, - is_name_present: bool = True): - super().__init__() - self._name = name or "" - self._param_type = param_type or object - self._modifiers = modifiers - self._is_implicit = is_implicit - self._is_name_present = is_name_present - self._parameterized_type = param_type - - def getName(self) -> str: - return self._name - - def getType(self) -> type: - return self._param_type - - def getParameterizedType(self): - return self._parameterized_type - - def getModifiers(self) -> int: - return self._modifiers - - def isNamePresent(self) -> bool: - return self._is_name_present - - def isImplicit(self) -> bool: - return self._is_implicit - - def isSynthetic(self) -> bool: - return False - - def isVarArgs(self) -> bool: - return False - - def toString(self) -> str: - return f"{Modifier.toString(self._modifiers)} {self._param_type.__name__} {self._name}" - - def equals(self, other) -> bool: - if not isinstance(other, Parameter): - return False - return (self._name == other._name and - self._param_type == other._param_type) - - def hashCode(self) -> int: - return hash((self._name, self._param_type)) & 0x7FFFFFFF - - -# ============================================================ -# InvocationHandler -# ============================================================ - -class InvocationHandler(Stub): - """ - java.lang.reflect.InvocationHandler - Interface for invocation handlers of proxy instances. - """ - - def invoke(self, proxy: Any, method: Any, args: List) -> Any: - """Processes a method invocation on a proxy instance.""" - raise NotImplementedError("invoke must be implemented by subclass") - - -# ============================================================ -# Proxy -# ============================================================ - -class Proxy(Stub): - """ - java.lang.reflect.Proxy - Provides static methods for creating dynamic proxy classes and instances. - """ - - _invocation_handler: InvocationHandler = None - - def __init__(self, handler: InvocationHandler = None): - super().__init__() - self._invocation_handler = handler - - @staticmethod - def newProxyInstance(class_loader: Any, interfaces: List[type], - handler: InvocationHandler) -> Stub: - """Creates a proxy instance for the specified interfaces.""" - proxy = Proxy(handler) - proxy._interfaces = interfaces - return proxy - - @staticmethod - def getInvocationHandler(proxy: Any) -> InvocationHandler: - """Returns the invocation handler for the specified proxy instance.""" - if isinstance(proxy, Proxy): - return proxy._invocation_handler - return None - - @staticmethod - def getProxyClass(class_loader: Any, interfaces: List[type]) -> type: - """Returns the proxy class for the specified interfaces.""" - return Proxy - - @staticmethod - def isProxyClass(cls: type) -> bool: - """Returns True if the class is a proxy class.""" - return cls == Proxy or (hasattr(cls, '__bases__') and Proxy in cls.__bases__) - - def toString(self) -> str: - return f"Proxy@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Generic Array Type (stub interface) -# ============================================================ - -class GenericArrayType(Stub): - """ - java.lang.reflect.GenericArrayType - Represents an array type whose component type is a parameterized type or type variable. - """ - def __init__(self, generic_component_type=None): - super().__init__() - self._generic_component_type = generic_component_type - - def getGenericComponentType(self): - return self._generic_component_type - - def toString(self) -> str: - return f"{self._generic_component_type}[]" - - -# ============================================================ -# ParameterizedType -# ============================================================ - -class ParameterizedType(Stub): - """ - java.lang.reflect.ParameterizedType - Represents a parameterized type, e.g. List. - """ - def __init__(self, raw_type=None, actual_type_arguments=None, owner_type=None): - super().__init__() - self._raw_type = raw_type - self._actual_type_arguments = actual_type_arguments or [] - self._owner_type = owner_type - - def getRawType(self): - return self._raw_type - - def getActualTypeArguments(self): - return list(self._actual_type_arguments) - - def getOwnerType(self): - return self._owner_type - - def toString(self) -> str: - args = ', '.join(str(a) for a in self._actual_type_arguments) - return f"{self._raw_type}<{args}>" - - -# ============================================================ -# TypeVariable -# ============================================================ - -class TypeVariable(Stub): - """ - java.lang.reflect.TypeVariable - Represents a type variable, like T in Foo. - """ - def __init__(self, name: str = None, bounds: List = None, - generic_declaration: Any = None): - super().__init__() - self._name = name or "T" - self._bounds = bounds or [object] - self._generic_declaration = generic_declaration - - def getName(self) -> str: - return self._name - - def getBounds(self) -> List: - return list(self._bounds) - - def getGenericDeclaration(self) -> Any: - return self._generic_declaration - - def toString(self) -> str: - return self._name - - -# ============================================================ -# WildcardType -# ============================================================ - -class WildcardType(Stub): - """ - java.lang.reflect.WildcardType - Represents a wildcard type, like ?, ? extends T, ? super T. - """ - def __init__(self, upper_bounds: List = None, lower_bounds: List = None): - super().__init__() - self._upper_bounds = upper_bounds or [object] - self._lower_bounds = lower_bounds or [] - - def getUpperBounds(self) -> List: - return list(self._upper_bounds) - - def getLowerBounds(self) -> List: - return list(self._lower_bounds) - - def toString(self) -> str: - if self._lower_bounds: - return f"? super {self._lower_bounds[0]}" - if self._upper_bounds and self._upper_bounds[0] is not object: - return f"? extends {self._upper_bounds[0]}" - return "?" - - -# ============================================================ -# GenericDeclaration -# ============================================================ - -class GenericDeclaration(Stub): - """ - java.lang.reflect.GenericDeclaration - Interface for entities that declare type variables. - """ - def getTypeParameters(self) -> List[TypeVariable]: - return [] - - def toGenericString(self) -> str: - return self.toString() - - -# ============================================================ -# AnnotatedType interfaces -# ============================================================ - -class AnnotatedType(Stub): - """ - java.lang.reflect.AnnotatedType - Represents an annotated type. - """ - def __init__(self, type_=None, annotations: List = None): - super().__init__() - self._type = type_ - self._annotations = annotations or [] - - def getType(self): - return self._type - - def getAnnotations(self) -> List: - return list(self._annotations) - - def getAnnotation(self, annotation_class: type): - for a in self._annotations: - if isinstance(a, annotation_class): - return a - return None - - def toString(self) -> str: - return str(self._type) if self._type else "?" - - -class AnnotatedArrayType(AnnotatedType): - """ - java.lang.reflect.AnnotatedArrayType - Represents an annotated array type. - """ - def __init__(self, annotated_generic_component_type=None, type_=None, annotations=None): - super().__init__(type_, annotations) - self._annotated_generic_component_type = annotated_generic_component_type - - def getAnnotatedGenericComponentType(self) -> AnnotatedType: - return self._annotated_generic_component_type - - -class AnnotatedParameterizedType(AnnotatedType): - """ - java.lang.reflect.AnnotatedParameterizedType - Represents an annotated parameterized type. - """ - def __init__(self, annotated_actual_type_arguments=None, type_=None, annotations=None): - super().__init__(type_, annotations) - self._annotated_actual_type_arguments = annotated_actual_type_arguments or [] - - def getAnnotatedActualTypeArguments(self) -> List[AnnotatedType]: - return list(self._annotated_actual_type_arguments) - - -class AnnotatedTypeVariable(AnnotatedType): - """ - java.lang.reflect.AnnotatedTypeVariable - Represents an annotated type variable. - """ - def __init__(self, annotated_bounds=None, type_=None, annotations=None): - super().__init__(type_, annotations) - self._annotated_bounds = annotated_bounds or [] - - def getAnnotatedBounds(self) -> List[AnnotatedType]: - return list(self._annotated_bounds) - - -class AnnotatedWildcardType(AnnotatedType): - """ - java.lang.reflect.AnnotatedWildcardType - Represents an annotated wildcard type. - """ - def __init__(self, annotated_upper_bounds=None, annotated_lower_bounds=None, - type_=None, annotations=None): - super().__init__(type_, annotations) - self._annotated_upper_bounds = annotated_upper_bounds or [] - self._annotated_lower_bounds = annotated_lower_bounds or [] - - def getAnnotatedUpperBounds(self) -> List[AnnotatedType]: - return list(self._annotated_upper_bounds) - - def getAnnotatedLowerBounds(self) -> List[AnnotatedType]: - return list(self._annotated_lower_bounds) \ No newline at end of file diff --git a/android_pyapi/java/math.py b/android_pyapi/java/math.py deleted file mode 100644 index bdbf2c7..0000000 --- a/android_pyapi/java/math.py +++ /dev/null @@ -1,683 +0,0 @@ -""" -Java math standard library APIs implemented in Python. -java.math package - BigInteger, BigDecimal, MathContext, RoundingMode. -""" - -from .._core import Stub -from .lang import Comparable -from decimal import ( - Decimal as _Decimal, - ROUND_DOWN as _DEC_ROUND_DOWN, - ROUND_HALF_UP as _DEC_ROUND_HALF_UP, - ROUND_HALF_EVEN as _DEC_ROUND_HALF_EVEN, - ROUND_UP as _DEC_ROUND_UP, - ROUND_CEILING as _DEC_ROUND_CEILING, - ROUND_FLOOR as _DEC_ROUND_FLOOR, - ROUND_HALF_DOWN as _DEC_ROUND_HALF_DOWN, - localcontext as _local_context, - getcontext as _get_context, -) -import random as _random -import math as _py_math -import sys as _sys - -__all__ = [ - "BigInteger", "BigDecimal", "MathContext", "RoundingMode", -] - - -# ============================================================ -# RoundingMode -# ============================================================ - -class RoundingMode(Stub): - UP = 0 - DOWN = 1 - CEILING = 2 - FLOOR = 3 - HALF_UP = 4 - HALF_DOWN = 5 - HALF_EVEN = 6 - UNNECESSARY = 7 - - _names = { - 0: "UP", 1: "DOWN", 2: "CEILING", 3: "FLOOR", - 4: "HALF_UP", 5: "HALF_DOWN", 6: "HALF_EVEN", 7: "UNNECESSARY", - } - - _py_map = { - 0: _DEC_ROUND_UP, - 1: _DEC_ROUND_DOWN, - 2: _DEC_ROUND_CEILING, - 3: _DEC_ROUND_FLOOR, - 4: _DEC_ROUND_HALF_UP, - 5: _DEC_ROUND_HALF_DOWN, - 6: _DEC_ROUND_HALF_EVEN, - 7: _DEC_ROUND_HALF_EVEN, # closest approximation - } - - def __init__(self, value): - super().__init__() - self._value = value - - def toString(self): - return self._names.get(self._value, "UNKNOWN") - - -# ============================================================ -# MathContext -# ============================================================ - -class MathContext(Stub): - UNLIMITED = None - DECIMAL32 = None - DECIMAL64 = None - DECIMAL128 = None - - def __init__(self, *args): - super().__init__() - if len(args) == 1: - if isinstance(args[0], str): - s = args[0].strip().lower() - if s == "precision=7 roundingmode=half_even": - self._precision = 7 - self._rounding_mode = RoundingMode.HALF_EVEN - elif s == "precision=16 roundingmode=half_even": - self._precision = 16 - self._rounding_mode = RoundingMode.HALF_EVEN - elif s == "precision=34 roundingmode=half_even": - self._precision = 34 - self._rounding_mode = RoundingMode.HALF_EVEN - else: - self._precision = 0 - self._rounding_mode = RoundingMode.HALF_UP - else: - self._precision = int(args[0]) - self._rounding_mode = RoundingMode.HALF_UP - elif len(args) == 2: - self._precision = int(args[0]) - self._rounding_mode = args[1] - - def getPrecision(self): - return self._precision - - def getRoundingMode(self): - return self._rounding_mode - - def equals(self, other): - if isinstance(other, MathContext): - return (self._precision == other._precision and - self._rounding_mode == other._rounding_mode) - return False - - def hashCode(self): - return hash((self._precision, self._rounding_mode)) & 0x7FFFFFFF - - def toString(self): - return f"precision={self._precision} roundingMode={self._rounding_mode.toString()}" - - -MathContext.UNLIMITED = MathContext(0) -MathContext.DECIMAL32 = MathContext(7, RoundingMode.HALF_EVEN) -MathContext.DECIMAL64 = MathContext(16, RoundingMode.HALF_EVEN) -MathContext.DECIMAL128 = MathContext(34, RoundingMode.HALF_EVEN) - - -# ============================================================ -# BigInteger -# ============================================================ - -class BigInteger(Comparable): - ZERO = None - ONE = None - TEN = None - - def __init__(self, *args): - super().__init__() - if len(args) == 0: - self._value = 0 - elif len(args) == 1: - if isinstance(args[0], str): - self._value = int(str(args[0])) - elif isinstance(args[0], bytes): - self._value = int.from_bytes(args[0], byteorder="big", signed=True) - elif isinstance(args[0], bytearray): - self._value = int.from_bytes(bytes(args[0]), byteorder="big", signed=True) - elif isinstance(args[0], int): - self._value = args[0] - elif isinstance(args[0], BigInteger): - self._value = args[0]._value - else: - self._value = int(args[0]) - elif len(args) == 2: - if isinstance(args[1], _random.Random): - bit_length, rng = args - self._value = rng.getrandbits(bit_length) - elif isinstance(args[1], int): - val_str, radix = args - self._value = int(str(val_str), radix) - else: - self._value = int.from_bytes(bytes(args[0]), byteorder="big", signed=True) - else: - self._value = 0 - - @staticmethod - def valueOf(val): - if isinstance(val, int): - if val == 0: - return BigInteger.ZERO - if val == 1: - return BigInteger.ONE - if val == 10: - return BigInteger.TEN - return BigInteger(val) - - def add(self, other): - if isinstance(other, BigInteger): - return BigInteger(self._value + other._value) - return BigInteger(self._value + int(other)) - - def subtract(self, other): - if isinstance(other, BigInteger): - return BigInteger(self._value - other._value) - return BigInteger(self._value - int(other)) - - def multiply(self, other): - if isinstance(other, BigInteger): - return BigInteger(self._value * other._value) - return BigInteger(self._value * int(other)) - - def divide(self, other): - if isinstance(other, BigInteger): - return BigInteger(self._value // other._value) - return BigInteger(self._value // int(other)) - - def pow(self, exponent): - return BigInteger(self._value ** exponent) - - def gcd(self, other): - return BigInteger(_py_math.gcd(self._value, other._value if isinstance(other, BigInteger) else int(other))) - - def abs(self): - return BigInteger(abs(self._value)) - - def negate(self): - return BigInteger(-self._value) - - def signum(self): - if self._value > 0: - return 1 - if self._value < 0: - return -1 - return 0 - - def mod(self, m): - if isinstance(m, BigInteger): - return BigInteger(self._value % m._value) - return BigInteger(self._value % int(m)) - - def remainder(self, other): - if isinstance(other, BigInteger): - return BigInteger(self._value % other._value) - return BigInteger(self._value % int(other)) - - def and_(self, other): - return BigInteger(self._value & (other._value if isinstance(other, BigInteger) else int(other))) - - def or_(self, other): - return BigInteger(self._value | (other._value if isinstance(other, BigInteger) else int(other))) - - def xor(self, other): - return BigInteger(self._value ^ (other._value if isinstance(other, BigInteger) else int(other))) - - def not_(self): - return BigInteger(~self._value) - - def andNot(self, other): - return BigInteger(self._value & ~(other._value if isinstance(other, BigInteger) else int(other))) - - def shiftLeft(self, n): - return BigInteger(self._value << n) - - def shiftRight(self, n): - return BigInteger(self._value >> n) - - def getLowestSetBit(self): - if self._value == 0: - return -1 - return (self._value & -self._value).bit_length() - 1 - - def bitLength(self): - if self._value == 0: - return 0 - return self._value.bit_length() - - def bitCount(self): - return bin(self._value & ((1 << self.bitLength()) - 1) if self._value >= 0 else self._value).count("1") - - def isProbablePrime(self, certainty): - if abs(self._value) < 2: - return False - if abs(self._value) == 2 or abs(self._value) == 3: - return True - if self._value % 2 == 0: - return False - d = self._value - 1 - s = 0 - while d % 2 == 0: - d //= 2 - s += 1 - for _ in range(min(certainty, 20)): - a = _random.randrange(2, min(self._value - 1, 1000000)) - x = pow(a, d, self._value) - if x == 1 or x == self._value - 1: - continue - for _ in range(s - 1): - x = pow(x, 2, self._value) - if x == self._value - 1: - break - else: - return False - return True - - def nextProbablePrime(self): - v = self._value + 1 - if v < 3: - return BigInteger(2) if v <= 2 else BigInteger(3) - while True: - if v % 2 == 0: - v += 1 - for i in range(3, int(_py_math.sqrt(v)) + 1, 2): - if v % i == 0: - break - else: - return BigInteger(v) - v += 2 - - def compareTo(self, other): - if isinstance(other, BigInteger): - return (self._value > other._value) - (self._value < other._value) - return -1 - - def equals(self, other): - if isinstance(other, BigInteger): - return self._value == other._value - return False - - def toByteArray(self): - if self._value == 0: - return b"\x00" - byte_length = (self._value.bit_length() + 8) // 8 - return self._value.to_bytes(byte_length, byteorder="big", signed=True) - - def toString(self, radix=10): - if radix == 10: - return str(self._value) - if radix == 16: - return hex(self._value)[2:] - if radix == 8: - return oct(self._value)[2:] - if radix == 2: - return bin(self._value)[2:] - return str(self._value) - - def intValue(self): - return self._value & 0xFFFFFFFF - - def longValue(self): - return self._value - - def floatValue(self): - return float(self._value) - - def doubleValue(self): - return float(self._value) - - def min(self, other): - return BigInteger(min(self._value, other._value if isinstance(other, BigInteger) else int(other))) - - def max(self, other): - return BigInteger(max(self._value, other._value if isinstance(other, BigInteger) else int(other))) - - def modPow(self, exponent, m): - if isinstance(exponent, BigInteger): - exp_val = exponent._value - else: - exp_val = int(exponent) - mod_val = m._value if isinstance(m, BigInteger) else int(m) - return BigInteger(pow(self._value, exp_val, mod_val)) - - def modInverse(self, m): - mod_val = m._value if isinstance(m, BigInteger) else int(m) - return BigInteger(pow(self._value, -1, mod_val)) - - def divideAndRemainder(self, other): - if isinstance(other, BigInteger): - divisor = other._value - else: - divisor = int(other) - q = self._value // divisor - r = self._value % divisor - return [BigInteger(q), BigInteger(r)] - - def __int__(self): - return self._value - - def __str__(self): - return str(self._value) - - def __neg__(self): - return self.negate() - - def __abs__(self): - return self.abs() - - def __add__(self, other): - return self.add(other) - - def __sub__(self, other): - return self.subtract(other) - - def __mul__(self, other): - return self.multiply(other) - - def __truediv__(self, other): - return self.divide(other) - - def __floordiv__(self, other): - return self.divide(other) - - def __mod__(self, other): - return self.mod(other) - - def __pow__(self, other): - return self.pow(other) - - def __and__(self, other): - return self.and_(other) - - def __or__(self, other): - return self.or_(other) - - def __xor__(self, other): - return self.xor(other) - - def __invert__(self): - return self.not_() - - def __lshift__(self, n): - return self.shiftLeft(n) - - def __rshift__(self, n): - return self.shiftRight(n) - - -BigInteger.ZERO = BigInteger(0) -BigInteger.ONE = BigInteger(1) -BigInteger.TEN = BigInteger(10) - - -# ============================================================ -# BigDecimal -# ============================================================ - -class BigDecimal(Comparable): - ZERO = None - ONE = None - TEN = None - - ROUND_UP = 0 - ROUND_DOWN = 1 - ROUND_CEILING = 2 - ROUND_FLOOR = 3 - ROUND_HALF_UP = 4 - ROUND_HALF_DOWN = 5 - ROUND_HALF_EVEN = 6 - ROUND_UNNECESSARY = 7 - - _round_map = { - 0: _DEC_ROUND_UP, - 1: _DEC_ROUND_DOWN, - 2: _DEC_ROUND_CEILING, - 3: _DEC_ROUND_FLOOR, - 4: _DEC_ROUND_HALF_UP, - 5: _DEC_ROUND_HALF_DOWN, - 6: _DEC_ROUND_HALF_EVEN, - 7: _DEC_ROUND_HALF_EVEN, - } - - def __init__(self, *args): - super().__init__() - if len(args) == 1: - val = args[0] - if isinstance(val, str): - self._dec = _Decimal(str(val)) - elif isinstance(val, float): - self._dec = _Decimal(repr(val)) - elif isinstance(val, int): - self._dec = _Decimal(val) - elif isinstance(val, BigInteger): - self._dec = _Decimal(val._value) - elif isinstance(val, BigDecimal): - self._dec = _Decimal(val._dec) - elif isinstance(val, _Decimal): - self._dec = val - else: - self._dec = _Decimal(str(val)) - elif len(args) == 2: - val, mc = args - if isinstance(val, str): - self._dec = _Decimal(str(val)) - elif isinstance(val, float): - self._dec = _Decimal(repr(val)) - else: - self._dec = _Decimal(val) - if mc and hasattr(mc, 'getPrecision') and mc.getPrecision() > 0: - self._dec = +self._dec - - @staticmethod - def valueOf(val): - if isinstance(val, int): - if val == 0: - return BigDecimal.ZERO - if val == 1: - return BigDecimal.ONE - if val == 10: - return BigDecimal.TEN - if isinstance(val, float): - return BigDecimal(repr(val)) - return BigDecimal(val) - - def add(self, other): - if isinstance(other, BigDecimal): - return BigDecimal(self._dec + other._dec) - return BigDecimal(self._dec + _Decimal(str(other))) - - def subtract(self, other): - if isinstance(other, BigDecimal): - return BigDecimal(self._dec - other._dec) - return BigDecimal(self._dec - _Decimal(str(other))) - - def multiply(self, other): - if isinstance(other, BigDecimal): - return BigDecimal(self._dec * other._dec) - return BigDecimal(self._dec * _Decimal(str(other))) - - def divide(self, *args): - if len(args) == 1: - other = args[0] - if isinstance(other, BigDecimal): - return BigDecimal(self._dec / other._dec) - return BigDecimal(self._dec / _Decimal(str(other))) - elif len(args) == 2: - other, rounding_mode = args - if isinstance(other, BigDecimal): - result = self._dec / other._dec - else: - result = self._dec / _Decimal(str(other)) - return BigDecimal(result) - elif len(args) == 3: - other, scale, rounding_mode = args - if isinstance(other, BigDecimal): - result = self._dec / other._dec - else: - result = self._dec / _Decimal(str(other)) - rounded = BigDecimal(result) - return rounded.setScale(scale, rounding_mode) - - def divideToIntegralValue(self, other): - if isinstance(other, BigDecimal): - return BigDecimal(int(self._dec / other._dec)) - return BigDecimal(int(self._dec / _Decimal(str(other)))) - - def remainder(self, other): - if isinstance(other, BigDecimal): - return BigDecimal(self._dec % other._dec) - return BigDecimal(self._dec % _Decimal(str(other))) - - def pow(self, n): - return BigDecimal(self._dec ** n) - - def abs(self): - return BigDecimal(abs(self._dec)) - - def negate(self): - return BigDecimal(-self._dec) - - def signum(self): - if self._dec > 0: - return 1 - if self._dec < 0: - return -1 - return 0 - - def scale(self): - return abs(self._dec.as_tuple().exponent) - - def precision(self): - return len(self._dec.as_tuple().digits) - - def round(self, mc): - if mc.getPrecision() > 0: - with _local_context() as ctx: - ctx.prec = mc.getPrecision() - return BigDecimal(+self._dec) - return BigDecimal(self._dec) - - def setScale(self, new_scale, rounding_mode=None): - if rounding_mode is None: - qe = _Decimal(10) ** (-new_scale) - return BigDecimal(self._dec.quantize(qe)) - py_mode = self._round_map.get(rounding_mode, _DEC_ROUND_HALF_UP) - qe = _Decimal(10) ** (-new_scale) - return BigDecimal(self._dec.quantize(qe, rounding=py_mode)) - - def ulp(self): - exponent = self._dec.as_tuple().exponent - return BigDecimal(_Decimal(10) ** exponent) - - def unscaledValue(self): - t = self._dec.as_tuple() - digits = t.digits - val = int("".join(str(d) for d in digits)) - return BigInteger(-val if t.sign else val) - - def byteValueExact(self): - val = self.intValueExact() - if val < -128 or val > 127: - raise ArithmeticError("Overflow") - return val - - def shortValueExact(self): - val = self.intValueExact() - if val < -32768 or val > 32767: - raise ArithmeticError("Overflow") - return val - - def intValueExact(self): - if self._dec != int(self._dec): - raise ArithmeticError("Rounding necessary") - return int(self._dec) - - def longValueExact(self): - if self._dec != int(self._dec): - raise ArithmeticError("Rounding necessary") - return int(self._dec) - - def floatValue(self): - return float(self._dec) - - def doubleValue(self): - return float(self._dec) - - def intValue(self): - return int(self._dec) - - def longValue(self): - return int(self._dec) - - def toString(self): - return str(self._dec) - - def toEngineeringString(self): - return format(self._dec, "e") - - def toPlainString(self): - return format(self._dec, "f") - - def compareTo(self, other): - if isinstance(other, BigDecimal): - return (self._dec > other._dec) - (self._dec < other._dec) - return -1 - - def equals(self, other): - if isinstance(other, BigDecimal): - return self._dec == other._dec - return False - - def hashCode(self): - return hash(self._dec) & 0x7FFFFFFF - - def min(self, other): - return BigDecimal(min(self._dec, other._dec if isinstance(other, BigDecimal) else _Decimal(str(other)))) - - def max(self, other): - return BigDecimal(max(self._dec, other._dec if isinstance(other, BigDecimal) else _Decimal(str(other)))) - - def __str__(self): - return str(self._dec) - - def __int__(self): - return int(self._dec) - - def __float__(self): - return float(self._dec) - - def __neg__(self): - return self.negate() - - def __abs__(self): - return self.abs() - - def __add__(self, other): - return self.add(other) - - def __sub__(self, other): - return self.subtract(other) - - def __mul__(self, other): - return self.multiply(other) - - def __truediv__(self, other): - return self.divide(other) - - def __mod__(self, other): - return self.remainder(other) - - def __pow__(self, other): - return self.pow(other) - - -BigDecimal.ZERO = BigDecimal(0) -BigDecimal.ONE = BigDecimal(1) -BigDecimal.TEN = BigDecimal(10) \ No newline at end of file diff --git a/android_pyapi/java/net.py b/android_pyapi/java/net.py deleted file mode 100644 index 1010666..0000000 --- a/android_pyapi/java/net.py +++ /dev/null @@ -1,1759 +0,0 @@ -""" -Java net standard library APIs implemented in Python. -java.net package - URL, Socket, HTTP connection, and networking classes. -""" - -from .._core import Stub, AndroidOnlyError, HAS_REQUESTS, HAS_HTTPX -from .io import IOException - -import os as _os -import socket as _socket -import struct as _struct -import threading as _threading -from urllib.parse import ( - urlparse as _urlparse, - urlunparse as _urlunparse, - quote as _quote, - unquote as _unquote, -) -from datetime import datetime as _datetime -from collections import OrderedDict as _OrderedDict - -__all__ = [ - "URL", "URLConnection", "HttpURLConnection", - "InetAddress", "InetSocketAddress", - "Socket", "ServerSocket", "DatagramSocket", "DatagramPacket", - "Proxy", "ProxySelector", "Authenticator", "PasswordAuthentication", - "NetworkInterface", "InterfaceAddress", "URI", - "CookieHandler", "CookieManager", "HttpCookie", - "URLDecoder", "URLEncoder", - "SocketException", "ConnectException", "BindException", - "NoRouteToHostException", "PortUnreachableException", - "ProtocolException", "SocketTimeoutException", - "MalformedURLException", "UnknownHostException", "UnknownServiceException", -] - - -# ============================================================ -# Exception classes -# ============================================================ - -class SocketException(IOException): - pass - -class ConnectException(SocketException): - pass - -class BindException(SocketException): - pass - -class NoRouteToHostException(SocketException): - pass - -class PortUnreachableException(SocketException): - pass - -class ProtocolException(IOException): - pass - -class SocketTimeoutException(IOException): - pass - -class MalformedURLException(IOException): - pass - -class UnknownHostException(IOException): - pass - -class UnknownServiceException(IOException): - pass - - -# ============================================================ -# InetAddress -# ============================================================ - -class InetAddress(Stub): - def __init__(self, host, addr): - super().__init__() - self._host = host - self._addr = addr # bytes, 4 or 16 - - @staticmethod - def getByName(host): - try: - addr = _socket.getaddrinfo(host, None, family=_socket.AF_UNSPEC) - if addr: - raw = addr[0][4][0] - return InetAddress(host, _socket.inet_pton(_socket.AF_INET, raw) if "." in raw else _socket.inet_pton(_socket.AF_INET6, raw)) - except Exception: - raise UnknownHostException(host) - - @staticmethod - def getAllByName(host): - try: - addrs = _socket.getaddrinfo(host, None, family=_socket.AF_UNSPEC) - results = [] - seen = set() - for a in addrs: - raw = a[4][0] - if raw not in seen: - seen.add(raw) - if "." in raw: - results.append(InetAddress(host, _socket.inet_pton(_socket.AF_INET, raw))) - else: - results.append(InetAddress(host, _socket.inet_pton(_socket.AF_INET6, raw))) - return results - except Exception: - raise UnknownHostException(host) - - @staticmethod - def getLocalHost(): - hostname = _socket.gethostname() - return InetAddress.getByName(hostname) - - @staticmethod - def getLoopbackAddress(): - return InetAddress("localhost", b"\x7f\x00\x00\x01") - - @staticmethod - def getByAddress(host, addr): - return InetAddress(host, addr) - - def getHostName(self): - return self._host - - def getHostAddress(self): - if len(self._addr) == 4: - return _socket.inet_ntoa(self._addr) - return _socket.inet_ntop(_socket.AF_INET6, self._addr) - - def getCanonicalHostName(self): - try: - name, _, _ = _socket.gethostbyaddr(self.getHostAddress()) - return name - except Exception: - return self._host - - def isReachable(self, timeout=5000): - try: - s = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM) - s.settimeout(timeout / 1000.0) - result = s.connect_ex((self.getHostAddress(), 7)) - s.close() - return result == 0 - except Exception: - return False - - def isLoopbackAddress(self): - if len(self._addr) == 4: - return self._addr[0] == 127 - return self._addr == b"\x00" * 15 + b"\x01" - - def isLinkLocalAddress(self): - if len(self._addr) == 4: - return self._addr[0] == 169 and self._addr[1] == 254 - return (self._addr[0] & 0xFF) == 0xFE and (self._addr[1] & 0xC0) == 0x80 - - def isSiteLocalAddress(self): - if len(self._addr) == 4: - b0 = self._addr[0] - return b0 == 10 or (b0 == 172 and 16 <= self._addr[1] <= 31) or (b0 == 192 and self._addr[1] == 168) - return False - - def isMCGlobal(self): - if len(self._addr) == 4: - return 224 <= self._addr[0] <= 239 and self._addr[0] != 224 and self._addr[0] != 239 - return (self._addr[0] & 0xFF) == 0xFF and (self._addr[1] & 0x0F) == 0x0E and (self._addr[2] & 0x20) == 0 - - def isMCLinkLocal(self): - if len(self._addr) == 4: - return self._addr[0] == 224 and self._addr[1] == 0 and self._addr[2] == 0 - return (self._addr[0] & 0xFF) == 0xFF and (self._addr[1] & 0x0F) == 0x02 - - def isMCNodeLocal(self): - if len(self._addr) == 4: - return self._addr[0] == 224 and self._addr[1] == 0 and self._addr[2] == 0 and self._addr[3] == 1 - return (self._addr[0] & 0xFF) == 0xFF and (self._addr[1] & 0x0F) == 0x01 - - def isMCOrgLocal(self): - if len(self._addr) == 4: - return self._addr[0] == 239 and 192 <= self._addr[1] <= 195 - return (self._addr[0] & 0xFF) == 0xFF and (self._addr[1] & 0x0F) == 0x08 - - def isMCSiteLocal(self): - if len(self._addr) == 4: - return self._addr[0] == 239 and 255 <= self._addr[1] <= 255 - return (self._addr[0] & 0xFF) == 0xFF and (self._addr[1] & 0x0F) == 0x05 - - def getAddress(self): - return self._addr - - def toString(self): - return f"{self._host}/{self.getHostAddress()}" - - def equals(self, other): - if isinstance(other, InetAddress): - return self._addr == other._addr - return False - - def hashCode(self): - return hash(bytes(self._addr)) & 0x7FFFFFFF - - -# ============================================================ -# InetSocketAddress -# ============================================================ - -class InetSocketAddress(Stub): - def __init__(self, *args): - super().__init__() - if len(args) == 2 and isinstance(args[0], int): - self._addr = None - self._port = args[0] if isinstance(args[0], int) else args[1] - elif len(args) == 2: - hostname, port = args - self._addr = InetAddress.getByName(hostname) if isinstance(hostname, str) else hostname - self._port = port - elif len(args) == 1 and isinstance(args[0], int): - self._addr = None - self._port = args[0] - - def getAddress(self): - return self._addr - - def getPort(self): - return self._port - - def getHostName(self): - return self._addr.getHostName() if self._addr else None - - def getHostString(self): - return self.getHostName() - - def isUnresolved(self): - return self._addr is None - - def toString(self): - return f"{self.getHostName()}:{self._port}" - - def equals(self, other): - if isinstance(other, InetSocketAddress): - return self._port == other._port and self._addr == other._addr - return False - - def hashCode(self): - return hash((self._port, self._addr)) & 0x7FFFFFFF - - -# ============================================================ -# URI -# ============================================================ - -class URI(Stub): - def __init__(self, *args): - super().__init__() - if len(args) == 1: - self._uri = args[0] - parsed = _urlparse(self._uri) - self._scheme = parsed.scheme - self._host = parsed.hostname - self._port = parsed.port - self._path = parsed.path - self._query = parsed.query - self._fragment = parsed.fragment - self._user_info = parsed.username - self._authority = parsed.netloc - elif len(args) == 5: - self._scheme = args[0] - self._authority = args[1] or "" - self._path = args[2] or "" - self._query = args[3] or "" - self._fragment = args[4] or "" - parsed = _urlparse(f"{self._scheme}://{self._authority}{self._path}") - self._host = parsed.hostname - self._port = parsed.port - self._user_info = parsed.username - self._uri = f"{self._scheme}://{self._authority}{self._path}" - if self._query: - self._uri += "?" + self._query - if self._fragment: - self._uri += "#" + self._fragment - else: - raise ValueError("URI constructor requires 1 or 5 arguments") - - def getScheme(self): - return self._scheme - - def getHost(self): - return self._host - - def getPort(self): - return self._port if self._port else -1 - - def getPath(self): - return self._path - - def getQuery(self): - return self._query - - def getFragment(self): - return self._fragment - - def getAuthority(self): - return self._authority - - def getRawAuthority(self): - return self._authority - - def getRawFragment(self): - return self._fragment - - def getRawPath(self): - return self._path - - def getRawQuery(self): - return self._query - - def getRawSchemeSpecificPart(self): - return f"//{self._authority}{self._path}" - - def getRawUserInfo(self): - return self._user_info or "" - - def getSchemeSpecificPart(self): - return self.getRawSchemeSpecificPart() - - def getUserInfo(self): - return self._user_info or "" - - def isAbsolute(self): - return bool(self._scheme) - - def isOpaque(self): - return not self._path and not self._authority and self._scheme - - def normalize(self): - import posixpath - normalized = posixpath.normpath(self._path) - return URI(f"{self._scheme}://{self._authority}{normalized}") - - def resolve(self, uri_or_str): - if isinstance(uri_or_str, URI): - other = uri_or_str - else: - other = URI(uri_or_str) - if other.isAbsolute(): - return other - base_path = self._path or "/" - if base_path.endswith("/"): - new_path = base_path + other._path - else: - new_path = "/".join(base_path.rsplit("/", 1)[:-1] + [other._path]) if other._path else base_path - return URI(f"{self._scheme}://{self._authority}{new_path}") - - def relativize(self, other): - if self._path and other._path and other._path.startswith(self._path): - rel = other._path[len(self._path):] - if rel.startswith("/"): - rel = rel[1:] - return URI(rel) - return other - - def toASCIIString(self): - return _quote(self._uri, safe=":/?#[]@!$&'()*+,;=-._~") - - def toString(self): - return self._uri - - def toURL(self): - return URL(self._uri) - - def compareTo(self, other): - return -1 if self._uri < other._uri else (1 if self._uri > other._uri else 0) - - def equals(self, other): - if isinstance(other, URI): - return self._uri == other._uri - return False - - def hashCode(self): - return hash(self._uri) & 0x7FFFFFFF - - def parseServerAuthority(self): - return self - - -# ============================================================ -# URL -# ============================================================ - -class URL(Stub): - def __init__(self, *args): - super().__init__() - if len(args) == 1: - spec = args[0] - parsed = _urlparse(str(spec)) - self._protocol = parsed.scheme - self._host = parsed.hostname or "" - self._port = parsed.port - self._path = parsed.path or "" - self._file = parsed.path or "" - if parsed.query: - self._file += "?" + parsed.query - self._query = parsed.query or "" - self._ref = parsed.fragment or "" - self._authority = parsed.netloc or "" - self._user_info = parsed.username or "" - self._url_str = str(spec) - elif len(args) == 4: - protocol, host, port, file = args - self._protocol = protocol or "" - self._host = host or "" - self._port = port - self._path = file or "" - self._file = file or "" - self._query = "" - self._ref = "" - self._authority = f"{host}:{port}" if port and port > 0 else host - self._user_info = "" - parts = [file or ""] - self._url_str = f"{protocol}://{self._authority}{file}" - - def getProtocol(self): - return self._protocol - - def getHost(self): - return self._host - - def getPort(self): - return self._port if self._port else -1 - - def getDefaultPort(self): - defaults = { - "http": 80, "https": 443, "ftp": 21, "smtp": 25, - "ssh": 22, "telnet": 23, "ldap": 389, - } - return defaults.get(self._protocol, -1) - - def getPath(self): - return self._path - - def getFile(self): - return self._file - - def getQuery(self): - return self._query - - def getRef(self): - return self._ref - - def getAuthority(self): - return self._authority - - def getUserInfo(self): - return self._user_info - - def getContent(self, classes=None): - return self.openConnection().getContent(classes) - - def openConnection(self, proxy=None): - return HttpURLConnection(self._url_str, proxy) - - def openStream(self): - return self.openConnection().getInputStream() - - def sameFile(self, other): - return (self._protocol == other._protocol and self._host == other._host - and self._port == other._port and self._path == other._path) - - def toExternalForm(self): - return self._url_str - - def toString(self): - return self._url_str - - def toURI(self): - return URI(self._url_str) - - def equals(self, other): - if isinstance(other, URL): - return self._url_str == other._url_str - return False - - def hashCode(self): - return hash(self._url_str) & 0x7FFFFFFF - - def openConnection(self, proxy=None): - return HttpURLConnection(self._url_str, proxy) - - def __str__(self): - return self._url_str - - -# ============================================================ -# URLConnection -# ============================================================ - -class URLConnection(Stub): - def __init__(self, url_str): - super().__init__() - self._url_str = url_str - self._connected = False - self._do_input = True - self._do_output = False - self._allow_user_interaction = False - self._use_caches = True - self._default_use_caches = True - self._if_modified_since = 0 - self._read_timeout = 0 - self._connect_timeout = 0 - self._request_properties = _OrderedDict() - self._response_code = None - self._response_message = None - self._response_headers = _OrderedDict() - self._content = None - - def connect(self): - if not self._connected: - self._connected = True - - def getContent(self, classes=None): - raise UnknownServiceException("getContent not implemented") - - def getContentEncoding(self): - return self._response_headers.get("Content-Encoding") - - def getContentLength(self): - val = self._response_headers.get("Content-Length") - return int(val) if val else -1 - - def getContentLengthLong(self): - return self.getContentLength() - - def getContentType(self): - return self._response_headers.get("Content-Type") - - def getDate(self): - val = self._response_headers.get("Date") - if val: - try: - return int(_datetime.strptime(val, "%a, %d %b %Y %H:%M:%S %Z").timestamp() * 1000) - except Exception: - pass - return 0 - - def getExpiration(self): - val = self._response_headers.get("Expires") - if val: - try: - return int(_datetime.strptime(val, "%a, %d %b %Y %H:%M:%S %Z").timestamp() * 1000) - except Exception: - pass - return 0 - - def getHeaderField(self, name): - return self._response_headers.get(name) - - def getHeaderFieldDate(self, name, default): - val = self._response_headers.get(name) - if val: - try: - return int(_datetime.strptime(val, "%a, %d %b %Y %H:%M:%S %Z").timestamp() * 1000) - except Exception: - pass - return default - - def getHeaderFieldInt(self, name, default): - val = self._response_headers.get(name) - return int(val) if val and val.isdigit() else default - - def getHeaderFieldKey(self, n): - keys = list(self._response_headers.keys()) - return keys[n] if n < len(keys) else None - - def getHeaderFields(self): - return self._response_headers - - def getInputStream(self): - raise UnknownServiceException("getInputStream not implemented") - - def getOutputStream(self): - raise UnknownServiceException("getOutputStream not implemented") - - def getPermission(self): - return None - - def getReadTimeout(self): - return self._read_timeout - - def setReadTimeout(self, timeout): - self._read_timeout = timeout - - def getConnectTimeout(self): - return self._connect_timeout - - def setConnectTimeout(self, timeout): - self._connect_timeout = timeout - - def getRequestProperty(self, key): - return self._request_properties.get(key) - - def setRequestProperty(self, key, value): - self._request_properties[key] = value - - def addRequestProperty(self, key, value): - if key in self._request_properties: - existing = self._request_properties[key] - if isinstance(existing, list): - existing.append(value) - else: - self._request_properties[key] = [existing, value] - else: - self._request_properties[key] = value - - def getDefaultUseCaches(self): - return self._default_use_caches - - def setDefaultUseCaches(self, use): - self._default_use_caches = use - - def getUseCaches(self): - return self._use_caches - - def setUseCaches(self, use): - self._use_caches = use - - def setDoInput(self, do_input): - self._do_input = do_input - - def getDoInput(self): - return self._do_input - - def setDoOutput(self, do_output): - self._do_output = do_output - - def getDoOutput(self): - return self._do_output - - def setAllowUserInteraction(self, allow): - self._allow_user_interaction = allow - - def getAllowUserInteraction(self): - return self._allow_user_interaction - - def setIfModifiedSince(self, time): - self._if_modified_since = time - - def getIfModifiedSince(self): - return self._if_modified_since - - -# ============================================================ -# HttpURLConnection -# ============================================================ - -class HttpURLConnection(URLConnection): - HTTP_OK = 200 - HTTP_CREATED = 201 - HTTP_ACCEPTED = 202 - HTTP_MOVED_PERM = 301 - HTTP_MOVED_TEMP = 302 - HTTP_NOT_MODIFIED = 304 - HTTP_BAD_REQUEST = 400 - HTTP_UNAUTHORIZED = 401 - HTTP_FORBIDDEN = 403 - HTTP_NOT_FOUND = 404 - HTTP_INTERNAL_ERROR = 500 - - def __init__(self, url_str, proxy=None): - super().__init__(url_str) - self._request_method = "GET" - self._instance_follow_redirects = True - self._chunked = False - self._fixed_length = -1 - self._input_stream = None - self._error_stream = None - self._response_code = -1 - self._response_message = "" - - def setRequestMethod(self, method): - method = method.upper() - valid = ["GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE", "PATCH"] - if method not in valid: - raise ProtocolException(f"Invalid HTTP method: {method}") - self._request_method = method - - def getRequestMethod(self): - return self._request_method - - def getResponseCode(self): - if self._response_code == -1: - self._do_request() - return self._response_code - - def getResponseMessage(self): - if self._response_code == -1: - self._do_request() - return self._response_message - - def getHeaderFields(self): - if self._response_code == -1: - self._do_request() - return self._response_headers - - def setInstanceFollowRedirects(self, follow): - self._instance_follow_redirects = follow - - def getInstanceFollowRedirects(self): - return self._instance_follow_redirects - - def getErrorStream(self): - if self._response_code == -1: - self._do_request() - if self._response_code >= 400: - return self._error_stream - return None - - def setChunkedStreamingMode(self, chunklen): - self._chunked = True - self._chunk_length = chunklen - - def setFixedLengthStreamingMode(self, content_length): - self._fixed_length = content_length - - def setFixedLengthStreamingModeLong(self, content_length): - self._fixed_length = content_length - - def disconnect(self): - self._input_stream = None - self._error_stream = None - self._connected = False - - def usingProxy(self): - return False - - def getInputStream(self): - if self._response_code == -1: - self._do_request() - if self._input_stream is None: - import io as _pyio_mod - self._input_stream = _pyio_mod.BytesIO(self._content.encode("utf-8") if isinstance(self._content, str) else self._content) - return self._input_stream - - def _do_request(self): - if HAS_REQUESTS: - self._do_requests() - elif HAS_HTTPX: - self._do_httpx() - else: - self._do_urllib() - - def _do_requests(self): - import requests - try: - kwargs = { - "headers": dict(self._request_properties), - "timeout": self._read_timeout / 1000.0 if self._read_timeout else None, - } - if self._instance_follow_redirects: - kwargs["allow_redirects"] = True - else: - kwargs["allow_redirects"] = False - method = self._request_method.lower() - if method == "get": - r = requests.get(self._url_str, **kwargs) - elif method == "post": - body = self._request_properties.pop("__body", None) - r = requests.post(self._url_str, data=body, **kwargs) - elif method == "put": - body = self._request_properties.pop("__body", None) - r = requests.put(self._url_str, data=body, **kwargs) - elif method == "delete": - r = requests.delete(self._url_str, **kwargs) - elif method == "head": - r = requests.head(self._url_str, **kwargs) - elif method == "options": - r = requests.options(self._url_str, **kwargs) - elif method == "patch": - body = self._request_properties.pop("__body", None) - r = requests.patch(self._url_str, data=body, **kwargs) - else: - r = requests.request(self._request_method, self._url_str, **kwargs) - self._response_code = r.status_code - self._response_message = r.reason - self._response_headers = dict(r.headers) - self._content = r.content - except Exception as e: - self._response_code = 0 - self._response_message = str(e) - - def _do_httpx(self): - import httpx - try: - client = httpx.Client(follow_redirects=self._instance_follow_redirects) - kwargs = { - "headers": dict(self._request_properties) if self._request_properties else None, - "timeout": self._read_timeout / 1000.0 if self._read_timeout else None, - } - method = self._request_method.lower() - if method == "get": - r = client.get(self._url_str, **kwargs) - elif method == "post": - body = self._request_properties.pop("__body", None) - r = client.post(self._url_str, content=body, **kwargs) - elif method == "put": - body = self._request_properties.pop("__body", None) - r = client.put(self._url_str, content=body, **kwargs) - elif method == "delete": - r = client.delete(self._url_str, **kwargs) - else: - r = client.request(self._request_method, self._url_str, **kwargs) - self._response_code = r.status_code - self._response_message = r.reason_phrase - self._response_headers = dict(r.headers) - self._content = r.content - except Exception as e: - self._response_code = 0 - self._response_message = str(e) - - def _do_urllib(self): - from urllib.request import urlopen, Request - from urllib.error import HTTPError - try: - req = Request(self._url_str, headers=dict(self._request_properties) if self._request_properties else {}, - method=self._request_method) - resp = urlopen(req, timeout=self._read_timeout / 1000.0 if self._read_timeout else None) - self._response_code = resp.status - self._response_message = resp.reason - self._response_headers = dict(resp.headers) - self._content = resp.read() - except HTTPError as e: - self._response_code = e.code - self._response_message = e.reason - self._response_headers = dict(e.headers) - self._content = e.read() - except Exception as e: - self._response_code = 0 - self._response_message = str(e) - self._content = b"" - - -# ============================================================ -# Socket -# ============================================================ - -class Socket(Stub): - def __init__(self, *args): - super().__init__() - self._socket = None - self._connected = False - self._bound = False - self._closed = False - self._input_shutdown = False - self._output_shutdown = False - self._input_stream = None - self._output_stream = None - self._timeout = 0 - self._keep_alive = False - self._tcp_no_delay = False - self._reuse_address = False - self._send_buffer_size = 0 - self._receive_buffer_size = 0 - self._linger = -1 - self._traffic_class = 0 - if len(args) == 2: - host, port = args - self.connect(InetSocketAddress(host, port)) - elif len(args) == 1 and isinstance(args[0], InetAddress): - addr, port = args[0], 0 - self.connect(InetSocketAddress(addr, port)) - - def connect(self, endpoint, timeout=None): - if isinstance(endpoint, InetSocketAddress): - host = endpoint.getHostName() - port = endpoint.getPort() - else: - host = str(endpoint) - port = 80 - self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM) - if self._timeout > 0: - self._socket.settimeout(self._timeout / 1000.0) - try: - self._socket.connect((host, port)) - self._connected = True - except OSError as e: - raise ConnectException(str(e)) - - def bind(self, endpoint): - if isinstance(endpoint, InetSocketAddress): - host = endpoint.getHostName() - port = endpoint.getPort() - else: - host = "0.0.0.0" - port = 0 - self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM) - self._socket.bind((host or "0.0.0.0", port)) - self._bound = True - - def close(self): - if not self._closed: - self._closed = True - if self._socket: - self._socket.close() - - def getInputStream(self): - if self._input_stream is None and self._socket: - import io as _pyio_mod - fd = self._socket.fileno() - self._input_stream = _pyio_mod.FileIO(fd, closefd=False) - return self._input_stream - - def getOutputStream(self): - if self._output_stream is None and self._socket: - import io as _pyio_mod - fd = self._socket.fileno() - self._output_stream = _pyio_mod.FileIO(fd, closefd=False, mode="ab") - return self._output_stream - - def getInetAddress(self): - if self._socket and self._connected: - host, _ = self._socket.getpeername() - return InetAddress.getByName(host) - return None - - def getPort(self): - if self._socket and self._connected: - _, port = self._socket.getpeername() - return port - return 0 - - def getLocalPort(self): - if self._socket: - _, port = self._socket.getsockname() - return port - return 0 - - def getLocalAddress(self): - if self._socket: - host, _ = self._socket.getsockname() - return InetAddress.getByName(host) - return InetAddress.getLoopbackAddress() - - def getRemoteSocketAddress(self): - if self._socket and self._connected: - host, port = self._socket.getpeername() - return InetSocketAddress(host, port) - return None - - def getLocalSocketAddress(self): - if self._socket: - host, port = self._socket.getsockname() - return InetSocketAddress(host, port) - return None - - def getSoTimeout(self): - return self._timeout - - def setSoTimeout(self, timeout): - self._timeout = timeout - if self._socket: - self._socket.settimeout(timeout / 1000.0 if timeout > 0 else None) - - def getTcpNoDelay(self): - return self._tcp_no_delay - - def setTcpNoDelay(self, on): - self._tcp_no_delay = on - if self._socket: - self._socket.setsockopt(_socket.IPPROTO_TCP, _socket.TCP_NODELAY, 1 if on else 0) - - def getKeepAlive(self): - return self._keep_alive - - def setKeepAlive(self, on): - self._keep_alive = on - if self._socket: - self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_KEEPALIVE, 1 if on else 0) - - def getReuseAddress(self): - return self._reuse_address - - def setReuseAddress(self, on): - self._reuse_address = on - if self._socket: - self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, 1 if on else 0) - - def getSendBufferSize(self): - return self._send_buffer_size - - def setSendBufferSize(self, size): - self._send_buffer_size = size - if self._socket: - self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_SNDBUF, size) - - def getReceiveBufferSize(self): - return self._receive_buffer_size - - def setReceiveBufferSize(self, size): - self._receive_buffer_size = size - if self._socket: - self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_RCVBUF, size) - - def getSoLinger(self): - return self._linger - - def setSoLinger(self, on, linger): - if on: - self._linger = linger - if self._socket: - self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_LINGER, _struct.pack("ii", 1, linger)) - else: - self._linger = -1 - - def getTrafficClass(self): - return self._traffic_class - - def setTrafficClass(self, tc): - self._traffic_class = tc - - def shutdownInput(self): - if self._socket: - self._socket.shutdown(_socket.SHUT_RD) - self._input_shutdown = True - - def shutdownOutput(self): - if self._socket: - self._socket.shutdown(_socket.SHUT_WR) - self._output_shutdown = True - - def isConnected(self): - return self._connected - - def isBound(self): - return self._bound - - def isClosed(self): - return self._closed - - def isInputShutdown(self): - return self._input_shutdown - - def isOutputShutdown(self): - return self._output_shutdown - - def setPerformancePreferences(self, connection_time, latency, bandwidth): - pass - - -# ============================================================ -# ServerSocket -# ============================================================ - -class ServerSocket(Stub): - def __init__(self, port=0, backlog=50, bindAddr=None): - super().__init__() - self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM) - self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, 1) - self._port = port - self._backlog = backlog - self._bind_addr = bindAddr - self._closed = False - self._timeout = 0 - if port > 0 or bindAddr: - self.bind(InetSocketAddress(bindAddr or "0.0.0.0", port), backlog) - - def bind(self, endpoint, backlog=50): - host = endpoint.getHostName() if isinstance(endpoint, InetSocketAddress) else str(endpoint) - port = endpoint.getPort() if isinstance(endpoint, InetSocketAddress) else 0 - self._socket.bind((host or "0.0.0.0", port)) - self._socket.listen(backlog) - self._port = self._socket.getsockname()[1] - - def accept(self): - if self._closed: - raise SocketException("Socket closed") - if self._timeout > 0: - self._socket.settimeout(self._timeout / 1000.0) - try: - client_sock, addr = self._socket.accept() - sock = Socket() - sock._socket = client_sock - sock._connected = True - return sock - except _socket.timeout: - raise SocketTimeoutException("Accept timed out") - - def close(self): - if not self._closed: - self._closed = True - self._socket.close() - - def getInetAddress(self): - host, _ = self._socket.getsockname() - return InetAddress.getByName(host) - - def getLocalPort(self): - return self._socket.getsockname()[1] if not self._closed else -1 - - def getSoTimeout(self): - return self._timeout - - def setSoTimeout(self, timeout): - self._timeout = timeout - self._socket.settimeout(timeout / 1000.0 if timeout > 0 else None) - - def getReceiveBufferSize(self): - return self._socket.getsockopt(_socket.SOL_SOCKET, _socket.SO_RCVBUF) - - def setReceiveBufferSize(self, size): - self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_RCVBUF, size) - - def getReuseAddress(self): - return bool(self._socket.getsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR)) - - def setReuseAddress(self, on): - self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, 1 if on else 0) - - -# ============================================================ -# DatagramSocket / DatagramPacket -# ============================================================ - -class DatagramPacket(Stub): - def __init__(self, buf, length, address=None, port=None): - super().__init__() - if address is not None: - self._buf = bytearray(buf) - self._length = length - self._address = address - self._port = port - else: - self._buf = bytearray(buf) - self._length = length - self._address = None - self._port = -1 - - def getAddress(self): - return self._address - - def getPort(self): - return self._port - - def getData(self): - return bytes(self._buf) - - def getLength(self): - return self._length - - def setAddress(self, address): - self._address = address - - def setPort(self, port): - self._port = port - - def setData(self, buf, offset=None, length=None): - if offset is not None and length is not None: - self._buf = bytearray(buf[offset:offset+length]) - self._length = length - else: - self._buf = bytearray(buf) - self._length = len(buf) - - def setLength(self, length): - self._length = length - - -class DatagramSocket(Stub): - def __init__(self, port=None): - super().__init__() - self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_DGRAM) - self._bound = False - self._closed = False - if port is not None: - self.bind(port) - - def bind(self, port): - self._socket.bind(("0.0.0.0", port)) - self._bound = True - - def send(self, p): - addr = p.getAddress() - port = p.getPort() - self._socket.sendto(p.getData()[:p.getLength()], (addr.getHostAddress(), port)) - - def receive(self, p): - data, addr = self._socket.recvfrom(p.getLength()) - p.setData(data) - p.setAddress(InetAddress(addr[0], _socket.inet_pton(_socket.AF_INET, addr[0]))) - p.setPort(addr[1]) - - def close(self): - if not self._closed: - self._closed = True - self._socket.close() - - def getLocalPort(self): - return self._socket.getsockname()[1] if not self._closed else -1 - - def isBound(self): - return self._bound - - def isClosed(self): - return self._closed - - def setSoTimeout(self, timeout): - self._socket.settimeout(timeout / 1000.0 if timeout > 0 else None) - - def getSoTimeout(self): - return self._socket.gettimeout() * 1000 if self._socket.gettimeout() else 0 - - -# ============================================================ -# Proxy -# ============================================================ - -class Proxy(Stub): - class Type: - DIRECT = "DIRECT" - HTTP = "HTTP" - SOCKS = "SOCKS" - - NO_PROXY = None - - def __init__(self, typ, sa=None): - super().__init__() - self._type = typ - self._address = sa - - def type(self): - return self._type - - def address(self): - return self._address - - def toString(self): - return f"{self._type} proxy @ {self._address}" - - -class ProxySelector(Stub): - _default = None - - @staticmethod - def getDefault(): - return ProxySelector._default - - @staticmethod - def setDefault(sel): - ProxySelector._default = sel - - def select(self, uri): - return [Proxy.NO_PROXY] - - def connectFailed(self, uri, sa, ioe): - pass - - -# ============================================================ -# Authenticator / PasswordAuthentication -# ============================================================ - -class Authenticator(Stub): - _default = None - - @staticmethod - def getDefault(): - return Authenticator._default - - @staticmethod - def setDefault(auth): - Authenticator._default = auth - - def getPasswordAuthentication(self): - return self._pwd_auth - - def requestPasswordAuthentication(self, addr, port, protocol, prompt, scheme): - return self._pwd_auth - - -class PasswordAuthentication(Stub): - def __init__(self, user, password): - super().__init__() - self._user = user - self._password = password - - def getUserName(self): - return self._user - - def getPassword(self): - return self._password - - -# ============================================================ -# InterfaceAddress -# ============================================================ - -class InterfaceAddress(Stub): - """java.net.InterfaceAddress — 接口地址信息 (地址/广播/前缀长度)""" - - def __init__(self, address=None, broadcast=None, prefix_length=0): - super().__init__() - self._address = address # InetAddress - self._broadcast = broadcast # InetAddress or None - self._prefix_length = prefix_length - - def getAddress(self): - return self._address - - def getBroadcast(self): - return self._broadcast - - def getNetworkPrefixLength(self): - return self._prefix_length - - def equals(self, other): - if isinstance(other, InterfaceAddress): - return (self._address == other._address - and self._prefix_length == other._prefix_length) - return False - - def hashCode(self): - return hash((repr(self._address), self._prefix_length)) & 0x7FFFFFFF - - def toString(self): - addr = self._address.getHostAddress() if self._address else "0.0.0.0" - return f"{addr}/{self._prefix_length}" - - -# ============================================================ -# NetworkInterface -# ============================================================ - -class NetworkInterface(Stub): - _cache = {} - _initialized = False - - def __init__(self, name, display_name, addrs): - super().__init__() - self._name = name - self._display_name = display_name - self._addrs = addrs - self._mac = None # bytes, hardware address - self._mtu = 1500 - self._state = "unknown" - self._flags = 0 - self._interface_addrs = [] # List[InterfaceAddress] - - @staticmethod - def _read_sys_file(path): - try: - with open(path, "r") as f: - return f.read().strip() - except Exception: - return None - - @staticmethod - def getByName(name): - if name is None: - return None - NetworkInterface._ensure_init() - return NetworkInterface._cache.get(name) - - @staticmethod - def getByInetAddress(addr): - NetworkInterface._ensure_init() - for ni in NetworkInterface._cache.values(): - for a in ni._addrs: - if a.equals(addr): - return ni - return None - - @staticmethod - def getNetworkInterfaces(): - NetworkInterface._ensure_init() - return list(NetworkInterface._cache.values()) - - @staticmethod - def _ensure_init(): - if not NetworkInterface._initialized: - NetworkInterface._initialized = True - try: - NetworkInterface._init_from_socket() - except Exception: - pass - - def getName(self): - return self._name - - def getDisplayName(self): - return self._display_name - - def getInetAddresses(self): - return iter(self._addrs) - - def getInterfaceAddresses(self): - return list(self._interface_addrs) - - def isUp(self): - return self._state.lower() == "up" or (self._flags & 0x1) != 0 - - def isLoopback(self): - return (self._flags & 0x8) != 0 or self._name == "lo" - - def isPointToPoint(self): - return (self._flags & 0x10) != 0 - - def supportsMulticast(self): - return (self._flags & 0x1000) != 0 - - def getHardwareAddress(self): - return self._mac - - def getMTU(self): - return self._mtu - - def getSubInterfaces(self): - return iter([]) - - def getParent(self): - return None - - def isVirtual(self): - return False - - def toString(self): - return f"{self._display_name} ({self._name})" - - @staticmethod - def _init_from_socket(): - """从 /sys/class/net/ 和 socket/fcntl 枚举真实网络接口 (不依赖 netifaces)""" - import os as _os - net_dir = "/sys/class/net" - if not _os.path.isdir(net_dir): - # 非 Linux 环境: 尝试用 socket 获取本机地址 - try: - hostname = _socket.gethostname() - addr = InetAddress.getByName(hostname) - ni = NetworkInterface(hostname, hostname, [addr]) - NetworkInterface._cache[hostname] = ni - except Exception: - pass - return - - # 尝试用 fcntl.ioctl 获取 IPv4 地址/掩码/广播 - fcntl_available = True - try: - import fcntl as _fcntl - except ImportError: - fcntl_available = False - - for iface_name in _os.listdir(net_dir): - addrs = [] - interface_addrs = [] - mac = None - mtu = 1500 - state = "unknown" - flags = 0 - - # --- MAC 地址 --- - mac_str = NetworkInterface._read_sys_file(f"{net_dir}/{iface_name}/address") - if mac_str: - try: - mac = bytes(int(b, 16) for b in mac_str.split(":")) - except Exception: - mac = None - - # --- MTU --- - mtu_str = NetworkInterface._read_sys_file(f"{net_dir}/{iface_name}/mtu") - if mtu_str: - try: - mtu = int(mtu_str) - except ValueError: - pass - - # --- operstate --- - state = NetworkInterface._read_sys_file( - f"{net_dir}/{iface_name}/operstate") or "unknown" - - # --- flags (十六进制字符串, 如 0x1003) --- - flags_str = NetworkInterface._read_sys_file( - f"{net_dir}/{iface_name}/flags") - if flags_str: - try: - flags = int(flags_str, 16) - except ValueError: - pass - - # --- IPv4 地址 (fcntl.ioctl) --- - if fcntl_available: - sock = None - try: - sock = _socket.socket(_socket.AF_INET, _socket.SOCK_DGRAM) - packed = _struct.pack('256s', iface_name.encode()[:15]) - # SIOCGIFADDR = 0x8915 - result = _fcntl.ioctl(sock.fileno(), 0x8915, packed) - ip_raw = result[20:24] - inet_addr = InetAddress(iface_name, bytes(ip_raw)) - addrs.append(inet_addr) - - # SIOCGIFNETMASK = 0x891b - prefix = 24 - try: - result = _fcntl.ioctl(sock.fileno(), 0x891b, packed) - netmask_str = _socket.inet_ntoa(result[20:24]) - prefix = sum(bin(int(b)).count('1') for b in netmask_str.split('.')) - except Exception: - pass - - # SIOCGIFBRDADDR = 0x8919 (广播地址) - broadcast_addr = None - try: - result = _fcntl.ioctl(sock.fileno(), 0x8919, packed) - bcast_raw = result[20:24] - broadcast_addr = InetAddress(iface_name, bytes(bcast_raw)) - except Exception: - pass - - interface_addrs.append(InterfaceAddress( - address=inet_addr, - broadcast=broadcast_addr, - prefix_length=prefix - )) - except OSError: - pass # 接口没有 IPv4 地址 - finally: - if sock is not None: - try: - sock.close() - except Exception: - pass - - # --- IPv6 地址 (/proc/net/if_inet6) --- - try: - with open("/proc/net/if_inet6", "r") as f: - for line in f: - parts = line.split() - if len(parts) >= 6 and parts[5] == iface_name: - hex_addr = parts[0] - raw = bytes.fromhex(hex_addr) - prefix6 = int(parts[2], 16) if parts[2] else 64 - inet6_addr = InetAddress(iface_name, raw) - addrs.append(inet6_addr) - interface_addrs.append(InterfaceAddress( - address=inet6_addr, - broadcast=None, - prefix_length=prefix6 - )) - except Exception: - pass - - ni = NetworkInterface(iface_name, iface_name, addrs) - ni._mac = mac - ni._mtu = mtu - ni._state = state - ni._flags = flags - ni._interface_addrs = interface_addrs - NetworkInterface._cache[iface_name] = ni - - -# 初始化缓存 -try: - NetworkInterface._ensure_init() -except Exception: - pass - - -# ============================================================ -# CookieHandler / CookieManager / HttpCookie -# ============================================================ - -class CookieHandler(Stub): - _default = None - - @staticmethod - def getDefault(): - return CookieHandler._default - - @staticmethod - def setDefault(handler): - CookieHandler._default = handler - - def get(self, uri, requestHeaders): - raise NotImplementedError - - def put(self, uri, responseHeaders): - raise NotImplementedError - - -class HttpCookie(Stub): - def __init__(self, name, value): - super().__init__() - self._name = name - self._value = value - self._comment = None - self._comment_url = None - self._discard = False - self._domain = None - self._max_age = -1 - self._path = None - self._portlist = None - self._secure = False - self._version = 0 - self._http_only = False - self._creation_time = _datetime.now() - - def getName(self): - return self._name - - def getValue(self): - return self._value - - def getComment(self): - return self._comment - - def getCommentURL(self): - return self._comment_url - - def getDiscard(self): - return self._discard - - def getDomain(self): - return self._domain - - def getMaxAge(self): - return self._max_age - - def getPath(self): - return self._path - - def getPortlist(self): - return self._portlist - - def getSecure(self): - return self._secure - - def getVersion(self): - return self._version - - def getHttpOnly(self): - return self._http_only - - def setComment(self, comment): - self._comment = comment - - def setCommentURL(self, url): - self._comment_url = url - - def setDiscard(self, discard): - self._discard = discard - - def setDomain(self, domain): - self._domain = domain - - def setMaxAge(self, max_age): - self._max_age = max_age - - def setPath(self, path): - self._path = path - - def setPortlist(self, portlist): - self._portlist = portlist - - def setSecure(self, secure): - self._secure = secure - - def setVersion(self, version): - self._version = version - - def setHttpOnly(self, http_only): - self._http_only = http_only - - def hasExpired(self): - if self._max_age < 0: - return False - elapsed = (_datetime.now() - self._creation_time).total_seconds() - return elapsed > self._max_age - - def equals(self, other): - if isinstance(other, HttpCookie): - return self._name == other._name - return False - - def toString(self): - return f"{self._name}={self._value}" - - def validate(self, name): - if not name or name.strip() == "": - raise ValueError("Cookie name cannot be empty") - - def __str__(self): - return self.toString() - - -class CookieManager(CookieHandler): - def __init__(self): - super().__init__() - self._cookie_store = {} - - def get(self, uri, requestHeaders): - cookies = [] - for domain, path, cookie in self._cookie_store.values(): - if not cookie.hasExpired(): - cookies.append(cookie.toString()) - return {"Cookie": "; ".join(cookies)} if cookies else {} - - def put(self, uri, responseHeaders): - set_cookie = responseHeaders.get("Set-Cookie") - if set_cookie: - parts = set_cookie.split(";") - name_val = parts[0].split("=", 1) - if len(name_val) == 2: - cookie = HttpCookie(name_val[0], name_val[1]) - for part in parts[1:]: - part = part.strip() - if "=" in part: - k, v = part.split("=", 1) - if k.lower() == "domain": - cookie.setDomain(v) - elif k.lower() == "path": - cookie.setPath(v) - elif k.lower() == "max-age": - cookie.setMaxAge(int(v)) - elif k.lower() == "version": - cookie.setVersion(int(v)) - else: - if part.lower() == "secure": - cookie.setSecure(True) - elif part.lower() == "httponly": - cookie.setHttpOnly(True) - elif part.lower() == "discard": - cookie.setDiscard(True) - self._cookie_store[(cookie.getDomain(), cookie.getPath(), cookie.getName())] = (cookie.getDomain(), cookie.getPath(), cookie) - - -# ============================================================ -# URLEncoder / URLDecoder -# ============================================================ - -class URLEncoder(Stub): - @staticmethod - def encode(s, charset="UTF-8"): - return _quote(str(s), safe="") - - @staticmethod - def encode(s): - return _quote(str(s), safe="") - - -class URLDecoder(Stub): - @staticmethod - def decode(s, charset="UTF-8"): - return _unquote(str(s), encoding=charset) - - @staticmethod - def decode(s): - return _unquote(str(s)) - - -# Initialize NO_PROXY -Proxy.NO_PROXY = Proxy(Proxy.Type.DIRECT, None) \ No newline at end of file diff --git a/android_pyapi/java/nio.py b/android_pyapi/java/nio.py deleted file mode 100644 index 9418716..0000000 --- a/android_pyapi/java/nio.py +++ /dev/null @@ -1,622 +0,0 @@ -""" -Java NIO standard library APIs implemented in Python. -java.nio package - Buffer, ByteBuffer, CharBuffer, etc. -""" - -from .._core import Stub -from .lang import RuntimeException -from .io import IOException -import struct as _struct -import sys as _sys - -IllegalStateException = type("IllegalStateException", (RuntimeException,), {}) -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "Buffer", "ByteBuffer", "CharBuffer", "ShortBuffer", - "IntBuffer", "LongBuffer", "FloatBuffer", "DoubleBuffer", - "MappedByteBuffer", "ByteOrder", - "BufferOverflowException", "BufferUnderflowException", - "ReadOnlyBufferException", "InvalidMarkException", -] - - -# ============================================================ -# Exception classes -# ============================================================ - -class BufferOverflowException(RuntimeException): - pass - -class BufferUnderflowException(RuntimeException): - pass - -class ReadOnlyBufferException(RuntimeException): - pass - -class InvalidMarkException(IllegalStateException): - pass - - -# ============================================================ -# ByteOrder -# ============================================================ - -class ByteOrder(Stub): - BIG_ENDIAN = None - LITTLE_ENDIAN = None - - def __init__(self, name): - super().__init__() - self._name = name - - @staticmethod - def nativeOrder(): - if _sys.byteorder == "little": - return ByteOrder.LITTLE_ENDIAN - return ByteOrder.BIG_ENDIAN - - def toString(self): - return self._name - - -ByteOrder.BIG_ENDIAN = ByteOrder("BIG_ENDIAN") -ByteOrder.LITTLE_ENDIAN = ByteOrder("LITTLE_ENDIAN") - - -# ============================================================ -# Buffer -# ============================================================ - -class Buffer(Stub): - def __init__(self, capacity, backing=None): - super().__init__() - self._capacity = capacity - self._position = 0 - self._limit = capacity - self._mark = -1 - self._backing = backing if backing is not None else bytearray(capacity) - self._read_only = False - - def capacity(self): - return self._capacity - - def position(self): - return self._position - - def position(self, new_position): - if new_position < 0 or new_position > self._limit: - raise IllegalArgumentException("Invalid position") - self._position = new_position - return self - - def limit(self): - return self._limit - - def limit(self, new_limit): - if new_limit < 0 or new_limit > self._capacity: - raise IllegalArgumentException("Invalid limit") - self._limit = new_limit - if self._position > self._limit: - self._position = self._limit - if self._mark > self._limit: - self._mark = -1 - return self - - def mark(self): - self._mark = self._position - return self - - def reset(self): - if self._mark < 0: - raise InvalidMarkException() - self._position = self._mark - return self - - def clear(self): - self._position = 0 - self._limit = self._capacity - self._mark = -1 - return self - - def flip(self): - self._limit = self._position - self._position = 0 - self._mark = -1 - return self - - def rewind(self): - self._position = 0 - self._mark = -1 - return self - - def remaining(self): - return self._limit - self._position - - def hasRemaining(self): - return self._position < self._limit - - def isReadOnly(self): - return self._read_only - - def isDirect(self): - return False - - def array(self): - if self._read_only: - raise ReadOnlyBufferException() - return self._backing - - def arrayOffset(self): - return 0 - - -# ============================================================ -# ByteBuffer -# ============================================================ - -class ByteBuffer(Buffer): - def __init__(self, capacity, backing=None, order=None): - super().__init__(capacity, backing) - self._order = order or ByteOrder.BIG_ENDIAN - - @staticmethod - def allocate(capacity): - return ByteBuffer(capacity, bytearray(capacity)) - - @staticmethod - def allocateDirect(capacity): - return ByteBuffer(capacity, bytearray(capacity)) - - @staticmethod - def wrap(array, offset=0, length=None): - if length is None: - length = len(array) - offset - buf = ByteBuffer(length, bytearray(array)) - buf._position = offset - buf._limit = offset + length - return buf - - def put(self, b, index=None): - if self._read_only: - raise ReadOnlyBufferException() - if index is None: - if isinstance(b, int): - if self._position >= self._limit: - raise BufferOverflowException() - self._backing[self._position] = b & 0xFF - self._position += 1 - elif isinstance(b, ByteBuffer): - n = b.remaining() - if self.remaining() < n: - raise BufferOverflowException() - self._backing[self._position:self._position+n] = b._backing[b._position:b._limit] - self._position += n - b._position = b._limit - elif isinstance(b, (bytes, bytearray)): - if self.remaining() < len(b): - raise BufferOverflowException() - self._backing[self._position:self._position+len(b)] = b - self._position += len(b) - else: - self._backing[index] = b & 0xFF - return self - - def get(self, index=None): - if index is None: - if self._position >= self._limit: - raise BufferUnderflowException() - val = self._backing[self._position] - self._position += 1 - return val & 0xFF - return self._backing[index] & 0xFF - - def putInt(self, value, index=None): - data = _struct.pack(">i" if self._order == ByteOrder.BIG_ENDIAN else "i" if self._order == ByteOrder.BIG_ENDIAN else "q" if self._order == ByteOrder.BIG_ENDIAN else "q" if self._order == ByteOrder.BIG_ENDIAN else "f" if self._order == ByteOrder.BIG_ENDIAN else "f" if self._order == ByteOrder.BIG_ENDIAN else "d" if self._order == ByteOrder.BIG_ENDIAN else "d" if self._order == ByteOrder.BIG_ENDIAN else "h" if self._order == ByteOrder.BIG_ENDIAN else "h" if self._order == ByteOrder.BIG_ENDIAN else " b) - (a < b) - return -1 - - -# ============================================================ -# IntBuffer -# ============================================================ - -class IntBuffer(Buffer): - def __init__(self, capacity, backing, offset=0, order=None): - super().__init__(capacity, backing) - self._int_order = order or ByteOrder.BIG_ENDIAN - self._int_offset = offset - - def get(self, index=None): - if index is not None: - idx = self._int_offset + index * 4 - return _struct.unpack(">i" if self._int_order == ByteOrder.BIG_ENDIAN else "i" if self._int_order == ByteOrder.BIG_ENDIAN else "i" if self._int_order == ByteOrder.BIG_ENDIAN else "q" if self._long_order == ByteOrder.BIG_ENDIAN else "q" if self._long_order == ByteOrder.BIG_ENDIAN else "q" if self._long_order == ByteOrder.BIG_ENDIAN else "f" if self._float_order == ByteOrder.BIG_ENDIAN else "f" if self._float_order == ByteOrder.BIG_ENDIAN else "f" if self._float_order == ByteOrder.BIG_ENDIAN else "d" if self._double_order == ByteOrder.BIG_ENDIAN else "d" if self._double_order == ByteOrder.BIG_ENDIAN else "d" if self._double_order == ByteOrder.BIG_ENDIAN else "h" if self._short_order == ByteOrder.BIG_ENDIAN else "h" if self._short_order == ByteOrder.BIG_ENDIAN else "h" if self._short_order == ByteOrder.BIG_ENDIAN else "h" if self._char_order == ByteOrder.BIG_ENDIAN else "h" if self._char_order == ByteOrder.BIG_ENDIAN else "h" if self._char_order == ByteOrder.BIG_ENDIAN else " bool: - return self._open - - def close(self): - self._open = False - - def toString(self) -> str: - return f"{self.__class__.__name__}@{hex(id(self))}[open={self._open}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -class ReadableByteChannel(Channel): - """A channel that can read bytes.""" - def read(self, dst) -> int: - raise NotImplementedError - - -class WritableByteChannel(Channel): - """A channel that can write bytes.""" - def write(self, src) -> int: - raise NotImplementedError - - -class ByteChannel(ReadableByteChannel, WritableByteChannel): - """A channel that can both read and write bytes.""" - pass - - -class ScatteringByteChannel(ReadableByteChannel): - """A channel that can read bytes into a sequence of buffers.""" - def read(self, dsts, offset=None, length=None) -> int: - total = 0 - buffers = dsts[offset:offset + length] if offset is not None else dsts - for buf in buffers: - total += self.read(buf) - return total - - -class GatheringByteChannel(WritableByteChannel): - """A channel that can write bytes from a sequence of buffers.""" - def write(self, srcs, offset=None, length=None) -> int: - total = 0 - buffers = srcs[offset:offset + length] if offset is not None else srcs - for buf in buffers: - total += self.write(buf) - return total - - -class InterruptibleChannel(Channel): - """A channel that can be asynchronously closed and interrupted.""" - pass - - -# ============================================================ -# SelectionKey -# ============================================================ - -class SelectionKey(Stub): - """ - java.nio.channels.SelectionKey - A token representing the registration of a SelectableChannel with a Selector. - """ - OP_ACCEPT: int = 1 << 4 # 16 - OP_CONNECT: int = 1 << 3 # 8 - OP_READ: int = 1 << 0 # 1 - OP_WRITE: int = 1 << 2 # 4 - - def __init__(self, channel: "SelectableChannel", selector: "Selector", - interest_ops: int = 0): - super().__init__() - self._channel = channel - self._selector = selector - self._interest_ops = interest_ops - self._ready_ops = 0 - self._valid = True - self._attachment = None - - def channel(self) -> "SelectableChannel": - return self._channel - - def selector(self) -> "Selector": - return self._selector - - def interestOps(self, ops: int = None): - if ops is not None: - self._interest_ops = ops - return self - return self._interest_ops - - def readyOps(self) -> int: - return self._ready_ops - - def isValid(self) -> bool: - return self._valid - - def isReadable(self) -> bool: - return (self._ready_ops & SelectionKey.OP_READ) != 0 - - def isWritable(self) -> bool: - return (self._ready_ops & SelectionKey.OP_WRITE) != 0 - - def isConnectable(self) -> bool: - return (self._ready_ops & SelectionKey.OP_CONNECT) != 0 - - def isAcceptable(self) -> bool: - return (self._ready_ops & SelectionKey.OP_ACCEPT) != 0 - - def cancel(self): - self._valid = False - - def attach(self, ob: Any) -> Any: - old = self._attachment - self._attachment = ob - return old - - def attachment(self) -> Any: - return self._attachment - - def toString(self) -> str: - return (f"SelectionKey@{hex(id(self))}[interest={self._interest_ops}, " - f"ready={self._ready_ops}, valid={self._valid}]") - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(id(self)) & 0x7FFFFFFF - - -# ============================================================ -# SelectableChannel -# ============================================================ - -class SelectableChannel(Channel): - """ - java.nio.channels.SelectableChannel - A channel that can be multiplexed via a Selector. - """ - - def __init__(self): - super().__init__() - self._blocking = True - self._lock = _threading.Lock() - self._keys = {} - - def register(self, sel: "Selector", ops: int, att: Any = None) -> SelectionKey: - key = SelectionKey(self, sel, ops) - if att is not None: - key.attach(att) - self._keys[sel] = key - sel._register_key(key) - return key - - def configureBlocking(self, block: bool) -> "SelectableChannel": - self._blocking = block - return self - - def isBlocking(self) -> bool: - return self._blocking - - def blockingLock(self) -> Any: - return self._lock - - def keyFor(self, sel: "Selector") -> Optional[SelectionKey]: - return self._keys.get(sel) - - def provider(self): - return None - - def validOps(self) -> int: - return 0 - - -# ============================================================ -# Selector -# ============================================================ - -class Selector(Stub): - """ - java.nio.channels.Selector - A selector for selectable channels. - """ - - def __init__(self): - super().__init__() - self._open = True - self._keys = {} - self._selected_keys = set() - - def _register_key(self, key: SelectionKey): - self._keys[key.channel()] = key - - @staticmethod - def open() -> "Selector": - return Selector() - - def isOpen(self) -> bool: - return self._open - - def provider(self): - return None - - def keys(self) -> Set[SelectionKey]: - return set(self._keys.values()) - - def selectedKeys(self) -> Set[SelectionKey]: - return self._selected_keys - - def selectNow(self) -> int: - """Non-blocking select. Returns number of ready channels.""" - return self._do_select(0) - - def select(self, timeout: int = 0) -> int: - """Blocking select with optional timeout (ms).""" - return self._do_select(timeout) - - def _do_select(self, timeout_ms: int) -> int: - """Internal select implementation using Python select module.""" - read_fds = [] - write_fds = [] - for ch, key in self._keys.items(): - ops = key.interestOps() - if hasattr(ch, '_socket') and ch._socket: - fd = ch._socket - if ops & SelectionKey.OP_READ: - read_fds.append(fd) - if ops & SelectionKey.OP_WRITE: - write_fds.append(fd) - - timeout_s = timeout_ms / 1000.0 if timeout_ms > 0 else None - ready_count = 0 - - try: - r_ready, w_ready, _ = _select.select(read_fds, write_fds, [], timeout_s if timeout_s else 0) - except (ValueError, _socket.error): - return 0 - - self._selected_keys.clear() - for fd in r_ready: - for ch, key in self._keys.items(): - if hasattr(ch, '_socket') and ch._socket is fd: - key._ready_ops |= SelectionKey.OP_READ - self._selected_keys.add(key) - ready_count += 1 - for fd in w_ready: - for ch, key in self._keys.items(): - if hasattr(ch, '_socket') and ch._socket is fd: - key._ready_ops |= SelectionKey.OP_WRITE - self._selected_keys.add(key) - ready_count += 1 - - return ready_count - - def wakeup(self): - """Wakes up a selector that is blocked on a select operation.""" - pass - - def close(self): - self._open = False - self._keys.clear() - self._selected_keys.clear() - - def toString(self) -> str: - return f"Selector@{hex(id(self))}[open={self._open}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# ServerSocketChannel -# ============================================================ - -class ServerSocketChannel(SelectableChannel): - """ - java.nio.channels.ServerSocketChannel - A selectable channel for stream-oriented listening sockets. - """ - - def __init__(self): - super().__init__() - self._socket = None - self._server_socket = None - self._local_address = None - - @staticmethod - def open() -> "ServerSocketChannel": - return ServerSocketChannel() - - def bind(self, local: Any, backlog: int = 50): - """Binds the channel's socket to a local address.""" - if self._socket is None: - self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM) - self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, 1) - if hasattr(local, 'getPort') and hasattr(local, 'getHostName'): - addr = (local.getHostName(), local.getPort()) - elif isinstance(local, tuple): - addr = local - else: - addr = ('0.0.0.0', 0) - self._socket.bind(addr) - self._socket.listen(backlog) - self._local_address = self._socket.getsockname() - return self - - def accept(self) -> Optional["SocketChannel"]: - """Accepts a connection.""" - if self._socket is None: - return None - try: - conn, addr = self._socket.accept() - sc = SocketChannel() - sc._socket = conn - sc._connected = True - sc._remote_address = addr - return sc - except (BlockingIOError, _socket.error): - return None - - def socket(self): - """Returns the underlying ServerSocket.""" - return self._server_socket - - def getLocalAddress(self): - """Returns the local address of this channel.""" - return self._local_address - - def setOption(self, name, value): - if self._socket: - self._socket.setsockopt(_socket.SOL_SOCKET, name, value) - return self - - def getOption(self, name): - if self._socket: - return self._socket.getsockopt(_socket.SOL_SOCKET, name) - return None - - def supportsOption(self, name) -> bool: - return True - - def validOps(self) -> int: - return SelectionKey.OP_ACCEPT - - def close(self): - super().close() - if self._socket: - self._socket.close() - - def toString(self) -> str: - return f"ServerSocketChannel@{hex(id(self))}[local={self._local_address}]" - - -# ============================================================ -# SocketChannel -# ============================================================ - -class SocketChannel(SelectableChannel): - """ - java.nio.channels.SocketChannel - A selectable channel for stream-oriented connecting sockets. - """ - - def __init__(self): - super().__init__() - self._socket = None - self._connected = False - self._connection_pending = False - self._remote_address = None - self._local_address = None - self._input_shutdown = False - self._output_shutdown = False - - @staticmethod - def open() -> "SocketChannel": - return SocketChannel() - - def connect(self, remote: Any) -> bool: - """Connects this channel's socket to the remote address.""" - if self._socket is None: - self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM) - if hasattr(remote, 'getHostName') and hasattr(remote, 'getPort'): - addr = (remote.getHostName(), remote.getPort()) - elif isinstance(remote, tuple): - addr = remote - else: - addr = (str(remote), 0) - try: - self._socket.connect(addr) - self._connected = True - self._remote_address = addr - self._local_address = self._socket.getsockname() - return True - except _socket.error: - self._connection_pending = True - return False - - def finishConnect(self) -> bool: - """Finishes the connection process.""" - self._connected = True - self._connection_pending = False - return True - - def isConnected(self) -> bool: - return self._connected - - def isConnectionPending(self) -> bool: - return self._connection_pending - - def read(self, dst) -> int: - """Reads bytes from this channel into the given buffer.""" - if not self._connected or self._socket is None: - return -1 - try: - data = self._socket.recv(4096) - if not data: - return -1 - from ..nio import ByteBuffer - if isinstance(dst, ByteBuffer): - for b in data: - dst.put(b) - return len(data) - except (_socket.error, BlockingIOError): - return 0 - - def write(self, src) -> int: - """Writes bytes to this channel from the given buffer.""" - if not self._connected or self._socket is None: - return 0 - from ..nio import ByteBuffer - if isinstance(src, ByteBuffer): - data = bytearray() - for i in range(src.position(), src.limit()): - data.append(src.get(i)) - try: - return self._socket.send(bytes(data)) - except _socket.error: - return 0 - - def socket(self): - return self._socket - - def getRemoteAddress(self): - return self._remote_address - - def getLocalAddress(self): - return self._local_address - - def shutdownInput(self): - self._input_shutdown = True - if self._socket: - try: - self._socket.shutdown(_socket.SHUT_RD) - except _socket.error: - pass - - def shutdownOutput(self): - self._output_shutdown = True - if self._socket: - try: - self._socket.shutdown(_socket.SHUT_WR) - except _socket.error: - pass - - def setOption(self, name, value): - if self._socket: - self._socket.setsockopt(_socket.SOL_SOCKET, name, value) - return self - - def getOption(self, name): - if self._socket: - return self._socket.getsockopt(_socket.SOL_SOCKET, name) - return None - - def supportsOption(self, name) -> bool: - return True - - def validOps(self) -> int: - return SelectionKey.OP_READ | SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT - - def close(self): - super().close() - self._connected = False - if self._socket: - self._socket.close() - - def toString(self) -> str: - return f"SocketChannel@{hex(id(self))}[connected={self._connected}]" - - -# ============================================================ -# DatagramChannel -# ============================================================ - -class DatagramChannel(SelectableChannel): - """ - java.nio.channels.DatagramChannel - A selectable channel for datagram-oriented sockets. - """ - - def __init__(self): - super().__init__() - self._socket = None - self._connected = False - self._remote_address = None - self._local_address = None - - @staticmethod - def open() -> "DatagramChannel": - return DatagramChannel() - - def bind(self, local: Any): - if self._socket is None: - self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_DGRAM) - if hasattr(local, 'getPort'): - addr = ('0.0.0.0', local.getPort()) - elif isinstance(local, tuple): - addr = local - else: - addr = ('0.0.0.0', 0) - self._socket.bind(addr) - self._local_address = self._socket.getsockname() - return self - - def connect(self, remote: Any): - if self._socket is None: - self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_DGRAM) - if hasattr(remote, 'getHostName') and hasattr(remote, 'getPort'): - addr = (remote.getHostName(), remote.getPort()) - elif isinstance(remote, tuple): - addr = remote - else: - addr = (str(remote), 0) - self._socket.connect(addr) - self._connected = True - self._remote_address = addr - - def disconnect(self): - self._connected = False - self._remote_address = None - - def isConnected(self) -> bool: - return self._connected - - def receive(self, dst) -> Any: - """Receives a datagram.""" - if self._socket is None: - return None - try: - data, addr = self._socket.recvfrom(65535) - from ..nio import ByteBuffer - if isinstance(dst, ByteBuffer): - for b in data: - dst.put(b) - return addr - except (_socket.error, BlockingIOError): - return None - - def send(self, src, target) -> int: - """Sends a datagram.""" - if self._socket is None: - return 0 - from ..nio import ByteBuffer - if isinstance(src, ByteBuffer): - data = bytearray() - for i in range(src.position(), src.limit()): - data.append(src.get(i)) - if hasattr(target, 'getHostName') and hasattr(target, 'getPort'): - addr = (target.getHostName(), target.getPort()) - else: - addr = target - try: - return self._socket.sendto(bytes(data), addr) - except _socket.error: - return 0 - return 0 - - def read(self, dst) -> int: - """Reads a datagram (connected mode).""" - return self.receive(dst) - - def write(self, src) -> int: - """Writes a datagram (connected mode).""" - return self.send(src, self._remote_address) - - def socket(self): - return self._socket - - def getRemoteAddress(self): - return self._remote_address - - def getLocalAddress(self): - return self._local_address - - def setOption(self, name, value): - if self._socket: - self._socket.setsockopt(_socket.SOL_SOCKET, name, value) - return self - - def getOption(self, name): - if self._socket: - return self._socket.getsockopt(_socket.SOL_SOCKET, name) - return None - - def supportsOption(self, name) -> bool: - return True - - def validOps(self) -> int: - return SelectionKey.OP_READ | SelectionKey.OP_WRITE - - def close(self): - super().close() - if self._socket: - self._socket.close() - - def toString(self) -> str: - return f"DatagramChannel@{hex(id(self))}[connected={self._connected}]" - - -# ============================================================ -# Pipe -# ============================================================ - -class Pipe(Stub): - """ - java.nio.channels.Pipe - A pair of channels that implements a unidirectional pipe. - """ - - class SourceChannel(SelectableChannel): - """The readable end of the pipe.""" - def __init__(self, r_fd): - super().__init__() - self._r_fd = r_fd - - def read(self, dst) -> int: - data = _os.read(self._r_fd, 4096) - from ..nio import ByteBuffer - if isinstance(dst, ByteBuffer): - for b in data: - dst.put(b) - return len(data) - - def validOps(self) -> int: - return SelectionKey.OP_READ - - def close(self): - super().close() - _os.close(self._r_fd) - - class SinkChannel(SelectableChannel): - """The writable end of the pipe.""" - def __init__(self, w_fd): - super().__init__() - self._w_fd = w_fd - - def write(self, src) -> int: - from ..nio import ByteBuffer - if isinstance(src, ByteBuffer): - data = bytearray() - for i in range(src.position(), src.limit()): - data.append(src.get(i)) - return _os.write(self._w_fd, bytes(data)) - return 0 - - def validOps(self) -> int: - return SelectionKey.OP_WRITE - - def close(self): - super().close() - _os.close(self._w_fd) - - @staticmethod - def open() -> "Pipe": - pipe = Pipe() - r, w = _os.pipe() - pipe._source = Pipe.SourceChannel(r) - pipe._sink = Pipe.SinkChannel(w) - return pipe - - def source(self) -> SourceChannel: - return self._source - - def sink(self) -> SinkChannel: - return self._sink - - def toString(self) -> str: - return f"Pipe@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# FileChannel MapMode -# ============================================================ - -class FileChannelMapMode(Stub): - """ - java.nio.channels.FileChannel.MapMode - A file mapping mode. - """ - READ_ONLY: "FileChannelMapMode" = None - READ_WRITE: "FileChannelMapMode" = None - PRIVATE: "FileChannelMapMode" = None - - def __init__(self, name: str): - super().__init__() - self._name = name - - def toString(self) -> str: - return self._name - - def equals(self, other) -> bool: - return isinstance(other, FileChannelMapMode) and self._name == other._name - - def hashCode(self) -> int: - return hash(self._name) & 0x7FFFFFFF - - -FileChannelMapMode.READ_ONLY = FileChannelMapMode("READ_ONLY") -FileChannelMapMode.READ_WRITE = FileChannelMapMode("READ_WRITE") -FileChannelMapMode.PRIVATE = FileChannelMapMode("PRIVATE") - - -# ============================================================ -# FileChannel -# ============================================================ - -class FileChannel(Channel): - """ - java.nio.channels.FileChannel - A channel for reading, writing, mapping, and manipulating a file. - """ - IMPL_OPEN_LOCKING: bool = True - - def __init__(self, file_path: str = None, mode: str = 'r'): - super().__init__() - self._file_path = file_path - self._mode = mode - self._fd = None - self._position = 0 - if file_path: - self._fd = _os.open(file_path, self._get_os_flags(mode)) - - @staticmethod - def open(path: Any, options=None, attrs=None) -> "FileChannel": - """Opens a file channel.""" - return FileChannel(str(path)) - - def _get_os_flags(self, mode: str) -> int: - flags = 0 - if 'r' in mode and 'w' not in mode and '+' not in mode: - flags = _os.O_RDONLY - elif 'w' in mode or '+' in mode: - flags = _os.O_RDWR | _os.O_CREAT - return flags - - def read(self, dst) -> int: - """Reads bytes from this channel into the given buffer.""" - if self._fd is None: - return -1 - try: - data = _os.read(self._fd, 4096) - if not data: - return -1 - from ..nio import ByteBuffer - if isinstance(dst, ByteBuffer): - for b in data: - dst.put(b) - self._position += len(data) - return len(data) - except OSError: - return -1 - - def read(self, dsts, offset: int = 0, length: int = 0) -> int: - total = 0 - bufs = dsts[offset:offset + length] if length else dsts - for buf in bufs: - n = self.read(buf) - if n < 0: - break - total += n - return total - - def write(self, src) -> int: - """Writes bytes to this channel from the given buffer.""" - if self._fd is None: - return 0 - from ..nio import ByteBuffer - if isinstance(src, ByteBuffer): - data = bytearray() - for i in range(src.position(), src.limit()): - data.append(src.get(i)) - try: - written = _os.write(self._fd, bytes(data)) - self._position += written - return written - except OSError: - return 0 - return 0 - - def write(self, srcs, offset: int = 0, length: int = 0) -> int: - total = 0 - bufs = srcs[offset:offset + length] if length else srcs - for buf in bufs: - total += self.write(buf) - return total - - def position(self) -> int: - """Returns this channel's file position.""" - if self._fd is not None: - self._position = _os.lseek(self._fd, 0, _os.SEEK_CUR) - return self._position - - def position(self, new_position: int) -> "FileChannel": - """Sets this channel's file position.""" - if self._fd is not None: - _os.lseek(self._fd, new_position, _os.SEEK_SET) - self._position = new_position - return self - - def size(self) -> int: - """Returns the current size of this channel's file.""" - if self._fd is not None: - return _os.lseek(self._fd, 0, _os.SEEK_END) - return 0 - - def truncate(self, size: int) -> "FileChannel": - """Truncates the file to the given size.""" - if self._fd is not None: - _os.ftruncate(self._fd, size) - return self - - def force(self, meta_data: bool): - """Forces any updates to this channel's file to be written to storage.""" - if self._fd is not None: - _os.fsync(self._fd) - - def transferTo(self, position: int, count: int, target: WritableByteChannel) -> int: - """Transfers bytes from this channel to the given writable channel.""" - old_pos = self.position() - self.position(position) - from ..nio import ByteBuffer - buf = ByteBuffer.allocate(count) - n = self.read(buf) - buf.flip() - target.write(buf) - self.position(old_pos) - return n - - def transferFrom(self, src: ReadableByteChannel, position: int, count: int) -> int: - """Transfers bytes from the given readable channel to this channel.""" - old_pos = self.position() - self.position(position) - from ..nio import ByteBuffer - buf = ByteBuffer.allocate(count) - n = src.read(buf) - buf.flip() - self.write(buf) - self.position(old_pos) - return n - - def map(self, mode: FileChannelMapMode, position: int, size: int): - """Maps a region of this channel's file into memory (stub).""" - return None - - def lock(self, position: int = 0, size: int = 0, shared: bool = False): - """Locks a region of this channel's file (stub).""" - return None - - def tryLock(self, position: int = 0, size: int = 0, shared: bool = False): - """Attempts to lock a region of this channel's file (stub).""" - return None - - def close(self): - super().close() - if self._fd is not None: - _os.close(self._fd) - self._fd = None - - def toString(self) -> str: - return f"FileChannel@{hex(id(self))}[path={self._file_path}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# AsynchronousChannel stubs -# ============================================================ - -class CompletionHandler(Stub): - """ - java.nio.channels.CompletionHandler - A handler for consuming the result of an asynchronous I/O operation. - """ - def completed(self, result, attachment): - raise NotImplementedError - - def failed(self, exc: Exception, attachment): - raise NotImplementedError - - -class AsynchronousChannel(Channel): - """An asynchronous channel for I/O operations.""" - pass - - -class AsynchronousByteChannel(AsynchronousChannel): - """An asynchronous channel that can read and write bytes.""" - pass - - -class AsynchronousChannelGroup(Stub): - """ - java.nio.channels.AsynchronousChannelGroup - A grouping of asynchronous channels. - """ - def __init__(self): - super().__init__() - self._channels = [] - - def shutdown(self): - pass - - def shutdownNow(self): - pass - - def awaitTermination(self, timeout: int, unit) -> bool: - return True - - def isShutdown(self) -> bool: - return False - - def isTerminated(self) -> bool: - return False - - def toString(self) -> str: - return f"AsynchronousChannelGroup@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -class AsynchronousServerSocketChannel(AsynchronousChannel): - """An asynchronous channel for stream-oriented listening sockets.""" - @staticmethod - def open(group: AsynchronousChannelGroup = None) -> "AsynchronousServerSocketChannel": - return AsynchronousServerSocketChannel() - - def bind(self, local: Any) -> "AsynchronousServerSocketChannel": - return self - - def accept(self, attachment=None, handler: CompletionHandler = None): - pass - - def getLocalAddress(self): - return None - - def setOption(self, name, value): - return self - - def close(self): - pass - - def toString(self) -> str: - return f"AsynchronousServerSocketChannel@{hex(id(self))}" - - -class AsynchronousSocketChannel(AsynchronousByteChannel): - """An asynchronous channel for stream-oriented connecting sockets.""" - @staticmethod - def open(group: AsynchronousChannelGroup = None) -> "AsynchronousSocketChannel": - return AsynchronousSocketChannel() - - def connect(self, remote: Any, attachment=None, handler: CompletionHandler = None): - pass - - def read(self, dst, attachment=None, handler: CompletionHandler = None): - pass - - def write(self, src, attachment=None, handler: CompletionHandler = None): - pass - - def getRemoteAddress(self): - return None - - def getLocalAddress(self): - return None - - def shutdownInput(self): - pass - - def shutdownOutput(self): - pass - - def close(self): - pass - - def toString(self) -> str: - return f"AsynchronousSocketChannel@{hex(id(self))}" - - -class AsynchronousFileChannel(AsynchronousChannel): - """An asynchronous channel for reading, writing, and manipulating a file.""" - @staticmethod - def open(path: Any, options=None, executor=None, attrs=None) -> "AsynchronousFileChannel": - return AsynchronousFileChannel() - - def read(self, dst, position: int, attachment=None, handler: CompletionHandler = None): - pass - - def write(self, src, position: int, attachment=None, handler: CompletionHandler = None): - pass - - def lock(self, position: int = 0, size: int = 0, shared: bool = False, - attachment=None, handler: CompletionHandler = None): - pass - - def tryLock(self, position: int = 0, size: int = 0, shared: bool = False): - return None - - def size(self) -> int: - return 0 - - def truncate(self, size: int) -> "AsynchronousFileChannel": - return self - - def force(self, meta_data: bool): - pass - - def close(self): - pass - - def isOpen(self) -> bool: - return True - - def toString(self) -> str: - return f"AsynchronousFileChannel@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/java/nio_charset.py b/android_pyapi/java/nio_charset.py deleted file mode 100644 index 0acef20..0000000 --- a/android_pyapi/java/nio_charset.py +++ /dev/null @@ -1,573 +0,0 @@ -""" -Java java.nio.charset APIs implemented in Python. -java.nio.charset package - Charset, CharsetEncoder, CharsetDecoder, CoderResult, etc. -Wraps Python codecs module. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -import codecs as _codecs -import encodings as _encodings -from typing import Set as _Set, List as _List - -__all__ = [ - "Charset", "CharsetEncoder", "CharsetDecoder", "CoderResult", - "CodingErrorAction", "StandardCharsets", -] - - -# ============================================================ -# CodingErrorAction -# ============================================================ - -class CodingErrorAction(Stub): - """ - java.nio.charset.CodingErrorAction - Describes actions that a charset encoder or decoder may take on a coding error. - """ - REPORT: "CodingErrorAction" = None - IGNORE: "CodingErrorAction" = None - REPLACE: "CodingErrorAction" = None - - def __init__(self, name: str): - super().__init__() - self._name = name - - def name(self) -> str: - return self._name - - def toString(self) -> str: - return self._name - - def equals(self, other) -> bool: - if isinstance(other, CodingErrorAction): - return self._name == other._name - return False - - def hashCode(self) -> int: - return hash(self._name) & 0x7FFFFFFF - - -CodingErrorAction.REPORT = CodingErrorAction("REPORT") -CodingErrorAction.IGNORE = CodingErrorAction("IGNORE") -CodingErrorAction.REPLACE = CodingErrorAction("REPLACE") - - -# ============================================================ -# CoderResult -# ============================================================ - -class CoderResult(Stub): - """ - java.nio.charset.CoderResult - Describes the result state of a coder (encoder or decoder). - """ - UNDERFLOW: "CoderResult" = None - OVERFLOW: "CoderResult" = None - - def __init__(self, _type: str, _length: int = 0): - super().__init__() - self._type = _type - self._length = _length - - def isUnderflow(self) -> bool: - """Returns True if the result is underflow (more input needed).""" - return self._type == "UNDERFLOW" - - def isOverflow(self) -> bool: - """Returns True if the result is overflow (output buffer too small).""" - return self._type == "OVERFLOW" - - def isError(self) -> bool: - """Returns True if the result is an error (malformed or unmappable).""" - return self._type in ("MALFORMED", "UNMAPPABLE") - - def isMalformed(self) -> bool: - """Returns True if the result is a malformed-input error.""" - return self._type == "MALFORMED" - - def isUnmappable(self) -> bool: - """Returns True if the result is an unmappable-character error.""" - return self._type == "UNMAPPABLE" - - def length(self) -> int: - """Returns the length of the erroneous input (for errors).""" - return self._length - - @classmethod - def malformedForLength(cls, length: int) -> "CoderResult": - """Creates a malformed CoderResult for the given length.""" - return cls("MALFORMED", length) - - @classmethod - def unmappableForLength(cls, length: int) -> "CoderResult": - """Creates an unmappable CoderResult for the given length.""" - return cls("UNMAPPABLE", length) - - def toString(self) -> str: - if self._type == "UNDERFLOW": - return "UNDERFLOW" - elif self._type == "OVERFLOW": - return "OVERFLOW" - return f"{self._type}[{self._length}]" - - def equals(self, other) -> bool: - if not isinstance(other, CoderResult): - return False - return self._type == other._type and self._length == other._length - - def hashCode(self) -> int: - return hash((self._type, self._length)) & 0x7FFFFFFF - - -CoderResult.UNDERFLOW = CoderResult("UNDERFLOW") -CoderResult.OVERFLOW = CoderResult("OVERFLOW") - - -# ============================================================ -# Charset -# ============================================================ - -class Charset(Stub): - """ - java.nio.charset.Charset - A named mapping between sequences of 16-bit Unicode characters - and sequences of bytes. - """ - - _cache: dict = {} - _aliases: dict = {} - - # Standard charset constants - US_ASCII: "Charset" = None - ISO_8859_1: "Charset" = None - UTF_8: "Charset" = None - UTF_16: "Charset" = None - UTF_16BE: "Charset" = None - UTF_16LE: "Charset" = None - - def __init__(self, canonical_name: str, aliases: _List[str] = None): - super().__init__() - self._name = canonical_name - self._aliases = aliases or [] - self._registered = True - - def name(self) -> str: - """Returns the canonical name of this charset.""" - return self._name - - def displayName(self, locale=None) -> str: - """Returns the display name of this charset.""" - return self._name - - def aliases(self) -> _Set[str]: - """Returns a set containing this charset's aliases.""" - return set(self._aliases) - - def isRegistered(self) -> bool: - """Returns True if this charset is registered in the IANA registry.""" - return self._registered - - def canEncode(self) -> bool: - """Returns True if this charset supports encoding.""" - try: - _codecs.lookup(self._name) - return True - except LookupError: - return False - - def encode(self, text: str) -> "ByteBuffer": - """ - Converts a string into bytes using this charset. - Returns a ByteBuffer. - """ - encoded = text.encode(self._name, errors="replace") - from ..nio import ByteBuffer - buf = ByteBuffer.allocate(len(encoded)) - buf.put(list(encoded)) - buf.flip() - return buf - - def decode(self, buf: "ByteBuffer") -> "CharBuffer": - """ - Converts bytes from a ByteBuffer into a CharBuffer. - """ - arr = bytearray() - for i in range(buf.position(), buf.limit()): - arr.append(buf.get(i)) - decoded = arr.decode(self._name, errors="replace") - from ..nio import CharBuffer - cb = CharBuffer.allocate(len(decoded)) - cb.put(decoded) - cb.flip() - return cb - - def newDecoder(self) -> "CharsetDecoder": - """Returns a new CharsetDecoder for this charset.""" - return CharsetDecoder(self) - - def newEncoder(self) -> "CharsetEncoder": - """Returns a new CharsetEncoder for this charset.""" - return CharsetEncoder(self) - - def toString(self) -> str: - return self._name - - def hashCode(self) -> int: - return hash(self._name) & 0x7FFFFFFF - - def equals(self, other) -> bool: - if not isinstance(other, Charset): - return False - return self._name == other._name - - def compareTo(self, other: "Charset") -> int: - """Compares this charset to another by canonical name.""" - if not isinstance(other, Charset): - return -1 - if self._name < other._name: - return -1 - elif self._name > other._name: - return 1 - return 0 - - @classmethod - def forName(cls, charset_name: str) -> "Charset": - """Returns a Charset for the given charset name.""" - if charset_name in cls._cache: - return cls._cache[charset_name] - - try: - _codecs.lookup(charset_name) - except LookupError: - raise AndroidOnlyError(f"Charset not found: {charset_name}") - - # Resolve aliases - canonical = charset_name.lower() - alias_map = { - "ascii": "US-ASCII", "us-ascii": "US-ASCII", - "iso-8859-1": "ISO-8859-1", "latin1": "ISO-8859-1", - "utf-8": "UTF-8", "utf8": "UTF-8", - "utf-16": "UTF-16", "utf16": "UTF-16", - "utf-16be": "UTF-16BE", "utf16be": "UTF-16BE", - "utf-16le": "UTF-16LE", "utf16le": "UTF-16LE", - } - canonical_name = alias_map.get(canonical, charset_name.upper()) - - charset = cls(canonical_name) - cls._cache[charset_name] = charset - return charset - - @classmethod - def forNameUEE(cls, charset_name: str) -> "Charset": - """Same as forName but wraps exception.""" - try: - return cls.forName(charset_name) - except Exception: - return None - - @classmethod - def isSupported(cls, charset_name: str) -> bool: - """Returns True if the named charset is supported.""" - try: - _codecs.lookup(charset_name) - return True - except LookupError: - return False - - @classmethod - def defaultCharset(cls) -> "Charset": - """Returns the default charset of the JVM (platform native encoding).""" - import locale - enc = locale.getpreferredencoding() - return cls.forName(enc) - - @classmethod - def availableCharsets(cls) -> dict: - """Returns a sorted map of available charset names to Charset objects.""" - result = {} - for name in _codecs.available_encodings(): - try: - result[name] = cls.forName(name) - except Exception: - pass - return result - - @classmethod - def requiredCharsets(cls) -> _Set[str]: - """Returns the set of required charset names per Java spec.""" - return { - "US-ASCII", "ISO-8859-1", "UTF-8", "UTF-16", - "UTF-16BE", "UTF-16LE", - } - - -# Initialize standard charset constants -Charset.US_ASCII = Charset("US-ASCII", ["ASCII", "US-ASCII", "ascii", "cp367"]) -Charset.ISO_8859_1 = Charset("ISO-8859-1", ["ISO-8859-1", "latin1", "LATIN-1", "l1", "cp819"]) -Charset.UTF_8 = Charset("UTF-8", ["UTF-8", "utf8", "UTF8"]) -Charset.UTF_16 = Charset("UTF-16", ["UTF-16", "utf16", "UTF16", "UnicodeBig"]) -Charset.UTF_16BE = Charset("UTF-16BE", ["UTF-16BE", "utf16be", "UTF16BE", "ISO-10646-UCS-2"]) -Charset.UTF_16LE = Charset("UTF-16LE", ["UTF-16LE", "utf16le", "UTF16LE"]) - - -# ============================================================ -# CharsetEncoder -# ============================================================ - -class CharsetEncoder(Stub): - """ - java.nio.charset.CharsetEncoder - Converts character data to byte data using a Charset. - """ - - def __init__(self, charset: Charset, average_bytes_per_char: float = 1.0, - max_bytes_per_char: float = 4.0, replacement: bytes = b'?'): - super().__init__() - self._charset = charset - self._average_bytes_per_char = average_bytes_per_char - self._max_bytes_per_char = max_bytes_per_char - self._replacement = replacement - self._malformed_input_action = CodingErrorAction.REPORT - self._unmappable_action = CodingErrorAction.REPORT - - def charset(self) -> Charset: - """Returns the charset that created this encoder.""" - return self._charset - - def replacement(self) -> bytes: - """Returns the replacement value.""" - return self._replacement - - def replaceWith(self, new_replacement: bytes) -> "CharsetEncoder": - """Sets a new replacement value.""" - self._replacement = new_replacement - return self - - def encode(self, input_str: str) -> "ByteBuffer": - """Encodes a string into bytes.""" - encoded = input_str.encode(self._charset.name(), errors="replace") - from ..nio import ByteBuffer - buf = ByteBuffer.allocate(len(encoded)) - buf.put(list(encoded)) - buf.flip() - return buf - - def encode(self, input: "CharBuffer", output: "ByteBuffer", end_of_input: bool) -> CoderResult: - """Encodes as much as possible from input to output.""" - in_str = "" - for i in range(input.position(), input.limit()): - in_str += chr(input.get(i)) - try: - encoded = in_str.encode(self._charset.name()) - from ..nio import ByteBuffer - out_arr = bytearray() - for i in range(output.position(), output.limit()): - if i < len(encoded): - out_arr.append(encoded[i]) - return CoderResult.UNDERFLOW - except UnicodeEncodeError: - return CoderResult.unmappableForLength(1) - - def encodeLoop(self, input: "CharBuffer", output: "ByteBuffer") -> CoderResult: - """The encoding loop (subclass hook).""" - return self.encode(input, output, True) - - def implReplaceWith(self, new_replacement: bytes): - """Hook for subclasses to react to replacement changes.""" - pass - - def implOnMalformedInput(self, new_action: CodingErrorAction): - """Hook for subclasses to react to malformed input action changes.""" - pass - - def implOnUnmappableCharacter(self, new_action: CodingErrorAction): - """Hook for subclasses to react to unmappable char action changes.""" - pass - - def implReset(self): - """Hook for subclasses to reset internal state.""" - pass - - def averageBytesPerChar(self) -> float: - return self._average_bytes_per_char - - def maxBytesPerChar(self) -> float: - return self._max_bytes_per_char - - def isLegalReplacement(self, repl: bytes) -> bool: - """Returns True if the replacement is a legal byte sequence.""" - return len(repl) > 0 - - def malformedInputAction(self) -> CodingErrorAction: - return self._malformed_input_action - - def unmappableCharacterAction(self) -> CodingErrorAction: - return self._unmappable_action - - def onMalformedInput(self, action: CodingErrorAction) -> "CharsetEncoder": - self._malformed_input_action = action - return self - - def onUnmappableCharacter(self, action: CodingErrorAction) -> "CharsetEncoder": - self._unmappable_action = action - return self - - def reset(self) -> "CharsetEncoder": - """Resets this encoder, clearing any internal state.""" - self.implReset() - return self - - def toString(self) -> str: - return f"CharsetEncoder@{hex(id(self))}[charset={self._charset.name()}]" - - def equals(self, other) -> bool: - if not isinstance(other, CharsetEncoder): - return False - return self._charset == other._charset - - def hashCode(self) -> int: - return hash(self._charset.name()) & 0x7FFFFFFF - - -# ============================================================ -# CharsetDecoder -# ============================================================ - -class CharsetDecoder(Stub): - """ - java.nio.charset.CharsetDecoder - Converts byte data to character data using a Charset. - """ - - def __init__(self, charset: Charset, average_chars_per_byte: float = 1.0, - max_chars_per_byte: float = 2.0, replacement: str = '\ufffd'): - super().__init__() - self._charset = charset - self._average_chars_per_byte = average_chars_per_byte - self._max_chars_per_byte = max_chars_per_byte - self._replacement = replacement - self._malformed_input_action = CodingErrorAction.REPORT - self._unmappable_action = CodingErrorAction.REPORT - self._auto_detect = False - self._detected_charset = None - - def charset(self) -> Charset: - """Returns the charset that created this decoder.""" - return self._charset - - def replacement(self) -> str: - """Returns the replacement value.""" - return self._replacement - - def replaceWith(self, new_replacement: str) -> "CharsetDecoder": - """Sets a new replacement value.""" - self._replacement = new_replacement - return self - - def decode(self, buf: "ByteBuffer") -> "CharBuffer": - """Decodes bytes from a ByteBuffer into a CharBuffer.""" - arr = bytearray() - for i in range(buf.position(), buf.limit()): - arr.append(buf.get(i)) - decoded = arr.decode(self._charset.name(), errors="replace") - from ..nio import CharBuffer - cb = CharBuffer.allocate(len(decoded)) - cb.put(decoded) - cb.flip() - return cb - - def decode(self, input: "ByteBuffer", output: "CharBuffer", end_of_input: bool) -> CoderResult: - """Decodes as much as possible from input to output.""" - arr = bytearray() - for i in range(input.position(), input.limit()): - arr.append(input.get(i)) - try: - decoded = arr.decode(self._charset.name()) - return CoderResult.UNDERFLOW - except UnicodeDecodeError: - return CoderResult.malformedForLength(1) - - def decodeLoop(self, input: "ByteBuffer", output: "CharBuffer") -> CoderResult: - """The decoding loop (subclass hook).""" - return self.decode(input, output, True) - - def implReplaceWith(self, new_replacement: str): - """Hook for subclasses.""" - pass - - def implOnMalformedInput(self, new_action: CodingErrorAction): - """Hook for subclasses.""" - pass - - def implOnUnmappableCharacter(self, new_action: CodingErrorAction): - """Hook for subclasses.""" - pass - - def implReset(self): - """Hook for subclasses.""" - pass - - def averageCharsPerByte(self) -> float: - return self._average_chars_per_byte - - def maxCharsPerByte(self) -> float: - return self._max_chars_per_byte - - def isCharsetDetected(self) -> bool: - return self._detected_charset is not None - - def isAutoDetect(self) -> bool: - return self._auto_detect - - def detectedCharset(self) -> Charset: - if self._detected_charset is None: - raise AndroidOnlyError("No charset detected") - return self._detected_charset - - def malformedInputAction(self) -> CodingErrorAction: - return self._malformed_input_action - - def unmappableCharacterAction(self) -> CodingErrorAction: - return self._unmappable_action - - def onMalformedInput(self, action: CodingErrorAction) -> "CharsetDecoder": - self._malformed_input_action = action - return self - - def onUnmappableCharacter(self, action: CodingErrorAction) -> "CharsetDecoder": - self._unmappable_action = action - return self - - def reset(self) -> "CharsetDecoder": - self.implReset() - return self - - def toString(self) -> str: - return f"CharsetDecoder@{hex(id(self))}[charset={self._charset.name()}]" - - def equals(self, other) -> bool: - if not isinstance(other, CharsetDecoder): - return False - return self._charset == other._charset - - def hashCode(self) -> int: - return hash(self._charset.name()) & 0x7FFFFFFF - - -# ============================================================ -# StandardCharsets -# ============================================================ - -class StandardCharsets(Stub): - """ - java.nio.charset.StandardCharsets - Constant definitions for the standard charsets. - """ - US_ASCII: Charset = Charset.US_ASCII - ISO_8859_1: Charset = Charset.ISO_8859_1 - UTF_8: Charset = Charset.UTF_8 - UTF_16: Charset = Charset.UTF_16 - UTF_16BE: Charset = Charset.UTF_16BE - UTF_16LE: Charset = Charset.UTF_16LE \ No newline at end of file diff --git a/android_pyapi/java/nio_file.py b/android_pyapi/java/nio_file.py deleted file mode 100644 index 76a1131..0000000 --- a/android_pyapi/java/nio_file.py +++ /dev/null @@ -1,703 +0,0 @@ -""" -Java NIO File standard library APIs implemented in Python. -java.nio.file package - Path, Files, FileSystem, FileVisitor, etc. -""" - -from .._core import Stub -from .io import IOException -from .lang import RuntimeException, Comparable -import os as _os -import shutil as _shutil -import stat as _stat -from pathlib import Path as _PathLibPath -import tempfile as _tempfile - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "Path", "Paths", "Files", "FileSystem", "FileSystems", - "FileStore", "FileVisitResult", "FileVisitor", "SimpleFileVisitor", - "DirectoryStream", "DirectoryStreamFilter", - "PathMatcher", "WatchService", "WatchKey", "WatchEvent", "Watchable", - "CopyOption", "StandardCopyOption", "LinkOption", "OpenOption", - "StandardOpenOption", - "FileAlreadyExistsException", "DirectoryNotEmptyException", - "NoSuchFileException", "FileSystemLoopException", - "FileSystemNotFoundException", "ProviderMismatchException", - "ProviderNotFoundException", "NotDirectoryException", "NotLinkException", - "AtomicMoveNotSupportedException", "AccessDeniedException", - "AccessMode", -] - - -# ============================================================ -# Exception classes -# ============================================================ - -class FileAlreadyExistsException(IOException): - pass - -class DirectoryNotEmptyException(IOException): - pass - -class NoSuchFileException(IOException): - def __init__(self, file=None, other=None, reason=None): - msg = str(file) if file else "" - super().__init__(msg) - -class FileSystemLoopException(IOException): - pass - -class FileSystemNotFoundException(IOException): - pass - -class ProviderMismatchException(IllegalArgumentException): - pass - -class ProviderNotFoundException(IllegalArgumentException): - pass - -class NotDirectoryException(IOException): - pass - -class NotLinkException(IOException): - pass - -class AtomicMoveNotSupportedException(IOException): - pass - -class AccessDeniedException(IOException): - pass - - -# ============================================================ -# Enums / Constants -# ============================================================ - -class FileVisitResult: - CONTINUE = 0 - TERMINATE = 1 - SKIP_SUBTREE = 2 - SKIP_SIBLINGS = 3 - -class StandardCopyOption: - REPLACE_EXISTING = 0 - COPY_ATTRIBUTES = 1 - ATOMIC_MOVE = 2 - -class StandardOpenOption: - READ = 0 - WRITE = 1 - APPEND = 2 - TRUNCATE_EXISTING = 3 - CREATE = 4 - CREATE_NEW = 5 - DELETE_ON_CLOSE = 6 - SPARSE = 7 - SYNC = 8 - DSYNC = 9 - -class LinkOption: - NOFOLLOW_LINKS = 0 - -class CopyOption(Stub): - pass - -class OpenOption(Stub): - pass - -class AccessMode: - READ = 0 - WRITE = 1 - EXECUTE = 2 - - -# ============================================================ -# Path -# ============================================================ - -class Path(Comparable): - def __init__(self, path_str, *more): - super().__init__() - if more: - path_str = _os.path.join(path_str, *more) - self._path = str(path_str) - self._pypath = _PathLibPath(self._path) - - def getFileName(self): - return Path(self._pypath.name) - - def getParent(self): - parent = self._pypath.parent - if parent == self._pypath: - return None - return Path(str(parent)) - - def getRoot(self): - root = self._pypath.anchor - return Path(root) if root else None - - def toAbsolutePath(self): - return Path(str(self._pypath.absolute())) - - def toRealPath(self): - return Path(str(self._pypath.resolve())) - - def getName(self, index): - parts = self._pypath.parts - if self.getRoot(): - parts = parts[1:] - if index < 0 or index >= len(parts): - raise IllegalArgumentException("Invalid index") - return Path(parts[index]) - - def getNameCount(self): - parts = self._pypath.parts - if self.getRoot(): - parts = parts[1:] - return len(parts) - - def subpath(self, beginIndex, endIndex): - parts = self._pypath.parts - if self.getRoot(): - parts = parts[1:] - return Path(_os.path.join(*parts[beginIndex:endIndex])) - - def startsWith(self, other): - if isinstance(other, Path): - return str(self._pypath).startswith(str(other._pypath)) - return str(self._pypath).startswith(str(other)) - - def endsWith(self, other): - if isinstance(other, Path): - return str(self._pypath).endswith(str(other._pypath)) - return str(self._pypath).endswith(str(other)) - - def normalize(self): - return Path(_os.path.normpath(self._path)) - - def resolve(self, other): - if isinstance(other, Path): - other_path = str(other) - else: - other_path = str(other) - if _os.path.isabs(other_path): - return Path(other_path) - return Path(_os.path.join(self._path, other_path)) - - def resolveSibling(self, other): - parent = self.getParent() - if parent: - return parent.resolve(other) - return Path(other) - - def relativize(self, other): - if isinstance(other, Path): - other_str = str(other._pypath) - else: - other_str = str(other) - try: - rel = _os.path.relpath(other_str, self._path) - return Path(rel) - except ValueError: - return other - - def compareTo(self, other): - if isinstance(other, Path): - return (self._path > other._path) - (self._path < other._path) - return -1 - - def toString(self): - return self._path - - def toFile(self): - from ..io import File - return File(self._path) - - def isAbsolute(self): - return _os.path.isabs(self._path) - - def getFileSystem(self): - return FileSystems.getDefault() - - def iterator(self): - parts = self._pypath.parts - if self.getRoot(): - parts = parts[1:] - return iter([Path(p) for p in parts]) - - def register(self, watcher, events, *modifiers): - raise AndroidOnlyError("WatchService nio") - - def __str__(self): - return self._path - - def __eq__(self, other): - if isinstance(other, Path): - return self._path == other._path - return NotImplemented - - def __hash__(self): - return hash(self._path) - - -# ============================================================ -# Paths -# ============================================================ - -class Paths(Stub): - @staticmethod - def get(first, *more): - parts = [str(first)] - parts.extend(str(m) for m in more) - return Path(_os.path.join(*parts)) - - -# ============================================================ -# FileSystem -# ============================================================ - -class FileSystem(Stub): - def __init__(self): - super().__init__() - self._separator = _os.sep - self._open = True - - def getPath(self, first, *more): - return Paths.get(first, *more) - - def getSeparator(self): - return self._separator - - def getRootDirectories(self): - if _os.name == "nt": - import string - return [Path(d + ":\\") for d in string.ascii_uppercase if _os.path.exists(d + ":\\")] - return [Path("/")] - - def getUserPrincipalLookupService(self): - raise AndroidOnlyError("UserPrincipalLookupService") - - def newWatchService(self): - raise AndroidOnlyError("WatchService") - - def getFileStores(self): - return [FileStore("/")] - - def supportedFileAttributeViews(self): - return ["basic", "owner", "posix"] - - def close(self): - self._open = False - - def isOpen(self): - return self._open - - def isReadOnly(self): - return False - - -class FileSystems(Stub): - _default = None - - @staticmethod - def getDefault(): - if FileSystems._default is None: - FileSystems._default = FileSystem() - return FileSystems._default - - -# ============================================================ -# FileStore -# ============================================================ - -class FileStore(Stub): - def __init__(self, path): - super().__init__() - self._path = path - - def name(self): - return _os.path.basename(self._path) or "/" - - def type(self): - return "ext4" - - def isReadOnly(self): - return False - - def getTotalSpace(self): - try: - st = _os.statvfs(self._path) - return st.f_frsize * st.f_blocks - except OSError: - return 0 - - def getUsableSpace(self): - try: - st = _os.statvfs(self._path) - return st.f_frsize * st.f_bavail - except OSError: - return 0 - - def getUnallocatedSpace(self): - try: - st = _os.statvfs(self._path) - return st.f_frsize * st.f_bfree - except OSError: - return 0 - - def toString(self): - return f"{self.name()} ({self.type()})" - - -# ============================================================ -# FileVisitor / SimpleFileVisitor -# ============================================================ - -class FileVisitor(Stub): - def preVisitDirectory(self, dir, attrs): - return FileVisitResult.CONTINUE - - def postVisitDirectory(self, dir, exc): - return FileVisitResult.CONTINUE - - def visitFile(self, file, attrs): - return FileVisitResult.CONTINUE - - def visitFileFailed(self, file, exc): - return FileVisitResult.CONTINUE - - -class SimpleFileVisitor(FileVisitor): - pass - - -# ============================================================ -# DirectoryStream / Filter -# ============================================================ - -class DirectoryStream(Stub): - def __init__(self, path, filter=None): - super().__init__() - self._path = path - self._filter = filter - - def __iter__(self): - try: - entries = _os.listdir(str(self._path)) - for entry in entries: - p = Path(self._path, entry) - if self._filter is None or self._filter.accept(p): - yield p - except OSError: - raise IOException(f"Failed to list directory: {self._path}") - - def close(self): - pass - - -class DirectoryStreamFilter(Stub): - def accept(self, entry): - raise NotImplementedError - - -# ============================================================ -# PathMatcher -# ============================================================ - -class PathMatcher(Stub): - def __init__(self, pattern): - super().__init__() - self._pattern = pattern - - def matches(self, path): - import fnmatch - return fnmatch.fnmatch(str(path), self._pattern) - - -# ============================================================ -# WatchService / WatchKey / WatchEvent / Watchable (stubs) -# ============================================================ - -class Watchable(Stub): - def register(self, watcher, events, *modifiers): - raise AndroidOnlyError("WatchService") - -class WatchService(Stub): - def close(self): - pass - def poll(self): - return None - def take(self): - raise AndroidOnlyError("WatchService.take") - def poll(self, timeout, unit): - return None - -class WatchKey(Stub): - def isValid(self): - return True - def pollEvents(self): - return [] - def reset(self): - return True - def cancel(self): - pass - def watchable(self): - return None - -class WatchEvent(Stub): - class Kind(Stub): - def name(self): - return "" - def type(self): - return None - - def kind(self): - return None - def count(self): - return 0 - def context(self): - return None - - -# ============================================================ -# Files -# ============================================================ - -class Files(Stub): - @staticmethod - def copy(source, target, *options): - src = str(source) - dst = str(target) - replace = any(isinstance(o, StandardCopyOption) and o == StandardCopyOption.REPLACE_EXISTING for o in options) - try: - if replace and _os.path.exists(dst): - _os.remove(dst) - if _os.path.isdir(src): - _shutil.copytree(src, dst) - else: - _shutil.copy2(src, dst) - except OSError as e: - raise IOException(str(e)) - - @staticmethod - def move(source, target, *options): - src = str(source) - dst = str(target) - replace = any(isinstance(o, StandardCopyOption) and o == StandardCopyOption.REPLACE_EXISTING for o in options) - try: - if replace and _os.path.exists(dst): - _os.remove(dst) - _shutil.move(src, dst) - except OSError as e: - raise IOException(str(e)) - - @staticmethod - def delete(path): - try: - p = str(path) - if _os.path.isdir(p): - _os.rmdir(p) - else: - _os.remove(p) - except OSError as e: - raise NoSuchFileException(str(path)) - - @staticmethod - def deleteIfExists(path): - try: - Files.delete(path) - return True - except NoSuchFileException: - return False - - @staticmethod - def createDirectories(path): - _os.makedirs(str(path), exist_ok=True) - return path - - @staticmethod - def createDirectory(path, attrs=None): - _os.makedirs(str(path), exist_ok=True) - return path - - @staticmethod - def createFile(path, attrs=None): - p = str(path) - if _os.path.exists(p): - raise FileAlreadyExistsException(str(path)) - open(p, "a").close() - return path - - @staticmethod - def createTempFile(prefix, suffix, dir=None): - f = _tempfile.NamedTemporaryFile(prefix=prefix, suffix=suffix, - dir=str(dir) if dir else None, delete=False) - p = f.name - f.close() - return Path(p) - - @staticmethod - def createTempDirectory(prefix, attrs=None): - d = _tempfile.mkdtemp(prefix=prefix) - return Path(d) - - @staticmethod - def exists(path, *options): - return _os.path.exists(str(path)) - - @staticmethod - def notExists(path, *options): - return not _os.path.exists(str(path)) - - @staticmethod - def isReadable(path): - return _os.access(str(path), _os.R_OK) - - @staticmethod - def isWritable(path): - return _os.access(str(path), _os.W_OK) - - @staticmethod - def isExecutable(path): - return _os.access(str(path), _os.X_OK) - - @staticmethod - def isDirectory(path, *options): - return _os.path.isdir(str(path)) - - @staticmethod - def isRegularFile(path, *options): - return _os.path.isfile(str(path)) - - @staticmethod - def isSymbolicLink(path): - return _os.path.islink(str(path)) - - @staticmethod - def isHidden(path): - return _os.path.basename(str(path)).startswith(".") - - @staticmethod - def getLastModifiedTime(path, *options): - return int(_os.path.getmtime(str(path)) * 1000) - - @staticmethod - def setLastModifiedTime(path, time): - _os.utime(str(path), (time / 1000, time / 1000)) - - @staticmethod - def getAttribute(path, attribute, *options): - return None - - @staticmethod - def setAttribute(path, attribute, value, *options): - pass - - @staticmethod - def getPosixFilePermissions(path, *options): - return [] - - @staticmethod - def setPosixFilePermissions(path, perms, *options): - pass - - @staticmethod - def newInputStream(path, *options): - from ..io import FileInputStream - return FileInputStream(str(path)) - - @staticmethod - def newOutputStream(path, *options): - from ..io import FileOutputStream - append = any(isinstance(o, StandardOpenOption) and o == StandardOpenOption.APPEND for o in options) - return FileOutputStream(str(path), append) - - @staticmethod - def newBufferedReader(path, charset=None): - from ..io import BufferedReader, InputStreamReader, FileInputStream - inp = FileInputStream(str(path)) - reader = InputStreamReader(inp, charset or "utf-8") - return BufferedReader(reader) - - @staticmethod - def newBufferedWriter(path, charset=None, *options): - from ..io import BufferedWriter, OutputStreamWriter, FileOutputStream - append = any(isinstance(o, StandardOpenOption) and o == StandardOpenOption.APPEND for o in options) - out = FileOutputStream(str(path), append) - writer = OutputStreamWriter(out, charset or "utf-8") - return BufferedWriter(writer) - - @staticmethod - def readAllBytes(path): - with open(str(path), "rb") as f: - return f.read() - - @staticmethod - def readAllLines(path, charset=None): - with open(str(path), "r", encoding=charset or "utf-8") as f: - return f.read().splitlines() - - @staticmethod - def readString(path, charset=None): - with open(str(path), "r", encoding=charset or "utf-8") as f: - return f.read() - - @staticmethod - def write(path, content, *options): - mode = "wb" if isinstance(content, bytes) else "w" - open_mode = mode - if any(isinstance(o, StandardOpenOption) and o == StandardOpenOption.APPEND for o in options): - open_mode = "ab" if mode == "wb" else "a" - with open(str(path), open_mode) as f: - f.write(content) - - @staticmethod - def writeString(path, content, *options): - open_mode = "w" - if any(isinstance(o, StandardOpenOption) and o == StandardOpenOption.APPEND for o in options): - open_mode = "a" - with open(str(path), open_mode, encoding="utf-8") as f: - f.write(content) - - @staticmethod - def list(path): - try: - return [Path(_os.path.join(str(path), entry)) for entry in _os.listdir(str(path))] - except OSError: - raise IOException(f"Failed to list directory: {path}") - - @staticmethod - def walk(path, maxDepth=None): - for root, dirs, files in _os.walk(str(path)): - yield Path(root) - - @staticmethod - def find(start, maxDepth, matcher, *options): - for root, dirs, files in _os.walk(str(start)): - for f in files: - p = Path(_os.path.join(root, f)) - if matcher.matches(p): - yield p - - @staticmethod - def lines(path, charset=None): - with open(str(path), "r", encoding=charset or "utf-8") as f: - for line in f: - yield line.rstrip("\n") - - @staticmethod - def getFileStore(path): - return FileStore(str(path)) - - @staticmethod - def probeContentType(path): - return "application/octet-stream" - - @staticmethod - def getOwner(path, *options): - raise AndroidOnlyError("getOwner") - - @staticmethod - def setOwner(path, owner, *options): - raise AndroidOnlyError("setOwner") \ No newline at end of file diff --git a/android_pyapi/java/rmi.py b/android_pyapi/java/rmi.py deleted file mode 100644 index 78ea658..0000000 --- a/android_pyapi/java/rmi.py +++ /dev/null @@ -1,776 +0,0 @@ -""" -Java java.rmi standard library APIs implemented in Python. -java.rmi package - Remote, Registry, Activation, etc. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -from .lang import RuntimeException, Exception as _JavaException -import typing as _typing - -__all__ = [ - "Remote", "RemoteException", "RMISecurityManager", "RMISecurityException", - "MarshalledObject", "Naming", "NamingException", - "NotBoundException", "AlreadyBoundException", - "ConnectException", "ConnectIOException", "ServerException", - "ServerError", "ServerRuntimeException", "StubNotFoundException", - "UnexpectedException", "UnmarshalException", "UnknownHostException", - "NoSuchObjectException", "AccessException", "MarshalException", - "ExportException", "SocketSecurityException", - "Registry", "RegistryHandler", - "ActivationException", "UnknownObjectException", - "Activatable", "ActivationDesc", "ActivationGroup", - "ActivationGroupDesc", "ActivationGroupID", "ActivationID", - "ActivationMonitor", "ActivationSystem", "Activator", - "RMIClassLoader", "RMISocketFactory", "RMIFailureHandler", - "LogStream", "RemoteCall", "RemoteRef", "ServerRef", "Operation", - "RemoteObject", "RemoteServer", "UnicastRemoteObject", "Unreferenced", -] - - -# ============================================================ -# Remote interface -# ============================================================ - -class Remote(Stub): - """java.rmi.Remote""" - pass - - -# ============================================================ -# Exception hierarchy -# ============================================================ - -class RemoteException(_JavaException): - """java.rmi.RemoteException""" - def __init__(self, message=None, cause=None): - msg = message or "" - super().__init__(msg) - self._detail = message - self._cause = cause - - def getMessage(self): - return self._detail or "" - - def getCause(self): - return self._cause - - -class NamingException(RemoteException): - """java.rmi.NamingException""" - pass - - -class NotBoundException(RemoteException): - """java.rmi.NotBoundException""" - pass - - -class AlreadyBoundException(RemoteException): - """java.rmi.AlreadyBoundException""" - pass - - -class ConnectException(RemoteException): - """java.rmi.ConnectException""" - pass - - -class ConnectIOException(RemoteException): - """java.rmi.ConnectIOException""" - pass - - -class ServerException(RemoteException): - """java.rmi.ServerException""" - pass - - -class ServerError(RemoteException): - """java.rmi.ServerError""" - pass - - -class ServerRuntimeException(RemoteException): - """java.rmi.ServerRuntimeException""" - pass - - -class StubNotFoundException(RemoteException): - """java.rmi.StubNotFoundException""" - pass - - -class UnexpectedException(RemoteException): - """java.rmi.UnexpectedException""" - pass - - -class UnmarshalException(RemoteException): - """java.rmi.UnmarshalException""" - pass - - -class UnknownHostException(RemoteException): - """java.rmi.UnknownHostException""" - pass - - -class NoSuchObjectException(RemoteException): - """java.rmi.NoSuchObjectException""" - pass - - -class AccessException(RemoteException): - """java.rmi.AccessException""" - pass - - -class MarshalException(RemoteException): - """java.rmi.MarshalException""" - pass - - -class ExportException(RemoteException): - """java.rmi.ExportException""" - pass - - -class SocketSecurityException(ExportException): - """java.rmi.SocketSecurityException""" - pass - - -# java.lang.SecurityException (桌面模拟) -class SecurityException(RuntimeException): - def __init__(self, message=""): - super().__init__(message) - - -class RMISecurityException(SecurityException): - """java.rmi.RMISecurityException""" - pass - - -# ============================================================ -# RMISecurityManager -# ============================================================ - -class RMISecurityManager(Stub): - """java.rmi.RMISecurityManager""" - def checkConnect(self, host, port): - pass - - def checkAccept(self, host, port): - pass - - def checkListen(self, port): - pass - - -# ============================================================ -# MarshalledObject -# ============================================================ - -class MarshalledObject(Stub): - """java.rmi.MarshalledObject""" - def __init__(self, obj): - super().__init__() - self._obj = obj - import pickle - self._bytes = pickle.dumps(obj) - - def get(self): - import pickle - try: - return pickle.loads(self._bytes) - except Exception: - return self._obj - - def hashCode(self): - return hash(self._bytes) - - def equals(self, obj): - if not isinstance(obj, MarshalledObject): - return False - return self._bytes == obj._bytes - - -# ============================================================ -# Naming -# ============================================================ - -class Naming(Stub): - """java.rmi.Naming""" - _registry = {} - - @staticmethod - def lookup(name): - if name in Naming._registry: - return Naming._registry[name] - raise NotBoundException(name) - - @staticmethod - def bind(name, obj): - if name in Naming._registry: - raise AlreadyBoundException(name) - Naming._registry[name] = obj - - @staticmethod - def unbind(name): - if name not in Naming._registry: - raise NotBoundException(name) - del Naming._registry[name] - - @staticmethod - def rebind(name, obj): - Naming._registry[name] = obj - - @staticmethod - def list(name): - return list(Naming._registry.keys()) - - -# ============================================================ -# Registry -# ============================================================ - -class Registry(Stub): - """java.rmi.registry.Registry""" - def lookup(self, name): - raise NotBoundException(name) - - def bind(self, name, obj): - raise AlreadyBoundException(name) - - def unbind(self, name): - raise NotBoundException(name) - - def rebind(self, name, obj): - pass - - def list(self): - return [] - - REGISTRY_PORT = 1099 - - -class RegistryHandler(Stub): - """java.rmi.registry.RegistryHandler""" - def registryStub(self, host, port): - return Registry() - - def registryImpl(self, port): - return Registry() - - -# ============================================================ -# Activation Exceptions -# ============================================================ - -class ActivationException(RemoteException): - """java.rmi.activation.ActivationException""" - pass - - -class UnknownObjectException(ActivationException): - """java.rmi.activation.UnknownObjectException""" - pass - - -# ============================================================ -# ActivationID -# ============================================================ - -class ActivationID(Stub): - """java.rmi.activation.ActivationID""" - def __init__(self, activator=None): - super().__init__() - self._activator = activator - - def activate(self, force): - if self._activator: - return self._activator.activate(self, force) - return None - - def hashCode(self): - return hash(id(self)) - - def equals(self, obj): - return self is obj - - -# ============================================================ -# ActivationDesc -# ============================================================ - -class ActivationDesc(Stub): - """java.rmi.activation.ActivationDesc""" - def __init__(self, class_name=None, location=None, marshalled_data=None, - group_id=None, restart_mode=False): - super().__init__() - self._class_name = class_name - self._location = location - self._marshalled_data = marshalled_data - self._group_id = group_id - self._restart_mode = restart_mode - - def getClassName(self): - return self._class_name - - def getLocation(self): - return self._location - - def getData(self): - return self._marshalled_data - - def getGroupID(self): - return self._group_id - - def getRestartMode(self): - return self._restart_mode - - -# ============================================================ -# Activatable -# ============================================================ - -class Activatable(Remote): - """java.rmi.activation.Activatable""" - def __init__(self, activation_desc, port, obj_id=None, server_port=0, - csf=None, ssf=None): - super().__init__() - self._activation_desc = activation_desc - self._port = port - self._group_id = ActivationGroupID() - self._id = ActivationID() - - @staticmethod - def register(desc): - return ActivationID() - - @staticmethod - def inactive(id_): - return True - - @staticmethod - def unregister(id_): - pass - - @staticmethod - def exportObject(obj, desc, port): - return Remote() - - def getID(self): - return self._id - - -# ============================================================ -# ActivationGroup / ActivationGroupDesc / ActivationGroupID -# ============================================================ - -class ActivationGroupID(Stub): - """java.rmi.activation.ActivationGroupID""" - def __init__(self, activation_system=None): - super().__init__() - self._activation_system = activation_system - - def getSystem(self): - return self._activation_system - - def hashCode(self): - return hash(id(self)) - - -class ActivationGroupDesc(Stub): - """java.rmi.activation.ActivationGroupDesc""" - class CommandEnvironment(Stub): - def __init__(self, cmd=None, props=None): - super().__init__() - self._command = cmd - self._properties = props or [] - - def getCommandPath(self): - return self._command - - def getCommandProperties(self): - return self._properties - - def __init__(self, props=None, env=None, class_name=None, - location=None, info=None): - super().__init__() - self._props = props or {} - self._env = env or ActivationGroupDesc.CommandEnvironment() - self._class_name = class_name - self._location = location - self._info = info - - def getPropertyOverrides(self): - return self._props - - def getCommandEnvironment(self): - return self._env - - def getClassName(self): - return self._class_name - - def getLocation(self): - return self._location - - def getData(self): - return self._info - - -class ActivationGroup(Activatable): - """java.rmi.activation.ActivationGroup""" - def __init__(self, group_id, desc, incarnation): - super().__init__(None, 0) - self._group_id = group_id - self._desc = desc - self._incarnation = incarnation - - @staticmethod - def createGroup(id_, desc, incarnation): - return ActivationGroup(id_, desc, incarnation) - - @staticmethod - def currentGroupID(): - return ActivationGroupID() - - @staticmethod - def setGroup(group): - pass - - @staticmethod - def activeGroup(id_, group, incarnation): - return True - - @staticmethod - def inactiveGroup(group_id): - pass - - def newInstance(self, desc): - return None - - def activeObject(self, id_, obj): - pass - - def inactiveObject(self, id_): - pass - - -# ============================================================ -# ActivationMonitor / ActivationSystem / Activator -# ============================================================ - -class ActivationMonitor(Stub): - """java.rmi.activation.ActivationMonitor""" - def inactiveObject(self, id_): - pass - - def activeObject(self, id_, obj): - pass - - def inactiveGroup(self, group_id, incarnation): - pass - - -class ActivationSystem(Stub): - """java.rmi.activation.ActivationSystem""" - def registerActivationDesc(self, desc): - return ActivationID() - - def unregisterActivationDesc(self, id_): - pass - - def shutdown(self): - pass - - def setActivationMonitor(self, monitor): - pass - - def getActivationMonitor(self): - return ActivationMonitor() - - def activeGroup(self, id_, group, incarnation): - return ActivationMonitor() - - def registerGroup(self, desc): - return ActivationGroupID() - - def unregisterGroup(self, group_id): - pass - - SYSTEM_PORT = 1098 - - -class Activator(Stub): - """java.rmi.activation.Activator""" - def activate(self, id_, force): - return MarshalledObject(None) - - -# ============================================================ -# RMIClassLoader -# ============================================================ - -class RMIClassLoader(Stub): - """java.rmi.server.RMIClassLoader""" - @staticmethod - def loadClass(codebase, name, default_loader=None): - import importlib - try: - return importlib.import_module(name) - except ImportError: - return None - - @staticmethod - def getClassAnnotation(cls): - return "" - - @staticmethod - def getClassLoader(codebase): - return None - - @staticmethod - def getDefaultProviderInstance(): - return None - - @staticmethod - def loadProxyClass(codebase, interfaces, default_loader): - return None - - -# ============================================================ -# RMISocketFactory / RMIFailureHandler -# ============================================================ - -class RMISocketFactory(Stub): - """java.rmi.server.RMISocketFactory""" - _factory = None - _handler = None - - @staticmethod - def getDefaultSocketFactory(): - if RMISocketFactory._factory is None: - RMISocketFactory._factory = RMISocketFactory() - return RMISocketFactory._factory - - @staticmethod - def setSocketFactory(factory): - RMISocketFactory._factory = factory - - @staticmethod - def getFailureHandler(): - return RMISocketFactory._handler - - @staticmethod - def setFailureHandler(handler): - RMISocketFactory._handler = handler - - def createSocket(self, host, port): - import socket - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect((host, port)) - return s - - def getDefaultPort(self): - return 0 - - -class RMIFailureHandler(Stub): - """java.rmi.server.RMIFailureHandler""" - def failure(self, ex): - return False - - -# ============================================================ -# LogStream -# ============================================================ - -class LogStream(Stub): - """java.rmi.server.LogStream""" - def __init__(self, name): - super().__init__() - self._name = name - self._stream = None - - @staticmethod - def log(name): - return LogStream(name) - - @staticmethod - def getDefaultStream(): - return None - - @staticmethod - def setDefaultStream(stream): - pass - - def getOutputStream(self): - return self._stream - - def setOutputStream(self, stream): - self._stream = stream - - def toString(self): - return self._name - - SILENT = 0 - BRIEF = 10 - VERBOSE = 20 - - -# ============================================================ -# RemoteCall / RemoteRef / ServerRef / Operation -# ============================================================ - -class RemoteCall(Stub): - """java.rmi.server.RemoteCall""" - def getInputStream(self): - return None - - def getOutputStream(self): - return None - - def getResultStream(self, success): - return None - - def executeCall(self): - pass - - def done(self): - pass - - def releaseInputStream(self): - pass - - def releaseOutputStream(self): - pass - - -class RemoteRef(Stub): - """java.rmi.server.RemoteRef""" - def invoke(self, call): - return None - - def remoteEquals(self, obj): - return False - - def remoteHashCode(self): - return 0 - - def remoteToString(self): - return "" - - def getRefClass(self, out): - return "" - - def newCall(self, obj, operations, opnum, hash_): - return RemoteCall() - - def invoke(self, call): - pass - - def done(self, call): - pass - - packagePrefix = "sun.rmi.server" - - -class ServerRef(RemoteRef): - """java.rmi.server.ServerRef""" - def exportObject(self, obj, data): - return RemoteStub() - - def getClientHost(self): - return "localhost" - - -class RemoteStub(Stub): - """java.rmi.server.RemoteStub""" - def __init__(self, ref=None): - super().__init__() - self._ref = ref - - -class Operation(Stub): - """java.rmi.server.Operation""" - def __init__(self, name): - super().__init__() - self._name = name - - def getOperation(self): - return self._name - - def toString(self): - return self._name - - -# ============================================================ -# RemoteObject / RemoteServer / UnicastRemoteObject -# ============================================================ - -class RemoteObject(Stub): - """java.rmi.server.RemoteObject""" - def __init__(self, ref=None): - super().__init__() - self._ref = ref - - def getRef(self): - return self._ref - - def toString(self): - return "RemoteObject" - - def hashCode(self): - return super().hashCode() - - def equals(self, obj): - return super().equals(obj) - - -class RemoteServer(RemoteObject): - """java.rmi.server.RemoteServer""" - def __init__(self, ref=None): - super().__init__(ref) - - @staticmethod - def getClientHost(): - return "localhost" - - @staticmethod - def setLog(output_stream): - pass - - @staticmethod - def getLog(): - return None - - -class UnicastRemoteObject(RemoteServer): - """java.rmi.server.UnicastRemoteObject""" - def __init__(self, port=0, csf=None, ssf=None): - super().__init__() - self._port = port - self._csf = csf - self._ssf = ssf - self._exported = False - - @staticmethod - def exportObject(obj, port=0, csf=None, ssf=None): - return RemoteStub() - - @staticmethod - def unexportObject(obj, force): - return True - - def clone(self): - return UnicastRemoteObject(self._port) - - -class Unreferenced(Stub): - """java.rmi.server.Unreferenced""" - def unreferenced(self): - pass \ No newline at end of file diff --git a/android_pyapi/java/security.py b/android_pyapi/java/security.py deleted file mode 100644 index 2a7e131..0000000 --- a/android_pyapi/java/security.py +++ /dev/null @@ -1,1145 +0,0 @@ -""" -Java security standard library APIs implemented in Python. -java.security package - MessageDigest, SecureRandom, KeyStore, Signature, etc. -""" - -from .._core import Stub, AndroidOnlyError -from .lang import RuntimeException -import hashlib as _hashlib -import os as _os - -GeneralSecurityException = type("GeneralSecurityException", (RuntimeException,), {}) -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) -DigestException = type("DigestException", (GeneralSecurityException,), {}) -InvalidAlgorithmParameterException = type("InvalidAlgorithmParameterException", (GeneralSecurityException,), {}) -InvalidKeyException = type("InvalidKeyException", (GeneralSecurityException,), {}) -InvalidKeySpecException = type("InvalidKeySpecException", (GeneralSecurityException,), {}) -InvalidParameterException = type("InvalidParameterException", (IllegalArgumentException,), {}) -KeyException = type("KeyException", (GeneralSecurityException,), {}) -KeyManagementException = type("KeyManagementException", (KeyException,), {}) -KeyStoreException = type("KeyStoreException", (GeneralSecurityException,), {}) -NoSuchAlgorithmException = type("NoSuchAlgorithmException", (GeneralSecurityException,), {}) -NoSuchProviderException = type("NoSuchProviderException", (GeneralSecurityException,), {}) -ProviderException = type("ProviderException", (RuntimeException,), {}) -SignatureException = type("SignatureException", (GeneralSecurityException,), {}) - -__all__ = [ - "MessageDigest", "MessageDigestSpi", - "SecureRandom", "SecureRandomSpi", - "KeyStore", "KeyStoreSpi", - "Key", "PrivateKey", "PublicKey", "KeyPair", "KeyPairGenerator", - "KeyPairGeneratorSpi", "KeyFactory", "KeyFactorySpi", - "KeySpec", "SecretKeySpec", - "RSAPublicKeySpec", "RSAPrivateKeySpec", - "PKCS8EncodedKeySpec", "X509EncodedKeySpec", - "AlgorithmParameters", "AlgorithmParameterGenerator", - "AlgorithmParameterGeneratorSpi", "AlgorithmParametersSpi", - "Provider", "Security", - "DigestInputStream", "DigestOutputStream", - "Signature", "SignatureSpi", - "CodeSigner", "CodeSource", "Policy", "Permission", - "AllPermission", "BasicPermission", "PermissionCollection", - "Principal", - "CertPath", "CertPathBuilder", "CertPathBuilderSpi", - "CertPathValidator", "CertPathValidatorSpi", - "CertificateFactory", "CertificateFactorySpi", - "CertStore", "CertStoreSpi", - "CRL", "X509CRL", "X509Certificate", "X509Extension", - "Guard", "GuardedObject", "Identity", "IdentityScope", "Signer", - "Timestamp", "UnresolvedPermission", - "GeneralSecurityException", "DigestException", - "InvalidAlgorithmParameterException", "InvalidKeyException", - "InvalidKeySpecException", "InvalidParameterException", - "KeyException", "KeyManagementException", "KeyStoreException", - "NoSuchAlgorithmException", "NoSuchProviderException", - "ProviderException", "SignatureException", -] - - -# ============================================================ -# Key interfaces -# ============================================================ - -class Key(Stub): - def getAlgorithm(self): - return "" - - def getFormat(self): - return "" - - def getEncoded(self): - return b"" - - -class PrivateKey(Key): - pass - - -class PublicKey(Key): - pass - - -# ============================================================ -# KeySpec / SecretKeySpec / RSA specs / EncodedKeySpecs -# ============================================================ - -class KeySpec(Stub): - pass - - -class SecretKeySpec(KeySpec, Key): - def __init__(self, key_bytes, algorithm): - super().__init__() - self._key = key_bytes - self._algorithm = algorithm - - def getAlgorithm(self): - return self._algorithm - - def getFormat(self): - return "RAW" - - def getEncoded(self): - return self._key - - def hashCode(self): - return hash(self._key) ^ hash(self._algorithm) - - -class RSAPublicKeySpec(KeySpec): - def __init__(self, modulus, public_exponent): - super().__init__() - self._modulus = modulus - self._public_exponent = public_exponent - - def getModulus(self): - return self._modulus - - def getPublicExponent(self): - return self._public_exponent - - -class RSAPrivateKeySpec(KeySpec): - def __init__(self, modulus, private_exponent): - super().__init__() - self._modulus = modulus - self._private_exponent = private_exponent - - def getModulus(self): - return self._modulus - - def getPrivateExponent(self): - return self._private_exponent - - -class PKCS8EncodedKeySpec(KeySpec): - def __init__(self, encoded_key): - super().__init__() - self._encoded_key = encoded_key - - def getEncoded(self): - return self._encoded_key - - def getFormat(self): - return "PKCS#8" - - -class X509EncodedKeySpec(KeySpec): - def __init__(self, encoded_key): - super().__init__() - self._encoded_key = encoded_key - - def getEncoded(self): - return self._encoded_key - - def getFormat(self): - return "X.509" - - -# ============================================================ -# KeyPair -# ============================================================ - -class KeyPair(Stub): - def __init__(self, public_key, private_key): - super().__init__() - self._public = public_key - self._private = private_key - - def getPublic(self): - return self._public - - def getPrivate(self): - return self._private - - -# ============================================================ -# KeyPairGenerator -# ============================================================ - -class KeyPairGenerator(Stub): - def __init__(self, algorithm): - super().__init__() - self._algorithm = algorithm - self._keysize = 2048 - - @staticmethod - def getInstance(algorithm, provider=None): - return KeyPairGenerator(algorithm) - - def initialize(self, keysize_or_spec, random=None): - if isinstance(keysize_or_spec, int): - self._keysize = keysize_or_spec - - def generateKeyPair(self): - return KeyPair(PublicKey(), PrivateKey()) - - def getAlgorithm(self): - return self._algorithm - - def getProvider(self): - return Provider("SUN", 1.0, "Default provider") - - -# ============================================================ -# KeyFactory -# ============================================================ - -class KeyFactory(Stub): - def __init__(self, algorithm): - super().__init__() - self._algorithm = algorithm - - @staticmethod - def getInstance(algorithm, provider=None): - return KeyFactory(algorithm) - - def generatePublic(self, key_spec): - return PublicKey() - - def generatePrivate(self, key_spec): - return PrivateKey() - - def getKeySpec(self, key, key_spec_cls): - if key_spec_cls == X509EncodedKeySpec: - return X509EncodedKeySpec(key.getEncoded()) - raise InvalidKeySpecException("Unsupported key spec") - - def translateKey(self, key): - return key - - def getAlgorithm(self): - return self._algorithm - - -# ============================================================ -# KeyFactorySpi / KeyPairGeneratorSpi (stubs) -# ============================================================ - -class KeyFactorySpi(Stub): - pass - -class KeyPairGeneratorSpi(Stub): - pass - - -# ============================================================ -# MessageDigest -# ============================================================ - -class MessageDigest(Stub): - _MD_MAP = { - "MD5": "md5", - "SHA-1": "sha1", - "SHA": "sha1", - "SHA-224": "sha224", - "SHA-256": "sha256", - "SHA-384": "sha384", - "SHA-512": "sha512", - } - - def __init__(self, algorithm): - super().__init__() - self._algorithm = algorithm - algo_lower = algorithm.replace("-", "").lower() - if algo_lower in ("md5", "sha1", "sha224", "sha256", "sha384", "sha512"): - self._impl = _hashlib.new(algo_lower) - else: - self._impl = _hashlib.new("sha256") - - @staticmethod - def getInstance(algorithm, provider=None): - if algorithm not in MessageDigest._MD_MAP: - raise NoSuchAlgorithmException(f"{algorithm} not available") - return MessageDigest(algorithm) - - def update(self, data, offset=0, length=-1): - if length < 0: - self._impl.update(data) - else: - self._impl.update(data[offset:offset+length]) - - def digest(self, data=None): - if data is not None: - self._impl.update(data) - return self._impl.digest() - - def toString(self): - return f"MessageDigest: {self._algorithm}" - - def getAlgorithm(self): - return self._algorithm - - def getProvider(self): - return Provider("SUN", 1.0, "Default provider") - - def getDigestLength(self): - return self._impl.digest_size - - @staticmethod - def isEqual(digest_a, digest_b): - return digest_a == digest_b - - def clone(self): - md = MessageDigest(self._algorithm) - md._impl = self._impl.copy() - return md - - def reset(self): - self._impl = _hashlib.new(self._algorithm.replace("-", "").lower()) - - -class MessageDigestSpi(Stub): - pass - - -# ============================================================ -# SecureRandom -# ============================================================ - -class SecureRandom(Stub): - def __init__(self, seed=None): - super().__init__() - import random as _random - self._random = _random.Random() - if seed is not None: - self._random.seed(seed) - - def nextBytes(self, bytes_arr): - data = _os.urandom(len(bytes_arr)) - for i in range(len(bytes_arr)): - bytes_arr[i] = data[i] - - def nextInt(self, bound=None): - if bound is None: - return int.from_bytes(_os.urandom(4), "big") - return int.from_bytes(_os.urandom(4), "big") % bound - - def nextLong(self): - return int.from_bytes(_os.urandom(8), "big") - - def nextBoolean(self): - return int.from_bytes(_os.urandom(1), "big") % 2 == 0 - - def nextFloat(self): - return int.from_bytes(_os.urandom(4), "big") / 4294967296.0 - - def nextDouble(self): - return int.from_bytes(_os.urandom(8), "big") / 18446744073709551616.0 - - def nextGaussian(self): - import random as _random - return _random.gauss(0, 1) - - def generateSeed(self, num_bytes): - return _os.urandom(num_bytes) - - def getAlgorithm(self): - return "SHA1PRNG" - - def getProvider(self): - return Provider("SUN", 1.0, "Default provider") - - def setSeed(self, seed): - self._random.seed(seed) - - def getSeed(self, num_bytes): - return _os.urandom(num_bytes) - - -class SecureRandomSpi(Stub): - pass - - -# ============================================================ -# Provider / Security -# ============================================================ - -class Provider(Stub): - def __init__(self, name, version, info): - super().__init__() - self._name = name - self._version = version - self._info = info - self._properties = {} - - def getName(self): - return self._name - - def getVersion(self): - return self._version - - def getInfo(self): - return self._info - - def getProperty(self, key): - return self._properties.get(key) - - def setProperty(self, key, value): - self._properties[key] = value - - def toString(self): - return f"{self._name} v{self._version}" - - -class Security(Stub): - _providers = [Provider("SUN", 1.0, "SUN provider")] - - @staticmethod - def getProvider(name): - for p in Security._providers: - if p.getName() == name: - return p - return None - - @staticmethod - def getProviders(): - return Security._providers - - @staticmethod - def addProvider(provider): - Security._providers.append(provider) - return len(Security._providers) - 1 - - @staticmethod - def removeProvider(name): - for i, p in enumerate(Security._providers): - if p.getName() == name: - Security._providers.pop(i) - return - - @staticmethod - def getAlgorithms(service_name): - if service_name == "MessageDigest": - return list(MessageDigest._MD_MAP.keys()) - return [] - - -# ============================================================ -# DigestInputStream / DigestOutputStream -# ============================================================ - -class DigestInputStream(Stub): - def __init__(self, stream, digest): - super().__init__() - self._stream = stream - self._digest = digest - - def getMessageDigest(self): - return self._digest - - def setMessageDigest(self, digest): - self._digest = digest - - def read(self, b=None, off=0, length=0): - if b is None: - data = self._stream.read() - else: - data = self._stream.read(length) - if data: - self._digest.update(data) - return data - - def toString(self): - return f"DigestInputStream: {self._digest}" - - -class DigestOutputStream(Stub): - def __init__(self, stream, digest): - super().__init__() - self._stream = stream - self._digest = digest - - def getMessageDigest(self): - return self._digest - - def setMessageDigest(self, digest): - self._digest = digest - - def write(self, b, off=0, length=0): - if isinstance(b, (bytes, bytearray)): - data = b if length == 0 else b[off:off+length] - else: - data = bytes([b]) - self._digest.update(data) - self._stream.write(data) - - def toString(self): - return f"DigestOutputStream: {self._digest}" - - -# ============================================================ -# Signature -# ============================================================ - -class Signature(Stub): - def __init__(self, algorithm): - super().__init__() - self._algorithm = algorithm - self._state = "UNINITIALIZED" - self._impl = _hashlib.new("sha256") - - @staticmethod - def getInstance(algorithm, provider=None): - return Signature(algorithm) - - def initSign(self, private_key, random=None): - self._state = "SIGN" - - def initVerify(self, public_key): - self._state = "VERIFY" - - def update(self, data, off=0, length=-1): - if length < 0: - self._impl.update(data) - else: - self._impl.update(data[off:off+length]) - - def sign(self): - self._state = "UNINITIALIZED" - return self._impl.digest() - - def verify(self, signature): - self._state = "UNINITIALIZED" - return self._impl.digest() == signature - - def setParameter(self, param, value=None): - pass - - def getParameters(self): - return None - - def getAlgorithm(self): - return self._algorithm - - def getProvider(self): - return Provider("SUN", 1.0, "SUN provider") - - -class SignatureSpi(Stub): - pass - - -# ============================================================ -# KeyStore -# ============================================================ - -class KeyStore(Stub): - class PasswordProtection(Stub): - def __init__(self, password): - super().__init__() - self._password = password - - def getPassword(self): - return self._password - - class Entry(Stub): - pass - - class PrivateKeyEntry(Entry): - def __init__(self, private_key, cert_chain): - super().__init__() - self._private_key = private_key - self._cert_chain = cert_chain - - def getPrivateKey(self): - return self._private_key - - def getCertificateChain(self): - return self._cert_chain - - class SecretKeyEntry(Entry): - def __init__(self, secret_key): - super().__init__() - self._secret_key = secret_key - - def getSecretKey(self): - return self._secret_key - - class TrustedCertificateEntry(Entry): - def __init__(self, cert): - super().__init__() - self._cert = cert - - def getTrustedCertificate(self): - return self._cert - - def __init__(self, type_name="JKS"): - super().__init__() - self._type = type_name - self._entries = {} - self._loaded = False - - @staticmethod - def getInstance(type_name, provider=None): - return KeyStore(type_name) - - def load(self, stream=None, password=None): - self._loaded = True - - def store(self, stream, password): - pass - - def setKeyEntry(self, alias, key, password, chain): - self._entries[alias] = (key, chain) - - def setCertificateEntry(self, alias, cert): - self._entries[alias] = cert - - def getKey(self, alias, password): - entry = self._entries.get(alias) - if isinstance(entry, tuple): - return entry[0] - return None - - def getCertificate(self, alias): - entry = self._entries.get(alias) - if isinstance(entry, tuple): - return entry[1][0] if entry[1] else None - return entry - - def getCertificateChain(self, alias): - entry = self._entries.get(alias) - if isinstance(entry, tuple): - return entry[1] - return None - - def getCertificateAliases(self, cert): - return [] - - def aliases(self): - return list(self._entries.keys()) - - def containsAlias(self, alias): - return alias in self._entries - - def deleteEntry(self, alias): - if alias in self._entries: - del self._entries[alias] - - def size(self): - return len(self._entries) - - def isCertificateEntry(self, alias): - return alias in self._entries and not isinstance(self._entries[alias], tuple) - - def isKeyEntry(self, alias): - return alias in self._entries and isinstance(self._entries[alias], tuple) - - def getType(self): - return self._type - - def getProvider(self): - return Provider("SUN", 1.0, "SUN provider") - - def getCreationDate(self, alias): - from datetime import datetime - return datetime.now() - - -class KeyStoreSpi(Stub): - pass - - -# ============================================================ -# X509Certificate -# ============================================================ - -class X509Certificate(Stub): - def __init__(self): - super().__init__() - self._encoded = b"" - - def getSubjectX500Principal(self): - return Principal() - - def getIssuerX500Principal(self): - return Principal() - - def getSerialNumber(self): - return 0 - - def getNotBefore(self): - from datetime import datetime - return datetime.now() - - def getNotAfter(self): - from datetime import datetime - return datetime.now() - - def getSigAlgName(self): - return "SHA256withRSA" - - def getSigAlgOID(self): - return "1.2.840.113549.1.1.11" - - def getVersion(self): - return 3 - - def getPublicKey(self): - return PublicKey() - - def getEncoded(self): - return self._encoded - - def verify(self, key): - return True - - def checkValidity(self, date=None): - return True - - def toString(self): - return "X509Certificate" - - -# ============================================================ -# AlgorithmParameters / AlgorithmParameterGenerator (stubs) -# ============================================================ - -class AlgorithmParameters(Stub): - def __init__(self, algorithm): - super().__init__() - self._algorithm = algorithm - - @staticmethod - def getInstance(algorithm, provider=None): - return AlgorithmParameters(algorithm) - - def init(self, params, format=None): - pass - - def getEncoded(self, format=None): - return b"" - - def getAlgorithm(self): - return self._algorithm - - def getProvider(self): - return Provider("SUN", 1.0, "SUN provider") - - def toString(self): - return f"{self._algorithm} parameters" - - -class AlgorithmParametersSpi(Stub): - pass - - -class AlgorithmParameterGenerator(Stub): - @staticmethod - def getInstance(algorithm, provider=None): - return AlgorithmParameterGenerator() - - def init(self, size_or_genparam, random=None): - pass - - def generateParameters(self): - return AlgorithmParameters("DSA") - - -class AlgorithmParameterGeneratorSpi(Stub): - pass - - -# ============================================================ -# Permission / AllPermission / BasicPermission (stubs) -# ============================================================ - -class Permission(Stub): - def __init__(self, name): - super().__init__() - self._name = name - - def getName(self): - return self._name - - def getActions(self): - return "" - - def implies(self, permission): - return True - - def equals(self, obj): - return isinstance(obj, Permission) and self._name == obj._name - - def hashCode(self): - return hash(self._name) - - def toString(self): - return f"({self.__class__.__name__} {self._name})" - - -class AllPermission(Permission): - def __init__(self): - super().__init__("") - - -class BasicPermission(Permission): - def __init__(self, name, actions=None): - super().__init__(name) - - -class PermissionCollection(Stub): - def __init__(self): - super().__init__() - self._permissions = [] - - def add(self, permission): - self._permissions.append(permission) - - def implies(self, permission): - return True - - def elements(self): - return iter(self._permissions) - - def toString(self): - return str(self._permissions) - - -class UnresolvedPermission(Permission): - def __init__(self, type_name, name, actions, certs): - super().__init__(name) - self._unresolved_type = type_name - self._unresolved_actions = actions - self._certs = certs - - def getUnresolvedType(self): - return self._unresolved_type - - def getUnresolvedActions(self): - return self._unresolved_actions - - def getUnresolvedCerts(self): - return self._certs - - -# ============================================================ -# Principal / CodeSource / Policy (stubs) -# ============================================================ - -class Principal(Stub): - def __init__(self, name=""): - super().__init__() - self._name = name - - def getName(self): - return self._name - - def toString(self): - return self._name - - def equals(self, other): - return isinstance(other, Principal) and self._name == other._name - - def hashCode(self): - return hash(self._name) - - -class CodeSource(Stub): - def __init__(self, location, certs): - super().__init__() - self._location = location - - def getLocation(self): - return self._location - - def getCertificates(self): - return [] - - def implies(self, codesource): - return True - - -class Policy(Stub): - _policy = None - - @staticmethod - def getPolicy(): - if Policy._policy is None: - Policy._policy = Policy() - return Policy._policy - - @staticmethod - def setPolicy(policy): - Policy._policy = policy - - def getPermissions(self, codesource): - return PermissionCollection() - - def implies(self, cs, perm): - return True - - def refresh(self): - pass - - -# ============================================================ -# CertPath / CertPathBuilder / CertPathValidator (stubs) -# ============================================================ - -class CertPath(Stub): - def __init__(self, algorithm): - super().__init__() - self._algorithm = algorithm - self._certs = [] - - def getCertificates(self): - return self._certs - - def getType(self): - return self._algorithm - - def getEncoded(self): - return b"" - - def toString(self): - return f"CertPath: {self._algorithm}" - - -class CertPathBuilder(Stub): - @staticmethod - def getInstance(algorithm, provider=None): - return CertPathBuilder() - - def build(self, params): - return CertPath("X.509") - - -class CertPathBuilderSpi(Stub): - pass - - -class CertPathValidator(Stub): - @staticmethod - def getInstance(algorithm, provider=None): - return CertPathValidator() - - def validate(self, cert_path, params): - return None - - -class CertPathValidatorSpi(Stub): - pass - - -class CertificateFactory(Stub): - @staticmethod - def getInstance(algorithm, provider=None): - return CertificateFactory() - - def generateCertificate(self, in_stream): - return X509Certificate() - - def generateCertPath(self, in_stream): - return CertPath("X.509") - - def getCertPathEncodings(self): - return ["PKCS7"] - - -class CertificateFactorySpi(Stub): - pass - - -class CertStore(Stub): - @staticmethod - def getInstance(type_name, params, provider=None): - return CertStore() - - def getCertificates(self, selector): - return [] - - def getCRLs(self, selector): - return [] - - -class CertStoreSpi(Stub): - pass - - -# ============================================================ -# CRL / X509CRL (stubs) -# ============================================================ - -class CRL(Stub): - def __init__(self): - super().__init__() - - def isRevoked(self, cert): - return False - - def toString(self): - return "CRL" - - -class X509CRL(CRL): - def __init__(self): - super().__init__() - - def getEncoded(self): - return b"" - - def verify(self, key): - return True - - def getIssuerX500Principal(self): - return Principal() - - def toString(self): - return "X509CRL" - - -class X509Extension(Stub): - pass - - -# ============================================================ -# Guard / GuardedObject (stubs) -# ============================================================ - -class Guard(Stub): - def checkGuard(self, object): - pass - - -class GuardedObject(Stub): - def __init__(self, object, guard): - super().__init__() - self._object = object - - def getObject(self): - return self._object - - -# ============================================================ -# Identity / IdentityScope / Signer (stubs) -# ============================================================ - -class Identity(Stub): - def __init__(self, name, scope=None): - super().__init__() - self._name = name - - def getName(self): - return self._name - - def getPublicKey(self): - return PublicKey() - - def setPublicKey(self, key): - pass - - def toString(self): - return self._name - - def getScope(self): - return None - - -class IdentityScope(Identity): - def __init__(self, name): - super().__init__(name) - - def getIdentity(self, name): - return None - - def addIdentity(self, identity): - pass - - def removeIdentity(self, identity): - pass - - def identities(self): - return [] - - @staticmethod - def getSystemScope(): - return None - - def size(self): - return 0 - - def toString(self): - return self._name - - -class Signer(Identity): - def __init__(self, name): - super().__init__(name) - - def getPrivateKey(self): - return PrivateKey() - - def setKeyPair(self, pair): - pass - - def toString(self): - return self._name - - -# ============================================================ -# CodeSigner / Timestamp (stubs) -# ============================================================ - -class Timestamp(Stub): - def __init__(self, timestamp, signer_cert_path): - super().__init__() - self._timestamp = timestamp - - def getTimestamp(self): - return self._timestamp - - def getSignerCertPath(self): - return CertPath("X.509") - - def equals(self, other): - return isinstance(other, Timestamp) - - def hashCode(self): - return 0 - - def toString(self): - return "Timestamp" - - -class CodeSigner(Stub): - def __init__(self, signer_cert_path, timestamp): - super().__init__() - self._signer_cert_path = signer_cert_path - self._timestamp = timestamp - - def getSignerCertPath(self): - return self._signer_cert_path - - def getTimestamp(self): - return self._timestamp - - def equals(self, other): - return isinstance(other, CodeSigner) - - def hashCode(self): - return 0 - - def toString(self): - return "CodeSigner" \ No newline at end of file diff --git a/android_pyapi/java/security_cert.py b/android_pyapi/java/security_cert.py deleted file mode 100644 index b2d436e..0000000 --- a/android_pyapi/java/security_cert.py +++ /dev/null @@ -1,907 +0,0 @@ -""" -Java security.cert standard library APIs implemented in Python. -java.security.cert package - Certificate, CertPath, CRL, PKIX, etc. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -from .security import GeneralSecurityException, CertificateFactory as _OldCF -from .lang import RuntimeException, String -import datetime as _datetime -import typing as _typing - -__all__ = [ - "Certificate", "CertificateFactory", "X509Certificate", - "X509CRL", "X509CRLEntry", "CertPath", "CertPathBuilder", - "CertPathValidator", "CertStore", "CertificateNotYetValidException", - "CertificateExpiredException", "CertificateEncodingException", - "CertificateParsingException", "CertPathValidatorException", - "CertPathBuilderException", "CRL", "CRLException", - "PolicyQualifierInfo", "TrustAnchor", "Extension", "X509Extension", - "CollectionCertStoreParameters", "LDAPCertStoreParameters", - "CertStoreParameters", "PKIXParameters", "PKIXBuilderParameters", - "PKIXCertPathBuilderResult", "PKIXCertPathValidatorResult", - "CertPathBuilderResult", "CertPathValidatorResult", "CertPathParameters", - "CRL_REASON_UNSPECIFIED", "CRL_REASON_KEY_COMPROMISE", - "CRL_REASON_CA_COMPROMISE", "CRL_REASON_AFFILIATION_CHANGED", - "CRL_REASON_SUPERSEDED", "CRL_REASON_CESSATION_OF_OPERATION", - "CRL_REASON_CERTIFICATE_HOLD", "CRL_REASON_REMOVE_FROM_CRL", - "CRL_REASON_PRIVILEGE_WITHDRAWN", "CRL_REASON_AA_COMPROMISE", -] - -# ============================================================ -# CRL Reason Constants -# ============================================================ - -CRL_REASON_UNSPECIFIED = 0 -CRL_REASON_KEY_COMPROMISE = 1 -CRL_REASON_CA_COMPROMISE = 2 -CRL_REASON_AFFILIATION_CHANGED = 3 -CRL_REASON_SUPERSEDED = 4 -CRL_REASON_CESSATION_OF_OPERATION = 5 -CRL_REASON_CERTIFICATE_HOLD = 6 -CRL_REASON_REMOVE_FROM_CRL = 8 -CRL_REASON_PRIVILEGE_WITHDRAWN = 9 -CRL_REASON_AA_COMPROMISE = 10 - -# ============================================================ -# CertPathParameters -# ============================================================ - -class CertPathParameters(Stub): - """java.security.cert.CertPathParameters""" - def clone(self): - return self.__class__() - - -class CertPathBuilderResult(Stub): - """java.security.cert.CertPathBuilderResult""" - def getCertPath(self): - return CertPath("") - - def clone(self): - return self.__class__() - - -class CertPathValidatorResult(Stub): - """java.security.cert.CertPathValidatorResult""" - def clone(self): - return self.__class__() - - -# ============================================================ -# CertStoreParameters -# ============================================================ - -class CertStoreParameters(Stub): - """java.security.cert.CertStoreParameters""" - def clone(self): - return self.__class__() - - -class CollectionCertStoreParameters(CertStoreParameters): - """java.security.cert.CollectionCertStoreParameters""" - def __init__(self, collection=None): - super().__init__() - if collection is None: - self._collection = [] - else: - self._collection = list(collection) - - def getCollection(self): - return self._collection - - def clone(self): - return CollectionCertStoreParameters(self._collection) - - def toString(self): - return f"CollectionCertStoreParameters: [collection: {len(self._collection)}]" - - -class LDAPCertStoreParameters(CertStoreParameters): - """java.security.cert.LDAPCertStoreParameters""" - def __init__(self, server_name="localhost", port=389): - super().__init__() - self._server_name = server_name - self._port = port - - def getServerName(self): - return self._server_name - - def getPort(self): - return self._port - - def clone(self): - return LDAPCertStoreParameters(self._server_name, self._port) - - def toString(self): - return f"LDAPCertStoreParameters: [serverName: {self._server_name}, port: {self._port}]" - - -# ============================================================ -# Exception Classes -# ============================================================ - -class CertificateException(GeneralSecurityException): - """java.security.cert.CertificateException""" - pass - -class CertificateEncodingException(CertificateException): - """java.security.cert.CertificateEncodingException""" - pass - -class CertificateParsingException(CertificateException): - """java.security.cert.CertificateParsingException""" - pass - -class CertificateNotYetValidException(CertificateException): - """java.security.cert.CertificateNotYetValidException""" - pass - -class CertificateExpiredException(CertificateException): - """java.security.cert.CertificateExpiredException""" - pass - -class CRLException(GeneralSecurityException): - """java.security.cert.CRLException""" - pass - -class CertPathBuilderException(GeneralSecurityException): - """java.security.cert.CertPathBuilderException""" - def __init__(self, message=None, cause=None): - super().__init__() - self._detail = message or "" - self._cause = cause - - def getMessage(self): - return self._detail - - def getCause(self): - return self._cause - -class CertPathValidatorException(GeneralSecurityException): - """java.security.cert.CertPathValidatorException""" - class Reason(Stub): - pass - - def __init__(self, message=None, cause=None, cert_path=None, index=-1): - super().__init__() - self._detail = message or "" - self._cause = cause - self._cert_path = cert_path - self._index = index - - def getMessage(self): - return self._detail - - def getCause(self): - return self._cause - - def getCertPath(self): - return self._cert_path - - def getIndex(self): - return self._index - - -# ============================================================ -# Certificate -# ============================================================ - -class Certificate(Stub): - """java.security.cert.Certificate""" - def __init__(self, type_name=""): - super().__init__() - self._type = type_name - - def getType(self): - return self._type - - def getEncoded(self): - raise CertificateEncodingException("Certificate not encoded") - - def verify(self, key): - raise NotImplementedError - - def toString(self): - return f"Certificate type={self._type}" - - def getPublicKey(self): - from .security import PublicKey - return PublicKey() - - def equals(self, other): - if not isinstance(other, Certificate): - return False - return self._type == other._type - - def hashCode(self): - return hash(self._type) - - -class X509Certificate(Certificate): - """java.security.cert.X509Certificate""" - def __init__(self): - super().__init__("X.509") - self._encoded = b"" - self._subject = "" - self._issuer = "" - self._serial = 0 - self._not_before = _datetime.datetime.now() - self._not_after = _datetime.datetime.now() + _datetime.timedelta(days=365) - self._sig_alg = "SHA256withRSA" - self._sig_alg_oid = "1.2.840.113549.1.1.11" - self._version = 3 - self._public_key = None - self._extensions = {} - - def getEncoded(self): - if not self._encoded: - raise CertificateEncodingException("X509Cert not encoded") - return self._encoded - - def verify(self, key): - return True - - def getPublicKey(self): - if self._public_key is None: - from .security import PublicKey - self._public_key = PublicKey() - return self._public_key - - def getSubjectX500Principal(self): - from .security import Principal - return Principal(self._subject) - - def getIssuerX500Principal(self): - from .security import Principal - return Principal(self._issuer) - - def getSerialNumber(self): - return self._serial - - def getNotBefore(self): - return self._not_before - - def getNotAfter(self): - return self._not_after - - def getSigAlgName(self): - return self._sig_alg - - def getSigAlgOID(self): - return self._sig_alg_oid - - def getVersion(self): - return self._version - - def checkValidity(self, date=None): - if date is None: - date = _datetime.datetime.now() - if date < self._not_before: - raise CertificateNotYetValidException( - f"Certificate not valid until {self._not_before}" - ) - if date > self._not_after: - raise CertificateExpiredException( - f"Certificate expired at {self._not_after}" - ) - - def getExtendedKeyUsage(self): - return None - - def getBasicConstraints(self): - return -1 - - def getSubjectAlternativeNames(self): - return None - - def getIssuerAlternativeNames(self): - return None - - def getKeyUsage(self): - return None - - def hasUnsupportedCriticalExtension(self): - return False - - def getCriticalExtensionOIDs(self): - return list(self._extensions.keys()) - - def getNonCriticalExtensionOIDs(self): - return [] - - def getExtensionValue(self, oid): - return self._extensions.get(oid) - - def toString(self): - return f"X509Certificate [subject={self._subject}, issuer={self._issuer}]" - - def setEncoded(self, encoded): - self._encoded = bytes(encoded) - - def setSubject(self, subject): - self._subject = subject - - def setIssuer(self, issuer): - self._issuer = issuer - - def setSerialNumber(self, serial): - self._serial = serial - - def setNotBefore(self, dt): - self._not_before = dt - - def setNotAfter(self, dt): - self._not_after = dt - - -# ============================================================ -# CRL / X509CRL / X509CRLEntry -# ============================================================ - -class CRL(Stub): - """java.security.cert.CRL""" - def __init__(self, type_name=""): - super().__init__() - self._type = type_name - - def getType(self): - return self._type - - def isRevoked(self, cert): - return False - - def toString(self): - return f"CRL type={self._type}" - - -class X509CRLEntry(Stub): - """java.security.cert.X509CRLEntry""" - def __init__(self): - super().__init__() - self._serial_number = 0 - self._revocation_date = _datetime.datetime.now() - self._extensions = {} - - def getSerialNumber(self): - return self._serial_number - - def getRevocationDate(self): - return self._revocation_date - - def hasExtensions(self): - return len(self._extensions) > 0 - - def toString(self): - return f"X509CRLEntry serial={self._serial_number}" - - def getCertificateIssuer(self): - return None - - def getEncoded(self): - return b"" - - def getCriticalExtensionOIDs(self): - return list(self._extensions.keys()) - - def getNonCriticalExtensionOIDs(self): - return [] - - def getExtensionValue(self, oid): - return self._extensions.get(oid) - - -class X509CRL(CRL): - """java.security.cert.X509CRL""" - def __init__(self): - super().__init__("X.509") - self._encoded = b"" - self._issuer = "" - self._this_update = _datetime.datetime.now() - self._next_update = _datetime.datetime.now() + _datetime.timedelta(days=7) - self._sig_alg = "SHA256withRSA" - self._version = 2 - self._entries = [] - - def getEncoded(self): - return self._encoded - - def verify(self, key): - return True - - def getIssuerX500Principal(self): - from .security import Principal - return Principal(self._issuer) - - def getThisUpdate(self): - return self._this_update - - def getNextUpdate(self): - return self._next_update - - def getSigAlgName(self): - return self._sig_alg - - def getSigAlgOID(self): - return "1.2.840.113549.1.1.11" - - def getVersion(self): - return self._version - - def isRevoked(self, cert): - for entry in self._entries: - if hasattr(cert, 'getSerialNumber'): - if entry.getSerialNumber() == cert.getSerialNumber(): - return True - return False - - def getRevokedCertificate(self, cert): - for entry in self._entries: - if hasattr(cert, 'getSerialNumber'): - if entry.getSerialNumber() == cert.getSerialNumber(): - return entry - return None - - def getRevokedCertificates(self): - return self._entries if self._entries else None - - def getCriticalExtensionOIDs(self): - return [] - - def getNonCriticalExtensionOIDs(self): - return [] - - def getExtensionValue(self, oid): - return None - - def hasUnsupportedCriticalExtension(self): - return False - - def toString(self): - return f"X509CRL issuer={self._issuer}" - - def addEntry(self, entry): - self._entries.append(entry) - - def setIssuer(self, issuer): - self._issuer = issuer - - -# ============================================================ -# CertPath -# ============================================================ - -class CertPath(Stub): - """java.security.cert.CertPath""" - def __init__(self, algorithm): - super().__init__() - self._algorithm = algorithm - self._certs = [] - self._encoded = b"" - - def getType(self): - return self._algorithm - - def getCertificates(self): - return self._certs - - def getEncoded(self, encoding=None): - return self._encoded - - def getEncodings(self): - return ["PKCS7"] - - def toString(self): - return f"CertPath: {self._algorithm} [{len(self._certs)} cert(s)]" - - def equals(self, other): - if not isinstance(other, CertPath): - return False - return self._algorithm == other._algorithm and self._certs == other._certs - - def hashCode(self): - return hash(self._algorithm) ^ hash(tuple(self._certs)) - - -# ============================================================ -# CertificateFactory -# ============================================================ - -class CertificateFactory(Stub): - """java.security.cert.CertificateFactory""" - def __init__(self, algorithm): - super().__init__() - self._algorithm = algorithm - - @staticmethod - def getInstance(algorithm, provider=None): - return CertificateFactory(algorithm) - - def generateCertificate(self, in_stream): - cert = X509Certificate() - if hasattr(in_stream, 'read'): - data = in_stream.read() - cert.setEncoded(data if isinstance(data, bytes) else data.encode()) - return cert - - def generateCertPath(self, in_stream, encoding=None): - return CertPath("X.509") - - def generateCRL(self, in_stream): - return X509CRL() - - def getCertPathEncodings(self): - return ["PKCS7"] - - def getProvider(self): - from .security import Provider - return Provider("SUN", 1.0, "Default provider") - - def getType(self): - return self._algorithm - - -# ============================================================ -# CertPathBuilder / CertPathValidator / CertStore -# ============================================================ - -class CertPathBuilder(Stub): - """java.security.cert.CertPathBuilder""" - def __init__(self, algorithm): - super().__init__() - self._algorithm = algorithm - - @staticmethod - def getInstance(algorithm, provider=None): - return CertPathBuilder(algorithm) - - def build(self, params): - if not isinstance(params, CertPathParameters): - raise CertPathBuilderException("Invalid params") - return PKIXCertPathBuilderResult(CertPath("X.509")) - - def getAlgorithm(self): - return self._algorithm - - def getProvider(self): - from .security import Provider - return Provider("SUN", 1.0, "Default provider") - - -class CertPathValidator(Stub): - """java.security.cert.CertPathValidator""" - def __init__(self, algorithm): - super().__init__() - self._algorithm = algorithm - - @staticmethod - def getInstance(algorithm, provider=None): - return CertPathValidator(algorithm) - - def validate(self, cert_path, params): - if not isinstance(cert_path, CertPath): - raise CertPathValidatorException("Invalid cert path") - return PKIXCertPathValidatorResult() - - def getAlgorithm(self): - return self._algorithm - - def getProvider(self): - from .security import Provider - return Provider("SUN", 1.0, "Default provider") - - -class CertStore(Stub): - """java.security.cert.CertStore""" - def __init__(self, type_name, params): - super().__init__() - self._type = type_name - self._params = params - - @staticmethod - def getInstance(type_name, params, provider=None): - return CertStore(type_name, params) - - def getCertificates(self, selector): - return [] - - def getCRLs(self, selector): - return [] - - def getType(self): - return self._type - - def getCertStoreParameters(self): - return self._params - - -# ============================================================ -# PolicyQualifierInfo -# ============================================================ - -class PolicyQualifierInfo(Stub): - """java.security.cert.PolicyQualifierInfo""" - def __init__(self, encoded): - super().__init__() - self._encoded = encoded - - def getEncoded(self): - return self._encoded - - def getPolicyQualifierId(self): - return "1.3.6.1.5.5.7.2.1" - - def getPolicyQualifier(self): - return self._encoded - - def toString(self): - return f"PolicyQualifierInfo [id={self.getPolicyQualifierId()}]" - - -# ============================================================ -# TrustAnchor -# ============================================================ - -class TrustAnchor(Stub): - """java.security.cert.TrustAnchor""" - def __init__(self, ca_cert, ca_name=None, ca_public_key=None, name_constraints=None): - super().__init__() - self._ca_cert = ca_cert - self._ca_name = ca_name - self._ca_public_key = ca_public_key - self._name_constraints = name_constraints - - def getTrustedCert(self): - return self._ca_cert - - def getCAName(self): - return self._ca_name - - def getCAPublicKey(self): - return self._ca_public_key - - def getNameConstraints(self): - return self._name_constraints - - def toString(self): - return f"TrustAnchor [CA={self._ca_name}]" - - -# ============================================================ -# Extension / X509Extension -# ============================================================ - -class Extension(Stub): - """java.security.cert.Extension""" - def __init__(self, oid="", critical=False, value=b""): - super().__init__() - self._oid = oid - self._critical = critical - self._value = value - - def getId(self): - return self._oid - - def isCritical(self): - return self._critical - - def getValue(self): - return self._value - - def getEncoded(self): - return self._value - - def toString(self): - return f"Extension oid={self._oid} critical={self._critical}" - - -class X509Extension(Stub): - """java.security.cert.X509Extension""" - def hasUnsupportedCriticalExtension(self): - return False - - def getCriticalExtensionOIDs(self): - return [] - - def getNonCriticalExtensionOIDs(self): - return [] - - def getExtensionValue(self, oid): - return None - - -# ============================================================ -# PKIXParameters / PKIXBuilderParameters -# ============================================================ - -class PKIXParameters(CertPathParameters): - """java.security.cert.PKIXParameters""" - def __init__(self, trust_anchors=None): - super().__init__() - if trust_anchors is not None: - if isinstance(trust_anchors, set): - self._trust_anchors = trust_anchors - else: - self._trust_anchors = set(trust_anchors) - else: - self._trust_anchors = set() - self._date = None - self._cert_stores = [] - self._revocation_enabled = True - self._explicit_policy_required = False - self._policy_mapping_inhibited = False - self._any_policy_inhibited = False - self._policy_qualifiers_rejected = True - self._sig_provider = None - self._target_cert_constraints = None - self._initial_policies = None - self._cert_path_checkers = [] - self._stores = [] - - def getTrustAnchors(self): - return self._trust_anchors - - def setTrustAnchors(self, trust_anchors): - if isinstance(trust_anchors, set): - self._trust_anchors = trust_anchors - else: - self._trust_anchors = set(trust_anchors) - - def getDate(self): - return self._date - - def setDate(self, date): - self._date = date - - def addCertStore(self, store): - self._cert_stores.append(store) - - def getCertStores(self): - return self._cert_stores - - def setCertStores(self, stores): - self._cert_stores = list(stores) - - def isRevocationEnabled(self): - return self._revocation_enabled - - def setRevocationEnabled(self, enabled): - self._revocation_enabled = enabled - - def isExplicitPolicyRequired(self): - return self._explicit_policy_required - - def setExplicitPolicyRequired(self, required): - self._explicit_policy_required = required - - def isPolicyMappingInhibited(self): - return self._policy_mapping_inhibited - - def setPolicyMappingInhibited(self, inhibited): - self._policy_mapping_inhibited = inhibited - - def isAnyPolicyInhibited(self): - return self._any_policy_inhibited - - def setAnyPolicyInhibited(self, inhibited): - self._any_policy_inhibited = inhibited - - def getPolicyQualifiersRejected(self): - return self._policy_qualifiers_rejected - - def setPolicyQualifiersRejected(self, rejected): - self._policy_qualifiers_rejected = rejected - - def getSigProvider(self): - return self._sig_provider - - def setSigProvider(self, provider): - self._sig_provider = provider - - def getTargetCertConstraints(self): - return self._target_cert_constraints - - def setTargetCertConstraints(self, constraints): - self._target_cert_constraints = constraints - - def getInitialPolicies(self): - return self._initial_policies - - def setInitialPolicies(self, policies): - self._initial_policies = policies - - def addCertPathChecker(self, checker): - self._cert_path_checkers.append(checker) - - def getCertPathCheckers(self): - return self._cert_path_checkers - - def setCertPathCheckers(self, checkers): - self._cert_path_checkers = list(checkers) - - def clone(self): - return PKIXParameters(self._trust_anchors) - - def toString(self): - return f"PKIXParameters [trust_anchors={len(self._trust_anchors)}]" - - -class PKIXBuilderParameters(PKIXParameters): - """java.security.cert.PKIXBuilderParameters""" - def __init__(self, trust_anchors, target_constraints=None): - super().__init__(trust_anchors) - self._max_path_length = 5 - if target_constraints is not None: - self.setTargetCertConstraints(target_constraints) - - def getMaxPathLength(self): - return self._max_path_length - - def setMaxPathLength(self, length): - if length < -1: - raise IllegalArgumentException("max path length must be >= -1") - self._max_path_length = length - - def toString(self): - return f"PKIXBuilderParameters [max_path={self._max_path_length}]" - - -# ============================================================ -# PKIXCertPathBuilderResult / PKIXCertPathValidatorResult -# ============================================================ - -class PKIXCertPathBuilderResult(CertPathBuilderResult): - """java.security.cert.PKIXCertPathBuilderResult""" - def __init__(self, cert_path=None, trust_anchor=None, policy_tree=None, subject_public_key=None): - super().__init__() - self._cert_path = cert_path - self._trust_anchor = trust_anchor - self._policy_tree = policy_tree - self._subject_public_key = subject_public_key - - def getCertPath(self): - return self._cert_path - - def getTrustAnchor(self): - return self._trust_anchor - - def getPolicyTree(self): - return self._policy_tree - - def getPublicKey(self): - return self._subject_public_key - - def toString(self): - return f"PKIXCertPathBuilderResult [path={self._cert_path}]" - - -class PKIXCertPathValidatorResult(CertPathValidatorResult): - """java.security.cert.PKIXCertPathValidatorResult""" - def __init__(self, trust_anchor=None, policy_tree=None, subject_public_key=None): - super().__init__() - self._trust_anchor = trust_anchor - self._policy_tree = policy_tree - self._subject_public_key = subject_public_key - - def getTrustAnchor(self): - return self._trust_anchor - - def getPolicyTree(self): - return self._policy_tree - - def getPublicKey(self): - return self._subject_public_key - - def toString(self): - return "PKIXCertPathValidatorResult" - - -# ============================================================ -# IllegalArgumentException (local) -# ============================================================ - -class IllegalArgumentException(RuntimeException): - """java.lang.IllegalArgumentException""" - pass \ No newline at end of file diff --git a/android_pyapi/java/security_spec.py b/android_pyapi/java/security_spec.py deleted file mode 100644 index 26963e3..0000000 --- a/android_pyapi/java/security_spec.py +++ /dev/null @@ -1,697 +0,0 @@ -""" -Java security.spec standard library APIs implemented in Python. -java.security.spec package - KeySpec, AlgorithmParameterSpec, etc. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -from .security import GeneralSecurityException, InvalidKeySpecException, InvalidParameterException -from .lang import RuntimeException -import math as _py_math - -__all__ = [ - "KeySpec", "EncodedKeySpec", "PKCS8EncodedKeySpec", "X509EncodedKeySpec", - "DSAPrivateKeySpec", "DSAPublicKeySpec", - "RSAPrivateKeySpec", "RSAPublicKeySpec", "RSAPrivateCrtKeySpec", - "RSAOtherPrimeInfo", - "ECPrivateKeySpec", "ECPublicKeySpec", - "ECField", "ECFieldF2m", "ECFieldFp", "EllipticCurve", - "ECGenParameterSpec", "ECParameterSpec", "ECPoint", - "AlgorithmParameterSpec", - "IvParameterSpec", "OAEPParameterSpec", "PSSParameterSpec", - "MGF1ParameterSpec", "PBEParameterSpec", - "RC2ParameterSpec", "RC5ParameterSpec", - "DHParameterSpec", "DHGenParameterSpec", - "DHPrivateKeySpec", "DHPublicKeySpec", - "DSAGenParameterSpec", -] - - -# ============================================================ -# KeySpec -# ============================================================ - -class KeySpec(Stub): - """java.security.spec.KeySpec""" - pass - - -class EncodedKeySpec(KeySpec): - """java.security.spec.EncodedKeySpec""" - def __init__(self, encoded_key): - super().__init__() - self._encoded_key = bytes(encoded_key) - - def getEncoded(self): - return self._encoded_key - - def getFormat(self): - raise NotImplementedError - - -class PKCS8EncodedKeySpec(EncodedKeySpec): - """java.security.spec.PKCS8EncodedKeySpec""" - def __init__(self, encoded_key): - super().__init__(encoded_key) - - def getFormat(self): - return "PKCS#8" - - -class X509EncodedKeySpec(EncodedKeySpec): - """java.security.spec.X509EncodedKeySpec""" - def __init__(self, encoded_key): - super().__init__(encoded_key) - - def getFormat(self): - return "X.509" - - -# ============================================================ -# DSA KeySpecs -# ============================================================ - -class DSAPrivateKeySpec(KeySpec): - """java.security.spec.DSAPrivateKeySpec""" - def __init__(self, x, p, q, g): - super().__init__() - self._x = x - self._p = p - self._q = q - self._g = g - - def getX(self): - return self._x - - def getP(self): - return self._p - - def getQ(self): - return self._q - - def getG(self): - return self._g - - -class DSAPublicKeySpec(KeySpec): - """java.security.spec.DSAPublicKeySpec""" - def __init__(self, y, p, q, g): - super().__init__() - self._y = y - self._p = p - self._q = q - self._g = g - - def getY(self): - return self._y - - def getP(self): - return self._p - - def getQ(self): - return self._q - - def getG(self): - return self._g - - -# ============================================================ -# RSA KeySpecs -# ============================================================ - -class RSAPrivateKeySpec(KeySpec): - """java.security.spec.RSAPrivateKeySpec""" - def __init__(self, modulus, private_exponent): - super().__init__() - self._modulus = modulus - self._private_exponent = private_exponent - - def getModulus(self): - return self._modulus - - def getPrivateExponent(self): - return self._private_exponent - - -class RSAPublicKeySpec(KeySpec): - """java.security.spec.RSAPublicKeySpec""" - def __init__(self, modulus, public_exponent): - super().__init__() - self._modulus = modulus - self._public_exponent = public_exponent - - def getModulus(self): - return self._modulus - - def getPublicExponent(self): - return self._public_exponent - - -class RSAPrivateCrtKeySpec(RSAPrivateKeySpec): - """java.security.spec.RSAPrivateCrtKeySpec""" - def __init__(self, modulus, public_exponent, private_exponent, - prime_p, prime_q, prime_exponent_p, prime_exponent_q, - crt_coefficient): - super().__init__(modulus, private_exponent) - self._public_exponent = public_exponent - self._prime_p = prime_p - self._prime_q = prime_q - self._prime_exponent_p = prime_exponent_p - self._prime_exponent_q = prime_exponent_q - self._crt_coefficient = crt_coefficient - - def getPublicExponent(self): - return self._public_exponent - - def getPrimeP(self): - return self._prime_p - - def getPrimeQ(self): - return self._prime_q - - def getPrimeExponentP(self): - return self._prime_exponent_p - - def getPrimeExponentQ(self): - return self._prime_exponent_q - - def getCrtCoefficient(self): - return self._crt_coefficient - - -class RSAOtherPrimeInfo(Stub): - """java.security.spec.RSAOtherPrimeInfo""" - def __init__(self, prime, exponent, coefficient): - super().__init__() - self._prime = prime - self._exponent = exponent - self._coefficient = coefficient - - def getPrime(self): - return self._prime - - def getExponent(self): - return self._exponent - - def getCrtCoefficient(self): - return self._coefficient - - -# ============================================================ -# EC KeySpecs -# ============================================================ - -class ECPoint(Stub): - """java.security.spec.ECPoint""" - def __init__(self, affine_x, affine_y): - super().__init__() - self._affine_x = affine_x - self._affine_y = affine_y - - def getAffineX(self): - return self._affine_x - - def getAffineY(self): - return self._affine_y - - def equals(self, other): - if not isinstance(other, ECPoint): - return False - return self._affine_x == other._affine_x and self._affine_y == other._affine_y - - def hashCode(self): - return hash((self._affine_x, self._affine_y)) - - def toString(self): - return f"ECPoint(x={self._affine_x}, y={self._affine_y})" - - POINT_INFINITY = None - - -class ECField(Stub): - """java.security.spec.ECField""" - def getFieldSize(self): - raise NotImplementedError - - -class ECFieldFp(ECField): - """java.security.spec.ECFieldFp""" - def __init__(self, prime): - super().__init__() - self._prime = prime - - def getP(self): - return self._prime - - def getFieldSize(self): - return self._prime.bit_length() - - def equals(self, other): - if not isinstance(other, ECFieldFp): - return False - return self._prime == other._prime - - def hashCode(self): - return hash(self._prime) - - -class ECFieldF2m(ECField): - """java.security.spec.ECFieldF2m""" - def __init__(self, m, ks=None, rp=None): - super().__init__() - self._m = m - if ks is not None and rp is None: - if isinstance(ks, int): - self._ks = [ks] - self._rp = None - else: - self._ks = list(ks) - self._rp = None - elif rp is not None: - self._ks = None - self._rp = rp - else: - self._ks = None - self._rp = None - - def getM(self): - return self._m - - def getKs(self): - return self._ks - - def getFieldSize(self): - return self._m - - def getReductionPolynomial(self): - return self._rp - - def equals(self, other): - if not isinstance(other, ECFieldF2m): - return False - return self._m == other._m and self._ks == other._ks and self._rp == other._rp - - def hashCode(self): - return hash(self._m) - - -class EllipticCurve(Stub): - """java.security.spec.EllipticCurve""" - def __init__(self, field, a, b, seed=None): - super().__init__() - self._field = field - self._a = a - self._b = b - self._seed = seed - - def getField(self): - return self._field - - def getA(self): - return self._a - - def getB(self): - return self._b - - def getSeed(self): - return self._seed - - def equals(self, other): - if not isinstance(other, EllipticCurve): - return False - return (self._field == other._field and self._a == other._a - and self._b == other._b) - - def hashCode(self): - return hash((self._field, self._a, self._b)) - - -class ECParameterSpec(Stub): - """java.security.spec.ECParameterSpec""" - def __init__(self, curve, generator, order, cofactor): - super().__init__() - self._curve = curve - self._generator = generator - self._order = order - self._cofactor = cofactor - - def getCurve(self): - return self._curve - - def getGenerator(self): - return self._generator - - def getOrder(self): - return self._order - - def getCofactor(self): - return self._cofactor - - def toString(self): - return f"ECParameterSpec [order={self._order}]" - - -class ECGenParameterSpec(Stub): - """java.security.spec.ECGenParameterSpec""" - def __init__(self, name): - super().__init__() - self._name = name - - def getName(self): - return self._name - - def toString(self): - return f"ECGenParameterSpec [name={self._name}]" - - -class ECPrivateKeySpec(KeySpec): - """java.security.spec.ECPrivateKeySpec""" - def __init__(self, s, params): - super().__init__() - self._s = s - self._params = params - - def getS(self): - return self._s - - def getParams(self): - return self._params - - -class ECPublicKeySpec(KeySpec): - """java.security.spec.ECPublicKeySpec""" - def __init__(self, w, params): - super().__init__() - self._w = w - self._params = params - - def getW(self): - return self._w - - def getParams(self): - return self._params - - -# ============================================================ -# AlgorithmParameterSpec -# ============================================================ - -class AlgorithmParameterSpec(Stub): - """java.security.spec.AlgorithmParameterSpec""" - pass - - -class IvParameterSpec(AlgorithmParameterSpec): - """javax.crypto.spec.IvParameterSpec""" - def __init__(self, iv, offset=0, length=None): - super().__init__() - if length is None: - self._iv = bytes(iv[offset:]) - else: - self._iv = bytes(iv[offset:offset + length]) - - def getIV(self): - return self._iv - - -class OAEPParameterSpec(AlgorithmParameterSpec): - """javax.crypto.spec.OAEPParameterSpec""" - def __init__(self, md_name="SHA-256", mgf_name="MGF1", mgf_spec=None, p_src=None): - super().__init__() - self._md_name = md_name - self._mgf_name = mgf_name - self._mgf_spec = mgf_spec or MGF1ParameterSpec.SHA256 - self._p_src = p_src - - def getDigestAlgorithm(self): - return self._md_name - - def getMGFAlgorithm(self): - return self._mgf_name - - def getMGFParameters(self): - return self._mgf_spec - - def getPSource(self): - return self._p_src - - DEFAULT = None - - -class PSSParameterSpec(AlgorithmParameterSpec): - """java.security.spec.PSSParameterSpec""" - def __init__(self, md_name="SHA-256", mgf_name="MGF1", mgf_spec=None, - salt_len=None, trailer_field=1): - super().__init__() - if isinstance(md_name, int): - # Legacy constructor: PSSParameterSpec(int saltLen) - self._salt_len = md_name - self._md_name = "SHA-1" - self._mgf_name = "MGF1" - self._mgf_spec = MGF1ParameterSpec.SHA1 - self._trailer_field = 1 - else: - self._md_name = md_name - self._mgf_name = mgf_name - self._mgf_spec = mgf_spec or MGF1ParameterSpec.SHA256 - self._salt_len = salt_len if salt_len is not None else 32 - self._trailer_field = trailer_field - - def getDigestAlgorithm(self): - return self._md_name - - def getMGFAlgorithm(self): - return self._mgf_name - - def getMGFParameters(self): - return self._mgf_spec - - def getSaltLength(self): - return self._salt_len - - def getTrailerField(self): - return self._trailer_field - - DEFAULT = None - TRAILER_FIELD_BC = 1 - - -class MGF1ParameterSpec(AlgorithmParameterSpec): - """java.security.spec.MGF1ParameterSpec""" - def __init__(self, md_name): - super().__init__() - self._md_name = md_name - - def getDigestAlgorithm(self): - return self._md_name - - def toString(self): - return f"MGF1ParameterSpec [{self._md_name}]" - - SHA1 = None - SHA224 = None - SHA256 = None - SHA384 = None - SHA512 = None - - -class PBEParameterSpec(AlgorithmParameterSpec): - """javax.crypto.spec.PBEParameterSpec""" - def __init__(self, salt, iteration_count): - super().__init__() - self._salt = bytes(salt) - self._iteration_count = iteration_count - - def getSalt(self): - return self._salt - - def getIterationCount(self): - return self._iteration_count - - -class RC2ParameterSpec(AlgorithmParameterSpec): - """javax.crypto.spec.RC2ParameterSpec""" - def __init__(self, effective_key_bits, iv=None, offset=0): - super().__init__() - self._effective_key_bits = effective_key_bits - if iv is not None: - self._iv = bytes(iv[offset:offset + 8]) if offset + 8 <= len(iv) else bytes(iv[offset:]) - else: - self._iv = None - - def getEffectiveKeyBits(self): - return self._effective_key_bits - - def getIV(self): - return self._iv - - def equals(self, other): - if not isinstance(other, RC2ParameterSpec): - return False - return (self._effective_key_bits == other._effective_key_bits - and self._iv == other._iv) - - def hashCode(self): - return hash(self._effective_key_bits) ^ hash(self._iv) - - -class RC5ParameterSpec(AlgorithmParameterSpec): - """javax.crypto.spec.RC5ParameterSpec""" - def __init__(self, version, rounds, word_size, iv=None, offset=0): - super().__init__() - self._version = version - self._rounds = rounds - self._word_size = word_size - if iv is not None: - self._iv = bytes(iv[offset:offset + 8]) if offset + 8 <= len(iv) else bytes(iv[offset:]) - else: - self._iv = None - - def getVersion(self): - return self._version - - def getRounds(self): - return self._rounds - - def getWordSize(self): - return self._word_size - - def getIV(self): - return self._iv - - def equals(self, other): - if not isinstance(other, RC5ParameterSpec): - return False - return (self._version == other._version and self._rounds == other._rounds - and self._word_size == other._word_size and self._iv == other._iv) - - def hashCode(self): - return hash((self._version, self._rounds, self._word_size, bytes(self._iv) if self._iv else b"")) - - -# ============================================================ -# DH (Diffie-Hellman) Specs -# ============================================================ - -class DHParameterSpec(AlgorithmParameterSpec): - """javax.crypto.spec.DHParameterSpec""" - def __init__(self, p, g, l=0): - super().__init__() - self._p = p - self._g = g - self._l = l - - def getP(self): - return self._p - - def getG(self): - return self._g - - def getL(self): - return self._l - - -class DHGenParameterSpec(AlgorithmParameterSpec): - """javax.crypto.spec.DHGenParameterSpec""" - def __init__(self, prime_size, exponent_size): - super().__init__() - self._prime_size = prime_size - self._exponent_size = exponent_size - - def getPrimeSize(self): - return self._prime_size - - def getExponentSize(self): - return self._exponent_size - - -class DHPrivateKeySpec(KeySpec): - """javax.crypto.spec.DHPrivateKeySpec""" - def __init__(self, x, p, g): - super().__init__() - self._x = x - self._p = p - self._g = g - - def getX(self): - return self._x - - def getP(self): - return self._p - - def getG(self): - return self._g - - -class DHPublicKeySpec(KeySpec): - """javax.crypto.spec.DHPublicKeySpec""" - def __init__(self, y, p, g): - super().__init__() - self._y = y - self._p = p - self._g = g - - def getY(self): - return self._y - - def getP(self): - return self._p - - def getG(self): - return self._g - - -# ============================================================ -# DSAGenParameterSpec -# ============================================================ - -class DSAGenParameterSpec(AlgorithmParameterSpec): - """java.security.spec.DSAGenParameterSpec""" - def __init__(self, prime_pl, subprime_ql, seed_len): - super().__init__() - self._prime_pl = prime_pl - self._subprime_ql = subprime_ql - self._seed_len = seed_len - - def getPrimePL(self): - return self._prime_pl - - def getSubprimeQL(self): - return self._subprime_ql - - def getSeedLen(self): - return self._seed_len - - -# ============================================================ -# Initialize MGF1ParameterSpec constants -# ============================================================ - -MGF1ParameterSpec.SHA1 = MGF1ParameterSpec("SHA-1") -MGF1ParameterSpec.SHA224 = MGF1ParameterSpec("SHA-224") -MGF1ParameterSpec.SHA256 = MGF1ParameterSpec("SHA-256") -MGF1ParameterSpec.SHA384 = MGF1ParameterSpec("SHA-384") -MGF1ParameterSpec.SHA512 = MGF1ParameterSpec("SHA-512") - -# ============================================================ -# Initialize OAEPParameterSpec DEFAULT -# ============================================================ - -OAEPParameterSpec.DEFAULT = OAEPParameterSpec( - "SHA-1", "MGF1", MGF1ParameterSpec.SHA1, None -) - -# ============================================================ -# Initialize PSSParameterSpec DEFAULT -# ============================================================ - -PSSParameterSpec.DEFAULT = PSSParameterSpec( - "SHA-1", "MGF1", MGF1ParameterSpec.SHA1, 20, 1 -) - -# ============================================================ -# ECPoint.POINT_INFINITY -# ============================================================ - -ECPoint.POINT_INFINITY = ECPoint(None, None) \ No newline at end of file diff --git a/android_pyapi/java/sql.py b/android_pyapi/java/sql.py deleted file mode 100644 index 323160c..0000000 --- a/android_pyapi/java/sql.py +++ /dev/null @@ -1,2242 +0,0 @@ -""" -Java SQL standard library APIs implemented in Python. -java.sql package - Connection, Statement, ResultSet, Types, etc. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -from .lang import RuntimeException, Exception as _JavaException -import datetime as _datetime -import typing as _typing - -__all__ = [ - "Connection", "Statement", "PreparedStatement", "CallableStatement", - "ResultSet", "ResultSetMetaData", "DatabaseMetaData", - "Driver", "DriverManager", "DriverPropertyInfo", - "SQLException", "SQLWarning", "SQLData", "SQLInput", "SQLOutput", - "BatchUpdateException", "DataTruncation", - "Date", "Time", "Timestamp", "Savepoint", - "Blob", "Clob", "NClob", "Ref", "RowId", "Struct", "SQLXML", "Array", - "Types", "JDBCType", "Wrapper", - "FETCH_FORWARD", "FETCH_REVERSE", "FETCH_UNKNOWN", - "TYPE_FORWARD_ONLY", "TYPE_SCROLL_INSENSITIVE", "TYPE_SCROLL_SENSITIVE", - "HOLD_CURSORS_OVER_COMMIT", "CLOSE_CURSORS_AT_COMMIT", - "CLOSE_ALL_RESULTS", "KEEP_CURRENT_RESULT", - "NO_GENERATED_KEYS", "RETURN_GENERATED_KEYS", - "CLOSE_CURRENT_RESULT", -] - -# ============================================================ -# Constants -# ============================================================ - -FETCH_FORWARD = 1000 -FETCH_REVERSE = 1001 -FETCH_UNKNOWN = 1002 - -TYPE_FORWARD_ONLY = 1003 -TYPE_SCROLL_INSENSITIVE = 1004 -TYPE_SCROLL_SENSITIVE = 1005 - -HOLD_CURSORS_OVER_COMMIT = 1 -CLOSE_CURSORS_AT_COMMIT = 2 - -CLOSE_ALL_RESULTS = 1 -KEEP_CURRENT_RESULT = 2 -CLOSE_CURRENT_RESULT = 3 - -NO_GENERATED_KEYS = 1 -RETURN_GENERATED_KEYS = 2 - - -# ============================================================ -# SQLException hierarchy -# ============================================================ - -class SQLException(_JavaException): - """java.sql.SQLException""" - def __init__(self, reason=None, sql_state=None, vendor_code=0, cause=None): - msg = reason or "" - super().__init__(msg) - self._reason = reason - self._sql_state = sql_state - self._vendor_code = vendor_code - self._cause = cause - self._next_exception = None - - def getMessage(self): - return self._reason or "" - - def getSQLState(self): - return self._sql_state - - def getErrorCode(self): - return self._vendor_code - - def getNextException(self): - return self._next_exception - - def setNextException(self, ex): - self._next_exception = ex - - def getCause(self): - return self._cause - - -class SQLWarning(SQLException): - """java.sql.SQLWarning""" - def __init__(self, reason=None, sql_state=None, vendor_code=0): - super().__init__(reason, sql_state, vendor_code) - self._next_warning = None - - def getNextWarning(self): - return self._next_warning - - def setNextWarning(self, warning): - self._next_warning = warning - - -class BatchUpdateException(SQLException): - """java.sql.BatchUpdateException""" - def __init__(self, reason=None, sql_state=None, vendor_code=0, - update_counts=None, cause=None): - super().__init__(reason, sql_state, vendor_code, cause) - self._update_counts = update_counts or [] - - def getUpdateCounts(self): - return self._update_counts - - def getLargeUpdateCounts(self): - return [int(c) for c in self._update_counts] - - -class DataTruncation(SQLWarning): - """java.sql.DataTruncation""" - def __init__(self, index, parameter, read, data_size, transfer_size): - super().__init__("Data truncation") - self._index = index - self._parameter = parameter - self._read = read - self._data_size = data_size - self._transfer_size = transfer_size - - def getIndex(self): - return self._index - - def getParameter(self): - return self._parameter - - def getRead(self): - return self._read - - def getDataSize(self): - return self._data_size - - def getTransferSize(self): - return self._transfer_size - - -# ============================================================ -# Wrapper -# ============================================================ - -class Wrapper(Stub): - """java.sql.Wrapper""" - def unwrap(self, iface): - if iface is None: - raise SQLException("iface is null") - return self - - def isWrapperFor(self, iface): - return iface is not None and isinstance(self, iface) - - -# ============================================================ -# Types Constants -# ============================================================ - -class Types: - """java.sql.Types""" - ARRAY = 2003 - BIGINT = -5 - BINARY = -2 - BIT = -7 - BLOB = 2004 - BOOLEAN = 16 - CHAR = 1 - CLOB = 2005 - DATALINK = 70 - DATE = 91 - DECIMAL = 3 - DISTINCT = 2001 - DOUBLE = 8 - FLOAT = 6 - INTEGER = 4 - JAVA_OBJECT = 2000 - LONGNVARCHAR = -16 - LONGVARBINARY = -4 - LONGVARCHAR = -1 - NCHAR = -15 - NCLOB = 2011 - NULL = 0 - NUMERIC = 2 - NVARCHAR = -9 - OTHER = 1111 - REAL = 7 - REF = 2006 - REF_CURSOR = 2012 - ROWID = -8 - SMALLINT = 5 - SQLXML = 2009 - STRUCT = 2002 - TIME = 92 - TIMESTAMP = 93 - TINYINT = -6 - VARBINARY = -3 - VARCHAR = 12 - - -class JDBCType(Stub): - """java.sql.JDBCType""" - _type_map = {} - - def __init__(self, vendor_type_number): - super().__init__() - self._vendor_type_number = vendor_type_number - - def getVendorTypeNumber(self): - return self._vendor_type_number - - def getName(self): - for name, val in vars(Types).items(): - if isinstance(val, int) and val == self._vendor_type_number: - return name - return "UNKNOWN" - - @staticmethod - def valueOf(name): - val = getattr(Types, name, None) - if val is None: - raise IllegalArgumentException(f"No JDBC type: {name}") - return JDBCType(val) - - @staticmethod - def values(): - return [JDBCType(v) for v in set( - v for v in vars(Types).values() if isinstance(v, int) - )] - - -# ============================================================ -# Date / Time / Timestamp -# ============================================================ - -class Date(_datetime.date): - """java.sql.Date""" - def __init__(self, *args): - if len(args) == 1: - ms = args[0] - dt = _datetime.datetime.fromtimestamp(ms / 1000.0) - super().__init__(dt.year, dt.month, dt.day) - elif len(args) == 3: - super().__init__(args[0], args[1], args[2]) - else: - super().__init__() - - def getTime(self): - dt = _datetime.datetime.combine(self, _datetime.time.min) - return int(dt.timestamp() * 1000) - - def setTime(self, ms): - dt = _datetime.datetime.fromtimestamp(ms / 1000.0) - super().__init__(dt.year, dt.month, dt.day) - - def toString(self): - return self.strftime("%Y-%m-%d") - - @staticmethod - def valueOf(s): - parts = s.split("-") - return Date(int(parts[0]), int(parts[1]), int(parts[2])) - - -class Time(_datetime.time): - """java.sql.Time""" - def __init__(self, *args): - if len(args) == 1: - ms = args[0] - dt = _datetime.datetime.fromtimestamp(ms / 1000.0) - super().__init__(dt.hour, dt.minute, dt.second) - elif len(args) == 3: - super().__init__(args[0], args[1], args[2]) - else: - super().__init__() - - def getTime(self): - dt = _datetime.datetime.combine(_datetime.date.today(), self) - return int(dt.timestamp() * 1000) - - def setTime(self, ms): - dt = _datetime.datetime.fromtimestamp(ms / 1000.0) - super().__init__(dt.hour, dt.minute, dt.second) - - def toString(self): - return self.strftime("%H:%M:%S") - - @staticmethod - def valueOf(s): - parts = s.split(":") - return Time(int(parts[0]), int(parts[1]), int(parts[2])) - - -class Timestamp(_datetime.datetime): - """java.sql.Timestamp""" - def __init__(self, *args): - if len(args) == 1: - ms = args[0] - dt = _datetime.datetime.fromtimestamp(ms / 1000.0) - super().__init__(dt.year, dt.month, dt.day, dt.hour, - dt.minute, dt.second, dt.microsecond) - self._nanos = (ms % 1000) * 1000000 - else: - super().__init__(*args) - self._nanos = 0 - - def getTime(self): - return int(self.timestamp() * 1000) - - def setTime(self, ms): - dt = _datetime.datetime.fromtimestamp(ms / 1000.0) - super().__init__(dt.year, dt.month, dt.day, dt.hour, - dt.minute, dt.second, dt.microsecond) - self._nanos = (ms % 1000) * 1000000 - - def getNanos(self): - return self._nanos - - def setNanos(self, n): - self._nanos = n - - def toString(self): - return self.strftime("%Y-%m-%d %H:%M:%S") - - @staticmethod - def valueOf(s): - parts = s.split(" ") - date_part = parts[0].split("-") - time_part = parts[1].split(":") - return Timestamp(int(date_part[0]), int(date_part[1]), int(date_part[2]), - int(time_part[0]), int(time_part[1]), int(time_part[2])) - - -# ============================================================ -# Savepoint -# ============================================================ - -class Savepoint(Stub): - """java.sql.Savepoint""" - def __init__(self, name=None): - super().__init__() - self._savepoint_id = id(self) - self._savepoint_name = name - - def getSavepointId(self): - if self._savepoint_name is not None: - raise SQLException("Named savepoint") - return self._savepoint_id - - def getSavepointName(self): - if self._savepoint_name is None: - raise SQLException("Unnamed savepoint") - return self._savepoint_name - - -# ============================================================ -# Blob / Clob / NClob / Ref / RowId / Struct / SQLXML / Array -# ============================================================ - -class Blob(Stub): - """java.sql.Blob""" - def __init__(self, data=b""): - super().__init__() - self._data = bytearray(data) - self._length = len(data) - - def length(self): - return self._length - - def getBytes(self, pos, length): - start = pos - 1 - return bytes(self._data[start:start + length]) - - def setBytes(self, pos, bytes_data, offset=0, length=None): - start = pos - 1 - if length is None: - length = len(bytes_data) - offset - self._data[start:start + length] = bytearray(bytes_data[offset:offset + length]) - return length - - def position(self, pattern, start): - return -1 - - def truncate(self, length): - self._data = self._data[:length] - self._length = length - - def free(self): - self._data = bytearray() - self._length = 0 - - def getBinaryStream(self, pos=None, length=None): - import io - return io.BytesIO(bytes(self._data)) - - -class Clob(Stub): - """java.sql.Clob""" - def __init__(self, data=""): - super().__init__() - self._data = data - self._length = len(data) - - def length(self): - return self._length - - def getSubString(self, pos, length): - start = pos - 1 - return self._data[start:start + length] - - def setString(self, pos, val, offset=0, length=None): - start = pos - 1 - if length is None: - length = len(val) - offset - self._data = self._data[:start] + val[offset:offset + length] + self._data[start + length:] - return length - - def position(self, search_str, start): - return self._data.find(search_str, start - 1) - - def truncate(self, length): - self._data = self._data[:length] - self._length = length - - def free(self): - self._data = "" - self._length = 0 - - def getAsciiStream(self): - import io - return io.BytesIO(self._data.encode()) - - def getCharacterStream(self, pos=None, length=None): - import io - if pos is not None and length is not None: - return io.StringIO(self._data[pos - 1:pos - 1 + length]) - return io.StringIO(self._data) - - def setAsciiStream(self, pos, stream, length=None): - data = stream.read() if length is None else stream.read(length) - if isinstance(data, bytes): - data = data.decode() - self._data = self._data[:pos - 1] + data + self._data[pos - 1 + len(data):] - - -class NClob(Clob): - """java.sql.NClob""" - pass - - -class Ref(Stub): - """java.sql.Ref""" - def __init__(self, base_type_name=None, obj=None): - super().__init__() - self._base_type_name = base_type_name - self._object = obj - - def getBaseTypeName(self): - return self._base_type_name - - def getObject(self, type_map=None): - return self._object - - def setObject(self, obj): - self._object = obj - - -class RowId(Stub): - """java.sql.RowId""" - def __init__(self, data=b""): - super().__init__() - self._data = bytes(data) - - def getBytes(self): - return self._data - - def toString(self): - return self._data.hex() - - def equals(self, other): - if not isinstance(other, RowId): - return False - return self._data == other._data - - def hashCode(self): - return hash(self._data) - - -class Struct(Stub): - """java.sql.Struct""" - def __init__(self, sql_type_name, attributes): - super().__init__() - self._sql_type_name = sql_type_name - self._attributes = list(attributes) - - def getSQLTypeName(self): - return self._sql_type_name - - def getAttributes(self, type_map=None): - return self._attributes - - -class SQLXML(Stub): - """java.sql.SQLXML""" - def __init__(self, xml_string=""): - super().__init__() - self._xml = xml_string - - def getString(self): - return self._xml - - def setString(self, value): - self._xml = value - - def free(self): - self._xml = "" - - def getBinaryStream(self): - import io - return io.BytesIO(self._xml.encode()) - - def setBinaryStream(self): - import io - buf = io.BytesIO() - return buf - - def getCharacterStream(self): - import io - return io.StringIO(self._xml) - - def setCharacterStream(self): - import io - buf = io.StringIO() - return buf - - def getSource(self, source_class=None): - return None - - def getResult(self, result_class=None): - return None - - -class Array(Stub): - """java.sql.Array""" - def __init__(self, type_name=None, elements=None): - super().__init__() - self._type_name = type_name - self._elements = list(elements) if elements else [] - - def getBaseTypeName(self): - return self._type_name or "VARCHAR" - - def getBaseType(self): - return Types.VARCHAR - - def getArray(self, index=None, count=None): - if index is None: - return self._elements - start = index - 1 - if count is None: - return self._elements[start:] - return self._elements[start:start + count] - - def getResultSet(self, index=None, count=None): - raise SQLException("Not implemented") - - -# ============================================================ -# SQLData / SQLInput / SQLOutput -# ============================================================ - -class SQLData(Stub): - """java.sql.SQLData""" - def getSQLTypeName(self): - raise SQLException("Not implemented") - - def readSQL(self, stream, type_name): - pass - - def writeSQL(self, stream): - pass - - -class SQLInput(Stub): - """java.sql.SQLInput""" - def __init__(self, data=None): - super().__init__() - self._data = list(data) if data else [] - self._pos = 0 - - def readString(self): - val = self._data[self._pos] if self._pos < len(self._data) else None - self._pos += 1 - return str(val) if val is not None else None - - def readBoolean(self): - val = self._data[self._pos] if self._pos < len(self._data) else None - self._pos += 1 - return bool(val) - - def readByte(self): - val = self._data[self._pos] if self._pos < len(self._data) else None - self._pos += 1 - return int(val) if val is not None else 0 - - def readShort(self): - val = self._data[self._pos] if self._pos < len(self._data) else None - self._pos += 1 - return int(val) if val is not None else 0 - - def readInt(self): - val = self._data[self._pos] if self._pos < len(self._data) else None - self._pos += 1 - return int(val) if val is not None else 0 - - def readLong(self): - val = self._data[self._pos] if self._pos < len(self._data) else None - self._pos += 1 - return int(val) if val is not None else 0 - - def readFloat(self): - val = self._data[self._pos] if self._pos < len(self._data) else None - self._pos += 1 - return float(val) if val is not None else 0.0 - - def readDouble(self): - val = self._data[self._pos] if self._pos < len(self._data) else None - self._pos += 1 - return float(val) if val is not None else 0.0 - - def readBigDecimal(self): - return self.readDouble() - - def readBytes(self): - return b"" - - def readDate(self): - val = self._data[self._pos] if self._pos < len(self._data) else None - self._pos += 1 - if isinstance(val, Date): - return val - return Date() - - def readTime(self): - val = self._data[self._pos] if self._pos < len(self._data) else None - self._pos += 1 - if isinstance(val, Time): - return val - return Time() - - def readTimestamp(self): - val = self._data[self._pos] if self._pos < len(self._data) else None - self._pos += 1 - if isinstance(val, Timestamp): - return val - return Timestamp() - - def readCharacterStream(self): - import io - return io.StringIO("") - - def readBinaryStream(self): - import io - return io.BytesIO(b"") - - def readObject(self): - val = self._data[self._pos] if self._pos < len(self._data) else None - self._pos += 1 - return val - - def readRef(self): - return Ref() - - def readBlob(self): - return Blob() - - def readClob(self): - return Clob() - - def readArray(self): - return Array() - - def readURL(self): - return None - - def readNClob(self): - return NClob() - - def readNString(self): - return self.readString() - - def readSQLXML(self): - return SQLXML() - - def readRowId(self): - return RowId() - - def wasNull(self): - return False - - -class SQLOutput(Stub): - """java.sql.SQLOutput""" - def __init__(self): - super().__init__() - self._data = [] - - def writeString(self, x): - self._data.append(x) - - def writeBoolean(self, x): - self._data.append(x) - - def writeByte(self, x): - self._data.append(x) - - def writeShort(self, x): - self._data.append(x) - - def writeInt(self, x): - self._data.append(x) - - def writeLong(self, x): - self._data.append(x) - - def writeFloat(self, x): - self._data.append(x) - - def writeDouble(self, x): - self._data.append(x) - - def writeBigDecimal(self, x): - self._data.append(x) - - def writeBytes(self, x): - self._data.append(x) - - def writeDate(self, x): - self._data.append(x) - - def writeTime(self, x): - self._data.append(x) - - def writeTimestamp(self, x): - self._data.append(x) - - def writeCharacterStream(self, x): - self._data.append(x.read() if hasattr(x, 'read') else x) - - def writeBinaryStream(self, x): - self._data.append(x.read() if hasattr(x, 'read') else x) - - def writeObject(self, x): - self._data.append(x) - - def writeRef(self, x): - self._data.append(x) - - def writeBlob(self, x): - self._data.append(x) - - def writeClob(self, x): - self._data.append(x) - - def writeStruct(self, x): - self._data.append(x) - - def writeArray(self, x): - self._data.append(x) - - def writeURL(self, x): - self._data.append(x) - - def writeNClob(self, x): - self._data.append(x) - - def writeNString(self, x): - self._data.append(x) - - def writeSQLXML(self, x): - self._data.append(x) - - def writeRowId(self, x): - self._data.append(x) - - -# ============================================================ -# Driver / DriverManager / DriverPropertyInfo -# ============================================================ - -class Driver(Stub): - """java.sql.Driver""" - def connect(self, url, info): - raise SQLException("No suitable driver") - - def acceptsURL(self, url): - return False - - def getPropertyInfo(self, url, info): - return [] - - def getMajorVersion(self): - return 1 - - def getMinorVersion(self): - return 0 - - def jdbcCompliant(self): - return False - - def getParentLogger(self): - return None - - -class DriverPropertyInfo(Stub): - """java.sql.DriverPropertyInfo""" - def __init__(self, name, value=None): - super().__init__() - self.name = name - self.description = None - self.required = False - self.value = value - self.choices = None - - -class DriverManager(Stub): - """java.sql.DriverManager""" - _registered_drivers = [] - _log_stream = None - _log_writer = None - _login_timeout = 0 - - @staticmethod - def getConnection(url, user=None, password=None): - for driver in DriverManager._registered_drivers: - if driver.acceptsURL(url): - props = {} - if user is not None: - props["user"] = user - if password is not None: - props["password"] = password - return driver.connect(url, props) - raise SQLException(f"No suitable driver found for {url}") - - @staticmethod - def registerDriver(driver): - DriverManager._registered_drivers.append(driver) - - @staticmethod - def deregisterDriver(driver): - if driver in DriverManager._registered_drivers: - DriverManager._registered_drivers.remove(driver) - - @staticmethod - def getDrivers(): - return DriverManager._registered_drivers - - @staticmethod - def getLoginTimeout(): - return DriverManager._login_timeout - - @staticmethod - def setLoginTimeout(seconds): - DriverManager._login_timeout = seconds - - @staticmethod - def getLogWriter(): - return DriverManager._log_writer - - @staticmethod - def setLogWriter(writer): - DriverManager._log_writer = writer - - @staticmethod - def println(message): - if DriverManager._log_writer is not None: - DriverManager._log_writer.write(str(message) + "\n") - - -# ============================================================ -# Connection -# ============================================================ - -class Connection(Stub): - """java.sql.Connection""" - def createStatement(self): - return Statement() - - def prepareStatement(self, sql): - return PreparedStatement(sql) - - def prepareCall(self, sql): - return CallableStatement(sql) - - def nativeSQL(self, sql): - return sql - - def setAutoCommit(self, auto_commit): - pass - - def getAutoCommit(self): - return True - - def commit(self): - pass - - def rollback(self, savepoint=None): - pass - - def close(self): - pass - - def isClosed(self): - return False - - def getMetaData(self): - return DatabaseMetaData() - - def setReadOnly(self, read_only): - pass - - def isReadOnly(self): - return False - - def setCatalog(self, catalog): - pass - - def getCatalog(self): - return "" - - def setTransactionIsolation(self, level): - pass - - def getTransactionIsolation(self): - return Connection.TRANSACTION_READ_COMMITTED - - def getWarnings(self): - return None - - def clearWarnings(self): - pass - - def setSavepoint(self, name=None): - return Savepoint(name) - - def releaseSavepoint(self, savepoint): - pass - - def createStatement(self, result_set_type, result_set_concurrency): - return Statement() - - def prepareStatement(self, sql, *args): - return PreparedStatement(sql) - - def prepareCall(self, sql, result_set_type, result_set_concurrency): - return CallableStatement(sql) - - def setTypeMap(self, type_map): - pass - - def getTypeMap(self): - return {} - - def setHoldability(self, holdability): - pass - - def getHoldability(self): - return HOLD_CURSORS_OVER_COMMIT - - def setSchema(self, schema): - pass - - def getSchema(self): - return "" - - def abort(self, executor): - pass - - def getNetworkTimeout(self): - return 0 - - def setNetworkTimeout(self, executor, milliseconds): - pass - - def createArrayOf(self, type_name, elements): - return Array(type_name, elements) - - def createBlob(self): - return Blob() - - def createClob(self): - return Clob() - - def createNClob(self): - return NClob() - - def createSQLXML(self): - return SQLXML() - - def createStruct(self, type_name, attributes): - return Struct(type_name, attributes) - - def isValid(self, timeout): - return not self.isClosed() - - # Transaction isolation constants - TRANSACTION_NONE = 0 - TRANSACTION_READ_COMMITTED = 2 - TRANSACTION_READ_UNCOMMITTED = 1 - TRANSACTION_REPEATABLE_READ = 4 - TRANSACTION_SERIALIZABLE = 8 - - -# ============================================================ -# Statement -# ============================================================ - -class Statement(Stub): - """java.sql.Statement""" - def __init__(self): - super().__init__() - self._max_rows = 0 - self._query_timeout = 0 - self._fetch_direction = FETCH_FORWARD - self._fetch_size = 0 - self._max_field_size = 0 - self._poolable = True - self._close_on_completion = False - self._result_set_type = TYPE_FORWARD_ONLY - self._result_set_concurrency = 0 - self._result_set_holdability = HOLD_CURSORS_OVER_COMMIT - self._generated_keys = None - self._update_count = -1 - self._result_set = None - self._more_results = False - self._closed = False - self._warnings = None - self._poolable = True - - def executeQuery(self, sql): - return ResultSet() - - def executeUpdate(self, sql): - return 0 - - def close(self): - self._closed = True - - def getMaxFieldSize(self): - return self._max_field_size - - def setMaxFieldSize(self, max_size): - self._max_field_size = max_size - - def getMaxRows(self): - return self._max_rows - - def setMaxRows(self, max_rows): - self._max_rows = max_rows - - def setEscapeProcessing(self, enable): - pass - - def getQueryTimeout(self): - return self._query_timeout - - def setQueryTimeout(self, seconds): - self._query_timeout = seconds - - def cancel(self): - pass - - def getWarnings(self): - return self._warnings - - def clearWarnings(self): - self._warnings = None - - def setCursorName(self, name): - pass - - def execute(self, sql): - return False - - def getResultSet(self): - return self._result_set - - def getUpdateCount(self): - return self._update_count - - def getMoreResults(self, current=None): - return False - - def setFetchDirection(self, direction): - self._fetch_direction = direction - - def getFetchDirection(self): - return self._fetch_direction - - def setFetchSize(self, rows): - self._fetch_size = rows - - def getFetchSize(self): - return self._fetch_size - - def getResultSetConcurrency(self): - return self._result_set_concurrency - - def getResultSetType(self): - return self._result_set_type - - def addBatch(self, sql): - pass - - def clearBatch(self): - pass - - def executeBatch(self): - return [] - - def getConnection(self): - return Connection() - - def getMoreResults(self, current): - return False - - def getGeneratedKeys(self): - return self._generated_keys or ResultSet() - - def getResultSetHoldability(self): - return self._result_set_holdability - - def isClosed(self): - return self._closed - - def setPoolable(self, poolable): - self._poolable = poolable - - def isPoolable(self): - return self._poolable - - def closeOnCompletion(self): - self._close_on_completion = True - - def isCloseOnCompletion(self): - return self._close_on_completion - - def executeLargeUpdate(self, sql): - return 0 - - def getLargeUpdateCount(self): - return self._update_count - - def getLargeMaxRows(self): - return self._max_rows - - def setLargeMaxRows(self, max_rows): - self._max_rows = max_rows - - def executeLargeBatch(self): - return [] - - def unwrap(self, iface): - return self - - def isWrapperFor(self, iface): - return isinstance(self, iface) - - SUCCESS_NO_INFO = -2 - EXECUTE_FAILED = -3 - RETURN_GENERATED_KEYS = 1 - NO_GENERATED_KEYS = 2 - CLOSE_ALL_RESULTS = 3 - KEEP_CURRENT_RESULT = 2 - CLOSE_CURRENT_RESULT = 1 - NO_GENERATED_KEYS = 2 - RETURN_GENERATED_KEYS = 1 - - -# ============================================================ -# PreparedStatement -# ============================================================ - -class PreparedStatement(Statement): - """java.sql.PreparedStatement""" - def __init__(self, sql=""): - super().__init__() - self._sql = sql - self._parameters = [] - - def setNull(self, parameter_index, sql_type, type_name=None): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = None - - def setBoolean(self, parameter_index, x): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = x - - def setByte(self, parameter_index, x): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = x - - def setShort(self, parameter_index, x): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = x - - def setInt(self, parameter_index, x): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = x - - def setLong(self, parameter_index, x): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = x - - def setFloat(self, parameter_index, x): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = x - - def setDouble(self, parameter_index, x): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = x - - def setBigDecimal(self, parameter_index, x): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = x - - def setString(self, parameter_index, x): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = x - - def setBytes(self, parameter_index, x): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = bytes(x) if x else None - - def setDate(self, parameter_index, x, calendar=None): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = x - - def setTime(self, parameter_index, x, calendar=None): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = x - - def setTimestamp(self, parameter_index, x, calendar=None): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = x - - def setAsciiStream(self, parameter_index, stream, length=None): - self._ensure_params(parameter_index) - data = stream.read() if hasattr(stream, 'read') else stream - if isinstance(data, bytes): - data = data.decode() - self._parameters[parameter_index - 1] = data - - def setBinaryStream(self, parameter_index, stream, length=None): - self._ensure_params(parameter_index) - data = stream.read() if hasattr(stream, 'read') else stream - self._parameters[parameter_index - 1] = data - - def setObject(self, parameter_index, x, target_sql_type=None, scale_or_length=None): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = x - - def setCharacterStream(self, parameter_index, reader, length=None): - self._ensure_params(parameter_index) - data = reader.read() if hasattr(reader, 'read') else reader - self._parameters[parameter_index - 1] = data - - def setBlob(self, parameter_index, blob): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = blob - - def setClob(self, parameter_index, clob): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = clob - - def setArray(self, parameter_index, array): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = array - - def setRef(self, parameter_index, ref): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = ref - - def setNString(self, parameter_index, value): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = value - - def setNClob(self, parameter_index, nclob): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = nclob - - def setNCharacterStream(self, parameter_index, reader, length=None): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = reader - - def setRowId(self, parameter_index, row_id): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = row_id - - def setSQLXML(self, parameter_index, xml): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = xml - - def setURL(self, parameter_index, url): - self._ensure_params(parameter_index) - self._parameters[parameter_index - 1] = url - - def clearParameters(self): - self._parameters = [] - - def executeQuery(self): - return ResultSet() - - def executeUpdate(self): - return 0 - - def execute(self): - return False - - def addBatch(self): - pass - - def getParameterMetaData(self): - return ResultSetMetaData() - - def getMetaData(self): - return ResultSetMetaData() - - def _ensure_params(self, idx): - while len(self._parameters) < idx: - self._parameters.append(None) - - -# ============================================================ -# CallableStatement -# ============================================================ - -class CallableStatement(PreparedStatement): - """java.sql.CallableStatement""" - def __init__(self, sql=""): - super().__init__(sql) - self._output_params = {} - - def registerOutParameter(self, parameter_index, sql_type, scale_or_type_name=None): - pass - - def wasNull(self): - return False - - def getString(self, parameter_index): - return self._output_params.get(parameter_index) - - def getBoolean(self, parameter_index): - return bool(self._output_params.get(parameter_index)) - - def getByte(self, parameter_index): - return self._output_params.get(parameter_index, 0) - - def getShort(self, parameter_index): - return self._output_params.get(parameter_index, 0) - - def getInt(self, parameter_index): - return self._output_params.get(parameter_index, 0) - - def getLong(self, parameter_index): - return self._output_params.get(parameter_index, 0) - - def getFloat(self, parameter_index): - return float(self._output_params.get(parameter_index, 0.0)) - - def getDouble(self, parameter_index): - return float(self._output_params.get(parameter_index, 0.0)) - - def getBigDecimal(self, parameter_index): - return self._output_params.get(parameter_index) - - def getBytes(self, parameter_index): - return b"" - - def getDate(self, parameter_index): - return Date() - - def getTime(self, parameter_index): - return Time() - - def getTimestamp(self, parameter_index): - return Timestamp() - - def getObject(self, parameter_index, type_map=None): - return self._output_params.get(parameter_index) - - def getRef(self, parameter_index): - return Ref() - - def getBlob(self, parameter_index): - return Blob() - - def getClob(self, parameter_index): - return Clob() - - def getArray(self, parameter_index): - return Array() - - def getURL(self, parameter_index): - return None - - def getNClob(self, parameter_index): - return NClob() - - def getNString(self, parameter_index): - return self.getString(parameter_index) - - def getNCharacterStream(self, parameter_index): - import io - return io.StringIO("") - - def getRowId(self, parameter_index): - return RowId() - - def getSQLXML(self, parameter_index): - return SQLXML() - - -# ============================================================ -# ResultSet -# ============================================================ - -class ResultSet(Stub): - """java.sql.ResultSet""" - def __init__(self): - super().__init__() - self._rows = [] - self._row_index = -1 - self._closed = False - self._warnings = None - self._fetch_direction = FETCH_FORWARD - self._fetch_size = 0 - self._type = TYPE_FORWARD_ONLY - self._concurrency = 0 - self._holdability = HOLD_CURSORS_OVER_COMMIT - self._meta_data = ResultSetMetaData() - - def next(self): - if self._row_index + 1 < len(self._rows): - self._row_index += 1 - return True - return False - - def close(self): - self._closed = True - - def wasNull(self): - return False - - def getString(self, column_index_or_label): - return str(self._get_value(column_index_or_label) or "") - - def getBoolean(self, column_index_or_label): - return bool(self._get_value(column_index_or_label)) - - def getByte(self, column_index_or_label): - return int(self._get_value(column_index_or_label) or 0) - - def getShort(self, column_index_or_label): - return int(self._get_value(column_index_or_label) or 0) - - def getInt(self, column_index_or_label): - return int(self._get_value(column_index_or_label) or 0) - - def getLong(self, column_index_or_label): - return int(self._get_value(column_index_or_label) or 0) - - def getFloat(self, column_index_or_label): - return float(self._get_value(column_index_or_label) or 0.0) - - def getDouble(self, column_index_or_label): - return float(self._get_value(column_index_or_label) or 0.0) - - def getBytes(self, column_index_or_label): - val = self._get_value(column_index_or_label) - if isinstance(val, bytes): - return val - if isinstance(val, str): - return val.encode() - return b"" - - def getDate(self, column_index_or_label): - val = self._get_value(column_index_or_label) - if isinstance(val, Date): - return val - return Date() - - def getTime(self, column_index_or_label): - val = self._get_value(column_index_or_label) - if isinstance(val, Time): - return val - return Time() - - def getTimestamp(self, column_index_or_label): - val = self._get_value(column_index_or_label) - if isinstance(val, Timestamp): - return val - return Timestamp() - - def getObject(self, column_index_or_label, type_map=None): - return self._get_value(column_index_or_label) - - def findColumn(self, column_label): - return 1 - - def getMetaData(self): - return self._meta_data - - def getWarnings(self): - return self._warnings - - def clearWarnings(self): - self._warnings = None - - def setFetchDirection(self, direction): - self._fetch_direction = direction - - def getFetchDirection(self): - return self._fetch_direction - - def setFetchSize(self, rows): - self._fetch_size = rows - - def getFetchSize(self): - return self._fetch_size - - def getType(self): - return self._type - - def getConcurrency(self): - return self._concurrency - - def rowUpdated(self): - return False - - def rowInserted(self): - return False - - def rowDeleted(self): - return False - - def updateNull(self, column_index_or_label): - pass - - def updateBoolean(self, column_index_or_label, x): - pass - - def updateByte(self, column_index_or_label, x): - pass - - def updateShort(self, column_index_or_label, x): - pass - - def updateInt(self, column_index_or_label, x): - pass - - def updateLong(self, column_index_or_label, x): - pass - - def updateFloat(self, column_index_or_label, x): - pass - - def updateDouble(self, column_index_or_label, x): - pass - - def updateString(self, column_index_or_label, x): - pass - - def updateBytes(self, column_index_or_label, x): - pass - - def updateDate(self, column_index_or_label, x): - pass - - def updateTime(self, column_index_or_label, x): - pass - - def updateTimestamp(self, column_index_or_label, x): - pass - - def updateRow(self): - pass - - def insertRow(self): - pass - - def deleteRow(self): - pass - - def refreshRow(self): - pass - - def cancelRowUpdates(self): - pass - - def moveToInsertRow(self): - pass - - def moveToCurrentRow(self): - pass - - def isClosed(self): - return self._closed - - def getHoldability(self): - return self._holdability - - def getStatement(self): - return None - - def getArray(self, column_index_or_label): - return Array() - - def getBlob(self, column_index_or_label): - return Blob() - - def getClob(self, column_index_or_label): - return Clob() - - def getRef(self, column_index_or_label): - return Ref() - - def getURL(self, column_index_or_label): - return None - - def getNClob(self, column_index_or_label): - return NClob() - - def getNString(self, column_index_or_label): - return self.getString(column_index_or_label) - - def getNCharacterStream(self, column_index_or_label): - import io - return io.StringIO("") - - def getRowId(self, column_index_or_label): - return RowId() - - def getSQLXML(self, column_index_or_label): - return SQLXML() - - def getBinaryStream(self, column_index_or_label): - import io - return io.BytesIO(b"") - - def getCharacterStream(self, column_index_or_label): - import io - return io.StringIO("") - - def getAsciiStream(self, column_index_or_label): - import io - return io.BytesIO(b"") - - def updateObject(self, column_index_or_label, x, scale_or_target=None): - pass - - def beforeFirst(self): - self._row_index = -1 - - def afterLast(self): - self._row_index = len(self._rows) - - def first(self): - if self._rows: - self._row_index = 0 - return True - return False - - def last(self): - if self._rows: - self._row_index = len(self._rows) - 1 - return True - return False - - def absolute(self, row): - if row == 0: - self._row_index = -1 - return False - idx = row - 1 if row > 0 else len(self._rows) + row - if 0 <= idx < len(self._rows): - self._row_index = idx - return True - return False - - def relative(self, rows): - new_idx = self._row_index + rows - if 0 <= new_idx < len(self._rows): - self._row_index = new_idx - return True - return False - - def previous(self): - if self._row_index > 0: - self._row_index -= 1 - return True - return False - - def isBeforeFirst(self): - return self._row_index < 0 and len(self._rows) > 0 - - def isAfterLast(self): - return self._row_index >= len(self._rows) and len(self._rows) > 0 - - def isFirst(self): - return self._row_index == 0 - - def isLast(self): - return self._row_index == len(self._rows) - 1 - - def getRow(self): - return self._row_index + 1 if 0 <= self._row_index < len(self._rows) else 0 - - def _get_value(self, col): - if 0 <= self._row_index < len(self._rows): - row = self._rows[self._row_index] - if isinstance(col, int) and col - 1 < len(row): - return row[col - 1] - return None - - # Constants - FETCH_FORWARD = 1000 - FETCH_REVERSE = 1001 - FETCH_UNKNOWN = 1002 - TYPE_FORWARD_ONLY = 1003 - TYPE_SCROLL_INSENSITIVE = 1004 - TYPE_SCROLL_SENSITIVE = 1005 - CONCUR_READ_ONLY = 1007 - CONCUR_UPDATABLE = 1008 - HOLD_CURSORS_OVER_COMMIT = 1 - CLOSE_CURSORS_AT_COMMIT = 2 - - -# ============================================================ -# ResultSetMetaData -# ============================================================ - -class ResultSetMetaData(Stub): - """java.sql.ResultSetMetaData""" - def __init__(self): - super().__init__() - self._column_count = 0 - self._column_names = [] - self._column_types = [] - - def getColumnCount(self): - return self._column_count - - def isAutoIncrement(self, column): - return False - - def isCaseSensitive(self, column): - return True - - def isSearchable(self, column): - return True - - def isCurrency(self, column): - return False - - def isNullable(self, column): - return ResultSetMetaData.columnNullable - - def isSigned(self, column): - return True - - def getColumnDisplaySize(self, column): - return 255 - - def getColumnLabel(self, column): - return self.getColumnName(column) - - def getColumnName(self, column): - if column - 1 < len(self._column_names): - return self._column_names[column - 1] - return f"COLUMN_{column}" - - def getSchemaName(self, column): - return "" - - def getPrecision(self, column): - return 0 - - def getScale(self, column): - return 0 - - def getTableName(self, column): - return "" - - def getCatalogName(self, column): - return "" - - def getColumnType(self, column): - if column - 1 < len(self._column_types): - return self._column_types[column - 1] - return Types.VARCHAR - - def getColumnTypeName(self, column): - return "VARCHAR" - - def isReadOnly(self, column): - return False - - def isWritable(self, column): - return True - - def isDefinitelyWritable(self, column): - return True - - def getColumnClassName(self, column): - return "java.lang.String" - - columnNoNulls = 0 - columnNullable = 1 - columnNullableUnknown = 2 - - -# ============================================================ -# DatabaseMetaData -# ============================================================ - -class DatabaseMetaData(Stub): - """java.sql.DatabaseMetaData""" - def getDatabaseProductName(self): - return "Android-pyApi SQL" - - def getDatabaseProductVersion(self): - return "1.0" - - def getDriverName(self): - return "Android-pyApi JDBC Driver" - - def getDriverVersion(self): - return "1.0" - - def getDriverMajorVersion(self): - return 1 - - def getDriverMinorVersion(self): - return 0 - - def getURL(self): - return "" - - def getUserName(self): - return "" - - def isReadOnly(self): - return False - - def nullsAreSortedHigh(self): - return True - - def nullsAreSortedLow(self): - return False - - def nullsAreSortedAtStart(self): - return False - - def nullsAreSortedAtEnd(self): - return False - - def getDatabaseMajorVersion(self): - return 1 - - def getDatabaseMinorVersion(self): - return 0 - - def getJDBCMajorVersion(self): - return 4 - - def getJDBCMinorVersion(self): - return 2 - - def getDefaultTransactionIsolation(self): - return Connection.TRANSACTION_READ_COMMITTED - - def supportsTransactions(self): - return True - - def supportsTransactionIsolationLevel(self, level): - return True - - def supportsResultSetType(self, type_): - return True - - def supportsResultSetConcurrency(self, type_, concurrency): - return True - - def getTables(self, catalog, schema_pattern, table_name_pattern, types): - return ResultSet() - - def getColumns(self, catalog, schema_pattern, table_name_pattern, column_name_pattern): - return ResultSet() - - def getPrimaryKeys(self, catalog, schema, table): - return ResultSet() - - def getImportedKeys(self, catalog, schema, table): - return ResultSet() - - def getExportedKeys(self, catalog, schema, table): - return ResultSet() - - def getSchemas(self): - return ResultSet() - - def getCatalogs(self): - return ResultSet() - - def getTableTypes(self): - return ResultSet() - - def getTypeInfo(self): - return ResultSet() - - def getIndexInfo(self, catalog, schema, table, unique, approximate): - return ResultSet() - - def getProcedures(self, catalog, schema_pattern, procedure_name_pattern): - return ResultSet() - - def getProcedureColumns(self, catalog, schema_pattern, procedure_name_pattern, column_name_pattern): - return ResultSet() - - def supportsSavepoints(self): - return True - - def supportsBatchUpdates(self): - return True - - def supportsStoredProcedures(self): - return True - - def allProceduresAreCallable(self): - return True - - def allTablesAreSelectable(self): - return True - - def dataDefinitionCausesTransactionCommit(self): - return False - - def dataDefinitionIgnoredInTransactions(self): - return False - - def doesMaxRowSizeIncludeBlobs(self): - return False - - def getMaxBinaryLiteralLength(self): - return 0 - - def getMaxCharLiteralLength(self): - return 0 - - def getMaxColumnNameLength(self): - return 128 - - def getMaxColumnsInGroupBy(self): - return 0 - - def getMaxColumnsInIndex(self): - return 0 - - def getMaxColumnsInOrderBy(self): - return 0 - - def getMaxColumnsInSelect(self): - return 0 - - def getMaxColumnsInTable(self): - return 0 - - def getMaxConnections(self): - return 0 - - def getMaxCursorNameLength(self): - return 0 - - def getMaxIndexLength(self): - return 0 - - def getMaxSchemaNameLength(self): - return 0 - - def getMaxProcedureNameLength(self): - return 0 - - def getMaxRowSize(self): - return 0 - - def getMaxStatementLength(self): - return 0 - - def getMaxStatements(self): - return 0 - - def getMaxTableNameLength(self): - return 0 - - def getMaxTablesInSelect(self): - return 0 - - def getMaxUserNameLength(self): - return 0 - - def getNumericFunctions(self): - return "" - - def getSQLKeywords(self): - return "" - - def getStringFunctions(self): - return "" - - def getSystemFunctions(self): - return "" - - def getTimeDateFunctions(self): - return "" - - def getSearchStringEscape(self): - return "\\" - - def getExtraNameCharacters(self): - return "" - - def getSchemaTerm(self): - return "schema" - - def getCatalogTerm(self): - return "catalog" - - def getProcedureTerm(self): - return "procedure" - - def getCatalogSeparator(self): - return "." - - def isCatalogAtStart(self): - return True - - def autoCommitFailureClosesAllResultSets(self): - return False - - def generatedKeyAlwaysReturned(self): - return True - - def getSQLStateType(self): - return DatabaseMetaData.sqlStateSQL - - def locatorsUpdateCopy(self): - return False - - def supportsDifferentTableCorrelationNames(self): - return True - - def supportsANSI92EntryLevelSQL(self): - return True - - def supportsANSI92IntermediateSQL(self): - return True - - def supportsANSI92FullSQL(self): - return False - - def supportsIntegrityEnhancementFacility(self): - return False - - def supportsOuterJoins(self): - return True - - def supportsFullOuterJoins(self): - return True - - def supportsLimitedOuterJoins(self): - return True - - def supportsSchemasInDataManipulation(self): - return True - - def supportsSchemasInProcedureCalls(self): - return True - - def supportsSchemasInTableDefinitions(self): - return True - - def supportsSchemasInIndexDefinitions(self): - return True - - def supportsSchemasInPrivilegeDefinitions(self): - return True - - def supportsCatalogsInDataManipulation(self): - return True - - def supportsCatalogsInProcedureCalls(self): - return True - - def supportsCatalogsInTableDefinitions(self): - return True - - def supportsCatalogsInIndexDefinitions(self): - return True - - def supportsCatalogsInPrivilegeDefinitions(self): - return True - - def supportsPositionedDelete(self): - return False - - def supportsPositionedUpdate(self): - return False - - def supportsSelectForUpdate(self): - return True - - def supportsSubqueriesInComparisons(self): - return True - - def supportsSubqueriesInExists(self): - return True - - def supportsSubqueriesInIns(self): - return True - - def supportsSubqueriesInQuantifieds(self): - return True - - def supportsCorrelatedSubqueries(self): - return True - - def supportsUnion(self): - return True - - def supportsUnionAll(self): - return True - - def supportsOpenCursorsAcrossCommit(self): - return True - - def supportsOpenCursorsAcrossRollback(self): - return True - - def supportsOpenStatementsAcrossCommit(self): - return True - - def supportsOpenStatementsAcrossRollback(self): - return True - - def supportsMixedCaseIdentifiers(self): - return True - - def storesMixedCaseIdentifiers(self): - return True - - def supportsMixedCaseQuotedIdentifiers(self): - return True - - def storesMixedCaseQuotedIdentifiers(self): - return True - - def supportsAlterTableWithAddColumn(self): - return True - - def supportsAlterTableWithDropColumn(self): - return True - - def supportsColumnAliasing(self): - return True - - def supportsConvert(self): - return True - - def supportsTableCorrelationNames(self): - return True - - def getConnection(self): - return Connection() - - # Constants - attributeNoNulls = 0 - attributeNullable = 1 - attributeNullableUnknown = 2 - bestRowTemporary = 0 - bestRowTransaction = 1 - bestRowSession = 2 - bestRowUnknown = 0 - bestRowNotPseudo = 1 - bestRowPseudo = 2 - columnNoNulls = 0 - columnNullable = 1 - columnNullableUnknown = 2 - functionColumnIn = 1 - functionColumnInOut = 2 - functionColumnOut = 3 - functionColumnReturn = 4 - functionColumnResult = 5 - functionNoNulls = 0 - functionNullable = 1 - functionNullableUnknown = 2 - functionResultUnknown = 0 - functionNoTable = 1 - functionReturnsTable = 2 - importedKeyCascade = 0 - importedKeyRestrict = 1 - importedKeySetNull = 2 - importedKeyNoAction = 3 - importedKeySetDefault = 4 - importedKeyInitiallyDeferred = 5 - importedKeyInitiallyImmediate = 6 - importedKeyNotDeferrable = 7 - procedureColumnIn = 1 - procedureColumnInOut = 2 - procedureColumnOut = 4 - procedureColumnReturn = 5 - procedureColumnResult = 3 - procedureNoNulls = 0 - procedureNullable = 1 - procedureNullableUnknown = 2 - procedureResultUnknown = 0 - sqlStateSQL = 2 - sqlStateXOpen = 1 - tableIndexStatistic = 0 - tableIndexClustered = 1 - tableIndexHashed = 2 - tableIndexOther = 3 - versionColumnUnknown = 0 - versionColumnNotPseudo = 1 - versionColumnPseudo = 2 - - -# ============================================================ -# IllegalArgumentException -# ============================================================ - -class IllegalArgumentException(RuntimeException): - """java.lang.IllegalArgumentException""" - pass \ No newline at end of file diff --git a/android_pyapi/java/text.py b/android_pyapi/java/text.py deleted file mode 100644 index 3e36ffa..0000000 --- a/android_pyapi/java/text.py +++ /dev/null @@ -1,1041 +0,0 @@ -""" -Java text standard library APIs implemented in Python. -java.text package - DateFormat, SimpleDateFormat, DecimalFormat, NumberFormat, etc. -""" - -from .._core import Stub -from .lang import RuntimeException -import datetime as _datetime -import time as _time -import re as _re -import math as _py_math -import locale as _locale -from decimal import Decimal as _Decimal - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "SimpleDateFormat", "DateFormat", "DateFormatSymbols", - "DecimalFormat", "DecimalFormatSymbols", "NumberFormat", - "ChoiceFormat", "MessageFormat", "FieldPosition", "ParsePosition", - "ParseException", "Normalizer", - "StringCharacterIterator", "CharacterIterator", - "BreakIterator", "Collator", "CollationKey", "RuleBasedCollator", - "Annotation", "AttributedCharacterIterator", "AttributedString", - "Bidi", "Format", -] - - -# ============================================================ -# ParseException -# ============================================================ - -class ParseException(Exception): - def __init__(self, s, error_offset): - super().__init__(s) - self._error_offset = error_offset - - def getErrorOffset(self): - return self._error_offset - - -# ============================================================ -# ParsePosition -# ============================================================ - -class ParsePosition(Stub): - def __init__(self, index): - super().__init__() - self._index = index - self._error_index = -1 - - def getIndex(self): - return self._index - - def setIndex(self, index): - self._index = index - - def getErrorIndex(self): - return self._error_index - - def setErrorIndex(self, ei): - self._error_index = ei - - -# ============================================================ -# FieldPosition -# ============================================================ - -class FieldPosition(Stub): - def __init__(self, field): - super().__init__() - self._field = field - self._begin = 0 - self._end = 0 - - def getField(self): - return self._field - - def getBeginIndex(self): - return self._begin - - def getEndIndex(self): - return self._end - - def setBeginIndex(self, bi): - self._begin = bi - - def setEndIndex(self, ei): - self._end = ei - - -# ============================================================ -# Format (base) -# ============================================================ - -class Format(Stub): - def format(self, obj, to_append_to=None, pos=None): - raise NotImplementedError - - def parseObject(self, source, pos=None): - raise NotImplementedError - - -# ============================================================ -# DateFormat -# ============================================================ - -class DateFormat(Format): - SHORT = 3 - MEDIUM = 2 - LONG = 1 - FULL = 0 - DEFAULT = 2 - - def __init__(self): - super().__init__() - self._calendar = None - self._number_format = None - self._lenient = False - self._timezone = None - - @staticmethod - def getDateInstance(style=2, locale=None): - return SimpleDateFormat("yyyy-MM-dd") - - @staticmethod - def getTimeInstance(style=2, locale=None): - return SimpleDateFormat("HH:mm:ss") - - @staticmethod - def getDateTimeInstance(date_style=2, time_style=2, locale=None): - return SimpleDateFormat("yyyy-MM-dd HH:mm:ss") - - @staticmethod - def getInstance(): - return SimpleDateFormat("yyyy-MM-dd HH:mm:ss") - - def format(self, date, to_append_to=None, pos=None): - raise NotImplementedError - - def parse(self, source, pos=None): - raise NotImplementedError - - def setLenient(self, lenient): - self._lenient = lenient - - def isLenient(self): - return self._lenient - - def getCalendar(self): - return self._calendar - - def setCalendar(self, cal): - self._calendar = cal - - def getNumberFormat(self): - return self._number_format - - def setNumberFormat(self, nf): - self._number_format = nf - - def getTimeZone(self): - return self._timezone - - def setTimeZone(self, tz): - self._timezone = tz - - -# ============================================================ -# DateFormatSymbols -# ============================================================ - -class DateFormatSymbols(Stub): - def __init__(self, locale=None): - super().__init__() - self._months = [ - "January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December", - ] - self._short_months = [ - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", - ] - self._weekdays = [ - "", "Sunday", "Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday", - ] - self._short_weekdays = [ - "", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", - ] - self._ampm = ["AM", "PM"] - self._eras = ["BC", "AD"] - self._zone_strings = [] - self._local_pattern_chars = "GyMdkHmsSEDFwWahKzZ" - - def getMonths(self): - return self._months - - def setMonths(self, months): - self._months = months - - def getShortMonths(self): - return self._short_months - - def setShortMonths(self, months): - self._short_months = months - - def getWeekdays(self): - return self._weekdays - - def setWeekdays(self, wd): - self._weekdays = wd - - def getShortWeekdays(self): - return self._short_weekdays - - def setShortWeekdays(self, wd): - self._short_weekdays = wd - - def getAmPmStrings(self): - return self._ampm - - def setAmPmStrings(self, ap): - self._ampm = ap - - def getZoneStrings(self): - return self._zone_strings - - def setZoneStrings(self, zs): - self._zone_strings = zs - - def getLocalPatternChars(self): - return self._local_pattern_chars - - def setLocalPatternChars(self, chars): - self._local_pattern_chars = chars - - def getEras(self): - return self._eras - - def setEras(self, eras): - self._eras = eras - - -# ============================================================ -# SimpleDateFormat -# ============================================================ - -_JAVA_TO_PYTHON_FORMAT = { - "yyyy": "%Y", "yy": "%y", - "MM": "%m", - "dd": "%d", - "HH": "%H", - "mm": "%M", - "ss": "%S", - "SSS": "%f", - "a": "%p", - "EEE": "%a", "EEEE": "%A", - "D": "%j", - "zzz": "%Z", "z": "%Z", - "Z": "%z", -} - -class SimpleDateFormat(DateFormat): - def __init__(self, pattern, locale=None): - super().__init__() - self._pattern = pattern - self._locale = locale - self._dfs = DateFormatSymbols(locale) - self._lenient = False - - def applyPattern(self, pattern): - self._pattern = pattern - - def toPattern(self): - return self._pattern - - def toLocalizedPattern(self): - return self._pattern - - def applyLocalizedPattern(self, pattern): - self._pattern = pattern - - def _java_to_py(self, pattern): - py_pattern = pattern - sorted_keys = sorted(_JAVA_TO_PYTHON_FORMAT.keys(), key=lambda x: -len(x)) - for java_fmt in sorted_keys: - py_fmt = _JAVA_TO_PYTHON_FORMAT[java_fmt] - py_pattern = py_pattern.replace(java_fmt, py_fmt) - py_pattern = py_pattern.replace("'", "") - return py_pattern - - def _to_datetime(self, date): - if isinstance(date, _datetime.datetime): - return date - if isinstance(date, _datetime.date): - return _datetime.datetime.combine(date, _datetime.time()) - try: - ts = date.getTime() / 1000.0 - return _datetime.datetime.fromtimestamp(ts) - except AttributeError: - return _datetime.datetime.now() - - def format(self, date, to_append_to=None, pos=None): - py_pattern = self._java_to_py(self._pattern) - dt = self._to_datetime(date) - result = dt.strftime(py_pattern) - if to_append_to is not None: - sb = str(to_append_to) - idx = len(sb) - to_append_to.append(result) - if pos is not None: - pos.setBeginIndex(idx) - pos.setEndIndex(idx + len(result)) - return to_append_to - return result - - def parse(self, source, pos=None): - py_pattern = self._java_to_py(self._pattern) - try: - dt = _datetime.datetime.strptime(source, py_pattern) - if pos is not None: - pos.setIndex(len(source)) - return dt - except ValueError: - if pos is not None: - pos.setErrorIndex(0) - raise ParseException(f"Unparseable date: {source}", 0) - - def setLenient(self, lenient): - self._lenient = lenient - - def isLenient(self): - return self._lenient - - def setTimeZone(self, tz): - self._timezone = tz - - def getTimeZone(self): - return self._timezone - - def setCalendar(self, cal): - self._calendar = cal - - def getCalendar(self): - return self._calendar - - def setNumberFormat(self, nf): - self._number_format = nf - - def getNumberFormat(self): - return self._number_format - - def getDateFormatSymbols(self): - return self._dfs - - def setDateFormatSymbols(self, dfs): - self._dfs = dfs - - -# ============================================================ -# DecimalFormatSymbols -# ============================================================ - -class DecimalFormatSymbols(Stub): - def __init__(self, locale=None): - super().__init__() - self._decimal_sep = "." - self._grouping_sep = "," - self._pattern_sep = ";" - self._minus_sign = "-" - self._infinity = "\u221e" - self._nan = "NaN" - self._percent = "%" - self._per_mill = "\u2030" - self._zero_digit = "0" - self._digit = "#" - self._exponential_sep = "E" - self._monetary_decimal_sep = "." - self._intl_currency_symbol = "USD" - self._currency = "USD" - - def getDecimalSeparator(self): return self._decimal_sep - def setDecimalSeparator(self, c): self._decimal_sep = c - def getGroupingSeparator(self): return self._grouping_sep - def setGroupingSeparator(self, c): self._grouping_sep = c - def getPatternSeparator(self): return self._pattern_sep - def setPatternSeparator(self, c): self._pattern_sep = c - def getMinusSign(self): return self._minus_sign - def setMinusSign(self, c): self._minus_sign = c - def getInfinity(self): return self._infinity - def setInfinity(self, s): self._infinity = s - def getNaN(self): return self._nan - def setNaN(self, s): self._nan = s - def getPercent(self): return self._percent - def setPercent(self, c): self._percent = c - def getPerMill(self): return self._per_mill - def setPerMill(self, c): self._per_mill = c - def getZeroDigit(self): return self._zero_digit - def setZeroDigit(self, c): self._zero_digit = c - def getDigit(self): return self._digit - def setDigit(self, c): self._digit = c - def getExponentialSeparator(self): return self._exponential_sep - def setExponentialSeparator(self, s): self._exponential_sep = s - def getMonetaryDecimalSeparator(self): return self._monetary_decimal_sep - def setMonetaryDecimalSeparator(self, c): self._monetary_decimal_sep = c - def getInternationalCurrencySymbol(self): return self._intl_currency_symbol - def setInternationalCurrencySymbol(self, s): self._intl_currency_symbol = s - def getCurrency(self): return self._currency - def setCurrency(self, c): self._currency = c - - -# ============================================================ -# DecimalFormat -# ============================================================ - -class NumberFormat(Format): - def __init__(self): - super().__init__() - self._grouping_used = True - self._max_int_digits = 2000000000 - self._min_int_digits = 1 - self._max_frac_digits = 3 - self._min_frac_digits = 0 - self._parse_integer_only = False - self._rounding_mode = 4 - - @staticmethod - def getInstance(locale=None): - return DecimalFormat("#,##0.###") - - @staticmethod - def getNumberInstance(locale=None): - return DecimalFormat("#,##0.###") - - @staticmethod - def getCurrencyInstance(locale=None): - return DecimalFormat("#,##0.00") - - @staticmethod - def getPercentInstance(locale=None): - return DecimalFormat("#,##0%") - - @staticmethod - def getIntegerInstance(locale=None): - nf = DecimalFormat("#,##0") - nf.setMaximumFractionDigits(0) - return nf - - @staticmethod - def getCompactNumberInstance(locale=None, style=None): - return DecimalFormat("#,##0") - - def format(self, number, to_append_to=None, pos=None): - raise NotImplementedError - - def parse(self, source, pos=None): - raise NotImplementedError - - def isGroupingUsed(self): return self._grouping_used - def setGroupingUsed(self, v): self._grouping_used = v - def getMaximumIntegerDigits(self): return self._max_int_digits - def setMaximumIntegerDigits(self, v): self._max_int_digits = v - def getMinimumIntegerDigits(self): return self._min_int_digits - def setMinimumIntegerDigits(self, v): self._min_int_digits = v - def getMaximumFractionDigits(self): return self._max_frac_digits - def setMaximumFractionDigits(self, v): self._max_frac_digits = v - def getMinimumFractionDigits(self): return self._min_frac_digits - def setMinimumFractionDigits(self, v): self._min_frac_digits = v - def setParseIntegerOnly(self, v): self._parse_integer_only = v - def isParseIntegerOnly(self): return self._parse_integer_only - def setRoundingMode(self, v): self._rounding_mode = v - def getRoundingMode(self): return self._rounding_mode - def getCurrency(self): return "USD" - def setCurrency(self, c): pass - - @staticmethod - def getAvailableLocales(): - return list(_locale.locale_alias.keys()) - - -# ============================================================ -# ChoiceFormat -# ============================================================ - -class ChoiceFormat(NumberFormat): - def __init__(self, formats=None, limits=None): - super().__init__() - self._formats = formats if formats is not None else [] - self._limits = limits if limits is not None else [] - - def format(self, number, to_append_to=None, pos=None): - val = float(number) - idx = 0 - for i, lim in enumerate(self._limits): - if val >= lim: - idx = i - result = str(self._formats[idx]) if idx < len(self._formats) else "" - if to_append_to is not None: - sb = str(to_append_to) - idx2 = len(sb) - to_append_to.append(result) - if pos is not None: - pos.setBeginIndex(idx2) - pos.setEndIndex(idx2 + len(result)) - return to_append_to - return result - - def parse(self, source, pos=None): - raise NotImplementedError - - @staticmethod - def nextDouble(d, upwards=True): - if upwards: - return d + _py_math.ulp(d) - return d - _py_math.ulp(d) - - @staticmethod - def previousDouble(d): - return ChoiceFormat.nextDouble(d, False) - - def getLimits(self): - return self._limits - - def getFormats(self): - return self._formats - - -# ============================================================ -# MessageFormat -# ============================================================ -# NumberFormat -# ============================================================ - -# ============================================================ -# DecimalFormat -# ============================================================ - -class DecimalFormat(NumberFormat): - def __init__(self, pattern=None, symbols=None): - super().__init__() - self._pattern = pattern or "#,##0.###" - self._symbols = symbols or DecimalFormatSymbols() - self._decimal_sep_always = False - self._grouping_size = 3 - self._grouping_used = True - self._max_int_digits = 2000000000 - self._min_int_digits = 1 - self._max_frac_digits = 2000000000 - self._min_frac_digits = 0 - self._neg_prefix = "-" - self._neg_suffix = "" - self._pos_prefix = "" - self._pos_suffix = "" - self._multiplier = 1 - self._rounding_mode = 4 - self._parse_bigdecimal = False - - def applyPattern(self, pattern): - self._pattern = pattern - - def toPattern(self): - return self._pattern - - def toLocalizedPattern(self): - return self._pattern - - def applyLocalizedPattern(self, pattern): - self._pattern = pattern - - def format(self, number, to_append_to=None, pos=None): - if isinstance(number, (int, float)): - val = number * self._multiplier - elif isinstance(number, _Decimal): - val = float(str(number)) * self._multiplier - else: - val = float(str(number)) * self._multiplier - if self._grouping_used and self._grouping_size > 0: - result = f"{val:,.{self._max_frac_digits}f}" - else: - fmt = "{{:.{}f}}".format(self._max_frac_digits) - result = fmt.format(val) - prefix = self._pos_prefix if val >= 0 else self._neg_prefix - suffix = self._pos_suffix if val >= 0 else self._neg_suffix - result = prefix + result + suffix - if to_append_to is not None: - sb = str(to_append_to) - idx = len(sb) - to_append_to.append(result) - if pos is not None: - pos.setBeginIndex(idx) - pos.setEndIndex(idx + len(result)) - return to_append_to - return result - - def parse(self, source, pos=None): - text = source.strip() - try: - val = float(text) - if pos is not None: - pos.setIndex(len(source)) - if self._parse_bigdecimal: - return _Decimal(str(val)) - return val - except ValueError: - if pos is not None: - pos.setErrorIndex(0) - raise Exception(f"Unparseable: {source}") - - def setDecimalSeparatorAlwaysShown(self, v): self._decimal_sep_always = v - def getDecimalSeparatorAlwaysShown(self): return self._decimal_sep_always - def setGroupingSize(self, v): self._grouping_size = v - def getGroupingSize(self): return self._grouping_size - def setGroupingUsed(self, v): self._grouping_used = v - def isGroupingUsed(self): return self._grouping_used - def setMaximumIntegerDigits(self, v): self._max_int_digits = v - def getMaximumIntegerDigits(self): return self._max_int_digits - def setMinimumIntegerDigits(self, v): self._min_int_digits = v - def getMinimumIntegerDigits(self): return self._min_int_digits - def setMaximumFractionDigits(self, v): self._max_frac_digits = v - def getMaximumFractionDigits(self): return self._max_frac_digits - def setMinimumFractionDigits(self, v): self._min_frac_digits = v - def getMinimumFractionDigits(self): return self._min_frac_digits - def setNegativePrefix(self, v): self._neg_prefix = v - def getNegativePrefix(self): return self._neg_prefix - def setNegativeSuffix(self, v): self._neg_suffix = v - def getNegativeSuffix(self): return self._neg_suffix - def setPositivePrefix(self, v): self._pos_prefix = v - def getPositivePrefix(self): return self._pos_prefix - def setPositiveSuffix(self, v): self._pos_suffix = v - def getPositiveSuffix(self): return self._pos_suffix - def setDecimalFormatSymbols(self, v): self._symbols = v - def getDecimalFormatSymbols(self): return self._symbols - def setParseBigDecimal(self, v): self._parse_bigdecimal = v - def isParseBigDecimal(self): return self._parse_bigdecimal - def setMultiplier(self, v): self._multiplier = v - def getMultiplier(self): return self._multiplier - def setRoundingMode(self, v): self._rounding_mode = v - def getRoundingMode(self): return self._rounding_mode - - -# ============================================================ -# MessageFormat -# ============================================================ - -_MSG_PATTERN_RE = _re.compile(r"\{(\d+)(?:,(\w+)(?:,(.+))?)?\}") - -class MessageFormat(Format): - def __init__(self, pattern, locale=None): - super().__init__() - self._pattern = pattern - self._locale = locale - self._formats = {} - - def applyPattern(self, pattern): - self._pattern = pattern - - def toPattern(self): - return self._pattern - - def _format_impl(self, objects): - def replacer(m): - idx = int(m.group(1)) - if idx < len(objects): - return str(objects[idx]) - return m.group(0) - return _MSG_PATTERN_RE.sub(replacer, self._pattern) - - def format(self, objects, to_append_to=None, pos=None): - result = self._format_impl(objects) - if to_append_to is not None: - sb = str(to_append_to) - idx = len(sb) - to_append_to.append(result) - if pos is not None: - pos.setBeginIndex(idx) - pos.setEndIndex(idx + len(result)) - return to_append_to - return result - - @staticmethod - def format_static(pattern, *args): - mf = MessageFormat(pattern) - return mf.format(args) - - def parse(self, source, pos=None): - parts = _MSG_PATTERN_RE.split(self._pattern) - result = [] - idx = 0 - for i, part in enumerate(parts): - if i % 2 == 0: - if not source[idx:].startswith(part): - if pos is not None: - pos.setErrorIndex(idx) - return result - idx += len(part) - else: - arg_idx = int(part) - remaining = source[idx:] - next_part = parts[i + 1] if i + 1 < len(parts) else "" - end = remaining.find(next_part) if next_part else len(remaining) - if end < 0: - end = len(remaining) - result.append(remaining[:end]) - idx += end - if pos is not None: - pos.setIndex(idx) - return result - - def setLocale(self, locale): - self._locale = locale - - def getLocale(self): - return self._locale - - def setFormats(self, formats): - self._formats = formats - - def getFormats(self): - return self._formats - - def setFormat(self, idx, fmt): - self._formats[idx] = fmt - - def getFormat(self, idx): - return self._formats.get(idx) - - -# ============================================================ -# CharacterIterator -# ============================================================ - -class CharacterIterator(Stub): - DONE = chr(0xFFFF) - - def first(self): - raise NotImplementedError - - def last(self): - raise NotImplementedError - - def current(self): - raise NotImplementedError - - def next(self): - raise NotImplementedError - - def previous(self): - raise NotImplementedError - - def setIndex(self, position): - raise NotImplementedError - - def getBeginIndex(self): - raise NotImplementedError - - def getEndIndex(self): - raise NotImplementedError - - def getIndex(self): - raise NotImplementedError - - def clone(self): - raise NotImplementedError - - -# ============================================================ -# StringCharacterIterator -# ============================================================ - -class StringCharacterIterator(CharacterIterator): - def __init__(self, text): - super().__init__() - self._text = text - self._begin = 0 - self._end = len(text) - self._pos = 0 - - def first(self): - self._pos = self._begin - return self.current() - - def last(self): - self._pos = self._end - 1 if self._end > self._begin else self._begin - return self.current() - - def current(self): - if self._pos >= self._begin and self._pos < self._end: - return self._text[self._pos] - return CharacterIterator.DONE - - def next(self): - self._pos += 1 - if self._pos >= self._end: - self._pos = self._end - return CharacterIterator.DONE - return self._text[self._pos] - - def previous(self): - self._pos -= 1 - if self._pos < self._begin: - self._pos = self._begin - return CharacterIterator.DONE - return self._text[self._pos] - - def setIndex(self, pos): - self._pos = max(self._begin, min(pos, self._end)) - return self.current() - - def getBeginIndex(self): - return self._begin - - def getEndIndex(self): - return self._end - - def getIndex(self): - return self._pos - - def clone(self): - return StringCharacterIterator(self._text) - - -# ============================================================ -# BreakIterator -# ============================================================ - -class BreakIterator(Stub): - DONE = -1 - - @staticmethod - def getWordInstance(locale=None): - return BreakIterator() - - @staticmethod - def getLineInstance(locale=None): - return BreakIterator() - - @staticmethod - def getCharacterInstance(locale=None): - return BreakIterator() - - @staticmethod - def getSentenceInstance(locale=None): - return BreakIterator() - - def setText(self, text): - self._text = text - - def first(self): - return 0 - - def last(self): - return len(self._text) if hasattr(self, "_text") else 0 - - def next(self, n=1): - return BreakIterator.DONE - - def previous(self): - return BreakIterator.DONE - - def current(self): - return 0 - - def following(self, offset): - return BreakIterator.DONE - - def preceding(self, offset): - return BreakIterator.DONE - - def isBoundary(self, offset): - return True - - -# ============================================================ -# Collator / CollationKey / RuleBasedCollator -# ============================================================ - -class CollationKey(Stub): - def __init__(self, source, key): - super().__init__() - self._source = source - self._key = key - - def compareTo(self, other): - if isinstance(other, CollationKey): - return (self._key > other._key) - (self._key < other._key) - return -1 - - def getSourceString(self): - return self._source - - def toByteArray(self): - return self._key.encode("utf-8") - - -class Collator(Stub): - PRIMARY = 0 - SECONDARY = 1 - TERTIARY = 2 - IDENTICAL = 3 - NO_DECOMPOSITION = 0 - CANONICAL_DECOMPOSITION = 1 - FULL_DECOMPOSITION = 2 - - @staticmethod - def getInstance(locale=None): - return RuleBasedCollator() - - def compare(self, source, target): - return (source > target) - (source < target) - - def getCollationKey(self, source): - return CollationKey(source, source) - - def setStrength(self, strength): - self._strength = strength - - def getStrength(self): - return getattr(self, "_strength", Collator.TERTIARY) - - def setDecomposition(self, decomposition): - self._decomposition = decomposition - - def getDecomposition(self): - return getattr(self, "_decomposition", Collator.NO_DECOMPOSITION) - - -class RuleBasedCollator(Collator): - def __init__(self, rules=None): - super().__init__() - self._rules = rules or "" - - -# ============================================================ -# Normalizer -# ============================================================ - -class Normalizer(Stub): - class Form(Stub): - NFC = "NFC" - NFD = "NFD" - NFKC = "NFKC" - NFKD = "NFKD" - - @staticmethod - def normalize(src, form): - import unicodedata - form_map = { - Normalizer.Form.NFC: "NFC", - Normalizer.Form.NFD: "NFD", - Normalizer.Form.NFKC: "NFKC", - Normalizer.Form.NFKD: "NFKD", - } - return unicodedata.normalize(form_map.get(form, "NFC"), src) - - @staticmethod - def isNormalized(src, form): - return Normalizer.normalize(src, form) == src - - -# ============================================================ -# Annotation / AttributedCharacterIterator / AttributedString -# ============================================================ - -class Annotation(Stub): - def __init__(self, value): - super().__init__() - self._value = value - - def getValue(self): - return self._value - - def toString(self): - return str(self._value) - - -class AttributedCharacterIterator(CharacterIterator): - pass - - -class AttributedString(Stub): - def __init__(self, text, attributes=None): - super().__init__() - self._text = text - - def getIterator(self): - return StringCharacterIterator(self._text) - - -# ============================================================ -# Bidi -# ============================================================ - -class Bidi(Stub): - DIRECTION_LEFT_TO_RIGHT = 0 - DIRECTION_RIGHT_TO_LEFT = 1 - DIRECTION_DEFAULT_LEFT_TO_RIGHT = -2 - DIRECTION_DEFAULT_RIGHT_TO_LEFT = -1 - - def __init__(self, text, flags=0): - super().__init__() - self._text = text - self._base_level = 0 if flags >= 0 else 1 - - def isLeftToRight(self): - return self._base_level == 0 - - def isRightToLeft(self): - return self._base_level == 1 - - def getLength(self): - return len(self._text) - - def getLevelAt(self, offset): - return self._base_level - - def getRunCount(self): - return 1 - - def getRunLevel(self, run): - return self._base_level - - def getRunLimit(self, run): - return len(self._text) - - def getRunStart(self, run): - return 0 - - def requiresBidi(self, text): - for ch in text: - if ord(ch) > 0x590: - return True - return False - - @staticmethod - def reorderVisually(levels, objects, index, count): - pass - - @staticmethod - def baseIsLeftToRight(): - return True \ No newline at end of file diff --git a/android_pyapi/java/time.py b/android_pyapi/java/time.py deleted file mode 100644 index c68f1cc..0000000 --- a/android_pyapi/java/time.py +++ /dev/null @@ -1,1619 +0,0 @@ -""" -Java time standard library APIs implemented in Python. -java.time package - LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Instant, Duration, Period, etc. -""" - -from .._core import Stub -from .lang import RuntimeException, Comparable -import datetime as _dt -import re as _re -import math as _py_math - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -__all__ = [ - "LocalDate", "LocalTime", "LocalDateTime", - "ZonedDateTime", "OffsetDateTime", "OffsetTime", - "Instant", "Duration", "Period", - "ZoneId", "ZoneOffset", "ZoneRules", - "DayOfWeek", "Month", "Year", "YearMonth", "MonthDay", - "Clock", - "DateTimeFormatter", "DateTimeFormatterBuilder", - "TemporalAdjusters", - "Temporal", "TemporalAccessor", "TemporalUnit", - "ChronoUnit", "ChronoField", -] - - -# ============================================================ -# TemporalAccessor / Temporal / TemporalUnit (base interfaces) -# ============================================================ - -class TemporalAccessor(Stub): - def get(self, field): - raise NotImplementedError - - def getLong(self, field): - raise NotImplementedError - - def isSupported(self, field): - raise NotImplementedError - - def range(self, field): - raise NotImplementedError - - -class Temporal(TemporalAccessor): - def plus(self, amount, unit=None): - raise NotImplementedError - - def minus(self, amount, unit=None): - raise NotImplementedError - - def with_(self, field, new_value): - raise NotImplementedError - - def until(self, end_exclusive, unit): - raise NotImplementedError - - -class TemporalUnit(Stub): - def getDuration(self): - raise NotImplementedError - - def isDurationEstimated(self): - raise NotImplementedError - - def isDateBased(self): - raise NotImplementedError - - def isTimeBased(self): - raise NotImplementedError - - def between(self, temporal1, temporal2): - raise NotImplementedError - - -# ============================================================ -# ChronoField -# ============================================================ - -class ChronoField(Stub): - NANO_OF_SECOND = "NanoOfSecond" - NANO_OF_DAY = "NanoOfDay" - MICRO_OF_SECOND = "MicroOfSecond" - MICRO_OF_DAY = "MicroOfDay" - MILLI_OF_SECOND = "MilliOfSecond" - MILLI_OF_DAY = "MilliOfDay" - SECOND_OF_MINUTE = "SecondOfMinute" - SECOND_OF_DAY = "SecondOfDay" - MINUTE_OF_HOUR = "MinuteOfHour" - MINUTE_OF_DAY = "MinuteOfDay" - HOUR_OF_AMPM = "HourOfAmPm" - CLOCK_HOUR_OF_AMPM = "ClockHourOfAmPm" - HOUR_OF_DAY = "HourOfDay" - CLOCK_HOUR_OF_DAY = "ClockHourOfDay" - AMPM_OF_DAY = "AmPmOfDay" - DAY_OF_WEEK = "DayOfWeek" - ALIGNED_DAY_OF_WEEK_IN_MONTH = "AlignedDayOfWeekInMonth" - ALIGNED_DAY_OF_WEEK_IN_YEAR = "AlignedDayOfWeekInYear" - DAY_OF_MONTH = "DayOfMonth" - DAY_OF_YEAR = "DayOfYear" - EPOCH_DAY = "EpochDay" - ALIGNED_WEEK_OF_MONTH = "AlignedWeekOfMonth" - ALIGNED_WEEK_OF_YEAR = "AlignedWeekOfYear" - MONTH_OF_YEAR = "MonthOfYear" - PROLEPTIC_MONTH = "ProlepticMonth" - YEAR_OF_ERA = "YearOfEra" - YEAR = "Year" - ERA = "Era" - INSTANT_SECONDS = "InstantSeconds" - OFFSET_SECONDS = "OffsetSeconds" - - def __init__(self, name): - super().__init__() - self._name = name - - def toString(self): - return self._name - - def getDisplayName(self, locale=None): - return self._name - - -# ============================================================ -# ChronoUnit -# ============================================================ - -class ChronoUnit(Stub): - NANOS = None - MICROS = None - MILLIS = None - SECONDS = None - MINUTES = None - HOURS = None - HALF_DAYS = None - DAYS = None - WEEKS = None - MONTHS = None - YEARS = None - DECADES = None - CENTURIES = None - MILLENNIA = None - ERAS = None - FOREVER = None - - def __init__(self, name, duration, estimated=False): - self._name = name - self._duration = duration - self._estimated = estimated - - def toString(self): - return self._name - - def getDuration(self): - return self._duration - - def isDurationEstimated(self): - return self._estimated - - def isDateBased(self): - return self in (ChronoUnit.WEEKS, ChronoUnit.MONTHS, ChronoUnit.YEARS, - ChronoUnit.DECADES, ChronoUnit.CENTURIES, - ChronoUnit.MILLENNIA, ChronoUnit.ERAS) - - def isTimeBased(self): - return self in (ChronoUnit.NANOS, ChronoUnit.MICROS, ChronoUnit.MILLIS, - ChronoUnit.SECONDS, ChronoUnit.MINUTES, ChronoUnit.HOURS, - ChronoUnit.HALF_DAYS) - - def between(self, temporal1, temporal2): - return temporal1.until(temporal2, self) - - @staticmethod - def from_(temporal): - if isinstance(temporal, ChronoUnit): - return temporal - raise Exception(f"Cannot convert to ChronoUnit: {temporal}") - - -# ChronoUnit 常量在 Duration 定义之后初始化(见下方) - - -# ============================================================ -# DayOfWeek -# ============================================================ - -class DayOfWeek(Stub): - MONDAY = None - TUESDAY = None - WEDNESDAY = None - THURSDAY = None - FRIDAY = None - SATURDAY = None - SUNDAY = None - - def __init__(self, value, name): - self._value = value - self._name = name - - def getValue(self): - return self._value - - def getDisplayName(self, style, locale=None): - return self._name - - def plus(self, days): - idx = ((self._value - 1 + days) % 7) + 1 - return DayOfWeek._from_value(idx) - - def minus(self, days): - return self.plus(-days) - - @staticmethod - def _from_value(v): - mapping = {1: DayOfWeek.MONDAY, 2: DayOfWeek.TUESDAY, 3: DayOfWeek.WEDNESDAY, - 4: DayOfWeek.THURSDAY, 5: DayOfWeek.FRIDAY, 6: DayOfWeek.SATURDAY, - 7: DayOfWeek.SUNDAY} - return mapping.get(v) - - def toString(self): - return self._name - - def compareTo(self, other): - if isinstance(other, DayOfWeek): - return self._value - other._value - return -1 - - -DayOfWeek.MONDAY = DayOfWeek(1, "MONDAY") -DayOfWeek.TUESDAY = DayOfWeek(2, "TUESDAY") -DayOfWeek.WEDNESDAY = DayOfWeek(3, "WEDNESDAY") -DayOfWeek.THURSDAY = DayOfWeek(4, "THURSDAY") -DayOfWeek.FRIDAY = DayOfWeek(5, "FRIDAY") -DayOfWeek.SATURDAY = DayOfWeek(6, "SATURDAY") -DayOfWeek.SUNDAY = DayOfWeek(7, "SUNDAY") - - -# ============================================================ -# Month -# ============================================================ - -class Month(Stub): - JANUARY = None - FEBRUARY = None - MARCH = None - APRIL = None - MAY = None - JUNE = None - JULY = None - AUGUST = None - SEPTEMBER = None - OCTOBER = None - NOVEMBER = None - DECEMBER = None - - def __init__(self, value, name): - self._value = value - self._name = name - - def getValue(self): - return self._value - - def getDisplayName(self, style, locale=None): - return self._name - - def plus(self, months): - idx = ((self._value - 1 + months) % 12) + 1 - return Month._from_value(idx) - - def minus(self, months): - return self.plus(-months) - - def length(self, leap_year=False): - if self._value == 2: - return 29 if leap_year else 28 - if self._value in (4, 6, 9, 11): - return 30 - return 31 - - def minLength(self): - return 29 if self._value == 2 else (30 if self._value in (4, 6, 9, 11) else 31) - - def maxLength(self): - return 29 if self._value == 2 else (30 if self._value in (4, 6, 9, 11) else 31) - - def firstDayOfYear(self, leap_year=False): - days = 0 - for m in range(1, self._value): - days += Month._from_value(m).length(leap_year) - return days + 1 - - def firstMonthOfQuarter(self): - q = (self._value - 1) // 3 - return Month._from_value(q * 3 + 1) - - @staticmethod - def _from_value(v): - values = [None, Month.JANUARY, Month.FEBRUARY, Month.MARCH, Month.APRIL, - Month.MAY, Month.JUNE, Month.JULY, Month.AUGUST, - Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER, Month.DECEMBER] - return values[v] if 0 < v <= 12 else None - - def toString(self): - return self._name - - def compareTo(self, other): - if isinstance(other, Month): - return self._value - other._value - return -1 - - -Month.JANUARY = Month(1, "JANUARY") -Month.FEBRUARY = Month(2, "FEBRUARY") -Month.MARCH = Month(3, "MARCH") -Month.APRIL = Month(4, "APRIL") -Month.MAY = Month(5, "MAY") -Month.JUNE = Month(6, "JUNE") -Month.JULY = Month(7, "JULY") -Month.AUGUST = Month(8, "AUGUST") -Month.SEPTEMBER = Month(9, "SEPTEMBER") -Month.OCTOBER = Month(10, "OCTOBER") -Month.NOVEMBER = Month(11, "NOVEMBER") -Month.DECEMBER = Month(12, "DECEMBER") - - -# ============================================================ -# ZoneId / ZoneOffset / ZoneRules -# ============================================================ - -class ZoneId(Stub): - SHORT_IDS = { - "ACT": "Australia/Darwin", - "AET": "Australia/Sydney", - "AGT": "America/Argentina/Buenos_Aires", - "ART": "Africa/Cairo", - "AST": "America/Anchorage", - "BET": "America/Sao_Paulo", - "BST": "Asia/Dhaka", - "CAT": "Africa/Harare", - "CNT": "America/St_Johns", - "CST": "America/Chicago", - "CTT": "Asia/Shanghai", - "EAT": "Africa/Addis_Ababa", - "ECT": "Europe/Paris", - "EST": "America/New_York", - "HST": "Pacific/Honolulu", - "IET": "America/Indianapolis", - "IST": "Asia/Kolkata", - "JST": "Asia/Tokyo", - "MIT": "Pacific/Apia", - "MST": "America/Denver", - "NET": "Asia/Yerevan", - "NST": "Pacific/Auckland", - "PLT": "Asia/Karachi", - "PNT": "America/Phoenix", - "PRT": "America/Puerto_Rico", - "PST": "America/Los_Angeles", - "SST": "Pacific/Guadalcanal", - "VST": "Asia/Ho_Chi_Minh", - } - - _cache = {} - - def __init__(self, zone_id): - super().__init__() - self._id = zone_id - - @staticmethod - def systemDefault(): - import time as _t - idx = 1 if _t.daylight else 0 - tz_name = _t.tzname[idx] if idx < len(_t.tzname) and _t.tzname[idx] else _t.tzname[0] - return ZoneId.of(tz_name) if tz_name else ZoneId("UTC") - - @staticmethod - def of(zone_id): - if zone_id in ZoneId.SHORT_IDS: - zone_id = ZoneId.SHORT_IDS[zone_id] - if zone_id not in ZoneId._cache: - ZoneId._cache[zone_id] = ZoneId(zone_id) - return ZoneId._cache[zone_id] - - @staticmethod - def getAvailableZoneIds(): - import time as _t - return set(_t.tzname) - - def getDisplayName(self, style, locale=None): - return self._id - - def getRules(self): - return ZoneRules() - - def toString(self): - return self._id - - def equals(self, other): - if isinstance(other, ZoneId): - return self._id == other._id - return False - - def hashCode(self): - return hash(self._id) - - -class ZoneOffset(ZoneId): - MAX = None - MIN = None - UTC = None - - _cache = {} - - def __init__(self, total_seconds): - hours = total_seconds // 3600 - mins = (abs(total_seconds) % 3600) // 60 - sign = "+" if total_seconds >= 0 else "-" - if mins == 0: - zone_id = f"{sign}{abs(hours):02d}:00" - else: - zone_id = f"{sign}{abs(hours):02d}:{mins:02d}" - super().__init__(zone_id) - self._total_seconds = total_seconds - - @staticmethod - def of(offset_id): - if offset_id in ZoneOffset._cache: - return ZoneOffset._cache[offset_id] - if offset_id == "Z" or offset_id == "UTC" or offset_id == "GMT": - z = ZoneOffset(0) - ZoneOffset._cache[offset_id] = z - return z - sign = 1 if offset_id[0] == "+" else -1 - parts = offset_id[1:].split(":") - h = int(parts[0]) - m = int(parts[1]) if len(parts) > 1 else 0 - total = sign * (h * 3600 + m * 60) - z = ZoneOffset(total) - ZoneOffset._cache[offset_id] = z - return z - - @staticmethod - def ofHours(hours): - return ZoneOffset(hours * 3600) - - @staticmethod - def ofHoursMinutes(hours, minutes): - sign = 1 if hours >= 0 else -1 - return ZoneOffset(sign * (abs(hours) * 3600 + minutes * 60)) - - @staticmethod - def ofTotalSeconds(total_seconds): - if total_seconds in ZoneOffset._cache: - return ZoneOffset._cache[total_seconds] - z = ZoneOffset(total_seconds) - ZoneOffset._cache[total_seconds] = z - return z - - def getTotalSeconds(self): - return self._total_seconds - - def toString(self): - if self._total_seconds == 0: - return "Z" - return super().toString() - - def equals(self, other): - if isinstance(other, ZoneOffset): - return self._total_seconds == other._total_seconds - return False - - def hashCode(self): - return self._total_seconds - - -ZoneOffset.MAX = ZoneOffset(18 * 3600) -ZoneOffset.MIN = ZoneOffset(-18 * 3600) -ZoneOffset.UTC = ZoneOffset(0) - - -class ZoneRules(Stub): - def isFixedOffset(self): - return True - - def getOffset(self, instant): - return ZoneOffset.UTC - - def getDaylightSavings(self, instant): - return Duration.ZERO - - def isDaylightSavings(self, instant): - return False - - def isValidOffset(self, local_date_time, offset): - return True - - def nextTransition(self, instant): - return None - - def previousTransition(self, instant): - return None - - def getTransitions(self): - return [] - - def getTransitionRules(self): - return [] - - -# ============================================================ -# Instant -# ============================================================ - -class Instant(Temporal, Comparable): - EPOCH = None - - def __init__(self, epoch_second, nano=0): - super().__init__() - self._epoch_second = int(epoch_second) - self._nano = int(nano) - - @staticmethod - def now(): - import time as _t - t = _t.time() - sec = int(t) - nano = int((t - sec) * 1_000_000_000) - return Instant(sec, nano) - - @staticmethod - def ofEpochMilli(epoch_milli): - return Instant(epoch_milli // 1000, (epoch_milli % 1000) * 1_000_000) - - @staticmethod - def ofEpochSecond(epoch_second, nano_adjustment=0): - return Instant(epoch_second + nano_adjustment // 1_000_000_000, - nano_adjustment % 1_000_000_000) - - def getEpochSecond(self): - return self._epoch_second - - def getNano(self): - return self._nano - - def toEpochMilli(self): - return self._epoch_second * 1000 + self._nano // 1_000_000 - - def plus(self, seconds, nano=0): - return Instant(self._epoch_second + seconds, self._nano + nano) - - def minus(self, seconds, nano=0): - return self.plus(-seconds, -nano) - - def isBefore(self, other): - if isinstance(other, Instant): - return (self._epoch_second, self._nano) < (other._epoch_second, other._nano) - return False - - def isAfter(self, other): - if isinstance(other, Instant): - return (self._epoch_second, self._nano) > (other._epoch_second, other._nano) - return False - - def compareTo(self, other): - if isinstance(other, Instant): - diff = self._epoch_second - other._epoch_second - if diff != 0: - return -1 if diff < 0 else 1 - diff = self._nano - other._nano - return -1 if diff < 0 else (1 if diff > 0 else 0) - return -1 - - def equals(self, other): - if isinstance(other, Instant): - return self._epoch_second == other._epoch_second and self._nano == other._nano - return False - - def hashCode(self): - return hash((self._epoch_second, self._nano)) - - def toString(self): - return f"Instant({self._epoch_second},{self._nano})" - - -Instant.EPOCH = Instant(0, 0) - - -# ============================================================ -# Duration -# ============================================================ - -class Duration(Temporal, Comparable): - ZERO = None - - def __init__(self, seconds, nanos=0): - super().__init__() - self._seconds = int(seconds) - self._nanos = int(nanos) - - @staticmethod - def ofDays(days): - return Duration(days * 86400) - - @staticmethod - def ofHours(hours): - return Duration(hours * 3600) - - @staticmethod - def ofMinutes(minutes): - return Duration(minutes * 60) - - @staticmethod - def ofSeconds(seconds): - return Duration(seconds) - - @staticmethod - def ofMillis(millis): - return Duration(millis // 1000, (millis % 1000) * 1_000_000) - - @staticmethod - def ofNanos(nanos): - return Duration(nanos // 1_000_000_000, nanos % 1_000_000_000) - - @staticmethod - def between(start, end): - if isinstance(start, Instant) and isinstance(end, Instant): - return Duration(end.getEpochSecond() - start.getEpochSecond(), - end.getNano() - start.getNano()) - return Duration.ZERO - - def getSeconds(self): - return self._seconds - - def getNano(self): - return self._nanos - - def toDays(self): - return self._seconds // 86400 - - def toHours(self): - return self._seconds // 3600 - - def toMinutes(self): - return self._seconds // 60 - - def toSeconds(self): - return self._seconds - - def toMillis(self): - return self._seconds * 1000 + self._nanos // 1_000_000 - - def toNanos(self): - return self._seconds * 1_000_000_000 + self._nanos - - def plus(self, duration): - if isinstance(duration, Duration): - return Duration(self._seconds + duration._seconds, - self._nanos + duration._nanos) - return self - - def minus(self, duration): - if isinstance(duration, Duration): - return Duration(self._seconds - duration._seconds, - self._nanos - duration._nanos) - return self - - def isNegative(self): - return self._seconds < 0 or (self._seconds == 0 and self._nanos < 0) - - def isZero(self): - return self._seconds == 0 and self._nanos == 0 - - def compareTo(self, other): - if isinstance(other, Duration): - diff = self._seconds - other._seconds - if diff != 0: - return -1 if diff < 0 else 1 - diff = self._nanos - other._nanos - return -1 if diff < 0 else (1 if diff > 0 else 0) - return -1 - - def equals(self, other): - if isinstance(other, Duration): - return self._seconds == other._seconds and self._nanos == other._nanos - return False - - def toString(self): - return f"PT{self._seconds}S" - - def hashCode(self): - return hash((self._seconds, self._nanos)) - - -Duration.ZERO = Duration(0, 0) - - -# === 延迟初始化 ChronoUnit 常量(必须在 Duration 定义之后) === -ChronoUnit.NANOS = ChronoUnit("Nanos", Duration.ofNanos(1)) -ChronoUnit.MICROS = ChronoUnit("Micros", Duration.ofNanos(1000)) -ChronoUnit.MILLIS = ChronoUnit("Millis", Duration.ofNanos(1_000_000)) -ChronoUnit.SECONDS = ChronoUnit("Seconds", Duration.ofSeconds(1)) -ChronoUnit.MINUTES = ChronoUnit("Minutes", Duration.ofMinutes(1)) -ChronoUnit.HOURS = ChronoUnit("Hours", Duration.ofHours(1)) -ChronoUnit.HALF_DAYS = ChronoUnit("HalfDays", Duration.ofHours(12)) -ChronoUnit.DAYS = ChronoUnit("Days", Duration.ofDays(1)) -ChronoUnit.WEEKS = ChronoUnit("Weeks", Duration.ofDays(7)) -ChronoUnit.MONTHS = ChronoUnit("Months", Duration.ofDays(30), estimated=True) -ChronoUnit.YEARS = ChronoUnit("Years", Duration.ofDays(365), estimated=True) -ChronoUnit.DECADES = ChronoUnit("Decades", Duration.ofDays(365 * 10), estimated=True) -ChronoUnit.CENTURIES = ChronoUnit("Centuries", Duration.ofDays(365 * 100), estimated=True) -ChronoUnit.MILLENNIA = ChronoUnit("Millennia", Duration.ofDays(365 * 1000), estimated=True) -ChronoUnit.ERAS = ChronoUnit("Eras", Duration.ofDays(365 * 1_000_000_000), estimated=True) -ChronoUnit.FOREVER = ChronoUnit("Forever", Duration.ofSeconds(1_000_000_000 * 86400 * 365), estimated=True) - - -# ============================================================ -# Period -# ============================================================ - -class Period(Stub): - ZERO = None - - def __init__(self, years, months, days): - super().__init__() - self._years = years - self._months = months - self._days = days - - @staticmethod - def ofDays(days): - return Period(0, 0, days) - - @staticmethod - def ofWeeks(weeks): - return Period(0, 0, weeks * 7) - - @staticmethod - def ofMonths(months): - return Period(0, months, 0) - - @staticmethod - def ofYears(years): - return Period(years, 0, 0) - - @staticmethod - def between(start, end): - if isinstance(start, LocalDate) and isinstance(end, LocalDate): - return Period(end._year - start._year, - end._month - start._month, - end._day - start._day) - return Period.ZERO - - def getYears(self): - return self._years - - def getMonths(self): - return self._months - - def getDays(self): - return self._days - - def plus(self, period): - if isinstance(period, Period): - return Period(self._years + period._years, - self._months + period._months, - self._days + period._days) - return self - - def minus(self, period): - if isinstance(period, Period): - return Period(self._years - period._years, - self._months - period._months, - self._days - period._days) - return self - - def isZero(self): - return self._years == 0 and self._months == 0 and self._days == 0 - - def isNegative(self): - return self._years < 0 or self._months < 0 or self._days < 0 - - def equals(self, other): - if isinstance(other, Period): - return (self._years == other._years and - self._months == other._months and - self._days == other._days) - return False - - def toString(self): - return f"P{self._years}Y{self._months}M{self._days}D" - - def hashCode(self): - return hash((self._years, self._months, self._days)) - - -Period.ZERO = Period(0, 0, 0) - - -# ============================================================ -# LocalDate -# ============================================================ - -class LocalDate(Temporal, Comparable): - MIN = None - MAX = None - - def __init__(self, year, month, day): - super().__init__() - self._year = year - self._month = month - self._day = day - - @staticmethod - def now(zone=None): - today = _dt.date.today() - return LocalDate(today.year, today.month, today.day) - - @staticmethod - def of(year, month, dayOfMonth): - if isinstance(month, Month): - month = month.getValue() - return LocalDate(year, month, dayOfMonth) - - @staticmethod - def parse(text, formatter=None): - if formatter is None: - parts = text.split("-") - return LocalDate(int(parts[0]), int(parts[1]), int(parts[2])) - return formatter.parse(text, LocalDate) - - def getYear(self): - return self._year - - def getMonth(self): - return Month._from_value(self._month) - - def getMonthValue(self): - return self._month - - def getDayOfMonth(self): - return self._day - - def getDayOfWeek(self): - import time as _t - import datetime as _dt2 - d = _dt2.date(self._year, self._month, self._day) - return DayOfWeek._from_value(d.isoweekday()) - - def getDayOfYear(self): - d = _dt.date(self._year, self._month, self._day) - return d.timetuple().tm_yday - - def getEra(self): - return "CE" if self._year > 0 else "BCE" - - def isLeapYear(self): - return _dt.date(self._year, 3, 1) - _dt.date(self._year, 2, 28) == _dt.timedelta(days=1) - - def lengthOfMonth(self): - m = self._month - if m == 2: - return 29 if self.isLeapYear() else 28 - if m in (4, 6, 9, 11): - return 30 - return 31 - - def lengthOfYear(self): - return 366 if self.isLeapYear() else 365 - - def toEpochDay(self): - d = _dt.date(self._year, self._month, self._day) - return d.toordinal() - - def plusDays(self, days): - d = _dt.date(self._year, self._month, self._day) + _dt.timedelta(days=days) - return LocalDate(d.year, d.month, d.day) - - def plusWeeks(self, weeks): - return self.plusDays(weeks * 7) - - def plusMonths(self, months): - total = self._year * 12 + (self._month - 1) + months - y = total // 12 - m = (total % 12) + 1 - d = min(self._day, 29 if m == 2 and _dt.date(y, 3, 1) - _dt.date(y, 2, 28) == _dt.timedelta(days=1) else - 30 if m in (4, 6, 9, 11) else 31) - return LocalDate(y, m, d) - - def plusYears(self, years): - return self.plusMonths(years * 12) - - def minusDays(self, days): - return self.plusDays(-days) - - def minusWeeks(self, weeks): - return self.plusDays(-weeks * 7) - - def minusMonths(self, months): - return self.plusMonths(-months) - - def minusYears(self, years): - return self.plusYears(-years) - - def isBefore(self, other): - if isinstance(other, LocalDate): - return (self._year, self._month, self._day) < (other._year, other._month, other._day) - return False - - def isAfter(self, other): - if isinstance(other, LocalDate): - return (self._year, self._month, self._day) > (other._year, other._month, other._day) - return False - - def isEqual(self, other): - if isinstance(other, LocalDate): - return (self._year, self._month, self._day) == (other._year, other._month, other._day) - return False - - def withDayOfMonth(self, day): - return LocalDate(self._year, self._month, day) - - def withMonth(self, month): - if isinstance(month, Month): - month = month.getValue() - return LocalDate(self._year, month, self._day) - - def withYear(self, year): - return LocalDate(year, self._month, self._day) - - def atTime(self, hour, minute=0, second=0, nano=0): - return LocalDateTime(self, LocalTime(hour, minute, second, nano)) - - def atStartOfDay(self, zone=None): - return LocalDateTime(self, LocalTime.MIDNIGHT) - - def format(self, formatter): - return formatter.format(self) - - def compareTo(self, other): - if isinstance(other, LocalDate): - v = self._year - other._year - if v != 0: - return -1 if v < 0 else 1 - v = self._month - other._month - if v != 0: - return -1 if v < 0 else 1 - v = self._day - other._day - return -1 if v < 0 else (1 if v > 0 else 0) - return -1 - - def equals(self, other): - if isinstance(other, LocalDate): - return (self._year, self._month, self._day) == (other._year, other._month, other._day) - return False - - def toString(self): - return f"{self._year:04d}-{self._month:02d}-{self._day:02d}" - - def hashCode(self): - return hash((self._year, self._month, self._day)) - - -LocalDate.MIN = LocalDate(-999999999, 1, 1) -LocalDate.MAX = LocalDate(999999999, 12, 31) - - -# ============================================================ -# LocalTime -# ============================================================ - -class LocalTime(Temporal, Comparable): - MIDNIGHT = None - NOON = None - MIN = None - MAX = None - - def __init__(self, hour, minute=0, second=0, nano=0): - super().__init__() - self._hour = hour - self._minute = minute - self._second = second - self._nano = nano - - @staticmethod - def now(zone=None): - t = _dt.datetime.now().time() - return LocalTime(t.hour, t.minute, t.second, t.microsecond * 1000) - - @staticmethod - def of(hour, minute=0, second=0, nano=0): - return LocalTime(hour, minute, second, nano) - - @staticmethod - def parse(text, formatter=None): - parts = text.split(":") - h = int(parts[0]) - m = int(parts[1]) - s = int(parts[2]) if len(parts) > 2 else 0 - return LocalTime(h, m, s) - - def getHour(self): - return self._hour - - def getMinute(self): - return self._minute - - def getSecond(self): - return self._second - - def getNano(self): - return self._nano - - def toSecondOfDay(self): - return self._hour * 3600 + self._minute * 60 + self._second - - def toNanoOfDay(self): - return self.toSecondOfDay() * 1_000_000_000 + self._nano - - def plusHours(self, h): - total = self._hour + h - return LocalTime(total % 24, self._minute, self._second, self._nano) - - def plusMinutes(self, m): - total = self.toSecondOfDay() + m * 60 - total %= 86400 - h = total // 3600 - total %= 3600 - mi = total // 60 - s = total % 60 - return LocalTime(h, mi, s, self._nano) - - def plusSeconds(self, s): - total = self.toSecondOfDay() + s - total %= 86400 - h = total // 3600 - total %= 3600 - mi = total // 60 - sec = total % 60 - return LocalTime(h, mi, sec, self._nano) - - def plusNanos(self, n): - return LocalTime(self._hour, self._minute, self._second, self._nano + n) - - def minusHours(self, h): - return self.plusHours(-h) - - def minusMinutes(self, m): - return self.plusMinutes(-m) - - def minusSeconds(self, s): - return self.plusSeconds(-s) - - def withHour(self, h): - return LocalTime(h, self._minute, self._second, self._nano) - - def withMinute(self, m): - return LocalTime(self._hour, m, self._second, self._nano) - - def withSecond(self, s): - return LocalTime(self._hour, self._minute, s, self._nano) - - def withNano(self, n): - return LocalTime(self._hour, self._minute, self._second, n) - - def atDate(self, date): - return LocalDateTime(date, self) - - def isBefore(self, other): - if isinstance(other, LocalTime): - return self.toNanoOfDay() < other.toNanoOfDay() - return False - - def isAfter(self, other): - if isinstance(other, LocalTime): - return self.toNanoOfDay() > other.toNanoOfDay() - return False - - def format(self, formatter): - return formatter.format(self) - - def compareTo(self, other): - if isinstance(other, LocalTime): - diff = self.toNanoOfDay() - other.toNanoOfDay() - return -1 if diff < 0 else (1 if diff > 0 else 0) - return -1 - - def equals(self, other): - if isinstance(other, LocalTime): - return (self._hour, self._minute, self._second, self._nano) == \ - (other._hour, other._minute, other._second, other._nano) - return False - - def toString(self): - return f"{self._hour:02d}:{self._minute:02d}:{self._second:02d}" - - def hashCode(self): - return hash((self._hour, self._minute, self._second, self._nano)) - - -LocalTime.MIDNIGHT = LocalTime(0, 0, 0, 0) -LocalTime.NOON = LocalTime(12, 0, 0, 0) -LocalTime.MIN = LocalTime(0, 0, 0, 0) -LocalTime.MAX = LocalTime(23, 59, 59, 999999999) - - -# ============================================================ -# LocalDateTime -# ============================================================ - -class LocalDateTime(Temporal, Comparable): - MIN = None - MAX = None - - def __init__(self, date, time): - super().__init__() - self._date = date - self._time = time - - @staticmethod - def now(zone=None): - now = _dt.datetime.now() - date = LocalDate(now.year, now.month, now.day) - time = LocalTime(now.hour, now.minute, now.second, now.microsecond * 1000) - return LocalDateTime(date, time) - - @staticmethod - def of(date_or_year, month=None, dayOfMonth=None, hour=None, minute=0, second=0, nano=0): - if isinstance(date_or_year, LocalDate): - return LocalDateTime(date_or_year, LocalTime(month or 0, dayOfMonth or 0, hour or 0)) - return LocalDateTime(LocalDate(date_or_year, month, dayOfMonth), - LocalTime(hour, minute, second, nano)) - - @staticmethod - def parse(text, formatter=None): - parts = text.split("T") - date = LocalDate.parse(parts[0]) - time = LocalTime.parse(parts[1]) if len(parts) > 1 else LocalTime.MIDNIGHT - return LocalDateTime(date, time) - - def toLocalDate(self): - return self._date - - def toLocalTime(self): - return self._time - - def getYear(self): - return self._date.getYear() - - def getMonth(self): - return self._date.getMonth() - - def getMonthValue(self): - return self._date.getMonthValue() - - def getDayOfMonth(self): - return self._date.getDayOfMonth() - - def getDayOfWeek(self): - return self._date.getDayOfWeek() - - def getDayOfYear(self): - return self._date.getDayOfYear() - - def getHour(self): - return self._time.getHour() - - def getMinute(self): - return self._time.getMinute() - - def getSecond(self): - return self._time.getSecond() - - def getNano(self): - return self._time.getNano() - - def plusDays(self, d): - return LocalDateTime(self._date.plusDays(d), self._time) - - def plusHours(self, h): - return LocalDateTime(self._date, self._time.plusHours(h)) - - def plusMinutes(self, m): - return LocalDateTime(self._date, self._time.plusMinutes(m)) - - def plusSeconds(self, s): - return LocalDateTime(self._date, self._time.plusSeconds(s)) - - def minusDays(self, d): - return self.plusDays(-d) - - def minusHours(self, h): - return self.plusHours(-h) - - def minusMinutes(self, m): - return self.plusMinutes(-m) - - def minusSeconds(self, s): - return self.plusSeconds(-s) - - def withYear(self, y): - return LocalDateTime(self._date.withYear(y), self._time) - - def withMonth(self, m): - return LocalDateTime(self._date.withMonth(m), self._time) - - def withDayOfMonth(self, d): - return LocalDateTime(self._date.withDayOfMonth(d), self._time) - - def atZone(self, zone): - return ZonedDateTime(self, zone) - - def isBefore(self, other): - if isinstance(other, LocalDateTime): - if not self._date.isEqual(other._date): - return self._date.isBefore(other._date) - return self._time.isBefore(other._time) - return False - - def isAfter(self, other): - if isinstance(other, LocalDateTime): - if not self._date.isEqual(other._date): - return self._date.isAfter(other._date) - return self._time.isAfter(other._time) - return False - - def format(self, formatter): - return formatter.format(self) - - def compareTo(self, other): - if isinstance(other, LocalDateTime): - c = self._date.compareTo(other._date) - return c if c != 0 else self._time.compareTo(other._time) - return -1 - - def equals(self, other): - if isinstance(other, LocalDateTime): - return self._date.equals(other._date) and self._time.equals(other._time) - return False - - def toString(self): - return f"{self._date}T{self._time}" - - def hashCode(self): - return hash((self._date, self._time)) - - -LocalDateTime.MIN = LocalDateTime(LocalDate.MIN, LocalTime.MIN) -LocalDateTime.MAX = LocalDateTime(LocalDate.MAX, LocalTime.MAX) - - -# ============================================================ -# ZonedDateTime -# ============================================================ - -class ZonedDateTime(Temporal, Comparable): - def __init__(self, date_time, zone): - super().__init__() - self._date_time = date_time - self._zone = zone - - @staticmethod - def now(zone=None): - ldt = LocalDateTime.now() - z = zone or ZoneId.systemDefault() - return ZonedDateTime(ldt, z) - - @staticmethod - def of(date, time=None, zone=None, offset=None): - if isinstance(date, LocalDateTime): - return ZonedDateTime(date, time or ZoneId.systemDefault()) - return ZonedDateTime(LocalDateTime.of(date, time), zone) - - @staticmethod - def parse(text, formatter=None): - return ZonedDateTime(LocalDateTime.now(), ZoneId.systemDefault()) - - def getZone(self): - return self._zone - - def getOffset(self): - return ZoneOffset.UTC - - def withZoneSameInstant(self, zone): - return ZonedDateTime(self._date_time, zone) - - def withZoneSameLocal(self, zone): - return ZonedDateTime(self._date_time, zone) - - def toLocalDate(self): - return self._date_time.toLocalDate() - - def toLocalTime(self): - return self._date_time.toLocalTime() - - def toLocalDateTime(self): - return self._date_time - - def toInstant(self): - return Instant.now() - - def plusDays(self, d): - return ZonedDateTime(self._date_time.plusDays(d), self._zone) - - def plusHours(self, h): - return ZonedDateTime(self._date_time.plusHours(h), self._zone) - - def minusDays(self, d): - return self.plusDays(-d) - - def minusHours(self, h): - return self.plusHours(-h) - - def format(self, formatter): - return formatter.format(self) - - def compareTo(self, other): - if isinstance(other, ZonedDateTime): - return self._date_time.compareTo(other._date_time) - return -1 - - def equals(self, other): - if isinstance(other, ZonedDateTime): - return self._date_time.equals(other._date_time) and str(self._zone) == str(other._zone) - return False - - def toString(self): - return f"{self._date_time}[{self._zone}]" - - def hashCode(self): - return hash((self._date_time, str(self._zone))) - - -# ============================================================ -# OffsetDateTime / OffsetTime (stubs) -# ============================================================ - -class OffsetDateTime(Temporal): - def __init__(self, date_time, offset): - super().__init__() - self._date_time = date_time - self._offset = offset - - @staticmethod - def now(): - return OffsetDateTime(LocalDateTime.now(), ZoneOffset.UTC) - - def toLocalDate(self): - return self._date_time.toLocalDate() - - def toLocalTime(self): - return self._date_time.toLocalTime() - - def toString(self): - return f"{self._date_time}{self._offset}" - - -class OffsetTime(Stub): - def __init__(self, time, offset): - super().__init__() - self._time = time - self._offset = offset - - @staticmethod - def now(): - return OffsetTime(LocalTime.now(), ZoneOffset.UTC) - - def toString(self): - return f"{self._time}{self._offset}" - - -# ============================================================ -# Year / YearMonth / MonthDay -# ============================================================ - -class Year(Temporal, Comparable): - def __init__(self, year): - super().__init__() - self._year = year - - @staticmethod - def now(): - return Year(_dt.date.today().year) - - @staticmethod - def of(year): - return Year(year) - - def getValue(self): - return self._year - - def isLeap(self): - return _dt.date(self._year, 3, 1) - _dt.date(self._year, 2, 28) == _dt.timedelta(days=1) - - def length(self): - return 366 if self.isLeap() else 365 - - def atMonth(self, month): - if isinstance(month, Month): - month = month.getValue() - return YearMonth(self._year, month) - - def compareTo(self, other): - if isinstance(other, Year): - return -1 if self._year < other._year else (1 if self._year > other._year else 0) - return -1 - - def toString(self): - return str(self._year) - - -class YearMonth(Temporal, Comparable): - def __init__(self, year, month): - super().__init__() - self._year = year - self._month = month - - @staticmethod - def now(): - d = _dt.date.today() - return YearMonth(d.year, d.month) - - @staticmethod - def of(year, month): - return YearMonth(year, month) - - def getYear(self): - return self._year - - def getMonth(self): - return self._month - - def getMonthValue(self): - return self._month if isinstance(self._month, int) else self._month.getValue() - - def lengthOfMonth(self): - m = self.getMonthValue() - if m == 2: - return 29 if _dt.date(self._year, 3, 1) - _dt.date(self._year, 2, 28) == _dt.timedelta(days=1) else 28 - return 30 if m in (4, 6, 9, 11) else 31 - - def lengthOfYear(self): - return 366 if _dt.date(self._year, 3, 1) - _dt.date(self._year, 2, 28) == _dt.timedelta(days=1) else 365 - - def atDay(self, day): - return LocalDate(self._year, self.getMonthValue(), day) - - def compareTo(self, other): - if isinstance(other, YearMonth): - diff = self._year - other._year - if diff != 0: - return -1 if diff < 0 else 1 - diff = self.getMonthValue() - other.getMonthValue() - return -1 if diff < 0 else (1 if diff > 0 else 0) - return -1 - - def toString(self): - return f"{self._year}-{self.getMonthValue():02d}" - - -class MonthDay(Stub): - def __init__(self, month, day): - super().__init__() - self._month = month - self._day = day - - @staticmethod - def now(): - d = _dt.date.today() - return MonthDay(d.month, d.day) - - @staticmethod - def of(month, day): - return MonthDay(month, day) - - def getMonth(self): - return self._month - - def getMonthValue(self): - return self._month if isinstance(self._month, int) else self._month.getValue() - - def getDayOfMonth(self): - return self._day - - def atYear(self, year): - return LocalDate.of(year, self.getMonthValue(), self._day) - - def toString(self): - return f"--{self.getMonthValue():02d}-{self._day:02d}" - - -# ============================================================ -# Clock -# ============================================================ - -class Clock(Stub): - @staticmethod - def systemUTC(): - return Clock() - - @staticmethod - def systemDefaultZone(): - return Clock() - - @staticmethod - def system(zone): - return Clock() - - def millis(self): - import time as _t - return int(_t.time() * 1000) - - def instant(self): - return Instant.now() - - def getZone(self): - return ZoneId.systemDefault() - - -# ============================================================ -# DateTimeFormatter -# ============================================================ - -class DateTimeFormatter(Stub): - ISO_LOCAL_DATE = None - ISO_LOCAL_TIME = None - ISO_LOCAL_DATE_TIME = None - ISO_INSTANT = None - ISO_ZONED_DATE_TIME = None - ISO_OFFSET_DATE_TIME = None - - def __init__(self, pattern): - super().__init__() - self._pattern = pattern - - @staticmethod - def ofPattern(pattern): - return DateTimeFormatter(pattern) - - def format(self, temporal): - if isinstance(temporal, LocalDate): - return temporal.toString() - if isinstance(temporal, LocalTime): - return temporal.toString() - if isinstance(temporal, LocalDateTime): - return temporal.toString() - if isinstance(temporal, ZonedDateTime): - return temporal.toString() - if isinstance(temporal, Instant): - return temporal.toString() - return str(temporal) - - def parse(self, text, query=None): - if query is LocalDate or query is None: - return LocalDate.parse(text) - if query is LocalTime: - return LocalTime.parse(text) - if query is LocalDateTime: - return LocalDateTime.parse(text) - return text - - -DateTimeFormatter.ISO_LOCAL_DATE = DateTimeFormatter("yyyy-MM-dd") -DateTimeFormatter.ISO_LOCAL_TIME = DateTimeFormatter("HH:mm:ss") -DateTimeFormatter.ISO_LOCAL_DATE_TIME = DateTimeFormatter("yyyy-MM-ddTHH:mm:ss") -DateTimeFormatter.ISO_INSTANT = DateTimeFormatter("yyyy-MM-ddTHH:mm:ssZ") -DateTimeFormatter.ISO_ZONED_DATE_TIME = DateTimeFormatter("yyyy-MM-ddTHH:mm:ssZ") -DateTimeFormatter.ISO_OFFSET_DATE_TIME = DateTimeFormatter("yyyy-MM-ddTHH:mm:ssZ") - - -# ============================================================ -# DateTimeFormatterBuilder (stub) -# ============================================================ - -class DateTimeFormatterBuilder(Stub): - def __init__(self): - super().__init__() - self._pattern_parts = [] - - def appendValue(self, field, width=1): - return self - - def appendLiteral(self, literal): - return self - - def appendPattern(self, pattern): - self._pattern_parts.append(pattern) - return self - - def toFormatter(self): - return DateTimeFormatter("".join(self._pattern_parts)) - - -# ============================================================ -# TemporalAdjusters (stub) -# ============================================================ - -class TemporalAdjusters(Stub): - @staticmethod - def firstDayOfMonth(): - return lambda d: d.withDayOfMonth(1) - - @staticmethod - def lastDayOfMonth(): - return lambda d: d.withDayOfMonth(d.lengthOfMonth()) - - @staticmethod - def firstDayOfYear(): - return lambda d: d.withDayOfMonth(1).withMonth(1) - - @staticmethod - def lastDayOfYear(): - return lambda d: d.withDayOfMonth(31).withMonth(12) - - @staticmethod - def firstInMonth(day_of_week): - return lambda d: d - - @staticmethod - def next(day_of_week): - return lambda d: d.plusDays(1) \ No newline at end of file diff --git a/android_pyapi/java/util.py b/android_pyapi/java/util.py deleted file mode 100644 index dd61244..0000000 --- a/android_pyapi/java/util.py +++ /dev/null @@ -1,4096 +0,0 @@ -""" -Java java.util standard library APIs implemented in Python. -Android-pyApi: desktop Python translation of Android Java APIs. -""" - -from .._core import Stub -from .lang import RuntimeException -import heapq as _heapq -import bisect as _bisect -import datetime as _datetime -import uuid as _uuid -import random as _py_random -import math as _py_math -import copy as _copy -import re as _re -import io as _io -import threading as _threading -import time as _time -from typing import Any, Dict, List, Callable, Tuple, Iterable as _IterableType -import typing as _typing -from collections.abc import Iterable as _IterableABC - -__all__ = [ - "ArrayList", "LinkedList", "HashMap", "LinkedHashMap", "TreeMap", - "HashSet", "TreeSet", "LinkedHashSet", - "Collections", "Arrays", "Date", "Calendar", "GregorianCalendar", - "UUID", "StringTokenizer", "Optional", "Random", - "Properties", "EnumSet", "EnumMap", - "Iterator", "ListIterator", "Enumeration", - "Objects", "Locale", "BitSet", "PriorityQueue", - "Vector", "Stack", - "ConcurrentModificationException", "NoSuchElementException", - "EmptyStackException", "TooManyListenersException", - "MissingResourceException", "IllegalFormatException", - "DuplicateFormatFlagsException", "FormatFlagsConversionMismatchException", - "FormatterClosedException", -] - -# ============================================================ -# Exceptions -# ============================================================ - -# Define exception classes in correct dependency order. -# IllegalArgumentException comes before IllegalFormatException. -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) - -ConcurrentModificationException = type( - "ConcurrentModificationException", (RuntimeException,), {} -) -NoSuchElementException = type("NoSuchElementException", (RuntimeException,), {}) -EmptyStackException = type("EmptyStackException", (RuntimeException,), {}) -TooManyListenersException = type("TooManyListenersException", (Exception,), {}) -MissingResourceException = type("MissingResourceException", (RuntimeException,), {}) -IllegalFormatException = type("IllegalFormatException", (IllegalArgumentException,), {}) -DuplicateFormatFlagsException = type( - "DuplicateFormatFlagsException", (IllegalFormatException,), {} -) -FormatFlagsConversionMismatchException = type( - "FormatFlagsConversionMismatchException", (IllegalFormatException,), {} -) - -# We need IllegalStateException for FormatterClosedException -from .lang import RuntimeException as _RuntimeException -IllegalStateException = type("IllegalStateException", (_RuntimeException,), {}) - -FormatterClosedException = type("FormatterClosedException", (IllegalStateException,), {}) -UnsupportedOperationException = type("UnsupportedOperationException", (RuntimeException,), {}) - -# ============================================================ -# Iterator / ListIterator / Enumeration -# ============================================================ - -class Iterator(Stub): - """java.util.Iterator""" - def __init__(self, data): - super().__init__() - self._data = list(data) - self._index = 0 - self._mod_count = 0 - - def hasNext(self) -> bool: - return self._index < len(self._data) - - def next(self): - if not self.hasNext(): - raise NoSuchElementException() - val = self._data[self._index] - self._index += 1 - return val - - def remove(self): - raise UnsupportedOperationException("remove") - - def forEachRemaining(self, action): - while self.hasNext(): - action(self.next()) - - -class ListIterator(Iterator): - """java.util.ListIterator""" - def __init__(self, data): - super().__init__(data) - - def hasPrevious(self) -> bool: - return self._index > 0 - - def previous(self): - if not self.hasPrevious(): - raise NoSuchElementException() - self._index -= 1 - return self._data[self._index] - - def nextIndex(self) -> int: - return self._index - - def previousIndex(self) -> int: - return self._index - 1 - - def set(self, obj): - self._data[self._index - 1] = obj - - def add(self, obj): - self._data.insert(self._index, obj) - self._index += 1 - - -class Enumeration(Stub): - """java.util.Enumeration""" - def __init__(self, data): - super().__init__() - self._it = iter(data) - - def hasMoreElements(self) -> bool: - try: - self._next = next(self._it) - return True - except StopIteration: - return False - - def nextElement(self): - if hasattr(self, '_next'): - val = self._next - del self._next - return val - return next(self._it) - - def asIterator(self) -> Iterator: - return Iterator(self._it) - - -# ============================================================ -# Stream / Spliterator helpers (lightweight, stdlib-backed) -# ============================================================ - -class _Spliterator(Stub): - """Lightweight java.util.Spliterator backed by a Python sequence.""" - ORDERED = 0x00000010 - DISTINCT = 0x00000001 - SORTED = 0x00000004 - SIZED = 0x00000040 - NONNULL = 0x00000100 - IMMUTABLE = 0x00000400 - CONCURRENT = 0x00001000 - SUBSIZED = 0x00004000 - - def __init__(self, data, characteristics: int = 0): - super().__init__() - self._data = list(data) - self._index = 0 - self._characteristics = (characteristics - | _Spliterator.SIZED - | _Spliterator.SUBSIZED) - - def tryAdvance(self, action) -> bool: - if self._index < len(self._data): - action(self._data[self._index]) - self._index += 1 - return True - return False - - def forEachRemaining(self, action) -> None: - while self._index < len(self._data): - action(self._data[self._index]) - self._index += 1 - - def trySplit(self): - mid = (self._index + len(self._data)) // 2 - if self._index >= mid: - return None - split = _Spliterator(self._data[self._index:mid], - self._characteristics) - self._index = mid - return split - - def estimateSize(self) -> int: - return len(self._data) - self._index - - def getExactSizeIfKnown(self) -> int: - return self.estimateSize() - - def characteristics(self) -> int: - return self._characteristics - - def hasCharacteristics(self, characteristics: int) -> bool: - return (self._characteristics & characteristics) == characteristics - - def iterator(self) -> Iterator: - return Iterator(self._data[self._index:]) - - -class _Stream(Stub): - """Lightweight java.util.stream.Stream wrapper over a Python list.""" - def __init__(self, data): - super().__init__() - self._data = list(data) - self._parallel = False - - def iterator(self) -> Iterator: - return Iterator(self._data) - - def spliterator(self) -> _Spliterator: - return _Spliterator(self._data) - - def isParallel(self) -> bool: - return self._parallel - - def parallel(self) -> '_Stream': - self._parallel = True - return self - - def sequential(self) -> '_Stream': - self._parallel = False - return self - - def unordered(self) -> '_Stream': - return self - - def filter(self, predicate) -> '_Stream': - return _Stream([x for x in self._data if predicate(x)]) - - def map(self, mapper) -> '_Stream': - return _Stream([mapper(x) for x in self._data]) - - def mapToInt(self, mapper) -> '_Stream': - return _Stream([mapper(x) for x in self._data]) - - def mapToLong(self, mapper) -> '_Stream': - return _Stream([mapper(x) for x in self._data]) - - def mapToDouble(self, mapper) -> '_Stream': - return _Stream([mapper(x) for x in self._data]) - - def flatMap(self, mapper) -> '_Stream': - result = [] - for x in self._data: - part = mapper(x) - if isinstance(part, (list, tuple)): - result.extend(part) - else: - result.extend(list(part)) - return _Stream(result) - - def peek(self, action) -> '_Stream': - for x in self._data: - action(x) - return self - - def distinct(self) -> '_Stream': - seen = [] - seen_keys = set() - for x in self._data: - key = x if isinstance(x, (int, str, tuple, frozenset)) else id(x) - if key not in seen_keys: - seen_keys.add(key) - seen.append(x) - return _Stream(seen) - - def sorted(self, key=None, reverse: bool = False) -> '_Stream': - return _Stream(sorted(self._data, key=key, reverse=reverse)) - - def limit(self, maxSize: int) -> '_Stream': - return _Stream(self._data[:maxSize]) - - def skip(self, n: int) -> '_Stream': - return _Stream(self._data[n:]) - - def forEach(self, action) -> None: - for x in self._data: - action(x) - - def forEachOrdered(self, action) -> None: - for x in self._data: - action(x) - - def toArray(self, generator=None): - if generator is None: - return list(self._data) - return list(self._data) - - def reduce(self, *args): - if len(args) == 1: - accumulator = args[0] - it = iter(self._data) - try: - acc = next(it) - except StopIteration: - return Optional.empty() - for x in it: - acc = accumulator(acc, x) - return Optional.of(acc) - elif len(args) == 2: - identity, accumulator = args - acc = identity - for x in self._data: - acc = accumulator(acc, x) - return acc - else: - identity, accumulator, combiner = args - acc = identity - for x in self._data: - acc = accumulator(acc, x) - return acc - - def collect(self, *args): - if not args: - return list(self._data) - if len(args) == 1: - collector = args[0] - supplier = getattr(collector, 'supplier', None) - accumulator = getattr(collector, 'accumulator', None) - finisher = getattr(collector, 'finisher', None) - if supplier is not None and accumulator is not None: - container = supplier() if callable(supplier) else supplier - for x in self._data: - if callable(accumulator): - accumulator(container, x) - if finisher is not None and callable(finisher): - return finisher(container) - return container - if callable(collector): - return collector(list(self._data)) - return list(self._data) - supplier, accumulator, combiner = args[0], args[1], args[2] - container = supplier() if callable(supplier) else supplier - for x in self._data: - if callable(accumulator): - accumulator(container, x) - return container - - def count(self) -> int: - return len(self._data) - - def findFirst(self): - return Optional.of(self._data[0]) if self._data else Optional.empty() - - def findAny(self): - return self.findFirst() - - def anyMatch(self, predicate) -> bool: - return any(predicate(x) for x in self._data) - - def allMatch(self, predicate) -> bool: - return all(predicate(x) for x in self._data) - - def noneMatch(self, predicate) -> bool: - return not any(predicate(x) for x in self._data) - - def min(self, key=None): - if not self._data: - return Optional.empty() - return Optional.of(min(self._data, key=key) if key else min(self._data)) - - def max(self, key=None): - if not self._data: - return Optional.empty() - return Optional.of(max(self._data, key=key) if key else max(self._data)) - - def onClose(self, close_handler) -> '_Stream': - return self - - def close(self) -> None: - pass - - -# ============================================================ -# ArrayList -# ============================================================ - -class ArrayList(Stub): - """java.util.ArrayList — backed by Python list""" - def __init__(self, initial_capacity: Any = None): - super().__init__() - if initial_capacity is None: - self._data = [] - elif isinstance(initial_capacity, int): - self._data = [] - elif isinstance(initial_capacity, _IterableABC): - self._data = list(initial_capacity) - else: - self._data = [] - - def add(self, obj) -> bool: - self._data.append(obj) - return True - - def addAt(self, index: int, obj) -> None: - self._data.insert(index, obj) - - def addAll(self, c) -> bool: - if isinstance(c, ArrayList): - self._data.extend(c._data) - else: - self._data.extend(c) - return True - - def addAllAt(self, index: int, c) -> bool: - if isinstance(c, ArrayList): - self._data[index:index] = c._data - else: - self._data[index:index] = list(c) - return True - - def clear(self) -> None: - self._data.clear() - - def clone(self) -> 'ArrayList': - return ArrayList(self._data) - - def contains(self, obj) -> bool: - return obj in self._data - - def containsAll(self, c) -> bool: - if isinstance(c, ArrayList): - return all(x in self._data for x in c._data) - return all(x in self._data for x in c) - - def ensureCapacity(self, min_capacity: int) -> None: - pass # Python list handles resizing automatically - - def get(self, index: int): - return self._data[index] - - def indexOf(self, obj) -> int: - try: - return self._data.index(obj) - except ValueError: - return -1 - - def isEmpty(self) -> bool: - return len(self._data) == 0 - - def iterator(self) -> Iterator: - return Iterator(self._data) - - def listIterator(self) -> ListIterator: - return ListIterator(self._data) - - def lastIndexOf(self, obj) -> int: - try: - return len(self._data) - 1 - self._data[::-1].index(obj) - except ValueError: - return -1 - - def remove(self, obj) -> bool: - if obj in self._data: - self._data.remove(obj) - return True - return False - - def removeAt(self, index: int): - return self._data.pop(index) - - def removeAll(self, c) -> bool: - if isinstance(c, ArrayList): - items = c._data - else: - items = list(c) - old_len = len(self._data) - self._data = [x for x in self._data if x not in items] - return len(self._data) != old_len - - def retainAll(self, c) -> bool: - if isinstance(c, ArrayList): - items = set(c._data) - else: - items = set(c) - old_len = len(self._data) - self._data = [x for x in self._data if x in items] - return len(self._data) != old_len - - def set(self, index: int, obj): - old = self._data[index] - self._data[index] = obj - return old - - def size(self) -> int: - return len(self._data) - - def sort(self, key=None, reverse: bool = False) -> None: - self._data.sort(key=key, reverse=reverse) - - def subList(self, from_index: int, to_index: int) -> 'ArrayList': - return ArrayList(self._data[from_index:to_index]) - - def toArray(self, a=None): - if a is None: - return list(self._data) - if len(a) < len(self._data): - return list(self._data) - for i, v in enumerate(self._data): - a[i] = v - if len(self._data) < len(a): - a[len(self._data)] = None - return a - - def trimToSize(self) -> None: - pass - - def forEach(self, action) -> None: - for item in self._data: - action(item) - - def removeIf(self, predicate) -> bool: - old_len = len(self._data) - self._data = [x for x in self._data if not predicate(x)] - return len(self._data) != old_len - - def replaceAll(self, operator) -> None: - self._data = [operator(x) for x in self._data] - - def spliterator(self) -> _Spliterator: - return _Spliterator(self._data, _Spliterator.ORDERED) - - def stream(self) -> _Stream: - return _Stream(self._data) - - def equals(self, other) -> bool: - if isinstance(other, ArrayList): - return self._data == other._data - return False - - def hashCode(self) -> int: - h = 1 - for v in self._data: - h = (31 * h + hash(v)) & 0x7FFFFFFF - return h - - def toString(self) -> str: - items = ", ".join(str(x) for x in self._data) - return f"[{items}]" - - def __len__(self): - return len(self._data) - - def __getitem__(self, idx): - return self._data[idx] - - def __setitem__(self, idx, val): - self._data[idx] = val - - def __delitem__(self, idx): - del self._data[idx] - - def __iter__(self): - return iter(self._data) - - def __repr__(self): - return self.toString() - - def __contains__(self, obj): - return obj in self._data - - def __add__(self, other): - if isinstance(other, ArrayList): - result = ArrayList(self._data) - result._data.extend(other._data) - return result - return NotImplemented - - def __eq__(self, other): - return self.equals(other) - - -# ============================================================ -# LinkedList -# ============================================================ - -class LinkedList(Stub): - """java.util.LinkedList — backed by Python list""" - def __init__(self, c=None): - super().__init__() - if c is None: - self._data = [] - elif isinstance(c, _IterableABC): - self._data = list(c) - else: - self._data = [] - - def add(self, obj) -> bool: - self._data.append(obj) - return True - - def addFirst(self, obj) -> None: - self._data.insert(0, obj) - - def addLast(self, obj) -> None: - self._data.append(obj) - - def addAt(self, index: int, obj) -> None: - self._data.insert(index, obj) - - def addAll(self, c) -> bool: - if isinstance(c, LinkedList): - self._data.extend(c._data) - else: - self._data.extend(c) - return True - - def clear(self) -> None: - self._data.clear() - - def clone(self) -> 'LinkedList': - return LinkedList(self._data) - - def contains(self, obj) -> bool: - return obj in self._data - - def descendingIterator(self) -> Iterator: - return Iterator(reversed(self._data)) - - def element(self): - if not self._data: - raise NoSuchElementException() - return self._data[0] - - def get(self, index: int): - return self._data[index] - - def getFirst(self): - if not self._data: - raise NoSuchElementException() - return self._data[0] - - def getLast(self): - if not self._data: - raise NoSuchElementException() - return self._data[-1] - - def indexOf(self, obj) -> int: - try: - return self._data.index(obj) - except ValueError: - return -1 - - def isEmpty(self) -> bool: - return len(self._data) == 0 - - def iterator(self) -> Iterator: - return Iterator(self._data) - - def lastIndexOf(self, obj) -> int: - try: - return len(self._data) - 1 - self._data[::-1].index(obj) - except ValueError: - return -1 - - def listIterator(self, index: int = 0) -> ListIterator: - li = ListIterator(self._data) - li._index = index - return li - - def offer(self, obj) -> bool: - self._data.append(obj) - return True - - def offerFirst(self, obj) -> bool: - self._data.insert(0, obj) - return True - - def offerLast(self, obj) -> bool: - self._data.append(obj) - return True - - def peek(self): - return self._data[0] if self._data else None - - def peekFirst(self): - return self._data[0] if self._data else None - - def peekLast(self): - return self._data[-1] if self._data else None - - def poll(self): - return self._data.pop(0) if self._data else None - - def pollFirst(self): - return self._data.pop(0) if self._data else None - - def pollLast(self): - return self._data.pop() if self._data else None - - def pop(self): - if not self._data: - raise NoSuchElementException() - return self._data.pop() - - def push(self, obj) -> None: - self._data.insert(0, obj) - - def remove(self, obj) -> bool: - if obj in self._data: - self._data.remove(obj) - return True - return False - - def removeAt(self, index: int): - return self._data.pop(index) - - def removeFirst(self): - if not self._data: - raise NoSuchElementException() - return self._data.pop(0) - - def removeFirstOccurrence(self, obj) -> bool: - try: - idx = self._data.index(obj) - self._data.pop(idx) - return True - except ValueError: - return False - - def removeLast(self): - if not self._data: - raise NoSuchElementException() - return self._data.pop() - - def removeLastOccurrence(self, obj) -> bool: - try: - idx = len(self._data) - 1 - self._data[::-1].index(obj) - self._data.pop(idx) - return True - except ValueError: - return False - - def set(self, index: int, obj): - old = self._data[index] - self._data[index] = obj - return old - - def size(self) -> int: - return len(self._data) - - def toArray(self, a=None): - if a is None: - return list(self._data) - if len(a) < len(self._data): - return list(self._data) - for i, v in enumerate(self._data): - a[i] = v - if len(self._data) < len(a): - a[len(self._data)] = None - return a - - def forEach(self, action) -> None: - for item in self._data: - action(item) - - def equals(self, other) -> bool: - if isinstance(other, LinkedList): - return self._data == other._data - return False - - def hashCode(self) -> int: - h = 1 - for v in self._data: - h = (31 * h + hash(v)) & 0x7FFFFFFF - return h - - def toString(self) -> str: - items = ", ".join(str(x) for x in self._data) - return f"[{items}]" - - def __len__(self): - return len(self._data) - - def __getitem__(self, idx): - return self._data[idx] - - def __iter__(self): - return iter(self._data) - - def __repr__(self): - return self.toString() - - def __eq__(self, other): - return self.equals(other) - - -# ============================================================ -# HashMap -# ============================================================ - -class HashMap(Stub): - """java.util.HashMap — backed by Python dict""" - def __init__(self, initial_capacity: Any = None, load_factor: float = 0.75): - super().__init__() - self._data: Dict = {} - if initial_capacity is not None and not isinstance(initial_capacity, (int, float)): - if isinstance(initial_capacity, HashMap): - self._data.update(initial_capacity._data) - elif isinstance(initial_capacity, dict): - self._data.update(initial_capacity) - elif isinstance(initial_capacity, _IterableABC): - for item in initial_capacity: - if hasattr(item, 'getKey') and hasattr(item, 'getValue'): - k, v = item.getKey(), item.getValue() - elif isinstance(item, tuple) and len(item) >= 2: - k, v = item[0], item[1] - else: - continue - self._data[k] = v - - def clear(self) -> None: - self._data.clear() - - def clone(self) -> 'HashMap': - return HashMap(self._data) - - def compute(self, key, remapping_function): - old = self._data.get(key) - new_val = remapping_function(key, old) - if new_val is not None: - self._data[key] = new_val - return new_val - self._data.pop(key, None) - return None - - def computeIfAbsent(self, key, mapping_function): - if key not in self._data: - new_val = mapping_function(key) - if new_val is not None: - self._data[key] = new_val - return new_val - return self._data.get(key) - - def computeIfPresent(self, key, remapping_function): - if key in self._data: - new_val = remapping_function(key, self._data[key]) - if new_val is not None: - self._data[key] = new_val - return new_val - self._data.pop(key, None) - return None - - def containsKey(self, key) -> bool: - return key in self._data - - def containsValue(self, value) -> bool: - return value in self._data.values() - - def entrySet(self) -> set: - return self._data.items() - - def forEach(self, action) -> None: - for k, v in self._data.items(): - action(k, v) - - def get(self, key): - return self._data.get(key) - - def getOrDefault(self, key, default): - return self._data.get(key, default) - - def isEmpty(self) -> bool: - return len(self._data) == 0 - - def keySet(self) -> set: - return set(self._data.keys()) - - def merge(self, key, value, remapping_function): - old = self._data.get(key) - if old is None: - self._data[key] = value - return value - new_val = remapping_function(old, value) - if new_val is not None: - self._data[key] = new_val - return new_val - self._data.pop(key, None) - return None - - def put(self, key, value): - old = self._data.get(key) - self._data[key] = value - return old - - def putAll(self, m) -> None: - if isinstance(m, HashMap): - self._data.update(m._data) - else: - self._data.update(m) - - def putIfAbsent(self, key, value): - if key not in self._data: - self._data[key] = value - return None - return self._data[key] - - def remove(self, key, value=None): - if value is not None: - # remove(key, value) — 返回 boolean - if key in self._data and self._data[key] == value: - del self._data[key] - return True - return False - else: - # remove(key) — 返回被删除的 value(与 Java 语义一致) - if key in self._data: - old = self._data[key] - del self._data[key] - return old - return None - - def replace(self, key, old_value=None, new_value=None): - if old_value is None and new_value is None: - return None - if new_value is None: - # replace(key, value) form - if key in self._data: - old = self._data[key] - self._data[key] = old_value # old_value is actually new value here - return old - return None - # replace(key, oldValue, newValue) form - if key in self._data and self._data[key] == old_value: - self._data[key] = new_value - return True - return False - - def replaceAll(self, function) -> None: - for k in list(self._data.keys()): - self._data[k] = function(k, self._data[k]) - - def size(self) -> int: - return len(self._data) - - def values(self): - return list(self._data.values()) - - def equals(self, other) -> bool: - if isinstance(other, HashMap): - return self._data == other._data - return False - - def hashCode(self) -> int: - h = 0 - for k, v in self._data.items(): - h = (h + (hash(k) ^ hash(v))) & 0x7FFFFFFF - return h - - def toString(self) -> str: - items = ", ".join(f"{k}={v}" for k, v in self._data.items()) - return f"{{{items}}}" - - def __len__(self): - return len(self._data) - - def __getitem__(self, key): - return self._data[key] - - def __setitem__(self, key, value): - self._data[key] = value - - def __delitem__(self, key): - del self._data[key] - - def __iter__(self): - return iter(self._data) - - def __contains__(self, key): - return key in self._data - - def __repr__(self): - return self.toString() - - def __eq__(self, other): - return self.equals(other) - - -# ============================================================ -# LinkedHashMap -# ============================================================ - -class LinkedHashMap(HashMap): - """java.util.LinkedHashMap — backed by dict (preserves order in Python 3.7+)""" - def __init__(self, initial_capacity: Any = None, load_factor: float = 0.75, - access_order: bool = False): - super().__init__(initial_capacity, load_factor) - self._access_order = access_order - - def get(self, key): - val = self._data.get(key) - if val is not None and self._access_order: - # Move to end (most recently accessed) by delete/reinsert - del self._data[key] - self._data[key] = val - return val - - def getOrDefault(self, key, default): - val = self._data.get(key, default) - if key in self._data and self._access_order: - del self._data[key] - self._data[key] = val - return val - - def put(self, key, value): - old = self._data.get(key) - if key in self._data: - del self._data[key] - self._data[key] = value - return old - - def removeEldestEntry(self, eldest) -> bool: - return False - - def eldest(self): - if not self._data: - return None - key = next(iter(self._data)) - return (key, self._data[key]) - - def clone(self) -> 'LinkedHashMap': - result = LinkedHashMap(self._data, 0.75, self._access_order) - return result - - -# ============================================================ -# TreeMap -# ============================================================ - -class TreeMap(Stub): - """java.util.TreeMap — sorted map using Python's sorted()""" - def __init__(self, comparator=None, m=None): - super().__init__() - self._data: Dict = {} - self._comparator = comparator - if m is not None: - if isinstance(m, TreeMap): - self._data.update(m._data) - elif isinstance(m, dict): - self._data.update(m) - elif isinstance(m, _IterableABC): - for item in m: - if isinstance(item, tuple) and len(item) >= 2: - self._data[item[0]] = item[1] - - def _sorted_keys(self): - key_fn = self._comparator if self._comparator else None - return sorted(self._data.keys(), key=key_fn) - - def _get_entry_by_key(self, key): - if key in self._data: - return (key, self._data[key]) - return None - - def clear(self) -> None: - self._data.clear() - - def clone(self) -> 'TreeMap': - return TreeMap(self._comparator, self._data) - - def comparator(self): - return self._comparator - - def containsKey(self, key) -> bool: - return key in self._data - - def containsValue(self, value) -> bool: - return value in self._data.values() - - def ceilingKey(self, key): - keys = self._sorted_keys() - for k in keys: - if (self._comparator(k, key) >= 0 if self._comparator else k >= key): - return k - return None - - def ceilingEntry(self, key): - k = self.ceilingKey(key) - return (k, self._data[k]) if k is not None else None - - def floorKey(self, key): - keys = self._sorted_keys() - for k in reversed(keys): - if (self._comparator(k, key) <= 0 if self._comparator else k <= key): - return k - return None - - def floorEntry(self, key): - k = self.floorKey(key) - return (k, self._data[k]) if k is not None else None - - def higherKey(self, key): - keys = self._sorted_keys() - for k in keys: - if (self._comparator(k, key) > 0 if self._comparator else k > key): - return k - return None - - def higherEntry(self, key): - k = self.higherKey(key) - return (k, self._data[k]) if k is not None else None - - def lowerKey(self, key): - keys = self._sorted_keys() - for k in reversed(keys): - if (self._comparator(k, key) < 0 if self._comparator else k < key): - return k - return None - - def lowerEntry(self, key): - k = self.lowerKey(key) - return (k, self._data[k]) if k is not None else None - - def firstKey(self): - if not self._data: - raise NoSuchElementException() - return self._sorted_keys()[0] - - def firstEntry(self): - k = self.firstKey() - return (k, self._data[k]) - - def lastKey(self): - if not self._data: - raise NoSuchElementException() - return self._sorted_keys()[-1] - - def lastEntry(self): - k = self.lastKey() - return (k, self._data[k]) - - def pollFirstEntry(self): - if not self._data: - return None - k = self.firstKey() - v = self._data.pop(k) - return (k, v) - - def pollLastEntry(self): - if not self._data: - return None - k = self.lastKey() - v = self._data.pop(k) - return (k, v) - - def entrySet(self): - return [(k, self._data[k]) for k in self._sorted_keys()] - - def forEach(self, action) -> None: - for k in self._sorted_keys(): - action(k, self._data[k]) - - def get(self, key): - return self._data.get(key) - - def getOrDefault(self, key, default): - return self._data.get(key, default) - - def headMap(self, to_key, inclusive: bool = False) -> 'TreeMap': - result = TreeMap(self._comparator) - for k in self._sorted_keys(): - cmp = self._comparator(k, to_key) if self._comparator else (k < to_key) - if cmp or (inclusive and (self._comparator(k, to_key) == 0 if self._comparator else k == to_key)): - result._data[k] = self._data[k] - elif not inclusive and (self._comparator(k, to_key) < 0 if self._comparator else k < to_key): - result._data[k] = self._data[k] - else: - break - return result - - def tailMap(self, from_key, inclusive: bool = True) -> 'TreeMap': - result = TreeMap(self._comparator) - started = False - for k in self._sorted_keys(): - cmp = self._comparator(k, from_key) if self._comparator else (k >= from_key) - if cmp or (inclusive and (self._comparator(k, from_key) == 0 if self._comparator else k == from_key)): - started = True - if started: - result._data[k] = self._data[k] - return result - - def subMap(self, from_key, to_key, from_inclusive: bool = True, to_inclusive: bool = False) -> 'TreeMap': - result = TreeMap(self._comparator) - for k in self._sorted_keys(): - cmp_from = self._comparator(k, from_key) if self._comparator else (k >= from_key if from_inclusive else k > from_key) - cmp_to = self._comparator(k, to_key) if self._comparator else (k <= to_key if to_inclusive else k < to_key) - if cmp_from and cmp_to: - result._data[k] = self._data[k] - return result - - def isEmpty(self) -> bool: - return len(self._data) == 0 - - def keySet(self): - return self._sorted_keys() - - def put(self, key, value): - old = self._data.get(key) - self._data[key] = value - return old - - def putAll(self, m) -> None: - if isinstance(m, TreeMap): - self._data.update(m._data) - else: - self._data.update(m) - - def putIfAbsent(self, key, value): - if key not in self._data: - self._data[key] = value - return None - return self._data[key] - - def remove(self, key): - return self._data.pop(key, None) - - def replace(self, key, old_value=None, new_value=None): - if new_value is None: - if key in self._data: - old = self._data[key] - self._data[key] = old_value - return old - return None - if key in self._data and self._data[key] == old_value: - self._data[key] = new_value - return True - return False - - def replaceAll(self, function) -> None: - for k in list(self._data.keys()): - self._data[k] = function(k, self._data[k]) - - def compute(self, key, remapping_function): - old = self._data.get(key) - new_val = remapping_function(key, old) - if new_val is not None: - self._data[key] = new_val - return new_val - self._data.pop(key, None) - return None - - def computeIfAbsent(self, key, mapping_function): - if key not in self._data: - new_val = mapping_function(key) - if new_val is not None: - self._data[key] = new_val - return new_val - return self._data.get(key) - - def computeIfPresent(self, key, remapping_function): - if key in self._data: - new_val = remapping_function(key, self._data[key]) - if new_val is not None: - self._data[key] = new_val - return new_val - self._data.pop(key, None) - return None - - def merge(self, key, value, remapping_function): - old = self._data.get(key) - if old is None: - self._data[key] = value - return value - new_val = remapping_function(old, value) - if new_val is not None: - self._data[key] = new_val - return new_val - self._data.pop(key, None) - return None - - def size(self) -> int: - return len(self._data) - - def values(self): - return [self._data[k] for k in self._sorted_keys()] - - def navigableKeySet(self): - return self._sorted_keys() - - def descendingKeySet(self): - return list(reversed(self._sorted_keys())) - - def descendingMap(self) -> 'TreeMap': - result = TreeMap(self._comparator) - for k in reversed(self._sorted_keys()): - result._data[k] = self._data[k] - return result - - def equals(self, other) -> bool: - if isinstance(other, TreeMap): - return self._data == other._data - return False - - def hashCode(self) -> int: - h = 0 - for k, v in self._data.items(): - h = (h + (hash(k) ^ hash(v))) & 0x7FFFFFFF - return h - - def toString(self) -> str: - items = ", ".join(f"{k}={v}" for k in self._data.items()) - return f"{{{items}}}" - - def __len__(self): - return len(self._data) - - def __getitem__(self, key): - return self._data[key] - - def __setitem__(self, key, value): - self._data[key] = value - - def __delitem__(self, key): - del self._data[key] - - def __iter__(self): - return iter(self._sorted_keys()) - - def __repr__(self): - return self.toString() - - def __eq__(self, other): - return self.equals(other) - - -# ============================================================ -# HashSet -# ============================================================ - -class HashSet(Stub): - """java.util.HashSet — backed by Python set""" - def __init__(self, c=None): - super().__init__() - if c is None: - self._data: set = set() - elif isinstance(c, _IterableABC): - self._data = set(c) - else: - self._data = set() - - def add(self, obj) -> bool: - if obj in self._data: - return False - self._data.add(obj) - return True - - def addAll(self, c) -> bool: - old_size = len(self._data) - if isinstance(c, HashSet): - self._data.update(c._data) - elif isinstance(c, set): - self._data.update(c) - else: - self._data.update(c) - return len(self._data) != old_size - - def clear(self) -> None: - self._data.clear() - - def clone(self) -> 'HashSet': - return HashSet(self._data) - - def contains(self, obj) -> bool: - return obj in self._data - - def containsAll(self, c) -> bool: - if isinstance(c, HashSet): - return all(x in self._data for x in c._data) - return all(x in self._data for x in c) - - def isEmpty(self) -> bool: - return len(self._data) == 0 - - def iterator(self) -> Iterator: - return Iterator(list(self._data)) - - def remove(self, obj) -> bool: - if obj in self._data: - self._data.remove(obj) - return True - return False - - def removeAll(self, c) -> bool: - old_size = len(self._data) - if isinstance(c, HashSet): - self._data.difference_update(c._data) - else: - self._data.difference_update(c) - return len(self._data) != old_size - - def retainAll(self, c) -> bool: - old_size = len(self._data) - if isinstance(c, HashSet): - self._data.intersection_update(c._data) - else: - self._data.intersection_update(c) - return len(self._data) != old_size - - def size(self) -> int: - return len(self._data) - - def toArray(self, a=None): - items = list(self._data) - if a is None: - return items - if len(a) < len(items): - return items - for i, v in enumerate(items): - a[i] = v - if len(items) < len(a): - a[len(items)] = None - return a - - def forEach(self, action) -> None: - for item in list(self._data): - action(item) - - def removeIf(self, predicate) -> bool: - old_size = len(self._data) - to_remove = [x for x in self._data if predicate(x)] - for x in to_remove: - self._data.discard(x) - return len(self._data) != old_size - - def spliterator(self) -> _Spliterator: - return _Spliterator(self._data, _Spliterator.DISTINCT) - - def stream(self) -> _Stream: - return _Stream(self._data) - - def equals(self, other) -> bool: - if isinstance(other, HashSet): - return self._data == other._data - return False - - def hashCode(self) -> int: - h = 0 - for v in self._data: - h = (h + hash(v)) & 0x7FFFFFFF - return h - - def toString(self) -> str: - items = ", ".join(str(x) for x in self._data) - return f"[{items}]" - - def __len__(self): - return len(self._data) - - def __iter__(self): - return iter(self._data) - - def __contains__(self, obj): - return obj in self._data - - def __repr__(self): - return self.toString() - - def __eq__(self, other): - return self.equals(other) - - -# ============================================================ -# LinkedHashSet -# ============================================================ - -class LinkedHashSet(HashSet): - """java.util.LinkedHashSet — ordered set (preserves insertion order)""" - def __init__(self, c=None): - # dict keys preserve insertion order in Python 3.7+ - self._order: Dict[Any, None] = {} - if c is not None: - if isinstance(c, _IterableABC): - for item in c: - self._order[item] = None - self._data = set(self._order.keys()) - - def add(self, obj) -> bool: - if obj in self._order: - return False - self._order[obj] = None - self._data.add(obj) - return True - - def remove(self, obj) -> bool: - if obj in self._order: - del self._order[obj] - self._data.discard(obj) - return True - return False - - def clear(self) -> None: - self._order.clear() - self._data.clear() - - def iterator(self) -> Iterator: - return Iterator(list(self._order.keys())) - - def toArray(self, a=None): - items = list(self._order.keys()) - if a is None: - return items - if len(a) < len(items): - return items - for i, v in enumerate(items): - a[i] = v - if len(items) < len(a): - a[len(items)] = None - return a - - def forEach(self, action) -> None: - for item in list(self._order.keys()): - action(item) - - def __iter__(self): - return iter(self._order.keys()) - - -# ============================================================ -# TreeSet -# ============================================================ - -class TreeSet(Stub): - """java.util.TreeSet — sorted set""" - def __init__(self, comparator=None, c=None): - super().__init__() - self._data: set = set() - self._comparator = comparator - if c is not None: - if isinstance(c, _IterableABC): - self._data = set(c) - - def _sorted(self): - key_fn = self._comparator if self._comparator else None - return sorted(self._data, key=key_fn) - - def add(self, obj) -> bool: - if obj in self._data: - return False - self._data.add(obj) - return True - - def addAll(self, c) -> bool: - old_size = len(self._data) - if isinstance(c, TreeSet): - self._data.update(c._data) - else: - self._data.update(c) - return len(self._data) != old_size - - def ceiling(self, obj): - sorted_items = self._sorted() - for item in sorted_items: - if (self._comparator(item, obj) >= 0 if self._comparator else item >= obj): - return item - return None - - def floor(self, obj): - sorted_items = self._sorted() - for item in reversed(sorted_items): - if (self._comparator(item, obj) <= 0 if self._comparator else item <= obj): - return item - return None - - def higher(self, obj): - sorted_items = self._sorted() - for item in sorted_items: - if (self._comparator(item, obj) > 0 if self._comparator else item > obj): - return item - return None - - def lower(self, obj): - sorted_items = self._sorted() - for item in reversed(sorted_items): - if (self._comparator(item, obj) < 0 if self._comparator else item < obj): - return item - return None - - def clear(self) -> None: - self._data.clear() - - def clone(self) -> 'TreeSet': - return TreeSet(self._comparator, self._data) - - def comparator(self): - return self._comparator - - def contains(self, obj) -> bool: - return obj in self._data - - def containsAll(self, c) -> bool: - if isinstance(c, TreeSet): - return all(x in self._data for x in c._data) - return all(x in self._data for x in c) - - def first(self): - if not self._data: - raise NoSuchElementException() - return self._sorted()[0] - - def last(self): - if not self._data: - raise NoSuchElementException() - return self._sorted()[-1] - - def pollFirst(self): - if not self._data: - return None - val = self._sorted()[0] - self._data.discard(val) - return val - - def pollLast(self): - if not self._data: - return None - val = self._sorted()[-1] - self._data.discard(val) - return val - - def headSet(self, to_element, inclusive: bool = False) -> 'TreeSet': - result = TreeSet(self._comparator) - for item in self._data: - cmp = self._comparator(item, to_element) if self._comparator else (item < to_element) - if cmp or (inclusive and item == to_element): - result._data.add(item) - return result - - def tailSet(self, from_element, inclusive: bool = True) -> 'TreeSet': - result = TreeSet(self._comparator) - for item in self._data: - cmp = self._comparator(item, from_element) if self._comparator else (item > from_element) - if cmp or (inclusive and item == from_element): - result._data.add(item) - return result - - def subSet(self, from_element, to_element, - from_inclusive: bool = True, to_inclusive: bool = False) -> 'TreeSet': - result = TreeSet(self._comparator) - for item in self._data: - cmp_from = self._comparator(item, from_element) if self._comparator else ( - item >= from_element if from_inclusive else item > from_element) - cmp_to = self._comparator(item, to_element) if self._comparator else ( - item <= to_element if to_inclusive else item < to_element) - if cmp_from and cmp_to: - result._data.add(item) - return result - - def descendingIterator(self) -> Iterator: - return Iterator(reversed(self._sorted())) - - def descendingSet(self) -> 'TreeSet': - result = TreeSet(self._comparator) - result._data = set(self._data) - return result - - def isEmpty(self) -> bool: - return len(self._data) == 0 - - def iterator(self) -> Iterator: - return Iterator(self._sorted()) - - def remove(self, obj) -> bool: - if obj in self._data: - self._data.discard(obj) - return True - return False - - def removeAll(self, c) -> bool: - old_size = len(self._data) - if isinstance(c, TreeSet): - self._data.difference_update(c._data) - else: - self._data.difference_update(c) - return len(self._data) != old_size - - def retainAll(self, c) -> bool: - old_size = len(self._data) - if isinstance(c, TreeSet): - self._data.intersection_update(c._data) - else: - self._data.intersection_update(c) - return len(self._data) != old_size - - def size(self) -> int: - return len(self._data) - - def toArray(self, a=None): - items = self._sorted() - if a is None: - return items - if len(a) < len(items): - return items - for i, v in enumerate(items): - a[i] = v - if len(items) < len(a): - a[len(items)] = None - return a - - def forEach(self, action) -> None: - for item in self._sorted(): - action(item) - - def equals(self, other) -> bool: - if isinstance(other, TreeSet): - return self._data == other._data - return False - - def hashCode(self) -> int: - h = 0 - for v in self._data: - h = (h + hash(v)) & 0x7FFFFFFF - return h - - def toString(self) -> str: - items = ", ".join(str(x) for x in self._sorted()) - return f"[{items}]" - - def __len__(self): - return len(self._data) - - def __iter__(self): - return iter(self._sorted()) - - def __contains__(self, obj): - return obj in self._data - - def __repr__(self): - return self.toString() - - def __eq__(self, other): - return self.equals(other) - - -# ============================================================ -# Collections -# ============================================================ - -class Collections: - """java.util.Collections — static utility methods""" - EMPTY_LIST = [] - EMPTY_SET = set() - EMPTY_MAP = {} - - @staticmethod - def sort(data, key=None, reverse: bool = False) -> None: - if key is not None: - if isinstance(data, ArrayList): - data._data.sort(key=key, reverse=reverse) - elif isinstance(data, Vector): - data._data.sort(key=key, reverse=reverse) - else: - data.sort(key=key, reverse=reverse) - else: - data.sort(reverse=reverse) - - @staticmethod - def reverse(data) -> None: - if isinstance(data, ArrayList): - data._data.reverse() - elif isinstance(data, Vector): - data._data.reverse() - else: - data.reverse() - - @staticmethod - def shuffle(data, rnd: _typing.Optional['Random'] = None) -> None: - if isinstance(data, ArrayList): - lst = data._data - elif isinstance(data, Vector): - lst = data._data - else: - lst = data - if rnd: - _py_random.shuffle(lst) - else: - _py_random.shuffle(lst) - - @staticmethod - def swap(data, i: int, j: int) -> None: - if isinstance(data, ArrayList): - data._data[i], data._data[j] = data._data[j], data._data[i] - elif isinstance(data, Vector): - data._data[i], data._data[j] = data._data[j], data._data[i] - else: - data[i], data[j] = data[j], data[i] - - @staticmethod - def fill(data, obj) -> None: - if isinstance(data, ArrayList): - for i in range(len(data._data)): - data._data[i] = obj - elif isinstance(data, Vector): - for i in range(len(data._data)): - data._data[i] = obj - else: - for i in range(len(data)): - data[i] = obj - - @staticmethod - def copy(dest, src) -> None: - if isinstance(dest, ArrayList) and isinstance(src, ArrayList): - dest._data[:len(src._data)] = src._data[:] - elif isinstance(dest, Vector) and isinstance(src, Vector): - dest._data[:len(src._data)] = src._data[:] - else: - dest[:len(src)] = src[:] - - @staticmethod - def min(coll, comp=None): - if comp: - return min(coll, key=comp) - return min(coll) - - @staticmethod - def max(coll, comp=None): - if comp: - return max(coll, key=comp) - return max(coll) - - @staticmethod - def rotate(data, distance: int) -> None: - if isinstance(data, ArrayList): - lst = data._data - elif isinstance(data, Vector): - lst = data._data - else: - lst = data - n = len(lst) - if n == 0: - return - d = distance % n - lst[:] = lst[-d:] + lst[:-d] - - @staticmethod - def indexOfSubList(source, target) -> int: - if isinstance(source, ArrayList): - src = source._data - else: - src = list(source) - if isinstance(target, ArrayList): - tgt = target._data - else: - tgt = list(target) - for i in range(len(src) - len(tgt) + 1): - if src[i:i + len(tgt)] == tgt: - return i - return -1 - - @staticmethod - def lastIndexOfSubList(source, target) -> int: - if isinstance(source, ArrayList): - src = source._data - else: - src = list(source) - if isinstance(target, ArrayList): - tgt = target._data - else: - tgt = list(target) - for i in range(len(src) - len(tgt), -1, -1): - if src[i:i + len(tgt)] == tgt: - return i - return -1 - - @staticmethod - def replaceAll(data, old_val, new_val) -> bool: - if isinstance(data, ArrayList): - lst = data._data - elif isinstance(data, Vector): - lst = data._data - else: - lst = data - found = False - for i, v in enumerate(lst): - if v == old_val: - lst[i] = new_val - found = True - return found - - @staticmethod - def binarySearch(data, key): - if isinstance(data, ArrayList): - lst = data._data - else: - lst = list(data) - idx = _bisect.bisect_left(lst, key) - if idx < len(lst) and lst[idx] == key: - return idx - return -1 - - @staticmethod - def disjoint(c1, c2) -> bool: - set1 = set(c1) - set2 = set(c2) - return set1.isdisjoint(set2) - - @staticmethod - def frequency(c, obj) -> int: - count = 0 - for item in c: - if item == obj: - count += 1 - return count - - @staticmethod - def nCopies(n: int, obj) -> list: - return [obj] * n - - @staticmethod - def singleton(obj) -> set: - return {obj} - - @staticmethod - def singletonList(obj) -> list: - return [obj] - - @staticmethod - def singletonMap(key, value) -> dict: - return {key: value} - - @staticmethod - def unmodifiableList(data) -> list: - if isinstance(data, ArrayList): - return tuple(data._data) - return tuple(data) - - @staticmethod - def unmodifiableSet(data) -> frozenset: - if isinstance(data, HashSet): - return frozenset(data._data) - return frozenset(data) - - @staticmethod - def unmodifiableMap(data) -> dict: - if isinstance(data, HashMap): - return dict(data._data) - return dict(data) - - @staticmethod - def synchronizedList(data): - lock = _threading.Lock() - - class _SyncList: - def __init__(self, lst): - self._lock = lock - self._lst = lst - - def __getattr__(self, name): - with self._lock: - return getattr(self._lst, name) - - def add(self, obj): - with self._lock: - return self._lst.add(obj) - - def get(self, idx): - with self._lock: - return self._lst[idx] - - def set(self, idx, val): - with self._lock: - old = self._lst[idx] - self._lst[idx] = val - return old - - def remove(self, obj): - with self._lock: - return self._lst.remove(obj) - - def size(self): - with self._lock: - return len(self._lst) - - def __len__(self): - with self._lock: - return len(self._lst) - - def __getitem__(self, idx): - with self._lock: - return self._lst[idx] - - def __setitem__(self, idx, val): - with self._lock: - self._lst[idx] = val - - def __iter__(self): - with self._lock: - return iter(list(self._lst)) - - return _SyncList(data) - - @staticmethod - def synchronizedSet(data): - lock = _threading.Lock() - - class _SyncSet: - def __init__(self, s): - self._lock = lock - self._s = s - - def add(self, obj): - with self._lock: - return self._s.add(obj) - - def remove(self, obj): - with self._lock: - return self._s.remove(obj) - - def __contains__(self, obj): - with self._lock: - return obj in self._s - - def __iter__(self): - with self._lock: - return iter(list(self._s)) - - def __len__(self): - with self._lock: - return len(self._s) - - return _SyncSet(data) - - @staticmethod - def synchronizedMap(data): - lock = _threading.Lock() - - class _SyncMap: - def __init__(self, m): - self._lock = lock - self._m = m - - def put(self, k, v): - with self._lock: - return self._m.put(k, v) - - def get(self, k): - with self._lock: - return self._m.get(k) - - def remove(self, k): - with self._lock: - return self._m.remove(k) - - def __getitem__(self, k): - with self._lock: - return self._m[k] - - def __setitem__(self, k, v): - with self._lock: - self._m[k] = v - - def __contains__(self, k): - with self._lock: - return k in self._m - - def __len__(self): - with self._lock: - return len(self._m) - - def keys(self): - with self._lock: - return list(self._m.keys()) - - return _SyncMap(data) - - @staticmethod - def enumaration(c) -> Enumeration: - return Enumeration(c) - - @staticmethod - def list(enum: Enumeration) -> ArrayList: - items = [] - while enum.hasMoreElements(): - items.append(enum.nextElement()) - return ArrayList(items) - - @staticmethod - def newSetFromMap(m) -> HashSet: - class _SetFromMap(HashSet): - def __init__(self, backing_map): - super().__init__() - self._backing = backing_map - - def add(self, obj): - return self._backing.put(obj, True) is None - - def iterator(self): - return Iterator(self._backing.keySet()) - - def size(self): - return self._backing.size() - - def remove(self, obj): - return self._backing.remove(obj) is not None - - def contains(self, obj): - return self._backing.containsKey(obj) - - def clear(self): - self._backing.clear() - - return _SetFromMap(m) - - @staticmethod - def asLifoQueue(deque) -> 'Collections._LifoQueue': - class _LifoQueue: - def __init__(self, dq): - self._dq = dq - - def add(self, e): - self._dq.addFirst(e) - return True - - def offer(self, e): - return self.add(e) - - def remove(self): - if len(self._dq) == 0: - raise NoSuchElementException() - return self._dq.removeFirst() - - def poll(self): - return self._dq.pollFirst() - - def element(self): - if len(self._dq) == 0: - raise NoSuchElementException() - return self._dq.getFirst() - - def peek(self): - return self._dq.peekFirst() - - def size(self): - return self._dq.size() - - def isEmpty(self): - return self._dq.isEmpty() - - return _LifoQueue(deque) - - @staticmethod - def addAll(collection, *elements): - for e in elements: - collection.add(e) - return len(elements) > 0 - - @staticmethod - def emptyList(): - return [] - - @staticmethod - def emptySet(): - return set() - - @staticmethod - def emptyMap(): - return {} - - @staticmethod - def reverseOrder(comparator=None): - if comparator is None: - return lambda a, b: -1 if a < b else (1 if a > b else 0) - return lambda a, b: -comparator(a, b) - - @staticmethod - def singletonSet(element): - s = HashSet() - s.add(element) - return s - - @staticmethod - def checkedList(lst, type_): - return lst - - @staticmethod - def checkedSet(s, type_): - return s - - @staticmethod - def checkedMap(m, keyType, valueType): - return m - - @staticmethod - def unmodifiableCollection(c): - return c - - @staticmethod - def unmodifiableQueue(q): - return q - - @staticmethod - def unmodifiableDeque(d): - return d - - -# ============================================================ -# Arrays -# ============================================================ - -class Arrays: - """java.util.Arrays — static utility methods""" - @staticmethod - def asList(*args) -> ArrayList: - if len(args) == 1 and isinstance(args[0], (list, tuple)): - return ArrayList(args[0]) - return ArrayList(list(args)) - - @staticmethod - def sort(a, from_index: int = 0, to_index: int = None) -> None: - if to_index is None: - a.sort() - else: - a[from_index:to_index] = sorted(a[from_index:to_index]) - - @staticmethod - def parallelSort(a, from_index: int = 0, to_index: int = None) -> None: - Arrays.sort(a, from_index, to_index) - - @staticmethod - def binarySearch(a, key, from_index: int = 0, to_index: int = None) -> int: - if to_index is None: - lst = a - else: - lst = a[from_index:to_index] - idx = _bisect.bisect_left(lst, key) - if idx < len(lst) and lst[idx] == key: - return idx + from_index - return -(idx + from_index + 1) - - @staticmethod - def fill(a, val, from_index: int = 0, to_index: int = None) -> None: - if to_index is None: - for i in range(len(a)): - a[i] = val - else: - for i in range(from_index, to_index): - a[i] = val - - @staticmethod - def copyOf(original, new_length: int) -> list: - return list(original[:new_length]) + [None] * max(0, new_length - len(original)) - - @staticmethod - def copyOfRange(original, from_index: int, to_index: int) -> list: - return list(original[from_index:to_index]) - - @staticmethod - def equals(a, b) -> bool: - if len(a) != len(b): - return False - return all(x == y for x, y in zip(a, b)) - - @staticmethod - def deepEquals(a, b) -> bool: - if len(a) != len(b): - return False - return all(_deep_eq(x, y) for x, y in zip(a, b)) - - @staticmethod - def hashCode(a) -> int: - if a is None: - return 0 - h = 1 - for v in a: - h = (31 * h + hash(v)) & 0x7FFFFFFF - return h - - @staticmethod - def deepHashCode(a) -> int: - if a is None: - return 0 - h = 1 - for v in a: - if isinstance(v, (list, tuple)): - h = (31 * h + Arrays.deepHashCode(v)) & 0x7FFFFFFF - else: - h = (31 * h + hash(v)) & 0x7FFFFFFF - return h - - @staticmethod - def toString(a) -> str: - if a is None: - return "null" - return "[" + ", ".join(str(x) for x in a) + "]" - - @staticmethod - def deepToString(a) -> str: - if a is None: - return "null" - return "[" + ", ".join(_deep_str(x) for x in a) + "]" - - @staticmethod - def stream(a) -> 'Arrays._ArrayStream': - class _ArrayStream: - def __init__(self, arr): - self._arr = arr - self._idx = 0 - - def filter(self, pred): - return _ArrayStream([x for x in self._arr if pred(x)]) - - def map(self, func): - return _ArrayStream([func(x) for x in self._arr]) - - def forEach(self, action): - for x in self._arr: - action(x) - - def toArray(self): - return list(self._arr) - - def count(self): - return len(self._arr) - - return _ArrayStream(a) - - @staticmethod - def setAll(a, generator) -> None: - for i in range(len(a)): - a[i] = generator(i) - - @staticmethod - def parallelSetAll(a, generator) -> None: - Arrays.setAll(a, generator) - - @staticmethod - def spliterator(a) -> Iterator: - return Iterator(a) - - -def _deep_eq(x, y) -> bool: - if isinstance(x, (list, tuple)) and isinstance(y, (list, tuple)): - return Arrays.deepEquals(x, y) - return x == y - - -def _deep_str(x) -> str: - if isinstance(x, (list, tuple)): - return Arrays.deepToString(x) - return str(x) - - -# ============================================================ -# Date -# ============================================================ - -class Date(Stub): - """java.util.Date — wraps Python datetime""" - def __init__(self, *args): - super().__init__() - if len(args) == 0: - self._dt = _datetime.datetime.now() - elif len(args) == 1: - if isinstance(args[0], (int, float)): - self._dt = _datetime.datetime.fromtimestamp(args[0] / 1000.0) - elif isinstance(args[0], str): - self._dt = _datetime.datetime.fromisoformat(args[0]) - elif isinstance(args[0], _datetime.datetime): - self._dt = args[0] - elif isinstance(args[0], Date): - self._dt = args[0]._dt - else: - self._dt = _datetime.datetime.now() - elif len(args) >= 6: - year = args[0] - 1900 - month = args[1] - day = args[2] - hour = args[3] if len(args) > 3 else 0 - minute = args[4] if len(args) > 4 else 0 - second = args[5] if len(args) > 5 else 0 - self._dt = _datetime.datetime(year + 1900, month + 1, day, hour, minute, second) - else: - self._dt = _datetime.datetime.now() - - def getTime(self) -> int: - return int(self._dt.timestamp() * 1000) - - def setTime(self, time_millis: int) -> None: - self._dt = _datetime.datetime.fromtimestamp(time_millis / 1000.0) - - def before(self, when: 'Date') -> bool: - return self._dt < when._dt - - def after(self, when: 'Date') -> bool: - return self._dt > when._dt - - def compareTo(self, another: 'Date') -> int: - return (self._dt > another._dt) - (self._dt < another._dt) - - def equals(self, other) -> bool: - if isinstance(other, Date): - return self._dt == other._dt - return False - - def hashCode(self) -> int: - return hash(self._dt) & 0x7FFFFFFF - - def toString(self) -> str: - return self._dt.strftime("%a %b %d %H:%M:%S %z %Y") - - def clone(self) -> 'Date': - return Date(self._dt) - - def getYear(self) -> int: - return self._dt.year - 1900 - - def getMonth(self) -> int: - return self._dt.month - 1 - - def getDay(self) -> int: - return self._dt.weekday() - - def getHours(self) -> int: - return self._dt.hour - - def getMinutes(self) -> int: - return self._dt.minute - - def getSeconds(self) -> int: - return self._dt.second - - def getDate(self) -> int: - return self._dt.day - - def setYear(self, year: int) -> None: - self._dt = self._dt.replace(year=year + 1900) - - def setMonth(self, month: int) -> None: - self._dt = self._dt.replace(month=month + 1) - - def setDate(self, date: int) -> None: - self._dt = self._dt.replace(day=date) - - def setHours(self, hours: int) -> None: - self._dt = self._dt.replace(hour=hours) - - def setMinutes(self, minutes: int) -> None: - self._dt = self._dt.replace(minute=minutes) - - def setSeconds(self, seconds: int) -> None: - self._dt = self._dt.replace(second=seconds) - - def toGMTString(self) -> str: - return self._dt.strftime("%d %b %Y %H:%M:%S GMT") - - def toLocaleString(self) -> str: - return self._dt.strftime("%Y-%m-%d %H:%M:%S") - - @staticmethod - def parse(s: str) -> 'Date': - try: - return Date(_datetime.datetime.fromisoformat(s)) - except ValueError: - return Date() - - @staticmethod - def fromInstant(instant) -> 'Date': - return Date(instant.toEpochMilli()) - - def toInstant(self): - return self._dt - - def __str__(self): - return self.toString() - - def __repr__(self): - return f"Date({self.getTime()})" - - -# ============================================================ -# Calendar / GregorianCalendar -# ============================================================ - -class Calendar(Stub): - """java.util.Calendar — wraps Python datetime with Java field system""" - # Field constants - YEAR = 1 - MONTH = 2 - DAY_OF_MONTH = 5 - DAY_OF_YEAR = 6 - DAY_OF_WEEK = 7 - DAY_OF_WEEK_IN_MONTH = 8 - AM_PM = 9 - HOUR = 10 - HOUR_OF_DAY = 11 - MINUTE = 12 - SECOND = 13 - MILLISECOND = 14 - WEEK_OF_MONTH = 15 - WEEK_OF_YEAR = 16 - ERA = 17 - ZONE_OFFSET = 18 - DST_OFFSET = 19 - FIELD_COUNT = 20 - - # Day of week constants - SUNDAY = 1 - MONDAY = 2 - TUESDAY = 3 - WEDNESDAY = 4 - THURSDAY = 5 - FRIDAY = 6 - SATURDAY = 7 - - # Month constants - JANUARY = 0 - FEBRUARY = 1 - MARCH = 2 - APRIL = 3 - MAY = 4 - JUNE = 5 - JULY = 6 - AUGUST = 7 - SEPTEMBER = 8 - OCTOBER = 9 - NOVEMBER = 10 - DECEMBER = 11 - UNDECIMBER = 12 - - # AM/PM - AM = 0 - PM = 1 - - def __init__(self): - super().__init__() - self._dt = _datetime.datetime.now() - self._fields: Dict[int, int] = {} - self._is_set: Dict[int, bool] = {} - self._lenient = True - self._first_day_of_week = Calendar.SUNDAY - self._minimal_days_in_first_week = 1 - self._timezone = _datetime.timezone.utc - self._set_fields_from_dt() - - def _set_fields_from_dt(self) -> None: - self._fields[Calendar.YEAR] = self._dt.year - self._fields[Calendar.MONTH] = self._dt.month - 1 - self._fields[Calendar.DAY_OF_MONTH] = self._dt.day - self._fields[Calendar.DAY_OF_YEAR] = self._dt.timetuple().tm_yday - self._fields[Calendar.DAY_OF_WEEK] = self._dt.isoweekday() % 7 + 1 - self._fields[Calendar.HOUR] = self._dt.hour % 12 - self._fields[Calendar.HOUR_OF_DAY] = self._dt.hour - self._fields[Calendar.MINUTE] = self._dt.minute - self._fields[Calendar.SECOND] = self._dt.second - self._fields[Calendar.MILLISECOND] = self._dt.microsecond // 1000 - self._fields[Calendar.AM_PM] = Calendar.AM if self._dt.hour < 12 else Calendar.PM - self._fields[Calendar.WEEK_OF_YEAR] = self._dt.isocalendar()[1] - self._fields[Calendar.ERA] = 1 # AD = 1 - - def get(self, field: int) -> int: - return self._fields.get(field, 0) - - def set(self, field: int, value: int) -> None: - self._fields[field] = value - self._is_set[field] = True - - def setTime(self, date: 'Date') -> None: - self._dt = date._dt - self._set_fields_from_dt() - - def getTime(self) -> Date: - return Date(self._dt) - - def getTimeInMillis(self) -> int: - return int(self._dt.timestamp() * 1000) - - def setTimeInMillis(self, millis: int) -> None: - self._dt = _datetime.datetime.fromtimestamp(millis / 1000.0) - self._set_fields_from_dt() - - def add(self, field: int, amount: int) -> None: - if field == Calendar.YEAR: - self._dt = self._dt.replace(year=self._dt.year + amount) - elif field == Calendar.MONTH: - new_month = self._dt.month - 1 + amount - new_year = self._dt.year + new_month // 12 - new_month = new_month % 12 - try: - self._dt = self._dt.replace(year=new_year, month=new_month + 1) - except ValueError: - self._dt = self._dt.replace(year=new_year, month=new_month + 1, day=28) - elif field == Calendar.DAY_OF_MONTH: - self._dt += _datetime.timedelta(days=amount) - elif field == Calendar.HOUR_OF_DAY or field == Calendar.HOUR: - self._dt += _datetime.timedelta(hours=amount) - elif field == Calendar.MINUTE: - self._dt += _datetime.timedelta(minutes=amount) - elif field == Calendar.SECOND: - self._dt += _datetime.timedelta(seconds=amount) - elif field == Calendar.MILLISECOND: - self._dt += _datetime.timedelta(milliseconds=amount) - elif field == Calendar.WEEK_OF_YEAR or field == Calendar.WEEK_OF_MONTH: - self._dt += _datetime.timedelta(weeks=amount) - self._set_fields_from_dt() - - def roll(self, field: int, amount: int) -> None: - if field == Calendar.MONTH: - old_month = self._dt.month - new_month = ((old_month - 1 + amount) % 12) + 1 - try: - self._dt = self._dt.replace(month=new_month) - except ValueError: - self._dt = self._dt.replace(month=new_month, day=28) - elif field == Calendar.DAY_OF_MONTH: - import calendar as _cal - max_day = _cal.monthrange(self._dt.year, self._dt.month)[1] - new_day = ((self._dt.day - 1 + amount) % max_day) + 1 - self._dt = self._dt.replace(day=new_day) - else: - self.add(field, amount) - self._set_fields_from_dt() - - def before(self, when: 'Calendar') -> bool: - return self._dt < when._dt - - def after(self, when: 'Calendar') -> bool: - return self._dt > when._dt - - def compareTo(self, another: 'Calendar') -> int: - return (self._dt > another._dt) - (self._dt < another._dt) - - def equals(self, other) -> bool: - if isinstance(other, Calendar): - return self._dt == other._dt - return False - - def hashCode(self) -> int: - return hash(self._dt) & 0x7FFFFFFF - - def toString(self) -> str: - return str(self._dt) - - def isLenient(self) -> bool: - return self._lenient - - def setLenient(self, lenient: bool) -> None: - self._lenient = lenient - - def getFirstDayOfWeek(self) -> int: - return self._first_day_of_week - - def setFirstDayOfWeek(self, value: int) -> None: - self._first_day_of_week = value - - def getMinimalDaysInFirstWeek(self) -> int: - return self._minimal_days_in_first_week - - def setMinimalDaysInFirstWeek(self, value: int) -> None: - self._minimal_days_in_first_week = value - - def getActualMaximum(self, field: int) -> int: - import calendar as _cal - if field == Calendar.DAY_OF_MONTH: - return _cal.monthrange(self._dt.year, self._dt.month)[1] - if field == Calendar.DAY_OF_YEAR: - return 366 if self._dt.year % 4 == 0 and (self._dt.year % 100 != 0 or self._dt.year % 400 == 0) else 365 - if field == Calendar.MONTH: - return 11 - if field == Calendar.HOUR: - return 11 - if field == Calendar.HOUR_OF_DAY: - return 23 - if field == Calendar.MINUTE: - return 59 - if field == Calendar.SECOND: - return 59 - if field == Calendar.MILLISECOND: - return 999 - return 0 - - def getActualMinimum(self, field: int) -> int: - return 0 - - def getDisplayName(self, field: int, style: int, locale: 'Locale') -> str: - return str(self.get(field)) - - @staticmethod - def getInstance() -> 'GregorianCalendar': - return GregorianCalendar() - - def clear(self, field=None): - if field is None: - self._fields = {} - else: - if field in self._fields: - del self._fields[field] - - def isSet(self, field): - return field in self._fields - - def getCalendarType(self): - return "gregory" - - @staticmethod - def getAvailableLocales(): - from .util import Locale - return [Locale.US, Locale.CHINA, Locale.JAPAN, Locale.KOREA, Locale.GERMANY, Locale.FRANCE] - - def toInstant(self): - from .time import Instant - return Instant.ofEpochMilli(self.getTimeInMillis()) - - def getWeekYear(self): - return self.get(Calendar.YEAR) - - def isWeekDateSupported(self): - return False - - def getWeeksInWeekYear(self): - return 52 - - -class GregorianCalendar(Calendar): - """java.util.GregorianCalendar""" - BC = 0 - AD = 1 - - def __init__(self, *args): - super().__init__() - if len(args) == 0: - self._dt = _datetime.datetime.now() - elif len(args) == 3: - year, month, day = args - self._dt = _datetime.datetime(year, month + 1, day) - elif len(args) >= 5: - year, month, day, hour, minute = args[:5] - second = args[5] if len(args) > 5 else 0 - self._dt = _datetime.datetime(year, month + 1, day, hour, minute, second) - else: - self._dt = _datetime.datetime.now() - self._set_fields_from_dt() - - def isLeapYear(self, year: int) -> bool: - return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0 - - def getGreatestMinimum(self, field: int) -> int: - return self.getActualMinimum(field) - - def getLeastMaximum(self, field: int) -> int: - return self.getActualMaximum(field) - - def getMaximum(self, field: int) -> int: - if field == Calendar.DAY_OF_MONTH: - return 31 - if field == Calendar.DAY_OF_YEAR: - return 366 - if field == Calendar.MONTH: - return 11 - return self.getActualMaximum(field) - - def getMinimum(self, field: int) -> int: - return 0 - - -# ============================================================ -# UUID -# ============================================================ - -class UUID(Stub): - """java.util.UUID — wraps Python uuid module""" - def __init__(self, most_sig_bits: int, least_sig_bits: int): - super().__init__() - self._most_sig_bits = most_sig_bits & 0xFFFFFFFFFFFFFFFF - self._least_sig_bits = least_sig_bits & 0xFFFFFFFFFFFFFFFF - # Rebuild a Python UUID from the bits for string representation - hex_str = f"{self._most_sig_bits:016x}{self._least_sig_bits:016x}" - self._uuid = _uuid.UUID(hex_str) - - @staticmethod - def randomUUID() -> 'UUID': - u = _uuid.uuid4() - most = u.int >> 64 - least = u.int & 0xFFFFFFFFFFFFFFFF - return UUID(most, least) - - @staticmethod - def nameUUIDFromBytes(name: bytes) -> 'UUID': - u = _uuid.uuid3(_uuid.NAMESPACE_DNS, name.decode('utf-8', errors='replace')) - most = u.int >> 64 - least = u.int & 0xFFFFFFFFFFFFFFFF - return UUID(most, least) - - @staticmethod - def fromString(name: str) -> 'UUID': - u = _uuid.UUID(name) - most = u.int >> 64 - least = u.int & 0xFFFFFFFFFFFFFFFF - return UUID(most, least) - - def getMostSignificantBits(self) -> int: - return self._most_sig_bits - - def getLeastSignificantBits(self) -> int: - return self._least_sig_bits - - def version(self) -> int: - return self._uuid.version - - def variant(self) -> int: - return self._uuid.variant - - def timestamp(self) -> int: - if self.version() != 1: - raise UnsupportedOperationException("Not a time-based UUID") - return self._uuid.time - - def clockSequence(self) -> int: - if self.version() != 1: - raise UnsupportedOperationException("Not a time-based UUID") - return self._uuid.clock_seq - - def node(self) -> int: - if self.version() != 1: - raise UnsupportedOperationException("Not a time-based UUID") - return self._uuid.node - - def compareTo(self, other: 'UUID') -> int: - return (self._uuid.int > other._uuid.int) - (self._uuid.int < other._uuid.int) - - def equals(self, other) -> bool: - if isinstance(other, UUID): - return self._uuid == other._uuid - return False - - def hashCode(self) -> int: - return hash(self._uuid) & 0x7FFFFFFF - - def toString(self) -> str: - return str(self._uuid) - - def __str__(self): - return str(self._uuid) - - def __repr__(self): - return f"UUID({str(self._uuid)})" - - -# ============================================================ -# StringTokenizer -# ============================================================ - -class StringTokenizer(Stub): - """java.util.StringTokenizer — simple tokenizer""" - def __init__(self, s: str, delim: str = " \t\n\r\f", return_delims: bool = False): - super().__init__() - self._str = str(s) - self._delim = delim - self._return_delims = return_delims - self._tokens = self._tokenize() - self._index = 0 - - def _tokenize(self): - if self._return_delims: - tokens = [] - buf = "" - for ch in self._str: - if ch in self._delim: - if buf: - tokens.append(buf) - buf = "" - tokens.append(ch) - else: - buf += ch - if buf: - tokens.append(buf) - return tokens - import re as _re - pattern = f"[{_re.escape(self._delim)}]+" - return [t for t in _re.split(pattern, self._str) if t] - - def countTokens(self) -> int: - return len(self._tokens) - - def hasMoreTokens(self) -> bool: - return self._index < len(self._tokens) - - def nextToken(self, delim: str = None) -> str: - if delim is not None: - self._delim = delim - remaining = self._str[self._index:] - st = StringTokenizer(remaining, delim, self._return_delims) - self._tokens = st._tokens - self._index = 0 - if not self.hasMoreTokens(): - raise NoSuchElementException() - tok = self._tokens[self._index] - self._index += 1 - return tok - - def hasMoreElements(self) -> bool: - return self.hasMoreTokens() - - def nextElement(self): - return self.nextToken() - - def __iter__(self): - return self - - def __next__(self): - if not self.hasMoreTokens(): - raise StopIteration - return self.nextToken() - - -# ============================================================ -# Optional -# ============================================================ - -class Optional(Stub): - """java.util.Optional — wraps value or None""" - def __init__(self, value=None): - super().__init__() - self._value = value - - @staticmethod - def empty() -> 'Optional': - return Optional(None) - - @staticmethod - def of(value) -> 'Optional': - if value is None: - raise TypeError("value is null") - return Optional(value) - - @staticmethod - def ofNullable(value) -> 'Optional': - return Optional(value) - - def get(self): - if self._value is None: - raise NoSuchElementException("No value present") - return self._value - - def isPresent(self) -> bool: - return self._value is not None - - def isEmpty(self) -> bool: - return self._value is None - - def ifPresent(self, consumer) -> None: - if self._value is not None: - consumer(self._value) - - def ifPresentOrElse(self, action, empty_action) -> None: - if self._value is not None: - action(self._value) - else: - empty_action() - - def filter(self, predicate) -> 'Optional': - if self._value is None: - return self - return Optional.ofNullable(self._value) if predicate(self._value) else Optional.empty() - - def map(self, mapper) -> 'Optional': - if self._value is None: - return Optional.empty() - return Optional.ofNullable(mapper(self._value)) - - def flatMap(self, mapper) -> 'Optional': - if self._value is None: - return Optional.empty() - return mapper(self._value) - - def orElse(self, other): - return self._value if self._value is not None else other - - def orElseGet(self, supplier): - if self._value is not None: - return self._value - return supplier() - - def orElseThrow(self, exception_supplier=None): - if self._value is not None: - return self._value - if exception_supplier: - raise exception_supplier() - raise NoSuchElementException("No value present") - - def stream(self): - if self._value is not None: - return [self._value] - return [] - - def equals(self, other) -> bool: - if isinstance(other, Optional): - return self._value == other._value - return False - - def hashCode(self) -> int: - return hash(self._value) & 0x7FFFFFFF if self._value is not None else 0 - - def toString(self) -> str: - if self._value is None: - return "Optional.empty" - return f"Optional[{self._value}]" - - def __str__(self): - return self.toString() - - def __repr__(self): - return self.toString() - - def __bool__(self): - return self._value is not None - - -# ============================================================ -# Random -# ============================================================ - -class Random(Stub): - """java.util.Random — wraps Python random module""" - def __init__(self, seed: _typing.Optional[int] = None): - super().__init__() - self._rng = _py_random.Random(seed) - - def setSeed(self, seed: int) -> None: - self._rng.seed(seed) - - def nextInt(self, bound: _typing.Optional[int] = None) -> int: - if bound is None: - return self._rng.randint(-2147483648, 2147483647) - if bound <= 0: - raise IllegalArgumentException("bound must be positive") - return self._rng.randrange(0, bound) - - def nextLong(self) -> int: - return (self._rng.randint(-2147483648, 2147483647) << 32) | self._rng.randint(0, 0xFFFFFFFF) - - def nextBoolean(self) -> bool: - return self._rng.random() < 0.5 - - def nextFloat(self) -> float: - return self._rng.random() - - def nextDouble(self) -> float: - return self._rng.random() - - def nextGaussian(self) -> float: - return self._rng.gauss(0.0, 1.0) - - def nextBytes(self, bytes_arr: bytearray) -> None: - for i in range(len(bytes_arr)): - bytes_arr[i] = self._rng.randint(0, 255) - - def next(self, bits: int) -> int: - return self._rng.getrandbits(bits) & ((1 << bits) - 1) - - @staticmethod - def create() -> 'Random': - return Random() - - -# ============================================================ -# Properties -# ============================================================ - -class Properties(HashMap): - """java.util.Properties — extends Hashtable-like dict with load/store""" - def __init__(self, defaults: 'Properties' = None): - super().__init__() - self._defaults = defaults - - def setProperty(self, key: str, value: str) -> None: - self.put(str(key), str(value)) - - def getProperty(self, key: str, default_value: str = None) -> _typing.Optional[str]: - val = self._data.get(str(key)) - if val is not None: - return str(val) - if self._defaults is not None: - return self._defaults.getProperty(key, default_value) - return default_value - - def load(self, in_stream) -> None: - if isinstance(in_stream, (str, bytes)): - lines = in_stream.splitlines() if isinstance(in_stream, str) else in_stream.decode('utf-8').splitlines() - else: - lines = in_stream.read().splitlines() if hasattr(in_stream, 'read') else in_stream.splitlines() - for line in lines: - line = line.strip() - if not line or line.startswith('#') or line.startswith('!'): - continue - if '=' in line: - key, val = line.split('=', 1) - elif ':' in line: - key, val = line.split(':', 1) - else: - key = line - val = "" - key = key.strip() - val = val.strip() - self._data[key] = val - - def store(self, out_stream, comments: str = None) -> None: - lines = [] - if comments: - lines.append(f"#{comments}") - lines.append(f"# {_datetime.datetime.now().isoformat()}") - for k, v in self._data.items(): - escaped_val = str(v).replace("\\", "\\\\").replace(" ", "\\ ") - lines.append(f"{k}={escaped_val}") - content = "\n".join(lines) + "\n" - if isinstance(out_stream, _io.IOBase): - out_stream.write(content) - else: - out_stream.write(content) # type: ignore - - def list(self, out_stream) -> None: - for k, v in self._data.items(): - out_stream.write(f"{k}={v}\n") - - def propertyNames(self): - return Iterator(self._data.keys()) - - def stringPropertyNames(self) -> set: - names = set() - for k in self._data.keys(): - if isinstance(k, str): - names.add(k) - if self._defaults: - names.update(self._defaults.stringPropertyNames()) - return names - - def loadFromXML(self, in_stream) -> None: - import xml.etree.ElementTree as _ET - tree = _ET.parse(in_stream) - root = tree.getroot() - for entry in root.findall('entry'): - key = entry.get('key', '') - self._data[key] = entry.text or '' - - def storeToXML(self, out_stream, comment: str = None) -> None: - import xml.etree.ElementTree as _ET - root = _ET.Element('properties') - if comment: - c = _ET.SubElement(root, 'comment') - c.text = comment - for k, v in self._data.items(): - entry = _ET.SubElement(root, 'entry') - entry.set('key', str(k)) - entry.text = str(v) - tree = _ET.ElementTree(root) - tree.write(out_stream, xml_declaration=True, encoding='UTF-8') - - def get(self, key): - val = self._data.get(key) - if val is not None: - return val - return self._defaults.get(key) if self._defaults else None - - def put(self, key, value): - return super().put(str(key), str(value)) - - def toString(self) -> str: - items = ", ".join(f"{k}={v}" for k, v in self._data.items()) - return f"{{{items}}}" - - -# ============================================================ -# EnumSet -# ============================================================ - -class EnumSet(HashSet): - """java.util.EnumSet — simple implementation using set""" - def __init__(self, c=None): - super().__init__() - self._element_type = None - if c is not None: - if isinstance(c, EnumSet): - self._data = set(c._data) - self._element_type = c._element_type - elif isinstance(c, type): - self._element_type = c - elif isinstance(c, _IterableABC): - for item in c: - self._data.add(item) - if self._element_type is None: - self._element_type = item.__class__ - - @staticmethod - def allOf(element_type) -> 'EnumSet': - es = EnumSet() - es._element_type = element_type - if hasattr(element_type, '_values'): - for v in element_type._values(): - es._data.add(v) - elif hasattr(element_type, '__members__'): - for name, member in element_type.__members__.items(): - es._data.add(member) - return es - - @staticmethod - def noneOf(element_type) -> 'EnumSet': - es = EnumSet() - es._element_type = element_type - return es - - @staticmethod - def range(element_type, from_val, to_val) -> 'EnumSet': - es = EnumSet() - es._element_type = element_type - started = False - if hasattr(element_type, '_values'): - for v in element_type._values(): - if v == from_val: - started = True - if started: - es._data.add(v) - if v == to_val: - break - elif hasattr(element_type, '__members__'): - for name, member in element_type.__members__.items(): - if member == from_val: - started = True - if started: - es._data.add(member) - if member == to_val: - break - return es - - @staticmethod - def copyOf(es) -> 'EnumSet': - return EnumSet(es) - - def complementOf(self, other: 'EnumSet') -> 'EnumSet': - result = EnumSet.noneOf(self._element_type) - if self._element_type and hasattr(self._element_type, '_values'): - for v in self._element_type._values(): - if v not in other._data: - result._data.add(v) - return result - - -# ============================================================ -# EnumMap -# ============================================================ - -class EnumMap(HashMap): - """java.util.EnumMap — simple implementation using dict""" - def __init__(self, key_type=None, m=None): - super().__init__() - self._key_type = key_type - if m is not None: - if isinstance(m, EnumMap): - self._data.update(m._data) - self._key_type = m._key_type - elif isinstance(m, dict): - self._data.update(m) - else: - for k, v in m.items(): - self._data[k] = v - - def put(self, key, value): - if self._key_type is None: - self._key_type = key.__class__ - return super().put(key, value) - - def keySet(self): - return set(self._data.keys()) - - -# ============================================================ -# Objects -# ============================================================ - -class Objects: - """java.util.Objects — static helper methods""" - @staticmethod - def equals(a, b) -> bool: - return a == b - - @staticmethod - def deepEquals(a, b) -> bool: - return _deep_eq(a, b) - - @staticmethod - def hashCode(o) -> int: - return 0 if o is None else (hash(o) & 0x7FFFFFFF) - - @staticmethod - def hash(*values) -> int: - return Arrays.hashCode(values) - - @staticmethod - def toString(o, null_default: str = "null") -> str: - if o is None: - return null_default - return str(o) - - @staticmethod - def compare(a, b, cmp) -> int: - if cmp: - return cmp(a, b) - return (a > b) - (a < b) - - @staticmethod - def requireNonNull(obj, message: Any = None): - if obj is None: - if message is None: - raise TypeError("null") - raise TypeError(str(message) if not callable(message) else message.get()) - return obj - - @staticmethod - def nonNull(obj) -> bool: - return obj is not None - - @staticmethod - def isNull(obj) -> bool: - return obj is None - - @staticmethod - def requireNonNullElse(obj, default): - return obj if obj is not None else default - - @staticmethod - def requireNonNullElseGet(obj, supplier): - return obj if obj is not None else supplier() - - @staticmethod - def checkIndex(index: int, length: int) -> int: - if index < 0 or index >= length: - raise IndexError(f"Index {index} out of bounds [0, {length})") - return index - - @staticmethod - def checkFromToIndex(from_index: int, to_index: int, length: int) -> int: - if from_index < 0 or from_index > to_index or to_index > length: - raise IndexError(f"Range [{from_index}, {to_index}) out of bounds [0, {length})") - return from_index - - @staticmethod - def checkFromIndexSize(from_index: int, size: int, length: int) -> int: - if from_index < 0 or size < 0 or from_index + size > length: - raise IndexError(f"Range [{from_index}, {from_index + size}) out of bounds [0, {length})") - return from_index - - -# ============================================================ -# Locale -# ============================================================ - -class Locale(Stub): - """java.util.Locale — basic locale representation""" - ENGLISH = None - FRENCH = None - GERMAN = None - ITALIAN = None - JAPANESE = None - KOREAN = None - CHINESE = None - SIMPLIFIED_CHINESE = None - TRADITIONAL_CHINESE = None - FRANCE = None - GERMANY = None - ITALY = None - JAPAN = None - KOREA = None - CHINA = None - UK = None - US = None - CANADA = None - CANADA_FRENCH = None - ROOT = None - - def __init__(self, language: str = "", country: str = "", variant: str = ""): - super().__init__() - self._language = language - self._country = country - self._variant = variant - - @staticmethod - def getDefault() -> 'Locale': - return Locale("en", "US") - - @staticmethod - def setDefault(locale: 'Locale') -> None: - pass - - def getLanguage(self) -> str: - return self._language - - def getCountry(self) -> str: - return self._country - - def getVariant(self) -> str: - return self._variant - - def getDisplayLanguage(self, locale: 'Locale' = None) -> str: - return self._language - - def getDisplayCountry(self, locale: 'Locale' = None) -> str: - return self._country - - def getDisplayName(self, locale: 'Locale' = None) -> str: - parts = [self._language] - if self._country: - parts.append(self._country) - if self._variant: - parts.append(self._variant) - return "_".join(parts) - - def toString(self) -> str: - return self.getDisplayName() - - def toLanguageTag(self) -> str: - tag = self._language - if self._country: - tag += "-" + self._country - if self._variant: - tag += "-" + self._variant - return tag - - @staticmethod - def forLanguageTag(tag: str) -> 'Locale': - parts = tag.replace("-", "_").split("_") - lang = parts[0] if len(parts) > 0 else "" - country = parts[1] if len(parts) > 1 else "" - variant = parts[2] if len(parts) > 2 else "" - return Locale(lang, country, variant) - - def equals(self, other) -> bool: - if isinstance(other, Locale): - return (self._language == other._language and - self._country == other._country and - self._variant == other._variant) - return False - - def hashCode(self) -> int: - return hash((self._language, self._country, self._variant)) & 0x7FFFFFFF - - -# Initialize Locale constants -Locale.ENGLISH = Locale("en") -Locale.FRENCH = Locale("fr") -Locale.GERMAN = Locale("de") -Locale.ITALIAN = Locale("it") -Locale.JAPANESE = Locale("ja") -Locale.KOREAN = Locale("ko") -Locale.CHINESE = Locale("zh") -Locale.SIMPLIFIED_CHINESE = Locale("zh", "CN") -Locale.TRADITIONAL_CHINESE = Locale("zh", "TW") -Locale.FRANCE = Locale("fr", "FR") -Locale.GERMANY = Locale("de", "DE") -Locale.ITALY = Locale("it", "IT") -Locale.JAPAN = Locale("ja", "JP") -Locale.KOREA = Locale("ko", "KR") -Locale.CHINA = Locale("zh", "CN") -Locale.UK = Locale("en", "GB") -Locale.US = Locale("en", "US") -Locale.CANADA = Locale("en", "CA") -Locale.CANADA_FRENCH = Locale("fr", "CA") -Locale.ROOT = Locale("", "") - - -# ============================================================ -# BitSet -# ============================================================ - -class BitSet(Stub): - """java.util.BitSet — bit manipulation""" - def __init__(self, nbits: int = 0): - super().__init__() - if isinstance(nbits, int): - self._bits = 0 - elif isinstance(nbits, BitSet): - self._bits = nbits._bits - else: - self._bits = 0 - - def _check(self, bit_index: int) -> None: - if bit_index < 0: - raise IndexError(f"bitIndex < 0: {bit_index}") - - def get(self, bit_index: int) -> bool: - self._check(bit_index) - return bool((self._bits >> bit_index) & 1) - - def getRange(self, from_index: int, to_index: int) -> 'BitSet': - self._check(from_index) - mask = ((1 << (to_index - from_index)) - 1) << from_index - result = BitSet() - result._bits = (self._bits & mask) >> from_index - return result - - def set(self, bit_index: int, value: bool = True) -> None: - self._check(bit_index) - if value: - self._bits |= (1 << bit_index) - else: - self._bits &= ~(1 << bit_index) - - def setRange(self, from_index: int, to_index: int, value: bool = True) -> None: - self._check(from_index) - mask = ((1 << (to_index - from_index)) - 1) << from_index - if value: - self._bits |= mask - else: - self._bits &= ~mask - - def clear(self, bit_index: int = None) -> None: - if bit_index is None: - self._bits = 0 - else: - self._check(bit_index) - self._bits &= ~(1 << bit_index) - - def clearRange(self, from_index: int, to_index: int) -> None: - self.setRange(from_index, to_index, False) - - def flip(self, bit_index: int) -> None: - self._check(bit_index) - self._bits ^= (1 << bit_index) - - def flipRange(self, from_index: int, to_index: int) -> None: - self._check(from_index) - mask = ((1 << (to_index - from_index)) - 1) << from_index - self._bits ^= mask - - def cardinality(self) -> int: - return self._bits.bit_count() - - def intersects(self, bs: 'BitSet') -> bool: - return (self._bits & bs._bits) != 0 - - def and_(self, bs: 'BitSet') -> None: - self._bits &= bs._bits - - def or_(self, bs: 'BitSet') -> None: - self._bits |= bs._bits - - def xor(self, bs: 'BitSet') -> None: - self._bits ^= bs._bits - - def andNot(self, bs: 'BitSet') -> None: - self._bits &= ~bs._bits - - def size(self) -> int: - return self._bits.bit_length() + (8 - (self._bits.bit_length() % 8)) % 8 - - def length(self) -> int: - return self._bits.bit_length() - - def isEmpty(self) -> bool: - return self._bits == 0 - - def nextSetBit(self, from_index: int) -> int: - self._check(from_index) - bits = self._bits >> from_index - if bits == 0: - return -1 - return (bits & -bits).bit_length() - 1 + from_index - - def nextClearBit(self, from_index: int) -> int: - self._check(from_index) - bits = ~(self._bits >> from_index) - if bits == 0: - return -1 - return (bits & -bits).bit_length() - 1 + from_index - - def previousSetBit(self, from_index: int) -> int: - if from_index < 0: - return -1 - mask = (1 << (from_index + 1)) - 1 - bits = self._bits & mask - if bits == 0: - return -1 - return bits.bit_length() - 1 - - def previousClearBit(self, from_index: int) -> int: - if from_index < 0: - return -1 - mask = (1 << (from_index + 1)) - 1 - bits = (~self._bits) & mask - if bits == 0: - return -1 - return bits.bit_length() - 1 - - def toByteArray(self) -> bytearray: - if self._bits == 0: - return bytearray() - n_bytes = (self._bits.bit_length() + 7) // 8 - return bytearray(self._bits.to_bytes(n_bytes, 'little')) - - def toLongArray(self) -> list: - if self._bits == 0: - return [] - n_longs = (self._bits.bit_length() + 63) // 64 - result = [] - for i in range(n_longs): - result.append((self._bits >> (i * 64)) & 0xFFFFFFFFFFFFFFFF) - return result - - @staticmethod - def valueOf(bytes_or_longs) -> 'BitSet': - bs = BitSet() - if isinstance(bytes_or_longs, (bytes, bytearray)): - bs._bits = int.from_bytes(bytes_or_longs, 'little') - elif isinstance(bytes_or_longs, list): - for i, v in enumerate(bytes_or_longs): - bs._bits |= (v & 0xFFFFFFFFFFFFFFFF) << (i * 64) - return bs - - def stream(self): - bits = [] - i = 0 - while True: - idx = self.nextSetBit(i) - if idx < 0: - break - bits.append(idx) - i = idx + 1 - return bits - - def equals(self, other) -> bool: - if isinstance(other, BitSet): - return self._bits == other._bits - return False - - def hashCode(self) -> int: - return hash(self._bits) & 0x7FFFFFFF - - def toString(self) -> str: - bits = [] - i = 0 - while True: - idx = self.nextSetBit(i) - if idx < 0: - break - bits.append(str(idx)) - i = idx + 1 - return "{" + ", ".join(bits) + "}" - - def clone(self) -> 'BitSet': - return BitSet(self) - - def __repr__(self): - return self.toString() - - def __eq__(self, other): - return self.equals(other) - - -# ============================================================ -# PriorityQueue -# ============================================================ - -class PriorityQueue(Stub): - """java.util.PriorityQueue — using heapq""" - def __init__(self, initial_capacity: Any = None, comparator=None): - super().__init__() - self._heap: list = [] - self._comparator = comparator - if initial_capacity is not None and not isinstance(initial_capacity, (int, float)): - if isinstance(initial_capacity, _IterableABC): - for item in initial_capacity: - self._heap.append(item) - if comparator: - self._heap.sort(key=comparator) - else: - self._heap.sort() - - def _wrap_key(self, obj): - if self._comparator: - return (self._comparator(obj), obj) - return obj - - def add(self, obj) -> bool: - _heapq.heappush(self._heap, self._wrap_key(obj)) - return True - - def offer(self, obj) -> bool: - return self.add(obj) - - def peek(self): - return self._heap[0][1] if self._heap and self._comparator else (self._heap[0] if self._heap else None) - - def element(self): - val = self.peek() - if val is None: - raise NoSuchElementException() - return val - - def poll(self): - if not self._heap: - return None - val = _heapq.heappop(self._heap) - return val[1] if self._comparator else val - - def remove(self, obj) -> bool: - if self._comparator: - wrapped = self._wrap_key(obj) - for i, v in enumerate(self._heap): - if v == wrapped: - self._heap[i] = self._heap[-1] - self._heap.pop() - _heapq.heapify(self._heap) - return True - else: - for i, v in enumerate(self._heap): - if v == obj: - self._heap[i] = self._heap[-1] - self._heap.pop() - _heapq.heapify(self._heap) - return True - return False - - def size(self) -> int: - return len(self._heap) - - def isEmpty(self) -> bool: - return len(self._heap) == 0 - - def clear(self) -> None: - self._heap.clear() - - def comparator(self): - return self._comparator - - def iterator(self) -> Iterator: - return Iterator(list(self._heap)) - - def toArray(self, a=None): - if self._comparator: - items = sorted([v[1] for v in self._heap], key=self._comparator) - else: - items = sorted(self._heap) - if a is None: - return items - return items[:len(a)] - - def forEach(self, action) -> None: - for item in list(self._heap): - action(item) - - def spliterator(self) -> _Spliterator: - if self._comparator: - items = [v[1] for v in self._heap] - else: - items = list(self._heap) - return _Spliterator(items) - - def clone(self) -> 'PriorityQueue': - result = PriorityQueue(comparator=self._comparator) - result._heap = list(self._heap) - return result - - def toString(self) -> str: - items = ", ".join(str(x) for x in self._heap) - return f"[{items}]" - - def __len__(self): - return len(self._heap) - - def __repr__(self): - return self.toString() - - -# ============================================================ -# Vector -# ============================================================ - -class Vector(Stub): - """java.util.Vector — synchronized list""" - def __init__(self, initial_capacity: Any = None, capacity_increment: int = 0): - super().__init__() - if initial_capacity is None: - self._data = [] - elif isinstance(initial_capacity, int): - self._data = [] - elif isinstance(initial_capacity, _IterableABC): - self._data = list(initial_capacity) - else: - self._data = [] - self._capacity_increment = capacity_increment - self._lock = _threading.Lock() - - def _synchronized(self, func, *args, **kwargs): - with self._lock: - return func(*args, **kwargs) - - def add(self, obj) -> bool: - with self._lock: - self._data.append(obj) - return True - - def addAt(self, index: int, obj) -> None: - with self._lock: - self._data.insert(index, obj) - - def addAll(self, c) -> bool: - with self._lock: - if isinstance(c, Vector): - self._data.extend(c._data) - else: - self._data.extend(c) - return True - - def addAllAt(self, index: int, c) -> bool: - with self._lock: - if isinstance(c, Vector): - self._data[index:index] = c._data - else: - self._data[index:index] = list(c) - return True - - def addElement(self, obj) -> None: - self.add(obj) - - def capacity(self) -> int: - with self._lock: - return len(self._data) - - def clear(self) -> None: - with self._lock: - self._data.clear() - - def clone(self) -> 'Vector': - with self._lock: - return Vector(self._data) - - def contains(self, obj) -> bool: - with self._lock: - return obj in self._data - - def containsAll(self, c) -> bool: - with self._lock: - if isinstance(c, Vector): - return all(x in self._data for x in c._data) - return all(x in self._data for x in c) - - def copyInto(self, an_array: list) -> None: - with self._lock: - for i, v in enumerate(self._data): - an_array[i] = v - - def elementAt(self, index: int): - with self._lock: - return self._data[index] - - def elements(self) -> Enumeration: - with self._lock: - return Enumeration(list(self._data)) - - def ensureCapacity(self, min_capacity: int) -> None: - pass - - def firstElement(self): - with self._lock: - if not self._data: - raise NoSuchElementException() - return self._data[0] - - def forEach(self, action) -> None: - with self._lock: - for item in list(self._data): - action(item) - - def get(self, index: int): - with self._lock: - return self._data[index] - - def indexOf(self, obj, index: int = 0) -> int: - with self._lock: - try: - return self._data.index(obj, index) - except ValueError: - return -1 - - def insertElementAt(self, obj, index: int) -> None: - with self._lock: - self._data.insert(index, obj) - - def isEmpty(self) -> bool: - with self._lock: - return len(self._data) == 0 - - def iterator(self) -> Iterator: - with self._lock: - return Iterator(list(self._data)) - - def lastElement(self): - with self._lock: - if not self._data: - raise NoSuchElementException() - return self._data[-1] - - def lastIndexOf(self, obj, index: int = None) -> int: - with self._lock: - if index is None: - index = len(self._data) - 1 - for i in range(index, -1, -1): - if self._data[i] == obj: - return i - return -1 - - def listIterator(self, index: int = 0) -> ListIterator: - with self._lock: - li = ListIterator(list(self._data)) - li._index = index - return li - - def remove(self, obj) -> bool: - with self._lock: - if obj in self._data: - self._data.remove(obj) - return True - return False - - def removeAt(self, index: int): - with self._lock: - return self._data.pop(index) - - def removeAll(self, c) -> bool: - with self._lock: - if isinstance(c, Vector): - items = c._data - else: - items = list(c) - old_len = len(self._data) - self._data = [x for x in self._data if x not in items] - return len(self._data) != old_len - - def removeAllElements(self) -> None: - with self._lock: - self._data.clear() - - def removeElement(self, obj) -> bool: - return self.remove(obj) - - def removeElementAt(self, index: int) -> None: - with self._lock: - self._data.pop(index) - - def removeIf(self, predicate) -> bool: - with self._lock: - old_len = len(self._data) - self._data = [x for x in self._data if not predicate(x)] - return len(self._data) != old_len - - def replaceAll(self, operator) -> None: - with self._lock: - self._data = [operator(x) for x in self._data] - - def retainAll(self, c) -> bool: - with self._lock: - if isinstance(c, Vector): - items = set(c._data) - else: - items = set(c) - old_len = len(self._data) - self._data = [x for x in self._data if x in items] - return len(self._data) != old_len - - def set(self, index: int, obj): - with self._lock: - old = self._data[index] - self._data[index] = obj - return old - - def setElementAt(self, obj, index: int) -> None: - with self._lock: - self._data[index] = obj - - def setSize(self, new_size: int) -> None: - with self._lock: - while len(self._data) < new_size: - self._data.append(None) - while len(self._data) > new_size: - self._data.pop() - - def size(self) -> int: - with self._lock: - return len(self._data) - - def sort(self, key=None, reverse: bool = False) -> None: - with self._lock: - self._data.sort(key=key, reverse=reverse) - - def subList(self, from_index: int, to_index: int) -> 'Vector': - with self._lock: - return Vector(self._data[from_index:to_index]) - - def toArray(self, a=None): - with self._lock: - if a is None: - return list(self._data) - if len(a) < len(self._data): - return list(self._data) - for i, v in enumerate(self._data): - a[i] = v - if len(self._data) < len(a): - a[len(self._data)] = None - return a - - def toString(self) -> str: - with self._lock: - items = ", ".join(str(x) for x in self._data) - return f"[{items}]" - - def trimToSize(self) -> None: - pass - - def __len__(self): - with self._lock: - return len(self._data) - - def __getitem__(self, idx): - with self._lock: - return self._data[idx] - - def __setitem__(self, idx, val): - with self._lock: - self._data[idx] = val - - def __delitem__(self, idx): - with self._lock: - del self._data[idx] - - def __iter__(self): - with self._lock: - return iter(list(self._data)) - - def __repr__(self): - return self.toString() - - def __contains__(self, obj): - with self._lock: - return obj in self._data - - def __eq__(self, other): - if isinstance(other, Vector): - with self._lock: - return self._data == other._data - return False - - -# ============================================================ -# Stack -# ============================================================ - -class Stack(Vector): - """java.util.Stack — extends Vector""" - def __init__(self): - super().__init__() - - def push(self, item): - with self._lock: - self._data.append(item) - return item - - def pop(self): - with self._lock: - if not self._data: - raise EmptyStackException() - return self._data.pop() - - def peek(self): - with self._lock: - if not self._data: - raise EmptyStackException() - return self._data[-1] - - def empty(self) -> bool: - with self._lock: - return len(self._data) == 0 - - def search(self, obj) -> int: - with self._lock: - try: - idx = len(self._data) - 1 - self._data[::-1].index(obj) - return len(self._data) - idx - except ValueError: - return -1 - - def add(self, obj) -> bool: - self.push(obj) - return True - - def remove(self, obj) -> bool: - with self._lock: - if obj in self._data: - self._data.remove(obj) - return True - return False \ No newline at end of file diff --git a/android_pyapi/java/util_concurrent.py b/android_pyapi/java/util_concurrent.py deleted file mode 100644 index fd2c16a..0000000 --- a/android_pyapi/java/util_concurrent.py +++ /dev/null @@ -1,2037 +0,0 @@ -""" -Java util.concurrent standard library APIs implemented in Python. -java.util.concurrent package - Executors, ThreadPoolExecutor, CountDownLatch, etc. -""" - -from .._core import Stub -from .lang import RuntimeException -import threading as _threading -import concurrent.futures as _futures -import time as _time -import queue as _queue -import math as _py_math - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) -CancellationException = type("CancellationException", (RuntimeException,), {}) -ExecutionException = type("ExecutionException", (RuntimeException,), {}) -RejectedExecutionException = type("RejectedExecutionException", (RuntimeException,), {}) -TimeoutException = type("TimeoutException", (RuntimeException,), {}) -BrokenBarrierException = type("BrokenBarrierException", (RuntimeException,), {}) -CompletionException = type("CompletionException", (RuntimeException,), {}) - -__all__ = [ - "Executors", "ExecutorService", "Executor", "ExecutorCompletionService", - "ThreadPoolExecutor", "AbstractExecutorService", - "ScheduledExecutorService", "ScheduledThreadPoolExecutor", - "FutureTask", "Future", "RunnableFuture", "RunnableScheduledFuture", - "Callable", "CompletionService", "CompletionStage", - "CountDownLatch", "CyclicBarrier", "Semaphore", "Phaser", "Exchanger", - "TimeUnit", - "TimeoutException", "CancellationException", "ExecutionException", - "RejectedExecutionException", "BrokenBarrierException", - "CompletionException", - "ConcurrentHashMap", "CopyOnWriteArrayList", "CopyOnWriteArraySet", - "ConcurrentLinkedQueue", "ConcurrentLinkedDeque", - "DelayQueue", "SynchronousQueue", "LinkedBlockingQueue", - "ArrayBlockingQueue", "PriorityBlockingQueue", - "BlockingQueue", "BlockingDeque", - "AtomicBoolean", "AtomicInteger", "AtomicLong", "AtomicReference", - "AtomicIntegerArray", "AtomicLongArray", "AtomicReferenceArray", - "AtomicIntegerFieldUpdater", "AtomicLongFieldUpdater", - "AtomicReferenceFieldUpdater", - "AtomicMarkableReference", "AtomicStampedReference", - "DoubleAccumulator", "DoubleAdder", - "LongAccumulator", "LongAdder", - "Lock", "ReentrantLock", "ReentrantReadWriteLock", - "ReadWriteLock", "Condition", "LockSupport", - "AbstractQueuedSynchronizer", "AbstractQueuedLongSynchronizer", - "Flow", "SubmissionPublisher", - "RecursiveAction", "RecursiveTask", "ForkJoinPool", - "ForkJoinTask", "ForkJoinWorkerThread", - "CompletableFuture", -] - - -# ============================================================ -# Interfaces -# ============================================================ - -class Callable(Stub): - def call(self): - raise NotImplementedError - - -class Future(Stub): - def get(self, timeout=None, unit=None): - raise NotImplementedError - - def cancel(self, may_interrupt): - raise NotImplementedError - - def isCancelled(self): - raise NotImplementedError - - def isDone(self): - raise NotImplementedError - - -class RunnableFuture(Future): - def run(self): - raise NotImplementedError - - -class RunnableScheduledFuture(RunnableFuture): - def isPeriodic(self): - raise NotImplementedError - - -class CompletionService(Stub): - def submit(self, task): - raise NotImplementedError - - def take(self): - raise NotImplementedError - - def poll(self, timeout=0, unit=None): - raise NotImplementedError - - -class CompletionStage(Stub): - pass - - -class Executor(Stub): - def execute(self, command): - raise NotImplementedError - - -class ExecutorService(Executor): - def submit(self, task, *args): - raise NotImplementedError - - def shutdown(self): - raise NotImplementedError - - def shutdownNow(self): - raise NotImplementedError - - def isShutdown(self): - raise NotImplementedError - - def isTerminated(self): - raise NotImplementedError - - def awaitTermination(self, timeout, unit): - raise NotImplementedError - - def invokeAll(self, tasks): - raise NotImplementedError - - def invokeAny(self, tasks): - raise NotImplementedError - - -class ScheduledExecutorService(ExecutorService): - def schedule(self, command, delay, unit): - raise NotImplementedError - - def scheduleAtFixedRate(self, command, initial_delay, period, unit): - raise NotImplementedError - - def scheduleWithFixedDelay(self, command, initial_delay, delay, unit): - raise NotImplementedError - - -# ============================================================ -# Runnable -# ============================================================ - -class Runnable(Stub): - def run(self): - raise NotImplementedError - - -# ============================================================ -# FutureTask -# ============================================================ - -class FutureTask(RunnableFuture): - def __init__(self, callable_or_runnable, result=None): - super().__init__() - self._callable = callable_or_runnable if isinstance(callable_or_runnable, Callable) else None - self._runnable = callable_or_runnable if isinstance(callable_or_runnable, Runnable) else None - self._result = result - self._done = False - self._cancelled = False - self._exception = None - self._lock = _threading.RLock() - self._done_event = _threading.Event() - - def run(self): - if self._cancelled: - return - try: - if self._callable: - self._result = self._callable.call() - elif self._runnable: - self._runnable.run() - with self._lock: - self._done = True - self._done_event.set() - except Exception as e: - with self._lock: - self._exception = e - self._done = True - self._done_event.set() - - def get(self, timeout=None, unit=None): - if timeout is not None and unit is not None: - seconds = unit.toMillis(timeout) / 1000.0 - if not self._done_event.wait(seconds): - raise TimeoutException() - else: - self._done_event.wait() - with self._lock: - if self._exception: - raise ExecutionException(str(self._exception)) - return self._result - - def cancel(self, may_interrupt): - with self._lock: - if self._done: - return False - self._cancelled = True - self._done = True - self._done_event.set() - return True - - def isCancelled(self): - return self._cancelled - - def isDone(self): - return self._done - - def doneTask(self): - pass - - def set(self, v): - with self._lock: - self._result = v - self._done = True - self._done_event.set() - - def setException(self, ex): - with self._lock: - self._exception = ex - self._done = True - self._done_event.set() - - -# ============================================================ -# CompletableFuture -# ============================================================ - -class CompletableFuture(Future): - def __init__(self): - super().__init__() - self._result = None - self._exception = None - self._done = False - self._lock = _threading.RLock() - self._done_event = _threading.Event() - self._callbacks = [] - - def _run_callbacks(self): - for cb in self._callbacks: - try: - cb(self) - except Exception: - pass - - def complete(self, result): - with self._lock: - if self._done: - return self - self._result = result - self._done = True - self._done_event.set() - self._run_callbacks() - return self - - def completeExceptionally(self, ex): - with self._lock: - if self._done: - return self - self._exception = ex - self._done = True - self._done_event.set() - self._run_callbacks() - return self - - def cancel(self, may_interrupt): - with self._lock: - if self._done: - return False - self._cancelled = True - self._done = True - self._done_event.set() - self._run_callbacks() - return True - - def get(self, timeout=None, unit=None): - if timeout is not None and unit is not None: - seconds = unit.toMillis(timeout) / 1000.0 - if not self._done_event.wait(seconds): - raise TimeoutException() - else: - self._done_event.wait() - with self._lock: - if self._exception: - raise ExecutionException(str(self._exception)) - return self._result - - def join(self): - return self.get() - - def isDone(self): - return self._done - - def isCancelled(self): - return getattr(self, "_cancelled", False) - - def thenApply(self, fn): - future = CompletableFuture() - def cb(f): - try: - future.complete(fn(f.get())) - except Exception as e: - future.completeExceptionally(e) - self._callbacks.append(cb) - return future - - def thenAccept(self, fn): - future = CompletableFuture() - def cb(f): - try: - fn(f.get()) - future.complete(None) - except Exception as e: - future.completeExceptionally(e) - self._callbacks.append(cb) - return future - - def thenRun(self, action): - future = CompletableFuture() - def cb(f): - try: - action.run() - future.complete(None) - except Exception as e: - future.completeExceptionally(e) - self._callbacks.append(cb) - return future - - def thenCombine(self, other, fn): - future = CompletableFuture() - def cb(f): - try: - other_result = other.get() - future.complete(fn(f.get(), other_result)) - except Exception as e: - future.completeExceptionally(e) - self._callbacks.append(cb) - return future - - def thenCompose(self, fn): - future = CompletableFuture() - def cb(f): - try: - next_future = fn(f.get()) - future.complete(next_future.get()) - except Exception as e: - future.completeExceptionally(e) - self._callbacks.append(cb) - return future - - def exceptionally(self, fn): - future = CompletableFuture() - def cb(f): - try: - f.get() - future.complete(f._result) - except Exception as e: - try: - fallback = fn(e) - future.complete(fallback) - except Exception as e2: - future.completeExceptionally(e2) - self._callbacks.append(cb) - return future - - @staticmethod - def completedFuture(result): - f = CompletableFuture() - f.complete(result) - return f - - @staticmethod - def failedFuture(exception): - f = CompletableFuture() - f.completeExceptionally(exception) - return f - - @staticmethod - def supplyAsync(supplier, executor=None): - f = CompletableFuture() - def run(): - try: - f.complete(supplier.get()) - except Exception as e: - f.completeExceptionally(e) - t = _threading.Thread(target=run, daemon=True) - t.start() - return f - - @staticmethod - def runAsync(runnable, executor=None): - f = CompletableFuture() - def run(): - try: - runnable.run() - f.complete(None) - except Exception as e: - f.completeExceptionally(e) - t = _threading.Thread(target=run, daemon=True) - t.start() - return f - - @staticmethod - def allOf(*futures): - result = CompletableFuture() - def check_all(): - for f in futures: - f.get() - result.complete(None) - t = _threading.Thread(target=check_all, daemon=True) - t.start() - return result - - @staticmethod - def anyOf(*futures): - result = CompletableFuture() - def check_any(): - while True: - for f in futures: - if f.isDone(): - result.complete(f.get()) - return - _time.sleep(0.01) - t = _threading.Thread(target=check_any, daemon=True) - t.start() - return result - - -# ============================================================ -# Executors -# ============================================================ - -class Executors(Stub): - @staticmethod - def newCachedThreadPool(): - pool = ThreadPoolExecutor(0, _futures.ThreadPoolExecutor(max_workers=10)) - pool._executor._max_workers = None - return pool - - @staticmethod - def newFixedThreadPool(n_threads): - return ThreadPoolExecutor(n_threads, _futures.ThreadPoolExecutor(max_workers=n_threads)) - - @staticmethod - def newSingleThreadExecutor(): - return ThreadPoolExecutor(1, _futures.ThreadPoolExecutor(max_workers=1)) - - @staticmethod - def newScheduledThreadPool(core_pool_size): - return ScheduledThreadPoolExecutor(core_pool_size) - - @staticmethod - def newWorkStealingPool(parallelism=None): - return ThreadPoolExecutor(parallelism or 4, _futures.ThreadPoolExecutor(max_workers=parallelism or 4)) - - @staticmethod - def defaultThreadFactory(): - return _threading.Thread - - @staticmethod - def privilegedThreadFactory(): - return _threading.Thread - - @staticmethod - def callable(runnable, result=None): - class _Callable(Callable): - def call(self): - if runnable: - runnable.run() - return result - return _Callable() - - -# ============================================================ -# ThreadPoolExecutor -# ============================================================ - -class ThreadPoolExecutor(ExecutorService): - def __init__(self, core_pool_size, executor=None): - super().__init__() - self._executor = executor or _futures.ThreadPoolExecutor(max_workers=core_pool_size) - self._core_pool_size = core_pool_size - self._max_pool_size = core_pool_size - self._keep_alive_time = 60 - self._allow_core_thread_timeout = False - - def execute(self, command): - def _run(): - try: - command.run() - except Exception: - pass - self._executor.submit(_run) - - def submit(self, task, *args): - if isinstance(task, Callable): - future = FutureTask(task) - self._executor.submit(future.run) - return future - elif isinstance(task, Runnable): - future = FutureTask(task) - self._executor.submit(future.run) - return future - else: - future = FutureTask(task) - self._executor.submit(future.run) - return future - - def shutdown(self): - self._executor.shutdown(wait=False) - - def shutdownNow(self): - self._executor.shutdown(wait=False) - - def isShutdown(self): - return self._executor._shutdown - - def isTerminated(self): - return self._executor._shutdown - - def awaitTermination(self, timeout, unit): - seconds = unit.toMillis(timeout) / 1000.0 - self._executor.shutdown(wait=True) - - def getPoolSize(self): - return self._core_pool_size - - def getActiveCount(self): - return 0 - - def getTaskCount(self): - return 0 - - def getCompletedTaskCount(self): - return 0 - - def getLargestPoolSize(self): - return self._core_pool_size - - def setCorePoolSize(self, size): - self._core_pool_size = size - - def setMaximumPoolSize(self, size): - self._max_pool_size = size - - def setKeepAliveTime(self, time, unit): - self._keep_alive_time = unit.toMillis(time) / 1000.0 - - def prestartAllCoreThreads(self): - pass - - def prestartCoreThread(self): - pass - - def allowsCoreThreadTimeOut(self): - return self._allow_core_thread_timeout - - def setRejectedExecutionHandler(self, handler): - pass - - def getRejectedExecutionHandler(self): - return None - - -class AbstractExecutorService(ExecutorService): - pass - - -# ============================================================ -# ScheduledThreadPoolExecutor -# ============================================================ - -class ScheduledThreadPoolExecutor(ThreadPoolExecutor, ScheduledExecutorService): - def __init__(self, core_pool_size): - super().__init__(core_pool_size) - - def schedule(self, command, delay, unit): - future = FutureTask(command) - seconds = unit.toMillis(delay) / 1000.0 - t = _threading.Timer(seconds, future.run) - t.daemon = True - t.start() - return future - - def scheduleAtFixedRate(self, command, initial_delay, period, unit): - seconds = unit.toMillis(period) / 1000.0 - init_sec = unit.toMillis(initial_delay) / 1000.0 - def _run(): - _time.sleep(init_sec) - while True: - command.run() - _time.sleep(seconds) - t = _threading.Thread(target=_run, daemon=True) - t.start() - return FutureTask(command) - - def scheduleWithFixedDelay(self, command, initial_delay, delay, unit): - return self.scheduleAtFixedRate(command, initial_delay, delay, unit) - - -class ExecutorCompletionService(CompletionService): - def __init__(self, executor): - super().__init__() - self._executor = executor - self._queue = _queue.Queue() - - def submit(self, task): - future = self._executor.submit(task) - self._queue.put(future) - return future - - def take(self): - return self._queue.get() - - def poll(self, timeout=0, unit=None): - if unit is not None: - sec = unit.toMillis(timeout) / 1000.0 - else: - sec = timeout - try: - return self._queue.get(timeout=sec) - except _queue.Empty: - return None - - -# ============================================================ -# TimeUnit -# ============================================================ - -class TimeUnit(Stub): - NANOSECONDS = None - MICROSECONDS = None - MILLISECONDS = None - SECONDS = None - MINUTES = None - HOURS = None - DAYS = None - - def __init__(self, name, scale): - self._name = name - self._scale = scale # nanos per unit - - def convert(self, source_duration, source_unit): - return int(source_duration * source_unit._scale / self._scale) - - def toNanos(self, duration): - return int(duration * self._scale) - - def toMicros(self, duration): - return int(duration * self._scale // 1000) - - def toMillis(self, duration): - return int(duration * self._scale // 1_000_000) - - def toSeconds(self, duration): - return int(duration * self._scale // 1_000_000_000) - - def toMinutes(self, duration): - return self.toSeconds(duration) // 60 - - def toHours(self, duration): - return self.toMinutes(duration) // 60 - - def toDays(self, duration): - return self.toHours(duration) // 24 - - def sleep(self, timeout): - _time.sleep(self.toMillis(timeout) / 1000.0) - - def timedJoin(self, thread, timeout): - thread.join(self.toMillis(timeout) / 1000.0) - - def timedWait(self, obj, timeout): - return True - - def toString(self): - return self._name - - -TimeUnit.NANOSECONDS = TimeUnit("NANOSECONDS", 1) -TimeUnit.MICROSECONDS = TimeUnit("MICROSECONDS", 1000) -TimeUnit.MILLISECONDS = TimeUnit("MILLISECONDS", 1_000_000) -TimeUnit.SECONDS = TimeUnit("SECONDS", 1_000_000_000) -TimeUnit.MINUTES = TimeUnit("MINUTES", 60_000_000_000) -TimeUnit.HOURS = TimeUnit("HOURS", 3_600_000_000_000) -TimeUnit.DAYS = TimeUnit("DAYS", 86_400_000_000_000) - - -# ============================================================ -# CountDownLatch -# ============================================================ - -class CountDownLatch(Stub): - def __init__(self, count): - super().__init__() - self._count = count - self._condition = _threading.Condition() - - def await_(self, timeout=None, unit=None): - with self._condition: - if self._count == 0: - return True - if timeout is not None and unit is not None: - seconds = unit.toMillis(timeout) / 1000.0 - self._condition.wait(timeout=seconds) - return self._count == 0 - self._condition.wait() - return True - - def countDown(self): - with self._condition: - if self._count > 0: - self._count -= 1 - if self._count == 0: - self._condition.notify_all() - - def getCount(self): - return self._count - - def toString(self): - return f"CountDownLatch(count={self._count})" - - -# ============================================================ -# CyclicBarrier -# ============================================================ - -class CyclicBarrier(Stub): - def __init__(self, parties, barrier_action=None): - super().__init__() - self._parties = parties - self._barrier_action = barrier_action - self._count = parties - self._broken = False - self._lock = _threading.RLock() - self._condition = _threading.Condition() - - def await_(self, timeout=None, unit=None): - with self._condition: - if self._broken: - raise BrokenBarrierException() - self._count -= 1 - if self._count == 0: - if self._barrier_action: - try: - self._barrier_action.run() - except Exception: - self._broken = True - self._condition.notify_all() - raise BrokenBarrierException() - self._count = self._parties - self._condition.notify_all() - return 0 - if timeout is not None and unit is not None: - seconds = unit.toMillis(timeout) / 1000.0 - self._condition.wait(timeout=seconds) - if self._count > 0: - self._broken = True - raise BrokenBarrierException() - else: - self._condition.wait() - return self._count - - def getNumberWaiting(self): - return self._parties - self._count - - def getParties(self): - return self._parties - - def isBroken(self): - return self._broken - - def reset(self): - with self._condition: - self._broken = False - self._count = self._parties - self._condition.notify_all() - - -# ============================================================ -# Semaphore -# ============================================================ - -class Semaphore(Stub): - def __init__(self, permits, fair=False): - super().__init__() - self._sem = _threading.Semaphore(permits) if not fair else _threading.Semaphore(permits) - self._fair = fair - self._permits = permits - self._lock = _threading.Lock() - - def acquire(self): - self._sem.acquire() - - def acquireUninterruptibly(self): - self._sem.acquire() - - def release(self): - self._sem.release() - - def tryAcquire(self, timeout=None, unit=None): - if timeout is not None and unit is not None: - seconds = unit.toMillis(timeout) / 1000.0 - return self._sem.acquire(blocking=True, timeout=seconds) - return self._sem.acquire(blocking=False) - - def availablePermits(self): - return self._sem._value - - def drainPermits(self): - with self._lock: - val = self._sem._value - for _ in range(val): - self._sem.acquire(blocking=False) - return val - - def getQueueLength(self): - return 0 - - def hasQueuedThreads(self): - return False - - -# ============================================================ -# Phaser (stub) -# ============================================================ - -class Phaser(Stub): - def __init__(self, parties=0): - super().__init__() - self._parties = parties - self._phase = 0 - self._arrived = 0 - - def register(self): - self._parties += 1 - - def arrive(self): - self._arrived += 1 - return self._phase - - def arriveAndDeregister(self): - self._arrived += 1 - self._parties -= 1 - return self._phase - - def arriveAndAwaitAdvance(self): - self._arrived += 1 - if self._arrived >= self._parties: - self._phase += 1 - self._arrived = 0 - return self._phase - - def awaitAdvance(self, phase): - return self._phase - - def getPhase(self): - return self._phase - - def getArrivedParties(self): - return self._arrived - - def getRegisteredParties(self): - return self._parties - - def getUnarrivedParties(self): - return self._parties - self._arrived - - def isTerminated(self): - return False - - def forceTermination(self): - pass - - -# ============================================================ -# Exchanger -# ============================================================ - -class Exchanger(Stub): - def __init__(self): - super().__init__() - self._lock = _threading.Lock() - self._item = None - self._other_ready = _threading.Event() - - def exchange(self, x, timeout=None, unit=None): - with self._lock: - if self._item is None: - self._item = x - self._other_ready.set() - else: - result = self._item - self._item = x - self._other_ready.set() - return result - if timeout is not None and unit is not None: - seconds = unit.toMillis(timeout) / 1000.0 - self._other_ready.wait(timeout=seconds) - else: - self._other_ready.wait() - with self._lock: - result = self._item - self._item = None - return result - - -# ============================================================ -# ConcurrentHashMap -# ============================================================ - -class ConcurrentHashMap(Stub): - def __init__(self, initial_capacity=16): - super().__init__() - self._map = {} - self._lock = _threading.RLock() - - def put(self, key, value): - with self._lock: - self._map[key] = value - return None - - def get(self, key): - with self._lock: - return self._map.get(key) - - def containsKey(self, key): - with self._lock: - return key in self._map - - def containsValue(self, value): - with self._lock: - return value in self._map.values() - - def size(self): - with self._lock: - return len(self._map) - - def isEmpty(self): - with self._lock: - return len(self._map) == 0 - - def remove(self, key): - with self._lock: - return self._map.pop(key, None) - - def clear(self): - with self._lock: - self._map.clear() - - def keySet(self): - with self._lock: - return set(self._map.keys()) - - def values(self): - with self._lock: - return list(self._map.values()) - - def entrySet(self): - with self._lock: - return set(self._map.items()) - - def putAll(self, m): - with self._lock: - if hasattr(m, "_map"): - self._map.update(m._map) - elif isinstance(m, dict): - self._map.update(m) - - def putIfAbsent(self, key, value): - with self._lock: - if key not in self._map: - self._map[key] = value - return None - return self._map[key] - - def getOrDefault(self, key, default): - with self._lock: - return self._map.get(key, default) - - def forEach(self, action): - with self._lock: - for k, v in self._map.items(): - action(k, v) - - -# ============================================================ -# CopyOnWriteArrayList / CopyOnWriteArraySet -# ============================================================ - -class CopyOnWriteArrayList(Stub): - def __init__(self, *args): - super().__init__() - self._list = [] - self._lock = _threading.RLock() - if args and isinstance(args[0], (list, tuple)): - self._list = list(args[0]) - - def add(self, e): - with self._lock: - self._list = list(self._list) - self._list.append(e) - return True - - def get(self, index): - return self._list[index] - - def set(self, index, element): - with self._lock: - old = self._list[index] - self._list = list(self._list) - self._list[index] = element - return old - - def remove(self, e): - with self._lock: - new_list = list(self._list) - try: - new_list.remove(e) - self._list = new_list - return True - except ValueError: - return False - - def size(self): - return len(self._list) - - def isEmpty(self): - return len(self._list) == 0 - - def contains(self, e): - return e in self._list - - def iterator(self): - return iter(list(self._list)) - - def toArray(self): - return list(self._list) - - def clear(self): - with self._lock: - self._list = [] - - -class CopyOnWriteArraySet(Stub): - def __init__(self): - super().__init__() - self._backend = CopyOnWriteArrayList() - - def add(self, e): - if self._backend.contains(e): - return False - return self._backend.add(e) - - def remove(self, e): - return self._backend.remove(e) - - def contains(self, e): - return self._backend.contains(e) - - def size(self): - return self._backend.size() - - def isEmpty(self): - return self._backend.isEmpty() - - def iterator(self): - return self._backend.iterator() - - -# ============================================================ -# BlockingQueue / LinkedBlockingQueue / ArrayBlockingQueue -# ============================================================ - -class BlockingQueue(Stub): - pass - - -class LinkedBlockingQueue(BlockingQueue): - def __init__(self, capacity=None): - super().__init__() - self._queue = _queue.Queue(maxsize=capacity if capacity else 0) - - def put(self, item): - self._queue.put(item) - - def take(self): - return self._queue.get() - - def offer(self, item, timeout=None, unit=None): - if timeout is not None and unit is not None: - seconds = unit.toMillis(timeout) / 1000.0 - try: - self._queue.put(item, timeout=seconds) - return True - except _queue.Full: - return False - try: - self._queue.put_nowait(item) - return True - except _queue.Full: - return False - - def poll(self, timeout=None, unit=None): - if timeout is not None and unit is not None: - seconds = unit.toMillis(timeout) / 1000.0 - try: - return self._queue.get(timeout=seconds) - except _queue.Empty: - return None - try: - return self._queue.get_nowait() - except _queue.Empty: - return None - - def size(self): - return self._queue.qsize() - - def isEmpty(self): - return self._queue.empty() - - def remainingCapacity(self): - return self._queue.maxsize - self._queue.qsize() - - def clear(self): - while not self._queue.empty(): - try: - self._queue.get_nowait() - except _queue.Empty: - break - - def contains(self, item): - return item in list(self._queue.queue) - - -class ArrayBlockingQueue(LinkedBlockingQueue): - def __init__(self, capacity, fair=False): - super().__init__(capacity) - - -class PriorityBlockingQueue(BlockingQueue): - def __init__(self, capacity=None): - super().__init__() - self._heap = [] - self._lock = _threading.RLock() - self._not_empty = _threading.Condition(self._lock) - - def put(self, item): - with self._lock: - _queue.heappush(self._heap, item) - self._not_empty.notify() - - def take(self): - with self._lock: - while not self._heap: - self._not_empty.wait() - return _queue.heappop(self._heap) - - def offer(self, item, timeout=None, unit=None): - self.put(item) - return True - - def poll(self, timeout=None, unit=None): - with self._lock: - if not self._heap: - return None - return _queue.heappop(self._heap) - - def size(self): - with self._lock: - return len(self._heap) - - def isEmpty(self): - with self._lock: - return len(self._heap) == 0 - - def clear(self): - with self._lock: - self._heap = [] - - -class SynchronousQueue(BlockingQueue): - def __init__(self, fair=False): - super().__init__() - self._exchange = _queue.Queue(maxsize=1) - - def put(self, item): - self._exchange.put(item) - - def take(self): - return self._exchange.get() - - def offer(self, item, timeout=None, unit=None): - try: - self._exchange.put_nowait(item) - return True - except _queue.Full: - return False - - def poll(self, timeout=None, unit=None): - try: - return self._exchange.get_nowait() - except _queue.Empty: - return None - - -class DelayQueue(BlockingQueue): - def __init__(self): - super().__init__() - self._queue = [] - - def put(self, item): - _queue.heappush(self._queue, item) - - def take(self): - while True: - if self._queue: - item = self._queue[0] - delay = item.getDelay(TimeUnit.MILLISECONDS) - if delay <= 0: - return _queue.heappop(self._queue) - _time.sleep(delay / 1000.0) - else: - _time.sleep(0.1) - - -class ConcurrentLinkedQueue(Stub): - def __init__(self): - super().__init__() - self._queue = _queue.Queue() - - def add(self, item): - self._queue.put(item) - return True - - def offer(self, item): - self._queue.put_nowait(item) - return True - - def poll(self): - try: - return self._queue.get_nowait() - except _queue.Empty: - return None - - def peek(self): - try: - item = self._queue.get_nowait() - self._queue.put_nowait(item) - return item - except _queue.Empty: - return None - - def size(self): - return self._queue.qsize() - - def isEmpty(self): - return self._queue.empty() - - def contains(self, item): - return item in list(self._queue.queue) - - -class ConcurrentLinkedDeque(ConcurrentLinkedQueue): - def addFirst(self, item): - self._queue.put(item) - - def addLast(self, item): - self._queue.put(item) - - def pollFirst(self): - return self.poll() - - def pollLast(self): - return self.poll() - - -class BlockingDeque(BlockingQueue): - pass - - -# ============================================================ -# Atomic classes -# ============================================================ - -class AtomicBoolean(Stub): - def __init__(self, initial=False): - super().__init__() - self._value = initial - self._lock = _threading.Lock() - - def get(self): - with self._lock: - return self._value - - def set(self, new_value): - with self._lock: - self._value = new_value - - def compareAndSet(self, expect, update): - with self._lock: - if self._value == expect: - self._value = update - return True - return False - - def getAndSet(self, new_value): - with self._lock: - old = self._value - self._value = new_value - return old - - def toString(self): - return str(self._value) - - -class AtomicInteger(Stub): - def __init__(self, initial=0): - super().__init__() - self._value = int(initial) - self._lock = _threading.Lock() - - def get(self): - with self._lock: - return self._value - - def set(self, new_value): - with self._lock: - self._value = int(new_value) - - def getAndSet(self, new_value): - with self._lock: - old = self._value - self._value = int(new_value) - return old - - def compareAndSet(self, expect, update): - with self._lock: - if self._value == expect: - self._value = int(update) - return True - return False - - def weakCompareAndSet(self, expect, update): - return self.compareAndSet(expect, update) - - def getAndIncrement(self): - with self._lock: - old = self._value - self._value += 1 - return old - - def getAndDecrement(self): - with self._lock: - old = self._value - self._value -= 1 - return old - - def getAndAdd(self, delta): - with self._lock: - old = self._value - self._value += delta - return old - - def incrementAndGet(self): - with self._lock: - self._value += 1 - return self._value - - def decrementAndGet(self): - with self._lock: - self._value -= 1 - return self._value - - def addAndGet(self, delta): - with self._lock: - self._value += delta - return self._value - - def lazySet(self, new_value): - with self._lock: - self._value = int(new_value) - - def updateAndGet(self, func): - with self._lock: - self._value = func(self._value) - return self._value - - def accumulateAndGet(self, x, func): - with self._lock: - self._value = func(self._value, x) - return self._value - - def intValue(self): - return self.get() - - def longValue(self): - return self.get() - - def floatValue(self): - return float(self.get()) - - def doubleValue(self): - return float(self.get()) - - def toString(self): - return str(self._value) - - -class AtomicLong(AtomicInteger): - pass - - -class AtomicReference(Stub): - def __init__(self, initial=None): - super().__init__() - self._value = initial - self._lock = _threading.Lock() - - def get(self): - with self._lock: - return self._value - - def set(self, new_value): - with self._lock: - self._value = new_value - - def compareAndSet(self, expect, update): - with self._lock: - if self._value == expect: - self._value = update - return True - return False - - def getAndSet(self, new_value): - with self._lock: - old = self._value - self._value = new_value - return old - - def getAndUpdate(self, func): - with self._lock: - old = self._value - self._value = func(old) - return old - - def updateAndGet(self, func): - with self._lock: - self._value = func(self._value) - return self._value - - def accumulateAndGet(self, x, func): - with self._lock: - self._value = func(self._value, x) - return self._value - - -class AtomicIntegerArray(Stub): - def __init__(self, length_or_array): - super().__init__() - if isinstance(length_or_array, int): - self._array = [0] * length_or_array - else: - self._array = list(length_or_array) - - def get(self, i): - return self._array[i] - - def set(self, i, new_value): - self._array[i] = new_value - - def compareAndSet(self, i, expect, update): - if self._array[i] == expect: - self._array[i] = update - return True - return False - - def getAndAdd(self, i, delta): - old = self._array[i] - self._array[i] += delta - return old - - def addAndGet(self, i, delta): - self._array[i] += delta - return self._array[i] - - def length(self): - return len(self._array) - - -class AtomicLongArray(AtomicIntegerArray): - pass - - -class AtomicReferenceArray(Stub): - def __init__(self, length_or_array): - super().__init__() - if isinstance(length_or_array, int): - self._array = [None] * length_or_array - else: - self._array = list(length_or_array) - - def get(self, i): - return self._array[i] - - def set(self, i, new_value): - self._array[i] = new_value - - def compareAndSet(self, i, expect, update): - if self._array[i] == expect: - self._array[i] = update - return True - return False - - def length(self): - return len(self._array) - - -class AtomicIntegerFieldUpdater(Stub): - @staticmethod - def newUpdater(tclass, field_name): - return AtomicIntegerFieldUpdater() - - def get(self, obj): - return 0 - - def set(self, obj, new_value): - pass - - def compareAndSet(self, obj, expect, update): - return True - - -class AtomicLongFieldUpdater(AtomicIntegerFieldUpdater): - pass - - -class AtomicReferenceFieldUpdater(Stub): - @staticmethod - def newUpdater(tclass, vclass, field_name): - return AtomicReferenceFieldUpdater() - - def get(self, obj): - return None - - def set(self, obj, new_value): - pass - - def compareAndSet(self, obj, expect, update): - return True - - -class AtomicMarkableReference(Stub): - def __init__(self, initial_ref, initial_mark): - super().__init__() - self._ref = initial_ref - self._mark = initial_mark - - def getReference(self): - return self._ref - - def isMarked(self): - return self._mark - - def compareAndSet(self, expected_ref, new_ref, expected_mark, new_mark): - if self._ref == expected_ref and self._mark == expected_mark: - self._ref = new_ref - self._mark = new_mark - return True - return False - - def attemptMark(self, expected_ref, new_mark): - if self._ref == expected_ref: - self._mark = new_mark - return True - return False - - -class AtomicStampedReference(Stub): - def __init__(self, initial_ref, initial_stamp): - super().__init__() - self._ref = initial_ref - self._stamp = initial_stamp - - def getReference(self): - return self._ref - - def getStamp(self): - return self._stamp - - def compareAndSet(self, expected_ref, new_ref, expected_stamp, new_stamp): - if self._ref == expected_ref and self._stamp == expected_stamp: - self._ref = new_ref - self._stamp = new_stamp - return True - return False - - def attemptStamp(self, expected_ref, new_stamp): - if self._ref == expected_ref: - self._stamp = new_stamp - return True - return False - - def set(self, new_ref, new_stamp): - self._ref = new_ref - self._stamp = new_stamp - - -class DoubleAccumulator(Stub): - def __init__(self, accumulator_function, identity): - super().__init__() - self._value = identity - self._func = accumulator_function - - def accumulate(self, x): - self._value = self._func(self._value, x) - - def get(self): - return self._value - - def reset(self): - self._value = 0.0 - - -class DoubleAdder(Stub): - def __init__(self): - super().__init__() - self._value = 0.0 - - def add(self, x): - self._value += x - - def sum(self): - return self._value - - def reset(self): - self._value = 0.0 - - def doubleValue(self): - return self._value - - def toString(self): - return str(self._value) - - -class LongAccumulator(Stub): - def __init__(self, accumulator_function, identity): - super().__init__() - self._value = identity - self._func = accumulator_function - - def accumulate(self, x): - self._value = self._func(self._value, x) - - def get(self): - return self._value - - def reset(self): - self._value = 0 - - -class LongAdder(Stub): - def __init__(self): - super().__init__() - self._value = 0 - - def add(self, x): - self._value += x - - def sum(self): - return self._value - - def reset(self): - self._value = 0 - - def intValue(self): - return self._value - - def longValue(self): - return self._value - - def toString(self): - return str(self._value) - - -# ============================================================ -# ReentrantLock -# ============================================================ - -class Lock(Stub): - def lock(self): - raise NotImplementedError - - def lockInterruptibly(self): - raise NotImplementedError - - def tryLock(self, timeout=None, unit=None): - raise NotImplementedError - - def unlock(self): - raise NotImplementedError - - def newCondition(self): - raise NotImplementedError - - -class ReentrantLock(Lock): - def __init__(self, fair=False): - super().__init__() - self._lock = _threading.RLock() if not fair else _threading.RLock() - - def lock(self): - self._lock.acquire() - - def lockInterruptibly(self): - self._lock.acquire() - - def tryLock(self, timeout=None, unit=None): - if timeout is not None and unit is not None: - seconds = unit.toMillis(timeout) / 1000.0 - return self._lock.acquire(timeout=seconds) - return self._lock.acquire(blocking=False) - - def unlock(self): - self._lock.release() - - def newCondition(self): - return Condition() - - def getHoldCount(self): - return 1 if self._lock._is_owned() else 0 - - def isHeldByCurrentThread(self): - return self._lock._is_owned() - - def isLocked(self): - return True - - def getQueueLength(self): - return 0 - - def hasQueuedThreads(self): - return False - - def getOwner(self): - return _threading.current_thread() if self.isHeldByCurrentThread() else None - - -# ============================================================ -# ReadWriteLock -# ============================================================ - -class ReadWriteLock(Stub): - def readLock(self): - raise NotImplementedError - - def writeLock(self): - raise NotImplementedError - - -class ReentrantReadWriteLock(ReadWriteLock): - class ReadLock(Lock): - def __init__(self, rw_lock): - super().__init__() - self._rw_lock = rw_lock - - def lock(self): - self._rw_lock._read_lock.acquire() - - def unlock(self): - self._rw_lock._read_lock.release() - - def tryLock(self, timeout=None, unit=None): - return self._rw_lock._read_lock.acquire(blocking=False) - - class WriteLock(Lock): - def __init__(self, rw_lock): - super().__init__() - self._rw_lock = rw_lock - - def lock(self): - self._rw_lock._write_lock.acquire() - - def unlock(self): - self._rw_lock._write_lock.release() - - def tryLock(self, timeout=None, unit=None): - return self._rw_lock._write_lock.acquire(blocking=False) - - def __init__(self, fair=False): - super().__init__() - self._read_lock_obj = _threading.RLock() - self._write_lock_obj = _threading.Lock() - self._read_lock = ReentrantReadWriteLock.ReadLock(self) - self._write_lock = ReentrantReadWriteLock.WriteLock(self) - - def readLock(self): - return self._read_lock - - def writeLock(self): - return self._write_lock - - -class Condition(Stub): - def __init__(self): - super().__init__() - self._cond = _threading.Condition() - - def await_(self, timeout=None, unit=None): - if timeout is not None and unit is not None: - seconds = unit.toMillis(timeout) / 1000.0 - with self._cond: - return self._cond.wait(timeout=seconds) - with self._cond: - self._cond.wait() - - def awaitUninterruptibly(self): - with self._cond: - self._cond.wait() - - def awaitNanos(self, nanos_timeout): - seconds = nanos_timeout / 1_000_000_000.0 - with self._cond: - self._cond.wait(timeout=seconds) - return 0 - - def awaitUntil(self, deadline): - remaining = (deadline - _time.time()) * 1000 - if remaining > 0: - self.await_(remaining, TimeUnit.MILLISECONDS) - - def signal(self): - with self._cond: - self._cond.notify() - - def signalAll(self): - with self._cond: - self._cond.notify_all() - - -class LockSupport(Stub): - @staticmethod - def park(): - _time.sleep(1) - - @staticmethod - def parkNanos(nanos): - _time.sleep(nanos / 1_000_000_000.0) - - @staticmethod - def parkUntil(deadline): - remaining = deadline - _time.time() - if remaining > 0: - _time.sleep(remaining) - - @staticmethod - def unpark(thread): - pass - - -# ============================================================ -# AbstractQueuedSynchronizer (stubs) -# ============================================================ - -class AbstractQueuedSynchronizer(Stub): - def __init__(self): - super().__init__() - self._state = 0 - - def getState(self): - return self._state - - def setState(self, state): - self._state = state - - def compareAndSetState(self, expect, update): - if self._state == expect: - self._state = update - return True - return False - - def acquire(self, arg): - pass - - def acquireInterruptibly(self, arg): - pass - - def tryAcquire(self, arg): - return True - - def tryRelease(self, arg): - return True - - def release(self, arg): - return True - - def acquireShared(self, arg): - pass - - def releaseShared(self, arg): - return True - - def tryAcquireShared(self, arg): - return 0 - - def tryReleaseShared(self, arg): - return True - - def hasQueuedThreads(self): - return False - - def getQueueLength(self): - return 0 - - -class AbstractQueuedLongSynchronizer(AbstractQueuedSynchronizer): - pass - - -# ============================================================ -# Flow / SubmissionPublisher (stubs) -# ============================================================ - -class Flow(Stub): - class Publisher(Stub): - def subscribe(self, subscriber): - pass - - class Subscriber(Stub): - def onSubscribe(self, subscription): - pass - - def onNext(self, item): - pass - - def onError(self, throwable): - pass - - def onComplete(self): - pass - - class Subscription(Stub): - def request(self, n): - pass - - def cancel(self): - pass - - class Processor(Stub): - pass - - -class SubmissionPublisher(Flow.Publisher): - def __init__(self): - super().__init__() - self._subscribers = [] - - def subscribe(self, subscriber): - self._subscribers.append(subscriber) - - def submit(self, item): - for sub in self._subscribers: - sub.onNext(item) - return 1 - - def close(self): - for sub in self._subscribers: - sub.onComplete() - - -# ============================================================ -# ForkJoinPool / ForkJoinTask (stubs) -# ============================================================ - -class ForkJoinPool(Stub): - def __init__(self, parallelism=None): - super().__init__() - self._parallelism = parallelism or _py_math.ceil(_threading._count / 2) - self._pool = _futures.ThreadPoolExecutor(max_workers=self._parallelism) - - def execute(self, task): - self._pool.submit(task.run) - - def submit(self, task): - future = FutureTask(task) - self._pool.submit(future.run) - return future - - def invoke(self, task): - return task.run() - - def shutdown(self): - self._pool.shutdown(wait=False) - - -class ForkJoinTask(Future): - def fork(self): - return self - - def join(self): - return None - - def invoke(self): - return None - - def adapt(self, callable_or_runnable): - return ForkJoinTask() - - -class RecursiveAction(ForkJoinTask): - def compute(self): - raise NotImplementedError - - def run(self): - self.compute() - - -class RecursiveTask(ForkJoinTask): - def compute(self): - raise NotImplementedError - - def run(self): - self.compute() - - -class ForkJoinWorkerThread(Stub): - def __init__(self, pool): - super().__init__() - self._pool = pool - - def getPool(self): - return self._pool \ No newline at end of file diff --git a/android_pyapi/java/util_concurrent_atomic.py b/android_pyapi/java/util_concurrent_atomic.py deleted file mode 100644 index aca93c9..0000000 --- a/android_pyapi/java/util_concurrent_atomic.py +++ /dev/null @@ -1,1044 +0,0 @@ -""" -Java java.util.concurrent.atomic standard library APIs implemented in Python. -java.util.concurrent.atomic package - AtomicBoolean, AtomicInteger, AtomicLong, etc. -""" - -from __future__ import annotations - -from .._core import Stub -from ..java.lang import RuntimeException -import threading as _threading -import math as _py_math - -__all__ = [ - "AtomicBoolean", "AtomicInteger", "AtomicLong", "AtomicReference", - "AtomicIntegerArray", "AtomicLongArray", "AtomicReferenceArray", - "AtomicIntegerFieldUpdater", "AtomicLongFieldUpdater", - "AtomicReferenceFieldUpdater", - "AtomicMarkableReference", "AtomicStampedReference", - "DoubleAccumulator", "DoubleAdder", - "LongAccumulator", "LongAdder", "Striped64", -] - - -# ============================================================ -# AtomicBoolean -# ============================================================ - -class AtomicBoolean(Stub): - """java.util.concurrent.atomic.AtomicBoolean""" - def __init__(self, initial_value=False): - super().__init__() - self._lock = _threading.Lock() - self._value = bool(initial_value) - - def get(self): - with self._lock: - return self._value - - def set(self, new_value): - with self._lock: - self._value = bool(new_value) - - def lazySet(self, new_value): - self.set(new_value) - - def getAndSet(self, new_value): - with self._lock: - old = self._value - self._value = bool(new_value) - return old - - def compareAndSet(self, expect, update): - with self._lock: - if self._value == expect: - self._value = update - return True - return False - - def weakCompareAndSet(self, expect, update): - return self.compareAndSet(expect, update) - - def toString(self): - return str(self._value) - - def hashCode(self): - return hash(self._value) - - def equals(self, other): - if not isinstance(other, AtomicBoolean): - return False - return self._value == other._value - - -# ============================================================ -# AtomicInteger -# ============================================================ - -class AtomicInteger(Stub): - """java.util.concurrent.atomic.AtomicInteger""" - def __init__(self, initial_value=0): - super().__init__() - self._lock = _threading.Lock() - self._value = int(initial_value) - - def get(self): - with self._lock: - return self._value - - def set(self, new_value): - with self._lock: - self._value = int(new_value) - - def lazySet(self, new_value): - self.set(new_value) - - def getAndSet(self, new_value): - with self._lock: - old = self._value - self._value = int(new_value) - return old - - def compareAndSet(self, expect, update): - with self._lock: - if self._value == expect: - self._value = int(update) - return True - return False - - def weakCompareAndSet(self, expect, update): - return self.compareAndSet(expect, update) - - def getAndIncrement(self): - with self._lock: - old = self._value - self._value += 1 - return old - - def getAndDecrement(self): - with self._lock: - old = self._value - self._value -= 1 - return old - - def getAndAdd(self, delta): - with self._lock: - old = self._value - self._value += delta - return old - - def incrementAndGet(self): - with self._lock: - self._value += 1 - return self._value - - def decrementAndGet(self): - with self._lock: - self._value -= 1 - return self._value - - def addAndGet(self, delta): - with self._lock: - self._value += delta - return self._value - - def getAndUpdate(self, update_func): - with self._lock: - old = self._value - self._value = update_func.applyAsInt(old) - return old - - def updateAndGet(self, update_func): - with self._lock: - self._value = update_func.applyAsInt(self._value) - return self._value - - def getAndAccumulate(self, x, accumulator_func): - with self._lock: - old = self._value - self._value = accumulator_func.applyAsInt(old, x) - return old - - def accumulateAndGet(self, x, accumulator_func): - with self._lock: - self._value = accumulator_func.applyAsInt(self._value, x) - return self._value - - def toString(self): - return str(self.get()) - - def intValue(self): - return self.get() - - def longValue(self): - return self.get() - - def floatValue(self): - return float(self.get()) - - def doubleValue(self): - return float(self.get()) - - def hashCode(self): - return self.get() - - -# ============================================================ -# AtomicLong -# ============================================================ - -class AtomicLong(Stub): - """java.util.concurrent.atomic.AtomicLong""" - def __init__(self, initial_value=0): - super().__init__() - self._lock = _threading.Lock() - self._value = int(initial_value) - - def get(self): - with self._lock: - return self._value - - def set(self, new_value): - with self._lock: - self._value = int(new_value) - - def lazySet(self, new_value): - self.set(new_value) - - def getAndSet(self, new_value): - with self._lock: - old = self._value - self._value = int(new_value) - return old - - def compareAndSet(self, expect, update): - with self._lock: - if self._value == expect: - self._value = int(update) - return True - return False - - def weakCompareAndSet(self, expect, update): - return self.compareAndSet(expect, update) - - def getAndIncrement(self): - with self._lock: - old = self._value - self._value += 1 - return old - - def getAndDecrement(self): - with self._lock: - old = self._value - self._value -= 1 - return old - - def getAndAdd(self, delta): - with self._lock: - old = self._value - self._value += delta - return old - - def incrementAndGet(self): - with self._lock: - self._value += 1 - return self._value - - def decrementAndGet(self): - with self._lock: - self._value -= 1 - return self._value - - def addAndGet(self, delta): - with self._lock: - self._value += delta - return self._value - - def getAndUpdate(self, update_func): - with self._lock: - old = self._value - self._value = update_func.applyAsLong(old) - return old - - def updateAndGet(self, update_func): - with self._lock: - self._value = update_func.applyAsLong(self._value) - return self._value - - def getAndAccumulate(self, x, accumulator_func): - with self._lock: - old = self._value - self._value = accumulator_func.applyAsLong(old, x) - return old - - def accumulateAndGet(self, x, accumulator_func): - with self._lock: - self._value = accumulator_func.applyAsLong(self._value, x) - return self._value - - def toString(self): - return str(self.get()) - - def intValue(self): - return int(self.get()) - - def longValue(self): - return self.get() - - def floatValue(self): - return float(self.get()) - - def doubleValue(self): - return float(self.get()) - - def hashCode(self): - return hash(self.get()) - - -# ============================================================ -# AtomicReference -# ============================================================ - -class AtomicReference(Stub): - """java.util.concurrent.atomic.AtomicReference""" - def __init__(self, initial_value=None): - super().__init__() - self._lock = _threading.Lock() - self._value = initial_value - - def get(self): - with self._lock: - return self._value - - def set(self, new_value): - with self._lock: - self._value = new_value - - def lazySet(self, new_value): - self.set(new_value) - - def getAndSet(self, new_value): - with self._lock: - old = self._value - self._value = new_value - return old - - def compareAndSet(self, expect, update): - with self._lock: - if self._value is expect or (self._value == expect): - self._value = update - return True - return False - - def weakCompareAndSet(self, expect, update): - return self.compareAndSet(expect, update) - - def getAndUpdate(self, update_func): - with self._lock: - old = self._value - self._value = update_func.apply(old) - return old - - def updateAndGet(self, update_func): - with self._lock: - self._value = update_func.apply(self._value) - return self._value - - def getAndAccumulate(self, x, accumulator_func): - with self._lock: - old = self._value - self._value = accumulator_func.apply(old, x) - return old - - def accumulateAndGet(self, x, accumulator_func): - with self._lock: - self._value = accumulator_func.apply(self._value, x) - return self._value - - def toString(self): - return str(self.get()) - - def hashCode(self): - return hash(self._value) - - -# ============================================================ -# AtomicIntegerArray -# ============================================================ - -class AtomicIntegerArray(Stub): - """java.util.concurrent.atomic.AtomicIntegerArray""" - def __init__(self, length_or_array): - super().__init__() - if isinstance(length_or_array, int): - self._array = [0] * length_or_array - else: - self._array = list(length_or_array) - self._lock = _threading.Lock() - - def length(self): - return len(self._array) - - def get(self, i): - with self._lock: - return self._array[i] - - def set(self, i, new_value): - with self._lock: - self._array[i] = int(new_value) - - def lazySet(self, i, new_value): - self.set(i, new_value) - - def getAndSet(self, i, new_value): - with self._lock: - old = self._array[i] - self._array[i] = int(new_value) - return old - - def compareAndSet(self, i, expect, update): - with self._lock: - if self._array[i] == expect: - self._array[i] = int(update) - return True - return False - - def weakCompareAndSet(self, i, expect, update): - return self.compareAndSet(i, expect, update) - - def getAndIncrement(self, i): - with self._lock: - old = self._array[i] - self._array[i] += 1 - return old - - def getAndDecrement(self, i): - with self._lock: - old = self._array[i] - self._array[i] -= 1 - return old - - def getAndAdd(self, i, delta): - with self._lock: - old = self._array[i] - self._array[i] += delta - return old - - def incrementAndGet(self, i): - with self._lock: - self._array[i] += 1 - return self._array[i] - - def decrementAndGet(self, i): - with self._lock: - self._array[i] -= 1 - return self._array[i] - - def addAndGet(self, i, delta): - with self._lock: - self._array[i] += delta - return self._array[i] - - def toString(self): - return str(self._array) - - -# ============================================================ -# AtomicLongArray -# ============================================================ - -class AtomicLongArray(Stub): - """java.util.concurrent.atomic.AtomicLongArray""" - def __init__(self, length_or_array): - super().__init__() - if isinstance(length_or_array, int): - self._array = [0] * length_or_array - else: - self._array = list(length_or_array) - self._lock = _threading.Lock() - - def length(self): - return len(self._array) - - def get(self, i): - with self._lock: - return self._array[i] - - def set(self, i, new_value): - with self._lock: - self._array[i] = int(new_value) - - def lazySet(self, i, new_value): - self.set(i, new_value) - - def getAndSet(self, i, new_value): - with self._lock: - old = self._array[i] - self._array[i] = int(new_value) - return old - - def compareAndSet(self, i, expect, update): - with self._lock: - if self._array[i] == expect: - self._array[i] = int(update) - return True - return False - - def weakCompareAndSet(self, i, expect, update): - return self.compareAndSet(i, expect, update) - - def getAndIncrement(self, i): - with self._lock: - old = self._array[i] - self._array[i] += 1 - return old - - def getAndDecrement(self, i): - with self._lock: - old = self._array[i] - self._array[i] -= 1 - return old - - def getAndAdd(self, i, delta): - with self._lock: - old = self._array[i] - self._array[i] += delta - return old - - def incrementAndGet(self, i): - with self._lock: - self._array[i] += 1 - return self._array[i] - - def decrementAndGet(self, i): - with self._lock: - self._array[i] -= 1 - return self._array[i] - - def addAndGet(self, i, delta): - with self._lock: - self._array[i] += delta - return self._array[i] - - def toString(self): - return str(self._array) - - -# ============================================================ -# AtomicReferenceArray -# ============================================================ - -class AtomicReferenceArray(Stub): - """java.util.concurrent.atomic.AtomicReferenceArray""" - def __init__(self, length_or_array): - super().__init__() - if isinstance(length_or_array, int): - self._array = [None] * length_or_array - else: - self._array = list(length_or_array) - self._lock = _threading.Lock() - - def length(self): - return len(self._array) - - def get(self, i): - with self._lock: - return self._array[i] - - def set(self, i, new_value): - with self._lock: - self._array[i] = new_value - - def lazySet(self, i, new_value): - self.set(i, new_value) - - def getAndSet(self, i, new_value): - with self._lock: - old = self._array[i] - self._array[i] = new_value - return old - - def compareAndSet(self, i, expect, update): - with self._lock: - if self._array[i] is expect or self._array[i] == expect: - self._array[i] = update - return True - return False - - def weakCompareAndSet(self, i, expect, update): - return self.compareAndSet(i, expect, update) - - def toString(self): - return str(self._array) - - -# ============================================================ -# AtomicMarkableReference -# ============================================================ - -class AtomicMarkableReference(Stub): - """java.util.concurrent.atomic.AtomicMarkableReference""" - def __init__(self, initial_ref, initial_mark): - super().__init__() - self._lock = _threading.Lock() - self._reference = initial_ref - self._mark = bool(initial_mark) - - def getReference(self): - with self._lock: - return self._reference - - def isMarked(self): - with self._lock: - return self._mark - - def get(self): - with self._lock: - return [self._reference, self._mark] - - def attemptMark(self, expected_reference, new_mark): - with self._lock: - if self._reference is expected_reference or self._reference == expected_reference: - self._mark = bool(new_mark) - return True - return False - - def compareAndSet(self, expected_reference, new_reference, - expected_mark, new_mark): - with self._lock: - if (self._reference is expected_reference or self._reference == expected_reference): - if self._mark == expected_mark: - self._reference = new_reference - self._mark = bool(new_mark) - return True - return False - - def weakCompareAndSet(self, expected_reference, new_reference, - expected_mark, new_mark): - return self.compareAndSet(expected_reference, new_reference, - expected_mark, new_mark) - - -# ============================================================ -# AtomicStampedReference -# ============================================================ - -class AtomicStampedReference(Stub): - """java.util.concurrent.atomic.AtomicStampedReference""" - def __init__(self, initial_ref, initial_stamp): - super().__init__() - self._lock = _threading.Lock() - self._reference = initial_ref - self._stamp = int(initial_stamp) - - def getReference(self): - with self._lock: - return self._reference - - def getStamp(self): - with self._lock: - return self._stamp - - def get(self): - with self._lock: - return [self._reference, self._stamp] - - def attemptStamp(self, expected_reference, new_stamp): - with self._lock: - if self._reference is expected_reference or self._reference == expected_reference: - self._stamp = int(new_stamp) - return True - return False - - def compareAndSet(self, expected_reference, new_reference, - expected_stamp, new_stamp): - with self._lock: - if (self._reference is expected_reference or self._reference == expected_reference): - if self._stamp == expected_stamp: - self._reference = new_reference - self._stamp = int(new_stamp) - return True - return False - - def weakCompareAndSet(self, expected_reference, new_reference, - expected_stamp, new_stamp): - return self.compareAndSet(expected_reference, new_reference, - expected_stamp, new_stamp) - - -# ============================================================ -# Field Updaters -# ============================================================ - -class AtomicIntegerFieldUpdater(Stub): - """java.util.concurrent.atomic.AtomicIntegerFieldUpdater""" - def __init__(self, tclass, field_name): - super().__init__() - self._tclass = tclass - self._field_name = field_name - - @staticmethod - def newUpdater(tclass, field_name): - return AtomicIntegerFieldUpdater(tclass, field_name) - - def get(self, obj): - return getattr(obj, self._field_name, 0) - - def set(self, obj, new_value): - setattr(obj, self._field_name, int(new_value)) - - def lazySet(self, obj, new_value): - self.set(obj, new_value) - - def getAndSet(self, obj, new_value): - old = self.get(obj) - self.set(obj, new_value) - return old - - def compareAndSet(self, obj, expect, update): - if self.get(obj) == expect: - self.set(obj, update) - return True - return False - - def weakCompareAndSet(self, obj, expect, update): - return self.compareAndSet(obj, expect, update) - - def getAndIncrement(self, obj): - old = self.get(obj) - self.set(obj, old + 1) - return old - - def getAndDecrement(self, obj): - old = self.get(obj) - self.set(obj, old - 1) - return old - - def getAndAdd(self, obj, delta): - old = self.get(obj) - self.set(obj, old + delta) - return old - - def incrementAndGet(self, obj): - val = self.get(obj) + 1 - self.set(obj, val) - return val - - def decrementAndGet(self, obj): - val = self.get(obj) - 1 - self.set(obj, val) - return val - - def addAndGet(self, obj, delta): - val = self.get(obj) + delta - self.set(obj, val) - return val - - -class AtomicLongFieldUpdater(Stub): - """java.util.concurrent.atomic.AtomicLongFieldUpdater""" - def __init__(self, tclass, field_name): - super().__init__() - self._tclass = tclass - self._field_name = field_name - - @staticmethod - def newUpdater(tclass, field_name): - return AtomicLongFieldUpdater(tclass, field_name) - - def get(self, obj): - return getattr(obj, self._field_name, 0) - - def set(self, obj, new_value): - setattr(obj, self._field_name, int(new_value)) - - def lazySet(self, obj, new_value): - self.set(obj, new_value) - - def getAndSet(self, obj, new_value): - old = self.get(obj) - self.set(obj, new_value) - return old - - def compareAndSet(self, obj, expect, update): - if self.get(obj) == expect: - self.set(obj, update) - return True - return False - - def weakCompareAndSet(self, obj, expect, update): - return self.compareAndSet(obj, expect, update) - - def getAndIncrement(self, obj): - old = self.get(obj) - self.set(obj, old + 1) - return old - - def getAndDecrement(self, obj): - old = self.get(obj) - self.set(obj, old - 1) - return old - - def getAndAdd(self, obj, delta): - old = self.get(obj) - self.set(obj, old + delta) - return old - - def incrementAndGet(self, obj): - val = self.get(obj) + 1 - self.set(obj, val) - return val - - def decrementAndGet(self, obj): - val = self.get(obj) - 1 - self.set(obj, val) - return val - - def addAndGet(self, obj, delta): - val = self.get(obj) + delta - self.set(obj, val) - return val - - -class AtomicReferenceFieldUpdater(Stub): - """java.util.concurrent.atomic.AtomicReferenceFieldUpdater""" - def __init__(self, tclass, vclass, field_name): - super().__init__() - self._tclass = tclass - self._vclass = vclass - self._field_name = field_name - - @staticmethod - def newUpdater(tclass, vclass, field_name): - return AtomicReferenceFieldUpdater(tclass, vclass, field_name) - - def get(self, obj): - return getattr(obj, self._field_name, None) - - def set(self, obj, new_value): - setattr(obj, self._field_name, new_value) - - def lazySet(self, obj, new_value): - self.set(obj, new_value) - - def getAndSet(self, obj, new_value): - old = self.get(obj) - self.set(obj, new_value) - return old - - def compareAndSet(self, obj, expect, update): - if self.get(obj) is expect or self.get(obj) == expect: - self.set(obj, update) - return True - return False - - def weakCompareAndSet(self, obj, expect, update): - return self.compareAndSet(obj, expect, update) - - -# ============================================================ -# DoubleAccumulator -# ============================================================ - -class DoubleAccumulator(Stub): - """java.util.concurrent.atomic.DoubleAccumulator""" - def __init__(self, accumulator_func, identity): - super().__init__() - self._lock = _threading.Lock() - self._value = float(identity) - self._func = accumulator_func - - def accumulate(self, x): - with self._lock: - self._value = self._func.applyAsDouble(self._value, float(x)) - - def get(self): - with self._lock: - return self._value - - def reset(self): - with self._lock: - self._value = 0.0 - - def getThenReset(self): - with self._lock: - old = self._value - self._value = 0.0 - return old - - def toString(self): - return str(self.get()) - - def doubleValue(self): - return self.get() - - def floatValue(self): - return float(self.get()) - - def intValue(self): - return int(self.get()) - - def longValue(self): - return int(self.get()) - - -# ============================================================ -# DoubleAdder -# ============================================================ - -class DoubleAdder(Stub): - """java.util.concurrent.atomic.DoubleAdder""" - def __init__(self): - super().__init__() - self._lock = _threading.Lock() - self._value = 0.0 - - def add(self, x): - with self._lock: - self._value += float(x) - - def sum(self): - with self._lock: - return self._value - - def reset(self): - with self._lock: - self._value = 0.0 - - def sumThenReset(self): - with self._lock: - old = self._value - self._value = 0.0 - return old - - def toString(self): - return str(self.sum()) - - def doubleValue(self): - return self.sum() - - def floatValue(self): - return float(self.sum()) - - def intValue(self): - return int(self.sum()) - - def longValue(self): - return int(self.sum()) - - -# ============================================================ -# LongAccumulator -# ============================================================ - -class LongAccumulator(Stub): - """java.util.concurrent.atomic.LongAccumulator""" - def __init__(self, accumulator_func, identity): - super().__init__() - self._lock = _threading.Lock() - self._value = int(identity) - self._func = accumulator_func - - def accumulate(self, x): - with self._lock: - self._value = self._func.applyAsLong(self._value, int(x)) - - def get(self): - with self._lock: - return self._value - - def reset(self): - with self._lock: - self._value = 0 - - def getThenReset(self): - with self._lock: - old = self._value - self._value = 0 - return old - - def toString(self): - return str(self.get()) - - def intValue(self): - return int(self.get()) - - def longValue(self): - return self.get() - - def floatValue(self): - return float(self.get()) - - def doubleValue(self): - return float(self.get()) - - -# ============================================================ -# LongAdder -# ============================================================ - -class LongAdder(Stub): - """java.util.concurrent.atomic.LongAdder""" - def __init__(self): - super().__init__() - self._lock = _threading.Lock() - self._value = 0 - - def add(self, x): - with self._lock: - self._value += int(x) - - def increment(self): - self.add(1) - - def decrement(self): - self.add(-1) - - def sum(self): - with self._lock: - return self._value - - def reset(self): - with self._lock: - self._value = 0 - - def sumThenReset(self): - with self._lock: - old = self._value - self._value = 0 - return old - - def toString(self): - return str(self.sum()) - - def intValue(self): - return int(self.sum()) - - def longValue(self): - return self.sum() - - def floatValue(self): - return float(self.sum()) - - def doubleValue(self): - return float(self.sum()) - - -# ============================================================ -# Striped64 -# ============================================================ - -class Striped64(Stub): - """java.util.concurrent.atomic.Striped64""" - def __init__(self): - super().__init__() - self._value = 0 - self._lock = _threading.Lock() - - def _get_value(self): - with self._lock: - return self._value - - def _set_value(self, v): - with self._lock: - self._value = v \ No newline at end of file diff --git a/android_pyapi/java/util_concurrent_locks.py b/android_pyapi/java/util_concurrent_locks.py deleted file mode 100644 index 871b16c..0000000 --- a/android_pyapi/java/util_concurrent_locks.py +++ /dev/null @@ -1,681 +0,0 @@ -""" -Java java.util.concurrent.locks standard library APIs implemented in Python. -java.util.concurrent.locks package - Lock, ReadWriteLock, ReentrantLock, etc. -""" - -from __future__ import annotations - -from .._core import Stub -from ..java.lang import RuntimeException - -# InterruptedException (桌面模拟) -class InterruptedException(RuntimeException): - def __init__(self, message=""): - super().__init__(message) -import threading as _threading -import time as _time -import collections as _collections - -__all__ = [ - "Lock", "ReadWriteLock", "ReentrantLock", - "ReentrantReadWriteLock", - "ReentrantReadWriteLock_ReadLock", - "ReentrantReadWriteLock_WriteLock", - "Condition", "LockSupport", - "AbstractQueuedSynchronizer", - "AbstractQueuedLongSynchronizer", - "AbstractOwnableSynchronizer", -] - - -# ============================================================ -# Lock interface -# ============================================================ - -class Lock(Stub): - """java.util.concurrent.locks.Lock""" - def lock(self): - raise NotImplementedError - - def lockInterruptibly(self): - raise NotImplementedError - - def tryLock(self, time_val=None, unit=None): - raise NotImplementedError - - def unlock(self): - raise NotImplementedError - - def newCondition(self): - raise NotImplementedError - - -class ReadWriteLock(Stub): - """java.util.concurrent.locks.ReadWriteLock""" - def readLock(self): - raise NotImplementedError - - def writeLock(self): - raise NotImplementedError - - -# ============================================================ -# ReentrantLock -# ============================================================ - -class ReentrantLock(Lock): - """java.util.concurrent.locks.ReentrantLock""" - def __init__(self, fair=False): - super().__init__() - self._lock = _threading.RLock() - self._fair = fair - self._owner = None - self._hold_count = 0 - - def lock(self): - self._lock.acquire() - self._owner = _threading.current_thread() - self._hold_count += 1 - - def lockInterruptibly(self): - self.lock() - - def tryLock(self, timeout=None, unit=None): - if timeout is None: - acquired = self._lock.acquire(blocking=False) - else: - if unit is not None: - timeout = timeout - acquired = self._lock.acquire(blocking=True, timeout=timeout) - if acquired: - self._owner = _threading.current_thread() - self._hold_count += 1 - return True - return False - - def unlock(self): - self._hold_count -= 1 - if self._hold_count == 0: - self._owner = None - self._lock.release() - - def newCondition(self): - return Condition(self) - - def getHoldCount(self): - return self._hold_count - - def isHeldByCurrentThread(self): - return self._owner is _threading.current_thread() - - def isLocked(self): - return self._owner is not None - - def isFair(self): - return self._fair - - def getOwner(self): - return self._owner - - def getQueuedThreads(self): - return [] - - def getQueueLength(self): - return 0 - - def hasQueuedThreads(self): - return False - - def hasQueuedThread(self, thread): - return False - - def toString(self): - status = "Locked" if self.isLocked() else "Unlocked" - return f"ReentrantLock[{status}, hold={self._hold_count}]" - - -# ============================================================ -# ReentrantReadWriteLock -# ============================================================ - -class ReentrantReadWriteLock(ReadWriteLock): - """java.util.concurrent.locks.ReentrantReadWriteLock""" - def __init__(self, fair=False): - super().__init__() - self._fair = fair - self._read_lock = ReentrantReadWriteLock_ReadLock(self) - self._write_lock = ReentrantReadWriteLock_WriteLock(self) - self._read_hold_count = 0 - self._write_hold_count = 0 - self._write_owner = None - self._lock = _threading.RLock() - self._read_lock_obj = _threading.Lock() - self._read_cond = _threading.Condition(self._read_lock_obj) - self._readers = 0 - self._writers_waiting = 0 - - def readLock(self): - return self._read_lock - - def writeLock(self): - return self._write_lock - - def isFair(self): - return self._fair - - def getReadLockCount(self): - return self._readers - - def isWriteLocked(self): - return self._write_owner is not None - - def isWriteLockedByCurrentThread(self): - return self._write_owner is _threading.current_thread() - - def getWriteHoldCount(self): - return self._write_hold_count - - def getReadHoldCount(self): - return self._read_hold_count - - def getOwner(self): - return self._write_owner - - def getQueuedWriterThreads(self): - return [] - - def getQueuedReaderThreads(self): - return [] - - def getQueueLength(self): - return 0 - - def hasQueuedThreads(self): - return False - - def hasQueuedThread(self, thread): - return False - - def toString(self): - w = self._write_hold_count - r = self._readers - return f"ReentrantReadWriteLock[read_locks={r}, write_locks={w}]" - - def _acquire_read(self): - with self._lock: - while self._write_owner is not None and self._write_owner != _threading.current_thread(): - self._read_cond.wait() - self._readers += 1 - self._read_hold_count += 1 - - def _release_read(self): - with self._lock: - self._readers -= 1 - self._read_hold_count -= 1 - if self._readers == 0: - self._read_cond.notify_all() - - def _acquire_write(self): - self._lock.acquire() - self._write_owner = _threading.current_thread() - self._write_hold_count += 1 - - def _release_write(self): - self._write_hold_count -= 1 - if self._write_hold_count == 0: - self._write_owner = None - self._read_cond.notify_all() - self._lock.release() - - -class ReentrantReadWriteLock_ReadLock(Lock): - """java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock""" - def __init__(self, rw_lock): - super().__init__() - self._rw_lock = rw_lock - - def lock(self): - self._rw_lock._acquire_read() - - def lockInterruptibly(self): - self.lock() - - def tryLock(self, timeout=None, unit=None): - self.lock() - return True - - def unlock(self): - self._rw_lock._release_read() - - def newCondition(self): - raise UnsupportedOperationException("Read locks do not support Condition") - - -class ReentrantReadWriteLock_WriteLock(Lock): - """java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock""" - def __init__(self, rw_lock): - super().__init__() - self._rw_lock = rw_lock - - def lock(self): - self._rw_lock._acquire_write() - - def lockInterruptibly(self): - self.lock() - - def tryLock(self, timeout=None, unit=None): - if timeout is None: - acquired = self._rw_lock._lock.acquire(blocking=False) - else: - acquired = self._rw_lock._lock.acquire(blocking=True, timeout=timeout) - if acquired: - self._rw_lock._write_owner = _threading.current_thread() - self._rw_lock._write_hold_count += 1 - return True - return False - - def unlock(self): - self._rw_lock._release_write() - - def newCondition(self): - return Condition(self) - - def isHeldByCurrentThread(self): - return self._rw_lock._write_owner is _threading.current_thread() - - -# ============================================================ -# Condition -# ============================================================ - -class Condition(Stub): - """java.util.concurrent.locks.Condition""" - def __init__(self, lock=None): - super().__init__() - self._lock = lock - self._cond = _threading.Condition(_threading.RLock()) if lock is None else _threading.Condition() - - def await_signal(self, time_val=None, unit=None): - self._cond.acquire() - try: - if time_val is None: - self._cond.wait() - return True - else: - if unit is not None: - secs = time_val / 1000.0 # milliseconds - else: - secs = float(time_val) - self._cond.wait(timeout=secs) - return True - finally: - self._cond.release() - - def awaitUninterruptibly(self): - self.await_signal() - - def awaitNanos(self, nanos_timeout): - self.await_signal(nanos_timeout / 1000000.0) - return 0 - - def awaitUntil(self, deadline): - now = _time.time() - diff = deadline - now - if diff > 0: - self.await_signal(diff) - return True - - def signal(self): - self._cond.acquire() - try: - self._cond.notify() - finally: - self._cond.release() - - def signalAll(self): - self._cond.acquire() - try: - self._cond.notify_all() - finally: - self._cond.release() - - -# ============================================================ -# LockSupport -# ============================================================ - -class LockSupport(Stub): - """java.util.concurrent.locks.LockSupport""" - _blocked = {} - - @staticmethod - def park(blocker=None): - if blocker is not None: - LockSupport._blocked[_threading.current_thread().ident] = blocker - _time.sleep(0.01) - if blocker is not None: - del LockSupport._blocked[_threading.current_thread().ident] - - @staticmethod - def parkNanos(nanos, blocker=None): - if blocker is not None: - LockSupport._blocked[_threading.current_thread().ident] = blocker - _time.sleep(nanos / 1000000000.0) - if blocker is not None: - del LockSupport._blocked[_threading.current_thread().ident] - - @staticmethod - def parkUntil(deadline, blocker=None): - now = _time.time() - diff = deadline - now - if diff > 0: - if blocker is not None: - LockSupport._blocked[_threading.current_thread().ident] = blocker - _time.sleep(diff) - if blocker is not None: - del LockSupport._blocked[_threading.current_thread().ident] - - @staticmethod - def unpark(thread): - pass - - @staticmethod - def getBlocker(thread): - return LockSupport._blocked.get(thread.ident) - - -# ============================================================ -# AbstractOwnableSynchronizer -# ============================================================ - -class AbstractOwnableSynchronizer(Stub): - """java.util.concurrent.locks.AbstractOwnableSynchronizer""" - def __init__(self): - super().__init__() - self._exclusive_owner_thread = None - - def setExclusiveOwnerThread(self, thread): - self._exclusive_owner_thread = thread - - def getExclusiveOwnerThread(self): - return self._exclusive_owner_thread - - -# ============================================================ -# AbstractQueuedSynchronizer -# ============================================================ - -class AbstractQueuedSynchronizer(AbstractOwnableSynchronizer): - """java.util.concurrent.locks.AbstractQueuedSynchronizer""" - class ConditionObject(Condition): - def __init__(self, sync): - super().__init__() - self._sync = sync - - def __init__(self): - super().__init__() - self._state = 0 - self._lock = _threading.Lock() - self._cond = _threading.Condition(self._lock) - self._head = None - self._tail = None - self._wait_queue = _collections.deque() - - def getState(self): - with self._lock: - return self._state - - def setState(self, new_state): - with self._lock: - self._state = new_state - - def compareAndSetState(self, expect, update): - with self._lock: - if self._state == expect: - self._state = int(update) - return True - return False - - def acquire(self, arg): - if not self.tryAcquire(arg): - self._acquire_queued(self._add_waiter(), arg) - - def acquireInterruptibly(self, arg): - self.acquire(arg) - - def tryAcquireNanos(self, arg, nanos_timeout): - return self.tryAcquire(arg) - - def release(self, arg): - if self.tryRelease(arg): - self._signal_next() - return True - return False - - def acquireShared(self, arg): - if self.tryAcquireShared(arg) < 0: - self._acquire_shared_queued(self._add_waiter(), arg) - - def acquireSharedInterruptibly(self, arg): - self.acquireShared(arg) - - def tryAcquireSharedNanos(self, arg, nanos_timeout): - return self.tryAcquireShared(arg) >= 0 - - def releaseShared(self, arg): - if self.tryReleaseShared(arg): - self._signal_next() - return True - return False - - def tryAcquire(self, arg): - raise NotImplementedError - - def tryRelease(self, arg): - raise NotImplementedError - - def tryAcquireShared(self, arg): - return 1 - - def tryReleaseShared(self, arg): - return True - - def isHeldExclusively(self): - return self.getExclusiveOwnerThread() is _threading.current_thread() - - def getQueuedThreads(self): - return list(self._wait_queue) - - def getQueueLength(self): - return len(self._wait_queue) - - def hasQueuedThreads(self): - return len(self._wait_queue) > 0 - - def hasContended(self): - return len(self._wait_queue) > 0 - - def getFirstQueuedThread(self): - if self._wait_queue: - return self._wait_queue[0] - return None - - def owns(self, condition): - return isinstance(condition, AbstractQueuedSynchronizer.ConditionObject) - - def hasWaiters(self, condition): - return False - - def getWaitQueueLength(self, condition): - return 0 - - def getWaitingThreads(self, condition): - return [] - - def toString(self): - return f"AbstractQueuedSynchronizer[state={self._state}]" - - def _add_waiter(self): - thread = _threading.current_thread() - with self._lock: - self._wait_queue.append(thread) - return thread - - def _acquire_queued(self, node, arg): - while True: - if self.tryAcquire(arg): - with self._lock: - if node in self._wait_queue: - self._wait_queue.remove(node) - return - _time.sleep(0.001) - - def _acquire_shared_queued(self, node, arg): - while True: - if self.tryAcquireShared(arg) >= 0: - with self._lock: - if node in self._wait_queue: - self._wait_queue.remove(node) - return - _time.sleep(0.001) - - def _signal_next(self): - with self._lock: - if self._wait_queue: - self._cond.notify() - - -# ============================================================ -# AbstractQueuedLongSynchronizer -# ============================================================ - -class AbstractQueuedLongSynchronizer(AbstractOwnableSynchronizer): - """java.util.concurrent.locks.AbstractQueuedLongSynchronizer""" - class ConditionObject(Condition): - def __init__(self, sync): - super().__init__() - self._sync = sync - - def __init__(self): - super().__init__() - self._state = 0 - self._lock = _threading.Lock() - self._cond = _threading.Condition(self._lock) - self._wait_queue = _collections.deque() - - def getState(self): - with self._lock: - return self._state - - def setState(self, new_state): - with self._lock: - self._state = int(new_state) - - def compareAndSetState(self, expect, update): - with self._lock: - if self._state == expect: - self._state = int(update) - return True - return False - - def acquire(self, arg): - if not self.tryAcquire(arg): - self._acquire_queued(self._add_waiter(), arg) - - def acquireInterruptibly(self, arg): - self.acquire(arg) - - def tryAcquireNanos(self, arg, nanos_timeout): - return self.tryAcquire(arg) - - def release(self, arg): - if self.tryRelease(arg): - self._signal_next() - return True - return False - - def acquireShared(self, arg): - if self.tryAcquireShared(arg) < 0: - self._acquire_shared_queued(self._add_waiter(), arg) - - def acquireSharedInterruptibly(self, arg): - self.acquireShared(arg) - - def tryAcquireSharedNanos(self, arg, nanos_timeout): - return self.tryAcquireShared(arg) >= 0 - - def releaseShared(self, arg): - if self.tryReleaseShared(arg): - self._signal_next() - return True - return False - - def tryAcquire(self, arg): - raise NotImplementedError - - def tryRelease(self, arg): - raise NotImplementedError - - def tryAcquireShared(self, arg): - return 1 - - def tryReleaseShared(self, arg): - return True - - def isHeldExclusively(self): - return self.getExclusiveOwnerThread() is _threading.current_thread() - - def getQueuedThreads(self): - return list(self._wait_queue) - - def getQueueLength(self): - return len(self._wait_queue) - - def hasQueuedThreads(self): - return len(self._wait_queue) > 0 - - def getFirstQueuedThread(self): - if self._wait_queue: - return self._wait_queue[0] - return None - - def toString(self): - return f"AbstractQueuedLongSynchronizer[state={self._state}]" - - def _add_waiter(self): - thread = _threading.current_thread() - with self._lock: - self._wait_queue.append(thread) - return thread - - def _acquire_queued(self, node, arg): - while True: - if self.tryAcquire(arg): - with self._lock: - if node in self._wait_queue: - self._wait_queue.remove(node) - return - _time.sleep(0.001) - - def _acquire_shared_queued(self, node, arg): - while True: - if self.tryAcquireShared(arg) >= 0: - with self._lock: - if node in self._wait_queue: - self._wait_queue.remove(node) - return - _time.sleep(0.001) - - def _signal_next(self): - with self._lock: - if self._wait_queue: - self._cond.notify() - - -# ============================================================ -# UnsupportedOperationException -# ============================================================ - -class UnsupportedOperationException(RuntimeException): - """java.lang.UnsupportedOperationException""" - pass \ No newline at end of file diff --git a/android_pyapi/java/util_function.py b/android_pyapi/java/util_function.py deleted file mode 100644 index 96becf7..0000000 --- a/android_pyapi/java/util_function.py +++ /dev/null @@ -1,599 +0,0 @@ -""" -Java java.util.function standard library APIs implemented in Python. -java.util.function package - Function, Consumer, Predicate, Supplier, etc. -""" - -from __future__ import annotations - -from .._core import Stub -from .lang import RuntimeException -import typing as _typing - -__all__ = [ - "BiConsumer", "BiFunction", "BiPredicate", "BinaryOperator", - "Consumer", "DoubleBinaryOperator", "DoubleConsumer", - "DoubleFunction", "DoublePredicate", "DoubleSupplier", - "DoubleToIntFunction", "DoubleToLongFunction", - "Function", "IntBinaryOperator", "IntConsumer", - "IntFunction", "IntPredicate", "IntSupplier", - "IntToDoubleFunction", "IntToLongFunction", - "LongBinaryOperator", "LongConsumer", "LongFunction", - "LongPredicate", "LongSupplier", "LongToDoubleFunction", - "LongToIntFunction", - "ObjDoubleConsumer", "ObjIntConsumer", "ObjLongConsumer", - "Predicate", "Supplier", - "ToDoubleBiFunction", "ToDoubleFunction", - "ToIntBiFunction", "ToIntFunction", - "ToLongBiFunction", "ToLongFunction", - "UnaryOperator", -] - -T = _typing.TypeVar("T") -R = _typing.TypeVar("R") - - -# ============================================================ -# Core functional interfaces -# ============================================================ - -class Function(Stub): - """java.util.function.Function""" - def apply(self, t): - raise NotImplementedError - - def compose(self, before): - class Composed(Function): - def apply(self, t): - return self.apply(before.apply(t)) - return Composed() - - def andThen(self, after): - class ComposedAndThen(Function): - def apply(self, t): - return after.apply(self.apply(t)) - return ComposedAndThen() - - @staticmethod - def identity(): - class Identity(Function): - def apply(self, t): - return t - return Identity() - - -class BiFunction(Stub): - """java.util.function.BiFunction""" - def apply(self, t, u): - raise NotImplementedError - - def andThen(self, after): - class Composed(BiFunction): - def apply(self, t, u): - return after.apply(self.apply(t, u)) - return Composed() - - -class UnaryOperator(Function): - """java.util.function.UnaryOperator""" - @staticmethod - def identity(): - class Identity(UnaryOperator): - def apply(self, t): - return t - return Identity() - - -class BinaryOperator(BiFunction): - """java.util.function.BinaryOperator""" - @staticmethod - def min_by(comparator): - class MinBy(BinaryOperator): - def apply(self, a, b): - return a if comparator.compare(a, b) <= 0 else b - return MinBy() - - @staticmethod - def max_by(comparator): - class MaxBy(BinaryOperator): - def apply(self, a, b): - return a if comparator.compare(a, b) >= 0 else b - return MaxBy() - - -class Predicate(Stub): - """java.util.function.Predicate""" - def test(self, t): - raise NotImplementedError - - def and_(self, other): - class And(Predicate): - def test(self, t): - return self.test(t) and other.test(t) - return And() - - def negate(self): - class Negate(Predicate): - def test(self, t): - return not self.test(t) - return Negate() - - def or_(self, other): - class Or(Predicate): - def test(self, t): - return self.test(t) or other.test(t) - return Or() - - @staticmethod - def is_equal(target_ref): - class IsEqual(Predicate): - def test(self, t): - return t == target_ref - return IsEqual() - - @staticmethod - def not_(target): - class Not(Predicate): - def test(self, t): - return not target.test(t) - return Not() - - -class BiPredicate(Stub): - """java.util.function.BiPredicate""" - def test(self, t, u): - raise NotImplementedError - - def and_(self, other): - class And(BiPredicate): - def test(self, t, u): - return self.test(t, u) and other.test(t, u) - return And() - - def negate(self): - class Negate(BiPredicate): - def test(self, t, u): - return not self.test(t, u) - return Negate() - - def or_(self, other): - class Or(BiPredicate): - def test(self, t, u): - return self.test(t, u) or other.test(t, u) - return Or() - - -class Consumer(Stub): - """java.util.function.Consumer""" - def accept(self, t): - raise NotImplementedError - - def andThen(self, after): - class Composed(Consumer): - def accept(self, t): - self.accept(t) - after.accept(t) - return Composed() - - -class BiConsumer(Stub): - """java.util.function.BiConsumer""" - def accept(self, t, u): - raise NotImplementedError - - def andThen(self, after): - class Composed(BiConsumer): - def accept(self, t, u): - self.accept(t, u) - after.accept(t, u) - return Composed() - - -class Supplier(Stub): - """java.util.function.Supplier""" - def get(self): - raise NotImplementedError - - -# ============================================================ -# Primitive specializations -# ============================================================ - -class IntFunction(Stub): - """java.util.function.IntFunction""" - def apply(self, value): - raise NotImplementedError - - -class LongFunction(Stub): - """java.util.function.LongFunction""" - def apply(self, value): - raise NotImplementedError - - -class DoubleFunction(Stub): - """java.util.function.DoubleFunction""" - def apply(self, value): - raise NotImplementedError - - -class IntConsumer(Stub): - """java.util.function.IntConsumer""" - def accept(self, value): - raise NotImplementedError - - def andThen(self, after): - class Composed(IntConsumer): - def accept(self, value): - self.accept(value) - after.accept(value) - return Composed() - - -class LongConsumer(Stub): - """java.util.function.LongConsumer""" - def accept(self, value): - raise NotImplementedError - - def andThen(self, after): - class Composed(LongConsumer): - def accept(self, value): - self.accept(value) - after.accept(value) - return Composed() - - -class DoubleConsumer(Stub): - """java.util.function.DoubleConsumer""" - def accept(self, value): - raise NotImplementedError - - def andThen(self, after): - class Composed(DoubleConsumer): - def accept(self, value): - self.accept(value) - after.accept(value) - return Composed() - - -class IntPredicate(Stub): - """java.util.function.IntPredicate""" - def test(self, value): - raise NotImplementedError - - def and_(self, other): - class And(IntPredicate): - def test(self, value): - return self.test(value) and other.test(value) - return And() - - def negate(self): - class Negate(IntPredicate): - def test(self, value): - return not self.test(value) - return Negate() - - def or_(self, other): - class Or(IntPredicate): - def test(self, value): - return self.test(value) or other.test(value) - return Or() - - -class LongPredicate(Stub): - """java.util.function.LongPredicate""" - def test(self, value): - raise NotImplementedError - - def and_(self, other): - class And(LongPredicate): - def test(self, value): - return self.test(value) and other.test(value) - return And() - - def negate(self): - class Negate(LongPredicate): - def test(self, value): - return not self.test(value) - return Negate() - - def or_(self, other): - class Or(LongPredicate): - def test(self, value): - return self.test(value) or other.test(value) - return Or() - - -class DoublePredicate(Stub): - """java.util.function.DoublePredicate""" - def test(self, value): - raise NotImplementedError - - def and_(self, other): - class And(DoublePredicate): - def test(self, value): - return self.test(value) and other.test(value) - return And() - - def negate(self): - class Negate(DoublePredicate): - def test(self, value): - return not self.test(value) - return Negate() - - def or_(self, other): - class Or(DoublePredicate): - def test(self, value): - return self.test(value) or other.test(value) - return Or() - - -class IntSupplier(Stub): - """java.util.function.IntSupplier""" - def getAsInt(self): - raise NotImplementedError - - -class LongSupplier(Stub): - """java.util.function.LongSupplier""" - def getAsLong(self): - raise NotImplementedError - - -class DoubleSupplier(Stub): - """java.util.function.DoubleSupplier""" - def getAsDouble(self): - raise NotImplementedError - - -class IntBinaryOperator(Stub): - """java.util.function.IntBinaryOperator""" - def applyAsInt(self, left, right): - raise NotImplementedError - - -class LongBinaryOperator(Stub): - """java.util.function.LongBinaryOperator""" - def applyAsLong(self, left, right): - raise NotImplementedError - - -class DoubleBinaryOperator(Stub): - """java.util.function.DoubleBinaryOperator""" - def applyAsDouble(self, left, right): - raise NotImplementedError - - -# ============================================================ -# Int-Unary conversion functions -# ============================================================ - -class IntToDoubleFunction(Stub): - """java.util.function.IntToDoubleFunction""" - def applyAsDouble(self, value): - raise NotImplementedError - - -class IntToLongFunction(Stub): - """java.util.function.IntToLongFunction""" - def applyAsLong(self, value): - raise NotImplementedError - - -class LongToDoubleFunction(Stub): - """java.util.function.LongToDoubleFunction""" - def applyAsDouble(self, value): - raise NotImplementedError - - -class LongToIntFunction(Stub): - """java.util.function.LongToIntFunction""" - def applyAsInt(self, value): - raise NotImplementedError - - -class DoubleToIntFunction(Stub): - """java.util.function.DoubleToIntFunction""" - def applyAsInt(self, value): - raise NotImplementedError - - -class DoubleToLongFunction(Stub): - """java.util.function.DoubleToLongFunction""" - def applyAsLong(self, value): - raise NotImplementedError - - -# ============================================================ -# Obj*Consumer (mix of object and primitive) -# ============================================================ - -class ObjIntConsumer(Stub): - """java.util.function.ObjIntConsumer""" - def accept(self, t, value): - raise NotImplementedError - - -class ObjLongConsumer(Stub): - """java.util.function.ObjLongConsumer""" - def accept(self, t, value): - raise NotImplementedError - - -class ObjDoubleConsumer(Stub): - """java.util.function.ObjDoubleConsumer""" - def accept(self, t, value): - raise NotImplementedError - - -# ============================================================ -# To* functions -# ============================================================ - -class ToIntFunction(Stub): - """java.util.function.ToIntFunction""" - def applyAsInt(self, value): - raise NotImplementedError - - -class ToLongFunction(Stub): - """java.util.function.ToLongFunction""" - def applyAsLong(self, value): - raise NotImplementedError - - -class ToDoubleFunction(Stub): - """java.util.function.ToDoubleFunction""" - def applyAsDouble(self, value): - raise NotImplementedError - - -class ToIntBiFunction(Stub): - """java.util.function.ToIntBiFunction""" - def applyAsInt(self, t, u): - raise NotImplementedError - - -class ToLongBiFunction(Stub): - """java.util.function.ToLongBiFunction""" - def applyAsLong(self, t, u): - raise NotImplementedError - - -class ToDoubleBiFunction(Stub): - """java.util.function.ToDoubleBiFunction""" - def applyAsDouble(self, t, u): - raise NotImplementedError - - -# ============================================================ -# Convenience wrapper helpers -# ============================================================ - -class _FunctionWrapper(Function): - """Wraps a Python callable as a java.util.function.Function""" - def __init__(self, fn): - super().__init__() - self._fn = fn - - def apply(self, t): - return self._fn(t) - - -class _ConsumerWrapper(Consumer): - """Wraps a Python callable as a java.util.function.Consumer""" - def __init__(self, fn): - super().__init__() - self._fn = fn - - def accept(self, t): - self._fn(t) - - -class _PredicateWrapper(Predicate): - """Wraps a Python callable as a java.util.function.Predicate""" - def __init__(self, fn): - super().__init__() - self._fn = fn - - def test(self, t): - return self._fn(t) - - -class _SupplierWrapper(Supplier): - """Wraps a Python callable as a java.util.function.Supplier""" - def __init__(self, fn): - super().__init__() - self._fn = fn - - def get(self): - return self._fn() - - -class _BiFunctionWrapper(BiFunction): - """Wraps a Python callable as a java.util.function.BiFunction""" - def __init__(self, fn): - super().__init__() - self._fn = fn - - def apply(self, t, u): - return self._fn(t, u) - - -class _BiConsumerWrapper(BiConsumer): - """Wraps a Python callable as a java.util.function.BiConsumer""" - def __init__(self, fn): - super().__init__() - self._fn = fn - - def accept(self, t, u): - self._fn(t, u) - - -class _BiPredicateWrapper(BiPredicate): - """Wraps a Python callable as a java.util.function.BiPredicate""" - def __init__(self, fn): - super().__init__() - self._fn = fn - - def test(self, t, u): - return self._fn(t, u) - - -class _IntFunctionWrapper(IntFunction): - """Wraps a Python callable as a java.util.function.IntFunction""" - def __init__(self, fn): - super().__init__() - self._fn = fn - - def apply(self, value): - return self._fn(value) - - -class _LongFunctionWrapper(LongFunction): - """Wraps a Python callable as a java.util.function.LongFunction""" - def __init__(self, fn): - super().__init__() - self._fn = fn - - def apply(self, value): - return self._fn(value) - - -class _DoubleFunctionWrapper(DoubleFunction): - """Wraps a Python callable as a java.util.function.DoubleFunction""" - def __init__(self, fn): - super().__init__() - self._fn = fn - - def apply(self, value): - return self._fn(value) - - -class _IntConsumerWrapper(IntConsumer): - """Wraps a Python callable as a java.util.function.IntConsumer""" - def __init__(self, fn): - super().__init__() - self._fn = fn - - def accept(self, value): - self._fn(value) - - -class _LongConsumerWrapper(LongConsumer): - """Wraps a Python callable as a java.util.function.LongConsumer""" - def __init__(self, fn): - super().__init__() - self._fn = fn - - def accept(self, value): - self._fn(value) - - -class _DoubleConsumerWrapper(DoubleConsumer): - """Wraps a Python callable as a java.util.function.DoubleConsumer""" - def __init__(self, fn): - super().__init__() - self._fn = fn - - def accept(self, value): - self._fn(value) \ No newline at end of file diff --git a/android_pyapi/java/util_logging.py b/android_pyapi/java/util_logging.py deleted file mode 100644 index 24be054..0000000 --- a/android_pyapi/java/util_logging.py +++ /dev/null @@ -1,939 +0,0 @@ -""" -Java java.util.logging standard library APIs implemented in Python. -java.util.logging package - Logger, Level, Handler, Formatter, etc. -""" - -from __future__ import annotations - -from .._core import Stub -from .lang import RuntimeException, Exception as _JavaException -from .security import BasicPermission -import typing as _typing -import sys as _sys -import datetime as _datetime -import io as _io -import threading as _threading -import traceback as _traceback -import os as _os - -__all__ = [ - "Logger", "Level", - "Handler", "ConsoleHandler", "FileHandler", "StreamHandler", - "SocketHandler", "MemoryHandler", - "Formatter", "SimpleFormatter", "XMLFormatter", - "LogRecord", "LogManager", "Filter", "ErrorManager", - "LoggingMXBean", "LoggingPermission", - "OFF", "SEVERE", "WARNING", "INFO", "CONFIG", "FINE", "FINER", "FINEST", "ALL", -] - -import sys as _sys -import threading as _threading - - -# ============================================================ -# Level Constants -# ============================================================ - -class _Integer: - """Wrapper to emulate Integer.MIN_VALUE / MAX_VALUE""" - MIN_VALUE = -2147483648 - MAX_VALUE = 2147483647 - - -OFF = _Integer.MAX_VALUE -SEVERE = 1000 -WARNING = 900 -INFO = 800 -CONFIG = 700 -FINE = 500 -FINER = 400 -FINEST = 300 -ALL = _Integer.MIN_VALUE - - -# ============================================================ -# Level -# ============================================================ - -class Level(Stub): - """java.util.logging.Level""" - def __init__(self, name, value, resource_bundle_name=None): - super().__init__() - self._name = name - self._value = value - self._resource_bundle_name = resource_bundle_name - - def getName(self): - return self._name - - def intValue(self): - return self._value - - def getResourceBundleName(self): - return self._resource_bundle_name - - def toString(self): - return self._name - - def hashCode(self): - return self._value - - def equals(self, other): - if not isinstance(other, Level): - return False - return self._value == other._value - - def parse(self, name): - name_upper = name.upper() - level_map = { - "OFF": Level.OFF, - "SEVERE": Level.SEVERE, - "WARNING": Level.WARNING, - "INFO": Level.INFO, - "CONFIG": Level.CONFIG, - "FINE": Level.FINE, - "FINER": Level.FINER, - "FINEST": Level.FINEST, - "ALL": Level.ALL, - } - if name_upper in level_map: - return level_map[name_upper] - try: - return Level(name, int(name)) - except ValueError: - raise IllegalArgumentException(f"Bad level name: {name}") - - def __lt__(self, other): - if isinstance(other, Level): - return self._value < other._value - return NotImplemented - - def __le__(self, other): - if isinstance(other, Level): - return self._value <= other._value - return NotImplemented - - def __gt__(self, other): - if isinstance(other, Level): - return self._value > other._value - return NotImplemented - - def __ge__(self, other): - if isinstance(other, Level): - return self._value >= other._value - return NotImplemented - - # Static level instances - OFF = None - SEVERE = None - WARNING = None - INFO = None - CONFIG = None - FINE = None - FINER = None - FINEST = None - ALL = None - - -# ============================================================ -# Initialize static Level instances -# ============================================================ - -Level.OFF = Level("OFF", _Integer.MAX_VALUE) -Level.SEVERE = Level("SEVERE", 1000) -Level.WARNING = Level("WARNING", 900) -Level.INFO = Level("INFO", 800) -Level.CONFIG = Level("CONFIG", 700) -Level.FINE = Level("FINE", 500) -Level.FINER = Level("FINER", 400) -Level.FINEST = Level("FINEST", 300) -Level.ALL = Level("ALL", _Integer.MIN_VALUE) - - -# ============================================================ -# LogRecord -# ============================================================ - -class LogRecord(Stub): - """java.util.logging.LogRecord""" - def __init__(self, level, msg): - super().__init__() - self._level = level if isinstance(level, Level) else Level.INFO - self._logger_name = "" - self._message = msg - self._millis = _datetime.datetime.now().timestamp() * 1000 - self._thread_id = _threading.get_ident() - self._source_class_name = None - self._source_method_name = None - self._thrown = None - self._parameters = None - self._resource_bundle = None - self._resource_bundle_name = None - self._sequence_number = id(self) - self._long_thread_id = _threading.get_ident() - - def getLevel(self): - return self._level - - def setLevel(self, level): - self._level = level - - def getLoggerName(self): - return self._logger_name - - def setLoggerName(self, name): - self._logger_name = name - - def getMessage(self): - return self._message - - def setMessage(self, msg): - self._message = msg - - def getMillis(self): - return self._millis - - def setMillis(self, millis): - self._millis = millis - - def getThreadID(self): - return self._thread_id - - def setThreadID(self, thread_id): - self._thread_id = thread_id - - def getSourceClassName(self): - return self._source_class_name - - def setSourceClassName(self, name): - self._source_class_name = name - - def getSourceMethodName(self): - return self._source_method_name - - def setSourceMethodName(self, name): - self._source_method_name = name - - def getThrown(self): - return self._thrown - - def setThrown(self, thrown): - self._thrown = thrown - - def getParameters(self): - return self._parameters - - def setParameters(self, params): - self._parameters = params - - def getResourceBundle(self): - return self._resource_bundle - - def setResourceBundle(self, bundle): - self._resource_bundle = bundle - - def getResourceBundleName(self): - return self._resource_bundle_name - - def setResourceBundleName(self, name): - self._resource_bundle_name = name - - def getSequenceNumber(self): - return self._sequence_number - - def setSequenceNumber(self, seq): - self._sequence_number = seq - - def getLongThreadID(self): - return self._long_thread_id - - def setLongThreadID(self, thread_id): - self._long_thread_id = thread_id - - -# ============================================================ -# Filter -# ============================================================ - -class Filter(Stub): - """java.util.logging.Filter""" - def isLoggable(self, record): - return True - - -# ============================================================ -# Formatter -# ============================================================ - -class Formatter(Stub): - """java.util.logging.Formatter""" - def format(self, record): - raise NotImplementedError - - def getHead(self, handler): - return "" - - def getTail(self, handler): - return "" - - def formatMessage(self, record): - msg = record.getMessage() - params = record.getParameters() - if params: - try: - return msg % params - except Exception: - pass - return msg - - -class SimpleFormatter(Formatter): - """java.util.logging.SimpleFormatter""" - def format(self, record): - import datetime as dt - ts = dt.datetime.fromtimestamp(record.getMillis() / 1000.0) - ts_str = ts.strftime("%Y-%m-%d %H:%M:%S") - msg = self.formatMessage(record) - thrown = record.getThrown() - result = f"{ts_str} {record.getLevel().getName()} {record.getLoggerName()}: {msg}" - if thrown is not None: - result += "\n" + "".join(_traceback.format_exception( - type(thrown), thrown, getattr(thrown, '__traceback__', None) - )) - return result - - -class XMLFormatter(Formatter): - """java.util.logging.XMLFormatter""" - def __init__(self): - super().__init__() - from xml.sax.saxutils import escape - - def format(self, record): - ts = _datetime.datetime.fromtimestamp(record.getMillis() / 1000.0) - ts_str = ts.strftime("%Y-%m-%dT%H:%M:%S") - from xml.sax.saxutils import escape - msg = escape(self.formatMessage(record)) - logger_name = escape(record.getLoggerName() or "") - level_name = record.getLevel().getName() - - parts = [ - "", - f" {ts_str}", - f" {int(record.getMillis())}", - f" {record.getSequenceNumber()}", - f" {logger_name}", - f" {level_name}", - f" {escape(record.getSourceClassName() or '')}", - f" {escape(record.getSourceMethodName() or '')}", - f" {record.getThreadID()}", - f" {msg}", - ] - - thrown = record.getThrown() - if thrown is not None: - import traceback - tb_str = "".join(traceback.format_exception( - type(thrown), thrown, getattr(thrown, '__traceback__', None) - )) - parts.append(f" {escape(tb_str)}") - - parts.append("") - return "\n".join(parts) - - def getHead(self, handler): - return '\n\n\n' - - def getTail(self, handler): - return "\n" - - -# ============================================================ -# ErrorManager -# ============================================================ - -class ErrorManager(Stub): - """java.util.logging.ErrorManager""" - def __init__(self): - super().__init__() - - def error(self, message, exception, error_code): - _sys.stderr.write(f"ErrorManager: {message}\n") - if exception: - _traceback.print_exception(type(exception), exception, - getattr(exception, '__traceback__', None)) - - GENERIC_FAILURE = 0 - WRITE_FAILURE = 1 - FLUSH_FAILURE = 2 - CLOSE_FAILURE = 3 - OPEN_FAILURE = 4 - FORMAT_FAILURE = 5 - - -# ============================================================ -# Handler -# ============================================================ - -class Handler(Stub): - """java.util.logging.Handler""" - def __init__(self): - super().__init__() - self._level = Level.ALL - self._filter = None - self._formatter = None - self._encoding = None - self._error_manager = ErrorManager() - self._closed = False - - def publish(self, record): - raise NotImplementedError - - def flush(self): - pass - - def close(self): - self._closed = True - - def isLoggable(self, record): - if self._closed: - return False - if record.getLevel().intValue() < self._level.intValue(): - return False - if self._filter is not None: - return self._filter.isLoggable(record) - return True - - def setLevel(self, level): - self._level = level - - def getLevel(self): - return self._level - - def setFilter(self, filter_): - self._filter = filter_ - - def getFilter(self): - return self._filter - - def setFormatter(self, formatter): - self._formatter = formatter - - def getFormatter(self): - return self._formatter - - def setEncoding(self, encoding): - self._encoding = encoding - - def getEncoding(self): - return self._encoding - - def setErrorManager(self, em): - self._error_manager = em - - def getErrorManager(self): - return self._error_manager - - def reportError(self, msg, ex, code): - if self._error_manager: - self._error_manager.error(msg, ex, code) - - -class StreamHandler(Handler): - """java.util.logging.StreamHandler""" - def __init__(self, out=None, formatter=None): - super().__init__() - if formatter is None: - formatter = SimpleFormatter() - self.setFormatter(formatter) - self._output = out or _sys.stderr - self._writer = None - if self._output is not None: - if isinstance(self._output, _io.TextIOBase): - self._writer = self._output - elif isinstance(self._output, _io.BufferedWriter): - self._writer = _io.TextIOWrapper(self._output) - else: - self._writer = _io.TextIOWrapper(_io.BufferedWriter(self._output)) - - def publish(self, record): - if not self.isLoggable(record): - return - try: - msg = self.getFormatter().format(record) - if self._writer is not None: - self._writer.write(msg + "\n") - else: - print(msg, file=_sys.stderr) - self.flush() - except Exception as e: - self.reportError("Error publishing record", e, ErrorManager.WRITE_FAILURE) - - def flush(self): - if self._writer is not None: - try: - self._writer.flush() - except Exception: - pass - - def close(self): - self.flush() - if self._writer is not None: - try: - self._writer.close() - except Exception: - pass - super().close() - - def setOutputStream(self, out): - self._output = out - if isinstance(out, _io.TextIOBase): - self._writer = out - elif isinstance(out, _io.BufferedWriter): - self._writer = _io.TextIOWrapper(out) - else: - self._writer = _io.TextIOWrapper(_io.BufferedWriter(out)) - - -class ConsoleHandler(StreamHandler): - """java.util.logging.ConsoleHandler""" - def __init__(self): - super().__init__(_sys.stderr, SimpleFormatter()) - self.setLevel(Level.INFO) - - -class FileHandler(Handler): - """java.util.logging.FileHandler""" - def __init__(self, pattern=None, append=None, limit=0, count=1): - super().__init__() - self._pattern = pattern or "logging.log" - self._limit = limit - self._count = count - self._append = append or False - self._files = [] - - if "%u" in self._pattern or "%g" in self._pattern: - self._current_filename = self._pattern.replace("%u", "0").replace("%g", "0") - else: - self._current_filename = self._pattern - - try: - mode = "a" if self._append else "w" - self._file = open(self._current_filename, mode, encoding="utf-8") - self._writer = self._file - except Exception as e: - self._file = None - self._writer = None - self.reportError("Cannot open file", e, ErrorManager.OPEN_FAILURE) - - self._formatter = SimpleFormatter() - - def publish(self, record): - if not self.isLoggable(record): - return - if self._writer is None: - return - try: - msg = self.getFormatter().format(record) - self._writer.write(msg + "\n") - self.flush() - except Exception as e: - self.reportError("Error publishing record", e, ErrorManager.WRITE_FAILURE) - - def flush(self): - if self._writer is not None: - try: - self._writer.flush() - except Exception: - pass - - def close(self): - self.flush() - if self._writer is not None: - try: - self._writer.close() - except Exception: - pass - super().close() - - -class SocketHandler(Handler): - """java.util.logging.SocketHandler""" - def __init__(self, host, port): - super().__init__() - self._host = host - self._port = port - self._sock = None - self._output_stream = None - - def connect(self): - import socket - self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self._sock.connect((self._host, self._port)) - self._output_stream = self._sock.makefile("wb") - - def publish(self, record): - if not self.isLoggable(record): - return - if self._output_stream is None: - try: - self.connect() - except Exception as e: - self.reportError("Cannot connect", e, ErrorManager.OPEN_FAILURE) - return - try: - msg = self.getFormatter().format(record) - self._output_stream.write(msg.encode() + b"\n") - self.flush() - except Exception as e: - self.reportError("Error sending record", e, ErrorManager.WRITE_FAILURE) - - def flush(self): - if self._output_stream is not None: - try: - self._output_stream.flush() - except Exception: - pass - - def close(self): - self.flush() - if self._output_stream is not None: - try: - self._output_stream.close() - except Exception: - pass - if self._sock is not None: - try: - self._sock.close() - except Exception: - pass - super().close() - - -class MemoryHandler(Handler): - """java.util.logging.MemoryHandler""" - def __init__(self, target=None, size=1000, push_level=Level.SEVERE): - super().__init__() - self._target = target - self._size = size - self._push_level = push_level - self._buffer = [] - - def publish(self, record): - if not self.isLoggable(record): - return - self._buffer.append(record) - if len(self._buffer) >= self._size: - self.push() - elif record.getLevel().intValue() >= self._push_level.intValue(): - self.push() - - def push(self): - if self._target is not None: - for rec in self._buffer: - self._target.publish(rec) - self._buffer.clear() - self._target.flush() - - def flush(self): - self.push() - - def close(self): - self.push() - if self._target is not None: - self._target.close() - super().close() - - def setPushLevel(self, level): - self._push_level = level - - def getPushLevel(self): - return self._push_level - - def setTarget(self, target): - self._target = target - - def getTarget(self): - return self._target - - -# ============================================================ -# Logger -# ============================================================ - -class Logger(Stub): - """java.util.logging.Logger""" - _loggers = {} - _root = None - _global_logger = None - - def __init__(self, name, resource_bundle_name=None): - super().__init__() - self._name = name - self._resource_bundle_name = resource_bundle_name - self._level = None - self._parent = None - self._handlers = [] - self._filter = None - self._use_parent_handlers = True - - @staticmethod - def getGlobal(): - if Logger._global_logger is None: - Logger._global_logger = Logger("global") - return Logger._global_logger - - @staticmethod - def getLogger(name, resource_bundle_name=None): - if not name: - if Logger._root is None: - Logger._root = Logger("", resource_bundle_name) - return Logger._root - if name not in Logger._loggers: - logger = Logger(name, resource_bundle_name) - Logger._loggers[name] = logger - logger._parent = Logger.getLogger("", None) - return Logger._loggers[name] - - def getName(self): - return self._name - - def getParent(self): - return self._parent - - def setParent(self, parent): - self._parent = parent - - def setLevel(self, level): - self._level = level - - def getLevel(self): - return self._level - - def isLoggable(self, level): - if self._level is not None: - return level.intValue() >= self._level.intValue() - if self._parent is not None: - return self._parent.isLoggable(level) - return level.intValue() >= Level.INFO.intValue() - - def addHandler(self, handler): - if handler not in self._handlers: - self._handlers.append(handler) - - def removeHandler(self, handler): - if handler in self._handlers: - self._handlers.remove(handler) - - def getHandlers(self): - return self._handlers - - def setUseParentHandlers(self, use): - self._use_parent_handlers = use - - def getUseParentHandlers(self): - return self._use_parent_handlers - - def setFilter(self, filter_): - self._filter = filter_ - - def getFilter(self): - return self._filter - - def getResourceBundle(self): - return None - - def getResourceBundleName(self): - return self._resource_bundle_name - - def log(self, level, msg_or_supplier, *args): - if isinstance(level, Level): - if self.isLoggable(level): - if callable(msg_or_supplier) and not args: - msg = msg_or_supplier() - else: - msg = msg_or_supplier - record = LogRecord(level, msg) - record.setLoggerName(self._name) - record.setParameters(args or None) - self._log(record) - else: - # log(LogRecord) - self._log(level) - - def _log(self, record): - if self._filter is not None and not self._filter.isLoggable(record): - return - for handler in self._handlers: - if handler.isLoggable(record): - handler.publish(record) - if self._use_parent_handlers and self._parent is not None: - self._parent._log(record) - - def severe(self, msg): - self.log(Level.SEVERE, msg) - - def warning(self, msg): - self.log(Level.WARNING, msg) - - def info(self, msg): - self.log(Level.INFO, msg) - - def config(self, msg): - self.log(Level.CONFIG, msg) - - def fine(self, msg): - self.log(Level.FINE, msg) - - def finer(self, msg): - self.log(Level.FINER, msg) - - def finest(self, msg): - self.log(Level.FINEST, msg) - - def entering(self, source_class, source_method, param=None): - msg = f"ENTRY {source_class}.{source_method}" - if param is not None: - msg += f" {param}" - self.log(Level.FINER, msg) - - def exiting(self, source_class, source_method, result=None): - msg = f"RETURN {source_class}.{source_method}" - if result is not None: - msg += f" {result}" - self.log(Level.FINER, msg) - - def throwing(self, source_class, source_method, thrown): - record = LogRecord(Level.FINER, "THROW") - record.setLoggerName(self._name) - record.setSourceClassName(source_class) - record.setSourceMethodName(source_method) - record.setThrown(thrown) - self._log(record) - - def logp(self, level, source_class, source_method, msg, *params): - if self.isLoggable(level): - record = LogRecord(level, msg) - record.setLoggerName(self._name) - record.setSourceClassName(source_class) - record.setSourceMethodName(source_method) - record.setParameters(params or None) - self._log(record) - - def logrb(self, level, source_class, source_method, bundle_name, msg, *params): - if self.isLoggable(level): - record = LogRecord(level, msg) - record.setLoggerName(self._name) - record.setSourceClassName(source_class) - record.setSourceMethodName(source_method) - record.setResourceBundleName(bundle_name) - record.setParameters(params or None) - self._log(record) - - @staticmethod - def getAnonymousLogger(resource_bundle_name=None): - logger = Logger("", resource_bundle_name) - return logger - - -# ============================================================ -# LogManager -# ============================================================ - -class LogManager(Stub): - """java.util.logging.LogManager""" - _singleton = None - - def __init__(self): - super().__init__() - self._loggers = {} - self._properties = {} - - @staticmethod - def getLogManager(): - if LogManager._singleton is None: - LogManager._singleton = LogManager() - return LogManager._singleton - - def addLogger(self, logger): - name = logger.getName() - if name in self._loggers: - return False - self._loggers[name] = logger - return True - - def getLogger(self, name): - return self._loggers.get(name) - - def getLoggers(self): - return list(self._loggers.values()) - - def readConfiguration(self, input_stream=None): - pass - - def readConfiguration(self, ins): - pass - - def reset(self): - for logger in self._loggers.values(): - for handler in logger.getHandlers()[:]: - handler.close() - logger.removeHandler(handler) - logger.setLevel(None) - - def checkAccess(self): - pass - - def addPropertyChangeListener(self, listener): - pass - - def removePropertyChangeListener(self, listener): - pass - - LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging" - - def getProperty(self, name): - return self._properties.get(name) - - -# ============================================================ -# LoggingMXBean -# ============================================================ - -class LoggingMXBean(Stub): - """java.util.logging.LoggingMXBean""" - def getLoggerLevel(self, logger_name): - logger = LogManager.getLogManager().getLogger(logger_name) - if logger and logger.getLevel(): - return logger.getLevel().getName() - return "" - - def setLoggerLevel(self, logger_name, level_name): - logger = LogManager.getLogManager().getLogger(logger_name) - if logger: - level = Level() - logger.setLevel(level.parse(level_name)) - - def getParentLogger(self, logger_name): - logger = LogManager.getLogManager().getLogger(logger_name) - if logger and logger.getParent(): - return logger.getParent().getName() - return "" - - def getLoggerNames(self): - return list(LogManager.getLogManager()._loggers.keys()) - - -# ============================================================ -# LoggingPermission -# ============================================================ - -class LoggingPermission(BasicPermission): - """java.util.logging.LoggingPermission""" - def __init__(self, name, actions=None): - super().__init__(name, actions) - - -class IllegalArgumentException(RuntimeException): - """java.lang.IllegalArgumentException""" - pass \ No newline at end of file diff --git a/android_pyapi/java/util_prefs.py b/android_pyapi/java/util_prefs.py deleted file mode 100644 index 5511c7f..0000000 --- a/android_pyapi/java/util_prefs.py +++ /dev/null @@ -1,544 +0,0 @@ -""" -Java java.util.prefs standard library APIs implemented in Python. -java.util.prefs package - Preferences, PreferencesFactory, etc. -""" - -from __future__ import annotations - -from .._core import Stub -from ..java.lang import RuntimeException, Exception as _JavaException -import typing as _typing -import json as _json -import os as _os -import threading as _threading - -__all__ = [ - "Preferences", "PreferencesFactory", "AbstractPreferences", - "NodeChangeEvent", "NodeChangeListener", - "PreferenceChangeEvent", "PreferenceChangeListener", - "BackingStoreException", "InvalidPreferencesFormatException", -] - -# ============================================================ -# Exceptions -# ============================================================ - -class BackingStoreException(_JavaException): - """java.util.prefs.BackingStoreException""" - def __init__(self, message): - super().__init__(message) - - -class InvalidPreferencesFormatException(_JavaException): - """java.util.prefs.InvalidPreferencesFormatException""" - def __init__(self, message, cause=None): - super().__init__(message) - self._cause = cause - - def getCause(self): - return self._cause - - -# ============================================================ -# Event Classes -# ============================================================ - -class NodeChangeEvent(Stub): - """java.util.prefs.NodeChangeEvent""" - def __init__(self, parent, child): - super().__init__() - self._parent = parent - self._child = child - - def getParent(self): - return self._parent - - def getChild(self): - return self._child - - -class PreferenceChangeEvent(Stub): - """java.util.prefs.PreferenceChangeEvent""" - def __init__(self, node, key, new_value): - super().__init__() - self._node = node - self._key = key - self._new_value = new_value - - def getNode(self): - return self._node - - def getKey(self): - return self._key - - def getNewValue(self): - return self._new_value - - -class NodeChangeListener(Stub): - """java.util.prefs.NodeChangeListener""" - def childAdded(self, evt): - pass - - def childRemoved(self, evt): - pass - - -class PreferenceChangeListener(Stub): - """java.util.prefs.PreferenceChangeListener""" - def preferenceChange(self, evt): - pass - - -# ============================================================ -# Preferences -# ============================================================ - -class Preferences(Stub): - """java.util.prefs.Preferences""" - MAX_KEY_LENGTH = 80 - MAX_VALUE_LENGTH = 8192 - MAX_NAME_LENGTH = 80 - - def __init__(self, name="", parent=None): - super().__init__() - self._name = name - self._parent = parent - self._children = {} - self._prefs = {} - self._node_listeners = [] - self._pref_listeners = [] - self._absolute_path = self._build_path() - - def _build_path(self): - if self._parent is None: - return "/" + self._name if self._name else "/" - parent_path = self._parent._absolute_path - if parent_path == "/": - return "/" + self._name - return parent_path + "/" + self._name - - def put(self, key, value): - if len(key) > Preferences.MAX_KEY_LENGTH: - raise IllegalArgumentException(f"Key too long: {len(key)}") - if len(value) > Preferences.MAX_VALUE_LENGTH: - raise IllegalArgumentException(f"Value too long: {len(value)}") - self._prefs[key] = value - self._fire_preference_change(key, value) - - def get(self, key, default_value): - return self._prefs.get(key, default_value) - - def remove(self, key): - if key in self._prefs: - old = self._prefs.pop(key) - self._fire_preference_change(key, None) - - def clear(self): - self._prefs.clear() - - def putInt(self, key, value): - self.put(key, str(value)) - - def getInt(self, key, default_value): - val = self.get(key, None) - if val is None: - return default_value - try: - return int(val) - except ValueError: - return default_value - - def putLong(self, key, value): - self.put(key, str(value)) - - def getLong(self, key, default_value): - val = self.get(key, None) - if val is None: - return default_value - try: - return int(val) - except ValueError: - return default_value - - def putFloat(self, key, value): - self.put(key, str(value)) - - def getFloat(self, key, default_value): - val = self.get(key, None) - if val is None: - return default_value - try: - return float(val) - except ValueError: - return default_value - - def putDouble(self, key, value): - self.put(key, str(value)) - - def getDouble(self, key, default_value): - val = self.get(key, None) - if val is None: - return default_value - try: - return float(val) - except ValueError: - return default_value - - def putBoolean(self, key, value): - self.put(key, "true" if value else "false") - - def getBoolean(self, key, default_value): - val = self.get(key, None) - if val is None: - return default_value - return val.lower() == "true" - - def putByteArray(self, key, value): - import base64 - self.put(key, base64.b64encode(value).decode()) - - def getByteArray(self, key, default_value): - import base64 - val = self.get(key, None) - if val is None: - return default_value - try: - return base64.b64decode(val) - except Exception: - return default_value - - def keys(self): - return list(self._prefs.keys()) - - def childrenNames(self): - return list(self._children.keys()) - - def parent(self): - return self._parent - - def node(self, path_name): - if path_name.startswith("/"): - return self._root()._node_path(path_name[1:]) - return self._node_path(path_name) - - def nodeExists(self, path_name): - try: - self.node(path_name) - return True - except BackingStoreException: - return False - - def removeNode(self): - if self._parent is not None: - self._parent._remove_child(self._name) - - def name(self): - return self._name - - def absolutePath(self): - return self._absolute_path - - def isUserNode(self): - return False - - def toString(self): - return self._absolute_path - - def flush(self): - pass - - def sync(self): - pass - - def addNodeChangeListener(self, listener): - self._node_listeners.append(listener) - - def removeNodeChangeListener(self, listener): - if listener in self._node_listeners: - self._node_listeners.remove(listener) - - def addPreferenceChangeListener(self, listener): - self._pref_listeners.append(listener) - - def removePreferenceChangeListener(self, listener): - if listener in self._pref_listeners: - self._pref_listeners.remove(listener) - - def exportNode(self, output_stream): - data = self._to_dict() - output_stream.write(_json.dumps(data, indent=2).encode()) - - def exportSubtree(self, output_stream): - data = self._to_dict(recursive=True) - output_stream.write(_json.dumps(data, indent=2).encode()) - - def _to_dict(self, recursive=False): - result = {"prefs": dict(self._prefs)} - if recursive: - result["children"] = { - name: child._to_dict(recursive=True) - for name, child in self._children.items() - } - return result - - def _root(self): - if self._parent is None: - return self - return self._parent._root() - - def _node_path(self, path): - if not path: - return self - parts = path.split("/") - current = self - for part in parts: - if not part: - continue - if part == "..": - if current._parent is not None: - current = current._parent - continue - if part not in current._children: - current._children[part] = Preferences(part, current) - current = current._children[part] - return current - - def _remove_child(self, name): - if name in self._children: - child = self._children.pop(name) - evt = NodeChangeEvent(self, child) - for listener in self._node_listeners: - listener.childRemoved(evt) - - def _fire_preference_change(self, key, value): - evt = PreferenceChangeEvent(self, key, value) - for listener in self._pref_listeners: - listener.preferenceChange(evt) - - @staticmethod - def userRoot(): - if Preferences._user_root is None: - Preferences._user_root = Preferences("") - return Preferences._user_root - - @staticmethod - def systemRoot(): - if Preferences._system_root is None: - Preferences._system_root = Preferences("") - return Preferences._system_root - - @staticmethod - def importPreferences(input_stream): - data = _json.loads(input_stream.read()) - root = Preferences("") - root._prefs = data.get("prefs", {}) - return root - - _user_root = None - _system_root = None - - -# ============================================================ -# AbstractPreferences -# ============================================================ - -class AbstractPreferences(Preferences): - """java.util.prefs.AbstractPreferences""" - def __init__(self, parent, name): - super().__init__(name, parent) - self._is_removed = False - self._lock = _threading.RLock() - - def put(self, key, value): - with self._lock: - if self._is_removed: - raise BackingStoreException("Node has been removed") - super().put(key, value) - - def get(self, key, default_value): - with self._lock: - if self._is_removed: - raise BackingStoreException("Node has been removed") - return super().get(key, default_value) - - def remove(self, key): - with self._lock: - super().remove(key) - - def keys(self): - with self._lock: - return super().keys() - - def childrenNames(self): - with self._lock: - return super().childrenNames() - - def node(self, path_name): - with self._lock: - return super().node(path_name) - - def removeNode(self): - with self._lock: - super().removeNode() - self._is_removed = True - - def flush(self): - pass - - def sync(self): - pass - - def isUserNode(self): - return False - - -# ============================================================ -# PreferencesFactory -# ============================================================ - -class PreferencesFactory(Stub): - """java.util.prefs.PreferencesFactory""" - def systemRoot(self): - return Preferences.systemRoot() - - def userRoot(self): - return Preferences.userRoot() - - -# ============================================================ -# IllegalArgumentException -# ============================================================ - -class IllegalArgumentException(RuntimeException): - """java.lang.IllegalArgumentException""" - pass - - -# ============================================================ -# Preferences utility helpers -# ============================================================ - -class _PreferencesService(Stub): - """Helper to manage Preferences with file-backed storage""" - def __init__(self, root_path=None): - super().__init__() - if root_path is None: - import os - root_path = os.path.join(os.path.expanduser("~"), ".java", "prefs") - self._root_path = root_path - self._cache = {} - - def loadPreferences(self, node_path): - import os - import json - file_path = os.path.join(self._root_path, node_path.replace("/", "_") + ".json") - prefs = Preferences(node_path) - if os.path.exists(file_path): - try: - with open(file_path, "r") as f: - data = json.load(f) - for key, val in data.items(): - prefs.put(key, val) - except Exception: - pass - self._cache[node_path] = prefs - return prefs - - def savePreferences(self, prefs): - import os - import json - file_path = os.path.join(self._root_path, - prefs.absolutePath().replace("/", "_") + ".json") - os.makedirs(os.path.dirname(file_path), exist_ok=True) - data = {} - for key in prefs.keys(): - data[key] = prefs.get(key, None) - with open(file_path, "w") as f: - json.dump(data, f, indent=2) - - def clearCache(self): - self._cache.clear() - - -class _EventDispatchThread(Stub): - """Helper for dispatching preference change events""" - def __init__(self): - super().__init__() - self._listeners = [] - import threading - self._lock = threading.Lock() - - def addListener(self, listener): - with self._lock: - self._listeners.append(listener) - - def removeListener(self, listener): - with self._lock: - if listener in self._listeners: - self._listeners.remove(listener) - - def dispatchEvent(self, event): - with self._lock: - for listener in self._listeners: - try: - if hasattr(listener, 'preferenceChange'): - listener.preferenceChange(event) - elif callable(listener): - listener(event) - except Exception: - pass - - def getListeners(self): - with self._lock: - return list(self._listeners) - - -# ============================================================ -# FileSystemPreferences - persistent Preferences backed by file -# ============================================================ - -class FileSystemPreferences(AbstractPreferences): - """java.util.prefs.FileSystemPreferences - persistent file-backed preferences""" - def __init__(self, parent, name, storage_path=None): - super().__init__(parent, name) - import os - if storage_path is None: - storage_path = os.path.join(os.path.expanduser("~"), ".java", "user_prefs") - self._storage_path = os.path.join( - storage_path, self.absolutePath().replace("/", "_") + ".xml" - ) - self._loaded = False - self._service = _PreferencesService(storage_path) - - def _ensureLoaded(self): - if not self._loaded: - try: - import os - if os.path.exists(self._storage_path): - node = self._service.loadPreferences(self.absolutePath()) - for key in node.keys(): - self._prefs[key] = node.get(key, "") - self._loaded = True - except Exception: - self._loaded = True - - def put(self, key, value): - self._ensureLoaded() - super().put(key, value) - - def get(self, key, default_value): - self._ensureLoaded() - return super().get(key, default_value) - - def keys(self): - self._ensureLoaded() - return super().keys() - - def flush(self): - self._service.savePreferences(self) - super().flush() - - def sync(self): - self._ensureLoaded() - self._service.savePreferences(self) \ No newline at end of file diff --git a/android_pyapi/java/util_regex.py b/android_pyapi/java/util_regex.py deleted file mode 100644 index 53e9b7b..0000000 --- a/android_pyapi/java/util_regex.py +++ /dev/null @@ -1,287 +0,0 @@ -""" -Java regex standard library APIs implemented in Python. -java.util.regex package - Pattern, Matcher, MatchResult, PatternSyntaxException. -""" - -from .._core import Stub -from .io import IOException -from .lang import RuntimeException -import re as _re - -IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) -IllegalStateException = type("IllegalStateException", (RuntimeException,), {}) - -__all__ = [ - "Pattern", "Matcher", "MatchResult", "PatternSyntaxException", -] - - -# ============================================================ -# PatternSyntaxException -# ============================================================ - -class PatternSyntaxException(IllegalArgumentException): - def __init__(self, desc, regex, index): - super().__init__(f"{desc}: {regex} at index {index}") - self._desc = desc - self._regex = regex - self._index = index - - def getDescription(self): - return self._desc - - def getPattern(self): - return self._regex - - def getIndex(self): - return self._index - - def getMessage(self): - return f"{self._desc} near index {self._index}: {self._regex}" - - -# ============================================================ -# MatchResult -# ============================================================ - -class MatchResult(Stub): - def start(self, group=None): - raise NotImplementedError - - def end(self, group=None): - raise NotImplementedError - - def group(self, group=None): - raise NotImplementedError - - def groupCount(self): - raise NotImplementedError - - -# ============================================================ -# Matcher -# ============================================================ - -class Matcher(MatchResult): - def __init__(self, pattern, input_str): - super().__init__() - self._pattern = pattern - self._input = input_str - self._re_pattern = pattern._pattern - self._match = None - self._region_start = 0 - self._region_end = len(input_str) - self._transparent_bounds = False - self._anchoring_bounds = True - self._last_match = None - - def matches(self): - self._match = self._re_pattern.fullmatch(self._input, self._region_start, self._region_end) - self._last_match = self._match - return self._match is not None - - def find(self, start=None): - if start is not None: - self._match = self._re_pattern.search(self._input, start, self._region_end) - else: - if self._match is None: - self._match = self._re_pattern.search(self._input, self._region_start, self._region_end) - else: - pos = self._match.end() - self._match = self._re_pattern.search(self._input, pos, self._region_end) - if self._match: - self._last_match = self._match - return self._match is not None - - def lookingAt(self): - self._match = self._re_pattern.match(self._input, self._region_start, self._region_end) - self._last_match = self._match - return self._match is not None - - def start(self, group=0): - if self._match is None: - raise IllegalStateException("No match available") - if group == 0: - return self._match.start() - return self._match.start(group) - - def end(self, group=0): - if self._match is None: - raise IllegalStateException("No match available") - if group == 0: - return self._match.end() - return self._match.end(group) - - def group(self, group=0): - if self._match is None: - raise IllegalStateException("No match available") - if isinstance(group, str): - return self._match.group(group) - return self._match.group(group) - - def groupCount(self): - return self._re_pattern.groups - - def replaceAll(self, replacement): - return self._re_pattern.sub(replacement, self._input) - - def replaceFirst(self, replacement): - return self._re_pattern.sub(replacement, self._input, count=1) - - def appendReplacement(self, sb, replacement): - if self._match is None: - raise IllegalStateException("No match available") - self._append_pos = getattr(self, '_append_pos', 0) - sb.append(self._input[self._append_pos:self._match.start()]) - sb.append(self._re_pattern.sub(replacement, self._match.group(), count=1)) - self._append_pos = self._match.end() - return self - - def appendTail(self, sb): - self._append_pos = getattr(self, '_append_pos', 0) - sb.append(self._input[self._append_pos:]) - return sb - - def reset(self, input_str=None): - if input_str is not None: - self._input = input_str - self._region_end = len(input_str) - self._match = None - self._last_match = None - return self - - def region(self, start, end): - self._region_start = start - self._region_end = end - return self - - def regionStart(self): - return self._region_start - - def regionEnd(self): - return self._region_end - - def hasTransparentBounds(self): - return self._transparent_bounds - - def hasAnchoringBounds(self): - return self._anchoring_bounds - - def useAnchoringBounds(self, value): - self._anchoring_bounds = value - return self - - def useTransparentBounds(self, value): - self._transparent_bounds = value - return self - - def hitEnd(self): - return self._match is not None and self._match.end() >= self._region_end - - def requireEnd(self): - return self._match is not None and not self.hitEnd() - - def toMatchResult(self): - result = Matcher(self._pattern, self._input) - result._match = self._match - result._last_match = self._last_match - return result - - def pattern(self): - return self._pattern - - def toString(self): - return f"Matcher[pattern={self._pattern.pattern()}]" - - def namedGroups(self): - return self._re_pattern.groupindex - - -class IllegalStateException(RuntimeException): - pass - - -# ============================================================ -# Pattern -# ============================================================ - -class Pattern(Stub): - CANON_EQ = 128 - CASE_INSENSITIVE = 2 - COMMENTS = 4 - DOTALL = 32 - LITERAL = 16 - MULTILINE = 8 - UNICODE_CASE = 64 - UNICODE_CHARACTER_CLASS = 256 - UNIX_LINES = 1 - - _re_flags_map = { - 0: 0, # default - 2: _re.IGNORECASE, - 8: _re.MULTILINE, - 32: _re.DOTALL, - 64: _re.UNICODE, - 4: _re.VERBOSE, - } - - def __init__(self, regex, flags=0): - super().__init__() - self._regex = str(regex) - self._flags = flags - re_flags = 0 - for java_flag, py_flag in self._re_flags_map.items(): - if flags & java_flag: - re_flags |= py_flag - try: - self._pattern = _re.compile(self._regex, re_flags) - except _re.error as e: - raise PatternSyntaxException(str(e), self._regex, e.pos if hasattr(e, 'pos') else 0) - - @staticmethod - def compile(regex, flags=0): - return Pattern(regex, flags) - - def matcher(self, input_str): - return Matcher(self, input_str) - - def split(self, input_str, limit=0): - if limit <= 0: - parts = self._pattern.split(str(input_str)) - if limit == 0: - while parts and parts[-1] == "": - parts.pop() - else: - parts = self._pattern.split(str(input_str), maxsplit=limit - 1) - while len(parts) < limit: - parts.append("") - return parts - - def splitAsStream(self, input_str): - return iter(self.split(input_str)) - - @staticmethod - def matches(regex, input_str): - return bool(_re.fullmatch(regex, str(input_str))) - - @staticmethod - def quote(s): - return _re.escape(str(s)) - - def flags(self): - return self._flags - - def pattern(self): - return self._regex - - def toString(self): - return self._regex - - def predicate(self): - return lambda s: bool(self._pattern.fullmatch(str(s))) - - def asPredicate(self): - return self.predicate() - - def asMatchPredicate(self): - return lambda s: bool(self._pattern.fullmatch(str(s))) \ No newline at end of file diff --git a/android_pyapi/java/util_stream.py b/android_pyapi/java/util_stream.py deleted file mode 100644 index 096cd6c..0000000 --- a/android_pyapi/java/util_stream.py +++ /dev/null @@ -1,1380 +0,0 @@ -""" -Java java.util.stream standard library APIs implemented in Python. -java.util.stream package - Stream, IntStream, LongStream, DoubleStream, Collectors, etc. -""" - -from __future__ import annotations - -from .._core import Stub -from ..java.util import Optional, NoSuchElementException -from ..java.lang import RuntimeException -import typing as _typing -import itertools as _itertools -import functools as _functools -import math as _py_math - -__all__ = [ - "Stream", "IntStream", "LongStream", "DoubleStream", - "Collector", "Collectors", "StreamSupport", - "BaseStream", "Sink", "TerminalOp", "TerminalSink", - "AbstractPipeline", "ReferencePipeline", - "IntPipeline", "LongPipeline", "DoublePipeline", - "SliceOps", "WhileOps", "DistinctOps", "SortedOps", - "FindOps", "MatchOps", "ReduceOps", "ForEachOps", - "MapOps", "FilterOps", "FlatMapOps", "PeekOps", -] - -T = _typing.TypeVar("T") -R = _typing.TypeVar("R") -A = _typing.TypeVar("A") - - -# ============================================================ -# BaseStream -# ============================================================ - -class BaseStream(Stub): - """java.util.stream.BaseStream""" - def __init__(self): - super().__init__() - self._closed = False - self._parallel = False - - def iterator(self): - raise NotImplementedError - - def spliterator(self): - raise NotImplementedError - - def isParallel(self): - return self._parallel - - def sequential(self): - self._parallel = False - return self - - def parallel(self): - self._parallel = True - return self - - def unordered(self): - return self - - def onClose(self, close_handler): - return self - - def close(self): - self._closed = True - - -# ============================================================ -# Builder -# ============================================================ - -class StreamBuilder(Stub): - """java.util.stream.Stream.Builder""" - def __init__(self): - super().__init__() - self._elements = [] - - def accept(self, t): - self._elements.append(t) - - def add(self, t): - self._elements.append(t) - return self - - def build(self): - return Stream(self._elements[:]) - - def toString(self): - return "Stream.Builder" - - -class IntStreamBuilder(Stub): - """java.util.stream.IntStream.Builder""" - def __init__(self): - super().__init__() - self._elements = [] - - def accept(self, t): - self._elements.append(int(t)) - - def add(self, t): - self._elements.append(int(t)) - return self - - def build(self): - return IntStream(self._elements[:]) - - -class LongStreamBuilder(Stub): - """java.util.stream.LongStream.Builder""" - def __init__(self): - super().__init__() - self._elements = [] - - def accept(self, t): - self._elements.append(int(t)) - - def add(self, t): - self._elements.append(int(t)) - return self - - def build(self): - return LongStream(self._elements[:]) - - -class DoubleStreamBuilder(Stub): - """java.util.stream.DoubleStream.Builder""" - def __init__(self): - super().__init__() - self._elements = [] - - def accept(self, t): - self._elements.append(float(t)) - - def add(self, t): - self._elements.append(float(t)) - return self - - def build(self): - return DoubleStream(self._elements[:]) - - -# ============================================================ -# Stream -# ============================================================ - -class Stream(BaseStream): - """java.util.stream.Stream""" - def __init__(self, data=None): - super().__init__() - self._data = list(data) if data is not None else [] - self._intermediate_ops = [] - - def filter(self, predicate): - result = [x for x in self._get_data() if predicate.test(x)] - return Stream(result) - - def map(self, mapper): - result = [mapper.apply(x) for x in self._get_data()] - return Stream(result) - - def mapToInt(self, mapper): - result = [mapper.applyAsInt(x) for x in self._get_data()] - return IntStream(result) - - def mapToLong(self, mapper): - result = [mapper.applyAsLong(x) for x in self._get_data()] - return LongStream(result) - - def mapToDouble(self, mapper): - result = [mapper.applyAsDouble(x) for x in self._get_data()] - return DoubleStream(result) - - def flatMap(self, mapper): - results = [] - for x in self._get_data(): - stream = mapper.apply(x) - if isinstance(stream, Stream): - results.extend(stream._get_data()) - elif hasattr(stream, '__iter__'): - results.extend(stream) - return Stream(results) - - def distinct(self): - seen = set() - result = [] - for x in self._get_data(): - if x not in seen: - seen.add(x) - result.append(x) - return Stream(result) - - def sorted(self, comparator=None): - result = list(self._get_data()) - if comparator: - from functools import cmp_to_key - result.sort(key=cmp_to_key( - lambda a, b: comparator.compare(a, b) - )) - else: - result.sort() - return Stream(result) - - def peek(self, action): - for x in self._get_data(): - action.accept(x) - return Stream(self._get_data()) - - def limit(self, max_size): - return Stream(self._get_data()[:max_size]) - - def skip(self, n): - return Stream(self._get_data()[n:]) - - def forEach(self, action): - for x in self._get_data(): - action.accept(x) - - def forEachOrdered(self, action): - self.forEach(action) - - def toArray(self, generator=None): - data = self._get_data() - if generator is not None: - arr = generator.apply(len(data)) - for i, x in enumerate(data): - arr[i] = x - return arr - return list(data) - - def reduce(self, identity_or_accumulator, accumulator=None, combiner=None): - data = self._get_data() - if accumulator is None: - # reduce(BinaryOperator) - acc = identity_or_accumulator - if not data: - return Optional.empty() - result = data[0] - for x in data[1:]: - result = acc.apply(result, x) - return Optional.of(result) - else: - # reduce(identity, accumulator) - result = identity_or_accumulator - for x in data: - result = accumulator.apply(result, x) - if combiner: - result = combiner.apply(result, result) - return result - - def collect(self, supplier_or_collector, accumulator=None, combiner=None): - data = self._get_data() - if accumulator is None: - return supplier_or_collector # Collector - container = supplier_or_collector.get() - for x in data: - accumulator.accept(container, x) - return container - - def min(self, comparator): - data = self._get_data() - if not data: - return Optional.empty() - return Optional.of(min(data, key=_functools.cmp_to_key( - lambda a, b: comparator.compare(a, b) - ))) - - def max(self, comparator): - data = self._get_data() - if not data: - return Optional.empty() - return Optional.of(max(data, key=_functools.cmp_to_key( - lambda a, b: comparator.compare(a, b) - ))) - - def count(self): - return len(self._get_data()) - - def anyMatch(self, predicate): - return any(predicate.test(x) for x in self._get_data()) - - def allMatch(self, predicate): - return all(predicate.test(x) for x in self._get_data()) - - def noneMatch(self, predicate): - return not any(predicate.test(x) for x in self._get_data()) - - def findFirst(self): - data = self._get_data() - if data: - return Optional.of(data[0]) - return Optional.empty() - - def findAny(self): - data = self._get_data() - if data: - return Optional.of(data[0]) - return Optional.empty() - - def flatMapToInt(self, mapper): - results = [] - for x in self._get_data(): - stream = mapper.apply(x) - if isinstance(stream, IntStream): - results.extend(stream._get_data()) - return IntStream(results) - - def flatMapToLong(self, mapper): - results = [] - for x in self._get_data(): - stream = mapper.apply(x) - if isinstance(stream, LongStream): - results.extend(stream._get_data()) - return LongStream(results) - - def flatMapToDouble(self, mapper): - results = [] - for x in self._get_data(): - stream = mapper.apply(x) - if isinstance(stream, DoubleStream): - results.extend(stream._get_data()) - return DoubleStream(results) - - def iterator(self): - return iter(self._get_data()) - - def spliterator(self): - return Spliterator(self._get_data()) - - def isParallel(self): - return self._parallel - - def _get_data(self): - return self._data - - @staticmethod - def of(*args): - return Stream(list(args)) - - @staticmethod - def empty(): - return Stream([]) - - @staticmethod - def concat(a, b): - return Stream(list(a._get_data()) + list(b._get_data())) - - @staticmethod - def generate(supplier): - return Stream([]) - - @staticmethod - def iterate(seed, has_next_or_predicate, next_fn=None): - if next_fn is None: - fn = seed - return Stream([]) - result = [] - current = seed - while has_next_or_predicate.test(current): - result.append(current) - current = next_fn.apply(current) - return Stream(result) - - # Nested Builder class - class Builder: - def __init__(self): - self._impl = StreamBuilder() - - def accept(self, t): - self._impl.accept(t) - - def add(self, t): - self._impl.add(t) - return self - - def build(self): - return self._impl.build() - - -# ============================================================ -# IntStream -# ============================================================ - -class IntStream(BaseStream): - """java.util.stream.IntStream""" - def __init__(self, data=None): - super().__init__() - self._data = [int(x) for x in (data or [])] - - def filter(self, predicate): - return IntStream([x for x in self._data if predicate.test(x)]) - - def map(self, mapper): - return IntStream([mapper.applyAsInt(x) for x in self._data]) - - def mapToObj(self, mapper): - return Stream([mapper.apply(x) for x in self._data]) - - def mapToLong(self, mapper): - return LongStream([mapper.applyAsLong(x) for x in self._data]) - - def mapToDouble(self, mapper): - return DoubleStream([mapper.applyAsDouble(x) for x in self._data]) - - def flatMap(self, mapper): - results = [] - for x in self._data: - stream = mapper.apply(x) - if isinstance(stream, IntStream): - results.extend(stream._data) - return IntStream(results) - - def distinct(self): - return IntStream(list(set(self._data))) - - def sorted(self): - return IntStream(sorted(self._data)) - - def peek(self, action): - for x in self._data: - action.accept(x) - return IntStream(self._data[:]) - - def limit(self, max_size): - return IntStream(self._data[:max_size]) - - def skip(self, n): - return IntStream(self._data[n:]) - - def forEach(self, action): - for x in self._data: - action.accept(x) - - def forEachOrdered(self, action): - self.forEach(action) - - def toArray(self): - return list(self._data) - - def reduce(self, identity_or_op, op=None): - if op is None: - if not self._data: - return Optional.empty() - result = self._data[0] - for x in self._data[1:]: - result = identity_or_op.applyAsInt(result, x) - return Optional.of(result) - result = identity_or_op - for x in self._data: - result = op.applyAsInt(result, x) - return result - - def collect(self, supplier, accumulator, combiner): - container = supplier.get() - for x in self._data: - accumulator.accept(container, x) - return container - - def sum(self): - return sum(self._data) - - def min(self): - if not self._data: - return Optional.empty() - return Optional.of(min(self._data)) - - def max(self): - if not self._data: - return Optional.empty() - return Optional.of(max(self._data)) - - def count(self): - return len(self._data) - - def average(self): - if not self._data: - return Optional.empty() - return Optional.of(sum(self._data) / len(self._data)) - - def summaryStatistics(self): - return IntSummaryStatistics(self._data) - - def anyMatch(self, predicate): - return any(predicate.test(x) for x in self._data) - - def allMatch(self, predicate): - return all(predicate.test(x) for x in self._data) - - def noneMatch(self, predicate): - return not any(predicate.test(x) for x in self._data) - - def findFirst(self): - if self._data: - return Optional.of(self._data[0]) - return Optional.empty() - - def findAny(self): - return self.findFirst() - - def asDoubleStream(self): - return DoubleStream([float(x) for x in self._data]) - - def asLongStream(self): - return LongStream(self._data[:]) - - def boxed(self): - return Stream(self._data[:]) - - def iterator(self): - return iter(self._data) - - def _get_data(self): - return self._data - - @staticmethod - def of(*args): - return IntStream(list(args)) - - @staticmethod - def empty(): - return IntStream([]) - - @staticmethod - def concat(a, b): - return IntStream(list(a._data) + list(b._data)) - - @staticmethod - def range(start, end): - return IntStream(list(range(start, end))) - - @staticmethod - def rangeClosed(start, end): - return IntStream(list(range(start, end + 1))) - - @staticmethod - def generate(supplier): - return IntStream([]) - - @staticmethod - def iterate(seed, has_next, next_fn): - return IntStream([]) - - class Builder: - def __init__(self): - self._impl = IntStreamBuilder() - - def accept(self, t): - self._impl.accept(t) - - def add(self, t): - self._impl.add(t) - return self - - def build(self): - return self._impl.build() - - -# ============================================================ -# LongStream -# ============================================================ - -class LongStream(BaseStream): - """java.util.stream.LongStream""" - def __init__(self, data=None): - super().__init__() - self._data = [int(x) for x in (data or [])] - - def filter(self, predicate): - return LongStream([x for x in self._data if predicate.test(x)]) - - def map(self, mapper): - return LongStream([mapper.applyAsLong(x) for x in self._data]) - - def mapToObj(self, mapper): - return Stream([mapper.apply(x) for x in self._data]) - - def mapToInt(self, mapper): - return IntStream([mapper.applyAsInt(x) for x in self._data]) - - def mapToDouble(self, mapper): - return DoubleStream([mapper.applyAsDouble(x) for x in self._data]) - - def flatMap(self, mapper): - results = [] - for x in self._data: - stream = mapper.apply(x) - if isinstance(stream, LongStream): - results.extend(stream._data) - return LongStream(results) - - def distinct(self): - return LongStream(list(set(self._data))) - - def sorted(self): - return LongStream(sorted(self._data)) - - def peek(self, action): - for x in self._data: - action.accept(x) - return LongStream(self._data[:]) - - def limit(self, max_size): - return LongStream(self._data[:max_size]) - - def skip(self, n): - return LongStream(self._data[n:]) - - def forEach(self, action): - for x in self._data: - action.accept(x) - - def forEachOrdered(self, action): - self.forEach(action) - - def toArray(self): - return list(self._data) - - def reduce(self, identity_or_op, op=None): - if op is None: - if not self._data: - return Optional.empty() - result = self._data[0] - for x in self._data[1:]: - result = identity_or_op.applyAsLong(result, x) - return Optional.of(result) - result = identity_or_op - for x in self._data: - result = op.applyAsLong(result, x) - return result - - def sum(self): - return sum(self._data) - - def min(self): - if not self._data: - return Optional.empty() - return Optional.of(min(self._data)) - - def max(self): - if not self._data: - return Optional.empty() - return Optional.of(max(self._data)) - - def count(self): - return len(self._data) - - def average(self): - if not self._data: - return Optional.empty() - return Optional.of(sum(self._data) / len(self._data)) - - def summaryStatistics(self): - return LongSummaryStatistics(self._data) - - def anyMatch(self, predicate): - return any(predicate.test(x) for x in self._data) - - def allMatch(self, predicate): - return all(predicate.test(x) for x in self._data) - - def noneMatch(self, predicate): - return not any(predicate.test(x) for x in self._data) - - def findFirst(self): - if self._data: - return Optional.of(self._data[0]) - return Optional.empty() - - def findAny(self): - return self.findFirst() - - def asDoubleStream(self): - return DoubleStream([float(x) for x in self._data]) - - def boxed(self): - return Stream(self._data[:]) - - def iterator(self): - return iter(self._data) - - def _get_data(self): - return self._data - - @staticmethod - def of(*args): - return LongStream(list(args)) - - @staticmethod - def empty(): - return LongStream([]) - - @staticmethod - def concat(a, b): - return LongStream(list(a._data) + list(b._data)) - - @staticmethod - def range(start, end): - return LongStream(list(range(start, end))) - - @staticmethod - def rangeClosed(start, end): - return LongStream(list(range(start, end + 1))) - - class Builder: - def __init__(self): - self._impl = LongStreamBuilder() - - def accept(self, t): - self._impl.accept(t) - - def add(self, t): - self._impl.add(t) - return self - - def build(self): - return self._impl.build() - - -# ============================================================ -# DoubleStream -# ============================================================ - -class DoubleStream(BaseStream): - """java.util.stream.DoubleStream""" - def __init__(self, data=None): - super().__init__() - self._data = [float(x) for x in (data or [])] - - def filter(self, predicate): - return DoubleStream([x for x in self._data if predicate.test(x)]) - - def map(self, mapper): - return DoubleStream([mapper.applyAsDouble(x) for x in self._data]) - - def mapToObj(self, mapper): - return Stream([mapper.apply(x) for x in self._data]) - - def mapToInt(self, mapper): - return IntStream([mapper.applyAsInt(x) for x in self._data]) - - def mapToLong(self, mapper): - return LongStream([mapper.applyAsLong(x) for x in self._data]) - - def flatMap(self, mapper): - results = [] - for x in self._data: - stream = mapper.apply(x) - if isinstance(stream, DoubleStream): - results.extend(stream._data) - return DoubleStream(results) - - def distinct(self): - return DoubleStream(list(set(self._data))) - - def sorted(self): - return DoubleStream(sorted(self._data)) - - def peek(self, action): - for x in self._data: - action.accept(x) - return DoubleStream(self._data[:]) - - def limit(self, max_size): - return DoubleStream(self._data[:max_size]) - - def skip(self, n): - return DoubleStream(self._data[n:]) - - def forEach(self, action): - for x in self._data: - action.accept(x) - - def forEachOrdered(self, action): - self.forEach(action) - - def toArray(self): - return list(self._data) - - def reduce(self, identity_or_op, op=None): - if op is None: - if not self._data: - return Optional.empty() - result = self._data[0] - for x in self._data[1:]: - result = identity_or_op.applyAsDouble(result, x) - return Optional.of(result) - result = identity_or_op - for x in self._data: - result = op.applyAsDouble(result, x) - return result - - def sum(self): - return sum(self._data) - - def min(self): - if not self._data: - return Optional.empty() - return Optional.of(min(self._data)) - - def max(self): - if not self._data: - return Optional.empty() - return Optional.of(max(self._data)) - - def count(self): - return len(self._data) - - def average(self): - if not self._data: - return Optional.empty() - return Optional.of(sum(self._data) / len(self._data)) - - def summaryStatistics(self): - return DoubleSummaryStatistics(self._data) - - def anyMatch(self, predicate): - return any(predicate.test(x) for x in self._data) - - def allMatch(self, predicate): - return all(predicate.test(x) for x in self._data) - - def noneMatch(self, predicate): - return not any(predicate.test(x) for x in self._data) - - def findFirst(self): - if self._data: - return Optional.of(self._data[0]) - return Optional.empty() - - def findAny(self): - return self.findFirst() - - def iterator(self): - return iter(self._data) - - def _get_data(self): - return self._data - - @staticmethod - def of(*args): - return DoubleStream(list(args)) - - @staticmethod - def empty(): - return DoubleStream([]) - - @staticmethod - def concat(a, b): - return DoubleStream(list(a._data) + list(b._data)) - - @staticmethod - def generate(supplier): - return DoubleStream([]) - - class Builder: - def __init__(self): - self._impl = DoubleStreamBuilder() - - def accept(self, t): - self._impl.accept(t) - - def add(self, t): - self._impl.add(t) - return self - - def build(self): - return self._impl.build() - - -# ============================================================ -# Collector -# ============================================================ - -class Collector(Stub): - """java.util.stream.Collector""" - class Characteristics: - CONCURRENT = "CONCURRENT" - UNORDERED = "UNORDERED" - IDENTITY_FINISH = "IDENTITY_FINISH" - - def __init__(self, supplier, accumulator, combiner, finisher=None, - characteristics=None): - super().__init__() - self._supplier = supplier - self._accumulator = accumulator - self._combiner = combiner - self._finisher = finisher - self._characteristics = characteristics or [] - - def supplier(self): - return self._supplier - - def accumulator(self): - return self._accumulator - - def combiner(self): - return self._combiner - - def finisher(self): - return self._finisher - - def characteristics(self): - return self._characteristics - - -# ============================================================ -# Collectors -# ============================================================ - -class Collectors(Stub): - """java.util.stream.Collectors""" - @staticmethod - def toList(): - class ToListCollector(Collector): - def __init__(self): - s = lambda: [] - a = lambda lst, item: lst.append(item) or lst - c = lambda l1, l2: l1 + l2 - super().__init__(s, a, c) - return ToListCollector() - - @staticmethod - def toSet(): - class ToSetCollector(Collector): - def __init__(self): - s = lambda: set() - a = lambda st, item: st.add(item) or st - c = lambda s1, s2: s1 | s2 - super().__init__(s, a, c) - return ToSetCollector() - - @staticmethod - def joining(delimiter="", prefix="", suffix=""): - class JoiningCollector(Collector): - def __init__(self): - s = lambda: [] - a = lambda lst, item: lst.append(str(item)) or lst - c = lambda l1, l2: l1 + l2 - f = lambda lst: prefix + delimiter.join(lst) + suffix - super().__init__(s, a, c, f) - return JoiningCollector() - - @staticmethod - def mapping(mapper, downstream): - class MappingCollector(Collector): - def __init__(self): - s = downstream.supplier() - a = lambda container, item: downstream.accumulator()(container, mapper(item)) - c = downstream.combiner() - f = downstream.finisher() - super().__init__(s, a, c, f) - return MappingCollector() - - @staticmethod - def filtering(predicate, downstream): - class FilteringCollector(Collector): - def __init__(self): - s = downstream.supplier() - a = lambda container, item: ( - downstream.accumulator()(container, item) - if predicate(item) else container - ) - c = downstream.combiner() - f = downstream.finisher() - super().__init__(s, a, c, f) - return FilteringCollector() - - @staticmethod - def collectingAndThen(downstream, finisher): - class CollectingAndThenCollector(Collector): - def __init__(self): - s = downstream.supplier() - a = downstream.accumulator() - c = downstream.combiner() - f = lambda result: finisher(downstream.finisher()(result)) if downstream.finisher() else finisher(result) - super().__init__(s, a, c, f) - return CollectingAndThenCollector() - - @staticmethod - def counting(): - class CountingCollector(Collector): - def __init__(self): - s = lambda: [0] - a = lambda cnt, item: [cnt[0] + 1] - c = lambda c1, c2: [c1[0] + c2[0]] - f = lambda cnt: cnt[0] - super().__init__(s, a, c, f) - return CountingCollector() - - @staticmethod - def summingInt(mapper): - class SummingIntCollector(Collector): - def __init__(self): - s = lambda: [0] - a = lambda total, item: [total[0] + mapper(item)] - c = lambda t1, t2: [t1[0] + t2[0]] - f = lambda total: total[0] - super().__init__(s, a, c, f) - return SummingIntCollector() - - @staticmethod - def summingLong(mapper): - class SummingLongCollector(Collector): - def __init__(self): - s = lambda: [0] - a = lambda total, item: [total[0] + mapper(item)] - c = lambda t1, t2: [t1[0] + t2[0]] - f = lambda total: total[0] - super().__init__(s, a, c, f) - return SummingLongCollector() - - @staticmethod - def summingDouble(mapper): - class SummingDoubleCollector(Collector): - def __init__(self): - s = lambda: [0.0] - a = lambda total, item: [total[0] + mapper(item)] - c = lambda t1, t2: [t1[0] + t2[0]] - f = lambda total: total[0] - super().__init__(s, a, c, f) - return SummingDoubleCollector() - - @staticmethod - def averagingInt(mapper): - class AveragingIntCollector(Collector): - def __init__(self): - s = lambda: [0, 0] # [sum, count] - a = lambda acc, item: [acc[0] + mapper(item), acc[1] + 1] - c = lambda a1, a2: [a1[0] + a2[0], a1[1] + a2[1]] - f = lambda acc: acc[0] / acc[1] if acc[1] > 0 else 0.0 - super().__init__(s, a, c, f) - return AveragingIntCollector() - - @staticmethod - def averagingLong(mapper): - class AveragingLongCollector(Collector): - def __init__(self): - s = lambda: [0, 0] - a = lambda acc, item: [acc[0] + mapper(item), acc[1] + 1] - c = lambda a1, a2: [a1[0] + a2[0], a1[1] + a2[1]] - f = lambda acc: acc[0] / acc[1] if acc[1] > 0 else 0.0 - super().__init__(s, a, c, f) - return AveragingLongCollector() - - @staticmethod - def averagingDouble(mapper): - class AveragingDoubleCollector(Collector): - def __init__(self): - s = lambda: [0.0, 0] - a = lambda acc, item: [acc[0] + mapper(item), acc[1] + 1] - c = lambda a1, a2: [a1[0] + a2[0], a1[1] + a2[1]] - f = lambda acc: acc[0] / acc[1] if acc[1] > 0 else 0.0 - super().__init__(s, a, c, f) - return AveragingDoubleCollector() - - @staticmethod - def reducing(identity, mapper_or_op, op=None): - class ReducingCollector(Collector): - def __init__(self): - if op is None: - s = lambda: [identity] - a = lambda acc, item: [acc[0] + mapper_or_op(item)] - c = lambda a1, a2: [a1[0] + a2[0]] - f = lambda acc: acc[0] - else: - s = lambda: [identity] - a = lambda acc, item: [mapper_or_op.apply(acc[0], item)] - c = lambda a1, a2: [a1[0] + a2[0]] - f = lambda acc: acc[0] - super().__init__(s, a, c, f) - return ReducingCollector() - - @staticmethod - def groupingBy(classifier, downstream_or_map_factory=None, downstream=None): - class GroupingByCollector(Collector): - def __init__(self): - s = lambda: {} - a = lambda m, item: ( - m.setdefault(classifier(item), []).append(item) or m - ) - c = lambda m1, m2: ( - {k: m1.get(k, []) + m2.get(k, []) for k in set(m1) | set(m2)} - ) - f = None - super().__init__(s, a, c, f) - return GroupingByCollector() - - @staticmethod - def partitioningBy(predicate, downstream=None): - class PartitioningByCollector(Collector): - def __init__(self): - s = lambda: {True: [], False: []} - a = lambda m, item: ( - m[True].append(item) if predicate(item) else m[False].append(item) - ) or m - c = lambda m1, m2: { - True: m1[True] + m2[True], - False: m1[False] + m2[False] - } - super().__init__(s, a, c) - return PartitioningByCollector() - - @staticmethod - def toMap(key_mapper, value_mapper, merge_function=None, map_supplier=None): - class ToMapCollector(Collector): - def __init__(self): - s = map_supplier or dict - a = lambda m, item: ( - m.__setitem__(key_mapper(item), value_mapper(item)) or m - ) - c = lambda m1, m2: {**m1, **m2} - super().__init__(s, a, c) - return ToMapCollector() - - -# ============================================================ -# StreamSupport -# ============================================================ - -class StreamSupport(Stub): - """java.util.stream.StreamSupport""" - @staticmethod - def stream(spliterator, parallel=False): - return Stream([]) - - @staticmethod - def intStream(spliterator, parallel=False): - return IntStream([]) - - @staticmethod - def longStream(spliterator, parallel=False): - return LongStream([]) - - @staticmethod - def doubleStream(spliterator, parallel=False): - return DoubleStream([]) - - -# ============================================================ -# Spliterator -# ============================================================ - -class Spliterator(Stub): - """java.util.Spliterator""" - def __init__(self, data): - super().__init__() - self._data = list(data) - self._index = 0 - - def tryAdvance(self, action): - if self._index < len(self._data): - action.accept(self._data[self._index]) - self._index += 1 - return True - return False - - def forEachRemaining(self, action): - while self._index < len(self._data): - action.accept(self._data[self._index]) - self._index += 1 - - def estimateSize(self): - return len(self._data) - self._index - - def getExactSizeIfKnown(self): - return self.estimateSize() - - def characteristics(self): - return 0 - - ORDERED = 16 - DISTINCT = 1 - SORTED = 4 - SIZED = 64 - NONNULL = 256 - IMMUTABLE = 1024 - CONCURRENT = 4096 - SUBSIZED = 16384 - - -# ============================================================ -# Summary Statistics -# ============================================================ - -class IntSummaryStatistics(Stub): - def __init__(self, data=None): - super().__init__() - self._data = list(data) if data else [] - - def getCount(self): - return len(self._data) - - def getSum(self): - return sum(self._data) - - def getMin(self): - return min(self._data) if self._data else 0 - - def getMax(self): - return max(self._data) if self._data else 0 - - def getAverage(self): - return sum(self._data) / len(self._data) if self._data else 0.0 - - def accept(self, value): - self._data.append(int(value)) - - def combine(self, other): - self._data.extend(other._data) - - -class LongSummaryStatistics(Stub): - def __init__(self, data=None): - super().__init__() - self._data = list(data) if data else [] - - def getCount(self): - return len(self._data) - - def getSum(self): - return sum(self._data) - - def getMin(self): - return min(self._data) if self._data else 0 - - def getMax(self): - return max(self._data) if self._data else 0 - - def getAverage(self): - return sum(self._data) / len(self._data) if self._data else 0.0 - - def accept(self, value): - self._data.append(int(value)) - - def combine(self, other): - self._data.extend(other._data) - - -class DoubleSummaryStatistics(Stub): - def __init__(self, data=None): - super().__init__() - self._data = list(data) if data else [] - - def getCount(self): - return len(self._data) - - def getSum(self): - return sum(self._data) - - def getMin(self): - return min(self._data) if self._data else 0.0 - - def getMax(self): - return max(self._data) if self._data else 0.0 - - def getAverage(self): - return sum(self._data) / len(self._data) if self._data else 0.0 - - def accept(self, value): - self._data.append(float(value)) - - def combine(self, other): - self._data.extend(other._data) - - -# ============================================================ -# Pipeline / Operations stubs -# ============================================================ - -class AbstractPipeline(BaseStream): - """java.util.stream.AbstractPipeline""" - pass - - -class ReferencePipeline(AbstractPipeline): - """java.util.stream.ReferencePipeline""" - pass - - -class IntPipeline(AbstractPipeline): - """java.util.stream.IntPipeline""" - pass - - -class LongPipeline(AbstractPipeline): - """java.util.stream.LongPipeline""" - pass - - -class DoublePipeline(AbstractPipeline): - """java.util.stream.DoublePipeline""" - pass - - -class Sink(Stub): - """java.util.stream.Sink""" - def begin(self, size): - pass - - def end(self): - pass - - def cancellationRequested(self): - return False - - def accept(self, t): - pass - - -class TerminalOp(Stub): - """java.util.stream.TerminalOp""" - pass - - -class TerminalSink(Sink): - """java.util.stream.TerminalSink""" - def get(self): - return None - - -class SliceOps(Stub): - """java.util.stream.SliceOps""" - pass - - -class WhileOps(Stub): - """java.util.stream.WhileOps""" - pass - - -class DistinctOps(Stub): - """java.util.stream.DistinctOps""" - pass - - -class SortedOps(Stub): - """java.util.stream.SortedOps""" - pass - - -class FindOps(Stub): - """java.util.stream.FindOps""" - pass - - -class MatchOps(Stub): - """java.util.stream.MatchOps""" - pass - - -class ReduceOps(Stub): - """java.util.stream.ReduceOps""" - pass - - -class ForEachOps(Stub): - """java.util.stream.ForEachOps""" - pass - - -class MapOps(Stub): - """java.util.stream.MapOps""" - pass - - -class FilterOps(Stub): - """java.util.stream.FilterOps""" - pass - - -class FlatMapOps(Stub): - """java.util.stream.FlatMapOps""" - pass - - -class PeekOps(Stub): - """java.util.stream.PeekOps""" - pass \ No newline at end of file diff --git a/android_pyapi/java/util_zip.py b/android_pyapi/java/util_zip.py deleted file mode 100644 index 034616c..0000000 --- a/android_pyapi/java/util_zip.py +++ /dev/null @@ -1,697 +0,0 @@ -""" -Java zip standard library APIs implemented in Python. -java.util.zip package - ZipFile, ZipEntry, GZIP, CRC, Deflater, Inflater. -""" - -from .._core import Stub -from .io import IOException, Closeable, Flushable -import zipfile as _zipfile -import gzip as _gzip -import zlib as _zlib -import io as _pyio -import os as _os -from datetime import datetime as _datetime - -__all__ = [ - "ZipFile", "ZipEntry", "ZipOutputStream", "ZipInputStream", - "GZIPInputStream", "GZIPOutputStream", - "CRC32", "CheckedInputStream", "CheckedOutputStream", - "Adler32", - "DataFormatException", "ZipException", - "Deflater", "Inflater", - "DeflaterOutputStream", "InflaterInputStream", -] - - -# ============================================================ -# Exception classes -# ============================================================ - -class ZipException(IOException): - pass - -class DataFormatException(Exception): - pass - - -# ============================================================ -# CRC32 -# ============================================================ - -class CRC32(Stub): - def __init__(self): - super().__init__() - self._value = 0 - - def getValue(self): - return self._value - - def reset(self): - self._value = 0 - - def update(self, b, off=None, length=None): - if isinstance(b, int): - self._value = _zlib.crc32(bytes([b & 0xFF]), self._value) & 0xFFFFFFFF - elif isinstance(b, (bytes, bytearray)): - if off is not None and length is not None: - self._value = _zlib.crc32(bytes(b[off:off+length]), self._value) & 0xFFFFFFFF - else: - self._value = _zlib.crc32(bytes(b), self._value) & 0xFFFFFFFF - - -# ============================================================ -# Adler32 -# ============================================================ - -class Adler32(Stub): - def __init__(self): - super().__init__() - self._value = 1 - - def getValue(self): - return self._value - - def reset(self): - self._value = 1 - - def update(self, b, off=None, length=None): - if isinstance(b, int): - self._value = _zlib.adler32(bytes([b & 0xFF]), self._value) & 0xFFFFFFFF - elif isinstance(b, (bytes, bytearray)): - if off is not None and length is not None: - self._value = _zlib.adler32(bytes(b[off:off+length]), self._value) & 0xFFFFFFFF - else: - self._value = _zlib.adler32(bytes(b), self._value) & 0xFFFFFFFF - - -# ============================================================ -# CheckedInputStream / CheckedOutputStream -# ============================================================ - -class CheckedInputStream(Closeable): - def __init__(self, inp, cksum): - super().__init__() - self._in = inp - self._cksum = cksum - - def read(self, b=None, off=None, length=None): - if b is None: - result = self._in.read() - if result >= 0: - self._cksum.update(result) - return result - if off is None: - off = 0 - if length is None: - length = len(b) - result = self._in.read(b, off, length) - if result > 0: - self._cksum.update(b[off:off+result]) - return result - - def getChecksum(self): - return self._cksum - - def close(self): - self._in.close() - - def skip(self, n): - return self._in.skip(n) - - -class CheckedOutputStream(Closeable, Flushable): - def __init__(self, out, cksum): - super().__init__() - self._out = out - self._cksum = cksum - - def write(self, b, off=None, length=None): - if isinstance(b, int): - self._out.write(b) - self._cksum.update(b) - else: - if off is not None and length is not None: - data = bytes(b[off:off+length]) - else: - data = bytes(b) - self._out.write(data) - self._cksum.update(data) - - def getChecksum(self): - return self._cksum - - def flush(self): - self._out.flush() - - def close(self): - self._out.close() - - -# ============================================================ -# ZipEntry -# ============================================================ - -class ZipEntry(Stub): - STORED = 0 - DEFLATED = 8 - - def __init__(self, name): - super().__init__() - self._name = name - self._method = ZipEntry.DEFLATED - self._size = 0 - self._compressed_size = 0 - self._crc = 0 - self._comment = "" - self._time = 0 - self._extra = b"" - self._is_directory = name.endswith("/") - - def getName(self): - return self._name - - def getMethod(self): - return self._method - - def setMethod(self, method): - self._method = method - - def getSize(self): - return self._size - - def setSize(self, size): - self._size = size - - def getCompressedSize(self): - return self._compressed_size - - def setCompressedSize(self, size): - self._compressed_size = size - - def getCrc(self): - return self._crc - - def setCrc(self, crc): - self._crc = crc - - def getComment(self): - return self._comment - - def setComment(self, comment): - self._comment = comment - - def getTime(self): - return self._time - - def setTime(self, time): - self._time = time - - def getExtra(self): - return self._extra - - def setExtra(self, extra): - self._extra = extra - - def isDirectory(self): - return self._is_directory - - def toString(self): - return self._name - - def __str__(self): - return self._name - - -# ============================================================ -# ZipFile -# ============================================================ - -class ZipFile(Closeable): - OPEN_READ = 1 - OPEN_DELETE = 2 - - def __init__(self, file, mode="r"): - super().__init__() - if isinstance(file, str): - path = file - else: - path = file.getPath() - self._path = path - self._mode = mode - try: - self._zfile = _zipfile.ZipFile(path, mode) - except Exception as e: - raise ZipException(str(e)) - - def entries(self): - return iter(self._zfile.infolist()) - - def getEntry(self, name): - try: - info = self._zfile.getinfo(name) - entry = ZipEntry(info.filename) - entry.setMethod(info.compress_type) - entry.setSize(info.file_size) - entry.setCompressedSize(info.compress_size) - entry.setCrc(info.CRC) - entry.setTime(int(_datetime(*info.date_time).timestamp() * 1000) if info.date_time else 0) - return entry - except KeyError: - return None - - def getInputStream(self, entry): - try: - data = self._zfile.read(entry.getName() if isinstance(entry, ZipEntry) else entry) - from ..io import ByteArrayInputStream - return ByteArrayInputStream(data) - except Exception as e: - raise ZipException(str(e)) - - def close(self): - self._zfile.close() - - def size(self): - return len(self._zfile.namelist()) - - def getName(self): - return self._path - - def getComment(self): - return "" - - -# ============================================================ -# ZipOutputStream -# ============================================================ - -class ZipOutputStream(Closeable, Flushable): - STORED = 0 - DEFLATED = 8 - - def __init__(self, out): - super().__init__() - self._out = out - self._zfile = _zipfile.ZipFile(out, "w", _zipfile.ZIP_DEFLATED) - self._method = ZipOutputStream.DEFLATED - self._level = -1 - self._comment = "" - - def putNextEntry(self, entry): - zi = _zipfile.ZipInfo(entry.getName() if isinstance(entry, ZipEntry) else str(entry)) - if hasattr(entry, 'getTime') and entry.getTime(): - dt = _datetime.fromtimestamp(entry.getTime() / 1000.0) - zi.date_time = dt.timetuple()[:6] - self._zfile.writestr(zi, b"") - - def closeEntry(self): - pass - - def write(self, b, off=None, length=None): - if off is not None and length is not None: - self._zfile._zout.write(bytes(b[off:off+length])) - else: - self._zfile._zout.write(bytes(b) if not isinstance(b, (bytes, bytearray)) else b) - - def finish(self): - self._zfile.close() - - def close(self): - self._zfile.close() - - def setMethod(self, method): - self._method = method - - def setLevel(self, level): - self._level = level - - def setComment(self, comment): - self._comment = comment - - def flush(self): - self._zfile._zout.flush() if hasattr(self._zfile._zout, 'flush') else None - - -# ============================================================ -# ZipInputStream -# ============================================================ - -class ZipInputStream(Closeable): - def __init__(self, inp): - super().__init__() - self._inp = inp - self._zfile = _zipfile.ZipFile(inp) - self._entries = iter(self._zfile.infolist()) - self._current_entry = None - self._current_data = b"" - self._current_pos = 0 - - def getNextEntry(self): - try: - info = next(self._entries) - entry = ZipEntry(info.filename) - entry.setMethod(info.compress_type) - entry.setSize(info.file_size) - entry.setCompressedSize(info.compress_size) - entry.setCrc(info.CRC) - self._current_entry = info - self._current_data = self._zfile.read(info) - self._current_pos = 0 - return entry - except StopIteration: - self._current_entry = None - self._current_data = b"" - return None - - def read(self, b=None, off=None, length=None): - if self._current_entry is None: - return -1 - avail = len(self._current_data) - self._current_pos - if avail <= 0: - return -1 - if b is None: - val = self._current_data[self._current_pos] - self._current_pos += 1 - return val - if off is None: - off = 0 - if length is None: - length = len(b) - n = min(length, avail) - if isinstance(b, bytearray): - b[off:off+n] = self._current_data[self._current_pos:self._current_pos+n] - self._current_pos += n - return n - - def closeEntry(self): - self._current_entry = None - self._current_data = b"" - - def available(self): - if self._current_entry is None: - return 0 - return len(self._current_data) - self._current_pos - - def close(self): - self._zfile.close() - - -# ============================================================ -# GZIPInputStream -# ============================================================ - -class GZIPInputStream(Closeable): - def __init__(self, inp, size=512): - super().__init__() - self._inp = inp - self._gzip_file = _gzip.GzipFile(fileobj=_pyio.BufferedReader(inp) if hasattr(inp, 'read') else inp) - - def read(self, b=None, off=None, length=None): - if b is None: - data = self._gzip_file.read(1) - return data[0] if data else -1 - if off is None: - off = 0 - if length is None: - length = len(b) - data = self._gzip_file.read(length) - if not data: - return -1 - n = len(data) - if isinstance(b, bytearray): - b[off:off+n] = data - else: - ba = bytearray(b) - ba[off:off+n] = data - b[:] = bytes(ba) - return n - - def close(self): - self._gzip_file.close() - - -# ============================================================ -# GZIPOutputStream -# ============================================================ - -class GZIPOutputStream(Closeable, Flushable): - def __init__(self, out, size=512): - super().__init__() - self._out = out - self._gzip_file = _gzip.GzipFile(fileobj=out, mode="wb") - - def write(self, b, off=None, length=None): - if isinstance(b, int): - self._gzip_file.write(bytes([b & 0xFF])) - elif isinstance(b, (bytes, bytearray)): - if off is not None and length is not None: - self._gzip_file.write(bytes(b[off:off+length])) - else: - self._gzip_file.write(bytes(b)) - - def finish(self): - self._gzip_file.close() - - def close(self): - self._gzip_file.close() - - def flush(self): - self._gzip_file.flush() - - -# ============================================================ -# Deflater -# ============================================================ - -class Deflater(Stub): - BEST_COMPRESSION = 9 - BEST_SPEED = 1 - DEFAULT_COMPRESSION = -1 - DEFAULT_STRATEGY = 0 - FILTERED = 1 - HUFFMAN_ONLY = 2 - NO_COMPRESSION = 0 - NO_FLUSH = 0 - SYNC_FLUSH = 2 - FULL_FLUSH = 3 - - def __init__(self, level=-1, nowrap=False): - super().__init__() - self._level = level - self._nowrap = nowrap - self._compress = _zlib.compressobj(level=level if level >= 0 else 6, - method=_zlib.DEFLATED, - wbits=-_zlib.MAX_WBITS if nowrap else _zlib.MAX_WBITS) - self._input_buffer = b"" - self._finished = False - - def setInput(self, data, off=0, length=-1): - if length < 0: - length = len(data) - off - self._input_buffer = bytes(data[off:off+length]) - - def needsInput(self): - return len(self._input_buffer) == 0 - - def setDictionary(self, dictionary): - pass - - def getTotalIn(self): - return len(self._input_buffer) - - def getTotalOut(self): - return 0 - - def deflate(self, output, off=0, length=-1): - if length < 0: - length = len(output) - off - data = self._compress.compress(self._input_buffer) - self._input_buffer = b"" - n = min(len(data), length) - if isinstance(output, bytearray): - output[off:off+n] = data[:n] - else: - ba = bytearray(output) - ba[off:off+n] = data[:n] - output[:] = bytes(ba) - return n - - def finish(self): - self._finished = True - - def finished(self): - return self._finished - - def end(self): - pass - - def getAdler(self): - return 1 - - def reset(self): - self._compress = _zlib.compressobj(level=self._level if self._level >= 0 else 6, - method=_zlib.DEFLATED, - wbits=-_zlib.MAX_WBITS if self._nowrap else _zlib.MAX_WBITS) - self._input_buffer = b"" - self._finished = False - - -# ============================================================ -# Inflater -# ============================================================ - -class Inflater(Stub): - def __init__(self, nowrap=False): - super().__init__() - self._nowrap = nowrap - wbits = -_zlib.MAX_WBITS if nowrap else _zlib.MAX_WBITS - self._decompress = _zlib.decompressobj(wbits) - self._input_buffer = b"" - self._finished = False - - def setInput(self, data, off=0, length=-1): - if length < 0: - length = len(data) - off - self._input_buffer = bytes(data[off:off+length]) - - def needsInput(self): - return len(self._input_buffer) == 0 - - def setDictionary(self, dictionary): - pass - - def getTotalIn(self): - return len(self._input_buffer) - - def getTotalOut(self): - return 0 - - def inflate(self, output, off=0, length=-1): - if length < 0: - length = len(output) - off - try: - data = self._decompress.decompress(self._input_buffer) - self._input_buffer = b"" - n = min(len(data), length) - if isinstance(output, bytearray): - output[off:off+n] = data[:n] - else: - ba = bytearray(output) - ba[off:off+n] = data[:n] - output[:] = bytes(ba) - return n - except _zlib.error as e: - raise DataFormatException(str(e)) - - def finished(self): - return self._finished - - def end(self): - pass - - def getAdler(self): - return 1 - - def needsDictionary(self): - return False - - def reset(self): - wbits = -_zlib.MAX_WBITS if self._nowrap else _zlib.MAX_WBITS - self._decompress = _zlib.decompressobj(wbits) - self._input_buffer = b"" - self._finished = False - - -# ============================================================ -# DeflaterOutputStream -# ============================================================ - -class DeflaterOutputStream(Closeable, Flushable): - def __init__(self, out, deflater=None, size=512): - super().__init__() - self._out = out - self._deflater = deflater or Deflater() - self._buffer = bytearray(size) - - def write(self, b, off=None, length=None): - if isinstance(b, int): - self._out.write(bytes([b & 0xFF])) - else: - if off is not None and length is not None: - self._out.write(bytes(b[off:off+length])) - else: - self._deflater.setInput(bytes(b) if isinstance(b, (bytes, bytearray)) else bytes(b)) - while not self._deflater.needsInput(): - n = self._deflater.deflate(self._buffer) - if n > 0: - self._out.write(bytes(self._buffer[:n])) - - def finish(self): - self._deflater.finish() - while not self._deflater.finished(): - n = self._deflater.deflate(self._buffer) - if n > 0: - self._out.write(bytes(self._buffer[:n])) - - def close(self): - self.finish() - self._deflater.end() - self._out.close() - - def flush(self): - self._out.flush() - - -# ============================================================ -# InflaterInputStream -# ============================================================ - -class InflaterInputStream(Closeable): - def __init__(self, inp, inflater=None, size=512): - super().__init__() - self._in = inp - self._inflater = inflater or Inflater() - self._buffer = bytearray(size) - - def read(self, b=None, off=None, length=None): - if b is None: - buf = bytearray(1) - n = self._read_from_inflater(buf, 0, 1) - return buf[0] if n > 0 else -1 - if off is None: - off = 0 - if length is None: - length = len(b) - return self._read_from_inflater(b, off, length) - - def _read_from_inflater(self, b, off, length): - while not self._inflater.finished(): - if self._inflater.needsInput(): - data = self._in.read(self._buffer) - if data is None or data == -1 or data == 0: - break - if isinstance(data, int): - break - self._inflater.setInput(data) - try: - n = self._inflater.inflate(b, off, length) - if n > 0: - return n - except DataFormatException: - break - return -1 - - def available(self): - return 0 - - def close(self): - self._inflater.end() - self._in.close() - - def mark(self, readlimit): - pass - - def reset(self): - raise IOException("mark/reset not supported") - - def markSupported(self): - return False \ No newline at end of file diff --git a/android_pyapi/javax/__init__.py b/android_pyapi/javax/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/android_pyapi/javax/crypto.py b/android_pyapi/javax/crypto.py deleted file mode 100644 index 4b02b7b..0000000 --- a/android_pyapi/javax/crypto.py +++ /dev/null @@ -1,1031 +0,0 @@ -""" -Java javax.crypto APIs implemented in Python. -javax.crypto package - Cipher, KeyGenerator, Mac, KeyAgreement, SecretKey, etc. -Wraps Python cryptography library. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -from typing import List, Optional, Any - -# Optional cryptography dependency -try: - from cryptography.fernet import Fernet as _Fernet - from cryptography.hazmat.primitives.ciphers import Cipher as _Cipher, algorithms as _algorithms, modes as _modes - from cryptography.hazmat.primitives import hashes as _hashes, hmac as _hmac, padding as _padding - from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC as _PBKDF2HMAC - HAS_CRYPTOGRAPHY = True -except ImportError: - HAS_CRYPTOGRAPHY = False - -__all__ = [ - "Cipher", "KeyGenerator", "SecretKey", "SecretKeySpec", - "Mac", "KeyAgreement", "ExemptionMechanism", - "EncryptedPrivateKeyInfo", "SealedObject", "NullCipher", - "PBEParameterSpec", "IvParameterSpec", "OAEPParameterSpec", - "PSource", "MGF1ParameterSpec", "DESedeKeySpec", "DESKeySpec", - "SecretKeyFactory", "CipherInputStream", "CipherOutputStream", - "BadPaddingException", "IllegalBlockSizeException", - "NoSuchPaddingException", "ShortBufferException", -] - - -# ============================================================ -# Exceptions -# ============================================================ - -class BadPaddingException(Exception, Stub): - def __init__(self, message: str = None): - super().__init__(message or "Bad padding") - self._message = message - - def toString(self) -> str: - return f"BadPaddingException: {self._message}" if self._message else "BadPaddingException" - - def equals(self, other) -> bool: - return isinstance(other, BadPaddingException) and self._message == other._message - - def hashCode(self) -> int: - return hash(self._message) & 0x7FFFFFFF - - -class IllegalBlockSizeException(Exception, Stub): - def __init__(self, message: str = None): - super().__init__(message or "Illegal block size") - self._message = message - - def toString(self) -> str: - return f"IllegalBlockSizeException: {self._message}" if self._message else "IllegalBlockSizeException" - - def equals(self, other) -> bool: - return isinstance(other, IllegalBlockSizeException) and self._message == other._message - - def hashCode(self) -> int: - return hash(self._message) & 0x7FFFFFFF - - -class NoSuchPaddingException(Exception, Stub): - def __init__(self, message: str = None): - super().__init__(message or "No such padding") - self._message = message - - def toString(self) -> str: - return f"NoSuchPaddingException: {self._message}" if self._message else "NoSuchPaddingException" - - def equals(self, other) -> bool: - return isinstance(other, NoSuchPaddingException) and self._message == other._message - - def hashCode(self) -> int: - return hash(self._message) & 0x7FFFFFFF - - -class ShortBufferException(Exception, Stub): - def __init__(self, message: str = None): - super().__init__(message or "Output buffer too short") - self._message = message - - def toString(self) -> str: - return f"ShortBufferException: {self._message}" if self._message else "ShortBufferException" - - def equals(self, other) -> bool: - return isinstance(other, ShortBufferException) and self._message == other._message - - def hashCode(self) -> int: - return hash(self._message) & 0x7FFFFFFF - - -# ============================================================ -# SecretKey -# ============================================================ - -class SecretKey(Stub): - """ - javax.crypto.SecretKey - A secret (symmetric) key. - """ - serialVersionUID: int = -4795878709 - - def __init__(self, encoded: bytes = None, algorithm: str = None): - super().__init__() - self._encoded = encoded or b'' - self._algorithm = algorithm or "RAW" - - def getAlgorithm(self) -> str: - return self._algorithm - - def getFormat(self) -> str: - return "RAW" - - def getEncoded(self) -> bytes: - return self._encoded - - def toString(self) -> str: - return f"SecretKey@{hex(id(self))}[algorithm={self._algorithm}]" - - def equals(self, other) -> bool: - if isinstance(other, SecretKey): - return self._encoded == other._encoded and self._algorithm == other._algorithm - return False - - def hashCode(self) -> int: - return hash((bytes(self._encoded), self._algorithm)) & 0x7FFFFFFF - - -# ============================================================ -# SecretKeySpec -# ============================================================ - -class SecretKeySpec(SecretKey): - """ - javax.crypto.spec.SecretKeySpec - A secret key specification. - """ - - def __init__(self, key: bytes, algorithm: str): - super().__init__(key, algorithm) - self._key = key - - def getAlgorithm(self) -> str: - return self._algorithm - - def getFormat(self) -> str: - return "RAW" - - def getEncoded(self) -> bytes: - return self._key - - def toString(self) -> str: - return f"SecretKeySpec@{hex(id(self))}[algorithm={self._algorithm}]" - - def equals(self, other) -> bool: - if isinstance(other, SecretKeySpec): - return self._key == other._key and self._algorithm == other._algorithm - return False - - def hashCode(self) -> int: - return hash((bytes(self._key), self._algorithm)) & 0x7FFFFFFF - - -# ============================================================ -# Cipher -# ============================================================ - -class Cipher(Stub): - """ - javax.crypto.Cipher - Engine for encryption/decryption. - """ - ENCRYPT_MODE: int = 1 - DECRYPT_MODE: int = 2 - WRAP_MODE: int = 3 - UNWRAP_MODE: int = 4 - PUBLIC_KEY: int = 1 - PRIVATE_KEY: int = 2 - SECRET_KEY: int = 3 - - def __init__(self, transformation: str = None): - super().__init__() - self._transformation = transformation - self._opmode = None - self._key = None - self._iv = None - self._provider = None - - @staticmethod - def getInstance(transformation: str) -> "Cipher": - return Cipher(transformation) - - def init(self, opmode: int, key: Any, params: Any = None, random: Any = None): - """Initializes this cipher with a key and optional parameters.""" - self._opmode = opmode - self._key = key - if params and hasattr(params, 'getIV'): - self._iv = params.getIV() - - def doFinal(self, input_data: bytes = None) -> bytes: - """Performs the final encryption/decryption operation.""" - if input_data is None: - return b'' - if not HAS_CRYPTOGRAPHY: - # Fallback simple XOR for demonstration - if self._key and hasattr(self._key, 'getEncoded'): - key_bytes = self._key.getEncoded() - if key_bytes: - result = bytearray() - for i, b in enumerate(input_data): - result.append(b ^ key_bytes[i % len(key_bytes)]) - return bytes(result) - return input_data - - # Try using cryptography library - try: - if self._key and hasattr(self._key, 'getEncoded'): - key_bytes = self._key.getEncoded() - if self._transformation and "AES" in self._transformation.upper(): - if len(key_bytes) in (16, 24, 32): - if self._opmode == Cipher.ENCRYPT_MODE: - from cryptography.hazmat.primitives.ciphers import Cipher as _CryptCipher, algorithms as _alg, modes as _mod - iv = self._iv or b'\x00' * 16 - c = _CryptCipher(_alg.AES(key_bytes), _mod.CBC(iv)) - encryptor = c.encryptor() - padder = _padding.PKCS7(128).padder() - padded = padder.update(input_data) + padder.finalize() - return encryptor.update(padded) + encryptor.finalize() - elif self._opmode == Cipher.DECRYPT_MODE: - iv = self._iv or b'\x00' * 16 - c = _CryptCipher(_alg.AES(key_bytes), _mod.CBC(iv)) - decryptor = c.decryptor() - padded = decryptor.update(input_data) + decryptor.finalize() - unpadder = _padding.PKCS7(128).unpadder() - return unpadder.update(padded) + unpadder.finalize() - except Exception: - pass - return input_data - - def update(self, input_data: bytes) -> bytes: - """Continues a multi-part encryption/decryption operation.""" - return input_data - - def wrap(self, key: Any) -> bytes: - """Wraps a key (stub).""" - if isinstance(key, SecretKey): - return key.getEncoded() - return b'' - - def unwrap(self, wrapped_key: bytes, wrapped_key_algorithm: str, wrapped_key_type: int) -> Any: - """Unwraps a key (stub).""" - return SecretKey(wrapped_key, wrapped_key_algorithm) - - def getBlockSize(self) -> int: - """Returns the block size (bytes).""" - if self._transformation and "AES" in self._transformation.upper(): - return 16 - if self._transformation and "DES" in self._transformation.upper(): - return 8 - return 0 - - def getIV(self) -> Optional[bytes]: - return self._iv - - def getParameters(self): - return None - - def getAlgorithm(self) -> str: - return self._transformation or "NONE" - - def getProvider(self): - return self._provider - - def getExemptionMechanism(self): - return None - - def getOutputSize(self, input_len: int) -> int: - """Returns the output buffer size for a given input length.""" - bs = self.getBlockSize() - if bs > 0: - return input_len + (bs - input_len % bs) - return input_len - - @staticmethod - def getMaxAllowedKeyLength(transformation: str) -> int: - """Returns the maximum key length for the given transformation.""" - return 2147483647 # Integer.MAX_VALUE - - def toString(self) -> str: - return f"Cipher@{hex(id(self))}[algorithm={self._transformation}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# NullCipher -# ============================================================ - -class NullCipher(Cipher): - """ - javax.crypto.NullCipher - A cipher that performs no transformation (identity). - """ - def __init__(self): - super().__init__("NULL") - - def getBlockSize(self) -> int: - return 1 - - def getOutputSize(self, input_len: int) -> int: - return input_len - - def getIV(self) -> Optional[bytes]: - return None - - def getParameters(self): - return None - - def getExemptionMechanism(self): - return None - - def update(self, input_data: bytes) -> bytes: - return input_data - - def doFinal(self, input_data: bytes = None) -> bytes: - return input_data if input_data else b'' - - def toString(self) -> str: - return "NullCipher" - - -# ============================================================ -# KeyGenerator -# ============================================================ - -class KeyGenerator(Stub): - """ - javax.crypto.KeyGenerator - Key generator for symmetric algorithms. - """ - - def __init__(self, algorithm: str = None): - super().__init__() - self._algorithm = algorithm or "AES" - self._provider = None - self._key_size = 128 - - @staticmethod - def getInstance(algorithm: str) -> "KeyGenerator": - return KeyGenerator(algorithm) - - def init(self, key_size_or_random: Any, random: Any = None): - """Initializes the key generator.""" - if isinstance(key_size_or_random, int): - self._key_size = key_size_or_random - elif key_size_or_random is not None and hasattr(key_size_or_random, 'nextBytes'): - pass - - def generateKey(self) -> SecretKey: - """Generates a secret key.""" - import os - key_bytes = os.urandom(self._key_size // 8) - return SecretKey(key_bytes, self._algorithm) - - def getAlgorithm(self) -> str: - return self._algorithm - - def getProvider(self): - return self._provider - - def toString(self) -> str: - return f"KeyGenerator@{hex(id(self))}[algorithm={self._algorithm}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Mac -# ============================================================ - -class Mac(Stub): - """ - javax.crypto.Mac - Message Authentication Code engine. - """ - - def __init__(self, algorithm: str = None): - super().__init__() - self._algorithm = algorithm or "HmacSHA256" - self._provider = None - self._key = None - self._hmac = None - self._initialized = False - - @staticmethod - def getInstance(algorithm: str) -> "Mac": - return Mac(algorithm) - - def init(self, key: Any, params: Any = None): - """Initializes the MAC with a key.""" - self._key = key - self._initialized = True - if HAS_CRYPTOGRAPHY and key and hasattr(key, 'getEncoded'): - key_bytes = key.getEncoded() - hash_algo = _hashes.SHA256() - self._hmac = _hmac.HMAC(key_bytes, hash_algo) - - def update(self, input_data: bytes): - """Processes the given byte array.""" - if self._hmac: - self._hmac.update(input_data) - - def doFinal(self, input_data: bytes = None) -> bytes: - """Finishes the MAC operation.""" - if self._hmac: - if input_data: - self._hmac.update(input_data) - return self._hmac.finalize() - if input_data: - import hashlib - return hashlib.sha256(input_data).digest() - return b'\x00' * 32 - - def getMacLength(self) -> int: - """Returns the output length of the MAC.""" - return 32 # SHA-256 - - def getAlgorithm(self) -> str: - return self._algorithm - - def getProvider(self): - return self._provider - - def reset(self): - """Resets the MAC.""" - if self._key and hasattr(self._key, 'getEncoded') and HAS_CRYPTOGRAPHY: - key_bytes = self._key.getEncoded() - hash_algo = _hashes.SHA256() - self._hmac = _hmac.HMAC(key_bytes, hash_algo) - - def clone(self): - return Mac(self._algorithm) - - def toString(self) -> str: - return f"Mac@{hex(id(self))}[algorithm={self._algorithm}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# KeyAgreement -# ============================================================ - -class KeyAgreement(Stub): - """ - javax.crypto.KeyAgreement - Key agreement protocol engine (e.g., Diffie-Hellman). - """ - - def __init__(self, algorithm: str = None): - super().__init__() - self._algorithm = algorithm or "DH" - self._provider = None - - @staticmethod - def getInstance(algorithm: str) -> "KeyAgreement": - return KeyAgreement(algorithm) - - def init(self, key: Any, params: Any = None): - """Initializes this key agreement with a key.""" - pass - - def doPhase(self, key: Any, last_phase: bool) -> Any: - """Executes the next phase of the key agreement.""" - return self - - def generateSecret(self, shared_secret: Any = None) -> bytes: - """Generates the shared secret.""" - import os - return os.urandom(32) - - def getAlgorithm(self) -> str: - return self._algorithm - - def getProvider(self): - return self._provider - - def toString(self) -> str: - return f"KeyAgreement@{hex(id(self))}[algorithm={self._algorithm}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# ExemptionMechanism -# ============================================================ - -class ExemptionMechanism(Stub): - """ - javax.crypto.ExemptionMechanism - Exemption mechanism for key recovery. - """ - - def __init__(self, algorithm: str = None): - super().__init__() - self._algorithm = algorithm or "KeyRecovery" - self._initialized = False - - @staticmethod - def getInstance(algorithm: str) -> "ExemptionMechanism": - return ExemptionMechanism(algorithm) - - def init(self, key: Any): - self._initialized = True - - def genExemptionBlob(self, input_data: bytes = None) -> bytes: - return b'\x00' * 32 - - def isCryptoAllowed(self, key: Any) -> bool: - return True - - def getOutputSize(self, input_len: int) -> int: - return input_len - - def getAlgorithm(self) -> str: - return self._algorithm - - def getProvider(self): - return None - - def getName(self) -> str: - return self._algorithm - - def toString(self) -> str: - return f"ExemptionMechanism@{hex(id(self))}[algorithm={self._algorithm}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# EncryptedPrivateKeyInfo -# ============================================================ - -class EncryptedPrivateKeyInfo(Stub): - """ - javax.crypto.EncryptedPrivateKeyInfo - Represents encrypted private key info from PKCS #8. - """ - - def __init__(self, alg_params=None, encrypted_data: bytes = None): - super().__init__() - self._alg_params = alg_params - self._encrypted_data = encrypted_data or b'' - - def getAlgParameters(self): - return self._alg_params - - def getEncryptedData(self) -> bytes: - return self._encrypted_data - - def getEncoded(self) -> bytes: - return self._encrypted_data - - def getName(self) -> str: - return "PKCS#8" - - def toString(self) -> str: - return f"EncryptedPrivateKeyInfo@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# SealedObject -# ============================================================ - -class SealedObject(Stub): - """ - javax.crypto.SealedObject - Enables protection of a serializable object using encryption. - """ - - def __init__(self, obj: Any = None, cipher: Cipher = None): - super().__init__() - self._encrypted_data = b'' - self._algorithm = "NONE" - if cipher and obj is not None: - import pickle - serialized = pickle.dumps(obj) - self._encrypted_data = cipher.doFinal(serialized) - self._algorithm = cipher.getAlgorithm() - - def getObject(self, cipher_or_key: Any = None) -> Any: - """Unseals the object.""" - import pickle - try: - if isinstance(cipher_or_key, Cipher): - decrypted = cipher_or_key.doFinal(self._encrypted_data) - else: - decrypted = self._encrypted_data - return pickle.loads(decrypted) - except Exception: - return None - - def getAlgorithm(self) -> str: - return self._algorithm - - def getEncoded(self) -> bytes: - return self._encrypted_data - - def toString(self) -> str: - return f"SealedObject@{hex(id(self))}[algorithm={self._algorithm}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# ParameterSpec classes -# ============================================================ - -class IvParameterSpec(Stub): - """ - javax.crypto.spec.IvParameterSpec - Initialization vector specification. - """ - def __init__(self, iv: bytes): - super().__init__() - self._iv = iv - - def getIV(self) -> bytes: - return self._iv - - def toString(self) -> str: - return f"IvParameterSpec@{hex(id(self))}[iv={self._iv.hex()[:16]}...]" - - -class PBEParameterSpec(Stub): - """ - javax.crypto.spec.PBEParameterSpec - Password-based encryption parameter specification. - """ - def __init__(self, salt: bytes, iteration_count: int): - super().__init__() - self._salt = salt - self._iteration_count = iteration_count - - def getSalt(self) -> bytes: - return self._salt - - def getIterationCount(self) -> int: - return self._iteration_count - - def toString(self) -> str: - return f"PBEParameterSpec@{hex(id(self))}[iterations={self._iteration_count}]" - - -class PSource(Stub): - """ - javax.crypto.spec.PSource - Source of the label for OAEP padding. - """ - class PSpecified(Stub): - DEFAULT: "PSource.PSpecified" = None - def __init__(self, value: bytes = b''): - super().__init__() - self._value = value - def getValue(self) -> bytes: - return self._value - def toString(self) -> str: - return "PSpecified" - - def __init__(self, algorithm: str = None): - super().__init__() - self._algorithm = algorithm - - def getAlgorithm(self) -> str: - return self._algorithm - - def toString(self) -> str: - return f"PSource.{self._algorithm}" if self._algorithm else "PSource" - - def equals(self, other) -> bool: - if isinstance(other, PSource): - return self._algorithm == other._algorithm - return False - - def hashCode(self) -> int: - return hash(self._algorithm) & 0x7FFFFFFF - - -PSource.PSpecified.DEFAULT = PSource.PSpecified(b'') - - -class OAEPParameterSpec(Stub): - """ - javax.crypto.spec.OAEPParameterSpec - OAEP padding parameter specification. - """ - DEFAULT: "OAEPParameterSpec" = None - - def __init__(self, digest_algorithm: str = "SHA-256", - mgf_algorithm: str = "MGF1", - mgf_parameters: Any = None, - p_source: PSource = None): - super().__init__() - self._digest_algorithm = digest_algorithm - self._mgf_algorithm = mgf_algorithm - self._mgf_parameters = mgf_parameters - self._p_source = p_source or PSource.PSpecified.DEFAULT - - def getDigestAlgorithm(self) -> str: - return self._digest_algorithm - - def getMGFAlgorithm(self) -> str: - return self._mgf_algorithm - - def getMGFParameters(self): - return self._mgf_parameters - - def getPSource(self) -> PSource: - return self._p_source - - def getPSourceAlgorithm(self) -> str: - return self._p_source.getAlgorithm() if self._p_source else "" - - def toString(self) -> str: - return f"OAEPParameterSpec@{hex(id(self))}[digest={self._digest_algorithm}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -OAEPParameterSpec.DEFAULT = OAEPParameterSpec() - - -class MGF1ParameterSpec(Stub): - """ - javax.crypto.spec.MGF1ParameterSpec - MGF1 parameter specification. - """ - SHA1: "MGF1ParameterSpec" = None - SHA224: "MGF1ParameterSpec" = None - SHA256: "MGF1ParameterSpec" = None - SHA384: "MGF1ParameterSpec" = None - SHA512: "MGF1ParameterSpec" = None - SHA512_224: "MGF1ParameterSpec" = None - SHA512_256: "MGF1ParameterSpec" = None - SHA3_224: "MGF1ParameterSpec" = None - SHA3_256: "MGF1ParameterSpec" = None - SHA3_384: "MGF1ParameterSpec" = None - SHA3_512: "MGF1ParameterSpec" = None - - def __init__(self, digest_algorithm: str = "SHA-1"): - super().__init__() - self._digest_algorithm = digest_algorithm - - def getDigestAlgorithm(self) -> str: - return self._digest_algorithm - - def toString(self) -> str: - return f"MGF1ParameterSpec[{self._digest_algorithm}]" - - def equals(self, other) -> bool: - if isinstance(other, MGF1ParameterSpec): - return self._digest_algorithm == other._digest_algorithm - return False - - def hashCode(self) -> int: - return hash(self._digest_algorithm) & 0x7FFFFFFF - - -MGF1ParameterSpec.SHA1 = MGF1ParameterSpec("SHA-1") -MGF1ParameterSpec.SHA224 = MGF1ParameterSpec("SHA-224") -MGF1ParameterSpec.SHA256 = MGF1ParameterSpec("SHA-256") -MGF1ParameterSpec.SHA384 = MGF1ParameterSpec("SHA-384") -MGF1ParameterSpec.SHA512 = MGF1ParameterSpec("SHA-512") -MGF1ParameterSpec.SHA512_224 = MGF1ParameterSpec("SHA-512/224") -MGF1ParameterSpec.SHA512_256 = MGF1ParameterSpec("SHA-512/256") -MGF1ParameterSpec.SHA3_224 = MGF1ParameterSpec("SHA3-224") -MGF1ParameterSpec.SHA3_256 = MGF1ParameterSpec("SHA3-256") -MGF1ParameterSpec.SHA3_384 = MGF1ParameterSpec("SHA3-384") -MGF1ParameterSpec.SHA3_512 = MGF1ParameterSpec("SHA3-512") - - -class DESedeKeySpec(Stub): - """ - javax.crypto.spec.DESedeKeySpec - Key specification for DES-EDE (Triple DES) keys. - """ - DES_EDE_KEY_LEN: int = 24 - - def __init__(self, key: bytes): - super().__init__() - self._key = key - - def getKey(self) -> bytes: - return self._key - - @staticmethod - def isParityAdjusted(key: bytes) -> bool: - return True - - def toString(self) -> str: - return f"DESedeKeySpec@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -class DESKeySpec(Stub): - """ - javax.crypto.spec.DESKeySpec - Key specification for DES keys. - """ - DES_KEY_LEN: int = 8 - - def __init__(self, key: bytes): - super().__init__() - self._key = key - - def getKey(self) -> bytes: - return self._key - - @staticmethod - def isParityAdjusted(key: bytes) -> bool: - return True - - @staticmethod - def isWeak(key: bytes) -> bool: - return False - - def toString(self) -> str: - return f"DESKeySpec@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# SecretKeyFactory -# ============================================================ - -class SecretKeyFactory(Stub): - """ - javax.crypto.SecretKeyFactory - Factory for converting between opaque and transparent keys. - """ - - def __init__(self, algorithm: str = None): - super().__init__() - self._algorithm = algorithm or "AES" - self._provider = None - - @staticmethod - def getInstance(algorithm: str) -> "SecretKeyFactory": - return SecretKeyFactory(algorithm) - - def translateKey(self, key: SecretKey) -> SecretKey: - return key - - def generateSecret(self, key_spec: Any) -> SecretKey: - if isinstance(key_spec, SecretKeySpec): - return key_spec - return SecretKey(b'', self._algorithm) - - def getKeySpec(self, key: SecretKey, key_spec_cls: type) -> Any: - if key_spec_cls == SecretKeySpec: - return SecretKeySpec(key.getEncoded(), key.getAlgorithm()) - return key - - def getAlgorithm(self) -> str: - return self._algorithm - - def getProvider(self): - return self._provider - - def toString(self) -> str: - return f"SecretKeyFactory@{hex(id(self))}[algorithm={self._algorithm}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# CipherInputStream -# ============================================================ - -class CipherInputStream(Stub): - """ - javax.crypto.CipherInputStream - A CipherInputStream is composed of an InputStream and a Cipher. - """ - def __init__(self, istream, cipher: Cipher): - super().__init__() - self._istream = istream - self._cipher = cipher - self._buffer = b'' - - def read(self, b: bytearray = None, off: int = 0, le: int = 0) -> int: - data = self._istream.read(le) if le else self._istream.read() - if not data: - return -1 - decrypted = self._cipher.update(data) - self._buffer += decrypted - if b is not None and le > 0: - n = min(le, len(self._buffer)) - b[off:off + n] = self._buffer[:n] - self._buffer = self._buffer[n:] - return n - return len(data) - - def available(self) -> int: - return len(self._buffer) - - def skip(self, n: int) -> int: - self._buffer = self._buffer[n:] - return n - - def close(self): - if hasattr(self._istream, 'close'): - self._istream.close() - - def markSupported(self) -> bool: - return False - - def mark(self, read_limit: int): - pass - - def reset(self): - pass - - def toString(self) -> str: - return f"CipherInputStream@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# CipherOutputStream -# ============================================================ - -class CipherOutputStream(Stub): - """ - javax.crypto.CipherOutputStream - A CipherOutputStream is composed of an OutputStream and a Cipher. - """ - def __init__(self, ostream, cipher: Cipher): - super().__init__() - self._ostream = ostream - self._cipher = cipher - - def write(self, b: Any, off: int = None, le: int = None): - if isinstance(b, int): - data = bytes([b]) - elif off is not None and le is not None: - data = bytes(b[off:off + le]) - else: - data = bytes(b) if not isinstance(b, bytes) else b - encrypted = self._cipher.update(data) - if hasattr(self._ostream, 'write'): - self._ostream.write(encrypted) - - def flush(self): - if hasattr(self._ostream, 'flush'): - self._ostream.flush() - - def close(self): - final = self._cipher.doFinal() - if final and hasattr(self._ostream, 'write'): - self._ostream.write(final) - if hasattr(self._ostream, 'close'): - self._ostream.close() - - def toString(self) -> str: - return f"CipherOutputStream@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/javax/net.py b/android_pyapi/javax/net.py deleted file mode 100644 index 29dc880..0000000 --- a/android_pyapi/javax/net.py +++ /dev/null @@ -1,785 +0,0 @@ -""" -Java javax.net APIs implemented in Python. -javax.net package - SocketFactory, SSLServerSocket, SSLSocket, SSLContext, etc. -Wraps Python ssl module. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -import ssl as _ssl -import socket as _socket -import threading as _threading -from typing import List, Optional - -__all__ = [ - "ServerSocketFactory", "SocketFactory", "SSLServerSocketFactory", - "SSLSocketFactory", "SSLServerSocket", "SSLSocket", - "HandshakeCompletedEvent", "HandshakeCompletedListener", - "HostnameVerifier", "HttpsURLConnection", - "TrustManager", "X509TrustManager", "KeyManager", "X509KeyManager", - "TrustManagerFactory", "KeyManagerFactory", "SSLContext", -] - - -# ============================================================ -# SocketFactory -# ============================================================ - -class SocketFactory(Stub): - """ - javax.net.SocketFactory - Factory for creating sockets. - """ - - _default = None - - @classmethod - def getDefault(cls) -> "SocketFactory": - if cls._default is None: - cls._default = SocketFactory() - return cls._default - - def createSocket(self, host: str = None, port: int = None, - host_inet: Any = None, local_port: int = None, - stream: bool = True) -> _socket.socket: - """Creates a socket.""" - sock = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM) - if host and port is not None: - sock.connect((host, port)) - return sock - - def toString(self) -> str: - return f"SocketFactory@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# ServerSocketFactory -# ============================================================ - -class ServerSocketFactory(Stub): - """ - javax.net.ServerSocketFactory - Factory for creating server sockets. - """ - - _default = None - - @classmethod - def getDefault(cls) -> "ServerSocketFactory": - if cls._default is None: - cls._default = ServerSocketFactory() - return cls._default - - def createServerSocket(self, port: int = 0, backlog: int = 50, - ifAddress: Any = None) -> _socket.socket: - """Creates a server socket.""" - sock = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM) - sock.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, 1) - if ifAddress: - sock.bind((ifAddress, port)) - else: - sock.bind(('0.0.0.0', port)) - sock.listen(backlog) - return sock - - def toString(self) -> str: - return f"ServerSocketFactory@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# SSLServerSocketFactory -# ============================================================ - -class SSLServerSocketFactory(ServerSocketFactory): - """ - javax.net.ssl.SSLServerSocketFactory - Factory for creating SSL server sockets. - """ - - _default = None - - @classmethod - def getDefault(cls) -> "SSLServerSocketFactory": - if cls._default is None: - cls._default = SSLServerSocketFactory() - return cls._default - - def getDefaultCipherSuites(self) -> List[str]: - try: - ctx = _ssl.create_default_context(_ssl.Purpose.CLIENT_AUTH) - return ctx.get_ciphers() - except Exception: - return [] - - def getSupportedCipherSuites(self) -> List[str]: - try: - ctx = _ssl.create_default_context(_ssl.Purpose.CLIENT_AUTH) - return ctx.get_ciphers() - except Exception: - return [] - - def createServerSocket(self, port: int = 0, backlog: int = 50, - ifAddress: Any = None) -> "SSLServerSocket": - sock = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM) - sock.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, 1) - context = _ssl.create_default_context(_ssl.Purpose.CLIENT_AUTH) - ssl_sock = context.wrap_socket(sock, server_side=True) - ssl_ss = SSLServerSocket() - ssl_ss._socket = ssl_sock - if ifAddress: - ssl_ss._socket.bind((ifAddress, port)) - else: - ssl_ss._socket.bind(('0.0.0.0', port)) - ssl_ss._socket.listen(backlog) - return ssl_ss - - def toString(self) -> str: - return f"SSLServerSocketFactory@{hex(id(self))}" - - -# ============================================================ -# SSLSocketFactory -# ============================================================ - -class SSLSocketFactory(SocketFactory): - """ - javax.net.ssl.SSLSocketFactory - Factory for creating SSL sockets. - """ - - _default = None - - @classmethod - def getDefault(cls) -> "SSLSocketFactory": - if cls._default is None: - cls._default = SSLSocketFactory() - return cls._default - - def getDefaultCipherSuites(self) -> List[str]: - try: - ctx = _ssl.create_default_context() - return [c['name'] for c in ctx.get_ciphers()] - except Exception: - return [] - - def getSupportedCipherSuites(self) -> List[str]: - try: - ctx = _ssl.create_default_context() - return [c['name'] for c in ctx.get_ciphers()] - except Exception: - return [] - - def createSocket(self, host: str = None, port: int = None, - host_inet: Any = None, local_port: int = None, - stream: bool = True) -> "SSLSocket": - sock = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM) - context = _ssl.create_default_context() - ssl_sock_impl = context.wrap_socket(sock, server_hostname=host) - if host and port is not None: - ssl_sock_impl.connect((host, port)) - ss = SSLSocket() - ss._socket = ssl_sock_impl - ss._connected = True - return ss - - def toString(self) -> str: - return f"SSLSocketFactory@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# SSLServerSocket -# ============================================================ - -class SSLServerSocket(Stub): - """ - javax.net.ssl.SSLServerSocket - A server socket that supports SSL/TLS. - """ - - def __init__(self): - super().__init__() - self._socket = None - self._enabled_cipher_suites = [] - self._enabled_protocols = [] - self._need_client_auth = False - self._use_client_mode = False - self._want_client_auth = False - self._enable_session_creation = True - - def getEnabledCipherSuites(self) -> List[str]: - return self._enabled_cipher_suites - - def setEnabledCipherSuites(self, suites: List[str]): - self._enabled_cipher_suites = suites - - def getSupportedCipherSuites(self) -> List[str]: - return ["TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256", - "TLS_AES_128_GCM_SHA256", "ECDHE-RSA-AES256-GCM-SHA384"] - - def setEnabledProtocols(self, protocols: List[str]): - self._enabled_protocols = protocols - - def getEnabledProtocols(self) -> List[str]: - return self._enabled_protocols or ["TLSv1.3", "TLSv1.2"] - - def getSupportedProtocols(self) -> List[str]: - return ["TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"] - - def getNeedClientAuth(self) -> bool: - return self._need_client_auth - - def setNeedClientAuth(self, need: bool): - self._need_client_auth = need - - def getUseClientMode(self) -> bool: - return self._use_client_mode - - def setUseClientMode(self, mode: bool): - self._use_client_mode = mode - - def getWantClientAuth(self) -> bool: - return self._want_client_auth - - def setWantClientAuth(self, want: bool): - self._want_client_auth = want - - def getEnableSessionCreation(self) -> bool: - return self._enable_session_creation - - def setEnableSessionCreation(self, flag: bool): - self._enable_session_creation = flag - - def accept(self) -> "SSLSocket": - if self._socket: - conn, addr = self._socket.accept() - ss = SSLSocket() - ss._socket = conn - ss._connected = True - ss._remote_address = addr - return ss - return SSLSocket() - - def close(self): - if self._socket: - self._socket.close() - - def toString(self) -> str: - return f"SSLServerSocket@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# SSLSocket -# ============================================================ - -class SSLSocket(Stub): - """ - javax.net.ssl.SSLSocket - A socket that supports SSL/TLS. - """ - - def __init__(self): - super().__init__() - self._socket = None - self._connected = False - self._remote_address = None - self._enabled_cipher_suites = [] - self._enabled_protocols = [] - self._need_client_auth = False - self._use_client_mode = False - self._want_client_auth = False - self._enable_session_creation = True - self._handshake_listeners = [] - self._session = None - - def getEnabledCipherSuites(self) -> List[str]: - return self._enabled_cipher_suites or ["TLS_AES_256_GCM_SHA384"] - - def setEnabledCipherSuites(self, suites: List[str]): - self._enabled_cipher_suites = suites - - def getSupportedCipherSuites(self) -> List[str]: - return ["TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256", - "TLS_AES_128_GCM_SHA256", "ECDHE-RSA-AES256-GCM-SHA384"] - - def setEnabledProtocols(self, protocols: List[str]): - self._enabled_protocols = protocols - - def getEnabledProtocols(self) -> List[str]: - return self._enabled_protocols or ["TLSv1.3", "TLSv1.2"] - - def getSupportedProtocols(self) -> List[str]: - return ["TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"] - - def getNeedClientAuth(self) -> bool: - return self._need_client_auth - - def setNeedClientAuth(self, need: bool): - self._need_client_auth = need - - def getUseClientMode(self) -> bool: - return self._use_client_mode - - def setUseClientMode(self, mode: bool): - self._use_client_mode = mode - - def getWantClientAuth(self) -> bool: - return self._want_client_auth - - def setWantClientAuth(self, want: bool): - self._want_client_auth = want - - def getEnableSessionCreation(self) -> bool: - return self._enable_session_creation - - def setEnableSessionCreation(self, flag: bool): - self._enable_session_creation = flag - - def getSession(self): - """Returns the SSL session.""" - return self._session - - def startHandshake(self): - """Starts or re-starts the SSL handshake.""" - if self._socket and hasattr(self._socket, 'do_handshake'): - try: - self._socket.do_handshake() - self._session = {"cipher": "TLS_AES_256_GCM_SHA384", - "protocol": "TLSv1.3"} - for listener in self._handshake_listeners: - listener.handshakeCompleted( - HandshakeCompletedEvent(self, self._session)) - except Exception: - pass - - def addHandshakeCompletedListener(self, listener: "HandshakeCompletedListener"): - self._handshake_listeners.append(listener) - - def removeHandshakeCompletedListener(self, listener: "HandshakeCompletedListener"): - if listener in self._handshake_listeners: - self._handshake_listeners.remove(listener) - - def getHandshakeApplicationProtocol(self) -> str: - return "http/1.1" - - def getApplicationProtocol(self) -> str: - return "http/1.1" - - def getHandshakeSession(self): - return self._session - - def setHandshakeApplicationProtocolSelector(self, selector): - pass - - def getHandshakeApplicationProtocolSelector(self): - return None - - def close(self): - if self._socket: - self._socket.close() - self._connected = False - - def toString(self) -> str: - return f"SSLSocket@{hex(id(self))}[connected={self._connected}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# HandshakeCompletedEvent -# ============================================================ - -class HandshakeCompletedEvent(Stub): - """ - javax.net.ssl.HandshakeCompletedEvent - An event indicating that an SSL handshake completed. - """ - - def __init__(self, sock: SSLSocket = None, session: Any = None): - super().__init__() - self._socket = sock - self._session = session - - def getSession(self): - return self._session - - def getCipherSuite(self) -> str: - if self._session and isinstance(self._session, dict): - return self._session.get("cipher", "UNKNOWN") - return "UNKNOWN" - - def getLocalCertificates(self) -> List: - return [] - - def getPeerCertificates(self) -> List: - return [] - - def getPeerPrincipal(self): - return None - - def getLocalPrincipal(self): - return None - - def getSocket(self) -> SSLSocket: - return self._socket - - def toString(self) -> str: - return f"HandshakeCompletedEvent@{hex(id(self))}[cipher={self.getCipherSuite()}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# HandshakeCompletedListener -# ============================================================ - -class HandshakeCompletedListener(Stub): - """ - javax.net.ssl.HandshakeCompletedListener - A listener for SSL handshake completion events. - """ - def handshakeCompleted(self, event: HandshakeCompletedEvent): - raise NotImplementedError - - -# ============================================================ -# HostnameVerifier -# ============================================================ - -class HostnameVerifier(Stub): - """ - javax.net.ssl.HostnameVerizer - A hostname verifier for HTTPS connections. - """ - def verify(self, hostname: str, session: Any) -> bool: - return True - - def toString(self) -> str: - return f"HostnameVerifier@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# HttpsURLConnection -# ============================================================ - -class HttpsURLConnection(Stub): - """ - javax.net.ssl.HttpsURLConnection - An HTTP URL connection with SSL/TLS support. - """ - _default_hostname_verifier = HostnameVerifier() - _default_sslsocket_factory = SSLSocketFactory() - - def __init__(self): - super().__init__() - self._hostname_verifier = None - self._ssl_socket_factory = None - - @classmethod - def getDefaultHostnameVerifier(cls) -> HostnameVerifier: - return cls._default_hostname_verifier - - @classmethod - def setDefaultHostnameVerifier(cls, verifier: HostnameVerifier): - cls._default_hostname_verifier = verifier - - @classmethod - def getDefaultSSLSocketFactory(cls) -> SSLSocketFactory: - return cls._default_sslsocket_factory - - @classmethod - def setDefaultSSLSocketFactory(cls, factory: SSLSocketFactory): - cls._default_sslsocket_factory = factory - - def getHostnameVerifier(self) -> HostnameVerifier: - return self._hostname_verifier or self._default_hostname_verifier - - def setHostnameVerifier(self, verifier: HostnameVerifier): - self._hostname_verifier = verifier - - def getSSLSocketFactory(self) -> SSLSocketFactory: - return self._ssl_socket_factory or self._default_sslsocket_factory - - def setSSLSocketFactory(self, factory: SSLSocketFactory): - self._ssl_socket_factory = factory - - def toString(self) -> str: - return f"HttpsURLConnection@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# TrustManager -# ============================================================ - -class TrustManager(Stub): - """ - javax.net.ssl.TrustManager - Base interface for trust managers. - """ - pass - - -# ============================================================ -# X509TrustManager -# ============================================================ - -class X509TrustManager(TrustManager): - """ - javax.net.ssl.X509TrustManager - Trust manager for X.509 certificates. - """ - def checkClientTrusted(self, chain: List, auth_type: str): - """Checks if a client certificate is trusted.""" - pass - - def checkServerTrusted(self, chain: List, auth_type: str): - """Checks if a server certificate is trusted.""" - pass - - def getAcceptedIssuers(self) -> List: - """Returns the list of accepted issuer certificates.""" - return [] - - def toString(self) -> str: - return f"X509TrustManager@{hex(id(self))}" - - -# ============================================================ -# KeyManager -# ============================================================ - -class KeyManager(Stub): - """ - javax.net.ssl.KeyManager - Base interface for key managers. - """ - pass - - -# ============================================================ -# X509KeyManager -# ============================================================ - -class X509KeyManager(KeyManager): - """ - javax.net.ssl.X509KeyManager - Key manager for X.509 keys. - """ - def getClientAliases(self, key_type: str, issuers: List) -> List[str]: - return [] - - def chooseClientAlias(self, key_types: List, issuers: List, sockets: List) -> str: - return None - - def getServerAliases(self, key_type: str, issuers: List) -> List[str]: - return [] - - def chooseServerAlias(self, key_type: str, issuers: List, sockets: List) -> str: - return None - - def getCertificateChain(self, alias: str) -> List: - return [] - - def getPrivateKey(self, alias: str): - return None - - def toString(self) -> str: - return f"X509KeyManager@{hex(id(self))}" - - -# ============================================================ -# TrustManagerFactory -# ============================================================ - -class TrustManagerFactory(Stub): - """ - javax.net.ssl.TrustManagerFactory - Factory for TrustManager instances. - """ - - def __init__(self, algorithm: str = None): - super().__init__() - self._algorithm = algorithm or "PKIX" - self._trust_managers = [] - - @staticmethod - def getInstance(algorithm: str) -> "TrustManagerFactory": - return TrustManagerFactory(algorithm) - - @staticmethod - def getDefaultAlgorithm() -> str: - return "PKIX" - - def getAlgorithm(self) -> str: - return self._algorithm - - def init(self, ks: Any = None): - """Initializes this factory with a key store.""" - self._trust_managers = [X509TrustManager()] - - def getTrustManagers(self) -> List[TrustManager]: - return list(self._trust_managers) - - def toString(self) -> str: - return f"TrustManagerFactory@{hex(id(self))}[algorithm={self._algorithm}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# KeyManagerFactory -# ============================================================ - -class KeyManagerFactory(Stub): - """ - javax.net.ssl.KeyManagerFactory - Factory for KeyManager instances. - """ - - def __init__(self, algorithm: str = None): - super().__init__() - self._algorithm = algorithm or "PKIX" - self._key_managers = [] - - @staticmethod - def getInstance(algorithm: str) -> "KeyManagerFactory": - return KeyManagerFactory(algorithm) - - @staticmethod - def getDefaultAlgorithm() -> str: - return "PKIX" - - def getAlgorithm(self) -> str: - return self._algorithm - - def init(self, ks: Any = None, password: Any = None): - """Initializes this factory with a key store.""" - self._key_managers = [X509KeyManager()] - - def getKeyManagers(self) -> List[KeyManager]: - return list(self._key_managers) - - def toString(self) -> str: - return f"KeyManagerFactory@{hex(id(self))}[algorithm={self._algorithm}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# SSLContext -# ============================================================ - -class SSLContext(Stub): - """ - javax.net.ssl.SSLContext - An implementation of a secure socket protocol. - """ - - _default = None - - def __init__(self, protocol: str = "TLS"): - super().__init__() - self._protocol = protocol - self._provider = None - self._ssl_context = None - - @staticmethod - def getInstance(protocol: str) -> "SSLContext": - ctx = SSLContext(protocol) - try: - ctx._ssl_context = _ssl.create_default_context() - except Exception: - pass - return ctx - - def init(self, km: List[KeyManager] = None, tm: List[TrustManager] = None, - random: Any = None): - """Initializes this context with key managers, trust managers, and randomness.""" - pass - - def getSocketFactory(self) -> SSLSocketFactory: - return SSLSocketFactory() - - def getServerSocketFactory(self) -> SSLServerSocketFactory: - return SSLServerSocketFactory() - - def createSSLEngine(self, host: str = None, port: int = None): - """Creates a new SSLEngine (stub).""" - return None - - def getProtocol(self) -> str: - return self._protocol - - def getProvider(self): - return self._provider - - @staticmethod - def getDefault() -> "SSLContext": - if SSLContext._default is None: - SSLContext._default = SSLContext("TLS") - return SSLContext._default - - @staticmethod - def setDefault(ctx: "SSLContext"): - SSLContext._default = ctx - - def toString(self) -> str: - return f"SSLContext@{hex(id(self))}[protocol={self._protocol}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/javax/security.py b/android_pyapi/javax/security.py deleted file mode 100644 index a916f42..0000000 --- a/android_pyapi/javax/security.py +++ /dev/null @@ -1,768 +0,0 @@ -""" -Java javax.security APIs implemented in Python. -javax.security package - Subject, Principal, Policy, Permission, etc. -Desktop stub implementation. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -from typing import List, Set, Optional - -__all__ = [ - "AuthProvider", "Subject", "Principal", "Policy", - "PrivateCredentialPermission", "SubjectDomainCombiner", - "Refreshable", "Destroyable", - "RefreshFailedException", "DestroyFailedException", - "CredentialException", "CredentialExpiredException", - "CredentialNotFoundException", "UnrecoverableKeyException", - "UnrecoverableEntryException", "KeyStoreCallbackHandler", - "DomainLoadStoreParameter", - "Certificate", "CodeSource", "ProtectionDomain", - "Permissions", "AllPermission", "BasicPermission", - "Permission", "PermissionCollection", "UnresolvedPermission", - "PermissionsEnumerator", "Guard", "GuardedObject", -] - - -# ============================================================ -# Principal -# ============================================================ - -class Principal(Stub): - """ - java.security.Principal - Represents a principal (entity) such as a user or group. - """ - - def __init__(self, name: str = ""): - super().__init__() - self._name = name - - def getName(self) -> str: - return self._name - - def implies(self, subject: "Subject") -> bool: - return False - - def toString(self) -> str: - return self._name - - def equals(self, other) -> bool: - if isinstance(other, Principal): - return self._name == other._name - return False - - def hashCode(self) -> int: - return hash(self._name) & 0x7FFFFFFF - - -# ============================================================ -# Subject -# ============================================================ - -class Subject(Stub): - """ - javax.security.auth.Subject - Represents a grouping of related information for a single entity. - """ - - def __init__(self, read_only: bool = False, - principals: Set[Principal] = None, - pub_creds: Set = None, - priv_creds: Set = None): - super().__init__() - self._read_only = read_only - self._principals = set(principals) if principals else set() - self._public_credentials = set(pub_creds) if pub_creds else set() - self._private_credentials = set(priv_creds) if priv_creds else set() - - def getPrincipals(self) -> Set[Principal]: - return set(self._principals) - - def getPrivateCredentials(self) -> Set: - return set(self._private_credentials) - - def getPublicCredentials(self) -> Set: - return set(self._public_credentials) - - def setReadOnly(self): - self._read_only = True - - def isReadOnly(self) -> bool: - return self._read_only - - @staticmethod - def doAs(subject: "Subject", action: callable) -> Any: - return action() - - @staticmethod - def doAsPrivileged(subject: "Subject", action: callable, access: Any) -> Any: - return action() - - def toString(self) -> str: - return (f"Subject@{hex(id(self))}[principals={len(self._principals)}, " - f"read_only={self._read_only}]") - - def equals(self, other) -> bool: - if not isinstance(other, Subject): - return False - return (self._principals == other._principals and - self._read_only == other._read_only) - - def hashCode(self) -> int: - return hash(frozenset(self._principals)) & 0x7FFFFFFF - - -# ============================================================ -# Policy -# ============================================================ - -class Policy(Stub): - """ - java.security.Policy - Policy for permissions (stub). - """ - _policy = None - - def __init__(self): - super().__init__() - self._parameters = None - - @staticmethod - def getPolicy() -> "Policy": - if Policy._policy is None: - Policy._policy = Policy() - return Policy._policy - - @staticmethod - def setPolicy(policy: "Policy"): - Policy._policy = policy - - def getParameters(self): - return self._parameters - - def setParameters(self, params): - self._parameters = params - - def implies(self, domain: "ProtectionDomain", permission: "Permission") -> bool: - return True - - def toString(self) -> str: - return f"Policy@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# AuthProvider -# ============================================================ - -class AuthProvider(Stub): - """ - javax.security.auth.AuthProvider - Provider for authentication services. - """ - def __init__(self): - super().__init__() - - def login(self, subject: Subject, handler: Any): - pass - - def logout(self, subject: Subject): - pass - - def setCallbackHandler(self, handler: Any): - pass - - def toString(self) -> str: - return f"AuthProvider@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# PrivateCredentialPermission -# ============================================================ - -class PrivateCredentialPermission(Stub): - """ - javax.security.auth.PrivateCredentialPermission - Permission to access private credentials. - """ - def __init__(self, name: str = "", actions: str = "read"): - super().__init__() - self._name = name - self._actions = actions - - def getName(self) -> str: - return self._name - - def getActions(self) -> str: - return self._actions - - def getCredentialClass(self) -> str: - return self._name - - def getPrincipals(self) -> List[List]: - return [] - - def implies(self, permission: "Permission") -> bool: - return isinstance(permission, PrivateCredentialPermission) and self._name == permission._name - - def toString(self) -> str: - return f'PrivateCredentialPermission "{self._name}" "{self._actions}"' - - def equals(self, other) -> bool: - if isinstance(other, PrivateCredentialPermission): - return self._name == other._name and self._actions == other._actions - return False - - def hashCode(self) -> int: - return hash((self._name, self._actions)) & 0x7FFFFFFF - - -# ============================================================ -# SubjectDomainCombiner -# ============================================================ - -class SubjectDomainCombiner(Stub): - """ - javax.security.auth.SubjectDomainCombiner - Combines ProtectionDomains with Subject context. - """ - def __init__(self, subject: Subject): - super().__init__() - self._subject = subject - - def combine(self, current_domains: List, assigned_domains: List) -> List: - return list(current_domains or []) + list(assigned_domains or []) - - def getSubject(self) -> Subject: - return self._subject - - def toString(self) -> str: - return f"SubjectDomainCombiner@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Refreshable / Destroyable interfaces -# ============================================================ - -class Refreshable(Stub): - """ - javax.security.auth.Refreshable - Interface for refreshable credentials. - """ - def isCurrent(self) -> bool: - return True - - def refresh(self): - raise AndroidOnlyError("refresh not supported on desktop") - - -class Destroyable(Stub): - """ - javax.security.auth.Destroyable - Interface for destroyable credentials. - """ - def isDestroyed(self) -> bool: - return False - - def destroy(self): - pass - - -# ============================================================ -# Exceptions -# ============================================================ - -class RefreshFailedException(Exception, Stub): - def __init__(self, message: str = None): - super().__init__(message or "Refresh failed") - self._message = message - def toString(self) -> str: - return f"RefreshFailedException: {self._message}" - def equals(self, other) -> bool: - return isinstance(other, RefreshFailedException) and self._message == other._message - def hashCode(self) -> int: - return hash(self._message) & 0x7FFFFFFF - - -class DestroyFailedException(Exception, Stub): - def __init__(self, message: str = None): - super().__init__(message or "Destroy failed") - self._message = message - def toString(self) -> str: - return f"DestroyFailedException: {self._message}" - def equals(self, other) -> bool: - return isinstance(other, DestroyFailedException) and self._message == other._message - def hashCode(self) -> int: - return hash(self._message) & 0x7FFFFFFF - - -class CredentialException(Exception, Stub): - def __init__(self, message: str = None): - super().__init__(message or "Credential error") - self._message = message - def toString(self) -> str: - return f"CredentialException: {self._message}" - - -class CredentialExpiredException(CredentialException): - def __init__(self, message: str = None): - super().__init__(message or "Credential expired") - - -class CredentialNotFoundException(CredentialException): - def __init__(self, message: str = None): - super().__init__(message or "Credential not found") - - -class UnrecoverableKeyException(Exception, Stub): - def __init__(self, message: str = None): - super().__init__(message or "Unrecoverable key") - self._message = message - def toString(self) -> str: - return f"UnrecoverableKeyException: {self._message}" - - -class UnrecoverableEntryException(Exception, Stub): - def __init__(self, message: str = None): - super().__init__(message or "Unrecoverable entry") - self._message = message - def toString(self) -> str: - return f"UnrecoverableEntryException: {self._message}" - - -# ============================================================ -# KeyStoreCallbackHandler -# ============================================================ - -class KeyStoreCallbackHandler(Stub): - """ - javax.security.auth.callback.KeyStoreCallbackHandler - Handler for key store callbacks. - """ - def __init__(self): - super().__init__() - - def handle(self, callbacks: List): - pass - - def toString(self) -> str: - return f"KeyStoreCallbackHandler@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# DomainLoadStoreParameter -# ============================================================ - -class DomainLoadStoreParameter(Stub): - """ - java.security.DomainLoadStoreParameter - Configuration data for loading/storing keystore domains. - """ - def __init__(self, config: dict = None): - super().__init__() - self._config = config or {} - - def getConfiguration(self) -> dict: - return self._config - - def toString(self) -> str: - return f"DomainLoadStoreParameter@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Certificate -# ============================================================ - -class Certificate(Stub): - """ - java.security.cert.Certificate - Abstract class for managing certificates. - """ - def __init__(self, cert_type: str = "X.509", encoded: bytes = None): - super().__init__() - self._cert_type = cert_type - self._encoded = encoded or b'' - - def getType(self) -> str: - return self._cert_type - - def getEncoded(self) -> bytes: - return self._encoded - - def getPublicKey(self): - return None - - def toString(self) -> str: - return f"Certificate@{hex(id(self))}[type={self._cert_type}]" - - def equals(self, other) -> bool: - if isinstance(other, Certificate): - return self._encoded == other._encoded - return False - - def hashCode(self) -> int: - return hash(bytes(self._encoded)) & 0x7FFFFFFF - - -# ============================================================ -# Permission & PermissionCollection -# ============================================================ - -class Permission(Stub): - """ - java.security.Permission - Abstract class for representing access to a system resource. - """ - def __init__(self, name: str = ""): - super().__init__() - self._name = name - - def getName(self) -> str: - return self._name - - def getActions(self) -> str: - return "" - - def implies(self, permission: "Permission") -> bool: - return self.equals(permission) - - def toString(self) -> str: - return f'Permission "{self._name}"' - - def equals(self, other) -> bool: - if isinstance(other, Permission): - return self._name == other._name - return False - - def hashCode(self) -> int: - return hash(self._name) & 0x7FFFFFFF - - -class PermissionCollection(Stub): - """ - java.security.PermissionCollection - Abstract class for holding a collection of Permission objects. - """ - def __init__(self): - super().__init__() - self._permissions = [] - self._read_only = False - - def add(self, permission: Permission): - if not self._read_only: - self._permissions.append(permission) - - def implies(self, permission: Permission) -> bool: - for p in self._permissions: - if p.implies(permission): - return True - return False - - def elements(self): - return iter(self._permissions) - - def isReadOnly(self) -> bool: - return self._read_only - - def setReadOnly(self): - self._read_only = True - - def toString(self) -> str: - return f"PermissionCollection@{hex(id(self))}[size={len(self._permissions)}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -class Permissions(PermissionCollection): - """ - java.security.Permissions - A collection of Permission objects. - """ - def add(self, permission: Permission): - super().add(permission) - - def implies(self, permission: Permission) -> bool: - return super().implies(permission) - - def elements(self): - return super().elements() - - def toString(self) -> str: - return f"Permissions@{hex(id(self))}[size={len(self._permissions)}]" - - -class AllPermission(Permission): - """ - java.security.AllPermission - A permission that implies all other permissions. - """ - def __init__(self): - super().__init__("") - - def implies(self, permission: Permission) -> bool: - return True - - def getActions(self) -> str: - return "" - - def toString(self) -> str: - return "AllPermission" - - -class BasicPermission(Permission): - """ - java.security.BasicPermission - Base class for basic permissions. - """ - def __init__(self, name: str = "", actions: str = ""): - super().__init__(name) - self._actions = actions - - def implies(self, permission: Permission) -> bool: - if isinstance(permission, BasicPermission): - return self._name == permission._name or permission._name.startswith(self._name + ".") - return False - - def getActions(self) -> str: - return self._actions - - def toString(self) -> str: - return f'BasicPermission "{self._name}"' - - def equals(self, other) -> bool: - if isinstance(other, BasicPermission): - return self._name == other._name - return False - - def hashCode(self) -> int: - return hash(self._name) & 0x7FFFFFFF - - -class UnresolvedPermission(Permission): - """ - java.security.UnresolvedPermission - A permission that was not resolved when the PermissionCollection was built. - """ - def __init__(self, perm_type: str = "", name: str = "", actions: str = "", - certs: List = None): - super().__init__(name) - self._unresolved_type = perm_type - self._unresolved_actions = actions - self._unresolved_certs = certs or [] - - def getUnresolvedType(self) -> str: - return self._unresolved_type - - def getUnresolvedName(self) -> str: - return self._name - - def getUnresolvedActions(self) -> str: - return self._unresolved_actions - - def getUnresolvedCerts(self) -> List: - return list(self._unresolved_certs) - - def implies(self, permission: Permission) -> bool: - return False - - def toString(self) -> str: - return f'UnresolvedPermission "{self._unresolved_type}" "{self._name}" "{self._unresolved_actions}"' - - def equals(self, other) -> bool: - if isinstance(other, UnresolvedPermission): - return (self._unresolved_type == other._unresolved_type and - self._name == other._name) - return False - - def hashCode(self) -> int: - return hash((self._unresolved_type, self._name)) & 0x7FFFFFFF - - -class PermissionsEnumerator(Stub): - """ - java.security.PermissionsEnumerator - Enumerator over Permission objects. - """ - def __init__(self, permissions: List): - super().__init__() - self._permissions = list(permissions) - self._index = 0 - - def hasMoreElements(self) -> bool: - return self._index < len(self._permissions) - - def nextElement(self) -> Permission: - if self._index < len(self._permissions): - p = self._permissions[self._index] - self._index += 1 - return p - return None - - def toString(self) -> str: - return f"PermissionsEnumerator@{hex(id(self))}" - - -# ============================================================ -# CodeSource -# ============================================================ - -class CodeSource(Stub): - """ - java.security.CodeSource - Represents the codebase and certificates from which code originated. - """ - def __init__(self, location: str = None, certs: List = None, - code_signers: List = None): - super().__init__() - self._location = location - self._certificates = certs or [] - self._code_signers = code_signers or [] - - def getCertificates(self) -> List: - return list(self._certificates) - - def getCodeSigners(self) -> List: - return list(self._code_signers) - - def getLocation(self) -> str: - return self._location - - def implies(self, codesource: "CodeSource") -> bool: - return self._location == codesource._location - - def toString(self) -> str: - return f"CodeSource@{hex(id(self))}[location={self._location}]" - - def equals(self, other) -> bool: - if isinstance(other, CodeSource): - return self._location == other._location - return False - - def hashCode(self) -> int: - return hash(self._location) & 0x7FFFFFFF - - -# ============================================================ -# ProtectionDomain -# ============================================================ - -class ProtectionDomain(Stub): - """ - java.security.ProtectionDomain - Groups a CodeSource and Permissions for code access control. - """ - def __init__(self, codesource: CodeSource = None, - permissions: PermissionCollection = None, - principals: List[Principal] = None, - static_permissions: bool = False): - super().__init__() - self._codesource = codesource - self._permissions = permissions or Permissions() - self._principals = principals or [] - self._static_permissions = static_permissions - - def getCodeSource(self) -> CodeSource: - return self._codesource - - def getPermissions(self) -> PermissionCollection: - return self._permissions - - def getPrincipals(self) -> List[Principal]: - return list(self._principals) - - def implies(self, permission: Permission) -> bool: - return self._permissions.implies(permission) if self._permissions else False - - def toString(self) -> str: - return f"ProtectionDomain@{hex(id(self))}[codesource={self._codesource}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Guard & GuardedObject -# ============================================================ - -class Guard(Stub): - """ - java.security.Guard - Interface for objects that guard access to another object. - """ - def checkGuard(self, obj: Any): - raise NotImplementedError - - def toString(self) -> str: - return f"Guard@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -class GuardedObject(Stub): - """ - java.security.GuardedObject - Wraps an object and guards access to it with a Guard. - """ - def __init__(self, obj: Any = None, guard: Guard = None): - super().__init__() - self._object = obj - self._guard = guard - - def getObject(self) -> Any: - if self._guard: - self._guard.checkGuard(self._object) - return self._object - - def setGuard(self, guard: Guard): - self._guard = guard - - def toString(self) -> str: - return f"GuardedObject@{hex(id(self))}[guarded={self._guard is not None}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/javax/sql.py b/android_pyapi/javax/sql.py deleted file mode 100644 index e1a3f02..0000000 --- a/android_pyapi/javax/sql.py +++ /dev/null @@ -1,1248 +0,0 @@ -""" -Java javax.sql APIs implemented in Python. -javax.sql package - DataSource, RowSet, RowSetListener, ConnectionEvent, etc. -Desktop implementation wrapping Python sqlite3 module. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -import sqlite3 as _sqlite3 -from typing import List, Optional, Any - -__all__ = [ - "DataSource", "RowSet", "RowSetListener", "RowSetMetaData", - "CachedRowSet", "WebRowSet", "FilteredRowSet", "JoinRowSet", - "Predicate", "SyncProvider", "SyncFactory", - "RowSetWriter", "RowSetReader", - "ConnectionEvent", "ConnectionEventListener", - "PooledConnection", "StatementEvent", "StatementEventListener", - "XADataSource", "XAConnection", "XAResource", -] - - -# ============================================================ -# DataSource -# ============================================================ - -class DataSource(Stub): - """ - javax.sql.DataSource - A factory for connections to a physical data source. - Wraps Python sqlite3. - """ - - def __init__(self, db_path: str = ":memory:"): - super().__init__() - self._db_path = db_path - self._log_writer = None - self._login_timeout = 0 - self._connection = None - - def getConnection(self, username: str = None, password: str = None) -> Any: - """Gets a connection to the data source.""" - self._connection = _sqlite3.connect(self._db_path) - return self._connection - - def getLogWriter(self): - return self._log_writer - - def setLogWriter(self, writer): - self._log_writer = writer - - def getLoginTimeout(self) -> int: - return self._login_timeout - - def setLoginTimeout(self, seconds: int): - self._login_timeout = seconds - - def getParentLogger(self): - return None - - def createConnectionBuilder(self): - return None - - def createPooledConnectionBuilder(self): - return None - - def unwrap(self, iface: type) -> Any: - return self - - def isWrapperFor(self, iface: type) -> bool: - return iface == DataSource - - def toString(self) -> str: - return f"DataSource@{hex(id(self))}[db={self._db_path}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# RowSetListener -# ============================================================ - -class RowSetListener(Stub): - """ - javax.sql.RowSetListener - Listener for RowSet events. - """ - def cursorMoved(self, event: Any): - pass - - def rowChanged(self, event: Any): - pass - - def rowSetChanged(self, event: Any): - pass - - def toString(self) -> str: - return f"RowSetListener@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# RowSet -# ============================================================ - -class RowSet(Stub): - """ - javax.sql.RowSet - A row set interface that wraps a result set. - """ - - def __init__(self): - super().__init__() - self._url = "" - self._data_source_name = "" - self._username = "" - self._password = "" - self._command = "" - self._query_timeout = 0 - self._max_rows = 0 - self._max_field_size = 0 - self._escape_processing = True - self._fetch_direction = 1000 # FETCH_FORWARD - self._fetch_size = 0 - self._type = 1003 # TYPE_FORWARD_ONLY - self._concurrency = 1007 # CONCUR_READ_ONLY - self._show_deleted = False - self._read_only = True - self._table_name = "" - self._transaction_isolation = 0 - self._type_map = None - self._key_columns = [] - self._match_column = "" - self._filter = None - self._cursor = None - self._listeners = [] - self._params = [] - self._rows = [] - self._current_row = -1 - - def setUrl(self, url: str): - self._url = url - - def getUrl(self) -> str: - return self._url - - def setDataSourceName(self, name: str): - self._data_source_name = name - - def getDataSourceName(self) -> str: - return self._data_source_name - - def setUsername(self, name: str): - self._username = name - - def getUsername(self) -> str: - return self._username - - def setPassword(self, pwd: str): - self._password = pwd - - def getPassword(self) -> str: - return self._password - - def setCommand(self, cmd: str): - self._command = cmd - - def getCommand(self) -> str: - return self._command - - def setQueryTimeout(self, seconds: int): - self._query_timeout = seconds - - def getQueryTimeout(self) -> int: - return self._query_timeout - - def setMaxRows(self, max_rows: int): - self._max_rows = max_rows - - def getMaxRows(self) -> int: - return self._max_rows - - def setMaxFieldSize(self, max_size: int): - self._max_field_size = max_size - - def getMaxFieldSize(self) -> int: - return self._max_field_size - - def setEscapeProcessing(self, enable: bool): - self._escape_processing = enable - - def getEscapeProcessing(self) -> bool: - return self._escape_processing - - def setFetchDirection(self, direction: int): - self._fetch_direction = direction - - def getFetchDirection(self) -> int: - return self._fetch_direction - - def setFetchSize(self, size: int): - self._fetch_size = size - - def getFetchSize(self) -> int: - return self._fetch_size - - def setType(self, rowset_type: int): - self._type = rowset_type - - def getType(self) -> int: - return self._type - - def setConcurrency(self, concurrency: int): - self._concurrency = concurrency - - def getConcurrency(self) -> int: - return self._concurrency - - def execute(self): - """Executes the command and populates the row set.""" - self._rows = [] - self._current_row = -1 - for listener in self._listeners: - listener.rowSetChanged(self) - - def rowSetListeners(self) -> List[RowSetListener]: - return list(self._listeners) - - def addRowSetListener(self, listener: RowSetListener): - self._listeners.append(listener) - - def removeRowSetListener(self, listener: RowSetListener): - if listener in self._listeners: - self._listeners.remove(listener) - - def setShowDeleted(self, show: bool): - self._show_deleted = show - - def isShowDeleted(self) -> bool: - return self._show_deleted - - def setReadOnly(self, read_only: bool): - self._read_only = read_only - - def isReadOnly(self) -> bool: - return self._read_only - - def setTableName(self, name: str): - self._table_name = name - - def getTableName(self) -> str: - return self._table_name - - def setTransactionIsolation(self, level: int): - self._transaction_isolation = level - - def getTransactionIsolation(self) -> int: - return self._transaction_isolation - - def setTypeMap(self, type_map: Any): - self._type_map = type_map - - def getTypeMap(self): - return self._type_map - - def setKeyColumns(self, columns: List[int]): - self._key_columns = columns - - def getKeyColumns(self) -> List[int]: - return list(self._key_columns) - - def setMatchColumn(self, column: Any): - self._match_column = str(column) - - def getMatchColumn(self) -> Any: - return self._match_column - - def setFilter(self, f: Any): - self._filter = f - - def getFilter(self): - return self._filter - - def setCursor(self, cursor: Any): - self._cursor = cursor - - def getCursor(self): - return self._cursor - - def clearParameters(self): - self._params = [] - - def validate(self) -> bool: - return True - - def getParams(self) -> List: - return list(self._params) - - def toString(self) -> str: - return f"RowSet@{hex(id(self))}[command={self._command}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# RowSetMetaData -# ============================================================ - -class RowSetMetaData(Stub): - """ - javax.sql.RowSetMetaData - Metadata for a RowSet. - """ - - def __init__(self): - super().__init__() - self._column_count = 0 - self._auto_increment = {} - self._case_sensitive = {} - self._searchable = {} - self._currency = {} - self._nullable = {} - self._signed = {} - self._display_size = {} - self._labels = {} - self._column_names = {} - self._schema_names = {} - self._precision = {} - self._scale = {} - self._table_names = {} - self._catalog_names = {} - self._column_types = {} - self._column_type_names = {} - self._column_class_names = {} - - def setColumnCount(self, count: int): - self._column_count = count - - def getColumnCount(self) -> int: - return self._column_count - - def setAutoIncrement(self, column: int, value: bool): - self._auto_increment[column] = value - - def isAutoIncrement(self, column: int) -> bool: - return self._auto_increment.get(column, False) - - def setCaseSensitive(self, column: int, value: bool): - self._case_sensitive[column] = value - - def isCaseSensitive(self, column: int) -> bool: - return self._case_sensitive.get(column, False) - - def setSearchable(self, column: int, value: bool): - self._searchable[column] = value - - def isSearchable(self, column: int) -> bool: - return self._searchable.get(column, True) - - def setCurrency(self, column: int, value: bool): - self._currency[column] = value - - def isCurrency(self, column: int) -> bool: - return self._currency.get(column, False) - - def setNullable(self, column: int, value: int): - self._nullable[column] = value - - def isNullable(self, column: int) -> int: - return self._nullable.get(column, 1) - - def setSigned(self, column: int, value: bool): - self._signed[column] = value - - def isSigned(self, column: int) -> bool: - return self._signed.get(column, False) - - def setDisplaySize(self, column: int, size: int): - self._display_size[column] = size - - def getDisplaySize(self, column: int) -> int: - return self._display_size.get(column, 0) - - def setLabel(self, column: int, label: str): - self._labels[column] = label - - def getColumnLabel(self, column: int) -> str: - return self._labels.get(column, "") - - def setColumnName(self, column: int, name: str): - self._column_names[column] = name - - def getColumnName(self, column: int) -> str: - return self._column_names.get(column, "") - - def setSchemaName(self, column: int, name: str): - self._schema_names[column] = name - - def getSchemaName(self, column: int) -> str: - return self._schema_names.get(column, "") - - def setPrecision(self, column: int, precision: int): - self._precision[column] = precision - - def getPrecision(self, column: int) -> int: - return self._precision.get(column, 0) - - def setScale(self, column: int, scale: int): - self._scale[column] = scale - - def getScale(self, column: int) -> int: - return self._scale.get(column, 0) - - def setTableName(self, column: int, name: str): - self._table_names[column] = name - - def getTableName(self, column: int) -> str: - return self._table_names.get(column, "") - - def setCatalogName(self, column: int, name: str): - self._catalog_names[column] = name - - def getCatalogName(self, column: int) -> str: - return self._catalog_names.get(column, "") - - def setColumnType(self, column: int, sql_type: int): - self._column_types[column] = sql_type - - def getColumnType(self, column: int) -> int: - return self._column_types.get(column, 0) - - def setColumnTypeName(self, column: int, name: str): - self._column_type_names[column] = name - - def getColumnTypeName(self, column: int) -> str: - return self._column_type_names.get(column, "") - - def setColumnClassName(self, column: int, name: str): - self._column_class_names[column] = name - - def getColumnClassName(self, column: int) -> str: - return self._column_class_names.get(column, "") - - def toString(self) -> str: - return f"RowSetMetaData@{hex(id(self))}[columns={self._column_count}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# CachedRowSet -# ============================================================ - -class CachedRowSet(RowSet): - """ - javax.sql.rowset.CachedRowSet - A row set that caches its data in memory. - """ - - def __init__(self): - super().__init__() - self._sync_provider = None - self._page_size = 0 - self._cached_data = [] - self._original_data = [] - - def execute(self): - super().execute() - self._cached_data = list(self._rows) - self._original_data = list(self._rows) - - def populate(self, result_set: Any): - """Populates the row set from a ResultSet.""" - pass - - def acceptChanges(self): - """Accepts changes to the row set.""" - self._original_data = list(self._cached_data) - - def commit(self): - """Commits changes.""" - pass - - def rollback(self, savepoint: Any = None): - """Rolls back changes.""" - self._cached_data = list(self._original_data) - - def setSyncProvider(self, provider: str): - self._sync_provider = provider - - def getSyncProvider(self) -> str: - return self._sync_provider or "com.sun.rowset.providers.RIOptimisticProvider" - - def sync(self) -> bool: - """Syncs changes to the data source.""" - return True - - def size(self) -> int: - return len(self._cached_data) - - def createCopy(self) -> "CachedRowSet": - copy = CachedRowSet() - copy._cached_data = list(self._cached_data) - return copy - - def createCopySchema(self) -> "CachedRowSet": - return CachedRowSet() - - def createCopyNoConstraints(self) -> "CachedRowSet": - return CachedRowSet() - - def getOriginal(self): - """Returns the original value of the current row.""" - return None - - def getOriginalRow(self): - """Returns the original row.""" - return None - - def setPageSize(self, size: int): - self._page_size = size - - def getPageSize(self) -> int: - return self._page_size - - def nextPage(self) -> bool: - return False - - def previousPage(self) -> bool: - return False - - def release(self): - self._cached_data = [] - - def originalContents(self): - """Returns the original contents.""" - return self._original_data - - def getBookmark(self): - return None - - def setBookmark(self, bookmark): - pass - - def getShowDeleted(self) -> bool: - return self._show_deleted - - def setShowDeleted(self, show: bool): - self._show_deleted = show - - def isBeforeFirst(self) -> bool: - return self._current_row < 0 and len(self._cached_data) > 0 - - def isAfterLast(self) -> bool: - return self._current_row >= len(self._cached_data) - - def isFirst(self) -> bool: - return self._current_row == 0 - - def isLast(self) -> bool: - return self._current_row == len(self._cached_data) - 1 - - def beforeFirst(self): - self._current_row = -1 - - def afterLast(self): - self._current_row = len(self._cached_data) - - def first(self) -> bool: - if self._cached_data: - self._current_row = 0 - return True - return False - - def last(self) -> bool: - if self._cached_data: - self._current_row = len(self._cached_data) - 1 - return True - return False - - def next(self) -> bool: - if self._current_row < len(self._cached_data) - 1: - self._current_row += 1 - return True - self._current_row = len(self._cached_data) - return False - - def previous(self) -> bool: - if self._current_row > 0: - self._current_row -= 1 - return True - return False - - def absolute(self, row: int) -> bool: - if 0 < row <= len(self._cached_data): - self._current_row = row - 1 - return True - return False - - def relative(self, rows: int) -> bool: - new_row = self._current_row + rows - if 0 <= new_row < len(self._cached_data): - self._current_row = new_row - return True - return False - - def getRow(self) -> int: - return self._current_row + 1 if 0 <= self._current_row < len(self._cached_data) else 0 - - def setMetaData(self, md: RowSetMetaData): - pass - - def getMetaData(self): - return None - - def findColumn(self, column_name: str) -> int: - return 1 - - # Data retrieval methods (stub) - def getObject(self, column: int) -> Any: - if 0 <= self._current_row < len(self._cached_data): - row = self._cached_data[self._current_row] - if isinstance(row, (list, tuple)) and column <= len(row): - return row[column - 1] - return None - - def getString(self, column: int) -> str: - return str(self.getObject(column) or "") - - def getInt(self, column: int) -> int: - val = self.getObject(column) - return int(val) if val else 0 - - def getLong(self, column: int) -> int: - return self.getInt(column) - - def getFloat(self, column: int) -> float: - val = self.getObject(column) - return float(val) if val else 0.0 - - def getDouble(self, column: int) -> float: - return self.getFloat(column) - - def getShort(self, column: int) -> int: - return self.getInt(column) - - def getByte(self, column: int) -> int: - return self.getInt(column) & 0xFF - - def getBytes(self, column: int) -> bytes: - val = self.getObject(column) - if isinstance(val, bytes): - return val - return b'' - - def getBoolean(self, column: int) -> bool: - val = self.getObject(column) - return bool(val) if val else False - - def getDate(self, column: int): - return self.getObject(column) - - def getTime(self, column: int): - return self.getObject(column) - - def getTimestamp(self, column: int): - return self.getObject(column) - - def getBigDecimal(self, column: int): - return self.getObject(column) - - def getAsciiStream(self, column: int): - return None - - def getUnicodeStream(self, column: int): - return None - - def getBinaryStream(self, column: int): - return None - - def getCharacterStream(self, column: int): - return None - - def getClob(self, column: int): - return None - - def getBlob(self, column: int): - return None - - def getArray(self, column: int): - return None - - def getRef(self, column: int): - return None - - def getRowId(self, column: int): - return None - - def getNClob(self, column: int): - return None - - def getNString(self, column: int) -> str: - return self.getString(column) - - def getNCharacterStream(self, column: int): - return None - - def getSQLXML(self, column: int): - return None - - def wasNull(self) -> bool: - return False - - # Update methods (stub) - def updateNull(self, column: int): - pass - def updateBoolean(self, column: int, value: bool): pass - def updateByte(self, column: int, value: int): pass - def updateShort(self, column: int, value: int): pass - def updateInt(self, column: int, value: int): pass - def updateLong(self, column: int, value: int): pass - def updateFloat(self, column: int, value: float): pass - def updateDouble(self, column: int, value: float): pass - def updateBigDecimal(self, column: int, value): pass - def updateString(self, column: int, value: str): pass - def updateBytes(self, column: int, value: bytes): pass - def updateDate(self, column: int, value): pass - def updateTime(self, column: int, value): pass - def updateTimestamp(self, column: int, value): pass - def updateAsciiStream(self, column: int, stream, length: int = None): pass - def updateBinaryStream(self, column: int, stream, length: int = None): pass - def updateCharacterStream(self, column: int, stream, length: int = None): pass - def updateObject(self, column: int, value, scale_or_target: Any = None): pass - def updateNull(self, column: int, type_name: str = None): pass - def updateRow(self): pass - def deleteRow(self): pass - def insertRow(self): pass - def cancelRowUpdates(self): pass - def moveToInsertRow(self): pass - def moveToCurrentRow(self): pass - def rowUpdated(self) -> bool: return False - def rowInserted(self) -> bool: return False - def rowDeleted(self) -> bool: return False - def close(self): pass - def clearWarnings(self): pass - def getWarnings(self): return None - - # Parameter setters (stub) - def setURL(self, col: int, url: str): pass - def setAsciiStream(self, col: int, stream, length: int = None): pass - def setBigDecimal(self, col: int, value): pass - def setBinaryStream(self, col: int, stream, length: int = None): pass - def setBlob(self, col: int, blob, length: int = None): pass - def setBoolean(self, col: int, value: bool): pass - def setByte(self, col: int, value: int): pass - def setBytes(self, col: int, value: bytes): pass - def setCharacterStream(self, col: int, stream, length: int = None): pass - def setClob(self, col: int, clob, length: int = None): pass - def setDate(self, col: int, value): pass - def setDouble(self, col: int, value: float): pass - def setFloat(self, col: int, value: float): pass - def setInt(self, col: int, value: int): pass - def setLong(self, col: int, value: int): pass - def setNCharacterStream(self, col: int, stream, length: int = None): pass - def setNClob(self, col: int, value, length: int = None): pass - def setNString(self, col: int, value: str): pass - def setNull(self, col: int, sql_type: int, type_name: str = None): pass - def setObject(self, col: int, value, target_type: Any = None, scale: int = None): pass - def setRef(self, col: int, ref): pass - def setRowId(self, col: int, row_id): pass - def setShort(self, col: int, value: int): pass - def setSQLXML(self, col: int, xml): pass - def setString(self, col: int, value: str): pass - def setTime(self, col: int, value): pass - def setTimestamp(self, col: int, value): pass - def setUnicodeStream(self, col: int, stream, length: int): pass - - def toString(self) -> str: - return f"CachedRowSet@{hex(id(self))}[size={self.size()}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -class WebRowSet(CachedRowSet): - """javax.sql.rowset.WebRowSet - Web-oriented row set.""" - pass - -class FilteredRowSet(CachedRowSet): - """javax.sql.rowset.FilteredRowSet - Filtered row set.""" - pass - -class JoinRowSet(CachedRowSet): - """javax.sql.rowset.JoinRowSet - Join row set.""" - pass - - -# ============================================================ -# Predicate -# ============================================================ - -class Predicate(Stub): - """ - javax.sql.rowset.Predicate - A filter for row set contents. - """ - def evaluate(self, row_set: CachedRowSet) -> bool: - return True - - def evaluate(self, value: Any, column: int) -> bool: - return True - - def evaluates(self, value: Any, column_name: str) -> bool: - return True - - def toString(self) -> str: - return f"Predicate@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# SyncProvider -# ============================================================ - -class SyncProvider(Stub): - """ - javax.sql.rowset.spi.SyncProvider - Service provider for synchronization. - """ - GRADE_NONE: int = 1 - GRADE_LOCK_WHEN_MODIFIED: int = 2 - GRADE_LOCK_MODIFIED_WHEN_GRANTED: int = 3 - GRADE_CHECK_MODIFIED_AT_COMMIT: int = 4 - GRADE_CHECK_ALL_AT_COMMIT: int = 5 - DATASOURCE_NO_LOCK: int = 1 - DATASOURCE_LOCK: int = 2 - DATASOURCE_ROW_LOCK: int = 3 - - def getProviderID(self) -> str: - return "com.sun.rowset.providers.RIOptimisticProvider" - - def getProviderGrade(self) -> int: - return self.GRADE_CHECK_MODIFIED_AT_COMMIT - - def setProviderGrade(self, grade: int): - pass - - def getDataSourceLock(self) -> int: - return self.DATASOURCE_NO_LOCK - - def setDataSourceLock(self, lock: int): - pass - - def getRowSetWriter(self): - return None - - def getRowSetReader(self): - return None - - def supportsUpdatableView(self) -> bool: - return True - - def toString(self) -> str: - return f"SyncProvider@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# SyncFactory -# ============================================================ - -class SyncFactory(Stub): - """ - javax.sql.rowset.spi.SyncFactory - Factory for SyncProvider instances. - """ - PROVIDER: str = "rowset.provider" - ROWSET_SYNC_PROVIDER: str = "rowset.syncProvider" - ROWSET_SYNC_VENDOR: str = "rowset.syncVendor" - ROWSET_SYNC_PROVIDER_GRADE: str = "rowset.syncProviderGrade" - DATASOURCE_LOCK: str = "rowset.datasourceLock" - - _instance = None - _logger = None - - @classmethod - def getInstance(cls) -> "SyncFactory": - if cls._instance is None: - cls._instance = SyncFactory() - return cls._instance - - def getSyncProvider(self, provider_id: str = None) -> SyncProvider: - return SyncProvider() - - def registerSyncProvider(self, provider_id: str): - pass - - def setJNDIContext(self, ctx): - pass - - def getJNDIContext(self): - return None - - def unregisterSyncProvider(self, provider_id: str): - pass - - def syncProviderRegistered(self, provider_id: str) -> bool: - return True - - def syncProviderAdded(self, provider_id: str) -> bool: - return True - - def syncProviderRemoved(self, provider_id: str) -> bool: - return True - - @classmethod - def setLogger(cls, logger): - cls._logger = logger - - @classmethod - def getLogger(cls): - return cls._logger - - def toString(self) -> str: - return f"SyncFactory@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# RowSetWriter / RowSetReader -# ============================================================ - -class RowSetWriter(Stub): - """ - javax.sql.RowSetWriter - Writes the contents of a RowSet to the data source. - """ - def writeData(self, caller: CachedRowSet) -> bool: - return True - - def toString(self) -> str: - return f"RowSetWriter@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -class RowSetReader(Stub): - """ - javax.sql.RowSetReader - Reads the contents of a RowSet from the data source. - """ - def readData(self, caller: CachedRowSet): - pass - - def toString(self) -> str: - return f"RowSetReader@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# ConnectionEvent / ConnectionEventListener -# ============================================================ - -class ConnectionEvent(Stub): - """ - javax.sql.ConnectionEvent - An event that provides information about connection events. - """ - def __init__(self, conn, exception: Exception = None): - super().__init__() - self._connection = conn - self._exception = exception - - def getSQLException(self): - return self._exception - - def toString(self) -> str: - return f"ConnectionEvent@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -class ConnectionEventListener(Stub): - """ - javax.sql.ConnectionEventListener - Listener for connection events on a PooledConnection. - """ - def connectionClosed(self, event: ConnectionEvent): - pass - - def connectionErrorOccurred(self, event: ConnectionEvent): - pass - - def toString(self) -> str: - return f"ConnectionEventListener@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# PooledConnection -# ============================================================ - -class PooledConnection(Stub): - """ - javax.sql.PooledConnection - A connection pool management interface. - """ - def __init__(self, db_path: str = ":memory:"): - super().__init__() - self._db_path = db_path - self._connection = None - self._listeners = [] - self._statement_pool_size = 0 - - def getConnection(self) -> Any: - self._connection = _sqlite3.connect(self._db_path) - return self._connection - - def close(self): - if self._connection: - self._connection.close() - event = ConnectionEvent(self) - for listener in self._listeners: - listener.connectionClosed(event) - - def addConnectionEventListener(self, listener: ConnectionEventListener): - self._listeners.append(listener) - - def removeConnectionEventListener(self, listener: ConnectionEventListener): - if listener in self._listeners: - self._listeners.remove(listener) - - def setStatementPoolingCacheSize(self, size: int): - self._statement_pool_size = size - - def getStatementPoolingCacheSize(self) -> int: - return self._statement_pool_size - - def setConnectionEventListener(self, listener: ConnectionEventListener): - self.addConnectionEventListener(listener) - - def setStatementEventListener(self, listener): - pass - - def removeStatementEventListener(self, listener): - pass - - def addStatementEventListener(self, listener): - pass - - def removeStatementEventListener(self, listener): - pass - - def toString(self) -> str: - return f"PooledConnection@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# StatementEvent / StatementEventListener -# ============================================================ - -class StatementEvent(Stub): - """ - javax.sql.StatementEvent - An event for statement-related events. - """ - def __init__(self, conn, statement: Any = None, exception: Exception = None): - super().__init__() - self._connection = conn - self._statement = statement - self._exception = exception - - def getStatement(self): - return self._statement - - def getSQLException(self): - return self._exception - - def toString(self) -> str: - return f"StatementEvent@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -class StatementEventListener(Stub): - """ - javax.sql.StatementEventListener - Listener for statement pool events. - """ - def statementClosed(self, event: StatementEvent): - pass - - def statementErrorOccurred(self, event: StatementEvent): - pass - - def toString(self) -> str: - return f"StatementEventListener@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# XA stubs -# ============================================================ - -class XADataSource(Stub): - """ - javax.sql.XADataSource - Factory for XAConnection objects. - """ - def getXAConnection(self, user: str = None, password: str = None) -> "XAConnection": - return XAConnection() - - def toString(self) -> str: - return f"XADataSource@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -class XAConnection(PooledConnection): - """ - javax.sql.XAConnection - Connection for distributed transactions (stub). - """ - def __init__(self): - super().__init__() - - def getXAResource(self) -> "XAResource": - return XAResource() - - def toString(self) -> str: - return f"XAConnection@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -class XAResource(Stub): - """ - javax.transaction.xa.XAResource - Resource manager for distributed transactions (stub). - """ - TMENDRSCAN: int = 8388608 - TMFAIL: int = 536870912 - TMJOIN: int = 2097152 - TMNOFLAGS: int = 0 - TMONEPHASE: int = 1073741824 - TMRESUME: int = 134217728 - TMSTARTRSCAN: int = 16777216 - TMSUCCESS: int = 67108864 - TMSUSPEND: int = 33554432 - XA_RDONLY: int = 3 - XA_OK: int = 0 - - def start(self, xid: Any, flags: int): pass - def end(self, xid: Any, flags: int): pass - def prepare(self, xid: Any) -> int: return XA_OK - def commit(self, xid: Any, one_phase: bool): pass - def rollback(self, xid: Any): pass - def recover(self, flag: int) -> List: return [] - def forget(self, xid: Any): pass - def getTransactionTimeout(self) -> int: return 0 - def setTransactionTimeout(self, timeout: int) -> bool: return True - def isSameRM(self, xares: "XAResource") -> bool: return self is xares - - def toString(self) -> str: - return f"XAResource@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/org/__init__.py b/android_pyapi/org/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/android_pyapi/org/apache/__init__.py b/android_pyapi/org/apache/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/android_pyapi/org/apache/http.py b/android_pyapi/org/apache/http.py deleted file mode 100644 index 68f4a0f..0000000 --- a/android_pyapi/org/apache/http.py +++ /dev/null @@ -1,1109 +0,0 @@ -""" -Apache HTTP (legacy Android) APIs implemented in Python. -org.apache.http package - HttpEntity, HttpResponse, HttpClient, DefaultHttpClient, etc. -Desktop implementation wrapping Python requests module. -""" - -from __future__ import annotations - -from ..._core import Stub, AndroidOnlyError -from typing import List, Optional, Any - -# Optional requests dependency -try: - import requests as _requests - HAS_REQUESTS = True -except ImportError: - HAS_REQUESTS = False - -__all__ = [ - "HttpEntity", "HttpResponse", "HttpGet", "HttpPost", "HttpPut", - "HttpDelete", "HttpHead", "HttpOptions", "HttpTrace", "HttpPatch", - "HttpRequest", "HttpRequestInterceptor", "HttpResponseInterceptor", - "HttpContext", "HttpClient", "DefaultHttpClient", - "BasicHttpContext", "BasicHttpResponse", - "HttpVersion", "StatusLine", "Header", "BasicHeader", - "HeaderElement", "NameValuePair", "BasicNameValuePair", - "HttpResponseException", "HttpHost", - "HttpConnection", "HttpConnectionParams", - "HttpEntityEnclosingRequest", "RequestLine", "HttpParams", -] - - -# ============================================================ -# HttpVersion -# ============================================================ - -class HttpVersion(Stub): - """ - org.apache.http.HttpVersion - Represents an HTTP version. - """ - HTTP: "HttpVersion" = None - HTTP_0_9: "HttpVersion" = None - HTTP_1_0: "HttpVersion" = None - HTTP_1_1: "HttpVersion" = None - - def __init__(self, major: int = 1, minor: int = 1): - super().__init__() - self._major = major - self._minor = minor - self._protocol = "HTTP" - - def getProtocol(self) -> str: - return self._protocol - - def getMajor(self) -> int: - return self._major - - def getMinor(self) -> int: - return self._minor - - def toString(self) -> str: - return f"HTTP/{self._major}.{self._minor}" - - def equals(self, other) -> bool: - if isinstance(other, HttpVersion): - return self._major == other._major and self._minor == other._minor - return False - - def hashCode(self) -> int: - return hash((self._major, self._minor)) & 0x7FFFFFFF - - -HttpVersion.HTTP = HttpVersion(1, 1) -HttpVersion.HTTP_0_9 = HttpVersion(0, 9) -HttpVersion.HTTP_1_0 = HttpVersion(1, 0) -HttpVersion.HTTP_1_1 = HttpVersion(1, 1) - - -# ============================================================ -# StatusLine -# ============================================================ - -class StatusLine(Stub): - """ - org.apache.http.StatusLine - Represents an HTTP status line. - """ - def __init__(self, version: HttpVersion = None, status_code: int = 200, - reason_phrase: str = "OK"): - super().__init__() - self._version = version or HttpVersion.HTTP_1_1 - self._status_code = status_code - self._reason_phrase = reason_phrase - - def getProtocolVersion(self) -> HttpVersion: - return self._version - - def getStatusCode(self) -> int: - return self._status_code - - def getReasonPhrase(self) -> str: - return self._reason_phrase - - def toString(self) -> str: - return f"HTTP/{self._version._major}.{self._version._minor} {self._status_code} {self._reason_phrase}" - - def equals(self, other) -> bool: - if isinstance(other, StatusLine): - return (self._status_code == other._status_code and - self._reason_phrase == other._reason_phrase) - return False - - def hashCode(self) -> int: - return hash(self._status_code) & 0x7FFFFFFF - - -# ============================================================ -# Header -# ============================================================ - -class Header(Stub): - """ - org.apache.http.Header - Represents an HTTP header. - """ - def __init__(self, name: str = "", value: str = ""): - super().__init__() - self._name = name - self._value = value - - def getName(self) -> str: - return self._name - - def getValue(self) -> str: - return self._value - - def getElements(self) -> List["HeaderElement"]: - elements = [] - for part in self._value.split(","): - part = part.strip() - if part: - elements.append(HeaderElement(part)) - return elements - - def toString(self) -> str: - return f"{self._name}: {self._value}" - - def equals(self, other) -> bool: - if isinstance(other, Header): - return self._name == other._name and self._value == other._value - return False - - def hashCode(self) -> int: - return hash((self._name, self._value)) & 0x7FFFFFFF - - -class BasicHeader(Header): - """ - org.apache.http.message.BasicHeader - A basic implementation of an HTTP header. - """ - def __init__(self, name: str = "", value: str = ""): - super().__init__(name, value) - - def clone(self) -> "BasicHeader": - return BasicHeader(self._name, self._value) - - def toString(self) -> str: - return super().toString() - - def equals(self, other) -> bool: - return isinstance(other, BasicHeader) and super().equals(other) - - def hashCode(self) -> int: - return super().hashCode() - - -# ============================================================ -# HeaderElement -# ============================================================ - -class HeaderElement(Stub): - """ - org.apache.http.HeaderElement - Represents an element of an HTTP header. - """ - def __init__(self, value: str = ""): - super().__init__() - self._name = value.split("=")[0] if "=" in value else value - self._value = value.split("=", 1)[1] if "=" in value else None - self._parameters = [] - - def getName(self) -> str: - return self._name - - def getValue(self) -> Optional[str]: - return self._value - - def getParameterCount(self) -> int: - return len(self._parameters) - - def getParameters(self) -> List["NameValuePair"]: - return list(self._parameters) - - def getParameterByName(self, name: str) -> Optional["NameValuePair"]: - for p in self._parameters: - if p.getName() == name: - return p - return None - - def getParameter(self, index: int) -> Optional["NameValuePair"]: - if 0 <= index < len(self._parameters): - return self._parameters[index] - return None - - def toString(self) -> str: - return self._name - - def equals(self, other) -> bool: - if isinstance(other, HeaderElement): - return self._name == other._name and self._value == other._value - return False - - def hashCode(self) -> int: - return hash((self._name, self._value)) & 0x7FFFFFFF - - -# ============================================================ -# NameValuePair -# ============================================================ - -class NameValuePair(Stub): - """ - org.apache.http.NameValuePair - Represents a name-value pair. - """ - def __init__(self, name: str = "", value: str = ""): - super().__init__() - self._name = name - self._value = value - - def getName(self) -> str: - return self._name - - def getValue(self) -> str: - return self._value - - def toString(self) -> str: - return f"{self._name}={self._value}" - - def equals(self, other) -> bool: - if isinstance(other, NameValuePair): - return self._name == other._name and self._value == other._value - return False - - def hashCode(self) -> int: - return hash((self._name, self._value)) & 0x7FFFFFFF - - -class BasicNameValuePair(NameValuePair): - """ - org.apache.http.message.BasicNameValuePair - A basic implementation of a name-value pair. - """ - def __init__(self, name: str = "", value: str = ""): - super().__init__(name, value) - - def clone(self) -> "BasicNameValuePair": - return BasicNameValuePair(self._name, self._value) - - def toString(self) -> str: - return super().toString() - - def equals(self, other) -> bool: - return isinstance(other, BasicNameValuePair) and super().equals(other) - - def hashCode(self) -> int: - return super().hashCode() - - -# ============================================================ -# HttpHost -# ============================================================ - -class HttpHost(Stub): - """ - org.apache.http.HttpHost - Represents an HTTP host. - """ - def __init__(self, hostname: str = "", port: int = -1, scheme: str = "http"): - super().__init__() - self._hostname = hostname - self._port = port - self._scheme = scheme - - def getHostName(self) -> str: - return self._hostname - - def getPort(self) -> int: - return self._port - - def getSchemeName(self) -> str: - return self._scheme - - def toURI(self) -> str: - port_suffix = f":{self._port}" if self._port > 0 else "" - return f"{self._scheme}://{self._hostname}{port_suffix}" - - def toString(self) -> str: - return self.toURI() - - def equals(self, other) -> bool: - if isinstance(other, HttpHost): - return (self._hostname == other._hostname and - self._port == other._port and - self._scheme == other._scheme) - return False - - def hashCode(self) -> int: - return hash((self._hostname, self._port, self._scheme)) & 0x7FFFFFFF - - def clone(self) -> "HttpHost": - return HttpHost(self._hostname, self._port, self._scheme) - - -# ============================================================ -# RequestLine -# ============================================================ - -class RequestLine(Stub): - """ - org.apache.http.RequestLine - Represents an HTTP request line. - """ - def __init__(self, method: str = "GET", uri: str = "/", - version: HttpVersion = None): - super().__init__() - self._method = method - self._uri = uri - self._version = version or HttpVersion.HTTP_1_1 - - def getMethod(self) -> str: - return self._method - - def getUri(self) -> str: - return self._uri - - def getProtocolVersion(self) -> HttpVersion: - return self._version - - def toString(self) -> str: - return f"{self._method} {self._uri} {self._version}" - - def equals(self, other) -> bool: - if isinstance(other, RequestLine): - return (self._method == other._method and - self._uri == other._uri) - return False - - def hashCode(self) -> int: - return hash((self._method, self._uri)) & 0x7FFFFFFF - - -# ============================================================ -# HttpParams -# ============================================================ - -class HttpParams(Stub): - """ - org.apache.http.params.HttpParams - Collection of HTTP parameters. - """ - def __init__(self): - super().__init__() - self._params = {} - - def getParameter(self, name: str) -> Any: - return self._params.get(name) - - def setParameter(self, name: str, value: Any) -> "HttpParams": - self._params[name] = value - return self - - def removeParameter(self, name: str) -> bool: - return self._params.pop(name, None) is not None - - def getNamesParameter(self) -> List[str]: - return list(self._params.keys()) - - def setLongParameter(self, name: str, value: int) -> "HttpParams": - return self.setParameter(name, value) - - def getBooleanParameter(self, name: str, default: bool = False) -> bool: - return bool(self._params.get(name, default)) - - def setBooleanParameter(self, name: str, value: bool) -> "HttpParams": - return self.setParameter(name, value) - - def getIntParameter(self, name: str, default: int = 0) -> int: - return int(self._params.get(name, default)) - - def setIntParameter(self, name: str, value: int) -> "HttpParams": - return self.setParameter(name, value) - - def getLongParameter(self, name: str, default: int = 0) -> int: - return int(self._params.get(name, default)) - - def getDoubleParameter(self, name: str, default: float = 0.0) -> float: - return float(self._params.get(name, default)) - - def setDoubleParameter(self, name: str, value: float) -> "HttpParams": - return self.setParameter(name, value) - - def getShortParameter(self, name: str, default: int = 0) -> int: - return int(self._params.get(name, default)) - - def setShortParameter(self, name: str, value: int) -> "HttpParams": - return self.setParameter(name, value) - - def getByteParameter(self, name: str, default: int = 0) -> int: - return int(self._params.get(name, default)) - - def setByteParameter(self, name: str, value: int) -> "HttpParams": - return self.setParameter(name, value) - - def toString(self) -> str: - return f"HttpParams@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# HttpEntity -# ============================================================ - -class HttpEntity(Stub): - """ - org.apache.http.HttpEntity - Represents an HTTP entity body. - """ - def __init__(self, content: bytes = b'', content_type: Header = None, - content_encoding: Header = None, chunked: bool = False): - super().__init__() - self._content = content - self._content_type = content_type - self._content_encoding = content_encoding - self._chunked = chunked - self._streaming = False - - def getContent(self) -> bytes: - return self._content - - def getContentLength(self) -> int: - return len(self._content) - - def getContentType(self) -> Optional[Header]: - return self._content_type - - def getContentEncoding(self) -> Optional[Header]: - return self._content_encoding - - def isChunked(self) -> bool: - return self._chunked - - def isStreaming(self) -> bool: - return self._streaming - - def consumeContent(self): - pass - - def writeTo(self, out_stream): - if hasattr(out_stream, 'write'): - out_stream.write(self._content) - - def toString(self) -> str: - return f"HttpEntity@{hex(id(self))}[length={len(self._content)}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# HttpRequest -# ============================================================ - -class HttpRequest(Stub): - """ - org.apache.http.HttpRequest - Represents an HTTP request message. - """ - def __init__(self, method: str = "GET", uri: str = "/"): - super().__init__() - self._method = method - self._uri = uri - self._headers = [] - self._params = HttpParams() - self._version = HttpVersion.HTTP_1_1 - - def getRequestLine(self) -> RequestLine: - return RequestLine(self._method, self._uri, self._version) - - def getMethod(self) -> str: - return self._method - - def getProtocolVersion(self) -> HttpVersion: - return self._version - - def addHeader(self, header: Header): - self._headers.append(header) - - def setHeader(self, header: Header): - self.removeHeaders(header.getName()) - self._headers.append(header) - - def setHeaders(self, headers: List[Header]): - self._headers = list(headers) - - def getAllHeaders(self) -> List[Header]: - return list(self._headers) - - def getHeaders(self, name: str) -> List[Header]: - return [h for h in self._headers if h.getName().lower() == name.lower()] - - def getFirstHeader(self, name: str) -> Optional[Header]: - for h in self._headers: - if h.getName().lower() == name.lower(): - return h - return None - - def getLastHeader(self, name: str) -> Optional[Header]: - result = None - for h in self._headers: - if h.getName().lower() == name.lower(): - result = h - return result - - def removeHeader(self, header: Header): - if header in self._headers: - self._headers.remove(header) - - def removeHeaders(self, name: str): - self._headers = [h for h in self._headers if h.getName().lower() != name.lower()] - - def containsHeader(self, name: str) -> bool: - return any(h.getName().lower() == name.lower() for h in self._headers) - - def headerIterator(self, name: str = None): - if name: - return iter(self.getHeaders(name)) - return iter(self._headers) - - def getParams(self) -> HttpParams: - return self._params - - def setParams(self, params: HttpParams): - self._params = params - - def getURI(self) -> str: - return self._uri - - def setURI(self, uri: str): - self._uri = uri - - def toString(self) -> str: - return f"{self._method} {self._uri} HTTP/{self._version._major}.{self._version._minor}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# HTTP method classes -# ============================================================ - -class HttpGet(HttpRequest): - """org.apache.http.client.methods.HttpGet""" - def __init__(self, uri: str = "/"): - super().__init__("GET", uri) - - -class HttpPost(HttpRequest): - """org.apache.http.client.methods.HttpPost""" - def __init__(self, uri: str = "/"): - super().__init__("POST", uri) - - -class HttpPut(HttpRequest): - """org.apache.http.client.methods.HttpPut""" - def __init__(self, uri: str = "/"): - super().__init__("PUT", uri) - - -class HttpDelete(HttpRequest): - """org.apache.http.client.methods.HttpDelete""" - def __init__(self, uri: str = "/"): - super().__init__("DELETE", uri) - - -class HttpHead(HttpRequest): - """org.apache.http.client.methods.HttpHead""" - def __init__(self, uri: str = "/"): - super().__init__("HEAD", uri) - - -class HttpOptions(HttpRequest): - """org.apache.http.client.methods.HttpOptions""" - def __init__(self, uri: str = "/"): - super().__init__("OPTIONS", uri) - - -class HttpTrace(HttpRequest): - """org.apache.http.client.methods.HttpTrace""" - def __init__(self, uri: str = "/"): - super().__init__("TRACE", uri) - - -class HttpPatch(HttpRequest): - """org.apache.http.client.methods.HttpPatch""" - def __init__(self, uri: str = "/"): - super().__init__("PATCH", uri) - - -# ============================================================ -# HttpEntityEnclosingRequest -# ============================================================ - -class HttpEntityEnclosingRequest(HttpRequest): - """ - org.apache.http.HttpEntityEnclosingRequest - An HTTP request that can contain an entity. - """ - def __init__(self, method: str = "POST", uri: str = "/"): - super().__init__(method, uri) - self._entity = None - - def expectContinue(self) -> bool: - return self.containsHeader("Expect") - - def getEntity(self) -> HttpEntity: - return self._entity - - def setEntity(self, entity: HttpEntity): - self._entity = entity - - def toString(self) -> str: - return f"{self._method} {self._uri} [entity={self._entity is not None}]" - - -# ============================================================ -# HttpResponse -# ============================================================ - -class HttpResponse(Stub): - """ - org.apache.http.HttpResponse - Represents an HTTP response message. - """ - def __init__(self, status_line: StatusLine = None): - super().__init__() - self._status_line = status_line or StatusLine() - self._entity = None - self._headers = [] - self._params = HttpParams() - self._locale = "en_US" - self._protocol_version = HttpVersion.HTTP_1_1 - - def getStatusLine(self) -> StatusLine: - return self._status_line - - def setStatusLine(self, status_line: StatusLine): - self._status_line = status_line - - def getEntity(self) -> HttpEntity: - return self._entity - - def setEntity(self, entity: HttpEntity): - self._entity = entity - - def getAllHeaders(self) -> List[Header]: - return list(self._headers) - - def getFirstHeader(self, name: str) -> Optional[Header]: - for h in self._headers: - if h.getName().lower() == name.lower(): - return h - return None - - def getHeaders(self, name: str) -> List[Header]: - return [h for h in self._headers if h.getName().lower() == name.lower()] - - def getLastHeader(self, name: str) -> Optional[Header]: - result = None - for h in self._headers: - if h.getName().lower() == name.lower(): - result = h - return result - - def setHeaders(self, headers: List[Header]): - self._headers = list(headers) - - def addHeader(self, header: Header): - self._headers.append(header) - - def removeHeader(self, header: Header): - if header in self._headers: - self._headers.remove(header) - - def containsHeader(self, name: str) -> bool: - return any(h.getName().lower() == name.lower() for h in self._headers) - - def headerIterator(self, name: str = None): - if name: - return iter(self.getHeaders(name)) - return iter(self._headers) - - def getParams(self) -> HttpParams: - return self._params - - def setParams(self, params: HttpParams): - self._params = params - - def getProtocolVersion(self) -> HttpVersion: - return self._protocol_version - - def getLocale(self) -> str: - return self._locale - - def setLocale(self, locale: str): - self._locale = locale - - def toString(self) -> str: - return f"HTTP/{self._protocol_version._major}.{self._protocol_version._minor} {self._status_line.getStatusCode()}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# BasicHttpResponse -# ============================================================ - -class BasicHttpResponse(HttpResponse): - """org.apache.http.message.BasicHttpResponse""" - def __init__(self, status_line: StatusLine = None): - super().__init__(status_line) - - def toString(self) -> str: - return super().toString() - - def equals(self, other) -> bool: - return isinstance(other, BasicHttpResponse) and super().equals(other) - - def hashCode(self) -> int: - return super().hashCode() - - -# ============================================================ -# HttpRequestInterceptor -# ============================================================ - -class HttpRequestInterceptor(Stub): - """org.apache.http.HttpRequestInterceptor""" - def process(self, request: HttpRequest, context: "HttpContext"): - pass - def toString(self) -> str: - return f"HttpRequestInterceptor@{hex(id(self))}" - def equals(self, other) -> bool: - return self is other - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -class HttpResponseInterceptor(Stub): - """org.apache.http.HttpResponseInterceptor""" - def process(self, response: HttpResponse, context: "HttpContext"): - pass - def toString(self) -> str: - return f"HttpResponseInterceptor@{hex(id(self))}" - def equals(self, other) -> bool: - return self is other - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# HttpContext -# ============================================================ - -class HttpContext(Stub): - """ - org.apache.http.protocol.HttpContext - Represents an execution context for HTTP requests. - """ - RESERVED_PREFIX: str = "http." - def __init__(self): - super().__init__() - self._attributes = {} - def getAttribute(self, name: str) -> Any: - return self._attributes.get(name) - def setAttribute(self, name: str, value: Any): - self._attributes[name] = value - def removeAttribute(self, name: str) -> bool: - return self._attributes.pop(name, None) is not None - def toString(self) -> str: - return f"HttpContext@{hex(id(self))}" - def equals(self, other) -> bool: - return self is other - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -class BasicHttpContext(HttpContext): - """org.apache.http.protocol.BasicHttpContext""" - def __init__(self): - super().__init__() - def toString(self) -> str: - return f"BasicHttpContext@{hex(id(self))}" - def equals(self, other) -> bool: - return isinstance(other, BasicHttpContext) - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# HttpClient -# ============================================================ - -class HttpClient(Stub): - """ - org.apache.http.client.HttpClient - Interface for HTTP client implementations. - Wraps Python requests library. - """ - def __init__(self): - super().__init__() - self._connection_manager = None - self._params = HttpParams() - - def execute(self, target: Any, request: HttpRequest = None, - context: HttpContext = None, response_handler: Any = None) -> Any: - """Executes an HTTP request.""" - if isinstance(target, HttpHost): - host = target - req = request or HttpGet("/") - url = f"{host.getSchemeName()}://{host.getHostName()}" - if host.getPort() > 0: - url += f":{host.getPort()}" - url += req.getURI() - elif isinstance(target, HttpRequest): - req = target - url = req.getURI() - else: - url = str(target) - req = request or HttpGet("/") - - method = req.getMethod().upper() if hasattr(req, 'getMethod') else "GET" - - # Build headers dict - headers = {} - for h in req.getAllHeaders(): - headers[h.getName()] = h.getValue() - - try: - if HAS_REQUESTS: - resp = _requests.request(method, url, headers=headers, timeout=30) - # Build response - response = BasicHttpResponse( - StatusLine(HttpVersion.HTTP_1_1, resp.status_code, resp.reason)) - entity = HttpEntity(resp.content, - BasicHeader("Content-Type", resp.headers.get("Content-Type", ""))) - response.setEntity(entity) - for k, v in resp.headers.items(): - response.addHeader(BasicHeader(k, v)) - return response - except Exception: - pass - - # Fallback: create a stub response - response = BasicHttpResponse(StatusLine(HttpVersion.HTTP_1_1, 200, "OK")) - response.setEntity(HttpEntity(b'{}')) - return response - - def getParams(self) -> HttpParams: - return self._params - - def getConnectionManager(self): - return self._connection_manager - - -# ============================================================ -# DefaultHttpClient -# ============================================================ - -class DefaultHttpClient(HttpClient): - """ - org.apache.http.impl.client.DefaultHttpClient - Default implementation of HttpClient. - """ - def __init__(self): - super().__init__() - self._credentials_provider = None - self._cookie_store = None - self._retry_handler = None - self._redirect_handler = None - - def getCredentialsProvider(self): - return self._credentials_provider - - def setCredentialsProvider(self, provider): - self._credentials_provider = provider - - def getCookieStore(self): - return self._cookie_store - - def setCookieStore(self, store): - self._cookie_store = store - - def getHttpRequestRetryHandler(self): - return self._retry_handler - - def setHttpRequestRetryHandler(self, handler): - self._retry_handler = handler - - def getRedirectHandler(self): - return self._redirect_handler - - def setRedirectHandler(self, handler): - self._redirect_handler = handler - - def toString(self) -> str: - return f"DefaultHttpClient@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# HttpResponseException -# ============================================================ - -class HttpResponseException(Exception, Stub): - """ - org.apache.http.client.HttpResponseException - Exception for HTTP response errors. - """ - def __init__(self, status_code: int = 500, reason_phrase: str = "Internal Server Error"): - super().__init__(f"{status_code}: {reason_phrase}") - self._status_code = status_code - self._reason_phrase = reason_phrase - - def getStatusCode(self) -> int: - return self._status_code - - def getReasonPhrase(self) -> str: - return self._reason_phrase - - def toString(self) -> str: - return f"HttpResponseException: {self._status_code} {self._reason_phrase}" - - def equals(self, other) -> bool: - if isinstance(other, HttpResponseException): - return (self._status_code == other._status_code and - self._reason_phrase == other._reason_phrase) - return False - - def hashCode(self) -> int: - return hash((self._status_code, self._reason_phrase)) & 0x7FFFFFFF - - -# ============================================================ -# HttpConnection -# ============================================================ - -class HttpConnection(Stub): - """ - org.apache.http.HttpConnection - Represents an HTTP connection. - """ - def __init__(self): - super().__init__() - self._open = True - self._socket_timeout = 0 - self._local_address = None - self._local_port = 0 - self._remote_address = None - self._remote_port = 0 - - def isOpen(self) -> bool: - return self._open - - def isStale(self) -> bool: - return not self._open - - def setSocketTimeout(self, timeout: int): - self._socket_timeout = timeout - - def getSocketTimeout(self) -> int: - return self._socket_timeout - - def close(self): - self._open = False - - def shutdown(self): - self._open = False - - def getLocalAddress(self): - return self._local_address - - def getLocalPort(self) -> int: - return self._local_port - - def getRemoteAddress(self): - return self._remote_address - - def getRemotePort(self) -> int: - return self._remote_port - - def toString(self) -> str: - return f"HttpConnection@{hex(id(self))}[open={self._open}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# HttpConnectionParams -# ============================================================ - -class HttpConnectionParams(Stub): - """ - org.apache.http.params.HttpConnectionParams - Parameters for HTTP connections. - """ - @staticmethod - def setConnectionTimeout(params: HttpParams, timeout: int): - params.setIntParameter("http.connection.timeout", timeout) - - @staticmethod - def getConnectionTimeout(params: HttpParams) -> int: - return params.getIntParameter("http.connection.timeout", 0) - - @staticmethod - def setSoTimeout(params: HttpParams, timeout: int): - params.setIntParameter("http.socket.timeout", timeout) - - @staticmethod - def getSoTimeout(params: HttpParams) -> int: - return params.getIntParameter("http.socket.timeout", 0) - - @staticmethod - def setLinger(params: HttpParams, linger: int): - params.setIntParameter("http.socket.linger", linger) - - @staticmethod - def getLinger(params: HttpParams) -> int: - return params.getIntParameter("http.socket.linger", 0) - - @staticmethod - def setTcpNoDelay(params: HttpParams, value: bool): - params.setBooleanParameter("http.tcp.nodelay", value) - - @staticmethod - def getTcpNoDelay(params: HttpParams) -> bool: - return params.getBooleanParameter("http.tcp.nodelay", True) - - @staticmethod - def setSocketBufferSize(params: HttpParams, size: int): - params.setIntParameter("http.socket.buffer-size", size) - - @staticmethod - def getSocketBufferSize(params: HttpParams) -> int: - return params.getIntParameter("http.socket.buffer-size", 8192) - - @staticmethod - def setStaleCheckingEnabled(params: HttpParams, value: bool): - params.setBooleanParameter("http.connection.stalecheck", value) - - @staticmethod - def isStaleCheckingEnabled(params: HttpParams) -> bool: - return params.getBooleanParameter("http.connection.stalecheck", True) - - def toString(self) -> str: - return "HttpConnectionParams" - - def equals(self, other) -> bool: - return isinstance(other, HttpConnectionParams) - - def hashCode(self) -> int: - return hash("HttpConnectionParams") & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/org/json.py b/android_pyapi/org/json.py deleted file mode 100644 index 2838e4e..0000000 --- a/android_pyapi/org/json.py +++ /dev/null @@ -1,910 +0,0 @@ -""" -Java JSON standard library APIs implemented in Python. -org.json package - JSONObject, JSONArray, JSONTokener, JSONStringer, JSONWriter, etc. -""" - -from .._core import Stub -import json as _json - -__all__ = [ - "JSONObject", "JSONArray", "JSONTokener", - "JSONStringer", "JSONWriter", "JSONException", "JSONPointer", - "JSONPointerBuilder", -] - - -# ============================================================ -# JSONException -# ============================================================ - -class JSONException(Exception): - def __init__(self, *args): - if len(args) == 1 and isinstance(args[0], Exception): - super().__init__(str(args[0])) - self._cause = args[0] - else: - super().__init__(*args) - self._cause = None - - def getCause(self): - return self._cause - - -# ============================================================ -# JSONObject.NULL -# ============================================================ - -class _JSONNull(Stub): - def __init__(self): - super().__init__() - - def equals(self, other): - return other is None or isinstance(other, _JSONNull) - - def hashCode(self): - return 0 - - def toString(self): - return "null" - - -# ============================================================ -# JSONObject -# ============================================================ - -class JSONObject(Stub): - NULL = _JSONNull() - - def __init__(self, *args): - super().__init__() - self._map = {} - if len(args) == 1: - arg = args[0] - if isinstance(arg, str): - tokener = JSONTokener(arg) - obj = tokener.nextValue() - if isinstance(obj, JSONObject): - self._map = obj._map - else: - raise JSONException("String is not a JSONObject") - elif isinstance(arg, dict): - for k, v in arg.items(): - self._map[str(k)] = JSONObject._wrap(v) - elif isinstance(arg, JSONObject): - names = None - self._map = dict(arg._map) - - def _wrap(val): - if val is None: - return JSONObject.NULL - if isinstance(val, (bool, int, float)): - return val - if isinstance(val, str): - return val - if isinstance(val, (list, tuple)): - arr = JSONArray() - for item in val: - arr.put(JSONObject._wrap(item)) - return arr - if isinstance(val, dict): - obj = JSONObject() - for k, v in val.items(): - obj.put(str(k), JSONObject._wrap(v)) - return obj - if isinstance(val, JSONObject) or isinstance(val, JSONArray): - return val - return val - - def put(self, key, value): - self._map[str(key)] = JSONObject._wrap(value) - return self - - def accumulate(self, key, value): - k = str(key) - current = self._map.get(k) - if current is None: - arr = JSONArray() - arr.put(value) - self._map[k] = arr - elif isinstance(current, JSONArray): - current.put(value) - else: - arr = JSONArray() - arr.put(current) - arr.put(value) - self._map[k] = arr - return self - - def append(self, key, value): - """Append value to the JSONArray under key. - - If the key is not present a new JSONArray containing value is created. - If the key maps to a JSONArray the value is appended to it. - Otherwise the existing value and the new value are placed into a new - JSONArray. - """ - k = str(key) - current = self._map.get(k) - if isinstance(current, JSONArray): - current.put(value) - else: - arr = JSONArray() - if current is not None and current is not JSONObject.NULL: - arr.put(current) - arr.put(value) - self._map[k] = arr - return self - - def putOnce(self, key, value): - """Put a key/value pair only if the key is not already present.""" - if key is None: - raise JSONException("Null key.") - k = str(key) - if k not in self._map: - self._map[k] = JSONObject._wrap(value) - return self - - def putOpt(self, key, value): - """Put a key/value pair, but only if key and value are both non-null.""" - if key is not None and value is not None: - return self.put(key, value) - return self - - def get(self, key): - k = str(key) - if k not in self._map: - raise JSONException(f"JSONObject[\"{k}\"] not found") - val = self._map[k] - if val is JSONObject.NULL: - raise JSONException(f"JSONObject[\"{k}\"] is null") - return val - - def opt(self, key, default=None): - k = str(key) - val = self._map.get(k) - if val is None or val is JSONObject.NULL: - return default - return val - - def getString(self, key): - val = self.get(key) - if isinstance(val, str): - return val - raise JSONException(f"JSONObject[\"{key}\"] is not a string") - - def getInt(self, key): - val = self.get(key) - if isinstance(val, int): - return val - if isinstance(val, float): - return int(val) - raise JSONException(f"JSONObject[\"{key}\"] is not an int") - - def getLong(self, key): - val = self.get(key) - if isinstance(val, int): - return val - if isinstance(val, float): - return int(val) - raise JSONException(f"JSONObject[\"{key}\"] is not a long") - - def getDouble(self, key): - val = self.get(key) - if isinstance(val, (int, float)): - return float(val) - raise JSONException(f"JSONObject[\"{key}\"] is not a double") - - def getBoolean(self, key): - val = self.get(key) - if isinstance(val, bool): - return val - if val == "true": - return True - if val == "false": - return False - raise JSONException(f"JSONObject[\"{key}\"] is not a boolean") - - def getJSONArray(self, key): - val = self.get(key) - if isinstance(val, JSONArray): - return val - raise JSONException(f"JSONObject[\"{key}\"] is not a JSONArray") - - def getJSONObject(self, key): - val = self.get(key) - if isinstance(val, JSONObject): - return val - raise JSONException(f"JSONObject[\"{key}\"] is not a JSONObject") - - def optString(self, key, default=""): - val = self.opt(key) - if val is None or val is JSONObject.NULL: - return default - return str(val) - - def optInt(self, key, default=0): - val = self.opt(key) - if val is None or val is JSONObject.NULL: - return default - if isinstance(val, int): - return val - if isinstance(val, float): - return int(val) - try: - return int(val) - except (ValueError, TypeError): - return default - - def optLong(self, key, default=0): - return self.optInt(key, default) - - def optDouble(self, key, default=0.0): - val = self.opt(key) - if val is None or val is JSONObject.NULL: - return default - if isinstance(val, (int, float)): - return float(val) - try: - return float(val) - except (ValueError, TypeError): - return default - - def optBoolean(self, key, default=False): - val = self.opt(key) - if val is None or val is JSONObject.NULL: - return default - if isinstance(val, bool): - return val - if isinstance(val, str): - return val.lower() == "true" - return default - - def optJSONArray(self, key): - val = self.opt(key) - if isinstance(val, JSONArray): - return val - return None - - def optJSONObject(self, key): - val = self.opt(key) - if isinstance(val, JSONObject): - return val - return None - - def has(self, key): - return str(key) in self._map - - def isNull(self, key): - """Returns true if this object has no mapping for key or if it has a - mapping whose value is JSONObject.NULL.""" - k = str(key) - if k not in self._map: - return True - return self._map[k] is JSONObject.NULL - - def remove(self, key): - k = str(key) - if k in self._map: - del self._map[k] - - def keys(self): - return iter(self._map.keys()) - - def names(self): - if not self._map: - return None - arr = JSONArray() - for k in self._map: - arr.put(k) - return arr - - @staticmethod - def getNames(jsonObject): - """Returns an array of field names, or None if the object is None/empty.""" - if jsonObject is None: - return None - if not jsonObject._map: - return None - return [str(k) for k in jsonObject._map.keys()] - - def length(self): - return len(self._map) - - def toString(self, indent_factor=None): - if indent_factor is None: - return self._to_json_string() - return self._to_json_pretty(indent_factor) - - def _to_json_string(self): - parts = ["{"] - for i, (k, v) in enumerate(self._map.items()): - parts.append('"' + k + '": ' + JSONObject._value_to_string(v)) - if i < len(self._map) - 1: - parts.append(", ") - parts.append("}") - return "".join(parts) - - def _to_json_pretty(self, indent): - parts = ["{"] - for i, (k, v) in enumerate(self._map.items()): - parts.append("\n" + " " * indent + '"' + k + '": ' + JSONObject._value_to_string(v)) - if i < len(self._map) - 1: - parts.append(",") - parts.append("\n}") - return "".join(parts) - - def _value_to_string(val): - if val is None or val is JSONObject.NULL: - return "null" - if isinstance(val, bool): - return "true" if val else "false" - if isinstance(val, (int, float)): - return str(val) - if isinstance(val, str): - return _json.dumps(val) - if isinstance(val, JSONObject): - return val._to_json_string() - if isinstance(val, JSONArray): - return val._to_json_string() - return str(val) - - def toMap(self): - result = {} - for k, v in self._map.items(): - if isinstance(v, JSONObject): - result[k] = v.toMap() - elif isinstance(v, JSONArray): - result[k] = v.toList() - elif v is JSONObject.NULL: - result[k] = None - else: - result[k] = v - return result - - def write(self, writer): - writer._write(self._to_json_string()) - - def toJSONArray(self, names): - if names is None or names.length() == 0: - return None - arr = JSONArray() - for i in range(names.length()): - arr.put(self.opt(names.getString(i))) - return arr - - def hashCode(self): - return hash(frozenset(self._map.items())) - - def equals(self, other): - if isinstance(other, JSONObject): - return self._map == other._map - return False - - -# ============================================================ -# JSONArray -# ============================================================ - -class JSONArray(Stub): - def __init__(self, *args): - super().__init__() - self._list = [] - if len(args) == 1: - arg = args[0] - if isinstance(arg, str): - tokener = JSONTokener(arg) - val = tokener.nextValue() - if isinstance(val, JSONArray): - self._list = val._list - else: - raise JSONException("String is not a JSONArray") - elif isinstance(arg, (list, tuple)): - for item in arg: - self.put(JSONObject._wrap(item)) - - def put(self, *args): - if len(args) == 1: - self._list.append(JSONObject._wrap(args[0])) - elif len(args) == 2: - index, value = args - while len(self._list) <= index: - self._list.append(None) - self._list[index] = JSONObject._wrap(value) - return self - - def get(self, index): - if index < 0 or index >= len(self._list): - raise JSONException(f"JSONArray[{index}] not found") - val = self._list[index] - if val is JSONObject.NULL: - raise JSONException(f"JSONArray[{index}] is null") - return val - - def getString(self, index): - val = self.get(index) - if isinstance(val, str): - return val - raise JSONException(f"JSONArray[{index}] is not a string") - - def getInt(self, index): - val = self.get(index) - if isinstance(val, int): - return val - if isinstance(val, float): - return int(val) - raise JSONException(f"JSONArray[{index}] is not an int") - - def getLong(self, index): - val = self.get(index) - if isinstance(val, int): - return val - if isinstance(val, float): - return int(val) - raise JSONException(f"JSONArray[{index}] is not a long") - - def getDouble(self, index): - val = self.get(index) - if isinstance(val, (int, float)): - return float(val) - raise JSONException(f"JSONArray[{index}] is not a double") - - def getBoolean(self, index): - val = self.get(index) - if isinstance(val, bool): - return val - if val == "true": - return True - if val == "false": - return False - raise JSONException(f"JSONArray[{index}] is not a boolean") - - def getJSONArray(self, index): - val = self.get(index) - if isinstance(val, JSONArray): - return val - raise JSONException(f"JSONArray[{index}] is not a JSONArray") - - def getJSONObject(self, index): - val = self.get(index) - if isinstance(val, JSONObject): - return val - raise JSONException(f"JSONArray[{index}] is not a JSONObject") - - def opt(self, index, default=None): - if index < 0 or index >= len(self._list): - return default - val = self._list[index] - if val is JSONObject.NULL: - return default - return val - - def optString(self, index, default=""): - val = self.opt(index) - if val is None: - return default - return str(val) - - def optInt(self, index, default=0): - val = self.opt(index) - if val is None: - return default - if isinstance(val, int): - return val - if isinstance(val, float): - return int(val) - try: - return int(val) - except (ValueError, TypeError): - return default - - def optLong(self, index, default=0): - return self.optInt(index, default) - - def optDouble(self, index, default=0.0): - val = self.opt(index) - if val is None: - return default - if isinstance(val, (int, float)): - return float(val) - try: - return float(val) - except (ValueError, TypeError): - return default - - def optBoolean(self, index, default=False): - val = self.opt(index) - if val is None: - return default - if isinstance(val, bool): - return val - if isinstance(val, str): - return val.lower() == "true" - return default - - def optJSONArray(self, index): - val = self.opt(index) - if isinstance(val, JSONArray): - return val - return None - - def optJSONObject(self, index): - val = self.opt(index) - if isinstance(val, JSONObject): - return val - return None - - def remove(self, index): - if 0 <= index < len(self._list): - return self._list.pop(index) - return None - - def length(self): - return len(self._list) - - def isNull(self, index): - if 0 <= index < len(self._list): - return self._list[index] is JSONObject.NULL or self._list[index] is None - return True - - def toString(self, indent_factor=None): - if indent_factor is None: - return self._to_json_string() - return self._to_json_pretty(indent_factor) - - def _to_json_string(self): - parts = ["["] - for i, v in enumerate(self._list): - parts.append(JSONObject._value_to_string(v)) - if i < len(self._list) - 1: - parts.append(", ") - parts.append("]") - return "".join(parts) - - def _to_json_pretty(self, indent): - parts = ["["] - for i, v in enumerate(self._list): - parts.append("\n" + " " * indent + JSONObject._value_to_string(v)) - if i < len(self._list) - 1: - parts.append(",") - parts.append("\n]") - return "".join(parts) - - def toList(self): - result = [] - for v in self._list: - if isinstance(v, JSONObject): - result.append(v.toMap()) - elif isinstance(v, JSONArray): - result.append(v.toList()) - elif v is JSONObject.NULL: - result.append(None) - else: - result.append(v) - return result - - def join(self, separator): - parts = [] - for v in self._list: - parts.append(JSONObject._value_to_string(v)) - return separator.join(parts) - - -# ============================================================ -# JSONTokener -# ============================================================ - -class JSONTokener(Stub): - def __init__(self, source): - super().__init__() - if hasattr(source, "read"): - self._source = source.read() - else: - self._source = str(source) - self._index = 0 - - def nextValue(self): - self._skipWhitespace() - if self._index >= len(self._source): - raise JSONException("Unexpected end of input") - ch = self._source[self._index] - if ch == "{": - return self._readObject() - if ch == "[": - return self._readArray() - if ch == '"': - return self._readString() - if ch == "'": - return self._readString("'") - if ch == "t" or ch == "f": - return self._readLiteral() - if ch == "n": - return self._readNull() - return self._readNumber() - - def _skipWhitespace(self): - while self._index < len(self._source) and self._source[self._index] in " \t\n\r": - self._index += 1 - - def _readObject(self): - self._index += 1 # skip { - obj = JSONObject() - self._skipWhitespace() - if self._index < len(self._source) and self._source[self._index] == "}": - self._index += 1 - return obj - while True: - self._skipWhitespace() - key = self.nextValue() - self._skipWhitespace() - if self._index < len(self._source) and self._source[self._index] == ":": - self._index += 1 - self._skipWhitespace() - val = self.nextValue() - obj.put(str(key), val) - self._skipWhitespace() - if self._index < len(self._source) and self._source[self._index] == ",": - self._index += 1 - elif self._index < len(self._source) and self._source[self._index] == "}": - self._index += 1 - return obj - else: - raise JSONException("Expected ',' or '}' in object") - - def _readArray(self): - self._index += 1 # skip [ - arr = JSONArray() - self._skipWhitespace() - if self._index < len(self._source) and self._source[self._index] == "]": - self._index += 1 - return arr - while True: - self._skipWhitespace() - val = self.nextValue() - arr.put(val) - self._skipWhitespace() - if self._index < len(self._source) and self._source[self._index] == ",": - self._index += 1 - elif self._index < len(self._source) and self._source[self._index] == "]": - self._index += 1 - return arr - else: - raise JSONException("Expected ',' or ']' in array") - - def _readString(self, quote='"'): - self._index += 1 # skip quote - result = [] - while self._index < len(self._source): - ch = self._source[self._index] - if ch == "\\": - self._index += 1 - if self._index >= len(self._source): - raise JSONException("Unterminated string escape") - esc = self._source[self._index] - if esc == '"': - result.append('"') - elif esc == "\\": - result.append("\\") - elif esc == "/": - result.append("/") - elif esc == "b": - result.append("\b") - elif esc == "f": - result.append("\f") - elif esc == "n": - result.append("\n") - elif esc == "r": - result.append("\r") - elif esc == "t": - result.append("\t") - elif esc == "u": - hex_str = self._source[self._index+1:self._index+5] - result.append(chr(int(hex_str, 16))) - self._index += 4 - else: - result.append(esc) - self._index += 1 - elif ch == quote: - self._index += 1 - return "".join(result) - else: - result.append(ch) - self._index += 1 - raise JSONException("Unterminated string") - - def _readLiteral(self): - for lit in ("true", "false"): - if self._source[self._index:self._index+len(lit)] == lit: - self._index += len(lit) - return lit == "true" - raise JSONException(f"Unexpected literal at {self._index}") - - def _readNull(self): - if self._source[self._index:self._index+4] == "null": - self._index += 4 - return JSONObject.NULL - raise JSONException(f"Unexpected token at {self._index}") - - def _readNumber(self): - start = self._index - while self._index < len(self._source) and self._source[self._index] in "-+0123456789.eE": - self._index += 1 - num_str = self._source[start:self._index] - if "." in num_str or "e" in num_str or "E" in num_str: - return float(num_str) - return int(num_str) - - def nextString(self, quote='"'): - return self._readString(quote) - - def nextChar(self, c): - self._skipWhitespace() - if self._index < len(self._source) and self._source[self._index] == c: - self._index += 1 - return c - raise JSONException(f"Expected '{c}'") - - def nextTo(self, delimiter): - start = self._index - while self._index < len(self._source) and self._source[self._index] not in delimiter: - self._index += 1 - return self._source[start:self._index] - - def skipPast(self, text): - idx = self._source.find(text, self._index) - if idx >= 0: - self._index = idx + len(text) - - def skipTo(self, c): - idx = self._source.find(c, self._index) - if idx >= 0: - self._index = idx - return idx - return -1 - - def back(self): - self._index -= 1 - - def more(self): - self._skipWhitespace() - return self._index < len(self._source) - - def readString(self): - return self._readString() - - -# ============================================================ -# JSONWriter -# ============================================================ - -class JSONWriter(Stub): - def __init__(self, writer): - super().__init__() - self._writer = writer - self._stack = [] - self._in_object = False - self._need_comma = False - - def _write(self, s): - self._writer.write(s) - - def array(self): - self._beforeValue() - self._stack.append(("array", False)) - self._write("[") - return self - - def endArray(self): - self._write("]") - self._stack.pop() - return self - - def object(self): - self._beforeValue() - self._stack.append(("object", False)) - self._write("{") - return self - - def endObject(self): - self._write("}") - self._stack.pop() - return self - - def key(self, s): - if not self._stack or self._stack[-1][0] != "object": - raise JSONException("Can't set key outside object") - self._beforeKey() - self._write('"' + s + '":') - return self - - def value(self, v): - self._beforeValue() - self._write(JSONObject._value_to_string(v)) - return self - - def _beforeValue(self): - if self._stack and self._stack[-1][0] == "object": - raise JSONException("Expected key in object") - - def _beforeKey(self): - if self._stack and self._stack[-1][1]: - self._write(",") - self._stack[-1] = (self._stack[-1][0], True) - - -# ============================================================ -# JSONStringer -# ============================================================ - -class JSONStringer(JSONWriter): - def __init__(self): - from io import StringIO - super().__init__(StringIO()) - - def toString(self): - return self._writer.getvalue() - - -# ============================================================ -# JSONPointer -# ============================================================ - -class JSONPointer(Stub): - def __init__(self, pointer): - super().__init__() - if not pointer or pointer == "": - self._tokens = [] - elif pointer.startswith("#"): - pointer = pointer[1:] - pointer = pointer.replace("~1", "/").replace("~0", "~") - import urllib.parse - pointer = urllib.parse.unquote(pointer) - self._tokens = pointer.split("/")[1:] if len(pointer) > 1 else [] - else: - self._tokens = pointer.split("/")[1:] if len(pointer) > 0 else [] - - def query(self, root): - current = root - for token in self._tokens: - token = token.replace("~1", "/").replace("~0", "~") - if isinstance(current, JSONObject): - current = current.get(token) - elif isinstance(current, JSONArray): - try: - idx = int(token) - current = current.get(idx) - except (ValueError, IndexError): - raise JSONException(f"Invalid index: {token}") - else: - raise JSONException(f"Cannot index into {type(current)}") - return current - - def queryFrom(self, root): - return self.query(root) - - def toString(self): - return "#/" + "/".join(t.replace("~", "~0").replace("/", "~1") for t in self._tokens) - - def hashCode(self): - return hash(tuple(self._tokens)) - - def equals(self, other): - if isinstance(other, JSONPointer): - return self._tokens == other._tokens - return False - - -# ============================================================ -# JSONPointerBuilder -# ============================================================ - -class JSONPointerBuilder(Stub): - def __init__(self): - super().__init__() - self._tokens = [] - - def append(self, token): - self._tokens.append(str(token)) - return self - - def build(self): - return JSONPointer("#/" + "/".join(t.replace("~", "~0").replace("/", "~1") for t in self._tokens)) \ No newline at end of file diff --git a/android_pyapi/org/w3c_dom.py b/android_pyapi/org/w3c_dom.py deleted file mode 100644 index 7e14d74..0000000 --- a/android_pyapi/org/w3c_dom.py +++ /dev/null @@ -1,1292 +0,0 @@ -""" -Java org.w3c.dom APIs implemented in Python. -org.w3c.dom package - Document, Node, Element, Attr, Text, etc. -Wraps Python xml.dom module. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -import xml.dom as _dom -import xml.dom.minidom as _minidom -from typing import List, Optional - -__all__ = [ - "Document", "Node", "Element", "Attr", "Text", "Comment", - "CDATASection", "DocumentType", "Notation", "Entity", - "EntityReference", "ProcessingInstruction", "NodeList", - "NamedNodeMap", "DOMImplementation", "DOMConfiguration", - "DOMException", "UserDataHandler", "DOMError", "DOMLocator", - "DOMErrorHandler", "TypeInfo", -] - - -# ============================================================ -# Constants -# ============================================================ - -class NodeConstants: - ELEMENT_NODE: int = 1 - ATTRIBUTE_NODE: int = 2 - TEXT_NODE: int = 3 - CDATA_SECTION_NODE: int = 4 - ENTITY_REFERENCE_NODE: int = 5 - ENTITY_NODE: int = 6 - PROCESSING_INSTRUCTION_NODE: int = 7 - COMMENT_NODE: int = 8 - DOCUMENT_NODE: int = 9 - DOCUMENT_TYPE_NODE: int = 10 - DOCUMENT_FRAGMENT_NODE: int = 11 - NOTATION_NODE: int = 12 - - DOCUMENT_POSITION_DISCONNECTED: int = 0x01 - DOCUMENT_POSITION_PRECEDING: int = 0x02 - DOCUMENT_POSITION_FOLLOWING: int = 0x04 - DOCUMENT_POSITION_CONTAINS: int = 0x08 - DOCUMENT_POSITION_CONTAINED_BY: int = 0x10 - DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: int = 0x20 - - -# ============================================================ -# DOMException -# ============================================================ - -class DOMException(Exception, Stub): - """ - org.w3c.dom.DOMException - Exception for DOM operations. - """ - INDEX_SIZE_ERR: int = 1 - DOMSTRING_SIZE_ERR: int = 2 - HIERARCHY_REQUEST_ERR: int = 3 - WRONG_DOCUMENT_ERR: int = 4 - INVALID_CHARACTER_ERR: int = 5 - NO_DATA_ALLOWED_ERR: int = 6 - NO_MODIFICATION_ALLOWED_ERR: int = 7 - NOT_FOUND_ERR: int = 8 - NOT_SUPPORTED_ERR: int = 9 - INUSE_ATTRIBUTE_ERR: int = 10 - INVALID_STATE_ERR: int = 11 - SYNTAX_ERR: int = 12 - INVALID_MODIFICATION_ERR: int = 13 - NAMESPACE_ERR: int = 14 - INVALID_ACCESS_ERR: int = 15 - VALIDATION_ERR: int = 16 - TYPE_MISMATCH_ERR: int = 17 - - def __init__(self, code: int = 0, message: str = None): - super().__init__(message or f"DOM error code {code}") - self._code = code - self._message = message - - def code(self) -> int: - return self._code - - def toString(self) -> str: - return f"DOMException[{self._code}]: {self._message}" - - def equals(self, other) -> bool: - if isinstance(other, DOMException): - return self._code == other._code - return False - - def hashCode(self) -> int: - return hash(self._code) & 0x7FFFFFFF - - -# ============================================================ -# NodeList -# ============================================================ - -class NodeList(Stub): - """ - org.w3c.dom.NodeList - Ordered list of nodes. - """ - def __init__(self, nodes: List = None): - super().__init__() - self._nodes = list(nodes) if nodes else [] - - def getLength(self) -> int: - return len(self._nodes) - - def item(self, index: int) -> Optional["Node"]: - if 0 <= index < len(self._nodes): - return self._nodes[index] - return None - - def __len__(self): - return len(self._nodes) - - def __iter__(self): - return iter(self._nodes) - - def __getitem__(self, index): - return self._nodes[index] - - def toString(self) -> str: - return f"NodeList@{hex(id(self))}[length={len(self._nodes)}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# NamedNodeMap -# ============================================================ - -class NamedNodeMap(Stub): - """ - org.w3c.dom.NamedNodeMap - Collection of nodes accessible by name. - """ - def __init__(self): - super().__init__() - self._nodes = {} - - def getNamedItem(self, name: str) -> Optional["Node"]: - return self._nodes.get(name) - - def setNamedItem(self, arg: "Node") -> Optional["Node"]: - name = arg.getNodeName() - old = self._nodes.get(name) - self._nodes[name] = arg - return old - - def removeNamedItem(self, name: str) -> Optional["Node"]: - return self._nodes.pop(name, None) - - def item(self, index: int) -> Optional["Node"]: - keys = list(self._nodes.keys()) - if 0 <= index < len(keys): - return self._nodes[keys[index]] - return None - - def getLength(self) -> int: - return len(self._nodes) - - def getNamedItemNS(self, namespace_uri: str, local_name: str) -> Optional["Node"]: - for name, node in self._nodes.items(): - if node.getNamespaceURI() == namespace_uri and node.getLocalName() == local_name: - return node - return None - - def setNamedItemNS(self, arg: "Node") -> Optional["Node"]: - return self.setNamedItem(arg) - - def removeNamedItemNS(self, namespace_uri: str, local_name: str) -> Optional["Node"]: - for name, node in list(self._nodes.items()): - if node.getNamespaceURI() == namespace_uri and node.getLocalName() == local_name: - return self._nodes.pop(name, None) - return None - - def toString(self) -> str: - return f"NamedNodeMap@{hex(id(self))}[length={len(self._nodes)}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Node -# ============================================================ - -class Node(Stub): - """ - org.w3c.dom.Node - The primary datatype for the DOM. - """ - ELEMENT_NODE: int = NodeConstants.ELEMENT_NODE - ATTRIBUTE_NODE: int = NodeConstants.ATTRIBUTE_NODE - TEXT_NODE: int = NodeConstants.TEXT_NODE - CDATA_SECTION_NODE: int = NodeConstants.CDATA_SECTION_NODE - ENTITY_REFERENCE_NODE: int = NodeConstants.ENTITY_REFERENCE_NODE - ENTITY_NODE: int = NodeConstants.ENTITY_NODE - PROCESSING_INSTRUCTION_NODE: int = NodeConstants.PROCESSING_INSTRUCTION_NODE - COMMENT_NODE: int = NodeConstants.COMMENT_NODE - DOCUMENT_NODE: int = NodeConstants.DOCUMENT_NODE - DOCUMENT_TYPE_NODE: int = NodeConstants.DOCUMENT_TYPE_NODE - DOCUMENT_FRAGMENT_NODE: int = NodeConstants.DOCUMENT_FRAGMENT_NODE - NOTATION_NODE: int = NodeConstants.NOTATION_NODE - DOCUMENT_POSITION_DISCONNECTED: int = NodeConstants.DOCUMENT_POSITION_DISCONNECTED - DOCUMENT_POSITION_PRECEDING: int = NodeConstants.DOCUMENT_POSITION_PRECEDING - DOCUMENT_POSITION_FOLLOWING: int = NodeConstants.DOCUMENT_POSITION_FOLLOWING - DOCUMENT_POSITION_CONTAINS: int = NodeConstants.DOCUMENT_POSITION_CONTAINS - DOCUMENT_POSITION_CONTAINED_BY: int = NodeConstants.DOCUMENT_POSITION_CONTAINED_BY - DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: int = NodeConstants.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC - - def __init__(self, node_type: int = ELEMENT_NODE, node_name: str = "", - node_value: str = None): - super().__init__() - self._node_type = node_type - self._node_name = node_name - self._node_value = node_value - self._parent_node = None - self._child_nodes = [] - self._first_child = None - self._last_child = None - self._previous_sibling = None - self._next_sibling = None - self._attributes = NamedNodeMap() - self._owner_document = None - self._namespace_uri = None - self._prefix = None - self._local_name = None - self._user_data = {} - - def getNodeName(self) -> str: - return self._node_name - - def getNodeValue(self) -> Optional[str]: - return self._node_value - - def setNodeValue(self, value: str): - self._node_value = value - - def getNodeType(self) -> int: - return self._node_type - - def getParentNode(self) -> Optional["Node"]: - return self._parent_node - - def getChildNodes(self) -> NodeList: - return NodeList(self._child_nodes) - - def getFirstChild(self) -> Optional["Node"]: - return self._child_nodes[0] if self._child_nodes else None - - def getLastChild(self) -> Optional["Node"]: - return self._child_nodes[-1] if self._child_nodes else None - - def getPreviousSibling(self) -> Optional["Node"]: - return self._previous_sibling - - def getNextSibling(self) -> Optional["Node"]: - return self._next_sibling - - def getAttributes(self) -> NamedNodeMap: - return self._attributes - - def getOwnerDocument(self) -> Optional["Document"]: - return self._owner_document - - def insertBefore(self, new_child: "Node", ref_child: Optional["Node"]) -> "Node": - if ref_child is None: - return self.appendChild(new_child) - idx = self._child_nodes.index(ref_child) if ref_child in self._child_nodes else len(self._child_nodes) - self._child_nodes.insert(idx, new_child) - new_child._parent_node = self - self._update_siblings() - return new_child - - def replaceChild(self, new_child: "Node", old_child: "Node") -> "Node": - if old_child in self._child_nodes: - idx = self._child_nodes.index(old_child) - self._child_nodes[idx] = new_child - new_child._parent_node = self - self._update_siblings() - return old_child - return None - - def removeChild(self, old_child: "Node") -> "Node": - if old_child in self._child_nodes: - self._child_nodes.remove(old_child) - old_child._parent_node = None - self._update_siblings() - return old_child - return None - - def appendChild(self, new_child: "Node") -> "Node": - self._child_nodes.append(new_child) - new_child._parent_node = self - self._update_siblings() - return new_child - - def hasChildNodes(self) -> bool: - return len(self._child_nodes) > 0 - - def cloneNode(self, deep: bool) -> "Node": - cloned = Node(self._node_type, self._node_name, self._node_value) - if deep: - for child in self._child_nodes: - cloned.appendChild(child.cloneNode(True)) - return cloned - - def normalize(self): - """Merges adjacent text nodes.""" - i = 0 - while i < len(self._child_nodes) - 1: - if (self._child_nodes[i].getNodeType() == Node.TEXT_NODE and - self._child_nodes[i + 1].getNodeType() == Node.TEXT_NODE): - combined = self._child_nodes[i].getNodeValue() + self._child_nodes[i + 1].getNodeValue() - self._child_nodes[i].setNodeValue(combined) - self._child_nodes.pop(i + 1) - self._update_siblings() - else: - i += 1 - - def isSupported(self, feature: str, version: str) -> bool: - return feature in ("XML", "DOM", "Core") - - def getNamespaceURI(self) -> Optional[str]: - return self._namespace_uri - - def getPrefix(self) -> Optional[str]: - return self._prefix - - def setPrefix(self, prefix: str): - self._prefix = prefix - - def getLocalName(self) -> Optional[str]: - return self._local_name - - def hasAttributes(self) -> bool: - return self._attributes.getLength() > 0 - - def lookupNamespaceURI(self, prefix: str) -> Optional[str]: - if self._prefix == prefix: - return self._namespace_uri - if self._parent_node: - return self._parent_node.lookupNamespaceURI(prefix) - return None - - def lookupPrefix(self, namespace_uri: str) -> Optional[str]: - if self._namespace_uri == namespace_uri: - return self._prefix - if self._parent_node: - return self._parent_node.lookupPrefix(namespace_uri) - return None - - def isDefaultNamespace(self, namespace_uri: str) -> bool: - return self._namespace_uri == namespace_uri - - def getBaseURI(self) -> Optional[str]: - return None - - def compareDocumentPosition(self, other: "Node") -> int: - """Compares the document position of two nodes.""" - return Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC - - def getTextContent(self) -> str: - parts = [] - for child in self._child_nodes: - if child.getNodeType() == Node.TEXT_NODE or child.getNodeType() == Node.CDATA_SECTION_NODE: - parts.append(child.getNodeValue() or "") - parts.append(child.getTextContent() or "") - return "".join(parts) - - def setTextContent(self, text: str): - self._child_nodes = [Text(text)] - self._update_siblings() - - def isSameNode(self, other: "Node") -> bool: - return self is other - - def isEqualNode(self, other: "Node") -> bool: - if self._node_type != other._node_type or self._node_name != other._node_name: - return False - return self._node_value == other._node_value - - def getFeature(self, feature: str, version: str): - return None - - def setUserData(self, key: str, data: Any, handler: Any = None) -> Any: - old = self._user_data.get(key) - self._user_data[key] = data - return old - - def getUserData(self, key: str) -> Any: - return self._user_data.get(key) - - def _update_siblings(self): - for i, child in enumerate(self._child_nodes): - child._previous_sibling = self._child_nodes[i - 1] if i > 0 else None - child._next_sibling = self._child_nodes[i + 1] if i < len(self._child_nodes) - 1 else None - - def toString(self) -> str: - return f"Node[{self._node_name}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Document -# ============================================================ - -class Document(Node): - """ - org.w3c.dom.Document - Represents an XML document. - """ - - def __init__(self): - super().__init__(Node.DOCUMENT_NODE, "#document", None) - self._document_element = None - self._doctype = None - self._implementation = DOMImplementation() - self._input_encoding = "UTF-8" - self._xml_encoding = "UTF-8" - self._xml_standalone = False - self._xml_version = "1.0" - self._strict_error_checking = True - self._document_uri = None - self._dom_config = DOMConfiguration() - - def getDocumentElement(self) -> Optional["Element"]: - return self._document_element - - def getElementsByTagName(self, tagname: str) -> NodeList: - result = [] - self._collect_elements_by_tag(self._document_element, tagname, result) - return NodeList(result) - - def _collect_elements_by_tag(self, node: Node, tagname: str, result: List): - if node is None: - return - if isinstance(node, Element) and (tagname == "*" or node.getTagName() == tagname): - result.append(node) - for child in node._child_nodes: - self._collect_elements_by_tag(child, tagname, result) - - def getElementById(self, element_id: str) -> Optional["Element"]: - result = [] - self._collect_elements_by_tag(self._document_element, "*", result) - for el in result: - if el.getAttribute("id") == element_id: - return el - return None - - def createElement(self, tag_name: str) -> "Element": - el = Element(tag_name) - el._owner_document = self - return el - - def createDocumentFragment(self): - frag = Node(Node.DOCUMENT_FRAGMENT_NODE, "#document-fragment", None) - frag._owner_document = self - return frag - - def createTextNode(self, data: str) -> "Text": - tn = Text(data) - tn._owner_document = self - return tn - - def createComment(self, data: str) -> "Comment": - c = Comment(data) - c._owner_document = self - return c - - def createCDATASection(self, data: str) -> "CDATASection": - cs = CDATASection(data) - cs._owner_document = self - return cs - - def createProcessingInstruction(self, target: str, data: str) -> "ProcessingInstruction": - pi = ProcessingInstruction(target, data) - pi._owner_document = self - return pi - - def createAttribute(self, name: str) -> "Attr": - attr = Attr(name) - attr._owner_document = self - return attr - - def createEntityReference(self, name: str) -> "EntityReference": - er = EntityReference(name) - er._owner_document = self - return er - - def importNode(self, imported_node: Node, deep: bool) -> Node: - return imported_node.cloneNode(deep) - - def adoptNode(self, node: Node) -> Node: - node._owner_document = self - return node - - def createAttributeNS(self, namespace_uri: str, qualified_name: str) -> "Attr": - attr = Attr(qualified_name) - attr._namespace_uri = namespace_uri - attr._owner_document = self - return attr - - def createElementNS(self, namespace_uri: str, qualified_name: str) -> "Element": - el = Element(qualified_name) - el._namespace_uri = namespace_uri - el._owner_document = self - return el - - def getDoctype(self) -> Optional["DocumentType"]: - return self._doctype - - def getImplementation(self) -> "DOMImplementation": - return self._implementation - - def getInputEncoding(self) -> str: - return self._input_encoding - - def getXmlEncoding(self) -> str: - return self._xml_encoding - - def getXmlStandalone(self) -> bool: - return self._xml_standalone - - def setXmlStandalone(self, standalone: bool): - self._xml_standalone = standalone - - def getXmlVersion(self) -> str: - return self._xml_version - - def setXmlVersion(self, version: str): - self._xml_version = version - - def getStrictErrorChecking(self) -> bool: - return self._strict_error_checking - - def setStrictErrorChecking(self, strict: bool): - self._strict_error_checking = strict - - def getDocumentURI(self) -> Optional[str]: - return self._document_uri - - def setDocumentURI(self, uri: str): - self._document_uri = uri - - def getDomConfig(self) -> "DOMConfiguration": - return self._dom_config - - def normalizeDocument(self): - self.normalize() - - def renameNode(self, node: Node, namespace_uri: str, qualified_name: str) -> Node: - node._node_name = qualified_name - node._namespace_uri = namespace_uri - return node - - def toString(self) -> str: - return f"Document@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Element -# ============================================================ - -class Element(Node): - """ - org.w3c.dom.Element - Represents an element in an XML document. - """ - - def __init__(self, tag_name: str): - super().__init__(Node.ELEMENT_NODE, tag_name, None) - self._tag_name = tag_name - self._local_name = tag_name - self._schema_type = None - - def getTagName(self) -> str: - return self._tag_name - - def getAttribute(self, name: str) -> str: - attr = self._attributes.getNamedItem(name) - return attr.getNodeValue() if attr else "" - - def getAttributeNode(self, name: str) -> Optional["Attr"]: - return self._attributes.getNamedItem(name) - - def getElementsByTagName(self, tagname: str) -> NodeList: - result = [] - doc = self.getOwnerDocument() - if doc: - doc._collect_elements_by_tag(self, tagname, result) - return NodeList(result) - - def getAttributeNS(self, namespace_uri: str, local_name: str) -> str: - attr = self._attributes.getNamedItemNS(namespace_uri, local_name) - return attr.getNodeValue() if attr else "" - - def setAttribute(self, name: str, value: str): - attr = Attr(name, value) - self._attributes.setNamedItem(attr) - - def setAttributeNS(self, namespace_uri: str, qualified_name: str, value: str): - attr = Attr(qualified_name, value) - attr._namespace_uri = namespace_uri - self._attributes.setNamedItem(attr) - - def removeAttribute(self, name: str): - self._attributes.removeNamedItem(name) - - def removeAttributeNS(self, namespace_uri: str, local_name: str): - self._attributes.removeNamedItemNS(namespace_uri, local_name) - - def getAttributeNodeNS(self, namespace_uri: str, local_name: str) -> Optional["Attr"]: - return self._attributes.getNamedItemNS(namespace_uri, local_name) - - def setAttributeNode(self, new_attr: "Attr") -> Optional["Attr"]: - return self._attributes.setNamedItem(new_attr) - - def setAttributeNodeNS(self, new_attr: "Attr") -> Optional["Attr"]: - return self._attributes.setNamedItemNS(new_attr) - - def removeAttributeNode(self, old_attr: "Attr") -> Optional["Attr"]: - return self._attributes.removeNamedItem(old_attr.getNodeName()) - - def getElementsByTagNameNS(self, namespace_uri: str, local_name: str) -> NodeList: - result = [] - self._collect_elements_by_tag_ns(self, namespace_uri, local_name, result) - return NodeList(result) - - def _collect_elements_by_tag_ns(self, node, ns, name, result): - if node is None: - return - if (isinstance(node, Element) and - (ns == "*" or node.getNamespaceURI() == ns) and - (name == "*" or node.getLocalName() == name)): - result.append(node) - for child in node._child_nodes: - self._collect_elements_by_tag_ns(child, ns, name, result) - - def hasAttribute(self, name: str) -> bool: - return self._attributes.getNamedItem(name) is not None - - def hasAttributeNS(self, namespace_uri: str, local_name: str) -> bool: - return self._attributes.getNamedItemNS(namespace_uri, local_name) is not None - - def getSchemaTypeInfo(self): - return self._schema_type - - def setIdAttribute(self, name: str, is_id: bool): - pass - - def setIdAttributeNS(self, namespace_uri: str, local_name: str, is_id: bool): - pass - - def setIdAttributeNode(self, id_attr: "Attr", is_id: bool): - pass - - def toString(self) -> str: - return f"Element[{self._tag_name}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Attr -# ============================================================ - -class Attr(Node): - """ - org.w3c.dom.Attr - Represents an attribute in a DOM document. - """ - def __init__(self, name: str, value: str = ""): - super().__init__(Node.ATTRIBUTE_NODE, name, value) - self._specified = True - self._owner_element = None - self._is_id = False - self._schema_type = None - self._local_name = name - - def getName(self) -> str: - return self._node_name - - def getValue(self) -> str: - return self._node_value or "" - - def setValue(self, value: str): - self._node_value = value - self._specified = True - - def getOwnerElement(self) -> Optional[Element]: - return self._owner_element - - def getSpecified(self) -> bool: - return self._specified - - def getSchemaTypeInfo(self): - return self._schema_type - - def isId(self) -> bool: - return self._is_id - - def toString(self) -> str: - return f'Attr[{self._node_name}="{self._node_value}"]' - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Text -# ============================================================ - -class Text(Node): - """ - org.w3c.dom.Text - Represents the textual content of an element or attribute. - """ - def __init__(self, data: str = ""): - super().__init__(Node.TEXT_NODE, "#text", data) - - def splitText(self, offset: int) -> "Text": - if offset >= len(self._node_value or ""): - raise DOMException(DOMException.INDEX_SIZE_ERR, "Offset out of bounds") - first_part = self._node_value[:offset] - second_part = self._node_value[offset:] - self._node_value = first_part - new_text = Text(second_part) - if self._parent_node: - self._parent_node.insertBefore(new_text, self._next_sibling) - return new_text - - def isElementContentWhitespace(self) -> bool: - return (self._node_value or "").strip() == "" - - def getWholeText(self) -> str: - parts = [self._node_value or ""] - # Collect adjacent text nodes - sibling = self._previous_sibling - while sibling and sibling.getNodeType() in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE): - parts.insert(0, sibling.getNodeValue() or "") - sibling = sibling._previous_sibling - sibling = self._next_sibling - while sibling and sibling.getNodeType() in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE): - parts.append(sibling.getNodeValue() or "") - sibling = sibling._next_sibling - return "".join(parts) - - def replaceWholeText(self, content: str) -> "Text": - self._node_value = content - # Remove adjacent text nodes - siblings = [] - sib = self._previous_sibling - while sib and sib.getNodeType() in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE): - siblings.append(sib) - sib = sib._previous_sibling - for s in siblings: - if self._parent_node: - self._parent_node.removeChild(s) - siblings = [] - sib = self._next_sibling - while sib and sib.getNodeType() in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE): - siblings.append(sib) - sib = sib._next_sibling - for s in siblings: - if self._parent_node: - self._parent_node.removeChild(s) - return self - - def toString(self) -> str: - return f"Text[{self._node_value}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Comment -# ============================================================ - -class Comment(Node): - """ - org.w3c.dom.Comment - Represents the content of an XML comment. - """ - def __init__(self, data: str = ""): - super().__init__(Node.COMMENT_NODE, "#comment", data) - - def toString(self) -> str: - return f"Comment[{self._node_value}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# CDATASection -# ============================================================ - -class CDATASection(Text): - """ - org.w3c.dom.CDATASection - Represents a CDATA section. - """ - def __init__(self, data: str = ""): - super().__init__(data) - self._node_type = Node.CDATA_SECTION_NODE - self._node_name = "#cdata-section" - - def toString(self) -> str: - return f"CDATASection[{self._node_value}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# DocumentType -# ============================================================ - -class DocumentType(Node): - """ - org.w3c.dom.DocumentType - Represents a document type declaration. - """ - def __init__(self, name: str = "", public_id: str = "", system_id: str = "", - internal_subset: str = ""): - super().__init__(Node.DOCUMENT_TYPE_NODE, name, None) - self._name = name - self._entities = NamedNodeMap() - self._notations = NamedNodeMap() - self._public_id = public_id - self._system_id = system_id - self._internal_subset = internal_subset - - def getName(self) -> str: - return self._name - - def getEntities(self) -> NamedNodeMap: - return self._entities - - def getNotations(self) -> NamedNodeMap: - return self._notations - - def getPublicId(self) -> str: - return self._public_id - - def getSystemId(self) -> str: - return self._system_id - - def getInternalSubset(self) -> str: - return self._internal_subset - - def toString(self) -> str: - return f"DocumentType[{self._name}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Notation -# ============================================================ - -class Notation(Node): - """ - org.w3c.dom.Notation - Represents a notation in a DTD. - """ - def __init__(self, name: str = "", public_id: str = "", system_id: str = ""): - super().__init__(Node.NOTATION_NODE, name, None) - self._public_id = public_id - self._system_id = system_id - - def getPublicId(self) -> str: - return self._public_id - - def getSystemId(self) -> str: - return self._system_id - - def toString(self) -> str: - return f"Notation[{self._node_name}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Entity -# ============================================================ - -class Entity(Node): - """ - org.w3c.dom.Entity - Represents an entity in a DTD. - """ - def __init__(self, name: str = "", public_id: str = "", system_id: str = "", - notation_name: str = ""): - super().__init__(Node.ENTITY_NODE, name, None) - self._public_id = public_id - self._system_id = system_id - self._notation_name = notation_name - self._input_encoding = "UTF-8" - self._xml_encoding = "UTF-8" - self._xml_version = "1.0" - - def getPublicId(self) -> str: - return self._public_id - - def getSystemId(self) -> str: - return self._system_id - - def getNotationName(self) -> str: - return self._notation_name - - def getInputEncoding(self) -> str: - return self._input_encoding - - def getXmlEncoding(self) -> str: - return self._xml_encoding - - def getXmlVersion(self) -> str: - return self._xml_version - - def toString(self) -> str: - return f"Entity[{self._node_name}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -class EntityReference(Node): - """ - org.w3c.dom.EntityReference - Represents an entity reference. - """ - def __init__(self, name: str = ""): - super().__init__(Node.ENTITY_REFERENCE_NODE, name, None) - - def toString(self) -> str: - return f"EntityReference[{self._node_name}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# ProcessingInstruction -# ============================================================ - -class ProcessingInstruction(Node): - """ - org.w3c.dom.ProcessingInstruction - Represents a processing instruction. - """ - def __init__(self, target: str = "", data: str = ""): - super().__init__(Node.PROCESSING_INSTRUCTION_NODE, target, data) - self._target = target - self._data = data - - def getTarget(self) -> str: - return self._target - - def getData(self) -> str: - return self._data - - def setData(self, data: str): - self._data = data - - def toString(self) -> str: - return f"ProcessingInstruction[{self._target}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# DOMImplementation -# ============================================================ - -class DOMImplementation(Stub): - """ - org.w3c.dom.DOMImplementation - Interface for DOM implementation. - """ - def __init__(self): - super().__init__() - - def hasFeature(self, feature: str, version: str) -> bool: - return feature in ("XML", "Core", "DOM") and version in ("1.0", "2.0", "3.0", "") - - def createDocumentType(self, qualified_name: str, public_id: str, - system_id: str) -> DocumentType: - return DocumentType(qualified_name, public_id, system_id) - - def createDocument(self, namespace_uri: str, qualified_name: str, - doctype: DocumentType = None) -> Document: - doc = Document() - if qualified_name: - root = doc.createElementNS(namespace_uri or "", qualified_name) - doc.appendChild(root) - doc._document_element = root - doc._doctype = doctype - return doc - - def getFeature(self, feature: str, version: str): - return self - - def toString(self) -> str: - return f"DOMImplementation@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# DOMConfiguration -# ============================================================ - -class DOMConfiguration(Stub): - """ - org.w3c.dom.DOMConfiguration - Configuration of a DOM document. - """ - def __init__(self): - super().__init__() - self._params = {} - - def setParameter(self, name: str, value: Any): - self._params[name] = value - - def getParameter(self, name: str) -> Any: - return self._params.get(name) - - def canSetParameter(self, name: str, value: Any) -> bool: - return True - - def getParameterNames(self) -> NodeList: - return NodeList([Node(Node.TEXT_NODE, k, str(v)) for k, v in self._params.items()]) - - def toString(self) -> str: - return f"DOMConfiguration@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# UserDataHandler -# ============================================================ - -class UserDataHandler(Stub): - """ - org.w3c.dom.UserDataHandler - Handler for user data events. - """ - NODE_ADOPTED: int = 5 - NODE_CLONED: int = 1 - NODE_IMPORTED: int = 2 - NODE_DELETED: int = 3 - NODE_RENAMED: int = 4 - - def handle(self, operation: int, key: str, data: Any, src: Node, dst: Node): - """Called when a node is adopted, cloned, imported, deleted, or renamed.""" - pass - - def toString(self) -> str: - return f"UserDataHandler@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# DOMError -# ============================================================ - -class DOMError(Stub): - """ - org.w3c.dom.DOMError - Represents an error in DOM operations. - """ - SEVERITY_WARNING: int = 1 - SEVERITY_ERROR: int = 2 - SEVERITY_FATAL_ERROR: int = 3 - - def __init__(self, severity: int = SEVERITY_ERROR, message: str = "", - error_type: str = "", related_exception: Exception = None, - related_data: Any = None, location: "DOMLocator" = None): - super().__init__() - self._severity = severity - self._message = message - self._type = error_type - self._related_exception = related_exception - self._related_data = related_data - self._location = location - - def getSeverity(self) -> int: - return self._severity - - def getMessage(self) -> str: - return self._message - - def getType(self) -> str: - return self._type - - def getRelatedException(self) -> Exception: - return self._related_exception - - def getRelatedData(self) -> Any: - return self._related_data - - def getLocation(self) -> "DOMLocator": - return self._location - - def toString(self) -> str: - return f"DOMError[{self._type}]: {self._message}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# DOMLocator -# ============================================================ - -class DOMLocator(Stub): - """ - org.w3c.dom.DOMLocator - Location information for DOM errors. - """ - def __init__(self, line: int = 0, column: int = 0, byte_offset: int = -1, - utf16_offset: int = -1, related_node: Node = None, uri: str = ""): - super().__init__() - self._line = line - self._column = column - self._byte_offset = byte_offset - self._utf16_offset = utf16_offset - self._related_node = related_node - self._uri = uri - - def getLineNumber(self) -> int: - return self._line - - def getColumnNumber(self) -> int: - return self._column - - def getByteOffset(self) -> int: - return self._byte_offset - - def getUtf16Offset(self) -> int: - return self._utf16_offset - - def getRelatedNode(self) -> Node: - return self._related_node - - def getUri(self) -> str: - return self._uri - - def toString(self) -> str: - return f"DOMLocator[line={self._line}, col={self._column}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# DOMErrorHandler -# ============================================================ - -class DOMErrorHandler(Stub): - """ - org.w3c.dom.DOMErrorHandler - Handler for DOM errors. - """ - def handleError(self, error: DOMError) -> bool: - """Returns True to continue, False to stop processing.""" - return True - - def toString(self) -> str: - return f"DOMErrorHandler@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# TypeInfo -# ============================================================ - -class TypeInfo(Stub): - """ - org.w3c.dom.TypeInfo - Represents type information about an element or attribute. - """ - DERIVATION_RESTRICTION: int = 1 - DERIVATION_EXTENSION: int = 2 - DERIVATION_UNION: int = 4 - DERIVATION_LIST: int = 8 - - def __init__(self, type_name: str = "", type_namespace: str = ""): - super().__init__() - self._type_name = type_name - self._type_namespace = type_namespace - - def getTypeName(self) -> str: - return self._type_name - - def getTypeNamespace(self) -> str: - return self._type_namespace - - def toString(self) -> str: - return f"TypeInfo[{self._type_name}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/org/xml_pull.py b/android_pyapi/org/xml_pull.py deleted file mode 100644 index 7e5e04f..0000000 --- a/android_pyapi/org/xml_pull.py +++ /dev/null @@ -1,589 +0,0 @@ -""" -Java org.xmlpull.v1 APIs implemented in Python. -org.xmlpull.v1 package - XmlPullParser, XmlSerializer, XmlPullParserFactory. -Wraps Python xml.etree.ElementTree. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -import xml.etree.ElementTree as _ET -from io import StringIO as _StringIO -from typing import List, Optional - -__all__ = [ - "XmlPullParser", "XmlSerializer", "XmlPullParserFactory", "Factory", -] - - -# ============================================================ -# XmlPullParser -# ============================================================ - -class XmlPullParser(Stub): - """ - org.xmlpull.v1.XmlPullParser - An XML pull parser interface. - """ - # Event types - START_TAG: int = 2 - END_TAG: int = 3 - TEXT: int = 4 - CDSECT: int = 5 - ENTITY_REF: int = 6 - IGNORABLE_WHITESPACE: int = 7 - PROCESSING_INSTRUCTION: int = 8 - COMMENT: int = 9 - DOCDECL: int = 10 - START_DOCUMENT: int = 0 - END_DOCUMENT: int = 1 - TYPES: List[str] = [ - "START_DOCUMENT", "END_DOCUMENT", "START_TAG", "END_TAG", - "TEXT", "CDSECT", "ENTITY_REF", "IGNORABLE_WHITESPACE", - "PROCESSING_INSTRUCTION", "COMMENT", "DOCDECL", - ] - NO_TYPES: List[str] = [] - # Features - FEATURE_PROCESS_NAMESPACES: str = "http://xmlpull.org/v1/doc/features.html#process-ns" - FEATURE_REPORT_NAMESPACE_ATTRIBUTES: str = "http://xmlpull.org/v1/doc/features.html#report-ns-attributes" - FEATURE_PROCESS_DOCDECL: str = "http://xmlpull.org/v1/doc/features.html#process-docdecl" - FEATURE_VALIDATION: str = "http://xmlpull.org/v1/doc/features.html#validation" - - class _Event: - def __init__(self, type_=0, name="", text="", namespace="", prefix="", - attributes=None, depth=0, line=0, column=0): - self.type = type_ - self.name = name - self.text = text - self.namespace = namespace - self.prefix = prefix - self.attributes = attributes or [] - self.depth = depth - self.line = line - self.column = column - - def __init__(self): - super().__init__() - self._events = [] - self._pos = -1 - self._depth = 0 - self._features = { - self.FEATURE_PROCESS_NAMESPACES: True, - self.FEATURE_REPORT_NAMESPACE_ATTRIBUTES: False, - self.FEATURE_PROCESS_DOCDECL: False, - self.FEATURE_VALIDATION: False, - } - self._properties = {} - self._input_encoding = "UTF-8" - self._namespace_stack = [] - - def setInput(self, source: Any, input_encoding: str = None): - """Set the input source for parsing.""" - if input_encoding: - self._input_encoding = input_encoding - - if isinstance(source, str): - # Parse directly from XML string - self._parse_string(source) - elif hasattr(source, 'read'): - # File-like object - data = source.read() - if isinstance(data, bytes): - data = data.decode(input_encoding or "UTF-8") - self._parse_string(data) - else: - self._parse_string(str(source)) - - def _parse_string(self, xml_str: str): - """Parse XML string into a list of events.""" - self._events = [] - self._pos = -1 - - try: - root = _ET.fromstring(xml_str) - except _ET.ParseError as e: - # Create minimal events if parsing fails - self._events.append(XmlPullParser._Event(XmlPullParser.START_DOCUMENT)) - self._events.append(XmlPullParser._Event(XmlPullParser.END_DOCUMENT)) - return - - self._events.append(XmlPullParser._Event(XmlPullParser.START_DOCUMENT)) - self._build_events(root, 0) - self._events.append(XmlPullParser._Event(XmlPullParser.END_DOCUMENT)) - - def _build_events(self, element: _ET.Element, depth: int): - """Recursively build events from ElementTree.""" - attrs = [] - for k, v in element.attrib.items(): - ns = "" - name = k - prefix = "" - if "}" in k: - ns = k[1:k.index("}")] - name = k[k.index("}") + 1:] - attrs.append({"namespace": ns, "name": name, "prefix": prefix, "value": v}) - - # START_TAG - tag = element.tag - ns = "" - prefix = "" - if "}" in tag: - ns = tag[1:tag.index("}")] - tag = tag[tag.index("}") + 1:] - - self._events.append(XmlPullParser._Event( - XmlPullParser.START_TAG, tag, "", ns, prefix, attrs, depth)) - - # Text content - if element.text and element.text.strip(): - self._events.append(XmlPullParser._Event( - XmlPullParser.TEXT, "", element.text, "", "", [], depth + 1)) - - # Child elements - for child in element: - self._build_events(child, depth + 1) - - # END_TAG - self._events.append(XmlPullParser._Event( - XmlPullParser.END_TAG, tag, "", ns, prefix, [], depth)) - - # Tail text - if element.tail and element.tail.strip(): - self._events.append(XmlPullParser._Event( - XmlPullParser.TEXT, "", element.tail, "", "", [], depth)) - - # --- Event navigation --- - - def getEventType(self) -> int: - if 0 <= self._pos < len(self._events): - return self._events[self._pos].type - return self.END_DOCUMENT - - def next(self) -> int: - """Move to next event.""" - if self._pos < len(self._events) - 1: - self._pos += 1 - return self.getEventType() - - def nextToken(self) -> int: - """Move to next token (same as next for this impl).""" - return self.next() - - def nextText(self) -> str: - """Returns the text content of the current element.""" - if self.getEventType() != self.START_TAG: - return "" - self.next() - text = self.getText() - self.next() # Move to END_TAG - return text or "" - - def nextTag(self) -> int: - """Move to next tag event.""" - self.next() - while self.getEventType() not in (self.START_TAG, self.END_TAG, self.END_DOCUMENT): - self.next() - return self.getEventType() - - def require(self, type_: int, namespace: str, name: str): - """Check that the current event matches expectations.""" - if self.getEventType() != type_: - raise AndroidOnlyError(f"Expected event {type_}, got {self.getEventType()}") - if namespace is not None and self.getNamespace() != namespace: - raise AndroidOnlyError(f"Expected namespace '{namespace}', got '{self.getNamespace()}'") - if name is not None and self.getName() != name: - raise AndroidOnlyError(f"Expected name '{name}', got '{self.getName()}'") - - # --- Position info --- - - def getDepth(self) -> int: - if 0 <= self._pos < len(self._events): - return self._events[self._pos].depth - return 0 - - def getLineNumber(self) -> int: - if 0 <= self._pos < len(self._events): - return self._events[self._pos].line - return -1 - - def getColumnNumber(self) -> int: - if 0 <= self._pos < len(self._events): - return self._events[self._pos].column - return -1 - - def getInputEncoding(self) -> str: - return self._input_encoding - - # --- Current event data --- - - def getName(self) -> str: - if 0 <= self._pos < len(self._events): - return self._events[self._pos].name - return "" - - def getNamespace(self) -> str: - if 0 <= self._pos < len(self._events): - return self._events[self._pos].namespace - return "" - - def getPrefix(self) -> str: - if 0 <= self._pos < len(self._events): - return self._events[self._pos].prefix - return "" - - def getText(self) -> Optional[str]: - if 0 <= self._pos < len(self._events): - return self._events[self._pos].text - return None - - def getTextCharacters(self, holder_for_start: List[int]) -> List[str]: - """Returns text characters and fills start/length in holder.""" - text = self.getText() or "" - if len(holder_for_start) >= 2: - holder_for_start[0] = 0 - holder_for_start[1] = len(text) - return list(text) - - # --- Attributes --- - - def getAttributeCount(self) -> int: - if 0 <= self._pos < len(self._events): - return len(self._events[self._pos].attributes) - return 0 - - def getAttributeName(self, index: int) -> str: - if 0 <= self._pos < len(self._events): - attrs = self._events[self._pos].attributes - if 0 <= index < len(attrs): - return attrs[index]["name"] - return "" - - def getAttributeNamespace(self, index: int) -> str: - if 0 <= self._pos < len(self._events): - attrs = self._events[self._pos].attributes - if 0 <= index < len(attrs): - return attrs[index]["namespace"] - return "" - - def getAttributePrefix(self, index: int) -> str: - if 0 <= self._pos < len(self._events): - attrs = self._events[self._pos].attributes - if 0 <= index < len(attrs): - return attrs[index]["prefix"] - return "" - - def getAttributeType(self, index: int) -> str: - return "CDATA" - - def getAttributeValue(self, index_or_name: Any, namespace: str = None) -> str: - if isinstance(index_or_name, int): - if 0 <= self._pos < len(self._events): - attrs = self._events[self._pos].attributes - if 0 <= index_or_name < len(attrs): - return attrs[index_or_name]["value"] - else: - # Lookup by name - if 0 <= self._pos < len(self._events): - attrs = self._events[self._pos].attributes - for attr in attrs: - if attr["name"] == index_or_name: - if namespace is None or attr["namespace"] == namespace: - return attr["value"] - return "" - - def isAttributeDefault(self, index: int) -> bool: - return False - - # --- Element state --- - - def isEmptyElementTag(self) -> bool: - """Returns True if the current event is an empty element tag.""" - if self.getEventType() == self.START_TAG: - if self._pos + 1 < len(self._events): - next_event = self._events[self._pos + 1] - return next_event.type == self.END_TAG and next_event.name == self.getName() - return False - - def isWhitespace(self) -> bool: - text = self.getText() or "" - return text.strip() == "" - - # --- Namespace support --- - - def getNamespaceCount(self, depth: int) -> int: - return len(self._namespace_stack) - - def getNamespacePrefix(self, index: int) -> str: - if 0 <= index < len(self._namespace_stack): - return self._namespace_stack[index][0] - return "" - - def getNamespaceUri(self, prefix: str) -> str: - for p, uri in self._namespace_stack: - if p == prefix: - return uri - return "" - - # --- Features / Properties --- - - def getFeature(self, name: str) -> bool: - return self._features.get(name, False) - - def setFeature(self, name: str, value: bool): - if name in self._features: - self._features[name] = value - else: - raise AndroidOnlyError(f"Unknown feature: {name}") - - def getProperty(self, name: str): - return self._properties.get(name) - - def setProperty(self, name: str, value: Any): - self._properties[name] = value - - def toString(self) -> str: - return f"XmlPullParser@{hex(id(self))}[event={self.getEventType()}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# XmlSerializer -# ============================================================ - -class XmlSerializer(Stub): - """ - org.xmlpull.v1.XmlSerializer - Interface for serializing XML documents. - """ - - def __init__(self): - super().__init__() - self._output = [] - self._depth = 0 - self._features = {} - self._properties = {} - self._prefixes = {} - self._output_stream = None - self._encoding = "UTF-8" - - def setOutput(self, output_stream, encoding: str = None): - """Sets the output writer/stream.""" - self._output_stream = output_stream - self._encoding = encoding or "UTF-8" - - def getFeature(self, name: str) -> bool: - return self._features.get(name, False) - - def setFeature(self, name: str, value: bool): - self._features[name] = value - - def getProperty(self, name: str): - return self._properties.get(name) - - def setProperty(self, name: str, value: Any): - self._properties[name] = value - - def setPrefix(self, prefix: str, namespace_uri: str): - """Sets the prefix for a namespace.""" - self._prefixes[namespace_uri] = prefix - if prefix: - self._write(f' xmlns:{prefix}="{namespace_uri}"') - else: - self._write(f' xmlns="{namespace_uri}"') - - def startTag(self, namespace: str, name: str) -> "XmlSerializer": - """Writes a start tag.""" - self._write(f"<{name}") - self._depth += 1 - return self - - def attribute(self, namespace: str, name: str, value: str) -> "XmlSerializer": - """Writes an attribute.""" - if value is not None: - escaped = value.replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """) - self._write(f' {name}="{escaped}"') - return self - - def endTag(self, namespace: str, name: str) -> "XmlSerializer": - """Writes an end tag.""" - self._depth -= 1 - self._write(f"") - return self - - def text(self, text_or_buf: Any, start: int = None, length: int = None) -> "XmlSerializer": - """Writes text content.""" - if isinstance(text_or_buf, str): - escaped = text_or_buf.replace("&", "&").replace("<", "<").replace(">", ">") - self._write(escaped) - elif isinstance(text_or_buf, (list, tuple)) and start is not None and length is not None: - chars = text_or_buf[start:start + length] - self._write("".join(chars)) - return self - - def cdsect(self, text: str) -> "XmlSerializer": - """Writes a CDATA section.""" - self._write(f"") - return self - - def entityRef(self, name: str) -> "XmlSerializer": - """Writes an entity reference.""" - self._write(f"&{name};") - return self - - def processingInstruction(self, text: str) -> "XmlSerializer": - """Writes a processing instruction.""" - self._write(f"") - return self - - def comment(self, text: str) -> "XmlSerializer": - """Writes a comment.""" - self._write(f"") - return self - - def docdecl(self, text: str) -> "XmlSerializer": - """Writes a document declaration.""" - self._write(f"") - return self - - def startDocument(self, encoding: str = None, standalone: bool = None): - """Writes the XML declaration.""" - encoding = encoding or self._encoding - decl = f'" - self._write(decl) - - def endDocument(self): - """Finishes the document.""" - self.flush() - - def flush(self): - """Flushes the output.""" - content = "".join(self._output) - if self._output_stream: - if hasattr(self._output_stream, 'write'): - self._output_stream.write(content) - self._output = [] - - def getDepth(self) -> int: - return self._depth - - def getName(self) -> str: - return "" - - def getNamespace(self) -> str: - return "" - - def getPrefix(self, namespace: str, prefix: bool = False) -> str: - return self._prefixes.get(namespace, "") - - def _write(self, s: str): - self._output.append(s) - - def verbatim(self, text: str) -> "XmlSerializer": - """Writes raw text without escaping.""" - self._write(text) - return self - - def write(self, text: str) -> "XmlSerializer": - """Writes text.""" - return self.text(text) - - def toString(self) -> str: - return f"XmlSerializer@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# XmlPullParserFactory -# ============================================================ - -class XmlPullParserFactory(Stub): - """ - org.xmlpull.v1.XmlPullParserFactory - Factory for creating XmlPullParser and XmlSerializer instances. - """ - - def __init__(self): - super().__init__() - self._features = {} - self._properties = {} - - @staticmethod - def newInstance(unused: Any = None, unused_cls: Any = None) -> "XmlPullParserFactory": - return XmlPullParserFactory() - - def setFeature(self, name: str, value: bool): - self._features[name] = value - - def getFeature(self, name: str) -> bool: - return self._features.get(name, False) - - def setProperty(self, name: str, value: Any): - self._properties[name] = value - - def getProperty(self, name: str): - return self._properties.get(name) - - def newPullParser(self) -> XmlPullParser: - """Creates a new XmlPullParser.""" - return XmlPullParser() - - def newSerializer(self) -> XmlSerializer: - """Creates a new XmlSerializer.""" - return XmlSerializer() - - def isNamespaceAware(self) -> bool: - return self.getFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES) - - def isValidating(self) -> bool: - return self.getFeature(XmlPullParser.FEATURE_VALIDATION) - - def toString(self) -> str: - return f"XmlPullParserFactory@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Factory (alias) -# ============================================================ - -class Factory(XmlPullParserFactory): - """ - Factory alias for XmlPullParserFactory. - """ - def __init__(self): - super().__init__() - - @staticmethod - def newInstance(unused: Any = None, unused_cls: Any = None) -> "Factory": - return Factory() - - @staticmethod - def getInstance() -> "Factory": - return Factory() - - def toString(self) -> str: - return f"Factory@{hex(id(self))}" - - def equals(self, other) -> bool: - return isinstance(other, Factory) - - def hashCode(self) -> int: - return hash("Factory") & 0x7FFFFFFF \ No newline at end of file diff --git a/android_pyapi/org/xml_sax.py b/android_pyapi/org/xml_sax.py deleted file mode 100644 index 167b1b6..0000000 --- a/android_pyapi/org/xml_sax.py +++ /dev/null @@ -1,890 +0,0 @@ -""" -Java org.xml.sax APIs implemented in Python. -org.xml.sax package - XMLReader, ContentHandler, SAXException, etc. -Wraps Python xml.sax module. -""" - -from __future__ import annotations - -from .._core import Stub, AndroidOnlyError -import xml.sax as _sax -import xml.sax.handler as _handler -import xml.sax.xmlreader as _reader -from typing import List, Optional - -__all__ = [ - "XMLReader", "ContentHandler", "DTDHandler", "EntityResolver", - "ErrorHandler", "InputSource", "SAXException", "SAXParseException", - "SAXNotRecognizedException", "SAXNotSupportedException", - "Locator", "Attributes", "AttributeList", "HandlerBase", - "DocumentHandler", "XMLFilter", "XMLFilterImpl", "DefaultHandler", - "Helpers", -] - - -# ============================================================ -# SAXException -# ============================================================ - -class SAXException(Exception, Stub): - """ - org.xml.sax.SAXException - Encapsulate a general SAX error or warning. - """ - def __init__(self, message: str = "", exception: Exception = None): - super().__init__(message) - self._message = message - self._exception = exception - - def getMessage(self) -> str: - return self._message - - def getException(self) -> Optional[Exception]: - return self._exception - - def toString(self) -> str: - return f"SAXException: {self._message}" - - def equals(self, other) -> bool: - if isinstance(other, SAXException): - return self._message == other._message - return False - - def hashCode(self) -> int: - return hash(self._message) & 0x7FFFFFFF - - -class SAXParseException(SAXException): - """ - org.xml.sax.SAXParseException - Encapsulate an XML parse error or warning. - """ - def __init__(self, message: str = "", public_id: str = "", system_id: str = "", - line: int = -1, column: int = -1, exception: Exception = None): - super().__init__(message, exception) - self._public_id = public_id - self._system_id = system_id - self._line = line - self._column = column - - def getPublicId(self) -> str: - return self._public_id - - def getSystemId(self) -> str: - return self._system_id - - def getLineNumber(self) -> int: - return self._line - - def getColumnNumber(self) -> int: - return self._column - - def toString(self) -> str: - return f"SAXParseException[line={self._line}, col={self._column}]: {self._message}" - - def equals(self, other) -> bool: - if isinstance(other, SAXParseException): - return (self._line == other._line and - self._column == other._column and - self._message == other._message) - return False - - def hashCode(self) -> int: - return hash((self._line, self._column, self._message)) & 0x7FFFFFFF - - -class SAXNotRecognizedException(SAXException): - """ - org.xml.sax.SAXNotRecognizedException - Exception for unrecognized feature or property. - """ - def __init__(self, message: str = ""): - super().__init__(message or "Feature or property not recognized") - - def toString(self) -> str: - return f"SAXNotRecognizedException: {self._message}" - - def equals(self, other) -> bool: - return isinstance(other, SAXNotRecognizedException) - - def hashCode(self) -> int: - return hash("SAXNotRecognizedException") & 0x7FFFFFFF - - -class SAXNotSupportedException(SAXException): - """ - org.xml.sax.SAXNotSupportedException - Exception for unsupported feature or property. - """ - def __init__(self, message: str = ""): - super().__init__(message or "Feature or property not supported") - - def toString(self) -> str: - return f"SAXNotSupportedException: {self._message}" - - def equals(self, other) -> bool: - return isinstance(other, SAXNotSupportedException) - - def hashCode(self) -> int: - return hash("SAXNotSupportedException") & 0x7FFFFFFF - - -# ============================================================ -# Locator -# ============================================================ - -class Locator(Stub): - """ - org.xml.sax.Locator - Interface for associating a SAX event with a document location. - """ - def __init__(self, public_id: str = "", system_id: str = "", - line: int = -1, column: int = -1): - super().__init__() - self._public_id = public_id - self._system_id = system_id - self._line = line - self._column = column - - def getPublicId(self) -> str: - return self._public_id - - def getSystemId(self) -> str: - return self._system_id - - def getLineNumber(self) -> int: - return self._line - - def getColumnNumber(self) -> int: - return self._column - - def toString(self) -> str: - return f"Locator[line={self._line}, col={self._column}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# InputSource -# ============================================================ - -class InputSource(Stub): - """ - org.xml.sax.InputSource - A single input source for a SAX parser. - """ - def __init__(self, system_id: str = None, byte_stream=None, - character_stream=None, encoding: str = None): - super().__init__() - self._public_id = None - self._system_id = system_id - self._byte_stream = byte_stream - self._character_stream = character_stream - self._encoding = encoding - - def getPublicId(self) -> Optional[str]: - return self._public_id - - def setPublicId(self, public_id: str): - self._public_id = public_id - - def getSystemId(self) -> Optional[str]: - return self._system_id - - def setSystemId(self, system_id: str): - self._system_id = system_id - - def getByteStream(self): - return self._byte_stream - - def setByteStream(self, byte_stream): - self._byte_stream = byte_stream - - def getEncoding(self) -> Optional[str]: - return self._encoding - - def setEncoding(self, encoding: str): - self._encoding = encoding - - def getCharacterStream(self): - return self._character_stream - - def setCharacterStream(self, character_stream): - self._character_stream = character_stream - - def toString(self) -> str: - return f"InputSource[systemId={self._system_id}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Attributes -# ============================================================ - -class Attributes(Stub): - """ - org.xml.sax.Attributes - Interface for a list of XML attributes. - """ - def __init__(self, attrs: List[dict] = None): - super().__init__() - self._attrs = attrs or [] - - def getLength(self) -> int: - return len(self._attrs) - - def getURI(self, index: int) -> str: - if 0 <= index < len(self._attrs): - return self._attrs[index].get("uri", "") - return "" - - def getLocalName(self, index: int) -> str: - if 0 <= index < len(self._attrs): - return self._attrs[index].get("localName", "") - return "" - - def getQName(self, index: int) -> str: - if 0 <= index < len(self._attrs): - return self._attrs[index].get("qname", "") - return "" - - def getType(self, index_or_name: Any) -> str: - if isinstance(index_or_name, int): - if 0 <= index_or_name < len(self._attrs): - return self._attrs[index_or_name].get("type", "CDATA") - return "" - for attr in self._attrs: - if attr.get("qname") == index_or_name: - return attr.get("type", "CDATA") - return "" - - def getValue(self, index_or_name: Any) -> str: - if isinstance(index_or_name, int): - if 0 <= index_or_name < len(self._attrs): - return self._attrs[index_or_name].get("value", "") - return "" - for attr in self._attrs: - if attr.get("qname") == index_or_name: - return attr.get("value", "") - return "" - - def getIndex(self, qname_or_uri: str, local_name: str = None) -> int: - if local_name is None: - for i, attr in enumerate(self._attrs): - if attr.get("qname") == qname_or_uri: - return i - else: - for i, attr in enumerate(self._attrs): - if attr.get("uri") == qname_or_uri and attr.get("localName") == local_name: - return i - return -1 - - def getIndexByQName(self, qname: str) -> int: - return self.getIndex(qname) - - def getIndexByURI(self, uri: str, local_name: str) -> int: - return self.getIndex(uri, local_name) - - def toString(self) -> str: - return f"Attributes@{hex(id(self))}[length={len(self._attrs)}]" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# AttributeList (deprecated) -# ============================================================ - -class AttributeList(Stub): - """ - org.xml.sax.AttributeList - Deprecated SAX1 interface for attribute list. - """ - def __init__(self): - super().__init__() - self._attrs = [] - - def getLength(self) -> int: - return len(self._attrs) - - def getName(self, index: int) -> str: - if 0 <= index < len(self._attrs): - return self._attrs[index].get("name", "") - return "" - - def getType(self, index_or_name: Any) -> str: - if isinstance(index_or_name, int): - if 0 <= index_or_name < len(self._attrs): - return self._attrs[index_or_name].get("type", "CDATA") - return "" - for attr in self._attrs: - if attr.get("name") == index_or_name: - return attr.get("type", "CDATA") - return "" - - def getValue(self, index_or_name: Any) -> str: - if isinstance(index_or_name, int): - if 0 <= index_or_name < len(self._attrs): - return self._attrs[index_or_name].get("value", "") - return "" - for attr in self._attrs: - if attr.get("name") == index_or_name: - return attr.get("value", "") - return "" - - def toString(self) -> str: - return f"AttributeList@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# ContentHandler -# ============================================================ - -class ContentHandler(Stub): - """ - org.xml.sax.ContentHandler - Interface for receiving SAX content events. - """ - def setDocumentLocator(self, locator: Locator): - pass - - def startDocument(self): - pass - - def endDocument(self): - pass - - def startPrefixMapping(self, prefix: str, uri: str): - pass - - def endPrefixMapping(self, prefix: str): - pass - - def startElement(self, namespace_uri: str, local_name: str, qname: str, atts: Attributes): - pass - - def endElement(self, namespace_uri: str, local_name: str, qname: str): - pass - - def characters(self, ch: str, start: int, length: int): - pass - - def ignorableWhitespace(self, ch: str, start: int, length: int): - pass - - def processingInstruction(self, target: str, data: str): - pass - - def skippedEntity(self, name: str): - pass - - def toString(self) -> str: - return f"ContentHandler@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# DTDHandler -# ============================================================ - -class DTDHandler(Stub): - """ - org.xml.sax.DTDHandler - Interface for receiving DTD events. - """ - def notationDecl(self, name: str, public_id: str, system_id: str): - pass - - def unparsedEntityDecl(self, name: str, public_id: str, system_id: str, - notation_name: str): - pass - - def toString(self) -> str: - return f"DTDHandler@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# EntityResolver -# ============================================================ - -class EntityResolver(Stub): - """ - org.xml.sax.EntityResolver - Interface for resolving entities. - """ - def resolveEntity(self, public_id: str, system_id: str) -> InputSource: - return InputSource(system_id) - - def toString(self) -> str: - return f"EntityResolver@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# ErrorHandler -# ============================================================ - -class ErrorHandler(Stub): - """ - org.xml.sax.ErrorHandler - Interface for receiving SAX error events. - """ - def warning(self, exception: SAXParseException): - pass - - def error(self, exception: SAXParseException): - pass - - def fatalError(self, exception: SAXParseException): - raise exception - - def toString(self) -> str: - return f"ErrorHandler@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# HandlerBase (deprecated SAX1) -# ============================================================ - -class HandlerBase(Stub): - """ - org.xml.sax.HandlerBase - Deprecated SAX1 base class for handlers. - """ - def setDocumentLocator(self, locator: Locator): pass - def startDocument(self): pass - def endDocument(self): pass - def startElement(self, name: str, atts: AttributeList): pass - def endElement(self, name: str): pass - def characters(self, ch: str, start: int, length: int): pass - def ignorableWhitespace(self, ch: str, start: int, length: int): pass - def processingInstruction(self, target: str, data: str): pass - def notationDecl(self, name: str, public_id: str, system_id: str): pass - def unparsedEntityDecl(self, name: str, public_id: str, system_id: str, notation_name: str): pass - def resolveEntity(self, public_id: str, system_id: str) -> InputSource: return InputSource(system_id) - def warning(self, exception: SAXParseException): pass - def error(self, exception: SAXParseException): pass - def fatalError(self, exception: SAXParseException): raise exception - - def toString(self) -> str: - return f"HandlerBase@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# DocumentHandler (deprecated SAX1) -# ============================================================ - -class DocumentHandler(Stub): - """ - org.xml.sax.DocumentHandler - Deprecated SAX1 interface for document handlers. - """ - def setDocumentLocator(self, locator: Locator): pass - def startDocument(self): pass - def endDocument(self): pass - def startElement(self, name: str, atts: AttributeList): pass - def endElement(self, name: str): pass - def characters(self, ch: str, start: int, length: int): pass - def ignorableWhitespace(self, ch: str, start: int, length: int): pass - def processingInstruction(self, target: str, data: str): pass - - def toString(self) -> str: - return f"DocumentHandler@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# XMLReader -# ============================================================ - -class XMLReader(Stub): - """ - org.xml.sax.XMLReader - Interface for reading an XML document using callbacks. - Wraps Python xml.sax. - """ - - def __init__(self): - super().__init__() - self._content_handler = None - self._dtd_handler = None - self._error_handler = None - self._entity_resolver = None - self._locale = None - self._features = {} - self._properties = {} - - def parse(self, input_source: Any): - """ - Parse an XML document from an InputSource or system ID. - """ - if isinstance(input_source, InputSource): - sys_id = input_source.getSystemId() - elif isinstance(input_source, str): - sys_id = input_source - else: - sys_id = str(input_source) - - try: - parser = _sax.make_parser() - if self._content_handler: - parser.setContentHandler(self._content_handler) - if self._error_handler: - parser.setErrorHandler(self._error_handler) - if self._dtd_handler: - parser.setDTDHandler(self._dtd_handler) - if self._entity_resolver: - parser.setEntityResolver(self._entity_resolver) - parser.parse(sys_id) - except _sax.SAXException as e: - if self._error_handler: - self._error_handler.fatalError( - SAXParseException(str(e), "", sys_id, 0, 0)) - raise SAXException(str(e)) from e - - def setContentHandler(self, handler: ContentHandler): - self._content_handler = handler - - def getContentHandler(self) -> Optional[ContentHandler]: - return self._content_handler - - def setDTDHandler(self, handler: DTDHandler): - self._dtd_handler = handler - - def getDTDHandler(self) -> Optional[DTDHandler]: - return self._dtd_handler - - def setErrorHandler(self, handler: ErrorHandler): - self._error_handler = handler - - def getErrorHandler(self) -> Optional[ErrorHandler]: - return self._error_handler - - def setEntityResolver(self, resolver: EntityResolver): - self._entity_resolver = resolver - - def getEntityResolver(self) -> Optional[EntityResolver]: - return self._entity_resolver - - def setLocale(self, locale): - self._locale = locale - - def getFeature(self, name: str) -> bool: - return self._features.get(name, False) - - def setFeature(self, name: str, value: bool): - if name == "http://xml.org/sax/features/namespaces": - self._features[name] = value - elif name == "http://xml.org/sax/features/validation": - self._features[name] = value - elif name == "http://xml.org/sax/features/external-general-entities": - self._features[name] = value - elif name == "http://xml.org/sax/features/external-parameter-entities": - self._features[name] = value - else: - raise SAXNotRecognizedException(f"Feature not recognized: {name}") - - def getProperty(self, name: str) -> Any: - return self._properties.get(name) - - def setProperty(self, name: str, value: Any): - if name == "http://xml.org/sax/properties/lexical-handler": - self._properties[name] = value - else: - raise SAXNotRecognizedException(f"Property not recognized: {name}") - - def toString(self) -> str: - return f"XMLReader@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# XMLFilter -# ============================================================ - -class XMLFilter(XMLReader): - """ - org.xml.sax.XMLFilter - Interface for an XML filter. - """ - def setParent(self, parent: XMLReader): - self._parent = parent - - def getParent(self) -> Optional[XMLReader]: - return getattr(self, '_parent', None) - - def toString(self) -> str: - return f"XMLFilter@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# XMLFilterImpl -# ============================================================ - -class XMLFilterImpl(XMLFilter, ContentHandler, DTDHandler, EntityResolver, ErrorHandler): - """ - org.xml.sax.helpers.XMLFilterImpl - Base class for XML filters with default implementation. - """ - - def __init__(self): - super().__init__() - self._parent = None - - def setParent(self, parent: XMLReader): - self._parent = parent - - def getParent(self) -> Optional[XMLReader]: - return self._parent - - def parse(self, input_source: Any): - if self._parent: - self._parent.setContentHandler(self) - self._parent.setDTDHandler(self) - self._parent.setEntityResolver(self) - self._parent.setErrorHandler(self) - self._parent.parse(input_source) - - # ContentHandler delegation - def setContentHandler(self, handler: ContentHandler): - self._content_handler = handler - - def getContentHandler(self) -> Optional[ContentHandler]: - return getattr(self, '_content_handler', None) - - def setDTDHandler(self, handler: DTDHandler): - self._dtd_handler = handler - - def getDTDHandler(self) -> Optional[DTDHandler]: - return getattr(self, '_dtd_handler', None) - - def setEntityResolver(self, resolver: EntityResolver): - self._entity_resolver = resolver - - def getEntityResolver(self) -> Optional[EntityResolver]: - return getattr(self, '_entity_resolver', None) - - def setErrorHandler(self, handler: ErrorHandler): - self._error_handler = handler - - def getErrorHandler(self) -> Optional[ErrorHandler]: - return getattr(self, '_error_handler', None) - - def setLocale(self, locale): pass - - def getFeature(self, name: str) -> bool: - return False - - def setFeature(self, name: str, value: bool): pass - - def getProperty(self, name: str): return None - - def setProperty(self, name: str, value): pass - - # Delegated implementations - def resolveEntity(self, public_id: str, system_id: str) -> InputSource: - if self._entity_resolver: - return self._entity_resolver.resolveEntity(public_id, system_id) - return InputSource(system_id) - - def notationDecl(self, name: str, public_id: str, system_id: str): - if self._dtd_handler: - self._dtd_handler.notationDecl(name, public_id, system_id) - - def unparsedEntityDecl(self, name: str, public_id: str, system_id: str, notation_name: str): - if self._dtd_handler: - self._dtd_handler.unparsedEntityDecl(name, public_id, system_id, notation_name) - - def setDocumentLocator(self, locator: Locator): - if self._content_handler: - self._content_handler.setDocumentLocator(locator) - - def startDocument(self): - if self._content_handler: - self._content_handler.startDocument() - - def endDocument(self): - if self._content_handler: - self._content_handler.endDocument() - - def startPrefixMapping(self, prefix: str, uri: str): - if self._content_handler: - self._content_handler.startPrefixMapping(prefix, uri) - - def endPrefixMapping(self, prefix: str): - if self._content_handler: - self._content_handler.endPrefixMapping(prefix) - - def startElement(self, namespace_uri: str, local_name: str, qname: str, atts: Attributes): - if self._content_handler: - self._content_handler.startElement(namespace_uri, local_name, qname, atts) - - def endElement(self, namespace_uri: str, local_name: str, qname: str): - if self._content_handler: - self._content_handler.endElement(namespace_uri, local_name, qname) - - def characters(self, ch: str, start: int, length: int): - if self._content_handler: - self._content_handler.characters(ch, start, length) - - def ignorableWhitespace(self, ch: str, start: int, length: int): - if self._content_handler: - self._content_handler.ignorableWhitespace(ch, start, length) - - def processingInstruction(self, target: str, data: str): - if self._content_handler: - self._content_handler.processingInstruction(target, data) - - def skippedEntity(self, name: str): - if self._content_handler: - self._content_handler.skippedEntity(name) - - def warning(self, exception: SAXParseException): - if self._error_handler: - self._error_handler.warning(exception) - - def error(self, exception: SAXParseException): - if self._error_handler: - self._error_handler.error(exception) - - def fatalError(self, exception: SAXParseException): - if self._error_handler: - self._error_handler.fatalError(exception) - raise exception - - def toString(self) -> str: - return f"XMLFilterImpl@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# DefaultHandler -# ============================================================ - -class DefaultHandler(ContentHandler, DTDHandler, EntityResolver, ErrorHandler): - """ - org.xml.sax.helpers.DefaultHandler - Default base class for SAX2 event handlers. - """ - def setDocumentLocator(self, locator: Locator): pass - def startDocument(self): pass - def endDocument(self): pass - def startPrefixMapping(self, prefix: str, uri: str): pass - def endPrefixMapping(self, prefix: str): pass - def startElement(self, namespace_uri: str, local_name: str, qname: str, atts: Attributes): pass - def endElement(self, namespace_uri: str, local_name: str, qname: str): pass - def characters(self, ch: str, start: int, length: int): pass - def ignorableWhitespace(self, ch: str, start: int, length: int): pass - def processingInstruction(self, target: str, data: str): pass - def skippedEntity(self, name: str): pass - def notationDecl(self, name: str, public_id: str, system_id: str): pass - def unparsedEntityDecl(self, name: str, public_id: str, system_id: str, notation_name: str): pass - def resolveEntity(self, public_id: str, system_id: str) -> InputSource: return InputSource(system_id) - def warning(self, exception: SAXParseException): pass - def error(self, exception: SAXParseException): pass - def fatalError(self, exception: SAXParseException): raise exception - - def toString(self) -> str: - return f"DefaultHandler@{hex(id(self))}" - - def equals(self, other) -> bool: - return self is other - - def hashCode(self) -> int: - return hash(self) & 0x7FFFFFFF - - -# ============================================================ -# Helpers (stub) -# ============================================================ - -class Helpers(Stub): - """ - org.xml.sax.helpers (namespace placeholder) - """ - @staticmethod - def makeParser(parser_class: str = None): - return XMLReader() - - @staticmethod - def parseXML(source: Any, handler: ContentHandler): - reader = XMLReader() - reader.setContentHandler(handler) - reader.parse(source) - - def toString(self) -> str: - return "Helpers" - - def equals(self, other) -> bool: - return isinstance(other, Helpers) - - def hashCode(self) -> int: - return hash("Helpers") & 0x7FFFFFFF \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 07c3838..0000000 --- a/setup.py +++ /dev/null @@ -1,64 +0,0 @@ -from setuptools import setup, find_packages -from pathlib import Path - -long_description = Path("README.md").read_text(encoding="utf-8") - -setup( - name="android-pyapi", - version="1.0.0", - description="Android API for Python — 在桌面/Linux 上原生运行 Android API", - long_description=long_description, - long_description_content_type="text/markdown", - license="MIT", - author="shuiyu1123", - url="https://gitee.com/shuiyu1123/Android-pyApi", - packages=find_packages(), - python_requires=">=3.8", - install_requires=[ - # 核心依赖(全部可选,缺失时自动降级) - ], - extras_require={ - "linux": [ - "psutil", - "requests", - "Pillow", - "opencv-python", - "numpy", - ], - "windows": [ - "psutil", - "requests", - "Pillow", - "opencv-python", - "numpy", - "pyautogui", - "pyperclip", - "screeninfo", - "pywin32", - "wmi", - "pycaw", - "comtypes", - "win10toast", - ], - "dev": [ - "pytest", - "pytest-cov", - ], - }, - classifiers=[ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Operating System :: POSIX :: Linux", - "Operating System :: MacOS", - "Operating System :: Microsoft :: Windows", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: System :: Hardware", - ], -) diff --git a/test_scripts/network_test.js b/test_scripts/network_test.js deleted file mode 100644 index e69de29..0000000 diff --git a/test_scripts/quick_start.js b/test_scripts/quick_start.js deleted file mode 100644 index e69de29..0000000 diff --git a/test_scripts/system_tools.js b/test_scripts/system_tools.js deleted file mode 100644 index e69de29..0000000 diff --git a/test_scripts/test_android_api.py b/test_scripts/test_android_api.py deleted file mode 100644 index 545bd7c..0000000 --- a/test_scripts/test_android_api.py +++ /dev/null @@ -1,141 +0,0 @@ -""" -Android API 综合测试脚本 -测试 Android-pyApi 的核心 API 功能 -""" - -import sys -import os -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from android_pyapi import ( - Intent, Uri, Bundle, ComponentName, - ArrayList, HashMap, HashSet, JSONObject, JSONArray, - String, StringBuilder, Integer, Math, System, Thread, - File, FileInputStream, FileOutputStream, - Log, Base64, Scanner, - Calendar, Date, SimpleDateFormat, -) - - -def test_intent(): - """测试 Intent""" - print("\n[Intent 测试]") - intent = Intent("android.intent.action.VIEW") - intent.setData(Uri.parse("https://example.com")) - intent.putExtra("key1", "value1") - intent.putExtra("key2", 42) - print(f" Action: {intent.getAction()}") - print(f" Data: {intent.getData()}") - print(f" Extra String: {intent.getStringExtra('key1')}") - print(f" Extra Int: {intent.getIntExtra('key2', 0)}") - - -def test_collections(): - """测试集合类""" - print("\n[集合类测试]") - lst = ArrayList() - lst.add("Apple") - lst.add("Banana") - lst.add("Cherry") - print(f" ArrayList size: {lst.size()}") - print(f" First: {lst.get(0)}") - - m = HashMap() - m.put("name", "Android-pyApi") - m.put("version", "1.0.0") - print(f" HashMap get: {m.get('name')}") - - json = JSONObject() - json.put("platform", "Windows") - json.put("ok", True) - print(f" JSONObject: {json.toString()}") - - -def test_string(): - """测试字符串类""" - print("\n[字符串测试]") - s = String("Hello, Android-pyApi!") - print(f" Length: {s.length()}") - print(f" Upper: {s.toUpperCase()}") - print(f" Contains 'Android': {s.contains('Android')}") - - sb = StringBuilder() - sb.append("Hello") - sb.append(", World!") - print(f" StringBuilder: {sb.toString()}") - - -def test_file(): - """测试文件类""" - print("\n[文件测试]") - import tempfile - temp_file = os.path.join(tempfile.gettempdir(), "android_pyapi_test.txt") - - f = File(temp_file) - print(f" Path: {f.getAbsolutePath()}") - - with open(temp_file, 'w') as file: - file.write("Android-pyApi test") - - print(f" Exists: {f.exists()}") - print(f" Name: {f.getName()}") - print(f" Size: {f.length()} bytes") - - os.remove(temp_file) - print(f" After delete: {f.exists()}") - - -def test_base64(): - """测试 Base64""" - print("\n[Base64 测试]") - text = "Hello, Android-pyApi!" - encoded = Base64.encodeToString(text.encode('utf-8')) - decoded = Base64.decode(encoded).decode('utf-8') - print(f" Original: {text}") - print(f" Encoded: {encoded}") - print(f" Decoded: {decoded}") - print(f" Match: {text == decoded}") - - -def test_math(): - """测试数学类""" - print("\n[数学测试]") - print(f" PI: {Math.PI}") - print(f" abs(-42): {Math.abs(-42)}") - print(f" max(10, 20): {Math.max(10, 20)}") - print(f" sqrt(144): {Math.sqrt(144)}") - print(f" pow(2, 10): {Math.pow(2, 10)}") - - -def test_calendar(): - """测试日历类""" - print("\n[日历测试]") - cal = Calendar.getInstance() - print(f" Year: {cal.get(Calendar.YEAR)}") - print(f" Month: {cal.get(Calendar.MONTH) + 1}") - print(f" Day: {cal.get(Calendar.DAY_OF_MONTH)}") - print(f" Hour: {cal.get(Calendar.HOUR_OF_DAY)}") - print(f" Minute: {cal.get(Calendar.MINUTE)}") - - -def main(): - """主测试函数""" - print("=" * 60) - print("Android-pyApi 综合测试") - print("=" * 60) - - test_intent() - test_collections() - test_string() - test_file() - test_base64() - test_math() - test_calendar() - - print("\n" + "=" * 60) - print("所有测试完成!") - print("=" * 60) - - -if __name__ == "__main__": - main() diff --git a/test_scripts/test_extended_api.py b/test_scripts/test_extended_api.py deleted file mode 100644 index 5f09fd8..0000000 --- a/test_scripts/test_extended_api.py +++ /dev/null @@ -1,400 +0,0 @@ -# -*- coding: utf-8 -*- -""" -扩展 Android API 测试脚本 -测试 android.os, android.content, android.app, android.widget, android.graphics 等模块 -""" - -import sys -import os -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -def test_os_module(): - """测试 android.os 模块""" - print("\n" + "=" * 60) - print("android.os 模块测试") - print("=" * 60) - - from android_pyapi.android.os import ( - Build, SystemClock, Environment, Process, - Bundle, Handler, Looper, Message, - BatteryManager, PowerManager, Vibrator, - AsyncTask, CountDownTimer, Messenger, - FileObserver, FileUtils, PatternMatcher, - ) - - # Build 测试 - print("\n[Build 设备信息]") - print(f" MODEL: {Build.MODEL}") - print(f" MANUFACTURER: {Build.MANUFACTURER}") - print(f" BRAND: {Build.BRAND}") - print(f" DEVICE: {Build.DEVICE}") - print(f" FINGERPRINT: {Build.FINGERPRINT}") - print(f" SDK_INT: {Build.VERSION.SDK_INT}") - print(f" RELEASE: {Build.VERSION.RELEASE}") - - # SystemClock 测试 - print("\n[SystemClock 系统时钟]") - print(f" 启动时间: {SystemClock.elapsedRealtime()} ms") - print(f" 运行时间: {SystemClock.uptimeMillis()} ms") - - # Environment 测试 - print("\n[Environment 环境目录]") - print(f" 外部存储: {Environment.getExternalStorageDirectory()}") - print(f" 下载目录: {Environment.getExternalStoragePublicDirectory('Download')}") - print(f" 数据目录: {Environment.getDataDirectory()}") - print(f" 根目录: {Environment.getRootDirectory()}") - - # Process 测试 - print("\n[Process 进程信息]") - print(f" PID: {Process.myPid()}") - print(f" TID: {Process.myTid()}") - - # Bundle 测试 - print("\n[Bundle 数据容器]") - bundle = Bundle() - bundle.putString("name", "Android-pyApi") - bundle.putInt("version", 100) - bundle.putBoolean("windows", True) - print(f" name: {bundle.getString('name')}") - print(f" version: {bundle.getInt('version')}") - print(f" windows: {bundle.getBoolean('windows')}") - print(f" size: {bundle.size()}") - - # BatteryManager 测试 - print("\n[BatteryManager 电池管理]") - bm = BatteryManager() - print(f" 电量: {bm.getLevel()}%") - print(f" 充电: {bm.isCharging()}") - print(f" 存在: {bm.isPresent()}") - print(f" 技术: {bm.getTechnology()}") - - # PowerManager 测试 - print("\n[PowerManager 电源管理]") - pm = PowerManager() - print(f" 交互: {pm.isInteractive()}") - print(f" 屏幕: {pm.isScreenOn()}") - - # Vibrator 测试 - print("\n[Vibrator 振动器]") - vib = Vibrator() - print(f" 有振动器: {vib.hasVibrator()}") - - # PatternMatcher 测试 - print("\n[PatternMatcher 模式匹配]") - pm_literal = PatternMatcher("/sdcard/test.txt", PatternMatcher.PATTERN_LITERAL) - pm_prefix = PatternMatcher("/sdcard/", PatternMatcher.PATTERN_PREFIX) - print(f" literal match: {pm_literal.match('/sdcard/test.txt')}") - print(f" prefix match: {pm_prefix.match('/sdcard/test.txt')}") - - # FileUtils 测试 - print("\n[FileUtils 文件工具]") - test_file = os.path.join(os.environ.get('TEMP', '/tmp'), 'android_pyapi_test.txt') - FileUtils.stringToFile(test_file, "Hello, Android-pyApi!") - content = FileUtils.readFileToString(test_file) - print(f" 写入读取: {content}") - os.remove(test_file) - - -def test_content_module(): - """测试 android.content 模块""" - print("\n" + "=" * 60) - print("android.content 模块测试") - print("=" * 60) - - from android_pyapi.android.content import ( - Uri, ContentValues, ContentResolver, Context, - SharedPreferences, IntentFilter, BroadcastReceiver, - ComponentName, ClipData, ClipDescription, - ContentProvider, ContentProviderClient, - ClipboardManager, - ) - - # Uri 测试 - print("\n[Uri 统一资源标识符]") - uri = Uri.parse("https://example.com/path?query=1&foo=bar#fragment") - print(f" scheme: {uri.getScheme()}") - print(f" host: {uri.getHost()}") - print(f" path: {uri.getPath()}") - print(f" query: {uri.getQuery()}") - print(f" fragment: {uri.getFragment()}") - print(f" toString: {uri.toString()}") - - # ContentValues 测试 - print("\n[ContentValues 内容值]") - cv = ContentValues() - cv.put("title", "Test") - cv.put("value", 42) - cv.put("flag", True) - print(f" title: {cv.getAsString('title')}") - print(f" value: {cv.getAsInteger('value')}") - print(f" flag: {cv.getAsBoolean('flag')}") - print(f" size: {cv.size()}") - - # Context 测试 - print("\n[Context 应用上下文]") - ctx = Context() - print(f" packageName: {ctx.getPackageName()}") - print(f" filesDir: {ctx.getFilesDir()}") - print(f" cacheDir: {ctx.getCacheDir()}") - print(f" dataDir: {ctx.getDataDir()}") - - # SharedPreferences 测试 - print("\n[SharedPreferences 持久化存储]") - sp = ctx.getSharedPreferences("test_prefs") - editor = sp.edit() - editor.putString("username", "test_user") - editor.putInt("login_count", 5) - editor.putBoolean("remember", True) - editor.commit() - print(f" username: {sp.getString('username')}") - print(f" login_count: {sp.getInt('login_count')}") - print(f" remember: {sp.getBoolean('remember')}") - - # ComponentName 测试 - print("\n[ComponentName 组件名]") - cn = ComponentName("com.example.app", "com.example.app.MainActivity") - print(f" package: {cn.getPackageName()}") - print(f" class: {cn.getClassName()}") - print(f" short: {cn.getShortClassName()}") - print(f" flatten: {cn.flattenToString()}") - - # ClipData 测试 - print("\n[ClipData 剪贴板数据]") - clip = ClipData.newPlainText("label", "Hello, Clipboard!") - item = clip.getItemAt(0) - print(f" text: {item.getText()}") - print(f" itemCount: {clip.getItemCount()}") - - # ClipboardManager 测试 - print("\n[ClipboardManager 剪贴板管理]") - cm = ClipboardManager() - cm.setText("Test Clipboard") - print(f" getText: {cm.getText()}") - print(f" hasText: {cm.hasText()}") - - -def test_app_module(): - """测试 android.app 模块""" - print("\n" + "=" * 60) - print("android.app 模块测试") - print("=" * 60) - - from android_pyapi.android.app import ( - Intent, Activity, Service, - Notification, NotificationManager, - AlarmManager, ActivityManager, - ) - - # Intent 测试 - print("\n[Intent 意图]") - intent = Intent(Intent.ACTION_VIEW) - intent.setData("https://example.com") - intent.putExtra("title", "Test") - intent.putExtra("count", 42) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - print(f" action: {intent.getAction()}") - print(f" data: {intent.getData()}") - print(f" title: {intent.getStringExtra('title')}") - print(f" count: {intent.getIntExtra('count')}") - print(f" flags: {hex(intent.getFlags())}") - - # Notification 测试 - print("\n[Notification 通知]") - notif = Notification() - notif.title = "Test Notification" - notif.text = "This is a test" - print(f" title: {notif.title}") - print(f" text: {notif.text}") - - # NotificationManager 测试 - print("\n[NotificationManager 通知管理]") - nm = NotificationManager() - print(f" 类型: {type(nm).__name__}") - - # AlarmManager 测试 - print("\n[AlarmManager 闹钟管理]") - am = AlarmManager() - print(f" 类型: {type(am).__name__}") - - -def test_widget_module(): - """测试 android.widget 模块""" - print("\n" + "=" * 60) - print("android.widget 模块测试") - print("=" * 60) - - try: - from android_pyapi.android.widget import ( - Toast, AlertDialog, Button, TextView, - EditText, ListView, RecyclerView, - ) - - # Toast 测试 - print("\n[Toast 提示]") - toast = Toast.makeText(None, "Hello, Toast!", Toast.LENGTH_SHORT) - print(f" text: {toast._text}") - print(f" duration: {toast._duration}") - - # AlertDialog 测试 - print("\n[AlertDialog 对话框]") - dialog = AlertDialog(None) - print(f" 类型: {type(dialog).__name__}") - - except ImportError as e: - print(f" widget 模块导入失败: {e}") - - -def test_graphics_module(): - """测试 android.graphics 模块""" - print("\n" + "=" * 60) - print("android.graphics 模块测试") - print("=" * 60) - - try: - from android_pyapi.android.graphics import ( - Bitmap, Canvas, Paint, Color, - Rect, RectF, Path, Matrix, - ) - - # Color 测试 - print("\n[Color 颜色]") - print(f" RED: {hex(Color.RED)}") - print(f" GREEN: {hex(Color.GREEN)}") - print(f" BLUE: {hex(Color.BLUE)}") - print(f" WHITE: {hex(Color.WHITE)}") - print(f" BLACK: {hex(Color.BLACK)}") - print(f" argb(255,128,64,32): {hex(Color.argb(255, 128, 64, 32))}") - - # Bitmap 测试 - print("\n[Bitmap 位图]") - bm = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888) - print(f" width: {bm.getWidth()}") - print(f" height: {bm.getHeight()}") - print(f" config: {bm.getConfig()}") - - # Paint 测试 - print("\n[Paint 画笔]") - paint = Paint() - paint.setColor(Color.RED) - paint.setTextSize(24.0) - print(f" color: {hex(paint.getColor())}") - print(f" textSize: {paint.getTextSize()}") - - # Rect 测试 - print("\n[Rect 矩形]") - rect = Rect(10, 20, 100, 200) - print(f" left: {rect.left}") - print(f" top: {rect.top}") - print(f" right: {rect.right}") - print(f" bottom: {rect.bottom}") - print(f" width: {rect.width()}") - print(f" height: {rect.height()}") - - except ImportError as e: - print(f" graphics 模块导入失败: {e}") - - -def test_java_module(): - """测试 java 模块""" - print("\n" + "=" * 60) - print("java 模块测试") - print("=" * 60) - - from android_pyapi.java.lang import ( - String, StringBuilder, Integer, Math, System, Thread, - ) - from android_pyapi.java.io import ( - File, FileInputStream, FileOutputStream, - ) - from android_pyapi.java.util import ( - ArrayList, HashMap, HashSet, - ) - from android_pyapi.org.json import JSONObject, JSONArray - - # String 测试 - print("\n[String 字符串]") - s = String("Hello, Android-pyApi!") - print(f" length: {s.length()}") - print(f" upper: {s.toUpperCase()}") - print(f" contains('Android'): {s.contains('Android')}") - print(f" substring(7): {s.substring(7)}") - - # StringBuilder 测试 - print("\n[StringBuilder 字符串构建器]") - sb = StringBuilder() - sb.append("Hello") - sb.append(", ") - sb.append("World!") - print(f" result: {sb.toString()}") - print(f" length: {sb.length()}") - - # Integer 测试 - print("\n[Integer 整数]") - print(f" parseInt('42'): {Integer.parseInt('42')}") - print(f" MAX_VALUE: {Integer.MAX_VALUE}") - print(f" MIN_VALUE: {Integer.MIN_VALUE}") - - # Math 测试 - print("\n[Math 数学]") - print(f" PI: {Math.PI}") - print(f" abs(-42): {Math.abs(-42)}") - print(f" max(10, 20): {Math.max(10, 20)}") - print(f" sqrt(144): {Math.sqrt(144)}") - print(f" pow(2, 10): {Math.pow(2, 10)}") - - # ArrayList 测试 - print("\n[ArrayList 列表]") - lst = ArrayList() - lst.add("Apple") - lst.add("Banana") - lst.add("Cherry") - print(f" size: {lst.size()}") - print(f" first: {lst.get(0)}") - print(f" contains('Banana'): {lst.contains('Banana')}") - - # HashMap 测试 - print("\n[HashMap 哈希表]") - m = HashMap() - m.put("name", "Android-pyApi") - m.put("version", "1.0.0") - print(f" name: {m.get('name')}") - print(f" size: {m.size()}") - print(f" containsKey('name'): {m.containsKey('name')}") - - # JSONObject 测试 - print("\n[JSONObject JSON 对象]") - json = JSONObject() - json.put("platform", "Windows") - json.put("python", True) - json.put("version", 3.12) - print(f" toString: {json.toString()}") - print(f" platform: {json.getString('platform')}") - - # File 测试 - print("\n[File 文件]") - f = File(os.environ.get('TEMP', '/tmp'), "test_file.txt") - print(f" path: {f.getAbsolutePath()}") - print(f" name: {f.getName()}") - print(f" parent: {f.getParent()}") - - -def main(): - """主测试函数""" - print("=" * 60) - print("Android-pyApi 扩展 API 测试") - print("=" * 60) - - test_os_module() - test_content_module() - test_app_module() - test_widget_module() - test_graphics_module() - test_java_module() - - print("\n" + "=" * 60) - print("所有测试完成!") - print("=" * 60) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/test_scripts/test_network.py b/test_scripts/test_network.py deleted file mode 100644 index 66ab82c..0000000 --- a/test_scripts/test_network.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -网络测试脚本 - 基于 Android-pyApi -模拟 Operit 的 network_test.js 功能 -""" - -import sys -import os -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from android_pyapi import ( - ConnectivityManager, NetworkInfo, Intent, Uri -) -from android_pyapi._windows_impl import WindowsNetwork - - -def test_network(): - """测试网络功能""" - print("=" * 60) - print("Android-pyApi 网络测试") - print("=" * 60) - - # 1. 获取 IP 地址 - print("\n[1] IP 地址:") - ip = WindowsNetwork.get_ip_address() - print(f" 本机 IP: {ip}") - - # 2. 网络连通性 - print("\n[2] 网络连通性:") - connected = WindowsNetwork.get_connectivity() - print(f" 连接状态: {'已连接' if connected else '未连接'}") - - # 3. WiFi 信息 - print("\n[3] WiFi 信息:") - wifi = WindowsNetwork.get_wifi_info() - print(f" SSID: {wifi.get('ssid', 'N/A') or 'N/A'}") - print(f" 信号: {wifi.get('signal', 0)}%") - print(f" 状态: {wifi.get('status', 'unknown')}") - - # 4. DNS 服务器 - print("\n[4] DNS 服务器:") - dns = WindowsNetwork.get_dns_servers() - for d in dns[:3]: - print(f" - {d}") - - # 5. 默认网关 - print("\n[5] 默认网关:") - gw = WindowsNetwork.get_gateway() - print(f" 网关: {gw}") - - # 6. MAC 地址 - print("\n[6] MAC 地址:") - mac = WindowsNetwork.get_mac_address() - print(f" MAC: {mac}") - - # 7. 网络接口 - print("\n[7] 网络接口:") - interfaces = WindowsNetwork.get_interfaces() - for iface in interfaces[:5]: - print(f" - {iface.get('ifname', 'N/A')}: {iface.get('operstate', 'N/A')}") - - # 8. 流量统计 - print("\n[8] 流量统计:") - traffic = WindowsNetwork.get_rx_tx_bytes() - print(f" 接收: {traffic.get('rx_bytes', 0) / 1024 / 1024:.2f} MB") - print(f" 发送: {traffic.get('tx_bytes', 0) / 1024 / 1024:.2f} MB") - - print("\n" + "=" * 60) - print("测试完成!") - print("=" * 60) - - -if __name__ == "__main__": - test_network() diff --git a/test_scripts/test_system.py b/test_scripts/test_system.py deleted file mode 100644 index 4cebd5b..0000000 --- a/test_scripts/test_system.py +++ /dev/null @@ -1,97 +0,0 @@ -""" -系统工具测试脚本 - 基于 Android-pyApi -模拟 Operit 的 system_tools.js 功能 -""" - -import sys -import os -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from android_pyapi import ( - Build, PowerManager, BatteryManager, ActivityManager, - Environment, Log -) -from android_pyapi._windows_impl import ( - WindowsPower, WindowsProcess, WindowsDisplay, WindowsShell -) - - -def test_system(): - """测试系统工具功能""" - print("=" * 60) - print("Android-pyApi 系统工具测试") - print("=" * 60) - - # 1. 设备信息 (Build) - print("\n[1] 设备信息 (Build):") - print(f" MODEL: {Build.MODEL}") - print(f" MANUFACTURER: {Build.MANUFACTURER}") - print(f" BRAND: {Build.BRAND}") - print(f" DEVICE: {Build.DEVICE}") - print(f" PRODUCT: {Build.PRODUCT}") - print(f" FINGERPRINT: {Build.FINGERPRINT}") - - # 2. 系统信息 - print("\n[2] 系统信息:") - sys_info = WindowsProcess.get_system_info() - print(f" 主机名: {sys_info.get('hostname', 'N/A')}") - print(f" 平台: {sys_info.get('platform', 'N/A')}") - print(f" 版本: {sys_info.get('windows_version', 'N/A')}") - print(f" 架构: {sys_info.get('arch', 'N/A')}") - print(f" Python: {sys_info.get('python', 'N/A')}") - if 'cpu_count' in sys_info: - print(f" CPU 核心: {sys_info['cpu_count']}") - if 'memory_total' in sys_info: - print(f" 内存总量: {sys_info['memory_total'] / 1024 / 1024 / 1024:.2f} GB") - - # 3. 电源信息 - print("\n[3] 电源信息:") - battery = WindowsPower.get_battery_info() - print(f" 电池存在: {battery['present']}") - print(f" 电量: {battery['level']}%") - print(f" 状态: {battery['status']}") - - power = WindowsPower.get_power_status() - print(f" 充电中: {power['is_charging']}") - - # 4. CPU 和内存 - print("\n[4] CPU 和内存:") - cpu = WindowsPower.get_cpu_usage() - print(f" CPU 使用率: {cpu}%") - - mem = WindowsPower.get_memory_info() - print(f" 内存使用率: {mem['percent']}%") - print(f" 可用内存: {mem['available'] / 1024 / 1024 / 1024:.2f} GB") - - # 5. 显示信息 - print("\n[5] 显示信息:") - w, h = WindowsDisplay.get_screen_size() - print(f" 屏幕尺寸: {w}x{h}") - - x, y = WindowsDisplay.get_mouse_position() - print(f" 鼠标位置: ({x}, {y})") - - # 6. 进程列表 - print("\n[6] 进程列表 (前10个):") - procs = WindowsProcess.list_processes() - for p in procs[:10]: - print(f" - {p.get('name', 'N/A')} (PID: {p.get('pid', 'N/A')})") - - # 7. 运行中的应用 - print("\n[7] 运行中的应用 (前5个):") - apps = WindowsProcess.get_running_apps() - for app in apps[:5]: - print(f" - {app.get('name', 'N/A')}") - - # 8. Shell 命令测试 - print("\n[8] Shell 命令测试:") - result = WindowsShell.exec("echo Hello from Android-pyApi!") - print(f" 输出: {result.get('stdout', 'N/A')}") - - print("\n" + "=" * 60) - print("测试完成!") - print("=" * 60) - - -if __name__ == "__main__": - test_system() diff --git a/test_scripts/time.js b/test_scripts/time.js deleted file mode 100644 index e69de29..0000000 diff --git a/test_windows.py b/test_windows.py deleted file mode 100644 index ae038ff..0000000 --- a/test_windows.py +++ /dev/null @@ -1,145 +0,0 @@ -""" -Windows 实现层测试脚本 -""" - -import sys -import platform - -print("=" * 60) -print(f"平台: {platform.system()} {platform.release()}") -print(f"Python: {platform.python_version()}") -print("=" * 60) - -# 测试基本导入 -print("\n[1] 测试基本导入...") -try: - from android_pyapi import Intent, Uri, Bundle, JSONObject, ArrayList - print(" ✓ 基本 Android API 导入成功") -except Exception as e: - print(f" ✗ 基本导入失败: {e}") - -# 测试 Intent -print("\n[2] 测试 Intent...") -try: - intent = Intent("android.intent.action.VIEW") - intent.setData(Uri.parse("https://example.com")) - intent.putExtra("key", "value") - print(f" ✓ Action: {intent.getAction()}") - print(f" ✓ Data: {intent.getData()}") - print(f" ✓ Extra: {intent.getStringExtra('key')}") -except Exception as e: - print(f" ✗ Intent 测试失败: {e}") - -# 测试集合类 -print("\n[3] 测试集合类...") -try: - lst = ArrayList() - lst.add("hello") - lst.add("world") - print(f" ✓ ArrayList size: {lst.size()}") - print(f" ✓ ArrayList[0]: {lst.get(0)}") - - json = JSONObject() - json.put("name", "test") - json.put("version", 42) - print(f" ✓ JSONObject: {json.toString()}") -except Exception as e: - print(f" ✗ 集合类测试失败: {e}") - -# 测试 Windows 实现层 -print("\n[4] 测试 Windows 实现层...") -try: - from android_pyapi._windows_impl import ( - IS_WINDOWS, get_windows_capabilities, - WindowsNetwork, WindowsPower, WindowsDisplay, - WindowsClipboard, WindowsProcess, WindowsLocation - ) - print(f" ✓ IS_WINDOWS: {IS_WINDOWS}") - - caps = get_windows_capabilities() - print(f" ✓ 能力检测: {sum(caps.values())}/{len(caps)} 项可用") -except Exception as e: - print(f" ✗ Windows 实现层导入失败: {e}") - import traceback - traceback.print_exc() - -# 测试网络功能 -print("\n[5] 测试网络功能...") -try: - ip = WindowsNetwork.get_ip_address() - print(f" ✓ IP 地址: {ip}") - - connected = WindowsNetwork.get_connectivity() - print(f" ✓ 网络连通: {connected}") - - wifi = WindowsNetwork.get_wifi_info() - print(f" ✓ WiFi SSID: {wifi.get('ssid', 'N/A')}") -except Exception as e: - print(f" ✗ 网络测试失败: {e}") - -# 测试电源信息 -print("\n[6] 测试电源信息...") -try: - battery = WindowsPower.get_battery_info() - print(f" ✓ 电池存在: {battery['present']}") - print(f" ✓ 电池电量: {battery['level']}%") - print(f" ✓ 电池状态: {battery['status']}") - - cpu = WindowsPower.get_cpu_usage() - print(f" ✓ CPU 使用率: {cpu}%") - - mem = WindowsPower.get_memory_info() - print(f" ✓ 内存使用: {mem['percent']}%") -except Exception as e: - print(f" ✗ 电源测试失败: {e}") - -# 测试显示信息 -print("\n[7] 测试显示信息...") -try: - w, h = WindowsDisplay.get_screen_size() - print(f" ✓ 屏幕尺寸: {w}x{h}") - - x, y = WindowsDisplay.get_mouse_position() - print(f" ✓ 鼠标位置: ({x}, {y})") -except Exception as e: - print(f" ✗ 显示测试失败: {e}") - -# 测试剪贴板 -print("\n[8] 测试剪贴板...") -try: - test_text = "Android-pyApi 测试文本" - WindowsClipboard.write(test_text) - read_text = WindowsClipboard.read() - if read_text == test_text: - print(f" ✓ 剪贴板读写成功") - else: - print(f" ⚠ 剪贴板内容不匹配: '{read_text}'") -except Exception as e: - print(f" ✗ 剪贴板测试失败: {e}") - -# 测试进程管理 -print("\n[9] 测试进程管理...") -try: - procs = WindowsProcess.list_processes() - print(f" ✓ 进程数量: {len(procs)}") - - sys_info = WindowsProcess.get_system_info() - print(f" ✓ 主机名: {sys_info['hostname']}") - print(f" ✓ 系统: {sys_info['platform']} {sys_info['windows_version']}") -except Exception as e: - print(f" ✗ 进程管理测试失败: {e}") - -# 测试位置信息 -print("\n[10] 测试位置信息...") -try: - loc = WindowsLocation.get_location() - print(f" ✓ 来源: {loc['source']}") - print(f" ✓ 经纬度: {loc['latitude']}, {loc['longitude']}") - if loc.get('city'): - print(f" ✓ 城市: {loc['city']}") -except Exception as e: - print(f" ✗ 位置测试失败: {e}") - -print("\n" + "=" * 60) -print("测试完成!") -print("=" * 60)