@_nazmiforreal/flutter-ota 0.1.26 → 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -18
- package/bin/flutter-ota-linux-x64 +0 -0
- package/bin/flutter-ota.js +12 -11
- package/dart-src/packages/cli-tools/README.md +146 -2
- package/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +46 -13
- package/dart-src/packages/cli-tools/lib/src/backend.dart +6 -6
- package/dart-src/packages/cli-tools/lib/src/commands/build.dart +2 -0
- package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +7 -7
- package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +4 -1
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +36 -8
- package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +1 -1
- package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +1 -1
- package/dart-src/packages/cli-tools/lib/src/config.dart +5 -5
- package/dart-src/packages/cli-tools/lib/src/operations.dart +2 -2
- package/dart-src/packages/cli-tools/pubspec.yaml +8 -21
- package/dart-src/packages/cli-tools/test/backends_test.dart +3 -3
- package/dart-src/packages/cli-tools/test/cli_test.dart +1 -1
- package/dart-src/packages/cli-tools/test/mocks/mock_aws_s3_client.dart +2 -2
- package/dart-src/packages/cli-tools/test/mocks/mock_d1_client.dart +2 -2
- package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +4 -1
- package/dart-src/packages/cli-tools/test/mocks/mock_postgres_client.dart +2 -2
- package/dart-src/packages/cli-tools/test/pocketbase_smoke_test.dart +1 -1
- package/dart-src/root/README.md +334 -0
- package/dart-src/root/analysis_options.yaml +21 -0
- package/dart-src/root/android/build.gradle +116 -0
- package/dart-src/root/android/consumer-rules.pro +23 -0
- package/dart-src/root/android/settings.gradle +1 -0
- package/dart-src/root/android/src/main/AndroidManifest.xml +21 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/ApplyResult.kt +52 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/BlacklistStore.kt +154 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/BootDiagnosticStore.kt +150 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/CrashGuard.kt +271 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherApplication.kt +201 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherAutoInitProvider.kt +93 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherPlugin.kt +245 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/LoaderHook.kt +298 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/PatchManager.kt +1579 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/PatcherConfig.kt +173 -0
- package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/SignatureVerifier.kt +177 -0
- package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/CrashGuardTest.kt +61 -0
- package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/LoaderHookTest.kt +25 -0
- package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/PatchManagerTest.kt +401 -0
- package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/PatcherConfigTest.kt +30 -0
- package/dart-src/root/bin/pack.dart +182 -0
- package/dart-src/root/lib/flutter_ota_kit.dart +1170 -0
- package/dart-src/root/lib/src/app_version_resolver.dart +36 -0
- package/dart-src/root/lib/src/aws_update_source.dart +97 -0
- package/dart-src/root/lib/src/blacklist.dart +54 -0
- package/dart-src/root/lib/src/boot_diagnostic.dart +138 -0
- package/dart-src/root/lib/src/cloudflare_update_source.dart +97 -0
- package/dart-src/root/lib/src/flutter_ota_app.dart +69 -0
- package/dart-src/root/lib/src/io.dart +48 -0
- package/dart-src/root/lib/src/io_stub.dart +19 -0
- package/dart-src/root/lib/src/ota_progress_overlay.dart +1186 -0
- package/dart-src/root/lib/src/pack/pack.dart +434 -0
- package/dart-src/root/lib/src/patch_info.dart +12 -0
- package/dart-src/root/lib/src/patcher_channel.dart +160 -0
- package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_database.dart +1 -1
- package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_s3_client.dart +1 -1
- package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_storage.dart +1 -1
- package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_storage_profile.dart +1 -1
- package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/with_cloudfront_signed_url.dart +1 -1
- package/dart-src/root/lib/src/pkg/client/flutter_ota_kit_client.dart +1 -0
- package/dart-src/root/lib/src/pkg/client/src/server_update_source.dart +46 -0
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/cloudflare_worker_database.dart +1 -1
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_build_where.dart +1 -1
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_bundle_mapper.dart +3 -3
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_database.dart +1 -1
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_database_plugin.dart +2 -2
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_s3_client.dart +1 -1
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_storage.dart +1 -1
- package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_storage_profile.dart +1 -1
- package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/status.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/flutter_ota_kit_plugin_core.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_blob_database_plugin.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/filter_compatible_app_versions.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/get_update_info.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/request_update_bundle_state.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/resolve_update_info_from_bundles.dart +1 -1
- package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/types.dart +2 -2
- package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_bundle_mapper.dart +2 -2
- package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_database.dart +26 -15
- package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_storage.dart +1 -1
- package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_bundle_mapper.dart +2 -2
- package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_database.dart +2 -2
- package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_get_update_info.dart +2 -2
- package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_storage.dart +1 -1
- package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_types.dart +1 -1
- package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_bundle_mapper.dart +2 -2
- package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_database.dart +2 -2
- package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_edge_function_database.dart +1 -1
- package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_edge_function_storage.dart +1 -1
- package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_storage.dart +1 -1
- package/dart-src/root/lib/src/pocketbase_update_source.dart +107 -0
- package/dart-src/root/lib/src/postgres_update_source.dart +95 -0
- package/dart-src/root/lib/src/shared_update_check.dart +131 -0
- package/dart-src/root/lib/src/supabase_update_source.dart +93 -0
- package/dart-src/root/pubspec.yaml +54 -0
- package/package.json +1 -1
- package/scripts/postinstall.js +17 -13
- package/scripts/vendor.sh +22 -7
- package/dart-src/packages/core/CHANGELOG.md +0 -21
- package/dart-src/packages/core/README.md +0 -23
- package/dart-src/packages/core/example/lib/main.dart +0 -69
- package/dart-src/packages/core/pubspec.yaml +0 -13
- package/dart-src/packages/core/test/rollout_test.dart +0 -135
- package/dart-src/packages/core/test/semver_test.dart +0 -55
- package/dart-src/packages/core/test/semver_vectors.json +0 -1
- package/dart-src/packages/core/test/types_test.dart +0 -131
- package/dart-src/packages/core/test/uuid_test.dart +0 -40
- package/dart-src/packages/core/tool/dbg.dart +0 -7
- package/dart-src/plugins/aws/.gitkeep +0 -0
- package/dart-src/plugins/aws/CHANGELOG.md +0 -23
- package/dart-src/plugins/aws/LICENSE +0 -21
- package/dart-src/plugins/aws/README.md +0 -28
- package/dart-src/plugins/aws/example/lib/main.dart +0 -60
- package/dart-src/plugins/aws/pubspec.yaml +0 -30
- package/dart-src/plugins/aws/test/aws_database_test.dart +0 -161
- package/dart-src/plugins/aws/test/aws_storage_test.dart +0 -73
- package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +0 -78
- package/dart-src/plugins/cloudflare/.gitkeep +0 -0
- package/dart-src/plugins/cloudflare/CHANGELOG.md +0 -23
- package/dart-src/plugins/cloudflare/LICENSE +0 -21
- package/dart-src/plugins/cloudflare/README.md +0 -25
- package/dart-src/plugins/cloudflare/example/lib/main.dart +0 -62
- package/dart-src/plugins/cloudflare/migrations/0001_hot-updater_init.sql +0 -16
- package/dart-src/plugins/cloudflare/migrations/0002_hot-updater_0.13.0.sql +0 -8
- package/dart-src/plugins/cloudflare/migrations/0003_hot-updater_0.18.0.sql +0 -38
- package/dart-src/plugins/cloudflare/migrations/0004_hot-updater_0.29.0.sql +0 -11
- package/dart-src/plugins/cloudflare/migrations/0005_hot-updater_0.31.0.sql +0 -24
- package/dart-src/plugins/cloudflare/pubspec.yaml +0 -28
- package/dart-src/plugins/cloudflare/sql/bundles.sql +0 -42
- package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +0 -126
- package/dart-src/plugins/cloudflare/test/d1_bundle_mapper_test.dart +0 -106
- package/dart-src/plugins/cloudflare/test/d1_database_test.dart +0 -200
- package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +0 -261
- package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +0 -78
- package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +0 -74
- package/dart-src/plugins/plugin-core/CHANGELOG.md +0 -20
- package/dart-src/plugins/plugin-core/LICENSE +0 -21
- package/dart-src/plugins/plugin-core/README.md +0 -21
- package/dart-src/plugins/plugin-core/example/lib/main.dart +0 -61
- package/dart-src/plugins/plugin-core/pubspec.yaml +0 -23
- package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +0 -170
- package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_store_test.dart +0 -74
- package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +0 -345
- package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +0 -843
- package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +0 -298
- package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +0 -195
- package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +0 -56
- package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +0 -1589
- package/dart-src/plugins/plugin-core/test/helpers_test.dart +0 -283
- package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +0 -202
- package/dart-src/plugins/plugin-core/test/request_update_bundle_state_test.dart +0 -86
- package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +0 -88
- package/dart-src/plugins/pocketbase/CHANGELOG.md +0 -23
- package/dart-src/plugins/pocketbase/LICENSE +0 -21
- package/dart-src/plugins/pocketbase/README.md +0 -54
- package/dart-src/plugins/pocketbase/example/lib/main.dart +0 -62
- package/dart-src/plugins/pocketbase/pubspec.yaml +0 -26
- package/dart-src/plugins/pocketbase/test/pb_uri_test.dart +0 -12
- package/dart-src/plugins/postgres/.gitkeep +0 -0
- package/dart-src/plugins/postgres/CHANGELOG.md +0 -23
- package/dart-src/plugins/postgres/LICENSE +0 -21
- package/dart-src/plugins/postgres/README.md +0 -29
- package/dart-src/plugins/postgres/example/lib/main.dart +0 -67
- package/dart-src/plugins/postgres/pubspec.yaml +0 -26
- package/dart-src/plugins/postgres/sql/bundles.sql +0 -45
- package/dart-src/plugins/postgres/sql/get_target_app_version_list.sql +0 -21
- package/dart-src/plugins/postgres/sql/get_update_info.spec.ts +0 -171
- package/dart-src/plugins/postgres/sql/get_update_info_by_app_version.sql +0 -126
- package/dart-src/plugins/postgres/sql/get_update_info_by_fingerprint_hash.sql +0 -125
- package/dart-src/plugins/postgres/sql/hash_user_id.sql +0 -30
- package/dart-src/plugins/postgres/sql/is_device_eligible.sql +0 -237
- package/dart-src/plugins/postgres/sql/prepareSql.ts +0 -11
- package/dart-src/plugins/postgres/test/mock_postgres_client.dart +0 -320
- package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +0 -139
- package/dart-src/plugins/postgres/test/postgres_database_test.dart +0 -210
- package/dart-src/plugins/postgres/test/postgres_storage_runtime_test.dart +0 -60
- package/dart-src/plugins/supabase/CHANGELOG.md +0 -24
- package/dart-src/plugins/supabase/LICENSE +0 -21
- package/dart-src/plugins/supabase/README.md +0 -53
- package/dart-src/plugins/supabase/example/lib/main.dart +0 -63
- package/dart-src/plugins/supabase/pubspec.yaml +0 -27
- package/dart-src/plugins/supabase/supabase/edge-functions/index.ts +0 -44
- package/dart-src/plugins/supabase/supabase/edge-functions/runtime.docker.integration.spec.ts +0 -968
- package/dart-src/plugins/supabase/supabase/migrations/20250103114225_init.sql +0 -232
- package/dart-src/plugins/supabase/supabase/migrations/20250314000000_hot-updater_0.13.0.sql +0 -137
- package/dart-src/plugins/supabase/supabase/migrations/20250516000000_hot-updater_0.18.0.sql +0 -206
- package/dart-src/plugins/supabase/supabase/migrations/20251014000000_hot-updater_0.21.0.sql +0 -186
- package/dart-src/plugins/supabase/supabase/migrations/20260401000000_hot-updater_0.29.0.sql +0 -505
- package/dart-src/plugins/supabase/supabase/migrations/20260414000000_hot-updater_0.30.0.sql +0 -50
- package/dart-src/plugins/supabase/supabase/migrations/20260422000000_hot-updater_0.31.0.sql +0 -20
- package/dart-src/plugins/supabase/supabase/migrations/20260520014100_hot-updater_rls.sql +0 -75
- package/dart-src/plugins/supabase/test/mock_supabase_client.dart +0 -654
- package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +0 -115
- package/dart-src/plugins/supabase/test/supabase_database_test.dart +0 -198
- package/dart-src/plugins/supabase/test/supabase_storage_test.dart +0 -168
- /package/dart-src/{packages/core → root}/LICENSE +0 -0
- /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/flutter_ota_kit_aws.dart +0 -0
- /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_cloudfront_client.dart +0 -0
- /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_cloudfront_signer.dart +0 -0
- /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_config.dart +0 -0
- /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_ssm_client.dart +0 -0
- /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/flutter_ota_kit_cloudflare.dart +0 -0
- /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_client.dart +0 -0
- /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_config.dart +0 -0
- /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_config.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/flutter_ota_kit_core.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/app_update_info.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle_artifacts.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle_patch_artifact.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/changed_asset.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/get_bundles_args.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/metadata.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/patch_info.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/platform.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/rollout.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver_range.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver_version.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/strategy.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/update_bundle_params.dart +0 -0
- /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/uuid.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/asset_storage_layout.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_helpers.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_storage_layout.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_unit_of_work.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_unit_of_work_store.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/calculate_pagination.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/compression_format.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/content_addressed_assets.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_database_plugin.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_storage_key_builder.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_storage_plugin.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/generate_min_bundle_id.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/is_object.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/legacy_asset_storage_layout.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/paginate_bundles.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/parse_storage_uri.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/query_bundles.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/storage_helpers.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/storage_profile.dart +0 -0
- /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/uuidv7.dart +0 -0
- /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/flutter_ota_kit_pocketbase.dart +0 -0
- /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_client.dart +0 -0
- /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_config.dart +0 -0
- /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/flutter_ota_kit_postgres.dart +0 -0
- /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_client.dart +0 -0
- /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_config.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/edge.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/flutter_ota_kit_supabase.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/error_message.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_client_adapter.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_client_http.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_config.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_signed_url_batcher.dart +0 -0
- /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/types.dart +0 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import 'dart:convert';
|
|
2
|
+
import 'dart:io';
|
|
3
|
+
import 'dart:typed_data';
|
|
4
|
+
|
|
5
|
+
import 'package:archive/archive.dart';
|
|
6
|
+
import 'package:crypto/crypto.dart';
|
|
7
|
+
|
|
8
|
+
const List<String> abiPriority = <String>['arm64-v8a', 'armeabi-v7a', 'x86_64'];
|
|
9
|
+
const String flutterAssetsPrefix = 'assets/flutter_assets/';
|
|
10
|
+
const String assetManifestPath = '${flutterAssetsPrefix}AssetManifest.bin';
|
|
11
|
+
|
|
12
|
+
/// Result of a successful [packPatch] run.
|
|
13
|
+
class PackResult {
|
|
14
|
+
const PackResult({
|
|
15
|
+
required this.payloadPath,
|
|
16
|
+
required this.manifestPath,
|
|
17
|
+
required this.abis,
|
|
18
|
+
required this.md5,
|
|
19
|
+
required this.version,
|
|
20
|
+
required this.targetVersionCode,
|
|
21
|
+
required this.assetCount,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
final String payloadPath;
|
|
25
|
+
final String manifestPath;
|
|
26
|
+
final List<String> abis;
|
|
27
|
+
final String md5;
|
|
28
|
+
final String version;
|
|
29
|
+
final int targetVersionCode;
|
|
30
|
+
final int assetCount;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Pack a release APK into a device-compatible OTA patch (`patch.zip`).
|
|
34
|
+
///
|
|
35
|
+
/// Mirrors the standalone `bin/pack.dart` CLI, but as a library usable by the
|
|
36
|
+
/// `flutter_ota_kit build` command. Includes **every** ABI found in the APK so
|
|
37
|
+
/// a single bundle serves all device ABIs (the device SDK picks its ABI).
|
|
38
|
+
Future<PackResult> packPatch({
|
|
39
|
+
required String apkPath,
|
|
40
|
+
required String version,
|
|
41
|
+
required int targetVersionCode,
|
|
42
|
+
String? abi,
|
|
43
|
+
List<String> requestedAssets = const [],
|
|
44
|
+
required String out,
|
|
45
|
+
void Function(int done, int total, String label)? onProgress,
|
|
46
|
+
}) async {
|
|
47
|
+
final apkFile = File(apkPath);
|
|
48
|
+
if (!apkFile.existsSync()) {
|
|
49
|
+
throw PackException('APK not found: $apkPath');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
final apkBytes = apkFile.readAsBytesSync();
|
|
53
|
+
final archive = ZipDecoder().decodeBytes(apkBytes);
|
|
54
|
+
|
|
55
|
+
final libs = _collectLibapps(archive);
|
|
56
|
+
if (libs.isEmpty) {
|
|
57
|
+
throw PackException(
|
|
58
|
+
'libapp.so not found in APK for any of $abiPriority '
|
|
59
|
+
'(or requested --abi $abi).',
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (abi != null && !libs.any((e) => e.$1 == abi)) {
|
|
64
|
+
throw PackException('requested --abi $abi not present in APK.');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
final outDir = Directory(out);
|
|
68
|
+
outDir.createSync(recursive: true);
|
|
69
|
+
final result = _writePatchPackage(
|
|
70
|
+
outDir: outDir,
|
|
71
|
+
archive: archive,
|
|
72
|
+
libs: libs,
|
|
73
|
+
version: version,
|
|
74
|
+
targetVersionCode: targetVersionCode,
|
|
75
|
+
requestedAssets: requestedAssets,
|
|
76
|
+
abi: abi,
|
|
77
|
+
onProgress: onProgress,
|
|
78
|
+
);
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
PackResult _writePatchPackage({
|
|
83
|
+
required Directory outDir,
|
|
84
|
+
required Archive archive,
|
|
85
|
+
required List<(String, List<int>)> libs,
|
|
86
|
+
required String version,
|
|
87
|
+
required int targetVersionCode,
|
|
88
|
+
required List<String> requestedAssets,
|
|
89
|
+
String? abi,
|
|
90
|
+
void Function(int done, int total, String label)? onProgress,
|
|
91
|
+
}) {
|
|
92
|
+
// When an ABI is requested, restrict the patch to only that ABI instead of
|
|
93
|
+
// shipping every architecture found in the APK.
|
|
94
|
+
final effectiveLibs = abi == null
|
|
95
|
+
? libs
|
|
96
|
+
: libs.where((e) => e.$1 == abi).toList();
|
|
97
|
+
final patchFiles = <String, _PatchAssetFile>{};
|
|
98
|
+
final operations = <Map<String, dynamic>>[];
|
|
99
|
+
var baseManifestSize = 0;
|
|
100
|
+
|
|
101
|
+
if (requestedAssets.isNotEmpty) {
|
|
102
|
+
final manifestEntry = _findFile(archive, assetManifestPath);
|
|
103
|
+
if (manifestEntry == null) {
|
|
104
|
+
throw PackException(
|
|
105
|
+
'AssetManifest.bin not found in APK: $assetManifestPath',
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
final manifestBytes = _archiveFileBytes(manifestEntry);
|
|
109
|
+
baseManifestSize = manifestBytes.length;
|
|
110
|
+
final decoded = _FlutterStandardMessageCodec().decode(manifestBytes);
|
|
111
|
+
if (decoded is! Map) {
|
|
112
|
+
throw PackException('AssetManifest.bin must decode to a map.');
|
|
113
|
+
}
|
|
114
|
+
final assetManifest = Map<String, dynamic>.from(decoded);
|
|
115
|
+
|
|
116
|
+
for (final key in requestedAssets) {
|
|
117
|
+
final variantsRaw = assetManifest[key];
|
|
118
|
+
if (variantsRaw is! List) {
|
|
119
|
+
throw PackException('asset key not found in AssetManifest.bin: $key');
|
|
120
|
+
}
|
|
121
|
+
final variants = variantsRaw
|
|
122
|
+
.map((variant) => Map<String, dynamic>.from(variant as Map))
|
|
123
|
+
.toList(growable: false);
|
|
124
|
+
operations.add({'op': 'upsert', 'key': key, 'variants': variants});
|
|
125
|
+
for (final variant in variants) {
|
|
126
|
+
final assetPath = variant['asset'];
|
|
127
|
+
if (assetPath is! String || assetPath.isEmpty) {
|
|
128
|
+
throw PackException('invalid variant asset for key $key');
|
|
129
|
+
}
|
|
130
|
+
_validateArchiveRelativePath(assetPath, 'asset path');
|
|
131
|
+
final apkPath = '$flutterAssetsPrefix$assetPath';
|
|
132
|
+
final entry = _findFile(archive, apkPath);
|
|
133
|
+
if (entry == null) {
|
|
134
|
+
throw PackException(
|
|
135
|
+
'variant file for $key not found in APK: $apkPath',
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
final bytes = _archiveFileBytes(entry);
|
|
139
|
+
patchFiles[assetPath] = _PatchAssetFile(
|
|
140
|
+
path: assetPath,
|
|
141
|
+
bytes: bytes,
|
|
142
|
+
md5Hex: md5.convert(bytes).toString(),
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
final libManifest = <String, dynamic>{
|
|
149
|
+
for (final (currentAbi, soBytes) in effectiveLibs)
|
|
150
|
+
currentAbi: {
|
|
151
|
+
'path': 'lib/$currentAbi/libapp.so',
|
|
152
|
+
'md5': md5.convert(soBytes).toString(),
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
final zipManifest = <String, dynamic>{
|
|
157
|
+
'schemaVersion': 2,
|
|
158
|
+
'version': version,
|
|
159
|
+
'targetVersionCode': targetVersionCode,
|
|
160
|
+
'lib': libManifest,
|
|
161
|
+
if (requestedAssets.isNotEmpty)
|
|
162
|
+
'assets': {
|
|
163
|
+
'mode': 'overlay',
|
|
164
|
+
'manifestPatch': 'manifest_patch.json',
|
|
165
|
+
'prefix': 'assets/',
|
|
166
|
+
'files': patchFiles.values
|
|
167
|
+
.map(
|
|
168
|
+
(file) => {
|
|
169
|
+
'path': file.path,
|
|
170
|
+
'md5': file.md5Hex,
|
|
171
|
+
'size': file.bytes.length,
|
|
172
|
+
},
|
|
173
|
+
)
|
|
174
|
+
.toList(),
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
final package = Archive()
|
|
179
|
+
..addFile(_jsonArchiveFile('manifest.json', zipManifest));
|
|
180
|
+
final progressTotal = effectiveLibs.length + 1;
|
|
181
|
+
onProgress?.call(0, progressTotal, 'libapp.so');
|
|
182
|
+
var progressDone = 0;
|
|
183
|
+
for (final (currentAbi, soBytes) in effectiveLibs) {
|
|
184
|
+
package.addFile(
|
|
185
|
+
ArchiveFile('lib/$currentAbi/libapp.so', soBytes.length, soBytes),
|
|
186
|
+
);
|
|
187
|
+
progressDone++;
|
|
188
|
+
onProgress?.call(progressDone, progressTotal, 'lib/$currentAbi/libapp.so');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (requestedAssets.isNotEmpty) {
|
|
192
|
+
final manifestPatch = <String, dynamic>{
|
|
193
|
+
'schemaVersion': 1,
|
|
194
|
+
'manifestFormat': 'bin',
|
|
195
|
+
'baseManifestSize': baseManifestSize,
|
|
196
|
+
'operations': operations,
|
|
197
|
+
};
|
|
198
|
+
package.addFile(_jsonArchiveFile('manifest_patch.json', manifestPatch));
|
|
199
|
+
for (final file in patchFiles.values) {
|
|
200
|
+
package.addFile(
|
|
201
|
+
ArchiveFile('assets/${file.path}', file.bytes.length, file.bytes),
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// archive 3.x returns List<int>?, 4.x returns List<int>; drop the null-coalesce
|
|
207
|
+
// because we resolve archive >= 4 in the lockfile (and the constraint below).
|
|
208
|
+
final List<int> packageBytes = ZipEncoder().encode(package);
|
|
209
|
+
final outZip = File('${outDir.path}/patch.zip');
|
|
210
|
+
outZip.writeAsBytesSync(packageBytes);
|
|
211
|
+
onProgress?.call(progressTotal, progressTotal, 'patch.zip');
|
|
212
|
+
|
|
213
|
+
final payloadMd5 = md5.convert(packageBytes).toString();
|
|
214
|
+
final outerManifest = <String, dynamic>{
|
|
215
|
+
'schemaVersion': 2,
|
|
216
|
+
'version': version,
|
|
217
|
+
'md5': payloadMd5,
|
|
218
|
+
'targetVersionCode': targetVersionCode,
|
|
219
|
+
'abis': effectiveLibs.map((e) => e.$1).toList(),
|
|
220
|
+
'payload': 'patch.zip',
|
|
221
|
+
};
|
|
222
|
+
_writeJson(File('${outDir.path}/manifest.json'), outerManifest);
|
|
223
|
+
|
|
224
|
+
return PackResult(
|
|
225
|
+
payloadPath: outZip.path,
|
|
226
|
+
manifestPath: '${outDir.path}/manifest.json',
|
|
227
|
+
abis: effectiveLibs.map((e) => e.$1).toList(),
|
|
228
|
+
md5: payloadMd5,
|
|
229
|
+
version: version,
|
|
230
|
+
targetVersionCode: targetVersionCode,
|
|
231
|
+
assetCount: patchFiles.length,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
ArchiveFile _jsonArchiveFile(String name, Map<String, dynamic> json) {
|
|
236
|
+
final bytes = utf8.encode(
|
|
237
|
+
'${const JsonEncoder.withIndent(' ').convert(json)}\n',
|
|
238
|
+
);
|
|
239
|
+
return ArchiveFile(name, bytes.length, bytes);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/// Collect every `lib/<abi>/libapp.so` present in the APK.
|
|
243
|
+
List<(String, List<int>)> _collectLibapps(Archive archive) {
|
|
244
|
+
final found = <(String, List<int>)>[];
|
|
245
|
+
final seen = <String>{};
|
|
246
|
+
final regex = RegExp(r'^lib/([^/]+)/libapp\.so$');
|
|
247
|
+
for (final file in archive.files) {
|
|
248
|
+
final m = regex.firstMatch(file.name);
|
|
249
|
+
if (m != null && seen.add(m.group(1)!)) {
|
|
250
|
+
found.add((m.group(1)!, _archiveFileBytes(file)));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
found.sort((a, b) {
|
|
254
|
+
final ia = abiPriority.indexOf(a.$1);
|
|
255
|
+
final ib = abiPriority.indexOf(b.$1);
|
|
256
|
+
final ra = ia < 0 ? _nonPriorityRank(a.$1) : ia;
|
|
257
|
+
final rb = ib < 0 ? _nonPriorityRank(b.$1) : ib;
|
|
258
|
+
return ra.compareTo(rb);
|
|
259
|
+
});
|
|
260
|
+
return found;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
int _nonPriorityRank(String abi) => abiPriority.length;
|
|
264
|
+
|
|
265
|
+
ArchiveFile? _findFile(Archive archive, String name) {
|
|
266
|
+
for (final file in archive.files) {
|
|
267
|
+
if (!file.isFile) continue;
|
|
268
|
+
if (file.name == name) return file;
|
|
269
|
+
}
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
void _writeJson(File file, Map<String, dynamic> json) {
|
|
274
|
+
file.writeAsStringSync(
|
|
275
|
+
'${const JsonEncoder.withIndent(' ').convert(json)}\n',
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
void _validateArchiveRelativePath(String path, String label) {
|
|
280
|
+
if (path.isEmpty ||
|
|
281
|
+
path.startsWith('/') ||
|
|
282
|
+
path.startsWith('\\') ||
|
|
283
|
+
path.contains('\u0000') ||
|
|
284
|
+
path.split('/').contains('..')) {
|
|
285
|
+
throw PackException('invalid $label: $path');
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
List<int> _archiveFileBytes(ArchiveFile file) {
|
|
290
|
+
final dynamic dynamicFile = file;
|
|
291
|
+
try {
|
|
292
|
+
return List<int>.from(dynamicFile.readBytes() as List<int>);
|
|
293
|
+
} on NoSuchMethodError {
|
|
294
|
+
return List<int>.from(dynamicFile.content as List<int>);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
class _PatchAssetFile {
|
|
299
|
+
const _PatchAssetFile({
|
|
300
|
+
required this.path,
|
|
301
|
+
required this.bytes,
|
|
302
|
+
required this.md5Hex,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
final String path;
|
|
306
|
+
final List<int> bytes;
|
|
307
|
+
final String md5Hex;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/// Error carrying a stable CLI exit code.
|
|
311
|
+
class PackException implements Exception {
|
|
312
|
+
const PackException(this.message, [this.exitCode = 1]);
|
|
313
|
+
|
|
314
|
+
final String message;
|
|
315
|
+
final int exitCode;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
class _FlutterStandardMessageCodec {
|
|
319
|
+
late ByteData _data;
|
|
320
|
+
late int _offset;
|
|
321
|
+
|
|
322
|
+
Object? decode(List<int> bytes) {
|
|
323
|
+
final list = Uint8List.fromList(bytes);
|
|
324
|
+
_data = ByteData.sublistView(list);
|
|
325
|
+
_offset = 0;
|
|
326
|
+
final value = _readValue();
|
|
327
|
+
if (_offset != _data.lengthInBytes) {
|
|
328
|
+
throw PackException('AssetManifest.bin has trailing bytes.');
|
|
329
|
+
}
|
|
330
|
+
return value;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
Object? _readValue() {
|
|
334
|
+
final type = _readUint8();
|
|
335
|
+
switch (type) {
|
|
336
|
+
case 0:
|
|
337
|
+
return null;
|
|
338
|
+
case 1:
|
|
339
|
+
return true;
|
|
340
|
+
case 2:
|
|
341
|
+
return false;
|
|
342
|
+
case 3:
|
|
343
|
+
return _readInt32();
|
|
344
|
+
case 4:
|
|
345
|
+
return _readInt64();
|
|
346
|
+
case 6:
|
|
347
|
+
_alignTo(8);
|
|
348
|
+
return _readFloat64();
|
|
349
|
+
case 7:
|
|
350
|
+
final len = _readSize();
|
|
351
|
+
final bytes = _readBytes(len);
|
|
352
|
+
return utf8.decode(bytes);
|
|
353
|
+
case 12:
|
|
354
|
+
final len = _readSize();
|
|
355
|
+
return List<Object?>.generate(len, (_) => _readValue());
|
|
356
|
+
case 13:
|
|
357
|
+
final len = _readSize();
|
|
358
|
+
final map = <Object?, Object?>{};
|
|
359
|
+
for (var i = 0; i < len; i++) {
|
|
360
|
+
map[_readValue()] = _readValue();
|
|
361
|
+
}
|
|
362
|
+
return map;
|
|
363
|
+
default:
|
|
364
|
+
throw PackException(
|
|
365
|
+
'unsupported StandardMessageCodec type in AssetManifest.bin: $type',
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
int _readSize() {
|
|
371
|
+
final first = _readUint8();
|
|
372
|
+
if (first < 254) return first;
|
|
373
|
+
if (first == 254) return _readUint16();
|
|
374
|
+
return _readUint32();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
int _readUint8() {
|
|
378
|
+
_ensure(1);
|
|
379
|
+
return _data.getUint8(_offset++);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
int _readUint16() {
|
|
383
|
+
_ensure(2);
|
|
384
|
+
final value = _data.getUint16(_offset, Endian.little);
|
|
385
|
+
_offset += 2;
|
|
386
|
+
return value;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
int _readUint32() {
|
|
390
|
+
_ensure(4);
|
|
391
|
+
final value = _data.getUint32(_offset, Endian.little);
|
|
392
|
+
_offset += 4;
|
|
393
|
+
return value;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
int _readInt32() {
|
|
397
|
+
_ensure(4);
|
|
398
|
+
final value = _data.getInt32(_offset, Endian.little);
|
|
399
|
+
_offset += 4;
|
|
400
|
+
return value;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
int _readInt64() {
|
|
404
|
+
_ensure(8);
|
|
405
|
+
final value = _data.getInt64(_offset, Endian.little);
|
|
406
|
+
_offset += 8;
|
|
407
|
+
return value;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
double _readFloat64() {
|
|
411
|
+
_ensure(8);
|
|
412
|
+
final value = _data.getFloat64(_offset, Endian.little);
|
|
413
|
+
_offset += 8;
|
|
414
|
+
return value;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
Uint8List _readBytes(int length) {
|
|
418
|
+
_ensure(length);
|
|
419
|
+
final start = _offset;
|
|
420
|
+
_offset += length;
|
|
421
|
+
return _data.buffer.asUint8List(start, length);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
void _alignTo(int alignment) {
|
|
425
|
+
final mod = _offset % alignment;
|
|
426
|
+
if (mod != 0) _offset += alignment - mod;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
void _ensure(int length) {
|
|
430
|
+
if (_offset + length > _data.lengthInBytes) {
|
|
431
|
+
throw PackException('truncated AssetManifest.bin');
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Re-exported from `flutter_ota_kit_core` (the canonical, framework-agnostic
|
|
2
|
+
// home for the data model). The device SDK previously defined these here; they
|
|
3
|
+
// now live in core so the server client can produce `PatchInfo` without pulling
|
|
4
|
+
// in the Flutter plugin.
|
|
5
|
+
export 'package:flutter_ota_kit/src/pkg/core/flutter_ota_kit_core.dart'
|
|
6
|
+
show
|
|
7
|
+
PatchApplyError,
|
|
8
|
+
PatchApplyPhase,
|
|
9
|
+
PatchApplyProgress,
|
|
10
|
+
PatchApplyResult,
|
|
11
|
+
PatchCheckResult,
|
|
12
|
+
PatchInfo;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import 'package:flutter/services.dart';
|
|
2
|
+
import 'package:restart_app/restart_app.dart';
|
|
3
|
+
|
|
4
|
+
/// MethodChannel constants and thin wrappers for talking to the Android native layer.
|
|
5
|
+
class PatcherChannel {
|
|
6
|
+
PatcherChannel._();
|
|
7
|
+
|
|
8
|
+
static const MethodChannel channel = MethodChannel('flutter_ota_kit');
|
|
9
|
+
|
|
10
|
+
/// Persists the Dart-side config to native (SharedPreferences). On the next cold
|
|
11
|
+
/// start the native side reads this config during `Application.attachBaseContext`
|
|
12
|
+
/// to verify signatures and load the patch.
|
|
13
|
+
static Future<void> saveConfig({
|
|
14
|
+
required String publicKeyBase64,
|
|
15
|
+
required int maxCrashCount,
|
|
16
|
+
required bool strictSignature,
|
|
17
|
+
required List<String> loaderFieldCandidates,
|
|
18
|
+
required bool loaderFallbackHeuristic,
|
|
19
|
+
int maxPatchHistory = 4,
|
|
20
|
+
int maxAssetHistory = 4,
|
|
21
|
+
}) async {
|
|
22
|
+
await channel.invokeMethod<void>('saveConfig', {
|
|
23
|
+
'publicKeyBase64': publicKeyBase64,
|
|
24
|
+
'maxCrashCount': maxCrashCount,
|
|
25
|
+
'strictSignature': strictSignature,
|
|
26
|
+
'loaderFieldCandidates': loaderFieldCandidates,
|
|
27
|
+
'loaderFallbackHeuristic': loaderFallbackHeuristic,
|
|
28
|
+
'maxPatchHistory': maxPatchHistory,
|
|
29
|
+
'maxAssetHistory': maxAssetHistory,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Called at the very start of Dart `init`: marks "booting" (it cross-checks the
|
|
34
|
+
/// native `attachBaseContext` marker as a safety net).
|
|
35
|
+
static Future<void> markBooting() async {
|
|
36
|
+
await channel.invokeMethod<void>('markBooting');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Called after the first Dart frame renders -> resets the circuit-breaker counter.
|
|
40
|
+
static Future<void> reportBootSuccess() async {
|
|
41
|
+
await channel.invokeMethod<void>('reportBootSuccess');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// Reports an uncaught Dart exception during the boot phase. The native side treats
|
|
45
|
+
/// it like an `ApplicationExitInfo` REASON_CRASH: `crash_count += 1`, and once the
|
|
46
|
+
/// threshold is reached it trips the circuit breaker, deletes the patch, and
|
|
47
|
+
/// blacklists it.
|
|
48
|
+
///
|
|
49
|
+
/// When called: only inside the "verifyAfter window" (between the first frame
|
|
50
|
+
/// rendering and `verifyAfter` seconds later), triggered by the
|
|
51
|
+
/// `PlatformDispatcher.onError` / `FlutterError.onError` hooks installed by
|
|
52
|
+
/// `FlutterPatcher.init`. Business exceptions after the window closes do not call
|
|
53
|
+
/// this method.
|
|
54
|
+
static Future<void> reportDartBootError(String message) async {
|
|
55
|
+
await channel.invokeMethod<void>('reportDartBootError', {
|
|
56
|
+
'message': message,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// Download + verify signature + write atomically (atomic swap). Takes effect on
|
|
61
|
+
/// the next cold start.
|
|
62
|
+
///
|
|
63
|
+
/// The native side returns `Map{ok, error, message}`, deserialized by the caller
|
|
64
|
+
/// via `PatchApplyResult.fromNative`.
|
|
65
|
+
static Future<Object?> applyPatch(Map<String, dynamic> patch) async {
|
|
66
|
+
return channel.invokeMethod<Object?>('applyPatch', patch);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/// Deletes the current patch + resets the circuit-breaker flag.
|
|
70
|
+
static Future<void> rollback() async {
|
|
71
|
+
await channel.invokeMethod<void>('rollback');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// Rolls back to the immediately previous patch in local history.
|
|
75
|
+
/// Returns the RollbackOutcome enum name as string.
|
|
76
|
+
static Future<String> rollbackToPrevious() async {
|
|
77
|
+
final result = await channel.invokeMethod<String>('rollbackToPrevious');
|
|
78
|
+
return result ?? 'FALLBACK_TO_BASE';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// The currently installed patch version (null / empty when none installed).
|
|
82
|
+
static Future<String?> currentVersion() async {
|
|
83
|
+
return channel.invokeMethod<String>('currentVersion');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// Diagnostics from the last cold-start patch load.
|
|
87
|
+
///
|
|
88
|
+
/// The native side returns `Map?` (see `BootDiagnosticStore`), deserialized by the
|
|
89
|
+
/// caller via `PatchBootDiagnostic.fromNative`; `null` means it was never recorded.
|
|
90
|
+
static Future<Map<dynamic, dynamic>?> lastBootDiagnostic() async {
|
|
91
|
+
return channel.invokeMethod<Map<dynamic, dynamic>?>('lastBootDiagnostic');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// Absolute path to a plugin-writable cache directory, used for internal staging by
|
|
95
|
+
/// `applyPatchBytes`. Isolated from the existing `PatchManager.patchDir` (one is
|
|
96
|
+
/// temporary staging, the other is the final patch).
|
|
97
|
+
static Future<String?> cacheDir() async {
|
|
98
|
+
return channel.invokeMethod<String>('cacheDir');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/// The host APK's versionCode (uses `longVersionCode` on API 28+). The native side
|
|
102
|
+
/// returns -1 (INVALID_VERSION_CODE) on failure.
|
|
103
|
+
static Future<int?> appVersionCode() async {
|
|
104
|
+
final v = await channel.invokeMethod<int>('appVersionCode');
|
|
105
|
+
if (v == null || v < 0) return null;
|
|
106
|
+
return v;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// The device's preferred ABI, so the server can serve ABI-specific patches.
|
|
110
|
+
static Future<String?> deviceAbi() async {
|
|
111
|
+
return channel.invokeMethod<String>('deviceAbi');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/// The local blacklist of patches known to "crash on install". The native side
|
|
115
|
+
/// returns `List<Map>`, deserialized by the caller via `BlacklistEntry.fromNative`.
|
|
116
|
+
static Future<List<dynamic>?> blacklist() async {
|
|
117
|
+
return channel.invokeMethod<List<dynamic>>('blacklist');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// Clears the blacklist. Use with care: normally only called during debugging.
|
|
121
|
+
static Future<void> clearBlacklist() async {
|
|
122
|
+
await channel.invokeMethod<void>('clearBlacklist');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/// Reports a failed patch apply to the native blacklist. The patch will be
|
|
126
|
+
/// skipped on future attempts.
|
|
127
|
+
static Future<void> reportApplyFailure({
|
|
128
|
+
required String version,
|
|
129
|
+
required String md5,
|
|
130
|
+
String reason = 'APPLY_FAILED',
|
|
131
|
+
}) async {
|
|
132
|
+
await channel.invokeMethod<void>('reportApplyFailure', {
|
|
133
|
+
'version': version,
|
|
134
|
+
'md5': md5,
|
|
135
|
+
'reason': reason,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/// Checks whether a patch version (optionally with md5) is blacklisted.
|
|
140
|
+
static Future<bool> isVersionBlacklisted(String version, {String md5 = ''}) async {
|
|
141
|
+
final found = await channel.invokeMethod<bool>('isVersionBlacklisted', {
|
|
142
|
+
'version': version,
|
|
143
|
+
'md5': md5,
|
|
144
|
+
});
|
|
145
|
+
return found ?? false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// Immediately restarts the whole App process (so a forced update takes effect).
|
|
149
|
+
///
|
|
150
|
+
/// Uses the `restart_app` package with [RestartMode.process], which on Android
|
|
151
|
+
/// starts a fresh launch activity and then terminates the current process
|
|
152
|
+
/// (`exit(0)`). This true cold restart is required so the patched `libapp.so`
|
|
153
|
+
/// and assets are reloaded on the next launch. Without the process kill, only
|
|
154
|
+
/// the Flutter engine would be recreated with the old, already-mapped library.
|
|
155
|
+
///
|
|
156
|
+
/// On non-Android this is a no-op handled upstream by `FlutterPatcher.restart`.
|
|
157
|
+
static Future<void> restartApp() async {
|
|
158
|
+
await Restart.restartApp(mode: RestartMode.process);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import 'dart:convert' show jsonDecode, jsonEncode, utf8;
|
|
2
2
|
|
|
3
|
-
import 'package:
|
|
3
|
+
import 'package:flutter_ota_kit/src/pkg/plugin_core/flutter_ota_kit_plugin_core.dart'
|
|
4
4
|
show BlobOperations, createBlobDatabasePlugin, updateJsonKeyRegex;
|
|
5
5
|
|
|
6
6
|
import 'aws_cloudfront_client.dart'
|
|
@@ -3,7 +3,7 @@ import 'dart:io' show File;
|
|
|
3
3
|
import 'dart:typed_data' show Uint8List;
|
|
4
4
|
|
|
5
5
|
import 'package:crypto/crypto.dart' show Hmac, sha256;
|
|
6
|
-
import 'package:
|
|
6
|
+
import 'package:flutter_ota_kit/src/pkg/plugin_core/flutter_ota_kit_plugin_core.dart'
|
|
7
7
|
show StorageObject;
|
|
8
8
|
import 'package:http/http.dart' show Client, Request, Response;
|
|
9
9
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import 'package:path/path.dart' show basename;
|
|
2
2
|
|
|
3
|
-
import 'package:
|
|
3
|
+
import 'package:flutter_ota_kit/src/pkg/plugin_core/flutter_ota_kit_plugin_core.dart'
|
|
4
4
|
show
|
|
5
5
|
NodeStorageProfile,
|
|
6
6
|
RuntimeStorageProfile,
|
package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/with_cloudfront_signed_url.dart
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import 'package:
|
|
1
|
+
import 'package:flutter_ota_kit/src/pkg/plugin_core/flutter_ota_kit_plugin_core.dart'
|
|
2
2
|
show RuntimeStorageProfile, StoragePlugin, StoragePluginProfiles;
|
|
3
3
|
|
|
4
4
|
import 'aws_cloudfront_signer.dart' show cloudfrontSignedUrl;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export 'src/server_update_source.dart';
|