Sample app deeplinking
This commit is contained in:
parent
9dce1c8533
commit
78ec885294
@ -18,6 +18,12 @@ android {
|
||||
resConfig 'en'
|
||||
|
||||
setProperty("archivesBaseName", "markwon-$versionName")
|
||||
|
||||
final def scheme = 'markwon'
|
||||
buildConfigField 'String', 'DEEPLINK_SCHEME', "\"$scheme\""
|
||||
manifestPlaceholders += [
|
||||
'deeplink_scheme': scheme
|
||||
]
|
||||
}
|
||||
|
||||
dexOptions {
|
||||
|
@ -16,10 +16,20 @@
|
||||
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
|
||||
|
||||
<activity android:name=".sample.MainActivity">
|
||||
<!-- launcher intent -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="${deeplink_scheme}" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
@ -39,8 +49,8 @@
|
||||
|
||||
<data android:pathPattern=".*\\.md" />
|
||||
<data android:pathPattern=".*\\..*\\.md" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\.md"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.md"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\.md" />
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.md" />
|
||||
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
@ -0,0 +1,73 @@
|
||||
package io.noties.markwon.app.sample
|
||||
|
||||
import android.net.Uri
|
||||
import io.noties.debug.Debug
|
||||
import io.noties.markwon.app.BuildConfig
|
||||
import io.noties.markwon.sample.annotations.MarkwonArtifact
|
||||
|
||||
sealed class Deeplink {
|
||||
data class Sample(val id: String) : Deeplink()
|
||||
data class Search(val search: SampleSearch) : Deeplink()
|
||||
|
||||
companion object {
|
||||
fun parse(data: Uri?): Deeplink? {
|
||||
Debug.i(data)
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val data = data ?: return null
|
||||
if (BuildConfig.DEEPLINK_SCHEME != data.scheme) return null
|
||||
return when (data.host) {
|
||||
// markwon://sample/20202827
|
||||
"sample" -> parseSample(data.lastPathSegment)
|
||||
// markwon://search?a=ext-latex&q=text
|
||||
"search" -> parseSearch(data.query)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseSample(id: String?): Sample? {
|
||||
if (id == null) return null
|
||||
return Sample(id)
|
||||
}
|
||||
|
||||
private fun parseSearch(query: String?): Search? {
|
||||
Debug.i("query: '$query'")
|
||||
|
||||
val params = query
|
||||
?.split("&")
|
||||
?.map {
|
||||
val (k, v) = it.split("=")
|
||||
Pair(k, v)
|
||||
}
|
||||
?.toMap()
|
||||
?: return null
|
||||
|
||||
val artifact = params["a"]
|
||||
val tag = params["t"]
|
||||
val search = params["q"]
|
||||
|
||||
Debug.i("artifact: '$artifact', tag: '$tag', search: '$search'")
|
||||
|
||||
val sampleSearch: SampleSearch? = if (artifact != null) {
|
||||
val encodedArtifact = MarkwonArtifact.values()
|
||||
.firstOrNull { it.artifactName() == artifact }
|
||||
if (encodedArtifact != null) {
|
||||
SampleSearch.Artifact(search, encodedArtifact)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else if (tag != null) {
|
||||
SampleSearch.Tag(search, tag)
|
||||
} else if (search != null) {
|
||||
SampleSearch.All(search)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
if (sampleSearch == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return Search(sampleSearch)
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,10 @@ package io.noties.markwon.app.sample
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Window
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import io.noties.markwon.app.App
|
||||
import io.noties.markwon.app.sample.ui.SampleFragment
|
||||
import io.noties.markwon.app.sample.ui.SampleListFragment
|
||||
|
||||
class MainActivity : FragmentActivity() {
|
||||
@ -11,9 +14,27 @@ class MainActivity : FragmentActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (supportFragmentManager.findFragmentById(Window.ID_ANDROID_CONTENT) == null) {
|
||||
|
||||
supportFragmentManager.beginTransaction()
|
||||
.add(Window.ID_ANDROID_CONTENT, SampleListFragment.init())
|
||||
.commitNowAllowingStateLoss()
|
||||
|
||||
// process deeplink if we are not restored
|
||||
val deeplink = Deeplink.parse(intent.data)
|
||||
val deepLinkFragment: Fragment? = if (deeplink != null) {
|
||||
when (deeplink) {
|
||||
is Deeplink.Sample -> App.sampleManager.sample(deeplink.id)
|
||||
?.let { SampleFragment.init(it) }
|
||||
is Deeplink.Search -> SampleListFragment.init(deeplink.search)
|
||||
}
|
||||
} else null
|
||||
|
||||
if (deepLinkFragment != null) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(Window.ID_ANDROID_CONTENT, deepLinkFragment)
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -15,6 +15,10 @@ class SampleManager(
|
||||
SampleUtils.readSamples(context)
|
||||
}
|
||||
|
||||
fun sample(id: String): Sample? {
|
||||
return samples.firstOrNull { id == it.id }
|
||||
}
|
||||
|
||||
fun samples(search: SampleSearch?, callback: (List<Sample>) -> Unit): Cancellable {
|
||||
|
||||
var action: ((List<Sample>) -> Unit)? = callback
|
||||
|
@ -98,11 +98,20 @@ class SampleListFragment : Fragment() {
|
||||
// }
|
||||
|
||||
val state: State? = arguments?.getParcelable(STATE)
|
||||
Debug.i(state)
|
||||
val initialSearch = arguments?.getString(ARG_SEARCH)
|
||||
|
||||
// clear it anyway
|
||||
arguments?.remove(ARG_SEARCH)
|
||||
|
||||
Debug.i(state, initialSearch)
|
||||
|
||||
pendingRecyclerScrollPosition = state?.recyclerScrollPosition
|
||||
if (state?.search != null) {
|
||||
searchBar.search(state.search)
|
||||
|
||||
val search = listOf(state?.search, initialSearch)
|
||||
.firstOrNull { it != null }
|
||||
|
||||
if (search != null) {
|
||||
searchBar.search(search)
|
||||
} else {
|
||||
fetch()
|
||||
}
|
||||
@ -144,35 +153,38 @@ class SampleListFragment : Fragment() {
|
||||
val appBarTitle: TextView = appBar.findViewById(R.id.app_bar_title)
|
||||
val appBarIconReadme: ImageView = appBar.findViewById(R.id.app_bar_icon_readme)
|
||||
|
||||
val isInitialScreen = type is Type.All
|
||||
val isInitialScreen = fragmentManager?.backStackEntryCount == 0
|
||||
|
||||
appBarIcon.hidden = isInitialScreen
|
||||
appBarIconReadme.hidden = !isInitialScreen
|
||||
|
||||
val type = this.type
|
||||
if (type is Type.All) {
|
||||
|
||||
val (text, background) = when (type) {
|
||||
is Type.Artifact -> Pair(type.artifact.displayName, R.drawable.bg_artifact)
|
||||
is Type.Tag -> Pair(type.tag.tagDisplayName, R.drawable.bg_tag)
|
||||
is Type.All -> Pair(resources.getString(R.string.app_name), 0)
|
||||
}
|
||||
|
||||
appBarTitle.text = text
|
||||
|
||||
if (background != 0) {
|
||||
appBarTitle.setBackgroundResource(background)
|
||||
}
|
||||
|
||||
if (isInitialScreen) {
|
||||
appBarIconReadme.setOnClickListener {
|
||||
context?.let {
|
||||
val intent = ReadMeActivity.makeIntent(it)
|
||||
it.startActivity(intent)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
} else {
|
||||
appBarIcon.setImageResource(R.drawable.ic_arrow_back_white_24dp)
|
||||
appBarIcon.setOnClickListener {
|
||||
requireActivity().onBackPressed()
|
||||
}
|
||||
|
||||
val (text, background) = when (type) {
|
||||
is Type.Artifact -> Pair(type.artifact.displayName, R.drawable.bg_artifact)
|
||||
is Type.Tag -> Pair(type.tag.tagDisplayName, R.drawable.bg_tag)
|
||||
else -> error("Unexpected type: $type")
|
||||
}
|
||||
|
||||
appBarTitle.text = text
|
||||
appBarTitle.setBackgroundResource(background)
|
||||
}
|
||||
|
||||
private fun bindSamples(samples: List<Sample>) {
|
||||
@ -192,7 +204,6 @@ class SampleListFragment : Fragment() {
|
||||
val scrollPosition = pendingRecyclerScrollPosition
|
||||
|
||||
Debug.i(scrollPosition)
|
||||
Debug.trace()
|
||||
|
||||
if (scrollPosition != null) {
|
||||
pendingRecyclerScrollPosition = null
|
||||
@ -242,7 +253,6 @@ class SampleListFragment : Fragment() {
|
||||
}
|
||||
|
||||
Debug.i(sampleSearch)
|
||||
Debug.trace()
|
||||
|
||||
// clear current
|
||||
cancellable?.let {
|
||||
@ -259,6 +269,7 @@ class SampleListFragment : Fragment() {
|
||||
companion object {
|
||||
private const val ARG_ARTIFACT = "arg.Artifact"
|
||||
private const val ARG_TAG = "arg.Tag"
|
||||
private const val ARG_SEARCH = "arg.Search"
|
||||
private const val STATE = "key.State"
|
||||
|
||||
fun init(): SampleListFragment {
|
||||
@ -283,6 +294,23 @@ class SampleListFragment : Fragment() {
|
||||
return fragment
|
||||
}
|
||||
|
||||
fun init(search: SampleSearch): SampleListFragment {
|
||||
val fragment = SampleListFragment()
|
||||
fragment.arguments = Bundle().apply {
|
||||
|
||||
when (search) {
|
||||
is SampleSearch.Artifact -> putString(ARG_ARTIFACT, search.artifact.name)
|
||||
is SampleSearch.Tag -> putString(ARG_TAG, search.tag)
|
||||
}
|
||||
|
||||
val query = search.text
|
||||
if (query != null) {
|
||||
putString(ARG_SEARCH, query)
|
||||
}
|
||||
}
|
||||
return fragment
|
||||
}
|
||||
|
||||
fun markwon(context: Context): Markwon {
|
||||
return Markwon.builder(context)
|
||||
.usePlugin(MovementMethodPlugin.none())
|
||||
|
Loading…
x
Reference in New Issue
Block a user