3408 home help message (#3415)
* 3408: First draft of help message on empty home timeline * 3408: Move image spanning to utils; tweak gui a bit (looks like status) * 3408: Use proper R again; appease linter * 3408: Add doc; remove narrow comment * 3408: null is default * 3408: Add German text * 3408: Stack refresh animation on top of help message (reorder)
This commit is contained in:
parent
9484a8b2b9
commit
182df2bfae
9 changed files with 200 additions and 93 deletions
|
|
@ -236,6 +236,9 @@ class TimelineFragment :
|
|||
if (loadState.append is LoadState.NotLoading && loadState.source.refresh is LoadState.NotLoading) {
|
||||
binding.statusView.show()
|
||||
binding.statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty)
|
||||
if (kind == TimelineViewModel.Kind.HOME) {
|
||||
binding.statusView.showHelp(R.string.help_empty_home)
|
||||
}
|
||||
}
|
||||
}
|
||||
is LoadState.Error -> {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,19 @@
|
|||
package com.keylesspalace.tusky.util
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
import android.text.style.CharacterStyle
|
||||
import android.text.style.DynamicDrawableSpan
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.ImageSpan
|
||||
import android.text.style.URLSpan
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.math.max
|
||||
|
||||
|
|
@ -61,6 +70,66 @@ private class PatternFinder(
|
|||
val pattern: Pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE)
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes text containing mentions and hashtags and urls and makes them the given colour.
|
||||
*/
|
||||
fun highlightSpans(text: Spannable, colour: Int) {
|
||||
// Strip all existing colour spans.
|
||||
for (spanClass in spanClasses) {
|
||||
clearSpans(text, spanClass)
|
||||
}
|
||||
|
||||
// Colour the mentions and hashtags.
|
||||
val string = text.toString()
|
||||
val length = text.length
|
||||
var start = 0
|
||||
var end = 0
|
||||
while (end in 0 until length && start >= 0) {
|
||||
// Search for url first because it can contain the other characters
|
||||
val found = findPattern(string, end)
|
||||
start = found.start
|
||||
end = found.end
|
||||
if (start in 0 until end) {
|
||||
text.setSpan(getSpan(found.matchType, string, colour, start, end), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
|
||||
start += finders[found.matchType]!!.searchPrefixWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces text of the form [drawabale name] or [iconics name] with their spanned counterparts (ImageSpan).
|
||||
*/
|
||||
fun addDrawables(text: CharSequence, color: Int, size: Int, context: Context): Spannable {
|
||||
val alignment = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) DynamicDrawableSpan.ALIGN_CENTER else DynamicDrawableSpan.ALIGN_BASELINE
|
||||
|
||||
val builder = SpannableStringBuilder(text)
|
||||
|
||||
val pattern = Pattern.compile("\\[(drawable|iconics) ([0-9a-z_]+)\\]")
|
||||
val matcher = pattern.matcher(builder)
|
||||
while (matcher.find()) {
|
||||
val resourceType = matcher.group(1)
|
||||
val resourceName = matcher.group(2)
|
||||
?: continue
|
||||
|
||||
val drawable: Drawable? = when (resourceType) {
|
||||
"iconics" -> IconicsDrawable(context, GoogleMaterial.getIcon(resourceName))
|
||||
else -> {
|
||||
val drawableResourceId = context.resources.getIdentifier(resourceName, "drawable", context.packageName)
|
||||
if (drawableResourceId != 0) AppCompatResources.getDrawable(context, drawableResourceId) else null
|
||||
}
|
||||
}
|
||||
|
||||
if (drawable != null) {
|
||||
drawable.setBounds(0, 0, size, size)
|
||||
drawable.setTint(color)
|
||||
|
||||
builder.setSpan(ImageSpan(drawable, alignment), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
}
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
private fun <T> clearSpans(text: Spannable, spanClass: Class<T>) {
|
||||
for (span in text.getSpans(0, text.length, spanClass)) {
|
||||
text.removeSpan(span)
|
||||
|
|
@ -136,30 +205,6 @@ private fun getSpan(matchType: FoundMatchType, string: String, colour: Int, star
|
|||
}
|
||||
}
|
||||
|
||||
/** Takes text containing mentions and hashtags and urls and makes them the given colour. */
|
||||
fun highlightSpans(text: Spannable, colour: Int) {
|
||||
// Strip all existing colour spans.
|
||||
for (spanClass in spanClasses) {
|
||||
clearSpans(text, spanClass)
|
||||
}
|
||||
|
||||
// Colour the mentions and hashtags.
|
||||
val string = text.toString()
|
||||
val length = text.length
|
||||
var start = 0
|
||||
var end = 0
|
||||
while (end in 0 until length && start >= 0) {
|
||||
// Search for url first because it can contain the other characters
|
||||
val found = findPattern(string, end)
|
||||
start = found.start
|
||||
end = found.end
|
||||
if (start in 0 until end) {
|
||||
text.setSpan(getSpan(found.matchType, string, colour, start, end), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
|
||||
start += finders[found.matchType]!!.searchPrefixWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isWordCharacters(codePoint: Int): Boolean {
|
||||
return (codePoint in 0x30..0x39) || // [0-9]
|
||||
(codePoint in 0x41..0x5a) || // [A-Z]
|
||||
|
|
|
|||
|
|
@ -6,15 +6,16 @@ import android.view.Gravity
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.databinding.ViewBackgroundMessageBinding
|
||||
import com.keylesspalace.tusky.util.addDrawables
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
|
||||
/**
|
||||
* This view is used for screens with downloadable content which may fail.
|
||||
* Can show an image, text and button below them.
|
||||
* This view is used for screens with content which may be empty or might have failed to download.
|
||||
*/
|
||||
class BackgroundMessageView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
|
|
@ -47,4 +48,15 @@ class BackgroundMessageView @JvmOverloads constructor(
|
|||
binding.button.setOnClickListener(clickListener)
|
||||
binding.button.visible(clickListener != null)
|
||||
}
|
||||
|
||||
fun showHelp(@StringRes helpRes: Int) {
|
||||
val size: Int = binding.helpText.textSize.toInt() + 2
|
||||
val color = binding.helpText.currentTextColor
|
||||
val text = context.getText(helpRes)
|
||||
val textWithDrawables = addDrawables(text, color, size, context)
|
||||
|
||||
binding.helpText.setText(textWithDrawables, TextView.BufferType.SPANNABLE)
|
||||
|
||||
binding.helpText.visible(true)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue