Commit fa39ce4b authored by Konstantin Tskhovrebov's avatar Konstantin Tskhovrebov 🤖

added showing markdown for item description on main screen. optimized...

added showing markdown for item description on main screen. optimized recyclerview adapters (removed ListItem wrappers)
parent fe3859ec
configurations.maybeCreate("default")
artifacts.add("default", file('anddown-0.3.0.aar'))
\ No newline at end of file
......@@ -94,7 +94,7 @@ dependencies {
//Image load and cache
compile "com.github.bumptech.glide:glide:3.8.0"
//Markdown to HTML converter
compile project(':anddown')
compile 'ru.noties:markwon:1.0.3'
//Bottom navigation bar
compile 'com.roughike:bottom-bar:2.3.1'
//Custom fonts
......
package ru.terrakok.gitlabclient
import android.app.Application
import android.graphics.Color
import ru.noties.markwon.SpannableConfiguration
import ru.noties.markwon.spans.SpannableTheme
import ru.terrakok.gitlabclient.model.data.auth.AuthHolder
import ru.terrakok.gitlabclient.toothpick.DI
import ru.terrakok.gitlabclient.toothpick.module.AppModule
......@@ -24,6 +27,7 @@ class App : Application() {
initToothpick()
initAppScope()
initCalligraphy()
initMarkwon()
}
private fun initLogger() {
......@@ -58,4 +62,14 @@ class App : Application() {
.setFontAttrId(R.attr.fontPath)
.build())
}
private fun initMarkwon() {
val theme = SpannableTheme.builderWithDefaults(this)
.codeTextColor(Color.parseColor("#C0341D"))
.codeBackgroundColor(Color.parseColor("#FCEDEA"))
.build()
SpannableConfiguration.builder(this)
.theme(theme)
.build()
}
}
\ No newline at end of file
......@@ -6,10 +6,10 @@ import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.support.annotation.DrawableRes
import android.support.annotation.LayoutRes
import android.support.v4.app.Fragment
import android.support.v4.content.ContextCompat
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory
import android.view.LayoutInflater
import android.view.View
......@@ -19,12 +19,9 @@ import android.widget.ImageView
import android.widget.TextView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.BitmapImageViewTarget
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import org.joda.time.DateTimeZone
import org.joda.time.format.DateTimeFormat
import retrofit2.HttpException
import ru.terrakok.cicerone.Router
import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.Screens
import ru.terrakok.gitlabclient.entity.app.develop.LicenseType
......@@ -40,16 +37,7 @@ import java.util.*
/**
* @author Konstantin Tskhovrebov (aka terrakok). Date: 03.03.17
*/
fun Resources.color(colorRes: Int) =
if (Build.VERSION.SDK_INT >= 23) {
this.getColor(colorRes, null)
} else {
this.getColor(colorRes)
}
fun Disposable.addTo(compositeDisposable: CompositeDisposable) {
compositeDisposable.add(this)
}
fun Context.color(colorRes: Int) = ContextCompat.getColor(this, colorRes)
fun ViewGroup.inflate(@LayoutRes layoutRes: Int, attachToRoot: Boolean = false): View {
return LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot)
......@@ -231,10 +219,10 @@ fun TargetBadgeStatus.getHumanName(resources: Resources) = when (this) {
TargetBadgeStatus.MERGED -> resources.getString(R.string.target_status_merged)
}
fun TargetBadgeStatus.getBadgeColors(resources: Resources) = when (this) {
TargetBadgeStatus.OPENED -> Pair(resources.color(R.color.green), resources.color(R.color.lightGreen))
TargetBadgeStatus.CLOSED -> Pair(resources.color(R.color.red), resources.color(R.color.lightRed))
TargetBadgeStatus.MERGED -> Pair(resources.color(R.color.blue), resources.color(R.color.lightBlue))
fun TargetBadgeStatus.getBadgeColors(context: Context) = when (this) {
TargetBadgeStatus.OPENED -> Pair(context.color(R.color.green), context.color(R.color.lightGreen))
TargetBadgeStatus.CLOSED -> Pair(context.color(R.color.red), context.color(R.color.lightRed))
TargetBadgeStatus.MERGED -> Pair(context.color(R.color.blue), context.color(R.color.lightBlue))
}
fun Fragment.sendEmail(email: String?) {
......
package ru.terrakok.gitlabclient.model.repository.tools
import com.commonsware.cwac.anddown.AndDown
import org.commonmark.parser.Parser
import org.commonmark.renderer.html.HtmlRenderer
/**
* @author Konstantin Tskhovrebov (aka terrakok). Date: 28.05.17
*/
class MarkDownConverter {
private val converter = AndDown()
fun markdownToHtml(raw: String) = converter.markdownToHtml(raw)
companion object {
private val parser = Parser.builder().build()
private val renderer = HtmlRenderer.builder().build()
}
fun markdownToHtml(raw: String) = renderer.render(parser.parse(raw))
}
\ No newline at end of file
......@@ -88,8 +88,8 @@ class Paginator<T>(
currentData.clear()
currentData.addAll(data)
currentPage = FIRST_PAGE
viewController.showEmptyProgress(false)
viewController.showData(true, currentData)
viewController.showEmptyProgress(false)
} else {
currentState = EMPTY_DATA()
viewController.showEmptyProgress(false)
......
......@@ -13,19 +13,19 @@ import ru.terrakok.gitlabclient.extension.inflate
/**
* @author Konstantin Tskhovrebov (aka terrakok) on 18.06.17.
*/
class AppLibraryAdapterDelegate(private val clickListener: (AppLibrary) -> Unit) : AdapterDelegate<MutableList<ListItem>>() {
class AppLibraryAdapterDelegate(private val clickListener: (AppLibrary) -> Unit) : AdapterDelegate<MutableList<Any>>() {
override fun isForViewType(items: MutableList<ListItem>, position: Int) =
items[position] is ListItem.AppLibraryItem
override fun isForViewType(items: MutableList<Any>, position: Int) =
items[position] is AppLibrary
override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder =
AppLibraryViewHolder(parent.inflate(R.layout.item_app_library), clickListener)
override fun onBindViewHolder(items: MutableList<ListItem>,
override fun onBindViewHolder(items: MutableList<Any>,
position: Int,
viewHolder: RecyclerView.ViewHolder,
payloads: MutableList<Any>) =
(viewHolder as AppLibraryViewHolder).bind((items[position] as ListItem.AppLibraryItem).item)
(viewHolder as AppLibraryViewHolder).bind(items[position] as AppLibrary)
private class AppLibraryViewHolder(val view: View, clickListener: (AppLibrary) -> Unit) : RecyclerView.ViewHolder(view) {
private lateinit var appLibrary: AppLibrary
......
......@@ -6,13 +6,13 @@ import android.support.v7.util.DiffUtil
* Created by Konstantin Tskhovrebov (aka @terrakok) on 04.01.18.
*/
class DiffCallback(
private val newData: List<ListItem>,
private val oldData: List<ListItem>
private val newData: List<Any>,
private val oldData: List<Any>
) : DiffUtil.Callback() {
override fun getOldListSize() = oldData.size
override fun getNewListSize() = newData.size
//without optimization at the moment
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = oldData[oldItemPosition] == newData[newItemPosition]
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = oldData[oldItemPosition] === newData[newItemPosition]
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = oldData[oldItemPosition] == newData[newItemPosition]
}
\ No newline at end of file
package ru.terrakok.gitlabclient.ui.global.list
import ru.terrakok.gitlabclient.entity.Project
import ru.terrakok.gitlabclient.entity.app.develop.AppLibrary
import ru.terrakok.gitlabclient.entity.app.target.TargetHeader
/**
* @author Konstantin Tskhovrebov (aka terrakok) on 18.06.17.
*/
sealed class ListItem {
class ProgressItem : ListItem()
data class ProjectItem(val project: Project) : ListItem()
data class AppLibraryItem(val item: AppLibrary) : ListItem()
data class TargetHeaderItem(val item: TargetHeader) : ListItem()
}
\ No newline at end of file
......@@ -10,15 +10,15 @@ import ru.terrakok.gitlabclient.extension.inflate
/**
* @author Konstantin Tskhovrebov (aka terrakok) on 18.06.17.
*/
class ProgressAdapterDelegate : AdapterDelegate<MutableList<ListItem>>() {
class ProgressAdapterDelegate : AdapterDelegate<MutableList<Any>>() {
override fun isForViewType(items: MutableList<ListItem>, position: Int) =
items[position] is ListItem.ProgressItem
override fun isForViewType(items: MutableList<Any>, position: Int) =
items[position] is ProgressItem
override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder =
ProgressViewHolder(parent.inflate(R.layout.item_progress))
override fun onBindViewHolder(items: MutableList<ListItem>,
override fun onBindViewHolder(items: MutableList<Any>,
position: Int,
viewHolder: RecyclerView.ViewHolder,
payloads: MutableList<Any>) {}
......
package ru.terrakok.gitlabclient.ui.global.list
/**
* @author Konstantin Tskhovrebov (aka terrakok) on 18.06.17.
*/
class ProgressItem
\ No newline at end of file
......@@ -14,19 +14,19 @@ import ru.terrakok.gitlabclient.extension.loadRoundedImage
/**
* @author Konstantin Tskhovrebov (aka terrakok) on 18.06.17.
*/
class ProjectAdapterDelegate(private val clickListener: (Project) -> Unit) : AdapterDelegate<MutableList<ListItem>>() {
class ProjectAdapterDelegate(private val clickListener: (Project) -> Unit) : AdapterDelegate<MutableList<Any>>() {
override fun isForViewType(items: MutableList<ListItem>, position: Int) =
items[position] is ListItem.ProjectItem
override fun isForViewType(items: MutableList<Any>, position: Int) =
items[position] is Project
override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder =
ProjectViewHolder(parent.inflate(R.layout.item_project), clickListener)
override fun onBindViewHolder(items: MutableList<ListItem>,
override fun onBindViewHolder(items: MutableList<Any>,
position: Int,
viewHolder: RecyclerView.ViewHolder,
payloads: MutableList<Any>) =
(viewHolder as ProjectViewHolder).bind((items[position] as ListItem.ProjectItem).project)
(viewHolder as ProjectViewHolder).bind(items[position] as Project)
private class ProjectViewHolder(val view: View, clickListener: (Project) -> Unit) : RecyclerView.ViewHolder(view) {
private lateinit var project: Project
......
package ru.terrakok.gitlabclient.ui.global.list
import android.content.res.Resources
import android.support.v7.widget.RecyclerView
import android.view.View
import android.view.ViewGroup
......@@ -8,6 +7,9 @@ import android.widget.TextView
import com.hannesdorfmann.adapterdelegates3.AdapterDelegate
import kotlinx.android.synthetic.main.item_target_badge.view.*
import kotlinx.android.synthetic.main.item_target_header.view.*
import ru.noties.markwon.Markwon
import ru.noties.markwon.SpannableConfiguration
import ru.noties.markwon.spans.SpannableTheme
import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.entity.app.target.TargetBadge
import ru.terrakok.gitlabclient.entity.app.target.TargetBadgeIcon
......@@ -21,19 +23,19 @@ import ru.terrakok.gitlabclient.extension.*
class TargetHeaderAdapterDelegate(
private val avatarClickListener: (Long) -> Unit,
private val clickListener: (TargetHeader) -> Unit
) : AdapterDelegate<MutableList<ListItem>>() {
) : AdapterDelegate<MutableList<Any>>() {
override fun isForViewType(items: MutableList<ListItem>, position: Int) =
items[position] is ListItem.TargetHeaderItem
override fun isForViewType(items: MutableList<Any>, position: Int) =
items[position] is TargetHeader
override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder =
TargetHeaderViewHolder(parent.inflate(R.layout.item_target_header), avatarClickListener, clickListener)
override fun onBindViewHolder(items: MutableList<ListItem>,
override fun onBindViewHolder(items: MutableList<Any>,
position: Int,
viewHolder: RecyclerView.ViewHolder,
payloads: MutableList<Any>) =
(viewHolder as TargetHeaderViewHolder).bind((items[position] as ListItem.TargetHeaderItem).item)
(viewHolder as TargetHeaderViewHolder).bind(items[position] as TargetHeader)
private class TargetHeaderViewHolder(
private val view: View,
......@@ -41,6 +43,7 @@ class TargetHeaderAdapterDelegate(
private val clickListener: (TargetHeader) -> Unit
) : RecyclerView.ViewHolder(view) {
private lateinit var item: TargetHeader
private val mdConfig: SpannableConfiguration
init {
view.setOnClickListener { clickListener(item) }
......@@ -49,6 +52,13 @@ class TargetHeaderAdapterDelegate(
(1..5).forEach {
view.badgesContainer.inflate(R.layout.item_target_badge, true)
}
val mdTheme = SpannableTheme.builderWithDefaults(view.context)
.codeBackgroundColor(view.context.color(R.color.beige))
.build()
mdConfig = SpannableConfiguration.builder(view.context)
.theme(mdTheme)
.build()
}
fun bind(item: TargetHeader) {
......@@ -56,7 +66,7 @@ class TargetHeaderAdapterDelegate(
val res = view.resources
view.titleTextView.text = item.title.getHumanName(res)
view.descriptionTextView.text = item.body
Markwon.setMarkdown(view.descriptionTextView, mdConfig, item.body ?: "")
view.avatarImageView.loadRoundedImage(item.author.avatarUrl)
view.iconImageView.setImageResource(item.icon.getIcon())
view.dateTextView.text = item.date.humanTime(res)
......@@ -64,10 +74,10 @@ class TargetHeaderAdapterDelegate(
view.descriptionTextView.visible(item.body != null)
view.iconImageView.visible(item.icon != TargetHeaderIcon.NONE)
bindBadges(item.badges, res)
bindBadges(item.badges)
}
private fun bindBadges(badges: List<TargetBadge>, res: Resources) {
private fun bindBadges(badges: List<TargetBadge>) {
view.commentsTextView.visible(false)
view.commitsTextView.visible(false)
view.upVotesTextView.visible(false)
......@@ -91,15 +101,15 @@ class TargetHeaderAdapterDelegate(
is TargetBadge.Text -> {
val badgeView = view.badgesContainer.getChildAt(i) as TextView
badgeView.textTextView.text = badge.text
badgeView.textTextView.setTextColor(res.color(R.color.colorPrimary))
badgeView.textTextView.setBackgroundColor(res.color(R.color.colorPrimaryLight))
badgeView.textTextView.setTextColor(view.context.color(R.color.colorPrimary))
badgeView.textTextView.setBackgroundColor(view.context.color(R.color.colorPrimaryLight))
badgeView.visible(true)
i++
}
is TargetBadge.Status -> {
val badgeView = view.badgesContainer.getChildAt(i) as TextView
badgeView.textTextView.text = badge.status.getHumanName(res)
val (textColor, bgColor) = badge.status.getBadgeColors(res)
badgeView.textTextView.text = badge.status.getHumanName(view.resources)
val (textColor, bgColor) = badge.status.getBadgeColors(view.context)
badgeView.textTextView.setTextColor(textColor)
badgeView.textTextView.setBackgroundColor(bgColor)
badgeView.visible(true)
......
......@@ -14,7 +14,6 @@ import ru.terrakok.gitlabclient.presentation.libraries.LibrariesView
import ru.terrakok.gitlabclient.toothpick.DI
import ru.terrakok.gitlabclient.ui.global.BaseFragment
import ru.terrakok.gitlabclient.ui.global.list.AppLibraryAdapterDelegate
import ru.terrakok.gitlabclient.ui.global.list.ListItem
import toothpick.Toothpick
/**
......@@ -52,15 +51,15 @@ class LibrariesFragment : BaseFragment(), LibrariesView {
presenter.onBackPressed()
}
private inner class LibraryAdapter : ListDelegationAdapter<MutableList<ListItem>>() {
private inner class LibraryAdapter : ListDelegationAdapter<MutableList<Any>>() {
init {
items = mutableListOf()
delegatesManager.addDelegate(AppLibraryAdapterDelegate({ tryOpenLink(it.url) }))
}
fun setData(projects: List<AppLibrary>) {
fun setData(libraries: List<AppLibrary>) {
items.clear()
items.addAll(projects.map { ListItem.AppLibraryItem(it) })
items.addAll(libraries)
notifyDataSetChanged()
}
......
......@@ -5,15 +5,15 @@ import android.support.v7.widget.RecyclerView
import com.hannesdorfmann.adapterdelegates3.ListDelegationAdapter
import ru.terrakok.gitlabclient.entity.app.target.TargetHeader
import ru.terrakok.gitlabclient.ui.global.list.DiffCallback
import ru.terrakok.gitlabclient.ui.global.list.ListItem
import ru.terrakok.gitlabclient.ui.global.list.ProgressAdapterDelegate
import ru.terrakok.gitlabclient.ui.global.list.ProgressItem
import ru.terrakok.gitlabclient.ui.global.list.TargetHeaderAdapterDelegate
class TargetsAdapter(
userClickListener: (Long) -> Unit,
clickListener: (TargetHeader) -> Unit,
private val nextListener: () -> Unit
) : ListDelegationAdapter<MutableList<ListItem>>() {
) : ListDelegationAdapter<MutableList<Any>>() {
init {
items = mutableListOf()
delegatesManager.addDelegate(TargetHeaderAdapterDelegate(userClickListener, clickListener))
......@@ -25,8 +25,8 @@ class TargetsAdapter(
val progress = isProgress()
items.clear()
items.addAll(events.map { ListItem.TargetHeaderItem(it) })
if (progress) items.add(ListItem.ProgressItem())
items.addAll(events)
if (progress) items.add(ProgressItem())
//yes, on main thread...
DiffUtil
......@@ -38,16 +38,16 @@ class TargetsAdapter(
val oldData = items.toList()
val currentProgress = isProgress()
if (isVisible && !currentProgress) items.add(ListItem.ProgressItem())
else if (!isVisible && currentProgress) items.remove(items.last())
//yes, on main thread...
DiffUtil
.calculateDiff(DiffCallback(items, oldData), false)
.dispatchUpdatesTo(this)
if (isVisible && !currentProgress) {
items.add(ProgressItem())
notifyItemInserted(items.lastIndex)
} else if (!isVisible && currentProgress) {
items.remove(items.last())
notifyItemRemoved(oldData.lastIndex)
}
}
private fun isProgress() = items.isNotEmpty() && items.last() is ListItem.ProgressItem
private fun isProgress() = items.isNotEmpty() && items.last() is ProgressItem
override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int, payloads: MutableList<Any?>?) {
super.onBindViewHolder(holder, position, payloads)
......
......@@ -18,8 +18,8 @@ import ru.terrakok.gitlabclient.toothpick.PrimitiveWrapper
import ru.terrakok.gitlabclient.toothpick.qualifier.ProjectListMode
import ru.terrakok.gitlabclient.ui.global.BaseFragment
import ru.terrakok.gitlabclient.ui.global.ZeroViewHolder
import ru.terrakok.gitlabclient.ui.global.list.ListItem
import ru.terrakok.gitlabclient.ui.global.list.ProgressAdapterDelegate
import ru.terrakok.gitlabclient.ui.global.list.ProgressItem
import ru.terrakok.gitlabclient.ui.global.list.ProjectAdapterDelegate
import toothpick.Toothpick
import toothpick.config.Module
......@@ -109,7 +109,7 @@ class ProjectsListFragment : BaseFragment(), ProjectsListView {
override fun onBackPressed() = presenter.onBackPressed()
inner class ProjectsAdapter : ListDelegationAdapter<MutableList<ListItem>>() {
inner class ProjectsAdapter : ListDelegationAdapter<MutableList<Any>>() {
init {
items = mutableListOf()
delegatesManager.addDelegate(ProjectAdapterDelegate({ presenter.onProjectClicked(it.id) }))
......@@ -120,8 +120,8 @@ class ProjectsListFragment : BaseFragment(), ProjectsListView {
val progress = isProgress()
items.clear()
items.addAll(projects.map { ListItem.ProjectItem(it) })
if (progress) items.add(ListItem.ProgressItem())
items.addAll(projects)
if (progress) items.add(ProgressItem())
notifyDataSetChanged()
}
......@@ -129,13 +129,13 @@ class ProjectsListFragment : BaseFragment(), ProjectsListView {
fun showProgress(isVisible: Boolean) {
val currentProgress = isProgress()
if (isVisible && !currentProgress) items.add(ListItem.ProgressItem())
if (isVisible && !currentProgress) items.add(ProgressItem())
else if (!isVisible && currentProgress) items.remove(items.last())
notifyDataSetChanged()
}
private fun isProgress() = items.isNotEmpty() && items.last() is ListItem.ProgressItem
private fun isProgress() = items.isNotEmpty() && items.last() is ProgressItem
override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int, payloads: MutableList<Any?>?) {
super.onBindViewHolder(holder, position, payloads)
......
......@@ -47,8 +47,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:maxLines="5"
android:ellipsize="end"
android:linksClickable="true"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="@id/titleTextView"
app:layout_constraintRight_toRightOf="@id/titleTextView"
......
......@@ -24,4 +24,6 @@
<color name="blue">#17d1f2</color>
<color name="lightBlue">#e7fafd</color>
<color name="beige">#fff8e1</color>
</resources>
include ':app', ':anddown'
include ':app'
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment