react-native-readium 4.0.2 → 5.0.0-rc.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/README.md +93 -9
- package/android/build.gradle +32 -29
- package/android/gradle.properties +3 -3
- package/android/src/main/java/com/reactnativereadium/ReadiumView.kt +88 -28
- package/android/src/main/java/com/reactnativereadium/ReadiumViewManager.kt +20 -19
- package/android/src/main/java/com/reactnativereadium/reader/BaseReaderFragment.kt +27 -6
- package/android/src/main/java/com/reactnativereadium/reader/EpubReaderFragment.kt +53 -69
- package/android/src/main/java/com/reactnativereadium/reader/PositionLabelManager.kt +132 -0
- package/android/src/main/java/com/reactnativereadium/reader/ReaderService.kt +64 -65
- package/android/src/main/java/com/reactnativereadium/reader/ReaderViewModel.kt +6 -85
- package/android/src/main/java/com/reactnativereadium/reader/VisualReaderFragment.kt +32 -2
- package/android/src/main/java/com/reactnativereadium/utils/FragmentFactory.kt +2 -3
- package/android/src/main/java/com/reactnativereadium/utils/JsonExtensions.kt +61 -0
- package/android/src/main/java/com/reactnativereadium/utils/MetadataNormalizer.kt +183 -0
- package/android/src/main/java/com/reactnativereadium/utils/NormalizedMetadata.kt +179 -0
- package/android/src/main/java/com/reactnativereadium/utils/extensions/InputStream.kt +0 -9
- package/ios/App/AppModule.swift +3 -9
- package/ios/Common/Toolkit/Extensions/Locator.swift +1 -1
- package/ios/Data/Bookmark.swift +1 -1
- package/ios/Reader/Common/ReaderViewController.swift +118 -21
- package/ios/Reader/EPUB/AssociatedColors.swift +1 -1
- package/ios/Reader/EPUB/EPUBHTTPServer.swift +13 -0
- package/ios/Reader/EPUB/EPUBModule.swift +1 -1
- package/ios/Reader/EPUB/EPUBViewController.swift +3 -4
- package/ios/Reader/ReaderFormatModule.swift +1 -1
- package/ios/Reader/ReaderModule.swift +1 -1
- package/ios/Reader/ReaderService.swift +70 -35
- package/ios/ReadiumView.swift +62 -25
- package/ios/ReadiumViewManager.m +1 -1
- package/lib/src/ReadiumViewNativeComponent.d.ts +19 -0
- package/lib/src/ReadiumViewNativeComponent.js +10 -0
- package/lib/src/components/BaseReadiumView.d.ts +1 -2
- package/lib/src/components/BaseReadiumView.js +3 -7
- package/lib/src/components/ReadiumView.js +15 -15
- package/lib/src/components/ReadiumView.web.js +100 -21
- package/lib/src/interfaces/BaseReadiumViewProps.d.ts +2 -1
- package/lib/src/interfaces/Preferences.d.ts +3 -2
- package/lib/src/interfaces/PublicationMetadata.d.ts +114 -0
- package/lib/src/interfaces/PublicationMetadata.js +1 -0
- package/lib/src/interfaces/PublicationReady.d.ts +15 -0
- package/lib/src/interfaces/PublicationReady.js +1 -0
- package/lib/src/interfaces/index.d.ts +2 -0
- package/lib/src/interfaces/index.js +2 -0
- package/lib/src/utils/index.d.ts +0 -1
- package/lib/src/utils/index.js +0 -1
- package/lib/web/hooks/index.d.ts +3 -2
- package/lib/web/hooks/index.js +3 -2
- package/lib/web/hooks/useLocationObserver.d.ts +2 -1
- package/lib/web/hooks/useLocationObserver.js +18 -11
- package/lib/web/hooks/useNavigator.d.ts +12 -0
- package/lib/web/hooks/useNavigator.js +87 -0
- package/lib/web/hooks/usePositionLabel.d.ts +9 -0
- package/lib/web/hooks/usePositionLabel.js +33 -0
- package/lib/web/hooks/usePreferencesObserver.d.ts +2 -0
- package/lib/web/hooks/usePreferencesObserver.js +54 -0
- package/lib/web/utils/manifestFetcher.d.ts +8 -0
- package/lib/web/utils/manifestFetcher.js +28 -0
- package/lib/web/utils/manifestNormalizer.d.ts +8 -0
- package/lib/web/utils/manifestNormalizer.js +70 -0
- package/lib/web/utils/metadataNormalizer.d.ts +53 -0
- package/lib/web/utils/metadataNormalizer.js +220 -0
- package/lib/web/utils/navigatorListeners.d.ts +6 -0
- package/lib/web/utils/navigatorListeners.js +50 -0
- package/lib/web/utils/publicationUtils.d.ts +15 -0
- package/lib/web/utils/publicationUtils.js +39 -0
- package/package.json +24 -14
- package/react-native-readium.podspec +7 -5
- package/src/ReadiumViewNativeComponent.ts +35 -0
- package/src/components/BaseReadiumView.tsx +3 -10
- package/src/components/ReadiumView.tsx +15 -15
- package/src/components/ReadiumView.web.tsx +120 -27
- package/src/interfaces/BaseReadiumViewProps.ts +2 -1
- package/src/interfaces/Preferences.ts +3 -2
- package/src/interfaces/PublicationMetadata.ts +141 -0
- package/src/interfaces/PublicationReady.ts +18 -0
- package/src/interfaces/index.ts +2 -0
- package/src/utils/index.ts +0 -1
- package/web/hooks/index.ts +3 -2
- package/web/hooks/useLocationObserver.ts +24 -11
- package/web/hooks/useNavigator.ts +146 -0
- package/web/hooks/usePositionLabel.ts +51 -0
- package/web/hooks/usePreferencesObserver.ts +69 -0
- package/web/utils/manifestFetcher.ts +38 -0
- package/web/utils/manifestNormalizer.ts +74 -0
- package/web/utils/metadataNormalizer.ts +238 -0
- package/web/utils/navigatorListeners.ts +60 -0
- package/web/utils/publicationUtils.ts +47 -0
- package/android/src/main/java/com/reactnativereadium/search/SearchFragment.kt +0 -100
- package/android/src/main/java/com/reactnativereadium/search/SearchPagingSource.kt +0 -44
- package/android/src/main/java/com/reactnativereadium/search/SearchResultAdapter.kt +0 -68
- package/android/src/main/java/com/reactnativereadium/utils/R2DispatcherActivity.kt +0 -45
- package/android/src/main/java/com/reactnativereadium/utils/SectionDecoration.kt +0 -98
- package/android/src/main/java/com/reactnativereadium/utils/SingleClickListener.kt +0 -32
- package/android/src/main/java/com/reactnativereadium/utils/extensions/Bitmap.kt +0 -23
- package/android/src/main/java/com/reactnativereadium/utils/extensions/Context.kt +0 -16
- package/android/src/main/java/com/reactnativereadium/utils/extensions/File.kt +0 -22
- package/android/src/main/java/com/reactnativereadium/utils/extensions/Link.kt +0 -6
- package/android/src/main/java/com/reactnativereadium/utils/extensions/Metadata.kt +0 -6
- package/android/src/main/java/com/reactnativereadium/utils/extensions/URL.kt +0 -29
- package/android/src/main/java/com/reactnativereadium/utils/extensions/Uri.kt +0 -17
- package/android/src/main/res/layout/fragment_search.xml +0 -39
- package/android/src/main/res/layout/item_recycle_search.xml +0 -14
- package/ios/Common/EPUBPreferences.swift +0 -8
- package/ios/Common/Paths.swift +0 -52
- package/ios/Common/Publication.swift +0 -15
- package/ios/Common/Toolkit/Extensions/HTTPClient.swift +0 -65
- package/ios/Common/Toolkit/Extensions/UIImage.swift +0 -12
- package/ios/Common/Toolkit/Extensions/UIViewController.swift +0 -19
- package/ios/Common/Toolkit/ScreenOrientation.swift +0 -13
- package/lib/src/utils/createFragment.d.ts +0 -1
- package/lib/src/utils/createFragment.js +0 -10
- package/lib/web/hooks/useReaderRef.d.ts +0 -3
- package/lib/web/hooks/useReaderRef.js +0 -85
- package/lib/web/hooks/useSettingsObserver.d.ts +0 -2
- package/lib/web/hooks/useSettingsObserver.js +0 -44
- package/src/utils/createFragment.ts +0 -15
- package/web/hooks/useReaderRef.ts +0 -109
- package/web/hooks/useSettingsObserver.ts +0 -61
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ allows you to do things like:
|
|
|
24
24
|
|
|
25
25
|
- Render an ebook view.
|
|
26
26
|
- Register for location changes (as the user pages through the book).
|
|
27
|
-
-
|
|
27
|
+
- Access publication metadata including table of contents, positions, and more via the `onPublicationReady` callback
|
|
28
28
|
- Control settings of the Reader. Things like:
|
|
29
29
|
- Dark Mode, Light Mode, Sepia Mode
|
|
30
30
|
- Font Size
|
|
@@ -71,36 +71,59 @@ yarn add react-native-readium
|
|
|
71
71
|
|
|
72
72
|
#### iOS
|
|
73
73
|
|
|
74
|
+
Requirements:
|
|
75
|
+
- Minimum iOS deployment target: iOS 13.4
|
|
76
|
+
- Swift compiler: Swift 6.0
|
|
77
|
+
- Xcode: Xcode 16.2 (or newer)
|
|
78
|
+
|
|
74
79
|
Due to the current state of the `Readium` swift libraries you need to manually
|
|
75
80
|
update your `Podfile` ([see more on that here](https://github.com/readium/swift-toolkit/issues/38)).
|
|
76
81
|
|
|
82
|
+
##### Breaking change when upgrading from v4 to v5!
|
|
83
|
+
|
|
84
|
+
If you are migrating from v4 to v5, please note that you must update your iOS Podfile to use the new Readium Pods (see iOS documentation below). Please make a note of both the new Pod names and the addition of the `source`'s in the Podfile.
|
|
85
|
+
|
|
77
86
|
```rb
|
|
78
87
|
# ./ios/Podfile
|
|
88
|
+
source 'https://github.com/readium/podspecs'
|
|
89
|
+
source 'https://cdn.cocoapods.org/'
|
|
90
|
+
|
|
79
91
|
...
|
|
80
|
-
|
|
92
|
+
|
|
93
|
+
platform :ios, '13.4'
|
|
81
94
|
|
|
82
95
|
...
|
|
83
96
|
|
|
84
97
|
target 'ExampleApp' do
|
|
85
98
|
config = use_native_modules!
|
|
86
99
|
...
|
|
87
|
-
pod '
|
|
88
|
-
pod 'ReadiumAdapterGCDWebServer',
|
|
89
|
-
pod '
|
|
90
|
-
pod '
|
|
91
|
-
pod '
|
|
92
|
-
pod '
|
|
100
|
+
pod 'ReadiumGCDWebServer', :modular_headers => true
|
|
101
|
+
pod 'ReadiumAdapterGCDWebServer', '~> 3.5.0'
|
|
102
|
+
pod 'ReadiumInternal', '~> 3.5.0'
|
|
103
|
+
pod 'ReadiumShared', '~> 3.5.0'
|
|
104
|
+
pod 'ReadiumStreamer', '~> 3.5.0'
|
|
105
|
+
pod 'ReadiumNavigator', '~> 3.5.0'
|
|
93
106
|
pod 'Minizip', modular_headers: true
|
|
94
107
|
...
|
|
95
108
|
end
|
|
96
109
|
```
|
|
97
110
|
|
|
111
|
+
|
|
98
112
|
Finally, install the pods:
|
|
99
113
|
|
|
100
114
|
`pod install`
|
|
101
115
|
|
|
102
116
|
#### Android
|
|
103
117
|
|
|
118
|
+
##### Breaking change when upgrading from v4 to v5!
|
|
119
|
+
|
|
120
|
+
This release upgrades the Android native implementation to a newer Readium Kotlin Toolkit.
|
|
121
|
+
Most apps won’t need code changes, but your **Android build configuration** might.
|
|
122
|
+
|
|
123
|
+
Requirements:
|
|
124
|
+
- **JDK 17** is required to build the Android app (the library targets Java/Kotlin 17).
|
|
125
|
+
- **compileSdkVersion** must be >= `31`.
|
|
126
|
+
|
|
104
127
|
If you're not using `compileSdkVersion` >= 31 you'll need to update that:
|
|
105
128
|
|
|
106
129
|
```groovy
|
|
@@ -114,8 +137,35 @@ buildscript {
|
|
|
114
137
|
...
|
|
115
138
|
```
|
|
116
139
|
|
|
140
|
+
##### Core library desugaring (may be required)
|
|
141
|
+
|
|
142
|
+
If you see build errors related to missing Java 8+ APIs (commonly `java.time.*`), enable
|
|
143
|
+
core library desugaring in your app:
|
|
144
|
+
|
|
145
|
+
```groovy
|
|
146
|
+
// android/app/build.gradle
|
|
147
|
+
android {
|
|
148
|
+
...
|
|
149
|
+
compileOptions {
|
|
150
|
+
coreLibraryDesugaringEnabled true
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
dependencies {
|
|
155
|
+
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.1.2"
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
##### Expo managed workflow
|
|
160
|
+
|
|
161
|
+
If your app uses Expo managed workflow (native `android/` is generated via `prebuild` / EAS),
|
|
162
|
+
apply the desugaring settings through an Expo config plugin (or `expo-build-properties`) so
|
|
163
|
+
they persist across builds.
|
|
164
|
+
|
|
117
165
|
## Usage
|
|
118
166
|
|
|
167
|
+
### Basic Example
|
|
168
|
+
|
|
119
169
|
```tsx
|
|
120
170
|
import React, { useState } from 'react';
|
|
121
171
|
import { ReadiumView } from 'react-native-readium';
|
|
@@ -134,6 +184,40 @@ const MyComponent: React.FC = () => {
|
|
|
134
184
|
}
|
|
135
185
|
```
|
|
136
186
|
|
|
187
|
+
### Using Publication Metadata
|
|
188
|
+
|
|
189
|
+
Access the table of contents, positions, and metadata when the publication is ready:
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
import React, { useState } from 'react';
|
|
193
|
+
import { ReadiumView } from 'react-native-readium';
|
|
194
|
+
import type { File, PublicationReadyEvent } from 'react-native-readium';
|
|
195
|
+
|
|
196
|
+
const MyComponent: React.FC = () => {
|
|
197
|
+
const [file] = useState<File>({
|
|
198
|
+
url: SOME_LOCAL_FILE_URL,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const [toc, setToc] = useState([]);
|
|
202
|
+
|
|
203
|
+
const handlePublicationReady = (event: PublicationReadyEvent) => {
|
|
204
|
+
console.log('Title:', event.metadata.title);
|
|
205
|
+
console.log('Author:', event.metadata.author);
|
|
206
|
+
console.log('Table of Contents:', event.tableOfContents);
|
|
207
|
+
console.log('Positions:', event.positions);
|
|
208
|
+
|
|
209
|
+
setToc(event.tableOfContents);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<ReadiumView
|
|
214
|
+
file={file}
|
|
215
|
+
onPublicationReady={handlePublicationReady}
|
|
216
|
+
/>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
137
221
|
[Take a look at the Example App](https://github.com/5-stones/react-native-readium/blob/main/example/src/App.tsx) for a more complex usage example.
|
|
138
222
|
|
|
139
223
|
## Supported Formats & DRM
|
|
@@ -164,7 +248,7 @@ DRM is not supported at this time. However, there is a clear path to [support it
|
|
|
164
248
|
| `preferences` | [`Partial<Preferences>`](https://github.com/readium/swift-toolkit/blob/main/docs/Guides/Navigator%20Preferences.md#appendix-preference-constraints) | :white_check_mark: | An object that allows you to control various aspects of the reader's UI (epub only) |
|
|
165
249
|
| `style` | `ViewStyle` | :white_check_mark: | A traditional style object. |
|
|
166
250
|
| `onLocationChange` | `(locator: Locator) => void` | :white_check_mark: | A callback that fires whenever the location is changed (e.g. the user transitions to a new page)|
|
|
167
|
-
| `
|
|
251
|
+
| `onPublicationReady` | `(event: PublicationReadyEvent) => void` | :white_check_mark: | A callback that fires once the publication is loaded and provides access to the table of contents, positions, and metadata. See the [`PublicationReadyEvent`](https://github.com/5-stones/react-native-readium/blob/main/src/interfaces/PublicationReady.ts) interface for details. |
|
|
168
252
|
|
|
169
253
|
#### :warning: Web vs Native File URLs
|
|
170
254
|
|
package/android/build.gradle
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
buildscript {
|
|
2
2
|
// Buildscript is evaluated before everything else so we can't use getExtOrDefault
|
|
3
3
|
def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['Readium_kotlinVersion']
|
|
4
|
-
ext.readium_version = '
|
|
4
|
+
ext.readium_version = '3.1.0'
|
|
5
5
|
|
|
6
6
|
repositories {
|
|
7
7
|
google()
|
|
@@ -9,7 +9,7 @@ buildscript {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
dependencies {
|
|
12
|
-
classpath 'com.android.tools.build:gradle:
|
|
12
|
+
classpath 'com.android.tools.build:gradle:8.5.0'
|
|
13
13
|
// noinspection DifferentKotlinGradleVersion
|
|
14
14
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
15
15
|
}
|
|
@@ -28,6 +28,7 @@ def getExtOrIntegerDefault(name) {
|
|
|
28
28
|
|
|
29
29
|
android {
|
|
30
30
|
compileSdkVersion getExtOrIntegerDefault('compileSdkVersion')
|
|
31
|
+
namespace "com.reactnativereadium"
|
|
31
32
|
defaultConfig {
|
|
32
33
|
minSdkVersion 21
|
|
33
34
|
targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
|
|
@@ -40,12 +41,15 @@ android {
|
|
|
40
41
|
minifyEnabled false
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
+
lint {
|
|
44
45
|
disable 'GradleCompatible'
|
|
45
46
|
}
|
|
46
47
|
compileOptions {
|
|
47
|
-
sourceCompatibility JavaVersion.
|
|
48
|
-
targetCompatibility JavaVersion.
|
|
48
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
49
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
50
|
+
}
|
|
51
|
+
kotlinOptions {
|
|
52
|
+
jvmTarget = "17"
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
buildFeatures {
|
|
@@ -140,46 +144,45 @@ dependencies {
|
|
|
140
144
|
}
|
|
141
145
|
|
|
142
146
|
// other deps
|
|
143
|
-
implementation "androidx.core:core-ktx:1.
|
|
144
|
-
implementation "androidx.activity:activity-ktx:1.
|
|
145
|
-
implementation "androidx.appcompat:appcompat:1.
|
|
146
|
-
implementation "androidx.browser:browser:1.
|
|
147
|
+
implementation "androidx.core:core-ktx:1.13.1"
|
|
148
|
+
implementation "androidx.activity:activity-ktx:1.9.0"
|
|
149
|
+
implementation "androidx.appcompat:appcompat:1.6.1"
|
|
150
|
+
implementation "androidx.browser:browser:1.7.0"
|
|
147
151
|
implementation "androidx.cardview:cardview:1.0.0"
|
|
148
|
-
implementation "androidx.constraintlayout:constraintlayout:2.1.
|
|
149
|
-
implementation "androidx.fragment:fragment-ktx:1.
|
|
150
|
-
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4
|
|
151
|
-
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4
|
|
152
|
-
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4
|
|
153
|
-
implementation 'androidx.navigation:navigation-fragment-ktx:2.
|
|
154
|
-
implementation 'androidx.navigation:navigation-ui-ktx:2.
|
|
155
|
-
implementation "androidx.paging:paging-runtime-ktx:3.1
|
|
156
|
-
implementation "androidx.recyclerview:recyclerview:1.2
|
|
157
|
-
implementation "androidx.viewpager2:viewpager2:1.
|
|
158
|
-
implementation "androidx.webkit:webkit:1.
|
|
159
|
-
implementation "com.google.android.material:material:1.
|
|
152
|
+
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
|
153
|
+
implementation "androidx.fragment:fragment-ktx:1.7.1"
|
|
154
|
+
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.8.4"
|
|
155
|
+
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.8.4"
|
|
156
|
+
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4"
|
|
157
|
+
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.7'
|
|
158
|
+
implementation 'androidx.navigation:navigation-ui-ktx:2.7.7'
|
|
159
|
+
implementation "androidx.paging:paging-runtime-ktx:3.2.1"
|
|
160
|
+
implementation "androidx.recyclerview:recyclerview:1.3.2"
|
|
161
|
+
implementation "androidx.viewpager2:viewpager2:1.1.0"
|
|
162
|
+
implementation "androidx.webkit:webkit:1.8.0"
|
|
163
|
+
implementation "com.google.android.material:material:1.11.0"
|
|
160
164
|
implementation "com.jakewharton.timber:timber:5.0.1"
|
|
161
165
|
// AM NOTE: needs to stay this version for now (June 24,2020)
|
|
162
166
|
//noinspection GradleDependency
|
|
163
167
|
implementation "com.squareup.picasso:picasso:2.71828"
|
|
164
168
|
implementation "joda-time:joda-time:2.10.13"
|
|
165
|
-
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
implementation "org.jsoup:jsoup:1.14.3"
|
|
169
|
+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0"
|
|
170
|
+
|
|
171
|
+
implementation "org.jsoup:jsoup:1.18.3"
|
|
169
172
|
|
|
170
173
|
// Room database
|
|
171
|
-
final roomVersion = "2.
|
|
174
|
+
final roomVersion = "2.6.1"
|
|
172
175
|
implementation "androidx.room:room-runtime:$roomVersion"
|
|
173
176
|
implementation "androidx.room:room-ktx:$roomVersion"
|
|
174
177
|
annotationProcessor "androidx.room:room-compiler:$roomVersion"
|
|
175
178
|
|
|
176
179
|
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
|
|
177
180
|
//noinspection LifecycleAnnotationProcessorWithJava8
|
|
178
|
-
annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.4
|
|
181
|
+
annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.8.4"
|
|
179
182
|
|
|
180
183
|
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
|
|
181
184
|
testImplementation "junit:junit:4.13.2"
|
|
182
185
|
|
|
183
|
-
androidTestImplementation "androidx.test.espresso:espresso-core:3.
|
|
184
|
-
androidTestImplementation "androidx.test:runner:1.
|
|
186
|
+
androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1"
|
|
187
|
+
androidTestImplementation "androidx.test:runner:1.5.2"
|
|
185
188
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
Readium_kotlinVersion=1.
|
|
2
|
-
Readium_compileSdkVersion=
|
|
3
|
-
Readium_targetSdkVersion=
|
|
1
|
+
Readium_kotlinVersion=1.9.25
|
|
2
|
+
Readium_compileSdkVersion=35
|
|
3
|
+
Readium_targetSdkVersion=35
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
package com.reactnativereadium
|
|
2
2
|
|
|
3
|
+
import android.util.Log
|
|
3
4
|
import android.view.Choreographer
|
|
4
5
|
import android.widget.FrameLayout
|
|
5
6
|
import androidx.fragment.app.FragmentActivity
|
|
6
7
|
import com.facebook.react.bridge.Arguments
|
|
8
|
+
import com.facebook.react.bridge.WritableMap
|
|
7
9
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
8
|
-
import com.facebook.react.uimanager.
|
|
10
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
11
|
+
import com.facebook.react.uimanager.events.Event
|
|
9
12
|
import com.reactnativereadium.reader.BaseReaderFragment
|
|
10
13
|
import com.reactnativereadium.reader.EpubReaderFragment
|
|
11
14
|
import com.reactnativereadium.reader.ReaderViewModel
|
|
@@ -13,18 +16,26 @@ import com.reactnativereadium.reader.VisualReaderFragment
|
|
|
13
16
|
import com.reactnativereadium.utils.Dimensions
|
|
14
17
|
import com.reactnativereadium.utils.File
|
|
15
18
|
import com.reactnativereadium.utils.LinkOrLocator
|
|
19
|
+
import com.reactnativereadium.utils.MetadataNormalizer
|
|
20
|
+
import com.reactnativereadium.utils.toWritableArray
|
|
21
|
+
import com.reactnativereadium.utils.toWritableMap
|
|
16
22
|
import org.readium.r2.navigator.epub.EpubNavigatorFragment
|
|
17
23
|
import org.readium.r2.navigator.epub.EpubPreferences
|
|
18
|
-
import org.readium.r2.shared.extensions.toMap
|
|
19
24
|
|
|
20
25
|
class ReadiumView(
|
|
21
26
|
val reactContext: ThemedReactContext
|
|
22
27
|
) : FrameLayout(reactContext) {
|
|
28
|
+
companion object {
|
|
29
|
+
private const val TAG = "ReadiumView"
|
|
30
|
+
}
|
|
31
|
+
|
|
23
32
|
var dimensions: Dimensions = Dimensions(0,0)
|
|
24
33
|
var file: File? = null
|
|
25
34
|
var fragment: BaseReaderFragment? = null
|
|
26
35
|
var isViewInitialized: Boolean = false
|
|
36
|
+
var isFragmentAdded: Boolean = false
|
|
27
37
|
var lateInitSerializedUserPreferences: String? = null
|
|
38
|
+
private var frameCallback: Choreographer.FrameCallback? = null
|
|
28
39
|
|
|
29
40
|
fun updateLocation(location: LinkOrLocator) : Boolean {
|
|
30
41
|
if (fragment == null) {
|
|
@@ -46,52 +57,93 @@ class ReadiumView(
|
|
|
46
57
|
}
|
|
47
58
|
|
|
48
59
|
fun addFragment(frag: BaseReaderFragment) {
|
|
60
|
+
if (isFragmentAdded) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
49
64
|
fragment = frag
|
|
65
|
+
isFragmentAdded = true
|
|
50
66
|
setupLayout()
|
|
51
67
|
lateInitSerializedUserPreferences?.let { updatePreferencesFromJsonString(it)}
|
|
52
68
|
val activity: FragmentActivity? = reactContext.currentActivity as FragmentActivity?
|
|
69
|
+
|
|
53
70
|
activity!!.supportFragmentManager
|
|
54
71
|
.beginTransaction()
|
|
55
72
|
.replace(this.id, frag, this.id.toString())
|
|
56
|
-
.
|
|
73
|
+
.commitNow()
|
|
74
|
+
|
|
75
|
+
// Ensure the fragment's view fills the container
|
|
76
|
+
frag.view?.layoutParams = FrameLayout.LayoutParams(
|
|
77
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
78
|
+
FrameLayout.LayoutParams.MATCH_PARENT
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, this.id)
|
|
82
|
+
|
|
83
|
+
val dispatch: (String, WritableMap?) -> Unit = { eventName, payload ->
|
|
84
|
+
if (eventDispatcher != null) {
|
|
85
|
+
eventDispatcher.dispatchEvent(ReadiumEvent(this.id, eventName, payload))
|
|
86
|
+
} else {
|
|
87
|
+
Log.w(TAG, "EventDispatcher is null for view id ${this.id}")
|
|
88
|
+
}
|
|
89
|
+
}
|
|
57
90
|
|
|
58
|
-
val module = reactContext.getJSModule(RCTEventEmitter::class.java)
|
|
59
91
|
// subscribe to reader events
|
|
60
92
|
frag.channel.receive(frag) { event ->
|
|
61
93
|
when (event) {
|
|
62
94
|
is ReaderViewModel.Event.LocatorUpdate -> {
|
|
63
|
-
val
|
|
64
|
-
|
|
65
|
-
module.receiveEvent(
|
|
66
|
-
this.id.toInt(),
|
|
67
|
-
ReadiumViewManager.ON_LOCATION_CHANGE,
|
|
68
|
-
payload
|
|
69
|
-
)
|
|
95
|
+
val payload = event.locator.toWritableMap()
|
|
96
|
+
dispatch(ReadiumViewManager.ON_LOCATION_CHANGE, payload)
|
|
70
97
|
}
|
|
71
|
-
is ReaderViewModel.Event.
|
|
72
|
-
val
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
98
|
+
is ReaderViewModel.Event.PublicationReady -> {
|
|
99
|
+
val payload = Arguments.createMap().apply {
|
|
100
|
+
putArray("tableOfContents", event.tableOfContents.toWritableArray())
|
|
101
|
+
putArray("positions", event.positions.map { it.toWritableMap() }.let { list ->
|
|
102
|
+
Arguments.createArray().apply {
|
|
103
|
+
list.forEach { pushMap(it) }
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
// Use spec-based normalizer to ensure consistent structure
|
|
107
|
+
putMap("metadata", MetadataNormalizer.normalize(event.metadata))
|
|
108
|
+
}
|
|
109
|
+
dispatch(ReadiumViewManager.ON_PUBLICATION_READY, payload)
|
|
82
110
|
}
|
|
83
111
|
}
|
|
84
112
|
}
|
|
85
113
|
}
|
|
86
114
|
|
|
115
|
+
// Custom event class for new architecture
|
|
116
|
+
private class ReadiumEvent(
|
|
117
|
+
viewTag: Int,
|
|
118
|
+
private val _eventName: String,
|
|
119
|
+
private val _eventData: WritableMap?
|
|
120
|
+
) : Event<ReadiumEvent>(viewTag) {
|
|
121
|
+
override fun getEventName(): String = _eventName
|
|
122
|
+
override fun getEventData(): WritableMap? = _eventData
|
|
123
|
+
}
|
|
124
|
+
|
|
87
125
|
private fun setupLayout() {
|
|
88
|
-
|
|
126
|
+
frameCallback = object : Choreographer.FrameCallback {
|
|
89
127
|
override fun doFrame(frameTimeNanos: Long) {
|
|
90
128
|
manuallyLayoutChildren()
|
|
91
129
|
this@ReadiumView.viewTreeObserver.dispatchOnGlobalLayout()
|
|
92
130
|
Choreographer.getInstance().postFrameCallback(this)
|
|
93
131
|
}
|
|
94
|
-
}
|
|
132
|
+
}
|
|
133
|
+
frameCallback?.let { Choreographer.getInstance().postFrameCallback(it) }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
override fun onDetachedFromWindow() {
|
|
137
|
+
super.onDetachedFromWindow()
|
|
138
|
+
// remove frame callback to avoid leaks/continuous callbacks after view is destroyed
|
|
139
|
+
frameCallback?.let {
|
|
140
|
+
try {
|
|
141
|
+
Choreographer.getInstance().removeFrameCallback(it)
|
|
142
|
+
} catch (e: Exception) {
|
|
143
|
+
Log.w(TAG, "Failed to remove frame callback: ${e.message}")
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
frameCallback = null
|
|
95
147
|
}
|
|
96
148
|
|
|
97
149
|
/**
|
|
@@ -101,9 +153,17 @@ class ReadiumView(
|
|
|
101
153
|
// propWidth and propHeight coming from react-native props
|
|
102
154
|
val width = dimensions.width
|
|
103
155
|
val height = dimensions.height
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
156
|
+
|
|
157
|
+
// Measure and layout each child within this container
|
|
158
|
+
for (i in 0 until childCount) {
|
|
159
|
+
val child = getChildAt(i)
|
|
160
|
+
child.measure(
|
|
161
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
162
|
+
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
|
163
|
+
)
|
|
164
|
+
// Position child at (0, 0) within this container, filling the container
|
|
165
|
+
child.layout(0, 0, width, height)
|
|
166
|
+
}
|
|
108
167
|
}
|
|
109
168
|
}
|
|
169
|
+
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.reactnativereadium
|
|
2
2
|
|
|
3
|
+
import android.util.Log
|
|
3
4
|
import com.facebook.react.bridge.*
|
|
4
5
|
import com.facebook.react.common.MapBuilder
|
|
5
6
|
import com.facebook.react.uimanager.annotations.ReactProp
|
|
@@ -35,33 +36,27 @@ class ReadiumViewManager(
|
|
|
35
36
|
)
|
|
36
37
|
)
|
|
37
38
|
.put(
|
|
38
|
-
|
|
39
|
+
ON_PUBLICATION_READY,
|
|
39
40
|
MapBuilder.of(
|
|
40
41
|
"phasedRegistrationNames",
|
|
41
|
-
MapBuilder.of("bubbled",
|
|
42
|
+
MapBuilder.of("bubbled", ON_PUBLICATION_READY)
|
|
42
43
|
)
|
|
43
44
|
)
|
|
44
45
|
.build()
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
override fun getCommandsMap(): MutableMap<String, Int> {
|
|
48
|
-
return MapBuilder.of("create", COMMAND_CREATE)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
48
|
override fun receiveCommand(view: ReadiumView, commandId: String?, args: ReadableArray?) {
|
|
52
49
|
super.receiveCommand(view, commandId, args)
|
|
53
|
-
val reactNativeViewId = args!!.getInt(0)
|
|
54
|
-
val commandIdInt = commandId!!.toInt()
|
|
55
50
|
|
|
56
|
-
when (
|
|
57
|
-
|
|
51
|
+
when (commandId) {
|
|
52
|
+
"create" -> {
|
|
58
53
|
view.isViewInitialized = true
|
|
59
|
-
|
|
60
54
|
if (view.file != null) {
|
|
61
55
|
buildForViewIfReady(view)
|
|
62
56
|
}
|
|
63
57
|
}
|
|
64
58
|
else -> {
|
|
59
|
+
Log.w(TAG, "Unknown command received: $commandId")
|
|
65
60
|
}
|
|
66
61
|
}
|
|
67
62
|
}
|
|
@@ -122,17 +117,23 @@ class ReadiumViewManager(
|
|
|
122
117
|
|
|
123
118
|
@ReactPropGroup(names = ["width", "height"], customType = "Style")
|
|
124
119
|
fun setStyle(view: ReadiumView?, index: Int, value: Int) {
|
|
125
|
-
if (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
120
|
+
if (view != null) {
|
|
121
|
+
if (index == 0) {
|
|
122
|
+
view.dimensions.width = value
|
|
123
|
+
}
|
|
124
|
+
if (index == 1) {
|
|
125
|
+
view.dimensions.height = value
|
|
126
|
+
}
|
|
127
|
+
buildForViewIfReady(view)
|
|
130
128
|
}
|
|
131
129
|
}
|
|
132
130
|
|
|
133
131
|
private fun buildForViewIfReady(view: ReadiumView) {
|
|
134
132
|
var file = view.file
|
|
135
|
-
|
|
133
|
+
val width = view.dimensions.width
|
|
134
|
+
val height = view.dimensions.height
|
|
135
|
+
|
|
136
|
+
if (file != null && view.isViewInitialized && width > 0 && height > 0) {
|
|
136
137
|
runBlocking {
|
|
137
138
|
svc.openPublication(file.path, file.initialLocation) { fragment ->
|
|
138
139
|
view.addFragment(fragment)
|
|
@@ -142,8 +143,8 @@ class ReadiumViewManager(
|
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
companion object {
|
|
146
|
+
private const val TAG = "ReadiumViewManager"
|
|
145
147
|
var ON_LOCATION_CHANGE = "onLocationChange"
|
|
146
|
-
var
|
|
147
|
-
var COMMAND_CREATE = 1
|
|
148
|
+
var ON_PUBLICATION_READY = "onPublicationReady"
|
|
148
149
|
}
|
|
149
150
|
}
|
|
@@ -4,20 +4,21 @@ import android.os.Bundle
|
|
|
4
4
|
import android.view.*
|
|
5
5
|
import androidx.fragment.app.Fragment
|
|
6
6
|
import androidx.lifecycle.lifecycleScope
|
|
7
|
+
import com.reactnativereadium.utils.EventChannel
|
|
7
8
|
import com.reactnativereadium.utils.LinkOrLocator
|
|
9
|
+
import kotlinx.coroutines.channels.Channel
|
|
8
10
|
import kotlinx.coroutines.flow.launchIn
|
|
9
11
|
import kotlinx.coroutines.flow.onEach
|
|
10
|
-
import
|
|
12
|
+
import kotlinx.coroutines.launch
|
|
13
|
+
import org.readium.r2.navigator.Navigator
|
|
11
14
|
import org.readium.r2.shared.publication.Locator
|
|
12
|
-
import
|
|
13
|
-
import kotlinx.coroutines.channels.Channel
|
|
15
|
+
import org.readium.r2.shared.publication.services.positions
|
|
14
16
|
|
|
15
17
|
/*
|
|
16
18
|
* Base reader fragment class
|
|
17
19
|
*
|
|
18
20
|
* Provides common menu items and saves last location on stop.
|
|
19
21
|
*/
|
|
20
|
-
@OptIn(ExperimentalDecorator::class)
|
|
21
22
|
abstract class BaseReaderFragment : Fragment() {
|
|
22
23
|
val channel = EventChannel(
|
|
23
24
|
Channel<ReaderViewModel.Event>(Channel.BUFFERED),
|
|
@@ -37,7 +38,27 @@ abstract class BaseReaderFragment : Fragment() {
|
|
|
37
38
|
|
|
38
39
|
val viewScope = viewLifecycleOwner.lifecycleScope
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
// Emit PublicationReady event with all metadata
|
|
42
|
+
viewScope.launch {
|
|
43
|
+
// positions() is a suspending function that returns List<Locator>
|
|
44
|
+
val positions = try {
|
|
45
|
+
model.publication.positions()
|
|
46
|
+
} catch (e: Exception) {
|
|
47
|
+
emptyList<Locator>()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Normalize metadata to ensure consistent structure across platforms
|
|
51
|
+
// This uses spec-based normalization to handle LocalizedStrings and other
|
|
52
|
+
// platform-specific serialization differences
|
|
53
|
+
channel.send(
|
|
54
|
+
ReaderViewModel.Event.PublicationReady(
|
|
55
|
+
tableOfContents = model.publication.tableOfContents,
|
|
56
|
+
positions = positions,
|
|
57
|
+
metadata = model.publication.metadata
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
41
62
|
navigator.currentLocator
|
|
42
63
|
.onEach { channel.send(ReaderViewModel.Event.LocatorUpdate(it)) }
|
|
43
64
|
.launchIn(viewScope)
|
|
@@ -53,7 +74,7 @@ abstract class BaseReaderFragment : Fragment() {
|
|
|
53
74
|
var locator: Locator? = null
|
|
54
75
|
when (location) {
|
|
55
76
|
is LinkOrLocator.Link -> {
|
|
56
|
-
locator =
|
|
77
|
+
locator = model.publication.locatorFromLink(location.link)
|
|
57
78
|
}
|
|
58
79
|
is LinkOrLocator.Locator -> {
|
|
59
80
|
locator = location.locator
|