Merge branch 'develop' into min_api_24

# Conflicts:
#	app/lint-baseline.xml
This commit is contained in:
Conny Duck 2023-07-05 20:46:20 +02:00
commit b08be1e1b0
97 changed files with 1274 additions and 517 deletions

View File

@ -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

View File

@ -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

View File

@ -828,7 +828,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="376"
line="379"
column="5"/>
</issue>
@ -839,7 +839,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="553"
line="558"
column="5"/>
</issue>
@ -850,7 +850,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="774"
line="778"
column="5"/>
</issue>
@ -2009,17 +2009,6 @@
column="5"/>
</issue>
<issue
id="Overdraw"
message="Possible overdraw: Root element paints background `?attr/windowBackgroundColor` with a theme that also paints a background (inferred theme is `@style/TuskyTheme`)"
errorLine1=" android:background=&quot;?attr/windowBackgroundColor&quot;>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout-sw640dp/fragment_timeline.xml"
line="7"
column="5"/>
</issue>
<issue
id="Overdraw"
message="Possible overdraw: Root element paints background `?android:attr/colorBackground` with a theme that also paints a background (inferred theme is `@style/TuskyTheme`)"
@ -2031,17 +2020,6 @@
column="5"/>
</issue>
<issue
id="Overdraw"
message="Possible overdraw: Root element paints background `?attr/windowBackgroundColor` with a theme that also paints a background (inferred theme is `@style/TuskyTheme`)"
errorLine1=" android:background=&quot;?attr/windowBackgroundColor&quot;>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout-sw640dp/fragment_timeline_notifications.xml"
line="24"
column="5"/>
</issue>
<issue
id="Overdraw"
message="Possible overdraw: Root element paints background `?android:attr/colorBackground` with a theme that also paints a background (inferred theme is `@style/TuskyTheme`)"
@ -2104,51 +2082,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/item_hashtag.xml"
line="6"
column="5"/>
</issue>
<issue
id="Overdraw"
message="Possible overdraw: Root element paints background `?android:colorBackground` with a theme that also paints a background (inferred theme is `@style/TuskyTheme`)"
errorLine1=" android:background=&quot;?android:colorBackground&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/item_status_bottom_sheet.xml"
line="8"
column="5"/>
</issue>
<issue
id="Overdraw"
message="Possible overdraw: Root element paints background `@color/dividerColorOther` with a theme that also paints a background (inferred theme is `@style/TuskyTheme`)"
errorLine1=" android:background=&quot;@color/dividerColorOther&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/item_status_placeholder.xml"
line="34"
column="5"/>
</issue>
<issue
id="Overdraw"
message="Possible overdraw: Root element paints background `?android:colorBackground` with a theme that also paints a background (inferred theme is `@style/TuskyTheme`)"
errorLine1=" android:background=&quot;?android:colorBackground&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/item_tab_preference.xml"
line="7"
column="5"/>
</issue>
<issue
id="Overdraw"
message="Possible overdraw: Root element paints background `?attr/selectableItemBackground` with a theme that also paints a background (inferred theme is `@style/TuskyTheme`)"
errorLine1=" android:background=&quot;?attr/selectableItemBackground&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/item_tab_preference_small.xml"
line="7"
line="16"
column="5"/>
</issue>
@ -2456,7 +2390,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="387"
line="392"
column="13"/>
</issue>
@ -2467,7 +2401,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="446"
line="451"
column="13"/>
</issue>
@ -2478,7 +2412,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="498"
line="503"
column="13"/>
</issue>
@ -2489,7 +2423,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="502"
line="507"
column="13"/>
</issue>
@ -2500,7 +2434,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="503"
line="508"
column="13"/>
</issue>
@ -2511,7 +2445,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="504"
line="509"
column="13"/>
</issue>
@ -2522,7 +2456,7 @@
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="505"
line="510"
column="13"/>
</issue>
@ -2533,7 +2467,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="506"
line="511"
column="13"/>
</issue>
@ -2544,7 +2478,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="507"
line="512"
column="13"/>
</issue>
@ -2555,7 +2489,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="508"
line="513"
column="13"/>
</issue>
@ -2566,7 +2500,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="509"
line="514"
column="13"/>
</issue>
@ -2577,7 +2511,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="511"
line="516"
column="13"/>
</issue>
@ -2588,7 +2522,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="596"
line="601"
column="13"/>
</issue>
@ -2599,7 +2533,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="597"
line="602"
column="13"/>
</issue>
@ -2610,7 +2544,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="611"
line="616"
column="13"/>
</issue>
@ -2621,7 +2555,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="613"
line="618"
column="13"/>
</issue>
@ -2632,7 +2566,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="614"
line="619"
column="13"/>
</issue>
@ -2643,7 +2577,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="617"
line="622"
column="13"/>
</issue>
@ -2654,7 +2588,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="662"
line="667"
column="13"/>
</issue>
@ -2665,7 +2599,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="711"
line="716"
column="13"/>
</issue>
@ -2676,7 +2610,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="717"
line="722"
column="13"/>
</issue>
@ -2687,7 +2621,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="758"
line="763"
column="13"/>
</issue>
@ -2698,7 +2632,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="795"
line="799"
column="13"/>
</issue>
@ -2918,7 +2852,7 @@
errorLine2=" ~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt"
line="117"
line="116"
column="17"/>
</issue>
@ -2929,7 +2863,7 @@
errorLine2=" ~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt"
line="124"
line="123"
column="21"/>
</issue>
@ -2940,7 +2874,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt"
line="195"
line="181"
column="17"/>
</issue>
@ -2951,7 +2885,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt"
line="205"
line="191"
column="128"/>
</issue>
@ -2962,7 +2896,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt"
line="205"
line="191"
column="128"/>
</issue>
@ -2973,7 +2907,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt"
line="207"
line="193"
column="71"/>
</issue>
@ -2984,7 +2918,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt"
line="207"
line="193"
column="63"/>
</issue>
@ -2995,7 +2929,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt"
line="232"
line="218"
column="21"/>
</issue>
@ -3006,7 +2940,7 @@
errorLine2=" ~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt"
line="234"
line="220"
column="21"/>
</issue>
@ -3017,7 +2951,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt"
line="244"
line="230"
column="128"/>
</issue>
@ -3028,7 +2962,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt"
line="244"
line="230"
column="128"/>
</issue>
@ -3039,7 +2973,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt"
line="246"
line="232"
column="71"/>
</issue>
@ -3050,7 +2984,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt"
line="246"
line="232"
column="63"/>
</issue>
@ -3160,7 +3094,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt"
line="163"
line="153"
column="21"/>
</issue>
@ -3171,7 +3105,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt"
line="165"
line="155"
column="29"/>
</issue>
@ -3226,7 +3160,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsActivity.kt"
line="92"
line="91"
column="35"/>
</issue>
@ -3237,7 +3171,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsActivity.kt"
line="93"
line="92"
column="25"/>
</issue>
@ -3248,7 +3182,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsActivity.kt"
line="94"
line="93"
column="43"/>
</issue>
@ -3259,7 +3193,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsActivity.kt"
line="95"
line="94"
column="25"/>
</issue>
@ -3270,7 +3204,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsActivity.kt"
line="207"
line="202"
column="21"/>
</issue>
@ -3281,7 +3215,7 @@
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/instancemute/fragment/InstanceListFragment.kt"
line="57"
line="56"
column="21"/>
</issue>
@ -3545,7 +3479,7 @@
errorLine2=" ~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/account/list/ListsForAccountFragment.kt"
line="188"
line="178"
column="21"/>
</issue>
@ -3556,7 +3490,7 @@
errorLine2=" ~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/account/list/ListsForAccountFragment.kt"
line="194"
line="184"
column="21"/>
</issue>
@ -4447,7 +4381,7 @@
errorLine2=" ~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt"
line="89"
line="90"
column="13"/>
</issue>
@ -4458,7 +4392,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt"
line="135"
line="136"
column="17"/>
</issue>
@ -4469,7 +4403,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt"
line="145"
line="146"
column="55"/>
</issue>
@ -4480,7 +4414,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt"
line="145"
line="146"
column="55"/>
</issue>
@ -4491,7 +4425,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt"
line="266"
line="256"
column="21"/>
</issue>
@ -4502,7 +4436,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt"
line="269"
line="259"
column="33"/>
</issue>
@ -4513,7 +4447,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt"
line="271"
line="261"
column="33"/>
</issue>
@ -4524,7 +4458,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt"
line="277"
line="267"
column="21"/>
</issue>
@ -5052,7 +4986,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="744"
line="749"
column="55"/>
</issue>
@ -5569,7 +5503,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout/item_followed_hashtag.xml"
line="27"
line="14"
column="6"/>
</issue>
@ -5580,7 +5514,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout/item_list.xml"
line="9"
line="14"
column="6"/>
</issue>
@ -6086,7 +6020,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout/item_tab_preference.xml"
line="24"
line="26"
column="6"/>
</issue>
@ -6174,7 +6108,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout-land/item_trending_cell.xml"
line="95"
line="94"
column="10"/>
</issue>
@ -6185,7 +6119,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout/item_trending_cell.xml"
line="95"
line="94"
column="10"/>
</issue>
@ -6196,7 +6130,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout-land/item_trending_cell.xml"
line="126"
line="125"
column="10"/>
</issue>
@ -6207,7 +6141,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout/item_trending_cell.xml"
line="127"
line="126"
column="10"/>
</issue>
@ -6394,7 +6328,7 @@
errorLine2=" ~~~~~~~~~">
<location
file="src/main/res/layout/activity_account.xml"
line="469"
line="468"
column="10"/>
</issue>
@ -6449,7 +6383,7 @@
errorLine2=" ~~~~~~~~~">
<location
file="src/main/res/layout/item_tab_preference.xml"
line="13"
line="14"
column="6"/>
</issue>
@ -6768,7 +6702,7 @@
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/BaseActivity.java"
line="59"
line="63"
column="12"/>
</issue>
@ -6779,7 +6713,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/BaseActivity.java"
line="123"
line="165"
column="51"/>
</issue>
@ -6790,7 +6724,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/BaseActivity.java"
line="129"
line="171"
column="42"/>
</issue>
@ -6801,7 +6735,7 @@
errorLine2=" ~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/BaseActivity.java"
line="157"
line="199"
column="36"/>
</issue>
@ -6812,7 +6746,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/BaseActivity.java"
line="157"
line="199"
column="105"/>
</issue>
@ -6823,7 +6757,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/BaseActivity.java"
line="165"
line="207"
column="42"/>
</issue>
@ -6834,7 +6768,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/BaseActivity.java"
line="165"
line="207"
column="95"/>
</issue>
@ -6845,7 +6779,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/BaseActivity.java"
line="233"
line="275"
column="36"/>
</issue>
@ -6856,7 +6790,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/BaseActivity.java"
line="233"
line="275"
column="58"/>
</issue>
@ -6867,7 +6801,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java"
line="152"
line="159"
column="55"/>
</issue>
@ -6878,7 +6812,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java"
line="152"
line="159"
column="72"/>
</issue>
@ -6889,7 +6823,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java"
line="152"
line="159"
column="113"/>
</issue>
@ -6900,7 +6834,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java"
line="152"
line="159"
column="132"/>
</issue>
@ -6911,7 +6845,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java"
line="273"
line="280"
column="51"/>
</issue>
@ -6922,7 +6856,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java"
line="273"
line="280"
column="68"/>
</issue>
@ -6933,7 +6867,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java"
line="273"
line="280"
column="109"/>
</issue>
@ -6944,7 +6878,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java"
line="576"
line="626"
column="48"/>
</issue>
@ -6955,7 +6889,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java"
line="604"
line="654"
column="49"/>
</issue>
@ -6966,7 +6900,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java"
line="620"
line="670"
column="46"/>
</issue>
@ -6977,7 +6911,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java"
line="620"
line="670"
column="87"/>
</issue>

View File

@ -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<TimelineAccount, Boolean>
@ -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) {

View File

@ -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;
}

View File

@ -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<MastoList>(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)

View File

@ -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()

View File

@ -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 <http://www.gnu.org/licenses>. */
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<MastoList>(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
}
}

View File

@ -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() }
}
}
}

View File

@ -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()

View File

@ -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)
}
}
}

View File

@ -89,7 +89,7 @@ data class ConversationStatusEntity(
val bookmarked: Boolean,
val sensitive: Boolean,
val spoilerText: String,
val attachments: ArrayList<Attachment>,
val attachments: List<Attachment>,
val mentions: List<Status.Mention>,
val tags: List<HashTag>?,
val showingHiddenContent: Boolean,

View File

@ -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()

View File

@ -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()

View File

@ -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)
}
}
}

View File

@ -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(

View File

@ -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) {

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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<TimelineAccount>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.searchRecyclerView.addItemDecoration(
DividerItemDecoration(
binding.searchRecyclerView.context,
DividerItemDecoration.VERTICAL
)
)
}
override fun createAdapter(): PagingDataAdapter<TimelineAccount, *> {
val preferences = PreferenceManager.getDefaultSharedPreferences(binding.searchRecyclerView.context)

View File

@ -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<T : Any> :
}
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

View File

@ -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<HashTag>() {
override val data: Flow<PagingData<HashTag>>
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<HashTag, *> = SearchHashtagsAdapter(this)
companion object {

View File

@ -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()

View File

@ -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 }) {

View File

@ -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)
}
}
}
}

View File

@ -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<EditsUiState> = MutableStateFlow(EditsUiState.Initial)
val uiState: StateFlow<EditsUiState> = _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("<br>", "<br/>"))
var previousContent =
loader.load(sortedEdits[1].content.replace("<br>", "<br/>"))
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("<br>", "<br/>"))
var previousContent =
loader.load(sortedEdits[1].content.replace("<br>", "<br/>"))
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("<br>", "<br/>")
)
}
}
_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("<br>", "<br/>")
)
}
}
},
{ 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)
}
)
}
}
}

View File

@ -102,8 +102,8 @@ class Converters @Inject constructor(
}
@TypeConverter
fun jsonToAttachmentList(attachmentListJson: String?): ArrayList<Attachment>? {
return gson.fromJson(attachmentListJson, object : TypeToken<ArrayList<Attachment>>() {}.type)
fun jsonToAttachmentList(attachmentListJson: String?): List<Attachment>? {
return gson.fromJson(attachmentListJson, object : TypeToken<List<Attachment>>() {}.type)
}
@TypeConverter

View File

@ -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<Attachment>?,
@SerializedName("media_attachments") val attachments: List<Attachment>?,
val poll: Poll?,
@SerializedName("created_at") val createdAt: Date,
val language: String?

View File

@ -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<Attachment>,
@SerializedName("media_attachments") val attachments: List<Attachment>,
val mentions: List<Mention>,
val tags: List<HashTag>?,
val application: Application?,

View File

@ -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"
}

View File

@ -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 <A> 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 {

View File

@ -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)
}

View File

@ -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)

View File

@ -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"
}
}

View File

@ -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)
}
}

View File

@ -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

View File

@ -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" />

View File

@ -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" />
</com.google.android.material.appbar.AppBarLayout>

View File

@ -25,6 +25,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:visibility="gone"
/>
<ProgressBar
@ -33,6 +34,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:visibility="gone"
/>
<com.google.android.material.floatingactionbutton.FloatingActionButton

View File

@ -28,8 +28,7 @@
android:layout_height="wrap_content"
app:tabGravity="fill"
app:tabMaxWidth="0dp"
app:tabMode="fixed"
app:tabTextAppearance="@style/TuskyTabAppearance" />
app:tabMode="fixed" />
</com.google.android.material.appbar.AppBarLayout>

View File

@ -1,41 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="72dp"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:gravity="center_vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
>
<TextView
android:id="@+id/followed_tag"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
tools:text="hashtag" />
<ImageButton
android:id="@+id/followed_tag_unfollow"
style="@style/TuskyImageButton"
android:layout_width="32dp"
android:layout_height="32dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/action_unfollow"
android:padding="4dp"
app:srcCompat="@drawable/ic_person_remove_24dp"
/>
<TextView
android:id="@+id/followed_tag"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:gravity="center_vertical"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:textColorSecondary"
android:textSize="?attr/status_text_medium"
tools:text="#hashtag" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

View File

@ -1,7 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copied from android.R.layout.simple_list_item_1, because view binding does not work with
android.R.layout.* -->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:gravity="center_vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:background="?attr/selectableItemBackground"
android:textSize="?attr/status_text_medium" />
tools:ignore="SelectableText" />

View File

@ -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">
<TextView
android:id="@+id/list_name_textview"
android:layout_width="0dp"
android:layout_height="60dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?selectableItemBackground"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textSize="?attr/status_text_medium"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
tools:text="Example list" />
<ImageButton
android:id="@+id/editListButton"
style="@style/TuskyImageButton"
android:layout_width="36dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_width="32dp"
android:layout_height="32dp"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/action_more"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:src="@drawable/ic_more_horiz_24dp" />
</LinearLayout>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:drawablePadding="8dp"
android:textColor="@color/textColorSecondary"
android:textSize="?attr/status_text_medium" />

View File

@ -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">
<ImageView
android:id="@+id/imageView"
@ -19,7 +20,8 @@
android:paddingBottom="8dp"
android:src="@drawable/ic_drag_indicator_24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="@id/textView"/>
<TextView
android:id="@+id/textView"
@ -30,8 +32,7 @@
android:drawablePadding="12dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:textColor="?android:attr/textColorSecondary"
android:textSize="?attr/status_text_large"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
app:drawableTint="?android:attr/textColorSecondary"
app:layout_constraintBottom_toTopOf="@id/chipGroup"
app:layout_constraintEnd_toEndOf="parent"

View File

@ -11,8 +11,7 @@
android:lines="1"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:textColor="?android:attr/textColorSecondary"
android:textSize="?attr/status_text_large"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
app:drawableStartCompat="@drawable/ic_home_24dp"
app:drawableTint="?android:attr/textColorSecondary" />

View File

@ -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" />
<TextView
android:id="@+id/usageLabel"

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2018 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<!-- Layout used by SeekBarPreference for the seekbar widget style. Minimally adapted for use
with Slider instead of SeekBar. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:gravity="center_vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:clipChildren="false"
android:clipToPadding="false"
android:baselineAligned="false">
<include layout="@layout/image_frame"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:clipChildren="false"
android:clipToPadding="false">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1">
<TextView
android:id="@android:id/title"
android:labelFor="@id/slider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"
tools:ignore="LabelFor,SelectableText" />
<TextView
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignStart="@android:id/title"
android:layout_gravity="start"
android:textAlignment="viewStart"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="4"
style="@style/PreferenceSummaryTextStyle"
tools:ignore="SelectableText" />
</RelativeLayout>
<!-- Using UnPressableLinearLayout as a workaround to disable the pressed state propagation
to the children of this container layout. Otherwise, the animated pressed state will also
play for the thumb in the AbsSeekBar in addition to the preference's ripple background.
The background of the SeekBar is also set to null to disable the ripple background -->
<androidx.preference.UnPressableLinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingLeft="0dp"
android:paddingStart="0dp"
android:paddingRight="0dp"
android:paddingEnd="0dp"
android:clipChildren="false"
android:clipToPadding="false">
<com.google.android.material.button.MaterialButton
android:id="@+id/decrement"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
style="@style/Widget.Material3.Button.IconButton" />
<!-- The total height of the Seekbar widget's area should be 48dp - this allows for an
increased touch area so you do not need to exactly tap the thumb to move it. However,
setting the Seekbar height directly causes the thumb and seekbar to be misaligned on
API 22 and 23 - so instead we just set 15dp padding above and below, to account for the
18dp default height of the Seekbar thumb for a total of 48dp.
Note: we set 0dp padding at the start and end of this seekbar to allow it to properly
fit into the layout, but this means that there's no leeway on either side for touch
input - this might be something we should reconsider down the line. -->
<com.google.android.material.slider.Slider
android:id="@+id/slider"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/preference_seekbar_padding_horizontal"
android:paddingStart="@dimen/preference_seekbar_padding_horizontal"
android:paddingRight="@dimen/preference_seekbar_padding_horizontal"
android:paddingEnd="@dimen/preference_seekbar_padding_horizontal"
android:paddingTop="@dimen/preference_seekbar_padding_vertical"
android:paddingBottom="@dimen/preference_seekbar_padding_vertical"
android:background="@null"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/increment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
style="@style/Widget.Material3.Button.IconButton" />
</androidx.preference.UnPressableLinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -35,7 +35,7 @@
android:scaleType="centerInside"
android:src="@drawable/elephant_offline" />
<TextView
<com.keylesspalace.tusky.view.ClickableSpanTextView
android:id="@+id/messageTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -665,4 +665,8 @@
<string name="select_list_manage">مدیریت سیاهه‌ها</string>
<string name="error_list_load">خطا در بار کردن سیاهه‌ها</string>
<string name="load_newest_notifications">بار کردن جدیدترین آگاهی‌ها</string>
<string name="compose_delete_draft">حذف پیش‌نویس؟</string>
<string name="error_missing_edits">کارسازتان می‌داند که این فرسته ویرایش شده؛ ولی رونوشتی از ویرایش‌ها ندارد. پس نمی‌توانند نشانتان داده شوند.
\n
\nاین <a href="https://github.com/mastodon/mastodon/issues/25398">نکتهٔ ماستودون</a> را ببینید.</string>
</resources>

View File

@ -681,4 +681,9 @@
<string name="help_empty_home">Seo <b>loidhne-ama do dhachaigh</b>. 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.</string>
</resources>
<string name="compose_delete_draft">A bheil thu airson an dreachd a sguabadh às\?</string>
<string name="load_newest_notifications">Luchdaich na brathan as ùire</string>
<string name="error_missing_edits">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 <a href="https://github.com/mastodon/mastodon/issues/25398">duilgheadas Mastodon #25398</a>.</string>
</resources>

View File

@ -584,7 +584,7 @@
<string name="a11y_label_loading_thread">Cargando fío</string>
<string name="mute_notifications_switch">Acalar notificacións</string>
<string name="title_edits">Edicións</string>
<string name="status_edit_info">Editado por %1$s</string>
<string name="status_edit_info">Editada: %1$s</string>
<string name="status_created_info">Creado por %1$s</string>
<string name="action_discard">Desbotar cambios</string>
<string name="action_continue_edit">Continuar a edición</string>
@ -657,4 +657,9 @@
<string name="select_list_empty">Aínda non tes listas</string>
<string name="select_list_manage">Xestionar listas</string>
<string name="error_list_load">Erro ao cargar as listas</string>
</resources>
<string name="error_missing_edits">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 <a href="https://github.com/mastodon/mastodon/issues/25398">problema coñecido</a> en Mastodon.</string>
<string name="load_newest_notifications">Cargar as notificacións máis recentes</string>
<string name="compose_delete_draft">Eliminar borrador\?</string>
</resources>

View File

@ -401,4 +401,9 @@
</plurals>
<string name="error_compose_character_limit">पोस्ट बहुत लंबा है!</string>
<string name="error_failed_app_registration">उस सर्वर से प्रमाणित करने में विफल।</string>
</resources>
<string name="error_multimedia_size_limit">वीडियो और ऑडियो फ़ाइलों का आकार %s एमबी से अधिक नहीं हो सकता है।</string>
<string name="error_image_edit_failed">फोटो संपादित नहीं किया जा सका।</string>
<string name="error_media_upload_image_or_video">फोटो और वीडियो दोनों को एक ही पोस्ट से अटैच नहीं किया जा सकता है।</string>
<string name="error_loading_account_details">खाता विवरण लोड करने में विफल रहा</string>
<string name="error_could_not_load_login_page">लॉगिन पेज लोड नहीं किया जा सका।</string>
</resources>

View File

@ -621,6 +621,6 @@
<string name="select_list_empty">リストはまだありません</string>
<string name="status_filtered_show_anyway">とにかく表示する</string>
<string name="pref_title_account_filter_keywords">プロフィール</string>
<string name="title_public_trending_hashtags">トレンドハッシュタグ</string>
<string name="title_public_trending_hashtags">トレンドハッシュタグ</string>
<string name="pref_title_show_stat_inline">タイムラインに投稿の統計情報を表示する</string>
</resources>
</resources>

View File

@ -4,21 +4,21 @@
<string name="error_network">Er deed zich een netwerkfout voor! Controleer je verbinding en probeer opnieuw!</string>
<string name="error_empty">Dit mag niet leeg zijn.</string>
<string name="error_invalid_domain">Ongeldige domeinnaam ingevoerd</string>
<string name="error_failed_app_registration">Authenticatie met die server is mislukt.</string>
<string name="error_failed_app_registration">Authenticatie met die server is mislukt. Als het probleem blijft, probeer dan de browser login via het menu.</string>
<string name="error_no_web_browser_found">Kon geen bruikbare webbrowser vinden.</string>
<string name="error_authorization_unknown">Er deed zich een onbekende autorisatiefout voor.</string>
<string name="error_authorization_denied">Autorisatie werd geweigerd.</string>
<string name="error_retrieving_oauth_token">Kon geen inlogsleutel verkrijgen.</string>
<string name="error_authorization_unknown">Er deed zich een onbekende autorisatiefout voor. Als dit blijft, probeer dan “ Via de browser inloggen” in het menu.</string>
<string name="error_authorization_denied">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.</string>
<string name="error_retrieving_oauth_token">Kon geen inlogsleutel verkrijgen. Als het probleem zich blijft herhalen; probeer dan de browser login via menu.</string>
<string name="error_compose_character_limit">Tekst van dit bericht is te lang!</string>
<string name="error_media_upload_type">Bestandstype kan niet worden geüpload.</string>
<string name="error_media_upload_type">Bestandstype wordt niet ondersteund.</string>
<string name="error_media_upload_opening">Bestand kon niet worden geopend.</string>
<string name="error_media_upload_permission">Er is toestemming nodig om deze media te lezen.</string>
<string name="error_media_download_permission">Er is toestemming nodig om media op te slaan.</string>
<string name="error_media_upload_image_or_video">Afbeeldingen en video\'s kunnen niet allebei aan hetzelfde bericht worden toegevoegd.</string>
<string name="error_media_upload_image_or_video">Afbeeldingen en video\'s kunnen niet samen aan hetzelfde bericht worden toegevoegd.</string>
<string name="error_media_upload_sending">Uploaden mislukt.</string>
<string name="error_sender_account_gone">Fout tijdens verzenden bericht.</string>
<string name="title_home">Start</string>
<string name="title_notifications">Meldingen</string>
<string name="title_notifications">Notificaties</string>
<string name="title_public_local">Lokaal</string>
<string name="title_public_federated">Globaal</string>
<string name="title_direct_messages">Directe berichten</string>
@ -47,7 +47,7 @@
<string name="post_content_show_less">Inklappen</string>
<string name="message_empty">Hier is niets.</string>
<string name="footer_empty">Niets te zien. Swipe naar beneden om te verversen!</string>
<string name="notification_reblog_format">%s boostte jouw bericht</string>
<string name="notification_reblog_format">%s boostte jou bericht</string>
<string name="notification_favourite_format">%s markeerde jouw bericht als favoriet</string>
<string name="notification_follow_format">%s volgt jou</string>
<string name="report_username_format">Rapporteer @%s</string>
@ -60,8 +60,8 @@
<string name="action_unfavourite">Favoriet verwijderen</string>
<string name="action_more">Meer</string>
<string name="action_compose">Bericht schrijven</string>
<string name="action_login">Aanmelden met Mastodon</string>
<string name="action_logout">Afmelden</string>
<string name="action_login">Aanmelden met Tusky</string>
<string name="action_logout">Uitloggen</string>
<string name="action_logout_confirm">Ben je er zeker van dat je het account %1$s wil afmelden?</string>
<string name="action_follow">Volgen</string>
<string name="action_unfollow">Ontvolgen</string>
@ -464,7 +464,7 @@
<string name="account_note_hint">Jouw eigen opmerking over dit account</string>
<string name="pref_title_wellbeing_mode">Welzijn</string>
<string name="pref_title_hide_top_toolbar">De bovenste werkbalk verbergen</string>
<string name="pref_title_confirm_reblogs">Vraag voor het boosten van een bericht een bevestiging</string>
<string name="pref_title_confirm_reblogs">Vraag bevestiging voor het boosten</string>
<string name="pref_title_show_cards_in_timelines">Linkpreviews in tijdlijnen weergeven</string>
<string name="no_announcements">Er zijn geen aankondigingen.</string>
<string name="duration_indefinite">Oneindig</string>
@ -534,7 +534,7 @@
<string name="duration_90_days">90 dagen</string>
<string name="duration_180_days">180 dagen</string>
<string name="duration_365_days">365 dagen</string>
<string name="pref_title_confirm_favourites">Vraag voor het markeren als favoriet een bevestiging</string>
<string name="pref_title_confirm_favourites">Vraag bevestiging voor het markeren als favoriet</string>
<string name="tusky_compose_post_quicksetting_label">Bericht schrijven</string>
<string name="account_date_joined">Geregistreerd in %1$s</string>
<string name="saving_draft">Concept wordt opgeslagen…</string>
@ -590,4 +590,65 @@
<string name="account_username_copied">Gebruikersnaam gekopieerd</string>
<string name="confirmation_hashtag_unfollowed">#%s ontvolgd</string>
<string name="title_followed_hashtags">Gevolgde hashtags</string>
</resources>
<string name="dialog_follow_hashtag_title">Volg hashtag</string>
<string name="dialog_follow_hashtag_hint">#hashtag</string>
<string name="hint_description">Beschrijving</string>
<string name="action_post_failed">Upload mislukt</string>
<string name="action_post_failed_detail">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.</string>
<string name="action_post_failed_detail_plural">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.</string>
<string name="action_post_failed_do_nothing">Afwijzen</string>
<string name="action_post_failed_show_drafts">Concepten tonen</string>
<string name="error_unmuting_hashtag_format">Fout bij unmuting #%s</string>
<string name="action_browser_login">Inloggen met een Browser</string>
<string name="action_add_reaction">reactie toevoegen</string>
<string name="action_discard">Veranderingen ongedaan maken</string>
<string name="post_edited">Bewerkt %s</string>
<string name="notification_header_report_format">%s gerapporteerd %s</string>
<string name="notification_summary_report_format">%s · %d berichten bijgevoegd</string>
<string name="action_share_account_link">Link naar account delen</string>
<string name="action_share_account_username">Deel de gebruikersnaam van het account</string>
<string name="send_account_username_to">Accountgebruikersnaam delen om…</string>
<string name="send_account_link_to">Account-URL delen met…</string>
<string name="error_following_hashtags_unsupported">Deze instance ondersteunt niet het volgen van hashtags.</string>
<string name="error_status_source_load">Fout bij het laden van de statusbron van server.</string>
<string name="title_public_trending_hashtags">Trending hashtags</string>
<string name="pref_summary_http_proxy_missing">&lt;niet ingesteld&gt;</string>
<string name="pref_summary_http_proxy_invalid">&lt;ongeldig&gt;</string>
<string name="action_refresh">Ververs</string>
<string name="post_media_image">Afbeelding</string>
<string name="ui_error_reblog">Boosten bericht mislukt: %s</string>
<string name="ui_error_vote">Stemmen in peiling mislukt: %s</string>
<string name="pref_title_http_proxy_port_message">Poort moet liggen tussen %d en %d</string>
<string name="notification_unknown_name">Onbekend</string>
<string name="ui_error_clear_notifications">Wissen meldingen mislukt: %s</string>
<string name="ui_success_accepted_follow_request">Volgverzoek geaccepteerd</string>
<string name="error_list_load">Fout bij laden lijsten</string>
<string name="select_list_empty">Je hebt nog geen lijsten</string>
<string name="select_list_manage">Lijsten beheren</string>
<string name="pref_title_account_filter_keywords">Profielen</string>
<string name="status_filtered_show_anyway">Toch tonen</string>
<string name="status_filter_placeholder_label_format">Gefilterd: %s</string>
<string name="hint_filter_title">Mijn filter</string>
<string name="label_filter_action">Filteractie</string>
<string name="filter_action_warn">Waarschuwen</string>
<string name="filter_action_hide">Verbergen</string>
<string name="filter_description_warn">Verberg met een waarschuwing</string>
<string name="filter_description_hide">Verberg helemaal</string>
<string name="action_add">Toevoegen</string>
<string name="filter_keyword_display_format">%s (heel woord)</string>
<string name="filter_description_format">%s: %s</string>
<string name="accessibility_talking_about_tag">%1$d mensen bespreken hashtag %2$s</string>
<string name="total_usage">Totaal gebruik</string>
<string name="total_accounts">Totaal accounts</string>
<string name="ui_error_reject_follow_request">Afwijzen volgverzoek mislukt: %s</string>
<string name="pref_title_show_stat_inline">Toon bericht statistieken in tijdlijn</string>
<string name="ui_error_unknown">onbekende reden</string>
<string name="ui_error_accept_follow_request">Accepteren volgverzoek mislukt: %s</string>
<string name="label_filter_title">Titel</string>
<string name="load_newest_notifications">Laad nieuwste meldingen</string>
<string name="compose_delete_draft">Verwijder concept\?</string>
</resources>

View File

@ -666,4 +666,9 @@
<string name="help_empty_home">Aquò es vòstra <b>cronologia</b>. Mòstra las publicacions recentas dels comptes que seguissètz.
\n
\nPer explorar mai de compte podètz siá los descobrir dins dautres 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.</string>
</resources>
<string name="error_missing_edits">Vòstre servidor sap quaquesta 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 <a href="https://github.com/mastodon/mastodon/issues/25398">Mastodon #25398</a>.</string>
<string name="load_newest_notifications">Cargar las notificacions mai recentas</string>
<string name="compose_delete_draft">Suprimir lo borrolhon \?</string>
</resources>

View File

@ -646,4 +646,6 @@
<string name="select_list_manage">Quản lý danh sách</string>
<string name="error_list_load">Xảy ra lỗi khi tải danh sách</string>
<string name="select_list_empty">Bạn chưa có danh sách</string>
</resources>
<string name="load_newest_notifications">Tải những thông báo mới nhất</string>
<string name="compose_delete_draft">Xóa bản nháp\?</string>
</resources>

View File

@ -423,7 +423,7 @@
<string name="report_remote_instance">转发到 %s</string>
<string name="failed_report">举报失败</string>
<string name="failed_fetch_posts">无法获取嘟文</string>
<string name="report_description_1">该报告将发送给给您的服务器管理员。您可以在下面提供有关回报此账户的原因的说明:</string>
<string name="report_description_1">举报将发送给你所在服务器的管理员。你可以在下面提供举报此账户的相关说明:</string>
<string name="report_description_remote_instance">该账户来自其他服务器。向那里发送一份匿名的报告副本?</string>
<string name="title_accounts">账户</string>
<string name="failed_search">搜索失败</string>
@ -441,7 +441,7 @@
<string name="poll_new_choice_hint">选择 %d</string>
<string name="edit_poll">编辑</string>
<string name="post_lookup_error_format">查找嘟文时出错 %s</string>
<string name="no_drafts">您没有草稿。</string>
<string name="no_drafts">你没有草稿</string>
<string name="no_scheduled_posts">您没有任何定时嘟文。</string>
<string name="warning_scheduling_interval">Mastodon的最小预订时间为5分钟。</string>
<string name="notification_follow_request_name">关注请求</string>
@ -486,10 +486,10 @@
<string name="review_notifications">反馈通知</string>
<string name="wellbeing_hide_stats_posts">隐藏嘟文的统计信息</string>
<string name="limit_notifications">限制时间线通知</string>
<string name="wellbeing_mode_notice">一些可能影响您精神状态的信息将被隐藏,这些信息包括:
<string name="wellbeing_mode_notice">一些可能影响你精神状态的信息将被隐藏,包括:
\n
\n - 喜欢、转发、关注通知
\n - 喜欢、转发数
\n - 嘟文的喜欢、转发数
\n - 账号的已关注数量、嘟文数量
\n
\n 推送通知不会被影响,但可以在通知设置中手动禁用。</string>
@ -506,7 +506,7 @@
<string name="pref_title_animate_custom_emojis">显示动态自定义Emoji</string>
<string name="pref_title_notification_filter_subscriptions">关注的人发布了新嘟文</string>
<string name="notification_subscription_format">%s 刚刚发送了新嘟文</string>
<string name="follow_requests_info">即使您的账号未上锁,管理员 %1$s 认为您可能需要手动处理来自这些账号的关注请求。</string>
<string name="follow_requests_info">即使你的账号未上锁,但 %1$s 的管理员认为你可能需要手动处理这些账号的关注请求。</string>
<string name="dialog_delete_conversation_warning">删除此对话吗?</string>
<string name="action_delete_conversation">删除对话</string>
<string name="pref_title_confirm_favourites">收藏前提示确认</string>
@ -556,7 +556,7 @@
<string name="delete_scheduled_post_warning">删除这条定时嘟文吗?</string>
<string name="instance_rule_info">登录即表示您同意 %s 的规定。</string>
<string name="instance_rule_title">%s 的规定</string>
<string name="compose_save_draft_loses_media">保存草稿?(当您恢复草稿时附件将被再次上传</string>
<string name="compose_save_draft_loses_media">保存草稿?(恢复草稿时附件将被再次上传)</string>
<string name="failed_to_pin">固定失败</string>
<string name="failed_to_unpin">取消固定失败</string>
<string name="action_add_reaction">添加回应</string>
@ -665,4 +665,8 @@
<string name="error_list_load">加载列表出错</string>
<string name="select_list_empty">你还没有列表</string>
<string name="load_newest_notifications">加载最新通知</string>
<string name="compose_delete_draft">删除草稿?</string>
<string name="error_missing_edits">你的服务器知晓这篇帖子被编辑,但没有编辑的副本,所以无法呈现给你。
\n
\n这是一个 <a href="https://github.com/mastodon/mastodon/issues/25398">Mastodon issue #25398</a></string>
</resources>

View File

@ -22,6 +22,16 @@
<attr name="proportionalTrending" format="boolean" />
</declare-styleable>
<declare-styleable name="SliderPreference">
<attr name="android:value" format="string|reference" />
<attr name="android:valueFrom" format="string|reference" />
<attr name="android:valueTo" format="string|reference" />
<attr name="android:stepSize" format="string|reference" />
<attr name="format" format="string|reference" />
<attr name="iconStart" format="reference" />
<attr name="iconEnd" format="reference" />
</declare-styleable>
<!--Themed Attributes-->
<attr name="colorBackgroundAccent" format="reference|color" />
<attr name="colorBackgroundHighlight" format="reference|color" />

View File

@ -18,7 +18,7 @@
<resources>
<string name="error_generic">An error occurred.</string>
<string name="error_network">A network error occurred! Please check your connection and try again!</string>
<string name="error_network">A network error occurred. Please check your connection and try again.</string>
<string name="error_empty">This cannot be empty.</string>
<string name="error_invalid_domain">Invalid domain entered</string>
<string name="error_failed_app_registration">Failed authenticating with that instance. If this persists, try "Login in Browser" from the menu.</string>
@ -338,6 +338,7 @@
<string name="post_privacy_unlisted">Unlisted</string>
<string name="post_privacy_followers_only">Followers-only</string>
<string name="pref_ui_text_size">UI text size</string>
<string name="pref_post_text_size">Post text size</string>
<string name="post_text_size_smallest">Smallest</string>
@ -370,6 +371,8 @@
<string name="notification_update_description">Notifications when posts you\'ve interacted with are edited</string>
<string name="notification_report_name">Reports</string>
<string name="notification_report_description">Notifications about moderation reports</string>
<string name="notification_listenable_worker_name">Background activity</string>
<string name="notification_listenable_worker_description">Notifications when Tusky is working in the background</string>
<string name="notification_unknown_name">Unknown</string>
<string name="notification_mention_format">%s mentioned you</string>
@ -380,6 +383,8 @@
<item quantity="one">%d new interaction</item>
<item quantity="other">%d new interactions</item>
</plurals>
<string name="notification_notification_worker">Fetching notifications…</string>
<string name="notification_prune_cache">Cache maintenance…</string>
<string name="description_account_locked">Locked Account</string>
@ -764,10 +769,9 @@
<string name="pref_reading_order_oldest_first">Oldest first</string>
<string name="pref_reading_order_newest_first">Newest first</string>
<!--@Tusky edited 19th December 2022 13:37 -->
<string name="status_edit_info">Edited: %1$s</string>
<!--@Tusky created 19th December 2022 13:12 -->
<string name="status_created_info">Created: %1$s</string>
<string name="error_missing_edits">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 <a href="https://github.com/mastodon/mastodon/issues/25398">Mastodon issue #25398</a>.</string>
<string name="a11y_label_loading_thread">Loading thread</string>
<!--@knossos@fosstodon.org created 2023-01-07 -->

View File

@ -106,7 +106,7 @@
</style>
<style name="TuskyTabAppearance" parent="Widget.MaterialComponents.TabLayout">
<item name="android:textSize">?attr/status_text_medium</item>
<item name="tabTextAppearance">?android:attr/textAppearanceButton</item>
<item name="android:textAllCaps">true</item>
<item name="tabIndicatorHeight">3dp</item>
</style>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,15 @@
تاسکی ۲۳٫۰ بتا
ویژگی‌های جدید:
- ترجیح جدید برای مقیاس متن میانای کاربری
رفع اشکال‌ها:
- ذخیرهٔ درست اطّلاعات حساب
- «گرفتن» آگاهی‌ها روی افزاره‌های با نگارش اندروید کم‌تر از ۱۱
- دور زدن مشکل امکان فراموشی توانایی رونوشت و جای‌گذاری در زمینه‌های متنی اندروید
- تجاوز نکردن از لیهٔ صفحه هنگام دیدن «تفاوت‌ها» در تاریخچهٔ ویرایش
- فرونپاشیدن در صورت نبودن تاریخچهٔ ویرایش فرسته روی کارساز
- افزودن دکمهٔ «حذف» هنگام ویرایش یک پالایه
- نمایش درست اموجی‌های نامربّعی

View File

@ -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. برای فعّال کردن این پشتیبانی باید دوباره به حساب‌هایتان وارد شوید.
- اکنون تعداد پاسخ‌ها به فرسته‌ها در خط‌های زمانی نشان داده می‌شود.
- اکنون تصویرها می‌توانند هنگام ایجاد فرسته بریده شوند.
- اکنون نمایه‌ها تاریخ ایجادشان را نشان می‌دهند.
- اکنون عنوان سیاهه هنگام دیدنش در نوار ابزار نمایش داده می‌شود.
- یک عالمه رفع اشکال
- بهبودهای ترجمه

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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..

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
Tusky 22.0 beta 4
Fixar:
- Fixade upprepad hämtning av meddelanden om konfigurerad med flera konton

View File

@ -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

View File

@ -0,0 +1,5 @@
Tusky 22.0 beta 6
Fixar:
- Spara läsposition på Aviseringar fliken oftare

View File

@ -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

View File

@ -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...

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,3 @@
Tusky v15.1
Den här versionen fixar en krasch vid bildtextning

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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" }