Merge remote-tracking branch 'megalodon_main/main'

# Conflicts:
#	README.md
#	mastodon/build.gradle
#	mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java
#	mastodon/src/main/java/org/joinmastodon/android/PushNotificationReceiver.java
#	mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSessionManager.java
#	mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java
#	mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java
#	mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java
#	mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java
#	mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverAccountsFragment.java
#	mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverNewsFragment.java
#	mastodon/src/main/java/org/joinmastodon/android/fragments/discover/TrendingHashtagsFragment.java
#	mastodon/src/main/java/org/joinmastodon/android/model/NotificationAction.java
#	mastodon/src/main/java/org/joinmastodon/android/model/Status.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ExtendedFooterStatusDisplayItem.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ReblogOrReplyLineStatusDisplayItem.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/text/LinkSpan.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/utils/MediaAttachmentViewController.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java
#	mastodon/src/main/res/layout/display_item_gifv.xml
#	mastodon/src/main/res/layout/display_item_photo.xml
#	mastodon/src/main/res/layout/display_item_video.xml
#	mastodon/src/main/res/menu/post.xml
#	mastodon/src/main/res/menu/profile.xml
#	mastodon/src/main/res/values-es-rES/strings_sk.xml
#	mastodon/src/main/res/values-fr-rFR/strings_sk.xml
#	mastodon/src/main/res/values-gl-rES/strings_sk.xml
#	mastodon/src/main/res/values-in-rID/strings_sk.xml
#	mastodon/src/main/res/values-pl-rPL/strings_sk.xml
#	mastodon/src/main/res/values-uk-rUA/strings_sk.xml
#	metadata/it-IT/full_description.txt
#	metadata/zh-Hans/short_description.txt
This commit is contained in:
LucasGGamerM
2023-03-21 20:25:51 -03:00
63 changed files with 904 additions and 273 deletions

View File

@@ -29,6 +29,8 @@ import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.NotificationAction;
import org.joinmastodon.android.model.Preferences;
import org.joinmastodon.android.model.PushNotification;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.StatusPrivacy;
import org.joinmastodon.android.model.StatusPrivacy;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.parceler.Parcels;
@@ -56,6 +58,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent){
UiUtils.setUserPreferredTheme(context);
if(BuildConfig.DEBUG){
Log.e(TAG, "received: "+intent);
Bundle extras=intent.getExtras();

View File

@@ -16,6 +16,7 @@ import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.api.gson.IsoInstantTypeAdapter;
import org.joinmastodon.android.api.gson.IsoLocalDateTypeAdapter;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.model.Status;
import java.io.BufferedReader;
import java.io.IOException;
@@ -40,12 +41,15 @@ import okhttp3.ResponseBody;
public class MastodonAPIController{
private static final String TAG="MastodonAPIController";
public static final Gson gson=new GsonBuilder()
public static final Gson gsonWithoutDeserializer = new GsonBuilder()
.disableHtmlEscaping()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeAdapter(Instant.class, new IsoInstantTypeAdapter())
.registerTypeAdapter(LocalDate.class, new IsoLocalDateTypeAdapter())
.create();
public static final Gson gson = gsonWithoutDeserializer.newBuilder()
.registerTypeAdapter(Status.class, new Status.StatusDeserializer())
.create();
private static WorkerThread thread=new WorkerThread("MastodonAPIController");
private static OkHttpClient httpClient=new OkHttpClient.Builder().build();

View File

@@ -276,6 +276,15 @@ public class AccountSessionManager{
}
}
private void preferencesFromSource(AccountSession session, Account account) {
if (account != null && account.source != null && session.preferences != null) {
if (account.source.privacy != null)
session.preferences.postingDefaultVisibility = account.source.privacy;
if (account.source.language != null)
session.preferences.postingDefaultLanguage = account.source.language;
}
}
private void updateSessionLocalInfo(AccountSession session){
new GetOwnAccount()
.setCallback(new Callback<>(){
@@ -283,19 +292,12 @@ public class AccountSessionManager{
public void onSuccess(Account result){
session.self=result;
session.infoLastUpdated=System.currentTimeMillis();
if(session.preferences != null && session.preferences.postingDefaultVisibility != null){
session.preferences.postingDefaultVisibility = result.source.privacy;
}
if(session.preferences != null && session.preferences.postingDefaultLanguage != null){
session.preferences.postingDefaultLanguage = result.source.language;
}
preferencesFromSource(session, result);
writeAccountsFile();
}
@Override
public void onError(ErrorResponse error){
}
public void onError(ErrorResponse error){}
})
.exec(session.getID());
}
@@ -305,16 +307,13 @@ public class AccountSessionManager{
@Override
public void onSuccess(Preferences preferences) {
session.preferences=preferences;
preferencesFromSource(session, session.self);
}
@Override
public void onError(ErrorResponse error) {
Preferences preferences = new Preferences();
if(session.self != null){
preferences.postingDefaultVisibility = session.self.source.privacy;
preferences.postingDefaultLanguage = session.self.source.language;
}
session.preferences = preferences;
session.preferences = new Preferences();
preferencesFromSource(session, session.self);
}
}).exec(session.getID());
}

View File

@@ -66,7 +66,6 @@ import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
@@ -537,7 +536,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
holder.getItem().status.textExpandable = expandable;
HeaderStatusDisplayItem.Holder header = findHolderOfType(holder.getItemID(), HeaderStatusDisplayItem.Holder.class);
if (header != null) header.rebind();
holder.rebind();
}
}
@@ -714,6 +712,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
return attachmentViewsPool;
}
protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter<BindableViewHolder<StatusDisplayItem>> implements ImageLoaderRecyclerAdapter{
public DisplayItemsAdapter(){

View File

@@ -359,7 +359,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
scheduleTimeBtn=view.findViewById(R.id.scheduled_time_btn);
sensitiveIcon=view.findViewById(R.id.sensitive_icon);
sensitiveItem=view.findViewById(R.id.sensitive_item);
replyText=view.findViewById(R.id.reply_text);
replyText=view.findViewById(GlobalUserPreferences.replyLineAboveHeader ? R.id.reply_text : R.id.reply_text_below);
view.findViewById(GlobalUserPreferences.replyLineAboveHeader ? R.id.reply_text_below : R.id.reply_text)
.setVisibility(View.GONE);
if (isPhotoPickerAvailable()) {
PopupMenu attachPopup = new PopupMenu(getContext(), mediaBtn);
@@ -695,8 +697,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
moreBtn.setImageDrawable(visibilityIcon);
moreBtn.setBackground(null);
TextView timestamp = view.findViewById(R.id.timestamp);
if (status.editedAt==null) timestamp.setText(UiUtils.formatRelativeTimestamp(getContext(), status.createdAt));
else timestamp.setText(getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(getContext(), status.editedAt)));
if (status.editedAt!=null) timestamp.setText(getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(getContext(), status.editedAt)));
else if (status.createdAt!=null) timestamp.setText(UiUtils.formatRelativeTimestamp(getContext(), status.createdAt));
else timestamp.setText("");
if (status.spoilerText != null && !status.spoilerText.isBlank()) {
view.findViewById(R.id.spoiler_header).setVisibility(View.VISIBLE);
((TextView) view.findViewById(R.id.spoiler_title_inline)).setText(status.spoilerText);
@@ -1179,7 +1182,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
}else{
E.post(new StatusUpdatedEvent(result));
}
Nav.finish(ComposeFragment.this);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !isStateSaved()) {
Nav.finish(ComposeFragment.this);
}
if (getArguments().getBoolean("navigateToStatus", false)) {
Bundle args=new Bundle();
args.putString("account", accountID);

View File

@@ -50,12 +50,11 @@ import java.util.Map;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.UsableRecyclerView;
public class EditTimelinesFragment extends BaseRecyclerFragment<TimelineDefinition> implements ScrollableToTop {
public class EditTimelinesFragment extends RecyclerFragment<TimelineDefinition> implements ScrollableToTop {
private String accountID;
private TimelinesAdapter adapter;
private final ItemTouchHelper itemTouchHelper;

View File

@@ -38,7 +38,6 @@ import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.api.SimpleCallback;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
@@ -47,7 +46,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.UsableRecyclerView;
public class FollowRequestsListFragment extends BaseRecyclerFragment<FollowRequestsListFragment.AccountWrapper> implements ScrollableToTop{
public class FollowRequestsListFragment extends RecyclerFragment<FollowRequestsListFragment.AccountWrapper> implements ScrollableToTop{
private String accountID;
private Map<String, Relationship> relationships=Collections.emptyMap();
private GetAccountRelationships relationshipsRequest;

View File

@@ -16,11 +16,10 @@ import org.joinmastodon.android.ui.DividerItemDecoration;
import org.joinmastodon.android.ui.utils.UiUtils;
import me.grishka.appkit.api.SimpleCallback;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.views.UsableRecyclerView;
public class FollowedHashtagsFragment extends BaseRecyclerFragment<Hashtag> implements ScrollableToTop {
public class FollowedHashtagsFragment extends RecyclerFragment<Hashtag> implements ScrollableToTop {
private String nextMaxID;
private String accountId;

View File

@@ -24,6 +24,7 @@ import org.joinmastodon.android.DomainManager;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.MainActivity;
import org.joinmastodon.android.E;
import org.joinmastodon.android.E;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.notifications.GetNotifications;
import org.joinmastodon.android.api.session.AccountSession;
@@ -407,4 +408,46 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
public void onAllNotificationsSeen(AllNotificationsSeenEvent allNotificationsSeenEvent) {
setNotificationBadge(false);
}
public void updateNotificationBadge() {
AccountSession session = AccountSessionManager.getInstance().getAccount(accountID);
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
new GetNotifications(null, 1, EnumSet.allOf(Notification.Type.class), instance.pleroma != null)
.setCallback(new Callback<>() {
@Override
public void onSuccess(List<Notification> notifications) {
if (notifications.size() > 0) {
try {
long newestId = Long.parseLong(notifications.get(0).id);
long lastSeenId = Long.parseLong(session.markers.notifications.lastReadId);
setNotificationBadge(newestId > lastSeenId);
} catch (Exception ignored) {
setNotificationBadge(false);
}
}
}
@Override
public void onError(ErrorResponse error) {
setNotificationBadge(false);
}
}).exec(accountID);
}
public void setNotificationBadge(boolean badge) {
notificationTabIcon.setImageResource(badge
? R.drawable.ic_fluent_alert_28_selector_badged
: R.drawable.ic_fluent_alert_28_selector);
}
@Subscribe
public void onNotificationReceived(NotificationReceivedEvent notificationReceivedEvent) {
if (notificationReceivedEvent.account.equals(accountID)) setNotificationBadge(true);
}
@Subscribe
public void onAllNotificationsSeen(AllNotificationsSeenEvent allNotificationsSeenEvent) {
setNotificationBadge(false);
}
}

View File

@@ -172,6 +172,10 @@ public class HomeTimelineFragment extends StatusListFragment {
}
})
.exec(accountID);
if (parent.getParentFragment() instanceof HomeFragment homeFragment) {
homeFragment.updateNotificationBadge();
}
}
@Override

View File

@@ -37,11 +37,10 @@ import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.api.SimpleCallback;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.views.UsableRecyclerView;
public class ListTimelinesFragment extends BaseRecyclerFragment<ListTimeline> implements ScrollableToTop {
public class ListTimelinesFragment extends RecyclerFragment<ListTimeline> implements ScrollableToTop {
private String accountId;
private String profileAccountId;
private final HashMap<String, Boolean> userInListBefore = new HashMap<>();

View File

@@ -241,7 +241,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
@Override
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
FrameLayout view=tabViews[viewType];
((ViewGroup)view.getParent()).removeView(view);
if (view.getParent() != null) ((ViewGroup)view.getParent()).removeView(view);
view.setVisibility(View.VISIBLE);
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return new SimpleViewHolder(view);

View File

@@ -21,7 +21,6 @@ import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.PaginatedResponse;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.AccountStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
@@ -41,7 +40,6 @@ import java.util.stream.Stream;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.SimpleCallback;
import me.grishka.appkit.utils.V;
public class NotificationsListFragment extends BaseStatusListFragment<Notification>{
private boolean onlyMentions;
@@ -126,6 +124,8 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
knownAccounts.put(s.account.id, s.account);
if(s.status!=null && !knownAccounts.containsKey(s.status.account.id))
knownAccounts.put(s.status.account.id, s.status.account);
if(s.status!=null && s.status.reblog!=null && !knownAccounts.containsKey(s.status.reblog.account.id))
knownAccounts.put(s.status.reblog.account.id, s.status.reblog.account);
}
@Override

View File

@@ -525,6 +525,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
toolbarTitleView.setTranslationY(titleTransY);
toolbarSubtitleView.setTranslationY(titleTransY);
}
RecyclerFragment.setRefreshLayoutColors(refreshLayout);
}
@Override
@@ -652,10 +653,12 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
fields.clear();
AccountField joined=new AccountField();
joined.parsedName=joined.name=getString(R.string.profile_joined);
joined.parsedValue=joined.value=DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(LocalDateTime.ofInstant(account.createdAt, ZoneId.systemDefault()));
fields.add(joined);
if (account.createdAt != null) {
AccountField joined=new AccountField();
joined.parsedName=joined.name=getString(R.string.profile_joined);
joined.parsedValue=joined.value=DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(LocalDateTime.ofInstant(account.createdAt, ZoneId.systemDefault()));
fields.add(joined);
}
for(AccountField field:account.fields){
field.parsedValue=ssb=HtmlParser.parse(field.value, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
@@ -1253,7 +1256,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
@Override
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
FrameLayout view=tabViews[viewType];
((ViewGroup)view.getParent()).removeView(view);
if (view.getParent() != null) ((ViewGroup)view.getParent()).removeView(view);
view.setVisibility(View.VISIBLE);
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return new SimpleViewHolder(view);

View File

@@ -0,0 +1,50 @@
package org.joinmastodon.android.fragments;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.joinmastodon.android.R;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
public abstract class RecyclerFragment<T> extends BaseRecyclerFragment<T> {
public RecyclerFragment(int perPage) {
super(perPage);
}
public RecyclerFragment(int layout, int perPage) {
super(layout, perPage);
}
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (refreshLayout != null) setRefreshLayoutColors(refreshLayout);
}
public static void setRefreshLayoutColors(SwipeRefreshLayout l) {
List<Integer> colors = new ArrayList<>(Arrays.asList(
R.color.primary_600,
R.color.red_primary_600,
R.color.green_primary_600,
R.color.blue_primary_600,
R.color.purple_600
));
int primary = UiUtils.getThemeColorRes(l.getContext(), R.attr.colorPrimary600);
if (!colors.contains(primary)) colors.add(0, primary);
int offset = colors.indexOf(primary);
int[] sorted = new int[colors.size()];
for (int i = 0; i < colors.size(); i++) {
sorted[i] = colors.get((i + offset) % colors.size());
}
l.setColorSchemeResources(sorted);
}
}

View File

@@ -81,7 +81,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
private ArrayList<Item> items=new ArrayList<>();
private ThemeItem themeItem;
private NotificationPolicyItem notificationPolicyItem;
private SwitchItem showNewPostsButtonItem, glitchModeItem;
private SwitchItem showNewPostsButtonItem, glitchModeItem, compactReblogReplyLineItem;
private String accountID;
private boolean needUpdateNotificationSettings;
private boolean needAppRestart;
@@ -296,12 +296,35 @@ public class SettingsFragment extends MastodonToolbarFragment{
GlobalUserPreferences.save();
needAppRestart=true;
}));
items.add(new SwitchItem(R.string.sk_settings_hide_fab, R.drawable.ic_fluent_edit_24_regular, GlobalUserPreferences.autoHideFab, i->{
GlobalUserPreferences.autoHideFab=i.checked;
GlobalUserPreferences.save();
needAppRestart=true;
}));
items.add(new SwitchItem(R.string.sk_reply_line_above_avatar, R.drawable.ic_fluent_arrow_reply_24_regular, GlobalUserPreferences.replyLineAboveHeader, i->{
GlobalUserPreferences.replyLineAboveHeader=i.checked;
GlobalUserPreferences.compactReblogReplyLine=i.checked;
compactReblogReplyLineItem.enabled=i.checked;
compactReblogReplyLineItem.checked= GlobalUserPreferences.replyLineAboveHeader;
if (list.findViewHolderForAdapterPosition(items.indexOf(compactReblogReplyLineItem)) instanceof SwitchViewHolder svh) svh.rebind();
GlobalUserPreferences.save();
needAppRestart=true;
}));
items.add(compactReblogReplyLineItem=new SwitchItem(R.string.sk_compact_reblog_reply_line, R.drawable.ic_fluent_re_order_24_regular, GlobalUserPreferences.compactReblogReplyLine, i->{
GlobalUserPreferences.compactReblogReplyLine=i.checked;
GlobalUserPreferences.save();
needAppRestart=true;
}));
compactReblogReplyLineItem.enabled=GlobalUserPreferences.replyLineAboveHeader;
// items.add(new SwitchItem(R.string.sk_settings_translate_only_opened, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
// GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
// GlobalUserPreferences.save();
// needAppRestart=true;
// }));
boolean translationAvailable = instance.v2 != null && instance.v2.configuration.translation != null && instance.v2.configuration.translation.enabled;
items.add(new SmallTextItem(getString(translationAvailable ?
R.string.sk_settings_translation_availability_note_available :
R.string.sk_settings_translation_availability_note_unavailable, instanceName)));
items.add(new HeaderItem(R.string.settings_notifications));
items.add(notificationPolicyItem=new NotificationPolicyItem());

View File

@@ -43,6 +43,8 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>
protected void addAccountToKnown(Status s){
if(!knownAccounts.containsKey(s.account.id))
knownAccounts.put(s.account.id, s.account);
if(s.reblog!=null && !knownAccounts.containsKey(s.reblog.account.id))
knownAccounts.put(s.reblog.account.id, s.reblog.account);
}
@Override

View File

@@ -25,6 +25,7 @@ import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.ListTimelinesFragment;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.fragments.RecyclerFragment;
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Relationship;
@@ -48,7 +49,6 @@ import me.grishka.appkit.Nav;
import me.grishka.appkit.api.APIRequest;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
@@ -57,7 +57,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.UsableRecyclerView;
public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseAccountListFragment.AccountItem>{
public abstract class BaseAccountListFragment extends RecyclerFragment<BaseAccountListFragment.AccountItem> {
protected HashMap<String, Relationship> relationships=new HashMap<>();
protected String accountID;
protected ArrayList<APIRequest<?>> relationshipsRequests=new ArrayList<>();
@@ -295,7 +295,6 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getShortUsername()));
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getShortUsername()));
menu.findItem(R.id.manage_user_lists).setTitle(getString(R.string.sk_lists_with_user, account.getShortUsername())).setVisible(relationship.following);
menu.findItem(R.id.soft_block).setVisible(relationship.followedBy && !relationship.following);
MenuItem hideBoosts=menu.findItem(R.id.hide_boosts);
MenuItem manageUserLists=menu.findItem(R.id.manage_user_lists);
@@ -309,7 +308,7 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
manageUserLists.setVisible(true);
}else{
hideBoosts.setVisible(false);
manageUserLists.setVisible(true);
manageUserLists.setVisible(false);
}
menu.findItem(R.id.block_domain).setVisible(false);

View File

@@ -18,6 +18,7 @@ import org.joinmastodon.android.api.requests.accounts.GetFollowSuggestions;
import org.joinmastodon.android.fragments.DomainDisplay;
import org.joinmastodon.android.fragments.IsOnTop;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.fragments.RecyclerFragment;
import org.joinmastodon.android.fragments.ScrollableToTop;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.FollowSuggestion;
@@ -41,7 +42,6 @@ import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.api.SimpleCallback;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;

View File

@@ -12,6 +12,7 @@ import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.trends.GetTrendingLinks;
import org.joinmastodon.android.fragments.DomainDisplay;
import org.joinmastodon.android.fragments.IsOnTop;
import org.joinmastodon.android.fragments.RecyclerFragment;
import org.joinmastodon.android.fragments.ScrollableToTop;
import org.joinmastodon.android.model.Card;
import org.joinmastodon.android.ui.DividerItemDecoration;
@@ -27,7 +28,6 @@ import java.util.stream.Collectors;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.api.SimpleCallback;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;

View File

@@ -9,6 +9,7 @@ import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.trends.GetTrendingHashtags;
import org.joinmastodon.android.fragments.DomainDisplay;
import org.joinmastodon.android.fragments.IsOnTop;
import org.joinmastodon.android.fragments.RecyclerFragment;
import org.joinmastodon.android.fragments.ScrollableToTop;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.ui.DividerItemDecoration;
@@ -21,7 +22,6 @@ import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.api.SimpleCallback;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.views.UsableRecyclerView;

View File

@@ -17,6 +17,7 @@ import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIController;
import org.joinmastodon.android.api.MastodonErrorResponse;
import org.joinmastodon.android.api.requests.instance.GetInstance;
import org.joinmastodon.android.fragments.RecyclerFragment;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.catalog.CatalogInstance;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
@@ -44,7 +45,6 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.utils.MergeRecyclerAdapter;
import me.grishka.appkit.utils.V;
@@ -52,7 +52,7 @@ import okhttp3.Call;
import okhttp3.Request;
import okhttp3.Response;
abstract class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstance>{
abstract class InstanceCatalogFragment extends RecyclerFragment<CatalogInstance> {
protected RecyclerView.Adapter adapter;
protected MergeRecyclerAdapter mergeAdapter;
protected CatalogInstance chosenInstance;
@@ -75,7 +75,7 @@ abstract class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInsta
private static final double DUNBAR=Math.log(800);
public InstanceCatalogFragment(int layout, int perPage){
super(layout, perPage);
super(layout, perPage);
}
@Override

View File

@@ -21,6 +21,7 @@ import org.joinmastodon.android.api.requests.accounts.GetFollowSuggestions;
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
import org.joinmastodon.android.fragments.HomeFragment;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.fragments.RecyclerFragment;
import org.joinmastodon.android.model.FollowSuggestion;
import org.joinmastodon.android.model.ParsedAccount;
import org.joinmastodon.android.model.Relationship;
@@ -43,7 +44,6 @@ import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.api.SimpleCallback;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
@@ -52,7 +52,7 @@ import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.FragmentRootLinearLayout;
import me.grishka.appkit.views.UsableRecyclerView;
public class OnboardingFollowSuggestionsFragment extends BaseRecyclerFragment<ParsedAccount>{
public class OnboardingFollowSuggestionsFragment extends RecyclerFragment<ParsedAccount> {
private String accountID;
private Map<String, Relationship> relationships=Collections.emptyMap();
private GetAccountRelationships relationshipsRequest;

View File

@@ -23,7 +23,6 @@ import org.joinmastodon.android.fragments.StatusListFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.PhotoLayoutHelper;
import org.joinmastodon.android.ui.displayitems.AudioStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.LinkCardStatusDisplayItem;

View File

@@ -43,7 +43,7 @@ public class Account extends BaseModel implements Searchable{
/**
* The profile's display name.
*/
@RequiredField
// @RequiredField
public String displayName;
/**
* The profile's bio / description.
@@ -86,7 +86,7 @@ public class Account extends BaseModel implements Searchable{
/**
* When the account was created.
*/
@RequiredField
// @RequiredField
public Instant createdAt;
/**
* When the most recent status was posted.

View File

@@ -22,7 +22,7 @@ public class Poll extends BaseModel{
public List<Integer> ownVotes;
@RequiredField
public List<Option> options;
@RequiredField
// @RequiredField
public List<Emoji> emojis;
public transient ArrayList<Option> selectedOptions;
@@ -30,6 +30,8 @@ public class Poll extends BaseModel{
@Override
public void postprocess() throws ObjectValidationException{
super.postprocess();
if (emojis == null) emojis = List.of();
if (ownVotes == null) ownVotes = List.of();
for(Emoji e:emojis)
e.postprocess();
}

View File

@@ -1,5 +1,14 @@
package org.joinmastodon.android.model;
import static org.joinmastodon.android.api.MastodonAPIController.gson;
import static org.joinmastodon.android.api.MastodonAPIController.gsonWithoutDeserializer;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.api.ObjectValidationException;
import org.joinmastodon.android.api.RequiredField;
@@ -7,6 +16,7 @@ import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.parceler.Parcel;
import java.lang.reflect.Type;
import java.time.Instant;
import java.util.List;
@@ -16,7 +26,7 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
public String id;
@RequiredField
public String uri;
@RequiredField
// @RequiredField // sometimes null on calckey
public Instant createdAt;
@RequiredField
public Account account;
@@ -58,12 +68,14 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
public boolean bookmarked;
public boolean pinned;
public Status quote;
public Status quote; // can be boolean in calckey
public transient boolean filterRevealed;
public transient boolean spoilerRevealed;
public transient boolean textExpanded, textExpandable;
public transient boolean hasGapAfter;
public transient TranslatedStatus translation;
public transient boolean translationShown;
public boolean reloadWhenClicked;
private transient String strippedText;
@@ -174,4 +186,28 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
public String getQuery() {
return url;
}
public static class StatusDeserializer implements JsonDeserializer<Status> {
@Override
public Status deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject obj = json.getAsJsonObject();
Status quote = null;
if (obj.has("quote") && obj.get("quote").isJsonObject())
quote = gson.fromJson(obj.get("quote"), Status.class);
obj.remove("quote");
Status reblog = null;
if (obj.has("reblog"))
reblog = gson.fromJson(obj.get("reblog"), Status.class);
obj.remove("reblog");
Status status = gsonWithoutDeserializer.fromJson(json, Status.class);
status.quote = quote;
status.reblog = reblog;
return status;
}
}
}

View File

@@ -79,10 +79,10 @@ public class ExtendedFooterStatusDisplayItem extends StatusDisplayItem{
}else{
editHistory.setVisibility(View.GONE);
}
String timeStr=TIME_FORMATTER.format(item.status.createdAt.atZone(ZoneId.systemDefault()));
String timeStr=item.status.createdAt != null ? TIME_FORMATTER.format(item.status.createdAt.atZone(ZoneId.systemDefault())) : null;
if (item.status.application!=null && !TextUtils.isEmpty(item.status.application.name)) {
time.setText(item.parentFragment.getString(R.string.timestamp_via_app, timeStr, ""));
time.setText(timeStr != null ? item.parentFragment.getString(R.string.timestamp_via_app, timeStr, "") : "");
applicationName.setText(item.status.application.name);
if (item.status.application.website != null && item.status.application.website.toLowerCase().startsWith("https://")) {
applicationName.setOnClickListener(e -> UiUtils.openURL(context, null, item.status.application.website));

View File

@@ -1,8 +1,12 @@
package org.joinmastodon.android.ui.displayitems;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -270,7 +274,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
progress.dismiss();
}, rel->{
relationship=rel;
Toast.makeText(activity, activity.getString(rel.following ? R.string.followed_user : R.string.unfollowed_user, account.getShortUsername()), Toast.LENGTH_SHORT).show();
Toast.makeText(activity, activity.getString(rel.following ? R.string.followed_user : rel.requested ? R.string.following_user_requested : R.string.unfollowed_user, account.getDisplayUsername()), Toast.LENGTH_SHORT).show();
});
}else if(id==R.id.block_domain){
UiUtils.confirmToggleBlockDomain(activity, item.parentFragment.getAccountID(), account.getDomain(), relationship!=null && relationship.domainBlocking, ()->{});
@@ -497,6 +501,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
}
Account account=item.user;
String username = account.getShortUsername();
boolean isOwnPost=AccountSessionManager.getInstance().isSelf(item.parentFragment.getAccountID(), account);
boolean isPostScheduled=item.scheduledStatus!=null;
menu.findItem(R.id.open_with_account).setVisible(!isPostScheduled && hasMultipleAccounts);
@@ -532,14 +537,15 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
manageUserLists.setVisible(false);
}else{
mute.setVisible(true);
block.setVisible(true);
// hiding when following to keep menu item count equal (trading it for user lists)
block.setVisible(relationship == null || !relationship.following);
report.setVisible(true);
follow.setVisible(relationship==null || relationship.following || (!relationship.blocking && !relationship.blockedBy && !relationship.domainBlocking && !relationship.muting));
mute.setTitle(item.parentFragment.getString(relationship!=null && relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getShortUsername()));
mute.setTitle(item.parentFragment.getString(relationship!=null && relationship.muting ? R.string.unmute_user : R.string.mute_user, username));
mute.setIcon(relationship!=null && relationship.muting ? R.drawable.ic_fluent_speaker_0_24_regular : R.drawable.ic_fluent_speaker_off_24_regular);
UiUtils.insetPopupMenuIcon(item.parentFragment.getContext(), mute);
block.setTitle(item.parentFragment.getString(relationship!=null && relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getShortUsername()));
report.setTitle(item.parentFragment.getString(R.string.report_user, account.getShortUsername()));
block.setTitle(item.parentFragment.getString(relationship!=null && relationship.blocking ? R.string.unblock_user : R.string.block_user, username));
report.setTitle(item.parentFragment.getString(R.string.report_user, username));
// disabled in megalodon. domain blocks from a post clutters the context menu and looks out of place
// if(!account.isLocal()){
// blockDomain.setVisible(true);
@@ -548,12 +554,53 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
blockDomain.setVisible(false);
// }
boolean following = relationship!=null && relationship.following;
follow.setTitle(item.parentFragment.getString(following ? R.string.unfollow_user : R.string.follow_user, account.getShortUsername()));
follow.setTitle(item.parentFragment.getString(following ? R.string.unfollow_user : R.string.follow_user, username));
follow.setIcon(following ? R.drawable.ic_fluent_person_delete_24_regular : R.drawable.ic_fluent_person_add_24_regular);
manageUserLists.setVisible(relationship != null && relationship.following);
manageUserLists.setTitle(item.parentFragment.getString(R.string.sk_lists_with_user, account.getShortUsername()));
manageUserLists.setTitle(item.parentFragment.getString(R.string.sk_lists_with_user, username));
UiUtils.insetPopupMenuIcon(item.parentFragment.getContext(), follow);
}
workaroundChangingMenuItemWidths(menu, username);
}
// ugliest piece of code you'll see in a while: i measure the menu items' text widths to
// determine the biggest one, because it's probably not being displayed at first
// (before the relationship loaded). i take the largest one's size and add a space to the
// last item ("open in browser") until it takes up as much space as the largest item.
// goal: no more ugly ellipsis after the relationship loads in when opening the context menu
// of a post
private void workaroundChangingMenuItemWidths(Menu menu, String username) {
String openInBrowserText = item.parentFragment.getString(R.string.open_in_browser);
if (relationship == null) {
float largestWidth = 0;
Paint paint = new Paint();
paint.setTypeface(Typeface.create("sans-serif", Typeface.NORMAL));
String[] otherStrings = new String[] {
item.parentFragment.getString(R.string.unfollow_user, username),
item.parentFragment.getString(R.string.unblock_user, username),
item.parentFragment.getString(R.string.unmute_user, username),
item.parentFragment.getString(R.string.sk_lists_with_user, username),
};
for (int i = 0; i < menu.size(); i++) {
MenuItem item = menu.getItem(i);
if (item.getItemId() == R.id.open_in_browser || !item.isVisible()) continue;
float width = paint.measureText(menu.getItem(i).getTitle().toString());
if (width > largestWidth) largestWidth = width;
}
for (String str : otherStrings) {
float width = paint.measureText(str);
if (width > largestWidth) largestWidth = width;
}
float textWidth = paint.measureText(openInBrowserText);
float missingWidth = Math.max(0, largestWidth - textWidth);
float singleSpaceWidth = paint.measureText("");
int howManySpaces = (int) Math.ceil(missingWidth / singleSpaceWidth);
String enlargedText = openInBrowserText + "".repeat(howManySpaces);
menu.findItem(R.id.open_in_browser).setTitle(enlargedText);
} else {
menu.findItem(R.id.open_in_browser).setTitle(openInBrowserText);
}
}
}
}

View File

@@ -1,18 +1,23 @@
package org.joinmastodon.android.ui.displayitems;
import static org.joinmastodon.android.ui.utils.MediaAttachmentViewController.altWrapPadding;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Attachment;
@@ -85,9 +90,10 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
private final FrameLayout altTextWrapper;
private final TextView altTextButton;
private final ImageView noAltTextButton;
private final View altTextScroller;
private final ImageButton altTextClose;
private final TextView altText;
private final TextView altText, noAltText;
private int altTextIndex=-1;
private Animator altTextAnimator;
@@ -101,9 +107,11 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
activity.getLayoutInflater().inflate(R.layout.overlay_image_alt_text, wrapper);
altTextWrapper=findViewById(R.id.alt_text_wrapper);
altTextButton=findViewById(R.id.alt_button);
noAltTextButton=findViewById(R.id.no_alt_button);
altTextScroller=findViewById(R.id.alt_text_scroller);
altTextClose=findViewById(R.id.alt_text_close);
altText=findViewById(R.id.alt_text);
noAltText=findViewById(R.id.no_alt_text);
altTextClose.setOnClickListener(this::onAltTextCloseClick);
}
@@ -133,15 +141,17 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
layout.addView(c.view);
c.view.setOnClickListener(clickListener);
c.view.setTag(i);
if(c.altButton!=null){
c.altButton.setOnClickListener(altTextClickListener);
c.altButton.setTag(i);
c.altButton.setAlpha(1f);
if(c.btnsWrap!=null){
c.btnsWrap.setOnClickListener(altTextClickListener);
c.btnsWrap.setTag(i);
c.btnsWrap.setAlpha(1f);
}
controllers.add(c);
c.bind(att, item.status);
i++;
}
altTextButton.setVisibility(View.VISIBLE);
noAltTextButton.setVisibility(View.VISIBLE);
altTextWrapper.setVisibility(View.GONE);
altTextIndex=-1;
}
@@ -172,8 +182,14 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
int index=(Integer)v.getTag();
altTextIndex=index;
Attachment att=item.attachments.get(index);
boolean hasAltText = !TextUtils.isEmpty(att.description);
altTextButton.setVisibility(hasAltText && GlobalUserPreferences.showAltIndicator ? View.VISIBLE : View.GONE);
noAltTextButton.setVisibility(!hasAltText && GlobalUserPreferences.showNoAltIndicator ? View.VISIBLE : View.GONE);
altText.setVisibility(hasAltText && GlobalUserPreferences.showAltIndicator ? View.VISIBLE : View.GONE);
noAltText.setVisibility(!hasAltText && GlobalUserPreferences.showNoAltIndicator ? View.VISIBLE : View.GONE);
altText.setText(att.description);
altTextWrapper.setVisibility(View.VISIBLE);
altTextWrapper.setBackgroundResource(hasAltText ? R.drawable.bg_image_alt_overlay : R.drawable.bg_image_no_alt_overlay);
altTextWrapper.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
@Override
public boolean onPreDraw(){
@@ -188,18 +204,19 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
ArrayList<Animator> anims=new ArrayList<>();
anims.add(ObjectAnimator.ofFloat(altTextButton, View.ALPHA, 1, 0));
anims.add(ObjectAnimator.ofFloat(noAltTextButton, View.ALPHA, 1, 0));
anims.add(ObjectAnimator.ofFloat(altTextScroller, View.ALPHA, 0, 1));
anims.add(ObjectAnimator.ofFloat(altTextClose, View.ALPHA, 0, 1));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "left", btnL, altTextWrapper.getLeft()));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "top", btnT, altTextWrapper.getTop()));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "right", btnL+v.getWidth(), altTextWrapper.getRight()));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "bottom", btnT+v.getHeight(), altTextWrapper.getBottom()));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "left", btnL+altWrapPadding[0], altTextWrapper.getLeft()));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "top", btnT+altWrapPadding[1], altTextWrapper.getTop()));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "right", btnL+v.getWidth()-altWrapPadding[2], altTextWrapper.getRight()));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "bottom", btnT+v.getHeight()-altWrapPadding[3], altTextWrapper.getBottom()));
for(Animator a:anims)
a.setDuration(300);
for(MediaAttachmentViewController c:controllers){
if(c.altButton!=null && c.altButton!=v){
anims.add(ObjectAnimator.ofFloat(c.altButton, View.ALPHA, 1, 0).setDuration(150));
if(c.btnsWrap!=null && c.btnsWrap!=v){
anims.add(ObjectAnimator.ofFloat(c.btnsWrap, View.ALPHA, 1, 0).setDuration(150));
}
}
@@ -211,8 +228,8 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
public void onAnimationEnd(Animator animation){
altTextAnimator=null;
for(MediaAttachmentViewController c:controllers){
if(c.altButton!=null){
c.altButton.setVisibility(View.INVISIBLE);
if(c.btnsWrap!=null){
c.btnsWrap.setVisibility(View.INVISIBLE);
}
}
}
@@ -229,10 +246,11 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
if(altTextAnimator!=null)
altTextAnimator.cancel();
View btn=controllers.get(altTextIndex).altButton;
View btn=controllers.get(altTextIndex).btnsWrap;
for(MediaAttachmentViewController c:controllers){
if(c.altButton!=null && c.altButton!=btn)
c.altButton.setVisibility(View.VISIBLE);
if(c.btnsWrap!=null && c.btnsWrap!=btn) {
c.btnsWrap.setVisibility(View.VISIBLE);
}
}
int[] loc={0, 0};
@@ -244,19 +262,20 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
ArrayList<Animator> anims=new ArrayList<>();
anims.add(ObjectAnimator.ofFloat(altTextButton, View.ALPHA, 1));
anims.add(ObjectAnimator.ofFloat(noAltTextButton, View.ALPHA, 1));
anims.add(ObjectAnimator.ofFloat(altTextScroller, View.ALPHA, 0));
anims.add(ObjectAnimator.ofFloat(altTextClose, View.ALPHA, 0));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "left", btnL));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "top", btnT));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "right", btnL+btn.getWidth()));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "bottom", btnT+btn.getHeight()));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "left", btnL+altWrapPadding[0]));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "top", btnT+altWrapPadding[1]));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "right", btnL+btn.getWidth()-altWrapPadding[2]));
anims.add(ObjectAnimator.ofInt(altTextWrapper, "bottom", btnT+btn.getHeight()-altWrapPadding[3]));
for(Animator a:anims)
a.setDuration(300);
for(MediaAttachmentViewController c:controllers){
if(c.altButton!=null && c.altButton!=btn){
anims.add(ObjectAnimator.ofFloat(c.altButton, View.ALPHA, 1).setDuration(150));
}
// if(c.btnsWrap!=null && c.btnsWrap!=btn){
anims.add(ObjectAnimator.ofFloat(c.btnsWrap, View.ALPHA, 1).setDuration(150));
// }
}
AnimatorSet set=new AnimatorSet();

View File

@@ -10,8 +10,10 @@ import android.text.SpannableStringBuilder;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Emoji;
@@ -27,6 +29,7 @@ import androidx.annotation.Nullable;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.utils.V;
public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
private CharSequence text;
@@ -35,10 +38,17 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
private StatusPrivacy visibility;
@DrawableRes
private int iconEnd;
private CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
private CustomEmojiHelper emojiHelper=new CustomEmojiHelper(), fullTextEmojiHelper;
private View.OnClickListener handleClick;
boolean belowHeader, needBottomPadding;
ReblogOrReplyLineStatusDisplayItem extra;
CharSequence fullText;
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon, StatusPrivacy visibility, @Nullable View.OnClickListener handleClick){
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon, StatusPrivacy visibility, @Nullable View.OnClickListener handleClick) {
this(parentID, parentFragment, text, emojis, icon, visibility, handleClick, text);
}
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon, StatusPrivacy visibility, @Nullable View.OnClickListener handleClick, CharSequence fullText) {
super(parentID, parentFragment);
SpannableStringBuilder ssb=new SpannableStringBuilder(text);
HtmlParser.parseCustomEmoji(ssb, emojis);
@@ -49,6 +59,15 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
updateVisibility(visibility);
if (fullText != null) {
fullTextEmojiHelper = new CustomEmojiHelper();
SpannableStringBuilder fullTextSsb = new SpannableStringBuilder(fullText);
HtmlParser.parseCustomEmoji(fullTextSsb, emojis);
this.fullText=fullTextSsb;
fullTextEmojiHelper.setText(fullTextSsb);
}
}
public void updateVisibility(StatusPrivacy visibility) {
@@ -77,30 +96,77 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
}
public static class Holder extends StatusDisplayItem.Holder<ReblogOrReplyLineStatusDisplayItem> implements ImageLoaderViewHolder{
private final TextView text;
private final TextView text, extraText;
private final View separator;
private final ViewGroup parent;
public Holder(Activity activity, ViewGroup parent){
super(activity, R.layout.display_item_reblog_or_reply_line, parent);
this.parent = parent;
text=findViewById(R.id.text);
extraText=findViewById(R.id.extra_text);
separator=findViewById(R.id.separator);
if (GlobalUserPreferences.replyLineAboveHeader && GlobalUserPreferences.compactReblogReplyLine) {
parent.addOnLayoutChangeListener((v, l, t, right, b, ol, ot, oldRight, ob) -> {
if (right != oldRight) layoutLine();
});
}
}
@Override
public void onBind(ReblogOrReplyLineStatusDisplayItem item){
private void bindLine(ReblogOrReplyLineStatusDisplayItem item, TextView text) {
if (item.fullText != null) text.setContentDescription(item.fullText);
text.setText(item.text);
text.setCompoundDrawablesRelativeWithIntrinsicBounds(item.icon, 0, item.iconEnd, 0);
if(item.handleClick!=null) text.setOnClickListener(item.handleClick);
text.setEnabled(!item.inset);
text.setClickable(!item.inset);
text.setOnClickListener(item.handleClick);
text.setEnabled(!item.inset && item.handleClick != null);
text.setClickable(!item.inset && item.handleClick != null);
Context ctx = itemView.getContext();
int visibilityText = item.visibility != null ? switch (item.visibility) {
case PUBLIC -> R.string.visibility_public;
case UNLISTED -> R.string.sk_visibility_unlisted;
case PRIVATE -> R.string.visibility_followers_only;
case LOCAL -> R.string.sk_local_only;
default -> 0;
} : 0;
if (visibilityText != 0) text.setContentDescription(item.text + " (" + ctx.getString(visibilityText) + ")");
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N)
UiUtils.fixCompoundDrawableTintOnAndroid6(text);
text.setTextAppearance(item.belowHeader ? R.style.m3_label_large : R.style.m3_title_small);
text.setCompoundDrawableTintList(text.getTextColors());
}
@Override
public void onBind(ReblogOrReplyLineStatusDisplayItem item){
bindLine(item, text);
if (item.extra != null) bindLine(item.extra, extraText);
extraText.setVisibility(item.extra == null ? View.GONE : View.VISIBLE);
separator.setVisibility(item.extra == null ? View.GONE : View.VISIBLE);
ViewGroup.MarginLayoutParams params = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.bottomMargin = item.belowHeader ? V.dp(-6) : V.dp(-12);
params.topMargin = item.belowHeader ? V.dp(-6) : 0;
itemView.setLayoutParams(params);
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : 0);
layoutLine();
}
private void layoutLine() {
// layout line only if above header, compact and has extra
if (!GlobalUserPreferences.replyLineAboveHeader
|| !GlobalUserPreferences.compactReblogReplyLine
|| item.extra == null) return;
itemView.measure(
View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.UNSPECIFIED);
boolean isVertical = ((LinearLayout) itemView).getOrientation() == LinearLayout.VERTICAL;
extraText.setPaddingRelative(extraText.getPaddingStart(), item.extra != null && isVertical ? 0 : V.dp(16), extraText.getPaddingEnd(), extraText.getPaddingBottom());
separator.setVisibility(item.extra != null && !isVertical ? View.VISIBLE : View.GONE);
((LinearLayout) itemView).removeView(extraText);
if (isVertical) ((LinearLayout) itemView).addView(extraText);
else ((LinearLayout) itemView).addView(extraText, 0);
text.setText(isVertical ? item.fullText : item.text);
if (item.extra != null) {
extraText.setText(isVertical ? item.extra.fullText : item.extra.text);
}
}
@Override

View File

@@ -7,13 +7,13 @@ import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
import org.joinmastodon.android.fragments.HomeTabFragment;
import org.joinmastodon.android.fragments.HomeTimelineFragment;
import org.joinmastodon.android.fragments.ListTimelineFragment;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.fragments.ThreadFragment;
@@ -21,7 +21,6 @@ import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Attachment;
import org.joinmastodon.android.model.DisplayItemsParent;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.Poll;
import org.joinmastodon.android.model.ScheduledStatus;
@@ -32,10 +31,8 @@ import org.joinmastodon.android.utils.StatusFilterPredicate;
import org.parceler.Parcels;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -98,10 +95,6 @@ public abstract class StatusDisplayItem{
}
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment<?> fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean addFooter, Notification notification, boolean disableTranslate, Filter.FilterContext filterContext){
return buildItems(fragment, status, accountID, parentObject, knownAccounts, inset, addFooter, notification, disableTranslate, filterContext, null);
}
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment<?> fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean addFooter, Notification notification, boolean disableTranslate, Filter.FilterContext filterContext, StatusDisplayItem titleItem){
String parentID=parentObject.getID();
ArrayList<StatusDisplayItem> items=new ArrayList<>();
@@ -118,24 +111,37 @@ public abstract class StatusDisplayItem{
statusForContent.filterRevealed = filterPredicate.testWithWarning(status);
}
ReblogOrReplyLineStatusDisplayItem replyLine = null;
boolean threadReply = statusForContent.inReplyToAccountId != null &&
statusForContent.inReplyToAccountId.equals(statusForContent.account.id);
if(statusForContent.inReplyToAccountId!=null && !(threadReply && fragment instanceof ThreadFragment)){
Account account = knownAccounts.get(statusForContent.inReplyToAccountId);
String text = threadReply ? fragment.getString(R.string.sk_show_thread)
: account == null ? fragment.getString(R.string.sk_in_reply)
: GlobalUserPreferences.compactReblogReplyLine && status.reblog != null ? account.displayName
: fragment.getString(R.string.in_reply_to, account.displayName);
String fullText = threadReply ? fragment.getString(R.string.sk_show_thread)
: account == null ? fragment.getString(R.string.sk_in_reply)
: fragment.getString(R.string.in_reply_to, account.displayName);
replyLine = new ReblogOrReplyLineStatusDisplayItem(
parentID, fragment, text, account == null ? List.of() : account.emojis,
R.drawable.ic_fluent_arrow_reply_20_filled, null, null, fullText
);
}
if(status.reblog!=null){
boolean isOwnPost = AccountSessionManager.getInstance().isSelf(fragment.getAccountID(), status.account);
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.user_boosted, status.account.displayName), status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20_filled, isOwnPost ? status.visibility : null, i->{
String fullText = fragment.getString(R.string.user_boosted, status.account.displayName);
String text = GlobalUserPreferences.compactReblogReplyLine && replyLine != null ? status.account.displayName : fullText;
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, text, status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20_filled, isOwnPost ? status.visibility : null, i->{
args.putParcelable("profileAccount", Parcels.wrap(status.account));
Nav.go(fragment.getActivity(), ProfileFragment.class, args);
}));
}else if(status.inReplyToAccountId!=null && knownAccounts.containsKey(status.inReplyToAccountId)){
Account account=Objects.requireNonNull(knownAccounts.get(status.inReplyToAccountId));
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.in_reply_to, account.displayName), account.emojis, R.drawable.ic_fluent_arrow_reply_20_filled, null, i->{
args.putParcelable("profileAccount", Parcels.wrap(account));
Nav.go(fragment.getActivity(), ProfileFragment.class, args);
}));
} else if (
!(status.tags.isEmpty() ||
fragment instanceof HashtagTimelineFragment ||
fragment instanceof ListTimelineFragment
) && fragment.getParentFragment() instanceof HomeTabFragment home
) {
}, fullText));
} else if (!(status.tags.isEmpty() ||
fragment instanceof HashtagTimelineFragment ||
fragment instanceof ListTimelineFragment
) && fragment.getParentFragment() instanceof HomeTabFragment home) {
home.getHashtags().stream()
.filter(followed -> status.tags.stream()
.anyMatch(hashtag -> followed.name.equalsIgnoreCase(hashtag.name)))
@@ -151,11 +157,32 @@ public abstract class StatusDisplayItem{
)));
}
if (replyLine != null && GlobalUserPreferences.replyLineAboveHeader) {
Optional<ReblogOrReplyLineStatusDisplayItem> primaryLine = items.stream()
.filter(i -> i instanceof ReblogOrReplyLineStatusDisplayItem)
.map(ReblogOrReplyLineStatusDisplayItem.class::cast)
.findFirst();
if (primaryLine.isPresent() && GlobalUserPreferences.compactReblogReplyLine) {
primaryLine.get().extra = replyLine;
} else {
items.add(replyLine);
}
}
HeaderStatusDisplayItem header;
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null, notification, scheduledStatus));
if(!TextUtils.isEmpty(statusForContent.content)){
if (replyLine != null && !GlobalUserPreferences.replyLineAboveHeader) {
replyLine.belowHeader = true;
items.add(replyLine);
}
if(!TextUtils.isEmpty(statusForContent.content))
items.add(new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID), fragment, statusForContent, disableTranslate));
} else
else if (!GlobalUserPreferences.replyLineAboveHeader && replyLine != null)
replyLine.needBottomPadding=true;
else
header.needBottomPadding=true;
List<Attachment> imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList());
if(!imageAttachments.isEmpty()){
@@ -186,8 +213,6 @@ public abstract class StatusDisplayItem{
item.index=i++;
}
if (titleItem != null) items.add(0, titleItem);
if (!statusForContent.filterRevealed) {
return new ArrayList<>(List.of(
new WarningFilteredStatusDisplayItem(parentID, fragment, statusForContent, items)

View File

@@ -31,6 +31,7 @@ import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.LinkedTextView;
import org.joinmastodon.android.utils.StatusTextEncoder;
import java.util.Locale;
import java.util.regex.Pattern;
import me.grishka.appkit.api.Callback;
@@ -47,9 +48,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
private CharSequence parsedSpoilerText;
public boolean textSelectable;
public final Status status;
public boolean disableTranslate;
public boolean translated = false;
public TranslatedStatus translation = null;
public boolean disableTranslate, translationShown;
private AccountSession session;
public static final Pattern BOTTOM_TEXT_PATTERN = Pattern.compile("(?:[\uD83E\uDEC2\uD83D\uDC96✨\uD83E\uDD7A,]+|❤️)(?:\uD83D\uDC49\uD83D\uDC48(?:[\uD83E\uDEC2\uD83D\uDC96✨\uD83E\uDD7A,]+|❤️))*\uD83D\uDC49\uD83D\uDC48");
@@ -58,6 +57,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
this.text=text;
this.status=status;
this.disableTranslate=disableTranslate;
this.translationShown=status.translationShown;
emojiHelper.setText(text);
if(!TextUtils.isEmpty(status.spoilerText)){
parsedSpoilerText=HtmlParser.parseCustomEmoji(status.spoilerText, status.emojis);
@@ -67,6 +67,11 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
session = AccountSessionManager.getInstance().getAccount(parentFragment.getAccountID());
}
public void setTranslationShown(boolean translationShown) {
this.translationShown = translationShown;
status.translationShown = translationShown;
}
@Override
public Type getType(){
return Type.TEXT;
@@ -97,9 +102,11 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
private final float textMaxHeight, textCollapsedHeight;
private final LinearLayout.LayoutParams collapseParams, wrapParams;
private final ViewGroup parent;
public Holder(Activity activity, ViewGroup parent){
super(activity, R.layout.display_item_text, parent);
this.parent=parent;
text=findViewById(R.id.text);
spoilerTitle=findViewById(R.id.spoiler_title);
spoilerTitleInline=findViewById(R.id.spoiler_title_inline);
@@ -127,9 +134,9 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
@Override
public void onBind(TextStatusDisplayItem item){
text.setText(item.translated
? HtmlParser.parse(item.translation.content, item.status.emojis, item.status.mentions, item.status.tags, item.parentFragment.getAccountID())
: item.text);
text.setText(item.translationShown
? HtmlParser.parse(item.status.translation.content, item.status.emojis, item.status.mentions, item.status.tags, item.parentFragment.getAccountID())
: item.text);
text.setTextIsSelectable(item.textSelectable);
if (item.textSelectable) {
textScrollView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
@@ -165,26 +172,33 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
boolean translateEnabled = !item.disableTranslate && instanceInfo != null &&
instanceInfo.v2 != null && instanceInfo.v2.configuration.translation != null &&
instanceInfo.v2.configuration.translation.enabled;
String bottomText = null;
try {
bottomText = BOTTOM_TEXT_PATTERN.matcher(item.status.getStrippedText()).find()
? new StatusTextEncoder(Bottom::decode).decode(item.status.getStrippedText(), BOTTOM_TEXT_PATTERN)
: null;
} catch (TranslationError ignored) {}
boolean isBottomText = BOTTOM_TEXT_PATTERN.matcher(item.status.getStrippedText()).find();
boolean translateVisible = (isBottomText || (
boolean translateVisible = (bottomText != null || (
translateEnabled &&
!item.status.visibility.isLessVisibleThan(StatusPrivacy.UNLISTED) &&
item.status.language != null &&
(item.session.preferences == null || !item.status.language.equalsIgnoreCase(item.session.preferences.postingDefaultLanguage))));
// && (!GlobalUserPreferences.translateButtonOpenedOnly || item.textSelectable);
// todo: compare to mastodon locale instead (how do i query that?!)
!item.status.language.equalsIgnoreCase(Locale.getDefault().getLanguage())))
&& (!GlobalUserPreferences.translateButtonOpenedOnly || item.textSelectable);
translateWrap.setVisibility(translateVisible ? View.VISIBLE : View.GONE);
translateButton.setText(item.translated ? R.string.sk_translate_show_original : R.string.sk_translate_post);
translateInfo.setText(item.translated ? itemView.getResources().getString(R.string.sk_translated_using, isBottomText ? "bottom-java" : item.translation.provider) : "");
translateButton.setText(item.translationShown ? R.string.sk_translate_show_original : R.string.sk_translate_post);
translateInfo.setText(item.translationShown ? itemView.getResources().getString(R.string.sk_translated_using, bottomText != null ? "bottom-java" : item.status.translation.provider) : "");
String finalBottomText = bottomText;
translateButton.setOnClickListener(v->{
if (item.translation == null) {
if (isBottomText) {
if (item.status.translation == null) {
if (finalBottomText != null) {
try {
item.translation = new TranslatedStatus();
item.translation.content = new StatusTextEncoder(Bottom::decode).decode(item.status.getStrippedText(), BOTTOM_TEXT_PATTERN);
item.translated = true;
item.status.translation = new TranslatedStatus();
item.status.translation.content = finalBottomText;
item.setTranslationShown(true);
} catch (TranslationError err) {
item.translation = null;
item.status.translation = null;
Toast.makeText(itemView.getContext(), err.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
rebind();
@@ -193,13 +207,24 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
translateProgress.setVisibility(View.VISIBLE);
translateButton.setClickable(false);
translateButton.animate().alpha(0.5f).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
new TranslateStatus(item.status.id).setCallback(new Callback<>() {
@Override
public void onSuccess(TranslatedStatus translatedStatus) {
item.status.translation = translatedStatus;
item.setTranslationShown(true);
if (item.parentFragment.getActivity() == null) return;
translateProgress.setVisibility(View.GONE);
translateButton.setClickable(true);
translateButton.animate().alpha(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(50).start();
rebind();
}
if(item.status.reloadWhenClicked){
UiUtils.lookupStatus(item.parentFragment.getContext(), item.status, item.parentFragment.getAccountID(), null, status1 -> {
new TranslateStatus(status1.id).setCallback(new Callback<>() {
new TranslateStatus(item.status.id).setCallback(new Callback<>() {
@Override
public void onSuccess(TranslatedStatus translatedStatus) {
item.translation = translatedStatus;
item.translated = true;
item.status.translation = translatedStatus;
item.setTranslationShown(true);
if (item.parentFragment.getActivity() == null) return;
translateProgress.setVisibility(View.GONE);
translateButton.setClickable(true);
@@ -220,8 +245,8 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
new TranslateStatus(item.status.id).setCallback(new Callback<>() {
@Override
public void onSuccess(TranslatedStatus translatedStatus) {
item.translation = translatedStatus;
item.translated = true;
item.status.translation = translatedStatus;
item.setTranslationShown(true);
if (item.parentFragment.getActivity() == null) return;
translateProgress.setVisibility(View.GONE);
translateButton.setClickable(true);
@@ -239,7 +264,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
}).exec(item.parentFragment.getAccountID());
}
} else {
item.translated = !item.translated;
item.setTranslationShown(!item.translationShown);
rebind();
}
});
@@ -252,13 +277,16 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
readMore.setVisibility(View.GONE);
}
if (GlobalUserPreferences.collapseLongPosts) text.post(() -> {
text.measure(
View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
if (GlobalUserPreferences.collapseLongPosts && !item.status.textExpandable) {
boolean tooBig = text.getMeasuredHeight() > textMaxHeight;
boolean inTimeline = !item.textSelectable;
boolean hasSpoiler = !TextUtils.isEmpty(item.status.spoilerText);
boolean expandable = inTimeline && tooBig && !hasSpoiler;
item.parentFragment.onEnableExpandable(this, expandable);
});
boolean expandable = tooBig && !hasSpoiler;
item.parentFragment.onEnableExpandable(Holder.this, expandable);
}
readMore.setVisibility(item.status.textExpandable && !item.status.textExpanded ? View.VISIBLE : View.GONE);
textScrollView.setLayoutParams(item.status.textExpandable && !item.status.textExpanded ? collapseParams : wrapParams);

View File

@@ -63,6 +63,10 @@ public class LinkSpan extends CharacterStyle {
return link;
}
public String getText() {
return text;
}
public Type getType(){
return type;
}

View File

@@ -8,7 +8,6 @@ import android.view.View;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.ui.PhotoLayoutHelper;
import org.joinmastodon.android.ui.displayitems.LinkCardStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;

View File

@@ -18,7 +18,8 @@ public class MediaAttachmentViewController{
public final View view;
public final MediaGridStatusDisplayItem.GridItemType type;
public final ImageView photo;
public final View altButton;
public final View altButton, noAltButton, btnsWrap;
public static int[] altWrapPadding = null;
private BlurhashCrossfadeDrawable crossfadeDrawable=new BlurhashCrossfadeDrawable();
private final Context context;
private boolean didClear;
@@ -32,8 +33,13 @@ public class MediaAttachmentViewController{
}, null);
photo=view.findViewById(R.id.photo);
altButton=view.findViewById(R.id.alt_button);
noAltButton=view.findViewById(R.id.no_alt_button);
btnsWrap=view.findViewById(R.id.alt_badges);
this.type=type;
this.context=context;
if (altWrapPadding == null) {
altWrapPadding = new int[] { btnsWrap.getPaddingLeft(), btnsWrap.getPaddingTop(), btnsWrap.getPaddingRight(), btnsWrap.getPaddingBottom() };
}
}
public void bind(Attachment attachment, Status status){
@@ -43,13 +49,12 @@ public class MediaAttachmentViewController{
crossfadeDrawable.setCrossfadeAlpha(status.spoilerRevealed ? 0f : 1f);
photo.setImageDrawable(null);
photo.setImageDrawable(crossfadeDrawable);
photo.setContentDescription(TextUtils.isEmpty(attachment.description) ? context.getString(R.string.media_no_description) : attachment.description);
if(altButton!=null){
if(GlobalUserPreferences.showAltIndicator){
altButton.setVisibility(TextUtils.isEmpty(attachment.description) ? View.GONE : View.VISIBLE);
}else{
altButton.setVisibility(View.GONE);
}
boolean hasAltText = !TextUtils.isEmpty(attachment.description);
photo.setContentDescription(!hasAltText ? context.getString(R.string.media_no_description) : attachment.description);
if(btnsWrap!=null){
btnsWrap.setVisibility(View.VISIBLE);
altButton.setVisibility(hasAltText && GlobalUserPreferences.showAltIndicator ? View.VISIBLE : View.GONE);
noAltButton.setVisibility(!hasAltText && GlobalUserPreferences.showNoAltIndicator ? View.VISIBLE : View.GONE);
}
didClear=false;
}

View File

@@ -344,15 +344,24 @@ public class UiUtils{
}
}
public static int getThemeColor(Context context, @AttrRes int attr){
TypedArray ta=context.obtainStyledAttributes(new int[]{attr});
int color=ta.getColor(0, 0xff00ff00);
public static int getThemeColor(Context context, @AttrRes int attr) {
if (context == null) return 0xff00ff00;
TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
int color = ta.getColor(0, 0xff00ff00);
ta.recycle();
return color;
}
public static void openProfileByID(Context context, String selfID, String id){
Bundle args=new Bundle();
public static int getThemeColorRes(Context context, @AttrRes int attr) {
if (context == null) return 0xff00ff00;
TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
int color = ta.getResourceId(0, R.color.black);
ta.recycle();
return color;
}
public static void openProfileByID(Context context, String selfID, String id) {
Bundle args = new Bundle();
args.putString("account", selfID);
args.putString("profileAccountID", id);
Nav.go((Activity)context, ProfileFragment.class, args);
@@ -754,7 +763,7 @@ public class UiUtils{
public void onSuccess(Relationship result){
resultCallback.accept(result);
progressCallback.accept(false);
if(!result.following){
if(!result.following && !result.requested){
E.post(new RemoveAccountPostsEvent(accountID, account.id, true));
}
}

View File

@@ -2,14 +2,11 @@ package org.joinmastodon.android.ui.views;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import org.joinmastodon.android.ui.PhotoLayoutHelper;
import java.util.Arrays;
import me.grishka.appkit.utils.V;
public class MediaGridLayout extends ViewGroup{

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M2.75 13.25h18.5c0.414 0 0.75 0.336 0.75 0.75 0 0.38-0.282 0.694-0.648 0.743L21.25 14.75H2.75C2.336 14.75 2 14.414 2 14c0-0.38 0.282-0.694 0.648-0.743L2.75 13.25h18.5-18.5zm0-4h18.5C21.664 9.25 22 9.586 22 10c0 0.38-0.282 0.694-0.648 0.743L21.25 10.75H2.75C2.336 10.75 2 10.414 2 10c0-0.38 0.282-0.694 0.648-0.743L2.75 9.25h18.5-18.5z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -1,79 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This is hidden from screenreaders because that same alt text is set as content description on the ImageView -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/alt_text_wrapper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|bottom"
android:layout_margin="12dp"
android:importantForAccessibility="noHideDescendants"
android:background="@drawable/bg_image_alt_overlay">
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/alt_badges"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|bottom"
android:padding="12dp"
android:importantForAccessibility="noHideDescendants">
<ImageView
android:id="@+id/no_alt_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:src="@drawable/ic_fluent_important_20_filled"
android:tint="?colorGray25" />
<ImageView
android:id="@+id/no_alt_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:padding="4dp"
android:src="@drawable/ic_fluent_important_20_filled"
android:background="@drawable/bg_image_no_alt_overlay"
android:tint="?colorGray25" />
<TextView
android:id="@+id/alt_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/m3_label_large"
android:textColor="?colorGray25"
android:gravity="center"
android:includeFontPadding="false"
android:paddingHorizontal="5dp"
android:paddingVertical="1dp"
android:text="@string/sk_alt_button"/>
<ImageButton
android:id="@+id/alt_text_close"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="end|top"
android:src="@drawable/ic_baseline_close_24"
android:tint="#FFF"
android:background="?android:actionBarItemBackground"/>
<org.joinmastodon.android.ui.views.NestableScrollView
android:id="@+id/alt_text_scroller"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="40dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/alt_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:textAppearance="@style/m3_body_medium"
android:textColor="?colorGray25"
tools:text="Alt text goes here"/>
<TextView
android:id="@+id/no_alt_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="14dp"
android:textAppearance="@style/m3_label_large"
android:textColor="?colorGray25"
android:text="@string/sk_no_alt_text"/>
</LinearLayout>
</org.joinmastodon.android.ui.views.NestableScrollView>
<TextView
android:id="@+id/alt_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:textAppearance="@style/m3_label_large"
android:textColor="?colorGray25"
android:gravity="center"
android:includeFontPadding="false"
android:paddingHorizontal="5dp"
android:paddingVertical="1dp"
android:background="@drawable/bg_image_alt_overlay"
android:text="@string/sk_alt_button"/>
</FrameLayout>

View File

@@ -27,6 +27,7 @@
android:visibility="gone"
android:background="?android:actionBarItemBackground"
android:contentDescription="@string/sk_delete_notification"
android:tooltipText="@string/sk_delete_notification"
android:scaleType="center"
android:src="@drawable/ic_fluent_dismiss_20_filled"
android:tint="?android:textColorSecondary" />

View File

@@ -1,14 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<org.joinmastodon.android.ui.views.AutoOrientationLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:layout_marginBottom="-12dp">
<TextView
android:id="@+id/extra_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="6dp"
android:textAppearance="@style/m3_title_small"
android:drawableStart="@drawable/ic_fluent_arrow_reply_20_filled"
android:drawableTint="?android:textColorSecondary"
android:drawablePadding="6dp"
android:singleLine="true"
android:ellipsize="end"/>
<TextView
android:id="@+id/separator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="6dp"
android:paddingTop="16dp"
android:paddingHorizontal="1dp"
android:textAppearance="@style/m3_title_small"
android:gravity="center_horizontal"
android:importantForAccessibility="no"
android:includeFontPadding="false"
android:text="@string/sk_separator" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:paddingTop="16dp"
android:paddingBottom="6dp"
android:textAppearance="@style/m3_title_small"
@@ -18,4 +46,4 @@
android:singleLine="true"
android:ellipsize="end"/>
</FrameLayout>
</org.joinmastodon.android.ui.views.AutoOrientationLinearLayout>

View File

@@ -64,10 +64,10 @@
android:paddingBottom="6dp"
android:textAppearance="@style/m3_title_small"
android:drawableStart="@drawable/ic_fluent_arrow_reply_20_filled"
tools:drawableEnd="@drawable/ic_fluent_earth_20_regular"
android:drawableTint="?android:textColorSecondary"
android:drawablePadding="6dp"
android:singleLine="true"
android:text="@string/sk_in_reply"
android:ellipsize="end"/>
<RelativeLayout
@@ -101,9 +101,9 @@
android:id="@+id/self_extra_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2sp"
android:layout_marginStart="8sp"
android:layout_toEndOf="@id/self_name"
android:paddingTop="4sp"
android:ellipsize="end"
android:fontFamily="sans-serif"
android:singleLine="true"
@@ -125,6 +125,23 @@
</RelativeLayout>
<TextView
android:id="@+id/reply_text_below"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-6dp"
android:layout_marginBottom="-6dp"
android:layout_marginStart="16dp"
android:paddingTop="16dp"
android:paddingBottom="6dp"
android:textAppearance="@style/m3_title_small"
android:drawableStart="@drawable/ic_fluent_arrow_reply_20_filled"
android:drawableTint="?android:textColorSecondary"
android:drawablePadding="6dp"
android:singleLine="true"
android:text="@string/sk_in_reply"
android:ellipsize="end"/>
<FrameLayout
android:id="@+id/toot_text_wrap"
android:layout_width="match_parent"

View File

@@ -5,22 +5,29 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|bottom"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_margin="12dp"
android:importantForAccessibility="noHideDescendants"
android:background="@drawable/bg_image_alt_overlay">
<ImageView
android:id="@+id/no_alt_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:src="@drawable/ic_fluent_important_20_filled"
android:tint="?colorGray25" />
<TextView
android:id="@+id/alt_button"
android:layout_width="40dp"
android:layout_height="22dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/m3_label_large"
android:textColor="#FFF"
android:textColor="?colorGray25"
android:gravity="center"
android:includeFontPadding="false"
android:text="ALT"/>
android:paddingHorizontal="5dp"
android:paddingVertical="1dp"
android:text="@string/sk_alt_button"/>
<ImageButton
android:id="@+id/alt_text_close"
@@ -29,12 +36,13 @@
android:layout_gravity="end|top"
android:src="@drawable/ic_baseline_close_24"
android:tint="#FFF"
android:background="?android:selectableItemBackgroundBorderless"/>
android:background="?android:actionBarItemBackground"/>
<org.joinmastodon.android.ui.views.NestableScrollView
android:id="@+id/alt_text_scroller"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="40dp">
<LinearLayout
@@ -51,6 +59,17 @@
android:textColor="#FFF"
tools:text="Alt text goes here"/>
<TextView
android:id="@+id/no_alt_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="14dp"
android:textAppearance="@style/m3_label_large"
android:textColor="?colorGray25"
android:text="@string/sk_no_alt_text"/>
</LinearLayout>
</org.joinmastodon.android.ui.views.NestableScrollView>

View File

@@ -8,11 +8,11 @@
<item android:id="@+id/delete_and_redraft" android:title="@string/sk_delete_and_redraft" android:icon="@drawable/ic_fluent_arrow_clockwise_24_regular" />
<item android:id="@+id/pin" android:title="@string/sk_pin_post" android:icon="@drawable/ic_fluent_pin_24_regular"/>
<item android:id="@+id/unpin" android:title="@string/sk_unpin_post" android:icon="@drawable/ic_fluent_pin_off_24_regular"/>
<item android:id="@+id/mute" android:title="@string/mute_user" android:icon="@drawable/ic_fluent_speaker_mute_24_regular"/>
<item android:id="@+id/manage_user_lists" android:title="@string/sk_lists_with_user" android:icon="@drawable/ic_fluent_people_24_regular"/>
<item android:id="@+id/mute" android:title="@string/mute_user" android:icon="@drawable/ic_fluent_speaker_off_24_regular"/>
<item android:id="@+id/follow" android:title="@string/follow_user" android:icon="@drawable/ic_fluent_person_add_24_regular"/>
<item android:id="@+id/block" android:title="@string/block_user" android:icon="@drawable/ic_fluent_person_prohibited_24_regular"/>
<item android:id="@+id/block_domain" android:title="@string/block_domain" android:icon="@drawable/ic_fluent_shield_prohibited_24_regular"/>
<item android:id="@+id/follow" android:title="@string/follow_user" android:icon="@drawable/ic_fluent_person_add_24_regular"/>
<item android:id="@+id/manage_user_lists" android:title="@string/sk_lists_with_user" android:icon="@drawable/ic_fluent_people_24_regular"/>
<item android:id="@+id/report" android:title="@string/report_user" android:icon="@drawable/ic_fluent_warning_24_regular"/>
<item android:id="@+id/bookmark" android:title="@string/add_bookmark" android:icon="@drawable/ic_fluent_bookmark_24_regular"/>
<item android:id="@+id/copy_link" android:title="@string/sk_copy_link_to_post" android:icon="@drawable/ic_fluent_link_24_regular"/>

View File

@@ -26,7 +26,7 @@
<string name="settings">Налады</string>
<string name="publish">Апублікаваць</string>
<string name="discard_draft">Скасаваць чарнавік?</string>
<string name="discard">Скасаваць</string>
<string name="discard">Выйсці</string>
<string name="cancel">Скасаваць</string>
<plurals name="followers">
<item quantity="one">падпісчык</item>
@@ -435,6 +435,7 @@
<string name="welcome_page2_text">Ваш ідэнтыфікатар можа быць @gothgirl654@example.social, але вы ўсё яшчэ можаце падпісвацца, пашыраць і перапісвацца з @fallout5ever@example.online.</string>
<string name="welcome_page3_title">Як выбраць сервер?</string>
<string name="welcome_page3_text">Розныя людзі выбіраюць розныя серверы па розных прычынах. art.example з\'яўляецца выдатным месцам для мастакоў, у той час як glasgow.example можа быць добрым выбарам для шатландцаў.\n\nВы не памыліцеся ні з адным з нашых рэкамендаваных сервераў, так што незалежна ад таго, які вы выбераце (або калі ўведзяце ваш уласны ў радку пошуку сервера), вы нідзе нічога не прапусціце.</string>
<string name="server_filter_any_language">Любая мова</string>
<string name="server_filter_region_europe">Еўропа</string>
<string name="server_filter_region_north_america">Паўночная Амерыка</string>
<string name="server_filter_region_south_america">Паўднёвая Амерыка</string>

View File

@@ -261,4 +261,10 @@
<string name="sk_followed_as">Mit %s gefolgt</string>
<string name="sk_settings_hide_fab">Verfassen-Button automatisch ausblenden</string>
<string name="sk_follow_as">Mit anderem Account folgen</string>
<string name="sk_in_reply">Als Antwort</string>
<string name="sk_settings_reply_visibility">Antwort-Sichtbarkeit</string>
<string name="sk_settings_reply_visibility_all">Alle Antworten</string>
<string name="sk_settings_reply_visibility_following">Antworten auf Follows</string>
<string name="sk_settings_reply_visibility_self">Antworten an mich</string>
<string name="sk_quoting_user">Zitiere %s</string>
</resources>

View File

@@ -252,14 +252,15 @@
<string name="sk_notify_poll_results">Resultado de encuestas</string>
<string name="sk_filtered">Filtrado: %s</string>
<string name="sk_expand">Ampliar</string>
<string name="sk_collapse">Reducir</string>
<string name="sk_settings_collapse_long_posts">Acortar publicaciones largas</string>
<string name="sk_collapse">Minimizar</string>
<string name="sk_settings_collapse_long_posts">Minimizar publicaciones largas</string>
<string name="sk_unfinished_attachments">¿Corregir adjuntos\?</string>
<string name="sk_unfinished_attachments_message">Algunos adjuntos no han terminado de subirse.</string>
<string name="sk_settings_prefix_reply_cw_with_re">Añadir \"re:\" a respuestas a Advertencias de Contenido</string>
<string name="sk_spectator_mode">Modo espectador</string>
<string name="sk_settings_hide_interaction">Ocultar los botones interactivos</string>
<string name="sk_settings_hide_interaction">Ocultar los botones de interacción</string>
<string name="sk_follow_as">Seguir desde otra cuenta</string>
<string name="sk_followed_as">Seguido de %s</string>
<string name="sk_followed_as">Seguido desde %s</string>
<string name="sk_settings_hide_fab">Ocultar automáticamente el botón Redactar</string>
<string name="sk_in_reply">Respondiendo a</string>
</resources>

View File

@@ -263,4 +263,5 @@
<string name="sk_follow_as">Suivre depuis un autre compte</string>
<string name="sk_followed_as">Suivi depuis %s</string>
<string name="sk_settings_hide_fab">Masquer automatiquement le bouton Composer</string>
<string name="sk_in_reply">En réponse</string>
</resources>

View File

@@ -262,4 +262,5 @@
<string name="sk_follow_as">Seguir dende outra conta</string>
<string name="sk_followed_as">Seguida dende %s</string>
<string name="sk_settings_hide_fab">Auto-ocultar botón de redacción</string>
<string name="sk_in_reply">Respondendo</string>
</resources>

View File

@@ -247,7 +247,7 @@
<string name="sk_reported">dilaporkan</string>
<string name="sk_sign_ups">Pendaftaran pengguna</string>
<string name="sk_new_reports">Laporan baru</string>
<string name="sk_settings_show_new_posts_button">Tombol “Lihat kiriman baru”</string>
<string name="sk_settings_see_new_posts_button">Tombol “Lihat kiriman baru”</string>
<string name="sk_settings_local_only_explanation">Server rumah Anda harus mendukung pengiriman hanya lokal untuk ini bekerja. Kebanyakan versi Mastodon yang dimodifikasi bisa, tetapi Mastodon tidak.</string>
<string name="sk_settings_support_local_only">Server hanya mendukung pengiriman hanya lokal</string>
<string name="sk_settings_server_version">Versi server: %s</string>
@@ -263,4 +263,5 @@
<string name="sk_unfinished_attachments">Perbaiki lampiran\?</string>
<string name="sk_unfinished_attachments_message">Beberapa lampiran belum selesai diunggah.</string>
<string name="sk_settings_hide_fab">Sembunyikan tombol Komposer</string>
<string name="sk_in_reply">Dalam balasan</string>
</resources>

View File

@@ -401,6 +401,7 @@
<string name="popular_on_mastodon">人気のアカウント</string>
<string name="follow_all">すべてフォローする</string>
<string name="server_rules_disagree">同意しない</string>
<string name="privacy_policy_explanation">※: 私たちはいかなる個人情報も収集しません。</string>
<!-- %s is server domain -->
<string name="server_policy_disagree">%s のポリシーに同意しない</string>
<string name="profile_bio">自己紹介</string>

View File

@@ -58,8 +58,168 @@
<string name="profile_joined">အကောင့်ဖွင့်ခဲ့သည့် ရက်စွဲ</string>
<string name="done">ပြီးပြီ</string>
<string name="loading">လုပ်ဆောင်နေသည်…</string>
<string name="post_from_user">%s ၏ ပို့စ်</string>
<plurals name="x_minutes">
<item quantity="other">%d မိနစ်</item>
</plurals>
<plurals name="x_hours">
<item quantity="other">%d နာရီ</item>
</plurals>
<plurals name="x_days">
<item quantity="other">%d ရက်</item>
</plurals>
<plurals name="x_seconds_left">
<item quantity="other">%d စက္ကန့် ကျန်ရှိ</item>
</plurals>
<plurals name="x_minutes_left">
<item quantity="other">%d မိနစ် ကျန်ရှိ</item>
</plurals>
<plurals name="x_hours_left">
<item quantity="other">%d နာရီ ကျန်ရှိ</item>
</plurals>
<plurals name="x_days_left">
<item quantity="other">%d ရက် ကျန်ရှိ</item>
</plurals>
<plurals name="x_voters">
<item quantity="other">မဲပေးသူ %,d ဦး</item>
</plurals>
<string name="poll_closed">မဲပေးခွင့်ပိတ်သွားပါပြီ</string>
<string name="confirm_block_title">အကောင့်ဘလော့ခြင်း</string>
<string name="confirm_block_domain_title">ဒိုမိန်းကို ဘလော့လုပ်ခြင်း</string>
<string name="confirm_block">%s ကို ဘလော့မှာသေချာပြီလား</string>
<string name="do_block">ဘလော့မည်</string>
<string name="confirm_unblock_title">အကောင့်ဘလော့ဖြုတ်ခြင်း</string>
<string name="confirm_unblock_domain_title">ဒိုမိန်းကို ဘလော့ဖြုတ်ခြင်း</string>
<string name="confirm_unblock">%s ကို ဘလော့ဖြုတ်မှာသေချာပြီလား</string>
<string name="do_unblock">ဘလော့ဖြုတ်မည်</string>
<string name="button_blocked">ဘလော့ထား</string>
<string name="action_vote">မဲပေးမည်</string>
<string name="tap_to_reveal">ကြည့်ရန် နှိပ်ပါ</string>
<string name="delete">ဖျက်မည်</string>
<string name="confirm_delete_title">ပို့စ်ဖျက်ခြင်း</string>
<string name="confirm_delete">ဒီပို့စ်ကိုဖျက်မှာ သေချာပြီလား?</string>
<string name="deleting">ပို့စ်ဖျက်နေသည်…</string>
<string name="play">ဖွင့်မည်</string>
<string name="pause">ခဏရပ်မည်</string>
<string name="log_out">အကောင့်ထွက်မည်</string>
<string name="add_account">အကောင့်ထည့်မည်</string>
<string name="search_hint">ရှာဖွေရန်</string>
<string name="hashtags">ဟက်ရှ်တက်များ</string>
<string name="news">သတင်းများ</string>
<string name="for_you">သင့်အတွက်</string>
<string name="all_notifications">အားလုံး</string>
<string name="mentions">မန်းရှင်းထားသည်များ</string>
<plurals name="x_people_talking">
<item quantity="other">လူ %d ဦး ပြောနေသည်</item>
</plurals>
<string name="report_title">%s ကို တိုင်ကြားခြင်း</string>
<string name="report_choose_reason">ဒီပိုစ့်က ဘာဖြစ်လို့ပါလဲ?</string>
<string name="report_choose_reason_account">%s က ဘာဖြစ်လို့ပါလဲ?</string>
<string name="report_choose_reason_subtitle">အကိုက်ညီဆုံးအဖြေကို ရွေးချယ်ပါ</string>
<string name="report_reason_personal">မကြိုက်လို့</string>
<string name="report_reason_personal_subtitle">သင်ကြည့်ချင်တာ မဟုတ်လို့</string>
<string name="report_reason_spam">Spam မို့လို့</string>
<string name="report_reason_spam_subtitle">အန္တရာယ်ရှိတဲ့ လင့်ခ်မို့လို့ (သို့) engagement အတုမို့လို့ (သို့) ထပ်ခါထပ်ခါရီပလိုင်းပြန်ထားလို့</string>
<string name="report_reason_violation">ဆာဗာစည်းမျဉ်းတွေကို ချိုးဖောက်ထားလို့</string>
<string name="report_reason_violation_subtitle">စည်းမျဉ်းစည်းကမ်းတစ်ခုခုကို ချိုးဖောက်ထားတယ်ဆိုတာ သင်သိနေလို့</string>
<string name="report_reason_other">တခြားဟာ</string>
<string name="report_reason_other_subtitle">အပေါ်က အမျိုးအစားတွေထဲက မဟုတ်လို့</string>
<string name="report_choose_rule">ဘယ်စည်းမျဉ်းတွေကို ချိုးဖောက်ထားပါသလဲ?</string>
<string name="report_choose_rule_subtitle">သက်ဆိုင်သမျှကို ရွေးချယ်ပါ</string>
<string name="report_choose_posts">ဤတိုင်ကြားမှုကို အထောက်အကူပေးနိုင်မယ့် နောက်ထပ်ပို့စ်တွေ ရှိပါသေးလား?</string>
<string name="report_choose_posts_subtitle">သက်ဆိုင်သမျှကို ရွေးချယ်ပါ</string>
<string name="report_comment_title">တခြား ကျွန်တော်တို့ကိုသိစေချင်တာ ရှိပါသလား?</string>
<string name="report_comment_hint">ထပ်ပေါင်းမှတ်ချက်ပေးရန်</string>
<string name="sending_report">တိုင်ကြားစာ ပေးပို့နေသည်…</string>
<string name="report_sent_title">တိုင်ကြားမှုအတွက် ကျေးဇူးတင်ပါတယ်။ ကျွန်တော်တို့ စစ်ဆေးပေးပါ့မယ်။</string>
<string name="unfollow_user">%s ကို ဖောလိုးမလုပ်တော့</string>
<string name="unfollow">ဖောလိုးမလုပ်တော့</string>
<string name="block_user_explain">သူက သင့်ကိုဖောလိုးလုပ်နိုင်မှာမဟုတ်တော့သလို သင့်ပိုစ့်တွေကိုလည်း မမြင်ရတော့ပါဘူး။ ဒါပေမယ့် သူဘလော့ခံလိုက်ရတာကိုတော့ တွေ့မြင်နိုင်ပါတယ်။</string>
<string name="report_personal_title">ဒါကို မမြင်ချင်ဘူးလား?</string>
<string name="report_personal_subtitle">Mastodon ပေါ်မှာ သင်မမြင်ချင်တာတစ်ခုခုတွေ့ခဲ့ရင် သူ့ကို သင့်ကွန်ရက်ကနေ ဖယ်ထုတ်နိုင်ပါတယ်။</string>
<string name="instance_catalog_title">Mastodon ကို မတူညီတဲ့ဆာဗာပေါင်းစုံက အသုံးပြုများနဲ့ ဖွဲ့စည်းထားပါတယ်။</string>
<string name="instance_catalog_subtitle">သင့်စိတ်ဝင်စားမှု သို့မဟုတ် သင့်ဒေသပေါ်လိုက်ပြီး ဆာဗာရွေးချယ်နိုင်သလို အထွေထွေသုံးဆာဗာကိုလည်း ရွေးချယ်နိုင်ပါသည်။ အခြားသူတွေနဲ့ ဆက်သွယ်ရာမှာတော့ မည်သည့်ဆာဗာပဲဖြစ်နေပါစေ အချင်းချင်း ပုံမှန်အတိုင်း ဆက်သွယ်ချိတ်ဆက်နိုင်မှာပါ။</string>
<string name="search_communities">ဆာဗာအမည် သို့မဟုတ် URL</string>
<string name="instance_rules_title">ဆာဗာစည်းမျဉ်းများ</string>
<string name="instance_rules_subtitle">ရှေ့ဆက်ခြင်းအားဖြင့် သင်သည် %s ၏ စီမံကွပ်ကဲသူများမှ ချမှတ်ထားသော အောက်ပါစည်းမျဉ်းများကို သဘောတူပြီးဖြစ်ပါသည်။</string>
<string name="signup_title">အကောင့်ဖန်တီးမည်</string>
<string name="display_name">အမည်</string>
<string name="username">အသုံးပြုသူအမည်</string>
<string name="email">အီးမေးလ်</string>
<string name="password">စကားဝှက်</string>
<string name="confirm_password">စကားဝှက်ကို အတည်ပြုပါ</string>
<string name="password_note">သင့်စကားဝှက်ခိုင်မာမှုကို မြှင့်တင်ရန် စာလုံးအကြီးများ၊ special character များနှင့် နံပါတ်များထည့်သွင်းပါ။</string>
<string name="category_academia">ပညာရေး</string>
<string name="category_activism">တက်ကြွလှုပ်ရှားမှု</string>
<string name="category_all">အားလုံး</string>
<string name="category_art">အနုပညာ</string>
<string name="category_food">အစားအသောက်</string>
<string name="category_games">ဂိမ်း</string>
<string name="category_general">အထွေထွေ</string>
<string name="category_journalism">သတင်းစာပညာ</string>
<string name="category_lgbt">LGBT</string>
<string name="category_music">ဂီတ</string>
<string name="category_regional">ဒေသဆိုင်ရာ</string>
<string name="category_tech">နည်းပညာ</string>
<string name="confirm_email_title">သင့်အီးမေးလ် စာဝင်ပုံးကို စစ်ဆေးပါ</string>
<!-- %s is the email address -->
<string name="media_attachment_unsupported_type">ဖိုင် %s သည် ထောက်ပံ့မပေးထားသည့်အမျိုးအစားဖြစ်သည်</string>
<string name="media_attachment_too_big">ဖိုင် %1$s သည် အရွယ်အစားကန့်သတ်ချက်ဖြစ်သော %2$s MB ထက် ကျော်လွန်နေသည်</string>
<string name="theme_auto">အလိုလျောက်</string>
<string name="theme_light">အလင်း</string>
<string name="theme_dark">အမှောင်</string>
<string name="theme_true_black">အနက်စစ်စစ်မုဒ်</string>
<string name="settings_behavior">အပြုအမူဆိုင်ရာ</string>
<string name="settings_gif">ရွေ့လျားနေသော Avatar များနှင့် အီမိုဂျီများ</string>
<string name="settings_custom_tabs">အက်ပ်တွင်း ဘရောက်ဇာကို သုံးမည်</string>
<string name="settings_notifications">နိုတီများ</string>
<string name="notify_me_when">အသိပေးပါ</string>
<string name="notify_anyone">တစ်ယောက်ယောက်က</string>
<string name="notify_follower">ဖောလိုးဝါတစ်ယောက်က</string>
<string name="notify_followed">ကျွန်ုပ်ဖောလိုးလုပ်ထားသူက</string>
<string name="notify_none">မည်သူကမျှ</string>
<string name="notify_favorites">ကျွန်ုပ်ပို့စ်ကို နှစ်သက်လျှင်</string>
<string name="notify_follow">ကျွန်ုပ်ကို ဖောလိုးလုပ်လျှင်</string>
<string name="notify_reblog">ကျွန်ုပ်ပို့စ်ကို reblog လုပ်လျှင်</string>
<string name="notify_mention">ကျွန်ုပ်ကို မန်းရှင်းခေါ်လျှင်</string>
<string name="settings_boring">ပျင်းစရာကောင်းသည်များ</string>
<string name="settings_account">အကောင့်ဆက်တင်များ</string>
<string name="settings_tos">ဝန်ဆောင်မှုဆိုင်ရာ စည်းကမ်းချက်များ</string>
<string name="settings_privacy_policy">ကိုယ်ရေးအချက်အလက်မူဝါဒ</string>
<string name="settings_spicy">ပူပူစပ်စပ်ဇုန်</string>
<string name="settings_clear_cache">မီဒီယာကက်ရှ်ကို ရှင်းလင်းမည်</string>
<string name="settings_app_version">အန်းဒရိုက်အတွက် မက်စ်စတိုဒွန် - ဗားရှင်း %1$s (%2$d)</string>
<string name="media_cache_cleared">မီဒီယာကက်ရှ်ကို ရှင်းလင်းလိုက်ပါပြီ</string>
<string name="confirm_log_out">အကောင့်ထွက်မှာ သေချာပြီလား?</string>
<string name="sensitive_content">ထိလွယ်ရှလွယ် အကြောင်းအရာ</string>
<string name="sensitive_content_explain">စာရေးသူက ဤဓာတ်ပုံ/ဗီဒီယိုကို ထိရှလွယ်သည်အဖြစ် မှတ်သားထားသည်၊ ကြည့်ရန် ထိပါ။</string>
<string name="media_hidden">ကြည့်ရန် ထိပါ</string>
<string name="avatar_description">%s ရဲ့ ပရိုဖိုင်းကို သွားမည်</string>
<string name="new_post">ပို့စ်အသစ်</string>
<string name="add_media">ဓာတ်ပုံ/ဗီဒီယိုထည့်ရန်</string>
<string name="add_poll">စစ်တမ်းကောက်ရန်</string>
<string name="emoji">အီမိုဂျီ</string>
<string name="post_visibility">ပို့စ်အား တွေ့မြင်နိုင်မှု</string>
<string name="home_timeline">မူလ တိုင်းမ်လိုင်း</string>
<string name="my_profile">ကျွန်ုပ်၏ပရိုဖိုင်း</string>
<string name="media_viewer">မီဒီယာကြည့်ရှုခြင်း</string>
<!-- translators: %,d is a valid placeholder, it formats the number with locale-dependent grouping separators -->
<string name="post_info_reblogs">Reblog များ</string>
<string name="post_info_favorites">နှစ်သက်မှုများ</string>
<string name="edit_history">ပြင်ဆင်မှုမှတ်တမ်း</string>
<string name="last_edit_at_x">နောက်ဆုံးပြင်ဆင်ချိန်မှာ %s</string>
<string name="time_just_now">အခုလေးတင်</string>
<plurals name="x_seconds_ago">
<item quantity="other">လွန်ခဲ့သော %d စက္ကန့်က</item>
</plurals>
<plurals name="x_minutes_ago">
<item quantity="other">လွန်ခဲ့သော %d မိနစ်က</item>
</plurals>
<string name="edited_timestamp">ပြင်ဆင်ထား %s</string>
<string name="edit_original_post">မူရင်းပို့စ်</string>
<string name="edit_spoiler_added">အကြောင်းအရာသတိပေးချက် ထည့်လိုက်သည်</string>
<string name="edit_spoiler_edited">အကြောင်းအရာသတိပေးချက်ကို ပြင်လိုက်သည်</string>
<string name="edit_spoiler_removed">အကြောင်းအရာသတိပေးချက်ကို ဖယ်ရှားလိုက်သည်</string>
<!-- %s is version like 1.2.3 -->
<!-- %s is version like 1.2.3 -->
<!-- %s is file size -->

View File

@@ -248,4 +248,7 @@
<string name="sk_settings_collapse_long_posts">Lange berichten inklappen</string>
<string name="sk_unfinished_attachments_message">Enkele bijlagen zijn nog niet klaar met uploaden.</string>
<string name="sk_compose_no_draft">Niet opslaan als concept</string>
<string name="sk_settings_hide_interaction">Verberg interactie knoppen</string>
<string name="sk_follow_as">Volgen met ander account</string>
<string name="sk_followed_as">Gevolgd met %s</string>
</resources>

View File

@@ -6,7 +6,7 @@
<string name="next">Dalej</string>
<string name="loading_instance">Pobieranie informacji o serwerze…</string>
<string name="error">Błąd</string>
<string name="not_a_mastodon_instance">%s wydaje się nie być instancją Mastodona.</string>
<string name="not_a_mastodon_instance">%s nie wydaje się być serwerem Mastodona.</string>
<string name="ok">OK</string>
<string name="preparing_auth">Przygotowywanie do uwierzytelniania…</string>
<string name="finishing_auth">Kończenie uwierzytelniania…</string>

View File

@@ -139,7 +139,7 @@
<string name="sk_settings_single_notification">Pokazuj tylko jedną notyfikację</string>
<string name="sk_list_replies_policy_none">nikt</string>
<string name="sk_delete_list">Usuń listę</string>
<string name="sk_delete_list_confirm">Czy jesteś pewien że chcesz usunąć listę\?</string>
<string name="sk_delete_list_confirm">Czy jesteś pewien że chcesz usunąć listę “%s”\?</string>
<string name="sk_edit_list_title">Edytuj listę</string>
<string name="sk_your_lists">Twoje listy</string>
<string name="sk_recent_searches_placeholder">Wpisz coś, aby rozpocząć wyszukiwanie</string>
@@ -245,7 +245,7 @@
<string name="sk_reported">zareportowano</string>
<string name="sk_sign_ups">Rejestrujący się użytkownicy</string>
<string name="sk_new_reports">Nowe raporty</string>
<string name="sk_settings_show_new_posts_button">Przycisk \"Pokaż nowe wpisy\"</string>
<string name="sk_settings_see_new_posts_button">Przycisk \"Pokaż nowe wpisy\"</string>
<string name="sk_instance_features">Funkcje instancji</string>
<string name="sk_settings_local_only_explanation">Twoja instancja domowa musi wspierać publikowanie w trybie lokalnym. Większość zmodyfikowanych wersji Mastodona to wspiera, ale podstawowy Mastodon nie.</string>
<string name="sk_settings_server_version">Wersja serwera: %s</string>
@@ -261,4 +261,5 @@
<string name="sk_followed_as">Obserwowane z %s</string>
<string name="sk_follow_as">Obserwuj z innego konta</string>
<string name="sk_settings_hide_fab">Automatycznie ukrywaj przycisk tworzenia nowego wpisu</string>
<string name="sk_in_reply">W odpowiedzi</string>
</resources>

View File

@@ -173,7 +173,16 @@
<string name="back">Tillbaka</string>
<string name="instance_catalog_title">Mastodon utgörs av användare på olika servrar.</string>
<string name="instance_catalog_subtitle">Välj en server baserat på dina intressen, region eller en allmän server. Du kan fortfarande nå alla, oavsett server.</string>
<string name="search_communities">Servernamn eller URL</string>
<string name="instance_rules_title">Serverregler</string>
<string name="instance_rules_subtitle">Genom att fortsätta, samtycker du till att följa följande regler som fastställts och verkställs av %s moderatorer.</string>
<string name="signup_title">Skapa konto</string>
<string name="edit_photo">redigera</string>
<string name="display_name">Namn</string>
<string name="username">Användarnamn</string>
<string name="email">E-post</string>
<string name="password">Lösenord</string>
<string name="confirm_password">Bekräfta lösenord</string>
<string name="password_note">Inkludera stora bokstäver, specialtecken och siffror för att öka ditt lösenords styrka.</string>
<string name="category_academia">Vetenskap</string>
<string name="category_activism">Aktivism</string>
@@ -188,7 +197,10 @@
<string name="category_music">Musik</string>
<string name="category_regional">Regionalt</string>
<string name="category_tech">Teknik</string>
<string name="confirm_email_title">Kolla din inkorg</string>
<!-- %s is the email address -->
<string name="confirm_email_subtitle">Tryck på länken vi skickade dig för att verifiera %s. Vi väntar här.</string>
<string name="confirm_email_didnt_get">Fick du ingen länk?</string>
<string name="resend">Skicka igen</string>
<string name="open_email_app">Öppna e-postappen</string>
<string name="resent_email">Bekräftelse via e-post skickad</string>
@@ -230,7 +242,7 @@
<string name="settings_behavior">Beteende</string>
<string name="settings_gif">Spela animerade avatarer och emojis</string>
<string name="settings_custom_tabs">Använd webbläsaren i appen</string>
<string name="settings_notifications">Notiser</string>
<string name="settings_notifications">Aviseringar</string>
<string name="notify_me_when">Meddela mig när</string>
<string name="notify_anyone">vem som helst</string>
<string name="notify_follower">en följare</string>
@@ -276,6 +288,7 @@
<string name="open_in_browser">Öppna i webbläsare</string>
<string name="hide_boosts_from_user">Dölj boostar från %s</string>
<string name="show_boosts_from_user">Visa boostar från %s</string>
<string name="signup_reason">Varför vill du gå med?</string>
<string name="signup_reason_note">Detta kommer hjälpa oss att granska din ansökan.</string>
<string name="clear">Rensa</string>
<string name="profile_header">Bild för sidhuvud</string>
@@ -368,6 +381,8 @@
<!-- %s is file size -->
<string name="download_update">Ladda ner (%s)</string>
<string name="install_update">Installera</string>
<string name="privacy_policy_title">Din integritet</string>
<string name="privacy_policy_subtitle">Även om Mastodon-appen inte samlar in några data, kan servern du registrerar dig genom ha en annan policy.\n\nOm du inte håller med policyn för %skan du gå tillbaka och välja en annan server.</string>
<string name="i_agree">Jag godkänner</string>
<string name="empty_list">Den här listan är tom</string>
<string name="instance_signup_closed">Denna server accepterar inte nyregistreringar.</string>
@@ -379,12 +394,41 @@
<string name="login_title">Välkommen tillbaka</string>
<string name="login_subtitle">Logga in på den server där du skapade ditt konto.</string>
<string name="server_url">Server-URL</string>
<string name="welcome_page1_title">Vad är Mastodon?</string>
<string name="welcome_page1_text">Tänk dig att du har en epostadress som slutar med @example.com.\n\nDu kan skicka och ta emot epost från vem som helst, även om deras epostadress slutar med @gmail.com, @icloud.com eller @example.com.</string>
<string name="welcome_page2_title">Mastodon fungerar så.</string>
<string name="welcome_page2_text">Ditt handtag kan vara @gothgirl654@example.social men du kan fortfarande följa, boosta och chatta med @fallout5ever@example.online.</string>
<string name="welcome_page3_title">Hur väljer jag en server?</string>
<string name="welcome_page3_text">Olika personer väljer olika servrar av olika anledningar. art.example är en bra plats för konstnärer medan glasgow.exempel kan vara ett bra val för skottar.\n\nMed någon av våra rekommenderar servrar kan du inte hamna fel så oavsett vilken du väljer, eller om du skriver in din egen i serverns sökfältet, kommer du aldrig missa något.</string>
<string name="signup_random_server_explain">Vi kommer att välja en server baserat på ditt språk om du fortsätter utan att göra ett val.</string>
<string name="server_filter_any_language">Vilket språk som helst</string>
<string name="server_filter_instant_signup">Omedelbar registrering</string>
<string name="server_filter_manual_review">Manuell granskning</string>
<string name="server_filter_any_signup_speed">Valfri registreringshastighet</string>
<string name="server_filter_region_europe">Europa</string>
<string name="server_filter_region_north_america">Nordamerika</string>
<string name="server_filter_region_south_america">Sydamerika</string>
<string name="server_filter_region_africa">Afrika</string>
<string name="server_filter_region_asia">Asien</string>
<string name="server_filter_region_oceania">Oceanien</string>
<string name="not_accepting_new_members">Accepterar inte nya medlemmar</string>
<string name="category_special_interests">Specialintressen</string>
<string name="signup_passwords_dont_match">Lösenorden matchar inte</string>
<string name="pick_server_for_me">Välj för mig</string>
<string name="profile_add_row">Lägg till rad</string>
<string name="profile_setup">Konfiguration av profil</string>
<string name="profile_setup_subtitle">Du kan alltid fylla i detta senare i fliken Profil.</string>
<string name="profile_setup_explanation">Du kan lägga till upp till fyra profilfält för vad du vill. Plats, länkar, pronomen - himlen är gränsen.</string>
<string name="popular_on_mastodon">Populärt på Mastodon</string>
<string name="follow_all">Följ alla</string>
<string name="server_rules_disagree">Instämmer inte alls</string>
<string name="privacy_policy_explanation">TL;DR: Vi samlar inte in eller bearbetar något.</string>
<!-- %s is server domain -->
<string name="server_policy_disagree">Håller inte med %s</string>
<string name="profile_bio">Biografi</string>
<!-- Shown in a progress dialog when you tap "follow all" -->
<string name="sending_follows">Följer användare…</string>
<!-- %1$s is server domain, %2$s is email domain. You can reorder these placeholders to fit your language better. -->
<string name="signup_email_domain_blocked">%1$s tillåter inte registrering från %2$s. Prova en annan eller &lt;a&gt;välj en annan server&lt;/a&gt;.</string>
<string name="signup_username_taken">Det här användarnamnet är redan taget.</string>
</resources>

View File

@@ -262,4 +262,5 @@
<string name="sk_follow_as">Підписатися з іншого облікового запису</string>
<string name="sk_followed_as">Відстежується з %s</string>
<string name="sk_settings_hide_fab">Автоматично ховати кнопку компонування</string>
<string name="sk_in_reply">У відповідь</string>
</resources>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- %s is the email address -->
<!-- translators: %,d is a valid placeholder, it formats the number with locale-dependent grouping separators -->
<!-- %s is version like 1.2.3 -->
<!-- %s is version like 1.2.3 -->
<!-- %s is file size -->
<!-- %s is server domain -->
<!-- Shown in a progress dialog when you tap "follow all" -->
<!-- %1$s is server domain, %2$s is email domain. You can reorder these placeholders to fit your language better. -->
</resources>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="text_max_height">350dp</dimen>
<dimen name="text_max_height">220dp</dimen>
<dimen name="text_collapsed_height">150dp</dimen>
</resources>

View File

@@ -291,6 +291,7 @@
<string name="follow_user">Follow %s</string>
<string name="unfollowed_user">Unfollowed %s</string>
<string name="followed_user">You\'re now following %s</string>
<string name="following_user_requested">Requested to follow %s</string>
<string name="open_in_browser">Open in browser</string>
<string name="hide_boosts_from_user">Hide reblogs from %s</string>
<string name="show_boosts_from_user">Show reblogs from %s</string>

View File

@@ -267,4 +267,9 @@
<string name="sk_follow_as">Follow from other account</string>
<string name="sk_followed_as">Followed from %s</string>
<string name="sk_settings_hide_fab">Auto-hide Compose button</string>
<string name="sk_notification_action_replied">Sent reply to %s</string>
<string name="sk_in_reply">In reply</string>
<string name="sk_reply_line_above_avatar">“In reply to” line above avatar</string>
<string name="sk_show_thread">Show thread</string>
<string name="sk_compact_reblog_reply_line">Compact reblog/reply line</string>
</resources>

View File

@@ -0,0 +1,4 @@
- Mantenga presionado el botón de seguimiento para seguir los perfiles de otra cuenta
- Opción para abrir perfiles en otra cuenta
- Ocultar automáticamente el botón de redacción al desplazarse hacia abajo en la línea de tiempo
- Solucione el bloqueo al abrir los perfiles de administrador del servidor