Merge remote-tracking branch 'tuskyapp/develop'

This commit is contained in:
kyori19 2020-12-16 23:47:26 +09:00
commit ea822004dd
71 changed files with 665 additions and 370 deletions

View File

@ -58,6 +58,7 @@ android {
productFlavors {
blue {}
green {
resValue "string", "app_name", APP_NAME + " Test"
applicationIdSuffix ".test"
versionNameSuffix "-" + getGitSha()
}
@ -105,10 +106,10 @@ project.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
ext.lifecycleVersion = "2.2.0"
ext.roomVersion = '2.2.5'
ext.retrofitVersion = '2.9.0'
ext.okhttpVersion = '4.8.1'
ext.okhttpVersion = '4.9.0'
ext.glideVersion = '4.11.0'
ext.daggerVersion = '2.28.3'
ext.materialdrawerVersion = '8.1.8'
ext.daggerVersion = '2.30.1'
ext.materialdrawerVersion = '8.2.0'
repositories {
maven {
@ -120,22 +121,22 @@ repositories {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.core:core-ktx:1.3.1"
implementation "androidx.core:core-ktx:1.3.2"
implementation "androidx.appcompat:appcompat:1.2.0"
implementation "androidx.fragment:fragment-ktx:1.2.5"
implementation "androidx.browser:browser:1.2.0"
implementation "androidx.browser:browser:1.3.0"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation "androidx.exifinterface:exifinterface:1.2.0"
implementation "androidx.exifinterface:exifinterface:1.3.2"
implementation "androidx.cardview:cardview:1.0.0"
implementation "androidx.preference:preference:1.1.1"
implementation "androidx.preference:preference-ktx:1.1.1"
implementation "androidx.sharetarget:sharetarget:1.0.0"
implementation "androidx.emoji:emoji:1.1.0"
implementation "androidx.emoji:emoji-appcompat:1.1.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycleVersion"
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
implementation "androidx.paging:paging-runtime-ktx:2.1.2"
implementation "androidx.viewpager2:viewpager2:1.0.0"
implementation "androidx.work:work-runtime:2.4.0"
@ -151,13 +152,14 @@ dependencies {
implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
implementation "com.squareup.okhttp3:logging-interceptor:$okhttpVersion"
implementation "com.squareup.okhttp3:okhttp-tls:$okhttpVersion"
implementation "org.conscrypt:conscrypt-android:2.4.0"
implementation "org.conscrypt:conscrypt-android:2.5.1"
implementation "com.github.bumptech.glide:glide:$glideVersion"
implementation "com.github.bumptech.glide:okhttp3-integration:$glideVersion"
implementation "io.reactivex.rxjava2:rxjava:2.2.19"
implementation "io.reactivex.rxjava2:rxjava:2.2.20"
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
implementation "io.reactivex.rxjava2:rxkotlin:2.4.0"
@ -170,7 +172,7 @@ dependencies {
implementation "com.google.dagger:dagger-android-support:$daggerVersion"
kapt "com.google.dagger:dagger-android-processor:$daggerVersion"
implementation "com.github.connyduck:sparkbutton:4.0.0"
implementation "com.github.connyduck:sparkbutton:4.1.0"
implementation "com.github.chrisbanes:PhotoView:2.3.0"
@ -182,14 +184,14 @@ dependencies {
implementation "de.c1710:filemojicompat:1.0.17"
testImplementation "androidx.test.ext:junit:1.1.1"
testImplementation "org.robolectric:robolectric:4.3.1"
testImplementation "org.mockito:mockito-inline:3.3.3"
testImplementation "androidx.test.ext:junit:1.1.2"
testImplementation "org.robolectric:robolectric:4.4"
testImplementation "org.mockito:mockito-inline:3.6.28"
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
androidTestImplementation "androidx.test.espresso:espresso-core:3.2.0"
androidTestImplementation "androidx.test.espresso:espresso-core:3.3.0"
androidTestImplementation "androidx.room:room-testing:$roomVersion"
androidTestImplementation "androidx.test.ext:junit:1.1.1"
androidTestImplementation "androidx.test.ext:junit:1.1.2"
implementation 'net.accelf:easter:1.0.2'
}

View File

@ -47,6 +47,7 @@ import androidx.lifecycle.Lifecycle
import androidx.preference.PreferenceManager
import androidx.viewpager2.widget.MarginPageTransformer
import com.bumptech.glide.Glide
import com.bumptech.glide.RequestManager
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.target.FixedSizeDrawable
@ -129,6 +130,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
private val preferences by lazy { PreferenceManager.getDefaultSharedPreferences(this) }
private lateinit var glide: RequestManager
private val emojiInitCallback = object : InitCallback() {
override fun onInitialized() {
if (!isDestroyed) {
@ -139,7 +142,9 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (accountManager.activeAccount == null) {
val activeAccount = accountManager.activeAccount
if (activeAccount == null) {
// will be redirected to LoginActivity by BaseActivity
return
}
@ -157,11 +162,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}
}
val accountRequested = accountId != -1L
if (accountRequested) {
val account = accountManager.activeAccount
if (account == null || accountId != account.id) {
accountManager.setActiveAccount(accountId)
}
if (accountRequested && accountId != activeAccount.id) {
accountManager.setActiveAccount(accountId)
}
if (canHandleMimeType(intent.type)) {
// Sharing to Tusky from an external app
@ -173,8 +175,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
showAccountChooserDialog(getString(R.string.action_share_as), true, object : AccountSelectionListener {
override fun onAccountSelected(account: AccountEntity) {
val requestedId = account.id
val activeAccount = accountManager.activeAccount
if (activeAccount != null && requestedId == activeAccount.id) {
if (requestedId == activeAccount.id) {
// The correct account is already active
forwardShare(intent)
} else {
@ -193,14 +194,15 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
window.statusBarColor = Color.TRANSPARENT // don't draw a status bar, the DrawerLayout and the MaterialDrawerLayout have their own
setContentView(R.layout.activity_main)
glide = Glide.with(this)
viewQuickToot.attachViewModel(quickTootViewModel, this)
composeButton.setOnClickListener(viewQuickToot::onFABClicked)
val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false)
mainToolbar.visible(!hideTopToolbar)
val navIconSize = resources.getDimensionPixelSize(R.dimen.avatar_toolbar_nav_icon_size)
mainToolbar.navigationIcon = FixedSizeDrawable(getDrawable(R.drawable.avatar_default), navIconSize, navIconSize)
loadDrawerAvatar(activeAccount.profilePictureUrl, true)
mainToolbar.menu.add(R.string.action_search).apply {
setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
@ -256,8 +258,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
viewQuickToot.handleEvent(event)
}
// Flush old media that was cached for sharing
deleteStaleCachedMedia(applicationContext.getExternalFilesDir("Tusky"))
Schedulers.io().scheduleDirect {
// Flush old media that was cached for sharing
deleteStaleCachedMedia(applicationContext.getExternalFilesDir("Tusky"))
}
}
override fun onResume() {
@ -381,13 +385,11 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) {
if (animateAvatars) {
Glide.with(imageView.context)
.load(uri)
glide.load(uri)
.placeholder(placeholder)
.into(imageView)
} else {
Glide.with(imageView.context)
.asBitmap()
glide.asBitmap()
.load(uri)
.placeholder(placeholder)
.into(imageView)
@ -395,7 +397,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}
override fun cancel(imageView: ImageView) {
Glide.with(imageView.context).clear(imageView)
glide.clear(imageView)
}
override fun placeholder(ctx: Context, tag: String?): Drawable {
@ -814,29 +816,11 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}
private fun onFetchUserInfoSuccess(me: Account) {
Glide.with(this)
.asBitmap()
glide.asBitmap()
.load(me.header)
.into(header.accountHeaderBackground)
val navIconSize = resources.getDimensionPixelSize(R.dimen.avatar_toolbar_nav_icon_size)
Glide.with(this)
.asDrawable()
.override(navIconSize)
.load(me.avatar)
.transform(
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp))
)
.into(object : CustomTarget<Drawable>() {
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
mainToolbar.navigationIcon = resource
}
override fun onLoadCleared(placeholder: Drawable?) {
mainToolbar.navigationIcon = placeholder
}
})
loadDrawerAvatar(me.avatar, false)
accountManager.updateActiveAccount(me)
NotificationHelper.createNotificationChannelsForAccount(accountManager.activeAccount!!, this)
@ -861,6 +845,36 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
updateShortcut(this, accountManager.activeAccount!!)
}
private fun loadDrawerAvatar(avatarUrl: String, showPlaceholder: Boolean) {
val navIconSize = resources.getDimensionPixelSize(R.dimen.avatar_toolbar_nav_icon_size)
glide.asDrawable()
.load(avatarUrl)
.transform(
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp))
)
.apply {
if (showPlaceholder) {
placeholder(R.drawable.avatar_default)
}
}
.into(object : CustomTarget<Drawable>(navIconSize, navIconSize) {
override fun onLoadStarted(placeholder: Drawable?) {
if(placeholder != null) {
mainToolbar.navigationIcon = FixedSizeDrawable(placeholder, navIconSize, navIconSize)
}
}
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
mainToolbar.navigationIcon = resource
}
override fun onLoadCleared(placeholder: Drawable?) {
mainToolbar.navigationIcon = placeholder
}
})
}
private fun fetchAnnouncements() {
mastodonApi.listAnnouncements(false)
.observeOn(AndroidSchedulers.mainThread())
@ -877,7 +891,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}
private fun updateAnnouncementsBadge() {
mainDrawer.updateBadge(DRAWER_ITEM_ANNOUNCEMENTS, StringHolder(if (unreadAnnouncementsCount == 0) null else unreadAnnouncementsCount.toString()))
mainDrawer.updateBadge(DRAWER_ITEM_ANNOUNCEMENTS, StringHolder(if (unreadAnnouncementsCount <= 0) null else unreadAnnouncementsCount.toString()))
}
private fun updateProfiles() {

View File

@ -25,7 +25,9 @@ import androidx.work.WorkManager
import com.keylesspalace.tusky.components.notifications.NotificationWorkerFactory
import com.keylesspalace.tusky.di.AppInjector
import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.util.*
import com.keylesspalace.tusky.util.EmojiCompatFont
import com.keylesspalace.tusky.util.LocaleManager
import com.keylesspalace.tusky.util.ThemeUtils
import com.uber.autodispose.AutoDisposePlugins
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
@ -38,11 +40,21 @@ class TuskyApplication : Application(), HasAndroidInjector {
@Inject
lateinit var androidInjector: DispatchingAndroidInjector<Any>
@Inject
lateinit var notificationWorkerFactory: NotificationWorkerFactory
override fun onCreate() {
// Uncomment me to get StrictMode violation logs
// if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
// StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
// .detectDiskReads()
// .detectDiskWrites()
// .detectNetwork()
// .detectUnbufferedIo()
// .penaltyLog()
// .build())
// }
super.onCreate()
Security.insertProviderAt(Conscrypt.newProvider(), 1)
@ -64,16 +76,16 @@ class TuskyApplication : Application(), HasAndroidInjector {
val theme = preferences.getString("appTheme", ThemeUtils.APP_THEME_DEFAULT)
ThemeUtils.setAppNightMode(theme)
RxJavaPlugins.setErrorHandler {
Log.w("RxJava", "undeliverable exception", it)
}
WorkManager.initialize(
this,
androidx.work.Configuration.Builder()
.setWorkerFactory(notificationWorkerFactory)
.build()
)
RxJavaPlugins.setErrorHandler {
Log.w("RxJava", "undeliverable exception", it)
}
}
override fun attachBaseContext(base: Context) {

View File

@ -22,7 +22,6 @@ import android.view.ViewGroup
import android.widget.ArrayAdapter
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.MastoList
import com.keylesspalace.tusky.util.ThemeUtils
import kotlinx.android.synthetic.main.item_picker_list.view.*
class ListSelectionAdapter(context: Context) : ArrayAdapter<MastoList>(context, R.layout.item_autocomplete_hashtag) {
@ -34,10 +33,7 @@ class ListSelectionAdapter(context: Context) : ArrayAdapter<MastoList>(context,
?: layoutInflater.inflate(R.layout.item_picker_list, parent, false)
getItem(position)?.let { list ->
val title = view.title
title.text = list.title
val icon = ThemeUtils.getTintedDrawable(context, R.drawable.ic_list, R.attr.iconColor)
title.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null)
view.title.text = list.title
}
return view

View File

@ -22,7 +22,6 @@ import android.widget.TextView
import androidx.core.widget.TextViewCompat
import androidx.recyclerview.widget.RecyclerView
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.util.ThemeUtils
class PreviewPollOptionsAdapter: RecyclerView.Adapter<PreviewViewHolder>() {
@ -55,9 +54,7 @@ class PreviewPollOptionsAdapter: RecyclerView.Adapter<PreviewViewHolder>() {
R.drawable.ic_radio_button_unchecked_18dp
}
val iconDrawable = ThemeUtils.getTintedDrawable(textView.context, iconId, android.R.attr.textColorTertiary)
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, iconDrawable, null, null, null)
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, iconId, 0, 0, 0)
textView.text = options[position]

View File

@ -60,7 +60,6 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import at.connyduck.sparkbutton.SparkButton;
import at.connyduck.sparkbutton.helpers.Utils;
@ -648,9 +647,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
// Set the icon next to the label.
int drawableId = getLabelIcon(attachments.get(0).getType());
Drawable drawable = Objects.requireNonNull(context.getDrawable(drawableId));
ThemeUtils.setDrawableTint(context, drawable, android.R.attr.textColorTertiary);
mediaLabel.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null);
mediaLabel.setCompoundDrawablesWithIntrinsicBounds(drawableId, 0, 0, 0);
setAttachmentClickListener(mediaLabel, listener, i, attachment, false);
} else {

View File

@ -15,6 +15,7 @@
package com.keylesspalace.tusky.adapter
import android.content.res.ColorStateList
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
@ -68,8 +69,7 @@ class TabAdapter(private var data: List<TabData>,
} else {
holder.itemView.textView.setText(tab.text)
}
val iconDrawable = ThemeUtils.getTintedDrawable(context, tab.icon, android.R.attr.textColorSecondary)
holder.itemView.textView.setCompoundDrawablesRelativeWithIntrinsicBounds(iconDrawable, null, null, null)
holder.itemView.textView.setCompoundDrawablesRelativeWithIntrinsicBounds(tab.icon, 0, 0, 0)
if (small) {
holder.itemView.textView.setOnClickListener {
listener.onTabAdded(tab)
@ -110,6 +110,7 @@ class TabAdapter(private var data: List<TabData>,
val chip = holder.itemView.chipGroup.getChildAt(i).takeUnless { it.id == R.id.actionChip } as Chip?
?: Chip(context).apply {
holder.itemView.chipGroup.addView(this, holder.itemView.chipGroup.size - 1)
chipIconTint = ColorStateList.valueOf(ThemeUtils.getColor(context, android.R.attr.textColorPrimary))
}
chip.text = arg
@ -118,8 +119,7 @@ class TabAdapter(private var data: List<TabData>,
chip.chipIcon = null
chip.setOnClickListener(null)
} else {
val cancelIcon = ThemeUtils.getTintedDrawable(context, R.drawable.ic_cancel_24dp, android.R.attr.textColorPrimary)
chip.chipIcon = cancelIcon
chip.setChipIconResource(R.drawable.ic_cancel_24dp)
chip.setOnClickListener {
listener.onChipClicked(tab, holder.adapterPosition, i)
}

View File

@ -27,10 +27,12 @@ import com.google.android.material.chip.ChipGroup
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Announcement
import com.keylesspalace.tusky.entity.Emoji
import com.keylesspalace.tusky.interfaces.LinkListener
import com.keylesspalace.tusky.util.LinkHelper
import com.keylesspalace.tusky.util.emojify
import kotlinx.android.synthetic.main.item_announcement.view.*
interface AnnouncementActionListener {
interface AnnouncementActionListener: LinkListener {
fun openReactionPicker(announcementId: String, target: View)
fun addReaction(announcementId: String, name: String)
fun removeReaction(announcementId: String, name: String)
@ -59,13 +61,12 @@ class AnnouncementAdapter(
}
inner class AnnouncementViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
private val text: TextView = view.text
private val chips: ChipGroup = view.chipGroup
private val addReactionChip: Chip = view.addReactionChip
fun bind(item: Announcement) {
text.text = item.content
LinkHelper.setClickableText(text, item.content, null, listener, false)
item.reactions.forEachIndexed { i, reaction ->
(chips.getChildAt(i)?.takeUnless { it.id == R.id.addReactionChip } as Chip?

View File

@ -22,11 +22,9 @@ import android.view.MenuItem
import android.view.View
import android.widget.PopupWindow
import androidx.activity.viewModels
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.keylesspalace.tusky.BaseActivity
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.*
import com.keylesspalace.tusky.adapter.EmojiAdapter
import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener
import com.keylesspalace.tusky.di.Injectable
@ -37,7 +35,7 @@ import kotlinx.android.synthetic.main.activity_announcements.*
import kotlinx.android.synthetic.main.toolbar_basic.*
import javax.inject.Inject
class AnnouncementsActivity : BaseActivity(), AnnouncementActionListener, OnEmojiSelectedListener, Injectable {
class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener, OnEmojiSelectedListener, Injectable {
@Inject
lateinit var viewModelFactory: ViewModelFactory
@ -79,7 +77,7 @@ class AnnouncementsActivity : BaseActivity(), AnnouncementActionListener, OnEmoj
announcementsList.addItemDecoration(divider)
announcementsList.adapter = adapter
viewModel.announcements.observe(this, Observer {
viewModel.announcements.observe(this) {
when (it) {
is Success -> {
progressBar.hide()
@ -104,11 +102,11 @@ class AnnouncementsActivity : BaseActivity(), AnnouncementActionListener, OnEmoj
errorMessageView.show()
}
}
})
}
viewModel.emojis.observe(this, Observer {
viewModel.emojis.observe(this) {
picker.adapter = EmojiAdapter(it, this)
})
}
viewModel.load()
progressBar.show()
@ -147,6 +145,24 @@ class AnnouncementsActivity : BaseActivity(), AnnouncementActionListener, OnEmoj
viewModel.removeReaction(announcementId, name)
}
override fun onViewTag(tag: String?) {
val intent = Intent(this, ViewTagActivity::class.java)
intent.putExtra("hashtag", tag)
startActivityWithSlideInAnimation(intent)
}
override fun onViewAccount(id: String?) {
if (id != null) {
viewAccount(id)
}
}
override fun onViewUrl(url: String?, text: String?) {
if (url != null) {
viewUrl(url)
}
}
companion object {
fun newIntent(context: Context) = Intent(context, AnnouncementsActivity::class.java)
}

View File

@ -51,7 +51,7 @@ class AnnouncementsViewModel @Inject constructor(
.map<Either<InstanceEntity, Instance>> { Either.Left(it) }
.onErrorResumeNext(
mastodonApi.getInstance()
.map { Either.Right<InstanceEntity, Instance>(it) }
.map { Either.Right(it) }
)
) { emojis, either ->
either.asLeftOrNull()?.copy(emojiList = emojis)

View File

@ -626,8 +626,7 @@ class ComposeActivity : BaseActivity(),
Status.Visibility.UNLEAKABLE -> R.drawable.ic_low_vision_24dp
else -> R.drawable.ic_lock_open_24dp
}
val drawable = ThemeUtils.getTintedDrawable(this, iconRes, android.R.attr.textColorTertiary)
composeToggleVisibilityButton.setImageDrawable(drawable)
composeToggleVisibilityButton.setImageResource(iconRes)
}
private fun showComposeOptions() {

View File

@ -19,10 +19,8 @@ import android.net.Uri
import android.util.Log
import androidx.core.net.toUri
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.work.impl.utils.LiveDataUtils
import com.keylesspalace.tusky.adapter.ComposeAutoCompleteAdapter
import com.keylesspalace.tusky.components.compose.ComposeActivity.QueuedMedia
import com.keylesspalace.tusky.components.search.SearchType
@ -35,7 +33,6 @@ import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.service.ServiceClient
import com.keylesspalace.tusky.service.TootToSend
import com.keylesspalace.tusky.util.*
import io.reactivex.Observable.empty
import io.reactivex.Observable.just
import io.reactivex.disposables.Disposable
import io.reactivex.rxkotlin.Singles

View File

@ -26,13 +26,13 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;
import com.google.android.material.datepicker.CalendarConstraints;
import com.google.android.material.datepicker.DateValidatorPointForward;
import com.google.android.material.datepicker.MaterialDatePicker;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.fragment.TimePickerFragment;
import com.keylesspalace.tusky.util.ThemeUtils;
import java.text.DateFormat;
import java.text.ParseException;
@ -106,7 +106,7 @@ public class ComposeScheduleView extends ConstraintLayout {
}
private void setEditIcons() {
Drawable icon = ThemeUtils.getTintedDrawable(getContext(), R.drawable.ic_create_24dp, android.R.attr.textColorTertiary);
Drawable icon = ContextCompat.getDrawable(getContext(), R.drawable.ic_create_24dp);
if (icon == null) {
return;
}

View File

@ -295,7 +295,7 @@ public class NotificationHelper {
.setSmallIcon(R.drawable.ic_notify)
.setContentIntent(summary ? summaryResultPendingIntent : eventResultPendingIntent)
.setDeleteIntent(deletePendingIntent)
.setColor(BuildConfig.DEBUG ? Color.parseColor("#19A341") : ContextCompat.getColor(context, R.color.tusky_blue))
.setColor(BuildConfig.FLAVOR == "green" ? Color.parseColor("#19A341") : ContextCompat.getColor(context, R.color.tusky_blue))
.setGroup(account.getAccountId())
.setAutoCancel(true)
.setShortcutId(Long.toString(account.getId()))

View File

@ -16,10 +16,10 @@
package com.keylesspalace.tusky.components.preference
import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.util.Log
import androidx.annotation.DrawableRes
import androidx.preference.PreferenceFragmentCompat
import com.google.android.material.snackbar.Snackbar
import com.keylesspalace.tusky.*
@ -71,7 +71,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
preference {
setTitle(R.string.title_tab_preferences)
icon = getTintedIcon(R.drawable.ic_tabs)
setIcon(R.drawable.ic_tabs)
setOnPreferenceClickListener {
val intent = Intent(context, TabPreferenceActivity::class.java)
activity?.startActivity(intent)
@ -83,7 +83,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
preference {
setTitle(R.string.action_view_mutes)
icon = getTintedIcon(R.drawable.ic_mute_24dp)
setIcon(R.drawable.ic_mute_24dp)
setOnPreferenceClickListener {
val intent = Intent(context, AccountListActivity::class.java)
intent.putExtra("type", AccountListActivity.Type.MUTES)
@ -112,7 +112,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
preference {
setTitle(R.string.title_domain_mutes)
icon = getTintedIcon(R.drawable.ic_mute_24dp)
setIcon(R.drawable.ic_mute_24dp)
setOnPreferenceClickListener {
val intent = Intent(context, InstanceListActivity::class.java)
activity?.startActivity(intent)
@ -132,11 +132,9 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
val visibility = accountManager.activeAccount?.defaultPostPrivacy
?: Status.Visibility.PUBLIC
value = visibility.serverString()
icon = getIconForVisibility(visibility)
setIcon(getIconForVisibility(visibility))
setOnPreferenceChangeListener { _, newValue ->
icon = getIconForVisibility(
Status.Visibility.byString(newValue as String)
)
setIcon(getIconForVisibility(Status.Visibility.byString(newValue as String)))
syncWithServer(visibility = newValue)
eventHub.dispatch(PreferenceChangedEvent(key))
true
@ -151,9 +149,9 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
val sensitivity = accountManager.activeAccount?.defaultMediaSensitivity
?: false
setDefaultValue(sensitivity)
icon = getIconForSensitivity(sensitivity)
setIcon(getIconForSensitivity(sensitivity))
setOnPreferenceChangeListener { _, newValue ->
icon = getIconForSensitivity(newValue as Boolean)
setIcon(getIconForSensitivity(newValue as Boolean))
syncWithServer(sensitive = newValue)
eventHub.dispatch(PreferenceChangedEvent(key))
true
@ -303,30 +301,24 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
}
}
private fun getIconForVisibility(visibility: Status.Visibility): Drawable? {
val drawableId = when (visibility) {
@DrawableRes
private fun getIconForVisibility(visibility: Status.Visibility): Int {
return when (visibility) {
Status.Visibility.PRIVATE -> R.drawable.ic_lock_outline_24dp
Status.Visibility.UNLISTED -> R.drawable.ic_lock_open_24dp
else -> R.drawable.ic_public_24dp
}
return getTintedIcon(drawableId)
}
private fun getIconForSensitivity(sensitive: Boolean): Drawable? {
val drawableId = if (sensitive) {
@DrawableRes
private fun getIconForSensitivity(sensitive: Boolean): Int {
return if (sensitive) {
R.drawable.ic_hide_media_24dp
} else {
R.drawable.ic_eye_24dp
}
return getTintedIcon(drawableId)
}
private fun getTintedIcon(iconId: Int): Drawable? {
return ThemeUtils.getTintedDrawable(requireContext(), iconId, R.attr.iconColor)
}
private fun launchFilterActivity(filterContext: String, titleResource: Int) {

View File

@ -42,7 +42,6 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
private var httpProxyPref: Preference? = null
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
val context = requireContext()
makePreferenceScreen {
lateinit var limitedBandwidthMobilePref: SwitchPreference
lateinit var limitedBandwidthTimelinePref: SwitchPreference
@ -121,11 +120,8 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
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
)
setIcon(R.drawable.ic_bot_24dp)
}
switchPreference {

View File

@ -62,7 +62,7 @@ class AccountManager @Inject constructor(db: AppDatabase) {
accountDao.insertOrReplace(it)
}
val maxAccountId = accounts.maxBy { it.id }?.id ?: 0
val maxAccountId = accounts.maxByOrNull { it.id }?.id ?: 0
val newAccountId = maxAccountId + 1
activeAccount = AccountEntity(id = newAccountId, domain = domain.toLowerCase(Locale.ROOT), accessToken = accessToken, isActive = true)

View File

@ -25,7 +25,7 @@ import com.keylesspalace.tusky.json.SpannedTypeAdapter
import com.keylesspalace.tusky.network.InstanceSwitchAuthInterceptor
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.network.NotestockApi
import com.keylesspalace.tusky.util.OkHttpUtils
import com.keylesspalace.tusky.util.okhttpClient
import dagger.Module
import dagger.Provides
import net.accelf.yuito.HttpToastInterceptor
@ -34,6 +34,7 @@ import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.create
import javax.inject.Singleton
/**
@ -57,7 +58,7 @@ class NetworkModule {
accountManager: AccountManager,
context: Context
): OkHttpClient {
return OkHttpUtils.getCompatibleClientBuilder(context)
return okhttpClient(context)
.apply {
addInterceptor(InstanceSwitchAuthInterceptor(accountManager))
if (BuildConfig.DEBUG) {
@ -84,13 +85,13 @@ class NetworkModule {
@Provides
@Singleton
fun providesApi(retrofit: Retrofit): MastodonApi = retrofit.create(MastodonApi::class.java)
fun providesApi(retrofit: Retrofit): MastodonApi = retrofit.create()
@Provides
@Singleton
fun providesNotestockApi(context: Context,
gson: Gson): NotestockApi {
val httpClient = OkHttpUtils.getCompatibleClientBuilder(context)
val httpClient = okhttpClient(context)
.apply {
if (BuildConfig.DEBUG) {
addInterceptor(HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BASIC })
@ -105,4 +106,4 @@ class NetworkModule {
.build()
return retrofit.create(NotestockApi::class.java)
}
}
}

View File

@ -28,7 +28,7 @@ import kotlinx.android.parcel.Parcelize
data class Attachment(
val id: String,
val url: String,
@SerializedName("preview_url") val previewUrl: String,
@SerializedName("preview_url") val previewUrl: String?, // can be null for e.g. audio attachments
val meta: MetaData?,
val type: Type,
val description: String?,

View File

@ -330,6 +330,7 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
private fun fetchRelationships(ids: List<String>) {
api.relationships(ids)
.observeOn(AndroidSchedulers.mainThread())
.autoDispose(from(this))
.subscribe(::onFetchRelationshipsSuccess) {
onFetchRelationshipsFailure(ids)

View File

@ -502,6 +502,7 @@ public class TimelineFragment extends SFragment implements
// home, notifications, public, thread
switch (kind) {
case HOME:
case LIST:
return filterContext.contains(Filter.HOME);
case PUBLIC_FEDERATED:
case PUBLIC_LOCAL:

View File

@ -54,7 +54,7 @@ fun CharSequence.emojify(emojis: List<Emoji>?, view: View) : CharSequence {
while(matcher.find()) {
val span = EmojiSpan(WeakReference(view))
builder.setSpan(span, matcher.start(), matcher.end(), 0);
builder.setSpan(span, matcher.start(), matcher.end(), 0)
Glide.with(view)
.asBitmap()
.load(url)
@ -89,7 +89,7 @@ class EmojiSpan(val viewWeakReference: WeakReference<View>) : ReplacementSpan()
drawable.setBounds(0, 0, emojiSize, emojiSize)
var transY = bottom - drawable.bounds.bottom
transY -= paint.fontMetricsInt.descent / 2;
transY -= paint.fontMetricsInt.descent / 2
canvas.translate(x, transY.toFloat())
drawable.draw(canvas)

View File

@ -1,87 +0,0 @@
/* Copyright 2017 Andrew Dawson
*
* This file is part of Tusky.
*
* Tusky is free software: you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with Tusky. If
* not, see <http://www.gnu.org/licenses/>. */
package com.keylesspalace.tusky.util;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
import com.keylesspalace.tusky.BuildConfig;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.concurrent.TimeUnit;
import okhttp3.Cache;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
public class OkHttpUtils {
@NonNull
public static OkHttpClient.Builder getCompatibleClientBuilder(@NonNull Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean httpProxyEnabled = preferences.getBoolean("httpProxyEnabled", false);
String httpServer = preferences.getString("httpProxyServer", "");
int httpPort;
try {
httpPort = Integer.parseInt(preferences.getString("httpProxyPort", "-1"));
} catch (NumberFormatException e) {
// user has entered wrong port, fall back to no proxy
httpPort = -1;
}
int cacheSize = 25*1024*1024; // 25 MiB
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.addInterceptor(getUserAgentInterceptor())
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.cache(new Cache(context.getCacheDir(), cacheSize));
if (httpProxyEnabled && !httpServer.isEmpty() && (httpPort > 0) && (httpPort < 65535)) {
InetSocketAddress address = InetSocketAddress.createUnresolved(httpServer, httpPort);
builder.proxy(new Proxy(Proxy.Type.HTTP, address));
}
return builder;
}
/**
* Add a custom User-Agent that contains Tusky & Android Version to all requests
* Example:
* User-Agent: Tusky/1.1.2 Android/5.0.2
*/
@NonNull
private static Interceptor getUserAgentInterceptor() {
return chain -> {
Request originalRequest = chain.request();
Request requestWithUserAgent = originalRequest.newBuilder()
.header("User-Agent", "Tusky/"+ BuildConfig.VERSION_NAME+" Android/"+Build.VERSION.RELEASE)
.build();
return chain.proceed(requestWithUserAgent);
};
}
}

View File

@ -0,0 +1,115 @@
/* Copyright 2020 Tusky Contributors
*
* This file is part of Tusky.
*
* Tusky is free software: you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with Tusky. If
* not, see <http://www.gnu.org/licenses/>. */
package com.keylesspalace.tusky.util
import android.content.Context
import android.os.Build
import androidx.preference.PreferenceManager
import com.keylesspalace.tusky.BuildConfig
import okhttp3.Cache
import okhttp3.OkHttp
import okhttp3.OkHttpClient
import okhttp3.tls.HandshakeCertificates
import java.io.ByteArrayInputStream
import java.net.InetSocketAddress
import java.net.Proxy
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
fun okhttpClient(context: Context): OkHttpClient.Builder {
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
val httpProxyEnabled = preferences.getBoolean("httpProxyEnabled", false)
val httpServer = preferences.getNonNullString("httpProxyServer", "")
val httpPort = preferences.getNonNullString("httpProxyPort", "-1").toIntOrNull() ?: -1
val cacheSize = 25 * 1024 * 1024 // 25 MiB
val builder = OkHttpClient.Builder()
.addInterceptor { chain ->
/**
* Add a custom User-Agent that contains Tusky, Android and Okhttp Version to all requests
* Example:
* User-Agent: Tusky/1.1.2 Android/5.0.2
* */
val requestWithUserAgent = chain.request().newBuilder()
.header(
"User-Agent",
"Tusky/${BuildConfig.VERSION_NAME} Android/${Build.VERSION.RELEASE} OkHttp/${OkHttp.VERSION}"
)
.build()
chain.proceed(requestWithUserAgent)
}
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.cache(Cache(context.cacheDir, cacheSize.toLong()))
if (httpProxyEnabled && httpServer.isNotEmpty() && httpPort > 0 && httpPort < 65535) {
val address = InetSocketAddress.createUnresolved(httpServer, httpPort)
builder.proxy(Proxy(Proxy.Type.HTTP, address))
}
// trust the new Let's Encrypt root certificate that is not available on Android < 7.1.1
// new cert https://letsencrypt.org/certs/isrgrootx1.pem
// see https://letsencrypt.org/2020/11/06/own-two-feet.html
// see https://stackoverflow.com/questions/64844311/certpathvalidatorexception-connecting-to-a-lets-encrypt-host-on-android-m-or-ea
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
val isgCert = """
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
""".trimIndent()
val cf = CertificateFactory.getInstance("X.509")
val isgCertificate = cf.generateCertificate(ByteArrayInputStream(isgCert.toByteArray(charset("UTF-8"))))
val certificates = HandshakeCertificates.Builder()
.addTrustedCertificate(isgCertificate as X509Certificate)
.addPlatformTrustedCertificates()
.build()
builder.sslSocketFactory(
certificates.sslSocketFactory(),
certificates.trustManager
)
}
return builder
}

View File

@ -23,7 +23,6 @@ import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
import com.bumptech.glide.Glide
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Attachment
@ -220,9 +219,7 @@ class StatusViewHelper(private val itemView: View) {
// Set the icon next to the label.
val drawableId = getLabelIcon(attachments[0].type)
val drawable = AppCompatResources.getDrawable(context, drawableId)
ThemeUtils.setDrawableTint(context, drawable!!, android.R.attr.textColorTertiary)
mediaLabel.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null)
mediaLabel.setCompoundDrawablesWithIntrinsicBounds(drawableId, 0, 0, 0)
mediaLabel.setOnClickListener { listener.onViewMedia(null, 0) }
}

View File

@ -24,9 +24,7 @@ import android.util.TypedValue;
import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatDelegate;
/**
@ -60,17 +58,6 @@ public class ThemeUtils {
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) {
Drawable drawable = context.getDrawable(drawableId);
if(drawable == null) {
return null;
}
setDrawableTint(context, drawable, colorAttr);
return drawable;
}
public static void setDrawableTint(Context context, Drawable drawable, @AttrRes int attribute) {
drawable.setColorFilter(getColor(context, attribute), PorterDuff.Mode.SRC_IN);
}

View File

@ -36,4 +36,6 @@
tools:src="@drawable/elephant_error"
tools:visibility="visible" />
<include layout="@layout/item_status_bottom_sheet" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -166,10 +166,10 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/composeMediaPreviewBar"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none" />
android:scrollbars="none"
android:visibility="gone" />
<com.keylesspalace.tusky.components.compose.view.PollPreviewView
android:id="@+id/pollPreview"
@ -297,9 +297,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:animateLayoutChanges="true"
android:background="?attr/colorSurface"
android:elevation="12dp"
android:animateLayoutChanges="true"
android:gravity="center_vertical"
android:paddingStart="8dp"
android:paddingTop="4dp"
@ -314,8 +314,8 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/action_add_media"
android:padding="4dp"
android:tooltipText="@string/action_add_media"
app:srcCompat="@drawable/ic_attach_file_24dp" />
app:srcCompat="@drawable/ic_attach_file_24dp"
app:tooltipText="@string/action_add_media" />
<ImageButton
android:id="@+id/composeToggleVisibilityButton"
@ -325,7 +325,8 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/action_toggle_visibility"
android:padding="4dp"
android:tooltipText="@string/action_toggle_visibility"
android:tint="?android:attr/textColorTertiary"
app:tooltipText="@string/action_toggle_visibility"
tools:src="@drawable/ic_public_24dp" />
<ImageButton
@ -336,7 +337,7 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/action_hide_media"
android:padding="4dp"
android:tooltipText="@string/action_hide_media"
app:tooltipText="@string/action_hide_media"
tools:src="@drawable/ic_eye_24dp" />
<ImageButton
@ -347,8 +348,8 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/action_content_warning"
android:padding="4dp"
android:tooltipText="@string/action_content_warning"
app:srcCompat="@drawable/ic_cw_24dp" />
app:srcCompat="@drawable/ic_cw_24dp"
app:tooltipText="@string/action_content_warning" />
<ImageButton
android:id="@+id/composeEmojiButton"
@ -358,8 +359,8 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/action_emoji_keyboard"
android:padding="4dp"
android:tooltipText="@string/action_emoji_keyboard"
app:srcCompat="@drawable/ic_emoji_24dp" />
app:srcCompat="@drawable/ic_emoji_24dp"
app:tooltipText="@string/action_emoji_keyboard" />
<ImageButton
android:id="@+id/composeScheduleButton"
@ -369,8 +370,8 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/action_schedule_toot"
android:padding="4dp"
android:tooltipText="@string/action_schedule_toot"
app:srcCompat="@drawable/ic_access_time" />
app:srcCompat="@drawable/ic_access_time"
app:tooltipText="@string/action_schedule_toot" />
<Space
android:layout_width="0dp"

View File

@ -29,7 +29,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:contentInsetStartWithNavigation="0dp"
app:layout_scrollFlags="scroll|enterAlways" />
app:layout_scrollFlags="scroll|enterAlways"
app:navigationContentDescription="@string/action_open_drawer" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"

View File

@ -1,8 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/hashtag"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:textSize="?attr/status_text_medium"
android:textStyle="normal|bold" />
android:textStyle="normal|bold"
app:drawableStartCompat="@drawable/ic_list"
app:drawableTint="?attr/iconColor" />

View File

@ -340,6 +340,7 @@
android:importantForAccessibility="no"
android:textSize="?attr/status_text_medium"
android:visibility="gone"
app:drawableTint="?android:attr/textColorTertiary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@ -353,6 +354,7 @@
android:importantForAccessibility="no"
android:textSize="?attr/status_text_medium"
android:visibility="gone"
app:drawableTint="?android:attr/textColorTertiary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_media_label_0" />
@ -366,6 +368,7 @@
android:importantForAccessibility="no"
android:textSize="?attr/status_text_medium"
android:visibility="gone"
app:drawableTint="?android:attr/textColorTertiary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_media_label_1" />
@ -379,6 +382,7 @@
android:importantForAccessibility="no"
android:textSize="?attr/status_text_medium"
android:visibility="gone"
app:drawableTint="?android:attr/textColorTertiary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_media_label_2" />

View File

@ -6,8 +6,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:paddingBottom="8dp"
android:paddingTop="8dp">
android:paddingTop="8dp"
android:paddingBottom="8dp">
<!--This is a thumbnail picture-->
<ImageView
@ -15,23 +15,25 @@
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_marginStart="16dp"
android:importantForAccessibility="no"
android:padding="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_emoji_24dp" />
app:srcCompat="@drawable/ic_emoji_24dp"
app:tint="#0000" /> <!-- unset the preference theme tint -->
<!--This is the font's name-->
<TextView
android:id="@+id/emojicompat_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="72dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="72dp"
android:textColor="?android:textColorPrimary"
app:layout_constraintEnd_toEndOf="parent"
android:textSize="?attr/status_text_medium"
app:layout_constraintBottom_toTopOf="@+id/emojicompat_caption"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/emojicompat_thumb"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/system_default" />
@ -58,8 +60,8 @@
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="72dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="72dp"
android:indeterminate="false"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="4dp"
@ -7,4 +8,5 @@
android:focusableInTouchMode="false"
android:gravity="center_vertical"
android:lines="1"
android:maxEms="20" />
android:maxEms="20"
app:drawableTint="?android:attr/textColorTertiary" />

View File

@ -198,8 +198,8 @@
app:layout_constraintLeft_toLeftOf="@+id/status_media_preview_container"
app:layout_constraintTop_toTopOf="@+id/status_media_preview_container"
app:srcCompat="@drawable/ic_eye_24dp"
tools:visibility="visible"
app:tint="@color/white" />
app:tint="@color/white"
tools:visibility="visible" />
<TextView
android:id="@+id/status_sensitive_media_warning"
@ -231,6 +231,7 @@
android:gravity="center_vertical"
android:textSize="?attr/status_text_medium"
android:visibility="gone"
app:drawableTint="?android:attr/textColorTertiary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

View File

@ -168,9 +168,9 @@
android:foreground="?attr/selectableItemBackground"
android:minHeight="80dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@+id/button_toggle_content"
app:layout_constraintEnd_toEndOf="parent"
tools:visibility="gone">
<ImageView
@ -411,6 +411,7 @@
android:importantForAccessibility="no"
android:textSize="?attr/status_text_medium"
android:visibility="gone"
app:drawableTint="?android:attr/textColorTertiary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@ -424,6 +425,7 @@
android:importantForAccessibility="no"
android:textSize="?attr/status_text_medium"
android:visibility="gone"
app:drawableTint="?android:attr/textColorTertiary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_media_label_0" />
@ -437,6 +439,7 @@
android:importantForAccessibility="no"
android:textSize="?attr/status_text_medium"
android:visibility="gone"
app:drawableTint="?android:attr/textColorTertiary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_media_label_1" />
@ -450,6 +453,7 @@
android:importantForAccessibility="no"
android:textSize="?attr/status_text_medium"
android:visibility="gone"
app:drawableTint="?android:attr/textColorTertiary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_media_label_2" />

View File

@ -356,6 +356,7 @@
android:importantForAccessibility="no"
android:textSize="?attr/status_text_medium"
android:visibility="gone"
app:drawableTint="?android:attr/textColorTertiary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@ -369,6 +370,7 @@
android:importantForAccessibility="no"
android:textSize="?attr/status_text_medium"
android:visibility="gone"
app:drawableTint="?android:attr/textColorTertiary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_media_label_0" />
@ -382,6 +384,7 @@
android:importantForAccessibility="no"
android:textSize="?attr/status_text_medium"
android:visibility="gone"
app:drawableTint="?android:attr/textColorTertiary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_media_label_1" />
@ -395,6 +398,7 @@
android:importantForAccessibility="no"
android:textSize="?attr/status_text_medium"
android:visibility="gone"
app:drawableTint="?android:attr/textColorTertiary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_media_label_2" />

View File

@ -32,6 +32,7 @@
android:paddingBottom="8dp"
android:textColor="?android:attr/textColorSecondary"
android:textSize="?attr/status_text_large"
app:drawableTint="?android:attr/textColorSecondary"
app:layout_constraintBottom_toTopOf="@id/chipGroup"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/imageView"

View File

@ -1,17 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/textView"
android:layout_width="match_parent"
android:drawableStart="@drawable/ic_home_24dp"
android:layout_height="48dp"
android:gravity="center_vertical"
android:background="?attr/selectableItemBackground"
android:drawablePadding="12dp"
android:ellipsize="end"
android:gravity="center_vertical"
android:lines="1"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:lines="1"
android:ellipsize="end"
android:background="?attr/selectableItemBackground"
android:textColor="?android:attr/textColorSecondary"
android:textSize="?attr/status_text_large" />
android:textSize="?attr/status_text_large"
app:drawableStartCompat="@drawable/ic_home_24dp"
app:drawableTint="?android:attr/textColorSecondary" />

View File

@ -23,6 +23,7 @@
android:paddingBottom="16dp"
android:textColor="?android:textColorTertiary"
android:textSize="?attr/status_text_medium"
app:drawableTint="?android:attr/textColorTertiary"
app:layout_constraintBottom_toTopOf="@id/invalidScheduleWarning"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1"
@ -39,11 +40,11 @@
android:paddingBottom="16dp"
android:textColor="?android:textColorTertiary"
android:textSize="?attr/status_text_medium"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"
tools:text="@string/warning_scheduling_interval"
android:visibility="gone" />
tools:text="@string/warning_scheduling_interval" />
</merge>

View File

@ -381,7 +381,7 @@
<string name="poll_vote">ভোট</string>
<string name="poll_info_closed">বন্ধ</string>
<string name="poll_info_time_absolute">\'%s এ শেষ হবে\'</string>
<string name="poll_info_format">" &lt;!-- ১৫ ভোট • ১ ঘন্টা বাকি --&gt; %1$s • %2$s"<!-- 15 votes • 1 hour left -->
<string name="poll_info_format"><!-- 15 votes • 1 hour left -->
%1$s • %2$s</string>
<string name="compose_preview_image_description">ছবি %s এর জন্য ক্রিয়া</string>
<string name="notification_clear_text">আপনি কি আপনার সমস্ত বিজ্ঞপ্তি স্থায়ীভাবে মুছে ফেলতে চান\?</string>

View File

@ -475,11 +475,7 @@
<string name="action_unmute_notifications_desc">Odkrýt oznámení od %s</string>
<string name="action_unmute_desc">Odkrýt %s</string>
<string name="dialog_mute_warning">Ztišit @%s\?</string>
<plurals name="poll_info_people">
<item quantity="one"/>
<item quantity="few"/>
<item quantity="other"/>
</plurals>
<string name="notification_follow_request_format">%s požádal/a aby vás mohl/a sledovat</string>
<string name="pref_title_confirm_reblogs">Zobrazit dialogové okno s potvrzením při boostování</string>
</resources>

View File

@ -101,7 +101,7 @@
<string name="action_accept">Akzeptieren</string>
<string name="action_reject">Ablehnen</string>
<string name="action_search">Suche</string>
<string name="action_access_saved_toot">Gespeicherte Beiträge</string>
<string name="action_access_saved_toot">Entwürfe</string>
<string name="action_toggle_visibility">Beitragssichtbarkeit</string>
<string name="action_content_warning">Inhaltswarnung</string>
<string name="action_emoji_keyboard">Emoji</string>
@ -465,4 +465,9 @@
<string name="abbreviated_hours_ago">%dSt.</string>
<string name="abbreviated_in_days">in %d T.</string>
<string name="abbreviated_years_ago">%dJ.</string>
<string name="account_note_saved">Gespeichert!</string>
<string name="account_note_hint">Private Notiz über diesen Account</string>
<string name="pref_title_hide_top_toolbar">Titel der Hauptnavigation verstecken</string>
<string name="no_announcements">Im Moment gibt es keine Ankündigungen.</string>
<string name="title_announcements">Ankündigungen</string>
</resources>

View File

@ -164,9 +164,9 @@
<string name="pref_title_notification_alert_vibrate">Sciigi per vibro</string>
<string name="pref_title_notification_alert_light">Sciigi per lumo</string>
<string name="pref_title_notification_filters">Sciigi al mi kiam</string>
<string name="pref_title_notification_filter_mentions">menciita</string>
<string name="pref_title_notification_filter_follows">sekvita</string>
<string name="pref_title_notification_filter_reblogs">viaj mesaĝoj estas diskonigitaj</string>
<string name="pref_title_notification_filter_mentions">iu mencias min</string>
<string name="pref_title_notification_filter_follows">iu sekvas min</string>
<string name="pref_title_notification_filter_reblogs">miaj mesaĝoj estas diskonigitaj</string>
<string name="pref_title_notification_filter_favourites">miaj mesaĝoj estas stelumitaj</string>
<string name="pref_title_appearance_settings">Aspekto</string>
<string name="pref_title_app_theme">Temo de la apo</string>
@ -363,7 +363,7 @@
<string name="pref_title_animate_gif_avatars">Moviĝi GIF profilbildojn</string>
<string name="notification_poll_name">Enketoj</string>
<string name="notification_poll_description">Sciigoj pri enketoj kiuj finiĝis</string>
<string name="edit_hashtag_hint">Kradvortoj sen #</string>
<string name="edit_hashtag_hint">Kradvorto sen #</string>
<string name="notifications_clear">Viŝi</string>
<string name="notifications_apply_filter">Filtri</string>
<string name="filter_apply">Apliki</string>
@ -373,15 +373,14 @@
<string name="compose_preview_image_description">Agoj por bildo %s</string>
<string name="poll_info_format"> <!-- 15 votes • 1 hour left --> %1$s • %2$s</string>
<plurals name="poll_info_votes">
<item quantity="one"/>
<item quantity="other"/>
<item quantity="one">%s voĉdono</item>
<item quantity="other">%s voĉdonoj</item>
</plurals>
<string name="poll_info_time_absolute">finiĝos je %s</string>
<string name="poll_info_closed">finiĝita</string>
<string name="poll_vote">Voĉdoni</string>
<string name="poll_ended_voted">Enketo al kiu vi voĉdonis finiĝis</string>
<string name="poll_ended_created">Enketo kiu vi kreis finiĝis</string>
<string name="title_domain_mutes">Kaŝitaj domajnoj</string>
<string name="action_view_domain_mutes">Kaŝitaj domajnoj</string>
<string name="action_mute_domain">Silentigi %s</string>
@ -437,4 +436,50 @@
<string name="no_scheduled_status">Vi ne havas iun ajn planitan mesaĝon.</string>
<string name="notification_follow_request_name">Petoj de sekvado</string>
<string name="hashtags">Kradvortoj</string>
<plurals name="poll_info_people">
<item quantity="one">%s homo</item>
<item quantity="other">%s homoj</item>
</plurals>
<string name="add_hashtag_title">Aldoni kradvorton</string>
<string name="notification_follow_request_description">Sciigoj pri petoj de sekvado</string>
<string name="pref_title_gradient_for_media">Montri buntajn transirojn por kaŝitaj aŭdovidaĵoj</string>
<string name="dialog_mute_hide_notifications">Kaŝi la sciigojn</string>
<string name="dialog_mute_warning">Silentigi @%s\?</string>
<string name="dialog_block_warning">Bloki @%s\?</string>
<string name="action_unmute_conversation">Malsilentigi la konversacion</string>
<string name="action_mute_conversation">Silentigi la konversacion</string>
<string name="action_unmute_domain">Malsilentigi %s</string>
<string name="action_mute_notifications_desc">Silentigi sciigojn de %s</string>
<string name="action_unmute_notifications_desc">Malsilentigi sciigojn de %s</string>
<string name="action_unmute_desc">Malsilentigi %s</string>
<string name="notification_follow_request_format">%s petis sekvi vin</string>
<string name="title_announcements">Anoncoj</string>
<plurals name="poll_timespan_minutes">
<item quantity="one">%d minuto restas</item>
<item quantity="other">%d minutoj restas</item>
</plurals>
<plurals name="poll_timespan_seconds">
<item quantity="one">%d sekundo restas</item>
<item quantity="other">%d sekundoj restas</item>
</plurals>
<plurals name="poll_timespan_hours">
<item quantity="one">%d horo restas</item>
<item quantity="other">%d horoj restas</item>
</plurals>
<plurals name="poll_timespan_days">
<item quantity="one">%d tago restas</item>
<item quantity="other">%d tagoj restas</item>
</plurals>
<string name="pref_main_nav_position">Pozicio de la ĉefa naviga breto</string>
<string name="pref_title_notification_filter_follow_requests">iu petas sekvi min</string>
<string name="pref_main_nav_position_option_bottom">Malsupro</string>
<string name="pref_main_nav_position_option_top">Supro</string>
<string name="account_note_saved">Konservita!</string>
<string name="account_note_hint">Via privata noto pri ĉi tiu konto</string>
<string name="pref_title_hide_top_toolbar">Kaŝi la titolon de la supra ilobreto</string>
<string name="pref_title_show_cards_in_timelines">Montri antaŭvidojn de ligiloj en tempolinioj</string>
<string name="pref_title_confirm_reblogs">Montri konfirman fenestron antaŭ ol diskonigi</string>
<string name="no_announcements">Estas neniu anonco.</string>
<string name="pref_title_enable_swipe_for_tabs">Ebligi ŝovumadon por ŝanĝi inter la langetoj</string>
<string name="warning_scheduling_interval">Mastodon havas minimuman intervalon de planado de 5 minutoj.</string>
</resources>

View File

@ -475,4 +475,8 @@
<string name="action_mute_notifications_desc">Silenciar notificaciones desde %s</string>
<string name="action_unmute_notifications_desc">Dejar de silenciar notificaciones desde %s</string>
<string name="pref_title_hide_top_toolbar">Ocultar el título de la barra de herramientas superior</string>
<string name="account_note_saved">¡Guardado!</string>
<string name="account_note_hint">Tu nota privada acerca de esta cuenta</string>
<string name="no_announcements">No hay anuncios.</string>
<string name="title_announcements">Anuncios</string>
</resources>

View File

@ -450,10 +450,7 @@
<string name="hashtags">Traolak</string>
<string name="dialog_mute_hide_notifications">Ez erakutsi jakinarazpenak</string>
<string name="action_unmute_desc">Desmututu %s</string>
<plurals name="poll_info_people">
<item quantity="one">Pertsona %1</item>
<item quantity="other">%2 pertsona</item>
</plurals>
<string name="pref_title_hide_top_toolbar">Ezkutatu goiko tresna-barraren izenburua</string>
<string name="pref_title_confirm_reblogs">Erakutsi berrespen-abisua tuta bultzatu aurretik</string>
<string name="pref_title_show_cards_in_timelines">Erakutsi esteken aurrebista denbora-lerroetan</string>

View File

@ -93,7 +93,7 @@
<string name="action_accept">پذیرش</string>
<string name="action_reject">رد</string>
<string name="action_search">جست‌وجو</string>
<string name="action_access_saved_toot">پیش‌نویس</string>
<string name="action_access_saved_toot">پیش‌نویسها</string>
<string name="action_toggle_visibility">نمایانی بوق</string>
<string name="action_content_warning">هشدار محتوا</string>
<string name="action_emoji_keyboard">صفحه‌کلید اموجی</string>
@ -148,27 +148,27 @@
<string name="pref_title_notification_filter_reblogs">فرسته‌هایم تقویت شدند</string>
<string name="pref_title_notification_filter_favourites">فرسته‌هایم برگزیده شدند</string>
<string name="pref_title_appearance_settings">ظاهر</string>
<string name="pref_title_app_theme">تم برنامه</string>
<string name="pref_title_app_theme">زمینهٔ کاره</string>
<string name="pref_title_timelines">خط‌ زمانی‌ها</string>
<string name="app_them_dark">روشن</string>
<string name="app_theme_light">سیاه</string>
<string name="app_them_dark">تاریک</string>
<string name="app_theme_light">روشن</string>
<string name="app_theme_black">سیاه</string>
<string name="app_theme_auto">خودکار در غروب</string>
<string name="pref_title_browser_settings">مرورگر</string>
<string name="pref_title_custom_tabs">استفاده از زبانه‌های سفارشی کروم</string>
<string name="pref_title_hide_follow_button">پنهان کردن دکمه ایجاد هنگام پیمایش</string>
<string name="pref_title_hide_follow_button">نهفتن دکمهٔ ایجاد، هنگام پیمایش</string>
<string name="pref_title_status_filter">فیلتر کردن خط زمانی</string>
<string name="pref_title_status_tabs">زبانه‌ها</string>
<string name="pref_title_show_boosts">نمایش تقویت‌ها</string>
<string name="pref_title_show_replies">نمایش پاسخ‌ها</string>
<string name="pref_title_show_media_preview">بارگیری پیش‌نمایش رسانه</string>
<string name="pref_title_proxy_settings">پراکسی</string>
<string name="pref_title_http_proxy_settings">پراکسی HTTP</string>
<string name="pref_title_http_proxy_enable">فعال‌سازی پراکسی HTTP</string>
<string name="pref_title_proxy_settings">پیشکار</string>
<string name="pref_title_http_proxy_settings">پیشکار HTTP</string>
<string name="pref_title_http_proxy_enable">به کار انداختن پیشکار HTTP</string>
<string name="pref_title_http_proxy_server">کارساز پیشکار HTTP</string>
<string name="pref_title_http_proxy_port">درگاه پیشکار HTTP</string>
<string name="pref_default_post_privacy">محرمانگی پیش‌گزیدهٔ فرسته</string>
<string name="pref_default_media_sensitivity">همواره رسانه را به عنوان حساس نشانه‌گذاری کن</string>
<string name="pref_default_media_sensitivity">علامت‌گذاری همیشگی رسانه به عنوان حساس</string>
<string name="pref_publishing">در حال انتشار (همگام با کارساز)</string>
<string name="pref_failed_to_sync">ناتوانی در هم‌گام‌سازی تنظیمات</string>
<string name="post_privacy_public">عمومی</string>
@ -279,7 +279,7 @@
<string name="title_statuses_pinned">سنجاق‌شده</string>
<string name="title_domain_mutes">دامنه‌های نهفته</string>
<string name="status_username_format">\@%s</string>
<string name="message_empty">این‌حا هیچ‌چیزی نیست.</string>
<string name="message_empty">این‌جا هیچ‌چیزی نیست.</string>
<string name="action_unreblog">برداشتن تقویت</string>
<string name="action_unfavourite">برداشتن برگزیدگی</string>
<string name="action_delete_and_redraft">حذف و بازنویسی</string>
@ -289,11 +289,11 @@
<string name="action_add_tab">افزودن زبانه</string>
<string name="action_links">پیوندها</string>
<string name="action_mentions">اشاره‌ها</string>
<string name="action_hashtags">هشتگها</string>
<string name="action_hashtags">برچسبها</string>
<string name="action_open_reblogger">گشودن تقویت‌کنندهٔ بوق</string>
<string name="action_open_reblogged_by">نمایش تقویت‌ها</string>
<string name="action_open_faved_by">نمایش برگزیده‌ها</string>
<string name="title_hashtags_dialog">هشتگها</string>
<string name="title_hashtags_dialog">برچسبها</string>
<string name="title_mentions_dialog">اشاره‌ها</string>
<string name="title_links_dialog">پیوندها</string>
<string name="action_open_media_n">گشودن رسانه #%d</string>
@ -305,11 +305,11 @@
<string name="dialog_redraft_toot_warning">می‌خواهید این بوق را پاک و بازنویسی کنید؟</string>
<string name="mute_domain_warning_dialog_ok">نهفتن تمام دامنه</string>
<string name="pref_title_notification_filter_poll">پایان نظرسنجی‌ها</string>
<string name="pref_title_timeline_filters">صافیها</string>
<string name="app_theme_system">استفاده از طرح سیستم</string>
<string name="pref_title_timeline_filters">پالایهها</string>
<string name="app_theme_system">استفاده از طراحی سامانه</string>
<string name="pref_title_language">زبان</string>
<string name="pref_title_bot_overlay">نمایش نشانگر برای بات‌ها</string>
<string name="pref_title_animate_gif_avatars">پویانمایی آواتار gif</string>
<string name="pref_title_animate_gif_avatars">پویانمایی آواتارهای جیف</string>
<string name="notification_poll_name">نظرسنجی‌ها</string>
<string name="notification_poll_description">آگاهی‌ها دربارهٔ نظرسنجی‌های پایان‌یافته</string>
<string name="about_tusky_version">تاسکی %s</string>
@ -364,7 +364,7 @@
<string name="description_visiblity_direct">مستقیم</string>
<string name="description_poll">نظرسنجی با گزینه‌ها: %1$s، %2$s، %3$s، %4$s؛ %5$s</string>
<string name="hint_list_name">نام فهرست</string>
<string name="edit_hashtag_hint">هشتگ بدون #</string>
<string name="edit_hashtag_hint">برچسب بدون #</string>
<string name="notifications_clear">پاک‌سازی</string>
<string name="notifications_apply_filter">پالایش</string>
<string name="filter_apply">اعمال</string>
@ -434,15 +434,15 @@
<string name="no_saved_status">هیچ پیش‌نویسی ندارید.</string>
<string name="no_scheduled_status">هیچ وضعیت زمان‌بسته‌ای ندارید.</string>
<string name="warning_scheduling_interval">ماستودون، بازهٔ زمان‌بندی‌ای با کمینهٔ ۵ دقیقه دارد.</string>
<string name="pref_title_confirm_reblogs">نمایش گفت‌وگوی تأیید پیش از تقویت</string>
<string name="pref_title_confirm_reblogs">نمایش گفت‌وگوی تأیید، پیش از تقویت</string>
<string name="pref_title_show_cards_in_timelines">پیش‌نمایش پیوندها در خط‌زمانی‌ها</string>
<string name="pref_title_enable_swipe_for_tabs">به کار انداختن اشارهٔ کشیدنی برای تعویض بین زبانه‌ها</string>
<plurals name="poll_info_people">
<item quantity="one">%s نفر</item>
<item quantity="other">%s نفر</item>
</plurals>
<string name="hashtags">هشتگها</string>
<string name="add_hashtag_title">افزودن هشتگ</string>
<string name="hashtags">برچسبها</string>
<string name="add_hashtag_title">افزودن برچسب</string>
<string name="notification_follow_request_description">آگاهی‌ها دربارهٔ درخواست‌های پی‌گیری</string>
<string name="notification_follow_request_name">درخواست‌های پی‌گیری</string>
<string name="pref_title_notification_filter_follow_requests">درخواست پی‌گیری</string>
@ -470,4 +470,8 @@
</plurals>
<string name="pref_title_hide_top_toolbar">نهفتن عنوان نوارابزار بالایی</string>
<string name="compose_preview_image_description">کنش‌ها برای تصویر %s</string>
<string name="account_note_saved">ذخیره شد!</string>
<string name="account_note_hint">یادداشت خصوصیتان دربارهٔ این حساب</string>
<string name="no_announcements">هیچ اعلامیه‌ای وجود ندارد.</string>
<string name="title_announcements">اعلامیه‌ها</string>
</resources>

View File

@ -39,7 +39,7 @@
<string name="title_saved_toot">Brouillons</string>
<string name="title_licenses">Licences</string>
<string name="status_username_format">\@%s</string>
<string name="status_boosted_format">%s a boosté</string>
<string name="status_boosted_format">%s a partagé</string>
<string name="status_sensitive_media_title">Contenu sensible</string>
<string name="status_media_hidden_title">Média caché</string>
<string name="status_sensitive_media_directions">Cliquer pour voir</string>
@ -49,7 +49,7 @@
<string name="status_content_show_less">Replier</string>
<string name="message_empty">Rien ici.</string>
<string name="footer_empty">Il ny a aucun pouet ici pour linstant. Glissez vers le bas pour actualiser !</string>
<string name="notification_reblog_format">%s a boosté votre pouet</string>
<string name="notification_reblog_format">%s a partagé votre pouet</string>
<string name="notification_favourite_format">%s a ajouté votre pouet à ses favoris</string>
<string name="notification_follow_format">%s vous suit</string>
<string name="report_username_format">Signaler @%s</string>
@ -69,8 +69,8 @@
<string name="action_unfollow">Ne plus suivre</string>
<string name="action_block">Bloquer</string>
<string name="action_unblock">Débloquer</string>
<string name="action_hide_reblogs">Cacher les boosts</string>
<string name="action_show_reblogs">Afficher les boosts</string>
<string name="action_hide_reblogs">Cacher les partages</string>
<string name="action_show_reblogs">Afficher les partages</string>
<string name="action_report">Signaler</string>
<string name="action_delete">Supprimer</string>
<string name="action_send">POUET</string>
@ -109,8 +109,8 @@
<string name="action_links">Liens</string>
<string name="action_mentions">Mentions</string>
<string name="action_hashtags">Hashtags</string>
<string name="action_open_reblogger">Afficher lauteur·rice du boost</string>
<string name="action_open_reblogged_by">Afficher les boosts</string>
<string name="action_open_reblogger">Afficher lauteur·rice du partage</string>
<string name="action_open_reblogged_by">Afficher les partages</string>
<string name="action_open_faved_by">Montrer les favoris</string>
<string name="title_hashtags_dialog">Hashtags</string>
<string name="title_mentions_dialog">Mentions</string>
@ -167,10 +167,10 @@
<string name="pref_title_notification_alert_sound">Notifier avec un son</string>
<string name="pref_title_notification_alert_vibrate">Notifier avec une vibration</string>
<string name="pref_title_notification_alert_light">Notifier avec une lumière</string>
<string name="pref_title_notification_filters">Me notifier lorsque</string>
<string name="pref_title_notification_filters">Me notifier quand</string>
<string name="pref_title_notification_filter_mentions">on me mentionne</string>
<string name="pref_title_notification_filter_follows">On me suit</string>
<string name="pref_title_notification_filter_reblogs">Mes pouets sont boostés</string>
<string name="pref_title_notification_filter_follows">on vient de me suivre</string>
<string name="pref_title_notification_filter_reblogs">mes pouets sont partagés</string>
<string name="pref_title_notification_filter_favourites">mes pouets sont mis en favoris</string>
<string name="pref_title_appearance_settings">Apparence</string>
<string name="pref_title_app_theme">Thème de lapplication</string>
@ -187,7 +187,7 @@
<string name="pref_title_language">Langue</string>
<string name="pref_title_status_filter">Filtrage des fils</string>
<string name="pref_title_status_tabs">Onglets</string>
<string name="pref_title_show_boosts">Afficher les boosts</string>
<string name="pref_title_show_boosts">Afficher les partages</string>
<string name="pref_title_show_replies">Afficher les réponses</string>
<string name="pref_title_show_media_preview">Montrer les miniatures des médias</string>
<string name="pref_title_proxy_settings">Proxy</string>
@ -212,8 +212,8 @@
<string name="notification_mention_descriptions">Notifications pour les nouvelles mentions</string>
<string name="notification_follow_name">Nouveaux abonnés</string>
<string name="notification_follow_description">Notifications pour les nouveaux abonnés</string>
<string name="notification_boost_name">Boosts</string>
<string name="notification_boost_description">Notifications quand vos pouets sont boostés</string>
<string name="notification_boost_name">Partages</string>
<string name="notification_boost_description">Notifications quand vos pouets sont partagés</string>
<string name="notification_favourite_name">Favoris</string>
<string name="notification_favourite_description">Notifications quand vos pouets sont mis en favoris</string>
<string name="notification_mention_format">%s vous a mentionné</string>
@ -316,8 +316,8 @@
<string name="download_failed">Échec du téléchargement</string>
<string name="profile_badge_bot_text">Robot</string>
<string name="account_moved_description">%1$s a déménagé vers :</string>
<string name="reblog_private">Booster vers laudience originale</string>
<string name="unreblog_private">Annuler le boost</string>
<string name="reblog_private">Partager à laudience originale</string>
<string name="unreblog_private">Annuler le partage</string>
<string name="license_description">Yuito contient du code et des ressources issus des projets open source suivants :</string>
<string name="license_apache_2">Sous licence Apache (copie ci-dessous)</string>
<string name="license_cc_by_4">CC-BY 4.0</string>
@ -335,10 +335,10 @@
<item quantity="other"><b>%1$s</b> Favoris</item>
</plurals>
<plurals name="reblogs">
<item quantity="one"><b>%s</b> Boost</item>
<item quantity="other"><b>%s</b> Boosts</item>
<item quantity="one"><b>%s</b> Partage</item>
<item quantity="other"><b>%s</b> Partages</item>
</plurals>
<string name="title_reblogged_by">Boosté par</string>
<string name="title_reblogged_by">Partagé par</string>
<string name="title_favourited_by">Mis en favoris par</string>
<string name="conversation_1_recipients">%1$s</string>
<string name="conversation_2_recipients">%1$s et %2$s</string>
@ -362,7 +362,7 @@
<string name="description_visiblity_direct"> Direct
</string>
<string name="hint_list_name">Nom de la liste</string>
<string name="edit_hashtag_hint">Hastags sans #</string>
<string name="edit_hashtag_hint">Hashtag sans #</string>
<string name="notifications_clear">Nettoyer</string>
<string name="notifications_apply_filter">Filtrer</string>
<string name="filter_apply">Appliquer</string>
@ -376,7 +376,7 @@
<string name="poll_info_closed">Terminé</string>
<string name="poll_vote">Voter</string>
<string name="notification_poll_name">Sondages</string>
<string name="pref_title_notification_filter_poll">Les sondages sont clos</string>
<string name="pref_title_notification_filter_poll">les sondages se terminent</string>
<string name="notification_poll_description">Notifications pour les sondages terminés</string>
<string name="poll_ended_created">Un sondage que vous avez créé est terminé</string>
<plurals name="poll_timespan_days">
@ -403,7 +403,7 @@
<string name="title_domain_mutes">Domaines cachés</string>
<string name="action_view_domain_mutes">Domaines cachés</string>
<string name="action_mute_domain">Masquer %s</string>
<string name="confirmation_domain_unmuted">%s nest plus masqué</string>
<string name="confirmation_domain_unmuted">%s nest plus masqué·e</string>
<string name="mute_domain_warning_dialog_ok">Masquer le domaine entier</string>
<string name="caption_notoemoji">Lensemble démojis actuel de Google</string>
<string name="button_continue">Continuer</string>
@ -452,28 +452,28 @@
<string name="error_audio_upload_size">Les fichiers audio doivent avoir moins de 40 Mo.</string>
<string name="no_saved_status">Vous navez aucun brouillon.</string>
<string name="no_scheduled_status">Vous navez aucun pouet planifié.</string>
<string name="warning_scheduling_interval">Lintervalle minimum de planification sur Mastodon est de5 minutes.</string>
<string name="warning_scheduling_interval">Lintervalle minimum de planification sur Mastodon est de 5 minutes.</string>
<string name="notification_follow_request_name">Demandes d\'abonnement</string>
<string name="dialog_block_warning">Bloquer @%s \?</string>
<string name="pref_title_confirm_reblogs">Afficher une boite de confirmation avant de booster</string>
<string name="pref_title_show_cards_in_timelines">Afficher des aperçus des liens dans les fils</string>
<string name="pref_title_confirm_reblogs">Afficher une fenêtre de confirmation avant de partager</string>
<string name="pref_title_show_cards_in_timelines">Afficher les aperçus des liens dans les fils</string>
<string name="notification_follow_request_format">%s a demandé à vous suivre</string>
<string name="notification_follow_request_description">Notifications à propos des demandes dabonnement</string>
<string name="pref_title_notification_filter_follow_requests">Demande dabonnement</string>
<string name="pref_title_notification_filter_follow_requests">on demande à me suivre</string>
<string name="dialog_mute_warning">Mettre en sourdine @%s \?</string>
<string name="action_unmute_conversation">Enlever la sourdine à la conversation</string>
<string name="action_mute_conversation">Silencer la conversation</string>
<string name="pref_title_enable_swipe_for_tabs">Activer les gestes de glissement pour passer dun onglet à lautre</string>
<string name="action_mute_conversation">Masquer la conversation</string>
<string name="pref_title_enable_swipe_for_tabs">Activer le glissement pour changer donglet</string>
<string name="hashtags">Hashtags</string>
<string name="add_hashtag_title">Ajouter hashtag</string>
<string name="add_hashtag_title">Ajouter un hashtag</string>
<string name="pref_title_gradient_for_media">Afficher des dégradés en couleur pour les médias cachés</string>
<string name="pref_main_nav_position_option_bottom">Bas</string>
<string name="pref_main_nav_position_option_top">Haut</string>
<string name="pref_main_nav_position">Position de navigation principale</string>
<string name="action_unmute_domain">Ne plus masquer %s</string>
<string name="dialog_mute_hide_notifications">Cacher les notifications</string>
<string name="action_mute_notifications_desc">Silencier les notifications de %s</string>
<string name="action_unmute_notifications_desc">Ne plus silencier les notifications de %s</string>
<string name="action_mute_notifications_desc">Masquer les notifications de %s</string>
<string name="action_unmute_notifications_desc">Ne plus masquer les notifications de %s</string>
<string name="action_unmute_desc">Ne plus masquer %s</string>
<plurals name="poll_info_people">
<item quantity="one">%s personne</item>
@ -484,4 +484,8 @@
<item quantity="one">%s voix</item>
<item quantity="other">%s voix</item>
</plurals>
<string name="account_note_saved">Sauvegardé !</string>
<string name="account_note_hint">Votre note privée sur ce compte</string>
<string name="no_announcements">Il ny a pas dannonce.</string>
<string name="title_announcements">Annonces</string>
</resources>

View File

@ -472,4 +472,8 @@
<string name="action_unmute_notifications_desc">Értesítések némításának feloldása tőle %s</string>
<string name="notification_follow_request_format">%s kéri, hogy követhessen</string>
<string name="pref_title_hide_top_toolbar">Felső eszköztár címének elrejtése</string>
<string name="account_note_saved">Elmentve!</string>
<string name="account_note_hint">Saját, mások számára nem látható megjegyzés erről a fiókról</string>
<string name="no_announcements">Nincsenek közlemények.</string>
<string name="title_announcements">Közlemények</string>
</resources>

View File

@ -415,8 +415,8 @@
<string name="notification_follow_request_name">Fylgjendabeiðnir</string>
<string name="hashtags">Myllumerki</string>
<plurals name="favs">
<item quantity="one"><b>%1$s</b> Eftirlæti</item>
<item quantity="other"><b>%1$s</b> Eftirlæti</item>
<item quantity="one"><b>%1$s</b> eftirlæti</item>
<item quantity="other"><b>%1$s</b> eftirlæti</item>
</plurals>
<plurals name="reblogs">
<item quantity="one"><b>%s</b> Endurbirting</item>
@ -438,4 +438,33 @@
<item quantity="one">%d dagur eftir</item>
<item quantity="other">%d dagar eftir</item>
</plurals>
<string name="account_note_saved">Vistað!</string>
<string name="account_note_hint">Þí eigin einkaathugasemd um þennan aðgang</string>
<string name="pref_title_hide_top_toolbar">Fela titil á verkfærastikunni efst</string>
<string name="pref_title_confirm_reblogs">Birta staðfestingarglugga áður en endurbirting fer fram</string>
<string name="pref_title_show_cards_in_timelines">Birta forskoðun tengla á tímalínum</string>
<string name="no_announcements">Það eru engar tilkynningar.</string>
<string name="pref_title_enable_swipe_for_tabs">Virkja strokuhreyfingu til að skipta milli flipa</string>
<plurals name="poll_info_people">
<item quantity="one">%s aðili</item>
<item quantity="other">%s aðilar</item>
</plurals>
<string name="add_hashtag_title">Bæta við myllumerki</string>
<string name="notification_follow_request_description">Tilkynningar um fylgjendabeiðnir</string>
<string name="pref_main_nav_position_option_bottom">Neðst</string>
<string name="pref_main_nav_position_option_top">Efst</string>
<string name="pref_main_nav_position">Aðalstaða leiðsagnar</string>
<string name="pref_title_gradient_for_media">Birta litstigla í stað falins myndefnis</string>
<string name="pref_title_notification_filter_follow_requests">beiðni um að fylgja</string>
<string name="dialog_mute_hide_notifications">Fela tilkynningar</string>
<string name="dialog_mute_warning">Þagga niður í @%s\?</string>
<string name="dialog_block_warning">Loka á @%s\?</string>
<string name="action_unmute_conversation">Hætta að þagga niður í samtölum</string>
<string name="action_mute_conversation">Þagga niður í samtölum</string>
<string name="action_unmute_domain">Afþagga %s</string>
<string name="action_mute_notifications_desc">Þagga tilkynningar frá %s</string>
<string name="action_unmute_notifications_desc">Afþagga tilkynningar frá %s</string>
<string name="action_unmute_desc">Afþagga %s</string>
<string name="notification_follow_request_format">%s bað um að fylgjast með þér</string>
<string name="title_announcements">Tilkynningar</string>
</resources>

View File

@ -414,8 +414,8 @@
<item quantity="other">%d minuti rimasti</item>
</plurals>
<plurals name="poll_timespan_seconds">
<item quantity="one">%d secondo</item>
<item quantity="other">%d secondi</item>
<item quantity="one">%d secondo rimasto</item>
<item quantity="other">%d secondi rimasti</item>
</plurals>
<string name="button_continue">Continua</string>
<string name="button_back">Indietro</string>
@ -464,4 +464,23 @@
<string name="error_audio_upload_size">La dimensione dei file audio deve essere inferiore a 40 MB.</string>
<string name="action_unmute_desc">Smetti di silenziare %s</string>
<string name="notification_follow_request_name">Richieste di seguirti</string>
<string name="account_note_saved">Salvato!</string>
<string name="account_note_hint">La tua nota privata su questo account</string>
<string name="pref_title_hide_top_toolbar">Nascondi il titolo della barra degli strumenti in alto</string>
<string name="pref_title_confirm_reblogs">Mostra la finestra di dialogo di conferma prima del boosting</string>
<string name="pref_title_show_cards_in_timelines">Mostra le anteprime dei collegamenti nelle sequenze temporali</string>
<string name="warning_scheduling_interval">Mastodon ha un intervallo minimo di programmazione di 5 minuti.</string>
<string name="no_announcements">Non ci sono annunci.</string>
<string name="no_scheduled_status">Non hai stati pianificati.</string>
<string name="pref_title_enable_swipe_for_tabs">Abilita il gesto di scorrimento per passare da una scheda all\'altra</string>
<string name="notification_follow_request_description">Notifiche sulle richieste di essere seguiti</string>
<string name="pref_main_nav_position_option_bottom">Parte inferiore</string>
<string name="pref_main_nav_position_option_top">In cima</string>
<string name="pref_main_nav_position">Posizione di navigazione principale</string>
<string name="pref_title_gradient_for_media">Mostra sfumature colorate per i media nascosti</string>
<string name="dialog_mute_hide_notifications">Nascondi notifiche</string>
<string name="action_mute_notifications_desc">Disattiva le notifiche da %s</string>
<string name="action_unmute_notifications_desc">Riattiva le notifiche da %s</string>
<string name="title_announcements">Annunci</string>
<string name="pref_title_notification_filter_follow_requests">Richieste di seguirti</string>
</resources>

View File

@ -465,4 +465,6 @@
<string name="pref_title_hide_top_toolbar">Skjul tittelen på den øverste verktøylinjen</string>
<string name="account_note_saved">Lagret!</string>
<string name="account_note_hint">Ditt private notat om denne kontoen</string>
<string name="no_announcements">Det er ingen kunngjøringer.</string>
<string name="title_announcements">Kunngjøringer</string>
</resources>

View File

@ -500,4 +500,9 @@
<string name="action_mute_notifications_desc">Заблокировать уведомления от %s</string>
<string name="action_unmute_notifications_desc">Получать уведомления от %s</string>
<string name="action_unmute_desc">Разблокировать %s</string>
<string name="account_note_saved">Сохранено!</string>
<string name="account_note_hint">Ваша личная заметка об этой учётной записи</string>
<string name="pref_title_hide_top_toolbar">Скрыть заголовок в верхней панели</string>
<string name="no_announcements">Объявлений нет.</string>
<string name="title_announcements">Объявления</string>
</resources>

View File

@ -369,7 +369,6 @@
<string name="pref_title_show_cards_in_timelines">जालस्थानप्रदर्शनं समयतालिकायां दर्शयतु</string>
<string name="warning_scheduling_interval">मास्टोडोने पञ्चनिमेषपरिमितो न्यूनतमः कालबद्धसमयः ।</string>
<string name="no_scheduled_status">न ते कालबद्धदौत्यानि सन्ति ।</string>
<string name="post_lookup_error_format">प्रकटनान्वेषणे विफलता</string>
<string name="edit_poll">सम्पाद्यताम्</string>
<string name="poll_new_choice_hint">मतम् %d</string>
<string name="poll_allow_multiple_choices">बहूनि मतानि</string>

View File

@ -480,4 +480,8 @@
<string name="dialog_mute_hide_notifications">Dölj aviseringar</string>
<string name="action_mute_notifications_desc">Tysta aviseringar från %s</string>
<string name="action_unmute_notifications_desc">Aktivera aviseringar från %s</string>
<string name="account_note_saved">Sparat!</string>
<string name="account_note_hint">Din privata notering om detta kontot</string>
<string name="no_announcements">Det finns inga meddelanden.</string>
<string name="title_announcements">Meddelanden</string>
</resources>

View File

@ -39,7 +39,7 @@
<string name="error_no_web_browser_found">Không tìm thấy trình duyệt web.</string>
<string name="error_invalid_domain">Tài khoản không đúng</string>
<string name="error_empty">Không được để trống.</string>
<string name="error_network">Không có Internet! Xin kiểm tra kết nối và thử lại!</string>
<string name="error_network">Rớt mạng! Xin kiểm tra kết nối và thử lại!</string>
<string name="error_generic">Đã có lỗi xảy ra.</string>
<string name="error_failed_app_registration">Máy chủ này không cấp quyền truy cập.</string>
<string name="title_lists">Danh sách</string>
@ -48,7 +48,7 @@
<string name="action_reset_schedule">Làm tươi</string>
<string name="action_search">Tìm kiếm</string>
<string name="action_edit_profile">Trang cá nhân</string>
<string name="action_view_account_preferences">Riêng bạn</string>
<string name="action_view_account_preferences">Tài khoản</string>
<string name="action_view_preferences">Cài đặt</string>
<string name="action_logout">Đăng xuất</string>
<string name="button_done">Xong</string>
@ -97,7 +97,7 @@
<string name="download_media">Tải về</string>
<string name="action_share_as">Chia sẻ với tư cách …</string>
<string name="action_open_as">Mở với tư cách %s</string>
<string name="action_copy_link">Sao chép URL</string>
<string name="action_copy_link">Chép URL</string>
<string name="download_image">Đang tải %1$s</string>
<string name="action_open_media_n">Mở tập tin #%d</string>
<string name="title_links_dialog">Links</string>
@ -174,7 +174,7 @@
<string name="status_content_warning_show_more">Mở rộng</string>
<string name="status_sensitive_media_directions">Hiển thị</string>
<string name="status_media_hidden_title">Nội dung bị ẩn</string>
<string name="status_sensitive_media_title">Nội dung nhạy cảm</string>
<string name="status_sensitive_media_title">Nhạy cảm</string>
<string name="status_boosted_format">%s chia sẻ</string>
<string name="status_username_format">\@%s</string>
<string name="title_licenses">Giấy phép</string>
@ -236,7 +236,7 @@
<string name="pref_title_edit_notification_settings">Thông báo</string>
<string name="visibility_direct">Nhắn tin: Chỉ người được nhắc tới mới thấy</string>
<string name="visibility_private">Người theo dõi: Ai đã theo dõi mới xem được</string>
<string name="visibility_unlisted">Mở: Công khai, không hiện trên bảng tin</string>
<string name="visibility_unlisted">Riêng tư: Không hiện trên bảng tin</string>
<string name="visibility_public">Công khai: Mọi người đều có thể thấy</string>
<string name="dialog_mute_warning">Ẩn @%s\?</string>
<string name="dialog_block_warning">Chặn @%s\?</string>
@ -253,20 +253,20 @@
<string name="status_text_size_smallest">Nhỏ</string>
<string name="pref_status_text_size">Kích thức phông chữ</string>
<string name="post_privacy_followers_only">Người theo dõi</string>
<string name="post_privacy_unlisted">Mở</string>
<string name="post_privacy_unlisted">Riêng tư</string>
<string name="post_privacy_public">Công khai</string>
<string name="pref_main_nav_position_option_bottom">Dưới màn hình</string>
<string name="pref_main_nav_position_option_top">Trên màn hình</string>
<string name="pref_main_nav_position">Vị trí menu</string>
<string name="pref_failed_to_sync">Đồng bộ hoá thất bại</string>
<string name="pref_publishing">Đăng (đồng bộ với máy chủ)</string>
<string name="pref_default_media_sensitivity">Luôn đánh dấu nội dung là nhạy cảm</string>
<string name="pref_default_media_sensitivity">Tài khoản nhạy cảm</string>
<string name="pref_default_post_privacy">Trạng thái tút mặc định</string>
<string name="pref_title_http_proxy_server">HTTP proxy server</string>
<string name="pref_title_http_proxy_port">HTTP proxy port</string>
<string name="pref_title_http_proxy_enable">Bật HTTP proxy</string>
<string name="pref_title_http_proxy_settings">HTTP proxy</string>
<string name="pref_title_proxy_settings">Proxy</string>
<string name="pref_title_http_proxy_server">Máy chủ proxy</string>
<string name="pref_title_http_proxy_port">Cổng</string>
<string name="pref_title_http_proxy_enable">Bật proxy</string>
<string name="pref_title_http_proxy_settings">Dùng proxy</string>
<string name="pref_title_proxy_settings">Vượt tường lửa</string>
<string name="notification_boost_description">Thông báo khi của bạn được chia sẻ</string>
<string name="notification_boost_name">Chia sẻ</string>
<string name="notification_follow_request_description">Thông báo về lượt yêu cầu theo dõi</string>
@ -278,7 +278,7 @@
<string name="about_powered_by_tusky">Powered by Tusky</string>
<string name="about_tusky_version">Tusky %s</string>
<string name="description_account_locked">Tài khoản bị khóa</string>
<string name="notification_poll_description">Thông báo khi những cuộc bình chọn kết thúc</string>
<string name="notification_poll_description">Thông báo khi một cuộc bình chọn kết thúc</string>
<string name="notification_poll_name">Bình chọn</string>
<string name="notification_favourite_description">Thông báo khi ai đó thích tút của bạn</string>
<string name="notification_favourite_name">Lượt thích</string>
@ -326,7 +326,7 @@
<string name="poll_duration_30_min">30 phút</string>
<string name="poll_duration_5_min">5 phút</string>
<string name="create_poll_title">Bình chọn</string>
<string name="pref_title_enable_swipe_for_tabs">Vuốt để chuyển qua lại giữa các tab</string>
<string name="pref_title_enable_swipe_for_tabs">Vuốt qua lại giữa các tab</string>
<string name="pref_title_show_notifications_filter">Hiện bộ lọc thông báo</string>
<string name="failed_search">Không thể tìm thấy</string>
<string name="title_accounts">Người</string>
@ -350,7 +350,7 @@
<item quantity="other">%d ngày nữa kết thúc</item>
</plurals>
<string name="poll_ended_created">Cuộc bình chọn bạn tạo đã kết thúc</string>
<string name="poll_ended_voted">Cuộc bình chọn bạn tham gia đã kết thúc</string>
<string name="poll_ended_voted">Cuộc bình chọn của bạn đã kết thúc</string>
<string name="poll_vote">Bình chọn</string>
<string name="poll_info_closed">Kết thúc</string>
<string name="poll_info_time_absolute">kết thúc lúc %s</string>
@ -358,9 +358,9 @@
<item quantity="other">%s người</item>
</plurals>
<plurals name="poll_info_votes">
<item quantity="other">%s phiếu</item>
<item quantity="other">%s người</item>
</plurals>
<string name="poll_info_format"> <!-- 15 phiếu • 1 tiếng nữa kết thúc --> %1$s • %2$s</string>
<string name="poll_info_format"> <!-- 15 người • 1 tiếng nữa kết thúc --> %1$s • %2$s</string>
<string name="compose_preview_image_description">Mô tả cho hình %s</string>
<string name="compose_shortcut_short_label">Viết</string>
<string name="compose_shortcut_long_label">Viết tút</string>
@ -373,10 +373,10 @@
<string name="edit_hashtag_hint">Không cần dấu #</string>
<string name="add_hashtag_title">Thêm hashtag</string>
<string name="hint_list_name">Tên danh sách</string>
<string name="description_poll">Những lựa chọn: %1$s, %2$s, %3$s, %4$s; %5$s</string>
<string name="description_poll">Lượt bình chọn: %1$s, %2$s, %3$s, %4$s; %5$s</string>
<string name="description_visiblity_direct">Tin nhắn</string>
<string name="description_visiblity_private">Người theo dõi</string>
<string name="description_visiblity_unlisted">Mở</string>
<string name="description_visiblity_unlisted">Riêng tư</string>
<string name="description_visiblity_public">Công khai</string>
<string name="description_status_bookmarked">Đã lưu</string>
<string name="description_status_favourited">Đã thích</string>
@ -455,4 +455,8 @@
<string name="action_unmute_desc">Bỏ ẩn %s</string>
<string name="action_unmute_domain">Bỏ ẩn %s</string>
<string name="pref_title_hide_top_toolbar">Ẩn tiêu đề tab</string>
<string name="account_note_saved">Đã lưu!</string>
<string name="account_note_hint">Ghi chú của bạn</string>
<string name="no_announcements">Chưa có thông báo.</string>
<string name="title_announcements">Tin tức</string>
</resources>

View File

@ -477,8 +477,8 @@
<string name="notification_follow_request_format">%s 请求关注你</string>
<string name="pref_main_nav_position">导航栏位置</string>
<string name="pref_title_hide_top_toolbar">隐藏顶部工具栏标题</string>
<plurals name="poll_info_people">
<item quantity="one">%s 人</item>
<item quantity="other"/>
</plurals>
<string name="no_announcements">本站暂无公告。</string>
<string name="title_announcements">公告</string>
<string name="account_note_saved">已保存</string>
<string name="account_note_hint">此账号的备注</string>
</resources>

View File

@ -81,6 +81,8 @@
<item name="chipStyle">@style/Widget.MaterialComponents.Chip.Choice</item>
<item name="preferenceTheme">@style/TuskyPreferenceTheme</item>
<item name="colorInfo">@color/colorInfo</item>
</style>
@ -105,6 +107,10 @@
<item name="tabIndicatorHeight">3dp</item>
</style>
<style name="TuskyPreferenceTheme" parent="@style/PreferenceThemeOverlay.v14.Material">
<item name="android:tint">?iconColor</item>
</style>
<style name="TuskyImageButton" parent="@style/Widget.MaterialComponents.Button.UnelevatedButton">
<item name="android:tint">?android:attr/textColorTertiary</item>
<item name="android:background">?attr/selectableItemBackgroundBorderless</item>

View File

@ -17,6 +17,7 @@
package com.keylesspalace.tusky
import android.content.Intent
import android.os.Looper.getMainLooper
import android.text.SpannedString
import android.widget.EditText
import androidx.test.ext.junit.runners.AndroidJUnit4
@ -41,6 +42,7 @@ import org.junit.runner.RunWith
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.robolectric.Robolectric
import org.robolectric.Shadows.shadowOf
import org.robolectric.annotation.Config
import org.robolectric.fakes.RoboMenuItem
@ -76,8 +78,8 @@ class ComposeActivityTest {
notificationVibration = true,
notificationLight = true
)
var instanceResponseCallback: (()->Instance)? = null
var composeOptions: ComposeActivity.ComposeOptions? = null
private var instanceResponseCallback: (()->Instance)? = null
private var composeOptions: ComposeActivity.ComposeOptions? = null
@Before
fun setupActivity() {
@ -185,6 +187,7 @@ class ComposeActivityTest {
val customMaximum = 1000
instanceResponseCallback = { getInstanceWithMaximumTootCharacters(customMaximum) }
setupActivity()
shadowOf(getMainLooper()).idle()
assertEquals(customMaximum, activity.maximumTootCharacters)
}
@ -230,7 +233,7 @@ class ComposeActivityTest {
editor.setSelection(caretIndex)
activity.prependSelectedWordsWith(insertText)
// Text should be inserted at caret
assertEquals("Unexpected value at ${caretIndex}", insertText, editor.text.substring(caretIndex, caretIndex + insertText.length))
assertEquals("Unexpected value at $caretIndex", insertText, editor.text.substring(caretIndex, caretIndex + insertText.length))
// Caret should be placed after inserted text
assertEquals(caretIndex + insertText.length, editor.selectionStart)

View File

@ -1,11 +1,11 @@
buildscript {
ext.kotlin_version = '1.4.10'
ext.kotlin_version = '1.4.21'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.android.tools.build:gradle:4.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

View File

@ -0,0 +1,10 @@
Tusky v13.0
- support for profile notes (Mastodon 3.2.0 feature)
- support for admin announcements (Mastodon 3.1.0 feature)
- the avatar of your selected account will now be shown in the main toolbar
- clicking the display name in a timeline will now open the profile page of that user
- a lot of bug fixes and small improvements
- improved translations

View File

@ -0,0 +1,10 @@
Tusky v13.0
- privát profilmegjegyzések támogatása (Mastodon 3.2.0 funkció)
- adminisztrátori közlemények támogatása (Mastodon 3.1.0 funkció)
- az éppen használt fiókod avatarja mostantól látszik az eszköztáron
- az idővonalon egy profilra kattintva előjön a felhasználó profiloldala
- rengeteg hibajavítás és apró fejlesztés
- javított fordítások

View File

@ -0,0 +1,10 @@
Tusky útg. 13.0
- stuðningur við minnispunkta í sniðum (Mastodon 3.2.0 eiginleiki)
- stuðningur við tilkynningar frá stjórnendum (Mastodon 3.1.0 eiginleiki)
- auðkennismynd úr völdum aðgangi birist núna í aðalverkfærastikunni
- smellt á birtingarnafn á tímalínu opnar núna notandasniðssíðu þess notanda
- hellingur að villulagfæringum og minni betrumbótum
- bættar þýðingar

View File

@ -0,0 +1,8 @@
Tusky v10.0
- Ora puoi contrassegnare gli stati ed elencare i tuoi segnalibri in Tusky.
- Ora puoi programmare i tuoi toot con Tusky. Tieni presente che il tempo selezionato deve essere di almeno 5 minuti in futuro.
- Ora puoi aggiungere elenchi alla schermata principale.
- Ora puoi pubblicare allegati audio con Tusky.
E molti altri piccoli miglioramenti e correzioni di bug!

View File

@ -0,0 +1,8 @@
Tusky v12.0
- Interfaccia principale migliorata - ora puoi spostare le schede in basso
- Quando si disattiva l'audio di un utente, ora è possibile anche decidere se disattivare l'audio delle sue notifiche
- Ora puoi seguire tutti gli hashtag che desideri in una singola scheda hashtag
- Migliorata la modalità di visualizzazione delle descrizioni dei media in modo che funzioni anche per descrizioni molto lunghe
Log delle modifiche completo: https://github.com/tuskyapp/Tusky/releases

View File

@ -0,0 +1,10 @@
Tusky v13.0
- supporto per le note del profilo (funzionalità di Mastodon 3.2.0)
- supporto per gli annunci dell'amministratore (funzionalità di Mastodon 3.1.0)
- l'avatar del tuo account selezionato verrà ora mostrato nella barra degli strumenti principale
- facendo clic sul nome visualizzato in una sequenza temporale si aprirà ora la pagina del profilo di quell'utente
- molte correzioni di bug e piccoli miglioramenti
- traduzioni migliorate

View File

@ -0,0 +1,10 @@
Tusky v13.0
- støtte for profilnotater (Mastodon 3.2.0-funksjonalitet)
- støtte for administratorkunngjøringer (Mastodon 3.1.0-funksjonalitet)
- avataren som tilhører valgt konto vil nå vises på hovedverktøylinjen
- trykk på en brukers visningsnavn i tidslinjen vil åpne profilen til brukeren
- mange feilrettinger og mindre forbedringer
- forbedrede oversettelser

View File

@ -0,0 +1,8 @@
Tusky v12.0
- Improved main interface - you can now move the tabs to the bottom
- When muting a user, you can now also decide whether to mute their notifications
- You can now follow as many hashtags as you want in one single hashtag tab
- Improved the way media descriptions are displayed so it works even for super long descriptions
Full changelog: https://github.com/tuskyapp/Tusky/releases

View File

@ -0,0 +1,10 @@
Tusky v13.0
- support for profile notes (Mastodon 3.2.0 feature)
- support for admin announcements (Mastodon 3.1.0 feature)
- the avatar of your selected account will now be shown in the main toolbar
- clicking the display name in a timeline will now open the profile page of that user
- a lot of bug fixes and small improvements
- improved translations

View File

@ -0,0 +1,8 @@
Tusky v13.0
- Hỗ trợ ghi chú về một ai đó (tính năng Mastodon 3.2.0)
- Hỗ trợ hiện thông báo máy chủ (tính năng Mastodon 3.1.0)
- Ảnh đại diện của tài khoản từ giờ sẽ hiện trên thanh menu chính
- Nhấn vào tên ai đó trên bảng tin sẽ chuyển tới trang cá nhân của họ
- Sửa lỗi linh tinh và cải thiện hiệu năng
- Trau dồi bản dịch

View File

@ -0,0 +1,10 @@
Tusky v13.0
- 支持账号备注Mastodon 3.2.0 特性)
- 支持公告栏Mastodon 3.1.0特性)
- 当前账号的头像将在导航栏显示
- 在时间线中点击账号名称后打开该用户的资料页
- 其他许多小改进和错误修复
- 改善翻译

View File

@ -0,0 +1 @@
Tusky