🚀 Add new text selection actions (Share/Web)
Added new actions to text selection toolbar. Share action shares selected text to the app you want to. Web Search action searches selected text in your browser. Partly resolves: #46
This commit is contained in:
parent
a830be1403
commit
3fdc289720
6 changed files with 162 additions and 6 deletions
|
|
@ -29,8 +29,10 @@ import ua.acclorite.book_story.R
|
|||
|
||||
|
||||
private const val MENU_ITEM_COPY = 0
|
||||
private const val MENU_ITEM_TRANSLATE = 1
|
||||
private const val MENU_ITEM_DICTIONARY = 2
|
||||
private const val MENU_ITEM_SHARE = 1
|
||||
private const val MENU_ITEM_WEB = 2
|
||||
private const val MENU_ITEM_TRANSLATE = 3
|
||||
private const val MENU_ITEM_DICTIONARY = 4
|
||||
|
||||
/**
|
||||
* Custom Text ActionMode callback. Used in pair with [CustomSelectionToolbar]. Follow [TextToolbar] for more info.
|
||||
|
|
@ -39,6 +41,8 @@ private class CustomTextActionModeCallback(
|
|||
private val context: Context,
|
||||
var rect: Rect = Rect.Zero,
|
||||
var onCopyRequested: (() -> Unit)? = null,
|
||||
var onShareRequested: (() -> Unit)? = null,
|
||||
var onWebSearchRequested: (() -> Unit)? = null,
|
||||
var onTranslateRequested: (() -> Unit)? = null,
|
||||
var onDictionaryRequested: (() -> Unit)? = null
|
||||
) : ActionMode.Callback {
|
||||
|
|
@ -51,13 +55,23 @@ private class CustomTextActionModeCallback(
|
|||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
|
||||
}
|
||||
|
||||
onShareRequested?.let {
|
||||
menu.add(0, MENU_ITEM_SHARE, 1, context.getString(R.string.share))
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
|
||||
}
|
||||
|
||||
onWebSearchRequested?.let {
|
||||
menu.add(0, MENU_ITEM_WEB, 2, context.getString(R.string.web_search))
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
|
||||
}
|
||||
|
||||
onTranslateRequested?.let {
|
||||
menu.add(0, MENU_ITEM_TRANSLATE, 1, context.getString(R.string.translate))
|
||||
menu.add(0, MENU_ITEM_TRANSLATE, 3, context.getString(R.string.translate))
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
|
||||
}
|
||||
|
||||
onDictionaryRequested?.let {
|
||||
menu.add(0, MENU_ITEM_DICTIONARY, 2, context.getString(R.string.dictionary))
|
||||
menu.add(0, MENU_ITEM_DICTIONARY, 4, context.getString(R.string.dictionary))
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
|
||||
}
|
||||
|
||||
|
|
@ -71,6 +85,8 @@ private class CustomTextActionModeCallback(
|
|||
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
|
||||
when (item!!.itemId) {
|
||||
MENU_ITEM_COPY -> onCopyRequested?.invoke()
|
||||
MENU_ITEM_SHARE -> onShareRequested?.invoke()
|
||||
MENU_ITEM_WEB -> onWebSearchRequested?.invoke()
|
||||
MENU_ITEM_TRANSLATE -> onTranslateRequested?.invoke()
|
||||
MENU_ITEM_DICTIONARY -> onDictionaryRequested?.invoke()
|
||||
else -> return false
|
||||
|
|
@ -116,12 +132,15 @@ private class FloatingTextActionModeCallback(
|
|||
}
|
||||
|
||||
/**
|
||||
* Custom Selection Toolbar. Used in pair with [CustomSelectionContainer] to display custom toolbar.
|
||||
* Custom Selection Toolbar.
|
||||
* Used in pair with [CustomSelectionContainer] to display custom toolbar.
|
||||
*/
|
||||
private class CustomSelectionToolbar(
|
||||
private val view: View,
|
||||
context: Context,
|
||||
private val onCopyRequest: (() -> Unit)?,
|
||||
private val onShareRequest: ((String) -> Unit)?,
|
||||
private val onWebSearchRequest: ((String) -> Unit)?,
|
||||
private val onTranslateRequest: ((String) -> Unit)?,
|
||||
private val onDictionaryRequest: ((String) -> Unit)?
|
||||
) : TextToolbar {
|
||||
|
|
@ -145,6 +164,36 @@ private class CustomSelectionToolbar(
|
|||
onCopyRequested?.invoke()
|
||||
onCopyRequest?.invoke()
|
||||
}
|
||||
callback.onShareRequested = {
|
||||
val previousClipboard = clipboardManager.primaryClip
|
||||
onCopyRequested?.invoke()
|
||||
val currentClipboard = clipboardManager.text
|
||||
|
||||
onShareRequest?.invoke(currentClipboard.toString())
|
||||
|
||||
if (previousClipboard != null) {
|
||||
clipboardManager.setPrimaryClip(
|
||||
previousClipboard
|
||||
)
|
||||
} else {
|
||||
clipboardManager.setPrimaryClip(ClipData.newPlainText(null, " "))
|
||||
}
|
||||
}
|
||||
callback.onWebSearchRequested = {
|
||||
val previousClipboard = clipboardManager.primaryClip
|
||||
onCopyRequested?.invoke()
|
||||
val currentClipboard = clipboardManager.text
|
||||
|
||||
onWebSearchRequest?.invoke(currentClipboard.toString())
|
||||
|
||||
if (previousClipboard != null) {
|
||||
clipboardManager.setPrimaryClip(
|
||||
previousClipboard
|
||||
)
|
||||
} else {
|
||||
clipboardManager.setPrimaryClip(ClipData.newPlainText(null, " "))
|
||||
}
|
||||
}
|
||||
callback.onTranslateRequested = {
|
||||
val previousClipboard = clipboardManager.primaryClip
|
||||
onCopyRequested?.invoke()
|
||||
|
|
@ -205,6 +254,8 @@ private class CustomSelectionToolbar(
|
|||
@Composable
|
||||
fun CustomSelectionContainer(
|
||||
onCopyRequested: (() -> Unit),
|
||||
onShareRequested: ((String) -> Unit),
|
||||
onWebSearchRequested: ((String) -> Unit),
|
||||
onTranslateRequested: ((String) -> Unit),
|
||||
onDictionaryRequested: ((String) -> Unit),
|
||||
content: @Composable (toolbarHidden: Boolean) -> Unit
|
||||
|
|
@ -220,6 +271,12 @@ fun CustomSelectionContainer(
|
|||
onCopyRequest = {
|
||||
onCopyRequested()
|
||||
},
|
||||
onShareRequest = {
|
||||
onShareRequested(it)
|
||||
},
|
||||
onWebSearchRequest = {
|
||||
onWebSearchRequested(it)
|
||||
},
|
||||
onTranslateRequest = {
|
||||
onTranslateRequested(it)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -274,6 +274,36 @@ private fun ReaderScreen(
|
|||
).show()
|
||||
}
|
||||
},
|
||||
onShareRequested = { textToShare ->
|
||||
onEvent(
|
||||
ReaderEvent.OnOpenShareApp(
|
||||
textToShare = textToShare,
|
||||
context,
|
||||
noAppsFound = {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.error_no_share_app),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
)
|
||||
)
|
||||
},
|
||||
onWebSearchRequested = { textToSearch ->
|
||||
onEvent(
|
||||
ReaderEvent.OnOpenWebBrowser(
|
||||
textToSearch = textToSearch,
|
||||
context,
|
||||
noAppsFound = {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.error_no_browser),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
)
|
||||
)
|
||||
},
|
||||
onTranslateRequested = { textToTranslate ->
|
||||
onEvent(
|
||||
ReaderEvent.OnOpenTranslator(
|
||||
|
|
@ -298,7 +328,7 @@ private fun ReaderScreen(
|
|||
noAppsFound = {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.error_no_browser),
|
||||
context.getString(R.string.error_no_dictionary),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,18 @@ sealed class ReaderEvent {
|
|||
val noAppsFound: () -> Unit
|
||||
) : ReaderEvent()
|
||||
|
||||
data class OnOpenShareApp(
|
||||
val textToShare: String,
|
||||
val context: ComponentActivity,
|
||||
val noAppsFound: () -> Unit
|
||||
) : ReaderEvent()
|
||||
|
||||
data class OnOpenWebBrowser(
|
||||
val textToSearch: String,
|
||||
val context: ComponentActivity,
|
||||
val noAppsFound: () -> Unit
|
||||
) : ReaderEvent()
|
||||
|
||||
data class OnOpenDictionary(
|
||||
val textToDefine: String,
|
||||
val context: ComponentActivity,
|
||||
|
|
|
|||
|
|
@ -360,6 +360,55 @@ class ReaderViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
is ReaderEvent.OnOpenShareApp -> {
|
||||
launch {
|
||||
val shareIntent = Intent()
|
||||
|
||||
shareIntent.action = Intent.ACTION_SEND
|
||||
shareIntent.type = "text/plain"
|
||||
shareIntent.putExtra(
|
||||
Intent.EXTRA_SUBJECT,
|
||||
event.context.getString(R.string.app_name)
|
||||
)
|
||||
shareIntent.putExtra(
|
||||
Intent.EXTRA_TEXT,
|
||||
event.textToShare.trim()
|
||||
)
|
||||
|
||||
var shareFailure = false
|
||||
shareIntent.launchActivity(event.context, createChooser = true) {
|
||||
shareFailure = true
|
||||
}
|
||||
if (!shareFailure) {
|
||||
return@launch
|
||||
}
|
||||
|
||||
event.noAppsFound()
|
||||
}
|
||||
}
|
||||
|
||||
is ReaderEvent.OnOpenWebBrowser -> {
|
||||
launch {
|
||||
val browserIntent = Intent()
|
||||
|
||||
browserIntent.action = Intent.ACTION_WEB_SEARCH
|
||||
browserIntent.putExtra(
|
||||
SearchManager.QUERY,
|
||||
event.textToSearch
|
||||
)
|
||||
|
||||
var browserFailure = false
|
||||
browserIntent.launchActivity(event.context) {
|
||||
browserFailure = true
|
||||
}
|
||||
if (!browserFailure) {
|
||||
return@launch
|
||||
}
|
||||
|
||||
event.noAppsFound()
|
||||
}
|
||||
}
|
||||
|
||||
is ReaderEvent.OnOpenDictionary -> {
|
||||
launch {
|
||||
val dictionaryIntent = Intent()
|
||||
|
|
|
|||
|
|
@ -101,7 +101,9 @@
|
|||
<string name="error_query">Помилка: %1$s</string>
|
||||
<string name="error_file_empty">Цей файл пустий або пошкоджений.</string>
|
||||
<string name="error_no_translator">Не знайдено жодного перекладача.</string>
|
||||
<string name="error_no_dictionary">Не знайдено жодного словника.</string>
|
||||
<string name="error_no_browser">Не знайдено жодного браузера.</string>
|
||||
<string name="error_no_share_app">Не знайдено застосунка щоб поділитися.</string>
|
||||
<string name="error_no_text">
|
||||
Вміст книги пустий. Будь ласка, перевірте вміст книги та оновіть її.
|
||||
</string>
|
||||
|
|
@ -160,6 +162,8 @@
|
|||
<string name="download">Завантажити</string>
|
||||
<string name="done">Готово</string>
|
||||
<string name="enable">Увімкнути</string>
|
||||
<string name="web_search">Веб-пошук</string>
|
||||
<string name="share">Поділитися</string>
|
||||
|
||||
<!-- Items -->
|
||||
<string name="files">файли</string>
|
||||
|
|
|
|||
|
|
@ -118,7 +118,9 @@
|
|||
<string name="error_query">Error: %1$s</string>
|
||||
<string name="error_file_empty">This file is empty or corrupted.</string>
|
||||
<string name="error_no_translator">No translator app found.</string>
|
||||
<string name="error_no_dictionary">No dictionary app found.</string>
|
||||
<string name="error_no_browser">No browser app found.</string>
|
||||
<string name="error_no_share_app">No app to share found.</string>
|
||||
<string name="error_no_text">
|
||||
The book content is empty. Please, make sure that the book content is not empty and update it.
|
||||
</string>
|
||||
|
|
@ -176,6 +178,8 @@
|
|||
<string name="download">Download</string>
|
||||
<string name="done">Done</string>
|
||||
<string name="enable">Enable</string>
|
||||
<string name="web_search">Web search</string>
|
||||
<string name="share">Share</string>
|
||||
|
||||
<!-- Items -->
|
||||
<string name="files">files</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue