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() { var lines = mutableListOf() var inlineImages = HashMap() 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){ 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) } }