ariane/app/src/main/java/oppen/ariane/ui/GemtextAdapter.kt

188 lines
8.3 KiB
Kotlin

package oppen.ariane.ui
import android.annotation.SuppressLint
import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.gemtext_code_block.view.*
import kotlinx.android.synthetic.main.gemtext_image_link.view.*
import kotlinx.android.synthetic.main.gemtext_link.view.gemtext_text_link
import kotlinx.android.synthetic.main.gemtext_text.view.gemtext_text_textview
import oppen.ariane.R
import oppen.endsWithImage
import oppen.visible
import java.net.URI
class GemtextAdapter(val onLink: (adapter: GemtextAdapter, link: URI, longTap: Boolean, adapterPosition: Int, view: View?) -> Unit): RecyclerView.Adapter<GemtextAdapter.ViewHolder>() {
var lines = mutableListOf<String>()
var inlineImages = HashMap<Int, Uri>()
private val typeText = 0
private val typeH1 = 1
private val typeH2 = 2
private val typeH3 = 3
private val typeListItem = 4
private val typeImageLink = 5
private val typeLink = 6
private val typeCodeBlock = 7
private val typeQuote = 8
sealed class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
class Text(itemView: View): ViewHolder(itemView)
class H1(itemView: View): ViewHolder(itemView)
class H2(itemView: View): ViewHolder(itemView)
class H3(itemView: View): ViewHolder(itemView)
class ListItem(itemView: View): ViewHolder(itemView)
class ImageLink(itemView: View): ViewHolder(itemView)
class Link(itemView: View): ViewHolder(itemView)
class Code(itemView: View): ViewHolder(itemView)
class Quote(itemView: View): ViewHolder(itemView)
}
fun render(lines: List<String>){
this.inlineImages.clear()
this.lines.clear()
this.lines.addAll(lines)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return when(viewType){
typeText -> ViewHolder.Text(LayoutInflater.from(parent.context).inflate(R.layout.gemtext_text, parent, false))
typeH1 -> ViewHolder.H1(LayoutInflater.from(parent.context).inflate(R.layout.gemtext_h1, parent, false))
typeH2 -> ViewHolder.H2(LayoutInflater.from(parent.context).inflate(R.layout.gemtext_h2, parent, false))
typeH3 -> ViewHolder.H3(LayoutInflater.from(parent.context).inflate(R.layout.gemtext_h3, parent, false))
typeListItem -> ViewHolder.ListItem(LayoutInflater.from(parent.context).inflate(R.layout.gemtext_text, parent, false))
typeImageLink -> ViewHolder.ImageLink(LayoutInflater.from(parent.context).inflate(R.layout.gemtext_image_link, parent, false))
typeLink -> ViewHolder.Link(LayoutInflater.from(parent.context).inflate(R.layout.gemtext_link, parent, false))
typeCodeBlock-> ViewHolder.Code(LayoutInflater.from(parent.context).inflate(R.layout.gemtext_code_block, parent, false))
typeQuote -> ViewHolder.Quote(LayoutInflater.from(parent.context).inflate(R.layout.gemtext_quote, parent, false))
else -> ViewHolder.Text(LayoutInflater.from(parent.context).inflate(R.layout.gemtext_text, parent, false))
}
}
override fun getItemViewType(position: Int): Int {
val line = lines[position]
return when {
line.startsWith("```") -> typeCodeBlock
line.startsWith("###") -> typeH3
line.startsWith("##") -> typeH2
line.startsWith("#") -> typeH1
line.startsWith("*") -> typeListItem
line.startsWith("=>") && getLink(line).endsWithImage() -> typeImageLink
line.startsWith("=>") -> typeLink
line.startsWith(">") -> typeQuote
else -> typeText
}
}
override fun getItemCount(): Int = lines.size
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val line = lines[position]
when(holder){
is ViewHolder.Text -> holder.itemView.gemtext_text_textview.text = line
is ViewHolder.Code-> holder.itemView.gemtext_text_monospace_textview.text = line.substring(3)
is ViewHolder.Quote-> holder.itemView.gemtext_text_monospace_textview.text = line.substring(1).trim()
is ViewHolder.H1 -> {
when {
line.length > 2 -> holder.itemView.gemtext_text_textview.text = line.substring(2).trim()
else -> holder.itemView.gemtext_text_textview.text = ""
}
}
is ViewHolder.H2 -> {
when {
line.length > 3 -> holder.itemView.gemtext_text_textview.text = line.substring(3).trim()
else -> holder.itemView.gemtext_text_textview.text = ""
}
}
is ViewHolder.H3 -> {
when {
line.length > 4 -> holder.itemView.gemtext_text_textview.text = line.substring(4).trim()
else -> holder.itemView.gemtext_text_textview.text = ""
}
}
is ViewHolder.ListItem -> holder.itemView.gemtext_text_textview.text = "${line.substring(1)}".trim()
is ViewHolder.Link -> {
val linkParts = line.substring(2).trim().split("\\s+".toRegex(), 2)
var linkName = linkParts[0]
if(linkParts.size > 1) {
linkName = linkParts[1]
}
val displayText = linkName
holder.itemView.gemtext_text_link.text = displayText
holder.itemView.gemtext_text_link.paint.isUnderlineText = true
holder.itemView.gemtext_text_link.setOnClickListener {
val uri = getUri(lines[holder.adapterPosition])
println("User clicked link: $uri")
onLink(this, uri, false, holder.adapterPosition, null)
}
holder.itemView.gemtext_text_link.setOnLongClickListener {view ->
val uri = getUri(lines[holder.adapterPosition])
println("User long-clicked link: $uri")
onLink(this, uri, true, holder.adapterPosition, view)
true
}
}
is ViewHolder.ImageLink -> {
//todo - extract, largely duplicate of above
val linkParts = line.substring(2).trim().split("\\s+".toRegex(), 2)
var linkName = linkParts[0]
if(linkParts.size > 1) {
linkName = linkParts[1]
}
val displayText = linkName
holder.itemView.gemtext_text_link.text = displayText
holder.itemView.gemtext_text_link.paint.isUnderlineText = true
holder.itemView.gemtext_text_link.setOnClickListener {
val uri = getUri(lines[holder.adapterPosition])
println("User clicked link: $uri")
onLink(this, uri, false, holder.adapterPosition, null)
}
holder.itemView.gemtext_text_link.setOnLongClickListener {view ->
val uri = getUri(lines[holder.adapterPosition])
println("User long-clicked link: $uri")
onLink(this, uri, true, holder.adapterPosition, view)
true
}
if(inlineImages.containsKey(position)){
holder.itemView.gemtext_inline_image.visible(true)
holder.itemView.gemtext_inline_image.setImageURI(inlineImages[position])
}else{
holder.itemView.gemtext_inline_image.visible(false)
}
}
}
}
private fun getLink(line: String): String{
val linkParts = line.substring(2).trim().split("\\s+".toRegex(), 2)
return linkParts[0]
}
private fun getUri(linkLine: String): URI{
val linkParts = linkLine.substring(2).trim().split("\\s+".toRegex(), 2)
return URI.create(linkParts.first())
}
fun inferTitle(): String? {
lines.forEach { line ->
if(line.startsWith("#")) return line.replace("#", "").trim()
}
return null
}
fun loadImage(position: Int, cacheUri: Uri){
inlineImages[position] = cacheUri
notifyItemChanged(position)
}
}