diff --git a/app-sample/build.gradle b/app-sample/build.gradle index afc24af7..2cdb4126 100644 --- a/app-sample/build.gradle +++ b/app-sample/build.gradle @@ -3,6 +3,15 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' 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 { compileSdkVersion config['compile-sdk'] @@ -17,7 +26,10 @@ android { 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' buildConfigField 'String', 'DEEPLINK_SCHEME', "\"$scheme\"" @@ -41,6 +53,37 @@ android { 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 { diff --git a/app-sample/deploy.sh b/app-sample/deploy.sh new file mode 100755 index 00000000..f4a83d14 --- /dev/null +++ b/app-sample/deploy.sh @@ -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 - \ No newline at end of file diff --git a/app-sample/keystore.jks b/app-sample/keystore.jks new file mode 100644 index 00000000..df4eb4ca Binary files /dev/null and b/app-sample/keystore.jks differ diff --git a/app-sample/release/markwon-4.4.1-SNAPSHOT-release.apk b/app-sample/release/markwon-4.4.1-SNAPSHOT-release.apk new file mode 100644 index 00000000..5033bf8a Binary files /dev/null and b/app-sample/release/markwon-4.4.1-SNAPSHOT-release.apk differ diff --git a/app-sample/release/output.json b/app-sample/release/output.json new file mode 100644 index 00000000..95ec8815 --- /dev/null +++ b/app-sample/release/output.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/app-sample/src/main/java/io/noties/markwon/app/sample/ui/SampleListFragment.kt b/app-sample/src/main/java/io/noties/markwon/app/sample/ui/SampleListFragment.kt index 16acb1aa..b9a753db 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/sample/ui/SampleListFragment.kt +++ b/app-sample/src/main/java/io/noties/markwon/app/sample/ui/SampleListFragment.kt @@ -1,8 +1,12 @@ package io.noties.markwon.app.sample.ui +import android.app.AlertDialog import android.content.Context +import android.content.Intent +import android.net.Uri import android.os.Bundle import android.os.Parcelable +import android.text.TextUtils import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -15,20 +19,26 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import io.noties.adapt.Adapt import io.noties.adapt.DiffUtilDataSetChanged +import io.noties.adapt.Item import io.noties.debug.Debug import io.noties.markwon.Markwon import io.noties.markwon.app.App +import io.noties.markwon.app.BuildConfig import io.noties.markwon.app.R import io.noties.markwon.app.readme.ReadMeActivity 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.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.UpdateUtils import io.noties.markwon.app.utils.displayName import io.noties.markwon.app.utils.hidden import io.noties.markwon.app.utils.onPreDraw 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.widget.SearchBar import io.noties.markwon.movement.MovementMethodPlugin @@ -50,6 +60,13 @@ class SampleListFragment : Fragment() { private var pendingRecyclerScrollPosition: RecyclerScrollPosition? = 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 get() = App.sampleManager @@ -73,6 +90,8 @@ class SampleListFragment : Fragment() { val context = requireContext() + progressBar = view.findViewById(R.id.progress_bar) + val searchBar: SearchBar = view.findViewById(R.id.search_bar) searchBar.onSearchListener = { search = it @@ -187,16 +206,30 @@ class SampleListFragment : Fragment() { } } - private fun bindSamples(samples: List) { - val items = samples.map { - SampleItem( - markwon, - it, - { artifact -> openArtifact(artifact) }, - { tag -> openTag(tag) }, - { sample -> openSample(sample) } - ) - } + private fun bindSamples(samples: List, addVersion: Boolean) { + + val items: List> = samples + .map { + SampleItem( + markwon, + it, + { artifact -> openArtifact(artifact) }, + { tag -> openTag(tag) }, + { sample -> openSample(sample) } + ) + } + .let { + if (addVersion) { + val list: List> = it + list.toMutableList().apply { + add(0, CheckForUpdateItem(this@SampleListFragment::checkForUpdate)) + add(0, versionItem) + } + } else { + it + } + } + adapt.setItems(items) 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) { Debug.i(artifact) openResultFragment(init(artifact)) @@ -262,7 +355,8 @@ class SampleListFragment : Fragment() { } cancellable = sampleManager.samples(sampleSearch) { - bindSamples(it) + val addVersion = sampleSearch is SampleSearch.All && TextUtils.isEmpty(sampleSearch.text) + bindSamples(it, addVersion) } } diff --git a/app-sample/src/main/java/io/noties/markwon/app/sample/ui/adapt/CheckForUpdateItem.kt b/app-sample/src/main/java/io/noties/markwon/app/sample/ui/adapt/CheckForUpdateItem.kt new file mode 100644 index 00000000..7112e4f5 --- /dev/null +++ b/app-sample/src/main/java/io/noties/markwon/app/sample/ui/adapt/CheckForUpdateItem.kt @@ -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(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) + } +} \ No newline at end of file diff --git a/app-sample/src/main/java/io/noties/markwon/app/sample/SampleItem.kt b/app-sample/src/main/java/io/noties/markwon/app/sample/ui/adapt/SampleItem.kt similarity index 97% rename from app-sample/src/main/java/io/noties/markwon/app/sample/SampleItem.kt rename to app-sample/src/main/java/io/noties/markwon/app/sample/ui/adapt/SampleItem.kt index e06f7705..8c6eb5e5 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/sample/SampleItem.kt +++ b/app-sample/src/main/java/io/noties/markwon/app/sample/ui/adapt/SampleItem.kt @@ -1,4 +1,4 @@ -package io.noties.markwon.app.sample +package io.noties.markwon.app.sample.ui.adapt import android.text.Spanned import android.view.LayoutInflater @@ -8,6 +8,7 @@ import android.widget.TextView import io.noties.adapt.Item import io.noties.markwon.Markwon 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.hidden import io.noties.markwon.app.utils.tagDisplayName diff --git a/app-sample/src/main/java/io/noties/markwon/app/sample/ui/adapt/VersionItem.kt b/app-sample/src/main/java/io/noties/markwon/app/sample/ui/adapt/VersionItem.kt new file mode 100644 index 00000000..6c090714 --- /dev/null +++ b/app-sample/src/main/java/io/noties/markwon/app/sample/ui/adapt/VersionItem.kt @@ -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(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 = """ + + + + ![stable](https://img.shields.io/maven-central/v/io.noties.markwon/core.svg?label=stable) + ![snapshot](https://img.shields.io/nexus/s/https/oss.sonatype.org/io.noties.markwon/core.svg?label=snapshot) + ![changelog](https://fonts.gstatic.com/s/i/materialicons/open_in_browser/v6/24px.svg?download=true) + + """.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 + } + } +} \ No newline at end of file diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/GithubUserIssueInlineParsingSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/GithubUserIssueInlineParsingSample.java index af942736..af4daa78 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/GithubUserIssueInlineParsingSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/GithubUserIssueInlineParsingSample.java @@ -11,6 +11,7 @@ import java.util.regex.Pattern; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.Markwon; +import io.noties.markwon.app.BuildConfig; import io.noties.markwon.app.sample.Tags; import io.noties.markwon.app.sample.ui.MarkwonTextViewSample; import io.noties.markwon.inlineparser.InlineProcessor; @@ -78,7 +79,7 @@ class IssueInlineProcessor extends InlineProcessor { @NonNull private static String createIssueOrPullRequestLinkDestination(@NonNull String id) { - return "https://github.com/noties/Markwon/issues/" + id; + return BuildConfig.GIT_REPOSITORY + "/issues/" + id; } } diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/GithubUserIssueOnTextAddedSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/GithubUserIssueOnTextAddedSample.java index 0a9e6e7c..4fc7c75c 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/GithubUserIssueOnTextAddedSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/GithubUserIssueOnTextAddedSample.java @@ -13,6 +13,7 @@ import io.noties.markwon.MarkwonConfiguration; import io.noties.markwon.MarkwonVisitor; import io.noties.markwon.RenderProps; import io.noties.markwon.SpannableBuilder; +import io.noties.markwon.app.BuildConfig; import io.noties.markwon.app.sample.Tags; import io.noties.markwon.app.sample.ui.MarkwonTextViewSample; 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 // 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 - return "https://github.com/noties/Markwon/issues/" + number; + return BuildConfig.GIT_REPOSITORY + "/issues/" + number; } @NonNull diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/ToastDynamicContentSample.kt b/app-sample/src/main/java/io/noties/markwon/app/samples/ToastDynamicContentSample.kt index 41212db2..c11147ea 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/ToastDynamicContentSample.kt +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/ToastDynamicContentSample.kt @@ -5,6 +5,7 @@ import android.view.ViewGroup import android.widget.TextView import android.widget.Toast import io.noties.markwon.Markwon +import io.noties.markwon.app.BuildConfig import io.noties.markwon.app.sample.Tags import io.noties.markwon.app.sample.ui.MarkwonTextViewSample import io.noties.markwon.image.ImagesPlugin @@ -23,7 +24,7 @@ class ToastDynamicContentSample : MarkwonTextViewSample() { val md = """ # Head! - ![alt](https://github.com/noties/Markwon/raw/master/art/markwon_logo.png) + ![alt](${BuildConfig.GIT_REPOSITORY}/raw/master/art/markwon_logo.png) Do you see an image? ☝️ """.trimIndent() diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/html/HtmlDetailsSample.java b/app-sample/src/main/java/io/noties/markwon/app/samples/html/HtmlDetailsSample.java index a63f44d9..dba85a94 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/html/HtmlDetailsSample.java +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/html/HtmlDetailsSample.java @@ -28,6 +28,7 @@ import java.util.List; import io.noties.markwon.Markwon; import io.noties.markwon.MarkwonVisitor; import io.noties.markwon.SpannableBuilder; +import io.noties.markwon.app.BuildConfig; import io.noties.markwon.app.R; import io.noties.markwon.app.sample.Tags; import io.noties.markwon.app.sample.ui.MarkwonSample; @@ -56,7 +57,7 @@ public class HtmlDetailsSample extends MarkwonSample { @Override protected int getLayoutResId() { - return R.layout.activity_html_details; + return R.layout.sample_html_details; } @Override @@ -83,7 +84,8 @@ public class HtmlDetailsSample extends MarkwonSample { "* list\n" + "* with\n" + "\n\n" + - "![img](https://raw.githubusercontent.com/noties/Markwon/master/art/markwon_logo.png)\n\n" + + "![img](" + BuildConfig.GIT_REPOSITORY + "/raw/master/art/markwon_logo.png)\n\n" + + "" + " 1. nested\n" + " 1. items\n" + "\n" + diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/movementmethod/ExplicitMovementMethodSample.kt b/app-sample/src/main/java/io/noties/markwon/app/samples/movementmethod/ExplicitMovementMethodSample.kt index 224e4714..3379e63a 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/movementmethod/ExplicitMovementMethodSample.kt +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/movementmethod/ExplicitMovementMethodSample.kt @@ -2,6 +2,7 @@ package io.noties.markwon.app.samples.movementmethod import android.text.method.ScrollingMovementMethod import io.noties.markwon.Markwon +import io.noties.markwon.app.BuildConfig import io.noties.markwon.app.sample.Tags import io.noties.markwon.app.sample.ui.MarkwonTextViewSample import io.noties.markwon.sample.annotations.MarkwonArtifact @@ -22,7 +23,7 @@ class ExplicitMovementMethodSample : MarkwonTextViewSample() { If `TextView` already has a movement method specified, then `Markwon` won't be applying a default one. You can specify movement 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_ """.trimIndent() diff --git a/app-sample/src/main/java/io/noties/markwon/app/samples/movementmethod/ImplicitMovementMethodSample.kt b/app-sample/src/main/java/io/noties/markwon/app/samples/movementmethod/ImplicitMovementMethodSample.kt index 81f1944a..2cf569fc 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/samples/movementmethod/ImplicitMovementMethodSample.kt +++ b/app-sample/src/main/java/io/noties/markwon/app/samples/movementmethod/ImplicitMovementMethodSample.kt @@ -1,6 +1,7 @@ package io.noties.markwon.app.samples.movementmethod import io.noties.markwon.Markwon +import io.noties.markwon.app.BuildConfig import io.noties.markwon.app.sample.Tags import io.noties.markwon.app.sample.ui.MarkwonTextViewSample import io.noties.markwon.sample.annotations.MarkwonArtifact @@ -18,7 +19,7 @@ class ImplicitMovementMethodSample : MarkwonTextViewSample() { val md = """ # Implicit movement method 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 """.trimIndent() diff --git a/app-sample/src/main/java/io/noties/markwon/app/utils/ThrowableUtils.kt b/app-sample/src/main/java/io/noties/markwon/app/utils/ThrowableUtils.kt new file mode 100644 index 00000000..4c753895 --- /dev/null +++ b/app-sample/src/main/java/io/noties/markwon/app/utils/ThrowableUtils.kt @@ -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() +} \ No newline at end of file diff --git a/app-sample/src/main/java/io/noties/markwon/app/utils/UncaughtExceptionHandler.kt b/app-sample/src/main/java/io/noties/markwon/app/utils/UncaughtExceptionHandler.kt new file mode 100644 index 00000000..56fd6598 --- /dev/null +++ b/app-sample/src/main/java/io/noties/markwon/app/utils/UncaughtExceptionHandler.kt @@ -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?) { + + } +} \ No newline at end of file diff --git a/app-sample/src/main/java/io/noties/markwon/app/utils/UpdateUtils.kt b/app-sample/src/main/java/io/noties/markwon/app/utils/UpdateUtils.kt new file mode 100644 index 00000000..d9a225e8 --- /dev/null +++ b/app-sample/src/main/java/io/noties/markwon/app/utils/UpdateUtils.kt @@ -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" +} \ No newline at end of file diff --git a/app-sample/src/main/res/drawable/ic_open_in_browser_white_24dp.xml b/app-sample/src/main/res/drawable/ic_open_in_browser_white_24dp.xml new file mode 100644 index 00000000..168a069c --- /dev/null +++ b/app-sample/src/main/res/drawable/ic_open_in_browser_white_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app-sample/src/main/res/layout/adapt_check_for_update.xml b/app-sample/src/main/res/layout/adapt_check_for_update.xml new file mode 100644 index 00000000..6f3446e2 --- /dev/null +++ b/app-sample/src/main/res/layout/adapt_check_for_update.xml @@ -0,0 +1,15 @@ + + + +