Sample app, bind list results, search
This commit is contained in:
		
							parent
							
								
									7e8ed3ea0b
								
							
						
					
					
						commit
						66f77f35fe
					
				| @ -1,6 +1,7 @@ | ||||
| apply plugin: 'com.android.application' | ||||
| apply plugin: 'kotlin-android' | ||||
| apply plugin: 'kotlin-kapt' | ||||
| apply plugin: 'kotlin-android-extensions' | ||||
| 
 | ||||
| android { | ||||
| 
 | ||||
| @ -40,14 +41,23 @@ kapt { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| androidExtensions { | ||||
|     features = ["parcelize"] | ||||
| } | ||||
| 
 | ||||
| dependencies { | ||||
|     kapt project(':sample-utils:processor') | ||||
| 
 | ||||
|     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" | ||||
| 
 | ||||
|     implementation project(':markwon-core') | ||||
| 
 | ||||
|     deps.with { | ||||
|         api it['x-recycler-view'] | ||||
|         api it['adapt'] | ||||
|         api it['debug'] | ||||
|         implementation it['x-recycler-view'] | ||||
|         implementation it['x-cardview'] | ||||
|         implementation it['x-fragment'] | ||||
|         implementation it['gson'] | ||||
|         implementation it['adapt'] | ||||
|         implementation it['debug'] | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										166
									
								
								app-sample/src/debug/res/layout/flowlayout_preview.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								app-sample/src/debug/res/layout/flowlayout_preview.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,166 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:orientation="vertical" | ||||
|     android:padding="@dimen/content_padding" | ||||
|     tools:ignore="MissingDefaultResource,HardcodedText"> | ||||
| 
 | ||||
|     <TextView | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:text="Hey!" /> | ||||
| 
 | ||||
|     <io.noties.markwon.app.base.FlowLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:paddingTop="@dimen/content_padding" | ||||
|         android:paddingBottom="@dimen/content_padding" | ||||
|         app:fl_spacingHorizontal="@dimen/content_padding" | ||||
|         app:fl_spacingVertical="4dip"> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_artifact" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_artifact" | ||||
|             android:text="ext-latex" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_artifact" | ||||
|             android:text="another" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_artifact" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_artifact" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_artifact" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_artifact" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_artifact" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_artifact" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_artifact" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_artifact" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_artifact" | ||||
|             android:text="corejksjk sjkdf sdhjf sdjhf sjjksdjkjksd sdjksd hjsd hsdhjhjs shjsdhjhjsdhj sdj dshjs dhjsd sdhj sdjhsd hjsdh sjhd sdhjsd jhsd sdhj sdjhsd hjsd sjdh s" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_artifact" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|     </io.noties.markwon.app.base.FlowLayout> | ||||
| 
 | ||||
|     <io.noties.markwon.app.base.FlowLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:paddingTop="@dimen/content_padding" | ||||
|         android:paddingBottom="@dimen/content_padding" | ||||
|         app:fl_spacingHorizontal="@dimen/content_padding" | ||||
|         app:fl_spacingVertical="4dip"> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_tag" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_tag" | ||||
|             android:text="ext-latex" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_tag" | ||||
|             android:text="another" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_tag" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_tag" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_tag" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_tag" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_tag" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_tag" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_tag" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_tag" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_tag" | ||||
|             android:text="corejksjk sjkdf sdhjf sdjhf sjjksdjkjksd sdjksd hjsd hsdhjhjs shjsdhjhjsdhj sdj dshjs dhjsd sdhj sdjhsd hjsdh sjhd sdhjsd jhsd sdhj sdjhsd hjsd sjdh s" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             style="@style/ArtifactTagText" | ||||
|             android:background="@drawable/bg_tag" | ||||
|             android:text="core" /> | ||||
| 
 | ||||
|     </io.noties.markwon.app.base.FlowLayout> | ||||
| 
 | ||||
| </LinearLayout> | ||||
| @ -3,12 +3,20 @@ package io.noties.markwon.app | ||||
| import android.app.Application | ||||
| import io.noties.debug.AndroidLogDebugOutput | ||||
| import io.noties.debug.Debug | ||||
| import java.util.concurrent.Executors | ||||
| 
 | ||||
| @Suppress("unused") | ||||
| class App : Application() { | ||||
| 
 | ||||
|     override fun onCreate() { | ||||
|         super.onCreate() | ||||
| 
 | ||||
|         Debug.init(AndroidLogDebugOutput(BuildConfig.DEBUG)) | ||||
| 
 | ||||
|         sampleManager = SampleManager(this, Executors.newCachedThreadPool()) | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         lateinit var sampleManager: SampleManager | ||||
|     } | ||||
| } | ||||
| @ -1,61 +1,19 @@ | ||||
| package io.noties.markwon.app | ||||
| 
 | ||||
| import android.app.Activity | ||||
| import android.os.Bundle | ||||
| import android.view.ViewTreeObserver | ||||
| import androidx.recyclerview.widget.DefaultItemAnimator | ||||
| import androidx.recyclerview.widget.LinearLayoutManager | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import io.noties.adapt.Adapt | ||||
| import io.noties.debug.Debug | ||||
| import io.noties.markwon.app.adapt.SampleItem | ||||
| import io.noties.markwon.app.base.SearchBar | ||||
| import io.noties.markwon.sample.annotations.MarkwonArtifact | ||||
| import android.view.Window | ||||
| import androidx.fragment.app.FragmentActivity | ||||
| import io.noties.markwon.app.ui.SampleListFragment | ||||
| 
 | ||||
| class MainActivity : Activity() { | ||||
| class MainActivity : FragmentActivity() { | ||||
| 
 | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
|         setContentView(R.layout.activity_main) | ||||
| 
 | ||||
|         val searchBar: SearchBar = findViewById(R.id.search_bar) | ||||
|         searchBar.onSearchListener = { | ||||
|             Debug.i("search: '$it'") | ||||
|         if (supportFragmentManager.findFragmentById(Window.ID_ANDROID_CONTENT) == null) { | ||||
|             supportFragmentManager.beginTransaction() | ||||
|                     .add(Window.ID_ANDROID_CONTENT, SampleListFragment.init()) | ||||
|                     .commitNowAllowingStateLoss() | ||||
|         } | ||||
| 
 | ||||
|         val recyclerView: RecyclerView = findViewById(R.id.recycler_view) | ||||
|         recyclerView.layoutManager = LinearLayoutManager(this) | ||||
|         recyclerView.itemAnimator = DefaultItemAnimator() | ||||
|         recyclerView.setHasFixedSize(true) | ||||
|         recyclerView.clipToPadding = false | ||||
| 
 | ||||
|         searchBar.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener { | ||||
|             override fun onPreDraw(): Boolean { | ||||
|                 searchBar.viewTreeObserver.removeOnPreDrawListener(this) | ||||
|                 recyclerView.setPadding( | ||||
|                         recyclerView.paddingLeft, | ||||
|                         recyclerView.paddingTop + searchBar.height, | ||||
|                         recyclerView.paddingRight, | ||||
|                         recyclerView.paddingBottom | ||||
|                 ) | ||||
|                 return true | ||||
|             } | ||||
|         }) | ||||
| 
 | ||||
|         val adapt = Adapt.create() | ||||
|         recyclerView.adapter = adapt | ||||
| 
 | ||||
|         val list = listOf( | ||||
|                 MarkwonSampleItem( | ||||
|                         "first", | ||||
|                         "1", | ||||
|                         "Title first", | ||||
|                         "Description her egoes and goes ang goes, so will it ever stop?", | ||||
|                         listOf(MarkwonArtifact.CORE, MarkwonArtifact.EDITOR), | ||||
|                         listOf("first", "second") | ||||
|                 ) | ||||
|         ) | ||||
| 
 | ||||
|         adapt.setItems(list.map { SampleItem(it) }) | ||||
|     } | ||||
| } | ||||
| @ -1,5 +0,0 @@ | ||||
| package io.noties.markwon.app | ||||
| 
 | ||||
| abstract class MarkwonSample { | ||||
| 
 | ||||
| } | ||||
| @ -1,12 +1,15 @@ | ||||
| package io.noties.markwon.app | ||||
| 
 | ||||
| import android.os.Parcelable | ||||
| import io.noties.markwon.sample.annotations.MarkwonArtifact | ||||
| import kotlinx.android.parcel.Parcelize | ||||
| 
 | ||||
| data class MarkwonSampleItem( | ||||
| @Parcelize | ||||
| data class Sample( | ||||
|         val javaClassName: String, | ||||
|         val id: String, | ||||
|         val title: String, | ||||
|         val description: String, | ||||
|         val artifacts: List<MarkwonArtifact>, | ||||
|         val tags: List<String> | ||||
| ) | ||||
| ) : Parcelable | ||||
| @ -0,0 +1,71 @@ | ||||
| package io.noties.markwon.app | ||||
| 
 | ||||
| import android.content.Context | ||||
| import io.noties.markwon.app.utils.Cancellable | ||||
| import io.noties.markwon.app.utils.SampleUtils | ||||
| import io.noties.markwon.sample.annotations.MarkwonArtifact | ||||
| import java.util.concurrent.ExecutorService | ||||
| 
 | ||||
| class SampleManager( | ||||
|         private val context: Context, | ||||
|         private val executorService: ExecutorService | ||||
| ) { | ||||
| 
 | ||||
|     private val samples: List<Sample> by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { | ||||
|         SampleUtils.readSamples(context) | ||||
|     } | ||||
| 
 | ||||
|     fun samples(search: SampleSearch?, callback: (List<Sample>) -> Unit): Cancellable { | ||||
| 
 | ||||
|         var action: ((List<Sample>) -> Unit)? = callback | ||||
| 
 | ||||
|         val future = executorService.submit { | ||||
| 
 | ||||
|             val source = when (search) { | ||||
|                 is SampleSearch.Artifact -> samples.filter { it.artifacts.contains(search.artifact) } | ||||
|                 is SampleSearch.Tag -> samples.filter { it.tags.contains(search.tag) } | ||||
|                 else -> samples.toList() // just copy all | ||||
|             } | ||||
| 
 | ||||
|             val text = search?.text | ||||
|             val results = if (text == null) { | ||||
|                 // no further filtering, just return the full source here | ||||
|                 source | ||||
|             } else { | ||||
|                 source.filter { filter(it, text) } | ||||
|             } | ||||
| 
 | ||||
|             action?.invoke(results) | ||||
|         } | ||||
| 
 | ||||
|         return object : Cancellable { | ||||
|             override val isCancelled: Boolean | ||||
|                 get() = future.isDone | ||||
| 
 | ||||
|             override fun cancel() { | ||||
|                 action = null | ||||
|                 future.cancel(true) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // if title contains, | ||||
|     // if description contains, | ||||
|     // if tags contains | ||||
|     // if artifacts contains, | ||||
|     private fun filter(sample: Sample, text: String): Boolean { | ||||
|         return sample.javaClassName.contains(text, true) | ||||
|                 || sample.title.contains(text, true) | ||||
|                 || sample.description.contains(text, true) | ||||
|                 || filterTags(sample.tags, text) | ||||
|                 || filterArtifacts(sample.artifacts, text) | ||||
|     } | ||||
| 
 | ||||
|     private fun filterTags(tags: List<String>, text: String): Boolean { | ||||
|         return tags.firstOrNull { it.contains(text, true) } != null | ||||
|     } | ||||
| 
 | ||||
|     private fun filterArtifacts(artifacts: List<MarkwonArtifact>, text: String): Boolean { | ||||
|         return artifacts.firstOrNull { it.artifactName().contains(text, true) } != null | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,13 @@ | ||||
| package io.noties.markwon.app | ||||
| 
 | ||||
| import io.noties.markwon.sample.annotations.MarkwonArtifact | ||||
| 
 | ||||
| sealed class SampleSearch(val text: String?) { | ||||
|     class Artifact(text: String?, val artifact: MarkwonArtifact) : SampleSearch(text) | ||||
|     class Tag(text: String?, val tag: String) : SampleSearch(text) | ||||
|     class All(text: String?) : SampleSearch(text) | ||||
| 
 | ||||
|     override fun toString(): String { | ||||
|         return "SampleSearch(text=$text,type=${javaClass.simpleName})" | ||||
|     } | ||||
| } | ||||
| @ -1,22 +0,0 @@ | ||||
| package io.noties.markwon.app.adapt | ||||
| 
 | ||||
| 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.sample.annotations.MarkwonArtifact | ||||
| 
 | ||||
| class ArtifactItem(artifact: MarkwonArtifact): Item<ArtifactItem.Holder>(artifact.name.hashCode().toLong()) { | ||||
| 
 | ||||
|     override fun createHolder(inflater: LayoutInflater, parent: ViewGroup): Holder { | ||||
|         return Holder(inflater.inflate(0, parent, false)) | ||||
|     } | ||||
| 
 | ||||
|     override fun render(holder: Holder) { | ||||
|     } | ||||
| 
 | ||||
|     class Holder(itemView: View): Item.Holder(itemView) { | ||||
|         val textView: TextView = requireView(0) | ||||
|     } | ||||
| } | ||||
| @ -1,89 +1,100 @@ | ||||
| package io.noties.markwon.app.adapt | ||||
| 
 | ||||
| import android.graphics.Color | ||||
| import android.text.Spannable | ||||
| import android.text.SpannableStringBuilder | ||||
| import android.text.TextPaint | ||||
| import android.text.method.LinkMovementMethod | ||||
| import android.text.style.ClickableSpan | ||||
| import android.text.Spanned | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import android.widget.TextView | ||||
| import io.noties.adapt.Item | ||||
| import io.noties.debug.Debug | ||||
| import io.noties.markwon.app.MarkwonSampleItem | ||||
| import io.noties.markwon.Markwon | ||||
| import io.noties.markwon.app.R | ||||
| import io.noties.markwon.app.Sample | ||||
| import io.noties.markwon.app.base.FlowLayout | ||||
| import io.noties.markwon.app.utils.displayName | ||||
| import io.noties.markwon.app.utils.hidden | ||||
| import io.noties.markwon.app.utils.tagDisplayName | ||||
| import io.noties.markwon.sample.annotations.MarkwonArtifact | ||||
| import io.noties.markwon.utils.NoCopySpannableFactory | ||||
| 
 | ||||
| class SampleItem(private val item: MarkwonSampleItem) : Item<SampleItem.Holder>(item.id.hashCode().toLong()) { | ||||
| class SampleItem( | ||||
|         private val markwon: Markwon, | ||||
|         private val sample: Sample, | ||||
|         private val onArtifactClick: (MarkwonArtifact) -> Unit, | ||||
|         private val onTagClick: (String) -> Unit, | ||||
|         private val onSampleClick: (Sample) -> Unit | ||||
| ) : Item<SampleItem.Holder>(sample.id.hashCode().toLong()) { | ||||
| 
 | ||||
|     var search: String? = null | ||||
| //    var search: String? = null | ||||
| 
 | ||||
|     private val text: Spanned by lazy(LazyThreadSafetyMode.NONE) { | ||||
|         markwon.toMarkdown(sample.description) | ||||
|     } | ||||
| 
 | ||||
|     override fun createHolder(inflater: LayoutInflater, parent: ViewGroup): Holder { | ||||
|         val holder = Holder(inflater.inflate(R.layout.adapt_sample, parent, false)) | ||||
|         holder.artifactsAndTags.movementMethod = LinkMovementMethod.getInstance() | ||||
|         return holder | ||||
|         return Holder(inflater.inflate(R.layout.adapt_sample, parent, false)).apply { | ||||
|             description.setSpannableFactory(NoCopySpannableFactory.getInstance()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun render(holder: Holder) { | ||||
|         holder.apply { | ||||
|             title.text = item.title | ||||
|             description.text = item.description | ||||
|             artifactsAndTags.text = buildArtifactsAndTags | ||||
|         } | ||||
|     } | ||||
|             title.text = sample.title | ||||
| 
 | ||||
|     private val buildArtifactsAndTags: CharSequence | ||||
|         get() { | ||||
|             val builder = SpannableStringBuilder() | ||||
| 
 | ||||
|             item.artifacts | ||||
|                     .forEach { | ||||
|                         val length = builder.length | ||||
|                         builder.append("\u00a0${it.name}\u00a0") | ||||
|                         builder.setSpan(ArtifactSpan(it), length, builder.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) | ||||
|                     } | ||||
| 
 | ||||
|             if (!builder.isEmpty()) { | ||||
|                 builder.append("\n") | ||||
|             val text = this@SampleItem.text | ||||
|             if (text.isEmpty()) { | ||||
|                 description.text = "" | ||||
|                 description.hidden = true | ||||
|             } else { | ||||
|                 markwon.setParsedMarkdown(description, text) | ||||
|                 description.hidden = false | ||||
|             } | ||||
| 
 | ||||
|             item.tags | ||||
|                     .forEach { | ||||
|                         val length = builder.length | ||||
|                         builder.append("\u00a0$it\u00a0") | ||||
|                         builder.setSpan(TagSpan(it), length, builder.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) | ||||
|             // there is no need to display the core artifact (it is implicit), | ||||
|             //  hide if empty (removed core) | ||||
|             artifacts.ensure(sample.artifacts.size, R.layout.view_artifact) | ||||
|                     .zip(sample.artifacts) | ||||
|                     .forEach { (view, artifact) -> | ||||
|                         (view as TextView).text = artifact.displayName | ||||
|                         view.setOnClickListener { | ||||
|                             onArtifactClick(artifact) | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|             return builder | ||||
|             tags.ensure(sample.tags.size, R.layout.view_tag) | ||||
|                     .zip(sample.tags) | ||||
|                     .forEach { (view, tag) -> | ||||
|                         (view as TextView).text = tag.tagDisplayName | ||||
|                         view.setOnClickListener { | ||||
|                             onTagClick(tag) | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|             itemView.setOnClickListener { | ||||
|                 onSampleClick(sample) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     class Holder(itemView: View) : Item.Holder(itemView) { | ||||
|         val title: TextView = requireView(R.id.title) | ||||
|         val description: TextView = requireView(R.id.description) | ||||
|         val artifactsAndTags: TextView = requireView(R.id.artifacts_and_tags) | ||||
|         val artifacts: FlowLayout = requireView(R.id.artifacts) | ||||
|         val tags: FlowLayout = requireView(R.id.tags) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     private class ArtifactSpan(val artifact: MarkwonArtifact) : ClickableSpan() { | ||||
|         override fun onClick(widget: View) { | ||||
|             Debug.i("clicked artifact: $artifact") | ||||
| private fun FlowLayout.ensure(viewsCount: Int, layoutResId: Int): List<View> { | ||||
|     if (viewsCount > childCount) { | ||||
|         // inflate new views | ||||
|         val inflater = LayoutInflater.from(context) | ||||
|         for (i in 0 until (viewsCount - childCount)) { | ||||
|             addView(inflater.inflate(layoutResId, this, false)) | ||||
|         } | ||||
| 
 | ||||
|         override fun updateDrawState(ds: TextPaint) { | ||||
|             ds.isUnderlineText = false | ||||
|             ds.bgColor = Color.GREEN | ||||
|     } else { | ||||
|         // return requested vies and GONE the rest | ||||
|         for (i in viewsCount until childCount) { | ||||
|             getChildAt(i).hidden = true | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private class TagSpan(val tag: String) : ClickableSpan() { | ||||
|         override fun onClick(widget: View) { | ||||
|             Debug.i("clicked tag: $tag") | ||||
|         } | ||||
| 
 | ||||
|         override fun updateDrawState(ds: TextPaint) { | ||||
|             ds.isUnderlineText = false | ||||
|             ds.bgColor = Color.BLUE | ||||
|         } | ||||
|     } | ||||
| } | ||||
|     return (0 until viewsCount).map { getChildAt(it) } | ||||
| } | ||||
|  | ||||
| @ -3,8 +3,25 @@ package io.noties.markwon.app.base | ||||
| import android.content.Context | ||||
| import android.util.AttributeSet | ||||
| import android.view.ViewGroup | ||||
| import io.noties.markwon.app.R | ||||
| import kotlin.math.max | ||||
| 
 | ||||
| class FlowLayout(context: Context, attrs: AttributeSet) : ViewGroup(context, attrs) { | ||||
| 
 | ||||
|     private val spacingVertical: Int | ||||
|     private val spacingHorizontal: Int | ||||
| 
 | ||||
|     init { | ||||
|         val array = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout) | ||||
|         try { | ||||
|             val spacing = array.getDimensionPixelSize(R.styleable.FlowLayout_fl_spacing, 0) | ||||
|             spacingVertical = array.getDimensionPixelSize(R.styleable.FlowLayout_fl_spacingVertical, spacing) | ||||
|             spacingHorizontal = array.getDimensionPixelSize(R.styleable.FlowLayout_fl_spacingHorizontal, spacing) | ||||
|         } finally { | ||||
|             array.recycle() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { | ||||
|         for (i in 0 until childCount) { | ||||
|             val child = getChildAt(i) | ||||
| @ -21,7 +38,61 @@ class FlowLayout(context: Context, attrs: AttributeSet) : ViewGroup(context, att | ||||
|     } | ||||
| 
 | ||||
|     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { | ||||
|         super.onMeasure(widthMeasureSpec, heightMeasureSpec) | ||||
|         val width = MeasureSpec.getSize(widthMeasureSpec) | ||||
| 
 | ||||
|         // we must have width (match_parent or exact dimension) | ||||
|         if (width <= 0) { | ||||
|             super.onMeasure(widthMeasureSpec, heightMeasureSpec) | ||||
|             return | ||||
|         } | ||||
| 
 | ||||
|         val availableWidth = width - paddingLeft - paddingRight | ||||
| 
 | ||||
|         // child must not exceed our width | ||||
|         val childWidthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST) | ||||
| 
 | ||||
|         // we also could enforce flexible height here (instead of exact one) | ||||
|         val childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) | ||||
| 
 | ||||
|         var x = 0 | ||||
|         var y = 0 | ||||
| 
 | ||||
|         var lineHeight = 0 | ||||
| 
 | ||||
|         for (i in 0 until childCount) { | ||||
|             val child = getChildAt(i) | ||||
| 
 | ||||
|             // measure | ||||
|             child.measure(childWidthSpec, childHeightSpec) | ||||
| 
 | ||||
|             val params = child.layoutParams as LayoutParams | ||||
|             val measuredWidth = child.measuredWidth | ||||
| 
 | ||||
|             if (measuredWidth > (availableWidth - x)) { | ||||
|                 // new line | ||||
|                 // make next child start at child measure width (starting at x = 0) | ||||
|                 params.x = 0 | ||||
|                 params.y = y + lineHeight + spacingVertical | ||||
| 
 | ||||
|                 x = measuredWidth + spacingHorizontal | ||||
|                 // move vertically by max value of child height on this line | ||||
|                 y += lineHeight + spacingVertical | ||||
| 
 | ||||
|                 lineHeight = child.measuredHeight | ||||
| 
 | ||||
|             } else { | ||||
|                 // we fit this line | ||||
|                 params.x = x | ||||
|                 params.y = y | ||||
| 
 | ||||
|                 x += measuredWidth + spacingHorizontal | ||||
|                 lineHeight = max(lineHeight, child.measuredHeight) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         val height = y + lineHeight + paddingTop + paddingBottom | ||||
| 
 | ||||
|         setMeasuredDimension(width, height) | ||||
|     } | ||||
| 
 | ||||
|     override fun generateDefaultLayoutParams(): ViewGroup.LayoutParams { | ||||
|  | ||||
| @ -61,6 +61,12 @@ class SearchBar(context: Context, attrs: AttributeSet?) : LinearLayout(context, | ||||
| 
 | ||||
|         clear.setOnClickListener { | ||||
|             textField.setText("") | ||||
|             // ensure that we have focus when clear is clicked | ||||
|             if (!textField.hasFocus()) { | ||||
|                 textField.requestFocus() | ||||
|                 // additionally ensure keyboard is showing | ||||
|                 KeyboardUtils.show(textField) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         cancel.setOnClickListener { | ||||
| @ -69,6 +75,10 @@ class SearchBar(context: Context, attrs: AttributeSet?) : LinearLayout(context, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fun search(text: String) { | ||||
|         textField.setText(text) | ||||
|     } | ||||
| 
 | ||||
|     private fun textFieldChanged(text: CharSequence) { | ||||
|         val isEmpty = text.isEmpty() | ||||
|         clear.hidden = isEmpty | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| package io.noties.markwon.app.samples | ||||
| 
 | ||||
| import io.noties.markwon.app.MarkwonSample | ||||
| import io.noties.markwon.app.ui.MarkwonSample | ||||
| import io.noties.markwon.sample.annotations.MarkwonArtifact | ||||
| import io.noties.markwon.sample.annotations.MarkwonSampleInfo | ||||
| 
 | ||||
|  | ||||
| @ -0,0 +1,5 @@ | ||||
| package io.noties.markwon.app.ui | ||||
| 
 | ||||
| abstract class MarkwonSample { | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,20 @@ | ||||
| package io.noties.markwon.app.ui | ||||
| 
 | ||||
| import android.os.Bundle | ||||
| import androidx.fragment.app.Fragment | ||||
| import io.noties.markwon.app.Sample | ||||
| 
 | ||||
| class SampleFragment : Fragment() { | ||||
| 
 | ||||
|     companion object { | ||||
|         private const val ARG_SAMPLE = "arg.Sample" | ||||
| 
 | ||||
|         fun init(sample: Sample): SampleFragment { | ||||
|             val fragment = SampleFragment() | ||||
|             fragment.arguments = Bundle().apply { | ||||
|                 putParcelable(ARG_SAMPLE, sample) | ||||
|             } | ||||
|             return fragment | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,288 @@ | ||||
| package io.noties.markwon.app.ui | ||||
| 
 | ||||
| import android.content.Context | ||||
| import android.os.Bundle | ||||
| import android.os.Parcelable | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import android.view.Window | ||||
| import android.widget.ImageView | ||||
| import android.widget.TextView | ||||
| import androidx.fragment.app.Fragment | ||||
| import androidx.recyclerview.widget.DefaultItemAnimator | ||||
| import androidx.recyclerview.widget.LinearLayoutManager | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import io.noties.adapt.Adapt | ||||
| import io.noties.adapt.DiffUtilDataSetChanged | ||||
| import io.noties.debug.Debug | ||||
| import io.noties.markwon.Markwon | ||||
| import io.noties.markwon.app.App | ||||
| import io.noties.markwon.app.R | ||||
| import io.noties.markwon.app.Sample | ||||
| import io.noties.markwon.app.SampleManager | ||||
| import io.noties.markwon.app.SampleSearch | ||||
| import io.noties.markwon.app.adapt.SampleItem | ||||
| import io.noties.markwon.app.base.SearchBar | ||||
| import io.noties.markwon.app.utils.Cancellable | ||||
| import io.noties.markwon.app.utils.displayName | ||||
| import io.noties.markwon.app.utils.onPreDraw | ||||
| import io.noties.markwon.app.utils.recyclerView | ||||
| import io.noties.markwon.app.utils.tagDisplayName | ||||
| import io.noties.markwon.movement.MovementMethodPlugin | ||||
| import io.noties.markwon.sample.annotations.MarkwonArtifact | ||||
| import kotlinx.android.parcel.Parcelize | ||||
| 
 | ||||
| class SampleListFragment : Fragment() { | ||||
| 
 | ||||
|     private val adapt: Adapt = Adapt.create(DiffUtilDataSetChanged.create()) | ||||
|     private lateinit var markwon: Markwon | ||||
| 
 | ||||
|     private val type: Type by lazy(LazyThreadSafetyMode.NONE) { | ||||
|         parseType(arguments!!) | ||||
|     } | ||||
| 
 | ||||
|     private var search: String? = null | ||||
| 
 | ||||
|     // postpone state restoration | ||||
|     private var pendingRecyclerScrollPosition: RecyclerScrollPosition? = null | ||||
| 
 | ||||
|     private var cancellable: Cancellable? = null | ||||
| 
 | ||||
|     private val sampleManager: SampleManager | ||||
|         get() = App.sampleManager | ||||
| 
 | ||||
|     override fun onAttach(context: Context?) { | ||||
|         super.onAttach(context) | ||||
| 
 | ||||
|         context?.also { | ||||
|             markwon = markwon(it) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { | ||||
|         return inflater.inflate(R.layout.fragment_sample_list, container, false) | ||||
|     } | ||||
| 
 | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
| 
 | ||||
|         initAppBar(view) | ||||
| 
 | ||||
|         val context = requireContext() | ||||
| 
 | ||||
|         val searchBar: SearchBar = view.findViewById(R.id.search_bar) | ||||
|         searchBar.onSearchListener = { | ||||
|             search = it | ||||
|             fetch() | ||||
|         } | ||||
| 
 | ||||
|         val recyclerView: RecyclerView = view.findViewById(R.id.recycler_view) | ||||
|         recyclerView.layoutManager = LinearLayoutManager(context) | ||||
|         recyclerView.itemAnimator = DefaultItemAnimator() | ||||
|         recyclerView.setHasFixedSize(true) | ||||
|         recyclerView.adapter = adapt | ||||
| 
 | ||||
|         // additional padding for RecyclerView | ||||
|         searchBar.onPreDraw { | ||||
|             recyclerView.setPadding( | ||||
|                     recyclerView.paddingLeft, | ||||
|                     recyclerView.paddingTop + searchBar.height, | ||||
|                     recyclerView.paddingRight, | ||||
|                     recyclerView.paddingBottom | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|         val state: State? = savedInstanceState?.getParcelable(STATE) | ||||
|         pendingRecyclerScrollPosition = state?.recyclerScrollPosition | ||||
|         if (state?.search != null) { | ||||
|             searchBar.search(state.search) | ||||
|         } else { | ||||
|             fetch() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun onDestroyView() { | ||||
|         val cancellable = this.cancellable | ||||
|         if (cancellable != null && !cancellable.isCancelled) { | ||||
|             cancellable.cancel() | ||||
|             this.cancellable = null | ||||
|         } | ||||
|         super.onDestroyView() | ||||
|     } | ||||
| 
 | ||||
|     override fun onSaveInstanceState(outState: Bundle) { | ||||
|         super.onSaveInstanceState(outState) | ||||
| 
 | ||||
|         val state = State( | ||||
|                 search, | ||||
|                 adapt.recyclerView?.scrollPosition | ||||
|         ) | ||||
|         outState.putParcelable(STATE, state) | ||||
|     } | ||||
| 
 | ||||
|     private fun initAppBar(view: View) { | ||||
|         val appBar = view.findViewById<View>(R.id.app_bar) | ||||
| 
 | ||||
|         val appBarIcon: ImageView = appBar.findViewById(R.id.app_bar_icon) | ||||
|         val appBarTitle: TextView = appBar.findViewById(R.id.app_bar_title) | ||||
| 
 | ||||
|         val type = this.type | ||||
|         if (type is Type.All) { | ||||
|             return | ||||
|         } | ||||
| 
 | ||||
|         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>) { | ||||
|         val items = samples.map { | ||||
|             SampleItem( | ||||
|                     markwon, | ||||
|                     it, | ||||
|                     { artifact -> openArtifact(artifact) }, | ||||
|                     { tag -> openTag(tag) }, | ||||
|                     { sample -> openSample(sample) } | ||||
|             ) | ||||
|         } | ||||
|         adapt.setItems(items) | ||||
| 
 | ||||
|         val scrollPosition = pendingRecyclerScrollPosition | ||||
|         if (scrollPosition != null) { | ||||
|             pendingRecyclerScrollPosition = null | ||||
|             val recyclerView = adapt.recyclerView ?: return | ||||
|             recyclerView.onPreDraw { | ||||
|                 (recyclerView.layoutManager as? LinearLayoutManager) | ||||
|                         ?.scrollToPositionWithOffset(scrollPosition.position, scrollPosition.offset) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun openArtifact(artifact: MarkwonArtifact) { | ||||
|         Debug.i(artifact) | ||||
|         openResultFragment(init(artifact)) | ||||
|     } | ||||
| 
 | ||||
|     private fun openTag(tag: String) { | ||||
|         Debug.i(tag) | ||||
|         openResultFragment(init(tag)) | ||||
|     } | ||||
| 
 | ||||
|     private fun openResultFragment(fragment: SampleListFragment) { | ||||
|         openFragment(fragment) | ||||
|     } | ||||
| 
 | ||||
|     private fun openSample(sample: Sample) { | ||||
|         openFragment(SampleFragment.init(sample)) | ||||
|     } | ||||
| 
 | ||||
|     private fun openFragment(fragment: Fragment) { | ||||
|         fragmentManager!!.beginTransaction() | ||||
|                 .setCustomAnimations(R.anim.screen_in, R.anim.screen_out, R.anim.screen_in_pop, R.anim.screen_out_pop) | ||||
|                 .replace(Window.ID_ANDROID_CONTENT, fragment) | ||||
|                 .addToBackStack(null) | ||||
|                 .commitAllowingStateLoss() | ||||
|     } | ||||
| 
 | ||||
|     private fun fetch() { | ||||
| 
 | ||||
|         val sampleSearch: SampleSearch = when (val type = this.type) { | ||||
|             is Type.Artifact -> SampleSearch.Artifact(search, type.artifact) | ||||
|             is Type.Tag -> SampleSearch.Tag(search, type.tag) | ||||
|             else -> SampleSearch.All(search) | ||||
|         } | ||||
| 
 | ||||
|         // clear current | ||||
|         cancellable?.let { | ||||
|             if (!it.isCancelled) { | ||||
|                 it.cancel() | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         cancellable = sampleManager.samples(sampleSearch) { | ||||
|             bindSamples(it) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         private const val ARG_ARTIFACT = "arg.Artifact" | ||||
|         private const val ARG_TAG = "arg.Tag" | ||||
|         private const val STATE = "key.State" | ||||
| 
 | ||||
|         fun init(): SampleListFragment { | ||||
|             val fragment = SampleListFragment() | ||||
|             fragment.arguments = Bundle() | ||||
|             return fragment | ||||
|         } | ||||
| 
 | ||||
|         fun init(artifact: MarkwonArtifact): SampleListFragment { | ||||
|             val fragment = SampleListFragment() | ||||
|             fragment.arguments = Bundle().apply { | ||||
|                 putString(ARG_ARTIFACT, artifact.name) | ||||
|             } | ||||
|             return fragment | ||||
|         } | ||||
| 
 | ||||
|         fun init(tag: String): SampleListFragment { | ||||
|             val fragment = SampleListFragment() | ||||
|             fragment.arguments = Bundle().apply { | ||||
|                 putString(ARG_TAG, tag) | ||||
|             } | ||||
|             return fragment | ||||
|         } | ||||
| 
 | ||||
|         fun markwon(context: Context): Markwon { | ||||
|             return Markwon.builder(context) | ||||
|                     .usePlugin(MovementMethodPlugin.none()) | ||||
|                     .build() | ||||
|         } | ||||
| 
 | ||||
|         private fun parseType(arguments: Bundle): Type { | ||||
|             val name = arguments.getString(ARG_ARTIFACT) | ||||
|             val tag = arguments.getString(ARG_TAG) | ||||
|             return when { | ||||
|                 name != null -> Type.Artifact(MarkwonArtifact.valueOf(name)) | ||||
|                 tag != null -> Type.Tag(tag) | ||||
|                 else -> Type.All | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Parcelize | ||||
|     private data class State( | ||||
|             val search: String?, | ||||
|             val recyclerScrollPosition: RecyclerScrollPosition? | ||||
|     ) : Parcelable | ||||
| 
 | ||||
|     @Parcelize | ||||
|     private data class RecyclerScrollPosition( | ||||
|             val position: Int, | ||||
|             val offset: Int | ||||
|     ) : Parcelable | ||||
| 
 | ||||
|     private val RecyclerView.scrollPosition: RecyclerScrollPosition? | ||||
|         get() { | ||||
|             val holder = findViewHolderForLayoutPosition(0) ?: return null | ||||
|             val position = holder.adapterPosition | ||||
|             val offset = holder.itemView.top | ||||
|             return RecyclerScrollPosition(position, offset) | ||||
|         } | ||||
| 
 | ||||
|     private sealed class Type { | ||||
|         class Artifact(val artifact: MarkwonArtifact) : Type() | ||||
|         class Tag(val tag: String) : Type() | ||||
|         object All : Type() | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| package io.noties.markwon.app.utils | ||||
| 
 | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import io.noties.adapt.Adapt | ||||
| import io.noties.debug.Debug | ||||
| 
 | ||||
| val Adapt.recyclerView: RecyclerView? | ||||
|     get() { | ||||
|         // internally throws if recycler is not present (detached from recyclerView) | ||||
|         return try { | ||||
|             recyclerView() | ||||
|         } catch (t: Throwable) { | ||||
|             Debug.e(t) | ||||
|             null | ||||
|         } | ||||
|     } | ||||
| @ -0,0 +1,7 @@ | ||||
| package io.noties.markwon.app.utils | ||||
| 
 | ||||
| interface Cancellable { | ||||
|     val isCancelled: Boolean | ||||
| 
 | ||||
|     fun cancel() | ||||
| } | ||||
| @ -0,0 +1,37 @@ | ||||
| package io.noties.markwon.app.utils; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import com.google.gson.Gson; | ||||
| import com.google.gson.reflect.TypeToken; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.InputStreamReader; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import io.noties.markwon.app.Sample; | ||||
| 
 | ||||
| public abstract class SampleUtils { | ||||
| 
 | ||||
|     @NonNull | ||||
|     public static List<Sample> readSamples(@NonNull Context context) { | ||||
| 
 | ||||
|         final Gson gson = new Gson(); | ||||
| 
 | ||||
|         try (InputStream inputStream = context.getAssets().open("samples.json")) { | ||||
|             return gson.fromJson( | ||||
|                     new InputStreamReader(inputStream), | ||||
|                     new TypeToken<List<Sample>>() { | ||||
|                     }.getType() | ||||
|             ); | ||||
|         } catch (IOException e) { | ||||
|             throw new RuntimeException(e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private SampleUtils() { | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,9 @@ | ||||
| package io.noties.markwon.app.utils | ||||
| 
 | ||||
| import io.noties.markwon.sample.annotations.MarkwonArtifact | ||||
| 
 | ||||
| val MarkwonArtifact.displayName: String | ||||
|     get() = "@${artifactName()}" | ||||
| 
 | ||||
| val String.tagDisplayName: String | ||||
|     get() = "#$this" | ||||
| @ -3,9 +3,24 @@ package io.noties.markwon.app.utils | ||||
| import android.view.View | ||||
| import android.view.View.GONE | ||||
| import android.view.View.VISIBLE | ||||
| import android.view.ViewTreeObserver | ||||
| 
 | ||||
| var View.hidden: Boolean | ||||
|     get() = visibility == GONE | ||||
|     set(value) { | ||||
|         visibility = if (value) GONE else VISIBLE | ||||
|     } | ||||
|     } | ||||
| 
 | ||||
| fun View.onPreDraw(action: () -> Unit) { | ||||
|     viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener { | ||||
|         override fun onPreDraw(): Boolean { | ||||
|             val vto = viewTreeObserver | ||||
|             if (vto.isAlive) { | ||||
|                 vto.removeOnPreDrawListener(this) | ||||
|             } | ||||
|             action() | ||||
|             // do not block drawing | ||||
|             return true | ||||
|         } | ||||
|     }) | ||||
| } | ||||
							
								
								
									
										13
									
								
								app-sample/src/main/res/anim/screen_in.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								app-sample/src/main/res/anim/screen_in.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <set xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:duration="@android:integer/config_shortAnimTime"> | ||||
| 
 | ||||
|     <alpha | ||||
|         android:fromAlpha="0" | ||||
|         android:toAlpha="1" /> | ||||
| 
 | ||||
|     <translate | ||||
|         android:fromXDelta="100%" | ||||
|         android:toXDelta="0" /> | ||||
| 
 | ||||
| </set> | ||||
							
								
								
									
										13
									
								
								app-sample/src/main/res/anim/screen_in_pop.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								app-sample/src/main/res/anim/screen_in_pop.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <set xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:duration="@android:integer/config_shortAnimTime"> | ||||
| 
 | ||||
|     <alpha | ||||
|         android:fromAlpha="0" | ||||
|         android:toAlpha="1" /> | ||||
| 
 | ||||
|     <translate | ||||
|         android:fromXDelta="-25%" | ||||
|         android:toXDelta="0" /> | ||||
| 
 | ||||
| </set> | ||||
							
								
								
									
										13
									
								
								app-sample/src/main/res/anim/screen_out.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								app-sample/src/main/res/anim/screen_out.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <set xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:duration="@android:integer/config_shortAnimTime"> | ||||
| 
 | ||||
|     <alpha | ||||
|         android:fromAlpha="1" | ||||
|         android:toAlpha="0.25" /> | ||||
| 
 | ||||
|     <translate | ||||
|         android:fromXDelta="0" | ||||
|         android:toXDelta="-25%" /> | ||||
| 
 | ||||
| </set> | ||||
							
								
								
									
										13
									
								
								app-sample/src/main/res/anim/screen_out_pop.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								app-sample/src/main/res/anim/screen_out_pop.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <set xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:duration="@android:integer/config_shortAnimTime"> | ||||
| 
 | ||||
|     <alpha | ||||
|         android:fromAlpha="1" | ||||
|         android:toAlpha="0" /> | ||||
| 
 | ||||
|     <translate | ||||
|         android:fromXDelta="0" | ||||
|         android:toXDelta="100%" /> | ||||
| 
 | ||||
| </set> | ||||
							
								
								
									
										5
									
								
								app-sample/src/main/res/drawable/bg_search_bar.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app-sample/src/main/res/drawable/bg_search_bar.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <shape xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <solid android:color="@color/search_bar_background" /> | ||||
|     <corners android:radius="128dip" /> | ||||
| </shape> | ||||
							
								
								
									
										14
									
								
								app-sample/src/main/res/drawable/bg_splash.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app-sample/src/main/res/drawable/bg_splash.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <item> | ||||
|         <shape> | ||||
|             <solid android:color="@color/window_background" /> | ||||
|         </shape> | ||||
|     </item> | ||||
|     <item> | ||||
|         <bitmap | ||||
|             android:gravity="center" | ||||
|             android:src="@mipmap/ic_launcher_foreground" | ||||
|             android:tileMode="disabled" /> | ||||
|     </item> | ||||
| </layer-list> | ||||
| @ -0,0 +1,5 @@ | ||||
| <vector android:height="24dp" android:tint="#FFFFFF" | ||||
|     android:viewportHeight="24" android:viewportWidth="24" | ||||
|     android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/> | ||||
| </vector> | ||||
| @ -1,17 +0,0 @@ | ||||
| <?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" | ||||
|     android:layout_width="wrap_content" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:background="@drawable/bg_artifact" | ||||
|     android:lines="1" | ||||
|     android:maxLines="1" | ||||
|     android:paddingStart="@dimen/content_padding" | ||||
|     android:paddingTop="2dip" | ||||
|     android:paddingEnd="@dimen/content_padding" | ||||
|     android:paddingBottom="2dip" | ||||
|     android:singleLine="true" | ||||
|     android:textAppearance="?android:attr/textAppearanceMedium" | ||||
|     android:textColor="@color/white" | ||||
|     tools:text="core" /> | ||||
| @ -1,51 +1,87 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:orientation="vertical" | ||||
|     android:paddingTop="@dimen/content_padding" | ||||
|     android:paddingEnd="@dimen/content_padding" | ||||
|     android:paddingBottom="@dimen/content_padding" | ||||
|     android:layout_marginStart="@dimen/content_padding" | ||||
|     android:layout_marginTop="4dip" | ||||
|     android:layout_marginEnd="@dimen/content_padding" | ||||
|     android:layout_marginBottom="4dip" | ||||
|     tools:ignore="RtlSymmetry"> | ||||
| 
 | ||||
|     <LinearLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:orientation="horizontal"> | ||||
|         android:orientation="vertical" | ||||
|         android:paddingTop="@dimen/content_padding_half" | ||||
|         android:paddingEnd="@dimen/content_padding" | ||||
|         android:paddingBottom="@dimen/content_padding"> | ||||
| 
 | ||||
|         <!--textView instead of icon, so we align baseline--> | ||||
|         <TextView | ||||
|             android:layout_width="@dimen/adapt_sample_hash_width" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:gravity="center" | ||||
|             android:text="#" | ||||
|             android:textAppearance="?android:attr/textAppearanceLarge" | ||||
|             android:textColor="?android:attr/colorSecondary" | ||||
|             tools:ignore="HardcodedText" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             android:id="@+id/title" | ||||
|         <LinearLayout | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:textAppearance="?android:attr/textAppearanceLarge" | ||||
|             tools:text="Title" /> | ||||
|             android:orientation="horizontal"> | ||||
| 
 | ||||
|             <!--textView instead of icon, so we align baseline--> | ||||
|             <TextView | ||||
|                 android:layout_width="@dimen/adapt_sample_hash_width" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:alpha="0.5" | ||||
|                 android:gravity="center" | ||||
|                 android:text="#" | ||||
|                 android:textAppearance="?android:attr/textAppearanceLarge" | ||||
|                 android:textColor="?android:attr/textColorSecondary" | ||||
|                 tools:ignore="HardcodedText" /> | ||||
| 
 | ||||
|             <TextView | ||||
|                 android:id="@+id/title" | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:textAppearance="?android:attr/textAppearanceLarge" | ||||
|                 android:textStyle="bold" | ||||
|                 tools:text="Title" /> | ||||
| 
 | ||||
|         </LinearLayout> | ||||
| 
 | ||||
|         <TextView | ||||
|             android:id="@+id/description" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_marginStart="@dimen/adapt_sample_hash_width" | ||||
|             android:breakStrategy="simple" | ||||
|             android:hyphenationFrequency="none" | ||||
|             android:textAppearance="?android:attr/textAppearance" | ||||
|             tools:text="Description goes here" /> | ||||
| 
 | ||||
|         <io.noties.markwon.app.base.FlowLayout | ||||
|             android:id="@+id/artifacts" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_marginStart="@dimen/adapt_sample_hash_width" | ||||
|             android:layout_marginTop="4dip" | ||||
|             app:fl_spacing="@dimen/content_padding" | ||||
|             app:fl_spacingVertical="2dip"> | ||||
| 
 | ||||
|             <!--    we are actually fine with pre-inflating a single view    --> | ||||
|             <include layout="@layout/view_artifact" /> | ||||
| 
 | ||||
|         </io.noties.markwon.app.base.FlowLayout> | ||||
| 
 | ||||
|         <io.noties.markwon.app.base.FlowLayout | ||||
|             android:id="@+id/tags" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_marginStart="@dimen/adapt_sample_hash_width" | ||||
|             android:layout_marginTop="4dip" | ||||
|             android:layout_marginBottom="4dip" | ||||
|             app:fl_spacing="@dimen/content_padding" | ||||
|             app:fl_spacingVertical="2dip"> | ||||
| 
 | ||||
|             <include layout="@layout/view_tag" /> | ||||
| 
 | ||||
|         </io.noties.markwon.app.base.FlowLayout> | ||||
| 
 | ||||
|     </LinearLayout> | ||||
| 
 | ||||
|     <TextView | ||||
|         android:id="@+id/description" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_marginStart="@dimen/adapt_sample_hash_width" | ||||
|         android:textAppearance="?android:attr/textAppearanceMedium" | ||||
|         tools:text="Description goes here" /> | ||||
| 
 | ||||
|     <TextView | ||||
|         android:id="@+id/artifacts_and_tags" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_marginStart="@dimen/adapt_sample_hash_width" | ||||
|         tools:text="recycler-view adapt another" /> | ||||
| 
 | ||||
| </LinearLayout> | ||||
| </androidx.cardview.widget.CardView> | ||||
|  | ||||
| @ -7,25 +7,39 @@ | ||||
|     android:orientation="vertical"> | ||||
| 
 | ||||
|     <LinearLayout | ||||
|         android:id="@+id/app_bar" | ||||
|         style="@style/AppBarContainer" | ||||
|         android:elevation="4dip" | ||||
|         android:orientation="horizontal"> | ||||
|         android:orientation="horizontal" | ||||
|         tools:ignore="UseCompoundDrawables"> | ||||
| 
 | ||||
|         <ImageView | ||||
|             android:id="@+id/app_bar_icon" | ||||
|             style="@style/AppBarIcon" | ||||
|             android:contentDescription="@null" | ||||
|             android:padding="8dip" | ||||
|             android:src="@mipmap/ic_launcher" /> | ||||
| 
 | ||||
|         <TextView | ||||
|         <FrameLayout | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="match_parent" | ||||
|             android:layout_marginEnd="@dimen/app_bar_height" | ||||
|             android:gravity="center" | ||||
|             android:text="@string/app_name" | ||||
|             android:textAppearance="?android:attr/textAppearanceLarge" | ||||
|             android:textColor="@color/white" | ||||
|             android:textStyle="bold" /> | ||||
|             android:layout_marginEnd="@dimen/app_bar_height"> | ||||
| 
 | ||||
|             <TextView | ||||
|                 android:id="@+id/app_bar_title" | ||||
|                 android:layout_width="wrap_content" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:layout_gravity="center" | ||||
|                 android:paddingLeft="@dimen/content_padding_double" | ||||
|                 android:paddingTop="@dimen/content_padding_half" | ||||
|                 android:paddingRight="@dimen/content_padding_double" | ||||
|                 android:paddingBottom="@dimen/content_padding_half" | ||||
|                 android:text="@string/app_name" | ||||
|                 android:textAppearance="?android:attr/textAppearanceLarge" | ||||
|                 android:textColor="@color/white" | ||||
|                 android:textStyle="bold" /> | ||||
| 
 | ||||
|         </FrameLayout> | ||||
| 
 | ||||
|     </LinearLayout> | ||||
| 
 | ||||
| @ -37,13 +51,18 @@ | ||||
|             android:id="@+id/recycler_view" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="match_parent" | ||||
|             tools:translationY="56dip" /> | ||||
|             android:clipChildren="false" | ||||
|             android:clipToPadding="false" | ||||
|             android:overScrollMode="never" | ||||
|             android:paddingBottom="36dip" | ||||
|             tools:layout_marginTop="56dip" /> | ||||
| 
 | ||||
|         <io.noties.markwon.app.base.SearchBar | ||||
|             android:id="@+id/search_bar" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:animateLayoutChanges="true" | ||||
|             android:background="@color/search_bar_background_full" | ||||
|             android:padding="@dimen/content_padding" /> | ||||
| 
 | ||||
|     </FrameLayout> | ||||
							
								
								
									
										23
									
								
								app-sample/src/main/res/layout/sample_text_view.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								app-sample/src/main/res/layout/sample_text_view.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:id="@+id/scroll_view" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent"> | ||||
| 
 | ||||
|     <FrameLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content"> | ||||
| 
 | ||||
|         <TextView | ||||
|             android:id="@+id/text_view" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:padding="@dimen/content_padding" | ||||
|             android:textAppearance="?android:attr/textAppearanceMedium" | ||||
|             android:textColor="?android:attr/textColorPrimary" | ||||
|             tools:text="Hello there" /> | ||||
| 
 | ||||
|     </FrameLayout> | ||||
| 
 | ||||
| </ScrollView> | ||||
							
								
								
									
										6
									
								
								app-sample/src/main/res/layout/view_artifact.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								app-sample/src/main/res/layout/view_artifact.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <TextView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     style="@style/ArtifactTagText" | ||||
|     android:background="@drawable/bg_artifact" | ||||
|     tools:text="core" /> | ||||
| @ -1,7 +1,7 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <merge xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     tools:background="#eee" | ||||
|     tools:background="#fff" | ||||
|     tools:gravity="center_vertical" | ||||
|     tools:layout_height="wrap_content" | ||||
|     tools:layout_widht="match_parent" | ||||
| @ -13,7 +13,8 @@ | ||||
|         android:layout_width="0px" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_weight="1" | ||||
|         android:background="@color/white" | ||||
|         android:animateLayoutChanges="true" | ||||
|         android:background="@drawable/bg_search_bar" | ||||
|         android:paddingTop="@dimen/content_padding" | ||||
|         android:paddingBottom="@dimen/content_padding"> | ||||
| 
 | ||||
| @ -31,10 +32,10 @@ | ||||
|         <ImageView | ||||
|             android:layout_width="@dimen/search_bar_icon_side" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_gravity="center_vertical" | ||||
|             android:contentDescription="@null" | ||||
|             android:scaleType="centerInside" | ||||
|             android:src="@drawable/ic_search_white_24dp" | ||||
|             android:layout_gravity="center_vertical" | ||||
|             android:tint="@color/gray" /> | ||||
| 
 | ||||
|         <ImageView | ||||
|  | ||||
							
								
								
									
										6
									
								
								app-sample/src/main/res/layout/view_tag.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								app-sample/src/main/res/layout/view_tag.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <TextView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     style="@style/ArtifactTagText" | ||||
|     android:background="@drawable/bg_tag" | ||||
|     tools:text="recycler-view" /> | ||||
							
								
								
									
										10
									
								
								app-sample/src/main/res/values/attrs.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app-sample/src/main/res/values/attrs.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
| 
 | ||||
|     <declare-styleable name="FlowLayout"> | ||||
|         <attr name="fl_spacing" format="dimension" /> | ||||
|         <attr name="fl_spacingVertical" format="dimension" /> | ||||
|         <attr name="fl_spacingHorizontal" format="dimension" /> | ||||
|     </declare-styleable> | ||||
| 
 | ||||
| </resources> | ||||
| @ -6,6 +6,10 @@ | ||||
|     <color name="gray_light">#999999</color> | ||||
| 
 | ||||
|     <color name="white">#FFFFFF</color> | ||||
|     <color name="window_background">#EEEEEE</color> | ||||
|     <color name="window_background">@color/white</color> | ||||
|     <color name="red">#FF0000</color> | ||||
| 
 | ||||
|     <color name="search_bar_background">#eee</color> | ||||
|     <color name="search_bar_background_full">#BFFFFFFF</color> | ||||
| 
 | ||||
| </resources> | ||||
| @ -4,6 +4,7 @@ | ||||
| 
 | ||||
|     <dimen name="content_padding">8dip</dimen> | ||||
|     <dimen name="content_padding_double">16dip</dimen> | ||||
|     <dimen name="content_padding_half">4dip</dimen> | ||||
| 
 | ||||
|     <dimen name="search_bar_icon_side">36dip</dimen> | ||||
|     <dimen name="adapt_sample_hash_width">36dip</dimen> | ||||
|  | ||||
							
								
								
									
										4
									
								
								app-sample/src/main/res/values/ids.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								app-sample/src/main/res/values/ids.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <item name="text" type="id" /> | ||||
| </resources> | ||||
| @ -1,11 +1,12 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
| <resources xmlns:tools="http://schemas.android.com/tools"> | ||||
| 
 | ||||
|     <style name="AppThemeBase" parent="android:Theme.Material.Light.NoActionBar"> | ||||
|         <item name="android:colorAccent">@color/accent</item> | ||||
|         <item name="android:colorPrimary">@color/gray_light</item> | ||||
|         <item name="android:colorPrimaryDark">@color/gray</item> | ||||
|         <item name="android:windowBackground">@color/window_background</item> | ||||
|         <item name="android:windowSplashscreenContent" tools:ignore="NewApi">@drawable/bg_splash</item> | ||||
|     </style> | ||||
| 
 | ||||
|     <style name="AppTheme" parent="AppThemeBase" /> | ||||
| @ -23,4 +24,19 @@ | ||||
|         <item name="android:background">?android:attr/selectableItemBackgroundBorderless</item> | ||||
|     </style> | ||||
| 
 | ||||
|     <style name="ArtifactTagText"> | ||||
|         <item name="android:id">@id/text</item> | ||||
|         <item name="android:layout_width">wrap_content</item> | ||||
|         <item name="android:layout_height">wrap_content</item> | ||||
|         <item name="android:lines">1</item> | ||||
|         <item name="android:singleLine">true</item> | ||||
|         <item name="android:maxLines">1</item> | ||||
|         <item name="android:paddingTop">@dimen/content_padding_half</item> | ||||
|         <item name="android:paddingStart">@dimen/content_padding</item> | ||||
|         <item name="android:paddingEnd">@dimen/content_padding</item> | ||||
|         <item name="android:paddingBottom">@dimen/content_padding_half</item> | ||||
|         <item name="android:textAppearance">?android:attr/textAppearance</item> | ||||
|         <item name="android:textColor">@color/white</item> | ||||
|     </style> | ||||
| 
 | ||||
| </resources> | ||||
| @ -6,7 +6,7 @@ buildscript { | ||||
|     } | ||||
|     dependencies { | ||||
|         // on `3.5.3` tests are not run from CLI | ||||
|         classpath 'com.android.tools.build:gradle:3.5.2' | ||||
|         classpath 'com.android.tools.build:gradle:4.0.0' | ||||
|         classpath 'com.github.ben-manes:gradle-versions-plugin:0.27.0' | ||||
|         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | ||||
|     } | ||||
| @ -69,6 +69,8 @@ ext { | ||||
|             'x-recycler-view'         : 'androidx.recyclerview:recyclerview:1.0.0', | ||||
|             'x-core'                  : 'androidx.core:core:1.0.2', | ||||
|             'x-appcompat'             : 'androidx.appcompat:appcompat:1.1.0', | ||||
|             'x-cardview'              : 'androidx.cardview:cardview:1.0.0', | ||||
|             'x-fragment'              : 'androidx.fragment:fragment:1.0.0', | ||||
|             'commonmark'              : "com.atlassian.commonmark:commonmark:$commonMarkVersion", | ||||
|             'commonmark-strikethrough': "com.atlassian.commonmark:commonmark-ext-gfm-strikethrough:$commonMarkVersion", | ||||
|             'commonmark-table'        : "com.atlassian.commonmark:commonmark-ext-gfm-tables:$commonMarkVersion", | ||||
|  | ||||
							
								
								
									
										3
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @ -1,5 +1,6 @@ | ||||
| #Wed Jun 17 17:05:04 MSK 2020 | ||||
| distributionBase=GRADLE_USER_HOME | ||||
| distributionPath=wrapper/dists | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip | ||||
| zipStoreBase=GRADLE_USER_HOME | ||||
| zipStorePath=wrapper/dists | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip | ||||
|  | ||||
| @ -1,5 +1,9 @@ | ||||
| package io.noties.markwon.sample.annotations; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import java.util.Locale; | ||||
| 
 | ||||
| public enum MarkwonArtifact { | ||||
|     CORE, | ||||
|     EDITOR, | ||||
| @ -17,5 +21,10 @@ public enum MarkwonArtifact { | ||||
|     RECYCLER, | ||||
|     RECYCLER_TABLE, | ||||
|     SIMPLE_EXT, | ||||
|     SYNTAX_HIGHLIGHT | ||||
|     SYNTAX_HIGHLIGHT; | ||||
| 
 | ||||
|     @NonNull | ||||
|     public String artifactName() { | ||||
|         return name().toLowerCase(Locale.US).replace('_', '-'); | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov