diff --git a/app/build.gradle b/app/build.gradle index 6b2efbcc7..9a61d7e97 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -104,11 +104,11 @@ project.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { ext.lifecycleVersion = "2.2.0" ext.roomVersion = '2.2.5' -ext.retrofitVersion = '2.8.1' -ext.okhttpVersion = '4.5.0' +ext.retrofitVersion = '2.9.0' +ext.okhttpVersion = '4.7.2' ext.glideVersion = '4.11.0' ext.daggerVersion = '2.27' -ext.materialdrawerVersion = '8.0.1' +ext.materialdrawerVersion = '8.0.3' repositories { maven { @@ -120,8 +120,8 @@ repositories { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation "androidx.core:core-ktx:1.2.0" - implementation "androidx.appcompat:appcompat:1.2.0-beta01" + implementation "androidx.core:core-ktx:1.3.0" + implementation "androidx.appcompat:appcompat:1.2.0-rc01" implementation "androidx.fragment:fragment-ktx:1.2.4" implementation "androidx.browser:browser:1.2.0" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" @@ -129,7 +129,7 @@ dependencies { implementation "androidx.exifinterface:exifinterface:1.2.0" implementation "androidx.cardview:cardview:1.0.0" implementation "androidx.preference:preference:1.1.1" - implementation "androidx.sharetarget:sharetarget:1.0.0-rc01" + implementation "androidx.sharetarget:sharetarget:1.0.0" implementation "androidx.emoji:emoji:1.0.0" implementation "androidx.emoji:emoji-appcompat:1.0.0" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion" @@ -157,7 +157,7 @@ dependencies { implementation "com.github.bumptech.glide:glide:$glideVersion" implementation "com.github.bumptech.glide:okhttp3-integration:$glideVersion" - implementation "io.reactivex.rxjava2:rxjava:2.2.17" + implementation "io.reactivex.rxjava2:rxjava:2.2.19" implementation "io.reactivex.rxjava2:rxandroid:2.1.1" implementation "io.reactivex.rxjava2:rxkotlin:2.4.0" diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 2d554c992..e25a4564e 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -17,12 +17,12 @@ # keep setters in Views so that animations can still work. # see http://proguard.sourceforge.net/manual/examples.html#beans -keepclassmembers public class * extends android.view.View { - void set*(***); - *** get*(); + void set*(***); + *** get*(); } # We want to keep methods in Activity that could be used in the XML attribute onClick -keepclassmembers class * extends android.app.Activity { - public void *(android.view.View); + public void *(android.view.View); } # For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations -keepclassmembers enum * { @@ -30,43 +30,19 @@ public static ** valueOf(java.lang.String); } -keepclassmembers class * implements android.os.Parcelable { - public static final android.os.Parcelable$Creator CREATOR; -} --keepclassmembers class **.R$* { - public static ; + public static final ** CREATOR; } # TUSKY SPECIFIC OPTIONS -## for okhttp --dontwarn javax.annotation.** --keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase --dontwarn org.codehaus.mojo.animal_sniffer.* --dontwarn okhttp3.internal.platform.ConscryptPlatform - -##for keep --dontwarn android.arch.util.paging.CountedDataSource --dontwarn android.arch.persistence.room.paging.LimitOffsetDataSource - -## for retrofit --dontwarn retrofit2.** --keep class retrofit2.** { *; } --keepattributes Signature --keepattributes Exceptions --keepattributes *Annotation* - --keepclasseswithmembers class * { - @retrofit2.http.* ; -} - --keep class com.keylesspalace.tusky.entity.** { *; } +# keep members of our model classes, they are used in json de/serialization +-keepclassmembers class com.keylesspalace.tusky.entity.* { *; } -keep public enum com.keylesspalace.tusky.entity.*$** { **[] $VALUES; public *; } - # preserve line numbers for crash reporting -keepattributes SourceFile,LineNumberTable -renamesourcefileattribute SourceFile @@ -87,17 +63,7 @@ static void throwUninitializedPropertyAccessException(java.lang.String); } --dontwarn com.google.errorprone.annotations.* - # without this emoji font downloading fails with AbstractMethodError -keep class * extends android.os.AsyncTask { public *; } - -# Glide --keep public class * implements com.bumptech.glide.module.GlideModule --keep public class * extends com.bumptech.glide.module.AppGlideModule --keep public enum com.bumptech.glide.load.ImageHeaderParser$** { - **[] $VALUES; - public *; -} diff --git a/app/src/main/java/com/keylesspalace/tusky/EmojiPreference.java b/app/src/main/java/com/keylesspalace/tusky/EmojiPreference.java index cdf09002a..8837c7d8d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EmojiPreference.java +++ b/app/src/main/java/com/keylesspalace/tusky/EmojiPreference.java @@ -43,6 +43,18 @@ public class EmojiPreference extends Preference { private boolean updated, currentNeedsUpdate; + public EmojiPreference(Context context) { + super(context); + + // Find out which font is currently active + this.selected = EmojiCompatFont.byId(PreferenceManager + .getDefaultSharedPreferences(context) + .getInt(FONT_PREFERENCE, 0)); + // We'll use this later to determine if anything has changed + this.original = this.selected; + + setSummary(selected.getDisplay(context)); + } public EmojiPreference(Context context, AttributeSet attrs) { super(context, attrs); diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt b/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt index 356e44665..a9e8e6c03 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt @@ -40,6 +40,7 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.view.menu.MenuBuilder import androidx.appcompat.widget.PopupMenu import androidx.constraintlayout.widget.ConstraintLayout +import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.content.ContextCompat import androidx.core.content.pm.ShortcutManagerCompat import androidx.emoji.text.EmojiCompat @@ -70,7 +71,10 @@ import com.keylesspalace.tusky.interfaces.ActionButtonActivity import com.keylesspalace.tusky.interfaces.ReselectableFragment import com.keylesspalace.tusky.pager.MainPagerAdapter import com.keylesspalace.tusky.util.* +import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial +import com.mikepenz.iconics.utils.colorInt +import com.mikepenz.iconics.utils.sizeDp import com.mikepenz.materialdrawer.iconics.iconicsIcon import com.mikepenz.materialdrawer.model.* import com.mikepenz.materialdrawer.model.interfaces.* @@ -107,8 +111,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje private var streamingTabsCount = 0 private var notificationTabPosition = 0 - - private var adapter: MainPagerAdapter? = null + private var onTabSelectedListener: OnTabSelectedListener? = null private val emojiInitCallback = object : InitCallback() { override fun onInitialized() { @@ -178,7 +181,18 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje composeButton.setOnClickListener { quickTootHelper.composeButton() } - tabLayout.requestFocus() + + mainToolbar.menu.add(R.string.action_search).apply { + setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) + icon = IconicsDrawable(this@MainActivity, GoogleMaterial.Icon.gmd_search).apply { + sizeDp = 20 + colorInt = ThemeUtils.getColor(this@MainActivity, android.R.attr.textColorPrimary) + } + setOnMenuItemClickListener { + startActivity(SearchActivity.getIntent(this@MainActivity)) + true + } + } setupDrawer(savedInstanceState) @@ -186,7 +200,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje * drawer, though, because its callback touches the header in the drawer. */ fetchUserInfo() - val popups = setupTabs(showNotificationTab) + setupTabs(showNotificationTab) val pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin) viewPager.setPageTransformer(MarginPageTransformer(pageMargin)) @@ -199,20 +213,6 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje viewPager.offscreenPageLimit = 9 } - tabLayout.addOnTabSelectedListener(object : OnTabSelectedListener { - override fun onTabSelected(tab: TabLayout.Tab) { - if (tab.position == notificationTabPosition) { - NotificationHelper.clearNotificationsForActiveAccount(this@MainActivity, accountManager) - } - } - - override fun onTabUnselected(tab: TabLayout.Tab) {} - - override fun onTabReselected(tab: TabLayout.Tab) { - popups[tab.position].show() - } - }) - // Setup push notifications if (NotificationHelper.areNotificationsEnabled(this, accountManager)) { NotificationHelper.enablePullNotifications(this) @@ -417,13 +417,6 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje startActivityWithSlideInAnimation(ListsActivity.newIntent(context)) } }, - primaryDrawerItem { - nameRes = R.string.action_search - iconicsIcon = GoogleMaterial.Icon.gmd_search - onClick = { - startActivityWithSlideInAnimation(SearchActivity.getIntent(context)) - } - }, primaryDrawerItem { nameRes = R.string.action_access_saved_toot iconRes = R.drawable.ic_notebook @@ -530,21 +523,37 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje } private fun setupTabs(selectNotificationTab: Boolean): ArrayList { + val preferences = PreferenceManager.getDefaultSharedPreferences(this) + + val activeTabLayout = if(preferences.getString("mainNavPosition", "top") == "bottom") { + val actionBarSize = ThemeUtils.getDimension(this, R.attr.actionBarSize) + val fabMargin = resources.getDimensionPixelSize(R.dimen.fabMargin) + (composeButton.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = actionBarSize + fabMargin + tabLayout.hide() + bottomTabLayout + } else { + bottomNav.hide() + (viewPager.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = 0 + (composeButton.layoutParams as CoordinatorLayout.LayoutParams).anchorId = R.id.viewPager + tabLayout + } + val tabs = accountManager.activeAccount!!.tabPreferences.toMutableList() - adapter = MainPagerAdapter(tabs, this) + + val adapter = MainPagerAdapter(tabs, this) viewPager.adapter = adapter - TabLayoutMediator(tabLayout, viewPager, TabConfigurationStrategy { _: TabLayout.Tab?, _: Int -> }).attach() - tabLayout.removeAllTabs() + TabLayoutMediator(activeTabLayout, viewPager, TabConfigurationStrategy { _: TabLayout.Tab?, _: Int -> }).attach() + activeTabLayout.removeAllTabs() val popups = ArrayList() for (i in tabs.indices) { - val tab = tabLayout.newTab() + val tab = activeTabLayout.newTab() .setIcon(tabs[i].icon) if (tabs[i].id == LIST) { tab.contentDescription = tabs[i].arguments[1] } else { tab.setContentDescription(tabs[i].text) } - tabLayout.addTab(tab) + activeTabLayout.addTab(tab) val popup = PopupMenu(this, tab.view) popup.menuInflater.inflate(R.menu.view_tab_action, popup.menu) @@ -584,7 +593,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje } popup.setOnMenuItemClickListener { item -> - val fragment = adapter?.getFragment(tab.position) + val fragment = adapter.getFragment(tab.position) when (item.itemId) { R.id.tabJumpToTop -> { if (fragment is ReselectableFragment) { @@ -647,6 +656,37 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje streamingTabsCount++ } } + + val pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin) + viewPager.setPageTransformer(MarginPageTransformer(pageMargin)) + + val uswSwipeForTabs = preferences.getBoolean("enableSwipeForTabs", true) + viewPager.isUserInputEnabled = uswSwipeForTabs + + onTabSelectedListener?.let { + activeTabLayout.removeOnTabSelectedListener(it) + } + + onTabSelectedListener = object : OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + if (tab.position == notificationTabPosition) { + NotificationHelper.clearNotificationsForActiveAccount(this@MainActivity, accountManager) + } + + mainToolbar.title = tabs[tab.position].title(this@MainActivity) + } + + override fun onTabUnselected(tab: TabLayout.Tab) {} + + override fun onTabReselected(tab: TabLayout.Tab) { + popups[tab.position].show() + } + }.also { + activeTabLayout.addOnTabSelectedListener(it) + } + + mainToolbar.title = tabs[0].title(this@MainActivity) + keepScreenOn() return popups } @@ -820,4 +860,4 @@ private var AbstractDrawerItem<*, *>.onClick: () -> Unit value() false } - } \ No newline at end of file + } diff --git a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt index 6599a3872..388ea44ca 100644 --- a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt @@ -129,7 +129,7 @@ class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreference } "statusTextSize", "absoluteTimeView", "showBotOverlay", "animateGifAvatars", - "useBlurhash", "showCardsInTimelines", "confirmReblogs", "enableSwipeForTabs", "viewPagerOffScreenLimit" -> { + "useBlurhash", "showCardsInTimelines", "confirmReblogs", "enableSwipeForTabs", "mainNavPosition", "viewPagerOffScreenLimit" -> { restartActivitiesOnExit = true } "language" -> { diff --git a/app/src/main/java/com/keylesspalace/tusky/TabData.kt b/app/src/main/java/com/keylesspalace/tusky/TabData.kt index e6a2a2088..6080035e1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TabData.kt +++ b/app/src/main/java/com/keylesspalace/tusky/TabData.kt @@ -15,6 +15,7 @@ package com.keylesspalace.tusky +import android.content.Context import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.fragment.app.Fragment @@ -39,18 +40,62 @@ data class TabData(val id: String, @DrawableRes val icon: Int, val fragment: (List) -> Fragment, val arguments: List = emptyList(), + val title: (Context) -> String = { context -> context.getString(text)}, val enableStreaming: Boolean = false) fun createTabDataFromId(id: String, arguments: List = emptyList()): TabData { val enableStreaming = id.endsWith(STREAMING) return when (if (enableStreaming) id.slice(IntRange(0, id.length - 4)) else id) { - HOME -> TabData(HOME, R.string.title_home, R.drawable.ic_home_24dp, { TimelineFragment.newInstance(TimelineFragment.Kind.HOME, enableStreaming) }, enableStreaming = enableStreaming) - NOTIFICATIONS -> TabData(NOTIFICATIONS, R.string.title_notifications, R.drawable.ic_notifications_24dp, { NotificationsFragment.newInstance() }) - LOCAL -> TabData(LOCAL, R.string.title_public_local, R.drawable.ic_local_24dp, { TimelineFragment.newInstance(TimelineFragment.Kind.PUBLIC_LOCAL, enableStreaming) }, enableStreaming = enableStreaming) - FEDERATED -> TabData(FEDERATED, R.string.title_public_federated, R.drawable.ic_public_24dp, { TimelineFragment.newInstance(TimelineFragment.Kind.PUBLIC_FEDERATED, enableStreaming) }, enableStreaming = enableStreaming) - DIRECT -> TabData(DIRECT, R.string.title_direct_messages, R.drawable.ic_reblog_direct_24dp, { ConversationsFragment.newInstance() }) - HASHTAG -> TabData(HASHTAG, R.string.hashtags, R.drawable.ic_hashtag, { args -> TimelineFragment.newHashtagInstance(args) }, arguments) - LIST -> TabData(LIST, R.string.list, R.drawable.ic_list, { args -> TimelineFragment.newInstance(TimelineFragment.Kind.LIST, args.getOrNull(0).orEmpty(), true, enableStreaming) }, arguments, enableStreaming) + HOME -> TabData( + HOME, + R.string.title_home, + R.drawable.ic_home_24dp, + { TimelineFragment.newInstance(TimelineFragment.Kind.HOME, enableStreaming) }, + enableStreaming = enableStreaming + ) + NOTIFICATIONS -> TabData( + NOTIFICATIONS, + R.string.title_notifications, + R.drawable.ic_notifications_24dp, + { NotificationsFragment.newInstance() } + ) + LOCAL -> TabData( + LOCAL, + R.string.title_public_local, + R.drawable.ic_local_24dp, + { TimelineFragment.newInstance(TimelineFragment.Kind.PUBLIC_LOCAL, enableStreaming) }, + enableStreaming = enableStreaming + ) + FEDERATED -> TabData( + FEDERATED, + R.string.title_public_federated, + R.drawable.ic_public_24dp, + { TimelineFragment.newInstance(TimelineFragment.Kind.PUBLIC_FEDERATED, enableStreaming) }, + enableStreaming = enableStreaming + ) + DIRECT -> TabData( + DIRECT, + R.string.title_direct_messages, + R.drawable.ic_reblog_direct_24dp, + { ConversationsFragment.newInstance() } + ) + HASHTAG -> TabData( + HASHTAG, + R.string.hashtags, + R.drawable.ic_hashtag, + { args -> TimelineFragment.newHashtagInstance(args) }, + arguments, + { context -> arguments.joinToString(separator = " ") { context.getString(R.string.title_tag, it) }} + ) + LIST -> TabData( + LIST, + R.string.list, + R.drawable.ic_list, + { args -> TimelineFragment.newInstance(TimelineFragment.Kind.LIST, args.getOrNull(0).orEmpty(), true, enableStreaming) }, + arguments, + { arguments.getOrNull(1).orEmpty() }, + enableStreaming + ) else -> throw IllegalArgumentException("unknown tab type") } } @@ -62,4 +107,4 @@ fun defaultTabs(): List { createTabDataFromId(LOCAL), createTabDataFromId(FEDERATED) ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java index b5e2f0fe6..a8797b6a4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -1100,7 +1100,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { status.getAttachments().size() == 0 && status.getCard() != null && !TextUtils.isEmpty(status.getCard().getUrl()) && - !status.isCollapsed()) { + (!status.isCollapsible() || !status.isCollapsed())) { final Card card = status.getCard(); cardView.setVisibility(View.VISIBLE); cardTitle.setText(card.getTitle()); diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java index 5e203bb81..164d81155 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java @@ -291,7 +291,7 @@ public class NotificationsFragment extends SFragment implements private void updateFilterVisibility() { CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); - if (showNotificationsFilter && !showingError && !notifications.isEmpty()) { + if (showNotificationsFilter && !showingError) { appBarOptions.setExpanded(true, false); appBarOptions.setVisibility(View.VISIBLE); //Set content behaviour to hide filter on scroll diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java index aa5eccbe0..ba8f04652 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java @@ -21,6 +21,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.net.ConnectivityManager; import android.net.NetworkInfo; +import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; @@ -82,6 +83,7 @@ import com.keylesspalace.tusky.repository.TimelineRepository; import com.keylesspalace.tusky.repository.TimelineRequestMode; import com.keylesspalace.tusky.util.CardViewMode; import com.keylesspalace.tusky.util.Either; +import com.keylesspalace.tusky.util.HttpHeaderLink; import com.keylesspalace.tusky.util.LinkHelper; import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate; import com.keylesspalace.tusky.util.ListUtils; @@ -178,6 +180,10 @@ public class TimelineFragment extends SFragment implements private Kind kind; private String id; private List tags; + /** + * For some timeline kinds we must use LINK headers and not just status ids. + */ + private String nextId; private LinearLayoutManager layoutManager; private EndlessOnScrollListener scrollListener; private boolean filterRemoveReplies; @@ -263,7 +269,7 @@ public class TimelineFragment extends SFragment implements || kind == Kind.LIST) { id = arguments.getString(ID_ARG); } - if(kind == Kind.TAG) { + if (kind == Kind.TAG) { tags = arguments.getStringArrayList(HASHTAGS_ARG); } @@ -1109,13 +1115,17 @@ public class TimelineFragment extends SFragment implements updateAdapter(); String bottomId = null; - final ListIterator> iterator = - this.statuses.listIterator(this.statuses.size()); - while (iterator.hasPrevious()) { - Either previous = iterator.previous(); - if (previous.isRight()) { - bottomId = previous.asRight().getId(); - break; + if (kind == Kind.FAVOURITES || kind == Kind.BOOKMARKS) { + bottomId = this.nextId; + } else { + final ListIterator> iterator = + this.statuses.listIterator(this.statuses.size()); + while (iterator.hasPrevious()) { + Either previous = iterator.previous(); + if (previous.isRight()) { + bottomId = previous.asRight().getId(); + break; + } } } sendFetchTimelineRequest(bottomId, null, null, FetchEnd.BOTTOM, -1); @@ -1196,6 +1206,14 @@ public class TimelineFragment extends SFragment implements @Override public void onResponse(@NonNull Call> call, @NonNull Response> response) { if (response.isSuccessful()) { + @Nullable + String newNextId = extractNextId(response); + if (newNextId != null) { + // when we reach the bottom of the list, we won't have a new link. If + // we blindly write `null` here we will start loading from the top + // again. + nextId = newNextId; + } onFetchTimelineSuccess(liftStatusList(response.body()), fetchEnd, pos); } else { onFetchTimelineFailure(new Exception(response.message()), fetchEnd, pos); @@ -1214,6 +1232,24 @@ public class TimelineFragment extends SFragment implements } } + @Nullable + private String extractNextId(Response response) { + String linkHeader = response.headers().get("Link"); + if (linkHeader == null) { + return null; + } + List links = HttpHeaderLink.parse(linkHeader); + HttpHeaderLink nextHeader = HttpHeaderLink.findByRelationType(links, "next"); + if (nextHeader == null) { + return null; + } + Uri nextLink = nextHeader.uri; + if (nextLink == null) { + return null; + } + return nextLink.getQueryParameter("max_id"); + } + private void onFetchTimelineSuccess(List> statuses, FetchEnd fetchEnd, int pos) { diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt index 3e54f7ce5..9fa6a6e54 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt @@ -19,21 +19,21 @@ import android.content.Intent import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle -import com.google.android.material.snackbar.Snackbar import android.util.Log -import android.view.View -import androidx.preference.* +import androidx.preference.PreferenceFragmentCompat +import com.google.android.material.snackbar.Snackbar import com.keylesspalace.tusky.* -import com.keylesspalace.tusky.R import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.PreferenceChangedEvent import com.keylesspalace.tusky.components.instancemute.InstanceListActivity +import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.entity.Account import com.keylesspalace.tusky.entity.Filter import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.network.MastodonApi +import com.keylesspalace.tusky.settings.* import com.keylesspalace.tusky.util.ThemeUtils import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial @@ -44,11 +44,7 @@ import retrofit2.Callback import retrofit2.Response import javax.inject.Inject - -class AccountPreferencesFragment : PreferenceFragmentCompat(), - Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener, - Injectable { - +class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { @Inject lateinit var accountManager: AccountManager @@ -58,182 +54,217 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), @Inject lateinit var eventHub: EventHub - private lateinit var notificationPreference: Preference - private lateinit var tabPreference: Preference - private lateinit var mutedUsersPreference: Preference - private lateinit var blockedUsersPreference: Preference - private lateinit var mutedDomainsPreference: Preference - - private lateinit var defaultPostPrivacyPreference: ListPreference - private lateinit var defaultMediaSensitivityPreference: SwitchPreferenceCompat - private lateinit var alwaysShowSensitiveMediaPreference: SwitchPreferenceCompat - private lateinit var alwaysOpenSpoilerPreference: SwitchPreferenceCompat - private lateinit var mediaPreviewEnabledPreference: SwitchPreferenceCompat - private lateinit var homeFiltersPreference: Preference - private lateinit var notificationFiltersPreference: Preference - private lateinit var publicFiltersPreference: Preference - private lateinit var threadFiltersPreference: Preference - private lateinit var accountFiltersPreference: Preference - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.account_preferences) - - notificationPreference = requirePreference("notificationPreference") - tabPreference = requirePreference("tabPreference") - mutedUsersPreference = requirePreference("mutedUsersPreference") - blockedUsersPreference = requirePreference("blockedUsersPreference") - mutedDomainsPreference = requirePreference("mutedDomainsPreference") - defaultPostPrivacyPreference = requirePreference("defaultPostPrivacy") as ListPreference - defaultMediaSensitivityPreference = requirePreference("defaultMediaSensitivity") as SwitchPreferenceCompat - mediaPreviewEnabledPreference = requirePreference("mediaPreviewEnabled") as SwitchPreferenceCompat - alwaysShowSensitiveMediaPreference = requirePreference("alwaysShowSensitiveMedia") as SwitchPreferenceCompat - alwaysOpenSpoilerPreference = requirePreference("alwaysOpenSpoiler") as SwitchPreferenceCompat - homeFiltersPreference = requirePreference("homeFilters") - notificationFiltersPreference = requirePreference("notificationFilters") - publicFiltersPreference = requirePreference("publicFilters") - threadFiltersPreference = requirePreference("threadFilters") - accountFiltersPreference = requirePreference("accountFilters") - - notificationPreference.icon = IconicsDrawable(notificationPreference.context, GoogleMaterial.Icon.gmd_notifications).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(notificationPreference.context, R.attr.iconColor) } - mutedUsersPreference.icon = getTintedIcon(R.drawable.ic_mute_24dp) - blockedUsersPreference.icon = IconicsDrawable(blockedUsersPreference.context, GoogleMaterial.Icon.gmd_block).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(blockedUsersPreference.context, R.attr.iconColor) } - mutedDomainsPreference.icon = getTintedIcon(R.drawable.ic_mute_24dp) - - notificationPreference.onPreferenceClickListener = this - tabPreference.onPreferenceClickListener = this - mutedUsersPreference.onPreferenceClickListener = this - blockedUsersPreference.onPreferenceClickListener = this - mutedDomainsPreference.onPreferenceClickListener = this - homeFiltersPreference.onPreferenceClickListener = this - notificationFiltersPreference.onPreferenceClickListener = this - publicFiltersPreference.onPreferenceClickListener = this - threadFiltersPreference.onPreferenceClickListener = this - accountFiltersPreference.onPreferenceClickListener = this - - defaultPostPrivacyPreference.onPreferenceChangeListener = this - defaultMediaSensitivityPreference.onPreferenceChangeListener = this - mediaPreviewEnabledPreference.onPreferenceChangeListener = this - alwaysShowSensitiveMediaPreference.onPreferenceChangeListener = this - alwaysOpenSpoilerPreference.onPreferenceChangeListener = this - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - accountManager.activeAccount?.let { - - defaultPostPrivacyPreference.value = it.defaultPostPrivacy.serverString() - defaultPostPrivacyPreference.icon = getIconForVisibility(it.defaultPostPrivacy) - - defaultMediaSensitivityPreference.isChecked = it.defaultMediaSensitivity - defaultMediaSensitivityPreference.icon = getIconForSensitivity(it.defaultMediaSensitivity) - - mediaPreviewEnabledPreference.isChecked = it.mediaPreviewEnabled - alwaysShowSensitiveMediaPreference.isChecked = it.alwaysShowSensitiveMedia - alwaysOpenSpoilerPreference.isChecked = it.alwaysOpenSpoiler - - } - } - - override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean { - when (preference) { - defaultPostPrivacyPreference -> { - preference.icon = getIconForVisibility(Status.Visibility.byString(newValue as String)) - syncWithServer(visibility = newValue) - } - defaultMediaSensitivityPreference -> { - preference.icon = getIconForSensitivity(newValue as Boolean) - syncWithServer(sensitive = newValue) - } - mediaPreviewEnabledPreference -> { - accountManager.activeAccount?.let { - it.mediaPreviewEnabled = newValue as Boolean - accountManager.saveAccount(it) + val context = requireContext() + makePreferenceScreen { + preference { + setTitle(R.string.pref_title_edit_notification_settings) + icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_notifications).apply { + sizeRes = R.dimen.preference_icon_size + colorInt = ThemeUtils.getColor(context, R.attr.iconColor) + } + setOnPreferenceClickListener { + openNotificationPrefs() + true } } - alwaysShowSensitiveMediaPreference -> { - accountManager.activeAccount?.let { - it.alwaysShowSensitiveMedia = newValue as Boolean - accountManager.saveAccount(it) + + preference { + setTitle(R.string.title_tab_preferences) + setOnPreferenceClickListener { + val intent = Intent(context, TabPreferenceActivity::class.java) + activity?.startActivity(intent) + activity?.overridePendingTransition(R.anim.slide_from_right, + R.anim.slide_to_left) + true } } - alwaysOpenSpoilerPreference -> { - accountManager.activeAccount?.let { - it.alwaysOpenSpoiler = newValue as Boolean - accountManager.saveAccount(it) + + preference { + setTitle(R.string.action_view_mutes) + icon = getTintedIcon(R.drawable.ic_mute_24dp) + setOnPreferenceClickListener { + val intent = Intent(context, AccountListActivity::class.java) + intent.putExtra("type", AccountListActivity.Type.MUTES) + activity?.startActivity(intent) + activity?.overridePendingTransition(R.anim.slide_from_right, + R.anim.slide_to_left) + true } } - } - eventHub.dispatch(PreferenceChangedEvent(preference.key)) + preference { + setTitle(R.string.action_view_blocks) + icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_block).apply { + sizeRes = R.dimen.preference_icon_size + colorInt = ThemeUtils.getColor(context, R.attr.iconColor) + } + setOnPreferenceClickListener { + val intent = Intent(context, AccountListActivity::class.java) + intent.putExtra("type", AccountListActivity.Type.BLOCKS) + activity?.startActivity(intent) + activity?.overridePendingTransition(R.anim.slide_from_right, + R.anim.slide_to_left) + true + } + } - return true - } + preference { + setTitle(R.string.title_domain_mutes) + icon = getTintedIcon(R.drawable.ic_mute_24dp) + setOnPreferenceClickListener { + val intent = Intent(context, InstanceListActivity::class.java) + activity?.startActivity(intent) + activity?.overridePendingTransition(R.anim.slide_from_right, + R.anim.slide_to_left) + true + } + } - override fun onPreferenceClick(preference: Preference): Boolean { - - return when (preference) { - notificationPreference -> { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val intent = Intent() - intent.action = "android.settings.APP_NOTIFICATION_SETTINGS" - intent.putExtra("android.provider.extra.APP_PACKAGE", BuildConfig.APPLICATION_ID) - startActivity(intent) - } else { - activity?.let { - val intent = PreferencesActivity.newIntent(it, PreferencesActivity.NOTIFICATION_PREFERENCES) - it.startActivity(intent) - it.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) + preferenceCategory(R.string.pref_publishing) { + listPreference { + setTitle(R.string.pref_default_post_privacy) + setEntries(R.array.post_privacy_names) + setEntryValues(R.array.post_privacy_values) + key = PrefKeys.DEFAULT_POST_PRIVACY + setSummaryProvider { entry } + val visibility = accountManager.activeAccount?.defaultPostPrivacy + ?: Status.Visibility.PUBLIC + value = visibility.serverString() + icon = getIconForVisibility(visibility) + setOnPreferenceChangeListener { _, newValue -> + icon = getIconForVisibility( + Status.Visibility.byString(newValue as String) + ) + syncWithServer(visibility = newValue) + eventHub.dispatch(PreferenceChangedEvent(key)) + true + } + } + + switchPreference { + setTitle(R.string.pref_default_media_sensitivity) + setIcon(R.drawable.ic_eye_24dp) + key = PrefKeys.DEFAULT_MEDIA_SENSITIVITY + isSingleLineTitle = false + val sensitivity = accountManager.activeAccount?.defaultMediaSensitivity + ?: false + setDefaultValue(sensitivity) + icon = getIconForSensitivity(sensitivity) + setOnPreferenceChangeListener { _, newValue -> + icon = getIconForSensitivity(newValue as Boolean) + syncWithServer(sensitive = newValue) + eventHub.dispatch(PreferenceChangedEvent(key)) + true } - } - true - } - tabPreference -> { - val intent = Intent(context, TabPreferenceActivity::class.java) - activity?.startActivity(intent) - activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) - true - } - mutedUsersPreference -> { - val intent = Intent(context, AccountListActivity::class.java) - intent.putExtra("type", AccountListActivity.Type.MUTES) - activity?.startActivity(intent) - activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) - true - } - blockedUsersPreference -> { - val intent = Intent(context, AccountListActivity::class.java) - intent.putExtra("type", AccountListActivity.Type.BLOCKS) - activity?.startActivity(intent) - activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) - true - } - mutedDomainsPreference -> { - val intent = Intent(context, InstanceListActivity::class.java) - activity?.startActivity(intent) - activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) - true - } - homeFiltersPreference -> { - launchFilterActivity(Filter.HOME, R.string.title_home) - } - notificationFiltersPreference -> { - launchFilterActivity(Filter.NOTIFICATIONS, R.string.title_notifications) - } - publicFiltersPreference -> { - launchFilterActivity(Filter.PUBLIC, R.string.pref_title_public_filter_keywords) - } - threadFiltersPreference -> { - launchFilterActivity(Filter.THREAD, R.string.pref_title_thread_filter_keywords) - } - accountFiltersPreference -> { - launchFilterActivity(Filter.ACCOUNT, R.string.title_accounts) } - else -> false + preferenceCategory(R.string.pref_title_timelines) { + switchPreference { + key = PrefKeys.MEDIA_PREVIEW_ENABLED + setTitle(R.string.pref_title_show_media_preview) + isSingleLineTitle = false + isChecked = accountManager.activeAccount?.mediaPreviewEnabled ?: true + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.mediaPreviewEnabled = newValue as Boolean } + eventHub.dispatch(PreferenceChangedEvent(key)) + true + } + } + + switchPreference { + key = PrefKeys.ALWAYS_SHOW_SENSITIVE_MEDIA + setTitle(R.string.pref_title_alway_show_sensitive_media) + isSingleLineTitle = false + isChecked = accountManager.activeAccount?.alwaysShowSensitiveMedia ?: false + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.alwaysShowSensitiveMedia = newValue as Boolean } + eventHub.dispatch(PreferenceChangedEvent(key)) + true + } + } + + switchPreference { + key = PrefKeys.ALWAYS_OPEN_SPOILER + setTitle(R.string.pref_title_alway_open_spoiler) + isSingleLineTitle = false + isChecked = accountManager.activeAccount?.alwaysOpenSpoiler ?: false + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.alwaysOpenSpoiler = newValue as Boolean } + eventHub.dispatch(PreferenceChangedEvent(key)) + true + } + } + } + + preferenceCategory(R.string.pref_title_timeline_filters) { + preference { + setTitle(R.string.pref_title_public_filter_keywords) + setOnPreferenceClickListener { + launchFilterActivity(Filter.THREAD, + R.string.pref_title_thread_filter_keywords) + true + } + } + + preference { + setTitle(R.string.title_notifications) + setOnPreferenceClickListener { + launchFilterActivity(Filter.NOTIFICATIONS, R.string.title_notifications) + true + } + } + + preference { + setTitle(R.string.title_home) + setOnPreferenceClickListener { + launchFilterActivity(Filter.HOME, R.string.title_home) + true + } + } + + preference { + setTitle(R.string.pref_title_thread_filter_keywords) + setOnPreferenceClickListener { + launchFilterActivity(Filter.THREAD, + R.string.pref_title_thread_filter_keywords) + true + } + } + + preference { + setTitle(R.string.title_accounts) + setOnPreferenceClickListener { + launchFilterActivity(Filter.ACCOUNT, R.string.title_accounts) + true + } + } + } } + } + private fun openNotificationPrefs() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val intent = Intent() + intent.action = "android.settings.APP_NOTIFICATION_SETTINGS" + intent.putExtra("android.provider.extra.APP_PACKAGE", BuildConfig.APPLICATION_ID) + startActivity(intent) + } else { + activity?.let { + val intent = PreferencesActivity.newIntent(it, PreferencesActivity.NOTIFICATION_PREFERENCES) + it.startActivity(intent) + it.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) + } + + } + } + + private inline fun updateAccount(changer: (AccountEntity) -> Unit) { + accountManager.activeAccount?.let { account -> + changer(account) + accountManager.saveAccount(account) + } } private fun syncWithServer(visibility: String? = null, sensitive: Boolean? = null) { @@ -297,17 +328,15 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), return ThemeUtils.getTintedDrawable(requireContext(), iconId, R.attr.iconColor) } - private fun launchFilterActivity(filterContext: String, titleResource: Int): Boolean { + private fun launchFilterActivity(filterContext: String, titleResource: Int) { val intent = Intent(context, FiltersActivity::class.java) intent.putExtra(FiltersActivity.FILTERS_CONTEXT, filterContext) intent.putExtra(FiltersActivity.FILTERS_TITLE, getString(titleResource)) activity?.startActivity(intent) activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) - return true } companion object { fun newInstance() = AccountPreferencesFragment() } - } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt index db01b0271..800ea7c1d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt @@ -16,80 +16,148 @@ package com.keylesspalace.tusky.fragment.preference import android.os.Bundle -import android.view.View -import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat -import androidx.preference.SwitchPreferenceCompat import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.components.notifications.NotificationHelper +import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.di.Injectable -import com.keylesspalace.tusky.components.notifications.NotificationHelper +import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.makePreferenceScreen +import com.keylesspalace.tusky.settings.preferenceCategory +import com.keylesspalace.tusky.settings.switchPreference import javax.inject.Inject -class NotificationPreferencesFragment : PreferenceFragmentCompat(), Preference.OnPreferenceChangeListener, Injectable { +class NotificationPreferencesFragment : PreferenceFragmentCompat(), Injectable { @Inject lateinit var accountManager: AccountManager override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.notification_preferences) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - val activeAccount = accountManager.activeAccount - - if (activeAccount != null) { - for (pair in mapOf( - "notificationsEnabled" to activeAccount.notificationsEnabled, - "notificationFilterMentions" to activeAccount.notificationsMentioned, - "notificationFilterFollows" to activeAccount.notificationsFollowed, - "notificationFilterFollowRequests" to activeAccount.notificationsFollowRequested, - "notificationFilterReblogs" to activeAccount.notificationsReblogged, - "notificationFilterFavourites" to activeAccount.notificationsFavorited, - "notificationFilterPolls" to activeAccount.notificationsPolls, - "notificationAlertSound" to activeAccount.notificationSound, - "notificationAlertVibrate" to activeAccount.notificationVibration, - "notificationAlertLight" to activeAccount.notificationLight - )) { - (requirePreference(pair.key) as SwitchPreferenceCompat).apply { - isChecked = pair.value - onPreferenceChangeListener = this@NotificationPreferencesFragment + val activeAccount = accountManager.activeAccount ?: return + val context = requireContext() + makePreferenceScreen { + switchPreference { + setTitle(R.string.pref_title_notifications_enabled) + key = PrefKeys.NOTIFICATIONS_ENABLED + isIconSpaceReserved = false + isChecked = activeAccount.notificationsEnabled + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationsEnabled = newValue as Boolean } + if (NotificationHelper.areNotificationsEnabled(context, accountManager)) { + NotificationHelper.enablePullNotifications(context) + } else { + NotificationHelper.disablePullNotifications(context) + } + true } } - } - } - override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean { + preferenceCategory(R.string.pref_title_notification_filters) { category -> + category.dependency = PrefKeys.NOTIFICATIONS_ENABLED + category.isIconSpaceReserved = false - val activeAccount = accountManager.activeAccount - - if (activeAccount != null) { - when (preference.key) { - "notificationsEnabled" -> { - activeAccount.notificationsEnabled = newValue as Boolean - if (NotificationHelper.areNotificationsEnabled(preference.context, accountManager)) { - NotificationHelper.enablePullNotifications(preference.context) - } else { - NotificationHelper.disablePullNotifications(preference.context) + switchPreference { + setTitle(R.string.pref_title_notification_filter_follows) + key = PrefKeys.NOTIFICATIONS_FILTER_FOLLOWS + isIconSpaceReserved = false + isChecked = activeAccount.notificationsFollowed + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationsFollowed = newValue as Boolean } + true } } - "notificationFilterMentions" -> activeAccount.notificationsMentioned = newValue as Boolean - "notificationFilterFollows" -> activeAccount.notificationsFollowed = newValue as Boolean - "notificationFilterFollowRequests" -> activeAccount.notificationsFollowRequested = newValue as Boolean - "notificationFilterReblogs" -> activeAccount.notificationsReblogged = newValue as Boolean - "notificationFilterFavourites" -> activeAccount.notificationsFavorited = newValue as Boolean - "notificationFilterPolls" -> activeAccount.notificationsPolls = newValue as Boolean - "notificationAlertSound" -> activeAccount.notificationSound = newValue as Boolean - "notificationAlertVibrate" -> activeAccount.notificationVibration = newValue as Boolean - "notificationAlertLight" -> activeAccount.notificationLight = newValue as Boolean - } - accountManager.saveAccount(activeAccount) - return true - } - return false + switchPreference { + setTitle(R.string.pref_title_notification_filter_follow_requests) + key = PrefKeys.NOTIFICATION_FILTER_FOLLOW_REQUESTS + isIconSpaceReserved = false + isChecked = activeAccount.notificationsFollowRequested + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationsFollowRequested = newValue as Boolean } + true + } + } + + switchPreference { + setTitle(R.string.pref_title_notification_filter_reblogs) + key = PrefKeys.NOTIFICATION_FILTER_REBLOGS + isIconSpaceReserved = false + isChecked = activeAccount.notificationsReblogged + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationsReblogged = newValue as Boolean } + true + } + } + + switchPreference { + setTitle(R.string.pref_title_notification_filter_favourites) + key = PrefKeys.NOTIFICATION_FILTER_FAVS + isIconSpaceReserved = false + isChecked = activeAccount.notificationsFavorited + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationsFavorited = newValue as Boolean } + true + } + } + + switchPreference { + setTitle(R.string.pref_title_notification_filter_poll) + key = PrefKeys.NOTIFICATION_FILTER_POLLS + isIconSpaceReserved = false + isChecked = activeAccount.notificationsPolls + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationsPolls = newValue as Boolean } + true + } + } + } + + preferenceCategory(R.string.pref_title_notification_alerts) { category -> + category.dependency = PrefKeys.NOTIFICATIONS_ENABLED + category.isIconSpaceReserved = false + + switchPreference { + setTitle(R.string.pref_title_notification_alert_sound) + key = PrefKeys.NOTIFICATION_ALERT_SOUND + isIconSpaceReserved = false + isChecked = activeAccount.notificationSound + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationSound = newValue as Boolean } + true + } + } + + switchPreference { + setTitle(R.string.pref_title_notification_alert_vibrate) + key = PrefKeys.NOTIFICATION_ALERT_VIBRATE + isIconSpaceReserved = false + isChecked = activeAccount.notificationVibration + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationVibration = newValue as Boolean } + true + } + } + + switchPreference { + setTitle(R.string.pref_title_notification_alert_light) + key = PrefKeys.NOTIFICATION_ALERT_LIGHT + isIconSpaceReserved = false + isChecked = activeAccount.notificationLight + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationLight = newValue as Boolean } + true + } + } + } + } + } + + private inline fun updateAccount(changer: (AccountEntity) -> Unit) { + accountManager.activeAccount?.let { account -> + changer(account) + accountManager.saveAccount(account) + } } companion object { diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt index 086aa809e..a27e3b0e6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt @@ -18,64 +18,249 @@ package com.keylesspalace.tusky.fragment.preference import android.os.Bundle import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat +import androidx.preference.SwitchPreference import com.keylesspalace.tusky.PreferencesActivity import com.keylesspalace.tusky.R import com.keylesspalace.tusky.components.compose.ComposeActivity import com.keylesspalace.tusky.components.compose.ComposeActivity.ComposeOptions +import com.keylesspalace.tusky.settings.* import com.keylesspalace.tusky.util.ThemeUtils import com.keylesspalace.tusky.util.getNonNullString import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt -import com.mikepenz.iconics.utils.sizeRes - -fun PreferenceFragmentCompat.requirePreference(key: String): Preference { - return findPreference(key)!! -} +import com.mikepenz.iconics.utils.sizePx class PreferencesFragment : PreferenceFragmentCompat() { + private val iconSize by lazy { resources.getDimensionPixelSize(R.dimen.preference_icon_size) } + private var httpProxyPref: Preference? = null + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + val context = requireContext() + makePreferenceScreen { + lateinit var limitedBandwidthMobilePref: SwitchPreference + lateinit var limitedBandwidthTimelinePref: SwitchPreference - addPreferencesFromResource(R.xml.preferences) + preferenceCategory(R.string.pref_title_appearance_settings) { + listPreference { + setDefaultValue(AppTheme.NIGHT.value) + setEntries(R.array.app_theme_names) + entryValues = AppTheme.stringValues() + key = PrefKeys.APP_THEME + setSummaryProvider { entry } + setTitle(R.string.pref_title_app_theme) + icon = makeIcon(GoogleMaterial.Icon.gmd_palette) + } - val themePreference: Preference = requirePreference("appTheme") - themePreference.icon = IconicsDrawable(themePreference.context, GoogleMaterial.Icon.gmd_palette).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(themePreference.context, R.attr.iconColor) } + emojiPreference { + setDefaultValue("system_default") + setIcon(R.drawable.ic_emoji_24dp) + key = PrefKeys.EMOJI + setSummary(R.string.system_default) + setTitle(R.string.emoji_style) + icon = makeIcon(GoogleMaterial.Icon.gmd_sentiment_satisfied) + } - val emojiPreference: Preference = requirePreference("emojiCompat") - emojiPreference.icon = IconicsDrawable(emojiPreference.context, GoogleMaterial.Icon.gmd_sentiment_satisfied).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(themePreference.context, R.attr.iconColor) } + listPreference { + setDefaultValue("default") + setEntries(R.array.language_entries) + setEntryValues(R.array.language_values) + key = PrefKeys.LANGUAGE + setSummaryProvider { entry } + setTitle(R.string.pref_title_language) + icon = makeIcon(GoogleMaterial.Icon.gmd_translate) + } - val textSizePreference: Preference = requirePreference("statusTextSize") - textSizePreference.icon = IconicsDrawable(textSizePreference.context, GoogleMaterial.Icon.gmd_format_size).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(themePreference.context, R.attr.iconColor) } + listPreference { + setDefaultValue("medium") + setEntries(R.array.status_text_size_names) + setEntryValues(R.array.status_text_size_values) + key = PrefKeys.STATUS_TEXT_SIZE + setSummaryProvider { entry } + setTitle(R.string.pref_status_text_size) + icon = makeIcon(GoogleMaterial.Icon.gmd_format_size) + } - val timelineFilterPreferences: Preference = requirePreference("timelineFilterPreferences") - timelineFilterPreferences.setOnPreferenceClickListener { - activity?.let { activity -> - val intent = PreferencesActivity.newIntent(activity, PreferencesActivity.TAB_FILTER_PREFERENCES) - activity.startActivity(intent) - activity.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) + listPreference { + setDefaultValue("top") + setEntries(R.array.pref_main_nav_position_options) + setEntryValues(R.array.pref_main_nav_position_values) + key = PrefKeys.MAIN_NAV_POSITION + setSummaryProvider { entry } + setTitle(R.string.pref_main_nav_position) + } + + switchPreference { + setDefaultValue(false) + key = PrefKeys.FAB_HIDE + setTitle(R.string.pref_title_hide_follow_button) + isSingleLineTitle = false + } + + switchPreference { + setDefaultValue(false) + key = PrefKeys.ABSOLUTE_TIME_VIEW + setTitle(R.string.pref_title_absolute_time) + isSingleLineTitle = false + } + + switchPreference { + setDefaultValue(true) + key = PrefKeys.SHOW_BOT_OVERLAY + setTitle(R.string.pref_title_bot_overlay) + isSingleLineTitle = false + icon = ThemeUtils.getTintedDrawable( + context, + R.drawable.ic_bot_24dp, + R.attr.iconColor + ) + } + + switchPreference { + setDefaultValue(false) + key = PrefKeys.ANIMATE_GIF_AVATARS + setTitle(R.string.pref_title_animate_gif_avatars) + isSingleLineTitle = false + } + + switchPreference { + setDefaultValue(true) + key = PrefKeys.USE_BLURHASH + setTitle(R.string.pref_title_gradient_for_media) + isSingleLineTitle = false + } + + switchPreference { + setDefaultValue(true) + key = PrefKeys.SHOW_NOTIFICATIONS_FILTER + setTitle(R.string.pref_title_show_notifications_filter) + isSingleLineTitle = false + setOnPreferenceClickListener { + activity?.let { activity -> + val intent = PreferencesActivity.newIntent(activity, PreferencesActivity.TAB_FILTER_PREFERENCES) + activity.startActivity(intent) + activity.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) + } + true + } + } + + switchPreference { + setDefaultValue(false) + key = PrefKeys.SHOW_CARDS_IN_TIMELINES + setTitle(R.string.pref_title_confirm_reblogs) + isSingleLineTitle = false + } + + switchPreference { + setDefaultValue(true) + key = PrefKeys.ENABLE_SWIPE_FOR_TABS + setTitle(R.string.pref_title_enable_swipe_for_tabs) + isSingleLineTitle = false + } } - true - } - val httpProxyPreferences: Preference = requirePreference("httpProxyPreferences") - httpProxyPreferences.setOnPreferenceClickListener { - activity?.let { activity -> - val intent = PreferencesActivity.newIntent(activity, PreferencesActivity.PROXY_PREFERENCES) - activity.startActivity(intent) - activity.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) + preferenceCategory(R.string.pref_title_limited_bandwidth_settings) { + switchPreference { + setDefaultValue(false) + key = PrefKeys.LIMITED_BANDWIDTH_ACTIVE + setTitle(R.string.pref_title_limited_bandwidth_active) + disableDependentsState = false + isSingleLineTitle = false + } + + limitedBandwidthMobilePref = switchPreference { + setDefaultValue(true) + key = PrefKeys.LIMITED_BANDWIDTH_ONLY_MOBILE_NETWORK + setTitle(R.string.pref_title_limited_bandwidth_mobile) + isSingleLineTitle = false + } + + limitedBandwidthTimelinePref = switchPreference { + setDefaultValue(true) + key = PrefKeys.LIMITED_BANDWIDTH_TIMELINE_LOADING + setTitle(R.string.pref_title_limited_bandwidth_timeline) + isSingleLineTitle = false + } + } + arrayOf(limitedBandwidthMobilePref, limitedBandwidthTimelinePref).forEach { + it.dependency = PrefKeys.LIMITED_BANDWIDTH_ACTIVE + } + + preferenceCategory(R.string.pref_title_browser_settings) { + switchPreference { + setDefaultValue(false) + key = PrefKeys.CUSTOM_TABS + setTitle(R.string.pref_title_custom_tabs) + isSingleLineTitle = false + } + } + + preferenceCategory(R.string.pref_title_timeline_filters) { + preference { + setTitle(R.string.pref_title_status_tabs) + setOnPreferenceClickListener { + activity?.let { activity -> + val intent = PreferencesActivity.newIntent(activity, PreferencesActivity.TAB_FILTER_PREFERENCES) + activity.startActivity(intent) + activity.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) + } + true + } + } + } + + preferenceCategory(R.string.pref_title_proxy_settings) { + httpProxyPref = preference { + setTitle(R.string.pref_title_http_proxy_settings) + setOnPreferenceClickListener { + activity?.let { activity -> + val intent = PreferencesActivity.newIntent(activity, PreferencesActivity.PROXY_PREFERENCES) + activity.startActivity(intent) + activity.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) + } + true + } + } + } + + preferenceManager.sharedPreferences.let { prefs -> + prefs.getString("stack_trace", null)?.let { stackTrace -> + preferenceCategory(R.string.pref_title_stacktrace) { + preference { + setTitle(R.string.pref_title_stacktrace_send) + setOnPreferenceClickListener { + activity?.let { activity -> + val intent = ComposeActivity.startIntent(activity, ComposeOptions( + tootText = "@ars42525@odakyu.app $stackTrace".substring(0, 400), + contentWarning = "Yuito StackTrace" + )) + activity.startActivity(intent) + prefs.edit() + .remove("stack_trace") + .apply() + } + true + } + } + + preference { + summary = stackTrace + isSelectable = false + } + } + } } - true } + } - val languagePreference: Preference = requirePreference("language") - languagePreference.icon = IconicsDrawable(languagePreference.context, GoogleMaterial.Icon.gmd_translate).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(themePreference.context, R.attr.iconColor) } - - val botIndicatorPreference = requirePreference("showBotOverlay") - - botIndicatorPreference.icon = ThemeUtils.getTintedDrawable(requireContext(), R.drawable.ic_bot_24dp, R.attr.iconColor) - - updateStackTracePreference() + private fun makeIcon(icon: GoogleMaterial.Icon): IconicsDrawable { + val context = requireContext() + return IconicsDrawable(context, icon).apply { + sizePx = iconSize + colorInt = ThemeUtils.getColor(context, R.attr.iconColor) + } } override fun onResume() { @@ -84,58 +269,23 @@ class PreferencesFragment : PreferenceFragmentCompat() { } private fun updateHttpProxySummary() { - - val httpProxyPref: Preference = requirePreference("httpProxyPreferences") - val sharedPreferences = preferenceManager.sharedPreferences - - val httpProxyEnabled = sharedPreferences.getBoolean("httpProxyEnabled", false) - - val httpServer = sharedPreferences.getNonNullString("httpProxyServer", "") + val httpProxyEnabled = sharedPreferences.getBoolean(PrefKeys.HTTP_PROXY_ENABLED, false) + val httpServer = sharedPreferences.getNonNullString(PrefKeys.HTTP_PROXY_SERVER, "") try { - val httpPort = sharedPreferences.getNonNullString("httpProxyPort", "-1").toInt() + val httpPort = sharedPreferences.getNonNullString(PrefKeys.HTTP_PROXY_PORT, "-1") + .toInt() if (httpProxyEnabled && httpServer.isNotBlank() && httpPort > 0 && httpPort < 65535) { - httpProxyPref.summary = "$httpServer:$httpPort" + httpProxyPref?.summary = "$httpServer:$httpPort" return } } catch (e: NumberFormatException) { // user has entered wrong port, fall back to empty summary } - httpProxyPref.summary = "" - - } - - private fun updateStackTracePreference() { - - val stackTraceCategory = requirePreference("stackTraceCategory") - - val sharedPreferences = preferenceManager.sharedPreferences - val stackTrace = sharedPreferences.getString("stack_trace", null) - if (stackTrace.isNullOrEmpty()) { - preferenceScreen.removePreference(stackTraceCategory) - } else { - val sendCrashReportPreference = requirePreference("sendCrashReport") - sendCrashReportPreference.setOnPreferenceClickListener { - activity?.let { activity -> - val intent = ComposeActivity.startIntent(activity, ComposeOptions( - tootText = "@ars42525@odakyu.app $stackTrace".substring(0, 400), - contentWarning = "Yuito StackTrace" - )) - activity.startActivity(intent) - sharedPreferences.edit() - .remove("stack_trace") - .apply() - } - true - } - - val stackTracePreference = requirePreference("stackTrace") - stackTracePreference.summary = stackTrace - } - + httpProxyPref?.summary = "" } companion object { diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/ProxyPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/ProxyPreferencesFragment.kt index a4efff2d7..e7ee7ade0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/ProxyPreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/ProxyPreferencesFragment.kt @@ -15,61 +15,53 @@ package com.keylesspalace.tusky.fragment.preference -import android.content.SharedPreferences import android.os.Bundle -import androidx.preference.EditTextPreference import androidx.preference.PreferenceFragmentCompat import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.editTextPreference +import com.keylesspalace.tusky.settings.makePreferenceScreen +import com.keylesspalace.tusky.settings.switchPreference import kotlin.system.exitProcess -class ProxyPreferencesFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener { - +class ProxyPreferencesFragment : PreferenceFragmentCompat() { private var pendingRestart = false - private lateinit var sharedPreferences: SharedPreferences - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.http_proxy_preferences) + makePreferenceScreen { + switchPreference { + setTitle(R.string.pref_title_http_proxy_enable) + isIconSpaceReserved = false + key = PrefKeys.HTTP_PROXY_ENABLED + setDefaultValue(false) + } - sharedPreferences = preferenceManager.sharedPreferences + editTextPreference { + setTitle(R.string.pref_title_http_proxy_server) + key = PrefKeys.HTTP_PROXY_SERVER + isIconSpaceReserved = false + setSummaryProvider { text } + } - } + editTextPreference { + setTitle(R.string.pref_title_http_proxy_port) + key = PrefKeys.HTTP_PROXY_PORT + isIconSpaceReserved = false + setSummaryProvider { text } + } + } - override fun onResume() { - super.onResume() - - sharedPreferences.registerOnSharedPreferenceChangeListener(this) - - updateSummary("httpProxyServer") - updateSummary("httpProxyPort") } override fun onPause() { super.onPause() - - sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) - if (pendingRestart) { pendingRestart = false exitProcess(0) } } - override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { - updateSummary (key) - } - - private fun updateSummary(key: String) { - when (key) { - "httpProxyServer", "httpProxyPort" -> { - val editTextPreference = requirePreference(key) as EditTextPreference - editTextPreference.summary = editTextPreference.text - } - } - } - companion object { - fun newInstance(): ProxyPreferencesFragment { return ProxyPreferencesFragment() } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/TabFilterPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/TabFilterPreferencesFragment.kt index 8d92d54bd..cd76300da 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/TabFilterPreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/TabFilterPreferencesFragment.kt @@ -15,22 +15,38 @@ package com.keylesspalace.tusky.fragment.preference -import android.content.SharedPreferences import android.os.Bundle import androidx.preference.PreferenceFragmentCompat import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.checkBoxPreference +import com.keylesspalace.tusky.settings.makePreferenceScreen +import com.keylesspalace.tusky.settings.preferenceCategory class TabFilterPreferencesFragment : PreferenceFragmentCompat() { - - private lateinit var sharedPreferences: SharedPreferences - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.timeline_filter_preferences) - sharedPreferences = preferenceManager.sharedPreferences + makePreferenceScreen { + preferenceCategory(R.string.title_home) { category -> + category.isIconSpaceReserved = false + + checkBoxPreference { + setTitle(R.string.pref_title_show_boosts) + key = PrefKeys.TAB_FILTER_HOME_BOOSTS + setDefaultValue(true) + isIconSpaceReserved = false + } + + checkBoxPreference { + setTitle(R.string.pref_title_show_replies) + key = PrefKeys.TAB_FILTER_HOME_REPLIES + setDefaultValue(false) + isIconSpaceReserved = false + } + } + } } companion object { - fun newInstance(): TabFilterPreferencesFragment { return TabFilterPreferencesFragment() } diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt new file mode 100644 index 000000000..50b7de776 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt @@ -0,0 +1,66 @@ +package com.keylesspalace.tusky.settings + +enum class AppTheme(val value: String) { + NIGHT("night"), + DAY("day"), + BLACK("black"), + AUTO("auto"), + AUTO_SYSTEM("auto_system"); + + companion object { + fun stringValues() = values().map { it.value }.toTypedArray() + } +} + +object PrefKeys { + // Note: not all of these keys are actually used as SharedPreferences keys but we must give + // each preference a key for it to work. + + const val APP_THEME = "appTheme" + const val EMOJI = "emojiCompat" + const val FAB_HIDE = "fabHide" + const val LANGUAGE = "language" + const val STATUS_TEXT_SIZE = "statusTextSize" + const val MAIN_NAV_POSITION = "mainNavPosition" + const val ABSOLUTE_TIME_VIEW = "absoluteTimeView" + const val SHOW_BOT_OVERLAY = "showBotOverlay" + const val ANIMATE_GIF_AVATARS = "animateGifAvatars" + const val USE_BLURHASH = "useBlurhash" + const val SHOW_NOTIFICATIONS_FILTER = "showNotificationsFilter" + const val SHOW_CARDS_IN_TIMELINES = "showCardsInTimelines" + const val ENABLE_SWIPE_FOR_TABS = "enableSwipeForTabs" + + const val LIMITED_BANDWIDTH_ACTIVE = "limitedBandwidthActive" + const val LIMITED_BANDWIDTH_ONLY_MOBILE_NETWORK = "limitedBandwidthOnlyMobileNetwork" + const val LIMITED_BANDWIDTH_TIMELINE_LOADING = "limitedBandwidthTimelineLoading" + + const val CUSTOM_TABS = "customTabs" + + const val HTTP_PROXY_ENABLED = "httpProxyEnabled" + const val HTTP_PROXY_SERVER = "httpProxyServer" + const val HTTP_PROXY_PORT = "httpProxyPort" + + const val VIEW_PAGER_OFF_SCREEN_LIMIT = "viewPagerOffScreenLimit" + + const val SEND_CRASH_REPORT = "sendCrashReport" + const val STACK_TRACE = "stackTrace" + + const val DEFAULT_POST_PRIVACY = "defaultPostPrivacy" + const val DEFAULT_MEDIA_SENSITIVITY = "defaultMediaSensitivity" + const val MEDIA_PREVIEW_ENABLED = "mediaPreviewEnabled" + const val ALWAYS_SHOW_SENSITIVE_MEDIA = "alwaysShowSensitiveMedia" + const val ALWAYS_OPEN_SPOILER = "alwaysOpenSpoiler" + + const val NOTIFICATIONS_ENABLED = "notificationsEnabled" + const val NOTIFICATION_ALERT_LIGHT = "notificationAlertLight" + const val NOTIFICATION_ALERT_VIBRATE = "notificationAlertVibrate" + const val NOTIFICATION_ALERT_SOUND = "notificationAlertSound" + const val NOTIFICATION_FILTER_POLLS = "notificationFilterPolls" + const val NOTIFICATION_FILTER_FAVS = "notificationFilterFavourites" + const val NOTIFICATION_FILTER_REBLOGS = "notificationFilterReblogs" + const val NOTIFICATION_FILTER_FOLLOW_REQUESTS = "notificationFilterFollowRequests" + const val NOTIFICATIONS_FILTER_FOLLOWS = "notificationFilterFollows" + + const val TAB_FILTER_HOME_REPLIES = "tabFilterHomeBoosts" + const val TAB_FILTER_HOME_BOOSTS = "tabFilterHomeReplies" +} diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsDSL.kt b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsDSL.kt new file mode 100644 index 000000000..438580eed --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsDSL.kt @@ -0,0 +1,83 @@ +package com.keylesspalace.tusky.settings + +import android.content.Context +import androidx.annotation.StringRes +import androidx.preference.* +import com.keylesspalace.tusky.EmojiPreference + +class PreferenceParent( + val context: Context, + val addPref: (pref: Preference) -> Unit +) + +inline fun PreferenceParent.preference(builder: Preference.() -> Unit): Preference { + val pref = Preference(context) + builder(pref) + addPref(pref) + return pref +} + +inline fun PreferenceParent.listPreference(builder: ListPreference.() -> Unit): ListPreference { + val pref = ListPreference(context) + builder(pref) + addPref(pref) + return pref +} + +inline fun PreferenceParent.emojiPreference(builder: EmojiPreference.() -> Unit): EmojiPreference { + val pref = EmojiPreference(context) + builder(pref) + addPref(pref) + return pref +} + +inline fun PreferenceParent.switchPreference( + builder: SwitchPreference.() -> Unit +): SwitchPreference { + val pref = SwitchPreference(context) + builder(pref) + addPref(pref) + return pref +} + +inline fun PreferenceParent.editTextPreference( + builder: EditTextPreference.() -> Unit +): EditTextPreference { + val pref = EditTextPreference(context) + builder(pref) + addPref(pref) + return pref +} + +inline fun PreferenceParent.checkBoxPreference( + builder: CheckBoxPreference.() -> Unit +): CheckBoxPreference { + val pref = CheckBoxPreference(context) + builder(pref) + addPref(pref) + return pref +} + +inline fun PreferenceParent.preferenceCategory( + @StringRes title: Int, + builder: PreferenceParent.(PreferenceCategory) -> Unit +) { + val category = PreferenceCategory(context) + addPref(category) + category.setTitle(title) + val newParent = PreferenceParent(context) { category.addPreference(it) } + builder(newParent, category) +} + +inline fun PreferenceFragmentCompat.makePreferenceScreen( + builder: PreferenceParent.() -> Unit +): PreferenceScreen { + val context = requireContext() + val screen = preferenceManager.createPreferenceScreen(context) + val parent = PreferenceParent(context) { screen.addPreference(it) } + // For some functions (like dependencies) it's much easier for us if we attach screen first + // and change it later + preferenceScreen = screen + builder(parent) + return screen +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.java index 8bf69796d..7888f7ce0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.java @@ -16,6 +16,7 @@ package com.keylesspalace.tusky.util; import android.content.Context; +import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; @@ -52,6 +53,13 @@ public class ThemeUtils { } } + public static int getDimension(@NonNull Context context, @AttrRes int attribute) { + TypedArray array = context.obtainStyledAttributes(new int[] { attribute }); + int dimen = array.getDimensionPixelSize(0, -1); + array.recycle(); + return dimen; + } + /** this can be replaced with drawableTint in xml once minSdkVersion >= 23 */ @Nullable public static Drawable getTintedDrawable(@NonNull Context context, @DrawableRes int drawableId, @AttrRes int colorAttr) { diff --git a/app/src/main/res/layout/activity_account.xml b/app/src/main/res/layout/activity_account.xml index b9cfabef3..9a36d1a74 100644 --- a/app/src/main/res/layout/activity_account.xml +++ b/app/src/main/res/layout/activity_account.xml @@ -125,12 +125,12 @@ android:layout_height="16sp" android:layout_marginStart="4dp" android:contentDescription="@string/description_account_locked" - android:tint="?android:textColorSecondary" android:visibility="gone" app:layout_constraintBottom_toBottomOf="@+id/accountUsernameTextView" app:layout_constraintStart_toEndOf="@+id/accountUsernameTextView" app:layout_constraintTop_toTopOf="@+id/accountUsernameTextView" app:srcCompat="@drawable/ic_reblog_private_24dp" + app:tint="?android:textColorSecondary" tools:visibility="visible" /> + android:elevation="@dimen/actionbar_elevation" + app:elevationOverlayEnabled="false"> + app:contentInsetStartWithNavigation="0dp" + app:layout_scrollFlags="scroll|enterAlways" /> - - - + @@ -50,15 +46,32 @@ android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_below="@id/tabLayout" + android:layout_marginBottom="?attr/actionBarSize" android:background="?attr/windowBackgroundColor" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> + + + + + + + android:layout_weight="0" /> diff --git a/app/src/main/res/layout/item_account.xml b/app/src/main/res/layout/item_account.xml index 45114bb6d..a7b3a0ef6 100644 --- a/app/src/main/res/layout/item_account.xml +++ b/app/src/main/res/layout/item_account.xml @@ -29,7 +29,7 @@ android:visibility="gone" app:layout_constraintBottom_toBottomOf="@id/account_avatar" app:layout_constraintEnd_toEndOf="@id/account_avatar" - tools:src="@color/accent" + tools:src="#000" tools:visibility="visible" /> + app:srcCompat="@drawable/ic_eye_24dp" + app:tint="@color/white" /> + tools:visibility="visible" + app:tint="@color/white" /> + app:layout_constraintTop_toBottomOf="@id/status_card_view" /> + app:srcCompat="@drawable/ic_eye_24dp" + app:tint="@color/white" /> + app:srcCompat="@drawable/ic_eye_24dp" + app:tint="@color/white" /> المفضلة الإشعار عندما يقوم أحدهم بإضافة تبويقاتك إلى مفضلاته %s أشار إليك - %1$s, %2$s, %3$s و %4$d أخرى + %1$s, %2$s, %3$s و %4$d آخرون %1$s, %2$s, و %3$s %1$s و %2$s %d تفاعلات جديدة @@ -468,7 +468,6 @@ أضيف إلى الفواصل المرجعية اختر قائمة القائمة - اظهر ألوانا متدرّجة للوسائط المخفية ليس لديك أية مسودات. ليس لديك أية منشورات مُبرمَجة للنشر. يجب أن يكون حجم الملفات الصوتية أقل مِن 40 ميغابايت. @@ -492,4 +491,6 @@ ألغِ كتم المحادثة اكتم المحادثة %s طلبَ متابعتك + الوسوم + إضافة وسم \ No newline at end of file diff --git a/app/src/main/res/values-bn-rIN/strings.xml b/app/src/main/res/values-bn-rIN/strings.xml index 9236de296..4afbd7cbf 100644 --- a/app/src/main/res/values-bn-rIN/strings.xml +++ b/app/src/main/res/values-bn-rIN/strings.xml @@ -357,7 +357,6 @@ সরাসরি পছন্দগুলি সহ নর্বাচন: %1$s, %2$s, %3$s, %4$s; %5$s নামের তালিকা - # ছাড়া হ্যাশট্যাগ পরিষ্কার ফিল্টার @@ -367,33 +366,14 @@ আপনি কি আপনার সমস্ত বিজ্ঞপ্তি স্থায়ীভাবে মুছে ফেলতে চান\? ছবি %s এর জন্য ক্রিয়া %1$s • %2$s - - - - + %s বাকি %s এ শেষ হবে বন্ধ ভোট আপনি ভোট দিয়েছেন যে নির্বাচন এ সেটি শেষ হয়েছে আপনি তৈরি একটি নির্বাচন শেষ হয়েছে - - - %d দিন - %d দিন - - - - - - - - - - - - - + চালিয়ে যান পিছনে যান সম্পন্ন diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 21cab22fd..d99fd7b8c 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -4,16 +4,16 @@ Això no pot estar buit. El domini introduït no és vàlid L\'autenticació en aquesta instància ha fallat. - No s\'ha trobat cap navegador web per a usar. + No s\'ha trobat cap navegador web per a utilitzar. S\'ha produït un error d\'autorització no identificat. S\'ha denegat l\'autorització. - L\'obtenció del testimoni d\'inici de sessió ha fallat. + L\'obtenció del token d\'inici de sessió ha fallat. L\'estat és massa llarg! El fitxer ha de ser inferior a 8MB. Aquest tipus de fitxer no es pot pujar. Aquest tipus de fitxer no es pot obrir. Cal permís d\'accés al emmagatzematge. - Cal permís d\'escriptura en el mitjà. + Cal permís d\'escriptura en el dispositiu. No es poden adjuntar imatges i vídeos en el mateix estat. La pujada ha fallat. Inici @@ -202,15 +202,15 @@ Missatges directes No hi ha res aquí. Elimina l\'impuls - S\'ha produït un error de connexió! Comprova la connexió! - Els fitxers de vídeo han de tenir menys de 40 MB. + S\'ha produït un error de connexió! Comprova la connexió i torna-ho a provar! + Els fitxers de vídeo han de pesar menys de 40 MB. Multimèdia amagada Amaga Estàs segur de tancar la sessió de %1$s\? Amaga els impulsos Mostra els impulsos Elimina i reecririu - Open drawer + Calaix obert Visibilitat del toot Contingut sensible Afegeix una pestanya @@ -226,7 +226,7 @@ Compartir la imatge a … Enviat! Petició de seguiment enviada - Amb resposta + Amb respostes Teclat d\'emojis Obrir el media #%d Obrir com %s @@ -351,7 +351,6 @@ Seguidors Directe Nom de la llista - Hashtag sense # Netejar Filtrar @@ -443,10 +442,25 @@ Seleccionar la llista Llista S\'ha produït un error en cercar la publicació %s - Mostra degradats de colors per a contingut multimèdia ocult No tens cap estat planificat. - Els fitxers d\'àudio han de ser més petits de 40MB. + Els fitxers d\'àudio han de ser més petits que 40MB. No tens cap esborrany. L\'interval mínim de planificació a Mastodon és de 5 minuts. Peticions de seguiment + Mostra el diàleg de confirmació abans de promoure + Mostra les previsualitzacions dels enllaços en els fils + Habilita el gest de desplaçament per despleçar-te entre pestanyes + + %s persona + %s persones + + Hashtags + Afegir hashtag + Notificacions sobre sol·licituds de seguiment + sol·licitació de seguiment + Silenciar @%s\? + Bloquejar @%s\? + No silenciar la conversació + Conversació muda + %s ha sol·licitat seguir-te \ No newline at end of file diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 46280836b..3c652ffea 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -362,9 +362,7 @@ Název seznamu Hashtag bez # - Napsat toot - Napsat Vymazat Filtrovat @@ -426,7 +424,7 @@ Přeposlat na %s Nahlášení selhalo Stahování tootů neuspělo - Nahlášení bude zasláno moderátorům vašeho serveru. Níže můžete uvést, proč tento účet nahlašujete: + Nahlášení bude zasláno moderátorovi vašeho serveru. Níže můžete uvést, proč tento účet nahlašujete: Tento účet je z jiného serveru. Chcete na něj také poslat anonymizovanou kopii\? Zobrazit filtr oznámení Anketa diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml index 239ab69c8..280193a17 100644 --- a/app/src/main/res/values-cy/strings.xml +++ b/app/src/main/res/values-cy/strings.xml @@ -290,4 +290,5 @@ Cysylltiadau Cysylltiadau Dangos hybiadau + Dilyn ceisiadau \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index fea216a84..327df667b 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -330,9 +330,7 @@ Suche nach Leuten denen du folgst Von der Liste entfernen Hashtag ohne # - Öffne Autor des geteilten Beitrages - Öffentliche Zeitleisten <b>%1$s</b> Favorit @@ -430,12 +428,7 @@ Lesezeichen Lesezeichen Lesezeichen - Bunten Farbverlauf für versteckte Medien anzeigen Powered by Tusky - - - - Als Lesezeichen gespeichert Liste auswählen Liste @@ -451,4 +444,14 @@ Stummschaltung der Konversation aufheben Konversation stummschalten "%s möchte dir folgen" + Hashtags + Hashtag hinzufügen + Bestätigungsdialog vor dem Teilen eines Beitrags + Linkvorschauen in Timelines anzeigen + Wischgeste zum Wechseln zwischen Tabs + + %s Person + %s Personen + + Farbverlauf für versteckte Medien anzeigen \ No newline at end of file diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index 39aeb4e5b..67c906d18 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -363,7 +363,6 @@ Moviĝi GIF profilbildojn Enketoj Sciigoj pri enketoj kiuj finiĝis - Kradvortoj sen # Viŝi Filtri @@ -450,7 +449,6 @@ Listo Eraro dum elserĉo de la mesaĝo %s Aŭdia dosiero devas esti malpli ol 40MB. - Montri buntajn transirojn por kaŝitaj aŭdovidaĵoj Vi ne havas iun ajn malneton. Vi ne havas iun ajn planitan mesaĝon. Petoj de sekvado diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index da8b89a66..a6b59f6cf 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -158,7 +158,7 @@ me siguen mis posts son impulsados me dan favorito - Interfaz + Apariencia Tema Cronologia Oscuro @@ -402,7 +402,7 @@ Ocultar dominio completo Animar avatares GIF Toda palabra - Cuando la palabra o frase sea solo alfanumérica, solo se aplicará si coincide con toda la palabra. + Cuando la palabra o frase sea solo alfanumérica, solo se aplicará si coincide con toda la palabra Set de emojis actual de Google Encuesta con opciones: %1$s, %2$s, %3$s, %4$s; %5$s Continuar @@ -446,9 +446,24 @@ Seleccionar lista Lista Los ficheros de audio deben ser menores de 40MB. - Mostrar degradados coloridos para el contenido multimedia oculto No tienes ningún borrador. No tienes ningún estado programado. Mastodon tiene un intervalo de programación mínimo de 5 minutos. Solicitudes + Bloquear @%s\? + Silenciar @%s\? + Silenciar conversación + Dejar de silenciar conversación + Notificaciones de solicitudes + solicitud de seguimiento + Mostrar diálogo de confirmación antes de impulsar + Mostrar previsualización de enlaces en la cronología + Habilitar gesto de deslizar para alternar entre pestañas + + %s persona + %s personas + + Etiquetas + Añadir etiqueta + %s solicita seguirte \ No newline at end of file diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 6939175ea..408c9d455 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -119,7 +119,7 @@ Goiburua Zer da instantzia? Konektatzen… - Sartu hemen helbidea edo mastodon.eus, mastodon.jalgi.eus, mastodon.social bezalako edozein instantzia, + Sartu hemen helbidea edo mastodon.eus, mastodon.jalgi.eus, mastodon.social bezalako edozein instantzia, \n \n Oraindik ez baduzu konturik, instantziaren izena sartu eta bertan kontua sortu dezakezu. \n @@ -388,7 +388,7 @@ %s amaitzen da itxita - Botatu + Bozkatu Botoa eman duzun galdeketa amaitu da Sortu duzun galdeketa amaitu da @@ -441,11 +441,15 @@ Laster-markatuta %s geratzen da Audioak 40MB baino gutxiago izan behar ditu. - Erakutsi degradatu koloretsuak ezkutuko multimedia-edukirako Aukeratu zerrenda Zerrenda Ez duzu zirriborrorik. Ez duzu tut programaturik. Mastodonek gutxienez 5 minutuko programazio-tartea du. Eskariak + Jarraitzeko eskaereri buruzko jakinarazpenak + \@%s isildu\? + \@%s blokeatu\? + Elkarrizketa isildu + %s -k zu jarraitzeko eskatu dizu \ No newline at end of file diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index bec564bc0..47cebbfc0 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -26,7 +26,7 @@ پست‌ها با پاسخ‌ دنبال شونده‌ها - پی‌گیر + پیروان علاقه‌مندی‌ها کاربرهای بی‌صدا کاربرهای مسدود شده @@ -119,13 +119,13 @@ سرایند یک نمونه چیست؟ در حال اتصال … - آدرس یا دامنه هر نمونه را می‌توانید وارد کنید، مثلا mastodon.social, icosahedron.website, social.tchncs.de, و <a href=\"https://instances.social\" >بیشتر! -\n -\n اگر شما هنوز حساب کاربری ندارید، می‌توانید نام نمونه مورد نظر را وارد کنید از اینجا بپیوندید و حساب کاربری ایجاد کنید. -\n -\n نمونه جایی است که حساب کاربری شما میزبان آن است اما شما به راحتی می‌توانید با افراد دیگر در نمونه‌های دیگر ارتباط برقرار کنید و آنها را دنبال کنید شما درست مثل اینکه در یکجا باشید. -\n -\n برای اطلاعات بیشتر به اینجا مراجعه کنید joinmastodon.org. + نشانی یا دامنهٔ هر نمونه‌ای می‌تواند وارد شود، مثل mastodon.social, icosahedron.website, social.tchncs.de, و بیش‌تر!. +\n +\n اگر هنوز حسابی ندارید، می‌توانید نام نمونه مورد نظر را وارد کرده و در آن حسابی بسازید. +\n +\n نمونه، جاییست که حسابتان رویش میزبانی می‌شود، ولی به راحتی می‌توانید با دیگر افراد روی نمونه‌های دیگر ارتباط داشته و دنبالشان کنید؛ انگار که روی یک پایگاه باشید. +\n +\nاطّلاعات بیش‌تر می‌تواند در joinmastodon.org پیدا شود. پایان بارگذاری رسانه در حال بارگذاری… بارگیری @@ -195,9 +195,7 @@ %d برهم‌کنش جدید حساب قفل‌شده درباره - Yuito یک برنامه آزاد و متن‌باز است که تحت مجوز GNU General Public License Version 3. منتشر شده است. -\n شما می‌توانید مجوز را از اینجا ببینید: -\n https://www.gnu.org/licenses/gpl-3.0.en.html + تاسکی نرم‌افزاری آزاد است که تحت نگارش ۳ از پروانهٔ جامع همگانی گنو منتشر شده است. پروانه را می‌توانید از این‌جا ببینید: https://www.gnu.org/licenses/gpl-3.0.en.html %1$s • %2$s - گزارش به ناظم‌های کارسازتان ارسال خواهد شد. می‌توانید توضیحی در باب چرایی گزارش این حساب در زیر بنویسید: + گزارش به ناظر کارسازتان ارسال خواهد شد. می‌توانید توضیحی در باب چرایی گزارش این حساب در زیر بنویسید: این حساب از کارسازی دیگر است. رونوشتی ناشناس از گزارش، به آن‌جا نیز ارسال شود؟ خطا در یافتن فرستهٔ %s قدرت‌گرفته از تاسکی @@ -439,11 +437,27 @@ نشانک‌ها نشانک نشانک‌ها - نمایش گرادیان رنگی برای رسانهٔ نهفته نشان‌شده گزینش فهرست فهرست هیچ پیش‌نویسی ندارید. هیچ وضعیت زمان‌بسته‌ای ندارید. ماستودون، بازهٔ زمان‌بندی‌ای با کمینهٔ ۵ دقیقه دارد. - \ No newline at end of file + نمایش گفت‌وگوی تأیید پیش از تقویت + پیش‌نمایش پیوندها در خط‌زمانی‌ها + به کار انداختن اشارهٔ کشیدنی برای تعویض بین زبانه‌ها + + %s نفر + %s نفر + + هشتگ‌ها + افزودن هشتگ + آگاهی‌ها در مورد درخواست‌های پی‌گیری + درخواست‌های پی‌گیری + درخواست پی‌گیری + خموشی @%s؟ + انسداد @%s؟ + ناخموشی گفت‌وگو + خموشی گفت‌وگو + %s می‌خواهد دنبالتان کند + diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 3bcea7fe5..fda2e0e68 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -69,7 +69,7 @@ Ne plus suivre Bloquer Débloquer - Cacher les boosts + Cacher les partages Montrer les boosts Signaler Supprimer @@ -369,7 +369,7 @@ Rédiger un pouet Écrire Afficher l\'indicateur de robots - "Nettoyer toutes les notifications de façon permanente\? " + Désirez-vous nettoyer toutes vos notifications de façon permanente \? Effacer et ré-écrire Effacer et ré-écrire ce pouet \? @@ -404,7 +404,7 @@ Actions pour l’image %s %1$s • %2$s Un sondage auquel vous avez participé vient de se terminer - Sondage avec des choix : %1$s, %2$s, %3$s, %4$s; %5$s + Sondage avec des choix : %1$s, %2$s, %3$s, %4$s ; %5$s Domaines cachés Domaines cachés Masquer %s @@ -449,13 +449,12 @@ Erreur lors de la récupération du message %s Propulsé par Tusky Signets - Marquer comme signet + Ajouter aux marque-pages Marque-pages Ajouté aux marque-pages Sélectionner la liste Liste Les fichiers audio doivent être inférieurs à 40 Mo. - Afficher des dégradés en couleur pour les médias cachés Vous n’avez aucun brouillon. Vous n’avez aucun pouet planifié. L’intervalle minimum de planification sur Mastodon est de 5 minutes. @@ -474,4 +473,7 @@ Enlever la sourdine à la conversation Silencer la conversation Activer les gestes de glissement pour passer d’un onglet à l’autre + Hashtags + Ajouter hashtag + Afficher des dégradés en couleur pour les médias cachés \ No newline at end of file diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 3bc9946bb..459ca4195 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -367,7 +367,6 @@ Közvetlen Szavazás válaszokkal: %1$s, %2$s, %3$s, %4$s; %5$s Lista neve - Hashtag # nélkül Törlés Szűrés @@ -378,8 +377,8 @@ Műveletek a %s képpel %1$s • %2$s - - + + %s maradt vége %s @@ -444,7 +443,6 @@ Lista kiválasztása Lista A hangfájlok mérete 40MB-nál kisebb kell legyen. - Színes átmenet mutatása rejtett médiánál Nincs egy vázlatod sem. Nincs egy ütemezett tülköd sem. A Mastodonban a legrövidebb ütemezhető időintervallum 5 perc. diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index adfa36311..1b34e0e4b 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -206,7 +206,6 @@ Tungumál Birta merki á róbótum Sýna hreyfingar GIF-auðkennismynda - Sýna skæra litstigla í stað falins myndefnis Síun tímalínu Flipar Sýna endurbirtingar @@ -345,14 +344,6 @@ Ekki er víst að upplýsingarnar hér að neðan endurspegli notandasniðið að fullu. Opnaðu fullt notandasnið í vafra. Losa Festa - - - - - - - - Endurbirt af Sett í eftirlæti af %1$s @@ -371,7 +362,6 @@ Beint Könnun með valkostunum: %1$s, %2$s, %3$s, %4$s; %5$s Heiti á lista - Myllumerki án # Veldu lista Listi diff --git a/app/src/main/res/values-kab/strings.xml b/app/src/main/res/values-kab/strings.xml index caa3e7ebd..d8bb6fadf 100644 --- a/app/src/main/res/values-kab/strings.xml +++ b/app/src/main/res/values-kab/strings.xml @@ -9,8 +9,8 @@ Ẓreg amaɣnu Nadi Ɣef - Umuɣen - Umuɣen + Tabdart + Tabdarin Tijewwiqt-ik aṭas i ɣuzzifet! Agejdan Iccaren @@ -22,12 +22,12 @@ Zeṛ ugar Zeṛ kra kan Ulac walu da. - %s yeṭafar-ik-id + %s yeṭṭafar-ik·ikem-id Err Ugar Azen Ḍfeṛ - Ur ṭafaṛ ara + Ur ṭṭafar ara Ẓreg Kkes Kkes tɛiwdeḍ tira @@ -87,7 +87,7 @@ Amaɣnu n Tusky Tugniwin Tibidyutin - Yeṭafaṛ-ik-id + Yeṭṭafar-ik·ikem-id Kkes Lqem Rnu amiḍan @@ -123,21 +123,21 @@ Bḍu aseɣwen ɣer tijewwiqt Rnu amsizdeg Ẓreg amsizdeg - Snulfu-d umuɣ - Snifel isem n wumuɣ - Kkes umuɣ-a - Ẓreg umuɣ-a - Rnu yiwen umiḍan ɣer wummuɣ - Kkes amiḍan seg wumuɣ + Snulfu-d tabdart + Snifel isem n tabdart + Kkes tabdart-a + Ẓreg tabdart-a + Rnu yiwen umiḍan ɣer tabdart + Kkes amiḍan seg tabdart Rnu isefka - Isem n wumuɣ + Isem n tebdart Fren tabdart - Umuɣ + Tabdart Sizdeg Imiḍanen Rnu yiwen wefran - Ccetki ɣef @%s - Ccetki fell-as + Cetki ɣef @%s + Cetki fell-as Ggami Yessidired %1$s Bḍu tugna s… @@ -194,7 +194,7 @@ Kemmel Uɣal - Tella-d tuccḍa deg ccetki + Tella-d tuccḍa deg cetki Tucḍa n unadi Assenqed 5 n tisdidin @@ -205,15 +205,15 @@ 3 n wussan 7 n wussan Tafrant %d - Ig ṭafaṛ + Ig ṭṭafar Imeḍfaṛen - Nadi ɣef medden i teṭafareḍ + Nadi ɣef medden ar at ḍfereḍ Imeḍfaṛen Iseɣwan Tibdarin Tibdarin Iseɣwan - Yettwaceyyaɛ! + Yettwaceyyeɛ! Yettwaceyyaɛ! Ula d yiwen n ugmuḍ I yimeḍfaṛen kan @@ -263,4 +263,9 @@ Cekkel Kkes tacekkalt Tettwakkes tacekkalt ɣef umiḍan-nni + + %s n wemdan + %s n yemdanen + + Bren-it ɣer %s \ No newline at end of file diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index ec37ff11a..e8a86cbf7 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -80,7 +80,7 @@ 다시 시도 닫기 프로필 - 환경설정 + 설정 계정 설정 즐겨찾기 뮤트한 유저 @@ -114,7 +114,7 @@ 멘션 해시태그 부스트한 유저의 프로필로 이동 - 부스트 보이기 + 이 유저의 부스트 보이기 즐겨찾기한 유저 보이기 해시태그 멘션 @@ -132,7 +132,7 @@ 차단이 해제됨 뮤트가 해제됨 %s 숨김 해제됨 - 보냈습니다! + 신고를 보냈습니다! 답장을 보냈습니다. 인스턴스 주소 지금 무엇을 하고 있나요\? @@ -240,7 +240,7 @@ %s님이 당신을 멘션했습니다 %1$s님, %2$s님, %3$s님 외 %4$d명 %1$s님, %2$s님, %3$s님 - %1$s, %2$s + %1$s님, %2$s님 %d개의 새로운 알림이 있습니다 계정 잠김 이 앱에 관하여 @@ -371,7 +371,7 @@ 타임라인에 비표시 비공개 다이렉트 - 투표: %1$s, %2$s, %3$s, %4$s; %5$s + 투표 선택지: %1$s, %2$s, %3$s, %4$s, %5$s 리스트 이름 #를 제외한 해시태그 알림 지우기 diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 1d5789c1c..d5678647b 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -58,4 +58,5 @@ വീണ്ടും ശ്രമിക്കുക പിന്‍തുടരുവാനുള്ള അഭ്യര്‍ത്ഥനകള്‍ മറയ്ക്കപ്പെട്ട ഡൊമൈനുകൾ + പിന്തുടരുന്നവർ \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 6df011e80..3b08e4450 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -53,7 +53,7 @@ %s markeerde jouw toot als favoriet %s volgt jou Rapporteer @%s - Extra opmerkingen + Extra opmerkingen\? Snelle reactie Reageren Boosten @@ -120,8 +120,8 @@ Link kopiëren Als %s openen Delen als … - Link van de toot delen - Inhoud van de toot delen + Link van de toot delen met… + Inhoud van de toot delen met… Media delen met … Verzonden! Gebruiker is gedeblokkeerd @@ -133,7 +133,7 @@ Waarschuwingstekst Weergavenaam Bio - Zoeken + Zoeken… Geen resultaten Reageren… Avatar @@ -142,9 +142,9 @@ Aan het verbinden Het adres of domein van elke Mastodonserver kan hier worden ingevoerd, zoals mastodon.social, mastodon.nl, octodon.social en nog veel meer! \n -\n Wanneer je nog geen account hebt, kun je de naam van de Mastodonserver waar jij je graag wil registeren invoeren, waarna je daar een account kunt aanmaken. +\nWanneer je nog geen account hebt, kun je de naam van de Mastodonserver waar jij je graag wil registeren invoeren, waarna je daar een account kunt aanmaken. \n -\n Een Mastodonserver (Engels: instance) is een computerserver waar jouw account zich bevindt (vergelijk het met een e-mailserver). Je kan eenvoudig mensen van andere servers volgen en met ze communiceren, alsof jullie met elkaar op dezelfde website zitten. +\nEen Mastodonserver (Engels: instance) is een computerserver waar jouw account zich bevindt (vergelijk het met een e-mailserver). Je kan eenvoudig mensen van andere servers volgen en met ze communiceren, alsof jullie met elkaar op dezelfde website zitten. \n \n Meer informatie kun je vinden op joinmastodon.org. Uploaden media bijna voltooid @@ -368,8 +368,8 @@ Weet je zeker dat je alle meldingen permanent wilt verwijderen\? %1$s • %2$s - - + %s stem + %s stemmen %s over eindigt op %s @@ -391,16 +391,16 @@ Poll met keuzes: %1$s, %2$s, %3$s, %4$s; %5$s Acties voor afbeelding %s - - + %d dag + %d dagen - - + %d uur + %d uur - - + %d minuut + %d minuten "%d seconde" @@ -440,7 +440,6 @@ Ingeplande toots Ingeplande toot Herstellen - Wazige kleurovergangen voor verborgen media tonen Powered by Tusky Altijd toots met tekstwaarschuwingen uitklappen Als bladwijzer toegevoegd diff --git a/app/src/main/res/values-no-rNB/strings.xml b/app/src/main/res/values-no-rNB/strings.xml index 3ef19fce2..276f8e05d 100644 --- a/app/src/main/res/values-no-rNB/strings.xml +++ b/app/src/main/res/values-no-rNB/strings.xml @@ -340,8 +340,8 @@ Vil du slette dette tottet og skrive det på nytt\? %1$s • %2$s - - + %s stemme + %s stemmer %s igjen avsluttes %s @@ -433,7 +433,6 @@ Bokmerke lagt til Velg liste Liste - Vis fargegradienter for skjult media Du har ingen planlagte statuser. Du har ikke lagret noen kladder. Lydfiler må være mindre enn 40MB. @@ -452,5 +451,8 @@ Blokkere @%s\? Fjern demping av samtale Demp samtale - % ba om å få følge deg + Stikkord + Legg til stikkord + %s ønsker å følge deg + Vis fargerike gradienter for skjult media \ No newline at end of file diff --git a/app/src/main/res/values-oc/strings.xml b/app/src/main/res/values-oc/strings.xml index 8297c9819..7da7e82fc 100644 --- a/app/src/main/res/values-oc/strings.xml +++ b/app/src/main/res/values-oc/strings.xml @@ -164,7 +164,7 @@ Pòrt del servidor proxy HTTP Privacitat predeterminada dels tuts Publicacion - Publica + Public Pas listada Seguidors solament Talha de text de l\'estatut @@ -332,8 +332,8 @@ %1$s Favorits - - + + Partejat per Aimat per @@ -445,7 +445,6 @@ Seleccionar la list Lista Los fichièrs àudio devon èsser inferiors a 40 Mo. - Mostrar los degradats de colors pels contenguts resconduts Avètz pas cap de borrolhon. Avètz pas cap de tut planificat. L’interval minimum de planificacion sus Mastodon e de 5 minutas. diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index d713a69f2..64ce18e11 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -1,7 +1,7 @@ Wystąpił błąd. - To nie może pozostać puste. + To pole nie może być puste. Wprowadzono nieprawidłową domenę Nie udało się uwierzytelnić z tą instancją. Nie znaleziono przeglądarki internetowej. @@ -293,7 +293,7 @@ Wycisz %s Dodaj zakładkę Otwórz konto osoby podbijającej - Pokazuj podbicia + Pokaż podbicia Otwórz media #%d Pobierz media Pobieranie mediów @@ -368,7 +368,7 @@ Podbity Polubiony Publiczny - Niewidoczny + Niewidoczne Śledzący Bezpośrednio Głosowanie z opcjami: %1$s, %2$s, %3$s, %4$s; %5$s @@ -452,9 +452,14 @@ Wybierz listę Lista Pliki audio muszą być mniejsze niż 40MB. - Pokaż kolorowe gradienty dla ukrytych mediów Nie masz żadnych szkiców. Nie masz żadnych zaplanowanych wpisów. Mastodon umożliwia wysłanie minimalnie 5 minut od zaplanowania Prośby o możliwość śledzenia + Pytaj o potwierdzenie przed podbiciem + Dodaj hashtag + Wyciszyć @%s\? + Zablokować @%s\? + Cofnij wyciszenie rozmowy + Wycisz rozmowę \ No newline at end of file diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 112195676..3b8061bc1 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -4,8 +4,8 @@ Não pode estar vazio. Instância inválida Falha ao autenticar com esta instância. - Navegador não foi encontrado. - Ocorreu um erro não-identificado de autorização. + Nenhum navegador foi encontrado. + Ocorreu um erro não identificado de autorização. Autorização negada. Falha ao adquirir token de entrada. O toot é muito longo! @@ -32,25 +32,25 @@ Favoritos Usuários silenciados Usuários bloqueados - Solicitações de seguidor + Seguidores pendentes Editar seu perfil Rascunhos Licenças - %s compartilhou - Conteúdo sensível + %s deu boost + Mídia sensível Mídia oculta - Clique para exibir + Toque para ver Mostrar mais Mostrar menos Nada aqui. Arraste para atualizar! %s compartilhou o seu toot - %s curtiu o seu toot - %s seguiu você + %s favoritou seu toot + %s te seguiu Denunciar @%s Comentários adicionais? Resposta rápida Responder - Compartilhar + Boost Desfazer boost Favoritar Desfavoritar @@ -76,7 +76,7 @@ Favoritos Usuários silenciados Usuários bloqueados - Solicitações de seguidor + Seguidores pendentes Mídia Abrir no navegador Adicionar mídia @@ -170,7 +170,7 @@ Esconder compositor ao rolar a página Filtro da linha do tempo Abas - Mostrar compartilhamentos + Mostrar boosts Mostrar respostas Mostrar prévias de mídia Proxy @@ -178,7 +178,7 @@ Ativar proxy HTTP Servidor do proxy HTTP Porta do proxy HTTP - Privacidade padrão dos posts + Privacidade padrão dos toots Sempre marcar mídia como sensível Publicação Falha ao sincronizar configurações @@ -190,15 +190,15 @@ Médio Grande Maior - Novas Menções - Notificações sobre novas menções - Novos Seguidores - Notificações sobre novos seguidores + Menções + Notificar sobre novas menções + Seguidores + Notificar sobre novos seguidores Boosts Notificar quando derem boost nos seus toots Favoritos Notificar quando favoritarem seus toots - %s mencionou você + %s te mencionou %1$s, %2$s, %3$s e %4$d outros %1$s, %2$s, e %3$s %1$s e %2$s @@ -237,7 +237,7 @@ %dm %ds Segue você - Sempre mostrar conteúdo sensível + Sempre mostrar mídia sensível Mídia Respondendo a @%s carregar mais @@ -329,8 +329,8 @@ Pesquisar pessoas que você segue Adicionar conta à lista Remover conta da lista - Descrever para deficientes visuais -\n(limite de %d caracteres) + Descrever para deficientes visuais +\n(até %d caracteres) CC-BY 4.0 CC-BY-SA 4.0 As informações abaixo podem refletir incompletamente o perfil do usuário. Toque aqui para abrir o perfil completo no navegador. @@ -403,7 +403,7 @@ Encaminhar para %s Falha na denúncia Falha ao carregar toots - A denúncia será enviada para o seu administrador da instância. Você pode explicar por que você denunciou a conta: + A denúncia será enviada aos moderadores da instância. Você pode explicar por que você denunciou a conta: A conta está em outra instância. Enviar uma cópia anônima da denúncia para lá\? Instâncias bloqueadas Instâncias bloqueadas @@ -430,23 +430,38 @@ Múltiplas opções Opção %d Editar - Toots agendados + Agendados Editar - Toots agendados + Agendados Agendar toot Cancelar Erro ao pesquisar %s Salvos - Salvo + Salvar Salvos Desenvolvido por Tusky Salvo Selecionar lista Lista - Mostrar blur em mídias ocultas Sem toots agendados. Áudios devem ser menores que 40MB. Sem rascunhos. - Mastodon possui um intervalo mínimo de agendamento de 5 minutos. - Solicitações de seguidor - \ No newline at end of file + Mastodon possui um intervalo mínimo de 5 minutos para agendar. + Seguidores pendentes + %s quer te seguir + Silenciar conversa + Desfazer silêncio + Bloquear @%s\? + Silenciar @%s\? + pedirem para me seguir + Notificar sobre seguidores pendentes + + %s pessoa + %s pessoas + + Ativar deslizar para alternar entre abas + Mostrar prévias de links nas linhas + Solicitar confirmação antes de dar boost + Hashtags + Adicionar hashtag + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 2c5ca851a..dfdfd468c 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -471,10 +471,9 @@ Выбрать список Список Аудиофайлы должны быть меньше 40МБ. - Показывать цветные градиенты для скрытых медиа Ошибка поиска поста %s У вас нет черновиков. - У вас нет запланированный постов. + У вас нет запланированных постов. Минимальный интервал планирования в Mastodon составляет 5 минут. Показвать диалог подтверждения перед продвижением Показывать предосмотр ссылок в лентах @@ -493,4 +492,7 @@ Показать обсуждение Заглушить обсуждение запрос на подписку от %s + Тэги + Добавить тэг + Отображать цветные градиенты для скрытых изображений \ No newline at end of file diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 8146b8ba0..2d9dde321 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -37,7 +37,6 @@ Osnutki Licence \@%s - % spodbudil Občutljiva vsebina Medij je skrit Kliknite za ogled @@ -284,7 +283,7 @@ Če želite uveljaviti te spremembe, morate znova zagnati Yuito Kasneje Znova zaženi - Privzeti komplet emotikonov vaše naprave + "Privzeti komplet emotikonov vaše naprave " Blob emotikoni so znani od Android 4.4-7.1 Mastodonov privzeti komplet emotikonov Prenos ni uspel @@ -304,12 +303,7 @@ Spodnje informacije lahko nepopolno odražajo profil uporabnika. Pritisnite, da odprete polni profil v brskalniku. Odpni Pripni - - - - - - + <b>%s</b> Spodbuda <b>%s</b> Spodbudi diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 951b526b5..d6c5db46b 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -450,7 +450,6 @@ Bokmärkt Välj lista Lista - Visa färgglada gradienter för gömd media Du har inga schemalagda statusar. Ljudfiler måste vara mindre än 40MB. Du har inga utkast. @@ -470,4 +469,6 @@ Blockera @%s\? Aktivera ljud på konversation %s vill följa dig + Lägg till hashtag + Hashtaggar \ No newline at end of file diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 8171f4b12..d45b62a06 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -123,7 +123,7 @@ அனைவருக்கும் காட்டாதே பின்பற்றுபவர்களுக்கு மட்டும் காண்பி குறிபிடபட்டுள்ள பயனர்களுக்கு மட்டும் காண்பி - அறிவிப்புகளை திருத்த + அறிவிப்புகள் அறிவிப்புகள் எச்சரிக்கைகள் ஒலி மூலம் தெரிவிக்கவும் @@ -259,7 +259,7 @@ விடுவி பொருத்து கணக்கரின் முன்னுரிமைகள் - பிணைய பிழை ஏற்பட்டது! உங்கள் இணைப்பைச் சரிபார்த்து மீண்டும் முயற்சிக்கவும்! + "பிணைய பிழை ஏற்பட்டது! உங்கள் இணைப்பைச் சரிபார்த்து மீண்டும் முயற்சிக்கவும்!" காணொளி 40MB க்கும் குறைவாக இருக்க வேண்டும். டூத் அனுப்ப இயலவில்லை நேரடி தகவல் diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml new file mode 100644 index 000000000..223cba67b --- /dev/null +++ b/app/src/main/res/values-th/strings.xml @@ -0,0 +1,456 @@ + + + เพิ่มตัวเลือก + 7 วัน + 3 วัน + 1 วัน + 6 ชั่วโมง + 1 ชั่วโมง + 30 นาที + 5 นาที + โพล + เปิดใช้งานการเลื่อนนิ้วเพื่อสลับระหว่างแท็บ + แสดงตัวกรองการแจ้งเตือน + ค้นหาล้มเหลว + บัญชี + บัญชีนี้มาจากเซิร์ฟเวอร์อื่น ส่งสำเนารายงานที่ไม่ระบุชื่อไปที่นั่นด้วยหรือไม่\? + รายงานจะถูกส่งไปยังผู้ดูแลเซิร์ฟเวอร์ของคุณ สามารถให้คำอธิบายว่าทำไมจึงรายงานบัญชีนี้ด้านล่าง: + ดึงข้อมูลสถานะล้มเหลว + รายงานล้มเหลว + ส่งต่อไปยัง %s + ความคิดเห็นเพิ่มเติม + รายงาน @%s เรียบร้อยแล้ว + ทำ + ย้อนกลับ + ต่อไป + + %d วินาที + + + %d นาที + + + %d ชั่วโมง + + + %d วัน + + โพลที่คุณสร้างสิ้นสุดลงแล้ว + โพลที่คุณโหวตสิ้นสุดลงแล้ว + โหวต + สิ้นสุดแล้ว + จบที่ %s + เหลืออีก %s + + %s คน + + + %s โหวต + + " <!-- 15 votes • 1 hour left --> %1$s • %2$s" + %1$s • %2$s + การกระทำสำหรับภาพ %s + ต้องการลบการแจ้งเตือนทั้งหมดอย่างสมบูรณ์\? + เขียน + เขียน Toot + ใช้งาน + คัดกรอง + ล้างการแจ้งเตือน + รายการ + เลือกรายการ + แฮชแท็ก + แฮชแท็กโดยไม่มี # + เพิ่มแฮชแท็ก + ชื่อรายการ + โพลกับตัวเลือก: %1$s, %2$s, %3$s, %4$s; %5$s + ไดเร็กต์ + ผู้ติดตาม + ไม่อยู่ในรายการ + สาธารณะ + คั่นหน้า + ชื่นชอบ + ได้ถูกเขียนใหม่ + ไม่มีคำอธิบาย + เตือนเนื้อหา : %s + สื่อ: %s + ถึงจำนวนแท็บสูงสุดคือ %1$d แล้ว + %1$s, %2$s และอีก %3$d + %1$s และ %2$s + %1$s + ชื่นชอบโดย + บูสต์โดย + + <b>%1$s</b> บูสต์ + + + <b>%1$s</b> ชื่นชอบ + + ปักหมุด + เลิกปักหมุด + ข้อมูลต่อไปนี้อาจไม่ถูกต้อง แตะเพื่อเปิดโปรไฟล์ในเบราว์เซอร์ + แสดงเวลาแบบเที่ยงตรง + เนื้อหา + ป้าย + เพิ่มข้อมูล + ข้อมูลอภิพันธุ์ + CC-BY-SA 4.0 + CC-BY 4.0 + ภายใต้สัญญาอนุญาต Apache License (คัดลอกด้านล่าง) + Tusky มีโค้ดและสินทรัพย์จากโครงการโอเพนซอร์สต่อไปนี้: + ยกเลิกบูสต์ + บูสต์โพสต์ต้นฉบับ + %1$s ได้ย้ายไปที่ : + บอต + ดาวน์โหลดล้มเหลว + ชุดเอโมจิปัจจุบันจากกูเกิล + ชุดเอโมจิจาก Mastodon + ที่รู้จักจาก Android 4.4 ถึง 7.1 ชุดเอโมจิ Blob + ชุดเริ่มต้นในอุปกรณ์คุณ + เริ่มใหม่ + ภายหลัง + จำเป็นต้องเริ่ม Tusky ใหม่ เพื่อใช้การเปลี่ยนแปลงเหล่านี้ + จำเป็นต้องเริ่มแอปใหม่ + เปิด Toot + ขยาย/ย่อทั้งหมด + กำลังค้นหา… + ต้องดาวน์โหลดชุดเอโมจิเหล่านี้ก่อน + ค่าปริยายของระบบ + รูปแบบเอโมจิ + คัดลอกไปยังคลิบบอร์ดแล้ว + Instance %s ไม่มีเอโมจิแบบกำหนดเอง + เขียน + สำเนา Toot บันทึกเป็นฉบับร่างแล้ว + การส่งถูกยกเลิก + ส่ง Toot + การส่ง Toot เกิดข้อผิดผลาด + กำลังส่ง Toot… + บันทึกฉบับร่าง\? + ต้องอนุมัติผู้ติดตามด้วยตัวเอง + ล็อกบัญชี + ลบ + ตั้งคำอธิบายล้มเหลว + ตั้งคำอธิบาย + อธิบายเพื่อผู้บกพร่องทางสายตา +\n(จำกัด %d ตัวอักขระ) + โพสต์ด้วยบัญชี %1$s + ลบบัญชีออกจากรายการ + เพิ่มบัญชีไปใส่รายการ + ค้นหาผู้ติดตาม + แก้ไขรายการ + ลบรายการ + เปลี่ยนชื่อรายการ + สร้างรายการ + ไม่สามารถลบรายการได้ + ไม่สามารถเปลี่ยนชื่อรายการได้ + ไม่สามารถสร้างรายการได้ + ไทม์ไลน์ในรายการ + เพิ่มบัญชี Mastodon ใหม่ + เพิ่มบัญชี + วลีที่ต้องการกรอง + ถ้าคำหลักหรือวลีเป็นอักษรผสมตัวเลข จะใช้ได้ผลเมื่อตรงทั้งคำเท่านั้น + ทั้งคำ + อัปเดต + ลบ + แก้ไขตัวคัดกรอง + เพิ่มตัวคัดกรอง + การสนทนา + ไทม์ไลน์สาธารณะ + โหลดเพิ่ม + ตอบกลับไป @%s + สื่อ + ขยาย Toot ที่มีเครื่องหมายเนื้อหาอ่อนไหวเสมอ + แสดงเนื้อหาอ่อนไหวเสมอ + กำลังติดตามคุณ + %d วินาทีที่แล้ว + %d นาทีที่แล้ว + %d ชั่วโมงที่แล้ว + %d วันที่แล้ว + %d ปีที่แล้ว + ใน %d วินาที + ใน %d นาที + ใน %d ชั่วโมง + ใน %d วัน + ใน %d ปี + กำลังขอติดตาม + วิดีทัศน์ + ภาพ + แบ่งปันลิงก์ Toot + แบ่งปันเนื้อหา Toot + บัญชีทางการของ Tusky + รายงานช่องโหว่ และ ขอฟีเจอร์ (ภาษาอังกฤษ): +\nhttps://github.com/tuskyapp/Tusky/issues + เว็บไซต์โปรเจกต์: +\nhttps://tusky.app + Tusky คือซอฟต์แวร์เสรีและโอเพนซอร์ส <!-- --> ภายใต้สัญญาอนุญาต GNU General Public License Version 3 <!-- -->ดูสัญญาที่ : https://www.gnu.org/licenses/gpl-3.0.ja.html + ขับเคลื่อนด้วย Tusky + Tusky %s + บัญชีไม่สาธารณะ + การโต้ตอบใหม่จำนวน %d + %1$s และ %2$s + %1$s, %2$s, และ %3$s + %1$s, %2$s, %3$s และอีก %4$d คน + %s ตอบกลับคุณ + การแจ้งเตือนเมื่อโพลได้สิ้นสุดลงแล้ว + โพล + การแจ้งเตือนเมื่อ Toot คุณถูกชื่นชอบ + ชื่นชอบ + การแจ้งเตือนเมื่อ Toot คุณถูกบูสต์ + บูสต์ + การแจ้งเตือนคำขอติดตามใหม่ + คำขอติดตาม + การแจ้งเตือนเกี่ยวกับผู้ติดตามใหม่ + ผู้ติดตามใหม่ + การกล่าวถึงใหม่ + การแจ้งเตือนเกี่ยวกับการกล่าวถึงใหม่ + ใหญ่มาก + ใหญ่ + กลาง + เล็ก + เล็กสุด + ขนาดอักษร Toot + เฉพาะผู้ติดตาม + ไม่อยู่ในรายการ + สาธารณะ + ซิงค์การตั้งค่าล้มเหลว + กำลังเผยแพร่ (synced with server) + ใส่เครื่องหมายว่าเป็นสื่ออ่อนไหวเสมอ + ความเป็นส่วนตัวโพสต์ปริยาย + พอร์ตพร็อกซี่ HTTP + เซิร์ฟเวอร์พร็อกซี่ HTTP + เปิดใช้งานพร็อกซี่ HTTP + พร็อกซี่ HTTP + พร็อกซี่ + ดาวน์โหลดตัวอย่างสื่อ + แสดงการตอบกลับ + แสดงบูสต์ + แท็บ + คัดกรองไทม์ไลน์ + อวตาร GIF เคลื่อนไหวได้ + แสดงสัญลักษณ์ว่าเป็นบอต + ภาษา + ซ่อนปุ่มเขียนเมื่อกำลังเลื่อนจอ + ใช้ Chrome Custom Tabs + เบราว์เซอร์ + ใช้ตามแบบระบบ + ปรับตามเวลา + ดำ + สว่าง + มืด + คัดกรอง + ไทม์ไลน์ + ธีมแอป + ลักษณะ + โพลสิ้นสุดแล้ว + โพสต์ถูกชื่นชอบ + โพสต์ถูกบูสต์ + คำขอติดตาม + ติดตาม + กล่าวถึง + แจ้งฉันเมื่อ + แจ้งด้วยเสียง + แจ้งด้วยการสั่น + แจ้งด้วยแสง + เตือน + การแจ้งเตือนแบบ Push + ตั้งค่าการแจ้งเตือน + ไดเร็กต์:โพสต์ให้เฉพาะผู้ที่ถูกกล่าวถึงเห็น + เฉพาะผู้ติดตาม:โพสต์ให้เฉพาะผู้ติดตามเห็น + ไม่อยู่ในรายการ:ไม่แสดงในไทม์ไลน์สาธารณะ + สาธารณะ:โพสต์ในไทม์ไลน์สาธารณะ + ปิดเสียง @%s\? + บล็อก @%s\? + ซ่อนทั้งโดเมน + ต้องการบล็อกทุกอย่างจาก %s \? คุณจะไม่เห็นเนื้อหาจากโดเมนนั้นในไทม์ไลน์สาธารณะหรือในการแจ้งเตือน ผู้ติดตามของคุณจากโดเมนนั้นจะถูกลบออก + ลบ แล้ว ร่าง Toot นี้ใหม่\? + ลบ Toot นี้\? + เลิกติดตามผู้ใช้นี้\? + ยกเลิกคำขอติดตาม\? + ดาวน์โหลด + กำลังอัปโหลด… + กำลังอัปโหลดสื่อใกล้เสร็จ + "ใส่ที่อยู่หรือโดเมนของ Instance ได้ที่นี่ เช่น mastodon.social icosahedron.website social.tchncs.de และ <a href=\"https://instances.social\">อีกมากมาย!</a> +\n +\nถ้ายังไม่มีบัญชี สามารถใส่ชื่อ Instance ที่ต้องการจะร่วมแล้วสร้างบัญชีที่นั่น +\n +\nInstance คือที่ที่หนึ่งไว้โฮสต์บัญชีคุณ แต่คุณยังสามารถสื่อสาร ติดตามบุคคลบน Instance อื่นได้เหมือนอยู่บนไซต์เดียวกัน +\n +\nพบข้อมูลเพิ่มเติมได้ที่ <a href=\"https://joinmastodon.org\">joinmastodon.org</a> "more! + \n\nIf you don\'t yet have an account, you can enter the name of the instance you\'d like to + join and create an account there.\n\nAn instance is a single place where your account is + hosted, but you can easily communicate with and follow folks on other instances as though + you were on the same site. + \n\nMore info can be found at joinmastodon.org. + + ภาพหัวบน + กำลังเชื่อมต่อ… + อวตาร + ตอบกลับ… + ไม่มีผลลัพธ์ + ค้นหา… + ข้อมูลส่วนตัว + ชื่อที่ใช้แสดง + คำเตือนเนื้อหา + เกิดอะไรขึ้นเอย\? + Instance ไหน\? + ตอบกลับสำเร็จ + ส่งแล้ว! + เลิกซ่อน %s แล้ว + เลิกบล็อกผู้ใช้แล้ว + เลิกปิดเสียงผู้ใช้นี้แล้ว + ส่งแล้ว! + แบ่งปันสื่อไป… + แบ่งปัน Toot ไป… + แชร์ URL Toot ไป… + กำลังดาวน์โหลดสื่อ + ดาวน์โหลดสื่อ + แบ่งปันโดย… + เปิดเป็น %s + คัดลอกลิงก์ + กำลังดาวน์โหลด %1$s + เปิดสื่อ #%d + ลิงก์ + โต้ตอบ + แฮชแท็ก + ดูชื่นชอบ + ดูบสต์ + ดูต้นตอบูสต์ + แฮชแท็ก + โต้ตอบ + ลิงก์ + เพิ่มแท็บ + Toot แบบตั้งเวลา + คีย์บอร์ดเอโมจิ + เตือนเนื้อหา + การมองเห็น Toot + Toot แบบตั้งเวลา + ฉบับร่าง + ปฏิเสธ + ยอมรับ + ย้อนการกระทำ + แก้ไข + บันทึก + เปิดเมนู + ซ่อนสื่อ + กล่าวถึง + เลิกปิดเสียงการสนทนา + ปิดเสียงการสนทนานี้ + ปิดเสียง %s + เลิกปิดเสียง + ปิดเสียง + แบ่งปัน + ถ่ายภาพ + เพิ่มโพล + เพิ่มสื่อ + เปิดในเบราว์เซอร์ + สื่อ + คำขอติดตาม + โดเมนที่ซ่อนไว้ + ผู้ใช้ที่ถูกบล็อกไว้ + ผู้ใช้ที่ปิดเสียงไว้ + คั่นหน้า + ชื่นชอบ + โปรไฟล์ + ปิด + ลองอีกครั้ง + TOOT! + TOOT + ลบ แล้ว ร่างใหม่ + ลบ + แก้ไข + รายงาน + แสดงบูสต์ + ซ่อนบูสต์ + เลิกบล็อก + บล็อก + เลิกติดตาม + ติดตาม + ต้องการออกจากระบบของบัญชี %1$s \? + เขียนโพสต์ใหม่ + อื่น ๆ + เลิกชื่นชอบ + คั่นหน้า + ชื่นชอบ + ลบบูสต์ + บูสต์ + ตอบกลับ + ตอบกลับด่วน + ความคิดเห็นเพิ่มเติม\? + รายงาน @%s + %s ต้องการติดตามคุณ + %s ได้ติดตามคุณ + %s ได้ชื่นชอบ Toot คุณ + %s ได้บูสต์ Toot คุณ + ไม่อะไรเลย ลากลงเพื่อรีเฟรช! + ไม่มีอะไร + ย่อ + ขยาย + แสดงน้อยลง + แสดงเพิ่มเติม + แตะเพื่อดู + สื่อที่ซ่อนไว้ + เนื้อหาอ่อนไหว + %s ได้บูสต์ + \@%s + สัญญาอนุญาต + Toot แบบกำหนดเวลา + แก้ไขโปรไฟล์ + คำขอติดตาม + โดเมนที่ซ่อนไว้ + ผู้ใช้ที่ถูกบล็อก + ผู้ใช้ที่ทำให้เป็นใบ้ + คั่นหน้า + ผู้ติดตาม + ติดตาม + ปักหมุด + โพสต์และตอบกลับ + โพสต์ + เธรด + แท็บ + ข้อความแบบไดเร็กต์ + สหพันธ์ + ท้องถิ่น + แจ้งเตือน + หน้าหลัก + การส่ง Toot เกิดความผิดพลาด + อัปโหลดล้มเหลว + ไม่สามารถแนบรูปภาพและวิดีทัศน์ในโพสต์เดียวกันได้ + ต้องมีสิทธิ์เขียนบนสื่อ + ต้องมีสิทธิ์อ่านสื่อ + ไม่สามารถเปิดไฟล์ได้ + ไม่สามารถอัปโหลดไฟล์ประเภทนี้ได้ + ไฟล์เสียงต้องมีขนาดน้อยกว่า 40MB + ไฟล์วีดิทัศน์ต้องมีขนาดน้อยกว่า 40MB + ไฟล์ต้องมีขนาดน้อยกว่า 8MB + ข้อความสถานะยาวเกินไป! + ไม่สามารถรับโทเค็นการเข้าสู่ระบบ + การขออนุญาตสิทธิถูกปฏิเสธ + เกิดข้อผิดพลาดในการขออนุญาตสิทธิโดยไม่ทราบสาเหตุ + ไม่พบเว็บเบราว์เซอร์ + โดเมนที่ป้อนไม่ถูกต้อง + ไม่สามารถโพสต์โดยไร้ข้อความได้ + เครือข่ายมีข้อผิดพลาดเกิดขึ้น! กรุณาตรวจสอบการเชื่อมต่อและลองอีกครั้ง! + เกิดข้อผิดพลาด + รายการ + รายการ + เกี่ยวกับแอปนี้ + ล้างค่า + ค้นหา + แก้ไขโปรไฟล์ + ตั้งค่าบัญชี + ตั้งค่า + ออกจากระบบ + ฉบับร่าง + ชื่นชอบ + การยืนยันตัวตนทางอิเล็กทรอนิกส์กับ Instance นั้นล้มเหลว + Instance คือ\? + เข้าสู่ระบบด้วย Mastodon + เลือกได้หลายตัวเลือก + แสดงข้อความยืนยันก่อนที่จะบูสต์ + แสดงตัวอย่างลิงก์ในไทม์ไลน์ + Mastodon กำหนดเวลาขั้นต่ำ 5 นาที + ไม่มีสถานะแบบตั้งเวลาใด ๆ + ไม่มีฉบับร่างใด ๆ + การค้นหาโพสต์ %s เกิดข้อผิดผลาด + แก้ไข + ตัวเลือกที่ %d + \ No newline at end of file diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 616fd1a73..ccb8dd292 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -184,7 +184,7 @@ Liste dışı Sadece takipçiler Durum metin boyutu - En küçük + Çok küçük Küçük Orta Büyük @@ -392,7 +392,7 @@ Ek Yorumlar %s adresine ilet Durumlar getirilemedi - "Bildirim sunucu yöneticinize gönderilecektir. Bu hesabı neden bildirdiğinle ilgili açıklama yapabilirsin:" + "Bildirim sunucu yöneticinize gönderilecektir. Bu hesabı neden bildirdiğinizle ilgili açıklama yapabilirsiniz:" Hesap başka bir sunucudan. Raporun anonim bir kopyasını da oraya gönderilsin mi\? Bildirim filtresini göster Bahsedenler @@ -433,7 +433,6 @@ Alt Metin %s gizleme Alan adından herşeyi gizle - Gizli medya için renkli degradeleri göster Hassas içerikleri göster bitiş %s Bildirilemedi diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 8e47e2087..eb1b9ed1f 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -108,4 +108,6 @@ Попередження про контент Змінити Написати + Скасувати приглушення бесіди + Приглушити бесіду \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index de0cd94eb..065110c37 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -413,14 +413,11 @@ 书签 隐藏域名 新增意见调查 - 隐藏 定时嘟文 定时嘟文 %s 已解除静音 - 您确定要封锁 %s 域名吗?您将不会在任何联邦时间轴或通知中看到该域名中的内容,并且来自该域名的关注者将被删除。 隐藏整个域 动画GIF头像 - 显示隐藏媒体的彩色渐变 由Tusky提供支持 始终扩展标有内容警告的嘟文 整个单词 @@ -430,11 +427,10 @@ 使用以下选项创建投票:%1$s, %2$s, %3$s, %4$s; %5$s 选择清单 清单 - 图片 %s 的动作 继续 返回 完成 - 成功回报 @%s + "成功回报 @%s" 附加留言 转发到 %s 回报失败 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 58f5a373e..6d5858df7 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -20,7 +20,7 @@ 媒體檔案上傳失敗 嘟文發送時出錯 主頁 - 通知 + 通知設定 本站時間軸 跨站公開時間軸 私信 @@ -30,7 +30,7 @@ 嘟文和回覆 已置頂 正在關注 - 僅關注者 + 關注者 我的收藏 被靜音的使用者 被封鎖的使用者 @@ -104,7 +104,7 @@ 搜尋 草稿 設定嘟文可見範圍 - 設定敏感內容警告 + 敏感內容警告 插入表情符號 新增標籤頁 連結 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index bbffc0a91..243b10bc0 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -48,4 +48,6 @@ 72dp 108dp + + 16dp diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index e0f0ec743..9be13e33d 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -110,6 +110,15 @@ ja + + @string/pref_main_nav_position_option_top + @string/pref_main_nav_position_option_bottom + + + + top + bottom + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 73fd2d52d..036ca7ff0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -258,7 +258,7 @@ Language Show indicator for bots Animate GIF avatars - Show colorful gradients for hidden media + Show colorful gradients for hidden media Timeline filtering Tabs @@ -276,6 +276,11 @@ Publishing (synced with server) Failed to sync settings + Main navigation position + Top + Bottom + + Public Unlisted Followers-only diff --git a/app/src/main/res/xml/account_preferences.xml b/app/src/main/res/xml/account_preferences.xml deleted file mode 100644 index ec4d2c2ae..000000000 --- a/app/src/main/res/xml/account_preferences.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/xml/http_proxy_preferences.xml b/app/src/main/res/xml/http_proxy_preferences.xml deleted file mode 100644 index fef294266..000000000 --- a/app/src/main/res/xml/http_proxy_preferences.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/notification_preferences.xml b/app/src/main/res/xml/notification_preferences.xml deleted file mode 100644 index fa03f8d1b..000000000 --- a/app/src/main/res/xml/notification_preferences.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml deleted file mode 100644 index 90bc2ca0a..000000000 --- a/app/src/main/res/xml/preferences.xml +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/xml/timeline_filter_preferences.xml b/app/src/main/res/xml/timeline_filter_preferences.xml deleted file mode 100644 index bbe749d02..000000000 --- a/app/src/main/res/xml/timeline_filter_preferences.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/build.gradle b/build.gradle index 63e793f84..828a4159b 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.6.3' + classpath 'com.android.tools.build:gradle:4.0.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/fastlane/metadata/android/ca/changelogs/72.txt b/fastlane/metadata/android/ca/changelogs/72.txt new file mode 100644 index 000000000..694f8b70d --- /dev/null +++ b/fastlane/metadata/android/ca/changelogs/72.txt @@ -0,0 +1,10 @@ +Tusky v11.0 + +- Notificacions sobre les noves sol·licituds de seguiment quan el compte està bloquejat +- Noves funcions que es poden commutar a la pantalla de preferències: + - desactivar el desplaçament entre pestanyes + - mostrar un diàleg de confirmació abans d'incorporar un toot + - mostrar les previsualitzacions d'enllaços en els terminis +- Ara es poden silenciar les converses +- Els resultats de les enquestes ara es calcularan en funció del nombre de votants i no del nombre de vots +- .. diff --git a/fastlane/metadata/android/es/changelogs/72.txt b/fastlane/metadata/android/es/changelogs/72.txt new file mode 100644 index 000000000..f56782d94 --- /dev/null +++ b/fastlane/metadata/android/es/changelogs/72.txt @@ -0,0 +1,9 @@ +Tusky v11.0 + +- Notificaciones sobre nuevas solicitudes de seguimiento cuando su cuenta está bloqueada +- Nuevas funciones que se pueden alternar en la pantalla de Preferencias: + - deshabilitar deslizar entre pestañas + - muestra un diálogo de confirmación antes de dar un toque + - Mostrar vistas previas de enlaces en líneas de tiempo +- Las conversaciones ahora se pueden silenciar +- Los resultados de la encuesta ahora se calcularán en función de la cantidad de votantes y no de la cantidad t diff --git a/fastlane/metadata/android/es/full_description.txt b/fastlane/metadata/android/es/full_description.txt new file mode 100644 index 000000000..ab3edf81f --- /dev/null +++ b/fastlane/metadata/android/es/full_description.txt @@ -0,0 +1,12 @@ +Tusky es un cliente ligero para Mastodon, un servidor de red social gratuito y de código abierto. + +• Material Design +• La mayoría de las API de Mastodon están implementadas +• Soporte de múltiples cuentas +• Tema oscuro y claro con la posibilidad de cambiar automáticamente según la hora del día +• Borradores - crear los toots y guárdalos para más adelante. +• Elige entre diferentes estilos de emojis +• Optimizado para todos los tamaños de pantalla. +• Completamente de código abierto: sin dependencias no libres como los servicios de Google + +Para obtener más información sobre Mastodon, visite https://joinmastodon.org/ diff --git a/fastlane/metadata/android/es/short_description.txt b/fastlane/metadata/android/es/short_description.txt new file mode 100644 index 000000000..88fb95824 --- /dev/null +++ b/fastlane/metadata/android/es/short_description.txt @@ -0,0 +1 @@ +Un cliente multicuentas para la red social Mastodon diff --git a/fastlane/metadata/android/es/title.txt b/fastlane/metadata/android/es/title.txt new file mode 100644 index 000000000..d3f12e307 --- /dev/null +++ b/fastlane/metadata/android/es/title.txt @@ -0,0 +1 @@ +Turky diff --git a/fastlane/metadata/android/fa/changelogs/58.txt b/fastlane/metadata/android/fa/changelogs/58.txt index 60e3f3a2d..74993285c 100644 --- a/fastlane/metadata/android/fa/changelogs/58.txt +++ b/fastlane/metadata/android/fa/changelogs/58.txt @@ -8,4 +8,3 @@ - سبک جدید «پیروی از سبک سیستم» در تنظیمات - بهبود دسترس‌پذییری خط‌زمانی - تاسکی اکنون اعلان‌های ناشناس را نادیده می‌گید و دیگر فرونمی‌پاشد -- diff --git a/fastlane/metadata/android/fr/changelogs/61.txt b/fastlane/metadata/android/fr/changelogs/61.txt index 92985f62c..10fc025c2 100644 --- a/fastlane/metadata/android/fr/changelogs/61.txt +++ b/fastlane/metadata/android/fr/changelogs/61.txt @@ -1,7 +1,7 @@ Tusky v7.0 - Supporte l'affichage, le vote et les notifications des sondages -- Nouveaux boutons pour filtrer l'onglet des notifications et effacer toutes les notifications -- "Effacer & réécrire" vos propres pouets -- Nouvel indicateur pour les profils robots (peut-être désactivé dans les préférences) +- Nouveaux boutons pour filtrer l’onglet des notifications et effacer toutes les notifications +- « Effacer & réécrire » vos propres pouets +- Nouvel indicateur qui montre si un compte est un robot sur l’image de profil (peut être désactivé dans les préférences) - Nouvelles traductions : Norvégien Bokmål et Slovène. diff --git a/fastlane/metadata/android/fr/changelogs/67.txt b/fastlane/metadata/android/fr/changelogs/67.txt index 1c1f62188..bd4cd18f6 100644 --- a/fastlane/metadata/android/fr/changelogs/67.txt +++ b/fastlane/metadata/android/fr/changelogs/67.txt @@ -1,9 +1,9 @@ Tusky v9.0 -- Vous pouvez désormais créer des sondages depuis Tusky +- Vous pouvez désormais créer des sondages dans Tusky - Recherche améliorée -- Nouvelle option dans les préférences du compte pour toujours étendre les avertissements de contenu -- Les avatars dans le menu de navigation ont désormais une forme de -- Il est désormais possible de signaler des utilisateurs même si ils n'ont jamais posté de status -- Tusky refuse désormais de se connecté via les connections cleartext sur Android 6+ -- Plein de petite corrections de bugs et d'améliorations +- Nouvelle option dans les Préférences du compte pour toujours déplier les avertissements +- Les avatars dans le menu de navigation ont désormais une forme de rectangle arrondi +- Il est désormais possible de signaler des utilisateurs même si ils n'ont jamais posté de statut +- Tusky refuse désormais de se connecter via une connexion non-sécurisée sur Android 6+ +- Plein de petites améliorations et corrections diff --git a/fastlane/metadata/android/fr/changelogs/68.txt b/fastlane/metadata/android/fr/changelogs/68.txt index ae5347589..f3e1b3ffa 100644 --- a/fastlane/metadata/android/fr/changelogs/68.txt +++ b/fastlane/metadata/android/fr/changelogs/68.txt @@ -1,3 +1,3 @@ Tusky v9.1 -Cette mise à jour assure la compatibilité avec Mastodon 3 et améliore les perfomances et la stabilité. +Cette mise à jour assure la compatibilité avec Mastodon 3 et améliore les performances et la stabilité. diff --git a/fastlane/metadata/android/fr/full_description.txt b/fastlane/metadata/android/fr/full_description.txt index bcb624d8f..891faf371 100644 --- a/fastlane/metadata/android/fr/full_description.txt +++ b/fastlane/metadata/android/fr/full_description.txt @@ -2,11 +2,11 @@ Tusky est un client léger pour Mastodon, un serveur de réseau social libre et • Design Material • La plupart de l'API Mastodon est implémentée -• Support multi-comptes -• Thèmes sombre et clair disponibles avec possibilité de basculer automatiquement entre les thèmes en fonction de l'heure -• Brouillons - rédigez des pouets et enregistrez les sous forme de brouillons pour les publier plus tard -• Possibilité de choisir différents styles d'émojis +• Support du multi-compte +• Thèmes sombre et clair avec possibilité de basculer automatiquement en fonction de l'heure +• Brouillons — rédigez des pouets et enregistrez-les pour plus tard +• Choisissez parmi différents styles d'émojis • Optimisé pour toutes les tailles d'écrans -• Entièrement ouvert, aucune dépendance des services Google +• Entièrement ouvert — aucune dépendance non libre comme les services Google -Pour en savoir plus à propos de Mastodon : https://joinmastodon.org/ (Anglais) +Pour en apprendre plus à propos de Mastodon, visitez https://joinmastodon.org/ (anglais) diff --git a/fastlane/metadata/android/fr/short_description.txt b/fastlane/metadata/android/fr/short_description.txt index 2c469b86e..bbc2f4e32 100644 --- a/fastlane/metadata/android/fr/short_description.txt +++ b/fastlane/metadata/android/fr/short_description.txt @@ -1 +1 @@ -Un client multi-comptes pour le réseau social Mastodon +Un client multi-compte pour le réseau social Mastodon diff --git a/fastlane/metadata/android/pl/changelogs/72.txt b/fastlane/metadata/android/pl/changelogs/72.txt new file mode 100644 index 000000000..9faee2f38 --- /dev/null +++ b/fastlane/metadata/android/pl/changelogs/72.txt @@ -0,0 +1,11 @@ +Tusky v11.0 + +- Powiadomienia o nowych prośbach o obserwowanie kiedy Twoje konto jest zablokowane +- Nowe funkcje, które mogą być zmienione w ekranie Ustawień: + - wyłącz przesuwanie pomiędzy kartami + - pokaż potwierdzenie przed podbiciem + - pokaż podgląd linku na osi czasu +- Rozmowy mogą być teraz wyciszone +- Wyniki ankiet są teraz przeliczane na podstawie liczby głosujących, a nie łącznej liczbie wszystkich głosów +- Dużo poprawek błędów, większość z nich dotyczy wpisów +- Poprawiono tłumaczenia diff --git a/fastlane/metadata/android/sl/short_description.txt b/fastlane/metadata/android/sl/short_description.txt index c1b4b901f..01bd82c7b 100644 --- a/fastlane/metadata/android/sl/short_description.txt +++ b/fastlane/metadata/android/sl/short_description.txt @@ -1 +1 @@ -Odjemalec z več računi za socialno omrežje Mastodon +Odjemalec z več računi za socialno omrežje Mastodon. diff --git a/fastlane/metadata/android/th/changelogs/72.txt b/fastlane/metadata/android/th/changelogs/72.txt new file mode 100644 index 000000000..f9cbe07e4 --- /dev/null +++ b/fastlane/metadata/android/th/changelogs/72.txt @@ -0,0 +1,11 @@ +Tusky v11.0 + +- การแจ้งเตือนเกี่ยวกับคำขอติดตามใหม่หากบัญชีของคุณไม่เป็นสาธารณะ(ล็อก) +- คุณสมบัติใหม่ที่สามารถ toggled ได้ในหน้าจอการตั้งค่า: + - ปิดการใช้งานการปัดระหว่างแท็บ + - แสดงข้อความยืนยันก่อนที่จะบูสต์ Toot + - แสดงตัวอย่างลิงก์ในไทม์ไลน์ +- สามารถปิดเสียงการสนทนาได้ในขณะนี้ (ไม่สนใจการส) +- ผลลัพธ์โพลจะคำนวณตามจำนวนผู้โหวตและไม่ได้อยู่ในคะแนนรวม ทำให้โพลหลายตัวเลือกง่ายต่อการเข้าใจ +- แก้ไขบั๊กจำนวนมาก ส่วนใหญ่เกี่ยวข้องกับการเขียน Toot +- ปรับปรุงการแปล diff --git a/fastlane/metadata/android/th/full_description.txt b/fastlane/metadata/android/th/full_description.txt new file mode 100644 index 000000000..f4d1e2fca --- /dev/null +++ b/fastlane/metadata/android/th/full_description.txt @@ -0,0 +1,12 @@ +Tusky เป็นไคลเอ็นต์ที่มีน้ำหนักเบาสำหรับ Mastodon ซึ่งเป็นเซิร์ฟเวอร์เครือข่ายโซเชียลที่ฟรีและโอเพนซอร์ส + +• Material Design +• Most Mastodon APIs implemented +• รองรับการใช้งานแบบหลายบัญชี +• ชุดรูปแบบมืดและสว่าง สามารถสลับอัตโนมัติตามเวลาของวัน +• แบบร่าง - เขียน Toot และบันทึกไว้เพื่อใช้ในภายหลัง +• เลือกรูปแบบเอโมจิที่แตกต่างกันได้ +• ปรับให้เหมาะสมสำหรับทุกขนาดหน้าจอ +• โอเพ่นซอร์สสมบูรณ์ - ไม่มีการขึ้นต่อกันอย่างไม่อิสระ เช่น บริการ Google ฯลฯ + +หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับ Mastodon โปรดเยี่ยมชมที่ https://joinmastodon.org/ diff --git a/fastlane/metadata/android/th/short_description.txt b/fastlane/metadata/android/th/short_description.txt new file mode 100644 index 000000000..8d8a359a9 --- /dev/null +++ b/fastlane/metadata/android/th/short_description.txt @@ -0,0 +1 @@ +ไคลเอ็นต์แบบหลายบัญชีสำหรับเครือข่ายสังคม Mastodon diff --git a/fastlane/metadata/android/th/title.txt b/fastlane/metadata/android/th/title.txt new file mode 100644 index 000000000..0238ffc0a --- /dev/null +++ b/fastlane/metadata/android/th/title.txt @@ -0,0 +1 @@ +Tusky diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index f3d88b1c2..490fda857 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6623300be..186b71557 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists