diff --git a/CHANGELOG.md b/CHANGELOG.md index eb5295dc6..1ea9aae4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,25 @@ ### Significant bug fixes +## v23.0 beta 1 + +### New features and other improvements + +- **New preference to scale UI text**, [PR#3248](https://github.com/tuskyapp/Tusky/pull/3248) by [@nikclayton](https://mastodon.social/@nikclayton) + +### Significant bug fixes + +- **Save account information correctly**, [PR#3720](https://github.com/tuskyapp/Tusky/pull/3720) by [@connyduck](https://chaos.social/@ConnyDuck) + - If you were logged in with multiple accounts it was possible to switch accounts in a way that the UI showed the new account, but database operations were happening using the old account. +- **"pull" notifications on devices running Android versions <= 11**, [PR#3649](https://github.com/tuskyapp/Tusky/pull/3649) by [@nikclayton](https://mastodon.social/@nikclayton) + - Pull notifications (i.e., not using ntfy.sh) could silently fail on devices running Android 11 and below +- **Work around Android bug where text fields could "forget" they can copy/paste**, [PR#3707](https://github.com/tuskyapp/Tusky/pull/3707) by [@nikclayton](https://mastodon.social/@nikclayton) +- **Viewing "diffs" in edit history will not extend off screen edge**, [PR#3431](https://github.com/tuskyapp/Tusky/pull/3431) by [@nikclayton](https://mastodon.social/@nikclayton) +- **Don't crash if your server has no post edit history**, [PR#3747](https://github.com/tuskyapp/Tusky/pull/3747) by [@nikclayton](https://mastodon.social/@nikclayton) + - Your Mastodon server might know that a post has been edited, but not know the details of those edits. Trying to view the history of those statuses no longer crashes. +- **Add a "Delete" button when editing a filter**, [PR#3553](https://github.com/tuskyapp/Tusky/pull/3553) by [@Tak](https://mastodon.gamedev.place/@Tak) +- **Show non-square emoji correctly**, [PR#3711](https://github.com/tuskyapp/Tusky/pull/3711) by [@connyduck](https://chaos.social/@ConnyDuck) + ## v22.0 ### New features and other improvements diff --git a/app/build.gradle b/app/build.gradle index 51ad95e29..3d01ac2f2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ plugins { alias(libs.plugins.android.application) + alias(libs.plugins.google.ksp) alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.kapt) alias(libs.plugins.kotlin.parcelize) @@ -28,8 +29,8 @@ android { namespace "com.keylesspalace.tusky" minSdk 24 targetSdk 33 - versionCode 110 - versionName "22.0" + versionCode 111 + versionName "23.0 beta 1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -113,11 +114,9 @@ android { } } -kapt { - arguments { - arg("room.schemaLocation", "$projectDir/schemas") - arg("room.incremental", "true") - } +ksp { + arg("room.schemaLocation", "$projectDir/schemas") + arg("room.incremental", "true") } configurations { @@ -134,7 +133,7 @@ dependencies { implementation libs.bundles.androidx implementation libs.bundles.room - kapt libs.androidx.room.compiler + ksp libs.androidx.room.compiler implementation libs.android.material diff --git a/app/lint-baseline.xml b/app/lint-baseline.xml index 5c592dba0..9db6ce282 100644 --- a/app/lint-baseline.xml +++ b/app/lint-baseline.xml @@ -828,7 +828,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -839,7 +839,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -850,7 +850,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2009,17 +2009,6 @@ column="5"/> - - - - - - - - - - - - - - - - - - - - - - - - @@ -2456,7 +2390,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2467,7 +2401,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2478,7 +2412,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~"> @@ -2489,7 +2423,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2500,7 +2434,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> @@ -2511,7 +2445,7 @@ errorLine2=" ~~~~~~~~~~~~"> @@ -2522,7 +2456,7 @@ errorLine2=" ~~~~~~~~~~~~~~"> @@ -2533,7 +2467,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2544,7 +2478,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2555,7 +2489,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> @@ -2566,7 +2500,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2577,7 +2511,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> @@ -2588,7 +2522,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2599,7 +2533,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2610,7 +2544,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~"> @@ -2621,7 +2555,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2632,7 +2566,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2643,7 +2577,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2654,7 +2588,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> @@ -2665,7 +2599,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2676,7 +2610,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2687,7 +2621,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2698,7 +2632,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2918,7 +2852,7 @@ errorLine2=" ~~~~~~~~~"> @@ -2929,7 +2863,7 @@ errorLine2=" ~~~~~~~~~"> @@ -2940,7 +2874,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~"> @@ -2951,7 +2885,7 @@ errorLine2=" ~~~~~~~~~~~~~"> @@ -2962,7 +2896,7 @@ errorLine2=" ~~~~~~~~~~~~~"> @@ -2973,7 +2907,7 @@ errorLine2=" ~~~~~~~~~~~~~"> @@ -2984,7 +2918,7 @@ errorLine2=" ~~~~~~"> @@ -2995,7 +2929,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~"> @@ -3006,7 +2940,7 @@ errorLine2=" ~~~~~~~~~~~"> @@ -3017,7 +2951,7 @@ errorLine2=" ~~~~~~~~~~~~~"> @@ -3028,7 +2962,7 @@ errorLine2=" ~~~~~~~~~~~~~"> @@ -3039,7 +2973,7 @@ errorLine2=" ~~~~~~~~~~~~~"> @@ -3050,7 +2984,7 @@ errorLine2=" ~~~~~~"> @@ -3160,7 +3094,7 @@ errorLine2=" ~~~~~~~"> @@ -3171,7 +3105,7 @@ errorLine2=" ~~~~~~~"> @@ -3226,7 +3160,7 @@ errorLine2=" ~~~~~~~"> @@ -3237,7 +3171,7 @@ errorLine2=" ~~~~~~~"> @@ -3248,7 +3182,7 @@ errorLine2=" ~~~~~~~"> @@ -3259,7 +3193,7 @@ errorLine2=" ~~~~~~~"> @@ -3270,7 +3204,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -3281,7 +3215,7 @@ errorLine2=" ~~~~~~~~~~~~~~"> @@ -3545,7 +3479,7 @@ errorLine2=" ~~~~~~~~~"> @@ -3556,7 +3490,7 @@ errorLine2=" ~~~~~~~~~"> @@ -4447,7 +4381,7 @@ errorLine2=" ~~~~~~~~~"> @@ -4458,7 +4392,7 @@ errorLine2=" ~~~~~~~~"> @@ -4469,7 +4403,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~"> @@ -4480,7 +4414,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~"> @@ -4491,7 +4425,7 @@ errorLine2=" ~~~~~~~"> @@ -4502,7 +4436,7 @@ errorLine2=" ~~~~~~~"> @@ -4513,7 +4447,7 @@ errorLine2=" ~~~~~~~"> @@ -4524,7 +4458,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -5052,7 +4986,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -5569,7 +5503,7 @@ errorLine2=" ~~~~~~~~"> @@ -5580,7 +5514,7 @@ errorLine2=" ~~~~~~~~"> @@ -6086,7 +6020,7 @@ errorLine2=" ~~~~~~~~"> @@ -6174,7 +6108,7 @@ errorLine2=" ~~~~~~~~"> @@ -6185,7 +6119,7 @@ errorLine2=" ~~~~~~~~"> @@ -6196,7 +6130,7 @@ errorLine2=" ~~~~~~~~"> @@ -6207,7 +6141,7 @@ errorLine2=" ~~~~~~~~"> @@ -6394,7 +6328,7 @@ errorLine2=" ~~~~~~~~~"> @@ -6449,7 +6383,7 @@ errorLine2=" ~~~~~~~~~"> @@ -6768,7 +6702,7 @@ errorLine2=" ~~~~~~~~~~~~~~"> @@ -6779,7 +6713,7 @@ errorLine2=" ~~~~~~"> @@ -6790,7 +6724,7 @@ errorLine2=" ~~~~~~~~"> @@ -6801,7 +6735,7 @@ errorLine2=" ~~~~"> @@ -6812,7 +6746,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> @@ -6823,7 +6757,7 @@ errorLine2=" ~~~~~~~~~~~~"> @@ -6834,7 +6768,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -6845,7 +6779,7 @@ errorLine2=" ~~~~~~~~"> @@ -6856,7 +6790,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~"> @@ -6867,7 +6801,7 @@ errorLine2=" ~~~~~~~"> @@ -6878,7 +6812,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~"> @@ -6889,7 +6823,7 @@ errorLine2=" ~~~~~~~~~~~~"> @@ -6900,7 +6834,7 @@ errorLine2=" ~~~~~~~~~~~~~"> @@ -6911,7 +6845,7 @@ errorLine2=" ~~~~~~~"> @@ -6922,7 +6856,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~"> @@ -6933,7 +6867,7 @@ errorLine2=" ~~~~~~~~~~~~~"> @@ -6944,7 +6878,7 @@ errorLine2=" ~~~~~~~"> @@ -6955,7 +6889,7 @@ errorLine2=" ~~~~~~~"> @@ -6966,7 +6900,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~"> @@ -6977,7 +6911,7 @@ errorLine2=" ~~~~~~~~~~~~~"> diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt b/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt index 7821630b5..a20526e95 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt @@ -46,7 +46,6 @@ import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.viewmodel.AccountsInListViewModel import com.keylesspalace.tusky.viewmodel.State import kotlinx.coroutines.launch -import java.io.IOException import javax.inject.Inject private typealias AccountInfo = Pair @@ -146,23 +145,10 @@ class AccountsInListFragment : DialogFragment(), Injectable { private fun handleError(error: Throwable) { binding.messageView.show() - val retryAction = { _: View -> + binding.messageView.setup(error) { _: View -> binding.messageView.hide() viewModel.load(listId) } - if (error is IOException) { - binding.messageView.setup( - R.drawable.elephant_offline, - R.string.error_network, - retryAction - ) - } else { - binding.messageView.setup( - R.drawable.elephant_error, - R.string.error_generic, - retryAction - ) - } } private fun onRemoveFromList(accountId: String) { diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java index 252be150d..62709d7c3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java @@ -16,9 +16,11 @@ package com.keylesspalace.tusky; import android.app.ActivityManager; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; @@ -45,6 +47,7 @@ import com.keylesspalace.tusky.db.AccountManager; import com.keylesspalace.tusky.di.Injectable; import com.keylesspalace.tusky.interfaces.AccountSelectionListener; import com.keylesspalace.tusky.interfaces.PermissionRequester; +import com.keylesspalace.tusky.settings.PrefKeys; import com.keylesspalace.tusky.util.ThemeUtils; import java.util.ArrayList; @@ -54,6 +57,7 @@ import java.util.List; import javax.inject.Inject; public abstract class BaseActivity extends AppCompatActivity implements Injectable { + private static final String TAG = "BaseActivity"; @Inject public AccountManager accountManager; @@ -93,6 +97,44 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab requesters = new HashMap<>(); } + @Override + protected void attachBaseContext(Context newBase) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(newBase); + + // Scale text in the UI from PrefKeys.UI_TEXT_SCALE_RATIO + float uiScaleRatio = preferences.getFloat(PrefKeys.UI_TEXT_SCALE_RATIO, 100F); + + Configuration configuration = newBase.getResources().getConfiguration(); + + // Adjust `fontScale` in the configuration. + // + // You can't repeatedly adjust the `fontScale` in `newBase` because that will contain the + // result of previous adjustments. E.g., going from 100% to 80% to 100% does not return + // you to the original 100%, it leaves it at 80%. + // + // Instead, calculate the new scale from the application context. This is unaffected by + // changes to the base context. It does contain contain any changes to the font scale from + // "Settings > Display > Font size" in the device settings, so scaling performed here + // is in addition to any scaling in the device settings. + Configuration appConfiguration = newBase.getApplicationContext().getResources().getConfiguration(); + + // This only adjusts the fonts, anything measured in `dp` is unaffected by this. + // You can try to adjust `densityDpi` as shown in the commented out code below. This + // works, to a point. However, dialogs do not react well to this. Beyond a certain + // scale (~ 120%) the right hand edge of the dialog will clip off the right of the + // screen. + // + // So for now, just adjust the font scale + // + // val displayMetrics = appContext.resources.displayMetrics + // configuration.densityDpi = ((displayMetrics.densityDpi * uiScaleRatio).toInt()) + configuration.fontScale = appConfiguration.fontScale * uiScaleRatio / 100F; + + Context fontScaleContext = newBase.createConfigurationContext(configuration); + + super.attachBaseContext(fontScaleContext); + } + protected boolean requiresLogin() { return true; } diff --git a/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt b/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt index 8ad35fe07..b47194110 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt @@ -21,6 +21,7 @@ import android.os.Bundle import android.util.Log import android.view.Gravity import android.view.View +import android.widget.ArrayAdapter import android.widget.FrameLayout import android.widget.LinearLayout import android.widget.ProgressBar @@ -42,12 +43,12 @@ import com.google.android.material.snackbar.Snackbar import com.google.android.material.transition.MaterialArcMotion import com.google.android.material.transition.MaterialContainerTransform import com.keylesspalace.tusky.adapter.ItemInteractionListener -import com.keylesspalace.tusky.adapter.ListSelectionAdapter import com.keylesspalace.tusky.adapter.TabAdapter import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.MainTabsChangedEvent import com.keylesspalace.tusky.databinding.ActivityTabPreferenceBinding import com.keylesspalace.tusky.di.Injectable +import com.keylesspalace.tusky.entity.MastoList import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.getDimension import com.keylesspalace.tusky.util.hide @@ -272,7 +273,7 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene } private fun showSelectListDialog() { - val adapter = ListSelectionAdapter(this) + val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1) val statusLayout = LinearLayout(this) statusLayout.gravity = Gravity.CENTER @@ -298,12 +299,13 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene .setNegativeButton(android.R.string.cancel, null) .setView(statusLayout) .setAdapter(adapter) { _, position -> - val list = adapter.getItem(position) - val newTab = createTabDataFromId(LIST, listOf(list!!.id, list.title)) - currentTabs.add(newTab) - currentTabsAdapter.notifyItemInserted(currentTabs.size - 1) - updateAvailableTabs() - saveTabs() + adapter.getItem(position)?.let { item -> + val newTab = createTabDataFromId(LIST, listOf(item.id, item.title)) + currentTabs.add(newTab) + currentTabsAdapter.notifyItemInserted(currentTabs.size - 1) + updateAvailableTabs() + saveTabs() + } } val showProgressBarJob = getProgressBarJob(progress, 500) diff --git a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt index 3107cea8c..e7c646991 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt +++ b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt @@ -23,6 +23,7 @@ import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import autodispose2.AutoDisposePlugins +import com.keylesspalace.tusky.components.notifications.NotificationHelper import com.keylesspalace.tusky.di.AppInjector import com.keylesspalace.tusky.settings.PrefKeys import com.keylesspalace.tusky.settings.SCHEMA_VERSION @@ -95,6 +96,8 @@ class TuskyApplication : Application(), HasAndroidInjector { Log.w("RxJava", "undeliverable exception", it) } + NotificationHelper.createWorkerNotificationChannel(this) + WorkManager.initialize( this, androidx.work.Configuration.Builder() diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/ListSelectionAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/ListSelectionAdapter.kt deleted file mode 100644 index 7c2a16935..000000000 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/ListSelectionAdapter.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright 2019 kyori19 - * - * This file is a part of Tusky. - * - * This program is free software; you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation; either version 3 of the - * License, or (at your option) any later version. - * - * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General - * Public License for more details. - * - * You should have received a copy of the GNU General Public License along with Tusky; if not, - * see . */ - -package com.keylesspalace.tusky.adapter - -import android.content.Context -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ArrayAdapter -import com.keylesspalace.tusky.R -import com.keylesspalace.tusky.databinding.ItemPickerListBinding -import com.keylesspalace.tusky.entity.MastoList - -class ListSelectionAdapter(context: Context) : ArrayAdapter(context, R.layout.item_picker_list) { - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val binding = if (convertView == null) { - ItemPickerListBinding.inflate(LayoutInflater.from(context), parent, false) - } else { - ItemPickerListBinding.bind(convertView) - } - - getItem(position)?.let { list -> - binding.root.text = list.title - } - - return binding.root - } -} diff --git a/app/src/main/java/com/keylesspalace/tusky/components/account/list/ListsForAccountFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/account/list/ListsForAccountFragment.kt index d527f613d..08c93756b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/account/list/ListsForAccountFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/account/list/ListsForAccountFragment.kt @@ -40,7 +40,6 @@ import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.visible import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import java.io.IOException import javax.inject.Inject class ListsForAccountFragment : DialogFragment(), Injectable { @@ -103,16 +102,7 @@ class ListsForAccountFragment : DialogFragment(), Injectable { binding.listsView.hide() binding.messageView.apply { show() - - if (error is IOException) { - setup(R.drawable.elephant_offline, R.string.error_network) { - load() - } - } else { - setup(R.drawable.elephant_error, R.string.error_generic) { - load() - } - } + setup(error) { load() } } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaFragment.kt index df87372eb..b39a8b5b4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaFragment.kt @@ -51,7 +51,6 @@ import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeDp import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import java.io.IOException import javax.inject.Inject /** @@ -133,12 +132,7 @@ class AccountMediaFragment : } is LoadState.Error -> { binding.statusView.show() - - if ((loadState.refresh as LoadState.Error).error is IOException) { - binding.statusView.setup(R.drawable.elephant_offline, R.string.error_network, null) - } else { - binding.statusView.setup(R.drawable.elephant_error, R.string.error_generic, null) - } + binding.statusView.setup((loadState.refresh as LoadState.Error).error) } is LoadState.Loading -> { binding.progressBar.show() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/accountlist/AccountListFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/accountlist/AccountListFragment.kt index dfa1726a8..68eba3c76 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/accountlist/AccountListFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/accountlist/AccountListFragment.kt @@ -393,16 +393,9 @@ class AccountListFragment : if (adapter.itemCount == 0) { binding.messageView.show() - if (throwable is IOException) { - binding.messageView.setup(R.drawable.elephant_offline, R.string.error_network) { - binding.messageView.hide() - this.fetchAccounts(null) - } - } else { - binding.messageView.setup(R.drawable.elephant_error, R.string.error_generic) { - binding.messageView.hide() - this.fetchAccounts(null) - } + binding.messageView.setup(throwable) { + binding.messageView.hide() + this.fetchAccounts(null) } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt index 9c5631916..f830b5bd9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt @@ -89,7 +89,7 @@ data class ConversationStatusEntity( val bookmarked: Boolean, val sensitive: Boolean, val spoilerText: String, - val attachments: ArrayList, + val attachments: List, val mentions: List, val tags: List?, val showingHiddenContent: Boolean, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt index 303ec6b03..48d1e87b8 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt @@ -64,7 +64,6 @@ import com.mikepenz.iconics.utils.sizeDp import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import java.io.IOException import javax.inject.Inject import kotlin.time.DurationUnit import kotlin.time.toDuration @@ -139,16 +138,7 @@ class ConversationsFragment : } is LoadState.Error -> { binding.statusView.show() - - if ((loadState.refresh as LoadState.Error).error is IOException) { - binding.statusView.setup(R.drawable.elephant_offline, R.string.error_network) { - refreshContent() - } - } else { - binding.statusView.setup(R.drawable.elephant_error, R.string.error_generic) { - refreshContent() - } - } + binding.statusView.setup((loadState.refresh as LoadState.Error).error) { refreshContent() } } is LoadState.Loading -> { binding.progressBar.show() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsActivity.kt index 94a0b47ef..b6b56d4a3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsActivity.kt @@ -31,7 +31,6 @@ import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.visible import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import java.io.IOException import javax.inject.Inject class FollowedTagsActivity : @@ -108,11 +107,7 @@ class FollowedTagsActivity : binding.followedTagsView.hide() binding.followedTagsMessageView.show() val errorState = loadState.refresh as LoadState.Error - if (errorState.error is IOException) { - binding.followedTagsMessageView.setup(R.drawable.elephant_offline, R.string.error_network) { retry() } - } else { - binding.followedTagsMessageView.setup(R.drawable.elephant_error, R.string.error_generic) { retry() } - } + binding.followedTagsMessageView.setup(errorState.error) { retry() } Log.w(TAG, "error loading followed hashtags", errorState.error) } else { binding.followedTagsView.show() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/instancemute/fragment/InstanceListFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/instancemute/fragment/InstanceListFragment.kt index 1e4925a51..1da0a2b7d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/instancemute/fragment/InstanceListFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/instancemute/fragment/InstanceListFragment.kt @@ -26,7 +26,6 @@ import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.view.EndlessOnScrollListener import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import kotlinx.coroutines.launch -import java.io.IOException import javax.inject.Inject class InstanceListFragment : Fragment(R.layout.fragment_instance_list), Injectable, InstanceActionListener { @@ -146,16 +145,9 @@ class InstanceListFragment : Fragment(R.layout.fragment_instance_list), Injectab if (adapter.itemCount == 0) { binding.messageView.show() - if (throwable is IOException) { - binding.messageView.setup(R.drawable.elephant_offline, R.string.error_network) { - binding.messageView.hide() - this.fetchInstances(null) - } - } else { - binding.messageView.setup(R.drawable.elephant_error, R.string.error_generic) { - binding.messageView.hide() - this.fetchInstances(null) - } + binding.messageView.setup(throwable) { + binding.messageView.hide() + this.fetchInstances(null) } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationFetcher.kt b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationFetcher.kt index 89f4222bd..633ca08f7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationFetcher.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationFetcher.kt @@ -11,9 +11,10 @@ import com.keylesspalace.tusky.entity.Marker import com.keylesspalace.tusky.entity.Notification import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.isLessThan -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.delay import javax.inject.Inject import kotlin.math.min +import kotlin.time.Duration.Companion.milliseconds /** * Fetch Mastodon notifications and show Android notifications, with summaries, for them. @@ -29,19 +30,17 @@ class NotificationFetcher @Inject constructor( private val accountManager: AccountManager, private val context: Context ) { - fun fetchAndShow() { + suspend fun fetchAndShow() { for (account in accountManager.getAllAccountsOrderedByActive()) { if (account.notificationsEnabled) { try { val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Create sorted list of new notifications - val notifications = runBlocking { // OK, because in a worker thread - fetchNewNotifications(account) - .filter { filterNotification(notificationManager, account, it) } - .sortedWith(compareBy({ it.id.length }, { it.id })) // oldest notifications first - .toMutableList() - } + val notifications = fetchNewNotifications(account) + .filter { filterNotification(notificationManager, account, it) } + .sortedWith(compareBy({ it.id.length }, { it.id })) // oldest notifications first + .toMutableList() // There's a maximum limit on the number of notifications an Android app // can display. If the total number of notifications (current notifications, @@ -82,7 +81,7 @@ class NotificationFetcher @Inject constructor( // Android will rate limit / drop notifications if they're posted too // quickly. There is no indication to the user that this happened. // See https://github.com/tuskyapp/Tusky/pull/3626#discussion_r1192963664 - Thread.sleep(1000) + delay(1000.milliseconds) } NotificationHelper.updateSummaryNotifications( 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 8950ce389..d31bab27f 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 @@ -37,6 +37,7 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.StringRes; import androidx.core.app.NotificationCompat; import androidx.core.app.RemoteInput; import androidx.core.app.TaskStackBuilder; @@ -77,7 +78,12 @@ import java.util.concurrent.TimeUnit; public class NotificationHelper { - private static int notificationId = 0; + /** ID of notification shown when fetching notifications */ + public static final int NOTIFICATION_ID_FETCH_NOTIFICATION = 0; + /** ID of notification shown when pruning the cache */ + public static final int NOTIFICATION_ID_PRUNE_CACHE = 1; + /** Dynamic notification IDs start here */ + private static int notificationId = NOTIFICATION_ID_PRUNE_CACHE + 1; /** * constants used in Intents @@ -121,6 +127,7 @@ public class NotificationHelper { public static final String CHANNEL_SIGN_UP = "CHANNEL_SIGN_UP"; public static final String CHANNEL_UPDATES = "CHANNEL_UPDATES"; public static final String CHANNEL_REPORT = "CHANNEL_REPORT"; + public static final String CHANNEL_BACKGROUND_TASKS = "CHANNEL_BACKGROUND_TASKS"; /** * WorkManager Tag @@ -471,6 +478,49 @@ public class NotificationHelper { pendingIntentFlags(false)); } + /** + * Creates a notification channel for notifications for background work that should not + * disturb the user. + * + * @param context context + */ + public static void createWorkerNotificationChannel(@NonNull Context context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return; + + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + + NotificationChannel channel = new NotificationChannel( + CHANNEL_BACKGROUND_TASKS, + context.getString(R.string.notification_listenable_worker_name), + NotificationManager.IMPORTANCE_NONE + ); + + channel.setDescription(context.getString(R.string.notification_listenable_worker_description)); + channel.enableLights(false); + channel.enableVibration(false); + channel.setShowBadge(false); + + notificationManager.createNotificationChannel(channel); + } + + /** + * Creates a notification for a background worker. + * + * @param context context + * @param titleResource String resource to use as the notification's title + * @return the notification + */ + @NonNull + public static android.app.Notification createWorkerNotification(@NonNull Context context, @StringRes int titleResource) { + String title = context.getString(titleResource); + return new NotificationCompat.Builder(context, CHANNEL_BACKGROUND_TASKS) + .setContentTitle(title) + .setTicker(title) + .setSmallIcon(R.drawable.ic_notify) + .setOngoing(true) + .build(); + } + public static void createNotificationChannelsForAccount(@NonNull AccountEntity account, @NonNull Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesActivity.kt index 8f6d51e35..b47df1596 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesActivity.kt @@ -95,7 +95,9 @@ class PreferencesActivity : } onBackPressedDispatcher.addCallback(this, restartActivitiesOnBackPressedCallback) - restartActivitiesOnBackPressedCallback.isEnabled = savedInstanceState?.getBoolean(EXTRA_RESTART_ON_BACK, false) ?: false + restartActivitiesOnBackPressedCallback.isEnabled = intent.extras?.getBoolean( + EXTRA_RESTART_ON_BACK + ) ?: savedInstanceState?.getBoolean(EXTRA_RESTART_ON_BACK, false) ?: false } override fun onPreferenceStartFragment( @@ -151,6 +153,10 @@ class PreferencesActivity : restartActivitiesOnBackPressedCallback.isEnabled = true this.restartCurrentActivity() } + PrefKeys.UI_TEXT_SCALE_RATIO -> { + restartActivitiesOnBackPressedCallback.isEnabled = true + this.restartCurrentActivity() + } "statusTextSize", "absoluteTimeView", "showBotOverlay", "animateGifAvatars", "useBlurhash", "showSelfUsername", "showCardsInTimelines", "confirmReblogs", "confirmFavourites", "enableSwipeForTabs", "mainNavPosition", PrefKeys.HIDE_TOP_TOOLBAR, PrefKeys.SHOW_STATS_INLINE -> { @@ -175,7 +181,8 @@ class PreferencesActivity : override fun androidInjector() = androidInjector companion object { - + @Suppress("unused") + private const val TAG = "PreferencesActivity" const val GENERAL_PREFERENCES = 0 const val ACCOUNT_PREFERENCES = 1 const val NOTIFICATION_PREFERENCES = 2 diff --git a/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesFragment.kt index 84ba4c0c2..e2d29d495 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesFragment.kt @@ -29,6 +29,7 @@ import com.keylesspalace.tusky.settings.listPreference import com.keylesspalace.tusky.settings.makePreferenceScreen import com.keylesspalace.tusky.settings.preference import com.keylesspalace.tusky.settings.preferenceCategory +import com.keylesspalace.tusky.settings.sliderPreference import com.keylesspalace.tusky.settings.switchPreference import com.keylesspalace.tusky.util.LocaleManager import com.keylesspalace.tusky.util.deserialize @@ -99,6 +100,19 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable { preferenceDataStore = localeManager } + sliderPreference { + key = PrefKeys.UI_TEXT_SCALE_RATIO + setDefaultValue(100F) + valueTo = 150F + valueFrom = 50F + stepSize = 5F + setTitle(R.string.pref_ui_text_size) + format = "%.0f%%" + decrementIcon = makeIcon(GoogleMaterial.Icon.gmd_zoom_out) + incrementIcon = makeIcon(GoogleMaterial.Icon.gmd_zoom_in) + icon = makeIcon(GoogleMaterial.Icon.gmd_format_size) + } + listPreference { setDefaultValue("medium") setEntries(R.array.post_text_size_names) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusActivity.kt index 102e67be5..a53c08928 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusActivity.kt @@ -47,7 +47,6 @@ import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeDp import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import java.io.IOException import javax.inject.Inject class ScheduledStatusActivity : @@ -102,15 +101,7 @@ class ScheduledStatusActivity : binding.errorMessageView.show() val errorState = loadState.refresh as LoadState.Error - if (errorState.error is IOException) { - binding.errorMessageView.setup(R.drawable.elephant_offline, R.string.error_network) { - refreshStatuses() - } - } else { - binding.errorMessageView.setup(R.drawable.elephant_error, R.string.error_generic) { - refreshStatuses() - } - } + binding.errorMessageView.setup(errorState.error) { refreshStatuses() } } if (loadState.refresh != LoadState.Loading) { binding.swipeRefreshLayout.isRefreshing = false diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchAccountsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchAccountsFragment.kt index 87be69807..1bcfaaaa0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchAccountsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchAccountsFragment.kt @@ -15,15 +15,28 @@ package com.keylesspalace.tusky.components.search.fragments +import android.os.Bundle +import android.view.View import androidx.paging.PagingData import androidx.paging.PagingDataAdapter import androidx.preference.PreferenceManager +import androidx.recyclerview.widget.DividerItemDecoration import com.keylesspalace.tusky.components.search.adapter.SearchAccountsAdapter import com.keylesspalace.tusky.entity.TimelineAccount import com.keylesspalace.tusky.settings.PrefKeys import kotlinx.coroutines.flow.Flow class SearchAccountsFragment : SearchFragment() { + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.searchRecyclerView.addItemDecoration( + DividerItemDecoration( + binding.searchRecyclerView.context, + DividerItemDecoration.VERTICAL + ) + ) + } + override fun createAdapter(): PagingDataAdapter { val preferences = PreferenceManager.getDefaultSharedPreferences(binding.searchRecyclerView.context) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchFragment.kt index 86b54238c..28790c2fb 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchFragment.kt @@ -13,7 +13,6 @@ import androidx.lifecycle.lifecycleScope import androidx.paging.LoadState import androidx.paging.PagingData import androidx.paging.PagingDataAdapter -import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.SimpleItemAnimator import androidx.swiperefreshlayout.widget.SwipeRefreshLayout @@ -129,7 +128,6 @@ abstract class SearchFragment : } private fun initAdapter() { - binding.searchRecyclerView.addItemDecoration(DividerItemDecoration(binding.searchRecyclerView.context, DividerItemDecoration.VERTICAL)) binding.searchRecyclerView.layoutManager = LinearLayoutManager(binding.searchRecyclerView.context) adapter = createAdapter() binding.searchRecyclerView.adapter = adapter diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchHashtagsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchHashtagsFragment.kt index d0b7e8fa9..8c4f41fb0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchHashtagsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchHashtagsFragment.kt @@ -15,8 +15,11 @@ package com.keylesspalace.tusky.components.search.fragments +import android.os.Bundle +import android.view.View import androidx.paging.PagingData import androidx.paging.PagingDataAdapter +import androidx.recyclerview.widget.DividerItemDecoration import com.keylesspalace.tusky.components.search.adapter.SearchHashtagsAdapter import com.keylesspalace.tusky.entity.HashTag import kotlinx.coroutines.flow.Flow @@ -26,6 +29,16 @@ class SearchHashtagsFragment : SearchFragment() { override val data: Flow> get() = viewModel.hashtagsFlow + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.searchRecyclerView.addItemDecoration( + DividerItemDecoration( + binding.searchRecyclerView.context, + DividerItemDecoration.VERTICAL + ) + ) + } + override fun createAdapter(): PagingDataAdapter = SearchHashtagsAdapter(this) companion object { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt index bae257a92..ef5d9085d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt @@ -79,7 +79,6 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Observable import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import java.io.IOException import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -242,16 +241,7 @@ class TimelineFragment : } is LoadState.Error -> { binding.statusView.show() - - if ((loadState.refresh as LoadState.Error).error is IOException) { - binding.statusView.setup(R.drawable.elephant_offline, R.string.error_network) { - onRefresh() - } - } else { - binding.statusView.setup(R.drawable.elephant_error, R.string.error_generic) { - onRefresh() - } - } + binding.statusView.setup((loadState.refresh as LoadState.Error).error) { onRefresh() } } is LoadState.Loading -> { binding.progressBar.show() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadFragment.kt index 1a3777f90..0d4ba3b35 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadFragment.kt @@ -60,7 +60,6 @@ import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import java.io.IOException import javax.inject.Inject class ViewThreadFragment : @@ -201,21 +200,7 @@ class ViewThreadFragment : binding.recyclerView.hide() binding.statusView.show() - if (uiState.throwable is IOException) { - binding.statusView.setup( - R.drawable.elephant_offline, - R.string.error_network - ) { - viewModel.retry(thisThreadsStatusId) - } - } else { - binding.statusView.setup( - R.drawable.elephant_error, - R.string.error_generic - ) { - viewModel.retry(thisThreadsStatusId) - } - } + binding.statusView.setup(uiState.throwable) { viewModel.retry(thisThreadsStatusId) } } is ThreadUiState.Success -> { if (uiState.statusViewData.none { viewData -> viewData.isDetailed }) { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsFragment.kt index 3378b7a27..95a0b96d1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsFragment.kt @@ -53,7 +53,6 @@ import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeDp import kotlinx.coroutines.launch -import java.io.IOException import javax.inject.Inject class ViewEditsFragment : @@ -111,13 +110,17 @@ class ViewEditsFragment : binding.statusView.show() binding.initialProgressBar.hide() - if (uiState.throwable is IOException) { - binding.statusView.setup(R.drawable.elephant_offline, R.string.error_network) { - viewModel.loadEdits(statusId, force = true) + when (uiState.throwable) { + is ViewEditsViewModel.MissingEditsException -> { + binding.statusView.setup( + R.drawable.elephant_friend_empty, + R.string.error_missing_edits + ) } - } else { - binding.statusView.setup(R.drawable.elephant_error, R.string.error_generic) { - viewModel.loadEdits(statusId, force = true) + else -> { + binding.statusView.setup(uiState.throwable) { + viewModel.loadEdits(statusId, force = true) + } } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsViewModel.kt index c1e76da3c..93f663587 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsViewModel.kt @@ -17,7 +17,7 @@ package com.keylesspalace.tusky.components.viewthread.edits import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import at.connyduck.calladapter.networkresult.fold +import at.connyduck.calladapter.networkresult.getOrElse import com.keylesspalace.tusky.components.viewthread.edits.TuskyTagHandler.Companion.DELETED_TEXT_EL import com.keylesspalace.tusky.components.viewthread.edits.TuskyTagHandler.Companion.INSERTED_TEXT_EL import com.keylesspalace.tusky.entity.StatusEdit @@ -48,6 +48,9 @@ class ViewEditsViewModel @Inject constructor(private val api: MastodonApi) : Vie private val _uiState: MutableStateFlow = MutableStateFlow(EditsUiState.Initial) val uiState: StateFlow = _uiState.asStateFlow() + /** The API call to fetch edit history returned less than two items */ + object MissingEditsException : Exception() + fun loadEdits(statusId: String, force: Boolean = false, refreshing: Boolean = false) { if (!force && _uiState.value !is EditsUiState.Initial) return @@ -58,63 +61,68 @@ class ViewEditsViewModel @Inject constructor(private val api: MastodonApi) : Vie } viewModelScope.launch { - api.statusEdits(statusId).fold( - { edits -> - // Diff each status' content against the previous version, producing new - // content with additional `ins` or `del` elements marking inserted or - // deleted content. - // - // This can be CPU intensive depending on the number of edits and the size - // of each, so don't run this on Dispatchers.Main. - viewModelScope.launch(Dispatchers.Default) { - val sortedEdits = edits.sortedBy { it.createdAt } - .reversed() - .toMutableList() + val edits = api.statusEdits(statusId).getOrElse { + _uiState.value = EditsUiState.Error(it) + return@launch + } - SAXLoader.setXMLReaderClass("org.xmlpull.v1.sax2.Driver") - val loader = SAXLoader() - loader.config = DiffConfig( - false, - WhiteSpaceProcessing.PRESERVE, - TextGranularity.SPACE_WORD + // `edits` might have fewer than the minimum number of entries because of + // https://github.com/mastodon/mastodon/issues/25398. + if (edits.size < 2) { + _uiState.value = EditsUiState.Error(MissingEditsException) + return@launch + } + + // Diff each status' content against the previous version, producing new + // content with additional `ins` or `del` elements marking inserted or + // deleted content. + // + // This can be CPU intensive depending on the number of edits and the size + // of each, so don't run this on Dispatchers.Main. + viewModelScope.launch(Dispatchers.Default) { + val sortedEdits = edits.sortedBy { it.createdAt } + .reversed() + .toMutableList() + + SAXLoader.setXMLReaderClass("org.xmlpull.v1.sax2.Driver") + val loader = SAXLoader() + loader.config = DiffConfig( + false, + WhiteSpaceProcessing.PRESERVE, + TextGranularity.SPACE_WORD + ) + val processor = OptimisticXMLProcessor() + processor.setCoalesce(true) + val output = HtmlDiffOutput() + + try { + // The XML processor expects `br` to be closed + var currentContent = + loader.load(sortedEdits[0].content.replace("
", "
")) + var previousContent = + loader.load(sortedEdits[1].content.replace("
", "
")) + + for (i in 1 until sortedEdits.size) { + processor.diff(previousContent, currentContent, output) + sortedEdits[i - 1] = sortedEdits[i - 1].copy( + content = output.xml.toString() ) - val processor = OptimisticXMLProcessor() - processor.setCoalesce(true) - val output = HtmlDiffOutput() - try { - // The XML processor expects `br` to be closed - var currentContent = - loader.load(sortedEdits[0].content.replace("
", "
")) - var previousContent = - loader.load(sortedEdits[1].content.replace("
", "
")) - - for (i in 1 until sortedEdits.size) { - processor.diff(previousContent, currentContent, output) - sortedEdits[i - 1] = sortedEdits[i - 1].copy( - content = output.xml.toString() - ) - - if (i < sortedEdits.size - 1) { - currentContent = previousContent - previousContent = loader.load( - sortedEdits[i + 1].content.replace("
", "
") - ) - } - } - _uiState.value = EditsUiState.Success(sortedEdits) - } catch (_: LoadingException) { - // Something failed parsing the XML from the server. Rather than - // show an error just return the sorted edits so the user can at - // least visually scan the differences. - _uiState.value = EditsUiState.Success(sortedEdits) + if (i < sortedEdits.size - 1) { + currentContent = previousContent + previousContent = loader.load( + sortedEdits[i + 1].content.replace("
", "
") + ) } } - }, - { throwable -> - _uiState.value = EditsUiState.Error(throwable) + _uiState.value = EditsUiState.Success(sortedEdits) + } catch (_: LoadingException) { + // Something failed parsing the XML from the server. Rather than + // show an error just return the sorted edits so the user can at + // least visually scan the differences. + _uiState.value = EditsUiState.Success(sortedEdits) } - ) + } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt b/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt index 6ef942545..491cd53d8 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt @@ -102,8 +102,8 @@ class Converters @Inject constructor( } @TypeConverter - fun jsonToAttachmentList(attachmentListJson: String?): ArrayList? { - return gson.fromJson(attachmentListJson, object : TypeToken>() {}.type) + fun jsonToAttachmentList(attachmentListJson: String?): List? { + return gson.fromJson(attachmentListJson, object : TypeToken>() {}.type) } @TypeConverter diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/DeletedStatus.kt b/app/src/main/java/com/keylesspalace/tusky/entity/DeletedStatus.kt index 872379138..c400a1af6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/DeletedStatus.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/DeletedStatus.kt @@ -16,7 +16,6 @@ package com.keylesspalace.tusky.entity import com.google.gson.annotations.SerializedName -import java.util.ArrayList import java.util.Date data class DeletedStatus( @@ -25,7 +24,7 @@ data class DeletedStatus( @SerializedName("spoiler_text") val spoilerText: String, val visibility: Status.Visibility, val sensitive: Boolean, - @SerializedName("media_attachments") val attachments: ArrayList?, + @SerializedName("media_attachments") val attachments: List?, val poll: Poll?, @SerializedName("created_at") val createdAt: Date, val language: String? diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt index f95997461..0b9a0796d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt @@ -19,7 +19,6 @@ import android.text.SpannableStringBuilder import android.text.style.URLSpan import com.google.gson.annotations.SerializedName import com.keylesspalace.tusky.util.parseAsMastodonHtml -import java.util.ArrayList import java.util.Date data class Status( @@ -42,7 +41,7 @@ data class Status( val sensitive: Boolean, @SerializedName("spoiler_text") val spoilerText: String, val visibility: Visibility, - @SerializedName("media_attachments") val attachments: ArrayList, + @SerializedName("media_attachments") val attachments: List, val mentions: List, val tags: List?, val application: Application?, diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt index a20fb4af0..1a64f69b0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt +++ b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt @@ -101,4 +101,7 @@ object PrefKeys { const val TAB_FILTER_HOME_REPLIES = "tabFilterHomeReplies_v2" // This was changed once to reset an unintentionally set default. const val TAB_FILTER_HOME_BOOSTS = "tabFilterHomeBoosts" + + /** UI text scaling factor, stored as float, 100 = 100% = no scaling */ + const val UI_TEXT_SCALE_RATIO = "uiTextScaleRatio" } diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsDSL.kt b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsDSL.kt index fc7a51c58..720dc817f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsDSL.kt +++ b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsDSL.kt @@ -14,6 +14,7 @@ import androidx.preference.PreferenceCategory import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceScreen import androidx.preference.SwitchPreference +import com.keylesspalace.tusky.view.SliderPreference import de.c1710.filemojicompat_ui.views.picker.preference.EmojiPickerPreference class PreferenceParent( @@ -43,6 +44,15 @@ inline fun PreferenceParent.emojiPreference(activity: A, builder: EmojiPicke return pref } +inline fun PreferenceParent.sliderPreference( + builder: SliderPreference.() -> Unit +): SliderPreference { + val pref = SliderPreference(context) + builder(pref) + addPref(pref) + return pref +} + inline fun PreferenceParent.switchPreference( builder: SwitchPreference.() -> Unit ): SwitchPreference { diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ThrowableExtensions.kt b/app/src/main/java/com/keylesspalace/tusky/util/ThrowableExtensions.kt index 26f962554..a3811a358 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ThrowableExtensions.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/ThrowableExtensions.kt @@ -1,8 +1,11 @@ package com.keylesspalace.tusky.util +import android.content.Context +import com.keylesspalace.tusky.R import org.json.JSONException import org.json.JSONObject import retrofit2.HttpException +import java.io.IOException /** * checks if this throwable indicates an error causes by a 4xx/5xx server response and @@ -24,3 +27,16 @@ fun Throwable.getServerErrorMessage(): String? { } return null } + +/** @return A drawable resource to accompany the error message for this throwable */ +fun Throwable.getDrawableRes(): Int = when (this) { + is IOException -> R.drawable.elephant_offline + is HttpException -> R.drawable.elephant_offline + else -> R.drawable.elephant_error +} + +/** @return A string error message for this throwable */ +fun Throwable.getErrorString(context: Context): String = getServerErrorMessage() ?: when (this) { + is IOException -> context.getString(R.string.error_network) + else -> context.getString(R.string.error_generic) +} diff --git a/app/src/main/java/com/keylesspalace/tusky/view/BackgroundMessageView.kt b/app/src/main/java/com/keylesspalace/tusky/view/BackgroundMessageView.kt index 4ccd5627e..650d92ccb 100644 --- a/app/src/main/java/com/keylesspalace/tusky/view/BackgroundMessageView.kt +++ b/app/src/main/java/com/keylesspalace/tusky/view/BackgroundMessageView.kt @@ -1,6 +1,7 @@ package com.keylesspalace.tusky.view import android.content.Context +import android.text.method.LinkMovementMethod import android.util.AttributeSet import android.view.Gravity import android.view.LayoutInflater @@ -12,6 +13,8 @@ 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.getDrawableRes +import com.keylesspalace.tusky.util.getErrorString import com.keylesspalace.tusky.util.visible /** @@ -34,16 +37,27 @@ class BackgroundMessageView @JvmOverloads constructor( } } + fun setup(throwable: Throwable, listener: ((v: View) -> Unit)? = null) { + setup(throwable.getDrawableRes(), throwable.getErrorString(context), listener) + } + + fun setup( + @DrawableRes imageRes: Int, + @StringRes messageRes: Int, + clickListener: ((v: View) -> Unit)? = null + ) = setup(imageRes, context.getString(messageRes), clickListener) + /** * Setup image, message and button. * If [clickListener] is `null` then the button will be hidden. */ fun setup( @DrawableRes imageRes: Int, - @StringRes messageRes: Int, + message: String, clickListener: ((v: View) -> Unit)? = null ) { - binding.messageTextView.setText(messageRes) + binding.messageTextView.text = message + binding.messageTextView.movementMethod = LinkMovementMethod.getInstance() binding.imageView.setImageResource(imageRes) binding.button.setOnClickListener(clickListener) binding.button.visible(clickListener != null) diff --git a/app/src/main/java/com/keylesspalace/tusky/view/SliderPreference.kt b/app/src/main/java/com/keylesspalace/tusky/view/SliderPreference.kt new file mode 100644 index 000000000..742a2cd76 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/view/SliderPreference.kt @@ -0,0 +1,185 @@ +package com.keylesspalace.tusky.view + +import android.content.Context +import android.content.res.TypedArray +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.view.View.VISIBLE +import androidx.appcompat.content.res.AppCompatResources +import androidx.preference.Preference +import androidx.preference.PreferenceViewHolder +import com.google.android.material.slider.LabelFormatter.LABEL_GONE +import com.google.android.material.slider.Slider +import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.databinding.PrefSliderBinding +import java.lang.Float.max +import java.lang.Float.min + +/** + * Slider preference + * + * Similar to [androidx.preference.SeekBarPreference], but better because: + * + * - Uses a [Slider] instead of a [android.widget.SeekBar]. Slider supports float values, and step sizes + * other than 1. + * - Displays the currently selected value in the Preference's summary, for consistency + * with platform norms. + * - Icon buttons can be displayed at the start/end of the slider. Pressing them will + * increment/decrement the slider by `stepSize`. + * - User can supply a custom formatter to format the summary value + */ +class SliderPreference @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = androidx.preference.R.attr.preferenceStyle, + defStyleRes: Int = 0 +) : Preference(context, attrs, defStyleAttr, defStyleRes), + Slider.OnChangeListener, + Slider.OnSliderTouchListener { + + /** Backing property for `value` */ + private var _value = 0F + + /** + * @see Slider.getValue + * @see Slider.setValue + */ + var value: Float = defaultValue + get() = _value + set(v) { + val clamped = max(max(v, valueFrom), min(v, valueTo)) + if (clamped == field) return + _value = clamped + persistFloat(v) + notifyChanged() + } + + /** @see Slider.setValueFrom */ + var valueFrom: Float + + /** @see Slider.setValueTo */ + var valueTo: Float + + /** @see Slider.setStepSize */ + var stepSize: Float + + /** + * Format string to be applied to values before setting the summary. For more control set + * [SliderPreference.formatter] + */ + var format: String = defaultFormat + + /** + * Function that will be used to format the summary. The default formatter formats using the + * value of the [SliderPreference.format] property. + */ + var formatter: (Float) -> String = { format.format(it) } + + /** + * Optional icon to show in a button at the start of the slide. If non-null the button is + * shown. Clicking the button decrements the value by one step. + */ + var decrementIcon: Drawable? = null + + /** + * Optional icon to show in a button at the end of the slider. If non-null the button is + * shown. Clicking the button increments the value by one step. + */ + var incrementIcon: Drawable? = null + + /** View binding */ + private lateinit var binding: PrefSliderBinding + + init { + // Using `widgetLayoutResource` here would be incorrect, as that tries to put the entire + // preference layout to the right of the title and summary. + layoutResource = R.layout.pref_slider + + val a = context.obtainStyledAttributes(attrs, R.styleable.SliderPreference, defStyleAttr, defStyleRes) + + value = a.getFloat(R.styleable.SliderPreference_android_value, defaultValue) + valueFrom = a.getFloat(R.styleable.SliderPreference_android_valueFrom, defaultValueFrom) + valueTo = a.getFloat(R.styleable.SliderPreference_android_valueTo, defaultValueTo) + stepSize = a.getFloat(R.styleable.SliderPreference_android_stepSize, defaultStepSize) + format = a.getString(R.styleable.SliderPreference_format) ?: defaultFormat + + val decrementIconResource = a.getResourceId(R.styleable.SliderPreference_iconStart, -1) + if (decrementIconResource != -1) { + decrementIcon = AppCompatResources.getDrawable(context, decrementIconResource) + } + + val incrementIconResource = a.getResourceId(R.styleable.SliderPreference_iconEnd, -1) + if (incrementIconResource != -1) { + incrementIcon = AppCompatResources.getDrawable(context, incrementIconResource) + } + + a.recycle() + } + + override fun onGetDefaultValue(a: TypedArray, i: Int): Any { + return a.getFloat(i, defaultValue) + } + + override fun onSetInitialValue(defaultValue: Any?) { + value = getPersistedFloat((defaultValue ?: Companion.defaultValue) as Float) + } + + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + binding = PrefSliderBinding.bind(holder.itemView) + + binding.root.isClickable = false + + binding.slider.addOnChangeListener(this) + binding.slider.addOnSliderTouchListener(this) + binding.slider.value = value // sliderValue + binding.slider.valueTo = valueTo + binding.slider.valueFrom = valueFrom + binding.slider.stepSize = stepSize + + // Disable the label, the value is shown in the preference summary + binding.slider.labelBehavior = LABEL_GONE + binding.slider.isEnabled = isEnabled + + binding.summary.visibility = VISIBLE + binding.summary.text = formatter(value) + + decrementIcon?.let { icon -> + binding.decrement.icon = icon + binding.decrement.visibility = VISIBLE + binding.decrement.setOnClickListener { + value -= stepSize + } + } + + incrementIcon?.let { icon -> + binding.increment.icon = icon + binding.increment.visibility = VISIBLE + binding.increment.setOnClickListener { + value += stepSize + } + } + } + + override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) { + if (!fromUser) return + binding.summary.text = formatter(value) + } + + override fun onStartTrackingTouch(slider: Slider) { + // Deliberately empty + } + + override fun onStopTrackingTouch(slider: Slider) { + value = slider.value + } + + companion object { + private const val TAG = "SliderPreference" + private const val defaultValueFrom = 0F + private const val defaultValueTo = 1F + private const val defaultValue = 0.5F + private const val defaultStepSize = 0.1F + private const val defaultFormat = "%3.1f" + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/worker/NotificationWorker.kt b/app/src/main/java/com/keylesspalace/tusky/worker/NotificationWorker.kt index 84fabd4a0..cc99b78b1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/worker/NotificationWorker.kt +++ b/app/src/main/java/com/keylesspalace/tusky/worker/NotificationWorker.kt @@ -17,10 +17,15 @@ package com.keylesspalace.tusky.worker +import android.app.Notification import android.content.Context -import androidx.work.Worker +import androidx.work.CoroutineWorker +import androidx.work.ForegroundInfo import androidx.work.WorkerParameters +import com.keylesspalace.tusky.R import com.keylesspalace.tusky.components.notifications.NotificationFetcher +import com.keylesspalace.tusky.components.notifications.NotificationHelper +import com.keylesspalace.tusky.components.notifications.NotificationHelper.NOTIFICATION_ID_FETCH_NOTIFICATION import javax.inject.Inject /** Fetch and show new notifications. */ @@ -28,16 +33,20 @@ class NotificationWorker( appContext: Context, params: WorkerParameters, private val notificationsFetcher: NotificationFetcher -) : Worker(appContext, params) { - override fun doWork(): Result { +) : CoroutineWorker(appContext, params) { + val notification: Notification = NotificationHelper.createWorkerNotification(applicationContext, R.string.notification_notification_worker) + + override suspend fun doWork(): Result { notificationsFetcher.fetchAndShow() return Result.success() } + override suspend fun getForegroundInfo() = ForegroundInfo(NOTIFICATION_ID_FETCH_NOTIFICATION, notification) + class Factory @Inject constructor( private val notificationsFetcher: NotificationFetcher ) : ChildWorkerFactory { - override fun createWorker(appContext: Context, params: WorkerParameters): Worker { + override fun createWorker(appContext: Context, params: WorkerParameters): CoroutineWorker { return NotificationWorker(appContext, params, notificationsFetcher) } } diff --git a/app/src/main/java/com/keylesspalace/tusky/worker/PruneCacheWorker.kt b/app/src/main/java/com/keylesspalace/tusky/worker/PruneCacheWorker.kt index c0ebdb79e..5a65a2ef9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/worker/PruneCacheWorker.kt +++ b/app/src/main/java/com/keylesspalace/tusky/worker/PruneCacheWorker.kt @@ -17,11 +17,16 @@ package com.keylesspalace.tusky.worker +import android.app.Notification import android.content.Context import android.util.Log import androidx.work.CoroutineWorker +import androidx.work.ForegroundInfo import androidx.work.ListenableWorker import androidx.work.WorkerParameters +import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.components.notifications.NotificationHelper +import com.keylesspalace.tusky.components.notifications.NotificationHelper.NOTIFICATION_ID_PRUNE_CACHE import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.db.AppDatabase import javax.inject.Inject @@ -33,6 +38,8 @@ class PruneCacheWorker( private val appDatabase: AppDatabase, private val accountManager: AccountManager ) : CoroutineWorker(appContext, workerParams) { + val notification: Notification = NotificationHelper.createWorkerNotification(applicationContext, R.string.notification_prune_cache) + override suspend fun doWork(): Result { for (account in accountManager.accounts) { Log.d(TAG, "Pruning database using account ID: ${account.id}") @@ -41,6 +48,8 @@ class PruneCacheWorker( return Result.success() } + override suspend fun getForegroundInfo() = ForegroundInfo(NOTIFICATION_ID_PRUNE_CACHE, notification) + companion object { private const val TAG = "PruneCacheWorker" private const val MAX_STATUSES_IN_CACHE = 1000 diff --git a/app/src/main/res/layout-land/item_trending_cell.xml b/app/src/main/res/layout-land/item_trending_cell.xml index a6f22b06d..7d71ee5f1 100644 --- a/app/src/main/res/layout-land/item_trending_cell.xml +++ b/app/src/main/res/layout-land/item_trending_cell.xml @@ -80,14 +80,13 @@ android:id="@+id/tag" android:layout_width="0dp" android:layout_height="wrap_content" - android:ellipsize="none" + android:ellipsize="end" android:importantForAccessibility="no" android:singleLine="true" android:textAlignment="textStart" android:textAppearance="?android:attr/textAppearanceListItem" android:textColor="?android:textColorPrimary" android:textStyle="normal" - app:layout_constrainedWidth="true" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="#itishashtagtuesdayitishashtagtuesday" /> diff --git a/app/src/main/res/layout/activity_account.xml b/app/src/main/res/layout/activity_account.xml index 021a1e8b6..9b6d4f813 100644 --- a/app/src/main/res/layout/activity_account.xml +++ b/app/src/main/res/layout/activity_account.xml @@ -443,8 +443,7 @@ android:layout_height="wrap_content" android:background="?attr/colorSurface" app:tabGravity="center" - app:tabMode="scrollable" - app:tabTextAppearance="@style/TuskyTabAppearance" /> + app:tabMode="scrollable" /> diff --git a/app/src/main/res/layout/activity_followed_tags.xml b/app/src/main/res/layout/activity_followed_tags.xml index 412b43105..27b8a38d0 100644 --- a/app/src/main/res/layout/activity_followed_tags.xml +++ b/app/src/main/res/layout/activity_followed_tags.xml @@ -25,6 +25,7 @@ android:layout_height="wrap_content" android:layout_gravity="center" app:layout_behavior="@string/appbar_scrolling_view_behavior" + tools:visibility="gone" /> + app:tabMode="fixed" /> diff --git a/app/src/main/res/layout/item_followed_hashtag.xml b/app/src/main/res/layout/item_followed_hashtag.xml index 4866679b9..6419af542 100644 --- a/app/src/main/res/layout/item_followed_hashtag.xml +++ b/app/src/main/res/layout/item_followed_hashtag.xml @@ -1,41 +1,36 @@ - + + - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/item_hashtag.xml b/app/src/main/res/layout/item_hashtag.xml index efdb24f72..8266eaf65 100644 --- a/app/src/main/res/layout/item_hashtag.xml +++ b/app/src/main/res/layout/item_hashtag.xml @@ -1,7 +1,17 @@ + + + + tools:ignore="SelectableText" /> diff --git a/app/src/main/res/layout/item_list.xml b/app/src/main/res/layout/item_list.xml index 42be426da..e1d5f7fc4 100644 --- a/app/src/main/res/layout/item_list.xml +++ b/app/src/main/res/layout/item_list.xml @@ -4,30 +4,29 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" - android:orientation="horizontal"> + android:orientation="horizontal" + android:minHeight="?android:attr/listPreferredItemHeightSmall" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:background="?attr/selectableItemBackground" + tools:ignore="Overdraw"> diff --git a/app/src/main/res/layout/item_picker_list.xml b/app/src/main/res/layout/item_picker_list.xml deleted file mode 100644 index d49c9303d..000000000 --- a/app/src/main/res/layout/item_picker_list.xml +++ /dev/null @@ -1,8 +0,0 @@ - - diff --git a/app/src/main/res/layout/item_tab_preference.xml b/app/src/main/res/layout/item_tab_preference.xml index 9eb164c3e..81e1f2b73 100644 --- a/app/src/main/res/layout/item_tab_preference.xml +++ b/app/src/main/res/layout/item_tab_preference.xml @@ -6,9 +6,10 @@ android:layout_height="wrap_content" android:background="?android:colorBackground" android:orientation="horizontal" - android:paddingStart="16dp" android:paddingTop="8dp" - android:paddingEnd="16dp"> + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:minHeight="?android:attr/listPreferredItemHeightSmall"> + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="@id/textView"/> diff --git a/app/src/main/res/layout/item_trending_cell.xml b/app/src/main/res/layout/item_trending_cell.xml index 10e497711..3e6360957 100644 --- a/app/src/main/res/layout/item_trending_cell.xml +++ b/app/src/main/res/layout/item_trending_cell.xml @@ -80,14 +80,13 @@ android:id="@+id/tag" android:layout_width="0dp" android:layout_height="wrap_content" - android:ellipsize="none" + android:ellipsize="end" android:importantForAccessibility="no" android:singleLine="true" android:textAlignment="textStart" android:textAppearance="?android:attr/textAppearanceListItem" android:textColor="?android:textColorPrimary" android:textStyle="normal" - app:layout_constrainedWidth="true" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="#itishashtagtuesdayitishashtagtuesday" /> @@ -107,7 +106,7 @@ app:layout_constraintHorizontal_chainStyle="spread_inside" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tag" - tools:text="12 345" /> + tools:text="12 34" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/view_background_message.xml b/app/src/main/res/layout/view_background_message.xml index 65e9cc5ac..3c6e26cdd 100644 --- a/app/src/main/res/layout/view_background_message.xml +++ b/app/src/main/res/layout/view_background_message.xml @@ -35,7 +35,7 @@ android:scaleType="centerInside" android:src="@drawable/elephant_offline" /> - مدیریت سیاهه‌ها خطا در بار کردن سیاهه‌ها بار کردن جدیدترین آگاهی‌ها + حذف پیش‌نویس؟ + کارسازتان می‌داند که این فرسته ویرایش شده؛ ولی رونوشتی از ویرایش‌ها ندارد. پس نمی‌توانند نشانتان داده شوند. +\n +\nاین نکتهٔ ماستودون را ببینید. \ No newline at end of file diff --git a/app/src/main/res/values-gd/strings.xml b/app/src/main/res/values-gd/strings.xml index ad77f0a88..2fc751cf0 100644 --- a/app/src/main/res/values-gd/strings.xml +++ b/app/src/main/res/values-gd/strings.xml @@ -681,4 +681,9 @@ Seo loidhne-ama do dhachaigh. Seallaidh i na postaichean o chionn goirid aig na cunntasan a leanas tu. \n \nAirson cunntasan a rùrachadh, lorg iad air tè dhe na loidhnichean-ama eile; mar eisimpleir, loidhne-ama ionadail an ionstans agad [iconics gmd_group]. Air neo lorg cunntasan a-rèir ainm [iconics gmd_search]; mar eisimpleir, lorg “Tusky” ach am faigh thu grèim air a’ chunntas againne air Mastodon. - + A bheil thu airson an dreachd a sguabadh às\? + Luchdaich na brathan as ùire + Tha fios aig an fhrithealaiche gun deach am post seo a dheasachadh ach chan eil lethbhreac dhen deasachadh aige-san is chan urrainn dhuinn a shealltainn dhut. +\n +\nSeo duilgheadas Mastodon #25398. + \ No newline at end of file diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 32e11f621..598c2584e 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -584,7 +584,7 @@ Cargando fío Acalar notificacións Edicións - Editado por %1$s + Editada: %1$s Creado por %1$s Desbotar cambios Continuar a edición @@ -657,4 +657,9 @@ Aínda non tes listas Xestionar listas Erro ao cargar as listas - + O teu servidor sabe que a publicación foi editada, pero non ten unha copia das edición, polo que non pode mostrarchas. +\n +\nÉ un problema coñecido en Mastodon. + Cargar as notificacións máis recentes + Eliminar borrador\? + \ No newline at end of file diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 379209134..323b4a225 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -401,4 +401,9 @@ पोस्ट बहुत लंबा है! उस सर्वर से प्रमाणित करने में विफल। - + वीडियो और ऑडियो फ़ाइलों का आकार %s एमबी से अधिक नहीं हो सकता है। + फोटो संपादित नहीं किया जा सका। + फोटो और वीडियो दोनों को एक ही पोस्ट से अटैच नहीं किया जा सकता है। + खाता विवरण लोड करने में विफल रहा + लॉगिन पेज लोड नहीं किया जा सका। + \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 7ee976085..0915d8bd4 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -621,6 +621,6 @@ リストはまだありません とにかく表示する プロフィール - トレンドノハッシュタグ + トレンドのハッシュタグ タイムラインに投稿の統計情報を表示する - + \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 8956cf714..aed69f913 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -4,21 +4,21 @@ Er deed zich een netwerkfout voor! Controleer je verbinding en probeer opnieuw! Dit mag niet leeg zijn. Ongeldige domeinnaam ingevoerd - Authenticatie met die server is mislukt. + Authenticatie met die server is mislukt. Als het probleem blijft, probeer dan de browser login via het menu. Kon geen bruikbare webbrowser vinden. - Er deed zich een onbekende autorisatiefout voor. - Autorisatie werd geweigerd. - Kon geen inlogsleutel verkrijgen. + Er deed zich een onbekende autorisatiefout voor. Als dit blijft, probeer dan “ Via de browser inloggen” in het menu. + Autorisatie werd geweigerd. Als u zeker weet dat u de correcte gegevens heeft ingevoerd, probeer dan in te loggen in de browser via het menu. + Kon geen inlogsleutel verkrijgen. Als het probleem zich blijft herhalen; probeer dan de browser login via menu. Tekst van dit bericht is te lang! - Bestandstype kan niet worden geüpload. + Bestandstype wordt niet ondersteund. Bestand kon niet worden geopend. Er is toestemming nodig om deze media te lezen. Er is toestemming nodig om media op te slaan. - Afbeeldingen en video\'s kunnen niet allebei aan hetzelfde bericht worden toegevoegd. + Afbeeldingen en video\'s kunnen niet samen aan hetzelfde bericht worden toegevoegd. Uploaden mislukt. Fout tijdens verzenden bericht. Start - Meldingen + Notificaties Lokaal Globaal Directe berichten @@ -47,7 +47,7 @@ Inklappen Hier is niets. Niets te zien. Swipe naar beneden om te verversen! - %s boostte jouw bericht + %s boostte jou bericht %s markeerde jouw bericht als favoriet %s volgt jou Rapporteer @%s @@ -60,8 +60,8 @@ Favoriet verwijderen Meer Bericht schrijven - Aanmelden met Mastodon - Afmelden + Aanmelden met Tusky + Uitloggen Ben je er zeker van dat je het account %1$s wil afmelden? Volgen Ontvolgen @@ -464,7 +464,7 @@ Jouw eigen opmerking over dit account Welzijn De bovenste werkbalk verbergen - Vraag voor het boosten van een bericht een bevestiging + Vraag bevestiging voor het boosten Linkpreviews in tijdlijnen weergeven Er zijn geen aankondigingen. Oneindig @@ -534,7 +534,7 @@ 90 dagen 180 dagen 365 dagen - Vraag voor het markeren als favoriet een bevestiging + Vraag bevestiging voor het markeren als favoriet Bericht schrijven Geregistreerd in %1$s Concept wordt opgeslagen… @@ -590,4 +590,65 @@ Gebruikersnaam gekopieerd #%s ontvolgd Gevolgde hashtags - + Volg hashtag + #hashtag + Beschrijving + Upload mislukt + Je bericht kan niet worden geüpload en is opgeslagen in concepten. +\n +\nEr kon geen contact worden opgenomen met de server of de post werd geweigerd. + Je berichten kunnen niet worden geüpload en zijn opgeslagen in concepten. +\n +\nEr kon geen contact worden opgenomen met de server of de berichten werden geweigerd. + Afwijzen + Concepten tonen + Fout bij unmuting #%s + Inloggen met een Browser + reactie toevoegen + Veranderingen ongedaan maken + Bewerkt %s + %s gerapporteerd %s + %s · %d berichten bijgevoegd + Link naar account delen + Deel de gebruikersnaam van het account + Accountgebruikersnaam delen om… + Account-URL delen met… + Deze instance ondersteunt niet het volgen van hashtags. + Fout bij het laden van de statusbron van server. + Trending hashtags + <niet ingesteld> + <ongeldig> + Ververs + Afbeelding + Boosten bericht mislukt: %s + Stemmen in peiling mislukt: %s + Poort moet liggen tussen %d en %d + Onbekend + Wissen meldingen mislukt: %s + Volgverzoek geaccepteerd + Fout bij laden lijsten + Je hebt nog geen lijsten + Lijsten beheren + Profielen + Toch tonen + Gefilterd: %s + Mijn filter + Filteractie + Waarschuwen + Verbergen + Verberg met een waarschuwing + Verberg helemaal + Toevoegen + %s (heel woord) + %s: %s + %1$d mensen bespreken hashtag %2$s + Totaal gebruik + Totaal accounts + Afwijzen volgverzoek mislukt: %s + Toon bericht statistieken in tijdlijn + onbekende reden + Accepteren volgverzoek mislukt: %s + Titel + Laad nieuwste meldingen + Verwijder concept\? + \ No newline at end of file diff --git a/app/src/main/res/values-oc/strings.xml b/app/src/main/res/values-oc/strings.xml index f9e3bae30..3562d02f2 100644 --- a/app/src/main/res/values-oc/strings.xml +++ b/app/src/main/res/values-oc/strings.xml @@ -666,4 +666,9 @@ Aquò es vòstra cronologia. Mòstra las publicacions recentas dels comptes que seguissètz. \n \nPer explorar mai de compte podètz siá los descobrir dins d’autres fils, per exemple lo fil local de vòstra instància [iconics gmd_group], siá los trapar per lor nom [iconics gmd_search], per exemple « Tusky » per trobar nòstre compte Mastodon. - + Vòstre servidor sap qu’aquesta publicacion foguèt modificada mas ten pas cap de còpia de las modificacions doncas vos las pòt afichar. +\n +\nAquò es un problèma de Mastodon #25398. + Cargar las notificacions mai recentas + Suprimir lo borrolhon \? + \ No newline at end of file diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index be6911222..95721b0b0 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -646,4 +646,6 @@ Quản lý danh sách Xảy ra lỗi khi tải danh sách Bạn chưa có danh sách - + Tải những thông báo mới nhất + Xóa bản nháp\? + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 4f6e85a21..cf649891c 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -423,7 +423,7 @@ 转发到 %s 举报失败 无法获取嘟文 - 该报告将发送给给您的服务器管理员。您可以在下面提供有关回报此账户的原因的说明: + 举报将发送给你所在服务器的管理员。你可以在下面提供举报此账户的相关说明: 该账户来自其他服务器。向那里发送一份匿名的报告副本? 账户 搜索失败 @@ -441,7 +441,7 @@ 选择 %d 编辑 查找嘟文时出错 %s - 您没有草稿。 + 你没有草稿 您没有任何定时嘟文。 Mastodon的最小预订时间为5分钟。 关注请求 @@ -486,10 +486,10 @@ 反馈通知 隐藏嘟文的统计信息 限制时间线通知 - 一些可能影响您精神状态的信息将被隐藏,这些信息包括: + 一些可能影响你精神状态的信息将被隐藏,包括: \n \n - 喜欢、转发、关注通知 -\n - 喜欢、转发数 +\n - 嘟文的喜欢、转发数 \n - 账号的已关注数量、嘟文数量 \n \n 推送通知不会被影响,但可以在通知设置中手动禁用。 @@ -506,7 +506,7 @@ 显示动态自定义Emoji 关注的人发布了新嘟文 %s 刚刚发送了新嘟文 - 即使您的账号未上锁,管理员 %1$s 认为您可能需要手动处理来自这些账号的关注请求。 + 即使你的账号未上锁,但 %1$s 的管理员认为你可能需要手动处理这些账号的关注请求。 删除此对话吗? 删除对话 收藏前提示确认 @@ -556,7 +556,7 @@ 删除这条定时嘟文吗? 登录即表示您同意 %s 的规定。 %s 的规定 - 保存草稿?(当您恢复草稿时附件将被再次上传。) + 保存草稿?(恢复草稿时附件将被再次上传) 固定失败 取消固定失败 添加回应 @@ -665,4 +665,8 @@ 加载列表出错 你还没有列表 加载最新通知 + 删除草稿? + 你的服务器知晓这篇帖子被编辑,但没有编辑的副本,所以无法呈现给你。 +\n +\n这是一个 Mastodon issue #25398 \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 45b0c4ec8..5cd9a21ac 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -22,6 +22,16 @@ + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5abb052dc..9fbb6fda3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -18,7 +18,7 @@ An error occurred. - A network error occurred! Please check your connection and try again! + A network error occurred. Please check your connection and try again. This cannot be empty. Invalid domain entered Failed authenticating with that instance. If this persists, try "Login in Browser" from the menu. @@ -338,6 +338,7 @@ Unlisted Followers-only + UI text size Post text size Smallest @@ -370,6 +371,8 @@ Notifications when posts you\'ve interacted with are edited Reports Notifications about moderation reports + Background activity + Notifications when Tusky is working in the background Unknown %s mentioned you @@ -380,6 +383,8 @@ %d new interaction %d new interactions + Fetching notifications… + Cache maintenance… Locked Account @@ -764,10 +769,9 @@ Oldest first Newest first - Edited: %1$s - Created: %1$s + Your server knows that this post was edited, but does not have a copy of the edits, so they can\'t be shown to you.\n\nThis is Mastodon issue #25398. Loading thread diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 3072a7396..bcf041b04 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -106,7 +106,7 @@ diff --git a/build.gradle b/build.gradle index 14527214e..7689e2fb6 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,6 @@ plugins { alias(libs.plugins.android.application) apply false + alias(libs.plugins.google.ksp) apply false alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.kapt) apply false alias(libs.plugins.kotlin.parcelize) apply false diff --git a/fastlane/metadata/android/en-US/changelogs/111.txt b/fastlane/metadata/android/en-US/changelogs/111.txt new file mode 100644 index 000000000..8b3d2039d --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/111.txt @@ -0,0 +1,15 @@ +Tusky 23.0 beta 1 + +New features: + +- New preference to scale UI text + +Fixes: + +- Save account information correctly +- "pull" notifications on devices running Android versions <= 11 +- Work around Android bug where text fields could "forget" they can copy/paste +- Viewing "diffs" in edit history will not extend off screen edge +- Don't crash if your server has no post edit history +- Add a "Delete" button when editing a filter +- Show non-square emoji correctly diff --git a/fastlane/metadata/android/es/changelogs/103.txt b/fastlane/metadata/android/es/changelogs/103.txt new file mode 100644 index 000000000..77d13088f --- /dev/null +++ b/fastlane/metadata/android/es/changelogs/103.txt @@ -0,0 +1,18 @@ +Tusky 22.0 beta 1 + +Entre sus características se incluyen: + +- Visualización de etiquetas que son tendencia +- Editar descripciones de imágenes y puntos de enfoque +- Menú "Refrescar" Para fines de accesibilidad +- Soporte para filtros de Mastodon v4 +- Mostrar información detallada cuando se edita una publicación +- Opción para mostrar estadísticas de publicaciones en la cronología + +Entre las correcciones se incluye: + +- Mostrar controles del reproductor durante la reproducción de audio +- Calculación correcta de la longitud de una publicación +- Siempre publicar descripciones de imágenes + +Entre otras diff --git a/fastlane/metadata/android/fa/changelogs/111.txt b/fastlane/metadata/android/fa/changelogs/111.txt new file mode 100644 index 000000000..4b708c31a --- /dev/null +++ b/fastlane/metadata/android/fa/changelogs/111.txt @@ -0,0 +1,15 @@ +تاسکی ۲۳٫۰ بتا + +ویژگی‌های جدید: + +- ترجیح جدید برای مقیاس متن میانای کاربری + +رفع اشکال‌ها: + +- ذخیرهٔ درست اطّلاعات حساب +- «گرفتن» آگاهی‌ها روی افزاره‌های با نگارش اندروید کم‌تر از ۱۱ +- دور زدن مشکل امکان فراموشی توانایی رونوشت و جای‌گذاری در زمینه‌های متنی اندروید +- تجاوز نکردن از لیهٔ صفحه هنگام دیدن «تفاوت‌ها» در تاریخچهٔ ویرایش +- فرونپاشیدن در صورت نبودن تاریخچهٔ ویرایش فرسته روی کارساز +- افزودن دکمهٔ «حذف» هنگام ویرایش یک پالایه +- نمایش درست اموجی‌های نامربّعی diff --git a/fastlane/metadata/android/fa/changelogs/94.txt b/fastlane/metadata/android/fa/changelogs/94.txt index 5a9697372..75c4e6e47 100644 --- a/fastlane/metadata/android/fa/changelogs/94.txt +++ b/fastlane/metadata/android/fa/changelogs/94.txt @@ -1,9 +1,9 @@ تاسکی ۱۹٫۰ -- Support for Unified Push. To activate the support you will have to relogin into your accounts. -- The number of responses to a post is now indicated in timelines. -- Images can now by cropped while composing a post. -- Profiles now show the date when they were created. -- When viewing a list the title is now displayed in the toolbar. -- A lot of bugfixes -- Translation improvements +- پشتیبانی از Unified Push. برای فعّال کردن این پشتیبانی باید دوباره به حساب‌هایتان وارد شوید. +- اکنون تعداد پاسخ‌ها به فرسته‌ها در خط‌های زمانی نشان داده می‌شود. +- اکنون تصویرها می‌توانند هنگام ایجاد فرسته بریده شوند. +- اکنون نمایه‌ها تاریخ ایجادشان را نشان می‌دهند. +- اکنون عنوان سیاهه هنگام دیدنش در نوار ابزار نمایش داده می‌شود. +- یک عالمه رفع اشکال +- بهبودهای ترجمه diff --git a/fastlane/metadata/android/hu/changelogs/103.txt b/fastlane/metadata/android/hu/changelogs/103.txt new file mode 100644 index 000000000..8496a0517 --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/103.txt @@ -0,0 +1,18 @@ +Tusky 22.0 beta 1 + +Új funkciók: + +- Trendi hashtag-ek megtekintése +- Képleírás és fókuszpont szerkesztése +- "Frissítés" menü az elérhetőségért +- Mastodon v4 szűrők támogatása +- Részletes különbségek mutatása, amikor egy bejegyzést szerkesztettek +- Opció bejegyzés-statisztikák idővonalon való megjelenítésére + +Javítások: + +- Lejátszó vezérlőinek mutatása hangvisszajátszás közben +- Bejegyzés hosszkalkuláció javítása +- Mindig közzétesszük a képfeliratokat + +és még sok más diff --git a/fastlane/metadata/android/hu/changelogs/104.txt b/fastlane/metadata/android/hu/changelogs/104.txt new file mode 100644 index 000000000..fe9867925 --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/104.txt @@ -0,0 +1,10 @@ +Tusky 22.0 beta 2 + +Javítások: + +- Javított értesítés-betöltési sebesség +- Újra mutatjuk a 0/1/1+ a válaszoknál +- Szűrőcímek mutatása, nem a kulcsszavaké a szűrt bejegyzéseken +- Hibajavítás: egy bejegyzés megnyitása megnyithatott egy nem kapcsolódó hivatkozást +- Mutassuk a "Hozzáadás" a megfelelő helyen, ha nincsenek szűrők +- Különböző programleállások javítása diff --git a/fastlane/metadata/android/hu/changelogs/105.txt b/fastlane/metadata/android/hu/changelogs/105.txt new file mode 100644 index 000000000..4457628bf --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/105.txt @@ -0,0 +1,11 @@ +Tusky 22.0 beta 3 + +Javítások: + +- Szálak nézegetése közbeni programleállás javítása +- Mastodon szűrők feldolgozása közbeni programleállás javítása +- Profilokon a követési hivatkozások kattinthatóak +- Androidos értesítés-kezelési frissítések + - Android értesítés a Mastodonról csak egyszer mutatandó + - Android értesítések Mastodon értesítési típus (követés, említés, megtolás, stb) szerint vannak csoportosítva + - Lehetséges értesítés-elhagyás megoldva diff --git a/fastlane/metadata/android/hu/changelogs/106.txt b/fastlane/metadata/android/hu/changelogs/106.txt new file mode 100644 index 000000000..25cf2ab07 --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/106.txt @@ -0,0 +1,5 @@ +Tusky 22.0 beta 4 + +Javítások: + +- Több fiók használata esetén az értesítések újra történő lekérésének megoldása diff --git a/fastlane/metadata/android/hu/changelogs/107.txt b/fastlane/metadata/android/hu/changelogs/107.txt new file mode 100644 index 000000000..fd151c7e0 --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/107.txt @@ -0,0 +1,6 @@ +Tusky 22.0 beta 5 + +Javítások: + +- APNG könytár vissza a régire a törött animált emojik javításáért +- Értesítésjelző helyi mentése arra az esetre, ha a kiszolgáló nem támogatja az API-t diff --git a/fastlane/metadata/android/hu/changelogs/108.txt b/fastlane/metadata/android/hu/changelogs/108.txt new file mode 100644 index 000000000..c88d7f640 --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/108.txt @@ -0,0 +1,5 @@ +Tusky 22.0 beta 6 + +Javítások: + +- Értesítési fülön az olvasási pozíció gyakoribb mentése diff --git a/fastlane/metadata/android/hu/changelogs/109.txt b/fastlane/metadata/android/hu/changelogs/109.txt new file mode 100644 index 000000000..277fbd6bb --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/109.txt @@ -0,0 +1,10 @@ +Tusky 22.0 beta 7 + +Javítások: + + +### Jelentős hibák + +- Minden fontos Mastodon értesítés lekérése, amikor Android értesítéseket hozunk létre +- A "Üzenetírás" kattintása rossz fiókra vitt, megoldva +- Annak biztosítása, hogy az "utolsó olvasott értesítés azonosítója" a helyes fiókba mentődjön diff --git a/fastlane/metadata/android/hu/changelogs/110.txt b/fastlane/metadata/android/hu/changelogs/110.txt new file mode 100644 index 000000000..60db53acb --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/110.txt @@ -0,0 +1,20 @@ +Tusky 22.0 + +Új funkciók: + +- Felkapott hashtag-ek mutatása +- Új hashtag-ek bekövetése +- Jobb sorrendezés nyelvek választásakor +- Bejegyzésverziók közötti különbségek mutatása +- Mastodon v4 szűrők támogatása +- Lehetőség bejegyzés-statisztikák mutatására az idővonalon +- És sok más... + +Javítások: + +- Kiválasztott fül és pozíció megjegyzése +- Értesítések megtartása az elolvasásig +- Kevert balról jobbra és jobbról balra szöveg helyes mutatása a profilon +- Bejegyzéshossz kiszámításának javítása +- Képfeliratok publikálása minden esetben +- És sok más.. diff --git a/fastlane/metadata/android/hu/changelogs/111.txt b/fastlane/metadata/android/hu/changelogs/111.txt new file mode 100644 index 000000000..47aa24dc8 --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/111.txt @@ -0,0 +1,15 @@ +Tusky 23.0 beta 1 + +Új funkciók: + +- Új beállítás UI-szöveg nagyítására + +Javítások: + +- Fiókinfók helyes mentése +- "leküldési" értesítések Android 11-nél régebbi eszközön +- Androidos hibajavítás, ahol a szövegmezők "elfelejtik", hogy tudnak szöveget másolni +- A "különbségek" mutatása a szerkesztési történetben nem túl nagy +- Összeomlás elkerülése, ha a kiszolgáló nem ismeri a bejegyzés történetét +- "Törlés" gomb hozzáadása, amikor szűrőt szerkesztünk +- Nem kocka emojik helyes megjelenítése diff --git a/fastlane/metadata/android/sv/changelogs/100.txt b/fastlane/metadata/android/sv/changelogs/100.txt new file mode 100644 index 000000000..8c61e658c --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/100.txt @@ -0,0 +1,7 @@ +Tusky 21.0 + +- Stöd för inläggsredigering +- Ny inställning för att styra önskad läsriktning +- Större mediaförhandsvisningar och ett nytt överlägg för att indikera media med beskrivning +– Det är nu möjligt att lägga till konton till listor från sin profil +och mycket mer diff --git a/fastlane/metadata/android/sv/changelogs/103.txt b/fastlane/metadata/android/sv/changelogs/103.txt new file mode 100644 index 000000000..9b64c3f26 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/103.txt @@ -0,0 +1,18 @@ +Tusky 22.0 beta 1 + +Funktioner inklusive: + +- Se trendiga hashtaggar +- Redigera bildbeskrivningar och fokuspunkt +- "Uppdatera"-menyn för tillgänglighet +- Stöd Mastodon v4-filter +- Visa detaljerade skillnader när ett inlägg redigeras +- Möjlighet att visa inläggsstatistik i tidslinjen + +Fixar inkluderar: + +- Visa spelarkontroller under ljuduppspelning +- Korrekt beräkning av stolplängd +- Publicera alltid bildtexter + +och mycket mer diff --git a/fastlane/metadata/android/sv/changelogs/104.txt b/fastlane/metadata/android/sv/changelogs/104.txt new file mode 100644 index 000000000..7307b7b33 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/104.txt @@ -0,0 +1,10 @@ +Tusky 22.0 beta 2 + +Fixar inkluderar: + +- Förbättrad aviseringshastighet +- Återställ visar 0/1/1+ för svar +- Visa filtertitlar, inte filtersökord, på filtrerade inlägg +- Fixade en bugg där öppnande av en status kunde öppna en orelaterade länk +- Visa "Lägg till"-knappen på rätt plats när det inte finns några filter +- Fixade diverse krascher diff --git a/fastlane/metadata/android/sv/changelogs/105.txt b/fastlane/metadata/android/sv/changelogs/105.txt new file mode 100644 index 000000000..df5478a66 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/105.txt @@ -0,0 +1,11 @@ +Tusky 22.0 beta 3 + +Fixar inkluderar: + +- Fixade en krasch vid visning av en tråd +- Fixat kraschbehandling av Mastodon-filter +- Länkar i bios för meddelanden om följ/följ-förfrågningar är klickbara +- Uppdateringar av Android-aviseringar + - Android-avisering för en Mastodon-avisering bör endast visas en gång + - Android-aviseringar grupperas efter Mastodon-meddelandetyp (följ, nämn, boost, etc) + - Potentialen för saknade aviseringar har tagits bort diff --git a/fastlane/metadata/android/sv/changelogs/106.txt b/fastlane/metadata/android/sv/changelogs/106.txt new file mode 100644 index 000000000..208429729 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/106.txt @@ -0,0 +1,5 @@ +Tusky 22.0 beta 4 + +Fixar: + +- Fixade upprepad hämtning av meddelanden om konfigurerad med flera konton diff --git a/fastlane/metadata/android/sv/changelogs/107.txt b/fastlane/metadata/android/sv/changelogs/107.txt new file mode 100644 index 000000000..1cdafd525 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/107.txt @@ -0,0 +1,6 @@ +Tusky 22.0 beta 5 + +Fixar: + +- Återställt APNG-bibliotek för att fixa trasiga animerade emojis +- Spara lokal kopia av meddelandemarkören om servern inte stöder API diff --git a/fastlane/metadata/android/sv/changelogs/108.txt b/fastlane/metadata/android/sv/changelogs/108.txt new file mode 100644 index 000000000..8814f3626 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/108.txt @@ -0,0 +1,5 @@ +Tusky 22.0 beta 6 + +Fixar: + +- Spara läsposition på Aviseringar fliken oftare diff --git a/fastlane/metadata/android/sv/changelogs/109.txt b/fastlane/metadata/android/sv/changelogs/109.txt new file mode 100644 index 000000000..2a6f078c3 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/109.txt @@ -0,0 +1,10 @@ +Tusky 22.0 beta 7 + +Fixar: + + +### Betydande buggfixar + +- Hämta alla utestående Mastodon aviseringar när du skapar Android-aviseringar +- Om du klickar på "Skriv" från ett meddelande ställs fel konto in +- Se till att "senast lästa meddelande-ID" är sparat på rätt konto diff --git a/fastlane/metadata/android/sv/changelogs/110.txt b/fastlane/metadata/android/sv/changelogs/110.txt new file mode 100644 index 000000000..3e80f8611 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/110.txt @@ -0,0 +1,20 @@ +Tusky 22.0 + +Nya egenskaper: + +- Se trendiga hashtaggar +- Följ nya hashtaggar +- Bättre ordning vid val av språk +- Visa skillnaden mellan versioner av ett inlägg +- Stöd Mastodon v4-filter +- Möjlighet att visa inläggsstatistik i tidslinjen +- Och mer... + +Fixar: + +- Kom ihåg vald flik och position +- Behåll aviseringar tills de läses +- Visa blandad RTL- och LTR-text korrekt i profiler +- Korrekt beräkning av stolplängd +- Publicera alltid bildtexter +- Och mer... diff --git a/fastlane/metadata/android/sv/changelogs/111.txt b/fastlane/metadata/android/sv/changelogs/111.txt new file mode 100644 index 000000000..f1ed7d0ce --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/111.txt @@ -0,0 +1,15 @@ +Tusky 23.0 beta 1 + +Nya funktioner: + +- Ny inställning för att skala UI-text + +Fixar: + +- Sparar kontoinformation korrekt +- "pull"-meddelanden på enheter som kör Android-versioner <= 11 +- Undvik Android-bugg där textfält kan "glömma" att de kan kopiera/klistra in +- Att visa "diffs" i redigeringshistoriken kommer inte att sträcka sig utanför skärmkanten +- Krascha inte om din server inte har någon inläggsredigeringshistorik +- Lägg till en "Radera"-knapp när du redigerar ett filter +- Visa icke-fyrkantiga emoji korrekt diff --git a/fastlane/metadata/android/sv/changelogs/58.txt b/fastlane/metadata/android/sv/changelogs/58.txt index 15f09a105..08f91a39a 100644 --- a/fastlane/metadata/android/sv/changelogs/58.txt +++ b/fastlane/metadata/android/sv/changelogs/58.txt @@ -1 +1,13 @@ -https://github.com/tuskyapp/Tusky/releases +Tusky v6.0 + +- Tidslinjefilter har flyttats till kontoinställningar och kommer att synkroniseras med servern +- Du kan nu ha en anpassad hashtagg som flik i huvudgränssnittet +- Listor kan nu redigeras +- Säkerhet: tog bort stödet för TLS 1.0 och TLS 1.1 och lade till stöd för TLS 1.3 på Android 6+ +- Skrivvyn kommer nu att föreslå anpassade emojis när du börjar skriva +- Ny temainställning "följ systemtema" +- Förbättrad tillgänglighet till tidslinjen +- Tusky kommer nu att ignorera okända meddelanden och inte längre krascha +- Ny inställning: Du kan nu åsidosätta systemspråket och ställa in ett annat språk i Tusky +- Nya översättningar: tjeckiska och esperanto +– Många andra förbättringar och fixar diff --git a/fastlane/metadata/android/sv/changelogs/67.txt b/fastlane/metadata/android/sv/changelogs/67.txt index 15f09a105..cf8935b65 100644 --- a/fastlane/metadata/android/sv/changelogs/67.txt +++ b/fastlane/metadata/android/sv/changelogs/67.txt @@ -1 +1,9 @@ -https://github.com/tuskyapp/Tusky/releases +Tusky v9.0 + +- Du kan nu skapa omröstningar från Tusky +- Förbättrad sökning +- Nytt alternativ i Kontoinställningar för att alltid utöka innehållsvarningar +- Avatarer i navigeringslådan har nu en rundad fyrkantig form +– Det är nu möjligt att rapportera användare även när de aldrig lagt upp en status +- Tusky kommer nu att vägra ansluta över klartextanslutningar på Android 6+ +– Många andra små förbättringar och buggfixar diff --git a/fastlane/metadata/android/sv/changelogs/72.txt b/fastlane/metadata/android/sv/changelogs/72.txt index dcb45833c..ae3975a29 100644 --- a/fastlane/metadata/android/sv/changelogs/72.txt +++ b/fastlane/metadata/android/sv/changelogs/72.txt @@ -1,2 +1,11 @@ Tusky v11.0 -https://github.com/tuskyapp/Tusky/releases + +- Aviseringar om nya följningsförfrågningar när ditt konto är låst +- Nya funktioner som kan växlas på skärmen Inställningar: + - inaktivera svepning mellan flikarna + - visa en bekräftelsedialog innan du förstärker en tut + - visa länkförhandsvisningar i tidslinjer +– Konversationer kan nu stängas av +- Omröstningsresultat kommer nu att beräknas baserat på antalet väljare och inte på antalet totala röster, vilket gör flervalsundersökningar lättare att förstå +– Många buggfixar, de flesta relaterade till att komponera tutter +- Förbättrade översättningar diff --git a/fastlane/metadata/android/sv/changelogs/77.txt b/fastlane/metadata/android/sv/changelogs/77.txt index bb3c96625..5b4a74b22 100644 --- a/fastlane/metadata/android/sv/changelogs/77.txt +++ b/fastlane/metadata/android/sv/changelogs/77.txt @@ -1,10 +1,10 @@ Tusky v13.0 -- support for profile notes (Mastodon 3.2.0 feature) -- support for admin announcements (Mastodon 3.1.0 feature) +- stöd för profilanteckningar (Mastodon 3.2.0-funktion) +- stöd för adminmeddelanden (Mastodon 3.1.0-funktion) -- the avatar of your selected account will now be shown in the main toolbar -- clicking the display name in a timeline will now open the profile page of that user +- avataren för ditt valda konto kommer nu att visas i huvudverktygsfältet +- genom att klicka på visningsnamnet i en tidslinje öppnas nu användarens profilsida -- a lot of bug fixes and small improvements -- improved translations +- många buggfixar och små förbättringar +- förbättrade översättningar diff --git a/fastlane/metadata/android/sv/changelogs/80.txt b/fastlane/metadata/android/sv/changelogs/80.txt new file mode 100644 index 000000000..8052c8803 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/80.txt @@ -0,0 +1,7 @@ +Tusky v14.0 + +- Få ett meddelande när en användare gör inlägg - klicka på klockikonen på sin profil! (Mastodon 3.3.0-funktion) +- Draft-funktionen i Tusky har gjorts om helt för att vara snabbare, mer användarvänlig och mindre buggig. +- Ett nytt välmåendeläge som låter dig begränsa vissa Tusky-funktioner har lagts till. +- Tusky kan nu animera anpassade emojis. +Fullständig ändringslogg: https://github.com/tuskyapp/Tusky/releases diff --git a/fastlane/metadata/android/sv/changelogs/82.txt b/fastlane/metadata/android/sv/changelogs/82.txt new file mode 100644 index 000000000..b6dd4ef05 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/82.txt @@ -0,0 +1,5 @@ +Tusky v15.0 + +- Följ förfrågningar visas nu alltid i huvudmenyn. +– Tidsväljaren för att schemalägga ett inlägg har en design som överensstämmer med resten av appen nu +Fullständig ändringslogg: https://github.com/tuskyapp/Tusky/releases diff --git a/fastlane/metadata/android/sv/changelogs/83.txt b/fastlane/metadata/android/sv/changelogs/83.txt new file mode 100644 index 000000000..4312a5dce --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/83.txt @@ -0,0 +1,3 @@ +Tusky v15.1 + +Den här versionen fixar en krasch vid bildtextning diff --git a/fastlane/metadata/android/sv/changelogs/87.txt b/fastlane/metadata/android/sv/changelogs/87.txt new file mode 100644 index 000000000..27ffb02b8 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/87.txt @@ -0,0 +1,8 @@ +Tusky v16.0 + +– Tidslinjens laddningslogik har skrivits om helt för att vara snabbare, mindre buggig och lättare att underhålla. +- Tusky kan nu animera anpassade emojis i APNG & Animated WebP-format. +- Många buggfixar +- Stöd för Android 11 +- Nya översättningar: skotsk gaeliska, galiciska, ukrainska +- Förbättrade översättningar diff --git a/fastlane/metadata/android/sv/changelogs/89.txt b/fastlane/metadata/android/sv/changelogs/89.txt new file mode 100644 index 000000000..6213dbccb --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/89.txt @@ -0,0 +1,7 @@ +Tusky v17.0 + +- "Öppna som..." finns nu även tillgängligt i menyn på kontoprofiler vid användning av flera konton +- Inloggning hanteras nu i en WebView i appen +- Stöd för Android 12 +- stöd för det nya Mastodon-instanskonfigurations-API:et +- och många andra småfixar och förbättringar diff --git a/fastlane/metadata/android/sv/changelogs/91.txt b/fastlane/metadata/android/sv/changelogs/91.txt new file mode 100644 index 000000000..71df4f7f9 --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/91.txt @@ -0,0 +1,6 @@ +Tusky v18.0 + +- Stöd för nya Mastodon 3.5-meddelandetyper +- Bot-märket ser nu bättre ut och anpassar sig till det valda temat +- Text kan nu väljas i inläggets detaljvy +- Fixade många buggar, inklusive en som förhindrade inloggningar på Android 6 och lägre diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dc4e43217..768f8bf5f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,7 +23,7 @@ androidx-room = "2.5.1" autodispose = "2.1.1" bouncycastle = "1.70" conscrypt = "2.5.2" -coroutines = "1.7.1" +coroutines = "1.7.2" dagger = "2.46.1" diffx = "1.1.1" emoji2 = "1.3.0" @@ -49,18 +49,18 @@ rxjava3 = "3.1.6" rxkotlin3 = "3.0.1" photoview = "2.3.0" sparkbutton = "4.1.0" -# Deliberate downgrade from 1.1.4, see https://github.com/google/truth/issues/1137 -truth = "1.1.3" -turbine = "0.13.0" +truth = "1.1.5" +turbine = "1.0.0" unified-push = "2.1.1" xmlwriter = "1.0.4" [plugins] android-application = { id = "com.android.application", version.ref = "agp" } +google-ksp = "com.google.devtools.ksp:1.8.22-1.0.11" kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } -ktlint = "org.jlleitschuh.gradle.ktlint:11.4.0" +ktlint = "org.jlleitschuh.gradle.ktlint:11.5.0" [libraries] android-material = { module = "com.google.android.material:material", version.ref = "material" }