Sample app process outgoing repositories links

This commit is contained in:
Dimitry Ivanov 2020-06-26 17:51:59 +03:00
parent 186390805a
commit 1a90f1e609
7 changed files with 83 additions and 5 deletions

View File

@ -85,6 +85,6 @@ dependencies {
implementation it['adapt'] implementation it['adapt']
implementation it['debug'] implementation it['debug']
implementation it['android-svg'] implementation it['android-svg']
implementation it['android-gif'] implementation it['android-gif-impl']
} }
} }

View File

@ -35,9 +35,13 @@
<data <data
android:host="github.com" android:host="github.com"
android:pathPattern=".*\\.md"
android:scheme="https" /> android:scheme="https" />
<data android:pathPattern=".*\\.md" />
<data android:pathPattern=".*\\..*\\.md" />
<data android:pathPattern=".*\\..*\\..*\\.md"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.md"/>
</intent-filter> </intent-filter>
</activity> </activity>

View File

@ -1,8 +1,14 @@
package io.noties.markwon.app package io.noties.markwon.app
import android.app.Application import android.app.Application
import android.content.ComponentName
import android.content.Intent
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
import android.os.Build
import io.noties.debug.AndroidLogDebugOutput import io.noties.debug.AndroidLogDebugOutput
import io.noties.debug.Debug import io.noties.debug.Debug
import io.noties.markwon.app.readme.ReadMeActivity
import io.noties.markwon.app.sample.SampleManager import io.noties.markwon.app.sample.SampleManager
import java.util.concurrent.ExecutorService import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors import java.util.concurrent.Executors
@ -17,6 +23,31 @@ class App : Application() {
executorService = Executors.newCachedThreadPool() executorService = Executors.newCachedThreadPool()
sampleManager = SampleManager(this, executorService) sampleManager = SampleManager(this, executorService)
ensureReadmeShortcut()
}
private fun ensureReadmeShortcut() {
if (Build.VERSION.SDK_INT < 25) {
return
}
val manager = getSystemService(ShortcutManager::class.java) ?: return
@Suppress("ReplaceNegatedIsEmptyWithIsNotEmpty")
if (!manager.dynamicShortcuts.isEmpty()) {
return
}
val intent = Intent(Intent.ACTION_VIEW).apply {
component = ComponentName(this@App, ReadMeActivity::class.java)
}
val shortcut = ShortcutInfo.Builder(this, "readme")
.setShortLabel("README")
.setIntent(intent)
.build()
manager.addDynamicShortcuts(mutableListOf(shortcut))
} }
companion object { companion object {

View File

@ -12,12 +12,14 @@ import androidx.recyclerview.widget.RecyclerView
import io.noties.debug.Debug import io.noties.debug.Debug
import io.noties.markwon.AbstractMarkwonPlugin import io.noties.markwon.AbstractMarkwonPlugin
import io.noties.markwon.Markwon import io.noties.markwon.Markwon
import io.noties.markwon.MarkwonConfiguration
import io.noties.markwon.MarkwonVisitor import io.noties.markwon.MarkwonVisitor
import io.noties.markwon.app.R import io.noties.markwon.app.R
import io.noties.markwon.app.utils.ReadMeUtils import io.noties.markwon.app.utils.ReadMeUtils
import io.noties.markwon.app.utils.hidden import io.noties.markwon.app.utils.hidden
import io.noties.markwon.app.utils.loadReadMe import io.noties.markwon.app.utils.loadReadMe
import io.noties.markwon.app.utils.textOrHide import io.noties.markwon.app.utils.textOrHide
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin
import io.noties.markwon.ext.tasklist.TaskListPlugin import io.noties.markwon.ext.tasklist.TaskListPlugin
import io.noties.markwon.html.HtmlPlugin import io.noties.markwon.html.HtmlPlugin
import io.noties.markwon.image.ImagesPlugin import io.noties.markwon.image.ImagesPlugin
@ -27,6 +29,7 @@ import io.noties.markwon.recycler.table.TableEntry
import io.noties.markwon.recycler.table.TableEntryPlugin import io.noties.markwon.recycler.table.TableEntryPlugin
import io.noties.markwon.syntax.Prism4jThemeDefault import io.noties.markwon.syntax.Prism4jThemeDefault
import io.noties.markwon.syntax.SyntaxHighlightPlugin import io.noties.markwon.syntax.SyntaxHighlightPlugin
import io.noties.markwon.utils.DumpNodes
import io.noties.prism4j.Prism4j import io.noties.prism4j.Prism4j
import io.noties.prism4j.annotations.PrismBundle import io.noties.prism4j.annotations.PrismBundle
import okhttp3.Call import okhttp3.Call
@ -65,6 +68,7 @@ class ReadMeActivity : Activity() {
.usePlugin(TableEntryPlugin.create(this)) .usePlugin(TableEntryPlugin.create(this))
.usePlugin(SyntaxHighlightPlugin.create(Prism4j(GrammarLocatorDef()), Prism4jThemeDefault.create(0))) .usePlugin(SyntaxHighlightPlugin.create(Prism4j(GrammarLocatorDef()), Prism4jThemeDefault.create(0)))
.usePlugin(TaskListPlugin.create(this)) .usePlugin(TaskListPlugin.create(this))
.usePlugin(StrikethroughPlugin.create())
.usePlugin(ReadMeImageDestinationPlugin(intent.data)) .usePlugin(ReadMeImageDestinationPlugin(intent.data))
.usePlugin(object : AbstractMarkwonPlugin() { .usePlugin(object : AbstractMarkwonPlugin() {
override fun configureVisitor(builder: MarkwonVisitor.Builder) { override fun configureVisitor(builder: MarkwonVisitor.Builder) {
@ -79,6 +83,10 @@ class ReadMeActivity : Activity() {
visitor.builder().append(code) visitor.builder().append(code)
} }
} }
override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
builder.linkResolver(ReadMeLinkResolver())
}
}) })
.build() .build()
@ -120,6 +128,7 @@ class ReadMeActivity : Activity() {
is Result.Success -> { is Result.Success -> {
val markwon = markwon val markwon = markwon
val node = markwon.parse(result.markdown) val node = markwon.parse(result.markdown)
Debug.i(DumpNodes.dump(node))
if (window != null) { if (window != null) {
recyclerView.post { recyclerView.post {
adapter.setParsedMarkdown(markwon, node) adapter.setParsedMarkdown(markwon, node)

View File

@ -0,0 +1,18 @@
package io.noties.markwon.app.readme
import android.view.View
import io.noties.markwon.LinkResolverDef
import io.noties.markwon.app.utils.ReadMeUtils
class ReadMeLinkResolver : LinkResolverDef() {
override fun resolve(view: View, link: String) {
val matcher = ReadMeUtils.RE_REPOSITORY.matcher(link)
val url = if (matcher.matches()) {
ReadMeUtils.buildRepositoryReadMeUrl(matcher.group(1), matcher.group(2))
} else {
link
}
super.resolve(view, url)
}
}

View File

@ -4,8 +4,13 @@ import android.net.Uri
import java.util.regex.Pattern import java.util.regex.Pattern
object ReadMeUtils { object ReadMeUtils {
// username, repo, branch, lastPathSegment // username, repo, branch, lastPathSegment
private val RE = Pattern.compile("^https:\\/\\/github\\.com\\/(\\w+?)\\/(\\w+?)\\/(?:blob|raw)\\/(\\w+?)\\/(.+)") @Suppress("RegExpRedundantEscape")
private val RE_FILE = Pattern.compile("^https:\\/\\/github\\.com\\/([\\w-.]+?)\\/([\\w-.]+?)\\/(?:blob|raw)\\/([\\w-.]+?)\\/(.+)\$")
@Suppress("RegExpRedundantEscape")
val RE_REPOSITORY: Pattern = Pattern.compile("^https:\\/\\/github.com\\/([\\w-.]+?)\\/([\\w-.]+?)\\/*\$")
data class GithubInfo( data class GithubInfo(
val username: String, val username: String,
@ -20,7 +25,7 @@ object ReadMeUtils {
return null return null
} }
val matcher = RE.matcher(data.toString()) val matcher = RE_FILE.matcher(data.toString())
if (!matcher.matches()) { if (!matcher.matches()) {
return null return null
} }
@ -38,7 +43,16 @@ object ReadMeUtils {
return if (info == null) { return if (info == null) {
data.toString() data.toString()
} else { } else {
"https://github.com/${info.username}/${info.repository}/raw/${info.branch}/${info.fileName}" buildRawGithubUrl(info)
} }
} }
@Suppress("MemberVisibilityCanBePrivate")
fun buildRawGithubUrl(info: GithubInfo): String {
return "https://github.com/${info.username}/${info.repository}/raw/${info.branch}/${info.fileName}"
}
fun buildRepositoryReadMeUrl(username: String, repository: String): String {
return buildRawGithubUrl(GithubInfo(username, repository, "master", "README.md"))
}
} }

View File

@ -76,7 +76,9 @@ ext {
'commonmark-strikethrough': "com.atlassian.commonmark:commonmark-ext-gfm-strikethrough:$commonMarkVersion", 'commonmark-strikethrough': "com.atlassian.commonmark:commonmark-ext-gfm-strikethrough:$commonMarkVersion",
'commonmark-table' : "com.atlassian.commonmark:commonmark-ext-gfm-tables:$commonMarkVersion", 'commonmark-table' : "com.atlassian.commonmark:commonmark-ext-gfm-tables:$commonMarkVersion",
'android-svg' : 'com.caverock:androidsvg:1.4', 'android-svg' : 'com.caverock:androidsvg:1.4',
// we need 2 gif artifacts at long as we have a difference in version used for API compatibility
'android-gif' : 'pl.droidsonroids.gif:android-gif-drawable:1.2.15', 'android-gif' : 'pl.droidsonroids.gif:android-gif-drawable:1.2.15',
'android-gif-impl' : 'pl.droidsonroids.gif:android-gif-drawable:1.2.20',
'jlatexmath-android' : 'ru.noties:jlatexmath-android:0.2.0', 'jlatexmath-android' : 'ru.noties:jlatexmath-android:0.2.0',
'okhttp' : 'com.squareup.okhttp3:okhttp:3.9.0', 'okhttp' : 'com.squareup.okhttp3:okhttp:3.9.0',
'prism4j' : 'io.noties:prism4j:2.0.0', 'prism4j' : 'io.noties:prism4j:2.0.0',