@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.
- package/LICENSE +160 -0
- package/README.md +259 -0
- package/RUST-STANDARD-LIBRARY-NOTICES.html +8266 -0
- package/ReactNativeProseEditor.podspec +59 -0
- package/THIRD_PARTY_NOTICES.md +31155 -0
- package/android/build.gradle +188 -0
- package/android/consumer-rules.pro +18 -0
- package/android/expo/build.gradle +17 -0
- package/android/src/debug/java/com/apollohg/editor/viewer/PreparedProseDrawInstrumentation.kt +7 -0
- package/android/src/main/assets/editor-icons/MaterialIcons.json +2236 -0
- package/android/src/main/assets/editor-icons/MaterialIcons.ttf +0 -0
- package/android/src/main/java/com/apollohg/editor/AndroidApiCompat.kt +19 -0
- package/android/src/main/java/com/apollohg/editor/AndroidCollaborationTransport.kt +860 -0
- package/android/src/main/java/com/apollohg/editor/CaretGeometry.kt +91 -0
- package/android/src/main/java/com/apollohg/editor/CodeHighlighting.kt +141 -0
- package/android/src/main/java/com/apollohg/editor/DecodedBitmapBudget.kt +250 -0
- package/android/src/main/java/com/apollohg/editor/EditorAddons.kt +133 -0
- package/android/src/main/java/com/apollohg/editor/EditorBlockSpacing.kt +51 -0
- package/android/src/main/java/com/apollohg/editor/EditorBoxDrawable.kt +16 -0
- package/android/src/main/java/com/apollohg/editor/EditorBoxDrawing.kt +234 -0
- package/android/src/main/java/com/apollohg/editor/EditorCheckboxSpan.kt +87 -0
- package/android/src/main/java/com/apollohg/editor/EditorCodeHighlighting.kt +120 -0
- package/android/src/main/java/com/apollohg/editor/EditorDecorationLayer.java +18 -0
- package/android/src/main/java/com/apollohg/editor/EditorDocumentLayout.kt +626 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +1042 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextAnnotations.kt +191 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextCaret.kt +100 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextClipboard.kt +101 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextComposition.kt +220 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextConfiguration.kt +316 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextCorrection.kt +303 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextDeferredUpdates.kt +327 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextDeletion.kt +258 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextDiagnostics.kt +83 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextDragDrop.kt +67 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextExternalComposition.kt +357 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextHardwareInput.kt +277 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextImages.kt +280 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextInputLifecycle.kt +183 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextInputPreparation.kt +79 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextInputTraits.kt +384 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextLayout.kt +100 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextNativeMutation.kt +243 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextPlaceholder.kt +154 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextRenderPatch.kt +347 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextRendering.kt +313 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextRustCommands.kt +281 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextSelection.kt +352 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextState.kt +113 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextTaskMarkers.kt +92 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextTextCommit.kt +327 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextToolbar.kt +84 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextUpdates.kt +245 -0
- package/android/src/main/java/com/apollohg/editor/EditorGlyphHeightCursorDrawable.kt +42 -0
- package/android/src/main/java/com/apollohg/editor/EditorHeightBehavior.kt +13 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputConnection.kt +923 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputCoordinates.kt +191 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputCorrection.kt +107 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputCursorGeometry.kt +128 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputSnapshots.kt +134 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputSurroundingDeletion.kt +321 -0
- package/android/src/main/java/com/apollohg/editor/EditorKeyboardToolbarView.kt +851 -0
- package/android/src/main/java/com/apollohg/editor/EditorMentionStyle.kt +192 -0
- package/android/src/main/java/com/apollohg/editor/EditorReconciliationWatcher.kt +46 -0
- package/android/src/main/java/com/apollohg/editor/EditorStyleSheet.kt +303 -0
- package/android/src/main/java/com/apollohg/editor/EditorStyleSheetSpans.kt +253 -0
- package/android/src/main/java/com/apollohg/editor/EditorSurfaceInputConnection.kt +67 -0
- package/android/src/main/java/com/apollohg/editor/EditorTextDecorationDrawing.kt +143 -0
- package/android/src/main/java/com/apollohg/editor/EditorTextSurface.kt +712 -0
- package/android/src/main/java/com/apollohg/editor/EditorTextSurfaceDragDrop.kt +94 -0
- package/android/src/main/java/com/apollohg/editor/EditorTextSurfaceInteraction.kt +708 -0
- package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +650 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2Adapter.kt +912 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2AdapterMutations.kt +402 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2AdapterRendering.kt +123 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2AdapterSelection.kt +203 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2Backend.kt +120 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2Driver.kt +87 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2Registry.kt +97 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2RenderValidation.kt +409 -0
- package/android/src/main/java/com/apollohg/editor/ExpoAutoGrowStyleSizePublisher.kt +34 -0
- package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +266 -0
- package/android/src/main/java/com/apollohg/editor/ImeTextCoordinateMapper.kt +102 -0
- package/android/src/main/java/com/apollohg/editor/KeyboardToolbarImeAnimationController.kt +101 -0
- package/android/src/main/java/com/apollohg/editor/MentionSuggestionChipView.kt +234 -0
- package/android/src/main/java/com/apollohg/editor/NativeCollaborationTransportRegistry.kt +493 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +813 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewAtoms.kt +68 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewBinding.kt +126 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewDiagnostics.kt +208 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewErrors.kt +146 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewEvents.kt +150 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewExternalComposition.kt +39 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewFocus.kt +176 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewLifecycle.kt +302 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewMentions.kt +259 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewNativeActions.kt +179 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewOutsideTap.kt +214 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewPendingUpdates.kt +376 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewProperties.kt +234 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewPropertyRetries.kt +197 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewToolbar.kt +285 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewTypes.kt +28 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewUpdates.kt +271 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +687 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorOutsideTapDispatcher.kt +380 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorV2Numbers.kt +45 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorViewRegistry.kt +372 -0
- package/android/src/main/java/com/apollohg/editor/NativeImagePipeline.kt +22 -0
- package/android/src/main/java/com/apollohg/editor/NativeToolbar.kt +193 -0
- package/android/src/main/java/com/apollohg/editor/NativeToolbarIcons.kt +177 -0
- package/android/src/main/java/com/apollohg/editor/NativeToolbarItem.kt +274 -0
- package/android/src/main/java/com/apollohg/editor/OkHttpCollaborationSocket.kt +102 -0
- package/android/src/main/java/com/apollohg/editor/OrderedListMarkerFormatter.kt +130 -0
- package/android/src/main/java/com/apollohg/editor/PointerTransparentView.java +18 -0
- package/android/src/main/java/com/apollohg/editor/PositionBridge.kt +128 -0
- package/android/src/main/java/com/apollohg/editor/ProseViewerView.kt +854 -0
- package/android/src/main/java/com/apollohg/editor/RemoteSelectionOverlayView.kt +314 -0
- package/android/src/main/java/com/apollohg/editor/RenderBlockSpans.kt +314 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +224 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridgeBlockStyles.kt +407 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridgeElements.kt +544 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridgeInlineAndAtoms.kt +631 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridgeParagraphs.kt +215 -0
- package/android/src/main/java/com/apollohg/editor/RenderImageDecoder.kt +748 -0
- package/android/src/main/java/com/apollohg/editor/RenderImageLoader.kt +900 -0
- package/android/src/main/java/com/apollohg/editor/RenderLayoutConstants.kt +117 -0
- package/android/src/main/java/com/apollohg/editor/RenderMediaSpans.kt +337 -0
- package/android/src/main/java/com/apollohg/editor/RenderParagraphSpans.kt +182 -0
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +951 -0
- package/android/src/main/java/com/apollohg/editor/SharedNativeImagePipeline.kt +78 -0
- package/android/src/main/java/com/apollohg/editor/UniffiEditorV2Backend.kt +261 -0
- package/android/src/main/java/com/apollohg/editor/viewer/AndroidProseLayoutEngine.kt +1656 -0
- package/android/src/main/java/com/apollohg/editor/viewer/FabricAttachmentSidecars.kt +80 -0
- package/android/src/main/java/com/apollohg/editor/viewer/FabricLeaseHandleBridge.kt +59 -0
- package/android/src/main/java/com/apollohg/editor/viewer/FallbackSelectionGeometry.kt +450 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseDrawingView.kt +678 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseInstrumentation.kt +608 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayout.kt +186 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayoutCache.kt +393 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayoutRegistry.kt +828 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseTheme.kt +316 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseViewerManager.kt +900 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseViewerPackage.kt +16 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedViewerAtoms.kt +89 -0
- package/android/src/main/java/com/apollohg/editor/viewer/ViewerCodeHighlighting.kt +91 -0
- package/android/src/main/java/com/apollohg/editor/viewer/ViewerDocument.kt +453 -0
- package/android/src/main/java/com/apollohg/editor/viewer/ViewerFontEnvironment.kt +282 -0
- package/android/src/main/java/com/apollohg/editor/viewer/ViewerImagePipeline.kt +460 -0
- package/android/src/main/jni/CMakeLists.txt +85 -0
- package/android/src/main/jni/PreparedProseViewerMeasurementsManager.cpp +334 -0
- package/android/src/main/jni/ReactNativeProseEditorSpec.h +17 -0
- package/android/src/main/jni/reactnativeproseeditor.h +6 -0
- package/android/src/main/res/values/attrs.xml +6 -0
- package/android/src/release/java/com/apollohg/editor/viewer/PreparedProseDrawInstrumentation.kt +9 -0
- package/app.plugin.js +62 -0
- package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseMeasurementsManager.h +62 -0
- package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerComponentDescriptor.h +30 -0
- package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerShadowNode.cpp +192 -0
- package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerShadowNode.h +46 -0
- package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerState.h +145 -0
- package/dist/AtomHost.d.ts +17 -0
- package/dist/AtomHost.js +135 -0
- package/dist/DefaultAtomChip.d.ts +2 -0
- package/dist/DefaultAtomChip.js +22 -0
- package/dist/EditorAddon.d.ts +32 -0
- package/dist/EditorAddon.js +29 -0
- package/dist/EditorAddonNormalization.d.ts +6 -0
- package/dist/EditorAddonNormalization.js +48 -0
- package/dist/EditorMentionThemeNormalization.d.ts +8 -0
- package/dist/EditorMentionThemeNormalization.js +22 -0
- package/dist/EditorMentionThemeValidation.d.ts +12 -0
- package/dist/EditorMentionThemeValidation.js +95 -0
- package/dist/EditorStyleSheet.d.ts +4 -0
- package/dist/EditorStyleSheet.js +10 -0
- package/dist/EditorStyleSheetNormalization.d.ts +3 -0
- package/dist/EditorStyleSheetNormalization.js +226 -0
- package/dist/EditorStyleSheetTypes.d.ts +151 -0
- package/dist/EditorStyleSheetTypes.js +2 -0
- package/dist/EditorTheme.d.ts +117 -0
- package/dist/EditorTheme.js +41 -0
- package/dist/EditorToolbar.d.ts +4 -0
- package/dist/EditorToolbar.js +17 -0
- package/dist/EditorToolbarComponent.d.ts +13 -0
- package/dist/EditorToolbarComponent.js +24 -0
- package/dist/EditorToolbarItems.d.ts +9 -0
- package/dist/EditorToolbarItems.js +58 -0
- package/dist/EditorToolbarRegistry.d.ts +59 -0
- package/dist/EditorToolbarRegistry.js +175 -0
- package/dist/EditorToolbarTypes.d.ts +273 -0
- package/dist/EditorToolbarTypes.js +2 -0
- package/dist/EditorToolbarVisuals.d.ts +154 -0
- package/dist/EditorToolbarVisuals.js +228 -0
- package/dist/EditorUpdateRevision.d.ts +8 -0
- package/dist/EditorUpdateRevision.js +22 -0
- package/dist/ExternalTextComposition.d.ts +45 -0
- package/dist/ExternalTextComposition.js +537 -0
- package/dist/ImageLoadingPolicy.d.ts +54 -0
- package/dist/ImageLoadingPolicy.js +60 -0
- package/dist/NativeEditorBoundaryError.d.ts +111 -0
- package/dist/NativeEditorBoundaryError.js +268 -0
- package/dist/NativeEditorBridge.d.ts +9 -0
- package/dist/NativeEditorBridge.js +37 -0
- package/dist/NativeEditorCollaborationTransport.d.ts +154 -0
- package/dist/NativeEditorCollaborationTransport.js +275 -0
- package/dist/NativeEditorCreateConfig.d.ts +16 -0
- package/dist/NativeEditorCreateConfig.js +249 -0
- package/dist/NativeEditorCreateJson.d.ts +76 -0
- package/dist/NativeEditorCreateJson.js +474 -0
- package/dist/NativeEditorCreateValidation.d.ts +32 -0
- package/dist/NativeEditorCreateValidation.js +150 -0
- package/dist/NativeEditorDocumentBridge.d.ts +72 -0
- package/dist/NativeEditorDocumentBridge.js +305 -0
- package/dist/NativeEditorDocumentHandle.d.ts +70 -0
- package/dist/NativeEditorDocumentHandle.js +89 -0
- package/dist/NativeEditorLocalAwareness.d.ts +34 -0
- package/dist/NativeEditorLocalAwareness.js +153 -0
- package/dist/NativeEditorNativeModule.d.ts +48 -0
- package/dist/NativeEditorNativeModule.js +34 -0
- package/dist/NativeEditorRenderNormalization.d.ts +22 -0
- package/dist/NativeEditorRenderNormalization.js +274 -0
- package/dist/NativeEditorResultNormalization.d.ts +108 -0
- package/dist/NativeEditorResultNormalization.js +400 -0
- package/dist/NativeEditorTypes.d.ts +318 -0
- package/dist/NativeEditorTypes.js +15 -0
- package/dist/NativeEditorV2Decimal.d.ts +5 -0
- package/dist/NativeEditorV2Decimal.js +18 -0
- package/dist/NativeProseViewer.d.ts +122 -0
- package/dist/NativeProseViewer.js +130 -0
- package/dist/NativeRichTextEditor.d.ts +3 -0
- package/dist/NativeRichTextEditor.js +6 -0
- package/dist/ResourceLimits.d.ts +100 -0
- package/dist/ResourceLimits.js +80 -0
- package/dist/RichTextEditor.d.ts +4 -0
- package/dist/RichTextEditor.js +25 -0
- package/dist/RichTextEditorNativeTypes.d.ts +122 -0
- package/dist/RichTextEditorNativeTypes.js +2 -0
- package/dist/RichTextEditorNativeView.d.ts +12 -0
- package/dist/RichTextEditorNativeView.js +15 -0
- package/dist/RichTextEditorSerialization.d.ts +53 -0
- package/dist/RichTextEditorSerialization.js +283 -0
- package/dist/RichTextEditorTypes.d.ts +281 -0
- package/dist/RichTextEditorTypes.js +2 -0
- package/dist/ViewerConfiguration.d.ts +22 -0
- package/dist/ViewerConfiguration.js +6 -0
- package/dist/YjsCollaboration.d.ts +146 -0
- package/dist/YjsCollaboration.js +496 -0
- package/dist/addons.d.ts +193 -0
- package/dist/addons.js +122 -0
- package/dist/atomConstants.d.ts +3 -0
- package/dist/atomConstants.js +6 -0
- package/dist/atomInstances.d.ts +35 -0
- package/dist/atomInstances.js +79 -0
- package/dist/atomPolicy.d.ts +4 -0
- package/dist/atomPolicy.js +47 -0
- package/dist/atomUpdates.d.ts +2 -0
- package/dist/atomUpdates.js +38 -0
- package/dist/atoms.d.ts +92 -0
- package/dist/atoms.js +222 -0
- package/dist/attributeValidation.d.ts +4 -0
- package/dist/attributeValidation.js +97 -0
- package/dist/contentExpression.d.ts +6 -0
- package/dist/contentExpression.js +335 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +78 -0
- package/dist/listMarkers.d.ts +15 -0
- package/dist/listMarkers.js +21 -0
- package/dist/schemaDefinition.d.ts +143 -0
- package/dist/schemaDefinition.js +206 -0
- package/dist/schemaDocument.d.ts +17 -0
- package/dist/schemaDocument.js +69 -0
- package/dist/schemaDocumentConstruction.d.ts +5 -0
- package/dist/schemaDocumentConstruction.js +125 -0
- package/dist/schemaNormalization.d.ts +29 -0
- package/dist/schemaNormalization.js +313 -0
- package/dist/schemaPresets.d.ts +68 -0
- package/dist/schemaPresets.js +198 -0
- package/dist/schemaResolution.d.ts +9 -0
- package/dist/schemaResolution.js +211 -0
- package/dist/schemas.d.ts +5 -0
- package/dist/schemas.js +28 -0
- package/dist/specs/PreparedProseViewerNativeComponent.d.ts +37 -0
- package/dist/specs/PreparedProseViewerNativeComponent.js +6 -0
- package/dist/useEditorToolbarInteractions.d.ts +12 -0
- package/dist/useEditorToolbarInteractions.js +218 -0
- package/dist/useEditorToolbarItems.d.ts +8 -0
- package/dist/useEditorToolbarItems.js +280 -0
- package/dist/useEditorToolbarPresentation.d.ts +4 -0
- package/dist/useEditorToolbarPresentation.js +203 -0
- package/dist/useEditorToolbarState.d.ts +63 -0
- package/dist/useEditorToolbarState.js +108 -0
- package/dist/useFocusPreservingFrames.d.ts +17 -0
- package/dist/useFocusPreservingFrames.js +108 -0
- package/dist/useNativeEditor.d.ts +66 -0
- package/dist/useNativeEditor.js +370 -0
- package/dist/useRichTextEditorCommands.d.ts +21 -0
- package/dist/useRichTextEditorCommands.js +299 -0
- package/dist/useRichTextEditorEvents.d.ts +18 -0
- package/dist/useRichTextEditorEvents.js +216 -0
- package/dist/useRichTextEditorMentions.d.ts +11 -0
- package/dist/useRichTextEditorMentions.js +316 -0
- package/dist/useRichTextEditorPresentation.d.ts +5 -0
- package/dist/useRichTextEditorPresentation.js +178 -0
- package/dist/useRichTextEditorState.d.ts +180 -0
- package/dist/useRichTextEditorState.js +405 -0
- package/dist/useRichTextEditorUpdates.d.ts +10 -0
- package/dist/useRichTextEditorUpdates.js +308 -0
- package/dist/useViewerAtoms.d.ts +38 -0
- package/dist/useViewerAtoms.js +288 -0
- package/expo-module.config.json +13 -0
- package/ios/EditorAddons.swift +354 -0
- package/ios/EditorCodeHighlighting.swift +184 -0
- package/ios/EditorCore.xcframework/Info.plist +44 -0
- package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
- package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
- package/ios/EditorInteractionOverlays.swift +697 -0
- package/ios/EditorLayoutManager.swift +971 -0
- package/ios/EditorNodeTypes.swift +37 -0
- package/ios/EditorStyleSheet.swift +461 -0
- package/ios/EditorTextView+AttributedPatch.swift +305 -0
- package/ios/EditorTextView+Binding.swift +61 -0
- package/ios/EditorTextView+Caret.swift +333 -0
- package/ios/EditorTextView+Commands.swift +203 -0
- package/ios/EditorTextView+Delegate.swift +65 -0
- package/ios/EditorTextView+Diagnostics.swift +225 -0
- package/ios/EditorTextView+DragDrop.swift +290 -0
- package/ios/EditorTextView+ExternalComposition.swift +364 -0
- package/ios/EditorTextView+Images.swift +354 -0
- package/ios/EditorTextView+InputTraits.swift +200 -0
- package/ios/EditorTextView+KeyboardInput.swift +318 -0
- package/ios/EditorTextView+Layout.swift +245 -0
- package/ios/EditorTextView+MarkedText.swift +267 -0
- package/ios/EditorTextView+NativeInputLifecycle.swift +372 -0
- package/ios/EditorTextView+NativeMutation.swift +471 -0
- package/ios/EditorTextView+Reconciliation.swift +97 -0
- package/ios/EditorTextView+RenderPatch.swift +573 -0
- package/ios/EditorTextView+Rendering.swift +539 -0
- package/ios/EditorTextView+Selection.swift +542 -0
- package/ios/EditorTextView.swift +953 -0
- package/ios/EditorTheme.swift +626 -0
- package/ios/EditorV2Adapter+Commands.swift +204 -0
- package/ios/EditorV2Adapter+Envelope.swift +238 -0
- package/ios/EditorV2Adapter+Mutation.swift +392 -0
- package/ios/EditorV2Adapter+Registry.swift +185 -0
- package/ios/EditorV2Adapter+RenderState.swift +360 -0
- package/ios/EditorV2Adapter+RenderValidation.swift +420 -0
- package/ios/EditorV2Adapter+Selection.swift +329 -0
- package/ios/EditorV2Adapter+TextCommands.swift +430 -0
- package/ios/EditorV2Adapter.swift +376 -0
- package/ios/EditorV2Shadow.swift +353 -0
- package/ios/Generated_editor_core.swift +2378 -0
- package/ios/NativeCodeHighlighting.swift +159 -0
- package/ios/NativeCollaborationSocket.swift +144 -0
- package/ios/NativeCollaborationTransport+Protocol.swift +258 -0
- package/ios/NativeCollaborationTransport+Socket.swift +379 -0
- package/ios/NativeCollaborationTransport+Validation.swift +103 -0
- package/ios/NativeCollaborationTransport.swift +322 -0
- package/ios/NativeCollaborationTransportRegistry.swift +521 -0
- package/ios/NativeEditorExpoView+AccessoryRetry.swift +219 -0
- package/ios/NativeEditorExpoView+Binding.swift +268 -0
- package/ios/NativeEditorExpoView+Diagnostics.swift +66 -0
- package/ios/NativeEditorExpoView+Events.swift +131 -0
- package/ios/NativeEditorExpoView+Focus.swift +169 -0
- package/ios/NativeEditorExpoView+MentionRetry.swift +288 -0
- package/ios/NativeEditorExpoView+Mentions.swift +288 -0
- package/ios/NativeEditorExpoView+Properties.swift +300 -0
- package/ios/NativeEditorExpoView+Toolbar.swift +86 -0
- package/ios/NativeEditorExpoView+Updates.swift +312 -0
- package/ios/NativeEditorExpoView.swift +349 -0
- package/ios/NativeEditorModule.swift +856 -0
- package/ios/NativeEditorViewRegistry.swift +226 -0
- package/ios/NativeToolbar+ButtonStyle.swift +214 -0
- package/ios/NativeToolbar+Buttons.swift +228 -0
- package/ios/NativeToolbar+Diagnostics.swift +114 -0
- package/ios/NativeToolbar+Menu.swift +101 -0
- package/ios/NativeToolbar+Models.swift +519 -0
- package/ios/NativeToolbar+Placeholder.swift +39 -0
- package/ios/NativeToolbar.swift +612 -0
- package/ios/OrderedListMarkerFormatter.swift +103 -0
- package/ios/PositionBridge.swift +596 -0
- package/ios/ProseViewerView.swift +462 -0
- package/ios/RenderBridge+Atoms.swift +288 -0
- package/ios/RenderBridge+Attachments.swift +246 -0
- package/ios/RenderBridge+BlockBoundaries.swift +313 -0
- package/ios/RenderBridge+Style.swift +343 -0
- package/ios/RenderBridge.swift +704 -0
- package/ios/RichTextEditorView.swift +930 -0
- package/ios/SharedNativeImagePipeline+Cache.swift +189 -0
- package/ios/SharedNativeImagePipeline+Transport.swift +328 -0
- package/ios/SharedNativeImagePipeline.swift +533 -0
- package/ios/Viewer/CoreTextProseLayoutEngine+AttributedText.swift +436 -0
- package/ios/Viewer/CoreTextProseLayoutEngine+Models.swift +322 -0
- package/ios/Viewer/CoreTextProseLayoutEngine.swift +635 -0
- package/ios/Viewer/Fabric/PREPPreparedProseViewerComponentView.h +4 -0
- package/ios/Viewer/Fabric/PREPPreparedProseViewerComponentView.mm +767 -0
- package/ios/Viewer/Fabric/PreparedProseMeasurementsManager.mm +118 -0
- package/ios/Viewer/NativeSVGImageDecoder.h +9 -0
- package/ios/Viewer/NativeSVGImageDecoder.mm +106 -0
- package/ios/Viewer/NativeSVGPreflight.h +11 -0
- package/ios/Viewer/NativeSVGPreflight.mm +219 -0
- package/ios/Viewer/PreparedProseDrawingView.swift +639 -0
- package/ios/Viewer/PreparedProseInstrumentation.swift +387 -0
- package/ios/Viewer/PreparedProseLayout.swift +326 -0
- package/ios/Viewer/PreparedProseLayoutCache.swift +766 -0
- package/ios/Viewer/PreparedProseLayoutRegistry+Artifacts.swift +377 -0
- package/ios/Viewer/PreparedProseLayoutRegistry+Fabric.swift +582 -0
- package/ios/Viewer/PreparedProseLayoutRegistry+FabricOwnership.swift +128 -0
- package/ios/Viewer/PreparedProseLayoutRegistry.swift +381 -0
- package/ios/Viewer/ViewerDocument.swift +575 -0
- package/ios/Viewer/ViewerFontEnvironment.swift +283 -0
- package/ios/Viewer/ViewerImagePipeline.swift +478 -0
- package/ios/editor_coreFFI/editor_coreFFI.h +1023 -0
- package/ios/editor_coreFFI/module.modulemap +7 -0
- package/package.json +151 -0
- package/react-native.config.js +17 -0
- package/rust/android/arm64-v8a/libeditor_core.so +0 -0
- package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
- package/rust/android/x86/libeditor_core.so +0 -0
- package/rust/android/x86_64/libeditor_core.so +0 -0
- package/rust/bindings/kotlin/uniffi/editor_core/editor_core.kt +3146 -0
- package/src/AtomHost.tsx +180 -0
- package/src/DefaultAtomChip.tsx +27 -0
- package/src/EditorAddon.ts +65 -0
- package/src/EditorAddonNormalization.ts +49 -0
- package/src/EditorMentionThemeNormalization.ts +25 -0
- package/src/EditorMentionThemeValidation.ts +133 -0
- package/src/EditorStyleSheet.ts +9 -0
- package/src/EditorStyleSheetNormalization.ts +219 -0
- package/src/EditorStyleSheetTypes.ts +189 -0
- package/src/EditorTheme.ts +196 -0
- package/src/EditorToolbar.tsx +31 -0
- package/src/EditorToolbarComponent.tsx +23 -0
- package/src/EditorToolbarItems.tsx +64 -0
- package/src/EditorToolbarRegistry.tsx +254 -0
- package/src/EditorToolbarTypes.tsx +322 -0
- package/src/EditorToolbarVisuals.tsx +270 -0
- package/src/EditorUpdateRevision.ts +26 -0
- package/src/ExternalTextComposition.ts +658 -0
- package/src/ImageLoadingPolicy.ts +125 -0
- package/src/NativeEditorBoundaryError.ts +345 -0
- package/src/NativeEditorBridge.ts +93 -0
- package/src/NativeEditorCollaborationTransport.ts +493 -0
- package/src/NativeEditorCreateConfig.ts +326 -0
- package/src/NativeEditorCreateJson.ts +579 -0
- package/src/NativeEditorCreateValidation.ts +178 -0
- package/src/NativeEditorDocumentBridge.ts +485 -0
- package/src/NativeEditorDocumentHandle.ts +168 -0
- package/src/NativeEditorLocalAwareness.ts +168 -0
- package/src/NativeEditorNativeModule.ts +97 -0
- package/src/NativeEditorRenderNormalization.ts +320 -0
- package/src/NativeEditorResultNormalization.ts +469 -0
- package/src/NativeEditorTypes.ts +366 -0
- package/src/NativeEditorV2Decimal.ts +13 -0
- package/src/NativeProseViewer.tsx +356 -0
- package/src/NativeRichTextEditor.tsx +33 -0
- package/src/ResourceLimits.ts +187 -0
- package/src/RichTextEditor.tsx +27 -0
- package/src/RichTextEditorNativeTypes.tsx +144 -0
- package/src/RichTextEditorNativeView.tsx +21 -0
- package/src/RichTextEditorSerialization.tsx +381 -0
- package/src/RichTextEditorTypes.tsx +327 -0
- package/src/ViewerConfiguration.ts +23 -0
- package/src/YjsCollaboration.ts +692 -0
- package/src/addons.ts +311 -0
- package/src/atomConstants.ts +3 -0
- package/src/atomInstances.ts +123 -0
- package/src/atomPolicy.ts +47 -0
- package/src/atomUpdates.ts +52 -0
- package/src/atoms.ts +379 -0
- package/src/attributeValidation.ts +116 -0
- package/src/contentExpression.ts +338 -0
- package/src/index.ts +295 -0
- package/src/listMarkers.ts +18 -0
- package/src/schemaDefinition.ts +395 -0
- package/src/schemaDocument.ts +92 -0
- package/src/schemaDocumentConstruction.ts +148 -0
- package/src/schemaNormalization.ts +367 -0
- package/src/schemaPresets.ts +250 -0
- package/src/schemaResolution.ts +242 -0
- package/src/schemas.ts +43 -0
- package/src/specs/PreparedProseViewerNativeComponent.ts +40 -0
- package/src/useEditorToolbarInteractions.tsx +329 -0
- package/src/useEditorToolbarItems.tsx +390 -0
- package/src/useEditorToolbarPresentation.tsx +483 -0
- package/src/useEditorToolbarState.tsx +172 -0
- package/src/useFocusPreservingFrames.ts +171 -0
- package/src/useNativeEditor.ts +534 -0
- package/src/useRichTextEditorCommands.tsx +478 -0
- package/src/useRichTextEditorEvents.tsx +342 -0
- package/src/useRichTextEditorMentions.tsx +441 -0
- package/src/useRichTextEditorPresentation.tsx +563 -0
- package/src/useRichTextEditorState.tsx +620 -0
- package/src/useRichTextEditorUpdates.tsx +403 -0
- package/src/useViewerAtoms.tsx +427 -0
|
@@ -0,0 +1,854 @@
|
|
|
1
|
+
package com.apollohg.editor
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.res.Configuration
|
|
5
|
+
import android.graphics.Rect
|
|
6
|
+
import android.graphics.Typeface
|
|
7
|
+
import android.os.Bundle
|
|
8
|
+
import android.util.AttributeSet
|
|
9
|
+
import android.view.View
|
|
10
|
+
import android.view.ViewGroup
|
|
11
|
+
import android.view.ViewTreeObserver
|
|
12
|
+
import android.view.accessibility.AccessibilityEvent
|
|
13
|
+
import android.view.accessibility.AccessibilityManager
|
|
14
|
+
import android.view.accessibility.AccessibilityNodeInfo
|
|
15
|
+
import android.view.accessibility.AccessibilityNodeProvider
|
|
16
|
+
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
|
|
17
|
+
import com.apollohg.editor.viewer.PreparedProseAccessibilityNode
|
|
18
|
+
import com.apollohg.editor.viewer.PreparedProseDrawingView
|
|
19
|
+
import com.apollohg.editor.viewer.PreparedProseInstrumentation
|
|
20
|
+
import com.apollohg.editor.viewer.PreparedProseInteraction
|
|
21
|
+
import com.apollohg.editor.viewer.PreparedProseLayout
|
|
22
|
+
import com.apollohg.editor.viewer.PreparedProseLayoutRegistry
|
|
23
|
+
import com.apollohg.editor.viewer.ProseViewerRequest
|
|
24
|
+
import com.apollohg.editor.viewer.ViewerAttachmentRevisionState
|
|
25
|
+
import com.apollohg.editor.viewer.ViewerDocument
|
|
26
|
+
import com.apollohg.editor.viewer.ViewerFontEnvironment
|
|
27
|
+
import com.apollohg.editor.viewer.ViewerImageAttachment
|
|
28
|
+
import com.apollohg.editor.viewer.ViewerImagePipeline
|
|
29
|
+
import com.apollohg.editor.viewer.accessibilityNodeVisibleOnScreen
|
|
30
|
+
import org.json.JSONArray
|
|
31
|
+
import org.json.JSONObject
|
|
32
|
+
|
|
33
|
+
sealed interface ProseViewerSource {
|
|
34
|
+
val value: String
|
|
35
|
+
val kind: String
|
|
36
|
+
|
|
37
|
+
data class Json(override val value: String) : ProseViewerSource {
|
|
38
|
+
override val kind: String = "json"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
data class Html(override val value: String) : ProseViewerSource {
|
|
42
|
+
override val kind: String = "html"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
data class ProseViewerConfiguration(
|
|
47
|
+
val configJson: String,
|
|
48
|
+
val themeJson: String? = null,
|
|
49
|
+
val imagePolicyJson: String? = null,
|
|
50
|
+
val imagesEnabled: Boolean = true,
|
|
51
|
+
val collapsesWhenEmpty: Boolean = true
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
@JvmInline
|
|
55
|
+
value class ProseViewerErrorCode(val value: String) {
|
|
56
|
+
companion object {
|
|
57
|
+
val INVALID_WIDTH = ProseViewerErrorCode("INVALID_WIDTH")
|
|
58
|
+
val LAYOUT_FAILED = ProseViewerErrorCode("LAYOUT_FAILED")
|
|
59
|
+
val RESOURCE_LOAD_FAILED = ProseViewerErrorCode("RESOURCE_LOAD_FAILED")
|
|
60
|
+
val INVALID_MENTION_ATTRIBUTES = ProseViewerErrorCode("INVALID_MENTION_ATTRIBUTES")
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
data class ProseViewerError(
|
|
65
|
+
val domain: String,
|
|
66
|
+
val code: ProseViewerErrorCode,
|
|
67
|
+
override val message: String
|
|
68
|
+
) : RuntimeException(message) {
|
|
69
|
+
companion object {
|
|
70
|
+
fun compiler(domain: String, code: String, message: String) =
|
|
71
|
+
ProseViewerError(domain, ProseViewerErrorCode(code), message)
|
|
72
|
+
|
|
73
|
+
fun invalidWidth() = ProseViewerError(
|
|
74
|
+
"viewer.host",
|
|
75
|
+
ProseViewerErrorCode.INVALID_WIDTH,
|
|
76
|
+
"A finite positive width is required for prose measurement."
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
fun layout(message: String) =
|
|
80
|
+
ProseViewerError("viewer.layout", ProseViewerErrorCode.LAYOUT_FAILED, message)
|
|
81
|
+
|
|
82
|
+
fun resource() = ProseViewerError(
|
|
83
|
+
"viewer.resource",
|
|
84
|
+
ProseViewerErrorCode.RESOURCE_LOAD_FAILED,
|
|
85
|
+
"An image resource could not be loaded."
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** A mention activated from an embedded prose viewer. */
|
|
91
|
+
data class ProseViewerMention(val docPos: Long, val label: String, val attrs: Map<String, Any?>)
|
|
92
|
+
|
|
93
|
+
/** Interaction callbacks for an embedded Android prose viewer. */
|
|
94
|
+
interface ProseViewerInteractionListener {
|
|
95
|
+
fun onLinkTap(view: ProseViewerView, href: String, text: String)
|
|
96
|
+
fun onMentionTap(view: ProseViewerView, mention: ProseViewerMention)
|
|
97
|
+
fun onViewerError(view: ProseViewerView, error: ProseViewerError) = Unit
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
abstract class ProseViewerInteractionListenerAdapter : ProseViewerInteractionListener {
|
|
101
|
+
override fun onLinkTap(view: ProseViewerView, href: String, text: String) = Unit
|
|
102
|
+
override fun onMentionTap(view: ProseViewerView, mention: ProseViewerMention) = Unit
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Display-only prose viewer for Android View hosts.
|
|
107
|
+
*/
|
|
108
|
+
class ProseViewerView @JvmOverloads constructor(
|
|
109
|
+
context: Context,
|
|
110
|
+
attrs: AttributeSet? = null,
|
|
111
|
+
defStyleAttr: Int = 0
|
|
112
|
+
) : ViewGroup(context, attrs, defStyleAttr) {
|
|
113
|
+
private val accessibilityManager = context.getSystemService(AccessibilityManager::class.java)
|
|
114
|
+
private val preparedInstrumentationOwner = "direct-${System.identityHashCode(this)}"
|
|
115
|
+
internal constructor(context: Context, registry: PreparedProseLayoutRegistry) : this(context) {
|
|
116
|
+
layoutRegistry = registry
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
var interactionListener: ProseViewerInteractionListener? = null
|
|
120
|
+
set(value) {
|
|
121
|
+
if (field === value) return
|
|
122
|
+
clearVirtualAccessibilityFocus()
|
|
123
|
+
field = value
|
|
124
|
+
updatePreparedInteractionCapabilities()
|
|
125
|
+
notifyAccessibilitySubtreeChanged()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private var layoutRegistry = PreparedProseLayoutRegistry.shared
|
|
129
|
+
private val preparedDrawingView = PreparedProseDrawingView(context)
|
|
130
|
+
private var preparedRequest: ProseViewerRequest? = null
|
|
131
|
+
private var retainedDocument: ViewerDocument? = null
|
|
132
|
+
private var preparedArtifact: PreparedProseLayout? = null
|
|
133
|
+
|
|
134
|
+
// Detach drops the direct registration but deliberately retains the
|
|
135
|
+
// immutable artifact for exact, no-recompile reattachment.
|
|
136
|
+
private var directMountedArtifact: PreparedProseLayout? = null
|
|
137
|
+
private var directError: ProseViewerError? = null
|
|
138
|
+
private var reportedGenerationIdentity: String? = null
|
|
139
|
+
private val attachmentRevisions = ViewerAttachmentRevisionState()
|
|
140
|
+
private val fontEnvironment = ViewerFontEnvironment()
|
|
141
|
+
private val viewerImagePipeline = ViewerImagePipeline()
|
|
142
|
+
|
|
143
|
+
private var accessibilityFocusedNode: FocusedVirtualNode? = null
|
|
144
|
+
private var preparedAccessibilityGeneration: String? = null
|
|
145
|
+
private val scrollChangedListener = ViewTreeObserver.OnScrollChangedListener {
|
|
146
|
+
reconcileVirtualAccessibilityFocus()
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
internal var linkTapsEnabled = true
|
|
150
|
+
set(value) {
|
|
151
|
+
if (field == value) return
|
|
152
|
+
clearVirtualAccessibilityFocus()
|
|
153
|
+
field = value
|
|
154
|
+
updatePreparedInteractionCapabilities()
|
|
155
|
+
notifyAccessibilitySubtreeChanged()
|
|
156
|
+
}
|
|
157
|
+
internal var onLinkTapForTesting: (() -> Unit)? = null
|
|
158
|
+
set(value) {
|
|
159
|
+
if (field === value) return
|
|
160
|
+
clearVirtualAccessibilityFocus()
|
|
161
|
+
field = value
|
|
162
|
+
updatePreparedInteractionCapabilities()
|
|
163
|
+
notifyAccessibilitySubtreeChanged()
|
|
164
|
+
}
|
|
165
|
+
internal var onMentionTapForTesting: (() -> Unit)? = null
|
|
166
|
+
set(value) {
|
|
167
|
+
if (field === value) return
|
|
168
|
+
clearVirtualAccessibilityFocus()
|
|
169
|
+
field = value
|
|
170
|
+
updatePreparedInteractionCapabilities()
|
|
171
|
+
notifyAccessibilitySubtreeChanged()
|
|
172
|
+
}
|
|
173
|
+
internal var accessibilityVisibilityForTesting: ((Rect) -> Boolean)? = null
|
|
174
|
+
internal val preparedLayoutForTesting: PreparedProseLayout?
|
|
175
|
+
get() = preparedArtifact
|
|
176
|
+
|
|
177
|
+
/** Mounted host total; the shared layout cache excludes mutable sidecars. */
|
|
178
|
+
internal val preparedSurfaceRetainedBytesForTesting: Long
|
|
179
|
+
get() = saturatedRetainedBytes(
|
|
180
|
+
preparedArtifact?.retainedBytes ?: 0L,
|
|
181
|
+
attachmentRevisions.retainedPublicationBytesForTesting.toLong(),
|
|
182
|
+
preparedDrawingView.retainedImagePixelsBytesForTesting
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
init {
|
|
186
|
+
DecodedBitmapBudget.shared(context)
|
|
187
|
+
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
|
|
188
|
+
|
|
189
|
+
// The public facade owns virtual accessibility. The drawing child is
|
|
190
|
+
// still interactive, but must not expose a duplicate virtual subtree.
|
|
191
|
+
preparedDrawingView.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
|
|
192
|
+
preparedDrawingView.publishesAccessibilitySubtree = false
|
|
193
|
+
updatePreparedInteractionCapabilities()
|
|
194
|
+
preparedDrawingView.onCodeHighlightsReady = {
|
|
195
|
+
preparedRequest?.let {
|
|
196
|
+
preparedRequest =
|
|
197
|
+
it.copy(nativeFontRevision = it.nativeFontRevision + 1)
|
|
198
|
+
}
|
|
199
|
+
requestLayout()
|
|
200
|
+
}
|
|
201
|
+
preparedDrawingView.onInteractionActivated = { activatePreparedInteraction(it) }
|
|
202
|
+
viewerImagePipeline.onPixels = { attachment, lease ->
|
|
203
|
+
val current = preparedRequest
|
|
204
|
+
if (current != null &&
|
|
205
|
+
viewerImagePipeline.acceptsCompletion(current.semanticGenerationIdentity)
|
|
206
|
+
) {
|
|
207
|
+
preparedDrawingView.putImageLease(attachment.id, lease)
|
|
208
|
+
} else {
|
|
209
|
+
lease.close()
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
viewerImagePipeline.onPixelsReleased = preparedDrawingView::removeImageLeases
|
|
213
|
+
viewerImagePipeline.onIntrinsicMetadata = { attachment, width, height ->
|
|
214
|
+
applyIntrinsicImageMetadata(attachment, width, height)
|
|
215
|
+
}
|
|
216
|
+
viewerImagePipeline.onResourceFailure =
|
|
217
|
+
{ attachment -> reportResourceFailureIfNeeded(attachment) }
|
|
218
|
+
fontEnvironment.onInvalidated = { revision -> applyFontEnvironmentRevision(revision) }
|
|
219
|
+
addView(
|
|
220
|
+
preparedDrawingView,
|
|
221
|
+
LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Starts an immutable direct-content generation. Compilation is retained through the first
|
|
227
|
+
* finite measurement even when the registry evicts its unmounted cache entry.
|
|
228
|
+
*/
|
|
229
|
+
fun apply(source: ProseViewerSource, configuration: ProseViewerConfiguration): Boolean {
|
|
230
|
+
fontEnvironment.activate()
|
|
231
|
+
val currentFontRevision = fontEnvironment.revision
|
|
232
|
+
preparedRequest?.let { current ->
|
|
233
|
+
if (current.source == source && current.configuration == configuration &&
|
|
234
|
+
current.fontEnvironmentRevision == currentFontRevision
|
|
235
|
+
) {
|
|
236
|
+
return directError == null
|
|
237
|
+
}
|
|
238
|
+
if (current.source == source && current.configuration == configuration) {
|
|
239
|
+
preparedRequest = current.copy(fontEnvironmentRevision = currentFontRevision)
|
|
240
|
+
requestLayout()
|
|
241
|
+
return directError == null
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
val next = ProseViewerRequest(
|
|
245
|
+
source,
|
|
246
|
+
configuration,
|
|
247
|
+
fontEnvironmentRevision = currentFontRevision,
|
|
248
|
+
attachmentRevision = 0
|
|
249
|
+
)
|
|
250
|
+
PreparedProseInstrumentation.invalidated(
|
|
251
|
+
PreparedProseInstrumentation.InvalidationReason.CONTENT
|
|
252
|
+
)
|
|
253
|
+
attachmentRevisions.beginSemanticGeneration(next.semanticGenerationIdentity)
|
|
254
|
+
clearVirtualAccessibilityFocus()
|
|
255
|
+
preparedRequest = next
|
|
256
|
+
retainedDocument = null
|
|
257
|
+
preparedArtifact = null
|
|
258
|
+
releaseDirectMountedArtifact()
|
|
259
|
+
viewerImagePipeline.cancel()
|
|
260
|
+
preparedDrawingView.clearImageLeases()
|
|
261
|
+
directError = null
|
|
262
|
+
reportedGenerationIdentity = null
|
|
263
|
+
preparedAccessibilityGeneration = null
|
|
264
|
+
// The replacement artifact owns the observable subtree transition.
|
|
265
|
+
preparedDrawingView.install(null, announceAccessibilitySubtree = false)
|
|
266
|
+
preparedDrawingView.visibility = View.VISIBLE
|
|
267
|
+
return try {
|
|
268
|
+
retainedDocument = layoutRegistry.compileDocument(next)
|
|
269
|
+
requestLayout()
|
|
270
|
+
true
|
|
271
|
+
} catch (error: ProseViewerError) {
|
|
272
|
+
directError = error
|
|
273
|
+
requestLayout()
|
|
274
|
+
false
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Clears content and pending work for a recycled host view.
|
|
280
|
+
*
|
|
281
|
+
* The interaction listener is retained so holders may assign it once.
|
|
282
|
+
*/
|
|
283
|
+
fun prepareForReuse() {
|
|
284
|
+
PreparedProseInstrumentation.invalidated(
|
|
285
|
+
PreparedProseInstrumentation.InvalidationReason.REUSE
|
|
286
|
+
)
|
|
287
|
+
fontEnvironment.deactivate()
|
|
288
|
+
clearDirectGeneration()
|
|
289
|
+
clearVirtualAccessibilityFocus()
|
|
290
|
+
requestLayout()
|
|
291
|
+
notifyAccessibilitySubtreeChanged()
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
295
|
+
preparedRequest?.let { request ->
|
|
296
|
+
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
|
|
297
|
+
val availableWidth = (
|
|
298
|
+
MeasureSpec.getSize(
|
|
299
|
+
widthMeasureSpec
|
|
300
|
+
) - paddingLeft - paddingRight
|
|
301
|
+
)
|
|
302
|
+
val artifact = layoutRegistry.measure(
|
|
303
|
+
request = request,
|
|
304
|
+
widthPx = if (widthMode == MeasureSpec.UNSPECIFIED) 0 else availableWidth,
|
|
305
|
+
density = resources.displayMetrics.density,
|
|
306
|
+
compiledDocument = retainedDocument,
|
|
307
|
+
fontScale = resources.configuration.fontScale,
|
|
308
|
+
measurementImageState = attachmentRevisions
|
|
309
|
+
)
|
|
310
|
+
val artifactChanged = preparedArtifact !== artifact
|
|
311
|
+
val accessibilityChanged =
|
|
312
|
+
artifactChanged ||
|
|
313
|
+
preparedAccessibilityGeneration != artifact.key.generationIdentity
|
|
314
|
+
if (accessibilityChanged) {
|
|
315
|
+
clearVirtualAccessibilityFocus()
|
|
316
|
+
}
|
|
317
|
+
preparedArtifact = artifact
|
|
318
|
+
registerDirectMountedArtifactIfAttached(artifact)
|
|
319
|
+
preparedDrawingView.install(artifact)
|
|
320
|
+
if (accessibilityChanged) {
|
|
321
|
+
preparedAccessibilityGeneration = artifact.key.generationIdentity
|
|
322
|
+
notifyAccessibilitySubtreeChanged()
|
|
323
|
+
}
|
|
324
|
+
reportDirectErrorIfNeeded(request, artifact.error ?: directError)
|
|
325
|
+
val desiredWidth = artifact.widthPx + paddingLeft + paddingRight
|
|
326
|
+
val intrinsicHeight = artifact.heightPx + paddingTop + paddingBottom
|
|
327
|
+
val measuredWidth = resolveSize(desiredWidth, widthMeasureSpec)
|
|
328
|
+
val measuredHeight = when (MeasureSpec.getMode(heightMeasureSpec)) {
|
|
329
|
+
MeasureSpec.EXACTLY -> MeasureSpec.getSize(heightMeasureSpec)
|
|
330
|
+
|
|
331
|
+
MeasureSpec.AT_MOST -> intrinsicHeight.coerceAtMost(
|
|
332
|
+
MeasureSpec.getSize(heightMeasureSpec)
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
else -> intrinsicHeight
|
|
336
|
+
}
|
|
337
|
+
setMeasuredDimension(measuredWidth, measuredHeight)
|
|
338
|
+
return
|
|
339
|
+
}
|
|
340
|
+
setMeasuredDimension(resolveSize(0, widthMeasureSpec), 0)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
344
|
+
if (preparedRequest != null) {
|
|
345
|
+
preparedDrawingView.layout(
|
|
346
|
+
paddingLeft,
|
|
347
|
+
paddingTop,
|
|
348
|
+
(right - left - paddingRight).coerceAtLeast(paddingLeft),
|
|
349
|
+
(bottom - top - paddingBottom).coerceAtLeast(paddingTop)
|
|
350
|
+
)
|
|
351
|
+
reconcileVirtualAccessibilityFocus()
|
|
352
|
+
requestVisibleImageAttachments()
|
|
353
|
+
return
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
override fun dispatchDraw(canvas: android.graphics.Canvas) {
|
|
358
|
+
reconcileVirtualAccessibilityFocus()
|
|
359
|
+
super.dispatchDraw(canvas)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
override fun onDetachedFromWindow() {
|
|
363
|
+
if (viewTreeObserver.isAlive) {
|
|
364
|
+
viewTreeObserver.removeOnScrollChangedListener(scrollChangedListener)
|
|
365
|
+
}
|
|
366
|
+
preparePreparedHostForWindowDetachment()
|
|
367
|
+
fontEnvironment.deactivate()
|
|
368
|
+
super.onDetachedFromWindow()
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
override fun onAttachedToWindow() {
|
|
372
|
+
super.onAttachedToWindow()
|
|
373
|
+
viewTreeObserver.addOnScrollChangedListener(scrollChangedListener)
|
|
374
|
+
if (preparedRequest != null) fontEnvironment.activate(deliverPending = true)
|
|
375
|
+
// Detachment only cancels image work. The immutable prepared artifact
|
|
376
|
+
// remains installed, so a direct host can draw/measure immediately on
|
|
377
|
+
// reattach without a semantic replacement or republish.
|
|
378
|
+
if (preparedRequest != null) {
|
|
379
|
+
preparedArtifact?.let(::registerDirectMountedArtifactIfAttached)
|
|
380
|
+
requestLayout()
|
|
381
|
+
preparedDrawingView.invalidate()
|
|
382
|
+
}
|
|
383
|
+
requestVisibleImageAttachments()
|
|
384
|
+
reconcileVirtualAccessibilityFocus()
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
override fun onVisibilityChanged(changedView: View, visibility: Int) {
|
|
388
|
+
super.onVisibilityChanged(changedView, visibility)
|
|
389
|
+
reconcileVirtualAccessibilityFocus()
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
override fun onWindowVisibilityChanged(visibility: Int) {
|
|
393
|
+
super.onWindowVisibilityChanged(visibility)
|
|
394
|
+
reconcileVirtualAccessibilityFocus()
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
override fun onConfigurationChanged(newConfig: Configuration) {
|
|
398
|
+
super.onConfigurationChanged(newConfig)
|
|
399
|
+
fontEnvironment.onConfigurationChanged(newConfig)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/** Explicit hook for React Native font loaders. */
|
|
403
|
+
fun invalidateFontEnvironment() = fontEnvironment.invalidateRegisteredFonts()
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Removes a directly owned prepared subtree. Virtual focus must clear
|
|
407
|
+
* before the artifact disappears and before its subtree notification.
|
|
408
|
+
*/
|
|
409
|
+
internal fun preparePreparedHostForWindowDetachment() {
|
|
410
|
+
clearVirtualAccessibilityFocus()
|
|
411
|
+
releaseDirectMountedArtifact()
|
|
412
|
+
if (preparedRequest != null) {
|
|
413
|
+
viewerImagePipeline.cancel()
|
|
414
|
+
// Cancellation/detachment does not create a new semantic source;
|
|
415
|
+
// retain its artifact/publication bits and revision for a later
|
|
416
|
+
// remount. Pixel ownership is released with the drawing map.
|
|
417
|
+
preparedDrawingView.clearImageLeases()
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private fun reportDirectErrorIfNeeded(request: ProseViewerRequest, error: ProseViewerError?) {
|
|
422
|
+
if (error == null ||
|
|
423
|
+
reportedGenerationIdentity == request.semanticGenerationIdentity
|
|
424
|
+
) {
|
|
425
|
+
return
|
|
426
|
+
}
|
|
427
|
+
reportedGenerationIdentity = request.semanticGenerationIdentity
|
|
428
|
+
interactionListener?.onViewerError(this, error)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
private fun reportResourceFailureIfNeeded(attachment: ViewerImageAttachment) {
|
|
432
|
+
if (!attachmentRevisions.recordResourceFailure(attachment.ordinal)) return
|
|
433
|
+
interactionListener?.onViewerError(this, ProseViewerError.resource())
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
private fun clearDirectGeneration() {
|
|
437
|
+
clearVirtualAccessibilityFocus()
|
|
438
|
+
preparedRequest = null
|
|
439
|
+
retainedDocument = null
|
|
440
|
+
preparedArtifact = null
|
|
441
|
+
releaseDirectMountedArtifact()
|
|
442
|
+
viewerImagePipeline.cancel()
|
|
443
|
+
attachmentRevisions.reset()
|
|
444
|
+
preparedDrawingView.clearImageLeases()
|
|
445
|
+
directError = null
|
|
446
|
+
reportedGenerationIdentity = null
|
|
447
|
+
preparedDrawingView.install(null, announceAccessibilitySubtree = false)
|
|
448
|
+
preparedDrawingView.visibility = View.GONE
|
|
449
|
+
preparedAccessibilityGeneration = null
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Android can measure a View that never enters a window. Keep that
|
|
454
|
+
* artifact on this View, but do not globally pin it as a direct mount
|
|
455
|
+
* until attachment gives us a deterministic matching release callback.
|
|
456
|
+
*/
|
|
457
|
+
private fun registerDirectMountedArtifactIfAttached(artifact: PreparedProseLayout) {
|
|
458
|
+
if (!isAttachedToWindow) return
|
|
459
|
+
if (directMountedArtifact === artifact) return
|
|
460
|
+
releaseDirectMountedArtifact()
|
|
461
|
+
layoutRegistry.registerDirectMounted(preparedInstrumentationOwner, artifact)
|
|
462
|
+
directMountedArtifact = artifact
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
private fun releaseDirectMountedArtifact() {
|
|
466
|
+
if (directMountedArtifact == null) return
|
|
467
|
+
layoutRegistry.releaseDirectMounted(preparedInstrumentationOwner)
|
|
468
|
+
directMountedArtifact = null
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
private fun configureImageGeneration(artifact: PreparedProseLayout) {
|
|
472
|
+
val request = preparedRequest ?: return
|
|
473
|
+
attachmentRevisions.beginSemanticGeneration(request.semanticGenerationIdentity)
|
|
474
|
+
attachmentRevisions.admit(artifact.imageAttachments.size)
|
|
475
|
+
viewerImagePipeline.begin(
|
|
476
|
+
request.semanticGenerationIdentity,
|
|
477
|
+
request.configuration.imagesEnabled,
|
|
478
|
+
ImageLoadingPolicy.fromJson(request.configuration.imagePolicyJson)
|
|
479
|
+
)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
private fun requestVisibleImageAttachments() {
|
|
483
|
+
val artifact = preparedArtifact ?: return
|
|
484
|
+
if (!isAttachedToWindow || preparedDrawingView.visibility != View.VISIBLE ||
|
|
485
|
+
!preparedDrawingView.isShown
|
|
486
|
+
) {
|
|
487
|
+
return
|
|
488
|
+
}
|
|
489
|
+
val visible = Rect()
|
|
490
|
+
if (!preparedDrawingView.getGlobalVisibleRect(visible) || visible.isEmpty) return
|
|
491
|
+
val location = IntArray(2)
|
|
492
|
+
preparedDrawingView.getLocationOnScreen(location)
|
|
493
|
+
visible.offset(-location[0], -location[1])
|
|
494
|
+
if (!visible.intersect(Rect(0, 0, preparedDrawingView.width, preparedDrawingView.height)) ||
|
|
495
|
+
visible.isEmpty
|
|
496
|
+
) {
|
|
497
|
+
return
|
|
498
|
+
}
|
|
499
|
+
configureImageGeneration(artifact)
|
|
500
|
+
viewerImagePipeline.updateVisibleRect(
|
|
501
|
+
visible,
|
|
502
|
+
artifact.imageAttachments
|
|
503
|
+
)
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
private fun applyIntrinsicImageMetadata(
|
|
507
|
+
attachment: ViewerImageAttachment,
|
|
508
|
+
width: Int,
|
|
509
|
+
height: Int
|
|
510
|
+
) {
|
|
511
|
+
val request = preparedRequest ?: return
|
|
512
|
+
if (!viewerImagePipeline.acceptsCompletion(request.semanticGenerationIdentity)) return
|
|
513
|
+
if (!attachmentRevisions.recordIntrinsicSize(
|
|
514
|
+
attachment.id,
|
|
515
|
+
attachment.ordinal,
|
|
516
|
+
width,
|
|
517
|
+
height,
|
|
518
|
+
attachment.declaredSize
|
|
519
|
+
)
|
|
520
|
+
) {
|
|
521
|
+
return
|
|
522
|
+
}
|
|
523
|
+
preparedRequest = request.copy(attachmentRevision = attachmentRevisions.revision)
|
|
524
|
+
PreparedProseInstrumentation.invalidated(
|
|
525
|
+
PreparedProseInstrumentation.InvalidationReason.ATTACHMENT
|
|
526
|
+
)
|
|
527
|
+
PreparedProseInstrumentation.retained(
|
|
528
|
+
PreparedProseInstrumentation.Owner.SIDECARS,
|
|
529
|
+
"direct-${System.identityHashCode(this)}",
|
|
530
|
+
attachmentRevisions.retainedPublicationBytesForTesting.toLong()
|
|
531
|
+
)
|
|
532
|
+
requestLayout()
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
private fun applyFontEnvironmentRevision(revision: Long) {
|
|
536
|
+
val request = preparedRequest ?: return
|
|
537
|
+
if (revision <= request.fontEnvironmentRevision) return
|
|
538
|
+
preparedRequest = request.copy(fontEnvironmentRevision = revision)
|
|
539
|
+
PreparedProseInstrumentation.invalidated(
|
|
540
|
+
PreparedProseInstrumentation.InvalidationReason.FONT
|
|
541
|
+
)
|
|
542
|
+
requestLayout()
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
private fun saturatedRetainedBytes(vararg values: Long): Long = values.fold(0L) {
|
|
546
|
+
total,
|
|
547
|
+
value
|
|
548
|
+
->
|
|
549
|
+
if (value > 0 && total > Long.MAX_VALUE - value) Long.MAX_VALUE else total + value
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
private fun activatePreparedInteraction(interaction: PreparedProseInteraction): Boolean {
|
|
553
|
+
return when (interaction.kind) {
|
|
554
|
+
PreparedProseInteraction.Kind.LINK -> {
|
|
555
|
+
val href = interaction.href ?: return false
|
|
556
|
+
if (!linkInteractionsActionable()) return false
|
|
557
|
+
onLinkTapForTesting?.invoke()
|
|
558
|
+
?: interactionListener?.onLinkTap(this, href, interaction.visibleText)
|
|
559
|
+
true
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
PreparedProseInteraction.Kind.MENTION -> {
|
|
563
|
+
if (!mentionInteractionsActionable()) return false
|
|
564
|
+
val docPos = interaction.docPos ?: return false
|
|
565
|
+
val attrs = interaction.attrsJson?.let(::parseMentionAttrs)
|
|
566
|
+
if (attrs == null) {
|
|
567
|
+
interactionListener?.onViewerError(
|
|
568
|
+
this,
|
|
569
|
+
ProseViewerError.compiler(
|
|
570
|
+
"viewer",
|
|
571
|
+
ProseViewerErrorCode.INVALID_MENTION_ATTRIBUTES.value,
|
|
572
|
+
"The prepared mention attributes are not a JSON object."
|
|
573
|
+
)
|
|
574
|
+
)
|
|
575
|
+
return false
|
|
576
|
+
}
|
|
577
|
+
onMentionTapForTesting?.invoke()
|
|
578
|
+
?: interactionListener?.onMentionTap(
|
|
579
|
+
this,
|
|
580
|
+
ProseViewerMention(docPos, interaction.label, attrs)
|
|
581
|
+
)
|
|
582
|
+
true
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
private fun parseMentionAttrs(json: String): Map<String, Any?>? = runCatching {
|
|
588
|
+
jsonObjectToMap(JSONObject(json))
|
|
589
|
+
}.getOrNull()
|
|
590
|
+
|
|
591
|
+
private fun jsonObjectToMap(value: JSONObject): Map<String, Any?> = buildMap {
|
|
592
|
+
value.keys().forEach { key -> put(key, jsonValue(value.get(key))) }
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
private fun jsonArrayToList(value: JSONArray): List<Any?> =
|
|
596
|
+
List(value.length()) { index -> jsonValue(value.get(index)) }
|
|
597
|
+
|
|
598
|
+
private fun jsonValue(value: Any): Any? = when (value) {
|
|
599
|
+
JSONObject.NULL -> null
|
|
600
|
+
is JSONObject -> jsonObjectToMap(value)
|
|
601
|
+
is JSONArray -> jsonArrayToList(value)
|
|
602
|
+
else -> value
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
internal fun activatePreparedInteractionForTesting(
|
|
606
|
+
interaction: PreparedProseInteraction
|
|
607
|
+
): Boolean = activatePreparedInteraction(interaction)
|
|
608
|
+
|
|
609
|
+
override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {
|
|
610
|
+
super.onInitializeAccessibilityNodeInfo(info)
|
|
611
|
+
info.className = android.widget.TextView::class.java.name
|
|
612
|
+
info.text = preparedAccessibleNodes().joinToString(" ") { it.label }
|
|
613
|
+
repeat(preparedAccessibleNodes().size) { index ->
|
|
614
|
+
info.addChild(this, index + FIRST_VIRTUAL_ANNOTATION_ID)
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
override fun getAccessibilityNodeProvider(): AccessibilityNodeProvider = annotationNodeProvider
|
|
619
|
+
|
|
620
|
+
// The replacement constructors are API 30 and this module's minSdk is 24;
|
|
621
|
+
// on API 30+ obtain() only delegates to them.
|
|
622
|
+
@Suppress("DEPRECATION")
|
|
623
|
+
private val annotationNodeProvider = object : AccessibilityNodeProvider() {
|
|
624
|
+
override fun createAccessibilityNodeInfo(virtualViewId: Int): AccessibilityNodeInfo? {
|
|
625
|
+
if (virtualViewId == View.NO_ID) {
|
|
626
|
+
return AccessibilityNodeInfo.obtain(this@ProseViewerView).also {
|
|
627
|
+
onInitializeAccessibilityNodeInfo(it)
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return preparedAccessibilityNodeInfo(virtualViewId)
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
override fun performAction(virtualViewId: Int, action: Int, arguments: Bundle?): Boolean =
|
|
634
|
+
performPreparedAccessibilityAction(virtualViewId, action)
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
private fun preparedAccessibleNodes() = preparedArtifact?.accessibilityNodes.orEmpty().filter {
|
|
638
|
+
when (it.role) {
|
|
639
|
+
PreparedProseAccessibilityNode.Role.LINK -> linkInteractionsActionable()
|
|
640
|
+
PreparedProseAccessibilityNode.Role.MENTION -> mentionInteractionsActionable()
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// AccessibilityNodeInfo() is API 30; setBoundsInParent has no replacement
|
|
645
|
+
// and API 24-28 services still read it.
|
|
646
|
+
@Suppress("DEPRECATION")
|
|
647
|
+
private fun preparedAccessibilityNodeInfo(virtualViewId: Int): AccessibilityNodeInfo? {
|
|
648
|
+
val node =
|
|
649
|
+
preparedAccessibleNodes().getOrNull(virtualViewId - FIRST_VIRTUAL_ANNOTATION_ID)
|
|
650
|
+
?: return null
|
|
651
|
+
val parentBounds = Rect(node.bounds).apply {
|
|
652
|
+
offset(preparedDrawingView.left, preparedDrawingView.top)
|
|
653
|
+
}
|
|
654
|
+
val screenBounds = preparedAccessibilityScreenBounds(node)
|
|
655
|
+
val visibleToUser = accessibilityNodeVisibleOnScreen(screenBounds)
|
|
656
|
+
reconcileVirtualAccessibilityFocus()
|
|
657
|
+
val identity = accessibilityIdentity(node)
|
|
658
|
+
return AccessibilityNodeInfo.obtain().apply {
|
|
659
|
+
packageName = context.packageName
|
|
660
|
+
className = android.widget.Button::class.java.name
|
|
661
|
+
setSource(this@ProseViewerView, virtualViewId)
|
|
662
|
+
setParent(this@ProseViewerView)
|
|
663
|
+
text = node.label
|
|
664
|
+
contentDescription = node.label
|
|
665
|
+
isClickable = true
|
|
666
|
+
isFocusable = true
|
|
667
|
+
AndroidApiCompat.setScreenReaderFocusable(this, true)
|
|
668
|
+
isAccessibilityFocused = accessibilityFocusedNode?.identity == identity
|
|
669
|
+
isVisibleToUser = visibleToUser
|
|
670
|
+
setBoundsInParent(parentBounds)
|
|
671
|
+
setBoundsInScreen(screenBounds)
|
|
672
|
+
addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK)
|
|
673
|
+
addAction(
|
|
674
|
+
if (isAccessibilityFocused) {
|
|
675
|
+
AccessibilityNodeInfo.AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS
|
|
676
|
+
} else {
|
|
677
|
+
AccessibilityNodeInfo.AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS
|
|
678
|
+
}
|
|
679
|
+
)
|
|
680
|
+
AccessibilityNodeInfoCompat.wrap(this).roleDescription =
|
|
681
|
+
if (node.role ==
|
|
682
|
+
com.apollohg.editor.viewer.PreparedProseAccessibilityNode.Role.LINK
|
|
683
|
+
) {
|
|
684
|
+
"link"
|
|
685
|
+
} else {
|
|
686
|
+
"mention"
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
private fun performPreparedAccessibilityAction(virtualViewId: Int, action: Int): Boolean {
|
|
692
|
+
val node =
|
|
693
|
+
preparedAccessibleNodes().getOrNull(virtualViewId - FIRST_VIRTUAL_ANNOTATION_ID)
|
|
694
|
+
?: return false
|
|
695
|
+
return when (action) {
|
|
696
|
+
AccessibilityNodeInfo.ACTION_CLICK -> if (preparedAccessibilityNodeVisible(node)) {
|
|
697
|
+
preparedArtifact?.interactions?.getOrNull(node.interactionIndex)
|
|
698
|
+
?.let(::activatePreparedInteraction) ?: false
|
|
699
|
+
} else {
|
|
700
|
+
false
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS ->
|
|
704
|
+
requestVirtualAccessibilityFocus(
|
|
705
|
+
virtualViewId
|
|
706
|
+
)
|
|
707
|
+
|
|
708
|
+
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS ->
|
|
709
|
+
clearVirtualAccessibilityFocus(
|
|
710
|
+
virtualViewId
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
else -> false
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
private fun requestVirtualAccessibilityFocus(virtualViewId: Int): Boolean {
|
|
718
|
+
val node = preparedAccessibleNodes()
|
|
719
|
+
.getOrNull(virtualViewId - FIRST_VIRTUAL_ANNOTATION_ID) ?: return false
|
|
720
|
+
if (!preparedAccessibilityNodeVisible(node)) return false
|
|
721
|
+
val identity = accessibilityIdentity(node)
|
|
722
|
+
if (accessibilityFocusedNode?.identity == identity) return false
|
|
723
|
+
accessibilityFocusedNode?.let { previous ->
|
|
724
|
+
accessibilityFocusedNode = null
|
|
725
|
+
sendVirtualAccessibilityEvent(
|
|
726
|
+
previous.virtualId,
|
|
727
|
+
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
|
|
728
|
+
)
|
|
729
|
+
}
|
|
730
|
+
accessibilityFocusedNode = FocusedVirtualNode(virtualViewId, identity)
|
|
731
|
+
invalidate()
|
|
732
|
+
sendVirtualAccessibilityEvent(
|
|
733
|
+
virtualViewId,
|
|
734
|
+
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
|
|
735
|
+
)
|
|
736
|
+
return true
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
private fun clearVirtualAccessibilityFocus(
|
|
740
|
+
virtualViewId: Int = accessibilityFocusedNode?.virtualId ?: View.NO_ID
|
|
741
|
+
): Boolean {
|
|
742
|
+
val focused = accessibilityFocusedNode ?: return false
|
|
743
|
+
if (virtualViewId == View.NO_ID || virtualViewId != focused.virtualId) return false
|
|
744
|
+
accessibilityFocusedNode = null
|
|
745
|
+
invalidate()
|
|
746
|
+
sendVirtualAccessibilityEvent(
|
|
747
|
+
virtualViewId,
|
|
748
|
+
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
|
|
749
|
+
)
|
|
750
|
+
return true
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
private fun reconcileVirtualAccessibilityFocus() {
|
|
754
|
+
val focused = accessibilityFocusedNode ?: return
|
|
755
|
+
val nodes = preparedAccessibleNodes()
|
|
756
|
+
val index = nodes.indexOfFirst { accessibilityIdentity(it) == focused.identity }
|
|
757
|
+
if (index < 0 || index + FIRST_VIRTUAL_ANNOTATION_ID != focused.virtualId) {
|
|
758
|
+
clearVirtualAccessibilityFocus(focused.virtualId)
|
|
759
|
+
return
|
|
760
|
+
}
|
|
761
|
+
if (!preparedAccessibilityNodeVisible(nodes[index])) {
|
|
762
|
+
clearVirtualAccessibilityFocus(focused.virtualId)
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
private fun preparedAccessibilityNodeVisible(node: PreparedProseAccessibilityNode): Boolean =
|
|
767
|
+
preparedAccessibilityScreenBounds(node).let { bounds ->
|
|
768
|
+
accessibilityVisibilityForTesting?.invoke(bounds)
|
|
769
|
+
?: accessibilityNodeVisibleOnScreen(bounds)
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
private fun preparedAccessibilityScreenBounds(node: PreparedProseAccessibilityNode): Rect {
|
|
773
|
+
val bounds = Rect(node.bounds).apply {
|
|
774
|
+
offset(preparedDrawingView.left, preparedDrawingView.top)
|
|
775
|
+
}
|
|
776
|
+
val location = IntArray(2)
|
|
777
|
+
getLocationOnScreen(location)
|
|
778
|
+
bounds.offset(location[0], location[1])
|
|
779
|
+
return bounds
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
private fun accessibilityIdentity(node: PreparedProseAccessibilityNode) =
|
|
783
|
+
AccessibilityNodeIdentity(
|
|
784
|
+
preparedArtifact?.key?.generationIdentity,
|
|
785
|
+
node.interactionIndex,
|
|
786
|
+
node.role,
|
|
787
|
+
node.label
|
|
788
|
+
)
|
|
789
|
+
|
|
790
|
+
private fun updatePreparedInteractionCapabilities() {
|
|
791
|
+
preparedDrawingView.linkInteractionsEnabled = linkInteractionsActionable()
|
|
792
|
+
preparedDrawingView.mentionInteractionsEnabled = mentionInteractionsActionable()
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
private fun linkInteractionsActionable(): Boolean =
|
|
796
|
+
linkTapsEnabled && (onLinkTapForTesting != null || interactionListener != null)
|
|
797
|
+
|
|
798
|
+
private fun mentionInteractionsActionable(): Boolean =
|
|
799
|
+
onMentionTapForTesting != null || interactionListener != null
|
|
800
|
+
|
|
801
|
+
// AccessibilityEvent(Int) is API 30; see the node provider above.
|
|
802
|
+
@Suppress("DEPRECATION")
|
|
803
|
+
private fun sendVirtualAccessibilityEvent(virtualViewId: Int, eventType: Int) {
|
|
804
|
+
if (!accessibilityManager.isEnabled) return
|
|
805
|
+
val event = AccessibilityEvent.obtain(eventType).apply {
|
|
806
|
+
packageName = context.packageName
|
|
807
|
+
className = android.widget.Button::class.java.name
|
|
808
|
+
setSource(this@ProseViewerView, virtualViewId)
|
|
809
|
+
}
|
|
810
|
+
parent?.requestSendAccessibilityEvent(this, event)
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
@Suppress("DEPRECATION")
|
|
814
|
+
private fun notifyAccessibilitySubtreeChanged() {
|
|
815
|
+
if (!accessibilityManager.isEnabled) return
|
|
816
|
+
val event = AccessibilityEvent.obtain(
|
|
817
|
+
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
|
|
818
|
+
).apply {
|
|
819
|
+
packageName = context.packageName
|
|
820
|
+
className = android.widget.TextView::class.java.name
|
|
821
|
+
contentChangeTypes = AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
|
|
822
|
+
setSource(this@ProseViewerView)
|
|
823
|
+
}
|
|
824
|
+
parent?.requestSendAccessibilityEvent(this, event)
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
companion object {
|
|
828
|
+
private const val FIRST_VIRTUAL_ANNOTATION_ID = 1
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Explicit availability signal for custom-family loaders. Unknown
|
|
832
|
+
* Typeface fallback is intentionally not treated as a missing font.
|
|
833
|
+
*/
|
|
834
|
+
@JvmStatic
|
|
835
|
+
fun registerAvailableFontFamily(family: String, typeface: Typeface) =
|
|
836
|
+
ViewerFontEnvironment.registerAvailableFamily(family, typeface)
|
|
837
|
+
|
|
838
|
+
@JvmStatic
|
|
839
|
+
fun markFontFamilyUnavailable(family: String) =
|
|
840
|
+
ViewerFontEnvironment.markFamilyUnavailable(family)
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
private data class AccessibilityNodeIdentity(
|
|
844
|
+
val generation: String?,
|
|
845
|
+
val interactionIndex: Int,
|
|
846
|
+
val role: PreparedProseAccessibilityNode.Role,
|
|
847
|
+
val label: String
|
|
848
|
+
)
|
|
849
|
+
|
|
850
|
+
private data class FocusedVirtualNode(
|
|
851
|
+
val virtualId: Int,
|
|
852
|
+
val identity: AccessibilityNodeIdentity
|
|
853
|
+
)
|
|
854
|
+
}
|