diff --git a/app/build.gradle b/app/build.gradle index 13b1fa375c383c0357aeb01c434793f4c305754d..c9abf69c4f744fbe79c3af5ca1546fd43fd28956 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { defaultConfig { minSdk 21 targetSdk 34 - versionCode 528 - versionName "3.31.3" + versionCode 532 + versionName "3.32.3" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } flavorDimensions "default" @@ -109,7 +109,7 @@ allprojects { } } dependencies { - implementation 'org.unifiedpush.android:connector:3.0.7' + implementation 'org.unifiedpush.android:connector:3.0.9' playstoreImplementation('org.unifiedpush.android:embedded-fcm-distributor:3.0.0') diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 94a24f1b5e85b727b5e61e62fc450f3b02d40018..28b4e7fb587e87c238de4aec573ab63e528eaae2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,6 +11,8 @@ tools:ignore="ScopedStorage" /> + + + @@ -228,6 +234,7 @@ android:configChanges="keyboardHidden|orientation|screenSize" /> @@ -750,4 +757,4 @@ android:resource="@xml/compose_shortcuts" /> - \ No newline at end of file + diff --git a/app/src/main/assets/release_notes/notes.json b/app/src/main/assets/release_notes/notes.json index dbb5859157a847e9de9bb014a71dd44b9e0662f2..8f9710a1479f4a08072672a30e1c4c43b25ad696 100644 --- a/app/src/main/assets/release_notes/notes.json +++ b/app/src/main/assets/release_notes/notes.json @@ -1,4 +1,24 @@ [ + { + "version": "3.32.3", + "code": "532", + "note": "Fixed:\n- Polls not displayed\n- Pagination with trends\n- Push notifications not working on some devices" + }, + { + "version": "3.32.2", + "code": "531", + "note": "Added:\n- An outline around media\n\nChanged:\n- Make username, display name in nav drawer clickable\n- Gif media not animated by default\n- Disable by default the mention to the booster when replying. Can be enabled in Settings > Compose (per account)\n\nFixed:\n- Wrong preview picture on share from another app\n- Crash when translating with MinT\n- Refresh and pagination broken for the Trending timeline\n- Fix lags / Crashes" + }, + { + "version": "3.32.1", + "code": "530", + "note": "Fixed:\n- Fix a crash on some devices\n- Hide quote button\n- Fix a layout issue with pictures in landscape\n- Fix a crash when opening the original message from a picture" +}, + { + "version": "3.32.0", + "code": "529", + "note": "Added:\n- Add option to disable auto hiding compose button\n\nChanged:\n- Add more content descriptions for buttons\n- Update some buttons\n- Update navigation drawer header\n- Squeeze action buttons when needed to prevent overlapping\n\nFixed:\n- Fix crash when media are too heavy\n- Some custom emojis in bio do not render\n- Posting messages does not work on some Friendica instances\n- Fix a crash with auto-split messages\n- Fix a crash when opening conversations\n- Fix a background color issue when displaying media" + }, { "version": "3.31.3", "code": "528", diff --git a/app/src/main/java/app/fedilab/android/BaseMainActivity.java b/app/src/main/java/app/fedilab/android/BaseMainActivity.java index 41ff2a0a7284e65b19f6a2da13484e606f4e0a9c..f99cea43451a96433f1c49a2310f3b467805d447 100644 --- a/app/src/main/java/app/fedilab/android/BaseMainActivity.java +++ b/app/src/main/java/app/fedilab/android/BaseMainActivity.java @@ -72,6 +72,7 @@ import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.PopupMenu; import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.Toolbar; +import androidx.appcompat.widget.TooltipCompat; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.core.view.GravityCompat; @@ -354,6 +355,8 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt }; private NetworkStateReceiver networkStateReceiver; + SharedPreferences sharedpreferences; + public static void fetchRecentAccounts(Activity activity, NavHeaderMainBinding headerMainBinding) { SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity); //Fetch some db values to initialize data @@ -461,7 +464,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt public static void manageDrawerMenu(Activity activity, NavigationView navigationView, NavHeaderMainBinding headerMainBinding) { SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity); if (headerMenuOpen) { - headerMainBinding.ownerAccounts.setImageResource(R.drawable.ic_baseline_arrow_drop_up_24); + headerMainBinding.ownerAccounts.setIconResource(R.drawable.ic_baseline_arrow_drop_up_24); new Thread(() -> { try { List accounts = new Account(activity).getOtherAccounts(); @@ -592,7 +595,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt Intent mainActivity = new Intent(activity, MainActivity.class); activity.startActivity(mainActivity); activity.finish(); - headerMainBinding.ownerAccounts.setImageResource(R.drawable.ic_baseline_arrow_drop_down_24); + headerMainBinding.ownerAccounts.setIconResource(R.drawable.ic_accounts); return true; } return false; @@ -624,47 +627,32 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt } else if (Helper.getCurrentAccount(activity).peertube_account != null) { navigationView.inflateMenu(R.menu.activity_main_drawer_peertube); } - headerMainBinding.ownerAccounts.setImageResource(R.drawable.ic_baseline_arrow_drop_down_24); + headerMainBinding.ownerAccounts.setIconResource(R.drawable.ic_accounts); headerMenuOpen = false; } } - public static void headerOptionInfoClick(Activity activity, NavHeaderMainBinding headerMainBinding, FragmentManager fragmentManager) { - PopupMenu popup = new PopupMenu(activity, headerMainBinding.headerOptionInfo); - popup.getMenuInflater() - .inflate(R.menu.main, popup.getMenu()); - - popup.setOnMenuItemClickListener(item -> { - int itemId = item.getItemId(); - if (itemId == R.id.action_logout_account) { - AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(activity); - alt_bld.setTitle(R.string.action_logout); - if (Helper.getCurrentAccount(activity).mastodon_account != null && Helper.getCurrentAccount(activity).instance != null) { - alt_bld.setMessage(activity.getString(R.string.logout_account_confirmation, Helper.getCurrentAccount(activity).mastodon_account.username, Helper.getCurrentAccount(activity).instance)); - } else if (Helper.getCurrentAccount(activity).peertube_account != null && Helper.getCurrentAccount(activity).instance != null) { - alt_bld.setMessage(activity.getString(R.string.logout_account_confirmation, Helper.getCurrentAccount(activity).peertube_account.getUsername(), Helper.getCurrentAccount(activity).instance)); - } else { - alt_bld.setMessage(activity.getString(R.string.logout_account_confirmation, "", "")); - } - alt_bld.setPositiveButton(R.string.action_logout, (dialog, id) -> { - dialog.dismiss(); - try { - Helper.removeAccount(activity); - } catch (DBException e) { - e.printStackTrace(); - } - }); - alt_bld.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); - AlertDialog alert = alt_bld.create(); - alert.show(); - return true; - } else if (itemId == R.id.action_proxy) { - (new ProxyActivity()).show(fragmentManager, null); - return true; + public static void headerLogoutClick(Activity activity, NavHeaderMainBinding headerMainBinding, FragmentManager fragmentManager) { + AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(activity); + alt_bld.setTitle(R.string.action_logout); + if (Helper.getCurrentAccount(activity).mastodon_account != null && Helper.getCurrentAccount(activity).instance != null) { + alt_bld.setMessage(activity.getString(R.string.logout_account_confirmation, Helper.getCurrentAccount(activity).mastodon_account.username, Helper.getCurrentAccount(activity).instance)); + } else if (Helper.getCurrentAccount(activity).peertube_account != null && Helper.getCurrentAccount(activity).instance != null) { + alt_bld.setMessage(activity.getString(R.string.logout_account_confirmation, Helper.getCurrentAccount(activity).peertube_account.getUsername(), Helper.getCurrentAccount(activity).instance)); + } else { + alt_bld.setMessage(activity.getString(R.string.logout_account_confirmation, "", "")); + } + alt_bld.setPositiveButton(R.string.action_logout, (dialog, id) -> { + dialog.dismiss(); + try { + Helper.removeAccount(activity); + } catch (DBException e) { + e.printStackTrace(); } - return true; }); - popup.show(); + alt_bld.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); + AlertDialog alert = alt_bld.create(); + alert.show(); } /** @@ -1125,7 +1113,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(BaseMainActivity.this); + sharedpreferences = PreferenceManager.getDefaultSharedPreferences(BaseMainActivity.this); if (!Helper.isLoggedIn(BaseMainActivity.this)) { //It is not, the user is redirected to the login page Intent myIntent = new Intent(BaseMainActivity.this, LoginActivity.class); @@ -1464,12 +1452,13 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt startActivity(intent); } else if (id == R.id.nav_about_instance) { (new InstanceActivity()).show(getSupportFragmentManager(), null); + } else if (id == R.id.nav_instance_info) { + (new InstanceHealthActivity()).show(getSupportFragmentManager(), null); } binding.drawerLayout.close(); return false; }); - headerMainBinding.instanceInfo.setOnClickListener(v -> (new InstanceHealthActivity()).show(getSupportFragmentManager(), null)); headerMainBinding.accountProfilePicture.setOnClickListener(v -> { Intent intent = new Intent(BaseMainActivity.this, ProfileActivity.class); Bundle args = new Bundle(); @@ -1483,13 +1472,16 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt }); - headerMainBinding.accountAcc.setOnClickListener(v -> headerMainBinding.changeAccount.callOnClick()); - headerMainBinding.changeAccount.setOnClickListener(v -> { + TooltipCompat.setTooltipText(headerMainBinding.ownerAccounts, getString(R.string.manage_accounts)); + headerMainBinding.accountName.setOnClickListener(v -> headerMainBinding.ownerAccounts.performClick()); + headerMainBinding.accountAcc.setOnClickListener(v -> headerMainBinding.ownerAccounts.performClick()); + headerMainBinding.ownerAccounts.setOnClickListener(v -> { headerMenuOpen = !headerMenuOpen; manageDrawerMenu(BaseMainActivity.this, binding.navView, headerMainBinding); }); - headerMainBinding.headerOptionInfo.setOnClickListener(v -> headerOptionInfoClick(BaseMainActivity.this, headerMainBinding, getSupportFragmentManager())); + TooltipCompat.setTooltipText(headerMainBinding.headerLogout,getString(R.string.action_logout)); + headerMainBinding.headerLogout.setOnClickListener(v -> headerLogoutClick(BaseMainActivity.this, headerMainBinding, getSupportFragmentManager())); //Toolbar search binding.toolbarSearch.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @@ -1605,8 +1597,14 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt fetchRecentAccounts(BaseMainActivity.this, headerMainBinding); } + @Override + protected void onResume() { + super.onResume(); + if (!sharedpreferences.getBoolean(getString(R.string.SET_AUTO_HIDE_COMPOSE), true) && !getFloatingVisibility()) + manageFloatingButton(true); + } + private void manageTopBarScrolling(Toolbar toolbar) { - final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this); final boolean topBarScrolling = !sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_TOPBAR_SCROLLING), false); final AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams(); @@ -2029,10 +2027,12 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt } public void manageFloatingButton(boolean display) { - if (display) { - binding.compose.show(); - } else { - binding.compose.hide(); + if (sharedpreferences.getBoolean(getString(R.string.SET_AUTO_HIDE_COMPOSE), true)) { + if (display) { + binding.compose.show(); + } else { + binding.compose.hide(); + } } } diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java index d0e53c863517c72dbd954e6022be322705bf1f65..bfd204e75279335baeb0a39e2a4606b386594fa4 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java @@ -502,6 +502,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana private void initializeAfterBundle(Bundle b) { SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this); + new Thread(() -> { if (b != null) { statusReply = (Status) b.getSerializable(Helper.ARG_STATUS_REPLY); @@ -514,6 +515,8 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana if (account == null) { account = Helper.getCurrentAccount(ComposeActivity.this); } + boolean setMentionBooster = sharedpreferences.getBoolean(getString(R.string.SET_MENTION_BOOSTER) + account.user_id + account.instance, false); + editMessageId = b.getString(Helper.ARG_EDIT_STATUS_ID, null); instance = b.getString(Helper.ARG_INSTANCE, null); token = b.getString(Helper.ARG_TOKEN, null); @@ -523,7 +526,11 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana } else if (visibility == null && Helper.getCurrentAccount(ComposeActivity.this) != null && Helper.getCurrentAccount(ComposeActivity.this).mastodon_account != null && Helper.getCurrentAccount(ComposeActivity.this).mastodon_account.source != null) { visibility = Helper.getCurrentAccount(ComposeActivity.this).mastodon_account.source.privacy; } - mentionBooster = (Account) b.getSerializable(Helper.ARG_MENTION_BOOSTER); + if(setMentionBooster) { + mentionBooster = (Account) b.getSerializable(Helper.ARG_MENTION_BOOSTER); + } else { + mentionBooster = null; + } accountMention = (Account) b.getSerializable(Helper.ARG_ACCOUNT_MENTION); //Shared elements sharedAttachments = (ArrayList) b.getSerializable(Helper.ARG_MEDIA_ATTACHMENTS); diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/MediaActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/MediaActivity.java index aca57088ca11427f1d3be653d6829e07bfb9a299..a705dbf5de4c602785b2d9021728b0335aaed7d8 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/MediaActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/MediaActivity.java @@ -141,6 +141,9 @@ public class MediaActivity extends BaseBarActivity implements OnDownloadInterfac if (bundle != null) { mediaPosition = bundle.getInt(Helper.ARG_MEDIA_POSITION, 1); + if(mediaPosition < 1 ) { + mediaPosition = 1; + } attachments = (ArrayList) bundle.getSerializable(Helper.ARG_MEDIA_ARRAY); mediaFromProfile = bundle.getBoolean(Helper.ARG_MEDIA_ARRAY_PROFILE, false); status = (Status) bundle.getSerializable(Helper.ARG_STATUS); @@ -230,6 +233,9 @@ public class MediaActivity extends BaseBarActivity implements OnDownloadInterfac public void onPageSelected(int position) { mediaPosition = position; + if(mediaPosition < 1 ) { + mediaPosition = 1; + } String description = attachments.get(position).description; if (handler != null) { handler.removeCallbacksAndMessages(null); diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/ProfileActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/ProfileActivity.java index 30af13d8db79992a1f27ca90d507d2e3af60c775..1588c20ce7a96b7e3a50f45189dad982d7e3a483 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/ProfileActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/ProfileActivity.java @@ -82,9 +82,6 @@ import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Set; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; import androidmads.library.qrgenearator.QRGContents; import androidmads.library.qrgenearator.QRGEncoder; @@ -103,6 +100,7 @@ import app.fedilab.android.mastodon.client.entities.api.Field; import app.fedilab.android.mastodon.client.entities.api.IdentityProof; import app.fedilab.android.mastodon.client.entities.api.MastodonList; import app.fedilab.android.mastodon.client.entities.api.RelationShip; +import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.app.CachedBundle; import app.fedilab.android.mastodon.client.entities.app.Languages; import app.fedilab.android.mastodon.client.entities.app.Pinned; @@ -132,7 +130,6 @@ public class ProfileActivity extends BaseActivity { private RelationShip relationship; private FamiliarFollowers familiarFollowers; private Account account; - private ScheduledExecutorService scheduledExecutorService; private action doAction; private AccountsVM accountsVM; private RecyclerView identityProofsRecycler; @@ -357,14 +354,7 @@ public class ProfileActivity extends BaseActivity { this.identityProofList = identityProofs; updateAccount(); }); - //Animate emojis - if (account.emojis != null && !account.emojis.isEmpty()) { - boolean disableAnimatedEmoji = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); - if (!disableAnimatedEmoji) { - scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); - scheduledExecutorService.scheduleAtFixedRate(() -> binding.accountDn.invalidate(), 0, 130, TimeUnit.MILLISECONDS); - } - } + binding.accountTabLayout.clearOnTabSelectedListeners(); binding.accountTabLayout.removeAllTabs(); //Tablayout for timelines/following/followers @@ -550,8 +540,15 @@ public class ProfileActivity extends BaseActivity { }); binding.accountNote.setText( account.getSpanNote(ProfileActivity.this, - new WeakReference<>(binding.accountNote)), + new WeakReference<>(binding.accountNote), () -> { + //TODO: replace this hack + binding.accountNote.setText( + account.getSpanNote(ProfileActivity.this, + new WeakReference<>(binding.accountNote)), TextView.BufferType.SPANNABLE); + + }), TextView.BufferType.SPANNABLE); + binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance()); @@ -1370,10 +1367,6 @@ public class ProfileActivity extends BaseActivity { @Override public void onDestroy() { - if (scheduledExecutorService != null) { - scheduledExecutorService.shutdownNow(); - scheduledExecutorService = null; - } try { unregisterReceiver(broadcast_data); } catch (IllegalArgumentException e) { diff --git a/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Account.java b/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Account.java index 8d4cad2906df1cc9ff6857259a162747e6f03887..2408db28e0224bbc68cf2784fefd1b4665747f1e 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Account.java +++ b/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Account.java @@ -112,6 +112,9 @@ public class Account implements Serializable { public synchronized Spannable getSpanNote(Context context, WeakReference viewWeakReference) { return SpannableHelper.convert(context, note, null, this, null, viewWeakReference, null, true, false); } + public synchronized Spannable getSpanNote(Context context, WeakReference viewWeakReference, Status.Callback callback) { + return SpannableHelper.convert(context, note, null, this, null, viewWeakReference, callback, true, false); + } @Override public boolean equals(@Nullable Object obj) { diff --git a/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java b/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java index 257820ba879504dfac5ba806b305eb727f3e2080..6beaa52c502913b0e12fa0121e76502370e40f53 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java +++ b/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java @@ -34,6 +34,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; @@ -99,6 +100,7 @@ import com.bumptech.glide.load.resource.bitmap.CenterCrop; import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.load.resource.gif.GifDrawable; import com.bumptech.glide.request.RequestOptions; +import com.google.android.material.button.MaterialButton; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -916,7 +918,7 @@ public class Helper { ft.setCustomAnimations(R.anim.enter, R.anim.exit, R.anim.pop_enter, R.anim.pop_exit); Fragment _fragment = fragmentManager.findFragmentByTag(tag); if (_fragment != null && _fragment.isAdded()) { - ft.show(_fragment).commit(); + ft.show(_fragment).commitAllowingStateLoss(); fragment = _fragment; } else { if (args != null) fragment.setArguments(args); @@ -928,7 +930,7 @@ public class Helper { }catch (Exception ignored){} } if (!fragmentManager.isDestroyed()) { - ft.commit(); + ft.commitAllowingStateLoss(); } } fragmentManager.executePendingTransactions(); @@ -1461,6 +1463,24 @@ public class Helper { imageView.setColorFilter(color); } + /** + * change color of a drawable + * + * @param materialButton {@link MaterialButton} + * @param hexaColor example 0xffff00 + */ + public static void changeDrawableColor(Context context, MaterialButton materialButton, int hexaColor) { + if (materialButton == null) + return; + int color; + try { + color = context.getResources().getColor(hexaColor); + } catch (Resources.NotFoundException e) { + color = hexaColor; + } + materialButton.setIconTint(ColorStateList.valueOf(color)); + } + /** * change color of a drawable * @@ -1828,7 +1848,6 @@ public class Helper { if (responseCode == HttpURLConnection.HTTP_OK) { String fileName = ""; String disposition = httpURLConnection.getHeaderField("Content-Disposition"); - if (disposition != null) { // extracts file name from header field int index = disposition.indexOf("filename="); @@ -1838,7 +1857,12 @@ public class Helper { } } else { // extracts file name from URL - fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/") + 1); + try { + URL downLoadUrlTmp = new URL(downloadUrl); + fileName = downLoadUrlTmp.getPath().replace("/","_"); + }catch (Exception exception) { + fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/") + 1); + } } fileName = FileNameCleaner.cleanFileName(fileName); // opens input stream from the HTTP connection @@ -2139,6 +2163,22 @@ public class Helper { .build(); } + public static OkHttpClient myPostOkHttpClient(Context context) { + return new OkHttpClient.Builder() + .addInterceptor(chain -> { + Request originalRequest = chain.request(); + Request requestWithUserAgent = originalRequest.newBuilder() + .header("User-Agent", context.getString(R.string.app_name) + "/" + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_CODE) + .build(); + return chain.proceed(requestWithUserAgent); + }) + .readTimeout(120, TimeUnit.SECONDS) + .connectTimeout(120, TimeUnit.SECONDS) + .callTimeout(120, TimeUnit.SECONDS) + .proxy(Helper.getProxy(context)) + .build(); + } + public static String parseHtml(String html) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY).toString(); diff --git a/app/src/main/java/app/fedilab/android/mastodon/helper/MediaHelper.java b/app/src/main/java/app/fedilab/android/mastodon/helper/MediaHelper.java index 6e90fac39b04c2fab7685ef2e86eef2c49cf141d..ca01bb2802ad1ada0a1edf79cbd924dfcf5321f3 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/helper/MediaHelper.java +++ b/app/src/main/java/app/fedilab/android/mastodon/helper/MediaHelper.java @@ -25,6 +25,7 @@ import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.media.MediaRecorder; import android.media.MediaScannerConnection; @@ -266,6 +267,32 @@ public class MediaHelper { ); } + + public static Drawable rescaleImageIfNeeded(Activity activity, Drawable image) { + if (!(image instanceof BitmapDrawable)) { + return image; + } + int maxSize = 2000; + int width = image.getIntrinsicWidth(); + int height = image.getIntrinsicHeight(); + float scaleFactor; + if(width > maxSize || height > maxSize) { + if(width >= height) { + scaleFactor = (float) maxSize / width; + } else { + scaleFactor = (float) maxSize / height; + } + } else { + return image; + } + Bitmap b = ((BitmapDrawable)image).getBitmap(); + int sizeX = Math.round(image.getIntrinsicWidth() * scaleFactor); + int sizeY = Math.round(image.getIntrinsicHeight() * scaleFactor); + Bitmap bitmapResized = Bitmap.createScaledBitmap(b, sizeX, sizeY, false); + image = new BitmapDrawable(activity.getResources(), bitmapResized); + return image; + } + /** * Record media * diff --git a/app/src/main/java/app/fedilab/android/mastodon/jobs/ComposeWorker.java b/app/src/main/java/app/fedilab/android/mastodon/jobs/ComposeWorker.java index 42e1d8e0d70692677f16980e16a58cf415af2695..ebcb2416f860c0ba889e4fa2fa48e2b69579afe1 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/jobs/ComposeWorker.java +++ b/app/src/main/java/app/fedilab/android/mastodon/jobs/ComposeWorker.java @@ -47,9 +47,7 @@ import com.google.gson.Gson; import java.io.IOException; import java.net.IDN; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; -import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import app.fedilab.android.BaseMainActivity; @@ -72,7 +70,6 @@ import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.ui.drawer.StatusAdapter; import okhttp3.MediaType; import okhttp3.MultipartBody; -import okhttp3.OkHttpClient; import okhttp3.RequestBody; import retrofit2.Call; import retrofit2.Response; @@ -94,19 +91,11 @@ public class ComposeWorker extends Worker { context.getSystemService(NOTIFICATION_SERVICE); } - private static OkHttpClient getOkHttpClient(Context context) { - return new OkHttpClient.Builder() - .readTimeout(120, TimeUnit.SECONDS) - .connectTimeout(120, TimeUnit.SECONDS) - .proxy(Helper.getProxy(context.getApplicationContext())) - .build(); - } - private static MastodonStatusesService init(Context context, String instance) { Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://" + (instance != null ? IDN.toASCII(instance, IDN.ALLOW_UNASSIGNED) : null) + "/api/v1/") .addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder())) - .client(getOkHttpClient(context)) + .client(Helper.myPostOkHttpClient(context)) .build(); return retrofit.create(MastodonStatusesService.class); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java index 93f301ecd11a0c6ac0bc0a479b130be6219c8bb8..53b019467bcc0fd230e7f6c0b5efb74339ed8be0 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java @@ -315,7 +315,7 @@ public class ComposeAdapter extends RecyclerView.Adapter 0) { + if (statusDraft.mentions != null && (statusDraft.text == null || statusDraft.text.isEmpty()) && !statusDraft.mentions.isEmpty()) { //Retrieves mentioned accounts + OP and adds them at the beginin of the toot final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); Mention inReplyToUser; @@ -426,25 +426,25 @@ public class ComposeAdapter extends RecyclerView.Adapter 0) { + if (instanceInfo != null && instanceInfo.getMimeTypeImage() != null && !instanceInfo.getMimeTypeImage().isEmpty()) { mimetypes = instanceInfo.getMimeTypeImage().toArray(new String[0]); } else { mimetypes = new String[]{"image/*"}; } } else if (type == ComposeActivity.mediaType.VIDEO) { - if (instanceInfo != null && instanceInfo.getMimeTypeVideo() != null && instanceInfo.getMimeTypeVideo().size() > 0) { + if (instanceInfo != null && instanceInfo.getMimeTypeVideo() != null && !instanceInfo.getMimeTypeVideo().isEmpty()) { mimetypes = instanceInfo.getMimeTypeVideo().toArray(new String[0]); } else { mimetypes = new String[]{"video/*"}; } } else if (type == ComposeActivity.mediaType.AUDIO) { - if (instanceInfo != null && instanceInfo.getMimeTypeAudio() != null && instanceInfo.getMimeTypeAudio().size() > 0) { + if (instanceInfo != null && instanceInfo.getMimeTypeAudio() != null && !instanceInfo.getMimeTypeAudio().isEmpty()) { mimetypes = instanceInfo.getMimeTypeAudio().toArray(new String[0]); } else { mimetypes = new String[]{"audio/*"}; } } else if (type == ComposeActivity.mediaType.ALL) { - if (instanceInfo != null && instanceInfo.getMimeTypeOther() != null && instanceInfo.getMimeTypeOther().size() > 0) { + if (instanceInfo != null && instanceInfo.getMimeTypeOther() != null && !instanceInfo.getMimeTypeOther().isEmpty()) { mimetypes = instanceInfo.getMimeTypeOther().toArray(new String[0]); } else { mimetypes = new String[]{"*/*"}; @@ -509,9 +509,9 @@ public class ComposeAdapter extends RecyclerView.Adapter splitText = ComposeHelper.splitToots(contentString, max_car); - contentString = splitText.get(0); + if(!splitText.isEmpty()) { + contentString = splitText.get(0); + } } int currentLength = MastodonHelper.countLength(holder); if (promptDraftListener != null) { @@ -715,7 +717,7 @@ public class ComposeAdapter extends RecyclerView.Adapter 0) { + if (!mentions.isEmpty()) { for (String mention : mentions) { newContent[0] += mention + " "; } @@ -732,7 +734,7 @@ public class ComposeAdapter extends RecyclerView.Adapter quotes = gson.fromJson(json, new TypeToken>() { }.getType()); - if (quotes != null && quotes.size() > 0) { + if (quotes != null && !quotes.isEmpty()) { final int random = new Random().nextInt(quotes.size()); Quotes.Quote quote = quotes.get(random); newContent[0] += quote.content + "\n- " + quote.author; @@ -760,7 +762,7 @@ public class ComposeAdapter extends RecyclerView.Adapter= oldContent.length()) deltaSearch = oldContent.substring(currentCursorPosition - searchLength); } - if (!search.equals("")) + if (!search.isEmpty()) deltaSearch = deltaSearch.replace("@" + search, ""); String newContent = oldContent.substring(0, currentCursorPosition - searchLength); newContent += deltaSearch; @@ -884,10 +886,10 @@ public class ComposeAdapter extends RecyclerView.Adapter { - if (results == null || results.hashtags == null || results.hashtags.size() == 0) { + if (results == null || results.hashtags == null || results.hashtags.isEmpty()) { return; } - if (camelTags != null && camelTags.size() > 0) { + if (camelTags != null && !camelTags.isEmpty()) { for (String camelTag : camelTags) { Tag tag = new Tag(); tag.name = camelTag; @@ -929,7 +931,7 @@ public class ComposeAdapter extends RecyclerView.Adapter { List emojisToDisplay = new ArrayList<>(); try { - if (emojisList == null || emojisList.size() == 0) { + if (emojisList == null || emojisList.isEmpty()) { emojisList = new EmojiInstance(context).getEmojiList(BaseMainActivity.currentInstance); } if (emojis == null) { @@ -995,7 +997,7 @@ public class ComposeAdapter extends RecyclerView.Adapter 0) { + if (title != null && !title.trim().isEmpty()) { contentBuilder.append(title); - } else if (subject != null && subject.trim().length() > 0) { + } else if (subject != null && !subject.trim().isEmpty()) { contentBuilder.append(subject); } @@ -1057,12 +1059,12 @@ public class ComposeAdapter extends RecyclerView.Adapter 0) { + if (description != null && !description.trim().isEmpty()) { if (url != null && !description.contains(url)) { contentBuilder.append(url).append("\n\n"); } contentBuilder.append("> ").append(description); - } else if (content != null && content.trim().length() > 0) { + } else if (content != null && !content.trim().isEmpty()) { if (!content.contains(url)) { contentBuilder.append(url).append("\n\n"); } @@ -1309,7 +1311,7 @@ public class ComposeAdapter extends RecyclerView.Adapter 0) { + if (status != null && status.media_attachments != null && !status.media_attachments.isEmpty()) { int mediaPosition = 0; for (Attachment attachment : status.media_attachments) { if (attachment.description == null || attachment.description.trim().isEmpty()) { @@ -1353,7 +1355,7 @@ public class ComposeAdapter extends RecyclerView.Adapter 0) { + if (status.media_attachments != null && !status.media_attachments.isEmpty()) { holder.binding.simpleMedia.removeAllViews(); List attachmentList = statusList.get(position).media_attachments; for (Attachment attachment : attachmentList) { @@ -1447,7 +1449,7 @@ public class ComposeAdapter extends RecyclerView.Adapter 0) { + if (accountFromUser.fields != null && !accountFromUser.fields.isEmpty()) { for (Field field : accountFromUser.fields) { if (PronounsHelper.pronouns.contains(field.name.toLowerCase().trim())) { statusList.get(position).pronouns = Helper.parseHtml(field.value); @@ -1570,16 +1572,16 @@ public class ComposeAdapter extends RecyclerView.Adapter { if (instanceInfo.configuration.media_attachments.supported_mime_types != null) { - if (instanceInfo.getMimeTypeAudio().size() == 0) { + if (instanceInfo.getMimeTypeAudio().isEmpty()) { holder.binding.buttonAttachAudio.setEnabled(false); } - if (instanceInfo.getMimeTypeImage().size() == 0) { + if (instanceInfo.getMimeTypeImage().isEmpty()) { holder.binding.buttonAttachImage.setEnabled(false); } - if (instanceInfo.getMimeTypeVideo().size() == 0) { + if (instanceInfo.getMimeTypeVideo().isEmpty()) { holder.binding.buttonAttachVideo.setEnabled(false); } - if (instanceInfo.getMimeTypeOther().size() == 0) { + if (instanceInfo.getMimeTypeOther().isEmpty()) { holder.binding.buttonAttachManual.setEnabled(false); } } @@ -1687,7 +1689,7 @@ public class ComposeAdapter extends RecyclerView.Adapter 0) { + if (statusDraft.spoilerChecked || statusDraft.spoiler_text != null && !statusDraft.spoiler_text.trim().isEmpty()) { holder.binding.contentSpoiler.setVisibility(View.VISIBLE); } else { holder.binding.contentSpoiler.setVisibility(View.GONE); @@ -1697,15 +1699,17 @@ public class ComposeAdapter extends RecyclerView.Adapter 0) { + if (emojis != null && !emojis.isEmpty()) { holder.binding.buttonEmoji.setVisibility(View.VISIBLE); } else { holder.binding.buttonEmoji.setVisibility(View.GONE); @@ -1781,7 +1785,7 @@ public class ComposeAdapter extends RecyclerView.Adapter 0) { + if (!camelCaseTags.isEmpty()) { statusDraft.text += "\n\n"; int lenght = 0; for (String tag : camelCaseTags) { @@ -1815,7 +1819,7 @@ public class ComposeAdapter extends RecyclerView.Adapter 0) { + if (!camelCaseTags.isEmpty()) { statusList.get(position).tagAdded = true; int lenght = 0; for (String tag : camelCaseTags) { @@ -1898,7 +1902,7 @@ public class ComposeAdapter extends RecyclerView.Adapter 0) { + if (storedLanguages != null && !storedLanguages.isEmpty()) { int i = 0; codesArr = new String[storedLanguages.size()]; languagesArr = new String[storedLanguages.size()]; diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java index 694e600edfbc4a381550b8ef6f62453cc90a40a7..eab59d9bb7e246f5f3c8e2d9143f8fe0605a8d34 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java @@ -59,7 +59,6 @@ import android.os.Looper; import android.text.Html; import android.text.SpannableString; import android.text.TextUtils; -import android.util.Log; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -1279,27 +1278,23 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.actionButtonReply.getLayoutParams().height = (int) (normalSize * scaleIcon); holder.binding.actionButtonReply.requestLayout(); - holder.binding.actionButtonTranslate.getLayoutParams().width = (int) (normalSize * scaleIcon); - holder.binding.actionButtonTranslate.getLayoutParams().height = (int) (normalSize * scaleIcon); + holder.binding.actionButtonTranslate.setIconSize((int) (normalSize * scaleIcon)); holder.binding.actionButtonTranslate.requestLayout(); holder.binding.actionButtonBoost.setImageSize((int) (normalSize * scaleIcon)); holder.binding.actionButtonFavorite.setImageSize((int) (normalSize * scaleIcon)); holder.binding.actionButtonBookmark.setImageSize((int) (normalSize * scaleIcon)); - - holder.binding.statusAddCustomEmoji.getLayoutParams().width = (int) (normalSize * scaleIcon); - holder.binding.statusAddCustomEmoji.getLayoutParams().height = (int) (normalSize * scaleIcon); + holder.binding.statusAddCustomEmoji.setIconSize((int) (normalSize * scaleIcon)); holder.binding.statusAddCustomEmoji.requestLayout(); - holder.binding.actionButtonQuote.getLayoutParams().width = (int) (normalSize * scaleIcon); - holder.binding.actionButtonQuote.getLayoutParams().height = (int) (normalSize * scaleIcon); + holder.binding.actionButtonQuote.setIconSize((int) (normalSize * scaleIcon)); holder.binding.actionButtonQuote.requestLayout(); - holder.binding.statusEmoji.getLayoutParams().width = (int) (normalSize * scaleIcon); - holder.binding.statusEmoji.getLayoutParams().height = (int) (normalSize * scaleIcon); - holder.binding.actionButtonMore.getLayoutParams().width = (int) (normalSize * scaleIcon); - holder.binding.actionButtonMore.getLayoutParams().height = (int) (normalSize * scaleIcon); + holder.binding.statusEmoji.setIconSize((int) (normalSize * scaleIcon)); + holder.binding.statusEmoji.requestLayout(); + + holder.binding.actionButtonMore.setIconSize((int) (normalSize * scaleIcon)); holder.binding.actionButtonMore.requestLayout(); holder.binding.actionShare.getLayoutParams().width = (int) (normalSize * scaleIcon); holder.binding.actionShare.getLayoutParams().height = (int) (normalSize * scaleIcon); @@ -1620,7 +1615,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.displayMedia.setVisibility(View.GONE); holder.binding.media.mediaContainer.setVisibility(View.VISIBLE); int mediaPosition = 1; - boolean autoplaygif = sharedpreferences.getBoolean(context.getString(R.string.SET_AUTO_PLAY_GIG_MEDIA), true); + boolean autoplaygif = sharedpreferences.getBoolean(context.getString(R.string.SET_AUTO_PLAY_GIG_MEDIA), false); if (!fullAttachement || statusToDeal.sensitive) { int defaultHeight = (int) Helper.convertDpToPixel(300, context); int orientation = context.getResources().getConfiguration().orientation; @@ -2112,7 +2107,11 @@ public class StatusAdapter extends RecyclerView.Adapter adapter.notifyItemChanged(holder.getBindingAdapterPosition()); } })); - holder.binding.poll.pollContainer.setVisibility(View.VISIBLE); + if (statusToDeal.spoiler_text == null || statusToDeal.spoiler_text.trim().isEmpty() || statusToDeal.isExpended) { + holder.binding.poll.pollContainer.setVisibility(View.VISIBLE); + } else { + holder.binding.poll.pollContainer.setVisibility(View.GONE); + } String pollInfo = context.getResources().getQuantityString(R.plurals.number_of_voters, normalize, normalize); if (statusToDeal.poll.expired) { pollInfo += " - " + context.getString(R.string.poll_finish_at, MastodonHelper.dateToStringPoll(statusToDeal.poll.expires_at)); @@ -2224,11 +2223,11 @@ public class StatusAdapter extends RecyclerView.Adapter // Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> holder.binding.statusContent.invalidate(), 0, 100, TimeUnit.MILLISECONDS); if (remote) { - holder.binding.actionButtonMoreContainer.setVisibility(View.GONE); + holder.binding.actionButtonMore.setVisibility(View.GONE); } else { - holder.binding.actionButtonMoreContainer.setVisibility(View.VISIBLE); + holder.binding.actionButtonMore.setVisibility(View.VISIBLE); } - holder.binding.actionButtonMoreContainer.setOnClickListener(v -> { + holder.binding.actionButtonMore.setOnClickListener(v -> { boolean isOwner = statusToDeal.account.id.compareTo(BaseMainActivity.currentUserID) == 0; PopupMenu popup = new PopupMenu(context, holder.binding.actionButtonMore); popup.getMenuInflater() diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusDirectMessageAdapter.java b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusDirectMessageAdapter.java index eab60c443358f98f4f335c1e023fd75ccceddfce..9485ee7258c61280f402ced0289b6e657c0e451b 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusDirectMessageAdapter.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusDirectMessageAdapter.java @@ -605,7 +605,7 @@ public class StatusDirectMessageAdapter extends RecyclerView.Adapter. */ + import android.content.SharedPreferences; import android.graphics.Color; import android.graphics.drawable.Drawable; @@ -58,6 +59,7 @@ import app.fedilab.android.mastodon.activities.MediaActivity; import app.fedilab.android.mastodon.client.entities.api.Attachment; import app.fedilab.android.mastodon.helper.CacheDataSourceFactory; import app.fedilab.android.mastodon.helper.Helper; +import app.fedilab.android.mastodon.helper.MediaHelper; import app.fedilab.android.mastodon.viewmodel.mastodon.TimelinesVM; import es.dmoral.toasty.Toasty; @@ -167,7 +169,9 @@ public class FragmentMedia extends Fragment { return; } binding.mediaPicture.setZoomable(true); - binding.mediaPicture.setImageDrawable(resource); + + Drawable scaledRessource = MediaHelper.rescaleImageIfNeeded(requireActivity(), resource); + binding.mediaPicture.setImageDrawable(scaledRessource); if (attachment.type.equalsIgnoreCase("image") && !attachment.url.toLowerCase().endsWith(".gif")) { binding.mediaPicture.setVisibility(View.VISIBLE); @@ -185,7 +189,8 @@ public class FragmentMedia extends Fragment { return; } binding.loader.setVisibility(View.GONE); - binding.mediaPicture.setImageDrawable(resource); + Drawable scaledRessource = MediaHelper.rescaleImageIfNeeded(requireActivity(), resource); + binding.mediaPicture.setImageDrawable(scaledRessource); binding.mediaPicture.setZoomable(true); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/settings/FragmentComposeSettings.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/settings/FragmentComposeSettings.java index 2b27992e41e6562a71618a391b01df25e5e6a3cf..232d731c699837e0c544057bd0729266831616a4 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/settings/FragmentComposeSettings.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/settings/FragmentComposeSettings.java @@ -23,6 +23,7 @@ import androidx.preference.ListPreference; import androidx.preference.MultiSelectListPreference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceManager; +import androidx.preference.SwitchPreferenceCompat; import java.util.List; import java.util.Objects; @@ -59,6 +60,12 @@ public class FragmentComposeSettings extends PreferenceFragmentCompat implements SET_WATERMARK_TEXT.setText(val); } + SwitchPreferenceCompat SET_MENTION_BOOSTER = findPreference(getString(R.string.SET_MENTION_BOOSTER)); + if (SET_MENTION_BOOSTER != null) { + boolean val = sharedPreferences.getBoolean(getString(R.string.SET_MENTION_BOOSTER) + BaseMainActivity.currentUserID + BaseMainActivity.currentInstance, sharedPreferences.getBoolean(getString(R.string.SET_MENTION_BOOSTER), false)); + SET_MENTION_BOOSTER.setChecked(val); + } + MultiSelectListPreference SET_SELECTED_LANGUAGE = findPreference(getString(R.string.SET_SELECTED_LANGUAGE)); if (SET_SELECTED_LANGUAGE != null) { @@ -66,7 +73,7 @@ public class FragmentComposeSettings extends PreferenceFragmentCompat implements Set storedLanguages = sharedPreferences.getStringSet(getString(R.string.SET_SELECTED_LANGUAGE), null); String[] selectedValue = new String[0]; - if (storedLanguages != null && storedLanguages.size() > 0) { + if (storedLanguages != null && !storedLanguages.isEmpty()) { if (storedLanguages.size() == 1 && storedLanguages.toArray()[0] == null) { sharedPreferences.edit().remove(getString(R.string.SET_SELECTED_LANGUAGE)).commit(); } else { @@ -102,6 +109,11 @@ public class FragmentComposeSettings extends PreferenceFragmentCompat implements editor.putString(getString(R.string.SET_WATERMARK_TEXT) + BaseMainActivity.currentUserID + BaseMainActivity.currentInstance, sharedPreferences.getString(getString(R.string.SET_WATERMARK_TEXT), null)); editor.apply(); } + if (Objects.requireNonNull(key).equalsIgnoreCase(getString(R.string.SET_MENTION_BOOSTER))) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean(getString(R.string.SET_MENTION_BOOSTER) + BaseMainActivity.currentUserID + BaseMainActivity.currentInstance, sharedPreferences.getBoolean(getString(R.string.SET_MENTION_BOOSTER), false)); + editor.apply(); + } } @Override diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/settings/FragmentNetworkSettings.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/settings/FragmentNetworkSettings.java new file mode 100644 index 0000000000000000000000000000000000000000..0c3bcb844ecce838805ed15a98241e26e7f1bed8 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/settings/FragmentNetworkSettings.java @@ -0,0 +1,60 @@ +package app.fedilab.android.mastodon.ui.fragment.settings; +/* Copyright 2022 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see . */ + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.widget.Toast; + +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; + +import java.io.IOException; + +import app.fedilab.android.R; +import app.fedilab.android.mastodon.activities.ProxyActivity; +import app.fedilab.android.mastodon.helper.Helper; +import app.fedilab.android.mastodon.helper.ZipHelper; +import es.dmoral.toasty.Toasty; + +public class FragmentNetworkSettings extends PreferenceFragmentCompat { + + @Override + public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) { + addPreferencesFromResource(R.xml.pref_network); + + + Preference pref_proxy = findPreference(getString(R.string.pref_key_proxy)); + if (pref_proxy != null) { + pref_proxy.setOnPreferenceClickListener(preference -> { + (new ProxyActivity()).show(getParentFragmentManager(), null); + return false; + }); + } + + } + +} diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/settings/FragmentSettingsCategories.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/settings/FragmentSettingsCategories.java index b628431b4b32e9dcb6c561eac239927d3e2fb182..b1d1d6566f5be7ddf3dd3639c6725c2474711127 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/settings/FragmentSettingsCategories.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/settings/FragmentSettingsCategories.java @@ -138,6 +138,15 @@ public class FragmentSettingsCategories extends PreferenceFragmentCompat { }); } + Preference pref_category_key_network = findPreference(getString(R.string.pref_category_key_network)); + if (pref_category_key_network != null) { + pref_category_key_network.setOnPreferenceClickListener(preference -> { + NavController navController = Navigation.findNavController(requireActivity(), R.id.fragment_container); + navController.navigate(FragmentSettingsCategoriesDirections.Companion.categoriesToNetwork()); + return false; + }); + } + Preference pref_category_key_extra_features = findPreference(getString(R.string.pref_category_key_extra_features)); if (pref_category_key_extra_features != null) { pref_category_key_extra_features.setOnPreferenceClickListener(preference -> { diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java index 8f15fa9b2978732ce6923e49b55c2879a43327e7..d2127dd4c1d82b1a0298cb7fe19c260f603d2a8f 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java @@ -17,6 +17,7 @@ package app.fedilab.android.mastodon.ui.fragment.timeline; import static app.fedilab.android.BaseMainActivity.currentInstance; import static app.fedilab.android.BaseMainActivity.networkAvailable; +import static app.fedilab.android.mastodon.helper.Helper.TAG; import android.content.BroadcastReceiver; import android.content.Context; @@ -26,6 +27,7 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -571,9 +573,13 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. insertedStatus = updateStatusListWith(fetched_statuses.statuses); } else { //Trends cannot be ordered by id insertedStatus = fetched_statuses.statuses.size(); - int fromPosition = timelineStatuses.size(); - timelineStatuses.addAll(fetched_statuses.statuses); - statusAdapter.notifyItemRangeInserted(fromPosition, insertedStatus); + for(Status statusReceived: fetched_statuses.statuses) { + if (!timelineStatuses.contains(statusReceived)) { + timelineStatuses.add(statusReceived); + statusAdapter.notifyItemInserted(timelineStatuses.size() - 1); + insertedStatus++; + } + } } //For these directions, the app will display counters for new messages if (insertedStatus >= 0 && update != null && direction != DIRECTION.FETCH_NEW && !fetchingMissing) { @@ -631,7 +637,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. binding.loader.setVisibility(View.GONE); binding.noAction.setVisibility(View.GONE); binding.swipeContainer.setRefreshing(false); - if (searchCache == null && timelineType != Timeline.TimeLineEnum.TREND_MESSAGE) { + if (searchCache == null ) { binding.swipeContainer.setOnRefreshListener(() -> { binding.swipeContainer.setRefreshing(true); flagLoading = false; @@ -722,7 +728,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. binding.recyclerView.addOnScrollListener(preloader); binding.recyclerView.setItemViewCacheSize(0); - if (timelineType != Timeline.TimeLineEnum.TREND_MESSAGE) { + binding.recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { @@ -759,7 +765,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. if (slug != null /*&& slug.compareTo(Helper.getSlugOfFirstFragment(requireActivity(), currentUserID, currentInstance)) == 0*/ && rememberPosition) { route(DIRECTION.FETCH_NEW, true); } - } + } @@ -1132,7 +1138,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. } }); } - } else if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.PIXELFED) { + } + else if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.PIXELFED) { if (direction == null) { timelinesVM.getPixelfedDiscoverTrending(remoteInstance) .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); @@ -1197,9 +1204,9 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. accountId[0] = accountTimeline.id; } displayStatuses(direction, accountId[0], tempInstance[0], tempToken[0], fetchStatus); - } else if (search != null) { + } + else if (search != null) { SearchVM searchVM = new ViewModelProvider(FragmentMastodonTimeline.this).get(viewModelKey, SearchVM.class); - if (direction == null) { searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, search.trim(), null, null, false, true, false, 0, null, null, MastodonHelper.SEARCH_PER_CALL) .observe(getViewLifecycleOwner(), results -> { @@ -1225,7 +1232,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. } else { flagLoading = false; } - } else if (searchCache != null) { + } + else if (searchCache != null) { SearchVM searchVM = new ViewModelProvider(FragmentMastodonTimeline.this).get(viewModelKey, SearchVM.class); searchVM.searchCache(BaseMainActivity.currentInstance, BaseMainActivity.currentUserID, searchCache.trim()) .observe(getViewLifecycleOwner(), results -> { @@ -1238,7 +1246,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. Toasty.error(requireActivity(), getString(R.string.toast_error), Toasty.LENGTH_LONG).show(); } }); - } else if (timelineType == Timeline.TimeLineEnum.FAVOURITE_TIMELINE) { + } + else if (timelineType == Timeline.TimeLineEnum.FAVOURITE_TIMELINE) { if (direction == null) { accountsVM.getFavourites(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, String.valueOf(MastodonHelper.statusesPerCall(requireActivity())), null, null) .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); @@ -1248,7 +1257,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. } else { flagLoading = false; } - } else if (timelineType == Timeline.TimeLineEnum.BOOKMARK_TIMELINE) { + } + else if (timelineType == Timeline.TimeLineEnum.BOOKMARK_TIMELINE) { if (direction == null) { accountsVM.getBookmarks(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, String.valueOf(MastodonHelper.statusesPerCall(requireActivity())), null, null, null) .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); @@ -1258,17 +1268,28 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. } else { flagLoading = false; } - } else if (timelineType == Timeline.TimeLineEnum.TREND_MESSAGE) { + } + else if (timelineType == Timeline.TimeLineEnum.TREND_MESSAGE) { if (direction == null) { timelinesVM.getStatusTrends(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, null, MastodonHelper.statusesPerCall(requireActivity())) .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); } else if (direction == DIRECTION.BOTTOM) { timelinesVM.getStatusTrends(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, max_id, MastodonHelper.statusesPerCall(requireActivity())) .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true, fetchStatus)); - } else { + }else if (direction == DIRECTION.TOP) { flagLoading = false; + } else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP || direction == DIRECTION.FETCH_NEW) { + timelinesVM.getStatusTrends(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, null, MastodonHelper.statusesPerCall(requireActivity())) + .observe(getViewLifecycleOwner(), statusesRefresh -> { + if (statusAdapter != null) { + dealWithPagination(statusesRefresh, direction, true, true, fetchStatus); + } else { + initializeStatusesCommonView(statusesRefresh); + } + }); } - } else if (timelineType == Timeline.TimeLineEnum.TREND_MESSAGE_PUBLIC) { + } + else if (timelineType == Timeline.TimeLineEnum.TREND_MESSAGE_PUBLIC) { if (direction == null) { timelinesVM.getStatusTrends(null, publicTrendsDomain, null, MastodonHelper.statusesPerCall(requireActivity())) .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeMainActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeMainActivity.java index 3e0fdf634aa7e3e00c8d83d120f0af88df80cd26..70eb61dc0b2767cfc1aeb21c2aeacbc5f9099a10 100644 --- a/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeMainActivity.java +++ b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeMainActivity.java @@ -20,7 +20,7 @@ import static app.fedilab.android.BaseMainActivity.currentToken; import static app.fedilab.android.BaseMainActivity.currentUserID; import static app.fedilab.android.BaseMainActivity.fetchRecentAccounts; import static app.fedilab.android.BaseMainActivity.headerMenuOpen; -import static app.fedilab.android.BaseMainActivity.headerOptionInfoClick; +import static app.fedilab.android.BaseMainActivity.headerLogoutClick; import static app.fedilab.android.BaseMainActivity.mamageNewIntent; import static app.fedilab.android.BaseMainActivity.manageDrawerMenu; import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_ID; @@ -328,8 +328,8 @@ public class PeertubeMainActivity extends PeertubeBaseMainActivity { headerMainBinding.accountAcc.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale); app.fedilab.android.mastodon.helper.Helper.loadPP(PeertubeMainActivity.this, headerMainBinding.accountProfilePicture, app.fedilab.android.mastodon.helper.Helper.getCurrentAccount(PeertubeMainActivity.this), false); headerMainBinding.backgroundImage.setAlpha(0.5f); - headerMainBinding.accountAcc.setOnClickListener(v -> headerMainBinding.changeAccount.callOnClick()); - headerMainBinding.changeAccount.setOnClickListener(v -> { + TooltipCompat.setTooltipText(headerMainBinding.ownerAccounts, getString(R.string.manage_accounts)); + headerMainBinding.ownerAccounts.setOnClickListener(v -> { headerMenuOpen = !headerMenuOpen; manageDrawerMenu(PeertubeMainActivity.this, binding.drawerNavView, headerMainBinding); @@ -345,9 +345,7 @@ public class PeertubeMainActivity extends PeertubeBaseMainActivity { headerMainBinding.accountAcc.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale); app.fedilab.android.mastodon.helper.Helper.loadPP(PeertubeMainActivity.this, headerMainBinding.accountProfilePicture, app.fedilab.android.mastodon.helper.Helper.getCurrentAccount(PeertubeMainActivity.this), false); headerMainBinding.backgroundImage.setAlpha(0.5f); - headerMainBinding.accountAcc.setOnClickListener(v -> headerMainBinding.changeAccount.callOnClick()); - headerMainBinding.changeAccount.setOnClickListener(v -> { - + headerMainBinding.ownerAccounts.setOnClickListener(v -> { headerMenuOpen = !headerMenuOpen; manageDrawerMenu(PeertubeMainActivity.this, binding.drawerNavView, headerMainBinding); }); @@ -358,7 +356,8 @@ public class PeertubeMainActivity extends PeertubeBaseMainActivity { }; mainHandler.post(myRunnable); }).start(); - headerMainBinding.instanceInfo.setVisibility(View.GONE); + View navInstanceInfo = binding.drawerNavView.findViewById(R.id.nav_instance_info); + binding.drawerNavView.removeView(navInstanceInfo); binding.drawerNavView.addHeaderView(headerMainBinding.getRoot()); binding.drawerNavView.setNavigationItemSelectedListener(item -> { if (item.getItemId() == R.id.action_settings) { @@ -414,7 +413,8 @@ public class PeertubeMainActivity extends PeertubeBaseMainActivity { binding.drawerLayout.close(); return false; }); - headerMainBinding.headerOptionInfo.setOnClickListener(v -> headerOptionInfoClick(PeertubeMainActivity.this, headerMainBinding, getSupportFragmentManager())); + TooltipCompat.setTooltipText(headerMainBinding.headerLogout, getString(R.string.action_logout)); + headerMainBinding.headerLogout.setOnClickListener(v -> headerLogoutClick(PeertubeMainActivity.this, headerMainBinding, getSupportFragmentManager())); fetchRecentAccounts(PeertubeMainActivity.this, headerMainBinding); } else { new Thread(() -> { @@ -512,9 +512,9 @@ public class PeertubeMainActivity extends PeertubeBaseMainActivity { } private void startInForeground() { - Intent retrieveInfoServiceIntent = new Intent(this, RetrieveInfoService.class); + Intent notificationIntent = new Intent(this, RetrieveInfoService.class); try { - startService(retrieveInfoServiceIntent); + startService(notificationIntent); } catch (Exception ignored) { } } diff --git a/app/src/main/java/app/fedilab/android/peertube/services/RetrieveInfoService.java b/app/src/main/java/app/fedilab/android/peertube/services/RetrieveInfoService.java index a49837624654dc6025aa1a55f1407b54c6ded899..d4274e0c838435baeef7109991d60c5550e3e79f 100644 --- a/app/src/main/java/app/fedilab/android/peertube/services/RetrieveInfoService.java +++ b/app/src/main/java/app/fedilab/android/peertube/services/RetrieveInfoService.java @@ -16,19 +16,28 @@ package app.fedilab.android.peertube.services; import static app.fedilab.android.peertube.helper.Helper.peertubeInformation; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; import android.app.Service; +import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ServiceInfo; import android.net.ConnectivityManager; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; import androidx.core.content.ContextCompat; import java.util.LinkedHashMap; +import java.util.Objects; +import app.fedilab.android.R; import app.fedilab.android.peertube.client.RetrofitPeertubeAPI; import app.fedilab.android.peertube.client.entities.PeertubeInformation; import app.fedilab.android.peertube.helper.EmojiHelper; @@ -37,6 +46,7 @@ import app.fedilab.android.peertube.helper.NetworkStateReceiver; public class RetrieveInfoService extends Service implements NetworkStateReceiver.NetworkStateReceiverListener { + static String NOTIFICATION_CHANNEL_ID = "update_info_peertube"; private NetworkStateReceiver networkStateReceiver; @@ -45,6 +55,36 @@ public class RetrieveInfoService extends Service implements NetworkStateReceiver networkStateReceiver = new NetworkStateReceiver(); networkStateReceiver.addListener(this); ContextCompat.registerReceiver(RetrieveInfoService.this, networkStateReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION), ContextCompat.RECEIVER_NOT_EXPORTED); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, + getString(R.string.notification_channel_name), + NotificationManager.IMPORTANCE_DEFAULT); + channel.setSound(null, null); + + ((NotificationManager) Objects.requireNonNull(getSystemService(Context.NOTIFICATION_SERVICE))).createNotificationChannel(channel); + Notification notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID) + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle(getString(R.string.app_name)) + .setContentText(getString(R.string.notification_channel_name)) + .setAutoCancel(true).build(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + startForeground(1, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC); + } else { + startForeground(1, notification); + } + + } else { + NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID) + .setContentTitle(getString(R.string.app_name)) + .setDefaults(Notification.DEFAULT_ALL) + .setContentText(getString(R.string.notification_channel_name)) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setAutoCancel(true); + + Notification notification = builder.build(); + startForeground(1, notification); + } } @@ -100,7 +140,7 @@ public class RetrieveInfoService extends Service implements NetworkStateReceiver @Override public void run() { EmojiHelper.fillMapEmoji(getApplicationContext()); - if (peertubeInformation == null || peertubeInformation.getCategories() == null || peertubeInformation.getCategories().isEmpty()) { + if (peertubeInformation == null || peertubeInformation.getCategories() == null || peertubeInformation.getCategories().size() == 0) { peertubeInformation = new PeertubeInformation(); peertubeInformation.setCategories(new LinkedHashMap<>()); peertubeInformation.setLanguages(new LinkedHashMap<>()); @@ -110,6 +150,7 @@ public class RetrieveInfoService extends Service implements NetworkStateReceiver peertubeInformation.setTranslations(new LinkedHashMap<>()); peertubeInformation = new RetrofitPeertubeAPI(RetrieveInfoService.this).getPeertubeInformation(); } + stopForeground(true); } }; thread.start(); diff --git a/app/src/main/res/layouts/mastodon/drawable/ic_accounts.xml b/app/src/main/res/layouts/mastodon/drawable/ic_accounts.xml new file mode 100644 index 0000000000000000000000000000000000000000..c2d0f8f6ee1a062ad69a535a3b6961e0278e402a --- /dev/null +++ b/app/src/main/res/layouts/mastodon/drawable/ic_accounts.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layouts/mastodon/drawable/ic_logout.xml b/app/src/main/res/layouts/mastodon/drawable/ic_logout.xml new file mode 100644 index 0000000000000000000000000000000000000000..5dc221b49843cba0678c3c0ccff038a4ac16ff91 --- /dev/null +++ b/app/src/main/res/layouts/mastodon/drawable/ic_logout.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/layouts/mastodon/drawable/ic_network_prefs.xml b/app/src/main/res/layouts/mastodon/drawable/ic_network_prefs.xml new file mode 100644 index 0000000000000000000000000000000000000000..c167dfb64abdc3b967381e5b09546f6ec9dc34c7 --- /dev/null +++ b/app/src/main/res/layouts/mastodon/drawable/ic_network_prefs.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layouts/mastodon/drawable/ic_proxy.xml b/app/src/main/res/layouts/mastodon/drawable/ic_proxy.xml new file mode 100644 index 0000000000000000000000000000000000000000..dc93ade865cdc1e905960a2e1d88e3c7ca2a81db --- /dev/null +++ b/app/src/main/res/layouts/mastodon/drawable/ic_proxy.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layouts/mastodon/layout/activity_media_pager.xml b/app/src/main/res/layouts/mastodon/layout/activity_media_pager.xml index 2b541a9d93e58ae9ee7f0d562eab84f22a736eba..2cf7620ed614aabdf50849b96cda59057177c17b 100644 --- a/app/src/main/res/layouts/mastodon/layout/activity_media_pager.xml +++ b/app/src/main/res/layouts/mastodon/layout/activity_media_pager.xml @@ -20,7 +20,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:animateLayoutChanges="true" - android:fitsSystemWindows="true" + android:fitsSystemWindows="false" android:background="@android:color/transparent"> @@ -35,6 +35,7 @@ + app:layout_constraintWidth_max="48dp"> - + - + app:layout_constraintWidth_max="48dp"> - + - + app:icon="@drawable/ic_baseline_format_quote_24" + app:iconGravity="textStart" + app:iconSize="28dp" + app:iconTint="?colorControlNormal" + app:layout_constraintWidth_max="48dp" /> - + app:layout_constraintWidth_max="48dp"> + + - + + - - - - - - - + android:contentDescription="@string/more_options" + app:icon="@drawable/ic_round_more_horiz_24" + app:iconGravity="textStart" + app:iconSize="28dp" + app:iconTint="?colorControlNormal" + app:layout_constraintWidth_max="48dp" /> - + diff --git a/app/src/main/res/layouts/mastodon/layout/drawer_status_compose.xml b/app/src/main/res/layouts/mastodon/layout/drawer_status_compose.xml index 506fb0a9928710796210ba7a0dd76053486662ac..7ccb3799ba5c509860f46e06696748c9448551d0 100644 --- a/app/src/main/res/layouts/mastodon/layout/drawer_status_compose.xml +++ b/app/src/main/res/layouts/mastodon/layout/drawer_status_compose.xml @@ -179,6 +179,7 @@ android:layout_height="wrap_content" android:layout_marginVertical="6dp" android:layout_marginStart="6dp" + android:contentDescription="@string/open_new_attachment_panel" app:icon="@drawable/ic_compose_attach" app:layout_constraintBottom_toBottomOf="@id/action_buttons_barrier" app:layout_constraintStart_toStartOf="parent" @@ -191,6 +192,7 @@ android:layout_height="wrap_content" android:layout_marginVertical="6dp" android:checkable="true" + android:contentDescription="@string/add_content_warning" app:icon="@drawable/ic_compose_sensitive" app:layout_constraintBottom_toBottomOf="@id/action_buttons_barrier" app:layout_constraintStart_toEndOf="@id/button_attach" @@ -203,6 +205,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginVertical="6dp" + android:contentDescription="@string/change_visibility" app:icon="@drawable/ic_compose_visibility_public" app:iconGravity="textStart" app:iconPadding="0dp" @@ -216,6 +219,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginVertical="6dp" + android:contentDescription="@string/set_language" android:fontFamily="monospace" android:minWidth="72dp" app:layout_constraintBottom_toBottomOf="@id/action_buttons_barrier" @@ -253,6 +257,7 @@ android:layout_height="wrap_content" android:layout_marginVertical="6dp" android:layout_marginEnd="6dp" + android:contentDescription="@string/action_publish" app:icon="@drawable/ic_compose_post" app:layout_constraintBottom_toBottomOf="@id/action_buttons_barrier" app:layout_constraintEnd_toEndOf="parent" @@ -276,6 +281,7 @@ style="@style/Widget.Material3.Button.IconButton" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:contentDescription="@string/attach_images" app:icon="@drawable/ic_compose_attach_image" /> diff --git a/app/src/main/res/layouts/mastodon/layout/fragment_notification_container.xml b/app/src/main/res/layouts/mastodon/layout/fragment_notification_container.xml index 742303a182c83396cc04f2aabc895f4a77e21710..cf25f709008cbcd1940c1f7a87a7f4b7afd7d91b 100644 --- a/app/src/main/res/layouts/mastodon/layout/fragment_notification_container.xml +++ b/app/src/main/res/layouts/mastodon/layout/fragment_notification_container.xml @@ -36,11 +36,11 @@ diff --git a/app/src/main/res/layouts/mastodon/layout/fragment_slide_media.xml b/app/src/main/res/layouts/mastodon/layout/fragment_slide_media.xml index e82be301dd37d726486ee85063d252044c0772bb..f59694b1040a6cde54eefec0ee330424d8bfbad3 100644 --- a/app/src/main/res/layouts/mastodon/layout/fragment_slide_media.xml +++ b/app/src/main/res/layouts/mastodon/layout/fragment_slide_media.xml @@ -9,8 +9,7 @@ + android:layout_height="match_parent"> - - - + - - - - - - - - - - - - - - - - - + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - - - - - - - - - - - + - + + + + + + - - \ No newline at end of file + + + diff --git a/app/src/main/res/menus/mastodon/menu/activity_main_drawer.xml b/app/src/main/res/menus/mastodon/menu/activity_main_drawer.xml index 12cbc5418e9bcd498cd65a909b5fd91e793ecc5a..eeb6be63f7e3b5602397d33b7381cc221e6db40f 100644 --- a/app/src/main/res/menus/mastodon/menu/activity_main_drawer.xml +++ b/app/src/main/res/menus/mastodon/menu/activity_main_drawer.xml @@ -73,6 +73,11 @@ android:icon="@drawable/ic_info_outline_white_24dp" android:title="@string/action_about_instance" android:visible="true" /> + diff --git a/app/src/main/res/menus/mastodon/menu/bottom_nav_menu.xml b/app/src/main/res/menus/mastodon/menu/bottom_nav_menu.xml index 3fda0121cd7d9e2e62f5309316de65d4b2ac99a9..0c328b2e6f3402591d30a66ccde2ca01d953344e 100644 --- a/app/src/main/res/menus/mastodon/menu/bottom_nav_menu.xml +++ b/app/src/main/res/menus/mastodon/menu/bottom_nav_menu.xml @@ -24,6 +24,6 @@ + android:title="@string/v_direct" /> \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph_settings.xml b/app/src/main/res/navigation/nav_graph_settings.xml index bc89c544b4e55957589f2a7aee212e4472204b01..4e364c76efccde06acc840053f2bebc9350a9d50 100644 --- a/app/src/main/res/navigation/nav_graph_settings.xml +++ b/app/src/main/res/navigation/nav_graph_settings.xml @@ -76,6 +76,14 @@ app:popEnterAnim="@anim/pop_enter" app:popExitAnim="@anim/pop_exit" /> + + + + Média přes celý displej Média se budou zobrazovat přes celou šířku displeje a u výšky se bude respektovat poměr stran. Tagy Twitteru (přes Nitter) + Více voleb + Oblíbené + Boostnout + Citovat + Přidat varování o obsahu + Odstranit varování o obsahu + Změnit viditelnost + Nastavit jazyk + Publikovat + Otevřít panel pro novou přílohu + Zavřít panel pro novou přílohu + Připojit obrázky + Připojit zvuk + Připojit videa + Připojit soubory + Přidat anketu + Informace o instanci + Síť + Automaticky skrývat tlačítko pro vytvoření + Automaticky skrývat tlačítko pro vytvoření během posouvání nahoru po časové ose diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 64c140a5e2b63f4f30415fb3f310bf43a4809edd..0f64e39bab8a332f0f9ae0eb8b458786e3434dba 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1078,4 +1078,13 @@ Pronomen Unterstützung von Pronomen QR Code Generator + Dein Token + Mehr Optionen + Favorisieren + Zitieren + Content Warnung (CW) hinzufügen + Content Warnung (CW) entfernen + Veröffentlichen + Sichtbarkeit anpassen + Sprache ändern diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 0da33f6d0a865e43d47e413f1f58c2eeaf2d332f..5a593f90090b5aa12f6422a7bf58bffc823b375d 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -2,20 +2,20 @@ درباره درباره این نمونه - حریم شخصی + حریم خصوصی حافظه نهان خروج از حساب بستن - بله - خیر - لغو + آری + نه + رد کردن بارگیری بارگیری %1$s - رسانه، ذخیره شد + رسانه ذخیره شد پرونده: %1$s گذرواژه - ایمیل + رایانامه حساب‌ها بوق‌ها برچسب‌ها @@ -24,59 +24,59 @@ نمونه: mastodon.social اکنون با این حساب کار می‌کند: %1$s افزودن حساب کاربری - از محتوای بوق در حافظه رونوشت گرفته شد - از نشانی بوق در حافظه رونوشت گرفته شد + محتوای بوق در بریده‌دان رونویسی شد + نشانی بوق در بریده‌دان رونویسی شد دوربین - حذف همه + پاک‌کردن همه زمان‌بندی - اندازه متن + اندازه نوشته بعدی - قبلی - باز کردن با - تایید + پیشین + گشودن با + پذیرش رسانه هم‌رسانی با - هم‌رسانی با Fedilab + هم‌رسانی‌شده با فدی‌لب پاسخ‌ها نام کاربری پیش‌نویس‌ها - پسندها - دنبال‌کننده‌های جدید + برگزیده‌ها + دنبال‌کنندگان جدید اشاره‌ها تقویت‌ها نمایش تقویت‌ها نمایش پاسخ‌ها - باز کردن در مرورگر + گشودن در مرورگر ترجمه خانه خط زمانی محلی - کاربران بی صدا شده - کاربران مسدود شده - اعلان‌ها - درخواست پیگیری + کاربران بی‌صدا + کاربران بسته‌شده + آگاه‌سازی‌ها + درخواست دنبال کردن تنظیمات - فرستادن یک رایانامه - بوق‌های زمان‌بندی شده - اطلاعات زیر ممکن است نمایه کاربر را کامل منعکس نکند. + فرستادن رایانامه + بوق‌های زمان‌بندی‌شده + داده‌های زیر ممکن است نمایه کاربر را به‌طور کامل نشان ندهند. درج شکلک - The app did not collect custom emojis for the moment. - Are you sure you want to logout @%1$s@%2$s? + برنامه هنوز شکلک‌های سفارشی را جمع‌آوری نکرده است. + آیا مطمئنید که می‌خواهید از @%1$s@%2$s خارج شوید؟ بوقی برای نمایش نیست - این بوق به علاقه‌مندی‌ها اضافه شود؟ - این بوق از علاقه‌مندی‌ها حذف شود؟ + این بوق به برگزیده‌ها افزوده شود؟ + این بوق از برگزیده‌ها پاک شود؟ این بوق تقویت شود؟ - تقویت این بوق حذف شود؟ - بی‌صدا + تقویت این بوق پاک شود؟ + بی‌صدا کردن مسدود کردن گزارش - حذف - رونوشت‌ - به‌اشتراک‌گذاری + پاک‌کردن + رونوشت + هم‌رسانی اشاره بی‌صدا کردن زمان‌دار - حذف و بازنویسی + پاک‌کردن و بازنویسی این حساب بی‌صدا شود؟ این حساب مسدود شود؟ @@ -93,127 +93,126 @@ این بوق پاک شود؟ این بوق پاک و بازنویسی شود؟ - نشانک‌ها - افزودن به نشانک‌ها - حذف نشانک - Status has been added to bookmarks! - مطلب از نشانک‌ها حذف شد! + نشانه‌ها + افزودن به نشانه‌ها + پاک‌کردن نشانه + بوق به نشانه‌ها افزوده شد! + بوق از نشانه‌ها پاک شد! - %d ث - %d د - %d س - %d ر + %d ثانیه + %d دقیقه + %d ساعت + %d روز - %d second + %d ثانیه %d ثانیه - %d minute + %d دقیقه %d دقیقه - %d hour + %d ساعت %d ساعت - %d day + %d روز %d روز - An error occurred while selecting the media! - Remove this media? - Your toot is empty! - The toot has been sent! - Sensitive content? - No drafts! - Choose an account - Select some accounts - Delete draft? - Describe for the visually impaired + خطایی هنگام انتخاب رسانه رخ داد! + این رسانه پاک شود؟ + بوق شما خالی است! + بوق فرستاده شد! + محتوای حساس؟ + پیش‌نویسی وجود ندارد! + انتخاب حساب + چند حساب انتخاب کنید + پیش‌نویس پاک شود؟ + توصیف برای افراد با مشکل بینایی - No description available! + توصیفی در دسترس نیست! - Release %1$s - Developer: - License: + نسخه %1$s + توسعه‌دهنده: + پروانه: GNU GPL V3 - Source code: - Search instances: + کد منبع: + کاوش نمونه‌ها: - No account to display - No follow request - بوق‌ها -\n %1$s - Following \n %1$s - Followers \n %1$s - Reject + حسابی برای نمایش نیست + درخواستی برای دنبال کردن نیست + بوق‌ها\n %1$s + دنبال‌شوندگان \n %1$s + دنبال‌کنندگان\n %1$s + رد کردن - No scheduled toots to display! - Delete scheduled toot? - The toot has been scheduled! - The scheduled date must be greater than the current hour! + بوق زمان‌بندی‌شده‌ای برای نمایش نیست! + بوق زمان‌بندی‌شده پاک شود؟ + بوق زمان‌بندی شد! + تاریخ زمان‌بندی باید از ساعت کنونی بیشتر باشد! - The time for muting should be greater than one minute. - %1$s has been muted until %2$s.\n You can unmute this account from their profile page. - %1$s is muted until %2$s.\n Tap here to unmute the account. + زمان بی‌صدا کردن باید بیشتر از یک دقیقه باشد. + %1$s تا %2$s بی‌صدا شد.\n می‌توانید این حساب را از صفحه نمایه‌اش از حالت بی‌صدا خارج کنید. + %1$s تا %2$s بی‌صدا است.\n برای خروج از حالت بی‌صدا اینجا بزنید. - No notification to display - mentioned you - wrote a new message - boosted your status - favourited your status - followed you - asked to follow you - Delete all notifications? - All notifications have been deleted! + آگاه‌سازی برای نمایش نیست + از شما یاد کرد + پیام جدیدی نوشت + بوق شما را تقویت کرد + بوق شما را برگزید + شما را دنبال کرد + درخواست دنبال کردن شما را داد + همه آگاه‌ساز‌ها پاک شوند؟ + همه آگاه‌ساز‌ها پاک شدند! - Followers + دنبال‌کنندگان - Unable to get client id! - The account was blocked! - The account is no longer blocked! - The account was muted! - The account is no longer muted! - The account was followed! - The account is no longer followed! - The toot was boosted! - The toot is no longer boosted! - The toot was added to your favourites! - The toot was removed from your favourites! - Oops ! An error occurred! - An error occurred! The instance did not return an authorisation code! - The instance domain does not seem to be valid! - An error occurred while switching between accounts! - An error occurred while searching! - No action can be taken - An error occurred while translating! + ناتوان در دریافت شناسه کارخواه(کلاینت)! + حساب بسته شد! + حساب دیگر بسته نیست! + حساب بی‌صدا شد! + حساب دیگر بی‌صدا نیست! + حساب دنبال شد! + حساب دیگر دنبال نمی‌شود! + بوق تقویت شد! + بوق دیگر تقویت‌شده نیست! + بوق به برگزیده‌های شما افزوده شد! + بوق از برگزیده‌های شما پاک شد! + اوه! خطایی رخ داد! + خطایی رخ داد! نمونه کد تأیید را برنگرداند! + دامنه نمونه درست به نظر نمی‌رسد! + خطایی هنگام جابه‌جایی بین حساب‌ها رخ داد! + خطایی هنگام جستجو رخ داد! + هیچ اقدامی نمی‌توان انجام داد + خطایی هنگام ترجمه رخ داد! - Number of toots per load - Disable GIF avatars - Notify when someone follows you - Notify when someone boosts your status - Notify when someone favourites your status - Notify when someone mentions you - Notify when a poll ended - Notify for new posts - Show confirmation dialog before boosting - Show confirmation dialog before adding to favourites - Notify? - Silent Notifications - NSFW view timeout (seconds, 0 means off) - Media Description timeout (seconds, 0 means off) - Custom sharing - Your custom sharing URL… - Lock account - Save changes - Fit preview images - Between - and - Use the built-in browser - Custom tabs - Automatically expand cw - Set LED colour: + شمار بوق‌ها در هر بارگذاری + نمایش تصاویر متحرک در آواتارها غیرفعال شود + آگاه‌سازی هنگام دنبال شدن به‌دست کسی + آگاه‌سازی هنگام تقویت بوق شما + آگاه‌سازی هنگام برگزیدن بوق شما + آگاه‌سازی هنگام اشاره به شما + آگاه‌سازی هنگام پایان نظرسنجی + آگاه‌سازی برای بوق‌های جدید + نمایش پیام پذیرش پیش از تقویت + نمایش پیام پذیرش پیش از افزودن به برگزیده‌ها + آگاه‌سازی شود؟ + آگاه‌سازی‌های بی‌صدا + مهلت نمایش محتوای حساس (ثانیه، 0 یعنی خاموش) + مهلت توصیف رسانه (ثانیه، 0 یعنی خاموش) + هم‌رسانی سفارشی + نشانی هم‌رسانی سفارشی شما… + قفل کردن حساب + ذخیره تغییرات + تناسب پیش‌نمایش تصاویر + از + تا + به‌کارگیری مرورگر داخلی + زبانه‌های سفارشی + گسترش خودکار هشدار محتوا + تنظیم رنگ LED: Blue Cyan @@ -223,120 +222,120 @@ Yellow White - Follow - Unblock - Mute - Unmute - Request sent - Follows you - First letter in capital for replies - Resize pictures - Resize videos + دنبال کردن + باز کردن + بی‌صدا کردن + خروج از بی‌صدا + درخواست فرستاده شد + شما را دنبال می‌کند + حرف نخست بزرگ برای پاسخ‌ها + تغییر اندازه تصاویر + تغییر اندازه ویدیوها - Mb + مگابایت - Title - Title… - Description - Keywords - Keywords… + عنوان + عنوان… + توصیف + کلیدواژه‌ها + کلیدواژه‌ها… - Public - Unlisted - Private - Direct + عمومی + فهرست‌نشده + خصوصی + مستقیم - Filter out by regular expressions - Search - Delete + پالایش با عبارات منظم + جستجو + پاک‌کردن - Lists - Are you sure you want to permanently delete this list? - Add to list - Delete list - New list title - The account was added to the list! - You don\'t have any lists yet! + فهرست‌ها + آیا مطمئنید که می‌خواهید این فهرست را برای همیشه پاک کنید؟ + افزودن به فهرست + پاک‌کردن فهرست + عنوان فهرست جدید + حساب به فهرست افزوده شد! + هنوز هیچ فهرستی ندارید! - %1$s has moved to %2$s - Media has been loaded. Tap here to display it. + %1$s به %2$s منتقل شده است + رسانه بارگذاری شد. برای نمایش اینجا بزنید. - Proxy - Enable proxy? - Host - Port - Login - Password - Add toot details when sharing - Support the app on Liberapay - There is an error in the regular expression! - No timelines was found on this instance! - Follow instance - You already follow this instance! - Partnerships - Hide boosts from %s - Feature on profile - Show boosts from %s - Don\'t feature on profile - Direct message - Filters - No filters to display. You can create one by tapping on the \"+\" button. - Keyword or phrase - Home timeline - Public timelines - Notifications - Conversations - Will be matched regardless of casing in text or content warning of a toot - Drop instead of hide - Filtered toots will disappear irreversibly, even if filter is later removed - When the keyword or phrase is alphanumeric only, it will only be applied if it matches the whole word - Whole word - Filter contexts - One or multiple contexts where the filter should apply - Expire after - Delete filter? - Update filter - You have not created a list yet. Tap on the \"+\" button to add a new one. - Automatically expand hidden media - New follow - New Boost - New Favourite - New Mention - Poll Ended - Toots Backup - New posts - Media Download - Select Tone - Enable time slot - Are you sure to block %s?\n\nYou will not see any content from that domain in any public timeline or in your notifications. Your followers from that domain will be removed. - Block domain - The domain is blocked - Fetching remote status - Peertube instance - Use Emoji One - Information - Display previews in all toots - The account id has been copied in the clipboard! - Change the language - Truncate long toots - Truncate toots over \'x\' lines. Zero means disabled. - Display more - Display less - The tag already exists! - Schedule boost - The boost is scheduled! - No scheduled boost to display! - Open menu - Profile picture - Profile banner - Contact admin of the instance - MastoHost logo - Emoji picker - Expand the conversation - Custom emoji picker - Favicon - Add description for media (for the visually impaired) + پروکسی + فعال‌سازی پروکسی؟ + میزبان + درگاه + ورود + گذرواژه + افزودن جزئیات بوق هنگام هم‌رسانی + پشتیبانی از برنامه در Liberapay + خطایی در عبارت منظم وجود دارد! + هیچ خط زمانی‌ای در این نمونه یافت نشد! + دنبال کردن نمونه + شما این نمونه را از قبل دنبال می‌کنید! + همکاری‌ها + پنهان کردن تقویت‌ها از %s + نمایش در نمایه + نمایش تقویت‌ها از %s + عدم نمایش در نمایه + پیام مستقیم + پالایشها + پالایشی برای نمایش نیست. با زدن دکمه «+» می‌توانید یکی بسازید. + کلیدواژه یا عبارت + خط زمانی خانه + خط‌های زمانی عمومی + آگاه‌ساز‌ها + گفت‌وگوها + صرف‌نظر از بزرگ یا کوچک بودن حروف، در متن یا هشدار محتوای بوق تطبیق داده می‌شود + پاک‌کردن به جای پنهان کردن + بوق‌های پالایش‌شده به‌طور غیرقابل بازگشت ناپدید می‌شوند، حتی اگر پالایش پس از آن پاک شود + اگر کلیدواژه یا عبارت فقط شامل حروف و اعداد باشد، تنها در صورت تطبیق با کل واژه اعمال می‌شود + کل واژه + زمینه‌های پالایش + یک یا چند زمینه که پالایش باید در آن‌ها اعمال شود + انقضا پس از + پالایش پاک شود؟ + به‌روزرسانی پالایش + هنوز فهرستی نساخته‌اید. برای افزودن فهرست جدید روی دکمه «+» بزنید. + گسترش خودکار رسانه‌های پنهان + دنبال‌کننده جدید + تقویت جدید + برگزیدن جدید + اشاره جدید + پایان نظرسنجی + پشتیبان بوق‌ها + بوق‌های جدید + بارگیری رسانه + انتخاب صدا + فعال‌سازی بازه زمانی + آیا مطمئنید که می‌خواهید %s را ببندید؟\n\nشما هیچ محتوایی از آن دامنه را در خط‌های زمانی عمومی یا آگاه‌سازی‌هایتان نخواهید دید. دنبال‌کنندگان شما از آن دامنه پاک خواهند شد. + بستن دامنه + دامنه بسته شد + بازیابی وضعیت از راه دور + نمونه پیرتیوب + به‌کارگیری Emoji One + داده‌ها + نمایش پیش‌نمایش در همه بوق‌ها + شناسه حساب در بریده‌دان رونویسی شد! + تغییر زبان + کوتاه کردن بوق‌های بلند + کوتاه کردن بوق‌های بیش از «x» خط. صفر یعنی غیرفعال. + نمایش بیشتر + نمایش کمتر + برچسب از قبل وجود دارد! + زمان‌بندی تقویت + تقویت زمان‌بندی شد! + تقویت زمان‌بندی‌شده‌ای برای نمایش نیست! + گشودن فهرست + تصویر نمایه + بنر نمایه + تماس با مدیر نمونه + نماد MastoHost + انتخاب‌کننده شکلک + گسترش گفت‌وگو + انتخاب‌کننده شکلک سفارشی + نمادک + افزودن توصیف برای رسانه (برای افراد با مشکل بینایی) Never 30 minutes @@ -347,143 +346,139 @@ 1 week - Languages - Media only - Show NSFW - Bot - Pixelfed instance - Mastodon instance - Any of these - All of these - None of these - Any of these words (space-separated) - All these words (space-separated) - Add some words to filter (space-separated) - Change column name - Misskey instance - Trending - Local - Category - Description - Share - Toots (Server) - Toots (Device) - Timelines - Interface - Contacts - An error occurred when selecting the backup file! - Logout account - All - Copy link - http calls blocked by the application - List of blocked calls - Submit - Filter timeline with tags - No tags - Attach an image when sharing a URL + زبان‌ها + فقط رسانه + نمایش محتوای حساس + ربات + نمونه پیکسل‌فد + نمونه ماستادون + هر یک از این‌ها + همه این‌ها + هیچ‌یک از این‌ها + هر یک از این واژه‌ها (با فاصله جدا شده) + همه این واژه‌ها (با فاصله جدا شده) + افزودن چند واژه برای پالایش (با فاصله جدا شده) + تغییر نام ستون برچسب + نمونه Misskey + روندها + محلی + دسته + توصیف + هم‌رسانی + بوق‌ها (کارساز) + بوق‌ها (دستگاه) + خط‌های زمانی + رابط کاربری + تماس‌ها + خطایی هنگام انتخاب پرونده پشتیبان رخ داد! + خروج از حساب + همه + رونوشت پیوند + فراخوان‌های HTTP توسط برنامه بسته شده‌اند + فهرست فراخوان‌های بسته‌شده + ارسال + پالایش خط زمانی با برچسب‌ها + بدون برچسب + پیوست تصویر هنگام هم‌رسانی پیوند - Create a poll - Choice %d - You need two choices at least for the poll! - Done - end at %s - Vote - A poll you have voted in has ended - A poll you tooted has ended - Categories - Move timeline - Hide timeline - Reorder timelines - List permanently deleted - Followed instance removed - Pinned tag removed - Undo - Main timelines can only be hidden! - Always mark media as sensitive - GNU instance - Forward tags in replies - Long press to store media - Manage tags - Display name - Emoji - Text - Filter - Brush - Discard - Saving… - Image Saved Successfully! - Failed to save Image - Add a poll item - Mute conversation - Unmute conversation - The conversation is no longer muted! - The conversation is muted - General - Regional - Art - Activism - Gaming - Technology - Furry - Food - Logo of the instance - Join Mastodon - Choose an instance by picking up a category, then tap on a check button. - %1$s users - Confirm password - I agree to %1$s and %2$s - server rules - terms of service - Sign up - This instance works with invitations. Your account will need to be manually approved by an administrator before being usable. - Passwords don\'t match! - The email doesn\'t seem to be valid! - You will be sent a confirmation e-mail - Use at least 8 characters - Password should contain at least 8 characters - Username should only contain letters, numbers and underscores - Account created! - Your account has been created!\n\n - Think to validate your email within the 48 next hours.\n\n - You can now connect your account by writing %1$s in the first field and click on Connect.\n\n - Important: If your instance required validation, you will receive an email once it is validated! - - Save the message in drafts? - Administration - Reports - Unresolved - Remote - Active - Pending - Disabled - Suspended - Permissions - Disable - Silence - Account - Undo silence - Undo disable - Suspend - Undo suspend - The application needs to access audio recording - Voice message - During the time slot, the app will send notifications. You can reverse (ie: silent) this time slot with the right spinner. - Previews will not be cropped in timelines - Automatically insert a line break after the mention to capitalize the first letter - Allow content creators to share statuses to their RSS feeds - Compose - Select - Add an instance - Enable crash reports - If enabled, a crash report will be created locally and then you will be able to share it. - Fedilab has stopped :( - You can send me by email the crash report. It will help to fix it :)\n\nYou can add additional content. Thank you! - Visibility - Disable custom animated emojis - Report account + ساخت نظرسنجی + گزینه %d + برای نظرسنجی حداقل به دو گزینه نیاز است! + انجام شد + پایان در %s + رأی + نظرسنجی‌ای که در آن رأی داده‌اید پایان یافت + نظرسنجی‌ای که منتشر کرده‌اید پایان یافت + دسته‌ها + جابه‌جایی خط زمانی + پنهان کردن خط زمانی + بازچینش خط‌های زمانی + فهرست برای همیشه پاک شد + نمونه دنبال‌شده پاک شد + برچسب سنجاق‌شده پاک شد + بازگرداندن + خط‌های زمانی اصلی فقط می‌توانند پنهان شوند! + همیشه علامت‌گذاری رسانه به‌عنوان حساس + نمونه گنو + ارسال برچسب‌ها در پاسخ‌ها + فشار طولانی برای ذخیره رسانه + مدیریت برچسب‌ها + نام نمایشی + شکلک + نوشته + پالایش + قلم‌مو + دور انداختن + در حال ذخیره… + تصویر با موفقیت ذخیره شد! + ذخیره تصویر ناموفق بود + افزودن گزینه نظرسنجی + بی‌صدا کردن گفت‌وگو + خروج گفت‌وگو از بی‌صدا + گفت‌وگو دیگر بی‌صدا نیست! + گفت‌وگو بی‌صدا شد + عمومی + منطقه‌ای + هنر + کنشگری + بازی + فناوری + فری + غذا + نماد نمونه + پیوستن به ماستادون + یک نمونه را با انتخاب دسته و سپس زدن دکمه پذیرش انتخاب کنید. + %1$s کاربر + تکرار گذرواژه + من با %1$s و %2$s موافقم + قوانین کارساز + شرایط خدمات + نام‌نویسی + این نمونه با دعوت‌نامه کار می‌کند. حساب شما باید پیش از به‌کارگیری به‌دست مدیر به‌صورت دستی پذیرفته شود. + گذرواژه‌ها همخوانی ندارند! + رایانامه درست به نظر نمی‌رسد! + رایانامه پذیرش برای شما فرستاده خواهد شد + حداقل از ۸ نویسه استفاده کنید + گذرواژه باید دست‌کم ۸ نویسه داشته باشد + نام کاربری فقط باید شامل حروف، اعداد و زیرخط باشد + حساب ساخته شد! + حساب شما ساخته شد!\n\n فراموش نکنید رایانامه خود را ظرف ۴۸ ساعت آینده بپذیرید.\n\n اکنون می‌توانید با نوشتن %1$s در کادر نخست و کلیک روی اتصال به حساب خود متصل شوید.\n\n مهم: اگر نمونه شما نیاز به پذیرش دارد، پس از پذیرش، رایانامه‌ای دریافت خواهید کرد! + پیام در پیش‌نویس‌ها ذخیره شود؟ + مدیریت + گزارش‌ها + حل‌نشده + دور + فعال + در انتظار + غیرفعال + تعلیق‌شده + اجازه‌ها + غیرفعال کردن + بی‌صدا کردن + حساب + لغو بی‌صدا + لغو غیرفعال + تعلیق + لغو تعلیق + برنامه نیاز به دسترسی به ضبط صدا دارد + پیام صوتی + در بازه زمانی، برنامه آگاه‌سازی‌ها را ارسال می‌کند. می‌توانید این بازه را با چرخاننده سمت راست معکوس کنید (یعنی بی‌صدا). + پیش‌نمایش‌ها در خط‌های زمانی برش نمی‌خورند + به‌طور خودکار پس از اشاره، یک خط جدید درج می‌شود تا حرف نخست بزرگ شود + اجازه به سازندگان محتوا برای هم‌رسانی وضعیت‌ها به خوراک‌های RSS آن‌ها + نگارش + انتخاب + افزودن نمونه + فعال‌سازی گزارش‌های خرابی + اگر فعال شود، گزارش خرابی به‌صورت محلی ساخته می‌شود و سپس می‌توانید آن را هم‌رسانی کنید. + فدی‌لب متوقف شد :(\" + می‌توانید گزارش خرابی را از طریق رایانامه برای من بفرستید. این کمک می‌کند تا مشکل برطرف شود :)\n\nمی‌توانید محتوای اضافی اضافه کنید. سپاسگزارم! + دیدپذیری + غیرفعال‌سازی شکلک‌های متحرک سفارشی + گزارش حساب - %d voter - %d voters + %d رأی‌دهنده + %d رأی‌دهنده Single choice @@ -498,80 +493,601 @@ 3 days 7 days - Your poll can\'t have duplicated options! - Clear cache when leaving - The cache (media, cached messages, data from the built-in browser) will be automatically cleared when leaving the application. - Do you want to unfollow this account? - Show confirmation dialog before unfollowing - Replace Medium links - Replace medium.com links with an open source alternative front-end focused on privacy. - Default: scribe.rip - Use a push notifications system for getting notifications in real time. - Add notes - Notes for the account - Allow to compress large photos into smaller sized photos with very less or negligible loss in quality of the image. - Allow compressing videos while maintaining their quality. - Order by - Links - Change the color of links (URLs, mentions, tags, etc.) in messages - Reblogs header - Change the color of display name at the top of messages - Change the color of the user name at the top of messages - Change the color of the header for reblogs - Posts - Background color of posts in timelines - Reset colors - Tap here to reset all your custom colors - Reset - Icons - Color of bottom icons in timelines - Logo of the instance - Edit profile - Make an action - Translation - Text color - Change the text color in messages - Use a custom theme - Theming - The theme was exported - The theme has been successfully exported in CSV - Import a theme - Tap here to import a theme from a previous export - Export the theme - Tap here to export the current theme - An error occurred when selecting the theme file - User count - Status count - Instance count - End in %s - This instance is not available on https://instances.social - Display full link - Share link - Open with another app - Check redirect - This URL does not redirect - %1$s \n\nredirects to\n\n %2$s - Remove UTM parameters - The app will automatically remove UTM parameters from URLs before visiting a link. - %d people talking - Twitter accounts (via Nitter) - Twitter usernames space separated - Identity proofs - هویت تایید شده - تایید شده توسط %1$s (%2$s) - Action disabled - Unfollow - Something went wrong, please check your download directory in settings. - Announcements - No announcements! - Add a reaction - Video cache in MB, zero means no cache. - Watermarks - Automatically add a watermark at the bottom of pictures. The text can be customized for each account. - No distributors found! - You need a distributor for receiving push notifications.\nYou will find more details at %1$s.\n\nYou can also disable push notifications in settings for ignoring that message. - Select a distributor + نظرسنجی شما نمی‌تواند گزینه‌های تکراری داشته باشد! + پاک‌کردن حافظه نهان هنگام خروج + حافظه نهان (رسانه، پیام‌های ذخیره‌شده، داده‌های مرورگر داخلی) هنگام خروج از برنامه به‌طور خودکار پاک می‌شود. + آیا می‌خواهید این حساب را دنبال نکنید؟ + نمایش گفت‌وگوی پذیرش پیش از لغو دنبال کردن + جایگزینی پیوندهای مدیوم + جایگزینی پیوندهای medium.com با یک نما متن‌باز با تمرکز بر حریم خصوصی + پیش‌فرض: scribe.rip + استفاده از سامانه آگاه‌سازی فشاری برای دریافت آگاه‌سازی‌ها در زمان واقعی. + افزودن یادداشت‌ها + یادداشت‌ها برای حساب + امکان فشرده‌سازی تصاویر بزرگ به تصاویر کوچک‌تر با کمترین یا بدون افت کیفیت. + امکان فشرده‌سازی ویدیوها با حفظ کیفیت آن‌ها. + مرتب‌سازی بر اساس + پیوندها + تغییر رنگ پیوندها (نشانی‌ها، اشاره‌ها، برچسب‌ها و...) در پیام‌ها + سربرگ تقویت‌ها + تغییر رنگ نام نمایشی در بالای پیام‌ها + تغییر رنگ نام کاربری در بالای پیام‌ها + تغییر رنگ سربرگ برای تقویت‌ها + بوق‌ها + رنگ پس‌زمینه بوق‌ها در خط‌های زمانی + بازنشانی رنگ‌ها + برای بازنشانی همه رنگ‌های سفارشی اینجا بزنید + بازنشانی + نمادها + رنگ نمادهای پایین در خط‌های زمانی + نماد نمونه + ویرایش نمایه + انجام یک کنش + ترجمه + رنگ نوشته + تغییر رنگ نوشته در پیام‌ها + استفاده از پوسته سفارشی + پوسته‌سازی + پوسته صادر شد + پوسته با موفقیت در قالب CSV صادر شد + وارد کردن پوسته + برای وارد کردن پوسته از صادرات قبلی اینجا بزنید + صدور پوسته + برای صدور پوسته کنونی اینجا بزنید + خطایی هنگام انتخاب پرونده پوسته رخ داد + شمار کاربران + شمار وضعیت‌ها + شمار نمونه‌ها + پایان در %s + این نمونه در https://instances.social در دسترس نیست + نمایش پیوند کامل + هم‌رسانی پیوند + گشودن با برنامه دیگر + بررسی تغییر مسیر + این نشانی تغییر مسیر نمی‌دهد + %1$s \n\nبه\n\n %2$s تغییر مسیر می‌دهد + پاک‌کردن پارامترهای UTM + برنامه به‌طور خودکار پارامترهای UTM را از نشانی‌ها پیش از بازدید پیوند پاک می‌کند. + %d نفر در حال گفتگو + حساب‌های توییتر (از طریق Nitter) + نام‌های کاربری توییتر با فاصله جدا شده + اثبات هویت + هویت تاییدشده + تاییدشده به‌دست %1$s (%2$s) + کنش غیرفعال + لغو دنبال کردن + مشکلی پیش آمد، لطفاً پوشه بارگیری را در تنظیمات بررسی کنید. + اطلاعیه + اطلاعیه‌ای وجود ندارد! + افزودن واکنش + حافظه نهان ویدیو به مگابایت، صفر یعنی بدون حافظه نهان. + واترمارک + افزودن خودکار واترمارک در پایین تصاویر. متن می‌تواند برای هر حساب سفارشی شود. + توزیع‌کننده‌ای یافت نشد! + برای دریافت آگاه‌سازی‌های فشاری به توزیع‌کننده نیاز دارید.\nجزئیات بیشتر را در %1$s خواهید یافت.\n\nهمچنین می‌توانید آگاه‌سازی‌های فشاری را در تنظیمات غیرفعال کنید تا این پیام نادیده گرفته شود. + انتخاب توزیع‌کننده اندازه نماد نمایش بوق‌های مستقیم - کاربران بی‌صدا شده خانه - \ No newline at end of file + کاربران بی‌صدای خانه + گزارش + برچسب‌های توییتر (با Nitter) + برگزیده‌شده توسط + مثال: محتوای حساس + هرزنامه است + به فدی‌ورس بپیوندید + نوع نظرسنجی: + به‌کارگیری توکن + تازه‌ترین + دامنه + آیا می‌خواهید بدون ذخیره تصویر خارج شوید؟ + اینجا بزنید تا نظرسنجی تازه شود + پاسخ + مدیریت حساب‌ها + نوشتن + اگر توصیفی برای رسانه وجود نداشته باشد، بوق فرستاده نمی‌شود + آخرین ۲۴ ساعت + رنگ برجسته تیره + دامنه‌های بسته‌شده + به‌روزرسانی جدید + توکن شما + کاربری گزارشی فرستاد + نمایه به‌روزرسانی شد! + زمان‌بندی‌شده + پالایه + گزینه‌های نمایش + دریافت آگاه‌سازی‌ها + گزارشی فرستاد + مترجم + به‌کاربردن نما جایگزین برای اینستاگرام + تغییر دیدپذیری + تنظیم زبان + پیوست تصاویر + پیوست پرونده‌ها + افزودن نظرسنجی + حساب غیرفعال شد + حساب از بی‌صدا خارج شد + لغو دنبال کردن برچسب + برچسب موردنظر برای دنبال کردن را بنویسید + ویرایش فهرست + افزودن کلیدواژه + نمایش نمایه از نمونه میزبان + پاک کردن خط زمانی + باز کردن دامنه + مرتب‌سازی فهرست‌ها + شدت + بی‌صدا کردن پست‌های حساب را برای کسانی که آن را دنبال نمی‌کنند نامرئی می‌کند. تعلیق همه محتوای حساب، رسانه‌ها و داده‌های نمایه را حذف می‌کند. اگر فقط می‌خواهید پرونده‌های رسانه‌ای را رد کنید، هیچ‌کدام را انتخاب کنید. + آیا مطمئنید که می‌خواهید برچسب %1$s را بی‌صدا کنید؟ + انتخاب یک حالت برای پوسته + امکان سفارشی‌سازی برخی عناصر در بوق‌ها برای پوسته تیره. + لطفاً بعداً دوباره تلاش کنید. + لغو بی‌صدا کردن برچسب + گروه‌بندی تقویت‌ها در خط زمانی خانه + دیدپذیری نمادها + دیدپذیری پاسخ + برنامه گفت‌وگوهای عمومی را برای دریافت همه بوق‌ها نمایش می‌دهد. کنش‌ها نیاز به گام اضافی برای فدراسیون بوق‌ها دارند. + هنگام انتشار نسخه جدید، در برنامه آگاه‌سازی نمی‌شوید. + پالایش زبان‌ها + ترجمه به + اشاره‌ها در بالا + شمار پاسخ‌ها + تاریخ به‌روزرسانی + توصیف‌های رسانه‌ای وجود ندارند + کوتاه کردن پیوندها + فقط هشدار + هیچ بوق نهان‌شده‌ای در خانه نیست! + %1$d بوق نهان‌شده + دریافت بوق‌ها + افزودن توصیف + انتخاب مسیرها + رنگ برجسته سفارشی + درخواست دنبال کردن شما را داده است + توییتر + اینستاگرام + دامنه نمای اینستاگرام + فقط دنبال‌کنندگان + ارسال بوق %d/%d + فعال است! + غیرفعال است! + بررسی‌شده در: %s + توقف ضبط + گزارش %1$s + بگویید مشکل این بوق چیست + اینجا گزینه‌های شما برای کنترل آنچه در Mastodon می‌بینید: + لغو دنبال کردن %1$s + آیا بوق‌هایی برای پشتیبانی از این گزارش وجود دارند؟ + همه موارد مرتبط را انتخاب کنید + حساب از سرور دیگری است. آیا نسخه‌ای ناشناس از گزارش را آنجا هم بفرستیم؟ + سلام! شما را به پیوستن به فدی‌ورس دعوت می‌کنیم. + «Mastodon یک وب‌سایت واحد مثل Twitter یا Facebook نیست، شبکه‌ای از هزاران جامعه است که توسط سازمان‌ها و افراد مختلف اداره می‌شوند و تجربه‌ای یکپارچه از رسانه‌های اجتماعی ارائه می‌دهند.» + نام‌بردن‌ها + پسندها + تقویت‌ها + نتایج نظرسنجی + دنبال کردن‌ها + علامت‌گذاری همه آگاه‌سازی‌ها به‌عنوان خوانده‌شده + نمایش همه دسته‌ها + بازشده + قفل‌شده + ذخیره تغییرات + حساب ربات + حساب قابل‌کشف + آیا مطمئنید که می‌خواهید این بخش را پاک کنید؟ + نمایش + منوی پایین + به‌کارگیری زبان پیش‌فرض سیستم + زبان بوق‌ها + برنامه نتوانست حساب را تصدیق کند! + گشودن پیش‌نویس + غیرفعال کردن پیمایش نوار بالا + پنهان‌سازی خودکار دکمه نوشتن + خط‌های زمانی در فهرست + نمایش تاریخ نسبی برای بوق‌ها + نمایش رسانه + نمایش خط‌های زمانی + بوق نهان‌شده + هنگام فعال بودن، برنامه آگاه‌سازی‌های مرتبط را جمع می‌کند + وضعیت ورود + هشدار + به‌کاربردن نما جایگزین برای توییتر + به‌کاربردن نما جایگزین برای ردیت + رسانه در آگاه‌سازی‌ها برای تقویت‌ها و پسندها نمایش داده می‌شود + لغو اختصاص + آیا مطمئنید که می‌خواهید %1$s را باز کنید؟ + سیاست حریم خصوصی + برنامه من + توزیع‌کننده فشار + گزینه‌های بیشتر + برگزیدن + تقویت + نقل + هشدار در صورت نبود توصیف رسانه پیش از تقویت + این بوق توصیف رسانه ندارد. آیا مطمئنید که می‌خواهید آن را تقویت کنید؟ + افزودن هشدار محتوا + پاک‌کردن هشدار محتوا + انتشار + گشودن بخش پیوست جدید + بستن بخش پیوست جدید + پیوست صدا + پیوست ویدیوها + توکن نمی‌تواند خالی باشد! + نام‌نویسی جدید + گزارش جدید + افزودن وضعیت + همچنین تقویت‌شده توسط: + لغو دنبال کردن برچسب + بوق از نشانک‌های شما پاک شد! + انتخاب پوسته‌ای که توسط مشارکت‌کنندگان ساخته شده است + پذیرفته‌شده + منشأ حساب گزارش‌شده + مستطیل + بوق‌های نهان‌شده برای دیگر خط‌های زمانی + آیا مطمئنید که می‌خواهید حافظه نهان را پاک کنید؟ اگر پیش‌نویس‌هایی با رسانه دارید، رسانه‌های پیوست از دست می‌روند. + تغییر لوگوی برنامه در دستگاه شما + سنجاق بوق + پاسخ‌های فهرست‌نشده + آگاه‌سازی‌ها از حافظه نهان پاک شدند. + بوق‌های گزارش‌شده + بی‌صدا + حساب رد شد + بوقی را ویرایش کرد + عنوان + آیا مطمئنید که می‌خواهید آن خط زمانی را از سنجاق بردارید؟ + خط‌های زمانی سنجاق‌شده پاک شوند؟ + دیدگاه درباره این محدودیت دامنه برای عموم، اگر نمایش فهرست محدودیت‌های دامنه فعال باشد. + این بوق به پاسخ‌ها تقسیم شود؟ + تقسیم نشود + تقسیم بوق + بوق به چندین پاسخ تقسیم می‌شود تا با بیشینه نویسه‌های نمونه شما سازگار باشد. + پوسته روشن پیش‌فرض + سفارشی‌سازی پوسته روشن + روشن - رنگ‌های سفارشی + گفت‌وگو در نمونه‌ی شما آغاز شد! + لغو سنجاق برچسب + همه را بی‌صدا کن + وارد کردن داده‌ها + مترجم + کلید رابط مترجم + نسخه + نسخه مترجم + با فعال کردن این گزینه، برنامه ویژگی‌های اضافی را نمایش می‌دهد. این ویژگی برای نرم‌افزارهای اجتماعی مانند پلروما، آکوما یا گلیچ سوشال طراحی شده است + می‌توانید این نمادها را در پایین پنهان کنید تا فضای بیشتری داشته باشید. آن‌ها در زیرمنو نیز هستند. + گفت‌وگوهای نمونه میزبان + دکمه‌های پایین بوق‌ها کل عرض را اشغال نمی‌کنند + پیرتیوب شما بیش از حد قدیمی است و برنامه از آن پشتیبانی نمی‌کند. + توکن ورود دو مرحله‌ای + دریافت خودکار بوق‌های خانه + دریافت خودکار بوق‌های گمشده + برچسب تغییر کرد! + برچسب پاک شد! + مدیریت برچسب‌ها + خط زمانی گفت‌وگو برای بوق‌های مستقیم + بیشینه نویسه‌ها در پیوندها + بازیابی حساب دور! + رنگ برجسته روشن + ردیت + داده‌های نمونه + نمونه لمی + رونویسی داده‌ها + کاربری نام‌نویسی کرد + نام‌نویسی‌ها + دامنه نمای توییتر + تولیدکننده کد QR + شبکه + ضمیر + پشتیبانی ضمیر + پاک‌کردن وضعیت + تقویت‌شده توسط + دیگر + در حال ارسال بوق… + زمان فعال: %,.2f %% + + ]]> + بهترین گزینه را انتخاب کنید + خوشم نمی‌آید + چیزی نیست که بخواهید ببینید + آیا چیز دیگری هست که باید بدانیم؟ + دیدگاه‌های اضافی + گزارش فرستاده شد! + حساب ندارید؟ + تعاملات + افزودن پالایه + افزودن بخش + کارکنان + حالت پاک‌کن + اعلان · %1$s - %2$s + پاک کردن حافظه نهان + اندازه حافظه نهان پرونده + تأخیر بین هر دریافت جدید را تنظیم کنید + دریافت آگاه‌سازی‌ها هر: + حذف حاشیه چپ + قالب بوق + دریافت خانه هر + تعریف رنگ پوسته برای هر حساب + بندانگشتی + اجازه دادن + هشدار سفارشی + نام‌نویسی کرد + نادیده گرفتن همه گزارش‌ها از این دامنه. برای تعلیق‌ها بی‌ربط است + به‌کارگیری حافظه نهان + خط‌های زمانی نهان می‌شوند تا برنامه سریع‌تر باشد. + بارگیری بندانگشتی‌ها برای رسانه + پاک کردن کلیدواژه + بی‌صدا برای خانه + بارگیری تنظیمات صادرشده + رد گزارش‌ها + دامنه مترجم + پوسته‌های مشارکت‌کنندگان + پنهان کردن محتوای پالایش‌شده پشت هشداری با ذکر عنوان پالایه + خانه و فهرست‌ها + برنامه نتوانست داده‌های نمونه میزبان را پیدا کند! + خط زیر موارد قابل کلیک + بوقی که هم‌رسانی کردید ویرایش شده است + نوع آگاه‌سازی‌ها را انتخاب کنید + من مدیر هستم + شکل + حساب فعال شد + پنهان‌سازی خودکار دکمه نوشتن هنگام پیمایش به بالا در خط زمانی + نمایش رسانه در آگاه‌سازی‌ها + فقط پاسخ‌های «عمومی» را شامل می‌شود. هنگام فعال بودن، پاسخ‌های شما خودکار «فهرست‌نشده» می‌شوند به‌جای «عمومی» + آگاه‌سازی کاربر از طریق رایانامه + گزارش‌های حافظه نهان خانه + ناکامی‌ها + تکرار (دقیقه) + خروج + هنگام فعال بودن، همه خط‌های زمانی سنجاق‌شده در منوی کشویی نمایش داده می‌شوند + نمایش شمارشگر برای بوق‌ها + کاربر + پاک‌سازی حافظه نهان + لغو سنجاق بوق + لغو دنبال کردن + سفارشی‌سازی پوسته تیره + نوشتن فرمول + قالب ریاضی + پنهان کردن رسانه تکی هنگام وجود پیش‌نمایش پیوند + تغییر زبان‌های اشتراک‌شده + امکان ایجاد پوسته سفارشی + "همچنین برگزیده‌شده توسط: " + پیشینه بوق + خود + بررسی حافظه نهان خانه + آخرین فعالیت + مکان + خط + نمایش دکمه‌های «واکنش‌ها» + وضعیت رایانامه + شمارشگر حبابی برای بوق‌های جدید در زبانه‌های خط زمانی نمایش می‌دهد + تنظیمات صادر شدند + تنظیمات با موفقیت صادر شدند + تنظیمات با موفقیت وارد شدند + پاک کردن بخش + پذیرفتن + نمایش تقویت‌های خود + بوق به نشانک‌های شما افزوده شد! + شمار حساب‌ها در هر بارگذاری + به‌کاربردن نما جایگزین برای یوتیوب + آیا مطمئنید که می‌خواهید همه آگاه‌سازی‌ها را پاک کنید؟ این کار قابل بازگشت نیست. + دنبال کردن برچسب + گزارشی فرستاد + دیدگاه خصوصی + دیدگاه درباره این محدودیت دامنه برای استفاده داخلی مدیران. + بوق‌های ذخیره‌شده در پیش‌نویس‌ها + کنش‌های بیشتر + وضعیت + بوق‌های نهان‌شده برای خانه + به‌روزرسانی‌ها + همیشه دکمه نشانه‌گذاری را نمایش بده + دریافت بوق‌های بیشتر… + بیضی + انتخاب لوگو + بازآغاز + سوابق حافظه نهان خانه در هر ساعت + خودکار + داده‌ها در بریده‌دان رونویسی شدند + نمایش پاسخ‌های خود + نمایش بوق‌های من + چندگزینه‌ای + شما هم‌اکنون این برچسب را دنبال می‌کنید! + دیدپذیری پیش‌فرض بوق‌ها: + ارتباط اینترنتی وجود ندارد! + شمار آگاه‌سازی‌ها در هر بارگذاری + پروتکل + موسیقی + این بخش نمی‌تواند خالی باشد! + جدید + دامنه نمای ردیت + ادامه + نسخه: %s\n%s کاربر - %s بوق + مشکل در دسته‌های دیگر جای نمی‌گیرد + شما این حساب را دنبال می‌کنید. برای ندیدن بوق‌هایشان در خوراک خانه، آن‌ها را لغو دنبال کنید. + بی‌صدا کردن %1$s + بوق‌هایشان را نخواهید دید. آن‌ها نمی‌توانند بوق‌هایتان را ببینند یا شما را دنبال کنند. متوجه خواهند شد که بسته شده‌اند. + کدام قوانین نقض شده‌اند؟ + ارسال به %1$s + به‌روزرسانی‌های افراد + حل‌شده + نمایش شمارشگرها + اختصاص به من + خط زمانی سنجاق‌شده پاک شود؟ + دامنه‌ها + به هر حال بفرست + %d بوق دریافت‌شده + یوتیوب + رسانه تمام‌صفحه + رسانه‌ها کل عرض صفحه را می‌گیرند و نسبت ابعاد برای ارتفاع رعایت می‌شود. + HTTP + SOCKS + دامنه نما یوتیوب + سفارشی + نمونه به نظر درست نمی‌آید! + پیوندهای مخرب، تعامل جعلی، یا پاسخ‌های تکراری + قوانین کارساز را نقض می‌کند + می‌دانید که قوانین خاصی را نقض می‌کند + چیز دیگری است + نمی‌خواهید این را ببینید؟ + بوق‌هایشان را نخواهید دید. آن‌ها همچنان می‌توانند شما را دنبال کنند و بوق‌هایتان را ببینند و نمی‌دانند که بی‌صدا شده‌اند. + بستن %1$s + «PeerTube ابزاری برای هم‌رسانی ویدیوهای آنلاین است که توسط Framasoft، یک سازمان غیرانتفاعی فرانسوی، توسعه یافته است... PeerTube به سکوها اجازه می‌دهد به یکدیگر متصل شوند و شبکه‌ای بزرگ از سکوهای خودمختار و به‌هم‌پیوسته ایجاد کنند.» + پاک کردن همه آگاه‌سازی‌ها + نام فهرست درست نیست! + هیچ حسابی برای این فهرست یافت نشد! + انواع آگاه‌سازی‌های قابل‌نمایش + تأیید لغو دنبال کردن‌ها + بوق فرستاده شد! + تک‌گزینه‌ای + مدت نظرسنجی: + همیشه دکمه ترجمه را نمایش بده + تغییر لوگو + بوق دیگر سنجاق نیست! + نگهداری آگاه‌سازی‌ها + آگاه‌سازی برای به‌روزرسانی‌ها + نوع آگاه‌سازی‌ها + نمونه من + حساب من + ویرایش بوق + %1$s ویرایش کرد %2$s + ویرایش‌شده در %1$s + ایجادشده در %1$s + پیوسته + آی‌پی اخیر + مدیر + مدیر + پذیرفته‌شده + حساب بی‌صدا شد + وضعیت + زبان‌های انتخابگر + آیا مطمئنید که می‌خواهید این برچسب را لغو دنبال کنید؟ + نام برچسب درست نیست! + برچسب‌های دنبال‌شده + دنبال کردن برچسب + نمایه‌ها + پنهان کردن کامل محتوای پالایش‌شده، مانند اینکه وجود نداشته باشد + کلیدواژه یا عبارت + علاقه‌مند نیستم + شما هیچ دامنه‌ای را نبسته‌اید + امکان تنظیم رنگ‌های سفارشی برای پوسته‌ها. + هنگام فعال بودن، موارد در خط‌های زمانی سایه و برجستگی خواهند داشت. + امکان سفارشی‌سازی برخی عناصر در بوق‌ها برای پوسته روشن. + تنظیم رنگ‌های سفارشی + تیره - رنگ‌های سفارشی + نمایش گفت‌وگو از نمونه میزبان + برنامه بوق را در نمونه میزبان پیدا نکرد. + بی‌صدا کردن برچسب + قالب بوق + نمادهای ویژگی‌های اضافی + فقط محلی + دنبال‌شده توسط: + دریافت بوق‌های خانه + برچسب ذخیره شد! + رنگی که برای پوسته روشن اعمال می‌شود + صداهای آگاه‌سازی + غیرفعال کردن آگاه‌سازی‌ها + در این بازه زمانی + پایه پوسته + انتخاب کنید که پایه پوسته تیره باشد یا روشن + سفارشی‌سازی خط‌های زمانی + انتخاب پوسته + منوی نوار بالا + حداکثر تعداد نویسه‌ها را تنظیم کنید + یادداشت‌های انتشار + برنامه نتوانست توکن دریافت کند + رسانه بارگذاری نشد! + زمان دریافت آگاه‌سازی‌ها + صدور تنظیمات + وارد کردن تنظیمات + اجازه داده نشد! + نوار کنش تکی + هنگام فعال بودن، برنامه فقط یک نوار برای خط‌های زمانی خواهد داشت + گشودن بوق اصلی + پذیرفته‌نشده + به‌خاطر سپردن جایگاه در خط‌های زمانی + تجمیع آگاه‌سازی‌ها + ترجمه بوق‌ها + بوق سنجاق شد + سنجاق‌شده + تصویر کارت + اجبار ترجمه به زبانی خاص. اولین مقدار را انتخاب کنید تا به تنظیمات دستگاه بازگردد + حداکثر تورفتگی در نخ‌ها + علامت‌گذاری به‌عنوان حل‌نشده + علامت‌گذاری به‌عنوان حل‌شده + حساب پذیرفته شد + حساب هشدار گرفت + حساب از تعلیق خارج شد + حساب معلق شد + برنامه بازآغاز شود؟ + برای اعمال تغییرات باید برنامه را بازآغاز کنید. + امکان کاهش فهرست زبان‌ها در انتخابگر هنگام نوشتن بوق. + شما هیچ برچسبی را دنبال نمی‌کنید! + نمونه‌ی شما از این ویژگی پشتیبانی نمی‌کند! + پیگیری روندها برای این نمونه + کارکرد پالایه + انتخاب کنید چه کارکردی هنگام تطبیق بوق با پالایه انجام شود + پنهان با هشدار + پنهان کامل + نمایش به هر حال + پالایش‌شده: %1$s + برنامه نتوانست حساب را به فهرست بیفزاید! + پیشنهادها + نام‌نویسی جدید (مدیران) + گزارش جدید (مدیران) + گشودن با حساب دیگر + رد رسانه + بستن دامنه مانع ایجاد ورودی‌های حساب در پایگاه داده نمی‌شود، اما روش‌های نظارتی خاصی را به‌صورت خودکار و گذشته‌نگر بر آن حساب‌ها اعمال می‌کند. + رد پرونده‌های رسانه‌ای + رد گزارش‌ها + نادیده گرفتن همه گزارش‌ها از این دامنه. برای تعلیق‌ها بی‌ربط است + پنهان‌سازی نام دامنه + پنهان‌سازی جزئی نام دامنه در فهرست، اگر نمایش فهرست محدودیت‌های دامنه فعال باشد + دیدگاه عمومی + تغییرات ذخیره شدند! + ایجاد بستن دامنه + تقسیم بوق‌های طولانی به پاسخ‌ها + سفارشی‌سازی رنگ‌ها + رنگ پویا + هماهنگی رنگی با طرح رنگ کاغذدیواری شخصی شما. + پوسته تیره پیش‌فرض + کارت‌های برجسته + سنجاق برچسب + لغو بی‌صدا برای خانه + افزودن همه کاربران به خانه بی‌صدا + همه حساب‌ها برای خط زمانی خانه بی‌صدا خواهند شد. + حذف حاشیه چپ در خط‌های زمانی برای فشرده‌تر کردن بوق‌ها + ویژگی‌های اضافی + اگر نمونه‌ی شما برخی ویژگی‌های اضافی را نمی‌پذیرد، می‌توانید این نمادها را پنهان کنید + نمایش دکمه‌ی «نقل» + حباب + حذف دیدپذیری + فهرست + دنبال‌شوندگان + نمایه‌های نمونه میزبان + برنامه نمایه‌های عمومی را برای دریافت همه بوق‌ها نمایش می‌دهد. کنش‌ها نیاز به گام اضافی برای فدراسیون بوق‌ها دارند. + نمایش دکمه‌ی «فقط محلی» + نمایش پیکسل‌فد برای رسانه + دکمه‌های کنش فشرده + راهنما + نمایش تاریخ اصلی برای تقویت‌ها + نمایش نوار پیمایش برای خط‌های زمانی + پشتیبانی از مارک‌داون + غیرفعال کردن یادداشت‌های انتشار + فرمول + زمان دریافت خانه + حافظه نهان خانه + هنگام پاسخ، همه اشاره‌ها به ابتدای بوق افزوده می‌شوند + شمار رسانه‌ها + %1$s رسانه دیگر + توصیف‌های اجباری رسانه + اگر رسانه‌ای بدون توصیف باشد، گفت‌وگویی نمایش داده می‌شود با امکان فرستادن بوق بدون توصیف رسانه + نادیده گرفتن بهینه‌سازی باتری + پخش خودکار رسانه‌های متحرک + بوق‌های جدید + بوق‌های به‌روزرسانی‌شده + مجموع بوق‌های دریافت‌شده + %d ناکامی + %d بوق جدید + %d بوق به‌روزرسانی‌شده + %d تکرار (دقیقه) + شما را دنبال می‌کند + بارگیری رسانه از نمونه میزبان + رسانه بارگیری نشد! + دریافت خودکار رسانه‌های نمونه میزبان هنگام در دسترس نبودن + رنگی که برای پوسته تیره اعمال می‌شود + diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index b0e0f87d65fd08c0faa161067b6e8d3144b56d40..93340f4d1bcd1fcae1fbe289523db4eb79c7b7e6 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -1074,4 +1074,26 @@ Etiquetas de Twitter (con Nitter) Multimedia a pantalla completa O multimedia ocupará todo o ancho da pantalla e vanse respetar as proporcións de aspecto. + Promover + Engadir aviso sobre o contido + Cambiar a visibilidade + Establecer idioma + Publicar + Abrir o panel de novo anexo + Pechar o panel de novo anexo + Anexar audio + Anexar vídeos + Anexar ficheiros + Anexar unha enquisa + Favorecer + Anexar imaxes + Máis opcións + Citar + Retirar aviso sobre o contido + Información da instancia + Oculta o botón para redactar + Rede + Oculta automáticamente o botón para redactar cando te desprazas nunha cronoloxía + Mencionar a quen promoveu + Ao responder a unha promoción, a persoa que promoveu será mencionada na resposta diff --git a/app/src/main/res/values-kab/strings.xml b/app/src/main/res/values-kab/strings.xml index a8dcdb39cb4fe8a3fd4b5e7010fb9dbc02f97643..3cf53ba0f3d2733125f25ee065a948f6bd1bbe80 100644 --- a/app/src/main/res/values-kab/strings.xml +++ b/app/src/main/res/values-kab/strings.xml @@ -45,7 +45,7 @@ Ibdaren Izuzar Sken-d izuzar - Ssekned tiririt + Sken-d tiririyin Ldi deg iminig Suqel @@ -63,12 +63,12 @@ The app did not collect custom emojis for the moment. Are you sure you want to logout @%1$s@%2$s? - Ulac ituten ara d-nesken + Ulac iznan ara d-nesken Add this toot to your favourites? Remove this toot from your favourites? Boost this toot? Unboost this toot? - Susem + Sgugem Seḥbes Cetki Kkes @@ -225,7 +225,7 @@ Ḍfeṛ Serreḥ - Susem + Sgugem Unmute Request sent Yeṭafar-ik id @@ -243,9 +243,9 @@ Awalen n tsaruţ… Azayez - Unlisted + War abdar Uslig - Direct + Srid Filter out by regular expressions Nadi @@ -324,7 +324,7 @@ Sken ddeqs Sken kra kan The tag already exists! - Schedule boost + Sɣiwes azuzer The boost is scheduled! No scheduled boost to display! Ldi umuɣ @@ -466,7 +466,7 @@ Suspend Undo suspend The application needs to access audio recording - Voice message + Izen s taɣect During the time slot, the app will send notifications. You can reverse (ie: silent) this time slot with the right spinner. Previews will not be cropped in timelines Automatically insert a line break after the mention to capitalize the first letter @@ -643,4 +643,9 @@ Iɣewwaṛen n uketer Ɛebbi iɣewwaṛen yettwasifḍen Kter isefka + Talɣut ɣef uqeddac + Timyigawin + Akaram + Aqeddac-iw + Imeḍfaṛen kan diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 93f00f9a2cff0b6425f5e25057072c93e71a6557..53f242153c76467b9934c5d5b41949e281e92b62 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -276,7 +276,7 @@ Порт Имя пользователя Пароль - Добавлять детали сообщения при перепосте + Добавлять детали сообщения при перепубликации Поддержать приложение на Liberapay В регулярном выражении есть ошибка! На этом инстансе лент не найдено! @@ -682,7 +682,7 @@ Отслеживаемые теги Подписаться на тег Другое - Показать содержимое > + ]]> Присоединяйтесь к fediverse Привет! Приглашаем Вас присоединиться к Fediverse. Проблема не подпадает под другие категории @@ -693,13 +693,13 @@ Запоминать позицию на ленте Будут отображаться медиа в уведомлениях для репостов и избранного Версия - Скрыть содержимое < + Пожаловаться на %1$s Это НЕ то что вы бы хотели видеть Использовать кэш Это спам Вредоносные ссылки, повторяющиеся ответы - «Mastodon — это не один веб-сайт, такой как Twitter или Facebook, это сеть из тысяч сообществ, управляемых различными организациями и частными лицами, которые обеспечивают бесперебойную работу в социальных сетях». + «Mastodon — это не один вебсайт как Twitter или Facebook, это сеть из тысяч сообществ, управляемых различными организациями и частными лицами, которые вместе обеспечивают безбарьерную социальную сеть». Измените логотип приложения на вашем устройстве Перезапустить приложение\? Вы должны перезапустить приложение, чтобы применить изменения. @@ -906,4 +906,132 @@ Миниатюра Определите цвет темы для каждой учетной записи Выберите действие, которое нужно выполнить, когда сообщение соответствует фильтру + Состояние + Языки на выбор + Редактировать список + Главная и ленты + Добавить ключевое слово + Отобразить удалённый профиль + Домены + Личный комментарий + Вы уверены что хотите игнорировать тег %1$s? + Раздробить длинные сообщения в ответах? + Позволяет установить свои собственные цвета для тем оформления. + Динамические цвета + Частично запутать доменное имя в списке, если включён список ограничений домена + Теги Твиттера (через Nitter) + Ваш токен + Токен не может быть пустым! + Удалить хронологическую ленту + Выравнивать тонально с цветовой гаммой ваших личных обоев. + Настроить свои цвета + Заблокированные домены + Позволяет сократить список языков при составлении сообщения. + Больше настроек + Повысить + Добавить предупреждение о содержимом + Закрыть новую панель вложения + Открыть новую панель вложения + Прикрепить аудио + Прикрепить видео + Прикрепить файлы + Добавить опрос + Используйте токен + Приложение не смогло аутентифицировать профиль! + Ключевое слово или фраза + У вас нет заблокированных доменов + Разблокировать домен + Удалить прикреплённую хронологическую ленту? + Не разделять + Разделять сообщение + Игнорировать для домашней ленты + Начался разговор на вашем сервере! + Подчеркните кликабельные элементы + %d полученных сообщений + %d новых сообщений + %d обновленных сообщений + Название + Информация о сервере + Не интересует + Вы уверены что хотите разблокировать %1$s? + Публичный комментарий + Изменения были сохранены! + Светлая тема - Свои цвета + Пожалуйста, попробуйте ещё раз позже. + Кеш домашней ленты + Сообщение не будет отправлено если отсутствует описание для медиа + Отсутствуют описания медиа + Обрезать ссылки + Новая регистрация (модераторы) + Разделять длинные сообщения в ответах + Если включено, то элементы в ленте будут иметь тень и приподняты. + Настроить светлую тему оформления + Не игнорировать тег + Формат сообщения + Отключить примечания к выпуску + Получать домашнюю ленту каждые + Получение сообщений + %d частота (минуты) + Медиафайлы не могут быть загружены! + Зарегистрировался + Сеть + Прокомментируйте почему этот домен ограничен для публичного использования, если список одобренных доменов включён. + отправил жалобу + Приподнятые карточки + Приложение не обнаружило удалённое сообщение. + Цитировать + Убрать предупреждение о содержимом + Опубликовать + Медиа будут принимать всю ширину экрана, и соотношение сторон для высоты будет соблюдаться. + Установить язык + Прикрепить изображения + Избранное + Изменить видимость + Речь идет только о «публичных» ответах. Если включено, то ваши ответы будут автоматически иметь видимость «не в ленте» вместо «публичные» + Политика конфиденциальности + Настроить цвета + Тёмная тема - Свои цвета + Отображать удалённый разговор + Составить + Автоматически получать сообщения из домашней ленты + Выберите режим для темы + Изменил сообщение + Действие фильтра + Вы уверены что хотите открепить хронологическую ленту? + Формула + Фильтр языков + Математический формат + Переводить на + Написать формулу + Удалить ключевое слово + Всё равно показать + Следить за обсуждаемым на этом сервере + Жалоба отправлена + Только предупреждать + Читает вас + Загрузить медиа удалённо + Прикрепить тег + Подключение к Интернету отсутствует! + Отфильтровано: %1$s + Полноэкранное медиа + В читаемых у: + Позволяет настраивать некоторые элементы в сообщениях для тёмной темы. + Настроить тёмную тему оформления + Позволяет настраивать некоторые элементы в сообщениях для светлой темы. + Установить формат сообщения + Значки для дополнительных возможностей + Токен двухэтапной аутентификации + Удалить прикреплённые хронологические ленты? + Оставить уведомления + Прокомментируйте почему этот домен ограничен для внутреннего использования модераторами. + Игнорировать тег + Открепить тег + Не следить за тегом + Не игнорировать для домашней + Игнорировать их все + Управление профилями + Приложение не может найти удаленные данные! + Новый отчёт (модераторы) + Открыть из другой учётной записи + Припрятывать кнопку Составить diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 3f0365b9ec3ec6b4b37aef75e05042297b968f10..e979ca433f4ea5d9822d9ef1b6d15ec935f26d2e 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -1088,4 +1088,26 @@ Теги Twitter (через Ritter) Повноекранний медіафайл Медіафайли займатимуть всю ширину екрана, а співвідношення сторін по висоті буде дотримано. + Підвищення + Вилучити попередження про вміст + Встановити мову + Відкрити нову панель вкладень + Додайте зображення + Прикріпіть аудіо + Прикріпіть відео + Змінити видимість + Опублікувати + Цитата + Закрийте нову панель вкладень + Прикріпити файли + Додайте попередження про вміст + Додайте опитування + Більше варіантів + Улюблений + Мережа + Інформація про примірник + Автоматично приховати кнопку створення + Автоматично приховувати кнопку створення під час прокручування вгору на часовій шкалі + Під час відповіді на посилення особа, яка здійснила посилення, буде згадана у відповіді + Згадайте бустер diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 85512e9e0f4b4b0936944412fdb9febdff5c2a32..ce664e62e8da7f6f95c7f4be388bfb41f61dda4f 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -1076,4 +1076,26 @@ Twitter 标签(通过 Nitter) 全屏媒体 媒体文件会占据整个屏幕宽度,会保持高度的长宽比。 + 更多选项 + 喜欢 + 转发 + 引用 + 移除内容警告 + 设置语言 + 发布 + 打开新附件面板 + 附加图像 + 附加音频 + 附加视频 + 发起投票 + 添加内容警告 + 关闭新附件面板 + 修改可见性 + 实例信息 + 附加文件 + 网络 + 向上滚动时间轴时自动隐藏编辑按钮 + 自动隐藏编辑按钮 + 回复转嘟时,会在回复中提及转发嘟文的人 + 提及转发嘟文者 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 36f21d294b70166071b925456a356e4ccceeb5a4..2766016da7b4948e3137d19c4868e445246c4a31 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -57,6 +57,7 @@ Show direct messages Open in browser Translate + More options Home Local timeline @@ -77,6 +78,9 @@ Add this message to your favourites? Remove this message from your favourites? Boost this message? + Favorite + Boost + Quote Warn if message has no media description before boosting This message has missing media description. Are you sure to boost it? Unboost this message? @@ -110,6 +114,21 @@ Remove bookmark Status has been added to bookmarks! Status was removed from bookmarks! + + + Add content warning + Remove content warning + Change visibility + Set language + Publish + Open new attachment panel + Close new attachment panel + Attach images + Attach audio + Attach videos + Attach files + Add a poll + %d s %d m @@ -350,6 +369,7 @@ Peertube instance Use Emoji One Information + Instance information Display previews in all messages The account id has been copied in the clipboard! Change the language @@ -608,6 +628,7 @@ Export the theme Tap here to export the current theme An error occurred when selecting the theme file + Network User count Status count Instance count @@ -1111,6 +1132,7 @@ SET_UNFOLLOW_VALIDATION SET_USE_SINGLE_TOPBAR SET_DISABLE_TOPBAR_SCROLLING + SET_AUTO_HIDE_COMPOSE SET_DISPLAY_COUNTERS SET_DISPLAY_COMPACT_ACTION_BUTTON @@ -1126,7 +1148,7 @@ SET_DISABLE_ANIMATED_EMOJI SET_CAPITALIZE SET_MENTIONS_AT_TOP - + SET_MENTION_BOOSTER SET_THREAD_MESSAGE SET_THEME_BASE SET_DYNAMICCOLOR @@ -1773,10 +1795,13 @@ pref_category_key_pixelfed pref_category_key_home_cache pref_category_theming + pref_category_network pref_category_administration pref_category_languages pref_category_key_extra_features + pref_key_proxy + pref_export_settings pref_import_settings Export settings @@ -1786,6 +1811,8 @@ Push distributor Single action bar Disable top bar scrolling + Auto hide compose button + Automatically hide compose button when scrolling up on a timeline When enabled, the app will only have a single bar for timelines Timelines in a list When enabled, all pinned timelines will be displayed in a drop-down menu @@ -2033,6 +2060,8 @@ Mentions at the top When replying mentions will all be added to the beginning of the message + Mention the booster + When replying to a boost, the person who boosted will be mentioned in the reply Number of media Number of replies diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index e69af45c371f7332ec9e06e29ad0a172669c63cb..889e6d3ef8dd1aedbf28b224c22e47cac8d370ab 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -273,5 +273,8 @@ 0dp + diff --git a/app/src/main/res/xml/pref_categories.xml b/app/src/main/res/xml/pref_categories.xml index cd88e770e45294d5ce695564a0aa1d9d8423f480..70dc57807f4e278052cdc37289af1a5fd2771b30 100644 --- a/app/src/main/res/xml/pref_categories.xml +++ b/app/src/main/res/xml/pref_categories.xml @@ -67,6 +67,13 @@ app:icon="@drawable/ic_theming" app:key="@string/pref_category_key_theming" /> + +