77package com.owncloud.android.ui.activities.adapter
88
99import android.content.Context
10- import android.graphics.Typeface
11- import android.graphics.drawable.Drawable
12- import android.text.Spannable
1310import android.text.SpannableStringBuilder
14- import android.text.TextPaint
15- import android.text.TextUtils
1611import android.text.format.DateFormat
1712import android.text.format.DateUtils
18- import android.text.style.ClickableSpan
19- import android.text.style.ForegroundColorSpan
20- import android.text.style.StyleSpan
2113import android.view.LayoutInflater
2214import android.view.View
2315import android.view.ViewGroup
@@ -29,12 +21,12 @@ import androidx.annotation.DrawableRes
2921import androidx.fragment.app.FragmentActivity
3022import androidx.lifecycle.lifecycleScope
3123import androidx.recyclerview.widget.RecyclerView
32- import com.google.android.material.chip.ChipDrawable
3324import com.nextcloud.android.common.ui.theme.utils.ColorRole
3425import com.nextcloud.client.account.CurrentAccountProvider
3526import com.nextcloud.common.NextcloudClient
3627import com.nextcloud.utils.GlideHelper
37- import com.nextcloud.utils.text.MentionChipSpan
28+ import com.nextcloud.utils.text.RichSubjectFormatter
29+ import com.nextcloud.utils.text.RichSubjectParam
3830import com.owncloud.android.MainApp
3931import com.owncloud.android.R
4032import com.owncloud.android.databinding.ActivityListItemBinding
@@ -53,7 +45,6 @@ import com.owncloud.android.utils.theme.ViewThemeUtils
5345import kotlinx.coroutines.Dispatchers
5446import kotlinx.coroutines.launch
5547import kotlinx.coroutines.withContext
56- import thirdparties.fresco.BetterImageSpan
5748import java.util.Locale
5849import kotlin.math.floor
5950import kotlin.math.log
@@ -67,13 +58,13 @@ open class ActivityListAdapter(
6758 private val isDetailView : Boolean ,
6859 private val viewThemeUtils : ViewThemeUtils
6960) : RecyclerView.Adapter<RecyclerView.ViewHolder>(),
70- StickyHeaderAdapter ,
71- DisplayUtils .AvatarGenerationListener {
61+ StickyHeaderAdapter {
7262
7363 protected var client: NextcloudClient ? = null
7464 val values: MutableList <Any > = mutableListOf ()
7565 private val px = getThumbnailDimension()
7666 private var cachedNextcloudClient: NextcloudClient ? = null
67+ private val richSubjectFormatter by lazy { RichSubjectFormatter (context, currentAccountProvider) }
7768
7869 // region Public Methods
7970 @Suppress(" NotifyDataSetChanged" )
@@ -138,12 +129,6 @@ open class ActivityListAdapter(
138129
139130 override fun isHeader (itemPosition : Int ) =
140131 itemPosition in values.indices && getItemViewType(itemPosition) == HEADER_TYPE
141-
142- override fun avatarGenerated (avatarDrawable : Drawable , callContext : Any ) {
143- (callContext as ChipDrawable ).chipIcon = avatarDrawable
144- }
145-
146- override fun shouldCallGeneratedCallback (tag : String , callContext : Any ): Boolean = true
147132 // endregion
148133
149134 // region Private Methods
@@ -248,15 +233,6 @@ open class ActivityListAdapter(
248233 }
249234 }
250235
251- private fun getDrawableForMentionChipSpan (chipResource : Int , text : String ): ChipDrawable =
252- ChipDrawable .createFromResource(context, chipResource).apply {
253- setEllipsize(TextUtils .TruncateAt .MIDDLE )
254- layoutDirection = context.resources.configuration.layoutDirection
255- setText(text)
256- setChipIconResource(R .drawable.accent_circle)
257- setBounds(0 , 0 , intrinsicWidth, intrinsicHeight)
258- }
259-
260236 private suspend fun nextcloudClient (): NextcloudClient = cachedNextcloudClient ? : withContext(Dispatchers .IO ) {
261237 OwnCloudClientManagerFactory .getDefaultSingleton()
262238 .getNextcloudClientFor(currentAccountProvider.user.toOwnCloudAccount(), context)
@@ -302,83 +278,16 @@ open class ActivityListAdapter(
302278 return imageView
303279 }
304280
305- private fun addClickablePart (richElement : RichElement ): SpannableStringBuilder {
306- var text = richElement.richSubject
307- val ssb = SpannableStringBuilder (text)
308-
309- var start = text.indexOf(PLACEHOLDER_START )
310- while (start != - 1 ) {
311- val end = text.indexOf(PLACEHOLDER_END , start) + 1
312- val tag = text.substring(start + 1 , end - 1 )
313- val richObject = richElement.richObjectList.firstOrNull { it.tag.equals(tag, ignoreCase = true ) }
314-
315- val nextSearchStart = when {
316- richObject == null -> end
317-
318- richObject.type == USER_TYPE -> {
319- ssb.applyMentionSpan(richObject, start, end)
320- end
321- }
322-
323- else -> {
324- val nameEnd = ssb.applyClickableNameSpan(richObject, start, end)
325- text = ssb.toString()
326- nameEnd
327- }
328- }
329-
330- start = text.indexOf(PLACEHOLDER_START , nextSearchStart)
331- }
332-
333- return ssb
334- }
335-
336- private fun SpannableStringBuilder.applyMentionSpan (richObject : RichObject , start : Int , end : Int ) {
337- val name = richObject.name
338- val chip = getDrawableForMentionChipSpan(R .xml.chip_others, name ? : " " )
339- val span = MentionChipSpan (chip, BetterImageSpan .ALIGN_CENTER , richObject.id ? : " " , name)
340-
341- richObject.id?.let { id ->
342- DisplayUtils .setAvatar(
343- currentAccountProvider.user,
344- id,
345- name,
346- this @ActivityListAdapter,
347- context.resources.getDimension(R .dimen.avatar_icon_radius),
348- context.resources,
349- chip,
350- context
351- )
281+ private fun addClickablePart (richElement : RichElement ): SpannableStringBuilder =
282+ richSubjectFormatter.format(richElement.richSubject) { tag ->
283+ richElement.richObjectList
284+ .firstOrNull { it.tag.equals(tag, ignoreCase = true ) }
285+ ?.toRichSubjectParam()
352286 }
353287
354- setSpan(span, start, end, Spannable .SPAN_INCLUSIVE_EXCLUSIVE )
355- }
356-
357- private fun SpannableStringBuilder.applyClickableNameSpan (richObject : RichObject , start : Int , end : Int ): Int {
358- val name = richObject.name.orEmpty()
359- replace(start, end, name)
360- val nameEnd = start + name.length
361-
362- setSpan(
363- object : ClickableSpan () {
364- override fun onClick (widget : View ) = activityListInterface.onActivityClicked(richObject)
365- override fun updateDrawState (ds : TextPaint ) {
366- ds.isUnderlineText = false
367- }
368- },
369- start,
370- nameEnd,
371- 0
372- )
373- setSpan(StyleSpan (Typeface .BOLD ), start, nameEnd, 0 )
374- setSpan(
375- ForegroundColorSpan (context.resources.getColor(R .color.text_color)),
376- start,
377- nameEnd,
378- Spannable .SPAN_EXCLUSIVE_EXCLUSIVE
379- )
380-
381- return nameEnd
288+ private fun RichObject.toRichSubjectParam (): RichSubjectParam {
289+ val richObject = this
290+ return RichSubjectParam (type, id, name) { activityListInterface.onActivityClicked(richObject) }
382291 }
383292
384293 private fun getThumbnailDimension (): Int {
@@ -397,9 +306,6 @@ open class ActivityListAdapter(
397306 const val HEADER_TYPE = 100
398307 const val ACTIVITY_TYPE = 101
399308 private const val COLORED_ICON_SUFFIX = " -color.svg"
400- private const val PLACEHOLDER_START = ' {'
401- private const val PLACEHOLDER_END = ' }'
402- private const val USER_TYPE = " user"
403309 private const val TIME_PATTERN = " HH:mm"
404310 private const val HEADER_DATE_SKELETON = " EEEE, MMMM d"
405311 private const val PREVIEW_COLUMN_SPACING = 20
0 commit comments