Sample app, update check
This commit is contained in:
parent
086494bd97
commit
c96ea690f6
@ -3,6 +3,15 @@ apply plugin: 'kotlin-android'
|
|||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
|
||||||
|
def gitSha = { ->
|
||||||
|
def output = new ByteArrayOutputStream()
|
||||||
|
exec {
|
||||||
|
commandLine 'git', 'rev-parse', '--short', 'HEAD'
|
||||||
|
standardOutput = output
|
||||||
|
}
|
||||||
|
return output.toString().trim()
|
||||||
|
}.memoize()
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
||||||
compileSdkVersion config['compile-sdk']
|
compileSdkVersion config['compile-sdk']
|
||||||
@ -17,7 +26,10 @@ android {
|
|||||||
|
|
||||||
resConfig 'en'
|
resConfig 'en'
|
||||||
|
|
||||||
setProperty("archivesBaseName", "markwon-$versionName")
|
setProperty("archivesBaseName", "markwon")
|
||||||
|
|
||||||
|
buildConfigField 'String', 'GIT_SHA', "\"${gitSha()}\""
|
||||||
|
buildConfigField 'String', 'GIT_REPOSITORY', '"https://github.com/noties/Markwon"'
|
||||||
|
|
||||||
final def scheme = 'markwon'
|
final def scheme = 'markwon'
|
||||||
buildConfigField 'String', 'DEEPLINK_SCHEME', "\"$scheme\""
|
buildConfigField 'String', 'DEEPLINK_SCHEME', "\"$scheme\""
|
||||||
@ -41,6 +53,37 @@ android {
|
|||||||
java.srcDirs += '../sample-utils/annotations'
|
java.srcDirs += '../sample-utils/annotations'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
config {
|
||||||
|
|
||||||
|
final def keystoreFile = project.file('keystore.jks')
|
||||||
|
final def keystoreFilePassword = 'MARKWON_KEYSTORE_FILE_PASSWORD'
|
||||||
|
final def keystoreAlias = 'MARKWON_KEY_ALIAS'
|
||||||
|
final def keystoreAliasPassword = 'MARKWON_KEY_ALIAS_PASSWORD'
|
||||||
|
|
||||||
|
final def properties = [
|
||||||
|
keystoreFilePassword,
|
||||||
|
keystoreAlias,
|
||||||
|
keystoreAliasPassword
|
||||||
|
]
|
||||||
|
|
||||||
|
if (!keystoreFile.exists()) {
|
||||||
|
throw new IllegalStateException("No '${keystoreFile.name}' file is found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
final def missingProperties = properties.findAll { !project.hasProperty(it) }
|
||||||
|
if (!missingProperties.isEmpty()) {
|
||||||
|
throw new IllegalStateException("Missing required signing properties: $missingProperties")
|
||||||
|
}
|
||||||
|
|
||||||
|
storeFile keystoreFile
|
||||||
|
storePassword project[keystoreFilePassword]
|
||||||
|
|
||||||
|
keyAlias project[keystoreAlias]
|
||||||
|
keyPassword project[keystoreAliasPassword]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kapt {
|
kapt {
|
||||||
|
24
app-sample/deploy.sh
Executable file
24
app-sample/deploy.sh
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# abort on errors
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# build
|
||||||
|
../gradlew :app-sample:clean
|
||||||
|
../gradlew :app-sample:assembleDebug
|
||||||
|
|
||||||
|
# navigate into the build output directory
|
||||||
|
cd ./build/outputs/apk/debug/
|
||||||
|
|
||||||
|
revision=$(git rev-parse --short HEAD)
|
||||||
|
|
||||||
|
echo "output.json" > ./.gitignore
|
||||||
|
echo "$revision" > ./version
|
||||||
|
|
||||||
|
git init
|
||||||
|
git add -A
|
||||||
|
git commit -m "sample $revision"
|
||||||
|
|
||||||
|
git push -f git@github.com:noties/Markwon.git master:sample-store
|
||||||
|
|
||||||
|
cd -
|
BIN
app-sample/keystore.jks
Normal file
BIN
app-sample/keystore.jks
Normal file
Binary file not shown.
BIN
app-sample/release/markwon-4.4.1-SNAPSHOT-release.apk
Normal file
BIN
app-sample/release/markwon-4.4.1-SNAPSHOT-release.apk
Normal file
Binary file not shown.
20
app-sample/release/output.json
Normal file
20
app-sample/release/output.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"artifactType": {
|
||||||
|
"type": "APK",
|
||||||
|
"kind": "Directory"
|
||||||
|
},
|
||||||
|
"applicationId": "io.noties.markwon.app",
|
||||||
|
"variantName": "release",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"type": "SINGLE",
|
||||||
|
"filters": [],
|
||||||
|
"properties": [],
|
||||||
|
"versionCode": 1,
|
||||||
|
"versionName": "1",
|
||||||
|
"enabled": true,
|
||||||
|
"outputFile": "markwon-4.4.1-SNAPSHOT-release.apk"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,8 +1,12 @@
|
|||||||
package io.noties.markwon.app.sample.ui
|
package io.noties.markwon.app.sample.ui
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import android.text.TextUtils
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -15,20 +19,26 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import io.noties.adapt.Adapt
|
import io.noties.adapt.Adapt
|
||||||
import io.noties.adapt.DiffUtilDataSetChanged
|
import io.noties.adapt.DiffUtilDataSetChanged
|
||||||
|
import io.noties.adapt.Item
|
||||||
import io.noties.debug.Debug
|
import io.noties.debug.Debug
|
||||||
import io.noties.markwon.Markwon
|
import io.noties.markwon.Markwon
|
||||||
import io.noties.markwon.app.App
|
import io.noties.markwon.app.App
|
||||||
|
import io.noties.markwon.app.BuildConfig
|
||||||
import io.noties.markwon.app.R
|
import io.noties.markwon.app.R
|
||||||
import io.noties.markwon.app.readme.ReadMeActivity
|
import io.noties.markwon.app.readme.ReadMeActivity
|
||||||
import io.noties.markwon.app.sample.Sample
|
import io.noties.markwon.app.sample.Sample
|
||||||
import io.noties.markwon.app.sample.SampleItem
|
|
||||||
import io.noties.markwon.app.sample.SampleManager
|
import io.noties.markwon.app.sample.SampleManager
|
||||||
import io.noties.markwon.app.sample.SampleSearch
|
import io.noties.markwon.app.sample.SampleSearch
|
||||||
|
import io.noties.markwon.app.sample.ui.adapt.CheckForUpdateItem
|
||||||
|
import io.noties.markwon.app.sample.ui.adapt.SampleItem
|
||||||
|
import io.noties.markwon.app.sample.ui.adapt.VersionItem
|
||||||
import io.noties.markwon.app.utils.Cancellable
|
import io.noties.markwon.app.utils.Cancellable
|
||||||
|
import io.noties.markwon.app.utils.UpdateUtils
|
||||||
import io.noties.markwon.app.utils.displayName
|
import io.noties.markwon.app.utils.displayName
|
||||||
import io.noties.markwon.app.utils.hidden
|
import io.noties.markwon.app.utils.hidden
|
||||||
import io.noties.markwon.app.utils.onPreDraw
|
import io.noties.markwon.app.utils.onPreDraw
|
||||||
import io.noties.markwon.app.utils.recyclerView
|
import io.noties.markwon.app.utils.recyclerView
|
||||||
|
import io.noties.markwon.app.utils.stackTraceString
|
||||||
import io.noties.markwon.app.utils.tagDisplayName
|
import io.noties.markwon.app.utils.tagDisplayName
|
||||||
import io.noties.markwon.app.widget.SearchBar
|
import io.noties.markwon.app.widget.SearchBar
|
||||||
import io.noties.markwon.movement.MovementMethodPlugin
|
import io.noties.markwon.movement.MovementMethodPlugin
|
||||||
@ -50,6 +60,13 @@ class SampleListFragment : Fragment() {
|
|||||||
private var pendingRecyclerScrollPosition: RecyclerScrollPosition? = null
|
private var pendingRecyclerScrollPosition: RecyclerScrollPosition? = null
|
||||||
|
|
||||||
private var cancellable: Cancellable? = null
|
private var cancellable: Cancellable? = null
|
||||||
|
private var checkForUpdateCancellable: Cancellable? = null
|
||||||
|
|
||||||
|
private lateinit var progressBar: View
|
||||||
|
|
||||||
|
private val versionItem: VersionItem by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
|
VersionItem()
|
||||||
|
}
|
||||||
|
|
||||||
private val sampleManager: SampleManager
|
private val sampleManager: SampleManager
|
||||||
get() = App.sampleManager
|
get() = App.sampleManager
|
||||||
@ -73,6 +90,8 @@ class SampleListFragment : Fragment() {
|
|||||||
|
|
||||||
val context = requireContext()
|
val context = requireContext()
|
||||||
|
|
||||||
|
progressBar = view.findViewById(R.id.progress_bar)
|
||||||
|
|
||||||
val searchBar: SearchBar = view.findViewById(R.id.search_bar)
|
val searchBar: SearchBar = view.findViewById(R.id.search_bar)
|
||||||
searchBar.onSearchListener = {
|
searchBar.onSearchListener = {
|
||||||
search = it
|
search = it
|
||||||
@ -187,16 +206,30 @@ class SampleListFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindSamples(samples: List<Sample>) {
|
private fun bindSamples(samples: List<Sample>, addVersion: Boolean) {
|
||||||
val items = samples.map {
|
|
||||||
SampleItem(
|
val items: List<Item<*>> = samples
|
||||||
markwon,
|
.map {
|
||||||
it,
|
SampleItem(
|
||||||
{ artifact -> openArtifact(artifact) },
|
markwon,
|
||||||
{ tag -> openTag(tag) },
|
it,
|
||||||
{ sample -> openSample(sample) }
|
{ artifact -> openArtifact(artifact) },
|
||||||
)
|
{ tag -> openTag(tag) },
|
||||||
}
|
{ sample -> openSample(sample) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.let {
|
||||||
|
if (addVersion) {
|
||||||
|
val list: List<Item<*>> = it
|
||||||
|
list.toMutableList().apply {
|
||||||
|
add(0, CheckForUpdateItem(this@SampleListFragment::checkForUpdate))
|
||||||
|
add(0, versionItem)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
adapt.setItems(items)
|
adapt.setItems(items)
|
||||||
|
|
||||||
val recyclerView = adapt.recyclerView ?: return
|
val recyclerView = adapt.recyclerView ?: return
|
||||||
@ -218,6 +251,66 @@ class SampleListFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkForUpdate() {
|
||||||
|
val current = checkForUpdateCancellable
|
||||||
|
if (current != null && !current.isCancelled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
progressBar.hidden = false
|
||||||
|
checkForUpdateCancellable = UpdateUtils.checkForUpdate { result ->
|
||||||
|
progressBar.post {
|
||||||
|
processUpdateResult(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processUpdateResult(result: UpdateUtils.Result) {
|
||||||
|
val context = context ?: return
|
||||||
|
|
||||||
|
progressBar.hidden = true
|
||||||
|
|
||||||
|
val builder = AlertDialog.Builder(context)
|
||||||
|
|
||||||
|
when (result) {
|
||||||
|
is UpdateUtils.Result.UpdateAvailable -> {
|
||||||
|
val md = """
|
||||||
|
## Update available
|
||||||
|
Would you like to download it?
|
||||||
|
""".trimIndent()
|
||||||
|
builder.setMessage(markwon.toMarkdown(md))
|
||||||
|
builder.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
builder.setPositiveButton("Download") { _, _ ->
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(result.url))
|
||||||
|
startActivity(Intent.createChooser(intent, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is UpdateUtils.Result.NoUpdate -> {
|
||||||
|
val md = """
|
||||||
|
## No update
|
||||||
|
You are using latest version (${BuildConfig.GIT_SHA})
|
||||||
|
""".trimIndent()
|
||||||
|
builder.setMessage(markwon.toMarkdown(md))
|
||||||
|
builder.setPositiveButton(android.R.string.ok, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
is UpdateUtils.Result.Error -> {
|
||||||
|
// trimIndent is confused by tabs in stack trace
|
||||||
|
val md = """
|
||||||
|
## Error
|
||||||
|
```
|
||||||
|
${result.throwable.stackTraceString()}
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
builder.setMessage(markwon.toMarkdown(md))
|
||||||
|
builder.setPositiveButton(android.R.string.ok, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.show()
|
||||||
|
}
|
||||||
|
|
||||||
private fun openArtifact(artifact: MarkwonArtifact) {
|
private fun openArtifact(artifact: MarkwonArtifact) {
|
||||||
Debug.i(artifact)
|
Debug.i(artifact)
|
||||||
openResultFragment(init(artifact))
|
openResultFragment(init(artifact))
|
||||||
@ -262,7 +355,8 @@ class SampleListFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cancellable = sampleManager.samples(sampleSearch) {
|
cancellable = sampleManager.samples(sampleSearch) {
|
||||||
bindSamples(it)
|
val addVersion = sampleSearch is SampleSearch.All && TextUtils.isEmpty(sampleSearch.text)
|
||||||
|
bindSamples(it, addVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package io.noties.markwon.app.sample.ui.adapt
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import io.noties.adapt.Item
|
||||||
|
import io.noties.markwon.app.R
|
||||||
|
|
||||||
|
class CheckForUpdateItem(private val action: () -> Unit) : Item<CheckForUpdateItem.Holder>(42L) {
|
||||||
|
|
||||||
|
override fun createHolder(inflater: LayoutInflater, parent: ViewGroup): Holder {
|
||||||
|
return Holder(inflater.inflate(R.layout.adapt_check_for_update, parent, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun render(holder: Holder) {
|
||||||
|
holder.button.setOnClickListener { action() }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder(view: View) : Item.Holder(view) {
|
||||||
|
val button: View = requireView(R.id.button)
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package io.noties.markwon.app.sample
|
package io.noties.markwon.app.sample.ui.adapt
|
||||||
|
|
||||||
import android.text.Spanned
|
import android.text.Spanned
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -8,6 +8,7 @@ import android.widget.TextView
|
|||||||
import io.noties.adapt.Item
|
import io.noties.adapt.Item
|
||||||
import io.noties.markwon.Markwon
|
import io.noties.markwon.Markwon
|
||||||
import io.noties.markwon.app.R
|
import io.noties.markwon.app.R
|
||||||
|
import io.noties.markwon.app.sample.Sample
|
||||||
import io.noties.markwon.app.utils.displayName
|
import io.noties.markwon.app.utils.displayName
|
||||||
import io.noties.markwon.app.utils.hidden
|
import io.noties.markwon.app.utils.hidden
|
||||||
import io.noties.markwon.app.utils.tagDisplayName
|
import io.noties.markwon.app.utils.tagDisplayName
|
@ -0,0 +1,84 @@
|
|||||||
|
package io.noties.markwon.app.sample.ui.adapt
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.text.Spanned
|
||||||
|
import android.text.TextPaint
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import io.noties.adapt.Item
|
||||||
|
import io.noties.markwon.AbstractMarkwonPlugin
|
||||||
|
import io.noties.markwon.LinkResolver
|
||||||
|
import io.noties.markwon.Markwon
|
||||||
|
import io.noties.markwon.MarkwonSpansFactory
|
||||||
|
import io.noties.markwon.app.BuildConfig
|
||||||
|
import io.noties.markwon.app.R
|
||||||
|
import io.noties.markwon.core.CoreProps
|
||||||
|
import io.noties.markwon.core.MarkwonTheme
|
||||||
|
import io.noties.markwon.core.spans.LinkSpan
|
||||||
|
import io.noties.markwon.html.HtmlPlugin
|
||||||
|
import io.noties.markwon.image.ImagesPlugin
|
||||||
|
import io.noties.markwon.movement.MovementMethodPlugin
|
||||||
|
import org.commonmark.node.Link
|
||||||
|
|
||||||
|
class VersionItem : Item<VersionItem.Holder>(42L) {
|
||||||
|
|
||||||
|
private lateinit var context: Context
|
||||||
|
|
||||||
|
private val markwon: Markwon by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
|
Markwon.builder(context)
|
||||||
|
.usePlugin(ImagesPlugin.create())
|
||||||
|
.usePlugin(MovementMethodPlugin.link())
|
||||||
|
.usePlugin(HtmlPlugin.create())
|
||||||
|
.usePlugin(object : AbstractMarkwonPlugin() {
|
||||||
|
override fun configureSpansFactory(builder: MarkwonSpansFactory.Builder) {
|
||||||
|
builder.setFactory(Link::class.java) { configuration, props ->
|
||||||
|
LinkSpanNoUnderline(
|
||||||
|
configuration.theme(),
|
||||||
|
CoreProps.LINK_DESTINATION.require(props),
|
||||||
|
configuration.linkResolver()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val text: Spanned by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
|
val md = """
|
||||||
|
<a href="${BuildConfig.GIT_REPOSITORY}/blob/master/CHANGELOG.md">
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
</a>
|
||||||
|
""".trimIndent()
|
||||||
|
markwon.toMarkdown(md)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createHolder(inflater: LayoutInflater, parent: ViewGroup): Holder {
|
||||||
|
context = parent.context
|
||||||
|
return Holder(inflater.inflate(R.layout.adapt_version, parent, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun render(holder: Holder) {
|
||||||
|
markwon.setParsedMarkdown(holder.textView, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder(view: View) : Item.Holder(view) {
|
||||||
|
val textView: TextView = requireView(R.id.text_view)
|
||||||
|
}
|
||||||
|
|
||||||
|
class LinkSpanNoUnderline(
|
||||||
|
theme: MarkwonTheme,
|
||||||
|
destination: String,
|
||||||
|
resolver: LinkResolver
|
||||||
|
) : LinkSpan(theme, destination, resolver) {
|
||||||
|
override fun updateDrawState(ds: TextPaint) {
|
||||||
|
super.updateDrawState(ds)
|
||||||
|
ds.isUnderlineText = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import io.noties.markwon.Markwon;
|
import io.noties.markwon.Markwon;
|
||||||
|
import io.noties.markwon.app.BuildConfig;
|
||||||
import io.noties.markwon.app.sample.Tags;
|
import io.noties.markwon.app.sample.Tags;
|
||||||
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
|
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
|
||||||
import io.noties.markwon.inlineparser.InlineProcessor;
|
import io.noties.markwon.inlineparser.InlineProcessor;
|
||||||
@ -78,7 +79,7 @@ class IssueInlineProcessor extends InlineProcessor {
|
|||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private static String createIssueOrPullRequestLinkDestination(@NonNull String id) {
|
private static String createIssueOrPullRequestLinkDestination(@NonNull String id) {
|
||||||
return "https://github.com/noties/Markwon/issues/" + id;
|
return BuildConfig.GIT_REPOSITORY + "/issues/" + id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import io.noties.markwon.MarkwonConfiguration;
|
|||||||
import io.noties.markwon.MarkwonVisitor;
|
import io.noties.markwon.MarkwonVisitor;
|
||||||
import io.noties.markwon.RenderProps;
|
import io.noties.markwon.RenderProps;
|
||||||
import io.noties.markwon.SpannableBuilder;
|
import io.noties.markwon.SpannableBuilder;
|
||||||
|
import io.noties.markwon.app.BuildConfig;
|
||||||
import io.noties.markwon.app.sample.Tags;
|
import io.noties.markwon.app.sample.Tags;
|
||||||
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
|
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
|
||||||
import io.noties.markwon.core.CorePlugin;
|
import io.noties.markwon.core.CorePlugin;
|
||||||
@ -87,7 +88,7 @@ class GithubLinkifyRegexTextAddedListener implements CorePlugin.OnTextAddedListe
|
|||||||
// issues and pull-requests on github follow the same pattern and we
|
// issues and pull-requests on github follow the same pattern and we
|
||||||
// cannot know for sure which one it is, but if we use issues for all types,
|
// cannot know for sure which one it is, but if we use issues for all types,
|
||||||
// github will automatically redirect to pull-request if it's the one which is opened
|
// github will automatically redirect to pull-request if it's the one which is opened
|
||||||
return "https://github.com/noties/Markwon/issues/" + number;
|
return BuildConfig.GIT_REPOSITORY + "/issues/" + number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -5,6 +5,7 @@ import android.view.ViewGroup
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import io.noties.markwon.Markwon
|
import io.noties.markwon.Markwon
|
||||||
|
import io.noties.markwon.app.BuildConfig
|
||||||
import io.noties.markwon.app.sample.Tags
|
import io.noties.markwon.app.sample.Tags
|
||||||
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample
|
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample
|
||||||
import io.noties.markwon.image.ImagesPlugin
|
import io.noties.markwon.image.ImagesPlugin
|
||||||
@ -23,7 +24,7 @@ class ToastDynamicContentSample : MarkwonTextViewSample() {
|
|||||||
val md = """
|
val md = """
|
||||||
# Head!
|
# Head!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Do you see an image? ☝️
|
Do you see an image? ☝️
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
@ -28,6 +28,7 @@ import java.util.List;
|
|||||||
import io.noties.markwon.Markwon;
|
import io.noties.markwon.Markwon;
|
||||||
import io.noties.markwon.MarkwonVisitor;
|
import io.noties.markwon.MarkwonVisitor;
|
||||||
import io.noties.markwon.SpannableBuilder;
|
import io.noties.markwon.SpannableBuilder;
|
||||||
|
import io.noties.markwon.app.BuildConfig;
|
||||||
import io.noties.markwon.app.R;
|
import io.noties.markwon.app.R;
|
||||||
import io.noties.markwon.app.sample.Tags;
|
import io.noties.markwon.app.sample.Tags;
|
||||||
import io.noties.markwon.app.sample.ui.MarkwonSample;
|
import io.noties.markwon.app.sample.ui.MarkwonSample;
|
||||||
@ -56,7 +57,7 @@ public class HtmlDetailsSample extends MarkwonSample {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getLayoutResId() {
|
protected int getLayoutResId() {
|
||||||
return R.layout.activity_html_details;
|
return R.layout.sample_html_details;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -83,7 +84,8 @@ public class HtmlDetailsSample extends MarkwonSample {
|
|||||||
"* list\n" +
|
"* list\n" +
|
||||||
"* with\n" +
|
"* with\n" +
|
||||||
"\n\n" +
|
"\n\n" +
|
||||||
"\n\n" +
|
"\n\n" +
|
||||||
|
"" +
|
||||||
" 1. nested\n" +
|
" 1. nested\n" +
|
||||||
" 1. items\n" +
|
" 1. items\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -2,6 +2,7 @@ package io.noties.markwon.app.samples.movementmethod
|
|||||||
|
|
||||||
import android.text.method.ScrollingMovementMethod
|
import android.text.method.ScrollingMovementMethod
|
||||||
import io.noties.markwon.Markwon
|
import io.noties.markwon.Markwon
|
||||||
|
import io.noties.markwon.app.BuildConfig
|
||||||
import io.noties.markwon.app.sample.Tags
|
import io.noties.markwon.app.sample.Tags
|
||||||
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample
|
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample
|
||||||
import io.noties.markwon.sample.annotations.MarkwonArtifact
|
import io.noties.markwon.sample.annotations.MarkwonArtifact
|
||||||
@ -22,7 +23,7 @@ class ExplicitMovementMethodSample : MarkwonTextViewSample() {
|
|||||||
If `TextView` already has a movement method specified, then `Markwon`
|
If `TextView` already has a movement method specified, then `Markwon`
|
||||||
won't be applying a default one. You can specify movement
|
won't be applying a default one. You can specify movement
|
||||||
method via call to `setMovementMethod`. If your movement method can
|
method via call to `setMovementMethod`. If your movement method can
|
||||||
handle [links](https://github.com/noties/Markwon) then link would be
|
handle [links](${BuildConfig.GIT_REPOSITORY}) then link would be
|
||||||
_clickable_
|
_clickable_
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.noties.markwon.app.samples.movementmethod
|
package io.noties.markwon.app.samples.movementmethod
|
||||||
|
|
||||||
import io.noties.markwon.Markwon
|
import io.noties.markwon.Markwon
|
||||||
|
import io.noties.markwon.app.BuildConfig
|
||||||
import io.noties.markwon.app.sample.Tags
|
import io.noties.markwon.app.sample.Tags
|
||||||
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample
|
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample
|
||||||
import io.noties.markwon.sample.annotations.MarkwonArtifact
|
import io.noties.markwon.sample.annotations.MarkwonArtifact
|
||||||
@ -18,7 +19,7 @@ class ImplicitMovementMethodSample : MarkwonTextViewSample() {
|
|||||||
val md = """
|
val md = """
|
||||||
# Implicit movement method
|
# Implicit movement method
|
||||||
By default `Markwon` applies `LinkMovementMethod` if it is missing,
|
By default `Markwon` applies `LinkMovementMethod` if it is missing,
|
||||||
so in order for [links](https://github.com/noties/Markwon) to be clickable
|
so in order for [links](${BuildConfig.GIT_REPOSITORY}) to be clickable
|
||||||
nothing special should be done
|
nothing special should be done
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
package io.noties.markwon.app.utils
|
||||||
|
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import java.io.StringWriter
|
||||||
|
|
||||||
|
object ThrowableUtils
|
||||||
|
|
||||||
|
fun Throwable.stackTraceString(): String {
|
||||||
|
val stringWriter = StringWriter()
|
||||||
|
val printWriter = PrintWriter(stringWriter)
|
||||||
|
this.printStackTrace(printWriter)
|
||||||
|
return stringWriter.toString()
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package io.noties.markwon.app.utils
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
class UncaughtExceptionHandler(private val origin: Thread.UncaughtExceptionHandler?)
|
||||||
|
: Thread.UncaughtExceptionHandler {
|
||||||
|
|
||||||
|
override fun uncaughtException(t: Thread?, e: Throwable?) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package io.noties.markwon.app.utils
|
||||||
|
|
||||||
|
import io.noties.markwon.app.App
|
||||||
|
import io.noties.markwon.app.BuildConfig
|
||||||
|
import okhttp3.Call
|
||||||
|
import okhttp3.Callback
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
object UpdateUtils {
|
||||||
|
|
||||||
|
sealed class Result {
|
||||||
|
class UpdateAvailable(val url: String) : Result()
|
||||||
|
object NoUpdate : Result()
|
||||||
|
class Error(val throwable: Throwable) : Result()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkForUpdate(updateAction: (Result) -> Unit): Cancellable {
|
||||||
|
var action: ((Result) -> Unit)? = updateAction
|
||||||
|
|
||||||
|
val future = App.executorService
|
||||||
|
.submit {
|
||||||
|
val url = "${BuildConfig.GIT_REPOSITORY}/raw/sample-store/version"
|
||||||
|
val request = Request.Builder()
|
||||||
|
.get()
|
||||||
|
.url(url)
|
||||||
|
.build()
|
||||||
|
OkHttpClient().newCall(request).enqueue(object : Callback {
|
||||||
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
action?.invoke(Result.Error(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResponse(call: Call, response: Response) {
|
||||||
|
try {
|
||||||
|
val revision = response.body()?.string()
|
||||||
|
val hasUpdate = revision != null && BuildConfig.GIT_SHA != revision
|
||||||
|
if (hasUpdate) {
|
||||||
|
action?.invoke(Result.UpdateAvailable(apkUrl))
|
||||||
|
} else {
|
||||||
|
action?.invoke(Result.NoUpdate)
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
action?.invoke(Result.Error(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return object : Cancellable {
|
||||||
|
override val isCancelled: Boolean
|
||||||
|
get() = future.isDone
|
||||||
|
|
||||||
|
override fun cancel() {
|
||||||
|
action = null
|
||||||
|
future.cancel(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val apkUrl = "${BuildConfig.GIT_REPOSITORY}/raw/sample-store/markwon-debug.apk"
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M19,4L5,4c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h4v-2L5,18L5,8h14v10h-4v2h4c1.1,0 2,-0.9 2,-2L21,6c0,-1.1 -0.89,-2 -2,-2zM12,10l-4,4h3v6h2v-6h3l-4,-4z" />
|
||||||
|
</vector>
|
15
app-sample/src/main/res/layout/adapt_check_for_update.xml
Normal file
15
app-sample/src/main/res/layout/adapt_check_for_update.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="@dimen/content_padding"
|
||||||
|
android:paddingRight="@dimen/content_padding">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button"
|
||||||
|
style="@android:style/Widget.Material.Button.Borderless.Colored"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/check_for_update" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
12
app-sample/src/main/res/layout/adapt_version.xml
Normal file
12
app-sample/src/main/res/layout/adapt_version.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/text_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="@dimen/content_padding_double"
|
||||||
|
android:paddingTop="@dimen/content_padding"
|
||||||
|
android:paddingRight="@dimen/content_padding_double"
|
||||||
|
android:paddingBottom="@dimen/content_padding"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
tools:text="Badges here" />
|
@ -48,6 +48,17 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress_bar"
|
||||||
|
style="@android:style/Widget.Material.ProgressBar.Horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="16dip"
|
||||||
|
android:layout_marginTop="-8dip"
|
||||||
|
android:layout_marginBottom="-8dip"
|
||||||
|
android:indeterminate="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
<string name="tab_bar_preview">Preview</string>
|
<string name="tab_bar_preview">Preview</string>
|
||||||
<string name="tab_bar_code">Code</string>
|
<string name="tab_bar_code">Code</string>
|
||||||
|
|
||||||
|
<string name="check_for_update">Check for update</string>
|
||||||
|
|
||||||
<string name="lorem"><![CDATA[
|
<string name="lorem"><![CDATA[
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis rutrum orci at aliquet dapibus. Quisque laoreet fermentum bibendum. Suspendisse euismod nisl vel sapien viverra faucibus. Nulla vel neque volutpat, egestas dui ac, consequat elit. Donec et interdum massa. Quisque porta ornare posuere. Nam at ante a felis facilisis tempus eu et erat. Curabitur auctor mauris eget purus iaculis vulputate.
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis rutrum orci at aliquet dapibus. Quisque laoreet fermentum bibendum. Suspendisse euismod nisl vel sapien viverra faucibus. Nulla vel neque volutpat, egestas dui ac, consequat elit. Donec et interdum massa. Quisque porta ornare posuere. Nam at ante a felis facilisis tempus eu et erat. Curabitur auctor mauris eget purus iaculis vulputate.
|
||||||
|
|
||||||
|
Binary file not shown.
@ -29,7 +29,6 @@ dependencies {
|
|||||||
testImplementation deps['commons-io']
|
testImplementation deps['commons-io']
|
||||||
|
|
||||||
deps['test'].with {
|
deps['test'].with {
|
||||||
|
|
||||||
testImplementation it['junit']
|
testImplementation it['junit']
|
||||||
testImplementation it['robolectric']
|
testImplementation it['robolectric']
|
||||||
testImplementation it['mockito']
|
testImplementation it['mockito']
|
||||||
|
Loading…
x
Reference in New Issue
Block a user