diff --git a/app-sample/build.gradle b/app-sample/build.gradle index a340eeaf..afc24af7 100644 --- a/app-sample/build.gradle +++ b/app-sample/build.gradle @@ -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 { diff --git a/app-sample/src/main/AndroidManifest.xml b/app-sample/src/main/AndroidManifest.xml index ac691724..411c6a42 100644 --- a/app-sample/src/main/AndroidManifest.xml +++ b/app-sample/src/main/AndroidManifest.xml @@ -16,10 +16,20 @@ tools:ignore="AllowBackup,GoogleAppIndexingWarning"> + + + + + + + + + + - - + + diff --git a/app-sample/src/main/java/io/noties/markwon/app/sample/Deeplink.kt b/app-sample/src/main/java/io/noties/markwon/app/sample/Deeplink.kt new file mode 100644 index 00000000..65034fa4 --- /dev/null +++ b/app-sample/src/main/java/io/noties/markwon/app/sample/Deeplink.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/app-sample/src/main/java/io/noties/markwon/app/sample/MainActivity.kt b/app-sample/src/main/java/io/noties/markwon/app/sample/MainActivity.kt index e60c63d7..c3405494 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/sample/MainActivity.kt +++ b/app-sample/src/main/java/io/noties/markwon/app/sample/MainActivity.kt @@ -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() + } } } } \ No newline at end of file diff --git a/app-sample/src/main/java/io/noties/markwon/app/sample/SampleManager.kt b/app-sample/src/main/java/io/noties/markwon/app/sample/SampleManager.kt index b45fd616..d0021d53 100644 --- a/app-sample/src/main/java/io/noties/markwon/app/sample/SampleManager.kt +++ b/app-sample/src/main/java/io/noties/markwon/app/sample/SampleManager.kt @@ -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) -> Unit): Cancellable { var action: ((List) -> Unit)? = callback 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 bf5154e2..16acb1aa 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 @@ -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() + } } - - 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) { @@ -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())