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.
- package/LICENSE +21 -0
- package/README.md +135 -0
- package/android/build.gradle +198 -0
- package/android/gradle.properties +3 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/com/reactnativereadium/ReadiumPackage.kt +17 -0
- package/android/src/main/java/com/reactnativereadium/ReadiumView.kt +84 -0
- package/android/src/main/java/com/reactnativereadium/ReadiumViewManager.kt +80 -0
- package/android/src/main/java/com/reactnativereadium/epub/UserSettings.kt +236 -0
- package/android/src/main/java/com/reactnativereadium/reader/BaseReaderFragment.kt +81 -0
- package/android/src/main/java/com/reactnativereadium/reader/EpubReaderFragment.kt +375 -0
- package/android/src/main/java/com/reactnativereadium/reader/ImageReaderFragment.kt +68 -0
- package/android/src/main/java/com/reactnativereadium/reader/PdfReaderFragment.kt +82 -0
- package/android/src/main/java/com/reactnativereadium/reader/ReaderService.kt +116 -0
- package/android/src/main/java/com/reactnativereadium/reader/ReaderViewModel.kt +120 -0
- package/android/src/main/java/com/reactnativereadium/reader/VisualReaderFragment.kt +78 -0
- package/android/src/main/java/com/reactnativereadium/search/SearchFragment.kt +100 -0
- package/android/src/main/java/com/reactnativereadium/search/SearchPagingSource.kt +44 -0
- package/android/src/main/java/com/reactnativereadium/search/SearchResultAdapter.kt +68 -0
- package/android/src/main/java/com/reactnativereadium/utils/ContentResolverUtil.kt +156 -0
- package/android/src/main/java/com/reactnativereadium/utils/Dimensions.kt +6 -0
- package/android/src/main/java/com/reactnativereadium/utils/EventChannel.kt +61 -0
- package/android/src/main/java/com/reactnativereadium/utils/FragmentFactory.kt +46 -0
- package/android/src/main/java/com/reactnativereadium/utils/R2DispatcherActivity.kt +45 -0
- package/android/src/main/java/com/reactnativereadium/utils/SectionDecoration.kt +98 -0
- package/android/src/main/java/com/reactnativereadium/utils/SingleClickListener.kt +32 -0
- package/android/src/main/java/com/reactnativereadium/utils/SystemUiManagement.kt +63 -0
- package/android/src/main/java/com/reactnativereadium/utils/extensions/Bitmap.kt +23 -0
- package/android/src/main/java/com/reactnativereadium/utils/extensions/Context.kt +16 -0
- package/android/src/main/java/com/reactnativereadium/utils/extensions/File.kt +22 -0
- package/android/src/main/java/com/reactnativereadium/utils/extensions/InputStream.kt +23 -0
- package/android/src/main/java/com/reactnativereadium/utils/extensions/Link.kt +6 -0
- package/android/src/main/java/com/reactnativereadium/utils/extensions/Metadata.kt +6 -0
- package/android/src/main/java/com/reactnativereadium/utils/extensions/URL.kt +29 -0
- package/android/src/main/java/com/reactnativereadium/utils/extensions/Uri.kt +17 -0
- package/android/src/main/res/drawable/background_action_mode.xml +6 -0
- package/android/src/main/res/drawable/cnl.png +0 -0
- package/android/src/main/res/drawable/cover.png +0 -0
- package/android/src/main/res/drawable/ic_add_white_24dp.xml +9 -0
- package/android/src/main/res/drawable/ic_baseline_arrow_forward_24.xml +5 -0
- package/android/src/main/res/drawable/ic_baseline_bookmark_24.xml +10 -0
- package/android/src/main/res/drawable/ic_baseline_delete_24.xml +10 -0
- package/android/src/main/res/drawable/ic_baseline_edit_24.xml +10 -0
- package/android/src/main/res/drawable/ic_baseline_enhanced_encryption_24.xml +10 -0
- package/android/src/main/res/drawable/ic_baseline_fast_forward_24.xml +10 -0
- package/android/src/main/res/drawable/ic_baseline_fast_rewind_24.xml +10 -0
- package/android/src/main/res/drawable/ic_baseline_headphones_24.xml +10 -0
- package/android/src/main/res/drawable/ic_baseline_pause_24.xml +10 -0
- package/android/src/main/res/drawable/ic_baseline_play_arrow_24.xml +10 -0
- package/android/src/main/res/drawable/ic_baseline_search_24.xml +10 -0
- package/android/src/main/res/drawable/ic_baseline_skip_next_24.xml +10 -0
- package/android/src/main/res/drawable/ic_baseline_skip_previous_24.xml +10 -0
- package/android/src/main/res/drawable/ic_dashboard_black_24dp.xml +9 -0
- package/android/src/main/res/drawable/ic_fastforward_30.xml +7 -0
- package/android/src/main/res/drawable/ic_info_black_24dp.xml +9 -0
- package/android/src/main/res/drawable/ic_local_library_black_24dp.xml +9 -0
- package/android/src/main/res/drawable/ic_notch.xml +4 -0
- package/android/src/main/res/drawable/ic_outline_add_24.xml +10 -0
- package/android/src/main/res/drawable/ic_outline_format_align_justify_24.xml +10 -0
- package/android/src/main/res/drawable/ic_outline_format_align_left_24.xml +11 -0
- package/android/src/main/res/drawable/ic_outline_format_size_24.xml +10 -0
- package/android/src/main/res/drawable/ic_outline_light_mode_24.xml +10 -0
- package/android/src/main/res/drawable/ic_outline_menu_24.xml +10 -0
- package/android/src/main/res/drawable/ic_outline_remove_24.xml +10 -0
- package/android/src/main/res/drawable/ic_outline_wb_sunny_24.xml +10 -0
- package/android/src/main/res/drawable/ic_rewind_30.xml +7 -0
- package/android/src/main/res/drawable/icon_font_decrease.png +0 -0
- package/android/src/main/res/drawable/icon_font_increase.png +0 -0
- package/android/src/main/res/drawable/icon_overflow.png +0 -0
- package/android/src/main/res/drawable/rbtn_selector.xml +25 -0
- package/android/src/main/res/drawable/rbtn_textcolor_selector.xml +16 -0
- package/android/src/main/res/drawable/repfr.png +0 -0
- package/android/src/main/res/drawable/selector_blue.xml +42 -0
- package/android/src/main/res/drawable/selector_green.xml +42 -0
- package/android/src/main/res/drawable/selector_purple.xml +42 -0
- package/android/src/main/res/drawable/selector_red.xml +42 -0
- package/android/src/main/res/drawable/selector_yellow.xml +41 -0
- package/android/src/main/res/layout/activity_epub.xml +23 -0
- package/android/src/main/res/layout/activity_main.xml +32 -0
- package/android/src/main/res/layout/activity_reader.xml +6 -0
- package/android/src/main/res/layout/add_catalog_dialog.xml +25 -0
- package/android/src/main/res/layout/filter_row.xml +34 -0
- package/android/src/main/res/layout/filter_window.xml +24 -0
- package/android/src/main/res/layout/fragment_about.xml +150 -0
- package/android/src/main/res/layout/fragment_audiobook.xml +151 -0
- package/android/src/main/res/layout/fragment_bookshelf.xml +35 -0
- package/android/src/main/res/layout/fragment_catalog.xml +56 -0
- package/android/src/main/res/layout/fragment_catalog_feed_list.xml +27 -0
- package/android/src/main/res/layout/fragment_drm_management.xml +284 -0
- package/android/src/main/res/layout/fragment_listview.xml +24 -0
- package/android/src/main/res/layout/fragment_outline.xml +31 -0
- package/android/src/main/res/layout/fragment_publication_detail.xml +55 -0
- package/android/src/main/res/layout/fragment_reader.xml +6 -0
- package/android/src/main/res/layout/fragment_screen_reader.xml +143 -0
- package/android/src/main/res/layout/fragment_search.xml +39 -0
- package/android/src/main/res/layout/item_group_view.xml +22 -0
- package/android/src/main/res/layout/item_recycle_book.xml +55 -0
- package/android/src/main/res/layout/item_recycle_bookmark.xml +72 -0
- package/android/src/main/res/layout/item_recycle_button.xml +23 -0
- package/android/src/main/res/layout/item_recycle_catalog.xml +56 -0
- package/android/src/main/res/layout/item_recycle_highlight.xml +85 -0
- package/android/src/main/res/layout/item_recycle_horizontal.xml +45 -0
- package/android/src/main/res/layout/item_recycle_navigation.xml +34 -0
- package/android/src/main/res/layout/item_recycle_search.xml +14 -0
- package/android/src/main/res/layout/item_spinner_days.xml +19 -0
- package/android/src/main/res/layout/my_fragment.xml +13 -0
- package/android/src/main/res/layout/popup_delete.xml +29 -0
- package/android/src/main/res/layout/popup_note.xml +105 -0
- package/android/src/main/res/layout/popup_passphrase.xml +126 -0
- package/android/src/main/res/layout/popup_window_user_settings.xml +576 -0
- package/android/src/main/res/layout/section_header.xml +25 -0
- package/android/src/main/res/layout/view_action_mode.xml +100 -0
- package/android/src/main/res/layout/view_action_mode_reverse.xml +99 -0
- package/android/src/main/res/menu/bottom_nav_menu.xml +19 -0
- package/android/src/main/res/menu/menu_action_mode.xml +26 -0
- package/android/src/main/res/menu/menu_bookmark.xml +18 -0
- package/android/src/main/res/menu/menu_epub.xml +41 -0
- package/android/src/main/res/menu/menu_filter.xml +21 -0
- package/android/src/main/res/menu/menu_reader.xml +33 -0
- package/android/src/main/res/navigation/navigation.xml +46 -0
- package/android/src/main/res/values/arrays.xml +18 -0
- package/android/src/main/res/values/colors.xml +22 -0
- package/android/src/main/res/values/refs.xml +5 -0
- package/android/src/main/res/values/strings.xml +200 -0
- package/android/src/main/res/values/styles.xml +46 -0
- package/android/src/main/res/xml/network_security_config.xml +38 -0
- package/ios/App/AppModule.swift +62 -0
- package/ios/Common/Paths.swift +52 -0
- package/ios/Common/Publication.swift +15 -0
- package/ios/Common/Toolkit/Extensions/AnyPublisher.swift +14 -0
- package/ios/Common/Toolkit/Extensions/Future.swift +16 -0
- package/ios/Common/Toolkit/Extensions/HTTPClient.swift +65 -0
- package/ios/Common/Toolkit/Extensions/Locator.swift +14 -0
- package/ios/Common/Toolkit/Extensions/String.swift +16 -0
- package/ios/Common/Toolkit/Extensions/UIImage.swift +12 -0
- package/ios/Common/Toolkit/Extensions/UIViewController.swift +19 -0
- package/ios/Common/Toolkit/ScreenOrientation.swift +13 -0
- package/ios/Data/Bookmark.swift +23 -0
- package/ios/Reader/Common/ReaderViewController.swift +309 -0
- package/ios/Reader/EPUB/AssociatedColors.swift +27 -0
- package/ios/Reader/EPUB/EPUBModule.swift +38 -0
- package/ios/Reader/EPUB/EPUBViewController.swift +79 -0
- package/ios/Reader/ReaderError.swift +25 -0
- package/ios/Reader/ReaderFormatModule.swift +27 -0
- package/ios/Reader/ReaderModule.swift +84 -0
- package/ios/Reader/ReaderService.swift +126 -0
- package/ios/Readium-Bridging-Header.h +2 -0
- package/ios/Readium.xcodeproj/project.pbxproj +293 -0
- package/ios/ReadiumView.swift +156 -0
- package/ios/ReadiumViewManager.m +10 -0
- package/ios/ReadiumViewManager.swift +9 -0
- package/lib/commonjs/enums/Appearance.js +16 -0
- package/lib/commonjs/enums/Appearance.js.map +1 -0
- package/lib/commonjs/enums/ColumnCount.js +16 -0
- package/lib/commonjs/enums/ColumnCount.js.map +1 -0
- package/lib/commonjs/enums/FontFamily.js +21 -0
- package/lib/commonjs/enums/FontFamily.js.map +1 -0
- package/lib/commonjs/enums/TextAlignment.js +15 -0
- package/lib/commonjs/enums/TextAlignment.js.map +1 -0
- package/lib/commonjs/enums/index.js +58 -0
- package/lib/commonjs/enums/index.js.map +1 -0
- package/lib/commonjs/index.js +105 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/interfaces/Dimensions.js +2 -0
- package/lib/commonjs/interfaces/Dimensions.js.map +1 -0
- package/lib/commonjs/interfaces/File.js +6 -0
- package/lib/commonjs/interfaces/File.js.map +1 -0
- package/lib/commonjs/interfaces/Locator.js +2 -0
- package/lib/commonjs/interfaces/Locator.js.map +1 -0
- package/lib/commonjs/interfaces/Settings.js +71 -0
- package/lib/commonjs/interfaces/Settings.js.map +1 -0
- package/lib/commonjs/interfaces/index.js +58 -0
- package/lib/commonjs/interfaces/index.js.map +1 -0
- package/lib/commonjs/utils/index.js +30 -0
- package/lib/commonjs/utils/index.js.map +1 -0
- package/lib/module/enums/Appearance.js +9 -0
- package/lib/module/enums/Appearance.js.map +1 -0
- package/lib/module/enums/ColumnCount.js +9 -0
- package/lib/module/enums/ColumnCount.js.map +1 -0
- package/lib/module/enums/FontFamily.js +14 -0
- package/lib/module/enums/FontFamily.js.map +1 -0
- package/lib/module/enums/TextAlignment.js +8 -0
- package/lib/module/enums/TextAlignment.js.map +1 -0
- package/lib/module/enums/index.js +5 -0
- package/lib/module/enums/index.js.map +1 -0
- package/lib/module/index.js +61 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/interfaces/Dimensions.js +2 -0
- package/lib/module/interfaces/Dimensions.js.map +1 -0
- package/lib/module/interfaces/File.js +2 -0
- package/lib/module/interfaces/File.js.map +1 -0
- package/lib/module/interfaces/Locator.js +2 -0
- package/lib/module/interfaces/Locator.js.map +1 -0
- package/lib/module/interfaces/Settings.js +61 -0
- package/lib/module/interfaces/Settings.js.map +1 -0
- package/lib/module/interfaces/index.js +5 -0
- package/lib/module/interfaces/index.js.map +1 -0
- package/lib/module/utils/index.js +17 -0
- package/lib/module/utils/index.js.map +1 -0
- package/lib/typescript/enums/Appearance.d.ts +11 -0
- package/lib/typescript/enums/ColumnCount.d.ts +5 -0
- package/lib/typescript/enums/FontFamily.d.ts +10 -0
- package/lib/typescript/enums/TextAlignment.d.ts +4 -0
- package/lib/typescript/enums/index.d.ts +4 -0
- package/lib/typescript/index.d.ts +17 -0
- package/lib/typescript/interfaces/Dimensions.d.ts +4 -0
- package/lib/typescript/interfaces/File.d.ts +11 -0
- package/lib/typescript/interfaces/Locator.d.ts +14 -0
- package/lib/typescript/interfaces/Settings.d.ts +40 -0
- package/lib/typescript/interfaces/index.d.ts +4 -0
- package/lib/typescript/utils/index.d.ts +10 -0
- package/package.json +160 -0
- package/react-native-readium.podspec +25 -0
- package/src/enums/Appearance.ts +14 -0
- package/src/enums/ColumnCount.ts +6 -0
- package/src/enums/FontFamily.ts +11 -0
- package/src/enums/TextAlignment.ts +5 -0
- package/src/enums/index.ts +4 -0
- package/src/index.tsx +78 -0
- package/src/interfaces/Dimensions.ts +4 -0
- package/src/interfaces/File.ts +14 -0
- package/src/interfaces/Locator.ts +14 -0
- package/src/interfaces/Settings.ts +85 -0
- package/src/interfaces/index.ts +4 -0
- 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
|
+
}
|