diff --git a/app/build.gradle b/app/build.gradle index 12b174e0b5..58cc5e064f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -60,12 +60,16 @@ dependencies { def epoxy_version = "3.0.0" def arrow_version = "0.8.2" + def coroutines_version = "1.0.1" + def markwon_version = '3.0.0-SNAPSHOT' implementation project(":matrix-sdk-android") implementation project(":matrix-sdk-android-rx") implementation 'com.android.support:multidex:1.0.3' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" implementation 'androidx.appcompat:appcompat:1.1.0-alpha01' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' @@ -97,6 +101,9 @@ dependencies { implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' implementation 'com.google.android.material:material:1.1.0-alpha02' implementation 'me.gujun.android:span:1.7' + implementation "ru.noties.markwon:core:$markwon_version" + implementation "ru.noties.markwon:html:$markwon_version" + // DI implementation "org.koin:koin-android:$koin_version" diff --git a/app/src/main/java/im/vector/riotredesign/core/extensions/Activity.kt b/app/src/main/java/im/vector/riotredesign/core/extensions/Activity.kt index e908974ebe..8c23d795c2 100644 --- a/app/src/main/java/im/vector/riotredesign/core/extensions/Activity.kt +++ b/app/src/main/java/im/vector/riotredesign/core/extensions/Activity.kt @@ -29,4 +29,8 @@ fun AppCompatActivity.replaceFragment(fragment: Fragment, frameId: Int) { fun AppCompatActivity.addFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) { supportFragmentManager.inTransaction { replace(frameId, fragment).addToBackStack(tag) } +} + +fun AppCompatActivity.hideKeyboard() { + currentFocus?.hideKeyboard() } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/core/extensions/Event.kt b/app/src/main/java/im/vector/riotredesign/core/extensions/Event.kt index 098d1af576..d93877a6bd 100644 --- a/app/src/main/java/im/vector/riotredesign/core/extensions/Event.kt +++ b/app/src/main/java/im/vector/riotredesign/core/extensions/Event.kt @@ -17,12 +17,9 @@ package im.vector.riotredesign.core.extensions import im.vector.matrix.android.api.session.events.model.Event -import org.threeten.bp.Instant +import im.vector.riotredesign.core.resources.DateProvider import org.threeten.bp.LocalDateTime -import org.threeten.bp.ZoneId - fun Event.localDateTime(): LocalDateTime { - val instant = Instant.ofEpochMilli(originServerTs ?: 0) - return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()) + return DateProvider.toLocalDateTime(originServerTs) } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/core/resources/DateProvider.kt b/app/src/main/java/im/vector/riotredesign/core/resources/DateProvider.kt new file mode 100644 index 0000000000..cc01bdf183 --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/core/resources/DateProvider.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotredesign.core.resources + +import org.threeten.bp.Instant +import org.threeten.bp.LocalDateTime +import org.threeten.bp.ZoneId + +object DateProvider { + + private val zoneId = ZoneId.systemDefault() + + fun toLocalDateTime(timestamp: Long?): LocalDateTime { + val instant = Instant.ofEpochMilli(timestamp ?: 0) + return LocalDateTime.ofInstant(instant, zoneId) + } + +} \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt b/app/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt index 5e66ddba27..3227dc6502 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt @@ -16,16 +16,21 @@ package im.vector.riotredesign.features.home +import android.content.Context +import android.graphics.drawable.Drawable import android.widget.ImageView import androidx.core.content.ContextCompat import com.amulyakhare.textdrawable.TextDrawable import com.bumptech.glide.request.RequestOptions import im.vector.matrix.android.api.Matrix +import im.vector.matrix.android.api.MatrixPatterns import im.vector.matrix.android.api.session.room.model.RoomMember import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.riotredesign.R -import im.vector.riotredesign.core.extensions.firstCharAsString import im.vector.riotredesign.core.glide.GlideApp +import im.vector.riotredesign.core.glide.GlideRequest +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch object AvatarRenderer { @@ -41,17 +46,47 @@ object AvatarRenderer { if (name.isNullOrEmpty()) { return } - val resolvedUrl = Matrix.getInstance().currentSession.contentUrlResolver().resolveFullSize(avatarUrl) - val avatarColor = ContextCompat.getColor(imageView.context, R.color.pale_teal) - val fallbackDrawable = TextDrawable.builder().buildRound(name.firstCharAsString().toUpperCase(), avatarColor) - - GlideApp - .with(imageView) - .load(resolvedUrl) - .placeholder(fallbackDrawable) - .apply(RequestOptions.circleCropTransform()) + val placeholder = buildPlaceholderDrawable(imageView.context, name) + buildGlideRequest(imageView.context, avatarUrl) + .placeholder(placeholder) .into(imageView) } + fun load(context: Context, avatarUrl: String?, name: String?, size: Int, callback: Callback) { + if (name.isNullOrEmpty()) { + return + } + val request = buildGlideRequest(context, avatarUrl) + GlobalScope.launch { + val placeholder = buildPlaceholderDrawable(context, name) + callback.onDrawableUpdated(placeholder) + try { + val drawable = request.submit(size, size).get() + callback.onDrawableUpdated(drawable) + } catch (exception: Exception) { + callback.onDrawableUpdated(placeholder) + } + } + } + + private fun buildGlideRequest(context: Context, avatarUrl: String?): GlideRequest { + val resolvedUrl = Matrix.getInstance().currentSession.contentUrlResolver().resolveFullSize(avatarUrl) + return GlideApp + .with(context) + .load(resolvedUrl) + .apply(RequestOptions.circleCropTransform()) + } + + private fun buildPlaceholderDrawable(context: Context, name: String): Drawable { + val avatarColor = ContextCompat.getColor(context, R.color.pale_teal) + val isNameUserId = MatrixPatterns.isUserId(name) + val firstLetterIndex = if (isNameUserId) 1 else 0 + val firstLetter = name[firstLetterIndex].toString().toUpperCase() + return TextDrawable.builder().buildRound(firstLetter, avatarColor) + } + + interface Callback { + fun onDrawableUpdated(drawable: Drawable?) + } } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt b/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt index 087a00eaec..12842034f8 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt @@ -24,9 +24,11 @@ import android.view.MenuItem import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.widget.Toolbar import androidx.core.view.GravityCompat +import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.FragmentManager import com.airbnb.mvrx.viewModel import im.vector.riotredesign.R +import im.vector.riotredesign.core.extensions.hideKeyboard import im.vector.riotredesign.core.extensions.observeEvent import im.vector.riotredesign.core.extensions.replaceFragment import im.vector.riotredesign.core.platform.OnBackPressed @@ -44,11 +46,18 @@ class HomeActivity : RiotActivity(), ToolbarConfigurable { private val homeActivityViewModel: HomeActivityViewModel by viewModel() private val homeNavigator by inject() + private val drawerListener = object : DrawerLayout.SimpleDrawerListener() { + override fun onDrawerStateChanged(newState: Int) { + hideKeyboard() + } + } + override fun onCreate(savedInstanceState: Bundle?) { - loadKoinModules(listOf(HomeModule().definition)) + loadKoinModules(listOf(HomeModule(this).definition)) homeNavigator.activity = this super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) + drawerLayout.addDrawerListener(drawerListener) if (savedInstanceState == null) { val homeDrawerFragment = HomeDrawerFragment.newInstance() val loadingDetail = LoadingRoomDetailFragment.newInstance() @@ -61,6 +70,7 @@ class HomeActivity : RiotActivity(), ToolbarConfigurable { } override fun onDestroy() { + drawerLayout.removeDrawerListener(drawerListener) homeNavigator.activity = null super.onDestroy() } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt b/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt index 2f2d85a026..2862765708 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt @@ -16,24 +16,43 @@ package im.vector.riotredesign.features.home +import im.vector.matrix.android.api.Matrix import im.vector.riotredesign.features.home.group.SelectedGroupStore import im.vector.riotredesign.features.home.room.VisibleRoomStore -import im.vector.riotredesign.features.home.room.detail.timeline.* +import im.vector.riotredesign.features.home.room.detail.timeline.CallItemFactory +import im.vector.riotredesign.features.home.room.detail.timeline.DefaultItemFactory +import im.vector.riotredesign.features.home.room.detail.timeline.MessageItemFactory +import im.vector.riotredesign.features.home.room.detail.timeline.RoomHistoryVisibilityItemFactory +import im.vector.riotredesign.features.home.room.detail.timeline.RoomMemberItemFactory +import im.vector.riotredesign.features.home.room.detail.timeline.RoomNameItemFactory +import im.vector.riotredesign.features.home.room.detail.timeline.RoomTopicItemFactory +import im.vector.riotredesign.features.home.room.detail.timeline.TimelineDateFormatter +import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController +import im.vector.riotredesign.features.home.room.detail.timeline.TimelineItemFactory import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider import im.vector.riotredesign.features.home.room.list.RoomSummaryComparator import im.vector.riotredesign.features.home.room.list.RoomSummaryController +import im.vector.riotredesign.features.html.EventHtmlRenderer import org.koin.dsl.module.module -class HomeModule { +class HomeModule(homeActivity: HomeActivity) { val definition = module(override = true) { + single { + Matrix.getInstance().currentSession + } + single { TimelineDateFormatter(get()) } single { - MessageItemFactory(get(), get(), get()) + EventHtmlRenderer(homeActivity, get()) + } + + single { + MessageItemFactory(get(), get(), get(), get()) } single { diff --git a/app/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt b/app/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt index 7ffe463bb1..4fb84ea10d 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt @@ -30,13 +30,13 @@ class HomeNavigator { var activity: HomeActivity? = null - private var currentRoomId: String? = null + private var rootRoomId: String? = null fun openRoomDetail(roomId: String, eventId: String?, addToBackstack: Boolean = false) { Timber.v("Open room detail $roomId - $eventId - $addToBackstack") - if (!addToBackstack && isRoomOpened(roomId)) { + if (!addToBackstack && isRoot(roomId)) { return } activity?.let { @@ -46,7 +46,7 @@ class HomeNavigator { if (addToBackstack) { it.addFragmentToBackstack(roomDetailFragment, R.id.homeDetailFragmentContainer, roomId) } else { - currentRoomId = roomId + rootRoomId = roomId clearBackStack(it.supportFragmentManager) it.replaceFragment(roomDetailFragment, R.id.homeDetailFragmentContainer) } @@ -61,9 +61,7 @@ class HomeNavigator { Timber.v("Open user detail $userId") } - fun isRoomOpened(roomId: String): Boolean { - return currentRoomId == roomId - } + // Private Methods ***************************************************************************** private fun clearBackStack(fragmentManager: FragmentManager) { if (fragmentManager.backStackEntryCount > 0) { @@ -72,4 +70,8 @@ class HomeNavigator { } } + private fun isRoot(roomId: String): Boolean { + return rootRoomId == roomId + } + } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt index e2f61cf0c8..d27475f77b 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt @@ -29,12 +29,14 @@ import im.vector.riotredesign.core.epoxy.RiotEpoxyModel import im.vector.riotredesign.core.extensions.localDateTime import im.vector.riotredesign.core.resources.ColorProvider import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider +import im.vector.riotredesign.features.html.EventHtmlRenderer import im.vector.riotredesign.features.media.MediaContentRenderer import me.gujun.android.span.span class MessageItemFactory(private val colorProvider: ColorProvider, private val timelineMediaSizeProvider: TimelineMediaSizeProvider, - private val timelineDateFormatter: TimelineDateFormatter) { + private val timelineDateFormatter: TimelineDateFormatter, + private val htmlRenderer: EventHtmlRenderer) { private val messagesDisplayedWithInformation = HashSet() @@ -102,9 +104,15 @@ class MessageItemFactory(private val colorProvider: ColorProvider, informationData: MessageInformationData, callback: TimelineEventController.Callback?): MessageTextItem? { - val message = linkifyBody(messageContent.body, callback) + val bodyToUse = messageContent.formattedBody + ?.let { + htmlRenderer.render(it) + } + ?: messageContent.body + + val linkifiedBody = linkifyBody(bodyToUse, callback) return MessageTextItem_() - .message(message) + .message(linkifiedBody) .informationData(informationData) } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt index 0d12ae8e45..ec865e4b8b 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt @@ -37,13 +37,14 @@ class TimelineEventController(private val roomId: String, EpoxyAsyncUtil.getAsyncBackgroundHandler(), EpoxyAsyncUtil.getAsyncBackgroundHandler() ) { - init { - setFilterDuplicates(true) - } private var isLoadingForward: Boolean = false private var isLoadingBackward: Boolean = false - private var hasReachedEnd: Boolean = false + private var hasReachedEnd: Boolean = true + + init { + requestModelBuild() + } var callback: Callback? = null diff --git a/app/src/main/java/im/vector/riotredesign/features/html/EventHtmlRenderer.kt b/app/src/main/java/im/vector/riotredesign/features/html/EventHtmlRenderer.kt new file mode 100644 index 0000000000..dcbe28f11b --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/features/html/EventHtmlRenderer.kt @@ -0,0 +1,183 @@ +/* + * + * * Copyright 2019 New Vector Ltd + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package im.vector.riotredesign.features.html + +import android.content.Context +import android.text.style.ImageSpan +import im.vector.matrix.android.api.permalinks.PermalinkData +import im.vector.matrix.android.api.permalinks.PermalinkParser +import im.vector.matrix.android.api.session.Session +import org.commonmark.node.BlockQuote +import org.commonmark.node.HtmlBlock +import org.commonmark.node.HtmlInline +import org.commonmark.node.Node +import ru.noties.markwon.AbstractMarkwonPlugin +import ru.noties.markwon.Markwon +import ru.noties.markwon.MarkwonConfiguration +import ru.noties.markwon.MarkwonVisitor +import ru.noties.markwon.SpannableBuilder +import ru.noties.markwon.html.HtmlTag +import ru.noties.markwon.html.MarkwonHtmlParserImpl +import ru.noties.markwon.html.MarkwonHtmlRenderer +import ru.noties.markwon.html.TagHandler +import ru.noties.markwon.html.tag.BlockquoteHandler +import ru.noties.markwon.html.tag.EmphasisHandler +import ru.noties.markwon.html.tag.HeadingHandler +import ru.noties.markwon.html.tag.ImageHandler +import ru.noties.markwon.html.tag.LinkHandler +import ru.noties.markwon.html.tag.ListHandler +import ru.noties.markwon.html.tag.StrikeHandler +import ru.noties.markwon.html.tag.StrongEmphasisHandler +import ru.noties.markwon.html.tag.SubScriptHandler +import ru.noties.markwon.html.tag.SuperScriptHandler +import ru.noties.markwon.html.tag.UnderlineHandler +import java.util.Arrays.asList + +class EventHtmlRenderer(private val context: Context, + private val session: Session) { + + private val markwon = Markwon.builder(context) + .usePlugin(MatrixPlugin.create(context, session)) + .build() + + fun render(text: String): CharSequence { + return markwon.toMarkdown(text) + } + +} + +private class MatrixPlugin private constructor(private val context: Context, + private val session: Session) : AbstractMarkwonPlugin() { + + override fun configureConfiguration(builder: MarkwonConfiguration.Builder) { + builder.htmlParser(MarkwonHtmlParserImpl.create()) + } + + override fun configureHtmlRenderer(builder: MarkwonHtmlRenderer.Builder) { + builder + .addHandler( + "img", + ImageHandler.create()) + .addHandler( + "a", + MxLinkHandler(context, session)) + .addHandler( + "blockquote", + BlockquoteHandler()) + .addHandler( + "sub", + SubScriptHandler()) + .addHandler( + "sup", + SuperScriptHandler()) + .addHandler( + asList("b", "strong"), + StrongEmphasisHandler()) + .addHandler( + asList("s", "del"), + StrikeHandler()) + .addHandler( + asList("u", "ins"), + UnderlineHandler()) + .addHandler( + asList("ul", "ol"), + ListHandler()) + .addHandler( + asList("i", "em", "cite", "dfn"), + EmphasisHandler()) + .addHandler( + asList("h1", "h2", "h3", "h4", "h5", "h6"), + HeadingHandler()) + .addHandler("mx-reply", + MxReplyTagHandler()) + + } + + override fun afterRender(node: Node, visitor: MarkwonVisitor) { + val configuration = visitor.configuration() + configuration.htmlRenderer().render(visitor, configuration.htmlParser()) + } + + override fun configureVisitor(builder: MarkwonVisitor.Builder) { + builder + .on(HtmlBlock::class.java) { visitor, htmlBlock -> visitHtml(visitor, htmlBlock.literal) } + .on(HtmlInline::class.java) { visitor, htmlInline -> visitHtml(visitor, htmlInline.literal) } + } + + private fun visitHtml(visitor: MarkwonVisitor, html: String?) { + if (html != null) { + visitor.configuration().htmlParser().processFragment(visitor.builder(), html) + } + } + + companion object { + + fun create(context: Context, session: Session): MatrixPlugin { + return MatrixPlugin(context, session) + } + } +} + +private class MxLinkHandler(private val context: Context, private val session: Session) : TagHandler() { + + private val linkHandler = LinkHandler() + + override fun handle(visitor: MarkwonVisitor, renderer: MarkwonHtmlRenderer, tag: HtmlTag) { + val link = tag.attributes()["href"] + if (link != null) { + val permalinkData = PermalinkParser.parse(link) + when (permalinkData) { + is PermalinkData.UserLink -> { + val user = session.getUser(permalinkData.userId) ?: return + val drawable = PillDrawableFactory.create(context, permalinkData.userId, user) + val span = ImageSpan(drawable) + SpannableBuilder.setSpans( + visitor.builder(), + span, + tag.start(), + tag.end() + ) + } + else -> linkHandler.handle(visitor, renderer, tag) + } + } else { + linkHandler.handle(visitor, renderer, tag) + } + } + +} + +private class MxReplyTagHandler : TagHandler() { + + override fun handle(visitor: MarkwonVisitor, renderer: MarkwonHtmlRenderer, tag: HtmlTag) { + val configuration = visitor.configuration() + val factory = configuration.spansFactory().get(BlockQuote::class.java) + if (factory != null) { + SpannableBuilder.setSpans( + visitor.builder(), + factory.getSpans(configuration, visitor.renderProps()), + tag.start(), + tag.end() + ) + val replyText = visitor.builder().removeFromEnd(tag.end()) + visitor.builder().append("\n\n").append(replyText) + } + } + +} diff --git a/app/src/main/java/im/vector/riotredesign/features/html/PillDrawableFactory.kt b/app/src/main/java/im/vector/riotredesign/features/html/PillDrawableFactory.kt new file mode 100644 index 0000000000..9b87c67135 --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/features/html/PillDrawableFactory.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotredesign.features.html + +import android.content.Context +import android.graphics.drawable.Drawable +import com.google.android.material.chip.ChipDrawable +import im.vector.matrix.android.api.session.user.model.User +import im.vector.riotredesign.R +import im.vector.riotredesign.features.home.AvatarRenderer +import java.lang.ref.WeakReference + +object PillDrawableFactory { + + fun create(context: Context, userId: String, user: User?): Drawable { + val textPadding = context.resources.getDimension(R.dimen.pill_text_padding) + + val chipDrawable = ChipDrawable.createFromResource(context, R.xml.pill_view).apply { + setText(user?.displayName ?: userId) + textEndPadding = textPadding + textStartPadding = textPadding + setChipMinHeightResource(R.dimen.pill_min_height) + setChipIconSizeResource(R.dimen.pill_avatar_size) + setBounds(0, 0, intrinsicWidth, intrinsicHeight) + } + val avatarRendererCallback = AvatarRendererChipCallback(chipDrawable) + AvatarRenderer.load(context, user?.avatarUrl, user?.displayName, 80, avatarRendererCallback) + return chipDrawable + } + + private class AvatarRendererChipCallback(chipDrawable: ChipDrawable) : AvatarRenderer.Callback { + + private val weakChipDrawable = WeakReference(chipDrawable) + + override fun onDrawableUpdated(drawable: Drawable?) { + weakChipDrawable.get()?.apply { + chipIcon = drawable + setBounds(0, 0, intrinsicWidth, intrinsicHeight) + } + } + + } + +} + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 9797d9459d..90e3e22a0b 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -17,4 +17,9 @@ #ebedf8 #a5a5a5 #61708B + + #FFC7C7C7 + #FF999999 + #FFF56679 + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000000..3d9cae027d --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,7 @@ + + + + 16dp + 20dp + 4dp + \ No newline at end of file diff --git a/app/src/main/res/values/themes_base.xml b/app/src/main/res/values/themes_base.xml index 04ea854aaf..257f8fc527 100644 --- a/app/src/main/res/values/themes_base.xml +++ b/app/src/main/res/values/themes_base.xml @@ -1,7 +1,7 @@ -