@apollohg/react-native-rich-text-editor 2.0.0

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 (494) hide show
  1. package/LICENSE +160 -0
  2. package/README.md +259 -0
  3. package/RUST-STANDARD-LIBRARY-NOTICES.html +8266 -0
  4. package/ReactNativeProseEditor.podspec +59 -0
  5. package/THIRD_PARTY_NOTICES.md +31155 -0
  6. package/android/build.gradle +188 -0
  7. package/android/consumer-rules.pro +18 -0
  8. package/android/expo/build.gradle +17 -0
  9. package/android/src/debug/java/com/apollohg/editor/viewer/PreparedProseDrawInstrumentation.kt +7 -0
  10. package/android/src/main/assets/editor-icons/MaterialIcons.json +2236 -0
  11. package/android/src/main/assets/editor-icons/MaterialIcons.ttf +0 -0
  12. package/android/src/main/java/com/apollohg/editor/AndroidApiCompat.kt +19 -0
  13. package/android/src/main/java/com/apollohg/editor/AndroidCollaborationTransport.kt +860 -0
  14. package/android/src/main/java/com/apollohg/editor/CaretGeometry.kt +91 -0
  15. package/android/src/main/java/com/apollohg/editor/CodeHighlighting.kt +141 -0
  16. package/android/src/main/java/com/apollohg/editor/DecodedBitmapBudget.kt +250 -0
  17. package/android/src/main/java/com/apollohg/editor/EditorAddons.kt +133 -0
  18. package/android/src/main/java/com/apollohg/editor/EditorBlockSpacing.kt +51 -0
  19. package/android/src/main/java/com/apollohg/editor/EditorBoxDrawable.kt +16 -0
  20. package/android/src/main/java/com/apollohg/editor/EditorBoxDrawing.kt +234 -0
  21. package/android/src/main/java/com/apollohg/editor/EditorCheckboxSpan.kt +87 -0
  22. package/android/src/main/java/com/apollohg/editor/EditorCodeHighlighting.kt +120 -0
  23. package/android/src/main/java/com/apollohg/editor/EditorDecorationLayer.java +18 -0
  24. package/android/src/main/java/com/apollohg/editor/EditorDocumentLayout.kt +626 -0
  25. package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +1042 -0
  26. package/android/src/main/java/com/apollohg/editor/EditorEditTextAnnotations.kt +191 -0
  27. package/android/src/main/java/com/apollohg/editor/EditorEditTextCaret.kt +100 -0
  28. package/android/src/main/java/com/apollohg/editor/EditorEditTextClipboard.kt +101 -0
  29. package/android/src/main/java/com/apollohg/editor/EditorEditTextComposition.kt +220 -0
  30. package/android/src/main/java/com/apollohg/editor/EditorEditTextConfiguration.kt +316 -0
  31. package/android/src/main/java/com/apollohg/editor/EditorEditTextCorrection.kt +303 -0
  32. package/android/src/main/java/com/apollohg/editor/EditorEditTextDeferredUpdates.kt +327 -0
  33. package/android/src/main/java/com/apollohg/editor/EditorEditTextDeletion.kt +258 -0
  34. package/android/src/main/java/com/apollohg/editor/EditorEditTextDiagnostics.kt +83 -0
  35. package/android/src/main/java/com/apollohg/editor/EditorEditTextDragDrop.kt +67 -0
  36. package/android/src/main/java/com/apollohg/editor/EditorEditTextExternalComposition.kt +357 -0
  37. package/android/src/main/java/com/apollohg/editor/EditorEditTextHardwareInput.kt +277 -0
  38. package/android/src/main/java/com/apollohg/editor/EditorEditTextImages.kt +280 -0
  39. package/android/src/main/java/com/apollohg/editor/EditorEditTextInputLifecycle.kt +183 -0
  40. package/android/src/main/java/com/apollohg/editor/EditorEditTextInputPreparation.kt +79 -0
  41. package/android/src/main/java/com/apollohg/editor/EditorEditTextInputTraits.kt +384 -0
  42. package/android/src/main/java/com/apollohg/editor/EditorEditTextLayout.kt +100 -0
  43. package/android/src/main/java/com/apollohg/editor/EditorEditTextNativeMutation.kt +243 -0
  44. package/android/src/main/java/com/apollohg/editor/EditorEditTextPlaceholder.kt +154 -0
  45. package/android/src/main/java/com/apollohg/editor/EditorEditTextRenderPatch.kt +347 -0
  46. package/android/src/main/java/com/apollohg/editor/EditorEditTextRendering.kt +313 -0
  47. package/android/src/main/java/com/apollohg/editor/EditorEditTextRustCommands.kt +281 -0
  48. package/android/src/main/java/com/apollohg/editor/EditorEditTextSelection.kt +352 -0
  49. package/android/src/main/java/com/apollohg/editor/EditorEditTextState.kt +113 -0
  50. package/android/src/main/java/com/apollohg/editor/EditorEditTextTaskMarkers.kt +92 -0
  51. package/android/src/main/java/com/apollohg/editor/EditorEditTextTextCommit.kt +327 -0
  52. package/android/src/main/java/com/apollohg/editor/EditorEditTextToolbar.kt +84 -0
  53. package/android/src/main/java/com/apollohg/editor/EditorEditTextUpdates.kt +245 -0
  54. package/android/src/main/java/com/apollohg/editor/EditorGlyphHeightCursorDrawable.kt +42 -0
  55. package/android/src/main/java/com/apollohg/editor/EditorHeightBehavior.kt +13 -0
  56. package/android/src/main/java/com/apollohg/editor/EditorInputConnection.kt +923 -0
  57. package/android/src/main/java/com/apollohg/editor/EditorInputCoordinates.kt +191 -0
  58. package/android/src/main/java/com/apollohg/editor/EditorInputCorrection.kt +107 -0
  59. package/android/src/main/java/com/apollohg/editor/EditorInputCursorGeometry.kt +128 -0
  60. package/android/src/main/java/com/apollohg/editor/EditorInputSnapshots.kt +134 -0
  61. package/android/src/main/java/com/apollohg/editor/EditorInputSurroundingDeletion.kt +321 -0
  62. package/android/src/main/java/com/apollohg/editor/EditorKeyboardToolbarView.kt +851 -0
  63. package/android/src/main/java/com/apollohg/editor/EditorMentionStyle.kt +192 -0
  64. package/android/src/main/java/com/apollohg/editor/EditorReconciliationWatcher.kt +46 -0
  65. package/android/src/main/java/com/apollohg/editor/EditorStyleSheet.kt +303 -0
  66. package/android/src/main/java/com/apollohg/editor/EditorStyleSheetSpans.kt +253 -0
  67. package/android/src/main/java/com/apollohg/editor/EditorSurfaceInputConnection.kt +67 -0
  68. package/android/src/main/java/com/apollohg/editor/EditorTextDecorationDrawing.kt +143 -0
  69. package/android/src/main/java/com/apollohg/editor/EditorTextSurface.kt +712 -0
  70. package/android/src/main/java/com/apollohg/editor/EditorTextSurfaceDragDrop.kt +94 -0
  71. package/android/src/main/java/com/apollohg/editor/EditorTextSurfaceInteraction.kt +708 -0
  72. package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +650 -0
  73. package/android/src/main/java/com/apollohg/editor/EditorV2Adapter.kt +912 -0
  74. package/android/src/main/java/com/apollohg/editor/EditorV2AdapterMutations.kt +402 -0
  75. package/android/src/main/java/com/apollohg/editor/EditorV2AdapterRendering.kt +123 -0
  76. package/android/src/main/java/com/apollohg/editor/EditorV2AdapterSelection.kt +203 -0
  77. package/android/src/main/java/com/apollohg/editor/EditorV2Backend.kt +120 -0
  78. package/android/src/main/java/com/apollohg/editor/EditorV2Driver.kt +87 -0
  79. package/android/src/main/java/com/apollohg/editor/EditorV2Registry.kt +97 -0
  80. package/android/src/main/java/com/apollohg/editor/EditorV2RenderValidation.kt +409 -0
  81. package/android/src/main/java/com/apollohg/editor/ExpoAutoGrowStyleSizePublisher.kt +34 -0
  82. package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +266 -0
  83. package/android/src/main/java/com/apollohg/editor/ImeTextCoordinateMapper.kt +102 -0
  84. package/android/src/main/java/com/apollohg/editor/KeyboardToolbarImeAnimationController.kt +101 -0
  85. package/android/src/main/java/com/apollohg/editor/MentionSuggestionChipView.kt +234 -0
  86. package/android/src/main/java/com/apollohg/editor/NativeCollaborationTransportRegistry.kt +493 -0
  87. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +813 -0
  88. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewAtoms.kt +68 -0
  89. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewBinding.kt +126 -0
  90. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewDiagnostics.kt +208 -0
  91. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewErrors.kt +146 -0
  92. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewEvents.kt +150 -0
  93. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewExternalComposition.kt +39 -0
  94. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewFocus.kt +176 -0
  95. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewLifecycle.kt +302 -0
  96. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewMentions.kt +259 -0
  97. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewNativeActions.kt +179 -0
  98. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewOutsideTap.kt +214 -0
  99. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewPendingUpdates.kt +376 -0
  100. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewProperties.kt +234 -0
  101. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewPropertyRetries.kt +197 -0
  102. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewToolbar.kt +285 -0
  103. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewTypes.kt +28 -0
  104. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewUpdates.kt +271 -0
  105. package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +687 -0
  106. package/android/src/main/java/com/apollohg/editor/NativeEditorOutsideTapDispatcher.kt +380 -0
  107. package/android/src/main/java/com/apollohg/editor/NativeEditorV2Numbers.kt +45 -0
  108. package/android/src/main/java/com/apollohg/editor/NativeEditorViewRegistry.kt +372 -0
  109. package/android/src/main/java/com/apollohg/editor/NativeImagePipeline.kt +22 -0
  110. package/android/src/main/java/com/apollohg/editor/NativeToolbar.kt +193 -0
  111. package/android/src/main/java/com/apollohg/editor/NativeToolbarIcons.kt +177 -0
  112. package/android/src/main/java/com/apollohg/editor/NativeToolbarItem.kt +274 -0
  113. package/android/src/main/java/com/apollohg/editor/OkHttpCollaborationSocket.kt +102 -0
  114. package/android/src/main/java/com/apollohg/editor/OrderedListMarkerFormatter.kt +130 -0
  115. package/android/src/main/java/com/apollohg/editor/PointerTransparentView.java +18 -0
  116. package/android/src/main/java/com/apollohg/editor/PositionBridge.kt +128 -0
  117. package/android/src/main/java/com/apollohg/editor/ProseViewerView.kt +854 -0
  118. package/android/src/main/java/com/apollohg/editor/RemoteSelectionOverlayView.kt +314 -0
  119. package/android/src/main/java/com/apollohg/editor/RenderBlockSpans.kt +314 -0
  120. package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +224 -0
  121. package/android/src/main/java/com/apollohg/editor/RenderBridgeBlockStyles.kt +407 -0
  122. package/android/src/main/java/com/apollohg/editor/RenderBridgeElements.kt +544 -0
  123. package/android/src/main/java/com/apollohg/editor/RenderBridgeInlineAndAtoms.kt +631 -0
  124. package/android/src/main/java/com/apollohg/editor/RenderBridgeParagraphs.kt +215 -0
  125. package/android/src/main/java/com/apollohg/editor/RenderImageDecoder.kt +748 -0
  126. package/android/src/main/java/com/apollohg/editor/RenderImageLoader.kt +900 -0
  127. package/android/src/main/java/com/apollohg/editor/RenderLayoutConstants.kt +117 -0
  128. package/android/src/main/java/com/apollohg/editor/RenderMediaSpans.kt +337 -0
  129. package/android/src/main/java/com/apollohg/editor/RenderParagraphSpans.kt +182 -0
  130. package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +951 -0
  131. package/android/src/main/java/com/apollohg/editor/SharedNativeImagePipeline.kt +78 -0
  132. package/android/src/main/java/com/apollohg/editor/UniffiEditorV2Backend.kt +261 -0
  133. package/android/src/main/java/com/apollohg/editor/viewer/AndroidProseLayoutEngine.kt +1656 -0
  134. package/android/src/main/java/com/apollohg/editor/viewer/FabricAttachmentSidecars.kt +80 -0
  135. package/android/src/main/java/com/apollohg/editor/viewer/FabricLeaseHandleBridge.kt +59 -0
  136. package/android/src/main/java/com/apollohg/editor/viewer/FallbackSelectionGeometry.kt +450 -0
  137. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseDrawingView.kt +678 -0
  138. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseInstrumentation.kt +608 -0
  139. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayout.kt +186 -0
  140. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayoutCache.kt +393 -0
  141. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayoutRegistry.kt +828 -0
  142. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseTheme.kt +316 -0
  143. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseViewerManager.kt +900 -0
  144. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseViewerPackage.kt +16 -0
  145. package/android/src/main/java/com/apollohg/editor/viewer/PreparedViewerAtoms.kt +89 -0
  146. package/android/src/main/java/com/apollohg/editor/viewer/ViewerCodeHighlighting.kt +91 -0
  147. package/android/src/main/java/com/apollohg/editor/viewer/ViewerDocument.kt +453 -0
  148. package/android/src/main/java/com/apollohg/editor/viewer/ViewerFontEnvironment.kt +282 -0
  149. package/android/src/main/java/com/apollohg/editor/viewer/ViewerImagePipeline.kt +460 -0
  150. package/android/src/main/jni/CMakeLists.txt +85 -0
  151. package/android/src/main/jni/PreparedProseViewerMeasurementsManager.cpp +334 -0
  152. package/android/src/main/jni/ReactNativeProseEditorSpec.h +17 -0
  153. package/android/src/main/jni/reactnativeproseeditor.h +6 -0
  154. package/android/src/main/res/values/attrs.xml +6 -0
  155. package/android/src/release/java/com/apollohg/editor/viewer/PreparedProseDrawInstrumentation.kt +9 -0
  156. package/app.plugin.js +62 -0
  157. package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseMeasurementsManager.h +62 -0
  158. package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerComponentDescriptor.h +30 -0
  159. package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerShadowNode.cpp +192 -0
  160. package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerShadowNode.h +46 -0
  161. package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerState.h +145 -0
  162. package/dist/AtomHost.d.ts +17 -0
  163. package/dist/AtomHost.js +135 -0
  164. package/dist/DefaultAtomChip.d.ts +2 -0
  165. package/dist/DefaultAtomChip.js +22 -0
  166. package/dist/EditorAddon.d.ts +32 -0
  167. package/dist/EditorAddon.js +29 -0
  168. package/dist/EditorAddonNormalization.d.ts +6 -0
  169. package/dist/EditorAddonNormalization.js +48 -0
  170. package/dist/EditorMentionThemeNormalization.d.ts +8 -0
  171. package/dist/EditorMentionThemeNormalization.js +22 -0
  172. package/dist/EditorMentionThemeValidation.d.ts +12 -0
  173. package/dist/EditorMentionThemeValidation.js +95 -0
  174. package/dist/EditorStyleSheet.d.ts +4 -0
  175. package/dist/EditorStyleSheet.js +10 -0
  176. package/dist/EditorStyleSheetNormalization.d.ts +3 -0
  177. package/dist/EditorStyleSheetNormalization.js +226 -0
  178. package/dist/EditorStyleSheetTypes.d.ts +151 -0
  179. package/dist/EditorStyleSheetTypes.js +2 -0
  180. package/dist/EditorTheme.d.ts +117 -0
  181. package/dist/EditorTheme.js +41 -0
  182. package/dist/EditorToolbar.d.ts +4 -0
  183. package/dist/EditorToolbar.js +17 -0
  184. package/dist/EditorToolbarComponent.d.ts +13 -0
  185. package/dist/EditorToolbarComponent.js +24 -0
  186. package/dist/EditorToolbarItems.d.ts +9 -0
  187. package/dist/EditorToolbarItems.js +58 -0
  188. package/dist/EditorToolbarRegistry.d.ts +59 -0
  189. package/dist/EditorToolbarRegistry.js +175 -0
  190. package/dist/EditorToolbarTypes.d.ts +273 -0
  191. package/dist/EditorToolbarTypes.js +2 -0
  192. package/dist/EditorToolbarVisuals.d.ts +154 -0
  193. package/dist/EditorToolbarVisuals.js +228 -0
  194. package/dist/EditorUpdateRevision.d.ts +8 -0
  195. package/dist/EditorUpdateRevision.js +22 -0
  196. package/dist/ExternalTextComposition.d.ts +45 -0
  197. package/dist/ExternalTextComposition.js +537 -0
  198. package/dist/ImageLoadingPolicy.d.ts +54 -0
  199. package/dist/ImageLoadingPolicy.js +60 -0
  200. package/dist/NativeEditorBoundaryError.d.ts +111 -0
  201. package/dist/NativeEditorBoundaryError.js +268 -0
  202. package/dist/NativeEditorBridge.d.ts +9 -0
  203. package/dist/NativeEditorBridge.js +37 -0
  204. package/dist/NativeEditorCollaborationTransport.d.ts +154 -0
  205. package/dist/NativeEditorCollaborationTransport.js +275 -0
  206. package/dist/NativeEditorCreateConfig.d.ts +16 -0
  207. package/dist/NativeEditorCreateConfig.js +249 -0
  208. package/dist/NativeEditorCreateJson.d.ts +76 -0
  209. package/dist/NativeEditorCreateJson.js +474 -0
  210. package/dist/NativeEditorCreateValidation.d.ts +32 -0
  211. package/dist/NativeEditorCreateValidation.js +150 -0
  212. package/dist/NativeEditorDocumentBridge.d.ts +72 -0
  213. package/dist/NativeEditorDocumentBridge.js +305 -0
  214. package/dist/NativeEditorDocumentHandle.d.ts +70 -0
  215. package/dist/NativeEditorDocumentHandle.js +89 -0
  216. package/dist/NativeEditorLocalAwareness.d.ts +34 -0
  217. package/dist/NativeEditorLocalAwareness.js +153 -0
  218. package/dist/NativeEditorNativeModule.d.ts +48 -0
  219. package/dist/NativeEditorNativeModule.js +34 -0
  220. package/dist/NativeEditorRenderNormalization.d.ts +22 -0
  221. package/dist/NativeEditorRenderNormalization.js +274 -0
  222. package/dist/NativeEditorResultNormalization.d.ts +108 -0
  223. package/dist/NativeEditorResultNormalization.js +400 -0
  224. package/dist/NativeEditorTypes.d.ts +318 -0
  225. package/dist/NativeEditorTypes.js +15 -0
  226. package/dist/NativeEditorV2Decimal.d.ts +5 -0
  227. package/dist/NativeEditorV2Decimal.js +18 -0
  228. package/dist/NativeProseViewer.d.ts +122 -0
  229. package/dist/NativeProseViewer.js +130 -0
  230. package/dist/NativeRichTextEditor.d.ts +3 -0
  231. package/dist/NativeRichTextEditor.js +6 -0
  232. package/dist/ResourceLimits.d.ts +100 -0
  233. package/dist/ResourceLimits.js +80 -0
  234. package/dist/RichTextEditor.d.ts +4 -0
  235. package/dist/RichTextEditor.js +25 -0
  236. package/dist/RichTextEditorNativeTypes.d.ts +122 -0
  237. package/dist/RichTextEditorNativeTypes.js +2 -0
  238. package/dist/RichTextEditorNativeView.d.ts +12 -0
  239. package/dist/RichTextEditorNativeView.js +15 -0
  240. package/dist/RichTextEditorSerialization.d.ts +53 -0
  241. package/dist/RichTextEditorSerialization.js +283 -0
  242. package/dist/RichTextEditorTypes.d.ts +281 -0
  243. package/dist/RichTextEditorTypes.js +2 -0
  244. package/dist/ViewerConfiguration.d.ts +22 -0
  245. package/dist/ViewerConfiguration.js +6 -0
  246. package/dist/YjsCollaboration.d.ts +146 -0
  247. package/dist/YjsCollaboration.js +496 -0
  248. package/dist/addons.d.ts +193 -0
  249. package/dist/addons.js +122 -0
  250. package/dist/atomConstants.d.ts +3 -0
  251. package/dist/atomConstants.js +6 -0
  252. package/dist/atomInstances.d.ts +35 -0
  253. package/dist/atomInstances.js +79 -0
  254. package/dist/atomPolicy.d.ts +4 -0
  255. package/dist/atomPolicy.js +47 -0
  256. package/dist/atomUpdates.d.ts +2 -0
  257. package/dist/atomUpdates.js +38 -0
  258. package/dist/atoms.d.ts +92 -0
  259. package/dist/atoms.js +222 -0
  260. package/dist/attributeValidation.d.ts +4 -0
  261. package/dist/attributeValidation.js +97 -0
  262. package/dist/contentExpression.d.ts +6 -0
  263. package/dist/contentExpression.js +335 -0
  264. package/dist/index.d.ts +21 -0
  265. package/dist/index.js +78 -0
  266. package/dist/listMarkers.d.ts +15 -0
  267. package/dist/listMarkers.js +21 -0
  268. package/dist/schemaDefinition.d.ts +143 -0
  269. package/dist/schemaDefinition.js +206 -0
  270. package/dist/schemaDocument.d.ts +17 -0
  271. package/dist/schemaDocument.js +69 -0
  272. package/dist/schemaDocumentConstruction.d.ts +5 -0
  273. package/dist/schemaDocumentConstruction.js +125 -0
  274. package/dist/schemaNormalization.d.ts +29 -0
  275. package/dist/schemaNormalization.js +313 -0
  276. package/dist/schemaPresets.d.ts +68 -0
  277. package/dist/schemaPresets.js +198 -0
  278. package/dist/schemaResolution.d.ts +9 -0
  279. package/dist/schemaResolution.js +211 -0
  280. package/dist/schemas.d.ts +5 -0
  281. package/dist/schemas.js +28 -0
  282. package/dist/specs/PreparedProseViewerNativeComponent.d.ts +37 -0
  283. package/dist/specs/PreparedProseViewerNativeComponent.js +6 -0
  284. package/dist/useEditorToolbarInteractions.d.ts +12 -0
  285. package/dist/useEditorToolbarInteractions.js +218 -0
  286. package/dist/useEditorToolbarItems.d.ts +8 -0
  287. package/dist/useEditorToolbarItems.js +280 -0
  288. package/dist/useEditorToolbarPresentation.d.ts +4 -0
  289. package/dist/useEditorToolbarPresentation.js +203 -0
  290. package/dist/useEditorToolbarState.d.ts +63 -0
  291. package/dist/useEditorToolbarState.js +108 -0
  292. package/dist/useFocusPreservingFrames.d.ts +17 -0
  293. package/dist/useFocusPreservingFrames.js +108 -0
  294. package/dist/useNativeEditor.d.ts +66 -0
  295. package/dist/useNativeEditor.js +370 -0
  296. package/dist/useRichTextEditorCommands.d.ts +21 -0
  297. package/dist/useRichTextEditorCommands.js +299 -0
  298. package/dist/useRichTextEditorEvents.d.ts +18 -0
  299. package/dist/useRichTextEditorEvents.js +216 -0
  300. package/dist/useRichTextEditorMentions.d.ts +11 -0
  301. package/dist/useRichTextEditorMentions.js +316 -0
  302. package/dist/useRichTextEditorPresentation.d.ts +5 -0
  303. package/dist/useRichTextEditorPresentation.js +178 -0
  304. package/dist/useRichTextEditorState.d.ts +180 -0
  305. package/dist/useRichTextEditorState.js +405 -0
  306. package/dist/useRichTextEditorUpdates.d.ts +10 -0
  307. package/dist/useRichTextEditorUpdates.js +308 -0
  308. package/dist/useViewerAtoms.d.ts +38 -0
  309. package/dist/useViewerAtoms.js +288 -0
  310. package/expo-module.config.json +13 -0
  311. package/ios/EditorAddons.swift +354 -0
  312. package/ios/EditorCodeHighlighting.swift +184 -0
  313. package/ios/EditorCore.xcframework/Info.plist +44 -0
  314. package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
  315. package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
  316. package/ios/EditorInteractionOverlays.swift +697 -0
  317. package/ios/EditorLayoutManager.swift +971 -0
  318. package/ios/EditorNodeTypes.swift +37 -0
  319. package/ios/EditorStyleSheet.swift +461 -0
  320. package/ios/EditorTextView+AttributedPatch.swift +305 -0
  321. package/ios/EditorTextView+Binding.swift +61 -0
  322. package/ios/EditorTextView+Caret.swift +333 -0
  323. package/ios/EditorTextView+Commands.swift +203 -0
  324. package/ios/EditorTextView+Delegate.swift +65 -0
  325. package/ios/EditorTextView+Diagnostics.swift +225 -0
  326. package/ios/EditorTextView+DragDrop.swift +290 -0
  327. package/ios/EditorTextView+ExternalComposition.swift +364 -0
  328. package/ios/EditorTextView+Images.swift +354 -0
  329. package/ios/EditorTextView+InputTraits.swift +200 -0
  330. package/ios/EditorTextView+KeyboardInput.swift +318 -0
  331. package/ios/EditorTextView+Layout.swift +245 -0
  332. package/ios/EditorTextView+MarkedText.swift +267 -0
  333. package/ios/EditorTextView+NativeInputLifecycle.swift +372 -0
  334. package/ios/EditorTextView+NativeMutation.swift +471 -0
  335. package/ios/EditorTextView+Reconciliation.swift +97 -0
  336. package/ios/EditorTextView+RenderPatch.swift +573 -0
  337. package/ios/EditorTextView+Rendering.swift +539 -0
  338. package/ios/EditorTextView+Selection.swift +542 -0
  339. package/ios/EditorTextView.swift +953 -0
  340. package/ios/EditorTheme.swift +626 -0
  341. package/ios/EditorV2Adapter+Commands.swift +204 -0
  342. package/ios/EditorV2Adapter+Envelope.swift +238 -0
  343. package/ios/EditorV2Adapter+Mutation.swift +392 -0
  344. package/ios/EditorV2Adapter+Registry.swift +185 -0
  345. package/ios/EditorV2Adapter+RenderState.swift +360 -0
  346. package/ios/EditorV2Adapter+RenderValidation.swift +420 -0
  347. package/ios/EditorV2Adapter+Selection.swift +329 -0
  348. package/ios/EditorV2Adapter+TextCommands.swift +430 -0
  349. package/ios/EditorV2Adapter.swift +376 -0
  350. package/ios/EditorV2Shadow.swift +353 -0
  351. package/ios/Generated_editor_core.swift +2378 -0
  352. package/ios/NativeCodeHighlighting.swift +159 -0
  353. package/ios/NativeCollaborationSocket.swift +144 -0
  354. package/ios/NativeCollaborationTransport+Protocol.swift +258 -0
  355. package/ios/NativeCollaborationTransport+Socket.swift +379 -0
  356. package/ios/NativeCollaborationTransport+Validation.swift +103 -0
  357. package/ios/NativeCollaborationTransport.swift +322 -0
  358. package/ios/NativeCollaborationTransportRegistry.swift +521 -0
  359. package/ios/NativeEditorExpoView+AccessoryRetry.swift +219 -0
  360. package/ios/NativeEditorExpoView+Binding.swift +268 -0
  361. package/ios/NativeEditorExpoView+Diagnostics.swift +66 -0
  362. package/ios/NativeEditorExpoView+Events.swift +131 -0
  363. package/ios/NativeEditorExpoView+Focus.swift +169 -0
  364. package/ios/NativeEditorExpoView+MentionRetry.swift +288 -0
  365. package/ios/NativeEditorExpoView+Mentions.swift +288 -0
  366. package/ios/NativeEditorExpoView+Properties.swift +300 -0
  367. package/ios/NativeEditorExpoView+Toolbar.swift +86 -0
  368. package/ios/NativeEditorExpoView+Updates.swift +312 -0
  369. package/ios/NativeEditorExpoView.swift +349 -0
  370. package/ios/NativeEditorModule.swift +856 -0
  371. package/ios/NativeEditorViewRegistry.swift +226 -0
  372. package/ios/NativeToolbar+ButtonStyle.swift +214 -0
  373. package/ios/NativeToolbar+Buttons.swift +228 -0
  374. package/ios/NativeToolbar+Diagnostics.swift +114 -0
  375. package/ios/NativeToolbar+Menu.swift +101 -0
  376. package/ios/NativeToolbar+Models.swift +519 -0
  377. package/ios/NativeToolbar+Placeholder.swift +39 -0
  378. package/ios/NativeToolbar.swift +612 -0
  379. package/ios/OrderedListMarkerFormatter.swift +103 -0
  380. package/ios/PositionBridge.swift +596 -0
  381. package/ios/ProseViewerView.swift +462 -0
  382. package/ios/RenderBridge+Atoms.swift +288 -0
  383. package/ios/RenderBridge+Attachments.swift +246 -0
  384. package/ios/RenderBridge+BlockBoundaries.swift +313 -0
  385. package/ios/RenderBridge+Style.swift +343 -0
  386. package/ios/RenderBridge.swift +704 -0
  387. package/ios/RichTextEditorView.swift +930 -0
  388. package/ios/SharedNativeImagePipeline+Cache.swift +189 -0
  389. package/ios/SharedNativeImagePipeline+Transport.swift +328 -0
  390. package/ios/SharedNativeImagePipeline.swift +533 -0
  391. package/ios/Viewer/CoreTextProseLayoutEngine+AttributedText.swift +436 -0
  392. package/ios/Viewer/CoreTextProseLayoutEngine+Models.swift +322 -0
  393. package/ios/Viewer/CoreTextProseLayoutEngine.swift +635 -0
  394. package/ios/Viewer/Fabric/PREPPreparedProseViewerComponentView.h +4 -0
  395. package/ios/Viewer/Fabric/PREPPreparedProseViewerComponentView.mm +767 -0
  396. package/ios/Viewer/Fabric/PreparedProseMeasurementsManager.mm +118 -0
  397. package/ios/Viewer/NativeSVGImageDecoder.h +9 -0
  398. package/ios/Viewer/NativeSVGImageDecoder.mm +106 -0
  399. package/ios/Viewer/NativeSVGPreflight.h +11 -0
  400. package/ios/Viewer/NativeSVGPreflight.mm +219 -0
  401. package/ios/Viewer/PreparedProseDrawingView.swift +639 -0
  402. package/ios/Viewer/PreparedProseInstrumentation.swift +387 -0
  403. package/ios/Viewer/PreparedProseLayout.swift +326 -0
  404. package/ios/Viewer/PreparedProseLayoutCache.swift +766 -0
  405. package/ios/Viewer/PreparedProseLayoutRegistry+Artifacts.swift +377 -0
  406. package/ios/Viewer/PreparedProseLayoutRegistry+Fabric.swift +582 -0
  407. package/ios/Viewer/PreparedProseLayoutRegistry+FabricOwnership.swift +128 -0
  408. package/ios/Viewer/PreparedProseLayoutRegistry.swift +381 -0
  409. package/ios/Viewer/ViewerDocument.swift +575 -0
  410. package/ios/Viewer/ViewerFontEnvironment.swift +283 -0
  411. package/ios/Viewer/ViewerImagePipeline.swift +478 -0
  412. package/ios/editor_coreFFI/editor_coreFFI.h +1023 -0
  413. package/ios/editor_coreFFI/module.modulemap +7 -0
  414. package/package.json +151 -0
  415. package/react-native.config.js +17 -0
  416. package/rust/android/arm64-v8a/libeditor_core.so +0 -0
  417. package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
  418. package/rust/android/x86/libeditor_core.so +0 -0
  419. package/rust/android/x86_64/libeditor_core.so +0 -0
  420. package/rust/bindings/kotlin/uniffi/editor_core/editor_core.kt +3146 -0
  421. package/src/AtomHost.tsx +180 -0
  422. package/src/DefaultAtomChip.tsx +27 -0
  423. package/src/EditorAddon.ts +65 -0
  424. package/src/EditorAddonNormalization.ts +49 -0
  425. package/src/EditorMentionThemeNormalization.ts +25 -0
  426. package/src/EditorMentionThemeValidation.ts +133 -0
  427. package/src/EditorStyleSheet.ts +9 -0
  428. package/src/EditorStyleSheetNormalization.ts +219 -0
  429. package/src/EditorStyleSheetTypes.ts +189 -0
  430. package/src/EditorTheme.ts +196 -0
  431. package/src/EditorToolbar.tsx +31 -0
  432. package/src/EditorToolbarComponent.tsx +23 -0
  433. package/src/EditorToolbarItems.tsx +64 -0
  434. package/src/EditorToolbarRegistry.tsx +254 -0
  435. package/src/EditorToolbarTypes.tsx +322 -0
  436. package/src/EditorToolbarVisuals.tsx +270 -0
  437. package/src/EditorUpdateRevision.ts +26 -0
  438. package/src/ExternalTextComposition.ts +658 -0
  439. package/src/ImageLoadingPolicy.ts +125 -0
  440. package/src/NativeEditorBoundaryError.ts +345 -0
  441. package/src/NativeEditorBridge.ts +93 -0
  442. package/src/NativeEditorCollaborationTransport.ts +493 -0
  443. package/src/NativeEditorCreateConfig.ts +326 -0
  444. package/src/NativeEditorCreateJson.ts +579 -0
  445. package/src/NativeEditorCreateValidation.ts +178 -0
  446. package/src/NativeEditorDocumentBridge.ts +485 -0
  447. package/src/NativeEditorDocumentHandle.ts +168 -0
  448. package/src/NativeEditorLocalAwareness.ts +168 -0
  449. package/src/NativeEditorNativeModule.ts +97 -0
  450. package/src/NativeEditorRenderNormalization.ts +320 -0
  451. package/src/NativeEditorResultNormalization.ts +469 -0
  452. package/src/NativeEditorTypes.ts +366 -0
  453. package/src/NativeEditorV2Decimal.ts +13 -0
  454. package/src/NativeProseViewer.tsx +356 -0
  455. package/src/NativeRichTextEditor.tsx +33 -0
  456. package/src/ResourceLimits.ts +187 -0
  457. package/src/RichTextEditor.tsx +27 -0
  458. package/src/RichTextEditorNativeTypes.tsx +144 -0
  459. package/src/RichTextEditorNativeView.tsx +21 -0
  460. package/src/RichTextEditorSerialization.tsx +381 -0
  461. package/src/RichTextEditorTypes.tsx +327 -0
  462. package/src/ViewerConfiguration.ts +23 -0
  463. package/src/YjsCollaboration.ts +692 -0
  464. package/src/addons.ts +311 -0
  465. package/src/atomConstants.ts +3 -0
  466. package/src/atomInstances.ts +123 -0
  467. package/src/atomPolicy.ts +47 -0
  468. package/src/atomUpdates.ts +52 -0
  469. package/src/atoms.ts +379 -0
  470. package/src/attributeValidation.ts +116 -0
  471. package/src/contentExpression.ts +338 -0
  472. package/src/index.ts +295 -0
  473. package/src/listMarkers.ts +18 -0
  474. package/src/schemaDefinition.ts +395 -0
  475. package/src/schemaDocument.ts +92 -0
  476. package/src/schemaDocumentConstruction.ts +148 -0
  477. package/src/schemaNormalization.ts +367 -0
  478. package/src/schemaPresets.ts +250 -0
  479. package/src/schemaResolution.ts +242 -0
  480. package/src/schemas.ts +43 -0
  481. package/src/specs/PreparedProseViewerNativeComponent.ts +40 -0
  482. package/src/useEditorToolbarInteractions.tsx +329 -0
  483. package/src/useEditorToolbarItems.tsx +390 -0
  484. package/src/useEditorToolbarPresentation.tsx +483 -0
  485. package/src/useEditorToolbarState.tsx +172 -0
  486. package/src/useFocusPreservingFrames.ts +171 -0
  487. package/src/useNativeEditor.ts +534 -0
  488. package/src/useRichTextEditorCommands.tsx +478 -0
  489. package/src/useRichTextEditorEvents.tsx +342 -0
  490. package/src/useRichTextEditorMentions.tsx +441 -0
  491. package/src/useRichTextEditorPresentation.tsx +563 -0
  492. package/src/useRichTextEditorState.tsx +620 -0
  493. package/src/useRichTextEditorUpdates.tsx +403 -0
  494. package/src/useViewerAtoms.tsx +427 -0
@@ -0,0 +1,900 @@
1
+ package com.apollohg.editor
2
+
3
+ import android.graphics.Bitmap
4
+ import android.os.Handler
5
+ import android.os.Looper
6
+ import android.util.LruCache
7
+ import java.security.MessageDigest
8
+ import java.util.concurrent.Callable
9
+ import java.util.concurrent.Executors
10
+ import java.util.concurrent.Future
11
+ import java.util.concurrent.FutureTask
12
+ import java.util.concurrent.PriorityBlockingQueue
13
+ import java.util.concurrent.RejectedExecutionException
14
+ import java.util.concurrent.ScheduledFuture
15
+ import java.util.concurrent.Semaphore
16
+ import java.util.concurrent.ThreadPoolExecutor
17
+ import java.util.concurrent.TimeUnit
18
+ import java.util.concurrent.atomic.AtomicBoolean
19
+ import java.util.concurrent.atomic.AtomicLong
20
+
21
+ internal object RenderImageLoader {
22
+ // Public policy values are per-policy upper bounds. These process-wide
23
+ // ceilings keep aggregate work bounded when many differently configured
24
+ // editor/viewer instances coexist; contention may yield lower concurrency.
25
+ private const val GLOBAL_WORKERS = 4
26
+ private const val GLOBAL_QUEUE_CAPACITY = 256
27
+ private const val GLOBAL_ADMISSION_LIMIT = GLOBAL_WORKERS + GLOBAL_QUEUE_CAPACITY
28
+ private const val REJECTION_NOTIFICATION_LIMIT = 64
29
+ private const val CACHE_ENTRY_OVERHEAD_BYTES = 64
30
+
31
+ internal class CacheKey(val digest: ByteArray) {
32
+ override fun equals(other: Any?): Boolean =
33
+ other is CacheKey && digest.contentEquals(other.digest)
34
+
35
+ override fun hashCode(): Int = digest.contentHashCode()
36
+ }
37
+
38
+ internal data class RequestKey(val digest: CacheKey, val policy: ImageLoadingPolicy)
39
+ internal class PreparedSource internal constructor(
40
+ internal val source: String,
41
+ internal val policy: ImageLoadingPolicy
42
+ )
43
+ internal class LoadHandle(private val cancelAction: () -> Unit) {
44
+ private val finished = AtomicBoolean(false)
45
+ private val finishListeners = mutableListOf<() -> Unit>()
46
+
47
+ fun cancel() {
48
+ try {
49
+ runCatching { cancelAction() }
50
+ } finally {
51
+ finish()
52
+ }
53
+ }
54
+
55
+ fun onFinished(listener: () -> Unit) {
56
+ val invokeNow = synchronized(finishListeners) {
57
+ if (finished.get()) {
58
+ true
59
+ } else {
60
+ finishListeners += listener
61
+ false
62
+ }
63
+ }
64
+ if (invokeNow) listener()
65
+ }
66
+
67
+ internal fun finish() {
68
+ if (!finished.compareAndSet(false, true)) return
69
+ val listeners = synchronized(finishListeners) {
70
+ finishListeners.toList().also { finishListeners.clear() }
71
+ }
72
+ listeners.forEach { runCatching { it() } }
73
+ }
74
+ }
75
+ private data class Callback(
76
+ val id: Long,
77
+ val cancelled: AtomicBoolean,
78
+ val admissionReleased: AtomicBoolean,
79
+ val handle: LoadHandle,
80
+ val ownerId: Long?,
81
+ val ownerLimitBytes: Long,
82
+ val priority: DecodedBitmapPriority,
83
+ val deliver: (DecodedBitmapLease?) -> Unit
84
+ )
85
+ private class PendingRequest(
86
+ val key: RequestKey,
87
+ val source: String,
88
+ val callbacks: MutableList<Callback>,
89
+ val cancellation: RenderImageDecoder.Cancellation,
90
+ val startedAtMs: Long
91
+ ) {
92
+ @Volatile var priority: DecodedBitmapPriority = callbacks.first().priority
93
+ val deadlineMs: Long = deadlineAfter(startedAtMs, key.policy.requestTimeoutMs)
94
+ var future: Future<*>? = null
95
+ var timeoutFuture: ScheduledFuture<*>? = null
96
+ var submitted = false
97
+ var dispatching = false
98
+ val started = AtomicBoolean(false)
99
+ val terminal = AtomicBoolean(false)
100
+ val workerSlotReleased = AtomicBoolean(false)
101
+ }
102
+ private class PrioritizedTask(
103
+ private val request: PendingRequest,
104
+ private val sequence: Long,
105
+ action: () -> Unit
106
+ ) : FutureTask<Unit>(
107
+ Callable {
108
+ action()
109
+ Unit
110
+ }
111
+ ),
112
+ Comparable<PrioritizedTask> {
113
+ override fun compareTo(other: PrioritizedTask): Int {
114
+ val priority = request.priority.compareTo(other.request.priority)
115
+ return if (priority != 0) priority else sequence.compareTo(other.sequence)
116
+ }
117
+ }
118
+ internal class BoundedPriorityQueue<E>(capacity: Int) : PriorityBlockingQueue<E>() {
119
+ private val permits = Semaphore(capacity)
120
+
121
+ override fun offer(element: E): Boolean {
122
+ if (!permits.tryAcquire()) return false
123
+ return try {
124
+ super.offer(element).also { added -> if (!added) permits.release() }
125
+ } catch (throwable: Throwable) {
126
+ permits.release()
127
+ throw throwable
128
+ }
129
+ }
130
+
131
+ override fun poll(): E? = super.poll()?.also { permits.release() }
132
+
133
+ override fun poll(timeout: Long, unit: TimeUnit): E? =
134
+ super.poll(timeout, unit)?.also { permits.release() }
135
+
136
+ override fun take(): E = super.take().also { permits.release() }
137
+
138
+ override fun remove(element: E?): Boolean =
139
+ super.remove(element).also { removed -> if (removed) permits.release() }
140
+
141
+ override fun clear() {
142
+ while (poll() != null) Unit
143
+ }
144
+
145
+ override fun drainTo(target: MutableCollection<in E>): Int =
146
+ super.drainTo(target).also(permits::release)
147
+
148
+ override fun drainTo(target: MutableCollection<in E>, maxElements: Int): Int =
149
+ super.drainTo(target, maxElements).also(permits::release)
150
+ }
151
+ private data class PolicyState(
152
+ var submittedCount: Int = 0,
153
+ val pending: java.util.ArrayDeque<PendingRequest> = java.util.ArrayDeque()
154
+ )
155
+
156
+ private val cache = object : LruCache<CacheKey, DecodedBitmapLease>(32 * 1024 * 1024) {
157
+ override fun sizeOf(key: CacheKey, value: DecodedBitmapLease): Int =
158
+ saturatingAdd(value.byteCount, key.digest.size.toLong() + CACHE_ENTRY_OVERHEAD_BYTES)
159
+ .coerceAtMost(Int.MAX_VALUE.toLong())
160
+ .toInt()
161
+
162
+ override fun entryRemoved(
163
+ evicted: Boolean,
164
+ key: CacheKey,
165
+ oldValue: DecodedBitmapLease,
166
+ newValue: DecodedBitmapLease?
167
+ ) {
168
+ if (oldValue !== newValue) oldValue.close()
169
+ }
170
+ }
171
+
172
+ /** Counts the allocation once even when cache and mounted leases share it. */
173
+ private fun decodedAllocationBytes(bitmap: Bitmap): Long {
174
+ val allocation = runCatching { bitmap.allocationByteCount.toLong() }.getOrNull()
175
+ if (allocation != null && allocation >= 0) return allocation
176
+ val pixels = bitmap.width.coerceAtLeast(0).toLong()
177
+ val rows = bitmap.height.coerceAtLeast(0).toLong()
178
+ return if (pixels == 0L || rows == 0L) {
179
+ 0L
180
+ } else if (pixels > Long.MAX_VALUE / rows ||
181
+ pixels * rows > Long.MAX_VALUE / 4L
182
+ ) {
183
+ Long.MAX_VALUE
184
+ } else {
185
+ pixels * rows * 4L
186
+ }
187
+ }
188
+
189
+ private fun saturatingAdd(left: Long, right: Long): Long =
190
+ if (right > 0 && left > Long.MAX_VALUE - right) Long.MAX_VALUE else left + right
191
+ private val lock = Any()
192
+ private val inFlight = mutableMapOf<RequestKey, PendingRequest>()
193
+ private val policyStates = mutableMapOf<ImageLoadingPolicy, PolicyState>()
194
+ private val readyToSubmit = java.util.ArrayDeque<PendingRequest>()
195
+ private val rejectionLock = Any()
196
+ private val rejectionNotifications = java.util.ArrayDeque<Callback>()
197
+ private var rejectionDrainPosted = false
198
+ private var admissionCount = 0
199
+ private val nextCallbackId = AtomicLong()
200
+ private val submissionRejectionCount = AtomicLong()
201
+ private val submissionSequence = AtomicLong()
202
+ private val digestConstructionCount = AtomicLong()
203
+ private val mainHandler by lazy { Handler(Looper.getMainLooper()) }
204
+ private var globalExecutor = createGlobalExecutor()
205
+ private val timeoutScheduler = Executors.newSingleThreadScheduledExecutor { runnable ->
206
+ Thread(runnable, "native-editor-image-deadline").apply { isDaemon = true }
207
+ }
208
+
209
+ init {
210
+ DecodedBitmapBudget.shared().setPressureHandler {
211
+ synchronized(cache) { cache.evictAll() }
212
+ }
213
+ }
214
+
215
+ @Volatile
216
+ internal var decodeSourceOverride: ((String, ImageLoadingPolicy) -> Bitmap?)? = null
217
+
218
+ @Volatile
219
+ internal var beforeWorkerReturnOverride: ((String) -> Unit)? = null
220
+
221
+ @Volatile
222
+ internal var deadlineExecutionGateOverride: (() -> Unit)? = null
223
+
224
+ @Volatile
225
+ internal var beforeCacheCommitOverride: (() -> Unit)? = null
226
+
227
+ @Volatile
228
+ internal var beforeTerminalClaimOverride: (() -> Unit)? = null
229
+
230
+ @Volatile
231
+ internal var decodedDeliveryPostedOverride: (() -> Unit)? = null
232
+
233
+ @Volatile
234
+ internal var monotonicClockOverride: MonotonicClock? = null
235
+
236
+ @Volatile
237
+ internal var beforeDigestOverride: (() -> Unit)? = null
238
+
239
+ internal fun isCachedForTesting(
240
+ source: String,
241
+ policy: ImageLoadingPolicy = ImageLoadingPolicy.DEFAULT
242
+ ): Boolean = prepare(source, policy)?.let {
243
+ synchronized(cache) { cache.get(cacheKey(it.source, it.policy)) != null }
244
+ } ?: false
245
+
246
+ internal fun prepare(
247
+ source: String,
248
+ policy: ImageLoadingPolicy = ImageLoadingPolicy.DEFAULT
249
+ ): PreparedSource? {
250
+ if (source.regionMatches(0, "data:image/", 0, "data:image/".length, ignoreCase = true) &&
251
+ RenderImageDecoder.preflightDataUrl(source, policy) == null
252
+ ) {
253
+ return null
254
+ }
255
+ return PreparedSource(source, policy)
256
+ }
257
+
258
+ internal fun cacheKeyByteCountForTesting(
259
+ source: String,
260
+ policy: ImageLoadingPolicy = ImageLoadingPolicy.DEFAULT
261
+ ): Int = cacheKey(source, policy).digest.size
262
+
263
+ internal fun cacheEntryCountForTesting(): Int = synchronized(cache) { cache.snapshot().size }
264
+
265
+ internal fun cacheRetainedCostForTesting(): Int = synchronized(cache) { cache.size() }
266
+
267
+ internal fun digestConstructionCountForTesting(): Long = digestConstructionCount.get()
268
+
269
+ internal fun resetForTesting() {
270
+ synchronized(cache) {
271
+ cache.evictAll()
272
+ }
273
+ val pending: List<PendingRequest>
274
+ val executor: ThreadPoolExecutor
275
+ synchronized(lock) {
276
+ pending = inFlight.values.toList()
277
+ inFlight.clear()
278
+ policyStates.clear()
279
+ readyToSubmit.clear()
280
+ admissionCount = 0
281
+ executor = globalExecutor
282
+ globalExecutor = createGlobalExecutor()
283
+ }
284
+ pending.forEach { request ->
285
+ runCatching { request.cancellation.cancel() }
286
+ runCatching { request.future?.cancel(true) }
287
+ runCatching { request.timeoutFuture?.cancel(false) }
288
+ }
289
+ executor.shutdownNow()
290
+ val rejected = synchronized(rejectionLock) {
291
+ rejectionNotifications.toList().also {
292
+ rejectionNotifications.clear()
293
+ rejectionDrainPosted = false
294
+ }
295
+ }
296
+ rejected.forEach { it.handle.finish() }
297
+ submissionRejectionCount.set(0)
298
+ digestConstructionCount.set(0)
299
+ decodeSourceOverride = null
300
+ beforeWorkerReturnOverride = null
301
+ deadlineExecutionGateOverride = null
302
+ beforeCacheCommitOverride = null
303
+ beforeTerminalClaimOverride = null
304
+ decodedDeliveryPostedOverride = null
305
+ monotonicClockOverride = null
306
+ beforeDigestOverride = null
307
+ }
308
+
309
+ internal fun executionResourceCountForTesting(): Int = 1
310
+
311
+ internal fun globalQueuedTaskCountForTesting(): Int = globalExecutor.queue.size
312
+
313
+ internal fun globalQueueLimitForTesting(): Int = GLOBAL_QUEUE_CAPACITY
314
+
315
+ internal fun globalActiveWorkerCountForTesting(): Int = globalExecutor.activeCount
316
+
317
+ internal fun globalWorkerLimitForTesting(): Int = GLOBAL_WORKERS
318
+
319
+ internal fun globalAdmissionCountForTesting(): Int = synchronized(lock) { admissionCount }
320
+
321
+ internal fun globalAdmissionLimitForTesting(): Int = GLOBAL_ADMISSION_LIMIT
322
+
323
+ internal fun rejectionNotificationCountForTesting(): Int =
324
+ synchronized(rejectionLock) { rejectionNotifications.size }
325
+
326
+ internal fun rejectionNotificationLimitForTesting(): Int = REJECTION_NOTIFICATION_LIMIT
327
+
328
+ internal fun submissionRejectionCountForTesting(): Long = submissionRejectionCount.get()
329
+
330
+ internal fun loadLease(
331
+ source: String,
332
+ policy: ImageLoadingPolicy = ImageLoadingPolicy.DEFAULT,
333
+ ownerId: Long,
334
+ priority: DecodedBitmapPriority,
335
+ onLoaded: (DecodedBitmapLease?) -> Unit
336
+ ): LoadHandle = loadInternal(
337
+ source,
338
+ policy,
339
+ null,
340
+ ownerId,
341
+ priority,
342
+ onLoaded
343
+ )
344
+
345
+ internal fun loadLease(
346
+ prepared: PreparedSource,
347
+ ownerId: Long,
348
+ priority: DecodedBitmapPriority,
349
+ onLoaded: (DecodedBitmapLease?) -> Unit
350
+ ): LoadHandle = loadInternal(
351
+ prepared.source,
352
+ prepared.policy,
353
+ prepared,
354
+ ownerId,
355
+ priority,
356
+ onLoaded
357
+ )
358
+
359
+ private fun loadInternal(
360
+ source: String,
361
+ policy: ImageLoadingPolicy,
362
+ prepared: PreparedSource?,
363
+ ownerId: Long?,
364
+ priority: DecodedBitmapPriority,
365
+ onLoaded: (DecodedBitmapLease?) -> Unit
366
+ ): LoadHandle {
367
+ val cancelled = AtomicBoolean(false)
368
+ var requestKey: RequestKey? = null
369
+ lateinit var callback: Callback
370
+ val handle = LoadHandle {
371
+ cancelled.set(true)
372
+ requestKey?.let { cancelCallback(it, callback) }
373
+ }
374
+ callback = Callback(
375
+ nextCallbackId.incrementAndGet(),
376
+ cancelled,
377
+ AtomicBoolean(false),
378
+ handle,
379
+ ownerId,
380
+ policy.maxDecodedBytes.toLong(),
381
+ priority,
382
+ onLoaded
383
+ )
384
+ val admitted = synchronized(lock) {
385
+ if (admissionCount >= GLOBAL_ADMISSION_LIMIT) {
386
+ false
387
+ } else {
388
+ admissionCount += 1
389
+ true
390
+ }
391
+ }
392
+ if (!admitted) {
393
+ enqueueRejectionNotification(callback)
394
+ return handle
395
+ }
396
+ handle.onFinished {
397
+ releaseAdmission(callback)
398
+ }
399
+ val requestedAtMs = monotonicNowMs()
400
+ val deadlineMs = deadlineAfter(requestedAtMs, policy.requestTimeoutMs)
401
+ val resolvedSource = prepared ?: prepare(source, policy)
402
+ if (resolvedSource == null) {
403
+ releaseAdmission(callback)
404
+ postCallbacks(listOf(callback), null)
405
+ return handle
406
+ }
407
+ if (monotonicNowMs() >= deadlineMs) {
408
+ releaseAdmission(callback)
409
+ postCallbacks(listOf(callback), null)
410
+ return handle
411
+ }
412
+ val resolvedRequestKey = RequestKey(
413
+ cacheKey(resolvedSource.source, resolvedSource.policy),
414
+ resolvedSource.policy
415
+ )
416
+ requestKey = resolvedRequestKey
417
+ if (monotonicNowMs() >= deadlineMs) {
418
+ releaseAdmission(callback)
419
+ postCallbacks(listOf(callback), null)
420
+ return handle
421
+ }
422
+ synchronized(cache) {
423
+ cache.get(resolvedRequestKey.digest)?.let { cached ->
424
+ leaseForCallback(cached, callback)
425
+ }
426
+ }?.let { lease ->
427
+ scheduleCachedDelivery(callback, lease, deadlineMs)
428
+ return handle
429
+ }
430
+ var drain = false
431
+ var reject = false
432
+ var createdRequest: PendingRequest? = null
433
+ synchronized(lock) {
434
+ val existing = inFlight[resolvedRequestKey]
435
+ if (existing != null) {
436
+ existing.callbacks += callback
437
+ if (
438
+ callback.priority == DecodedBitmapPriority.VISIBLE &&
439
+ existing.priority != DecodedBitmapPriority.VISIBLE
440
+ ) {
441
+ existing.priority = DecodedBitmapPriority.VISIBLE
442
+ promoteRequestLocked(existing)
443
+ }
444
+ } else {
445
+ val pending = PendingRequest(
446
+ key = resolvedRequestKey,
447
+ source = source,
448
+ callbacks = mutableListOf(callback),
449
+ cancellation = RenderImageDecoder.Cancellation(),
450
+ startedAtMs = requestedAtMs
451
+ )
452
+ val state = policyStates.getOrPut(policy) { PolicyState() }
453
+ when {
454
+ state.submittedCount < policy.maxConcurrentRequests -> {
455
+ createdRequest = pending
456
+ inFlight[resolvedRequestKey] = pending
457
+ state.submittedCount += 1
458
+ pending.submitted = true
459
+ enqueueReadyLocked(pending)
460
+ drain = true
461
+ }
462
+
463
+ state.pending.size < policy.maxPendingRequests -> {
464
+ createdRequest = pending
465
+ inFlight[resolvedRequestKey] = pending
466
+ if (pending.priority == DecodedBitmapPriority.VISIBLE) {
467
+ state.pending.addFirst(pending)
468
+ } else {
469
+ state.pending.addLast(pending)
470
+ }
471
+ }
472
+
473
+ else -> reject = true
474
+ }
475
+ }
476
+ }
477
+ createdRequest?.let(::scheduleDeadline)
478
+ if (reject) {
479
+ enqueueRejectionNotification(callback)
480
+ } else if (drain) {
481
+ drainSubmissions()
482
+ }
483
+ return handle
484
+ }
485
+
486
+ private fun promoteRequestLocked(request: PendingRequest) {
487
+ val state = policyStates[request.key.policy]
488
+ if (!request.submitted) {
489
+ if (state?.pending?.remove(request) == true) state.pending.addFirst(request)
490
+ return
491
+ }
492
+ if (readyToSubmit.remove(request)) {
493
+ readyToSubmit.addFirst(request)
494
+ return
495
+ }
496
+ val future = request.future ?: return
497
+ if (!request.started.get() && globalExecutor.remove(future as Runnable)) {
498
+ try {
499
+ globalExecutor.execute(future)
500
+ } catch (_: RejectedExecutionException) {
501
+ submissionRejectionCount.incrementAndGet()
502
+ request.future = null
503
+ enqueueReadyLocked(request)
504
+ mainHandler.post { drainSubmissions() }
505
+ }
506
+ }
507
+ }
508
+
509
+ private fun enqueueReadyLocked(request: PendingRequest) {
510
+ if (request.priority == DecodedBitmapPriority.VISIBLE) {
511
+ readyToSubmit.addFirst(request)
512
+ } else {
513
+ readyToSubmit.addLast(request)
514
+ }
515
+ }
516
+
517
+ private fun drainSubmissions() {
518
+ while (true) {
519
+ val request = synchronized(lock) {
520
+ readyToSubmit.pollFirst()?.also { it.dispatching = true }
521
+ } ?: return
522
+ if (!submitRequest(request)) {
523
+ synchronized(lock) {
524
+ request.dispatching = false
525
+ if (inFlight[request.key] === request) {
526
+ readyToSubmit.addFirst(request)
527
+ } else {
528
+ releaseRequestSlotLocked(request)
529
+ }
530
+ }
531
+ return
532
+ }
533
+ }
534
+ }
535
+
536
+ private fun submitRequest(request: PendingRequest): Boolean {
537
+ try {
538
+ val future = PrioritizedTask(
539
+ request,
540
+ submissionSequence.incrementAndGet()
541
+ ) {
542
+ request.started.set(true)
543
+ var bitmap: DecodedBitmapLease? = null
544
+ try {
545
+ bitmap = decode(request)
546
+ } catch (_: Exception) {
547
+ bitmap = null
548
+ } catch (_: OutOfMemoryError) {
549
+ bitmap = null
550
+ } finally {
551
+ completeDecodedRequest(request, bitmap)
552
+ beforeWorkerReturnOverride?.invoke(request.source)
553
+ }
554
+ }
555
+ globalExecutor.execute(future)
556
+ var orphaned = false
557
+ synchronized(lock) {
558
+ request.dispatching = false
559
+ request.future = future
560
+ orphaned = inFlight[request.key] !== request
561
+ }
562
+ if (orphaned) cancelOrphanedSubmission(request, future)
563
+ return true
564
+ } catch (_: RejectedExecutionException) {
565
+ submissionRejectionCount.incrementAndGet()
566
+ return false
567
+ }
568
+ }
569
+
570
+ private fun cancelOrphanedSubmission(request: PendingRequest, future: Future<*>) {
571
+ runCatching { request.cancellation.cancel() }
572
+ val removed = !request.started.get() &&
573
+ runCatching { globalExecutor.remove(future as Runnable) }.getOrDefault(false)
574
+ if (removed) {
575
+ runCatching { future.cancel(false) }
576
+ if (!request.terminal.get()) {
577
+ synchronized(lock) { releaseRequestSlotLocked(request) }
578
+ mainHandler.post { drainSubmissions() }
579
+ }
580
+ } else if (request.started.get()) {
581
+ runCatching { future.cancel(true) }
582
+ }
583
+ }
584
+
585
+ private fun completeDecodedRequest(request: PendingRequest, bitmap: DecodedBitmapLease?) {
586
+ synchronized(lock) {
587
+ releaseRequestSlotLocked(request)
588
+ }
589
+ mainHandler.post { drainSubmissions() }
590
+ if (request.terminal.get()) {
591
+ bitmap?.close()
592
+ return
593
+ }
594
+ if (bitmap == null || request.cancellation.isCancelled() ||
595
+ monotonicNowMs() >= request.deadlineMs
596
+ ) {
597
+ claimRequestOutcome(request, null, deliverInline = false)
598
+ return
599
+ }
600
+ decodedDeliveryPostedOverride?.invoke()
601
+ if (!mainHandler.post {
602
+ if (request.terminal.get()) return@post
603
+ beforeCacheCommitOverride?.invoke()
604
+ val deliverable = bitmap.takeIf {
605
+ !request.cancellation.isCancelled() &&
606
+ monotonicNowMs() < request.deadlineMs
607
+ }
608
+ beforeTerminalClaimOverride?.invoke()
609
+ claimRequestOutcome(request, deliverable, deliverInline = true)
610
+ }
611
+ ) {
612
+ claimRequestOutcome(request, null, deliverInline = false)
613
+ }
614
+ }
615
+
616
+ private fun claimRequestOutcome(
617
+ request: PendingRequest,
618
+ candidateBitmap: DecodedBitmapLease?,
619
+ deliverInline: Boolean
620
+ ): Boolean {
621
+ if (!request.terminal.compareAndSet(false, true)) {
622
+ candidateBitmap?.close()
623
+ return false
624
+ }
625
+ request.timeoutFuture?.cancel(false)
626
+ val callbacks: List<Callback>
627
+ synchronized(lock) {
628
+ if (inFlight[request.key] === request) inFlight.remove(request.key)
629
+ releaseRequestSlotLocked(request)
630
+ callbacks = request.callbacks.toList()
631
+ }
632
+ val resolvedBitmap = candidateBitmap?.takeIf {
633
+ !request.cancellation.isCancelled() &&
634
+ monotonicNowMs() < request.deadlineMs &&
635
+ callbacks.any { callback -> !callback.cancelled.get() }
636
+ }
637
+ if (resolvedBitmap == null) {
638
+ candidateBitmap?.close()
639
+ request.cancellation.cancel()
640
+ }
641
+ val deliveries = callbacks.map { callback ->
642
+ callback to resolvedBitmap?.let { leaseForCallback(it, callback) }
643
+ }
644
+ if (resolvedBitmap != null) {
645
+ synchronized(cache) { cache.put(request.key.digest, resolvedBitmap) }
646
+ }
647
+ callbacks.forEach(::releaseAdmission)
648
+ if (deliverInline) {
649
+ deliveries.forEach { (callback, lease) -> deliverCallback(callback, lease) }
650
+ drainSubmissions()
651
+ } else {
652
+ postDeliveries(deliveries)
653
+ }
654
+ return true
655
+ }
656
+
657
+ private fun scheduleDeadline(request: PendingRequest) {
658
+ val delayMs = (request.deadlineMs - monotonicNowMs()).coerceAtLeast(0L)
659
+ val future = timeoutScheduler.schedule(
660
+ {
661
+ deadlineExecutionGateOverride?.invoke()
662
+ expireRequest(request)
663
+ },
664
+ delayMs,
665
+ TimeUnit.MILLISECONDS
666
+ )
667
+ request.timeoutFuture = future
668
+ if (request.terminal.get()) future.cancel(false)
669
+ }
670
+
671
+ private fun scheduleCachedDelivery(
672
+ callback: Callback,
673
+ lease: DecodedBitmapLease,
674
+ deadlineMs: Long
675
+ ) {
676
+ val terminal = AtomicBoolean(false)
677
+ val delayMs = (deadlineMs - monotonicNowMs()).coerceAtLeast(0L)
678
+ val timeoutFuture = timeoutScheduler.schedule(
679
+ {
680
+ deadlineExecutionGateOverride?.invoke()
681
+ if (terminal.compareAndSet(false, true)) {
682
+ lease.close()
683
+ releaseAdmission(callback)
684
+ postCallbacks(listOf(callback), null)
685
+ }
686
+ },
687
+ delayMs,
688
+ TimeUnit.MILLISECONDS
689
+ )
690
+ if (!mainHandler.post {
691
+ if (!terminal.compareAndSet(false, true)) return@post
692
+ timeoutFuture.cancel(false)
693
+ releaseAdmission(callback)
694
+ val result = lease.takeIf {
695
+ !callback.cancelled.get() && monotonicNowMs() < deadlineMs
696
+ }
697
+ if (result == null) lease.close()
698
+ deliverCallback(callback, result)
699
+ }
700
+ ) {
701
+ timeoutFuture.cancel(false)
702
+ terminal.set(true)
703
+ lease.close()
704
+ callback.handle.finish()
705
+ }
706
+ }
707
+
708
+ private fun expireRequest(request: PendingRequest) {
709
+ if (!claimRequestOutcome(request, null, deliverInline = false)) return
710
+ val future = request.future
711
+ if (future != null) {
712
+ if (!request.started.get()) runCatching { globalExecutor.remove(future as Runnable) }
713
+ runCatching { future.cancel(true) }
714
+ }
715
+ }
716
+
717
+ private fun postCallbacks(callbacks: List<Callback>, bitmap: DecodedBitmapLease?) {
718
+ val deliveries = callbacks.map { callback ->
719
+ callback to bitmap?.let { leaseForCallback(it, callback) }
720
+ }
721
+ postDeliveries(deliveries)
722
+ }
723
+
724
+ private fun postDeliveries(deliveries: List<Pair<Callback, DecodedBitmapLease?>>) {
725
+ if (!mainHandler.post {
726
+ deliveries.forEach { (callback, lease) -> deliverCallback(callback, lease) }
727
+ drainSubmissions()
728
+ }
729
+ ) {
730
+ deliveries.forEach { (callback, lease) ->
731
+ lease?.close()
732
+ callback.handle.finish()
733
+ }
734
+ drainSubmissions()
735
+ }
736
+ }
737
+
738
+ private fun enqueueRejectionNotification(callback: Callback) {
739
+ var postDrain = false
740
+ val dropNotification = synchronized(rejectionLock) {
741
+ if (rejectionNotifications.size >= REJECTION_NOTIFICATION_LIMIT) {
742
+ true
743
+ } else {
744
+ rejectionNotifications.addLast(callback)
745
+ if (!rejectionDrainPosted) {
746
+ rejectionDrainPosted = true
747
+ postDrain = true
748
+ }
749
+ false
750
+ }
751
+ }
752
+ if (dropNotification) {
753
+ // Rejection delivery itself is bounded. Once the retained main-thread batch
754
+ // is full, shed the notification but always finish the handle. Calling the
755
+ // consumer inline would violate async/main-thread delivery and enable retry
756
+ // recursion under sustained overload.
757
+ callback.handle.finish()
758
+ } else if (postDrain && !mainHandler.post { drainRejectionNotifications() }) {
759
+ // A stopped looper cannot honor the delivery contract; finish without
760
+ // invoking consumer code on the caller thread.
761
+ takeRejectionNotifications().forEach { it.handle.finish() }
762
+ }
763
+ }
764
+
765
+ private fun drainRejectionNotifications() {
766
+ takeRejectionNotifications().forEach { deliverCallback(it, null) }
767
+ }
768
+
769
+ private fun takeRejectionNotifications(): List<Callback> = synchronized(rejectionLock) {
770
+ rejectionNotifications.toList().also {
771
+ rejectionNotifications.clear()
772
+ rejectionDrainPosted = false
773
+ }
774
+ }
775
+
776
+ private fun deliverCallback(callback: Callback, lease: DecodedBitmapLease?) {
777
+ try {
778
+ if (!callback.cancelled.get()) callback.deliver(lease) else lease?.close()
779
+ } catch (_: Exception) {
780
+ lease?.close()
781
+ // Consumer failures must not crash the delivery runnable or retain admission.
782
+ } finally {
783
+ callback.handle.finish()
784
+ }
785
+ }
786
+
787
+ private fun releaseAdmission(callback: Callback) {
788
+ if (!callback.admissionReleased.compareAndSet(false, true)) return
789
+ synchronized(lock) {
790
+ admissionCount = (admissionCount - 1).coerceAtLeast(0)
791
+ }
792
+ }
793
+
794
+ private fun cancelCallback(key: RequestKey, callback: Callback) {
795
+ callback.cancelled.set(true)
796
+ var requestToCancel: PendingRequest? = null
797
+ synchronized(lock) {
798
+ val request = inFlight[key] ?: return@synchronized
799
+ request.callbacks.removeAll { it.id == callback.id }
800
+ if (request.callbacks.isNotEmpty()) return@synchronized
801
+ if (!request.terminal.compareAndSet(false, true)) return@synchronized
802
+ inFlight.remove(key)
803
+ requestToCancel = request
804
+ releaseRequestSlotLocked(request)
805
+ }
806
+ val request = requestToCancel ?: return
807
+ runCatching { request.timeoutFuture?.cancel(false) }
808
+ runCatching { request.cancellation.cancel() }
809
+ val future = request.future
810
+ if (future != null) {
811
+ if (!request.started.get()) runCatching { globalExecutor.remove(future as Runnable) }
812
+ runCatching { future.cancel(true) }
813
+ }
814
+ drainSubmissions()
815
+ }
816
+
817
+ private fun releaseRequestSlotLocked(request: PendingRequest) {
818
+ if (!request.submitted) {
819
+ policyStates[request.key.policy]?.pending?.remove(request)
820
+ removePolicyStateIfEmptyLocked(request.key.policy)
821
+ return
822
+ }
823
+ if (!request.workerSlotReleased.compareAndSet(false, true)) return
824
+ readyToSubmit.remove(request)
825
+ releaseSubmittedSlotLocked(request.key.policy)
826
+ }
827
+
828
+ private fun releaseSubmittedSlotLocked(policy: ImageLoadingPolicy) {
829
+ val state = policyStates[policy] ?: return
830
+ state.submittedCount = (state.submittedCount - 1).coerceAtLeast(0)
831
+ val next = state.pending.pollFirst()
832
+ if (next != null) {
833
+ state.submittedCount += 1
834
+ next.submitted = true
835
+ enqueueReadyLocked(next)
836
+ }
837
+ removePolicyStateIfEmptyLocked(policy)
838
+ }
839
+
840
+ private fun removePolicyStateIfEmptyLocked(policy: ImageLoadingPolicy) {
841
+ val state = policyStates[policy] ?: return
842
+ if (state.submittedCount == 0 && state.pending.isEmpty()) policyStates.remove(policy)
843
+ }
844
+
845
+ private fun createGlobalExecutor() = object : ThreadPoolExecutor(
846
+ GLOBAL_WORKERS,
847
+ GLOBAL_WORKERS,
848
+ 30L,
849
+ TimeUnit.SECONDS,
850
+ BoundedPriorityQueue<Runnable>(GLOBAL_QUEUE_CAPACITY)
851
+ ) {
852
+ override fun afterExecute(runnable: Runnable?, throwable: Throwable?) {
853
+ super.afterExecute(runnable, throwable)
854
+ mainHandler.post { drainSubmissions() }
855
+ }
856
+ }.apply { allowCoreThreadTimeOut(true) }
857
+
858
+ private fun decode(request: PendingRequest): DecodedBitmapLease? {
859
+ decodeSourceOverride?.let { override ->
860
+ val bitmap = try {
861
+ override(request.source, request.key.policy)
862
+ } catch (_: OutOfMemoryError) {
863
+ null
864
+ } ?: return null
865
+ val bytes = decodedAllocationBytes(bitmap)
866
+ val reservation = DecodedBitmapBudget.shared().reserve(
867
+ bytes,
868
+ request.priority
869
+ ) ?: return null
870
+ return reservation.commit(bitmap, bytes)
871
+ }
872
+ return RenderImageDecoder.decodeSourceLease(
873
+ request.source,
874
+ request.key.policy,
875
+ request.cancellation,
876
+ monotonicClockOverride ?: systemMonotonicClock,
877
+ request.deadlineMs,
878
+ request.priority
879
+ )
880
+ }
881
+
882
+ private fun leaseForCallback(
883
+ lease: DecodedBitmapLease,
884
+ callback: Callback
885
+ ): DecodedBitmapLease? = callback.ownerId?.let { ownerId ->
886
+ lease.fork(ownerId, callback.ownerLimitBytes, callback.priority)
887
+ } ?: lease.forkUnowned()
888
+
889
+ private fun monotonicNowMs(): Long =
890
+ (monotonicClockOverride ?: systemMonotonicClock).elapsedRealtime()
891
+
892
+ private fun cacheKey(source: String, policy: ImageLoadingPolicy): CacheKey {
893
+ digestConstructionCount.incrementAndGet()
894
+ beforeDigestOverride?.invoke()
895
+ val digest = MessageDigest.getInstance("SHA-256")
896
+ digest.update(source.toByteArray(Charsets.UTF_8))
897
+ digest.update(ImageLoadingPolicy.canonicalBytes(policy))
898
+ return CacheKey(digest.digest())
899
+ }
900
+ }