Recently Written · git

subread-anki

One tap makes an Anki card on Android: word, definition, sentence, screenshot, audio. Works with any app, with SubRead Dictionary and SubRead Overlay.

git clone https://github.com/equwal/subread-anki

Log | Files | Refs


app/src/main/kotlin/space/subread/anki/Requests.kt (4220 bytes)

1 package space.subread.anki
2 
3 import android.content.Intent
4 import android.net.Uri
5 import androidx.core.content.IntentCompat
6 import space.subread.anki.core.MineRequest
7 import space.subread.anki.core.SharedText
8 
9 /** Reads a [MineRequest] from each kind of Intent that [AddActivity] takes. */
10 object Requests {
11 
12     /** The action for another app: the extras below, any subset of them. */
13     const val ACTION_ADD = "space.subread.anki.action.ADD"
14 
15     /** Each key of [MineRequest.KEYS], in upper case, after this prefix: `space.subread.anki.extra.SENTENCE`. */
16     const val EXTRA_PREFIX = "space.subread.anki.extra."
17 
18     /** In the result of the activity: the id of the note in AnkiDroid (`Long`). */
19     const val EXTRA_NOTE_ID = "space.subread.anki.extra.NOTE_ID"
20 
21     /** In the result of the activity: why no card was added (`String`). */
22     const val EXTRA_ERROR = "space.subread.anki.extra.ERROR"
23 
24     /** The name of the extra for a key of [MineRequest]. */
25     fun extraName(key: String): String = EXTRA_PREFIX + key.uppercase()
26 
27     /**
28      * The request in an Intent, or null when the Intent is none of the kinds the app takes.
29      * A text from the selection menu or the share sheet is the word. A picture from the
30      * share sheet is the picture, with the text beside it as the word. A browser shares the
31      * link of the page with the text: the link does not go on the card.
32      */
33     fun fromIntent(intent: Intent): MineRequest? = when (intent.action) {
34         Intent.ACTION_PROCESS_TEXT -> MineRequest(expression = intent.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT)?.toString())
35         Intent.ACTION_SEND -> {
36             val stream = IntentCompat.getParcelableExtra(intent, Intent.EXTRA_STREAM, Uri::class.java)
37             val text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT)?.toString()?.let(SharedText::clean)
38             MineRequest(expression = text?.ifEmpty { null }, image = stream?.toString())
39         }
40         Intent.ACTION_VIEW -> {
41             // A web page: the query of the link. An intent: link can carry extras too; they win.
42             val fromQuery = MineRequest.parseQuery(intent.data?.encodedQuery)
43             MineRequest.fromMap(fromQuery + fromExtras(intent))
44         }
45         ACTION_ADD -> MineRequest.fromMap(fromExtras(intent))
46         else -> null
47     }
48 
49     /**
50      * The extras of the request, as strings. A Uri, a number and a boolean are written as text.
51      *
52      * The extras of the intent API of the main branch are taken too, with their meaning there:
53      * `WORD` is the word, `TEXT` a text with no word chosen, `AUDIO` the audio of the word,
54      * `SENTENCE_AUDIO` the audio of the sentence, `SHOW` the same as `CONFIRM`. SubRead
55      * Overlay and SubRead Dictionary send those.
56      */
57     fun fromExtras(intent: Intent): Map<String, String> {
58         val extras = intent.extras ?: return emptyMap()
59         val out = LinkedHashMap<String, String>()
60         fun text(name: String): String? {
61             @Suppress("DEPRECATION") // The value can be of any type: only the untyped get tells which.
62             val value = extras.get(name) ?: return null
63             return if (value is Boolean) (if (value) "1" else "0") else value.toString()
64         }
65         for (key in MineRequest.KEYS) text(extraName(key))?.let { out[key] = it }
66         val other = text(EXTRA_PREFIX + "WORD") != null || text(EXTRA_PREFIX + "TEXT") != null
67         if (other) {
68             text(EXTRA_PREFIX + "WORD")?.let { out[MineRequest.KEY_EXPRESSION] = it }
69             text(EXTRA_PREFIX + "TEXT")?.let { out.putIfAbsent(MineRequest.KEY_SENTENCE, it) }
70             text(EXTRA_PREFIX + "AUDIO")?.let { out[MineRequest.KEY_WORD_AUDIO] = it }
71             out.remove(MineRequest.KEY_AUDIO)
72             text(EXTRA_PREFIX + "SENTENCE_AUDIO")?.let { out[MineRequest.KEY_AUDIO] = it }
73             text(EXTRA_PREFIX + "SHOW")?.let { out.putIfAbsent(MineRequest.KEY_CONFIRM, it) }
74         }
75         return out
76     }
77 
78     /** The Intent for another app on the device: the request as extras of [ACTION_ADD]. */
79     fun toIntent(request: MineRequest): Intent = Intent(ACTION_ADD).apply {
80         for ((key, value) in request.toMap()) putExtra(extraName(key), value)
81     }
82 }