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,236 @@
1
+ package com.reactnativereadium.epub
2
+
3
+ import android.content.Context
4
+ import android.content.SharedPreferences
5
+ import androidx.appcompat.app.AppCompatActivity
6
+ import org.json.JSONArray
7
+ import org.readium.r2.navigator.R2BasicWebView
8
+ import org.readium.r2.navigator.R2WebView
9
+ import org.readium.r2.navigator.epub.fxl.R2FXLLayout
10
+ import org.readium.r2.navigator.pager.R2ViewPager
11
+ import org.readium.r2.shared.*
12
+ import com.reactnativereadium.R
13
+ import java.io.File
14
+
15
+ class UserSettings(
16
+ var preferences: SharedPreferences,
17
+ val context: Context,
18
+ private val UIPreset: MutableMap<ReadiumCSSName, Boolean>
19
+ ) {
20
+
21
+ lateinit var resourcePager: R2ViewPager
22
+
23
+ private val appearanceValues = listOf("readium-default-on", "readium-sepia-on", "readium-night-on")
24
+ private val fontFamilyValues = listOf("Original", "PT Serif", "Roboto", "Source Sans Pro", "Vollkorn", "OpenDyslexic", "AccessibleDfA", "IA Writer Duospace")
25
+ private val textAlignmentValues = listOf("justify", "start")
26
+ private val columnCountValues = listOf("auto", "1", "2")
27
+
28
+ private var fontSize = 100f
29
+ private var fontOverride = false
30
+ private var fontFamily = 0
31
+ private var appearance = 0
32
+ private var verticalScroll = false
33
+
34
+ //Advanced settings
35
+ private var publisherDefaults = false
36
+ private var textAlignment = 0
37
+ private var columnCount = 0
38
+ private var wordSpacing = 0f
39
+ private var letterSpacing = 0f
40
+ private var pageMargins = 2f
41
+ private var lineHeight = 1f
42
+
43
+ private var userProperties: UserProperties
44
+
45
+ init {
46
+ appearance = preferences.getInt(APPEARANCE_REF, appearance)
47
+ verticalScroll = preferences.getBoolean(SCROLL_REF, verticalScroll)
48
+ fontFamily = preferences.getInt(FONT_FAMILY_REF, fontFamily)
49
+ if (fontFamily != 0) {
50
+ fontOverride = true
51
+ }
52
+ publisherDefaults = preferences.getBoolean(PUBLISHER_DEFAULT_REF, publisherDefaults)
53
+ textAlignment = preferences.getInt(TEXT_ALIGNMENT_REF, textAlignment)
54
+ columnCount = preferences.getInt(COLUMN_COUNT_REF, columnCount)
55
+
56
+
57
+ fontSize = preferences.getFloat(FONT_SIZE_REF, fontSize)
58
+ wordSpacing = preferences.getFloat(WORD_SPACING_REF, wordSpacing)
59
+ letterSpacing = preferences.getFloat(LETTER_SPACING_REF, letterSpacing)
60
+ pageMargins = preferences.getFloat(PAGE_MARGINS_REF, pageMargins)
61
+ lineHeight = preferences.getFloat(LINE_HEIGHT_REF, lineHeight)
62
+ userProperties = getUserSettings()
63
+
64
+ //Setting up screen brightness
65
+ val backLightValue = preferences.getInt("reader_brightness", 50).toFloat() / 100
66
+ val layoutParams = (context as AppCompatActivity).window.attributes
67
+ layoutParams.screenBrightness = backLightValue
68
+ context.window.attributes = layoutParams
69
+ }
70
+
71
+ fun updateSettingsFromMap(map: Map<String, Any>) {
72
+ userProperties.properties.forEach { property ->
73
+ val key = property.ref
74
+ val value = map[key]
75
+
76
+ val isPropertyModified = when (property) {
77
+ is Enumerable -> updateEnumerableFromKeyValue(property, key, value)
78
+ is Incremental -> updateIncrementalFromKeyValue(property, key, value)
79
+ is Switchable -> updateSwitchableFromKeyValue(property, key, value)
80
+ }
81
+
82
+ // apply the changes to the view
83
+ if (isPropertyModified) {
84
+ updateViewCSS(key)
85
+ }
86
+ }
87
+ }
88
+
89
+ private fun updateEnumerableFromKeyValue(property: Enumerable, key: String, value: Any?): Boolean {
90
+ if (value == null) return false
91
+ var update: Int?
92
+
93
+ if (value is Int) {
94
+ update = value
95
+ } else if (value is Float) {
96
+ update = value.toInt()
97
+ } else if (value is Double) {
98
+ update = value.toInt()
99
+ } else {
100
+ throw Error("Invalid value type '${value.javaClass.simpleName}' passed for setting: $key = $value")
101
+ }
102
+
103
+ property.index = update
104
+ updateEnumerable(property)
105
+ return true
106
+ }
107
+
108
+ private fun updateIncrementalFromKeyValue(property: Incremental, key: String, value: Any?): Boolean {
109
+ if (value == null) return false
110
+ var update: Float?
111
+
112
+ if (value is Int) {
113
+ update = value.toFloat()
114
+ } else if (value is Float) {
115
+ update = value
116
+ } else if (value is Double) {
117
+ update = value.toFloat()
118
+ } else {
119
+ throw Error("Invalid value type '${value.javaClass.simpleName}' passed for setting: $key = $value")
120
+ }
121
+
122
+ property.value = update
123
+ updateIncremental(property)
124
+ return true
125
+ }
126
+
127
+ private fun updateSwitchableFromKeyValue(property: Switchable, key: String, value: Any?): Boolean {
128
+ if (value == null) return false
129
+ var update: Boolean?
130
+
131
+ if (value is Boolean) {
132
+ update = value
133
+ } else {
134
+ throw Error("Invalid value type '${value.javaClass.simpleName}' passed for setting: $key = $value")
135
+ }
136
+
137
+ property.on = update
138
+ updateSwitchable(property)
139
+ return true
140
+ }
141
+
142
+ private fun getUserSettings(): UserProperties {
143
+
144
+ val userProperties = UserProperties()
145
+ // Publisher default system
146
+ userProperties.addSwitchable("readium-advanced-off", "readium-advanced-on", publisherDefaults, PUBLISHER_DEFAULT_REF, PUBLISHER_DEFAULT_NAME)
147
+ // Font override
148
+ userProperties.addSwitchable("readium-font-on", "readium-font-off", fontOverride, FONT_OVERRIDE_REF, FONT_OVERRIDE_NAME)
149
+ // Column count
150
+ userProperties.addEnumerable(columnCount, columnCountValues, COLUMN_COUNT_REF, COLUMN_COUNT_NAME)
151
+ // Appearance
152
+ userProperties.addEnumerable(appearance, appearanceValues, APPEARANCE_REF, APPEARANCE_NAME)
153
+ // Page margins
154
+ userProperties.addIncremental(pageMargins, 0.5f, 4f, 0.25f, "", PAGE_MARGINS_REF, PAGE_MARGINS_NAME)
155
+ // Text alignment
156
+ userProperties.addEnumerable(textAlignment, textAlignmentValues, TEXT_ALIGNMENT_REF, TEXT_ALIGNMENT_NAME)
157
+ // Font family
158
+ userProperties.addEnumerable(fontFamily, fontFamilyValues, FONT_FAMILY_REF, FONT_FAMILY_NAME)
159
+ // Font size
160
+ userProperties.addIncremental(fontSize, 100f, 300f, 25f, "%", FONT_SIZE_REF, FONT_SIZE_NAME)
161
+ // Line height
162
+ userProperties.addIncremental(lineHeight, 1f, 2f, 0.25f, "", LINE_HEIGHT_REF, LINE_HEIGHT_NAME)
163
+ // Word spacing
164
+ userProperties.addIncremental(wordSpacing, 0f, 0.5f, 0.25f, "rem", WORD_SPACING_REF, WORD_SPACING_NAME)
165
+ // Letter spacing
166
+ userProperties.addIncremental(letterSpacing, 0f, 0.5f, 0.0625f, "em", LETTER_SPACING_REF, LETTER_SPACING_NAME)
167
+ // Scroll
168
+ userProperties.addSwitchable("readium-scroll-on", "readium-scroll-off", verticalScroll, SCROLL_REF, SCROLL_NAME)
169
+
170
+ return userProperties
171
+ }
172
+
173
+ private fun makeJson(): JSONArray {
174
+ val array = JSONArray()
175
+ for (userProperty in userProperties.properties) {
176
+ array.put(userProperty.getJson())
177
+ }
178
+ return array
179
+ }
180
+
181
+
182
+ fun saveChanges() {
183
+ val json = makeJson()
184
+ val dir = File(context.filesDir.path + "/" + Injectable.Style.rawValue + "/")
185
+ dir.mkdirs()
186
+ val file = File(dir, "UserProperties.json")
187
+ file.printWriter().use { out ->
188
+ out.println(json)
189
+ }
190
+ }
191
+
192
+ private fun updateEnumerable(enumerable: Enumerable) {
193
+ preferences.edit().putInt(enumerable.ref, enumerable.index).apply()
194
+ saveChanges()
195
+ }
196
+
197
+
198
+ private fun updateSwitchable(switchable: Switchable) {
199
+ preferences.edit().putBoolean(switchable.ref, switchable.on).apply()
200
+ saveChanges()
201
+ }
202
+
203
+ private fun updateIncremental(incremental: Incremental) {
204
+ preferences.edit().putFloat(incremental.ref, incremental.value).apply()
205
+ saveChanges()
206
+ }
207
+
208
+ fun updateViewCSS(ref: String) {
209
+ for (i in 0 until resourcePager.childCount) {
210
+ val webView = resourcePager.getChildAt(i).findViewById(R.id.webView) as? R2WebView
211
+ webView?.let {
212
+ applyCSS(webView, ref)
213
+ } ?: run {
214
+ val zoomView = resourcePager.getChildAt(i).findViewById(R.id.r2FXLLayout) as R2FXLLayout
215
+ val webView1 = zoomView.findViewById(R.id.firstWebView) as? R2BasicWebView
216
+ val webView2 = zoomView.findViewById(R.id.secondWebView) as? R2BasicWebView
217
+ val webViewSingle = zoomView.findViewById(R.id.webViewSingle) as? R2BasicWebView
218
+
219
+ webView1?.let {
220
+ applyCSS(webView1, ref)
221
+ }
222
+ webView2?.let {
223
+ applyCSS(webView2, ref)
224
+ }
225
+ webViewSingle?.let {
226
+ applyCSS(webViewSingle, ref)
227
+ }
228
+ }
229
+ }
230
+ }
231
+
232
+ private fun applyCSS(view: R2BasicWebView, ref: String) {
233
+ val userSetting = userProperties.getByRef<UserProperty>(ref)
234
+ view.setProperty(userSetting.name, userSetting.toString())
235
+ }
236
+ }
@@ -0,0 +1,81 @@
1
+ package com.reactnativereadium.reader
2
+
3
+ import android.os.Bundle
4
+ import android.view.*
5
+ import androidx.fragment.app.Fragment
6
+ import androidx.lifecycle.lifecycleScope
7
+ import kotlinx.coroutines.flow.launchIn
8
+ import kotlinx.coroutines.flow.onEach
9
+ import org.readium.r2.navigator.*
10
+ import org.readium.r2.shared.publication.Locator
11
+ import com.reactnativereadium.R
12
+ import com.reactnativereadium.utils.EventChannel
13
+ import kotlinx.coroutines.channels.Channel
14
+
15
+ /*
16
+ * Base reader fragment class
17
+ *
18
+ * Provides common menu items and saves last location on stop.
19
+ */
20
+ @OptIn(ExperimentalDecorator::class)
21
+ abstract class BaseReaderFragment : Fragment() {
22
+ val channel = EventChannel(
23
+ Channel<ReaderViewModel.Event>(Channel.BUFFERED),
24
+ lifecycleScope
25
+ )
26
+
27
+ protected abstract val model: ReaderViewModel
28
+ protected abstract val navigator: Navigator
29
+
30
+ override fun onCreate(savedInstanceState: Bundle?) {
31
+ setHasOptionsMenu(true)
32
+ super.onCreate(savedInstanceState)
33
+ }
34
+
35
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
36
+ super.onViewCreated(view, savedInstanceState)
37
+
38
+ val viewScope = viewLifecycleOwner.lifecycleScope
39
+
40
+ navigator.currentLocator
41
+ .onEach { channel.send(ReaderViewModel.Event.LocatorUpdate(it)) }
42
+ .launchIn(viewScope)
43
+ }
44
+
45
+ override fun onHiddenChanged(hidden: Boolean) {
46
+ super.onHiddenChanged(hidden)
47
+ setMenuVisibility(!hidden)
48
+ requireActivity().invalidateOptionsMenu()
49
+ }
50
+
51
+ // TODO: this should probably be removed
52
+ override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
53
+ menuInflater.inflate(R.menu.menu_reader, menu)
54
+ menu.findItem(R.id.drm).isVisible = false
55
+ }
56
+
57
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
58
+ return when (item.itemId) {
59
+ R.id.toc -> {
60
+ model.channel.send(ReaderViewModel.Event.OpenOutlineRequested)
61
+ true
62
+ }
63
+ R.id.drm -> {
64
+ model.channel.send(ReaderViewModel.Event.OpenDrmManagementRequested)
65
+ true
66
+ }
67
+ else -> false
68
+ }
69
+ }
70
+
71
+ fun go(locator: Locator, animated: Boolean): Boolean {
72
+ // don't attempt to navigate if we're already there
73
+ val currentLocator = navigator.currentLocator.value
74
+ if (locator.hashCode() == currentLocator.hashCode()) {
75
+ return true
76
+ }
77
+
78
+ return navigator.go(locator, animated)
79
+ }
80
+
81
+ }
@@ -0,0 +1,375 @@
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.Color
10
+ import android.graphics.PointF
11
+ import android.os.Bundle
12
+ import android.view.*
13
+ import android.view.accessibility.AccessibilityManager
14
+ import androidx.appcompat.app.AppCompatActivity
15
+ import androidx.appcompat.widget.SearchView
16
+ import androidx.fragment.app.commitNow
17
+ import androidx.lifecycle.lifecycleScope
18
+ import androidx.lifecycle.ViewModelProvider
19
+ import com.reactnativereadium.epub.UserSettings
20
+ import com.reactnativereadium.R
21
+ import com.reactnativereadium.utils.toggleSystemUi
22
+ import java.net.URL
23
+ import kotlinx.coroutines.delay
24
+ import org.readium.r2.navigator.epub.EpubNavigatorFragment
25
+ import org.readium.r2.navigator.ExperimentalDecorator
26
+ import org.readium.r2.navigator.Navigator
27
+ import org.readium.r2.shared.APPEARANCE_REF
28
+ import org.readium.r2.shared.publication.Locator
29
+ import org.readium.r2.shared.publication.Publication
30
+ import org.readium.r2.shared.ReadiumCSSName
31
+ import org.readium.r2.shared.SCROLL_REF
32
+
33
+ @OptIn(ExperimentalDecorator::class)
34
+ class EpubReaderFragment : VisualReaderFragment(), EpubNavigatorFragment.Listener {
35
+
36
+ override lateinit var model: ReaderViewModel
37
+ override lateinit var navigator: Navigator
38
+ private lateinit var publication: Publication
39
+ lateinit var navigatorFragment: EpubNavigatorFragment
40
+ private lateinit var factory: ReaderViewModel.Factory
41
+
42
+ private lateinit var menuScreenReader: MenuItem
43
+ private lateinit var menuSearch: MenuItem
44
+ lateinit var menuSearchView: SearchView
45
+
46
+ private lateinit var userSettings: UserSettings
47
+ private var isScreenReaderVisible = false
48
+ private var isSearchViewIconified = true
49
+
50
+ // Accessibility
51
+ private var isExploreByTouchEnabled = false
52
+
53
+ fun initFactory(
54
+ publication: Publication,
55
+ initialLocation: Locator?
56
+ ) {
57
+ factory = ReaderViewModel.Factory(
58
+ publication,
59
+ initialLocation
60
+ )
61
+ }
62
+
63
+ fun updateSettingsFromMap(map: Map<String, Any>) {
64
+ if (userSettings != null) {
65
+ userSettings.updateSettingsFromMap(map)
66
+ }
67
+ }
68
+
69
+ override fun onCreate(savedInstanceState: Bundle?) {
70
+ // FIXME: this should be checked
71
+ // check(R2App.isServerStarted)
72
+
73
+ if (savedInstanceState != null) {
74
+ isScreenReaderVisible = savedInstanceState.getBoolean(IS_SCREEN_READER_VISIBLE_KEY)
75
+ isSearchViewIconified = savedInstanceState.getBoolean(IS_SEARCH_VIEW_ICONIFIED)
76
+ }
77
+
78
+ ViewModelProvider(this, factory)
79
+ .get(ReaderViewModel::class.java)
80
+ .let {
81
+ model = it
82
+ publication = it.publication
83
+ }
84
+
85
+ val baseUrl = checkNotNull(requireArguments().getString(BASE_URL_ARG))
86
+
87
+ childFragmentManager.fragmentFactory =
88
+ EpubNavigatorFragment.createFactory(
89
+ publication = publication,
90
+ baseUrl = baseUrl,
91
+ initialLocator = model.initialLocation,
92
+ listener = this,
93
+ config = EpubNavigatorFragment.Configuration().apply {
94
+ // Register the HTML template for our custom [DecorationStyleAnnotationMark].
95
+ // TODO: remove?
96
+ /* decorationTemplates[DecorationStyleAnnotationMark::class] = annotationMarkTemplate(activity) */
97
+ /* selectionActionModeCallback = customSelectionActionModeCallback */
98
+ }
99
+ )
100
+ // TODO: add search back in
101
+ // childFragmentManager.setFragmentResultListener(
102
+ // SearchFragment::class.java.name,
103
+ // this,
104
+ // FragmentResultListener { _, result ->
105
+ // menuSearch.collapseActionView()
106
+ // result.getParcelable<Locator>(SearchFragment::class.java.name)?.let {
107
+ // navigatorFragment.go(it)
108
+ // }
109
+ // }
110
+ // )
111
+ // TODO: add TTS back in
112
+ // childFragmentManager.setFragmentResultListener(
113
+ // ScreenReaderContract.REQUEST_KEY,
114
+ // this,
115
+ // FragmentResultListener { _, result ->
116
+ // val locator = ScreenReaderContract.parseResult(result).locator
117
+ // if (locator.href != navigator.currentLocator.value.href) {
118
+ // navigator.go(locator)
119
+ // }
120
+ // }
121
+ // )
122
+
123
+ setHasOptionsMenu(true)
124
+
125
+ super.onCreate(savedInstanceState)
126
+ }
127
+
128
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
129
+ val view = super.onCreateView(inflater, container, savedInstanceState)
130
+ val navigatorFragmentTag = getString(R.string.epub_navigator_tag)
131
+
132
+ if (savedInstanceState == null) {
133
+ childFragmentManager.commitNow {
134
+ add(R.id.fragment_reader_container, EpubNavigatorFragment::class.java, Bundle(), navigatorFragmentTag)
135
+ }
136
+ }
137
+ navigator = childFragmentManager.findFragmentByTag(navigatorFragmentTag) as Navigator
138
+ navigatorFragment = navigator as EpubNavigatorFragment
139
+
140
+ return view
141
+ }
142
+
143
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
144
+ super.onViewCreated(view, savedInstanceState)
145
+
146
+ val activity = requireActivity()
147
+ userSettings = UserSettings(navigatorFragment.preferences, activity, publication.userSettingsUIPreset)
148
+
149
+ // This is a hack to draw the right background color on top and bottom blank spaces
150
+ navigatorFragment.lifecycleScope.launchWhenStarted {
151
+ val appearancePref = navigatorFragment.preferences.getInt(APPEARANCE_REF, 0)
152
+ val backgroundsColors = mutableListOf("#ffffff", "#faf4e8", "#000000")
153
+ navigatorFragment.resourcePager.setBackgroundColor(Color.parseColor(backgroundsColors[appearancePref]))
154
+ }
155
+ }
156
+
157
+ override fun onResume() {
158
+ super.onResume()
159
+ val activity = requireActivity()
160
+
161
+ userSettings.resourcePager = navigatorFragment.resourcePager
162
+
163
+ // If TalkBack or any touch exploration service is activated we force scroll mode (and
164
+ // override user preferences)
165
+ val am = activity.getSystemService(AppCompatActivity.ACCESSIBILITY_SERVICE) as AccessibilityManager
166
+ isExploreByTouchEnabled = am.isTouchExplorationEnabled
167
+
168
+ if (isExploreByTouchEnabled) {
169
+ // Preset & preferences adapted
170
+ publication.userSettingsUIPreset[ReadiumCSSName.ref(SCROLL_REF)] = true
171
+ navigatorFragment.preferences.edit().putBoolean(SCROLL_REF, true).apply() //overriding user preferences
172
+ userSettings.saveChanges()
173
+
174
+ lifecycleScope.launchWhenResumed {
175
+ delay(500)
176
+ userSettings.updateViewCSS(SCROLL_REF)
177
+ }
178
+ } else {
179
+ if (publication.cssStyle != "cjk-vertical") {
180
+ publication.userSettingsUIPreset.remove(ReadiumCSSName.ref(SCROLL_REF))
181
+ }
182
+ }
183
+ }
184
+
185
+ override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
186
+ super.onCreateOptionsMenu(menu, menuInflater)
187
+ menuInflater.inflate(R.menu.menu_epub, menu)
188
+
189
+ menuScreenReader = menu.findItem(R.id.screen_reader)
190
+ menuSearch = menu.findItem(R.id.search)
191
+ menuSearchView = menuSearch.actionView as SearchView
192
+
193
+ /* connectSearch() */
194
+ if (!isSearchViewIconified) menuSearch.expandActionView()
195
+ }
196
+
197
+ override fun onSaveInstanceState(outState: Bundle) {
198
+ super.onSaveInstanceState(outState)
199
+ outState.putBoolean(IS_SCREEN_READER_VISIBLE_KEY, isScreenReaderVisible)
200
+ outState.putBoolean(IS_SEARCH_VIEW_ICONIFIED, isSearchViewIconified)
201
+ }
202
+ // TODO: add search
203
+ // private fun connectSearch() {
204
+ // menuSearch.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
205
+ //
206
+ // override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
207
+ // if (isSearchViewIconified) { // It is not a state restoration.
208
+ // showSearchFragment()
209
+ // }
210
+ //
211
+ // isSearchViewIconified = false
212
+ // return true
213
+ // }
214
+ //
215
+ // override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
216
+ // isSearchViewIconified = true
217
+ // childFragmentManager.popBackStack()
218
+ // menuSearchView.clearFocus()
219
+ //
220
+ // return true
221
+ // }
222
+ // })
223
+ //
224
+ // menuSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
225
+ //
226
+ // override fun onQueryTextSubmit(query: String): Boolean {
227
+ // model.search(query)
228
+ // menuSearchView.clearFocus()
229
+ //
230
+ // return false
231
+ // }
232
+ //
233
+ // override fun onQueryTextChange(s: String): Boolean {
234
+ // return false
235
+ // }
236
+ // })
237
+ //
238
+ // menuSearchView.findViewById<ImageView>(R.id.search_close_btn).setOnClickListener {
239
+ // menuSearchView.requestFocus()
240
+ // model.cancelSearch()
241
+ // menuSearchView.setQuery("", false)
242
+ //
243
+ // (activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager)?.showSoftInput(
244
+ // this.view, InputMethodManager.SHOW_FORCED
245
+ // )
246
+ // }
247
+ // }
248
+
249
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
250
+ if (super.onOptionsItemSelected(item)) {
251
+ return true
252
+ }
253
+
254
+ return when (item.itemId) {
255
+ R.id.settings -> {
256
+ /* TODO: this should be stripped out */
257
+ /* userSettings.userSettingsPopUp().showAsDropDown(requireActivity().findViewById(R.id.settings), 0, 0, Gravity.END) */
258
+ true
259
+ }
260
+ R.id.search -> {
261
+ super.onOptionsItemSelected(item)
262
+ }
263
+
264
+ android.R.id.home -> {
265
+ menuSearch.collapseActionView()
266
+ true
267
+ }
268
+
269
+ // TODO: tts
270
+ // R.id.screen_reader -> {
271
+ // if (isScreenReaderVisible) {
272
+ // closeScreenReaderFragment()
273
+ // } else {
274
+ // showScreenReaderFragment()
275
+ // }
276
+ // true
277
+ // }
278
+ else -> false
279
+ }
280
+ }
281
+
282
+ override fun onTap(point: PointF): Boolean {
283
+ requireActivity().toggleSystemUi()
284
+ return true
285
+ }
286
+ // TODO: search
287
+ // private fun showSearchFragment() {
288
+ // childFragmentManager.commit {
289
+ // childFragmentManager.findFragmentByTag(SEARCH_FRAGMENT_TAG)?.let { remove(it) }
290
+ // add(R.id.fragment_reader_container, SearchFragment::class.java, Bundle(), SEARCH_FRAGMENT_TAG)
291
+ // hide(navigatorFragment)
292
+ // addToBackStack(SEARCH_FRAGMENT_TAG)
293
+ // }
294
+ // }
295
+
296
+ // TODO: tts
297
+ // private fun showScreenReaderFragment() {
298
+ // menuScreenReader.title = resources.getString(R.string.epubactivity_read_aloud_stop)
299
+ // isScreenReaderVisible = true
300
+ // val arguments = ScreenReaderContract.createArguments(navigator.currentLocator.value)
301
+ // childFragmentManager.commit {
302
+ // add(R.id.fragment_reader_container, ScreenReaderFragment::class.java, arguments)
303
+ // hide(navigatorFragment)
304
+ // addToBackStack(null)
305
+ // }
306
+ // }
307
+ //
308
+ // private fun closeScreenReaderFragment() {
309
+ // menuScreenReader.title = resources.getString(R.string.epubactivity_read_aloud_start)
310
+ // isScreenReaderVisible = false
311
+ // childFragmentManager.popBackStack()
312
+ // }
313
+
314
+ companion object {
315
+
316
+ private const val BASE_URL_ARG = "baseUrl"
317
+
318
+ private const val SEARCH_FRAGMENT_TAG = "search"
319
+
320
+ private const val IS_SCREEN_READER_VISIBLE_KEY = "isScreenReaderVisible"
321
+
322
+ private const val IS_SEARCH_VIEW_ICONIFIED = "isSearchViewIconified"
323
+
324
+ fun newInstance(baseUrl: URL): EpubReaderFragment {
325
+ return EpubReaderFragment().apply {
326
+ arguments = Bundle().apply {
327
+ putString(BASE_URL_ARG, baseUrl.toString())
328
+ }
329
+ }
330
+ }
331
+ }
332
+ }
333
+
334
+ /* TODO: remove */
335
+ // /**
336
+ // * Example of an HTML template for a custom Decoration Style.
337
+ // *
338
+ // * This one will display a tinted "pen" icon in the page margin to show that a highlight has an
339
+ // * associated note.
340
+ // */
341
+ // @OptIn(ExperimentalDecorator::class)
342
+ // private fun annotationMarkTemplate(context: Context, @ColorInt defaultTint: Int = Color.YELLOW): HtmlDecorationTemplate {
343
+ // // Converts the pen icon to a base 64 data URL, to be embedded in the decoration stylesheet.
344
+ // // Alternatively, serve the image with the local HTTP server and use its URL.
345
+ // val imageUrl = ContextCompat.getDrawable(context, R.drawable.ic_baseline_edit_24)
346
+ // ?.toBitmap()?.toDataUrl()
347
+ // requireNotNull(imageUrl)
348
+ //
349
+ // val className = "testapp-annotation-mark"
350
+ // return HtmlDecorationTemplate(
351
+ // layout = HtmlDecorationTemplate.Layout.BOUNDS,
352
+ // width = HtmlDecorationTemplate.Width.PAGE,
353
+ // element = { decoration ->
354
+ // val style = decoration.style as? DecorationStyleAnnotationMark
355
+ // val tint = style?.tint ?: defaultTint
356
+ // // Using `data-activable=1` prevents the whole decoration container from being
357
+ // // clickable. Only the icon will respond to activation events.
358
+ // """
359
+ // <div><div data-activable="1" class="$className" style="background-color: ${tint.toCss()} !important"/></div>"
360
+ // """
361
+ // },
362
+ // stylesheet = """
363
+ // .$className {
364
+ // float: left;
365
+ // margin-left: 8px;
366
+ // width: 30px;
367
+ // height: 30px;
368
+ // border-radius: 50%;
369
+ // background: url('$imageUrl') no-repeat center;
370
+ // background-size: auto 50%;
371
+ // opacity: 0.8;
372
+ // }
373
+ // """
374
+ // )
375
+ // }