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,156 @@
|
|
|
1
|
+
package com.reactnativereadium.utils
|
|
2
|
+
|
|
3
|
+
import android.content.ContentUris
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.net.Uri
|
|
6
|
+
import android.provider.DocumentsContract
|
|
7
|
+
import android.provider.MediaStore
|
|
8
|
+
import android.text.TextUtils
|
|
9
|
+
import kotlinx.coroutines.Dispatchers
|
|
10
|
+
import kotlinx.coroutines.withContext
|
|
11
|
+
import com.reactnativereadium.utils.extensions.toFile
|
|
12
|
+
import java.io.File
|
|
13
|
+
import java.io.FileNotFoundException
|
|
14
|
+
import java.io.InputStream
|
|
15
|
+
import java.net.URL
|
|
16
|
+
|
|
17
|
+
object ContentResolverUtil {
|
|
18
|
+
|
|
19
|
+
suspend fun getContentInputStream(context: Context, uri: Uri, publicationPath: String) {
|
|
20
|
+
withContext(Dispatchers.IO) {
|
|
21
|
+
try {
|
|
22
|
+
val path = getRealPath(context, uri)
|
|
23
|
+
if (path != null) {
|
|
24
|
+
File(path).copyTo(File(publicationPath))
|
|
25
|
+
} else {
|
|
26
|
+
val input = URL(uri.toString()).openStream()
|
|
27
|
+
input.toFile(publicationPath)
|
|
28
|
+
}
|
|
29
|
+
} catch (e: Exception) {
|
|
30
|
+
val input = getInputStream(context, uri)
|
|
31
|
+
input?.let {
|
|
32
|
+
input.toFile(publicationPath)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private fun getInputStream(context: Context, uri: Uri): InputStream? {
|
|
39
|
+
return try {
|
|
40
|
+
context.contentResolver.openInputStream(uri)
|
|
41
|
+
} catch (e: FileNotFoundException) {
|
|
42
|
+
e.printStackTrace()
|
|
43
|
+
null
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private fun getRealPath(context: Context, uri: Uri): String? {
|
|
48
|
+
// DocumentProvider
|
|
49
|
+
if (DocumentsContract.isDocumentUri(context, uri)) {
|
|
50
|
+
// ExternalStorageProvider
|
|
51
|
+
if (isExternalStorageDocument(uri)) {
|
|
52
|
+
val docId = DocumentsContract.getDocumentId(uri)
|
|
53
|
+
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
|
54
|
+
val type = split[0]
|
|
55
|
+
if ("primary".equals(type, ignoreCase = true)) {
|
|
56
|
+
return context.getExternalFilesDir(null).toString() + "/" + split[1]
|
|
57
|
+
}
|
|
58
|
+
// TODO handle non-primary volumes
|
|
59
|
+
|
|
60
|
+
} else if (isDownloadsDocument(uri)) {
|
|
61
|
+
val id = DocumentsContract.getDocumentId(uri)
|
|
62
|
+
if (!TextUtils.isEmpty(id)) {
|
|
63
|
+
if (id.startsWith("raw:")) {
|
|
64
|
+
return id.replaceFirst("raw:".toRegex(), "")
|
|
65
|
+
}
|
|
66
|
+
return try {
|
|
67
|
+
val contentUri = ContentUris.withAppendedId(
|
|
68
|
+
Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id))
|
|
69
|
+
getDataColumn(context, contentUri, null, null)
|
|
70
|
+
} catch (e: NumberFormatException) {
|
|
71
|
+
null
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
} else if (isMediaDocument(uri)) {
|
|
76
|
+
val docId = DocumentsContract.getDocumentId(uri)
|
|
77
|
+
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
|
78
|
+
val type = split[0]
|
|
79
|
+
|
|
80
|
+
var contentUri: Uri? = null
|
|
81
|
+
when (type) {
|
|
82
|
+
"image" -> contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
|
83
|
+
"video" -> contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
|
|
84
|
+
"audio" -> contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
val selection = "_id=?"
|
|
88
|
+
val selectionArgs = arrayOf(split[1])
|
|
89
|
+
|
|
90
|
+
return getDataColumn(context, contentUri, selection, selectionArgs)
|
|
91
|
+
}
|
|
92
|
+
} else if ("content".equals(uri.scheme!!, ignoreCase = true)) {
|
|
93
|
+
|
|
94
|
+
// Return the remote address
|
|
95
|
+
return getDataColumn(context, uri, null, null)
|
|
96
|
+
|
|
97
|
+
} else if ("file".equals(uri.scheme!!, ignoreCase = true)) {
|
|
98
|
+
return uri.path
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return null
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get the value of the data column for this Uri. This is useful for
|
|
106
|
+
* MediaStore Uris, and other file-based ContentProviders.
|
|
107
|
+
*
|
|
108
|
+
* @param context The context.
|
|
109
|
+
* @param uri The Uri to query.
|
|
110
|
+
* @param selection (Optional) Filter used in the query.
|
|
111
|
+
* @param selectionArgs (Optional) Selection arguments used in the query.
|
|
112
|
+
* @return The value of the _data column, which is typically a file path.
|
|
113
|
+
*/
|
|
114
|
+
private fun getDataColumn(context: Context, uri: Uri?, selection: String?,
|
|
115
|
+
selectionArgs: Array<String>?): String? {
|
|
116
|
+
|
|
117
|
+
val column = "_data"
|
|
118
|
+
val projection = arrayOf(column)
|
|
119
|
+
context.contentResolver.query(uri!!, projection, selection, selectionArgs, null).use { cursor ->
|
|
120
|
+
cursor?.let {
|
|
121
|
+
if (cursor.moveToFirst()) {
|
|
122
|
+
val index = cursor.getColumnIndexOrThrow(column)
|
|
123
|
+
return cursor.getString(index)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return null
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @param uri The Uri to check.
|
|
133
|
+
* @return Whether the Uri authority is ExternalStorageProvider.
|
|
134
|
+
*/
|
|
135
|
+
private fun isExternalStorageDocument(uri: Uri): Boolean {
|
|
136
|
+
return "com.android.externalstorage.documents" == uri.authority
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @param uri The Uri to check.
|
|
141
|
+
* @return Whether the Uri authority is DownloadsProvider.
|
|
142
|
+
*/
|
|
143
|
+
private fun isDownloadsDocument(uri: Uri): Boolean {
|
|
144
|
+
return "com.android.providers.downloads.documents" == uri.authority
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @param uri The Uri to check.
|
|
149
|
+
* @return Whether the Uri authority is MediaProvider.
|
|
150
|
+
*/
|
|
151
|
+
private fun isMediaDocument(uri: Uri): Boolean {
|
|
152
|
+
return "com.android.providers.media.documents" == uri.authority
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
package com.reactnativereadium.utils
|
|
2
|
+
|
|
3
|
+
import androidx.lifecycle.Lifecycle
|
|
4
|
+
import androidx.lifecycle.LifecycleObserver
|
|
5
|
+
import androidx.lifecycle.LifecycleOwner
|
|
6
|
+
import androidx.lifecycle.OnLifecycleEvent
|
|
7
|
+
import androidx.lifecycle.lifecycleScope
|
|
8
|
+
import kotlinx.coroutines.CoroutineScope
|
|
9
|
+
import kotlinx.coroutines.Job
|
|
10
|
+
import kotlinx.coroutines.channels.Channel
|
|
11
|
+
import kotlinx.coroutines.flow.Flow
|
|
12
|
+
import kotlinx.coroutines.flow.collect
|
|
13
|
+
import kotlinx.coroutines.flow.receiveAsFlow
|
|
14
|
+
import kotlinx.coroutines.launch
|
|
15
|
+
|
|
16
|
+
class EventChannel<T>(private val channel: Channel<T>, private val sendScope: CoroutineScope) {
|
|
17
|
+
|
|
18
|
+
fun send(event: T) {
|
|
19
|
+
sendScope.launch {
|
|
20
|
+
channel.send(event)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
fun receive(lifecycleOwner: LifecycleOwner, callback: suspend (T) -> Unit) {
|
|
25
|
+
val observer = FlowObserver(lifecycleOwner, channel.receiveAsFlow(), callback)
|
|
26
|
+
lifecycleOwner.lifecycle.addObserver(observer)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
class FlowObserver<T> (
|
|
31
|
+
private val lifecycleOwner: LifecycleOwner,
|
|
32
|
+
private val flow: Flow<T>,
|
|
33
|
+
private val collector: suspend (T) -> Unit
|
|
34
|
+
) : LifecycleObserver {
|
|
35
|
+
|
|
36
|
+
private var job: Job? = null
|
|
37
|
+
|
|
38
|
+
@OnLifecycleEvent(Lifecycle.Event.ON_START)
|
|
39
|
+
fun onStart() {
|
|
40
|
+
if (job == null) {
|
|
41
|
+
job = lifecycleOwner.lifecycleScope.launch {
|
|
42
|
+
flow.collect { collector(it) }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
|
|
48
|
+
fun onStop() {
|
|
49
|
+
job?.cancel()
|
|
50
|
+
job = null
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
inline fun <reified T> Flow<T>.observeWhenStarted(
|
|
56
|
+
lifecycleOwner: LifecycleOwner,
|
|
57
|
+
noinline collector: suspend (T) -> Unit
|
|
58
|
+
) {
|
|
59
|
+
val observer = FlowObserver(lifecycleOwner, this, collector)
|
|
60
|
+
lifecycleOwner.lifecycle.addObserver(observer)
|
|
61
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
package com.reactnativereadium.utils
|
|
2
|
+
|
|
3
|
+
import androidx.fragment.app.Fragment
|
|
4
|
+
import androidx.fragment.app.FragmentFactory
|
|
5
|
+
import org.readium.r2.shared.extensions.tryOrNull
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates a [FragmentFactory] for a single type of [Fragment] using the result of the given
|
|
9
|
+
* [factory] closure.
|
|
10
|
+
*/
|
|
11
|
+
inline fun <reified T : Fragment> createFragmentFactory(crossinline factory: () -> T): FragmentFactory = object : FragmentFactory() {
|
|
12
|
+
|
|
13
|
+
override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
|
|
14
|
+
return when (className) {
|
|
15
|
+
T::class.java.name -> factory()
|
|
16
|
+
else -> super.instantiate(classLoader, className)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A [FragmentFactory] which will iterate over a provided list of [factories] until finding one
|
|
24
|
+
* instantiating successfully the requested [Fragment].
|
|
25
|
+
*
|
|
26
|
+
* ```
|
|
27
|
+
* supportFragmentManager.fragmentFactory = CompositeFragmentFactory(
|
|
28
|
+
* EpubNavigatorFragment.createFactory(publication, baseUrl, initialLocator, this),
|
|
29
|
+
* PdfNavigatorFragment.createFactory(publication, initialLocator, this)
|
|
30
|
+
* )
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
class CompositeFragmentFactory(private val factories: List<FragmentFactory>) : FragmentFactory() {
|
|
34
|
+
|
|
35
|
+
constructor(vararg factories: FragmentFactory) : this(factories.toList())
|
|
36
|
+
|
|
37
|
+
override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
|
|
38
|
+
for (factory in factories) {
|
|
39
|
+
tryOrNull { factory.instantiate(classLoader, className) }
|
|
40
|
+
?.let { return it }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return super.instantiate(classLoader, className)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
package com.reactnativereadium.utils
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.content.Intent
|
|
5
|
+
import android.net.Uri
|
|
6
|
+
import android.os.Bundle
|
|
7
|
+
//import com.reactnativereadium.MainActivity
|
|
8
|
+
import timber.log.Timber
|
|
9
|
+
|
|
10
|
+
class R2DispatcherActivity : Activity() {
|
|
11
|
+
|
|
12
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
13
|
+
super.onCreate(savedInstanceState)
|
|
14
|
+
dispatchIntent(intent)
|
|
15
|
+
finish()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private fun dispatchIntent(intent: Intent) {
|
|
19
|
+
val uri = uriFromIntent(intent)
|
|
20
|
+
?: run {
|
|
21
|
+
Timber.d("Got an empty intent.")
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
// FIXME: MainActivity
|
|
25
|
+
// val newIntent = Intent(this, MainActivity::class.java).apply {
|
|
26
|
+
// addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
|
27
|
+
// data = uri
|
|
28
|
+
// }
|
|
29
|
+
// startActivity(newIntent)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private fun uriFromIntent(intent: Intent): Uri? =
|
|
33
|
+
when (intent.action) {
|
|
34
|
+
Intent.ACTION_SEND -> {
|
|
35
|
+
if ("text/plain" == intent.type) {
|
|
36
|
+
intent.getStringExtra(Intent.EXTRA_TEXT).let { Uri.parse(it) }
|
|
37
|
+
} else {
|
|
38
|
+
intent.getParcelableExtra(Intent.EXTRA_STREAM)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else -> {
|
|
42
|
+
intent.data
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
package com.reactnativereadium.utils
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Canvas
|
|
5
|
+
import android.graphics.Rect
|
|
6
|
+
import android.view.LayoutInflater
|
|
7
|
+
import android.view.View
|
|
8
|
+
import android.view.ViewGroup
|
|
9
|
+
import android.widget.TextView
|
|
10
|
+
import androidx.core.view.children
|
|
11
|
+
import androidx.recyclerview.widget.RecyclerView
|
|
12
|
+
import androidx.recyclerview.widget.RecyclerView.NO_POSITION
|
|
13
|
+
/* import com.reactnativereadium.databinding.SectionHeaderBinding */
|
|
14
|
+
|
|
15
|
+
class SectionDecoration(
|
|
16
|
+
private val context: Context,
|
|
17
|
+
private val listener: Listener
|
|
18
|
+
) : RecyclerView.ItemDecoration() {
|
|
19
|
+
|
|
20
|
+
interface Listener {
|
|
21
|
+
fun isStartOfSection(itemPos: Int): Boolean
|
|
22
|
+
fun sectionTitle(itemPos: Int): String
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private lateinit var headerView: View
|
|
26
|
+
private lateinit var sectionTitleView: TextView
|
|
27
|
+
|
|
28
|
+
override fun getItemOffsets(
|
|
29
|
+
outRect: Rect,
|
|
30
|
+
view: View,
|
|
31
|
+
parent: RecyclerView,
|
|
32
|
+
state: RecyclerView.State
|
|
33
|
+
) {
|
|
34
|
+
super.getItemOffsets(outRect, view, parent, state)
|
|
35
|
+
val pos = parent.getChildAdapterPosition(view)
|
|
36
|
+
initHeaderViewIfNeeded(parent)
|
|
37
|
+
if (listener.sectionTitle(pos) != "" && listener.isStartOfSection(pos)) {
|
|
38
|
+
sectionTitleView.text = listener.sectionTitle(pos)
|
|
39
|
+
fixLayoutSize(headerView, parent)
|
|
40
|
+
outRect.top = headerView.height
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
|
45
|
+
super.onDrawOver(c, parent, state)
|
|
46
|
+
initHeaderViewIfNeeded(parent)
|
|
47
|
+
|
|
48
|
+
val children = parent.children.toList()
|
|
49
|
+
children.forEach { child ->
|
|
50
|
+
val pos = parent.getChildAdapterPosition(child)
|
|
51
|
+
if (pos != NO_POSITION && listener.sectionTitle(pos) != "" &&
|
|
52
|
+
(listener.isStartOfSection(pos) || isTopChild(child, children))) {
|
|
53
|
+
sectionTitleView.text = listener.sectionTitle(pos)
|
|
54
|
+
fixLayoutSize(headerView, parent)
|
|
55
|
+
drawHeader(c, child, headerView)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private fun initHeaderViewIfNeeded(parent: RecyclerView) {
|
|
61
|
+
if (::headerView.isInitialized) return
|
|
62
|
+
/* FIXME: databinding.SectionHeaderBinding */
|
|
63
|
+
/* SectionHeaderBinding.inflate(
|
|
64
|
+
LayoutInflater.from(context),
|
|
65
|
+
parent,
|
|
66
|
+
false
|
|
67
|
+
).apply {
|
|
68
|
+
headerView = root
|
|
69
|
+
sectionTitleView = header
|
|
70
|
+
} */
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private fun fixLayoutSize(v: View, parent: ViewGroup) {
|
|
74
|
+
val widthSpec = View.MeasureSpec.makeMeasureSpec(parent.width, View.MeasureSpec.EXACTLY)
|
|
75
|
+
val heightSpec = View.MeasureSpec.makeMeasureSpec(parent.height, View.MeasureSpec.UNSPECIFIED)
|
|
76
|
+
val childWidth = ViewGroup.getChildMeasureSpec(widthSpec, parent.paddingStart + parent.paddingEnd, v.layoutParams.width)
|
|
77
|
+
val childHeight = ViewGroup.getChildMeasureSpec(heightSpec, parent.paddingTop + parent.paddingBottom, v.layoutParams.height)
|
|
78
|
+
v.measure(childWidth, childHeight)
|
|
79
|
+
v.layout(0, 0, v.measuredWidth, v.measuredHeight)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private fun drawHeader(c: Canvas, child: View, headerView: View) {
|
|
83
|
+
c.run {
|
|
84
|
+
save()
|
|
85
|
+
translate(0F, maxOf(0, child.top - headerView.height).toFloat())
|
|
86
|
+
headerView.draw(this)
|
|
87
|
+
restore()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private fun isTopChild(child: View, children: List<View>): Boolean {
|
|
92
|
+
var tmp = child.top
|
|
93
|
+
children.forEach { c ->
|
|
94
|
+
tmp = minOf(c.top, tmp)
|
|
95
|
+
}
|
|
96
|
+
return child.top == tmp
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
package com.reactnativereadium.utils
|
|
2
|
+
|
|
3
|
+
import android.view.View
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Prevents from double clicks on a view, which could otherwise lead to unpredictable states. Useful
|
|
8
|
+
* while transitioning to another activity for instance.
|
|
9
|
+
*/
|
|
10
|
+
class SingleClickListener(private val click: (v: View) -> Unit) : View.OnClickListener {
|
|
11
|
+
|
|
12
|
+
companion object {
|
|
13
|
+
private const val DOUBLE_CLICK_TIMEOUT = 2500
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
private var lastClick: Long = 0
|
|
17
|
+
|
|
18
|
+
override fun onClick(v: View) {
|
|
19
|
+
if (getLastClickTimeout() > DOUBLE_CLICK_TIMEOUT) {
|
|
20
|
+
lastClick = System.currentTimeMillis()
|
|
21
|
+
click(v)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private fun getLastClickTimeout(): Long {
|
|
26
|
+
return System.currentTimeMillis() - lastClick
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fun View.singleClick(l: (View) -> Unit) {
|
|
31
|
+
setOnClickListener(SingleClickListener(l))
|
|
32
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
package com.reactnativereadium.utils
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.view.View
|
|
5
|
+
import android.view.WindowInsets
|
|
6
|
+
import androidx.core.view.WindowInsetsCompat
|
|
7
|
+
|
|
8
|
+
// Using ViewCompat and WindowInsetsCompat does not work properly in all versions of Android
|
|
9
|
+
@Suppress("DEPRECATION")
|
|
10
|
+
/** Returns `true` if fullscreen or immersive mode is not set. */
|
|
11
|
+
private fun Activity.isSystemUiVisible(): Boolean {
|
|
12
|
+
return this.window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Using ViewCompat and WindowInsetsCompat does not work properly in all versions of Android
|
|
16
|
+
@Suppress("DEPRECATION")
|
|
17
|
+
/** Enable fullscreen or immersive mode. */
|
|
18
|
+
fun Activity.hideSystemUi() {
|
|
19
|
+
this.window.decorView.systemUiVisibility = (
|
|
20
|
+
View.SYSTEM_UI_FLAG_IMMERSIVE
|
|
21
|
+
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
|
22
|
+
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
|
23
|
+
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
|
24
|
+
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
|
25
|
+
or View.SYSTEM_UI_FLAG_FULLSCREEN
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Using ViewCompat and WindowInsetsCompat does not work properly in all versions of Android
|
|
30
|
+
@Suppress("DEPRECATION")
|
|
31
|
+
/** Disable fullscreen or immersive mode. */
|
|
32
|
+
fun Activity.showSystemUi() {
|
|
33
|
+
this.window.decorView.systemUiVisibility = (
|
|
34
|
+
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
|
35
|
+
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
|
36
|
+
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Toggle fullscreen or immersive mode. */
|
|
41
|
+
fun Activity.toggleSystemUi() {
|
|
42
|
+
if (this.isSystemUiVisible()) {
|
|
43
|
+
this.hideSystemUi()
|
|
44
|
+
} else {
|
|
45
|
+
this.showSystemUi()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Set padding around view so that content doesn't overlap system UI */
|
|
50
|
+
fun View.padSystemUi(insets: WindowInsets, activity: Activity) =
|
|
51
|
+
WindowInsetsCompat.toWindowInsetsCompat(insets, this)
|
|
52
|
+
.getInsets(WindowInsetsCompat.Type.systemBars()).apply {
|
|
53
|
+
setPadding(
|
|
54
|
+
left,
|
|
55
|
+
top,
|
|
56
|
+
right,
|
|
57
|
+
bottom
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Clear padding around view */
|
|
62
|
+
fun View.clearPadding() =
|
|
63
|
+
setPadding(0, 0, 0, 0)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
package com.reactnativereadium.utils.extensions
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import android.util.Base64
|
|
5
|
+
import timber.log.Timber
|
|
6
|
+
import java.io.ByteArrayOutputStream
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Converts the receiver bitmap into a data URL ready to be used in HTML or CSS.
|
|
10
|
+
*
|
|
11
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
|
|
12
|
+
*/
|
|
13
|
+
fun Bitmap.toDataUrl(): String? =
|
|
14
|
+
try {
|
|
15
|
+
val stream = ByteArrayOutputStream()
|
|
16
|
+
compress(Bitmap.CompressFormat.PNG, 100, stream)
|
|
17
|
+
.also { success -> if (!success) throw Exception("Can't compress image to PNG") }
|
|
18
|
+
val b64 = Base64.encodeToString(stream.toByteArray(), Base64.DEFAULT)
|
|
19
|
+
"data:image/png;base64,$b64"
|
|
20
|
+
} catch (e: Exception) {
|
|
21
|
+
Timber.e(e)
|
|
22
|
+
null
|
|
23
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
package com.reactnativereadium.utils.extensions
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import androidx.annotation.ColorInt
|
|
5
|
+
import androidx.annotation.ColorRes
|
|
6
|
+
import androidx.core.content.ContextCompat
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Extensions
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
@ColorInt
|
|
14
|
+
fun Context.color(@ColorRes id: Int): Int {
|
|
15
|
+
return ContextCompat.getColor(this, id)
|
|
16
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
package com.reactnativereadium.utils.extensions
|
|
2
|
+
|
|
3
|
+
import kotlinx.coroutines.Dispatchers
|
|
4
|
+
import kotlinx.coroutines.withContext
|
|
5
|
+
import java.io.File
|
|
6
|
+
import java.io.FileFilter
|
|
7
|
+
import java.io.IOException
|
|
8
|
+
|
|
9
|
+
suspend fun File.moveTo(target: File) = withContext(Dispatchers.IO) {
|
|
10
|
+
if (!this@moveTo.renameTo(target))
|
|
11
|
+
throw IOException()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* As there are cases where [File.listFiles] returns null even though it is a directory, we return
|
|
17
|
+
* an empty list instead.
|
|
18
|
+
*/
|
|
19
|
+
fun File.listFilesSafely(filter: FileFilter? = null): List<File> {
|
|
20
|
+
val array: Array<File>? = if (filter == null) listFiles() else listFiles(filter)
|
|
21
|
+
return array?.toList() ?: emptyList()
|
|
22
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
package com.reactnativereadium.utils.extensions
|
|
2
|
+
|
|
3
|
+
import kotlinx.coroutines.Dispatchers
|
|
4
|
+
import kotlinx.coroutines.withContext
|
|
5
|
+
import org.readium.r2.shared.extensions.tryOrNull
|
|
6
|
+
import java.io.File
|
|
7
|
+
import java.io.InputStream
|
|
8
|
+
import java.util.*
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
suspend fun InputStream.toFile(path: String) {
|
|
12
|
+
withContext(Dispatchers.IO) {
|
|
13
|
+
use { input ->
|
|
14
|
+
File(path).outputStream().use { input.copyTo(it) }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
suspend fun InputStream.copyToTempFile(dir: String): File? = tryOrNull {
|
|
20
|
+
val filename = UUID.randomUUID().toString()
|
|
21
|
+
File(dir + filename)
|
|
22
|
+
.also { toFile(it.path) }
|
|
23
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
package com.reactnativereadium.utils.extensions
|
|
2
|
+
|
|
3
|
+
import kotlinx.coroutines.Dispatchers
|
|
4
|
+
import kotlinx.coroutines.withContext
|
|
5
|
+
import org.readium.r2.shared.extensions.extension
|
|
6
|
+
import org.readium.r2.shared.extensions.tryOr
|
|
7
|
+
import org.readium.r2.shared.extensions.tryOrNull
|
|
8
|
+
import java.io.File
|
|
9
|
+
import java.io.FileOutputStream
|
|
10
|
+
import java.net.URL
|
|
11
|
+
import java.util.*
|
|
12
|
+
|
|
13
|
+
suspend fun URL.download(path: String): File? = tryOr(null) {
|
|
14
|
+
val file = File(path)
|
|
15
|
+
withContext(Dispatchers.IO) {
|
|
16
|
+
openStream().use { input ->
|
|
17
|
+
FileOutputStream(file).use { output ->
|
|
18
|
+
input.copyTo(output)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
file
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
suspend fun URL.copyToTempFile(dir: String): File? = tryOrNull {
|
|
26
|
+
val filename = UUID.randomUUID().toString()
|
|
27
|
+
val path = "$dir$filename.$extension"
|
|
28
|
+
download(path)
|
|
29
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
package com.reactnativereadium.utils.extensions
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.net.Uri
|
|
5
|
+
import org.readium.r2.shared.extensions.tryOrNull
|
|
6
|
+
import org.readium.r2.shared.util.mediatype.MediaType
|
|
7
|
+
import com.reactnativereadium.utils.ContentResolverUtil
|
|
8
|
+
import java.io.File
|
|
9
|
+
import java.util.*
|
|
10
|
+
|
|
11
|
+
suspend fun Uri.copyToTempFile(context: Context, dir: String): File? = tryOrNull {
|
|
12
|
+
val filename = UUID.randomUUID().toString()
|
|
13
|
+
val mediaType = MediaType.ofUri(this, context.contentResolver)
|
|
14
|
+
val path = "$dir$filename.${mediaType?.fileExtension ?: "tmp"}"
|
|
15
|
+
ContentResolverUtil.getContentInputStream(context, this, path)
|
|
16
|
+
return@tryOrNull File(path)
|
|
17
|
+
}
|
|
Binary file
|
|
Binary file
|