react-native-readium 1.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +135 -0
  3. package/android/build.gradle +198 -0
  4. package/android/gradle.properties +3 -0
  5. package/android/src/main/AndroidManifest.xml +4 -0
  6. package/android/src/main/java/com/reactnativereadium/ReadiumPackage.kt +17 -0
  7. package/android/src/main/java/com/reactnativereadium/ReadiumView.kt +84 -0
  8. package/android/src/main/java/com/reactnativereadium/ReadiumViewManager.kt +80 -0
  9. package/android/src/main/java/com/reactnativereadium/epub/UserSettings.kt +236 -0
  10. package/android/src/main/java/com/reactnativereadium/reader/BaseReaderFragment.kt +81 -0
  11. package/android/src/main/java/com/reactnativereadium/reader/EpubReaderFragment.kt +375 -0
  12. package/android/src/main/java/com/reactnativereadium/reader/ImageReaderFragment.kt +68 -0
  13. package/android/src/main/java/com/reactnativereadium/reader/PdfReaderFragment.kt +82 -0
  14. package/android/src/main/java/com/reactnativereadium/reader/ReaderService.kt +116 -0
  15. package/android/src/main/java/com/reactnativereadium/reader/ReaderViewModel.kt +120 -0
  16. package/android/src/main/java/com/reactnativereadium/reader/VisualReaderFragment.kt +78 -0
  17. package/android/src/main/java/com/reactnativereadium/search/SearchFragment.kt +100 -0
  18. package/android/src/main/java/com/reactnativereadium/search/SearchPagingSource.kt +44 -0
  19. package/android/src/main/java/com/reactnativereadium/search/SearchResultAdapter.kt +68 -0
  20. package/android/src/main/java/com/reactnativereadium/utils/ContentResolverUtil.kt +156 -0
  21. package/android/src/main/java/com/reactnativereadium/utils/Dimensions.kt +6 -0
  22. package/android/src/main/java/com/reactnativereadium/utils/EventChannel.kt +61 -0
  23. package/android/src/main/java/com/reactnativereadium/utils/FragmentFactory.kt +46 -0
  24. package/android/src/main/java/com/reactnativereadium/utils/R2DispatcherActivity.kt +45 -0
  25. package/android/src/main/java/com/reactnativereadium/utils/SectionDecoration.kt +98 -0
  26. package/android/src/main/java/com/reactnativereadium/utils/SingleClickListener.kt +32 -0
  27. package/android/src/main/java/com/reactnativereadium/utils/SystemUiManagement.kt +63 -0
  28. package/android/src/main/java/com/reactnativereadium/utils/extensions/Bitmap.kt +23 -0
  29. package/android/src/main/java/com/reactnativereadium/utils/extensions/Context.kt +16 -0
  30. package/android/src/main/java/com/reactnativereadium/utils/extensions/File.kt +22 -0
  31. package/android/src/main/java/com/reactnativereadium/utils/extensions/InputStream.kt +23 -0
  32. package/android/src/main/java/com/reactnativereadium/utils/extensions/Link.kt +6 -0
  33. package/android/src/main/java/com/reactnativereadium/utils/extensions/Metadata.kt +6 -0
  34. package/android/src/main/java/com/reactnativereadium/utils/extensions/URL.kt +29 -0
  35. package/android/src/main/java/com/reactnativereadium/utils/extensions/Uri.kt +17 -0
  36. package/android/src/main/res/drawable/background_action_mode.xml +6 -0
  37. package/android/src/main/res/drawable/cnl.png +0 -0
  38. package/android/src/main/res/drawable/cover.png +0 -0
  39. package/android/src/main/res/drawable/ic_add_white_24dp.xml +9 -0
  40. package/android/src/main/res/drawable/ic_baseline_arrow_forward_24.xml +5 -0
  41. package/android/src/main/res/drawable/ic_baseline_bookmark_24.xml +10 -0
  42. package/android/src/main/res/drawable/ic_baseline_delete_24.xml +10 -0
  43. package/android/src/main/res/drawable/ic_baseline_edit_24.xml +10 -0
  44. package/android/src/main/res/drawable/ic_baseline_enhanced_encryption_24.xml +10 -0
  45. package/android/src/main/res/drawable/ic_baseline_fast_forward_24.xml +10 -0
  46. package/android/src/main/res/drawable/ic_baseline_fast_rewind_24.xml +10 -0
  47. package/android/src/main/res/drawable/ic_baseline_headphones_24.xml +10 -0
  48. package/android/src/main/res/drawable/ic_baseline_pause_24.xml +10 -0
  49. package/android/src/main/res/drawable/ic_baseline_play_arrow_24.xml +10 -0
  50. package/android/src/main/res/drawable/ic_baseline_search_24.xml +10 -0
  51. package/android/src/main/res/drawable/ic_baseline_skip_next_24.xml +10 -0
  52. package/android/src/main/res/drawable/ic_baseline_skip_previous_24.xml +10 -0
  53. package/android/src/main/res/drawable/ic_dashboard_black_24dp.xml +9 -0
  54. package/android/src/main/res/drawable/ic_fastforward_30.xml +7 -0
  55. package/android/src/main/res/drawable/ic_info_black_24dp.xml +9 -0
  56. package/android/src/main/res/drawable/ic_local_library_black_24dp.xml +9 -0
  57. package/android/src/main/res/drawable/ic_notch.xml +4 -0
  58. package/android/src/main/res/drawable/ic_outline_add_24.xml +10 -0
  59. package/android/src/main/res/drawable/ic_outline_format_align_justify_24.xml +10 -0
  60. package/android/src/main/res/drawable/ic_outline_format_align_left_24.xml +11 -0
  61. package/android/src/main/res/drawable/ic_outline_format_size_24.xml +10 -0
  62. package/android/src/main/res/drawable/ic_outline_light_mode_24.xml +10 -0
  63. package/android/src/main/res/drawable/ic_outline_menu_24.xml +10 -0
  64. package/android/src/main/res/drawable/ic_outline_remove_24.xml +10 -0
  65. package/android/src/main/res/drawable/ic_outline_wb_sunny_24.xml +10 -0
  66. package/android/src/main/res/drawable/ic_rewind_30.xml +7 -0
  67. package/android/src/main/res/drawable/icon_font_decrease.png +0 -0
  68. package/android/src/main/res/drawable/icon_font_increase.png +0 -0
  69. package/android/src/main/res/drawable/icon_overflow.png +0 -0
  70. package/android/src/main/res/drawable/rbtn_selector.xml +25 -0
  71. package/android/src/main/res/drawable/rbtn_textcolor_selector.xml +16 -0
  72. package/android/src/main/res/drawable/repfr.png +0 -0
  73. package/android/src/main/res/drawable/selector_blue.xml +42 -0
  74. package/android/src/main/res/drawable/selector_green.xml +42 -0
  75. package/android/src/main/res/drawable/selector_purple.xml +42 -0
  76. package/android/src/main/res/drawable/selector_red.xml +42 -0
  77. package/android/src/main/res/drawable/selector_yellow.xml +41 -0
  78. package/android/src/main/res/layout/activity_epub.xml +23 -0
  79. package/android/src/main/res/layout/activity_main.xml +32 -0
  80. package/android/src/main/res/layout/activity_reader.xml +6 -0
  81. package/android/src/main/res/layout/add_catalog_dialog.xml +25 -0
  82. package/android/src/main/res/layout/filter_row.xml +34 -0
  83. package/android/src/main/res/layout/filter_window.xml +24 -0
  84. package/android/src/main/res/layout/fragment_about.xml +150 -0
  85. package/android/src/main/res/layout/fragment_audiobook.xml +151 -0
  86. package/android/src/main/res/layout/fragment_bookshelf.xml +35 -0
  87. package/android/src/main/res/layout/fragment_catalog.xml +56 -0
  88. package/android/src/main/res/layout/fragment_catalog_feed_list.xml +27 -0
  89. package/android/src/main/res/layout/fragment_drm_management.xml +284 -0
  90. package/android/src/main/res/layout/fragment_listview.xml +24 -0
  91. package/android/src/main/res/layout/fragment_outline.xml +31 -0
  92. package/android/src/main/res/layout/fragment_publication_detail.xml +55 -0
  93. package/android/src/main/res/layout/fragment_reader.xml +6 -0
  94. package/android/src/main/res/layout/fragment_screen_reader.xml +143 -0
  95. package/android/src/main/res/layout/fragment_search.xml +39 -0
  96. package/android/src/main/res/layout/item_group_view.xml +22 -0
  97. package/android/src/main/res/layout/item_recycle_book.xml +55 -0
  98. package/android/src/main/res/layout/item_recycle_bookmark.xml +72 -0
  99. package/android/src/main/res/layout/item_recycle_button.xml +23 -0
  100. package/android/src/main/res/layout/item_recycle_catalog.xml +56 -0
  101. package/android/src/main/res/layout/item_recycle_highlight.xml +85 -0
  102. package/android/src/main/res/layout/item_recycle_horizontal.xml +45 -0
  103. package/android/src/main/res/layout/item_recycle_navigation.xml +34 -0
  104. package/android/src/main/res/layout/item_recycle_search.xml +14 -0
  105. package/android/src/main/res/layout/item_spinner_days.xml +19 -0
  106. package/android/src/main/res/layout/my_fragment.xml +13 -0
  107. package/android/src/main/res/layout/popup_delete.xml +29 -0
  108. package/android/src/main/res/layout/popup_note.xml +105 -0
  109. package/android/src/main/res/layout/popup_passphrase.xml +126 -0
  110. package/android/src/main/res/layout/popup_window_user_settings.xml +576 -0
  111. package/android/src/main/res/layout/section_header.xml +25 -0
  112. package/android/src/main/res/layout/view_action_mode.xml +100 -0
  113. package/android/src/main/res/layout/view_action_mode_reverse.xml +99 -0
  114. package/android/src/main/res/menu/bottom_nav_menu.xml +19 -0
  115. package/android/src/main/res/menu/menu_action_mode.xml +26 -0
  116. package/android/src/main/res/menu/menu_bookmark.xml +18 -0
  117. package/android/src/main/res/menu/menu_epub.xml +41 -0
  118. package/android/src/main/res/menu/menu_filter.xml +21 -0
  119. package/android/src/main/res/menu/menu_reader.xml +33 -0
  120. package/android/src/main/res/navigation/navigation.xml +46 -0
  121. package/android/src/main/res/values/arrays.xml +18 -0
  122. package/android/src/main/res/values/colors.xml +22 -0
  123. package/android/src/main/res/values/refs.xml +5 -0
  124. package/android/src/main/res/values/strings.xml +200 -0
  125. package/android/src/main/res/values/styles.xml +46 -0
  126. package/android/src/main/res/xml/network_security_config.xml +38 -0
  127. package/ios/App/AppModule.swift +62 -0
  128. package/ios/Common/Paths.swift +52 -0
  129. package/ios/Common/Publication.swift +15 -0
  130. package/ios/Common/Toolkit/Extensions/AnyPublisher.swift +14 -0
  131. package/ios/Common/Toolkit/Extensions/Future.swift +16 -0
  132. package/ios/Common/Toolkit/Extensions/HTTPClient.swift +65 -0
  133. package/ios/Common/Toolkit/Extensions/Locator.swift +14 -0
  134. package/ios/Common/Toolkit/Extensions/String.swift +16 -0
  135. package/ios/Common/Toolkit/Extensions/UIImage.swift +12 -0
  136. package/ios/Common/Toolkit/Extensions/UIViewController.swift +19 -0
  137. package/ios/Common/Toolkit/ScreenOrientation.swift +13 -0
  138. package/ios/Data/Bookmark.swift +23 -0
  139. package/ios/Reader/Common/ReaderViewController.swift +309 -0
  140. package/ios/Reader/EPUB/AssociatedColors.swift +27 -0
  141. package/ios/Reader/EPUB/EPUBModule.swift +38 -0
  142. package/ios/Reader/EPUB/EPUBViewController.swift +79 -0
  143. package/ios/Reader/ReaderError.swift +25 -0
  144. package/ios/Reader/ReaderFormatModule.swift +27 -0
  145. package/ios/Reader/ReaderModule.swift +84 -0
  146. package/ios/Reader/ReaderService.swift +126 -0
  147. package/ios/Readium-Bridging-Header.h +2 -0
  148. package/ios/Readium.xcodeproj/project.pbxproj +293 -0
  149. package/ios/ReadiumView.swift +156 -0
  150. package/ios/ReadiumViewManager.m +10 -0
  151. package/ios/ReadiumViewManager.swift +9 -0
  152. package/lib/commonjs/enums/Appearance.js +16 -0
  153. package/lib/commonjs/enums/Appearance.js.map +1 -0
  154. package/lib/commonjs/enums/ColumnCount.js +16 -0
  155. package/lib/commonjs/enums/ColumnCount.js.map +1 -0
  156. package/lib/commonjs/enums/FontFamily.js +21 -0
  157. package/lib/commonjs/enums/FontFamily.js.map +1 -0
  158. package/lib/commonjs/enums/TextAlignment.js +15 -0
  159. package/lib/commonjs/enums/TextAlignment.js.map +1 -0
  160. package/lib/commonjs/enums/index.js +58 -0
  161. package/lib/commonjs/enums/index.js.map +1 -0
  162. package/lib/commonjs/index.js +105 -0
  163. package/lib/commonjs/index.js.map +1 -0
  164. package/lib/commonjs/interfaces/Dimensions.js +2 -0
  165. package/lib/commonjs/interfaces/Dimensions.js.map +1 -0
  166. package/lib/commonjs/interfaces/File.js +6 -0
  167. package/lib/commonjs/interfaces/File.js.map +1 -0
  168. package/lib/commonjs/interfaces/Locator.js +2 -0
  169. package/lib/commonjs/interfaces/Locator.js.map +1 -0
  170. package/lib/commonjs/interfaces/Settings.js +71 -0
  171. package/lib/commonjs/interfaces/Settings.js.map +1 -0
  172. package/lib/commonjs/interfaces/index.js +58 -0
  173. package/lib/commonjs/interfaces/index.js.map +1 -0
  174. package/lib/commonjs/utils/index.js +30 -0
  175. package/lib/commonjs/utils/index.js.map +1 -0
  176. package/lib/module/enums/Appearance.js +9 -0
  177. package/lib/module/enums/Appearance.js.map +1 -0
  178. package/lib/module/enums/ColumnCount.js +9 -0
  179. package/lib/module/enums/ColumnCount.js.map +1 -0
  180. package/lib/module/enums/FontFamily.js +14 -0
  181. package/lib/module/enums/FontFamily.js.map +1 -0
  182. package/lib/module/enums/TextAlignment.js +8 -0
  183. package/lib/module/enums/TextAlignment.js.map +1 -0
  184. package/lib/module/enums/index.js +5 -0
  185. package/lib/module/enums/index.js.map +1 -0
  186. package/lib/module/index.js +61 -0
  187. package/lib/module/index.js.map +1 -0
  188. package/lib/module/interfaces/Dimensions.js +2 -0
  189. package/lib/module/interfaces/Dimensions.js.map +1 -0
  190. package/lib/module/interfaces/File.js +2 -0
  191. package/lib/module/interfaces/File.js.map +1 -0
  192. package/lib/module/interfaces/Locator.js +2 -0
  193. package/lib/module/interfaces/Locator.js.map +1 -0
  194. package/lib/module/interfaces/Settings.js +61 -0
  195. package/lib/module/interfaces/Settings.js.map +1 -0
  196. package/lib/module/interfaces/index.js +5 -0
  197. package/lib/module/interfaces/index.js.map +1 -0
  198. package/lib/module/utils/index.js +17 -0
  199. package/lib/module/utils/index.js.map +1 -0
  200. package/lib/typescript/enums/Appearance.d.ts +11 -0
  201. package/lib/typescript/enums/ColumnCount.d.ts +5 -0
  202. package/lib/typescript/enums/FontFamily.d.ts +10 -0
  203. package/lib/typescript/enums/TextAlignment.d.ts +4 -0
  204. package/lib/typescript/enums/index.d.ts +4 -0
  205. package/lib/typescript/index.d.ts +17 -0
  206. package/lib/typescript/interfaces/Dimensions.d.ts +4 -0
  207. package/lib/typescript/interfaces/File.d.ts +11 -0
  208. package/lib/typescript/interfaces/Locator.d.ts +14 -0
  209. package/lib/typescript/interfaces/Settings.d.ts +40 -0
  210. package/lib/typescript/interfaces/index.d.ts +4 -0
  211. package/lib/typescript/utils/index.d.ts +10 -0
  212. package/package.json +160 -0
  213. package/react-native-readium.podspec +25 -0
  214. package/src/enums/Appearance.ts +14 -0
  215. package/src/enums/ColumnCount.ts +6 -0
  216. package/src/enums/FontFamily.ts +11 -0
  217. package/src/enums/TextAlignment.ts +5 -0
  218. package/src/enums/index.ts +4 -0
  219. package/src/index.tsx +78 -0
  220. package/src/interfaces/Dimensions.ts +4 -0
  221. package/src/interfaces/File.ts +14 -0
  222. package/src/interfaces/Locator.ts +14 -0
  223. package/src/interfaces/Settings.ts +85 -0
  224. package/src/interfaces/index.ts +4 -0
  225. package/src/utils/index.ts +18 -0
@@ -0,0 +1,68 @@
1
+ /*
2
+ * Copyright 2021 Readium Foundation. All rights reserved.
3
+ * Use of this source code is governed by the BSD-style license
4
+ * available in the top-level LICENSE file of the project.
5
+ */
6
+
7
+ package com.reactnativereadium.reader
8
+
9
+ import android.graphics.PointF
10
+ import android.os.Bundle
11
+ import android.view.LayoutInflater
12
+ import android.view.View
13
+ import android.view.ViewGroup
14
+ import androidx.fragment.app.commitNow
15
+ import androidx.lifecycle.ViewModelProvider
16
+ import org.readium.r2.navigator.Navigator
17
+ import org.readium.r2.navigator.image.ImageNavigatorFragment
18
+ import org.readium.r2.shared.publication.Publication
19
+ import com.reactnativereadium.R
20
+ import com.reactnativereadium.utils.toggleSystemUi
21
+
22
+ class ImageReaderFragment : VisualReaderFragment(), ImageNavigatorFragment.Listener {
23
+
24
+ override lateinit var model: ReaderViewModel
25
+ override lateinit var navigator: Navigator
26
+ private lateinit var publication: Publication
27
+
28
+ override fun onCreate(savedInstanceState: Bundle?) {
29
+ ViewModelProvider(requireActivity()).get(ReaderViewModel::class.java).let {
30
+ model = it
31
+ publication = it.publication
32
+ }
33
+
34
+ childFragmentManager.fragmentFactory =
35
+ ImageNavigatorFragment.createFactory(publication, model.initialLocation, this)
36
+
37
+ super.onCreate(savedInstanceState)
38
+ }
39
+
40
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
41
+ val view = super.onCreateView(inflater, container, savedInstanceState)
42
+ if (savedInstanceState == null) {
43
+ childFragmentManager.commitNow {
44
+ add(R.id.fragment_reader_container, ImageNavigatorFragment::class.java, Bundle(), NAVIGATOR_FRAGMENT_TAG)
45
+ }
46
+ }
47
+ navigator = childFragmentManager.findFragmentByTag(NAVIGATOR_FRAGMENT_TAG)!! as Navigator
48
+ return view
49
+ }
50
+
51
+ override fun onTap(point: PointF): Boolean {
52
+ val viewWidth = requireView().width
53
+ val leftRange = 0.0..(0.2 * viewWidth)
54
+
55
+ when {
56
+ leftRange.contains(point.x) -> navigator.goBackward(animated = true)
57
+ leftRange.contains(viewWidth - point.x) -> navigator.goForward(animated = true)
58
+ else -> requireActivity().toggleSystemUi()
59
+ }
60
+
61
+ return true
62
+ }
63
+
64
+ companion object {
65
+
66
+ const val NAVIGATOR_FRAGMENT_TAG = "navigator"
67
+ }
68
+ }
@@ -0,0 +1,82 @@
1
+ /*
2
+ * Copyright 2021 Readium Foundation. All rights reserved.
3
+ * Use of this source code is governed by the BSD-style license
4
+ * available in the top-level LICENSE file of the project.
5
+ */
6
+
7
+ package com.reactnativereadium.reader
8
+
9
+ import android.graphics.PointF
10
+ import android.os.Bundle
11
+ import android.view.LayoutInflater
12
+ import android.view.View
13
+ import android.view.ViewGroup
14
+ import android.widget.Toast
15
+ import androidx.fragment.app.commitNow
16
+ import androidx.lifecycle.ViewModelProvider
17
+ import org.readium.r2.navigator.Navigator
18
+ import org.readium.r2.navigator.pdf.PdfNavigatorFragment
19
+ import org.readium.r2.shared.fetcher.Resource
20
+ import org.readium.r2.shared.publication.Link
21
+ import org.readium.r2.shared.publication.Publication
22
+ import com.reactnativereadium.R
23
+ import com.reactnativereadium.utils.toggleSystemUi
24
+
25
+ class PdfReaderFragment : VisualReaderFragment(), PdfNavigatorFragment.Listener {
26
+
27
+ override lateinit var model: ReaderViewModel
28
+ override lateinit var navigator: Navigator
29
+ private lateinit var publication: Publication
30
+
31
+ override fun onCreate(savedInstanceState: Bundle?) {
32
+ ViewModelProvider(requireActivity()).get(ReaderViewModel::class.java).let {
33
+ model = it
34
+ publication = it.publication
35
+ }
36
+
37
+ childFragmentManager.fragmentFactory =
38
+ PdfNavigatorFragment.createFactory(publication, model.initialLocation, this)
39
+
40
+ super.onCreate(savedInstanceState)
41
+ }
42
+
43
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
44
+ val view = super.onCreateView(inflater, container, savedInstanceState)
45
+ if (savedInstanceState == null) {
46
+ childFragmentManager.commitNow {
47
+ add(R.id.fragment_reader_container, PdfNavigatorFragment::class.java, Bundle(), NAVIGATOR_FRAGMENT_TAG)
48
+ }
49
+ }
50
+ navigator = childFragmentManager.findFragmentByTag(NAVIGATOR_FRAGMENT_TAG)!! as Navigator
51
+ return view
52
+ }
53
+
54
+ override fun onResourceLoadFailed(link: Link, error: Resource.Exception) {
55
+ val message = when (error) {
56
+ is Resource.Exception.OutOfMemory -> "The PDF is too large to be rendered on this device"
57
+ else -> "Failed to render this PDF"
58
+ }
59
+ Toast.makeText(requireActivity(), message, Toast.LENGTH_LONG).show()
60
+
61
+ // There's nothing we can do to recover, so we quit the Activity.
62
+ requireActivity().finish()
63
+ }
64
+
65
+ override fun onTap(point: PointF): Boolean {
66
+ val viewWidth = requireView().width
67
+ val leftRange = 0.0..(0.2 * viewWidth)
68
+
69
+ when {
70
+ leftRange.contains(point.x) -> navigator.goBackward()
71
+ leftRange.contains(viewWidth - point.x) -> navigator.goForward()
72
+ else -> requireActivity().toggleSystemUi()
73
+ }
74
+
75
+ return true
76
+ }
77
+
78
+ companion object {
79
+
80
+ const val NAVIGATOR_FRAGMENT_TAG = "navigator"
81
+ }
82
+ }
@@ -0,0 +1,116 @@
1
+ package com.reactnativereadium.reader
2
+
3
+ import android.annotation.SuppressLint
4
+ import androidx.lifecycle.ViewModelStore
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.util.RNLog
7
+ import java.io.File
8
+ import java.io.IOException
9
+ import java.net.ServerSocket
10
+ import java.net.URL
11
+ import org.readium.r2.shared.extensions.mediaType
12
+ import org.readium.r2.shared.extensions.tryOrNull
13
+ import org.readium.r2.shared.Injectable
14
+ import org.readium.r2.shared.publication.Locator
15
+ import org.readium.r2.shared.publication.asset.FileAsset
16
+ import org.readium.r2.shared.publication.Publication
17
+ import org.readium.r2.streamer.server.Server
18
+ import org.readium.r2.streamer.Streamer
19
+
20
+
21
+ class ReaderService(
22
+ private val reactContext: ReactApplicationContext
23
+ ) {
24
+ private var streamer = Streamer(reactContext)
25
+ // see R2App.onCreate
26
+ private var server: Server
27
+ // val channel = EventChannel(Channel<Event>(Channel.BUFFERED), viewModelScope)
28
+ private var store = ViewModelStore()
29
+
30
+ companion object {
31
+ @SuppressLint("StaticFieldLeak")
32
+ lateinit var server: Server
33
+ private set
34
+
35
+ lateinit var R2DIRECTORY: String
36
+ private set
37
+
38
+ var isServerStarted = false
39
+ private set
40
+ }
41
+
42
+ init {
43
+ val s = ServerSocket(0)
44
+ s.close()
45
+ server = Server(s.localPort, reactContext)
46
+ this.startServer()
47
+ }
48
+
49
+ suspend fun openPublication(
50
+ fileName: String,
51
+ initialLocation: Locator?,
52
+ callback: suspend (fragment: BaseReaderFragment) -> Unit
53
+ ) {
54
+ val file = File(fileName)
55
+ val asset = FileAsset(file, file.mediaType())
56
+
57
+ streamer.open(
58
+ asset,
59
+ allowUserInteraction = false,
60
+ sender = reactContext
61
+ )
62
+ .onSuccess {
63
+ val url = prepareToServe(it)
64
+ if (url != null) {
65
+ val readerFragment = EpubReaderFragment.newInstance(url)
66
+ readerFragment.initFactory(it, initialLocation)
67
+ callback.invoke(readerFragment)
68
+ }
69
+ }
70
+ .onFailure {
71
+ tryOrNull { asset.file.delete() }
72
+ RNLog.w(reactContext, "Error executing ReaderService.openPublication")
73
+ // TODO: implement failure event
74
+ }
75
+ }
76
+
77
+ private fun prepareToServe(publication: Publication): URL? {
78
+ val userProperties =
79
+ reactContext.filesDir.path + "/" + Injectable.Style.rawValue + "/UserProperties.json"
80
+ return server.addPublication(
81
+ publication,
82
+ userPropertiesFile = File(userProperties)
83
+ )
84
+ }
85
+
86
+ private fun startServer() {
87
+ if (!server.isAlive) {
88
+ try {
89
+ server.start()
90
+ } catch (e: IOException) {
91
+ RNLog.e(reactContext, "Unable to start the Readium server.")
92
+ }
93
+ if (server.isAlive) {
94
+ // // Add your own resources here
95
+ // server.loadCustomResource(assets.open("scripts/test.js"), "test.js")
96
+ // server.loadCustomResource(assets.open("styles/test.css"), "test.css")
97
+ // server.loadCustomFont(assets.open("fonts/test.otf"), applicationContext, "test.otf")
98
+
99
+ isServerStarted = true
100
+ }
101
+ }
102
+ }
103
+
104
+ sealed class Event {
105
+
106
+ class ImportPublicationFailed(val errorMessage: String?) : Event()
107
+
108
+ object UnableToMovePublication : Event()
109
+
110
+ object ImportPublicationSuccess : Event()
111
+
112
+ object ImportDatabaseFailed : Event()
113
+
114
+ class OpenBookError(val errorMessage: String?) : Event()
115
+ }
116
+ }
@@ -0,0 +1,120 @@
1
+ package com.reactnativereadium.reader
2
+
3
+ import android.graphics.Color
4
+ import androidx.lifecycle.ViewModel
5
+ import androidx.lifecycle.ViewModelProvider
6
+ import androidx.lifecycle.viewModelScope
7
+ import androidx.paging.*
8
+ import com.reactnativereadium.search.SearchPagingSource
9
+ import com.reactnativereadium.utils.EventChannel
10
+ import kotlinx.coroutines.channels.Channel
11
+ import kotlinx.coroutines.flow.*
12
+ import kotlinx.coroutines.launch
13
+ import org.readium.r2.navigator.Decoration
14
+ import org.readium.r2.navigator.ExperimentalDecorator
15
+ import org.readium.r2.shared.publication.Locator
16
+ import org.readium.r2.shared.publication.LocatorCollection
17
+ import org.readium.r2.shared.publication.Publication
18
+ import org.readium.r2.shared.publication.services.search.search
19
+ import org.readium.r2.shared.publication.services.search.SearchIterator
20
+ import org.readium.r2.shared.publication.services.search.SearchTry
21
+ import org.readium.r2.shared.Search
22
+ import org.readium.r2.shared.UserException
23
+ import org.readium.r2.shared.util.Try
24
+
25
+ @OptIn(Search::class, ExperimentalDecorator::class)
26
+ class ReaderViewModel(
27
+ val publication: Publication,
28
+ val initialLocation: Locator?
29
+ ) : ViewModel() {
30
+ val channel = EventChannel(Channel<Event>(Channel.BUFFERED), viewModelScope)
31
+
32
+ fun search(query: String) = viewModelScope.launch {
33
+ if (query == lastSearchQuery) return@launch
34
+ lastSearchQuery = query
35
+ _searchLocators.value = emptyList()
36
+ searchIterator = publication.search(query)
37
+ .onFailure { channel.send(Event.Failure(it)) }
38
+ .getOrNull()
39
+ pagingSourceFactory.invalidate()
40
+ channel.send(Event.StartNewSearch)
41
+ }
42
+
43
+ fun cancelSearch() = viewModelScope.launch {
44
+ _searchLocators.value = emptyList()
45
+ searchIterator?.close()
46
+ searchIterator = null
47
+ pagingSourceFactory.invalidate()
48
+ }
49
+
50
+ val searchLocators: StateFlow<List<Locator>> get() = _searchLocators
51
+ private var _searchLocators = MutableStateFlow<List<Locator>>(emptyList())
52
+
53
+ /**
54
+ * Maps the current list of search result locators into a list of [Decoration] objects to
55
+ * underline the results in the navigator.
56
+ */
57
+ val searchDecorations: Flow<List<Decoration>> by lazy {
58
+ searchLocators.map {
59
+ it.mapIndexed { index, locator ->
60
+ Decoration(
61
+ // The index in the search result list is a suitable Decoration ID, as long as
62
+ // we clear the search decorations between two searches.
63
+ id = index.toString(),
64
+ locator = locator,
65
+ style = Decoration.Style.Underline(tint = Color.RED)
66
+ )
67
+ }
68
+ }
69
+ }
70
+
71
+ private var lastSearchQuery: String? = null
72
+
73
+ private var searchIterator: SearchIterator? = null
74
+
75
+ private val pagingSourceFactory = InvalidatingPagingSourceFactory {
76
+ SearchPagingSource(listener = PagingSourceListener())
77
+ }
78
+
79
+ inner class PagingSourceListener : SearchPagingSource.Listener {
80
+ override suspend fun next(): SearchTry<LocatorCollection?> {
81
+ val iterator = searchIterator ?: return Try.success(null)
82
+ return iterator.next().onSuccess {
83
+ _searchLocators.value += (it?.locators ?: emptyList())
84
+ }
85
+ }
86
+ }
87
+
88
+ val searchResult: Flow<PagingData<Locator>> =
89
+ Pager(PagingConfig(pageSize = 20), pagingSourceFactory = pagingSourceFactory)
90
+ .flow.cachedIn(viewModelScope)
91
+
92
+ class Factory(
93
+ private val publication: Publication,
94
+ private val initialLocation: Locator?
95
+ ) : ViewModelProvider.NewInstanceFactory() {
96
+ override fun <T : ViewModel?> create(modelClass: Class<T>): T =
97
+ modelClass
98
+ .getDeclaredConstructor(
99
+ Publication::class.java,
100
+ Locator::class.java
101
+ )
102
+ .newInstance(
103
+ publication,
104
+ initialLocation
105
+ )
106
+ }
107
+
108
+ sealed class Event {
109
+ object OpenOutlineRequested : Event()
110
+ object OpenDrmManagementRequested : Event()
111
+ object StartNewSearch : Event()
112
+ class Failure(val error: UserException) : Event()
113
+ class LocatorUpdate(val locator: Locator) : Event()
114
+ }
115
+
116
+ sealed class FeedbackEvent {
117
+ object BookmarkSuccessfullyAdded : FeedbackEvent()
118
+ object BookmarkFailed : FeedbackEvent()
119
+ }
120
+ }
@@ -0,0 +1,78 @@
1
+ /*
2
+ * Copyright 2021 Readium Foundation. All rights reserved.
3
+ * Use of this source code is governed by the BSD-style license
4
+ * available in the top-level LICENSE file of the project.
5
+ */
6
+
7
+ package com.reactnativereadium.reader
8
+
9
+ import android.os.Bundle
10
+ import android.view.LayoutInflater
11
+ import android.view.View
12
+ import android.view.ViewGroup
13
+ import android.view.WindowInsets
14
+ import android.widget.FrameLayout
15
+ import androidx.fragment.app.Fragment
16
+ import org.readium.r2.navigator.DecorableNavigator
17
+ import org.readium.r2.navigator.ExperimentalDecorator
18
+ import com.reactnativereadium.R
19
+ import com.reactnativereadium.databinding.FragmentReaderBinding
20
+ import com.reactnativereadium.utils.clearPadding
21
+ import com.reactnativereadium.utils.hideSystemUi
22
+ import com.reactnativereadium.utils.padSystemUi
23
+ import com.reactnativereadium.utils.showSystemUi
24
+
25
+ /*
26
+ * Adds fullscreen support to the BaseReaderFragment
27
+ */
28
+ abstract class VisualReaderFragment : BaseReaderFragment() {
29
+
30
+ private lateinit var navigatorFragment: Fragment
31
+
32
+ private var _binding: FragmentReaderBinding? = null
33
+ val binding get() = _binding!!
34
+
35
+ override fun onCreateView(
36
+ inflater: LayoutInflater,
37
+ container: ViewGroup?,
38
+ savedInstanceState: Bundle?
39
+ ): View? {
40
+ _binding = FragmentReaderBinding.inflate(inflater, container, false)
41
+ return binding.root
42
+ }
43
+
44
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
45
+ super.onViewCreated(view, savedInstanceState)
46
+ navigatorFragment = navigator as Fragment
47
+
48
+ childFragmentManager.addOnBackStackChangedListener {
49
+ updateSystemUiVisibility()
50
+ }
51
+ binding.fragmentReaderContainer.setOnApplyWindowInsetsListener { container, insets ->
52
+ updateSystemUiPadding(container, insets)
53
+ insets
54
+ }
55
+ }
56
+
57
+ override fun onDestroyView() {
58
+ _binding = null
59
+ super.onDestroyView()
60
+ }
61
+
62
+ fun updateSystemUiVisibility() {
63
+ if (navigatorFragment.isHidden)
64
+ requireActivity().showSystemUi()
65
+ else
66
+ requireActivity().hideSystemUi()
67
+
68
+ requireView().requestApplyInsets()
69
+ }
70
+
71
+ private fun updateSystemUiPadding(container: View, insets: WindowInsets) {
72
+ if (navigatorFragment.isHidden) {
73
+ container.padSystemUi(insets, requireActivity())
74
+ } else {
75
+ container.clearPadding()
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,100 @@
1
+ /*
2
+ * Copyright 2021 Readium Foundation. All rights reserved.
3
+ * Use of this source code is governed by the BSD-style license
4
+ * available in the top-level LICENSE file of the project.
5
+ */
6
+
7
+ package com.reactnativereadium.search
8
+
9
+ import android.os.Bundle
10
+ import android.view.LayoutInflater
11
+ import android.view.View
12
+ import android.view.ViewGroup
13
+ import androidx.core.view.isVisible
14
+ import androidx.fragment.app.Fragment
15
+ import androidx.fragment.app.activityViewModels
16
+ import androidx.fragment.app.setFragmentResult
17
+ import androidx.lifecycle.lifecycleScope
18
+ import androidx.recyclerview.widget.DividerItemDecoration
19
+ import androidx.recyclerview.widget.LinearLayoutManager
20
+ import kotlinx.coroutines.flow.launchIn
21
+ import kotlinx.coroutines.flow.onEach
22
+ import org.readium.r2.shared.publication.Locator
23
+ import com.reactnativereadium.R
24
+ import com.reactnativereadium.databinding.FragmentSearchBinding
25
+ import com.reactnativereadium.reader.ReaderViewModel
26
+ import com.reactnativereadium.utils.SectionDecoration
27
+
28
+ class SearchFragment : Fragment(R.layout.fragment_search) {
29
+
30
+ private val viewModel: ReaderViewModel by activityViewModels()
31
+
32
+ private var _binding: FragmentSearchBinding? = null
33
+ private val binding get() = _binding!!
34
+
35
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
36
+ super.onViewCreated(view, savedInstanceState)
37
+
38
+ val viewScope = viewLifecycleOwner.lifecycleScope
39
+
40
+ val searchAdapter = SearchResultAdapter(object : SearchResultAdapter.Listener {
41
+ override fun onItemClicked(v: View, locator: Locator) {
42
+ val result = Bundle().apply {
43
+ putParcelable(SearchFragment::class.java.name, locator)
44
+ }
45
+ setFragmentResult(SearchFragment::class.java.name, result)
46
+ }
47
+ })
48
+
49
+ viewModel.searchResult
50
+ .onEach { searchAdapter.submitData(it) }
51
+ .launchIn(viewScope)
52
+
53
+ viewModel.searchLocators
54
+ .onEach { binding.noResultLabel.isVisible = it.isEmpty() }
55
+ .launchIn(viewScope)
56
+
57
+ viewModel.channel
58
+ .receive(viewLifecycleOwner) { event ->
59
+ when (event) {
60
+ ReaderViewModel.Event.StartNewSearch ->
61
+ binding.searchRecyclerView.scrollToPosition(0)
62
+ else -> {}
63
+ }
64
+ }
65
+
66
+ binding.searchRecyclerView.apply {
67
+ adapter = searchAdapter
68
+ layoutManager = LinearLayoutManager(activity)
69
+ addItemDecoration(SectionDecoration(context, object : SectionDecoration.Listener {
70
+ override fun isStartOfSection(itemPos: Int): Boolean =
71
+ viewModel.searchLocators.value.run {
72
+ when {
73
+ itemPos == 0 -> true
74
+ itemPos < 0 -> false
75
+ itemPos >= size -> false
76
+ else -> getOrNull(itemPos)?.title != getOrNull(itemPos-1)?.title
77
+ }
78
+ }
79
+
80
+ override fun sectionTitle(itemPos: Int): String =
81
+ viewModel.searchLocators.value.getOrNull(itemPos)?.title ?: ""
82
+ }))
83
+ addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
84
+ }
85
+ }
86
+
87
+ override fun onCreateView(
88
+ inflater: LayoutInflater,
89
+ container: ViewGroup?,
90
+ savedInstanceState: Bundle?
91
+ ): View {
92
+ _binding = FragmentSearchBinding.inflate(inflater, container, false)
93
+ return binding.root
94
+ }
95
+
96
+ override fun onDestroyView() {
97
+ super.onDestroyView()
98
+ _binding = null
99
+ }
100
+ }
@@ -0,0 +1,44 @@
1
+ /*
2
+ * Copyright 2021 Readium Foundation. All rights reserved.
3
+ * Use of this source code is governed by the BSD-style license
4
+ * available in the top-level LICENSE file of the project.
5
+ */
6
+
7
+ package com.reactnativereadium.search
8
+
9
+ import androidx.paging.PagingSource
10
+ import androidx.paging.PagingState
11
+ import org.readium.r2.shared.Search
12
+ import org.readium.r2.shared.publication.Locator
13
+ import org.readium.r2.shared.publication.LocatorCollection
14
+ import org.readium.r2.shared.publication.services.search.SearchIterator
15
+ import org.readium.r2.shared.publication.services.search.SearchTry
16
+
17
+ @OptIn(Search::class)
18
+ class SearchPagingSource(
19
+ private val listener: Listener?
20
+ ) : PagingSource<Unit, Locator>() {
21
+
22
+ interface Listener {
23
+ suspend fun next(): SearchTry<LocatorCollection?>
24
+ }
25
+
26
+ override val keyReuseSupported: Boolean get() = true
27
+
28
+ override fun getRefreshKey(state: PagingState<Unit, Locator>): Unit? = null
29
+
30
+ override suspend fun load(params: LoadParams<Unit>): LoadResult<Unit, Locator> {
31
+ listener ?: return LoadResult.Page(data = emptyList(), prevKey = null, nextKey = null)
32
+
33
+ return try {
34
+ val page = listener.next().getOrThrow()
35
+ LoadResult.Page(
36
+ data = page?.locators ?: emptyList(),
37
+ prevKey = null,
38
+ nextKey = if (page == null) null else Unit
39
+ )
40
+ } catch (e: Exception) {
41
+ LoadResult.Error(e)
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,68 @@
1
+ /*
2
+ * Copyright 2021 Readium Foundation. All rights reserved.
3
+ * Use of this source code is governed by the BSD-style license
4
+ * available in the top-level LICENSE file of the project.
5
+ */
6
+
7
+ package com.reactnativereadium.search
8
+
9
+ import android.os.Build
10
+ import android.text.Html
11
+ import android.view.LayoutInflater
12
+ import android.view.View
13
+ import android.view.ViewGroup
14
+ import androidx.paging.PagingDataAdapter
15
+ import androidx.recyclerview.widget.DiffUtil
16
+ import androidx.recyclerview.widget.RecyclerView
17
+ import org.readium.r2.shared.publication.Locator
18
+ import com.reactnativereadium.databinding.ItemRecycleSearchBinding
19
+ import com.reactnativereadium.utils.singleClick
20
+
21
+ /**
22
+ * This class is an adapter for Search results' recycler view.
23
+ */
24
+ class SearchResultAdapter(private var listener: Listener) :
25
+ PagingDataAdapter<Locator, SearchResultAdapter.ViewHolder>(ItemCallback()) {
26
+
27
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
28
+ return ViewHolder(
29
+ ItemRecycleSearchBinding.inflate(
30
+ LayoutInflater.from(parent.context), parent, false
31
+ )
32
+ )
33
+ }
34
+
35
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
36
+ val locator = getItem(position) ?: return
37
+ val html =
38
+ "${locator.text.before}<span style=\"background:yellow;\"><b>${locator.text.highlight}</b></span>${locator.text.after}"
39
+ holder.textView.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
40
+ Html.fromHtml(html, Html.FROM_HTML_MODE_COMPACT)
41
+ } else {
42
+ @Suppress("DEPRECATION")
43
+ Html.fromHtml(html)
44
+ }
45
+
46
+ holder.itemView.singleClick { v ->
47
+ listener.onItemClicked(v, locator)
48
+ }
49
+ }
50
+
51
+ inner class ViewHolder(val binding: ItemRecycleSearchBinding) :
52
+ RecyclerView.ViewHolder(binding.root) {
53
+ val textView = binding.text
54
+ }
55
+
56
+ interface Listener {
57
+ fun onItemClicked(v: View, locator: Locator)
58
+ }
59
+
60
+ private class ItemCallback : DiffUtil.ItemCallback<Locator>() {
61
+
62
+ override fun areItemsTheSame(oldItem: Locator, newItem: Locator): Boolean =
63
+ oldItem == newItem
64
+
65
+ override fun areContentsTheSame(oldItem: Locator, newItem: Locator): Boolean =
66
+ oldItem == newItem
67
+ }
68
+ }