diff --git a/app/build.gradle b/app/build.gradle index ed4623b3..28929ca4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,7 +27,7 @@ android { defaultConfig { applicationId APP_ID namespace "com.keylesspalace.tusky" - minSdk 23 + minSdk 24 targetSdk 33 versionCode 113 versionName "23.0" diff --git a/app/lint-baseline.xml b/app/lint-baseline.xml index 88124ee8..0af47faa 100644 --- a/app/lint-baseline.xml +++ b/app/lint-baseline.xml @@ -36,7 +36,7 @@ - - - - + message="Unnecessary; SDK_INT is always >= 24" + errorLine1=" if (Build.VERSION.SDK_INT > 23) {" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + file="src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt" + line="252" + column="13"/> + + + + + + + + + + + + @@ -1114,7 +1136,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~"> @@ -1125,7 +1147,7 @@ errorLine2=" ~~~~~~~~~~~~~"> @@ -1136,7 +1158,7 @@ errorLine2=" ~~~~~~~"> @@ -1147,7 +1169,7 @@ errorLine2=" ~~~~~~~"> @@ -1158,7 +1180,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~"> @@ -1169,7 +1191,7 @@ errorLine2=" ~~~~~~~~~~~~~"> diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f88f1fc8..53831a1c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -188,8 +188,7 @@ android:icon="@drawable/ic_quicksettings" android:label="@string/tusky_compose_post_quicksetting_label" android:permission="android.permission.BIND_QUICK_SETTINGS_TILE" - android:exported="true" - tools:targetApi="24"> + android:exported="true"> diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java index a73b52ef..17946454 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java @@ -679,17 +679,12 @@ public class NotificationsAdapter extends RecyclerView.Adapter 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 ""; } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java index 5dc0bf98..3c3103e0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java @@ -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()); diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java index f15d44d2..f7292391 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java +++ b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java @@ -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; } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusViewHolder.kt index 0ee7ec5d..21e1f51a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusViewHolder.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusViewHolder.kt @@ -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() diff --git a/app/src/main/java/com/keylesspalace/tusky/service/TuskyTileService.kt b/app/src/main/java/com/keylesspalace/tusky/service/TuskyTileService.kt index 2bf761f9..9d1b90a2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/service/TuskyTileService.kt +++ b/app/src/main/java/com/keylesspalace/tusky/service/TuskyTileService.kt @@ -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() { diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ListStatusAccessibilityDelegate.kt b/app/src/main/java/com/keylesspalace/tusky/util/ListStatusAccessibilityDelegate.kt index 9402edd0..077ec008 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ListStatusAccessibilityDelegate.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/ListStatusAccessibilityDelegate.kt @@ -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) } diff --git a/app/src/main/java/com/keylesspalace/tusky/util/SmartLengthInputFilter.kt b/app/src/main/java/com/keylesspalace/tusky/util/SmartLengthInputFilter.kt index c7b583e5..9a9c19bf 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/SmartLengthInputFilter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/SmartLengthInputFilter.kt @@ -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 { diff --git a/app/src/main/java/com/keylesspalace/tusky/util/StatusParsingHelper.kt b/app/src/main/java/com/keylesspalace/tusky/util/StatusParsingHelper.kt index b8c3c6a0..117a44e2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/StatusParsingHelper.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/StatusParsingHelper.kt @@ -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 = '-' diff --git a/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.kt b/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.kt index 18e504ec..870d7b16 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.kt @@ -14,12 +14,10 @@ * see . */ 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) diff --git a/app/src/main/res/color-v24/launcher_shadow_gradient.xml b/app/src/main/res/color-v24/launcher_shadow_gradient.xml deleted file mode 100644 index 98ce8338..00000000 --- a/app/src/main/res/color-v24/launcher_shadow_gradient.xml +++ /dev/null @@ -1,12 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_notoemoji.xml b/app/src/main/res/drawable-v24/ic_notoemoji.xml deleted file mode 100644 index d016e35c..00000000 --- a/app/src/main/res/drawable-v24/ic_notoemoji.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_notoemoji.xml b/app/src/main/res/drawable/ic_notoemoji.xml deleted file mode 100644 index 55628c01..00000000 --- a/app/src/main/res/drawable/ic_notoemoji.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - -