From 97dd6a739acc94f108c96718679bf7d9e74895ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E5=AE=87=E5=AE=81?= Date: Thu, 8 Oct 2026 11:37:25 +0700 Subject: [PATCH] Initial commit: 91dj android client dev_1.0.0 Co-Authored-By: Claude Opus 5.5 --- .fvmrc | 3 + .gitignore | 193 ++ README.md | 16 + analysis_options.yaml | 35 + analytics_sdk/.gitignore | 29 + analytics_sdk/LICENSE | 1 + analytics_sdk/README.md | 1158 ++++++++++++ analytics_sdk/SDK_USAGE_GUIDE.md | 1027 +++++++++++ analytics_sdk/analysis_options.yaml | 4 + analytics_sdk/android/.gitignore | 9 + analytics_sdk/android/build.gradle | 64 + .../gradle/wrapper/gradle-wrapper.properties | 7 + analytics_sdk/android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 3 + .../analytics_sdk/AnalyticsSdkPlugin.kt | 491 +++++ .../analytics_sdk/ClientIdentifierResolver.kt | 27 + .../ClientIdentifierResolverTest.kt | 80 + .../data_plus_sdk/DataPlusSdkPluginTest.kt | 27 + analytics_sdk/ios/.gitignore | 38 + analytics_sdk/ios/Assets/.gitkeep | 0 .../ios/Classes/DataPlusSdkPlugin.swift | 320 ++++ analytics_sdk/ios/analytics_sdk.podspec | 23 + analytics_sdk/lib/analytics_sdk.dart | 1589 +++++++++++++++++ analytics_sdk/lib/analytics_sdk_web.dart | 12 + analytics_sdk/lib/config/api_config.dart | 84 + analytics_sdk/lib/config/sdk_config.dart | 334 ++++ analytics_sdk/lib/const/event_type.dart | 189 ++ analytics_sdk/lib/entity/ad_click_event.dart | 79 + .../lib/entity/ad_impression_event.dart | 97 + analytics_sdk/lib/entity/ad_sdk_events.dart | 459 +++++ .../lib/entity/advertising_event.dart | 57 + analytics_sdk/lib/entity/analytics_tab.dart | 24 + analytics_sdk/lib/entity/analytics_utils.dart | 5 + .../lib/entity/app_install_event.dart | 46 + .../lib/entity/app_lifecycle_event.dart | 189 ++ .../lib/entity/app_page_item_event.dart | 217 +++ .../lib/entity/app_page_view_event.dart | 181 ++ .../lib/entity/app_session_summary_event.dart | 128 ++ analytics_sdk/lib/entity/comic_event.dart | 130 ++ .../lib/entity/keyword_click_event.dart | 71 + .../lib/entity/keyword_search_event.dart | 65 + .../lib/entity/navigation_event.dart | 67 + .../entity/network_state_change_event.dart | 89 + analytics_sdk/lib/entity/novel_event.dart | 130 ++ .../lib/entity/page_click_event.dart | 284 +++ .../entity/recommend_list_click_event.dart | 87 + .../lib/entity/recommend_list_view_event.dart | 88 + analytics_sdk/lib/entity/sdk_init_event.dart | 32 + analytics_sdk/lib/entity/video_event.dart | 132 ++ analytics_sdk/lib/enum/ad_type_enum.dart | 40 + analytics_sdk/lib/enum/auth_type_enum.dart | 25 + analytics_sdk/lib/enum/banner_event_enum.dart | 11 + .../lib/enum/click_item_type_enum.dart | 34 + .../lib/enum/consume_reason_enum.dart | 25 + analytics_sdk/lib/enum/currency_enum.dart | 19 + analytics_sdk/lib/enum/device_enum.dart | 11 + analytics_sdk/lib/enum/order_type_enum.dart | 19 + analytics_sdk/lib/enum/page_route_enum.dart | 174 ++ analytics_sdk/lib/enum/pay_type_enum.dart | 25 + .../lib/enum/read_behavior_enum.dart | 35 + .../lib/enum/recommend_content_type_enum.dart | 28 + analytics_sdk/lib/enum/user_type_enum.dart | 28 + .../lib/enum/video_content_type_enum.dart | 30 + analytics_sdk/lib/enum/video_event_enum.dart | 38 + .../extension/tab_analytics_extension.dart | 51 + .../lib/manager/ad_impression_manager.dart | 105 ++ analytics_sdk/lib/manager/domain_manager.dart | 204 +++ .../lib/manager/event_persistence.dart | 7 + .../lib/manager/event_persistence_native.dart | 398 +++++ .../lib/manager/event_persistence_web.dart | 35 + .../lib/manager/event_processor.dart | 552 ++++++ .../manager/event_type_config_manager.dart | 168 ++ .../manager/network_state_fields_stub.dart | 3 + .../lib/manager/network_state_fields_web.dart | 65 + .../lib/manager/page_click_auto_fields.dart | 2 + .../manager/page_click_auto_fields_stub.dart | 33 + .../manager/page_click_auto_fields_web.dart | 77 + .../lib/manager/page_name_manager.dart | 177 ++ .../page_observer_registration_guard.dart | 41 + .../lib/manager/page_view_auto_fields.dart | 2 + .../manager/page_view_auto_fields_stub.dart | 1 + .../manager/page_view_auto_fields_web.dart | 27 + .../lib/manager/sdk_activation_store.dart | 21 + analytics_sdk/lib/manager/sdk_context.dart | 439 +++++ .../lib/manager/sdk_init_payload_builder.dart | 239 +++ .../lib/manager/sdk_init_platform_fields.dart | 3 + .../sdk_init_platform_fields_native.dart | 61 + .../manager/sdk_init_platform_fields_web.dart | 540 ++++++ .../lib/manager/session_behavior_manager.dart | 292 +++ .../lib/manager/session_behavior_monitor.dart | 2 + .../session_behavior_monitor_stub.dart | 10 + .../manager/session_behavior_monitor_web.dart | 195 ++ .../lib/manager/session_manager.dart | 316 ++++ .../lib/manager/session_metrics_manager.dart | 300 ++++ .../lib/manager/session_sensor_proximity.dart | 2 + .../session_sensor_proximity_native.dart | 17 + .../session_sensor_proximity_stub.dart | 1 + .../lib/manager/session_sensor_sampler.dart | 467 +++++ analytics_sdk/lib/manager/user_manager.dart | 35 + .../lib/observer/app_lifecycle_observer.dart | 160 ++ .../app_lifecycle_web_unload_stub.dart | 8 + .../app_lifecycle_web_unload_web.dart | 88 + .../lib/observer/page_lifecycle_observer.dart | 458 +++++ analytics_sdk/lib/utils/aes_gcm_util.dart | 346 ++++ .../lib/utils/device_fingerprint_native.dart | 52 + .../lib/utils/device_fingerprint_util.dart | 216 +++ .../lib/utils/device_fingerprint_web.dart | 78 + analytics_sdk/lib/utils/device_id_native.dart | 29 + analytics_sdk/lib/utils/device_id_util.dart | 13 + analytics_sdk/lib/utils/device_id_web.dart | 18 + .../lib/utils/device_info_native.dart | 60 + analytics_sdk/lib/utils/device_info_util.dart | 36 + analytics_sdk/lib/utils/device_info_web.dart | 86 + .../lib/utils/environment_info_native.dart | 119 ++ .../lib/utils/environment_info_util.dart | 291 +++ .../lib/utils/environment_info_web.dart | 181 ++ analytics_sdk/lib/utils/event_validator.dart | 592 ++++++ .../lib/utils/js_interop_compat.dart | 2 + .../lib/utils/js_interop_legacy.dart | 23 + .../lib/utils/js_interop_modern.dart | 57 + analytics_sdk/lib/utils/logger.dart | 218 +++ .../lib/utils/network_type_resolver.dart | 57 + analytics_sdk/lib/utils/platform_storage.dart | 14 + .../lib/utils/platform_storage_native.dart | 38 + .../lib/utils/platform_storage_web.dart | 14 + .../lib/utils/runtime_observation_native.dart | 164 ++ .../lib/utils/runtime_observation_stub.dart | 16 + analytics_sdk/lib/utils/uuid_util.dart | 15 + analytics_sdk/lib/utils/widget_bridge.dart | 34 + analytics_sdk/lib/widget/_tab_helpers.dart | 20 + .../widget/analytics_bottom_nav_wrapper.dart | 163 ++ .../lib/widget/analytics_debug_banner.dart | 158 ++ .../lib/widget/analytics_tab_bar_wrapper.dart | 165 ++ .../lib/widget/global_click_wrapper.dart | 393 ++++ analytics_sdk/pubspec.yaml | 53 + .../test/analytics_sdk_ad_dedup_test.dart | 181 ++ .../test/analytics_sdk_decrypt_test.dart | 104 ++ .../test/analytics_utils_fp_version_test.dart | 39 + analytics_sdk/test/configure_null_test.dart | 39 + .../data_plus_sdk_method_channel_test.dart | 5 + analytics_sdk/test/data_plus_sdk_test.dart | 1 + .../test/domain_manager_cache_test.dart | 94 + .../test/event_processor_logic_test.dart | 128 ++ .../test/event_processor_tombstone_test.dart | 117 ++ .../test/event_validator_regression_test.dart | 142 ++ .../event_validator_type_coercion_test.dart | 59 + analytics_sdk/test/logout_user_test.dart | 36 + analytics_sdk/test/page_name_mapper_test.dart | 68 + .../test/session_manager_regression_test.dart | 189 ++ android/.gitignore | 14 + .../org.eclipse.buildship.core.prefs | 13 + android/app/build.gradle | 140 ++ android/app/key/README.md | 3 + android/app/key/genkey.sh | 21 + android/app/key/resp_genkey.sh | 74 + android/app/proguard-rules.pro | 21 + android/app/src/debug/AndroidManifest.xml | 7 + android/app/src/main/AndroidManifest.xml | 318 ++++ .../java/io/flutter/plugins/JPFileUtils.java | 104 ++ .../io/flutter/plugins/JPNativePlugin.java | 96 + .../kotlin/com/xvproject/JPApplication.kt | 5 + .../main/kotlin/com/xvproject/MainActivity.kt | 77 + .../res/drawable-v21/launch_background.xml | 12 + .../res/drawable/ic_launcher_background.xml | 74 + .../main/res/drawable/launch_background.xml | 12 + .../app/src/main/res/drawable/launch_bg.xml | 13 + .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 50994 bytes .../main/res/mipmap-xxhdpi/ic_splash_bg.webp | Bin 0 -> 105502 bytes .../src/main/res/mipmap-xxhdpi/icon_1.webp | Bin 0 -> 4602 bytes .../src/main/res/mipmap-xxhdpi/icon_10.webp | Bin 0 -> 2870 bytes .../src/main/res/mipmap-xxhdpi/icon_11.webp | Bin 0 -> 10194 bytes .../src/main/res/mipmap-xxhdpi/icon_12.webp | Bin 0 -> 3768 bytes .../src/main/res/mipmap-xxhdpi/icon_13.webp | Bin 0 -> 3956 bytes .../src/main/res/mipmap-xxhdpi/icon_14.webp | Bin 0 -> 8472 bytes .../src/main/res/mipmap-xxhdpi/icon_15.webp | Bin 0 -> 5848 bytes .../src/main/res/mipmap-xxhdpi/icon_16.webp | Bin 0 -> 7184 bytes .../src/main/res/mipmap-xxhdpi/icon_17.webp | Bin 0 -> 7564 bytes .../src/main/res/mipmap-xxhdpi/icon_18.webp | Bin 0 -> 7306 bytes .../src/main/res/mipmap-xxhdpi/icon_2.webp | Bin 0 -> 3578 bytes .../src/main/res/mipmap-xxhdpi/icon_3.webp | Bin 0 -> 4732 bytes .../src/main/res/mipmap-xxhdpi/icon_4.webp | Bin 0 -> 4806 bytes .../src/main/res/mipmap-xxhdpi/icon_5.webp | Bin 0 -> 13216 bytes .../src/main/res/mipmap-xxhdpi/icon_6.webp | Bin 0 -> 5324 bytes .../src/main/res/mipmap-xxhdpi/icon_7.webp | Bin 0 -> 11280 bytes .../src/main/res/mipmap-xxhdpi/icon_8.webp | Bin 0 -> 9182 bytes .../src/main/res/mipmap-xxhdpi/icon_9.webp | Bin 0 -> 3952 bytes .../app/src/main/res/values-night/styles.xml | 18 + android/app/src/main/res/values/styles.xml | 19 + .../app/src/main/res/xml/totalfilepath.xml | 23 + android/app/src/profile/AndroidManifest.xml | 7 + android/build.gradle | 128 ++ android/build_release.sh | 19 + android/generatekey.sh | 28 + android/gradle.properties | 4 + .../gradle/wrapper/gradle-wrapper.properties | 5 + android/settings.gradle | 25 + assets/images/acg/acg_like_nor.png | Bin 0 -> 398 bytes assets/images/acg/acg_like_sel.png | Bin 0 -> 417 bytes assets/images/acg/acg_menu.png | Bin 0 -> 164 bytes assets/images/acg/acg_narrow.png | Bin 0 -> 310 bytes assets/images/acg/acg_noval_back.png | Bin 0 -> 651 bytes assets/images/acg/acg_share.png | Bin 0 -> 431 bytes assets/images/acg/cartoon_auto_play.gif | Bin 0 -> 15281 bytes assets/images/acg/cartoon_auto_play.png | Bin 0 -> 1686 bytes assets/images/acg/cartoon_comment.png | Bin 0 -> 485 bytes assets/images/acg/cartoon_menu.png | Bin 0 -> 350 bytes assets/images/acg/cartoon_progress.png | Bin 0 -> 432 bytes assets/images/acg/forward.png | Bin 0 -> 416 bytes assets/images/acg/list_cycle.png | Bin 0 -> 639 bytes assets/images/acg/next.png | Bin 0 -> 419 bytes assets/images/acg/noval_sign.png | Bin 0 -> 723 bytes assets/images/acg/noval_time_back.png | Bin 0 -> 677 bytes assets/images/acg/noval_time_forword.png | Bin 0 -> 673 bytes assets/images/acg/pause.png | Bin 0 -> 432 bytes assets/images/acg/play.png | Bin 0 -> 714 bytes assets/images/acg/play_list.png | Bin 0 -> 163 bytes assets/images/acg/single_cycle.png | Bin 0 -> 493 bytes assets/images/acg/text_last.png | Bin 0 -> 424 bytes assets/images/acg/text_moon.png | Bin 0 -> 811 bytes assets/images/acg/text_next.png | Bin 0 -> 434 bytes assets/images/acg/text_play_menu.png | Bin 0 -> 170 bytes assets/images/acg/text_play_sort.png | Bin 0 -> 314 bytes assets/images/acg/text_sun.png | Bin 0 -> 601 bytes assets/images/ai/ai_address.png | Bin 0 -> 602 bytes assets/images/ai/ai_changeface_right.webp | Bin 0 -> 6218 bytes assets/images/ai/ai_changeface_wrong_1.webp | Bin 0 -> 15334 bytes assets/images/ai/ai_changeface_wrong_2.webp | Bin 0 -> 7720 bytes assets/images/ai/ai_coupon_bg.webp | Bin 0 -> 11862 bytes assets/images/ai/ai_draw.png | Bin 0 -> 683 bytes assets/images/ai/ai_draw_narrow.png | Bin 0 -> 456 bytes assets/images/ai/ai_draw_style.png | Bin 0 -> 1084 bytes assets/images/ai/ai_draw_unselected.png | Bin 0 -> 417 bytes assets/images/ai/ai_girl_friend_bg.webp | Bin 0 -> 62978 bytes assets/images/ai/ai_girl_friend_btn.webp | Bin 0 -> 25536 bytes assets/images/ai/ai_girl_friend_top.webp | Bin 0 -> 22018 bytes assets/images/ai/ai_girl_friend_topup.webp | Bin 0 -> 5240 bytes assets/images/ai/ai_itv.png | Bin 0 -> 1151 bytes assets/images/ai/ai_mate_enter.png | Bin 0 -> 495 bytes assets/images/ai/ai_narrow.png | Bin 0 -> 186 bytes assets/images/ai/ai_novel_enter.png | Bin 0 -> 536 bytes assets/images/ai/ai_pic_face.png | Bin 0 -> 685 bytes assets/images/ai/ai_record_add.webp | Bin 0 -> 226 bytes assets/images/ai/ai_record_delete.png | Bin 0 -> 427 bytes assets/images/ai/ai_record_equals.png | Bin 0 -> 122 bytes assets/images/ai/ai_record_failed.webp | Bin 0 -> 414 bytes assets/images/ai/ai_record_org.png | Bin 0 -> 887 bytes assets/images/ai/ai_video_face.png | Bin 0 -> 567 bytes assets/images/ai/icon_ai_download.png | Bin 0 -> 380 bytes assets/images/ai/make_the_same.png | Bin 0 -> 349 bytes assets/images/ai/paly_btn.png | Bin 0 -> 718 bytes assets/images/app_icon/icon_1.webp | Bin 0 -> 4448 bytes assets/images/app_icon/icon_10.webp | Bin 0 -> 2462 bytes assets/images/app_icon/icon_11.webp | Bin 0 -> 10692 bytes assets/images/app_icon/icon_12.webp | Bin 0 -> 3260 bytes assets/images/app_icon/icon_13.webp | Bin 0 -> 3438 bytes assets/images/app_icon/icon_14.webp | Bin 0 -> 8700 bytes assets/images/app_icon/icon_15.webp | Bin 0 -> 5432 bytes assets/images/app_icon/icon_2.webp | Bin 0 -> 3194 bytes assets/images/app_icon/icon_3.webp | Bin 0 -> 4766 bytes assets/images/app_icon/icon_4.webp | Bin 0 -> 4338 bytes assets/images/app_icon/icon_5.webp | Bin 0 -> 13962 bytes assets/images/app_icon/icon_6.webp | Bin 0 -> 5244 bytes assets/images/app_icon/icon_7.webp | Bin 0 -> 11642 bytes assets/images/app_icon/icon_8.webp | Bin 0 -> 9196 bytes assets/images/app_icon/icon_9.webp | Bin 0 -> 3388 bytes assets/images/comment/arrow_grey.webp | Bin 0 -> 232 bytes assets/images/comment/god_comment.webp | Bin 0 -> 7828 bytes assets/images/comment/ic_send.webp | Bin 0 -> 378 bytes assets/images/comment/icon_pic.webp | Bin 0 -> 330 bytes assets/images/comment/like_red.webp | Bin 0 -> 504 bytes assets/images/comment/like_white.webp | Bin 0 -> 460 bytes assets/images/comment/msg_icon.png | Bin 0 -> 410 bytes assets/images/comment/vip_v.webp | Bin 0 -> 698 bytes assets/images/common/ad_close.png | Bin 0 -> 348 bytes assets/images/common/add_red.png | Bin 0 -> 183 bytes assets/images/common/arrow_down.png | Bin 0 -> 238 bytes assets/images/common/arrow_right_grey.webp | Bin 0 -> 226 bytes assets/images/common/back_circle.png | Bin 0 -> 833 bytes assets/images/common/back_circle_grey.png | Bin 0 -> 714 bytes assets/images/common/check.webp | Bin 0 -> 288 bytes assets/images/common/chevron_down.webp | Bin 0 -> 200 bytes assets/images/common/close_button.png | Bin 0 -> 830 bytes assets/images/common/close_grey.png | Bin 0 -> 524 bytes assets/images/common/coin.webp | Bin 0 -> 756 bytes assets/images/common/collect_empty.png | Bin 0 -> 564 bytes assets/images/common/collect_grey.png | Bin 0 -> 371 bytes assets/images/common/collect_path.png | Bin 0 -> 633 bytes assets/images/common/collect_red.png | Bin 0 -> 483 bytes assets/images/common/collect_white.png | Bin 0 -> 615 bytes assets/images/common/collect_yellow.png | Bin 0 -> 1308 bytes assets/images/common/common_back.png | Bin 0 -> 354 bytes assets/images/common/common_back_new.png | Bin 0 -> 198 bytes assets/images/common/eye_grey.webp | Bin 0 -> 492 bytes assets/images/common/eye_white.webp | Bin 0 -> 498 bytes assets/images/common/ic_nodata.webp | Bin 0 -> 3286 bytes assets/images/common/icon_broadcast.png | Bin 0 -> 1276 bytes assets/images/common/like_red.png | Bin 0 -> 1110 bytes assets/images/common/like_white.png | Bin 0 -> 568 bytes assets/images/common/lucky_lottery.webp | Bin 0 -> 105222 bytes assets/images/common/official_icon.png | Bin 0 -> 389 bytes assets/images/common/place_holder_logo.webp | Bin 0 -> 24612 bytes assets/images/common/radio_sel.png | Bin 0 -> 1435 bytes assets/images/common/radio_select.png | Bin 0 -> 1707 bytes assets/images/common/search_close.png | Bin 0 -> 451 bytes assets/images/common/search_icon.png | Bin 0 -> 647 bytes assets/images/common/share_grey.png | Bin 0 -> 358 bytes assets/images/common/share_grey_pic.png | Bin 0 -> 690 bytes assets/images/community/add_grey.png | Bin 0 -> 227 bytes .../community/community_chatgroup_sel.png | Bin 0 -> 2321 bytes assets/images/community/community_coin.webp | Bin 0 -> 1698 bytes .../images/community/community_main_sel.png | Bin 0 -> 2037 bytes .../images/community/community_noval_sel.png | Bin 0 -> 2107 bytes assets/images/community/community_pic_sel.png | Bin 0 -> 2037 bytes assets/images/community/community_sign.webp | Bin 0 -> 16602 bytes .../images/community/community_thumb_sel.png | Bin 0 -> 424 bytes assets/images/community/count_msg.png | Bin 0 -> 479 bytes assets/images/community/count_msg_gray.png | Bin 0 -> 479 bytes assets/images/community/hot_search.png | Bin 0 -> 424 bytes assets/images/community/icon_pic.png | Bin 0 -> 427 bytes assets/images/community/like_count.png | Bin 0 -> 501 bytes assets/images/community/like_empty.png | Bin 0 -> 531 bytes assets/images/community/like_empty_gray.png | Bin 0 -> 520 bytes assets/images/community/link_copy_icon.webp | Bin 0 -> 2668 bytes assets/images/community/locked_white.png | Bin 0 -> 547 bytes assets/images/community/play_icon.png | Bin 0 -> 814 bytes assets/images/community/post_detail_coin.png | Bin 0 -> 2656 bytes assets/images/community/publish_img.webp | Bin 0 -> 3118 bytes assets/images/community/publish_img_text.webp | Bin 0 -> 4318 bytes assets/images/community/publish_video.webp | Bin 0 -> 1632 bytes assets/images/community/tuji_icon.webp | Bin 0 -> 360 bytes assets/images/community/view_count.png | Bin 0 -> 481 bytes assets/images/holiday/1/home_0_0.png | Bin 0 -> 5200 bytes assets/images/holiday/1/home_0_1.png | Bin 0 -> 1427 bytes assets/images/holiday/1/home_1_0.png | Bin 0 -> 4188 bytes assets/images/holiday/1/home_1_1.png | Bin 0 -> 1265 bytes assets/images/holiday/1/home_2_0.png | Bin 0 -> 2652 bytes assets/images/holiday/1/home_2_1.png | Bin 0 -> 945 bytes assets/images/holiday/1/home_3_0.png | Bin 0 -> 4376 bytes assets/images/holiday/1/home_3_1.png | Bin 0 -> 1262 bytes assets/images/holiday/1/home_4_0.png | Bin 0 -> 4435 bytes assets/images/holiday/1/home_4_1.png | Bin 0 -> 1431 bytes assets/images/holiday/1/home_5_0.png | Bin 0 -> 5224 bytes assets/images/holiday/1/home_5_1.png | Bin 0 -> 1868 bytes assets/images/holiday/1/ic_launcher.webp | Bin 0 -> 50994 bytes assets/images/holiday/1/mine_agent_enter.webp | Bin 0 -> 4030 bytes assets/images/holiday/1/mine_ai_enter.webp | Bin 0 -> 12496 bytes assets/images/holiday/1/mine_gold_enter.webp | Bin 0 -> 7452 bytes assets/images/holiday/1/mine_vip_bg.webp | Bin 0 -> 13114 bytes assets/images/holiday/1/mine_vip_enter.webp | Bin 0 -> 13724 bytes assets/images/holiday/1/publish.png | Bin 0 -> 1342 bytes assets/images/holiday/5/home_0_0.png | Bin 0 -> 6668 bytes assets/images/holiday/5/home_0_1.png | Bin 0 -> 6668 bytes assets/images/holiday/5/home_1_0.png | Bin 0 -> 6254 bytes assets/images/holiday/5/home_1_1.png | Bin 0 -> 6254 bytes assets/images/holiday/5/home_2_0.png | Bin 0 -> 6445 bytes assets/images/holiday/5/home_2_1.png | Bin 0 -> 6445 bytes assets/images/holiday/5/home_3_0.png | Bin 0 -> 7151 bytes assets/images/holiday/5/home_3_1.png | Bin 0 -> 7151 bytes assets/images/holiday/5/home_4_0.png | Bin 0 -> 5630 bytes assets/images/holiday/5/home_4_1.png | Bin 0 -> 5630 bytes assets/images/holiday/5/home_5_0.png | Bin 0 -> 5965 bytes assets/images/holiday/5/home_5_1.png | Bin 0 -> 5965 bytes assets/images/holiday/5/ic_launcher.webp | Bin 0 -> 50994 bytes assets/images/holiday/5/icon_broadcast.png | Bin 0 -> 1148 bytes assets/images/holiday/5/main_bg.webp | Bin 0 -> 284056 bytes assets/images/holiday/5/mine_agent_enter.webp | Bin 0 -> 4030 bytes assets/images/holiday/5/mine_ai_enter.webp | Bin 0 -> 12496 bytes assets/images/holiday/5/mine_bg.webp | Bin 0 -> 50866 bytes assets/images/holiday/5/mine_gold_enter.webp | Bin 0 -> 7452 bytes assets/images/holiday/5/mine_vip_bg.webp | Bin 0 -> 13114 bytes assets/images/holiday/5/mine_vip_enter.webp | Bin 0 -> 13724 bytes assets/images/holiday/5/publish.png | Bin 0 -> 628 bytes assets/images/holiday/5/sign_icon.png | Bin 0 -> 6452 bytes assets/images/home/aw_bg.webp | Bin 0 -> 415958 bytes assets/images/home/aw_button.webp | Bin 0 -> 15840 bytes assets/images/home/aw_tag.webp | Bin 0 -> 1268 bytes assets/images/home/aw_warning.webp | Bin 0 -> 5014 bytes assets/images/home/collect_start.webp | Bin 0 -> 450 bytes assets/images/home/drama_heat.svg | 3 + assets/images/home/drama_section_more.svg | 5 + assets/images/home/drama_tip.webp | Bin 0 -> 5428 bytes assets/images/home/grid_style.webp | Bin 0 -> 258 bytes assets/images/home/history_delete.webp | Bin 0 -> 440 bytes assets/images/home/home_hot.webp | Bin 0 -> 1322 bytes assets/images/home/jump_to_top.png | Bin 0 -> 876 bytes assets/images/home/libary_search.webp | Bin 0 -> 1004 bytes assets/images/home/list_style.webp | Bin 0 -> 236 bytes assets/images/home/nav_logo.webp | Bin 0 -> 2570 bytes assets/images/home/nav_sign.webp | Bin 0 -> 2816 bytes assets/images/home/rank_crown.webp | Bin 0 -> 480 bytes assets/images/home/search_close.webp | Bin 0 -> 844 bytes assets/images/launch/app_mask.webp | Bin 0 -> 131926 bytes assets/images/launch/ic_splash_bg.webp | Bin 0 -> 105502 bytes assets/images/live_images/cp_1.webp | Bin 0 -> 4450 bytes assets/images/live_images/cp_2.webp | Bin 0 -> 4660 bytes assets/images/live_images/cp_3.webp | Bin 0 -> 4580 bytes assets/images/live_images/cp_4.webp | Bin 0 -> 3902 bytes assets/images/live_images/cp_title.webp | Bin 0 -> 9360 bytes assets/images/live_images/four_grid_bg.webp | Bin 0 -> 10782 bytes assets/images/live_images/live_charge.webp | Bin 0 -> 56212 bytes assets/images/live_images/live_main_bg.webp | Bin 0 -> 129254 bytes assets/images/live_images/live_main_bg2.webp | Bin 0 -> 74044 bytes .../images/live_images/live_section_bg_1.webp | Bin 0 -> 47518 bytes .../images/live_images/live_section_bg_2.webp | Bin 0 -> 54184 bytes .../images/live_images/live_section_bg_3.webp | Bin 0 -> 62318 bytes .../images/live_images/live_section_bg_4.webp | Bin 0 -> 55872 bytes assets/images/live_images/live_share.png | Bin 0 -> 701 bytes .../images/live_images/live_six_item_bg.webp | Bin 0 -> 5042 bytes assets/images/live_images/nine_grid_bg.webp | Bin 0 -> 11768 bytes assets/images/live_images/six_grid_bg.webp | Bin 0 -> 12108 bytes assets/images/live_images/six_round_bg.webp | Bin 0 -> 12030 bytes assets/images/mine/add_equal.png | Bin 0 -> 341 bytes assets/images/mine/area_discount_bg.webp | Bin 0 -> 42122 bytes assets/images/mine/check_in_coin.webp | Bin 0 -> 5162 bytes assets/images/mine/checked_in_coin.webp | Bin 0 -> 1858 bytes assets/images/mine/code_bg.webp | Bin 0 -> 276 bytes assets/images/mine/coin_icon.webp | Bin 0 -> 8248 bytes assets/images/mine/error.webp | Bin 0 -> 7862 bytes assets/images/mine/ic_add2.png | Bin 0 -> 466 bytes assets/images/mine/ic_alipay.png | Bin 0 -> 1077 bytes assets/images/mine/ic_coin.webp | Bin 0 -> 4018 bytes assets/images/mine/ic_sign_expired.webp | Bin 0 -> 1928 bytes assets/images/mine/ic_sign_indicator.webp | Bin 0 -> 2572 bytes assets/images/mine/ic_sign_lock.png | Bin 0 -> 971 bytes assets/images/mine/ic_union.png | Bin 0 -> 2444 bytes assets/images/mine/ic_usdt.png | Bin 0 -> 1045 bytes assets/images/mine/ic_vip_enter.png | Bin 0 -> 460 bytes assets/images/mine/ic_wechat.png | Bin 0 -> 1121 bytes assets/images/mine/icon_copy.png | Bin 0 -> 344 bytes assets/images/mine/icon_resign_bg.webp | Bin 0 -> 76138 bytes assets/images/mine/icon_resign_close.png | Bin 0 -> 1689 bytes assets/images/mine/icon_sign_click.webp | Bin 0 -> 7174 bytes assets/images/mine/invite_steps.webp | Bin 0 -> 97478 bytes assets/images/mine/mine_bg.webp | Bin 0 -> 45418 bytes assets/images/mine/mine_col_delete.png | Bin 0 -> 805 bytes assets/images/mine/mine_collect.png | Bin 0 -> 745 bytes assets/images/mine/mine_follow.png | Bin 0 -> 715 bytes assets/images/mine/mine_id.png | Bin 0 -> 1383 bytes assets/images/mine/mine_lock_password.png | Bin 0 -> 932 bytes assets/images/mine/mine_menu_1.png | Bin 0 -> 545 bytes assets/images/mine/mine_menu_10.png | Bin 0 -> 662 bytes assets/images/mine/mine_menu_12.png | Bin 0 -> 609 bytes assets/images/mine/mine_menu_2.png | Bin 0 -> 755 bytes assets/images/mine/mine_menu_3.png | Bin 0 -> 591 bytes assets/images/mine/mine_menu_4.png | Bin 0 -> 894 bytes assets/images/mine/mine_menu_5.png | Bin 0 -> 487 bytes assets/images/mine/mine_menu_6.png | Bin 0 -> 220 bytes assets/images/mine/mine_menu_7.png | Bin 0 -> 749 bytes assets/images/mine/mine_menu_8.png | Bin 0 -> 668 bytes assets/images/mine/mine_menu_9.png | Bin 0 -> 395 bytes assets/images/mine/mine_notification.png | Bin 0 -> 634 bytes assets/images/mine/mine_offical.png | Bin 0 -> 738 bytes assets/images/mine/mine_post.png | Bin 0 -> 694 bytes assets/images/mine/mine_shop_icon.webp | Bin 0 -> 24852 bytes assets/images/mine/mine_vip_bg.webp | Bin 0 -> 19436 bytes assets/images/mine/mine_withdraw_nor.png | Bin 0 -> 539 bytes assets/images/mine/no_vip_bottom.webp | Bin 0 -> 2568 bytes assets/images/mine/normal.png | Bin 0 -> 1246 bytes assets/images/mine/pay_icon103.png | Bin 0 -> 1405 bytes assets/images/mine/privilege_ai.webp | Bin 0 -> 3364 bytes assets/images/mine/privilege_bg.webp | Bin 0 -> 13198 bytes assets/images/mine/privilege_game.webp | Bin 0 -> 3830 bytes assets/images/mine/privilege_gold.webp | Bin 0 -> 2962 bytes assets/images/mine/proxy_banner.webp | Bin 0 -> 29190 bytes assets/images/mine/proxy_header_bg.webp | Bin 0 -> 153410 bytes assets/images/mine/red_checked.png | Bin 0 -> 975 bytes assets/images/mine/scan_photo.png | Bin 0 -> 1291 bytes assets/images/mine/select.png | Bin 0 -> 2333 bytes assets/images/mine/setting.png | Bin 0 -> 572 bytes assets/images/mine/share_bg.webp | Bin 0 -> 6598 bytes assets/images/mine/sign_take_top.webp | Bin 0 -> 8508 bytes assets/images/mine/user_center_bg.webp | Bin 0 -> 6008 bytes assets/images/mine/vip_1.webp | Bin 0 -> 1582 bytes assets/images/mine/vip_2.webp | Bin 0 -> 1592 bytes assets/images/mine/vip_3.webp | Bin 0 -> 1590 bytes assets/images/mine/vip_4.webp | Bin 0 -> 1598 bytes assets/images/mine/vip_arrow.svg | 5 + assets/images/mine/vip_bottom.webp | Bin 0 -> 4320 bytes assets/images/mine/vip_entry_card_badge.png | Bin 0 -> 17412 bytes assets/images/mine/vip_entry_card_bg.png | Bin 0 -> 25950 bytes assets/images/mine/vip_more_title.webp | Bin 0 -> 5288 bytes assets/images/mine/vip_privilege_bg.webp | Bin 0 -> 24806 bytes assets/images/mine/vip_special_title.webp | Bin 0 -> 7210 bytes assets/images/mine/vip_upgrade_arrow.svg | 3 + assets/images/mine/welfare_normal_bg.webp | Bin 0 -> 1732 bytes .../images/mine/welfare_vip_background.webp | Bin 0 -> 38980 bytes assets/images/mine/welfare_vip_bg.webp | Bin 0 -> 2830 bytes assets/images/video/add_red.webp | Bin 0 -> 884 bytes assets/images/video/cache_selected.webp | Bin 0 -> 880 bytes assets/images/video/cache_start.webp | Bin 0 -> 272 bytes assets/images/video/cache_stop.webp | Bin 0 -> 140 bytes assets/images/video/cache_unselected.webp | Bin 0 -> 1036 bytes assets/images/video/chevron_right.webp | Bin 0 -> 166 bytes assets/images/video/chevron_up.webp | Bin 0 -> 190 bytes assets/images/video/circle_play.webp | Bin 0 -> 1678 bytes assets/images/video/comment.png | Bin 0 -> 589 bytes assets/images/video/core_privilege_bg.webp | Bin 0 -> 2012 bytes assets/images/video/download.png | Bin 0 -> 762 bytes assets/images/video/download_free.png | Bin 0 -> 1240 bytes assets/images/video/download_short.png | Bin 0 -> 731 bytes assets/images/video/eye_close.png | Bin 0 -> 622 bytes assets/images/video/eye_open.png | Bin 0 -> 635 bytes assets/images/video/full_drama.webp | Bin 0 -> 290 bytes assets/images/video/full_icon.png | Bin 0 -> 526 bytes assets/images/video/full_screen.webp | Bin 0 -> 342 bytes assets/images/video/line_switch.webp | Bin 0 -> 248 bytes assets/images/video/line_white2.png | Bin 0 -> 675 bytes assets/images/video/pause_icon.webp | Bin 0 -> 304 bytes assets/images/video/play.webp | Bin 0 -> 422 bytes assets/images/video/share_arrow.png | Bin 0 -> 567 bytes assets/images/video/stopwatch.svg | 5 + assets/images/video/update_video.webp | Bin 0 -> 5764 bytes assets/images/video/video_like_border.png | Bin 0 -> 481 bytes assets/images/video/video_like_grey.webp | Bin 0 -> 304 bytes assets/images/video/voice_noval_icon.png | Bin 0 -> 619 bytes build_apk.sh | 238 +++ devtools_options.yaml | 1 + ios/ExportOptions.plist | 25 + ios/ExportOptions.plist.example | 16 + ios/Flutter/AppFrameworkInfo.plist | 26 + ios/Flutter/Debug.xcconfig | 2 + ios/Flutter/Flutter.podspec | 18 + ios/Flutter/Release.xcconfig | 2 + ios/Flutter/ephemeral/flutter_lldb_helper.py | 32 + ios/Flutter/ephemeral/flutter_lldbinit | 5 + ios/Flutter/flutter_export_environment.sh | 13 + ios/Podfile | 89 + ios/Podfile.lock | 257 +++ ios/Runner.xcodeproj/project.pbxproj | 767 ++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 101 ++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + ios/Runner/AppDelegate.swift | 32 + .../AppIcon.appiconset/AppIcon20x20@2x 1.png | Bin 0 -> 4341 bytes .../AppIcon.appiconset/AppIcon20x20@2x 2.png | Bin 0 -> 4341 bytes .../AppIcon.appiconset/AppIcon20x20@2x.png | Bin 0 -> 4341 bytes .../AppIcon.appiconset/Contents.json | 122 ++ .../AppIcon.appiconset/icon-20-ipad.png | Bin 0 -> 1812 bytes .../AppIcon.appiconset/icon-20@3x.png | Bin 0 -> 8064 bytes .../AppIcon.appiconset/icon-29 1.png | Bin 0 -> 2793 bytes .../AppIcon.appiconset/icon-29.png | Bin 0 -> 2793 bytes .../AppIcon.appiconset/icon-29@2x 1.png | Bin 0 -> 7721 bytes .../AppIcon.appiconset/icon-29@2x.png | Bin 0 -> 7721 bytes .../AppIcon.appiconset/icon-29@3x.png | Bin 0 -> 14857 bytes .../AppIcon.appiconset/icon-38@2x.png | Bin 0 -> 11920 bytes .../AppIcon.appiconset/icon-40@2x 1.png | Bin 0 -> 13017 bytes .../AppIcon.appiconset/icon-40@2x.png | Bin 0 -> 13017 bytes .../AppIcon.appiconset/icon-40@3x 1.png | Bin 0 -> 25516 bytes .../AppIcon.appiconset/icon-40@3x.png | Bin 0 -> 25516 bytes .../AppIcon.appiconset/icon-60@3x.png | Bin 0 -> 50865 bytes .../AppIcon.appiconset/icon-76@2x.png | Bin 0 -> 38135 bytes .../AppIcon.appiconset/icon-83.5@2x.png | Bin 0 -> 44812 bytes .../AppIcon.appiconset/logo.png | Bin 0 -> 273557 bytes ios/Runner/Assets.xcassets/Contents.json | 6 + .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/README.md | 5 + .../LaunchImage.imageset/ic_splash_bg 1.png | Bin 0 -> 458550 bytes .../LaunchImage.imageset/ic_splash_bg 2.png | Bin 0 -> 2742996 bytes .../LaunchImage.imageset/ic_splash_bg.png | Bin 0 -> 1627471 bytes ios/Runner/Base.lproj/LaunchScreen.storyboard | 43 + ios/Runner/Base.lproj/Main.storyboard | 26 + ios/Runner/Info.plist | 66 + ios/Runner/Runner-Bridging-Header.h | 1 + ios/RunnerTests/RunnerTests.swift | 12 + lib/alert/aw_permission_alert.dart | 98 + lib/alert/mine/vip_level_dialog.dart | 82 + lib/alert/splash/app_center_dialog.dart | 109 ++ lib/alert/splash/line_error_dialog.dart | 141 ++ lib/alert/splash/notice_image_dialog.dart | 92 + lib/alert/splash/notice_text_dialog.dart | 78 + lib/alert/splash/update_dialog.dart | 257 +++ lib/alert/video/buy_vip_alert.dart | 478 +++++ lib/alert/video/buy_vip_alert_logic.dart | 15 + lib/alert/video/comic_buy_alert.dart | 166 ++ lib/alert/video/share_media_dialog.dart | 185 ++ lib/alert/video/video_buy_bottom_sheet.dart | 248 +++ lib/alert/video/video_line_menu_alert.dart | 88 + lib/alert/video/vip_acg_dialog.dart | 49 + lib/alert/vip_guide/guide_bottom_sheet.dart | 131 ++ lib/alert/vip_guide/guide_common.dart | 51 + lib/alert/vip_guide/guide_config.dart | 63 + .../vip_guide/guide_countdown_dialog.dart | 229 +++ .../vip_guide/guide_free_trial_sheet.dart | 63 + lib/alert/vip_guide/guide_home_dialog.dart | 156 ++ lib/alert/vip_guide/guide_manager.dart | 52 + lib/alert/vip_guide/guide_push_banner.dart | 436 +++++ lib/alert/vip_guide/guide_push_model.dart | 73 + lib/alert/vip_guide/timed_popup_manager.dart | 65 + lib/assets_tool/app_colors.dart | 19 + lib/assets_tool/images.dart | 27 + lib/config/address.dart | 54 + lib/config/config.dart | 83 + lib/extension/datetime_extension.dart | 13 + lib/extension/double_extension.dart | 5 + lib/extension/extensions.dart | 8 + lib/extension/int_extension.dart | 19 + lib/extension/list_extension.dart | 11 + lib/extension/map_extension.dart | 5 + lib/extension/num_extension.dart | 17 + lib/extension/string_extension.dart | 81 + lib/hj_model/acg/cartoon_more_list.dart | 301 ++++ lib/hj_model/acg/comic_chapters_model.dart | 71 + lib/hj_model/actress_info.dart | 131 ++ lib/hj_model/actress_model.dart | 22 + .../banner/comment_top_banner_model.dart | 38 + lib/hj_model/cartoon_media_info.dart | 414 +++++ lib/hj_model/comment/comment_list_res.dart | 38 + lib/hj_model/comment/comment_model.dart | 65 + lib/hj_model/comment/reply_model.dart | 36 + lib/hj_model/comment/user_comment_res.dart | 23 + lib/hj_model/drama/drama_models.dart | 177 ++ lib/hj_model/drama_media_info.dart | 228 +++ lib/hj_model/home/collection_model.dart | 48 + lib/hj_model/home/module_detail_model.dart | 166 ++ lib/hj_model/home/plate_model.dart | 133 ++ lib/hj_model/home/recommend_list_model.dart | 19 + lib/hj_model/home/update_marker_model.dart | 39 + lib/hj_model/home/video_library_model.dart | 90 + lib/hj_model/home/video_list_model.dart | 44 + lib/hj_model/list_base_model.dart | 63 + lib/hj_model/media_content.dart | 161 ++ .../message/message_dynamic_list.dart | 75 + lib/hj_model/mine/credit_record_model.dart | 13 + .../mine/exchange/bill_item_model.dart | 62 + lib/hj_model/mine/exchange/dc_model.dart | 172 ++ .../mine/exchange/recharge_list_model.dart | 16 + .../exchange/recharge_type_list_model.dart | 60 + .../mine/exchange/recharge_url_model.dart | 13 + lib/hj_model/mine/exchange_record_model.dart | 25 + lib/hj_model/mine/follow_user_list_model.dart | 31 + lib/hj_model/mine/happy/happy_model.dart | 41 + .../mine/official_list_item_model.dart | 17 + lib/hj_model/mine/promotion_record.dart | 13 + lib/hj_model/mine/task_center_data.dart | 66 + .../mine/vip_card_analytics_event.dart | 45 + lib/hj_model/splash/ads_model.dart | 129 ++ lib/hj_model/splash/cut_info.dart | 24 + lib/hj_model/splash/domain_source_model.dart | 543 ++++++ lib/hj_model/splash/watch_count_model.dart | 21 + lib/hj_model/user/user_income_info_model.dart | 47 + lib/hj_model/user/user_info_model.dart | 154 ++ lib/hj_model/user/wallet_model.dart | 35 + lib/hj_model/video_model.dart | 659 +++++++ lib/hj_page/ai/ai_change_face_logic.dart | 38 + lib/hj_page/ai/ai_change_face_page.dart | 97 + lib/hj_page/ai/ai_draw/ai_paint_logic.dart | 89 + lib/hj_page/ai/ai_draw/ai_paint_page.dart | 278 +++ lib/hj_page/ai/ai_face_sub_logic.dart | 69 + lib/hj_page/ai/ai_face_sub_page.dart | 119 ++ .../ai/ai_girl/ai_girl_friend_logic.dart | 76 + .../ai/ai_girl/ai_girl_friend_page.dart | 151 ++ lib/hj_page/ai/ai_girl/ai_h5_page.dart | 59 + lib/hj_page/ai/ai_home_logic.dart | 96 + lib/hj_page/ai/ai_home_page.dart | 138 ++ .../ai/ai_image_to_video/ai_itv_logic.dart | 77 + .../ai/ai_image_to_video/ai_itv_page.dart | 149 ++ .../ai_image_to_video/ai_main_itv_page.dart | 65 + lib/hj_page/ai/ai_mod_detail_page.dart | 300 ++++ .../ai/ai_novel/ai_novel_detail_page.dart | 22 + lib/hj_page/ai/ai_novel/ai_novel_logic.dart | 56 + lib/hj_page/ai/ai_novel/ai_novel_page.dart | 204 +++ .../ai/ai_record/ai_main_record_logic.dart | 37 + .../ai/ai_record/ai_main_record_page.dart | 78 + lib/hj_page/ai/ai_record/ai_record_logic.dart | 112 ++ lib/hj_page/ai/ai_record/ai_record_page.dart | 114 ++ lib/hj_page/ai/ai_square/ai_square_logic.dart | 50 + lib/hj_page/ai/ai_square/ai_square_page.dart | 46 + .../ai/ai_sub_type/ai_function_logic.dart | 195 ++ .../ai/ai_sub_type/ai_strip_sub_page.dart | 197 ++ .../ai/models/ai_change_face_video_model.dart | 35 + lib/hj_page/ai/models/ai_girl_resp_model.dart | 48 + lib/hj_page/ai/models/ai_mod_list_model.dart | 96 + lib/hj_page/ai/models/ai_record_model.dart | 76 + lib/hj_page/ai/models/ai_square_model.dart | 64 + lib/hj_page/ai/widgets/ai_coupon_sheet.dart | 127 ++ .../ai/widgets/ai_draw_text_field.dart | 78 + lib/hj_page/ai/widgets/ai_girl_sheet.dart | 192 ++ lib/hj_page/ai/widgets/ai_image_record.dart | 186 ++ lib/hj_page/ai/widgets/ai_mod_cell.dart | 47 + lib/hj_page/ai/widgets/ai_novel_record.dart | 51 + lib/hj_page/ai/widgets/ai_paint_record.dart | 80 + lib/hj_page/ai/widgets/ai_record_parts.dart | 312 ++++ lib/hj_page/ai/widgets/ai_square_cell.dart | 231 +++ lib/hj_page/ai/widgets/ai_video_record.dart | 66 + lib/hj_page/ai/widgets/pic_picker.dart | 129 ++ lib/hj_page/cartoon/acg_widget_item.dart | 122 ++ .../cartoon/audio_player_bottomsheet.dart | 156 ++ lib/hj_page/cartoon/audio_player_content.dart | 347 ++++ lib/hj_page/cartoon/cartoon_detail_logic.dart | 106 ++ lib/hj_page/cartoon/cartoon_detail_page.dart | 560 ++++++ lib/hj_page/cartoon/cartoon_read_logic.dart | 239 +++ lib/hj_page/cartoon/cartoon_read_page.dart | 472 +++++ .../cartoon/cartoon_recommend_logic.dart | 39 + .../cartoon/cartoon_recommend_page.dart | 98 + .../cartoon/cartoon_sectionlist_page.dart | 92 + .../cartoon_sectionlist_sub_logic.dart | 39 + .../cartoon/cartoon_sectionlist_sub_page.dart | 48 + .../cartoon/cartoon_sub_detail_page.dart | 26 + .../cartoon/cartoon_sub_grid_logic.dart | 135 ++ .../cartoon/cartoon_sub_grid_page.dart | 151 ++ .../cartoon/cartoon_sub_module_logic.dart | 49 + .../cartoon/cartoon_sub_module_page.dart | 66 + lib/hj_page/cartoon/novel_detail_logic.dart | 127 ++ lib/hj_page/cartoon/novel_detail_page.dart | 280 +++ lib/hj_page/cartoon/novel_header.dart | 139 ++ lib/hj_page/cartoon/photo_gallery_item.dart | 107 ++ .../cartoon/text_novel_read_logic.dart | 185 ++ lib/hj_page/cartoon/text_novel_read_page.dart | 295 +++ .../cartoon/voice_novel_read_logic.dart | 49 + .../cartoon/voice_novel_read_page.dart | 107 ++ .../cartoon/widget/acg_expand_text.dart | 107 ++ .../cartoon/widget/acg_source_manager.dart | 451 +++++ .../cartoon/widget/cartoon_subset_alert.dart | 145 ++ lib/hj_page/cartoon/widget/free_badge.dart | 44 + lib/hj_page/comment/cell/comment_item.dart | 304 ++++ .../comment/cell/reply_comment_item.dart | 170 ++ lib/hj_page/comment/comment_alert.dart | 49 + lib/hj_page/comment/comment_input_view.dart | 173 ++ lib/hj_page/comment/comment_mixin.dart | 209 +++ lib/hj_page/comment/comment_views.dart | 129 ++ .../comment/widget/comment_foot_view.dart | 183 ++ .../comment/widget/comment_rich_text.dart | 41 + .../comment/widget/comment_top_banner.dart | 126 ++ .../community/community_module_logic.dart | 30 + .../community/community_module_page.dart | 79 + .../community/community_tab_logic.dart | 131 ++ .../community/community_tab_page.dart | 217 +++ .../community_main_logic.dart | 54 + .../community_main_page.dart | 139 ++ .../community_section_tab.dart | 85 + .../community_tag_logic.dart | 40 + .../community_tag_page.dart | 154 ++ .../subviews/tag_detail_sub_controller.dart | 126 ++ .../subviews/tag_detail_subview.dart | 69 + .../alert/group_buy_bottom_sheet.dart | 165 ++ .../detail/soul_group_detail_logic.dart | 128 ++ .../detail/soul_group_detail_page.dart | 58 + .../detail/widget/chat_item_cell.dart | 272 +++ .../group_chat/group_chat_logic.dart | 44 + .../community/group_chat/group_chat_page.dart | 61 + .../group_chat/model/im_group_list_model.dart | 87 + .../group_chat/model/im_message_resp.dart | 46 + .../group_chat/widget/chat_group_cell.dart | 139 ++ .../post_detail_cover_content.dart | 226 +++ .../post_detail_operation_view.dart | 113 ++ .../post_detail_unlock_dialog.dart | 55 + .../post_detail_word_content.dart | 102 ++ .../post_resource_info_widget.dart | 103 ++ .../post_detail_page/post_detail_logic.dart | 72 + .../post_detail_page/post_detail_page.dart | 186 ++ .../publish_page/chose_tag_logic.dart | 55 + .../publish_page/chose_tag_page.dart | 72 + .../community/publish_page/publish_logic.dart | 381 ++++ .../community/publish_page/publish_page.dart | 147 ++ .../publish_page/publish_rule_page.dart | 93 + .../publish_page/widget/publish_widget.dart | 378 ++++ .../widget/video_cover_widget.dart | 49 + .../widget/community_middle_header.dart | 57 + .../widget/community_post_widget.dart | 306 ++++ .../widget/community_sort_header.dart | 79 + .../widget/follow_loading_button.dart | 47 + .../community/widget/pic_simple_cell.dart | 101 ++ .../community/widget/post_user_info_view.dart | 135 ++ .../community/widget/post_word_rich_text.dart | 40 + lib/hj_page/drama/drama_detail_logic.dart | 290 +++ lib/hj_page/drama/drama_detail_page.dart | 185 ++ lib/hj_page/drama/drama_feed_logic.dart | 101 ++ lib/hj_page/drama/drama_feed_page.dart | 81 + lib/hj_page/drama/drama_list_logic.dart | 93 + lib/hj_page/drama/drama_list_page.dart | 174 ++ lib/hj_page/drama/drama_tag_detail_logic.dart | 55 + lib/hj_page/drama/drama_tag_detail_page.dart | 114 ++ lib/hj_page/drama/view/drama_bottom_bar.dart | 78 + .../drama/view/drama_episode_sheet.dart | 272 +++ lib/hj_page/drama/view/drama_menu.dart | 205 +++ .../drama/view/drama_paywall_view.dart | 257 +++ lib/hj_page/drama/view/drama_speed_sheet.dart | 103 ++ .../drama/view/drama_video_player.dart | 262 +++ .../drama/view/drama_video_player_logic.dart | 369 ++++ lib/hj_page/find/acg_game_detail_logic.dart | 116 ++ lib/hj_page/find/acg_game_detail_page.dart | 373 ++++ .../find/acg_game_recommend_logic.dart | 32 + lib/hj_page/find/acg_game_recommend_page.dart | 60 + .../find/pic_collect/album_action_widget.dart | 169 ++ .../find/pic_collect/pics_detail_logic.dart | 70 + .../find/pic_collect/pics_detail_page.dart | 171 ++ .../find/rank_module/acg_rank_item.dart | 74 + .../find/rank_module/image_rank_item.dart | 80 + lib/hj_page/find/rank_module/rank_badge.dart | 24 + .../find/rank_module/rank_main_logic.dart | 20 + .../find/rank_module/rank_main_page.dart | 68 + .../find/rank_module/rank_sort_tab_bar.dart | 46 + .../find/rank_module/rank_sub_logic.dart | 40 + .../find/rank_module/rank_sub_page.dart | 72 + .../find/rank_module/rank_type_tab_view.dart | 56 + .../find/rank_module/video_rank_item.dart | 88 + .../activity_float/home_activity_float.dart | 115 ++ .../activity_float/home_float_banner.dart | 235 +++ .../activity_float/home_float_widget.dart | 26 + .../swiper_floating_widget.dart | 142 ++ lib/hj_page/home/dark_home_page.dart | 73 + .../home/home_cell_style/divider_tab_bar.dart | 63 + .../home/home_cell_style/drama_banner.dart | 87 + .../home_cell_style/drama_cover_card.dart | 250 +++ .../home_cell_style/guess_like_sliver.dart | 91 + .../home_cell_style/home_section_cell.dart | 472 +++++ .../home_cell_style/home_section_title.dart | 41 + .../home/home_cell_style/level_mark_chip.dart | 44 + .../home/home_cell_style/quick_entry_row.dart | 71 + .../home_cell_style/tiktok_simple_cell.dart | 160 ++ .../home/home_cell_style/video_hor_cell.dart | 84 + .../home_cell_style/video_simple_cell.dart | 478 +++++ lib/hj_page/home/home_drawer.dart | 193 ++ lib/hj_page/home/home_drawer_logic.dart | 40 + lib/hj_page/home/home_main_logic.dart | 199 +++ lib/hj_page/home/home_main_page.dart | 257 +++ .../home_sub_module/home_tab_base_logic.dart | 56 + .../home_sub_module/home_tab_drama_logic.dart | 122 ++ .../home_sub_module/home_tab_drama_page.dart | 123 ++ .../home_tab_section_logic.dart | 129 ++ .../home_tab_section_page.dart | 122 ++ .../home_sub_module/home_tab_sort_logic.dart | 174 ++ .../home_sub_module/home_tab_sort_page.dart | 316 ++++ .../home_sub_module/widget/sort_header.dart | 124 ++ .../widget/special_topics_view.dart | 247 +++ .../provider/home_update_marker_provider.dart | 133 ++ .../home/search_page/search_main_logic.dart | 57 + .../home/search_page/search_main_page.dart | 110 ++ .../search_acg_result_page.dart | 112 ++ .../search_result_main_page.dart | 110 ++ .../search_video_result_page.dart | 458 +++++ .../search_page/video_all_type_logic.dart | 147 ++ .../home/search_page/video_all_type_page.dart | 369 ++++ .../widget/common_search_widget.dart | 134 ++ .../search_page/widget/search_app_bar.dart | 180 ++ .../widget/search_history_view.dart | 134 ++ .../widget/search_hot_tag_view.dart | 69 + .../home/search_page/widget/topic_item.dart | 89 + .../section_all_page/section_all_logic.dart | 31 + .../section_all_page/section_all_page.dart | 83 + .../special_topics_detail_logic.dart | 113 ++ .../special_topics_detail_page.dart | 102 ++ lib/hj_page/home/tag/cartoon_tag_logic.dart | 69 + lib/hj_page/home/tag/cartoon_tag_page.dart | 80 + lib/hj_page/home/tag/video_tag_logic.dart | 56 + lib/hj_page/home/tag/video_tag_page.dart | 101 ++ .../home/widget/collect_status_wrapper.dart | 97 + .../home/widget/home_sort_menu_view.dart | 85 + lib/hj_page/home/widget/home_txt_marquee.dart | 537 ++++++ .../home/widget/publist_entry_alert.dart | 92 + .../home/widget/quick_search_view.dart | 79 + lib/hj_page/home/widget/support_up_alert.dart | 252 +++ lib/hj_page/home/widget/user_avatar.dart | 55 + lib/hj_page/home/widget/user_name_view.dart | 36 + lib/hj_page/live/live_detail_logic.dart | 200 +++ lib/hj_page/live/live_detail_page.dart | 312 ++++ lib/hj_page/live/live_item_widget.dart | 307 ++++ lib/hj_page/live/live_main_logic.dart | 43 + lib/hj_page/live/live_main_page.dart | 71 + lib/hj_page/live/live_model.dart | 103 ++ lib/hj_page/live/live_service.dart | 75 + lib/hj_page/live/live_sub_logic.dart | 76 + lib/hj_page/live/live_sub_page.dart | 307 ++++ lib/hj_page/live/live_widget.dart | 176 ++ lib/hj_page/main_page/drama_tip_bubble.dart | 92 + lib/hj_page/main_page/main_logic.dart | 281 +++ lib/hj_page/main_page/main_page.dart | 139 ++ lib/hj_page/main_page/main_tabbar.dart | 165 ++ .../provider/bottom_bar_provider.dart | 102 ++ .../main_page/provider/msg_provider.dart | 118 ++ .../collec_history_buy/col_his_buy_logic.dart | 43 + .../collec_history_buy/col_his_buy_page.dart | 106 ++ .../col_his_buy_sub_logic.dart | 234 +++ .../col_his_buy_sub_page.dart | 267 +++ lib/hj_page/mine/home_mine_logic.dart | 192 ++ lib/hj_page/mine/home_mine_page.dart | 373 ++++ .../mine/identity/mine_identity_logic.dart | 30 + .../mine/identity/mine_identity_page.dart | 210 +++ .../mine/make_money/alipay_ccdcapi_model.dart | 29 + .../mine/make_money/in_come_entity.dart | 123 ++ .../mine_withdrawal_record_logic.dart | 149 ++ .../mine_withdrawal_record_page.dart | 60 + .../make_money/widget/record_list_item.dart | 336 ++++ .../make_money/withdraw_details_model.dart | 369 ++++ .../mine/make_money/withdrawal_logic.dart | 230 +++ .../mine/make_money/withdrawal_page.dart | 447 +++++ .../mine/message/message_center_logic.dart | 17 + .../mine/message/message_center_page.dart | 85 + .../message_center_sub_controller.dart | 91 + .../mine/message/message_center_sub_page.dart | 73 + .../mine/message/un_read_msg_num_model.dart | 35 + .../mine/message/widget/message_items.dart | 184 ++ .../mine/mine_drive/mine_drive_logic.dart | 25 + .../mine/mine_drive/mine_drive_page.dart | 124 ++ .../mine_following/mine_follow_sub_logic.dart | 163 ++ .../mine_following/mine_follow_sub_page.dart | 74 + .../mine_following/mine_following_view.dart | 15 + .../widget/follow_list_items.dart | 277 +++ .../mine_gesture_pwd/model/point_item.dart | 26 + .../widget/gesture_password_widget.dart | 614 +++++++ .../mine_gesture_pwd/widget/line_painter.dart | 38 + .../mine_post/mine_publish_main_logic.dart | 66 + .../mine_post/mine_publish_main_page.dart | 291 +++ .../mine_post/mine_publish_sub_logic.dart | 22 + .../mine/mine_post/mine_publish_sub_page.dart | 61 + .../mine/mine_profit/bank_card_home_page.dart | 293 +++ .../model/alipay_bank_list_model.dart | 53 + .../mine/mine_profit/model/bankcard_info.dart | 201 +++ .../mine_profit/widget/add_bank_dialog.dart | 166 ++ .../mine/mine_setting/bind_phone_logic.dart | 72 + .../mine/mine_setting/bing_phone_page.dart | 152 ++ .../mine/mine_setting/invite_bind_logic.dart | 33 + .../mine/mine_setting/invite_bind_page.dart | 106 ++ .../mine/mine_setting/mine_code_logic.dart | 86 + .../mine/mine_setting/mine_code_page.dart | 245 +++ .../mine_setting/mine_find_account_page.dart | 88 + .../mine_setting/mine_password_logic.dart | 43 + .../mine/mine_setting/mine_password_page.dart | 62 + .../mine_setting/mine_scan_login_logic.dart | 83 + .../mine_setting/mine_scan_login_page.dart | 91 + .../mine/mine_setting/mine_setting_page.dart | 252 +++ .../mine_setting_profile_logic.dart | 88 + .../mine_setting_profile_page.dart | 38 + .../mine_setting/setting_avatar_logic.dart | 49 + .../mine_setting/setting_avatar_page.dart | 98 + .../mine_setting/setting_profile_widget.dart | 127 ++ .../mine/mine_share/mine_share_page.dart | 224 +++ .../mine_share/mine_share_record_logic.dart | 30 + .../mine_share/mine_share_record_page.dart | 88 + .../mine_video_cache/video_cache_cell.dart | 377 ++++ .../mine_video_cache/video_cache_logic.dart | 232 +++ .../mine_video_cache/video_cache_page.dart | 191 ++ .../mine/mine_vip/charge_user_header.dart | 192 ++ lib/hj_page/mine/mine_vip/coupon_model.dart | 56 + .../mine/mine_vip/mine_charge_coin_logic.dart | 65 + .../mine/mine_vip/mine_charge_coin_page.dart | 216 +++ .../mine/mine_vip/mine_charge_vip_logic.dart | 212 +++ .../mine/mine_vip/mine_charge_vip_page.dart | 451 +++++ .../mine/mine_vip/online_pay_page.dart | 544 ++++++ .../mine/mine_vip/pay_order_source.dart | 86 + .../mine/mine_vip/pay_success_alert.dart | 90 + .../mine/mine_vip/vip_card_analytics.dart | 144 ++ lib/hj_page/mine/mine_vip/vip_card_item.dart | 848 +++++++++ lib/hj_page/mine/mine_vip/vip_pay_button.dart | 94 + .../mine/mine_vip/vip_product_manager.dart | 239 +++ .../mine/mine_vip/vip_support_model.dart | 765 ++++++++ lib/hj_page/mine/mine_vip/vip_ui_kit.dart | 127 ++ .../mine/mine_vip/widgets/coin_item.dart | 76 + .../widgets/coin_pay_bottom_sheet.dart | 277 +++ .../feedback_question_category_view.dart | 107 ++ .../more_question/mine_feedback_logic.dart | 95 + .../more_question/mine_feedback_page.dart | 151 ++ .../mine/more_question/mine_qa_page.dart | 134 ++ .../mine/more_question/photo_manage_view.dart | 86 + .../mine/welfare/lou_feng_ad_logic.dart | 75 + .../mine/welfare/lou_feng_ad_page.dart | 99 + .../mine/welfare/mine_income_page.dart | 174 ++ .../mine/welfare/more_free_video_logic.dart | 42 + .../mine/welfare/more_free_video_page.dart | 104 ++ .../mine/welfare/share_data_list_page.dart | 205 +++ .../mine/welfare/share_details_page.dart | 155 ++ .../mine/welfare/sign_daily_logic.dart | 84 + lib/hj_page/mine/welfare/sign_daily_page.dart | 264 +++ .../mine/welfare/welfare_home_page.dart | 84 + .../mine/welfare/widget/checkin_model.dart | 277 +++ .../widget/congratulations_reward_dialog.dart | 139 ++ .../welfare/widget/custom_progressbar.dart | 66 + .../mine/welfare/widget/mine_sign_logic.dart | 317 ++++ .../welfare/widget/recommend_app_page.dart | 145 ++ .../mine/welfare/widget/sign_dialog.dart | 252 +++ .../mine/welfare/widget/sign_in_model.dart | 271 +++ .../mine/welfare/widget/task_widgets.dart | 1040 +++++++++++ .../mine/widgets/exchange_vip_page.dart | 114 ++ lib/hj_page/mine/widgets/gradient_text.dart | 25 + .../mine/widgets/integral_exchange_logic.dart | 23 + .../mine/widgets/integral_exchange_page.dart | 194 ++ .../mine/widgets/integral_record_logic.dart | 42 + .../mine/widgets/mine_info_widget.dart | 299 ++++ .../mine/widgets/mine_share_qr_view.dart | 205 +++ .../mine/widgets/stage_cell_widget.dart | 23 + lib/hj_page/mine/widgets/vip_entry_card.dart | 266 +++ lib/hj_page/pre_sale/limit_time_provider.dart | 96 + lib/hj_page/pre_sale/pre_sale_alert.dart | 52 + lib/hj_page/pre_sale/pre_sale_entry.dart | 119 ++ lib/hj_page/pre_sale/pre_sale_logic.dart | 65 + lib/hj_page/pre_sale/pre_sale_model.dart | 76 + lib/hj_page/pre_sale/pre_sale_page.dart | 242 +++ lib/hj_page/pre_sale/pre_sale_provider.dart | 239 +++ lib/hj_page/pre_sale/pre_sale_service.dart | 45 + .../short_video/short_main_video_logic.dart | 49 + .../short_video/short_main_video_page.dart | 63 + .../short_video/short_video_logic.dart | 188 ++ lib/hj_page/short_video/short_video_page.dart | 145 ++ .../short_video/view/feed_ad_view.dart | 30 + .../short_video/view/float_ad_view.dart | 72 + .../short_video/view/media_action_menu.dart | 344 ++++ .../short_video/view/short_top_menu.dart | 69 + .../short_video/view/short_video_menu.dart | 381 ++++ .../short_video/view/short_video_player.dart | 175 ++ .../view/short_video_player_logic.dart | 177 ++ .../short_video/view/video_menu_kit.dart | 231 +++ .../view/video_player_base_logic.dart | 265 +++ .../view/video_progress_widget.dart | 342 ++++ lib/hj_page/splash/splash_ad_view.dart | 260 +++ lib/hj_page/splash/splash_logic.dart | 364 ++++ lib/hj_page/splash/splash_page.dart | 55 + .../user_center_page/user_center_logic.dart | 107 ++ .../user_center_page/user_center_page.dart | 161 ++ .../view/user_work_list_logic.dart | 31 + .../view/user_work_list_view.dart | 75 + .../widget/user_center_header.dart | 131 ++ .../cartoon/cartoon_episode_menu_alert.dart | 192 ++ .../cartoon/video_detail_cartoon_view.dart | 379 ++++ .../video/simple_video_player_logic.dart | 64 + .../video/simple_video_player_page.dart | 45 + lib/hj_page/video/video_full_logic.dart | 51 + lib/hj_page/video/video_full_page.dart | 110 ++ lib/hj_page/video/video_logic.dart | 712 ++++++++ lib/hj_page/video/video_page.dart | 289 +++ lib/hj_page/video/video_player_alert.dart | 121 ++ lib/hj_page/video/view/long_video_status.dart | 117 ++ lib/hj_page/video/view/video_ad_widget.dart | 142 ++ .../video/view/video_detail_bottom_menu.dart | 159 ++ lib/hj_page/video/view/video_detail_view.dart | 252 +++ .../video/view/video_mask_buy_view.dart | 184 ++ lib/hj_page/video/view/video_menu_view.dart | 632 +++++++ lib/hj_page/video/view/video_status_view.dart | 87 + .../video/view/video_tabbar_menu_widget.dart | 142 ++ lib/hj_page/video/view/vip_promo_banner.dart | 123 ++ lib/hj_page/web_page/h5_logic.dart | 52 + lib/hj_page/web_page/h5_page.dart | 75 + lib/hj_page/web_page/h5_webview_settings.dart | 16 + lib/hj_utils/api_service/acg_service.dart | 251 +++ lib/hj_utils/api_service/ai_service.dart | 363 ++++ lib/hj_utils/api_service/buy_service.dart | 90 + lib/hj_utils/api_service/comment_service.dart | 142 ++ lib/hj_utils/api_service/common_service.dart | 246 +++ lib/hj_utils/api_service/drama_service.dart | 203 +++ lib/hj_utils/api_service/group_service.dart | 36 + lib/hj_utils/api_service/mine_service.dart | 833 +++++++++ lib/hj_utils/api_service/search_service.dart | 55 + lib/hj_utils/api_service/tag_service.dart | 31 + lib/hj_utils/api_service/vid_service.dart | 406 +++++ lib/hj_utils/buy_util.dart | 52 + lib/hj_utils/codec_support.dart | 51 + lib/hj_utils/const.dart | 80 + lib/hj_utils/date_time_util.dart | 212 +++ lib/hj_utils/detect_line_manager.dart | 132 ++ lib/hj_utils/dns_solve/_answer.dart | 176 ++ lib/hj_utils/dns_solve/_dnsolve.dart | 188 ++ lib/hj_utils/dns_solve/_question.dart | 22 + lib/hj_utils/dns_solve/_response.dart | 90 + lib/hj_utils/dns_solve/dnsolve.dart | 5 + lib/hj_utils/dns_solve/exception.dart | 42 + lib/hj_utils/file_util.dart | 98 + lib/hj_utils/free_play_manager.dart | 107 ++ lib/hj_utils/history_util.dart | 122 ++ lib/hj_utils/holiday_theme.dart | 144 ++ lib/hj_utils/image_util.dart | 276 +++ lib/hj_utils/install_util.dart | 51 + lib/hj_utils/light_model.dart | 125 ++ lib/hj_utils/local_server.dart | 730 ++++++++ lib/hj_utils/local_server_guard.dart | 46 + lib/hj_utils/media_info.dart | 46 + lib/hj_utils/pay/pay_manager.dart | 170 ++ lib/hj_utils/permission_util.dart | 73 + lib/hj_utils/play_diagnose.dart | 69 + lib/hj_utils/screen.dart | 99 + lib/hj_utils/sliver_delegate.dart | 49 + lib/hj_utils/store_keys.dart | 96 + lib/hj_utils/text_util.dart | 17 + lib/hj_utils/version_util.dart | 38 + lib/hj_utils/video_cache_manager.dart | 44 + lib/hj_utils/video_view_type.dart | 96 + lib/hj_utils/widget_util.dart | 11 + lib/hj_utils/widget_utils.dart | 35 + lib/im_manager/api/im_api_service.dart | 76 + .../chat_detail_page/Im_ad_view.dart | 283 +++ .../chat_detail_page/chat_detail_logic.dart | 382 ++++ .../chat_detail_page/chat_detail_page.dart | 100 ++ .../chat_session_list_logic.dart | 94 + .../chat_session_list_view.dart | 135 ++ .../widget/chat_item_cell.dart | 234 +++ .../widget/input_foot_view.dart | 157 ++ .../widget/input_sheet_view.dart | 166 ++ lib/im_manager/im_ad/im_ad_item.dart | 111 ++ lib/im_manager/im_ad/im_ad_manager.dart | 155 ++ lib/im_manager/im_ad/im_ad_provider.dart | 101 ++ lib/im_manager/im_manager.dart | 278 +++ lib/im_manager/im_notification.dart | 99 + lib/im_manager/model/im_message_resp.dart | 203 +++ lib/im_manager/model/im_user_model.dart | 74 + lib/main.dart | 209 +++ lib/routers/jump_router.dart | 289 +++ lib/routers/router_map.dart | 29 + lib/tools_base/ad_manager.dart | 187 ++ lib/tools_base/banner/ads_banner.dart | 337 ++++ lib/tools_base/banner/ads_grid_view.dart | 177 ++ lib/tools_base/banner/ads_item.dart | 191 ++ lib/tools_base/base_list_controller.dart | 83 + lib/tools_base/cache/cache_util.dart | 141 ++ .../cache/cancel_token_manager.dart | 56 + lib/tools_base/cache/dio_file_service.dart | 67 + .../history/base_history_record_store.dart | 104 ++ .../cache/history/cartoon_history_store.dart | 14 + .../cache/history/comics_history_store.dart | 14 + .../history/community_history_store.dart | 14 + .../cache/history/drama_resume_store.dart | 152 ++ .../cache/history/game_history_store.dart | 14 + .../cache/history/novel_history_store.dart | 14 + .../cache/history/pic_history_store.dart | 14 + .../cache/history/search_history_store.dart | 81 + .../history/short_video_history_store.dart | 14 + .../cache/history/video_history_store.dart | 14 + lib/tools_base/cache/image_cache_manager.dart | 119 ++ lib/tools_base/debug_log.dart | 28 + lib/tools_base/event_bus/event_bus_util.dart | 45 + lib/tools_base/event_bus/events.dart | 48 + .../file_upload/file_upload_tool.dart | 335 ++++ .../file_upload/upload_result_model.dart | 23 + lib/tools_base/global_store/store.dart | 251 +++ lib/tools_base/headphone_monitor.dart | 79 + lib/tools_base/image/image_crypto.dart | 64 + lib/tools_base/image/image_disk_cache.dart | 32 + .../indicator/custom_tab_indicator.dart | 73 + .../loading/loading_alert_widget.dart | 101 ++ .../loading/loading_center_widget.dart | 117 ++ lib/tools_base/loading/loading_helper.dart | 36 + lib/tools_base/module_sort_manager.dart | 75 + lib/tools_base/net/aes_util.dart | 32 + lib/tools_base/net/api_exception.dart | 12 + lib/tools_base/net/base_resp_bean.dart | 60 + lib/tools_base/net/curl_util.dart | 57 + lib/tools_base/net/http_manager.dart | 272 +++ lib/tools_base/net/http_resp_interceptor.dart | 102 ++ .../net/http_response_interceptor.dart | 108 ++ lib/tools_base/net/load_apk/dio_cli.dart | 108 ++ .../net/load_apk/dio_slice_downloader.dart | 354 ++++ lib/tools_base/net/load_apk/e_data.dart | 32 + lib/tools_base/net/net_code.dart | 56 + lib/tools_base/net/net_manager.dart | 115 ++ .../refresh/horizontal_load_more.dart | 127 ++ lib/tools_base/refresh/pull_refresh.dart | 172 ++ .../richTextParsing/html_parser.dart | 414 +++++ .../richTextParsing/html_pro_parser.dart | 71 + lib/tools_base/toast.dart | 62 + lib/tools_base/unique_tag_mixin.dart | 23 + .../video_download/download_button.dart | 94 + .../video_download/ios_video_downloader.dart | 556 ++++++ .../media_download_manager.dart | 353 ++++ .../video_download/video_cache_store.dart | 172 ++ .../video_download_manager.dart | 299 ++++ .../video_download/video_save_util.dart | 243 +++ .../widget/add_media_source_button.dart | 58 + .../widget/card_swiper/card_swiper.dart | 12 + .../widget/card_swiper/src/custom_layout.dart | 456 +++++ .../flutter_page_indicator.dart | 356 ++++ .../widget/card_swiper/src/swiper.dart | 996 +++++++++++ .../card_swiper/src/swiper_control.dart | 131 ++ .../card_swiper/src/swiper_controller.dart | 45 + .../card_swiper/src/swiper_pagination.dart | 303 ++++ .../widget/card_swiper/src/swiper_plugin.dart | 53 + .../index_controller.dart | 119 ++ .../transformer_page_view.dart | 611 +++++++ lib/tools_base/widget/common_alert.dart | 144 ++ lib/tools_base/widget/common_dialog.dart | 68 + lib/tools_base/widget/fly_to_overlay.dart | 118 ++ lib/tools_base/widget/follow_button.dart | 193 ++ lib/tools_base/widget/group_text_filed.dart | 87 + lib/tools_base/widget/header_widget.dart | 45 + lib/tools_base/widget/image_browser_page.dart | 341 ++++ lib/tools_base/widget/keep_alive_widget.dart | 27 + lib/tools_base/widget/lazy_indexed_stack.dart | 53 + lib/tools_base/widget/like_burst_view.dart | 91 + lib/tools_base/widget/marquee_widget.dart | 68 + .../widget/multitap_recognizer.dart | 33 + lib/tools_base/widget/net_image_widget.dart | 88 + .../src/element_registry.dart | 96 + .../src/item_positions_listener.dart | 61 + .../src/item_positions_notifier.dart | 13 + .../src/positioned_list.dart | 370 ++++ .../src/post_mount_callback.dart | 35 + .../src/scroll_offset_listener.dart | 21 + .../src/scroll_offset_notifier.dart | 20 + .../src/scroll_view.dart | 83 + .../src/scrollable_positioned_list.dart | 681 +++++++ .../scroll_positioned_list/src/viewport.dart | 315 ++++ .../scroll_positioned_list/src/wrapping.dart | 995 +++++++++++ lib/tools_base/widget/sheet_handle_bar.dart | 17 + lib/tools_base/widget/shrink_wrap.dart | 847 +++++++++ lib/tools_base/widget/stagger_in_item.dart | 58 + lib/tools_base/widget/swipe_action_item.dart | 129 ++ lib/track_event_manager/device_service.dart | 315 ++++ lib/track_event_manager/track_manager.dart | 445 +++++ lib/track_event_manager/track_session.dart | 89 + m3u8_downloader-master/LICENSE | 1 + m3u8_downloader-master/README.md | 15 + m3u8_downloader-master/android/build.gradle | 31 + .../android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 3 + .../FlutterBackgroundExecutor.java | 100 ++ .../m3u8Downloader/M3U8DownloaderPlugin.java | 389 ++++ .../com/vincent/m3u8Downloader/bean/M3U8.java | 111 ++ .../vincent/m3u8Downloader/bean/M3U8Task.java | 93 + .../m3u8Downloader/bean/M3U8TaskState.java | 41 + .../vincent/m3u8Downloader/bean/M3U8Ts.java | 95 + .../downloader/M3U8DownloadConfig.java | 92 + .../downloader/M3U8DownloadTask.java | 338 ++++ .../downloader/M3U8Downloader.java | 273 +++ .../downloader/WeakHandler.java | 294 +++ .../listener/OnInfoCallback.java | 14 + .../listener/OnM3U8DownloadListener.java | 69 + .../listener/OnTaskDownloadListener.java | 56 + .../m3u8Downloader/utils/EncryptUtil.java | 180 ++ .../vincent/m3u8Downloader/utils/M3U8Log.java | 24 + .../m3u8Downloader/utils/M3U8Util.java | 249 +++ .../utils/NotificationUtil.java | 160 ++ .../m3u8Downloader/utils/SpHelper.java | 68 + m3u8_downloader-master/example/README.md | 16 + .../example/android/app/build.gradle | 58 + .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 49 + .../m3u8_downloader_example/MainActivity.java | 6 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + .../example/android/build.gradle | 27 + .../example/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 6 + .../example/android/settings.gradle | 11 + m3u8_downloader-master/example/lib/main.dart | 224 +++ .../example/lib/video_player_page.dart | 112 ++ m3u8_downloader-master/example/pubspec.yaml | 75 + .../example/test/widget_test.dart | 26 + .../ios/Classes/M3u8DownloaderPlugin.swift | 18 + .../ios/m3u8_downloader.podspec | 20 + .../lib/callback_dispatcher.dart | 32 + .../lib/m3u8_downloader.dart | 237 +++ m3u8_downloader-master/pubspec.yaml | 65 + .../test/m3u8_downloader_test.dart | 22 + project.md | 219 +++ pubspec.yaml | 137 ++ test/widget_test.dart | 30 + .../lib/flutter_native_image.dart | 83 + third_party/flutter_native_image/pubspec.yaml | 15 + .../lib/image_gallery_saver.dart | 33 + third_party/image_gallery_saver/pubspec.yaml | 13 + .../image_pickers/lib/image_pickers.dart | 121 ++ third_party/image_pickers/pubspec.yaml | 14 + 1262 files changed, 119813 insertions(+) create mode 100644 .fvmrc create mode 100644 .gitignore create mode 100644 README.md create mode 100644 analysis_options.yaml create mode 100644 analytics_sdk/.gitignore create mode 100644 analytics_sdk/LICENSE create mode 100644 analytics_sdk/README.md create mode 100644 analytics_sdk/SDK_USAGE_GUIDE.md create mode 100644 analytics_sdk/analysis_options.yaml create mode 100644 analytics_sdk/android/.gitignore create mode 100644 analytics_sdk/android/build.gradle create mode 100644 analytics_sdk/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 analytics_sdk/android/settings.gradle create mode 100644 analytics_sdk/android/src/main/AndroidManifest.xml create mode 100644 analytics_sdk/android/src/main/kotlin/com/example/analytics_sdk/AnalyticsSdkPlugin.kt create mode 100644 analytics_sdk/android/src/main/kotlin/com/example/analytics_sdk/ClientIdentifierResolver.kt create mode 100644 analytics_sdk/android/src/test/kotlin/com/example/analytics_sdk/ClientIdentifierResolverTest.kt create mode 100644 analytics_sdk/android/src/test/kotlin/com/example/data_plus_sdk/DataPlusSdkPluginTest.kt create mode 100644 analytics_sdk/ios/.gitignore create mode 100644 analytics_sdk/ios/Assets/.gitkeep create mode 100644 analytics_sdk/ios/Classes/DataPlusSdkPlugin.swift create mode 100644 analytics_sdk/ios/analytics_sdk.podspec create mode 100644 analytics_sdk/lib/analytics_sdk.dart create mode 100644 analytics_sdk/lib/analytics_sdk_web.dart create mode 100644 analytics_sdk/lib/config/api_config.dart create mode 100644 analytics_sdk/lib/config/sdk_config.dart create mode 100644 analytics_sdk/lib/const/event_type.dart create mode 100644 analytics_sdk/lib/entity/ad_click_event.dart create mode 100644 analytics_sdk/lib/entity/ad_impression_event.dart create mode 100644 analytics_sdk/lib/entity/ad_sdk_events.dart create mode 100644 analytics_sdk/lib/entity/advertising_event.dart create mode 100644 analytics_sdk/lib/entity/analytics_tab.dart create mode 100644 analytics_sdk/lib/entity/analytics_utils.dart create mode 100644 analytics_sdk/lib/entity/app_install_event.dart create mode 100644 analytics_sdk/lib/entity/app_lifecycle_event.dart create mode 100644 analytics_sdk/lib/entity/app_page_item_event.dart create mode 100644 analytics_sdk/lib/entity/app_page_view_event.dart create mode 100644 analytics_sdk/lib/entity/app_session_summary_event.dart create mode 100644 analytics_sdk/lib/entity/comic_event.dart create mode 100644 analytics_sdk/lib/entity/keyword_click_event.dart create mode 100644 analytics_sdk/lib/entity/keyword_search_event.dart create mode 100644 analytics_sdk/lib/entity/navigation_event.dart create mode 100644 analytics_sdk/lib/entity/network_state_change_event.dart create mode 100644 analytics_sdk/lib/entity/novel_event.dart create mode 100644 analytics_sdk/lib/entity/page_click_event.dart create mode 100644 analytics_sdk/lib/entity/recommend_list_click_event.dart create mode 100644 analytics_sdk/lib/entity/recommend_list_view_event.dart create mode 100644 analytics_sdk/lib/entity/sdk_init_event.dart create mode 100644 analytics_sdk/lib/entity/video_event.dart create mode 100644 analytics_sdk/lib/enum/ad_type_enum.dart create mode 100644 analytics_sdk/lib/enum/auth_type_enum.dart create mode 100644 analytics_sdk/lib/enum/banner_event_enum.dart create mode 100644 analytics_sdk/lib/enum/click_item_type_enum.dart create mode 100644 analytics_sdk/lib/enum/consume_reason_enum.dart create mode 100644 analytics_sdk/lib/enum/currency_enum.dart create mode 100644 analytics_sdk/lib/enum/device_enum.dart create mode 100644 analytics_sdk/lib/enum/order_type_enum.dart create mode 100644 analytics_sdk/lib/enum/page_route_enum.dart create mode 100644 analytics_sdk/lib/enum/pay_type_enum.dart create mode 100644 analytics_sdk/lib/enum/read_behavior_enum.dart create mode 100644 analytics_sdk/lib/enum/recommend_content_type_enum.dart create mode 100644 analytics_sdk/lib/enum/user_type_enum.dart create mode 100644 analytics_sdk/lib/enum/video_content_type_enum.dart create mode 100644 analytics_sdk/lib/enum/video_event_enum.dart create mode 100644 analytics_sdk/lib/extension/tab_analytics_extension.dart create mode 100644 analytics_sdk/lib/manager/ad_impression_manager.dart create mode 100644 analytics_sdk/lib/manager/domain_manager.dart create mode 100644 analytics_sdk/lib/manager/event_persistence.dart create mode 100644 analytics_sdk/lib/manager/event_persistence_native.dart create mode 100644 analytics_sdk/lib/manager/event_persistence_web.dart create mode 100644 analytics_sdk/lib/manager/event_processor.dart create mode 100644 analytics_sdk/lib/manager/event_type_config_manager.dart create mode 100644 analytics_sdk/lib/manager/network_state_fields_stub.dart create mode 100644 analytics_sdk/lib/manager/network_state_fields_web.dart create mode 100644 analytics_sdk/lib/manager/page_click_auto_fields.dart create mode 100644 analytics_sdk/lib/manager/page_click_auto_fields_stub.dart create mode 100644 analytics_sdk/lib/manager/page_click_auto_fields_web.dart create mode 100644 analytics_sdk/lib/manager/page_name_manager.dart create mode 100644 analytics_sdk/lib/manager/page_observer_registration_guard.dart create mode 100644 analytics_sdk/lib/manager/page_view_auto_fields.dart create mode 100644 analytics_sdk/lib/manager/page_view_auto_fields_stub.dart create mode 100644 analytics_sdk/lib/manager/page_view_auto_fields_web.dart create mode 100644 analytics_sdk/lib/manager/sdk_activation_store.dart create mode 100644 analytics_sdk/lib/manager/sdk_context.dart create mode 100644 analytics_sdk/lib/manager/sdk_init_payload_builder.dart create mode 100644 analytics_sdk/lib/manager/sdk_init_platform_fields.dart create mode 100644 analytics_sdk/lib/manager/sdk_init_platform_fields_native.dart create mode 100644 analytics_sdk/lib/manager/sdk_init_platform_fields_web.dart create mode 100644 analytics_sdk/lib/manager/session_behavior_manager.dart create mode 100644 analytics_sdk/lib/manager/session_behavior_monitor.dart create mode 100644 analytics_sdk/lib/manager/session_behavior_monitor_stub.dart create mode 100644 analytics_sdk/lib/manager/session_behavior_monitor_web.dart create mode 100644 analytics_sdk/lib/manager/session_manager.dart create mode 100644 analytics_sdk/lib/manager/session_metrics_manager.dart create mode 100644 analytics_sdk/lib/manager/session_sensor_proximity.dart create mode 100644 analytics_sdk/lib/manager/session_sensor_proximity_native.dart create mode 100644 analytics_sdk/lib/manager/session_sensor_proximity_stub.dart create mode 100644 analytics_sdk/lib/manager/session_sensor_sampler.dart create mode 100644 analytics_sdk/lib/manager/user_manager.dart create mode 100644 analytics_sdk/lib/observer/app_lifecycle_observer.dart create mode 100644 analytics_sdk/lib/observer/app_lifecycle_web_unload_stub.dart create mode 100644 analytics_sdk/lib/observer/app_lifecycle_web_unload_web.dart create mode 100644 analytics_sdk/lib/observer/page_lifecycle_observer.dart create mode 100644 analytics_sdk/lib/utils/aes_gcm_util.dart create mode 100644 analytics_sdk/lib/utils/device_fingerprint_native.dart create mode 100644 analytics_sdk/lib/utils/device_fingerprint_util.dart create mode 100644 analytics_sdk/lib/utils/device_fingerprint_web.dart create mode 100644 analytics_sdk/lib/utils/device_id_native.dart create mode 100644 analytics_sdk/lib/utils/device_id_util.dart create mode 100644 analytics_sdk/lib/utils/device_id_web.dart create mode 100644 analytics_sdk/lib/utils/device_info_native.dart create mode 100644 analytics_sdk/lib/utils/device_info_util.dart create mode 100644 analytics_sdk/lib/utils/device_info_web.dart create mode 100644 analytics_sdk/lib/utils/environment_info_native.dart create mode 100644 analytics_sdk/lib/utils/environment_info_util.dart create mode 100644 analytics_sdk/lib/utils/environment_info_web.dart create mode 100644 analytics_sdk/lib/utils/event_validator.dart create mode 100644 analytics_sdk/lib/utils/js_interop_compat.dart create mode 100644 analytics_sdk/lib/utils/js_interop_legacy.dart create mode 100644 analytics_sdk/lib/utils/js_interop_modern.dart create mode 100644 analytics_sdk/lib/utils/logger.dart create mode 100644 analytics_sdk/lib/utils/network_type_resolver.dart create mode 100644 analytics_sdk/lib/utils/platform_storage.dart create mode 100644 analytics_sdk/lib/utils/platform_storage_native.dart create mode 100644 analytics_sdk/lib/utils/platform_storage_web.dart create mode 100644 analytics_sdk/lib/utils/runtime_observation_native.dart create mode 100644 analytics_sdk/lib/utils/runtime_observation_stub.dart create mode 100644 analytics_sdk/lib/utils/uuid_util.dart create mode 100644 analytics_sdk/lib/utils/widget_bridge.dart create mode 100644 analytics_sdk/lib/widget/_tab_helpers.dart create mode 100644 analytics_sdk/lib/widget/analytics_bottom_nav_wrapper.dart create mode 100644 analytics_sdk/lib/widget/analytics_debug_banner.dart create mode 100644 analytics_sdk/lib/widget/analytics_tab_bar_wrapper.dart create mode 100644 analytics_sdk/lib/widget/global_click_wrapper.dart create mode 100644 analytics_sdk/pubspec.yaml create mode 100644 analytics_sdk/test/analytics_sdk_ad_dedup_test.dart create mode 100644 analytics_sdk/test/analytics_sdk_decrypt_test.dart create mode 100644 analytics_sdk/test/analytics_utils_fp_version_test.dart create mode 100644 analytics_sdk/test/configure_null_test.dart create mode 100644 analytics_sdk/test/data_plus_sdk_method_channel_test.dart create mode 100644 analytics_sdk/test/data_plus_sdk_test.dart create mode 100644 analytics_sdk/test/domain_manager_cache_test.dart create mode 100644 analytics_sdk/test/event_processor_logic_test.dart create mode 100644 analytics_sdk/test/event_processor_tombstone_test.dart create mode 100644 analytics_sdk/test/event_validator_regression_test.dart create mode 100644 analytics_sdk/test/event_validator_type_coercion_test.dart create mode 100644 analytics_sdk/test/logout_user_test.dart create mode 100644 analytics_sdk/test/page_name_mapper_test.dart create mode 100644 analytics_sdk/test/session_manager_regression_test.dart create mode 100644 android/.gitignore create mode 100644 android/.settings/org.eclipse.buildship.core.prefs create mode 100644 android/app/build.gradle create mode 100644 android/app/key/README.md create mode 100644 android/app/key/genkey.sh create mode 100644 android/app/key/resp_genkey.sh create mode 100644 android/app/proguard-rules.pro create mode 100644 android/app/src/debug/AndroidManifest.xml create mode 100644 android/app/src/main/AndroidManifest.xml create mode 100644 android/app/src/main/java/io/flutter/plugins/JPFileUtils.java create mode 100644 android/app/src/main/java/io/flutter/plugins/JPNativePlugin.java create mode 100644 android/app/src/main/kotlin/com/xvproject/JPApplication.kt create mode 100644 android/app/src/main/kotlin/com/xvproject/MainActivity.kt create mode 100644 android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 android/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 android/app/src/main/res/drawable/launch_background.xml create mode 100644 android/app/src/main/res/drawable/launch_bg.xml create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_splash_bg.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_1.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_10.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_11.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_12.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_13.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_14.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_15.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_16.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_17.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_18.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_2.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_3.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_4.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_5.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_6.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_7.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_8.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_9.webp create mode 100644 android/app/src/main/res/values-night/styles.xml create mode 100644 android/app/src/main/res/values/styles.xml create mode 100644 android/app/src/main/res/xml/totalfilepath.xml create mode 100644 android/app/src/profile/AndroidManifest.xml create mode 100644 android/build.gradle create mode 100755 android/build_release.sh create mode 100755 android/generatekey.sh create mode 100644 android/gradle.properties create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100644 android/settings.gradle create mode 100644 assets/images/acg/acg_like_nor.png create mode 100644 assets/images/acg/acg_like_sel.png create mode 100644 assets/images/acg/acg_menu.png create mode 100644 assets/images/acg/acg_narrow.png create mode 100644 assets/images/acg/acg_noval_back.png create mode 100644 assets/images/acg/acg_share.png create mode 100644 assets/images/acg/cartoon_auto_play.gif create mode 100644 assets/images/acg/cartoon_auto_play.png create mode 100644 assets/images/acg/cartoon_comment.png create mode 100644 assets/images/acg/cartoon_menu.png create mode 100644 assets/images/acg/cartoon_progress.png create mode 100644 assets/images/acg/forward.png create mode 100644 assets/images/acg/list_cycle.png create mode 100644 assets/images/acg/next.png create mode 100644 assets/images/acg/noval_sign.png create mode 100644 assets/images/acg/noval_time_back.png create mode 100644 assets/images/acg/noval_time_forword.png create mode 100644 assets/images/acg/pause.png create mode 100644 assets/images/acg/play.png create mode 100644 assets/images/acg/play_list.png create mode 100644 assets/images/acg/single_cycle.png create mode 100644 assets/images/acg/text_last.png create mode 100644 assets/images/acg/text_moon.png create mode 100644 assets/images/acg/text_next.png create mode 100644 assets/images/acg/text_play_menu.png create mode 100644 assets/images/acg/text_play_sort.png create mode 100644 assets/images/acg/text_sun.png create mode 100644 assets/images/ai/ai_address.png create mode 100644 assets/images/ai/ai_changeface_right.webp create mode 100644 assets/images/ai/ai_changeface_wrong_1.webp create mode 100644 assets/images/ai/ai_changeface_wrong_2.webp create mode 100644 assets/images/ai/ai_coupon_bg.webp create mode 100644 assets/images/ai/ai_draw.png create mode 100644 assets/images/ai/ai_draw_narrow.png create mode 100644 assets/images/ai/ai_draw_style.png create mode 100644 assets/images/ai/ai_draw_unselected.png create mode 100644 assets/images/ai/ai_girl_friend_bg.webp create mode 100644 assets/images/ai/ai_girl_friend_btn.webp create mode 100644 assets/images/ai/ai_girl_friend_top.webp create mode 100644 assets/images/ai/ai_girl_friend_topup.webp create mode 100644 assets/images/ai/ai_itv.png create mode 100644 assets/images/ai/ai_mate_enter.png create mode 100644 assets/images/ai/ai_narrow.png create mode 100644 assets/images/ai/ai_novel_enter.png create mode 100644 assets/images/ai/ai_pic_face.png create mode 100644 assets/images/ai/ai_record_add.webp create mode 100644 assets/images/ai/ai_record_delete.png create mode 100644 assets/images/ai/ai_record_equals.png create mode 100644 assets/images/ai/ai_record_failed.webp create mode 100644 assets/images/ai/ai_record_org.png create mode 100644 assets/images/ai/ai_video_face.png create mode 100644 assets/images/ai/icon_ai_download.png create mode 100644 assets/images/ai/make_the_same.png create mode 100644 assets/images/ai/paly_btn.png create mode 100644 assets/images/app_icon/icon_1.webp create mode 100644 assets/images/app_icon/icon_10.webp create mode 100644 assets/images/app_icon/icon_11.webp create mode 100644 assets/images/app_icon/icon_12.webp create mode 100644 assets/images/app_icon/icon_13.webp create mode 100644 assets/images/app_icon/icon_14.webp create mode 100644 assets/images/app_icon/icon_15.webp create mode 100644 assets/images/app_icon/icon_2.webp create mode 100644 assets/images/app_icon/icon_3.webp create mode 100644 assets/images/app_icon/icon_4.webp create mode 100644 assets/images/app_icon/icon_5.webp create mode 100644 assets/images/app_icon/icon_6.webp create mode 100644 assets/images/app_icon/icon_7.webp create mode 100644 assets/images/app_icon/icon_8.webp create mode 100644 assets/images/app_icon/icon_9.webp create mode 100644 assets/images/comment/arrow_grey.webp create mode 100644 assets/images/comment/god_comment.webp create mode 100644 assets/images/comment/ic_send.webp create mode 100644 assets/images/comment/icon_pic.webp create mode 100644 assets/images/comment/like_red.webp create mode 100644 assets/images/comment/like_white.webp create mode 100644 assets/images/comment/msg_icon.png create mode 100644 assets/images/comment/vip_v.webp create mode 100644 assets/images/common/ad_close.png create mode 100644 assets/images/common/add_red.png create mode 100644 assets/images/common/arrow_down.png create mode 100644 assets/images/common/arrow_right_grey.webp create mode 100644 assets/images/common/back_circle.png create mode 100644 assets/images/common/back_circle_grey.png create mode 100644 assets/images/common/check.webp create mode 100644 assets/images/common/chevron_down.webp create mode 100644 assets/images/common/close_button.png create mode 100644 assets/images/common/close_grey.png create mode 100644 assets/images/common/coin.webp create mode 100644 assets/images/common/collect_empty.png create mode 100644 assets/images/common/collect_grey.png create mode 100644 assets/images/common/collect_path.png create mode 100644 assets/images/common/collect_red.png create mode 100644 assets/images/common/collect_white.png create mode 100644 assets/images/common/collect_yellow.png create mode 100644 assets/images/common/common_back.png create mode 100644 assets/images/common/common_back_new.png create mode 100644 assets/images/common/eye_grey.webp create mode 100644 assets/images/common/eye_white.webp create mode 100644 assets/images/common/ic_nodata.webp create mode 100644 assets/images/common/icon_broadcast.png create mode 100644 assets/images/common/like_red.png create mode 100644 assets/images/common/like_white.png create mode 100644 assets/images/common/lucky_lottery.webp create mode 100644 assets/images/common/official_icon.png create mode 100644 assets/images/common/place_holder_logo.webp create mode 100644 assets/images/common/radio_sel.png create mode 100644 assets/images/common/radio_select.png create mode 100644 assets/images/common/search_close.png create mode 100644 assets/images/common/search_icon.png create mode 100644 assets/images/common/share_grey.png create mode 100644 assets/images/common/share_grey_pic.png create mode 100644 assets/images/community/add_grey.png create mode 100644 assets/images/community/community_chatgroup_sel.png create mode 100644 assets/images/community/community_coin.webp create mode 100644 assets/images/community/community_main_sel.png create mode 100644 assets/images/community/community_noval_sel.png create mode 100644 assets/images/community/community_pic_sel.png create mode 100644 assets/images/community/community_sign.webp create mode 100644 assets/images/community/community_thumb_sel.png create mode 100644 assets/images/community/count_msg.png create mode 100644 assets/images/community/count_msg_gray.png create mode 100644 assets/images/community/hot_search.png create mode 100644 assets/images/community/icon_pic.png create mode 100644 assets/images/community/like_count.png create mode 100644 assets/images/community/like_empty.png create mode 100644 assets/images/community/like_empty_gray.png create mode 100644 assets/images/community/link_copy_icon.webp create mode 100644 assets/images/community/locked_white.png create mode 100644 assets/images/community/play_icon.png create mode 100644 assets/images/community/post_detail_coin.png create mode 100644 assets/images/community/publish_img.webp create mode 100644 assets/images/community/publish_img_text.webp create mode 100644 assets/images/community/publish_video.webp create mode 100644 assets/images/community/tuji_icon.webp create mode 100644 assets/images/community/view_count.png create mode 100644 assets/images/holiday/1/home_0_0.png create mode 100644 assets/images/holiday/1/home_0_1.png create mode 100644 assets/images/holiday/1/home_1_0.png create mode 100644 assets/images/holiday/1/home_1_1.png create mode 100644 assets/images/holiday/1/home_2_0.png create mode 100644 assets/images/holiday/1/home_2_1.png create mode 100644 assets/images/holiday/1/home_3_0.png create mode 100644 assets/images/holiday/1/home_3_1.png create mode 100644 assets/images/holiday/1/home_4_0.png create mode 100644 assets/images/holiday/1/home_4_1.png create mode 100644 assets/images/holiday/1/home_5_0.png create mode 100644 assets/images/holiday/1/home_5_1.png create mode 100644 assets/images/holiday/1/ic_launcher.webp create mode 100644 assets/images/holiday/1/mine_agent_enter.webp create mode 100644 assets/images/holiday/1/mine_ai_enter.webp create mode 100644 assets/images/holiday/1/mine_gold_enter.webp create mode 100644 assets/images/holiday/1/mine_vip_bg.webp create mode 100644 assets/images/holiday/1/mine_vip_enter.webp create mode 100644 assets/images/holiday/1/publish.png create mode 100644 assets/images/holiday/5/home_0_0.png create mode 100644 assets/images/holiday/5/home_0_1.png create mode 100644 assets/images/holiday/5/home_1_0.png create mode 100644 assets/images/holiday/5/home_1_1.png create mode 100644 assets/images/holiday/5/home_2_0.png create mode 100644 assets/images/holiday/5/home_2_1.png create mode 100644 assets/images/holiday/5/home_3_0.png create mode 100644 assets/images/holiday/5/home_3_1.png create mode 100644 assets/images/holiday/5/home_4_0.png create mode 100644 assets/images/holiday/5/home_4_1.png create mode 100644 assets/images/holiday/5/home_5_0.png create mode 100644 assets/images/holiday/5/home_5_1.png create mode 100644 assets/images/holiday/5/ic_launcher.webp create mode 100644 assets/images/holiday/5/icon_broadcast.png create mode 100644 assets/images/holiday/5/main_bg.webp create mode 100644 assets/images/holiday/5/mine_agent_enter.webp create mode 100644 assets/images/holiday/5/mine_ai_enter.webp create mode 100644 assets/images/holiday/5/mine_bg.webp create mode 100644 assets/images/holiday/5/mine_gold_enter.webp create mode 100644 assets/images/holiday/5/mine_vip_bg.webp create mode 100644 assets/images/holiday/5/mine_vip_enter.webp create mode 100644 assets/images/holiday/5/publish.png create mode 100644 assets/images/holiday/5/sign_icon.png create mode 100644 assets/images/home/aw_bg.webp create mode 100644 assets/images/home/aw_button.webp create mode 100644 assets/images/home/aw_tag.webp create mode 100644 assets/images/home/aw_warning.webp create mode 100644 assets/images/home/collect_start.webp create mode 100644 assets/images/home/drama_heat.svg create mode 100644 assets/images/home/drama_section_more.svg create mode 100644 assets/images/home/drama_tip.webp create mode 100644 assets/images/home/grid_style.webp create mode 100644 assets/images/home/history_delete.webp create mode 100644 assets/images/home/home_hot.webp create mode 100644 assets/images/home/jump_to_top.png create mode 100644 assets/images/home/libary_search.webp create mode 100644 assets/images/home/list_style.webp create mode 100644 assets/images/home/nav_logo.webp create mode 100644 assets/images/home/nav_sign.webp create mode 100644 assets/images/home/rank_crown.webp create mode 100644 assets/images/home/search_close.webp create mode 100644 assets/images/launch/app_mask.webp create mode 100644 assets/images/launch/ic_splash_bg.webp create mode 100644 assets/images/live_images/cp_1.webp create mode 100644 assets/images/live_images/cp_2.webp create mode 100644 assets/images/live_images/cp_3.webp create mode 100644 assets/images/live_images/cp_4.webp create mode 100644 assets/images/live_images/cp_title.webp create mode 100644 assets/images/live_images/four_grid_bg.webp create mode 100644 assets/images/live_images/live_charge.webp create mode 100644 assets/images/live_images/live_main_bg.webp create mode 100644 assets/images/live_images/live_main_bg2.webp create mode 100644 assets/images/live_images/live_section_bg_1.webp create mode 100644 assets/images/live_images/live_section_bg_2.webp create mode 100644 assets/images/live_images/live_section_bg_3.webp create mode 100644 assets/images/live_images/live_section_bg_4.webp create mode 100644 assets/images/live_images/live_share.png create mode 100644 assets/images/live_images/live_six_item_bg.webp create mode 100644 assets/images/live_images/nine_grid_bg.webp create mode 100644 assets/images/live_images/six_grid_bg.webp create mode 100644 assets/images/live_images/six_round_bg.webp create mode 100644 assets/images/mine/add_equal.png create mode 100644 assets/images/mine/area_discount_bg.webp create mode 100644 assets/images/mine/check_in_coin.webp create mode 100644 assets/images/mine/checked_in_coin.webp create mode 100644 assets/images/mine/code_bg.webp create mode 100644 assets/images/mine/coin_icon.webp create mode 100644 assets/images/mine/error.webp create mode 100644 assets/images/mine/ic_add2.png create mode 100644 assets/images/mine/ic_alipay.png create mode 100644 assets/images/mine/ic_coin.webp create mode 100644 assets/images/mine/ic_sign_expired.webp create mode 100644 assets/images/mine/ic_sign_indicator.webp create mode 100644 assets/images/mine/ic_sign_lock.png create mode 100644 assets/images/mine/ic_union.png create mode 100644 assets/images/mine/ic_usdt.png create mode 100644 assets/images/mine/ic_vip_enter.png create mode 100644 assets/images/mine/ic_wechat.png create mode 100644 assets/images/mine/icon_copy.png create mode 100644 assets/images/mine/icon_resign_bg.webp create mode 100644 assets/images/mine/icon_resign_close.png create mode 100644 assets/images/mine/icon_sign_click.webp create mode 100644 assets/images/mine/invite_steps.webp create mode 100644 assets/images/mine/mine_bg.webp create mode 100644 assets/images/mine/mine_col_delete.png create mode 100644 assets/images/mine/mine_collect.png create mode 100644 assets/images/mine/mine_follow.png create mode 100644 assets/images/mine/mine_id.png create mode 100644 assets/images/mine/mine_lock_password.png create mode 100644 assets/images/mine/mine_menu_1.png create mode 100644 assets/images/mine/mine_menu_10.png create mode 100644 assets/images/mine/mine_menu_12.png create mode 100644 assets/images/mine/mine_menu_2.png create mode 100644 assets/images/mine/mine_menu_3.png create mode 100644 assets/images/mine/mine_menu_4.png create mode 100644 assets/images/mine/mine_menu_5.png create mode 100644 assets/images/mine/mine_menu_6.png create mode 100644 assets/images/mine/mine_menu_7.png create mode 100644 assets/images/mine/mine_menu_8.png create mode 100644 assets/images/mine/mine_menu_9.png create mode 100644 assets/images/mine/mine_notification.png create mode 100644 assets/images/mine/mine_offical.png create mode 100644 assets/images/mine/mine_post.png create mode 100644 assets/images/mine/mine_shop_icon.webp create mode 100644 assets/images/mine/mine_vip_bg.webp create mode 100644 assets/images/mine/mine_withdraw_nor.png create mode 100644 assets/images/mine/no_vip_bottom.webp create mode 100644 assets/images/mine/normal.png create mode 100644 assets/images/mine/pay_icon103.png create mode 100644 assets/images/mine/privilege_ai.webp create mode 100644 assets/images/mine/privilege_bg.webp create mode 100644 assets/images/mine/privilege_game.webp create mode 100644 assets/images/mine/privilege_gold.webp create mode 100644 assets/images/mine/proxy_banner.webp create mode 100644 assets/images/mine/proxy_header_bg.webp create mode 100644 assets/images/mine/red_checked.png create mode 100644 assets/images/mine/scan_photo.png create mode 100644 assets/images/mine/select.png create mode 100644 assets/images/mine/setting.png create mode 100644 assets/images/mine/share_bg.webp create mode 100644 assets/images/mine/sign_take_top.webp create mode 100644 assets/images/mine/user_center_bg.webp create mode 100644 assets/images/mine/vip_1.webp create mode 100644 assets/images/mine/vip_2.webp create mode 100644 assets/images/mine/vip_3.webp create mode 100644 assets/images/mine/vip_4.webp create mode 100644 assets/images/mine/vip_arrow.svg create mode 100644 assets/images/mine/vip_bottom.webp create mode 100644 assets/images/mine/vip_entry_card_badge.png create mode 100644 assets/images/mine/vip_entry_card_bg.png create mode 100644 assets/images/mine/vip_more_title.webp create mode 100644 assets/images/mine/vip_privilege_bg.webp create mode 100644 assets/images/mine/vip_special_title.webp create mode 100644 assets/images/mine/vip_upgrade_arrow.svg create mode 100644 assets/images/mine/welfare_normal_bg.webp create mode 100644 assets/images/mine/welfare_vip_background.webp create mode 100644 assets/images/mine/welfare_vip_bg.webp create mode 100644 assets/images/video/add_red.webp create mode 100644 assets/images/video/cache_selected.webp create mode 100644 assets/images/video/cache_start.webp create mode 100644 assets/images/video/cache_stop.webp create mode 100644 assets/images/video/cache_unselected.webp create mode 100644 assets/images/video/chevron_right.webp create mode 100644 assets/images/video/chevron_up.webp create mode 100644 assets/images/video/circle_play.webp create mode 100644 assets/images/video/comment.png create mode 100644 assets/images/video/core_privilege_bg.webp create mode 100644 assets/images/video/download.png create mode 100644 assets/images/video/download_free.png create mode 100644 assets/images/video/download_short.png create mode 100644 assets/images/video/eye_close.png create mode 100644 assets/images/video/eye_open.png create mode 100644 assets/images/video/full_drama.webp create mode 100644 assets/images/video/full_icon.png create mode 100644 assets/images/video/full_screen.webp create mode 100644 assets/images/video/line_switch.webp create mode 100644 assets/images/video/line_white2.png create mode 100644 assets/images/video/pause_icon.webp create mode 100644 assets/images/video/play.webp create mode 100644 assets/images/video/share_arrow.png create mode 100644 assets/images/video/stopwatch.svg create mode 100644 assets/images/video/update_video.webp create mode 100644 assets/images/video/video_like_border.png create mode 100644 assets/images/video/video_like_grey.webp create mode 100644 assets/images/video/voice_noval_icon.png create mode 100755 build_apk.sh create mode 100644 devtools_options.yaml create mode 100644 ios/ExportOptions.plist create mode 100644 ios/ExportOptions.plist.example create mode 100644 ios/Flutter/AppFrameworkInfo.plist create mode 100644 ios/Flutter/Debug.xcconfig create mode 100644 ios/Flutter/Flutter.podspec create mode 100644 ios/Flutter/Release.xcconfig create mode 100644 ios/Flutter/ephemeral/flutter_lldb_helper.py create mode 100644 ios/Flutter/ephemeral/flutter_lldbinit create mode 100755 ios/Flutter/flutter_export_environment.sh create mode 100644 ios/Podfile create mode 100644 ios/Podfile.lock create mode 100644 ios/Runner.xcodeproj/project.pbxproj create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 ios/Runner/AppDelegate.swift create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon20x20@2x 1.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon20x20@2x 2.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon20x20@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20-ipad.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29 1.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x 1.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-38@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x 1.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x 1.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/logo.png create mode 100644 ios/Runner/Assets.xcassets/Contents.json create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/ic_splash_bg 1.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/ic_splash_bg 2.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/ic_splash_bg.png create mode 100644 ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 ios/Runner/Base.lproj/Main.storyboard create mode 100644 ios/Runner/Info.plist create mode 100644 ios/Runner/Runner-Bridging-Header.h create mode 100644 ios/RunnerTests/RunnerTests.swift create mode 100644 lib/alert/aw_permission_alert.dart create mode 100644 lib/alert/mine/vip_level_dialog.dart create mode 100644 lib/alert/splash/app_center_dialog.dart create mode 100644 lib/alert/splash/line_error_dialog.dart create mode 100644 lib/alert/splash/notice_image_dialog.dart create mode 100644 lib/alert/splash/notice_text_dialog.dart create mode 100644 lib/alert/splash/update_dialog.dart create mode 100644 lib/alert/video/buy_vip_alert.dart create mode 100644 lib/alert/video/buy_vip_alert_logic.dart create mode 100644 lib/alert/video/comic_buy_alert.dart create mode 100644 lib/alert/video/share_media_dialog.dart create mode 100644 lib/alert/video/video_buy_bottom_sheet.dart create mode 100644 lib/alert/video/video_line_menu_alert.dart create mode 100644 lib/alert/video/vip_acg_dialog.dart create mode 100644 lib/alert/vip_guide/guide_bottom_sheet.dart create mode 100644 lib/alert/vip_guide/guide_common.dart create mode 100644 lib/alert/vip_guide/guide_config.dart create mode 100644 lib/alert/vip_guide/guide_countdown_dialog.dart create mode 100644 lib/alert/vip_guide/guide_free_trial_sheet.dart create mode 100644 lib/alert/vip_guide/guide_home_dialog.dart create mode 100644 lib/alert/vip_guide/guide_manager.dart create mode 100644 lib/alert/vip_guide/guide_push_banner.dart create mode 100644 lib/alert/vip_guide/guide_push_model.dart create mode 100644 lib/alert/vip_guide/timed_popup_manager.dart create mode 100644 lib/assets_tool/app_colors.dart create mode 100644 lib/assets_tool/images.dart create mode 100755 lib/config/address.dart create mode 100644 lib/config/config.dart create mode 100644 lib/extension/datetime_extension.dart create mode 100644 lib/extension/double_extension.dart create mode 100644 lib/extension/extensions.dart create mode 100644 lib/extension/int_extension.dart create mode 100644 lib/extension/list_extension.dart create mode 100644 lib/extension/map_extension.dart create mode 100644 lib/extension/num_extension.dart create mode 100644 lib/extension/string_extension.dart create mode 100644 lib/hj_model/acg/cartoon_more_list.dart create mode 100644 lib/hj_model/acg/comic_chapters_model.dart create mode 100644 lib/hj_model/actress_info.dart create mode 100644 lib/hj_model/actress_model.dart create mode 100644 lib/hj_model/banner/comment_top_banner_model.dart create mode 100644 lib/hj_model/cartoon_media_info.dart create mode 100644 lib/hj_model/comment/comment_list_res.dart create mode 100644 lib/hj_model/comment/comment_model.dart create mode 100644 lib/hj_model/comment/reply_model.dart create mode 100644 lib/hj_model/comment/user_comment_res.dart create mode 100644 lib/hj_model/drama/drama_models.dart create mode 100644 lib/hj_model/drama_media_info.dart create mode 100644 lib/hj_model/home/collection_model.dart create mode 100644 lib/hj_model/home/module_detail_model.dart create mode 100644 lib/hj_model/home/plate_model.dart create mode 100644 lib/hj_model/home/recommend_list_model.dart create mode 100644 lib/hj_model/home/update_marker_model.dart create mode 100644 lib/hj_model/home/video_library_model.dart create mode 100644 lib/hj_model/home/video_list_model.dart create mode 100644 lib/hj_model/list_base_model.dart create mode 100644 lib/hj_model/media_content.dart create mode 100644 lib/hj_model/message/message_dynamic_list.dart create mode 100644 lib/hj_model/mine/credit_record_model.dart create mode 100644 lib/hj_model/mine/exchange/bill_item_model.dart create mode 100644 lib/hj_model/mine/exchange/dc_model.dart create mode 100644 lib/hj_model/mine/exchange/recharge_list_model.dart create mode 100644 lib/hj_model/mine/exchange/recharge_type_list_model.dart create mode 100644 lib/hj_model/mine/exchange/recharge_url_model.dart create mode 100644 lib/hj_model/mine/exchange_record_model.dart create mode 100644 lib/hj_model/mine/follow_user_list_model.dart create mode 100644 lib/hj_model/mine/happy/happy_model.dart create mode 100644 lib/hj_model/mine/official_list_item_model.dart create mode 100644 lib/hj_model/mine/promotion_record.dart create mode 100644 lib/hj_model/mine/task_center_data.dart create mode 100644 lib/hj_model/mine/vip_card_analytics_event.dart create mode 100644 lib/hj_model/splash/ads_model.dart create mode 100644 lib/hj_model/splash/cut_info.dart create mode 100644 lib/hj_model/splash/domain_source_model.dart create mode 100644 lib/hj_model/splash/watch_count_model.dart create mode 100644 lib/hj_model/user/user_income_info_model.dart create mode 100644 lib/hj_model/user/user_info_model.dart create mode 100644 lib/hj_model/user/wallet_model.dart create mode 100644 lib/hj_model/video_model.dart create mode 100644 lib/hj_page/ai/ai_change_face_logic.dart create mode 100644 lib/hj_page/ai/ai_change_face_page.dart create mode 100644 lib/hj_page/ai/ai_draw/ai_paint_logic.dart create mode 100644 lib/hj_page/ai/ai_draw/ai_paint_page.dart create mode 100644 lib/hj_page/ai/ai_face_sub_logic.dart create mode 100644 lib/hj_page/ai/ai_face_sub_page.dart create mode 100644 lib/hj_page/ai/ai_girl/ai_girl_friend_logic.dart create mode 100644 lib/hj_page/ai/ai_girl/ai_girl_friend_page.dart create mode 100644 lib/hj_page/ai/ai_girl/ai_h5_page.dart create mode 100644 lib/hj_page/ai/ai_home_logic.dart create mode 100644 lib/hj_page/ai/ai_home_page.dart create mode 100644 lib/hj_page/ai/ai_image_to_video/ai_itv_logic.dart create mode 100644 lib/hj_page/ai/ai_image_to_video/ai_itv_page.dart create mode 100644 lib/hj_page/ai/ai_image_to_video/ai_main_itv_page.dart create mode 100644 lib/hj_page/ai/ai_mod_detail_page.dart create mode 100644 lib/hj_page/ai/ai_novel/ai_novel_detail_page.dart create mode 100644 lib/hj_page/ai/ai_novel/ai_novel_logic.dart create mode 100644 lib/hj_page/ai/ai_novel/ai_novel_page.dart create mode 100644 lib/hj_page/ai/ai_record/ai_main_record_logic.dart create mode 100644 lib/hj_page/ai/ai_record/ai_main_record_page.dart create mode 100644 lib/hj_page/ai/ai_record/ai_record_logic.dart create mode 100644 lib/hj_page/ai/ai_record/ai_record_page.dart create mode 100644 lib/hj_page/ai/ai_square/ai_square_logic.dart create mode 100644 lib/hj_page/ai/ai_square/ai_square_page.dart create mode 100644 lib/hj_page/ai/ai_sub_type/ai_function_logic.dart create mode 100644 lib/hj_page/ai/ai_sub_type/ai_strip_sub_page.dart create mode 100644 lib/hj_page/ai/models/ai_change_face_video_model.dart create mode 100644 lib/hj_page/ai/models/ai_girl_resp_model.dart create mode 100644 lib/hj_page/ai/models/ai_mod_list_model.dart create mode 100644 lib/hj_page/ai/models/ai_record_model.dart create mode 100644 lib/hj_page/ai/models/ai_square_model.dart create mode 100644 lib/hj_page/ai/widgets/ai_coupon_sheet.dart create mode 100644 lib/hj_page/ai/widgets/ai_draw_text_field.dart create mode 100644 lib/hj_page/ai/widgets/ai_girl_sheet.dart create mode 100644 lib/hj_page/ai/widgets/ai_image_record.dart create mode 100644 lib/hj_page/ai/widgets/ai_mod_cell.dart create mode 100644 lib/hj_page/ai/widgets/ai_novel_record.dart create mode 100644 lib/hj_page/ai/widgets/ai_paint_record.dart create mode 100644 lib/hj_page/ai/widgets/ai_record_parts.dart create mode 100644 lib/hj_page/ai/widgets/ai_square_cell.dart create mode 100644 lib/hj_page/ai/widgets/ai_video_record.dart create mode 100644 lib/hj_page/ai/widgets/pic_picker.dart create mode 100644 lib/hj_page/cartoon/acg_widget_item.dart create mode 100644 lib/hj_page/cartoon/audio_player_bottomsheet.dart create mode 100644 lib/hj_page/cartoon/audio_player_content.dart create mode 100644 lib/hj_page/cartoon/cartoon_detail_logic.dart create mode 100644 lib/hj_page/cartoon/cartoon_detail_page.dart create mode 100644 lib/hj_page/cartoon/cartoon_read_logic.dart create mode 100644 lib/hj_page/cartoon/cartoon_read_page.dart create mode 100644 lib/hj_page/cartoon/cartoon_recommend_logic.dart create mode 100644 lib/hj_page/cartoon/cartoon_recommend_page.dart create mode 100644 lib/hj_page/cartoon/cartoon_sectionlist_page.dart create mode 100644 lib/hj_page/cartoon/cartoon_sectionlist_sub_logic.dart create mode 100644 lib/hj_page/cartoon/cartoon_sectionlist_sub_page.dart create mode 100644 lib/hj_page/cartoon/cartoon_sub_detail_page.dart create mode 100644 lib/hj_page/cartoon/cartoon_sub_grid_logic.dart create mode 100644 lib/hj_page/cartoon/cartoon_sub_grid_page.dart create mode 100644 lib/hj_page/cartoon/cartoon_sub_module_logic.dart create mode 100644 lib/hj_page/cartoon/cartoon_sub_module_page.dart create mode 100644 lib/hj_page/cartoon/novel_detail_logic.dart create mode 100644 lib/hj_page/cartoon/novel_detail_page.dart create mode 100644 lib/hj_page/cartoon/novel_header.dart create mode 100644 lib/hj_page/cartoon/photo_gallery_item.dart create mode 100644 lib/hj_page/cartoon/text_novel_read_logic.dart create mode 100644 lib/hj_page/cartoon/text_novel_read_page.dart create mode 100644 lib/hj_page/cartoon/voice_novel_read_logic.dart create mode 100644 lib/hj_page/cartoon/voice_novel_read_page.dart create mode 100644 lib/hj_page/cartoon/widget/acg_expand_text.dart create mode 100644 lib/hj_page/cartoon/widget/acg_source_manager.dart create mode 100644 lib/hj_page/cartoon/widget/cartoon_subset_alert.dart create mode 100644 lib/hj_page/cartoon/widget/free_badge.dart create mode 100644 lib/hj_page/comment/cell/comment_item.dart create mode 100644 lib/hj_page/comment/cell/reply_comment_item.dart create mode 100644 lib/hj_page/comment/comment_alert.dart create mode 100644 lib/hj_page/comment/comment_input_view.dart create mode 100644 lib/hj_page/comment/comment_mixin.dart create mode 100644 lib/hj_page/comment/comment_views.dart create mode 100644 lib/hj_page/comment/widget/comment_foot_view.dart create mode 100644 lib/hj_page/comment/widget/comment_rich_text.dart create mode 100644 lib/hj_page/comment/widget/comment_top_banner.dart create mode 100644 lib/hj_page/community/community/community_module_logic.dart create mode 100644 lib/hj_page/community/community/community_module_page.dart create mode 100644 lib/hj_page/community/community/community_tab_logic.dart create mode 100644 lib/hj_page/community/community/community_tab_page.dart create mode 100644 lib/hj_page/community/community_main_page_page/community_main_logic.dart create mode 100644 lib/hj_page/community/community_main_page_page/community_main_page.dart create mode 100644 lib/hj_page/community/community_main_page_page/community_section_tab.dart create mode 100644 lib/hj_page/community/community_tag_page/community_tag_logic.dart create mode 100644 lib/hj_page/community/community_tag_page/community_tag_page.dart create mode 100644 lib/hj_page/community/community_tag_page/subviews/tag_detail_sub_controller.dart create mode 100644 lib/hj_page/community/community_tag_page/subviews/tag_detail_subview.dart create mode 100644 lib/hj_page/community/group_chat/alert/group_buy_bottom_sheet.dart create mode 100644 lib/hj_page/community/group_chat/detail/soul_group_detail_logic.dart create mode 100644 lib/hj_page/community/group_chat/detail/soul_group_detail_page.dart create mode 100644 lib/hj_page/community/group_chat/detail/widget/chat_item_cell.dart create mode 100644 lib/hj_page/community/group_chat/group_chat_logic.dart create mode 100644 lib/hj_page/community/group_chat/group_chat_page.dart create mode 100644 lib/hj_page/community/group_chat/model/im_group_list_model.dart create mode 100644 lib/hj_page/community/group_chat/model/im_message_resp.dart create mode 100644 lib/hj_page/community/group_chat/widget/chat_group_cell.dart create mode 100644 lib/hj_page/community/post_detail_page/detail_widget/post_detail_cover_content.dart create mode 100644 lib/hj_page/community/post_detail_page/detail_widget/post_detail_operation_view.dart create mode 100644 lib/hj_page/community/post_detail_page/detail_widget/post_detail_unlock_dialog.dart create mode 100644 lib/hj_page/community/post_detail_page/detail_widget/post_detail_word_content.dart create mode 100644 lib/hj_page/community/post_detail_page/detail_widget/post_resource_info_widget.dart create mode 100644 lib/hj_page/community/post_detail_page/post_detail_logic.dart create mode 100644 lib/hj_page/community/post_detail_page/post_detail_page.dart create mode 100644 lib/hj_page/community/publish_page/chose_tag_logic.dart create mode 100644 lib/hj_page/community/publish_page/chose_tag_page.dart create mode 100644 lib/hj_page/community/publish_page/publish_logic.dart create mode 100644 lib/hj_page/community/publish_page/publish_page.dart create mode 100644 lib/hj_page/community/publish_page/publish_rule_page.dart create mode 100644 lib/hj_page/community/publish_page/widget/publish_widget.dart create mode 100644 lib/hj_page/community/publish_page/widget/video_cover_widget.dart create mode 100644 lib/hj_page/community/widget/community_middle_header.dart create mode 100644 lib/hj_page/community/widget/community_post_widget.dart create mode 100644 lib/hj_page/community/widget/community_sort_header.dart create mode 100644 lib/hj_page/community/widget/follow_loading_button.dart create mode 100644 lib/hj_page/community/widget/pic_simple_cell.dart create mode 100644 lib/hj_page/community/widget/post_user_info_view.dart create mode 100644 lib/hj_page/community/widget/post_word_rich_text.dart create mode 100644 lib/hj_page/drama/drama_detail_logic.dart create mode 100644 lib/hj_page/drama/drama_detail_page.dart create mode 100644 lib/hj_page/drama/drama_feed_logic.dart create mode 100644 lib/hj_page/drama/drama_feed_page.dart create mode 100644 lib/hj_page/drama/drama_list_logic.dart create mode 100644 lib/hj_page/drama/drama_list_page.dart create mode 100644 lib/hj_page/drama/drama_tag_detail_logic.dart create mode 100644 lib/hj_page/drama/drama_tag_detail_page.dart create mode 100644 lib/hj_page/drama/view/drama_bottom_bar.dart create mode 100644 lib/hj_page/drama/view/drama_episode_sheet.dart create mode 100644 lib/hj_page/drama/view/drama_menu.dart create mode 100644 lib/hj_page/drama/view/drama_paywall_view.dart create mode 100644 lib/hj_page/drama/view/drama_speed_sheet.dart create mode 100644 lib/hj_page/drama/view/drama_video_player.dart create mode 100644 lib/hj_page/drama/view/drama_video_player_logic.dart create mode 100644 lib/hj_page/find/acg_game_detail_logic.dart create mode 100644 lib/hj_page/find/acg_game_detail_page.dart create mode 100644 lib/hj_page/find/acg_game_recommend_logic.dart create mode 100644 lib/hj_page/find/acg_game_recommend_page.dart create mode 100644 lib/hj_page/find/pic_collect/album_action_widget.dart create mode 100644 lib/hj_page/find/pic_collect/pics_detail_logic.dart create mode 100644 lib/hj_page/find/pic_collect/pics_detail_page.dart create mode 100644 lib/hj_page/find/rank_module/acg_rank_item.dart create mode 100644 lib/hj_page/find/rank_module/image_rank_item.dart create mode 100644 lib/hj_page/find/rank_module/rank_badge.dart create mode 100644 lib/hj_page/find/rank_module/rank_main_logic.dart create mode 100644 lib/hj_page/find/rank_module/rank_main_page.dart create mode 100644 lib/hj_page/find/rank_module/rank_sort_tab_bar.dart create mode 100644 lib/hj_page/find/rank_module/rank_sub_logic.dart create mode 100644 lib/hj_page/find/rank_module/rank_sub_page.dart create mode 100644 lib/hj_page/find/rank_module/rank_type_tab_view.dart create mode 100644 lib/hj_page/find/rank_module/video_rank_item.dart create mode 100644 lib/hj_page/home/activity_float/home_activity_float.dart create mode 100644 lib/hj_page/home/activity_float/home_float_banner.dart create mode 100644 lib/hj_page/home/activity_float/home_float_widget.dart create mode 100644 lib/hj_page/home/activity_float/swiper_floating_widget.dart create mode 100644 lib/hj_page/home/dark_home_page.dart create mode 100644 lib/hj_page/home/home_cell_style/divider_tab_bar.dart create mode 100644 lib/hj_page/home/home_cell_style/drama_banner.dart create mode 100644 lib/hj_page/home/home_cell_style/drama_cover_card.dart create mode 100644 lib/hj_page/home/home_cell_style/guess_like_sliver.dart create mode 100644 lib/hj_page/home/home_cell_style/home_section_cell.dart create mode 100644 lib/hj_page/home/home_cell_style/home_section_title.dart create mode 100644 lib/hj_page/home/home_cell_style/level_mark_chip.dart create mode 100644 lib/hj_page/home/home_cell_style/quick_entry_row.dart create mode 100644 lib/hj_page/home/home_cell_style/tiktok_simple_cell.dart create mode 100644 lib/hj_page/home/home_cell_style/video_hor_cell.dart create mode 100644 lib/hj_page/home/home_cell_style/video_simple_cell.dart create mode 100644 lib/hj_page/home/home_drawer.dart create mode 100644 lib/hj_page/home/home_drawer_logic.dart create mode 100644 lib/hj_page/home/home_main_logic.dart create mode 100644 lib/hj_page/home/home_main_page.dart create mode 100644 lib/hj_page/home/home_sub_module/home_tab_base_logic.dart create mode 100644 lib/hj_page/home/home_sub_module/home_tab_drama_logic.dart create mode 100644 lib/hj_page/home/home_sub_module/home_tab_drama_page.dart create mode 100644 lib/hj_page/home/home_sub_module/home_tab_section_logic.dart create mode 100644 lib/hj_page/home/home_sub_module/home_tab_section_page.dart create mode 100644 lib/hj_page/home/home_sub_module/home_tab_sort_logic.dart create mode 100644 lib/hj_page/home/home_sub_module/home_tab_sort_page.dart create mode 100644 lib/hj_page/home/home_sub_module/widget/sort_header.dart create mode 100644 lib/hj_page/home/home_sub_module/widget/special_topics_view.dart create mode 100644 lib/hj_page/home/provider/home_update_marker_provider.dart create mode 100644 lib/hj_page/home/search_page/search_main_logic.dart create mode 100644 lib/hj_page/home/search_page/search_main_page.dart create mode 100644 lib/hj_page/home/search_page/search_result_page/search_acg_result_page.dart create mode 100644 lib/hj_page/home/search_page/search_result_page/search_result_main_page.dart create mode 100644 lib/hj_page/home/search_page/search_result_page/search_video_result_page.dart create mode 100644 lib/hj_page/home/search_page/video_all_type_logic.dart create mode 100644 lib/hj_page/home/search_page/video_all_type_page.dart create mode 100644 lib/hj_page/home/search_page/widget/common_search_widget.dart create mode 100644 lib/hj_page/home/search_page/widget/search_app_bar.dart create mode 100644 lib/hj_page/home/search_page/widget/search_history_view.dart create mode 100644 lib/hj_page/home/search_page/widget/search_hot_tag_view.dart create mode 100644 lib/hj_page/home/search_page/widget/topic_item.dart create mode 100644 lib/hj_page/home/section_all_page/section_all_logic.dart create mode 100644 lib/hj_page/home/section_all_page/section_all_page.dart create mode 100644 lib/hj_page/home/special_topic_detail/special_topics_detail_logic.dart create mode 100644 lib/hj_page/home/special_topic_detail/special_topics_detail_page.dart create mode 100644 lib/hj_page/home/tag/cartoon_tag_logic.dart create mode 100644 lib/hj_page/home/tag/cartoon_tag_page.dart create mode 100644 lib/hj_page/home/tag/video_tag_logic.dart create mode 100644 lib/hj_page/home/tag/video_tag_page.dart create mode 100644 lib/hj_page/home/widget/collect_status_wrapper.dart create mode 100644 lib/hj_page/home/widget/home_sort_menu_view.dart create mode 100644 lib/hj_page/home/widget/home_txt_marquee.dart create mode 100644 lib/hj_page/home/widget/publist_entry_alert.dart create mode 100644 lib/hj_page/home/widget/quick_search_view.dart create mode 100644 lib/hj_page/home/widget/support_up_alert.dart create mode 100644 lib/hj_page/home/widget/user_avatar.dart create mode 100644 lib/hj_page/home/widget/user_name_view.dart create mode 100644 lib/hj_page/live/live_detail_logic.dart create mode 100644 lib/hj_page/live/live_detail_page.dart create mode 100644 lib/hj_page/live/live_item_widget.dart create mode 100644 lib/hj_page/live/live_main_logic.dart create mode 100644 lib/hj_page/live/live_main_page.dart create mode 100644 lib/hj_page/live/live_model.dart create mode 100644 lib/hj_page/live/live_service.dart create mode 100644 lib/hj_page/live/live_sub_logic.dart create mode 100644 lib/hj_page/live/live_sub_page.dart create mode 100644 lib/hj_page/live/live_widget.dart create mode 100644 lib/hj_page/main_page/drama_tip_bubble.dart create mode 100644 lib/hj_page/main_page/main_logic.dart create mode 100644 lib/hj_page/main_page/main_page.dart create mode 100644 lib/hj_page/main_page/main_tabbar.dart create mode 100644 lib/hj_page/main_page/provider/bottom_bar_provider.dart create mode 100644 lib/hj_page/main_page/provider/msg_provider.dart create mode 100644 lib/hj_page/mine/collec_history_buy/col_his_buy_logic.dart create mode 100644 lib/hj_page/mine/collec_history_buy/col_his_buy_page.dart create mode 100644 lib/hj_page/mine/collec_history_buy/col_his_buy_sub_logic.dart create mode 100644 lib/hj_page/mine/collec_history_buy/col_his_buy_sub_page.dart create mode 100644 lib/hj_page/mine/home_mine_logic.dart create mode 100644 lib/hj_page/mine/home_mine_page.dart create mode 100644 lib/hj_page/mine/identity/mine_identity_logic.dart create mode 100644 lib/hj_page/mine/identity/mine_identity_page.dart create mode 100644 lib/hj_page/mine/make_money/alipay_ccdcapi_model.dart create mode 100644 lib/hj_page/mine/make_money/in_come_entity.dart create mode 100644 lib/hj_page/mine/make_money/mine_withdrawal_record_logic.dart create mode 100644 lib/hj_page/mine/make_money/mine_withdrawal_record_page.dart create mode 100644 lib/hj_page/mine/make_money/widget/record_list_item.dart create mode 100644 lib/hj_page/mine/make_money/withdraw_details_model.dart create mode 100644 lib/hj_page/mine/make_money/withdrawal_logic.dart create mode 100644 lib/hj_page/mine/make_money/withdrawal_page.dart create mode 100644 lib/hj_page/mine/message/message_center_logic.dart create mode 100644 lib/hj_page/mine/message/message_center_page.dart create mode 100644 lib/hj_page/mine/message/message_center_sub_controller.dart create mode 100644 lib/hj_page/mine/message/message_center_sub_page.dart create mode 100644 lib/hj_page/mine/message/un_read_msg_num_model.dart create mode 100644 lib/hj_page/mine/message/widget/message_items.dart create mode 100644 lib/hj_page/mine/mine_drive/mine_drive_logic.dart create mode 100644 lib/hj_page/mine/mine_drive/mine_drive_page.dart create mode 100644 lib/hj_page/mine/mine_following/mine_follow_sub_logic.dart create mode 100644 lib/hj_page/mine/mine_following/mine_follow_sub_page.dart create mode 100644 lib/hj_page/mine/mine_following/mine_following_view.dart create mode 100644 lib/hj_page/mine/mine_following/widget/follow_list_items.dart create mode 100644 lib/hj_page/mine/mine_gesture_pwd/model/point_item.dart create mode 100644 lib/hj_page/mine/mine_gesture_pwd/widget/gesture_password_widget.dart create mode 100644 lib/hj_page/mine/mine_gesture_pwd/widget/line_painter.dart create mode 100644 lib/hj_page/mine/mine_post/mine_publish_main_logic.dart create mode 100644 lib/hj_page/mine/mine_post/mine_publish_main_page.dart create mode 100644 lib/hj_page/mine/mine_post/mine_publish_sub_logic.dart create mode 100644 lib/hj_page/mine/mine_post/mine_publish_sub_page.dart create mode 100644 lib/hj_page/mine/mine_profit/bank_card_home_page.dart create mode 100644 lib/hj_page/mine/mine_profit/model/alipay_bank_list_model.dart create mode 100644 lib/hj_page/mine/mine_profit/model/bankcard_info.dart create mode 100644 lib/hj_page/mine/mine_profit/widget/add_bank_dialog.dart create mode 100644 lib/hj_page/mine/mine_setting/bind_phone_logic.dart create mode 100644 lib/hj_page/mine/mine_setting/bing_phone_page.dart create mode 100644 lib/hj_page/mine/mine_setting/invite_bind_logic.dart create mode 100644 lib/hj_page/mine/mine_setting/invite_bind_page.dart create mode 100644 lib/hj_page/mine/mine_setting/mine_code_logic.dart create mode 100644 lib/hj_page/mine/mine_setting/mine_code_page.dart create mode 100644 lib/hj_page/mine/mine_setting/mine_find_account_page.dart create mode 100644 lib/hj_page/mine/mine_setting/mine_password_logic.dart create mode 100644 lib/hj_page/mine/mine_setting/mine_password_page.dart create mode 100644 lib/hj_page/mine/mine_setting/mine_scan_login_logic.dart create mode 100644 lib/hj_page/mine/mine_setting/mine_scan_login_page.dart create mode 100644 lib/hj_page/mine/mine_setting/mine_setting_page.dart create mode 100644 lib/hj_page/mine/mine_setting/mine_setting_profile_logic.dart create mode 100644 lib/hj_page/mine/mine_setting/mine_setting_profile_page.dart create mode 100644 lib/hj_page/mine/mine_setting/setting_avatar_logic.dart create mode 100644 lib/hj_page/mine/mine_setting/setting_avatar_page.dart create mode 100644 lib/hj_page/mine/mine_setting/setting_profile_widget.dart create mode 100644 lib/hj_page/mine/mine_share/mine_share_page.dart create mode 100644 lib/hj_page/mine/mine_share/mine_share_record_logic.dart create mode 100644 lib/hj_page/mine/mine_share/mine_share_record_page.dart create mode 100644 lib/hj_page/mine/mine_video_cache/video_cache_cell.dart create mode 100644 lib/hj_page/mine/mine_video_cache/video_cache_logic.dart create mode 100644 lib/hj_page/mine/mine_video_cache/video_cache_page.dart create mode 100644 lib/hj_page/mine/mine_vip/charge_user_header.dart create mode 100644 lib/hj_page/mine/mine_vip/coupon_model.dart create mode 100644 lib/hj_page/mine/mine_vip/mine_charge_coin_logic.dart create mode 100644 lib/hj_page/mine/mine_vip/mine_charge_coin_page.dart create mode 100644 lib/hj_page/mine/mine_vip/mine_charge_vip_logic.dart create mode 100644 lib/hj_page/mine/mine_vip/mine_charge_vip_page.dart create mode 100644 lib/hj_page/mine/mine_vip/online_pay_page.dart create mode 100644 lib/hj_page/mine/mine_vip/pay_order_source.dart create mode 100644 lib/hj_page/mine/mine_vip/pay_success_alert.dart create mode 100644 lib/hj_page/mine/mine_vip/vip_card_analytics.dart create mode 100644 lib/hj_page/mine/mine_vip/vip_card_item.dart create mode 100644 lib/hj_page/mine/mine_vip/vip_pay_button.dart create mode 100644 lib/hj_page/mine/mine_vip/vip_product_manager.dart create mode 100644 lib/hj_page/mine/mine_vip/vip_support_model.dart create mode 100644 lib/hj_page/mine/mine_vip/vip_ui_kit.dart create mode 100644 lib/hj_page/mine/mine_vip/widgets/coin_item.dart create mode 100644 lib/hj_page/mine/mine_vip/widgets/coin_pay_bottom_sheet.dart create mode 100644 lib/hj_page/mine/more_question/feedback_question_category_view.dart create mode 100644 lib/hj_page/mine/more_question/mine_feedback_logic.dart create mode 100644 lib/hj_page/mine/more_question/mine_feedback_page.dart create mode 100644 lib/hj_page/mine/more_question/mine_qa_page.dart create mode 100644 lib/hj_page/mine/more_question/photo_manage_view.dart create mode 100644 lib/hj_page/mine/welfare/lou_feng_ad_logic.dart create mode 100644 lib/hj_page/mine/welfare/lou_feng_ad_page.dart create mode 100644 lib/hj_page/mine/welfare/mine_income_page.dart create mode 100644 lib/hj_page/mine/welfare/more_free_video_logic.dart create mode 100644 lib/hj_page/mine/welfare/more_free_video_page.dart create mode 100644 lib/hj_page/mine/welfare/share_data_list_page.dart create mode 100644 lib/hj_page/mine/welfare/share_details_page.dart create mode 100644 lib/hj_page/mine/welfare/sign_daily_logic.dart create mode 100644 lib/hj_page/mine/welfare/sign_daily_page.dart create mode 100644 lib/hj_page/mine/welfare/welfare_home_page.dart create mode 100644 lib/hj_page/mine/welfare/widget/checkin_model.dart create mode 100644 lib/hj_page/mine/welfare/widget/congratulations_reward_dialog.dart create mode 100644 lib/hj_page/mine/welfare/widget/custom_progressbar.dart create mode 100644 lib/hj_page/mine/welfare/widget/mine_sign_logic.dart create mode 100644 lib/hj_page/mine/welfare/widget/recommend_app_page.dart create mode 100644 lib/hj_page/mine/welfare/widget/sign_dialog.dart create mode 100644 lib/hj_page/mine/welfare/widget/sign_in_model.dart create mode 100644 lib/hj_page/mine/welfare/widget/task_widgets.dart create mode 100644 lib/hj_page/mine/widgets/exchange_vip_page.dart create mode 100644 lib/hj_page/mine/widgets/gradient_text.dart create mode 100644 lib/hj_page/mine/widgets/integral_exchange_logic.dart create mode 100644 lib/hj_page/mine/widgets/integral_exchange_page.dart create mode 100644 lib/hj_page/mine/widgets/integral_record_logic.dart create mode 100644 lib/hj_page/mine/widgets/mine_info_widget.dart create mode 100644 lib/hj_page/mine/widgets/mine_share_qr_view.dart create mode 100644 lib/hj_page/mine/widgets/stage_cell_widget.dart create mode 100644 lib/hj_page/mine/widgets/vip_entry_card.dart create mode 100644 lib/hj_page/pre_sale/limit_time_provider.dart create mode 100644 lib/hj_page/pre_sale/pre_sale_alert.dart create mode 100644 lib/hj_page/pre_sale/pre_sale_entry.dart create mode 100644 lib/hj_page/pre_sale/pre_sale_logic.dart create mode 100644 lib/hj_page/pre_sale/pre_sale_model.dart create mode 100644 lib/hj_page/pre_sale/pre_sale_page.dart create mode 100644 lib/hj_page/pre_sale/pre_sale_provider.dart create mode 100644 lib/hj_page/pre_sale/pre_sale_service.dart create mode 100644 lib/hj_page/short_video/short_main_video_logic.dart create mode 100644 lib/hj_page/short_video/short_main_video_page.dart create mode 100644 lib/hj_page/short_video/short_video_logic.dart create mode 100644 lib/hj_page/short_video/short_video_page.dart create mode 100644 lib/hj_page/short_video/view/feed_ad_view.dart create mode 100644 lib/hj_page/short_video/view/float_ad_view.dart create mode 100644 lib/hj_page/short_video/view/media_action_menu.dart create mode 100644 lib/hj_page/short_video/view/short_top_menu.dart create mode 100644 lib/hj_page/short_video/view/short_video_menu.dart create mode 100644 lib/hj_page/short_video/view/short_video_player.dart create mode 100644 lib/hj_page/short_video/view/short_video_player_logic.dart create mode 100644 lib/hj_page/short_video/view/video_menu_kit.dart create mode 100644 lib/hj_page/short_video/view/video_player_base_logic.dart create mode 100644 lib/hj_page/short_video/view/video_progress_widget.dart create mode 100644 lib/hj_page/splash/splash_ad_view.dart create mode 100644 lib/hj_page/splash/splash_logic.dart create mode 100644 lib/hj_page/splash/splash_page.dart create mode 100644 lib/hj_page/user_center_page/user_center_logic.dart create mode 100644 lib/hj_page/user_center_page/user_center_page.dart create mode 100644 lib/hj_page/user_center_page/view/user_work_list_logic.dart create mode 100644 lib/hj_page/user_center_page/view/user_work_list_view.dart create mode 100644 lib/hj_page/user_center_page/widget/user_center_header.dart create mode 100644 lib/hj_page/video/cartoon/cartoon_episode_menu_alert.dart create mode 100644 lib/hj_page/video/cartoon/video_detail_cartoon_view.dart create mode 100644 lib/hj_page/video/simple_video_player_logic.dart create mode 100644 lib/hj_page/video/simple_video_player_page.dart create mode 100644 lib/hj_page/video/video_full_logic.dart create mode 100644 lib/hj_page/video/video_full_page.dart create mode 100644 lib/hj_page/video/video_logic.dart create mode 100644 lib/hj_page/video/video_page.dart create mode 100644 lib/hj_page/video/video_player_alert.dart create mode 100644 lib/hj_page/video/view/long_video_status.dart create mode 100644 lib/hj_page/video/view/video_ad_widget.dart create mode 100644 lib/hj_page/video/view/video_detail_bottom_menu.dart create mode 100644 lib/hj_page/video/view/video_detail_view.dart create mode 100644 lib/hj_page/video/view/video_mask_buy_view.dart create mode 100644 lib/hj_page/video/view/video_menu_view.dart create mode 100644 lib/hj_page/video/view/video_status_view.dart create mode 100644 lib/hj_page/video/view/video_tabbar_menu_widget.dart create mode 100644 lib/hj_page/video/view/vip_promo_banner.dart create mode 100644 lib/hj_page/web_page/h5_logic.dart create mode 100644 lib/hj_page/web_page/h5_page.dart create mode 100644 lib/hj_page/web_page/h5_webview_settings.dart create mode 100644 lib/hj_utils/api_service/acg_service.dart create mode 100644 lib/hj_utils/api_service/ai_service.dart create mode 100644 lib/hj_utils/api_service/buy_service.dart create mode 100644 lib/hj_utils/api_service/comment_service.dart create mode 100644 lib/hj_utils/api_service/common_service.dart create mode 100644 lib/hj_utils/api_service/drama_service.dart create mode 100644 lib/hj_utils/api_service/group_service.dart create mode 100644 lib/hj_utils/api_service/mine_service.dart create mode 100644 lib/hj_utils/api_service/search_service.dart create mode 100644 lib/hj_utils/api_service/tag_service.dart create mode 100644 lib/hj_utils/api_service/vid_service.dart create mode 100644 lib/hj_utils/buy_util.dart create mode 100644 lib/hj_utils/codec_support.dart create mode 100644 lib/hj_utils/const.dart create mode 100644 lib/hj_utils/date_time_util.dart create mode 100644 lib/hj_utils/detect_line_manager.dart create mode 100644 lib/hj_utils/dns_solve/_answer.dart create mode 100644 lib/hj_utils/dns_solve/_dnsolve.dart create mode 100644 lib/hj_utils/dns_solve/_question.dart create mode 100644 lib/hj_utils/dns_solve/_response.dart create mode 100644 lib/hj_utils/dns_solve/dnsolve.dart create mode 100644 lib/hj_utils/dns_solve/exception.dart create mode 100644 lib/hj_utils/file_util.dart create mode 100644 lib/hj_utils/free_play_manager.dart create mode 100644 lib/hj_utils/history_util.dart create mode 100644 lib/hj_utils/holiday_theme.dart create mode 100644 lib/hj_utils/image_util.dart create mode 100644 lib/hj_utils/install_util.dart create mode 100644 lib/hj_utils/light_model.dart create mode 100644 lib/hj_utils/local_server.dart create mode 100644 lib/hj_utils/local_server_guard.dart create mode 100644 lib/hj_utils/media_info.dart create mode 100644 lib/hj_utils/pay/pay_manager.dart create mode 100644 lib/hj_utils/permission_util.dart create mode 100644 lib/hj_utils/play_diagnose.dart create mode 100644 lib/hj_utils/screen.dart create mode 100644 lib/hj_utils/sliver_delegate.dart create mode 100644 lib/hj_utils/store_keys.dart create mode 100644 lib/hj_utils/text_util.dart create mode 100644 lib/hj_utils/version_util.dart create mode 100644 lib/hj_utils/video_cache_manager.dart create mode 100644 lib/hj_utils/video_view_type.dart create mode 100644 lib/hj_utils/widget_util.dart create mode 100644 lib/hj_utils/widget_utils.dart create mode 100644 lib/im_manager/api/im_api_service.dart create mode 100644 lib/im_manager/chat_detail_page/Im_ad_view.dart create mode 100644 lib/im_manager/chat_detail_page/chat_detail_logic.dart create mode 100644 lib/im_manager/chat_detail_page/chat_detail_page.dart create mode 100644 lib/im_manager/chat_detail_page/chat_session_list_logic.dart create mode 100644 lib/im_manager/chat_detail_page/chat_session_list_view.dart create mode 100644 lib/im_manager/chat_detail_page/widget/chat_item_cell.dart create mode 100644 lib/im_manager/chat_detail_page/widget/input_foot_view.dart create mode 100644 lib/im_manager/chat_detail_page/widget/input_sheet_view.dart create mode 100644 lib/im_manager/im_ad/im_ad_item.dart create mode 100644 lib/im_manager/im_ad/im_ad_manager.dart create mode 100644 lib/im_manager/im_ad/im_ad_provider.dart create mode 100644 lib/im_manager/im_manager.dart create mode 100644 lib/im_manager/im_notification.dart create mode 100644 lib/im_manager/model/im_message_resp.dart create mode 100644 lib/im_manager/model/im_user_model.dart create mode 100644 lib/main.dart create mode 100644 lib/routers/jump_router.dart create mode 100644 lib/routers/router_map.dart create mode 100644 lib/tools_base/ad_manager.dart create mode 100644 lib/tools_base/banner/ads_banner.dart create mode 100644 lib/tools_base/banner/ads_grid_view.dart create mode 100644 lib/tools_base/banner/ads_item.dart create mode 100644 lib/tools_base/base_list_controller.dart create mode 100644 lib/tools_base/cache/cache_util.dart create mode 100644 lib/tools_base/cache/cancel_token_manager.dart create mode 100644 lib/tools_base/cache/dio_file_service.dart create mode 100644 lib/tools_base/cache/history/base_history_record_store.dart create mode 100644 lib/tools_base/cache/history/cartoon_history_store.dart create mode 100644 lib/tools_base/cache/history/comics_history_store.dart create mode 100644 lib/tools_base/cache/history/community_history_store.dart create mode 100644 lib/tools_base/cache/history/drama_resume_store.dart create mode 100644 lib/tools_base/cache/history/game_history_store.dart create mode 100644 lib/tools_base/cache/history/novel_history_store.dart create mode 100644 lib/tools_base/cache/history/pic_history_store.dart create mode 100644 lib/tools_base/cache/history/search_history_store.dart create mode 100644 lib/tools_base/cache/history/short_video_history_store.dart create mode 100644 lib/tools_base/cache/history/video_history_store.dart create mode 100644 lib/tools_base/cache/image_cache_manager.dart create mode 100644 lib/tools_base/debug_log.dart create mode 100644 lib/tools_base/event_bus/event_bus_util.dart create mode 100644 lib/tools_base/event_bus/events.dart create mode 100644 lib/tools_base/file_upload/file_upload_tool.dart create mode 100644 lib/tools_base/file_upload/upload_result_model.dart create mode 100644 lib/tools_base/global_store/store.dart create mode 100644 lib/tools_base/headphone_monitor.dart create mode 100644 lib/tools_base/image/image_crypto.dart create mode 100644 lib/tools_base/image/image_disk_cache.dart create mode 100644 lib/tools_base/indicator/custom_tab_indicator.dart create mode 100644 lib/tools_base/loading/loading_alert_widget.dart create mode 100644 lib/tools_base/loading/loading_center_widget.dart create mode 100644 lib/tools_base/loading/loading_helper.dart create mode 100644 lib/tools_base/module_sort_manager.dart create mode 100644 lib/tools_base/net/aes_util.dart create mode 100644 lib/tools_base/net/api_exception.dart create mode 100644 lib/tools_base/net/base_resp_bean.dart create mode 100644 lib/tools_base/net/curl_util.dart create mode 100644 lib/tools_base/net/http_manager.dart create mode 100755 lib/tools_base/net/http_resp_interceptor.dart create mode 100644 lib/tools_base/net/http_response_interceptor.dart create mode 100644 lib/tools_base/net/load_apk/dio_cli.dart create mode 100644 lib/tools_base/net/load_apk/dio_slice_downloader.dart create mode 100644 lib/tools_base/net/load_apk/e_data.dart create mode 100755 lib/tools_base/net/net_code.dart create mode 100644 lib/tools_base/net/net_manager.dart create mode 100644 lib/tools_base/refresh/horizontal_load_more.dart create mode 100644 lib/tools_base/refresh/pull_refresh.dart create mode 100644 lib/tools_base/richTextParsing/html_parser.dart create mode 100644 lib/tools_base/richTextParsing/html_pro_parser.dart create mode 100644 lib/tools_base/toast.dart create mode 100644 lib/tools_base/unique_tag_mixin.dart create mode 100644 lib/tools_base/video_download/download_button.dart create mode 100644 lib/tools_base/video_download/ios_video_downloader.dart create mode 100644 lib/tools_base/video_download/media_download_manager.dart create mode 100644 lib/tools_base/video_download/video_cache_store.dart create mode 100644 lib/tools_base/video_download/video_download_manager.dart create mode 100644 lib/tools_base/video_download/video_save_util.dart create mode 100644 lib/tools_base/widget/add_media_source_button.dart create mode 100644 lib/tools_base/widget/card_swiper/card_swiper.dart create mode 100644 lib/tools_base/widget/card_swiper/src/custom_layout.dart create mode 100644 lib/tools_base/widget/card_swiper/src/flutter_page_indicator/flutter_page_indicator.dart create mode 100644 lib/tools_base/widget/card_swiper/src/swiper.dart create mode 100644 lib/tools_base/widget/card_swiper/src/swiper_control.dart create mode 100644 lib/tools_base/widget/card_swiper/src/swiper_controller.dart create mode 100644 lib/tools_base/widget/card_swiper/src/swiper_pagination.dart create mode 100644 lib/tools_base/widget/card_swiper/src/swiper_plugin.dart create mode 100644 lib/tools_base/widget/card_swiper/src/transformer_page_view/index_controller.dart create mode 100644 lib/tools_base/widget/card_swiper/src/transformer_page_view/transformer_page_view.dart create mode 100644 lib/tools_base/widget/common_alert.dart create mode 100644 lib/tools_base/widget/common_dialog.dart create mode 100644 lib/tools_base/widget/fly_to_overlay.dart create mode 100644 lib/tools_base/widget/follow_button.dart create mode 100644 lib/tools_base/widget/group_text_filed.dart create mode 100644 lib/tools_base/widget/header_widget.dart create mode 100644 lib/tools_base/widget/image_browser_page.dart create mode 100644 lib/tools_base/widget/keep_alive_widget.dart create mode 100644 lib/tools_base/widget/lazy_indexed_stack.dart create mode 100644 lib/tools_base/widget/like_burst_view.dart create mode 100644 lib/tools_base/widget/marquee_widget.dart create mode 100644 lib/tools_base/widget/multitap_recognizer.dart create mode 100644 lib/tools_base/widget/net_image_widget.dart create mode 100644 lib/tools_base/widget/scroll_positioned_list/src/element_registry.dart create mode 100644 lib/tools_base/widget/scroll_positioned_list/src/item_positions_listener.dart create mode 100644 lib/tools_base/widget/scroll_positioned_list/src/item_positions_notifier.dart create mode 100644 lib/tools_base/widget/scroll_positioned_list/src/positioned_list.dart create mode 100644 lib/tools_base/widget/scroll_positioned_list/src/post_mount_callback.dart create mode 100644 lib/tools_base/widget/scroll_positioned_list/src/scroll_offset_listener.dart create mode 100644 lib/tools_base/widget/scroll_positioned_list/src/scroll_offset_notifier.dart create mode 100644 lib/tools_base/widget/scroll_positioned_list/src/scroll_view.dart create mode 100644 lib/tools_base/widget/scroll_positioned_list/src/scrollable_positioned_list.dart create mode 100644 lib/tools_base/widget/scroll_positioned_list/src/viewport.dart create mode 100644 lib/tools_base/widget/scroll_positioned_list/src/wrapping.dart create mode 100644 lib/tools_base/widget/sheet_handle_bar.dart create mode 100644 lib/tools_base/widget/shrink_wrap.dart create mode 100644 lib/tools_base/widget/stagger_in_item.dart create mode 100644 lib/tools_base/widget/swipe_action_item.dart create mode 100644 lib/track_event_manager/device_service.dart create mode 100644 lib/track_event_manager/track_manager.dart create mode 100644 lib/track_event_manager/track_session.dart create mode 100644 m3u8_downloader-master/LICENSE create mode 100644 m3u8_downloader-master/README.md create mode 100644 m3u8_downloader-master/android/build.gradle create mode 100644 m3u8_downloader-master/android/gradle.properties create mode 100644 m3u8_downloader-master/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 m3u8_downloader-master/android/settings.gradle create mode 100644 m3u8_downloader-master/android/src/main/AndroidManifest.xml create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/FlutterBackgroundExecutor.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/M3U8DownloaderPlugin.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/bean/M3U8.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/bean/M3U8Task.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/bean/M3U8TaskState.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/bean/M3U8Ts.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/downloader/M3U8DownloadConfig.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/downloader/M3U8DownloadTask.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/downloader/M3U8Downloader.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/downloader/WeakHandler.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/listener/OnInfoCallback.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/listener/OnM3U8DownloadListener.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/listener/OnTaskDownloadListener.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/utils/EncryptUtil.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/utils/M3U8Log.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/utils/M3U8Util.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/utils/NotificationUtil.java create mode 100644 m3u8_downloader-master/android/src/main/java/com/vincent/m3u8Downloader/utils/SpHelper.java create mode 100644 m3u8_downloader-master/example/README.md create mode 100644 m3u8_downloader-master/example/android/app/build.gradle create mode 100644 m3u8_downloader-master/example/android/app/src/debug/AndroidManifest.xml create mode 100644 m3u8_downloader-master/example/android/app/src/main/AndroidManifest.xml create mode 100644 m3u8_downloader-master/example/android/app/src/main/java/com/vincent/m3u8_downloader_example/MainActivity.java create mode 100644 m3u8_downloader-master/example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 m3u8_downloader-master/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 m3u8_downloader-master/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 m3u8_downloader-master/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 m3u8_downloader-master/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 m3u8_downloader-master/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 m3u8_downloader-master/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 m3u8_downloader-master/example/android/app/src/main/res/values-night/styles.xml create mode 100644 m3u8_downloader-master/example/android/app/src/main/res/values/styles.xml create mode 100644 m3u8_downloader-master/example/android/app/src/profile/AndroidManifest.xml create mode 100644 m3u8_downloader-master/example/android/build.gradle create mode 100644 m3u8_downloader-master/example/android/gradle.properties create mode 100644 m3u8_downloader-master/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 m3u8_downloader-master/example/android/settings.gradle create mode 100644 m3u8_downloader-master/example/lib/main.dart create mode 100644 m3u8_downloader-master/example/lib/video_player_page.dart create mode 100644 m3u8_downloader-master/example/pubspec.yaml create mode 100644 m3u8_downloader-master/example/test/widget_test.dart create mode 100644 m3u8_downloader-master/ios/Classes/M3u8DownloaderPlugin.swift create mode 100644 m3u8_downloader-master/ios/m3u8_downloader.podspec create mode 100644 m3u8_downloader-master/lib/callback_dispatcher.dart create mode 100644 m3u8_downloader-master/lib/m3u8_downloader.dart create mode 100644 m3u8_downloader-master/pubspec.yaml create mode 100644 m3u8_downloader-master/test/m3u8_downloader_test.dart create mode 100644 project.md create mode 100644 pubspec.yaml create mode 100644 test/widget_test.dart create mode 100644 third_party/flutter_native_image/lib/flutter_native_image.dart create mode 100644 third_party/flutter_native_image/pubspec.yaml create mode 100644 third_party/image_gallery_saver/lib/image_gallery_saver.dart create mode 100644 third_party/image_gallery_saver/pubspec.yaml create mode 100644 third_party/image_pickers/lib/image_pickers.dart create mode 100644 third_party/image_pickers/pubspec.yaml diff --git a/.fvmrc b/.fvmrc new file mode 100644 index 0000000..a06c1db --- /dev/null +++ b/.fvmrc @@ -0,0 +1,3 @@ +{ + "flutter": "3.38.3" +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..98573fd --- /dev/null +++ b/.gitignore @@ -0,0 +1,193 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +.properties +.flutter-plugins +.packages +.lock +.bin +pubspec.look +local.properties +.packages +.flutter-plugins +searchFile.js + + + +.packages +.metadata +.fvm/ +# VSCode 工作区配置纳入版本管理(settings.json 里的排除规则要团队共用), +# 其余个人化/临时文件仍然忽略。 +# 注意:全局 ~/.gitignore_global 也忽略了这两个文件,靠项目级白名单覆盖 +.vscode/* +!.vscode/settings.json +!.vscode/launch.json +!.vscode/extensions.json +zaixiankefu/pubspec.lock +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/* +.flutter-plugins +.packages +.pub-cache/ +.pub/ +/build/ + + +# Android related +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java +**/android/key.properties +*.jks +*.jks.old +**/android/build/ + + +# 针对flutter当做module集成进现有项目时,.android目录下的文件 +**/.android/**/gradle-wrapper.jar +**/.android/.gradle +**/.android/captures/ +**/.android/gradlew +**/.android/gradlew.bat +**/.android/local.properties +**/.android/**/GeneratedPluginRegistrant.java +**/.android/key.properties +**/*.jks + + + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + + +# 针对flutter当做module集成进现有项目时,.ios目录下的文件 +**/.ios/**/*.mode1v3 +**/.ios/**/*.mode2v3 +**/.ios/**/*.moved-aside +**/.ios/**/*.pbxuser +**/.ios/**/*.perspectivev3 +**/.ios/**/*sync/ +**/.ios/**/.sconsign.dblite +**/.ios/**/.tags* +**/.ios/**/.vagrant/ +**/.ios/**/DerivedData/ +**/.ios/**/Icon? +**/.ios/**/Pods/ +**/.ios/**/.symlinks/ +**/.ios/**/profile +**/.ios/**/xcuserdata +**/.ios/.generated/ +**/.ios/Flutter/App.framework +**/.ios/Flutter/Flutter.framework +**/.ios/Flutter/Generated.xcconfig +**/.ios/Flutter/app.flx +**/.ios/Flutter/app.zip +**/.ios/Flutter/flutter_assets/ +**/.ios/ServiceDefinitions.json +**/.ios/Runner/GeneratedPluginRegistrant.* + + + + +# macOS +**/macos/Flutter/GeneratedPluginRegistrant.swift +**/macos/Flutter/Flutter-Debug.xcconfig +**/macos/Flutter/Flutter-Release.xcconfig +**/macos/Flutter/Flutter-Profile.xcconfig + + +# Coverage +coverage/ + + +# Symbols +app.*.symbols + + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages +!/dev/ci/**/Gemfile.lock +.packages~92442170bbeb67da2b4cc92142720c48c0d7f490 + +/pubspec.lock +/.flutter-plugins-dependencies +linux/* +!ios/Podfile +!ios/ExportOptions.plist.example + +m3u8_downloader-master/.dart_tool/ +m3u8_downloader-master/example/.dart_tool/ +m3u8_downloader-master/example/.flutter-plugins-dependencies +m3u8_downloader-master/example/build/ +m3u8_downloader-master/*/*.lock +m3u8_downloader-master/pubspec.lock +dist/ + +ume_kit/*/.dart_tool/ +ume_kit/*/.flutter-plugins-dependencies +ume_kit/*/pubspec.lock + +third_party/*/.dart_tool/ +third_party/*/.flutter-plugins-dependencies +third_party/*/pubspec.lock + + +*.apk +ume_kit/ + +# Claude Code 本地配置 +.claude/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..49c4c71 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# 91Dj + +A new Flutter project. 3.16.8 + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..e87255d --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,35 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + + # 禁止提示要把构造函数设为 const + prefer_const_constructors: false + # 禁止提示要把集合/列表设为 const + prefer_const_literals_to_create_immutables: false + # 禁止提示在构造函数中使用 const + prefer_const_constructors_in_immutables: false + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/analytics_sdk/.gitignore b/analytics_sdk/.gitignore new file mode 100644 index 0000000..ac5aa98 --- /dev/null +++ b/analytics_sdk/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/analytics_sdk/LICENSE b/analytics_sdk/LICENSE new file mode 100644 index 0000000..ba75c69 --- /dev/null +++ b/analytics_sdk/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/analytics_sdk/README.md b/analytics_sdk/README.md new file mode 100644 index 0000000..1fc9d6c --- /dev/null +++ b/analytics_sdk/README.md @@ -0,0 +1,1158 @@ +> **版本说明:** 本分支(`dev`)适用于 **Flutter 3.0.5 及以上**(Dart >=2.17)。 +> 如使用 Flutter 1.22.5,请切换至 `compat/flutter-1.22` 分支。 + +# Analytics SDK + +一个 Flutter 埋点 SDK,用于采集 App 内页面、点击、广告、视频、小说、漫画、搜索、推荐等行为,并提供批量上报、本地持久化、域名测速和事件类型动态配置能力。 + +## 目录 + +- [功能特性](#功能特性) +- [安装](#安装) +- [快速开始](#快速开始) +- [核心 API](#核心-api) +- [域名与事件类型管理](#域名与事件类型管理) +- [SDK 配置(可选)](#sdk-配置可选) +- [更多文档](#更多文档) +- [手动上报事件](#手动上报事件) +- [用户类型管理](#用户类型管理) +- [页面名称映射](#页面名称映射) +- [事件数据结构](#事件数据结构) +- [上报机制](#上报机制) +- [事件去重](#事件去重) +- [完整示例](#完整示例) +- [枚举类型参考](#枚举类型参考) +- [注意事项](#注意事项) +- [调试](#调试) +- [常见问题 FAQ](#常见问题-faq) +- [依赖项](#依赖项) +- [环境要求](#环境要求) + +## 功能特性 + +- **多事件类型**:页面浏览、页面点击、导航、广告展示/点击、视频/小说/漫画行为、关键词搜索、推荐等事件 +- **自动埋点**:页面生命周期(曝光/停留)、全局点击(默认开启) +- **批量上报**:队列聚合,默认每 5 秒或队列达 10 条时批量上报 +- **本地持久化**:事件写入本地 JSONL 文件,重启后自动恢复(默认最多 2000 行) +- **智能重试**:指数退避重试机制,初始 2s,最大 60s,最多 5 级 +- **域名测速**:从 `encryptedConfig` 解析域名列表,并发测速选最快可用节点 +- **事件类型动态开关**:通过 `encryptedConfig` 下发白名单,SDK 侧统一过滤 +- **会话管理**:按"冷启动 / 前后台 30 分钟 / 行为 30 分钟"规则生成 `sid` +- **用户管理**:支持用户 ID 与用户类型动态更新 +- **广告展示去重**:session 级别按 adId 自动去重,防止重复上报 +- **设备信息自动采集**:通过 `device_info_plus` 自动获取品牌、型号、系统名称与版本,无需外部传入 +- **设备指纹**:基于设备因子生成 SHA-256 指纹;因子全空时降级为 UUID v4 持久化兜底 +- **公共参数暴露**:通过 `getParams()` 获取全部或指定公共埋点参数,供服务端复用 + +## 安装 + +在宿主工程的 `pubspec.yaml` 中添加依赖(示例): + +```yaml +dependencies: + analytics_sdk: + path: ../analytics_sdk # 本地导入:根据实际目录调整相对路径 +``` + +然后执行: + +```bash +flutter pub get +``` + +## 快速开始 + +### 1. 初始化 SDK + +```dart +import 'package:flutter/widgets.dart'; +import 'package:analytics_sdk/analytics_sdk.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + // 从你的服务端获取加密配置(由业务方自行实现网络请求) + final encryptedConfig = await yourApiClient.fetchEncryptedConfig(); + + await AnalyticsSdk.instance.init( + appId: 'your_app_id', + encryptedConfig: encryptedConfig, // 加密配置,解密后包含域名列表和事件白名单 + channel: 'official', + uid: 'user_123', // 未登录可传 null 或空字符串 + deviceId: 'device_unique_id', // 设备唯一标识 + ); + + runApp(const MyApp()); +} +``` + +> 设备品牌、型号、系统名称和版本由 SDK 内部通过 `device_info_plus` 自动采集,无需外部传入。 + +### 2. 支持「先 init,后补 encryptedConfig」的场景 + +如果你需要先初始化 SDK,再从自己配置接口拿到加密串,可以这样用: + +```dart +// 1)先初始化(此时还没有 encryptedConfig,事件会正常入队和落盘,但无法实际上报) +await AnalyticsSdk.instance.init( + appId: 'your_app_id', + encryptedConfig: null, // 先传 null,SDK 会尝试读取本地缓存域名 + deviceId: 'device_unique_id', +); + +// 2)等拿到配置后再传入 encryptedConfig +final encryptedConfig = await yourApiClient.fetchEncryptedConfig(); +await AnalyticsSdk.instance.refreshDomainConfig(encryptedConfig: encryptedConfig); +``` + +`refreshDomainConfig()` 会: +- 解密 `encryptedConfig`,解析最新域名列表并并发测速,选出最快可用域名; +- 更新内部上报 URL,后续定时上报和 `flush()` 会把之前队列里的事件一并发出; +- 更新事件类型白名单配置。 + +### 3. 页面生命周期 & 全局点击埋点 + +```dart +import 'package:analytics_sdk/analytics_sdk.dart'; +import 'package:analytics_sdk/widget/global_click_wrapper.dart'; + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + navigatorObservers: [ + AnalyticsSdk.instance.pageObserver, // 页面自动埋点 + ], + builder: (context, child) { + return GlobalClickWrapper( // 全局点击自动抓取 + child: child!, + ); + }, + home: const HomePage(), + ); + } +} +``` + +## 核心 API + +### AnalyticsSdk.init + +```dart +Future init({ + required String appId, + required String? encryptedConfig, + String? channel, + String? uid, + required String deviceId, + bool enableDebugBanner = false, +}) +``` + +建议在 `main()` 中、`runApp()` 之前调用。内部流程: +- 通过 `device_info_plus` 自动采集设备品牌、型号、系统名称、版本等信息; +- 配置公共字段(appId / channel / uid / device 等); +- 初始化 SessionManager、AppLifecycleObserver、设备指纹; +- 解密 `encryptedConfig`,解析域名列表并发测速选最快节点; +- 解析事件类型白名单; +- 如 `encryptedConfig` 为 null / 解密失败,尝试读取本地缓存域名和事件类型配置; +- 初始化事件队列、本地缓存和定时上报定时器。 + +**`enableDebugBanner`**:仅在非 release 包下生效,配合 `AnalyticsDebugBanner` 显示调试悬浮条。 + +### AnalyticsSdk.getParams + +```dart +static Map getParams([List? keys]) +``` + +获取 SDK 当前全部公共参数(或按 key 过滤子集),可用于服务端事件拼装或调试: + +```dart +// 获取全部公共参数 +final all = AnalyticsSdk.getParams(); +// { 'app_id': '...', 'channel': '...', 'uid': '...', 'user_type': '...', 'sid': '...', 'device': '...', ... } + +// 只取指定参数 +final partial = AnalyticsSdk.getParams(['app_id', 'uid', 'device_id']); +``` + +返回字段包括:`app_id`、`channel`、`uid`、`user_type`、`sid`、`device`、`device_id`、`device_brand`、`device_model`、`user_agent`、`system_name`、`system_version`、`device_fingerprint`、`fp_version`。 + +### AnalyticsSdk.refreshDomainConfig + +```dart +Future refreshDomainConfig({String? encryptedConfig}) +``` + +使用场景: +- 先调用 `init()`,再从服务端获取 `encryptedConfig`; +- 运行中需要更新域名列表或事件类型白名单。 + +```dart +// 获取到最新加密配置后刷新 +final newConfig = await yourApiClient.fetchEncryptedConfig(); +await AnalyticsSdk.instance.refreshDomainConfig(encryptedConfig: newConfig); +``` + +> 传入 null 或空字符串时忽略本次刷新。本方法永远不会抛出异常。 + +### 事件上报相关 + +```dart +// 上报内置事件 +void track(dynamic event) + +// 立即触发上报 +Future flush() + +// 页面生命周期观察者 +NavigatorObserver get pageObserver + +// 资源清理(可选) +Future dispose() +``` + +`track()` 支持传入 SDK 内置的事件类(如 `VideoEvent`、`AdImpressionEvent` 等)或直接传 `Map`。 + +SDK 会自动: +- 做广告展示事件去重与过滤; +- 检查事件类型是否在白名单中; +- 校验必填字段; +- 检查单条事件大小(超过 300KB 丢弃); +- 将事件写入队列和本地缓存; +- 在队列长度达到阈值或定时器触发时批量上报。 + +### 用户与会话 + +```dart +// 设置用户 ID 和类型(登录 / 切换账号) +static void setUserIdAndType({String userId = "", UserTypeEnum? userTypeEnum}) + +// 只更新用户 ID(不改变用户类型) +static void setUid(String uid) + +// 更新用户类型 +void updateUserType(String newType) + +// 登出(清空 uid 和 userType) +static void logoutUser() + +// 更新渠道标识 +static void setChannel(String channel) +``` + +`sid`(会话 ID)由 SessionManager 自动管理,无需手动干预。 + +### 获取设备公共字段(服务端复用) + +```dart +static Map getDeviceCommonFields() +``` + +返回字段:`device`、`device_id`、`device_brand`、`device_model`、`user_agent`、`system_name`、`system_version`、`device_fingerprint`。可将这些字段传递给服务端,由服务端上报事件时附加。 + +## 域名与事件类型管理 + +### encryptedConfig 格式 + +`encryptedConfig` 是宿主 App 从自己服务端获取的 **Base64 编码 AES-GCM 密文**,SDK 内部解密后格式为: + +```json +{ + "domainList": [ + "https://api1.example.com", + "https://api2.example.com" + ], + "eventList": [ + "app_page_view", + "page_click", + "video_event", + "ad_impression" + ] +} +``` + +- **`domainList`**:上报域名列表,SDK 启动时会并发测速(GET 请求),选择响应最快的域名上报。 +- **`eventList`**:启用的事件类型白名单。**不在列表中的事件会被静默丢弃**。传空列表时,所有事件类型均允许上报(向后兼容)。 + +### 测速与降级策略 + +1. 解密 `encryptedConfig` 成功 → 用 `domainList` 并发测速,选最快域名; +2. `encryptedConfig` 为 null 或解密失败 → 尝试从磁盘缓存(`data_plus_domains_cache.json`)读取上次保存的域名列表; +3. 缓存也为空 → 上报 URL 保持未配置,事件缓存在本地,等 `refreshDomainConfig()` 成功后自动上报。 + +### 验证加密配置(调试包) + +```dart +final result = AnalyticsSdk.instance.validateEncryptedConfig(encryptedString); +if (result['success'] == true) { + print('条目数量: ${result['count']}'); + print('第一条预览: ${result['preview']}'); +} else { + print('验证失败: ${result['error']}'); +} +``` + +> 此方法仅在 debug 包可用,release 包直接返回 `{'success': false}`。 + +## SDK 配置(可选) + +通过 `SdkConfig.configure()` 调整 SDK 参数,需在 `init()` **之前**调用: + +```dart +import 'package:analytics_sdk/config/sdk_config.dart'; + +SdkConfig.configure( + maxQueueSize: 5000, + uploadInterval: const Duration(seconds: 10), + maxCacheLines: 2000, + connectionTimeout: const Duration(seconds: 15), +); +``` + +常用配置: + +**队列与批量** +- `maxQueueSize`:事件队列最大容量(默认 `5000`) +- `autoUploadThreshold`:达到阈值时自动触发上报(默认 `10`) +- `maxBatchSize`:单次上报最大事件数(默认 `500`) +- `maxJsonSize`:单次请求 JSON 最大字节数(默认 `5MB`) +- `maxSingleEventSize`:单条事件最大字节数,超过则丢弃(默认 `300KB`) + +**定时器** +- `uploadInterval`:定时上报间隔(默认 `5秒`) +- `clickThrottleDuration`:全局点击节流时间(默认 `300毫秒`) +- `speedTestTimeout`:域名测速超时(默认 `5秒`) + +**网络超时** +- `connectionTimeout`:HTTP 连接超时(默认 `10秒`) +- `readTimeout`:HTTP 读取超时(默认 `30秒`) + +**缓存相关** +- `cacheFileName`:事件缓存文件名(默认 `data_plus_events_cache.jsonl`) +- `maxCacheLines`:缓存文件最大行数(默认 `2000`) +- `maxCacheBytes`:缓存文件最大字节数(默认 `2MB`) +- `tombstoneCompactionThreshold`:tombstone 文件达到此条目数时触发主缓存压缩(默认 `200`) +- `tombstoneFileName`:tombstone 文件名(默认 `data_plus_events_tombstone.txt`) + +**重试** +- `baseRetryDelay`:基础重试延迟(默认 `2秒`) +- `maxRetryDelay`:最大重试延迟(默认 `60秒`) +- `maxBackoffLevel`:最大退避级别(默认 `5`) + +**广告去重** +- `maxAdImpressionCapacity`:广告去重集合最大容量,FIFO 淘汰(默认 `10000`) + +恢复所有默认值: + +```dart +SdkConfig.reset(); +``` + +## 更多文档 + +本仓库根目录提供了更详细的开发文档: + +- **SDK_USAGE_GUIDE.md**:完整使用指南,包含所有内置事件的数据结构与字段说明、会话规则、自动埋点细节、性能与容错机制说明等。 + +建议接入前先通读 `SDK_USAGE_GUIDE.md`,本 README 主要用于快速了解和集成。 + +--- + +## 手动上报事件 + +```dart +void track(dynamic event) +``` + +上报任意事件,传入 SDK 内置事件类即可。 + +### 1. App 安装事件(客户端) + +> 首次安装启动时,读取剪贴板获取到 `traceId` 后上报。 + +```dart +AnalyticsSdk.instance.track(AppInstallEvent( + traceId: 'landing_page_trace_id_xxx', // 落地页点击生成的唯一 ID +)); +``` + +### 2. 视频行为事件(客户端) + +```dart +import 'package:analytics_sdk/enum/video_event_enum.dart'; + +AnalyticsSdk.instance.track(VideoEvent( + videoId: 'video_123', + videoTitle: '示例视频', + videoTypeId: 'type_1', + videoTypeName: '娱乐', + videoTagKey: 'tag_1,tag_2', + videoTagName: '热门,最新', + videoDuration: 300, + playDuration: 60, + playProgress: 20, + videoBehavior: VideoEventEnum.VIDEO_PLAY, + mediaId: '', +)); +``` + +### 3. 小说阅读事件(客户端) + +```dart +import 'package:analytics_sdk/enum/read_behavior_enum.dart'; + +AnalyticsSdk.instance.track(NovelEvent( + novelId: 'novel_123', + novelTitle: '示例小说', + novelTypeId: 'type_1', + novelTypeName: '玄幻', + novelTagKey: 'tag_1,tag_2', + novelTagName: '玄幻,穿越', + readProgress: 50, + pageNo: 10, + novelBehavior: ReadBehaviorEnum.PAGE_NEXT, +)); +``` + +### 4. 漫画阅读事件(客户端) + +```dart +AnalyticsSdk.instance.track(ComicEvent( + comicId: 'comic_123', + comicTitle: '示例漫画', + comicTypeId: 'type_1', + comicTypeName: '热血', + comicTagKey: 'tag_1,tag_2', + comicTagName: '热血,冒险', + readProgress: 30, + pageNo: 5, + comicBehavior: ReadBehaviorEnum.VIEW, +)); +``` + +### 5. 广告展示事件(客户端,带自动去重) + +> SDK 会对 `adId` 进行 session 级别去重:同一个 `adId` 在当前 session 内只会上报一次。 + +```dart +import 'package:analytics_sdk/enum/ad_type_enum.dart'; + +AnalyticsSdk.instance.track(AdImpressionEvent( + pageKey: PageLifecycleObserver.currentPageKey, + pageName: PageNameMapper.getPageName(PageLifecycleObserver.currentPageKey), + adSlotKey: 'home_banner_1', + adSlotName: '首页顶部Banner', + adId: 'ad_123,ad_456,ad_789', // 多个 adId 英文逗号分隔 + creativeId: 'cr_001,cr_002,cr_003', // 与 adId 一一对应,可传 null + adType: AdTypeEnum.banner, +)); +``` + +### 6. 广告点击事件(客户端) + +```dart +AnalyticsSdk.instance.track(AdClickEvent( + pageKey: PageLifecycleObserver.currentPageKey, + pageName: PageNameMapper.getPageName(PageLifecycleObserver.currentPageKey), + adSlotKey: 'home_banner_1', + adSlotName: '首页顶部Banner', + adId: 'ad_123', + adType: AdTypeEnum.banner, +)); +``` + +### 7. APP 广告行为事件(客户端) + +> 用于上报 APP 内置活动弹窗、运营 Banner 等的展示、点击、关闭行为。 + +```dart +import 'package:analytics_sdk/enum/banner_event_enum.dart'; + +AnalyticsSdk.instance.track(AdvertisingEvent( + eventType: BannerEventEnum.CLICK.label, // 'click' + advertisingKey: 'home_banner', + advertisingName: '首页Banner', + advertisingId: 'ad_001', +)); +``` + +### 8. 关键词搜索事件(客户端) + +```dart +AnalyticsSdk.instance.track(KeywordSearchEvent( + keyword: '示例关键词', + searchResultCount: 100, +)); +``` + +### 9. 关键词搜索结果点击事件(客户端) + +```dart +import 'package:analytics_sdk/enum/click_item_type_enum.dart'; + +AnalyticsSdk.instance.track(KeywordClickEvent( + keyword: '示例关键词', + clickItemId: 'video_123', + clickItemType: ClickItemTypeEnum.VIDEO, + clickPosition: 1, +)); +``` + +### 10. 推荐列表展示事件(客户端) + +```dart +AnalyticsSdk.instance.track(RecommendListViewEvent( + pageKey: 'home', + pageName: '首页', + recommendContentType: RecommendContentTypeEnum.video, + recommendTraceId: 'rec_trace_abc123', + recommendId: 'vid_001,vid_002,vid_003', +)); +``` + +### 11. 推荐列表点击事件(客户端) + +```dart +AnalyticsSdk.instance.track(RecommendListClickEvent( + pageKey: 'home', + pageName: '首页', + recommendTraceId: 'rec_trace_abc123', + recommendId: 'vid_001', +)); +``` + +### 强制立即上报 + +```dart +await AnalyticsSdk.instance.flush(); +``` + +### 资源清理 + +建议在应用退出前调用,SDK 会先尝试上报剩余事件,再释放所有资源: + +```dart +await AnalyticsSdk.instance.dispose(); +``` + +--- + +## 用户类型管理 + +```dart +import 'package:analytics_sdk/enum/user_type_enum.dart'; + +// 用户登录时设置用户 ID 和类型 +AnalyticsSdk.setUserIdAndType( + userId: 'user_123', + userTypeEnum: UserTypeEnum.vip, +); + +// 只更新用户类型(升级会员等) +AnalyticsSdk.instance.updateUserType(UserTypeEnum.vip.label); + +// 用户登出 +AnalyticsSdk.logoutUser(); +``` + +--- + +## 页面名称映射 + +SDK 使用路由 key(`RouteSettings.name`)作为 `pageKey`,建议在应用启动时统一注册映射: + +```dart +import 'package:analytics_sdk/manager/page_name_manager.dart'; + +// 批量添加 +PageNameMapper.addMappings({ + '/': '首页', + '/video_detail': '视频详情页', + '/user_center': '个人中心', + '/search': '搜索页', +}); + +// 单条添加 +PageNameMapper.addMapping('/settings', '设置页'); + +// 获取 +String pageName = PageNameMapper.getPageName('/video_detail'); // '视频详情页' +``` + +### 动态获取当前页面信息 + +```dart +import 'package:analytics_sdk/observer/page_lifecycle_observer.dart'; +import 'package:analytics_sdk/manager/page_name_manager.dart'; + +AnalyticsSdk.instance.track(AdImpressionEvent( + pageKey: PageLifecycleObserver.currentPageKey, + pageName: PageNameMapper.getPageName(PageLifecycleObserver.currentPageKey), + adSlotKey: 'home_banner_1', + adSlotName: '首页顶部Banner', + adId: 'ad_123', + adType: AdTypeEnum.banner, +)); +``` + +--- + +## 事件数据结构 + +所有事件上报时都包含以下公共字段(顶层): + +| 字段名 | 类型 | 说明 | +|--------|------|------| +| `event` | String | 事件类型,如 `video_event` | +| `channel` | String | 渠道标识 | +| `app_id` | String | 应用 ID | +| `uid` | String | 用户 ID | +| `sid` | String | 会话 ID(冷启动或后台超 30 分钟时重新生成) | +| `client_ts` | int | 事件对象**创建时**的时间戳(10位秒级) | +| `device` | String | 设备类型(SDK 自动检测) | +| `device_id` | String | 设备唯一标识(由 `init(deviceId)` 传入) | +| `device_brand` | String | 设备品牌(SDK 自动检测) | +| `device_model` | String | 设备型号(SDK 自动检测) | +| `user_agent` | String | 客户端 UA(SDK 自动检测) | +| `system_name` | String | 操作系统名称(SDK 自动检测) | +| `system_version` | String | 操作系统版本(SDK 自动检测) | +| `device_fingerprint` | String | 设备指纹(SHA-256;因子全空时降级为 UUID v4) | +| `fp_version` | String | 指纹生成规则版本号(当前为 `1`) | +| `event_id` | String | 事件去重 ID(双重 MD5) | +| `payload` | Object | 事件业务字段 | + +> `device_fingerprint` 和 `fp_version` 均不参与 `event_id` 的 MD5 计算。 + +--- + +## 上报机制 + +### 批量上报地址 + +``` +POST {fastestDomain}/api/eventTracking/batchReport.json?appId={appId} +Content-Type: application/json +Body: [ {...事件1}, {...事件2}, ... ] +``` + +### 触发条件 + +- 队列达到 10 条(`SdkConfig.autoUploadThreshold`) +- 定时器每 5 秒触发(`SdkConfig.uploadInterval`) +- 手动调用 `flush()` + +### 本地持久化 + +- 事件自动保存到 `data_plus_events_cache.jsonl`(应用文档目录) +- 应用重启后自动从缓存恢复并上报;恢复时会过滤掉已通过 tombstone 标记为已上报的事件,以及当前事件类型白名单中未启用的事件类型 +- 上报成功后,已上报的 `event_id` 追加写入 tombstone 文件(`data_plus_events_tombstone.txt`),避免每次上报都重写整个缓存文件;tombstone 条目达到 200 条(`SdkConfig.tombstoneCompactionThreshold`)时触发一次主缓存压缩合并 + +### 重试机制 + +失败后指数退避重试:`2s → 4s → 8s → 16s → 32s`,超过 60s 上限后不再翻倍,最多重试 5 级。 + +--- + +## 事件去重 + +### 通用事件去重 + +每个事件都有唯一的 `event_id`(基于所有字段的双重 MD5 计算),服务端可以使用 `event_id` 去重。 + +### 广告展示事件去重 + +`AdImpressionEvent` 在客户端进行额外去重: + +- 基于 `adId` 去重,同一 `adId` 在当前 session 只上报一次 +- 支持多个 `adId` 逗号分隔,只上报未曾上报的 ID +- 去重记录在内存中,应用重启后清空 +- 容量上限默认 10000 条,超出时 FIFO 淘汰最旧记录 + +去重管理 API: + +```dart +import 'package:analytics_sdk/manager/ad_impression_manager.dart'; + +// 检查广告 ID 是否已上报 +bool isReported = AdImpressionManager.instance.isReported('ad_123'); + +// 获取多个 ID 中哪些未上报 +List unreported = AdImpressionManager.instance.getUnreportedAdIds('ad_123,ad_456'); + +// 清除已上报记录(测试用) +AdImpressionManager.instance.clear(); +``` + +--- + +## 完整示例 + +```dart +import 'package:analytics_sdk/analytics_sdk.dart'; +import 'package:analytics_sdk/config/sdk_config.dart'; +import 'package:analytics_sdk/entity/video_event.dart'; +import 'package:analytics_sdk/entity/ad_impression_event.dart'; +import 'package:analytics_sdk/entity/advertising_event.dart'; +import 'package:analytics_sdk/enum/user_type_enum.dart'; +import 'package:analytics_sdk/enum/video_event_enum.dart'; +import 'package:analytics_sdk/enum/ad_type_enum.dart'; +import 'package:analytics_sdk/enum/banner_event_enum.dart'; +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:analytics_sdk/observer/page_lifecycle_observer.dart'; +import 'package:analytics_sdk/widget/global_click_wrapper.dart'; +import 'package:flutter/material.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + // 可选:调整 SDK 默认参数 + SdkConfig.configure( + uploadInterval: const Duration(seconds: 10), + autoUploadThreshold: 20, + ); + + // 配置页面名称映射 + PageNameMapper.addMappings({ + '/': '首页', + '/video_detail': '视频详情页', + '/user_center': '个人中心', + }); + + // 从自己的服务端获取加密配置 + final encryptedConfig = await yourApiClient.fetchEncryptedConfig(); + + // 初始化 SDK(设备信息自动采集,无需外部传入) + await AnalyticsSdk.instance.init( + appId: 'your_app_id', + encryptedConfig: encryptedConfig, + channel: 'official', + uid: '', // 未登录时传空 + deviceId: 'device_unique_id', + enableDebugBanner: true, // 仅 debug 包生效 + ); + + // 设置登录用户信息(已登录时) + AnalyticsSdk.setUserIdAndType( + userId: 'user_123', + userTypeEnum: UserTypeEnum.vip, + ); + + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return AnalyticsDebugBanner( // 仅 debug 包显示调试悬浮条 + child: MaterialApp( + navigatorObservers: [ + AnalyticsSdk.instance.pageObserver, // 页面生命周期自动埋点 + ], + builder: (context, child) { + return GlobalClickWrapper( // 全局点击自动抓取 + child: child!, + ); + }, + home: const HomePage(), + ), + ); + } +} + +class HomePage extends StatelessWidget { + const HomePage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('首页')), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: () { + AnalyticsSdk.instance.track(VideoEvent( + videoId: 'video_123', + videoTitle: '示例视频', + videoTypeId: 'type_1', + videoTypeName: '娱乐', + videoTagKey: 'hot', + videoTagName: '热门', + videoDuration: 300, + playDuration: 60, + playProgress: 20, + videoBehavior: VideoEventEnum.VIDEO_PLAY, + )); + }, + child: const Text('上报视频播放'), + ), + ElevatedButton( + onPressed: () { + AnalyticsSdk.instance.track(AdImpressionEvent( + pageKey: PageLifecycleObserver.currentPageKey, + pageName: PageNameMapper.getPageName(PageLifecycleObserver.currentPageKey), + adSlotKey: 'home_banner_1', + adSlotName: '首页顶部Banner', + adId: 'ad_123,ad_456', + adType: AdTypeEnum.banner, + )); + }, + child: const Text('上报广告展示'), + ), + ElevatedButton( + onPressed: () { + AnalyticsSdk.instance.track(AdvertisingEvent( + eventType: BannerEventEnum.CLICK.label, + advertisingKey: 'home_popup', + advertisingName: '首页弹窗', + advertisingId: 'adv_001', + )); + }, + child: const Text('上报弹窗点击'), + ), + ElevatedButton( + onPressed: () => AnalyticsSdk.instance.flush(), + child: const Text('立即上报'), + ), + ], + ), + ), + ); + } +} + +// 应用退出时清理资源 +class _MyAppState extends State with WidgetsBindingObserver { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + AnalyticsSdk.instance.dispose(); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.detached) { + AnalyticsSdk.instance.dispose(); + } + } +} +``` + +--- + +## 枚举类型参考 + +### UserTypeEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `UserTypeEnum.normal` | `normal` | 普通用户 | +| `UserTypeEnum.vip` | `vip` | VIP 用户 | + +### DeviceEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `DeviceEnum.android` | `Android` | Android 设备 | +| `DeviceEnum.iOS` | `iOS` | iOS 设备 | +| `DeviceEnum.pc` | `PC` | PC 设备 | + +### VideoEventEnum + +| 枚举值 | key | 说明 | +|--------|-----|------| +| `VideoEventEnum.VIDEO_PLAY` | `video_play` | 播放 | +| `VideoEventEnum.VIDEO_PAUSE` | `video_pause` | 暂停 | +| `VideoEventEnum.VIDEO_SHARE` | `video_share` | 分享 | +| `VideoEventEnum.VIDEO_COMPLETE` | `video_complete` | 播放完成 | +| `VideoEventEnum.VIDEO_FORWARD` | `video_forward` | 快进 | +| `VideoEventEnum.VIDEO_REWIND` | `video_rewind` | 快退 | + +### VideoContentTypeEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `VideoContentTypeEnum.video` | `video` | 长视频 | +| `VideoContentTypeEnum.shortVideo` | `short_video` | 短视频 | + +### ReadBehaviorEnum + +| 枚举值 | key | 说明 | +|--------|-----|------| +| `ReadBehaviorEnum.VIEW` | `view` | 展示 | +| `ReadBehaviorEnum.PAGE_NEXT` | `page_next` | 下一页 | +| `ReadBehaviorEnum.PAGE_PREV` | `page_prev` | 上一页 | +| `ReadBehaviorEnum.COMPLETE` | `complete` | 读完 | + +### AdTypeEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `AdTypeEnum.icon` | `icon` | 图标广告 | +| `AdTypeEnum.banner` | `banner` | Banner | +| `AdTypeEnum.feed` | `feed` | 信息流 | +| `AdTypeEnum.player` | `player` | 播放器广告 | +| `AdTypeEnum.splash` | `splash` | 开屏广告 | +| `AdTypeEnum.text` | `text` | 文字广告 | + +### BannerEventEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `BannerEventEnum.CLICK` | `click` | 点击 | +| `BannerEventEnum.CLOSE` | `close` | 关闭 | +| `BannerEventEnum.SHOW` | `show` | 展示 | + +### AuthTypeEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `AuthTypeEnum.phone` | `phone` | 手机号 | +| `AuthTypeEnum.deviceid` | `deviceid` | 设备 ID | +| `AuthTypeEnum.email` | `email` | 邮箱 | +| `AuthTypeEnum.username` | `username` | 账号 | + +### ClickItemTypeEnum + +| 枚举值 | key | 说明 | +|--------|-----|------| +| `ClickItemTypeEnum.VIDEO` | `video` | 视频 | +| `ClickItemTypeEnum.NOVEL` | `novel` | 小说 | +| `ClickItemTypeEnum.COMIC` | `comic` | 漫画 | + +### RecommendContentTypeEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `RecommendContentTypeEnum.video` | `video` | 视频 | +| `RecommendContentTypeEnum.novel` | `novel` | 小说 | +| `RecommendContentTypeEnum.comic` | `comic` | 漫画 | + +### OrderTypeEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `OrderTypeEnum.coinPurchase` | `coin_purchase` | 金币购买 | +| `OrderTypeEnum.vipSubscription` | `vip_subscription` | VIP 订阅 | + +### CurrencyEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `CurrencyEnum.cny` | `CNY` | 人民币 | +| `CurrencyEnum.usd` | `USD` | 美元 | + +### PayTypeEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `PayTypeEnum.wechat` | `wechat` | 微信支付 | +| `PayTypeEnum.alipay` | `alipay` | 支付宝 | +| `PayTypeEnum.bankCard` | `bank_card` | 银行卡 | +| `PayTypeEnum.applePay` | `apple_pay` | Apple Pay | + +### ConsumeReasonEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `ConsumeReasonEnum.videoUnlock` | `video_unlock` | 视频解锁 | +| `ConsumeReasonEnum.giftSend` | `gift_send` | 礼物赠送 | +| `ConsumeReasonEnum.contentPurchase` | `content_purchase` | 内容购买 | + +--- + +## 注意事项 + +1. **初始化时机**:建议在 `main()` 函数中、`runApp()` 之前初始化 SDK。 + +2. **encryptedConfig 由宿主 App 负责获取**:SDK 不自主发起配置接口请求,宿主 App 需自行从服务端获取加密配置串,再传入 `init()` 或 `refreshDomainConfig()`。 + +3. **设备信息自动采集**:品牌、型号、系统名称和版本由 SDK 通过 `device_info_plus` 自动读取,无需外部传入。`deviceId` 仍由宿主 App 提供(Android IMEI/OAID、iOS IDFV 等)。 + +4. **页面路由命名**:确保所有页面路由都有名称,SDK 才能正确追踪页面生命周期: + ```dart + MaterialApp( + routes: { + '/': (context) => HomePage(), // ✅ 有路由名称 + '/detail': (context) => DetailPage(), + }, + ) + ``` + +5. **用户信息更新**:用户登录、登出、升级会员等操作时,记得调用相应的用户管理方法。 + +6. **网络权限**: + - Android:在 `AndroidManifest.xml` 中添加 `INTERNET` 权限 + - iOS:确保 `Info.plist` 中 `NSAppTransportSecurity` 配置正确 + +7. **资源清理**:应用退出前建议调用 `dispose()` 清理资源。 + +8. **事件过滤**:未在 `encryptedConfig.eventList` 中启用的事件类型会被静默丢弃,可通过日志确认。`eventList` 为空时默认允许所有事件上报。 + +9. **AdImpressionEvent 的 adType 需传枚举**:不能传字符串,需使用 `AdTypeEnum.banner` 等枚举值。 + +--- + +## 调试 + +### AnalyticsDebugBanner + +在 `init()` 时设置 `enableDebugBanner: true`,并在 Widget 树根部加入 `AnalyticsDebugBanner`(仅 debug 包显示): + +```dart +import 'package:analytics_sdk/analytics_sdk.dart'; + +AnalyticsDebugBanner( + child: MaterialApp(...), +) +``` + +悬浮条显示 7 个埋点流程步骤:①域名配置 → ②SDK初始化 → ③域名列表 → ④域名测速 → ⑤上报地址 → ⑥待上报队列 → ⑦最近上报结果。 + +### 通过代码获取调试信息 + +```dart +// 获取摘要 +final info = AnalyticsSdk.instance.getDebugInfo(); +print(info['inited']); // SDK 是否初始化 +print(info['queueLength']); // 当前队列长度 +print(info['reportUrl']); // 上报 URL + +// 获取详细步骤 +final steps = AnalyticsSdk.instance.getDebugSteps(); +for (final step in steps) { + print('${step['name']}: ${step['status']} - ${step['detail']}'); +} +``` + +### 控制日志输出 + +```dart +import 'package:analytics_sdk/utils/logger.dart'; + +Logger.enabled = false; // 关闭所有日志 +Logger.enabled = true; // 开启日志(debug 包默认已开启) +``` + +--- + +## 常见问题 FAQ + +**Q: 事件没有上报成功怎么办?** + +A: +1. 检查网络连接和权限 +2. 确认 `encryptedConfig` 是否传入且格式正确(可用 `validateEncryptedConfig()` 验证) +3. 查看控制台日志了解具体错误 +4. 检查是否调用了 `flush()` 或在应用退出前调用了 `dispose()` + +**Q: 如何验证 encryptedConfig 是否正确?** + +A: 在 debug 包中调用: + +```dart +final result = AnalyticsSdk.instance.validateEncryptedConfig(encryptedString); +print(result); +``` + +**Q: 事件上报延迟多久?** + +A: 正常情况下: +- 队列达到 10 条时立即上报 +- 每 5 秒定时上报一次 +- 调用 `flush()` 立即上报 + +**Q: 为什么某些事件没有上报?** + +A: 可能原因: +1. 事件类型不在 `encryptedConfig.eventList` 白名单中 +2. 广告展示事件被 session 级别去重过滤 +3. 事件单条超过 300KB 被丢弃 +4. 队列满(默认上限 5000 条) +5. 上报 URL 未就绪(域名测速未完成或 encryptedConfig 未传入) + +**Q: 如何动态开启/关闭某些事件的上报?** + +A: 通过服务端更新 `encryptedConfig` 中的 `eventList`,然后调用: + +```dart +await AnalyticsSdk.instance.refreshDomainConfig(encryptedConfig: newEncryptedConfig); +``` + +**Q: 如何自定义 SDK 配置参数?** + +A: + +```dart +import 'package:analytics_sdk/config/sdk_config.dart'; + +SdkConfig.configure( + maxQueueSize: 10000, + uploadInterval: const Duration(seconds: 30), + connectionTimeout: const Duration(seconds: 15), +); + +await AnalyticsSdk.instance.init(...); +``` + +**Q: 如何控制日志输出?** + +A: + +```dart +import 'package:analytics_sdk/utils/logger.dart'; +Logger.enabled = false; +``` + +--- + +## 支持的事件类型 + +| 事件类型字符串 | 说明 | 上报方式 | +|--------------|------|---------| +| `app_install` | App 安装 | 客户端 | +| `navigation` | 导航事件 | **自动** | +| `app_page_view` | 应用页面展示 | **自动** | +| `page_click` | 应用页面点击 | **自动** | +| `page_lifecycle` | 页面生命周期 | **自动** | +| `advertising` | APP 广告行为(弹窗/运营 Banner) | 客户端 | +| `ad_click` | 广告点击 | 客户端 | +| `ad_impression` | 广告展示 | 客户端 | +| `video_event` | 视频行为 | 客户端 | +| `novel_event` | 小说阅读 | 客户端 | +| `comic_event` | 漫画阅读 | 客户端 | +| `keyword_search` | 关键词搜索 | 客户端 | +| `keyword_click` | 关键词搜索结果点击 | 客户端 | +| `recommend_list_view` | 推荐列表展示 | 客户端 | +| `recommend_list_click` | 推荐列表点击 | 客户端 | + +--- + +## 依赖项 + +- `flutter`: Flutter SDK (>=3.3.0) +- `uuid`: UUID 生成(设备指纹因子全空时的降级兜底) +- `path_provider: ^2.1.4`: 本地文件路径获取 +- `synchronized: ^3.1.0`: 并发控制锁 +- `device_info_plus: ^10.0.0`: 自动采集设备品牌、型号、系统名称与版本 +- `pointycastle: 3.7.3`: AES-GCM 解密 +- `crypto: ^3.0.2`: SHA-256 / MD5 哈希计算 + +## 环境要求 + +- Dart SDK: `>=3.4.4 <4.0.0` +- Flutter: `>=3.3.0` diff --git a/analytics_sdk/SDK_USAGE_GUIDE.md b/analytics_sdk/SDK_USAGE_GUIDE.md new file mode 100644 index 0000000..6224497 --- /dev/null +++ b/analytics_sdk/SDK_USAGE_GUIDE.md @@ -0,0 +1,1027 @@ +# Analytics SDK 使用文档 + +## 目录 + +1. [概述](#1-概述) +2. [安装](#2-安装) +3. [SDK 初始化](#3-sdk-初始化) +4. [用户管理](#4-用户管理) +5. [自动埋点](#5-自动埋点) +6. [手动事件上报](#6-手动事件上报) +7. [枚举参考](#7-枚举参考) +8. [SdkConfig 全局参数](#8-sdkconfig-全局参数) +9. [上报数据格式](#9-上报数据格式) +10. [调试工具](#10-调试工具) +11. [高级用法](#11-高级用法) +12. [完整集成示例](#12-完整集成示例) + +--- + +## 1. 概述 + +Analytics SDK 是一个 Flutter 插件,提供: + +- 自动埋点:页面浏览、页面生命周期、全局点击、导航事件(均默认开启) +- 手动埋点:视频/小说/漫画行为、广告、推荐等客户端事件 +- 本地持久化缓存(JSONL 格式),断网重传 +- 域名并发测速,自动选择最快上报节点 +- AES-GCM 加密配置,防止配置明文泄露 +- 事件类型白名单控制(服务端下发) +- 广告展示去重(session 级别) +- 设备信息自动采集(通过 `device_info_plus`,无需外部传入) +- 设备指纹自动生成 +- `getParams()` 方法暴露全部或指定公共参数,供服务端复用 + +所有公开方法均**不会向外抛出异常**,SDK 内部失败不影响宿主应用运行。 + +--- + +## 2. 安装 + +在 `pubspec.yaml` 中添加依赖: + +```yaml +dependencies: + analytics_sdk: + path: ../analytics_sdk # 或 git/pub 地址 +``` + +运行: + +```bash +flutter pub get +``` + +--- + +## 3. SDK 初始化 + +### 3.1 最简初始化 + +```dart +import 'package:analytics_sdk/analytics_sdk.dart'; + +await AnalyticsSdk.instance.init( + appId: 'your_app_id', + encryptedConfig: 'Base64加密字符串', // 服务端下发 + deviceId: 'your_device_id', // Android IMEI/OAID、iOS IDFV 等 +); +``` + +设备品牌、型号、系统名称、版本等信息由 SDK 内部通过 `device_info_plus` 自动采集。 + +### 3.2 完整参数说明 + +| 参数 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| `appId` | `String` | 是 | — | 应用 ID | +| `encryptedConfig` | `String?` | 是(可传 null) | null | 加密配置字符串,见下方格式说明 | +| `channel` | `String?` | 否 | '' | 渠道标识,可后续通过 `setChannel` 更新 | +| `uid` | `String?` | 否 | '' | 用户 ID,未登录可传空,登录后通过 `setUserIdAndType` 更新 | +| `deviceId` | `String` | 是 | — | 设备唯一标识(Android IMEI/OAID、iOS IDFV 等) | +| `enableDebugBanner` | `bool` | 否 | `false` | 是否显示调试悬浮条(仅在非 release 包生效) | + +> **自动采集字段**:`device`(类型)、`device_brand`、`device_model`、`system_name`、`system_version`、`user_agent` 均由 SDK 通过 `device_info_plus` 自动读取,无需传入。 + +### 3.3 encryptedConfig 格式说明 + +`encryptedConfig` 是一个 Base64 编码的 AES-GCM 密文,解密后内容为: + +```json +{ + "domainList": [ + "https://api1.example.com", + "https://api2.example.com" + ], + "eventList": [ + "app_page_view", + "page_click", + "video_event", + "ad_impression" + ] +} +``` + +- `domainList`:上报域名列表,SDK 启动时会并发测速,选择最快的域名上报。 +- `eventList`:启用的事件类型白名单。**不在列表中的事件会被静默丢弃**。传空列表或省略此字段时,所有事件类型均允许上报。 + +如需在调试包中验证加密串,使用 [`validateEncryptedConfig`](#validateencryptedconfig)。 + +### 3.4 初始化时机 + +建议在 `main()` 中 `runApp()` 之前调用: + +```dart +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + // 可选:在 init() 前覆盖默认参数 + SdkConfig.configure( + uploadInterval: const Duration(seconds: 5), + autoUploadThreshold: 10, + ); + + await AnalyticsSdk.instance.init( + appId: 'your_app_id', + encryptedConfig: await fetchEncryptedConfigFromServer(), + deviceId: await getDeviceId(), + channel: 'google_play', + uid: currentUser?.id ?? '', + ); + + runApp(const MyApp()); +} +``` + +--- + +## 4. 用户管理 + +### 4.1 登录 / 切换账号 + +```dart +AnalyticsSdk.setUserIdAndType( + userId: '12345', + userTypeEnum: UserTypeEnum.vip, +); +``` + +| 参数 | 类型 | 说明 | +|------|------|------| +| `userId` | `String` | 用户 ID,默认空字符串(未登录状态) | +| `userTypeEnum` | `UserTypeEnum?` | 用户类型,null 时默认为 `normal` | + +### 4.2 仅更新 uid(不改变用户类型) + +```dart +AnalyticsSdk.setUid('12345'); +``` + +### 4.3 更新渠道 + +```dart +AnalyticsSdk.setChannel('huawei_store'); +``` + +### 4.4 登出 + +```dart +AnalyticsSdk.logoutUser(); +``` + +--- + +## 5. 自动埋点 + +### 5.1 页面生命周期自动追踪(PageLifecycleObserver) + +```dart +MaterialApp( + navigatorObservers: [ + AnalyticsSdk.instance.pageObserver, + ], +) +``` + +注册后 SDK 会自动追踪页面进入、离开和导航事件,无需手动上报。 + +获取当前页面 key: + +```dart +final currentPage = PageLifecycleObserver.currentPageKey; +``` + +### 5.2 页面名称映射(PageNameMapper) + +```dart +import 'package:analytics_sdk/manager/page_name_manager.dart'; + +PageNameMapper.addMappings({ + 'home': '首页', + 'video_detail': '视频详情页', + 'user_center': '个人中心', + 'search': '搜索页', +}); + +final name = PageNameMapper.getPageName('video_detail'); // '视频详情页' +``` + +> **注意**:`pageKey` 和映射 key 均会自动去除前导 `/`,传 `'/video_detail'` 与 `'video_detail'` 效果相同。 + +### 5.3 全局点击自动追踪(GlobalClickWrapper) + +```dart +import 'package:analytics_sdk/widget/global_click_wrapper.dart'; + +MaterialApp( + builder: (context, child) => GlobalClickWrapper(child: child!), +) +``` + +自动上报 `PageClickEvent`,含归一化坐标(300ms 节流)。 + +--- + +## 6. 手动事件上报 + +所有手动事件通过 `AnalyticsSdk.instance.track(event)` 上报。 + +### 6.1 App 安装事件【客户端】 + +> **触发时机**:首次安装启动后,读取剪贴板获取到 traceId 时上报。 + +```dart +AnalyticsSdk.instance.track(AppInstallEvent( + traceId: 'clipboard_trace_id', +)); +``` + +| 字段 | 类型 | 说明 | +|------|------|------| +| `traceId` | `String` | 落地页生成并写入剪贴板的唯一追踪 ID | + +--- + +### 6.2 视频行为事件【客户端】 + +> **触发时机**:用户播放、暂停、快进、完播等操作时上报。 + +```dart +AnalyticsSdk.instance.track(VideoEvent( + videoId: 'vid_001', + videoTitle: '精彩大片', + videoTypeId: 'action', + videoTypeName: '动作片', + videoTagKey: 'hot,new', + videoTagName: '热门,最新', + videoDuration: 7200, + playDuration: 120, + playProgress: 30, + videoBehavior: VideoEventEnum.VIDEO_PLAY, + videoContentType: VideoContentTypeEnum.video, + recommendTraceId: 'trace_abc123', // 未接推荐引擎传 '' + mediaId: '', +)); +``` + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `videoId` | `String` | 是 | 视频 ID | +| `videoTitle` | `String` | 是 | 视频标题 | +| `videoTypeId` | `String` | 是 | 视频分类 ID | +| `videoTypeName` | `String` | 是 | 视频分类名称 | +| `videoTagKey` | `String` | 是 | 标签 KEY,多个用英文逗号分隔 | +| `videoTagName` | `String` | 是 | 标签名称,多个用英文逗号分隔 | +| `videoDuration` | `int` | 是 | 视频总时长(秒) | +| `playDuration` | `int` | 是 | 本次播放时长(秒) | +| `playProgress` | `int` | 是 | 播放进度百分比(0-100) | +| `videoBehavior` | `VideoEventEnum` | 是 | 视频行为,见 [VideoEventEnum](#videoeventenum) | +| `videoContentType` | `VideoContentTypeEnum?` | 否 | 内容类型:`video`(长视频) / `short_video`(短视频),未传时上报空字符串 | +| `recommendTraceId` | `String` | 否 | 推荐引擎 trace ID,未接推荐引擎传 `''`,默认 `''` | +| `mediaId` | `String` | 否 | 媒体资源 ID,默认 `''` | + +--- + +### 6.3 小说阅读事件【客户端】 + +```dart +AnalyticsSdk.instance.track(NovelEvent( + novelId: 'novel_001', + novelTitle: '斗破苍穹', + novelTypeId: 'fantasy', + novelTypeName: '玄幻', + novelTagKey: 'hot,classic', + novelTagName: '热门,经典', + readProgress: 45, + pageNo: 10, + novelBehavior: ReadBehaviorEnum.PAGE_NEXT, + mediaId: '', +)); +``` + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `novelId` | `String` | 是 | 小说 ID | +| `novelTitle` | `String` | 是 | 小说标题 | +| `novelTypeId` | `String` | 是 | 分类 ID | +| `novelTypeName` | `String` | 是 | 分类名称 | +| `novelTagKey` | `String` | 是 | 标签 KEY,多个用英文逗号分隔 | +| `novelTagName` | `String` | 是 | 标签名称,多个用英文逗号分隔 | +| `readProgress` | `int` | 是 | 阅读进度百分比(0-100) | +| `pageNo` | `int` | 是 | 当前阅读第几页(从 1 开始) | +| `novelBehavior` | `ReadBehaviorEnum` | 是 | 阅读行为,见 [ReadBehaviorEnum](#readbehaviorenum) | +| `recommendTraceId` | `String` | 否 | 推荐引擎 trace ID,未接推荐引擎传 `''`,默认 `''` | +| `mediaId` | `String` | 否 | 媒体资源 ID,未使用老司机库资源时传 `''` | + +--- + +### 6.4 漫画阅读事件【客户端】 + +```dart +AnalyticsSdk.instance.track(ComicEvent( + comicId: 'comic_001', + comicTitle: '火影忍者', + comicTypeId: 'battle', + comicTypeName: '热血', + comicTagKey: 'hot,classic', + comicTagName: '热门,经典', + readProgress: 60, + pageNo: 5, + comicBehavior: ReadBehaviorEnum.PAGE_NEXT, +)); +``` + +字段与 `NovelEvent` 对称,将 `novel*` 前缀替换为 `comic*`,`novelBehavior` 替换为 `comicBehavior`。`recommendTraceId` 字段同样支持,默认 `''`。 + +--- + +### 6.5 广告展示事件【客户端】(带自动去重) + +> SDK 会对 `adId` 进行 session 级别去重:同一个 `adId` 在当前 session 内只会上报一次。 + +```dart +AnalyticsSdk.instance.track(AdImpressionEvent( + pageKey: 'home', + pageName: '首页', + adSlotKey: 'home_banner_1', + adSlotName: '首页顶部Banner', + adId: 'ad_001,ad_002,ad_003', + creativeId: 'cr_001,cr_002,cr_003', + adType: 'banner', // 传广告类型字符串,不传默认空字符串 +)); +``` + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `pageKey` | `String` | 是 | 页面标识 | +| `pageName` | `String` | 是 | 页面名称 | +| `adSlotKey` | `String` | 是 | 广告位标识 | +| `adSlotName` | `String` | 是 | 广告位名称 | +| `adId` | `String` | 是 | 广告 ID,多个用英文逗号分隔 | +| `creativeId` | `String` | 否 | 素材 ID,多个用英文逗号分隔,与 `adId` 一一对应,默认 `''` | +| `adType` | `String` | 否 | 广告类型字符串,如 `'banner'`、`'feed'`、`'splash'`,默认 `''` | + +**去重逻辑**: +- 进入 TabA 展示 20 条广告 → 上报 20 条 +- 切换到 TabB 展示 20 条广告 → 上报 20 条 +- 返回 TabA 上滑新加载 10 条广告 → 上报新的 10 条 +- 再次下滑看到之前已展示的广告 → 全部被过滤 + +--- + +### 6.6 广告点击事件【客户端】 + +```dart +AnalyticsSdk.instance.track(AdClickEvent( + pageKey: 'home', + pageName: '首页', + adSlotKey: 'home_banner_1', + adSlotName: '首页顶部Banner', + adId: 'ad_001', + creativeId: 'cr_001', + adType: 'banner', +)); +``` + +字段与 `AdImpressionEvent` 相同,`adId` 为单个广告 ID。 + +--- + +### 6.7 APP 广告行为事件【客户端】(弹窗/Banner 行为) + +> 用于上报 APP 内置广告(弹窗、Banner 等)的展示、点击、关闭行为。 + +```dart +AnalyticsSdk.instance.track(AdvertisingEvent( + eventType: BannerEventEnum.CLICK.label, + advertisingKey: 'home_popup', + advertisingName: '首页弹窗', + advertisingId: 'adv_001', +)); +``` + +| 字段 | 类型 | 说明 | +|------|------|------| +| `eventType` | `String` | 行为类型:`click`/`close`/`show` | +| `advertisingKey` | `String` | 广告标识 | +| `advertisingName` | `String` | 广告名称 | +| `advertisingId` | `String` | 广告 ID | + +--- + +### 6.8 关键词搜索事件【客户端】 + +```dart +AnalyticsSdk.instance.track(KeywordSearchEvent( + keyword: '火影忍者', + searchResultCount: 42, + searchTraceId: '', // 未接搜索引擎时传空字符串 + searchId: '', // 未接搜索引擎时传空字符串 +)); +``` + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `keyword` | `String` | 是 | 搜索关键词 | +| `searchResultCount` | `int` | 是 | 搜索结果数量 | +| `searchTraceId` | `String` | 否 | 搜索引擎 trace ID,未接搜索引擎时传 `''` | +| `searchId` | `String` | 否 | 搜索引擎 search ID,未接搜索引擎时传 `''` | + +--- + +### 6.9 关键词点击事件【客户端】 + +```dart +AnalyticsSdk.instance.track(KeywordClickEvent( + keyword: '火影忍者', + clickItemId: 'vid_001', + clickItemType: ClickItemTypeEnum.VIDEO, + clickPosition: 1, + searchTraceId: '', // 未接搜索引擎时传空字符串 +)); +``` + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `keyword` | `String` | 是 | 搜索关键词 | +| `clickItemId` | `String` | 是 | 点击项目 ID(视频ID / 小说ID 等) | +| `clickItemType` | `ClickItemTypeEnum` | 是 | 点击项目类型:`VIDEO` / `NOVEL` / `COMIC` | +| `clickPosition` | `int` | 是 | 点击位置(搜索结果中的排序位置,从 1 开始) | +| `searchTraceId` | `String` | 否 | 搜索引擎 trace ID,未接搜索引擎时传 `''` | + +--- + +### 6.10 推荐列表展示事件【客户端】 + +```dart +AnalyticsSdk.instance.track(RecommendListViewEvent( + pageKey: 'home', + pageName: '首页', + recommendContentType: RecommendContentTypeEnum.video, + recommendTraceId: 'rec_trace_abc123', + recommendId: 'vid_001,vid_002,vid_003', + recommendTraceInfo: '{"source":"server"}', // 未接推荐引擎传 '' + clientVersion: '1.2.3', // 未接推荐引擎传 '' +)); +``` + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `pageKey` | `String` | 是 | 页面标识 | +| `pageName` | `String` | 是 | 页面名称 | +| `recommendContentType` | `RecommendContentTypeEnum` | 是 | 推荐内容类型,见 [RecommendContentTypeEnum](#recommendcontenttypeenum) | +| `recommendTraceId` | `String` | 是 | 推荐引擎 trace ID | +| `recommendId` | `String` | 是 | 可视窗口内的内容 ID,多个用英文逗号分隔 | +| `recommendTraceInfo` | `String` | 否 | 推荐引擎 trace info,未接推荐引擎传 `''`,默认 `''` | +| `clientVersion` | `String` | 否 | 客户端版本号,如 `'1.2.3'`,默认 `''` | + +--- + +### 6.11 推荐列表点击事件【客户端】 + +```dart +AnalyticsSdk.instance.track(RecommendListClickEvent( + pageKey: 'home', + pageName: '首页', + recommendContentType: RecommendContentTypeEnum.video, + recommendTraceId: 'rec_trace_abc123', + recommendId: 'vid_001', + recommendTraceInfo: '{"source":"server"}', // 未接推荐引擎传 '' + clientVersion: '1.2.3', // 未接推荐引擎传 '' +)); +``` + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `pageKey` | `String` | 是 | 页面标识 | +| `pageName` | `String` | 是 | 页面名称 | +| `recommendContentType` | `RecommendContentTypeEnum` | 是 | 推荐内容类型,见 [RecommendContentTypeEnum](#recommendcontenttypeenum) | +| `recommendTraceId` | `String` | 是 | 推荐引擎 trace ID | +| `recommendId` | `String` | 是 | 被点击内容的 ID | +| `recommendTraceInfo` | `String` | 否 | 推荐引擎 trace info,未接推荐引擎传 `''`,默认 `''` | +| `clientVersion` | `String` | 否 | 客户端版本号,如 `'1.2.3'`,默认 `''` | + +--- + +### 6.12 强制立即上报 + +```dart +await AnalyticsSdk.instance.flush(); +``` + +### 6.13 释放资源 + +```dart +await AnalyticsSdk.instance.dispose(); +``` + +--- + +## 7. 枚举参考 + +### DeviceEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `DeviceEnum.android` | `Android` | Android 设备 | +| `DeviceEnum.iOS` | `iOS` | iOS 设备 | +| `DeviceEnum.pc` | `PC` | PC 设备 | + +### UserTypeEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `UserTypeEnum.normal` | `normal` | 普通用户 | +| `UserTypeEnum.vip` | `vip` | VIP 用户 | + +### AuthTypeEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `AuthTypeEnum.phone` | `phone` | 手机号 | +| `AuthTypeEnum.deviceid` | `deviceid` | 设备 ID | +| `AuthTypeEnum.email` | `email` | 邮箱 | +| `AuthTypeEnum.username` | `username` | 账号 | + +### AdTypeEnum + +> **已废弃**:`adType` 字段已改为普通 `String`,直接传字符串即可,如 `'banner'`、`'feed'`、`'splash'`。无需 import 枚举类。 + +### OrderTypeEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `OrderTypeEnum.coinPurchase` | `coin_purchase` | 金币购买 | +| `OrderTypeEnum.vipSubscription` | `vip_subscription` | VIP 订阅 | + +### CurrencyEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `CurrencyEnum.cny` | `CNY` | 人民币 | +| `CurrencyEnum.usd` | `USD` | 美元 | + +### PayTypeEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `PayTypeEnum.wechat` | `wechat` | 微信支付 | +| `PayTypeEnum.alipay` | `alipay` | 支付宝 | +| `PayTypeEnum.bankCard` | `bank_card` | 银行卡 | +| `PayTypeEnum.applePay` | `apple_pay` | Apple Pay | + +### ConsumeReasonEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `ConsumeReasonEnum.videoUnlock` | `video_unlock` | 视频解锁 | +| `ConsumeReasonEnum.giftSend` | `gift_send` | 礼物赠送 | +| `ConsumeReasonEnum.contentPurchase` | `content_purchase` | 内容购买 | + +### VideoEventEnum + +| 枚举值 | key | 说明 | +|--------|-----|------| +| `VideoEventEnum.VIDEO_VIEW` | `video_view` | 展示 | +| `VideoEventEnum.VIDEO_PLAY` | `video_play` | 播放 | +| `VideoEventEnum.VIDEO_PAUSE` | `video_pause` | 暂停 | +| `VideoEventEnum.VIDEO_SHARE` | `video_share` | 分享 | +| `VideoEventEnum.VIDEO_COMPLETE` | `video_complete` | 播放完成 | +| `VideoEventEnum.VIDEO_FORWARD` | `video_forward` | 快进 | +| `VideoEventEnum.VIDEO_REWIND` | `video_rewind` | 快退 | + +### VideoContentTypeEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `VideoContentTypeEnum.video` | `video` | 长视频 | +| `VideoContentTypeEnum.shortVideo` | `short_video` | 短视频 | + +### ReadBehaviorEnum + +| 枚举值 | key | 说明 | +|--------|-----|------| +| `ReadBehaviorEnum.VIEW` | `view` | 展示 | +| `ReadBehaviorEnum.PAGE_NEXT` | `page_next` | 下一页 | +| `ReadBehaviorEnum.PAGE_PREV` | `page_prev` | 上一页 | +| `ReadBehaviorEnum.COMPLETE` | `complete` | 读完 | + +### ClickItemTypeEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `ClickItemTypeEnum.VIDEO` | `video` | 视频 | +| `ClickItemTypeEnum.NOVEL` | `novel` | 小说 | +| `ClickItemTypeEnum.COMIC` | `comic` | 漫画 | + +### RecommendContentTypeEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `RecommendContentTypeEnum.video` | `video` | 视频 | +| `RecommendContentTypeEnum.novel` | `novel` | 小说 | +| `RecommendContentTypeEnum.comic` | `comic` | 漫画 | + +### BannerEventEnum + +| 枚举值 | label | 说明 | +|--------|-------|------| +| `BannerEventEnum.CLICK` | `click` | 点击 | +| `BannerEventEnum.CLOSE` | `close` | 关闭 | +| `BannerEventEnum.SHOW` | `show` | 展示 | + +--- + +## 8. SdkConfig 全局参数 + +在 `AnalyticsSdk.instance.init()` **之前**调用 `SdkConfig.configure(...)` 覆盖默认值: + +```dart +SdkConfig.configure( + uploadInterval: const Duration(seconds: 5), + autoUploadThreshold: 10, + maxBatchSize: 200, + connectionTimeout: const Duration(seconds: 15), +); +``` + +| 参数 | 默认值 | 说明 | +|------|--------|------| +| `maxQueueSize` | `5000` | 内存队列最大容量,超过后新事件丢弃 | +| `autoUploadThreshold` | `10` | 队列达到此数量时立即触发上报 | +| `maxBatchSize` | `500` | 单次上报最大事件数 | +| `uploadInterval` | `Duration(seconds: 5)` | 定时上报间隔 | +| `clickThrottleDuration` | `Duration(milliseconds: 300)` | 点击事件节流时间 | +| `connectionTimeout` | `Duration(seconds: 10)` | HTTP 连接超时 | +| `readTimeout` | `Duration(seconds: 30)` | HTTP 读取超时 | +| `speedTestTimeout` | `Duration(seconds: 5)` | 域名测速超时 | +| `maxCacheLines` | `2000` | 缓存文件最大行数 | +| `maxCacheBytes` | `2 * 1024 * 1024` (2MB) | 缓存文件最大字节数 | +| `maxSingleEventSize` | `300 * 1024` (300KB) | 单条事件最大字节数,超过丢弃 | +| `maxAdImpressionCapacity` | `10000` | 广告去重集合最大容量(FIFO 淘汰) | + +恢复所有默认值: + +```dart +SdkConfig.reset(); +``` + +--- + +## 9. 上报数据格式 + +### 9.1 公共字段 + +每个事件上报时,顶层 JSON 自动携带以下公共字段: + +| 字段名 | 类型 | 说明 | +|--------|------|------| +| `event` | `String` | 事件类型,如 `video_event`、`ad_impression` | +| `channel` | `String` | 渠道标识 | +| `app_id` | `String` | 应用 ID | +| `uid` | `String` | 用户 ID | +| `sid` | `String` | 会话 ID,自动管理 | +| `client_ts` | `int` | 事件创建时间戳(10位,秒) | +| `device` | `String` | 设备类型(自动采集) | +| `device_id` | `String` | 设备唯一标识 | +| `device_brand` | `String` | 设备品牌(自动采集) | +| `device_model` | `String` | 设备型号(自动采集) | +| `user_agent` | `String` | 客户端标识(自动采集) | +| `system_name` | `String` | 操作系统名称(自动采集) | +| `system_version` | `String` | 操作系统版本(自动采集) | +| `device_fingerprint` | `String` | 设备指纹(自动生成) | +| `fp_version` | `String` | 指纹版本号 | +| `event_id` | `String` | 事件去重 ID | +| `payload` | `Object` | 事件业务字段 | + +### 9.2 上报数据格式 + +```json +{ + "event": "video_event", + "channel": "google_play", + "app_id": "your_app_id", + "uid": "12345", + "sid": "sess_abc123", + "client_ts": 1704067200, + "device": "Android", + "device_id": "xxxx", + "device_brand": "Samsung", + "device_model": "SM-G991B", + "user_agent": "...", + "system_name": "Android", + "system_version": "14.0", + "device_fingerprint": "sha256-hex-xxxx", + "fp_version": "1", + "event_id": "md5-hash-for-dedup", + "payload": { + "event": "video_event", + "video_id": "vid_001", + ... + } +} +``` + +### 9.3 字段校验规则 + +SDK 在事件入队前会自动校验并修正字段。**关键字段**不合规时整个事件被静默丢弃;**可纠正字段**超限时自动截断或补全,不影响上报。 + +关键字段包括:`event`、`event_id`、`app_id`、`sid`、`client_ts`、`device`(提供了无法识别的值时)、`device_id`、`payload`(类型错误时),以及 payload 中的核心业务 ID 字段(`video_id` / `novel_id` / `comic_id` / `click_item_id`)。 + +> **通用类型处理规则** +> - 所有字符串字段在校验前均会自动 `trim()`(去除首尾空格)。 +> - 非字符串类型(如 `int`、`bool`、`null` 等)传入字符串字段时视为类型错误,等同于未传该字段(关键字段 → 丢弃事件;可选字段 → 移除该字段)。 +> - 数字字段(百分比 / 位置 / 数量 / 时间戳类)支持 `int`、`double` 以及纯数字字符串三种类型,均可正常解析。 + +#### 公共字段 + +| 字段 | 规则 | 不合规处理 | +|------|------|-----------| +| `event` | 小写字母/数字/下划线,最大 64 字符 | 丢弃事件 | +| `event_id` | 字母/数字,1~32 字符(SDK 自动生成) | 丢弃事件 | +| `app_id` | 非空,最大 64 字符 | 空时丢弃事件;超长截断 | +| `channel` | 可选,最大 128 字符 | 超长截断 | +| `uid` | 可选,最大 128 字符 | 超长截断 | +| `sid` | 自动生成;为空时 SDK 自动填充会话 ID | 生成失败则丢弃事件 | +| `client_ts` | 整数,范围 946684800 ~ 4102444800(2000 ~ 2100 年) | 丢弃事件 | +| `device` | 仅 `Android` / `iOS` / `PC`,大小写自动纠正 | 丢弃事件 | +| `device_id` | 非空,最大 128 字符 | 空时丢弃事件;超长截断 | +| `device_fingerprint` | 可选,最大 128 字符 | 超长截断 | +| `fp_version` | 若指纹非空而版本为空,自动填充默认版本 | — | + +#### payload 业务字段 + +| 类别 | 代表字段 | 规则 | 不合规处理 | +|------|---------|------|-----------| +| 字段名 | 所有 key | `^[A-Za-z0-9_]+$` | 移除该字段 | +| 关键 ID | `video_id` / `novel_id` / `comic_id` / `click_item_id` | 非空 | 丢弃事件 | +| 普通 ID | `ad_id` / `recommend_id` 等 | 非空 | 移除该字段 | +| 逗号分隔 | `ad_id` / `novel_tag_key` / `video_tag_name` 等 | 自动清理各段首尾空格、过滤空段;清洗后仍为空则移除该字段 | 移除该字段 | +| 可选字符串 | `creative_id` / `recommend_trace_id` 等 | 可为空 | 空字符串原样保留 | +| 名称字段 | `page_name` / `ad_name` / `video_name` 等 | 最大 128 字符 | 截断 | +| 标题字段 | `title` / `content_title` / `search_keyword` 等 | 最大 256 字符 | 截断 | +| 关键词字段 | `keywords` / `tags` / `description` | 最大 500 字符 | 截断 | +| 百分比字段 | `progress` / `completion_rate` / `view_ratio` | 整数,0 ~ 100 | 移除该字段 | +| 位置字段 | `position` / `rank` / `slot` / `index` | 整数,≥ 1 | 移除该字段 | +| 数字字段 | `duration` / `count` / `amount` 等 | 整数,≥ 0 | 移除该字段 | +| 时间戳字段 | `start_ts` / `end_ts` / `publish_ts` | 同 `client_ts` 范围 | 移除该字段 | +| 其他字段 | — | 字符串最大 500 字符;非字符串原样保留 | 截断 | + +### 9.4 设备指纹(device_fingerprint / fp_version) + +设备指纹由 SDK 在 `init()` 时自动生成,业务方**无需**手动传入。 + +#### 采集因子 + +| 平台 | 参与计算的因子 | +|------|--------------| +| Native(Android / iOS / 桌面) | `device_id`、`device`(类型)、`device_brand`、`device_model`、`system_name`、`system_version` | +| Web | `device`、`device_brand`、`device_model`、`system_name`、`system_version`、`user_agent`、屏幕分辨率(`WxH`)、浏览器语言、时区名称 | + +#### 生成规则 + +1. 过滤所有空因子,将剩余因子用 `|` 拼接后做 **SHA-256**,取 64 位十六进制字符串作为指纹。 +2. 若**所有因子均为空**(降级场景),改为生成随机 **UUID v4** 并持久化至本地(见缓存规则),后续从本地读取保证稳定性。 + +> `device_fingerprint` **不参与** `event_id` 的计算。 + +#### 缓存规则 + +- **内存缓存**:首次生成后缓存在内存中,同一应用生命周期内直接返回,不重复计算。 +- **持久化缓存**(仅降级 UUID 路径): + - Native:写入 `applicationDocumentsDirectory/analytics_fingerprint.txt`; + - Web:写入 `localStorage['analytics_fp']`。 +- 重新 `init()` 后若设备因子发生变化,可通过内部 `clearCache()` 重新计算(一般无需手动调用)。 + +#### 失败兜底 + +任何环节发生异常,均静默返回空字符串,事件照常上报;`device_fingerprint` 为空时,`fp_version` 字段亦不写入(无指纹则无需版本号)。 + +#### fp_version 说明 + +`fp_version` 标识当前指纹生成规则的版本(当前为 `"1"`),供服务端区分不同规则生成的指纹,支持跨版本数据对比分析。生成规则(因子组合或哈希算法)变更时版本号递增。 + +--- + +## 10. 调试工具 + +### 10.1 AnalyticsDebugBanner(悬浮调试条) + +在 `init()` 时传 `enableDebugBanner: true`,然后将 `AnalyticsDebugBanner()` 放入 widget 树顶部(通常是页面 Column 的第一个子节点或 Stack 的顶层): + +```dart +await AnalyticsSdk.instance.init( + // ... + enableDebugBanner: true, +); + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + body: Column( + children: [ + const AnalyticsDebugBanner(), // 无需任何参数 + Expanded(child: YourContent()), + ], + ), + ), + ); + } +} +``` + +调试条显示 7 个步骤:①域名配置 ②SDK初始化 ③域名列表 ④域名测速 ⑤上报地址 ⑥待上报队列 ⑦最近上报。release 包自动隐藏,无性能影响。 + +### 10.2 getDebugSteps / getDebugInfo + +```dart +final steps = AnalyticsSdk.instance.getDebugSteps(); +for (final step in steps) { + print('${step['name']}: ${step['status']} - ${step['detail']}'); +} + +final info = AnalyticsSdk.instance.getDebugInfo(); +print(info['inited']); +print(info['queueLength']); +print(info['reportUrl']); +``` + +### 10.3 validateEncryptedConfig + +```dart +final result = AnalyticsSdk.instance.validateEncryptedConfig(encryptedString); +if (result['success'] == true) { + print('域名数量: ${result['domainCount']}'); + print('事件类型数量: ${result['eventCount']}'); +} else { + print('验证失败: ${result['error']}'); +} +``` + +仅 debug 包可用,release 包返回错误。 + +### 10.4 Logger + +```dart +import 'package:analytics_sdk/utils/logger.dart'; + +Logger.enabled = false; // 关闭所有日志 +``` + +--- + +## 11. 高级用法 + +### 11.1 延迟传入加密配置 + +```dart +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + await AnalyticsSdk.instance.init( + appId: 'your_app_id', + encryptedConfig: null, // SDK 尝试读取磁盘缓存域名 + deviceId: await getDeviceId(), + ); + + runApp(const MyApp()); + + // 异步获取,不阻塞 UI 启动 + _fetchAndApplyConfig(); +} + +Future _fetchAndApplyConfig() async { + try { + final config = await yourApiClient.fetchEncryptedConfig(); + await AnalyticsSdk.instance.refreshDomainConfig(encryptedConfig: config); + } catch (_) {} +} +``` + +### 11.2 获取公共参数(getParams) + +`getParams()` 可获取 SDK 当前全部公共参数,适合服务端拼装事件或埋点调试: + +```dart +// 获取全部参数 +final all = AnalyticsSdk.getParams(); + +// 只取指定字段 +final partial = AnalyticsSdk.getParams(['app_id', 'uid', 'device_id', 'device_fingerprint']); +``` + +返回 `Map`,包含:`app_id`、`channel`、`uid`、`user_type`、`sid`、`device`、`device_id`、`device_brand`、`device_model`、`user_agent`、`system_name`、`system_version`、`device_fingerprint`、`fp_version`。 + +如果 key 不存在,该 key 不会出现在返回 Map 中(静默忽略)。 + +### 11.3 服务端复用设备公共字段 + +```dart +final deviceFields = AnalyticsSdk.getDeviceCommonFields(); +// 包含:device, device_id, device_brand, device_model, +// user_agent, system_name, system_version, device_fingerprint +``` + +--- + +## 12. 完整集成示例 + +```dart +import 'package:flutter/material.dart'; +import 'package:analytics_sdk/analytics_sdk.dart'; +import 'package:analytics_sdk/config/sdk_config.dart'; +import 'package:analytics_sdk/enum/user_type_enum.dart'; +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:analytics_sdk/widget/global_click_wrapper.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + SdkConfig.configure( + uploadInterval: const Duration(seconds: 5), + autoUploadThreshold: 10, + ); + + PageNameMapper.addMappings({ + 'home': '首页', + 'video_detail': '视频详情页', + 'user_center': '个人中心', + 'search': '搜索页', + }); + + await AnalyticsSdk.instance.init( + appId: 'your_app_id', + encryptedConfig: 'YOUR_BASE64_ENCRYPTED_CONFIG', + deviceId: 'device_unique_id', + channel: 'google_play', + uid: '', + enableDebugBanner: true, + ); + + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return GlobalClickWrapper( + child: MaterialApp( + navigatorObservers: [ + AnalyticsSdk.instance.pageObserver, + ], + initialRoute: '/', + routes: { + '/': (context) => const HomePage(), + '/video_detail': (context) => const VideoDetailPage(), + }, + ), + ); + } +} + +// ——— 登录场景 ——— +void onLoginSuccess(String userId, bool isVip) { + AnalyticsSdk.setUserIdAndType( + userId: userId, + userTypeEnum: isVip ? UserTypeEnum.vip : UserTypeEnum.normal, + ); +} + +// ——— 视频播放场景 ——— +void onVideoPlay(String videoId, String title) { + AnalyticsSdk.instance.track(VideoEvent( + videoId: videoId, + videoTitle: title, + videoTypeId: 'action', + videoTypeName: '动作片', + videoTagKey: 'hot', + videoTagName: '热门', + videoDuration: 7200, + playDuration: 0, + playProgress: 0, + videoBehavior: VideoEventEnum.VIDEO_PLAY, + )); +} + +// ——— 广告展示场景 ——— +void onAdsRendered(List adIds) { + AnalyticsSdk.instance.track(AdImpressionEvent( + pageKey: 'home', + pageName: '首页', + adSlotKey: 'home_feed_ad', + adSlotName: '首页信息流广告', + adId: adIds.join(','), + adType: 'feed', + )); +} + +// ——— 应用退出场景 ——— +Future onAppExit() async { + await AnalyticsSdk.instance.flush(); + await AnalyticsSdk.instance.dispose(); +} +``` diff --git a/analytics_sdk/analysis_options.yaml b/analytics_sdk/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/analytics_sdk/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/analytics_sdk/android/.gitignore b/analytics_sdk/android/.gitignore new file mode 100644 index 0000000..161bdcd --- /dev/null +++ b/analytics_sdk/android/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx diff --git a/analytics_sdk/android/build.gradle b/analytics_sdk/android/build.gradle new file mode 100644 index 0000000..8c594be --- /dev/null +++ b/analytics_sdk/android/build.gradle @@ -0,0 +1,64 @@ +group = "com.example.analytics_sdk" +version = "1.0-SNAPSHOT" + +// Flutter plugins are built within the host application's Gradle process. +// Do not pin AGP or Kotlin versions here: Flutter 3.0 hosts still use +// Gradle 6 / AGP 4, while newer hosts use AGP 8. +rootProject.allprojects { + repositories { + google() + mavenCentral() + maven { url 'https://jitpack.io' } + maven { url 'https://developer.huawei.com/repo/' } + maven { url 'https://developer.honor.com/repo/' } + } +} + +apply plugin: "com.android.library" +apply plugin: "kotlin-android" + +android { + if (project.android.hasProperty("namespace")) { + namespace = "com.example.analytics_sdk" + } + + // AGP 4.x understands compileSdkVersion; AGP 7/8 retains it as a + // compatible (deprecated) alias. + compileSdkVersion 34 + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } + + sourceSets { + main.java.srcDirs += "src/main/kotlin" + test.java.srcDirs += "src/test/kotlin" + } + + defaultConfig { + minSdkVersion 21 + } + + testOptions { + unitTests.all { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen {false} + showStandardStreams = true + } + } + } +} + +dependencies { + testImplementation("org.jetbrains.kotlin:kotlin-test") + testImplementation("org.mockito:mockito-core:5.0.0") + implementation 'com.github.gzu-liyujiang:Android_CN_OAID:4.2.16' +} diff --git a/analytics_sdk/android/gradle/wrapper/gradle-wrapper.properties b/analytics_sdk/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..9355b41 --- /dev/null +++ b/analytics_sdk/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/analytics_sdk/android/settings.gradle b/analytics_sdk/android/settings.gradle new file mode 100644 index 0000000..aedd5d4 --- /dev/null +++ b/analytics_sdk/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'analytics_sdk' diff --git a/analytics_sdk/android/src/main/AndroidManifest.xml b/analytics_sdk/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3bee847 --- /dev/null +++ b/analytics_sdk/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/analytics_sdk/android/src/main/kotlin/com/example/analytics_sdk/AnalyticsSdkPlugin.kt b/analytics_sdk/android/src/main/kotlin/com/example/analytics_sdk/AnalyticsSdkPlugin.kt new file mode 100644 index 0000000..a72d0ff --- /dev/null +++ b/analytics_sdk/android/src/main/kotlin/com/example/analytics_sdk/AnalyticsSdkPlugin.kt @@ -0,0 +1,491 @@ +package com.example.analytics_sdk + +import android.app.ActivityManager +import android.app.Application +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.hardware.Sensor +import android.hardware.SensorEvent +import android.hardware.SensorEventListener +import android.hardware.SensorManager +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build +import android.os.Handler +import android.os.Looper +import android.os.StatFs +import android.provider.Settings +import android.telephony.TelephonyManager +import android.util.Log +import com.github.gzuliyujiang.oaid.DeviceID +import com.github.gzuliyujiang.oaid.DeviceIdentifier +import com.github.gzuliyujiang.oaid.IGetter +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import io.flutter.plugin.common.MethodChannel.Result +import io.flutter.plugin.common.EventChannel +import java.io.File +import java.util.TimeZone + +class AnalyticsSdkPlugin: FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler { + private lateinit var channel : MethodChannel + private var proximityChannel: EventChannel? = null + private lateinit var context: Context + private val mainHandler = Handler(Looper.getMainLooper()) + private var proximityEventSink: EventChannel.EventSink? = null + private var proximityListener: SensorEventListener? = null + + private companion object { + private const val LOG_TAG = "AnalyticsSdk" + private const val LOG_CHUNK_SIZE = 1000 + } + + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + context = flutterPluginBinding.applicationContext + registerDeviceIdentifier() + channel = MethodChannel(flutterPluginBinding.binaryMessenger, "analytics_sdk") + channel.setMethodCallHandler(this) + proximityChannel = EventChannel( + flutterPluginBinding.binaryMessenger, + "analytics_sdk/proximity" + ) + proximityChannel?.setStreamHandler(this) + } + + override fun onMethodCall(call: MethodCall, result: Result) { + when (call.method) { + "getPlatformVersion" -> result.success("Android ${Build.VERSION.RELEASE}") + "getDeviceId" -> result.success(deviceId()) + "getSdkInitPlatformFields" -> collectSdkInitPlatformFields(result) + else -> result.notImplemented() + } + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + stopProximityEvents() + proximityChannel?.setStreamHandler(null) + proximityChannel = null + channel.setMethodCallHandler(null) + } + + override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { + stopProximityEvents() + proximityEventSink = events + startProximityEvents(events) + } + + override fun onCancel(arguments: Any?) { + stopProximityEvents() + } + + private fun collectSdkInitPlatformFields(result: Result) { + collectAdvertisingIdFields { advertisingIdFields -> + result.success(getSdkInitPlatformFields(advertisingIdFields)) + } + } + + private fun getSdkInitPlatformFields( + advertisingIdFields: Map + ): Map { + val fields = mutableMapOf() + + fields["package_name"] = context.packageName + fields["timezone"] = TimeZone.getDefault().id + fields["brand"] = unknownIfBlank(Build.BRAND) + fields["manufacturer"] = unknownIfBlank(Build.MANUFACTURER) + fields["model"] = unknownIfBlank(Build.MODEL) + fields["abi"] = unknownIfBlank(Build.SUPPORTED_ABIS.firstOrNull()) + fields["build_hardware"] = unknownIfBlank(Build.HARDWARE) + fields["build_product"] = unknownIfBlank(Build.PRODUCT) + fields["build_fingerprint"] = unknownIfBlank(Build.FINGERPRINT) + + fields.putAll(telephonyFields()) + fields.putAll(hardwareCapabilityFields()) + fields.putAll(storageFields()) + fields.putAll(networkStateFields()) + fields.putAll(androidIdFields()) + fields.putAll(advertisingIdFields) + fields.putAll(debugStateFields()) + fields.putAll(sensorFields()) + + fields["install_source"] = installSource() + fields["is_rooted"] = rootedState() + fields["has_baseband"] = hasBaseband() + + return fields + } + + private fun hardwareCapabilityFields(): Map { + val fields = mutableMapOf( + "cpu_cores" to -1, + "device_memory_gb" to -1, + "touch_support" to -1, + "max_touch_points" to -1 + ) + + try { + fields["cpu_cores"] = Runtime.getRuntime().availableProcessors().coerceAtLeast(1) + } catch (_: Throwable) { + } + + try { + val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager + if (manager != null) { + val memoryInfo = ActivityManager.MemoryInfo() + manager.getMemoryInfo(memoryInfo) + fields["device_memory_gb"] = bytesToGb(memoryInfo.totalMem) + } + } catch (_: Throwable) { + } + + try { + val packageManager = context.packageManager + val hasTouch = packageManager.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN) + fields["touch_support"] = if (hasTouch) 1 else 0 + fields["max_touch_points"] = when { + packageManager.hasSystemFeature( + PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND + ) -> 5 + packageManager.hasSystemFeature( + PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT + ) -> 2 + packageManager.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH) -> 2 + hasTouch -> 1 + else -> 0 + } + } catch (_: Throwable) { + } + + return fields + } + + private fun telephonyFields(): Map { + val fields = mutableMapOf( + "carrier" to "", + "mcc" to "unknown", + "mnc" to "unknown", + "has_sim" to -1 + ) + try { + val manager = context.getSystemService(Context.TELEPHONY_SERVICE) as? TelephonyManager + ?: return fields + fields["carrier"] = manager.simOperatorName ?: "" + val operator = manager.simOperator ?: "" + if (operator.length >= 3) fields["mcc"] = operator.substring(0, 3) + if (operator.length > 3) fields["mnc"] = operator.substring(3) + fields["has_sim"] = when (manager.simState) { + TelephonyManager.SIM_STATE_READY -> 1 + TelephonyManager.SIM_STATE_ABSENT -> 0 + else -> -1 + } + } catch (_: Throwable) { + } + return fields + } + + private fun storageFields(): Map { + return try { + val stat = StatFs(context.applicationInfo.dataDir) + mapOf( + "storage" to bytesToGb(stat.totalBytes), + "available_storage" to bytesToGb(stat.availableBytes) + ) + } catch (_: Throwable) { + mapOf("storage" to -1, "available_storage" to -1) + } + } + + private fun networkStateFields(): Map { + val fields = mutableMapOf( + "vpn_enabled" to -1, + "proxy_enabled" to -1 + ) + try { + val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager + val capabilities = manager?.getNetworkCapabilities(manager.activeNetwork) + if (capabilities == null) return fields + fields["vpn_enabled"] = + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) 1 else 0 + } catch (_: Throwable) { + } + try { + val proxyHost = System.getProperty("http.proxyHost")?.trim().orEmpty() + fields["proxy_enabled"] = if (proxyHost.isNotEmpty()) 1 else 0 + } catch (_: Throwable) { + } + return fields + } + + private fun androidIdFields(): Map { + return try { + val androidId = Settings.Secure.getString( + context.contentResolver, + Settings.Secure.ANDROID_ID + )?.trim().orEmpty() + if (androidId.isNotEmpty()) { + mapOf( + "android_id" to hashIdentifier(androidId), + "android_id_o" to androidId, + "android_id_status" to "available" + ) + } else { + mapOf( + "android_id" to "", + "android_id_o" to "", + "android_id_status" to "unavailable" + ) + } + } catch (_: Throwable) { + mapOf( + "android_id" to "", + "android_id_o" to "", + "android_id_status" to "unknown" + ) + } + } + + private fun collectAdvertisingIdFields(onReady: (Map) -> Unit) { + val clientIdValue = resolveClientIdRaw() + val rawGuid = guid() + val fields = mutableMapOf( + "oaid" to hashIdentifier(clientIdValue), + "oaid_o" to clientIdValue, + "aaid" to "", + "aaid_o" to "", + "guid" to hashIdentifier(rawGuid), + "guid_o" to rawGuid + ) + val lock = Any() + var completed = false + + fun finishAaid(rawValue: String?) { + val aaidValue = rawValue?.trim().orEmpty() + val snapshot = synchronized(lock) { + if (completed) { + null + } else { + completed = true + fields["aaid"] = hashIdentifier(aaidValue) + fields["aaid_o"] = aaidValue + fields.toMap() + } + } + snapshot?.let { mainHandler.post { onReady(it) } } + } + + mainHandler.postDelayed({ + finishAaid("") + }, 1500L) + + try { + DeviceID.getByGms(context, object : IGetter { + override fun onOAIDGetComplete(result: String?) { + finishAaid(result) + } + + override fun onOAIDGetError(error: Exception?) { + finishAaid("") + } + }) + } catch (_: Throwable) { + finishAaid("") + } + } + + private fun guid(): String { + return try { + DeviceIdentifier.getGUID(context)?.trim().orEmpty() + } catch (_: Throwable) { + "" + } + } + + @Suppress("DEPRECATION") + private fun resolveClientIdRaw(): String { + return ClientIdentifierResolver.resolve( + { DeviceIdentifier.getIMEI(context) }, + { DeviceIdentifier.getOAID(context) }, + { DeviceIdentifier.getAndroidID(context) }, + { DeviceIdentifier.getGUID(context) } + ) + } + + private fun deviceId(): String { + return hashIdentifier(resolveClientIdRaw()) + } + + private fun hashIdentifier(value: String?): String { + val text = value?.trim().orEmpty() + if (text.isEmpty()) return "" + return DeviceID.calculateHash(text, "MD5") + } + + private fun registerDeviceIdentifier() { + val application = context.applicationContext as? Application + ?: return + try { + DeviceIdentifier.register(application) + } catch (_: Throwable) {} + } + + private fun debugStateFields(): Map { + val fields = mutableMapOf( + "developer_options_enabled" to -1, + "adb_enabled" to -1, + "usb_debug_enabled" to -1 + ) + try { + val developerOptions = Settings.Global.getInt( + context.contentResolver, + Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, + 0 + ) + fields["developer_options_enabled"] = if (developerOptions == 1) 1 else 0 + } catch (_: Throwable) { + } + try { + val adb = Settings.Global.getInt( + context.contentResolver, + Settings.Global.ADB_ENABLED, + 0 + ) + val enabled = if (adb == 1) 1 else 0 + fields["adb_enabled"] = enabled + fields["usb_debug_enabled"] = enabled + } catch (_: Throwable) { + } + return fields + } + + private fun sensorFields(): Map { + val fields = mutableMapOf( + "sensor_count_supported" to -1, + "has_accelerometer" to -1, + "has_gyroscope" to -1, + "has_magnetometer" to -1, + "has_light_sensor" to -1, + "has_proximity_sensor" to -1 + ) + try { + val manager = context.getSystemService(Context.SENSOR_SERVICE) as? SensorManager + ?: return fields + fields["sensor_count_supported"] = manager.getSensorList(Sensor.TYPE_ALL).size + fields["has_accelerometer"] = + if (manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null) 1 else 0 + fields["has_gyroscope"] = + if (manager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null) 1 else 0 + fields["has_magnetometer"] = + if (manager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null) 1 else 0 + fields["has_light_sensor"] = + if (manager.getDefaultSensor(Sensor.TYPE_LIGHT) != null) 1 else 0 + fields["has_proximity_sensor"] = + if (manager.getDefaultSensor(Sensor.TYPE_PROXIMITY) != null) 1 else 0 + } catch (_: Throwable) { + } + return fields + } + + private fun startProximityEvents(events: EventChannel.EventSink?) { + try { + val manager = context.getSystemService(Context.SENSOR_SERVICE) as? SensorManager + if (manager == null) { + events?.error("proximity_unavailable", "SensorManager unavailable", null) + return + } + val sensor = manager.getDefaultSensor(Sensor.TYPE_PROXIMITY) + if (sensor == null) { + events?.error("proximity_unavailable", "Proximity sensor unavailable", null) + return + } + val listener = object : SensorEventListener { + override fun onSensorChanged(event: SensorEvent) { + val value = event.values.firstOrNull() ?: return + val isNear = value < sensor.maximumRange + mainHandler.post { + proximityEventSink?.success(if (isNear) 1 else 0) + } + } + + override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { + } + } + proximityListener = listener + manager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL) + } catch (e: Throwable) { + events?.error("proximity_error", e.message ?: "Proximity listener failed", null) + } + } + + private fun stopProximityEvents() { + try { + val manager = context.getSystemService(Context.SENSOR_SERVICE) as? SensorManager + val listener = proximityListener + if (manager != null && listener != null) { + manager.unregisterListener(listener) + } + } catch (_: Throwable) { + } + proximityListener = null + proximityEventSink = null + } + + private fun installSource(): String { + return try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + context.packageManager.getInstallSourceInfo(context.packageName) + .installingPackageName ?: "" + } else { + @Suppress("DEPRECATION") + context.packageManager.getInstallerPackageName(context.packageName) ?: "" + } + } catch (_: Throwable) { + "" + } + } + + private fun hasBaseband(): Int { + return try { + val radio = Build.getRadioVersion()?.trim().orEmpty() + if (radio.isEmpty() || radio.equals("unknown", ignoreCase = true)) -1 else 1 + } catch (_: Throwable) { + -1 + } + } + + private fun rootedState(): Int { + return try { + if (isRooted()) 1 else 0 + } catch (_: Throwable) { + -1 + } + } + + private fun isRooted(): Boolean { + val tags = Build.TAGS ?: "" + if (tags.contains("test-keys")) return true + val paths = listOf( + "/system/app/Superuser.apk", + "/sbin/su", + "/system/bin/su", + "/system/xbin/su", + "/data/local/xbin/su", + "/data/local/bin/su", + "/system/sd/xbin/su", + "/system/bin/failsafe/su", + "/data/local/su" + ) + return paths.any { File(it).exists() } + } + private fun bytesToGb(bytes: Long): Long { + if (bytes <= 0L) return -1L + return bytes / (1024L * 1024L * 1024L) + } + + private fun unknownIfBlank(value: String?): String { + val text = value?.trim().orEmpty() + return if (text.isEmpty()) "unknown" else text + } +} diff --git a/analytics_sdk/android/src/main/kotlin/com/example/analytics_sdk/ClientIdentifierResolver.kt b/analytics_sdk/android/src/main/kotlin/com/example/analytics_sdk/ClientIdentifierResolver.kt new file mode 100644 index 0000000..97a1175 --- /dev/null +++ b/analytics_sdk/android/src/main/kotlin/com/example/analytics_sdk/ClientIdentifierResolver.kt @@ -0,0 +1,27 @@ +package com.example.analytics_sdk + +import java.util.Locale + +internal object ClientIdentifierResolver { + fun resolve(vararg candidates: () -> String?): String { + for (candidate in candidates) { + val value = try { + candidate()?.trim().orEmpty() + } catch (_: Throwable) { + continue + } + if (isValid(value)) return value + } + return "" + } + + private fun isValid(value: String?): Boolean { + val text = value?.trim().orEmpty() + if (text.isEmpty()) return false + when (text.lowercase(Locale.ROOT)) { + "null", "unknown", "unkown" -> return false + } + val withoutHyphens = text.replace("-", "") + return withoutHyphens.isNotEmpty() && !withoutHyphens.all { it == '0' } + } +} diff --git a/analytics_sdk/android/src/test/kotlin/com/example/analytics_sdk/ClientIdentifierResolverTest.kt b/analytics_sdk/android/src/test/kotlin/com/example/analytics_sdk/ClientIdentifierResolverTest.kt new file mode 100644 index 0000000..93795e8 --- /dev/null +++ b/analytics_sdk/android/src/test/kotlin/com/example/analytics_sdk/ClientIdentifierResolverTest.kt @@ -0,0 +1,80 @@ +package com.example.analytics_sdk + +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class ClientIdentifierResolverTest { + @Test + fun resolve_returnsFirstValidCandidateAndStopsReading() { + val calls = mutableListOf() + + val result = ClientIdentifierResolver.resolve( + { calls += "imei"; " imei-123 " }, + { calls += "oaid"; "oaid-456" }, + { calls += "android"; "android-789" }, + { calls += "guid"; "guid-000" } + ) + + assertEquals("imei-123", result) + assertEquals(listOf("imei"), calls) + } + + @Test + fun resolve_skipsEverySupportedInvalidSentinel() { + val invalidValues = listOf( + null, + "", + " ", + "null", + "NULL", + "unknown", + "UnKnOwN", + "unkown", + "0", + "000000", + "---", + "000000-000-0000-00000000", + "00000000-0000-0000-0000-000000000000" + ) + + for (invalidValue in invalidValues) { + val result = ClientIdentifierResolver.resolve( + { invalidValue }, + { "android-id" } + ) + assertEquals("android-id", result, "invalidValue=$invalidValue") + } + } + + @Test + fun resolve_preservesValidHyphenatedIdentifier() { + val result = ClientIdentifierResolver.resolve( + { "00000000-0000-0000-0000-00000000000a" }, + { "fallback" } + ) + + assertEquals("00000000-0000-0000-0000-00000000000a", result) + } + + @Test + fun resolve_skipsThrowingCandidate() { + val result = ClientIdentifierResolver.resolve( + { throw IllegalStateException("imei unavailable") }, + { "oaid-123" } + ) + + assertEquals("oaid-123", result) + } + + @Test + fun resolve_returnsEmptyWhenEveryCandidateIsInvalid() { + val result = ClientIdentifierResolver.resolve( + { null }, + { "unknown" }, + { "0000-0000" }, + { "" } + ) + + assertEquals("", result) + } +} diff --git a/analytics_sdk/android/src/test/kotlin/com/example/data_plus_sdk/DataPlusSdkPluginTest.kt b/analytics_sdk/android/src/test/kotlin/com/example/data_plus_sdk/DataPlusSdkPluginTest.kt new file mode 100644 index 0000000..46c6cae --- /dev/null +++ b/analytics_sdk/android/src/test/kotlin/com/example/data_plus_sdk/DataPlusSdkPluginTest.kt @@ -0,0 +1,27 @@ +package com.example.analytics_sdk + +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import kotlin.test.Test +import org.mockito.Mockito + +/* + * This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation. + * + * Once you have built the plugin's example app, you can run these tests from the command + * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or + * you can run them directly from IDEs that support JUnit such as Android Studio. + */ + +internal class AnalyticsSdkPluginTest { + @Test + fun onMethodCall_getPlatformVersion_returnsExpectedValue() { + val plugin = AnalyticsSdkPlugin() + + val call = MethodCall("getPlatformVersion", null) + val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java) + plugin.onMethodCall(call, mockResult) + + Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE) + } +} diff --git a/analytics_sdk/ios/.gitignore b/analytics_sdk/ios/.gitignore new file mode 100644 index 0000000..034771f --- /dev/null +++ b/analytics_sdk/ios/.gitignore @@ -0,0 +1,38 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/Generated.xcconfig +/Flutter/ephemeral/ +/Flutter/flutter_export_environment.sh diff --git a/analytics_sdk/ios/Assets/.gitkeep b/analytics_sdk/ios/Assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/analytics_sdk/ios/Classes/DataPlusSdkPlugin.swift b/analytics_sdk/ios/Classes/DataPlusSdkPlugin.swift new file mode 100644 index 0000000..dc37144 --- /dev/null +++ b/analytics_sdk/ios/Classes/DataPlusSdkPlugin.swift @@ -0,0 +1,320 @@ +import Flutter +import UIKit +import AdSupport +import AppTrackingTransparency +import CFNetwork +import CoreTelephony +import Darwin + +public class AnalyticsSdkPlugin: NSObject, FlutterPlugin, FlutterStreamHandler { + private var proximityEventSink: FlutterEventSink? + private var proximityMonitoringWasEnabled = false + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "analytics_sdk", binaryMessenger: registrar.messenger()) + let instance = AnalyticsSdkPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + let proximityChannel = FlutterEventChannel( + name: "analytics_sdk/proximity", + binaryMessenger: registrar.messenger() + ) + proximityChannel.setStreamHandler(instance) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "getPlatformVersion": + result("iOS " + UIDevice.current.systemVersion) + case "getDeviceId": + result(UIDevice.current.identifierForVendor?.uuidString ?? "") + case "getSdkInitPlatformFields": + result(sdkInitPlatformFields()) + default: + result(FlutterMethodNotImplemented) + } + } + + public func onListen( + withArguments arguments: Any?, + eventSink events: @escaping FlutterEventSink + ) -> FlutterError? { + _ = onCancel(withArguments: nil) + proximityEventSink = events + proximityMonitoringWasEnabled = UIDevice.current.isProximityMonitoringEnabled + UIDevice.current.isProximityMonitoringEnabled = true + guard UIDevice.current.isProximityMonitoringEnabled else { + proximityEventSink = nil + return FlutterError( + code: "proximity_unavailable", + message: "Proximity monitoring unavailable", + details: nil + ) + } + NotificationCenter.default.addObserver( + self, + selector: #selector(proximityStateDidChange), + name: UIDevice.proximityStateDidChangeNotification, + object: UIDevice.current + ) + events(UIDevice.current.proximityState ? 1 : 0) + return nil + } + + public func onCancel(withArguments arguments: Any?) -> FlutterError? { + NotificationCenter.default.removeObserver( + self, + name: UIDevice.proximityStateDidChangeNotification, + object: UIDevice.current + ) + UIDevice.current.isProximityMonitoringEnabled = proximityMonitoringWasEnabled + proximityEventSink = nil + return nil + } + + @objc private func proximityStateDidChange() { + proximityEventSink?(UIDevice.current.proximityState ? 1 : 0) + } + + private func sdkInitPlatformFields() -> [String: Any] { + var fields: [String: Any] = [ + "language": normalizedLocaleIdentifier(), + "timezone": TimeZone.current.identifier, + "bundle_id": Bundle.main.bundleIdentifier ?? "", + "ios_device_model_raw": rawDeviceModel(), + "idfv": UIDevice.current.identifierForVendor?.uuidString ?? "", + "is_jailbroken": isJailbroken() ? 1 : 0, + "cpu_cores": ProcessInfo.processInfo.activeProcessorCount, + "device_memory_gb": bytesToGb(Int64(ProcessInfo.processInfo.physicalMemory)), + "touch_support": 1 + ] + + fields.merge(screenFields()) { _, new in new } + fields.merge(storageFields()) { _, new in new } + fields.merge(batteryFields()) { _, new in new } + fields.merge(telephonyFields()) { _, new in new } + fields.merge(advertisingFields()) { _, new in new } + fields.merge(networkStateFields()) { _, new in new } + + return fields + } + + private func normalizedLocaleIdentifier() -> String { + return Locale.current.identifier.replacingOccurrences(of: "_", with: "-") + } + + private func rawDeviceModel() -> String { + var systemInfo = utsname() + uname(&systemInfo) + let mirror = Mirror(reflecting: systemInfo.machine) + let identifier = mirror.children.reduce(into: "") { value, element in + guard let byte = element.value as? Int8, byte != 0 else { return } + value.append(String(UnicodeScalar(UInt8(byte)))) + } + return identifier + } + + private func screenFields() -> [String: Any] { + let screen = UIScreen.main + let nativeBounds = screen.nativeBounds + let scale = screen.scale + return [ + "screen_width": Int(nativeBounds.width.rounded()), + "screen_height": Int(nativeBounds.height.rounded()), + "screen_dpi": Int((scale * 160).rounded()), + "device_pixel_ratio": scale, + "orientation": interfaceOrientation() + ] + } + + private func interfaceOrientation() -> String { + if #available(iOS 13.0, *) { + let orientation = UIApplication.shared.connectedScenes + .compactMap { $0 as? UIWindowScene } + .first? + .interfaceOrientation + if orientation?.isLandscape == true { + return "landscape" + } + if orientation?.isPortrait == true { + return "portrait" + } + } else { + let orientation = UIApplication.shared.statusBarOrientation + if orientation.isLandscape { + return "landscape" + } + if orientation.isPortrait { + return "portrait" + } + } + return screenFallbackOrientation() + } + + private func screenFallbackOrientation() -> String { + let bounds = UIScreen.main.nativeBounds + return bounds.width >= bounds.height ? "landscape" : "portrait" + } + + private func storageFields() -> [String: Any] { + do { + let attributes = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory()) + let total = (attributes[.systemSize] as? NSNumber)?.int64Value ?? -1 + let free = (attributes[.systemFreeSize] as? NSNumber)?.int64Value ?? -1 + return [ + "storage": bytesToGb(total), + "available_storage": bytesToGb(free) + ] + } catch { + return ["storage": -1, "available_storage": -1] + } + } + + private func batteryFields() -> [String: Any] { + UIDevice.current.isBatteryMonitoringEnabled = true + let level = UIDevice.current.batteryLevel + let batteryLevel = level >= 0 ? Int((level * 100).rounded()) : -1 + let charging: Int + switch UIDevice.current.batteryState { + case .charging, .full: + charging = 1 + case .unplugged: + charging = 0 + default: + charging = -1 + } + return ["battery_level": batteryLevel, "is_charging": charging] + } + + private func telephonyFields() -> [String: Any] { + var fields: [String: Any] = [ + "carrier": "", + "mcc": "unknown", + "mnc": "unknown", + "has_sim": -1 + ] + + let info = CTTelephonyNetworkInfo() + var carrier: CTCarrier? + if #available(iOS 12.0, *) { + carrier = info.serviceSubscriberCellularProviders?.values.first + } else { + carrier = info.subscriberCellularProvider + } + + guard let carrier = carrier else { + fields["has_sim"] = 0 + return fields + } + + fields["carrier"] = carrier.carrierName ?? "" + fields["mcc"] = carrier.mobileCountryCode ?? "unknown" + fields["mnc"] = carrier.mobileNetworkCode ?? "unknown" + fields["has_sim"] = 1 + return fields + } + + private func advertisingFields() -> [String: Any] { + if #available(iOS 14, *) { + switch ATTrackingManager.trackingAuthorizationStatus { + case .authorized: + return [ + "idfa": ASIdentifierManager.shared().advertisingIdentifier.uuidString, + "att_status": "authorized", + "lat": 0 + ] + case .denied: + return ["idfa": "", "att_status": "denied", "lat": 1] + case .restricted: + return ["idfa": "", "att_status": "restricted", "lat": 1] + case .notDetermined: + return ["idfa": "", "att_status": "not_determined", "lat": -1] + @unknown default: + return ["idfa": "", "att_status": "unknown", "lat": -1] + } + } else { + let manager = ASIdentifierManager.shared() + let trackingEnabled = manager.isAdvertisingTrackingEnabled + return [ + "idfa": trackingEnabled ? manager.advertisingIdentifier.uuidString : "", + "att_status": "unknown", + "lat": trackingEnabled ? 0 : 1 + ] + } + } + + private func networkStateFields() -> [String: Any] { + return [ + "vpn_enabled": isVpnEnabled() ? 1 : 0, + "proxy_enabled": isProxyEnabled() + ] + } + + private func isProxyEnabled() -> Int { + guard + let settings = CFNetworkCopySystemProxySettings()?.takeRetainedValue() as? [String: Any] + else { + return -1 + } + + let httpEnabled = (settings[kCFNetworkProxiesHTTPEnable as String] as? NSNumber)?.intValue == 1 + let httpsEnabled = (settings["HTTPSEnable"] as? NSNumber)?.intValue == 1 + return httpEnabled || httpsEnabled ? 1 : 0 + } + + private func isVpnEnabled() -> Bool { + var addresses: UnsafeMutablePointer? + guard getifaddrs(&addresses) == 0, let firstAddress = addresses else { + return false + } + defer { freeifaddrs(firstAddress) } + + var pointer: UnsafeMutablePointer? = firstAddress + while pointer != nil { + if let name = pointer?.pointee.ifa_name { + let interface = String(cString: name) + if interface.hasPrefix("utun") || + interface.hasPrefix("ppp") || + interface.hasPrefix("ipsec") || + interface.hasPrefix("tap") || + interface.hasPrefix("tun") { + return true + } + } + pointer = pointer?.pointee.ifa_next + } + return false + } + + private func isJailbroken() -> Bool { + #if targetEnvironment(simulator) + return false + #else + let paths = [ + "/Applications/Cydia.app", + "/Library/MobileSubstrate/MobileSubstrate.dylib", + "/bin/bash", + "/usr/sbin/sshd", + "/etc/apt", + "/private/var/lib/apt/" + ] + if paths.contains(where: { FileManager.default.fileExists(atPath: $0) }) { + return true + } + do { + let path = "/private/analytics_sdk_jailbreak_test.txt" + try "test".write(toFile: path, atomically: true, encoding: .utf8) + try FileManager.default.removeItem(atPath: path) + return true + } catch { + return false + } + #endif + } + + private func bytesToGb(_ bytes: Int64) -> Int64 { + if bytes <= 0 { + return -1 + } + return bytes / (1024 * 1024 * 1024) + } +} diff --git a/analytics_sdk/ios/analytics_sdk.podspec b/analytics_sdk/ios/analytics_sdk.podspec new file mode 100644 index 0000000..3bba147 --- /dev/null +++ b/analytics_sdk/ios/analytics_sdk.podspec @@ -0,0 +1,23 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint analytics_sdk.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'analytics_sdk' + s.version = '0.0.1' + s.summary = 'A new Flutter project.' + s.description = <<-DESC +A new Flutter project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'Flutter' + s.platform = :ios, '12.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } + s.swift_version = '5.0' +end diff --git a/analytics_sdk/lib/analytics_sdk.dart b/analytics_sdk/lib/analytics_sdk.dart new file mode 100644 index 0000000..fa17738 --- /dev/null +++ b/analytics_sdk/lib/analytics_sdk.dart @@ -0,0 +1,1589 @@ +import 'dart:async'; +import 'dart:convert'; + +export 'widget/analytics_debug_banner.dart'; +export 'entity/analytics_tab.dart'; +export 'entity/ad_sdk_events.dart'; +export 'entity/app_page_item_event.dart'; +export 'entity/app_lifecycle_event.dart'; +export 'entity/app_session_summary_event.dart'; +export 'entity/network_state_change_event.dart'; +export 'extension/tab_analytics_extension.dart'; +export 'enum/page_route_enum.dart'; + +import 'package:analytics_sdk/config/api_config.dart'; +import 'package:analytics_sdk/config/sdk_config.dart'; +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/entity/ad_sdk_events.dart'; +import 'package:analytics_sdk/entity/ad_impression_event.dart'; +import 'package:analytics_sdk/entity/app_lifecycle_event.dart'; +import 'package:analytics_sdk/entity/app_session_summary_event.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; +import 'package:analytics_sdk/enum/user_type_enum.dart'; +import 'package:analytics_sdk/entity/sdk_init_event.dart'; +import 'package:analytics_sdk/manager/ad_impression_manager.dart'; +import 'package:analytics_sdk/manager/event_processor.dart'; +import 'package:analytics_sdk/manager/user_manager.dart'; +import 'package:analytics_sdk/manager/domain_manager.dart'; +import 'package:analytics_sdk/manager/event_type_config_manager.dart'; +import 'package:analytics_sdk/manager/session_manager.dart'; +import 'package:analytics_sdk/manager/sdk_activation_store.dart'; +import 'package:analytics_sdk/manager/sdk_init_payload_builder.dart'; +import 'package:analytics_sdk/manager/sdk_init_platform_fields.dart'; +import 'package:analytics_sdk/manager/session_behavior_manager.dart'; +import 'package:analytics_sdk/manager/session_behavior_monitor.dart'; +import 'package:analytics_sdk/manager/session_metrics_manager.dart'; +import 'package:analytics_sdk/manager/session_sensor_sampler.dart'; +import 'package:analytics_sdk/observer/app_lifecycle_observer.dart'; +import 'package:analytics_sdk/observer/app_lifecycle_web_unload_stub.dart' + if (dart.library.html) 'package:analytics_sdk/observer/app_lifecycle_web_unload_web.dart'; +import 'package:analytics_sdk/entity/app_page_view_event.dart'; +import 'package:analytics_sdk/entity/navigation_event.dart'; +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:analytics_sdk/manager/page_observer_registration_guard.dart'; +import 'package:analytics_sdk/observer/page_lifecycle_observer.dart'; +import 'package:analytics_sdk/utils/aes_gcm_util.dart'; +import 'package:analytics_sdk/utils/device_id_util.dart'; +import 'package:analytics_sdk/utils/device_fingerprint_util.dart'; +import 'package:analytics_sdk/utils/device_info_util.dart'; +import 'package:analytics_sdk/utils/environment_info_util.dart'; +import 'package:analytics_sdk/utils/event_validator.dart'; +import 'package:analytics_sdk/utils/logger.dart'; +import 'package:analytics_sdk/utils/widget_bridge.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +class AnalyticsSdk { + static final AnalyticsSdk instance = AnalyticsSdk._internal(); + + factory AnalyticsSdk() => instance; + + AnalyticsSdk._internal(); + + final EventProcessor _eventProcessor = EventProcessor(); + + /// 单调递增的导航序号,用于快速连续切换时的去重。 + /// 每次 [trackNavigation] 调用自增;回调触发时序号不匹配则丢弃。 + /// 不在 init() 重置:计数器单调递增,旧回调 generation 永远小于新调用,天然隔离。 + int _navigationGeneration = 0; + + /// 最近一次成功上报的导航 pageKey,用于拦截同 Tab 重复点击。 + String _lastReportedNavigationKey = ''; + + PageLifecycleObserver? _pageObserver; + PageLifecycleObserver? _sessionObserver; + String Function(String? actualRoute)? _realRouteResolver; + final PageObserverRegistrationGuard _pageObserverRegistrationGuard = + PageObserverRegistrationGuard(); + + PageLifecycleObserver _createPageObserver({ + required bool autoTrackPageViews, + }) { + return PageLifecycleObserver( + track: track, + getUserType: () => AnalyticsSdk.userTypeProvider(), + onPageExit: (pageKey) => AdImpressionManager.instance.clearPage(pageKey), + resolveRealRoute: _realRouteResolver, + autoTrackPageViews: autoTrackPageViews, + ); + } + + static bool _enableDebugBanner = false; + + /// 是否启用调试条(供 AnalyticsDebugBanner 读取) + static bool get enableDebugBanner => _enableDebugBanner; + + /// 防止 init() 并发调用(异步挂起期间重复调用时直接跳过) + bool _isInitializing = false; + + /// 当前进程内 sdk_init 只上报一次,匹配冷启动/Web 首次加载语义。 + bool _sdkInitReported = false; + + String _lastSessionSummarySid = ''; + + AppLifecycleWebUnloadDisposer? _webLifecycleUnloadDisposer; + + // ==================== 初始化 ==================== + + /// 初始化 SDK。 + /// + /// 本方法永远不会抛出异常,即使初始化失败也不影响应用运行。 + /// + /// **参数说明** + /// + /// - [appId]:应用 ID(必填) + /// - [channel]:渠道标识,可在初始化后通过 [setChannel] 更新 + /// - [uid]:用户 ID,未登录时可传空,登录后通过 [setUserIdAndType] 或 [setUid] 更新 + /// - [deviceId]:已废弃,SDK 不再信任外部传入 device_id,会内部生成 + /// - [appVersion]:应用版本号,格式 `1.0.0`,由接入方传入,可不传 + /// - [encryptedConfig]:加密配置字符串(Base64 编码 AES 密文),解密后为 + /// `{"domainList":["https://..."],"eventList":["event_type",...]}` 格式,可选 + /// + /// device、deviceBrand、deviceModel、userAgent、systemName、systemVersion + /// 由 SDK 内部通过 device_info_plus 自动检测,无需外部传入。 + Future init({ + required String appId, + required String? encryptedConfig, + String? channel, + String? uid, + @Deprecated('device_id 由 SDK 内部生成,外部传入值会被忽略') String? deviceId, + String? appVersion, + bool enableDebugBanner = false, + }) async { + if (_isInitializing) { + Logger.analyticsSdk('init() 正在进行中,跳过重复调用'); + return; + } + final initStartedAt = DateTime.now(); + _isInitializing = true; + _pageObserverRegistrationGuard.reset(); + try { + _enableDebugBanner = !kReleaseMode && enableDebugBanner; + WidgetBridge.register( + track: track, + getUserType: userTypeProvider, + enableDebugBanner: _enableDebugBanner, + getDebugSteps: getDebugSteps, + ); + + _eventProcessor.stopAutoUploadTimer(); + _eventProcessor.resetState(); + _lastSessionSummarySid = ''; + SessionMetricsManager.instance.reset(); + SessionBehaviorManager.instance.reset(); + SessionSensorSampler.instance.reset(); + SessionBehaviorMonitor.instance.start(); + + await DeviceInfoUtil.initialize(); + await EnvironmentInfoUtil.initialize(); + + final device = DeviceInfoUtil.deviceType; + final deviceBrand = DeviceInfoUtil.deviceBrand; + final deviceModel = DeviceInfoUtil.deviceModel; + final userAgent = DeviceInfoUtil.userAgent; + final systemName = DeviceInfoUtil.systemName; + final systemVersion = DeviceInfoUtil.systemVersion; + final sdkDeviceId = await DeviceIdUtil.getDeviceId(); + + SdkContext.configure( + appId: appId, + channel: channel, + uid: uid, + appVersion: appVersion, + device: device, + deviceId: sdkDeviceId, + deviceBrand: deviceBrand, + deviceModel: deviceModel, + userAgent: userAgent, + systemName: systemName, + systemVersion: systemVersion, + environmentFields: EnvironmentInfoUtil.fields, + ); + EnvironmentInfoUtil.startMonitoring(track: track); + + Logger.analyticsSdk('响应解密已启用(使用内置密钥)'); + + // re-init 时先重置会话,确保新一轮初始化生成新 session + SessionManager.instance.reset(); + _lastReportedNavigationKey = ''; + // 注入新 session 回调:新 session 开始时清空广告去重状态 + SessionManager.instance.onNewSession = () { + AdImpressionManager.instance.clear(); + SessionMetricsManager.instance.reset( + sid: SessionManager.instance.currentSessionId ?? '', + ); + final pageLifecycleObserver = _pageObserver ?? _sessionObserver; + final sessionStartedAt = SessionManager.instance.sessionStartTime; + if (pageLifecycleObserver != null && sessionStartedAt != null) { + pageLifecycleObserver.startCurrentPageStayForNewSession( + sessionStartedAt, + ); + } + SessionBehaviorManager.instance.reset( + sid: SessionManager.instance.currentSessionId ?? '', + ); + SessionSensorSampler.instance.reset( + sid: SessionManager.instance.currentSessionId ?? '', + ); + }; + SessionManager.instance.onSessionEnding = + (reason) => _trackSessionSummary(sessionEndReason: reason); + SessionManager.instance.initialize(); + // 注入 SID 提供者,避免 entity 层直接依赖 SessionManager + SdkContext.sidProvider = () { + return SessionManager.instance.getSessionId(); + }; + AppLifecycleObserver.instance.initialize(track: track); + + // 初始化设备指纹 + _prepareDeviceFingerprint(); + + await _applyEncryptedConfig( + encryptedConfig: encryptedConfig, + source: 'init', + ); + + try { + await _eventProcessor.initPersistence(); + } catch (e) { + Logger.analyticsSdk('持久化初始化失败,继续运行: $e'); + } + + try { + await _eventProcessor.loadCachedEvents(); + } catch (e) { + Logger.analyticsSdk('缓存加载失败,继续运行: $e'); + } + + await _trackSdkInit(initStartedAt: initStartedAt); + + _eventProcessor.startAutoUploadTimer(); + _schedulePageObserverRegistrationCheck(); + } catch (e) { + Logger.analyticsSdk('初始化异常: $e'); + } finally { + _isInitializing = false; + } + } + + void _schedulePageObserverRegistrationCheck() { + try { + WidgetsBinding.instance.addPostFrameCallback((_) { + _pageObserverRegistrationGuard.check( + pageObserverAttached: _pageObserver?.navigator != null, + sessionObserverAttached: _sessionObserver?.navigator != null, + ); + }); + // init() 发生在首帧之后时,主动请求一帧,避免接入检查长期不执行。 + WidgetsBinding.instance.ensureVisualUpdate(); + } catch (e) { + Logger.analyticsSdk('页面 Observer 接入检查注册失败: $e'); + } + } + + // ==================== UID / Channel 设置 ==================== + + /// 更新用户 ID 和用户类型(登录、切换账号时调用)。 + /// + /// [userId] 为空时表示未登录状态。 + /// 本方法永远不会抛出异常。 + static void setUserIdAndType( + {String userId = '', UserTypeEnum? userTypeEnum}) { + try { + SdkContext.setUid(userId); + UserManager.instance.updateUserType(userTypeEnum == null + ? UserTypeEnum.normal.label + : userTypeEnum.label); + Logger.analyticsSdk('用户ID已更新为: $userId'); + } catch (e) { + Logger.analyticsSdk('设置用户信息失败: $e'); + } + } + + /// 单独更新用户 ID(不影响用户类型)。 + /// + /// 适用于已知 userType 无变化、只需更新 uid 的场景(如静默登录)。 + /// 本方法永远不会抛出异常。 + static void setUid(String uid) { + try { + SdkContext.setUid(uid); + Logger.analyticsSdk('uid 已更新为: $uid'); + } catch (e) { + Logger.analyticsSdk('setUid 失败: $e'); + } + } + + /// 更新渠道标识(渠道变更时调用,后续事件自动带上最新值)。 + /// + /// 本方法永远不会抛出异常。 + static void setChannel(String channel) { + try { + SdkContext.setChannel(channel); + Logger.analyticsSdk('channel 已更新为: $channel'); + } catch (e) { + Logger.analyticsSdk('setChannel 失败: $e'); + } + } + + /// 登出:清空 uid 和 userType。 + /// + /// 本方法永远不会抛出异常。 + static void logoutUser() { + try { + SdkContext.setUid(''); + UserManager.instance.logout(); + Logger.analyticsSdk('用户已登出'); + } catch (e) { + Logger.analyticsSdk('用户登出失败: $e'); + } + } + + // ==================== 设备公共字段 ==================== + + /// 获取当前设备公共字段(供产品方在服务端上报事件时复用)。 + /// + /// 返回字段:sid、device、device_id、device_brand、device_model、 + /// user_agent、system_name、system_version、device_fingerprint、fp_version。 + /// 未配置的字段返回空字符串。 + /// + /// `sid` 为客户端 SDK 自动生成的会话 ID;服务端上报事件时应将其一并回传。 + /// + /// 本方法永远不会抛出异常。 + static Map getDeviceCommonFields() { + try { + return SdkContext.getDeviceCommonFields(); + } catch (e) { + Logger.analyticsSdk('getDeviceCommonFields 失败: $e'); + return {}; + } + } + + /// 获取 SDK 当前全部公共参数,或按 [keys] 指定只返回部分参数。 + /// + /// - 不传 [keys](或传 null):返回全部参数 + /// - 传入 key 列表:只返回命中的 key,不存在的 key 会被忽略 + /// + /// 可用 key:`app_id` `channel` `uid` `user_type` `sid` + /// `device` `device_id` `device_brand` `device_model` + /// `user_agent` `system_name` `system_version` + /// `device_fingerprint` `fp_version` `sdk_version` `app_version` + /// + /// `sid` 为客户端 SDK 自动生成的会话ID,服务端上报事件时需将其一并回传, + /// 以确保服务端事件与客户端事件归属同一会话。 + /// + /// 本方法永远不会抛出异常。 + static Map getParams([List? keys]) { + try { + final all = { + 'app_id': SdkContext.appId ?? '', + 'channel': SdkContext.channel ?? '', + 'uid': SdkContext.uid ?? '', + 'user_type': UserManager.instance.userType, + 'sid': SessionManager.instance.currentSessionId ?? '', + 'sdk_version': SdkContext.kSdkVersion, + 'app_version': SdkContext.appVersion ?? SdkContext.kDefaultAppVersion, + ...SdkContext.getDeviceCommonFields(), + }; + if (keys == null || keys.isEmpty) return all; + return { + for (final k in keys) + if (all.containsKey(k)) k: all[k] + }; + } catch (e) { + Logger.analyticsSdk('getParams 失败: $e'); + return {}; + } + } + + // ==================== 上报 ==================== + + /// 自动页面曝光模式的生命周期观察者。 + /// + /// 注册后既记录 Session 页面停留,也自动追踪所有命名路由的 + /// [AppPageViewEvent]。与 [sessionObserver] 必须二选一,不能同时注册。 + /// + /// ```dart + /// MaterialApp( + /// navigatorObservers: [AnalyticsSdk.instance.pageObserver], + /// ) + /// ``` + NavigatorObserver get pageObserver => + _pageObserver ??= _createPageObserver(autoTrackPageViews: true); + + /// 手动页面曝光模式的生命周期观察者。 + /// + /// 只记录 Session 页面停留,不自动上报 [AppPageViewEvent]。如果业务已手动 + /// 上报页面曝光,应在 [MaterialApp.navigatorObservers] 中注册此 observer。 + /// 与 [pageObserver] 必须二选一,不能同时注册。 + /// + /// ```dart + /// MaterialApp( + /// navigatorObservers: [AnalyticsSdk.instance.sessionObserver], + /// ) + /// ``` + NavigatorObserver get sessionObserver => + _sessionObserver ??= _createPageObserver(autoTrackPageViews: false); + + /// 注入路由解析回调,将实际路径(如 `/video/123`)转换为模板路径(`/video/:id`)。 + /// + /// 与 [PageNameMapper.addMappings] 配合使用,实现带参数路由的稳定 pageKey 归因。 + /// GoRouter 场景通常不需要注入(`settings.name` 即为完整路径); + /// GetX 等路由框架可能需要注入以获取模板路径。 + /// + /// 传入 `null` 可清除已注入的回调,恢复默认行为(直接使用 `settings.name`)。 + void setRealRouteResolver(String Function(String? actualRoute)? resolver) { + _realRouteResolver = resolver; + _pageObserver?.setRealRouteResolver(resolver); + _sessionObserver?.setRealRouteResolver(resolver); + } + + /// 手动上报导航事件,在用户点击导航菜单时调用。 + /// + /// **适用场景**:底部 Tab、顶部 Tab、侧边抽屉等导航菜单点击。 + /// 内部同时调用 [PageLifecycleObserver.recordNavigation],更新全局 currentPageKey + /// 及栈顶 effectivePageKey,确保后续 push 的 referrer 和 page_click 归属均正确。 + /// + /// **已接入 [BottomNavigationBar.withAnalytics] / [TabBar.withAnalytics] 时无需调用**, + /// 包装器内部已处理;仅在完全手动管理导航时使用。 + /// + /// 同时上报 [AppPageViewEvent] 和 [NavigationEvent],通过 + /// [WidgetsBinding.addPostFrameCallback] 在渲染完成后触发; + /// [pageLoadTime] 反映从调用到渲染完成的实际耗时。 + /// 快速连续切换时只上报最后一次目标,中间状态自动丢弃。 + /// + /// - [pageKey]:导航目标的唯一标识,建议使用 [AppPageEnum](如 `AppPageEnum.video.key`), + /// 也可传入自定义字符串;与 app_page_view 的 page_key 保持一致 + /// - [pageName]:导航目标的展示名称,建议使用 [AppPageEnum](如 `AppPageEnum.video.name`) + /// + /// 本方法永远不会抛出异常。 + void trackNavigation({ + required String pageKey, + required String pageName, + }) { + try { + PageNameMapper.addMapping(pageKey, pageName); + final referrerKey = PageLifecycleObserver.currentPageKey; + final referrerName = PageNameMapper.getPageName(referrerKey); + final tapTime = DateTime.now(); + PageLifecycleObserver.recordNavigation(pageKey); + final generation = ++_navigationGeneration; + + bool callbackExecuted = false; + Timer? loadTimer; + + void fireEvents() { + if (callbackExecuted) return; + callbackExecuted = true; + loadTimer?.cancel(); + if (generation != _navigationGeneration) return; + if (pageKey == _lastReportedNavigationKey) return; + _lastReportedNavigationKey = pageKey; + try { + final pageLoadTime = + DateTime.now().difference(tapTime).inMilliseconds; + track(AppPageViewEvent( + userType: AnalyticsSdk.userTypeProvider(), + pageKey: pageKey, + pageName: pageName, + referrerPageKey: referrerKey, + referrerPageName: referrerName, + currentPageKey: pageKey, + currentPageName: pageName, + pageLoadTime: pageLoadTime, + pageEnterTs: tapTime.millisecondsSinceEpoch ~/ 1000, + )); + track(NavigationEvent( + navigationKey: pageKey, + navigationName: pageName, + )); + Logger.analyticsSdk( + 'trackNavigation() 上报成功: pageKey=$pageKey, referrer=$referrerKey, pageLoadTime=${pageLoadTime}ms', + ); + } catch (e) { + Logger.analyticsSdk('trackNavigation() post-frame 上报异常,已安全处理: $e'); + } + } + + try { + WidgetsBinding.instance.addPostFrameCallback((_) => fireEvents()); + loadTimer = Timer(SdkConfig.pageLoadTimeout, fireEvents); + } catch (e) { + Logger.analyticsSdk('trackNavigation() WidgetsBinding 未初始化,降级同步上报: $e'); + fireEvents(); + } + } catch (e) { + Logger.analyticsSdk('trackNavigation() 异常,已安全处理: $e'); + } + } + + /// 手动同步当前页面状态,专用于 PageView + AutomaticKeepAliveClientMixin 场景。 + /// + /// [PageView] 内 Tab 切换不经过路由系统,[PageLifecycleObserver] 无法感知; + /// 在 `PageView.onPageChanged` 中调用此方法,确保后续 page_click 的 `page_key` + /// 归属到实际可见的子页面,而非父路由 key。 + /// + /// 内部调用 [PageLifecycleObserver.recordNavigation],同时更新 + /// [PageLifecycleObserver.currentPageKey] 和栈顶的 effectivePageKey; + /// pop 返回时会自动恢复到最后一次调用的 pageKey。 + /// + /// - [pageKey]:当前可见子页面的唯一标识,建议使用 [AppPageEnum](如 `AppPageEnum.videoList.key`), + /// 也可传入自定义字符串 + /// - [pageName]:当前可见子页面的展示名称(仅用于日志,不上报事件) + /// + /// 本方法永远不会抛出异常。 + void updateCurrentPage({ + required String pageKey, + required String pageName, + }) { + try { + PageLifecycleObserver.recordNavigation(pageKey); + Logger.analyticsSdk( + 'updateCurrentPage: pageKey=$pageKey, pageName=$pageName'); + } catch (e) { + Logger.analyticsSdk('updateCurrentPage() 异常,已安全处理: $e'); + } + } + + /// 手动上报任意事件(事件对象必须有 toJson() 方法)。 + /// + /// 本方法永远不会抛出异常,即使事件无效也不影响应用运行。 + void track(dynamic event) { + try { + unawaited(EnvironmentInfoUtil.refreshBatteryLevelIfNeeded()); + if (event == null) { + Logger.analyticsSdk('事件为 null,跳过'); + return; + } + + final adResult = _TrackPipeline.deduplicateAdImpression(event); + if (adResult == null) return; + event = adResult.processedEvent; + + final json = _TrackPipeline.serializeEvent(event); + if (json == null) return; + + final validated = _TrackPipeline.validateEvent(json); + if (validated == null) return; + + if (!_TrackPipeline.checkEventSize(validated)) return; + if (!_TrackPipeline.isEventTypeEnabled(validated)) return; + + final accepted = _eventProcessor.enqueueEvent(validated); + + if (accepted && + adResult.adsToMark != null && + adResult.adsToMark!.isNotEmpty) { + try { + AdImpressionManager.instance.markAsReportedBatch(adResult.adsToMark!, + pageKey: adResult.pageKey); + } catch (e) { + Logger.analyticsSdk('标记广告ID为已上报失败: $e'); + } + } + if (accepted && + adResult.sdkTraceToMark != null && + adResult.sdkTraceToMark!.isNotEmpty) { + try { + AdImpressionManager.instance + .markSdkTraceReported(adResult.sdkTraceToMark!); + } catch (e) { + Logger.analyticsSdk('标记广告 SDK 曝光 trace_id 失败: $e'); + } + } + } catch (e) { + Logger.analyticsSdk('track() 发生异常,已安全处理: $e'); + } + } + + /// 使用事件名和业务 payload 上报事件。 + /// + /// 公共字段与 event_id 由埋点 SDK 统一生成,调用方无需构造事件实体。 + /// 本方法永远不会抛出异常,即使参数无效也不影响应用运行。 + void trackEvent({ + required String eventName, + required Map payload, + }) { + try { + final normalizedEventName = eventName.trim(); + if (normalizedEventName.isEmpty) { + Logger.analyticsSdk('trackEvent() eventName 为空,跳过'); + return; + } + final payloadCopy = Map.from(payload); + if (normalizedEventName == EventType.adImpressionSdk.event) { + track(AdImpressionSdkEvent( + traceId: payloadCopy['trace_id']?.toString() ?? '', + requestId: payloadCopy['request_id']?.toString() ?? '', + adSlotKey: payloadCopy['ad_slot_key']?.toString() ?? '', + adId: payloadCopy['ad_id']?.toString() ?? '', + adType: payloadCopy['ad_type']?.toString() ?? '', + materialSize: payloadCopy['material_size']?.toString() ?? '', + )); + return; + } + final commonFields = SdkContext.generateCommonFields(normalizedEventName); + final sortedPayloadKeys = payloadCopy.keys.toList()..sort(); + final eventId = SdkContext.generateEventIdFromCommonFields( + commonFields, + [ + jsonEncode({ + for (final key in sortedPayloadKeys) key: payloadCopy[key], + }), + ], + ); + track({ + ...commonFields, + 'event_id': eventId, + 'payload': payloadCopy, + }); + } catch (e) { + Logger.analyticsSdk('trackEvent() 发生异常,已安全处理: $e'); + } + } + + Future _trackSdkInit({required DateTime initStartedAt}) async { + if (_sdkInitReported) { + Logger.analyticsSdk('sdk_init 当前进程已上报,跳过重复启动快照'); + return; + } + try { + final platformFields = { + ...await collectSdkInitPlatformFields(), + 'activation_time': await SdkActivationStore.getOrCreateActivationTime(), + }; + _applySdkInitEnvironmentOverrides(platformFields); + final commonFields = + SdkContext.generateCommonFields(EventType.sdkInit.event); + final sdkInitSnapshotFields = { + ...commonFields, + ...SdkContext.getEnvironmentSnapshotFields(), + }; + final payload = SdkInitPayloadBuilder.build( + commonFields: sdkInitSnapshotFields, + platformFields: platformFields, + isWeb: kIsWeb, + ); + final fingerprint = + DeviceFingerprintUtil.generateV3FromSdkInitPayload(payload); + if (fingerprint.isNotEmpty) { + SdkContext.configureGeneratedDeviceFingerprint(fingerprint); + } + final sdkInitEvent = SdkInitEvent(payload: payload); + track(sdkInitEvent); + _sdkInitReported = true; + _trackInitialAppLifecycle( + initStartedAt: initStartedAt, + platformFields: platformFields, + ); + _registerWebLifecycleUnload(initStartedAt); + Logger.analyticsSdk('sdk_init 上报已入队'); + } catch (e) { + Logger.analyticsSdk('sdk_init 上报失败,已安全处理: $e'); + } + } + + void _trackInitialAppLifecycle({ + required DateTime initStartedAt, + required Map platformFields, + }) { + try { + final durationMs = + DateTime.now().difference(initStartedAt).inMilliseconds; + if (kIsWeb) { + track(AppLifecycleEvent.webLoad( + pageUrl: platformFields['current_url']?.toString() ?? '', + pageLoadTimeMs: durationMs, + viewportWidth: _intField(platformFields['viewport_width'], -1), + viewportHeight: _intField(platformFields['viewport_height'], -1), + )); + } else { + track(AppLifecycleEvent.coldStart( + coldStartDurationMs: durationMs, + lastExitType: + platformFields['last_exit_type']?.toString() ?? 'unknown', + bootTime: _intField(platformFields['boot_time'], 0), + )); + } + } catch (e) { + Logger.analyticsSdk('app_lifecycle 初始事件上报失败,已安全处理: $e'); + track(AppLifecycleEvent.unknown(unknownReason: 'collect_error')); + } + } + + void _registerWebLifecycleUnload(DateTime pageEnterTime) { + try { + _webLifecycleUnloadDisposer?.call(); + _webLifecycleUnloadDisposer = registerAppLifecycleWebUnloadListener( + track: track, + pageEnterTime: pageEnterTime, + ); + } catch (e) { + Logger.analyticsSdk('Web 生命周期卸载监听注册失败,已安全处理: $e'); + } + } + + int _intField(dynamic value, int fallback) { + if (value is int) return value; + if (value is num) return value.round(); + return int.tryParse(value?.toString() ?? '') ?? fallback; + } + + void _trackSessionSummary({required String sessionEndReason}) { + final currentSid = SessionManager.instance.currentSessionId ?? ''; + if (currentSid.isEmpty || _lastSessionSummarySid == currentSid) return; + + final previousSidProvider = SdkContext.sidProvider; + try { + SdkContext.sidProvider = () => currentSid; + final summaryEvent = AppSessionSummaryEvent( + sessionEndReason: sessionEndReason, + endedAt: SessionManager.instance.sessionEndTimeForNotification ?? + DateTime.now(), + ); + final serialized = _TrackPipeline.serializeEvent(summaryEvent); + if (serialized == null) return; + + final validated = _TrackPipeline.validateEvent(serialized); + if (validated == null) return; + + final summaryJson = validated; + if (!_TrackPipeline.checkEventSize(summaryJson)) return; + if (!_TrackPipeline.isEventTypeEnabled(summaryJson)) return; + + final accepted = _eventProcessor.enqueueEvent(summaryJson); + if (accepted) { + _lastSessionSummarySid = currentSid; + } + } catch (e) { + Logger.analyticsSdk('app_session_summary 上报失败,已安全处理: $e'); + } finally { + SdkContext.sidProvider = previousSidProvider; + } + } + + /// Debug/QA only: 模拟旧 sid idle 超时,触发 app_session_summary 后创建新 sid。 + /// + /// Release 包中返回 false 且不做任何事。 + Future debugTriggerIdleSessionSummaryForQa({ + Duration idleFor = const Duration(minutes: 31), + }) async { + if (kReleaseMode) return false; + try { + final oldSid = SessionManager.instance.getSessionId(); + final armed = + SessionManager.instance.debugSetBackgroundElapsedForQa(idleFor); + if (!armed) return false; + SessionManager.instance.onAppForeground(); + await flush(); + return SessionManager.instance.currentSessionId != oldSid; + } catch (e) { + Logger.analyticsSdk('QA 触发 app_session_summary 失败: $e'); + return false; + } + } + + void _applySdkInitEnvironmentOverrides(Map platformFields) { + const keys = { + 'timezone', + 'package_name', + 'carrier', + 'network_type', + 'screen_width', + 'screen_height', + 'screen_dpi', + 'device_pixel_ratio', + 'orientation', + 'screen_brightness', + 'volume', + 'is_screen_on', + 'battery_level', + 'is_charging', + 'battery_state', + 'cpu_cores', + 'device_memory_gb', + 'touch_support', + 'max_touch_points', + }; + final overrides = {}; + for (final key in keys) { + final value = platformFields[key]; + if (_isUsefulSdkInitEnvironmentValue(value)) { + overrides[key] = value; + } + } + SdkContext.updateEnvironmentFields(overrides); + } + + bool _isUsefulSdkInitEnvironmentValue(dynamic value) { + if (value == null) return false; + if (value is num) return value >= 0; + final text = value.toString().trim(); + return text.isNotEmpty && text != 'unknown'; + } + + /// 强制立即上报。 + /// + /// 本方法永远不会抛出异常。 + Future flush() async { + try { + await _eventProcessor.flush(); + } catch (e) { + Logger.analyticsSdk('flush() 发生异常,已安全处理: $e'); + } + } + + /// 清理 SDK 资源(先尝试上报剩余事件,再释放资源)。 + /// + /// 本方法永远不会抛出异常。 + Future dispose() async { + try { + _trackSessionSummary(sessionEndReason: 'app_exit'); + + try { + await flush(); + } catch (e) { + Logger.analyticsSdk('dispose() 时上报失败: $e'); + } + + await _eventProcessor.disposeAsync(); + + try { + DomainManager.instance.dispose(); + } catch (e) { + Logger.analyticsSdk('销毁域名管理器失败: $e'); + } + + try { + EventTypeConfigManager.instance.dispose(); + } catch (e) { + Logger.analyticsSdk('销毁事件类型配置管理器失败: $e'); + } + + try { + _webLifecycleUnloadDisposer?.call(); + _webLifecycleUnloadDisposer = null; + } catch (e) { + Logger.analyticsSdk('销毁 Web 生命周期监听失败: $e'); + } + + try { + AppLifecycleObserver.instance.dispose(); + } catch (e) { + Logger.analyticsSdk('销毁应用生命周期观察者失败: $e'); + } + + try { + await EnvironmentInfoUtil.dispose(); + } catch (e) { + Logger.analyticsSdk('销毁环境字段监听失败: $e'); + } + + try { + await SessionSensorSampler.instance.dispose(); + } catch (e) { + Logger.analyticsSdk('销毁传感器采样器失败: $e'); + } + + try { + SessionBehaviorMonitor.instance.dispose(); + } catch (e) { + Logger.analyticsSdk('销毁行为监听器失败: $e'); + } + + try { + _pageObserver?.dispose(); + _sessionObserver?.dispose(); + _pageObserver = null; + _sessionObserver = null; + } catch (e) { + Logger.analyticsSdk('销毁页面生命周期观察者失败: $e'); + } + } catch (e) { + Logger.analyticsSdk('dispose() 发生异常: $e'); + } + } + + // ==================== 调试 ==================== + + /// 更新用户类型(登录、升级会员、切换账号时调用)。 + /// + /// 本方法永远不会抛出异常。 + void updateUserType(String newType) { + try { + UserManager.instance.updateUserType(newType); + } catch (e) { + Logger.analyticsSdk('更新用户类型失败: $e'); + } + } + + /// 在 init() 之后、需要更新配置时调用。 + /// + /// [encryptedConfig] 格式与 init() 中的同名参数一致。 + /// 本方法永远不会抛出异常。 + Future refreshDomainConfig({ + String? encryptedConfig, + }) async { + try { + if (encryptedConfig == null || encryptedConfig.isEmpty) { + Logger.analyticsSdk('refreshDomainConfig encryptedConfig 为空,忽略本次刷新', + level: LogLevel.warn); + return; + } + await _applyEncryptedConfig( + encryptedConfig: encryptedConfig, + source: 'refreshDomainConfig', + ); + } catch (e) { + Logger.analyticsSdk('refreshDomainConfig 异常: $e', level: LogLevel.warn); + } + } + + /// 测试包用:获取埋点调试信息 + Map getDebugInfo() { + try { + final steps = getDebugSteps(); + final lastStep = steps.isNotEmpty ? steps.last : {}; + return { + 'inited': steps.isNotEmpty && steps.length >= 2 + ? steps[1]['status'] ?? '未知' + : '未知', + 'queueLength': '${_eventProcessor.queueLength}', + 'reportUrl': _eventProcessor.debugReportUrl != null + ? (_eventProcessor.debugReportUrl!.length > 40 + ? '${_eventProcessor.debugReportUrl!.substring(0, 40)}...' + : _eventProcessor.debugReportUrl!) + : '未配置', + 'debugReportEventLogPath': + _eventProcessor.debugReportEventLogPath ?? '不可用', + 'lastStep': lastStep['name'] ?? '', + 'lastStatus': lastStep['status'] ?? '', + }; + } catch (e) { + Logger.analyticsSdk('getDebugInfo 异常,已安全处理: $e', level: LogLevel.warn); + return {}; + } + } + + /// 测试包用:获取埋点流程步骤 + List> getDebugSteps() { + try { + final dm = DomainManager.instance; + final ep = _eventProcessor; + final steps = >[]; + + final hasDomains = dm.reportDomains.isNotEmpty; + steps.add({ + 'name': '①域名配置', + 'status': hasDomains ? '✓已配置' : '✗未配置', + 'detail': hasDomains ? '由业务方传入' : '未传入 encryptedConfig', + }); + + final appIdOk = SdkContext.appId != null && SdkContext.appId!.isNotEmpty; + steps.add({ + 'name': '②SDK初始化', + 'status': appIdOk ? '✓已初始化' : '✗未初始化', + 'detail': appIdOk ? 'appId已设置' : '-', + }); + + final domains = dm.reportDomains; + steps.add({ + 'name': '③域名列表', + 'status': domains.isEmpty + ? (appIdOk ? '✗未配置或测速中' : '-') + : '✓已配置(${domains.length}个)', + 'detail': domains.isEmpty ? '-' : domains.join(','), + }); + + final fastest = dm.fastestDomain; + String speedStatus; + if (fastest != null && fastest.isNotEmpty) { + speedStatus = '✓已选'; + } else if (domains.isNotEmpty) { + speedStatus = '✗测速失败或进行中'; + } else { + speedStatus = '-'; + } + steps.add({ + 'name': '④域名测速', + 'status': speedStatus, + 'detail': fastest ?? '-', + }); + + final hasUrl = ep.debugReportUrl != null && ep.debugReportUrl!.isNotEmpty; + String urlDetail = '-'; + if (hasUrl && ep.debugReportUrl != null) { + try { + final uri = Uri.parse(ep.debugReportUrl!); + urlDetail = '${uri.host}${uri.path}'; + } catch (_) { + urlDetail = '已配置'; + } + } + steps.add({ + 'name': '⑤上报地址', + 'status': hasUrl ? '✓已就绪' : '✗未就绪', + 'detail': urlDetail, + }); + + steps.add({ + 'name': '⑥待上报队列', + 'status': '${ep.queueLength}条', + 'detail': ep.queueLength > 0 ? '有事件待上报' : '空', + }); + + String uploadStatus; + String uploadDetail = '-'; + if (ep.lastUploadTime == null) { + uploadStatus = '未尝试'; + } else if (ep.lastUploadSuccess == true) { + uploadStatus = '✓成功'; + uploadDetail = _DebugInspector.formatTime(ep.lastUploadTime); + } else { + uploadStatus = '✗失败'; + uploadDetail = ep.lastUploadError ?? '未知'; + } + steps.add({ + 'name': '⑦最近上报', + 'status': uploadStatus, + 'detail': uploadDetail, + }); + + return steps; + } catch (e) { + Logger.analyticsSdk('getDebugSteps 异常,已安全处理: $e', level: LogLevel.warn); + return []; + } + } + + /// 验证加密配置字符串格式(仅 debug 包可用) + /// + /// 支持以下解密后格式: + /// - JSON 数组:`["https://api.example.com", ...]`(域名列表或事件类型列表) + /// - JSON 对象含 `domainList`:`{"domainList":[...]}` + /// - JSON 对象含 `eventList`:`{"eventList":[...]}` + /// - JSON 对象含 `enabled_event_types`:`{"enabled_event_types":[...]}` + Map validateEncryptedConfig(String ciphertext) { + return _DebugInspector.validateEncryptedConfig(ciphertext); + } + + // ==================== 私有方法 ==================== + + /// 用户类型提供者 + static String Function() userTypeProvider = + () => UserManager.instance.userType; + + /// 准备设备指纹状态。 + /// + /// v3 指纹必须在 sdk_init payload 采集完整后计算,这里只清理旧缓存并 + /// 提前写入版本号,避免 re-init 复用旧设备参数。 + void _prepareDeviceFingerprint() { + try { + DeviceFingerprintUtil.clearCache(); + SdkContext.resetGeneratedDeviceFingerprint(); + Logger.analyticsSdk('设备指纹状态已准备,规则版本: ${DeviceFingerprintUtil.kVersion}'); + } catch (e) { + Logger.analyticsSdk('设备指纹状态准备失败,device_fingerprint 将为空: $e'); + } + } + + /// 解密并应用域名配置与事件类型配置(供 init() 和 refreshDomainConfig() 复用) + /// + /// [encryptedConfig] 解密后格式:`{"domainList":[...],"eventList":[...]}` + Future _applyEncryptedConfig({ + String? encryptedConfig, + required String source, + }) async { + final appId = SdkContext.appId ?? ''; + + if (encryptedConfig != null && encryptedConfig.isNotEmpty) { + Logger.analyticsSdk( + '$source 收到 encryptedConfig: length=${encryptedConfig.length}'); + DomainManager.instance.reset(); + + // ── 阶段 1:域名配置(独立 try-catch,异常不影响阶段 2)────────────── + List? parsedEventList; + try { + final plaintext = AesGcmUtil.decryptResponseAuto(encryptedConfig); + Logger.analyticsSdkDeveloperLog( + '$source encryptedConfig 解密成功,明文(敏感字段已脱敏): ${_redactEncryptedConfigForLog(plaintext)}', + ); + final raw = jsonDecode(plaintext); + + // 纯 JSON 数组:直接作为域名列表处理,不含 eventList + if (raw is List) { + final domains = raw + .map((e) => e.toString().trim()) + .where((e) => e.isNotEmpty) + .toList(); + if (domains.isNotEmpty) { + _startSpeedTest(domains, appId); + Logger.analyticsSdk( + '$source encryptedConfig 解析成功: type=List, domainList=${domains.length}, eventList=0'); + } else { + Logger.analyticsSdk('$source encryptedConfig 解析结果为空数组,尝试加载本地缓存', + level: LogLevel.warn); + await _applyDomainCache(appId); + } + // 纯数组格式不含 eventList,parsedEventList 保持 null(走缓存) + } else if (raw is! Map) { + Logger.analyticsSdk( + '$source encryptedConfig 解析格式异常:期望 Map,实际 ${raw.runtimeType},尝试加载本地缓存', + level: LogLevel.warn); + await _applyDomainCache(appId); + // parsedEventList 保持 null,走缓存逻辑(阶段 2 处理) + } else { + final decoded = raw; + + // 解析 domainList + final rawDomains = decoded['domainList']; + final domains = rawDomains is List + ? rawDomains + .map((e) => e.toString().trim()) + .where((e) => e.isNotEmpty) + .toList() + : []; + + if (domains.isNotEmpty) { + _startSpeedTest(domains, appId); + } else { + Logger.analyticsSdk('domainList 解析结果为空,尝试加载本地缓存', + level: LogLevel.warn); + await _applyDomainCache(appId); + } + + // 捕获 eventList 供阶段 2 使用 + final rawEvents = + decoded['eventList'] ?? decoded['enabled_event_types']; + if (rawEvents is List && rawEvents.isNotEmpty) { + parsedEventList = rawEvents.map((e) => e.toString()).toList(); + } + + _applyEventSignatureConfig(decoded); + Logger.analyticsSdk( + '$source encryptedConfig 解析成功: type=Map, domainList=${domains.length}, eventList=${parsedEventList?.length ?? 0}, eventSignature=${_hasEventSignatureConfig(decoded)}'); + } + } catch (e) { + Logger.analyticsSdk('$source encryptedConfig 解密/解析失败: $e,尝试加载本地缓存', + level: LogLevel.warn); + await _applyDomainCache(appId); + } + + // ── 阶段 2:事件类型配置(独立 try-catch,异常不触发第二轮测速)────── + try { + if (parsedEventList != null) { + await EventTypeConfigManager.instance + .initWithConfig(jsonEncode(parsedEventList)); + } else { + Logger.analyticsSdk('eventList 解析结果为空,尝试加载本地缓存', + level: LogLevel.warn); + await EventTypeConfigManager.instance.loadCachedConfig(); + } + } catch (e) { + Logger.analyticsSdk('eventList 初始化失败: $e,尝试加载本地缓存', + level: LogLevel.warn); + await EventTypeConfigManager.instance.loadCachedConfig(); + } + } else { + Logger.analyticsSdk('$source encryptedConfig 未传入,尝试加载本地缓存', + level: LogLevel.warn); + await _applyDomainCache(appId); + await EventTypeConfigManager.instance.loadCachedConfig(); + } + } + + String _redactEncryptedConfigForLog(String plaintext) { + try { + final decoded = jsonDecode(plaintext); + return jsonEncode(_redactSensitiveConfigValue(decoded)); + } catch (_) { + return plaintext; + } + } + + dynamic _redactSensitiveConfigValue(dynamic value) { + const sensitiveKeys = { + 'signKey', + 'eventSignKey', + 'event_signature_key', + 'event_sig_key', + 'hmacKey', + 'key', + }; + if (value is Map) { + return value.map((key, child) { + final keyText = key.toString(); + if (sensitiveKeys.contains(keyText)) { + return MapEntry(keyText, '***'); + } + return MapEntry(keyText, _redactSensitiveConfigValue(child)); + }); + } + if (value is List) { + return value.map(_redactSensitiveConfigValue).toList(); + } + return value; + } + + bool _hasEventSignatureConfig(Map decoded) { + if (_firstConfigString(decoded, const [ + 'signKey', + 'eventSignKey', + 'event_signature_key', + 'event_sig_key', + ]) != + null) { + return true; + } + final rawEventSignature = decoded['eventSignature']; + if (rawEventSignature is Map) { + final eventSignature = Map.from(rawEventSignature); + return _firstConfigString(eventSignature, const [ + 'signKey', + 'key', + 'hmacKey', + ]) != + null; + } + return false; + } + + void _applyEventSignatureConfig(Map decoded) { + try { + final directSignKey = _firstConfigString(decoded, const [ + 'signKey', + 'eventSignKey', + 'event_signature_key', + 'event_sig_key', + ]); + final directVersion = _firstConfigString(decoded, const [ + 'signVersion', + 'eventSigVersion', + 'event_sig_version', + ]); + + String? nestedSignKey; + String? nestedVersion; + final rawEventSignature = decoded['eventSignature']; + if (rawEventSignature is Map) { + final eventSignature = Map.from(rawEventSignature); + nestedSignKey = _firstConfigString(eventSignature, const [ + 'signKey', + 'key', + 'hmacKey', + ]); + nestedVersion = _firstConfigString(eventSignature, const [ + 'version', + 'signVersion', + 'eventSigVersion', + 'event_sig_version', + ]); + } + + final signKey = directSignKey ?? nestedSignKey; + if (signKey == null) return; + + SdkContext.configureEventSignature( + signKey: signKey, + version: directVersion ?? nestedVersion, + ); + } catch (e) { + Logger.analyticsSdk('签名配置解析失败,事件签名将保持当前状态: $e', level: LogLevel.warn); + } + } + + String? _firstConfigString( + Map decoded, + List keys, + ) { + for (final key in keys) { + final value = decoded[key]; + if (value is String && value.trim().isNotEmpty) { + return value.trim(); + } + } + return null; + } + + Future _applyDomainCache(String appId) async { + try { + final cachedDomains = await DomainManager.instance.loadCachedDomains(); + if (cachedDomains.isNotEmpty) { + Logger.analyticsSdk('使用缓存域名降级: $cachedDomains'); + _startSpeedTest(cachedDomains, appId); + } else { + Logger.analyticsSdk( + '无可用域名缓存,上报 URL 保持未配置,事件将在 refreshDomainConfig() 后上报', + level: LogLevel.warn); + } + } catch (e) { + Logger.analyticsSdk('加载域名缓存失败: $e', level: LogLevel.warn); + } + } + + /// 设置域名测速回调并启动测速(fire-and-forget) + void _startSpeedTest(List domains, String appId) { + DomainManager.instance.onFastestDomainChanged = (fastest) { + try { + if (fastest != null && fastest.isNotEmpty) { + final url = ApiConfig.getReportUrl(fastest, appId: appId); + _eventProcessor.updateReportUrl(url); + Logger.analyticsSdk('域名测速完成,已更新上报URL: $url'); + } + } catch (e) { + Logger.analyticsSdk('_startSpeedTest 回调异常,已安全处理: $e', + level: LogLevel.warn); + } + }; + DomainManager.instance.initWithDomains(domains); + } +} + +/// 广告展示去重结果(仅在 [AnalyticsSdk] 内部使用) +class _AdDedupeResult { + /// 经过去重处理后的事件(可能是过滤了部分 adId 的 [AdImpressionEvent],或原始事件) + final dynamic processedEvent; + + /// 成功入队后需要标记为已上报的 adId 字符串;非广告展示事件时为 null + final String? adsToMark; + + /// 广告所在页面的 pageKey,用于页面级去重;非广告事件时为空字符串 + final String pageKey; + + /// 成功入队后需要标记为已上报的广告 SDK trace_id。 + final String? sdkTraceToMark; + + const _AdDedupeResult( + this.processedEvent, + this.adsToMark, [ + this.pageKey = '', + this.sdkTraceToMark, + ]); +} + +/// track() 管道辅助方法(纯静态,不依赖 AnalyticsSdk 实例) +class _TrackPipeline { + /// 广告展示事件去重处理。返回 null 表示全部 adId 均已上报,应跳过整条事件。 + static _AdDedupeResult? deduplicateAdImpression(dynamic event) { + if (event is AdImpressionSdkEvent) { + return _deduplicateAdSdkImpression(event); + } + if (event is! AdImpressionEvent) { + return _AdDedupeResult(event, null); + } + final AdImpressionEvent adEvent = event; + try { + final unreportedAdIds = _getUnreportedAdIds(adEvent); + if (unreportedAdIds != null && unreportedAdIds.isEmpty) { + Logger.analyticsSdk('广告展示事件已上报,跳过: ${adEvent.adId}'); + return null; + } + if (unreportedAdIds != null) { + final originalAdIdsCount = adEvent.adId + .split(',') + .map((id) => id.trim()) + .where((id) => id.isNotEmpty) + .length; + if (unreportedAdIds.length < originalAdIdsCount) { + final filtered = _createFilteredAdEvent(adEvent, unreportedAdIds); + Logger.analyticsSdk( + '广告展示事件已过滤: 原始$originalAdIdsCount个ID,只上报${unreportedAdIds.length}个未上报的ID: ${filtered.adId}'); + return _AdDedupeResult(filtered, filtered.adId, filtered.pageKey); + } + } + return _AdDedupeResult(adEvent, adEvent.adId, adEvent.pageKey); + } catch (e) { + Logger.analyticsSdk('广告去重检查异常,跳过去重: $e'); + return _AdDedupeResult(adEvent, adEvent.adId, adEvent.pageKey); + } + } + + /// 广告 SDK 曝光以服务端下发的 trace_id 为单次投放标识,在同一 session 内去重。 + static _AdDedupeResult? _deduplicateAdSdkImpression( + AdImpressionSdkEvent event, + ) { + final traceId = event.traceId.trim(); + if (traceId.isEmpty) return _AdDedupeResult(event, null); + if (AdImpressionManager.instance.isSdkTraceReported(traceId)) { + Logger.analyticsSdk('广告 SDK 曝光已上报,跳过: $traceId'); + return null; + } + return _AdDedupeResult(event, null, '', traceId); + } + + /// 将事件对象序列化为 JSON Map。返回 null 表示序列化失败,应丢弃事件。 + static Map? serializeEvent(dynamic event) { + try { + final json = event is Map ? event : event.toJson(); + if (json == null) { + Logger.analyticsSdk('事件 toJson() 返回 null,跳过'); + return null; + } + return SdkContext.attachDeviceEnvIfNeeded(json); + } catch (e) { + Logger.analyticsSdk('事件序列化失败,跳过: $e'); + return null; + } + } + + /// 校验事件字段。返回 null 表示校验不通过,应丢弃事件。 + static Map? validateEvent(Map json) { + final validated = EventValidator.validate(json); + if (validated == null) { + Logger.analyticsSdk('事件校验不通过,已丢弃: ${json['event']}', + level: LogLevel.warn); + } + return validated; + } + + /// 检查单条事件大小。返回 false 表示超限应丢弃。 + static bool checkEventSize(Map json) { + try { + final encoded = jsonEncode(json); + final sizeBytes = utf8.encode(encoded).length; + if (sizeBytes > SdkConfig.maxSingleEventSize) { + final sizeKB = (sizeBytes / 1024).toStringAsFixed(1); + final limitKB = + (SdkConfig.maxSingleEventSize / 1024).toStringAsFixed(0); + Logger.analyticsSdk( + '事件过大 (${sizeKB}KB > ${limitKB}KB 限制),丢弃。event=${json['event']}', + level: LogLevel.warn); + return false; + } + return true; + } catch (e) { + Logger.analyticsSdk('事件大小检查失败,继续上报: $e'); + return true; + } + } + + /// 检查事件类型是否在配置中启用。返回 false 表示未启用应跳过。 + static bool isEventTypeEnabled(Map json) { + try { + final eventType = json['event'] as String?; + if (eventType != null && + !EventTypeConfigManager.instance.isEventTypeEnabled(eventType)) { + Logger.analyticsSdk('事件类型 $eventType 未在配置中启用,跳过上报'); + return false; + } + return true; + } catch (e) { + Logger.analyticsSdk('事件类型检查失败,继续上报: $e'); + return true; + } + } + + static List? _getUnreportedAdIds(AdImpressionEvent event) { + try { + if (event.adId.isEmpty) return null; + final trimmedAdId = event.adId.trim(); + if (trimmedAdId.isEmpty) return null; + return AdImpressionManager.instance + .getUnreportedAdIds(trimmedAdId, pageKey: event.pageKey); + } catch (e) { + Logger.analyticsSdk('广告去重检查异常,跳过: $e'); + return null; + } + } + + static AdImpressionEvent _createFilteredAdEvent( + AdImpressionEvent originalEvent, List unreportedIds) { + try { + final originalAdIds = originalEvent.adId + .split(',') + .map((id) => id.trim()) + .where((id) => id.isNotEmpty) + .toList(); + + List? originalCreativeIds; + if (originalEvent.creativeId.isNotEmpty) { + originalCreativeIds = + originalEvent.creativeId.split(',').map((id) => id.trim()).toList(); + } + + final filteredAdIds = []; + final filteredCreativeIds = []; + + for (int i = 0; i < originalAdIds.length; i++) { + if (unreportedIds.contains(originalAdIds[i])) { + filteredAdIds.add(originalAdIds[i]); + if (originalCreativeIds != null && i < originalCreativeIds.length) { + filteredCreativeIds.add(originalCreativeIds[i]); + } else { + filteredCreativeIds.add(''); + } + } + } + + return AdImpressionEvent( + pageKey: originalEvent.pageKey, + pageName: originalEvent.pageName, + adSlotKey: originalEvent.adSlotKey, + requestId: originalEvent.requestId, + adId: filteredAdIds.join(','), + materialSize: originalEvent.materialSize, + creativeId: originalEvent.creativeId.isNotEmpty + ? filteredCreativeIds.join(',') + : "", + adType: originalEvent.adType, + ); + } catch (e) { + Logger.analyticsSdk('创建过滤后的广告事件失败,使用原始事件: $e'); + return originalEvent; + } + } +} + +/// Debug 调试辅助方法(纯静态,不依赖 AnalyticsSdk 实例) +class _DebugInspector { + static String formatTime(DateTime? dt) { + if (dt == null) return '-'; + return '${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}:${dt.second.toString().padLeft(2, '0')}'; + } + + /// 验证加密配置字符串格式(仅 debug 包可用) + static Map validateEncryptedConfig(String ciphertext) { + if (kReleaseMode) { + return {'success': false, 'preview': null, 'error': 'release 包不可用'}; + } + try { + final plaintext = AesGcmUtil.decryptResponseAuto(ciphertext); + final raw = jsonDecode(plaintext); + + if (raw is List) { + if (raw.isNotEmpty) { + return { + 'success': true, + 'preview': raw[0].toString(), + 'count': raw.length, + 'error': null, + }; + } + final preview = + plaintext.length > 60 ? plaintext.substring(0, 60) : plaintext; + return { + 'success': false, + 'preview': preview, + 'error': '解密成功但列表为空(0 个元素),请检查配置', + }; + } + + if (raw is! Map) { + return { + 'success': false, + 'preview': + plaintext.length > 60 ? plaintext.substring(0, 60) : plaintext, + 'error': '解密成功但格式错误:应为 JSON 数组或对象,实际为 ${raw.runtimeType}', + }; + } + + final decoded = raw; + List? items; + if (decoded['domainList'] is List) { + items = decoded['domainList'] as List; + } else if (decoded['eventList'] is List) { + items = decoded['eventList'] as List; + } else if (decoded['enabled_event_types'] is List) { + items = decoded['enabled_event_types'] as List; + } + + if (items != null && items.isNotEmpty) { + return { + 'success': true, + 'preview': items[0].toString(), + 'count': items.length, + 'error': null, + }; + } + + final preview = + plaintext.length > 60 ? plaintext.substring(0, 60) : plaintext; + return { + 'success': false, + 'preview': preview, + 'error': '解密成功但格式无法识别(0 个元素),请检查 JSON 格式', + }; + } catch (e) { + return {'success': false, 'preview': null, 'error': '解密失败: $e'}; + } + } +} diff --git a/analytics_sdk/lib/analytics_sdk_web.dart b/analytics_sdk/lib/analytics_sdk_web.dart new file mode 100644 index 0000000..6f917f6 --- /dev/null +++ b/analytics_sdk/lib/analytics_sdk_web.dart @@ -0,0 +1,12 @@ +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; + +/// Analytics SDK 的 Web 平台插件入口。 +/// +/// Flutter Web 插件系统要求此文件存在,并实现 [registerWith]。 +/// SDK 的 Web 实现(设备信息、指纹等)位于 utils/ 目录下的 web 专属文件。 +class AnalyticsSdkWeb { + static void registerWith(Registrar registrar) { + // SDK 不使用 MethodChannel,无需注册通道。 + // Web 平台功能通过条件导入的工具类直接实现。 + } +} diff --git a/analytics_sdk/lib/config/api_config.dart b/analytics_sdk/lib/config/api_config.dart new file mode 100644 index 0000000..f6c2244 --- /dev/null +++ b/analytics_sdk/lib/config/api_config.dart @@ -0,0 +1,84 @@ +/// API 配置管理类:统一管理所有 API 端点路径 +class ApiConfig { + ApiConfig._(); + + // ==================== API 路径(固定不变)==================== + + /// 上报接口路径(固定) + static const String defaultReportPath = '/api/eventTracking/batchReport.json'; + + /// 上报接口路径 + static String get reportPath => defaultReportPath; + + // ==================== 配置方法 ==================== + + /// 构建完整的 URL + /// [baseDomain] 基础域名,如 'https://api.example.com' + /// [path] API 路径,如 '/api/analytics/report' + /// [appId] 应用ID(可选),如果提供会作为查询参数添加到URL中 + /// 返回完整的 URL,如 'https://api.example.com/api/analytics/report?appId=xxx' + static String buildUrl(String baseDomain, String path, {String? appId}) { + // 确保路径以 / 开头 + final normalizedPath = _normalizePath(path); + + // 解析基础域名 + final uri = Uri.parse(baseDomain); + final scheme = uri.scheme.isEmpty ? 'https' : uri.scheme; + final host = uri.host.isEmpty ? baseDomain : uri.host; + + // 保留非默认端口(如 :8080),避免 IP+端口 场景下端口丢失 + final defaultPort = scheme == 'https' ? 443 : 80; + final portSuffix = + (uri.port > 0 && uri.port != defaultPort) ? ':${uri.port}' : ''; + + // 保留 baseDomain 中的路径前缀(如 /v2),仅在 host 成功解析时使用 + // 去掉末尾 / 避免与 normalizedPath 拼出双斜杠 + final basePath = uri.host.isNotEmpty && uri.path.isNotEmpty && uri.path != '/' + ? (uri.path.endsWith('/') + ? uri.path.substring(0, uri.path.length - 1) + : uri.path) + : ''; + + // 构建完整 URL + String url = '$scheme://$host$portSuffix$basePath$normalizedPath'; + + // 如果提供了 appId,添加到查询参数 + if (appId != null && appId.isNotEmpty) { + url = _addQueryParameter(url, 'appId', appId); + } + + return url; + } + + /// 规范化路径:确保路径以 / 开头 + static String _normalizePath(String path) { + if (path.isEmpty) { + return '/'; + } + return path.startsWith('/') ? path : '/$path'; + } + + /// 获取上报接口 URL + /// [baseDomain] 基础域名 + /// [appId] 应用ID(可选,如果提供会作为查询参数) + static String getReportUrl(String baseDomain, {String? appId}) { + return buildUrl(baseDomain, reportPath, appId: appId); + } + + /// 添加查询参数到 URL + /// [url] 原始 URL + /// [key] 参数名 + /// [value] 参数值 + static String _addQueryParameter(String url, String key, String value) { + try { + final uri = Uri.parse(url); + final queryParams = Map.from(uri.queryParameters); + queryParams[key] = value; + return uri.replace(queryParameters: queryParams).toString(); + } catch (e) { + // 如果 URL 解析失败,直接拼接 + final separator = url.contains('?') ? '&' : '?'; + return '$url$separator$key=${Uri.encodeComponent(value)}'; + } + } +} diff --git a/analytics_sdk/lib/config/sdk_config.dart b/analytics_sdk/lib/config/sdk_config.dart new file mode 100644 index 0000000..98be36a --- /dev/null +++ b/analytics_sdk/lib/config/sdk_config.dart @@ -0,0 +1,334 @@ +/// SDK 配置管理类:统一管理所有 SDK 相关的配置常量 +class SdkConfig { + SdkConfig._(); + + // ==================== 默认值常量(单一来源)==================== + // 字段声明和 reset() 均引用这些常量,避免三处重复。 + + static const int _defaultMaxQueueSize = 5000; + static const int _defaultAutoUploadThreshold = 10; + static const int _defaultMaxBatchSize = 500; + static const int _defaultIsolateEncodingThreshold = 100; + static const Duration _defaultUploadInterval = Duration(seconds: 5); + static const Duration _defaultPageLoadTimeout = Duration(seconds: 5); + static const Duration _defaultClickThrottleDuration = + Duration(milliseconds: 300); + static const Duration _defaultConnectionTimeout = Duration(seconds: 10); + static const Duration _defaultReadTimeout = Duration(seconds: 30); + static const Duration _defaultSpeedTestTimeout = Duration(seconds: 5); + static const Duration _defaultConfigRequestTimeout = Duration(seconds: 10); + static const int _defaultMaxBackoffLevel = 5; + static const Duration _defaultBaseRetryDelay = Duration(seconds: 2); + static const Duration _defaultMaxRetryDelay = Duration(seconds: 60); + static const String _defaultCacheFileName = 'data_plus_events_cache.jsonl'; + static const String _defaultDebugReportEventLogFileName = + 'data_plus_report_events_debug.jsonl'; + static const bool _defaultEnableDebugReportLog = false; + static const String _defaultEventTypeConfigCacheFileName = + 'analytics_event_types_config.json'; + static const String _defaultDomainCacheFileName = + 'data_plus_domains_cache.json'; + static const int _defaultMaxCacheLines = 2000; + static const int _defaultMaxCacheBytes = 2 * 1024 * 1024; + static const int _defaultTombstoneCompactionThreshold = 200; + static const String _defaultTombstoneFileName = + 'data_plus_events_tombstone.txt'; + static const int _defaultMaxJsonSize = 5 * 1024 * 1024; + static const int _defaultMaxSingleEventSize = 300 * 1024; + static const int _defaultMaxConfigResponseSize = 1 * 1024 * 1024; + static const int _defaultMaxPageStackSize = 100; + static const int _defaultMaxAdImpressionCapacity = 10000; + static const int _defaultCacheBufferThreshold = 10; + static const Duration _defaultCacheFlushInterval = Duration(seconds: 5); + + // ==================== 事件队列配置 ==================== + + /// 事件队列最大容量限制,防止内存无限增长 + static int maxQueueSize = _defaultMaxQueueSize; + + /// 自动上报阈值:队列达到此数量时立即触发上报 + static int autoUploadThreshold = _defaultAutoUploadThreshold; + + /// 单次上报的最大事件数量限制,避免请求过大导致超时 + static int maxBatchSize = _defaultMaxBatchSize; + + /// JSON 编码的批次大小阈值,超过此大小使用 isolate 编码 + static int isolateEncodingThreshold = _defaultIsolateEncodingThreshold; + + // ==================== 定时器配置 ==================== + + /// 定时上报间隔(秒) + static Duration uploadInterval = _defaultUploadInterval; + + /// 页面加载超时时间(秒) + static Duration pageLoadTimeout = _defaultPageLoadTimeout; + + /// 点击事件节流时间(毫秒) + static Duration clickThrottleDuration = _defaultClickThrottleDuration; + + // ==================== 网络超时配置 ==================== + + /// HTTP 连接超时时间 + static Duration connectionTimeout = _defaultConnectionTimeout; + + /// HTTP 读取超时时间 + static Duration readTimeout = _defaultReadTimeout; + + /// HTTP 写入超时时间(与连接超时相同) + static Duration get writeTimeout => connectionTimeout; + + /// HTTP 总超时时间(连接 + 写入 + 读取) + static Duration get totalRequestTimeout => + connectionTimeout + writeTimeout + readTimeout; + + /// 域名测速超时时间 + static Duration speedTestTimeout = _defaultSpeedTestTimeout; + + /// 配置请求超时时间 + static Duration configRequestTimeout = _defaultConfigRequestTimeout; + + // ==================== 加密配置 ==================== + + /// AES-GCM 解密密钥(固定密钥,用于 decryptResponseAuto 等) + static const String decryptKey = + 'en1BNo0VBrN/zi+mI2LO7E9W40ehCBYwC+frBn8s3rQ'; + + // ==================== 重试配置 ==================== + + /// 最大退避级别 + static int maxBackoffLevel = _defaultMaxBackoffLevel; + + /// 基础延迟时间 + static Duration baseRetryDelay = _defaultBaseRetryDelay; + + /// 最大延迟时间 + static Duration maxRetryDelay = _defaultMaxRetryDelay; + + // ==================== 缓存配置 ==================== + + /// 事件缓存文件名 + static String cacheFileName = _defaultCacheFileName; + + /// Debug 包完整事件 JSONL 文件名(Native 平台 applicationDocumentsDirectory 下) + static String debugReportEventLogFileName = + _defaultDebugReportEventLogFileName; + + /// 是否启用调试事件日志。 + /// + /// 默认关闭;开启后才会本地记录上传事件完整 JSONL,并打印 accepted event 完整 JSON。 + static bool enableDebugReportLog = _defaultEnableDebugReportLog; + + /// 事件类型配置缓存文件名 + static String eventTypeConfigCacheFileName = + _defaultEventTypeConfigCacheFileName; + + /// 域名列表缓存文件名 + static String domainCacheFileName = _defaultDomainCacheFileName; + + /// 最大缓存行数限制,防止文件过大导致内存问题 + /// 默认 2000 行,避免在网络不稳定时生成过大的缓存文件 + static int maxCacheLines = _defaultMaxCacheLines; + + /// 缓存文件最大字节数限制,超过后从文件开头裁剪旧事件 + /// 默认 2MB + static int maxCacheBytes = _defaultMaxCacheBytes; + + /// tombstone 触发压缩的 event_id 数量阈值 + /// 默认 200:每积累 200 条已上报 ID 就压缩一次主缓存文件 + static int tombstoneCompactionThreshold = + _defaultTombstoneCompactionThreshold; + + /// tombstone 缓存文件名 + static String tombstoneFileName = _defaultTombstoneFileName; + + /// 持久化 buffer 攒批阈值(达到此数量立即写盘) + static int cacheBufferThreshold = _defaultCacheBufferThreshold; + + /// 持久化 buffer 定时刷写间隔 + static Duration cacheFlushInterval = _defaultCacheFlushInterval; + + // ==================== 请求大小限制 ==================== + + /// JSON 最大大小限制(5MB) + static int maxJsonSize = _defaultMaxJsonSize; + + /// 单条事件最大字节数限制,超过则丢弃(300KB) + static int maxSingleEventSize = _defaultMaxSingleEventSize; + + /// 配置接口响应体最大字节数限制(1MB),防止恶意/异常服务器返回超大数据导致 OOM + static int maxConfigResponseSize = _defaultMaxConfigResponseSize; + + // ==================== 容量配置 ==================== + + /// 页面栈最大容量 + static int maxPageStackSize = _defaultMaxPageStackSize; + + /// 广告去重最大容量 + static int maxAdImpressionCapacity = _defaultMaxAdImpressionCapacity; + + // ==================== 配置方法 ==================== + + /// 配置所有 SDK 参数 + /// + /// 示例: + /// ```dart + /// SdkConfig.configure( + /// maxQueueSize: 20000, + /// uploadInterval: Duration(seconds: 30), + /// connectionTimeout: Duration(seconds: 15), + /// ); + /// ``` + static void configure({ + int? maxQueueSize, + int? autoUploadThreshold, + int? maxBatchSize, + int? isolateEncodingThreshold, + Duration? uploadInterval, + Duration? pageLoadTimeout, + Duration? clickThrottleDuration, + Duration? connectionTimeout, + Duration? readTimeout, + Duration? speedTestTimeout, + Duration? configRequestTimeout, + int? maxBackoffLevel, + Duration? baseRetryDelay, + Duration? maxRetryDelay, + String? cacheFileName, + String? debugReportEventLogFileName, + bool? enableDebugReportLog, + String? eventTypeConfigCacheFileName, + String? domainCacheFileName, + int? maxCacheLines, + int? maxCacheBytes, + int? maxJsonSize, + int? maxSingleEventSize, + int? maxPageStackSize, + int? maxAdImpressionCapacity, + }) { + if (maxQueueSize != null && maxQueueSize > 0) { + SdkConfig.maxQueueSize = maxQueueSize; + } + if (autoUploadThreshold != null && autoUploadThreshold > 0) { + SdkConfig.autoUploadThreshold = autoUploadThreshold; + } + if (maxBatchSize != null && maxBatchSize > 0) { + SdkConfig.maxBatchSize = maxBatchSize; + } + if (isolateEncodingThreshold != null && isolateEncodingThreshold > 0) { + SdkConfig.isolateEncodingThreshold = isolateEncodingThreshold; + } + if (uploadInterval != null) { + SdkConfig.uploadInterval = uploadInterval; + } + if (pageLoadTimeout != null) { + SdkConfig.pageLoadTimeout = pageLoadTimeout; + } + if (clickThrottleDuration != null) { + SdkConfig.clickThrottleDuration = clickThrottleDuration; + } + if (connectionTimeout != null) { + SdkConfig.connectionTimeout = connectionTimeout; + } + if (readTimeout != null) { + SdkConfig.readTimeout = readTimeout; + } + if (speedTestTimeout != null) { + SdkConfig.speedTestTimeout = speedTestTimeout; + } + if (configRequestTimeout != null) { + SdkConfig.configRequestTimeout = configRequestTimeout; + } + if (maxBackoffLevel != null && maxBackoffLevel >= 0) { + SdkConfig.maxBackoffLevel = maxBackoffLevel; + } + if (baseRetryDelay != null) { + SdkConfig.baseRetryDelay = baseRetryDelay; + } + if (maxRetryDelay != null) { + SdkConfig.maxRetryDelay = maxRetryDelay; + } + if (cacheFileName != null && + cacheFileName.isNotEmpty && + !cacheFileName.contains('/') && + !cacheFileName.contains('\\') && + !cacheFileName.contains('..')) { + SdkConfig.cacheFileName = cacheFileName; + } + if (debugReportEventLogFileName != null && + debugReportEventLogFileName.isNotEmpty && + !debugReportEventLogFileName.contains('/') && + !debugReportEventLogFileName.contains('\\') && + !debugReportEventLogFileName.contains('..')) { + SdkConfig.debugReportEventLogFileName = debugReportEventLogFileName; + } + if (enableDebugReportLog != null) { + SdkConfig.enableDebugReportLog = enableDebugReportLog; + } + if (eventTypeConfigCacheFileName != null && + eventTypeConfigCacheFileName.isNotEmpty && + !eventTypeConfigCacheFileName.contains('/') && + !eventTypeConfigCacheFileName.contains('\\') && + !eventTypeConfigCacheFileName.contains('..')) { + SdkConfig.eventTypeConfigCacheFileName = eventTypeConfigCacheFileName; + } + if (domainCacheFileName != null && + domainCacheFileName.isNotEmpty && + !domainCacheFileName.contains('/') && + !domainCacheFileName.contains('\\') && + !domainCacheFileName.contains('..')) { + SdkConfig.domainCacheFileName = domainCacheFileName; + } + if (maxCacheLines != null && maxCacheLines > 0) { + SdkConfig.maxCacheLines = maxCacheLines; + } + if (maxCacheBytes != null && maxCacheBytes > 0) { + SdkConfig.maxCacheBytes = maxCacheBytes; + } + if (maxJsonSize != null && maxJsonSize > 0) { + SdkConfig.maxJsonSize = maxJsonSize; + } + if (maxSingleEventSize != null && maxSingleEventSize > 0) { + SdkConfig.maxSingleEventSize = maxSingleEventSize; + } + if (maxPageStackSize != null && maxPageStackSize > 0) { + SdkConfig.maxPageStackSize = maxPageStackSize; + } + if (maxAdImpressionCapacity != null && maxAdImpressionCapacity > 0) { + SdkConfig.maxAdImpressionCapacity = maxAdImpressionCapacity; + } + } + + /// 重置所有配置为默认值 + static void reset() { + maxQueueSize = _defaultMaxQueueSize; + autoUploadThreshold = _defaultAutoUploadThreshold; + maxBatchSize = _defaultMaxBatchSize; + isolateEncodingThreshold = _defaultIsolateEncodingThreshold; + uploadInterval = _defaultUploadInterval; + pageLoadTimeout = _defaultPageLoadTimeout; + clickThrottleDuration = _defaultClickThrottleDuration; + connectionTimeout = _defaultConnectionTimeout; + readTimeout = _defaultReadTimeout; + speedTestTimeout = _defaultSpeedTestTimeout; + configRequestTimeout = _defaultConfigRequestTimeout; + maxBackoffLevel = _defaultMaxBackoffLevel; + baseRetryDelay = _defaultBaseRetryDelay; + maxRetryDelay = _defaultMaxRetryDelay; + cacheFileName = _defaultCacheFileName; + debugReportEventLogFileName = _defaultDebugReportEventLogFileName; + enableDebugReportLog = _defaultEnableDebugReportLog; + eventTypeConfigCacheFileName = _defaultEventTypeConfigCacheFileName; + domainCacheFileName = _defaultDomainCacheFileName; + maxCacheLines = _defaultMaxCacheLines; + maxCacheBytes = _defaultMaxCacheBytes; + tombstoneCompactionThreshold = _defaultTombstoneCompactionThreshold; + tombstoneFileName = _defaultTombstoneFileName; + cacheBufferThreshold = _defaultCacheBufferThreshold; + cacheFlushInterval = _defaultCacheFlushInterval; + maxJsonSize = _defaultMaxJsonSize; + maxSingleEventSize = _defaultMaxSingleEventSize; + maxConfigResponseSize = _defaultMaxConfigResponseSize; + maxPageStackSize = _defaultMaxPageStackSize; + maxAdImpressionCapacity = _defaultMaxAdImpressionCapacity; + } +} diff --git a/analytics_sdk/lib/const/event_type.dart b/analytics_sdk/lib/const/event_type.dart new file mode 100644 index 0000000..f54585a --- /dev/null +++ b/analytics_sdk/lib/const/event_type.dart @@ -0,0 +1,189 @@ +// 如果需要其他 UUID 生成,保留;但 eventId 现在固定,不再使用 + +/// 事件类型枚举:定义所有可能的事件类型 +/// 每个枚举值存储固定的 event 字符串 +/// 同时覆盖客户端、服务端上报的所有事件,方便统一管理 +enum EventType { + // ==================== 安装 & 用户 ==================== + + /// App 安装事件(客户端) + appInstall(event: 'app_install'), + + /// SDK 初始化快照事件(客户端自动) + sdkInit(event: 'sdk_init'), + + /// 应用生命周期事件(客户端自动) + appLifecycle(event: 'app_lifecycle'), + + /// 网络状态变化事件(客户端自动) + networkStateChange(event: 'network_state_change'), + + /// 会话汇总事件(客户端自动) + appSessionSummary(event: 'app_session_summary'), + + /// 用户注册事件(服务端) + userRegister(event: 'user_register'), + + /// 用户登录事件(服务端) + userLogin(event: 'user_login'), + + /// 实时在线人数(服务端) + realtimeOnline(event: 'realtime_online'), + + // ==================== 订单 & 金币 ==================== + + /// 订单创建事件(服务端) + orderCreated(event: 'order_created'), + + /// 订单支付成功事件(服务端) + orderPaid(event: 'order_paid'), + + /// 金币消耗事件(服务端) + coinConsume(event: 'coin_consume'), + + /// 导航模块点击事件 + navigation(event: 'navigation'), + + /// 应用页面展示 + appPageView(event: 'app_page_view'), + + /// 应用页面点击 + pageClick(event: 'page_click'), + + /// 页面内元素曝光事件(客户端) + appPageItemExposure(event: 'app_pageitem_exposure'), + + /// 页面内元素点击事件(客户端) + appPageItemClick(event: 'app_pageitem_click'), + + /// 广告事件 + advertising(event: 'advertising'), + + /// 广告点击事件 + adClick(event: 'ad_click'), + + /// 页面存活 + pageLifecycle(event: 'page_lifecycle'), + + /// 视频事件 + videoEvent(event: 'video_event'), + + /// 广告展示 + adImpression(event: 'ad_impression'), + + // ==================== 广告 SDK 生命周期事件 ==================== + + /// 广告 SDK 初始化 + adSdkInit(event: 'ad_sdk_init'), + + /// 广告请求 + adRequest(event: 'ad_request'), + + /// 广告填充成功 + adFill(event: 'ad_fill'), + + /// 广告无填充 + adNoFill(event: 'ad_no_fill'), + + /// 广告素材加载成功 + adLoadSuccess(event: 'ad_load_success'), + + /// 广告素材加载失败。 + @Deprecated('ad_load_fail 已并入 ad_error,使用 MATERIAL_LOAD_FAIL 错误码。') + adLoadFail(event: 'ad_load_fail'), + + /// 广告 SDK 曝光 + adImpressionSdk(event: 'ad_impression_sdk'), + + /// 广告 SDK 点击 + adClickSdk(event: 'ad_click_sdk'), + + /// 广告关闭 + adClose(event: 'ad_close'), + + /// 广告跳过 + adSkip(event: 'ad_skip'), + + /// 广告错误 + adError(event: 'ad_error'), + + /// 视频点赞事件 + videoLike(event: 'video_like'), + + /// 小说事件 + novelEvent(event: 'novel_event'), + + /// 小说点赞事件 + novelLike(event: 'novel_like'), + + /// 漫画事件 + comicEvent(event: 'comic_event'), + + /// 漫画点赞事件 + comicLike(event: 'comic_like'), + + /// 关键词搜索事件 + keywordSearch(event: 'keyword_search'), + + /// 关键词搜索结果点击事件 + keywordClick(event: 'keyword_click'), + + /// 小说收藏事件 + novelCollect(event: 'novel_collect'), + + /// 漫画收藏事件 + comicCollect(event: 'comic_collect'), + + // ==================== 推荐引擎相关 ==================== + + /// 推荐列表展示事件(服务端/客户端) + recommendListView(event: 'recommend_list_view'), + + /// 推荐列表点击事件(服务端/客户端) + recommendListClick(event: 'recommend_list_click'), + + // ==================== 视频相关扩展事件 ==================== + + /// 视频评论事件(服务端/客户端) + videoComment(event: 'video_comment'), + + /// 视频收藏事件(服务端/客户端) + videoCollect(event: 'video_collect'), + + /// 视频购买事件(服务端/客户端) + videoPurchase(event: 'video_purchase'), + + /// 视频状态更改事件(服务端) + videoStatusChange(event: 'video_status_change'), + + // ==================== 小说相关扩展事件 ==================== + + /// 小说评论事件(服务端/客户端) + novelComment(event: 'novel_comment'), + + /// 小说购买事件(服务端/客户端) + novelPurchase(event: 'novel_purchase'), + + // ==================== 漫画相关扩展事件 ==================== + + /// 漫画评论事件(服务端/客户端) + comicComment(event: 'comic_comment'), + + /// 漫画购买事件(服务端/客户端) + comicPurchase(event: 'comic_purchase'), + ; + + /// 事件类型字符串 + final String event; + + const EventType({required this.event}); + + /// 静态方法:根据 event 字符串获取枚举值(如果存在) + static EventType? fromEvent(String event) { + try { + return EventType.values.firstWhere((e) => e.event == event); + } catch (_) { + return null; // 或 throw ArgumentError('Unknown event: $event') + } + } +} diff --git a/analytics_sdk/lib/entity/ad_click_event.dart b/analytics_sdk/lib/entity/ad_click_event.dart new file mode 100644 index 0000000..49a24be --- /dev/null +++ b/analytics_sdk/lib/entity/ad_click_event.dart @@ -0,0 +1,79 @@ +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; + +/// 广告点击日志 (手动上报) +class AdClickEvent { + /// 事件类型(固定) + final String event = EventType.adClick.event; + + /// 去重 ID(动态计算) + late final String eventId; + + /// 页面唯一标识,建议使用 [AppPageEnum](如 `AppPageEnum.videoPlay.key`), + /// 也可传入自定义字符串。 + final String pageKey; + + /// 页面展示名称,建议与 [pageKey] 保持对应(如 `AppPageEnum.videoPlay.name`)。 + final String pageName; + + /// 广告位标识:与展示事件一致,如 home_banner_1 + final String adSlotKey; + + /// 被点击的广告ID + final String adId; + + /// 素材ID(可选) + final String creativeId; + + /// 广告类型字符串,由广告中心定义,如 banner, feed 等 + final String adType; + + /// 公共字段(通过工具类生成) + late final Map _commonFields; + + AdClickEvent({ + required String pageKey, + required this.pageName, + required this.adSlotKey, + required this.adId, + this.creativeId = '', + this.adType = '', + }) : pageKey = PageNameMapper.normalizeKey(pageKey) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + pageKey, + pageName, + adSlotKey, + adId, + adType, + creativeId, + ]); + } + + /// 转换为 JSON Map,用于上报和缓存 + Map toJson() { + Map payload = { + 'page_key': pageKey, + 'page_name': pageName, + 'ad_slot_key': adSlotKey, + 'ad_id': adId, + 'ad_type': adType, + 'creative_id': creativeId, + }; + return {..._commonFields, 'event_id': eventId, "payload": payload}; + } + + /// 从 JSON 恢复实例(用于缓存恢复) + factory AdClickEvent.fromJson(Map json) { + return AdClickEvent( + pageKey: json['page_key'] as String, + pageName: json['page_name'] as String, + adSlotKey: json['ad_slot_key'] as String, + adId: json['ad_id'] as String, + creativeId: json['creative_id'] as String? ?? '', + adType: json['ad_type'] as String? ?? '', + ); + } +} diff --git a/analytics_sdk/lib/entity/ad_impression_event.dart b/analytics_sdk/lib/entity/ad_impression_event.dart new file mode 100644 index 0000000..7082190 --- /dev/null +++ b/analytics_sdk/lib/entity/ad_impression_event.dart @@ -0,0 +1,97 @@ +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; + +/// 广告展示日志 (手动) +/// 展示上报逻辑: +/// 1. 进入 APP → TabA +/// - 20 条曝光 → 上报 20 条(批量) +/// 2. 切换到TabB +/// - 20 条曝光 → 上报 20 条 +/// 3. 返回TabA → 上滑加载 10 条 +/// - 新 10 条渲染出来 → 上报 10 条 +/// 4. 用户再次上下滑动TabA +/// - 所有广告已曝光,无需再上报 + +class AdImpressionEvent { + final String event = EventType.adImpression.event; + late final String eventId; + + /// 页面唯一标识,建议使用 [AppPageEnum](如 `AppPageEnum.home.key`), + /// 也可传入自定义字符串。 + final String pageKey; + + /// 页面展示名称,建议与 [pageKey] 保持对应(如 `AppPageEnum.home.name`)。 + final String pageName; + + /// 广告位标识:如 home_banner_1等 对应广告导入数据的advertiseLocationCode + final String adSlotKey; + + /// 本次广告请求唯一 ID。 + final String requestId; + + /// 广告ID, 多个广告ID英文逗号分隔 对应广告导入数据的advertiseCode + final String adId; + + /// 实际素材尺寸,格式如 "1080x1920"。 + final String materialSize; + + /// 素材ID(可选)多个素材ID英文逗号分隔,和广告ID一一对应,没有的填充空字符串 + final String creativeId; + + /// 广告类型字符串,如 banner, feed, interstitial, reward_video 等,由广告中心定义 + final String adType; + + late final Map _commonFields; + + AdImpressionEvent({ + required String pageKey, + required this.pageName, + required this.adSlotKey, + this.requestId = '', + required this.adId, + this.materialSize = '', + this.creativeId = '', + this.adType = '', + }) : pageKey = PageNameMapper.normalizeKey(pageKey) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + pageKey, + pageName, + adSlotKey, + requestId, + adId, + adType, + materialSize, + creativeId, + ]); + } + + Map toJson() { + Map payload = { + 'page_key': pageKey, + 'page_name': pageName, + 'request_id': requestId, + 'ad_slot_key': adSlotKey, + 'ad_id': adId, + 'ad_type': adType, + 'material_size': materialSize, + 'creative_id': creativeId, + }; + return {..._commonFields, 'event_id': eventId, "payload": payload}; + } + + factory AdImpressionEvent.fromJson(Map json) { + return AdImpressionEvent( + pageKey: json['page_key'] as String, + pageName: json['page_name'] as String, + adSlotKey: json['ad_slot_key'] as String, + requestId: json['request_id'] as String? ?? '', + adId: json['ad_id'] as String, + materialSize: json['material_size'] as String? ?? '', + creativeId: json['creative_id'] as String? ?? '', + adType: json['ad_type'] as String? ?? '', + ); + } +} diff --git a/analytics_sdk/lib/entity/ad_sdk_events.dart b/analytics_sdk/lib/entity/ad_sdk_events.dart new file mode 100644 index 0000000..37334a4 --- /dev/null +++ b/analytics_sdk/lib/entity/ad_sdk_events.dart @@ -0,0 +1,459 @@ +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; + +abstract class _AdSdkEvent { + final String event; + + late final String eventId; + late final Map _commonFields; + + _AdSdkEvent(this.event, List eventIdFields) { + _commonFields = SdkContext.generateCommonFields(event); + eventId = SdkContext.generateEventIdFromCommonFields( + _commonFields, + eventIdFields, + ); + } + + Map buildPayload(); + + Map toJson() { + return { + ..._commonFields, + 'event_id': eventId, + 'payload': buildPayload(), + }; + } +} + +/// 广告 SDK 初始化事件。 +class AdSdkInitEvent extends _AdSdkEvent { + final int screenWidth; + final int screenHeight; + final int screenDpi; + final String orientation; + final String networkType; + final String carrier; + final String language; + final String timezone; + final String oaid; + final String androidId; + final String idfv; + final String idfa; + final bool lat; + final String adSdkVersion; + final int availableStorage; + final int batteryLevel; + final String cpu; + final int storage; + final String activationTime; + + AdSdkInitEvent({ + this.screenWidth = 0, + this.screenHeight = 0, + this.screenDpi = 0, + this.orientation = '', + this.networkType = '', + this.carrier = '', + this.language = '', + this.timezone = '', + this.oaid = '', + this.androidId = '', + this.idfv = '', + this.idfa = '', + this.lat = false, + required this.adSdkVersion, + this.availableStorage = 0, + this.batteryLevel = 0, + this.cpu = '', + this.storage = 0, + this.activationTime = '', + }) : super(EventType.adSdkInit.event, [ + screenWidth.toString(), + screenHeight.toString(), + screenDpi.toString(), + orientation, + networkType, + carrier, + language, + timezone, + oaid, + androidId, + idfv, + idfa, + lat.toString(), + adSdkVersion, + availableStorage.toString(), + batteryLevel.toString(), + cpu, + storage.toString(), + activationTime, + ]); + + @override + Map buildPayload() => { + 'screen_width': screenWidth, + 'screen_height': screenHeight, + 'screen_dpi': screenDpi, + 'orientation': orientation, + 'network_type': networkType, + 'carrier': carrier, + 'language': language, + 'timezone': timezone, + 'oaid': oaid, + 'android_id': androidId, + 'idfv': idfv, + 'idfa': idfa, + 'lat': lat, + 'ad_sdk_version': adSdkVersion, + 'available_storage': availableStorage, + 'battery_level': batteryLevel, + 'cpu': cpu, + 'storage': storage, + 'activation_time': activationTime, + }; +} + +/// 广告请求事件。 +class AdRequestEvent extends _AdSdkEvent { + final String requestId; + final String adSlotKey; + final String adType; + final String? adId; + + AdRequestEvent({ + required this.requestId, + required this.adSlotKey, + this.adType = '', + this.adId, + }) : super(EventType.adRequest.event, [ + requestId, + adSlotKey, + adType, + adId ?? '', + ]); + + @override + Map buildPayload() => { + 'request_id': requestId, + 'ad_slot_key': adSlotKey, + 'ad_type': adType, + if (adId?.trim().isNotEmpty ?? false) 'ad_id': adId!.trim(), + }; +} + +/// 广告填充成功事件。 +class AdFillEvent extends _AdSdkEvent { + final String traceId; + final String requestId; + final String adSlotKey; + final String adId; + final String adType; + final int fillCount; + + AdFillEvent({ + this.traceId = '', + required this.requestId, + required this.adSlotKey, + required this.adId, + required this.adType, + required this.fillCount, + }) : super(EventType.adFill.event, [ + if (traceId.isNotEmpty) traceId, + requestId, + adSlotKey, + adId, + adType, + fillCount.toString(), + ]); + + @override + Map buildPayload() => { + 'trace_id': traceId, + 'request_id': requestId, + 'ad_slot_key': adSlotKey, + 'ad_id': adId, + 'ad_type': adType, + 'fill_count': fillCount, + }; +} + +/// 广告无填充事件。 +class AdNoFillEvent extends _AdSdkEvent { + final String requestId; + final String adSlotKey; + final String adType; + final String reason; + + AdNoFillEvent({ + required this.requestId, + required this.adSlotKey, + required this.adType, + required this.reason, + }) : super(EventType.adNoFill.event, [requestId, adSlotKey, adType, reason]); + + @override + Map buildPayload() => { + 'request_id': requestId, + 'ad_slot_key': adSlotKey, + 'ad_type': adType, + 'reason': reason, + }; +} + +/// 广告素材加载成功事件。 +class AdLoadSuccessEvent extends _AdSdkEvent { + final String traceId; + final String requestId; + final String adSlotKey; + final String adId; + final String adType; + final int loadDuration; + + AdLoadSuccessEvent({ + this.traceId = '', + required this.requestId, + required this.adSlotKey, + required this.adId, + required this.adType, + required this.loadDuration, + }) : super(EventType.adLoadSuccess.event, [ + if (traceId.isNotEmpty) traceId, + requestId, + adSlotKey, + adId, + adType, + loadDuration.toString(), + ]); + + @override + Map buildPayload() => { + 'trace_id': traceId, + 'request_id': requestId, + 'ad_slot_key': adSlotKey, + 'ad_id': adId, + 'ad_type': adType, + 'load_duration': loadDuration, + }; +} + +/// 广告素材加载失败事件。 +@Deprecated('ad_load_fail 已并入 ad_error,使用 MATERIAL_LOAD_FAIL 错误码。') +class AdLoadFailEvent extends _AdSdkEvent { + final String traceId; + final String requestId; + final String adSlotKey; + final String adId; + final String adType; + final String errorCode; + + AdLoadFailEvent({ + this.traceId = '', + required this.requestId, + required this.adSlotKey, + required this.adId, + required this.adType, + required this.errorCode, + }) : super(EventType.adLoadFail.event, [ + if (traceId.isNotEmpty) traceId, + requestId, + adSlotKey, + adId, + adType, + errorCode, + ]); + + @override + Map buildPayload() => { + 'trace_id': traceId, + 'request_id': requestId, + 'ad_slot_key': adSlotKey, + 'ad_id': adId, + 'ad_type': adType, + 'error_code': errorCode, + }; +} + +/// 广告 SDK 曝光事件。 +class AdImpressionSdkEvent extends _AdSdkEvent { + final String traceId; + final String requestId; + final String adSlotKey; + final String adId; + final String adType; + final String materialSize; + + AdImpressionSdkEvent({ + this.traceId = '', + required this.requestId, + required this.adSlotKey, + required this.adId, + required this.adType, + required this.materialSize, + }) : super(EventType.adImpressionSdk.event, [ + traceId, + requestId, + adSlotKey, + adId, + adType, + materialSize, + ]); + + @override + Map buildPayload() => { + 'trace_id': traceId, + 'request_id': requestId, + 'ad_slot_key': adSlotKey, + 'ad_id': adId, + 'ad_type': adType, + 'material_size': materialSize, + }; +} + +/// 广告 SDK 点击事件。 +class AdClickSdkEvent extends _AdSdkEvent { + final String traceId; + final String requestId; + final String adSlotKey; + final String adId; + final String adType; + + AdClickSdkEvent({ + this.traceId = '', + required this.requestId, + required this.adSlotKey, + required this.adId, + required this.adType, + }) : super(EventType.adClickSdk.event, [ + traceId, + requestId, + adSlotKey, + adId, + adType, + ]); + + @override + Map buildPayload() => { + 'trace_id': traceId, + 'request_id': requestId, + 'ad_slot_key': adSlotKey, + 'ad_id': adId, + 'ad_type': adType, + }; +} + +/// 广告关闭事件。 +class AdCloseEvent extends _AdSdkEvent { + final String traceId; + final String requestId; + final String adSlotKey; + final String adId; + final String adType; + final String closeType; + final int shownDuration; + + AdCloseEvent({ + this.traceId = '', + required this.requestId, + required this.adSlotKey, + required this.adId, + required this.adType, + required this.closeType, + this.shownDuration = 0, + }) : super(EventType.adClose.event, [ + if (traceId.isNotEmpty) traceId, + requestId, + adSlotKey, + adId, + adType, + closeType, + shownDuration.toString(), + ]); + + @override + Map buildPayload() => { + 'trace_id': traceId, + 'request_id': requestId, + 'ad_slot_key': adSlotKey, + 'ad_id': adId, + 'ad_type': adType, + 'close_type': closeType, + 'shown_duration': shownDuration, + }; +} + +/// 广告跳过事件。 +class AdSkipEvent extends _AdSdkEvent { + final String traceId; + final String requestId; + final String adSlotKey; + final String adId; + final String adType; + final int shownDuration; + + AdSkipEvent({ + this.traceId = '', + required this.requestId, + required this.adSlotKey, + required this.adId, + required this.adType, + this.shownDuration = 0, + }) : super(EventType.adSkip.event, [ + if (traceId.isNotEmpty) traceId, + requestId, + adSlotKey, + adId, + adType, + shownDuration.toString(), + ]); + + @override + Map buildPayload() => { + 'trace_id': traceId, + 'request_id': requestId, + 'ad_slot_key': adSlotKey, + 'ad_id': adId, + 'ad_type': adType, + 'shown_duration': shownDuration, + }; +} + +/// 广告错误事件。 +class AdErrorEvent extends _AdSdkEvent { + final String traceId; + final String requestId; + final String adSlotKey; + final String adType; + final String errorCode; + final String errorMsg; + final String errorStage; + + AdErrorEvent({ + this.traceId = '', + this.requestId = '', + required this.adSlotKey, + required this.adType, + required this.errorCode, + required this.errorMsg, + required this.errorStage, + }) : super(EventType.adError.event, [ + if (traceId.isNotEmpty) traceId, + requestId, + adSlotKey, + adType, + errorCode, + errorMsg, + errorStage, + ]); + + @override + Map buildPayload() => { + 'trace_id': traceId, + 'request_id': requestId, + 'ad_slot_key': adSlotKey, + 'ad_type': adType, + 'error_code': errorCode, + 'error_msg': errorMsg, + 'error_stage': errorStage, + }; +} diff --git a/analytics_sdk/lib/entity/advertising_event.dart b/analytics_sdk/lib/entity/advertising_event.dart new file mode 100644 index 0000000..c8fcbef --- /dev/null +++ b/analytics_sdk/lib/entity/advertising_event.dart @@ -0,0 +1,57 @@ +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; + +/// APP广告行为 +class AdvertisingEvent { + final String event = EventType.advertising.event; + late final String eventId; + + ///事件类型:click(点击), close(关闭), show(展示) + final String eventType; + + /// 广告标识:home_popup(首页弹窗), home_banner(首页Banner), video_reward(激励视频)等 + final String advertisingKey; + + /// 广告标识名称:首页弹窗, 首页Banner, 激励视频 + final String advertisingName; + + /// 广告ID + final String advertisingId; + + late final Map _commonFields; + + AdvertisingEvent({ + required this.eventType, + required this.advertisingKey, + required this.advertisingName, + required this.advertisingId, + }) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + eventType, + advertisingKey, + advertisingName, + advertisingId, + ]); + } + + Map toJson() { + Map payload = { + 'event_type': eventType, + 'advertising_key': advertisingKey, + 'advertising_name': advertisingName, + 'advertising_id': advertisingId, + }; + return {..._commonFields, 'event_id': eventId, "payload": payload}; + } + + factory AdvertisingEvent.fromJson(Map json) { + return AdvertisingEvent( + eventType: json['event_type'] as String, + advertisingKey: json['advertising_key'] as String, + advertisingName: json['advertising_name'] as String, + advertisingId: json['advertising_id'] as String, + ); + } +} diff --git a/analytics_sdk/lib/entity/analytics_tab.dart b/analytics_sdk/lib/entity/analytics_tab.dart new file mode 100644 index 0000000..2913c71 --- /dev/null +++ b/analytics_sdk/lib/entity/analytics_tab.dart @@ -0,0 +1,24 @@ +/// Tab 页面的 analytics 配置。 +/// +/// - [key]:pageKey(必填),建议使用 [AppPageEnum](如 `AppPageEnum.home.key`), +/// 也可传入自定义字符串。 +/// - [name]:pageName(可选),省略时由 [PageNameMapper] 自动解析; +/// 传入时建议与 [AppPageEnum] 保持一致(如 `AppPageEnum.home.name`)。 +/// +/// ```dart +/// // 使用枚举 +/// AnalyticsTab(AppPageEnum.home.key, AppPageEnum.home.name) +/// AnalyticsTab(AppPageEnum.video.key, AppPageEnum.video.name) +/// +/// // 使用自定义值 +/// AnalyticsTab('live_room', '直播间') +/// +/// // 省略 name(由 PageNameMapper 解析) +/// AnalyticsTab('home') +/// ``` +class AnalyticsTab { + final String key; + final String? name; + + const AnalyticsTab(this.key, [this.name]); +} diff --git a/analytics_sdk/lib/entity/analytics_utils.dart b/analytics_sdk/lib/entity/analytics_utils.dart new file mode 100644 index 0000000..587ad85 --- /dev/null +++ b/analytics_sdk/lib/entity/analytics_utils.dart @@ -0,0 +1,5 @@ +import 'package:analytics_sdk/manager/sdk_context.dart'; +export 'package:analytics_sdk/manager/sdk_context.dart'; + +// SdkContext 的向后兼容别名,内部使用。 +typedef AnalyticsUtils = SdkContext; diff --git a/analytics_sdk/lib/entity/app_install_event.dart b/analytics_sdk/lib/entity/app_install_event.dart new file mode 100644 index 0000000..504f45e --- /dev/null +++ b/analytics_sdk/lib/entity/app_install_event.dart @@ -0,0 +1,46 @@ +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; + +/// App 安装事件(客户端) +class AppInstallEvent { + /// 事件类型(固定) + final String event = EventType.appInstall.event; + + /// 去重 ID(动态计算) + late final String eventId; + + /// 落地页点击生成唯一ID, 复制到剪切板, 打开APP后读取剪切板 + final String traceId; + + /// 原生包是否开启归因:1 开启,0 关闭(默认) + final int attributionFlag; + + late final Map _commonFields; + + AppInstallEvent({ + required this.traceId, + this.attributionFlag = 0, + }) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + traceId, + attributionFlag.toString(), + ]); + } + + Map toJson() { + final payload = { + 'trace_id': traceId, + 'attribution_flag': attributionFlag, + }; + return {..._commonFields, 'event_id': eventId, 'payload': payload}; + } + + factory AppInstallEvent.fromJson(Map json) { + return AppInstallEvent( + traceId: json['trace_id'] as String, + attributionFlag: (json['attribution_flag'] as int?) ?? 0, + ); + } +} diff --git a/analytics_sdk/lib/entity/app_lifecycle_event.dart b/analytics_sdk/lib/entity/app_lifecycle_event.dart new file mode 100644 index 0000000..a27947a --- /dev/null +++ b/analytics_sdk/lib/entity/app_lifecycle_event.dart @@ -0,0 +1,189 @@ +import 'dart:convert'; + +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; + +/// 应用生命周期事件。 +/// +/// `app_lifecycle` 的子事件字段写入 `payload`。每个工厂方法只写入该 +/// 子事件文档定义的字段。 +class AppLifecycleEvent { + final String event = EventType.appLifecycle.event; + final String lifecycleEventKey; + final Map _fields; + + late final String eventId; + late final Map _commonFields; + + AppLifecycleEvent._( + this.lifecycleEventKey, + Map fields, + ) : _fields = Map.from(fields) + ..removeWhere((_, value) => value == null) { + _commonFields = SdkContext.generateCommonFields(event); + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + lifecycleEventKey, + jsonEncode(_stableFields), + ]); + } + + factory AppLifecycleEvent.coldStart({ + required int coldStartDurationMs, + String lastExitType = 'unknown', + int bootTime = 0, + }) => + AppLifecycleEvent._('cold_start', { + 'cold_start_duration_ms': _nonNegative(coldStartDurationMs), + 'last_exit_type': _allowedString( + lastExitType, + const {'normal', 'killed', 'crash', 'unknown'}, + 'unknown', + ), + 'boot_time': bootTime > 0 ? bootTime : 0, + }); + + factory AppLifecycleEvent.webLoad({ + required String pageUrl, + required int pageLoadTimeMs, + required int viewportWidth, + required int viewportHeight, + }) => + AppLifecycleEvent._('web_load', { + 'page_url': pageUrl, + 'page_load_time_ms': _nonNegative(pageLoadTimeMs), + 'viewport_width': viewportWidth, + 'viewport_height': viewportHeight, + }); + + factory AppLifecycleEvent.warmStart({ + required int backgroundDurationMs, + String resumeReason = 'unknown', + }) => + AppLifecycleEvent._('warm_start', { + 'background_duration_ms': _nonNegative(backgroundDurationMs), + 'resume_reason': _allowedString( + resumeReason, + const {'user', 'push', 'scheme', 'unknown'}, + 'unknown', + ), + }); + + factory AppLifecycleEvent.foreground({ + required int foregroundCountInSid, + }) => + AppLifecycleEvent._('foreground', { + 'foreground_count_in_sid': + foregroundCountInSid < 1 ? 1 : foregroundCountInSid, + 'is_foreground': 1, + }); + + factory AppLifecycleEvent.background({ + required int foregroundDurationMs, + required String lastActivePageKey, + }) => + AppLifecycleEvent._('background', { + 'is_foreground': 0, + 'foreground_duration_ms': _nonNegative(foregroundDurationMs), + 'last_active_page_key': lastActivePageKey, + }); + + factory AppLifecycleEvent.webUnload({ + required String pageUrl, + required String lastActivePageKey, + required int pageStayDurationMs, + String unloadReason = 'unknown', + required int scrollDepthPercentAtUnload, + required String webVisibilityState, + }) => + AppLifecycleEvent._('web_unload', { + 'page_url': pageUrl, + 'last_active_page_key': lastActivePageKey, + 'page_stay_duration_ms': _nonNegative(pageStayDurationMs), + 'unload_reason': _allowedString( + unloadReason, + const { + 'normal', + 'visibility_hidden', + 'navigation', + 'tab_close', + 'unknown', + }, + 'unknown', + ), + 'scroll_depth_percent_at_unload': + scrollDepthPercentAtUnload.clamp(0, 100), + 'web_visibility_state': _allowedString( + webVisibilityState, + const {'visible', 'hidden', 'unknown'}, + 'unknown', + ), + }); + + factory AppLifecycleEvent.terminate({ + required String lastActivePageKey, + String terminateType = 'unknown', + required int sessionDurationMs, + required int terminateTs, + }) => + AppLifecycleEvent._('terminate', { + 'last_active_page_key': lastActivePageKey, + 'terminate_type': _allowedString( + terminateType, + const {'user_quit', 'system_kill', 'app_finish', 'unknown'}, + 'unknown', + ), + 'session_duration_ms': _nonNegative(sessionDurationMs), + 'terminate_ts': terminateTs > 0 + ? terminateTs + : DateTime.now().millisecondsSinceEpoch ~/ 1000, + }); + + factory AppLifecycleEvent.unknown({ + String unknownReason = 'unknown', + }) => + AppLifecycleEvent._('unknown', { + 'unknown_reason': _allowedString( + unknownReason, + const { + 'callback_missing', + 'state_conflict', + 'unsupported_platform', + 'sdk_not_ready', + 'system_restricted', + 'route_unrecognized', + 'collect_error', + 'unknown', + }, + 'unknown', + ), + }); + + Map toJson() { + return { + ..._commonFields, + 'event_id': eventId, + 'payload': { + 'lifecycle_event_key': lifecycleEventKey, + ..._fields, + }, + }; + } + + Map get _stableFields { + final sortedKeys = _fields.keys.toList()..sort(); + return { + for (final key in sortedKeys) key: _fields[key], + }; + } + + static int _nonNegative(int value) => value < 0 ? 0 : value; + + static String _allowedString( + String value, + Set allowed, + String fallback, + ) { + final normalized = value.trim(); + return allowed.contains(normalized) ? normalized : fallback; + } +} diff --git a/analytics_sdk/lib/entity/app_page_item_event.dart b/analytics_sdk/lib/entity/app_page_item_event.dart new file mode 100644 index 0000000..4e143c6 --- /dev/null +++ b/analytics_sdk/lib/entity/app_page_item_event.dart @@ -0,0 +1,217 @@ +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; + +/// 页面内元素曝光事件(客户端手动上报)。 +/// +/// 仅限需要采集页面内元素曝光的项目接入。未对接推荐引擎时,推荐相关字段传空字符串。 +class AppPageItemExposureEvent { + final String event = EventType.appPageItemExposure.event; + late final String eventId; + + /// 元素唯一 ID。 + final String itemId; + + /// 元素类型,如推荐、关注、评论、小说、视频、漫画等业务字典值。 + final String itemType; + + /// 元素名称,如推荐、关注、评论、小说、视频、漫画等业务字典值。 + final String itemName; + + /// 父元素 ID。 + final String parentItemId; + + /// 当前页面标识,建议使用 [AppPageEnum] 或业务字典中的稳定 key。 + final String pageKey; + + /// 当前页面名称。 + final String pageName; + + /// 来路页面标识。 + final String referrerPageKey; + + /// 来路页面名称。 + final String referrerPageName; + + /// 推荐引擎 trace_id;多个推荐列表用英文逗号分隔。 + final String recommendTraceId; + + /// 推荐内容 ID;未对接推荐引擎传空字符串。 + final String recommendId; + + /// 推荐引擎 trace_info;未对接推荐引擎传空字符串。 + final String recommendTraceInfo; + + late final Map _commonFields; + + AppPageItemExposureEvent({ + required this.itemId, + required this.itemType, + required this.itemName, + required this.parentItemId, + required String pageKey, + required this.pageName, + required String referrerPageKey, + required this.referrerPageName, + this.recommendTraceId = '', + this.recommendId = '', + this.recommendTraceInfo = '', + }) : pageKey = PageNameMapper.normalizeKey(pageKey), + referrerPageKey = PageNameMapper.normalizeKey(referrerPageKey) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + itemId, + itemType, + itemName, + parentItemId, + this.pageKey, + pageName, + this.referrerPageKey, + referrerPageName, + recommendTraceId, + recommendId, + recommendTraceInfo, + ]); + } + + Map toJson() { + final payload = { + 'item_id': itemId, + 'item_type': itemType, + 'item_name': itemName, + 'parent_item_id': parentItemId, + 'page_key': pageKey, + 'page_name': pageName, + 'referrer_page_key': referrerPageKey, + 'referrer_page_name': referrerPageName, + 'recommend_trace_id': recommendTraceId, + 'recommend_id': recommendId, + 'recommend_trace_info': recommendTraceInfo, + }; + return {..._commonFields, 'event_id': eventId, 'payload': payload}; + } + + factory AppPageItemExposureEvent.fromJson(Map json) { + final source = _payloadOrSelf(json); + return AppPageItemExposureEvent( + itemId: source['item_id'] as String, + itemType: source['item_type'] as String, + itemName: source['item_name'] as String, + parentItemId: source['parent_item_id'] as String, + pageKey: source['page_key'] as String, + pageName: source['page_name'] as String, + referrerPageKey: source['referrer_page_key'] as String, + referrerPageName: source['referrer_page_name'] as String, + recommendTraceId: source['recommend_trace_id'] as String? ?? '', + recommendId: source['recommend_id'] as String? ?? '', + recommendTraceInfo: source['recommend_trace_info'] as String? ?? '', + ); + } +} + +/// 页面内元素点击事件(客户端手动上报)。 +/// +/// 仅限需要采集页面内元素点击的项目接入。未对接推荐引擎时,推荐相关字段传空字符串。 +class AppPageItemClickEvent { + final String event = EventType.appPageItemClick.event; + late final String eventId; + + /// 元素唯一 ID。 + final String itemId; + + /// 点击元素类型,如推荐、关注、评论、小说、视频、漫画等业务字典值。 + final String itemType; + + /// 点击元素名称,如推荐、关注、评论、小说、视频、漫画等业务字典值。 + final String itemName; + + /// 父元素 ID。 + final String parentItemId; + + /// 在父元素中的顺序索引(从 0 或 1 开始,按业务约定传入)。 + final int position; + + /// 当前页面标识,建议使用 [AppPageEnum] 或业务字典中的稳定 key。 + final String pageKey; + + /// 当前页面名称。 + final String pageName; + + /// 推荐引擎 trace_id;多个推荐列表用英文逗号分隔。 + final String recommendTraceId; + + /// 推荐内容 ID;未对接推荐引擎传空字符串。 + final String recommendId; + + /// 推荐引擎 trace_info;未对接推荐引擎传空字符串。 + final String recommendTraceInfo; + + late final Map _commonFields; + + AppPageItemClickEvent({ + required this.itemId, + required this.itemType, + required this.itemName, + required this.parentItemId, + required this.position, + required String pageKey, + required this.pageName, + this.recommendTraceId = '', + this.recommendId = '', + this.recommendTraceInfo = '', + }) : pageKey = PageNameMapper.normalizeKey(pageKey) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + itemId, + itemType, + itemName, + parentItemId, + position.toString(), + this.pageKey, + pageName, + recommendTraceId, + recommendId, + recommendTraceInfo, + ]); + } + + Map toJson() { + final payload = { + 'item_id': itemId, + 'item_type': itemType, + 'item_name': itemName, + 'parent_item_id': parentItemId, + 'position': position, + 'page_key': pageKey, + 'page_name': pageName, + 'recommend_trace_id': recommendTraceId, + 'recommend_id': recommendId, + 'recommend_trace_info': recommendTraceInfo, + }; + return {..._commonFields, 'event_id': eventId, 'payload': payload}; + } + + factory AppPageItemClickEvent.fromJson(Map json) { + final source = _payloadOrSelf(json); + return AppPageItemClickEvent( + itemId: source['item_id'] as String, + itemType: source['item_type'] as String, + itemName: source['item_name'] as String, + parentItemId: source['parent_item_id'] as String, + position: source['position'] as int, + pageKey: source['page_key'] as String, + pageName: source['page_name'] as String, + recommendTraceId: source['recommend_trace_id'] as String? ?? '', + recommendId: source['recommend_id'] as String? ?? '', + recommendTraceInfo: source['recommend_trace_info'] as String? ?? '', + ); + } +} + +Map _payloadOrSelf(Map json) { + final payload = json['payload']; + if (payload is Map) return Map.from(payload); + return json; +} diff --git a/analytics_sdk/lib/entity/app_page_view_event.dart b/analytics_sdk/lib/entity/app_page_view_event.dart new file mode 100644 index 0000000..8fe81f1 --- /dev/null +++ b/analytics_sdk/lib/entity/app_page_view_event.dart @@ -0,0 +1,181 @@ +import 'dart:ui' show window; + +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/page_view_auto_fields.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:analytics_sdk/utils/uuid_util.dart'; + +/// 应用页面展示日志(由 [PageLifecycleObserver] 和 Tab 包装器自动上报)。 +/// +/// **page_key / page_name 填写规范** +/// +/// 建议使用 [AppPageEnum] 预定义值,也可直接传入自定义字符串: +/// ```dart +/// AppPageViewEvent( +/// pageKey: AppPageEnum.videoDetail.key, // 'video_detail' +/// pageName: AppPageEnum.videoDetail.name, // '视频详情页' +/// ... +/// ) +/// ``` +class AppPageViewEvent { + final String event = EventType.appPageView.event; + late final String eventId; + + final String userType; + + /// 当前页面标识,建议使用 [AppPageEnum](如 `AppPageEnum.videoDetail.key`), + /// 也可传入自定义字符串。 + final String pageKey; + + /// 当前页面展示名称,建议与 [pageKey] 保持对应(如 `AppPageEnum.videoDetail.name`)。 + final String pageName; + + /// 来路页面标识,填写跳转前所在页面的 pageKey,规范同 [pageKey]。 + final String referrerPageKey; + + /// 来路页面展示名称,规范同 [pageName]。 + final String referrerPageName; + + /// 当前页面标识(与 [pageKey] 通常保持一致,保留用于扩展场景)。 + final String currentPageKey; + + /// 当前页面展示名称(与 [pageName] 通常保持一致)。 + final String currentPageName; + + /// 页面加载耗时(毫秒) + final int pageLoadTime; + + /// 当前页面实例唯一 ID,由 SDK 自动生成。 + final String pageId; + + /// SDK 首次识别当前页面实例的时间,10 位时间戳。 + final int pageEnterTs; + + /// Web/H5 当前页面物理地址,仅包含 scheme + host + path。 + final String pageUrl; + + /// 当前页面根容器可交互可视区域宽度。 + final int viewportWidth; + + /// 当前页面根容器可交互可视区域高度。 + final int viewportHeight; + + /// 推荐引擎的trace_id,有多个推荐列表时用英文逗号分隔,未接推荐引擎传空字符串 + final String recommendTraceId; + + late final Map _commonFields; + + AppPageViewEvent({ + required this.userType, + required String pageKey, + required this.pageName, + required String referrerPageKey, + required this.referrerPageName, + required String currentPageKey, + required this.currentPageName, + required this.pageLoadTime, + String? pageId, + int? pageEnterTs, + String? pageUrl, + int? viewportWidth, + int? viewportHeight, + this.recommendTraceId = '', + }) : pageKey = PageNameMapper.normalizeKey(pageKey), + referrerPageKey = PageNameMapper.normalizeKey(referrerPageKey), + currentPageKey = PageNameMapper.normalizeKey(currentPageKey), + pageId = _pageId(pageId), + pageEnterTs = _pageEnterTs(pageEnterTs), + pageUrl = pageUrl ?? currentPageUrl(), + viewportWidth = _viewportDimension(viewportWidth, isWidth: true), + viewportHeight = _viewportDimension(viewportHeight, isWidth: false) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + this.pageId, + this.pageEnterTs.toString(), + this.pageUrl, + this.viewportWidth.toString(), + this.viewportHeight.toString(), + userType, + this.pageKey, + pageName, + this.referrerPageKey, + referrerPageName, + this.currentPageKey, + currentPageName, + pageLoadTime.toString(), + recommendTraceId, + ]); + } + + Map toJson() { + Map payload = { + 'user_type': userType, + 'page_key': pageKey, + 'page_name': pageName, + 'referrer_page_key': referrerPageKey, + 'referrer_page_name': referrerPageName, + 'current_page_key': currentPageKey, + 'current_page_name': currentPageName, + 'page_load_time': pageLoadTime, + 'page_id': pageId, + 'page_enter_ts': pageEnterTs, + 'page_url': pageUrl, + 'viewport_width': viewportWidth, + 'viewport_height': viewportHeight, + 'recommend_trace_id': recommendTraceId, + }; + return { + ..._commonFields, + 'event_id': eventId, + "payload": payload, + }; + } + + factory AppPageViewEvent.fromJson(Map json) { + return AppPageViewEvent( + userType: json['user_type'] as String, + pageKey: json['page_key'] as String, + pageName: json['page_name'] as String, + referrerPageKey: json['referrer_page_key'] as String, + referrerPageName: json['referrer_page_name'] as String, + currentPageKey: json['current_page_key'] as String, + currentPageName: json['current_page_name'] as String, + pageLoadTime: json['page_load_time'] as int, + pageId: json['page_id'] as String?, + pageEnterTs: json['page_enter_ts'] as int?, + pageUrl: json['page_url'] as String?, + viewportWidth: json['viewport_width'] as int?, + viewportHeight: json['viewport_height'] as int?, + recommendTraceId: json['recommend_trace_id'] as String? ?? '', + ); + } + + static String _pageId(String? value) { + final normalized = value?.trim() ?? ''; + if (normalized.isNotEmpty) return normalized; + return generateUuidV4().replaceAll('-', ''); + } + + static int _pageEnterTs(int? value) { + if (value != null && value > 0) return value; + return DateTime.now().millisecondsSinceEpoch ~/ 1000; + } + + static int _viewportDimension(int? value, {required bool isWidth}) { + if (value != null) return value >= 0 ? value : -1; + try { + final pixelRatio = window.devicePixelRatio; + if (pixelRatio <= 0) return -1; + final physicalSize = window.physicalSize; + final logical = isWidth + ? physicalSize.width / pixelRatio + : physicalSize.height / pixelRatio; + if (!logical.isFinite || logical <= 0) return -1; + return logical.round(); + } catch (_) { + return -1; + } + } +} diff --git a/analytics_sdk/lib/entity/app_session_summary_event.dart b/analytics_sdk/lib/entity/app_session_summary_event.dart new file mode 100644 index 0000000..1bff765 --- /dev/null +++ b/analytics_sdk/lib/entity/app_session_summary_event.dart @@ -0,0 +1,128 @@ +import 'dart:convert'; + +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; +import 'package:analytics_sdk/manager/session_manager.dart'; +import 'package:analytics_sdk/manager/session_behavior_manager.dart'; +import 'package:analytics_sdk/manager/session_metrics_manager.dart'; +import 'package:analytics_sdk/manager/session_sensor_sampler.dart'; + +/// 会话汇总事件。 +/// +/// 汇总字段写入 `payload`;签名类 payload 字段由 +/// [SdkContext.attachDeviceEnvIfNeeded] 在 track 管道中统一补充。 +class AppSessionSummaryEvent { + final String event = EventType.appSessionSummary.event; + final Map _payloadFields; + + late final String eventId; + late final Map _commonFields; + + AppSessionSummaryEvent({ + required String sessionEndReason, + DateTime? endedAt, + }) : _payloadFields = _buildPayloadFields( + sessionEndReason: sessionEndReason, + endedAt: endedAt ?? DateTime.now(), + ) { + _commonFields = SdkContext.generateCommonFields(event); + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + jsonEncode(_stablePayloadFields), + ]); + } + + Map toJson() { + return { + ..._commonFields, + 'event_id': eventId, + 'payload': Map.from(_payloadFields), + }; + } + + Map get _stablePayloadFields { + final sortedKeys = _payloadFields.keys.toList()..sort(); + return { + for (final key in sortedKeys) key: _payloadFields[key], + }; + } + + static Map _buildPayloadFields({ + required String sessionEndReason, + required DateTime endedAt, + }) { + final metrics = SessionMetricsManager.instance.snapshot(endedAt: endedAt); + final behavior = SessionBehaviorManager.instance.snapshot(); + final environment = SdkContext.getEnvironmentSnapshotFields(); + final screenBrightness = _intField(environment['screen_brightness'], -1); + final sensor = SessionSensorSampler.instance.snapshot( + screenBrightness: screenBrightness, + ); + final sessionStart = SessionManager.instance.sessionStartTime ?? endedAt; + final sessionStartTs = sessionStart.millisecondsSinceEpoch ~/ 1000; + final sessionEndTs = endedAt.millisecondsSinceEpoch ~/ 1000; + final durationMs = endedAt.difference(sessionStart).inMilliseconds; + + return { + 'session_start_ts': sessionStartTs, + 'session_end_ts': sessionEndTs, + 'session_duration_ms': durationMs < 0 ? 0 : durationMs, + 'event_count_total': metrics.eventCountTotal, + 'business_event_count': metrics.businessEventCount, + 'foreground_count': metrics.foregroundCount, + 'background_count_in_sid': metrics.backgroundCountInSid, + 'page_count': metrics.pageCount, + 'page_view_count': metrics.pageViewCount, + 'page_click_count': metrics.pageClickCount, + 'network_change_count': metrics.networkChangeCount, + 'session_end_reason': _sessionEndReason(sessionEndReason), + 'last_event_name': metrics.lastEventName, + 'orientation': environment['orientation']?.toString() ?? '', + 'screen_brightness': screenBrightness, + 'volume': _intField(environment['volume'], -1), + 'is_screen_on': _intField(environment['is_screen_on'], -1), + 'battery_level': _intField(environment['battery_level'], -1), + 'is_charging': _intField(environment['is_charging'], -1), + 'battery_state': _stringField(environment['battery_state'], 'unknown'), + 'sensor_signature': sensor.sensorSignature, + 'sensor_count_active': sensor.sensorCountActive, + 'scroll_total_count': behavior.scrollTotalCount, + 'scroll_total_distance_px': behavior.scrollTotalDistancePx, + 'scroll_up_count': behavior.scrollUpCount, + 'scroll_down_count': behavior.scrollDownCount, + 'scroll_direction_change_count': behavior.scrollDirectionChangeCount, + 'first_scroll_delay_ms': behavior.firstScrollDelayMs, + 'max_scroll_depth_percent': behavior.maxScrollDepthPercent, + 'is_reach_bottom': behavior.isReachBottom, + 'content_height': behavior.contentHeight, + 'touch_total_count': behavior.touchTotalCount, + 'swipe_total_count': behavior.swipeTotalCount, + 'long_press_total_count': behavior.longPressTotalCount, + 'pinch_total_count': behavior.pinchTotalCount, + 'first_interaction_delay_ms': behavior.firstInteractionDelayMs, + 'page_stay_list': metrics.pageStayList, + 'behavior_sequence_hash': metrics.behaviorSequenceHash, + }; + } + + static int _intField(dynamic value, int fallback) { + if (value is int) return value; + if (value is num) return value.round(); + return int.tryParse(value?.toString() ?? '') ?? fallback; + } + + static String _stringField(dynamic value, String fallback) { + final text = value?.toString().trim() ?? ''; + return text.isEmpty ? fallback : text; + } + + static String _sessionEndReason(String value) { + final normalized = value.trim(); + const allowed = { + 'idle', + 'app_exit', + 'web_visibility_hidden', + 'forced', + }; + return allowed.contains(normalized) ? normalized : 'app_exit'; + } +} diff --git a/analytics_sdk/lib/entity/comic_event.dart b/analytics_sdk/lib/entity/comic_event.dart new file mode 100644 index 0000000..dc4c5c5 --- /dev/null +++ b/analytics_sdk/lib/entity/comic_event.dart @@ -0,0 +1,130 @@ +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; +import 'package:analytics_sdk/enum/read_behavior_enum.dart'; + +/// 漫画事件 +class ComicEvent { + final String event = EventType.comicEvent.event; + late final String eventId; + + /// 漫画 id + final String comicId; + + /// 漫画标题 + final String comicTitle; + + /// 分类ID + final String comicTypeId; + + /// 分类名称 + final String comicTypeName; + + /// 标签KEY, 多个标签使用英文逗号分隔 + final String comicTagKey; + + /// 标签名称,多个标签使用英文逗号分隔 + final String comicTagName; + + /// 推荐引擎的trace_id,未接推荐引擎传空字符串 + final String recommendTraceId; + + /// 阅读进度百分比(0-100) + final int readProgress; + + /// 当前阅读第几页(从1开始) + final int pageNo; + + /// 行为标识:view(展示), page_next(下一页), page_prev(上一页), complete(读完) 等 + final ReadBehaviorEnum comicBehavior; + + /// 章节ID,没有章节上报空字符串,没有对接搜索推荐引擎的上报空字符串 + final String chapterId; + + /// 章节名称,没有章节上报空字符串,没有对接搜索推荐引擎的上报空字符串 + final String chapterName; + + /// 老司机媒体资源ID,如未使用老司机库资源可传空字符串 + final String mediaId; + + late final Map _commonFields; + + ComicEvent({ + required this.comicId, + required this.comicTitle, + required this.comicTypeId, + required this.comicTypeName, + required this.comicTagKey, + required this.comicTagName, + required this.readProgress, + required this.pageNo, + required this.comicBehavior, + this.recommendTraceId = '', + this.mediaId = '', + this.chapterId = '', + this.chapterName = '', + }) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + comicId, + comicTitle, + comicTypeId, + comicTypeName, + comicTagKey, + comicTagName, + readProgress.toString(), + pageNo.toString(), + comicBehavior.key, + comicBehavior.name, + recommendTraceId, + mediaId, + chapterId, + chapterName, + ]); + } + + Map toJson() { + Map payload = { + 'comic_id': comicId, + 'comic_title': comicTitle, + 'comic_type_id': comicTypeId, + 'comic_type_name': comicTypeName, + 'comic_tag_key': comicTagKey, + 'comic_tag_name': comicTagName, + 'recommend_trace_id': recommendTraceId, + 'read_progress': readProgress, + 'page_no': pageNo, + 'comic_behavior_key': comicBehavior.key, + 'comic_behavior_name': comicBehavior.name, + 'media_id': mediaId, + 'chapter_id': chapterId, + 'chapter_name': chapterName, + }; + return {..._commonFields, 'event_id': eventId, "payload": payload}; + } + + factory ComicEvent.fromJson(Map json) { + final behaviorKey = json['comic_behavior_key'] as String? ?? ''; + final behaviorName = json['comic_behavior_name'] as String? ?? ''; + final behavior = ReadBehaviorEnum( + behaviorKey.isNotEmpty ? behaviorKey : ReadBehaviorEnum.VIEW.key, + behaviorName.isNotEmpty ? behaviorName : ReadBehaviorEnum.VIEW.name, + ); + + return ComicEvent( + comicId: json['comic_id'] as String, + comicTitle: json['comic_title'] as String, + comicTypeId: json['comic_type_id'] as String, + comicTypeName: json['comic_type_name'] as String, + comicTagKey: json['comic_tag_key'] as String, + comicTagName: json['comic_tag_name'] as String, + readProgress: json['read_progress'] as int, + pageNo: json['page_no'] as int, + comicBehavior: behavior, + recommendTraceId: json['recommend_trace_id'] as String? ?? '', + mediaId: json['media_id'] as String? ?? '', + chapterId: json['chapter_id'] as String? ?? '', + chapterName: json['chapter_name'] as String? ?? '', + ); + } +} diff --git a/analytics_sdk/lib/entity/keyword_click_event.dart b/analytics_sdk/lib/entity/keyword_click_event.dart new file mode 100644 index 0000000..2dc60ac --- /dev/null +++ b/analytics_sdk/lib/entity/keyword_click_event.dart @@ -0,0 +1,71 @@ +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; +import 'package:analytics_sdk/enum/click_item_type_enum.dart'; + +/// 关键词搜索结果点击事件 +class KeywordClickEvent { + final String event = EventType.keywordClick.event; + late final String eventId; + + /// 关联关键词 + final String keyword; + + /// 点击项目ID(视频ID/小说ID等) + final String clickItemId; + + /// 点击项目类型,可用预定义常量或自定义: + /// `ClickItemTypeEnum.video` / `ClickItemTypeEnum('manga', '漫画类')` + final ClickItemTypeEnum clickItemType; + + /// 点击位置(搜索结果中的排序位置) + final int clickPosition; + + /// 搜索引擎 trace ID,未接搜索引擎时传空字符串 + final String searchTraceId; + + late final Map _commonFields; + + KeywordClickEvent({ + required this.keyword, + required this.clickItemId, + required this.clickItemType, + required this.clickPosition, + this.searchTraceId = '', + }) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + keyword, + clickItemId, + clickItemType.key, + clickItemType.name, + clickPosition.toString(), + searchTraceId, + ]); + } + + Map toJson() { + Map payload = { + 'keyword': keyword, + 'click_item_id': clickItemId, + 'click_item_type_key': clickItemType.key, + 'click_item_type_name': clickItemType.name, + 'click_position': clickPosition, + 'search_trace_id': searchTraceId, + }; + return {..._commonFields, 'event_id': eventId, "payload": payload}; + } + + factory KeywordClickEvent.fromJson(Map json) { + return KeywordClickEvent( + keyword: json['keyword'] as String, + clickItemId: json['click_item_id'] as String, + clickItemType: ClickItemTypeEnum( + json['click_item_type_key'] as String, + json['click_item_type_name'] as String, + ), + clickPosition: json['click_position'] as int, + searchTraceId: json['search_trace_id'] as String? ?? '', + ); + } +} diff --git a/analytics_sdk/lib/entity/keyword_search_event.dart b/analytics_sdk/lib/entity/keyword_search_event.dart new file mode 100644 index 0000000..f64aa2b --- /dev/null +++ b/analytics_sdk/lib/entity/keyword_search_event.dart @@ -0,0 +1,65 @@ +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; + +/// 关键词搜索事件 +class KeywordSearchEvent { + final String event = EventType.keywordSearch.event; + late final String eventId; + + /// 搜索关键词 + final String keyword; + + /// 搜索结果数量 + final int searchResultCount; + + /// 搜索引擎 trace ID,未接搜索引擎时传空字符串 + final String searchTraceId; + + /// 搜索引擎 search ID,未接搜索引擎时传空字符串 + final String searchId; + + /// 搜索列表的内容类型:video / novel / comic + /// 未接搜索推荐引擎时传空字符串 + final String searchContentType; + + late final Map _commonFields; + + KeywordSearchEvent({ + required this.keyword, + required this.searchResultCount, + this.searchTraceId = '', + this.searchId = '', + this.searchContentType = '', + }) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + keyword, + searchResultCount.toString(), + searchTraceId, + searchId, + searchContentType, + ]); + } + + Map toJson() { + Map payload = { + 'keyword': keyword, + 'search_result_count': searchResultCount, + 'search_trace_id': searchTraceId, + 'search_id': searchId, + 'search_content_type': searchContentType, + }; + return {..._commonFields, 'event_id': eventId, "payload": payload}; + } + + factory KeywordSearchEvent.fromJson(Map json) { + return KeywordSearchEvent( + keyword: json['keyword'] as String, + searchResultCount: json['search_result_count'] as int, + searchTraceId: json['search_trace_id'] as String? ?? '', + searchId: json['search_id'] as String? ?? '', + searchContentType: json['search_content_type'] as String? ?? '', + ); + } +} diff --git a/analytics_sdk/lib/entity/navigation_event.dart b/analytics_sdk/lib/entity/navigation_event.dart new file mode 100644 index 0000000..5024bbe --- /dev/null +++ b/analytics_sdk/lib/entity/navigation_event.dart @@ -0,0 +1,67 @@ +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; + +/// 导航菜单点击事件(纯手动上报)。 +/// +/// 仅在用户主动点击导航菜单时触发:底部 Tab、顶部 Tab、侧边抽屉等。 +/// SDK 不会在路由跳转时自动发送此事件;触发方式: +/// - [AnalyticsSdk.trackNavigation] 手动调用 +/// - [BottomNavigationBar.withAnalytics] / [TabBar.withAnalytics] 包装器内部自动处理 +/// +/// **page_key / page_name 填写规范** +/// +/// 建议使用 [AppPageEnum] 预定义值,也可直接传入自定义字符串: +/// ```dart +/// NavigationEvent( +/// navigationKey: AppPageEnum.video.key, // 'video' +/// navigationName: AppPageEnum.video.name, // '影视' +/// ) +/// ``` +/// +/// **上报时序**:应在同次导航的 [AppPageViewEvent] 之前发出。 +class NavigationEvent { + final String event = EventType.navigation.event; + late final String eventId; + + /// 导航目标的唯一标识,与对应页面的 [AppPageViewEvent.pageKey] 保持一致。 + /// + /// 示例:`home`(首页)、`video`(影视)、`video_hot`(影视·热门子 Tab) + final String navigationKey; + + /// 导航目标的展示名称。 + /// + /// 示例:`首页`、`影视`、`影视·热门` + final String navigationName; + + late final Map _commonFields; + + NavigationEvent({ + required String navigationKey, + required this.navigationName, + }) : navigationKey = PageNameMapper.normalizeKey(navigationKey) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + this.navigationKey, + navigationName, + ]); + } + + /// 序列化为上报 JSON;payload 包含 `navigation_key` 和 `navigation_name`。 + Map toJson() { + Map payload = { + 'navigation_key': navigationKey, + 'navigation_name': navigationName, + }; + return {..._commonFields, 'event_id': eventId, "payload": payload}; + } + + /// 从 JSON 反序列化(通常用于缓存恢复场景)。 + factory NavigationEvent.fromJson(Map json) { + return NavigationEvent( + navigationKey: json['navigation_key'] as String, + navigationName: json['navigation_name'] as String, + ); + } +} diff --git a/analytics_sdk/lib/entity/network_state_change_event.dart b/analytics_sdk/lib/entity/network_state_change_event.dart new file mode 100644 index 0000000..f02d92e --- /dev/null +++ b/analytics_sdk/lib/entity/network_state_change_event.dart @@ -0,0 +1,89 @@ +import 'dart:convert'; + +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; +import 'package:analytics_sdk/utils/network_type_resolver.dart'; + +/// 网络状态变化事件。 +/// +/// 文档要求除公共字段外,下表字段写入 `payload`。 +/// 完整设备环境、SIM 详情和当前页面信息不在本事件重传。 +class NetworkStateChangeEvent { + final String event = EventType.networkStateChange.event; + final Map _payloadFields; + + late final String eventId; + late final Map _commonFields; + + NetworkStateChangeEvent({ + required String networkType, + required String fromNetworkType, + required int isOnline, + required int changeCountInSid, + String carrier = '', + Map webConnectionFields = const {}, + }) : _payloadFields = _buildPayloadFields( + networkType: networkType, + fromNetworkType: fromNetworkType, + isOnline: isOnline, + changeCountInSid: changeCountInSid, + carrier: carrier, + webConnectionFields: webConnectionFields, + ) { + _commonFields = SdkContext.generateCommonFields(event); + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + jsonEncode(_stablePayloadFields), + ]); + } + + Map toJson() { + return { + ..._commonFields, + 'event_id': eventId, + 'payload': Map.from(_payloadFields), + }; + } + + Map get _stablePayloadFields { + final sortedKeys = _payloadFields.keys.toList()..sort(); + return { + for (final key in sortedKeys) key: _payloadFields[key], + }; + } + + static Map _buildPayloadFields({ + required String networkType, + required String fromNetworkType, + required int isOnline, + required int changeCountInSid, + required String carrier, + required Map webConnectionFields, + }) { + final normalizedNetworkType = _networkType(networkType); + final fields = { + 'network_type': normalizedNetworkType, + 'carrier': carrier.trim(), + 'from_network_type': _networkType(fromNetworkType), + 'to_network_type': normalizedNetworkType, + 'is_online': isOnlineForNetworkType(normalizedNetworkType), + 'change_count_in_sid': changeCountInSid < 1 ? 1 : changeCountInSid, + }; + + for (final key in const [ + 'connection_type', + 'effective_type', + 'downlink', + 'rtt', + ]) { + final value = webConnectionFields[key]; + if (value == null) continue; + if (value is String && value.trim().isEmpty) continue; + fields[key] = value; + } + return fields; + } + + static String _networkType(String value) { + return normalizeNetworkType(value); + } +} diff --git a/analytics_sdk/lib/entity/novel_event.dart b/analytics_sdk/lib/entity/novel_event.dart new file mode 100644 index 0000000..1cb4926 --- /dev/null +++ b/analytics_sdk/lib/entity/novel_event.dart @@ -0,0 +1,130 @@ +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; +import 'package:analytics_sdk/enum/read_behavior_enum.dart'; + +/// 小说事件 +class NovelEvent { + final String event = EventType.novelEvent.event; + late final String eventId; + + /// 小说 id + final String novelId; + + /// 小说标题 + final String novelTitle; + + /// 分类ID + final String novelTypeId; + + /// 分类名称 + final String novelTypeName; + + /// 标签KEY, 多个标签使用英文逗号分隔 + final String novelTagKey; + + /// 标签名称,多个标签使用英文逗号分隔 + final String novelTagName; + + /// 推荐引擎的trace_id,未接推荐引擎传空字符串 + final String recommendTraceId; + + /// 阅读进度百分比(0-100) + final int readProgress; + + /// 当前阅读第几页(从1开始) + final int pageNo; + + /// 行为标识:view(展示), page_next(下一页), page_prev(上一页), complete(读完) 等 + final ReadBehaviorEnum novelBehavior; + + /// 章节ID,没有章节上报空字符串,没有对接搜索推荐引擎的上报空字符串 + final String chapterId; + + /// 章节名称,没有章节上报空字符串,没有对接搜索推荐引擎的上报空字符串 + final String chapterName; + + /// 老司机媒体资源ID,如未使用老司机库资源可传空字符串 + final String mediaId; + + late final Map _commonFields; + + NovelEvent({ + required this.novelId, + required this.novelTitle, + required this.novelTypeId, + required this.novelTypeName, + required this.novelTagKey, + required this.novelTagName, + required this.readProgress, + required this.pageNo, + required this.novelBehavior, + this.recommendTraceId = '', + this.mediaId = '', + this.chapterId = '', + this.chapterName = '', + }) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + novelId, + novelTitle, + novelTypeId, + novelTypeName, + novelTagKey, + novelTagName, + readProgress.toString(), + pageNo.toString(), + novelBehavior.key, + novelBehavior.name, + recommendTraceId, + mediaId, + chapterId, + chapterName, + ]); + } + + Map toJson() { + Map payload = { + 'novel_id': novelId, + 'novel_title': novelTitle, + 'novel_type_id': novelTypeId, + 'novel_type_name': novelTypeName, + 'novel_tag_key': novelTagKey, + 'novel_tag_name': novelTagName, + 'recommend_trace_id': recommendTraceId, + 'read_progress': readProgress, + 'page_no': pageNo, + 'novel_behavior_key': novelBehavior.key, + 'novel_behavior_name': novelBehavior.name, + 'media_id': mediaId, + 'chapter_id': chapterId, + 'chapter_name': chapterName, + }; + return {..._commonFields, 'event_id': eventId, "payload": payload}; + } + + factory NovelEvent.fromJson(Map json) { + final behaviorKey = json['novel_behavior_key'] as String? ?? ''; + final behaviorName = json['novel_behavior_name'] as String? ?? ''; + final behavior = ReadBehaviorEnum( + behaviorKey.isNotEmpty ? behaviorKey : ReadBehaviorEnum.VIEW.key, + behaviorName.isNotEmpty ? behaviorName : ReadBehaviorEnum.VIEW.name, + ); + + return NovelEvent( + novelId: json['novel_id'] as String, + novelTitle: json['novel_title'] as String, + novelTypeId: json['novel_type_id'] as String, + novelTypeName: json['novel_type_name'] as String, + novelTagKey: json['novel_tag_key'] as String, + novelTagName: json['novel_tag_name'] as String, + readProgress: json['read_progress'] as int, + pageNo: json['page_no'] as int, + novelBehavior: behavior, + recommendTraceId: json['recommend_trace_id'] as String? ?? '', + mediaId: json['media_id'] as String? ?? '', + chapterId: json['chapter_id'] as String? ?? '', + chapterName: json['chapter_name'] as String? ?? '', + ); + } +} diff --git a/analytics_sdk/lib/entity/page_click_event.dart b/analytics_sdk/lib/entity/page_click_event.dart new file mode 100644 index 0000000..38eda6b --- /dev/null +++ b/analytics_sdk/lib/entity/page_click_event.dart @@ -0,0 +1,284 @@ +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/page_click_auto_fields.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; + +/// 应用页面点击日志 (自动) +class PageClickEvent { + final String event = EventType.pageClick.event; + late final String eventId; + + final String pageKey; + + /// 页面名称:首页, 我的, 视频页 + final String pageName; + + /// 点击位置坐标x轴 + final int clickPageX; + + /// 点击位置坐标y轴 + final int clickPageY; + + /// SDK 标准化点击相对当前页面根容器可视区域左上角的 X 坐标。 + final int clickViewportX; + + /// SDK 标准化点击相对当前页面根容器可视区域左上角的 Y 坐标。 + final int clickViewportY; + + /// 点击相对当前页面主内容完整区域左上角的 X 坐标。 + final int clickDocumentX; + + /// 点击相对当前页面主内容完整区域左上角的 Y 坐标。 + final int clickDocumentY; + + /// 点击位置横坐标百分比(0-100) + final int clickXPercent; + + /// 点击位置纵坐标百分比(0-100) + final int clickYPercent; + + /// 屏幕宽度(像素) + final int screenWidth; + + /// 屏幕高度(像素) + final int screenHeight; + + /// 点击时当前页面根容器可交互可视区域宽度。 + final int viewportWidth; + + /// 点击时当前页面根容器可交互可视区域高度。 + final int viewportHeight; + + /// 点击时当前页面主内容容器完整宽度。 + final int pageWidth; + + /// 点击时当前页面主内容容器完整高度。 + final int pageHeight; + + /// 点击时当前页面主内容容器纵向滚动偏移。 + final int scrollTop; + + /// 点击时当前页面主内容容器横向滚动偏移。 + final int scrollLeft; + + /// 推荐引擎的trace_id,有多个推荐列表时用英文逗号分隔,未接推荐引擎传空字符串 + final String recommendTraceId; + + late final Map _commonFields; + + PageClickEvent({ + required String pageKey, + required this.pageName, + required this.clickPageX, + required this.clickPageY, + required this.clickXPercent, + required this.clickYPercent, + required this.screenWidth, + required this.screenHeight, + int? clickViewportX, + int? clickViewportY, + int? clickDocumentX, + int? clickDocumentY, + int? viewportWidth, + int? viewportHeight, + int? pageWidth, + int? pageHeight, + int? scrollTop, + int? scrollLeft, + this.recommendTraceId = '', + }) : pageKey = PageNameMapper.normalizeKey(pageKey), + clickViewportX = _coordinateOrFallback(clickViewportX, clickPageX), + clickViewportY = _coordinateOrFallback(clickViewportY, clickPageY), + viewportWidth = _dimensionOrFallback(viewportWidth, screenWidth), + viewportHeight = _dimensionOrFallback(viewportHeight, screenHeight), + clickDocumentX = _hasInvalidHorizontalDocumentMetrics( + pageWidth: pageWidth, + viewportWidth: viewportWidth, + screenWidth: screenWidth, + scrollLeft: scrollLeft, + ) + ? -1 + : _unknownable(clickDocumentX), + clickDocumentY = _unknownable(clickDocumentY), + pageWidth = _hasInvalidHorizontalDocumentMetrics( + pageWidth: pageWidth, + viewportWidth: viewportWidth, + screenWidth: screenWidth, + scrollLeft: scrollLeft, + ) + ? -1 + : _unknownable(pageWidth), + pageHeight = _unknownable(pageHeight), + scrollTop = _unknownable(scrollTop), + scrollLeft = _hasInvalidHorizontalDocumentMetrics( + pageWidth: pageWidth, + viewportWidth: viewportWidth, + screenWidth: screenWidth, + scrollLeft: scrollLeft, + ) + ? -1 + : _unknownable(scrollLeft) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + this.pageKey, + pageName, + clickPageX.toString(), + clickPageY.toString(), + this.clickViewportX.toString(), + this.clickViewportY.toString(), + this.clickDocumentX.toString(), + this.clickDocumentY.toString(), + clickXPercent.toString(), + clickYPercent.toString(), + screenWidth.toString(), + screenHeight.toString(), + this.viewportWidth.toString(), + this.viewportHeight.toString(), + this.pageWidth.toString(), + this.pageHeight.toString(), + this.scrollTop.toString(), + this.scrollLeft.toString(), + recommendTraceId, + ]); + } + + Map toJson() { + Map payload = { + 'page_key': pageKey, + 'page_name': pageName, + 'click_page_x': clickPageX, + 'click_page_y': clickPageY, + 'click_viewport_x': clickViewportX, + 'click_viewport_y': clickViewportY, + 'click_document_x': clickDocumentX, + 'click_document_y': clickDocumentY, + 'click_x_percent': clickXPercent, + 'click_y_percent': clickYPercent, + 'screen_width': screenWidth, + 'screen_height': screenHeight, + 'viewport_width': viewportWidth, + 'viewport_height': viewportHeight, + 'page_width': pageWidth, + 'page_height': pageHeight, + 'scroll_top': scrollTop, + 'scroll_left': scrollLeft, + 'recommend_trace_id': recommendTraceId, + }; + return { + ..._commonFields, + 'event_id': eventId, + "payload": payload, + }; + } + + factory PageClickEvent.fromJson(Map json) { + return PageClickEvent( + pageKey: json['page_key'] as String, + pageName: json['page_name'] as String, + clickPageX: json['click_page_x'] as int, + clickPageY: json['click_page_y'] as int, + clickXPercent: json['click_x_percent'] as int, + clickYPercent: json['click_y_percent'] as int, + screenWidth: json['screen_width'] as int, + screenHeight: json['screen_height'] as int, + clickViewportX: json['click_viewport_x'] as int?, + clickViewportY: json['click_viewport_y'] as int?, + clickDocumentX: json['click_document_x'] as int?, + clickDocumentY: json['click_document_y'] as int?, + viewportWidth: json['viewport_width'] as int?, + viewportHeight: json['viewport_height'] as int?, + pageWidth: json['page_width'] as int?, + pageHeight: json['page_height'] as int?, + scrollTop: json['scroll_top'] as int?, + scrollLeft: json['scroll_left'] as int?, + recommendTraceId: json['recommend_trace_id'] as String? ?? '', + ); + } + + static PageClickEvent fromViewport({ + required String pageKey, + required String pageName, + required int clickPageX, + required int clickPageY, + required int clickViewportX, + required int clickViewportY, + required int viewportWidth, + required int viewportHeight, + int? clickDocumentX, + int? clickDocumentY, + int? pageWidth, + int? pageHeight, + int? scrollTop, + int? scrollLeft, + String recommendTraceId = '', + }) { + final documentMetrics = currentClickDocumentMetrics( + clickViewportX: clickViewportX, + clickViewportY: clickViewportY, + ); + final clickXPercent = viewportWidth > 0 + ? (clickViewportX / viewportWidth * 100).round().clamp(0, 100) + : 0; + final clickYPercent = viewportHeight > 0 + ? (clickViewportY / viewportHeight * 100).round().clamp(0, 100) + : 0; + return PageClickEvent( + pageKey: pageKey, + pageName: pageName, + clickPageX: clickPageX, + clickPageY: clickPageY, + clickXPercent: clickXPercent, + clickYPercent: clickYPercent, + screenWidth: viewportWidth, + screenHeight: viewportHeight, + clickViewportX: clickViewportX, + clickViewportY: clickViewportY, + clickDocumentX: clickDocumentX ?? documentMetrics.clickDocumentX, + clickDocumentY: clickDocumentY ?? documentMetrics.clickDocumentY, + viewportWidth: viewportWidth, + viewportHeight: viewportHeight, + pageWidth: pageWidth ?? documentMetrics.pageWidth, + pageHeight: pageHeight ?? documentMetrics.pageHeight, + scrollTop: scrollTop ?? documentMetrics.scrollTop, + scrollLeft: scrollLeft ?? documentMetrics.scrollLeft, + recommendTraceId: recommendTraceId, + ); + } + + static int _coordinateOrFallback(int? value, int fallback) { + final resolved = value ?? fallback; + return resolved < 0 ? -1 : resolved; + } + + static int _dimensionOrFallback(int? value, int fallback) { + final resolved = value ?? fallback; + return resolved < 0 ? -1 : resolved; + } + + static int _unknownable(int? value) { + if (value == null || value < 0) return -1; + return value; + } + + static bool _hasInvalidHorizontalDocumentMetrics({ + required int? pageWidth, + required int? viewportWidth, + required int screenWidth, + required int? scrollLeft, + }) { + final resolvedPageWidth = _unknownable(pageWidth); + final resolvedViewportWidth = + _dimensionOrFallback(viewportWidth, screenWidth); + final resolvedScrollLeft = _unknownable(scrollLeft); + + if (resolvedPageWidth < 0 || + resolvedViewportWidth < 0 || + resolvedScrollLeft <= 0) { + return false; + } + + final maxScrollLeft = resolvedPageWidth - resolvedViewportWidth; + return maxScrollLeft < 0 || resolvedScrollLeft > maxScrollLeft; + } +} diff --git a/analytics_sdk/lib/entity/recommend_list_click_event.dart b/analytics_sdk/lib/entity/recommend_list_click_event.dart new file mode 100644 index 0000000..7e66f8e --- /dev/null +++ b/analytics_sdk/lib/entity/recommend_list_click_event.dart @@ -0,0 +1,87 @@ +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; +import 'package:analytics_sdk/enum/recommend_content_type_enum.dart'; + +/// 推荐引擎列表点击事件(服务端/客户端) +class RecommendListClickEvent { + /// 事件类型(固定) + final String event = EventType.recommendListClick.event; + + /// 去重 ID(动态计算) + late final String eventId; + + /// 页面标识:page_home(首页), page_user(我的), page_video(视频页)等 + final String pageKey; + + /// 页面名称:首页, 我的, 视频页 + final String pageName; + + /// 推荐列表的类型: video(视频),novel(小说),comic(漫画) + final RecommendContentTypeEnum recommendContentType; + + /// 推荐引擎的trace_id,未接推荐引擎传空字符串 + final String recommendTraceId; + + /// 点击内容的ID + final String recommendId; + + /// 推荐引擎的trace_info,未接推荐引擎传空字符串 + final String recommendTraceInfo; + + /// 客户端版本,三位数字点分隔,例如:1.0.1 + final String clientVersion; + + late final Map _commonFields; + + RecommendListClickEvent({ + required String pageKey, + required this.pageName, + required this.recommendContentType, + required this.recommendId, + this.recommendTraceId = '', + this.recommendTraceInfo = '', + this.clientVersion = '', + }) : pageKey = PageNameMapper.normalizeKey(pageKey) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + pageKey, + pageName, + recommendContentType.label, + recommendTraceId, + recommendId, + recommendTraceInfo, + clientVersion, + ]); + } + + Map toJson() { + final payload = { + 'page_key': pageKey, + 'page_name': pageName, + 'recommend_content_type': recommendContentType.label, + 'recommend_trace_id': recommendTraceId, + 'recommend_id': recommendId, + 'recommend_trace_info': recommendTraceInfo, + 'client_version': clientVersion, + }; + return {..._commonFields, 'event_id': eventId, 'payload': payload}; + } + + factory RecommendListClickEvent.fromJson(Map json) { + final contentTypeStr = json['recommend_content_type'] as String? ?? ''; + final contentType = RecommendContentTypeEnum( + contentTypeStr.isNotEmpty ? contentTypeStr : RecommendContentTypeEnum.VIDEO.label, + ); + return RecommendListClickEvent( + pageKey: json['page_key'] as String, + pageName: json['page_name'] as String, + recommendContentType: contentType, + recommendTraceId: json['recommend_trace_id'] as String? ?? '', + recommendId: json['recommend_id'] as String? ?? '', + recommendTraceInfo: json['recommend_trace_info'] as String? ?? '', + clientVersion: json['client_version'] as String? ?? '', + ); + } +} diff --git a/analytics_sdk/lib/entity/recommend_list_view_event.dart b/analytics_sdk/lib/entity/recommend_list_view_event.dart new file mode 100644 index 0000000..7f222ee --- /dev/null +++ b/analytics_sdk/lib/entity/recommend_list_view_event.dart @@ -0,0 +1,88 @@ +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; +import 'package:analytics_sdk/enum/recommend_content_type_enum.dart'; + +/// 推荐列表内容展示事件(服务端/客户端) +class RecommendListViewEvent { + /// 事件类型(固定) + final String event = EventType.recommendListView.event; + + /// 去重 ID(动态计算) + late final String eventId; + + /// 页面标识:home(首页), video_detail(视频详情), user_center(个人中心), discover(发现页)等 + final String pageKey; + + /// 页面名称:首页, 视频详情页, 个人中心, 发现页 + final String pageName; + + /// 推荐列表的类型: video(视频),novel(小说),comic(漫画) + final RecommendContentTypeEnum recommendContentType; + + /// 推荐引擎的trace_id + final String recommendTraceId; + + /// 推荐引擎列表的video_id(视频),novel_id(小说),comic_id(漫画), 可视窗口有多个,使用英文逗号分隔 + final String recommendId; + + /// 推荐引擎的trace_info,未接推荐引擎传空字符串 + final String recommendTraceInfo; + + /// 客户端版本,三位数字点分隔,例如:1.0.1 + final String clientVersion; + + late final Map _commonFields; + + RecommendListViewEvent({ + required String pageKey, + required this.pageName, + required this.recommendContentType, + required this.recommendId, + this.recommendTraceId = '', + this.recommendTraceInfo = '', + this.clientVersion = '', + }) : pageKey = PageNameMapper.normalizeKey(pageKey) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + pageKey, + pageName, + recommendContentType.label, + recommendTraceId, + recommendId, + recommendTraceInfo, + clientVersion, + ]); + } + + Map toJson() { + final payload = { + 'event': event, + 'page_key': pageKey, + 'page_name': pageName, + 'recommend_content_type': recommendContentType.label, + 'recommend_trace_id': recommendTraceId, + 'recommend_id': recommendId, + 'recommend_trace_info': recommendTraceInfo, + 'client_version': clientVersion, + }; + return {..._commonFields, 'event_id': eventId, 'payload': payload}; + } + + factory RecommendListViewEvent.fromJson(Map json) { + final contentTypeStr = json['recommend_content_type'] as String? ?? ''; + final contentType = RecommendContentTypeEnum( + contentTypeStr.isNotEmpty ? contentTypeStr : RecommendContentTypeEnum.VIDEO.label, + ); + return RecommendListViewEvent( + pageKey: json['page_key'] as String, + pageName: json['page_name'] as String, + recommendContentType: contentType, + recommendTraceId: json['recommend_trace_id'] as String, + recommendId: json['recommend_id'] as String, + recommendTraceInfo: json['recommend_trace_info'] as String? ?? '', + clientVersion: json['client_version'] as String? ?? '', + ); + } +} diff --git a/analytics_sdk/lib/entity/sdk_init_event.dart b/analytics_sdk/lib/entity/sdk_init_event.dart new file mode 100644 index 0000000..75259cd --- /dev/null +++ b/analytics_sdk/lib/entity/sdk_init_event.dart @@ -0,0 +1,32 @@ +import 'dart:convert'; + +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; + +/// SDK 初始化快照事件。 +/// +/// `sdk_init` 不携带 `device_env`,硬件与环境快照字段直接写入 payload。 +class SdkInitEvent { + final String event = EventType.sdkInit.event; + late final String eventId; + + final Map payload; + + late final Map _commonFields; + + SdkInitEvent({required Map payload}) + : payload = Map.from(payload) { + _commonFields = SdkContext.generateCommonFields(event); + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + jsonEncode(this.payload), + ]); + } + + Map toJson() { + return { + ..._commonFields, + 'event_id': eventId, + 'payload': payload, + }; + } +} diff --git a/analytics_sdk/lib/entity/video_event.dart b/analytics_sdk/lib/entity/video_event.dart new file mode 100644 index 0000000..594551d --- /dev/null +++ b/analytics_sdk/lib/entity/video_event.dart @@ -0,0 +1,132 @@ +import 'package:analytics_sdk/const/event_type.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; +import 'package:analytics_sdk/enum/video_content_type_enum.dart'; +import 'package:analytics_sdk/enum/video_event_enum.dart'; + +/// 视频事件 (手动) +class VideoEvent { + final String event = EventType.videoEvent.event; + late final String eventId; + + final String videoId; + + /// 视频标题 + final String videoTitle; + + /// 视频分类ID + final String videoTypeId; + + /// 视频分类名称 + final String videoTypeName; + + /// 标签名称,多个标签使用英文逗号分隔 + final String videoTagName; + + /// 标签KEY, 多个标签使用英文逗号分隔 + final String videoTagKey; + + /// 视频总时长(秒) + final int videoDuration; + + /// 本次播放时长(秒) + final int playDuration; + + /// 播放进度百分比(0-100) + final int playProgress; + + /// 视频行为标识:video_play(播放), video_pause(暂停), video_share(分享), video_complete(播放完成), video_forward(快进), video_rewind(快退)等 + final VideoEventEnum videoBehavior; + + /// 视频内容类型:video(长视频), short_video(短视频) + final VideoContentTypeEnum? videoContentType; + + /// 推荐引擎的trace_id,未接推荐引擎传空字符串 + final String recommendTraceId; + + /// 老司机媒体资源ID,视频分析日志新加字段,可为空字符串 + final String mediaId; + + late final Map _commonFields; + + VideoEvent( + {required this.videoId, + required this.videoTitle, + required this.videoTypeId, + required this.videoTypeName, + required this.videoTagKey, + required this.videoTagName, + required this.videoDuration, + required this.playDuration, + required this.playProgress, + required this.videoBehavior, + required this.videoContentType, + this.recommendTraceId = '', + this.mediaId = ''}) { + _commonFields = SdkContext.generateCommonFields(event); + + eventId = SdkContext.generateEventIdFromCommonFields(_commonFields, [ + videoId, + videoTitle, + videoTypeId, + videoTypeName, + videoTagKey, + videoTagName, + videoDuration.toString(), + playDuration.toString(), + playProgress.toString(), + videoBehavior.key, + videoBehavior.name, + videoContentType?.label ?? '', + recommendTraceId, + ]); + } + + Map toJson() { + Map payload = { + 'video_id': videoId, + 'video_title': videoTitle, + 'video_type_id': videoTypeId, + 'video_type_name': videoTypeName, + 'video_tag_key': videoTagKey, + 'video_tag_name': videoTagName, + 'video_duration': videoDuration, + 'play_duration': playDuration, + 'play_progress': playProgress, + 'video_behavior_key': videoBehavior.key, + 'video_behavior_name': videoBehavior.name, + 'video_content_type': videoContentType?.label ?? '', + 'recommend_trace_id': recommendTraceId, + 'media_id': mediaId, + }; + return {..._commonFields, 'event_id': eventId, "payload": payload}; + } + + factory VideoEvent.fromJson(Map json) { + final behaviorKey = json['video_behavior_key'] as String? ?? ''; + final behaviorName = json['video_behavior_name'] as String? ?? ''; + final behavior = VideoEventEnum( + behaviorKey.isNotEmpty ? behaviorKey : VideoEventEnum.VIDEO_PLAY.key, + behaviorName.isNotEmpty ? behaviorName : VideoEventEnum.VIDEO_PLAY.name, + ); + + final contentTypeStr = json['video_content_type'] as String? ?? ''; + final VideoContentTypeEnum? contentType = + contentTypeStr.isNotEmpty ? VideoContentTypeEnum(contentTypeStr) : null; + + return VideoEvent( + videoId: json['video_id'] as String, + videoTitle: json['video_title'] as String, + videoTypeId: json['video_type_id'] as String, + videoTypeName: json['video_type_name'] as String, + videoTagKey: json['video_tag_key'] as String, + videoTagName: json['video_tag_name'] as String, + videoDuration: json['video_duration'] as int, + playDuration: json['play_duration'] as int, + playProgress: json['play_progress'] as int, + videoBehavior: behavior, + videoContentType: contentType, + recommendTraceId: json['recommend_trace_id'] as String? ?? '', + mediaId: json['media_id'] as String? ?? '', + ); + } +} diff --git a/analytics_sdk/lib/enum/ad_type_enum.dart b/analytics_sdk/lib/enum/ad_type_enum.dart new file mode 100644 index 0000000..b8157fd --- /dev/null +++ b/analytics_sdk/lib/enum/ad_type_enum.dart @@ -0,0 +1,40 @@ +/// 广告类型枚举 +/// 对应埋点中的 ad_type 字段:icon、banner、feed、player、splash、text +enum AdTypeEnum { + /// 图标广告 + icon(label: 'icon'), + + /// 横幅广告 + banner(label: 'banner'), + + /// 信息流广告 + feed(label: 'feed'), + + /// 播放器内广告 + player(label: 'player'), + + /// 开屏广告 + splash(label: 'splash'), + + /// 文字广告 + text(label: 'text'), + + ; + + /// 上报时使用的字符串值 + final String label; + + const AdTypeEnum({ + required this.label, + }); + + /// 根据字符串解析为枚举,未匹配时返回 null + static AdTypeEnum? fromLabel(String? value) { + if (value == null || value.isEmpty) return null; + for (final e in AdTypeEnum.values) { + if (e.label == value) return e; + } + return null; + } +} + diff --git a/analytics_sdk/lib/enum/auth_type_enum.dart b/analytics_sdk/lib/enum/auth_type_enum.dart new file mode 100644 index 0000000..335fb7a --- /dev/null +++ b/analytics_sdk/lib/enum/auth_type_enum.dart @@ -0,0 +1,25 @@ +/// 认证/注册/登录方式枚举 +/// 对应文档中的 type 字段:phone、deviceid、email、username +enum AuthTypeEnum { + /// 手机号 + PHONE(label: 'phone'), + + /// 设备ID + DEVICE_ID(label: 'deviceid'), + + /// 邮箱 + EMAIL(label: 'email'), + + /// 用户名/账号 + USERNAME(label: 'username'), + + ; + + /// 上报时使用的字符串值 + final String label; + + const AuthTypeEnum({ + required this.label, + }); +} + diff --git a/analytics_sdk/lib/enum/banner_event_enum.dart b/analytics_sdk/lib/enum/banner_event_enum.dart new file mode 100644 index 0000000..dd19afe --- /dev/null +++ b/analytics_sdk/lib/enum/banner_event_enum.dart @@ -0,0 +1,11 @@ +enum BannerEventEnum{ + CLICK(label: 'click'), + CLOSE(label: 'close'), + SHOW(label: 'show'); + + final String label; + + const BannerEventEnum({ + required this.label, + }); +} \ No newline at end of file diff --git a/analytics_sdk/lib/enum/click_item_type_enum.dart b/analytics_sdk/lib/enum/click_item_type_enum.dart new file mode 100644 index 0000000..ce3f8e0 --- /dev/null +++ b/analytics_sdk/lib/enum/click_item_type_enum.dart @@ -0,0 +1,34 @@ +/// 点击项目类型。 +/// +/// 可直接使用预定义常量,也可传入自定义字符串: +/// ```dart +/// ClickItemTypeEnum.video // 预定义 +/// ClickItemTypeEnum('manga', '漫画类') // 自定义 +/// ``` +/// +/// 字段规则(debug 模式下触发 assert): +/// - [key]:仅限字母、数字、`_`、`-` +/// - [name]:字母、数字、`_`、`-`、中文 +class ClickItemTypeEnum { + final String key; + final String name; + + const ClickItemTypeEnum._internal(this.key, this.name); + + /// 自定义类型。[key] 仅限字母/数字/`_`/`-`;[name] 允许中文。 + factory ClickItemTypeEnum(String key, String name) { + assert( + RegExp(r'^[A-Za-z0-9_-]+$').hasMatch(key), + 'ClickItemTypeEnum.key 仅支持字母、数字、_ 和 -,实际传入: "$key"', + ); + assert( + RegExp(r'^[\u4e00-\u9fa5A-Za-z0-9_-]+$').hasMatch(name), + 'ClickItemTypeEnum.name 仅支持字母、数字、_、- 和中文,实际传入: "$name"', + ); + return ClickItemTypeEnum._internal(key, name); + } + + static const VIDEO = ClickItemTypeEnum._internal('video', '视频'); + static const NOVEL = ClickItemTypeEnum._internal('novel', '小说'); + static const COMIC = ClickItemTypeEnum._internal('comic', '漫画'); +} diff --git a/analytics_sdk/lib/enum/consume_reason_enum.dart b/analytics_sdk/lib/enum/consume_reason_enum.dart new file mode 100644 index 0000000..7071928 --- /dev/null +++ b/analytics_sdk/lib/enum/consume_reason_enum.dart @@ -0,0 +1,25 @@ +/// 金币消耗原因枚举 +/// 对应文档中的 consume_reason_key 字段: +/// - video_unlock(视频解锁) +/// - gift_send(礼物赠送) +/// - content_purchase(内容购买) +enum ConsumeReasonEnum { + /// 视频解锁 + VIDEO_UNLOCK(label: 'video_unlock'), + + /// 礼物赠送 + GIFT_SEND(label: 'gift_send'), + + /// 内容购买 + CONTENT_PURCHASE(label: 'content_purchase'), + + ; + + /// 上报时使用的字符串值 + final String label; + + const ConsumeReasonEnum({ + required this.label, + }); +} + diff --git a/analytics_sdk/lib/enum/currency_enum.dart b/analytics_sdk/lib/enum/currency_enum.dart new file mode 100644 index 0000000..3ba47f6 --- /dev/null +++ b/analytics_sdk/lib/enum/currency_enum.dart @@ -0,0 +1,19 @@ +/// 货币类型枚举 +/// 对应文档中的 currency 字段:CNY(人民币), USD(美元) +enum CurrencyEnum { + /// 人民币 + CNY(label: 'CNY'), + + /// 美元 + USD(label: 'USD'), + + ; + + /// 上报时使用的字符串值 + final String label; + + const CurrencyEnum({ + required this.label, + }); +} + diff --git a/analytics_sdk/lib/enum/device_enum.dart b/analytics_sdk/lib/enum/device_enum.dart new file mode 100644 index 0000000..8bbfd03 --- /dev/null +++ b/analytics_sdk/lib/enum/device_enum.dart @@ -0,0 +1,11 @@ +enum DeviceEnum { + Android(label: 'Android'), + iOS(label: 'iOS'), + PC(label: 'PC'); + + final String label; + + const DeviceEnum({ + required this.label, + }); +} diff --git a/analytics_sdk/lib/enum/order_type_enum.dart b/analytics_sdk/lib/enum/order_type_enum.dart new file mode 100644 index 0000000..8469dd9 --- /dev/null +++ b/analytics_sdk/lib/enum/order_type_enum.dart @@ -0,0 +1,19 @@ +/// 订单类型枚举 +/// 对应文档中的 order_type 字段:coin_purchase(金币购买), vip_subscription(VIP订阅) +enum OrderTypeEnum { + /// 金币购买 + COIN_PURCHASE(label: 'coin_purchase'), + + /// VIP 订阅 + VIP_SUBSCRIPTION(label: 'vip_subscription'), + + ; + + /// 上报时使用的字符串值 + final String label; + + const OrderTypeEnum({ + required this.label, + }); +} + diff --git a/analytics_sdk/lib/enum/page_route_enum.dart b/analytics_sdk/lib/enum/page_route_enum.dart new file mode 100644 index 0000000..8a6c635 --- /dev/null +++ b/analytics_sdk/lib/enum/page_route_enum.dart @@ -0,0 +1,174 @@ +/// 预定义页面路由枚举,提供标准 [key](页面唯一标识)和 [name](展示名称)。 +/// +/// **用法示例** +/// +/// ```dart +/// // 上报导航事件 +/// AnalyticsSdk.instance.trackNavigation( +/// pageKey: AppPageEnum.video.key, +/// pageName: AppPageEnum.video.name, +/// ); +/// +/// // 配置底部/顶部 Tab +/// BottomNavigationBar(...).withAnalytics(tabs: [ +/// AnalyticsTab(AppPageEnum.home.key, AppPageEnum.home.name), +/// AnalyticsTab(AppPageEnum.video.key, AppPageEnum.video.name), +/// AnalyticsTab(AppPageEnum.mine.key, AppPageEnum.mine.name), +/// ]); +/// +/// // PageView 子页面同步 +/// AnalyticsSdk.instance.updateCurrentPage( +/// pageKey: AppPageEnum.videoList.key, +/// pageName: AppPageEnum.videoList.name, +/// ); +/// ``` +/// +/// **自定义页面**:直接传入字符串即可,无需扩展枚举: +/// ```dart +/// AnalyticsSdk.instance.trackNavigation( +/// pageKey: 'live_room', +/// pageName: '直播间', +/// ); +/// ``` +enum AppPageEnum { + // ── 公共类 ────────────────────────────────────────────────── + + /// 启动页 + launch('launch', '启动页'), + + /// 引导页(新用户首次打开时展示) + guide('guide', '引导页'), + + /// 首页(底部导航第一 Tab 常用值) + home('home', '首页'), + + /// 主页 + mainPage('main_page', '主页'), + + /// 发现页 + discover('discover', '发现页'), + + /// 搜索页(搜索入口页,含搜索框和热搜) + search('search', '搜索页'), + + /// 搜索结果页 + searchResult('search_result', '搜索结果页'), + + /// 应用中心 + appCenter('app_center', '应用中心'), + + /// 社区页 + community('community', '社区页'), + + /// 广场页 + square('square', '广场页'), + + /// 我的(底部导航"我的"Tab) + mine('mine', '我的'), + + /// 个人中心(登录用户的详情页) + userCenter('user_center', '个人中心'), + + /// 用户主页(查看他人主页) + userHome('user_home', '用户主页'), + + /// 作者主页 + authorHome('author_home', '作者主页'), + + /// 登录页 + login('login', '登录页'), + + /// 注册页 + register('register', '注册页'), + + /// 消息中心 + messageCenter('message_center', '消息中心'), + + /// 设置页 + setting('setting', '设置页'), + + /// 充值页 + recharge('recharge', '充值页'), + + /// 充值记录页 + rechargeRecord('recharge_record', '充值记录页'), + + /// 我的收藏 + mineCollect('mine_collect', '我的收藏'), + + /// 我的历史 + mineHistory('mine_history', '我的历史'), + + /// 会员中心页 + mineVip('mine_vip', '会员中心页'), + + // ── 视频类 ────────────────────────────────────────────────── + + /// 视频列表页 + videoList('video_list', '视频列表页'), + + /// 视频分类页 + videoCategory('video_category', '视频分类页'), + + /// 视频榜单页 + videoRank('video_rank', '视频榜单页'), + + /// 视频搜索页 + videoSearch('video_search', '视频搜索页'), + + /// 视频详情页 + videoDetail('video_detail', '视频详情页'), + + /// 视频播放页 + videoPlay('video_play', '视频播放页'), + + // ── 小说类 ────────────────────────────────────────────────── + + /// 小说列表页 + novelList('novel_list', '小说列表页'), + + /// 小说分类页 + novelCategory('novel_category', '小说分类页'), + + /// 小说榜单页 + novelRank('novel_rank', '小说榜单页'), + + /// 小说详情页 + novelDetail('novel_detail', '小说详情页'), + + /// 小说阅读页 + novelRead('novel_read', '小说阅读页'), + + /// 小说章节页 + novelChapterList('novel_chapter_list', '小说章节页'), + + // ── 漫画类 ────────────────────────────────────────────────── + + /// 漫画列表页 + comicList('comic_list', '漫画列表页'), + + /// 漫画分类页 + comicCategory('comic_category', '漫画分类页'), + + /// 漫画榜单页 + comicRank('comic_rank', '漫画榜单页'), + + /// 漫画详情页 + comicDetail('comic_detail', '漫画详情页'), + + /// 漫画阅读页 + comicRead('comic_read', '漫画阅读页'), + + /// 漫画章节页 + comicChapterList('comic_chapter_list', '漫画章节页'); + + // ───────────────────────────────────────────────────────────── + + const AppPageEnum(this.key, this.name); + + /// 页面唯一标识,用于 `page_key` 字段。 + final String key; + + /// 页面展示名称,用于 `page_name` 字段。 + final String name; +} diff --git a/analytics_sdk/lib/enum/pay_type_enum.dart b/analytics_sdk/lib/enum/pay_type_enum.dart new file mode 100644 index 0000000..6ca196f --- /dev/null +++ b/analytics_sdk/lib/enum/pay_type_enum.dart @@ -0,0 +1,25 @@ +/// 支付方式枚举 +/// 对应文档中的 pay_type 字段:wechat, alipay, bank_card, apple_pay 等 +enum PayTypeEnum { + /// 微信支付 + WECHAT(label: 'wechat'), + + /// 支付宝 + ALIPAY(label: 'alipay'), + + /// 银行卡 + BANK_CARD(label: 'bank_card'), + + /// Apple Pay + APPLE_PAY(label: 'apple_pay'), + + ; + + /// 上报时使用的字符串值 + final String label; + + const PayTypeEnum({ + required this.label, + }); +} + diff --git a/analytics_sdk/lib/enum/read_behavior_enum.dart b/analytics_sdk/lib/enum/read_behavior_enum.dart new file mode 100644 index 0000000..1d23926 --- /dev/null +++ b/analytics_sdk/lib/enum/read_behavior_enum.dart @@ -0,0 +1,35 @@ +/// 阅读行为类型,用于小说和漫画事件。 +/// +/// 可使用预定义常量,也可传入自定义字符串: +/// ```dart +/// ReadBehaviorEnum.view +/// ReadBehaviorEnum('custom_key', '自定义行为') +/// ``` +/// +/// 字段规则(debug 模式下触发 assert): +/// - [key]:仅限字母、数字、`_`、`-` +/// - [name]:字母、数字、`_`、`-`、中文 +class ReadBehaviorEnum { + final String key; + final String name; + + const ReadBehaviorEnum._internal(this.key, this.name); + + /// 自定义行为类型。[key] 仅限字母/数字/`_`/`-`;[name] 允许中文。 + factory ReadBehaviorEnum(String key, String name) { + assert( + RegExp(r'^[A-Za-z0-9_-]+$').hasMatch(key), + 'ReadBehaviorEnum.key 仅支持字母、数字、_ 和 -,实际传入: "$key"', + ); + assert( + RegExp(r'^[\u4e00-\u9fa5A-Za-z0-9_-]+$').hasMatch(name), + 'ReadBehaviorEnum.name 仅支持字母、数字、_、- 和中文,实际传入: "$name"', + ); + return ReadBehaviorEnum._internal(key, name); + } + + static const VIEW = ReadBehaviorEnum._internal('view', '展示'); + static const PAGE_NEXT = ReadBehaviorEnum._internal('page_next', '下一页'); + static const PAGE_PREV = ReadBehaviorEnum._internal('page_prev', '上一页'); + static const COMPLETE = ReadBehaviorEnum._internal('complete', '读完'); +} diff --git a/analytics_sdk/lib/enum/recommend_content_type_enum.dart b/analytics_sdk/lib/enum/recommend_content_type_enum.dart new file mode 100644 index 0000000..5be5605 --- /dev/null +++ b/analytics_sdk/lib/enum/recommend_content_type_enum.dart @@ -0,0 +1,28 @@ +/// 推荐列表内容类型,用于推荐列表展示/点击事件。 +/// +/// 可使用预定义常量,也可传入自定义字符串: +/// ```dart +/// RecommendContentTypeEnum.video +/// RecommendContentTypeEnum('podcast') +/// ``` +/// +/// 字段规则(debug 模式下触发 assert): +/// - [label]:仅限字母、数字、`_`、`-` +class RecommendContentTypeEnum { + final String label; + + const RecommendContentTypeEnum._internal(this.label); + + /// 自定义内容类型。[label] 仅限字母/数字/`_`/`-`。 + factory RecommendContentTypeEnum(String label) { + assert( + RegExp(r'^[A-Za-z0-9_-]+$').hasMatch(label), + 'RecommendContentTypeEnum.label 仅支持字母、数字、_ 和 -,实际传入: "$label"', + ); + return RecommendContentTypeEnum._internal(label); + } + + static const VIDEO = RecommendContentTypeEnum._internal('video'); + static const NOVEL = RecommendContentTypeEnum._internal('novel'); + static const COMIC = RecommendContentTypeEnum._internal('comic'); +} diff --git a/analytics_sdk/lib/enum/user_type_enum.dart b/analytics_sdk/lib/enum/user_type_enum.dart new file mode 100644 index 0000000..2f166fb --- /dev/null +++ b/analytics_sdk/lib/enum/user_type_enum.dart @@ -0,0 +1,28 @@ +/// 用户类型,用于上报用户身份信息。 +/// +/// 可使用预定义常量,也可传入自定义字符串: +/// ```dart +/// UserTypeEnum.normal +/// UserTypeEnum.vip +/// UserTypeEnum('svip') +/// ``` +/// +/// 字段规则(debug 模式下触发 assert): +/// - [label]:仅限字母、数字、`_`、`-` +class UserTypeEnum { + final String label; + + const UserTypeEnum._internal(this.label); + + /// 自定义用户类型。[label] 仅限字母/数字/`_`/`-`。 + factory UserTypeEnum(String label) { + assert( + RegExp(r'^[A-Za-z0-9_-]+$').hasMatch(label), + 'UserTypeEnum.label 仅支持字母、数字、_ 和 -,实际传入: "$label"', + ); + return UserTypeEnum._internal(label); + } + + static const normal = UserTypeEnum._internal('normal'); + static const vip = UserTypeEnum._internal('vip'); +} diff --git a/analytics_sdk/lib/enum/video_content_type_enum.dart b/analytics_sdk/lib/enum/video_content_type_enum.dart new file mode 100644 index 0000000..e439ec4 --- /dev/null +++ b/analytics_sdk/lib/enum/video_content_type_enum.dart @@ -0,0 +1,30 @@ +/// 视频内容类型,用于视频事件。 +/// +/// 可使用预定义常量,也可传入自定义字符串: +/// ```dart +/// VideoContentTypeEnum.video +/// VideoContentTypeEnum('live') +/// ``` +/// +/// 字段规则(debug 模式下触发 assert): +/// - [label]:仅限字母、数字、`_`、`-` +class VideoContentTypeEnum { + final String label; + + const VideoContentTypeEnum._internal(this.label); + + /// 自定义内容类型。[label] 仅限字母/数字/`_`/`-`。 + factory VideoContentTypeEnum(String label) { + assert( + RegExp(r'^[A-Za-z0-9_-]+$').hasMatch(label), + 'VideoContentTypeEnum.label 仅支持字母、数字、_ 和 -,实际传入: "$label"', + ); + return VideoContentTypeEnum._internal(label); + } + + /// 长视频 + static const video = VideoContentTypeEnum._internal('video'); + + /// 短视频 + static const shortVideo = VideoContentTypeEnum._internal('short_video'); +} diff --git a/analytics_sdk/lib/enum/video_event_enum.dart b/analytics_sdk/lib/enum/video_event_enum.dart new file mode 100644 index 0000000..62363a4 --- /dev/null +++ b/analytics_sdk/lib/enum/video_event_enum.dart @@ -0,0 +1,38 @@ +/// 视频行为类型,用于视频事件。 +/// +/// 可使用预定义常量,也可传入自定义字符串: +/// ```dart +/// VideoEventEnum.play +/// VideoEventEnum('custom_key', '自定义行为') +/// ``` +/// +/// 字段规则(debug 模式下触发 assert): +/// - [key]:仅限字母、数字、`_`、`-` +/// - [name]:字母、数字、`_`、`-`、中文 +class VideoEventEnum { + final String key; + final String name; + + const VideoEventEnum._internal(this.key, this.name); + + /// 自定义行为类型。[key] 仅限字母/数字/`_`/`-`;[name] 允许中文。 + factory VideoEventEnum(String key, String name) { + assert( + RegExp(r'^[A-Za-z0-9_-]+$').hasMatch(key), + 'VideoEventEnum.key 仅支持字母、数字、_ 和 -,实际传入: "$key"', + ); + assert( + RegExp(r'^[\u4e00-\u9fa5A-Za-z0-9_-]+$').hasMatch(name), + 'VideoEventEnum.name 仅支持字母、数字、_、- 和中文,实际传入: "$name"', + ); + return VideoEventEnum._internal(key, name); + } + + static const VIDEO_VIEW = VideoEventEnum._internal('video_view', '视频展示'); + static const VIDEO_PLAY = VideoEventEnum._internal('video_play', '视频播放'); + static const VIDEO_PAUSE = VideoEventEnum._internal('video_pause', '暂停'); + static const VIDEO_SHARE = VideoEventEnum._internal('video_share', '分享'); + static const VIDEO_COMPLETE = VideoEventEnum._internal('video_complete', '播放完成'); + static const VIDEO_FORWARD = VideoEventEnum._internal('video_forward', '快进'); + static const VIDEO_REWIND = VideoEventEnum._internal('video_rewind', '快退'); +} diff --git a/analytics_sdk/lib/extension/tab_analytics_extension.dart b/analytics_sdk/lib/extension/tab_analytics_extension.dart new file mode 100644 index 0000000..4d74ab9 --- /dev/null +++ b/analytics_sdk/lib/extension/tab_analytics_extension.dart @@ -0,0 +1,51 @@ +// lib/extension/tab_analytics_extension.dart +import 'package:analytics_sdk/entity/analytics_tab.dart'; +import 'package:analytics_sdk/widget/analytics_bottom_nav_wrapper.dart'; +import 'package:analytics_sdk/widget/analytics_tab_bar_wrapper.dart'; +import 'package:flutter/material.dart'; + +/// 为 [BottomNavigationBar] 提供 analytics 自动埋点能力。 +/// +/// 示例: +/// ```dart +/// BottomNavigationBar( +/// currentIndex: _index, +/// onTap: (i) => setState(() => _index = i), +/// items: const [...], +/// ).withAnalytics(tabs: [ +/// AnalyticsTab('home', '首页'), +/// AnalyticsTab('discover'), // pageName 走 PageNameMapper 自动解析 +/// ]) +/// ``` +extension AnalyticsBottomNavExtension on BottomNavigationBar { + /// 切换 Tab 时自动上报 AppPageViewEvent + NavigationEvent, + /// 并更新 PageLifecycleObserver.currentPageKey。 + Widget withAnalytics({required List tabs}) { + return AnalyticsBottomNavWrapper( + tabs: tabs, + child: this, + ); + } +} + +/// 为 [TabBar] 提供 analytics 轻导航埋点能力。 +/// +/// 示例: +/// ```dart +/// TabBar( +/// controller: _tabController, +/// tabs: const [Tab(text: '视频'), Tab(text: '小说')], +/// ).withAnalytics(tabs: [ +/// AnalyticsTab('video', '视频'), +/// AnalyticsTab('novel'), +/// ]) +/// ``` +extension AnalyticsTabBarExtension on TabBar { + /// 切换 Tab 时自动上报 NavigationEvent(轻导航,不更新 currentPageKey)。 + AnalyticsTabBarWrapper withAnalytics({required List tabs}) { + return AnalyticsTabBarWrapper( + tabs: tabs, + child: this, + ); + } +} diff --git a/analytics_sdk/lib/manager/ad_impression_manager.dart b/analytics_sdk/lib/manager/ad_impression_manager.dart new file mode 100644 index 0000000..8bd6a82 --- /dev/null +++ b/analytics_sdk/lib/manager/ad_impression_manager.dart @@ -0,0 +1,105 @@ +import 'package:analytics_sdk/config/sdk_config.dart'; +import 'package:analytics_sdk/utils/logger.dart'; + +/// 广告展示去重管理器 +/// +/// 以页面为粒度做去重:同一页面访问内不重复上报同一广告, +/// 页面退出后去重状态随之清除,下次进入同一页面广告可重新上报。 +/// 新 session 时调用 [clear] 清除所有页面记录。 +class AdImpressionManager { + static final AdImpressionManager instance = AdImpressionManager._internal(); + + factory AdImpressionManager() => instance; + + AdImpressionManager._internal(); + + /// 页面级广告去重表:pageKey → 已上报 adId 集合 + final Map> _reportedAdIdsByPage = {}; + + /// 广告 SDK 曝光已上报的 trace_id;同一 session 内全局去重。 + final Set _reportedSdkTraceIds = {}; + + /// 当前已存储的广告ID总数(跨所有页面) + int get reportedCount => + _reportedAdIdsByPage.values.fold(0, (sum, s) => sum + s.length); + + /// 批量检查多个广告ID,返回在指定页面内尚未上报的列表(不修改状态) + List getUnreportedAdIds(String adIds, {required String pageKey}) { + if (adIds.isEmpty) return []; + + final adIdList = adIds + .split(',') + .map((id) => id.trim()) + .where((id) => id.isNotEmpty) + .toList(); + + if (adIdList.isEmpty) return []; + + final pageSet = _reportedAdIdsByPage[pageKey]; + if (pageSet == null || pageSet.isEmpty) return adIdList; + + return adIdList.where((id) => !pageSet.contains(id)).toList(); + } + + /// 在指定页面内批量标记广告ID为已上报 + void markAsReportedBatch(String adIds, {required String pageKey}) { + if (adIds.isEmpty) return; + + final adIdList = + adIds.split(',').map((id) => id.trim()).where((id) => id.isNotEmpty); + + final pageSet = _reportedAdIdsByPage.putIfAbsent(pageKey, () => {}); + for (final id in adIdList) { + if (reportedCount >= SdkConfig.maxAdImpressionCapacity) { + Logger.adImpressionManager( + '广告去重表已达上限(${SdkConfig.maxAdImpressionCapacity}),停止写入,跳过 id: $id', + level: LogLevel.warn); + break; + } + pageSet.add(id); + } + } + + /// 指定 trace_id 是否已作为广告 SDK 曝光上报。 + bool isSdkTraceReported(String traceId) { + return _reportedSdkTraceIds.contains(traceId.trim()); + } + + /// 在广告 SDK 曝光成功入队后标记其 trace_id。 + void markSdkTraceReported(String traceId) { + final normalized = traceId.trim(); + if (normalized.isEmpty || isSdkTraceReported(normalized)) return; + if (_reportedSdkTraceIds.length >= SdkConfig.maxAdImpressionCapacity) { + Logger.adImpressionManager( + '广告 SDK 曝光 trace_id 去重表已达上限(${SdkConfig.maxAdImpressionCapacity}),停止写入: $normalized', + level: LogLevel.warn, + ); + return; + } + _reportedSdkTraceIds.add(normalized); + } + + /// 清除指定页面的广告去重记录(页面退出时调用,下次进入可重新上报) + void clearPage(String pageKey) { + if (_reportedAdIdsByPage.remove(pageKey) != null) { + Logger.adImpressionManager('页面 "$pageKey" 广告去重记录已清除'); + } + } + + /// 清除所有页面的广告去重记录(新 session 开始时调用) + void clear() { + _reportedAdIdsByPage.clear(); + _reportedSdkTraceIds.clear(); + } + + // ── 以下方法供测试使用 ────────────────────────────── + + /// 检查广告ID在指定页面内是否已上报(不修改状态) + bool isReported(String adId, {required String pageKey}) { + return _reportedAdIdsByPage[pageKey]?.contains(adId.trim()) ?? false; + } + + /// [isReported] 的别名,用于断言 + bool contains(String adId, {required String pageKey}) => + isReported(adId, pageKey: pageKey); +} diff --git a/analytics_sdk/lib/manager/domain_manager.dart b/analytics_sdk/lib/manager/domain_manager.dart new file mode 100644 index 0000000..a52d8ca --- /dev/null +++ b/analytics_sdk/lib/manager/domain_manager.dart @@ -0,0 +1,204 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:analytics_sdk/config/sdk_config.dart'; +import 'package:analytics_sdk/utils/logger.dart'; +import 'package:analytics_sdk/utils/platform_storage.dart'; +import 'package:http/http.dart' as http; + +/// 域名管理器:对业务方传入的域名列表进行并发测速,选择最快的上报域名 +class DomainManager { + static final DomainManager instance = DomainManager._internal(); + + factory DomainManager() => instance; + + DomainManager._internal(); + + List _reportDomains = []; + String? _fastestDomain; + + /// 每次 initWithDomains / reset 时递增,用于丢弃过期测速结果 + int _generation = 0; + + http.Client? _httpClient; + http.Client get _client => _httpClient ??= http.Client(); + + /// 域名更新回调,当最快域名选出后调用 + Function(String? fastestDomain)? onFastestDomainChanged; + + /// 获取最快的上报域名 + String? get fastestDomain => _fastestDomain; + + /// 获取所有上报域名 + List get reportDomains => List.unmodifiable(_reportDomains); + + /// 传入已解密的域名列表,执行并发测速并选出最快域名 + /// + /// [domains] 每项为带协议的完整域名,如 "https://api.example.com" + /// + /// 注意:此方法设计为 fire-and-forget,调用方无需 await, + /// 测速完成后通过 [onFastestDomainChanged] 回调通知结果。 + Future initWithDomains(List domains) async { + // 记录本次调用的代次,测速完成后用于判断结果是否已过期 + final gen = ++_generation; + try { + if (domains.isEmpty) { + Logger.domainManager('域名列表为空,跳过测速', level: LogLevel.warn); + return null; + } + + _reportDomains = domains; + Logger.domainManager('开始域名测速,共 ${domains.length} 个: $domains'); + + String? fastest; + if (domains.length == 1) { + fastest = domains.first; + Logger.domainManager('只有一个域名,跳过测速: $fastest'); + } else { + fastest = await _speedTestAndSelect(domains); + Logger.domainManager('测速完成,最快域名: $fastest'); + } + + // 若 reset() 或新一轮 initWithDomains 已被调用,丢弃本次过期结果 + if (gen != _generation) { + Logger.domainManager('测速结果已过期(generation 不匹配),忽略本次结果'); + return null; + } + + _fastestDomain = fastest; + // 测速完成后持久化域名列表(供下次 init 时解密失败时作为降级) + _saveDomainCache(); + if (onFastestDomainChanged != null && _fastestDomain != null) { + try { + onFastestDomainChanged!(_fastestDomain); + } catch (e) { + Logger.domainManager('域名更新回调异常: $e'); + } + } + return _fastestDomain; + } catch (e) { + Logger.domainManager('域名测速失败: $e', level: LogLevel.warn); + return null; + } + } + + /// 重置域名管理器状态,供 re-init 时使用 + /// + /// 清除内存中的域名列表和最快域名,不关闭 HttpClient(复用连接池)。 + /// 不删除磁盘缓存(缓存代表最近已知可用状态,跨 init 保留)。 + void reset() { + _generation++; // 使所有进行中的测速结果失效 + _fastestDomain = null; + _reportDomains = []; + onFastestDomainChanged = null; // 防止旧回调在新一轮测速前残留 + Logger.domainManager('域名管理器状态已重置'); + } + + /// 释放 HttpClient 资源,通常在 SDK dispose 时调用 + void dispose() { + _httpClient?.close(); + _httpClient = null; + Logger.domainManager('域名管理器已销毁'); + } + + /// 从磁盘加载上次缓存的域名列表 + /// + /// 返回非空列表时可直接传入 [initWithDomains]。 + /// 无缓存文件或读取失败时返回空列表(不抛出异常)。 + Future> loadCachedDomains() async { + try { + final content = + await PlatformStorage.read(SdkConfig.domainCacheFileName); + if (content == null) return []; + final decoded = jsonDecode(content); + if (decoded is List) { + return (decoded as List) + .map((item) => item.toString().trim()) + .where((d) => d.isNotEmpty) + .toList(); + } + return []; + } catch (e) { + Logger.domainManager('加载域名缓存失败: $e'); + return []; + } + } + + // ==================== 私有方法 ==================== + + /// 将当前 _reportDomains 写入磁盘缓存(fire-and-forget) + void _saveDomainCache() { + if (_reportDomains.isEmpty) return; + final domainsSnapshot = List.from(_reportDomains); + Future(() async { + try { + await PlatformStorage.write( + SdkConfig.domainCacheFileName, + jsonEncode(domainsSnapshot), + ); + Logger.domainManager('域名缓存已写入: $domainsSnapshot'); + } catch (e) { + Logger.domainManager('写入域名缓存失败: $e'); + } + }); + } + + /// 对所有域名进行并发测速,返回最快的域名 + Future _speedTestAndSelect(List domains) async { + if (domains.isEmpty) return null; + + final speedResults = {}; + + final futures = domains.map((domain) async { + try { + final speed = await _speedTest(domain); + if (speed != null) { + speedResults[domain] = speed; + Logger.domainManager('域名 $domain 测速: ${speed.inMilliseconds}ms'); + } + } catch (e) { + Logger.domainManager('域名 $domain 测速失败: $e'); + } + }); + + await Future.wait(futures); + + if (speedResults.isEmpty) { + Logger.domainManager('所有域名测速失败,回退到使用第一个域名作为上报域名', + level: LogLevel.warn); + return domains.first; + } + + final fastest = + speedResults.entries.reduce((a, b) => a.value < b.value ? a : b); + + Logger.domainManager( + '最快域名: ${fastest.key} (${fastest.value.inMilliseconds}ms)'); + return fastest.key; + } + + /// 测速单个域名,返回连接耗时;连接超时或网络异常返回 null + /// + /// 仅 HTTP 200 视为可达。 + /// 非 200(3xx 重定向、4xx 客户端错误、5xx 服务端错误)/ 连接失败 / 超时 视为不可达,返回 null。 + Future _speedTest(String domain) async { + try { + final uri = Uri.parse(domain); + final startTime = DateTime.now(); + final response = + await _client.get(uri).timeout(SdkConfig.speedTestTimeout); + final elapsed = DateTime.now().difference(startTime); + if (response.statusCode != 200) { + Logger.domainManager( + '域名 $domain 返回 ${response.statusCode},视为不可达', + level: LogLevel.warn); + return null; + } + Logger.domainManager( + '域名 $domain 可达,状态码: ${response.statusCode}'); + return elapsed; + } catch (e) { + Logger.domainManager('域名 $domain 测速失败: $e', level: LogLevel.warn); + return null; + } + } +} diff --git a/analytics_sdk/lib/manager/event_persistence.dart b/analytics_sdk/lib/manager/event_persistence.dart new file mode 100644 index 0000000..b027b0f --- /dev/null +++ b/analytics_sdk/lib/manager/event_persistence.dart @@ -0,0 +1,7 @@ +export 'event_persistence_native.dart' + // ignore: uri_does_not_exist + if (dart.library.html) 'event_persistence_web.dart'; + +// EventPersistenceImpl 由条件导入文件提供 +// Native:lib/manager/event_persistence_native.dart +// Web :lib/manager/event_persistence_web.dart diff --git a/analytics_sdk/lib/manager/event_persistence_native.dart b/analytics_sdk/lib/manager/event_persistence_native.dart new file mode 100644 index 0000000..cc43170 --- /dev/null +++ b/analytics_sdk/lib/manager/event_persistence_native.dart @@ -0,0 +1,398 @@ +import 'dart:async'; +import 'dart:collection'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:analytics_sdk/config/sdk_config.dart'; +import 'package:analytics_sdk/utils/logger.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:synchronized/synchronized.dart'; + +/// Native 平台事件持久化:基于本地文件 + tombstone 机制。 +class EventPersistenceImpl { + String? _cacheFilePath; + String? _debugReportEventLogFilePath; + String? _tombstoneFilePath; + String? _inFlightFilePath; + final Lock _cacheLock = Lock(); + final List> _cacheBuffer = []; + static int get _cacheBufferThreshold => SdkConfig.cacheBufferThreshold; + static Duration get _cacheFlushInterval => SdkConfig.cacheFlushInterval; + Timer? _cacheFlushTimer; + + Future init() async { + try { + final dir = await getApplicationDocumentsDirectory(); + _cacheFilePath = '${dir.path}/${SdkConfig.cacheFileName}'; + _debugReportEventLogFilePath = + '${dir.path}/${SdkConfig.debugReportEventLogFileName}'; + _tombstoneFilePath = '${dir.path}/${SdkConfig.tombstoneFileName}'; + _inFlightFilePath = '${dir.path}/data_plus_events_inflight.txt'; + } catch (e) { + Logger.analyticsSdk('持久化初始化失败: $e'); + _cacheFilePath = null; + _debugReportEventLogFilePath = null; + _tombstoneFilePath = null; + } + } + + String? get debugReportEventLogFilePath => + SdkConfig.enableDebugReportLog ? _debugReportEventLogFilePath : null; + + void appendDebugReportEvent(Map event) { + if (!SdkConfig.enableDebugReportLog || + _debugReportEventLogFilePath == null) { + return; + } + try { + File(_debugReportEventLogFilePath!).writeAsStringSync( + '${jsonEncode(event)}\n', + mode: FileMode.append, + flush: true, + ); + } catch (e) { + Logger.analyticsSdk('debug 完整事件 JSONL 写入失败: $e', level: LogLevel.warn); + } + } + + void bufferEvent(Map event) { + if (_cacheFilePath == null) return; + _cacheBuffer.add(event); + if (_cacheBuffer.length >= _cacheBufferThreshold) { + flushBuffer(); + } else { + _cacheFlushTimer ??= Timer(_cacheFlushInterval, () { + _cacheFlushTimer = null; + flushBuffer(); + }); + } + } + + /// 同步写入关键事件,供应用终止阶段的会话汇总使用。 + /// + /// 此时 Dart isolate 可能在下一个 await 前被系统终止,不能依赖延迟 + /// buffer 或异步文件写入。事件已包含 event_id,后续仍通过 tombstone + /// 和服务端 event_id 去重保证幂等。 + void persistCriticalEvent(Map event) { + if (_cacheFilePath == null) return; + try { + final line = jsonEncode(event); + File(_cacheFilePath!).writeAsStringSync( + '$line\n', + mode: FileMode.append, + flush: true, + ); + } catch (e) { + Logger.analyticsSdk('关键事件同步缓存写入失败: $e', level: LogLevel.warn); + } + } + + Future>> loadEvents({ + required Queue> queue, + required int maxQueueSize, + required int maxCacheLines, + required bool Function(String) isEventTypeEnabled, + }) async { + if (_cacheFilePath == null) return const []; + final file = File(_cacheFilePath!); + if (!await file.exists()) return const []; + + final recovered = >[]; + try { + await _cacheLock.synchronized(() async { + if (await file.length() > SdkConfig.maxCacheBytes) { + await _trimCacheFileIfNeeded(file); + } + + final tombstonedIds = await _loadTombstoneIds(); + final inFlightIds = await _loadInFlightIds(); + // 请求开始不代表服务端已经确认。进程在请求途中被终止时,in-flight + // 事件必须随缓存一起恢复,交由服务端按 event_id 去重,不能在客户端 + // 为了避免重复而丢失会话汇总等终止事件。 + if (inFlightIds.isNotEmpty) { + Logger.analyticsSdk( + '检测到 ${inFlightIds.length} 条未确认 in-flight 事件,将恢复重试'); + } + final lines = await file.readAsLines(); + final linesToProcess = lines.length > maxCacheLines + ? lines.sublist(lines.length - maxCacheLines) + : lines; + + for (final line in linesToProcess) { + if (line.trim().isEmpty) continue; + try { + final decoded = jsonDecode(line); + if (decoded is Map) { + final eventId = decoded['event_id']; + if (eventId != null && tombstonedIds.contains(eventId)) { + continue; + } + final eventType = decoded['event'] as String?; + if (eventType != null && !isEventTypeEnabled(eventType)) { + Logger.analyticsSdk('缓存恢复:事件类型 $eventType 未启用,跳过'); + continue; + } + recovered.add(decoded); + } + } catch (_) { + continue; + } + } + + // 缓存和 tombstone 都不能在“恢复到内存队列”时删除。恢复后的 + // 事件尚未得到本次请求的 200 确认,进程再次终止时仍需能从磁盘 + // 重试;只有 removeEvents() 在 200 后写入 tombstone。 + if (_inFlightFilePath != null) { + final inFlight = File(_inFlightFilePath!); + if (await inFlight.exists()) await inFlight.delete(); + } + }); + } catch (e) { + Logger.analyticsSdk('缓存加载失败: $e'); + try { + await file.delete(); + } catch (_) {} + } + return recovered; + } + + Future removeEvents(List> reportedEvents) async { + if (_tombstoneFilePath == null || reportedEvents.isEmpty) return; + + final ids = reportedEvents + .map((e) => e['event_id']) + .whereType() + .where((id) => id.isNotEmpty && !id.contains('\n')) + .toList(); + + if (ids.isEmpty) return; + + try { + await _cacheLock.synchronized(() async { + try { + final tombstone = File(_tombstoneFilePath!); + await tombstone.writeAsString( + '${ids.join('\n')}\n', + mode: FileMode.append, + ); + Logger.analyticsSdk('tombstone 追加 ${ids.length} 条 event_id'); + + final tombstoneContent = await tombstone.readAsString(); + final tombstoneCount = + tombstoneContent.split('\n').where((l) => l.isNotEmpty).length; + if (tombstoneCount >= SdkConfig.tombstoneCompactionThreshold) { + await _compactCache(tombstoneContent); + } + } catch (e) { + Logger.analyticsSdk('tombstone 写入失败: $e'); + } + }); + } catch (e) { + Logger.analyticsSdk('缓存移除操作异常: $e'); + } + } + + Future clearCache() async { + if (_cacheFilePath == null) return; + try { + await _cacheLock.synchronized(() async { + try { + final file = File(_cacheFilePath!); + if (await file.exists()) await file.delete(); + if (_tombstoneFilePath != null) { + final tombstone = File(_tombstoneFilePath!); + if (await tombstone.exists()) await tombstone.delete(); + } + } catch (e) { + Logger.analyticsSdk('缓存清除失败: $e'); + } + }); + } catch (e) { + Logger.analyticsSdk('缓存清除操作异常: $e'); + } + } + + void flushBuffer() { + if (_cacheFilePath == null || _cacheBuffer.isEmpty) return; + _cacheFlushTimer?.cancel(); + _cacheFlushTimer = null; + final batch = List>.from(_cacheBuffer); + _cacheBuffer.clear(); + Future.microtask(() async { + await _saveBatchToCache(batch); + }); + } + + Future flushBufferAsync() async { + if (_cacheFilePath == null || _cacheBuffer.isEmpty) return; + _cacheFlushTimer?.cancel(); + _cacheFlushTimer = null; + final batch = List>.from(_cacheBuffer); + _cacheBuffer.clear(); + await _saveBatchToCache(batch); + } + + /// 发送前记录 batch 中的 event_id,便于诊断未确认请求。 + /// + /// 崩溃重启后这些事件会与缓存一起恢复并重试,采用 at-least-once + /// 语义;服务端应按 event_id 去重。 + Future markInFlight(List> batch) async { + if (_inFlightFilePath == null) return; + final ids = batch + .map((e) => e['event_id']) + .whereType() + .where((id) => id.isNotEmpty && !id.contains('\n')) + .toList(); + if (ids.isEmpty) return; + try { + await File(_inFlightFilePath!).writeAsString('${ids.join('\n')}\n'); + } catch (e) { + Logger.analyticsSdk('in-flight 标记写入失败: $e'); + } + } + + /// 清除 in-flight 文件(上报完成或明确失败后调用,幂等)。 + Future clearInFlight() async { + if (_inFlightFilePath == null) return; + try { + final file = File(_inFlightFilePath!); + if (await file.exists()) await file.delete(); + } catch (e) { + Logger.analyticsSdk('in-flight 文件清除失败: $e'); + } + } + + // ==================== 私有方法 ==================== + + Future _trimCacheFileIfNeeded(File file) async { + try { + final len = await file.length(); + if (len <= SdkConfig.maxCacheBytes) return; + final lines = await file.readAsLines(); + int total = 0; + int startIndex = 0; + for (int i = lines.length - 1; i >= 0; i--) { + total += utf8.encode(lines[i]).length + 1; + if (total > SdkConfig.maxCacheBytes) { + startIndex = i + 1; + break; + } + } + if (startIndex <= 0) return; + final kept = lines.sublist(startIndex); + await file.writeAsString(kept.isEmpty ? '' : '${kept.join('\n')}\n'); + Logger.analyticsSdk( + '缓存文件超过 ${SdkConfig.maxCacheBytes} 字节,已裁剪掉前 $startIndex 条'); + } catch (e) { + Logger.analyticsSdk('缓存文件裁剪失败: $e'); + } + } + + Future _saveBatchToCache(List> batch) async { + if (_cacheFilePath == null || batch.isEmpty) return; + try { + await _cacheLock.synchronized(() async { + try { + final file = File(_cacheFilePath!); + final lines = batch + .map((item) { + try { + return jsonEncode(item); + } catch (e) { + Logger.analyticsSdk('单个事件编码失败,跳过: $e'); + return null; + } + }) + .where((line) => line != null) + .join('\n'); + if (lines.isNotEmpty) { + await file.writeAsString('$lines\n', mode: FileMode.append); + await _trimCacheFileIfNeeded(file); + } + } catch (e) { + Logger.analyticsSdk('批量缓存写入失败: $e'); + } + }); + } catch (e) { + Logger.analyticsSdk('缓存保存异常: $e'); + } + } + + Future> _loadTombstoneIds() async { + if (_tombstoneFilePath == null) return {}; + try { + final tombstone = File(_tombstoneFilePath!); + if (!await tombstone.exists()) return {}; + final content = await tombstone.readAsString(); + return content.split('\n').where((l) => l.isNotEmpty).toSet(); + } catch (e) { + Logger.analyticsSdk('tombstone 读取失败: $e'); + return {}; + } + } + + Future> _loadInFlightIds() async { + if (_inFlightFilePath == null) return {}; + try { + final file = File(_inFlightFilePath!); + if (!await file.exists()) return {}; + final content = await file.readAsString(); + return content.split('\n').where((l) => l.isNotEmpty).toSet(); + } catch (e) { + Logger.analyticsSdk('in-flight 文件读取失败: $e'); + return {}; + } + } + + Future _compactCache(String tombstoneContent) async { + if (_cacheFilePath == null || _tombstoneFilePath == null) return; + try { + final tombstonedIds = + tombstoneContent.split('\n').where((l) => l.isNotEmpty).toSet(); + + final cacheFile = File(_cacheFilePath!); + if (!await cacheFile.exists()) { + await File(_tombstoneFilePath!).delete(); + return; + } + + final lines = await cacheFile.readAsLines(); + final kept = []; + int removed = 0; + + for (final line in lines) { + if (line.trim().isEmpty) continue; + try { + final decoded = jsonDecode(line); + if (decoded is Map) { + final id = decoded['event_id']; + if (id != null && tombstonedIds.contains(id)) { + removed++; + } else { + kept.add(line); + } + } else { + kept.add(line); + } + } catch (_) { + kept.add(line); + } + } + + if (kept.isEmpty) { + await cacheFile.delete(); + } else { + await cacheFile.writeAsString('${kept.join('\n')}\n'); + } + try { + await File(_tombstoneFilePath!).delete(); + } catch (e) { + Logger.analyticsSdk('tombstone 删除失败(可忽略,下次压缩会重试): $e'); + } + + Logger.analyticsSdk('缓存压缩完成:删除 $removed 条,保留 ${kept.length} 条'); + } catch (e) { + Logger.analyticsSdk('缓存压缩失败: $e'); + } + } +} diff --git a/analytics_sdk/lib/manager/event_persistence_web.dart b/analytics_sdk/lib/manager/event_persistence_web.dart new file mode 100644 index 0000000..e9c9d8c --- /dev/null +++ b/analytics_sdk/lib/manager/event_persistence_web.dart @@ -0,0 +1,35 @@ +import 'dart:collection'; + +/// Web 平台事件持久化:内存 only,所有持久化操作为空实现。 +/// Web 会话通常短暂,不需要跨页面持久化事件。 +class EventPersistenceImpl { + Future init() async {} + + String? get debugReportEventLogFilePath => null; + + void appendDebugReportEvent(Map event) {} + + void bufferEvent(Map event) {} + + void persistCriticalEvent(Map event) {} + + Future>> loadEvents({ + required Queue> queue, + required int maxQueueSize, + required int maxCacheLines, + required bool Function(String) isEventTypeEnabled, + }) async => + const []; + + Future removeEvents(List> events) async {} + + Future markInFlight(List> events) async {} + + Future clearInFlight() async {} + + Future clearCache() async {} + + void flushBuffer() {} + + Future flushBufferAsync() async {} +} diff --git a/analytics_sdk/lib/manager/event_processor.dart b/analytics_sdk/lib/manager/event_processor.dart new file mode 100644 index 0000000..d6ff614 --- /dev/null +++ b/analytics_sdk/lib/manager/event_processor.dart @@ -0,0 +1,552 @@ +import 'dart:async'; +import 'dart:collection'; +import 'dart:convert'; +import 'package:analytics_sdk/manager/event_persistence.dart'; +import 'package:http/http.dart' as http; +import 'dart:math'; + +import 'package:analytics_sdk/config/sdk_config.dart'; +import 'package:analytics_sdk/manager/event_type_config_manager.dart'; +import 'package:analytics_sdk/manager/session_behavior_manager.dart'; +import 'package:analytics_sdk/manager/session_manager.dart'; +import 'package:analytics_sdk/manager/session_metrics_manager.dart'; +import 'package:analytics_sdk/manager/session_sensor_sampler.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; +import 'package:analytics_sdk/utils/logger.dart'; +import 'package:flutter/foundation.dart'; +import 'package:synchronized/synchronized.dart'; + +/// 事件处理与上报管理器 +/// +/// 负责: +/// - 维护事件队列 +/// - 本地持久化(缓存文件) +/// - 批量上报 & 指数退避重试 +/// - 定时上报定时器 +/// +/// 对外由 `AnalyticsSdk` 持有并调用,不直接暴露给业务方。 +class EventProcessor { + String? _reportUrl; + final Queue> _eventQueue = Queue>(); + + Timer? _uploadTimer; + + final EventPersistenceImpl _persistence = EventPersistenceImpl(); + + int _backoffLevel = 0; + Timer? _retryTimer; + + /// JSON 过大时折半缩小批次;成功后重置为 null(恢复用 maxBatchSize) + int? _overrideBatchSize; + + http.Client? _httpClient; + + http.Client get _client => _httpClient ??= http.Client(); + + /// 上报锁,防止并发上报 + bool _isUploading = false; + final Lock _uploadLock = Lock(); + + /// event_id 防抖:记录最近见到该 id 的时间,窗口内重复则丢弃 + final Map _recentEventIds = {}; + + /// 防抖窗口:1000ms 秒级时间戳粒度 + 100ms 时钟抖动容差。 + /// event_id 的哈希因子包含秒级 client_ts,同秒内同事件会产生同一 id; + /// 额外 100ms 用于覆盖调用链路上的微小时间偏移,避免临界点误判。 + static const _dedupWindow = Duration(milliseconds: 1100); + + /// 测试包用:最近一次上报结果 + bool? lastUploadSuccess; + String? lastUploadError; + DateTime? lastUploadTime; + + /// 是否已配置可用的上报 URL + bool get hasValidReportUrl => + _reportUrl != null && _reportUrl!.isNotEmpty == true; + + /// 测试包用:队列中待上报事件数 + int get queueLength => _eventQueue.length; + + /// 测试包用:当前上报地址(脱敏) + String? get debugReportUrl => _reportUrl; + + /// 测试包用:完整事件 JSONL 调试文件路径(仅 Native debug 包) + String? get debugReportEventLogPath => + kDebugMode && SdkConfig.enableDebugReportLog + ? _persistence.debugReportEventLogFilePath + : null; + + /// 更新上报 URL(域名管理器回调时调用) + void updateReportUrl(String? reportUrl) { + _reportUrl = reportUrl; + } + + /// 启动定时上报定时器 + void startAutoUploadTimer() { + _uploadTimer?.cancel(); + _uploadTimer = Timer.periodic(SdkConfig.uploadInterval, (_) { + try { + uploadBatch().catchError((e) { + Logger.analyticsSdk('定时上报 Future 异常: $e'); + }); + } catch (e) { + Logger.analyticsSdk('定时上报异常: $e'); + } + }); + } + + /// 停止定时上报定时器 + void stopAutoUploadTimer() { + _uploadTimer?.cancel(); + _uploadTimer = null; + } + + /// 重置退避和上报状态(SDK 重新初始化时调用) + void resetState() { + _backoffLevel = 0; + _overrideBatchSize = null; + _retryTimer?.cancel(); + _retryTimer = null; + lastUploadSuccess = null; + lastUploadError = null; + lastUploadTime = null; + _isUploading = false; + _recentEventIds.clear(); + } + + /// 事件入队: + /// - 负责队列容量检查 + /// - 触发必要的上报 + /// - 写入本地缓存 + /// + /// 返回值: + /// - `true`:事件成功进入队列 + /// - `false`:因队列已满等原因被丢弃 + bool enqueueEvent(Map json) { + // 队列容量检查,防止内存无限增长 + if (_eventQueue.length >= SdkConfig.maxQueueSize) { + Logger.analyticsSdk('警告:事件队列已满(${_eventQueue.length}),尝试立即上报'); + // 尝试立即上报以释放队列空间(不 await) + try { + uploadBatch().catchError((e) { + Logger.analyticsSdk('立即上报 Future 异常: $e'); + }); + } catch (e) { + Logger.analyticsSdk('立即上报异常: $e'); + } + // 队列已满时直接丢弃新事件 + return false; + } + + // event_id 防抖:1100ms 窗口内同一 id 只保留第一条 + final eventId = json['event_id'] as String?; + if (eventId != null) { + final now = DateTime.now(); + _recentEventIds.removeWhere((_, t) => now.difference(t) > _dedupWindow); + final lastSeen = _recentEventIds[eventId]; + if (lastSeen != null && now.difference(lastSeen) <= _dedupWindow) { + Logger.analyticsSdk('event_id 防抖,已丢弃: $eventId'); + return false; + } + _recentEventIds[eventId] = now; + } + + _eventQueue.add(json); + SessionManager.instance.recordActivityForEvent(json); + SessionMetricsManager.instance.recordAcceptedEvent(json); + SessionBehaviorManager.instance.recordAcceptedEvent(json); + SessionSensorSampler.instance.recordAcceptedEvent(json); + if (json['event'] == 'app_session_summary') { + _persistence.persistCriticalEvent(json); + Logger.analyticsSdk( + 'app_session_summary 已同步写入本地缓存: ${json['event_id'] ?? ''}'); + } else { + _persistence.bufferEvent(json); + } + _writeDebugReportEvent(json); + _logAcceptedEvent(json); + _flushIfNeeded(); + return true; + } + + /// 当队列长度达到阈值时触发上报 + void _flushIfNeeded() { + if (_eventQueue.length >= SdkConfig.autoUploadThreshold) { + uploadBatch().catchError((e) { + Logger.analyticsSdk('阈值触发上报 Future 异常: $e'); + }); + } + } + + /// 强制立即上报 + /// 该方法永远不会抛出异常 + Future flush() async { + try { + await uploadBatch(); + } catch (e) { + Logger.analyticsSdk('flush() 发生异常,已安全处理: $e'); + } + } + + /// 批量上报队列中的事件 + Future uploadBatch() async { + if (_eventQueue.isEmpty || _reportUrl == null) return; + + return _uploadLock.synchronized(() async { + if (_eventQueue.isEmpty || _reportUrl == null || _isUploading) return; + + _isUploading = true; + // 限制单次上报的批次大小,避免请求过大导致超时或内存问题 + // _overrideBatchSize 在 JSON 过大时折半缩小,上报成功后重置 + final maxBatch = _overrideBatchSize ?? SdkConfig.maxBatchSize; + final batchSize = + _eventQueue.length > maxBatch ? maxBatch : _eventQueue.length; + + // 安全检查:确保批次大小有效 + if (batchSize <= 0) { + Logger.analyticsSdk('批次大小无效: $batchSize'); + _isUploading = false; + return; + } + + // 取出批次但不从队列移除,上报成功后再移除(at-least-once 语义) + final batch = List>.from( + _eventQueue.take(batchSize), + ); + + try { + // 安全检查:确保批次不为空 + if (batch.isEmpty) { + Logger.analyticsSdk('批次为空,跳过上报'); + return; + } + + final requestBatch = batch + .map(_ensureEventSignatureBeforeUpload) + .toList(growable: false); + + // 在后台线程执行 JSON 编码,避免阻塞主线程 + final batchJson = await _encodeBatchAsync(requestBatch); + + // 检查编码结果 + if (batchJson == '[]' || batchJson.isEmpty) { + Logger.analyticsSdk('JSON 编码失败或结果为空'); + _scheduleRetry(); + return; + } + + // 检查 JSON 大小,避免请求过大 + final jsonSize = utf8.encode(batchJson).length; + if (jsonSize > SdkConfig.maxJsonSize) { + Logger.analyticsSdk( + '警告:JSON 大小过大 (${(jsonSize / 1024 / 1024).toStringAsFixed(2)}MB),缩小批次重试'); + if (batchSize > 1) { + // 折半批次后重试,避免同一批次反复触发 JSON 过大 + _overrideBatchSize = (batchSize / 2).ceil(); + _isUploading = false; + Future.microtask(() { + uploadBatch().catchError((e) { + Logger.analyticsSdk('缩小批次后重试上报 Future 异常: $e'); + }); + }); + } else { + // 单条事件过大,移除该事件避免阻塞队列 + if (_eventQueue.isNotEmpty) { + _eventQueue.removeFirst(); + } + Logger.analyticsSdk( + '警告:单条事件过大 (${(jsonSize / 1024).toStringAsFixed(2)}KB),已丢弃'); + } + return; + } + + try { + // 解析 URL,添加错误处理 + Uri uri; + try { + uri = Uri.parse(_reportUrl!); + if (!uri.hasScheme || (!uri.scheme.startsWith('http'))) { + throw const FormatException('无效的 URL scheme'); + } + } catch (e) { + Logger.analyticsSdk('URL 解析失败: $_reportUrl, 错误: $e'); + _scheduleRetry(); + return; + } + + // 终止窗口内也要保证批次已落盘:否则请求已开始但进程被杀时, + // in-flight 文件虽存在,主缓存却没有这批事件可供重试。 + // 服务端通过 event_id 去重,客户端采用 at-least-once 投递。 + await _persistence.flushBufferAsync(); + await _persistence.markInFlight(requestBatch); + + Logger.analyticsSdk( + '上报中:${requestBatch.length} 条事件,JSON大小: ${(jsonSize / 1024).toStringAsFixed(2)}KB(已落盘)'); + + // 复用 http.Client,利用连接池 + final response = await _client + .post( + uri, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + }, + body: batchJson, + ) + .timeout(SdkConfig.totalRequestTimeout); + final statusCode = response.statusCode; + if (statusCode == 200) { + // 上报成功,按 event_id 从队列中移除已上报的事件 + final reportedIds = + batch.map((e) => e['event_id']).whereType().toSet(); + if (reportedIds.isNotEmpty) { + _eventQueue + .removeWhere((e) => reportedIds.contains(e['event_id'])); + } else { + // event_id 全缺失时回退到按数量移除(兜底) + for (int i = 0; i < batchSize && _eventQueue.isNotEmpty; i++) { + _eventQueue.removeFirst(); + } + } + lastUploadSuccess = true; + lastUploadError = null; + lastUploadTime = DateTime.now(); + _overrideBatchSize = null; // 上报成功,恢复正常批次大小 + _resetBackoff(); + await _persistence.removeEvents(requestBatch); + final confirmedEvents = requestBatch + .map((event) => + '${event['event'] ?? 'unknown'}(${event['event_id'] ?? 'missing_event_id'})') + .join(', '); + Logger.analyticsSdk( + '上报成功: ${requestBatch.length} 条事件,HTTP $statusCode 批次确认,' + '已写入 tombstone;确认事件: [$confirmedEvents]'); + } else { + lastUploadSuccess = false; + lastUploadError = 'HTTP $statusCode'; + lastUploadTime = DateTime.now(); + _scheduleRetry(); + if (statusCode >= 400 && statusCode < 500) { + Logger.analyticsSdk('上报失败: 客户端错误,状态码: $statusCode(可能是数据格式问题)'); + } else if (statusCode >= 500) { + Logger.analyticsSdk('上报失败: 服务器错误,状态码: $statusCode(服务器问题,将重试)'); + } else { + Logger.analyticsSdk('上报失败: 未知状态码 $statusCode'); + } + } + } catch (e) { + lastUploadSuccess = false; + lastUploadError = e is TimeoutException + ? '请求超时' + : e is http.ClientException + ? '网络连接失败' + : e.toString().length > 30 + ? '${e.toString().substring(0, 30)}...' + : e.toString(); + lastUploadTime = DateTime.now(); + _scheduleRetry(); + if (e is TimeoutException) { + Logger.analyticsSdk('上报异常: 请求超时 - ${e.message ?? "未知超时"}', + level: LogLevel.error); + } else if (e is http.ClientException) { + Logger.analyticsSdk('上报异常: 网络连接失败 - ${e.message}', + level: LogLevel.error); + } else { + final errorMessage = e.toString(); + Logger.analyticsSdk('上报异常: $errorMessage', level: LogLevel.error); + } + } finally { + // 无论成功、失败还是异常,确保 in-flight 文件被清除(幂等) + try { + await _persistence.clearInFlight(); + } catch (_) {} + } + } finally { + _isUploading = false; + } + }); + } + + Map _ensureEventSignatureBeforeUpload( + Map event, + ) { + final eventSig = event['event_sig']; + if (eventSig is String && eventSig.trim().isNotEmpty) { + return event; + } + + final signed = SdkContext.attachDeviceEnvIfNeeded(event); + final signedEventSig = signed['event_sig']; + if (signedEventSig is String && signedEventSig.trim().isNotEmpty) { + return signed; + } + return event; + } + + void _logAcceptedEvent(Map json) { + if (!SdkConfig.enableDebugReportLog) return; + try { + Logger.analyticsSdkDeveloperLog( + 'accepted event json: ${jsonEncode(json)}', + ); + } catch (e) { + Logger.analyticsSdk('事件日志打印失败: $e', level: LogLevel.warn); + } + } + + void _writeDebugReportEvent(Map json) { + if (!kDebugMode || !SdkConfig.enableDebugReportLog) return; + try { + _persistence.appendDebugReportEvent(json); + } catch (e) { + Logger.analyticsSdk('debug 完整事件 JSONL 记录失败: $e', level: LogLevel.warn); + } + } + + /// JSON 编码的静态方法,用于 compute isolate + static String _encodeBatchJson(List> batch) { + return jsonEncode(batch); + } + + /// 在后台 isolate 中异步执行 JSON 编码,避免阻塞主线程 + Future _encodeBatchAsync(List> batch) async { + try { + if (batch.isEmpty) { + return '[]'; + } + if (batch.length <= SdkConfig.isolateEncodingThreshold) { + return jsonEncode(batch); + } + try { + return await compute(_encodeBatchJson, batch); + } catch (e) { + Logger.analyticsSdk('compute 失败,回退到主线程编码: $e'); + return jsonEncode(batch); + } + } catch (e) { + Logger.analyticsSdk('JSON 编码失败: $e'); + return '[]'; + } + } + + /// 安排指数退避重试(事件仍在队列中,无需重新入队) + void _scheduleRetry() { + _backoffLevel = (_backoffLevel + 1).clamp(0, SdkConfig.maxBackoffLevel); + _retryTimer?.cancel(); + final delay = _nextDelay(); + _retryTimer = Timer(delay, () { + try { + Logger.analyticsSdk('指数退避重试 (延迟 ${delay.inSeconds}s)'); + uploadBatch(); + } catch (e) { + Logger.analyticsSdk('重试上报异常: $e'); + } + }); + } + + /// 计算下次延迟时间(指数退避 + 上限) + Duration _nextDelay() { + final exponential = SdkConfig.baseRetryDelay * pow(2, _backoffLevel); + return exponential > SdkConfig.maxRetryDelay + ? SdkConfig.maxRetryDelay + : exponential; + } + + /// 上报成功时重置退避状态 + void _resetBackoff() { + if (_backoffLevel > 0) { + _backoffLevel = 0; + _retryTimer?.cancel(); + _retryTimer = null; + Logger.analyticsSdk('上报成功,退避机制已重置'); + } + } + + Future initPersistence() async { + try { + await _persistence.init(); + final debugPath = debugReportEventLogPath; + if (debugPath != null && debugPath.isNotEmpty) { + Logger.analyticsSdk('debug 完整事件 JSONL 文件: $debugPath'); + } + } catch (e) { + Logger.analyticsSdk('持久化初始化失败: $e'); + } + } + + Future loadCachedEvents() async { + try { + final recovered = await _persistence.loadEvents( + queue: _eventQueue, + maxQueueSize: SdkConfig.maxQueueSize, + maxCacheLines: SdkConfig.maxCacheLines, + isEventTypeEnabled: EventTypeConfigManager.instance.isEventTypeEnabled, + ); + + if (recovered.isNotEmpty) { + final availableSpace = SdkConfig.maxQueueSize - _eventQueue.length; + if (availableSpace > 0) { + final eventsToRecover = recovered.length > availableSpace + ? recovered.sublist(0, availableSpace) + : recovered; + _eventQueue.addAll(eventsToRecover); + Logger.analyticsSdk('从缓存恢复 ${eventsToRecover.length} 条事件'); + } else { + Logger.analyticsSdk('警告:队列已满,无法恢复缓存事件'); + } + } + + _flushIfNeeded(); + } catch (e) { + Logger.analyticsSdk('缓存加载失败: $e'); + } + } + + Future clearCache() async { + await _persistence.clearCache(); + } + + /// 释放资源(定时器、HttpClient、缓存 buffer 等) + void dispose() { + try { + stopAutoUploadTimer(); + _retryTimer?.cancel(); + _retryTimer = null; + _isUploading = false; + _persistence.flushBuffer(); + _httpClient?.close(); + _httpClient = null; + } catch (e) { + Logger.analyticsSdk('EventProcessor.dispose 异常: $e'); + } + } + + /// 测试专用入口:触发 tombstone 写入以供单元测试验证缓存移除行为 + @visibleForTesting + Future removeEventsFromCacheForTest( + List> events) => + _persistence.removeEvents(events); + + /// 测试专用入口:注入 HTTP client 以验证最终请求体。 + @visibleForTesting + void setHttpClientForTest(http.Client client) { + _httpClient?.close(); + _httpClient = client; + } + + /// 异步释放资源(先 await 缓存刷写,再释放其他资源) + /// + /// 推荐在 SDK dispose 时使用此方法替代 [dispose],确保缓存 buffer 写入磁盘。 + Future disposeAsync() async { + try { + stopAutoUploadTimer(); + _retryTimer?.cancel(); + _retryTimer = null; + _isUploading = false; + await _persistence.flushBufferAsync(); + _httpClient?.close(); + _httpClient = null; + } catch (e) { + Logger.analyticsSdk('EventProcessor.disposeAsync 异常: $e'); + } + } +} diff --git a/analytics_sdk/lib/manager/event_type_config_manager.dart b/analytics_sdk/lib/manager/event_type_config_manager.dart new file mode 100644 index 0000000..e16d9f5 --- /dev/null +++ b/analytics_sdk/lib/manager/event_type_config_manager.dart @@ -0,0 +1,168 @@ +import 'dart:convert'; +import 'package:analytics_sdk/config/sdk_config.dart'; +import 'package:analytics_sdk/utils/logger.dart'; +import 'package:analytics_sdk/utils/platform_storage.dart'; + +/// 事件类型配置管理器:解析并缓存业务方传入的事件类型配置 +class EventTypeConfigManager { + static final EventTypeConfigManager instance = + EventTypeConfigManager._internal(); + + factory EventTypeConfigManager() => instance; + + EventTypeConfigManager._internal(); + + Set _enabledEventTypes = {}; + + /// 获取已启用的事件类型集合 + Set get enabledEventTypes => Set.unmodifiable(_enabledEventTypes); + + /// 检查某个事件类型是否被启用 + /// 如果配置为空,默认允许所有事件上报(向后兼容) + bool isEventTypeEnabled(String eventType) { + if (_enabledEventTypes.isEmpty) return true; + return _enabledEventTypes.contains(eventType); + } + + /// 传入已解密的事件类型配置 JSON 字符串,解析后写入本地缓存 + /// + /// 支持格式: + /// - 字符串数组:`["app_page_view", "ad_impression", ...]` + /// - JSON 对象:`{"enabled_event_types": [...]}` + /// + /// 返回解析到的事件类型数量 + Future initWithConfig(String decryptedJson) async { + try { + final eventTypes = _parseEventTypes(decryptedJson); + if (eventTypes.isNotEmpty) { + _enabledEventTypes = eventTypes; + await _saveConfigToCache(eventTypes); + Logger.eventTypeConfigManager( + '事件类型配置已更新,共 ${eventTypes.length} 个: ${eventTypes.join(", ")}'); + } else { + Logger.eventTypeConfigManager('解析结果为空,保持原有配置'); + } + return eventTypes.length; + } catch (e) { + Logger.eventTypeConfigManager('initWithConfig 异常: $e'); + return 0; + } + } + + /// 从本地缓存加载配置(若无缓存则保持 allow-all 状态) + Future loadCachedConfig() async { + try { + await _loadCachedConfig(); + } catch (e) { + Logger.eventTypeConfigManager('加载缓存配置异常: $e'); + } + } + + /// 清理资源 + void dispose() { + _enabledEventTypes.clear(); + } + + /// 解析 JSON 字符串,提取事件类型列表(供外部验证用,如 validateEncryptedConfig) + /// + /// 返回 List,解析失败或结果为空则返回空列表,不抛出异常。 + static List parseForValidation(String json) { + try { + dynamic decoded = jsonDecode(json); + if (decoded is String && decoded.trim().isNotEmpty) { + try { + decoded = jsonDecode(decoded); + } catch (_) {} + } + if (decoded is List) { + return (decoded as List) + .map((e) => e.toString().trim()) + .where((e) => e.isNotEmpty) + .toList(); + } else if (decoded is Map) { + final list = decoded['enabled_event_types'] as List? ?? + decoded['event_types'] as List? ?? + decoded['data'] as List?; + if (list != null) { + return list + .map((e) => e.toString().trim()) + .where((e) => e.isNotEmpty) + .toList(); + } + } + } catch (_) {} + return []; + } + + // ==================== 私有方法 ==================== + + /// 解析 JSON 字符串,提取事件类型列表 + Set _parseEventTypes(String json) { + try { + dynamic decoded = jsonDecode(json); + // 兼容外层为字符串的格式:"[\"event_type\", ...]" + if (decoded is String && decoded.trim().isNotEmpty) { + try { + decoded = jsonDecode(decoded); + } catch (_) {} + } + if (decoded is List) { + return (decoded as List) + .map((e) => e.toString().trim()) + .where((e) => e.isNotEmpty) + .toSet(); + } else if (decoded is Map) { + final list = decoded['enabled_event_types'] as List? ?? + decoded['event_types'] as List? ?? + decoded['data'] as List?; + if (list != null) { + return list + .map((e) => e.toString().trim()) + .where((e) => e.isNotEmpty) + .toSet(); + } + } + } catch (e) { + Logger.eventTypeConfigManager('事件类型 JSON 解析失败: $e'); + } + return {}; + } + + Future _saveConfigToCache(Set eventTypes) async { + try { + await PlatformStorage.write( + SdkConfig.eventTypeConfigCacheFileName, + jsonEncode({ + 'enabled_event_types': eventTypes.toList(), + 'updated_at': DateTime.now().toIso8601String(), + }), + ); + Logger.eventTypeConfigManager('配置已保存到缓存'); + } catch (e) { + Logger.eventTypeConfigManager('保存缓存失败: $e'); + } + } + + Future _loadCachedConfig() async { + try { + final content = + await PlatformStorage.read(SdkConfig.eventTypeConfigCacheFileName); + if (content == null) { + Logger.eventTypeConfigManager('缓存文件不存在'); + return; + } + final json = jsonDecode(content) as Map; + final list = json['enabled_event_types']; + if (list is List) { + _enabledEventTypes = (list as List) + .map((e) => e.toString().trim()) + .where((e) => e.isNotEmpty) + .toSet(); + Logger.eventTypeConfigManager( + '从缓存加载 ${_enabledEventTypes.length} 个事件类型'); + } + } catch (e) { + Logger.eventTypeConfigManager('加载缓存失败: $e'); + } + } +} diff --git a/analytics_sdk/lib/manager/network_state_fields_stub.dart b/analytics_sdk/lib/manager/network_state_fields_stub.dart new file mode 100644 index 0000000..cb5f84b --- /dev/null +++ b/analytics_sdk/lib/manager/network_state_fields_stub.dart @@ -0,0 +1,3 @@ +Future> collectNetworkStateFields() async { + return const {}; +} diff --git a/analytics_sdk/lib/manager/network_state_fields_web.dart b/analytics_sdk/lib/manager/network_state_fields_web.dart new file mode 100644 index 0000000..d6ffd81 --- /dev/null +++ b/analytics_sdk/lib/manager/network_state_fields_web.dart @@ -0,0 +1,65 @@ +// ignore: avoid_web_libraries_in_flutter +import 'dart:html' as html; + +import 'package:analytics_sdk/utils/js_interop_compat.dart'; + +Future> collectNetworkStateFields() async { + final connection = _connection(html.window.navigator); + if (connection == null) return const {}; + final fields = {}; + fields['connection_type'] = _string(connection, 'type', 'unknown'); + fields['effective_type'] = _effectiveType(connection); + fields['downlink'] = _downlinkKbps(connection); + fields['rtt'] = _number(connection, 'rtt', -1).round(); + fields.removeWhere((key, value) { + if (value is String) return value.trim().isEmpty; + if (value is num) return value < 0; + return value == null; + }); + return fields; +} + +Object? _connection(html.Navigator navigator) { + for (final key in ['connection', 'mozConnection', 'webkitConnection']) { + try { + if (hasJsProperty(navigator, key)) { + return getJsProperty(navigator, key); + } + } catch (_) {} + } + return null; +} + +String _string(Object? object, String key, String fallback) { + if (object == null) return fallback; + try { + final value = getJsProperty(object, key); + final string = value?.toString().trim() ?? ''; + return string.isEmpty ? fallback : string; + } catch (_) { + return fallback; + } +} + +num _number(Object? object, String key, num fallback) { + if (object == null) return fallback; + try { + final value = getJsProperty(object, key); + if (value is num && value.isFinite) return value; + return num.tryParse(value?.toString() ?? '') ?? fallback; + } catch (_) { + return fallback; + } +} + +String _effectiveType(Object? connection) { + final value = _string(connection, 'effectiveType', 'unknown').toLowerCase(); + if (value == 'slow-2g') return '2g'; + const allowed = {'2g', '3g', '4g', '5g'}; + return allowed.contains(value) ? value : 'unknown'; +} + +int _downlinkKbps(Object? connection) { + final downlink = _number(connection, 'downlink', -1); + return downlink >= 0 ? (downlink * 1000).round() : -1; +} diff --git a/analytics_sdk/lib/manager/page_click_auto_fields.dart b/analytics_sdk/lib/manager/page_click_auto_fields.dart new file mode 100644 index 0000000..ee4c62b --- /dev/null +++ b/analytics_sdk/lib/manager/page_click_auto_fields.dart @@ -0,0 +1,2 @@ +export 'page_click_auto_fields_stub.dart' + if (dart.library.html) 'page_click_auto_fields_web.dart'; diff --git a/analytics_sdk/lib/manager/page_click_auto_fields_stub.dart b/analytics_sdk/lib/manager/page_click_auto_fields_stub.dart new file mode 100644 index 0000000..b92ac4e --- /dev/null +++ b/analytics_sdk/lib/manager/page_click_auto_fields_stub.dart @@ -0,0 +1,33 @@ +class ClickDocumentMetrics { + final int clickDocumentX; + final int clickDocumentY; + final int pageWidth; + final int pageHeight; + final int scrollTop; + final int scrollLeft; + + const ClickDocumentMetrics({ + required this.clickDocumentX, + required this.clickDocumentY, + required this.pageWidth, + required this.pageHeight, + required this.scrollTop, + required this.scrollLeft, + }); + + static const unknown = ClickDocumentMetrics( + clickDocumentX: -1, + clickDocumentY: -1, + pageWidth: -1, + pageHeight: -1, + scrollTop: -1, + scrollLeft: -1, + ); +} + +ClickDocumentMetrics currentClickDocumentMetrics({ + required int clickViewportX, + required int clickViewportY, +}) { + return ClickDocumentMetrics.unknown; +} diff --git a/analytics_sdk/lib/manager/page_click_auto_fields_web.dart b/analytics_sdk/lib/manager/page_click_auto_fields_web.dart new file mode 100644 index 0000000..21f7153 --- /dev/null +++ b/analytics_sdk/lib/manager/page_click_auto_fields_web.dart @@ -0,0 +1,77 @@ +// ignore: avoid_web_libraries_in_flutter +import 'dart:html' as html; + +class ClickDocumentMetrics { + final int clickDocumentX; + final int clickDocumentY; + final int pageWidth; + final int pageHeight; + final int scrollTop; + final int scrollLeft; + + const ClickDocumentMetrics({ + required this.clickDocumentX, + required this.clickDocumentY, + required this.pageWidth, + required this.pageHeight, + required this.scrollTop, + required this.scrollLeft, + }); + + static const unknown = ClickDocumentMetrics( + clickDocumentX: -1, + clickDocumentY: -1, + pageWidth: -1, + pageHeight: -1, + scrollTop: -1, + scrollLeft: -1, + ); +} + +ClickDocumentMetrics currentClickDocumentMetrics({ + required int clickViewportX, + required int clickViewportY, +}) { + try { + final documentElement = html.document.documentElement; + final body = html.document.body; + final pageWidth = _maxPositive([ + documentElement?.scrollWidth, + body?.scrollWidth, + documentElement?.clientWidth, + body?.clientWidth, + ]); + final pageHeight = _maxPositive([ + documentElement?.scrollHeight, + body?.scrollHeight, + documentElement?.clientHeight, + body?.clientHeight, + ]); + if (pageWidth <= 0 || pageHeight <= 0) { + return ClickDocumentMetrics.unknown; + } + + final scrollLeft = _nonNegative(html.window.pageXOffset.round()); + final scrollTop = _nonNegative(html.window.pageYOffset.round()); + return ClickDocumentMetrics( + clickDocumentX: _nonNegative(clickViewportX + scrollLeft), + clickDocumentY: _nonNegative(clickViewportY + scrollTop), + pageWidth: pageWidth, + pageHeight: pageHeight, + scrollTop: scrollTop, + scrollLeft: scrollLeft, + ); + } catch (_) { + return ClickDocumentMetrics.unknown; + } +} + +int _maxPositive(List values) { + var max = -1; + for (final value in values) { + if (value != null && value > max) max = value; + } + return max > 0 ? max : -1; +} + +int _nonNegative(int value) => value < 0 ? 0 : value; diff --git a/analytics_sdk/lib/manager/page_name_manager.dart b/analytics_sdk/lib/manager/page_name_manager.dart new file mode 100644 index 0000000..e3b62e9 --- /dev/null +++ b/analytics_sdk/lib/manager/page_name_manager.dart @@ -0,0 +1,177 @@ +// lib/utils/page_name_mapper.dart + +import 'package:analytics_sdk/utils/logger.dart'; + +/// 统一的页面 pageKey → pageName 映射管理器 +/// 支持静态默认映射 + 动态添加/覆盖(业务方可在初始化时自定义) +/// +/// ## 两种注册格式 +/// +/// **老格式(精确匹配)**:值为字符串,pageKey 与路由名称完全一致时命中 +/// ```dart +/// PageNameMapper.addMappings({'home': '首页'}); +/// ``` +/// +/// **新格式(支持 `:param` 动态路由)**:值为 `{'key': ..., 'name': ...}` +/// - `key`:上报时使用的稳定 pageKey(替换掉含真实 ID 的原始路由名) +/// - `name`:页面展示名称 +/// ```dart +/// PageNameMapper.addMappings({ +/// '/video/:id': {'key': 'video_detail', 'name': '视频详情'}, +/// }); +/// ``` +class PageNameMapper { + // 默认映射表(SDK 内置,精确匹配) + static const Map _defaultMap = {}; + + // 老格式:精确匹配映射表(路由名 → pageName) + static final Map _customMap = {}; + + // 新格式::param 模式映射表(pattern → ResolvedPageInfo) + static final Map _patternMap = {}; + + // 新格式 resolvedKey 缓存(resolvedKey → pageName),与 _customMap 隔离 + // 供 getPageName 在 currentPageKey 已被解析为稳定 key 时反查 pageName + static final Map _resolvedKeyMap = {}; + + static const String _tag = 'PageNameMapper'; + + /// 去除 pageKey 前导斜杠,结果为空时保留原值(如纯 "/" 保持为 "/") + static String normalizeKey(String pageKey) { + if (!pageKey.startsWith('/')) return pageKey; + final stripped = pageKey.substring(1); + return stripped.isEmpty ? pageKey : stripped; + } + + /// 获取页面名称(老格式精确匹配,行为与之前完全一致) + /// + /// 同时查 _resolvedKeyMap,确保新格式解析后的稳定 key 也能反查到 pageName + static String getPageName(String pageKey) { + final key = normalizeKey(pageKey); + return _customMap[key] ?? _resolvedKeyMap[key] ?? _defaultMap[key] ?? key; + } + + /// 解析路由名称,返回上报用的稳定 [ResolvedPageInfo]。 + /// + /// 查找优先级: + /// 1. 老格式精确匹配(_customMap / _defaultMap) + /// 2. 新格式精确匹配(_patternMap) + /// 3. 新格式 `:param` 通配匹配(_patternMap) + /// 4. Fallback:key = name = 归一化后的路由名 + static ResolvedPageInfo resolvePageInfo(String routeName) { + final normalized = normalizeKey(routeName); + + // 1. 老格式精确匹配(只查 _customMap / _defaultMap,不查 _resolvedKeyMap) + final directName = _customMap[normalized] ?? _defaultMap[normalized]; + if (directName != null) return ResolvedPageInfo(normalized, directName); + + // 2. 新格式精确匹配 + final exactPattern = _patternMap[normalized]; + if (exactPattern != null) return exactPattern; + + // 3. 新格式 :param 通配匹配 + for (final entry in _patternMap.entries) { + if (_matchesPattern(entry.key, normalized)) { + return entry.value; + } + } + + // 4. Fallback + return ResolvedPageInfo(normalized, normalized); + } + + /// 动态添加或覆盖单个映射 + /// + /// [value] 可为字符串(老格式)或 `{'key': String, 'name': String}`(新格式) + static void addMapping(String routePattern, dynamic value) { + final normalized = normalizeKey(routePattern); + if (value is String) { + _customMap[normalized] = value; + } else if (value is Map) { + // ── 风险 1 修复:严格校验 key / name 字段,避免运行时 crash ── + final resolvedKey = value['key']; + final name = value['name']; + if (resolvedKey is! String || resolvedKey.isEmpty) { + Logger.warn(_tag, 'addMapping("$routePattern") 的 key 字段无效或为空,已忽略'); + return; + } + if (name is! String || name.isEmpty) { + Logger.warn(_tag, 'addMapping("$routePattern") 的 name 字段无效或为空,已忽略'); + return; + } + + // ── 风险 3 修复:检测与已注册 pattern 的歧义冲突 ── + if (_hasConflictingPattern(normalized)) { + Logger.warn( + _tag, + 'addMapping("$routePattern") 与已注册的某个 pattern 存在歧义,' + '将按注册顺序匹配,请检查是否重复注册', + ); + } + + _patternMap[normalized] = ResolvedPageInfo(resolvedKey, name); + // ── 风险 2 修复:写入独立的 _resolvedKeyMap,不污染 _customMap ── + _resolvedKeyMap[resolvedKey] = name; + } + } + + /// 批量动态添加或覆盖映射,兼容老格式与新格式混传 + static void addMappings(Map mappings) { + for (final entry in mappings.entries) { + addMapping(entry.key, entry.value); + } + } + + /// 清空自定义映射(测试或重置用) + static void clearCustom() { + _customMap.clear(); + _patternMap.clear(); + _resolvedKeyMap.clear(); + } + + /// 判断路由名称是否匹配某个 :param 模式 + static bool _matchesPattern(String pattern, String routeName) { + final patternParts = pattern.split('/'); + final routeParts = routeName.split('/'); + if (patternParts.length != routeParts.length) return false; + for (int i = 0; i < patternParts.length; i++) { + if (patternParts[i].startsWith(':')) continue; // 通配符,跳过 + if (patternParts[i] != routeParts[i]) return false; + } + return true; + } + + /// 检测新 pattern 是否与已注册的 pattern 存在歧义(可能命中同一路由) + static bool _hasConflictingPattern(String newPattern) { + for (final existing in _patternMap.keys) { + if (existing == newPattern) continue; // 相同 pattern,属于覆盖,不是冲突 + if (_patternsConflict(existing, newPattern)) return true; + } + return false; + } + + /// 判断两个 pattern 是否可能匹配同一条路由 + static bool _patternsConflict(String p1, String p2) { + final parts1 = p1.split('/'); + final parts2 = p2.split('/'); + if (parts1.length != parts2.length) return false; + for (int i = 0; i < parts1.length; i++) { + final isParam1 = parts1[i].startsWith(':'); + final isParam2 = parts2[i].startsWith(':'); + // 两段都是固定字面量且不相等 → 不可能同时匹配 + if (!isParam1 && !isParam2 && parts1[i] != parts2[i]) return false; + } + return true; + } +} + +/// [PageNameMapper.resolvePageInfo] 的返回值:路由解析后的稳定标识 +class ResolvedPageInfo { + /// 上报用的稳定 pageKey(新格式下为业务方自定义 key,老格式下为归一化路由名) + final String key; + + /// 页面展示名称 + final String name; + + const ResolvedPageInfo(this.key, this.name); +} diff --git a/analytics_sdk/lib/manager/page_observer_registration_guard.dart b/analytics_sdk/lib/manager/page_observer_registration_guard.dart new file mode 100644 index 0000000..0613a03 --- /dev/null +++ b/analytics_sdk/lib/manager/page_observer_registration_guard.dart @@ -0,0 +1,41 @@ +import 'package:analytics_sdk/utils/logger.dart'; + +/// 校验页面生命周期 Observer 的接入状态,并避免重复刷屏。 +class PageObserverRegistrationGuard { + bool _missingWarningLogged = false; + bool _duplicateWarningLogged = false; + + void reset() { + _missingWarningLogged = false; + _duplicateWarningLogged = false; + } + + void check({ + required bool pageObserverAttached, + required bool sessionObserverAttached, + }) { + if (!pageObserverAttached && !sessionObserverAttached) { + if (_missingWarningLogged) return; + _missingWarningLogged = true; + Logger.pageLifecycleObserver( + '未检测到页面 Observer:请在 MaterialApp.navigatorObservers 中注册 ' + 'AnalyticsSdk.instance.pageObserver 或 ' + 'AnalyticsSdk.instance.sessionObserver,二选一。否则 ' + 'app_session_summary 的 page_stay_list 可能不完整。', + level: LogLevel.warn, + ); + return; + } + + if (pageObserverAttached && sessionObserverAttached) { + if (_duplicateWarningLogged) return; + _duplicateWarningLogged = true; + Logger.pageLifecycleObserver( + '检测到 pageObserver 与 sessionObserver 同时注册:两者必须二选一。' + 'SDK 已对页面停留和自动 app_page_view 做幂等保护,' + '但请尽快修正接入配置。', + level: LogLevel.warn, + ); + } + } +} diff --git a/analytics_sdk/lib/manager/page_view_auto_fields.dart b/analytics_sdk/lib/manager/page_view_auto_fields.dart new file mode 100644 index 0000000..1609db2 --- /dev/null +++ b/analytics_sdk/lib/manager/page_view_auto_fields.dart @@ -0,0 +1,2 @@ +export 'page_view_auto_fields_stub.dart' + if (dart.library.html) 'page_view_auto_fields_web.dart'; diff --git a/analytics_sdk/lib/manager/page_view_auto_fields_stub.dart b/analytics_sdk/lib/manager/page_view_auto_fields_stub.dart new file mode 100644 index 0000000..2c0657a --- /dev/null +++ b/analytics_sdk/lib/manager/page_view_auto_fields_stub.dart @@ -0,0 +1 @@ +String currentPageUrl() => ''; diff --git a/analytics_sdk/lib/manager/page_view_auto_fields_web.dart b/analytics_sdk/lib/manager/page_view_auto_fields_web.dart new file mode 100644 index 0000000..02dd5d4 --- /dev/null +++ b/analytics_sdk/lib/manager/page_view_auto_fields_web.dart @@ -0,0 +1,27 @@ +// ignore: avoid_web_libraries_in_flutter +import 'dart:html' as html; + +String currentPageUrl() { + try { + final uri = Uri.tryParse(html.window.location.href); + if (uri == null) return ''; + final buffer = StringBuffer(); + if (uri.scheme.isNotEmpty) { + buffer + ..write(uri.scheme) + ..write('://'); + } + if (uri.host.isNotEmpty) { + buffer.write(uri.host); + } + if (uri.hasPort) { + buffer.write(':${uri.port}'); + } + if (uri.path.isNotEmpty) { + buffer.write(uri.path); + } + return buffer.toString(); + } catch (_) { + return ''; + } +} diff --git a/analytics_sdk/lib/manager/sdk_activation_store.dart b/analytics_sdk/lib/manager/sdk_activation_store.dart new file mode 100644 index 0000000..9cc1526 --- /dev/null +++ b/analytics_sdk/lib/manager/sdk_activation_store.dart @@ -0,0 +1,21 @@ +import 'package:analytics_sdk/utils/platform_storage.dart'; + +class SdkActivationStore { + SdkActivationStore._(); + + static const String _storageKey = 'analytics_sdk_activation_time.txt'; + + static Future getOrCreateActivationTime() async { + try { + final cached = await PlatformStorage.read(_storageKey); + final cachedTs = int.tryParse(cached?.trim() ?? ''); + if (cachedTs != null && cachedTs > 0) return cachedTs; + + final now = DateTime.now().millisecondsSinceEpoch ~/ 1000; + await PlatformStorage.write(_storageKey, now.toString()); + return now; + } catch (_) { + return 0; + } + } +} diff --git a/analytics_sdk/lib/manager/sdk_context.dart b/analytics_sdk/lib/manager/sdk_context.dart new file mode 100644 index 0000000..b14dc9b --- /dev/null +++ b/analytics_sdk/lib/manager/sdk_context.dart @@ -0,0 +1,439 @@ +import 'dart:convert'; + +import 'package:analytics_sdk/utils/device_fingerprint_util.dart'; +import 'package:analytics_sdk/utils/logger.dart'; +import 'package:crypto/crypto.dart'; +import 'package:flutter/foundation.dart'; + +/// SDK 全局上下文:持有所有公共字段、提供 event_id 计算。 +/// +/// 作为 SDK 内部的单一状态来源(SSOT),所有事件构造时从此处读取公共字段。 +/// 不对外暴露,接入方无需直接使用。 +class SdkContext { + /// SDK 版本号(写死,与 pubspec.yaml 保持一致) + static const String kSdkVersion = '1.4.6'; + + /// appVersion 公共字段默认值。 + /// 接入方未传 `appVersion`、或 release 模式下传入非法值时均使用该值, + /// 保证事件 payload 与 `AnalyticsSdk.getParams()` 返回值保持一致。 + static const String kDefaultAppVersion = '1.0.0'; + + /// 客户端事件签名规则版本。 + static const String kEventSignatureVersion = '2.0.0'; + + static String _eventSignatureHmacKey = ''; + static String _eventSignatureVersion = kEventSignatureVersion; + + static String? _appId; + static String? _channel; + static String? _uid; + static String? _appVersion; + static String? _device; + static String? _deviceId; + static String? _deviceBrand; + static String? _deviceModel; + static String? _userAgent; + static String? _systemName; + static String? _systemVersion; + static String? _deviceFingerprint; + static String _fingerprintVersion = DeviceFingerprintUtil.kVersion; + static Map _environmentFields = _defaultEnvironmentFields(); + + /// SID 提供者(由 AnalyticsSdk.init() 注入)。 + /// 调用时同时返回当前 SID 并记录用户行为,避免 entity 层直接依赖 SessionManager。 + static String Function()? sidProvider; + + /// 配置公共字段 + /// + /// 所有参数均为可选,非 null 值才会覆盖现有值。 + /// 通常在 [AnalyticsSdk.init] 中调用,也可在 init 之后按需更新。 + /// deviceFingerprint / fingerprintVersion 不再支持从外部传入。 + static void configure({ + String? appId, + String? channel, + String? uid, + String? appVersion, + String? device, + String? deviceId, + String? deviceBrand, + String? deviceModel, + String? userAgent, + String? systemName, + String? systemVersion, + String? deviceFingerprint, + String? fingerprintVersion, + Map? environmentFields, + }) { + if (appId != null) _appId = appId; + if (channel != null) _channel = channel; + if (uid != null) _uid = uid; + if (appVersion != null) { + if (RegExp(r'^\d+\.\d+\.\d+$').hasMatch(appVersion)) { + _appVersion = appVersion; + } else if (kDebugMode) { + Logger.analyticsSdk('appVersion 格式不正确,应为 x.y.z,当前值:$appVersion,已忽略', + level: LogLevel.error); + } else { + _appVersion = kDefaultAppVersion; + } + } + if (device != null) _device = device; + if (deviceId != null) _deviceId = deviceId; + if (deviceBrand != null) _deviceBrand = deviceBrand; + if (deviceModel != null) _deviceModel = deviceModel; + if (userAgent != null) _userAgent = userAgent; + if (systemName != null) _systemName = systemName; + if (systemVersion != null) _systemVersion = systemVersion; + if (environmentFields != null) { + _environmentFields = { + ..._defaultEnvironmentFields(), + ..._normalizeEnvironmentFields(environmentFields), + }; + } + } + + /// SDK 内部调用:重置自动生成的设备指纹状态。 + static void resetGeneratedDeviceFingerprint() { + _deviceFingerprint = null; + _fingerprintVersion = DeviceFingerprintUtil.kVersion; + } + + /// SDK 内部调用:写入已生成的设备指纹。 + static void configureGeneratedDeviceFingerprint(String deviceFingerprint) { + final normalized = deviceFingerprint.trim(); + _deviceFingerprint = normalized.isEmpty ? null : normalized; + _fingerprintVersion = DeviceFingerprintUtil.kVersion; + } + + /// 更新用户 ID(登录或切换账号时调用) + static void setUid(String newUid) { + _uid = newUid; + } + + /// 更新渠道(渠道变更时调用) + static void setChannel(String channel) { + _channel = channel; + } + + /// 增量更新系统、网络、硬件和浏览器环境快照字段。 + /// + /// 用于网络、电量、充电状态等运行期可能变化的字段刷新。 + static void updateEnvironmentFields(Map fields) { + if (fields.isEmpty) return; + _environmentFields = { + ..._environmentFields, + ..._normalizeEnvironmentFields(fields), + }; + } + + /// 配置客户端事件签名密钥。 + /// + /// 密钥来自加密配置解密后的 `signKey`,不写入事件、payload、日志或缓存。 + static void configureEventSignature({ + String? signKey, + String? version, + }) { + if (signKey != null) { + _eventSignatureHmacKey = signKey.trim(); + } + final normalizedVersion = version?.trim(); + if (normalizedVersion != null && normalizedVersion.isNotEmpty) { + _eventSignatureVersion = normalizedVersion; + } + } + + /// 重置所有公共字段为 null(SDK 重新初始化时使用) + /// + /// 与 [configure] 不同,此方法会将所有字段清为 null, + /// 而 configure 中传 null 表示"不修改"。 + static void reset() { + _appId = null; + _channel = null; + _uid = null; + _appVersion = null; + _device = null; + _deviceId = null; + _deviceBrand = null; + _deviceModel = null; + _userAgent = null; + _systemName = null; + _systemVersion = null; + _deviceFingerprint = null; + _fingerprintVersion = DeviceFingerprintUtil.kVersion; + _environmentFields = _defaultEnvironmentFields(); + _eventSignatureHmacKey = ''; + _eventSignatureVersion = kEventSignatureVersion; + sidProvider = null; + } + + // ──────── Getters(调试 / 外部复用) ──────────────────────────────────── + + static String? get appId => _appId; + static String? get channel => _channel; + static String? get uid => _uid; + static String? get appVersion => _appVersion; + static String? get deviceId => _deviceId; + static String? get deviceBrand => _deviceBrand; + static String? get deviceModel => _deviceModel; + static String get deviceFingerprint => _deviceFingerprint ?? ''; + static String get fingerprintVersion => _fingerprintVersion; + static Map get environmentFields => + Map.from(_environmentFields); + + /// 获取设备公共字段(供产品方在服务端上报事件时复用) + /// + /// 返回的 Map 包含:sid、device、device_id、device_brand、device_model、 + /// user_agent、system_name、system_version、device_fingerprint、fp_version。 + /// 未配置的字段返回空字符串。 + static Map getDeviceCommonFields() { + String sid = ''; + try { + sid = sidProvider?.call() ?? ''; + } catch (_) { + // 未初始化或 SID 获取异常时保持为空,确保该读取 API 不抛异常。 + } + + return { + 'sid': sid, + 'device': _device ?? '', + 'device_id': _deviceId ?? '', + 'device_brand': _deviceBrand ?? '', + 'device_model': _deviceModel ?? '', + 'user_agent': _userAgent ?? '', + 'system_name': _systemName ?? '', + 'system_version': _systemVersion ?? '', + 'device_fingerprint': _deviceFingerprint ?? '', + 'fp_version': _fingerprintVersion, + }; + } + + /// 获取系统、网络、硬件和浏览器环境快照字段。 + /// + /// 仅供 SDK 内部构建 `sdk_init` 启动快照和少量汇总事件使用,普通事件公共字段 + /// 不再重复携带这些环境快照。 + static Map getEnvironmentSnapshotFields() { + return Map.from(_environmentFields); + } + + /// 给 SDK 客户端事件补充外层签名字段。 + /// + /// 历史版本曾把轻量环境签名写入 payload;这里会清理这些保留字段, + /// 避免同一事件同时出现新旧两套签名。 + static Map attachDeviceEnvIfNeeded( + Map event, + ) { + final eventName = event['event']?.toString() ?? ''; + final isServerOnlyEvent = _serverOnlyEvents.contains(eventName); + final result = Map.from(event); + final existingPayload = result['payload']; + final existingDeviceEnv = result.remove('device_env'); + + if (existingPayload is Map || existingPayload == null) { + final payload = { + if (existingPayload is Map) + ...Map.from(existingPayload), + if (existingDeviceEnv is Map) + ...Map.from(existingDeviceEnv), + }; + payload.removeWhere( + (key, _) => _reservedPayloadSignatureFields.contains(key)); + result['payload'] = payload; + } + + if (!isServerOnlyEvent && _eventSignatureHmacKey.isNotEmpty) { + final eventForSignature = Map.from(result); + result['event_sig_version'] = _eventSignatureVersion; + result['event_sig'] = _eventSignature(eventForSignature); + } + return result; + } + + /// 生成公共字段 Map + /// + /// 包含所有客户端事件上报所需的公共字段: + /// event、channel、app_id、uid、sid、client_ts、 + /// device、device_id、device_brand、device_model、 + /// user_agent、system_name、system_version、 + /// device_fingerprint、fp_version、sdk_version、app_version。 + /// + /// 本方法永远不会抛出异常。 + static Map generateCommonFields(String event) { + final now = DateTime.now(); + final clientTs = now.millisecondsSinceEpoch ~/ 1000; + + String sid; + try { + sid = sidProvider?.call() ?? 'fallback_${now.millisecondsSinceEpoch}'; + } catch (_) { + sid = 'fallback_${now.millisecondsSinceEpoch}'; + } + + return { + 'event': event, + 'channel': _channel ?? '', + 'app_id': _appId ?? '', + 'uid': _uid ?? '', + 'sid': sid, + 'client_ts': clientTs, + 'device': _device ?? '', + 'device_id': _deviceId ?? '', + 'device_brand': _deviceBrand ?? '', + 'device_model': _deviceModel ?? '', + 'user_agent': _userAgent ?? '', + 'system_name': _systemName ?? '', + 'system_version': _systemVersion ?? '', + 'device_fingerprint': _deviceFingerprint ?? '', + 'fp_version': _fingerprintVersion, + 'sdk_version': kSdkVersion, + 'app_version': _appVersion ?? kDefaultAppVersion, + }; + } + + /// 根据字段列表生成 event_id + /// + /// 计算规则: + /// 1. 对每个字段值单独做 MD5 + /// 2. 将所有 MD5 用 `|` 拼接 + /// 3. 对拼接结果再做一次 MD5,作为最终 event_id + /// + /// **注意**:`device_fingerprint` 不参与 event_id 计算。 + /// 本方法永远不会抛出异常,失败时返回基于时间戳的备选 ID。 + static String generateEventId(List fields) { + try { + final fieldDigests = []; + for (final value in fields) { + final v = value; + final bytes = utf8.encode(v); + final digest = md5.convert(bytes); + fieldDigests.add(digest.toString()); + } + + final joined = fieldDigests.join('|'); + final finalDigest = md5.convert(utf8.encode(joined)); + return finalDigest.toString(); + } catch (e) { + try { + final content = fields.join('|'); + return 'h${(content.hashCode & 0x7FFFFFFF).toRadixString(16)}'; + } catch (_) { + return 't${DateTime.now().millisecondsSinceEpoch}'; + } + } + } + + static Map _defaultEnvironmentFields() => { + 'language': '', + 'timezone': '', + 'package_name': '', + 'screen_width': -1, + 'screen_height': -1, + 'screen_dpi': -1, + 'device_pixel_ratio': -1, + 'orientation': '', + 'screen_brightness': -1, + 'volume': -1, + 'is_screen_on': -1, + 'network_type': 'unknown', + 'battery_level': -1, + 'is_charging': -1, + 'battery_state': 'unknown', + 'cpu_cores': -1, + 'device_memory_gb': -1, + 'browser_name': '', + 'browser_version': '', + 'cookie_enabled': -1, + 'local_storage_enabled': -1, + 'touch_support': -1, + 'max_touch_points': -1, + 'color_depth': -1, + 'is_pwa': -1, + }; + + static Map _normalizeEnvironmentFields( + Map fields, + ) { + final normalized = Map.from(fields); + if (normalized.containsKey('device_pixel_ratio')) { + normalized['device_pixel_ratio'] = + _normalizeDevicePixelRatio(normalized['device_pixel_ratio']); + } + return normalized; + } + + static int _normalizeDevicePixelRatio(dynamic value) { + if (value == null) return -1; + final ratio = + value is num ? value.toDouble() : double.tryParse(value.toString()); + if (ratio == null || !ratio.isFinite) return -1; + if (ratio > 10) return ratio.round(); + return (ratio * 100).round(); + } + + static const Set _serverOnlyEvents = { + 'user_register', + 'user_login', + 'realtime_online', + 'order_created', + 'order_paid', + 'coin_consume', + 'video_status_change', + }; + + static const Set _reservedPayloadSignatureFields = { + 'sdk_type', + 'client_type', + 'id_type', + 'event_sig', + 'event_sig_version', + 'security_key_version', + 'sign_algorithm', + 'sign_ts', + }; + + static String _eventSignature( + Map event, + ) { + try { + if (_eventSignatureHmacKey.isEmpty) return ''; + final factors = [ + event['event']?.toString() ?? '', + event['event_id']?.toString() ?? '', + event['app_id']?.toString() ?? '', + event['client_ts']?.toString() ?? '', + event['device_fingerprint']?.toString() ?? '', + ]; + final signatureInput = factors.join('|'); + final signature = Hmac(sha256, utf8.encode(_eventSignatureHmacKey)) + .convert(utf8.encode(signatureInput)) + .toString(); + Logger.analyticsSdk( + 'event_sig input="$signatureInput" ' + 'event_sig=$signature ' + 'event_sig_version=$_eventSignatureVersion', + ); + return signature; + } catch (_) { + return ''; + } + } + + /// 从公共字段 Map 和业务特有字段列表生成 event_id。 + /// + /// 本方法永远不会抛出异常。 + static String generateEventIdFromCommonFields( + Map commonFields, List extraFields) { + return generateEventId([ + commonFields['event'] as String? ?? '', + commonFields['app_id'] as String? ?? '', + commonFields['channel'] as String? ?? '', + commonFields['uid'] as String? ?? '', + commonFields['device'] as String? ?? '', + commonFields['device_id'] as String? ?? '', + commonFields['device_brand'] as String? ?? '', + commonFields['device_model'] as String? ?? '', + (commonFields['client_ts'] ?? '').toString(), + commonFields['sid'] as String? ?? '', + ...extraFields, + ]); + } +} diff --git a/analytics_sdk/lib/manager/sdk_init_payload_builder.dart b/analytics_sdk/lib/manager/sdk_init_payload_builder.dart new file mode 100644 index 0000000..a3efad9 --- /dev/null +++ b/analytics_sdk/lib/manager/sdk_init_payload_builder.dart @@ -0,0 +1,239 @@ +import 'package:flutter/foundation.dart'; + +/// Builds the sdk_init payload from the SDK common fields plus platform-only +/// fields. Web and app clients intentionally carry different field families. +class SdkInitPayloadBuilder { + SdkInitPayloadBuilder._(); + + static Map build({ + required Map commonFields, + required bool isWeb, + Map platformFields = const {}, + }) { + final fields = { + ...commonFields, + ...platformFields, + }; + + final osFamily = _osFamily(_string(fields['system_name'], 'unknown')); + final payload = { + 'platform': isWeb ? 'web' : 'flutter', + 'os_family': osFamily, + 'device_type': _deviceType(_string(fields['device'], '')), + 'language': _string(fields['language'], 'unknown'), + 'timezone': _string(fields['timezone'], 'unknown'), + 'network_type': _string(fields['network_type'], 'unknown'), + 'screen_width': _int(fields['screen_width'], -1), + 'screen_height': _int(fields['screen_height'], -1), + 'screen_dpi': _int(fields['screen_dpi'], -1), + 'device_pixel_ratio': _devicePixelRatio(fields['device_pixel_ratio']), + 'orientation': _string(fields['orientation'], 'unknown'), + 'cpu_cores': _int(fields['cpu_cores'], -1), + 'device_memory_gb': _int(fields['device_memory_gb'], -1), + 'is_debug': kDebugMode ? 1 : 0, + 'flutter_build_mode': _string(fields['flutter_build_mode'], _buildMode()), + 'touch_support': _int(fields['touch_support'], -1), + 'max_touch_points': _int(fields['max_touch_points'], -1), + }; + + if (isWeb) { + payload.addAll(_webPayload(fields)); + } else if (osFamily == 'android') { + payload.addAll(_androidPayload(fields)); + } else if (osFamily == 'ios') { + payload.addAll(_iosPayload(fields)); + } else { + payload.addAll(_flutterPayload()); + } + + return payload; + } + + static Map _nativeSharedPayload( + Map fields, + ) { + return { + 'carrier': _string(fields['carrier'], ''), + 'mcc': _string(fields['mcc'], 'unknown'), + 'mnc': _string(fields['mnc'], 'unknown'), + 'has_sim': _int(fields['has_sim'], -1), + 'storage': _int(fields['storage'], -1), + 'available_storage': _int(fields['available_storage'], -1), + 'battery_level': _int(fields['battery_level'], -1), + 'is_charging': _int(fields['is_charging'], -1), + 'vpn_enabled': _int(fields['vpn_enabled'], -1), + 'proxy_enabled': _int(fields['proxy_enabled'], -1), + }; + } + + static Map _androidPayload(Map fields) { + return { + ..._nativeSharedPayload(fields), + 'activation_time': _int(fields['activation_time'], 0), + 'install_source': _string(fields['install_source'], ''), + 'android_id': _string(fields['android_id'], ''), + 'android_id_o': _string(fields['android_id_o'], ''), + 'android_id_status': _string(fields['android_id_status'], 'unknown'), + 'oaid': _string(fields['oaid'], ''), + 'oaid_o': _string(fields['oaid_o'], ''), + 'aaid': _string(fields['aaid'], ''), + 'aaid_o': _string(fields['aaid_o'], ''), + 'guid': _string(fields['guid'], ''), + 'guid_o': _string(fields['guid_o'], ''), + 'brand': + _string(fields['brand'], _string(fields['device_brand'], 'unknown')), + 'manufacturer': _string(fields['manufacturer'], 'unknown'), + 'model': + _string(fields['model'], _string(fields['device_model'], 'unknown')), + 'package_name': _string(fields['package_name'], 'unknown'), + 'abi': _string(fields['abi'], 'unknown'), + 'is_rooted': _int(fields['is_rooted'], -1), + 'usb_debug_enabled': _int(fields['usb_debug_enabled'], -1), + 'developer_options_enabled': + _int(fields['developer_options_enabled'], -1), + 'adb_enabled': _int(fields['adb_enabled'], -1), + 'has_baseband': _int(fields['has_baseband'], -1), + 'sensor_count_supported': _int(fields['sensor_count_supported'], -1), + 'has_accelerometer': _int(fields['has_accelerometer'], -1), + 'has_gyroscope': _int(fields['has_gyroscope'], -1), + 'has_magnetometer': _int(fields['has_magnetometer'], -1), + 'has_light_sensor': _int(fields['has_light_sensor'], -1), + 'has_proximity_sensor': _int(fields['has_proximity_sensor'], -1), + 'build_hardware': _string(fields['build_hardware'], 'unknown'), + 'build_product': _string(fields['build_product'], 'unknown'), + 'build_fingerprint': _string(fields['build_fingerprint'], 'unknown'), + }; + } + + static Map _iosPayload(Map fields) { + return { + ..._nativeSharedPayload(fields), + 'bundle_id': _string(fields['bundle_id'], ''), + 'ios_device_model_raw': _string(fields['ios_device_model_raw'], ''), + 'idfv': _string(fields['idfv'], ''), + 'idfa': _string(fields['idfa'], ''), + 'att_status': _string(fields['att_status'], 'unknown'), + 'lat': _int(fields['lat'], -1), + 'is_jailbroken': _int(fields['is_jailbroken'], -1), + }; + } + + static Map _flutterPayload() { + return const {}; + } + + static Map _webPayload(Map fields) { + return { + 'current_url': _string(fields['current_url'], ''), + 'current_domain': _string(fields['current_domain'], ''), + 'current_path': _string(fields['current_path'], ''), + 'current_query_hash': _string(fields['current_query_hash'], ''), + 'referer': _string(fields['referer'], ''), + 'referer_domain': _string(fields['referer_domain'], ''), + 'referer_path': _string(fields['referer_path'], ''), + 'browser_name': _string(fields['browser_name'], 'unknown'), + 'browser_version': _string(fields['browser_version'], 'unknown'), + 'browser_kernel': _string(fields['browser_kernel'], 'unknown'), + 'display_mode': _string(fields['display_mode'], 'unknown'), + 'visibility_state': _string(fields['visibility_state'], 'unknown'), + 'cookie_enabled': _int(fields['cookie_enabled'], -1), + 'local_storage_enabled': _int(fields['local_storage_enabled'], -1), + 'session_storage_enabled': _int(fields['session_storage_enabled'], -1), + 'indexeddb_enabled': _int(fields['indexeddb_enabled'], -1), + 'service_worker_enabled': _int(fields['service_worker_enabled'], -1), + 'connection_type': _string(fields['connection_type'], 'unknown'), + 'effective_type': _string(fields['effective_type'], 'unknown'), + 'downlink': _int(fields['downlink'], -1), + 'rtt': _int(fields['rtt'], -1), + 'canvas_fp': _string(fields['canvas_fp'], ''), + 'webgl_fp': _string(fields['webgl_fp'], ''), + 'webgl_vendor': _string(fields['webgl_vendor'], 'unknown'), + 'webgl_renderer': _string(fields['webgl_renderer'], 'unknown'), + 'webgl_status': _string(fields['webgl_status'], 'unknown'), + 'webgl_extensions_hash': _string(fields['webgl_extensions_hash'], ''), + 'webgl_params_hash': _string(fields['webgl_params_hash'], ''), + 'audio_sample_rate': _int(fields['audio_sample_rate'], -1), + 'fonts': _string(fields['fonts'], ''), + 'fonts_hash': _string(fields['fonts_hash'], ''), + 'font_count': _int(fields['font_count'], -1), + 'plugins': _string(fields['plugins'], ''), + 'plugins_hash': _string(fields['plugins_hash'], ''), + 'plugin_count': _int(fields['plugin_count'], -1), + 'mimes': _string(fields['mimes'], ''), + 'mimes_hash': _string(fields['mimes_hash'], ''), + 'mime_count': _int(fields['mime_count'], -1), + 'has_webdriver': _int(fields['has_webdriver'], 0), + 'hardware_concurrency': _int(fields['hardware_concurrency'], -1), + 'browser_platform': _string(fields['browser_platform'], 'unknown'), + 'color_depth': _int(fields['color_depth'], -1), + 'audio_fp': _string(fields['audio_fp'], ''), + 'viewport_width': _int(fields['viewport_width'], -1), + 'viewport_height': _int(fields['viewport_height'], -1), + 'is_headless': _int(fields['is_headless'], -1), + 'headless_reason': _string(fields['headless_reason'], ''), + }; + } + + static String _string(dynamic value, String fallback) { + if (value == null) return fallback; + final string = value.toString().trim(); + return string.isEmpty ? fallback : string; + } + + static int _int(dynamic value, int fallback) { + if (value is int) return value; + if (value is double && value.isFinite) return value.round(); + if (value is String) { + final asInt = int.tryParse(value.trim()); + if (asInt != null) return asInt; + final asDouble = double.tryParse(value.trim()); + if (asDouble != null && asDouble.isFinite) return asDouble.round(); + } + return fallback; + } + + static int _devicePixelRatio(dynamic value) { + if (value == null) return -1; + final ratio = + value is num ? value.toDouble() : double.tryParse(value.toString()); + if (ratio == null || !ratio.isFinite) return -1; + if (ratio > 10) return ratio.round(); + return (ratio * 100).round(); + } + + static String _deviceType(String device) { + switch (device.toLowerCase()) { + case 'android': + case 'ios': + return 'phone'; + case 'pc': + return 'desktop'; + default: + return 'unknown'; + } + } + + static String _osFamily(String systemName) { + switch (systemName.toLowerCase()) { + case 'android': + return 'android'; + case 'ios': + return 'ios'; + case 'macos': + return 'macos'; + case 'windows': + return 'windows'; + case 'linux': + return 'linux'; + default: + return 'unknown'; + } + } + + static String _buildMode() { + if (kReleaseMode) return 'release'; + if (kProfileMode) return 'profile'; + if (kDebugMode) return 'debug'; + return 'unknown'; + } +} diff --git a/analytics_sdk/lib/manager/sdk_init_platform_fields.dart b/analytics_sdk/lib/manager/sdk_init_platform_fields.dart new file mode 100644 index 0000000..637c7bc --- /dev/null +++ b/analytics_sdk/lib/manager/sdk_init_platform_fields.dart @@ -0,0 +1,3 @@ +export 'sdk_init_platform_fields_native.dart' + // ignore: uri_does_not_exist + if (dart.library.html) 'sdk_init_platform_fields_web.dart'; diff --git a/analytics_sdk/lib/manager/sdk_init_platform_fields_native.dart b/analytics_sdk/lib/manager/sdk_init_platform_fields_native.dart new file mode 100644 index 0000000..38a6472 --- /dev/null +++ b/analytics_sdk/lib/manager/sdk_init_platform_fields_native.dart @@ -0,0 +1,61 @@ +import 'dart:io'; + +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/services.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + +const MethodChannel _nativeChannel = MethodChannel('analytics_sdk'); + +Future> collectSdkInitPlatformFields() async { + final fields = {}; + + try { + final info = await PackageInfo.fromPlatform(); + fields['package_name'] = info.packageName; + fields['bundle_id'] = info.packageName; + } catch (_) {} + + try { + final plugin = DeviceInfoPlugin(); + if (Platform.isAndroid) { + final info = await plugin.androidInfo; + fields.addAll({ + 'brand': info.brand, + 'manufacturer': info.manufacturer, + 'model': info.model, + 'abi': info.supportedAbis.isNotEmpty + ? info.supportedAbis.first + : 'unknown', + 'is_rooted': -1, + 'build_hardware': info.hardware, + 'build_product': info.product, + 'build_fingerprint': info.fingerprint, + }); + } else if (Platform.isIOS) { + final info = await plugin.iosInfo; + fields.addAll({ + 'bundle_id': fields['bundle_id'] ?? '', + 'ios_device_model_raw': info.utsname.machine, + 'is_jailbroken': -1, + }); + } + } catch (_) {} + + fields.addAll(await _collectNativeChannelFields()); + + return fields; +} + +Future> _collectNativeChannelFields() async { + try { + final result = await _nativeChannel + .invokeMapMethod('getSdkInitPlatformFields'); + if (result == null) return const {}; + return Map.from(result) + ..removeWhere((key, value) => value == null); + } on MissingPluginException { + return const {}; + } catch (_) { + return const {}; + } +} diff --git a/analytics_sdk/lib/manager/sdk_init_platform_fields_web.dart b/analytics_sdk/lib/manager/sdk_init_platform_fields_web.dart new file mode 100644 index 0000000..4d1809e --- /dev/null +++ b/analytics_sdk/lib/manager/sdk_init_platform_fields_web.dart @@ -0,0 +1,540 @@ +// ignore: avoid_web_libraries_in_flutter +import 'dart:convert'; +import 'dart:html' as html; + +import 'package:crypto/crypto.dart'; +import 'package:analytics_sdk/utils/js_interop_compat.dart'; + +Future> collectSdkInitPlatformFields() async { + final navigator = html.window.navigator; + final screen = html.window.screen; + final uri = Uri.tryParse(html.window.location.href); + final refererUri = Uri.tryParse(html.document.referrer); + final connection = _connection(navigator); + final browser = _parseBrowser(navigator.userAgent); + final canvasFp = _canvasFingerprint(); + final webgl = _webglInfo(); + final audio = _audioInfo(); + final fonts = _fonts(); + final plugins = _plugins(navigator); + final mimes = _mimes(navigator); + final headless = _headlessReasons(navigator, plugins); + headless.addAll(webgl.headlessReasons); + if (fonts != null && fonts.isNotEmpty && fonts.length < 3) { + headless.add('fonts_low'); + } + + return { + 'current_url': _urlWithoutQueryAndFragment(uri), + 'current_domain': uri?.host ?? '', + 'current_path': uri?.path ?? '', + 'current_query_hash': _hash(uri?.query ?? ''), + 'referer': _urlWithoutQueryAndFragment(refererUri), + 'referer_domain': refererUri?.host ?? '', + 'referer_path': refererUri?.path ?? '', + 'browser_name': browser.name, + 'browser_version': browser.version, + 'browser_kernel': _browserKernel(navigator.userAgent), + 'display_mode': _displayMode(), + 'visibility_state': _nonEmpty(html.document.visibilityState, 'unknown'), + 'cookie_enabled': navigator.cookieEnabled == true ? 1 : 0, + 'local_storage_enabled': _storageEnabled(html.window.localStorage), + 'session_storage_enabled': _storageEnabled(html.window.sessionStorage), + 'indexeddb_enabled': _hasProperty(html.window, 'indexedDB'), + 'service_worker_enabled': _hasProperty(navigator, 'serviceWorker'), + 'connection_type': _string(connection, 'type', 'unknown'), + 'effective_type': _effectiveType(connection), + 'downlink': _downlinkKbps(connection), + 'rtt': _number(connection, 'rtt', -1).round(), + 'canvas_fp': canvasFp, + 'webgl_fp': webgl.fp, + 'webgl_vendor': webgl.vendor, + 'webgl_renderer': webgl.renderer, + 'webgl_status': webgl.status, + 'webgl_extensions_hash': webgl.extensionsHash, + 'webgl_params_hash': webgl.paramsHash, + 'audio_sample_rate': audio.sampleRate, + 'fonts': fonts?.join(',') ?? '', + 'fonts_hash': _hash(fonts?.join('|') ?? ''), + 'font_count': fonts?.length ?? -1, + 'plugins': plugins.join(','), + 'plugins_hash': _hash(plugins.join('|')), + 'plugin_count': plugins.length, + 'mimes': mimes.join(','), + 'mimes_hash': _hash(mimes.join('|')), + 'mime_count': mimes.length, + 'has_webdriver': navigator.webdriver == true ? 1 : 0, + 'hardware_concurrency': navigator.hardwareConcurrency ?? -1, + 'browser_platform': _nonEmpty(navigator.platform, 'unknown'), + 'color_depth': screen?.colorDepth ?? -1, + 'audio_fp': audio.fp, + 'viewport_width': html.window.innerWidth ?? -1, + 'viewport_height': html.window.innerHeight ?? -1, + 'touch_support': (navigator.maxTouchPoints ?? 0) > 0 ? 1 : 0, + 'max_touch_points': navigator.maxTouchPoints ?? -1, + 'is_headless': headless.isEmpty ? 0 : 1, + 'headless_reason': _unique(headless).join(','), + }; +} + +class _BrowserInfo { + final String name; + final String version; + + const _BrowserInfo(this.name, this.version); +} + +class _WebglInfo { + final String fp; + final String vendor; + final String renderer; + final String status; + final String extensionsHash; + final String paramsHash; + final List headlessReasons; + + const _WebglInfo({ + required this.fp, + required this.vendor, + required this.renderer, + required this.status, + required this.extensionsHash, + required this.paramsHash, + required this.headlessReasons, + }); +} + +class _AudioInfo { + final int sampleRate; + final String fp; + + const _AudioInfo(this.sampleRate, this.fp); +} + +String _urlWithoutQueryAndFragment(Uri? uri) { + if (uri == null) return ''; + final buffer = StringBuffer(); + if (uri.scheme.isNotEmpty) { + buffer + ..write(uri.scheme) + ..write('://'); + } + if (uri.host.isNotEmpty) { + buffer.write(uri.host); + } + if (uri.hasPort) { + buffer.write(':${uri.port}'); + } + if (uri.path.isNotEmpty) { + buffer.write(uri.path); + } + return buffer.toString(); +} + +Object? _connection(html.Navigator navigator) { + for (final key in ['connection', 'mozConnection', 'webkitConnection']) { + try { + if (hasJsProperty(navigator, key)) { + return getJsProperty(navigator, key); + } + } catch (_) {} + } + return null; +} + +_BrowserInfo _parseBrowser(String ua) { + final rules = { + 'Edge': RegExp(r'(?:Edg|EdgA|EdgiOS|Edge)/([\d.]+)'), + 'Opera': RegExp(r'(?:OPR|Opera)/([\d.]+)'), + 'Chrome': RegExp(r'(?:Chrome|CriOS)/([\d.]+)'), + 'Firefox': RegExp(r'(?:Firefox|FxiOS)/([\d.]+)'), + 'Safari': RegExp(r'Version/([\d.]+).*Safari'), + }; + for (final entry in rules.entries) { + final match = entry.value.firstMatch(ua); + if (match != null) { + return _BrowserInfo(entry.key, _nonEmpty(match.group(1), 'unknown')); + } + } + return const _BrowserInfo('unknown', 'unknown'); +} + +String _string(Object? object, String key, String fallback) { + if (object == null) return fallback; + try { + final value = getJsProperty(object, key); + final string = value?.toString().trim() ?? ''; + return string.isEmpty ? fallback : string; + } catch (_) { + return fallback; + } +} + +num _number(Object? object, String key, num fallback) { + if (object == null) return fallback; + try { + final value = getJsProperty(object, key); + if (value is num && value.isFinite) return value; + return num.tryParse(value?.toString() ?? '') ?? fallback; + } catch (_) { + return fallback; + } +} + +String _effectiveType(Object? connection) { + final value = _string(connection, 'effectiveType', 'unknown').toLowerCase(); + if (value == 'slow-2g') return '2g'; + const allowed = {'2g', '3g', '4g', '5g'}; + return allowed.contains(value) ? value : 'unknown'; +} + +int _downlinkKbps(Object? connection) { + final downlink = _number(connection, 'downlink', -1); + return downlink >= 0 ? (downlink * 1000).round() : -1; +} + +int _hasProperty(Object object, String key) { + try { + return hasJsProperty(object, key) ? 1 : 0; + } catch (_) { + return -1; + } +} + +int _storageEnabled(html.Storage storage) { + try { + const key = '__analytics_sdk_storage_test__'; + storage[key] = '1'; + final ok = storage[key] == '1'; + storage.remove(key); + return ok ? 1 : 0; + } catch (_) { + return 0; + } +} + +String _canvasFingerprint() { + try { + final canvas = html.CanvasElement(width: 240, height: 60); + final context = canvas.context2D; + context + ..textBaseline = 'top' + ..font = '14px Arial' + ..fillStyle = '#f60'; + context.fillRect(125, 1, 62, 20); + context + ..fillStyle = '#069' + ..fillText('analytics_sdk_canvas_fp', 2, 15) + ..fillStyle = 'rgba(102, 204, 0, 0.7)' + ..fillText('analytics_sdk_canvas_fp', 4, 17); + return _hash(canvas.toDataUrl()); + } catch (_) { + return ''; + } +} + +_WebglInfo _webglInfo() { + Object? gl; + try { + final canvas = html.CanvasElement(width: 1, height: 1); + gl = canvas.getContext('webgl') ?? canvas.getContext('experimental-webgl'); + } catch (_) { + return const _WebglInfo( + fp: '', + vendor: 'unknown', + renderer: 'unknown', + status: 'error', + extensionsHash: '', + paramsHash: '', + headlessReasons: ['webgl_unavailable'], + ); + } + + if (gl == null) { + return const _WebglInfo( + fp: '', + vendor: 'unknown', + renderer: 'unknown', + status: 'unsupported', + extensionsHash: '', + paramsHash: '', + headlessReasons: ['webgl_unavailable'], + ); + } + + try { + final debugInfo = callJsMethod(gl, 'getExtension', [ + 'WEBGL_debug_renderer_info', + ]); + final vendor = + _webglParameter(gl, debugInfo, 'UNMASKED_VENDOR_WEBGL') ?? + _webglParameterByConstant(gl, 0x1F00) ?? + 'unknown'; + final renderer = + _webglParameter(gl, debugInfo, 'UNMASKED_RENDERER_WEBGL') ?? + _webglParameterByConstant(gl, 0x1F01) ?? + 'unknown'; + final extensions = _jsStringArray( + callJsMethod(gl, 'getSupportedExtensions'), + )..sort(); + final params = _webglParams(gl); + final extensionsHash = _hash(extensions.join('|')); + final paramsHash = _hash(params.join('|')); + final fp = _hash( + [vendor, renderer, extensions.join('|'), params.join('|')].join('|'), + ); + + return _WebglInfo( + fp: fp, + vendor: vendor, + renderer: renderer, + status: 'available', + extensionsHash: extensionsHash, + paramsHash: paramsHash, + headlessReasons: const [], + ); + } catch (_) { + return const _WebglInfo( + fp: '', + vendor: 'unknown', + renderer: 'unknown', + status: 'error', + extensionsHash: '', + paramsHash: '', + headlessReasons: ['webgl_unavailable'], + ); + } +} + +String? _webglParameter(Object gl, Object? debugInfo, String key) { + if (debugInfo == null) return null; + try { + final parameter = getJsProperty(debugInfo, key); + if (parameter == null) return null; + final value = callJsMethod(gl, 'getParameter', [parameter]); + final string = _jsValueToString(value).trim(); + return string.isEmpty ? null : string; + } catch (_) { + return null; + } +} + +String? _webglParameterByConstant(Object gl, int parameter) { + try { + final value = callJsMethod(gl, 'getParameter', [parameter]); + final string = _jsValueToString(value).trim(); + return string.isEmpty ? null : string; + } catch (_) { + return null; + } +} + +List _webglParams(Object gl) { + const parameters = [ + 0x0D33, // MAX_TEXTURE_SIZE + 0x0D3A, // MAX_VIEWPORT_DIMS + 0x846D, // ALIASED_POINT_SIZE_RANGE + 0x846E, // ALIASED_LINE_WIDTH_RANGE + 0x84E8, // MAX_RENDERBUFFER_SIZE + 0x8869, // MAX_VERTEX_ATTRIBS + 0x8872, // MAX_TEXTURE_IMAGE_UNITS + 0x8B4C, // MAX_VERTEX_TEXTURE_IMAGE_UNITS + 0x8B8C, // SHADING_LANGUAGE_VERSION + ]; + final values = []; + for (final parameter in parameters) { + final value = _webglParameterByConstant(gl, parameter); + if (value != null) values.add('$parameter=$value'); + } + return values; +} + +_AudioInfo _audioInfo() { + Object? context; + try { + final constructor = + getJsProperty(html.window, 'AudioContext') ?? + getJsProperty(html.window, 'webkitAudioContext'); + if (constructor == null) return const _AudioInfo(-1, ''); + context = constructJsObject(constructor); + final sampleRate = _number(context, 'sampleRate', -1).round(); + final fp = sampleRate > 0 ? _hash('sampleRate:$sampleRate') : ''; + return _AudioInfo(sampleRate, fp); + } catch (_) { + return const _AudioInfo(-1, ''); + } finally { + if (context != null) { + try { + callJsMethod(context, 'close'); + } catch (_) {} + } + } +} + +List? _fonts() { + try { + final canvas = html.CanvasElement(width: 200, height: 50); + final context = canvas.context2D; + const text = 'mmmmmmmmmmlli'; + const baseFonts = ['monospace', 'serif', 'sans-serif']; + final baseline = {}; + for (final base in baseFonts) { + context.font = '72px $base'; + baseline[base] = context.measureText(text).width ?? -1; + } + + const candidates = [ + 'Arial', + 'Arial Black', + 'Calibri', + 'Cambria', + 'Comic Sans MS', + 'Courier New', + 'Georgia', + 'Helvetica', + 'Impact', + 'Menlo', + 'Microsoft YaHei', + 'Monaco', + 'Noto Sans', + 'PingFang SC', + 'Roboto', + 'San Francisco', + 'Segoe UI', + 'Tahoma', + 'Times New Roman', + 'Trebuchet MS', + 'Verdana', + ]; + + final detected = []; + for (final font in candidates) { + var found = false; + for (final base in baseFonts) { + context.font = '72px "$font", $base'; + final width = context.measureText(text).width ?? -1; + if (width != baseline[base]) { + found = true; + break; + } + } + if (found) detected.add(font); + } + return detected; + } catch (_) { + return null; + } +} + +List _plugins(html.Navigator navigator) { + try { + return _indexedPropertyList(getJsProperty(navigator, 'plugins'), 'name'); + } catch (_) { + return const []; + } +} + +List _mimes(html.Navigator navigator) { + try { + return _indexedPropertyList(getJsProperty(navigator, 'mimeTypes'), 'type'); + } catch (_) { + return const []; + } +} + +List _indexedPropertyList(Object? collection, String property) { + if (collection == null) return const []; + final length = _number(collection, 'length', 0).round(); + if (length <= 0) return const []; + final values = []; + for (var i = 0; i < length; i++) { + try { + final item = getJsProperty(collection, i); + if (item == null) continue; + final value = getJsProperty(item, property); + final string = value?.toString().trim() ?? ''; + if (string.isNotEmpty) values.add(string); + } catch (_) {} + } + return values; +} + +List _jsStringArray(Object? collection) { + if (collection == null) return const []; + final length = _number(collection, 'length', 0).round(); + if (length <= 0) return const []; + final values = []; + for (var i = 0; i < length; i++) { + try { + final value = getJsProperty(collection, i); + final string = value?.toString().trim() ?? ''; + if (string.isNotEmpty) values.add(string); + } catch (_) {} + } + return values; +} + +String _jsValueToString(Object? value) { + if (value == null) return ''; + if (value is String || value is num || value is bool) return '$value'; + try { + final values = _jsStringArray(value); + if (values.isNotEmpty) return values.join(','); + } catch (_) {} + return value.toString(); +} + +List _headlessReasons(html.Navigator navigator, List plugins) { + final reasons = []; + try { + if (navigator.webdriver == true) reasons.add('webdriver'); + } catch (_) {} + if (plugins.isEmpty) reasons.add('plugins_empty'); + try { + final width = html.window.innerWidth ?? 0; + final height = html.window.innerHeight ?? 0; + if (width <= 0 || height <= 0) reasons.add('viewport_zero'); + } catch (_) {} + return reasons; +} + +String _displayMode() { + const modes = ['standalone', 'minimal-ui', 'fullscreen', 'browser']; + for (final mode in modes) { + try { + if (html.window.matchMedia('(display-mode: $mode)').matches) { + return mode; + } + } catch (_) {} + } + return 'unknown'; +} + +String _browserKernel(String ua) { + if (ua.contains('Edge/')) return 'edgehtml'; + if (ua.contains('Edg/')) return 'chromium'; + if (ua.contains('OPR/') || ua.contains('Opera/')) return 'chromium'; + if (ua.contains('Chrome/') || ua.contains('Chromium/')) return 'chromium'; + if (ua.contains('Firefox/')) return 'gecko'; + if (ua.contains('Trident/')) return 'trident'; + if (ua.contains('Safari/')) return 'webkit'; + return 'unknown'; +} + +String _nonEmpty(String? value, String fallback) { + final string = value?.trim() ?? ''; + return string.isEmpty ? fallback : string; +} + +List _unique(List values) { + final seen = {}; + return [ + for (final value in values) + if (seen.add(value)) value, + ]; +} + +String _hash(String value) { + if (value.isEmpty) return ''; + return md5.convert(utf8.encode(value)).toString(); +} diff --git a/analytics_sdk/lib/manager/session_behavior_manager.dart b/analytics_sdk/lib/manager/session_behavior_manager.dart new file mode 100644 index 0000000..481f0d0 --- /dev/null +++ b/analytics_sdk/lib/manager/session_behavior_manager.dart @@ -0,0 +1,292 @@ +class SessionBehaviorManager { + static final SessionBehaviorManager instance = + SessionBehaviorManager._internal(); + + factory SessionBehaviorManager() => instance; + + SessionBehaviorManager._internal() : _nowProvider = DateTime.now; + + SessionBehaviorManager.forTest({ + DateTime Function()? nowProvider, + }) : _nowProvider = nowProvider ?? DateTime.now; + + static const int _scrollThresholdPx = 8; + static const int _bottomTolerancePx = 1; + static const _scrollMinInterval = Duration(milliseconds: 200); + static const _touchDedupWindow = Duration(milliseconds: 50); + + final DateTime Function() _nowProvider; + + String _sid = ''; + DateTime? _pageStartedAt; + int? _lastScrollTop; + DateTime? _lastScrollProcessedAt; + DateTime? _lastTouchStartAt; + DateTime? _lastSwipeAt; + DateTime? _lastLongPressAt; + DateTime? _lastPinchAt; + int _lastScrollDirection = 0; + + int _scrollTotalCount = 0; + int _scrollTotalDistancePx = 0; + int _scrollUpCount = 0; + int _scrollDownCount = 0; + int _scrollDirectionChangeCount = 0; + int _firstScrollDelayMs = -1; + int _maxScrollDepthPercent = -1; + int _isReachBottom = -1; + int _contentHeight = -1; + int _touchTotalCount = 0; + int _swipeTotalCount = 0; + int _longPressTotalCount = 0; + int _pinchTotalCount = 0; + int _firstInteractionDelayMs = -1; + + void reset({String sid = ''}) { + _sid = sid; + _pageStartedAt = _nowProvider(); + _lastScrollTop = null; + _lastScrollProcessedAt = null; + _lastTouchStartAt = null; + _lastSwipeAt = null; + _lastLongPressAt = null; + _lastPinchAt = null; + _lastScrollDirection = 0; + + _scrollTotalCount = 0; + _scrollTotalDistancePx = 0; + _scrollUpCount = 0; + _scrollDownCount = 0; + _scrollDirectionChangeCount = 0; + _firstScrollDelayMs = -1; + _maxScrollDepthPercent = -1; + _isReachBottom = -1; + _contentHeight = -1; + _touchTotalCount = 0; + _swipeTotalCount = 0; + _longPressTotalCount = 0; + _pinchTotalCount = 0; + _firstInteractionDelayMs = -1; + } + + void recordAcceptedEvent(Map event) { + final eventName = event['event']?.toString() ?? ''; + if (eventName.isEmpty || eventName == 'app_session_summary') return; + + final sid = event['sid']?.toString() ?? ''; + if (sid != _sid) { + reset(sid: sid); + } + + if (eventName == 'app_page_view') { + recordPageEnter(_pageEnterTime(event) ?? _nowProvider()); + } + } + + void recordPageEnter(DateTime enteredAt) { + _pageStartedAt = enteredAt; + _lastScrollTop = null; + _lastScrollProcessedAt = null; + _lastScrollDirection = 0; + } + + void recordScroll({ + required int scrollTop, + required int viewportHeight, + required int contentHeight, + DateTime? at, + }) { + final observedAt = at ?? _nowProvider(); + final normalizedScrollTop = _nonNegative(scrollTop); + final normalizedViewportHeight = _nonNegative(viewportHeight); + final normalizedContentHeight = _nonNegative(contentHeight); + + _updateScrollExtent( + scrollTop: normalizedScrollTop, + viewportHeight: normalizedViewportHeight, + contentHeight: normalizedContentHeight, + ); + + final previousScrollTop = _lastScrollTop; + if (previousScrollTop == null) { + _lastScrollTop = normalizedScrollTop; + _lastScrollProcessedAt = observedAt; + return; + } + + final previousProcessedAt = _lastScrollProcessedAt; + if (previousProcessedAt != null && + observedAt.difference(previousProcessedAt) < _scrollMinInterval) { + return; + } + + final delta = normalizedScrollTop - previousScrollTop; + final distance = delta.abs(); + if (distance < _scrollThresholdPx) { + _lastScrollTop = normalizedScrollTop; + _lastScrollProcessedAt = observedAt; + return; + } + + final direction = delta > 0 ? 1 : -1; + _scrollTotalCount += 1; + _scrollTotalDistancePx += distance; + if (direction > 0) { + _scrollDownCount += 1; + } else { + _scrollUpCount += 1; + } + if (_lastScrollDirection != 0 && _lastScrollDirection != direction) { + _scrollDirectionChangeCount += 1; + } + _lastScrollDirection = direction; + _lastScrollTop = normalizedScrollTop; + _lastScrollProcessedAt = observedAt; + _recordFirstScrollDelay(observedAt); + } + + void recordTouchStart({DateTime? at}) { + final observedAt = at ?? _nowProvider(); + if (_isDuplicate(_lastTouchStartAt, observedAt)) return; + _lastTouchStartAt = observedAt; + _touchTotalCount += 1; + _recordFirstInteractionDelay(observedAt); + } + + void recordSwipe({DateTime? at}) { + final observedAt = at ?? _nowProvider(); + if (_isDuplicate(_lastSwipeAt, observedAt)) return; + _lastSwipeAt = observedAt; + _swipeTotalCount += 1; + _recordFirstInteractionDelay(observedAt); + } + + void recordLongPress({DateTime? at}) { + final observedAt = at ?? _nowProvider(); + if (_isDuplicate(_lastLongPressAt, observedAt)) return; + _lastLongPressAt = observedAt; + _longPressTotalCount += 1; + _recordFirstInteractionDelay(observedAt); + } + + void recordPinch({DateTime? at}) { + final observedAt = at ?? _nowProvider(); + if (_isDuplicate(_lastPinchAt, observedAt)) return; + _lastPinchAt = observedAt; + _pinchTotalCount += 1; + _recordFirstInteractionDelay(observedAt); + } + + SessionBehaviorSnapshot snapshot() { + return SessionBehaviorSnapshot( + scrollTotalCount: _scrollTotalCount, + scrollTotalDistancePx: _scrollTotalDistancePx, + scrollUpCount: _scrollUpCount, + scrollDownCount: _scrollDownCount, + scrollDirectionChangeCount: _scrollDirectionChangeCount, + firstScrollDelayMs: _firstScrollDelayMs, + maxScrollDepthPercent: _maxScrollDepthPercent, + isReachBottom: _isReachBottom, + contentHeight: _contentHeight, + touchTotalCount: _touchTotalCount, + swipeTotalCount: _swipeTotalCount, + longPressTotalCount: _longPressTotalCount, + pinchTotalCount: _pinchTotalCount, + firstInteractionDelayMs: _firstInteractionDelayMs, + ); + } + + void _updateScrollExtent({ + required int scrollTop, + required int viewportHeight, + required int contentHeight, + }) { + if (contentHeight <= 0 || viewportHeight <= 0) return; + _contentHeight = contentHeight; + final depth = ((scrollTop + viewportHeight) / contentHeight * 100) + .round() + .clamp(0, 100); + if (_maxScrollDepthPercent < 0 || depth > _maxScrollDepthPercent) { + _maxScrollDepthPercent = depth; + } + if (scrollTop + viewportHeight >= contentHeight - _bottomTolerancePx) { + _isReachBottom = 1; + } else if (_isReachBottom < 0) { + _isReachBottom = 0; + } + } + + void _recordFirstScrollDelay(DateTime at) { + if (_firstScrollDelayMs >= 0) return; + _firstScrollDelayMs = _delaySincePageStart(at); + } + + void _recordFirstInteractionDelay(DateTime at) { + if (_firstInteractionDelayMs >= 0) return; + _firstInteractionDelayMs = _delaySincePageStart(at); + } + + int _delaySincePageStart(DateTime at) { + final pageStartedAt = _pageStartedAt; + if (pageStartedAt == null) return -1; + final delay = at.difference(pageStartedAt).inMilliseconds; + return delay < 0 ? 0 : delay; + } + + DateTime? _pageEnterTime(Map event) { + final payload = event['payload']; + if (payload is! Map) return null; + final value = payload['page_enter_ts']; + if (value is int) { + return DateTime.fromMillisecondsSinceEpoch(value * 1000); + } + if (value is num) { + return DateTime.fromMillisecondsSinceEpoch(value.round() * 1000); + } + final parsed = int.tryParse(value?.toString() ?? ''); + if (parsed == null) return null; + return DateTime.fromMillisecondsSinceEpoch(parsed * 1000); + } + + bool _isDuplicate(DateTime? previous, DateTime current) { + if (previous == null) return false; + final diffMs = current.difference(previous).inMilliseconds.abs(); + return diffMs < _touchDedupWindow.inMilliseconds; + } + + int _nonNegative(int value) => value < 0 ? 0 : value; +} + +class SessionBehaviorSnapshot { + final int scrollTotalCount; + final int scrollTotalDistancePx; + final int scrollUpCount; + final int scrollDownCount; + final int scrollDirectionChangeCount; + final int firstScrollDelayMs; + final int maxScrollDepthPercent; + final int isReachBottom; + final int contentHeight; + final int touchTotalCount; + final int swipeTotalCount; + final int longPressTotalCount; + final int pinchTotalCount; + final int firstInteractionDelayMs; + + const SessionBehaviorSnapshot({ + required this.scrollTotalCount, + required this.scrollTotalDistancePx, + required this.scrollUpCount, + required this.scrollDownCount, + required this.scrollDirectionChangeCount, + required this.firstScrollDelayMs, + required this.maxScrollDepthPercent, + required this.isReachBottom, + required this.contentHeight, + required this.touchTotalCount, + required this.swipeTotalCount, + required this.longPressTotalCount, + required this.pinchTotalCount, + required this.firstInteractionDelayMs, + }); +} diff --git a/analytics_sdk/lib/manager/session_behavior_monitor.dart b/analytics_sdk/lib/manager/session_behavior_monitor.dart new file mode 100644 index 0000000..cc0b65d --- /dev/null +++ b/analytics_sdk/lib/manager/session_behavior_monitor.dart @@ -0,0 +1,2 @@ +export 'session_behavior_monitor_stub.dart' + if (dart.library.html) 'session_behavior_monitor_web.dart'; diff --git a/analytics_sdk/lib/manager/session_behavior_monitor_stub.dart b/analytics_sdk/lib/manager/session_behavior_monitor_stub.dart new file mode 100644 index 0000000..b8e0d01 --- /dev/null +++ b/analytics_sdk/lib/manager/session_behavior_monitor_stub.dart @@ -0,0 +1,10 @@ +class SessionBehaviorMonitor { + static final SessionBehaviorMonitor instance = + SessionBehaviorMonitor._internal(); + + SessionBehaviorMonitor._internal(); + + void start() {} + + void dispose() {} +} diff --git a/analytics_sdk/lib/manager/session_behavior_monitor_web.dart b/analytics_sdk/lib/manager/session_behavior_monitor_web.dart new file mode 100644 index 0000000..1dd8a7a --- /dev/null +++ b/analytics_sdk/lib/manager/session_behavior_monitor_web.dart @@ -0,0 +1,195 @@ +// ignore: avoid_web_libraries_in_flutter +import 'dart:async'; +import 'dart:html' as html; +import 'dart:math' as math; + +import 'package:analytics_sdk/manager/session_behavior_manager.dart'; + +class SessionBehaviorMonitor { + static final SessionBehaviorMonitor instance = + SessionBehaviorMonitor._internal(); + + SessionBehaviorMonitor._internal(); + + static const int _swipeThresholdPx = 50; + static const int _pinchThresholdPx = 20; + static const int _longPressMoveTolerancePx = 10; + static const _longPressDuration = Duration(milliseconds: 500); + + final List> _subscriptions = []; + final Map _touchStarts = {}; + final Map> _activeTouches = >{}; + + double? _pinchStartDistance; + bool _pinchRecorded = false; + bool _started = false; + + void start() { + if (_started) return; + _started = true; + _subscriptions + ..add(html.window.onScroll.listen(_handleScroll)) + ..add(html.document.onTouchStart.listen(_handleTouchStart)) + ..add(html.document.onTouchMove.listen(_handleTouchMove)) + ..add(html.document.onTouchEnd.listen(_handleTouchEnd)) + ..add(html.document.onTouchCancel.listen(_handleTouchCancel)); + _handleScroll(null); + } + + void dispose() { + for (final subscription in _subscriptions) { + subscription.cancel(); + } + _subscriptions.clear(); + _touchStarts.clear(); + _activeTouches.clear(); + _pinchStartDistance = null; + _pinchRecorded = false; + _started = false; + } + + void _handleScroll(Object? _) { + try { + final element = html.document.scrollingElement ?? + html.document.documentElement ?? + html.document.body; + final viewportHeight = html.window.innerHeight ?? element?.clientHeight; + final contentHeight = element?.scrollHeight; + final scrollTop = element?.scrollTop ?? html.window.pageYOffset.round(); + if (viewportHeight == null || contentHeight == null) return; + SessionBehaviorManager.instance.recordScroll( + scrollTop: scrollTop, + viewportHeight: viewportHeight, + contentHeight: contentHeight, + ); + } catch (_) {} + } + + void _handleTouchStart(html.TouchEvent event) { + try { + final touches = event.touches; + final changedTouches = event.changedTouches; + if (touches == null || changedTouches == null) return; + + final now = DateTime.now(); + if (touches.length == 1) { + SessionBehaviorManager.instance.recordTouchStart(at: now); + } + + for (final touch in changedTouches) { + final identifier = touch.identifier ?? 0; + final point = touch.client; + _touchStarts[identifier] = _TouchStart(point: point, startedAt: now); + _activeTouches[identifier] = point; + } + _updatePinchBaseline(); + } catch (_) {} + } + + void _handleTouchMove(html.TouchEvent event) { + try { + final touches = event.touches; + if (touches == null) return; + for (final touch in touches) { + _activeTouches[touch.identifier ?? 0] = touch.client; + } + if (_activeTouches.length < 2) return; + final distance = _firstTwoTouchDistance(); + if (distance == null) return; + _pinchStartDistance ??= distance; + if (!_pinchRecorded && + (distance - _pinchStartDistance!).abs() >= _pinchThresholdPx) { + _pinchRecorded = true; + SessionBehaviorManager.instance.recordPinch(); + } + } catch (_) {} + } + + void _handleTouchEnd(html.TouchEvent event) { + try { + final changedTouches = event.changedTouches; + if (changedTouches == null) return; + final now = DateTime.now(); + final wasSingleTouch = _activeTouches.length == 1; + for (final touch in changedTouches) { + final identifier = touch.identifier ?? 0; + final start = _touchStarts[identifier]; + if (start != null && wasSingleTouch) { + _recordFinishedSingleTouch( + start: start, + endPoint: touch.client, + endedAt: now, + ); + } + _touchStarts.remove(identifier); + _activeTouches.remove(identifier); + } + _updatePinchBaseline(); + } catch (_) {} + } + + void _handleTouchCancel(html.TouchEvent event) { + try { + final changedTouches = event.changedTouches; + if (changedTouches == null) return; + for (final touch in changedTouches) { + final identifier = touch.identifier ?? 0; + _touchStarts.remove(identifier); + _activeTouches.remove(identifier); + } + _updatePinchBaseline(); + } catch (_) {} + } + + void _recordFinishedSingleTouch({ + required _TouchStart start, + required math.Point endPoint, + required DateTime endedAt, + }) { + final distance = _distance(start.point, endPoint); + if (distance >= _swipeThresholdPx) { + SessionBehaviorManager.instance.recordSwipe(at: endedAt); + return; + } + if (endedAt.difference(start.startedAt) >= _longPressDuration && + distance <= _longPressMoveTolerancePx) { + SessionBehaviorManager.instance.recordLongPress(at: endedAt); + } + } + + void _updatePinchBaseline() { + if (_activeTouches.length < 2) { + _pinchStartDistance = null; + _pinchRecorded = false; + return; + } + _pinchStartDistance = _firstTwoTouchDistance(); + _pinchRecorded = false; + } + + double? _firstTwoTouchDistance() { + if (_activeTouches.length < 2) return null; + final iterator = _activeTouches.values.iterator; + iterator.moveNext(); + final first = iterator.current; + iterator.moveNext(); + final second = iterator.current; + return _distance(first, second); + } + + double _distance(math.Point a, math.Point b) { + final dx = a.x - b.x; + final dy = a.y - b.y; + return math.sqrt(dx * dx + dy * dy); + } +} + +class _TouchStart { + final math.Point point; + final DateTime startedAt; + + const _TouchStart({ + required this.point, + required this.startedAt, + }); +} diff --git a/analytics_sdk/lib/manager/session_manager.dart b/analytics_sdk/lib/manager/session_manager.dart new file mode 100644 index 0000000..1ec848d --- /dev/null +++ b/analytics_sdk/lib/manager/session_manager.dart @@ -0,0 +1,316 @@ +import 'package:flutter/foundation.dart'; +import 'package:analytics_sdk/utils/logger.dart'; +import 'package:analytics_sdk/utils/uuid_util.dart'; + +/// 会话管理器:管理会话ID(sid)的生成和更新 +/// +/// 会话定义:用户在一次连续、自然的产品使用过程中产生的一组行为集合 +/// +/// **sid 说明**:sid 由客户端 SDK 自动生成,每个事件均会携带。 +/// 服务端上报事件时,需通过 [AnalyticsSdk.getParams] 获取当前 sid 并一同回传, +/// 以便服务端事件与客户端事件归属同一会话。 +/// +/// 会话中断阈值:30 分钟无任何有效行为,生成新会话ID +/// +/// APP产品定义方案,任意满足一条就重新生成会话ID: +/// 1. App 冷启动(进程首次启动) +/// 2. App 从后台切换前台,且后台停留 > 30 分钟 +/// 3. 上一次行为距离当前 > 30 分钟 +class SessionManager { + static final SessionManager instance = SessionManager._internal(); + + factory SessionManager() => instance; + + SessionManager._internal(); + + /// 当前会话ID + String? _currentSessionId; + + /// 会话创建时间 + DateTime? _sessionStartTime; + + /// 最后一次行为时间 + DateTime? _lastActivityTime; + + /// 应用进入后台的时间 + DateTime? _backgroundTime; + + /// 当前正在通知结束的旧 session 逻辑结束时间。 + DateTime? _notifyingSessionEndTime; + + /// 会话中断阈值(30分钟) + static const Duration sessionTimeout = Duration(minutes: 30); + + static const Set _effectiveLifecycleKeys = { + 'cold_start', + 'web_load', + 'warm_start', + 'foreground', + }; + + static const Set _nonBusinessActivityEvents = { + 'sdk_init', + 'network_state_change', + 'app_session_summary', + 'navigation', + 'page_lifecycle', + }; + + /// 是否已初始化(用于判断冷启动) + bool _isInitialized = false; + + /// 新 session 创建时的回调(由外部注入,避免直接依赖 AdImpressionManager) + /// 通常由 AnalyticsSdk.init() 注入:`() => AdImpressionManager.instance.clear()` + void Function()? onNewSession; + + /// 旧 session 即将结束时的回调。 + /// + /// 在新 sid 生成前调用,使调用方仍能用旧 sid 补发 app_session_summary。 + void Function(String sessionEndReason)? onSessionEnding; + + /// 获取当前会话ID + /// 如果当前没有会话或会话已过期,会自动创建新会话 + String getSessionId() { + final now = DateTime.now(); + + // 检查是否需要生成新会话 + // 注意:这里只检查和创建会话,不更新 _lastActivityTime + // 调用方应通过 recordActivity() 显式记录行为时间,避免非行为调用(如 debug 面板)误延长会话 + if (_shouldCreateNewSession(now)) { + _createNewSession(now); + } + + // 安全检查:确保会话ID不为null(理论上不会发生,但作为保护措施) + if (_currentSessionId == null) { + Logger.analyticsSdk('警告:会话ID为null,强制创建新会话', level: LogLevel.warn); + _createNewSession(now); + } + + return _currentSessionId!; + } + + /// 检查是否需要创建新会话 + bool _shouldCreateNewSession(DateTime now) { + // 1. 如果还没有会话ID,需要创建 + if (_currentSessionId == null) { + return true; + } + + // 2. 如果还未初始化,说明是冷启动,需要创建新会话 + if (!_isInitialized) { + return true; + } + + // 3. 如果从后台恢复,且后台停留时间 > 30 分钟 + // 注意:这里只在超时时清空 _backgroundTime(并返回 true 触发新会话)。 + // 非超时分支不清空,保留给 onAppForeground() 统一处理, + // 避免后台期间有代码调用 getSessionId()(如推送通知触发 track())时 + // 提前清空导致 onAppForeground() 无法正确判断后台时长。 + if (_backgroundTime != null) { + final backgroundDuration = now.difference(_backgroundTime!); + if (backgroundDuration > sessionTimeout) { + Logger.analyticsSdk( + '后台停留时间 ${backgroundDuration.inMinutes} 分钟,超过阈值,创建新会话'); + _backgroundTime = null; + return true; + } + // 未超时:不清空 _backgroundTime,由 onAppForeground() 负责清空 + } + + // 4. 如果上一次行为距离当前 > 30 分钟 + if (_lastActivityTime != null) { + final timeSinceLastActivity = now.difference(_lastActivityTime!); + if (timeSinceLastActivity > sessionTimeout) { + Logger.analyticsSdk( + '距离上次行为 ${timeSinceLastActivity.inMinutes} 分钟,超过阈值,创建新会话'); + return true; + } + } + + return false; + } + + /// 创建新会话 + void _createNewSession(DateTime now) { + try { + _notifySessionEndingIfNeeded('idle'); + _currentSessionId = generateUuidV4().replaceAll("-", ""); + _sessionStartTime = now; + _lastActivityTime = now; + _isInitialized = true; + // 新 session 开始时通知外部清理广告去重状态 + onNewSession?.call(); + Logger.analyticsSdk('创建新会话ID: $_currentSessionId'); + } catch (e) { + // UUID 生成失败时使用时间戳作为备选方案 + final timestamp = now.millisecondsSinceEpoch; + _currentSessionId = 'session_${timestamp}_${now.microsecondsSinceEpoch}'; + _sessionStartTime = now; + _lastActivityTime = now; + _isInitialized = true; + onNewSession?.call(); + Logger.analyticsSdk('UUID 生成失败,使用时间戳创建会话ID: $_currentSessionId'); + } + } + + /// 初始化会话管理器 + /// 在 SDK 初始化时调用,标记为冷启动 + void initialize() { + if (!_isInitialized) { + final now = DateTime.now(); + _createNewSession(now); + Logger.analyticsSdk('会话管理器初始化(冷启动),创建新会话'); + } + } + + /// 应用进入后台 + /// 在应用生命周期变为后台时调用 + void onAppBackground() { + _backgroundTime = DateTime.now(); + Logger.analyticsSdk('应用进入后台,记录时间: $_backgroundTime'); + } + + /// 应用从后台恢复 + /// 在应用生命周期变为前台时调用 + void onAppForeground() { + final now = DateTime.now(); + + // 如果从后台恢复,检查是否需要创建新会话 + if (_backgroundTime != null) { + final backgroundDuration = now.difference(_backgroundTime!); + if (backgroundDuration > sessionTimeout) { + Logger.analyticsSdk( + '从后台恢复,后台停留 ${backgroundDuration.inMinutes} 分钟,超过阈值,创建新会话'); + _createNewSession(now); + } else { + Logger.analyticsSdk( + '从后台恢复,后台停留 ${backgroundDuration.inMinutes} 分钟,继续使用当前会话'); + // 更新最后行为时间,表示用户重新活跃 + _lastActivityTime = now; + } + _backgroundTime = null; // 清空后台时间 + } else { + // 如果没有后台时间记录,直接更新最后行为时间 + _lastActivityTime = now; + } + } + + /// 记录用户行为 + /// 每次生成事件时调用,用于更新最后行为时间 + void recordActivity() { + _lastActivityTime = DateTime.now(); + } + + /// 仅在成功进入 SDK 队列且属于有效行为时更新最后行为时间。 + void recordActivityForEvent(Map event, {DateTime? at}) { + if (!_isEffectiveActivityEvent(event)) return; + _lastActivityTime = at ?? DateTime.now(); + } + + /// 获取当前会话ID(不触发新会话创建) + String? get currentSessionId => _currentSessionId; + + /// 获取会话开始时间 + DateTime? get sessionStartTime => _sessionStartTime; + + /// 获取最后行为时间 + DateTime? get lastActivityTime => _lastActivityTime; + + /// 当前 session 结束回调内可读取的逻辑结束时间。 + DateTime? get sessionEndTimeForNotification => _notifyingSessionEndTime; + + /// 仅供测试使用:获取后台时间(验证 _shouldCreateNewSession 不提前清空) + @visibleForTesting + DateTime? get backgroundTimeForTest => _backgroundTime; + + @visibleForTesting + void setBackgroundTimeForTest(DateTime? value) { + _backgroundTime = value; + } + + @visibleForTesting + DateTime? get sessionEndTimeForNotificationForTest => + _notifyingSessionEndTime; + + @visibleForTesting + void setLastActivityTimeForTest(DateTime? value) { + _lastActivityTime = value; + } + + @visibleForTesting + void setSessionStartTimeForTest(DateTime? value) { + _sessionStartTime = value; + } + + /// QA 调试用:把后台停留时间虚拟为指定时长,便于手工触发 idle summary。 + /// + /// Release 包中不生效,避免接入方误用影响真实 session 规则。 + bool debugSetBackgroundElapsedForQa(Duration elapsed) { + if (kReleaseMode) return false; + if (elapsed <= Duration.zero) return false; + final now = DateTime.now(); + _backgroundTime = now.subtract(elapsed); + _lastActivityTime = now.subtract(elapsed); + return true; + } + + /// 手动重置会话(用于测试或特殊场景) + void reset() { + _currentSessionId = null; + _sessionStartTime = null; + _lastActivityTime = null; + _backgroundTime = null; + _notifyingSessionEndTime = null; + _isInitialized = false; + Logger.analyticsSdk('会话已重置'); + } + + void _notifySessionEndingIfNeeded(String reason) { + if (_currentSessionId == null || !_isInitialized) return; + final previousEndTime = _notifyingSessionEndTime; + _notifyingSessionEndTime = _logicalSessionEndTime( + reason: reason, + now: DateTime.now(), + ); + try { + onSessionEnding?.call(reason); + } catch (e) { + Logger.analyticsSdk('旧 session 结束回调执行失败: $e', level: LogLevel.warn); + } finally { + _notifyingSessionEndTime = previousEndTime; + } + } + + DateTime _logicalSessionEndTime({ + required String reason, + required DateTime now, + }) { + if (reason != 'idle') return now; + final sessionStart = _sessionStartTime ?? now; + final rawLastActivity = _lastActivityTime ?? sessionStart; + final lastActivity = + rawLastActivity.isBefore(sessionStart) ? sessionStart : rawLastActivity; + final logicalEnd = lastActivity.add(sessionTimeout); + return logicalEnd.isAfter(now) ? now : logicalEnd; + } + + bool _isEffectiveActivityEvent(Map event) { + final eventName = event['event']?.toString() ?? ''; + if (eventName.isEmpty) return false; + if (eventName == 'app_lifecycle') { + final key = _lifecycleEventKey(event); + return _effectiveLifecycleKeys.contains(key); + } + if (_nonBusinessActivityEvents.contains(eventName)) return false; + return true; + } + + String _lifecycleEventKey(Map event) { + final payload = event['payload']; + if (payload is Map) { + final value = payload['lifecycle_event_key']?.toString().trim() ?? ''; + if (value.isNotEmpty) return value; + } + return event['lifecycle_event_key']?.toString().trim() ?? ''; + } +} diff --git a/analytics_sdk/lib/manager/session_metrics_manager.dart b/analytics_sdk/lib/manager/session_metrics_manager.dart new file mode 100644 index 0000000..5b0cfef --- /dev/null +++ b/analytics_sdk/lib/manager/session_metrics_manager.dart @@ -0,0 +1,300 @@ +import 'dart:convert'; + +import 'package:crypto/crypto.dart'; + +/// Session 内自动汇总指标。 +/// +/// 只记录成功进入 SDK 队列的事件。这里不采集滚动轨迹、键盘输入内容、 +/// 长按、双指缩放等高风险/不稳定指标,避免为了汇总事件扩大监听面。 +class SessionMetricsManager { + static final SessionMetricsManager instance = + SessionMetricsManager._internal(); + + factory SessionMetricsManager() => instance; + + SessionMetricsManager._internal(); + + static const int _maxBehaviorSequence = 200; + static const int _maxPageStayRecords = 100; + + static const Set _nonBusinessEvents = { + 'sdk_init', + 'app_lifecycle', + 'network_state_change', + 'app_session_summary', + 'app_page_view', + 'page_click', + 'navigation', + 'page_lifecycle', + }; + + String _sid = ''; + int _eventCountTotal = 0; + int _businessEventCount = 0; + int _foregroundCount = 0; + int _backgroundCountInSid = 0; + int _pageViewCount = 0; + int _pageClickCount = 0; + int _networkChangeCount = 0; + String _lastEventName = ''; + final Set _pageKeys = {}; + final Set _pagesWithInteraction = {}; + final List _behaviorSequence = []; + final List<_PageStayRecord> _activePageStays = <_PageStayRecord>[]; + final List<_PageStayRecord> _completedPageStays = <_PageStayRecord>[]; + + void recordAcceptedEvent(Map event) { + final eventName = event['event']?.toString() ?? ''; + if (eventName.isEmpty || eventName == 'app_session_summary') return; + + final sid = event['sid']?.toString() ?? ''; + if (sid != _sid) { + reset(sid: sid); + } + + _eventCountTotal += 1; + _lastEventName = eventName; + _recordBehaviorSequence(eventName, _payloadPageKey(event)); + + if (!_nonBusinessEvents.contains(eventName)) { + _businessEventCount += 1; + } + + if (eventName == 'app_page_view') { + _pageViewCount += 1; + _recordPageKey(event); + } else if (eventName == 'page_click') { + _pageClickCount += 1; + _recordPageKey(event); + final pageKey = _payloadPageKey(event); + if (pageKey.isNotEmpty) _pagesWithInteraction.add(pageKey); + } else if (eventName == 'app_lifecycle') { + final key = _payloadString(event, 'lifecycle_event_key', + fallback: event['lifecycle_event_key']); + if (key == 'foreground') { + _foregroundCount += 1; + } else if (key == 'background') { + _backgroundCountInSid += 1; + } + } else if (eventName == 'network_state_change') { + _networkChangeCount += 1; + } + } + + void recordPageEnter({ + required String pageKey, + required String pageName, + required DateTime enterTime, + }) { + final normalizedPageKey = pageKey.trim(); + if (normalizedPageKey.isEmpty) return; + _pageKeys.add(normalizedPageKey); + _closeActivePageStays(exitTime: enterTime); + _activePageStays.add(_PageStayRecord( + pageKey: normalizedPageKey, + pageName: pageName, + enterTime: enterTime, + )); + } + + void recordPageExit({ + required String pageKey, + required String pageName, + required DateTime enterTime, + required DateTime exitTime, + }) { + final normalizedPageKey = pageKey.trim(); + if (normalizedPageKey.isEmpty) return; + _pageKeys.add(normalizedPageKey); + + final index = _findActivePageStayIndex(normalizedPageKey, enterTime); + if (index < 0) return; + final record = _activePageStays.removeAt(index); + _completedPageStays.add(record.close( + exitTime: exitTime, + hasInteraction: _pagesWithInteraction.contains(normalizedPageKey), + )); + _trimCompletedPageStays(); + } + + SessionMetricsSnapshot snapshot({DateTime? endedAt}) { + final now = endedAt ?? DateTime.now(); + return SessionMetricsSnapshot( + eventCountTotal: _eventCountTotal, + businessEventCount: _businessEventCount, + foregroundCount: _foregroundCount, + backgroundCountInSid: _backgroundCountInSid, + pageCount: _pageKeys.length, + pageViewCount: _pageViewCount, + pageClickCount: _pageClickCount, + networkChangeCount: _networkChangeCount, + sessionSid: _sid, + lastEventName: _lastEventName, + pageStayList: _pageStayList(now), + behaviorSequenceHash: _behaviorSequenceHash(), + ); + } + + void reset({String sid = ''}) { + _sid = sid; + _eventCountTotal = 0; + _businessEventCount = 0; + _foregroundCount = 0; + _backgroundCountInSid = 0; + _pageViewCount = 0; + _pageClickCount = 0; + _networkChangeCount = 0; + _lastEventName = ''; + _pageKeys.clear(); + _pagesWithInteraction.clear(); + _behaviorSequence.clear(); + _activePageStays.clear(); + _completedPageStays.clear(); + } + + void _recordPageKey(Map event) { + final pageKey = _payloadPageKey(event); + if (pageKey.isNotEmpty) _pageKeys.add(pageKey); + } + + String _payloadPageKey(Map event) { + return _payloadString(event, 'page_key'); + } + + String _payloadString( + Map event, + String key, { + Object? fallback, + }) { + final payload = event['payload']; + if (payload is Map) { + final value = payload[key]?.toString().trim() ?? ''; + if (value.isNotEmpty) return value; + } + return fallback?.toString().trim() ?? ''; + } + + void _recordBehaviorSequence(String eventName, String pageKey) { + final token = pageKey.isEmpty ? eventName : '$eventName:$pageKey'; + _behaviorSequence.add(token); + if (_behaviorSequence.length > _maxBehaviorSequence) { + _behaviorSequence.removeAt(0); + } + } + + String _behaviorSequenceHash() { + if (_behaviorSequence.isEmpty) return ''; + return sha256.convert(utf8.encode(_behaviorSequence.join('|'))).toString(); + } + + List> _pageStayList(DateTime endedAt) { + final all = [ + ..._completedPageStays, + ..._activePageStays.map((record) => record.close( + exitTime: endedAt, + hasInteraction: _pagesWithInteraction.contains(record.pageKey), + )), + ]..sort((a, b) => a.enterTime.compareTo(b.enterTime)); + final capped = all.length > _maxPageStayRecords + ? all.sublist(all.length - _maxPageStayRecords) + : all; + return capped.map((record) => record.toJson()).toList(); + } + + void _closeActivePageStays({required DateTime exitTime}) { + if (_activePageStays.isEmpty) return; + for (final record in _activePageStays) { + _completedPageStays.add(record.close( + exitTime: exitTime, + hasInteraction: _pagesWithInteraction.contains(record.pageKey), + )); + } + _activePageStays.clear(); + _trimCompletedPageStays(); + } + + int _findActivePageStayIndex(String pageKey, DateTime enterTime) { + for (var i = _activePageStays.length - 1; i >= 0; i--) { + final record = _activePageStays[i]; + if (record.pageKey == pageKey && record.enterTime == enterTime) return i; + } + return -1; + } + + void _trimCompletedPageStays() { + while (_completedPageStays.length > _maxPageStayRecords) { + _completedPageStays.removeAt(0); + } + } +} + +class SessionMetricsSnapshot { + final int eventCountTotal; + final int businessEventCount; + final int foregroundCount; + final int backgroundCountInSid; + final int pageCount; + final int pageViewCount; + final int pageClickCount; + final int networkChangeCount; + final String sessionSid; + final String lastEventName; + final List> pageStayList; + final String behaviorSequenceHash; + + const SessionMetricsSnapshot({ + required this.eventCountTotal, + required this.businessEventCount, + required this.foregroundCount, + required this.backgroundCountInSid, + required this.pageCount, + required this.pageViewCount, + required this.pageClickCount, + required this.networkChangeCount, + required this.sessionSid, + required this.lastEventName, + required this.pageStayList, + required this.behaviorSequenceHash, + }); +} + +class _PageStayRecord { + final String pageKey; + final String pageName; + final DateTime enterTime; + final DateTime? exitTime; + final bool hasInteraction; + + const _PageStayRecord({ + required this.pageKey, + required this.pageName, + required this.enterTime, + this.exitTime, + this.hasInteraction = false, + }); + + _PageStayRecord close({ + required DateTime exitTime, + required bool hasInteraction, + }) { + return _PageStayRecord( + pageKey: pageKey, + pageName: pageName, + enterTime: enterTime, + exitTime: exitTime, + hasInteraction: hasInteraction, + ); + } + + Map toJson() { + final endTime = exitTime ?? DateTime.now(); + final stayDurationMs = endTime.difference(enterTime).inMilliseconds; + return { + 'page_key': pageKey, + 'page_name': pageName, + 'stay_duration_ms': stayDurationMs < 0 ? 0 : stayDurationMs, + 'enter_time': enterTime.millisecondsSinceEpoch ~/ 1000, + 'has_interaction': hasInteraction ? 1 : 0, + }; + } +} diff --git a/analytics_sdk/lib/manager/session_sensor_proximity.dart b/analytics_sdk/lib/manager/session_sensor_proximity.dart new file mode 100644 index 0000000..c1f5b41 --- /dev/null +++ b/analytics_sdk/lib/manager/session_sensor_proximity.dart @@ -0,0 +1,2 @@ +export 'session_sensor_proximity_stub.dart' + if (dart.library.io) 'session_sensor_proximity_native.dart'; diff --git a/analytics_sdk/lib/manager/session_sensor_proximity_native.dart b/analytics_sdk/lib/manager/session_sensor_proximity_native.dart new file mode 100644 index 0000000..5e29f7f --- /dev/null +++ b/analytics_sdk/lib/manager/session_sensor_proximity_native.dart @@ -0,0 +1,17 @@ +import 'dart:io'; + +import 'package:flutter/services.dart'; + +const EventChannel _proximityChannel = EventChannel('analytics_sdk/proximity'); + +Stream proximityEvents() { + if (!Platform.isAndroid && !Platform.isIOS) { + return const Stream.empty(); + } + return _proximityChannel.receiveBroadcastStream().map((value) { + if (value is bool) return value ? 1 : 0; + if (value is num) return value == 0 ? 0 : 1; + final text = value?.toString().trim().toLowerCase() ?? ''; + return text == 'true' || text == '1' ? 1 : 0; + }); +} diff --git a/analytics_sdk/lib/manager/session_sensor_proximity_stub.dart b/analytics_sdk/lib/manager/session_sensor_proximity_stub.dart new file mode 100644 index 0000000..9990d14 --- /dev/null +++ b/analytics_sdk/lib/manager/session_sensor_proximity_stub.dart @@ -0,0 +1 @@ +Stream proximityEvents() => const Stream.empty(); diff --git a/analytics_sdk/lib/manager/session_sensor_sampler.dart b/analytics_sdk/lib/manager/session_sensor_sampler.dart new file mode 100644 index 0000000..e8085b2 --- /dev/null +++ b/analytics_sdk/lib/manager/session_sensor_sampler.dart @@ -0,0 +1,467 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:math'; + +import 'package:analytics_sdk/manager/session_sensor_proximity.dart'; +import 'package:analytics_sdk/utils/logger.dart'; +import 'package:crypto/crypto.dart'; +import 'package:flutter/foundation.dart'; +import 'package:sensors_plus/sensors_plus.dart'; + +enum SensorSampleType { + accelerometer, + gyroscope, + magnetometer, + proximity, +} + +class SensorMagnitudeSample { + final SensorSampleType type; + final double magnitude; + final DateTime sampledAt; + + const SensorMagnitudeSample({ + required this.type, + required this.magnitude, + required this.sampledAt, + }); +} + +class SessionSensorSnapshot { + final String sensorSignature; + final int sensorCountActive; + + const SessionSensorSnapshot({ + required this.sensorSignature, + required this.sensorCountActive, + }); + + const SessionSensorSnapshot.unsupported() + : sensorSignature = '', + sensorCountActive = -1; +} + +typedef SensorStreamFactory = Stream Function( + SensorSampleType type, + Duration samplingPeriod, +); + +typedef PlatformSupportResolver = bool Function(); + +/// Android/iOS session-level sensor sampler. +/// +/// It only keeps rolling aggregate statistics for the current sid. Raw sensor +/// sequences are never retained or exposed. +class SessionSensorSampler { + static final SessionSensorSampler instance = SessionSensorSampler._internal(); + + static const Duration _defaultWindowInterval = Duration(seconds: 60); + static const Duration _defaultWindowDuration = Duration(seconds: 5); + static const Duration _defaultSamplingPeriod = Duration(milliseconds: 200); + static const int _defaultMaxWindowCount = 6; + + final PlatformSupportResolver _isSupportedPlatform; + final SensorStreamFactory _sensorStreams; + final Duration _windowInterval; + final Duration _windowDuration; + final Duration _samplingPeriod; + final int _maxWindowCount; + + String _sid = ''; + bool _isForeground = true; + bool _hasForegroundPageView = false; + bool _samplingStarted = false; + bool _samplingUnavailable = false; + bool _windowActive = false; + int _windowsStarted = 0; + Timer? _nextWindowTimer; + Timer? _windowStopTimer; + final List> _subscriptions = + >[]; + final Map _stats = + {}; + + factory SessionSensorSampler() => instance; + + SessionSensorSampler._internal() + : _isSupportedPlatform = _defaultPlatformSupported, + _sensorStreams = _defaultSensorStreams, + _windowInterval = _defaultWindowInterval, + _windowDuration = _defaultWindowDuration, + _samplingPeriod = _defaultSamplingPeriod, + _maxWindowCount = _defaultMaxWindowCount; + + @visibleForTesting + SessionSensorSampler.forTest({ + required PlatformSupportResolver isSupportedPlatform, + required SensorStreamFactory sensorStreams, + Duration windowInterval = _defaultWindowInterval, + Duration windowDuration = _defaultWindowDuration, + Duration samplingPeriod = _defaultSamplingPeriod, + int maxWindowCount = _defaultMaxWindowCount, + }) : _isSupportedPlatform = isSupportedPlatform, + _sensorStreams = sensorStreams, + _windowInterval = windowInterval, + _windowDuration = windowDuration, + _samplingPeriod = samplingPeriod, + _maxWindowCount = maxWindowCount; + + void recordAcceptedEvent(Map event) { + final eventName = event['event']?.toString() ?? ''; + if (eventName.isEmpty || eventName == 'app_session_summary') return; + + final sid = event['sid']?.toString() ?? ''; + if (sid != _sid) { + reset(sid: sid); + } + + if (eventName == 'app_page_view') { + _hasForegroundPageView = _isForeground; + _ensureSamplingTask(); + return; + } + + if (eventName != 'app_lifecycle') return; + final key = _lifecycleEventKey(event); + if (key == 'background') { + markBackground(); + } else if (key == 'foreground') { + markForeground(); + } + } + + void markForeground() { + _isForeground = true; + _hasForegroundPageView = false; + } + + String _lifecycleEventKey(Map event) { + final payload = event['payload']; + if (payload is Map) { + final value = payload['lifecycle_event_key']?.toString().trim() ?? ''; + if (value.isNotEmpty) return value; + } + return event['lifecycle_event_key']?.toString().trim() ?? ''; + } + + void markBackground() { + _isForeground = false; + _hasForegroundPageView = false; + _pauseSamplingTask(); + } + + void reset({String sid = ''}) { + _pauseSamplingTask(); + _sid = sid; + _isForeground = true; + _hasForegroundPageView = false; + _samplingStarted = false; + _samplingUnavailable = false; + _windowsStarted = 0; + _stats.clear(); + } + + SessionSensorSnapshot snapshot({int screenBrightness = -1}) { + if (_samplingUnavailable || !_samplingStarted) { + return const SessionSensorSnapshot.unsupported(); + } + final activeCount = _stats.entries + .where((entry) => entry.value.isActive(_activeThreshold(entry.key))) + .length; + final totalSamples = + _stats.values.fold(0, (sum, stats) => sum + stats.count); + if (totalSamples <= 0) { + return const SessionSensorSnapshot( + sensorSignature: '', + sensorCountActive: 0, + ); + } + if (activeCount <= 0) { + return const SessionSensorSnapshot( + sensorSignature: '', + sensorCountActive: 0, + ); + } + final signatureInput = _signatureInput(screenBrightness: screenBrightness); + return SessionSensorSnapshot( + sensorSignature: sha256.convert(utf8.encode(signatureInput)).toString(), + sensorCountActive: activeCount, + ); + } + + Future dispose() async { + _pauseSamplingTask(); + _stats.clear(); + } + + @visibleForTesting + void debugMarkSamplingStartedForTest() { + _samplingStarted = true; + _samplingUnavailable = false; + } + + @visibleForTesting + void debugRecordSampleForTest( + SensorSampleType type, { + required double magnitude, + required DateTime sampledAt, + }) { + _recordSample(SensorMagnitudeSample( + type: type, + magnitude: magnitude, + sampledAt: sampledAt, + )); + } + + void _ensureSamplingTask() { + if (!_isForeground || !_hasForegroundPageView) return; + if (_windowsStarted >= _maxWindowCount) return; + if (_windowActive || _nextWindowTimer != null) return; + if (!_isSupportedPlatform()) { + _samplingUnavailable = true; + return; + } + _startWindow(); + } + + void _startWindow() { + if (!_isForeground || !_hasForegroundPageView) return; + if (_windowsStarted >= _maxWindowCount) return; + if (!_isSupportedPlatform()) { + _samplingUnavailable = true; + return; + } + + final subscriptions = >[]; + for (final type in SensorSampleType.values) { + try { + final subscription = _sensorStreams(type, _samplingPeriod).listen( + _recordSample, + onError: (Object error) { + Logger.analyticsSdk('传感器采样异常($type): $error', level: LogLevel.warn); + }, + cancelOnError: false, + ); + subscriptions.add(subscription); + } catch (e) { + Logger.analyticsSdk('传感器监听启动失败($type): $e', level: LogLevel.warn); + } + } + + if (subscriptions.isEmpty) { + _samplingUnavailable = true; + return; + } + + _subscriptions.addAll(subscriptions); + _samplingStarted = true; + _samplingUnavailable = false; + _windowActive = true; + _windowsStarted += 1; + _windowStopTimer?.cancel(); + _windowStopTimer = Timer(_windowDuration, _finishWindow); + } + + void _finishWindow() { + _stopCurrentWindow(); + if (!_isForeground || !_hasForegroundPageView) return; + if (_windowsStarted >= _maxWindowCount) return; + _nextWindowTimer?.cancel(); + _nextWindowTimer = Timer(_windowInterval, () { + _nextWindowTimer = null; + _startWindow(); + }); + } + + void _pauseSamplingTask() { + _nextWindowTimer?.cancel(); + _nextWindowTimer = null; + _windowStopTimer?.cancel(); + _windowStopTimer = null; + _stopCurrentWindow(); + } + + void _stopCurrentWindow() { + _windowActive = false; + for (final subscription in _subscriptions) { + unawaited(subscription.cancel()); + } + _subscriptions.clear(); + } + + void _recordSample(SensorMagnitudeSample sample) { + if (!sample.magnitude.isFinite) return; + final stats = _stats.putIfAbsent(sample.type, _SensorStats.new); + if (!stats.canRecord(sample.sampledAt, _samplingPeriod)) return; + stats.record(sample.magnitude, sample.sampledAt); + } + + String _signatureInput({required int screenBrightness}) { + final tokens = ['sid=$_sid', 'windows=$_windowsStarted']; + if (screenBrightness >= 0 && screenBrightness <= 100) { + tokens.add('screen_brightness=$screenBrightness'); + } + for (final type in SensorSampleType.values) { + final stats = _stats[type]; + if (stats == null || stats.count == 0) continue; + tokens.add('${_typeCode(type)}:${stats.signatureToken}'); + } + return tokens.join('|'); + } + + static bool _defaultPlatformSupported() { + return isSupportedPlatformForTest( + isWeb: kIsWeb, + platform: defaultTargetPlatform, + ); + } + + @visibleForTesting + static bool isSupportedPlatformForTest({ + required bool isWeb, + required TargetPlatform platform, + }) { + return !isWeb && + (platform == TargetPlatform.android || platform == TargetPlatform.iOS); + } + + static Stream _defaultSensorStreams( + SensorSampleType type, + Duration samplingPeriod, + ) { + switch (type) { + case SensorSampleType.accelerometer: + return accelerometerEvents.map( + (event) => SensorMagnitudeSample( + type: type, + magnitude: _magnitude(event.x, event.y, event.z), + sampledAt: DateTime.now(), + ), + ); + case SensorSampleType.gyroscope: + return gyroscopeEvents.map( + (event) => SensorMagnitudeSample( + type: type, + magnitude: _magnitude(event.x, event.y, event.z), + sampledAt: DateTime.now(), + ), + ); + case SensorSampleType.magnetometer: + return magnetometerEvents.map( + (event) => SensorMagnitudeSample( + type: type, + magnitude: _magnitude(event.x, event.y, event.z), + sampledAt: DateTime.now(), + ), + ); + case SensorSampleType.proximity: + return proximityEvents().map( + (value) => SensorMagnitudeSample( + type: type, + magnitude: value == 0 ? 0 : 1, + sampledAt: DateTime.now(), + ), + ); + } + } + + static double _magnitude(double x, double y, double z) { + return sqrt(x * x + y * y + z * z); + } + + static String _typeCode(SensorSampleType type) { + switch (type) { + case SensorSampleType.accelerometer: + return 'acc'; + case SensorSampleType.gyroscope: + return 'gyro'; + case SensorSampleType.magnetometer: + return 'mag'; + case SensorSampleType.proximity: + return 'prox'; + } + } + + static double _activeThreshold(SensorSampleType type) { + switch (type) { + case SensorSampleType.accelerometer: + return 0.8; + case SensorSampleType.gyroscope: + return 0.05; + case SensorSampleType.magnetometer: + return 3.0; + case SensorSampleType.proximity: + return 1.0; + } + } +} + +class _SensorStats { + int count = 0; + double mean = 0; + double _m2 = 0; + double minValue = double.infinity; + double maxValue = double.negativeInfinity; + DateTime? _lastSampleAt; + int _intervalCount = 0; + int _totalIntervalMs = 0; + + bool canRecord(DateTime sampledAt, Duration minInterval) { + final last = _lastSampleAt; + if (last == null) return true; + return sampledAt.difference(last) >= minInterval; + } + + void record(double value, DateTime sampledAt) { + final last = _lastSampleAt; + if (last != null) { + final intervalMs = sampledAt.difference(last).inMilliseconds; + if (intervalMs > 0) { + _intervalCount += 1; + _totalIntervalMs += intervalMs; + } + } + _lastSampleAt = sampledAt; + + count += 1; + final delta = value - mean; + mean += delta / count; + final delta2 = value - mean; + _m2 += delta * delta2; + if (value < minValue) minValue = value; + if (value > maxValue) maxValue = value; + } + + double get variance => count > 1 ? _m2 / (count - 1) : 0; + + double get range { + if (count <= 0) return 0; + return maxValue - minValue; + } + + int get averageIntervalMs { + if (_intervalCount <= 0) return 0; + return (_totalIntervalMs / _intervalCount).round(); + } + + bool isActive(double threshold) { + return count > 0 && range >= threshold; + } + + String get signatureToken { + return [ + 'n=$count', + 'mean=${_fixed(mean)}', + 'var=${_fixed(variance)}', + 'min=${_fixed(minValue)}', + 'max=${_fixed(maxValue)}', + 'range=${_fixed(range)}', + 'avg_i=$averageIntervalMs', + ].join(','); + } + + static String _fixed(double value) { + if (!value.isFinite) return '0.000'; + return value.toStringAsFixed(3); + } +} diff --git a/analytics_sdk/lib/manager/user_manager.dart b/analytics_sdk/lib/manager/user_manager.dart new file mode 100644 index 0000000..97ba935 --- /dev/null +++ b/analytics_sdk/lib/manager/user_manager.dart @@ -0,0 +1,35 @@ +import 'package:analytics_sdk/enum/user_type_enum.dart'; + +/// 全局用户管理器 +/// 负责管理用户类型信息,支持用户类型更新和登出操作 +class UserManager { + static final UserManager instance = UserManager._internal(); + + factory UserManager() => instance; + + UserManager._internal(); + + /// 当前用户类型:normal、vip 等 + String _userType = UserTypeEnum.normal.label; + + String get userType => _userType; + + static String normalizeUserType(String? value) { + return value?.trim().toLowerCase() == UserTypeEnum.vip.label + ? UserTypeEnum.vip.label + : UserTypeEnum.normal.label; + } + + /// 更新用户类型(登录、升级会员、切换账号时调用) + void updateUserType(String newType) { + final normalized = normalizeUserType(newType); + if (_userType != normalized) { + _userType = normalized; + } + } + + /// 登出 + void logout() { + _userType = UserTypeEnum.normal.label; // 或 'guest',根据业务决定 + } +} diff --git a/analytics_sdk/lib/observer/app_lifecycle_observer.dart b/analytics_sdk/lib/observer/app_lifecycle_observer.dart new file mode 100644 index 0000000..1ef675f --- /dev/null +++ b/analytics_sdk/lib/observer/app_lifecycle_observer.dart @@ -0,0 +1,160 @@ +import 'dart:async'; + +import 'package:analytics_sdk/entity/app_lifecycle_event.dart'; +import 'package:analytics_sdk/manager/session_manager.dart'; +import 'package:analytics_sdk/observer/page_lifecycle_observer.dart'; +import 'package:analytics_sdk/utils/environment_info_util.dart'; +import 'package:analytics_sdk/utils/logger.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +/// 应用生命周期观察者:监听应用前后台切换,用于会话管理与生命周期埋点 +class AppLifecycleObserver with WidgetsBindingObserver { + static final AppLifecycleObserver instance = AppLifecycleObserver._internal(); + + factory AppLifecycleObserver() => instance; + + AppLifecycleObserver._internal(); + + bool _isInitialized = false; + void Function(dynamic event)? _track; + DateTime Function() _now = DateTime.now; + DateTime? _foregroundStartedAt; + DateTime? _backgroundStartedAt; + int _foregroundCountInSid = 0; + String _foregroundCountSid = ''; + + /// 初始化观察者 + void initialize({ + void Function(dynamic event)? track, + DateTime Function()? now, + }) { + if (track != null) _track = track; + if (now != null) _now = now; + _foregroundStartedAt ??= _now(); + if (!_isInitialized) { + WidgetsBinding.instance.addObserver(this); + _isInitialized = true; + Logger.analyticsSdk('应用生命周期观察者已初始化'); + } + } + + /// 销毁观察者 + void dispose() { + if (_isInitialized) { + WidgetsBinding.instance.removeObserver(this); + _isInitialized = false; + Logger.analyticsSdk('应用生命周期观察者已销毁'); + } + _track = null; + _foregroundStartedAt = null; + _backgroundStartedAt = null; + _foregroundCountInSid = 0; + _foregroundCountSid = ''; + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + + try { + switch (state) { + case AppLifecycleState.resumed: + if (!kIsWeb) { + final now = _now(); + final backgroundStartedAt = _backgroundStartedAt; + final backgroundDurationMs = backgroundStartedAt == null + ? 0 + : now.difference(backgroundStartedAt).inMilliseconds; + // 应用从后台恢复前台 + SessionManager.instance.onAppForeground(); + if (backgroundStartedAt != null) { + _emit(AppLifecycleEvent.warmStart( + backgroundDurationMs: backgroundDurationMs, + resumeReason: 'unknown', + )); + _emit(AppLifecycleEvent.foreground( + foregroundCountInSid: _nextForegroundCountInSid(), + )); + } + _backgroundStartedAt = null; + _foregroundStartedAt = now; + } + unawaited( + EnvironmentInfoUtil.refreshBatteryLevelIfNeeded(force: true), + ); + unawaited(EnvironmentInfoUtil.refreshRuntimeObservationFields()); + Logger.analyticsSdk('应用恢复前台'); + break; + case AppLifecycleState.paused: + if (!kIsWeb) { + final now = _now(); + final foregroundDurationMs = _foregroundStartedAt == null + ? 0 + : now.difference(_foregroundStartedAt!).inMilliseconds; + _emit(AppLifecycleEvent.background( + foregroundDurationMs: foregroundDurationMs, + lastActivePageKey: PageLifecycleObserver.currentPageKey, + )); + // Android 和 iOS 进入后台均走 paused(iOS 先经过 hidden 再到 paused) + // 统一在此记录后台开始时间,避免在 hidden 重复记录 + SessionManager.instance.onAppBackground(); + _backgroundStartedAt = now; + } + Logger.analyticsSdk('应用进入后台'); + break; + case AppLifecycleState.inactive: + // inactive 是过渡状态(进后台和回前台都会经过),不记录后台时间 + Logger.analyticsSdk('应用进入 inactive 状态'); + break; + case AppLifecycleState.detached: + if (!kIsWeb) { + final now = _now(); + _emit(AppLifecycleEvent.terminate( + lastActivePageKey: PageLifecycleObserver.currentPageKey, + terminateType: 'unknown', + sessionDurationMs: _sessionDurationMs(now), + terminateTs: now.millisecondsSinceEpoch ~/ 1000, + )); + } + Logger.analyticsSdk('应用即将终止'); + break; + default: + // Flutter 3.0 没有 hidden;较新版本的 hidden 是过渡状态, + // 不应影响会话开始或后台时长。 + Logger.analyticsSdk('应用生命周期过渡状态: $state'); + break; + } + } catch (e) { + Logger.analyticsSdk('应用生命周期状态变化处理异常: $e'); + _emit(AppLifecycleEvent.unknown(unknownReason: 'collect_error')); + } + } + + void _emit(AppLifecycleEvent event) { + try { + final track = _track; + if (track == null) return; + track(event); + } catch (e) { + Logger.analyticsSdk('app_lifecycle 上报异常,已安全处理: $e'); + } + } + + int _nextForegroundCountInSid() { + final sid = SessionManager.instance.currentSessionId ?? ''; + if (sid != _foregroundCountSid) { + _foregroundCountSid = sid; + _foregroundCountInSid = 0; + } + _foregroundCountInSid += 1; + return _foregroundCountInSid; + } + + int _sessionDurationMs(DateTime now) { + final start = SessionManager.instance.sessionStartTime; + if (start == null) return 0; + final duration = now.difference(start).inMilliseconds; + return duration < 0 ? 0 : duration; + } +} diff --git a/analytics_sdk/lib/observer/app_lifecycle_web_unload_stub.dart b/analytics_sdk/lib/observer/app_lifecycle_web_unload_stub.dart new file mode 100644 index 0000000..999604e --- /dev/null +++ b/analytics_sdk/lib/observer/app_lifecycle_web_unload_stub.dart @@ -0,0 +1,8 @@ +typedef AppLifecycleWebUnloadDisposer = void Function(); + +AppLifecycleWebUnloadDisposer registerAppLifecycleWebUnloadListener({ + required void Function(dynamic event) track, + required DateTime pageEnterTime, +}) { + return () {}; +} diff --git a/analytics_sdk/lib/observer/app_lifecycle_web_unload_web.dart b/analytics_sdk/lib/observer/app_lifecycle_web_unload_web.dart new file mode 100644 index 0000000..e01b790 --- /dev/null +++ b/analytics_sdk/lib/observer/app_lifecycle_web_unload_web.dart @@ -0,0 +1,88 @@ +// ignore: avoid_web_libraries_in_flutter +import 'dart:async'; +import 'dart:html' as html; + +import 'package:analytics_sdk/entity/app_lifecycle_event.dart'; +import 'package:analytics_sdk/observer/page_lifecycle_observer.dart'; + +typedef AppLifecycleWebUnloadDisposer = void Function(); + +AppLifecycleWebUnloadDisposer registerAppLifecycleWebUnloadListener({ + required void Function(dynamic event) track, + required DateTime pageEnterTime, +}) { + StreamSubscription? pageHideSubscription; + pageHideSubscription = html.window.onPageHide.listen((event) { + if (_isPersistedPageHide(event)) return; + final now = DateTime.now(); + track(AppLifecycleEvent.webUnload( + pageUrl: _currentPageUrl(), + lastActivePageKey: PageLifecycleObserver.currentPageKey, + pageStayDurationMs: now.difference(pageEnterTime).inMilliseconds, + unloadReason: 'unknown', + scrollDepthPercentAtUnload: _scrollDepthPercent(), + webVisibilityState: _visibilityState(), + )); + }); + + return () { + pageHideSubscription?.cancel(); + pageHideSubscription = null; + }; +} + +bool _isPersistedPageHide(html.Event event) { + try { + if (event is html.PageTransitionEvent) { + return event.persisted == true; + } + } catch (_) {} + return false; +} + +String _currentPageUrl() { + final uri = Uri.tryParse(html.window.location.href); + if (uri == null) return ''; + final buffer = StringBuffer(); + if (uri.scheme.isNotEmpty) { + buffer + ..write(uri.scheme) + ..write('://'); + } + if (uri.host.isNotEmpty) { + buffer.write(uri.host); + } + if (uri.hasPort) { + buffer.write(':${uri.port}'); + } + if (uri.path.isNotEmpty) { + buffer.write(uri.path); + } + return buffer.toString(); +} + +int _scrollDepthPercent() { + try { + final documentElement = html.document.documentElement; + final body = html.document.body; + final scrollTop = html.window.pageYOffset.round(); + final viewportHeight = html.window.innerHeight ?? 0; + final documentHeight = [ + documentElement?.scrollHeight ?? 0, + body?.scrollHeight ?? 0, + documentElement?.clientHeight ?? 0, + body?.clientHeight ?? 0, + ].reduce((value, element) => value > element ? value : element); + if (documentHeight <= 0 || viewportHeight <= 0) return 0; + final percent = ((scrollTop + viewportHeight) / documentHeight) * 100; + return percent.round().clamp(0, 100); + } catch (_) { + return 0; + } +} + +String _visibilityState() { + final state = html.document.visibilityState.trim(); + if (state == 'visible' || state == 'hidden') return state; + return 'unknown'; +} diff --git a/analytics_sdk/lib/observer/page_lifecycle_observer.dart b/analytics_sdk/lib/observer/page_lifecycle_observer.dart new file mode 100644 index 0000000..a40e6a8 --- /dev/null +++ b/analytics_sdk/lib/observer/page_lifecycle_observer.dart @@ -0,0 +1,458 @@ +// lib/observer/page_lifecycle_observer.dart + +import 'dart:async'; + +import 'package:analytics_sdk/config/sdk_config.dart'; +import 'package:analytics_sdk/entity/app_page_view_event.dart'; +import 'package:analytics_sdk/manager/session_metrics_manager.dart'; +import 'package:analytics_sdk/utils/logger.dart'; +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +/// 路由页面生命周期观察者,维护页面状态并按配置自动追踪页面展示事件。 +/// +/// **职责** +/// - 监听 [Navigator] 的 push / pop / remove / replace,记录 Session 页面停留 +/// - [autoTrackPageViews] 开启时,在渲染完成后自动上报 [AppPageViewEvent] +/// - 维护 [currentPageKey],供 [GlobalClickWrapper] 进行 page_click 点击归属 +/// - 通过 [_PageInfo.effectivePageKey] 支持子页面精准归属: +/// Tab / PageView 切换时更新栈顶的 effectivePageKey,pop 返回时自动恢复正确 key +/// +/// **接入**:在 [MaterialApp.navigatorObservers] 注册 SDK 提供的 `pageObserver` +/// 或 `sessionObserver`,无需手动实例化。 +/// +/// **限制**:仅追踪带名称的 [PageRoute]([RouteSettings.name] 非空); +/// Dialog、BottomSheet 等非 PageRoute 不会触发上报。 +class PageLifecycleObserver extends NavigatorObserver { + /// 全局路由堆栈(静态,确保 [recordNavigation] 等静态方法可直接访问)。 + /// + /// 每个 [_PageInfo] 元素记录路由 key、来路、进入时间以及当前有效子页面 key。 + static final List<_PageInfo> _pageStack = []; + + /// 事件上报回调(由 AnalyticsSdk 在组合根注入,避免循环依赖) + final void Function(dynamic event) _track; + + /// 用户类型提供者(由 AnalyticsSdk 在组合根注入) + final String Function() _getUserType; + + /// 页面退出回调,传出退出页面的 pageKey(用于清除页面级广告去重状态) + final void Function(String pageKey)? _onPageExit; + + /// 真实路由解析回调(由外部注入,例如 GetX 反向匹配模板路由) + String Function(String? actualRoute)? _resolveRealRoute; + + /// 是否自动上报 [AppPageViewEvent]。 + /// + /// `false` 时仍维护页面状态并写入 Session 停留明细,供手动曝光模式使用。 + final bool autoTrackPageViews; + + /// 页面堆栈最大容量限制,防止内存无限增长 + static int get _maxPageStackSize => SdkConfig.maxPageStackSize; + + /// 当前有效页面 key,供 [GlobalClickWrapper] 及业务方读取。 + /// + /// 更新时机: + /// - 路由 push → 更新为新路由 key + /// - 路由 pop → 恢复为目标路由的 [_PageInfo.effectivePageKey](含子页面状态) + /// - [recordNavigation] → Tab 切换或 PageView 翻页时手动更新 + static final ValueNotifier _currentPageKeyNotifier = + ValueNotifier('main'); + + /// 当前有效页面 key,供 [GlobalClickWrapper] 及业务方读取。 + /// + /// 更新时机: + /// - 路由 push → 更新为新路由 key + /// - 路由 pop → 恢复为目标路由的 [_PageInfo.effectivePageKey](含子页面状态) + /// - [recordNavigation] → Tab 切换或 PageView 翻页时手动更新 + static String get currentPageKey => _currentPageKeyNotifier.value; + + /// 可监听的 currentPageKey,供业务方响应式监听路由变化。 + /// + /// 类型为 [ValueListenable](只读),防止外部直接写入破坏 SDK 内部状态。 + /// 使用示例: + /// ```dart + /// PageLifecycleObserver.currentPageKeyListenable.addListener(() { + /// print(PageLifecycleObserver.currentPageKey); + /// }); + /// ``` + static ValueListenable get currentPageKeyListenable => + _currentPageKeyNotifier; + + /// [_PageInfo.effectivePageKey],使 pop 返回时能恢复到正确的子页面 key。 + /// + /// **调用场景** + /// - 由 [AnalyticsSdk.trackNavigation] 和 [AnalyticsSdk.updateCurrentPage] 统一调用; + /// [BottomNavigationBar.withAnalytics] / [TabBar.withAnalytics] 内部已处理, + /// 接入方通常不需要直接调用此方法。 + /// + /// 若堆栈为空(纯 Tab 导航、无路由层级),则只更新 [currentPageKey]。 + static void recordNavigation(String pageKey) { + _currentPageKeyNotifier.value = pageKey; + if (_pageStack.isNotEmpty) { + _pageStack.last.effectivePageKey = pageKey; + } + } + + static String _stripLeadingSlash(String name) => + PageNameMapper.normalizeKey(name); + + /// 动态更新真实路由解析回调(支持 SDK re-init 时刷新) + void setRealRouteResolver(String Function(String? actualRoute)? resolver) { + _resolveRealRoute = resolver; + } + + /// 解析路由名:优先使用注入的真实路由回调,失败时降级为 RouteSettings.name + String? _resolveRouteName(Route? route) { + if (route == null) return null; + final actualRoute = route.settings.name; + if (_resolveRealRoute == null) return actualRoute; + try { + final resolved = _resolveRealRoute?.call(actualRoute); + if (resolved != null && resolved.isNotEmpty) return resolved; + } catch (e) { + Logger.pageLifecycleObserver(' 真实路由回调执行失败,降级使用 settings.name: $e'); + } + return actualRoute; + } + + // 跟踪活动的 Timer,防止内存泄漏 + final Map _activeTimers = {}; + + /// 本 observer 管辖的路由集合,用于 dispose 时精准清理, + /// 避免多 Navigator 场景下影响其他 observer 的堆栈条目。 + final Set _ownedRoutes = {}; + + PageLifecycleObserver({ + required void Function(dynamic event) track, + required String Function() getUserType, + void Function(String pageKey)? onPageExit, + String Function(String? actualRoute)? resolveRealRoute, + this.autoTrackPageViews = true, + }) : _track = track, + _getUserType = getUserType, + _onPageExit = onPageExit, + _resolveRealRoute = resolveRealRoute; + + @override + void didPush(Route route, Route? previousRoute) { + super.didPush(route, previousRoute); + try { + if (route is PageRoute && route.settings.name != null) { + final page = _ensurePageEntered(route); + if (autoTrackPageViews) _schedulePageView(page); + } + } catch (e) { + Logger.pageLifecycleObserver(' didPush() 异常,已安全处理: $e'); + } + } + + @override + void didPop(Route route, Route? previousRoute) { + super.didPop(route, previousRoute); + try { + _cancelPageViewTimer(route); + if (_pageStack.isNotEmpty && _pageStack.last.route == route) { + // 在出栈前先读取 prevEffectiveKey(push 时捕获的来路有效 key), + // 避免依赖 _pageStack.last.effectivePageKey 在 pop 时仍正确—— + // 后者可能因 recordNavigation 在 main 未入栈时调用而未被更新。 + final restoreKey = _pageStack.last.prevEffectiveKey; + _handlePageExit(route); + + _currentPageKeyNotifier.value = restoreKey.isNotEmpty + ? restoreKey + : _stripLeadingSlash(previousRoute?.settings.name ?? 'main'); + } + } catch (e) { + Logger.pageLifecycleObserver(' didPop() 异常,已安全处理: $e'); + } + } + + @override + void didRemove(Route route, Route? previousRoute) { + super.didRemove(route, previousRoute); + try { + // pushAndRemoveUntil / removeRoute 走这里,需主动清理被移除路由 + _cancelPageViewTimer(route); + final removed = _pageStack.where((info) => info.route == route).toList(); + _pageStack.removeWhere((info) => info.route == route); + _ownedRoutes.remove(route); + for (final info in removed) { + SessionMetricsManager.instance.recordPageExit( + pageKey: info.pageKey, + pageName: info.pageName, + enterTime: info.sessionEnterTime, + exitTime: DateTime.now(), + ); + try { + _onPageExit?.call(info.pageKey); + } catch (e) { + Logger.pageLifecycleObserver('onPageExit 回调异常 (didRemove): $e'); + } + } + } catch (e) { + Logger.pageLifecycleObserver(' didRemove() 异常,已安全处理: $e'); + } + } + + @override + void didReplace({Route? newRoute, Route? oldRoute}) { + super.didReplace(newRoute: newRoute, oldRoute: oldRoute); + try { + // 旧页面 hide + if (oldRoute != null) _cancelPageViewTimer(oldRoute); + if (oldRoute is PageRoute && _pageStack.isNotEmpty) { + if (_pageStack.last.route == oldRoute) { + _handlePageExit(oldRoute); + // 旧路由出栈后,将 currentPageKey 同步为新栈顶的 effectivePageKey。 + // 不更新的话,_ensurePageEntered 捕获的 prevEffectiveKey 会是旧路由的 + // key,导致新路由被 pop 时恢复到已不存在的旧路由,而非下层真实页面。 + _currentPageKeyNotifier.value = + _pageStack.isNotEmpty ? _pageStack.last.effectivePageKey : 'main'; + } + } + // 新页面 show + 曝光 + if (newRoute is PageRoute && newRoute.settings.name != null) { + final page = _ensurePageEntered(newRoute); + if (autoTrackPageViews) _schedulePageView(page); + } + } catch (e) { + Logger.pageLifecycleObserver(' didReplace() 异常,已安全处理: $e'); + } + } + + /// 新 Session 开始且当前路由未变化时,从 Session 起点续开页面停留。 + /// + /// 该操作只写入 [SessionMetricsManager],不会调度或上报 [AppPageViewEvent]。 + void startCurrentPageStayForNewSession(DateTime sessionStartedAt) { + if (_pageStack.isEmpty) return; + final current = _pageStack.last; + current.sessionEnterTime = sessionStartedAt; + SessionMetricsManager.instance.recordPageEnter( + pageKey: current.pageKey, + pageName: current.pageName, + enterTime: sessionStartedAt, + ); + } + + /// 确保同一个 Flutter [Route] 只写入一次页面状态和 Session 停留明细。 + _PageInfo _ensurePageEntered(PageRoute route) { + for (final existing in _pageStack) { + if (existing.route == route) return existing; + } + + final rawKey = + _stripLeadingSlash(_resolveRouteName(route) ?? route.settings.name!); + final pageInfo = PageNameMapper.resolvePageInfo(rawKey); + final pageKey = pageInfo.key; + final pageName = pageInfo.name; + // 在 currentPageKey 被覆盖前捕获:这是 pop 返回时应恢复的目标 key。 + // 必须在 currentPageKey = pageKey 之前读取,否则读到的是新页面自身的 key。 + final prevEffectiveKey = currentPageKey; + _currentPageKeyNotifier.value = pageKey; + + final referrerKey = + _pageStack.isEmpty ? '' : _pageStack.last.effectivePageKey; + final referrerName = _pageStack.isEmpty + ? '' + : PageNameMapper.getPageName(_pageStack.last.effectivePageKey); + final enterTime = DateTime.now(); + + // 页面堆栈容量检查 + if (_pageStack.length >= _maxPageStackSize) { + final evicted = _pageStack.removeAt(0); + final evictedTimer = _activeTimers.remove(evicted.route); + evictedTimer?.cancel(); + SessionMetricsManager.instance.recordPageExit( + pageKey: evicted.pageKey, + pageName: evicted.pageName, + enterTime: evicted.sessionEnterTime, + exitTime: DateTime.now(), + ); + } + + final page = _PageInfo( + pageKey: pageKey, + pageName: pageName, + routeEnterTime: enterTime, + route: route, + prevEffectiveKey: prevEffectiveKey, + referrerPageKey: referrerKey, + referrerPageName: referrerName, + ); + _pageStack.add(page); + SessionMetricsManager.instance.recordPageEnter( + pageKey: pageKey, + pageName: pageName, + enterTime: enterTime, + ); + _ownedRoutes.add(route); + return page; + } + + /// 在页面完成首帧渲染后自动上报一次 [AppPageViewEvent]。 + void _schedulePageView(_PageInfo page) { + if (page.pageViewScheduled) return; + page.pageViewScheduled = true; + + final route = page.route; + bool callbackExecuted = false; + Timer? loadTimer; + + void frameCallback(Duration timestamp) { + if (callbackExecuted) return; + // 页面可能在帧回调等待期间被 pop,检查是否仍在栈中。 + final stillOnStack = _pageStack.any((info) => info.route == route); + if (!stillOnStack) { + callbackExecuted = true; + loadTimer?.cancel(); + _activeTimers.remove(route); + return; + } + callbackExecuted = true; + + final pageLoadTime = + DateTime.now().difference(page.routeEnterTime).inMilliseconds; + loadTimer?.cancel(); + _activeTimers.remove(route); + + String userType; + try { + userType = _getUserType(); + } catch (e) { + Logger.pageLifecycleObserver(' 获取用户类型失败,使用默认值: $e'); + userType = 'normal'; + } + + try { + _track(AppPageViewEvent( + userType: userType, + pageKey: page.pageKey, + pageName: page.pageName, + referrerPageKey: page.referrerPageKey, + referrerPageName: page.referrerPageName, + currentPageKey: page.pageKey, + currentPageName: page.pageName, + pageLoadTime: pageLoadTime, + pageEnterTs: page.routeEnterTime.millisecondsSinceEpoch ~/ 1000, + )); + } catch (e) { + Logger.pageLifecycleObserver(' AppPageViewEvent 上报失败: $e'); + } + } + + // 超时保护:如果第一帧回调未在规定时间内执行,重新注册。 + loadTimer = Timer(SdkConfig.pageLoadTimeout, () { + if (!callbackExecuted && + _pageStack.isNotEmpty && + _pageStack.last.route == route) { + SchedulerBinding.instance.addPostFrameCallback(frameCallback); + } + }); + _activeTimers[route] = loadTimer; + SchedulerBinding.instance.addPostFrameCallback(frameCallback); + } + + /// 释放所有待触发的 Timer,防止 dispose 后继续触发幽灵上报。 + /// 只移除本 observer 管辖的路由条目,避免多 Navigator 场景下清空其他 observer 的堆栈。 + void dispose() { + for (final timer in _activeTimers.values) { + timer.cancel(); + } + _pageStack.removeWhere((info) => _ownedRoutes.contains(info.route)); + _activeTimers.clear(); + _ownedRoutes.clear(); + } + + void _cancelPageViewTimer(Route route) { + final timer = _activeTimers.remove(route); + timer?.cancel(); + } + + /// 处理页面退出:出栈、取消对应 Timer、触发 [_onPageExit] 回调。 + void _handlePageExit(Route route) { + if (_pageStack.isEmpty || _pageStack.last.route != route) return; + + final exitingPage = _pageStack.last; + final exitingPageKey = exitingPage.pageKey; + _pageStack.removeLast(); + _ownedRoutes.remove(route); + SessionMetricsManager.instance.recordPageExit( + pageKey: exitingPage.pageKey, + pageName: exitingPage.pageName, + enterTime: exitingPage.sessionEnterTime, + exitTime: DateTime.now(), + ); + + // 清理该路由的 Timer,防止内存泄漏 + _cancelPageViewTimer(route); + + try { + _onPageExit?.call(exitingPageKey); + } catch (e) { + Logger.pageLifecycleObserver('onPageExit 回调异常: $e'); + } + } +} + +/// 路由页面快照,记录页面进入时的状态。 +/// +/// [effectivePageKey] 是唯一可变字段:初始值与 [pageKey] 相同, +/// 当该路由位于栈顶时,内部 Tab / PageView 切换会通过 +/// [PageLifecycleObserver.recordNavigation] 将其更新为子页面 key; +/// 新路由 push 的 referrer 均能精准反映子页面位置。 +/// +/// [prevEffectiveKey] 在 push 时捕获彼时的 [PageLifecycleObserver.currentPageKey], +/// pop 返回时用于恢复 [PageLifecycleObserver.currentPageKey]。 +/// 与 [effectivePageKey] 的区别:前者存储在被 push 的新页面里, +/// 后者存储在上一层页面里——当上层页面是"容器路由"时, +/// effectivePageKey 可能因时序问题未能同步 tab 状态, +/// 而 prevEffectiveKey 在 push 时即时捕获,始终准确。 +class _PageInfo { + /// 路由自身的页面 key(由路由名称规范化而来,不可变)。 + final String pageKey; + + /// 路由自身的展示名称(由 [PageNameMapper] 解析,不可变)。 + final String pageName; + + /// 该路由当前的有效页面 key。 + /// + /// 初始等于 [pageKey];路由处于栈顶时,内部子页面切换会将其更新为子页面 key, + /// 从而使新路由 push 的 referrer 能精准反映子页面位置。 + String effectivePageKey; + + /// push 此路由时捕获的前一页有效 key,pop 时用于恢复 [PageLifecycleObserver.currentPageKey]。 + /// + /// 在 [PageLifecycleObserver._ensurePageEntered] 的第一行读取 + /// [PageLifecycleObserver.currentPageKey](早于 `currentPageKey = pageKey`), + /// 保证容器路由 + tab 场景下返回时能恢复到正确的子页面 key 而非容器路由 key。 + final String prevEffectiveKey; + + /// 进入该路由时的来路页面信息,供自动页面曝光使用。 + final String referrerPageKey; + final String referrerPageName; + + /// 防止 `pageObserver` 与 `sessionObserver` 误同时注册时重复自动曝光。 + bool pageViewScheduled = false; + + /// 路由首次进入时刻,只用于页面曝光时间和 pageLoadTime。 + final DateTime routeEnterTime; + + /// 当前 Session 内的页面进入时刻,用于页面停留统计。 + DateTime sessionEnterTime; + + /// 对应的 Flutter 路由对象,用于 Timer / 堆栈查找时的精确匹配。 + final Route route; + + _PageInfo({ + required this.pageKey, + required this.pageName, + required this.routeEnterTime, + required this.route, + required this.prevEffectiveKey, + required this.referrerPageKey, + required this.referrerPageName, + }) : effectivePageKey = pageKey, + sessionEnterTime = routeEnterTime; +} diff --git a/analytics_sdk/lib/utils/aes_gcm_util.dart b/analytics_sdk/lib/utils/aes_gcm_util.dart new file mode 100644 index 0000000..92af02f --- /dev/null +++ b/analytics_sdk/lib/utils/aes_gcm_util.dart @@ -0,0 +1,346 @@ +import 'dart:convert'; +import 'package:archive/archive.dart'; +import 'dart:math'; +import 'dart:typed_data'; + +import 'package:analytics_sdk/config/sdk_config.dart'; +import 'package:pointycastle/export.dart'; + +/// AES-GCM 加密工具类 +/// 提供 AES-256-GCM 加密/解密、GZIP 压缩/解压功能 +class AesGcmUtil { + // AES-256 密钥长度 + static const int keySize = 32; + + // GCM IV 长度(12 字节) + static const int gcmIvLength = 12; + + // GCM Tag 长度(128 位 = 16 字节) + static const int gcmTagLength = 16; + + // 随机数生成器 + static final Random _random = Random.secure(); + + /// 生成唯一的密钥(Base64 字符串,无填充) + /// 返回 32 字节的 AES-256 密钥的 Base64 编码 + static String generateKey() { + final keyBytes = Uint8List(keySize); + for (int i = 0; i < keySize; i++) { + keyBytes[i] = _random.nextInt(256); + } + return base64Encode(keyBytes) + .replaceAll('=', '') + .replaceAll('+', '-') + .replaceAll('/', '_'); + } + + /// 加密:Object → String(Base64) + /// [keyBase64] Base64 编码的密钥 + /// [data] 要加密的数据(任意对象,会被转换为 JSON) + /// 返回 Base64 编码的加密数据(IV + 密文) + static String encrypt(String keyBase64, dynamic data) { + try { + // 解码密钥 + final key = _decodeKey(keyBase64); + + // 将数据转换为 JSON 字符串 + final json = jsonEncode(data); + final input = Uint8List.fromList(utf8.encode(json)); + + // 生成随机 IV(12 字节) + final iv = Uint8List(gcmIvLength); + for (int i = 0; i < gcmIvLength; i++) { + iv[i] = _random.nextInt(256); + } + + // 执行 AES-GCM 加密 + final cipher = GCMBlockCipher(AESEngine()); + final params = AEADParameters( + KeyParameter(key), + gcmTagLength * 8, // Tag 长度(位) + iv, + Uint8List(0), // 无附加认证数据(AAD) + ); + cipher.init(true, params); + + final cipherText = cipher.process(input); + + // 组合 IV + 密文 + final output = Uint8List(iv.length + cipherText.length); + output.setRange(0, iv.length, iv); + output.setRange(iv.length, output.length, cipherText); + + // Base64 编码(无填充) + return base64Encode(output) + .replaceAll('=', '') + .replaceAll('+', '-') + .replaceAll('/', '_'); + } catch (e) { + throw Exception('Encryption failed: $e'); + } + } + + /// 加密(带 GZIP 压缩):先压缩再加密 + /// [keyBase64] Base64 编码的密钥 + /// [data] 要加密的数据(任意对象,会被转换为 JSON) + /// 返回 Base64 编码的加密数据 + static String encryptGzip(String keyBase64, dynamic data) { + try { + // 解码密钥 + final key = _decodeKey(keyBase64); + + // 将数据转换为 JSON 字符串 + final json = jsonEncode(data); + + // ① 先 GZIP 压缩并转 Base64 + final gzipBase64 = gzipToBase64(json); + + // ② AES-GCM 加密这个 Base64 字符串 + final input = Uint8List.fromList(utf8.encode(gzipBase64)); + + // 生成随机 IV(12 字节) + final iv = Uint8List(gcmIvLength); + for (int i = 0; i < gcmIvLength; i++) { + iv[i] = _random.nextInt(256); + } + + // 执行 AES-GCM 加密 + final cipher = GCMBlockCipher(AESEngine()); + final params = AEADParameters( + KeyParameter(key), + gcmTagLength * 8, // Tag 长度(位) + iv, + Uint8List(0), // 无附加认证数据(AAD) + ); + cipher.init(true, params); + + final cipherText = cipher.process(input); + + // 组合 IV + 密文 + final output = Uint8List(iv.length + cipherText.length); + output.setRange(0, iv.length, iv); + output.setRange(iv.length, output.length, cipherText); + + // Base64 编码(无填充) + return base64Encode(output) + .replaceAll('=', '') + .replaceAll('+', '-') + .replaceAll('/', '_'); + } catch (e) { + throw Exception('Encryption failed: $e'); + } + } + + /// 解密:返回明文 JSON 字符串 + /// [keyBase64] Base64 编码的密钥 + /// [encryptedBase64] Base64 编码的加密数据(IV + 密文) + /// 返回解密后的 JSON 字符串 + static String decryptToString(String keyBase64, String encryptedBase64) { + try { + // 解码 Base64(处理 URL 安全格式) + String normalizedBase64 = + encryptedBase64.replaceAll('-', '+').replaceAll('_', '/'); + while (normalizedBase64.length % 4 != 0) { + normalizedBase64 += '='; + } + final data = base64Decode(normalizedBase64); + return decryptFromCipherBytes(keyBase64, Uint8List.fromList(data)); + } catch (e) { + throw Exception('Decryption failed: $e'); + } + } + + /// 解密:从原始字节(IV + 密文)解密为明文字符串 + /// [keyBase64] Base64 编码的密钥 + /// [cipherBytes] 密文字节:前 12 字节为 IV,后续为 AES-GCM 密文 + /// 返回解密后的 UTF-8 字符串 + /// + /// 若抛出 [InvalidCipherTextException](或 "Decryption failed: InvalidCipherTextException"), + /// 常见原因:① 密钥与加密端不一致 ② 服务端密文格式不是「前 12 字节 IV + 密文」 + /// (如 IV 单独返回或顺序不同,需先自行截取再传入) + static String decryptFromCipherBytes( + String keyBase64, Uint8List cipherBytes) { + try { + final key = _decodeKey(keyBase64); + if (cipherBytes.length < gcmIvLength) { + throw ArgumentError( + 'Invalid ciphertext: too short (need at least $gcmIvLength bytes for IV)'); + } + final iv = cipherBytes.sublist(0, gcmIvLength); + final cipherText = cipherBytes.sublist(gcmIvLength); + final cipher = GCMBlockCipher(AESEngine()); + final params = AEADParameters( + KeyParameter(key), + gcmTagLength * 8, + iv, + Uint8List(0), + ); + cipher.init(false, params); + final plainText = cipher.process(cipherText); + return utf8.decode(plainText); + } on InvalidCipherTextException catch (e) { + throw Exception( + 'Decryption failed: GCM 校验失败(密钥错误、IV/密文格式不符或数据被篡改)。' + ' 请确认:① 密钥与加密端一致 ② 密文为「前 12 字节 IV + 密文」。原始: $e', + ); + } catch (e) { + throw Exception('Decryption failed: $e'); + } + } + + /// 解密:从 "[169, 246, 173, ...]" 格式的字节列表字符串解密 + /// 适用于接口返回的密文是「数字列表字符串」而非 Base64 的情况 + /// [keyBase64] Base64 编码的密钥,传 null 则使用 SdkConfig.decryptKey + /// [byteListString] 形如 "[169, 246, 173, 197, ...]" 的字符串(前 12 字节为 IV,后续为密文) + /// 返回解密后的 UTF-8 字符串 + static String decryptFromByteListString(String byteListString, + {String? keyBase64}) { + final key = keyBase64 ?? _defaultKey; + final cipherBytes = _parseByteListString(byteListString); + return decryptFromCipherBytes(key, cipherBytes); + } + + /// 将 "[169, 246, ...]" 格式的字符串解析为 Uint8List + static Uint8List _parseByteListString(String byteListString) { + final list = byteListString + .replaceAll(RegExp(r'[\[\]\s]+'), ' ') + .split(',') + .map((s) => s.trim()) + .where((s) => s.isNotEmpty) + .map((s) => int.parse(s)) + .toList(); + for (final b in list) { + if (b < 0 || b > 255) throw ArgumentError('Byte value out of range: $b'); + } + return Uint8List.fromList(list); + } + + /// 解码 Base64 密钥 + static Uint8List _decodeKey(String keyBase64) { + // 处理 URL 安全格式 + String normalizedBase64 = + keyBase64.replaceAll('-', '+').replaceAll('_', '/'); + // 添加填充 + while (normalizedBase64.length % 4 != 0) { + normalizedBase64 += '='; + } + + final keyBytes = base64Decode(normalizedBase64); + if (keyBytes.length != keySize) { + throw ArgumentError('Key must be 32 bytes for AES-256'); + } + return keyBytes; + } + + /// GZIP 压缩字节数组 + static Uint8List gzip(Uint8List data) { + return gzipEncode(data); + } + + /// GZIP 压缩字符串并转 Base64 + static String gzipToBase64(String str) { + if (str.isEmpty) return str; + try { + final compressed = gzipEncode(utf8.encode(str)); + return base64Encode(compressed); + } catch (e) { + throw Exception('Gzip 压缩失败: $e'); + } + } + + /// GZIP 解压字节数组 + static Uint8List gunzip(Uint8List data) { + return gzipDecode(data); + } + + /// 从 Base64 解压 GZIP + static String gunzipFromBase64(String base64) { + if (base64.isEmpty) return base64; + try { + final compressed = base64Decode(base64); + final decompressed = gzipDecode(compressed); + return utf8.decode(decompressed); + } catch (e) { + throw Exception('Gzip 解压失败: $e'); + } + } + + /// GZIP 编码(使用 package:archive,兼容 Web) + static Uint8List gzipEncode(List data) { + final encoder = GZipEncoder(); + final compressed = encoder.encode(data) ?? []; + return Uint8List.fromList(compressed); + } + + /// GZIP 解码(使用 package:archive,兼容 Web) + static Uint8List gzipDecode(List data) { + final decoder = GZipDecoder(); + final decompressed = decoder.decodeBytes(data); + return Uint8List.fromList(decompressed); + } + + /// 解密接口响应(使用 SDK 默认密钥) + /// + /// 接口直接返回加密字符串,整段解密后返回明文。 + /// + /// [responseBody] 接口返回的加密字符串 + /// [useGzip] 是否对解密结果再做 GZIP 解压(默认:false) + static String decryptResponseAuto(String responseBody, + {bool useGzip = false}) { + return decryptResponse(_defaultKey, responseBody, useGzip: useGzip); + } + + /// 默认密钥(从 SdkConfig 获取) + static String get _defaultKey => SdkConfig.decryptKey; + + /// 解密接口响应 + /// + /// 接口直接返回加密字符串,整段解密后返回明文。 + /// + /// [keyBase64] Base64 编码的密钥 + /// [responseBody] 接口返回的加密字符串 + /// [useGzip] 是否对解密结果再做 GZIP 解压(默认:false) + static String decryptResponse(String keyBase64, String responseBody, + {bool useGzip = false}) { + try { + final decrypted = decryptToString(keyBase64, responseBody.trim()); + if (useGzip) return gunzipFromBase64(decrypted); + return decrypted; + } catch (e) { + if (e is Exception) rethrow; + throw Exception('解密响应失败: $e'); + } + } + + /// 解密接口响应并解析为 Map + /// 接口直接返回加密字符串,解密后再按 JSON 解析为 Map。 + /// [keyBase64] Base64 编码的密钥 + /// [responseBody] 接口返回的加密字符串 + /// [useGzip] 是否对解密结果再做 GZIP 解压(默认:false) + static Map decryptResponseToMap( + String keyBase64, + String responseBody, { + bool useGzip = false, + }) { + final decryptedJson = + decryptResponse(keyBase64, responseBody, useGzip: useGzip); + return jsonDecode(decryptedJson) as Map; + } + + /// 解密接口响应并解析为指定类型 + /// 接口直接返回加密字符串,解密后再按 JSON 解析并转为 T。 + /// [keyBase64] Base64 编码的密钥 + /// [responseBody] 接口返回的加密字符串 + /// [fromJson] JSON 解析函数 + /// [useGzip] 是否对解密结果再做 GZIP 解压(默认:false) + static T decryptResponseToObject( + String keyBase64, + String responseBody, + T Function(Map) fromJson, { + bool useGzip = false, + }) { + final decryptedMap = + decryptResponseToMap(keyBase64, responseBody, useGzip: useGzip); + return fromJson(decryptedMap); + } +} diff --git a/analytics_sdk/lib/utils/device_fingerprint_native.dart b/analytics_sdk/lib/utils/device_fingerprint_native.dart new file mode 100644 index 0000000..99aad97 --- /dev/null +++ b/analytics_sdk/lib/utils/device_fingerprint_native.dart @@ -0,0 +1,52 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:crypto/crypto.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:analytics_sdk/utils/uuid_util.dart'; + +const _kFingerprintFileName = 'analytics_fingerprint.txt'; + +/// 原生平台设备指纹生成 +/// +/// **生成规则** +/// 1. 将非空因子用 `|` 拼接后做 SHA-256,取 64 位十六进制作为指纹。 +/// 因子:deviceId、device、deviceBrand、deviceModel、systemName、systemVersion。 +/// 2. 若所有因子均为空(降级场景),则生成 UUID v4 并持久化至 +/// `applicationDocumentsDirectory/analytics_fingerprint.txt`, +/// 后续从文件读取保证稳定。 +/// +/// 任何异常均返回空字符串,不影响正常上报。 +Future generateOrLoadFingerprint({ + String deviceId = '', + String device = '', + String deviceBrand = '', + String deviceModel = '', + String systemName = '', + String systemVersion = '', + String userAgent = '', // 原生端不使用,仅保持接口与 Web 端一致 +}) async { + try { + final factors = [deviceId, device, deviceBrand, deviceModel, systemName, systemVersion] + .where((f) => f.isNotEmpty) + .toList(); + + if (factors.isNotEmpty) { + final digest = sha256.convert(utf8.encode(factors.join('|'))); + return digest.toString(); + } + + // 所有因子为空时降级为持久化 UUID v4 + final dir = await getApplicationDocumentsDirectory(); + final file = File('${dir.path}/$_kFingerprintFileName'); + if (await file.exists()) { + final existing = (await file.readAsString()).trim(); + if (existing.isNotEmpty) return existing; + } + final fp = generateUuidV4(); + await file.writeAsString(fp); + return fp; + } catch (_) { + return ''; + } +} diff --git a/analytics_sdk/lib/utils/device_fingerprint_util.dart b/analytics_sdk/lib/utils/device_fingerprint_util.dart new file mode 100644 index 0000000..b40690e --- /dev/null +++ b/analytics_sdk/lib/utils/device_fingerprint_util.dart @@ -0,0 +1,216 @@ +import 'device_fingerprint_native.dart' + // ignore: uri_does_not_exist + if (dart.library.html) 'device_fingerprint_web.dart'; + +import 'dart:convert'; + +import 'package:crypto/crypto.dart'; + +/// 设备指纹工具类 +/// +/// SDK 内部自动调用,业务方无需手动传入。 +/// +/// **生成规则** +/// 1. 现行外层 `device_fingerprint` 使用 v3 规则: +/// 基于 `sdk_init` payload 内的平台稳定字段,按固定顺序规范化后做 +/// SHA-256,输出 64 位小写十六进制。 +/// 2. 网络、电量、页面、行为等易变字段不参与 v3 指纹。 +/// 3. [getFingerprint] 保留为旧调用方兼容入口;SDK 自动上报链路使用 +/// [generateV3FromSdkInitPayload]。 +/// +/// **平台差异** +/// - Native(Android/iOS/桌面):降级 UUID 存于 `applicationDocumentsDirectory/analytics_fingerprint.txt` +/// - Web:降级 UUID 存于 `localStorage['analytics_fp']` +/// +/// **失败兜底**:任何异常均返回空字符串,事件正常上报,仅 `device_fingerprint` 字段为空。 +/// +/// **注意**:`device_fingerprint` 不参与 `event_id` 的计算。 +class DeviceFingerprintUtil { + /// 指纹生成规则版本号。 + /// + /// 每次生成规则(因子组合、哈希算法)发生变更时,此版本号必须同步递增, + /// 以便服务端区分不同规则下生成的指纹,支持跨版本数据对比分析。 + /// + /// 版本历史: + /// - `3.0.0`:按 sdk_init 启动快照中的平台稳定字段生成。 + static const String kVersion = '3.0.0'; + + static String? _cache; + + /// 获取设备指纹。 + /// + /// 首次调用时根据传入的设备因子计算并内存缓存,后续直接返回缓存值。 + /// 若设备因子发生变化需重新计算,请先调用 [clearCache]。 + static Future getFingerprint({ + String deviceId = '', + String device = '', + String deviceBrand = '', + String deviceModel = '', + String systemName = '', + String systemVersion = '', + String userAgent = '', + }) async { + if (_cache != null) return _cache!; + _cache = await generateOrLoadFingerprint( + deviceId: deviceId, + device: device, + deviceBrand: deviceBrand, + deviceModel: deviceModel, + systemName: systemName, + systemVersion: systemVersion, + userAgent: userAgent, + ); + return _cache!; + } + + /// 清除内存缓存(通常仅测试时使用,不会删除本地存储文件) + static void clearCache() { + _cache = null; + } + + /// 按产品文档 v3 规则,基于 sdk_init payload 生成外层设备指纹。 + /// + /// 该方法只使用启动快照内相对稳定的字段;网络、电量、页面、行为等 + /// 易变字段不参与。输入缺失时保留字段名并使用文档默认值,确保同一 + /// 平台的参与字段顺序稳定。 + static String generateV3FromSdkInitPayload(Map payload) { + try { + final platform = _fingerprintPlatform(payload); + final values = platform == 'web' + ? _webFingerprintValues(payload) + : platform == 'ios' + ? _iosFingerprintValues(payload) + : _androidFingerprintValues(payload); + final canonical = + 'fp_v3|fingerprint_platform=$platform|${values.join('|')}'; + return sha256.convert(utf8.encode(canonical)).toString(); + } catch (_) { + return ''; + } + } + + static String _fingerprintPlatform(Map payload) { + if (_string(payload['platform']) == 'web') return 'web'; + final osFamily = _string(payload['os_family']); + if (osFamily == 'ios') return 'ios'; + return 'android'; + } + + static List _androidFingerprintValues(Map payload) { + final id = _selectFirstId(payload, const [ + 'oaid', + 'aaid', + 'android_id', + 'guid', + ]); + return [ + _kvString(payload, 'os_family'), + _kvString(payload, 'brand'), + _kvString(payload, 'manufacturer'), + _kvString(payload, 'model'), + _kvString(payload, 'abi'), + _kvInt(payload, 'screen_width'), + _kvInt(payload, 'screen_height'), + _kvInt(payload, 'screen_dpi'), + _kvInt(payload, 'device_pixel_ratio'), + _kvInt(payload, 'cpu_cores'), + _kvInt(payload, 'device_memory_gb'), + _kvString(payload, 'build_hardware'), + _kvString(payload, 'build_product'), + _kvString(payload, 'build_fingerprint'), + _kvInt(payload, 'has_baseband'), + _kvString(payload, 'language'), + _kvString(payload, 'timezone'), + 'id_source=${id.source}', + 'id_value=${id.value}', + ]; + } + + static List _iosFingerprintValues(Map payload) { + final idfv = _string(payload['idfv']); + return [ + _kvString(payload, 'os_family'), + _kvString(payload, 'ios_device_model_raw'), + _kvInt(payload, 'screen_width'), + _kvInt(payload, 'screen_height'), + _kvInt(payload, 'screen_dpi'), + _kvInt(payload, 'device_pixel_ratio'), + _kvInt(payload, 'cpu_cores'), + _kvInt(payload, 'device_memory_gb'), + _kvString(payload, 'language'), + _kvString(payload, 'timezone'), + 'id_source=${idfv.isEmpty ? 'none' : 'idfv'}', + 'id_value=$idfv', + ]; + } + + static List _webFingerprintValues(Map payload) { + return [ + _kvString(payload, 'os_family'), + _kvString(payload, 'browser_kernel'), + _kvString(payload, 'browser_platform'), + _kvInt(payload, 'screen_width'), + _kvInt(payload, 'screen_height'), + _kvInt(payload, 'screen_dpi'), + _kvInt(payload, 'device_pixel_ratio'), + _kvInt(payload, 'color_depth'), + _kvInt(payload, 'max_touch_points'), + _kvInt(payload, 'hardware_concurrency'), + _kvString(payload, 'language'), + _kvString(payload, 'timezone'), + _kvInt(payload, 'cookie_enabled'), + _kvInt(payload, 'local_storage_enabled'), + _kvInt(payload, 'session_storage_enabled'), + _kvInt(payload, 'indexeddb_enabled'), + _kvInt(payload, 'service_worker_enabled'), + _kvString(payload, 'canvas_fp'), + _kvString(payload, 'webgl_fp'), + _kvString(payload, 'audio_fp'), + _kvString(payload, 'fonts_hash'), + _kvString(payload, 'plugins_hash'), + _kvString(payload, 'mimes_hash'), + ]; + } + + static _SelectedId _selectFirstId( + Map payload, + List keys, + ) { + for (final key in keys) { + final value = _string(payload[key]); + if (value.isNotEmpty) return _SelectedId(key, value); + } + return const _SelectedId('none', ''); + } + + static String _kvString(Map payload, String key) => + '$key=${_string(payload[key])}'; + + static String _kvInt(Map payload, String key) => + '$key=${_int(payload[key])}'; + + static String _string(dynamic value) { + if (value == null) return ''; + return value.toString().trim().toLowerCase(); + } + + static int _int(dynamic value) { + if (value is int) return value; + if (value is double && value.isFinite) return value.round(); + if (value is String) { + final text = value.trim(); + final asInt = int.tryParse(text); + if (asInt != null) return asInt; + final asDouble = double.tryParse(text); + if (asDouble != null && asDouble.isFinite) return asDouble.round(); + } + return -1; + } +} + +class _SelectedId { + final String source; + final String value; + + const _SelectedId(this.source, this.value); +} diff --git a/analytics_sdk/lib/utils/device_fingerprint_web.dart b/analytics_sdk/lib/utils/device_fingerprint_web.dart new file mode 100644 index 0000000..0bfcca7 --- /dev/null +++ b/analytics_sdk/lib/utils/device_fingerprint_web.dart @@ -0,0 +1,78 @@ +// ignore: avoid_web_libraries_in_flutter +import 'dart:convert'; +// ignore: avoid_web_libraries_in_flutter +import 'dart:html' as html; + +import 'package:crypto/crypto.dart'; +import 'package:analytics_sdk/utils/uuid_util.dart'; + +const _kFingerprintKey = 'analytics_fp'; + +/// Web 平台设备指纹生成 +/// +/// **生成规则** +/// 1. 收集宿主 App 传入字段(device、deviceBrand、deviceModel、systemName、 +/// systemVersion、userAgent)及浏览器环境信息(屏幕分辨率、语言、时区), +/// 过滤空值后用 `|` 拼接,做 SHA-256 生成指纹。 +/// 2. 若所有因子均为空,降级生成 UUID v4 并写入 localStorage,后续复用。 +/// +/// 任何异常均返回空字符串,不影响正常上报。 +Future generateOrLoadFingerprint({ + String deviceId = '', + String device = '', + String deviceBrand = '', + String deviceModel = '', + String systemName = '', + String systemVersion = '', + String userAgent = '', +}) async { + try { + final envFactors = _collectBrowserEnv(); + final factors = [ + device, deviceBrand, deviceModel, + systemName, systemVersion, + userAgent, + ...envFactors, + ].where((f) => f.isNotEmpty).toList(); + + if (factors.isNotEmpty) { + final digest = sha256.convert(utf8.encode(factors.join('|'))); + return digest.toString(); + } + + // 所有因子为空时降级为持久化 UUID v4 + final storage = html.window.localStorage; + final existing = storage[_kFingerprintKey] ?? ''; + if (existing.isNotEmpty) return existing; + + final fp = generateUuidV4(); + storage[_kFingerprintKey] = fp; + return fp; + } catch (_) { + return ''; + } +} + +/// 收集浏览器环境因子:屏幕分辨率、语言、时区 +List _collectBrowserEnv() { + final factors = []; + try { + final screen = html.window.screen; + if (screen != null) { + final w = screen.width; + final h = screen.height; + if (w != null && h != null && w > 0 && h > 0) { + factors.add('${w}x$h'); + } + } + } catch (_) {} + try { + final lang = html.window.navigator.language; + if (lang.isNotEmpty) factors.add(lang); + } catch (_) {} + try { + final tz = DateTime.now().timeZoneName; + if (tz.isNotEmpty) factors.add(tz); + } catch (_) {} + return factors; +} diff --git a/analytics_sdk/lib/utils/device_id_native.dart b/analytics_sdk/lib/utils/device_id_native.dart new file mode 100644 index 0000000..6d52bea --- /dev/null +++ b/analytics_sdk/lib/utils/device_id_native.dart @@ -0,0 +1,29 @@ +import 'package:analytics_sdk/utils/platform_storage.dart'; +import 'package:analytics_sdk/utils/uuid_util.dart'; +import 'package:flutter/services.dart'; + +const _kDeviceIdStorageKey = 'analytics_device_id.txt'; +const _kNativeChannel = MethodChannel('analytics_sdk'); + +Future generateOrLoadDeviceId() async { + try { + final nativeDeviceId = + (await _kNativeChannel.invokeMethod('getDeviceId'))?.trim() ?? + ''; + if (nativeDeviceId.isNotEmpty) return nativeDeviceId; + } on MissingPluginException { + // Desktop platforms may not implement this method; fall back to storage. + } catch (_) {} + + try { + final existing = + (await PlatformStorage.read(_kDeviceIdStorageKey))?.trim() ?? ''; + if (existing.isNotEmpty) return existing; + + final generated = generateUuidV4(); + await PlatformStorage.write(_kDeviceIdStorageKey, generated); + return generated; + } catch (_) { + return ''; + } +} diff --git a/analytics_sdk/lib/utils/device_id_util.dart b/analytics_sdk/lib/utils/device_id_util.dart new file mode 100644 index 0000000..0f27454 --- /dev/null +++ b/analytics_sdk/lib/utils/device_id_util.dart @@ -0,0 +1,13 @@ +import 'device_id_native.dart' + // ignore: uri_does_not_exist + if (dart.library.html) 'device_id_web.dart'; + +/// SDK 内部 device_id 生成工具。 +/// +/// device_id 不再信任业务方传入值: +/// - Android/iOS 优先读取原生平台稳定 ID; +/// - 其它端降级生成 UUID v4 并持久化; +/// - 任何异常都返回空字符串,由事件校验链路决定是否丢弃。 +class DeviceIdUtil { + static Future getDeviceId() => generateOrLoadDeviceId(); +} diff --git a/analytics_sdk/lib/utils/device_id_web.dart b/analytics_sdk/lib/utils/device_id_web.dart new file mode 100644 index 0000000..3cd9e5c --- /dev/null +++ b/analytics_sdk/lib/utils/device_id_web.dart @@ -0,0 +1,18 @@ +import 'package:analytics_sdk/utils/platform_storage.dart'; +import 'package:analytics_sdk/utils/uuid_util.dart'; + +const _kDeviceIdStorageKey = 'analytics_device_id'; + +Future generateOrLoadDeviceId() async { + try { + final existing = + (await PlatformStorage.read(_kDeviceIdStorageKey))?.trim() ?? ''; + if (existing.isNotEmpty) return existing; + + final generated = generateUuidV4(); + await PlatformStorage.write(_kDeviceIdStorageKey, generated); + return generated; + } catch (_) { + return ''; + } +} diff --git a/analytics_sdk/lib/utils/device_info_native.dart b/analytics_sdk/lib/utils/device_info_native.dart new file mode 100644 index 0000000..c56a323 --- /dev/null +++ b/analytics_sdk/lib/utils/device_info_native.dart @@ -0,0 +1,60 @@ +import 'dart:io'; + +import 'package:device_info_plus/device_info_plus.dart'; + +String? _cachedBrand; +String? _cachedModel; +String? _cachedVersion; + +Future initializeDeviceInfo() async { + try { + final plugin = DeviceInfoPlugin(); + if (Platform.isAndroid) { + final info = await plugin.androidInfo; + _cachedBrand = info.brand; + _cachedModel = info.model; + _cachedVersion = info.version.release; + } else if (Platform.isIOS) { + final info = await plugin.iosInfo; + _cachedBrand = 'Apple'; + _cachedModel = info.model; + } else if (Platform.isMacOS) { + final info = await plugin.macOsInfo; + _cachedBrand = 'MAC'; + _cachedModel = info.model; + } else if (Platform.isWindows) { + final info = await plugin.windowsInfo; + _cachedBrand = ''; + _cachedModel = info.productName; + } + // Linux:无通用品牌/型号,留空 + } catch (_) {} +} + +String getDeviceType() { + if (Platform.isAndroid) return 'Android'; + if (Platform.isIOS) return 'iOS'; + return 'PC'; +} + +String getSystemName() { + if (Platform.isAndroid) return 'Android'; + if (Platform.isIOS) return 'iOS'; + if (Platform.isMacOS) return 'macOS'; + if (Platform.isWindows) return 'Windows'; + if (Platform.isLinux) return 'Linux'; + return ''; +} + +String getSystemVersion() { + if (_cachedVersion != null) return _cachedVersion!; + // 非 Android 平台:从 "macOS 14.5.0" / "Windows 11 ..." 等字符串中提取版本号 + final match = RegExp(r'[\d]+(?:\.[\d]+)*').firstMatch(Platform.operatingSystemVersion); + return match?.group(0) ?? Platform.operatingSystemVersion; +} + +String getDeviceBrand() => _cachedBrand ?? ''; + +String getDeviceModel() => _cachedModel ?? ''; + +String getUserAgent() => ''; diff --git a/analytics_sdk/lib/utils/device_info_util.dart b/analytics_sdk/lib/utils/device_info_util.dart new file mode 100644 index 0000000..8f85147 --- /dev/null +++ b/analytics_sdk/lib/utils/device_info_util.dart @@ -0,0 +1,36 @@ +import 'device_info_native.dart' + // ignore: uri_does_not_exist + if (dart.library.html) 'device_info_web.dart'; + +/// 设备信息工具类:SDK 内部自动检测,业务方无需手动传入。 +/// +/// 使用前须在 SDK 初始化时调用一次 [initialize],之后所有 getter 同步返回缓存值。 +class DeviceInfoUtil { + static bool _initialized = false; + + /// 异步初始化(调用 device_info_plus 获取品牌/型号并缓存)。 + /// 重复调用无副作用。 + static Future initialize() async { + if (_initialized) return; + _initialized = true; + await initializeDeviceInfo(); + } + + /// 设备类型:Android / iOS / PC + static String get deviceType => getDeviceType(); + + /// 操作系统名称:Android / iOS / macOS / Windows / Linux / Web + static String get systemName => getSystemName(); + + /// 操作系统版本号 + static String get systemVersion => getSystemVersion(); + + /// 设备品牌(由 device_info_plus 自动获取) + static String get deviceBrand => getDeviceBrand(); + + /// 设备型号(由 device_info_plus 自动获取) + static String get deviceModel => getDeviceModel(); + + /// User-Agent(Web 端自动读取,Native 端返回空字符串) + static String get userAgent => getUserAgent(); +} diff --git a/analytics_sdk/lib/utils/device_info_web.dart b/analytics_sdk/lib/utils/device_info_web.dart new file mode 100644 index 0000000..2da56f2 --- /dev/null +++ b/analytics_sdk/lib/utils/device_info_web.dart @@ -0,0 +1,86 @@ +// ignore: avoid_web_libraries_in_flutter +import 'dart:html' as html; + +/// Web 端设备信息:浏览器无法稳定获取品牌/型号,系统名和版本从 UA 解析。 +Future initializeDeviceInfo() async {} + +String getDeviceType() { + try { + final ua = html.window.navigator.userAgent; + if (ua.contains('iPhone') || ua.contains('iPad') || ua.contains('iPod')) { + return 'iOS'; + } + if (ua.contains('Android')) return 'Android'; + } catch (_) {} + return 'PC'; +} + +String getSystemName() => _osInfo().name; + +String getSystemVersion() => _osInfo().version; + +String getDeviceBrand() => ''; + +String getDeviceModel() => ''; + +String getUserAgent() { + try { + return html.window.navigator.userAgent; + } catch (_) { + return ''; + } +} + +_OsInfo _osInfo() { + try { + return _parseOsInfo(html.window.navigator.userAgent); + } catch (_) { + return const _OsInfo('', ''); + } +} + +_OsInfo _parseOsInfo(String ua) { + if (ua.contains('iPhone') || ua.contains('iPad') || ua.contains('iPod')) { + final match = + RegExp(r'(?:CPU (?:iPhone )?OS|iPhone OS) ([\d_]+)').firstMatch(ua); + if (match != null) { + return _OsInfo('iOS', _normalizeVersion(match.group(1))); + } + return const _OsInfo('iOS', ''); + } + + if (ua.contains('Android')) { + final match = RegExp(r'Android ([\d.]+)').firstMatch(ua); + if (match != null) return _OsInfo('Android', match.group(1) ?? ''); + return const _OsInfo('Android', ''); + } + + final macMatch = RegExp(r'Mac OS X ([^;)]+)').firstMatch(ua); + if (macMatch != null) { + return _OsInfo('macOS', _normalizeVersion(macMatch.group(1))); + } + + final windowsMatch = RegExp(r'Windows NT ([\d.]+)').firstMatch(ua); + if (windowsMatch != null) { + return _OsInfo('Windows', windowsMatch.group(1) ?? ''); + } + + final chromeOsMatch = RegExp(r'CrOS [^ ]+ ([\d.]+)').firstMatch(ua); + if (chromeOsMatch != null) { + return _OsInfo('ChromeOS', chromeOsMatch.group(1) ?? ''); + } + + if (ua.contains('Linux')) return const _OsInfo('Linux', ''); + return const _OsInfo('', ''); +} + +String _normalizeVersion(String? version) { + return (version ?? '').replaceAll('_', '.').trim(); +} + +class _OsInfo { + final String name; + final String version; + + const _OsInfo(this.name, this.version); +} diff --git a/analytics_sdk/lib/utils/environment_info_native.dart b/analytics_sdk/lib/utils/environment_info_native.dart new file mode 100644 index 0000000..3737900 --- /dev/null +++ b/analytics_sdk/lib/utils/environment_info_native.dart @@ -0,0 +1,119 @@ +import 'dart:io' show Platform; +import 'dart:ui' show window; + +import 'package:battery_plus/battery_plus.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + +/// 原生/桌面端环境字段采集。 +Future> collectEnvironmentInfo() async { + final screenInfo = _screenInfo(); + final batteryState = await _batteryState(); + final fields = { + 'language': window.locale.toLanguageTag(), + 'timezone': DateTime.now().timeZoneName, + ...screenInfo, + 'network_type': await _networkType(), + 'battery_level': await _batteryLevel(), + 'is_charging': _isChargingState(batteryState), + 'battery_state': _batteryStateName(batteryState), + 'cpu_cores': _cpuCores(), + }; + + try { + final info = await PackageInfo.fromPlatform(); + fields['package_name'] = info.packageName; + } catch (_) { + fields['package_name'] = ''; + } + + return fields; +} + +Future _networkType() async { + try { + final result = await Connectivity().checkConnectivity(); + switch (result) { + case ConnectivityResult.wifi: + return 'wifi'; + case ConnectivityResult.ethernet: + return 'ethernet'; + case ConnectivityResult.mobile: + case ConnectivityResult.none: + case ConnectivityResult.bluetooth: + case ConnectivityResult.vpn: + case ConnectivityResult.other: + return 'unknown'; + } + } catch (_) { + return 'unknown'; + } +} + +Future _batteryLevel() async { + try { + return await Battery().batteryLevel; + } catch (_) { + return -1; + } +} + +Future _batteryState() async { + try { + return await Battery().batteryState; + } catch (_) { + return BatteryState.unknown; + } +} + +int _isChargingState(BatteryState state) { + if (state == BatteryState.charging || state == BatteryState.full) return 1; + if (state == BatteryState.discharging) return 0; + return -1; +} + +String _batteryStateName(BatteryState state) { + switch (state) { + case BatteryState.charging: + return 'charging'; + case BatteryState.discharging: + return 'discharging'; + case BatteryState.full: + return 'full'; + case BatteryState.unknown: + return 'unknown'; + } +} + +int _cpuCores() { + try { + return Platform.numberOfProcessors; + } catch (_) { + return -1; + } +} + +Map _screenInfo() { + try { + final size = window.physicalSize; + final width = size.width.round(); + final height = size.height.round(); + final pixelRatio = (window.devicePixelRatio * 100).round(); + final dpi = (window.devicePixelRatio * 160).round(); + return { + 'screen_width': width, + 'screen_height': height, + 'screen_dpi': dpi, + 'device_pixel_ratio': pixelRatio, + 'orientation': width >= height ? 'landscape' : 'portrait', + }; + } catch (_) { + return const { + 'screen_width': -1, + 'screen_height': -1, + 'screen_dpi': -1, + 'device_pixel_ratio': -1, + 'orientation': '', + }; + } +} diff --git a/analytics_sdk/lib/utils/environment_info_util.dart b/analytics_sdk/lib/utils/environment_info_util.dart new file mode 100644 index 0000000..007b27a --- /dev/null +++ b/analytics_sdk/lib/utils/environment_info_util.dart @@ -0,0 +1,291 @@ +import 'dart:async'; + +import 'package:analytics_sdk/entity/network_state_change_event.dart'; +import 'package:analytics_sdk/manager/network_state_fields_stub.dart' + // ignore: uri_does_not_exist + if (dart.library.html) 'package:analytics_sdk/manager/network_state_fields_web.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; +import 'package:analytics_sdk/manager/session_manager.dart'; +import 'package:battery_plus/battery_plus.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:flutter/foundation.dart'; +import 'package:analytics_sdk/utils/network_type_resolver.dart'; + +import 'environment_info_native.dart' + // ignore: uri_does_not_exist + if (dart.library.html) 'environment_info_web.dart'; +import 'runtime_observation_stub.dart' + // ignore: uri_does_not_exist + if (dart.library.io) 'runtime_observation_native.dart' + as runtime_observation; + +/// 系统、网络、硬件和浏览器环境快照字段采集工具。 +class EnvironmentInfoUtil { + static const Duration _batteryRefreshInterval = Duration(seconds: 60); + + static final Battery _battery = Battery(); + static final Connectivity _connectivity = Connectivity(); + static Map _fields = _defaultFields(); + static bool _initialized = false; + static bool _monitoring = false; + static DateTime? _lastBatteryRefreshAt; + static StreamSubscription? _connectivitySubscription; + static StreamSubscription? _batteryStateSubscription; + static runtime_observation.RuntimeObservationMonitor? + _runtimeObservationMonitor; + static String _lastNetworkType = 'unknown'; + static String _networkChangeCountSid = ''; + static int _networkChangeCountInSid = 0; + static void Function(dynamic event)? _trackNetworkStateChange; + + /// 初始化并缓存环境字段。失败时保留默认 unknown/-1 兜底值。 + static Future initialize() async { + if (_initialized) return; + _initialized = true; + try { + _fields = { + ..._defaultFields(), + ...await collectEnvironmentInfo(), + ...await runtime_observation.collectRuntimeObservationFields(), + }; + _lastNetworkType = _stringField(_fields['network_type'], 'unknown'); + _lastBatteryRefreshAt = DateTime.now(); + } catch (_) { + _fields = _defaultFields(); + _lastNetworkType = 'unknown'; + } + } + + /// 启动运行期字段监听,后续变化会同步更新内存公共字段。 + static void startMonitoring({void Function(dynamic event)? track}) { + if (track != null) _trackNetworkStateChange = track; + if (_monitoring) return; + _monitoring = true; + _connectivitySubscription = _connectivity.onConnectivityChanged.listen( + (result) => _handleNetworkChange(result), + onError: (_) => _mergeFields({'network_type': 'unknown'}), + ); + _batteryStateSubscription = _battery.onBatteryStateChanged.listen( + (state) async { + final updates = { + 'is_charging': _mapBatteryState(state), + 'battery_state': _batteryStateName(state), + }; + final level = await _readBatteryLevel(); + if (level >= 0) updates['battery_level'] = level; + _mergeFields(updates); + }, + onError: (_) => _mergeFields({ + 'is_charging': -1, + 'battery_state': 'unknown', + }), + ); + _runtimeObservationMonitor = + runtime_observation.startRuntimeObservationMonitoring(_mergeFields); + } + + /// 按最小间隔刷新电量百分比,避免为了字段新鲜度持续轮询。 + static Future refreshBatteryLevelIfNeeded({bool force = false}) async { + final now = DateTime.now(); + final last = _lastBatteryRefreshAt; + if (!force && + last != null && + now.difference(last) < _batteryRefreshInterval) { + return; + } + final level = await _readBatteryLevel(); + if (level < 0) return; + _lastBatteryRefreshAt = now; + _mergeFields({'battery_level': level}); + } + + /// 主动刷新低频运行态快照,通常在前台恢复时调用。 + static Future refreshRuntimeObservationFields() async { + try { + _mergeFields(await runtime_observation.collectRuntimeObservationFields()); + } catch (_) {} + } + + /// 停止监听并清理订阅。 + static Future dispose() async { + _monitoring = false; + await _connectivitySubscription?.cancel(); + await _batteryStateSubscription?.cancel(); + await _runtimeObservationMonitor?.dispose(); + _connectivitySubscription = null; + _batteryStateSubscription = null; + _runtimeObservationMonitor = null; + _trackNetworkStateChange = null; + } + + /// 当前已缓存的环境快照字段。 + static Map get fields => Map.from(_fields); + + static void _mergeFields(Map fields) { + if (fields.isEmpty) return; + _fields = { + ..._fields, + ...fields, + }; + SdkContext.updateEnvironmentFields(fields); + } + + static Future _handleNetworkChange(ConnectivityResult result) async { + final webConnectionFields = + kIsWeb ? await collectNetworkStateFields() : const {}; + final toNetworkType = kIsWeb + ? resolveWebNetworkType( + connectionType: _stringField( + webConnectionFields['connection_type'], + 'unknown', + ), + effectiveType: _stringField( + webConnectionFields['effective_type'], + 'unknown', + ), + ) + : _mapNetworkType(result); + final fromNetworkType = _lastNetworkType; + _mergeFields({'network_type': toNetworkType}); + if (toNetworkType == fromNetworkType) return; + + _lastNetworkType = toNetworkType; + final track = _trackNetworkStateChange; + if (track == null) return; + + try { + track(NetworkStateChangeEvent( + networkType: toNetworkType, + fromNetworkType: fromNetworkType, + isOnline: isOnlineForNetworkType(toNetworkType), + changeCountInSid: _nextNetworkChangeCountInSid(), + carrier: kIsWeb + ? '' + : _stringField( + SdkContext.getEnvironmentSnapshotFields()['carrier'], + '', + ), + webConnectionFields: kIsWeb ? webConnectionFields : const {}, + )); + } catch (_) { + // 网络变化是低优先级自动事件,采集失败不得影响业务。 + } + } + + static String _mapNetworkType(ConnectivityResult result) { + switch (result) { + case ConnectivityResult.wifi: + return 'wifi'; + case ConnectivityResult.ethernet: + return 'ethernet'; + case ConnectivityResult.mobile: + return 'cellular'; + case ConnectivityResult.none: + return 'offline'; + case ConnectivityResult.bluetooth: + case ConnectivityResult.vpn: + case ConnectivityResult.other: + return 'unknown'; + } + } + + static int _mapBatteryState(BatteryState state) { + if (state == BatteryState.charging || state == BatteryState.full) return 1; + if (state == BatteryState.discharging) return 0; + return -1; + } + + static String _batteryStateName(BatteryState state) { + switch (state) { + case BatteryState.charging: + return 'charging'; + case BatteryState.discharging: + return 'discharging'; + case BatteryState.full: + return 'full'; + case BatteryState.unknown: + return 'unknown'; + } + } + + static Future _readBatteryLevel() async { + try { + return await _battery.batteryLevel; + } catch (_) { + return -1; + } + } + + static int _isOnline(String networkType) { + return isOnlineForNetworkType(networkType); + } + + static int _nextNetworkChangeCountInSid() { + final sid = SessionManager.instance.currentSessionId ?? ''; + if (sid != _networkChangeCountSid) { + _networkChangeCountSid = sid; + _networkChangeCountInSid = 0; + } + _networkChangeCountInSid += 1; + return _networkChangeCountInSid; + } + + static String _stringField(dynamic value, String fallback) { + final text = value?.toString().trim() ?? ''; + return text.isEmpty ? fallback : text; + } + + @visibleForTesting + static Future handleNetworkChangeForTest( + ConnectivityResult result, { + required void Function(dynamic event) track, + }) async { + final previous = _trackNetworkStateChange; + _trackNetworkStateChange = track; + try { + await _handleNetworkChange(result); + } finally { + _trackNetworkStateChange = previous; + } + } + + @visibleForTesting + static void resetNetworkChangeStateForTest({ + String networkType = 'unknown', + String sid = '', + }) { + _lastNetworkType = networkType; + _networkChangeCountSid = sid; + _networkChangeCountInSid = 0; + _trackNetworkStateChange = null; + _mergeFields({'network_type': networkType}); + } + + static Map _defaultFields() => { + 'language': '', + 'timezone': '', + 'package_name': '', + 'screen_width': -1, + 'screen_height': -1, + 'screen_dpi': -1, + 'device_pixel_ratio': -1, + 'orientation': '', + 'screen_brightness': -1, + 'volume': -1, + 'is_screen_on': -1, + 'network_type': 'unknown', + 'battery_level': -1, + 'is_charging': -1, + 'battery_state': 'unknown', + 'cpu_cores': -1, + 'device_memory_gb': -1, + 'browser_name': '', + 'browser_version': '', + 'cookie_enabled': -1, + 'local_storage_enabled': -1, + 'touch_support': -1, + 'max_touch_points': -1, + 'color_depth': -1, + 'is_pwa': -1, + }; +} diff --git a/analytics_sdk/lib/utils/environment_info_web.dart b/analytics_sdk/lib/utils/environment_info_web.dart new file mode 100644 index 0000000..dfe150a --- /dev/null +++ b/analytics_sdk/lib/utils/environment_info_web.dart @@ -0,0 +1,181 @@ +// ignore: avoid_web_libraries_in_flutter +import 'dart:html' as html; + +import 'package:analytics_sdk/manager/network_state_fields_web.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:analytics_sdk/utils/js_interop_compat.dart'; +import 'package:analytics_sdk/utils/network_type_resolver.dart'; +import 'package:battery_plus/battery_plus.dart'; + +/// Web 端环境字段采集。 +Future> collectEnvironmentInfo() async { + final navigator = html.window.navigator; + final screen = html.window.screen; + final browser = _parseBrowser(navigator.userAgent); + final batteryState = await _batteryState(); + + return { + 'language': navigator.language, + 'timezone': _timezoneIdentifier(), + 'package_name': await _packageName(), + 'screen_width': screen?.width ?? -1, + 'screen_height': screen?.height ?? -1, + 'screen_dpi': -1, + 'orientation': _orientation(), + 'viewport_width': html.window.innerWidth ?? -1, + 'viewport_height': html.window.innerHeight ?? -1, + 'device_pixel_ratio': (html.window.devicePixelRatio * 100).round(), + 'network_type': await _networkType(), + 'battery_level': await _batteryLevel(), + 'is_charging': _isChargingState(batteryState), + 'battery_state': _batteryStateName(batteryState), + 'cpu_cores': navigator.hardwareConcurrency ?? -1, + 'device_memory_gb': _deviceMemoryGb(navigator), + 'browser_name': browser.name, + 'browser_version': browser.version, + 'cookie_enabled': navigator.cookieEnabled == true ? 1 : 0, + 'local_storage_enabled': _localStorageEnabled(), + 'touch_support': (navigator.maxTouchPoints ?? 0) > 0 ? 1 : 0, + 'color_depth': screen?.colorDepth ?? -1, + 'is_pwa': _isPwa(), + }; +} + +String _timezoneIdentifier() { + try { + final intl = getJsProperty(html.window, 'Intl'); + if (intl == null) return DateTime.now().timeZoneName; + final formatter = callJsMethod(intl, 'DateTimeFormat', const []); + if (formatter == null) return DateTime.now().timeZoneName; + final options = callJsMethod(formatter, 'resolvedOptions', const []); + if (options == null) return DateTime.now().timeZoneName; + final timezone = + getJsProperty(options, 'timeZone')?.toString().trim() ?? ''; + if (timezone.isNotEmpty) return timezone; + } catch (_) {} + return DateTime.now().timeZoneName; +} + +Future _packageName() async { + try { + final info = await PackageInfo.fromPlatform(); + return info.packageName; + } catch (_) { + return ''; + } +} + +Future _networkType() async { + try { + final connection = await collectNetworkStateFields(); + return resolveWebNetworkType( + connectionType: connection['connection_type']?.toString() ?? 'unknown', + effectiveType: connection['effective_type']?.toString() ?? 'unknown', + ); + } catch (_) { + return 'unknown'; + } +} + +Future _batteryLevel() async { + try { + return await Battery().batteryLevel; + } catch (_) { + return -1; + } +} + +Future _batteryState() async { + try { + return await Battery().batteryState; + } catch (_) { + return BatteryState.unknown; + } +} + +int _isChargingState(BatteryState state) { + if (state == BatteryState.charging || state == BatteryState.full) return 1; + if (state == BatteryState.discharging) return 0; + return -1; +} + +String _batteryStateName(BatteryState state) { + switch (state) { + case BatteryState.charging: + return 'charging'; + case BatteryState.discharging: + return 'discharging'; + case BatteryState.full: + return 'full'; + case BatteryState.unknown: + return 'unknown'; + } +} + +String _orientation() { + try { + final width = html.window.innerWidth ?? 0; + final height = html.window.innerHeight ?? 0; + if (width <= 0 || height <= 0) return ''; + return width >= height ? 'landscape' : 'portrait'; + } catch (_) { + return ''; + } +} + +int _deviceMemoryGb(html.Navigator navigator) { + try { + final memory = getJsProperty(navigator, 'deviceMemory'); + return memory is num ? memory.round() : -1; + } catch (_) { + return -1; + } +} + +int _localStorageEnabled() { + try { + const key = '__analytics_sdk_storage_test__'; + html.window.localStorage[key] = '1'; + final ok = html.window.localStorage[key] == '1'; + html.window.localStorage.remove(key); + return ok ? 1 : 0; + } catch (_) { + return 0; + } +} + +int _isPwa() { + try { + final standalone = html.window + .matchMedia('(display-mode: standalone)') + .matches; + final iosStandalone = + getJsProperty(html.window.navigator, 'standalone') == true; + return standalone || iosStandalone ? 1 : 0; + } catch (_) { + return -1; + } +} + +_BrowserInfo _parseBrowser(String ua) { + final rules = { + 'Edge': RegExp(r'Edg/([\d.]+)'), + 'Chrome': RegExp(r'Chrome/([\d.]+)'), + 'Firefox': RegExp(r'Firefox/([\d.]+)'), + 'Safari': RegExp(r'Version/([\d.]+).*Safari'), + }; + for (final entry in rules.entries) { + final match = entry.value.firstMatch(ua); + if (match != null) { + return _BrowserInfo(entry.key, match.group(1) ?? ''); + } + } + return const _BrowserInfo('', ''); +} + +class _BrowserInfo { + final String name; + final String version; + + const _BrowserInfo(this.name, this.version); +} diff --git a/analytics_sdk/lib/utils/event_validator.dart b/analytics_sdk/lib/utils/event_validator.dart new file mode 100644 index 0000000..c83ab72 --- /dev/null +++ b/analytics_sdk/lib/utils/event_validator.dart @@ -0,0 +1,592 @@ +import 'package:analytics_sdk/manager/session_manager.dart'; +import 'package:analytics_sdk/manager/sdk_context.dart'; +import 'package:analytics_sdk/utils/logger.dart'; + +/// 事件字段校验器 +/// +/// 校验规则分两类: +/// +/// 1. **关键字段**:校验失败直接丢弃整个事件,[validate] 返回 `null`。 +/// 2. **可纠正字段**:自动截断/转换/填充,记录日志后继续上报。 +class EventValidator { + // ──────── 公共字段长度限制 ──────── + static const int _maxEventLen = 64; + static const int _maxAppIdLen = 64; + static const int _maxShortLen = 128; + static const int _maxFpLen = 128; + static const int _maxUaLen = 512; + + // ──────── timestamp 合法范围(2000-01-01 ~ 2100-01-01) ──────── + static const int _minTs = 946684800; + static const int _maxTs = 4102444800; + + // ──────── 正则 ──────── + static final RegExp _eventPattern = RegExp(r'^[a-z0-9_]+$'); + static final RegExp _eventIdPattern = RegExp(r'^[A-Za-z0-9]{1,32}$'); + static final RegExp _eventSigPattern = RegExp(r'^[a-f0-9]{64}$'); + static final RegExp _keyPattern = RegExp(r'^[A-Za-z0-9_]+$'); + + // ──────── payload 字段分类 ──────── + + /// 关键 ID 字段:校验失败丢弃整个事件 + static const Set _criticalIdFields = { + 'video_id', + 'novel_id', + 'comic_id', + 'click_item_id', + }; + + /// 普通 ID 字段:校验失败仅移除该字段 + /// 注意:ad_id / recommend_id 已在 _commaSeparatedFields 中处理,不重复列入 + static const Set _normalIdFields = { + 'order_id', + 'item_id', + 'page_id', + }; + + /// 逗号分隔字段(分割 → trim → 过滤空 → 重新拼接) + static const Set _commaSeparatedFields = { + 'ad_id', + }; + + /// 名称字段,最大 128 字符 + static const Set _nameFields = { + 'page_name', + 'referrer_page_name', + 'current_page_name', + 'source_page_name', + 'item_name', + 'tab_name', + 'product_name', + 'video_type_name', + 'novel_type_name', + 'comic_type_name', + 'navigation_name', + 'advertising_name', + 'video_behavior_name', + 'novel_behavior_name', + 'comic_behavior_name', + 'consume_reason_name', + 'vip_duration_name', + 'click_item_type_name', + }; + + /// 标题字段,最大 256 字符 + static const Set _titleFields = { + 'video_title', + 'novel_title', + 'comic_title', + }; + + /// 关键词/评论字段,最大 500 字符 + static const Set _keywordFields = { + 'keyword', + 'comment_content', + }; + + /// 百分比字段,范围 0~100 + static const Set _percentageFields = { + 'play_progress', + 'read_progress', + 'click_x_percent', + 'click_y_percent', + }; + + /// 位置字段,值 ≥ 1 + static const Set _positionFields = { + 'page_no', + 'click_position', + }; + + /// 非负数字字段,值 ≥ 0 + static const Set _numericFields = { + 'play_duration', + 'video_duration', + 'coin_quantity', + 'coin_consume_amount', + 'coin_balance_before', + 'coin_balance_after', + 'amount', + 'search_result_count', + 'screen_width', + 'screen_height', + 'screen_dpi', + 'page_load_time', + 'flag', + 'available_storage', + 'storage', + 'fill_count', + 'load_duration', + 'position', + }; + + /// 时间戳字段(范围同 client_ts) + static const Set _timestampFields = { + 'create_time', + 'vip_expiration_time', + }; + + // ───────────────────────────────────────────────────────── + // 公开 API + // ───────────────────────────────────────────────────────── + + /// 校验并自动纠正事件 Map。 + /// + /// 返回纠正后的 Map;若关键字段无法修复则返回 `null`(调用方应丢弃该事件)。 + /// 本方法永远不会抛出异常。 + static Map? validate(Map event) { + try { + final result = Map.from(event); + + // ── 1. event ───────────────────────────────────────── + // 必填,trim,正则 ^[a-z0-9_]+$,最大 64 字符;不符合 → 丢弃 + final rawEvent = _toStr(result['event']); + if (rawEvent == null) { + Logger.analyticsSdk('校验失败:event 字段缺失或类型错误,丢弃事件', level: LogLevel.warn); + return null; + } + if (!_eventPattern.hasMatch(rawEvent) || rawEvent.length > _maxEventLen) { + Logger.analyticsSdk('校验失败:event "$rawEvent" 格式不合规或超长,丢弃事件', + level: LogLevel.warn); + return null; + } + result['event'] = rawEvent; + + // ── 2. event_id ────────────────────────────────────── + // 必填,trim,正则 ^[A-Za-z0-9]{1,32}$;不符合 → 丢弃 + final rawEventId = _toStr(result['event_id']); + if (rawEventId == null || rawEventId.isEmpty) { + Logger.analyticsSdk('校验失败:event_id 字段缺失或为空,丢弃事件', level: LogLevel.warn); + return null; + } + if (!_eventIdPattern.hasMatch(rawEventId)) { + Logger.analyticsSdk('校验失败:event_id "$rawEventId" 格式不合规,丢弃事件', + level: LogLevel.warn); + return null; + } + result['event_id'] = rawEventId; + + // ── 3. app_id ──────────────────────────────────────── + // 必填,不能为空,最大 64 字符;空或缺失 → 丢弃;超长 → 截断 + final rawAppId = _toStr(result['app_id']); + if (rawAppId == null || rawAppId.isEmpty) { + Logger.analyticsSdk('校验失败:app_id 字段缺失或为空,丢弃事件', level: LogLevel.warn); + return null; + } + result['app_id'] = _trimTrunc(rawAppId, _maxAppIdLen); + + // ── 4. channel ─────────────────────────────────────── + // 可选,trim,最大 128;超长截断 + final rawChannel = _toStr(result['channel']); + if (rawChannel != null) { + result['channel'] = _trimTrunc(rawChannel, _maxShortLen); + } + + // ── 5. uid ─────────────────────────────────────────── + // 可选,trim,最大 128;超长截断 + final rawUid = _toStr(result['uid']); + if (rawUid != null) { + result['uid'] = _trimTrunc(rawUid, _maxShortLen); + } + + // ── 6. sid ─────────────────────────────────────────── + // 必填;空/缺失 → 自动生成;生成后仍为空 → 丢弃 + String sid = _toStr(result['sid']) ?? ''; + if (sid.isEmpty) { + try { + sid = SessionManager.instance.getSessionId(); + Logger.analyticsSdk('自动纠正:sid 为空,已自动生成: $sid'); + } catch (e) { + Logger.analyticsSdk('自动纠正:sid 生成失败: $e', level: LogLevel.warn); + sid = ''; + } + } + if (sid.isEmpty) { + Logger.analyticsSdk('校验失败:sid 无法获取,丢弃事件', level: LogLevel.warn); + return null; + } + result['sid'] = sid; + + // ── 7. client_ts ───────────────────────────────────── + // 必填,整数,范围 946684800~4102444800;字符串可尝试转换;超出范围 → 丢弃 + final tsVal = _parseTs(result['client_ts']); + if (tsVal == null) { + Logger.analyticsSdk('校验失败:client_ts 无效或超出范围,丢弃事件', + level: LogLevel.warn); + return null; + } + result['client_ts'] = tsVal; + + // ── 8. device ──────────────────────────────────────── + // 仅 Android/iOS/PC;大小写自动纠正;其他值 → 丢弃;空字符串视为未设置,跳过 + final rawDevice = _toStr(result['device']); + if (rawDevice != null && rawDevice.isNotEmpty) { + final normalized = _normalizeDevice(rawDevice); + if (normalized == null) { + Logger.analyticsSdk( + '校验失败:device "$rawDevice" 不合规(须为 Android/iOS/PC),丢弃事件', + level: LogLevel.warn); + return null; + } + result['device'] = normalized; + } + + // ── 9. device_id ───────────────────────────────────── + // 必填,trim,1~128;空或缺失 → 丢弃;超长截断 + final rawDeviceId = _toStr(result['device_id']); + if (rawDeviceId == null || rawDeviceId.isEmpty) { + Logger.analyticsSdk('校验失败:device_id 字段缺失或为空,丢弃事件', + level: LogLevel.warn); + return null; + } + result['device_id'] = _trimTrunc(rawDeviceId, _maxShortLen); + + // ── 10. user_agent ─────────────────────────────────── + // 可选,trim,超长截断(512,规范要求) + final rawUa = _toStr(result['user_agent']); + if (rawUa != null) { + result['user_agent'] = _trimTrunc(rawUa, _maxUaLen); + } + + // ── 11. device_brand / device_model / system_name / system_version ── + // 可选,trim,超长截断(128) + for (final field in [ + 'device_brand', + 'device_model', + 'system_name', + 'system_version' + ]) { + final v = _toStr(result[field]); + if (v != null) { + result[field] = _trimTrunc(v, _maxShortLen); + } + } + + // ── 12. sdk_version / app_version ──────────────────── + // 可选,trim,最大 128 字符;超长截断 + for (final field in ['sdk_version', 'app_version']) { + final v = _toStr(result[field]); + if (v != null) { + result[field] = _trimTrunc(v, _maxShortLen); + } + } + + // ── 12.1 system / app / network environment ───────── + for (final field in [ + 'language', + 'timezone', + 'package_name', + 'network_type', + 'browser_name', + 'browser_version', + ]) { + final v = _toStr(result[field]); + if (v != null) { + result[field] = _trimTrunc(v, _maxShortLen); + } + } + + // ── 12.2 hardware / browser numeric environment ───── + for (final field in [ + 'battery_level', + 'is_charging', + 'cpu_cores', + 'device_memory_gb', + 'cookie_enabled', + 'local_storage_enabled', + 'touch_support', + 'color_depth', + 'is_pwa', + ]) { + final v = _parseInt(result[field]); + if (v != null) result[field] = v; + } + + // ── 13. device_fingerprint ─────────────────────────── + // SDK 管控字段,不接受外部覆盖。 + final rawFp = _toStr(result['device_fingerprint']); + final hasDeviceFingerprintField = + result.containsKey('device_fingerprint'); + if (SdkContext.deviceFingerprint.isNotEmpty || + hasDeviceFingerprintField) { + final sdkFp = _trimTrunc(SdkContext.deviceFingerprint, _maxFpLen); + if (rawFp != sdkFp) { + Logger.analyticsSdk('自动纠正:device_fingerprint 不允许外部覆盖,已改为 SDK 内部值'); + } + result['device_fingerprint'] = sdkFp; + } + + // ── 14. fp_version ─────────────────────────────────── + // fp_version 是 SDK 指纹规则版本,不接受外部覆盖。 + final fp = (result['device_fingerprint'] as String?) ?? ''; + final hasFpVersionField = result.containsKey('fp_version'); + if (fp.isNotEmpty || hasFpVersionField || hasDeviceFingerprintField) { + final fpVer = _toStr(result['fp_version']) ?? ''; + if (fpVer != SdkContext.fingerprintVersion) { + result['fp_version'] = SdkContext.fingerprintVersion; + if (fpVer.isEmpty) { + Logger.analyticsSdk( + '自动纠正:fp_version 为空,已填充默认版本 ${SdkContext.fingerprintVersion}'); + } else { + Logger.analyticsSdk( + '自动纠正:fp_version=$fpVer 不允许外部覆盖,已改为 ${SdkContext.fingerprintVersion}'); + } + } + } + + // ── 14.1 event_sig / event_sig_version ─────────────── + // 客户端签名字段由 SdkContext 生成;校验器只规范化已存在字段。 + if (result.containsKey('event_sig')) { + final rawEventSig = _toStr(result['event_sig']) ?? ''; + result['event_sig'] = + _eventSigPattern.hasMatch(rawEventSig) ? rawEventSig : ''; + } + + if (result.containsKey('event_sig_version')) { + final rawEventSigVersion = _toStr(result['event_sig_version']) ?? ''; + result['event_sig_version'] = + _trimTrunc(rawEventSigVersion, _maxShortLen); + } + + // ── 15. payload ────────────────────────────────────── + // 可为 null(补空 Map),类型错误 → 丢弃 + final rawPayload = result['payload']; + if (rawPayload == null) { + result['payload'] = {}; + } else if (rawPayload is! Map) { + Logger.analyticsSdk('校验失败:payload 类型错误(${rawPayload.runtimeType}),丢弃事件', + level: LogLevel.warn); + return null; + } else { + final validated = _validatePayload( + Map.from(rawPayload), + eventType: result['event'] as String, + ); + if (validated == null) return null; // 关键 ID 字段校验失败 + result['payload'] = validated; + } + + return result; + } catch (e) { + Logger.analyticsSdk('事件校验内部异常: $e,丢弃事件', level: LogLevel.warn); + return null; + } + } + + // ───────────────────────────────────────────────────────── + // payload 业务字段校验 + // ───────────────────────────────────────────────────────── + + /// 校验 payload 业务字段。 + /// 返回纠正后的 Map;若关键 ID 字段不合规则返回 `null`(丢弃事件)。 + static Map? _validatePayload( + Map payload, { + required String eventType, + }) { + final result = {}; + + for (final entry in payload.entries) { + final key = entry.key; + final value = entry.value; + + // key 本身须符合 ^[A-Za-z0-9_]+$ + if (!_keyPattern.hasMatch(key)) { + Logger.analyticsSdk('自动纠正:payload 字段名 "$key" 不合规,已移除', + level: LogLevel.warn); + continue; + } + + // ── 逗号分隔字段 ───────────────────────────────────── + if (_commaSeparatedFields.contains(key)) { + final s = _toStr(value); + if (s == null) { + Logger.analyticsSdk('自动纠正:payload.$key 类型错误,已移除'); + continue; + } + final cleaned = _cleanComma(s); + if (cleaned.isEmpty) { + // 逗号分隔字段清洗后为空,视为无效字段移除 + Logger.analyticsSdk('自动纠正:payload.$key 清洗后为空,已移除'); + continue; + } + result[key] = cleaned; + continue; + } + + // ── 关键 ID 字段 ───────────────────────────────────── + if (_criticalIdFields.contains(key)) { + final s = _toStr(value); + if (s == null || s.isEmpty) { + Logger.analyticsSdk('校验失败:payload.$key(关键ID)为空或类型错误,丢弃事件', + level: LogLevel.warn); + return null; + } + result[key] = s; + continue; + } + + // ── 普通 ID 字段 ───────────────────────────────────── + if (_normalIdFields.contains(key)) { + final s = _toStr(value); + if (s == null || s.isEmpty) { + Logger.analyticsSdk('自动纠正:payload.$key(ID字段)为空或类型错误,已移除'); + continue; + } + result[key] = s; + continue; + } + + // ── 名称字段(最大 128) ────────────────────────────── + if (_nameFields.contains(key)) { + final s = _toStr(value); + if (s == null) { + Logger.analyticsSdk('自动纠正:payload.$key 类型错误,已移除'); + continue; + } + result[key] = _trimTrunc(s, 128); + continue; + } + + // ── 标题字段(最大 256) ────────────────────────────── + if (_titleFields.contains(key)) { + final s = _toStr(value); + if (s == null) { + Logger.analyticsSdk('自动纠正:payload.$key 类型错误,已移除'); + continue; + } + result[key] = _trimTrunc(s, 256); + continue; + } + + // ── 关键词字段(最大 500) ──────────────────────────── + if (_keywordFields.contains(key)) { + final s = _toStr(value); + if (s == null) { + Logger.analyticsSdk('自动纠正:payload.$key 类型错误,已移除'); + continue; + } + result[key] = _trimTrunc(s, 500); + continue; + } + + // ── 百分比字段(0~100) ─────────────────────────────── + if (_percentageFields.contains(key)) { + final n = _parseInt(value); + if (n == null || n < 0 || n > 100) { + Logger.analyticsSdk('自动纠正:payload.$key 百分比值无效($value),已移除'); + continue; + } + result[key] = n; + continue; + } + + // ── 位置字段(≥ 1) ─────────────────────────────────── + if (_positionFields.contains(key)) { + final n = _parseInt(value); + if (n == null || n < 1) { + Logger.analyticsSdk('自动纠正:payload.$key 位置值无效($value),已移除'); + continue; + } + result[key] = n; + continue; + } + + // ── 数字字段(≥ 0) ─────────────────────────────────── + if (_numericFields.contains(key)) { + final n = _parseInt(value); + final min = eventType == 'sdk_init' ? -1 : 0; + if (n == null || n < min) { + Logger.analyticsSdk('自动纠正:payload.$key 数字值无效($value),已移除'); + continue; + } + result[key] = n; + continue; + } + + // ── 时间戳字段(范围校验) ───────────────────────────── + if (_timestampFields.contains(key)) { + final ts = _parseTs(value); + if (ts == null) { + Logger.analyticsSdk('自动纠正:payload.$key 时间戳无效($value),已移除'); + continue; + } + result[key] = ts; + continue; + } + + // ── 其他字段(字符串超长截断,非字符串保留原值) ─────── + if (value is String && value.length > 500) { + result[key] = value.substring(0, 500); + Logger.analyticsSdk('自动纠正:payload.$key 超长,已截断至500字符'); + } else { + result[key] = value; + } + } + + return result; + } + + // ───────────────────────────────────────────────────────── + // 工具方法 + // ───────────────────────────────────────────────────────── + + /// 将 dynamic 转为 trim 后的 String + /// + /// - String → trim + /// - num / bool → toString().trim()(业务方可能传入 int 类型的 uid、channel 等) + /// - 其他类型 → null + static String? _toStr(dynamic v) { + if (v == null) return null; + if (v is String) return v.trim(); + if (v is num || v is bool) return v.toString().trim(); + return null; + } + + /// 截断字符串到最大长度(已是 trim 后的值) + static String _trimTrunc(String s, int max) { + return s.length > max ? s.substring(0, max) : s; + } + + /// 将 device 字段大小写标准化,返回 null 表示无法识别 + static String? _normalizeDevice(String raw) { + final lower = raw.trim().toLowerCase(); + if (lower == 'android') return 'Android'; + if (lower == 'ios') return 'iOS'; + if (lower == 'pc') return 'PC'; + return null; + } + + /// 解析整数(支持 int 和数字字符串) + static int? _parseInt(dynamic v) { + if (v is int) return v; + if (v is double) return v.toInt(); + if (v is String) return int.tryParse(v.trim()); + return null; + } + + /// 解析时间戳,范围须在 [_minTs, _maxTs];支持 int / double / 数字字符串 + /// + /// 注意:Dart Web 上 int 与 double 统一为 JS number,显式处理 double + /// 以保证 Native 与 Web 行为一致。 + static int? _parseTs(dynamic v) { + int? ts; + if (v is int) { + ts = v; + } else if (v is double) { + if (v.isFinite) ts = v.toInt(); + } else if (v is String) { + ts = int.tryParse(v.trim()); + } + if (ts == null || ts < _minTs || ts > _maxTs) return null; + return ts; + } + + /// 清理逗号分隔字符串:各段 trim、过滤空段、重新拼接 + static String _cleanComma(String raw) { + return raw + .split(',') + .map((s) => s.trim()) + .where((s) => s.isNotEmpty) + .join(','); + } +} diff --git a/analytics_sdk/lib/utils/js_interop_compat.dart b/analytics_sdk/lib/utils/js_interop_compat.dart new file mode 100644 index 0000000..2efc663 --- /dev/null +++ b/analytics_sdk/lib/utils/js_interop_compat.dart @@ -0,0 +1,2 @@ +export 'js_interop_legacy.dart' + if (dart.library.js_interop) 'js_interop_modern.dart'; diff --git a/analytics_sdk/lib/utils/js_interop_legacy.dart b/analytics_sdk/lib/utils/js_interop_legacy.dart new file mode 100644 index 0000000..07a5182 --- /dev/null +++ b/analytics_sdk/lib/utils/js_interop_legacy.dart @@ -0,0 +1,23 @@ +// ignore_for_file: uri_does_not_exist + +import 'dart:js_util' as js_util; + +/// Dynamic JavaScript interop helpers for Dart 2.17 / Flutter 3.0. +Object? getJsProperty(Object object, Object property) => + js_util.getProperty(object, property); + +bool hasJsProperty(Object object, String property) => + js_util.hasProperty(object, property); + +Object? callJsMethod( + Object object, + String method, [ + List arguments = const [], +]) => + js_util.callMethod(object, method, arguments); + +Object? constructJsObject( + Object constructor, [ + List arguments = const [], +]) => + js_util.callConstructor(constructor, arguments); diff --git a/analytics_sdk/lib/utils/js_interop_modern.dart b/analytics_sdk/lib/utils/js_interop_modern.dart new file mode 100644 index 0000000..ec31db0 --- /dev/null +++ b/analytics_sdk/lib/utils/js_interop_modern.dart @@ -0,0 +1,57 @@ +// ignore_for_file: undefined_class, undefined_identifier, undefined_method, uri_does_not_exist + +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; + +/// Dynamic JavaScript interop helpers for Dart 3.10 and WebAssembly builds. +Object? getJsProperty(Object object, Object property) { + final value = (object as JSObject).getProperty( + _propertyToJs(property), + ); + return _toDartValue(value); +} + +bool hasJsProperty(Object object, String property) => + (object as JSObject).has(property); + +Object? callJsMethod( + Object object, + String method, [ + List arguments = const [], +]) { + final value = (object as JSObject).callMethodVarArgs( + method.toJS, + arguments.map(_valueToJs).toList(), + ); + return _toDartValue(value); +} + +Object? constructJsObject( + Object constructor, [ + List arguments = const [], +]) => + (constructor as JSFunction).callAsConstructorVarArgs( + arguments.map(_valueToJs).toList(), + ); + +JSAny _propertyToJs(Object property) { + if (property is String) return property.toJS; + if (property is num) return property.toJS; + throw ArgumentError.value(property, 'property', 'Must be a string or number'); +} + +JSAny? _valueToJs(Object? value) { + if (value == null) return null; + if (value is JSAny) return value; + if (value is String) return value.toJS; + if (value is num) return value.toJS; + if (value is bool) return value.toJS; + throw ArgumentError.value(value, 'value', 'Unsupported JavaScript argument'); +} + +Object? _toDartValue(JSAny? value) { + if (value is JSString) return value.toDart; + if (value is JSNumber) return value.toDartDouble; + if (value is JSBoolean) return value.toDart; + return value; +} diff --git a/analytics_sdk/lib/utils/logger.dart b/analytics_sdk/lib/utils/logger.dart new file mode 100644 index 0000000..e4e13d4 --- /dev/null +++ b/analytics_sdk/lib/utils/logger.dart @@ -0,0 +1,218 @@ +import 'dart:developer' as developer; + +import 'package:flutter/foundation.dart'; + +/// 日志级别枚举 +enum LogLevel { + debug, + info, + warn, + error, +} + +/// 日志工具类:统一管理 SDK 中的所有日志输出 +class Logger { + Logger._(); + + /// 是否启用日志(默认:仅在 debug 模式下启用) + static bool enabled = false; + + /// 外部日志回调(可选)。 + /// 注册后,所有 SDK 日志在控制台输出的同时也会转发给此回调。 + /// 示例:在 example app 中将 SDK 内部日志显示到 UI 面板。 + static void Function(String message, LogLevel level)? onLog; + + /// 测试专用:替换 dart:developer.log,便于验证长日志走 developer log。 + @visibleForTesting + static void Function(String message, {String name})? developerLogWriterForTest; + + /// 测试专用:替换 debugPrint,便于验证长日志分片输出到控制台/logcat。 + @visibleForTesting + static void Function(String message)? consoleLogWriterForTest; + + /// Android logcat 单行有长度限制,保守按字符分片,避免超长 JSON 被截断。 + static const int _consoleChunkSize = 800; + + /// 日志标签映射 + static const Map _tags = { + 'AnalyticsSdk': '[AnalyticsSdk]', + 'DomainManager': '[DomainManager]', + 'EventTypeConfigManager': '[EventTypeConfigManager]', + 'GlobalClick': '[GlobalClick]', + 'AdImpressionManager': '[AdImpressionManager]', + 'PageLifecycleObserver': '[PageLifecycleObserver]', + 'InitConfigResult': '[InitConfigResult]', + }; + + /// 获取日志标签 + static String _getTag(String tag) { + return _tags[tag] ?? '[$tag]'; + } + + /// 格式化日志消息 + static String _formatMessage(String tag, String message) { + return '${_getTag(tag)} $message'; + } + + /// 输出到 Flutter 控制台;Android 上会进入 logcat 的 flutter 日志流。 + static void _writeConsole(String message) { + final writer = consoleLogWriterForTest; + if (writer != null) { + writer(message); + } else { + debugPrint(message); + } + } + + /// 分片输出超长日志,避免 logcat / IDE 控制台按单行截断。 + static void _writeConsoleMessage(String tag, String message) { + if (message.length <= _consoleChunkSize) { + _writeConsole(_formatMessage(tag, message)); + return; + } + + final tagPrefix = _getTag(tag); + final total = (message.length + _consoleChunkSize - 1) ~/ _consoleChunkSize; + for (var index = 0; index < total; index++) { + final start = index * _consoleChunkSize; + final end = (start + _consoleChunkSize < message.length) ? start + _consoleChunkSize : message.length; + _writeConsole( + '$tagPrefix part ${index + 1}/$total: ${message.substring(start, end)}', + ); + } + } + + /// 输出调试日志 + static void debug(String tag, String message) { + if (enabled) { + final msg = _formatMessage(tag, message); + _writeConsoleMessage(tag, message); + try { + onLog?.call(msg, LogLevel.debug); + } catch (_) {} + } + } + + /// 输出信息日志 + static void info(String tag, String message) { + if (enabled) { + final msg = _formatMessage(tag, message); + _writeConsoleMessage(tag, message); + try { + onLog?.call(msg, LogLevel.info); + } catch (_) {} + } + } + + /// 输出警告日志 + static void warn(String tag, String message) { + if (enabled) { + final formattedMessage = '⚠️ $message'; + final msg = _formatMessage(tag, formattedMessage); + _writeConsoleMessage(tag, formattedMessage); + try { + onLog?.call(msg, LogLevel.warn); + } catch (_) {} + } + } + + /// 输出错误日志 + static void error(String tag, String message, [Object? error, StackTrace? stackTrace]) { + if (enabled) { + final errorInfo = error != null ? ', 错误: $error' : ''; + final stackInfo = stackTrace != null ? '\n堆栈: $stackTrace' : ''; + final formattedMessage = '❌ $message$errorInfo$stackInfo'; + final msg = _formatMessage(tag, formattedMessage); + _writeConsoleMessage(tag, formattedMessage); + try { + onLog?.call(msg, LogLevel.error); + } catch (_) {} + } + } + + /// 输出日志(自动判断级别) + static void log(String tag, String message, {LogLevel level = LogLevel.debug}) { + switch (level) { + case LogLevel.debug: + debug(tag, message); + break; + case LogLevel.info: + info(tag, message); + break; + case LogLevel.warn: + warn(tag, message); + break; + case LogLevel.error: + error(tag, message); + break; + } + } + + /// 输出 developer log。 + /// + /// 长 JSON 等需要完整复制的日志使用此入口。 + /// + /// developer.log 保留完整单条内容给 DevTools;控制台/logcat 使用分片,避免单行截断。 + static void developerLog( + String tag, + String message, { + LogLevel level = LogLevel.debug, + }) { + if (enabled) { + final msg = _formatMessage(tag, message); + final writer = developerLogWriterForTest; + if (writer != null) { + writer(msg, name: tag); + } else { + developer.log(msg, name: tag); + } + _writeConsoleMessage(tag, message); + try { + onLog?.call(msg, level); + } catch (_) {} + } + } + + /// 便捷方法:AnalyticsSdk 日志 + static void analyticsSdk(String message, {LogLevel level = LogLevel.debug}) { + log('AnalyticsSdk', message, level: level); + } + + /// 便捷方法:AnalyticsSdk developer log + static void analyticsSdkDeveloperLog( + String message, { + LogLevel level = LogLevel.debug, + }) { + developerLog('AnalyticsSdk', message, level: level); + } + + /// 便捷方法:DomainManager 日志 + static void domainManager(String message, {LogLevel level = LogLevel.debug}) { + log('DomainManager', message, level: level); + } + + /// 便捷方法:EventTypeConfigManager 日志 + static void eventTypeConfigManager(String message, {LogLevel level = LogLevel.debug}) { + log('EventTypeConfigManager', message, level: level); + } + + /// 便捷方法:GlobalClick 日志 + static void globalClick(String message, {LogLevel level = LogLevel.debug}) { + log('GlobalClick', message, level: level); + } + + /// 便捷方法:AdImpressionManager 日志 + static void adImpressionManager(String message, {LogLevel level = LogLevel.debug}) { + log('AdImpressionManager', message, level: level); + } + + /// 便捷方法:PageLifecycleObserver 日志 + static void pageLifecycleObserver(String message, {LogLevel level = LogLevel.debug}) { + log('PageLifecycleObserver', message, level: level); + } + + /// 便捷方法:InitConfigResult 日志 + static void initConfigResult(String message, {LogLevel level = LogLevel.debug}) { + log('InitConfigResult', message, level: level); + } +} diff --git a/analytics_sdk/lib/utils/network_type_resolver.dart b/analytics_sdk/lib/utils/network_type_resolver.dart new file mode 100644 index 0000000..6c1a39b --- /dev/null +++ b/analytics_sdk/lib/utils/network_type_resolver.dart @@ -0,0 +1,57 @@ +const Set networkTypeValues = { + 'wifi', + 'ethernet', + 'cellular', + 'cellular_2g', + 'cellular_3g', + 'cellular_4g', + 'cellular_5g', + 'offline', + 'unknown', +}; + +String resolveWebNetworkType({ + required String connectionType, + required String effectiveType, +}) { + switch (connectionType.trim().toLowerCase()) { + case 'wifi': + return 'wifi'; + case 'ethernet': + return 'ethernet'; + case 'none': + return 'offline'; + case 'cellular': + switch (effectiveType.trim().toLowerCase()) { + case 'slow-2g': + case '2g': + return 'cellular_2g'; + case '3g': + return 'cellular_3g'; + case '4g': + return 'cellular_4g'; + case '5g': + return 'cellular_5g'; + default: + return 'cellular'; + } + default: + return 'unknown'; + } +} + +String normalizeNetworkType(String value) { + final normalized = value.trim().toLowerCase(); + return networkTypeValues.contains(normalized) ? normalized : 'unknown'; +} + +int isOnlineForNetworkType(String networkType) { + switch (normalizeNetworkType(networkType)) { + case 'offline': + return 0; + case 'unknown': + return -1; + default: + return 1; + } +} diff --git a/analytics_sdk/lib/utils/platform_storage.dart b/analytics_sdk/lib/utils/platform_storage.dart new file mode 100644 index 0000000..47158c4 --- /dev/null +++ b/analytics_sdk/lib/utils/platform_storage.dart @@ -0,0 +1,14 @@ +import 'platform_storage_native.dart' + // ignore: uri_does_not_exist + if (dart.library.html) 'platform_storage_web.dart'; + +/// 跨平台 key-value 存储。 +/// Native:文件存储(path_provider);Web:localStorage。 +/// key 即文件名(native)或 localStorage key(web),不含路径。 +class PlatformStorage { + static Future read(String key) => platformRead(key); + static Future write(String key, String value) => + platformWrite(key, value); + static Future has(String key) => platformHas(key); + static Future delete(String key) => platformDelete(key); +} diff --git a/analytics_sdk/lib/utils/platform_storage_native.dart b/analytics_sdk/lib/utils/platform_storage_native.dart new file mode 100644 index 0000000..7f2e083 --- /dev/null +++ b/analytics_sdk/lib/utils/platform_storage_native.dart @@ -0,0 +1,38 @@ +import 'dart:io'; + +import 'package:path_provider/path_provider.dart'; + +Future platformRead(String key) async { + try { + final dir = await getApplicationDocumentsDirectory(); + final file = File('${dir.path}/$key'); + if (!await file.exists()) return null; + return await file.readAsString(); + } catch (_) { + return null; + } +} + +Future platformWrite(String key, String value) async { + try { + final dir = await getApplicationDocumentsDirectory(); + await File('${dir.path}/$key').writeAsString(value); + } catch (_) {} +} + +Future platformHas(String key) async { + try { + final dir = await getApplicationDocumentsDirectory(); + return File('${dir.path}/$key').existsSync(); + } catch (_) { + return false; + } +} + +Future platformDelete(String key) async { + try { + final dir = await getApplicationDocumentsDirectory(); + final file = File('${dir.path}/$key'); + if (await file.exists()) await file.delete(); + } catch (_) {} +} diff --git a/analytics_sdk/lib/utils/platform_storage_web.dart b/analytics_sdk/lib/utils/platform_storage_web.dart new file mode 100644 index 0000000..109e129 --- /dev/null +++ b/analytics_sdk/lib/utils/platform_storage_web.dart @@ -0,0 +1,14 @@ +// ignore: avoid_web_libraries_in_flutter +import 'dart:html' as html; + +Future platformRead(String key) async => + html.window.localStorage[key]; + +Future platformWrite(String key, String value) async => + html.window.localStorage[key] = value; + +Future platformHas(String key) async => + html.window.localStorage.containsKey(key); + +Future platformDelete(String key) async => + html.window.localStorage.remove(key); diff --git a/analytics_sdk/lib/utils/runtime_observation_native.dart b/analytics_sdk/lib/utils/runtime_observation_native.dart new file mode 100644 index 0000000..bf382b3 --- /dev/null +++ b/analytics_sdk/lib/utils/runtime_observation_native.dart @@ -0,0 +1,164 @@ +import 'dart:async'; +import 'dart:io' show Platform; +import 'dart:ui' show window; + +import 'package:flutter/widgets.dart'; +import 'package:screen_brightness/screen_brightness.dart'; +import 'package:screen_state/screen_state.dart'; +import 'package:volume_controller/volume_controller.dart'; + +/// Native runtime observation fields backed by platform plugins. +class RuntimeObservationMonitor { + final List> _subscriptions; + final WidgetsBindingObserver? _metricsObserver; + + const RuntimeObservationMonitor( + this._subscriptions, { + WidgetsBindingObserver? metricsObserver, + }) : _metricsObserver = metricsObserver; + + Future dispose() async { + for (final subscription in _subscriptions) { + await subscription.cancel(); + } + final metricsObserver = _metricsObserver; + if (metricsObserver != null) { + try { + WidgetsBinding.instance.removeObserver(metricsObserver); + } catch (_) {} + } + try { + VolumeController().removeListener(); + } catch (_) {} + } +} + +Future> collectRuntimeObservationFields() async { + return { + ..._screenObservationFields(), + 'screen_brightness': await _readScreenBrightness(), + 'volume': await _readVolume(), + 'is_screen_on': _initialScreenOn(), + }; +} + +RuntimeObservationMonitor startRuntimeObservationMonitoring( + void Function(Map fields) onFields, +) { + final subscriptions = >[]; + WidgetsBindingObserver? metricsObserver; + + try { + metricsObserver = _MetricsObserver( + onMetricsChanged: () => onFields(_screenObservationFields()), + ); + WidgetsBinding.instance.addObserver(metricsObserver); + } catch (_) {} + + try { + subscriptions.add(ScreenBrightness().onCurrentBrightnessChanged.listen( + (value) => onFields({ + 'screen_brightness': _normalizedPercent(value), + }), onError: (_) { + onFields({'screen_brightness': -1}); + })); + } catch (_) {} + + try { + subscriptions.add(VolumeController().listener((value) { + onFields({'volume': _normalizedPercent(value)}); + })); + } catch (_) {} + + try { + if (Platform.isAndroid || Platform.isIOS) { + subscriptions.add(Screen().screenStateStream.listen((event) { + onFields({'is_screen_on': _screenStateValue(event)}); + }, onError: (_) { + onFields({'is_screen_on': -1}); + })); + } + } catch (_) {} + + return RuntimeObservationMonitor( + subscriptions, + metricsObserver: metricsObserver, + ); +} + +Map _screenObservationFields() { + try { + final size = window.physicalSize; + final width = size.width.round(); + final height = size.height.round(); + final pixelRatio = (window.devicePixelRatio * 100).round(); + final dpi = (window.devicePixelRatio * 160).round(); + return { + 'screen_width': width, + 'screen_height': height, + 'screen_dpi': dpi, + 'device_pixel_ratio': pixelRatio, + 'orientation': width >= height ? 'landscape' : 'portrait', + }; + } catch (_) { + return const { + 'screen_width': -1, + 'screen_height': -1, + 'screen_dpi': -1, + 'device_pixel_ratio': -1, + 'orientation': '', + }; + } +} + +Future _readScreenBrightness() async { + try { + return _normalizedPercent(await ScreenBrightness().current); + } catch (_) { + return -1; + } +} + +Future _readVolume() async { + try { + return _normalizedPercent(await VolumeController().getVolume()); + } catch (_) { + return -1; + } +} + +int _initialScreenOn() { + try { + return Platform.isAndroid || Platform.isIOS ? 1 : -1; + } catch (_) { + return -1; + } +} + +int _screenStateValue(ScreenStateEvent event) { + switch (event) { + case ScreenStateEvent.SCREEN_ON: + case ScreenStateEvent.SCREEN_UNLOCKED: + return 1; + case ScreenStateEvent.SCREEN_OFF: + return 0; + } +} + +int _normalizedPercent(num value) { + final numeric = value.toDouble(); + if (!numeric.isFinite) return -1; + final scaled = numeric <= 1 ? numeric * 100 : numeric; + return scaled.clamp(0, 100).round(); +} + +class _MetricsObserver with WidgetsBindingObserver { + final void Function() onMetricsChanged; + + const _MetricsObserver({required this.onMetricsChanged}); + + @override + void didChangeMetrics() { + onMetricsChanged(); + } +} diff --git a/analytics_sdk/lib/utils/runtime_observation_stub.dart b/analytics_sdk/lib/utils/runtime_observation_stub.dart new file mode 100644 index 0000000..de5dd3b --- /dev/null +++ b/analytics_sdk/lib/utils/runtime_observation_stub.dart @@ -0,0 +1,16 @@ +/// Runtime observation fields for platforms without native observers. +class RuntimeObservationMonitor { + const RuntimeObservationMonitor(); + + Future dispose() async {} +} + +Future> collectRuntimeObservationFields() async { + return const {}; +} + +RuntimeObservationMonitor startRuntimeObservationMonitoring( + void Function(Map fields) onFields, +) { + return const RuntimeObservationMonitor(); +} diff --git a/analytics_sdk/lib/utils/uuid_util.dart b/analytics_sdk/lib/utils/uuid_util.dart new file mode 100644 index 0000000..01b2047 --- /dev/null +++ b/analytics_sdk/lib/utils/uuid_util.dart @@ -0,0 +1,15 @@ +import 'dart:math'; + +/// 生成符合 RFC 4122 的 UUID v4 字符串 +/// 替代 uuid 包,消除跨 Dart 版本的依赖约束冲突 +String generateUuidV4() { + final random = Random.secure(); + final bytes = List.generate(16, (_) => random.nextInt(256)); + bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4 + bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 1 + String toHex(int b) => b.toRadixString(16).padLeft(2, '0'); + return '${toHex(bytes[0])}${toHex(bytes[1])}${toHex(bytes[2])}${toHex(bytes[3])}-' + '${toHex(bytes[4])}${toHex(bytes[5])}-${toHex(bytes[6])}${toHex(bytes[7])}-' + '${toHex(bytes[8])}${toHex(bytes[9])}-' + '${toHex(bytes[10])}${toHex(bytes[11])}${toHex(bytes[12])}${toHex(bytes[13])}${toHex(bytes[14])}${toHex(bytes[15])}'; +} diff --git a/analytics_sdk/lib/utils/widget_bridge.dart b/analytics_sdk/lib/utils/widget_bridge.dart new file mode 100644 index 0000000..81ef892 --- /dev/null +++ b/analytics_sdk/lib/utils/widget_bridge.dart @@ -0,0 +1,34 @@ +/// 内部桥接层:解除 widget 对 AnalyticsSdk 的直接依赖。 +/// +/// AnalyticsSdk.init() 调用 [register] 注册实现; +/// widget 层通过此类的静态方法访问 SDK 能力, +/// 无需 import analytics_sdk.dart,避免上行依赖。 +class WidgetBridge { + static void Function(dynamic event)? _track; + static String Function()? _getUserType; + static bool _enableDebugBanner = false; + static List> Function()? _getDebugSteps; + + /// 由 AnalyticsSdk.init() 调用,注册 SDK 实现 + static void register({ + required void Function(dynamic event) track, + required String Function() getUserType, + required bool enableDebugBanner, + required List> Function() getDebugSteps, + }) { + _track = track; + _getUserType = getUserType; + _enableDebugBanner = enableDebugBanner; + _getDebugSteps = getDebugSteps; + } + + static void track(dynamic event) => _track?.call(event); + + /// 获取当前用户类型,SDK 未初始化时返回 'normal'。 + static String getUserType() => _getUserType?.call() ?? 'normal'; + + static bool get enableDebugBanner => _enableDebugBanner; + + static List> getDebugSteps() => + _getDebugSteps?.call() ?? []; +} diff --git a/analytics_sdk/lib/widget/_tab_helpers.dart b/analytics_sdk/lib/widget/_tab_helpers.dart new file mode 100644 index 0000000..1931212 --- /dev/null +++ b/analytics_sdk/lib/widget/_tab_helpers.dart @@ -0,0 +1,20 @@ +import 'package:analytics_sdk/entity/analytics_tab.dart'; +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:analytics_sdk/utils/widget_bridge.dart'; + +/// Tab 解析与 track 分发的包内共享工具。 +/// +/// 供 widget 层各 analytics 包装使用,消除跨文件重复逻辑。 +/// 文件名以下划线开头,表示不对外暴露。 + +/// 按索引解析 tab,超出范围时用 tab_N 兜底。 +AnalyticsTab resolveTab(List tabs, int index) => + index < tabs.length ? tabs[index] : AnalyticsTab('tab_$index'); + +/// 解析 tab 展示名称,未配置时通过 PageNameMapper 自动映射。 +String resolveTabName(AnalyticsTab tab) => + tab.name ?? PageNameMapper.getPageName(tab.key); + +/// 上报事件:优先使用测试注入的 override,否则走 WidgetBridge.track。 +void trackWith(void Function(dynamic)? override, Object event) => + (override ?? WidgetBridge.track)(event); diff --git a/analytics_sdk/lib/widget/analytics_bottom_nav_wrapper.dart b/analytics_sdk/lib/widget/analytics_bottom_nav_wrapper.dart new file mode 100644 index 0000000..a81ea05 --- /dev/null +++ b/analytics_sdk/lib/widget/analytics_bottom_nav_wrapper.dart @@ -0,0 +1,163 @@ +import 'package:analytics_sdk/entity/analytics_tab.dart'; +import 'package:analytics_sdk/entity/app_page_view_event.dart'; +import 'package:analytics_sdk/entity/navigation_event.dart'; +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:analytics_sdk/observer/page_lifecycle_observer.dart'; +import 'package:analytics_sdk/utils/logger.dart'; +import 'package:analytics_sdk/utils/widget_bridge.dart'; +import 'package:analytics_sdk/widget/_tab_helpers.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +/// BottomNavigationBar 的 analytics 包装。 +/// +/// 使用 Listener + LayoutBuilder 方案:保持原始 child 完整进入 widget 树, +/// 通过指针位移计算 tab 索引,无需拷贝 BottomNavigationBar 的所有属性。 +/// 这样 Flutter 升级新增属性时,SDK 不需要任何改动。 +/// +/// 不应直接使用;通过 [AnalyticsBottomNavExtension.withAnalytics] 调用。 +class AnalyticsBottomNavWrapper extends StatefulWidget { + final BottomNavigationBar child; + final List tabs; + + // ignore: invalid_use_of_visible_for_testing_member + /// 仅供测试使用;null 时走 AnalyticsSdk.instance.track。 + @visibleForTesting + final void Function(dynamic event)? onTrack; + + const AnalyticsBottomNavWrapper({ + super.key, + required this.child, + required this.tabs, + this.onTrack, + }); + + @override + State createState() => + _AnalyticsBottomNavWrapperState(); +} + +class _AnalyticsBottomNavWrapperState extends State { + /// 记录 pointer-down 位置,用于判断是否为 tap(而非拖拽) + Offset? _tapDownPosition; + + /// 去重:记录最近一次上报的 tab 索引,防止快速连点重复上报 + int _lastTrackedIndex = -1; + + @override + void didUpdateWidget(AnalyticsBottomNavWrapper oldWidget) { + super.didUpdateWidget(oldWidget); + // 父组件以编程方式切换 currentIndex 时同步去重状态 + if (widget.child.currentIndex != oldWidget.child.currentIndex) { + _lastTrackedIndex = widget.child.currentIndex; + } + } + + /// 纯计算:根据 tap 坐标解析 tab 索引。 + /// 返回 null 表示无效点击(越界、重复、当前已选中)。 + int? _indexFromTap( + double tapX, double totalWidth, TextDirection textDirection) { + final itemCount = widget.child.items.length; + if (itemCount == 0 || totalWidth <= 0) return null; + + var index = + (tapX / (totalWidth / itemCount)).floor().clamp(0, itemCount - 1); + + // RTL 布局下 BottomNavigationBar items 从右到左排列,镜像索引 + if (textDirection == TextDirection.rtl) { + index = itemCount - 1 - index; + } + + if (index == _lastTrackedIndex || index == widget.child.currentIndex) { + return null; + } + return index; + } + + /// 根据横坐标、容器宽度和文字方向解析 tab 索引并上报埋点。 + /// + /// 通过 [SchedulerBinding.addPostFrameCallback] 在渲染完成后上报 + /// [AppPageViewEvent](含来路 key)和 [NavigationEvent], + /// pageLoadTime 反映从点击到渲染完成的真实耗时,与 [PageLifecycleObserver] 保持一致。 + void _handleTapAt( + double tapX, double totalWidth, TextDirection textDirection) { + try { + if (widget.tabs.isEmpty) return; + + final index = _indexFromTap(tapX, totalWidth, textDirection); + if (index == null) return; + _lastTrackedIndex = index; + + final tab = resolveTab(widget.tabs, index); + final pageKey = tab.key; + final pageName = resolveTabName(tab); + + PageNameMapper.addMapping(pageKey, pageName); + final referrerKey = PageLifecycleObserver.currentPageKey; + final referrerName = PageNameMapper.getPageName(referrerKey); + PageLifecycleObserver.recordNavigation(pageKey); + + // 记录 tap 时刻;在下一帧渲染完成后测量实际耗时,与 PageLifecycleObserver 保持一致 + final tapTime = DateTime.now(); + final onTrackSnapshot = widget.onTrack; + SchedulerBinding.instance.addPostFrameCallback((_) { + final pageLoadTime = DateTime.now().difference(tapTime).inMilliseconds; + try { + trackWith( + onTrackSnapshot, + AppPageViewEvent( + userType: WidgetBridge.getUserType(), + pageKey: pageKey, + pageName: pageName, + referrerPageKey: referrerKey, + referrerPageName: referrerName, + currentPageKey: pageKey, + currentPageName: pageName, + pageLoadTime: pageLoadTime, + pageEnterTs: tapTime.millisecondsSinceEpoch ~/ 1000, + )); + trackWith( + onTrackSnapshot, + NavigationEvent( + navigationKey: pageKey, + navigationName: pageName, + )); + } catch (e) { + Logger.analyticsSdk('BottomNavWrapper.post-frame 上报异常,已安全处理: $e'); + } + }); + } catch (e) { + Logger.analyticsSdk('BottomNavWrapper._handleTapAt 异常,已安全处理: $e'); + } + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + final textDirection = Directionality.of(context); + return Listener( + // 透传所有指针事件,不干扰 BottomNavigationBar 自身的手势识别 + behavior: HitTestBehavior.translucent, + onPointerDown: (event) { + _tapDownPosition = event.localPosition; + }, + onPointerUp: (event) { + final down = _tapDownPosition; + _tapDownPosition = null; + if (down == null) return; + // 位移 > 18px(与 Flutter kTouchSlop 对齐)视为拖拽,不触发 tab 埋点 + if ((event.localPosition - down).distance > 18) return; + _handleTapAt( + event.localPosition.dx, constraints.maxWidth, textDirection); + }, + onPointerCancel: (_) { + _tapDownPosition = null; + }, + // 原始 child 完整保留,onTap 由 BottomNavigationBar 自身调用 + child: widget.child, + ); + }, + ); + } +} diff --git a/analytics_sdk/lib/widget/analytics_debug_banner.dart b/analytics_sdk/lib/widget/analytics_debug_banner.dart new file mode 100644 index 0000000..19edfc1 --- /dev/null +++ b/analytics_sdk/lib/widget/analytics_debug_banner.dart @@ -0,0 +1,158 @@ +import 'dart:async'; + +import 'package:analytics_sdk/manager/sdk_context.dart'; +import 'package:analytics_sdk/utils/widget_bridge.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// 埋点调试条 - 展示设备信息、埋点流程 7 步,便于排查某机型上报问题 +/// +/// release 包自动隐藏(无性能影响);debug 包由 WidgetBridge 内的 enableDebugBanner 控制。 +class AnalyticsDebugBanner extends StatelessWidget { + const AnalyticsDebugBanner({super.key}); + + @override + Widget build(BuildContext context) { + if (kReleaseMode || !WidgetBridge.enableDebugBanner) { + return const SizedBox.shrink(); + } + return const _AnalyticsDebugBannerContent(); + } +} + +class _AnalyticsDebugBannerContent extends StatefulWidget { + const _AnalyticsDebugBannerContent(); + + @override + State<_AnalyticsDebugBannerContent> createState() => + _AnalyticsDebugBannerContentState(); +} + +class _AnalyticsDebugBannerContentState + extends State<_AnalyticsDebugBannerContent> { + List> _steps = []; + Timer? _refreshTimer; + + @override + void initState() { + super.initState(); + _refreshSteps(); + _refreshTimer = Timer.periodic( + const Duration(seconds: 2), (_) => _refreshSteps()); + } + + @override + void dispose() { + _refreshTimer?.cancel(); + super.dispose(); + } + + void _refreshSteps() { + try { + if (mounted) { + setState(() { + _steps = WidgetBridge.getDebugSteps(); + }); + } + } catch (_) {} + } + + @override + Widget build(BuildContext context) { + + final brand = SdkContext.deviceBrand ?? ''; + final model = SdkContext.deviceModel ?? ''; + final deviceInfo = brand.isNotEmpty || model.isNotEmpty + ? '$brand $model'.trim() + : (kIsWeb + ? 'Web' + : defaultTargetPlatform == TargetPlatform.android + ? 'Android' + : defaultTargetPlatform == TargetPlatform.iOS + ? 'iOS' + : defaultTargetPlatform.name); + final deviceId = SdkContext.deviceId ?? ''; + final channel = SdkContext.channel ?? ''; + + return IgnorePointer( + child: Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.55), + border: const Border( + bottom: BorderSide(color: Colors.white, width: 2)), + ), + child: SafeArea( + bottom: false, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + '【埋点测试包】', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white, + shadows: [ + Shadow( + color: Colors.black, + offset: Offset(1, 1), + blurRadius: 2), + ], + ), + ), + const SizedBox(height: 6), + Text( + '设备: $deviceInfo', + style: const TextStyle(fontSize: 12, color: Colors.white), + ), + Text( + 'deviceId: ${deviceId.isEmpty ? "未获取" : "${deviceId.length > 16 ? deviceId.substring(0, 16) : deviceId}..."}', + style: const TextStyle(fontSize: 11, color: Colors.white70), + ), + Text( + '渠道: ${channel.isEmpty ? "未获取" : channel}', + style: const TextStyle(fontSize: 11, color: Colors.white70), + ), + const SizedBox(height: 8), + const Text( + '埋点流程(卡在某步=该机型问题)', + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w600, + color: Colors.yellow), + ), + ..._steps.map((s) { + final status = s['status'] ?? ''; + final isOk = status.startsWith('✓'); + final isFail = status.startsWith('✗'); + var detail = s['detail'] ?? ''; + if (detail.toString().length > 35) { + detail = '${detail.toString().substring(0, 35)}...'; + } + final detailStr = + detail.isNotEmpty && detail != '-' ? ' ($detail)' : ''; + return Padding( + padding: const EdgeInsets.only(top: 2), + child: Text( + '${s['name']}: $status$detailStr', + style: TextStyle( + fontSize: 10, + color: isOk + ? Colors.greenAccent + : isFail + ? Colors.orange + : Colors.white70, + ), + ), + ); + }), + ], + ), + ), + ), + ); + } +} diff --git a/analytics_sdk/lib/widget/analytics_tab_bar_wrapper.dart b/analytics_sdk/lib/widget/analytics_tab_bar_wrapper.dart new file mode 100644 index 0000000..25e7faf --- /dev/null +++ b/analytics_sdk/lib/widget/analytics_tab_bar_wrapper.dart @@ -0,0 +1,165 @@ +import 'package:analytics_sdk/entity/analytics_tab.dart'; +import 'package:analytics_sdk/entity/app_page_view_event.dart'; +import 'package:analytics_sdk/entity/navigation_event.dart'; +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:analytics_sdk/observer/page_lifecycle_observer.dart'; +import 'package:analytics_sdk/utils/logger.dart'; +import 'package:analytics_sdk/utils/widget_bridge.dart'; +import 'package:analytics_sdk/widget/_tab_helpers.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +/// TabBar 的 analytics 包装。 +/// +/// 监听 [TabController],Tab 切换完成时在下一帧渲染后自动上报: +/// - [AppPageViewEvent]:含来路页面 key(取自切换前的 [PageLifecycleObserver.currentPageKey]) +/// - [NavigationEvent]:用于导航行为报表 +/// +/// 同时调用 [PageLifecycleObserver.recordNavigation] 更新全局 currentPageKey +/// 及栈顶 effectivePageKey,确保后续 page_click 归属正确的子页面; +/// 即便从详情页 pop 返回,也能恢复到切换前的子 Tab key。 +/// +/// 不应直接使用;通过 [AnalyticsTabBarExtension.withAnalytics] 调用。 +class AnalyticsTabBarWrapper extends StatefulWidget + implements PreferredSizeWidget { + final TabBar child; + final List tabs; + + /// 仅供测试使用;null 时走 AnalyticsSdk.instance.track。 + @visibleForTesting + final void Function(dynamic event)? onTrack; + + const AnalyticsTabBarWrapper({ + super.key, + required this.child, + required this.tabs, + this.onTrack, + }); + + @override + Size get preferredSize => child.preferredSize; + + @override + State createState() => _AnalyticsTabBarWrapperState(); +} + +class _AnalyticsTabBarWrapperState extends State { + /// 当前监听的 TabController;在 [didChangeDependencies] / [didUpdateWidget] 中维护。 + TabController? _controller; + + /// 最近一次上报的 tab 索引,过滤 indexIsChanging 动画期间的重复触发。 + int _lastTrackedIndex = -1; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + try { + _attachController(_resolveController()); + } catch (e) { + Logger.analyticsSdk( + 'AnalyticsTabBarWrapper.didChangeDependencies 异常,已安全处理: $e'); + } + } + + @override + void didUpdateWidget(AnalyticsTabBarWrapper oldWidget) { + super.didUpdateWidget(oldWidget); + try { + final newController = _resolveController(); + if (newController != _controller) { + _attachController(newController); + _lastTrackedIndex = -1; // 新 controller 时重置,防止去重误判 + } + } catch (e) { + Logger.analyticsSdk( + 'AnalyticsTabBarWrapper.didUpdateWidget 异常,已安全处理: $e'); + } + } + + /// 优先取 widget.child.controller,回退到 [DefaultTabController]。 + TabController? _resolveController() { + final controller = widget.child.controller; + if (controller != null) return controller; + try { + return DefaultTabController.of(context); + } catch (_) { + return null; + } + } + + /// 切换监听目标:移除旧 controller 监听后绑定新 controller;controller 未变时直接返回。 + void _attachController(TabController? controller) { + if (controller == _controller) return; + _controller?.removeListener(_onControllerChange); + _controller = controller; + _controller?.addListener(_onControllerChange); + } + + /// TabController 索引变化回调。 + /// + /// 过滤动画帧(indexIsChanging)和重复索引后,通过 [SchedulerBinding.addPostFrameCallback] + /// 在渲染完成后上报 [AppPageViewEvent] 和 [NavigationEvent], + /// pageLoadTime 反映从点击到渲染完成的真实耗时。 + void _onControllerChange() { + try { + final ctrl = _controller; + if (ctrl == null || ctrl.indexIsChanging) return; + + final index = ctrl.index; + if (index == _lastTrackedIndex) return; + _lastTrackedIndex = index; + + if (widget.tabs.isEmpty) return; + + final tab = resolveTab(widget.tabs, index); + final pageKey = tab.key; + final pageName = resolveTabName(tab); + + PageNameMapper.addMapping(pageKey, pageName); + final referrerKey = PageLifecycleObserver.currentPageKey; + final referrerName = PageNameMapper.getPageName(referrerKey); + PageLifecycleObserver.recordNavigation(pageKey); + + final tapTime = DateTime.now(); + final onTrackSnapshot = widget.onTrack; + SchedulerBinding.instance.addPostFrameCallback((_) { + final pageLoadTime = DateTime.now().difference(tapTime).inMilliseconds; + try { + trackWith( + onTrackSnapshot, + AppPageViewEvent( + userType: WidgetBridge.getUserType(), + pageKey: pageKey, + pageName: pageName, + referrerPageKey: referrerKey, + referrerPageName: referrerName, + currentPageKey: pageKey, + currentPageName: pageName, + pageLoadTime: pageLoadTime, + pageEnterTs: tapTime.millisecondsSinceEpoch ~/ 1000, + )); + trackWith( + onTrackSnapshot, + NavigationEvent( + navigationKey: pageKey, + navigationName: pageName, + )); + } catch (e) { + Logger.analyticsSdk('TabBarWrapper.post-frame 上报异常,已安全处理: $e'); + } + }); + } catch (e) { + Logger.analyticsSdk('TabBarWrapper._onControllerChange 异常,已安全处理: $e'); + } + } + + @override + void dispose() { + _controller?.removeListener(_onControllerChange); + _controller = null; + super.dispose(); + } + + @override + Widget build(BuildContext context) => widget.child; +} diff --git a/analytics_sdk/lib/widget/global_click_wrapper.dart b/analytics_sdk/lib/widget/global_click_wrapper.dart new file mode 100644 index 0000000..37a9bb4 --- /dev/null +++ b/analytics_sdk/lib/widget/global_click_wrapper.dart @@ -0,0 +1,393 @@ +import 'package:analytics_sdk/config/sdk_config.dart'; +import 'package:analytics_sdk/entity/page_click_event.dart'; +import 'package:analytics_sdk/manager/session_behavior_manager.dart'; +import 'package:analytics_sdk/utils/logger.dart'; +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:analytics_sdk/observer/page_lifecycle_observer.dart'; +import 'package:analytics_sdk/utils/widget_bridge.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; + +class GlobalClickWrapper extends StatefulWidget { + final Widget child; + + const GlobalClickWrapper({ + required this.child, + super.key, + }); + + @override + State createState() => _GlobalClickWrapperState(); +} + +class _GlobalClickWrapperState extends State { + DateTime? _lastClickTime; + Offset? _pointerDownPosition; + bool _pointerScrolled = false; + _ClickScrollSnapshot? _lastScrollSnapshot; + final Map _touchStarts = {}; + final Map _activeTouchPositions = {}; + double? _pinchStartDistance; + bool _pinchRecorded = false; + + static const double _swipeThresholdPx = 50; + static const double _pinchThresholdPx = 20; + static const double _longPressMoveTolerancePx = 10; + static const _longPressDuration = Duration(milliseconds: 500); + + @override + Widget build(BuildContext context) { + return NotificationListener( + onNotification: (notification) { + _recordScroll(notification); + return false; + }, + child: Listener( + onPointerDown: (PointerDownEvent event) { + _pointerDownPosition = event.position; + _pointerScrolled = false; + _recordPointerDown(event); + }, + onPointerMove: (PointerMoveEvent event) { + if (!_pointerScrolled && _pointerDownPosition != null) { + final dist = (event.position - _pointerDownPosition!).distance; + if (dist > kTouchSlop) { + _pointerScrolled = true; + } + } + _recordPointerMove(event); + }, + onPointerUp: (PointerUpEvent event) { + if (!_pointerScrolled) { + _handleTap(event); + } + _recordPointerUp(event); + _pointerDownPosition = null; + _pointerScrolled = false; + }, + onPointerCancel: (event) { + _recordPointerCancel(event); + _pointerDownPosition = null; + _pointerScrolled = false; + }, + // 透传所有事件,不影响原有交互 + behavior: HitTestBehavior.translucent, + child: widget.child, + ), + ); + } + + void _handleTap(PointerUpEvent event) { + try { + // 点击节流:限制点击事件上报频率,避免高频点击造成性能问题 + final now = DateTime.now(); + if (_lastClickTime != null && + now.difference(_lastClickTime!) < SdkConfig.clickThrottleDuration) { + return; + } + _lastClickTime = now; + + // 允许触摸(移动端)、鼠标(桌面/Web)、触控板(macOS);过滤触控笔等其他设备。 + final kind = event.kind; + if (kind != PointerDeviceKind.touch && + kind != PointerDeviceKind.mouse && + kind != PointerDeviceKind.trackpad) { + return; + } + + final pageKey = PageLifecycleObserver.currentPageKey; + + if (pageKey.isEmpty || pageKey == 'unknown_page') { + return; + } + + final pageName = PageNameMapper.getPageName(pageKey); + + // 保存事件位置信息,避免在异步回调中访问已失效的事件对象 + final clickPosition = event.position; + + // 异步获取 RenderBox,避免在 build 阶段调用 findRenderObject + WidgetsBinding.instance.addPostFrameCallback((_) { + try { + if (!mounted) return; + + final renderObject = context.findRenderObject(); + if (renderObject == null || renderObject is! RenderBox) return; + final renderBox = renderObject; + + final size = renderBox.size; + + final clickX = clickPosition.dx.round(); + final clickY = clickPosition.dy.round(); + final localPosition = renderBox.globalToLocal(clickPosition); + final clickViewportX = localPosition.dx.round(); + final clickViewportY = localPosition.dy.round(); + final viewportWidth = size.width.round(); + final viewportHeight = size.height.round(); + final percentX = viewportWidth > 0 + ? (clickViewportX / viewportWidth * 100).round().clamp(0, 100) + : 0; + final percentY = viewportHeight > 0 + ? (clickViewportY / viewportHeight * 100).round().clamp(0, 100) + : 0; + final scrollSnapshot = _scrollSnapshotFor( + pageKey: pageKey, + clickViewportX: clickViewportX, + clickViewportY: clickViewportY, + viewportWidth: viewportWidth, + viewportHeight: viewportHeight, + ); + + Logger.globalClick( + '上报点击成功 → $pageKey ($clickX,$clickY) [$percentX%,$percentY%]'); + + WidgetBridge.track(PageClickEvent.fromViewport( + pageKey: pageKey, + pageName: pageName, + clickPageX: clickX, + clickPageY: clickY, + clickViewportX: clickViewportX, + clickViewportY: clickViewportY, + viewportWidth: viewportWidth, + viewportHeight: viewportHeight, + clickDocumentX: scrollSnapshot?.clickDocumentX, + clickDocumentY: scrollSnapshot?.clickDocumentY, + pageWidth: scrollSnapshot?.pageWidth, + pageHeight: scrollSnapshot?.pageHeight, + scrollTop: scrollSnapshot?.scrollTop, + scrollLeft: scrollSnapshot?.scrollLeft, + )); + } catch (e) { + Logger.globalClick('点击事件处理异常,已安全处理: $e'); + } + }); + } catch (e) { + Logger.globalClick('_handleTap() 异常,已安全处理: $e'); + } + } + + void _recordScroll(ScrollNotification notification) { + try { + final pageKey = PageLifecycleObserver.currentPageKey; + if (pageKey.isEmpty || pageKey == 'unknown_page') return; + + final metrics = notification.metrics; + final scrollOffset = _roundedNonNegativeFinite(metrics.pixels); + final viewportExtent = + _roundedNonNegativeFinite(metrics.viewportDimension); + final contentExtent = _roundedNonNegativeFinite( + metrics.maxScrollExtent + metrics.viewportDimension); + if (scrollOffset == null || + viewportExtent == null || + contentExtent == null || + contentExtent <= 0) { + return; + } + + if (metrics.axis == Axis.vertical) { + SessionBehaviorManager.instance.recordScroll( + scrollTop: scrollOffset, + viewportHeight: viewportExtent, + contentHeight: contentExtent, + ); + _lastScrollSnapshot = _ClickScrollSnapshot( + pageKey: pageKey, + scrollTop: scrollOffset, + scrollLeft: 0, + pageWidth: -1, + pageHeight: contentExtent, + ); + } else if (metrics.axis == Axis.horizontal) { + _lastScrollSnapshot = _ClickScrollSnapshot( + pageKey: pageKey, + scrollTop: 0, + scrollLeft: scrollOffset, + pageWidth: contentExtent, + pageHeight: -1, + ); + } + } catch (e) { + Logger.globalClick('滚动快照记录异常,已安全处理: $e'); + } + } + + void _recordPointerDown(PointerDownEvent event) { + try { + if (event.kind != PointerDeviceKind.touch) return; + final now = DateTime.now(); + if (_activeTouchPositions.isEmpty) { + SessionBehaviorManager.instance.recordTouchStart(at: now); + } else { + _pointerScrolled = true; + } + _touchStarts[event.pointer] = _PointerTouchStart( + position: event.position, + startedAt: now, + ); + _activeTouchPositions[event.pointer] = event.position; + _updatePinchBaseline(); + } catch (e) { + Logger.globalClick('触摸开始行为记录异常,已安全处理: $e'); + } + } + + void _recordPointerMove(PointerMoveEvent event) { + try { + if (event.kind != PointerDeviceKind.touch) return; + if (!_activeTouchPositions.containsKey(event.pointer)) return; + _activeTouchPositions[event.pointer] = event.position; + if (_activeTouchPositions.length < 2) return; + final distance = _firstTwoTouchDistance(); + if (distance == null) return; + _pinchStartDistance ??= distance; + if (!_pinchRecorded && + (distance - _pinchStartDistance!).abs() >= _pinchThresholdPx) { + _pinchRecorded = true; + SessionBehaviorManager.instance.recordPinch(); + } + } catch (e) { + Logger.globalClick('触摸移动行为记录异常,已安全处理: $e'); + } + } + + void _recordPointerUp(PointerUpEvent event) { + try { + if (event.kind != PointerDeviceKind.touch) return; + final now = DateTime.now(); + final wasSingleTouch = _activeTouchPositions.length == 1; + final start = _touchStarts[event.pointer]; + if (start != null && wasSingleTouch) { + _recordFinishedSingleTouch( + start: start, + endPosition: event.position, + endedAt: now, + ); + } + _touchStarts.remove(event.pointer); + _activeTouchPositions.remove(event.pointer); + _updatePinchBaseline(); + } catch (e) { + Logger.globalClick('触摸结束行为记录异常,已安全处理: $e'); + } + } + + void _recordPointerCancel(PointerCancelEvent event) { + try { + if (event.kind != PointerDeviceKind.touch) return; + _touchStarts.remove(event.pointer); + _activeTouchPositions.remove(event.pointer); + _updatePinchBaseline(); + } catch (e) { + Logger.globalClick('触摸取消行为记录异常,已安全处理: $e'); + } + } + + void _recordFinishedSingleTouch({ + required _PointerTouchStart start, + required Offset endPosition, + required DateTime endedAt, + }) { + final distance = (endPosition - start.position).distance; + if (distance >= _swipeThresholdPx) { + SessionBehaviorManager.instance.recordSwipe(at: endedAt); + return; + } + if (endedAt.difference(start.startedAt) >= _longPressDuration && + distance <= _longPressMoveTolerancePx) { + SessionBehaviorManager.instance.recordLongPress(at: endedAt); + } + } + + void _updatePinchBaseline() { + if (_activeTouchPositions.length < 2) { + _pinchStartDistance = null; + _pinchRecorded = false; + return; + } + _pinchStartDistance = _firstTwoTouchDistance(); + _pinchRecorded = false; + } + + double? _firstTwoTouchDistance() { + if (_activeTouchPositions.length < 2) return null; + final iterator = _activeTouchPositions.values.iterator; + iterator.moveNext(); + final first = iterator.current; + iterator.moveNext(); + final second = iterator.current; + return (first - second).distance; + } + + _ClickScrollSnapshot? _scrollSnapshotFor({ + required String pageKey, + required int clickViewportX, + required int clickViewportY, + required int viewportWidth, + required int viewportHeight, + }) { + final snapshot = _lastScrollSnapshot; + if (snapshot == null || snapshot.pageKey != pageKey) return null; + return snapshot.resolve( + clickViewportX: clickViewportX, + clickViewportY: clickViewportY, + viewportWidth: viewportWidth, + viewportHeight: viewportHeight, + ); + } + + int _nonNegative(int value) => value < 0 ? 0 : value; + + int? _roundedNonNegativeFinite(double value) { + if (!value.isFinite) return null; + return _nonNegative(value.round()); + } +} + +class _PointerTouchStart { + final Offset position; + final DateTime startedAt; + + const _PointerTouchStart({ + required this.position, + required this.startedAt, + }); +} + +class _ClickScrollSnapshot { + final String pageKey; + final int scrollTop; + final int scrollLeft; + final int pageWidth; + final int pageHeight; + final int clickDocumentX; + final int clickDocumentY; + + const _ClickScrollSnapshot({ + required this.pageKey, + required this.scrollTop, + required this.scrollLeft, + required this.pageWidth, + required this.pageHeight, + this.clickDocumentX = -1, + this.clickDocumentY = -1, + }); + + _ClickScrollSnapshot resolve({ + required int clickViewportX, + required int clickViewportY, + required int viewportWidth, + required int viewportHeight, + }) { + final resolvedPageWidth = pageWidth >= 0 ? pageWidth : viewportWidth; + final resolvedPageHeight = pageHeight >= 0 ? pageHeight : viewportHeight; + return _ClickScrollSnapshot( + pageKey: pageKey, + scrollTop: scrollTop, + scrollLeft: scrollLeft, + pageWidth: resolvedPageWidth, + pageHeight: resolvedPageHeight, + clickDocumentX: clickViewportX + scrollLeft, + clickDocumentY: clickViewportY + scrollTop, + ); + } +} diff --git a/analytics_sdk/pubspec.yaml b/analytics_sdk/pubspec.yaml new file mode 100644 index 0000000..05390aa --- /dev/null +++ b/analytics_sdk/pubspec.yaml @@ -0,0 +1,53 @@ +name: analytics_sdk +description: "A new Flutter project." +version: 1.4.6 +homepage: + +environment: + sdk: '>=2.17.0 <4.0.0' + flutter: '>=3.0.5' + +dependencies: + flutter: + sdk: flutter + flutter_web_plugins: + sdk: flutter + + synchronized: '>=3.0.0+3 <3.2.0' + battery_plus: '>=2.1.4+1 <5.0.0' + connectivity_plus: '>=2.3.9 <5.0.0' + device_info_plus: '>=8.2.2 <10.0.0' + package_info_plus: '>=3.1.2 <5.0.0' + path_provider: '>=2.0.15 <2.2.0' + screen_brightness: 0.2.2+1 + screen_state: 4.1.1 + sensors_plus: '>=2.0.5 <5.0.0' + volume_controller: 2.0.8 + pointycastle: '>=3.7.3 <4.0.0' + crypto: '>=3.0.1 <4.0.0' + http: '>=0.13.5 <2.0.0' + archive: '>=3.1.11 <4.0.0' + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.1 + path_provider_platform_interface: 2.0.6 + +flutter: + plugin: + platforms: + android: + package: com.example.analytics_sdk + pluginClass: AnalyticsSdkPlugin + ios: + pluginClass: AnalyticsSdkPlugin + linux: + pluginClass: AnalyticsSdkPlugin + macos: + pluginClass: AnalyticsSdkPlugin + windows: + pluginClass: AnalyticsSdkPluginCApi + web: + pluginClass: AnalyticsSdkWeb + fileName: analytics_sdk_web.dart diff --git a/analytics_sdk/test/analytics_sdk_ad_dedup_test.dart b/analytics_sdk/test/analytics_sdk_ad_dedup_test.dart new file mode 100644 index 0000000..26075ba --- /dev/null +++ b/analytics_sdk/test/analytics_sdk_ad_dedup_test.dart @@ -0,0 +1,181 @@ +import 'package:analytics_sdk/manager/ad_impression_manager.dart'; +import 'package:analytics_sdk/manager/session_manager.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// AdImpressionManager 去重逻辑回归测试 +/// +/// 覆盖 Bug 修复: +/// - _getUnreportedAdIds 异常时(返回 null)应降级为"继续上报(不去重)", +/// 而非被当作"全部已上报"而丢弃事件。 +/// 修复点:analytics_sdk.dart track() 中的 null/empty 判断分离。 +void main() { + const page = 'test_page'; + + setUp(() { + AdImpressionManager.instance.clear(); + SessionManager.instance.reset(); + }); + + tearDown(() { + AdImpressionManager.instance.clear(); + SessionManager.instance.reset(); + }); + + group('getUnreportedAdIds', () { + test('所有 adId 未上报时,返回完整列表', () { + final result = + AdImpressionManager.instance.getUnreportedAdIds('ad1,ad2,ad3', pageKey: page); + expect(result, ['ad1', 'ad2', 'ad3']); + }); + + test('所有 adId 已上报时,返回空列表', () { + AdImpressionManager.instance.markAsReportedBatch('ad1,ad2,ad3', pageKey: page); + final result = + AdImpressionManager.instance.getUnreportedAdIds('ad1,ad2,ad3', pageKey: page); + expect(result, isEmpty); + }); + + test('部分 adId 已上报时,只返回未上报的', () { + AdImpressionManager.instance.markAsReportedBatch('ad1,ad3', pageKey: page); + final result = + AdImpressionManager.instance.getUnreportedAdIds('ad1,ad2,ad3', pageKey: page); + expect(result, ['ad2']); + }); + + test('空字符串 adId 返回空列表', () { + final result = AdImpressionManager.instance.getUnreportedAdIds('', pageKey: page); + expect(result, isEmpty); + }); + + test('含空白项的 adId 被 trim 后正确处理', () { + AdImpressionManager.instance.markAsReportedBatch('ad1', pageKey: page); + final result = + AdImpressionManager.instance.getUnreportedAdIds(' ad1 , ad2 ', pageKey: page); + expect(result, ['ad2']); + }); + }); + + group('页面级去重:不同页面互不干扰', () { + test('同一 adId 在不同页面各自独立去重', () { + AdImpressionManager.instance.markAsReportedBatch('ad1', pageKey: 'page_a'); + // page_a 已上报 + expect( + AdImpressionManager.instance.getUnreportedAdIds('ad1', pageKey: 'page_a'), + isEmpty, + ); + // page_b 未上报 + expect( + AdImpressionManager.instance.getUnreportedAdIds('ad1', pageKey: 'page_b'), + equals(['ad1']), + ); + }); + + test('clearPage 只清除指定页面的去重记录', () { + AdImpressionManager.instance.markAsReportedBatch('ad1', pageKey: 'page_a'); + AdImpressionManager.instance.markAsReportedBatch('ad1', pageKey: 'page_b'); + + AdImpressionManager.instance.clearPage('page_a'); + + // page_a 已清除,可重新上报 + expect( + AdImpressionManager.instance.getUnreportedAdIds('ad1', pageKey: 'page_a'), + equals(['ad1']), + ); + // page_b 不受影响,仍然已上报 + expect( + AdImpressionManager.instance.getUnreportedAdIds('ad1', pageKey: 'page_b'), + isEmpty, + ); + }); + + test('离开页面再返回后同一广告可重新上报', () { + // 第一次进入 list_page,上报广告 + AdImpressionManager.instance.markAsReportedBatch('ad1,ad2,ad3', pageKey: 'list_page'); + expect( + AdImpressionManager.instance.getUnreportedAdIds('ad1,ad2,ad3', pageKey: 'list_page'), + isEmpty, + reason: '同一次页面访问内不重复上报', + ); + + // 离开 list_page(模拟页面退出清除去重) + AdImpressionManager.instance.clearPage('list_page'); + + // 再次进入 list_page,广告可重新上报 + expect( + AdImpressionManager.instance.getUnreportedAdIds('ad1,ad2,ad3', pageKey: 'list_page'), + equals(['ad1', 'ad2', 'ad3']), + reason: '页面退出后重新进入,广告可重新上报', + ); + }); + }); + + group('session 级去重:新 session 后同一 adId 可重新上报', () { + test('标记后 clear() 使 adId 重新出现在未上报列表', () { + AdImpressionManager.instance.markAsReportedBatch('adX', pageKey: page); + expect(AdImpressionManager.instance.getUnreportedAdIds('adX', pageKey: page), isEmpty); + + // SessionManager._createNewSession() 内部调用 AdImpressionManager.instance.clear() + SessionManager.instance.reset(); + SessionManager.instance.getSessionId(); + + expect( + AdImpressionManager.instance.getUnreportedAdIds('adX', pageKey: page), + equals(['adX']), + reason: '新 session 后 adX 应视为未上报', + ); + }); + + test('多 adId:新 session 后全部解除去重', () { + AdImpressionManager.instance.markAsReportedBatch('ad1,ad2,ad3', pageKey: page); + expect( + AdImpressionManager.instance.getUnreportedAdIds('ad1,ad2,ad3', pageKey: page), isEmpty); + + SessionManager.instance.reset(); + SessionManager.instance.getSessionId(); + + expect( + AdImpressionManager.instance.getUnreportedAdIds('ad1,ad2,ad3', pageKey: page), + equals(['ad1', 'ad2', 'ad3']), + ); + }); + + test('新 session 后重新标记,同 session 内再次去重', () { + // Session 1:标记 ad1 + SessionManager.instance.getSessionId(); + AdImpressionManager.instance.markAsReportedBatch('ad1', pageKey: page); + + // Session 2:ad1 解除去重,重新标记 + SessionManager.instance.reset(); + SessionManager.instance.getSessionId(); + expect(AdImpressionManager.instance.getUnreportedAdIds('ad1', pageKey: page), equals(['ad1'])); + AdImpressionManager.instance.markAsReportedBatch('ad1', pageKey: page); + + // Session 2 内 ad1 再次被去重 + expect( + AdImpressionManager.instance.getUnreportedAdIds('ad1', pageKey: page), + isEmpty, + reason: '同 session 2 内 ad1 已标记,不重复', + ); + }); + }); + + group('null 与 empty 语义区分(Bug 1 核心)', () { + /// 在 analytics_sdk.dart track() 里: + /// null = _getUnreportedAdIds 发生异常 → 降级:继续上报(不去重) + /// empty = 所有 adId 已上报 → 跳过整个事件 + /// + /// 修复前:null || empty 统一 return(事件丢失) + /// 修复后:只有 empty 才 return,null 降级继续上报 + /// + /// 此处通过 AdImpressionManager.getUnreportedAdIds 验证: + /// "全部已上报"场景返回 [](非 null),框架需区分处理。 + test('全部已上报时 getUnreportedAdIds 返回 [] 而非 null', () { + AdImpressionManager.instance.markAsReportedBatch('ad1', pageKey: page); + final result = AdImpressionManager.instance.getUnreportedAdIds('ad1', pageKey: page); + // 必须是 empty list,不能是 null + // analytics_sdk.dart 只应在结果为 empty(非 null)时跳过事件 + expect(result, isNotNull); + expect(result, isEmpty); + }); + }); +} diff --git a/analytics_sdk/test/analytics_sdk_decrypt_test.dart b/analytics_sdk/test/analytics_sdk_decrypt_test.dart new file mode 100644 index 0000000..7eed621 --- /dev/null +++ b/analytics_sdk/test/analytics_sdk_decrypt_test.dart @@ -0,0 +1,104 @@ +import 'dart:convert'; + +import 'package:analytics_sdk/analytics_sdk.dart'; +import 'package:analytics_sdk/config/sdk_config.dart'; +import 'package:analytics_sdk/utils/aes_gcm_util.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// GCM 解密固定测试 + validateEncryptedConfig 覆盖 +/// +/// 目的: +/// 1. 保护 SdkConfig.decryptKey + AesGcmUtil pipeline 的完整性。 +/// 2. 覆盖 validateEncryptedConfig() 的所有逻辑分支, +/// 确保上线前格式校验工具本身是可靠的。 +void main() { + const key = SdkConfig.decryptKey; + + group('AesGcmUtil GCM 加解密', () { + test('域名列表:加密后可正确解密还原', () { + final domains = ['https://api.example.com', 'https://api2.example.com']; + final ciphertext = AesGcmUtil.encrypt(key, domains); + final plaintext = AesGcmUtil.decryptResponseAuto(ciphertext); + final decoded = jsonDecode(plaintext); + expect(decoded, isA()); + expect(List.from(decoded), equals(domains)); + }); + + test('事件类型列表:加密后可正确解密还原', () { + final eventTypes = ['app_page_view', 'ad_impression', 'app_click']; + final ciphertext = AesGcmUtil.encrypt(key, eventTypes); + final plaintext = AesGcmUtil.decryptResponseAuto(ciphertext); + final decoded = jsonDecode(plaintext); + expect(decoded, isA()); + expect(List.from(decoded), equals(eventTypes)); + }); + + test('decryptResponseAuto 与 decryptToString 结果一致', () { + final data = ['https://api.example.com']; + final ciphertext = AesGcmUtil.encrypt(key, data); + final auto = AesGcmUtil.decryptResponseAuto(ciphertext); + final explicit = AesGcmUtil.decryptToString(key, ciphertext); + expect(auto, equals(explicit)); + }); + + test('错误密文抛出异常而非静默失败', () { + expect( + () => AesGcmUtil.decryptResponseAuto('not-a-valid-ciphertext'), + throwsException, + ); + }); + }); + + group('AnalyticsSdk.validateEncryptedConfig()', () { + test('域名列表加密串 → success=true, preview=第一个域名', () { + final domains = ['https://api.example.com', 'https://api2.example.com']; + final encrypted = AesGcmUtil.encrypt(key, domains); + final result = AnalyticsSdk.instance.validateEncryptedConfig(encrypted); + expect(result['success'], isTrue); + expect(result['preview'], equals('https://api.example.com')); + expect(result['error'], isNull); + }); + + test('事件类型列表加密串 → success=true, preview=第一个事件类型', () { + final eventTypes = ['app_page_view', 'ad_impression']; + final encrypted = AesGcmUtil.encrypt(key, eventTypes); + final result = AnalyticsSdk.instance.validateEncryptedConfig(encrypted); + expect(result['success'], isTrue); + expect(result['preview'], equals('app_page_view')); + expect(result['error'], isNull); + }); + + test('enabled_event_types 格式 → success=true', () { + final payload = {'enabled_event_types': ['app_click', 'ad_impression']}; + final encrypted = AesGcmUtil.encrypt(key, payload); + final result = AnalyticsSdk.instance.validateEncryptedConfig(encrypted); + expect(result['success'], isTrue); + expect(result['preview'], equals('app_click')); + }); + + test('解密成功但无法识别格式 → success=false, preview=前60字符', () { + // 加密一个 Map,其中没有已知 key + final unrecognized = {'unknown_key': 'some_value'}; + final encrypted = AesGcmUtil.encrypt(key, unrecognized); + final result = AnalyticsSdk.instance.validateEncryptedConfig(encrypted); + expect(result['success'], isFalse); + expect(result['preview'], isNotNull); + expect((result['preview'] as String).length, lessThanOrEqualTo(60)); + expect(result['error'], contains('0 个元素')); + }); + + test('无效密文 → success=false, error 包含"解密失败"', () { + final result = AnalyticsSdk.instance.validateEncryptedConfig('not-valid-ciphertext'); + expect(result['success'], isFalse); + expect(result['error'], contains('解密失败')); + expect(result['preview'], isNull); + }); + + test('空列表加密串 → success=false, error 包含"0 个元素"', () { + final encrypted = AesGcmUtil.encrypt(key, []); + final result = AnalyticsSdk.instance.validateEncryptedConfig(encrypted); + expect(result['success'], isFalse); + expect(result['error'], contains('0 个元素')); + }); + }); +} diff --git a/analytics_sdk/test/analytics_utils_fp_version_test.dart b/analytics_sdk/test/analytics_utils_fp_version_test.dart new file mode 100644 index 0000000..e1241dd --- /dev/null +++ b/analytics_sdk/test/analytics_utils_fp_version_test.dart @@ -0,0 +1,39 @@ +import 'package:analytics_sdk/entity/analytics_utils.dart'; +import 'package:analytics_sdk/utils/device_fingerprint_util.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// fp_version 公共字段测试 +/// +/// 确保 getDeviceCommonFields() 和 generateCommonFields() 均包含 +/// fp_version 字段,且值与 DeviceFingerprintUtil.kVersion 一致。 +void main() { + group('AnalyticsUtils fp_version', () { + setUp(() { + AnalyticsUtils.configure( + appId: 'test_app', + channel: 'test', + deviceFingerprint: 'fp_abc', + fingerprintVersion: DeviceFingerprintUtil.kVersion, + ); + }); + + test('getDeviceCommonFields() 包含 fp_version 字段', () { + final fields = AnalyticsUtils.getDeviceCommonFields(); + expect(fields.containsKey('fp_version'), isTrue); + expect(fields['fp_version'], equals(DeviceFingerprintUtil.kVersion)); + }); + + test('generateCommonFields() 包含 fp_version 字段', () { + final fields = AnalyticsUtils.generateCommonFields('test_event'); + expect(fields.containsKey('fp_version'), isTrue); + expect(fields['fp_version'], equals(DeviceFingerprintUtil.kVersion)); + }); + + test('fp_version 默认值为 kVersion', () { + // 不传 fingerprintVersion,依然应有默认值 + AnalyticsUtils.configure(appId: 'test2'); + final fields = AnalyticsUtils.getDeviceCommonFields(); + expect(fields['fp_version'], isNotEmpty); + }); + }); +} diff --git a/analytics_sdk/test/configure_null_test.dart b/analytics_sdk/test/configure_null_test.dart new file mode 100644 index 0000000..9a9b4fd --- /dev/null +++ b/analytics_sdk/test/configure_null_test.dart @@ -0,0 +1,39 @@ +import 'package:analytics_sdk/entity/analytics_utils.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('AnalyticsUtils.configure field semantics', () { + setUp(() { + AnalyticsUtils.configure( + appId: 'app1', + channel: 'ch1', + uid: 'user1', + device: 'Android', + deviceId: 'dev1', + ); + }); + + test('configure with null does NOT clear existing value', () { + AnalyticsUtils.configure(uid: null); + expect(AnalyticsUtils.uid, 'user1'); + }); + + test('configure with empty string DOES clear existing value', () { + AnalyticsUtils.configure(uid: ''); + expect(AnalyticsUtils.uid, ''); + }); + + test('setUid clears uid to empty string', () { + AnalyticsUtils.setUid(''); + expect(AnalyticsUtils.uid, ''); + }); + + test('reset clears all fields to null', () { + AnalyticsUtils.reset(); + expect(AnalyticsUtils.appId, isNull); + expect(AnalyticsUtils.uid, isNull); + expect(AnalyticsUtils.channel, isNull); + expect(AnalyticsUtils.deviceId, isNull); + }); + }); +} diff --git a/analytics_sdk/test/data_plus_sdk_method_channel_test.dart b/analytics_sdk/test/data_plus_sdk_method_channel_test.dart new file mode 100644 index 0000000..1f50843 --- /dev/null +++ b/analytics_sdk/test/data_plus_sdk_method_channel_test.dart @@ -0,0 +1,5 @@ +import 'package:flutter_test/flutter_test.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); +} diff --git a/analytics_sdk/test/data_plus_sdk_test.dart b/analytics_sdk/test/data_plus_sdk_test.dart new file mode 100644 index 0000000..ab73b3a --- /dev/null +++ b/analytics_sdk/test/data_plus_sdk_test.dart @@ -0,0 +1 @@ +void main() {} diff --git a/analytics_sdk/test/domain_manager_cache_test.dart b/analytics_sdk/test/domain_manager_cache_test.dart new file mode 100644 index 0000000..a9132f8 --- /dev/null +++ b/analytics_sdk/test/domain_manager_cache_test.dart @@ -0,0 +1,94 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:analytics_sdk/config/sdk_config.dart'; +import 'package:analytics_sdk/manager/domain_manager.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// DomainManager 磁盘缓存测试 +/// +/// 使用 MethodChannel mock 将 path_provider 重定向到系统临时目录, +/// 测试缓存读写逻辑而无需真实设备文件系统。 +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late Directory tempDir; + const channel = MethodChannel('plugins.flutter.io/path_provider'); + + setUp(() async { + tempDir = await Directory.systemTemp.createTemp('dm_cache_test_'); + // 将 path_provider 所有路径请求重定向到临时目录 + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall call) async { + return tempDir.path; + }); + // 重置单例缓存路径(确保每个 test 都从全新状态开始) + DomainManager.instance.reset(); + }); + + tearDown(() async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + if (tempDir.existsSync()) { + await tempDir.delete(recursive: true); + } + }); + + group('DomainManager 磁盘缓存', () { + test('缓存文件不存在时 loadCachedDomains() 返回空列表', () async { + final result = await DomainManager.instance.loadCachedDomains(); + expect(result, isEmpty); + }); + + test('写入缓存后 loadCachedDomains() 返回正确域名列表', () async { + final domains = ['https://api.example.com', 'https://api2.example.com']; + // 直接写入缓存文件模拟之前 initWithDomains 写入的结果 + final cacheFile = + File('${tempDir.path}/${SdkConfig.domainCacheFileName}'); + await cacheFile.writeAsString(jsonEncode(domains)); + + final result = await DomainManager.instance.loadCachedDomains(); + expect(result, equals(domains)); + }); + + test('缓存文件 JSON 格式损坏时 loadCachedDomains() 返回空列表(不抛异常)', () async { + final cacheFile = + File('${tempDir.path}/${SdkConfig.domainCacheFileName}'); + await cacheFile.writeAsString('not valid json {{{'); + + expect( + () async => await DomainManager.instance.loadCachedDomains(), + returnsNormally, + ); + final result = await DomainManager.instance.loadCachedDomains(); + expect(result, isEmpty); + }); + + test('空字符串或空格不被解析为有效域名', () async { + final cacheFile = + File('${tempDir.path}/${SdkConfig.domainCacheFileName}'); + await cacheFile.writeAsString(jsonEncode([' ', '', 'https://ok.com'])); + + final result = await DomainManager.instance.loadCachedDomains(); + expect(result, equals(['https://ok.com'])); + }); + }); + + group('DomainManager.reset()', () { + test('reset() 后 fastestDomain 和 reportDomains 均为空', () async { + // 先通过缓存文件让 loadCachedDomains 有数据 + final cacheFile = + File('${tempDir.path}/${SdkConfig.domainCacheFileName}'); + await cacheFile.writeAsString(jsonEncode(['https://api.example.com'])); + + // reset() 只清内存状态,不删文件 + DomainManager.instance.reset(); + expect(DomainManager.instance.fastestDomain, isNull); + expect(DomainManager.instance.reportDomains, isEmpty); + + // 缓存文件应仍然存在 + expect(cacheFile.existsSync(), isTrue); + }); + }); +} diff --git a/analytics_sdk/test/event_processor_logic_test.dart b/analytics_sdk/test/event_processor_logic_test.dart new file mode 100644 index 0000000..2c74d02 --- /dev/null +++ b/analytics_sdk/test/event_processor_logic_test.dart @@ -0,0 +1,128 @@ +import 'dart:io'; + +import 'package:analytics_sdk/manager/event_processor.dart'; +import 'package:analytics_sdk/manager/session_manager.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late Directory tempDir; + const channel = MethodChannel('plugins.flutter.io/path_provider'); + + setUp(() async { + tempDir = await Directory.systemTemp.createTemp('ep_logic_test_'); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (call) async { + if (call.method == 'getApplicationDocumentsDirectory') { + return tempDir.path; + } + return null; + }); + }); + + tearDown(() async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + await tempDir.delete(recursive: true); + }); + + group('uploadBatch concurrent guard', () { + test('resetState clears _isUploading so next uploadBatch can proceed', () async { + final processor = EventProcessor(); + await processor.initPersistence(); + + processor.enqueueEvent({ + 'event': 'test_event', + 'event_id': 'eid1', + 'app_id': 'app1', + 'sid': 'sid1', + 'client_ts': 1711929600, + 'device_id': 'dev1', + }); + expect(processor.queueLength, 1); + + processor.resetState(); + + await processor.uploadBatch(); + expect(processor.queueLength, 1); + }); + }); + + group('queue removal correctness', () { + test('enqueueEvent during upload does not corrupt removal', () { + final processor = EventProcessor(); + + for (int i = 1; i <= 3; i++) { + processor.enqueueEvent({ + 'event': 'evt', + 'event_id': 'eid_$i', + 'app_id': 'app1', + 'sid': 'sid1', + 'client_ts': 1711929600, + 'device_id': 'dev1', + }); + } + expect(processor.queueLength, 3); + + processor.enqueueEvent({ + 'event': 'evt', + 'event_id': 'eid_4', + 'app_id': 'app1', + 'sid': 'sid1', + 'client_ts': 1711929600, + 'device_id': 'dev1', + }); + expect(processor.queueLength, 4); + }); + }); + + group('dispose cache buffer flush', () { + test('disposeAsync writes buffered events to cache file', () async { + final processor = EventProcessor(); + await processor.initPersistence(); + + // Enqueue 3 events (below _cacheBufferThreshold of 10, so they stay in buffer) + for (int i = 1; i <= 3; i++) { + processor.enqueueEvent({ + 'event': 'evt', + 'event_id': 'buf_$i', + 'app_id': 'app1', + 'sid': 'sid1', + 'client_ts': 1711929600, + 'device_id': 'dev1', + }); + } + + // Call disposeAsync — should flush buffer to disk + await processor.disposeAsync(); + + // Wait for any microtasks to complete + await Future.delayed(Duration(milliseconds: 100)); + + // Verify cache file has the 3 events + final cacheFile = File('${tempDir.path}/data_plus_events_cache.jsonl'); + expect(await cacheFile.exists(), isTrue, reason: 'cache file should exist after dispose'); + final lines = (await cacheFile.readAsLines()) + .where((l) => l.trim().isNotEmpty) + .toList(); + expect(lines.length, 3); + }); + }); + + group('SessionManager re-init', () { + test('reset + initialize creates a new session ID', () { + SessionManager.instance.reset(); + SessionManager.instance.initialize(); + final firstSid = SessionManager.instance.currentSessionId; + expect(firstSid, isNotNull); + + SessionManager.instance.reset(); + SessionManager.instance.initialize(); + final secondSid = SessionManager.instance.currentSessionId; + expect(secondSid, isNotNull); + expect(secondSid, isNot(equals(firstSid))); + }); + }); +} diff --git a/analytics_sdk/test/event_processor_tombstone_test.dart b/analytics_sdk/test/event_processor_tombstone_test.dart new file mode 100644 index 0000000..6da7c78 --- /dev/null +++ b/analytics_sdk/test/event_processor_tombstone_test.dart @@ -0,0 +1,117 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:analytics_sdk/manager/event_processor.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// EventProcessor tombstone 缓存测试 +/// +/// 验证: +/// 1. 上报成功后写 tombstone,不重写主文件 +/// 2. loadCachedEvents 用 tombstone 过滤已上报事件 +/// 3. tombstone 达到阈值时触发压缩 +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late Directory tempDir; + const channel = MethodChannel('plugins.flutter.io/path_provider'); + + setUp(() async { + tempDir = await Directory.systemTemp.createTemp('ep_tombstone_test_'); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (call) async { + if (call.method == 'getApplicationDocumentsDirectory') { + return tempDir.path; + } + return null; + }); + }); + + tearDown(() async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + await tempDir.delete(recursive: true); + }); + + /// 写几条事件到主缓存文件 + Future writeCacheLines(Directory dir, List> events) async { + final file = File('${dir.path}/data_plus_events_cache.jsonl'); + final lines = events.map(jsonEncode).join('\n'); + await file.writeAsString('$lines\n'); + } + + /// 读 tombstone 文件内容 + Future> readTombstone(Directory dir) async { + final file = File('${dir.path}/data_plus_events_tombstone.txt'); + if (!await file.exists()) return {}; + final content = await file.readAsString(); + return content.split('\n').where((l) => l.isNotEmpty).toSet(); + } + + test('_removeEventsFromCache 追加写 tombstone,不重写主文件', () async { + final processor = EventProcessor(); + await processor.initPersistence(); + + final events = [ + {'event_id': 'id_1', 'event': 'page_view'}, + {'event_id': 'id_2', 'event': 'click'}, + {'event_id': 'id_3', 'event': 'purchase'}, + ]; + await writeCacheLines(tempDir, events); + + final cacheFile = File('${tempDir.path}/data_plus_events_cache.jsonl'); + final sizeBefore = await cacheFile.length(); + + // 上报成功,移除前两条 + await processor.removeEventsFromCacheForTest(events.sublist(0, 2)); + + // 主文件大小不变(没有重写) + expect(await cacheFile.length(), equals(sizeBefore)); + + // tombstone 包含两个 ID + final ids = await readTombstone(tempDir); + expect(ids, containsAll(['id_1', 'id_2'])); + expect(ids, isNot(contains('id_3'))); + }); + + test('loadCachedEvents 用 tombstone 过滤,只恢复未上报的事件', () async { + final processor = EventProcessor(); + await processor.initPersistence(); + + final events = [ + {'event_id': 'id_1', 'event': 'page_view'}, + {'event_id': 'id_2', 'event': 'click'}, + {'event_id': 'id_3', 'event': 'purchase'}, + ]; + await writeCacheLines(tempDir, events); + + // 预先写 tombstone,标记 id_1 已上报 + final tombstone = File('${tempDir.path}/data_plus_events_tombstone.txt'); + await tombstone.writeAsString('id_1\n'); + + await processor.loadCachedEvents(); + + // 只有 id_2 和 id_3 进入队列 + expect(processor.queueLength, equals(2)); + + // 两个文件都被删除 + expect(await File('${tempDir.path}/data_plus_events_cache.jsonl').exists(), isFalse); + expect(await tombstone.exists(), isFalse); + }); + + test('tombstone 为空时 loadCachedEvents 恢复全部事件', () async { + final processor = EventProcessor(); + await processor.initPersistence(); + + final events = [ + {'event_id': 'id_1', 'event': 'page_view'}, + {'event_id': 'id_2', 'event': 'click'}, + ]; + await writeCacheLines(tempDir, events); + + await processor.loadCachedEvents(); + + expect(processor.queueLength, equals(2)); + }); +} diff --git a/analytics_sdk/test/event_validator_regression_test.dart b/analytics_sdk/test/event_validator_regression_test.dart new file mode 100644 index 0000000..105a3ca --- /dev/null +++ b/analytics_sdk/test/event_validator_regression_test.dart @@ -0,0 +1,142 @@ +import 'package:analytics_sdk/entity/analytics_utils.dart'; +import 'package:analytics_sdk/utils/event_validator.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// EventValidator 回归测试 +/// +/// 覆盖三个历史 bug 修复路径: +/// 1. device 传入空字符串时,事件不应被丢弃 +/// 2. 逗号分隔字段清洗后为空时,字段应被移除而非保留空字符串 +/// 3. generateEventId() fallback 值符合 ^[A-Za-z0-9]{1,32}$ 正则 +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + // 为 SessionManager(内部依赖 path_provider)提供 mock + const channel = MethodChannel('plugins.flutter.io/path_provider'); + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (call) async { + if (call.method == 'getApplicationDocumentsDirectory') { + return '/tmp/test_validator'; + } + return null; + }); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + // 构造一个基础合法事件,仅覆盖要测试的字段 + Map baseEvent({Map? overrides}) { + final base = { + 'event': 'test_event', + 'event_id': 'abc123', + 'app_id': 'test_app', + 'sid': 'sess_001', + 'client_ts': 1704067200, + 'device_id': 'device_001', + 'payload': {}, + }; + if (overrides != null) base.addAll(overrides); + return base; + } + + group('EventValidator 回归:device 空字符串', () { + test('device 为空字符串时,事件不被丢弃(应正常返回)', () { + final event = baseEvent(overrides: {'device': ''}); + final result = EventValidator.validate(event); + // 空字符串 device 应视为未提供,事件正常通过 + expect(result, isNotNull, reason: 'device 空字符串不应导致事件被丢弃'); + }); + + test('device 为合法值 android 时,自动纠正大小写', () { + final event = baseEvent(overrides: {'device': 'android'}); + final result = EventValidator.validate(event); + expect(result, isNotNull); + expect(result!['device'], equals('Android')); + }); + + test('device 为无法识别的值时,事件应被丢弃', () { + final event = baseEvent(overrides: {'device': 'Windows'}); + final result = EventValidator.validate(event); + expect(result, isNull, reason: '无法识别的 device 应丢弃事件'); + }); + }); + + group('EventValidator 回归:逗号分隔字段清洗后为空', () { + test('ad_id 为纯空格逗号时,清洗后为空,字段应被移除', () { + final event = baseEvent(overrides: { + 'payload': {'ad_id': ' , , '}, + }); + final result = EventValidator.validate(event); + expect(result, isNotNull); + expect( + (result!['payload'] as Map).containsKey('ad_id'), + isFalse, + reason: '清洗后为空的逗号分隔字段应被移除,而非保留空字符串', + ); + }); + + test('ad_id 有合法值时,正常保留并清洗', () { + final event = baseEvent(overrides: { + 'payload': {'ad_id': 'ad_001 , , ad_002'}, + }); + final result = EventValidator.validate(event); + expect(result, isNotNull); + expect( + (result!['payload'] as Map)['ad_id'], + equals('ad_001,ad_002'), + ); + }); + + test('novel_tag_key 清洗后为空时,字段被移除', () { + final event = baseEvent(overrides: { + 'payload': {'novel_tag_key': ' '}, + }); + final result = EventValidator.validate(event); + expect(result, isNotNull); + expect( + (result!['payload'] as Map).containsKey('novel_tag_key'), + isFalse, + ); + }); + }); + + group('generateEventId() 回归:fallback 值格式合规', () { + final idPattern = RegExp(r'^[A-Za-z0-9]{1,32}$'); + + test('正常路径生成的 event_id 符合 ^[A-Za-z0-9]{1,32}\$', () { + final id = AnalyticsUtils.generateEventId(['event', 'app', 'uid', 'ts']); + expect(idPattern.hasMatch(id), isTrue, + reason: 'MD5 生成的 event_id 应符合正则: $id'); + }); + + test('空列表生成的 event_id 符合正则', () { + final id = AnalyticsUtils.generateEventId([]); + expect(idPattern.hasMatch(id), isTrue, + reason: '空列表 fallback event_id 应符合正则: $id'); + }); + + test('生成的 event_id 长度不超过 32', () { + final id = AnalyticsUtils.generateEventId(['a', 'b', 'c']); + expect(id.length, lessThanOrEqualTo(32)); + }); + + test('fallback h\${abs} 格式:字母数字开头,无连字符或下划线', () { + // 验证 h 前缀 + 纯数字的形式符合正则 + final hashFallback = 'h${('abc').hashCode.abs()}'; + expect(idPattern.hasMatch(hashFallback), isTrue, + reason: 'h\${hashCode.abs()} 应符合正则: $hashFallback'); + }); + + test('fallback t\${milliseconds} 格式符合正则', () { + final tsFallback = 't${DateTime.now().millisecondsSinceEpoch}'; + // 13 位时间戳 + 't' 前缀 = 14 字符,≤ 32 + expect(idPattern.hasMatch(tsFallback), isTrue, + reason: 't\${ms} 应符合正则: $tsFallback'); + }); + }); +} diff --git a/analytics_sdk/test/event_validator_type_coercion_test.dart b/analytics_sdk/test/event_validator_type_coercion_test.dart new file mode 100644 index 0000000..2843cde --- /dev/null +++ b/analytics_sdk/test/event_validator_type_coercion_test.dart @@ -0,0 +1,59 @@ +import 'package:analytics_sdk/utils/event_validator.dart'; +import 'package:analytics_sdk/manager/session_manager.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + setUp(() { + SessionManager.instance.reset(); + SessionManager.instance.initialize(); + }); + + tearDown(() { + SessionManager.instance.reset(); + }); + + group('EventValidator type coercion', () { + Map makeEvent({dynamic uid, dynamic channel}) { + return { + 'event': 'test_event', + 'event_id': 'abc123', + 'app_id': 'app1', + 'sid': SessionManager.instance.getSessionId(), + 'client_ts': 1711929600, + 'device_id': 'dev1', + if (uid != null) 'uid': uid, + if (channel != null) 'channel': channel, + }; + } + + test('int uid is coerced to string', () { + final result = EventValidator.validate(makeEvent(uid: 12345)); + expect(result, isNotNull); + expect(result!['uid'], '12345'); + }); + + test('double uid is coerced to string', () { + final result = EventValidator.validate(makeEvent(uid: 123.0)); + expect(result, isNotNull); + expect(result!['uid'], '123.0'); + }); + + test('int channel is coerced to string', () { + final result = EventValidator.validate(makeEvent(channel: 100)); + expect(result, isNotNull); + expect(result!['channel'], '100'); + }); + + test('string uid is preserved', () { + final result = EventValidator.validate(makeEvent(uid: 'user_abc')); + expect(result, isNotNull); + expect(result!['uid'], 'user_abc'); + }); + + test('bool uid is coerced to string', () { + final result = EventValidator.validate(makeEvent(uid: true)); + expect(result, isNotNull); + expect(result!['uid'], 'true'); + }); + }); +} diff --git a/analytics_sdk/test/logout_user_test.dart b/analytics_sdk/test/logout_user_test.dart new file mode 100644 index 0000000..5f93e4f --- /dev/null +++ b/analytics_sdk/test/logout_user_test.dart @@ -0,0 +1,36 @@ +import 'package:analytics_sdk/analytics_sdk.dart'; +import 'package:analytics_sdk/enum/user_type_enum.dart'; +import 'package:analytics_sdk/manager/user_manager.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + setUp(() { + UserManager.instance.updateUserType(UserTypeEnum.normal.label); + }); + + group('logoutUser', () { + test('logoutUser resets uid and userType to normal', () { + AnalyticsSdk.setUserIdAndType( + userId: 'user123', + userTypeEnum: UserTypeEnum.vip, + ); + expect(UserManager.instance.userType, UserTypeEnum.vip.label); + + AnalyticsSdk.logoutUser(); + + expect(UserManager.instance.userType, UserTypeEnum.normal.label); + }); + + test('logoutUser is idempotent — calling twice has same result', () { + AnalyticsSdk.setUserIdAndType( + userId: 'user123', + userTypeEnum: UserTypeEnum.vip, + ); + + AnalyticsSdk.logoutUser(); + AnalyticsSdk.logoutUser(); + + expect(UserManager.instance.userType, UserTypeEnum.normal.label); + }); + }); +} diff --git a/analytics_sdk/test/page_name_mapper_test.dart b/analytics_sdk/test/page_name_mapper_test.dart new file mode 100644 index 0000000..69e8095 --- /dev/null +++ b/analytics_sdk/test/page_name_mapper_test.dart @@ -0,0 +1,68 @@ +import 'package:analytics_sdk/manager/page_name_manager.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('PageNameMapper.normalizeKey', () { + test('普通字符串不变', () { + expect(PageNameMapper.normalizeKey('home'), 'home'); + }); + + test('去掉单个前导斜杠', () { + expect(PageNameMapper.normalizeKey('/home'), 'home'); + }); + + test('去掉前导斜杠后剩余部分保留', () { + expect(PageNameMapper.normalizeKey('/video_detail'), 'video_detail'); + }); + + test('纯 / 保留原值(避免 pageKey 变空字符串)', () { + expect(PageNameMapper.normalizeKey('/'), '/'); + }); + + test('空字符串不变', () { + expect(PageNameMapper.normalizeKey(''), ''); + }); + + test('双斜杠开头只去掉第一个', () { + expect(PageNameMapper.normalizeKey('//home'), '/home'); + }); + + test('斜杠在中间不受影响', () { + expect(PageNameMapper.normalizeKey('a/b'), 'a/b'); + }); + + test('斜杠在结尾不受影响', () { + expect(PageNameMapper.normalizeKey('home/'), 'home/'); + }); + }); + + group('PageNameMapper.getPageName', () { + setUp(() { + PageNameMapper.clearCustom(); + }); + + tearDown(() { + PageNameMapper.clearCustom(); + }); + + test('无映射时返回归一化后的 key', () { + expect(PageNameMapper.getPageName('/home'), 'home'); + }); + + test('有映射时返回映射名称(带斜杠注册)', () { + PageNameMapper.addMapping('/home', '首页'); + expect(PageNameMapper.getPageName('/home'), '首页'); + expect(PageNameMapper.getPageName('home'), '首页'); + }); + + test('有映射时返回映射名称(不带斜杠注册)', () { + PageNameMapper.addMapping('home', '首页'); + expect(PageNameMapper.getPageName('/home'), '首页'); + expect(PageNameMapper.getPageName('home'), '首页'); + }); + + test('纯 / 时 getPageName 返回 /', () { + expect(PageNameMapper.getPageName('/'), '/'); + }); + }); +} diff --git a/analytics_sdk/test/session_manager_regression_test.dart b/analytics_sdk/test/session_manager_regression_test.dart new file mode 100644 index 0000000..a38fff4 --- /dev/null +++ b/analytics_sdk/test/session_manager_regression_test.dart @@ -0,0 +1,189 @@ +import 'package:analytics_sdk/manager/ad_impression_manager.dart'; +import 'package:analytics_sdk/manager/session_manager.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// SessionManager 回归测试 +/// +/// 覆盖 Bug 修复: +/// - _shouldCreateNewSession 是查询方法,不应在非超时分支清空 _backgroundTime。 +/// 修复前:getSessionId() 在后台期间被调用时(如推送通知触发 track()), +/// 非超时分支的 `_backgroundTime = null` 会提前清空后台时间, +/// 导致后续 onAppForeground() 无法判断后台时长,旧会话被错误保留。 +/// 修复点:session_manager.dart _shouldCreateNewSession 中只在超时分支清空。 +void main() { + const page = 'test_page'; + + setUp(() { + SessionManager.instance.reset(); + AdImpressionManager.instance.clear(); + }); + + tearDown(() { + SessionManager.instance.reset(); + AdImpressionManager.instance.clear(); + }); + + group('_shouldCreateNewSession 不应提前清空 _backgroundTime(Bug 2 核心)', () { + test('onAppBackground 后 backgroundTime 非 null', () { + SessionManager.instance.getSessionId(); // 确保已初始化 + SessionManager.instance.onAppBackground(); + expect( + SessionManager.instance.backgroundTimeForTest, + isNotNull, + reason: 'onAppBackground() 应设置 _backgroundTime', + ); + }); + + test('后台期间调用 getSessionId() 不应清空 backgroundTime', () { + SessionManager.instance.getSessionId(); // 初始化会话 + SessionManager.instance.onAppBackground(); + + // 模拟后台期间有推送通知触发了 track() → getSessionId() + // 后台时间极短(< 30 min),不应触发新会话,但也不应清空 _backgroundTime + SessionManager.instance.getSessionId(); + + expect( + SessionManager.instance.backgroundTimeForTest, + isNotNull, + reason: '后台期间调用 getSessionId() 不应清空 _backgroundTime,' + '否则后续 onAppForeground() 无法正确判断后台时长', + ); + }); + + test('onAppForeground 后 backgroundTime 被正确清空', () { + SessionManager.instance.getSessionId(); + SessionManager.instance.onAppBackground(); + SessionManager.instance.onAppForeground(); + expect( + SessionManager.instance.backgroundTimeForTest, + isNull, + reason: 'onAppForeground() 执行完毕后应清空 _backgroundTime', + ); + }); + + test('未调用 onAppBackground 时,getSessionId 不影响 backgroundTime(始终为 null)', () { + SessionManager.instance.getSessionId(); + expect(SessionManager.instance.backgroundTimeForTest, isNull); + SessionManager.instance.getSessionId(); + expect(SessionManager.instance.backgroundTimeForTest, isNull); + }); + }); + + group('新 session 时清空广告去重集(session 级去重)', () { + test('新 session 创建后 AdImpressionManager 被清空', () { + SessionManager.instance.getSessionId(); + AdImpressionManager.instance.markAsReportedBatch('ad1,ad2', pageKey: page); + + // 确认已标记 + expect(AdImpressionManager.instance.getUnreportedAdIds('ad1,ad2', pageKey: page), isEmpty); + + // 重置触发新 session + SessionManager.instance.reset(); + SessionManager.instance.getSessionId(); + + expect( + AdImpressionManager.instance.getUnreportedAdIds('ad1,ad2', pageKey: page), + equals(['ad1', 'ad2']), + reason: '新 session 后同一 adId 应视为未上报', + ); + }); + + test('同一 session 内标记后不再返回未上报', () { + SessionManager.instance.getSessionId(); + AdImpressionManager.instance.markAsReportedBatch('ad1', pageKey: page); + + // 没有新 session,ad1 应仍被去重 + expect( + AdImpressionManager.instance.getUnreportedAdIds('ad1', pageKey: page), + isEmpty, + reason: '同 session 内 ad1 不应重复上报', + ); + }); + + test('连续建立多个 session,每次都清空去重集', () { + const ids = 'ad1,ad2,ad3'; + + // Session 1 + SessionManager.instance.getSessionId(); + AdImpressionManager.instance.markAsReportedBatch(ids, pageKey: page); + expect(AdImpressionManager.instance.getUnreportedAdIds(ids, pageKey: page), isEmpty); + + // Session 2 + SessionManager.instance.reset(); + SessionManager.instance.getSessionId(); + expect( + AdImpressionManager.instance.getUnreportedAdIds(ids, pageKey: page), + equals(['ad1', 'ad2', 'ad3']), + reason: 'Session 2 开始时去重集应已清空', + ); + + // Session 2 内再次标记 + AdImpressionManager.instance.markAsReportedBatch(ids, pageKey: page); + expect(AdImpressionManager.instance.getUnreportedAdIds(ids, pageKey: page), isEmpty); + + // Session 3 + SessionManager.instance.reset(); + SessionManager.instance.getSessionId(); + expect( + AdImpressionManager.instance.getUnreportedAdIds(ids, pageKey: page), + equals(['ad1', 'ad2', 'ad3']), + reason: 'Session 3 开始时去重集应再次清空', + ); + }); + + test('端到端:上报 → 同 session 去重 → 新 session → 再次可报', () { + const adId = 'banner_001'; + + // Step 1: 首次出现 → 未上报 + SessionManager.instance.getSessionId(); + expect(AdImpressionManager.instance.getUnreportedAdIds(adId, pageKey: page), [adId]); + AdImpressionManager.instance.markAsReportedBatch(adId, pageKey: page); + + // Step 2: 同 session 内再次出现 → 去重 + expect( + AdImpressionManager.instance.getUnreportedAdIds(adId, pageKey: page), + isEmpty, + reason: '同 session 内不重复上报', + ); + + // Step 3: 新 session + SessionManager.instance.reset(); + SessionManager.instance.getSessionId(); + + // Step 4: 新 session 内首次出现 → 可上报 + expect( + AdImpressionManager.instance.getUnreportedAdIds(adId, pageKey: page), + equals([adId]), + reason: '新 session 内首次出现,应可上报', + ); + + // Step 5: 新 session 内标记后 → 再次去重 + AdImpressionManager.instance.markAsReportedBatch(adId, pageKey: page); + expect( + AdImpressionManager.instance.getUnreportedAdIds(adId, pageKey: page), + isEmpty, + reason: '新 session 内已标记,不重复上报', + ); + }); + }); + + group('会话基础行为', () { + test('未初始化时 getSessionId 返回非空 ID', () { + final id = SessionManager.instance.getSessionId(); + expect(id, isNotEmpty); + }); + + test('连续调用 getSessionId 返回相同 ID', () { + final id1 = SessionManager.instance.getSessionId(); + final id2 = SessionManager.instance.getSessionId(); + expect(id1, equals(id2)); + }); + + test('reset 后 getSessionId 创建新 ID', () { + final id1 = SessionManager.instance.getSessionId(); + SessionManager.instance.reset(); + final id2 = SessionManager.instance.getSessionId(); + expect(id1, isNot(equals(id2))); + }); + }); +} diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..be3943c --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,14 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java +.cxx/ + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/android/.settings/org.eclipse.buildship.core.prefs b/android/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 0000000..0e90bae --- /dev/null +++ b/android/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,13 @@ +arguments=--init-script /var/folders/sx/77qvfp_d7bx3spccwlmk1vs80000gn/T/db3b08fc4a9ef609cb16b96b200fa13e563f396e9bb1ed0905fdab7bc3bc513b.gradle --init-script /var/folders/sx/77qvfp_d7bx3spccwlmk1vs80000gn/T/52cde0cfcf3e28b8b7510e992210d9614505e0911af0c190bd590d7158574963.gradle --init-script /var/folders/sx/77qvfp_d7bx3spccwlmk1vs80000gn/T/861a75667e10803d304a058d833cb7404195ca44013d0d61d3b653eb084379b8.gradle --init-script /var/folders/sx/77qvfp_d7bx3spccwlmk1vs80000gn/T/68eb1b6516fe21c6fbba58e63c99c3207ccfc918360613709367eecde56fa77f.gradle --init-script /var/folders/sx/77qvfp_d7bx3spccwlmk1vs80000gn/T/da64152279c70a8b4f3de4ca9ea66fd3b3405b7aca4e1f20f2d08e5593aa1ce1.gradle +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir= +eclipse.preferences.version=1 +gradle.user.home= +java.home=/Library/Java/JavaVirtualMachines/jdk-11.0.15.jdk/Contents/Home +jvm.arguments= +offline.mode=false +override.workspace.settings=true +show.console.view=true +show.executions.view=true diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..b506e39 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,140 @@ +plugins { + id 'com.android.application' + id 'kotlin-android' + id 'dev.flutter.flutter-gradle-plugin' +} + +apply plugin: 'top.niunaijun.blackobfuscator' +apply plugin: 'walle' + +def bundledReleaseKeystore = file('../keystore/nut.jks') +def legacyBundledReleaseKeystore = file('../keystore/nut.jks.old') +def resolvedReleaseKeystore = bundledReleaseKeystore.exists() ? bundledReleaseKeystore : legacyBundledReleaseKeystore + +android { + namespace "com.xvproject" + compileSdk 36 + ndkVersion "28.2.13676358" + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + coreLibraryDesugaringEnabled true + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + applicationId "com.jydj.tzzoer" + minSdk = 24 + targetSdk = 36 + versionCode = 1 + versionName = "2.0.4" + } + + signingConfigs { + release { + storeFile resolvedReleaseKeystore + storePassword 'jiahua123456' + storeType 'jks' + keyAlias 'nut' + keyPassword 'jiahua123456' + v1SigningEnabled false + v2SigningEnabled true + } + } + + buildTypes { + release { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + shrinkResources true + signingConfig signingConfigs.release + } + debug { + minifyEnabled false + shrinkResources false + versionNameSuffix "test" + } + } + + packagingOptions { + jniLibs { + excludes += ['**/armeabi-v7a/**', '**/x86/**', '**/x86_64/**'] + } + } + + applicationVariants.configureEach { variant -> + println "variant.name= ${variant.name}" + variant.outputs.configureEach { + //只处理生产版本 + if (buildType.name == 'release') { + println "buildType= ${variant.buildType.getName()}" + def outputFileDir = "${project.projectDir.absolutePath}/release/" + def createTime = new Date().format("MM-dd-HH-mm", TimeZone.getTimeZone("GMT+08:00")) + println "outputFileDir= ${outputFileDir}" + def abi = filters.find { it.filterType.toString() == "ABI" }?.identifier + def abiSuffix = (variant.outputs.size() > 1 && abi) ? "_${abi}" : "" + def path = "91dj" + '_release' + "_" + variant.versionName + abiSuffix + "_" + createTime + ".apk" + println("path= " + path) + outputFileName = path + } + } + } +} + +flutter { + source '../..' +} + +configurations.configureEach { + resolutionStrategy { + force 'androidx.core:core-ktx:1.6.0' + } +} + +configurations.all { + resolutionStrategy { + force 'com.tencent:mmkv:1.2.14' + } +} + +// 加入混淆配置 +BlackObfuscator { + // 是否启用 + enabled true + // 混淆深度 + depth 3 + // 需要混淆的包或者类(匹配前面一段) + obfClass = ["lib"] + // blackClass中的包或者类不会进行混淆(匹配前面一段) + // blackClass = ["top.niunaijun.black"] +} + +dependencies { + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5' + // android 读取渠道号码 + implementation 'com.mcxiaoke.packer-ng:helper:2.0.1' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'androidx.core:core-ktx:1.12.0' + implementation 'androidx.lifecycle:lifecycle-common:2.6.2' + def camerax_version = "1.0.0-beta11" + implementation "androidx.camera:camera-core:${camerax_version}" + implementation "androidx.camera:camera-camera2:${camerax_version}" + implementation "androidx.camera:camera-lifecycle:${camerax_version}" + implementation 'com.squareup.okhttp3:okhttp:4.9.3' + // 协程核心库 + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4" + // 协程Android支持库 + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4" + // 协程Java8支持库 + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.6.4" + implementation 'com.qiniu:happy-dns:2.0.1' + implementation 'com.github.Petterpx.walle:library:1.0.5' +} diff --git a/android/app/key/README.md b/android/app/key/README.md new file mode 100644 index 0000000..694a0ae --- /dev/null +++ b/android/app/key/README.md @@ -0,0 +1,3 @@ +运行 ./resp_genkey.sh xxxxx +xxxxx 为alias/密码和密钥的名称; +如果提示输入密钥,直接输入xxxxx即可 \ No newline at end of file diff --git a/android/app/key/genkey.sh b/android/app/key/genkey.sh new file mode 100644 index 0000000..ccf652c --- /dev/null +++ b/android/app/key/genkey.sh @@ -0,0 +1,21 @@ +#!/bin/bash +if [ $# -ne 1 ]; then + printf -- "Please input alias.\n\n" + exit +fi +echo "输入参数密钥$1\n" +keystoreName="$1.jks" +# echo "test code 2" + +printf "\n开始生成key:[$keystoreName]...\n\n" +`keytool -genkey -alias $1 -keyalg RSA -validity 10000 -keystore $keystoreName` +printf "\n[$keystoreName]正在进行pkcs12重新加密...\n\n" +`keytool -importkeystore -srckeystore $keystoreName -destkeystore $keystoreName -deststoretype pkcs12` +printf "\n删除老的key:[$keystoreName.old]...okay\n\n" +`rm -rf $keystoreName.old` + +cat /dev/null > ../../key.properties +echo "storePassword=$1" >> ../../key.properties +echo "keyPassword=$1" >> ../../key.properties +echo "keyAlias=$1" >> ../../key.properties +echo "storeFile=./key/$keystoreName" >> ../../key.properties \ No newline at end of file diff --git a/android/app/key/resp_genkey.sh b/android/app/key/resp_genkey.sh new file mode 100644 index 0000000..fcce4c0 --- /dev/null +++ b/android/app/key/resp_genkey.sh @@ -0,0 +1,74 @@ +#!/usr/bin/expect +#功能:自动填充生成androidkey功能 +#注意:expect 是用c语言写的 所以用argc和argv接收参数 +#注意:expect 只接受shell而不是bash 请./运行 + +# expect 常用命令 +# spawn 交互程序开始后面跟命令或者指定程序 +# expect 获取匹配信息匹配成功则执行expect后面的程序动作 +# send exp_send 用于发送指定的字符串信息 +# exp_continue 在expect中多次匹配就需要用到 +# send_user 用来打印输出 相当于shell中的echo +# exit 退出expect脚本 +# eof expect执行结束 退出 +# set 定义变量 +# puts 输出变量 +# set timeout 设置超时时间 + +if {$argc < 1} { + puts "Please input alias.\n\n" + exit 1 +} + +set password [lindex $argv 0] +# send_user $password +puts stderr $password +# spawn [exec bash ./genkey.sh $password] +spawn bash ./genkey.sh $password + +# expect "输入密钥库口令:" +# send "$password\r" +# expect "再次输入新口令:" +# send "$password\r" +# expect "是什么?" +# send "$password\r" +# expect "是什么?" +# send "$password\r" +# expect "是什么?" +# send "$password\r" +# expect "是什么?" +# send "$password\r" +# expect "是什么?" +# send "$password\r" +# expect "是什么?" +# send "$password\r" +# expect "是否正确?" +# send "y\r" +# expect "按回车):" +# send "\r" +# expect "输入源密钥库口令:" +# send "$password\r" +# # expect "输入目标密钥库口令:" +# # send "$password\r" +# # expect "再次输入新口令:" +# # send "$password\r" + + expect { + "输入密钥库口令:" + { send "$password\r"; exp_continue } + "再次输入新口令:" + {send "$password\r"; exp_continue} + "是什么?" + {send "$password\r"; exp_continue} + "是否正确?" + {send "y\r"; exp_continue} + "按回车):" + {send "\r"; exp_continue} + "输入源密钥库口令:" + {send "$password\r";exp_continue} + "输入目标密钥库口令:" + {send "$password\r";exp_continue} + "再次输入新口令:" + {send "$password\r";exp_continue} + } +# interact \ No newline at end of file diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 0000000..f835713 --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +-dontwarn javax.imageio.spi.ImageInputStreamSpi +-dontwarn javax.imageio.spi.ImageOutputStreamSpi +-dontwarn javax.imageio.spi.ImageReaderSpi +-dontwarn javax.imageio.spi.ImageWriterSpi +-dontwarn org.bouncycastle.jsse.BCSSLParameters +-dontwarn org.bouncycastle.jsse.BCSSLSocket +-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider +-dontwarn org.conscrypt.Conscrypt$Version +-dontwarn org.conscrypt.Conscrypt +-dontwarn org.conscrypt.ConscryptHostnameVerifier +-dontwarn org.openjsse.javax.net.ssl.SSLParameters +-dontwarn org.openjsse.javax.net.ssl.SSLSocket +-dontwarn org.openjsse.net.ssl.OpenJSSE + +# ffmpeg_kit_flutter_new_https_gpl:原生库 JNI_OnLoad 通过 RegisterNatives 绑定 AbiDetect.getNativeCpuAbi 等方法, +# release 下 R8 混淆改名会导致绑定失败 → JNI_OnLoad 返回 0 → 启动 SIGSEGV 闪退,必须 keep。 +-keep class com.antonkarpenko.ffmpegkit.** { *; } +# 通用兜底:任何含 native 方法的类,保留其 native 方法名(防止其它 JNI 库重蹈覆辙) +-keepclasseswithmembernames,includedescriptorclasses class * { + native ; +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ce10d11 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/java/io/flutter/plugins/JPFileUtils.java b/android/app/src/main/java/io/flutter/plugins/JPFileUtils.java new file mode 100644 index 0000000..663c6ef --- /dev/null +++ b/android/app/src/main/java/io/flutter/plugins/JPFileUtils.java @@ -0,0 +1,104 @@ +package io.flutter.plugins; + +import android.media.MediaMetadataRetriever; +import android.text.TextUtils; + +import java.io.FileInputStream; + +/// 本地视频文件信息:时长 / 分辨率 / 比例 / 高度 / 比特率 +/// 注:MediaMetadataRetriever 持有 native 资源,每次用完必须 release,否则多次调用后取不到任何信息 +public class JPFileUtils { + + /** + * 获取视频时长(秒) + */ + public static int getVideoDuration(String filePath) { + String ms = extract(filePath, MediaMetadataRetriever.METADATA_KEY_DURATION); + if (TextUtils.isEmpty(ms) || !TextUtils.isDigitsOnly(ms)) return 0; + return Integer.parseInt(ms) / 1000; + } + + /** + * 获取视频分辨率,格式 "宽*高",取不到返回空串 + */ + public static String getVideoResolution(String filePath) { + int[] size = videoSize(filePath); + return size[0] > 0 && size[1] > 0 ? size[0] + "*" + size[1] : ""; + } + + /** + * 获取视频宽高比 + */ + public static String getVideoRatio(String filePath) { + int[] size = videoSize(filePath); + if (size[0] <= 0 || size[1] <= 0) return "0"; + return String.valueOf((float) size[0] / size[1]); + } + + /** + * 获取视频高度 + */ + public static String getVideoHeight(String filePath) { + int[] size = videoSize(filePath); + if (size[0] <= 0 || size[1] <= 0) return "0"; + return String.valueOf(size[1]); + } + + /** + * 获取比特率 + */ + public static String getVideoBitrate(String filePath) { + String bitrate = extract(filePath, MediaMetadataRetriever.METADATA_KEY_BITRATE); + return TextUtils.isEmpty(bitrate) ? "0" : bitrate; + } + + /** + * 取单个 metadata;用 FD 而非路径设源,避开部分机型对中文/特殊字符路径的兼容问题 + */ + private static String extract(String filePath, int key) { + if (TextUtils.isEmpty(filePath)) return null; + MediaMetadataRetriever mmr = new MediaMetadataRetriever(); + try (FileInputStream in = new FileInputStream(filePath)) { + mmr.setDataSource(in.getFD()); + return mmr.extractMetadata(key); + } catch (Exception e) { + return null; + } finally { + release(mmr); + } + } + + /** + * 视频宽高,取不到返回 {0,0}。宽高一次打开取完,避免为了宽和高开两次文件 + */ + private static int[] videoSize(String filePath) { + int[] empty = {0, 0}; + if (TextUtils.isEmpty(filePath)) return empty; + MediaMetadataRetriever mmr = new MediaMetadataRetriever(); + try (FileInputStream in = new FileInputStream(filePath)) { + mmr.setDataSource(in.getFD()); + int width = parseInt(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)); + int height = parseInt(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)); + return new int[]{width, height}; + } catch (Exception e) { + return empty; + } finally { + release(mmr); + } + } + + private static int parseInt(String value) { + try { + return Integer.parseInt(value); + } catch (Exception e) { + return 0; + } + } + + private static void release(MediaMetadataRetriever mmr) { + try { + mmr.release(); + } catch (Exception ignored) { + } + } +} diff --git a/android/app/src/main/java/io/flutter/plugins/JPNativePlugin.java b/android/app/src/main/java/io/flutter/plugins/JPNativePlugin.java new file mode 100644 index 0000000..61f9504 --- /dev/null +++ b/android/app/src/main/java/io/flutter/plugins/JPNativePlugin.java @@ -0,0 +1,96 @@ +package io.flutter.plugins; + +import android.app.Activity; +import android.content.Context; +import android.media.MediaCodecInfo; +import android.media.MediaCodecList; +import android.media.MediaFormat; +import android.os.Build; + +import com.meituan.android.walle.WalleChannelReader; + +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; + +/// 本地插件:设备号 / 视频文件信息 / 渠道号 / H265 硬解能力 +public class JPNativePlugin implements MethodChannel.MethodCallHandler { + + private static final String CHANNEL = "com.yinse/device"; + + private final Activity activity; + + private JPNativePlugin(Activity activity) { + this.activity = activity; + } + + /// 注册 + public static void registerWith(FlutterActivity flutterActivity, FlutterEngine engine) { + MethodChannel channel = new MethodChannel(engine.getDartExecutor().getBinaryMessenger(), CHANNEL); + channel.setMethodCallHandler(new JPNativePlugin(flutterActivity)); + } + + /// 调用 + @Override + public void onMethodCall(MethodCall call, MethodChannel.Result result) { + switch (call.method) { + case "getVideoDuration": + result.success(JPFileUtils.getVideoDuration(call.argument("filePath"))); + break; + case "getVideoResolution": + result.success(JPFileUtils.getVideoResolution(call.argument("filePath"))); + break; + case "getVideoRatio": + result.success(JPFileUtils.getVideoRatio(call.argument("filePath"))); + break; + case "getVideoHeight": + result.success(JPFileUtils.getVideoHeight(call.argument("filePath"))); + break; + case "getVideoBitrate": + result.success(JPFileUtils.getVideoBitrate(call.argument("filePath"))); + break; + case "getChannel": // 获取android 渠道的东西 + result.success(getChannel(activity)); + break; + case "isH265HardwareSupported": + result.success(isHevcHardwareSupported()); + break; + default: + // 不回 result 的话 Dart 那边的 Future 永远不完成 + result.notImplemented(); + } + } + + /// 获取渠道号 + public static String getChannel(Context context) { + return WalleChannelReader.getChannel(context); + } + + /// 本机是否**硬解** H.265(HEVC)。只认硬解:软解(google/c2.android)当不支持,回落 264。 + /// 查询编解码器能力,任何异常都按不支持处理。 + private static boolean isHevcHardwareSupported() { + try { + MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS); + for (MediaCodecInfo info : list.getCodecInfos()) { + if (info.isEncoder() || !supportsHevc(info)) continue; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + if (info.isHardwareAccelerated()) return true; // API29+ 直接问硬解 + } else { + String name = info.getName().toLowerCase(); // 旧版本靠名字排除软解 + if (!name.startsWith("omx.google.") && !name.startsWith("c2.android.")) return true; + } + } + } catch (Exception e) { + return false; + } + return false; + } + + private static boolean supportsHevc(MediaCodecInfo info) { + for (String type : info.getSupportedTypes()) { + if (type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) return true; + } + return false; + } +} diff --git a/android/app/src/main/kotlin/com/xvproject/JPApplication.kt b/android/app/src/main/kotlin/com/xvproject/JPApplication.kt new file mode 100644 index 0000000..3a46c84 --- /dev/null +++ b/android/app/src/main/kotlin/com/xvproject/JPApplication.kt @@ -0,0 +1,5 @@ +package com.xvproject + +import io.flutter.app.FlutterApplication + +class JPApplication : FlutterApplication() diff --git a/android/app/src/main/kotlin/com/xvproject/MainActivity.kt b/android/app/src/main/kotlin/com/xvproject/MainActivity.kt new file mode 100644 index 0000000..ec57d81 --- /dev/null +++ b/android/app/src/main/kotlin/com/xvproject/MainActivity.kt @@ -0,0 +1,77 @@ +package com.xvproject + +import android.annotation.SuppressLint +import android.app.ActivityManager +import android.content.ComponentName +import android.content.Intent +import android.content.pm.PackageManager +import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugins.JPNativePlugin + +class MainActivity : FlutterActivity() { + + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + JPNativePlugin.registerWith(this, flutterEngine) + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL) + .setMethodCallHandler { call, result -> + when (call.method) { + METHOD_CHANGE_ICON -> { + val index = call.argument(ARG_ICON_INDEX) ?: -1 + ICON_ALIASES.getOrNull(index)?.let { switchIcon("$ALIAS_PACKAGE.$it") } + result.success(true) + } + // 不回 result 的话 Dart 那边的 Future 永远不完成 + else -> result.notImplemented() + } + } + } + + /// 切换启动图标:禁用当前入口 + 启用目标 alias,再杀掉桌面进程让图标立刻刷新 + private fun switchIcon(alias: String) { + val pm = packageManager + pm.setComponentEnabledSetting( + componentName, + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP + ) + pm.setComponentEnabledSetting( + ComponentName(this, alias), + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP + ) + restartLauncher(pm) + } + + /// 杀掉桌面进程,强制它重读图标 + @SuppressLint("ServiceCast") + private fun restartLauncher(pm: PackageManager) { + val am = getSystemService(ACTIVITY_SERVICE) as ActivityManager + val intent = Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_HOME) + .addCategory(Intent.CATEGORY_DEFAULT) + for (info in pm.queryIntentActivities(intent, 0)) { + am.killBackgroundProcesses(info.activityInfo?.packageName ?: continue) + } + } + + companion object { + //这个跟flutter的通道名称保持一致 + private const val CHANNEL = "com.yinse/request" + + private const val METHOD_CHANGE_ICON = "changeIcon" + private const val ARG_ICON_INDEX = "iconIndex" + + /// 图标 alias 所在包名:AndroidManifest 里 activity-alias 写的是相对名(manifest package=com.xvproject), + /// 与 applicationId 不同,不能用 packageName 拼 + private const val ALIAS_PACKAGE = "com.xvproject" + + /// 下标即 Flutter 传来的 iconIndex,顺序必须与 manifest 里 activity-alias 的声明顺序一致 + private val ICON_ALIASES = listOf( + "lld", "aiyinyue", "aiyuedu", "alipay", "bilibili", "binggandazhuozhan", + "meisijiejian", "myworld", "shenxuanzhizhan", "shuixinwuxian", "texuanxiaoshuo", + "tiantianmajiang", "tuyou", "weichat", "wendao", "yinyutusujiao", + "icon_16", "icon_17", "icon_18", //清明 / 端午 / 劳动 + ) + } +} diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..ca3826a --- /dev/null +++ b/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_bg.xml b/android/app/src/main/res/drawable/launch_bg.xml new file mode 100644 index 0000000..3d0315d --- /dev/null +++ b/android/app/src/main/res/drawable/launch_bg.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..3b00caaa7c2c031f63d7108d632fead267589ea8 GIT binary patch literal 50994 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_splash_bg.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_splash_bg.webp new file mode 100644 index 0000000000000000000000000000000000000000..31525078b80b9b42098109e630a5c0744edb1765 GIT binary patch literal 105502 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 NqSyny00000001lQ(Psbv literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/icon_1.webp b/android/app/src/main/res/mipmap-xxhdpi/icon_1.webp new file mode 100644 index 0000000000000000000000000000000000000000..98b0c1feca122e6e9d50916e11c3b410a49b3551 GIT binary patch literal 4602 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/icon_10.webp b/android/app/src/main/res/mipmap-xxhdpi/icon_10.webp new file mode 100644 index 0000000000000000000000000000000000000000..5e3fda6679f3769e6c3d3c4685486c388414a32b GIT binary patch literal 2870 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�`DV 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 MSi}1mKmY&$0LI803;+NC literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/icon_12.webp b/android/app/src/main/res/mipmap-xxhdpi/icon_12.webp new file mode 100644 index 0000000000000000000000000000000000000000..959d53dacf7e4b2d6a172da68cdee83699bcefeb GIT binary patch literal 3768 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 a@{5}+n^`ZO`vHA(^FQ zPsuDLvt*XcESV*zo`GBf_NLgXW+XHy{et){WaGD@BJ~^;62sz zC-AOlbQ-&g>EbB|xjj3BXa`wX-(YMIe%qF7S9pEyPh-geMaIX^)H9d`6 z*-Q_*ss^@?+jwb{i<)t(=7nBCEu#y0ADa)_^W?MEvle8@y?oJ_T*R!$6a3KcvjO|9 z5Bb~SXTY|R-};6Uw45`|{G;0MqLn$80`2;%169q9dO+Hu9Hh+m>QLYbH=00w)ma3A z5?(W4N*+zY?ReS&DSf#fBx-QZgs6Nr1;5)l2cYuXMF^n!aAnMuGhZEG$N5x`s>LDCMD3tFuud+Wp}*#O^Opiihn--+{UTB8wheC09H^q zAkGT_01!F=odGJq0Kfn~JsgWeq9LIc8#Htv1H`cg+y+JP2B`w8_;1J#`fvN^ewPspWxf)>Wxr^?1pk-+U;mr=|FT-nf7E|f z?-A`-k^j9vyy0(a{ppYayFQoM|1)WZeQAqI}ZbWHyUD^wqX?3JwxiITFe&PASiOCV;ryyC9=dpidgkXnsDvs@(8cT z$O35Ng9k_1D=O?}8CdIkrdnomO5O3D7?zE9wPYsZ4jUv5kv=Qc4U_?3s;qm($QOA6 z!f!;zwHTteiYd{`2_TQS2nw_T`Rt1xkHyGfw}tAQ#S? zujn;xA@}SZ_5c9>{1X5F%R~S9VgLW~lRfk%->mU9tJMVw)s+ch#@TqNeg~#Q`v{z9 zZ34Vlsp6_({o$RX9$pXamx%sG*L`E{;lLK}iHh7cX+{4fxY05ejV z0A0MDh}A&0!{a(Q$ncyr7a!NBpOk*a(}NpBJYI&!gD(x1QsEmlO_7n zLQc-+A;35Fcum=REr zBm7(t48CVJEu#Vs>S_%4wi`d_Oz&<7-NZMJ#3T1F&UNa?Nb3=atp^oEZ^Nf7^gW!a z4i=;p07{?d2^s1zHvieXW7LT*QP>M$QqzkYFFJm;d-etnI`(88y$=&9A4>=o2lyr^ zK7z!knPchP3)0dkKY~o7Ys$g~mBjhkzGU)%favIjpRSl5N}!*PRJn^~cZFo(5uuDn z@-~{o2sSC*OKh0SJHRNIf@vRzga9y`;|KKSnwwA{g3@Feq0~o3)~@rq_j8+I2<_wK zO=+q)H)Uu%9QQ7!^ns@I{lF(vLc^pz+)y{gg~xKVxLvy!g5y2HuY76QS&;eTlLDx|MW@79bjf zD~+7;>S=o-;j9V%S^c%*#% zmHNUXtnsizo+1(?pfPI%dGbP_J6n2EK3w#4V#gWpZQsTHa;}eD!=7;j!9pBH*5nV- zUNDm-){`hKDBxq4e88H@4p_Im#T$lKBr9)4abICi(5Fp0-fERYtj`c`Qja@}^uFB9 zaPXQ0+G;Fm?kjzvpxZo@IOuZG1H{7U<5U$4wFdUkDMr$L6!E4g&9*$kG9+7VT6|AMY z)WmcQYrS1+jjgIo@H(?jB42u_7x`XxsXl7LSos`k3G*_jDcW>tkm8N#at;y!-@+ve zS4@vV^jfWQkPCpm!~&$!GemxkouJu28L~u5Z3y2FfQc(txj*t~KF*$7FEc5V-BRMT z+dq`XtTR+sf+Q68M(CCW?dly6YVOMNT6O`*I~(RmC%Y8svkW8RG!l#F4h?+ypvUJZ z{{u8}aP;GCcS>4mqr?C} zZ%Mtb8I?E#v70EyGBqo6z;G^4JO|X&DFBoxbW~l zEEtf&g1Ka>^mBOxo&;|64EX7HU+v9=cPE|adp7F%qUCWXd(ovH7TT?qi%hZ3-wBcFm4>ghw98DWHi1D!V)!`X9HSpD<&+BJ)fDTIB$iYdy?{zv|(D$K$UX zztTvjswfI*v-&zX;0tuzl*4cobt!xzWDL@J)mJf_Y9yIU#5}s=0JZ=NxkCTY z_9M@g5s{*w+%^%-A~I>+WyapSfU>G)=1`ZT$by5mtFa0YTZv9YPuwI}| z=#k}evTOZvU`8T?RuwyDW9P9-6MlZH86yF8149i#c zwqd+=j`<3y=Orj?yC2TG82qFKb-jpjQ9gi6g{J3K197}#k89hVVAqbr@scCVL?)eU zP(dUdBo$AEDur*v`YQ&r10^iGg;kZlX&=L$O^wm4C~CA6z^3v#X-~=`n5ELPG}u%b zdObC>g#ZBx=<@D=8?T>*mKqd{w{u!DK8n{;;n<$2x{(E<@7c&ln-7%L1X)W&mKe}h zS#tH?Bg;}h!;@1_t6zAgeje7y9IS;2@E?EG{VmyxzDpDV=16$G1I0!+@%e*che~7I_}?kEwudTN7qV2eaIJEoxCFv~Tizr{ zJ5h~LTSuB7^hXaGZFdT12nb8#;FtM+m5F?k23Vj=LLlJ-{ZR5e%Sx#zkAqD+{Y$f& zI&w3RM~6|01g1^FE>a+Wk86{d|Dv%{5MeGyF6_f0-+bxxS6y!`WKv7t{bu(Z9jKT* zn-$Gmex1Jx^)5r`e`9pfX`c?!i`<=K=86FBa&{-3+cl5=H+SY};|hDJku596be##I z7|TE0#)Mr}gdhL~L?)5O51DeL%TJ#(1PJA_b+dq~EJR{Sxwco8CSeTOM$E_1`NrN3=O`53t*Dn=XI#>c zx)H@V5Mw`YD(|Hy-t~?~a5Rcf#-`E@=d=qyN*s`pCmMO^M2&+H9Fy^%h6d?@e}`ux zhoxafCMWH~^lyvqIBQuuF^c0;xKq@6g62kW zgtBwRtex~49?3B(9M5KDU8RG8qtv`DwN(^*D8Yn(@9SwrH9gA?Gsg*g-quwWzd4Z{ zs%P`6QuPJHO4!UKDkR;_(fX#9%DMXefJ^-G)QaT2F2RKxy0|GFWw?RkMLdv3DMoOK zBi7VgAv)IG1R3#UvIL|*FF}SIn!CuU^NnLhToxdo-@rh_9~6|4;R8oaa5o|%=SESm zVVhD|@SPkF8-6o~g|sIaZhqEvCaU<5<3=3nPt^GbaHI`sh-L}EVHXQh;0BJ|=M5JUbLW~4RB$KJgP)AEt=z&oy$qo?vuXHKQNUXXs zJjzEhtGs7e>gF0yy+-DhfpSvq7C7X|@D?s4pY8gQBUU;aND0hxCLVF{Af%^K-l*Q+ z(bP$pE+OM%X-UCsXGN$VnM}66n_}uc_ilJs#~)0llDBSKJkSyQwfuD@QnAt_hV?He zqGtN3rrI9z9Z&mCV}nUOb*WRTl~D6$-mgzqSoDqrW|Ca>eb2L_nD)f~ZuKtX3S>%e7p8V59W1dORUpUwv7?_( z{dNeL@-QAzT_xgJ4LAL|YEpK0Fq|pj_Nc_9r^(x%jR4bcjRiS9NoUjcxNO4F#(O!t zm>cP@N9K`-KE^Qw{Wf{Au(Utf$dpZIUVj&6c4T6VPj~hy?Mk-OB}v{6D^9=sCYU9O zG3FRiOL@u6k`VFo@!o0WQhD0G-#BT@b)LND=%oj*UWeJMKMX%q{P5!O?Zexaj%ipR z&~CcE5106sk!i9;eGp%{zZ$RGp6v;bfe9W7r=KUw(^?AvAj#rPNdx9u;> zPoNK4x~u&++E2nyGk=!;-~IFUFXhkt5BYx7zeNAA_Xhn$|0Dg+@_*us_A}nE?w_ci z=s(|oYQ5V3+#uO%kNgXZtdo$T!^}*3qY)V%YcX|H0GJ`Y}ILXCx&1A0wAM zJR9NKqGG0e=sqCugL+*FH>mcqR*~H`CCmQJu#{$9+K3+BVKY^%`5>qE^*9WJmuF2V zwZtSlLlgfjY>gGsk`LSJnVM;RnRewD^uYD`LNBpMbVm3F`Da22R(c-;1fYLjko(RE zb%O3=VwKcazk!d-$2@Vo5*DB*b;rE65wcdGTjOJ)K=9#LNBnCg+GrJ^38?lrI?=v0 zV4)v~@fujuu2H|n0aRTe!k^Qu%}HO!QZ?glZhn5gquCB64oOcW!CFxhs|YesNrn}^ zXz@`ozy8y#tA&wuG0J%9KdkUmq=~L4YvcQ8D?%yP5%$}}>#r7Zct!?hxe59isktO9 zSJM&hWA%}(pyzL-?(w~0Cnc*1oo}S@B=EL3Gl*jMs52=foc-@UN-#CrLjDqQh!%E? zL+Y!KM;tMnhRe^cqXRX~btD9)jqA5f4@&Qx*!v=5TBSwJ(J*mc5=0i>cyA(oYlDNW z9C8cy#fcR9ld=U5JKHr3pINUJ3NueuTEb;>&0yHlO@0xF`YrEdb|bXnLTLWatbj?% zR`o=^&r%z#eB{zE1ZD-R|1E4OyyZ}}gUSSm-~}I~mpBA54gTJm00QjNUYnq*@f?-v zAd$pOhYB4v5X67nE$ijQ@&F4SSIpY%ch<9$sFqJ1@*+;tM}1X_bo|H|Bs#BrAqabf zAM4MH;|)H>EJ~WffsJje!*#2yVu|)Q7@DsJ&qpGKIbP6BG?7E%iLSnI zBe_BN_I7=N@1GqIvq_y2j!;g36dayxL$*S4oLZM|YAKX|*G)D|>`(K=59vX<4pZH^ z{fW5EPf(>`p$e^sPft@6g{Kg_Uz>?t01`!Z=?xq zV=JxY?M;FeUsyB2_6F{Ig`|A(wDO-H-<@Y!!XWjfk$L+(NslRaTt9chkbUAi^SCDi zG_ySLga6ZQ#o)Mrmj4|N5patqvXQai^ZTI^j;C`ZFLqBoY zz4m0GtbS@u=OalD*JWv<5|mOtDg^~~1BjE;xd`hek$whqI3Kso`Rhi0>_^+meG-GN zCE^@y5R_o5y3=_$e$E=W7i4N-TK7lB0+x9Ozq6XLD}^E-_Sp|*mP=^Nm7ITkoRo)aL>F!hG+5>Q?LvG?4ozfo@4S!ZaMaZB^NC*^idPX z!0FdZ>**q{ct?0lQ(xGav34T>sB)E#BIn+s)!b~wWxCVJzk1y}a4gQ*?7EnlJG$qb z+w1veG6y{S+y6-(`+l8pmG_F$>9Fd}U5kl?=|*e1X z9A{T}7y)tO#eOWd{EIluhMYpsk=OQj`~RbePf1r+hODd~E}Wo`(Ff|f2L;V7H`bpfMaQeO zJ6QP=UaU@|oA4SsZ^+$q-ZE5KA2)&wN2R^7d;l+xbQ+w!#6x$RaSw3IBh*zK6TWLz zP8bF`#JbQeXLt;hUVk%6baEzldx zdPH<(Kew_i*e}+v^!djKjUg#g$;k;Jnh^Aw9gRFcHQ`XIM;OfTdY7(Gs2<k# zepBqXob>M|rn+EifT)2_<5`)4EtL;cb8;=AQXa+d-QWzUB8V|ge(Bst`csW?f7ZFd zfy%1A?vEUdc~W{fhQ3d#hVTsi4X70WzX!f2f&|E<3rd$Mwq~pCYO<`gxW%xE!mT$= zM?US+Oxdd&^X~3{uBvcB{?Q|)k&(!ZymT+7_Fk9X@#(&AAlF$QSU=%9y-|@Ct9$!6DzU;;R%>)2r{OS&aHC5pK4_Ppq}Ut#ouUt?hNjyps!1 zEXk}3_{6U`GZV`=)nKYKv=}3@5e^5gNKrl-VU{{hG%Uq=f#hhcC8?*%oz(pIZ(d_O zvunjWO4-j9=>ArJ+m6Q6C7h9=^410T_P22}U>%9&N!mLy?9W_~BgJ6)i*-lZGa?`hAQWKWqc8ppT|@a+Ie( zYC6RghwMZg0!-Vo{9zfDALYypTFaK=IYLHww?pgG9Eg5=BQJ3T34sg$ny~$S*3Jf> zCd$1Vp+)VtR=Yf2)J>TX zGz8*XOXCyjSr4F2yeIFDiX}%JzyBcgguA850;~^wrndnm1@iU}$=rfe*W25W52HbO zZ&NUD!j#@a6|BZnil1_*yqj<7WK{yKE9L8>;5$~J-duT<0g zPDF#zg++ogq3;*x5am)4NTbH?2`V0=#6qiq&9yMcMTNU^IT5^gJnaW z$>g#AeE2iU;Qh?(K>(%I{IT;8mv<8GGEKvF-p1){O-Hao?NvH{4PK{5p*W$(RUi#% zQ*G5VoxcHqeqRR>GE$b6dAIkuu(z5fPWd0xE{FOgg&sAbNXyY9Zc%m}-^$Ob2au}u z*t}X90igJkDRfySy*W~UnnIEP%`+qwZ<`pHdx2eTdMR{$Y~zW(oYq?Powc6~Cucvz zlMy})PsP8NErj<0Inr|dwn}`HO=W6Wd3ymmdkxTN!E=KUS?c5XF5mnEhNnEfj%L2- zaGUD5_CptIE&c;Wu5--8u0YL;NhM``1u-a}^EK)+Hs5?F3+2$;I1Dp7j2I`reDD-< zRQ}g`qOm}R>SPv4BAP5I*yD^xM+>s-&#$qus_6`I7g{O{ra;4r&HZ~E=Q0&+?$Bj3 zkQBXW+Qb&k2CZw9p1V^oH!;h4fE`Tu-<WU}h;MevZ^K(2&(ruYBdKp%Qp2Y zY5NObP@23H2HZ1HSe>LOthyC~I*igw@V{E`l*-Cx%pWk8G zvMqbQjSV!5iYXT-hUdL*VpH8L9G|rrc&dYUplCX`JKiAbD6-tUtaA09Y#tnm(FsvJU#9F zs*_HuO=@^bG{x0*71TCkE6?5rVXw~u1t|oa?HV-aF_T`pNlG>C+Zhh$Dpno^S3lA1 z=}ZV{YgPO3SwbB68x7{Yqu;v1T`U8)EV>nprH_8#aKK~3%tUPFr5-$_WG<4^dNUkm zlVFn(_P8G+FM9hx(JsXd&tws{$l=2}eexDI$x$@`5EoRXS?IjG;|^v?>pqfSkxt*x ziW|)|cT3pNP|+qOFzH$EojiyYsek1XA%|JLZy3r6G1`qH2Qok%F> zX7{_U#O^knoi&f{oEaik75(DH5I0x|P5V~?nWYtnXKzE+lQoKi$6ssCq$J=KT&K|K z2F42O8WR4jN_eS*$9LpQBxonPrqalN!Le@<*J$ zTX&F}BqDWzTx!%5!B61sOHg~%&EJH^M`Nk22ag71H@~T){)^_-PZwFkqM3+aeUl}xFVi}Wswb+{iA8@3uT4^Es{D=T2 zYkq=Ei%{TnwMs&NfqEi8%d}F+Y%|f?vQJ?*t^G%80cs?|SVk_3yr_X#7dkWdJsymf zSDkqk!!`pe`OTiAC%@8IG;2p^k59ld6e)0(mx547vg-by2VPjW_6T8`iL@5=?N06# zLHK?f$gz(=NW=?*RI^aUAM#e#=?R8nCAnZEF^3T(DFw)?bDj~_l%R~J$MO4D*jq_7 zRkqKkHP-Y4 z(IG2r!_f;@`TNwX#CS*<H=RukUwWI7Tj28ym@JoWb2}m`MI9lZf_Gq#3=P_y7!+;9hq`yKRGhL?V}a9ASE1Y*Wc;VFqpJ z$grZ>u-1%B51CYkel;&O^)PCFg@>JMB&XQ!hsC;L3x}Y^*Z8T_AQqmT%E&sS{$F3A z2+pqcHCJoXYhQ)Uu4Am2iAdNFNkbUYew*TU-SccA|vxuuE zF#LVw$a6n?AtptdC6#dT0l?h@`|9FE>SJK91}JK(HDrZHFgucfc+H=E;8_YqG^2an zY+SYgKb7B`nTvC7fck)CwMo_k8m|AQruzI&UN018zx)AVfRXg8u)|Dbq z*{@&%yZW$$Sti%Qdl|uVPTzOo?XWyReIU1M69P>k-nAlTBipjX$JE!f*?C7`-}q=i zIf&{jwv{Ciwcaos>6w4(brlNytMI`=;!55GrwW<@Dcqc?))V1I^@5;!XpH9%IEx?L zzYJ$14no3R{Z-#3jO<6cDzjdP6qFkK2Vec)d$ylEUJ7?+ql-{D-qkL*e14LRjw;+} zLfb78Rz`9s=!ZWJc0uG%Dpt0SSoU6+U_M%-IKBm1xEWALsD9Py&jQ;>;+4kU52Co| zJQGByN%G#)?;AM?1QvC_(!aCcTh2?id1F0L zNXjb)hidV#Jb`*YgwKX=arLAMK$Vc3XWxejJXl{+3ZL0eh+T0w>$l3m`%Nd!yf|lA zr;cD`ICAFE{L}ZL!Swb-81nE|s7|fn9SRWNrm3OJML4hR3&&e$K|;h+c+r!$d*Xf=1BYx0+){c355VeTowp-#Maq6^~3m0Ujta zBYe)s2Q*>?y{E@g!KA1`vA!(G9EXiwuMeUm;O+RKm-j(@Hw3F#E^%vtms2pSaHM5` zUlX~l>TSilS1ku-8ZWcpMrC7}kwU<97)Da;&o|je#el(z{mdO!~&j5Z_ zHN#4`jH(_Cox$h^(kU04ru{RFCCcz4xfmrUu^%|W>Dtard1^)fZ2+}L4IcRGB45OV_Iv}ba*d=H%&l!_-pe{v zD^HxQZKkM^DB7p{k?X0jjQ{`(xKZb2(H;u_Mwi>Vgt-2wf1P51ylPOBTpO&_GTsdu*iw>SW%hlsg|xZMbej}n4KXK%KhVDM_|kf~j{n%-^~YCQRnp=-r_7(uFC5nQfChHpD<{W2 zuhZKv)C{4xMkkXUhN?13avl9#%`r{!3jT}P_qzKAfub0~oCHfk+5$o8&QB(269b;& zNMg~qNA&SRbVPRZvzp-OI|VKUeiJW9QWGN=Fjq8 zlStN7C1D<9(Y(}PcqawlgI~Vslo{Q4nFBoou-TEsdhU_$9N&M_4Xq%B{~*y#smU;qNy-Gfzdr%+wY*kLo**MO8RE4z3EA~KUl2pf;K+x=q?OG zo~KM3uoIb`O7se z0g5V+j6CXZOX{vo6a~yXcw*ybZA4thxI4kv@O+;8X!#PnLD1l6%KN={FT&H|#nA^j z??gT0NCkI@imEiECO65uy_P`6kQlJRM0fRHWQLq8ZR2WwUqB(~fYMh<3F~P zLm5}XGPFWX;$N}{)={{=go*Tx+Wt^HkB&TvdU~iUGB#1p(|kjpyc_`u;}Nc{huQz1 zV~9U~-zBSZ0KT_IPi>AIR_FQxaiGY7n?iG46=vt1c`d2m6T26`MGjnNc%!{w)dReq z!9DWNIy?xe_&+z1WsSZ z0#~&MPkYWgQpixXTedu2H@rBC7zVqBzZ$+<{*k`am1Kkl1aIIm6Tto|T9w#n#h0>| z`9DbTnA>I1LnwHOf?fYpV4l6NZ~wBRd28xat)*4nAw+y|&`4Oj3NF|S2%ByCe9uhRNVvDn=;~3an~R{nl9$qp3jHF^Sba}afQApm+4=^z|9qR4 zZ17h9Fzym@QBNpHw)*Be3C_#>k7bOxt-n2yu7yHI>qRVgkGRat>Kr*F-fXJwr|vAO z-kxxU0&Fi5s^z}*Yt}U{0h{K^@N-tlLExy}z3Ago6QQ}+^>bJd#^77Zip-9guFM2C zu9W>S#aP<$DxF_#UCkNhJxGtQZv3k`VJX6ijK{CI;Ooh-tun!{%wHx6Gau= G0000kOQ>`J literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/icon_15.webp b/android/app/src/main/res/mipmap-xxhdpi/icon_15.webp new file mode 100644 index 0000000000000000000000000000000000000000..6df2642fa8aa2cc2eed61211392438b6ed8c069d GIT binary patch literal 5848 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/icon_16.webp b/android/app/src/main/res/mipmap-xxhdpi/icon_16.webp new file mode 100644 index 0000000000000000000000000000000000000000..68c3a786f8a5b7ee6b854cf6977111bfbaaa3c6a GIT binary patch literal 7184 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 S@WeGdEM8`w7B4fP0001YiYDj) literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/icon_17.webp b/android/app/src/main/res/mipmap-xxhdpi/icon_17.webp new file mode 100644 index 0000000000000000000000000000000000000000..2f184df57d609e1bda831798de87bb64e6577d4f GIT binary patch literal 7564 zcmV;79dqJRNk&G59RL7VMM6+kP&il$0000G0002L006%L06|PpNaFzj00Fnbux%v8 zvG+VPQ$$1owdPXqR<))yP&IawOIi7Kq7!A(Q(f@aQE!LyZ0l#!^Af%CnvVz4ydCqITk=fr6b6 zj4f_k{jR2fuis6Pn+a*B$fY*fR?v5ZF?KPvGjx|BwGa{{Q&@0=>lqy-9#x&_b`}py$dNJ8BYR_d|BDYYXiZ zxzr{(LykhSj$G|d-{H13?l{1W^3Xh?cH|@WQ0wz)Wr^9!c*bgm7=3v)LM)V*%?(~_ z>os}_S_$RdMurue^8N^`5%U})HDe!>6pec*eQ{NMcl{~z+70w2r2vVYI@ z()MlpW9+5-t}|@&pY(8H$b#%B4`-b^YT0bo6-6a8?WST!4xFtN9mrNG7p$X#U5}R%0TB*;I4<*+Gt zIHWf668a6?&1fpXzr3aR7qgGh8x@Wr{EkFp!MJJt3<${EpG=f@8R427O7@)~;Bc~4 zS~qgIHHU(F`Ut=JPk}9Ad)pz6nrJmA9K-d?>_l-RYT72}uQ|e|VzIf;uuQ9lqx>aO z<(kq2myEER@BV{JYNT{@m{)nsyX{`Tv1Hrm7v?2Mm&_YYvR>vTEE( zOLBtGdM>IQ^p0n)N}s9O2g_5;^SMO8hS9)>UheAL&SrDTTh29stVNIGVrCcPx-Q7$2C_*4!{dv@Ghsb?f)i zuHqa`AkZqaKmYh#HM{Nu@>YAh132gYv(ME&&e~pmJ3_4PWj5LH39%&=bJJZ-`XHTj z`Wv2+5@+0_xu;#x4sC5dA{vW3-|3$|eDA;3@zdj<8)qNL?U=Cl0nV}_95M+FJJ+yk zx>}*KbhXKF6~{BG74otgJz5xwZs-_4d-%ks(=+HK-{ipeH#`U5#{_7&;4IziG1SGS z{${B_Dw8hCEYuQ9Ex!Y4z^`r~xK&uo>?^dn5QWOj#7ujE6OfpR>hhQ#Dy3It$e^>+ zEG0r$$cN$By!3J;(+IHOWaR$Z;FHqB=(D7+-(|JeY&3ttbAlti$R|oRBTb>kpmt1> zlG(rB{U(Hhwx#4E45JY5g@)>{g07q>ul29&-3;Se)(aE|) z=U?VMDA6(aDrCkS$^A81R9ZzDxG2eK3S_>{^>7V710na3&_EWg@LxHW-{;n-Q96z+ z$|a>Wy1~i#AH_!A(MJGn$6oNvJbNN5Y(W^YzF|eG)2bw|lccJj2d|&qeb4Y(*JK`9*@cqMe`4AHeA3pdxjC1sWe{n`V1^ zq6=3-V&L;6kIKyyrx@*h;qZRe(Zu70u+QXdTj)F+hbh)2uu`AXT(LpwhG}7&H(Mfo zJ*IB&4b>mctW9AT=SCc5fpAF7L*@tooJp8SGTVbPc(%eNOGmeFO&;&%Kt=%DjyTRG zWqGi8nG-HbLMFBPNL(An!y)+w4`=JnrQVQKSxDnjFCnMLrzWSSthCs%4()u67WMLKhSg-8m)(Ukil4bQWD*t070IZ~S zL8S0?yyZl#ItA3Y!`DDZzL0h|4k*!A3+4WnjLG%4LkCo&pLvh=U;Kjmk0Ypd}&QsWMA`v7t;Ry={_^|!a4 zwQb_yFWk*aXxB>n%|-8dJles@yE+SU&0jwfi~;pzmd{{2CIsC?^*gNiyRkb)sQ|o#`O3LM9BIH zbmuD{56nL)PhLe?a5oW%T#zq(A0!@(z#Q^D+UPq( zX{m1TbKp_~+qm06YUw-V2Vh77N{HGQN<|PuBA3Wjn^0;>dXkc}j9Sfr-;Dn5{v;sx zxqX`-*Z_1a6j*#&yH3}W8?wCrMq`TD_2=+SP}jQLUp5pjuiFcK8ccus`0xyse34lu zB)agdgZ5f%S`#?+rZ@x&I4(w3;wpSTiO;9)v%n2Txvs-A5ZlwbXU>}Nb= z$Bf`m+2*kmWO=W=%;3E{&%vb3(YBlx0KF{C6JyoghX2%MC!*T?c(nucls*E0`V{w5 zWq84`OMc16{F!h{6llldcW85Mm(QE(Uva8#*qNPJKT}M}@w!E17SwDC{r~YqKjz>F zAl_sCZvBwMtej$Z8|}1!d}2s@y(80JrcR=_Z{0qXm25N}k#tgj4f(XO&Y!ULTW7Wv zt|VqSON2Cyso8Zoe!7QX(_!)SY5q5(I#0+aF_lZsvFU2fx}y>rBUJ(|;*IYlP&J*{ z(CVNa#j+plRYraRIIR54I^{-3kU-?#PMld%T!LfOwzK1h2-Sen*Atj-||NfEhs zsaVRk{fQShD54SVXThZjD9jEp{WqsESNIUC2#(A~U7PLH5py}ak=xhvqODD<- zZ();g-zDp;mEs2Pj@PsSa!-OD_6e)fgn6;Q3X&~!(F>uRdG#>k>3yzBy^~Owhr0<< z!~h(^!=`H!6uS0lE}_0~h-2AN0(CZu`#sLK33yhFMPMqsjocTa@wIfWMlQlakxbU@ zJMTi6G|t{D_Xg!Y_%SuV&8Ti?=iGR5h_ATYxnuG+6FQsF zNPN3ggxMPqmzUURh3?UR8|{Z~GPRbu4))=)2nz@5`nxTga{cH0_Tg!9M$LIJ9b;|> zJR(Yq@!Jp;L}j=u)!HCT&+2pyAx|vqm6r0o2bkLN>z~nIyviae{nsO%z*%Dw)jBlOmj##Ci1@GI>KN+jv<@;O zk-|t@;<3!`cJE_fQn1(o8^@9WQb1S7MT%H%TIqX`S1)*rUh`f{!F!*8i?fxL@^{h% zQy>=~@4hoF3m9l$DO-jbDg>twY4Y9`i?)_2qcd>wyU{Fvg@2bBp9K^t0 zeu3CI&E7!if$YUC6g^PUx#L40@DHC?h94HAKB_=I&CxFH8&ag2>b%^XDr9D+hU=IO z!$N2^eQnTR;(qN|&6+s_ql2sDkYimEpTBLsuI|7h+yaJ5EGbajnfRG0o}xzbaLIU= z9lO2!@un+zk1DrKveHm&S{is7I{O#Y#x=VF&*$T$Kg6-Qs>=dvvZpj7gc`m6&%ghP zg6%=!o}4B0f#-qIPM}Ac6CSuz{r)h?B9EMwmg$3kg!=*Y`;u25E80RDrn*ajLX1L7 z9KeWdo*n#DNp&;8goZz$A$k64E71|2AjJI7KbaaQYmYE2=?{Jrh~f&hj5^8Lrf-}X zP=4NLz92Ua(w)}-iYX0@{$v)>Z|Ux6%{3;LYb1BM=j^8W=U}`Viy%&oO>V}jK$)iS zS-xv>vHr`J*L!*^cGP0XGjRC+X3CCE87kHhJMhwR=Ni^eQi0x6a>4x zX|gBGAQV9;ToelrMN-OhXqw#H>|Sz=z|IKm(%6*EH{bN{2i=M0JZwXTz6hc}*$;Om zB8E3tV$@iaUtnFFgL{{CG;>0pgFFalGnQ+x3hm~#>(^Z6kPY&brX@c|{1lYhN~fIn ztqtA4{8M!;G?$y$tb~9EzuH>K0zhpQ=UO1c!72j6+@$lJBod|s;E^?|?BSVjRlQC` zWjkTvrF%MpGZ9*?fs)c9|43C+shuvFpCW@21-3X!tN_>`X4eJ6N22~K^HJYsB0~M`^ z!Z6?ilD)W&$ub%3%2DvZ-d9DN!?Lw>rev$LhmU%i|1MkxTFnLseI+_s*0UrlUlY@y z>$dK{X&JoSjU8F6v;D`=Q4MhL)_6C4Kl&iQ{ZBbXMAza7Rn58auWsWcK%8LE`iC=x zt9{mNhsR-g1StNY-~_qIE^^`5_DhBoN|gHb7O6;SKQYTPYq7_947Ff>Cpa$0$ytZ6 zgE*PA;Pp+4msCI`P(Vgww8?=Mk@;WIufB+1Mu)47C9nMt$1Mar-E^K`oqj;+5+mTZ^>-J&!xAAp++|P>fyD7*i1_} z#8xbcgVJG9ibhh2gvm~JHSp=K{*CE6>5kTaBgAy<3#_IbYmfQ$twc=|?s5>S=>sx8 z#Sdzry~%ghCrbHej_B14+;kO&e}=6+TdA}1cjZH&nrmVOdD#pnXPeciM@a14yxHP+ z^3q{<&+M)ETqXeW%Z8AsiEGW{6Jb>|5}Ei{QQ?fmeANl^w58mm1{ z6F>)krzEFU4sXGAPy?L1&*!~9OsxGHDw|sEx>`CNRphsZgo;9Si@R1{cK!JiV*bSo zchc^=6b06}^rHU9?N-BGa99WSoW|>KARWCnqtnx49M?Fw_Tx?sh(lp=Z?XT_6+Z~u`6+#QJ@J}QlBfvt2ZE=dP72(Z>Dw@2vB_fL ziXy*9TAKgX=$H$E6c_dpcVBetgLCr%&C)PwEq*Ub*lgOHWlYP5cs>KvE8S97VUQEv z8bW+^@9!Q_OiA<^nhT*yZCvDJ2U*Z#hB~Y(bAiajTHh;QP2E5Ms$w3_CqeMgxr`DV zXO>dIkb?)4iy*P^%EB(upzQtQzbvLJR{K`(GKn}Uzy3?T;?3U^PukK6`Pp1%Rf%lW zz>G%uuExz>wZ8IZ1ThoHujY)P9`dUI*$5S#-9BXoLgTia{hj6vokG#TsDVf6aX^VU zxik0i!dDa(>3d~U^JHu{;+lZvQ5S6|5dK71n-0!j2x$q~CSDeGLN~3zx5Sm4(?JG! zCd(U113O-JC1?bi4#sy>8bFp@d4x@=m`*es#tNaPrPijY;!%7MdoJ%4A~ zdX!EhzYyhlVS7gRa~A%Rv4-=<IWG4_rAJ| zL>_zxMvNS}HvONYGC;mm6Ul%|75qgM1NO}eFig~2p_VTEQLWIH*FamEN z9Lxl^Do6QE2hu&0W8X?n^#7MnsPO;pUhC59x$Uo5Be!7^-gEWac_}Q}D7oOZK?hfd z7Cbwiiwb>i<-%jcXZ+8a&h|SLfb_A|J^0&7>KGTw~1q4r6Q{`m?%nHAgAAs>6V>r zeRCe9bYP@R8U zzcYdk)*KL@)_o&Tei5Yp!4!NL1rET|KTD1o1w67Ka`4WoEQoQvx&QorVO`1iVdZ~F zEeCjT83TQ%UHzP%xSBZmf>8me>pMYz>3%L{{@${glHVCB+{Qv}%WbSVX?OSEZ*uZz z)TDj8d>Ry#2-M>8y*A5Ww(X6_mp&X%gR17v_%bUEFft|7)( z7N8j$WxBp;MqTKAJYCNPdctL6e{TqBQ90-p*;?iM&x6jo*?Yhw6irRk`G1dB{J(h* zp$;Q2SNJt^@mx-+gGocleB~KBIwG9mUYtmNw~M6}Tm^&HiaJm1X3;yx0oI|OcweQd zv}R7TD-|P^w7>x4ZveOIA5B7di`0rV-?SW*##K%o)W$FKm2cLyjGQl4e_S;3*81G1 zSGpqY@&C;%PK1~(C`t^c2a!}KEA4^hAC~lZz=Y zmN~rbNaiZ%YIcP^xcdCQOuza1l=HQwl4lFES6?AXM(1WTdjbd}an~(I!2U&jXI32u zbb|Q$i9?tWloN{N@1K0GZ6vFMk%m9+vF5DF|BvV{W(kTYhSw;xt?$Tb!)N)Az44Z` z@Vaj*LpGd0EZ$veYGK74hyj)ORHFiLWUnR<@GPtj=8ugs{Xs&H6m8iC-490niMu_C z^DY>7wZyboe8mi~W472g^)_r%H9Q!v3A=27A-Fz!Q2D2@Px(oh;*@gz-^ucDRRe-q zh$Wbz@2laa9m=&_y*;#S-r<0bBVlvGp5KU*#;wB$1{hqpo=l?BX`cK~z0(0uVy7Me zfeFPd$?cXeLC-D=zN^ae>}?di_1gpel!PyRR+Ve=-8Fr3iqH}sZk{Yf;d1g zHV&gEk;GA_QlC9-5J}E)3si;@AJNS10-1hwD6rf&D1;m|X1PN3LkwkQ z78*(?S86LQmeMdL90grxdHf08J`5xCvSADXqJ0^vaqc=yR*ba$Khj*Y@O?FU_FQgw z+RTB;s$SrtzvAaenj~IE3#1{Pvn+^(GLh&cfGDz2^MB{US|^a1HWW_VNUtN;52QUk zcV5T<=zFH&!I26s>%W#F;iwbD!I{wmu1{%5z2muM&;{-oOxax3XM!FC&N{Hrmlu!xY?k$BO7pQ!m3`apS!4qdcC*UPhJB5G4HM+l~1 i1@u4V!NJ{fml|3XYM_OMDUX8Agq9El9bmxOAOHZ?x&w#+ literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/icon_18.webp b/android/app/src/main/res/mipmap-xxhdpi/icon_18.webp new file mode 100644 index 0000000000000000000000000000000000000000..1a6746716e5ed5d18d85300318f9d9a27ab5548e GIT binary patch literal 7306 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/icon_2.webp b/android/app/src/main/res/mipmap-xxhdpi/icon_2.webp new file mode 100644 index 0000000000000000000000000000000000000000..5a3aa8543569c8f9233e18538848e2fe101f2c14 GIT binary patch literal 3578 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 A%>V!Z literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/icon_3.webp b/android/app/src/main/res/mipmap-xxhdpi/icon_3.webp new file mode 100644 index 0000000000000000000000000000000000000000..366adf8ad3dfaeae09dda58c67ef2c5ffa87c5dd GIT binary patch literal 4732 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/icon_5.webp b/android/app/src/main/res/mipmap-xxhdpi/icon_5.webp new file mode 100644 index 0000000000000000000000000000000000000000..0677cdf1fec68066583a9c42a3c86954a676f575 GIT binary patch literal 13216 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/icon_6.webp b/android/app/src/main/res/mipmap-xxhdpi/icon_6.webp new file mode 100644 index 0000000000000000000000000000000000000000..06bcb20cf03fa2672ee03672ddc42acfd4e79e4d GIT binary patch literal 5324 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/icon_7.webp b/android/app/src/main/res/mipmap-xxhdpi/icon_7.webp new file mode 100644 index 0000000000000000000000000000000000000000..ae5d1eadaf2b6b7a4f3858366cfd45c3598fad35 GIT binary patch literal 11280 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/icon_9.webp b/android/app/src/main/res/mipmap-xxhdpi/icon_9.webp new file mode 100644 index 0000000000000000000000000000000000000000..f66aeda409a15c1d1d8b2b7332732131ff3c9e1d GIT binary patch literal 3952 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 + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..e16bdd5 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/android/app/src/main/res/xml/totalfilepath.xml b/android/app/src/main/res/xml/totalfilepath.xml new file mode 100644 index 0000000..977055a --- /dev/null +++ b/android/app/src/main/res/xml/totalfilepath.xml @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..becd2b4 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,128 @@ +buildscript { + repositories { + google() + mavenCentral() + maven { url 'https://jitpack.io' } // 混淆 + maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' } + maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' } + maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' } + maven { url 'https://maven.aliyun.com/nexus/content/repositories/gradle-plugin' } + } + + dependencies { + classpath "com.github.CodingGay:BlackObfuscator-ASPlugin:3.9" + classpath 'com.github.Petterpx.walle:plugin:1.0.5' + } +} + +allprojects { + repositories { + google() + mavenCentral() + maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' } + maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' } + maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' } + maven { url 'https://maven.aliyun.com/nexus/content/repositories/gradle-plugin' } + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} + +def ensureAndroidNamespace = { project -> + def androidExtension = project.extensions.findByName("android") + if (androidExtension == null || !androidExtension.hasProperty("namespace") || androidExtension.namespace != null) { + return + } + + def manifestFile = project.file("src/main/AndroidManifest.xml") + if (manifestFile.exists()) { + def manifestPackage = new XmlSlurper(false, false).parse(manifestFile).@package?.text() + if (manifestPackage) { + androidExtension.namespace = manifestPackage + return + } + } + + if (project.group) { + androidExtension.namespace = project.group.toString() + } +} + +def setLibraryCompileSdk = { project -> + def androidExtension = project.extensions.findByName("android") + if (androidExtension == null) { + return + } + try { + if (androidExtension.hasProperty("compileSdk")) { + androidExtension.compileSdk = 36 + } else if (androidExtension.hasProperty("compileSdkVersion")) { + androidExtension.compileSdkVersion 36 + } + } catch (Exception ignored) { + // AGP 已锁定 compileSdk,跳过;该模块用自己声明的值即可 + } +} + +def alignAndroidJvmTargets = { project -> + def androidExtension = project.extensions.findByName("android") + if (androidExtension == null) { + return + } + + try { + if (androidExtension.hasProperty("compileOptions")) { + androidExtension.compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + } + } catch (Exception ignored) {} + + try { + if (androidExtension.hasProperty("kotlinOptions")) { + androidExtension.kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + } + } catch (Exception ignored) {} +} + +def finalizeAndroidAlignment = { project -> + if (project.plugins.hasPlugin("com.android.application") || project.plugins.hasPlugin("com.android.library")) { + ensureAndroidNamespace(project) + alignAndroidJvmTargets(project) + } + // afterEvaluate 阶段再强制一次 library 的 compileSdk,覆盖个别老插件 + // (如 flutter_custom_dialog 在自身 build.gradle 写死 compileSdkVersion 28) + // 在 plugin apply 之后声明的低版本,否则配 Java 17 编译会报需 compileSdk≥30 + if (project.plugins.hasPlugin("com.android.library")) { + setLibraryCompileSdk(project) + } +} + +subprojects { + plugins.withId("com.android.application") { + ensureAndroidNamespace(project) + // app 自己 build.gradle 已声明 compileSdk 36,这里不再重设(AGP 8.x 在 plugin apply 后会立即锁定 compileSdk) + } + plugins.withId("com.android.library") { + ensureAndroidNamespace(project) + setLibraryCompileSdk(project) + } + if (project.state.executed) { + finalizeAndroidAlignment(project) + } else { + afterEvaluate { + finalizeAndroidAlignment(project) + } + } + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/android/build_release.sh b/android/build_release.sh new file mode 100755 index 0000000..8277d09 --- /dev/null +++ b/android/build_release.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# build apk +# author: @sudong +# 使用方法:./build_release.sh 文件服务 http://in.test.tsmgsoce.com/sudong1/ +./gradlew clean +rm -rf ./app/release/ +if [ $? -eq 0 ]; then + echo "gradle clean success" + ./gradlew assembleRelease + if [ $? -eq 0 ]; then + echo "gradle build success" + filename=`ls -t ./app/release|head -n1|awk '{print $0}'` + echo "filename is $filename" + cd ./app/release +# pwd +# scp -P 22889 $filename root@35.220.142.29:/data/test-apk/sudong1 + fi +fi + diff --git a/android/generatekey.sh b/android/generatekey.sh new file mode 100755 index 0000000..eeb1a0a --- /dev/null +++ b/android/generatekey.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# 生成密钥脚本 +# author: @sudong +# 使用方法:./generatekey.sh alias + +if [ $# -ne 1 ]; then + printf -- "Please input alias.\n\n" + exit +fi +echo "输入参数密钥$1\n" +keystoreName="$1.jks" + echo "$keystoreName" + echo "$1" +if [ ! -d "./keystore" ]; then + mkdir ./keystore +fi +keystoreName=`uuidgen |cut -c 1-6` + keytool -genkeypair -alias $1 -keyalg "RSA" -keysize 2048 -keypass jiahua123456 -validity 3650 -keystore ./keystore/$1.jks -storepass jiahua123456 -dname "CN='中国', OU='广东', O='广东', L='广东', ST='广东', C='中国'" + echo jiahua123456 | keytool -importkeystore -srckeystore ./keystore/$1.jks -destkeystore ./keystore/$1.jks -deststoretype pkcs12 + +if [ ! -f "./key.properties" ]; then + touch ./key.properties +fi +# shellcheck disable=SC2129 +echo "storePassword=jiahua123456" >> ./key.properties +echo "keyPassword=jiahua123456" >> ./key.properties +echo "keyAlias=$1" >> ./key.properties +echo "storeFile=./keystore/$1.jks" >> ./key.properties \ No newline at end of file diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..a580ce1 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx4g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +android.useAndroidX=true +android.enableJetifier=true +android.enableR8=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ac3b479 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..b507b94 --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,25 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.9.1" apply false + id "org.jetbrains.kotlin.android" version "2.1.0" apply false +} + +include ":app" diff --git a/assets/images/acg/acg_like_nor.png b/assets/images/acg/acg_like_nor.png new file mode 100644 index 0000000000000000000000000000000000000000..b6e65a8d26129db76d5711db7902f511cc652141 GIT binary patch literal 398 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dymH|E?u0VRmj2ZLi&z}wkKsE#b zc{9NP&VX>IO`8T$aJK*aZJ=hMk|4iehWi2v4)6ERPdGoJp+2BseSv}24Wsoy*-xG> zjv*GO_Xgb-Yf<2EQQk8p_x1n!H%-}>Hrbi;%O)jAgjDeT<8j&P(K7Re==Auy`0VEH z7q7$*YVTcQ)fg=J@TJR{wbggI+$M3=zx;gu+ETY)4^rMSTj_sjjF8~2S6UueBxJc` zvB6g6fawzyZZUa2ym~@<)>7r!zP%rerqwn#iu+BguD7+?P!QzssAwI7s_X@kD~DM^ zQzZ0l*>?M69kevODE)bZ%>SR|vWv~Py-e?J$PmwYwiW*PDNCQh1%3 z-MViSNeA5p9t`qhj$N(t_3)r$j8)$?`NswPK!~6RR z`x6`%G!*QgU~oP_U_-sY`I}dx4g%%>db&7K5r}KKYZ~6Xz|JKMX zp*FUcdoAG-nZo?9&S=R9uhU5;*r(6PpvTFU&bj>ux^Z*uu7tEwU9!nsBvB> zmj_?)k)<9p5=D5d4Q7S89}HkzoLMT;!^FeDptqv>b-;z}k3h2+JYD@<);T3K0RY7r BG7|s* literal 0 HcmV?d00001 diff --git a/assets/images/acg/acg_narrow.png b/assets/images/acg/acg_narrow.png new file mode 100644 index 0000000000000000000000000000000000000000..9e41facc5e9abf4d586a1e1cdc8acbd16667b2b4 GIT binary patch literal 310 zcmeAS@N?(olHy`uVBq!ia0vp^=0I%5!3-orB0pXPQuYBpA+A8$z`#OR7laH99Q5=+ z3>_T{Jv~b>3FMmT>YD56nd|BTML-D10LlVMpvtuEeIPqTN`m}?85H*47r4J5;e12A z!Ta~?C!Fsu2vFKA^Z+P7$_w>z0?$>6iEb$cE{^GX%!-;QyoYYEmnUcfmcIe6FfN8cr9~eHE8ga*_?MURN o4#$u~GZl_GIR|*HwBO5EwwLSFr-_$m0bR%7>FVdQ&MBb@08x;4H2?qr literal 0 HcmV?d00001 diff --git a/assets/images/acg/acg_noval_back.png b/assets/images/acg/acg_noval_back.png new file mode 100644 index 0000000000000000000000000000000000000000..765d1c4b2e99a8cac856bfa8a5c5a9d8240733e6 GIT binary patch literal 651 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAjKcz{ocE0C_KscC3v0FrP3`ubX0TIS^Bq^GAxL_~y#hkJNhf*n9Xk0|Vn_PZ!6Kh{IcFaP~DD@VGwCQAxcuOKjKu z|8p)odR&{5bL^G%f$5K@w@8FK$oJYaJdnN-oxkW-lbP8A^|I?5gAJPR8YaaYV0vJc z$>X}xN^k??i|nTrIY+WX7cl3ran0>wl>C!2J$*uFpr_2H!k<-7Ouz2v-0?}YK-gaX zoyh~Y2mk9nMLaK+YvT;B$ZPhS{_yP1J8BsxB->{`dL$FJlb3tPF+;x_wGlIdo|P0k zK2LlYdFMgE<*##`S6rPrsjM`XyZ@8t#?V-9^D~Lh!^~ev`Ok2_d@5^~-tt?z3Aao3 zPViXYyHfkLu2+emd#cJVj>UUi#2-$b`a+2>OaG#)!?OjZuNNu!7*1RlcF9pY_RxZl zT0D!w?%!@!xT{jPdEuh^Lu%h%HDt=|;m!Q){={lipU1}wMo%P5bz+s@ep+;iF}>x} zi&axkZvOIQ^2y)Q0Vd~8JSqLsC~|W1%jY`r_iU;rZa@2};s2VMUFW5=R_eWvk;z}N zWY?ZY3JX*F16^HIK7BIseYKOnY|bRzPw^6pD$c$Uyh{YlFV?4inB2eU(?bhq`H0%S z_#cf5zn2CsYhPBhXw~CYfd@0MIz4xgaDJncw!uwXELKT7fU7@a&)vg!Vk|0(b(a2N Z@;d#0@#MOr$-t;*@O1TaS?83{1ON}LDoX$W literal 0 HcmV?d00001 diff --git a/assets/images/acg/acg_share.png b/assets/images/acg/acg_share.png new file mode 100644 index 0000000000000000000000000000000000000000..bcba9d24b4d639e147598d29ddc239d86ae17948 GIT binary patch literal 431 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~aez;VE0F$ABG^9lQ4>&`KuM5a zFvI=#3iJ0n7@ThiNSGk7z93~ri30-zqqL`sV@Sl|x6=>i9aa!<*}S{qpx>;Y|JT1& z<%&DB@UG|6kWag#q!y+dO6jSq|DQ97WksfHvd-g1iOHKa=Dl#5o)7Zx4p=Gag;FHqxs&26b(>EXCzHm~J>%z%}TpLb;PC#hvUWB1R86&-NCnMgH={h|k|*cZPCK*0=%(lcwVqX_@eRInw3xj8L^ms* zU72LIB-}Ok#JW?9-BuK(q{V-hE=o@KUHppWW heIZ&^^l4`ebBVvTYI$YVLtqdxc)I$ztaD0e0ssQj&i?=a literal 0 HcmV?d00001 diff --git a/assets/images/acg/cartoon_auto_play.gif b/assets/images/acg/cartoon_auto_play.gif new file mode 100644 index 0000000000000000000000000000000000000000..9a34c3220efe7c3ec32e7e3b582ebd9fc96265f8 GIT binary patch literal 15281 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 L%!dC%;^zMX`AL;_ literal 0 HcmV?d00001 diff --git a/assets/images/acg/cartoon_auto_play.png b/assets/images/acg/cartoon_auto_play.png new file mode 100644 index 0000000000000000000000000000000000000000..88ce07148e549a548febb0cbfdbc9f38d6261359 GIT binary patch literal 1686 zcmV;H25I?;P) z_V)HB*a$p-{yc{WC@!t7t#z_$QRTyj55Gl&QqAz1%e{N|ZUvoTICA7jfKp8{I(qcz z+hDW!(Dl6%Hb_cJiUl`dg!MyIR8)-D*4F;a>=9I6yLRm>Sn|nyZ~OM`A+`^1+qNwj zHUNgi#KiZNNm%53gGP|Fw6s%V6`;b=umRwRSyNN4Tk*rpj+;$QO-(8hz)Rr7U8-wwq^!TcKa(??&0HFdMuBlfm`o=AUsqSh?QY(@ z`5|}{w{PE07EXW(kl3?l535YTL`Uq~w~vVuX4!o9OK9{KM+R;Sw1F zCPrfa{{6}XM0j{OD^2x}A3x@M)Rhb%>)Eo|#b(LLF5%wZUa@J@CcY*cRs@TRiUdYJ zzK)5B;rjUacri9MW*Hb5Xp{YW)|PR##|S4VDJfx6q}=7pm-(9PHY6m3>jMG;*kfSi z=DN_(P#%0-TwI8=_q&~~MMp=Ua@MB1yPM05j10g40VhwM1YDnR_Uu`{k0BhInwr4J z$A_{tz!@>1tAnVhD7R8gmw+p~%#k?|a8WM0uk`fvWpx)XURPzNGvY8XFr~#RCTpKwe%Rpho~S1(=wa0CayO z031AckW{z$`T3ciJbCgtZl|&(Hv#Ja$9!}{uN|rDYDz)TuD`!uiSIwvWw>x4<9~ES_^3=OHoaB<~=*mK&76zsm+(>vBhOIE_-6&j-Qrng_*^Cv*w(Z)rD-7H6IRib% zl}4ih{c3e^aIn)5x)#up1}^SbxbTY5SI=RlH;v`&mMvTSuq-JpEgj;9NQ{m9`0?ZK z^!4?1DP`r<@F+2qR?yi4@z##s)4&75IC}%a>Lpi)BTKb?m=5_03JSi%kQoF^GeG{h z-qU>Lk?p(^+tExAb2g1zw{9grefsp9yLa!VoH=tQ!rp_KnVFvmF90{SS>P%#vvS$z zQ9j{GlOX?#p?X$!?}_ui<-mlc+*5>=-#vf+{0HErji`Z+Qt2g{lb$t^$Cb}yb8>P% zQhkG$V2F*4eGeCE18#3dgjfozZjj~n_wLM6tsjvqef+AWv(P`bruz6|`E>|JTzMZ}#MMg#j!pd)Vx`I`D^ym?PyIUYuHtUl9 zkBp2^v@&lrSp`;`J|IdHv1q*s4MMil@5IstDCJB;Lqq)F^ht~P=O4*(vYdPVuyvltPbq)x60`Wj zZdryuwc4V*RLmy!JyO#vuA6Hwc6Xt9rdGSxbB*h1LfcMW;55j#;9dUz&6b)U8?p;O zZ&vnvWqtQ`yQJi(`{gvgycWZoYqg7VEpJI{j$DjKzC@pKr zKH;n{B8qy?+(NwT|D^FPf4HQGYZjk#Rsz%9hkDz*V)o^oG!5|0G5vg5PVNn#@A{L+ zI6|@obs}UMJQO~@p6EF3CMWOCT1l@k%ZUxMH@CPixp$wfVD@&?{$RK1v8&}=cXr=( zW?l0|D3L#P%4iyPEa7YQ@ZwR@ lEbK8_qO$g;y3`*1`K;PLj62mjgn^;X;OXk;vd$@?2>>05=Q;oY literal 0 HcmV?d00001 diff --git a/assets/images/acg/cartoon_menu.png b/assets/images/acg/cartoon_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..d9f4d7412a3c009a41f2f82f41ff6488779402eb GIT binary patch literal 350 zcmeAS@N?(olHy`uVBq!ia0vp^ohs^9mzun*6exSh)5S3);_%yR-du+bcvvn(6uqhWeeo~< zZI&edMFCf42x(O9j_e8CB=BlZ@w@PMDz?f+`eyFjYBvRpIwea_>Fkb45}AFg$+c|b z5yzY??nU#AYyXxCNd4PU{H}h9Ay3v>mf4G0eUyrv-&nA8-HRpHb~)xAyi@eM wZr7sZ&!VxWF&7U{z9PDJ!o^uu&+n+l8Ll?ZJCS;3HPANzopr0ILSC-v9sr literal 0 HcmV?d00001 diff --git a/assets/images/acg/cartoon_progress.png b/assets/images/acg/cartoon_progress.png new file mode 100644 index 0000000000000000000000000000000000000000..63d9e67c7757e7fe2ff796c4fba430c6a85dcd9c GIT binary patch literal 432 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?2=RS;(M3{v?36l5$8 za(7}_cTVOdki(Mh=D0s)gy?}XFqxObQWs4?di$gb$nVGrtE!z8K)*WxD zAB<0JnM4j4@EkVaQdl%u-rrs>QGV)&V&1wtFV2bvxqO_nUwP4=ntRGB9p&@0Ty;)# zv(0&FJzH7gC+DOq9T{r#IseF9jIk5GDYMYcPFSliKz)AaTDKKb{R;$NJr(vj61=yj zsq=#V{D{sO7nr#Pg{zXp3>UiZ`EjnFan^$G>4grdNpIvP{owAonQXX7$Z?Kcw(E^| z3J0f)e}3PgVIQx3Zo&con)Q+!733`(n4UGwR-Um_MrEboMW1~R38ss7)-;~@aoCbi z_lD3yzkLolIu^$qnPXb2ezGX9inlxH@?yIG8W)Q%2b@}*{VzP{H*SeZkZ4PkaN5J5 Y{!iikp6OFVdQ&MBb@01myRCjbBd literal 0 HcmV?d00001 diff --git a/assets/images/acg/forward.png b/assets/images/acg/forward.png new file mode 100644 index 0000000000000000000000000000000000000000..f66d65b7439c760efc7099d2e8bfcacc6072e493 GIT binary patch literal 416 zcmeAS@N?(olHy`uVBq!ia0vp^oZ2y0Hi42L zzhH(2h5PRpBslCB$TwI&!T)^mPs5W842(RUE{-7)hu>bin|0WLr!Dbx*Z;|G-|M$Z z=e&{LeErB|zOWC~VO;z#w0R3xUwxJ8#oj$fac82g%)fb$%YWv4U%cLY;@Ud}T)Ce_ z`86hee`P8Xx$k(Y&c&w=k##Qhle{LZTkGQ~u|nV1n}1qjsn;QgS`*c&&PQAhIaq0K z>}Y*rIKfLxB}wV3?-ZfB%DFA+?S6+6y*TA_muQ3uMSBLWS#sCM>a5DY_Up1~K}M5f z%qH8cRt<>a47uR7Vq=)o#Hm7`mP|L89(Sm-igQ}jq0TJMuxs8Hfts=Vf7{ISGA`a) znfYhG-Q7JV^R&OM-?rmt<(J?0D$3fu*@_uoW#7BI<|)6r`toz(i*DV?+B)k!@3jS~ VmRSbR!+~MO;OXk;vd$@?2>^MO%lH5Q literal 0 HcmV?d00001 diff --git a/assets/images/acg/list_cycle.png b/assets/images/acg/list_cycle.png new file mode 100644 index 0000000000000000000000000000000000000000..2b43e12a98864a6b7a872cdd826f1afc516b191f GIT binary patch literal 639 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~Wq?nJE0F$A7T~fn%?D~0Dhcun zW|*&V|9!)L2Z8v43H}D>*Z1egb86jUU|@Xg>Eaj?aro_Y?`bUxJgz_4L|nW7>TmmP z;w06qG~uwZ)BYFBTbe}pIay1O+kSrdAoWH>ZrlaA zgp0@Gf*)!rv{q-|h-ll!)54{u_eea7Q&b>JBaqAVvcbc1uLNZ$ROMc%^-6mzbF8e# z(qj#;>{GSsId$0;VvkMd`cJy}EnfC#mI_;C&Se=Tg@58pvRf1H=tsy!LozocWlCvk}SL?C;_tk8g!nVg;o?3RGIao+faGhL^%bP{E32Uqm z=Gg0ml%IW-y)IK|^EsHX zi%uF}{kF}!L*0Ibr*^jE{q+k=e+KukRHf=4KQGAIF12W}%j?E9{8~SEXRj+cG-G~F zq0NdMqn{_QJwMDbf0LNhgqAl?LhemWcQ22Z!Sd~k!?CGCEh6Nix$9!WK`)9c0g0GfL8GlK?ZNolA mm(@MCO%If-D}Nf*G1xA1XW^K#?lCYiFnGH9xvXZ2y0Hi42L zzhH(2h5Pdz_6xi(h@W7v{(Qco)5$Xo42=AqE{-7)hu>bi+jT^Nr!CQUjs4cSC;!I3 zeRt>(d$w5Wq(r|O{#Lt7Q*O20&HMhb?}$>rOIZ7+Kijeb-!IYKydWZMfmV?Fsy_}L z=XYLtvfE6@{iiVh?v>MzB+g9LxTt+e(aUt7f@$F6;@Cs0;?#Dx6iTWdIjff#Q^vVH zV`_l<=9Xv^?+toD@kmvXvqBoHTV~A;=?-){>+6+w>9B8-x#n)qyS`R?mRMVO`h;<+ zclVb}f1vdA@be|B&%XT9t2#rq=IUFaWCu1*W+U&tEaIB+iZ@hwGGeX1GCn-1@4dv4 z^IV8i<)^GQ-p8MuPtjUvAw6NArCG?z*@wJhRang6~{lurYYL`njxgN@xNAu!+ls literal 0 HcmV?d00001 diff --git a/assets/images/acg/noval_sign.png b/assets/images/acg/noval_sign.png new file mode 100644 index 0000000000000000000000000000000000000000..5fcf424669fb7d8aa138e165be38974084c846aa GIT binary patch literal 723 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw3=&b&bO2Hb0X`wFKpF^EuU-vdz{&3J?sxCr z-MV$l($Z2+P7WxZo}Pa6=ux-=AR{m^@cZ}gPoF-$c=6)SojX^pS~Yw2?6kBrpj2gL zWmQ$x|Ns9pDo%I<`SK+}e!&e4`*Y(3I21O#zdxbFV*dF64+o7I-=AMUeqhG}gB8oq zf9|zlU|?eQba4#vIKK7z^Rh<<0&NfZMNj)4ys>cl&i}jbaDH32!BnHe_g?&ymgg%H zdU~ct82<3fF1R)0o~X8=$0qZLls&bayFK0R9gSF0ROOMBJz>q}^Ba$szdS7*lk>qz zT2Nd{e`#)y?(LY~oYT7^61L5>dceEsmdzt>qtA@dy?5T&-%+&wEY4q{Y@W!w^L)_C zPsWBO$?Pk??O0lUb>-CdeaXxV`@3Fk`YBcGf3@AlB$<2Rq*cbtW_5XSi1zF?(R+KV zUG$BI)b8}0mak!<(tmZ&NV7ijyOMBirpTfhR!2gsbyjWfkiNh@Uw`UHHm>>G+Zp33 z(v@>7)DvAhCW)OIkV zDA(K)sI|`3x4r9{*Ib3fcBdo3?vJDjn9_C`ao+jxTFQUJLu=3Z2Uc{RO21*O=OXj$ zuFc`H!q>L?g}bdE2*!(Vzc+RA`VA9mT6bE{%e@&|nA+ogbgtl~)q>W0E*d1tS?$*Q zvm-q^SN+V-n3|uB%)Zw?>c9EY@_Fkv`{J52S9b}Vzx`fjrt|do?B6?&x9Iuy`r0kv zv2wio-bSi*jrqhk+#eVEtSQ)QC^hHWT=@vEzl;&Tw5)$|36+6To2RRv%Q~loCIFX6 BLdyUE literal 0 HcmV?d00001 diff --git a/assets/images/acg/noval_time_back.png b/assets/images/acg/noval_time_back.png new file mode 100644 index 0000000000000000000000000000000000000000..0ace358a236d6a17e8841ed20672e6b5bd87a98e GIT binary patch literal 677 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~aez;VE0F$ABG^9lQ4>&`KuM5a zFvER?{S6Ka-U}p5IA36}K44>>{Bi~cCP7aZ$B>A_Z>QfbYf<2F_1)2&@$Y}Obq`mF zTjQzV+ncZ1WX$o^6MFPzeY^^bY5zlkm4elVUDPJ#(UwUKKetw&WlcV^{%@cD~{_g4h zpu4NRS9fBLG3T=CNg2vJcV9nx%*1vow~8G@iz_GAYy(}~|4n};H^E_{%QKISXD_kMRhaLfDN}bfYtI4g2`?CL zUg&gh=!%WtK6~l8rErp|x4<)nxRtZIzk3I)u(F?iKQ_l~rj4#+&j0hX&c+|*40tzJ zXiMI0M`y113)c67{zY#Sj0?(qyh(ta+uYz;)ozWYpJq&)R8iMAdyhpy?Xiv|k*{%k}zBP1}LJ`?+4r_&e@1l4UkKqczoR z^}^N`UjI%D#cLKJk-7Ig{-xzopr03AX_G5`Po literal 0 HcmV?d00001 diff --git a/assets/images/acg/noval_time_forword.png b/assets/images/acg/noval_time_forword.png new file mode 100644 index 0000000000000000000000000000000000000000..ffff16c24cd317865d440d1960e576cd0605a488 GIT binary patch literal 673 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~aez;VE0F$ABG^9lQ4>&`KuM5a zFvER?{S6Ka-X{nY7_6UgKH&WkSt$kvCSFe$$B>A_Z>PWAv{-?st(D=S_u0SkZ&xi% zm@Dkd|L4}-@?~=!UtU&GQ}p})^}6<=OU@2m89bdC0-bIXGekgyfM+nfX2$PpyDsQ^ znx(n+JXw-5;oX$hV+^(&E`eu5u0D=h zWAlVEWiIS>{r%~5$#%hDi@m*P?-#3y6gBO7?i1HIZ%NM^+{a;m zN@0iF39IMVE@uTAcz6A~^vz_BE>FNIW!om5+)tv}FD6*pbKJ3yn&R>+ra0@j(cAcE z1yjEKVL9c}vi!ti&UH<9+H@Uwnj#Kg@wokuZPhe|i&`g?z85ZSbmEn_`CZmBjd7{* ziPDBQ{RJFoZPY5KR=~zC}pmFreRdM#bCkRnBv+$2Y48abGdjpPUTTOJTmhiSiwtpCGrY7IXSJm6PF>&@8Mm_HT7yW;J z+P+oV^ylNI$PR%^?$--zoSWABaaCW=Ui;_8E!)>-nJYgg`F08UtzrA97AR!Vw#QL- z_ta^=$?px5!3-qBtIiw*QZ@lTA+A9BKe^zBXNE9Pzi>&A zUogY|`|BM98Wzl-aK0cR-asM$y?^sxt8@kiMgdP3$B>MBZ*O`}YBu0;4YaRka5kTi z@xS`5=W-55(Vxao=5FkYzA^8lprGJg|1*pnzoU$H`7;#djqQD5fpb?!;ml3l)f(@xEr&Kx5!3-qBtIiw*QZ@lTA+A9BKe^zBXNE9Pzi>&A zUogY{{R#pN4hioI4C?a(=AYlNV1in#gD3+7la{B8V@Srmx3k{ny;k69%Qa|O^7Gr@ z`>!vSs72n|C3J6Br0LV2u`fG&dpR{@V`5@z6fRq=Kl6)GKT&@3h3pA=X@B|7$CiZm zI@YW0;3~c#Rcv=SRp+jxUi183{dKnv{kih1*Y$RS&0L`^qBeUPMH|^Qy1OoIKb_ZA zd^lwX-}Hx8F>S94X70}m;$N1$DdI|V(VDZ7wGk{4CUf@cb-!|F`_ysx#Ji5kT9qkZ zm)Ph{@vZgD=={N69-M#4nDyji10U(#EY~wzeeLQ`@ZCyK`!5}5Pvq)OP?#O4C4BX|*uM>Hr+M!2y5+U& z|5v^lao6rjT23`k+ikz{N$PH+k`mnqMko}$+D{kA>KRwLzP$gBdrRy(Gr^XCU)MfxO>ni?V|uAL#ctjapYxY> zJGj;Jfw=79#H=k&CBk|3e-57t+2<>_*Z)FEn6<{b@R)fmpy{-@0dNYWw;@m=AY1N&Y%!y{O`cZwHyzNWV$WH z57#fas(3m}p>Oi^tc3;(Bi1xo_{E8CDVAZKW!cBn%07c3S5S9BEu)l!9i!3_|8>sF zl8lc+m7d7nawsv$a8W*C+LUI}bS>;cN4&M}iwn!Ia8h*DAfuu4wJ6@H%ZqL*bHD%Vw{!C2 z&+@MA7V!#my8Pm194=U&kgpXbaV5++`tXIzopr E0P}>ZHvj+t literal 0 HcmV?d00001 diff --git a/assets/images/acg/text_moon.png b/assets/images/acg/text_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..d2cfdd9309d5897f0d0143384d98a93374a6a6a6 GIT binary patch literal 811 zcmV+`1JwM9P)1fjbCdPHd?6?*s|n*t2OOYU+Xf&qR2Az8aJ=jY7M&W?x* z3JMC2Nr^1e>2&^;%jLy>zmM_f7_Yx(v)PCYQ6bB;TCE0N-r(|ay)2bV?;4H9pxJDm z)a&)%>-Bm;8kEUC(ZdfQ!o$_CR;z#Dp|76j`IHmu0ydk?IBh%;a@Fm22b2Tr0u}&9 z#u?K*TtkQ$k|Hibh<3Z(SaRwBB7TwrP6UAe3UfnV7qXLG2~dL9h3uMeA^-+GCv}Fs zUN6X+0V@X@E|0lXCWB0; z(?@MJ9ztwEsm+)Gi^emtXuybocDwxv#$Q=7p1BqcXl0?K zOyF!2l0~o&2)h`$1h_K%z;?ThL}Qr(IJRrG8rLELj)t%3u0@ioJkCjkV|6SV6beYr zrXjzF3vWL|jFW3{?~AnYv_*VQ$0y3_;t4x*%jGgg2Q9!;KcORmp3-Sn=PoskV)1-T zdYZ@JJT3MQqzd4s6hyp*2(A95D)9ORYM(Dsb7dwW69vgGYd!nm9+#8Jq%S*PYA1#x z#_+_c?Qr5JuVOqS-CoCH`ipk>ciiMz+5saE9HRK2pJl`5K1#n2a7I|IRwHC5u1x|H zAqC^V!MpG0^Z5_Tf!5BHoO!wH5UPAHNeY+v?g&pkA`Ogetw|{z;_Yj$NMWFV-#gr1 pPde1sY&8F7z!{DT3JMCIJU8ls+=w6Ayy*Y{002ovPDHLkV1i+DTIB!$ literal 0 HcmV?d00001 diff --git a/assets/images/acg/text_next.png b/assets/images/acg/text_next.png new file mode 100644 index 0000000000000000000000000000000000000000..a16d3362a970627842ab18e298191f0d301b0dd8 GIT binary patch literal 434 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~eSlAhE0BI*q43aD9)j-MC_FL+ zB6%RXZ=&$f3@EN}AB5z93LcrtJqF5}fu!zRfpjHUy>12?AyN|L7tEmWzTv(=!v6UV z_X7+jobS(HUtpm6T1bU~fsxPC#W5t}@Y`!AvyK=Dv^)&jo)Y;@C-T?d`KB#0v(9LI zyu@03vEFR{TBc?LsouDNi&|=>T~_%?_D}a-G5J0(=@$ N44$rjF6*2UngF6d`LHw08qfm)5S5w;&iga#RG;Syek}3LoTW^EfiYxLdY?IQ@MmQvF#zN%)~!$ z7z7XTXft*Qq(x{pxEdVRy3mj)(R8uIRhxmqA;7k9->a_$AR9eh{an^LB{Ts5^MEW@ literal 0 HcmV?d00001 diff --git a/assets/images/acg/text_play_sort.png b/assets/images/acg/text_play_sort.png new file mode 100644 index 0000000000000000000000000000000000000000..bae3100e8891ebe453fb7da375dc28e3d2fb280e GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKx3?xrnI^qbV6a#!hT!Hj|yx_dz>z_bHTqQw% z!3+)W&+k_#a0s~nqW)tdP;{TCi(`nz>9>~^xep}>9QznOt8lYMeo*tl6tkJW%zqgU zXBf>sQ^)1n{atga^zGt;JKYZ=B&r|P{Ygxg;r*R`ywXSbS#b+qEdh*TYs*j7EIo0| dTk6_5JJmObpLfi=>;d!%gQu&X%Q~loCIBVfmRJA) literal 0 HcmV?d00001 diff --git a/assets/images/acg/text_sun.png b/assets/images/acg/text_sun.png new file mode 100644 index 0000000000000000000000000000000000000000..8d835ec9f2e86db533254aa1114f59785e898c02 GIT binary patch literal 601 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~Q-Dv1E0F$A8Q_S&)CM#~v?Rzc znBl#{{s|5D`wbKX<}X-ZP=7ulU_<_lp64eS7#KHtx;TbJ9DY0FbzifBK&xb)R$N%_ z`QQ6rT{A26;wts%yqWXs@7#hEJqzYnGvpV!g)TVA8}8Uq(5%Jr@Z5fpRbtBR(qW+o zghbQqb)rNstc$%>z#0&6$hJW<^~HvZr%TP84sB|hy2L6XNbhl@#s*uOP0&#erS4G_BvzE8VhNa z%qFcxpHDp4z37#C%E!0qRyh8njf_c;_=9Z~#Zwnv z+4}rxS@iDn(sE8|TLty&Gb2p<*0u_iUlZh1=_{4JU$ykrk8ATjub!IUX?;6AaMN-V zk7bLQ=f2yb*fjM|!#6CvBJi?|OWN6u&=H-=I*v|HS=j104KlIdxG2H#jfqD(%c~%VvxeEJn&WJfg2mRaY+d&p z6y`mb|5B~Xt9wiP#Ocp%pC9gf?(D5_r+ry_{`2qO@7ygG;##|6>!yN@Dv>?gTD<2> zI9d_CgU4H)>9X!LInMAUnVwpyEUQzU0Yo|lNj+5a;bUUW?;-4-d@aG!0PT;t*++q!z> zn;-XFV=G!Bx7$9nLF9bWyI!#t$5#`z+zVFn7YncB`|o+hWA99(nRg{S;+UI1x38Y< zeEj3rxBIr=QP`TkrZb(VL8SlC=eJ7@pWpsiuCpQcx%{$kBHTVkOI{x?`*Yynu`|wz zk#U@xwwAu&5#!h2+xE%i#gc!jx)*}wgk_y8TT81>RWDm8BjO-=$M^w;S;N(C$2cROQ6-AA8xB l?f3XqWPV~^i__|t;vW?Dd;XR?Xa|gN22WQ%mvv4FO#o|$C`14N literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_changeface_right.webp b/assets/images/ai/ai_changeface_right.webp new file mode 100644 index 0000000000000000000000000000000000000000..058223e9bf47f2c1469d7c5d7a9bb8c5a948b059 GIT binary patch literal 6218 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 literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_changeface_wrong_1.webp b/assets/images/ai/ai_changeface_wrong_1.webp new file mode 100644 index 0000000000000000000000000000000000000000..b30b90a331d94317b680a0111a166840065269c9 GIT binary patch literal 15334 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 E0AeN9SO5S3 literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_changeface_wrong_2.webp b/assets/images/ai/ai_changeface_wrong_2.webp new file mode 100644 index 0000000000000000000000000000000000000000..6f96bbae95d4013c5736d77d1c935a71d67f2967 GIT binary patch literal 7720 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 ihX4HVO8JPTjKmP!V^{w%- literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_coupon_bg.webp b/assets/images/ai/ai_coupon_bg.webp new file mode 100644 index 0000000000000000000000000000000000000000..ed6bb29e411a3d51fc9ec234c2070d0d36ce64c3 GIT binary patch literal 11862 zcmV-cE~(K{Nk&FaE&u>mMM6+kP&il$0000G000041OUqb06|PpNPZ�A5YZQC$u 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 Q*WdoEu8qK`GE%vK0LtvcN&o-= literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_draw.png b/assets/images/ai/ai_draw.png new file mode 100644 index 0000000000000000000000000000000000000000..3eb4d068c81a68f22c64fb9be721b0a68fefd570 GIT binary patch literal 683 zcmV;c0#yBpP)1&_2puFH#Oc5tkW`R#fK!3f0d(MW@Un{66X6K-!(uxbzL^~u zkc6IA>`Fj1Gcz-Di7ZGF00keGY*%v3#0>9jt=KEuX0cej(Ivng&)DwSc8I58E9snY z2W}HPA0>wGnVK9vmSeJI4X$^r~&-stH} z3O)f_j{rkODL@Zfz&*YTu%}`M_Y9~h7Q}_tA*x6LecnH-h(bZYpol-D!<(3kR*r>r z?TL4F|JlrLk3@{=L<8%EggYdca_>$?`tj&FJ`WryC>CklN9m|{oULmb z9;grj4W$DA$~AY?ae1SHf5$dNNvUw#s)*19U=6+Oe&87z9|c6n8mFbR$_>H$1rYn`k{%ttV{(H%!mV60dEkX} z$A+dr(Hk!`0ihXtMPHeB41ejIK<}GV`YugL$Th~ZrE>#?TtJrq#(fRcjC7wQEyXzu zwrv0S^nv2p(4`?>SqFN?l*vh%DSmeuGrzk`Ld)gYvOTe_4;!JGnVFg2&j+p-RKx-* R-Om63002ovPDHLkV1jX&DCPhF literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_draw_narrow.png b/assets/images/ai/ai_draw_narrow.png new file mode 100644 index 0000000000000000000000000000000000000000..b75dc853020db4929df97093822f0def434f6a6c GIT binary patch literal 456 zcmV;(0XP1MP)Wm%%UTHhtgk@MD6n!#Iu)bzL89_HYtM;cys+_i35}>Gsk0 z{aak?sQX-i11S7-BFqV}6E9$awevJh6MWliO75zvVnD#!x#xK%cRL_*2^M5ow#9aD z4Q1C+6n(aB`(~)4w_Sv6kK!SroQLlQzjM?Tk=u^Kk3?Zkz(Z*&B01--G07ibiE!V| zt&^NnT&OUOo>80_vN;klnr`BVlj~n}qp2OWJkNQ^?%_OwIF7xhY1rw)Y~UmCjK(gg*fH-N@HUar}i=4+T_k-%h?(rkyeN#S;oY yPbo|BT$bgld_#M@iUT5n^I_Eg_?4BF#rXz_c$)3flC_rr0000*g5Dz{XzjPHJD)vpALO0BqDbsdwMX^^jVaO(?z#pKM zT|*q!6;FtE!?0`G^w>nn91Ooo3J@<9zabtspu@oRN(15O`yTrCfGfyd#Dj~s&G_|_ zzN?D!PCs7ve1k$gP!$xeX=vk}=$U7WiUa6yYKd|0%ODoPL@B86w%-hn^U_|SFQJQutt z^*(X^Me($`eguCE(p${e2gN-eIyVuJIXrj^eM%oSZ3=%3S;dA3Ddt=-=9ip%6rQJT zO4#Jh{}iK^rzaNkI`2a~8pzf%T+CLJ^khm3wHTN=8sgp=#TUq#H^l=KatZ#}Wcbly zse6H7h|Y>Oz0A^jM2Qp+{DLm88K*^WT71@k-smeR_Xxdr0eTPGah&6%=rdu~9@%3l z9+02{Om`Qt>GA`L$DxhGJ@FAamNVek76Q_x!87RUc8C-YNZ8~4IdsT;R=liu1X>aD z4{|IAoypqR%s3%fgp(bsdO(8Su;&5=Kc)B+I^3w}CDE4_UnrT6V>Zk$J;_ka?5V9P zr={@3Z^ciFKTO-c`*zLsyffau6ZDPH!RdG_iaA$!f*Io)v?3NZqqLm93<;hgU2`3E zmQ>vXMZ!~{H_)4OQuHNfoW8eE;oPXAfKT|oXv@mh%{KhRQl<1r4xtd?2?|$aU8ck; z2i-rUC=e>VMP3cV{;}^1x$i5nX*5=NBCqsd|Jcy!h;@f)IVO#!2yYj=s(8;_Gjvko zKK6ZO1QebYkYAlUsQ2@E)2z9S$Ph}kd@U$bV625Fd|9YY;dwf@k$|F2T({*L&{bef#!7 z7;q9OvS-g8AQz|#NPajgGZUyypd`pInBl&H!2E*!3GdfC7@R*pq5teH(^LiqMqW=B z#}JR>Z$obiH7oGwv^ndY{$HQVnVWt^_0fknc9xbWCOvreHG!}DVwP^I?>!y!LvwYl zRm?f~SIt?lF6c$9)eR4o`QFW@6SK+;zJ;dKS$gMsW-`rla zoBf#ew%>6t-LxAb4dsP)uF7LvC_SMgI%8Ju>E{(I!XviWR>`{d%^*}ks` zJmCAs;euu3l;cdi(zk898pK>4xUN)p3^dg~%PpCF{-IYm`uyIaMp6 sJ7o+1_IKA#ed-LKcBwSgcE%xgjx_PccX!gWfPu!~>FVdQ&MBb@03?jO{r~^~ literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_girl_friend_bg.webp b/assets/images/ai/ai_girl_friend_bg.webp new file mode 100644 index 0000000000000000000000000000000000000000..e9612bc88bbfc1c3a5e6fd912362248f9cbdc5e8 GIT binary patch literal 62978 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 f064$^#v3i<04GmC00000000000000000000Gq5(V literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_girl_friend_btn.webp b/assets/images/ai/ai_girl_friend_btn.webp new file mode 100644 index 0000000000000000000000000000000000000000..762ae3cba97a743dfee790acd37bdfa785d55467 GIT binary patch literal 25536 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 z(+#b0000000000000000000000000 R000000000000000004{eOAY`4 literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_girl_friend_top.webp b/assets/images/ai/ai_girl_friend_top.webp new file mode 100644 index 0000000000000000000000000000000000000000..fa72e66e45c7e8717442c2610901d8c883b4cd63 GIT binary patch literal 22018 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Π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

YJ?#4 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 Rx)lHb002ovPDHLkV1m%P6?gyu literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_mate_enter.png b/assets/images/ai/ai_mate_enter.png new file mode 100644 index 0000000000000000000000000000000000000000..536e604feb2484c9c730892d38b3ff9ecedebdab GIT binary patch literal 495 zcmVZ7=doU2y}yvKqu*S0@Mi@ff3RT=myRPbc5?zIHEF6fzqV+a@CL@d@6{>a0a-X#=-A66nVz|`SuCA#42O{XP%Z5Ny0 zhB&dV@K)R|@b!_|<~4En+B&8{%-_fGpBszQgtzW4+$zvvi*_3sC10OZM|r zQ(#j2Hr-Bk?l6B0cWbRMOv>;rz(o#*+1BH z4}3zOV5X)Wuvgze&n{FlUStO<|BK)b$iyFcd4nDwq42F_Cy3`q7=q_5K^VhKB?x0m zT!Ju#L?sAQnz#fZg^5ZKQX07gVGbjeAk1lC34#VaOAs{aSc0HYWC?<1EhPv`&`^S~ lBwh)^61ftDEX%To^$VT8d7&WZU_<}_002ovPDHLkV1l#F%j*CD literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_narrow.png b/assets/images/ai/ai_narrow.png new file mode 100644 index 0000000000000000000000000000000000000000..6866cf6f0af9c2e3ec7f656bbe31d9aa5bb2bc29 GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^AT|dF8<0HkD{mW+;w*8o|0J>k`Tm|Rjv*C{t!D!{85DS2FS;5u1@W+@pAS)}NE)Ra-NuMd#fe?rZEi>Rc1Lf1h4X#=)78&qol`;+0IfSXyZ`_I literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_novel_enter.png b/assets/images/ai/ai_novel_enter.png new file mode 100644 index 0000000000000000000000000000000000000000..e20336048b067808078c12c9b3a636ee1dbf4c9d GIT binary patch literal 536 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?393lL^>oo1K-6l5$8 za(7}_cTVOdki(Mh=UOTK@UNyIg9%KtMf zv=2nhzN~C7Wg!*X(V+gn@6&q&5ih&Vb#^E3os=t7GFhe&Js~&xlfD6qS3-`3bRYkT zncfc<`s^x7)3s|ZIF_R7Yw?Y>;{fkXk%u$ZHSl~ixY2DOSKoPAOtnLqaZ{<<3wHTq zq0Z+dtHku5AN}zsQg;S(`#KT-h$}_sF3LAgGmoxk-W$HEZTER@p5q6K%Qm$gkho*H zf4*S*?Pd1Ka!J$gR6b=(e-gu_*T&`1i;B+13tYGr_mOjl;_3Balv>?ZDg_-W0_cQ1p!G~L(EylUC?+p6q-q4kDIdPmEb zAKF%a>6OGuNvpQ!v0l72i)WiY`XaKU^wBA{|A||;jTp*T}4ds4YJi| URN@R=ff32z>FVdQ&MBb@030viF#rGn literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_pic_face.png b/assets/images/ai/ai_pic_face.png new file mode 100644 index 0000000000000000000000000000000000000000..1a129e139b278ea28a9fb3bc15403cfb74f3198e GIT binary patch literal 685 zcmeAS@N?(olHy`uVBq!ia0vp^Q6S913?xsr#TWu9tpJ}8S0MeLIMBBHls-^3Z%L3} zFhhgF{cvl6JrfGvpPwHpXS#}kfl1ZV#W5t}@Z0J8vxN&q4(#XLx%%i*)u7p`+QGU41$8LCXhAg4@k>O+D@Zcjs2w$GxAu zyWHLC*=E~!hwtuw|NZXX@21>(lcW8^9>-*>@7Qi{&@CUIcCz2&(O%vBNoV%%_|s-$ z7dxe!`I5x6-X(`LC(rGjU3lyMvhR_TH$}7WD?h8#<(r_iK2~tkS>A-`Bj*!u7L;2>&%&DOouhJ7@vyV{kvzv}TRgibKy2@P&PjO~^mLA^cI9>4y6A`RAvf3`#KHbffjbV$;Z+>-XpW_1X5) z;OX+T+o4NuFH`#GvnzMr-&L{ZyL2ON%z9h1-PH#+Ouo49uwu$kX@VNFCEz#wWX=3LHO~vi#hWB8*hd@**J$w zZgGJQms7gRKV2*Kq@YVLU0ZwnocXk+Hffe!u-IcTGwVd$JH9P0yRKcIduW#Fjb#R! z?yF|)yqiB;^V8p*eg?l@o>=y`!1Ua8-pvn$e)6AUThp2Jd1^i|B{6uq`njxgN@xNA D3xZMX literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_record_add.webp b/assets/images/ai/ai_record_add.webp new file mode 100644 index 0000000000000000000000000000000000000000..480fa36a37757fa5345fc7bcfe9a23afde3092fc GIT binary patch literal 226 zcmWIYbaT7Kz`zjh>J$(bU=hIuWD5YX3Il^O1B0VafJYIK$Cw~`J8z1IgOf@Kmypsz zz7Cc-4a@}%EnW_XQtwZ{-w|+o8?SP1=Ir8@J4+J7AN)wZ5cHJmuz0UUUFc8EEMdD0 zUAe;wsjLl|{3VsFDy9t@oH?CGR5@3C=6f-{!mN0WjQxv%j8MIg&y$3oEc*Vbw^AJF z76m7eTUZ$8GHR&;-DB5iGR1wVvXY{y2g3pZ&P)b|Dh1JVD(+0GM<;E#*}(Ac@64Y6 Xfvf+2nt0+Xe>{+i`JcD{U;_gHyHQhL literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_record_delete.png b/assets/images/ai/ai_record_delete.png new file mode 100644 index 0000000000000000000000000000000000000000..51f04e76efef9a85a64cf802dcd5ad7934d32629 GIT binary patch literal 427 zcmV;c0aX5pP)q-Mqt%Dfl=Pp8ng)X z*xGk>4W{stf~+PfeMt|*1${|D?DR$GR1kHaI9L%LuWC1e2hS@I2SwIg?<}_h*B_04 zi{Ik=SNheNxkIf)=B{bLx;mN!y(k3k5>ZJuDBaA;z?$2Wmtf0T4d4T1{;zLR!UYGF VY?L($#WDZ@002ovPDHLkV1g(hr$zt( literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_record_equals.png b/assets/images/ai/ai_record_equals.png new file mode 100644 index 0000000000000000000000000000000000000000..c04fba4e0b70ecb8eebddc4645e616f6fff68831 GIT binary patch literal 122 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|n3?%2|6>tM7o&cW^S0Fuo`t+G#kU8mwF_6z( z666=mkZ`~Ad4dg)E9L3p7$R{wd4chT{{O%1|Gnl{5eqZOdDF_sPWnw1{!(%`Na0j3qwh1R?#7V_jIra&Xh~OQ-owGqc zVL02e_s9U;0odpWssjU{Q~)-32IXD>pd=MQ?WLg2N6NEKND!>UQrRz+-GZpiJJ*|p zAqOSI!9olxU;&0pV?80Y3!V|j4y4YwZCD{4K+^Mz^mol00OYtdxF`?C?Gc+g7w~g* z69Dp|JI@Q4dzlwkuszI*^BV>KE&d&RZvPIix%3^<9FBDj7p1Lgas|@X9D=~~@+f~L z{9^%DP&gni0000m0|1=?Dlh;r06vjKol2#nqM@fK0I(7XX#fB}WrJ?*fB^Vc|NLn* I{{pxG0CZlf4FCWD literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_record_org.png b/assets/images/ai/ai_record_org.png new file mode 100644 index 0000000000000000000000000000000000000000..c8d959a2f792da841388580e3bc0242cd17d38e2 GIT binary patch literal 887 zcmV--1Bm>IP);(Vo0{!d+`|AVz>jVAj1O4j)`|Jk)>I3}j1N`d){OSYz>;n4j1N`g*{Oc4H z6bHh!5-~3Y-_QxAnhtAa5Kc-HA|V9s-VBI<4SI44mW>9{!w9sf3y6OYT~`r6JP|-W zq?VC90000HbW%=J0Dzw$-$1|L&u|d$kYG?SV2}A55dZ)JsYygZR9M5k*6Wg@Fc1Y` z7iS!u>-Nb#0mPa2f5#1K2ui~4Vv+MhiB{#a=|sLb{D6;__94x8tz6$eDVd`j@&gkW$2jOd8jwDW1Pf_^ z#=nWQR;4wLba<+>niTHhQd;&z)@q1>iHrIn?UiUj-@+Vm;QUT4yGCQ~Og=Brh6n(O z4&LwX}`f) zXp*y&hH9XOX-B2W#hP_~GaHT#xVZb*I4OX1KdQI@~GyIWU zsMP?B)|Z*|(Q%JhNUsqKtyBJ!h1&5v_di;n%++dmC~hd^tWmAP^&pmN6#In}>yH%7 zi1YI^+ylh<#h{~yG^PR5g;^YP4v;|$wR(QJ#%co-+a+4Y2hJL38f(^nCzfi`UE1ZG zQ|hGoa04XSogAi}bu`k1$2wZ93FjK=b7ZWof$=1srcVm~ zQ`EFji#c8gqTjI=muA|DCJdnHuiDGJ#=fBjjRRVypMtCjmCb6^oOZqqcR{nVUvin! zsOjh*w9l+&n|^aGC}=)H1#i(4-peMf`8vYC)v)E*%V~l!cGNko%4yMRlBbIs0-t)kCGWCl(!j^aY-wJbO{;ul{t9t!^4OOd|83Ap!zG`@JcCd_q8de?VdeRMtiHgRYH5(SBF5w`~m)ef2oHD__Y84 N002ovPDHLkV1iNfuX+Fg literal 0 HcmV?d00001 diff --git a/assets/images/ai/ai_video_face.png b/assets/images/ai/ai_video_face.png new file mode 100644 index 0000000000000000000000000000000000000000..8490b74fda001a879eb03893a02f7dce73bd6328 GIT binary patch literal 567 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw3=&b&bO2J80X`wFK>9yffXm7>AE;TVB*-tA zLE(Ob!25*#=N;xxC|Dn05a0jD>US&y1LJH@7sn8f<9EYummOB%k^Ua-5%uqX^7WEz zR?iKZ`LpjeELK)de!RZEEw}U1l%$%tZRcl1{S0!9GCUW1rkm}TLa4*mW&EA%Czxu6 zbx(bMkLyE$mdJx|oZ;`Ji{|KNt?sFNV&xY-#b_zpz~EheG1~>zA)>{e8V|eQ(B!JsI=*_O5%B$H;Zk zYFSiX|5^>BccSlZ-evwHp?rLK@Xzvvg*Vi1yG87u{N(GtCA`j;PyBvW$MI$Eh62xP zTjxLeFmEEaZ+TOJ&#tv*f3BNeTK)c@!V%pWvv@wd_PKXL`|uReYstzB7RMbvWRT0Z zb;E;SOST+deZbUMx97%}pA$KbTyQs98~19f^G7YoaTnqM5T*wQyq_af^L!4KyjOG_?{|k1|%Oc%$NbBI14-?iy0WiR6&^0Gf3qFP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBIYCbs$B+ufx6}3t9x~u*P3==sUXs!>Ws06; zbD%`4p@yxK+k%vql4q7b_$D8=-!!?|>HggFev3Txy_9Bp2(;t|Ug_8xmVD{o1&yUV zrW+R9JF`3$aI|y@;M=BfWiI1l?G3LD`i`WWH{|Pj!2DXf>w(C|;->Qwd!viHQVZK2 z{BU^Rb!T_OeIwDXkIT30FOPMsIAyISJZF|`V#}gwNixUoM~k)3(5VXNE6ZPmx+B6; XrJ1HiZ+hi`fyLnI>gTe~DWM4fm9LY1 literal 0 HcmV?d00001 diff --git a/assets/images/ai/make_the_same.png b/assets/images/ai/make_the_same.png new file mode 100644 index 0000000000000000000000000000000000000000..49edbcb1613a4d7efe5bba66584821614234f3ed GIT binary patch literal 349 zcmeAS@N?(olHy`uVBq!ia0vp^G9b*s3?yAI>n{URW&u7Su0Z-f>EL7Xu|Ghaf+azI z!3+xb1r|8G-`|j6Fd?A7VEuWsv^)!-6GdGb&DSuT;^DLN1st_$HS${o4z)zxxgg1Jm!MvHvT^SyeS#2 zDjI9UvdkSe^Lz^Z!)E@2ahjytj@Nb#xhXm)lGzSjad*p?$ZR#x-4d6(j7zGQd4cVP zgICwy_~-RcCaeE$vLO^HYUuCp2nQN_e&92e3|M%v)!@^#J2# ywVy7=QSX>TGk@D(+_F6T&0M*oJ8$m0Chz)&vnc4i%u=9-7(8A5T-G@yGywpBZmuN& literal 0 HcmV?d00001 diff --git a/assets/images/ai/paly_btn.png b/assets/images/ai/paly_btn.png new file mode 100644 index 0000000000000000000000000000000000000000..6fd8ce36af81519de4e35319779576f0cc02c49b GIT binary patch literal 718 zcmV;<0x|uGP)@f z0080P;VLRBI5;>fD=RB2D=I1~DJdy1Ffb}ADk>@}@$vD_&dw_^4Ffg~bx9REWxVX5htE(p`Cy$ShfPjE)ZEgSm|3y*M zQ2+n{B6LztQ-A>9a0p>|KsXRcAn(sGDEEkvILEJ$pja5d=fAi}sGpdZBK(^G00Htz zL_t(o!_AlpZsRZvMZZl+*3x*Hq)pOoW_tfa)-_NlO-z$=5}@w@4QoCOTF)r(ByumCkd5Q& zP=M<=kw+ohc~OI6nmLij3bvP`4y#D#qTsL!7k-KiVI^dKh$2=KPemQBAf1TBz~tZ{ z{6Xr!32cQ8bzlF724^M_cc9J3$mWmsNMJKme@wipjx>o{aH5HaM~G}Ek>y0+yomAH zFHwLX(xl3aqLvqB9^RzbP+3_dr^=2Z$RasDXOKiuX2+|J@PbGg-xg6N%Bp{uCBjM~ zU8)XxQRa2P1q6~rx>OM$h?w{wAc!(gzfH8mIjfT0Ki=f4_0Iz?usD>8>e=+z(h_;u zGbK`1wEY`jw8xTbWB^BkaiS<$4gH#kOWQU0_f8wuU>>u<7f)a=f`|>nC;DGIY|_CR{;xBC6i8ns~Dj z;x0qY`ZRSn8vOiSM$z?W^4z%Im5uA^ehY+*dB1X{k?C|xmZaDpHKNKjMzx3v4XHG| zDAV$*F>lJ2bz#QkvV{>bBp4@(CspuQ3lnV8F8}}l07*qoM6N<$f)pS@ AR{#J2 literal 0 HcmV?d00001 diff --git a/assets/images/app_icon/icon_1.webp b/assets/images/app_icon/icon_1.webp new file mode 100644 index 0000000000000000000000000000000000000000..f866bb9d680352d34834d635aeab2d811b8c2982 GIT binary patch literal 4448 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 literal 0 HcmV?d00001 diff --git a/assets/images/app_icon/icon_11.webp b/assets/images/app_icon/icon_11.webp new file mode 100644 index 0000000000000000000000000000000000000000..53e3c8ee8c773724bbc2f0eeaeedea24049a77b0 GIT binary patch literal 10692 zcmb7|Q+FH;1B5rmZq%r;)5f;b*xA^&(_mvZww=aF!^U=F+qP|d?_c;X=3?&V%*Au& z3|Lx1qMZf+&=40@Qdi>ELG{13h)&QAR?zEMGpr6KoP>(tuiC|t*ncqV_*s| zhB1W$|2SH&mVGz+&d7|8kk$=Fy|bk)3=Id7B0)*qdG`x$C=L%pio>$e$(GK|Yd#Xd zY27gb^^2tF#mC`eR~!gJga>aKoP*Flgzq`XsY!jSCt?wqiB#;5&_vhmeW(rC}i@Mk#tRSXbFUgg>e)^Yqh_PH1 z#FFM8Q|Uml?p4osnteBFPwDV|rMaY$ytHI+kH$`lVTDg6L(>nI`zCv?Pclb}?iD2g zHTyos#~rsBmZTV?J6C9;AID#p4H%|aScOPqj?lPXRK)I(INcKw;A;?8M$8|ki#Znx zT|oP4Au}v9er7;oEWWi&$uVl1pH9?de`FG&GDub`x^u2eRHj%O9pru6PYO)tp2}hT zHlSWtF(AoK@1uUzQxKCG1fh^1`vA9p!0c;Rgm>t39LpxiG0x3fhYo0R^FGnU4}s#Q zn)`|bnr>6uHr4xO##ZO%Ehj@kZ5Ta6QBC?souRB+OGqQKHfa{LU5e;7z-5&($+<&h z%DNF@g<;A*-~rfo2b_~a{<@<5wN(nJ)+J1$NKEV!~ukpNaBy=`~H_%4xXB)u)#GckuBz%;oslQRsGVI1b$CcL<*6fWbxje`K6_8EL?CmPtm`?!a816haeQPj1WVRY13Yh6 zPO72aA}2t()GXz=2%Znq+*j45qN1nP_}|@mSfDNRJIj->9#%C?t>3wipqlkPtwI(N+b%EcNrj%-P+_ zpeWGKfWA(L%jc|$drXI`5w{iT?trNCG28gv6nR7$LZs<2-4xZ9|A@C^t_F9|;$QZ+ zFR3e|Zb%2@V?>lElDFu%Al4Dfhv2GkrCqP_vC%}b=*1n=}84zIki^%2(^3zi*F4Fv}vs*nxrAF=~=4T z&e;FK!Ht}Y1-^@MmU{?>vPypHP+Qhd2;X=BUG(adS8SGXdF9wD?eXP=e{g8B(l&z7;X6X2sQDBAEh4 z^DX?O7mVHoa#tNX*X!OwA3s+N)_nG!ey-@->Mek@i9&FAzART!deAJpbaJi?s4+ zTy2SF6@yT>VffRi#3_bM@eZp6^!79o-Y5YOY5z_R;og*v_Y|f^$ zWoOwqp<|FsnScKQ#=pUB6Bweiq;IN})D4iY5m6u5wEOMSOM7FEH+7%_IRa8*3P0 zY|Uj4^AUON0#pbTJaLJTq7L_{e+^t=vLh6>W@IYd5xK)cY##HM*c$$qh~KJd8@3)$Z9I45!<3Vfj}AP_b)(cDhstQt@|b;INN`5@RN zBJ9cX8(U*T`ZT)zn)!FLVb;7l_j_+cT;**8yWx-Cmycns>>#8wwrTH3N{?Q;$807W znWgN)r%kHgjSA`fD@g83sqr4HEFjuhG*T^meDKj;p5`YTX@m;~wCWqr^QfuIdAJ4_ zoQe&@M4R?Ycp3RB8PwXB{f?;v8Na)F0T%Hp@V^HZw!J4U$x58;>_hFFATgrnlxv^U zmA59zRZG|Unguf{n=&_42@X?4BPgaQp$oc9esg;bXkn{*6LT20-#kMvii7Ei{$jKqWcZ6h? zFTWq6Wz`~-!%&CDImQs0W1vRsF0Yb-h;ljXF<+S;e}?8T+U8fW;V9DSeed{DQ2?LT zd)SpY%?3ONjhMpysz()Z{QRQ*uw;XqJty;{++F!?3__4e1h}DQqd3bhk~l|Jt^Sx# z){l^M@|mrfCsF4xg7iV)Wak;&7&Ffx^pd3h3!poe^@|i6RVDX*!A=>Uo}4x3nD|_B z;K=fo^L}%;fC}0h2|1p?>Xv+VN;?SU%*tDR@?J)E_bxOhg8v14?xDJ+m^Ym3G@h@R zD032jfxE}mXgS0iqzA=&$UgYFb8VfP;=ls{e7wG^SqR}r$HbB}2Pi_L5J;_$i>Z72 zv6ybyKGpT)N;wMo&Nj(Kc>2CdWfx<62l*e)8peM6ep-MCJ5C|vRQno?b$cQ#dRk}b zzHyz;auwT+(?H`oEK9oZOPSzHlv3N?L)a91s{;iS@1`=Jr_JyRcG@Q8q2XL4wbWsT z<_}*hhC$EdLmN_)l3CSA4vk#Mm zwUj%2lCr?!1B$HYjdLm+JQ_*HMSk9XmvY?4uY|SY`e}~V1V2@};a~w(U%tF!p2=P8 zME4@|vjSco-q%A(rU}7k0hrSg`0d+f>Amih^*0zw#kg==n>hUWFzchYluFb~FaXKl zt3S)YH(fnjDF?1(3HTli&E6U}5msyVjPVr&wJ}-HtLamBHE)Y(xt#XT*vaC*epKKK z8ZgH4#TfNmU1!b)9nCc})dM4VLG9iVXEQBnRJa$7Z9zY`{?_B)@)tMx!VrM zC;IH8*l>!5*7fSB52v{s#&YBbJ>UCCX2CETv{2qC*KuJ+8HRolcI{FXN#OHmM44W_ z*z~}50LU>Es1@iFAo3OJy6;2Kl9z&molKOM>0JxDb5TFlAiClRpiaXlMZ}x>rcihB*b>wtwCecS*Lg1f*hv-x3B!=SX{RP6i zKHc@bE7zPFHZW|Qh+YPgjjpy0t~=bD?ww4}YFXA*h@Xb;O-$p~`;sDs$k{+ho>8Sy zFLP!{9NpQL0HuV z@z~`_x$pOkRR%v*P`WB1f4dh=|0#h~XiJZ;=kH&4!kkZ(znjV7+t<^nGR-6Ss)sp}K9sGk3=tuWriG;Yw z-WK(occSa>#sw+ik|eo=apVOlI+xA333N^r3K*~e)4rD!Ejgdz;i0UAK)E70fkwB! zd>Ak+3+lu9>g(d~kAC!Mrn(z!)zu3US91Z@u0a<|2t`XQdGhzFN?LcfIxc)VlFs*| zk}gF~@R*AFb4)>2{a9sWnIDr{0em@sxCI$tlRbC4RVEm%g>li+lAndPIt2nmRrZS$ zEoEghgua9F86+CoFxKb*a@4wn)zl`=94d>7zQ~Hz-X3Hz9`iW_Fe%zq+{(08@%q{d zo6?sf?sl9dl3-Tf2=SgF5t=#J3qsV<5?(C3hs6Y`t;DeEn>Ev;)_q5*h0epx0q4Zv zOBl5-3e!rilO>y=YvCjuz^0#`XoypF&lW(@GA%jv?5N4j!3I9Ca=7vJ?jY9tk8kp3 z-&uTcuDr!yH{ogQ0rZQK3hE?RtDa(rUfq;?kS<1>TqGI-KFWr%?XJ zKEBUT2x=-%^3LpWEQ3T|BQ?uEG}bTLj-%gQ!zh1@D|yJw{xBgjfIV+q!iQ$~zQVaS zR4Ppj3t(;jn6J=p_59IDkQHV&$Qe`CvmZ<>^L(d~$wH}Dsn!Y(+)d{W72DJrB|xkC zS#nQwFLTc^sk;!;}*)bU_Vg59Dv8z@=lTex-RjbWqo6gYfwJDqjQ&>p#l^nxm-+F*T8{hH0gzxH#Z5^P_x2_^MY6FLNAh|U?qLyH?rt^<`n{&D3J zZGV=PR&227GR`vc=fA4IwOT$MqN3tp!mCZFVRFEjdrYr@NmiAC8fsB9opx<~FiB;T zoET8#{BD#M{oYQZL zo%_E8MAwG=NG5{T6LmIol^1KioSGEc#fkO1T*ctFLEabAhdQTW4qO`-jw*KZB4(EW zG@@Y^;s%gfrdcV6Z4?>D_ok#Q_y)Y2HaGqC=fP*@+Ob>1Q)=&!B50cBF%>@S%tT5j zboQuOX7gq=G+;=bRUCj;VV645%jR}eijFyy6nOL$@5`B?$Rvrip8*9#Vdxbp4crg_+e`fN67XLo5` zx%u1HM$rSJENx*E9QO9Km~f&(jC4Q37E-0MdY`C>NYypXvV_XrWOK$;Rm0t!nGUn9 zOd_B*mV=eCgAflW=iPLFN?3~LQ<_&>q1D&KynQ=m@6Q2}h3I57EvE6LcUN0ovV!J< zPT|@7*vjDstv&qf-XUyrFRKX)NF{73b=3&vk!qtYz6(MvSbQHjGgoDkKMB7hhBI}Y z&<^8W)p)L=*DTyog#061j#QaoRf%1qUhb~e+h}>n`->a?_@?vWYuxC0d=Kl>I-PD2 z|KAyipVdvg|MUdAsFmLjmX@pq@@9Ef9vp6n4D2Hy@G#L1cDqPjnxsC(l_{vZ*$+;SKE5 zhn~=bkb~UBaB+!_RzVkDm6$~@OK<%Ir29}@SeO(7V}>1!j7m#?WpQbZSh=u1jq3H2 z8)cRyhMNMdf#S^)w_=Cx4zr zNt}+oPmc_@7qshNrUlo`(8px+uJ`cAKV+@%j9U*7i1=h7xXbsU{6?r3~9_JRJY5!mfZ(ZqqS0l-Re&P0n)P@RKCBxKiN_ZEP}_69E}fnw>h~6A0Jr zh=!}C&3gAc4~=KL8A=N7_8=e#{0o^8fE*|lAN13-nMlr7Ya^-YTzl%Cz@sqy(h;CEt3n0)eKNDRi+)?x|qp3kCN7=+*K z>S*WeE4B&5aMKadQEzz<473E*FiVfV+Q4hlj;-L5^?WOqU3Oqf?5uz`Nu`ra{NGal`6W_Pq1J})XKEfd!eN6%WxW85{Se2U2L;1DeMk@}S)5>v-b4BeFY* zhwEH-PUz-zR^c3}`#@EbVk{lV#=l?RbO# zp|^&#+fz=`>fgqF<~WsUSI1BD_Jvg z@qk#cU64|2hqB9zazQHiW_H`WsN@h`g#?n#u+uA=7=DF*!jVC8=DjEIiit(>{>p*} zV!Hs1BTp{9o@?Z{V`qhug+OEhzDVXRv;Z5hm6*CeZ;}wXOoD&j`wu}a*Z5flKeRf2 z8mQ@7bM7Ys5j>^Xbth^X5L9PL2m%e+6@Wn zx!`5*)E(Nf*Y)6Ep?2btJLznd@Ll|_am2nPn-}ivI|azfPg(0xhThGB)?HQK-DRZ3 zYP#qdJp3<`WssMUfPleVMy6)v!%-O*mHlj|8hePKPm?S>%46|3tWwJyl2I8CuT+M$ zcC-20EF)?&s^1V#J@G`shebtUt5!?q28h?DqDu50q?!$Hg4Kb<|EH)6|q8N zgFI%zH|}!w5`Ia%9LSscZ+n~7-ej0AjK~9t>)p$Fkhx=+WBq)i*w`EV)cN)zJPwM- zXNl91*AqC2t<{W#;KZg-*zxzSm9gqSr`BH*M8R6KzfJM+^L>kO!KIW5=fC~!)gQm* z^i`Du@*Q&xF0|%8=qQr<64;0yF`Z9%anvz$iCSYza~FAB-?-F=@A*y6aXH$NN9!~Y zo2LtVS8d+j4ORfb1djB*t@tU|@)tvimg-7>@3V?o+I&fSLIG}lTqX(}Cv@$*r&QZk zj@1w?zl9~EhgT9|DeV|&KJ+u{Q;$w({WE9{gtEVEG?e^J${uw9J4GFT4lbO zz3frqzvl!^dORul`Qg7xvTza$;VQOjE?JkEJf z_KjMDy-WF$u-q836#;{W7n0|Mof~02Y3_{AH831pvTMKa;tM?moAy^OdT+aoP!m3O zd;sPB_r;Ghf~62;tH>2*1^`;+Y*Ft@B~K|s2~^)}{1aC`oLigIOU$SCGTEAnr@v!8 zqgT?lTdGgNRxp9c5jPU)`ZPCN`Jv`hw|m5#L&d0oKzQEffs~n7M!w02kdH6Y$pKJ? zLrt*zfS^@{N=nqg3lnteIXtDq+(Xckij%aYUGyNXF)4lw)QUTKa6;i&yTshX!j9A`T z0_<{?b`3P1Ppj!$PZRkGO9#vpjOL2FO%njfP|b<;ORO>x!lG(2%z6{kXrK??5;rBg zIRGP63!orxzL<#H(bxERRTiOh)m4Buehm4xIhuV**ZKbSR-M`ii0S@Wv5^9soVat3 z#ry7b%*$uWnIebJ>yKw)?HfAbiHg2z-N60rOw>fn1CwWnD-KS@w(R4t4laRPBV*H- zC@H;~lb?9uZZMe%w5W9WYm)e=ltgSyD&ds7Vc87I{`xUV+m)*oo)KC4@>Hu$0jqaU zk~mtZE(ea}XJ&&tL;SJ}@9vw><1eb}B7$OJ)5E`5JPzwcC8?L%q^{Hd_;~|}I?1E# z#}#xqu4m!R_~x445abNX19jh?HWcNwu&kBlf(+dZDkOi}NoVChIl)a~Kb}cJ-;4Mr zI0(s9CfFIU7aP1)WP@%r@vWq^H7<4ONVGT%3;l0g(U(ItqAMP7+yy=0wJ?b9Hi=JjryGfE3 zzK3x{DBa8HJ1dx1ov@D_q2?ZN*ROwFzSwJ3Ago$4%pU`UopD*528(VG)-i5a!8h4R_o6 zU!<9jlw3aS0a(8TUgw{`qazYI;L}BV^dk+MLb+ICVtAC-{zUNa;t1Hhpi)?EU7vu0 zyLKFL%1-YNiS~fC)>{~v-BsI$K1g%$t(mSIDxxRJLr~ux>(g%+YM;HZ zLvO7|2o%yt&7*HB)nB>nUd4eMf3!O< z1FO$;Jy3qa(1_)w9bn$+ZdI*GTML46m(H0PkIVb9&YIpB6q4@iEDTb0Jq5!!?)86; z4BgM8O*!9@VJYypJ7khS1P}x;;Z3g;Pa@Wj*z%!oA;sBH9uYh)N;1_(T9f(Xcn#Q3 z+bmOJsQ@bWZR<~c4vKc5Psdh8xoGYx)t}{+^4-YWq7rI z2;OgfwQF{*$q~+*^$$PBL8*shK*QpTT&S^xaQ>Z;=j`wkgSD&L7@;WHHoiSw7VNQA zEysa>KO^58+(vSGy2~+z>fi4hl4LpcWPAZBS^N(i;iakc0t963v~~hy+^pkX8})CG z6vhUEL1bC462SFDZG%Qx^%NP$dtj9v?JcjEsp3v5L4F`S!_C`My`Ff6ey~=LAF0@x=FD+PnJ&3Xk;X zoNsGW4x&cS0aO{(UB<0s!hj$F3&gAJSBMs%;aasc32i&(FFWY%gZ+32ZUVbBTFNz< zRF|a55Y@@{-4n_oS%e3JoTrJ&S7IaJroNl)T$48pK)qtv?jGwCu7JG<6;=Tm;JRdU-&8xiyc=BJs1t*JG zx`++Wf13ff@YkJzMtWbBfvNa!=lNZ>JEE(B_!~95CEUn0cZ-F^{*+u6BSbWU9=dA( z5(ho0zio^sOSQ%*1Q;`U>D;-e<#>c7EUVqk*-q6Pf8qnRU)8+(sq1M-%Gb9Acdfdb zxXm~?3r#qb<#i+|l5`ra#wU6)a=%cC8mWlK_IZ2tbHnVez>cOmFc8NGoD#j(AoX(P z3n!Z$jg+%HvQf5z=-p}YtUj5@eCCW8--4-R+KkIeyi~1+*iHSW9#IC2Gur;OG_)1Y z44nj`?H^MX_Y|(XGn1b`W5eJ!Te`)IwHA@>{z4QPytYOGHJs`v&soN{eGV+JIBSiQ zKTgn2ZJ3_!8y&YJ2#`D$q&z;c?EtmDbzu35Agd9=I!M68mq5j>eH|{H7v5||KuO!? zh%*#7A*`H0t8q?b>BT7^WXvPV@Y0(r{O21&xh(8%qdE`~fgPEAM#b*U*5qA#&wlc4 z-0h(BtZ_^G+c0VMmx5H6vbYRM#CrLY{uxu+eryZzrC1Yob!%VfVO|%JONQ(TO0(PS z4N1cW95c?kS99DqB8y2k=fhT=KjgGo)k>P8qjv+940xDV*p)ZJ)(h%KBK;9Z{i_1w ziIDO4yU%C3(;)utU)UGhQrCJMqnH9m`fv2eSLk$!aJ%@gc(uBFvMbm9);^sTE)a}4 zkoh;%)CA+|NRRVmT%BR|WQp9UH|aGoyJahUq3%ZakcyxHxX;{f0rI`XTChD*26X5< zQ4Jvsr!$%rN2Tt4I0h}Q91AyqKWH(r^e=y=7Q_t>Id`=8*Jjn z-;G8Q5IRL8mEJyTR~f`m{JRZBazf=&ni=A55tB?Kit7{aymYIcxVOCsp+8?89sz1v z{f_1Wb6=~>kIw&Lcni=xQC$~4zv+6 zvrQ$z>YN%q=vuQpHrg+XKQsB7&*ja49^Ggy2QGLO|;O)fjk^q)o zi;qe40K5|4*9CxY!s11lPVa#B_`j-kUfi946DTVibTeg0rKdA|fNM#2LamQ&7=BUQTEBh!bqyG2HgR8Tj7XbX@Yc!}#i zcWI73oLoVqZI=$p>di|#V#WQ$7dKZrwkCL&ZfM@d^}JSri;Pr)o>WUTciFM|)D_gl zNcCp4BCv5yJ&J9+DIZrW`x27rPL^!pup@#ablrex2X;IFivrgH#KFuH=1)<$c7{t| zu6gq=!(p$AIl`aj5Q?y%|6vd;$*Oa`5aWNGb$xfS{KJoxt%v(M>s)5jdLqwtNwH(0 zBjl%l5Y<9c>gDXSp)?f@Ck0B#5rojmHFCRc5}@81GvHG8fyo$-OBqsA1+)dS?GguSagMbv z+EdE1j9}QBO_1lCO|V>H*c2zJ%9)2gj1t5op;dwj#2a`l&#Q|)6V5blY{P7uKAKyj z#u`_^Q9F7uSF5etWP;JPfQ;w*LC!P9(eh^1XLPQ7KEvxZS_UUuy;I(#vS#m9A9N=& mK;SL@`%8G>vNyF)0JJa&^CYorEd@Rm;2HiD&*Xyh8}L7yp>}cr literal 0 HcmV?d00001 diff --git a/assets/images/app_icon/icon_12.webp b/assets/images/app_icon/icon_12.webp new file mode 100644 index 0000000000000000000000000000000000000000..b9e65904fc23a06927a9dbf3311a67d08f772531 GIT binary patch literal 3260 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 literal 0 HcmV?d00001 diff --git a/assets/images/app_icon/icon_13.webp b/assets/images/app_icon/icon_13.webp new file mode 100644 index 0000000000000000000000000000000000000000..3aebe99e2b35f5284f2994637d10ccf68739735b GIT binary patch literal 3438 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 QzT5*^Es_+R<-~9R0RLstHvj+t literal 0 HcmV?d00001 diff --git a/assets/images/app_icon/icon_14.webp b/assets/images/app_icon/icon_14.webp new file mode 100644 index 0000000000000000000000000000000000000000..07d85609029a4229a15936ba0ad199f0640dffc3 GIT binary patch literal 8700 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 literal 0 HcmV?d00001 diff --git a/assets/images/app_icon/icon_2.webp b/assets/images/app_icon/icon_2.webp new file mode 100644 index 0000000000000000000000000000000000000000..1656a2969e794e8960822ff5818fd715bf2cd7a1 GIT binary patch literal 3194 zcmV-=42APjNk&F;3;+OEMM6+kP&il$0000G0000P0RR~R06|PpNTC7%00EFy+iu-J zf?x=OUC9ZMfgjFA(dI} z@MG&}HGWVgbl+2Plb_eiQavGCqVHAfcS6RGCR z!h6Bh99nn}!lju@HEmDS+^W2-a5dLnwiMNztGt8H6~ zR$=pf<2BTIR#&*1`_p1#{%^dL0>J7*7lUA0gbjl&EKxvUd4Xs!^_JY~h^(221ZK8tNNjaFkaC5BXKYgT5FHkn*e`B0p0^)F;s(86FZQ zhKD;QrU%7DC8A)MY(ylWFe1vpxC5gCqXENk!N8E;GR8kl5-{@VztexG|4#p%{yY75 z`tS7L>A&+-2^ixaCIXBI3Il-^3{8VR^;Y8`fZ}sgL-YI4q1Jsp$B61m@@Slo4=H)*Vw$JO#Mdnl`{3r>M3RFo7GP>Q19XH zrHQtfeUxaMgZf8>w#u`2?twO(UDFb6o7FFsY4fA{LzFBd3{Z-(#-FtR|*!7>21Hs#Yfn)IR`LP&go}2><|4 zJpi2nDjESA0Y05hpGz_;sVJyc7-+B)iDPczZw^KoQB1sF&L7GBs&xZ(2X?v&(1&qz z&HW?n3HpQ7C(H+(AEEE>UW=Vc9*7+-|GU{uo>%`r+wM5=ncE%GnWZZMy@cqMb4ZtE zw0$iB6_RoJStl6C;w~olDuXH`)wN>8j?)cHJJ&83Djf#{-8)P*Fzl#+YsDoxAvTrw zGvafm8Pg`$gL6nQCJM^U!JnO>HPyu zX^!*0YI*xHVkKTX!Yz$6dZc!kY87ttaZCiwZd)NWs0xoEHq$PM)pzOO8}>QN$S

J)p*vRmMDpsaq= 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 literal 0 HcmV?d00001 diff --git a/assets/images/app_icon/icon_5.webp b/assets/images/app_icon/icon_5.webp new file mode 100644 index 0000000000000000000000000000000000000000..52f21943437823250b6b0d69122fda6cc50dcdc7 GIT binary patch literal 13962 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 literal 0 HcmV?d00001 diff --git a/assets/images/app_icon/icon_6.webp b/assets/images/app_icon/icon_6.webp new file mode 100644 index 0000000000000000000000000000000000000000..ef40822bd5cee8705435ff7b77cf1331f31a5fa0 GIT binary patch literal 5244 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 Cx<@ns literal 0 HcmV?d00001 diff --git a/assets/images/app_icon/icon_7.webp b/assets/images/app_icon/icon_7.webp new file mode 100644 index 0000000000000000000000000000000000000000..799acb7741ce7b174d4f6c86d1a82dc90ceef39d GIT binary patch literal 11642 zcmV-=ErrrjNk&F;EdT&lMM6+kP&il$0000G0000P0RR{Q06|PpNJ9ny00E%fY+D@J zA|S%>x)N|Ej)Miu3c=gT;0#`$)?FUyPL2Qx@q?}h448lD;YtzVz-=2xlBZ85+jnQL zd_dhSt@SXBL}}QhwN?nFhjFsje%rJ*QTiP&<*)R6ZLaj^Nhzm4FZFyrt(`_pL-l5R zJQAs;e7X=MELu0cUb`s*hGSp4L68vZ)YW%`p6uR7wAQub20d;qiFWP7$39D^@#Hty zQFmAB#vTZ0_-!7#yAl!M&(wn!J9P6Hw7vNT5?uFD(9^fXl3tU(o$*j&sxaQ%de^A1 zi4vet29oLf<&`t%LneAO zX9z7#RW{mYFCLbIp~c7(`7zP(EisRCbFqzOJ-XvKLx)FCq(hA{0xiy5g+sd{jBz89 zuo%b%FeHqDNYX;xtO>|{Hd2shii;`B!&d8!P`Mk<1(zHd%PGpH6?xgwGR^GMOK3Cp zK)g6VkavX2?kS~P&F=VNLB8xr=)dS5%ktLlLI^3`$0MkJ#Sp}2EE`LPFF-zLauj&Wzo)G_*q zbj6s`Ud`8085H>r7?)P26Y0dXVzi_zKX9C6YGggcDaKR5p#Q;Jq=?hLFo){+4O`u3 zcK{HSL;!{M(U`0lT{iw0*E+N_Wp5q?#CJ@dv~g^Jd*;b77A zT$nn{N5VmIDQ-2>y`^umE`anV;;Qd=NANXMF`GOY6h8+Hf*)Q~XAb8fiq|Jb(R$2C zf07dOv&-t>@Le+zH&C{F5cS1IYqcn)krA?4?cs7Ax3#*}9R8w-aD+o~Ik-*5p~f^` zfy^v4JE3~HUEHuysHoj1u?GA?i^sFeeT$Q$>;#D?5LY8vpPQjh5>&@OaYu(02$>&x zkyVD2Nr)0OrIT|$o?}2vAmlz)wk|%UT!JxBRfsNgg@k|A>?OW`i(TLBNyaj}OD^59F><(%NS(rA6|`rP{(a?vu&JT(&sUfV0gU@H&&?xF(r z^~HxI7{b>Vd=(&o2FX+e27HVp04VV`1vWD<$vKV(S%o8$Sv5#Sx0K+hi}z6%qOOhq=pdpGI0 zQ8J%?eAobzN#13EWD<}}0-s9|-+cIsbI04nKq3(ujR&DN%Z=yf3sBko5dj<}lSDp1 z4UA8{&tfZq)I;FwQ`Rm*bC{obddYm7^WFm_le|ZGvV-pk%TJUG>Ei__ndIXIFPTKj zz|J4M6Khb@e~2V3N4O7yA&HB5V|?*{%|A0{6^7i_lc@@$ zIPdlIU|ED{RE9ws^(|++55-v(X%a!UJjzg35Yz5fLwJ24%zG}ACRZ3`@a@H{Dm0KE;@wf# zpvE@|coc|D%tqoC1?ZPW_7ohN+Qe3IQHsidue|6MsNu!6hc@Aj-}}XuRb_q*88_UBLYORDzNtrUwey zgaSlvF(6_8GwR!uPAH!v7A-WvjuQJ6qS7EZ2yaZrR$@0>k-uFtg zgEhpvP{9Poc%YyatYkl$O5XAv3KrOarW%MZ8Sq%gQOdw%y(6UyJvZ%gc$)g4aiIBq z@(iMi*TJ7EbStb*{RUh#Es53+t6CPnI=B9lHZFx5b3y)xa**DSCB-&ens>oc+Wg;D za}=rK7O~!iYo+bzZ}EY-RjhH!E1~JRs#(mpX!MDA2YQ~@rBYI&Cmj)drhgQ7v}y8t z9QZ)BM2_DLr_vZn>=Xe)WspGvwLY}hS$-u&12V34^E;D5#SyEC`oR=1uGAeB9hF z*KtjMA}b(fr6MHQtU&(a7$;mGA&QanMD!RDA6UNcHjDI<8j}&G{np43rx6~&I0p1v&|zW0@xvC| zsyzvlI$S<0pe!M{^%kY+nn|!}^9i&1j)|t-7vtw+U11=fPV!ACvay5j81@i$NBee+ z1BYaRY}W%S=iO7=9wG{Mak>T|>OI}%zKOP&S4?XlY-JF5e69!mj>`*0GZ)S01#%mbc@_mW2( zDHNoFRlI06Cw+U&%*+?^o#5r|v4gW5nYC&o(lT+>V6Zrd2v!jc!^vs6#GLw)S}@nj z7?R#<13A)(-`3p6Le|5SZL7`=*n?u3u{ZEq^)U52?vVDrNGJP;P7Ax)D4zRqe*=g1 z0s)Hf-nVShww-~c6Yt?2747=TuxU7YJf0oy9}$Al^MxRU>D+KP(u*}Hqoc|EHE&op z!RZQ80svGoC9q8A;PbA5EF=5-RDmAvIonZaQyzmC!37Jr>jYII(U4#>f4g&Q5ktBn zI4kZN@BN&nhn`ej>q}4TJYe-xe$B8TS7?KNP-EB9GG*?s77hIdO=DVd{)7}c5t5~1 z&qWivK}nBCU2D19<&^46=YVd6F6%*YW$cJRVXMOguo~6?uQa-F0XL!dF{?rJs4_ zU}ia*)|utu%vb5Z%iz96k0mpCag-25Hzz1z&OS|t-3l)ll_$ynMt`vhBLWnBWTR2-Oq0RH@~5c7@X4;wwx#pj(ddqA=kuhm*O zCLX{S?vv_RX(0$!{X6Dq|49c8mh`pXTYuAva$2PySR%`hnI)%?QcdVJlNDX-OJBLx z#|$&^=z>0CJ+#?zK(hfx_57!KHT1FzXHSydw~)IXj&RVMwtmw|@p7l&e8g9RxpSJ= z{6F>}rBH?RSeE$yb}r5GYS2<;AJ7@uA4_#HvT1&InNKTr_Ib5Ls~Y~}7#h9e(E>+q zyU`Y4gq$*@IwM*|_&vtCJkDo6?%9nbO7L}ft`Gt!l==rG%O!~%n1;N3-&*DOXH31C zf-2D!Ohpfp$k*qdJ8ha_G>mafq>yQNsg2kxSelsh78jxJ)-4*Wf9$WLck`V@=DFM2 zA*Ng#+;;9>vn}4@s<(fzaCN*d?qu0Zh*(68DFM!sdH6A`!ZcMs?}t2pKpCJ-7D3@P zw6!H1ZwD|v?D_=SCA^>B(j`@$Y`^n4xL@$ZL!87v_BBamZGtZxotTIUPy`QteVAyp zUwt}d44n*vHBg6TGNg}!sVnH; zinN4Z3YjFSk!9H7U%bd(PH4>TL3-viFL-_(b%t@;T zGiO0ee3<*zgWL_BuwTWnjSs_eTO}K6T_W<&#lXb*YO8+E>?(WTAK8E89Y$V5;S!tr z#%55`AVrAkA`xJ429}bYNBy#BV*I6AKd$<+k1kBQnFQeA4^S%NlwVFsg&gKIc7}zi zi}rE^#VXz|ZV`npjJf!has%%yX(@2HC!9DR-qt@&a32?{5mFm6H6lWZvlGWpc8Nrx zE1x!y#gz%F#_EbsY)jMbIVSpERoUZi?C8wMK0vl@6}?pNhn_(zU4zz4%|C~xP9!A2 z{+>P#qZsKx7?O&{nhAwSL<2wENF_w*G2AX~6d00Dvt!B^3c()%o?oWCEa<4_OY*9L#$$W7P!( z5*T(yj!`^SmY2NJ;-j^MKV&EeCzN7d@7qE%{689}aT=XB1#(}Gex;^?tT04{ zLr5ynvxp0Q9;9wPpPZA{ zv1cM*xtF$^1zxJz8K2~DS5GX)(98<$tvwMgIB;vXs_w;c0)sTzI$SOah7Muj`=hGz zL0Al@_yibtzeHrrKM!-8syaX9Y=^7mZXCsUi#1a=PdWlpTqXby$z36IVw1UIMufT> zPhEsMshJ|APUOrNB~|v?l~eI_kLNeHDdZ6+1N{`1%}{FTTrDZf^xkWqd1dz)^icN< zKG(b*JJa~{SZz;k<-}$9&P>l2=jRV|dF)oJcglyJgtZO>oAM_Najb)kku*1>BsSuo z4ThP+j+Ej+VUg(m)O_C((sZ-pOK{|oi82yR_iu~0CtGM({uCNjnxCxg_EfG|_$Y6J z{VL7jt&e5rY@-5p&O5rpQRQ0l9@u`I_2ibK3}D)q>D>E4>1T42&Cyma-OT~o|GSPF zEIgds#R~A5ZaPia3PARNBZU+tHS4@TUjb-_o;wsZ4jFiPVO7**_}0V#svh%u@35lv zqdWYQ`5O7!6t-6CgZ4=Mt{;6XH+>Q7Bw@#@iYDyqd1ero*)<3RlyY1aI*vb^*G*HQ z0OQ6|dX5sBAKp6@=*pApWBWn=w4dkpA@fOu1jke0{L%{sbt*OwIbf35Gs%+^%K)Ir z|I}Nz0lE!jsR$IlR7|{(Q@@?ibYc`=@oEaShye= zOke`?1cnDkf)25Der3dE;Y^p2OZ0Q?60%#%xIPjhOWv5AfBf>t^l-te=zq z%x!urCUF%9`f&ii{*L9zb`1|032W#Xo3{?#qzg0DbJ=~u##tqk6^aG8n7@4M%UmEt z{_bNY2J2(q;(Q^E)cYWb32oc1sU&R8;W3@1`CREDsSia)1#stuxivGSs(%Jy!-EH4 zClYojdkC%y_?uSVW&y|k{>^4mmS|{qH{GpG4`Vg*Y#Z_@Q&81t;ZE7Y-2i}5b+e$Q zOoU7sk~c_|`DP?z@7U5A6QKC7%+K)0`LC}+qvzjhTAhroW$2Pq`TlzkHIJ0PZQ#`l z1IV}gr6vYr?Az;0t&ti_)3s;YWQyl1N(RWuKI|q57V4&h`bN4xFqB_H;#;0rMXpKA z@bP++qM?mBANkP|oK@I-XQ5;oYX0CU-lF6ROHnF#}JUBy3&*Fcfk|FBqU z9*zYJ<*W{@RbkBUeeyf83n9YDM8*qFJQOcmCqo2Meku7Uxt7zx_SGiFnQPc=6%5za-esi%xC6IK!)`otyCW>dTZVS#q z53~6~o>e-O*$rZ}V~k@N_~* z4bKf9MHy%6&w(z7lW+wHQtrjXcP_)O4`6v0w=*2Icnup#4!3k*HZQf(En2#fFLvkW z_BCR!HPWZY!RELdsH1B<&XF+_^>e{=4T*!czul-Lazih_a0`9UPzAC`8SP4{vSdFY zu4!`(h#)u|#&O|ilax?lR@OJkyf*4|vixe2W>8YS%Vss}AJNRAE43>eDBY8J|Mx|L z(Ux|G5+3w6BOWq(){|JtxPBb!FnL$ce~?_y%hu{msVqjx1S|wBsj{BBs@9UgY5oMO z>=*x>3X_=j^;iy@2PErgj-Su5HdvxOCe$1onc^%)qGyna}d` zDbJ?ML|l$b{$GhMpd?hl9`*uuj}Rq?I%6+)A|nkOGy*c4eDM-=z)m!9Me-ArQ~9*T zHX8GVP?L5EVL{1l?V5GIuD7);OM5!WGMAwL#@3}v{pJNT`y1NkoO>tSEztIpNCuZp8VfP`Tj&>>R=jx=Ce za{zNrK565A=zEa}u5nn2G{I2`t|nir+nS6s8sA4DBsmXY`V0WT&ES+e(KjO)!3v(L zFpmILn3FoX+87_0!82ulyj5t9jK=*3O13kGSIs8^@1>D^Mj_WaVH3fNi0;QyY6Zm< z@Fy6Nd7sC&kTiB$u`d@-CU!Mk77juO|2j?%6o3)2z?HA65W+cnW*{Mqy`sigTiX@*%>TxUrItYjNotq;^o$9Ce0T`esgSw)xrUzD#`sZE48N}@91CKoIdx!rN%xu|I*sVIY`nq&-7`1ALL_b=TnJfJ z?6^1%$YhA@E!7d20V>(6JZ{%nTYKwFTZYOnkUH9RRKnQpf$gLsd;@20Aa(dS*bOW8 z#lev6|GEwI?2%3P2W|AZDvK`wtM}V=aLYZAkt+nTlth_uh}BAyX?pl@J-o{L=6@*v`tLE8ZseK!S;6El-f4pwGyGqO)u^Qb$K$Au#JL-&q7 zlqO69u2+rT{wSkrRq!Grjb?xrD&|@Fhg=K;-O8WGcVJ+$*S*N>d$1N(#Ca`}1Uwk- zs52&tge^h&q_P(RFCoV?Rnjd2x@Rx8U_~4UDaI+DA)6nI%}OUQc~#dJ0A%*9B`Pp zRcyGQ=%hYLPaZ0s|7?+Xm|_FiVNuE!D>p0PRBqYS_us`ag+yBDn`F$!pmZUbIZ@OK z^0+C|`=+~J#z@hBU?QV^j}ZV-ylQI89x{(+q~{HNTJP5; ze>{yG4h>lG4&>|Qyd-(Km)leq&G}^AW50BXoe?y+Vgwwqz4UyyBIih|@_II0)dJX5 zG>5wR{b<_y(+W2 zvNO$h^QKb;hTRX3T}H_1?^MS2(Z!fL9N>t<_TaDBY@=rMRIBxa)P$=CgO!K?5^jN# zZbo$Mu1FD4lWcse+5rGI3oQbh6iwOy0A^x|a8HeNL;E(cQ zwqC-)7u_&JVIkN4gFwgV_BkoVgT$#z=S4jv^<_Scg9^(S!=einE)Nz)hhXk;|;pu*vSug{$%x_%o`@UNQoZRqb!aubz=4`?0TaOt$1>b zF*6hzToLh?Xqhju!yXREF0@NbSc4c1uvOI9;ED4~^0I86vuNu#9g$~O?{Esq$4FL4YdRRs-$M)`yZ@`j znJh9l>FgVyvP9{O6L-V5ZT&^7{dum$=2FD;ZOIVJoGZc}U^Z3ziI9*cQfR zQupgeI2_#?1Ut=2|3m{p3+YgKmvQ;O#=pv$4=psUg86mJsWI0~uq*=m6db+bC=s}o zslPzzIGtstDKIz0ktlKwvh=oxY?d%Cr%Ky@5KnTo zLmv4wW4b0AuHbP}OD>%Rukqdg@OB0LKr4VVc3;E(j)j{^C%miy(b}KrUoW1CUh?*7 zefXOcYO);Jt=EPfNLf1LGU2Oewzb*CxqjY0lYkWZpTAAYiO0m^aCAN0ebx|gji7x9 zVOwx%i?fyxeGUpahfw{g)cqaRc$C+Ck3n~J{sA5LEeDA0JXE$kEdHLd%~;l!KFyaM zrcj{HOb&~8$K;_gOuez%uK{DTwIi2k3%3!A?u0ne@Li?ri?YsFa$Fst?3Z4FwB8To zgru}?ocTyBB)vwq4z`QwAy$ydCTZwMu(7(?CPR zHgH(+Y@8VImZ}95h9P;%%}{N-M@{%xo@GKfLrU^}gWePG!Lso9I*CZC!&pW{*1X!& z*BUzDwEQ5B%}!ztsEpj;%4ovq=Mxu-Jw|(0MLn44CP+_2@9p*lgP>PjF{E$jB%m^6 zCnAH)Donit->Osf2}x7^Rp@Q~w)9`qJrdAqK#?lB`i zp2fnQhAYW#*+9-&$s4Zaf0<&upiT%vt(dg_&t!CoA;!0kPxU^z-3RM{y6)^ZF*~I zxLp>OeRZwWd&nNwGDN?{Xx2xrv%!9WiRd>2HH!p-&qiv9eO-CdS^{L}vF6nxZ8t0;ZkV3lfR? z0sTzJHZ@!OA*vpQ zFhRgiDuK;LVY;U-rU5X=63?A*rUK4tbCS=;&c?e*y}SlPuuD`iDtPD6EFeY!r7|Qg zP;Dy8OK%vki4DYmMH{+b9KW)0cl!+}pUK6D3}9sfRah2QWKF~BS4d{ky$1yI$S#Xm z@vcdy=R`po1Hgy)=q)BLN^!B(%yJ>2nq5tvx7t{BQ0fRpXvw7B&dYrBZ$-)rrYooTr=oFz2wnaY9-j9_zT$96Z?WrvX>E6EE@NFY0sEk9F%peUj?`l!4#NQTKZXAS%#;nRrY{G_M_A(ww=V&nh5QoHeqYL zQxiGg2MG&GEvw^@wDVJG@yBGD{j6qc6(*0Xc+?jXJq{W)ven&%J7yJcj63Iv9 z&1c(rbBJ3$vUvNr@KaR3`IxAzxdL9ab;O|EgCc3(9PaJNY@ac@Wf(Q*3)79sD;8rP zWH@P6*JT`Q8KV>~YxAy>96Gg{>uaqP*5yg-E*$`2KwK3Cc3FT^8eEEoHV*O6xdw;q zUjI*SUN7^Q#BqFSVDt6Q_U!R=qjdw&It zat}EGUMo$rr&~H*w;l|)3552Ug#+~{rHN1Sf-aTJL#bxoqa*B$lDb{`$M8bX%QOqv zKTU_Z*~$ZA^zZmdCab8dc50y4HxH2Il#JI{1qD6+TeRwJL@k)=AXj3sKU-RCC!?6F zCgo4477%cZEVxXo7hU@;H=v^(D8ik9Nx zIjCm!yHL+ah0o5*WG2TIhrpHvIaftjQWZ^-G8Uv0^KTg%&{` zPaxEn2&9I8S{-RpPO*v5zQ~z0wo^QT&U)uZxY{gUV~xf3@a}##g2<(hfvXIxE`4!w zNXCe3`w^&cJ;#xWcT>XoN&-+=C~RNkh{~(_Tq9O3IUh|CZ&Y5Ce7nua|EmhxrbpB6 z3t43$APg2Ox^*0Bz02(J;HGybs^5cr(?xq2;nJtUjzz6Zb!$3)cjjFsnbsdgQx~KL zFE*v)><6eMjobNvT{jIey{T-L%ln0CZbDULXQRa;=ye7mHqdTZQhb|-{fP_6e78|t z8YaRcxT&J=Pci*>EAoSA?~+de=)ht@Be9^8oew$TP3}t1L_J%*F%$qhoBF31jv0-N z`%4>S4u_8)^Oc?awnN;CcRHC?#Ufo7f}_@gl@Us#Uq8>S6xs{ERx87iKqB>K3UN(c zfRQq62^ZhFZptg~|6uUwx3IBo=eg2N21UTkb~qF}DF%Q+S?`0;WN7dMrZ0OlV_g?v zec@puw(TY+#4N8rj`30!kr(qH!I1Nv&}GnX{e6}}m#zP^E|B-z`DN+k;Juv$fVNvd z-BhR9RT0?+#gwU0dsDPMKNykQNK>nstz;tvufSWzY0cUA8Z8Op?`X1PHkRh6?*jo$ zYA1F!@y-NxscEV@?7qNapkco)ZVEqsfRrzksM0z;r!+3Zed>{SP#?aMD80HQz%c{ zdpJAq^MQn|C+k`^3Z%@c5f!GXtMmDO6!VbA=t>8+2|vt)x#UanD27P~`I>8{F%&8X zDZa-fHcGIjBhC315_AL5p`Pcd)!87wk^fWY4+UX2)sfg2PMnE@WGDKp{KK#nmbNGa z1W8p)x0|rw2VXffOtV0eV!`-pvqeYf1njhnKonr3;a~3_;fE!!w@3!2P7zQS`L77x zmYmqRyOsRDn7E~o!?jv|eV%{5Lu}Ov`o2M~Cf`7GQ(@Y1PbG%u(C#V(a%vc28t+1I zbZ)>I$sC@y}Bf_!^et|V`&M^-1IW3R6uwb zLzyqHL}4xWsFX>1?B9Qnke~Dn%Mpr<2cm=T)Ut&c682oJz#3OYAHB={|Jk-fjx_Of zTx}pH$2fGi=emoAu*2VFrKMb&Ioh7OtXtLms@E9J;!AQJw_R=j04)#=e6j2EQ^hQJ zX7tdfff0nC16*kr(bxZn@BAH(R+VzF!-1k7w?ef>^+082iqk7=wDcOwLJU+$s;XN| zf+68@ndxUr9I%7-6Bo1@c_|ZIucPB!-v6Tj+f@cb*=TKI3HH@nQ3t9af7jNL;Pj5HDtp$-y$im_Mw zLmT5rpkzfL=(jj$Lg@s;zh`7ZmZ+AUW`165G9?lUOU~*I28^pgn0Ms-2Oa$oM=fD~ zh-Bj(Sp7W1qZoPXYKchrr2Ff!AZQe`tyc{YFz}TLTI%Wt%})r8X_Rt-F4>B1Fs^V& zJ~&;eb{}^?oCJNRkv%~4PIg(aX#7eD?>^1JkS0@+j0Eq#(YUOdTJ zui=Im$&bB}T3uP^n+(!Naftp>6LW+%vs*eFj^S0nEV<)-JzuIJF7{@5!C(IaDqu2opGtrk5`xT!E@m{!R>X-<`+UU0pR|hf z?!v$ajM8r6b&PI3MRma{%gGjwBzf0?1%bNy?sO;siW#ZukE%Y7iSq~@la&l90ur49 zFVV%lJ?qQ>MMFO-h^Wksrd0DXXBu^Tx4-Xq@fz-mLqARGjP>P$$5 zJB%vrL{|Q@WTA9+$as!QBqxEsl@$H~(^pX6WjUA3`6!Y{DQ&59Q|=N6MQYE5ys>W+20!jy zQKq{0GF7y7fjt_P1aH!*%WC)6DjbsTxfp*j{S9OXH_J32BX3`ZB;VLPLWv?GgOR(HBfR7e^gf5wu8^kNMA%Zmpcu0*y@|B00E|&M`2u)X)`XJaP{(byi(tq>@1dSYDGA4j9m`|eB^g*6 zX77PReC=Ex{3sMbvEZ=n5)G_&+UydoSr6RJmawx5p*#|DmC>O*II5#eaXhD~A+Xm> zmT1OMB1W#BLyClTWTh*Cc@==vWpZc))Fux6yK_gy>R7l~k5wRp8X1XD251TegA~me ziljNzJ1C8L#+v14dWVNX5vCFD{^KVD?spQxZzQ;1FbIFb!2N$a~ivPPIO;_PB` z2u}|U3Z^^;C;~}s_KN6zz~4YK&_3UMfAt^VfEsum(WA+0wV+6-E2s@hKIE>@62!Fv zi3etD{-2>4(23$L`X4}`VPaxW>9JO0XF!9a)5L9woT0{(V8`=Fi|fRU+8`(|PiG!w zG5&Rd)QpVi$t=!aVTNef_;RL{xXM)`^LC=>c47r-G9R-^l4n=qAJ-5%`~PoAD@jLe65F+p-DOP%H0Y2v&c6;^H$_Wb~^jHF}{RlCvhfo0W zSVaN!$k8tu2@4<%|0Y%e)WGZ~G;U8B%_jd{$h_u=8jzvgWDOtA!XR`*oi+o^lpjod=Ym|^BP=HPcck|9h}*L9C`?otW39SR|3t(0n-M&CG_ z|6k*%_4RtaT;9A90&bKdYlO4}09H^qAod;r0PvjvodGHt0U7~5nLwIIFsdb^F!VfN zuoDSnZT(BhF{W7c2o2lnpYDG&J+-ZGmH*4`3;MnP zJNn;XPu-tVzqx<9zU^L||G)Z-e`D&H^cU%m{onMynmyhp;( zRelGdnx~nVc+;Wwepcnn3)?X~DTQFTGZ$1>hUJIV?qZbQ>7%WH89{pNVEfdc6o+az zcci3*5pc+D%j9vEEtgB_Q4pwWT%aSG)fTBvm&S+By=n}7U}?sF=kZN@0(&% zI5dc555dG7M<{97Z>sX`F@_fglu1)=*}Not`EPwQ%=vuE>l$7i1v8n;SvBj6uN zVBD4L>xv>g{VCJ)@827jLk+N| z9vXW?(Tw=2!nu%u_M89y#~8!f2o943GxCu*qEAU^l&NnonD_qd0ufi=tUc1bAX`V4 zNk^XY&I8S{*%VtQbe>84bfbWD>ST{8$5QiekH%6Oqxy3LG zjPKHwnxFOTTfoSzweNuY^#=6E-|)GhSs;O-Jbj1S3c`<>d|^DKgU zq6T#TDC!p)}q7l12Wm9)ExMrLu$42>$l~8a&6ycP=~!b z+`M9NJv`aQeUF&+Fr2J2pZ{9b*CN#GwEbG*EbbTDPV9|#@HuuTx)GvUI7vt`9hn~) z8Oa&Mc|~HJSKC6BpmSdTvTTYvR0PF`f_ie|`A)OAn;JzbfZitr6el0OYd8u`6=Njl zH*oLl|F5>CJZU*DasK`9R3%^NF9W?jCyAK&6w1y!Cvm&%K z7f3R5VI#Y4+3#is_N@`+in0au{F#)y7+4t}xY&_naIqKki@RKdAa;%x8Aev5E0^E` z2FP6vmvA`mFs7<<)#mys@hnng!J~Ma$IZ8J)Sgcmr+OCw=WDbDDO<+N|4v%C+XnHz z0092`{Zaq)CDGT05Q+0Pt$Y2-#_XN2eVkc@#Kl--AN`FnF8bBYl{&~brtty?w66=K zt8!=6VY>Y}|I_>LA%0DxZ8FU^JCGGAuj3S^k%)}xo1&%ena@`=f7pey@!UDgSI%q1 z5%(pA%jI4E=8vdoPXU5BtN{J?F=nMhW-rHJKosCKtu;mc@!#}tAM9mb~f>L#D{#!RTv$T>X`kb*7+s_Kp}Yt|&3p--*aU7YK=evGFkcG3UQlLrOFqtZw#= z=M%fFFric4153Rts*X_R@nesd-UN>=Az5cYEfBVi?IV>RU>8~Q(@t#6guJn(mP>7$$bi+0DyMZ(` z@kYMJ-L6Z2mw@-~WH@g1$DD2$)|Y?DaxtR)w^y!L^Mgnrt^{qWRhE|a72EXxz1@jE z=>NcvY0$^$`7rQR<<4O+^v@2*8%u|_8Tf{7awZxJ=boq*MxDWv`|?+7YD^ZT#mw*{ zsrvRNZjB|bcPg)64>Fp9a_20+sgIz(|AN|}^dA@f>M{h2Rlu+Pqp=w{*bdSc?QNra zZO+A)AOQBh2^dePr`CJnw&;{jLrB`}s`!JVoR4Ik>t7ape3I7dsW&5zbQqG){hHLC zg!M5Om^vA&u_tcuds|(cF$+!NrrYpYPy{fQEBQ_9vb$rC1Cvqr7aZP!L_@((9#4; z+QA>Z;vC#r{{0CW+`;1@a_~|C{ksd}WSvN>`s@t~mk+n>qXkkqHF3l@G3QN@x3Q)9 z42l5BKfme=I2FP6uMfh}sd?LkYIE?DxTB!;AKzrg#Eo=?dTH<6j^yqz7v#}~}J+kweWHv;M zKnd=vi73oh)R%`AlJxK*hT!B&#P3RPK(Yfh>2O3x&?-Ht5ljjDhK1jm>6U=MpcPP3 z7Rqh^V>Dgd(8BCxQZB^OJ*y4)#xN*{4{B2UL1sAXNF^fnMiQJ@8y%5)nWP5KF?n6Y zAN8kpjuNe@bvm~UiN$DfE0$j~t9FaIKP=y@vsL0!Y!t+C($`^|`dl5_qk7EO+?|!* z1F-0cwYCsz!BD~XomQbFHhdH8{Z=&^)b+U%^fDS zPgj)`DBcM*&WU-dLRP$acj9}h^eRguv78eT`X;0<>5{hO%Nh_}J#nnJ}?P88tzFGx*La(0QFf$@3-Wyn<*#r_Y(i^go1xzO#NEpSged!IPzzH4mLA@XGAkJ+**}Ww~RE*@nWlggNU3Q0wzRP@Fy9C&$;U*;<-cP7s;HF z2Ynf&T~DK)0BEV_3(q*Op#(B`j;B9dtj_NCZ34Vq@^DL$97nr>cRQrj6iyuZRV<#W zRd~fzyFMJ?kIUb>u-1I9l*Hw*)LigDCo$1U){pVR2khG&El%>WxSW~RAkZ_J0m4bs zx-)eiKjSLv#It7&m;NA5tkf1$SljB{O|ig!=v*Z<33QvvAy&mAE&o*I22a^*8y_Ve4tmfq8`(d0a?*T z_ZmLP;%5>Wp)lVASgUFl{3j$pmQ1|MHq?6Db7QhcZqoKNg&1-k-U`iSiS3{9Eg28( z?$lul#zlzo0=Dz&-Gs1qqDyh(0o~y)mE^v_qIB(KO=(P1NHxyS46P22Y-Tzzb!nV8 zB;7le=DP>#Qy$Jd_Mjy)30YE~Hb+Ki224n3?7w&7euTXwNZq@6{8}76scIaVpdLPVo&OF5ospXwi0kd2qZcr#4 zyobe(teA6Z9ZDl@yVcd~>lUY{yRpB~svvE{CDo7%BLep!m8d~aEF~NeE38s)pS-+Q zYd?kH7ZcSk5omF7SqOfut|(O?TQ6Slc|Dw}*|(SR|0ewgJp@#rP!eLO-8{?+5KRr~ z-TZPv>!3hvtFv_%+-zJ&$8wkG;17iYX!lJA6`euV*|>_-wt60pLTy7AbTd=8K{Q&A z2rw+pcI`7-n|J4hU;X^22tj47wDsDf4aPUcH@mXr-qfWSp7^)nbM1qzV$3X&E^(=| z_aLHPKufcugZ8E|4e50h4snnNG-}5aqEFNKfx`@mJwO&kuESeA&R#1#TX)Qh$yEzj_SEqIOJrgOUAH@+0xb$oi3E_MB#N2#1N zn6VM6Ff#aXMHW?ZB724$oe(yp!CY@W%nhf|oR&Y@*eJDM?vQo+eECYhEae5d0mLm$ z6>h1{K=M12Sc(v14!M1GEU9;G8ZCYFKTz=iw?-WEW8UwWjs{)Sp3$#Bp9dHCTXA}L z=v?m%^&%;b-Z@~w_nC~?dBfCQwQ1l+IzNFRa0hJN4w4c`O#3p&o{ib6bi64+of^zsAVF2=DZR zmC+J~h;KO};m49@=)GY%R=U3P%f5(T@k*sLZ;S}#&;1m5J4`7FeE``f@;WzX{?e(M zgI#}Z7kdZ!!3HSB`~t)#Kb)zqkn5$^>746jyZ-a`{E+#4fK{~jp?p>itKSs!qx*J^ zW(WWS?(?(xKcLQa$XRT+i*a@!U3^`@gm zBWf&;voZ_`+Xw1^ynwn5$}LYNLMPX{uyC?Fw77=34vXraY&{lm7R?#`^aAsB#T{f= zsSh(%X2Tz4m{=n-h^J^{+hF2V|xx? zG1WH+I~+w{{AR;=U##zUmZ0_jjg2cfp%Ah6p?KhZ<*_#0Zl9H2_;S~13BLLF7DwP1 z_C+YF2@XAom(a*L=XG-q?Vcs?kzi#gI%quA(f+!(sHleT>GUjK2Zja8@x?v?iHU2i zb^KnDj(V#>iD46X05R+6?cxs4n^4Bx1C+JitH=VINMMD;YmX8;=fmq{s=3<6C7s(9 zT)q?qchk|_XT?6y8UxIbq`s)n-m~|AtIS1HDj5|HkjDH@!vV3l1)hD@Oh* zr%2XOqH1R9gc{sp%I!v^m|e)NC&Kdj3_md@XpvPH@?eD#-gC5YQ)M9#GzHl>Aj&XQWEqU~ zE>B4Uv?*Y1rYbK5AS-LYsv7)Iep`c~cV^?suQNChok|=_f+hkCFClY$eY=rAszq}H z3Z$Qi(*FtK+SfRT*kR89aZxaE9svrV7U1sy%s~dK&4KMS8yCsQOa?5}3($T1X0J1f zlzCOs&PBbk?=TD*%Dl_J{7JZ?<}KSaZ#S~W__3>xe7Og5ttqVKYp__hF$XW1 zt(7D(oer5Z8DHuGJo+yhHzp2)&S#YDvR4w#KjrpsmqvK%n)-V(;IJ@m4pe~F(?;Q2 z#hhu(%tNWa;n#qYWoIO5(IE~+Wk*hbqV$1^iH4oM1JRtd3nNA?Ec<~y&7R>x=nSBL zA~jAI^K3ob18{I?<}T%=fn7p~vJBsqa-p_vjmoDQ*GQyd(v@;Bdi?d3iLqllJ}`WS zXrh_&kjaYJ%9^@-P-e&EZ0%b|S*lV;G|SV>4g+4Tp@B@yp>#}G@Gzx~VJlJli3#sY z0N4J8gMQhikrbQH_9O!)Mp&*3>D=DxPzI+QreD>_=C``_=TQ6Ck1nI3^(Z{|Bv8nm zzSF8jek~=FvSbRkd5s?$|ARb>*4ceh*~q0xXl_#4Exe7J4iM3~1_i3wXc^>C*>u#I zCz7S|iyV9WtC*FP!P=$ea30xBS>oo^JGUZDR0Vo$vWA<>`@Q%Z3PFo$?1~6JD`7=- zOk58j0d#K+2_Rwk9Ff1?1AY?A1(dXUPZJ@OIf*Bx&SzX6SPlT8R?# zyF|UG8q8SXKjn6H1{ZO+Ni+3)To{gt?J#xqHkfxEj@Y5pPO4^_ZZU5wIm;H7TQOK{vo& zfV1Oqs20!{=)4ksC;`v=T=*P&k2UwGMc_pNwcD1SBY7|=DPlBu=q#j?-f0fWW zzt9CCo^*dxGfsh5{bz#4C*hLJVNn0x@QAfj+Ur!YDCXbC$lEzTMkK=^gnoLYj>NY2 ztRe$6tL1Jp%e&dI~g?BFCzQN zTrmTsW?*Edg~(cc`l9^aYJ8J}-R;;WWVE>XTCfa6tc$rHR>9_vCjj=f>}FQgJBwkp zd;3TPMh*g2_tI8|c+lv^2;m>X&%-I3Sns?MpPve;s!Z!BIh+z}dh{;NxoT4Qd?637 zuu;|VsFW=LqXN0UG$xWSh$$dc|%T)qQR)fZgk;l{V z;R#ghQMeStP2alcYeXZ;4CM@E>~%ciPrW3X(Hj0|R9bAp+wJ z$W6(O6XOeks%P_Ft{C}B9syI8#Hu&AIAB8D)+$}9qc#t5i?8n`E0g1GKLZKZe^bch zjq9*~FnKuR1KS0KoxSITu~ivl7&15zJ|imj`M5jHLMx-Q#i&|V%T%+;je}{xEss{G z*m!tT%=d4ZRhjta_Zs(Y#-Lafbub}nYHk^sLbg-DEGp&>G%YR6h(%)zii|(ZNe#|4 zv^EV4;?H7emU|bPVbm{tH2dTBX@aUq$aJrC z?Ag_*K*hSo$p>bbxNnzJ;12JSQQ9e?9Uq?Kr-L|~k??45bEpmmU!h;_WJX<6ovi%2 zSgGnC&M!Tsxk89PZjG1=UC+hcLqY{B%6WW5**HPb3pX99xsZ1+JG|w~uawde+aF%v zS-lcxs0@R1zE6cUBXE6nz(8c6H-9a>aA*Ip6^gsO*If773fg;;u;8Ov-J{4cyQ>17D zZB=RX9F5jm1Bt~DPZLC}NmtKWL*i(gElj6-i|{AW1$*W-P3`Q+ljHG#dnKWh2m~_E zFVW<%5oUHPl2jyqNVa@q;YeTKvSI&%>tGHV5h}r8G!$+j$H?5!#eEaINV2SU^?`ns}|a*roiMyhCt7Ac3NGA_fpx+RpMn_Y1QKu%CGW&Au`Egi(z4N zfewM`|3O@lya%If649+?nB<-F(fm{>UIxR;1t9%`O%u?WNM4F99{)n2o$TI_-1enC zpxzS2G4xwrvV}=Fa+9bV#u*y}%I9x1s|Fq5-#kh@V=;A|^MK%x%jBx<_Q&y!SxYFQ z93H9Uk&`o0x_F%htSnQxP6@eQA#arQ7zdIgiph7st1H9 zt3a#t$onSC5zEFUxWP3Jh2G^vS6mZ-TV}Vns|zEBalp0bG-aM5He!SEsb2|S;YLfq z_a(*arW@o2-C2-)zUUq$##Q2r6MgxX2Y#Tdx>KhyGS;6^_)}3V=efU@!+xAi)c6YL zG~6&?dh;iNs|4u(onbYwcUcCP@`zt|QRgGF(@`ZwuCItr3W3x!Yk$24#q(A(byeBLP1W$>eJ^c$FnGpN8nf~3hUOHry3dF9_6P4p%yNTc2` zE=y%m?Nx!6mM|#>YxB3*hjLv*TFWvXJTFhulbq0!>n7#grJd%{Y@}E6qVz&IqTWv( zq3mzaKQ$0V9g#{HCg~HbjZ^6kFVn)W_yb8VBXO*0`97MHDgNJJvvx-W4qYhMg`Dli zN{K(TfK2+5V^{;qXAslaDTjX3RRx61sn!Ta>O{;X$Xru{=GRy^qa)}{AXkW_~;@nd4l9qVmnBsaCxDbw^p(hSEPI0X36ej$~Fw}i=Y5I!vq z%=HFW!vGo7{|2d@13bY?Wd}=>tx^%?LzC&DH3xvRX^gN=!mH}-Fj8_@@}abQ zla(CRQxLLuoe8sel&~WW?U|f|Vq9w>B=oCQH|k&u43RW9Oy)VrIvvu{= zpzVZ%flm>%{uj?I5mMnFBtt5;d& zU=E%QaK3WPYH;tHw8G2SGT*1^4NROT<^5eZwE3u>>l%y4d`_e5HQTV=13jax7Uyib zVM!kKWB@wBvwqbbl#fk|MpY|%Yu(3PkS#?@tj$bd=!qMh#aAcB{v?rjEaujjy`)R4KsmqV^zdR z$aDL=i*fg9pS{{;)(>udlu+&9h^io*!k!=>bo; zomDlRcyXe?^#jC*^|o1n2fkAaYG`aRX*;?ir4$NXa%C5o{XDW-=r0vIRK0{IJw7`K z5Je69 z<{ObqHtQ7F=qT{|CO96(LJF_$-eN+re^_7u1YX&32Ql{2eCLSA6!Z*4o{)gB23HYy z)Q#5BL6id?X)wj<6#bl>1DF%UA|ZiqF(#vkJzQ3sNjq{0yNY@s43ie;ppc|dPX)$5 zL6i6nrmfyn(%rfyZh_>952KZrV9;>ZVCDaish)mywiU<^k^kuOz#UG6>udQBc)G`H zWS*Hc{z*$?doDljN16ZGDM#JKJugI7g4{6SQih=11 zbL_|3>;Shi_L08_r<>ZruD6cAfLx0m^pa%PxG#3-OOm~c#T+=s9pY5Xi8bvEUz<5n z)1IVe&eXIQv6@4x?14 zb+MV7HLUDno^EkfS9A7dMOSn8Wi?lG_+=#*b9q=rG@rMy0%=Y&mBmEO?Uk1*=J;`; ztGT{kq%+^Quz;xZOnJW1+<%)b835xr#QWN?hHCOix;*znk1BtTevHX&ko))1jj zqCt`$dQ7B;f{EdwVWJXIFi;Xv2aF7i2^a+!4Hs;<5MadLGSVL=Juvd=ztexG|4#p% z{yY75`tS7L>A&+->8}|P7y%dp3_^wiV*&O0@KESw#zT4)@gT3Lpy4OI*`uL*hZ`1@-p*J^?=ciq-lIDbGJPmO zQ2B)JILP#|G!&?OMi~W(W@N>H(ySc@!aP(EFw)G@{HHP(_5SN<{>gip7G(9E(xSn2 zO_rhFa~&-`_$|}&#cKntkgLxMty0uumDfR{{!*s3ih8RGtyk1nm1)gVJyn?2&6A%7 zT7ULZPh0Gphgzgp3z9#mb+gDrp*WSNeQ%zN&TT6?ImaRMaxV( zDp~!|GSl8B^+D-qm*f5LwIR}Ob1Da}mB_U3+}-tv=@6N|Vo3LTr0S4_zH8|6RJ0wL zre(}2&{hFfP&go53IG7mLjautDjES90Y05fo=UVgqopa_m{_nAiDPd3XZL~5AIEY* zo&)&qc`b+jP#5I4CUGQi7Dv$e>s;T?el+BE<$Sh=tMu=%PwDqjPcvS5UXy>9{tX zk&4o^4+i2t<<=*jW#vHq*IYG5_&lfo%YhZoQ=F!>E{L~!XUF?2r7f%sXk%NRonb^A zu4t70>v@YQnM;Lp@8z*yc^HLTEO4%2lB?fwp`k(DkoNUEgPPhmPB}}`@*I=|o1oGg zqWeN*b+G8~3`+tK8kXX9kh99+zmry)!3I`^>?5|5y}@qE`NRv5OM&lfL+CX?SPwQ| zi;~D7xzGCAm-4Q=zXOPYXe(?cBVr5P2|PO53imNa2fYGRtawj~EjGzcPBl&yH;Gen z=1_3lo4trfddU8T*>sjsruKE`npHQ4``ZsKy?*|634QoN`*pC3L3X|dHQ+YGNa?*D zBZZ`_T4ALZCJ}S?Xqu)^U#{-nOcl>S*EewMW=7a%g_*#Rh{{d~lya5@W#g4q4}?gC zI1X37LZ9EF6_nK|k7E6EO5#x$AM(8U?0NL5p|zgq&8KAT$^tZ83kl|`@Q(33=8%5> z*#h}<>`jM34|T)(v`?Na*O%*xu^skR&^Dcuu9=ym9rPMOMOOIU`2z*mYn40PquNFW=$o&&_>ilwNN(Vzp;8!XHv>lWdD<&?>BsCk$`o z0NO&@g95TVB$$atSh>UGw^~XbZN^Vi?{2RLw*F<}Xpbu3P&I);)yq~;(d7E!%uJjS zgVMJX1@uzI8w%QI^n$!qAY77Gj9iwmm zxIlqrqk9*5#f6CeiZ`)77ux%t3Z;U?maa+TPW|`yrrw|^{b)4wJaXI!5WfeC&1p_s z>yGoi;$kCIdC9bK#<(}N=b(IxBWq(aBj|MP?7t(H!sE(*oEFi}j>p(_hAzuxH#QKc zmL>dNSpm0WhWFF2$Fa|a!{8g0q+!o&cgMiTesi#Z34B=T6K*DS?0JS8T$_!WrIc_| zf*T{U#=#GuOQzT$fMZcB4kKc&p0R8FWzUNKRjWBuzw&4^VDayP?RlGeKKDbcEirb8 z*qsvWYpOEe*0Q_8k~Wq?K-0|P7Qe;MRl&VOb`z!OV4SAQ0k8BY8@~wi)X@kdxHi7M zr;OX5Y#Y(vT>wQg+4~^I?l1tMrtpwm)RV=V8uA`8zrB<(jf6Ofg5Z8G45`*3H-MH@ z=ik3s>@XCzk=aLHJJtH$;%nn;NtlXU9Zv%UB8^1>kpUN5Spj92fhk#HSGx;GRd$Y# zlp@e=8M-OOd}d!uP%i!Y z#eLu*%LM!3^T@m@a>cbA3TY(uSn<>@uy^aS2cduGt_vg+5|jbR!*E5NC=h`kCG0l2 zn5x*QE=jmtx%pztSCMjXmS$gLE&*UTQYxkbQ$e`?_iHkDh|xA%+7~vE-qDGa6plfJd%5-zoEt|J^HQRK@!BSQTXgJ73Ghq84O4Sh{lPt0zRE$vodP(5MvT#(jqo$p7j;T&pkHN0J}s!n z^uG4{Tn7e1A0W=%rt&vZ@}?%K3>#x;EI1&l0`{<2GyVNqE-^*S1X`_yWMTq^#H-*P zYfB6Eo;;ojgo~9e=$>G`KvjKDsH@I7|8(iU$R)*&duLQe@T{LDZnigZqKUN?i!0$5 zPx;R&nhTTi=ta;c07#U-h)w@@Yf^ONg^?qHeE7q6qt9UnLpbr-%T70u(Oeg|1E`*UN`Y0>Mg&$Zv>qW>Tu0y= zekk&@_$9&(l2nbXkH0hsYJxjo?zFRq14>veIj#UMeIG^NvkJIho1W6vB~sr~_@Yd4 zv7q+@fS|h=izujcqqJ(PheIOnKs3Hsl2=@!EE`TK1QC+Y!I~cK#(R*_XLnXhUo;E; zH|=JWGskcig?2SRwtFzzgFTnBd{5*1{{zHRP+J%Y1l~E(HG&%`b|(@4!TYAoyL1;GurOs5Zr__MAm20s&I)J75*6wzyBt+f!H^_$c>it@NBFlaW1=GM+R59v zdFg&CotN)uq0?_H%Mn(R7@(aAn0d-eMWn{co{dhXOO9?JBT^%1URfhAW3U3kVP72R z!pnqy^hbhQ8CtjsF@+!sw5U0k>*&L)@3W#H zZW2GL1E}X&oj-rckR+e!a|O{cL<2UF%kI=gNP!Yv(cWoZ7PiKU9@K|459_a`l91?W z_s4Cz>Vg^alzgrZc&k?a#3-GB;GU^$|&L>>>^DgXkKLT_~AkqdCl;L3?cL-OJ^u{=yDol6ROb=v>{003QiS_rNyC;-a& SJEp+^xZO*z;ls(=0000ZynYk_ literal 0 HcmV?d00001 diff --git a/assets/images/comment/arrow_grey.webp b/assets/images/comment/arrow_grey.webp new file mode 100644 index 0000000000000000000000000000000000000000..7a9b51b3b271f1b51bbcf8f7ace21560af38db63 GIT binary patch literal 232 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 literal 0 HcmV?d00001 diff --git a/assets/images/comment/ic_send.webp b/assets/images/comment/ic_send.webp new file mode 100644 index 0000000000000000000000000000000000000000..ad8d50813b38ed348008198d6c4b6d0a9df99367 GIT binary patch literal 378 zcmV-=0fqijNk&F;0RRA3MM6+kP&il$0000G0000x0027x06|PpNB{u<00DrMZEx7- zhad=sU7kTPf)7(&ay5Cq{r#8w*RxJyJ#0RHL9eWV!w?jcj<@eaBf zyB1EC%5e>*PosB@B*oa@wvL7otsH(dw~hqS%{a6LlkwOPQI<+)P55R16+m~R(l(%%%oPN!TT#pt(H0I77oabe@hngFB^z55lL&lUhkjqdphr1?2| z3IOCrf4&5OOpiu3#ZirZLquH!#~+@pA%=ygw?KvGr$F|F6o}^=;>s6Zfl{ZrmOx6< z6st#LSq3n*kp!A(8OKz6Zv>t#W4LzqNJ-$#jpQ8ZWO}fr+W><9tX5DsAWQ%N01yQL zodGI506YLbc_@)bq@p3AIsUi+kPiuI006r1z_(Pbj9I|Z~C;$Nd`q!0z Y{@nEXm@7a1x#{~BMZet5000000NnARfdBvi literal 0 HcmV?d00001 diff --git a/assets/images/comment/icon_pic.webp b/assets/images/comment/icon_pic.webp new file mode 100644 index 0000000000000000000000000000000000000000..652eab4e47df61d7b0102ae12623986bb02a410a GIT binary patch literal 330 zcmV-Q0k!^8Nk&FO0RRA3MM6+kP&il$0000G0000x0027x06|PpNW}mE00Dqi+io34 z5DY;O27(}zK@1@bA_gskfg%Vk13@r^`(Y)q<-WfnVgl%Uto<_RfBVE5?rWzQsW%P` z%~G+FErs=HazI0W4a(7AvWlUXOmaaGTA=;31N)gq2qs?H0Bk3FiqkPZoH02RO=Kmh92;hV5G?Qs6((|$yHpFf)hyE@y)mlAe(0R8Ro c-{I0|-C&*4aC`p{=*avwzyIw7000000AZzt4*&oF literal 0 HcmV?d00001 diff --git a/assets/images/comment/like_red.webp b/assets/images/comment/like_red.webp new file mode 100644 index 0000000000000000000000000000000000000000..a2551ecc2f141fad7572a23a436456a34f566b23 GIT binary patch literal 504 zcmV;CqVquoEm@DIBt=*81FjoUlnA#8Ta5(SPPRGhTL99h(9E%S2lMaRle0~HTkOt09H^q zAoc(N0MG{jodGI506YLbeKL?oq@y9BH~m@wkPnGw0LPpads|5Z00Z;`(aYEa_Y=}M zCO83QPAGc?om53ZV83vHwv_@+)d$@G0RH~WvftVRa4Poezwc@1EIC#4)Q;yq7lG)P z-_vvcx4gqGG5&lnDb*}5aD%u?Be(M>mRVKV#F1Tx8|IZ@y8V*>zHXR-sMKlx>`h#Ad`Y->OZP| uD&~LcUdM_k{{#E{yUe3-1%@B5{|aP9%5{FkKhP8mYsKc%pkXjGh#4#lS_T~hLr572f+4&Y`IFMST}8wM$ba}@sNet&Dkm8HatDaxU);Yx4UAc&qG}zUei| zuD$@CZWBP2lWJfpCo4eqGN8(7T|X+P7oa(1z$Dm!?DGPqr~)iLE0CfRu=;dZCETF< z04=ToZtM3L^tcY@^9SUp4E*^Nphdla$tQIn$4r2Ua|J9#Bm=5fGT=Aj1(>*JfZ{g; zWItH|veyHkN?=-nKKP6PO+wXxK6vB~n8t`2(1&Xe08@$94)D1017Irgg8|IvW&l&U zqZ+_`B0T|2<(_H)P51Nz@KL#|t^lT+Yyd6EjTg4F$Pe2^?p-%L8$d5|_iP;6Aotg{ z*OGi?1$vQh)lyXc)dK)lP&gn^0000`1pu7^Dm(x@06uXfk4L1UAt62f;E)apX#fDA zu;kB%qAyU{I%qK)>*`go{&$o4q5uH?{)YBH|HGua{E?I&|HL`~0Wtah?Ek#E0002E CkjCTy literal 0 HcmV?d00001 diff --git a/assets/images/comment/msg_icon.png b/assets/images/comment/msg_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f562773bb9e94b39441b70c9dbb34a3a46cc8d14 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw3=&b&bO2Ij0X`wFK>9!F;A8T!KR}&=B|(0{ z3=IzZ748cZykBs^AYuK4fP4w{br%^J7@0g>978;gznvPLb;N+jHTS2Y<;gQ&-qmmZ zo6#M;F1g!jVdETq#UiEsqIr3G&ex{-Rc6dvF=>Os+*g8pkB<0iEr0R&N}q|AnmU8k z3FeGMPiCF|O{x|*8q`&J1^RUwLeEWou&6+Esic3}+CA3_mwwg%zci+`dFh?QL6#p* zvNjYwn9O_NV55K7f)u_v+|$bW{*|nrw|^SH!p|oxdw%F{cvWcJVDlrfq_1l66UK!2 z-M^ntE6$ueRokr2q=@mtx0Sap^q)S^w)D#S>*t>(ztFqQEC1x5NAdodJ*PjE{=L`Z zlf5azZ{n}DKldzgc1`zPA0WGb_tos3ht|obUiZ&k+ctZ4_h&w54)L8lYnMF*1{H&+ LtDnm{r-UW|zFyHF literal 0 HcmV?d00001 diff --git a/assets/images/comment/vip_v.webp b/assets/images/comment/vip_v.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f3ee4008ed6d82860c49f2f2c2989e01d59724a GIT binary patch literal 698 zcmV;r0!95&Nk&Gp0ssJ4MM6+kP&il$0000G0000r001=r06|PpNK*j-00EFy+jiYn z8-gGl2!S97VGu(I0}DbJC=3cg5DMWy5Cg3bA3L^spMMcC0s8;DQteoN`*VhGpe=j3 z0X~DX^(9GZb+!|M*R>N*b@-B8)>77D?TjxgYgx;BJHrXe^S(6v4` zquXs`1~9;NaV8v9FQ}>;V7W>yfSkIU5xXwWJ5A3;Z$^->Q;Wz_Ca#(=?=;DSfz6mQ?n#_uLPR#(=xt&xBj2;-g5~I zx^&%fRxk6qkkw83&orNo_5PCYn9KL)y3}+{gHK4&&cnYpax&pRjy;`lm1|@R`Gh+6 zQp`c4592;Kl;r4)S=Syv+&%uKIHi1WL3D-f%I$;9J7%me3--Q2CEjv#x4+)2-<-Nqj%p|8xs* zGDsxtkjUmaHhn^IUsdPi+#dB?1;G}6GYaPg3QHHx-6>YQqhFUba)!W^)|YjAC&_QQ z`!nXmgX;^v=*l#$i&p9zGpDKqE`GDCh4Xlgi}jIvHZSfwl>K-$u{n+F zf|;w(-7VWVWpf|5mgfE6>~YnGuTSmCjy>+(zjxf4!+L$D$#L~L=?mSYRVVLo*0{TV psqo*O_KZsz=M-n=Ot`j};ipr__K&fFNkA_$c)I$ztaD0e0suaVg|7er literal 0 HcmV?d00001 diff --git a/assets/images/common/add_red.png b/assets/images/common/add_red.png new file mode 100644 index 0000000000000000000000000000000000000000..fb9537ad81685d69bb341b0616b0988513595e1c GIT binary patch literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDB3?!H8JlO)Iqyv0HT!Hj$Gx^)5@^{SSZ<)#8 zu~4{cp>WGW0VvolzX+s{2)((%#~# z%-7l17s$CuHNw;?z@eBSq-iHJFNcGw#6?w^1wwD+Ry^|MY(Ch0*w<+hYr9LQ)Zq!N Z45~&BHR|g3=K+ml@O1TaS?83{1OV)vHyZ!| literal 0 HcmV?d00001 diff --git a/assets/images/common/arrow_down.png b/assets/images/common/arrow_down.png new file mode 100644 index 0000000000000000000000000000000000000000..fd00431f21a64600bf6f528ca1e9e59ae70944d2 GIT binary patch literal 238 zcmeAS@N?(olHy`uVBq!ia0vp^S|H5A3?$9pUdaGb(g8jpu0Z-f4)EI7?hsI#qa?^L znBl!afr7yK{UZEa?}1`fo-U3d8t31h+Q{2rz~d6geTX4oDUZn(51j)XGaEHCE-Uo+ zHYxqJ5DqAq`u@)o|C3ehmv_GwDU4I!d~VjFFRL`KTv?r0S!-yrzFA*lR^K0%%T@+U z58V+fX4)aZ+v;#Ipn0Ka+yYU#g%4yjB)HnlfLMx0HLw5EUz-bdP8rMgJ8uQLg2B_( K&t;ucLK6Uf=3_Vj literal 0 HcmV?d00001 diff --git a/assets/images/common/arrow_right_grey.webp b/assets/images/common/arrow_right_grey.webp new file mode 100644 index 0000000000000000000000000000000000000000..a28b1b70d8ef478ab2eb937444f3f98def6570d7 GIT binary patch literal 226 zcmWIYbaT7Kz`zjh>J$(bU=hIuWD5YXDHu8W1b8F>d5rl96LOziu|Fu9C@2_yGnvtB zVy^Yh`60@UT;?7QOpgQ_SXRwhH@|+>|I;1+R_ESs+t@zK)L&!%&94lzUsvprdt4}a zB1hrvbSBA;H7wkm!j_qn7Vm0&#klG$Z`F!D%NT$*D}(^;DPUok%cy0>V8&oK(Q1bK z(#1*(4LBG!2ytdIFchzR=W_kp$AqnO{#wsdV02*k_v8HPUo4FF|39ctlehU?|G$B; Z@>l8qzce*+9WLbLN~se;&xL ztgQV1|9|+AB1xdAVo8u+a05fZ1cCPo6?z;F0UjL*-w$LwKRzSEV#9*_=XXeKzrKCB z=e^(B3=B+}o-U3d5r>b?1w~t(C~(OO9a8D96c$y!nWNj%^RCgDtz+Z57danz64U&aO%a|sS+g|g#gvx&C4LtK z?z9!R@y(ubIa1^0%+0R4&)1(mZ)-N=Uy<9DqNslbI<`R^<@@&Q+@Hg}yFKUm?&(iY zao*mozT8`k9Gth)LSv znQVI}%~>;f&N+kSWo4J2Jul4*$eT0AMe3?okyc+w2Y>qhdrmIzwbHJvo2pZ^YR*!3 z?P!&9yZpDcmCw$vO;Ro|dfj2O@bme<*B?KU*wCo@P;5V=`4!KzpW2(*L5bPZ)z4*} HQ$iB}M+beZ literal 0 HcmV?d00001 diff --git a/assets/images/common/back_circle_grey.png b/assets/images/common/back_circle_grey.png new file mode 100644 index 0000000000000000000000000000000000000000..519613001327bcd4a9c58710c40c04a950bbaa09 GIT binary patch literal 714 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAjKY=BRQ>;M1%flMHY2IAu4L`6kq zWo6;wfq{W)YHFuWojP&i1Vqr))ioz4Xa4;8n>TNM^ym>>Wp;LUZf-6wFYiA?j~zhM z91i?=?;)UY{P~0l1{&vgESS-e5h1Z-yZQ1@&I}BU|2$nBLn01u zopHNpwE>T7`BQGj+bXSbMV9UV|DV|Xq z5~FALHn#eW&Ej1*npnyvbcZBQXq1W<+o-l)sDsIop_g@Hk-(!6zbBkYCziV&Okhs) z=`=B0blsKL?DCpU6Riv07dx~L#ZNR}ap=c=CD#=#SM(=3&-}bFw_2w_XOifPuNJO+ z*{Ml_oHlpA_|C4fwem_W7)eF6Rj?`$e81E*sZ$O=&Um?x}kAPP&`@W z(7YWpq`%DDA;G&{UGLbzcb(lkA8k_CYkRc0%|cIX?WN+F-x;ojCtdHVe>=GJAHU%g z{YiUnghT}M3yQ~dOMen&%K6n%Q?_~K$9pw z?6*q!lvX&4Xc}C7SozQKTHA4ZkGk!@KD6!bC|=Fmziqo|?XtBKbvWPeG@U5ErA{Vp z<=4q&Hhpmpqc+@EjpuJem6(~jj{ z|N3DC}5HQU!c)I$ztaD0e0sunkQy~BV literal 0 HcmV?d00001 diff --git a/assets/images/common/check.webp b/assets/images/common/check.webp new file mode 100644 index 0000000000000000000000000000000000000000..3160357d52e00bae64490287cd5839c7d72a91dd GIT binary patch literal 288 zcmV+*0pI>oNk&E(0RRA3MM6+kP&iBs0RR9mN5Byfr=Xy18^#-dX@D;cg>Bn5^#3P! zNKxBHlIH+0{CVzoi=b`WCE5&cLocv`GI0?(_?-hZ>9?`U)4Hfk zv2pZuh_%sUfL$)T<7NlWxEgN>7?x=j!5Xr4Wk9FTbMJm^f~s`7(FmVXxMpMfbI6oMrH literal 0 HcmV?d00001 diff --git a/assets/images/common/chevron_down.webp b/assets/images/common/chevron_down.webp new file mode 100644 index 0000000000000000000000000000000000000000..8e464fa45adad2d3b5aab30c5ae4320b7976d4d4 GIT binary patch literal 200 zcmV;(05|_qNk&G%00012MM6+kP&iDp0000lN5ByfH8679Hd6opX;TEN+O9cP%;GWO(D?LxN(6)wFx+1#B;kL($CS4!uv z?hC!)t0XppY$Ar)DV5$7=c2;|Z~NhZg8{>Aw4lOIYWwjt`<0ZOzrhcTw8UZm$q@kW CoLGwh literal 0 HcmV?d00001 diff --git a/assets/images/common/close_button.png b/assets/images/common/close_button.png new file mode 100644 index 0000000000000000000000000000000000000000..14f4e49056d1436be3102d2bab60ad4b79f91ee3 GIT binary patch literal 830 zcmeAS@N?(olHy`uVBq!ia0vp^i9npd!3-p=THcrdsmuVM5LY1mpL8H0A>rcU^6}%x zM~@zm>&)>d%dthMT%9ShY>gw|H@)8mfes2rp23jUq z666=ma9^Q8;Qa!J{RtN)7!(Ao&!5oVsm{Q_RP5>E7?N@C>a5p!iwy+al6!g^Ti&HJ zdCuBWUHQK~CUR}cN@rUMi7E5;Pb@Mr-q%+=!#gnh^%N%u({r!hRov+FvS#io+;!{8 z>*(h9fn`gVTw#;(5x8wnTq;uDTs$YXL%qnkz;XCMB6OO64Pmm zpL6J3yYNXhV?*Dv6g5We2coV$wZ66R@!;J z&U@-=bzI@QzwF@wB~~rw?-w@-vb6 zin_4*1gMrjt9O|wWHU)P`lDqB*T3Yb*IXw8KFD7(iF~3_!RR~LsZGhB!8I(@?I5Q{ zqWY@|iyyF-T-tgf@B>p6N4pNI_JYf|6ub=$n5>k&8o~t@H8ksZ^)zu+IJe5MihgL5 zvgFcwz^nd5V?~2`Mv6uRWBvotzz>gkCP!9H^E%ysE$?nmbyui!)ziuGd!Lpsh%LJq z^+n~v$@ytd{8JjkK7?d`w~%D-x$@FZMRVe^<80<4)gCblZD*pd+1{)ya56udw*B_I zlC|$=ntwAF2-&r>e^rdf*M5I&efck^MGhmcV$qRn z6Y^5Kb1xd%zVEoTc(J#oqVPU#-Ec15e^v_r4=MihQvP>o%k%7+M>;n7f6UVS@X1S4 zXWD~PbHqRVG4Q!lm~tq?nW2eKT)nX)`RIKouC)geODZE=Q-g&rhuJ@vZ#iGaIyZK) iK)|WU*z;@N)~Crot=zxm@EKs*WbkzLb6Mw<&;$U+=8G-> literal 0 HcmV?d00001 diff --git a/assets/images/common/close_grey.png b/assets/images/common/close_grey.png new file mode 100644 index 0000000000000000000000000000000000000000..99070b9142945a1c3758b79c510f07881595ae6b GIT binary patch literal 524 zcmeAS@N?(olHy`uVBq!ia0vp^W+2SL3?z5Yp7kC`IR*HHxc>kDAIRLhcQ23u0bjm+ zfiXdJUS3{VSs7g9&Ye5R?6kDBkOK?O0F4kW3Gxd*!|-7L=E(ELX$|#%e$Mojyt=H1 z_nPvfdABPuJmMJglSjzZ~mi{Eq^lVGn37+S=aZ5 z*2PSnUv#%W_~-0v;;n67-V-dfuLneZm^S$>i{7)9pH3Z>dA9DK&9iO9KYM!~ zKYFL5H_J|KWgTzegxinY*==o7UYk8!rCYxy%i!3qvS;tl3aighf9kx+Fvh1$E3MMw s@Vwlr%@8-pGKWnCshzTJ7L8rw0(!5w3Jj3Rf!2fM9 zMiwRaBZ$7pk*6lx36dYgQ_@}onvEtj9L|95jfFI`d!YR!K#I&f(0}hEAr3H~*^4_y zfm;JHVSqd;C!!1sVnr;Wy)_kjiUu`8@A1Cqvjb%UPZ_vT;oD{aLipNQsN_pvVT;e` z8w20?$T9<DWA-H2F&?L0R!3sDraaviEtE$bs&QDVwHDaHww6Ewo>TjsN+jFfQ~H!jC|dMUc^ppYX1?O#{{H58ICyGJXzNu!1F;-8WlRuF zGEPKXM92|j!}yBPwbg-qU-It<09H^qAf^ET01yiRodGH;04e}Ji8z=@CMlw&IM&<% zpb-gd0C*Gj?=VW6_dHsz-Cd$@FTUchtdHhl(FWGq+D9k0nOZhc|COObRkBmLEm>Mc zq!;#^P}g{dHh#c3R_Q#v-|ShfIe-BE@58x+t@x%C6h*dIVRc?(-YE%jle?3e$bH`= zKv+rWhol^Bnmv#9?^ZZ{yRgbWi1HH<~OBU3Py|LPBkSs4gQ{Ll&}E*FoJ zX|McVXd*`D3sk59WEL{Bp2J7(eIL{5a&6(YB97ia;WdfsL1%@WkfURBdo=mJqhaJ3 zAB|PCSd<-5EfN81|kc?&|@N?HK!oc4K5WK;O0X$>4|u8SUlPZSQH?;IX_tts@JSN)AQn%cs`BY#z~TgSh-!1Pe<7b2g(P3fS)x3 mS^H^720O#>^&x<`en)~44icMMs(6#t<7L1u7ji=RkN^OlAX;_+ literal 0 HcmV?d00001 diff --git a/assets/images/common/collect_empty.png b/assets/images/common/collect_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..fb5f00c50c374931a32d304f17d02fc8a416bdd6 GIT binary patch literal 564 zcmeAS@N?(olHy`uVBq!ia0vp^wm@vd!3-oHJ#eZ6aRPioT!Hj|a={JH3}K*t;gTS~ zU9%$(kdi$JC{9pBzXO*+e*k?Q3N_${hv!(Cwr$-MOUMsa{)`^Cm zUUXi2zUJGw)yH=VHJi-i*{OR@wC|vfW5JFe${)QZyiHzOnWeCT@s|41lV#T6 zERFQhiTR!;zGh;!oX7W;HxHHjmKC%#3Zza8nI#uiwEyV$hO(XAwy$<9jj`4(pJsZ4 zYq71c?4{nVCppam4PR3xKHt^4 ztYq86oEM_+4@$n(+Pfk2hMPrq^V)*}O`Pkajw{6++2~K9@V(@U6W=Hky&bRCb#0!McvD4xx(hjE$s|;_fEUT zA%T!+OxZkt951Nj(iS_nfE?wQks<2l85T&KgSdcY!*M-C{dW&a!l(& z>(<}8oOaK$=lNV~ds4kVct4xQiM}@7j|W2U%wKzAZ{X?h`WXAAtV+?Fwb$zdqmRMU L)z4*}Q$iB}Av7Qq literal 0 HcmV?d00001 diff --git a/assets/images/common/collect_grey.png b/assets/images/common/collect_grey.png new file mode 100644 index 0000000000000000000000000000000000000000..55cf83594f0817ebcbf4e4e9fd0ba1e22ca56905 GIT binary patch literal 371 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sW&u7Su0VRmj2Y9XPlu2Y282C- z{(J~`=FFKu5g>pwB9#t@0d)$N1o;Is+*eqT;P8HbgFu17`3V8<*W126Z3~pV?djqe z;&J@#RByh+3Op{^2Lw)(zpKBwEwIIj^Vq~)XKttIyh)UaJwzG_YIX6_e(RWbP0l+XkKUMHl4 literal 0 HcmV?d00001 diff --git a/assets/images/common/collect_path.png b/assets/images/common/collect_path.png new file mode 100644 index 0000000000000000000000000000000000000000..79449610278bb6b464db9f6965ce5526da65a817 GIT binary patch literal 633 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~Wq?nJE0F$A7T~fn%?D~0Dhcun zX1K5LzCmDrfKSGO{y9ec(5&}o&(tAc}9Uf#$F z*rs{y#S5Lp*sb@u)SWKRcpo47ddnO(gTGB{tgE)>oxNhY*YNaRky&%+9iRAT-}4gt z{*&*2`{}FfdA_~O)^E#~C1>k)dn*;1G~5)~S;^(Sz9+DlYm?)HuDgDfRrgqDpIi0O z_JLTSp21NSiTar3>^hDAj^0X%o6b=>$IgkRzQW-oU+l&U?=QPY=bgWMWuxitSDj0x z-dDVg{cCgYVuj_;yZ5s9++glv3c7TOg}vYPQ@LT|k;|Uy=1pQ(ri)%zJ1ge(JxuKc z`$t2DUB9<{pP|jaC)>vC$X)@Be@gYy?GhQc)1UJDuANw9Cs5{cCsU|3<$m|4FGU;w zZ9J{an^LB{Ts5 zELQh0So$zf^(0|L#5SY$-qNVONSnjHl zz>rjf!!XxAkO6~&-I@0=n6tYJHo~64osuyit3q-J^uc}=<`fZK;_-T1c#}~xf%oEB zo9WK`o^ii#i8rgYRrbyb$D8`v!r0VJghSL7gbvJGNbRAHP3W+l6_gnh!k!V04w^9` zq^(JT5wzl+o9$P@VYsw)=gK%m6lSDHr0vh2(lD|{Y$!FoD;1JfU5o?mUxT-tt-4(P ZjSpWIec#FQa=`!q002ovPDHLkV1k>|+gbns literal 0 HcmV?d00001 diff --git a/assets/images/common/collect_white.png b/assets/images/common/collect_white.png new file mode 100644 index 0000000000000000000000000000000000000000..3669888327ab3f6d6aa6928e35548b6e10d5b7f5 GIT binary patch literal 615 zcmeAS@N?(olHy`uVBq!ia0vp^IUvlz3?z5#SpFYKnFaWSxB}__q=S#i$Nm6y3YG-< z1v4nTzrVjB!9iex!TErK1?%TWTn@O)z`%Ik)5S3)O!juY#z(zz1(=+c+qm_6&?#k zT6-gM}FS62P)CwtFO#%wv~YnwZ+HA@ywX!X0K`ta2&u5~W6x_3OvJolJO`@mF( z;>7}PH+81$*0P9^vSEFdU$JZ@r~cW}UyJKJCVj|U996JhIy33by=wE@t^ekIK7Z!Y z`q}?(oNLo9)t7Ah7Z+bW?ceIP`;EW6US`i1Yrwm=?ylAz9&T1vDfh<1&Fa$gWXv2V zYo55UBz~Lw-&3y^Mrz(z&?(}Q;l{;e5*6#{+<7ZZNL{QWrK0GD%C@O0-MOlsNztBF z8`rALE{(2SxctT7amk=ZWE z^KZ$A=!30^hCdYK_tpmi|d8+o?gA&zq0v^?!1Ni8MBsU&a!_ezv9!s VDW9fp{s)YA22WQ%mvv4FO#tzuJ^cUx literal 0 HcmV?d00001 diff --git a/assets/images/common/collect_yellow.png b/assets/images/common/collect_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..4337c2b00a3eaba7f54d147746955c69640a12da GIT binary patch literal 1308 zcmV+%1>^dOP)v-(lo|-33W_EtP&py9-Gdlr}8DG&Y?wc6{TL@51zH5tXa%7bsCmvoQToTS4gF&TkCPvum_af|O_B(7h%zE&=s- zfZCf}u1cF&z7#11n6B5?5xc_kp8;o9fwP|iFRsU3nsnpT7D|x9z_QRF7Cincu=p%n zdtXxZ1>n@%(ow04bgfoHK`CzGc%iw+f#o-VfI)*j$$if2?~0ocYB=TaCvbpJ(s{nRyIl3oj51%A!79D&hj}F`#lna+R{AW8g-B<9bemo}~W2obLdS{?DW8vF;&@7qMT zLm%_sNx)7=;?NkeDAM3Vh)N1PFfe{%W%3hiN9}{8{}5Kc80XO8(A4YoFk)eKFa0RjNN8pstAMHE{yrJO1W01X6u_yT?k)*t^B!a-8jZjtEgK!umn1mlZNhc3st z4N?}{sLDizc;QWm60sjg<%LYWw(=w3&X2r9?g}i0=d&R%H>^cRMRIQ1L2GeRFxl`{ z$$>RV|G)%mS+@0PNkVoa;29>JLmIXnt$@q;JfEuLr<#>p*jaet3A)3KVh8Ma zPu#*dz{@t>EW;W#U@MAx2%RgO^GoWl1!Xzbwd!XF94Vi=i?r7arp3{0~-{ZPE(GWEsh|3Xt~ome2&vi{;TJ?kki^xA9Sr;!VXwRi7J1EKs98P z`PuoCYS!Cza~Y$Lt>>cHPJE$4!G`T+%4|13`p!b}T}40GF7w|2M{*=jlRO4nAGfZ? ScJOfk0000<>_cUM$$H zcKzEy$1luYi*&XdtX;FmvRvz+pj`AmL%C-!K6SGi+KGuxxTSSmqvNp^`{df0r*4X@ z*r`_O@%g3&`!Y+DzBH-pe1&CsXBi(qIcB-BP&MzYc$i)6X6DoFl`(>sWoDX+pAND6 z?(@_ch$?%yyI($4W?iy#o}0UJs*+G(SN~r3@D!7Y9ZPGkb+jbSj@!u<{QmU)?QTch zv#Xvi`hK)RG5uOV-cG+j*NPa8lNV2QeW_uHeV(~crnoq*RG6Y`0wIkm91 zZv6zTqSzFzJ1noUWTtDZ&Z2B|F1oiU<>GEIqC795*N~!v?hF7G9?#aK#maJ~PjNpTOHvQGbL;>3o zc3y3EN*AWhf=|-N4Ox2FvSwFN;^XcX+bJmS)x3UR8HQlO><5M3c{k{q$3G;qgPk zeA3nku$!_y$wQgiZiHy+?dH<_zBY0AJlu<G@wq$kVWdtAOvuTem zCJNY&u=8TGQ@Sv17JQOEI%Mf#%X+$s5+C;;>?mMcWA$-f-&oj_zBaSrH<)VUI&80G zU*G>3Vc`VF8kfuV_vQxL2YTcX_d}=c*#nC8zQdC(wlu5O_NuZYnk@NbC7OIT?5Cfs z3y&WP=99KYfZdc;>g~qPOUx7v$hs%9v9q^j-IraxDuJYKsB}y6i^PcAs!XIdM&kfg z8FZ8#UBO4RnP3E6uJ=^i%sO=)&_Ob~j%}R}Md$fzx$^;xtZZ3T_jM8% oi_4}?lH`WRP*=Zs&^hewgp1z^=k$$k{N&oYh_u2Kk6-&Z0Ll{d`2YX_ literal 0 HcmV?d00001 diff --git a/assets/images/common/ic_nodata.webp b/assets/images/common/ic_nodata.webp new file mode 100644 index 0000000000000000000000000000000000000000..a0a93738cabf1fd00924dc59ca6cec371bfbddac GIT binary patch literal 3286 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 literal 0 HcmV?d00001 diff --git a/assets/images/common/icon_broadcast.png b/assets/images/common/icon_broadcast.png new file mode 100644 index 0000000000000000000000000000000000000000..0cd29e675f8f8f4fd62a437af8ba204256baf425 GIT binary patch literal 1276 zcmVk2kce=RIu3! zfC?}b5Gq)21%wJ#9S|D8jDBPzk4Cl}Y+&Eh_nz}CMHOhaC@`vgIJN#lSM%WUqa80jjpjk`AuZXzDd1KUGew!;}cEY5@ zidOWRh`h?|VpjWqQuu%guw*OT(rfK1a6vNKv3^f5BfJuy6J(t!BO+${e8hCrIFdD} z@Fu6zY=pZzys%_LB^=BOzNY8jO4;Wrz2C4l%5H&a*aJN?UXzSbtNLk^_Ivduy>8+^ z@s57ClDSg9x4z#IEltJ+rjLCaR2}TZ4s4HX!gg=cECP0rfBbt*&q`=+27XNgH-j=; zeC_q`@O1KCZY^OI$haXH+f@5elQu-jT(61by`4?xc`xAzOJw)n2{mJ~y#<8WAf>}C zHr^|98U5|ix0(opgdaR%XJ>4MVI1u2Doa))4{fhh;TdEd?yX}ZUA(28x;W1q%2^Hh zgCya=?XQuBrf2uylm<*GfjuirV=0^_yQU|0O!N`cD_7(|6G-l1+8giTOgBX9&Cn=% zn7ZE*u>hyNc}_=LzY*;RLKRw+40h%4;(%4NTjzKE1P+;_%g=OF75uJTGS zSVyjS351$52+@3j3K`4Jh=xz&&~` zv^k9YFRn13VU1;>S-DQMYkMWxWCtCf!K}BXh0(XPPB?Zl9N?oN936ud{>B-2Oe;JK z9u47WKgQ;pwd#KYVZP5&rvg7_^q4H^IF$&GJBN9ZOSoobJt9k#6A|{#;nY~Tvi%ms zdhCT(>yiJHCe6@Av{AV@MGDVj8Mf>a68(m`T+By%hry+{X;)Jo@251H3YM8(=(d14rCM z7B7*5c;B^%$3Ix}beW4NQ18d8Lm5NcY*OYS2*%fNIU#dJQ;;t06=fWrr6VQ6zB@M% z9|i&8vyn}z{Vdr-@8FDC#dm3!)zgR-t#U-r@X z$fJg{j5rtNLa!Rr#^V-pH5P1~H9zc4emj=#h&vAFgA!x$<0{KSX1y(02jiTa8CYj^ za>v;g(-Edib#7#J))fxnkEiWG+QVcJ-knJ{9LG4(?8LxggmF@+hV$g>hYPwpZ<`sj m7X<|c1qB5K1qB7a8b1Kwp^fLI-=};40000 zO->s@5QPi2voSXy`;lT!06sy&X11|U5PSld6PTPJ!*({n32;sTk!GL1LA;rLR8mdZ)4%VNtSAfIR==+5>YC~rA^-pY0000000000000000000$hXe9RUse+$I+M>a z5q&12iF~%G!RHqdJr!O1WNG}VjLqXtWnBIpTQ)K-|NEPW*8C~Dmj9oJU?S3-h`z{V z>7a<{o`|lCu2p-EKb5iPFT~jg7Vf6#+J;gUOoaMQL>DhC$drbN>RrP1WyOo*(qNIi zhLZ7b_FXtZ3)Yv_g-n)Bp*|#BUsh~I*J4#;p=9hPYQbc(ep+7DAD+0rtiFj@V=j~- zytkf@X>FqkCNAjK;baZDBBCR4!LDJkmPOazMI)HFz!l}qvglg2ZmS&H{=&Yjc(2M` zEM_Hh5w3;Fp5Z*y4V-z=wKu^DCIQByr#&lV_>eubb=W_!c*c4(IEbc>BBB!;8InVb zQ5nQ~n`4^k2@cNalt5pk5~kgOtZFWXG~*KpJ;k; zwqER?0kz2=8wn=WKiU;QnOc|fotiRQF_}@Deu1%IurhbnWi$D^K*YHFDlb{UieR#G z;2Pg%XO{zC7y2GhP4%iPWDgYN^Dwx^=UDeVRt1A={Evn077*5-ILpoXAGW_Y(tT?k zONqc$=GpxFqHC?(xO5_z&ithOAZ-+UH=IeuxuM#A=>OV5KBY!E@-j@W@GxZom+pkqe_IUA`?qzN&& zh}yzN+}D;N?^j6(Hpq;u3=Y{!a)LqcSIG!A=;-~Rv9GPiDDZSew1REv%j$ZM_k?+Q zhGmoJ1sk}?%Xh3TS{In4U;`J)qD=?QBiC|qp5I2BQ+*@=00000000000000000000 c06@?A2bPmtxNUw`c>n+a07*qoM6N<$f`2#%rvLx| literal 0 HcmV?d00001 diff --git a/assets/images/common/like_white.png b/assets/images/common/like_white.png new file mode 100644 index 0000000000000000000000000000000000000000..2d557c367270c4e420d522d4c8f6c583be185adc GIT binary patch literal 568 zcmeAS@N?(olHy`uVBq!ia0vp^IUvlz3?z5#SpFYKSqAuoxB}__WC1QK(|n+2p^_lK zU&saPq7Kc9F0k{+Y%+(5`P@Lv`tQiv0&aIpQ)8I%h&|I@5y@^c4^Yb zmhkUWo=Ex^{jfOYer4JE9g_l+-@jn~ujC^8oNv#u#(PhVj&#rG7QBB;cJES`EkD?! zDymnv?8sbr+9vOFNnk^h!}J{-tcAP+>+Hi?zMFO}t^76P z{RGvTb}6p)6H+INneWoo*pqN&-_9EoxsG4oRJHV}OV*{vWm7{cTets^a#j!vGT$7V zedSJ{_%au>JwMAje>sbZ8wJhZ@#e&}C|-T<#-%Oa?rdScsV)Ty>4)Jz`D>U}9sc_E RnE_*w!PC{xWt~$(697p09qa%A literal 0 HcmV?d00001 diff --git a/assets/images/common/lucky_lottery.webp b/assets/images/common/lucky_lottery.webp new file mode 100644 index 0000000000000000000000000000000000000000..cf9423bf723c653f4fc49589f3caa3cca4f4a840 GIT binary patch literal 105222 zcmc%QQ;_Ch!>H@GZQGc(ZF_o})3$BfcK5VxYudJL+qU`c`TnogM(tY1>q&C(RHbr| z93;7N=l4$uiSG;$fSS07f~o?SIxGMHK>fEh{Ld;Z_fr<~-|W9bSWZ?#3hjT*{=0`U z001g1qaei(0sw&cA2uhD{R9FV?GTiMuTFE=Nhtuep7GM`jW7syLca_gD_-DfUZE>q!D-$A z6>mNzYL5&`OLm;eUl1ZLejW;YJHd}Pz}iB#VNR0w{RuFeJBYv<_l`OA8x~#P+&NUJ zydQExqWA&6TwquB(CvHZYy*j1_K2E$XL4LHF-&zx(;|2>SAlJ^Ol?r6ExnJ3X*&Q( zQleiCb(}bvPIbH(8?1GLD2NaGEN}ve`s@(4too}&Ia>xBILkT)J18hy$O|xvI`J!j zbhiX=I0(7IQEKIPa9t3_7Rx!ZxA2v%^j>j{F+@WL}w z_gth_mzLXY$tw^w?KE{?`8^0)@J|DEpWwdJzx9AE)i-rmdGc3&B{SDOeLc^5@44;U zOFO_Xf5GQ>f9IDcwJ%CLy>lK#0nI-%gNl;9Ki0KYZWrV(mp+R=H??$E`46yp-lX>Z zY7?d&%$D`~_w93gVIMgKdMyY8@MW%d!itVTYGq?`mt-kt$Ds_3d_{HugjA``A~iyP z-uKB@tE`ErH_2prMj9iDiQ{9|Kvb2g9uD$Jz{m8lK_w_yr}fa31?vbQHhF$!atBkL+{8_9p-< zc?%y|;TGqXjp=qHfT+{eXC#K43GvYLT<@EU^Sw$zlpJVN8`M-06(h%+nfBVY1oy5W z5x8BiLUlF@ui|r0qQDUXdF|PHI~#&+)XS~j#y%fctIb$ z9(wZ6JqrSBFMMx#U$^j;Plj?Vfx}#29$D~t`u+ZIhE+fm4fT@La)gA6rt=$~QuK9? zMwp%J(_*jxS<#Pr2eOP{_=72^BdCbqP;hUIrKGI;rdRpGig*|~SkGM6>Sv2%3-xiT zFMd>*I{HKR`Ozc@aLJOnOj_Z00Ue*QzFEg04B$upo=1OJab_c)<0i=BCH%vxMqMpl5&- z{o?ca8BuHWC&mkYjfRyU39&mw>Q6No1sMy=N@^zTrRQaXX((;7iLG=gbfwE}f2Y>82WktCb6rDz!!~AHF+E*Igyya|F2q zQJI_LCLVeaQZUhk z%U2;w(E%m1kz$ZrUkZh?>BtsXlhY|B3Ie5{o9<1YE`3jC?y2$ORs^V4EKFy1#Sk0xAQ+SW+{U>MVdI=oI79uRw41$T^>ags$i_E1a(p0 zqt^#j-kLVfL@@Y&@kg`h(O0~W3TC{ZqWWOhvo%_8g&o+*BLH@Gk$0XII`BXB+HKLd z4LGT6jq=WCajVRD7{JQ-T-CK+yQSc^7+xO2qy4NEU#qI15jG{v+HI#cZ_7}b{Lk6GVkA7!SyuYQ$z22v;EDTVbH)kFFVPQujDRQ+Lm5yv4A^F11G zzw4*fqsI4>zOl~->U9I1;(5fMjdpi}`a7$7<2CnEYN#onBG^lJ9)8zH%RD9GU=@Ic zAvnzp4Q6dUsS}Q4ofx(M$Vq+hR2dipPl!0RgB|Ag1(2nNU4W^MjC3*`^{ ziWAO6)ThXnExP!HH`XN)w#3ypWhe$ST%FIkeiwm;fG5TLh}W6R#*QLNNl<^#>Su>> zn`A(2)m>d}kvTI76uVE*rWFXS=F)!1^sn%?uXe>7;>uWqiPYEnrM`R5L7K;!AB-^N zNy*CHAYtPIhOra7cwyO+KG)Kajv@G6>g=PPv-KcjU*>*=s14xKtgzm=wLn}|k@Y`1 zQpx6LW@;uD3-P4!oY-`da=j9DZehKc*VRu}fs{3Z{FbB$dpBE>JcV;TI!f4hBJ&$B zO_foFJ#Zn0pWxeZ~~3PzCZH2 z5c0Cy5n^&%%r;4Y-P-K_A+8q-m4zhBP2)g(PpeSy?DF@U9eA(L&`@OPXigfDJgsSi zYzR5yFPXskB-d}A5#YCY&scDT!d~hvKeJK)(RZM=zH;rKdbdt!*vzJ5xa+@|N(bML zq-}gTC#d!Yl0f+R}jEC-k5@$q&#Fh1wcvJJfoilSrVu>W%1`X z#Lb3J;WNi{_qXmjXA%kzi|3;^OpdkF)2)v`QW z_AELw*RN3iK5TuF??fwWBKSI0V(7p5R}v-YSgi_AOZgkwdcM^-GKHnjY2=^_x$GYc z$QOu1ZhcLy-+uDpkGlw!`og<)M`snePiJI-WKDg%p*99~t$z2EHlCY6L-Xh1YQM_r zA*T3(*q%$s>FGbB+#+1)jNUxDOUVj$Ocf#rXoNpaWp5fi@)SHIk9Pzp0$A7^VS5D9 zJ2-c0@SSV5a-s?HoKVxc?W)egOR`@SJzNueR0v|t3kItAJ++GDtDxcU&GdO8*IzB# z(>Ywwp~Z-gzC@4t{p^bqJGr!+rU%&X++;pYB(L$Z64g^Jb`PFAX0hT42Jjx2BVXvS zcD9PB))G3j;5xtib&hw-=U44LR9MeOo}uUNWp8&#by=yv0VFHOspVyyk@7f;@UC;} zmS?$%9e18M6*Jfk3qj8kwvDNjShW2WFLOC%xU%Ux#vidI^mvU*Q^^2V5c>Fr(p|_w zSCe+Yb`2?F?#Jkgp`m3R|MPEI>bpY>;-BEfU7h_Zp!DPA$6$`$R%3?5*89m4V!rX~ zwI;3)!*g;^BfP$A%PxO<0^N~HbL15m5N0L7LfC&Dh6kcJsA|Iloyn;V*am(5DMNx^dW?+g$?O+;$HMZ@=@xr}TU`#U@!wjZdnHP-ta)`OaY0TZF zexVXK71_K!z;T+v*vlGl<$-9tr0S?BpzQ|GEX(a?D&#nd7%PC#e=17L z;SQ@G;p1^TX>s)lFu^R_%=Mz2&m-7+9{0LH&4S84@Y-ao;8ABMjuJZ#_NCN?MdP4_ z*L4QZZwZcx4?pb}kSnmiRp>3M!TA$4=R}wx3-FB)hmj|O+_d=+NPOk&_1mAnW!CG8 z)eIepq&OSIH#=kw(&vCf+xq89SAcZtu^$(>Tzin3J>8@EJmNm@(wPpi$Pf(*f1}4@ z3agkQNU>jf3Yu=FSA*c2z(von>YF{J(7rR9an;Q|QG1Ln$M9mxtGl)Z(Mv35+#>wi zgvE+qI3kzoDGeEtjl5t8w0vgtHgRJEb;b@vNYj!vf=0=niV8p1G1`$zLRh6OKiGq$ zRQA>iV6o?9}O96#TQVtmY`0vqFg`)~%Me`tw|1By@pK^Q#*+WyFle zp~Z!;avLm)vM_NisK2;vhDUNQg)^~yg)S(kVJ|dMN`v64!mL331UF{mvB$h?0bA*i z-5SNj3J*O~sqmC5shXnPeuhwSD;_;uUY}~q#w4Y7XJasIx~eQ>xX^Q_c@HPtNx$pr zbsBit#r>F_pH=7((sOB@WrXL2FVN-Mt^pD)*qnWS^eJ#m4S0FkJ%t~XIj^RIewKr# zZS2m4+!;6-yMN^=mo?mb#~ch&$fmXa<*_kYm&qzEZpvRD=p2wXAwd6ZtXCf_J*lU8_C1 z+gJl0ciFi{c5{`{lUq{Ep53Bvn+ihs_l-83k>vOnt(_e!l=r(-RC5FC*mcvMKd566Z z?1h`Jq^z7PRZB~}y5;95?C(;3Te8(mib7l;EB!vh#~9)t)W}i{`6CDVb^^;wB~jE4P*#dU8Nl2G6%&(819X+alN&Fce|-* zXY&)eyn?dg2`w?Y(%q8xh-@@HjRKsptg!0OCNdgb(dMD&V}ua}(jk1NRYi~vy_eT@ z*a)WbQ<&PHAs}O9MLnSQE(G;p(L) zddd@}%E8eP3-p<_QRabgh?!<)#74xZd*h0JK+*bb_XrDIJZ=6+p(gk4wfRW0>^gUC z&%LqUAkkfd%Zylf9AcO>y`_E{jtfdh!=4Zq;D8esHXdLx?2ptC7uM^FQ&1X6NRi|d zhA}Gsh4Vf{Y@?ilfBJYbmFgCNa!t_1DO-Gwm;~qi@@qZTi-YK8Gd+i8U(LCbK)J)p zB#mKn%wVw8N=K2KkUc||9##{gk*Ambd82o7`8;>K7gp1I&I^Bft()qwtN&+J7^TQf>X-Vi5R{EdFg2mriz$eF(t!8#W3tP z+)NLwUwcuCu&-z)ep4J{|EYa)jIyls38h3q6tx(Mnejo1UIilHN8W|DdFF2;sL4cO z?Rls>GK%AP+#2buZo`sb+YiXVNpZlaV>-uHjsJqQOh>49~(HC(logIRqH*@ z{)P&NeuseLz03X(OE>mc`c}X=qm@}y{^_p|sGzNwfD5xagXdU-Iss+{n9+K0G4~My zwrFGT8i(4B%3&0xZaLO!8wPZRwqGutLmT~hEP}jWu7W}<_kJ53vh}J8-gaE}C`uw} znH0hg$Xn8AI0G2Bmw|&0l!$6cj@GETH4&(1gx;xMK!lK*8k0Ip7G|Z)^v3*r)7A+IG3XQ`5AliRC?eG8J(*XbRG@t*Er^&Oc zU>NY4Ns7q;be~9*EzQwzxBXKj7S0vciKHkRM$Hk^r-%o>jn^}yKmK?q)!#^=Tu~Ka zL9iE;F`%ymg5oJ0y1;hH)TvRwl8WyXywWP~l9cy5&^V-7;un)bohjOW?A|lSC}QCj za5&SQi-IkN$BJd2lJk!qe7Jv-5QKTo!C6X3=G!_WTS_9<43rZQ1me!4l>H#^*PKuK z_-L)(%Rc+@PN$^w?yRJy$(%aj1u3|;n9|K(#)#AKjUWr8IlT81VUr+?+L=3zVq{`y znZ?{8K8=*;l(>EEzF_uFx!apz)!YZ1hBNckJiT1ct_)#TDP0o3pzU z^H5H&JudT*7T!H6^WX^ix9<5Ud^j7XCb(mczC*&aEzkvCecxy`ZnFD2HaxGyI&Q2E zr8<7J541fdB#MSzRv1;KT@J9&ue~*b=4^v4jAsIrIRxfx=~EhR=OMY+I92&O#N)SwgpeV zr$4H`i_by6ygto_*SA`%Dxc348-kgIh>@3$z_G}u* z+;a$O@b;2yKZoo>g|=InRv*DUA+STD4V82dE=h2lKwr!H7dbX@EZ{x9Mp|e&xwa!= zFeruEuLnquXj)#K@UnL13KS4LL{I|hG7IRAB8caT+rAYt3Cs>;V3;T&TM#{_1%FNI z{P~P?aAiSr{t_H1VBk>^5+))qI1mP{c5sC&YYZuqKkKu^ggO5H<(wW_wzQ(E2_%|p z=ze$2UvW*kc(gGXRhJ5mK%`tC%NM<*%^Y$I$WT!BLI9M7nVjuIentS3VfWfJ>m4Hc z)ZoqO1;9{+-2oKx5)2Q3$DSBA zruSB8YxUr|z+<*O97|`FzyBT{<*buo>UxV-(5NRgHvCO?gulWWq7m)ylh ztn}JR2@ZyRsl~An&Y>Ayd+j4y4Xq$j^Irte38mAJw7mTeGN83j&?2)8z!fp$_s>Ds z)M~o}Bv<Y@>c0n{|EVGHL2>C^fh@a8g2eP)x9Ga!Gm=!xI2JcR_niV0)#o>aBXQFU!Ci z2q#6e(r?GgT;%X>reXl0>`Yj!p5IsHpVE4OA4~4%dM8_wgM#;)$*0^W(S~abgxjq# zM{6rQAQm5!8(&RQS}}gL|K4H(2WtWu!PY)Z3Tqyr@pn})a21s>^*s$CI*gGp_w!Hd zS1Cts1D+b=h;)m8s#p;Ho$(yV{I}$CCsH-T6{nusA$daUmJWUTBl5!XMiRW4BTyWr=zWzVz;|NFZIxxc)L~4G7<* zlrC9v`MlHA^NS4kReF!U9Y4tYNjle8ta%gBk3!`r^&%|thcv)laQ5b;-lxFt!IWM> z62$r$OLh}AjVO00p~Is(&RjsHt@~a=O$p6+gdmzr*3@?6h56sJl^S>yI^&zfj-LuY z84Sf4O9I!I&-3udvCJX@sWm^oFY3=9I{GoKGA$Pb2||Gs{D#G(=<2d9x^|T9ye46K zlZh@{Mo+MNPwp9OFi=kBZOQVU~G7fUs@SdV9! zYRd(I-nX=Nq3Z=UQ1x*mZZeJxm6AUqbdV>a&r?-|O%iATM9dvf>BAb)2M0>pJ=yfs zf&p*r*>cX6_gO%%ky%t*5>I04=YXx>%SYb`XfLJ|Dx8P&**J%Y^$wWDxsdX~3_9{xI>*{h3d)K}I60$Su$@`N&5*kX5`KkV4 zyWq%?uOs+qD6NoFeKTb9)nB^vBjJnkfSg7bgKDQM7H5(7HWG!Ba8` z$X$gR1WVWzdl*Iub~_!?e38^&O!81VQ_p_bH7sU$^6$LJQf24&cjViB^e!5ht<)%qTV=3{+FYm#uF{i{%#c{7Hs$fo2;|YGL;Ya@yJ;gGIe|Ous!x1Myqvb*M}R z(h`;8z{8uHd-s$LDk@(hFHn4flxB0@Vuoi&=8H?;r=J#1$&0ZL;@0j#N+B-0{OyvR z_NDEd#`_Uq__gKM3k2@cJgUXx+wm$k#9_v)U_vhHgU2|+wzt-D;U;1pt+IT#6dlFy zbj^4wmERDyg)?Jxyl3xJ@g{0GYY-RntkAB(VHb~>qK&lKwPEd-TL|~JJ??Z`?0GYj zwq8XZN{`=vDmc&K)){NI>lylIcXyReKKObP1Keg0c4C9I27sq1Y$+m~aDU;0Ognaa zFFLcv>Vj4-p!~UD(P8A8Dg*GG`j_X}ItYKKT6N7A!C!txFz+x2PN7kP2s=D{8k|pt z!Fi51#MVT)ggn`E`;BQqHO`7j|5zl8!vIO*V7*jV?4pzEd%wZK!IpHq5^Q*bvvhMh z=gB^X=fk`7oX`&C4Ts$O-j7uZJOn9lSOB2GcYwGyfh0ll-qm{QHcCw8c09@Qvy35#akKiaDG4mKK#4$n5BDLy`jyf7v06o2xDoqw=w1t^xbL5%0BY z9RdGe_j7G1YaW*thycQw091{EfSR|0 zeIm#~;8DZFWMF)(0kh3hdpIsv{tPiXa~$&nF5diJxr785c=sgHrK?{1ALkm8v^K1O z+bk!oW~3$f6**N7Tpk0Q?8dOO+6U@lN*L+%0u$bdFKgN;ZrH%a-Qf= zZqFmA826JtjcrO~NjK#h!Zvj&g=v34u^T-8K*2Dx&y=(GCrwSh3#ukS0DpcM#HnZR zKDj81>$td5#$T}@5%Ivv3}blP<;6rg!EI%8x0E^24}bB3qU;dGdF~pvyZvDT+!q=@ zLI{TKH3IiUtQDjxLdGGn-=4w}e+yN}e5a#$?e-N`p^H7XrIu?XjC2&x#P8AMy4k^-%`mq zHmRCpMS(zv9b_WQ)jloZfput{S-RnsZZn-9wBmuxyGxKWuUg+`U$282aUMR#%|Yh= zqCojJ!%fC&(SGyKQqx1Q{-b^vsn%Y}m(iN08*?1}sQUtRMbfI;L-}p=rXtNK!s~>_ z2f^~?JD4DEFp*#S!1=!WKb0DyOsrQI zdep&=;!!ewIvV@kjl>hZd^TT#9Tzu+60i5>5`+c^bM&g>ON_3^$!46)n#TlNIu)8@ z=@`s$-h^;!Q5)RhVy!7UI?L|k{6e@;cqX<%v;BDTwP^a=+Ubsl_UCi5Cs}6mp!j!} z`g+fDP2ugkiA2<;j)U~RQ-8AcAgC%Vm#=CfX=v@}*|WF#BHAwbKuy|OIfC{D%a7!J z4=a&plQtaPJ2znNE&%tt;J)Y3@%rfkC0?-_U0X8zo+7TM#p`RBig4<02LnvFrn=LE zC!04E(AJc3jk8VFvb;Y>zBQnrycPdZo__BB17smXYNq9zp=E6+ow+x`>EMbGs&Y%l z{_CEjwVUN^s4gzXnm!O#coq4EfYcwZOhBrP$v&^Wr6H@-t5YaJ{1p_dM%GlFho4=q zELgY7XB~=XMOGAQoE~eyK;0|{(Q?i6w9NS8eV^(GSk&2w(%AEB$SKAybFeY5dfChE zXlwBkL{iuM0+I8j@a-Fqy!c^Fd0%U+Gz}x*Yxc`ivs<*Lm0pS(3$AKpsi+)bLRG>L z=}0g7iWa1R3Y2$At#D(}gt9@3ep7h1i`NKkT!KHQHf!sI!XTj+3lywQN%ZhlNAN*$ zTBrrOaeGU@t&mh~D0;uf@>ja6Np(Ewo-6~bDgr)_aE=aeH=C8U=XvoLc2k|gTZ7jCRSU|>OD-2J74MAvp%?iaY8$z*mKP{E6s1d&7eWQ6R{=MH^h z3%tCeJg5+Pf#GEySW^`6qf1gw${&J5reTcg76ok;C^lTlXNO3uDa*e^>Qof0_AW zm^p^c!+vkHlM!!T8Vk5lm$&(q@KH6l^mdlK90*Fiqb7pMn+x&o1U9M4kXi=;J`J1`7Mt z! zjh4Q>GTEcZ-mSD8BB52*=V)|@T~B|yt^n+Dk0r9U5gpO$5$~G_aZqIpCY-Uz@K>1S zp+2y*khvrXkAUU}Hu*_ovlF;#ba=W)%%!e`*5T z?01shMn9XY>c(^Vgf%Y`AFt^Ob@hv@Y47=J-+#9!n!+HScqK3(lqQ&{a`?O81GW?- zfeYYzU(5z(zg5u^?go&mi1Fkre5CyWxQwIXKf|5YPIbe59@P%~nVhinh=m?#U#u_ZN)hOwIZf=SAz;1%72f;X@nqX5)wd z-)=YmuhjbQ_}9VJ0RaCxxc_mxp#Qj?{b6$q*&8r-xxAzjb%cF`gp@)^m%B`gIs<$N zE$__y-~IU;%)k*ddOBbYhv^b&VVmh1?xE!GO*qOGQxO)=#T^M#KUr&@2mjw*K5Tu` zb!N1wv`E!S@gH>6adQxfK6wxi4nN6+xLNYBsTCsese;+ZQ%T?=;~`f>2+@gAYJz3B z#Avjk+U)&}TVd<>NTtGlX4#VL{vzI|B|d~BK33A~4ss81!})pV`h&LF+>5~Gru3*I zrt#I2m=U&mCgwFn!VphT(DSor3<#vBpE z?uaTmk`~s{zk(jFlp9)&-X$hD8gAqtv1|o+F=t|h?X#VbY)*`_a!L^DvWWk!=@^g! zfJe#y>RtW6n(m+8ffnMA|5xvDN8w%6k%|0?@`z?pC{nUTH3vq4c3<<)HT6BgpEh5a z&t@eKfamnj9SyxhA9>%3XTX>GSIC9nN9{S>^UZVOR_-^{zO*1iAhym9QdI$OZW*TBH@` z(|qqNzU`6eB=M6T9qas^dG^9Butg_DwuB9{4lod4E1`YW{V`Wtu<+qEZI&rW_OU0o zLbI}vob41}EI)bhHjUBSPvT5+2jy;Cg3_|fR%^=kp2QHTc2oQeB`24O9THCg!&<{& z9BL$|FTWAeG^`r=5FBIU5EM_1rtp4XGvst(qJVr|+KL z<4SaN_nd@EOt@V512Edpt}Uv{@zJr2(IA?-G*B{Jklfh{p@9@4`}!Q#;bRQ1e41TH z8{g`+4oW2aF_`;J*CLZuWX%W6R(ACavE`Kw)7lgO2m%B}#qM^X+Tb?FLN|zsRf-ZE(Vo1nx!R(_&$io!@s=_#cHl-8)zFshaeV zfo3+AZhy{$UdD8l=`F!A%!mk?^f9o~ianAVTpQ(&t4yK5jw{o06(QkL{c~mVuKDd8 zmD02k|KZ$8APiB0?;u(+7hCbytvPN*b$aSsDG9-~khFPGro+O8`swlI zE+Vl6i5AJP_)^}~5}&=gEkBp?BGYQowZe+4_9iEJ`L{?6vE8B`j2a8 z`0)*B&^9RR74OoTstT%d9`r!(Cn@4jI_h<_d)wfg2Ry6iR5l#GOol*F#!qBC=vT%l(L>0N+Vt&Jk7In zX=NT2IaXKBQxi)QG!qz(QCVGg69)~DD>KAdG(J<9(JzuFk3d8xY zzNmLZeiIIgv)KqCoDRMi?ATJFGtWQm&DK9uP|KXUmgl;bO>i|uI zjZ?nWoWu*7TxSX6{=qhyx_b{N!z?#>N2wGs7 zdp+Swa2!NF)dv~Wo#wW&5`?-F-)F<-Ok1J>v>RXfb`{ww1BH>oeqYz&=ap~#t3re} zirT4CtpMc9s&da6wC*2^3gx!kE*HBe>u_!+P_H&=>~1#0z(K1fYX^~IG%@f4AtF}O z5jUFo)I->me}ooF>;wVvL=&T_FLDAz&H)F2d&{U3vCPVCOf0XHVQPs{h8jsh>8&Q6 zftY$*zZzHGiR^L;MZ>A?FZo|yj&mpt^n75hYmaYPCqA%x@#p5NDX7|Vk-HW z3Zx4WO+v?H4R~NT{k0&8A>0mlf?P***d)!%T z+pFY&fgenJYnhUE&XrmbF4Q|ga^vrhQaM%ei^gl2ih>q;%MZ-V2wM4u_l^alR0^R& zW9?;_K#hEqMA$+#vs&w=>Krv+d{bxUBCt5VV<-AUM)P3CcQciDoz=lj7%nb?6LhMc zhHxG#T}$R(KQv+={dCGFGIY&BR&VW8k)d976BO@?9s>MT`!?BO$M2;)9)_rH9s5!+ zRuZ&1JPO2t_s$ir)$peOf%aZe_MFJWEKcxJXqP$oo8+} z@fzbpHMZ5SlYECkDc@2KBB5SD&iMMsa#&~Vj2q{tV#MPz=aQMAYz(TsiSyXJnfY(8%( zuDFx{*#ua*wPwj|0gNrbbzkux{vT)FD+xByg?AU1|Pxmb06mo)yI*ON(EXA%c;8G+gI|^DcMbKrozmVC9sJZ zq3DlUSF>**sXMJ%DP~ZdV=hv4?#@9>$Fz(Dgj~)ik1#`$m%K&w{8`Z31(lxSFWcYq zoBk(!&2(@bpUJz3l)e}s!q&oCLn37XMgtxASvVL*2=;FDjdgB9enK>ybr$t}V!N3y z!T$4e$lr((s&JE#$LGx6gKsbN+4VxbqDb2Qh1I#bp(8Sdg$mncbM}dh{t@36Mr2mP z)*;iC?aOHVVJtGbixlvx7 zFzjtaP568P0}-tJLr2Z_{_}H{4%Uwt|mvJA+mnIvD9_B z{syncmYskKQuv7q(8-doDvlp$I$k>FR#}bYusQrX4&$$=XV#GGo(Pe!`g^`2rS#c< zcz|=n{J49F9lnordCy{#@~aMR%y+q_rJ)$vxFnHN9%ri0C`TbH=$AYig+`|5iel9tiON^ zrv;Kt>%WuyaH}P4mwDa2B_0NeLhjV|i9u|R-pD5BSIsX$eLODFTO3hY^Li#zc44!U zaIf9Z1kbQoJGh~u`7-oN=9jgFOm2CN%f#)BNL&?l9<0pZ*Z9pk2n3DoAJtCFP(6k_ zkfo?6UigQ3a3b^A_jh*D8~fqTYJU{&4cxhDDtrLE%i}f>Xt8(SooSokEl-1rO|ar} z30zt*7f=y=Q>=}Q$bMziPc3=@k&LsCf2HW|hEMsX*^Y}NJj6Z-N-6q}N-rF34adr! znB%%uMA%u*$&EY^iHm*MVPZ{((RgAB2BN+`Ej4GS_Sgkq@0iJQHZ=B?b0UOFWCU?H zT5_a~L?d}EvTZlNRV8sl^q-_Nw$FjB2c$vM$3J9PP5yTIx9`X-2MXCKsf$I}RF4#h zz9V1f=h$BE6mK8J7g`X~-XjkRyghK7D0qCt4$8_zHHB=Xrw2bUJwfEG&J`Xgtht|( zZ_R0=9wA}%9w=OU3R zt#V4Uz2`FR-j32A_bXON^5xkeG63rQDw4UU%ll?CV%EtuOS&2{Y^`z6y1gMn922l_ z)hf%e2Bj&Mx=raBy9ZMx%u;QYzhdf4MHny(;g*Vm%te*$$)y=txD9j%-niuKv`{0o_6M+8gST~8N_r$V?Ci_B!GdL0>B?1NqCZXY?M>baR^#>XS( z`EyOjFRbmcMtQ`1C^v%mUun4yrWQGQ%pnEduQqA}aQqkHl!C!(p`Y75D)k(+S0dUu_=X@7zKks3cSZaQ%m1_-~|BU@y$SpAmG#xm01 zWU9CB!2kf!_yT9~0l>N4W7AZC&5GYE!A)G7K%?slwT=uO5R0ciMCV>}2~A>RfxHmF zKCE0iL;J~SA-!Gv4X=b!XZiW}e=z4iMV9LSrpS`{pCW5h;?EE2Pd6St$`Tq@D|yoY z^M5$Hl)Rj(lvL>1(rn<``977=`*UC&{Ekorj$GIsO- zH@r&4lf9!k*sA?cbLBh;V;=rv7RfA+08yATE6~)v9&1{dmTwD&G9QKFxlhA9s9cAs z1%~3K_YyO08{`3RSL)Eng5#OO$ce$V)X+47IAH*9H>)W1xZ8ns=a` z_L7&h0MC$zqyp|9m-vC0ZrT*)y$jAO8H2wDU{*MGb z048Aptbj7H@1E+35O=?8q85QA_K~aHWxwA*%dd(z$z47-uY~WDmmZ7U(=Vy-LSWEW z*e=|YQPIq`SsBvW%toA>;M-1vz@=cNPp@|bPzHzt1g`v8eVOHbVO%hJ@!I|P5d5;+ z+Oa|ce)kf6`3Q08_e{9Hd?%K8Yn~k9!w2~CZl3}C)LPpY3fbl9awdiJ-7ry`w4roV z9Z#LDTkN$dMmqqQzjC9dIAYqjWDe93$S^1V^dsDIp75~=!b7b#8WjH(^E zL+9IDVlSCvd@BURhh_zk$L~qP$5{%Rv)n4h2UFEe@=<@UzVGO=H%R7Zyo*R143>8QWgEPz zN|l_zRdp;K7-M<7wi4j+5lY!C87XASZA@FC6WSe;f=NA%NMW`fB;UHb?K=D1a1Kh5 z(8tePNunhVC3K15#eyUjLSUoFly5ekVIeLG*bw)A7DZPql+5x9h#3e;GL~P%tC`p9IGXB^bAohGtyP;lQ`-D1?zhRk|1&pihOf( zn-YS)g@nv1st!%9?G)es$8h-fqXobF3HvTv2Nbdo+x%&6hbq;Rf*JnCv_uI-F{n?D z%v|c1GPC>&fqU$Q7`u6a5!_2mtI(+BM8k%y@yYm3sQb`V$k_hWxB4rQ`78oWoO+sD z^6bGqgGnaT?951+49j7W9QBQ}r-I(7zHqu82u*jX z8*Z^d!F~)uJN))Mxb}Ex!pq{hNW?fowZ)H-g@q7raJQj*2g)TjYk09Xi z6q1W!p_+C^)_Q$of0$Br{CwujA!3JlV3HxH2eTmZbPxPJ_hTjWt1p!)g-TBpKTFB7 z^wV8@WEdFKbUAt%a7jz^K4Vq}{E(PS=cQh;j?1muen^l~W3j^_JGB0JJ*@{NPthM< z3W9Y0K-pO!&#zsdtX_4$?CuiyqrK`)Pb~TlmLbXRdJUu*q==@CUVM2GLhn=x zb-fp}4GEP-#W45{$nG(-`S34iNh>>>Q|wC4az{tvwx5X0w?WDk0gRjO_QnM@db(>! z>E`=G3WqeM3=cwn8D|_oPh^mbIum|``n3*J);8&YvioPg#x9rZrA_o}<+ovo+rW=i zjU-#t2s$5lVz6_tp+eo~U71w=_@8Ron#qbCb(Lq}+t|WuuN%ddNx&y+qXpCbY(3AK zP4ZDro)AVjaUDu8K97RbA-hATB}Y3Y-ndiIJkxU^LCDveoB{C4c>Ta!WL3u_B9St4jV%O0`Lr%R;s~2iy?J7Q1DH<60HwhI^>cv~kg7t(Goe`rgrFA>^R1EBA$MJB*W-FK^q$NJi zwJWnh?-hP!uT@mf?ky7yd9>|a1C2T_d!9)0^%=~w#1gY=d3Za_n##NYTi&wujJHwB zNA%7c+sVm$rEn^J1V9pJ8^ckNz5rt#E*2K6=)$F=j6r4r!&`AU$T8>C1ahV@i^)(< z*c$t4ZfZ36)}c?tL26p{mZJ+kKc8sXaoX^e$1O-*H?e#1A%LLEZlzjq1S<9t97XDs zbvw&0Pr6Y)!V6tTs?lb9@*7<4>@$T#E#{N}s`6iJ<-o$64-zg5KscfBHb#y;%Hyt+ z`jxqW!$j~jUc;$9c_7#%r8gROs=Kb!cfaaUd6R8(Uvqd+z?VwXRMp=SP78fq%T+oQ zyyU?Aq=g6qEbIG+NTZOwA!6E2{JgVv^13eySp4qKE?|XZZPCqbP%DTu2D^ISzFV3I z3vXDhGk3nppG10scK2x8_?Q&NC%SDoI(gwG%)ekQEQgzdgFkv?vj!Z@`utnV3|S$8 zdYyG9O@Y42BCZ&LCl>6O(Jr>lZD!e9U0SwzOhUcY?T1k@KJmteve*30li`m<{S>N% z97a0jZ|(9BmJJB|%9x*AFZk>ZFzDdLd^lTJpd_+|a#T!QjpifEOQ!S=V{SSz68<#? zd1f=@RBDnA+XIbcx!OlV6=v*bU4~2t?>J@)eL|@g<4ElFGZA?MjSA1CIIEM%oD1I!@?Er{z=jO`3 zG=Efi%kcEFgBNTC{#Y>t$H!;~W?IGr5}`YR)lWkT7g0W?4(+F$){6M?H#XtCW}!I; z!iIYUCh&ZZrXzEQaHD1Ez3@{VL*?(GSAs}VxNV8BgX73a6-RwD>_(T9I~#-1Sf}&O zS^xN#f|x@`l@hG45cb%CJ%wv&3s`syuhHN35_*(iF;kxA1Jvo>x)hzg7pHjh%(|!G z3?^WktIO3iJ>E^LILpK0a<~Yeg}H@@HKz^fJLjD!`>$sW;cxvc2gW@+3O&eW^ie;l8{=TB>MVoR0FO^%G?(20Qr~+h}$cxpclV)WmcC9F1Y@~ zyz=7&2Q60$PQT6gA>00^nWj7JIz$L zuBy@*>V}T#*K&(YWP%|5-nhsy#obF*;~4ylyno@g3!X~>FgtIj9}ZC~-{b1<7T@e)Tt)5>k;ocA?r%zapNsme)F425Yq;7kh-pbegZFox#&||E#G1h! z(!}d787TAiy3qbskoA^R?u|h20$M#fB@tg%0t2P%bl>CS{|X>abSsY@tPCw=tL7D- z2cq9-H2;jCtQ5u&7r=*L-G-gCpNvnn&TXp{DlSbjU$N8K8(#GHyv_FQ+nWkFme2wH z;^C~fQTMyKSycUMYX|yzWib@YCnVG#TeP1GAMDLPlMP&mOH(_fO{4VWIC;7rqh!=O z;_NwVDaYV>2(nJ{E#TExZL5`ZNYuQL6oGgip(u7VczFRc-NJxROn{MS-=~TshJ}<` zmQz?R#W8u*J^hQ1wc?^Djrwcq39SBjGWq#fRYWtx@r{^4v|^4@2d;wxqAqBAVv2~{ z^msAJNzGAhd*X|T^)W~)EFtSWNR3{JPBLo0XtKTBHFcpHbDL)FYgkN11TOdqqSr2p zd{#sPL+LfS_ly4nAE8-9G2@`@EDP3Bumvs?LCF$zKADwU_#?D+bsCK&z-soJ`JJuM zU?ZmR2NJ_4SpQq(5JmQJ#)n2lhW2bL59)G8t_Z%E5uQ8sBM-+^>#ddu?O6MdP_-G0 z0K?&@uS9m|FYt|_M~GTfuh+3`nkz5Ae(;Nc{;+ysxF1?0gH)g?dX*!0o1%yyfA~iBYzEjy|L|pYs>j{G z?-9dem%l20J6&fA)QqTSXI>QR-})x8-cJpRc3kfPPMDluR}I7R({RTXP{-^#eZ5Cj z&E-^aeQd){qplhh6Dq1CgRDs2d2b?Re3q}4tTT2e>!Xi4HweLqkC9NN;v%CZpPeE3 z4O@hw3Pun;!fX^-xz|IMD}eK$`B*l2e4+1YTqaPouQGVpkT)(ZW_|tV6b=w``V^$Y zCkJq2K%r$i^TZJqX!7y(u1j-qmwU9qoGhd8@OqOXG50jL!IYG@(o%AUmmaKv zJFs<_FBGYn4mL!LfQatcpj&qS`8_~!gu*Am)(EBEk>9L925-FU)DX>7tY3Q{TZ}bx zmv+iJ2IOuj;q%x0=gCYDQSlB}53$bA>DQlfMiVfl1g`L&3PksfH5&m6sW{)`00zh3 zN7AcjrE_}q6oai=fGq-lzzq>TKq6mKuYb;xFz5JR!4DVh*)y?7W-l8MrC1J}S&+9h+CJ9=gLTSX2{cy@bN@@2Su9e~J% zKnOw3Y*i&R(cB&~)wcS1Dm58XSPK}r`5-6|&>KP;F-ZBs>bi65mCyrtFQbiF0L2SEntH#Fnqj@HyP_We6unTlj6=M1hI zGc30?$Z*SJmN~Yr1Unx$D+x#g5wEiT+>TEI|?`Zdl4Wot+>G;TzkwV%{&x_bhuqE zuReEu{U!Z5nNy$q!^ioe*{;NxDh@C>xIn4>6EeTk6&PbxJX+s;Z~;q4ki5b4V=7GZ z14}P8ula7K(MQ*AI~t%}5uhKCmD`z~qu3tyQLN-!wEtq0JlmT;x5avg>S5rg=4mnt zT{E7%i%7S9hIb%F>$iSNOqW?XEK)&Qf4=1-X1(1=XGDXD_)9M$Mu;+`_jKigsV4XW zg~+X8uR7z{P3Lf|B^hTwbj2BB_sn%@RK&WVXcA0)_GUzHD*&h|NWv)(iCJ?$kbKg*K%F^ob7((I?sJLG#MrgA^Er0pc9DEs) z*6Hq{mR_q9{Bk^Kt)`T1Qrh`sKIvS4qP$vrXGAc$oEYEysg%mEb+w7gU`Uqhs}=7m zB75l3d{U&C3usJ_cY}044#*~v0Auhq6eYNr*yNB6@1Jn8#&I(>e@r&&^=^c}#+!x3 ze3Smp2%zo#N?sWE8~#rmz++2yKj&IwewW$6UvG88x_v-Ik}boC#4K{b$x9*)aUgpt3UfCjg1OAAI;T5d5Sc2xUGt!(v3G$%r1C2}{@F0#u8U zK$;kMA^~{#cc9XdAQ7!WBdn%E2&)E+r$T_J#;**A2#r;tI>-n&w2)4y16@#HhQJ7Q z!Lfg(sZ*@hJ6e*9QXE6=g+YC`;^9qDPeV~t0hHfUXOCi|x5}F|cz_<(+gFm<*Zuun zy5#hmO8?9{|ZxuS(kEJe-;#$X;nCWsm=&BD=R$1st5qnNj{dJ z+g$KuaL@kTQ7-?KxzL{}#GN5YY8*QyZQu^wn)wHX9l zxD+btTIhjhwx^MomV;E45LQwypgchT5Az6%00gx3`~PGfnUlmp8F;$_{$+Cc;WR(y ziI{r`bN{YAe8GS1sfb^Zm3a@{KG=h!0O#+tmw~!o^DmZfK>v5nYxuYE62V8_n?B$>A~$Hal|SoeOkkz= z!tc^s8vu9#dIH+_2JmdYes+AnzNd*GHDgYzPt;Hb2S>K7Mr?y5{o_*V!#QUBLpCJv zq%g&43<3};^Tjj6Hlh}s)88ayCOoT|`Q>o(LvJ!16?6j2FC6+%Ybd#Z)Zf~#6Lnn& zztMUn6Z)nvrLrzO{%BLFXAVYQ{-C(w1)H8-60mIdEODrpQ0ag1k@K7?K^^$kn{h z8;*NSM7u-_B+Sskx0dg3@~!c!IeUVCH66UyOJ>AnMlqnudR`Tk0A~f{2>jX>h}VO( zCR}I{G_s;g*dOlh_-B+*V8b9h*~76u+|D)j;If340n?Z&0>&3G1rb_Wo|a2rFTp&s zZETGJ8>T~1TG;7fN7jbUG3pDuWw|AeHp5&PvsB;_84~%WJW0t=wg+9GR-?PB#eH4pe!>$NhXaP2+|XfKGyoG9(wK)i5E4 z*y&>L*vfPBlcX*qWKQmejxxx#xd-MmW6xJ^pT7T|n) zQggQXc)RKFl$YHS+Qa%LW_V3xiuTR5`EKaV_T2lESL}yyNb;k6?6EQN=nG1_(lvm* ze$V_~{sH)xscc{7O;4W0H#Cpah?(%DLL6Pl>tt}+Slt|TNnlBpIAmD+e@LZht`V*)BqX7FLp;Ue|Thz{<-lota`Nsq5NUu4IN*M6jD z0G>7GRk_x*A7HC(cPAajV}F!0IY2gDB&?wJHw5H?0J~vxRkI`8jr8 z4Ot;#zklj4;W~xQ2=3unR~H=|v01`u)u3Yun8vh~>i;}bc%!F>26dcWK&Dr3MAiyg z30T1afk<(Krpd$+LOiBE6H~RJD0xAHpTYzh`IrNNL`wA37|{Qxzndp zb6%alV#vxL3Dhyjfw8hfb&!g;4{igV6Tpfc48cxV=ag3 z1JPIVvVv^Nxv=FLHIm;do-*vcb0D4_!z4HN3PIm`yX#gx#=YVd62gD08C%>o=BeWk z2~j{#ztv=M4NI3Vsvgk=F%DLRx7FDy@~ozah&>DAHx>*PhHg0yG*d#6~T+KU)At zi8IE2fX>GP?AAK)i0*&=VYI8rt?l2>YDG+D;DR1)U;kv=0~?J$bQ43Mg86Yziwlsi zA?ip#g?bo%3&c5bjG6E~dwR@xFi7##LO2DXa?q<>^B#|`q>n+2@4zZHva<6Acx$kc z{>xe22Nqmc+Y~~+ zO=~Ooc#1uF?0|#U${u`THVONXUebJ|Dq73aM6s_kEzrf$=tfL&P%lfHkEx+FaUqA; z;Wk4TObSV;wpBR3eoMn%tkYmEb%{@WiGK13Fx0wE<|vh%`3v=Kx3KC!CfgMxO;`q5 zkx%jPWTe(Lu4q@E|8$=vAtBBu80`FZzv{sH19O6%0Q{0M+sGhZc?> zepV;P{Q9DK3nm^F%v?~)BmqoMc9oG=>8ZALzmH}hN(^hV>nB2HEq(s63deKG&<}80 z-Jw9AJVe^pa)DqK>E}PI(Lxo_n|}2OPU~6<&BaI=UW(aF6}2`(4%cA@4Id;T)ZQ&j zROK6XXa(U(FJrCLk6749$G5{}`B0go-z?@}XXaM%@u^`jzCxsj)iwTw(KRc({a`N_ z`CGr5O9hR{+R2Nds^eG!IVk6d@gm5M=+zL5Oe9Y|{K`H>g>aD5QZp=_vRHINSEL=7 zHDFY#+qrb~R)Mn;zlm<0^sb$c=L~|a%UKk~koylJxGuxKhSZFHk4o(Wtra;nR_<>I zMxR*!CldloNEs8q5R03qsV;-8fN%WL4-Z5{h!cY!V7bFh9LVXM;c32sAb7Z3t>nFT z2C3wOJ57R=TanzMSl8y>WxoWaz6NMQ7j=qJ3vUsS$PP?YgCEDvw0&Q$>&=C4*DM=P zizb37lF`U$BZ|`Z@e`-~Fwz-HX$rQ|!==i@Pe_NNC+rroV+!~S$I>rG*R5r#{tJ7~ z8!Kd;5myRQ;ni0q&yXMS*n>-JE3dzx{%-WH)FOL@$&Z+#PJP!(Xu8yD5#sVwv8*-& zH^khu{l=_C0YD{b?%FLj??cYU1*8v$xftOYm|*SA9kN&7d1!&ADQDK;G@`o+lV%A-B$sgVep#*Pn~{Dg;ekU=Um|43ATpL( zQLgb;k+xk;s(7AbC>?nvc4>9cT8qDQ&A-JN|4cjNVl6A-#l1!n|(F03=w-094LR zq!rpKJQ6Vdz21!mc?=zeB(zGG&bO`|P-MlK2K5x9GtafvAWY@*nL#+xFxL)R- zpoC#(OnEfa*=0ZQx!EDQbf5**f8JYXtrI-uE(1ArPmWvM_KY%ZZ-0ZX8$-p@b1(V) z2n7*uE#Mu(S;bV9AI0i@mZwZU=~biOPh5jqoa$r_!i zIx}rk_g+1)XYUU|`0H|FQhA*AXs$;iOK2P5n9oo(aaRV3Ik8ks@Kh5#L(_I>@6AAn zjy>KNEH{>ryW+H@?UIWc9T=D4=4Jmpwnkz~D`|@>5!QOaFPv-h@OayeGe?ANKT9OP zRwfx#dA`tjKH51&LE+&GuL^DW6$T*mALIa;PpLhFb~;lNKSItrBXiBW+SQS7CwPgU z5%*8_gIl`p>wLsa4vl>#vCQPZt5`SEO2rXF`XDyw)fgkFZ~RU5CiM1+8MItELh{T! z`peu7fqc(?J+iq7Fu)+Y`IQUv%PO!{TMwxuH^g0133Rf$xKnc>M!~S`?u$%s^c+YS zv;MeuQo<#Va9z5(kGFv>6qB0|QK&?gkjZQ52Pm#^pHk0)J4taNr6DprB}R+tFl^x+ zTl|(jYx3X!ra&Z*bJGvQvK-?N705Blw+|N_E)v0TI;^Or+nM2>tUJmam`hhWQ#d%! zSOMjXBTg7_Os*=>R_eH4r*;v)c}_mhqX$WCa>HhmU4f%l_094VFkhh3BHrcc?nC41 z8%_+2w;D_76>ltVM=oZP7!KWB_Fj6ND<>Zy!xY^ff*acFGs0 zW8>!>O<7fr+Yz_QSB$2{;vVmj9yp*P4e&o`ut`_q)7HqqGs}w8oS6rcw8*N~ahw_T z*MxZMg*x=!18yKVGGu0rD^ zXLVhuLTZSyzO{1Wx@BLb?v@-eWNl-3^9iUUIflj9FpwCQBnfj*nCT)DGDTK+Nl7yCe5tf>}w19n6p z{1@}$MSi_>SW?l?Um(0M6Z2Ym&_qpqtATcB z+KJFts`uk5UmZ+-n{uqEcKWx%hVT>&xyQ3q&9P5PdU6jGygFDT_y|-rARwB#St~!qyo&Pt!W0jH!lsa`S=QKJ zec%Hx;PXey;v4ZfJsQ~A;Wu{V?dm4DZBYOaNEDa4O@7-irXYssi3A}r$4N=ngWn~j z$>vwH{?CIC0(mWwyER`XG|0Nawb%KchQ=- zlm0&w2lh0H;@;i@DwZql|E%YBdwrl}UV?rB{y;&%Z@?eGU%*KIB1`Q@J_s+pTX|Me zpRcn+dgGmC@d)Pn>rd(cinY@VfC0l&+?s&at}1OZs$o0`_;cgo64=%ZTP#{aBPeC#~!51XpfMwaQuRH~UbRg2Teby7q}R|^A=T;f)) zw)<3|yO-zay&(uKWH6eBLOxa_Tqilm?3DRekXne6$F{6?oSu1tt&w;Rx9(Q!#6No^ zX_Bq+?GS*nd-x%f`51R}UstPwL^gE{CYQD8pqdcW7WhzV&i%y|$Vg(^$fM-L_(9cY zIYZdm)f~@QXh4l0K(?jsHBfSrCp;BOHc6i_NO&&^3A$w&S$KfUSNVIA9NaF)x8AJ& zo@GTDRVARazc#wYcNu$0pbD02j zK)|MQuBhn~L~OQ2a9|U}Z60g1_HOR=ZlzO&|7@swu6p(F)saa; zV{mVCcgWRV&N{%yj2GTeY)yAa0%=O-w<7stro*Oh>DRv~;)rZ?rKuCd2ok^6Hb~zA3C((_<2s!G5U}s4jeN}F#mh9W}HRExV*6T;x(p`;yp5$@MPH*bI zI(XzZLm{AU6HL(tP-OamTY(NNzMY|flD{&ZPUh%Ezjn79*`kb0%v@U&zRU|%lHXL2qH>pV&7^eKH%ZWs-dYrke5Gc6h%`V64LJk)uC?8SAVT&s)Iwm zpI4`Z7UDC(xTCAK1LUfG+;m>Uv_l+|;Ag(oxh`}=aB0O_=*J0rz}7~@cb6ugTpelX zzES_72sEvI;+7YQ3R&{Lrmux03R`-Q|9_r5P)TW0BX(9v#$}mqk#UM-&O;YGVjk4a z=eoD;ShB{Fih+NmUlkaI@mn^zoM_znEKm3A`(rX9+%Kb)k_4@aHP88);oIJ<Svk6TYBZKm`h3ef=W<`_ZnOm3J-O2|7&W`P- z>D(WPvu)R0iXNv*u-DVDcqfnC#VKgj&P&%rK!|i{f6+3r@SpNMIlfQ97wFd!Obka# zDVnb%cm&!QrRerAju%7Y{7^U4ukM6Kr#SK~1#@bd@mTUHZT-E+*K!hoK9w zSE)ejuA6kTwvkmDBXm*eF~OeJr&b9-{Q#Eel87DhF*KpGwM_)yXe|BR(x8m!9rG7S zr8)l%**^?u{b5=nImDl9qrTtR#;meL&d+T-=uQ2*(j|3|ZlsDq;g zlZ9;BOaI$nAxbYMamD1;6-tfR$eZ)G9tv{#u`-mHtFybSei~Y}aZW*o5ObV12bZ}f z8OTNOf`2B;_V9Q^u0Jb9XPBm+H-=_Y#XYfQy&wl~p7ZCKrR@~N6d@|x4ZNxiEejF} zFV$Zx7%n0cmG-2LcJ!^edX*>rB>wmJbR2Vj8oY>wupks?qBLbnjCYuzDT39f4T(#U z#=U}%rNA0CJs0N@cK_C^xdMgRNw>>;a1cHT4BKJtc*pJvjF*J8O&S+a%yJM{5!*tm z3tnx6CAYTt)U7e)Pbs!LggUvk3+?pKZNPGvFheB^UEPRh40zWB4HYTw9t)Os_A1~f zr!Exhj7#I6sSBa5hV;e0jWym>AL7D+_mON#!8jW<`o*!RwQ#-r~U}!-fn@ zJ~NNtU0a6JfSxSu7Sua>Q)xqkz z91Qgl2CN>pWN^9n@*?R!0c9u_eGe&C8m?@!QEKyWg5zNL($KI(W-eqo8h!*dK`cS8 zqTBPNCtu)M(4L-Gk1S>=eP2VTa$`{-_z(&*F!UQ! z8+>jV@-RDlfTOr?ej=qc8~&c8O*2o!E1;&Z>9f3zwGbPXY|1d2)&OpeiWi;EZlOnriW{<_);j3GdeDM;s>oFmslt zreHfrR1m)tp*{@&-k3#;;+CRAM#t<)x!^K5dfeb+B&C&athP);22pW)oW*uO;2y@*8vge zy6?)JSZ0@W?=(DY$EGGf!CKwmSo5hOFYUsGibSGZr7!mLb_~}SPiw zUMo!d$f0m`w%hN&6N{1!1-Y00Nq^Je2S7czXv0^@%Sw7d*shlAgGH|cze|IkX3xVV znF}6)G}y);pG5(7!cg7IXRR>qOdL(Ja%>iYKpO{PzypivrFt=Yzw(>*I;zol(IyKw z5<~S|HpT%Me$~vt(trXpJ72b$Q;)k#KQn5y)V;0>X_Xb~$qmr@f$LL*nNUsEl(_8i zu8J7Q{CU+X{^{jnj0eCRygRuV=VA3kWHJXhdY zLir%zzU^k14ZYDJZ4n7^_iP=Dh^=+D;Yl_SfZ;lxz`_B(hh&7;5;X3!w^L9Pzu)lu;A%6eD(aVVCoVmMbQu+aky+cg^g%Q89Di9NhBTgM+&zQ*f-mUh{%{;znMH zMB{B37;kvv=Z~J@;I5ifh=9=q>S|&gv^@oaN0rwe?MR=FCut@^qRm)E#kpuya9kKk z5H4mu*J(5c60bCYq6I2>ZcV6L&CRroor)XXV=K77RbpXGl{cy*X)^1ZISVFgYu+fX z5@?2^UXKXu2Ee(ks92D$SY4f|1M9Lhj)B_xqYjChKuMUtZT4>o2WO}~+%}rDPccDv z)!|;;el$+%gEJ{~@ZdHDXkTpR;Ocus(gm1Mv904ZWTs=6i zCnOVw0mP{Wbghch0?d>NZ`fc9(=24*UDJDU4&_#h8p0h_gkMm!wOVD}$&R|50pPtg z*J+3kExTPh!bG)OH{U5wYi#5F#pMI2HwVi}jTL^ADu1dtZg}2yldd)+=w-W#_0P*D zt>zwObIRyOqDnYxC#&I=XHYPj%rO7_IyZe_Ua<}-IL&^r)F%Zx>-<)uMQc1Dfq_Ir?eG-ce-jbYTy`z^S7F7C>5bW04oLuY9TQ~@YJ9lol)(?TegaR z&h-=qw&S#*q0Z%j0JgMqbaj+Q|1Qs1tL?5I<>O$NWiDqJCgVmLcW!(zYD#G#9u3H8c<~us|@Qgo9=qJIfMb~eM&rs==P)`&e&=1Aoa{WjiJCN584cg zV%0BUJQcZHY>+&it#7F!SmOd0en@4~d_@=i@Jt1BhBv$aIELby`pVetAaaf}d|lde zmC^Rrj3{s(+LW4@C?G`1ZBqoCmpbWjEC3U($I}BE^0@GI3};qz!(wiZDFgfHZz0rDUJc$i`S=#pMyFx&}*N=8WSnT z0V7c+gEWPxGg}ViM9Xzs+GmJ2Z9#ztr@_V%pO;i z3?Pxm!j27{Vbz{s|7Y5g;t90U-a!`k7ZS8WmcJ2945kCvdm2p_ISUG=pGS1CwiLQ# zLGPanp0b$(f&IA?a+>?Q@j;TxVQ^cN);b^Yk(gZ#JjnXy`y`ZRS>vXt{-(1;ZXJ*} zLiwW>>=6OovjA%;^YoB_6JNAD6p*G`OB) zt-zf1d=%i-UXXWs9lRC2I_EI-dtEb2zoU8`FAi86!C(sl-68b`Aqx1M;cJZU!7(w5DWpj_G_?pr#ouU4!B{`aNhgJcrXvcVo$GxRAyb`I%g)8sk$ zeyN||;W~F8q0%%R-A>md2YR1Th<+L<9_Xe1!^;3$nIVIn#4warA39ULfx~T-KtA$F zn5WZuD&%5qZkPf@Xql`kh&k~I8oAR`VY=&M%DviYUz`_HwvRRNamA3sj7IXF>=)l( z1NITmvJbRwNQ_@i2VaA0(PQakq}LJM+5XY}z{Hqbq&~hSoSDn*S=JleK>!N$CKv` z`0O2-Wr{5j>a5!ev~26%Fpcd$@W>MIfU^gUxXoGQ)(DRpmB-JKOrUg)JjEnCwk8b) zo;WD9;ZaP^!@s?Ao48JAf}5N!3E}Qgb5UFnPL}we^$aRR&My4)T<^tCQsQp%Du#45 zRpO!vk<(EfA1s*%OcEhhel^>T)y*)5DnNF&>#*~yz0aOmHu#L_p& zz~G1Lr~DzYBVrJ5`(N46e@W5O|2--CA1_eDfBhB&MgS5Lh@H)*W7eopKs;GKYAQtX zh>`+`h~&r~_!~~wFw%D(&{<9ulntpSh#m}rdXa`soam}bVv!n!u^r2x^n2i-H zx^k<^;^e>}v&^{gqoqZQ(HRV(>Y#CvA!i~xo@UXZN@gi#>#0&lDfR3>#Xvc%dSVHU zec>f)W-V0fEpkUKbQ~yYsCr+0bU>3S#8D{lX)L-T&=%=$WYT{foaQ146(D&q_b%aB zI1*AxMujXQPf(GWHgx5Kn{;bnpiCt7`QCMuLHk!|Zq#@n^gW8`{3q$Dd|oP0Lcs$D zgSmHQlXw6l;-5DGkbFcyB33duEtup11E$dVCz6Vg5!oWm*&A6dp--zEhv166+y0Lk zUC9v9$x;9~Db;j*mtjaTTy4DZAPNXL2pBY9AxIlZ4IW(~kl;}Jzn3*lCoHC`4KVfu zsdLjuv)d(O)koUn`RDu{&kCcFjDvA{RXKjqk$OZ0&sENf%5L8Oy>S?@W4kEwJbGUOsN-M=&l$e$9*!~o)%Ovw}t_z{}?rp zf2JoD$^UCQB_32DI`to;HY7?0geCjYc<^5x22t>z*yi78K6)S)n>clXJOR(2!%z2b zi5Edi&%$`xLNgyUSmM_56p&sA;MBmUtWbk9iF+r!?&>CwBM32HQ0_mko=y3+rXRSI64<6E zhpIi~r>fexUQI!_A?BJrij!Pe&5ybFvpIN<2+#Ue{0^d*0>yE8yN-jg$*j)VYd$Sz zhM(sApeLMWV`^Q#i}pR=OMJVBn97v5VE_Vr!oN@R*d>ekBnKlsNGsZ!dxi3`pmSLXi(h*Si3HTxL_KmrA@d#hg zOq^d+W@*VsZ|3>$PQ~*uaUPv$qh@D)vH28g;xbM%4%*}K@^P{h<=poy_^+1n@h?Rv7(Astm_ieOSbftoYPGx4OttY%EfgmNF=F>d5J<= zXE0Hh4GOpTRB$aq=7q@AyFW@cLyzT z4D*Lf`2m!P3qUV-_Wgs=++3AmkCfz7!Eu5c<{mx#RT?0*p-`&7X( z+Q?v2=|~>F45nH!8_vJLdtRBSdmxe9xuK_29oTq~R#d9$#8!FGkYipJ+q{cJUEw|& zeGUdyZuce(K@Fh~$6zDDL<5IJAoKD1(UV>auYRkM$*o>-DR0a6tj%rzIs*Y>pujy7 z*Tknvmx}L-Z0aAwqV{j|0s01<;~V9_Z+fnE6E7{e&N*K?z-ERB_6H z_=ald)Q#MtfxbWeTE6^5Ela6grUnM^k}<*0a^e%$J#{L2p0fxww?MSBp7Ct^9vfa8 zTXhLu795n;8b}}tQ!9}>(@yK`>IcPJpP&KneX*eqz-R}i1DHEdce}XsTctwV@ZYtv zvQ}GYui3{fDWApkD4GHRx9x(A^`Oy5Y~@eT2=ur0Dq)n6A9!Cc@_RsVey>1{l#kx< zs=)yV*0W5LMU%+pz)k207O@#*=$?&82wmvBOfc|gue%goi&>Y6({-1RV6+v%xUbOA zCCQiWH@Y|L1baIoq)q+OmPas)K-3T5-eWD7j=f5>9}UHuT$8&y1b}0EbLJ!vI%%pA z)r6}ARmjHpU*~0C*^SM3%K&4#xO6^> zs8eFe>Xj?#kg#M|mRUd;h`i>IP;X~R(`D};|*<{HvbmqI_j!N zh3T1wC6)2)m<$Ov(+o{1onPfcm$CiB$a9pi_(X?_I4`z|!!kF!a zi;D{!(&gF@&<&h`B34n$7x*-3RJlE+Qlk{eI;nz^5a9f{wZo^&(~+EO`ZrE-;9#o` zi<6+}=axTuvaNgC;qs8H_b=CUyX4}^x9gtyoMUvC%`H>fFaRL887+8REb2{sv7R4) zG3IdNsR{K(dM2Y+y0aeGk*e|45U zi!pP1xg-l$Zy)I8Xjf!E^M=U6sDPX7DV82-+B%QXSSh@~meF+vxKxUT$%}$12p;M< z3duHW&N&@u)=IlPJBiF-QhxERzcSRF7!rMxNA{`#Eb74h9Ja5qfQ#x~J6uXl`Bx;~ z8C190&KW<%K4qIa1~}ag1T0U zjpHtd$o_>6fh_spmR@qtP!bZtzbHIvW2yt+QR(ypzmS&d{%WxdV#Ie@mj*tKcC}lI zTN@Rtt0B_A+7SZuGs+r`n|Zu3>}S4qkC{M_;JS!NmrzN3LEQ%)Fk0`lCk=saXz4SVH5=792 z?(UqcqB`|V8J2yZ!^a4TEwi63289D zjkN1(${*b2x4tMK(2M=9Yz zv@x6Z-;hFqPSfpZhf&D|C?_!raV&Qe$+zv}jRX_rtPX0}R$-Hc?K)R+Tr&{b7Qz5u zh>iqrA-p11c%3;LV`YI22l#$kMAQ5=R3S`mV|uZYdmTO9FRi~rp^pjna&gHHbL5^A z!y!KYx`gU6(1tH!522-ZZxi4MG#jvJd}r`mAqkc@ZP>1X+(CFB1rE3}%sb;Cb9gzO zxk*}&$Q8UOjGEUiV88hk)S~ z6aQw7x!ygm8BBV1Ry@(I?-ok!L`D1<0rAZXc`^mob$_LcNIa^}L)#X+FI%@6Ei@fz z^dRkumnchd;%z^y3i=Lcg&cAW()`IVAquYxh$+SxSTGa&^3D?3*gLUDO1*#A>O#5HjQ0JD7cGA_&XZGQ7a3^G%DN6a@#v^K zQMEzV^H$Nu>9^sSr~5$@L&Q-u8qprSI$RQvC zQ4ZxZWvvyEia(nj1pOa^@e2%R18OD0_O87k`_>%cw;XG=&_Da$TwBh~t_>(Bh+Jk2 z-wxy(HNK27Ry!ghHz>~w-}8Zuy&6V*46MNkZ3f}&UW=NfdNu$+1DC69@$a`VK0Z(_ z2#+TdAthz2dSPFgB1NS2$IG-9lt>t#lhd!2MVw8s&v5VAW!=}ND=E*j z_b63+?E9`0bj^2-mCHc8al4tM`Bqr<{3iqRB{h{iC0Noh5px_OZn*w6bG653|#r@<1sE!#?cvt(q8?T=XVyRmOK4Zqkdpz zuLyo%K*YfUvC2t?Nn`~aD>yb+bQP`}SjrdNct(z`6rws- znPElY81A!sGmg48Ph8(v7nKnBkY^Qj%s1&b?1{I2qTU&Rs%0mz*->5!AnffR_WS{_ zv`{!o1mg^DdFFzVTJk||RPNviti1pTedsD~#?S9g$sn%vKxgKa?knY*@L zD+im+ z9xd9zW-APkm=|GLkmr4YBjBYY zp-cm75kC$QpEF=>VfKEemL_Vb7GQNh{wgUaz6hDmYB08YN06X@?&(D-*Oxtyy4}&z zZ<0Utv{o>Ptx(xv8joUSoIOW6`4x92mM*?V;`js;c&cyZTGOk0@F{AH87?0LF_1DL0>7P5t@+{Gl zlGdj2T#WGj|7vRu|CqkWc*A0vT04prmNti}C{@WL%Sx8`8|YuTDV5d$5gR zDr=yJM7(J*Uazcb^Zy^YH6Jy|?ov>WVA1bG8wo}!0f9;aE|mZp8qol2h)y)qjA#K& ztoy5gU$p9yaP9wz+*-vl_eeo{o{XuZJAZM9v%J3-)We)Jg^zRi3KTbj(C6s*=9;*; z%d`XVoxk;b#<{f5+~11v{%n1(#?~r6yn>OHSU+Fx-=WBQp2s+S;fe$PnXd!>nXlXb zGhe5iKVAMma$ITOR9Jlf!*M;ikZVOED~fT9<~04^)YpC>T8Sshi|b47%&xi8-K9#e z8B4v{VqDo0u%(iH72_W;x0u@ITk7ynX3V2H-E=FDf_IT z8={DPh4p~{ORRSO5nQPM7lMm5RXk{xf8alYt1p~zJk*&J&Hq2|_0(4YrU6zI6v7qv zD!YEn&-bVEwYUEN!QMMQNglA@dt=)+cWm3XcI?@)XLfAcwr$(CZQJhb^W>LQzLiQ< zUgTxE|AVgT>b}qCT<7{(evABs6a|5QzC5hG_Vs{1ULYTdG~F)kIX;G3Yhw zwd>yXJ@#n$nY_UK*8ckE+a8z|Lfp!g%iW0$1Y8n6^}2qS^#TBQum7R9!mc*I>AZFr zUN31`7##vjC#?9j_@PV*NK1MjZRnkWPR^wJ;l&i$2<>XWtAPC$@Z@8)Iu(C? zL~5X(pqybPj(+$^H6_o{MI`wyl@5~_ByoXo%0ZXJj8m2q(_*+d9s65_+tO2PaIfvf zvS6#%$h(;8#Z$ua$Xx2LksC=5ut_VnWlqNN&za?bH|*ULUwWoKtL@=A;_3m z9T;<6)~s)0z!*7Jnut^Y1V1K0L%~%V8XeQx;s<#Ps*Up>XT-xji31Zt!`<~{MwxO| zP-zAZ<5N6~OG7R+6AqCuntA~b*|xB1Y1UJjE4Dug)h!|@19P9QG6VEQch24Kx=~hg zjh8WZuIC3lX+|@jD`()KtC6O1q=x8fKP%rO^89aoX4ee5|PIs0!2$z5K`n( z8E9^Iio6hu8q>YiDu4*Ljf>hWe9?7ix=NNK5Sf9gL}OTY?kTXW-WpRhf?pX(uMKpd z3+(uWN6;u=$8!Fk6DEJN9Qz0(TWGZ+zJGC*Nq43$?+7Up?YSh)GqFXE>1bg6DK=Ck#X`m|%Os0Q^V0$dY_6!Fke zMso8lVsf~>_yiUIPP3V(sv`6&Z*_@na2Ux5(A3?f*vEDLrC2{!Aq1#=;~J^EI-dTK z)k|jixyL}K9}?j+Rr+v8u6&XvGBnC=<8~^(sF>yTJ~17^43U}!bLREy6MD(XDbsb= z5>f>IeDP9o!?-$6ZOqMCJN!}sMMpIz2tl?+s?*-rf*;Go6eOsMggIJGs^- z^@^Ct1yn?C(u%&p8dq&i~N(^KFy|VY!H5) z+ths|uUi5_XK*wrH7MzWw@+kzod=uJI|t1{T_X%2o;Jb>Iw&(CW4y@Li4%!L>w|b3 z0Tvu9F8`F~Song(Ox-RE5>rJHE&TE2C`YfY-5q7mJCK}{9!VmTsm0-N*k`ku#v^=r z4Koneu}XwHTjz`!Vo1HJybcBBlrIwG#k)4%WHiS$_kp1lnQ9-y6_6ytR>R!;qER6W|DHUr7e}xXy%0K1biuK_W>&%8l_D$F+Ui+Mf{fIfVrcho zomLJ*OvM^vtOU{UXv4E%v1B^FfocL`SA<>`olid?Ol44I&`?HSK9D-Ws%sbb9y+X26@e^?f-NsDO@~xM_vz3)sKEs_Op^fsb5qjOPnz zm!36gZj30TV=!qPf9x<#BuCM9R2)`^Ho0dJEtPE{_zY5pTkE%$B}Mut_{XHSC}vqF z@&bU~f%^sQw#KZ|M)_Y=n5ynQ^Ox%Dcwl=ct7qlfS4T+lN{|=ZFRfPg*XBI(?|@MD*wF z$_-@I_&vgBLsSQH^-V2E9%(r57xPCBYP3IjS*`s=&6dEgZ8yFGbF@J6KSdRh9<@%s|lJ5Uv)_ zrUx&xt1pN$vuv{$+~zAT2er(*SUrWztfTxup?1HaIhK2gU~!`zAW>bZ5*t*8I~2PE z#DJvIxGi!_M9@qR(Cr91#jo4lN68peGJc9=qTqAjSs%|JnSt* zIy>cw(iLk9dB&00bMnUpJOf6!c zCGb;D;LNZh&9NN@TcZKq=ezUa?=S7+^4%5j{^E*n0>RkCD7UPyZ6C_UMI+W3W%Fq! z)mar%**3tqGgg?)27GVl7IJ`zRJ#W_4EXA@3;y>cx8zhj6P4ltYI3U@*^97rZ@4g z-s@^4@3g{tK3MYrGx-+s*V?B~4wGh82Qg1l;aCm5hhfa8Qw_%Vzk+ZKf?|56nB`|2 zNWIE5M#{O`?C!{8MGN~Q(2I)57oG!R>l|&KEhX{1O@Ff1tvT>sXUrM_ zL!Pvvy;a5mP3&?cfgRq~OPxVB9Leo+4?n|DI^c*TBE{P(uHxU&glRy*MxG>H{j`8t zD1`dc&puv%LPwG8WMz~b<$FU}zJE({G;B zto$ykTHTCu-N{Lgz++&E83Kz$ePAK> zj=Ehf#34j~AIkJsjpM8_&L7ESXfCe|2>76dS}T07J6J7b*vsUCeg|<4btIv2}vwU4eDXZo>ECe1H4rl#Yn7O1fju0SfS} zSx;ClKGKI5k85755TpYA09JTVX8*pPSos2V*7?B1cUh-Sw zGSr?LfI=6$eRFSCEBu12)FZ5^W7X5ApH`Qsvij8>Xigp+cj9})6J|c{9D2j9P~l2= zz->X@e;^=LW7eWPs5H~Jt295sg^V%y<|ivvuYP}W)7rdqEfl8%XvdGs)(P6Xv_lZ) zF(j7r7O65Z+>F>F%}eS!IcmJ#&VxygM}9pQeNG*0E&5TmhBg2K%4!1&;-2w_M+{_n zB8@+fSFy>J9*{?X3Y0zVd<4=QI)0StWwN1=VLRdn^Nj3nZgmXQ_G}l0*5+mdz(1<| zW^+{}y>>wR#B9Jctj|J+wL&=M<)6vvZm1jVUDT3RHRO}0Z)w1@{)vMWR5#MENQLQ+9GRN#0D|}lZ{_Ye znuxwv3Mm^0?pMZ@77hpn5rnU|h-tUFLl6mi)Y+sO#?kfz6&&)=D@&9_RV79PlQU1m zYDVkB>5omlwt?HKhQ1jEwogc&=^4BkaHOPF&!uS)CbPK(#ojto#)8G}v*PiD(}{R< z1|olYNwz85N4suj#~7z&;(*wlM*r5#MVj2%pN4|b>a6R(FyTlNFO zOr}jH^Cv2h*w+?lWTlX&W<8V!Qh)6@HM^1w;nqXKI2>1tETI$c{r>9O)Jz;}-Y5;_7=DyJ5X+d_*Hjr;G9+MZh&j9Yo+T1eb`H%s_f2Iz*hPYYk zvZ$9t9n`Vuf@02Hg#kd}Fr7N~A??LRo6lg4itVj2Q+jf3mGD~ey<+X3ts^$rDw=8D zInQRlC*m&e`)>gApETy>pOol-fBnxi#{IuL)ueqkYY+gyliVLV6u*R~|23nN%-jSW z!qK;2QH3!4BbYW7Fprlu?Putl_7dWtpT-gFEg{+@BFSLAQj6%1oUj*g{MZNbmr;;p zB|hA=$Oye%GpKlVbS+?K&x)-FrmAJyq#>GrA%6Y}M)Mtu&~{L1*9q(PYfRF`&`HnCar$zDJW_G(~fEU(~b$tc27PX zzNY_P052n5pP!_)luXU@afY{)z|6Nb5sN)lWZ_bw;|FZJHUn)yPpc%rkBExpO|2VlH%%orf0?l8QtaWNbV{C_9Ax) zkOp}naOJDj%edQ^+e~mr_#hzfz4HM95Cs^nIGw?NYM-}WIeiv?Mqd;EBi95BAW=Bp z86LWaeEUAsnmT>c1Ge!E5Od!0uT+NRgn4{eSc1gOxls?oFFoUuJGgOph zgljv+QRkq~Jt#M_6)hVm;uA_D==zY-ihNykF!OAl>d5{iDSjOB36PJn9o?q5kNMp6 zN0jbBBjv8}SL`tuyhpHH6*!W_WQut1DZ9Ho< ze;Ceq;IHX*nu7R~mag%yWQA4=uHzRicnk3fovnh16YQxwed`lLnT8vBUy{K=%3)bd&vBi`e!NMPs?W5f<55pA!#-R|ESV z*blD=J;0CJYXTa{ zN{f9trBRnd@6eFgIHmQ|sKNzBCHC+TkBZ`WL zd}7rVV{HPVV2<2Wxf%n$+GDW1$1*N(Na4S1U@ZFax_!FXY3_R+0@fYlRV>M`^qaHn zBl298Zi}a-iw1ij9u0ut4ew0xV!+LrA7bB9h# zoy*$A6_vCb9hD%cT4TLhAbKN;jr z*ePP|iLKE~I+7^~wVNLI^!j`QCMI-IIl=d$$(z-08Z->T0{z}ORuyv>s5>;E(2}E7 zG1f66`Fw!slMSfCu9{BDFif=lBCjbDVS+FqX36&VB{wTrHTmtv`Yqu032#rvSQMx- z{Uku}_wUj7g5#Oq(nLlvfPgs*8p=5$egcITHnM?u!UnD`W{17QS=^4byUHmb%TQ-Zbnm$A`>+_j=}K zd?mW~v3$k51>-*$7qK6%qnC>7%qS!_H;jTpAp;2cke~7Ni;rT^XQMhr-@O42A{a)v z%%1BQcpxy z?AJmq5aQ`$AuS3iXQ2GVvQ^_qWA@reG52&(d~M1rG`8^MZ<)HV5CX5zyYv zyuTCvJ&74@o$YojvU0C&gebxLw>-hFFj5o*=Uu7PZl3y=*{HF~@UaEMC904~nruMZ->s`BSzh3dbd z=51leVYeB1p{vQ2`>RfIYEbLoiqVn0U4>uk{_o8Gv@Qv>=mziRiH)#P~qYN+m7PrK#Oj8tf;RBX1RGYhq+8f{fJC@y@=^j23C9P?j9% zS7)_6E>8>6<0kxqL7D-;r?j;s=c-e_y91Al!qZL_%y32+kxdNdiLy?&th(BN3%GR z-c_L6`Sv=@ng}zIza?d+nHVR96B3GNa?q~r9QO4|{Q}UtLyIt4<_KzxFUxt!rCqBBRFr9XeXKQ=$KDh5}z(IKa12_+m_KB3~XI%yd zLM_lWHy0~OKv3NkauOqmd*`vxq2~ti7178$(BX5#0<(|4ompW}>C4$>diU~&Jhp9} z!i2x~yxO|MK)U`2!!ATR3|iHd86Z0mu5Ll$C?;yWpMQ{aY6(SFRLb9SpqStT(-L^( zQ%HA-BilEegPhG8Xm=b=;LQalJ!=b_o_hHdZx!8dE$G5_@{Vfnz{6njRd$*Qq9#a*N5!_B9XoIFELyi`e*!u5-3Vt*u{EnXi{yv`lfFu!zMC+`? zh}wCvE%ie?B!)Rx-E{R*_6zCT5Z-_fEXDWKAU=8;=4qjZ{XqZNHw2PR)ZgQ~>|T9} zaCi;X(3A|=Wnn@!eQP;2v(KLl6&qcIyh01f$f7vgqjQT40}vKa7O3@!k&x@_g{HQV1St{t!DFQyUj*2CG{xh z0N!#2h5F0PD+jgCHyQbxy^V!^_v_?M(FC!IUjVtr=7+P}81~2&svNQ&i?6{@ecnl# z6bd>`xOaf4HpWqP3i2XqN18c&JJ|&i>x_N5(*^%I6(Rc%;9d2TaE-bTC<&p! z3?Jhb{iOb{8&4m`y0j&{-{yh^_f;v;F6SiJQqr_oG?FpI)yARBIsL{}QD?=;IUC#- zw_5Zaft#`CMnJ| zdfqy#M^OGwR#pbX5O#~>8bSSSBCqsh*W=*Vdfy-klW!;MTs)BF%Mm!TByDmb{`}SQ zZjxBE7t;;EYA&Q#ifvKnc*b6m9x&b)+q&gIKS8g>f}XYWTX|L4fh2smo9yI5YNJeI z^LL}xPuc~1R~wHkI+Yr|&3IVNxjWP#tZF#~i6rvl5=UHPU$3xf$eMLl>cU|o2a)hW zcB0?vL`%6i)>2jYc&1i%Ck`q<$OG{F}w8jxxeM75#Y-bfoH z`lG+RP8HA|-%(!wSm4tdkR%&8G`cHBow6O+c`paRJ>um5uQ#j&*}p{=$|Tv% z|17eI3M*>FVH5p_b`kLV&n}B-_0qF~J;C*N;LGHOaQFA(^XfO@E5k-@-1nP zAn0rGjqAev>GZqTo?%&kOO|GcxmyXQbZv zsUfAjz$VQcbjedPP|GTq+4V)@CV;UBklX}$fF0`^VX@|i%6poA%}{jbl&}E5B6U%{ z*fEX%Q?4LB2-SH>61Dc^TC{Ig%7SeoH?BVNw0}%k9-pHy5ipDp+}9_l*(mOBH+W8r&yn|PP_k|bUN+jI8& zoQ>BuD=j*Y8;Dh!ge^gTsK&p3BCyE)HT@8cO!p0UeM(G*d<{HVAXMFOT$WNY2oKAy zLixj21edD2u;w7a?Z&#H@_Ain^G+!Y!NfmTboTrrv?xe-Rhgs1M<)!QJRIE$tia|W0cr?C_{7o@tIf3w(9cUDnr5pHOZ^7BWa#t{_F;#N+=y*&@yteax1BE4%l{LL>$ zHHZC0w&s^?V*4b7NQO|eaDsfj#DZO&COz~6?rc7QbCd7o4NYVXLP$kOzL|4>=DV^q z+ZVdjc#n~iOZ2No$KHT(yG(N8yEtgKP>g_KKaWek!|QqiGtt@P!@Q%U_%>wAv~4rj zx_RMZZ`zr6C-xwkyiof$gZmxmDkZoGhD|l)Ad7ou+NN^Q-4>xAsMtyz`$U_Mazb&FzfV3^s zw>0-5yL~Rr*%^VfFd=Z>WrE(_i1;3c2{)RJbzAz%llkeKW{JZ!iWaQjV7pg|0%j!$mP(XkNCLeB-ZLF zWSG+2Do78-#9++4y3P&9jE=Shf7{EP^>?wM0N4}XU&%%c4n=gKL z>mniDjYV}pC$C#fL*Gn?tGCPBc&TUeU9?VJQD6M~$HdC5`)5Ja;;rx9FRv|_Ks|mV zO9RMTc;dk7*_?$(tYD;7-U80&PGM9TetirkoWv=ez0%QEC?u2Iqg&bU1H(^mTQj?S!`wPSCfzSB%vmK^VK3_h$wnF!>+;uHARradBS()- z?xxfu(2y+6MKi_+#LlV=D1&YkXkDCV3^fJ8Vg~QY`L3hMw+O3!DL;h#Kp_809oZcC z*yI3GvoZxWw0G_qqXp!alVC3`XPt&Wak1y_^FZr|TD|6D5;8?70f4n{7P0{Xs=i<< zV~-_$MmWX6JqEDaZ@beuAY??uH;2TVQANueE|cNlERJDAs)hASiQg}V7Q0CTp3khd zu7x(lB_ui-leLjIo!5UJFw-?$vf4z0JfIUFrAMm^d05??tZ3X^L~at!3CXt&7c64lH>+?U z^O>XcW0Jnf41cpQK_ROQn_lb8B?gDHm(nJ!+(3Wx_hTc+I>x7!gld28Y-m{; z1?~h+SM(MGOkwH%Uah$Dh>1GjvYQOlFtme|2p|CqrDUQEmKsGwBP0r1H(H(LbH!Jb zB7Vm?J|2`rxOprD1hv>WLHzo=WJ^@|brKty8-!1uqgqeVZXNUsB8uLgVP{BYjc~wN z=$Tk)6aW*$dYKif9(}2v6Zvp}*aOn8m@Bqj(a0qi#!)%l#`TuCi-wG6oDY3PIfrnj zvm;xRS~YQ~rTUrYVBf5l>i4RaKBpBL?w*BHv%8P;L~e|9{i_`i6a>Yl7Yu&EjZKyh z&BDXUpn$sXaJm9QL7L{2Wv3ys4$Q1}ryI{Buh@X{LwQ6CF=4XOpqJ6hjin1Ua;jbR z1!U32yZc+rLTgZ`cYb37WApdEML==zn$UjD;B89^e`NpU*QIjskDa!l&kvbUbxPN@ zPw*eOPr5rRA+6&qDsE}4a~|zMBZ*9h-j?~4uJcQ;pJF$tj?eGST;kpN8|>3fMD-2*#p zYrt(tI468PZ@9_CgtGrY()f_n-d}eWv6~DzoLvtQ->rIxV~x`CJ#oWn$pU-u7e{FA zJz+0BY<6|LrWa?Dn(z4C50Tk%C!hpv-@)rkdKlv7jeZ%&zPwsa_(n+i2)E13)3l(p z4&G#!as>>C2By}X`xD&{87|wlk-K9^z9w=%OL_kZZXiRHi#|NmzVG(TyKYWAE9OK{ zAj{|Uny`-t3C}800e7v)0A7;{fxpn2ab8og=F`kJ{{j$Ei2rt93Tul^@Q3C53eQnh zYF2$t-#E)zR*w>a(_l~kG8=oF zfF%;Z-}D39ooycz*K~ufoT!xjDBm;I=-lrrVN8(A;$g8x6Sy(EJv7CZS*e*akv+!e z9(AT!{3~&u+Y~2Ye@xdSFM9^EryUMX7h#^z#w+o{@-L8gfBRdNdJLYQl@91wTJtN*}IoU}Z(-G#QG$)$S#QjFb0|9q1&a zL@(w{?q`Fr6U?$)7Iy!_PYAi3bH#9YabYI!gwFH!^S_)lU5hoq%l*mURFCWsF$gow zkGs-vMKp9<>@iUU&`M_$JBr%znBNO_RzevC-a@Yh+4YDxgIGN?|Lj7vp0Y5Box{0W>k+0(rjPNUjkyFfW;orR z^AGDb7ShTfHx)e7nq$zr7@@o;9bM+p7l#CA7Wr%tlkEbVEGU$>UDf z;XkeQqAXgsuz(Q8p=2W5QhX0;IbfCXm+x^|?t%Otdr!_MU3FznmckU@Tu{;>@R&&R7$ zV@IU8T1OY7((97ZyZ%MN+6#NPR$nx(Dg8F&a6CjzJUa_2G^>KQFXry*hm2#Tp;<}P z*e9;+29dwe>VjDr3XJ?r%IZ+Lcb!tq;+^z>lc^DOd6$mP=le>2hCb8kX_I$ zJ&HMW6pjYn!giP9ATV}|m>jazOLOq}iyuAnGpW)DVml9d+ZEm{OmNhZ5AKQ=i#*!A z;=-=)4E@*uoIVdHpg7YA3MdXWLX3P`0Btf@@DvjT#w!Kgnt_`?&SMmH*PU?jw+>&{ zx*v!TpUZo?Ca%NPo-M!`Mam6-t-OJv&BSUft&zuo2F=?zD}o7aqIM}CUwI>DlClNN z{-TkUi^hMfu5<*EU=Tg!=ubDW)W4?%h0#FC*Nu4bsk6_f!fNP@yg$GwV*a?I;($Ve z6XR$%a2@euLF_k~Z7k9R>|W$L7LWqC+|yst{D~LWE9W5_{9@ zi2{RRpG%wqo_2om8>GKECbGbJ5$f7G)eDf=)^6n*TKB$uP8KC0=yM)`j6^Jn)c|t0 zh8rIOVpBM;G++LO(viSmCI$)>%;_Irm$mJj_rf#L<#6?og-%0EWtFSN%yU@+Qoj3D z&t8{>p2+$TSXh(Niy1J>Nh~PZCXO$sVzFq&{Rm;e-gyIqRIvfg&&uCTWJ1kKemh_K zTD+)v)p%Z@6#-n~3D4)?d@Laxv#Y`Ut5KtkiHk%TE*g8%lBb;d_DE4y!w; zf%y|g8H6Y0jnGiw(p!4>W6^;-VnoKed8UNoDf9i`H|8qTGazJ zHS8}41boCi7IHj_?l|ee@mAep^kSPmaREGmdtFPl=t2Ty-RUPwD$Hr+#vazXOJ~O_ zDk^$PTy&q`RKy0V~HxY=-uCMc$s=pf?(5+YgfuJ{i-e~21O}To|7oIy-Ap3u& zUY!3A_N1EsX-_gAkAZS?imbTc7aal{qk%-H7E8=?2VupJUJ;r1C6b*9nCD{T0S);! z+`t|2Hrzpb!DlMK4|x(cwP64ePXoh5yM@F^g9Mco6WllIa7FTcLti861*0(Mi9{sOc z$(JH7ZGJT}rndY#qXn>2^rFE1^NBqASq-qwA8JzHRTx)bJuc>$c;t%uDQtQI&aZlM zRDrhft*AX_{)gpk*0K~A!77UXD!gve*RIVEa)zFeSj}^GMxGEa&bhSsyZeZ<`AUtQ zJtemKU)PfEKSpO**JU+l)T=C&43MDODz6>HAO7{HF(g(|3}IO(oWg`pJkbUO%aF&P`1cC z$g_X%J47s+-L`M@Px?>q0=&-q-n+oHz&&8#qZD9x<@U_* z^`DGwca6VM&p^OHz_xp*cl)Q$v+<|%0{vUyEB7C)qc5beg(x>`g1B9~)oU3Gcwh8A z^>hR1Cwz}HY4O^odH*VdfWRBT7hi7lhkb(ygBr5o zFmL=1?wmGb;W@NRF9%Vq(Mbr4e)GJGX#Q(7b@e#(iZ7HqRgL zdTg(E`#4zxJxe{yJ6B}9JHBe`GuXJ8d^5n{t*BEWLcABd>r-1wwa?ZxxE8W3Kwk<0 z&ONJH*823|?VZBPK!=&b-W`|_(6e>gr#HA7k4HZ;<_MghDnVsY^v8m1Z?-dKkF_aw zPez!|NRV9hKv?xM5|x)nDs&)%`{?DBoOG&qQ|HAwHnpV7z&r zEYBYQLWInw`mY4SB-$T2U2gebEGeaNA|gbX9Me;V zO)#R?R+8wnN!;~zt=3PZBKdt(!t9#LaTI^TYsfykl8#LO(T>9fzjPJGJ$d~x%;Z%X z>1F<=_XY4jsuTk#$Chxkqi|?V8YV3E>~Ey|rLI3V7o2ED!>$|j0`T&1 zv%6`6C$+Omt{DUE@J4P0RDxt9LL~_Yg`J*tw_M|GjHNd`#D)g*7ZQ&zRN$GlxotP? z&BUMLK9c~x%-dTln$CqY>4je^_=eN_vbrmOYX))?wn|UbRCu)fpH*L*L!?UZdCuj3 z6D@rXU?M~WhfXy);Kg*2o6Wwa{QR&rAaL{*tP`W-hR=3NgIA!7M9K9AquG(CIWO&u zsh0xk@bh~sB#u>?W|xDTH;T)^M|zAdp8N0I@~y&6QuE1Ivpu+Jxw@~YvirE%@g-c> z-?NS_v7`oY5dI_?(9*y1q1;#+hdlW{lJjb&8C>uOVs5$Tmv>z%6-5*pFE@|Fmk6&8 zNRsl?zH8vCboQ9=n9!hRWSh_#Dss0ro(L~ns&jxyfjjk2>f6}c;z-n=YQ6CU0v{9`rU^e}87LBTw}}X!BAo zG#9Io9{ycL6D!`KZ{NFfn81~Zu9NIB=c^F`qC~{0@Eg<<6z|+v-TNt*X6DpbBpLCA zDV>QOY1J9LWOZ~E25reBlc}XQjZbATO9O-XRL8%NRwzdKmCHk1-T_EuT>yc zrV$wTHYbGFboACMJJf7{NmklV_iqnzQlXe^>%Q$dAFS>~pBf&e5dN*J5eG)3UrSrZ zBClPFP}#n5m+*!P?QfE$U*HnNoCUL_^lSPWkUH?S>kf0JS^P>1QVGUcfmJjLZeM`! z;JS=L{B<2+CCcK^oxrdQ_vpkgtG$g;PVln6;AvUyyI`ig33FG@5!2)2f2J7IhT%Hs zw1w_nJV)7aTh8vy>jqyf5iUcd8~dyqqd3!T-M}nY9noYl1X^(XKVB$!1U(E7904m{ zV@^Fq>$cavduEGY%7mtv`)4J^|5izvp6Lj&LwrR>H^54O?e=59oVwxZJ+%2pPb7YW zn^C^ELiLkE6Rk?3gNE3NWr%{TwFC|poGZD@k6F|q)p_F*wEAzlyJ_*7 zU)NegpIo?DM*pzM4$&!;g#{~+r?w*W{;(30O;>;N!S=;7yx(dlY7=GQRAOyJV18O+ z@0LM`PG5$m%nB*rQnt*C&4(`CrQc;)R5k`YM@@<|F!I!9m{GM8&I`0nFpL$vI@ywCnNOKna=Q(LOE*qq#QY6KO8U-ZK zkhwSlO^MD@(NeFIPCR)m7$h-X(CN?dAvQbEPm_$# zTE`Y`kF1q*Yl*TUFScpUSrzff4BlvnOkszr^ppq(`goW2=z}NY)&`tBP)hn3DShSz>@DTc)i_~|fXGz_mbvq-D z#Isinl+t2Mtd@oB9C39;M{GhDv2>>L8M=RML1Au=-4X|_3_(EWbC8zT)KEge&aC*x->w~LOJ+F+^_|!UoL)f{ywBg|*Zmjuhq_LGAD~kxP zoOpIlQ)c{zz2;W3-6#Uz@5~v^^??SGo|a z3TIq<1+C}uLTqlcmKGLc54;fV$Qlk5iu5jKoOP_ro-aiwk>2cRn5~Xf`!x0N??~Dc z<~80r#jta(M>FtaI=wNTN$L-q?Or>?K-qRm7d&JLGbwS-6b7Bnc=DZ5g7nifmQg;T z@hF!36}27wz&{O%p?rbgc@8IgLV+#Z%oZ);6#B-&E-XfTAJ4)sPzX8#SO-fG^f8!6&4l9eCoCSC>7D=CAd^>WS71ascF`428v4t-@Q%h(xRWS_%n(wYD#h z7#8^W!T@sCx_ax20DAj&?P~cN?r;4v+qYXjp0mju1CrP;$+2_79m)(it;0)*dOW*R zhwsATh?7mblYgG<l5%D(b9F|+7!j?00rW_0YMUdN-KEOJ( z;WIP-Paf$^6Fq$USA-8bkukR4w}V~kWPLsX^Wlndui9SxYi5g34mA1aCZTTzTka~< z542?PzkfUZZZ_%gM#hVEhz2IhAKjZQSsg(~jz1syLbIu$&@WS4Md|4kcrTELcqJLo z_-peM@m+q0Mo;C|YznRba%{uW0@hQZfdl#H^MsD@AoYIM3mCM#wY6MOdsBaNbKN(w5CNkbUe!ubVzJsldN>L(SS65g%cf-A8*CuEcK zJt|J~YwB>ku}8;!kk_Riv#1SJ45GR_dn0(PGh|!|8&N>n3rvn$2jXJz4{7&@uG z51P`G6a&WeasgqL=bISz0?ns0ZnFwT;*CPJ!Of*H#GNRuxhh%3?h%qy{B7w>q)H^L zY{<>Pl(_kKHtZ|KB2S+uNjdV|VkB{((rjE-i z%o*BQyihUO@UF1Le+5=x?*iFo=~x67&ituJyIyHa+uC>^>EfzC%g-A9yjYarZbx9saDM&{r8RS6c&+a#v$WDPm_on9yvDd~7t&&jlE5_UhgyU;NRXpZRlNwa#9q#dv!46k+RT4iGkE`JYo_6U*q2NG%ZwbgL=%kz08R4E+fsO|Q#S-8 zMj%JY!sOD6L>2@9EziIoP<@KZH@hMs9HAr9JUuR;Hqrn3<8YDTTt$UewS zgMj_-KHyKr*yywo`6(kBih4{ZceUDAu2iO=)K@>~t5NiLpzSd~y&^$M$2`Q^iY~K~{Y_qghsbAqAm zgMF)}(orTt8vdZ4fipNVcRJ+sbdPQEh5@C{+rZ*PtX{ma7)zUv?daZ#T;Ram0Wj?o zm{($!LzJfuPztq|9OWa->jr)(d7Ukk-7n}!jc|YWc^u_eJ?f~@k6gSOwhNiPpOwke z(h6A=S3X2J6Mke%(UKYj-Oo|d5E_IusFZnk_k<^@m@su9loamEyOPR^BewEtovNmU zXZ|c`ktugVvdIa~w9J2gJ}sx8_s?w48nG|!_pyG|#Op~ONz+N~_d9gLUi&`HwkE6* z90P%wWsQx_hxRbiQpz(6k8x_#v z({#)JSK!HO0+9CtwEBi)3jSqZ8n0kqieC{IKfdw+dF9OOGiweWy|2AZxi^B~0r>%a z7X*(eUzLw*&wPMUAomM05ZE2?J)hLGOB6`JymQVU1Pq%$r*#30T2i$^c+dE#gE}CV z1r@wxa;SxD-w(HkxqN2~sc<<>nNI0+NAfLj1A^?AV~bF@6ce-TShHz8GW0kG4Qw_z z&XinpRRObRSOD(9_3Sglw4dq^_q@%6X?Y3u zOGw)jPC+wxexIjbS`@NY=jTC5AIFCuI9u1WHg6c}FHkSFwM5$si>I7Ty@MYv(7b~) z@S^7CA))!tA#E0%H{FI^hl(jFiMoZ?yUkE8EVNgb+A+>&FiD$r#AF3{DF1;?3Twvg zdGMThw%9dWhOmf%Db$r(4E;v24|uVRkeZ1t>pnT~FkA2C zBcs>l0Q@hHIw@Uwab|HL-A!Tl=(v1 z5R14z+_ooi_22phS}{u(@TDl^eTNz{+D_EW)|%E!fhsfTMj$<;kKA;9~E?pM$=T4+uYJcje_JPpu7LsjCNWJ1e&;17(B-Z7hLT z_#Q$QMQF9$O=d0tXKaoP)13NwMltmn(Rz6QjU7*vo{5^zRt*_TBoD-|13z*N3d!fy zx}kUG)T);q%~yE`NJiKZao;@@0&FMplIX7XEs9^rn&b$1<_Wh$XEei-CgZIVAhYZ@zQyK3D{ z^SBWoH=@oI8S}H$1+h=^oUW>@zhGBWwTX*K=Mh!S=^73agCQjIYJ*@8cd!pL8_?N9 zFQ>NxZKf#+T1!q!g0UXWN(B-MKQ3{I_|We2guYIK`;aiSHZ_(<`-J(|io!t+fT81aRaE)pyvd$(;8q#nz=@xp5MMA%D_k;G%H^Xy0tl;V38eWBBiMHq{ ze7KbCpFG6V2{H3Qiy^{;jB?;p8{9Z@+qhO*{EL51a@<)D2gGpt)zRh|fTn>Sd8_n~ z)Ig~>Da`y9wE6vaU;%`nNFlX<_z5CS-<8)ZfZ0g?Bb_Y!kX+8{hO@ee(n$yz{%`4l zphHx^4-|0j>WRVjeKcuQpEJ9v`4S#=ur*1Y_&H`Qn#$2j4=N{KC4gE&37Yzc4uCG(33&j)04%M_ztJE1^qrY&VJ zd2mJQT4L#wKTJf4VJ`@e#tfwc(FVG)nP%bSI+ro%86_3nSH&w>P{%R@p29$pDk2=~ zkD%WV3C$y*SIe4apvU++B6y*00Wf%=kfPDc8AxSDx%}O`h)$u<;Q!$2Ezwj`p3Nz) zK3)I_nj>91P7Szzc@CPNLpUiTh`Gg>N^57%_sQj%Ju`A?@s7m4OW5{;t)^Y5E0e~7 z^LspC!G)V%NSbgz zPBAuV3MlUK>KpzZWbu?vvgap_-71=wv4t2MoZM6bzRvbgtk&ewjo3Q}Mu`UnN((Dk zjfJE|FaJI=<$CdD4y5u+t&xh1T$kl;hg}jmV2HnwSbK>MMkfw?U@GY2(+Si{6KXlO zy+{Y0^F6w{<)XgMvQLXhO~Jfgi3@ZUwLRPU{tw5j-%tykJK;i3FWJoRxISUfr?8C+N~T+;;G!+C%bRUW#}) z@>UqY=jDjl5IqwjR^Zym6-*Hj?P{i}1Q#U^IQ%KI^_;tSB=l~BmuJ62hO!HrgM2)V zr$`PpQ$t3yd^!E}2OOH_j=zw->WdJpTEw>iK9C%%BLZC$=~KX!FP=m$UzM(6`W3gX zMso-g8@@*sV=%$on>4KzD)sFZM@xn+`=28CSXiFN2L8X`^*2jk9%Ck=Dge?EqU+?~ zNl_9KjwibQQR7>hhmv_gc`-eBRldO5xE_~uCw&H7kk<~F8AZd89Rt3-MsSKDYQHPKGDk(Np0XO zfw~^wKs;Te91|*DfHd;bIbQ>XQJlswbZ{(pwTE~n5oAYt`&QkQ$?Vt)wLe+^6WMpE z!7J=_?U2vKX(_>Wh%k~7+LPjS42NUYGRpKFZB@Z=(=rr%)3cRo;|Vt!#MT;W z6Saemc@-8P5IN51XIAPT8@lVUg-O9JFpn|4KPhi_%QZ5W?=+E&Y3yz|zfaCKSkF^aoS0$g}AahXyucbldRn2S*L&gRytG6Mn17rBv5; zg~LKl-Aew`)hHQ#K+9r<7GVLy$nk9ySq>L&h1ycl-KSCbqpa1=b&JXH&3K8&hcJ|S zD~wLl?<6f|PO{((g{dlTv=nbD*bB)oyH?lFmEw)NJ_!(LUdQ3yLunZ~Y?PgxDzv^$ z2oS$Fdqp(tEqJ`cc=W_f31}<71>u!{P9gU7y{2TpmHTDIq1g$4QwPFJo=DBw$o4>2 zj)yevLvJp2`r`;Ho~9lKi_3#%I(5zGrN;proG!;VI&S-){UsS1{lee;->Rm5+^0{v z6=w4W6LeBeBJ6~y4#;L#rCYaUUlrCaEP&?EvRrrMD|Gu5sDWU7@Cp*PdMvX6&Ws96 zaPgcf;Z_m^FF;4_LQY?rC9f692~2fcFtHU&tV)%ihxw(?-v`=k9PNb3CW80=6#lXg z6E?aMoqbTM#BS=3D#K%9X@3TE`>DN8OMehvILmB&#r$)F`o&VO;Y1jt_-?4yu1uX+ z#$XV`L!yp58EBp3W~X9hg9w>CTfsPg$L4d?CfP@bx04IFW5hZ`O>XsmA>8- zTys}#)AX?5YuZ%C(^{3c0Ve=+RtOU5#`?@zYGc&h)8=1m;l+Mhg5%L8#^zI%qf>%j z;FI;pI+-BiW;}4nbb2zr;@|z~MoQnE!{xs;jfu3fU#2%b26;-qC9K`$*!HBX7b}Xv zIFAm_uAu(AOTV*vo{< zs?)gXY+eh?rvC`Hm#LThBL@jWtlvDYv>S9N!gL~p2%4B!1hRb)j37q$)Igk*cISle zqQ9s{`IEh?wVj(A6BMfx-RAeFD%YDgX?C2{Gjp4RA!!qZvKzw-k**`Wzbg4J8ziB! z+MsK3lA~W;(nXf>jVf$oWCTK)QuTO(w=FLZyaX^7kmI)n!e_-hUR#~4d{tZeH9If{ z$WRMH@%9&8<&7-cZi_V6J>G!(@Z$)EEoB}Y*Vf2<&60Vr5e@gosIYPk(w+*`@GIavwtw}{JtcQ zVALPcl~O76xNDK!4{Q0@(kf~Km%ZJ;+9;2T7I>T0p}AM$JOCtk6tu5KjootS>$ zG8ltkvDTL8npf#SzWfNT%8R-hxXwi07Y_{MX35R#Rn9Uw`bcu7EM89o2?G=!iuWX}w_BRF=<>dZ^36XS_^UbJBj4_I zRJQg@c_%i7ocEATRUH>-DQzP#BTA zItT8^{&5EbQ>l3idW^Y8*wVMePEF0h+4?T>?FgCVYtgF~E7R#x3sOwPS$Faq;U`xd znx}2YpaP5Gnx3T2Xo9K_jT38k`fi*+KpcS;Lz=L1J|Q- z>>w4&nT?lPv{_IgN2$BvB^k8uY(KkQTp<$M)I(E%n0}0&5=xRWyj56*%sRlvfc$+E z1+stA#y9L1d|*!bzwt^E{*z4pV??Lu|2_S8{hz%e|HFtJzZnt58!&IVqVzA?DCY(# z8I_13Z@DyWCd4p${+U%1Q8WPD#2G7gI%o}-nDmAj9=YeG7@6<=r0v|DLo#`;5c!UNy>?n%UuAYE-#i#ebmQ9JO4o>A)p zXt@T!p1T7w$;v+ntlUq0GorXwLlZp<4_ZdQA2jscTTK?zewWLg^31(6DM?HsxD`1> zFFH31&hcpTlr+Q!A@g$*G+AGqO@4VqH~hUy95g zqHf8`M+EBpkQy^0PpwN1C35q=Hhfv{ilx&o6))=NLdox~wzLrsV}}g)yirw(q^7@g5zwndVuMG;&*uLntC#7Fdm!EcGWPe-!_8y?0~dAmSywO&{?j_4U*^d-$)7d*YBjB7-fGx;sfu8uNKA zYSuyRbe<9sS8%W!tGs!sho`yUp0$Vi(bS`GE0ve|-;m$lXxK>d;7vt)`fCdkt|tMc z31_m4H?o{(7=%&yo#cTLJ@Kl5@}zpnB@;tZ{K&v(Xz5)?!YGSy6vpWDHuR@uHw9no zoIR#HllvLytMoX-1s%jQLfAKN1cCk7H&>^$WFj>$Rj;Nozo34C5|cP`$d(55dXQ6B z4#4q5Qm=ZllcPV0MpKjG@wS3ZsMuehf}ILFkL18X4)9ny$d=uveymJAZ_*#9Q!FOK zf|>Rb`Vg z9=#7w2slSU^qTYu;6ER03=Y&-TqWlkQuoC8r_6TfZb1M5s70-*17Se_DN#VX2yKBR z>m+I@cANVFymo=c3J8nQ@SFiZ1}j0n&5X=a|1@b~j%u(WR*U#MH7AIK@Z7fcJslc8 zv!ERq95Y!O`JkM1%e&sl# zTsjK+)po7Cw5=flG4rH{3^+X@szNX5UJHtGn#L&f?ZLPEg-njafNpfy=)xpqv8)`` z0^~MulZsgwE5jJ_9M%B39d^GhVeUpKMjjX7p{^$HNwK)gXv)3o4f&N!N!`l;65WD6 zHAa{Y9DvVTuYnkw4M9+R<;)M`FZtT%W3ISN%;5VSjPEa$+=X_Z42z|!^>h+cpSt$k~=fNw^&XKrw*i+8}R zF@*uZ8bV5Up(E#dYRRoHWN*+1^m_+1e@bWh6~_~8!v6GVEzlk+#7~4U5%H+g9;5dK zEWV;$!tiVr37`0#Wuan2Gm82xuTeSYlJ=lFN+=5$`v* za$TdxSA?orB!i?1bXehSuh@BkuF=91t%Mb?5PXfwddMq67dU8nXhK*IbjSs8YzTuq*p-kOG{4V7i>v zw~zo93aQFD3kQ^q9GybcMi2aP#UaGFT*dLjGfe$)5%)s!e^b^$d+6{EA7sGNs|2?G z=sS6>hHMSA2&57rq&2T=h@B_-8bW+#&K7vnJPZ{D3(@XSDQaiDB~pR*g@pN4V4!XhA8Kdd*-t#~o zs#n)*o(!_7FQ~6X$$TNu*CD3-W@i)~iMMQ99`OSLibhgCIEAmfiwtbH>#r}(^;E5Ts`49*4 zfXjMay(hDTw_cQkon2QN**-mH(qD?!H$>cv!2v@`J9eoQ5cQjnL^cmMH_6#d?l{LW zqOr?mrHaMYbqjHO3Y7orzD^ra`tQf14O(JSCP+s;vrAs%tErXrqU?)Q)ere5qda77 z2uKadY9+k9?)=&fv3GB^YVFUtZPa)}xSg34AaJbpxtzO5pdiNr=83we5apF5{hdc# zB_lZsTf5UXD`3}D;XwLNy!@Z3W2P9MtV);YHT%jC&5gV|PGAW+|3^gp^1DGMd0_RQ z0^RF@!>S~r#mU=Hir2c;vLge73fY|-ab`x7@0xaZ8T>EF}9)@>ma3YX=PQT;<{`^*_EE!m6Z+F{nEz8maP{Wt(38i26ck zGXG~Lp12L$CBzSJvpwY)#UVKHsbcW1r^~bX%&rirAH_DozcZ|h+3Fo92y;?|xIfDJ zK^{x<2-I<7B&<9U@A5?`7a|+$iElqfR_(wLB3%}*mMpzfu4qRV5=`0ffd2VxXozst zx*0@q-1-s(G_IdAxcdUKh687tJg_|N)}?{x5FprK zaIW~LG`ZdVD8 z4rPR^#->gT%=H8h`L8xZiJ4WbT~9$Q>r`~T8m#$<91z>{3OIH45nogQh8mIlNCCYY z3mQ5kHmgNsgX|^ka%I57L1hsL^}|G&RN@b4$|1tjc1@RT&RKJ^B9chma=Wfz1~A`D z-NDNmj|>Iq^gY~HH}@*g_SH zVS^A4T8Fe}nN=bSW$E1nXnsob)VI84{AzeTEHa87n-A?i zpgpBuFqm~;IVu1b3Sr*=W#^r|FNtK$KM@nFj;FUJ?#KdRadK%T?^5xlr>UJ4C$8BI z3bvE4_+r&#n@LS5wvZV~@VPLp?M>>Z`qF+FTF&_gRk}91y%e$F#w(6H&I=k3p(L6MpgSuguc}^4--uHE z_A?^W-#R)twje!-#-UR{4H@)7K|pK{=bjrXAtA=EakEUCVZsC$0gZAZ=3*DJSpr*z zSF?gcM6>95765ict_>QvB4OSvA(DTMOQ<4ngd1@u2DVb)^!hH4{j0cm)7GZSM#CzhYk-bCq#eKsbGhnhf6XZDn3Xh_n8bS9o}+`8ZG;U7 z=^uQVtF#0zFG|PfnwXE9GK4Z$nyWUSSLEH8CQfqF+7G&BdVU!uF|?4I34U_mLEZbO zd6#nPrsnfsIyhc&z5kjww2L$@rQWcHO6FkHapo_&FojGD$n|5Vn$HhZSWgZG`Hea8&-#k;_?mSNVh+cN|Q~@RxoB~8W`5qfsc4)=-`G&0fTOlkx`OtoGiMJ>trLTA~AfW9d9XbpZ z&p>$yr8c&sx_#4CGtz;j`+4ZG0GF$B#kaSbw$dOqnuFIN%?zU_H<$o_S0R)j!1@(%t!b4U|mnu!Og8sq%m9*j)0V1Un zh^d+BcPWm#8J=!s7=x9GnUTqi2lB;UbW^}ySy>#Brz~$AJYKc4$qzAc3Ild3&U)Fc zUQtld`0~9pl6fTVK=OGly=d^lD)mDHTI^wHJ@k8Kkj0)?+DsDp&UO5CTI2&4{W~&D zKCzd(%G62)O6Ohl1X3n{SQz%l_S)y8hNzPsV#|U%W>Yum?%ki9yt*Kl&CG( z1y5&bQA9!yjyFFgmU9E!3<4h;BLvXnyr^nd6c3Z$Xw;<5#(SYf^pM7M5zytBINCpm z+gWA^n80C#{t-$HB~QqJd{>Xj9{bFdhCVqjCnH646Sjgew2IZtbV%qQIDnb!Sh_X- ztZFkAunycPP3)o|h0<+yEJOee@A_~85LVAhPs+161c(O0;!)$SNwhES7zb-IYt)_a zRKH<1nw&FV)by3)a`^n=7F1W?C1A?K%EQVXGYjo1b;*o!lfv!*T%jT9P=U2U;pWLO z!hW&ZfH*4j=Z)AerEVXVse>TOA5xkOX+Jd~4%8;c<93nYb2S$z8ow)KxHr1>j!I52 zBN3GJ@sW#R1Q9|s*o(kgX3?xdkA$>NobXc{lx{A7Hqfp@YI+BfjR3M-wyqxVSZ-EO zbCCr5wVvMOoEeh};QoCv$x%f$UmHHamYU`23TlbbM{q28Z-bv>cPz08EIJ!a@WLDa zs;1K>;S}lK8mzF6h>PFo_e`0wgXG zST1K7k+*pLokjtL8{euBW} zTtbD|yl-3l&FdHNjvwUQGB7h;CR7Gmtp{o53Iak8^N6;O-GbehAh81lA2!!OAykq+ zXS>1@_DCl5OZNB4=gNq<|2CE`l$;!hZ#L=B5W2zzNqpAEe$rdgis;2u9r922vksYz zu8#ux;$fzh;xAK&rTmj(Fy2TSY(IpYuLg5d+8!6*zyFKy{m1*V{~ssp|KxoM|Ko%a z$Rv@`K56ki=R;REG^7!svVz|Ki;GSvlu;G{&HOESQE)1ZLS@;1N|2rdItXC7LOkeU zae({yjWdTN=3UUh^#40hmip^dRE>Bx_-&N}EuR2aITnLP0tsV47RYA)19je5Z!83i zVjwjEmfJ*K4&CmX^ns7pVVVz0CLp2n^n?a-2Sv69CU6I)=UEc$=}0araSyZJ|k ze8tK|{3d%z3P$fli%3KYl3xJf^ z>q&`?@7S0J)jAqwVYK~nP7|Z7m`0=W7KJ)5^pfI_C3fX6h$fj#B?U{ho$*C4rl(xl zElX9P^93g1kt0z?wuBIM;u7tlI~49S^~|+-r@EJp2j$0ajzavnGx4I1*)Av!7iNWR z2~bV71PzQc!h5*??61pj`^#1Sf2YR&BYXW+WB-x8A*nDB?A`A%^6xov_&|iQhP+1u zmra*Gpwm|+VE9H@7jX0y|77_r?3(lGHk!+w26zFof3*H(Rx>IWKKuysuK;wqWq!>( z!G3UmI9(qA*`L9;w4RJqh|h>mfWY@9A4G3B|9{^0{&$~50j9v_kFqN%AaE6Y7Cda? zi+L70b?3Cf=5>eAu0#*sD;w+v_j~GzNTtXA&&^bsNoDWNk4sdHOht20W2kp|o$Wyq zlA`{V>$UQKF>zQ!Jtdz4lnE(9_O#e?VdnQ8P@JU%b5p`J=FrcG{wre2qNLZS4^99E zo@&Es+x4%=`b=c2vvPn=f@#j*F<5FF=x~h6L;T}L5jFdJI%I-4=9*gs8X#zxl%?S* z;!6&FEYV=Ws-1P^Br@+Km$j-6^EIB{b;07{Da%E507)`axOxe8_CWO0MVOA&#U;+Y$nKEQ`OA*fX?UU12r|ha z2e&fqlfP;17x)4cZW^0)3TUzk{p#NUhr>;oumPMh)J+2pai3gEm@KX)7PA#K3&{l8 z-jx?l`$S(CN$YiCzzJ^_VJ zi$4KPV-V|y8>HpZZ#FaxyH(|J_1FgF1fTr3H&_l5YQMmj7FRPa75Pz6HQgA1 z`>0A{1qx>S7Eya0g5aZ@g zXo}Ax`Oge%HCo*Pu095LPsLj4h(xw9^+@I1@hY?eLfAAtYF1h=6hoXlxth455+?Hq zx|a-Bs&L&YcJow5lyJ;4cLJRm7TBRUEMMr=sZ0&&c)sTiu!)hM<*PNZ2}hI)etevT zHhi8idyew(M{I@Q$v-SKP50D;#E*PYFeL^tq%vMw8h zqFTt?i_TPJ)dE)O&~6Mh4q#yv5)h%i+5t1>AFdXmQOjKN6|*}uMlz46S5MY1?&E_k zFR;2x{3bl<=g?C>FoH*USI ze%c2KuQ0RZZ~N;IVkYnQdA)+UC2%@JpmfXBe3n6u$m~qe!X1HUqyc;rUz=l#|Lv_h z%Ozsj%+K`V&}$>2y$L6-w~s7AQ=Vq7%RR{OUXw3FTQB1-o7)%uei_+u)CNyi1;_5>e+|m z3%FO1%M9=t_xLs?c-l=y5K@6JB3RaOj;>@p*d2muQ>xbX-w_Qpc&mK~M95&F;%Nro zDV|5d(?A%uuRVc<(g{(&bWKo+!r-x3gH9xAb4<9A30=#+s4f(&wN*Oq!c}PO9>gpc zf3XC6Cet`^hov7AS>g{dex*&%m5JCKZemDHY+F;6e~h<{KWHl4BC1>-%?a5vTanj>fgum{HA5_p}I-jWb0t;grO-7w(X}2=Ra!GW7l0YX#Mvr z#ypm({JteJD8VL5U=LpkW=iX8cc|K_8NF8_A~#A}AMaS;P8|qc8Ge!%;vPhX*Wb;q znr%7V`qz(8Dt0*2sWbsLxqTCCYt)zp1!mEx00g?Hcy?<(xahO2Ri1fzZY$3TLMov1g3qDUeEYDrJ6MoZbMQXE#-!b7J-5Rtm69`b>>6tnB5&t zrK|%}iymGG)30MOHC|ox6$ZGcdT58ut|j1Xv-n1a0tbdcQCKed&aur^8992750b$s!3_| z0u@hxR3B22*WSJwVgy83!ux1K1}%xhh%R`0Q}e2BcTk)?>TqY2Z7hPO>T#~&%ol~5 z1T)#aYA*LJYw5Nbyt+{cLUo5H)$T0LLR_NOqww!ytyu^p`K(!IkD?@ODTE zK&~;nvPn5R*`-_QBMQz4p7OM7Z0MT=eB=uVxXAr@>I3 zd$5*v;2WF%F|jDLCOzA=3DTXvNkX+hUS)NC-0e?0?tvrIEUX2KgmP28SEbb?qxJVF z-@R>qx@mbD3b>yO>`Cv~l9Bf0IB(}Cejn-GKjw0=9fCbrRo0Vx#z4To<)sTUGDS#^ z`g{|ZwdxQk(q?a`YmxK9a5ZH_TA_0 zfmcahYC2GG`a#5*v~K{}AW5hEc)t?u=TsGDY1&#op8Y>9Lq5xiJjUL8w-w0W5)>RR zR8_AEJSSGzj&FL`PGSMRIFNIAw%Z7!!;2CTf`7##J zG7GLk9@W0M{-Pgc+aL{yBrp?T@K_gY;JNyR1DZm94tM6Hqt&J@;s5Tif?~CtUg{*7_{#b=po?u%y!u0 zz8(`*+*@+>2xqLu-iq+j+VS25G zuSGL#!YTFDR2XubNJWwV74ZNG3ueatop+qyJBg6mYmz_YIuuPO4%d!;g;WmXurNQP zWBS$KN$*QxW04&h?T=bxoKlR%e=Wu8>Ri+YD(baIwx-$_?bdN8MA9f%?t)`(U3yo9 zXe-aTq6tfV#seqj9PDtoNZ745!eHK}ary`Q@`kxwfA=T>RV_U9qLgN1mbgc?H3LhZ z@VmJpwf`3LIzfk|=}V^lO3FPIY9|uLCn6f3th!EzA@U6xPFkH&Xs2>y26d}{V-DgM zBP4^TuNkM97r6;cqnr-p)rY zPotZC0+cE#lAjJ0a|nz;{33Ku+fCHwooW(KzY4Vj)vSOwxAKAPu&TKcIS%+;rvlO@ zJb@9D((j2!4+0*?#uAQy_35LKjfL(~P$IVt`t`S$_H;EjNG5_llm<`+uZGW(t@N>_ z$_IAwY1FzG3RUw9Or;98$$;manfEX>5vBBL!5v~|js2`ka;<4L0t}Jzfk)_d9g)V1 zp`$;n1KOBF4b}lG#ohdnxNpJhRE(*(B&^iXU^`AYZ1`AQb}8tt$&E6S399NEVZE=? zlC|^qEaFL(qk>kAGq9Gm@s+WcWyM%;0$~`51PQ@eYO8S_X&se_ot5C!FGy=k4Dwc; z-Kj8APg21yv{xvO|Ju1kmDOALQMr@oe{IUYA^~{P|H`lG+Q!}mxb&Y5Niwx)iv(yw z&WsL{={$hy?#?KgUtQ2AacE_KitVq{e=TrFhwMc#nmQUkpVzk=d{saMGIeTI5Bt%d5-9?ednXnQ*oWWUh2c!bYo>BCm0Rv z_UWw@1*89ht&mGuwZ|mH#8&wnMfu=ZGu4jqt~6EjyH1!(j9tEki?tadGs4?O<%_Xd zxvN>Q;#+=^KxL-JrsZzwE9)Z8CGvnk^j3N%4~72%MZ;gvnYUKEjC?IvwSIen*r}oF zH7DDDXn8&p;1YAJ`NtWYSdNR0CqIoyw{;#pJeT^awdxZa^e?HI?gBJJ^qjRa42#R9 zX5h|iuw`kChScj{e{pXz?q{UMH4On2SnRHS2gZm9j~Yl!fB!Se5`l0gxc^B*E!9ng z$7VGD{$F-;EyFh@4w}ieEldzjPt!+*nW4I5Bebi;y}&67#tabp_PS>Xvz*yt#qm77 zv`Al5l<=9WKZ;|~917SN7844;2#$sn6za-xQy5~mblv5RT&}F^YrBN@S9J5B^Mn#q z8^d|fX4iKUq|FH~fsg!Pd!!zg(M>z}O=Ll~Hif%nARzWG?j5_SLfp=f$NG z75=voSrGGrR`YVYt9>s91rKEtNinRss`7~2&wyrStv^`F7WJ=msDsAFbMA6?BHq!R zHcxpUh~6UFxkgsZUMzj$O=eV?teDm5F_PN4#6|E_A0qI_2T9U_&gLSls`(ha$}qMm z%wkv=q=+?P0yH9I`mh;}k?L(o&U+x^j^BVv;u}y|Ma?lD4pJtdXo*PbgV63xyZsc{ zGmu<#;2zj6ymD*m&P%=Fn%}<&>Y{1dE^F{3`Zmy3M`rrHOImw$v#fzhfyYgPk91KJ#EJ`#pQ*>xwA*J$q$mF0pZ< z%*L1TJ$qGg>Ppq|ADi+z)lCVn{8`W;Q|^pZnG;-Tng9O3X0IM9&g{0wOc2Cu0i*RRRN*goECc4CgDwZ^xNsDjs2S0lHM25T#1 zR&qlX3Yn-r;WP5m%5gPP7oS_}abRbeJNzMRHe4BG#C|@Sk>j%Qkp6D zK@YMgzMrvj#dL0=)pe1dJWX3_*gT%4Yyx

IGAY+4j58Te+;7=MzCN7x+lF27>+&zffB5#5NcS9FGykc z;v$K>^!X=xphCB}PD`sK+on3eU?G3eSoxm<;4u5G^HVxeUf!$@>ODkJk;&1GGg=^q zzQc|{75(Ujx*9~s?8B@1w%VMk9*4P)9&1Lw;sIyw4Q4_q7+~N!{q(0a4u0}Itv!9G9 z;y?}80B_F#XP?_mP&)S8@J&g1zlZD*uYo=7JT^9}VLZn()1!dJLS@D!S;K)Op{;*c zC`h|@X>x@(Y2x`w`)AE+8*A%KineLZ{Oc>rzFJan(M9gAaOesk;`MG{(A3i6h=eK} zid;q2K?jpGC-PIT#r!Q->P0(L%f~A$g2!+sO!+nJatqD@hN}>a&IBMqFV4yb>Xdbz zX@IUpz>#x3B=nvFhna9~+ogb5Fq8J={thoy%&2B0oE&ybGlK$`fc#Q&GLS?!a9h3+ z;QL}#Q>C0o@4I8S`AX=RRoTC9@sQ#F`TMZnw|3cQ|Mw|$@ij@88sv7R4_ol^;y?E7 zf7yeLbzi>`^1VSnYEp^+Pk&Qde3PM~R2AM_B!Y_riwB1qYk$1fh(KUH1@a-0jH(!C zBz{JzfKC<%sYR;Hq_0zE+!0#lk9?SMSA0oXjToAx$gXd9*ybZd8<5O=d^rey66RQ6 zeJyIcN)$E74OIOzx6l{t^=`be$+xSw)7A5mQ-_Ru2^ET)I`s<2&jQF>vuo8OkIeZ8 z-OkJ@ctWY&#aC>+aVFZy{aSUkDlBIb@Bf0yUk#q;+3|L4G?wQy?Rya#_btGp^%UF_+JH0&Zp@3UN$t-; zGXo`zWeS>NvIofejTY^PG6ku-5lYqF&!nW3j-PnRtz&81+Nv+E47Suo7cTrD{Br=} zv-199wj^@>yfv%m{e9Nhq2b1QM^1mwv!OlZie=I30zqKFYM2(5N=BS8BbnwyKckAE zdv|rHzOdsK5B@>^*#w802~|B#bg&$$x#$syG;MANi5Qv_Y2FULeb0mB>}Q1ewmTYI zJZh8G*{2<#oKc`hG~;biXYX=>zIv@iE!wa0vFKgRg_}mqo>h?d?$SR`&&V3}+@$L+ zzPKwax=NzxD-<2B+|fkcUN`-Lxfj%L{>ye1L%3)adE&Hj5REXCp$W6q4F>%{`-?AE z_oKbZNJBJ!Z_tb8fOy`5byqrQQB0nAE7J}m=~F2;g0x5mv=q+;(svp-GA=Srk%Pvf zC%}@BYXI6&h|yI}bb~daHxM&Z=Q0KXv5Q6n8Zl#dog@)W#OK?12!_IS14fK`D68*n zlQ?GAU0YIDCm4MZXs%xE^U1GC_JM<|yrh=Ta6c;bN)zPkZqPWVjX=cclIY@CKZyBK ziHfEZK=Ayf9d2ppl9YS~tHCI;v}B+TSJN;x&=lN`A~n(ze0%3W#P==Jq;SJJh(*fr zYN-ts4onlJos=8Fm3}_9wLmN3&wFwKoJgk8D5u~HP!XW97i-R;eZ<<5`lm7m^eD>i zH}#t8qIWMMq^=5%@VQ@C0BMDrUM&!amLj`($ljeYV0~^jz@@0 znnF0cgFu+_JpzJ6BbYga7&`3$k7Y-xk-)VM4G|PE5#+7OtlrYmyjVnHQ7OjObYHOd z0`JcInP!D+KSiP}gF?|(w-FXGc{8~Rf+z1LvrI7oq$FGBK%|oV=!YJ`C|P)@0#^_M5Vh%z>lV^`L|* z4D-U;jDiD1rkSS!7OlrbC)Dq_2jtKJ)XrAzG3gI81*gfOr_U`0)7VBN8k)*yo`Kms z`oc@bsMf3=qP3s*EnCW>TWEtX*Hug(H*Mrf+Ab|)z?{?!@wf1E6UFbge(=hxi(g(I z3>;YPPUu;i-s7@=8bb#$A$$I7(Lm&8XO2sVPP~y2$q81gW@r%2^Qfb4L?4AY4%MQL zSq=3o7H4m8WKRSoxRM~u`P6GV=YXExNvb37D(F0O$+n{nGyfo2#pmVvfEIo7Ey4R$5U4fK6oto`(u7NG zU>*GwSWmz0(5Y3H^8}C+3REn?XwZud+%PwT+MwE{n{`*K`4}#k2PJXSG{=fR<*r6jY7(8mguqU5`F1{Q&(nLbXSxQ!W z!C^p**3Z0CawW`bUh{+Iow`Dh`r?yNgE)-`=h!rmhFuHN`Oo}#y3+(H;@tqg;wOxn z%$jVT(oGJ?fQ4{IS2V@Xs(p8_iI6(*VRHhYwF){^RH)x}{o9$tmtSiRUWd-)LB}z5 zIU{?d3b*40`eA#3AlcIA&6i{1y{^;p1kS^2txSe4;~=A!!`5U*mpBH5J@s73JbNiP zvnHSq3!>TWr*9vWC{7ij8EJAI)@qe3iIm)fA$#Q8ke2^Qhc32`jkJFmq0{72gukvF zdKa{RO>!szdKY8u+)X%oVVb1K^ZqAv!{d)Nm)Gm)K6noOu6;xX*oS3;ZPI26Wfzz0 z%jBP7ien0q(hJ`boK|EX9zmuRC6Rh_BmAiRZGEQ%K}I0d4Q9N5DW&# zCnY7l2S+)$qZOt}7HQ_={s}2#aSm@J8yGD)nlX$AtFgMryvId9FXYTByq#$v=F|<# zsF3`!d4{s(Q0ZK8bq0q%FB&X{hVSkMD?TTC^adh|cJ9LuKdghJlz4p~#ME^>7wMnk zgmX2{%<>TkOBT+2_WCUdN&Q(vN>M2O@4;8}H?XD*13_&4B>GSa>QFhdvWp@u2%A96 zycFz-Bk1&+f5?y_cfwIhTtBN_(E^1jqNx>>er=AT*|z{dkMth%d@xwlp2IeOk-FA)5=(=2ZOjHLUi5T z#%wC)I=1BDka`wQCEI$yzxt~u?g$I3>+l7{YPM_q>KRM^ZBfOtVf${~t1?U`9%>5J1; zVFW&DuBQ40?Ah9n8Q5TUf;_V0mP5>wzV+cqDZ8wY*Nf;~t zD6VngqL}e0x|p3TG)xkd9?O@v*=$_gTf70{XW^d*M?p=s25D9sn@r^B=P5dZWXVYa zOD;|~CrHe1;5-f(wU!lk1qLMGph*Oa*RZiBGSw_>O99eJd*aNqprAXfz$YgQwez=W zy=RtEeLZYh^ou3l0I}|`83=<$r^D_t9H(Sqd7;6g#(>R%35hn~?5H`&^icpP<405k zKrt%<-e|{Wz9<-|EQm4hv~#STh6a=(v8T!PF<6$q8YXjXu*LVcu5mXpFr8!ZZ(w5o z6j;6=xpPTi{1(rd2_zi?rEeH$f;odo=iD`pX$kx80e5x!frm>_Y-u)&JJad?azp(m z35iga*aAPyX@b4EHrO@099sF1)|xA>ax4I%!ayoL70&6K4y$kk$#qEg*ewPqDlVB0 z4i1zf8|ma!G(%Cq$v)1F_F_lx?y@sd%y!No)p}~CD4mvdVA{0L`Yl}e?Q#FD#<2p3 zN?Vv+b~;>ojuWnEl%X$1{a}I>_AV@gFCPEwmYFhfj^{&7ineB*1w3w4HyfzUVXzE2 zMHTz?5G|SRa?Uy@500Uh97&EPKpmgaWkezBg4J^U{!2(%xvGD!mCNeKKv|heT9D>AGhdW)95s`!tLn9U z`SR9}4t|yY&_LUq6j2{q8!Wtugm>ReNZ^i|S+m-HTJY=mo$3*!9i-;_g2aCpd2!a5 zZ1da+2d6u{9Tw!5Ei16%oDxT;X=}W`MDyGUH>+-=zSVo24iK^=%px%zwx&Qgk!}M} z%poF^or*cP3+ZcjGISH^HUJ6D8giyCkgH|5`|dF(HRw=jRHEp2B%DOFL$o$@QiBea zyxiQL$Yn6<6zlh1&`qS<05pF5RalzRANMAJD<@NilhR4pi`u&GnTuQM7EJGW>_p4| Y19D6tT?PRm^#A|>07*qoM6N<$f_}!GHvj+t literal 0 HcmV?d00001 diff --git a/assets/images/holiday/5/home_4_1.png b/assets/images/holiday/5/home_4_1.png new file mode 100644 index 0000000000000000000000000000000000000000..7f022435b4d6ed6afc0a21833cafa033c374ad7e GIT binary patch literal 5630 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 Y19D6tT?PRm^#A|>07*qoM6N<$f_}!GHvj+t literal 0 HcmV?d00001 diff --git a/assets/images/holiday/5/home_5_0.png b/assets/images/holiday/5/home_5_0.png new file mode 100644 index 0000000000000000000000000000000000000000..c81384a8078be465b1dc3277234b270e5072f6a6 GIT binary patch literal 5965 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 literal 0 HcmV?d00001 diff --git a/assets/images/holiday/5/home_5_1.png b/assets/images/holiday/5/home_5_1.png new file mode 100644 index 0000000000000000000000000000000000000000..c81384a8078be465b1dc3277234b270e5072f6a6 GIT binary patch literal 5965 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 literal 0 HcmV?d00001 diff --git a/assets/images/holiday/5/ic_launcher.webp b/assets/images/holiday/5/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..3b00caaa7c2c031f63d7108d632fead267589ea8 GIT binary patch literal 50994 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 literal 0 HcmV?d00001 diff --git a/assets/images/holiday/5/icon_broadcast.png b/assets/images/holiday/5/icon_broadcast.png new file mode 100644 index 0000000000000000000000000000000000000000..1a0d344fe45cf15824397e78b8cd834077660e26 GIT binary patch literal 1148 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 z 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 R`3v+cgQu&X%Q~loCICA{y?6is literal 0 HcmV?d00001 diff --git a/assets/images/common/place_holder_logo.webp b/assets/images/common/place_holder_logo.webp new file mode 100644 index 0000000000000000000000000000000000000000..12db5c8ed0ae88c9800abcc0ad9c5906fb80c017 GIT binary patch literal 24612 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°bhXf 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 literal 0 HcmV?d00001 diff --git a/assets/images/common/radio_sel.png b/assets/images/common/radio_sel.png new file mode 100644 index 0000000000000000000000000000000000000000..c0d41589d61c747eecba9b6810197c2661674078 GIT binary patch literal 1435 zcmV;M1!Ve(P)`k0G9GQ@7Fz4jXZj`(ivo!6dPp~rpa2Gt5=;G9b%-AJORQK0 zqH>|o$nDK~0CT3Igo8DYpfa}5T!9#gO`yi7+1WNx*}gDmiB~A=0@bUdQ-rMt>em_Q zD_BFyT+b~uYG4ruEW%dfIj!HJ@8U2tMe2VaEM@1wBGxRZzT_?vK^X=BL@4LxTNSW? z6@m8eWgZ}45e5hXTseQYc^e`US)f4_r-KcVBE=wft3efqL7*b{5h7r0L4H8?Xc>k9 z#8Q5~Y1CIEfp{wC*(&}V#}X<7N6&rTBn2>XF~auR(1^pRzW$$^X`?OBUNu|foCAiB z8Iv4ITObD^QqW5lTBNd`^6?)mmmJh!UswuDd@l~Xdo8IU(ZR{dY&jtx&QO%bMJ5dlYu|gON6qb zZYY(*0a5IrWq!!pAAvo@Qyp{`nzIeS3lj9!fA{~>=#fYv!LVWmr zKGd~tUOIw1ZybUN$|EjI+KB{G;V%*QVz>Hb%ME z+n~w#2>BVVRHR;|1Sf=ck$veh3-a(W7;sTpkwhf6oG{m7SJf^h1gHS8BWWi~6dl+A zLnI=FC4)@qYCLfV5!WT{WCD3)ED$3RDTpYgL@%Uc5-+0cFS$>nR`fheoy?88!EwhI zHI7+JZ3E@!TNflv<z5q)*8y!pH8SGc};=Jjt*P2>OrzWT`SnJ%7 zmIXb%4~26_?%zYT?R*M7>tVENu?r>G#x`0xxs?RE*5iK$45;68*C>&5h*8kD9>%Pi zC-=60sC!7@zVZoJFu+J4K4&vbu7-dW)8oO$>p}UCn#*M1)BT3~XnMMgiF}B2ubNqL z24Gt{2u~%1V+-vfM8L{1;6W70hB%CsC0FjqAh{d)cQ>Mo!&tqmaqvlYjxw~ER3hom zRw^Jzi8*J9=`u@uErGK{>!{KX+m~5lyAD;aW{c$4ZSe{fht-@U_8W(B3FRlMs(8*H zjxA$(JGP{?uU9fXzP8}dJ5jD=I=tBRqFl)ggT*>NpF`JJtmDHGICZ)e)p)x}39_Mt pd5j5N>P<;WNl8gbNl8fwcC002ovPDHLkV1g>7i1`2j literal 0 HcmV?d00001 diff --git a/assets/images/common/radio_select.png b/assets/images/common/radio_select.png new file mode 100644 index 0000000000000000000000000000000000000000..2bfa83684c9eb0f39a283ff99e5c38899c0a0cf2 GIT binary patch literal 1707 zcmV;c22}ZpP)@~0drDELIAGL9O(c600d`2O+f$vv5yP}B5UEIKZ} z+nt#=yR&!mlTPQvE8p$==Dm6IX7&MrKp+qZ1OkCTAP@)y0)b(n1&>KrFd^O|@oHlP z&mxsSPpMx=U6&`~wI*LJtYRC#h@V$m(}>|Q-U*n0Hc;*9WyuTdeD37|&(c{S?&rG-TimM#b()=&YJ2q26fiBDRA zf1|z^Z6vG+=It?|uZ1aVkLiotS8|kze;jYr=&1?q3NJoQ6zi z0tn+%ykA7qY2l*07iWLo0dnLIg7G`aG9>nUVi!QhP2sv2M6!uF1|zHjsBDKO+$P5S zGOP&^4sd2iYy!wUzj@h?35gPcWu3q`36IE$D#G9sse5IC#RCR9?)QReKHw1`#?Kgz z(ccM6D6$SgDo2hmd_>}9y-2h2fCkohxS_0QpN$vTdB6xxqG87W^|uimIX{l)PCSXd zuRn#83uE&C454(}$_wl~pkc9}4BdWj0(Uq5Z}9OwDONuF9g};vVStqv7)vk0&kPzi z`VW6N{($j`JL@f+zVrkP$@a4iF%Lm48GahWxureA19tWN{q5u-7?T|)Fv3UxIr+Lj zy}-uRl}B;;=3{-YAuq6?txk4mW9k8IGz5zK3)e>ruRHkCU$A5b2AB!Zpzw3zld-;` zhZq~(#{BG`u);754YUW)$S)Jy&%Uu~ngMk(WfF$81(18Z4d8aN3!jzmhCP93@@($4$viP{9XCXCh# z9TCcw8YuM4&seT@9Z;J9Ya9JPx~vFgOAQp(e8fb61Pttt{wchsNQ8<@4P{86No^kR z)}c*|j}{xHmk5+SRa|N)gZ8eNi2$o$fJWr}o9m^n?G}OFPhTp|_Z^fPil{($1~WRI zP{#PqN)5$mhiB6)i2Vsd9TECbXQ{#Dtf;mC>ii4KMBELB2#!k){fJ?JSsI$+1{`4{ z0>%B>N)1^v5N%A?0n2cRi3k*WYKr?gF$^#gK!u?g4$%?8gHnTW8yxeMu){EvQ&xlu zOAR|?7=8%j<6!EUFgl#;e20w64z%*^UVPEW|9EyC$ERy8F(<7y_Q4R_=L6=%UvTyi zb2qeykQd*cd3qZMU%Zbo!`(ABQrXyJ4u;qYP?;Baigi;Dt7@p6j8lA$VI{Tiwq^a0 z-Vi+>+>KT2&#~SQP+l;LVI##EFu<}QBWobT6pF;Xa62Ls`=_WMm z(1X2M2kV}&{9-ANVo(ut9d{o&_Dy*;AFxbNUg4;vrIS+;va?iLPa{+(WUE~#9l)Hn zt&|P#TDXWN-P7~1t3D>*4|WdGEQYj2O6;0 zfKFuln@or{+!Cw(?BzNQpW}@HIsbKH;S(b}Nam5zJMm6{Zd7XodOAN+kAY5L=e7We zdS6v1xar?9z58bLKp+qZ1OkCTAP@)y0)fDA@-OZjHrl@X@5KNB002ovPDHLkV1kBr B`TPI? literal 0 HcmV?d00001 diff --git a/assets/images/common/search_close.png b/assets/images/common/search_close.png new file mode 100644 index 0000000000000000000000000000000000000000..53689ba1fa193aac551bdfba4d8547afba5d2506 GIT binary patch literal 451 zcmeAS@N?(olHy`uVBq!ia0vp^P9V&}3?v1sWN!m0=>VS)SN8&+|2TlyUs*n&GR~49 zzhDNP1G1g!++t;I0V{PG7#RILT^vI^j_;jno6ls()3(y@f@rt*X_>nRIGjrIugG5r zZau>B>OhoO*^j3MjcVJ3=P&j;>g~77{a?l78_fqF=kAX=TvaLZzdx*C^ZH!5A8i@= zQWIRiu`n3>7iBa`o_~6Pv0|wM>&lB3xorj#5>Dsr1UH@#n#^J2e6Nmaqj1M`2M&ib zp;XQ|+7rF4%c;k=J?9^22E8|dn^=-RFCuLI-#XJvJF z9NXQ|vT*5~y_3%!?GX_cUAtGfoL?-|bym5({kx3+1|E<3(|E%DqVd($S4A&}@|&kGnSJ_Rop)T%J;&WQ5NL-M9=ac4Af2-OkF`t;)EODQ@+Uh0KlPBdyd_AsltA+LUmIP(- doUi{ka~E(mUl+K0jTac)44$rjF6*2Ung9v1%E<>_~qO4Z`pEsN?ZMXeS67shrp%rnsGhX^@Dhpvlnhi>Uqv`G_K2X%B*(7 zX~DrJ5(xz>mACdi`7vdcOzX{8n?2<{maYn(FRXpkMx|_b@=?EvuuvYY*FtYcCY+y6gB&G z+Lxu>)mILsW^U-;=Ip&sEyLs438Ci)mIY?E>)tF0%**avdVES-mPgvj11B!$?fqhey*X#whAENJZw|6{V_Q)jep7-mx*DTFp#hZAWF0QxLe50A2 z$-j8Yrf=77+@F7J`|XN5_j>#)Zh5%ZZ*}TnmhtKOYT$a?d1)$--0#9;+S(P>Wl7H+ z^g`b@H2XQo?kvz`dhR#(!-}(s(R_M;&1Qr$KYo8)r|SQXcW%*_+3FTa{Jo=|_R70@ zbHTr*f|_TfQ{in#?X-0_x!}3GxeOxW7L^;e3O@`wa^wI2aUcRoEd2l)C2W;uvCa z`s!pup@R+*t;v%D6O9&Mm{G_i?5ChRy~A(j#S9sx-nhl#T7}GyS=LRMVe0ttPnqeY zU9t|D>tyabsgsi=944?Cqu^C8pe4se;a%wK^?% zq_;6_WxeEmaS!*WYe_GPVs@7E-&_}!BqwND*>5@Y%pkT3@i{SeiGW9Wo$o>?Kfk_92^`R{x@Q_j{SZ=Gn>tP z&+{^8TzR=%=4!jeV(}K{NPZ)BD?tpfTCHlE&885G#m42>BEST2`Cdc{D@78Ef|B%o)IN~KJ$=okHGbq>Z+yWMV|9tB~JD2i?B zFfg@ACX-`CPuiFyh^VCK*Hl+yya(H;AI_LK>ug&}UxuYpOMkIyj%t>y#~!Pks8~ zPEueu0%=ge*|N)cNRTa18bwvGO2`yQm#f^lY;)1I(z_bH+$BMN z!3;CoEPaKG)B3p#M5jKR0TfR2ba4!cIQ;g~M$QHY0hf!piNeLcKCH&d(E%G+r3KDx zWjffl_bmGpCI27xvT%;I3bwKt#a3|+8)!S3m@ phWSNOUG`rIWiE?if>JOB2523%n%AuKN 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 s{}U?yoFmN6ZmHn!;;aQjebA?0MkW9K>z>% literal 0 HcmV?d00001 diff --git a/assets/images/community/community_main_sel.png b/assets/images/community/community_main_sel.png new file mode 100644 index 0000000000000000000000000000000000000000..4e32343a6289bb6673e89c4b5c85d3d3e3e7b09b GIT binary patch literal 2037 zcmV)~d6o4N{*_qCy6KCJ1)CsDbAo2;~E}Chk z3(F_4H4|sDv%Kphow0lZ%ZpB0XA$KDW=;_I1Y&;nRToKH%Od&!f-$9#1VDkJWaFFh zp9Fy9d%*hxWB`lBVzF2(7K_DVu~;k?%XNX|y-82O135#o4nt4`$86w_HA%Ao#|+>a zP|JFQ3!lIMWKh`0?;TKuz)Sq@867k37EPEYEHF5+dda_QaJ6Wnf{$Ra=_UVgz)~p_ zD*%_U{tP0pO?}wB90Y#AWp)M@CDKfg2YRf*q*IkSSW5zHf-2^!Ibk)TC@UQYZsGea z{4KzruRsL?d-(lKxqX6bsQ~m*owV1{2ewd%2N0Rj1}dXZ?&T%17>FhU&mI)He(aUMl zTdEok<%$dx+j4WnSB}$S0t){PsOCyIw#-~A zDL^c_Bgw@Mm$8DWtps)9w^igJRe++s3-GH8OG*JH!J|~X1xt)DzXy7qV1wrfnmFQuYiJr?O5=Prc*we}O3=!3 zC-H>Y*1{3?jwKfxV2a3w_h8AGLHIFEW`=b-uoV#CmyzNYhr)3Y2U;}-gf9ky9i3{Z z!$T&l+CkCym&MK~bn(|2j@N=E!5mX$>( zUfXYJRDGMWXz+_fj3pDyi7wl0)OW+peyzGu{>gt?wBR}A?|Y4U5WvTtZ+id11z@pQEEWqC_<)8box$@R&#kQ-0VVO#%l>$L-mX9F26_D! zQUQabowb$K6)$wAHntrybS`M{&CkykW~YwbYwhOhHcdZMHAE%c%~QfcDB#0yyN$6+ zo5XQg37QGrlXMxPGWujRKK}OMuRc`7`{$3E)OFatub0_~Q2)-4&oWErl_`MVe9gHp zK9gJg)AbJNjYt3O%a*!HxVdpx*#Rt-!)(Jl51!u6dULJ{NGw-&aLMGqGzf61ua~Wa zqW6yq$dvP3_5-5XPWI42H7SKMU^DU*XJ#o%ND3amv?dM>N$#iTOH)ANrdu@H-Yc6Z z3ITIlm9q$5FbpZlJ*H^f1eIuJlo*dgTm`c^W+Tsl&Ubv_Zq*y79+UK z)IqU=;D?jl1|{VFjL$HnA?$zm)3dHh11q@DhTP~12N{yUg2xv%Og(URh)7Z}vAYQY9 z*L;&VkbCn6TLtZ`_gNU}klKb2dX%}&;G?kXSvU^cMND6z6_AW-U^a%Ryu&aIeVvvO2I z=B|KuCy%vU7YcrV9)h3d?H`XjM1Cqe9}EoW7{lNaXM5^p^WH=S`~@oJM=|X#lfm;R zp5uf}5o}MvQ|J=q9Td&XzoY)VCrwPg0_5>FmVv{qdWD-JX|QAs%aEP}?&Xde4OwAq z2rdfz<0z;~;L}K)@Dp^xVX70Z%G_Kv6v9xhgn0L7i00-D6QOqf!Q$-8E;IiFy8>fh TTQ%d100000NkvXXu0mjfFCVL; literal 0 HcmV?d00001 diff --git a/assets/images/community/community_noval_sel.png b/assets/images/community/community_noval_sel.png new file mode 100644 index 0000000000000000000000000000000000000000..5455f50aa2e23f59fa2b1173ce43cd9a92ffa8e8 GIT binary patch literal 2107 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 TVd4>? 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 literal 0 HcmV?d00001 diff --git a/assets/images/community/community_thumb_sel.png b/assets/images/community/community_thumb_sel.png new file mode 100644 index 0000000000000000000000000000000000000000..33386fe15207bbba729553d9719b36da57e04f13 GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^=0I%5!3-orB0pXPQmz3$A+A9ByMDx1ov?2@;oo$? z4Bhbm1_9r6!@lZ*b#Ri^CKyW{OG8SK9Gt|CUdgbIyKww=>o3^p=jZ`}i&_sau)%X4{q3G1Ip^ z-mTO5f64XJYr3Xdz10r;KX*>2UG2rr?-5T|pEDOz-r2CTBmcpMvR?74Ya*Vv-n-JAknJIJhakCD%FixJmT)U$o}tbP|?z21-N4F$}P*t)JLKRG<_ z;`R3YDb7dT9?IX_P&m=)v65Iuj0xj?iCw`<7cu`n|JW*vKQFLJF~H7DLE#uv*!Kuo zo=OY#WzENI&C;ySm?>v3UUbRH^Hhk0dDF@XbK|D?&AhN_>&$z5m*tylmYov_c)kJ{ O4h){IelF{r5}E+{p1s@v literal 0 HcmV?d00001 diff --git a/assets/images/community/count_msg.png b/assets/images/community/count_msg.png new file mode 100644 index 0000000000000000000000000000000000000000..61c161aa625826eaf311449b355eef21c6f73d1d GIT binary patch literal 479 zcmeAS@N?(olHy`uVBq!ia0vp^P9V&|3?#2~eYgdr%mREuT!Hj|(!s~%V}F1;g-U|_ zf*C%^%WaLWe9V8vHfwr^kKVE)Q(D9`85kJdJY5_^A`ZWudb_Aafu~KFH{J5bKmFUB zZ@bn$NVDFXvAiby|At?@Y0Ql$Q$7jVy<>ZSWAT1*0j`3|oDL0_eje$ro4?q4d0O|} zdC=^v_Kl~j-jH#gP}bgR*V3F53JRMKA8b96#i|rkFWvf@!D&Twpz>{()w3J*Bks*^ zWn8p8JdVZi&zE2NBB$M6DNl7jHRlOe-jxFfi#}aVweS?t75DpRF|}*5nD&~7;{8`E zPE7gZ%=BoVogTv`_o}Dz8@=`acJAAd8|bi4Zu2$nPlxxFvo}oEiur!(<9l5NC;oTw ztNyB+iR{=k>(<%R#*MueI<*J9IJM^5TCNmsgTe~DWM4fPPFRY literal 0 HcmV?d00001 diff --git a/assets/images/community/count_msg_gray.png b/assets/images/community/count_msg_gray.png new file mode 100644 index 0000000000000000000000000000000000000000..acd863e02832d4dbc831eea6788e5f1f1c496aee GIT binary patch literal 479 zcmeAS@N?(olHy`uVBq!ia0vp^P9V&|3?#2~eYgdr%mREuT!Hiq(gC~o?L44Pp^_lK zV1`ffa$BP-AM;H)NbAl(o0owKV61g2Lv*2V0M1u_^`COSisea9R-^sC?UH_3TFdh&3VF=cjdsrqEA;-Ej&eZ#r^(SOzoO1roHB&c>mRk z6I1>;Gdv zs=w-HB0DzCx^?!nabvHAPVE6NPOZ7NmMeuDd5jYG9Q2y4KJQGnv+2(B#>T>BhMa9{ zW*_Xzn*3ht!nb`9nnDxp>ZdOjR$bMYVDqB(KkrV*!`}n--*Icr$pq`njxgN@xNAIK9-~ literal 0 HcmV?d00001 diff --git a/assets/images/community/hot_search.png b/assets/images/community/hot_search.png new file mode 100644 index 0000000000000000000000000000000000000000..2073369e6223fb9286a8ba9ad2e3b9f597404a72 GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^DnP8t!3-p2n|AX9se}NZ5LY05+f?qZnfxtt`S-@g zcTDB)n91KUm%nGCc+*7gHjoV#xdl{Uq6p%eD*)Lyfm~CCd&W|?O~Gt4uu2oz+a@wV zb8ea|+%{3TZ>kMcZ{8M~1++uHB*-tA!Ph~a)nea#L8bRP_mkCLURqX_z_oGqgoX%@ zmm3Nsg3qe+0+rtIba4!^IDL0gq)?NA0IM-)i$lZ|SF!0k|Lb0rGMvHl@czH0ufK|j zeq>kCTL0QzKG=uLC95o4$0FyDwa_+?Ym25Rr_Pb8`E=l=X4uPxQA<@hw0;>VEXunp zQhibKzKL@ERF##ki!QET^+okZfokWfIWD&Dmp@vrDDa-C`7M0jhv_-Rk>|d4e2VrJ zQ%K{{JUHLV@Y>n^uY{~vfB2{JO*zTY{-waV*wuPLa$c%j9lHohuVzeh6?o^v{BD|Wd=6i~+Oz`8KGa53}y2!Z66l|7qz z7d-A4VJkJ5;@jYN*tkrHt>HjNkzNa%g5&Or{uPlI7)+*}n|IL7X0?x%KxUj^vBi9@ z2XoaJ73K(p*2-K;UG}G@;g(V8#Rq!r;Zt;WMso!})xUnmYldal6aBrKkt;&JUt0f# Z^KPfs^X12{mjeTi!PC{xWt~$(698J{(bE6` literal 0 HcmV?d00001 diff --git a/assets/images/community/like_count.png b/assets/images/community/like_count.png new file mode 100644 index 0000000000000000000000000000000000000000..4627d0b0ba19db0040e01356920304887992be26 GIT binary patch literal 501 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sW&u7Su0Z-f>EL7Xu|Ghaf+azI z!3+xb9o{z-?4K_%A;I8$|N4LfN9(sSFfhh?x;Tb-9Dh3{vWZEN$E9&Y{@tDbQ_~YJ za0t#}{$VHL#OlE+8lSRB?O?H#f@JmSxmGip>VK?bG+(B=_v}WgPN_q!EnYG4x0}lB z;#R%?_5G!9$*%VsUe(?{-ez}B&f2iUFY9F8wb}dZd<_FjKbB{ym0xRc-SWEagHd^h zpU<7#%GC@mQ)VAKAmQWwBT(4A)Vp0Rs3N6!*V3{(PWFp_8a_L*LZDP)=chmg(IcFx z5*3Q~o+OGy3q88JPuVi}mYmG44N)P_TNYgO*D6!J*S#U;rQCL#$15^U=G@()vcKoy zWVx!h%@ul0_VO?8+9*AL{`lLmNnf+xEt@u<&*RPa!gUU7LajBH^M1l5(5KcnWu|mNW|f{(*h?oD+suxO1LmCe)g|D zR@7_D64|ov+M@U6k8H6$+hD@aAK&q;RkMG2?L-rNM zPxG!!3=?_TBV-V^VZ;8pFI3Vx%FNg08?67afeFN zHMu-2&o+b}c<%Z7+~Y*!OO0_0LLE75Y|~CH%?aEmEf~gqH1%chL?gbNA6B=loA`p) zBbiOj;iT@1&W#5AJi&=}?V5|OGEKNJU&WAt^H=Pg^@nO0x_iY$zr?JV91(rG`I`O~ z-8acswrz83N|4`uqgY97z3l2$4qxx~@r7mv7&Yde`II9Bpdlfd`Rud_Yl>JYD@<);T3K0RULk0g3|S$k{&TpI({O5xrISsL!%M*F|?37#NE^T^vIq4!@mtcUrRoPm3>yJyY2I|C!-O zgTK7+DEq!@`8nB;V-J-Vmi5ckF6=o|RQiTZQ1Z&@*!j9^lk`uwxGY;Vr{~y#=v$q^ zNw-d$`&N1L-n`K9q0ajA!81ntFS}e%+in?B;nj^6 zCYGXza)sk31^OQ+@?L6=dT=z6U&cOd{q{|ZLZ4(y{wyblt zz7y)QnfKQ#M%kveX`*Kun-;YST;`BzIR10}^^^w;pXP1kK3sC6`4FRVLW@Ze=cHF^ z+Y-F4nm#%E?sWR9iY>Em`YgE-*}{0*XNx%hUHL7wudW*Z>-S_6oT4_NDb8ruyNiqU auknX4b9ja4&iN0F8wO8TKbLh*2~7Z&$nbOk literal 0 HcmV?d00001 diff --git a/assets/images/community/link_copy_icon.webp b/assets/images/community/link_copy_icon.webp new file mode 100644 index 0000000000000000000000000000000000000000..30edd5b9f3de7239e10d682bb578f2b59bf2c66c GIT binary patch literal 2668 zcmV-y3X}CxNk&Fw3IG6CMM6+kP&iCj3IG5v9Ksh6&!M1=q^NfXf>DbZlGlWSwrwQu z;V>M>pWBr{5F-BhKL>+0l7t)>_<09N$I44U0TOOb!RF2bKL)`d7zhyzgbm#?Kd^&{9k5xu0D=+#gpsKhH&S?>>m8tJq*Qd+c=T--#XXp zHzFng;tACQv@_TRhv`x_Fn$}`7!^a?i0A?|BGE^UvI>|RaBRzOx{hQgj`HC~U_j&_ znZfHzrb?>)i0D5fwrv~P1=j6-m!xg(3p=*0sM6tjZ54bnghT$330y||JEH#%+_rHf zd3(7_4!P;`0ZF#4hK;lc1zWbtj%>x!_pqnl|H>?cP!d)3FrxnuC0niAoNFhkA1a_2Rl|F*O&40E~O^K&OgQp zYCe}pltEGsJ2#15Wq7p+OFe|nPoA7lO5qE)Jq?gz=P(ff9#6RG?YFTQetIWv&!+Rf zre&Wuk0Uk0({H~ybUxE%cslE2=QN}562!P!#=~xRA5*vi1hyjpY}kHxRQVo{#r|vB zJFE6sA6j<0eXU&QJl#!!t4p=>o+i1Qnq=-1``-y23IfnxFek`O3}Dl_{51mVeN_Cv=+)Wtc+_zFn!?)bluH4MGI5NYndKGm8$)djgP^qEUi2*(7ubg( zUqD1%;_$XG^L_=wv2IKyg$5nNaa7d{DOA9yi%Q}RC_kPnvSZ7@r=5pW>X~h)TFAX| z!%ajBrO*Cw0+n*WCK%Dlj1riF0#ILpuL!`Pl+6Rw<7da_)d_49aH|_ApGqp;rbpz1 zQq}T6_j&+nvbQ|2SzB1(Me|@GI>|3TpuIpw+eSo^n9_9 zh1gZHOx!sY))sC`Zzclvw}aZG)x-(6OL?hq1WD+Oh!oIXptxmhT`vu`gm$-X%9>z&BWE>*@`b+^{LK2G6XhXa z!u2YKIm6bl7}3}*R!WN$^P)ekFK|@p)*r#BKHd(uii6A7Jkv?^BHa%$6hDLp%q+5> zJiiLFnsT@btHSCjZ=kEPh=fy8>Ie-mtXRz=5&d7wcW|ri3aV_rABU=mXEVcCmAk3i zQuj`E%sN>4Ui;=3*lfN9t_wheE$t_T1=+>T4`2gA(WEZz6hcv_X|hXmqjrxE z{bVb7&U|F3_+jo?v99G~+(EG+^6_iGTjEzj6;@#VdgQse0aASvS_&Bko>)n{vQB%h zaOzS3q;{Bjn-@iV9GZncf=kCP+U#M4{YyOF!T3zzv5fUElp8ef1eTtQ*##?@gk*Za z7r=Cm zvcv0*XP#??3wiJuhg&rFkzXNDZ#&C54l(&)Awo=y(g%)Uq1QOM;Ie?-@)o!+kgIVq z#?tTyEpptRSU8&FEmPrX^t#V;>$)BSmf20>^HuAT%vuHV^6>OL{!EsM z38msk2-{n)b%^O}ePM7~%qQ>=G^X5DG}^)y4|x zgnTQA#gQ;5>9ek*JX5i$De4JvB~8v%9mrTaeDqr_o9$|JE7i9Mk^U?3XukCn0cchG zMpMZSAc;b=+WDke?I;ip^7qq75W4cX&~a%IhE9O;>4=c*-LZM^#)lP!jt5)+J0brk zinq`Nz`=rc*EI*$qmA=UXq-CfF|$6H$sHWoa3o1MT+k8^o={A74B8ZYS0Bp2B?^Y1jE%IprAxX;z@Rv%ozs& literal 0 HcmV?d00001 diff --git a/assets/images/community/locked_white.png b/assets/images/community/locked_white.png new file mode 100644 index 0000000000000000000000000000000000000000..dfb9fbc4c40cebdaeda83006f4be3d9eef2050d5 GIT binary patch literal 547 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~eSlAhE0F$A0dU^^D+_3ZNJ)@i zFoQxu!+nAG`zOpVIKRNbV10jnz=o;+#2+v)FxGpzIEF+VemmoCUb6vD>+>UKiVK&% z{`2qrF4HZqmYwzUnWThIXw%c=xQ{cD zl|403GTFymLq5F7;?mkzJNmY?On6aza@v~{M=Pgxf4bg%JnGM6^NrTe{Z{>GW}cG0 zdFtzC=9Z|{r(3VYFLmbi;QjjG^qb>$!n=Nb`*tvKDevU;`8;W!bGOQs2MC)qEA=1V z6ruI56=xg+=Y5zJcU!{HVYEWrIvxl7Q~j>7vte{E$~Q!B1^>DrXx5f~_?wJhwGT4`3+)B0m_JkR?QO*A$B n^D}S-CyF)|FPk3gzp#AuX8U7@f1Zd3MiqmntDnm{r-UW|bGi)v literal 0 HcmV?d00001 diff --git a/assets/images/community/play_icon.png b/assets/images/community/play_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7ede1d6838ba1689c4a6ba2c1dca3165f60c33bb GIT binary patch literal 814 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 ICoq5j0DbTNdH?_b literal 0 HcmV?d00001 diff --git a/assets/images/community/publish_img_text.webp b/assets/images/community/publish_img_text.webp new file mode 100644 index 0000000000000000000000000000000000000000..64afb7f8b349b57278a65119f7a1cce4c11a5754 GIT binary patch literal 4318 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 literal 0 HcmV?d00001 diff --git a/assets/images/community/tuji_icon.webp b/assets/images/community/tuji_icon.webp new file mode 100644 index 0000000000000000000000000000000000000000..e3b0793658f8aec921e056ad4805f249ba91ca1d GIT binary patch literal 360 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 literal 0 HcmV?d00001 diff --git a/assets/images/holiday/1/home_1_0.png b/assets/images/holiday/1/home_1_0.png new file mode 100644 index 0000000000000000000000000000000000000000..ed90360ce9857c124727bffd9f75c397cdeffd82 GIT binary patch literal 4188 zcmV-i5ToyjP)#Mr6?hCAzGzE@0clrb$g|O#7oYq#6zNkyhzL5qwp7 zv%v1&bIwe^$ILnB-rbdZFT3Yf)T%oY0z*^v{5JtN3z&-f>z(DWXr(qIdLJ4&1 z?JK|HQuio|#vIxv?vb`0wprX~4nn-FUqSk6`eUe^;u5%b^ zxqQpUp66i#p)LZ=U(&rD@tFfv3YO6Ws8*mJ?5I8@Ng8+KyyDFWGe%zmQ9-ry*RD0X z4HZ<^Kwr-@FhQW}>^BWpP7rDTC@ygq3GO06v|`jdiR15LKj3fa1BtaR=5KJZvzEu_ z{b){`y(32?&bLcj{w{O63>TS`aKLX34|Xo?{xVDu)Wt!acdq&ZZf_ZO?y%M(Cus6f z&c&v4t2!1fPv-B`%23{qM5w5Av2FnW|6|RZ4~$wT>+TJ~=!PRor#B5e{_qEIl5kQ= zdfwu$enj$WdJnYVy^qm1BTj%#Ksw{_5iUxijQKE!xA8d{7HO!tU&sK%j#H+vU&@NE}K!CDI4=v z)i@<{(OAgW%;uIh{=V3>xBUc85a0IW zwryT|2)vTc7E~e>-c9CjkoR(1^qozQuKBPQ59HJ*aI&ka%syLBTi_lo%W|J=d*oBz zJg{-yKcPv8#%s8BY1i_2*!@TuCF|01BlvFkHFszi zUa3^Z^d)i0UR+^A?tN?Y6D&{3E&=1`8_DlbxftS#%dxlvnuKvh%sTiXTMuMc+z_#L zg8l}ww<6I=f$Bjllp}Fs>mvQ3_^^eMck0LO3Q`*DTsEHGRlX6 zx74X5Z}a^un`xZJGg3JHRlCc$WP<(vEZe(~|A;jv)B z%Cl@_ug8V1!_Uhz(^a#RDvtvjJ4(lI+^}K8QD_jNdOI4WzmX#MgvGUDUxT{vG((G9j|Ab@~-RSpad=@Ma>3?%(Kh6L_$U6 z7JB2(&Ig+UZ1h>BDG@x+_m10~{PQv!MHL4PK&n$DJBKd5hWUsBT*bJs*DvcKhn@#b zKwTV!gt%Ifk|uO)fpfS{%Ak~kLMAS8Y*C1ltbF@=?uR9kn3b)fl1{lE87m@hjFb74 zIHO_B3>PKef>>-Q5>LgHRU-;Rf2gdCrt_D63-lECnH@cx|Fz5J7Y?-3!Pyuh<#p z-7Ra^9$UEVXJ^Jkl?U;}r(uxk#gq&}cc{9@SbN!D$@@{C;NkVd)D4VHd zE=ePzrv%oQtSU*_jW8GOf{7*&ESoeP*4^6YVMPCC z%a)!h9@)J9fxQ@czm3yg;@I08ZDcTQdnHbHnzHHjsnyrdThg^xEB6)>YUv$pRofm02wYsKV?sc2BBUnRzl}BX6M!Bhm1aka&me;vbH{IU=~wyD*+p~_O07K z-o$wNsU`71D3!{iILNZ{RcW4|wx?mI>(E!cj#tte@v>gQan3Pw8yFu^e3FcGZsG~_ z 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 b-X{M5TCi%2{E#<-00000NkvXXu0mjfh?zMd literal 0 HcmV?d00001 diff --git a/assets/images/holiday/1/home_2_0.png b/assets/images/holiday/1/home_2_0.png new file mode 100644 index 0000000000000000000000000000000000000000..f33266ce64e93bb1f3dd66ca6e4be489dec693fe GIT binary patch literal 2652 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 KMNUMnLSTXg-Q@x8NbgeOa+tpsz&PVkl$k?Qu zE{-E4^vYC?pcp-WQ9?-AJ5-`>1ck0u`iy&2q?$m(@9eHM=Eg?i9V$_!i?MXA=eUIZ zriM4F6G3J+xpua}?_GZRf8cDMX=-^R@d)z%Wv($H8oJSa^HFC-wfFskAfgcz8%Y_O zr@T#rIjrz;qAP}M7hB;2sMCpKC=D0)Mq&c_=o#>lczJ#l*mu}>ts#V51f_YNDS=wh z-zDaaLM@j|qR>HrV8U6?gqgHC;UQL{Bk%ovjVihRX zY_{OQ;3d*=b)%Fz(=2QPc|T&Zhun7JT;9lL6E>$e;)|eF(PWgv^ctiF5D^6cTo^H!7OV$I0j)aO99sknazk%TYfn zt{as_P>$!J!S&2yZ7SCj7I zr#p6#A=w*|BdB200pa1t%gKJ^;aG`1CdnJoBghm@M$bQbVy#_p{k=&k(UJs;K4OdO zVMFysvo1fGNdozurD4y`HHfs2+h%&o#bl%C z_wQH-3e?*`DZp-OFVZ=(O0IE<*Ckcyp_q#vZlZbt{~V(zilQirq9}@@D8>8({6N_j TgEcmo00000NkvXXu0mjfvjeAx literal 0 HcmV?d00001 diff --git a/assets/images/holiday/1/home_3_0.png b/assets/images/holiday/1/home_3_0.png new file mode 100644 index 0000000000000000000000000000000000000000..30c39a4a71f6f8b68704331a9ce1991c28bdd0b4 GIT binary patch literal 4376 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 Y1B`Ql62Yshy#N3J07*qoM6N<$f<~}SXaE2J literal 0 HcmV?d00001 diff --git a/assets/images/holiday/1/home_4_0.png b/assets/images/holiday/1/home_4_0.png new file mode 100644 index 0000000000000000000000000000000000000000..3b77a729ea360a28bc8fdd3d06dcf546315f5cef GIT binary patch literal 4435 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 lQzbr literal 0 HcmV?d00001 diff --git a/assets/images/holiday/1/home_5_0.png b/assets/images/holiday/1/home_5_0.png new file mode 100644 index 0000000000000000000000000000000000000000..b9f013b9a2d694e38cddd64569dc40da9102fd71 GIT binary patch literal 5224 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 literal 0 HcmV?d00001 diff --git a/assets/images/holiday/1/mine_agent_enter.webp b/assets/images/holiday/1/mine_agent_enter.webp new file mode 100644 index 0000000000000000000000000000000000000000..064df1d197f56ae487e17cba0bbd1858b6528090 GIT binary patch literal 4030 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 literal 0 HcmV?d00001 diff --git a/assets/images/holiday/1/mine_ai_enter.webp b/assets/images/holiday/1/mine_ai_enter.webp new file mode 100644 index 0000000000000000000000000000000000000000..0916c7fe1482efd3bf1e6732292158d53f888f23 GIT binary patch literal 12496 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 aOO$=&q?Go!U7%lNdWwzRrf&L%zyJU{>mNV> literal 0 HcmV?d00001 diff --git a/assets/images/holiday/1/mine_gold_enter.webp b/assets/images/holiday/1/mine_gold_enter.webp new file mode 100644 index 0000000000000000000000000000000000000000..84f3681cec171707088c711c08076c0749786456 GIT binary patch literal 7452 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 A*Z=?k literal 0 HcmV?d00001 diff --git a/assets/images/holiday/5/home_0_0.png b/assets/images/holiday/5/home_0_0.png new file mode 100644 index 0000000000000000000000000000000000000000..a35b2a94248ded4d63b80839e9951ce940840687 GIT binary patch literal 6668 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≶JSokaj?wIvoUXUeossY!%V4)Ta{5GS?5=Hiq&B{!A? zMuZTlG3f#DLa)Le&&tl8{aSq^y5Nnpv#m+{j@+QEtaZ) z_05Rws6zCE?RFtv>D8&xkkqV14Fw5cQ$-cl@mtDE*`Ra+2FXS!dsNbmEGt$RCfj6s$g0lS-d+eZDkxNmjxaFYKBrq9z!*ytVWg}W1dz}De zcKt}?g^6|u{(iBYLxrT)q_&jDI@KN6NRSW6RiW%`#lqy_bJ`cP=hV>D^wTW$qX)v< zerU>;S7vT{a6XCv3a+Cv!(nJOizry6WYkSfz=5L?dA}9)*W3?*&9X^l}+Of zloDi+m)cCm<+BM|?1KSj=MqP|x7ht3QK}F%^fesk1r^r?B^#JD@6t~55z=MAbYAG0UdL zP5g1)%vNMA?gvv@r2xiLx#6p#bwboHU*qchxKI)eRh@`z}uc4xR zm{`_hCdrw!C$7Uv%DKhEbMB3uNMpjqIH+GP z-Xas5NHoJS4FT$H9jU5^50b8I-maPs5i=>GWDkKiS7f{UYHF6KVRP^3QcDRsWjV*U zkhy<*GrmA@Qj6=VaqPfx(zv3Gx%UQd$dGd{k4CKlOqNwrBxZ6w8gxn97A~)TY}U_j zn5VjNMU1*6eC>LLyD7I&o%Sde`FDeA*OJ@Conr=aN2W)Tvpi2T&u>Lf>k9=ZwXp6O z@1HJ!x6NB;8PW21#X*Dr`5kIPlE-toU`DC_?DcT<(l2pGHZ|A+`fp_4^Rsadz!B2b z0R4C5BySn*zj}umk@4}0ssu=JT7PyXSN^TcQgb}Aq_SVK;8XXZoBut=efScF$D4lmM>Q7x zdNiczZl9!*x!oh@J}K(M=S*{YiloRL(LFOZ^Xgl9WY6@+lfU(d|NBCBylMYGk;K0@ zAaNC*-_BJ(_U~(HPyS}02gtbws6$+ZIZ!g7I=sEngQQ0QR9;@LK+dC5WB~58f-LkP z=@9^BXUD1X<8#NN5=zFFWuga3j{wN4J4L?XQm9NzMWg_Fkn{+E>Ka--lw-IOMQEpg z#>kN>dWiH0fad;gx*JNiqG`H#moMFUQESg}9z8(pV0Fv7-%hro?`&GPa@G*^Ao>5H WSY0wi`9G@w00000VbLPxB-}%1hyzldF z2Z3`hlK89oz}o4Y^L?w{c>dz#afW|MLY6Ooco?^iE*JZp*Zp_$twITOV z#h&+bv(ta%azXF}{F(i~J$>2@ix+=z0{_c}k+)hq z?dubAQU?YN8ss>%Z{g)!d%Mjz@+>!7o~?T3(OWJ=r-yg%y*FJHjc$Jp=Rm$t4k|A% zmjPMNDjwX1q{-~wy<2Omu55r=@8wb}c(~fPb@Psc^czpjkH^pykA^t@FUT{`{B$h2 z^DWQ+?gv-?@IS8VSM$ld+1y;jLzO=@u|s!ruKz;&Y{SecFFk(qfVC?gxPi{! z`t;L3D8xCCFPejFlD=>Dl#;72A9;D%W&JOK)ikXw+-kXAf0jwB_c3ewI8(o5g5+^$ zFnCKIT=mA5`t_ewzVgT;uZEG(sQ)!2Eym`j`R<@i+pyuOjL`)R6>?ryrnTrvF0J3q zcn$+Q#$isqg0u10FzDE&24|++z76hwF@Cp5cX5zdj6L}&y?VLl%(3s2T8#GU8{f|# zH?HtF&-P{NFFtKqYj!gu%?Tr}!bnpgy}p*Q50@LJOCG}cpX=7mDL)C$5B`2U@!73b z&>e%@X7KPZU(W2Zj6cF94TE8{hr*F}D-VVi<@f5-nE%)<@3k&{+hc6}%tB3zBxmPB zk|l6P)3L<5V^E^aVAqZ}c)x*)H^a4O+imk77+mDL+u>3A!QqIsh2=*AuO9jholL|J z6Wt`;g8q#Be9wZZ&f3s1EyzrZo1DW1i%HCcsYem6jB-n&$S_NhDaZj+6);)0ujQO5 z#i?Wx>33%lD=z{CllNX>ZWBn zB__!nOtTocL7SyQE|!^qgwaAlz=%&q+CyN_r_dx)L)8Q;NP}fkgiXg|gs6SK#KftF z8Rd%NkS{_vOLuUPG<5la+HDn)8#04VnJmT>mLnD?5g3xGGAlQo zPEH|o0s$MWq}Iqutw^qqCmoYekrbsf@t-(HA!Rp+YL2%vcTonaCyl<{ch_KbXf6-G z^I=mwxt1>XYTbprO`KeSGJkwDv~shPW%+Dw;r2}vf7{DXK7XEC8triL#U|_I`2;9$ zkJ-SvXHVo9+s{-LvQm>SbgEiwbL_74%m<%EHxPDrn3z4u1v6%-z>2!|TP8ZC^K&e- zp`G~00M~U`x&h13!A;sX#eQ-V$L-(a_GW6DeV)}uF`{L{`}DV=lwZK4a9q*rPr#@> z!nMQ4lr+D(pk~Lc>(C9}tgA^{0B$Z_Iz|54g}hQbzA4|iZHpnR2b6Y?2l4$)U_qj1 zcfztIDfxU-CPf~MbCZB0FJ@VZ|7@EI5QfX z5X@)4rV*B)i=`_8IvEU3nNnV%A3aoLZQf)kudG%0rA6@l@)occ-$wK=pF*z*gGXQ| z4U(5s&MB%v^j)XV)E1+`bn@>(32AqgLA{^^MyMUw@hLz>5Ba%OV7FccHedh|y@A$a z;Ye~GT^eC`6$jbiQ0bKY{KxXXcqmpXZQH7|rEjam6krz*1pC%Zs&$0GqhvaDjnMYj zLqFIE<4^;P<06YERNUmPyyW4$E=WE%oP+Y?^90CaLfT>I57tt~p3Z{4ITOINE%BNC?iU}!Tv@hiu| zJM9ukL0{q$5!?tf5;kOt_(&WU9*ieuCEI_2YcUwaH^SrDwEbx%l*>oKXpMq}+hEuE zHOrttOL6q|FJqo3zIp1ra29g5ZoUH?{gItoC^7^)w-GoiRZieYUXIw|0L)k#^o{!v zeQPtcT~#oTpCFBiBq-G+uVtDx2@Ak@oCvxtMp1MNVw)=v|6nha^K&5O`Vo3)1zNwi z8J;0wu>M8htpt#`1_q&L7-pL`^uc+ToL@FT-M{$F+T-W~IU5a1T}#=)V_USQ&;{7N zQjWgoQNIFg5=a}0u~<1Yzh;X5E$g+I0b~vFU*QISAq_$7{aO44fejJ3N0g#7^iU^ieYLIC2LqZUi`(@;ote$ z{v9|=PC8RJ-vN&C#NV{WwhQ>)U55Nx0vzVx5gLtYLR+#LN2t9tlKzMr%^Z*5% zpYDKpq5-;7hUeY&iZ4nh)Vc8GF@vXfTH_fj!kGZnNpt3Z7;kJI%PaQ~ILj0Pj?fH7 zTpFthkogE7E$qlj1b;jcE5lAZ6q~Topo08Oq>Rj0z@s`0>ek` z4Mn14m)S#FjcqU#6Y8D{I6m15joKg4-nnot`x7F>{lyob_q~>?n;${Kkk1&P|EZhr z0Ee$y8$Yt?B6hgSkQTkBz)L>~ZJ-CJHL1{=Nn6}Tf_#yPj+uy(?WmaC{E0Z^%gew< zjvy2B(&i?>Vq`8F%!&huFMAJqV+7{@7AVIWfgpo2CL7Tgj?%d_Vv!1~;VALC4o5vj z*+*&-D$a%bvx9K`;dLc4WvY?ZH~sFm+8Ggl?`MC8GmNt3(*V}Fq$!;aoaQZeu5|h0 zSMu6wU3zt)3O5pULYBB9vd2g1fn|Ft8x(<(&>&I?tG)#+CkS=k{{xD8gOhiX^75ds zdk?Iz0Gtw7mYoB~H4`8OIND#VfINx@@9m@uiOs zR<3wBIaumKo9sA$dwF`l!b#D6m4>wN1r<&j&yk3PWe=HFtpjG*_AC_W8FnhcMH)eN zVIFA#hLk{k^p`N+|IjXNsG~+!IOvb>L&H{@wBd!Pp0tvVg)(FqSs7%uRCHUhLW$J0nI50}m$8PK zKSH7^sU#J{nmuir=ivU8iw{<;Tyx`%6a6?1nRf<=FIX@`{qmP*tPY2j+0nRWaDHST z=O@!htF7jq#k}?7tBvYf-SOJ<3Kv@AWC=~7+nN;pMh^*X(rr+I1?zhrnA3&Wn@bSe9!BJeS48-1KMC*c*F)dA8%nPX1a7?ut&=E3c)b?d z`Z~nlJdEhHo!|$_D-!25exk!gX^HE2JK`>mXgVEqQXwuDhq7w5961~|9g_5Gt@V3% zBthbnCXLH@_?74CPn>94xqpAf>n}XFVAbgW`aAOa*0&}XE?oHLBF5Bm@%?!1%X3*Y zTAbZ)=JnyDvtM-k{nMlS4jGO`FH=HA%H>XaQ2L`#{1@6y096p`KFN*)6IpuVo>mQq z`^kG??%oG=!gzpkGs)+H)!vS#3Aa(qL^b+@76Lz>jKJOBg}!GG9HqHv{@DU}Ck{vb zZ7-9TiziF|G;|h?O{Pr_kCIxTbb2P*iI)x!kdaYsA-~duPV?j8D4D9C8xz06-33m` z3cJ3m^nUCGEBcp;<5e}c=MDbuyW%ku4Q!;*Mlw56{RbxY$}21D!tK@4r#qI+6bEv8 z-~6AdR)NUD+n;nhLEuywj@JkrsS`NjLg8&_?%^sr;t;u1BH1)W(n*1!6{Yj~28nOz z$&&Yy3i2VPlSUgBny>x7opVu&3aN7{k2f&+Y>q*+Df(M5OMaock@Uq3ugjy7hF9p=9_5Ktz^4<6AR!dt1t zg06&FrPxVuPkS|yvv@DekyI)x;o(I#VK&p@uZ$u0?;ySz1hwz^Fm~=l<2UZ6z>z#q zPz1u)D+7^FGfnOs6R}F%a??q(&E~teRNiRPDmWB>PNPY^>sW%x24hbNfuEE!+ z!qZL;!A?&^&B!49S>CD&~eY@9B)i{NSqHJc?nK!>L$>L9jaV5Ux%Yu0+a&_WiUgCo*xKJZ@VV zE!v>CM(WdtTsZMjTRR%AzXS2N_mQ`vlL#E4adtSs6RTE=8Jd6dAwo;HBkvdE?GlJk z7v`7jqT8wdM^t0AR*u%H2#ORx#ZCqVjtm|8AnM_CU2YyVPDtM~R2(`ItG@Pz2|re) zcrjBcD^3#kDR_L&)miiB-{qu7-?I9T4~=s;zk1`QEk@Mzx6W`E=1Ar@s0&<@&shsi zR$+F%MsiTZd|XEKm`ELH03)%PMglA|xo_uLx@ljh_AdnYFj@$=rzFxa0gz=!1GE?| zam-QTg_5a~Ht7^Za-3XoG6g*ucZerVu~E1(0`4U*=9KKo2<;KAB&#(9dDsw>k13PX zbR`sP)9x!NcDJPGUNt7%-t=p3g?Hi~1eBix=*~MHT(SADPkn#VH-7fvefNBQ;Ha;D zZH%K}(Q{^S-=(gMfTT^D3afuFjuXPKQw&4JFJWF*{O>{GLX(nUae6lTfm%EF5(hKx zn~A~=^B@C3p}(G$mZZ9i7O zVa`9*pxLvhd6}G6Flzi=FNyCD&VAy~YgVkECH7Ty)rJkrcZ~M-^)YS2wXg>CvCu-v zBDs<aY zObi_)yZd^gTbAt4A9K_C!}s40>+p`Mmp@v4rTuR9@1Oboom^|Y5vP&QsZ=z)XJITV zd(wkB!`j-d7XpExC}pO&%2>bf>JgVs?Y}jASm(|U)^KafKI!1wWNLDU&a+OspP~Vs zJY3;hcOdfK4)`YxC$Ri>tw$nPePG;^q;@(1MGk8IIHcWl0rdS1Q2S-$$eqiP`JFP< zeCJODeB6$=P7azZ_zUi1YC>y$L;VO&EF?frKpr{N#DKoC`+QG$)0@?OMql$*6FS=B zrhel7Kg6THfSNwe+uT(5R(|hm9{y(l**);(OMf-vU`6%czW0-7cEL_RMKQGFcOG9t zCBn74V~u_uf9cP>Zr&crQP?r@RL>&yDRxAxA}KEPS^GoO&w2@YbH4^TjlwA^l0`RG z;lMXnBY1TldjI}90<#H9pL87k(Hi(Clp?a}FdCoO1*Nc)#-tvEl+YewVnTb>EEPFd z=0O@V$goOF6n9^D+sBLd_8N23nh-kjv7~E(vGT=RztR+ntKa_qD=YE&7bH%b<+4+r z5kDP>>lRNKP=5UtyN~~B1#5a>gQfKPCzS!>AA{;U?vXhteC|3l{Nhh=k1B@$ic%^By%*<}HDTzlYDT!l3iwYmU z%0O{%#Z~HQ*|_YZf{VVnF^bbCtDRq`Iqj=8XIciZL3(q^)V^PoUaxpY_c~hn_@~9^&sr15S@g@!rlOcI$2{q&i!=Dl zW)bM|QwC0jOUd=w-iK<o?sD4_awPW|≶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 Hu0mjf8wy8x literal 0 HcmV?d00001 diff --git a/assets/images/holiday/5/home_2_1.png b/assets/images/holiday/5/home_2_1.png new file mode 100644 index 0000000000000000000000000000000000000000..36497b9ee3c7b9195ebd5e347b1086a16916433a GIT binary patch literal 6445 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 Hu0mjf8wy8x literal 0 HcmV?d00001 diff --git a/assets/images/holiday/5/home_3_0.png b/assets/images/holiday/5/home_3_0.png new file mode 100644 index 0000000000000000000000000000000000000000..0fd24f75514d280d260af15966b1481e5c17a3df GIT binary patch literal 7151 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 literal 0 HcmV?d00001 diff --git a/assets/images/holiday/5/home_3_1.png b/assets/images/holiday/5/home_3_1.png new file mode 100644 index 0000000000000000000000000000000000000000..0fd24f75514d280d260af15966b1481e5c17a3df GIT binary patch literal 7151 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 literal 0 HcmV?d00001 diff --git a/assets/images/holiday/5/home_4_0.png b/assets/images/holiday/5/home_4_0.png new file mode 100644 index 0000000000000000000000000000000000000000..7f022435b4d6ed6afc0a21833cafa033c374ad7e GIT binary patch literal 5630 zcmVgcXM(w)v7NJt2WLO@{@P*4XvS5XmMg2$)~9x#I9HM{D#ij0mkE8-~3DB=yG z2)HPocyK5n2pY~9OdttK$kjb-w`=l@j$&^<(; ze?D6E+~|*1K6!h`V<*Lw2k)Db-{LELzqWSoysEN~Cc{bTEX@r?3+K+AaY{h}2;s&r zm*XK0x`FV}O~jGl;_B+;@bcxLDS99R-2@;Jx`}+VVd-Z#Tt8vbWuvp!1S4)g9@62Y zv4UGdB@9eErEl)#&`sn-0K`GN18#G~C3~uz05SmC57%6Nj`NWAt?T37-xC24kLJX; zG!{NxS+eq_`pS=A+PVGB2YO^m3XpmQ^bmx7{>pdXdT?T-`SX|e7rnDE65PA^_1B+B z#%l@u8ldf9uC>Nkl}_bQRSZ+x?T^PDLoMvO?I zZJvMX-ro=s{e0G}S>47Z98)30|Bksy&4vYvSLROXMf8<~`2J4zufJ`Qd7c8oqFGbJ zh6Oj>bU_yU8l<>*)y%Se8(*Q?fxFxnOYRv#)WurhhljP$o|y^>Jxu(?f4Z4!%$aAN zX{oKofe#4aBkdSppSxT=d=O3KfzKWFZI9J%Z zLFJch5h2S4AX$NPCITcR;gSa6P6rO*(9ge0BR%>G7Du1P1J%mc8L6IARaKS3;jmz_ zSPECIS~X+xjFI#sG^6$CIiqdIHaEv29E6AG@cG5st{e*WQ~qY zCmJ~#VA>W${J`cLNYRG_NvLU?MxRqsQgS8y8whjyJO0>rFhWCALR3ufbl;volW_Ga z_U7XO5S3W8o&>y`**5wlQd?jDI2;3sCm+PZ_o7R{dHNL>6)GSRR3%=EHgljifEKGd z!lj09`6>G#=3n}sMM`4}LH9)ckjL#F4#z;^1yF1F)^D7UR2URb6BBS7UoLO;FhD|KBVn7JZQQO zk|d$l5)LJNCPX3Mv+R0)~y3A zT!G`I2Q3HyUWri(NE8$$1cCB5L2%|mLiW{Qa}QwmH588no@YgV+O4 z^Z|9sYUFCiR6!uOM4gh#X`v1YXh)4{-KOABO#q10ybqzWmqAyWfVZWf#&~eC1IOEN zEdjwbZ%ITAOaoP}h5DTjfVcQ5rgiil>?USEZkv8r9>iIW79cuerEoY5vMA9Pd?8&E zBuUg8cOBsp(w3}*DqaQEzZ(bN69JK^QJOt6{0zEe;|jXQg$FBG9K9jz-2~NJr{ZzN z)ItNKbeHc^^C5Bbtw#$G>sfvltf~&xa|&T%?)FCWnTZ(#~as;8VN>59T$*}+Qc^Z zgM4%oeRvjoHD+BTP=b9vT-I+mWZAb3nbrNki-{fbW^;`yp(8kg)C0{fU^ryF7oi~0 zMW>63=Tjl--wTcVU&4Fv)XsT1l%gjO>$53PIC&7n38bS2s3if$hC@J=fYa$~Y>5F; zT7blU>?J*bX8qw;&-g6w=b@#aqV_^blay>IKn)7f>n+zXC-wKb8yt!<#dAY#Z zEBqw5w}IG4rtR}@xhVwSpb^);kenLzjJE!F