@_nazmiforreal/flutter-ota 0.1.25 → 0.1.27

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.
Files changed (256) hide show
  1. package/bin/flutter-ota-linux-x64 +0 -0
  2. package/bin/flutter-ota.js +12 -11
  3. package/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +46 -13
  4. package/dart-src/packages/cli-tools/lib/src/backend.dart +6 -6
  5. package/dart-src/packages/cli-tools/lib/src/commands/build.dart +2 -0
  6. package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +7 -7
  7. package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +4 -1
  8. package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +1 -1
  9. package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +1 -1
  10. package/dart-src/packages/cli-tools/lib/src/config.dart +5 -5
  11. package/dart-src/packages/cli-tools/lib/src/operations.dart +2 -2
  12. package/dart-src/packages/cli-tools/pubspec.yaml +8 -21
  13. package/dart-src/packages/cli-tools/test/backends_test.dart +3 -3
  14. package/dart-src/packages/cli-tools/test/cli_test.dart +1 -1
  15. package/dart-src/packages/cli-tools/test/mocks/mock_aws_s3_client.dart +2 -2
  16. package/dart-src/packages/cli-tools/test/mocks/mock_d1_client.dart +2 -2
  17. package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +4 -1
  18. package/dart-src/packages/cli-tools/test/mocks/mock_postgres_client.dart +2 -2
  19. package/dart-src/packages/cli-tools/test/pocketbase_smoke_test.dart +1 -1
  20. package/dart-src/root/README.md +394 -0
  21. package/dart-src/root/analysis_options.yaml +21 -0
  22. package/dart-src/root/android/build.gradle +116 -0
  23. package/dart-src/root/android/consumer-rules.pro +23 -0
  24. package/dart-src/root/android/settings.gradle +1 -0
  25. package/dart-src/root/android/src/main/AndroidManifest.xml +21 -0
  26. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/ApplyResult.kt +52 -0
  27. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/BlacklistStore.kt +154 -0
  28. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/BootDiagnosticStore.kt +150 -0
  29. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/CrashGuard.kt +271 -0
  30. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherApplication.kt +201 -0
  31. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherAutoInitProvider.kt +93 -0
  32. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherPlugin.kt +245 -0
  33. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/LoaderHook.kt +298 -0
  34. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/PatchManager.kt +1579 -0
  35. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/PatcherConfig.kt +173 -0
  36. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/SignatureVerifier.kt +177 -0
  37. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/CrashGuardTest.kt +61 -0
  38. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/LoaderHookTest.kt +25 -0
  39. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/PatchManagerTest.kt +401 -0
  40. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/PatcherConfigTest.kt +30 -0
  41. package/dart-src/root/bin/pack.dart +182 -0
  42. package/dart-src/root/lib/flutter_ota_kit.dart +1170 -0
  43. package/dart-src/root/lib/src/app_version_resolver.dart +36 -0
  44. package/dart-src/root/lib/src/aws_update_source.dart +97 -0
  45. package/dart-src/root/lib/src/blacklist.dart +54 -0
  46. package/dart-src/root/lib/src/boot_diagnostic.dart +138 -0
  47. package/dart-src/root/lib/src/cloudflare_update_source.dart +97 -0
  48. package/dart-src/root/lib/src/flutter_ota_app.dart +69 -0
  49. package/dart-src/root/lib/src/io.dart +48 -0
  50. package/dart-src/root/lib/src/io_stub.dart +19 -0
  51. package/dart-src/root/lib/src/ota_progress_overlay.dart +987 -0
  52. package/dart-src/root/lib/src/pack/pack.dart +434 -0
  53. package/dart-src/root/lib/src/patch_info.dart +12 -0
  54. package/dart-src/root/lib/src/patcher_channel.dart +160 -0
  55. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_database.dart +1 -1
  56. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_s3_client.dart +1 -1
  57. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_storage.dart +1 -1
  58. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_storage_profile.dart +1 -1
  59. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/with_cloudfront_signed_url.dart +1 -1
  60. package/dart-src/root/lib/src/pkg/client/flutter_ota_kit_client.dart +1 -0
  61. package/dart-src/root/lib/src/pkg/client/src/server_update_source.dart +46 -0
  62. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/cloudflare_worker_database.dart +1 -1
  63. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_build_where.dart +1 -1
  64. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_bundle_mapper.dart +3 -3
  65. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_database.dart +1 -1
  66. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_database_plugin.dart +2 -2
  67. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_s3_client.dart +1 -1
  68. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_storage.dart +1 -1
  69. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_storage_profile.dart +1 -1
  70. package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/status.dart +1 -1
  71. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/flutter_ota_kit_plugin_core.dart +1 -1
  72. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_blob_database_plugin.dart +1 -1
  73. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/filter_compatible_app_versions.dart +1 -1
  74. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/get_update_info.dart +1 -1
  75. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/request_update_bundle_state.dart +1 -1
  76. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/resolve_update_info_from_bundles.dart +1 -1
  77. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/types.dart +2 -2
  78. package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_bundle_mapper.dart +2 -2
  79. package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_database.dart +26 -15
  80. package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_storage.dart +1 -1
  81. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_bundle_mapper.dart +2 -2
  82. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_database.dart +2 -2
  83. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_get_update_info.dart +2 -2
  84. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_storage.dart +1 -1
  85. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_types.dart +1 -1
  86. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_bundle_mapper.dart +2 -2
  87. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_database.dart +2 -2
  88. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_edge_function_database.dart +1 -1
  89. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_edge_function_storage.dart +1 -1
  90. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_storage.dart +1 -1
  91. package/dart-src/root/lib/src/pocketbase_update_source.dart +107 -0
  92. package/dart-src/root/lib/src/postgres_update_source.dart +95 -0
  93. package/dart-src/root/lib/src/shared_update_check.dart +131 -0
  94. package/dart-src/root/lib/src/supabase_update_source.dart +93 -0
  95. package/dart-src/root/pubspec.yaml +54 -0
  96. package/package.json +2 -1
  97. package/scripts/postinstall.js +17 -13
  98. package/scripts/vendor.sh +22 -7
  99. package/dart-src/packages/core/CHANGELOG.md +0 -21
  100. package/dart-src/packages/core/README.md +0 -23
  101. package/dart-src/packages/core/example/lib/main.dart +0 -69
  102. package/dart-src/packages/core/pubspec.yaml +0 -13
  103. package/dart-src/packages/core/test/rollout_test.dart +0 -135
  104. package/dart-src/packages/core/test/semver_test.dart +0 -55
  105. package/dart-src/packages/core/test/semver_vectors.json +0 -1
  106. package/dart-src/packages/core/test/types_test.dart +0 -131
  107. package/dart-src/packages/core/test/uuid_test.dart +0 -40
  108. package/dart-src/packages/core/tool/dbg.dart +0 -7
  109. package/dart-src/plugins/aws/.gitkeep +0 -0
  110. package/dart-src/plugins/aws/CHANGELOG.md +0 -23
  111. package/dart-src/plugins/aws/LICENSE +0 -21
  112. package/dart-src/plugins/aws/README.md +0 -28
  113. package/dart-src/plugins/aws/example/lib/main.dart +0 -60
  114. package/dart-src/plugins/aws/pubspec.yaml +0 -30
  115. package/dart-src/plugins/aws/test/aws_database_test.dart +0 -161
  116. package/dart-src/plugins/aws/test/aws_storage_test.dart +0 -73
  117. package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +0 -78
  118. package/dart-src/plugins/cloudflare/.gitkeep +0 -0
  119. package/dart-src/plugins/cloudflare/CHANGELOG.md +0 -23
  120. package/dart-src/plugins/cloudflare/LICENSE +0 -21
  121. package/dart-src/plugins/cloudflare/README.md +0 -25
  122. package/dart-src/plugins/cloudflare/example/lib/main.dart +0 -62
  123. package/dart-src/plugins/cloudflare/migrations/0001_hot-updater_init.sql +0 -16
  124. package/dart-src/plugins/cloudflare/migrations/0002_hot-updater_0.13.0.sql +0 -8
  125. package/dart-src/plugins/cloudflare/migrations/0003_hot-updater_0.18.0.sql +0 -38
  126. package/dart-src/plugins/cloudflare/migrations/0004_hot-updater_0.29.0.sql +0 -11
  127. package/dart-src/plugins/cloudflare/migrations/0005_hot-updater_0.31.0.sql +0 -24
  128. package/dart-src/plugins/cloudflare/pubspec.yaml +0 -28
  129. package/dart-src/plugins/cloudflare/sql/bundles.sql +0 -42
  130. package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +0 -126
  131. package/dart-src/plugins/cloudflare/test/d1_bundle_mapper_test.dart +0 -106
  132. package/dart-src/plugins/cloudflare/test/d1_database_test.dart +0 -200
  133. package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +0 -261
  134. package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +0 -78
  135. package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +0 -74
  136. package/dart-src/plugins/plugin-core/CHANGELOG.md +0 -20
  137. package/dart-src/plugins/plugin-core/LICENSE +0 -21
  138. package/dart-src/plugins/plugin-core/README.md +0 -21
  139. package/dart-src/plugins/plugin-core/example/lib/main.dart +0 -61
  140. package/dart-src/plugins/plugin-core/pubspec.yaml +0 -23
  141. package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +0 -170
  142. package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_store_test.dart +0 -74
  143. package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +0 -345
  144. package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +0 -843
  145. package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +0 -298
  146. package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +0 -195
  147. package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +0 -56
  148. package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +0 -1589
  149. package/dart-src/plugins/plugin-core/test/helpers_test.dart +0 -283
  150. package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +0 -202
  151. package/dart-src/plugins/plugin-core/test/request_update_bundle_state_test.dart +0 -86
  152. package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +0 -88
  153. package/dart-src/plugins/pocketbase/CHANGELOG.md +0 -23
  154. package/dart-src/plugins/pocketbase/LICENSE +0 -21
  155. package/dart-src/plugins/pocketbase/README.md +0 -54
  156. package/dart-src/plugins/pocketbase/example/lib/main.dart +0 -62
  157. package/dart-src/plugins/pocketbase/pubspec.yaml +0 -26
  158. package/dart-src/plugins/pocketbase/test/pb_uri_test.dart +0 -12
  159. package/dart-src/plugins/postgres/.gitkeep +0 -0
  160. package/dart-src/plugins/postgres/CHANGELOG.md +0 -23
  161. package/dart-src/plugins/postgres/LICENSE +0 -21
  162. package/dart-src/plugins/postgres/README.md +0 -29
  163. package/dart-src/plugins/postgres/example/lib/main.dart +0 -67
  164. package/dart-src/plugins/postgres/pubspec.yaml +0 -26
  165. package/dart-src/plugins/postgres/sql/bundles.sql +0 -45
  166. package/dart-src/plugins/postgres/sql/get_target_app_version_list.sql +0 -21
  167. package/dart-src/plugins/postgres/sql/get_update_info.spec.ts +0 -171
  168. package/dart-src/plugins/postgres/sql/get_update_info_by_app_version.sql +0 -126
  169. package/dart-src/plugins/postgres/sql/get_update_info_by_fingerprint_hash.sql +0 -125
  170. package/dart-src/plugins/postgres/sql/hash_user_id.sql +0 -30
  171. package/dart-src/plugins/postgres/sql/is_device_eligible.sql +0 -237
  172. package/dart-src/plugins/postgres/sql/prepareSql.ts +0 -11
  173. package/dart-src/plugins/postgres/test/mock_postgres_client.dart +0 -320
  174. package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +0 -139
  175. package/dart-src/plugins/postgres/test/postgres_database_test.dart +0 -210
  176. package/dart-src/plugins/postgres/test/postgres_storage_runtime_test.dart +0 -60
  177. package/dart-src/plugins/supabase/CHANGELOG.md +0 -24
  178. package/dart-src/plugins/supabase/LICENSE +0 -21
  179. package/dart-src/plugins/supabase/README.md +0 -53
  180. package/dart-src/plugins/supabase/example/lib/main.dart +0 -63
  181. package/dart-src/plugins/supabase/pubspec.yaml +0 -27
  182. package/dart-src/plugins/supabase/supabase/edge-functions/index.ts +0 -44
  183. package/dart-src/plugins/supabase/supabase/edge-functions/runtime.docker.integration.spec.ts +0 -968
  184. package/dart-src/plugins/supabase/supabase/migrations/20250103114225_init.sql +0 -232
  185. package/dart-src/plugins/supabase/supabase/migrations/20250314000000_hot-updater_0.13.0.sql +0 -137
  186. package/dart-src/plugins/supabase/supabase/migrations/20250516000000_hot-updater_0.18.0.sql +0 -206
  187. package/dart-src/plugins/supabase/supabase/migrations/20251014000000_hot-updater_0.21.0.sql +0 -186
  188. package/dart-src/plugins/supabase/supabase/migrations/20260401000000_hot-updater_0.29.0.sql +0 -505
  189. package/dart-src/plugins/supabase/supabase/migrations/20260414000000_hot-updater_0.30.0.sql +0 -50
  190. package/dart-src/plugins/supabase/supabase/migrations/20260422000000_hot-updater_0.31.0.sql +0 -20
  191. package/dart-src/plugins/supabase/supabase/migrations/20260520014100_hot-updater_rls.sql +0 -75
  192. package/dart-src/plugins/supabase/test/mock_supabase_client.dart +0 -654
  193. package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +0 -115
  194. package/dart-src/plugins/supabase/test/supabase_database_test.dart +0 -198
  195. package/dart-src/plugins/supabase/test/supabase_storage_test.dart +0 -168
  196. /package/dart-src/{packages/core → root}/LICENSE +0 -0
  197. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/flutter_ota_kit_aws.dart +0 -0
  198. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_cloudfront_client.dart +0 -0
  199. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_cloudfront_signer.dart +0 -0
  200. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_config.dart +0 -0
  201. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_ssm_client.dart +0 -0
  202. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/flutter_ota_kit_cloudflare.dart +0 -0
  203. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_client.dart +0 -0
  204. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_config.dart +0 -0
  205. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_config.dart +0 -0
  206. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/flutter_ota_kit_core.dart +0 -0
  207. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/app_update_info.dart +0 -0
  208. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle.dart +0 -0
  209. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle_artifacts.dart +0 -0
  210. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle_patch_artifact.dart +0 -0
  211. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/changed_asset.dart +0 -0
  212. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/get_bundles_args.dart +0 -0
  213. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/metadata.dart +0 -0
  214. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/patch_info.dart +0 -0
  215. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/platform.dart +0 -0
  216. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/rollout.dart +0 -0
  217. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver.dart +0 -0
  218. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver_range.dart +0 -0
  219. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver_version.dart +0 -0
  220. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/strategy.dart +0 -0
  221. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/update_bundle_params.dart +0 -0
  222. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/uuid.dart +0 -0
  223. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/asset_storage_layout.dart +0 -0
  224. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_helpers.dart +0 -0
  225. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_storage_layout.dart +0 -0
  226. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_unit_of_work.dart +0 -0
  227. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_unit_of_work_store.dart +0 -0
  228. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/calculate_pagination.dart +0 -0
  229. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/compression_format.dart +0 -0
  230. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/content_addressed_assets.dart +0 -0
  231. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_database_plugin.dart +0 -0
  232. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_storage_key_builder.dart +0 -0
  233. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_storage_plugin.dart +0 -0
  234. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/generate_min_bundle_id.dart +0 -0
  235. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/is_object.dart +0 -0
  236. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/legacy_asset_storage_layout.dart +0 -0
  237. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/paginate_bundles.dart +0 -0
  238. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/parse_storage_uri.dart +0 -0
  239. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/query_bundles.dart +0 -0
  240. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/storage_helpers.dart +0 -0
  241. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/storage_profile.dart +0 -0
  242. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/uuidv7.dart +0 -0
  243. /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/flutter_ota_kit_pocketbase.dart +0 -0
  244. /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_client.dart +0 -0
  245. /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_config.dart +0 -0
  246. /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/flutter_ota_kit_postgres.dart +0 -0
  247. /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_client.dart +0 -0
  248. /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_config.dart +0 -0
  249. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/edge.dart +0 -0
  250. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/flutter_ota_kit_supabase.dart +0 -0
  251. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/error_message.dart +0 -0
  252. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_client_adapter.dart +0 -0
  253. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_client_http.dart +0 -0
  254. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_config.dart +0 -0
  255. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_signed_url_batcher.dart +0 -0
  256. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/types.dart +0 -0
@@ -0,0 +1,394 @@
1
+ # flutter_ota_kit
2
+
3
+ **English** | [简体中文](README-zh.md)
4
+
5
+ [![pub package](https://img.shields.io/pub/v/flutter_ota_kit.svg)](https://pub.dev/packages/flutter_ota_kit)
6
+ [![Platform](https://img.shields.io/badge/platform-Android-brightgreen)](https://flutter.dev)
7
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
8
+
9
+ **Open-source code push for Flutter Android.**
10
+
11
+ Ship Dart code and asset hotfixes over the air — no store release, no
12
+ forced vendor lock-in, no per-month fees. Patches live on **your** backend
13
+ (Supabase, Postgres, Cloudflare, AWS, or your own object storage) and load
14
+ on the next cold start.
15
+
16
+ If you've used [Shorebird](https://shorebird.dev/),
17
+ [CodePush](https://learn.microsoft.com/en-us/appcenter/distribution/codepush/),
18
+ or [Expo EAS Update](https://docs.expo.dev/eas-update/introduction/) —
19
+ flutter_ota_kit is the same OTA update model for Flutter Android, MIT-licensed,
20
+ with your choice of cloud backend.
21
+
22
+ ![Feature demo: apply a patch, cold restart, and rollback](doc/feature-presentation.gif)
23
+
24
+ ---
25
+
26
+ ## How it compares
27
+
28
+ | | flutter_ota_kit | Shorebird | CodePush (React Native) |
29
+ |-----------------------|--------------------------------------------------|-------------------------------|-------------------------------|
30
+ | **Framework** | Flutter | Flutter | React Native |
31
+ | **Platforms** | Android | Android + iOS | Android + iOS (retired 2025) |
32
+ | **Hosting** | Your backend or your own CDN — see [Backends](doc/backends.md) | Shorebird cloud (managed) | AppCenter cloud (deprecated) |
33
+ | **Update scope** | Dart AOT + Flutter assets | Dart code (engine-level diff) | JS bundle |
34
+ | **Takes effect** | Next cold start | Next restart | Next restart |
35
+ | **Cost** | Free (MIT) | Free tier + paid plans | — |
36
+ | **Backend flexibility** | Supabase / Postgres / Cloudflare / AWS / bring-your-own | Cloud-managed only | — |
37
+ | **Forced updates** | Yes — built-in progress UI | Yes | Yes |
38
+ | **Crash rollback** | Automatic + bad-patch blacklist | Yes | Yes |
39
+ | **Signing** | Ed25519 (Android 13+) + MD5 | Yes | — |
40
+
41
+ **Choose Shorebird** if you need iOS support today or want a fully managed cloud.
42
+
43
+ **Choose flutter_ota_kit** if you need OTA on infrastructure you control —
44
+ enterprise apps, regional distribution, air-gapped deployments, or non-Play
45
+ distribution channels. Bring your own backend, your own CDN, your own auth.
46
+
47
+ > Google Play and some stores restrict downloading executable code at runtime.
48
+ > flutter_ota_kit targets self-controlled, enterprise, or permissive
49
+ > distribution channels. Verify your channel's policy before shipping.
50
+
51
+ ---
52
+
53
+ ## Features
54
+
55
+ - **OTA code push** — replace the Dart AOT `libapp.so` and Flutter assets on the
56
+ next cold start, with MD5 + Ed25519 integrity checks.
57
+ - **Five cloud backends** — Supabase (fully automated), Postgres, Cloudflare (D1 + R2),
58
+ AWS (S3), **PocketBase** (single-binary self-hosted). Or bring your own CDN.
59
+ - **Crash rollback** — automatic on boot failure with a bad-patch blacklist.
60
+ Configurable via `maxCrashCount` and `verifyAfter`.
61
+ - **Forced updates with built-in UI** — opt into zero-click updates with
62
+ `autoApplyUpdates: true`; the SDK shows a download spinner, progress bar, and
63
+ the server's OTA message during the install. No UI code from you.
64
+ - **Staged rollout** — server-side cohort math (per-mille, target-cohorts) lets
65
+ you ship 1% → 5% → 20% → 50% → 100% safely.
66
+ - **Tooling included** — `pack` CLI (builds patches from release APKs),
67
+ `flutter_ota_kit console` (web-based admin UI for the sidecar server),
68
+ runtime diagnostics, sample app.
69
+ - **No native plugin churn** — the patch system uses the Android
70
+ `ContentProvider` to stage files; no platform channel gymnastics, no
71
+ per-vendor SDKs to upgrade.
72
+
73
+ ---
74
+
75
+ ## Try it in 5 minutes (no server needed)
76
+
77
+ ```bash
78
+ git clone https://github.com/HYPER12755/flutter-ota-kit.git
79
+ cd flutter_ota_kit/example
80
+ flutter pub get
81
+ flutter build apk --release
82
+ flutter install
83
+ ```
84
+
85
+ 1. Launch the app — it shows the **original** `assets/patch_demo.png`.
86
+ 2. Tap **Apply patch**.
87
+ 3. Swipe the app away from recents and reopen.
88
+ 4. The image has changed — the asset patch took effect.
89
+ 5. Tap **Rollback** → restart → original image is back.
90
+
91
+ The example bundles a precompiled `patch.zip`. Everything runs offline on
92
+ the device. For HTTP-based testing with a real backend, see
93
+ [Getting Started](doc/getting-started.md) or the
94
+ [Beginner Guide](doc/beginner-guide.md) (the human-narrated walkthrough).
95
+
96
+ ---
97
+
98
+ ## Requirements
99
+
100
+ | Item | Requirement |
101
+ |----------------------------|--------------------------------------------------------------------|
102
+ | **Platform** | Android only (iOS / macOS / Windows / Linux / Web are no-op) |
103
+ | **Dart SDK** | `>=3.13.0 <4.0.0` |
104
+ | **Flutter** | `>=3.47.0` |
105
+ | **Android `minSdk`** | 24 |
106
+ | **Android `compileSdk`** | 36 |
107
+ | **ABI** | `armeabi-v7a` / `arm64-v8a` / `x86_64` |
108
+ | **NDK** | 27.0.12077973+ |
109
+ | **AGP** | 8.11.1+ (including AGP 9.x) |
110
+ | **Kotlin** | 2.2.20+ (or AGP 9 built-in Kotlin) |
111
+ | **Java / JVM** | 17 |
112
+
113
+ On iOS / macOS / Windows / Linux / Web, every API is safe to call but is a
114
+ **no-op** — the plugin logs a one-time warning and returns safe defaults.
115
+ This makes it safe to ship `flutter_ota_kit` in cross-platform code without
116
+ guarding every call site.
117
+
118
+ ---
119
+
120
+ ## Quick start
121
+
122
+ ### 1. Install
123
+
124
+ ```yaml
125
+ # pubspec.yaml
126
+ dependencies:
127
+ flutter_ota_kit: ^0.1.10
128
+ ```
129
+
130
+ ### 2. Initialize
131
+
132
+ Call `setupFlutterOta()` (or the bare `FlutterPatcher.init()`) before `runApp()`:
133
+
134
+ ```dart
135
+ import 'package:flutter/material.dart';
136
+ import 'package:flutter_ota_kit/flutter_ota_kit.dart';
137
+ import 'flutter_ota_kit_setup.dart'; // generated by `flutter-ota init <backend>`
138
+
139
+ Future<void> main() async {
140
+ WidgetsFlutterBinding.ensureInitialized();
141
+ await setupFlutterOta(); // configures the backend + zero-click forced updates
142
+ runApp(const FlutterOtaApp(child: MyApp()));
143
+ }
144
+ ```
145
+
146
+ `FlutterOtaApp` is the zero-code wrapper that gives the SDK an
147
+ `Overlay` to render the built-in forced-update progress UI into. The user
148
+ sees a download spinner + progress bar + the server's OTA message during
149
+ the install — no UI from you.
150
+
151
+ Disable the overlay app-wide with `FlutterPatcher.showUpdateUi = false`
152
+ (or `FlutterOtaApp(showUpdateUi: false)`), or host it yourself by
153
+ assigning `MaterialApp.navigatorKey = FlutterPatcher.navigatorKey`.
154
+
155
+ ### 3. Build a patch
156
+
157
+ Rebuild the release APK, then run `pack` against the new APK to produce
158
+ `dist/patch.zip` and `dist/manifest.json`:
159
+
160
+ ```bash
161
+ dart run flutter_ota_kit:pack \
162
+ --apk build/app/outputs/flutter-apk/app-release.apk \
163
+ --version 1.0.0-h1 \
164
+ --target-version-code 100
165
+ ```
166
+
167
+ To include assets (since 0.1.3), append `--assets`:
168
+
169
+ ```bash
170
+ dart run flutter_ota_kit:pack \
171
+ --apk build/app/outputs/flutter-apk/app-release.apk \
172
+ --version 1.0.1 \
173
+ --target-version-code 100 \
174
+ --assets assets/hero.png,assets/strings/zh.json
175
+ ```
176
+
177
+ Each asset must be registered in the new APK's `pubspec.yaml`. Upload
178
+ `patch.zip` to your backend (or CDN) and have your update endpoint return
179
+ a `PatchInfo` pointing at it.
180
+
181
+ ### 4. Apply a patch (most apps just do this)
182
+
183
+ ```dart
184
+ final result = await FlutterPatcher.applyPatch(
185
+ PatchInfo(
186
+ version: 'fix-1',
187
+ patchUrl: 'https://your-cdn.com/v100/patch.zip',
188
+ md5: '0123456789abcdef0123456789abcdef',
189
+ targetVersionCode: 100,
190
+ ),
191
+ );
192
+
193
+ if (result.ok) {
194
+ // Patch takes effect on the next cold start.
195
+ }
196
+ ```
197
+
198
+ If you manage the download yourself, use `applyPatchBytes`:
199
+
200
+ ```dart
201
+ final bytes = await loadPatchFromYourSource();
202
+ final result = await FlutterPatcher.applyPatchBytes(
203
+ bytes,
204
+ version: '1.0.0-h1',
205
+ targetVersionCode: 100,
206
+ );
207
+ ```
208
+
209
+ ### 5. Roll back
210
+
211
+ ```dart
212
+ await FlutterPatcher.rollback();
213
+ ```
214
+
215
+ Deletes the current patch. The app reverts to the APK's built-in version on
216
+ the next cold start. This is **not** the same as a server-side rollback —
217
+ this deletes the local patch, it doesn't change what's in your backend.
218
+
219
+ ---
220
+
221
+ ## How it works
222
+
223
+ ```text
224
+ ┌─────────────────┐
225
+ │ Your backend │ Supabase / Postgres / Cloudflare / AWS / PocketBase / your CDN
226
+ │ (or CDN) │ stores: patch.zip (the diff) + manifest.json (the metadata)
227
+ └────────▲────────┘
228
+ │ 1. check-for-update on launch
229
+ │ 2. server returns PatchInfo (url, md5, signature, version, shouldForceUpdate)
230
+ │
231
+ ┌────────┴────────┐
232
+ │ The app │
233
+ │ │ 3. download patch.zip
234
+ │ │ 4. verify MD5 + Ed25519 signature
235
+ │ │ 5. write to local patch dir (atomic rename)
236
+ │ │ 6. cold-restart (forced) OR next cold start (staged)
237
+ │ │ 7. loader hook reads patched libapp.so + asset overlays
238
+ │ │ 8. boot succeeds → keep patch
239
+ │ │ boot fails → auto-rollback + blacklist
240
+ └─────────────────┘
241
+ ```
242
+
243
+ A patch is a **byte-for-byte diff** of the new APK's `libapp.so` against the
244
+ old one (the versionCode the user has installed). Asset patches overlay
245
+ `flutter_assets/` files by path. The patch never ships inside the running
246
+ process — it loads on the next cold start. The cold-restart for forced
247
+ updates goes through `restart_app` (with `RestartMode.process`, which
248
+ actually exits the process so the new code reloads).
249
+
250
+ Full internals: [Architecture](doc/architecture.md).
251
+
252
+ ---
253
+
254
+ ## What can and can't be patched
255
+
256
+ | ✅ Hot-patchable | ❌ Not hot-patchable |
257
+ |-----------------------------------------------------------------|-----------------------------------------------------------------------|
258
+ | Anything in `lib/` — widgets, logic, routes, constants, tests | Native code (Kotlin / Java / C++ in `android/src/main/`) |
259
+ | Pure-Dart package upgrades (same native side) | `AndroidManifest.xml` changes |
260
+ | Flutter asset files (registered in `pubspec.yaml`) | APK `res/` (icons, layouts, strings.xml) |
261
+ | New `Image.asset()` / `rootBundle.load()` calls pick up new bytes | Flutter Engine upgrades |
262
+ | | Adding or removing native plugins |
263
+ | | Removing assets that exist in the base APK |
264
+ | | `pubspec.yaml` font registration changes |
265
+
266
+ For edge cases (ProGuard/R8, multi-ABI / flavors, state migrations, asset
267
+ overlay quirks), see [API Reference → What can be patched](doc/api-reference.md#what-can-and-cannot-be-patched).
268
+
269
+ ---
270
+
271
+ ## Safety
272
+
273
+ ### Crash protection (on by default)
274
+
275
+ The plugin is **fail-fast**: if a patch causes a boot failure, it auto-rolls
276
+ back and blacklists the offending version so it won't be retried. Tunable
277
+ via `maxCrashCount` (default 1) and `verifyAfter` (default 5s).
278
+
279
+ Design and Android version differences:
280
+ [Crash Protection](doc/crash-protection.md).
281
+
282
+ ### Integrity & signing
283
+
284
+ - **MD5** verification is strongly recommended; omit only for quick testing.
285
+ - **Ed25519 signature** verification is available on Android 13+ (API 33).
286
+ Falls back to MD5-only on older Android (with a warning).
287
+ - Patches are bound to the host APK's `versionCode` — old patches expire
288
+ after an APK upgrade and the server stops serving them.
289
+ - Always download over HTTPS. Keep private signing keys on the server only,
290
+ never in the app bundle.
291
+
292
+ ### Production tips
293
+
294
+ - **Stage your rollout** (1% → 5% → 20% → 50% → 100%) and monitor crash
295
+ rate at each stage. The SDK records `lastBootDiagnostic` per device.
296
+ - **Report diagnostics** to your analytics pipeline so you can spot a bad
297
+ patch before users hit the app.
298
+ - **Prepare for emergency rollback** — stop returning the bad patch from
299
+ your endpoint; devices that already tripped crash protection have
300
+ rolled back locally and won't re-download.
301
+
302
+ Full release workflow + diagnostic-reporting code:
303
+ [Production Playbook](doc/production-playbook.md).
304
+
305
+ ---
306
+
307
+ ## FAQ
308
+
309
+ **Q: Must the patch and base APK use the same Flutter version?**
310
+ A: Yes. `libapp.so` is tightly coupled to the Flutter Engine ABI.
311
+ After upgrading the SDK, ship a new release.
312
+
313
+ **Q: Why doesn't a patch take effect immediately?**
314
+ A: Once `libapp.so` is loaded by the current process, it can't be swapped
315
+ at runtime. The patch is written to disk and loaded on the next cold
316
+ start. Forced updates trigger a process restart so the new code loads
317
+ immediately.
318
+
319
+ **Q: Why does each patch need a `targetVersionCode`?**
320
+ A: Two reasons: (1) old patches expire after an APK upgrade (so a user on
321
+ v2 doesn't accidentally load a v1-targeted patch), and (2) the server
322
+ won't ship a patch built for v1 to a user on v2 (the SDK filters them
323
+ out at update-check time).
324
+
325
+ **Q: How does the client report its app version, and why must it match `--target-app-version`?**
326
+ A: The client reports an `appVersion` that the backend matches against
327
+ each bundle's `target_app_version`. By default this is **auto-detected at
328
+ runtime** from the host app's `versionName` via `package_info_plus` — no
329
+ build flag needed. You can override it explicitly with
330
+ `--dart-define=APP_VERSION=1.2.3` or `SupabaseUpdateConfig.appVersion`.
331
+ The backend keeps only bundles whose `target_app_version` is
332
+ semver-compatible with the reported version. **If they don't match, the
333
+ backend returns no bundle and the app silently stays "up to date"** — so
334
+ always deploy with `--target-app-version` equal to the app's real
335
+ `versionName` (e.g. `1.0.1` for a `version: 1.0.1+2` pubspec). A mismatched
336
+ version is the most common cause of "the update never arrives".
337
+
338
+ More questions: [Full FAQ](doc/faq.md).
339
+
340
+ ---
341
+
342
+ ## Documentation
343
+
344
+ **Guides** (narrative, in reading order)
345
+
346
+ - [Beginner Guide](doc/beginner-guide.md) — zero-to-first-OTA walkthrough, as a human would do it
347
+ - [Getting Started](doc/getting-started.md) — scaffold → build → deploy in 5 minutes
348
+ - [Developer Guide](doc/developer-guide.md) — full workflow reference (init, migrate, build, deploy, SDK API, targeting, troubleshooting)
349
+ - [Configuration](doc/configuration.md) — every env var, `.env`, resolution order, secrets policy
350
+ - [Backends](doc/backends.md) — Supabase / Postgres / Cloudflare / AWS / PocketBase setup, env vars, CLI snippets
351
+ - [Production Playbook](doc/production-playbook.md) — staged rollout, diagnostics, emergency rollback
352
+
353
+ **Reference**
354
+
355
+ - [API Reference](doc/api-reference.md) — `FlutterPatcher` methods, error codes, asset patching
356
+ - [Architecture](doc/architecture.md) — internals, server protocol, signing, advanced config
357
+ - [CLI Reference](doc/cli-reference.md) — every command, subcommand, and flag
358
+ - [Crash Protection](doc/crash-protection.md) — auto-rollback, blacklist, Android version differences
359
+ - [Golden Testing](doc/golden-testing.md) — how Flutter's pixel-perfect test system works (and how this project uses it)
360
+
361
+ **Other**
362
+
363
+ - [FAQ](doc/faq.md) — versioning, cold start, store policy
364
+ - [CHANGELOG.md](CHANGELOG.md) — release notes
365
+
366
+ 中文文档: [README-zh.md](README-zh.md) · [api-reference-zh](doc/api-reference-zh.md) ·
367
+ [architecture-zh](doc/architecture-zh.md) · [crash-protection-zh](doc/crash-protection-zh.md) ·
368
+ [getting-started-zh](doc/getting-started-zh.md) · [production-playbook-zh](doc/production-playbook-zh.md) ·
369
+ [faq-zh](doc/faq-zh.md)
370
+
371
+ ---
372
+
373
+ ## Who's using it?
374
+
375
+ If you run flutter_ota_kit in production, [open an issue](https://github.com/HYPER12755/flutter-ota-kit/issues) and tell us about your use case — we'd love to list you here.
376
+
377
+ ---
378
+
379
+ ## Contributing
380
+
381
+ Issues and PRs are welcome.
382
+
383
+ Before submitting, please make sure:
384
+
385
+ - `flutter analyze` reports no warnings
386
+ - `flutter test` is fully green
387
+ - If you touched native code, you've run a real-device end-to-end patch / rollback flow
388
+ - If you added a new public API, you've documented it on `dartdoc_options.yaml` topics (the doc appears on pub.dev)
389
+
390
+ ---
391
+
392
+ ## License
393
+
394
+ MIT
@@ -0,0 +1,21 @@
1
+ # Root analysis options for the flutter_ota_kit monorepo.
2
+ #
3
+ # The repo is a monorepo: `packages/*`, `plugins/*`, and `example/` are each
4
+ # standalone Dart/Flutter packages that are analyzed (and lint-configured)
5
+ # independently. Excluding them here keeps `dart analyze .` focused on the root
6
+ # device-SDK package (`lib/` + `test/`), which is the only thing the root
7
+ # package owns. Each sub-package is analyzed on its own and is already clean.
8
+ analyzer:
9
+ exclude:
10
+ - packages/**
11
+ - plugins/**
12
+ - example/**
13
+ - node_modules/**
14
+ - npm/**
15
+ - build/**
16
+ - android/**
17
+ - ios/**
18
+ - web/**
19
+ - windows/**
20
+ - macos/**
21
+ - linux/**
@@ -0,0 +1,116 @@
1
+ group = "com.flutter_patcher.flutter_patcher"
2
+ version = "1.0-SNAPSHOT"
3
+
4
+ buildscript {
5
+ ext.kotlin_version = "2.2.20"
6
+ repositories {
7
+ google()
8
+ mavenCentral()
9
+ }
10
+
11
+ dependencies {
12
+ classpath("com.android.tools.build:gradle:8.11.1")
13
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
14
+ }
15
+ }
16
+
17
+ allprojects {
18
+ repositories {
19
+ google()
20
+ mavenCentral()
21
+ }
22
+ }
23
+
24
+ apply plugin: "com.android.library"
25
+
26
+ // AGP 9 compiles Kotlin itself ("built-in Kotlin") and rejects the
27
+ // kotlin-android plugin. AGP 9 hosts that opt out via
28
+ // `android.builtInKotlin=false` -- which is what the Flutter 3.44 template
29
+ // generates -- still need us to bring our own KGP, exactly like AGP 8 hosts.
30
+ // So the switch is built-in Kotlin, not the AGP major on its own.
31
+ def agpMajor = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0] as int
32
+ def builtInKotlinFlag = project.findProperty("android.builtInKotlin")
33
+ def builtInKotlin = agpMajor >= 9 &&
34
+ (builtInKotlinFlag == null || Boolean.parseBoolean(builtInKotlinFlag.toString()))
35
+
36
+ // Keep this as a literal `apply plugin:` at line start even though it is
37
+ // guarded. The Flutter Gradle Plugin regex-matches this file's text to decide
38
+ // whether a subproject brings its own KGP; when it finds none it applies
39
+ // kotlin-android to us itself, which hard-fails under built-in Kotlin. The cost
40
+ // is that FGP prints its "plugin applies KGP" warning on AGP 9 even when this
41
+ // branch never runs.
42
+ if (!builtInKotlin) {
43
+ apply plugin: "kotlin-android"
44
+ }
45
+
46
+ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
47
+ // Kotlin incremental cache path relativization can fail on Windows when a
48
+ // Flutter plugin is compiled from Pub Cache on C: into an app on another
49
+ // drive. Keep this library compile non-incremental; app modules are
50
+ // unaffected.
51
+ incremental = false
52
+ }
53
+
54
+ android {
55
+ namespace = "com.flutter_patcher.flutter_patcher"
56
+
57
+ compileSdk = 36
58
+ ndkVersion = "27.0.12077973"
59
+
60
+ compileOptions {
61
+ sourceCompatibility = JavaVersion.VERSION_17
62
+ targetCompatibility = JavaVersion.VERSION_17
63
+ }
64
+
65
+ if (!builtInKotlin) {
66
+ // Contributed by KGP, not by AGP -- only exists where KGP was applied.
67
+ kotlinOptions {
68
+ jvmTarget = JavaVersion.VERSION_17
69
+ }
70
+ }
71
+
72
+ sourceSets {
73
+ main.java.srcDirs += "src/main/kotlin"
74
+ test.java.srcDirs += "src/test/kotlin"
75
+ }
76
+
77
+ defaultConfig {
78
+ minSdk = 24
79
+
80
+ // ABI 白名单:覆盖主流 Android 设备。Flutter 默认只出 arm64-v8a,
81
+ // 但插件要兼容 32 位旧机型和 x86_64 模拟器。
82
+ ndk {
83
+ abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'
84
+ }
85
+
86
+ // 让 AGP 把本插件的 consumer R8 规则合并到宿主 release build
87
+ consumerProguardFiles 'consumer-rules.pro'
88
+ }
89
+
90
+ dependencies {
91
+ testImplementation("org.jetbrains.kotlin:kotlin-test")
92
+ testImplementation("org.json:json:20240303")
93
+ testImplementation("org.mockito:mockito-core:5.0.0")
94
+ }
95
+
96
+ testOptions {
97
+ unitTests.all {
98
+ useJUnitPlatform()
99
+ jvmArgs "-Dnet.bytebuddy.experimental=true"
100
+
101
+ testLogging {
102
+ events "passed", "skipped", "failed", "standardOut", "standardError"
103
+ outputs.upToDateWhen {false}
104
+ showStandardStreams = true
105
+ }
106
+ }
107
+ }
108
+ }
109
+
110
+ if (builtInKotlin) {
111
+ kotlin {
112
+ compilerOptions {
113
+ jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
114
+ }
115
+ }
116
+ }
@@ -0,0 +1,23 @@
1
+ # flutter_patcher consumer ProGuard / R8 rules.
2
+ # AGP merges this file into the host app release R8 configuration.
3
+ # Goal: keep the small set of Android / Flutter classes referenced by manifest
4
+ # entries or startup reflection.
5
+
6
+ # Application: referenced by AndroidManifest android:name.
7
+ -keep class com.flutter_patcher.flutter_patcher.FlutterPatcherApplication { *; }
8
+
9
+ # Auto-init ContentProvider: declared by the plugin manifest.
10
+ -keep class com.flutter_patcher.flutter_patcher.FlutterPatcherAutoInitProvider { *; }
11
+
12
+ # Flutter plugin: registered by GeneratedPluginRegistrant.
13
+ -keep class com.flutter_patcher.flutter_patcher.FlutterPatcherPlugin { *; }
14
+
15
+ # PatchedFlutterLoader is installed reflectively and overrides engine init.
16
+ -keep class com.flutter_patcher.flutter_patcher.PatchedFlutterLoader {
17
+ <init>(...);
18
+ public void ensureInitializationComplete(android.content.Context, java.lang.String[]);
19
+ }
20
+
21
+ # Flutter Engine reflection targets used by LoaderHook.
22
+ -keep class io.flutter.FlutterInjector { *; }
23
+ -keep class io.flutter.embedding.engine.loader.FlutterLoader { *; }
@@ -0,0 +1 @@
1
+ rootProject.name = 'flutter_patcher'
@@ -0,0 +1,21 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.flutter_patcher.flutter_patcher">
3
+ <!-- 宿主 App 必须自行声明 INTERNET 权限以允许补丁下载 / checkUpdate 发起 HTTP 请求 -->
4
+
5
+ <application>
6
+ <!--
7
+ 自动初始化:在 ContentProvider.onCreate 阶段(早于 Application.onCreate /
8
+ 任何 FlutterActivity.onCreate)完成熔断检查 + FlutterLoader 反射替换,宿主
9
+ 无需接入 Application。
10
+
11
+ 需要关掉的极少数场景(如在 Application.attachBaseContext 预热 FlutterEngine)
12
+ 可在宿主 Manifest 里用 tools:node="remove" 移除本 provider,然后显式调
13
+ FlutterPatcherApplication.attachPatcher(base)。详见 README。
14
+ -->
15
+ <provider
16
+ android:name="com.flutter_patcher.flutter_patcher.FlutterPatcherAutoInitProvider"
17
+ android:authorities="${applicationId}.flutter_patcher.autoinit"
18
+ android:exported="false"
19
+ android:initOrder="100" />
20
+ </application>
21
+ </manifest>
@@ -0,0 +1,52 @@
1
+ package com.flutter_patcher.flutter_patcher
2
+
3
+ /**
4
+ * [PatchManager.applyPatch] 的结构化返回值。
5
+ *
6
+ * 通过 MethodChannel 以 `Map<String, Any?>` 形式序列化到 Dart 侧,由
7
+ * `PatchApplyResult.fromNative()` 反解。
8
+ *
9
+ * 错误码与使用建议见 [ApplyErrorCode]。
10
+ */
11
+ internal data class ApplyResult(
12
+ val ok: Boolean,
13
+ val errorCode: String? = null,
14
+ val message: String? = null
15
+ ) {
16
+ fun toMap(): Map<String, Any?> = mapOf(
17
+ "ok" to ok,
18
+ "error" to errorCode,
19
+ "message" to message
20
+ )
21
+
22
+ companion object {
23
+ val SUCCESS = ApplyResult(ok = true)
24
+
25
+ fun failure(code: String, message: String? = null): ApplyResult =
26
+ ApplyResult(ok = false, errorCode = code, message = message)
27
+ }
28
+ }
29
+
30
+ /**
31
+ * applyPatch 可能的失败原因。调用方(Dart 业务层)按需要做不同处理:
32
+ *
33
+ * - [INVALID_ARGS]:服务端下发的 JSON 缺字段 → 告警服务端
34
+ * - [BLACKLISTED]:(version, md5) 已在本地黑名单 → 服务端告警,下架补丁
35
+ * - [NETWORK]:下载失败(重试后依然失败)→ 稍后重试
36
+ * - [MD5_MISMATCH]:下载文件 md5 不匹配 → CDN 脏数据或服务端 md5 算错,检查后重试
37
+ * - [SIGNATURE_INVALID]:Ed25519 验签失败(或 strict 模式下 API < 33 直接拒绝)→
38
+ * 可能被篡改,**不建议**自动重试
39
+ * - [IO_ERROR]:磁盘 / 文件系统错误(磁盘满、权限)→ 稍后重试
40
+ * - [UNKNOWN]:未被分类的异常 → 上报到监控,看日志定位
41
+ */
42
+ internal object ApplyErrorCode {
43
+ const val INVALID_ARGS = "INVALID_ARGS"
44
+ const val BLACKLISTED = "BLACKLISTED"
45
+ const val NETWORK = "NETWORK"
46
+ const val MD5_MISMATCH = "MD5_MISMATCH"
47
+ const val SIGNATURE_INVALID = "SIGNATURE_INVALID"
48
+ const val UNSUPPORTED_ABI = "UNSUPPORTED_ABI"
49
+ const val ASSET_PACKAGE_INVALID = "ASSET_PACKAGE_INVALID"
50
+ const val IO_ERROR = "IO_ERROR"
51
+ const val UNKNOWN = "UNKNOWN"
52
+ }