Commit dd7f500e authored by Eugene Shapovalov's avatar Eugene Shapovalov 💬

Implement custom avatar view with name and url logic.

parent 32f8b3fd
......@@ -30,4 +30,7 @@ data class User(
@SerializedName("can_create_project") val canCreateProject: Boolean,
@SerializedName("two_factor_enabled") val twoFactorEnabled: Boolean,
@SerializedName("external") val external: Boolean
)
) {
fun toShortUser() = ShortUser(id, state, name, webUrl, avatarUrl, username)
}
......@@ -24,8 +24,6 @@ import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.fragment.app.Fragment
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.google.android.material.snackbar.Snackbar
import retrofit2.adapter.rxjava2.Result
import ru.terrakok.cicerone.Navigator
......@@ -140,20 +138,6 @@ fun Fragment.sendEmail(email: String?) {
}
}
fun ImageView.loadRoundedImage(
url: String?,
ctx: Context? = null
) {
Glide.with(ctx ?: context)
.load(url)
.apply(RequestOptions().apply {
placeholder(R.drawable.default_img)
error(R.drawable.default_img)
})
.apply(RequestOptions.circleCropTransform())
.into(this)
}
fun TargetHeader.Public.openInfo(router: FlowRouter) {
when (target) {
AppTarget.PROJECT -> {
......
......@@ -9,7 +9,6 @@ import kotlinx.android.synthetic.main.item_user_acount.view.*
import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.entity.app.session.UserAccount
import ru.terrakok.gitlabclient.extension.inflate
import ru.terrakok.gitlabclient.extension.loadRoundedImage
import ru.terrakok.gitlabclient.extension.visible
import ru.terrakok.gitlabclient.presentation.drawer.NavigationDrawerPresenter
import ru.terrakok.gitlabclient.presentation.drawer.NavigationDrawerView
......@@ -17,6 +16,7 @@ import ru.terrakok.gitlabclient.presentation.drawer.NavigationDrawerView.MenuIte
import ru.terrakok.gitlabclient.presentation.drawer.NavigationDrawerView.MenuItem.*
import ru.terrakok.gitlabclient.ui.global.BaseFragment
import ru.terrakok.gitlabclient.ui.global.MessageDialogFragment
import ru.terrakok.gitlabclient.ui.global.view.custom.bindUserAccount
/**
* @author Konstantin Tskhovrebov (aka terrakok). Date: 04.04.17
......@@ -71,13 +71,13 @@ class NavigationDrawerFragment : BaseFragment(), NavigationDrawerView, MessageDi
override fun setAccounts(accounts: List<UserAccount>, currentAccount: UserAccount) {
nickTV.text = currentAccount.userName
serverNameTV.text = currentAccount.serverPath
avatarImageView.setUserInfo(currentAccount.userId, currentAccount.avatarUrl)
avatarImageView.bindUserAccount(currentAccount, true)
accountsContainer.removeAllViews()
accounts.forEach { acc ->
accountsContainer.inflate(R.layout.item_user_acount)
.apply {
avatarImageView.loadRoundedImage(acc.avatarUrl, context)
avatarImageView.bindUserAccount(acc, false)
nameTextView.text = acc.userName
serverTextView.text = acc.serverPath
selectorView.visible(acc == currentAccount)
......
......@@ -8,6 +8,7 @@ import kotlinx.android.synthetic.main.item_assignee.view.*
import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.entity.ShortUser
import ru.terrakok.gitlabclient.extension.inflate
import ru.terrakok.gitlabclient.ui.global.view.custom.bindShortUser
/**
* Created by Eugene Shapovalov (@CraggyHaggy) on 26.05.19.
......@@ -35,7 +36,7 @@ class AssigneesAdapterDelegate : AdapterDelegate<MutableList<ShortUser>>() {
with(itemView) {
titleTextView.text = item.name
subtitleTextView.text = item.username
avatarImageView.setUserInfo(item.id, item.avatarUrl)
avatarImageView.bindShortUser(item, true)
}
}
}
......
......@@ -9,6 +9,7 @@ import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.entity.app.CommitWithShortUser
import ru.terrakok.gitlabclient.extension.humanTime
import ru.terrakok.gitlabclient.extension.inflate
import ru.terrakok.gitlabclient.ui.global.view.custom.bindShortUser
/**
* Created by Eugene Shapovalov (@CraggyHaggy) on 20.10.18.
......@@ -34,8 +35,9 @@ class CommitAdapterDelegate : AdapterDelegate<MutableList<Any>>() {
fun bind(commitWithShortUser: CommitWithShortUser) {
this.commitWithShortUser = commitWithShortUser
with(itemView) {
commitWithShortUser.shortUser?.run {
avatarImageView.setUserInfo(id, avatarUrl)
val shortUser = commitWithShortUser.shortUser
if (shortUser != null) {
avatarImageView.bindShortUser(shortUser, true)
}
titleTextView.text = commitWithShortUser.commit.title
descriptionTextView.text = String.format(
......
......@@ -10,8 +10,8 @@ import ru.terrakok.gitlabclient.entity.Project
import ru.terrakok.gitlabclient.entity.Visibility
import ru.terrakok.gitlabclient.extension.getTintDrawable
import ru.terrakok.gitlabclient.extension.inflate
import ru.terrakok.gitlabclient.extension.loadRoundedImage
import ru.terrakok.gitlabclient.extension.setStartDrawable
import ru.terrakok.gitlabclient.ui.global.view.custom.bindProject
/**
* @author Konstantin Tskhovrebov (aka terrakok) on 18.06.17.
......@@ -63,7 +63,7 @@ class ProjectAdapterDelegate(private val clickListener: (Project) -> Unit) : Ada
else -> R.drawable.ic_globe_18dp
}
)
avatarImageView.loadRoundedImage(project.avatarUrl)
avatarImageView.bindProject(project)
}
}
}
......
......@@ -14,6 +14,7 @@ import ru.terrakok.gitlabclient.entity.app.target.TargetBadgeIcon
import ru.terrakok.gitlabclient.entity.app.target.TargetHeader
import ru.terrakok.gitlabclient.entity.app.target.TargetHeaderIcon
import ru.terrakok.gitlabclient.extension.*
import ru.terrakok.gitlabclient.ui.global.view.custom.bindShortUser
/**
* @author Konstantin Tskhovrebov (aka terrakok) on 18.06.17.
......@@ -70,7 +71,7 @@ class TargetHeaderPublicAdapterDelegate(
titleTextView.text = item.title.getHumanName(resources)
Markwon.setText(descriptionTextView, item.body)
descriptionTextView.movementMethod = null //disable internal link click
avatarImageView.setUserInfo(item.author.id, item.author.avatarUrl)
avatarImageView.bindShortUser(item.author, true)
iconImageView.setImageResource(item.icon.getIcon())
dateTextView.text = item.date.humanTime(resources)
......
......@@ -11,6 +11,7 @@ import ru.terrakok.gitlabclient.entity.Note
import ru.terrakok.gitlabclient.extension.humanTime
import ru.terrakok.gitlabclient.extension.inflate
import ru.terrakok.gitlabclient.presentation.global.NoteWithFormattedBody
import ru.terrakok.gitlabclient.ui.global.view.custom.bindShortUser
/**
* @author Konstantin Tskhovrebov (aka terrakok) on 18.06.17.
......@@ -36,7 +37,7 @@ class UserNoteAdapterDelegate : AdapterDelegate<MutableList<Any>>() {
fun bind(data: NoteWithFormattedBody) {
this.note = data.note
with(itemView) {
avatarImageView.setUserInfo(note.author.id, note.author.avatarUrl)
avatarImageView.bindShortUser(note.author, true)
titleTextView.text = note.author.name
subtitleTextView.text = note.createdAt.humanTime(context.resources)
Markwon.setText(descriptionTextView, data.body)
......
package ru.terrakok.gitlabclient.ui.global.view.custom
import android.content.Context
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.util.TypedValue
import android.view.View
import android.widget.FrameLayout
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
import kotlinx.android.synthetic.main.view_avatar.view.*
import ru.terrakok.cicerone.Router
import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.Screens
import ru.terrakok.gitlabclient.di.DI
import ru.terrakok.gitlabclient.entity.Project
import ru.terrakok.gitlabclient.entity.ShortUser
import ru.terrakok.gitlabclient.entity.app.session.UserAccount
import ru.terrakok.gitlabclient.extension.getTintDrawable
import ru.terrakok.gitlabclient.extension.visible
import toothpick.Toothpick
class AvatarView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private val nameBackgroundColors = intArrayOf(
R.color.green,
R.color.silver,
R.color.grey,
R.color.fruit_salad,
R.color.red,
R.color.brandy_punch,
R.color.blue,
R.color.mariner
)
private var nameBackgroundColor = R.color.green
private var isTextSizeSet = false
init {
View.inflate(context, R.layout.view_avatar, this)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
if (!isTextSizeSet && measuredWidth > 0) {
val width = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48f, resources.displayMetrics).toInt()
val textSize = when {
measuredWidth < width -> 16f
measuredWidth == width -> 18f
else -> 22f
}
avatarName.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize)
isTextSizeSet = true
}
}
fun setData(id: Long, name: String, avatarUrl: String?, clickAction: () -> Unit = {}) {
nameBackgroundColor = nameBackgroundColors[(id % nameBackgroundColors.size).toInt()]
Glide.with(avatarImage)
.clear(avatarImage)
Glide.with(avatarImage)
.load(avatarUrl)
.apply(RequestOptions.placeholderOf(R.drawable.default_img))
.listener(
object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
handleLoadFailed(name)
return true
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
return false
}
}
)
.apply(RequestOptions.circleCropTransform())
.into(avatarImage)
setOnClickListener { clickAction() }
}
private fun handleLoadFailed(name: String) {
val names = name.split(" ")
val shortName = when (names.size) {
0 -> ""
1 -> getShortNameFromSingleWord(names[0])
else -> getShortNameFromMultipleWords(names)
}
if (shortName.length == SHORT_NAME_LENGTH) {
avatarName.text = shortName
avatarName.background = context.getTintDrawable(R.drawable.circle, nameBackgroundColor)
avatarImage.visible(false)
avatarName.visible(true)
} else {
avatarImage.setImageResource(R.drawable.default_img)
avatarImage.visible(true)
avatarName.visible(false)
}
}
private fun getShortNameFromSingleWord(name: String): String {
val result = StringBuilder()
name.forEach {
if (it.isLetter()) {
result.append(it.toUpperCase())
}
if (result.length == SHORT_NAME_LENGTH) {
return result.toString()
}
}
return result.toString()
}
private fun getShortNameFromMultipleWords(names: List<String>): String {
val result = StringBuilder()
names.forEach {
val letter = it.find { char -> char.isLetter() }
if (letter != null) {
result.append(letter.toUpperCase())
}
if (result.length == SHORT_NAME_LENGTH) {
return result.toString()
}
}
return result.toString()
}
companion object {
private const val SHORT_NAME_LENGTH = 2
}
}
fun AvatarView.bindShortUser(shortUser: ShortUser, withNavigation: Boolean) {
with(shortUser) {
if (withNavigation) {
val router = Toothpick.openScope(DI.APP_SCOPE).getInstance(Router::class.java)
setData(id, name, avatarUrl) { router.navigateTo(Screens.UserFlow(id)) }
} else {
setData(id, name, avatarUrl)
}
}
}
fun AvatarView.bindUserAccount(userAccount: UserAccount, withNavigation: Boolean) {
with(userAccount) {
if (withNavigation) {
val router = Toothpick.openScope(DI.APP_SCOPE).getInstance(Router::class.java)
setData(userId, userName, avatarUrl) { router.navigateTo(Screens.UserFlow(userId)) }
} else {
setData(userId, userName, avatarUrl)
}
}
}
fun AvatarView.bindProject(project: Project) {
with(project) {
setData(id, name, avatarUrl, {})
}
}
\ No newline at end of file
package ru.terrakok.gitlabclient.ui.global.view.custom
import android.content.Context
import android.util.AttributeSet
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import ru.terrakok.cicerone.Router
import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.Screens
import ru.terrakok.gitlabclient.di.DI
import toothpick.Toothpick
class UserAvatarView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ImageView(context, attrs, defStyleAttr) {
private val router = Toothpick.openScope(DI.APP_SCOPE).getInstance(Router::class.java)
private var userId: Long? = null
init {
setOnClickListener {
userId?.let {
router.navigateTo(Screens.UserFlow(it))
}
}
}
fun setUserInfo(userId: Long, avatarUrl: String?) {
this.userId = userId
Glide.with(context)
.load(avatarUrl)
.apply(
RequestOptions().apply {
placeholder(R.drawable.default_img)
error(R.drawable.default_img)
}
)
.apply(RequestOptions.circleCropTransform())
.into(this)
}
}
\ No newline at end of file
......@@ -14,6 +14,7 @@ import ru.terrakok.gitlabclient.extension.visible
import ru.terrakok.gitlabclient.presentation.issue.details.IssueDetailsPresenter
import ru.terrakok.gitlabclient.presentation.issue.details.IssueDetailsView
import ru.terrakok.gitlabclient.ui.global.BaseFragment
import ru.terrakok.gitlabclient.ui.global.view.custom.bindShortUser
/**
* Created by Eugene Shapovalov (@CraggyHaggy) on 26.05.19.
......@@ -56,7 +57,7 @@ class IssueDetailsFragment : BaseFragment(), IssueDetailsView {
}
}
}
avatarImageView.setUserInfo(issue.author.id, issue.author.avatarUrl)
avatarImageView.bindShortUser(issue.author, true)
Markwon.setText(descriptionTextView, mdDescription)
}
......
......@@ -14,6 +14,7 @@ import ru.terrakok.gitlabclient.extension.visible
import ru.terrakok.gitlabclient.presentation.mergerequest.details.MergeRequestDetailsPresenter
import ru.terrakok.gitlabclient.presentation.mergerequest.details.MergeRequestDetailsView
import ru.terrakok.gitlabclient.ui.global.BaseFragment
import ru.terrakok.gitlabclient.ui.global.view.custom.bindShortUser
/**
* Created by Eugene Shapovalov (@CraggyHaggy) on 31.05.19.
......@@ -70,7 +71,7 @@ class MergeRequestDetailsFragment : BaseFragment(), MergeRequestDetailsView {
}
}
}
avatarImageView.setUserInfo(mr.author.id, mr.author.avatarUrl)
avatarImageView.bindShortUser(mr.author, true)
Markwon.setText(descriptionTextView, mdDescription)
}
......
......@@ -9,10 +9,14 @@ import ru.noties.markwon.Markwon
import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.entity.Project
import ru.terrakok.gitlabclient.entity.Visibility
import ru.terrakok.gitlabclient.extension.*
import ru.terrakok.gitlabclient.extension.getTintDrawable
import ru.terrakok.gitlabclient.extension.setStartDrawable
import ru.terrakok.gitlabclient.extension.showSnackMessage
import ru.terrakok.gitlabclient.extension.visible
import ru.terrakok.gitlabclient.presentation.project.info.ProjectInfoPresenter
import ru.terrakok.gitlabclient.presentation.project.info.ProjectInfoView
import ru.terrakok.gitlabclient.ui.global.BaseFragment
import ru.terrakok.gitlabclient.ui.global.view.custom.bindProject
/**
* @author Konstantin Tskhovrebov (aka terrakok) on 27.04.17.
......@@ -43,7 +47,7 @@ class ProjectInfoFragment : BaseFragment(), ProjectInfoView {
starsTextView.text = project.starCount.toString()
forksTextView.text = project.forksCount.toString()
avatarImageView.loadRoundedImage(project.avatarUrl, context)
avatarImageView.bindProject(project)
iconImageView.setBackgroundResource(R.drawable.circle)
iconImageView.setImageResource(
when (project.visibility) {
......
......@@ -6,10 +6,14 @@ import com.arellomobile.mvp.presenter.ProvidePresenter
import kotlinx.android.synthetic.main.fragment_user_info.*
import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.entity.User
import ru.terrakok.gitlabclient.extension.*
import ru.terrakok.gitlabclient.extension.shareText
import ru.terrakok.gitlabclient.extension.showSnackMessage
import ru.terrakok.gitlabclient.extension.showTextOrHide
import ru.terrakok.gitlabclient.extension.tryOpenLink
import ru.terrakok.gitlabclient.presentation.user.info.UserInfoPresenter
import ru.terrakok.gitlabclient.presentation.user.info.UserInfoView
import ru.terrakok.gitlabclient.ui.global.BaseFragment
import ru.terrakok.gitlabclient.ui.global.view.custom.bindShortUser
/**
* Created by Konstantin Tskhovrebov (aka @terrakok) on 25.11.17.
......@@ -54,7 +58,7 @@ class UserInfoFragment : BaseFragment(), UserInfoView {
override fun showUser(user: User) {
this.user = user
toolbar.title = user.username
avatarImageView.loadRoundedImage(user.avatarUrl, context)
avatarImageView.bindShortUser(user.toShortUser(), false)
usernameTextView.text = user.name
userIdTextView.text = "@${user.username}"
......
......@@ -14,15 +14,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ru.terrakok.gitlabclient.ui.global.view.custom.UserAvatarView
<ru.terrakok.gitlabclient.ui.global.view.custom.AvatarView
android:id="@+id/avatarImageView"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/default_img" />
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/titleTextView"
......
......@@ -14,15 +14,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ru.terrakok.gitlabclient.ui.global.view.custom.UserAvatarView
<ru.terrakok.gitlabclient.ui.global.view.custom.AvatarView
android:id="@+id/avatarImageView"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/default_img" />
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/titleTextView"
......
......@@ -22,12 +22,11 @@
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ru.terrakok.gitlabclient.ui.global.view.custom.UserAvatarView
<ru.terrakok.gitlabclient.ui.global.view.custom.AvatarView
android:id="@+id/avatarImageView"
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_margin="16dp"
android:src="@drawable/default_img"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
......
......@@ -15,14 +15,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
<ru.terrakok.gitlabclient.ui.global.view.custom.AvatarView
android:id="@+id/avatarImageView"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="16dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/default_img" />
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iconImageView"
......
......@@ -27,14 +27,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
<ru.terrakok.gitlabclient.ui.global.view.custom.AvatarView
android:id="@+id/avatarImageView"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="16dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"