update minSdk to 24, cleanup code (#4014)

closes https://github.com/tuskyapp/Tusky/issues/2607
redo of https://github.com/tuskyapp/Tusky/pull/3593
This commit is contained in:
Konrad Pozniak 2023-09-12 19:25:45 +02:00 committed by GitHub
commit 7dfc8790c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 72 additions and 204 deletions

View file

@ -679,17 +679,12 @@ public class NotificationsAdapter extends RecyclerView.Adapter<RecyclerView.View
);
LinkHelper.setClickableText(statusContent, emojifiedText, statusViewData.getActionable().getMentions(), statusViewData.getActionable().getTags(), listener);
CharSequence emojifiedContentWarning;
if (statusViewData.getSpoilerText() != null) {
emojifiedContentWarning = CustomEmojiHelper.emojify(
statusViewData.getSpoilerText(),
CharSequence emojifiedContentWarning = CustomEmojiHelper.emojify(
statusViewData.getStatus().getSpoilerText(),
statusViewData.getActionable().getEmojis(),
contentWarningDescriptionTextView,
statusDisplayOptions.animateEmojis()
);
} else {
emojifiedContentWarning = "";
}
contentWarningDescriptionTextView.setText(emojifiedContentWarning);
}

View file

@ -213,7 +213,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
final StatusActionListener listener) {
Status actionable = status.getActionable();
String spoilerText = status.getSpoilerText();
String spoilerText = actionable.getSpoilerText();
List<Emoji> emojis = actionable.getEmojis();
boolean sensitive = !TextUtils.isEmpty(spoilerText);
@ -764,7 +764,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
if (payloads == null) {
Status actionable = status.getActionable();
setDisplayName(actionable.getAccount().getName(), actionable.getAccount().getEmojis(), statusDisplayOptions);
setUsername(status.getUsername());
setUsername(actionable.getAccount().getUsername());
setMetaData(status, statusDisplayOptions, listener);
setIsReply(actionable.getInReplyToId() != null);
setReplyCount(actionable.getRepliesCount(), statusDisplayOptions.showStatsInline());
@ -860,11 +860,11 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
String description = context.getString(R.string.description_status,
actionable.getAccount().getDisplayName(),
getContentWarningDescription(context, status),
(TextUtils.isEmpty(status.getSpoilerText()) || !actionable.getSensitive() || status.isExpanded() ? status.getContent() : ""),
(TextUtils.isEmpty(actionable.getSpoilerText()) || !actionable.getSensitive() || status.isExpanded() ? status.getContent() : ""),
getCreatedAtDescription(actionable.getCreatedAt(), statusDisplayOptions),
actionable.getEditedAt() != null ? context.getString(R.string.description_post_edited) : "",
getReblogDescription(context, status),
status.getUsername(),
actionable.getAccount().getUsername(),
actionable.getReblogged() ? context.getString(R.string.description_post_reblogged) : "",
actionable.getFavourited() ? context.getString(R.string.description_post_favourited) : "",
actionable.getBookmarked() ? context.getString(R.string.description_post_bookmarked) : "",
@ -911,8 +911,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
private static CharSequence getContentWarningDescription(Context context,
@NonNull StatusViewData.Concrete status) {
if (!TextUtils.isEmpty(status.getSpoilerText())) {
return context.getString(R.string.description_post_cw, status.getSpoilerText());
if (!TextUtils.isEmpty(status.getActionable().getSpoilerText())) {
return context.getString(R.string.description_post_cw, status.getActionable().getSpoilerText());
} else {
return "";
}

View file

@ -78,7 +78,7 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
if (payloads == null) {
TimelineAccount account = status.getAccount();
setupCollapsedState(statusViewData.isCollapsible(), statusViewData.isCollapsed(), statusViewData.isExpanded(), statusViewData.getSpoilerText(), listener);
setupCollapsedState(statusViewData.isCollapsible(), statusViewData.isCollapsed(), statusViewData.isExpanded(), status.getSpoilerText(), listener);
setDisplayName(account.getDisplayName(), account.getEmojis(), statusDisplayOptions);
setUsername(account.getUsername());

View file

@ -201,8 +201,7 @@ public class NotificationHelper {
builder.setLargeIcon(accountAvatar);
// Reply to mention action; RemoteInput is available from KitKat Watch, but buttons are available from Nougat
if (body.getType() == Notification.Type.MENTION
&& android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (body.getType() == Notification.Type.MENTION) {
RemoteInput replyRemoteInput = new RemoteInput.Builder(KEY_REPLY)
.setLabel(context.getString(R.string.label_quick_reply))
.build();
@ -859,7 +858,7 @@ public class NotificationHelper {
if (mutable) {
return PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_MUTABLE : 0);
} else {
return PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0);
return PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;
}
}
}

View file

@ -103,15 +103,15 @@ class StatusViewHolder(
shouldTrimStatus(viewdata.content),
viewState.isCollapsed(viewdata.id, true),
viewState.isContentShow(viewdata.id, viewdata.status.sensitive),
viewdata.spoilerText
viewdata.status.spoilerText
)
if (viewdata.spoilerText.isBlank()) {
if (viewdata.status.spoilerText.isBlank()) {
setTextVisible(true, viewdata.content, viewdata.status.mentions, viewdata.status.tags, viewdata.status.emojis, adapterHandler)
binding.statusContentWarningButton.hide()
binding.statusContentWarningDescription.hide()
} else {
val emojiSpoiler = viewdata.spoilerText.emojify(viewdata.status.emojis, binding.statusContentWarningDescription, statusDisplayOptions.animateEmojis)
val emojiSpoiler = viewdata.status.spoilerText.emojify(viewdata.status.emojis, binding.statusContentWarningDescription, statusDisplayOptions.animateEmojis)
binding.statusContentWarningDescription.text = emojiSpoiler
binding.statusContentWarningDescription.show()
binding.statusContentWarningButton.show()

View file

@ -15,7 +15,6 @@
package com.keylesspalace.tusky.service
import android.annotation.TargetApi
import android.content.Intent
import android.service.quicksettings.TileService
import com.keylesspalace.tusky.MainActivity
@ -25,8 +24,6 @@ import com.keylesspalace.tusky.components.compose.ComposeActivity
* Small Addition that adds in a QuickSettings tile
* opens the Compose activity or shows an account selector when multiple accounts are present
*/
@TargetApi(24)
class TuskyTileService : TileService() {
override fun onClick() {

View file

@ -48,7 +48,7 @@ class ListStatusAccessibilityDelegate(
val pos = recyclerView.getChildAdapterPosition(host)
val status = statusProvider.getStatus(pos) ?: return
if (status is StatusViewData.Concrete) {
if (status.spoilerText.isNotEmpty()) {
if (status.status.spoilerText.isNotEmpty()) {
info.addAction(if (status.isExpanded) collapseCwAction else expandCwAction)
}

View file

@ -15,6 +15,7 @@
package com.keylesspalace.tusky.util
import android.icu.text.BreakIterator
import android.text.InputFilter
import android.text.SpannableStringBuilder
import android.text.Spanned
@ -72,20 +73,10 @@ object SmartLengthInputFilter : InputFilter {
if (source[keep].isLetterOrDigit()) {
var boundary: Int
// Android N+ offer a clone of the ICU APIs in Java for better internationalization and
// unicode support. Using the ICU version of BreakIterator grants better support for
// those without having to add the ICU4J library at a minimum Api trade-off.
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
val iterator = android.icu.text.BreakIterator.getWordInstance()
iterator.setText(source.toString())
boundary = iterator.following(keep)
if (keep - boundary > RUNWAY) boundary = iterator.preceding(keep)
} else {
val iterator = java.text.BreakIterator.getWordInstance()
iterator.setText(source.toString())
boundary = iterator.following(keep)
if (keep - boundary > RUNWAY) boundary = iterator.preceding(keep)
}
val iterator = BreakIterator.getWordInstance()
iterator.setText(source.toString())
boundary = iterator.following(keep)
if (keep - boundary > RUNWAY) boundary = iterator.preceding(keep)
keep = boundary
} else {

View file

@ -18,7 +18,6 @@
package com.keylesspalace.tusky.util
import android.text.Html.TagHandler
import android.text.SpannableStringBuilder
import android.text.Spanned
import androidx.core.text.parseAsHtml
@ -36,31 +35,3 @@ fun String.parseAsMastodonHtml(tagHandler: TagHandler? = null): Spanned {
* most status contents do, so it should be trimmed. */
.trimTrailingWhitespace()
}
fun replaceCrashingCharacters(content: Spanned): Spanned {
return replaceCrashingCharacters(content as CharSequence) as Spanned
}
fun replaceCrashingCharacters(content: CharSequence): CharSequence? {
var replacing = false
var builder: SpannableStringBuilder? = null
val length = content.length
for (index in 0 until length) {
val character = content[index]
// If there are more than one or two, switch to a map
if (character == SOFT_HYPHEN) {
if (!replacing) {
replacing = true
builder = SpannableStringBuilder(content, 0, index)
}
builder!!.append(ASCII_HYPHEN)
} else if (replacing) {
builder!!.append(character)
}
}
return if (replacing) builder else content
}
private const val SOFT_HYPHEN = '\u00ad'
private const val ASCII_HYPHEN = '-'

View file

@ -14,12 +14,10 @@
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.viewdata
import android.os.Build
import android.text.Spanned
import com.keylesspalace.tusky.entity.Filter
import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.util.parseAsMastodonHtml
import com.keylesspalace.tusky.util.replaceCrashingCharacters
import com.keylesspalace.tusky.util.shouldTrimStatus
/**
@ -48,17 +46,15 @@ sealed class StatusViewData {
override val id: String
get() = status.id
val content: Spanned = status.actionableStatus.content.parseAsMastodonHtml()
/**
* Specifies whether the content of this post is long enough to be automatically
* collapsed or if it should show all content regardless.
*
* @return Whether the post is collapsible or never collapsed.
*/
val isCollapsible: Boolean
val content: Spanned
val spoilerText: String
val username: String
val isCollapsible: Boolean = shouldTrimStatus(this.content)
val actionable: Status
get() = status.actionableStatus
@ -76,22 +72,6 @@ sealed class StatusViewData {
val rebloggingStatus: Status?
get() = if (status.reblog != null) status else null
init {
if (Build.VERSION.SDK_INT == 23) {
// https://github.com/tuskyapp/Tusky/issues/563
this.content = replaceCrashingCharacters(status.actionableStatus.content.parseAsMastodonHtml())
this.spoilerText =
replaceCrashingCharacters(status.actionableStatus.spoilerText).toString()
this.username =
replaceCrashingCharacters(status.actionableStatus.account.username).toString()
} else {
this.content = status.actionableStatus.content.parseAsMastodonHtml()
this.spoilerText = status.actionableStatus.spoilerText
this.username = status.actionableStatus.account.username
}
this.isCollapsible = shouldTrimStatus(this.content)
}
/** Helper for Java */
fun copyWithStatus(status: Status): Concrete {
return copy(status = status)