Compare commits
183 Commits
v2.0.3+for
...
upstream/f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdff407514 | ||
|
|
422c3c3809 | ||
|
|
1988849b26 | ||
|
|
fc10fbffb0 | ||
|
|
a21a74a8e7 | ||
|
|
20e5d2a545 | ||
|
|
7da363fb87 | ||
|
|
c46f78272d | ||
|
|
95685d4de8 | ||
|
|
cbee0fe72e | ||
|
|
6d085ae6f0 | ||
|
|
4de7211523 | ||
|
|
05f7a44bd5 | ||
|
|
1079f600bc | ||
|
|
72580dadd0 | ||
|
|
98a02e874b | ||
|
|
d219d7aa4b | ||
|
|
68a9fe8376 | ||
|
|
f2f8620312 | ||
|
|
4ee229ea79 | ||
|
|
c261214e49 | ||
|
|
b06df8c3d0 | ||
|
|
a00afd5d7f | ||
|
|
9a41a2d6fb | ||
|
|
2cd98a6620 | ||
|
|
283b56be5b | ||
|
|
6d56771aba | ||
|
|
1724d8a532 | ||
|
|
52030b3b2d | ||
|
|
b4cdf35d36 | ||
|
|
cad0ad7a59 | ||
|
|
ca60003c39 | ||
|
|
0f030e0bac | ||
|
|
6d4f212a18 | ||
|
|
183b39bc24 | ||
|
|
27ad0c6fcf | ||
|
|
b5f661f1af | ||
|
|
0015f3f0bf | ||
|
|
c5d0fdd645 | ||
|
|
2d09ad44fb | ||
|
|
667fffd124 | ||
|
|
699233d8c7 | ||
|
|
56aabdc4a6 | ||
|
|
443e2c7a6f | ||
|
|
985b0f6e63 | ||
|
|
cc86edf276 | ||
|
|
4071b9342d | ||
|
|
f71d1bc5d3 | ||
|
|
2b926ffa46 | ||
|
|
6bcdbaba34 | ||
|
|
a2beead3a5 | ||
|
|
e7a25e353d | ||
|
|
af04a01130 | ||
|
|
fe1cfa1d7b | ||
|
|
b248797bb0 | ||
|
|
f24eba08d3 | ||
|
|
0e89559a47 | ||
|
|
858657799f | ||
|
|
d02a72e079 | ||
|
|
3be57d1b0b | ||
|
|
bed550e97c | ||
|
|
7e2619ea75 | ||
|
|
4b22f1d3a7 | ||
|
|
9dcc7e293f | ||
|
|
6a68cf5e41 | ||
|
|
29297be4a3 | ||
|
|
90b87529e0 | ||
|
|
39af05524d | ||
|
|
e3fb2cd03c | ||
|
|
90f84d628a | ||
|
|
b89e0b5c5a | ||
|
|
f724644d84 | ||
|
|
aac89c354c | ||
|
|
a032f9af10 | ||
|
|
642aaec6da | ||
|
|
ff667d6aed | ||
|
|
5e98496ea6 | ||
|
|
972fe1d15b | ||
|
|
26eaa36faa | ||
|
|
c517f41595 | ||
|
|
56a6d7243f | ||
|
|
18e43dfc22 | ||
|
|
816f6370ef | ||
|
|
30866a5292 | ||
|
|
3e1403d18a | ||
|
|
ebc2b2e59d | ||
|
|
c9a796dbfe | ||
|
|
1ba185ea9c | ||
|
|
1a50c3ff5f | ||
|
|
35a85c3247 | ||
|
|
6a729fa97f | ||
|
|
923639a329 | ||
|
|
a78be8bc1d | ||
|
|
abfb497577 | ||
|
|
a10b184508 | ||
|
|
f0ea6660e6 | ||
|
|
a829f25d56 | ||
|
|
deff3dd8e0 | ||
|
|
dab596f527 | ||
|
|
0c18ab2319 | ||
|
|
6c5fb5ea09 | ||
|
|
afe0c9e0db | ||
|
|
1f2213042f | ||
|
|
5edd2466f9 | ||
|
|
f3b3a1a577 | ||
|
|
068619b815 | ||
|
|
f121e94979 | ||
|
|
b5b52529d4 | ||
|
|
876bf73454 | ||
|
|
522dbf6e4a | ||
|
|
ae685095ba | ||
|
|
30d5fe2f12 | ||
|
|
2bf27c561c | ||
|
|
bbdc72323d | ||
|
|
6e335930f3 | ||
|
|
9b309939da | ||
|
|
faf2e5115d | ||
|
|
dc5d9412c8 | ||
|
|
fc0680d66f | ||
|
|
56c9a5433f | ||
|
|
60e473ee55 | ||
|
|
ae34ecd5c3 | ||
|
|
fd1caa8729 | ||
|
|
1182e5c60c | ||
|
|
d99d515dfa | ||
|
|
70a15e7d9c | ||
|
|
1691382369 | ||
|
|
b7da9c6d51 | ||
|
|
3426538dca | ||
|
|
63de2b200b | ||
|
|
ff1ee766dc | ||
|
|
f033411adf | ||
|
|
a738eaf8c0 | ||
|
|
5074aadd6e | ||
|
|
0854961470 | ||
|
|
227b077935 | ||
|
|
1e4358290a | ||
|
|
925169eb31 | ||
|
|
e1abeb9252 | ||
|
|
cbe0add211 | ||
|
|
299b524d62 | ||
|
|
31c094e696 | ||
|
|
a8038a2863 | ||
|
|
29933bb916 | ||
|
|
5ec0c078d8 | ||
|
|
e6287f1ff2 | ||
|
|
be9caf8905 | ||
|
|
f375142084 | ||
|
|
fd3668d520 | ||
|
|
d5e03e9d9e | ||
|
|
d62f094919 | ||
|
|
6d84f28600 | ||
|
|
209e603f2c | ||
|
|
1b4dc01c74 | ||
|
|
6aab8f6578 | ||
|
|
645af12c3f | ||
|
|
fadc42d72b | ||
|
|
fc831e7d42 | ||
|
|
2998ee9145 | ||
|
|
971c4e5879 | ||
|
|
48c53ee88b | ||
|
|
acf1fa15da | ||
|
|
1c3b28f9d7 | ||
|
|
b396ee7987 | ||
|
|
90856a414a | ||
|
|
ea19925be6 | ||
|
|
03b3775843 | ||
|
|
38b39751ae | ||
|
|
54a4b0fe41 | ||
|
|
3bf591c944 | ||
|
|
584a6bbfa3 | ||
|
|
0f803cd4fa | ||
|
|
167a14b8db | ||
|
|
81cbc2d10c | ||
|
|
9bd8aff99b | ||
|
|
a770828165 | ||
|
|
ab457035ff | ||
|
|
f886e4c1d2 | ||
|
|
380e4ff77e | ||
|
|
58f0c07357 | ||
|
|
77dee59b9c | ||
|
|
464dc93d99 | ||
|
|
dcdfd3e5d3 |
@@ -15,8 +15,8 @@ android {
|
||||
applicationId "org.joinmastodon.android.sk"
|
||||
minSdk 23
|
||||
targetSdk 33
|
||||
versionCode 99
|
||||
versionName "2.0.3+fork.99"
|
||||
versionCode 100
|
||||
versionName "2.1.4+fork.100"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
resourceConfigurations += ['ar-rSA', 'ar-rDZ', 'be-rBY', 'bn-rBD', 'bs-rBA', 'ca-rES', 'cs-rCZ', 'da-rDK', 'de-rDE', 'el-rGR', 'es-rES', 'eu-rES', 'fa-rIR', 'fi-rFI', 'fil-rPH', 'fr-rFR', 'ga-rIE', 'gd-rGB', 'gl-rES', 'hi-rIN', 'hr-rHR', 'hu-rHU', 'hy-rAM', 'ig-rNG', 'in-rID', 'is-rIS', 'it-rIT', 'iw-rIL', 'ja-rJP', 'kab', 'ko-rKR', 'my-rMM', 'nl-rNL', 'no-rNO', 'oc-rFR', 'pl-rPL', 'pt-rBR', 'pt-rPT', 'ro-rRO', 'ru-rRU', 'si-rLK', 'sl-rSI', 'sv-rSE', 'th-rTH', 'tr-rTR', 'uk-rUA', 'ur-rIN', 'vi-rVN', 'zh-rCN', 'zh-rTW']
|
||||
}
|
||||
|
||||
@@ -43,50 +43,7 @@ public class MainActivity extends FragmentStackActivity implements ProvidesAssis
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if(savedInstanceState==null){
|
||||
if(AccountSessionManager.getInstance().getLoggedInAccounts().isEmpty()){
|
||||
showFragmentClearingBackStack(new CustomWelcomeFragment());
|
||||
}else{
|
||||
AccountSession session;
|
||||
Bundle args=new Bundle();
|
||||
Intent intent=getIntent();
|
||||
if(intent.hasExtra("fromExternalShare")) {
|
||||
AccountSessionManager.getInstance()
|
||||
.setLastActiveAccountID(intent.getStringExtra("account"));
|
||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo(
|
||||
AccountSessionManager.getInstance().getLastActiveAccount());
|
||||
showFragmentForExternalShare(intent.getExtras());
|
||||
return;
|
||||
}
|
||||
|
||||
boolean fromNotification = intent.getBooleanExtra("fromNotification", false);
|
||||
boolean hasNotification = intent.hasExtra("notification");
|
||||
if(fromNotification){
|
||||
String accountID=intent.getStringExtra("accountID");
|
||||
try{
|
||||
session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||
if(!hasNotification) args.putString("tab", "notifications");
|
||||
}catch(IllegalStateException x){
|
||||
session=AccountSessionManager.getInstance().getLastActiveAccount();
|
||||
}
|
||||
}else{
|
||||
session=AccountSessionManager.getInstance().getLastActiveAccount();
|
||||
}
|
||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo(session);
|
||||
args.putString("account", session.getID());
|
||||
Fragment fragment=session.activated ? new HomeFragment() : new AccountActivationFragment();
|
||||
fragment.setArguments(args);
|
||||
if(fromNotification && hasNotification){
|
||||
Notification notification=Parcels.unwrap(intent.getParcelableExtra("notification"));
|
||||
showFragmentForNotification(notification, session.getID());
|
||||
} else if (intent.getBooleanExtra("compose", false)){
|
||||
showCompose();
|
||||
} else if (Intent.ACTION_VIEW.equals(intent.getAction())){
|
||||
handleURL(intent.getData(), null);
|
||||
} else {
|
||||
showFragmentClearingBackStack(fragment);
|
||||
maybeRequestNotificationsPermission();
|
||||
}
|
||||
}
|
||||
restartHomeFragment();
|
||||
}
|
||||
|
||||
if(GithubSelfUpdater.needSelfUpdating()){
|
||||
@@ -143,7 +100,7 @@ public class MainActivity extends FragmentStackActivity implements ProvidesAssis
|
||||
}
|
||||
|
||||
public void openSearchQuery(String q, String accountID, int progressText, boolean fromSearch){
|
||||
new GetSearchResults(q, null, true)
|
||||
new GetSearchResults(q, null, true, null, 0, 0)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(SearchResults result){
|
||||
@@ -259,4 +216,51 @@ public class MainActivity extends FragmentStackActivity implements ProvidesAssis
|
||||
Fragment fragment = getCurrentFragment();
|
||||
if (fragment != null) callFragmentToProvideAssistContent(fragment, assistContent);
|
||||
}
|
||||
|
||||
public void restartHomeFragment(){
|
||||
if(AccountSessionManager.getInstance().getLoggedInAccounts().isEmpty()){
|
||||
showFragmentClearingBackStack(new CustomWelcomeFragment());
|
||||
}else{
|
||||
AccountSession session;
|
||||
Bundle args=new Bundle();
|
||||
Intent intent=getIntent();
|
||||
if(intent.hasExtra("fromExternalShare")) {
|
||||
AccountSessionManager.getInstance()
|
||||
.setLastActiveAccountID(intent.getStringExtra("account"));
|
||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo(
|
||||
AccountSessionManager.getInstance().getLastActiveAccount());
|
||||
showFragmentForExternalShare(intent.getExtras());
|
||||
return;
|
||||
}
|
||||
|
||||
boolean fromNotification = intent.getBooleanExtra("fromNotification", false);
|
||||
boolean hasNotification = intent.hasExtra("notification");
|
||||
if(fromNotification){
|
||||
String accountID=intent.getStringExtra("accountID");
|
||||
try{
|
||||
session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||
if(!hasNotification) args.putString("tab", "notifications");
|
||||
}catch(IllegalStateException x){
|
||||
session=AccountSessionManager.getInstance().getLastActiveAccount();
|
||||
}
|
||||
}else{
|
||||
session=AccountSessionManager.getInstance().getLastActiveAccount();
|
||||
}
|
||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo(session);
|
||||
args.putString("account", session.getID());
|
||||
Fragment fragment=session.activated ? new HomeFragment() : new AccountActivationFragment();
|
||||
fragment.setArguments(args);
|
||||
if(fromNotification && hasNotification){
|
||||
Notification notification=Parcels.unwrap(intent.getParcelableExtra("notification"));
|
||||
showFragmentForNotification(notification, session.getID());
|
||||
} else if (intent.getBooleanExtra("compose", false)){
|
||||
showCompose();
|
||||
} else if (Intent.ACTION_VIEW.equals(intent.getAction())){
|
||||
handleURL(intent.getData(), null);
|
||||
} else {
|
||||
showFragmentClearingBackStack(fragment);
|
||||
maybeRequestNotificationsPermission();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +176,8 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
List<NotificationChannel> channels=Arrays.stream(PushNotification.Type.values())
|
||||
.map(type->{
|
||||
NotificationChannel channel=new NotificationChannel(accountID+"_"+type, context.getString(type.localizedName), NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel.setLightColor(context.getColor(R.color.primary_700));
|
||||
channel.enableLights(true);
|
||||
channel.setGroup(accountID);
|
||||
return channel;
|
||||
})
|
||||
@@ -205,6 +207,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
.setShowWhen(true)
|
||||
.setCategory(Notification.CATEGORY_SOCIAL)
|
||||
.setAutoCancel(true)
|
||||
.setLights(UiUtils.getThemeColor(context, android.R.attr.colorAccent), 500, 1000)
|
||||
.setColor(UiUtils.getThemeColor(context, android.R.attr.colorAccent));
|
||||
|
||||
if (!GlobalUserPreferences.uniformNotificationIcon) {
|
||||
@@ -321,8 +324,8 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
|
||||
CreateStatus.Request req=new CreateStatus.Request();
|
||||
req.status = initialText + input.toString();
|
||||
req.language = preferences.postingDefaultLanguage;
|
||||
req.visibility = preferences.postingDefaultVisibility;
|
||||
req.language = notification.status.language;
|
||||
req.visibility = notification.status.visibility;
|
||||
req.inReplyToId = notification.status.id;
|
||||
|
||||
if (notification.status.hasSpoiler() &&
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
|
||||
public class GetAccountBlocks extends HeaderPaginationRequest<Account>{
|
||||
public GetAccountBlocks(String maxID, int limit){
|
||||
super(HttpMethod.GET, "/blocks", new TypeToken<>(){});
|
||||
if(maxID!=null)
|
||||
addQueryParameter("max_id", maxID);
|
||||
if(limit>0)
|
||||
addQueryParameter("limit", limit+"");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
|
||||
public class GetAccountMutes extends HeaderPaginationRequest<Account>{
|
||||
public GetAccountMutes(String maxID, int limit){
|
||||
super(HttpMethod.GET, "/mutes/", new TypeToken<>(){});
|
||||
if(maxID!=null)
|
||||
addQueryParameter("max_id", maxID);
|
||||
if(limit>0)
|
||||
addQueryParameter("limit", limit+"");
|
||||
}
|
||||
}
|
||||
@@ -6,18 +6,19 @@ import org.joinmastodon.android.model.Preferences;
|
||||
import org.joinmastodon.android.model.StatusPrivacy;
|
||||
|
||||
public class UpdateAccountCredentialsPreferences extends MastodonAPIRequest<Account>{
|
||||
public UpdateAccountCredentialsPreferences(Preferences preferences, Boolean locked, Boolean discoverable){
|
||||
public UpdateAccountCredentialsPreferences(Preferences preferences, Boolean locked, Boolean discoverable, Boolean indexable){
|
||||
super(HttpMethod.PATCH, "/accounts/update_credentials", Account.class);
|
||||
setRequestBody(new Request(locked, discoverable, new RequestSource(preferences.postingDefaultVisibility, preferences.postingDefaultLanguage)));
|
||||
setRequestBody(new Request(locked, discoverable, indexable, new RequestSource(preferences.postingDefaultVisibility, preferences.postingDefaultLanguage)));
|
||||
}
|
||||
|
||||
private static class Request{
|
||||
public Boolean locked, discoverable;
|
||||
public Boolean locked, discoverable, indexable;
|
||||
public RequestSource source;
|
||||
|
||||
public Request(Boolean locked, Boolean discoverable, RequestSource source){
|
||||
public Request(Boolean locked, Boolean discoverable, Boolean indexable, RequestSource source){
|
||||
this.locked=locked;
|
||||
this.discoverable=discoverable;
|
||||
this.indexable=indexable;
|
||||
this.source=source;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ import org.joinmastodon.android.model.FilterContext;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
@Keep
|
||||
class FilterRequest{
|
||||
public String title;
|
||||
public EnumSet<FilterContext> context;
|
||||
|
||||
@@ -4,13 +4,19 @@ import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.SearchResults;
|
||||
|
||||
public class GetSearchResults extends MastodonAPIRequest<SearchResults>{
|
||||
public GetSearchResults(String query, Type type, boolean resolve){
|
||||
public GetSearchResults(String query, Type type, boolean resolve, String maxID, int offset, int count){
|
||||
super(HttpMethod.GET, "/search", SearchResults.class);
|
||||
addQueryParameter("q", query);
|
||||
if(type!=null)
|
||||
addQueryParameter("type", type.name().toLowerCase());
|
||||
if(resolve)
|
||||
addQueryParameter("resolve", "true");
|
||||
if(maxID!=null)
|
||||
addQueryParameter("max_id", maxID);
|
||||
if(offset>0)
|
||||
addQueryParameter("offset", String.valueOf(offset));
|
||||
if(count>0)
|
||||
addQueryParameter("limit", String.valueOf(count));
|
||||
}
|
||||
|
||||
public GetSearchResults limit(int limit){
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package org.joinmastodon.android.api.requests.statuses;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.TranslatedStatus;
|
||||
import org.joinmastodon.android.model.Translation;
|
||||
|
||||
public class TranslateStatus extends MastodonAPIRequest<TranslatedStatus> {
|
||||
public TranslateStatus(String id) {
|
||||
super(HttpMethod.POST, "/statuses/"+id+"/translate", TranslatedStatus.class);
|
||||
setRequestBody(new Object());
|
||||
}
|
||||
import java.util.Map;
|
||||
|
||||
public class TranslateStatus extends MastodonAPIRequest<Translation>{
|
||||
public TranslateStatus(String id, String lang){
|
||||
super(HttpMethod.POST, "/statuses/"+id+"/translate", Translation.class);
|
||||
setRequestBody(Map.of("lang", lang));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.joinmastodon.android.api.requests.tags;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
|
||||
public class GetTag extends MastodonAPIRequest<Hashtag>{
|
||||
public GetTag(String tag){
|
||||
super(HttpMethod.GET, "/tags/"+tag, Hashtag.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.joinmastodon.android.api.requests.tags;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
|
||||
public class SetTagFollowed extends MastodonAPIRequest<Hashtag>{
|
||||
public SetTagFollowed(String tag, boolean followed){
|
||||
super(HttpMethod.POST, "/tags/"+tag+(followed ? "/follow" : "/unfollow"), Hashtag.class);
|
||||
setRequestBody(new Object());
|
||||
}
|
||||
}
|
||||
@@ -219,7 +219,7 @@ public class AccountSession{
|
||||
|
||||
public void savePreferencesIfPending(){
|
||||
if(preferencesNeedSaving){
|
||||
new UpdateAccountCredentialsPreferences(preferences, null, null)
|
||||
new UpdateAccountCredentialsPreferences(preferences, null, self.discoverable, self.source.indexable)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Account result){
|
||||
@@ -303,6 +303,10 @@ public class AccountSession{
|
||||
});
|
||||
}
|
||||
|
||||
public void updateAccountInfo(){
|
||||
AccountSessionManager.getInstance().updateSessionLocalInfo(this);
|
||||
}
|
||||
|
||||
public Optional<Instance> getInstance() {
|
||||
return Optional.ofNullable(AccountSessionManager.getInstance().getInstanceInfo(domain));
|
||||
}
|
||||
|
||||
@@ -303,8 +303,7 @@ public class AccountSessionManager{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void updateSessionLocalInfo(AccountSession session){
|
||||
/*package*/ void updateSessionLocalInfo(AccountSession session){
|
||||
new GetOwnAccount()
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||
import org.joinmastodon.android.api.requests.polls.SubmitPollVote;
|
||||
import org.joinmastodon.android.api.requests.statuses.TranslateStatus;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.events.PollUpdatedEvent;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
@@ -29,7 +30,9 @@ import org.joinmastodon.android.model.DisplayItemsParent;
|
||||
import org.joinmastodon.android.model.Poll;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.Translation;
|
||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||
import org.joinmastodon.android.ui.displayitems.AccountStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.EmojiReactionsStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
||||
@@ -55,6 +58,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -772,6 +776,61 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
assistContent.setWebUri(getWebUri(getSession().getInstanceUri().buildUpon()));
|
||||
}
|
||||
|
||||
public void togglePostTranslation(Status status, String itemID){
|
||||
switch(status.translationState){
|
||||
case LOADING -> {
|
||||
return;
|
||||
}
|
||||
case SHOWN -> {
|
||||
status.translationState=Status.TranslationState.HIDDEN;
|
||||
}
|
||||
case HIDDEN -> {
|
||||
if(status.translation!=null){
|
||||
status.translationState=Status.TranslationState.SHOWN;
|
||||
}else{
|
||||
status.translationState=Status.TranslationState.LOADING;
|
||||
new TranslateStatus(status.getContentStatus().id, Locale.getDefault().getLanguage())
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Translation result){
|
||||
if(getActivity()==null)
|
||||
return;
|
||||
status.translation=result;
|
||||
status.translationState=Status.TranslationState.SHOWN;
|
||||
TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class);
|
||||
if(text!=null){
|
||||
text.updateTranslation(true);
|
||||
imgLoader.bindViewHolder((ImageLoaderRecyclerAdapter) list.getAdapter(), text, text.getAbsoluteAdapterPosition());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
if(getActivity()==null)
|
||||
return;
|
||||
status.translationState=Status.TranslationState.HIDDEN;
|
||||
TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class);
|
||||
if(text!=null){
|
||||
text.updateTranslation(true);
|
||||
}
|
||||
new M3AlertDialogBuilder(getActivity())
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(R.string.translation_failed)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
}
|
||||
}
|
||||
}
|
||||
TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class);
|
||||
if(text!=null){
|
||||
text.updateTranslation(true);
|
||||
imgLoader.bindViewHolder((ImageLoaderRecyclerAdapter) list.getAdapter(), text, text.getAbsoluteAdapterPosition());
|
||||
}
|
||||
}
|
||||
|
||||
public void rebuildAllDisplayItems(){
|
||||
displayItems.clear();
|
||||
for(T item:data){
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.text.TextWatcher;
|
||||
import android.text.format.DateFormat;
|
||||
import android.text.style.BackgroundColorSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
@@ -826,6 +827,14 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
publishButton = wrap.findViewById(R.id.publish_btn);
|
||||
languageButton = wrap.findViewById(R.id.language_btn);
|
||||
languageButton.setOnClickListener(v->showLanguageAlert());
|
||||
languageButton.setOnLongClickListener(v->{
|
||||
languageButton.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
if(!getLocalPrefs().bottomEncoding){
|
||||
getLocalPrefs().bottomEncoding=true;
|
||||
getLocalPrefs().save();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
publishButton.setOnClickListener(v -> {
|
||||
if(GlobalUserPreferences.altTextReminders && editingStatus==null)
|
||||
@@ -1308,7 +1317,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
boolean usePhotoPicker=photoPicker && UiUtils.isPhotoPickerAvailable();
|
||||
if(usePhotoPicker){
|
||||
intent=new Intent(MediaStore.ACTION_PICK_IMAGES);
|
||||
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, mediaViewController.getMaxAttachments()-mediaViewController.getMediaAttachmentsCount());
|
||||
if(mediaViewController.getMaxAttachments()-mediaViewController.getMediaAttachmentsCount()>1)
|
||||
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, mediaViewController.getMaxAttachments()-mediaViewController.getMediaAttachmentsCount());
|
||||
}else{
|
||||
intent=new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
|
||||
@@ -239,16 +239,21 @@ public class EditTimelinesFragment extends MastodonRecyclerFragment<TimelineDefi
|
||||
|
||||
private boolean setTagListContent(NachoTextView editText, @Nullable List<String> tags) {
|
||||
if (tags == null || tags.isEmpty()) return false;
|
||||
editText.setText(String.join(",", tags));
|
||||
editText.chipifyAllUnterminatedTokens();
|
||||
editText.setText(tags);
|
||||
editText.chipifyAllUnterminatedTokens();
|
||||
return true;
|
||||
}
|
||||
|
||||
private NachoTextView prepareChipTextView(NachoTextView nacho) {
|
||||
nacho.addChipTerminator(',', BEHAVIOR_CHIPIFY_ALL);
|
||||
nacho.addChipTerminator('\n', BEHAVIOR_CHIPIFY_ALL);
|
||||
nacho.addChipTerminator(' ', BEHAVIOR_CHIPIFY_ALL);
|
||||
nacho.addChipTerminator(';', BEHAVIOR_CHIPIFY_ALL);
|
||||
//I’ll Be Back
|
||||
nacho.setChipTerminators(
|
||||
Map.of(
|
||||
',', BEHAVIOR_CHIPIFY_ALL,
|
||||
'\n', BEHAVIOR_CHIPIFY_ALL,
|
||||
' ', BEHAVIOR_CHIPIFY_ALL,
|
||||
';', BEHAVIOR_CHIPIFY_ALL
|
||||
)
|
||||
);
|
||||
nacho.enableEditChipOnTouch(true, true);
|
||||
nacho.setOnFocusChangeListener((v, hasFocus) -> nacho.chipifyAllUnterminatedTokens());
|
||||
return nacho;
|
||||
|
||||
@@ -45,8 +45,8 @@ public class FeaturedHashtagsListFragment extends BaseStatusListFragment<Hashtag
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(String hashtag){
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, hashtag, data.stream().filter(h -> Objects.equals(h.name, hashtag)).findAny().map(h -> h.following).orElse(null));
|
||||
public void onItemClick(String id){
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, Objects.requireNonNull(findItemOfType(id, HashtagStatusDisplayItem.class)).tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -121,7 +121,7 @@ public class FollowedHashtagsFragment extends MastodonRecyclerFragment<Hashtag>
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, item.name, item.following);
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, item.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,64 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.res.TypedArray;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.joinmastodon.android.E;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.tags.GetHashtag;
|
||||
import org.joinmastodon.android.api.requests.tags.SetHashtagFollowed;
|
||||
import org.joinmastodon.android.api.MastodonErrorResponse;
|
||||
import org.joinmastodon.android.api.requests.tags.GetTag;
|
||||
import org.joinmastodon.android.api.requests.tags.SetTagFollowed;
|
||||
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
|
||||
import org.joinmastodon.android.events.HashtagUpdatedEvent;
|
||||
import org.joinmastodon.android.model.FilterContext;
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.TimelineDefinition;
|
||||
import org.joinmastodon.android.ui.text.SpacerSpan;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||
import org.joinmastodon.android.ui.views.ProgressBarButton;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
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.api.SimpleCallback;
|
||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||
private String hashtag;
|
||||
public class HashtagTimelineFragment extends PinnableStatusListFragment{
|
||||
private Hashtag hashtag;
|
||||
private String hashtagName;
|
||||
private TextView headerTitle, headerSubtitle;
|
||||
private ProgressBarButton followButton;
|
||||
private ProgressBar followProgress;
|
||||
private MenuItem followMenuItem, pinMenuItem;
|
||||
private boolean followRequestRunning;
|
||||
private boolean toolbarContentVisible;
|
||||
|
||||
private List<String> any;
|
||||
private List<String> all;
|
||||
private List<String> none;
|
||||
private boolean following;
|
||||
private boolean localOnly;
|
||||
private MenuItem followButton;
|
||||
private Menu optionsMenu;
|
||||
private MenuInflater optionsMenuInflater;
|
||||
|
||||
@Override
|
||||
protected boolean wantsComposeButton() {
|
||||
@@ -50,73 +68,19 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||
@Override
|
||||
public void onAttach(Activity activity){
|
||||
super.onAttach(activity);
|
||||
updateTitle(getArguments().getString("hashtag"));
|
||||
following=getArguments().getBoolean("following", false);
|
||||
localOnly=getArguments().getBoolean("localOnly", false);
|
||||
any=getArguments().getStringArrayList("any");
|
||||
all=getArguments().getStringArrayList("all");
|
||||
none=getArguments().getStringArrayList("none");
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
private void updateTitle(String hashtagName) {
|
||||
hashtag = hashtagName;
|
||||
setTitle('#'+hashtag);
|
||||
}
|
||||
|
||||
private void updateFollowingState(boolean newFollowing) {
|
||||
this.following = newFollowing;
|
||||
followButton.setTitle(getString(newFollowing ? R.string.unfollow_user : R.string.follow_user, "#" + hashtag));
|
||||
followButton.setIcon(newFollowing ? R.drawable.ic_fluent_person_delete_24_filled : R.drawable.ic_fluent_person_add_24_regular);
|
||||
E.post(new HashtagUpdatedEvent(hashtag, following));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.hashtag_timeline, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
followButton = menu.findItem(R.id.follow_hashtag);
|
||||
updateFollowingState(following);
|
||||
|
||||
new GetHashtag(hashtag).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(Hashtag hashtag) {
|
||||
if (getActivity() == null) return;
|
||||
updateTitle(hashtag.name);
|
||||
updateFollowingState(hashtag.following);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error) {
|
||||
error.showToast(getActivity());
|
||||
}
|
||||
}).exec(accountID);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (super.onOptionsItemSelected(item)) return true;
|
||||
if (item.getItemId() == R.id.follow_hashtag) {
|
||||
updateFollowingState(!following);
|
||||
getToolbar().performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
||||
new SetHashtagFollowed(hashtag, following).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(Hashtag i) {
|
||||
if (getActivity() == null) return;
|
||||
if (i.following == following) Toast.makeText(getActivity(), getString(i.following ? R.string.followed_user : R.string.unfollowed_user, "#" + i.name), Toast.LENGTH_SHORT).show();
|
||||
updateFollowingState(i.following);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error) {
|
||||
error.showToast(getActivity());
|
||||
updateFollowingState(!following);
|
||||
}
|
||||
}).exec(accountID);
|
||||
return true;
|
||||
if(getArguments().containsKey("hashtag")){
|
||||
hashtag=Parcels.unwrap(getArguments().getParcelable("hashtag"));
|
||||
hashtagName=hashtag.name;
|
||||
}else{
|
||||
hashtagName=getArguments().getString("hashtagName");
|
||||
}
|
||||
return false;
|
||||
setTitle('#'+hashtagName);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -126,12 +90,10 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){
|
||||
currentRequest=new GetHashtagTimeline(hashtag, offset==0 ? null : getMaxID(), null, count, any, all, none, localOnly, getLocalPrefs().timelineReplyVisibility)
|
||||
currentRequest=new GetHashtagTimeline(hashtagName, offset==0 ? null : getMaxID(), null, count, any, all, none, localOnly, getLocalPrefs().timelineReplyVisibility)
|
||||
.setCallback(new SimpleCallback<>(this){
|
||||
@Override
|
||||
public void onSuccess(List<Status> result){
|
||||
if (getActivity() == null) return;
|
||||
result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList());
|
||||
onDataLoaded(result, !result.isEmpty());
|
||||
}
|
||||
})
|
||||
@@ -146,15 +108,40 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFabLongClick(View v) {
|
||||
return UiUtils.pickAccountForCompose(getActivity(), accountID, '#'+hashtag+' ');
|
||||
public void loadData(){
|
||||
reloadTag();
|
||||
super.loadData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
fab=view.findViewById(R.id.fab);
|
||||
fab.setOnClickListener(this::onFabClick);
|
||||
|
||||
if(getParentFragment() instanceof HomeTabFragment) return;
|
||||
|
||||
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
|
||||
View topChild=recyclerView.getChildAt(0);
|
||||
int firstChildPos=recyclerView.getChildAdapterPosition(topChild);
|
||||
float newAlpha=firstChildPos>0 ? 1f : Math.min(1f, -topChild.getTop()/(float)headerTitle.getHeight());
|
||||
toolbarTitleView.setAlpha(newAlpha);
|
||||
boolean newToolbarVisibility=newAlpha>0.5f;
|
||||
if(newToolbarVisibility!=toolbarContentVisible){
|
||||
toolbarContentVisible=newToolbarVisibility;
|
||||
createOptionsMenu();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFabClick(View v){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putString("prefilledText", '#'+hashtag+' ');
|
||||
args.putString("prefilledText", '#'+hashtagName+' ');
|
||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||
}
|
||||
|
||||
@@ -170,6 +157,204 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||
|
||||
@Override
|
||||
public Uri getWebUri(Uri.Builder base) {
|
||||
return base.path((isInstanceAkkoma() ? "/tag/" : "/tags") + hashtag).build();
|
||||
return base.path((isInstanceAkkoma() ? "/tag/" : "/tags/") + hashtag).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecyclerView.Adapter getAdapter(){
|
||||
View header=getActivity().getLayoutInflater().inflate(R.layout.header_hashtag_timeline, list, false);
|
||||
headerTitle=header.findViewById(R.id.title);
|
||||
headerSubtitle=header.findViewById(R.id.subtitle);
|
||||
followButton=header.findViewById(R.id.profile_action_btn);
|
||||
followProgress=header.findViewById(R.id.action_progress);
|
||||
|
||||
headerTitle.setText("#"+hashtagName);
|
||||
followButton.setVisibility(View.GONE);
|
||||
followButton.setOnClickListener(v->{
|
||||
if(hashtag==null)
|
||||
return;
|
||||
setFollowed(!hashtag.following);
|
||||
});
|
||||
followButton.setOnLongClickListener(v->{
|
||||
if(hashtag==null) return false;
|
||||
UiUtils.pickAccount(getActivity(), accountID, R.string.sk_follow_as, R.drawable.ic_fluent_person_add_28_regular, session -> {
|
||||
new SetTagFollowed(hashtagName, true).setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Hashtag hashtag) {
|
||||
Toast.makeText(
|
||||
getActivity(),
|
||||
getString(R.string.sk_followed_as, session.self.getShortUsername()),
|
||||
Toast.LENGTH_SHORT
|
||||
).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error) {
|
||||
error.showToast(getActivity());
|
||||
}
|
||||
}).exec(session.getID());
|
||||
}, null);
|
||||
return true;
|
||||
});
|
||||
updateHeader();
|
||||
|
||||
MergeRecyclerAdapter mergeAdapter=new MergeRecyclerAdapter();
|
||||
if(!(getParentFragment() instanceof HomeTabFragment)){
|
||||
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(header));
|
||||
}
|
||||
mergeAdapter.addAdapter(super.getAdapter());
|
||||
return mergeAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMainAdapterOffset(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
private void createOptionsMenu(){
|
||||
optionsMenu.clear();
|
||||
optionsMenuInflater.inflate(R.menu.hashtag_timeline, optionsMenu);
|
||||
followMenuItem=optionsMenu.findItem(R.id.follow_hashtag);
|
||||
pinMenuItem=optionsMenu.findItem(R.id.pin);
|
||||
followMenuItem.setVisible(toolbarContentVisible);
|
||||
followMenuItem.setTitle(getString(hashtag.following ? R.string.unfollow_user : R.string.follow_user, "#"+hashtagName));
|
||||
followMenuItem.setIcon(hashtag.following ? R.drawable.ic_fluent_person_delete_24_filled : R.drawable.ic_fluent_person_add_24_regular);
|
||||
pinMenuItem.setShowAsAction(toolbarContentVisible ? MenuItem.SHOW_AS_ACTION_NEVER : MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
super.updatePinButton(pinMenuItem);
|
||||
if(toolbarContentVisible){
|
||||
UiUtils.enableOptionsMenuIcons(getContext(), optionsMenu);
|
||||
}else{
|
||||
UiUtils.enableOptionsMenuIcons(getContext(), optionsMenu, R.id.pin);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePinButton(MenuItem pin){
|
||||
super.updatePinButton(pin);
|
||||
if(toolbarContentVisible) UiUtils.insetPopupMenuIcon(getContext(), pin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
||||
inflater.inflate(R.menu.hashtag_timeline, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
optionsMenu=menu;
|
||||
optionsMenuInflater=inflater;
|
||||
createOptionsMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item){
|
||||
if (super.onOptionsItemSelected(item)) return true;
|
||||
if (item.getItemId() == R.id.follow_hashtag && hashtag!=null) {
|
||||
setFollowed(!hashtag.following);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUpdateToolbar(){
|
||||
super.onUpdateToolbar();
|
||||
toolbarTitleView.setAlpha(toolbarContentVisible ? 1f : 0f);
|
||||
createOptionsMenu();
|
||||
}
|
||||
|
||||
private void updateHeader(){
|
||||
if(hashtag==null)
|
||||
return;
|
||||
|
||||
if(hashtag.history!=null && !hashtag.history.isEmpty()){
|
||||
int weekPosts=hashtag.history.stream().mapToInt(h->h.uses).sum();
|
||||
int todayPosts=hashtag.history.get(0).uses;
|
||||
int numAccounts=hashtag.history.stream().mapToInt(h->h.accounts).sum();
|
||||
int hSpace=V.dp(8);
|
||||
SpannableStringBuilder ssb=new SpannableStringBuilder();
|
||||
ssb.append(getResources().getQuantityString(R.plurals.x_posts, weekPosts, weekPosts));
|
||||
ssb.append(" ", new SpacerSpan(hSpace, 0), 0);
|
||||
ssb.append('·');
|
||||
ssb.append(" ", new SpacerSpan(hSpace, 0), 0);
|
||||
ssb.append(getResources().getQuantityString(R.plurals.x_participants, numAccounts, numAccounts));
|
||||
ssb.append(" ", new SpacerSpan(hSpace, 0), 0);
|
||||
ssb.append('·');
|
||||
ssb.append(" ", new SpacerSpan(hSpace, 0), 0);
|
||||
ssb.append(getResources().getQuantityString(R.plurals.x_posts_today, todayPosts, todayPosts));
|
||||
headerSubtitle.setText(ssb);
|
||||
}
|
||||
|
||||
int styleRes;
|
||||
followButton.setVisibility(View.VISIBLE);
|
||||
if(hashtag.following){
|
||||
followButton.setText(R.string.button_following);
|
||||
styleRes=R.style.Widget_Mastodon_M3_Button_Tonal;
|
||||
}else{
|
||||
followButton.setText(R.string.button_follow);
|
||||
styleRes=R.style.Widget_Mastodon_M3_Button_Filled;
|
||||
}
|
||||
TypedArray ta=followButton.getContext().obtainStyledAttributes(styleRes, new int[]{android.R.attr.background});
|
||||
followButton.setBackground(ta.getDrawable(0));
|
||||
ta.recycle();
|
||||
ta=followButton.getContext().obtainStyledAttributes(styleRes, new int[]{android.R.attr.textColor});
|
||||
followButton.setTextColor(ta.getColorStateList(0));
|
||||
followProgress.setIndeterminateTintList(ta.getColorStateList(0));
|
||||
ta.recycle();
|
||||
|
||||
followButton.setTextVisible(true);
|
||||
followProgress.setVisibility(View.GONE);
|
||||
if(followMenuItem!=null){
|
||||
followMenuItem.setTitle(getString(hashtag.following ? R.string.unfollow_user : R.string.follow_user, "#"+hashtagName));
|
||||
followMenuItem.setIcon(hashtag.following ? R.drawable.ic_fluent_person_delete_24_filled : R.drawable.ic_fluent_person_add_24_regular);
|
||||
UiUtils.insetPopupMenuIcon(getContext(), followMenuItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadTag(){
|
||||
new GetTag(hashtagName)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Hashtag result){
|
||||
hashtag=result;
|
||||
updateHeader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
}
|
||||
|
||||
private void setFollowed(boolean followed){
|
||||
if(followRequestRunning)
|
||||
return;
|
||||
getToolbar().performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
||||
followButton.setTextVisible(false);
|
||||
followProgress.setVisibility(View.VISIBLE);
|
||||
followRequestRunning=true;
|
||||
new SetTagFollowed(hashtagName, followed)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Hashtag result){
|
||||
if(getActivity()==null)
|
||||
return;
|
||||
hashtag=result;
|
||||
updateHeader();
|
||||
followRequestRunning=false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
if(getActivity()==null)
|
||||
return;
|
||||
if(error instanceof MastodonErrorResponse er && "Duplicate record".equals(er.error)){
|
||||
hashtag.following=true;
|
||||
}else{
|
||||
error.showToast(getActivity());
|
||||
}
|
||||
updateHeader();
|
||||
followRequestRunning=false;
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,6 +184,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
});
|
||||
}
|
||||
}
|
||||
tabBar.selectTab(currentTab);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
@@ -524,9 +524,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||
if (list.repliesPolicy != null) args.putInt("repliesPolicy", list.repliesPolicy.ordinal());
|
||||
Nav.go(getActivity(), ListTimelineFragment.class, args);
|
||||
} else if ((hashtag = hashtagsItems.get(id)) != null) {
|
||||
args.putString("hashtag", hashtag.name);
|
||||
args.putBoolean("following", hashtag.following);
|
||||
Nav.go(getActivity(), HashtagTimelineFragment.class, args);
|
||||
UiUtils.openHashtagTimeline(getContext(), accountID, hashtag);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.transition.ChangeBounds;
|
||||
import android.transition.Fade;
|
||||
import android.transition.TransitionManager;
|
||||
@@ -60,8 +59,10 @@ import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
||||
import org.joinmastodon.android.api.requests.accounts.UpdateAccountCredentials;
|
||||
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.account_list.BlockedAccountsListFragment;
|
||||
import org.joinmastodon.android.fragments.account_list.FollowerListFragment;
|
||||
import org.joinmastodon.android.fragments.account_list.FollowingListFragment;
|
||||
import org.joinmastodon.android.fragments.account_list.MutedAccountsListFragment;
|
||||
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
||||
import org.joinmastodon.android.fragments.settings.SettingsServerFragment;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
@@ -304,6 +305,12 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
|
||||
tabbar.setTabTextColors(UiUtils.getThemeColor(getActivity(), R.attr.colorM3OnSurfaceVariant), UiUtils.getThemeColor(getActivity(), R.attr.colorM3Primary));
|
||||
tabbar.setTabTextSize(V.dp(14));
|
||||
tabLayoutMediator=new TabLayoutMediator(tabbar, pager, (tab, position)->tab.setText(switch(position){
|
||||
case 0 -> R.string.profile_featured;
|
||||
case 1 -> R.string.profile_timeline;
|
||||
case 2 -> R.string.profile_about;
|
||||
default -> throw new IllegalStateException();
|
||||
}));
|
||||
tabLayoutMediator=new TabLayoutMediator(tabbar, pager, new TabLayoutMediator.TabConfigurationStrategy(){
|
||||
@Override
|
||||
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position){
|
||||
@@ -317,6 +324,19 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
if (position == 4) tab.view.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
tabbar.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab){}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab){}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab){
|
||||
if(getFragmentForPage(tab.getPosition()) instanceof ScrollableToTop stt)
|
||||
stt.scrollToTop();
|
||||
}
|
||||
});
|
||||
|
||||
cover.setOutlineProvider(new ViewOutlineProvider(){
|
||||
@Override
|
||||
@@ -827,6 +847,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
args.putString("profileDisplayUsername", account.getDisplayUsername());
|
||||
}
|
||||
Nav.go(getActivity(), ListsFragment.class, args);
|
||||
}else if(id==R.id.muted_accounts){
|
||||
final Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putParcelable("targetAccount", Parcels.wrap(account));
|
||||
Nav.go(getActivity(), MutedAccountsListFragment.class, args);
|
||||
}else if(id==R.id.blocked_accounts){
|
||||
final Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putParcelable("targetAccount", Parcels.wrap(account));
|
||||
Nav.go(getActivity(), BlockedAccountsListFragment.class, args);
|
||||
}else if(id==R.id.followed_hashtags){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
@@ -1304,9 +1334,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
@NonNull
|
||||
@Override
|
||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
FrameLayout view=tabViews[viewType];
|
||||
if (view.getParent() != null) ((ViewGroup)view.getParent()).removeView(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
FrameLayout view=new FrameLayout(parent.getContext());
|
||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
return new SimpleViewHolder(view);
|
||||
}
|
||||
@@ -1314,8 +1342,13 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){
|
||||
Fragment fragment=getFragmentForPage(position);
|
||||
FrameLayout fragmentView=tabViews[position];
|
||||
fragmentView.setVisibility(View.VISIBLE);
|
||||
if(fragmentView.getParent() instanceof ViewGroup parent)
|
||||
parent.removeView(fragmentView);
|
||||
((FrameLayout)holder.itemView).addView(fragmentView);
|
||||
if(!fragment.isAdded()){
|
||||
getChildFragmentManager().beginTransaction().add(holder.itemView.getId(), fragment).commit();
|
||||
getChildFragmentManager().beginTransaction().add(fragmentView.getId(), fragment).commit();
|
||||
holder.itemView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
||||
@Override
|
||||
public boolean onPreDraw(){
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.SpoilerStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.WarningFilteredStatusDisplayItem;
|
||||
@@ -105,6 +106,12 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
|
||||
text.textSelectable=true;
|
||||
else if(item instanceof FooterStatusDisplayItem footer)
|
||||
footer.hideCounts=true;
|
||||
else if(item instanceof SpoilerStatusDisplayItem spoiler){
|
||||
for(StatusDisplayItem subItem:spoiler.contentItems){
|
||||
if(subItem instanceof TextStatusDisplayItem text)
|
||||
text.textSelectable=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.joinmastodon.android.fragments.account_list;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetAccountBlocks;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.ui.viewholders.AccountViewHolder;
|
||||
|
||||
public class BlockedAccountsListFragment extends AccountRelatedAccountListFragment{
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
setTitle(R.string.sk_blocked_accounts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
||||
return new GetAccountBlocks(maxID, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigureViewHolder(AccountViewHolder holder){
|
||||
super.onConfigureViewHolder(holder);
|
||||
holder.setStyle(AccountViewHolder.AccessoryType.NONE, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getWebUri(Uri.Builder base) {
|
||||
return super.getWebUri(base).buildUpon()
|
||||
.appendPath("/blocks").build();
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ public class ComposeAccountSearchFragment extends BaseAccountListFragment{
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){
|
||||
refreshing=true;
|
||||
currentRequest=new GetSearchResults(currentQuery, GetSearchResults.Type.ACCOUNTS, false)
|
||||
currentRequest=new GetSearchResults(currentQuery, GetSearchResults.Type.ACCOUNTS, false, null, 0, 0)
|
||||
.setCallback(new SimpleCallback<>(this){
|
||||
@Override
|
||||
public void onSuccess(SearchResults result){
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.joinmastodon.android.fragments.account_list;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetAccountMutes;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.ui.viewholders.AccountViewHolder;
|
||||
|
||||
public class MutedAccountsListFragment extends AccountRelatedAccountListFragment{
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
setTitle(R.string.sk_muted_accounts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
||||
return new GetAccountMutes(maxID, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigureViewHolder(AccountViewHolder holder){
|
||||
super.onConfigureViewHolder(holder);
|
||||
holder.setStyle(AccountViewHolder.AccessoryType.NONE, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getWebUri(Uri.Builder base) {
|
||||
return super.getWebUri(base).buildUpon()
|
||||
.appendPath("/mutes").build();
|
||||
}
|
||||
}
|
||||
@@ -289,15 +289,19 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
@NonNull
|
||||
@Override
|
||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
FrameLayout view=tabViews[viewType];
|
||||
((ViewGroup)view.getParent()).removeView(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
FrameLayout view=new FrameLayout(parent.getContext());
|
||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
return new SimpleViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){}
|
||||
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){
|
||||
FrameLayout view=tabViews[position];
|
||||
if(view.getParent() instanceof ViewGroup parent)
|
||||
parent.removeView(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
((FrameLayout)holder.itemView).addView(view, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount(){
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.displayitems.AccountStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.HashtagStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.tabs.TabLayout;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
@@ -31,7 +30,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.grishka.appkit.Nav;
|
||||
@@ -97,7 +96,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||
args.putParcelable("profileAccount", Parcels.wrap(res.account));
|
||||
Nav.go(getActivity(), ProfileFragment.class, args);
|
||||
}
|
||||
case HASHTAG -> UiUtils.openHashtagTimeline(getActivity(), accountID, res.hashtag.name, res.hashtag.following);
|
||||
case HASHTAG -> UiUtils.openHashtagTimeline(getActivity(), accountID, res.hashtag);
|
||||
case STATUS -> {
|
||||
Status status=res.status.getContentStatus();
|
||||
Bundle args=new Bundle();
|
||||
@@ -113,7 +112,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){
|
||||
protected void doLoadData(int _offset, int count){
|
||||
GetSearchResults.Type type;
|
||||
if(currentFilter.size()==1){
|
||||
type=switch(currentFilter.iterator().next()){
|
||||
@@ -128,7 +127,21 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||
dataLoaded();
|
||||
return;
|
||||
}
|
||||
currentRequest=new GetSearchResults(currentQuery, type, true)
|
||||
String maxID=null;
|
||||
// TODO server-side bug
|
||||
/*int offset=0;
|
||||
if(_offset>0){
|
||||
if(type==GetSearchResults.Type.STATUSES){
|
||||
if(!preloadedData.isEmpty())
|
||||
maxID=preloadedData.get(preloadedData.size()-1).status.id;
|
||||
else if(!data.isEmpty())
|
||||
maxID=data.get(data.size()-1).status.id;
|
||||
}else{
|
||||
offset=_offset;
|
||||
}
|
||||
}*/
|
||||
int offset=_offset;
|
||||
currentRequest=new GetSearchResults(currentQuery, type, type==null, maxID, offset, type==null ? 0 : count)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(SearchResults result){
|
||||
@@ -142,12 +155,15 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||
results.add(new SearchResult(tag));
|
||||
}
|
||||
if(result.statuses!=null){
|
||||
for(Status status:result.statuses)
|
||||
results.add(new SearchResult(status));
|
||||
Set<String> alreadyLoadedStatuses=data.stream().filter(r->r.type==SearchResult.Type.STATUS).map(r->r.status.id).collect(Collectors.toSet());
|
||||
for(Status status:result.statuses){
|
||||
if(!alreadyLoadedStatuses.contains(status.id))
|
||||
results.add(new SearchResult(status));
|
||||
}
|
||||
}
|
||||
prevDisplayItems=new ArrayList<>(displayItems);
|
||||
unfilteredResults=results;
|
||||
onDataLoaded(filterSearchResults(results), false);
|
||||
onDataLoaded(filterSearchResults(results), type!=null && !results.isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -122,7 +122,7 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
|
||||
recentsHeader.setVisible(!data.isEmpty());
|
||||
});
|
||||
}else{
|
||||
currentRequest=new GetSearchResults(currentQuery, null, false)
|
||||
currentRequest=new GetSearchResults(currentQuery, null, false, null, 0, 0)
|
||||
.limit(2)
|
||||
.setCallback(new SimpleCallback<>(this){
|
||||
@Override
|
||||
@@ -378,7 +378,7 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
|
||||
|
||||
private void openHashtag(SearchResult res){
|
||||
wrapSuicideDialog(()->{
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, res.hashtag.name, res.hashtag.following);
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, res.hashtag);
|
||||
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putRecentSearch(res);
|
||||
});
|
||||
}
|
||||
@@ -424,6 +424,8 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
|
||||
}
|
||||
|
||||
private void onSearchViewEnter(){
|
||||
if(TextUtils.isEmpty(currentQuery) || currentQuery.trim().isEmpty())
|
||||
return;
|
||||
wrapSuicideDialog(()->deliverResult(currentQuery, null));
|
||||
}
|
||||
|
||||
@@ -436,7 +438,7 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
|
||||
String q=searchViewHelper.getQuery();
|
||||
if(q.startsWith("#"))
|
||||
q=q.substring(1);
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, q, null);
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, q);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag> impl
|
||||
|
||||
@Override
|
||||
public void onClick(){
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, item.name, item.following);
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,9 +165,7 @@ public class AccountActivationFragment extends ToolbarFragment{
|
||||
private void tryGetAccount(){
|
||||
if(AccountSessionManager.getInstance().tryGetAccount(accountID)==null){
|
||||
uiHandler.removeCallbacks(pollRunnable);
|
||||
getActivity().finish();
|
||||
Intent intent=new Intent(getActivity(), MainActivity.class);
|
||||
startActivity(intent);
|
||||
((MainActivity)getActivity()).restartHomeFragment();
|
||||
return;
|
||||
}
|
||||
currentRequest=new GetOwnAccount()
|
||||
|
||||
@@ -271,7 +271,7 @@ public class SignupFragment extends ToolbarFragment{
|
||||
@Override
|
||||
public void tail(Node node, int depth){
|
||||
if(node instanceof Element){
|
||||
ssb.setSpan(new LinkSpan("", SignupFragment.this::onGoBackLinkClick, LinkSpan.Type.CUSTOM, null), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
ssb.setSpan(new LinkSpan("", SignupFragment.this::onGoBackLinkClick, LinkSpan.Type.CUSTOM, null, null, null), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
ssb.setSpan(new TypefaceSpan("sans-serif-medium"), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public class SettingsAboutAppFragment extends BaseSettingsFragment<Void>{
|
||||
setTitle(getString(R.string.about_app, getString(R.string.sk_app_name)));
|
||||
AccountSession s=AccountSessionManager.get(accountID);
|
||||
onDataLoaded(List.of(
|
||||
new ListItem<>(R.string.sk_settings_donate, 0, R.drawable.ic_fluent_heart_24_regular, ()->UiUtils.openHashtagTimeline(getActivity(), accountID, getString(R.string.donate_hashtag), null)),
|
||||
new ListItem<>(R.string.sk_settings_donate, 0, R.drawable.ic_fluent_heart_24_regular, ()->UiUtils.openHashtagTimeline(getActivity(), accountID, getString(R.string.donate_hashtag))),
|
||||
new ListItem<>(R.string.sk_settings_contribute, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), getString(R.string.repo_url))),
|
||||
new ListItem<>(R.string.settings_tos, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+s.domain+"/terms")),
|
||||
new ListItem<>(R.string.settings_privacy_policy, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), getString(R.string.privacy_policy_url)), 0, true),
|
||||
|
||||
@@ -55,6 +55,8 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
||||
onDataLoaded(List.of(
|
||||
new ListItem<>(R.string.settings_behavior, 0, R.drawable.ic_fluent_settings_24_regular, this::onBehaviorClick),
|
||||
new ListItem<>(R.string.settings_display, 0, R.drawable.ic_fluent_color_24_regular, this::onDisplayClick),
|
||||
new ListItem<>(R.string.settings_privacy, 0, R.drawable.ic_privacy_tip_24px, this::onPrivacyClick),
|
||||
new ListItem<>(R.string.settings_filters, 0, R.drawable.ic_filter_alt_24px, this::onFiltersClick),
|
||||
new ListItem<>(R.string.settings_notifications, 0, R.drawable.ic_fluent_alert_24_regular, this::onNotificationsClick),
|
||||
new ListItem<>(R.string.sk_settings_instance, 0, R.drawable.ic_fluent_server_24_regular, this::onInstanceClick),
|
||||
new ListItem<>(getString(R.string.about_app, getString(R.string.sk_app_name)), null, R.drawable.ic_fluent_info_24_regular, this::onAboutClick, null, 0, true),
|
||||
@@ -69,7 +71,9 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
||||
data.add(0, new ListItem<>("Debug settings", null, R.drawable.ic_fluent_wrench_screwdriver_24_regular, ()->Nav.go(getActivity(), SettingsDebugFragment.class, makeFragmentArgs()), null, 0, true));
|
||||
}
|
||||
|
||||
account.reloadPreferences(null);
|
||||
AccountSession session=AccountSessionManager.get(accountID);
|
||||
session.reloadPreferences(null);
|
||||
session.updateAccountInfo();
|
||||
E.register(this);
|
||||
}
|
||||
|
||||
@@ -133,6 +137,10 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
||||
Nav.go(getActivity(), SettingsDisplayFragment.class, makeFragmentArgs());
|
||||
}
|
||||
|
||||
private void onPrivacyClick(){
|
||||
Nav.go(getActivity(), SettingsPrivacyFragment.class, makeFragmentArgs());
|
||||
}
|
||||
|
||||
private void onFiltersClick(){
|
||||
Nav.go(getActivity(), SettingsFiltersFragment.class, makeFragmentArgs());
|
||||
}
|
||||
@@ -155,9 +163,7 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
||||
.setMessage(getString(R.string.confirm_log_out, session.getFullUsername()))
|
||||
.setPositiveButton(R.string.log_out, (dialog, which)->account.logOut(getActivity(), ()->{
|
||||
loggedOut=true;
|
||||
getActivity().finish();
|
||||
Intent intent=new Intent(getActivity(), MainActivity.class);
|
||||
startActivity(intent);
|
||||
((MainActivity)getActivity()).restartHomeFragment();
|
||||
}))
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.joinmastodon.android.fragments.settings;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.viewmodel.CheckableListItem;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SettingsPrivacyFragment extends BaseSettingsFragment<Void>{
|
||||
private CheckableListItem<Void> discoverableItem, indexableItem;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
setTitle(R.string.settings_privacy);
|
||||
Account self=AccountSessionManager.get(accountID).self;
|
||||
onDataLoaded(List.of(
|
||||
discoverableItem=new CheckableListItem<>(R.string.settings_discoverable, 0, CheckableListItem.Style.SWITCH, self.discoverable, R.drawable.ic_thumbs_up_down_24px, ()->toggleCheckableItem(discoverableItem)),
|
||||
indexableItem=new CheckableListItem<>(R.string.settings_indexable, 0, CheckableListItem.Style.SWITCH, self.source.indexable!=null ? self.source.indexable : true, R.drawable.ic_search_24px, ()->toggleCheckableItem(indexableItem))
|
||||
));
|
||||
if(self.source.indexable==null)
|
||||
indexableItem.isEnabled=false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){}
|
||||
|
||||
@Override
|
||||
public void onPause(){
|
||||
super.onPause();
|
||||
Account self=AccountSessionManager.get(accountID).self;
|
||||
if(self.discoverable!=discoverableItem.checked || (self.source.indexable!=null && self.source.indexable!=indexableItem.checked)){
|
||||
self.discoverable=discoverableItem.checked;
|
||||
self.source.indexable=indexableItem.checked;
|
||||
AccountSessionManager.get(accountID).savePreferencesLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,18 +153,21 @@ public class SettingsServerFragment extends AppKitFragment{
|
||||
@NonNull
|
||||
@Override
|
||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
FrameLayout view=tabViews[viewType];
|
||||
((ViewGroup)view.getParent()).removeView(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
FrameLayout view=new FrameLayout(parent.getContext());
|
||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
return new SimpleViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){
|
||||
FrameLayout view=tabViews[position];
|
||||
if(view.getParent() instanceof ViewGroup parent)
|
||||
parent.removeView(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
((FrameLayout)holder.itemView).addView(view, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
Fragment fragment=getFragmentForPage(position);
|
||||
if(!fragment.isAdded()){
|
||||
getChildFragmentManager().beginTransaction().add(holder.itemView.getId(), fragment).commit();
|
||||
getChildFragmentManager().beginTransaction().add(view.getId(), fragment).commit();
|
||||
holder.itemView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
||||
@Override
|
||||
public boolean onPreDraw(){
|
||||
|
||||
@@ -139,6 +139,7 @@ public class Account extends BaseModel implements Searchable{
|
||||
* When a timed mute will expire, if applicable.
|
||||
*/
|
||||
public Instant muteExpiresAt;
|
||||
public boolean noindex;
|
||||
|
||||
public List<Role> roles;
|
||||
|
||||
@@ -238,6 +239,7 @@ public class Account extends BaseModel implements Searchable{
|
||||
", source="+source+
|
||||
", suspended="+suspended+
|
||||
", muteExpiresAt="+muteExpiresAt+
|
||||
", noindex="+noindex+
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,26 +46,26 @@ public class Attachment extends BaseModel{
|
||||
|
||||
public int getWidth(){
|
||||
if(meta==null)
|
||||
return 0;
|
||||
return 1920;
|
||||
if(meta.width>0)
|
||||
return meta.width;
|
||||
if(meta.original!=null && meta.original.width>0)
|
||||
return meta.original.width;
|
||||
if(meta.small!=null && meta.small.width>0)
|
||||
return meta.small.width;
|
||||
return 0;
|
||||
return 1920;
|
||||
}
|
||||
|
||||
public int getHeight(){
|
||||
if(meta==null)
|
||||
return 0;
|
||||
return 1080;
|
||||
if(meta.height>0)
|
||||
return meta.height;
|
||||
if(meta.original!=null && meta.original.height>0)
|
||||
return meta.original.height;
|
||||
if(meta.small!=null && meta.small.height>0)
|
||||
return meta.small.height;
|
||||
return 0;
|
||||
return 1080;
|
||||
}
|
||||
|
||||
public double getDuration(){
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class EmojiCategory{
|
||||
@@ -10,4 +11,8 @@ public class EmojiCategory{
|
||||
this.title=title;
|
||||
this.emojis=emojis;
|
||||
}
|
||||
public EmojiCategory(EmojiCategory category){
|
||||
this.title = category.title;
|
||||
this.emojis = new ArrayList<>(category.emojis);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ public class Hashtag extends BaseModel implements DisplayItemsParent{
|
||||
", following="+following+
|
||||
", history="+history+
|
||||
", statusesCount="+statusesCount+
|
||||
", following="+following+
|
||||
'}';
|
||||
}
|
||||
|
||||
@@ -30,4 +31,19 @@ public class Hashtag extends BaseModel implements DisplayItemsParent{
|
||||
public String getID(){
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o){
|
||||
if(this==o) return true;
|
||||
if(o==null || getClass()!=o.getClass()) return false;
|
||||
|
||||
Hashtag hashtag=(Hashtag) o;
|
||||
|
||||
return name.equals(hashtag.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(){
|
||||
return name.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,22 @@ public class Mention extends BaseModel{
|
||||
", url='"+url+'\''+
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o){
|
||||
if(this==o) return true;
|
||||
if(o==null || getClass()!=o.getClass()) return false;
|
||||
|
||||
Mention mention=(Mention) o;
|
||||
|
||||
if(!id.equals(mention.id)) return false;
|
||||
return url.equals(mention.url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(){
|
||||
int result=id.hashCode();
|
||||
result=31*result+url.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ public class Source extends BaseModel{
|
||||
* The number of pending follow requests.
|
||||
*/
|
||||
public int followRequestCount;
|
||||
public Boolean indexable;
|
||||
public boolean hideCollections;
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
@@ -54,6 +56,8 @@ public class Source extends BaseModel{
|
||||
", sensitive="+sensitive+
|
||||
", language='"+language+'\''+
|
||||
", followRequestCount="+followRequestCount+
|
||||
", indexable="+indexable+
|
||||
", hideCollections="+hideCollections+
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,21 @@ import static org.joinmastodon.android.api.MastodonAPIController.gsonWithoutDese
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.joinmastodon.android.api.ObjectValidationException;
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.github.bottomSoftwareFoundation.bottom.Bottom;
|
||||
import com.github.bottomSoftwareFoundation.bottom.TranslationError;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
@@ -15,16 +28,21 @@ import com.google.gson.JsonParseException;
|
||||
|
||||
import org.joinmastodon.android.api.ObjectValidationException;
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.events.EmojiReactionsUpdatedEvent;
|
||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.joinmastodon.android.utils.StatusTextEncoder;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Parcel
|
||||
public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
||||
@@ -83,9 +101,9 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
||||
public transient boolean sensitiveRevealed;
|
||||
public transient boolean textExpanded, textExpandable;
|
||||
public transient boolean hasGapAfter;
|
||||
public transient TranslatedStatus translation;
|
||||
public transient boolean translationShown;
|
||||
private transient String strippedText;
|
||||
public transient TranslationState translationState=TranslationState.HIDDEN;
|
||||
public transient Translation translation;
|
||||
|
||||
public Status(){}
|
||||
|
||||
@@ -201,6 +219,38 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
||||
return (Status) super.clone();
|
||||
}
|
||||
|
||||
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");
|
||||
public boolean isEligibleForTranslation(AccountSession session){
|
||||
Instance instanceInfo = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
|
||||
boolean translateEnabled = instanceInfo != null &&
|
||||
instanceInfo.v2 != null && instanceInfo.v2.configuration.translation != null &&
|
||||
instanceInfo.v2.configuration.translation.enabled;
|
||||
|
||||
try {
|
||||
String bottomText = BOTTOM_TEXT_PATTERN.matcher(getStrippedText()).find()
|
||||
? new StatusTextEncoder(Bottom::decode).decode(getStrippedText(), BOTTOM_TEXT_PATTERN)
|
||||
: null;
|
||||
if(bottomText==null || bottomText.length()==0 || bottomText.equals("\u0005")) bottomText=null;
|
||||
if(bottomText!=null){
|
||||
translation=new Translation();
|
||||
translation.content=bottomText;
|
||||
translation.detectedSourceLanguage="\uD83E\uDD7A\uD83D\uDC49\uD83D\uDC48";
|
||||
translation.provider="bottom-java";
|
||||
return true;
|
||||
}
|
||||
} catch (TranslationError ignored) {}
|
||||
|
||||
return translateEnabled && !TextUtils.isEmpty(content) && !TextUtils.isEmpty(language)
|
||||
&& !Objects.equals(Locale.getDefault().getLanguage(), language)
|
||||
&& (visibility==StatusPrivacy.PUBLIC || visibility==StatusPrivacy.UNLISTED);
|
||||
}
|
||||
|
||||
public enum TranslationState{
|
||||
HIDDEN,
|
||||
SHOWN,
|
||||
LOADING
|
||||
}
|
||||
|
||||
public boolean isReblogPermitted(String accountID){
|
||||
return visibility.isReblogPermitted(account.id.equals(
|
||||
AccountSessionManager.getInstance().getAccount(accountID).self.id
|
||||
|
||||
@@ -219,7 +219,7 @@ public class TimelineDefinition {
|
||||
args.putString("listID", listId);
|
||||
args.putBoolean("listIsExclusive", listIsExclusive);
|
||||
} else if (type == TimelineType.HASHTAG) {
|
||||
args.putString("hashtag", hashtagName);
|
||||
args.putString("hashtagName", hashtagName);
|
||||
args.putBoolean("localOnly", hashtagLocalOnly);
|
||||
args.putStringArrayList("any", hashtagAny == null ? new ArrayList<>() : new ArrayList<>(hashtagAny));
|
||||
args.putStringArrayList("all", hashtagAll == null ? new ArrayList<>() : new ArrayList<>(hashtagAll));
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
public class TranslatedStatus extends BaseModel {
|
||||
public String content;
|
||||
public String detectedSourceLanguage;
|
||||
public String provider;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import org.joinmastodon.android.api.AllFieldsAreRequired;
|
||||
|
||||
@AllFieldsAreRequired
|
||||
public class Translation extends BaseModel{
|
||||
public String content;
|
||||
public String detectedSourceLanguage;
|
||||
public String provider;
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import android.graphics.drawable.Animatable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
@@ -39,6 +40,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import me.grishka.appkit.FragmentStackActivity;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
@@ -144,21 +146,10 @@ public class AccountSwitcherSheet extends BottomSheet{
|
||||
}
|
||||
|
||||
private void logOut(String accountID){
|
||||
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||
new RevokeOauthToken(session.app.clientId, session.app.clientSecret, session.token.accessToken)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Object result){
|
||||
onLoggedOut(accountID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
onLoggedOut(accountID);
|
||||
}
|
||||
})
|
||||
.wrapProgress(activity, R.string.loading, false)
|
||||
.exec(accountID);
|
||||
AccountSessionManager.get(accountID).logOut(activity, ()->{
|
||||
dismiss();
|
||||
((MainActivity)activity).restartHomeFragment();
|
||||
});
|
||||
}
|
||||
|
||||
private void logOutAll(){
|
||||
@@ -326,15 +317,15 @@ public class AccountSwitcherSheet extends BottomSheet{
|
||||
@Override
|
||||
public void onClick(){
|
||||
setOnDismissListener(null);
|
||||
dismiss();
|
||||
if (onClick != null) {
|
||||
dismiss();
|
||||
onClick.accept(item.getID(), false);
|
||||
return;
|
||||
}
|
||||
if(AccountSessionManager.getInstance().tryGetAccount(item.getID())!=null)
|
||||
if(AccountSessionManager.getInstance().tryGetAccount(item.getID())!=null){
|
||||
AccountSessionManager.getInstance().setLastActiveAccountID(item.getID());
|
||||
activity.finish();
|
||||
activity.startActivity(new Intent(activity, MainActivity.class));
|
||||
((MainActivity)activity).restartHomeFragment();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,6 +17,8 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Filter;
|
||||
import android.widget.Filterable;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
@@ -34,6 +36,7 @@ import org.joinmastodon.android.model.Emoji;
|
||||
import org.joinmastodon.android.model.EmojiCategory;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -125,13 +128,13 @@ public class CustomEmojiPopupKeyboard extends PopupKeyboard{
|
||||
new StickyHeadersOverlay(activity, 0).install(list);
|
||||
|
||||
LinearLayout ll=new LinearLayout(activity) {
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent e){
|
||||
if (e.getAction() == MotionEvent.ACTION_MOVE) {
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
return false;
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent e){
|
||||
if (e.getAction() == MotionEvent.ACTION_MOVE) {
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
ll.setOrientation(LinearLayout.VERTICAL);
|
||||
ll.setElevation(V.dp(3));
|
||||
@@ -139,43 +142,52 @@ public class CustomEmojiPopupKeyboard extends PopupKeyboard{
|
||||
|
||||
ll.addView(list, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
|
||||
|
||||
FrameLayout bottomPanel=new FrameLayout(activity);
|
||||
bottomPanel.setPadding(V.dp(16), V.dp(8), V.dp(16), V.dp(8));
|
||||
bottomPanel.setBackgroundResource(R.drawable.bg_m3_surface2);
|
||||
ll.addView(bottomPanel, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
if(forReaction){
|
||||
FrameLayout topPanel=new FrameLayout(activity);
|
||||
topPanel.setPadding(V.dp(16), V.dp(12), V.dp(16), V.dp(12));
|
||||
topPanel.setBackgroundResource(R.drawable.bg_m3_surface2);
|
||||
ll.addView(topPanel, 0, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
InputMethodManager imm=(InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
|
||||
// TODO: support filtering custom emoji
|
||||
EditText input=new EditText(activity);
|
||||
input.setHint(R.string.sk_enter_emoji_hint);
|
||||
input.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
if (!s.toString().isEmpty()) {
|
||||
if (emojiRegex.matcher(s.toString()).find()) {
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count){
|
||||
// Only check the emoji regex if the text field was empty before
|
||||
if(start == 0){
|
||||
if(emojiRegex.matcher(s.toString()).find()){
|
||||
imm.hideSoftInputFromWindow(input.getWindowToken(), 0);
|
||||
listener.onEmojiSelected(s.toString().substring(before));
|
||||
input.getText().clear();
|
||||
} else {
|
||||
Toast.makeText(activity, R.string.sk_enter_emoji_toast, Toast.LENGTH_SHORT).show();
|
||||
input.getText().clear();
|
||||
}
|
||||
}
|
||||
for(int i=0; i<adapter.getAdapterCount(); i++){
|
||||
SingleCategoryAdapter currentAdapter=(SingleCategoryAdapter) adapter.getAdapterAt(i);
|
||||
currentAdapter.getFilter().filter(s.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
@Override public void afterTextChanged(Editable s) {}
|
||||
});
|
||||
input.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener(){
|
||||
@Override
|
||||
public void onViewAttachedToWindow(@NonNull View view){}
|
||||
|
||||
FrameLayout.LayoutParams params=new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START);
|
||||
int pad=forReaction ? 0 : V.dp(36 + 16);
|
||||
params.setMargins(pad, V.dp(8), pad, V.dp(8));
|
||||
bottomPanel.addView(input, params);
|
||||
}
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(@NonNull View view){
|
||||
input.getText().clear();
|
||||
}
|
||||
});
|
||||
topPanel.addView(input);
|
||||
|
||||
}else{ // in compose fragment
|
||||
FrameLayout bottomPanel=new FrameLayout(activity);
|
||||
bottomPanel.setPadding(V.dp(16), V.dp(8), V.dp(16), V.dp(8));
|
||||
bottomPanel.setBackgroundResource(R.drawable.bg_m3_surface2);
|
||||
ll.addView(bottomPanel, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
if(!forReaction){
|
||||
ImageButton hideKeyboard=new ImageButton(activity);
|
||||
hideKeyboard.setImageResource(R.drawable.ic_fluent_keyboard_dock_24_regular);
|
||||
hideKeyboard.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(activity, R.attr.colorM3OnSurfaceVariant)));
|
||||
@@ -207,13 +219,16 @@ public class CustomEmojiPopupKeyboard extends PopupKeyboard{
|
||||
}
|
||||
}
|
||||
|
||||
private class SingleCategoryAdapter extends UsableRecyclerView.Adapter<RecyclerView.ViewHolder> implements ImageLoaderRecyclerAdapter{
|
||||
private final EmojiCategory category;
|
||||
private final List<ImageLoaderRequest> requests;
|
||||
private class SingleCategoryAdapter extends UsableRecyclerView.Adapter<RecyclerView.ViewHolder> implements ImageLoaderRecyclerAdapter, Filterable{
|
||||
private EmojiCategory category;
|
||||
|
||||
private final EmojiCategory originalCategory;
|
||||
private List<ImageLoaderRequest> requests;
|
||||
|
||||
public SingleCategoryAdapter(EmojiCategory category){
|
||||
super(imgLoader);
|
||||
this.category=category;
|
||||
this.originalCategory=new EmojiCategory(category);
|
||||
requests=category.emojis.stream().map(e->new UrlImageLoaderRequest(e.getUrl(playGifs), V.dp(24), V.dp(24))).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -225,17 +240,22 @@ public class CustomEmojiPopupKeyboard extends PopupKeyboard{
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position){
|
||||
if(category.emojis.size() == 0) {
|
||||
holder.itemView.setVisibility(View.GONE);
|
||||
}
|
||||
if(holder instanceof EmojiViewHolder evh){
|
||||
evh.bind(category.emojis.get(position-1));
|
||||
evh.positionWithinCategory=position-1;
|
||||
}else if(holder instanceof SectionHeaderViewHolder shvh){
|
||||
shvh.bind(TextUtils.isEmpty(category.title) ? domain : category.title);
|
||||
}
|
||||
|
||||
super.onBindViewHolder(holder, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount(){
|
||||
if(category.emojis.size() == 0) return 0;
|
||||
return category.emojis.size()+1;
|
||||
}
|
||||
|
||||
@@ -253,6 +273,39 @@ public class CustomEmojiPopupKeyboard extends PopupKeyboard{
|
||||
public ImageLoaderRequest getImageRequest(int position, int image){
|
||||
return requests.get(position-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter getFilter(){
|
||||
return emojiFilter;
|
||||
}
|
||||
private final Filter emojiFilter = new Filter(){
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence charSequence){
|
||||
List<Emoji> filteredEmoji=new ArrayList<>();
|
||||
String search=charSequence.toString().toLowerCase().trim();
|
||||
|
||||
if(charSequence==null || charSequence.length()==0){
|
||||
filteredEmoji.addAll(originalCategory.emojis);
|
||||
}else{
|
||||
for(Emoji emoji : originalCategory.emojis){
|
||||
if(emoji.shortcode.toLowerCase().contains(search)){
|
||||
filteredEmoji.add(emoji);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
FilterResults results=new FilterResults();
|
||||
results.values=filteredEmoji;
|
||||
return results;
|
||||
}
|
||||
@Override
|
||||
protected void publishResults(CharSequence charSequence, FilterResults filterResults){
|
||||
category.emojis.clear();
|
||||
category.emojis.addAll((List) filterResults.values);
|
||||
requests=category.emojis.stream().map(e->new UrlImageLoaderRequest(e.getUrl(playGifs), V.dp(24), V.dp(24))).collect(Collectors.toList());
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class SectionHeaderViewHolder extends BindableViewHolder<String> implements StickyHeadersOverlay.HeaderViewHolder{
|
||||
|
||||
@@ -79,7 +79,7 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
|
||||
progressBg=activity.getResources().getDrawable(R.drawable.bg_poll_option_voted, activity.getTheme()).mutate();
|
||||
progressBgInset=activity.getResources().getDrawable(R.drawable.bg_poll_option_voted_inset, activity.getTheme()).mutate();
|
||||
itemView.setOnClickListener(this::onButtonClick);
|
||||
button.setOutlineProvider(OutlineProviders.M3_BUTTON);
|
||||
button.setOutlineProvider(OutlineProviders.roundedRect(20));
|
||||
button.setClipToOutline(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||
import org.joinmastodon.android.model.Emoji;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
@@ -53,7 +54,8 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
|
||||
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon, StatusPrivacy visibility, @Nullable View.OnClickListener handleClick, CharSequence fullText, Status status) {
|
||||
super(parentID, parentFragment);
|
||||
SpannableStringBuilder ssb=new SpannableStringBuilder(text);
|
||||
HtmlParser.parseCustomEmoji(ssb, emojis);
|
||||
if(AccountSessionManager.get(parentFragment.getAccountID()).getLocalPreferences().customEmojiInNames)
|
||||
HtmlParser.parseCustomEmoji(ssb, emojis);
|
||||
this.text=ssb;
|
||||
emojiHelper.setText(ssb);
|
||||
this.icon=icon;
|
||||
|
||||
@@ -54,7 +54,7 @@ import me.grishka.appkit.views.UsableRecyclerView;
|
||||
public abstract class StatusDisplayItem{
|
||||
public final String parentID;
|
||||
public final BaseStatusListFragment<?> parentFragment;
|
||||
public boolean inset, insetPadding=true;
|
||||
public boolean inset;
|
||||
public int index;
|
||||
public boolean
|
||||
hasDescendantNeighbor = false,
|
||||
@@ -180,10 +180,8 @@ public abstract class StatusDisplayItem{
|
||||
.ifPresent(hashtag -> items.add(new ReblogOrReplyLineStatusDisplayItem(
|
||||
parentID, fragment, hashtag.name, List.of(),
|
||||
R.drawable.ic_fluent_number_symbol_20sp_filled, null,
|
||||
i -> {
|
||||
args.putString("hashtag", hashtag.name);
|
||||
Nav.go(fragment.getActivity(), HashtagTimelineFragment.class, args);
|
||||
}, status
|
||||
i->UiUtils.openHashtagTimeline(fragment.getActivity(), accountID, hashtag),
|
||||
status
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -397,10 +395,11 @@ public abstract class StatusDisplayItem{
|
||||
}
|
||||
|
||||
public Optional<StatusDisplayItem> getDisplayItemOffset(int offset){
|
||||
int nextPos=getAbsoluteAdapterPosition() + offset;
|
||||
List<StatusDisplayItem> displayItems=item.parentFragment.getDisplayItems();
|
||||
return displayItems.size() > nextPos
|
||||
? Optional.of(displayItems.get(nextPos))
|
||||
int thisPos=displayItems.indexOf(item);
|
||||
int offsetPos=thisPos + offset;
|
||||
return displayItems.size() > offsetPos && thisPos >= 0 && offsetPos >= 0
|
||||
? Optional.of(displayItems.get(offsetPos))
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,71 +1,53 @@
|
||||
package org.joinmastodon.android.ui.displayitems;
|
||||
|
||||
import static org.joinmastodon.android.ui.utils.UiUtils.opacityIn;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewStub;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.github.bottomSoftwareFoundation.bottom.Bottom;
|
||||
import com.github.bottomSoftwareFoundation.bottom.TranslationError;
|
||||
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.statuses.TranslateStatus;
|
||||
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.ThreadFragment;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.StatusPrivacy;
|
||||
import org.joinmastodon.android.model.TranslatedStatus;
|
||||
import org.joinmastodon.android.model.Translation;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
||||
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;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||
import me.grishka.appkit.imageloader.MovieDrawable;
|
||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||
private CharSequence text;
|
||||
private CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
|
||||
private CharSequence translatedText;
|
||||
private CustomEmojiHelper translationEmojiHelper=new CustomEmojiHelper();
|
||||
public boolean textSelectable;
|
||||
public boolean reduceTopPadding;
|
||||
public boolean disableTranslate;
|
||||
public final Status status;
|
||||
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");
|
||||
|
||||
public TextStatusDisplayItem(String parentID, CharSequence text, BaseStatusListFragment parentFragment, Status status, boolean disableTranslate){
|
||||
super(parentID, parentFragment);
|
||||
this.text=text;
|
||||
this.status=status;
|
||||
this.disableTranslate=disableTranslate;
|
||||
this.translationShown=status.translationShown;
|
||||
emojiHelper.setText(text);
|
||||
session = AccountSessionManager.getInstance().getAccount(parentFragment.getAccountID());
|
||||
}
|
||||
|
||||
public void setTranslationShown(boolean translationShown) {
|
||||
this.translationShown = translationShown;
|
||||
status.translationShown = translationShown;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,38 +57,47 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||
|
||||
@Override
|
||||
public int getImageCount(){
|
||||
return emojiHelper.getImageCount();
|
||||
return getCurrentEmojiHelper().getImageCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageLoaderRequest getImageRequest(int index){
|
||||
return emojiHelper.getImageRequest(index);
|
||||
return getCurrentEmojiHelper().getImageRequest(index);
|
||||
}
|
||||
|
||||
public void setTranslatedText(String text){
|
||||
Status statusForContent=status.getContentStatus();
|
||||
translatedText=HtmlParser.parse(text, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, parentFragment.getAccountID());
|
||||
translationEmojiHelper.setText(translatedText);
|
||||
}
|
||||
|
||||
private CustomEmojiHelper getCurrentEmojiHelper(){
|
||||
return status.translationState==Status.TranslationState.SHOWN ? translationEmojiHelper : emojiHelper;
|
||||
}
|
||||
|
||||
public static class Holder extends StatusDisplayItem.Holder<TextStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||
private final LinkedTextView text;
|
||||
private final TextView translateInfo, readMore;
|
||||
private final View textWrap, translateWrap, translateProgress;
|
||||
private final Button translateButton;
|
||||
private final ScrollView textScrollView;
|
||||
private final ViewStub translationFooterStub;
|
||||
private View translationFooter, translationButtonWrap;
|
||||
private TextView translationInfo;
|
||||
private Button translationButton;
|
||||
private ProgressBar translationProgress;
|
||||
|
||||
private final float textMaxHeight, textCollapsedHeight;
|
||||
private final float textMaxHeight;
|
||||
private final LinearLayout.LayoutParams collapseParams, wrapParams;
|
||||
private final ViewGroup parent;
|
||||
private final TextView readMore;
|
||||
private final ScrollView textScrollView;
|
||||
|
||||
public Holder(Activity activity, ViewGroup parent){
|
||||
super(activity, R.layout.display_item_text, parent);
|
||||
this.parent=parent;
|
||||
text=findViewById(R.id.text);
|
||||
textWrap = (LinearLayout) itemView;
|
||||
translateWrap=findViewById(R.id.translate_wrap);
|
||||
translateButton=findViewById(R.id.translate_btn);
|
||||
translateInfo=findViewById(R.id.translate_info);
|
||||
translateProgress=findViewById(R.id.translate_progress);
|
||||
translationFooterStub=findViewById(R.id.translation_info);
|
||||
textScrollView=findViewById(R.id.text_scroll_view);
|
||||
readMore=findViewById(R.id.read_more);
|
||||
textMaxHeight=activity.getResources().getDimension(R.dimen.text_max_height);
|
||||
textCollapsedHeight=activity.getResources().getDimension(R.dimen.text_collapsed_height);
|
||||
float textCollapsedHeight=activity.getResources().getDimension(R.dimen.text_collapsed_height);
|
||||
collapseParams=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) textCollapsedHeight);
|
||||
wrapParams=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
readMore.setOnClickListener(v -> item.parentFragment.onToggleExpanded(item.status, getItemID()));
|
||||
@@ -114,83 +105,20 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||
|
||||
@Override
|
||||
public void onBind(TextStatusDisplayItem item){
|
||||
boolean hasSpoiler = !TextUtils.isEmpty(item.status.spoilerText);
|
||||
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));
|
||||
if(item.status.translationState==Status.TranslationState.SHOWN){
|
||||
if(item.translatedText==null){
|
||||
item.setTranslatedText(item.status.translation.content);
|
||||
}
|
||||
text.setText(item.translatedText);
|
||||
}else{
|
||||
text.setText(item.text);
|
||||
}
|
||||
text.setTextIsSelectable(item.textSelectable);
|
||||
text.setInvalidateOnEveryFrame(false);
|
||||
itemView.setClickable(false);
|
||||
itemView.setPadding(itemView.getPaddingLeft(), item.reduceTopPadding ? V.dp(6) : V.dp(12), itemView.getPaddingRight(), itemView.getPaddingBottom());
|
||||
text.setTextColor(UiUtils.getThemeColor(text.getContext(), item.inset ? R.attr.colorM3OnSurfaceVariant : R.attr.colorM3OnSurface));
|
||||
|
||||
Instance instanceInfo = AccountSessionManager.getInstance().getInstanceInfo(item.session.domain);
|
||||
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 translateVisible = (bottomText != null || (
|
||||
translateEnabled &&
|
||||
!item.status.visibility.isLessVisibleThan(StatusPrivacy.UNLISTED) &&
|
||||
item.status.language != null &&
|
||||
// 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.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.status.translation == null) {
|
||||
if (finalBottomText != null) {
|
||||
try {
|
||||
item.status.translation = new TranslatedStatus();
|
||||
item.status.translation.content = finalBottomText;
|
||||
item.setTranslationShown(true);
|
||||
} catch (TranslationError err) {
|
||||
item.status.translation = null;
|
||||
Toast.makeText(itemView.getContext(), err.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
rebind();
|
||||
return;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error) {
|
||||
translateProgress.setVisibility(View.GONE);
|
||||
translateButton.setClickable(true);
|
||||
translateButton.animate().alpha(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(50).start();
|
||||
error.showToast(itemView.getContext());
|
||||
}
|
||||
}).exec(item.parentFragment.getAccountID());
|
||||
} else {
|
||||
item.setTranslationShown(!item.translationShown);
|
||||
rebind();
|
||||
}
|
||||
});
|
||||
updateTranslation(false);
|
||||
|
||||
readMore.setText(item.status.textExpanded ? R.string.sk_collapse : R.string.sk_expand);
|
||||
|
||||
@@ -224,18 +152,19 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||
|
||||
if (GlobalUserPreferences.collapseLongPosts && !item.status.textExpandable) {
|
||||
boolean tooBig = text.getMeasuredHeight() > textMaxHeight;
|
||||
boolean expandable = tooBig && !hasSpoiler;
|
||||
boolean expandable = tooBig && !item.status.hasSpoiler();
|
||||
item.parentFragment.onEnableExpandable(Holder.this, expandable);
|
||||
}
|
||||
|
||||
boolean expandButtonShown=item.status.textExpandable && !item.status.textExpanded;
|
||||
translateWrap.setPadding(0, V.dp(expandButtonShown ? 0 : 4), 0, 0);
|
||||
if(translationFooter!=null)
|
||||
translationFooter.setPadding(0, V.dp(expandButtonShown ? 0 : 4), 0, 0);
|
||||
readMore.setVisibility(expandButtonShown ? View.VISIBLE : View.GONE);
|
||||
textScrollView.setLayoutParams(item.status.textExpandable && !item.status.textExpanded ? collapseParams : wrapParams);
|
||||
|
||||
// compensate for spoiler's bottom margin
|
||||
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) itemView.getLayoutParams();
|
||||
params.setMargins(params.leftMargin, item.inset && hasSpoiler ? V.dp(-16) : 0,
|
||||
params.setMargins(params.leftMargin, item.inset && item.status.hasSpoiler() ? V.dp(-16) : 0,
|
||||
params.rightMargin, params.bottomMargin);
|
||||
}
|
||||
|
||||
@@ -259,5 +188,60 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||
private CustomEmojiHelper getEmojiHelper(){
|
||||
return item.emojiHelper;
|
||||
}
|
||||
|
||||
public void updateTranslation(boolean updateText){
|
||||
if(item.status==null)
|
||||
return;
|
||||
boolean translateEnabled=!item.disableTranslate && item.status.isEligibleForTranslation(item.parentFragment.getSession());
|
||||
if(translationFooter==null && translateEnabled){
|
||||
translationFooter=translationFooterStub.inflate();
|
||||
translationInfo=findViewById(R.id.translation_info_text);
|
||||
translationButton=findViewById(R.id.translation_btn);
|
||||
translationButtonWrap=findViewById(R.id.translation_btn_wrap);
|
||||
translationProgress=findViewById(R.id.translation_progress);
|
||||
translationButton.setOnClickListener(v->item.parentFragment.togglePostTranslation(item.status, item.parentID));
|
||||
}
|
||||
if(item.status.translationState==Status.TranslationState.HIDDEN){
|
||||
if(updateText) text.setText(item.text);
|
||||
if(translationFooter==null) return;
|
||||
translationFooter.setVisibility(translateEnabled ? View.VISIBLE : View.GONE);
|
||||
translationProgress.setVisibility(View.GONE);
|
||||
Translation existingTrans=item.status.getContentStatus().translation;
|
||||
String existingTransLang=existingTrans!=null ? existingTrans.detectedSourceLanguage : null;
|
||||
String lang=existingTransLang!=null ? existingTransLang : item.status.getContentStatus().language;
|
||||
String displayLang=Locale.forLanguageTag(lang != null ? lang
|
||||
: AccountSessionManager.get(item.parentFragment.getAccountID()).preferences.postingDefaultLanguage).getDisplayLanguage();
|
||||
translationButton.setText(item.parentFragment.getString(R.string.translate_post, !displayLang.isBlank() ? displayLang : lang));
|
||||
translationButton.setEnabled(true);
|
||||
translationButton.setAlpha(1);
|
||||
translationInfo.setVisibility(View.GONE);
|
||||
UiUtils.beginLayoutTransition((ViewGroup) translationButtonWrap);
|
||||
}else{
|
||||
translationFooter.setVisibility(View.VISIBLE);
|
||||
if(item.status.translationState==Status.TranslationState.SHOWN){
|
||||
translationProgress.setVisibility(View.GONE);
|
||||
translationButton.setText(R.string.translation_show_original);
|
||||
translationButton.setEnabled(true);
|
||||
translationButton.setAlpha(1);
|
||||
translationInfo.setVisibility(View.VISIBLE);
|
||||
translationButton.setVisibility(View.VISIBLE);
|
||||
String displayLang=Locale.forLanguageTag(item.status.translation.detectedSourceLanguage).getDisplayLanguage();
|
||||
translationInfo.setText(translationInfo.getContext().getString(R.string.post_translated, !displayLang.isBlank() ? displayLang : item.status.translation.detectedSourceLanguage, item.status.translation.provider));
|
||||
UiUtils.beginLayoutTransition((ViewGroup) translationButtonWrap);
|
||||
if(updateText){
|
||||
if(item.translatedText==null){
|
||||
item.setTranslatedText(item.status.translation.content);
|
||||
}
|
||||
text.setText(item.translatedText);
|
||||
}
|
||||
}else{ // LOADING
|
||||
translationProgress.setVisibility(View.VISIBLE);
|
||||
translationButton.setEnabled(false);
|
||||
translationButton.startAnimation(opacityIn);
|
||||
translationInfo.setVisibility(View.INVISIBLE);
|
||||
UiUtils.beginLayoutTransition((ViewGroup) translationButton.getParent());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,15 +99,11 @@ public class BlurhashCrossfadeDrawable extends Drawable{
|
||||
|
||||
@Override
|
||||
public int getIntrinsicWidth(){
|
||||
if(width==0)
|
||||
return imageDrawable==null ? 1920 : imageDrawable.getIntrinsicWidth();
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicHeight(){
|
||||
if(height==0)
|
||||
return imageDrawable==null ? 1080 : imageDrawable.getIntrinsicHeight();
|
||||
return height;
|
||||
}
|
||||
|
||||
|
||||
@@ -878,6 +878,8 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
||||
|
||||
@Override
|
||||
public void onVideoSizeChanged(MediaPlayer mp, int width, int height){
|
||||
if(width<=0 || height<=0)
|
||||
return;
|
||||
FrameLayout.LayoutParams params=(FrameLayout.LayoutParams) wrap.getLayoutParams();
|
||||
params.width=width;
|
||||
params.height=height;
|
||||
|
||||
@@ -119,6 +119,9 @@ public class ZoomPanView extends FrameLayout implements ScaleGestureDetector.OnS
|
||||
|
||||
int width=right-left;
|
||||
int height=bottom-top;
|
||||
if(width==0 || height==0)
|
||||
return;
|
||||
|
||||
float scale=Math.min(width/(float)child.getWidth(), height/(float)child.getHeight());
|
||||
minScale=scale;
|
||||
maxScale=Math.max(3f, height/(float)child.getHeight());
|
||||
@@ -306,8 +309,6 @@ public class ZoomPanView extends FrameLayout implements ScaleGestureDetector.OnS
|
||||
}, 1f).setMinimumVisibleChange(DynamicAnimation.MIN_VISIBLE_CHANGE_ALPHA));
|
||||
}
|
||||
}else{
|
||||
if(animatingTransition)
|
||||
Log.w(TAG, "updateViewTransform: ", new Throwable().fillInStackTrace());
|
||||
child.setScaleX(matrixValues[Matrix.MSCALE_X]);
|
||||
child.setScaleY(matrixValues[Matrix.MSCALE_Y]);
|
||||
child.setTranslationX(matrixValues[Matrix.MTRANS_X]);
|
||||
|
||||
@@ -100,9 +100,10 @@ public class HtmlParser{
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> idsByUrl=mentions.stream().collect(Collectors.toMap(m->m.url, m->m.id));
|
||||
Map<String, String> idsByUrl=mentions.stream().distinct().collect(Collectors.toMap(m->m.url, m->m.id));
|
||||
// Hashtags in remote posts have remote URLs, these have local URLs so they don't match.
|
||||
// Map<String, String> tagsByUrl=tags.stream().collect(Collectors.toMap(t->t.url, t->t.name));
|
||||
Map<String, Hashtag> tagsByTag=tags.stream().distinct().collect(Collectors.toMap(t->t.name.toLowerCase(), Function.identity()));
|
||||
|
||||
final SpannableStringBuilder ssb=new SpannableStringBuilder();
|
||||
Jsoup.parseBodyFragment(source).body().traverse(new NodeVisitor(){
|
||||
@@ -115,6 +116,7 @@ public class HtmlParser{
|
||||
}else if(node instanceof Element el){
|
||||
switch(el.nodeName()){
|
||||
case "a" -> {
|
||||
Object linkObject=null;
|
||||
String href=el.attr("href");
|
||||
LinkSpan.Type linkType;
|
||||
String text=el.text();
|
||||
@@ -122,6 +124,7 @@ public class HtmlParser{
|
||||
if(text.startsWith("#")){
|
||||
linkType=LinkSpan.Type.HASHTAG;
|
||||
href=text.substring(1);
|
||||
linkObject=tagsByTag.get(text.substring(1).toLowerCase());
|
||||
}else{
|
||||
linkType=LinkSpan.Type.URL;
|
||||
}
|
||||
@@ -136,7 +139,7 @@ public class HtmlParser{
|
||||
}else{
|
||||
linkType=LinkSpan.Type.URL;
|
||||
}
|
||||
openSpans.add(new SpanInfo(new LinkSpan(href, null, linkType, accountID), ssb.length(), el));
|
||||
openSpans.add(new SpanInfo(new LinkSpan(href, null, linkType, accountID, linkObject, text), ssb.length(), el));
|
||||
}
|
||||
case "br" -> ssb.append('\n');
|
||||
case "span" -> {
|
||||
@@ -271,7 +274,7 @@ public class HtmlParser{
|
||||
String url=matcher.group(3);
|
||||
if(TextUtils.isEmpty(matcher.group(4)))
|
||||
url="http://"+url;
|
||||
ssb.setSpan(new LinkSpan(url, null, LinkSpan.Type.URL, null), matcher.start(3), matcher.end(3), 0);
|
||||
ssb.setSpan(new LinkSpan(url, null, LinkSpan.Type.URL, null, null, null), matcher.start(3), matcher.end(3), 0);
|
||||
}while(matcher.find()); // Find more URLs
|
||||
return ssb;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.text.TextPaint;
|
||||
import android.text.style.CharacterStyle;
|
||||
import android.view.View;
|
||||
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
public class LinkSpan extends CharacterStyle {
|
||||
@@ -14,17 +15,15 @@ public class LinkSpan extends CharacterStyle {
|
||||
private String link;
|
||||
private Type type;
|
||||
private String accountID;
|
||||
private Object linkObject;
|
||||
private String text;
|
||||
|
||||
public LinkSpan(String link, OnLinkClickListener listener, Type type, String accountID){
|
||||
this(link, listener, type, accountID, null);
|
||||
}
|
||||
|
||||
public LinkSpan(String link, OnLinkClickListener listener, Type type, String accountID, String text){
|
||||
public LinkSpan(String link, OnLinkClickListener listener, Type type, String accountID, Object linkObject, String text){
|
||||
this.listener=listener;
|
||||
this.link=link;
|
||||
this.type=type;
|
||||
this.accountID=accountID;
|
||||
this.linkObject=linkObject;
|
||||
this.text=text;
|
||||
}
|
||||
|
||||
@@ -42,7 +41,12 @@ public class LinkSpan extends CharacterStyle {
|
||||
switch(getType()){
|
||||
case URL -> UiUtils.openURL(context, accountID, link);
|
||||
case MENTION -> UiUtils.openProfileByID(context, accountID, link);
|
||||
case HASHTAG -> UiUtils.openHashtagTimeline(context, accountID, link, null);
|
||||
case HASHTAG -> {
|
||||
if(linkObject instanceof Hashtag ht)
|
||||
UiUtils.openHashtagTimeline(context, accountID, ht);
|
||||
else
|
||||
UiUtils.openHashtagTimeline(context, accountID, text);
|
||||
}
|
||||
case CUSTOM -> listener.onLinkClick(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public class DiscoverInfoBannerHelper{
|
||||
TextView text=banner.findViewById(R.id.banner_text);
|
||||
text.setText(switch(type){
|
||||
case TRENDING_POSTS -> list.getResources().getString(R.string.sk_trending_posts_info_banner);
|
||||
case TRENDING_LINKS -> list.getResources().getString(R.string.trending_links_info_banner);
|
||||
case TRENDING_LINKS -> list.getResources().getString(R.string.sk_trending_links_info_banner);
|
||||
case FEDERATED_TIMELINE -> list.getResources().getString(R.string.sk_federated_timeline_info_banner);
|
||||
case POST_NOTIFICATIONS -> list.getResources().getString(R.string.sk_notify_posts_info_banner);
|
||||
case BUBBLE_TIMELINE -> list.getResources().getString(R.string.sk_bubble_timeline_info_banner);
|
||||
|
||||
@@ -74,24 +74,19 @@ public class InsetStatusItemDecoration extends RecyclerView.ItemDecoration{
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
|
||||
List<StatusDisplayItem> displayItems=listFragment.getDisplayItems();
|
||||
// List<StatusDisplayItem> displayItems=listFragment.getDisplayItems();
|
||||
RecyclerView.ViewHolder holder=parent.getChildViewHolder(view);
|
||||
if(holder instanceof StatusDisplayItem.Holder<?> sdi){
|
||||
boolean inset=sdi.getItem().inset;
|
||||
int pos=holder.getAbsoluteAdapterPosition();
|
||||
// int pos=holder.getAbsoluteAdapterPosition();
|
||||
if(inset){
|
||||
boolean topSiblingInset=pos>0 && displayItems.get(pos-1).inset;
|
||||
boolean bottomSiblingInset=pos<displayItems.size()-1 && displayItems.get(pos+1).inset;
|
||||
int pad;
|
||||
// boolean topSiblingInset=pos>0 && displayItems.get(pos-1).inset;
|
||||
// boolean bottomSiblingInset=pos<displayItems.size()-1 && displayItems.get(pos+1).inset;
|
||||
// if(holder instanceof MediaGridStatusDisplayItem.Holder || holder instanceof LinkCardStatusDisplayItem.Holder)
|
||||
pad=V.dp(16);
|
||||
// else
|
||||
// pad=V.dp(12);
|
||||
boolean insetPadding=((StatusDisplayItem.Holder<?>) holder).getItem().insetPadding;
|
||||
if(insetPadding)
|
||||
outRect.left=pad;
|
||||
if(insetPadding)
|
||||
outRect.right=pad;
|
||||
int pad=V.dp(16);
|
||||
// else pad=V.dp(12);
|
||||
outRect.left=pad;
|
||||
outRect.right=pad;
|
||||
|
||||
// had to comment this out because animations with offsets aren't handled properly.
|
||||
// can be worked around by manually applying top margins to items
|
||||
|
||||
@@ -104,6 +104,7 @@ import org.joinmastodon.android.model.AccountField;
|
||||
import org.joinmastodon.android.model.Emoji;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.Notification;
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
import org.joinmastodon.android.model.ScheduledStatus;
|
||||
import org.joinmastodon.android.model.SearchResults;
|
||||
@@ -217,11 +218,20 @@ public class UiUtils {
|
||||
if(diff<1000L){
|
||||
return context.getString(R.string.time_now);
|
||||
}else if(diff<60_000L){
|
||||
return context.getString(ago ? R.string.time_seconds_ago_short : R.string.sk_time_seconds, diff/1000L);
|
||||
long time = diff/1000L;
|
||||
return ago ?
|
||||
context.getString(R.string.time_seconds_ago_short, time) :
|
||||
context.getResources().getQuantityString(R.plurals.sk_time_seconds, (int) time, time);
|
||||
}else if(diff<3600_000L){
|
||||
return context.getString(ago ? R.string.time_minutes_ago_short : R.string.sk_time_minutes, diff/60_000L);
|
||||
long time = diff/60_000L;
|
||||
return ago ?
|
||||
context.getString(R.string.time_minutes_ago_short, time) :
|
||||
context.getResources().getQuantityString(R.plurals.sk_time_minutes, (int) time, time);
|
||||
}else if(diff<3600_000L*24L){
|
||||
return context.getString(ago ? R.string.time_hours_ago_short : R.string.sk_time_hours, diff/3600_000L);
|
||||
long time = diff/3600_000L;
|
||||
return ago ?
|
||||
context.getString(R.string.time_hours_ago_short, time) :
|
||||
context.getResources().getQuantityString(R.plurals.sk_time_hours, (int) time, time);
|
||||
} else {
|
||||
int days = (int) (diff / (3600_000L * 24L));
|
||||
if (ago && days > 30) {
|
||||
@@ -232,7 +242,7 @@ public class UiUtils {
|
||||
return DATE_FORMATTER_SHORT_WITH_YEAR.format(dt);
|
||||
}
|
||||
}
|
||||
return context.getString(ago ? R.string.time_days_ago_short : R.string.sk_time_days, days);
|
||||
return ago ? context.getString(R.string.time_days_ago_short, days) : context.getResources().getQuantityString(R.plurals.sk_time_days, days, days);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,12 +455,18 @@ public class UiUtils {
|
||||
Nav.go((Activity) context, ProfileFragment.class, args);
|
||||
}
|
||||
|
||||
public static void openHashtagTimeline(Context context, String accountID, String hashtag, @Nullable Boolean following) {
|
||||
Bundle args = new Bundle();
|
||||
public static void openHashtagTimeline(Context context, String accountID, Hashtag hashtag){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putString("hashtag", hashtag);
|
||||
if (following != null) args.putBoolean("following", following);
|
||||
Nav.go((Activity) context, HashtagTimelineFragment.class, args);
|
||||
args.putParcelable("hashtag", Parcels.wrap(hashtag));
|
||||
Nav.go((Activity)context, HashtagTimelineFragment.class, args);
|
||||
}
|
||||
|
||||
public static void openHashtagTimeline(Context context, String accountID, String hashtag){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putString("hashtagName", hashtag);
|
||||
Nav.go((Activity)context, HashtagTimelineFragment.class, args);
|
||||
}
|
||||
|
||||
public static void showConfirmationAlert(Context context, @StringRes int title, @StringRes int message, @StringRes int confirmButton, Runnable onConfirmed) {
|
||||
@@ -1157,7 +1173,7 @@ public class UiUtils {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(new GetSearchResults(query.getQuery(), type, true).setCallback(new Callback<>() {
|
||||
return Optional.of(new GetSearchResults(query.getQuery(), type, true, null, 0, 0).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(SearchResults results) {
|
||||
Optional<T> result = extractResult.apply(results);
|
||||
@@ -1254,7 +1270,7 @@ public class UiUtils {
|
||||
}
|
||||
public static MastodonAPIRequest<SearchResults> lookupAccountHandle(Context context, String accountID, Pair<String, Optional<String>> queryHandle, BiConsumer<Class<? extends Fragment>, Bundle> go) {
|
||||
String fullHandle = ("@" + queryHandle.first) + (queryHandle.second.map(domain -> "@" + domain).orElse(""));
|
||||
return new GetSearchResults(fullHandle, GetSearchResults.Type.ACCOUNTS, true)
|
||||
return new GetSearchResults(fullHandle, GetSearchResults.Type.ACCOUNTS, true, null, 0, 0)
|
||||
.setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(SearchResults results) {
|
||||
@@ -1317,7 +1333,7 @@ public class UiUtils {
|
||||
})
|
||||
.execNoAuth(uri.getHost()));
|
||||
} else if (looksLikeFediverseUrl(url)) {
|
||||
return Optional.of(new GetSearchResults(url, null, true)
|
||||
return Optional.of(new GetSearchResults(url, null, true, null, 0, 0)
|
||||
.setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(SearchResults results) {
|
||||
|
||||
@@ -15,15 +15,12 @@ import android.widget.TextView;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.search.GetSearchResults;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Emoji;
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
import org.joinmastodon.android.model.SearchResults;
|
||||
import org.joinmastodon.android.model.viewmodel.AccountViewModel;
|
||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||
import org.joinmastodon.android.ui.OutlineProviders;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
||||
import org.joinmastodon.android.ui.utils.HideableSingleViewRecyclerAdapter;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.ui.views.FilterChipView;
|
||||
@@ -96,6 +93,24 @@ public class ComposeAutocompleteViewController{
|
||||
outRect.right=V.dp(8);
|
||||
}
|
||||
});
|
||||
// Set empty adapter to prevent NPEs
|
||||
list.setAdapter(new RecyclerView.Adapter<>(){
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount(){
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
contentView.addView(list, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
emptyButton=new FilterChipView(activity);
|
||||
@@ -222,11 +237,13 @@ public class ComposeAutocompleteViewController{
|
||||
}
|
||||
|
||||
private void doSearchUsers(){
|
||||
currentRequest=new GetSearchResults(lastText, GetSearchResults.Type.ACCOUNTS, false)
|
||||
currentRequest=new GetSearchResults(lastText, GetSearchResults.Type.ACCOUNTS, false, null, 0, 0)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(SearchResults result){
|
||||
currentRequest=null;
|
||||
if(mode!=Mode.USERS)
|
||||
return;
|
||||
List<AccountViewModel> oldList=users;
|
||||
users=result.accounts.stream().map(a->new AccountViewModel(a, accountID)).collect(Collectors.toList());
|
||||
if(isLoading){
|
||||
@@ -256,7 +273,7 @@ public class ComposeAutocompleteViewController{
|
||||
}
|
||||
|
||||
private void doSearchHashtags(){
|
||||
currentRequest=new GetSearchResults(lastText, GetSearchResults.Type.HASHTAGS, false)
|
||||
currentRequest=new GetSearchResults(lastText, GetSearchResults.Type.HASHTAGS, false, null, 0, 0)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(SearchResults result){
|
||||
|
||||
@@ -75,14 +75,14 @@ public class ComposePollViewController{
|
||||
|
||||
Instance instance=fragment.instance;
|
||||
if (!instance.isAkkoma()) {
|
||||
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxOptions>0)
|
||||
if(instance!=null && instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxOptions>0)
|
||||
maxPollOptions=instance.configuration.polls.maxOptions;
|
||||
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxCharactersPerOption>0)
|
||||
if(instance!=null && instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxCharactersPerOption>0)
|
||||
maxPollOptionLength=instance.configuration.polls.maxCharactersPerOption;
|
||||
} else {
|
||||
if (instance.pollLimits!=null && instance.pollLimits.maxOptions>0)
|
||||
if(instance!=null && instance.pollLimits!=null && instance.pollLimits.maxOptions>0)
|
||||
maxPollOptions=instance.pollLimits.maxOptions;
|
||||
if(instance.pollLimits!=null && instance.pollLimits.maxOptionChars>0)
|
||||
if(instance!=null && instance.pollLimits!=null && instance.pollLimits.maxOptionChars>0)
|
||||
maxPollOptionLength=instance.pollLimits.maxOptionChars;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ public class MediaGridLayout extends ViewGroup{
|
||||
|
||||
private static final int GAP=2; // dp
|
||||
private PhotoLayoutHelper.TiledLayoutResult tiledLayout;
|
||||
private int[] columnStarts=new int[10], columnEnds=new int[10], rowStarts=new int[10], rowEnds=new int[10];
|
||||
private int[] columnStarts, columnEnds, rowStarts, rowEnds;
|
||||
|
||||
public MediaGridLayout(Context context){
|
||||
this(context, null);
|
||||
@@ -41,6 +41,14 @@ public class MediaGridLayout extends ViewGroup{
|
||||
width=Math.round(width*(tiledLayout.width/(float)PhotoLayoutHelper.MAX_WIDTH));
|
||||
}
|
||||
|
||||
if(rowStarts==null || rowStarts.length<tiledLayout.rowSizes.length){
|
||||
rowStarts=new int[tiledLayout.rowSizes.length];
|
||||
rowEnds=new int[tiledLayout.rowSizes.length];
|
||||
}
|
||||
if(columnStarts==null || columnStarts.length<tiledLayout.columnSizes.length){
|
||||
columnStarts=new int[tiledLayout.columnSizes.length];
|
||||
columnEnds=new int[tiledLayout.columnSizes.length];
|
||||
}
|
||||
int offset=0;
|
||||
for(int i=0;i<tiledLayout.columnSizes.length;i++){
|
||||
columnStarts[i]=offset;
|
||||
@@ -73,7 +81,7 @@ public class MediaGridLayout extends ViewGroup{
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b){
|
||||
if(tiledLayout==null)
|
||||
if(tiledLayout==null || rowStarts==null)
|
||||
return;
|
||||
|
||||
int maxWidth=UiUtils.MAX_WIDTH;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?android:colorControlHighlight">
|
||||
<item android:gravity="center_vertical" android:height="40dp">
|
||||
<item>
|
||||
<shape>
|
||||
<stroke android:width="1dp" android:color="?colorM3Outline"/>
|
||||
<corners android:radius="20dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:id="@android:id/mask" android:gravity="center_vertical" android:height="40dp">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape>
|
||||
<solid android:color="#000"/>
|
||||
<corners android:radius="20dp"/>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?android:colorControlHighlight">
|
||||
<item android:gravity="center_vertical" android:height="40dp">
|
||||
<item>
|
||||
<shape>
|
||||
<stroke android:width="1dp" android:color="?colorM3Outline"/>
|
||||
<solid android:color="?colorM3Surface"/>
|
||||
<corners android:radius="20dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:id="@android:id/mask" android:gravity="center_vertical" android:height="40dp">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape>
|
||||
<solid android:color="#000"/>
|
||||
<corners android:radius="20dp"/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:gravity="center_vertical" android:height="40dp">
|
||||
<item>
|
||||
<scale android:scaleGravity="start|fill_vertical" android:scaleWidth="100%">
|
||||
<shape>
|
||||
<solid android:color="?colorM3SecondaryContainer"/>
|
||||
@@ -8,7 +8,7 @@
|
||||
</shape>
|
||||
</scale>
|
||||
</item>
|
||||
<item android:gravity="center_vertical" android:height="40dp">
|
||||
<item>
|
||||
<shape>
|
||||
<stroke android:width="1dp" android:color="?colorM3OutlineVariant"/>
|
||||
<corners android:radius="20dp"/>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:gravity="center_vertical" android:height="40dp">
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="?colorM3Surface"/>
|
||||
<corners android:radius="20dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:gravity="center_vertical" android:height="40dp">
|
||||
<item>
|
||||
<scale android:scaleGravity="start|fill_vertical" android:scaleWidth="100%">
|
||||
<shape>
|
||||
<solid android:color="@color/poll_option_progress_inset"/>
|
||||
@@ -14,7 +14,7 @@
|
||||
</shape>
|
||||
</scale>
|
||||
</item>
|
||||
<item android:gravity="center_vertical" android:height="40dp">
|
||||
<item>
|
||||
<shape>
|
||||
<stroke android:width="1dp" android:color="?colorM3Outline"/>
|
||||
<corners android:radius="20dp"/>
|
||||
|
||||
9
mastodon/src/main/res/drawable/ic_privacy_tip_24px.xml
Normal file
9
mastodon/src/main/res/drawable/ic_privacy_tip_24px.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M11,17H13V11H11ZM12,9Q12.425,9 12.713,8.712Q13,8.425 13,8Q13,7.575 12.713,7.287Q12.425,7 12,7Q11.575,7 11.288,7.287Q11,7.575 11,8Q11,8.425 11.288,8.712Q11.575,9 12,9ZM12,22Q8.525,21.125 6.263,18.012Q4,14.9 4,11.1V5L12,2L20,5V11.1Q20,14.9 17.738,18.012Q15.475,21.125 12,22ZM12,19.9Q14.6,19.075 16.3,16.6Q18,14.125 18,11.1V6.375L12,4.125L6,6.375V11.1Q6,14.125 7.7,16.6Q9.4,19.075 12,19.9ZM12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M2,14Q1.175,14 0.588,13.412Q0,12.825 0,12V6Q0,5.7 0.125,5.425Q0.25,5.15 0.45,4.95L5.4,0L6.15,0.75Q6.3,0.9 6.4,1.137Q6.5,1.375 6.5,1.6V1.8L5.8,5H11Q11.425,5 11.713,5.287Q12,5.575 12,6V7.25Q12,7.4 11.975,7.537Q11.95,7.675 11.9,7.8L9.65,13.1Q9.475,13.525 9.088,13.762Q8.7,14 8.25,14ZM7.95,12 L10,7.15V7Q10,7 10,7Q10,7 10,7H3.35L3.95,4.3L2,6.2V12Q2,12 2,12Q2,12 2,12ZM18.6,24 L17.85,23.25Q17.7,23.1 17.6,22.863Q17.5,22.625 17.5,22.4V22.2L18.2,19H13Q12.575,19 12.288,18.712Q12,18.425 12,18V16.75Q12,16.6 12.025,16.462Q12.05,16.325 12.1,16.2L14.35,10.9Q14.55,10.475 14.925,10.238Q15.3,10 15.75,10H22Q22.825,10 23.413,10.587Q24,11.175 24,12V18Q24,18.3 23.888,18.562Q23.775,18.825 23.55,19.05ZM16.05,12 L14,16.85V17Q14,17 14,17Q14,17 14,17H20.65L20.05,19.7L22,17.8V12Q22,12 22,12Q22,12 22,12ZM2,12V6.2V7Q2,7 2,7Q2,7 2,7V7.15V12Q2,12 2,12Q2,12 2,12ZM22,12V17.8V17Q22,17 22,17Q22,17 22,17V16.85V12Q22,12 22,12Q22,12 22,12Z"/>
|
||||
</vector>
|
||||
@@ -5,7 +5,9 @@
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:padding="16dp"
|
||||
android:background="@drawable/rect_12dp"
|
||||
android:backgroundTint="?colorM3SurfaceVariant">
|
||||
|
||||
@@ -5,28 +5,30 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingVertical="4dp"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="40dp"
|
||||
android:background="@drawable/bg_poll_option_clickable"
|
||||
android:duplicateParentState="true"
|
||||
android:layoutDirection="locale">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:paddingVertical="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:tint="?colorM3OnSecondaryContainer"
|
||||
android:scaleType="center"
|
||||
@@ -41,7 +43,6 @@
|
||||
android:layout_marginEnd="8dp"
|
||||
android:textAppearance="@style/m3_label_large"
|
||||
android:textColor="?colorM3Primary"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:paddingEnd="26dp"
|
||||
tools:text="scream into void jsfdklfjdalskfjdsalkfjdsalkfjdsalkfdjsalkfdsajlk"/>
|
||||
|
||||
@@ -45,56 +45,10 @@
|
||||
android:visibility="gone"
|
||||
android:importantForAccessibility="no"/>
|
||||
|
||||
<org.joinmastodon.android.ui.views.AutoOrientationLinearLayout
|
||||
android:id="@+id/translate_wrap"
|
||||
<ViewStub
|
||||
android:id="@+id/translation_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="6dp"
|
||||
android:gravity="center_vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/action_btn_wrap"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="6dp"
|
||||
android:clipToPadding="false">
|
||||
<org.joinmastodon.android.ui.views.ProgressBarButton
|
||||
android:id="@+id/translate_btn"
|
||||
style="@style/Widget.Mastodon.M3.Button.Text"
|
||||
android:textColor="?colorM3OnSurfaceVariant"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:minWidth="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="@string/sk_translate_post"/>
|
||||
<ProgressBar
|
||||
android:id="@+id/translate_progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
style="?android:progressBarStyleSmall"
|
||||
android:elevation="10dp"
|
||||
android:outlineProvider="none"
|
||||
android:visibility="gone"/>
|
||||
</FrameLayout>
|
||||
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/translate_info"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:textColor="?colorM3OnSurfaceVariant"
|
||||
android:textAlignment="textEnd"
|
||||
tools:text="Translated using TranslateEngine" />
|
||||
|
||||
</org.joinmastodon.android.ui.views.AutoOrientationLinearLayout>
|
||||
android:layout="@layout/footer_text_translation"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
50
mastodon/src/main/res/layout/footer_text_translation.xml
Normal file
50
mastodon/src/main/res/layout/footer_text_translation.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="6dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/translation_btn_wrap"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="6dp"
|
||||
android:clipToPadding="false">
|
||||
<ProgressBar
|
||||
android:id="@+id/translation_progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:indeterminate="true"
|
||||
style="?android:progressBarStyleSmall"
|
||||
android:visibility="gone"/>
|
||||
<Button
|
||||
android:id="@+id/translation_btn"
|
||||
style="@style/Widget.Mastodon.M3.Button.Text"
|
||||
android:textColor="?colorM3OnSurfaceVariant"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/translation_show_original"/>
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/translation_info_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
android:textAppearance="@style/m3_body_small"
|
||||
android:textColor="?colorM3OnSurfaceVariant"
|
||||
android:textAlignment="textEnd"
|
||||
tools:text="Translated using TranslateEngine" />
|
||||
|
||||
</LinearLayout>
|
||||
62
mastodon/src/main/res/layout/header_hashtag_timeline.xml
Normal file
62
mastodon/src/main/res/layout/header_hashtag_timeline.xml
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toStartOf="@id/follow_btn_wrap"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:textAppearance="@style/m3_headline_small"
|
||||
android:textColor="?colorM3OnSurface"
|
||||
android:maxLines="4"
|
||||
android:ellipsize="end"
|
||||
android:minHeight="48dp"
|
||||
android:gravity="center_vertical"
|
||||
tools:text="#CatsOfMastodonButLong"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/follow_btn_wrap"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignTop="@id/title"
|
||||
android:layout_alignBottom="@id/title"
|
||||
android:layout_alignParentEnd="true">
|
||||
|
||||
<org.joinmastodon.android.ui.views.ProgressBarButton
|
||||
android:id="@+id/profile_action_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center"
|
||||
style="@style/Widget.Mastodon.M3.Button.Filled"
|
||||
android:paddingHorizontal="16dp"
|
||||
tools:text="@string/button_follow" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/action_progress"
|
||||
style="?android:progressBarStyleSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:elevation="10dp"
|
||||
android:indeterminate="true"
|
||||
android:outlineProvider="none"
|
||||
android:visibility="gone" />
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/title"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textAppearance="@style/m3_label_large"
|
||||
android:textColor="?colorM3OnSurfaceVariant"
|
||||
tools:text="123 posts"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -1,13 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/pin"
|
||||
android:title="@string/sk_pin_timeline"
|
||||
android:icon="@drawable/ic_fluent_pin_24_regular"
|
||||
android:showAsAction="always" />
|
||||
<item
|
||||
android:id="@+id/pin"
|
||||
android:title="@string/sk_pin_timeline"
|
||||
android:showAsAction="always"
|
||||
android:icon="@drawable/ic_fluent_pin_24_regular" />
|
||||
<item
|
||||
android:id="@+id/follow_hashtag"
|
||||
android:icon="@drawable/ic_fluent_person_add_24_regular"
|
||||
android:showAsAction="always"
|
||||
android:title="@string/button_follow"/>
|
||||
</menu>
|
||||
@@ -7,6 +7,8 @@
|
||||
</item>
|
||||
<item android:id="@+id/manage_user_lists" android:title="@string/sk_your_lists" android:icon="@drawable/ic_fluent_people_24_regular"/>
|
||||
<item android:id="@+id/favorites" android:title="@string/your_favorites" android:icon="@drawable/ic_fluent_star_24_regular"/>
|
||||
<item android:id="@+id/muted_accounts" android:title="@string/sk_muted_accounts" android:icon="@drawable/ic_fluent_speaker_off_24_regular"/>
|
||||
<item android:id="@+id/blocked_accounts" android:title="@string/sk_blocked_accounts" android:icon="@drawable/ic_fluent_shield_24_regular"/>
|
||||
<item android:id="@+id/scheduled" android:title="@string/sk_unsent_posts" android:icon="@drawable/ic_fluent_folder_open_24_regular"/>
|
||||
<item android:id="@+id/share" android:title="@string/share_user" android:icon="@drawable/ic_fluent_share_24_regular"/>
|
||||
</menu>
|
||||
@@ -8,7 +8,7 @@
|
||||
<string name="ok">حسنًا</string>
|
||||
<string name="preparing_auth">جَارٍ الإعدَادُ لِلمُصادَقَة…</string>
|
||||
<string name="finishing_auth">يُنهي المصادقة…</string>
|
||||
<string name="user_boosted">%s إعادة نشر</string>
|
||||
<string name="user_boosted">قام %s بإعادة نشر</string>
|
||||
<string name="in_reply_to">ردًا على %s</string>
|
||||
<string name="notifications">الإشعارات</string>
|
||||
<string name="user_followed_you">%s بَدَأ بِمُتابَعَتِك</string>
|
||||
@@ -242,7 +242,7 @@
|
||||
<string name="skip">تخطى</string>
|
||||
<string name="notification_type_follow">متابعُون جُدُد</string>
|
||||
<string name="notification_type_favorite">المفضلة</string>
|
||||
<string name="notification_type_reblog">المشاركات</string>
|
||||
<string name="notification_type_reblog">المعاد نشرها</string>
|
||||
<string name="notification_type_mention">الإشارات</string>
|
||||
<string name="notification_type_poll">استطلاع رأي</string>
|
||||
<string name="choose_account">اختر حسابًا</string>
|
||||
@@ -277,7 +277,7 @@
|
||||
<string name="more_options">مزيد من الخيارات</string>
|
||||
<string name="new_post">منشور جديد</string>
|
||||
<string name="button_reply">ردّ</string>
|
||||
<string name="button_reblog">شارك</string>
|
||||
<string name="button_reblog">إعادة النشر</string>
|
||||
<string name="button_favorite">فضّل</string>
|
||||
<string name="button_share">شارك</string>
|
||||
<string name="media_no_description">وسائط بدون وصف</string>
|
||||
@@ -292,8 +292,8 @@
|
||||
<string name="followed_user">أنت تتابع الآن %s</string>
|
||||
<string name="following_user_requested">طَلَبَ %s مُتابَعتك</string>
|
||||
<string name="open_in_browser">افتح في المتصفح</string>
|
||||
<string name="hide_boosts_from_user">اخف مشاركات %s</string>
|
||||
<string name="show_boosts_from_user">أظهر مشاركات %s</string>
|
||||
<string name="hide_boosts_from_user">أخفِ المعاد نشرها مِن %s</string>
|
||||
<string name="show_boosts_from_user">أظهر ما أعاد %s نشرَه</string>
|
||||
<string name="signup_reason">لماذا تريد الانضمام؟</string>
|
||||
<string name="signup_reason_note">هذا سوف يساعدنا في مراجعة تطبيقك.</string>
|
||||
<string name="clear">امسح</string>
|
||||
@@ -346,10 +346,10 @@
|
||||
<item quantity="other">%,d تفضيل</item>
|
||||
</plurals>
|
||||
<plurals name="x_reblogs">
|
||||
<item quantity="zero">%,d إعادة نشر</item>
|
||||
<item quantity="zero">لم يُعد نشره</item>
|
||||
<item quantity="one">إعادة نشر واحدة</item>
|
||||
<item quantity="two">أعيد نشره مرّتان</item>
|
||||
<item quantity="few">أعيد نشره %,d مرة</item>
|
||||
<item quantity="few">أعيد نشره %,d مرات</item>
|
||||
<item quantity="many">أعيد نشره %,d مرات</item>
|
||||
<item quantity="other">أعيد نشره %,d مرات</item>
|
||||
</plurals>
|
||||
@@ -698,4 +698,18 @@
|
||||
<string name="time_minutes_ago_short">مُنذُ %dد</string>
|
||||
<string name="time_hours_ago_short">مُنذُ %dسا</string>
|
||||
<string name="time_days_ago_short">مُنذُ %d أيام</string>
|
||||
<!-- %s is the name of the post language -->
|
||||
<string name="translate_post">تُرجِم مِن %s</string>
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
<string name="post_translated">مُترجَم مِن %1$s باستخدام %2$s</string>
|
||||
<string name="translation_show_original">إظهار الأصل</string>
|
||||
<string name="translation_failed">فشِلَت الترجَمة. قد لم يتمكّن مدير الخادم من تفعيل الترجمات على هذا الخادم أو أنّ هذا الخادم يُشغِّل نسخة قديمة من ماستدون حيث الترجمات غير مدعومة بعد.</string>
|
||||
<plurals name="x_participants">
|
||||
<item quantity="zero">لا مُشارِك</item>
|
||||
<item quantity="one">مشارِك واحد</item>
|
||||
<item quantity="two">مشاركَيْنِ</item>
|
||||
<item quantity="few">مشاركين</item>
|
||||
<item quantity="many">مُشارِكًا</item>
|
||||
<item quantity="other">مُشارك</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -249,4 +249,71 @@
|
||||
<string name="sk_notify_poll_results">نتيجة استطلاع الرأي</string>
|
||||
<string name="sk_filtered">تمت تصفيته: %s</string>
|
||||
<string name="sk_search_fediverse">البحث في الفديفرس</string>
|
||||
<string name="sk_suicide_search_terms">انتحار, انتِحار, الانتحار, الإنتحار, أنتحر</string>
|
||||
<string name="sk_search_suicide_title">إن كنت في مِحنة وبحاجة إلى مساعدة…</string>
|
||||
<string name="sk_search_suicide_message">إذا كنت تبحث عن إشارة لعدم الانتحار، فها هي. إذا كنت تعاني من ضائقة و/أو تراودك أفكار انتحارية، وإذا كنت بحاجة إلى مساعدة، فيمكنك الاتصال بخط المساعدة للوقاية من الانتحار إن كنت في مِحنة.</string>
|
||||
<string name="sk_load_missing_posts_above">حمِّل المنشورات الحديثة</string>
|
||||
<string name="sk_load_missing_posts_below">حمِّل المنشورات القديمة</string>
|
||||
<string name="sk_time_seconds">%d ثواني</string>
|
||||
<string name="sk_time_minutes">%d دقائق</string>
|
||||
<string name="sk_time_hours">%d ساعات</string>
|
||||
<string name="sk_time_days">%d أيام</string>
|
||||
<string name="sk_search_suicide_hotlines">للعثور على خط للمساعدة</string>
|
||||
<string name="sk_confirm_delete_scheduled_post_title">احذف المنشور المُبَرمَج</string>
|
||||
<string name="sk_compose_scheduled">مُبَرمَج لـ</string>
|
||||
<string name="sk_do_remove_follower">إزالة</string>
|
||||
<string name="sk_icon_academic_cap">قبعة جامعية</string>
|
||||
<string name="sk_icon_tag">ملصقة</string>
|
||||
<string name="sk_add_timeline_tag_error_empty">لا يجب أن يُترَك الوسم فارغًا</string>
|
||||
<string name="sk_unfinished_attachments">إصلاح المرفقات؟</string>
|
||||
<plurals name="sk_posts_count_label">
|
||||
<item quantity="zero">لا منشور</item>
|
||||
<item quantity="one">منشور واحد</item>
|
||||
<item quantity="two">منشورَيْنِ</item>
|
||||
<item quantity="few">منشورات</item>
|
||||
<item quantity="many">منشورًا</item>
|
||||
<item quantity="other">منشور</item>
|
||||
</plurals>
|
||||
<string name="sk_notification_mention">لقد قام %s بذِكرك</string>
|
||||
<string name="sk_settings_emoji_reactions">تمكين الوجوه التعبيرية للتفاعل</string>
|
||||
<string name="sk_icon_verified">مُتحَقَّق مِنه</string>
|
||||
<string name="sk_switch_timeline">تبديل الخيط الزمني</string>
|
||||
<string name="sk_settings_show_emoji_reactions_always">إظهار زر الإضافة دائما</string>
|
||||
<string name="sk_do_not_show_again">لا تظهر مرة أخرى</string>
|
||||
<string name="sk_settings_display_pronouns_in_user_listings">إظهار الأسماء في قوائم المستخدِمين</string>
|
||||
<string name="sk_post_contains_media">يحتوي المنشور على وسائط</string>
|
||||
<string name="sk_settings_see_new_posts_button">أظهر زِرّ ”إظهار المنشورات الجديدة“</string>
|
||||
<string name="sk_followed_as">مُتابَع عبر %s</string>
|
||||
<string name="sk_settings_allow_remote_loading">تحميل المعلومات مِن الخوادم البعيدة</string>
|
||||
<string name="sk_content_type_html">HTML</string>
|
||||
<string name="sk_reacted">تَفاعَل %s</string>
|
||||
<string name="sk_reacted_with">تَفاعَلَ %1$s مع %2$s</string>
|
||||
<string name="sk_external_share_or_open_title">إعادة نشر أو فتح بحساب آخَر</string>
|
||||
<string name="sk_content_type_plain">نص عادي</string>
|
||||
<string name="sk_settings_prefix_replies_always">ردًّا على أي كان</string>
|
||||
<string name="sk_settings_forward_report_default">إعادة ”تحويل الإبلاغ“ افتراضيا</string>
|
||||
<string name="sk_content_type_mfm">MFM</string>
|
||||
<string name="sk_settings_hide_fab">إخفاء زر التحرير مبدئيا</string>
|
||||
<string name="sk_enter_emoji_hint">اضغط للتفاعل بوجوه تعبيرية</string>
|
||||
<string name="sk_content_type_markdown">Markdown</string>
|
||||
<string name="sk_content_type_bbcode">BBCode</string>
|
||||
<string name="sk_enter_emoji_toast">يُرجى إدخال إيموجي</string>
|
||||
<string name="sk_in_reply">ردّا على</string>
|
||||
<string name="sk_open_in_app_failed">لا يمكن فتحه في التطبيق</string>
|
||||
<string name="sk_settings_enable_delete_notifications">تمكين حذف الإشعارات</string>
|
||||
<string name="sk_clear_all_notifications_confirm">أتريد حقا حذف كافة الإشعارات؟</string>
|
||||
<string name="sk_already_bookmarked">موجود بالفعل في الفواصل المرجعية</string>
|
||||
<string name="sk_resource_not_found">المَورِد غير موجود</string>
|
||||
<string name="sk_delete_notification_confirm">أتريد حقا حذف هذا الإشعار؟</string>
|
||||
<string name="sk_draft_or_schedule">تحرير مسودة أو برمجتها</string>
|
||||
<string name="sk_trending_links_info_banner">هذه هي القصص الإخبارية التي يُتَحدّث عنها في خادمكم.</string>
|
||||
<string name="sk_notification_type_posts">إشعارات المنشورات</string>
|
||||
<string name="sk_confirm_delete_draft">هل أنت متأكد من أنك تريد حذف مُسودّة هذا المنشور؟</string>
|
||||
<string name="sk_compose_no_schedule">لا تُبرمِجه</string>
|
||||
<string name="sk_confirm_clear_recent_languages">أتريد حقا مسح لغاتك المُستخدَمة حديثا؟</string>
|
||||
<string name="sk_confirm_delete_scheduled_post">هل أنت متأكد من أنك تريد حذف هذا المنشور المُبرمَج؟</string>
|
||||
<string name="sk_trending_posts_info_banner">هذه هي المنشورات المثيرة للإهتمام على خادمكم.</string>
|
||||
<string name="sk_federated_timeline_info_banner">هذه هي أحدث المنشورات للأعضاء في فديراليتك.</string>
|
||||
<string name="sk_bubble_timeline_info_banner">هذه هي أحدث المنشورات على الشبكة والتي انتقاها مُدراء خادمكم.</string>
|
||||
<string name="sk_schedule_or_draft">برمجة أو تحرير مُسودّة</string>
|
||||
</resources>
|
||||
@@ -527,4 +527,6 @@
|
||||
<string name="time_minutes_ago_short">%d хв таму</string>
|
||||
<string name="time_hours_ago_short">%d г таму</string>
|
||||
<string name="time_days_ago_short">%d дз таму</string>
|
||||
<!-- %s is the name of the post language -->
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
</resources>
|
||||
|
||||
@@ -199,4 +199,6 @@
|
||||
<!-- %s is the timestamp ("tomorrow at 12:34") -->
|
||||
<!-- Shown like a content warning, %s is the name of the filter -->
|
||||
<!-- Shown in the post header. Please keep it short -->
|
||||
<!-- %s is the name of the post language -->
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
</resources>
|
||||
|
||||
@@ -182,4 +182,6 @@
|
||||
<!-- %s is the timestamp ("tomorrow at 12:34") -->
|
||||
<!-- Shown like a content warning, %s is the name of the filter -->
|
||||
<!-- Shown in the post header. Please keep it short -->
|
||||
<!-- %s is the name of the post language -->
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
</resources>
|
||||
|
||||
@@ -293,4 +293,6 @@
|
||||
<!-- %s is the timestamp ("tomorrow at 12:34") -->
|
||||
<!-- Shown like a content warning, %s is the name of the filter -->
|
||||
<!-- Shown in the post header. Please keep it short -->
|
||||
<!-- %s is the name of the post language -->
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
</resources>
|
||||
|
||||
@@ -639,4 +639,6 @@
|
||||
<string name="time_minutes_ago_short">Před %dm</string>
|
||||
<string name="time_hours_ago_short">Před %dh</string>
|
||||
<string name="time_days_ago_short">Před %dd</string>
|
||||
<!-- %s is the name of the post language -->
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
</resources>
|
||||
|
||||
@@ -503,4 +503,6 @@
|
||||
<string name="search_open_url">Åbn URL i Mastodon</string>
|
||||
<string name="posts_matching_hashtag">Indlæg med “%s”</string>
|
||||
<!-- Shown in the post header. Please keep it short -->
|
||||
<!-- %s is the name of the post language -->
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
</resources>
|
||||
|
||||
@@ -582,4 +582,6 @@
|
||||
<string name="time_minutes_ago_short">vor %d Minuten</string>
|
||||
<string name="time_hours_ago_short">vor %d Stunden</string>
|
||||
<string name="time_days_ago_short">vor %d Tagen</string>
|
||||
<!-- %s is the name of the post language -->
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
</resources>
|
||||
|
||||
@@ -382,10 +382,6 @@
|
||||
<item quantity="other">Beiträge</item>
|
||||
</plurals>
|
||||
<string name="sk_settings_show_labels_in_navigation_bar">Tab-Bezeichnungen in der Navigationsleiste anzeigen</string>
|
||||
<string name="sk_time_seconds">%d Sekunden</string>
|
||||
<string name="sk_time_minutes">%d Minuten</string>
|
||||
<string name="sk_time_hours">%d Stunden</string>
|
||||
<string name="sk_time_days">%d Tage</string>
|
||||
<string name="sk_suicide_helplines_url">https://findahelpline.com</string>
|
||||
<string name="sk_load_missing_posts_below">Ältere Beiträge laden</string>
|
||||
<string name="sk_load_missing_posts_above">Neuere Beiträge laden</string>
|
||||
|
||||
@@ -387,6 +387,7 @@
|
||||
<string name="welcome_to_mastodon">Καλώς ήρθες στο Mastodon</string>
|
||||
<string name="welcome_paragraph1">Το Mastodon είναι ένα αποκεντρωμένο κοινωνικό δίκτυο που σημαίνει ότι καμία εταιρεία δεν το ελέγχει. Αποτελείται από πολλούς ανεξάρτητους διακομιστές, όλοι συνδεδεμένοι μαζί.</string>
|
||||
<string name="what_are_servers">Τι είναι οι διακομιστές;</string>
|
||||
<string name="welcome_paragraph2">Κάθε λογαριασμός Mastodon φιλοξενείται σε ένα διακομιστή - ο καθένας με τις δικές του αξίες, κανόνες & διαχειριστές. Ανεξάρτητα από το ποιον μπορεί να επιλέξεις, μπορείς να ακολουθήσεις και να αλληλεπιδράσεις με άτομα από οποιονδήποτε διακομιστή.</string>
|
||||
<string name="opening_link">Άνοιγμα συνδέσμου…</string>
|
||||
<string name="link_not_supported">Αυτός ο σύνδεσμος δεν υποστηρίζεται στην εφαρμογή</string>
|
||||
<string name="log_out_all_accounts">Αποσύνδεση από όλους τους λογαριασμούς</string>
|
||||
@@ -581,4 +582,21 @@
|
||||
<string name="time_minutes_ago_short">%dλ πριν</string>
|
||||
<string name="time_hours_ago_short">%dώ πριν</string>
|
||||
<string name="time_days_ago_short">%dημ πριν</string>
|
||||
<!-- %s is the name of the post language -->
|
||||
<string name="translate_post">Μετάφραση από %s</string>
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
<string name="post_translated">Μεταφράστηκε από %1$s χρησιμοποιώντας %2$s</string>
|
||||
<string name="translation_show_original">Εμφάνιση αρχικού</string>
|
||||
<string name="translation_failed">Η μετάφραση απέτυχε. Ίσως ο διαχειριστής δεν έχει ενεργοποιήσει μεταφράσεις σε αυτόν τον διακομιστή ή αυτός ο διακομιστής εκτελεί μια παλαιότερη έκδοση του Mastodon όπου οι μεταφράσεις δεν υποστηρίζονται ακόμα.</string>
|
||||
<string name="settings_privacy">Ιδιωτικότητα και προσιτότητα</string>
|
||||
<string name="settings_discoverable">Παροχή προφίλ και δημοσιεύσεων σε αλγορίθμους ανακάλυψης</string>
|
||||
<string name="settings_indexable">Συμπερίληψη δημόσιων αναρτήσεων στα αποτελέσματα αναζήτησης</string>
|
||||
<plurals name="x_participants">
|
||||
<item quantity="one">%,d συμμετέχων</item>
|
||||
<item quantity="other">%,d συμμετέχοντες</item>
|
||||
</plurals>
|
||||
<plurals name="x_posts_today">
|
||||
<item quantity="one">%,d ανάρτηση σήμερα</item>
|
||||
<item quantity="other">%,d αναρτήσεις σήμερα</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -573,4 +573,6 @@
|
||||
<string name="time_minutes_ago_short">hace %dm</string>
|
||||
<string name="time_hours_ago_short">hace %dh</string>
|
||||
<string name="time_days_ago_short">hace %dd</string>
|
||||
<!-- %s is the name of the post language -->
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
</resources>
|
||||
|
||||
@@ -224,8 +224,8 @@
|
||||
<string name="sk_updater_enable_pre_releases">Activar versiones beta</string>
|
||||
<string name="sk_timeline_posts">Publicaciones</string>
|
||||
<string name="sk_timelines_add">Añadir</string>
|
||||
<string name="sk_timeline">Cronología</string>
|
||||
<string name="sk_hashtag">Etiqueta</string>
|
||||
<string name="sk_timeline">Línea de tiempo</string>
|
||||
<string name="sk_hashtag">Hashtag</string>
|
||||
<string name="sk_pin_timeline">Anclar cronología</string>
|
||||
<string name="sk_unpin_timeline">Desanclar cronología</string>
|
||||
<string name="sk_pinned_timeline">Anclado a inicio</string>
|
||||
@@ -256,7 +256,7 @@
|
||||
<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">Prefijo CW con \"re:\" al responder</string>
|
||||
<string name="sk_settings_prefix_reply_cw_with_re">Añadir \"re\" a Avisos de Contenido</string>
|
||||
<string name="sk_spectator_mode">Modo espectador</string>
|
||||
<string name="sk_settings_hide_interaction">Ocultar los botones de interacción</string>
|
||||
<string name="sk_follow_as">Seguir desde otra cuenta</string>
|
||||
@@ -292,7 +292,7 @@
|
||||
<string name="sk_external_share_or_open_title">Compartir o abrir con una cuenta</string>
|
||||
<string name="sk_open_in_app">Abrir en la app</string>
|
||||
<string name="sk_external_share_title">Compartir con una cuenta</string>
|
||||
<string name="sk_settings_auto_reveal_equal_spoilers">Revelar automáticamente los CW iguales en las respuestas</string>
|
||||
<string name="sk_settings_auto_reveal_equal_spoilers">Mostrar automáticamente Avisos de Contenido de respuestas</string>
|
||||
<string name="sk_settings_auto_reveal_nobody">Nunca</string>
|
||||
<string name="sk_settings_auto_reveal_author">Respuestas del mismo autor</string>
|
||||
<string name="sk_settings_auto_reveal_anyone">Respuestas de todos</string>
|
||||
@@ -301,14 +301,14 @@
|
||||
<string name="sk_error_loading_profile">No se pudo cargar el perfil a través de %s</string>
|
||||
<string name="sk_settings_allow_remote_loading">Cargar la información desde las instancias remotas</string>
|
||||
<string name="sk_settings_allow_remote_loading_explanation">Intenta obtener listas más precisas de seguidores, Me gusta y promociones cargando la información desde la instancia de origen.</string>
|
||||
<string name="sk_settings_prefix_replies_always">Responder a cualquiera</string>
|
||||
<string name="sk_settings_prefix_replies_always">En cualquier respuesta</string>
|
||||
<string name="sk_settings_prefix_replies_never">Nunca</string>
|
||||
<string name="sk_settings_prefix_replies_to_others">Solo en respuesta a otros</string>
|
||||
<string name="sk_settings_prefix_replies_to_others">Solo es respuestas a otros</string>
|
||||
<string name="sk_settings_forward_report_default">\"Reenviar denuncia\" activado por defecto</string>
|
||||
<string name="sk_list_exclusive_switch">Hacer una lista exclusiva</string>
|
||||
<string name="sk_icon_feed">Cronología</string>
|
||||
<string name="sk_exclusive_list">Lista exclusiva</string>
|
||||
<string name="sk_list_exclusive_switch_explanation">Los miembros de una lista exclusiva no aparecerán en tu cronología de inicio - si tu instancia lo admite.</string>
|
||||
<string name="sk_list_exclusive_switch">Crear una lista excluyente</string>
|
||||
<string name="sk_icon_feed">Feed</string>
|
||||
<string name="sk_exclusive_list">Lista excluyente</string>
|
||||
<string name="sk_list_exclusive_switch_explanation">Los miembros de una lista excluyente no aparecerán en tu cronología de inicio - si tu instancia lo permite.</string>
|
||||
<string name="sk_icon_verified">Verificado</string>
|
||||
<string name="sk_icon_doctor">Médico</string>
|
||||
<string name="sk_icon_diamond">Diamante</string>
|
||||
@@ -319,8 +319,8 @@
|
||||
<string name="sk_edit_timeline_tag_none">...pero ninguna de estas</string>
|
||||
<string name="sk_hashtag_timeline_local_only_switch">¿Mostrar sólo las publicaciones locales\?</string>
|
||||
<string name="sk_add_timeline_tag_error_empty">La etiqueta no puede quedar vacía</string>
|
||||
<string name="sk_advanced_options_show">Mostrar las opciones avanzadas</string>
|
||||
<string name="sk_advanced_options_hide">Ocultar las opciones avanzadas</string>
|
||||
<string name="sk_advanced_options_show">Mostrar opciones avanzadas</string>
|
||||
<string name="sk_advanced_options_hide">Ocultar opciones avanzadas</string>
|
||||
<string name="sk_icon_recycle_bin">Papelera de reciclaje</string>
|
||||
<string name="sk_add_timeline">Agregar una cronología</string>
|
||||
<string name="sk_edit_timeline_tag_hint">Introduce la etiqueta…</string>
|
||||
@@ -357,37 +357,47 @@
|
||||
<string name="sk_settings_continues_playback_summary">Permitir que los medios que ya se están reproduciendo sigan reproduciéndose, superponiéndose a la nueva reproducción</string>
|
||||
<string name="sk_settings_unifiedpush">Usar UnifiedPush</string>
|
||||
<string name="sk_settings_unifiedpush_no_distributor_body">Es necesario instalar un distribuidor para que funcionen las notificaciones de UnifiedPush. Para obtener más información, visita https://unifiedpush.org/</string>
|
||||
<string name="sk_settings_display_pronouns_in_timelines">Mostrar pronombres en líneas de tiempo</string>
|
||||
<string name="sk_settings_display_pronouns_in_timelines">Mostrar pronombres en cronologías</string>
|
||||
<string name="sk_settings_display_pronouns_in_threads">Mostrar pronombres en los hilos</string>
|
||||
<string name="sk_settings_show_labels_in_navigation_bar">Mostrar las etiquetas de las pestañas en la barra de navegación</string>
|
||||
<string name="sk_settings_show_labels_in_navigation_bar">Mostrar etiquetas de pestañas en la barra de navegación</string>
|
||||
<string name="sk_settings_emoji_reactions_in_lists">Mostrar las reacciones con los emoticonos en las líneas de tiempo</string>
|
||||
<plurals name="sk_users_reacted_with">
|
||||
<item quantity="one">Un usuario reaccionó con %2$s</item>
|
||||
<item quantity="many">%1$,d usuarios reaccionaron con %2$s</item>
|
||||
<item quantity="other">%1$,d usuarios reaccionaron con %2$s</item>
|
||||
</plurals>
|
||||
<string name="sk_enter_emoji_toast">Necesitas escribir un emoticono</string>
|
||||
<string name="sk_enter_emoji_toast">Por favor, introduce un emoticono</string>
|
||||
<string name="sk_enter_emoji_hint">Escribe para reaccionar con un emoticono</string>
|
||||
<string name="sk_settings_emoji_reactions">Activar las reacciones con los emoticonos</string>
|
||||
<string name="sk_settings_emoji_reactions_explanation">Muestra las reacciones con los emoticonos a los mensajes y te permite interactuar con ellos. Algunas versiones modificadas de Mastodon lo soportan, pero Mastodon no.</string>
|
||||
<string name="sk_settings_emoji_reactions">Activar reacciones con emoticonos</string>
|
||||
<string name="sk_settings_emoji_reactions_explanation">Muestra las reacciones con emoticonos a las publicaciones y te permite añadir las tuyas. Varias instancias del Fediverso lo soportan, pero Mastodon no.</string>
|
||||
<string name="sk_settings_emoji_reactions_in_lists_explanation">Las reacciones con los emoticonos deben mostrarse en las líneas de tiempo. Si esta opción está desactivada, las reacciones con los emoticonos solo se mostrarán al ver un hilo.</string>
|
||||
<string name="sk_button_react">Reacciona con un emoticono</string>
|
||||
<string name="sk_again_for_system_keyboard">Pulsa de nuevo para el Teclado del sistema</string>
|
||||
<plurals name="sk_posts_count_label">
|
||||
<item quantity="one">tema</item>
|
||||
<item quantity="many">temas</item>
|
||||
<item quantity="other">temas</item>
|
||||
<item quantity="one">publicación</item>
|
||||
<item quantity="many">publicaciones</item>
|
||||
<item quantity="other">publicaciones</item>
|
||||
</plurals>
|
||||
<string name="sk_settings_show_emoji_reactions_hide_empty">Ocultar emoji vacíos</string>
|
||||
<string name="sk_settings_show_emoji_reactions_only_opened">Sólo cuando se abre un tema</string>
|
||||
<string name="sk_settings_show_emoji_reactions_hide_empty">Ocultar emoticonos sin reacciones</string>
|
||||
<string name="sk_settings_show_emoji_reactions_only_opened">Sólo cuando se abre la publicación</string>
|
||||
<string name="sk_settings_show_emoji_reactions_always">Mostrar siempre el botón de añadir</string>
|
||||
<string name="sk_search_suicide_hotlines">Encontrar un teléfono de ayuda</string>
|
||||
<string name="sk_search_suicide_hotlines">Encontrar un teléfono de ayuda (024 en España)</string>
|
||||
<string name="sk_do_not_show_again">No mostrar de nuevo</string>
|
||||
<string name="sk_post_contains_media">El tema contiene medios</string>
|
||||
<string name="sk_post_contains_media">La publicación contiene multimedia</string>
|
||||
<string name="sk_load_missing_posts_oldest_first">El más antiguo primero (hacia arriba)</string>
|
||||
<string name="sk_settings_show_emoji_reactions">Mostrar los emoji en las líneas de tiempo</string>
|
||||
<string name="sk_search_suicide_title">En caso de que estés en apuros…</string>
|
||||
<string name="sk_settings_show_emoji_reactions">Mostrar emoticonos en las cronologías</string>
|
||||
<string name="sk_search_suicide_title">En caso de que estés en un mal momento…</string>
|
||||
<string name="sk_suicide_helplines_url">https://findahelpline.com</string>
|
||||
<string name="sk_settings_load_missing_posts">Cargar los mensajes que faltan</string>
|
||||
<string name="sk_load_missing_posts_newest_first">Lo más nuevo primero (hacia abajo)</string>
|
||||
<string name="sk_time_minutes">%d minutos</string>
|
||||
<string name="sk_time_hours">%d horas</string>
|
||||
<string name="sk_time_days">%d días</string>
|
||||
<string name="sk_suicide_search_terms">Suicidio</string>
|
||||
<string name="sk_search_suicide_message">Si buscas una señal para no suicidarte, es ésta. Por favor, piensa el ponerte en contacto con una línea directa para ayuda al suicido si estás angustiado.</string>
|
||||
<string name="sk_load_missing_posts_below">Cargar publicaciones más antiguas</string>
|
||||
<string name="sk_time_seconds">%d segundos</string>
|
||||
<string name="sk_load_missing_posts_above">Cargar publicaciones más recientes</string>
|
||||
<string name="sk_trending_posts_info_banner">Estos son los posts que ganan popularidad en tu servidor.</string>
|
||||
<string name="sk_trending_links_info_banner">Estas son las noticias de las que se habla en tu servidor.</string>
|
||||
</resources>
|
||||
@@ -441,4 +441,6 @@
|
||||
<string name="clear_all">Garbitu dena</string>
|
||||
<string name="search_open_url">Ireki URLa Mastodonen</string>
|
||||
<!-- Shown in the post header. Please keep it short -->
|
||||
<!-- %s is the name of the post language -->
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
</resources>
|
||||
|
||||
@@ -387,6 +387,7 @@
|
||||
<string name="welcome_to_mastodon">به ماستودون خوش آمدید</string>
|
||||
<string name="welcome_paragraph1">ماستودون یک شبکه اجتماعی غیر متمرکز است،به این معنی که هیچ شرکتی آن را کنترل نمی کند. این از بسیاری از کارسازهای مستقل تشکیل شده است که همه به هم متصل هستند.</string>
|
||||
<string name="what_are_servers">کارساز شما کجاست؟</string>
|
||||
<string name="welcome_paragraph2">هر حساب ماستودون بر روی یک سرور میزبانی می شود — هر کدام با مقادیر، قوانین، & مدیران خاص خود. مهم نیست کدام یک را انتخاب می کنید، می توانید افراد را در هر کارسازی دنبال کنید و با آنها تعامل داشته باشید.</string>
|
||||
<string name="opening_link">باز کردن پیوند…</string>
|
||||
<string name="link_not_supported">این پیوند در کاره پشتیبانی نمی شود</string>
|
||||
<string name="log_out_all_accounts">از همه حسابها خارج شوید</string>
|
||||
@@ -581,4 +582,21 @@
|
||||
<string name="time_minutes_ago_short">%dدقيقه پيش</string>
|
||||
<string name="time_hours_ago_short">%dساعت پيش</string>
|
||||
<string name="time_days_ago_short">%dروز پیش</string>
|
||||
<!-- %s is the name of the post language -->
|
||||
<string name="translate_post">ترجمه از %s</string>
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
<string name="post_translated">ترجمه از %1$s با %2$s</string>
|
||||
<string name="translation_show_original">نمایش اصلی</string>
|
||||
<string name="translation_failed">ترجمه ناموفق بود. شاید مدیر ترجمهها را در این کارساز فعال نکرده باشد یا این کارساز نسخه قدیمی ماستودون را اجرا می کند که در آن ترجمهها هنوز پشتیبانی نمی شوند.</string>
|
||||
<string name="settings_privacy">محرمانگی و دسترسی</string>
|
||||
<string name="settings_discoverable">مشخص کردن مشخصات و فرستهها در الگوریتمهای اکتشاف</string>
|
||||
<string name="settings_indexable">قرار دادن فرستههای عمومی در نتایج جستجو</string>
|
||||
<plurals name="x_participants">
|
||||
<item quantity="one">%,d شرکت کننده</item>
|
||||
<item quantity="other">%,d شرکتکننده</item>
|
||||
</plurals>
|
||||
<plurals name="x_posts_today">
|
||||
<item quantity="one">%,d فرسته امروز</item>
|
||||
<item quantity="other">%,d فرسته امروز</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -357,12 +357,12 @@
|
||||
<string name="sk_notification_mention">شما توسط %s نامبرده شده اید</string>
|
||||
<string name="sk_settings_unifiedpush">استفاده از UnifiedPush</string>
|
||||
<string name="sk_duration_days_7">7 روز</string>
|
||||
<string name="sk_settings_unifiedpush_no_distributor_body">برای کارکردن آگاهیهای UnifiedPush باید یک توزیعکننده نصب کنید. برای اطلاعات بیشتر به https://unifiedpush.org/ مراجعه کنید.</string>
|
||||
<string name="sk_settings_unifiedpush_no_distributor_body">برای کارکردن آگاهیهای یونیفیتاید پوش باید یک توزیعکننده نصب کنید. برای اطلاعات بیشتر به https://unifiedpush.org/ مراجعه کنید</string>
|
||||
<string name="sk_settings_continues_playback">پوشش صوتی</string>
|
||||
<string name="sk_settings_continues_playback_summary">به رسانهای که از قبل پخش میشود اجازه دهید به پخش ادامه دهد و پخش جدید را پوشش دهد</string>
|
||||
<string name="sk_button_react">واکنش با ایموجی</string>
|
||||
<string name="sk_again_for_system_keyboard">دوباره برای کیبورد سامانه ضربه بزنید</string>
|
||||
<string name="sk_enter_emoji_toast">باید یک ایموجی تایپ کنید</string>
|
||||
<string name="sk_enter_emoji_toast">لطفا یک ایموجی تایپ کنید</string>
|
||||
<string name="sk_enter_emoji_hint">برای واکنش با ایموجی تایپ کنید</string>
|
||||
<plurals name="sk_users_reacted_with">
|
||||
<item quantity="one">یک کاربر با %2$s واکنش نشان داد</item>
|
||||
@@ -371,5 +371,28 @@
|
||||
<string name="sk_settings_emoji_reactions">بهکار انداختن واکنش های ایموجی</string>
|
||||
<string name="sk_settings_emoji_reactions_in_lists">نمایش واکنش های ایموجی در خطوط زمانی</string>
|
||||
<string name="sk_settings_emoji_reactions_in_lists_explanation">اینکه آیا واکنش های ایموجی باید در خطوط زمانی نمایش داده شود یا خیر. اگر این گزینه خاموش باشد، واکنش های ایموجی فقط هنگام مشاهده یک موضوع نمایش داده می شود.</string>
|
||||
<string name="sk_settings_emoji_reactions_explanation">واکنشهای ایموجی را به فرستهها نشان میدهد و به شما امکان میدهد با آنها تعامل داشته باشید. برخی از نسخه های اصلاح شده ماستودون از این پشتیبانی می کنند، اما ماستودون اینطور نیست.</string>
|
||||
<string name="sk_settings_emoji_reactions_explanation">واکنشهای ایموجی را به فرستهها نشان میدهد و به شما امکان میدهد عکسالعمل خود را اضافه کنید.. سرور های مختلف فدیورس از این پشتیبانی می کنند، اما ماستودون اینطور نیست.</string>
|
||||
<string name="sk_settings_show_emoji_reactions_only_opened">فقط زمانی که فرسته باز شود</string>
|
||||
<string name="sk_settings_show_emoji_reactions_hide_empty">نهفتن واکنشهای ایموجی خالی</string>
|
||||
<string name="sk_settings_show_emoji_reactions">نمایش واکنش های ایموجی در خطوط زمانی</string>
|
||||
<string name="sk_time_seconds">%d ثانیه</string>
|
||||
<string name="sk_time_hours">%d ساعت</string>
|
||||
<plurals name="sk_posts_count_label">
|
||||
<item quantity="one">فرسته</item>
|
||||
<item quantity="other">فرستهها</item>
|
||||
</plurals>
|
||||
<string name="sk_suicide_search_terms">خودکشی</string>
|
||||
<string name="sk_load_missing_posts_above">بارگذاری فرستههای جدیدتر</string>
|
||||
<string name="sk_time_days">%d روز</string>
|
||||
<string name="sk_settings_show_emoji_reactions_always">دکمه افزودن همیشه نشان داده شود</string>
|
||||
<string name="sk_search_suicide_hotlines">یک راه کمکی بیابید</string>
|
||||
<string name="sk_do_not_show_again">دوباره نمایش نده</string>
|
||||
<string name="sk_suicide_helplines_url">https://findahelpline.com</string>
|
||||
<string name="sk_trending_posts_info_banner">اینها فرستههایی هستند که در کارساز شما مورد توجه قرار می گیرند.</string>
|
||||
<string name="sk_load_missing_posts_below">بارگذاری فرستههای قدیمیتر</string>
|
||||
<string name="sk_search_suicide_title">اگر نیازمند کمک هستید…</string>
|
||||
<string name="sk_time_minutes">%d دقیقه</string>
|
||||
<string name="sk_search_suicide_message">اگر به دنبال نشانه ای برای خودکشی نیستید، همین است. لطفاً اگر نیازمند کمک هستید با شماره تلفن محلی خودکشی تماس بگیرید.</string>
|
||||
<string name="sk_trending_links_info_banner">اینها اخباری است که در کارساز شما در مورد آنها صحبت می شود.</string>
|
||||
<string name="sk_post_contains_media">فرسته حاوی رسانهست</string>
|
||||
</resources>
|
||||
@@ -2,13 +2,167 @@
|
||||
<resources>
|
||||
<string name="log_in">Kirjaudu sisään</string>
|
||||
<string name="next">Seuraava</string>
|
||||
<string name="loading_instance">Haetaan palvelimen tietoja…</string>
|
||||
<string name="error">Virhe</string>
|
||||
<string name="not_a_mastodon_instance">%s ei näytä olevan Mastodonin palvelin.</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="preparing_auth">Valmistellaan todennusta…</string>
|
||||
<string name="finishing_auth">Viimeistellään todennusta…</string>
|
||||
<string name="user_boosted">%s tehosti</string>
|
||||
<string name="in_reply_to">Vastauksessa %s</string>
|
||||
<string name="notifications">Ilmoitukset</string>
|
||||
<string name="user_followed_you">%s seurasi sinua</string>
|
||||
<string name="user_sent_follow_request">%s lähetti sinulle seurauspyynnön</string>
|
||||
<string name="user_favorited">%s tykkäsi julkaisustasi</string>
|
||||
<string name="notification_boosted">%s tehosti viestiäsi</string>
|
||||
<string name="poll_ended">Katso tulokset äänestyksestä johon osallistuit</string>
|
||||
<string name="share_toot_title">Jaa</string>
|
||||
<string name="settings">Asetukset</string>
|
||||
<string name="publish">Julkaise</string>
|
||||
<string name="discard_draft">Hylkää luonnos?</string>
|
||||
<string name="discard">Hylkää</string>
|
||||
<string name="cancel">Kumoa</string>
|
||||
<plurals name="followers">
|
||||
<item quantity="one">seuraaja</item>
|
||||
<item quantity="other">seuraajat</item>
|
||||
</plurals>
|
||||
<plurals name="following">
|
||||
<item quantity="one">seurataan</item>
|
||||
<item quantity="other">seurataan</item>
|
||||
</plurals>
|
||||
<string name="posts">Viestit</string>
|
||||
<string name="posts_and_replies">Viestit ja vastaukset</string>
|
||||
<string name="media">Media</string>
|
||||
<string name="profile_about">Tietoja</string>
|
||||
<string name="button_follow">Seuraa</string>
|
||||
<string name="button_following">Seurataan</string>
|
||||
<string name="edit_profile">Muokkaa profiilia</string>
|
||||
<string name="share_user">Jaa profiili</string>
|
||||
<string name="mute_user">Mykistä %s</string>
|
||||
<string name="unmute_user">Poista mykistys tililtä %s</string>
|
||||
<string name="block_user">Estä %s</string>
|
||||
<string name="unblock_user">Poista käyttäjän %s esto</string>
|
||||
<string name="report_user">Raportoi %s</string>
|
||||
<string name="block_domain">Estä %s</string>
|
||||
<string name="unblock_domain">Poista käyttäjän %s esto</string>
|
||||
<plurals name="x_posts">
|
||||
<item quantity="one">%,d julkaisu</item>
|
||||
<item quantity="other">%,d julkaisua</item>
|
||||
</plurals>
|
||||
<string name="profile_joined">Liittynyt</string>
|
||||
<string name="done">Valmis</string>
|
||||
<string name="loading">Ladataan…</string>
|
||||
<string name="field_label">Nimi</string>
|
||||
<string name="field_content">Sisältö</string>
|
||||
<string name="saving">Tallennetaan…</string>
|
||||
<string name="post_from_user">Julkaisu tililtä %s</string>
|
||||
<string name="poll_option_hint">Vaihtoehto %d</string>
|
||||
<plurals name="x_minutes">
|
||||
<item quantity="one">%d minuutti</item>
|
||||
<item quantity="other">%d minuuttia</item>
|
||||
</plurals>
|
||||
<plurals name="x_hours">
|
||||
<item quantity="one">%d tunti</item>
|
||||
<item quantity="other">%d tuntia</item>
|
||||
</plurals>
|
||||
<plurals name="x_days">
|
||||
<item quantity="one">%d päivä</item>
|
||||
<item quantity="other">%d päivää</item>
|
||||
</plurals>
|
||||
<plurals name="x_seconds_left">
|
||||
<item quantity="one">%d sekunti jäljellä</item>
|
||||
<item quantity="other">%d sekunttia jäljellä</item>
|
||||
</plurals>
|
||||
<plurals name="x_minutes_left">
|
||||
<item quantity="one">%d minuutti jäljellä</item>
|
||||
<item quantity="other">%d minuuttia jäljellä</item>
|
||||
</plurals>
|
||||
<plurals name="x_hours_left">
|
||||
<item quantity="one">%d tunti jäljellä</item>
|
||||
<item quantity="other">%d tuntia jäljellä</item>
|
||||
</plurals>
|
||||
<plurals name="x_days_left">
|
||||
<item quantity="one">%d päivä jäljellä</item>
|
||||
<item quantity="other">%d päivää jäljellä</item>
|
||||
</plurals>
|
||||
<plurals name="x_votes">
|
||||
<item quantity="one">%d ääni</item>
|
||||
<item quantity="other">%d ääntä</item>
|
||||
</plurals>
|
||||
<string name="poll_closed">Suljettu</string>
|
||||
<string name="confirm_mute_title">Mykistä tili</string>
|
||||
<string name="confirm_mute">Vahvista käyttäjän %s mykistys</string>
|
||||
<string name="do_mute">Mykistä</string>
|
||||
<string name="confirm_unmute_title">Poista tilin mykistys</string>
|
||||
<string name="confirm_unmute">Vahvista, että haluat poistaa mykistyksen tililtä %s</string>
|
||||
<string name="do_unmute">Poista mykistys</string>
|
||||
<string name="confirm_block_title">Estä tili</string>
|
||||
<string name="confirm_block_domain_title">Estä verkkotunnus</string>
|
||||
<string name="confirm_block">Vahvista käyttäjän %s esto</string>
|
||||
<string name="do_block">Estä</string>
|
||||
<string name="confirm_unblock_title">Poista tilin esto</string>
|
||||
<string name="confirm_unblock_domain_title">Poista verkkotunnuksen esto</string>
|
||||
<string name="confirm_unblock">Vahvista, että haluat poistaa tilin %s eston</string>
|
||||
<string name="do_unblock">Poista esto</string>
|
||||
<string name="button_blocked">Estetty</string>
|
||||
<string name="action_vote">Äänestä</string>
|
||||
<string name="delete">Poista</string>
|
||||
<string name="confirm_delete_title">Poista julkaisu</string>
|
||||
<string name="confirm_delete">Haluatko varmasti poistaa tämän julkaisun?</string>
|
||||
<string name="deleting">Poistetaan…</string>
|
||||
<string name="notification_channel_audio_player">Äänen toisto</string>
|
||||
<string name="play">Toista</string>
|
||||
<string name="pause">Tauko</string>
|
||||
<string name="log_out">Kirjaudu ulos</string>
|
||||
<string name="add_account">Lisää tili</string>
|
||||
<string name="search_hint">Haku</string>
|
||||
<string name="hashtags">Aihetunnisteet</string>
|
||||
<string name="news">Uutiset</string>
|
||||
<string name="for_you">Sinulle</string>
|
||||
<string name="all_notifications">Kaikki</string>
|
||||
<string name="mentions">Maininnat</string>
|
||||
<plurals name="x_people_talking">
|
||||
<item quantity="one">%d henkilö puhuu</item>
|
||||
<item quantity="other">%d henkilöä puhuu</item>
|
||||
</plurals>
|
||||
<string name="report_title">Raportoi %s</string>
|
||||
<string name="report_choose_reason">Mikä on väärin tässä julkaisussa?</string>
|
||||
<string name="report_choose_reason_account">Mikä on vialla käyttäjässä %s?</string>
|
||||
<string name="report_choose_reason_subtitle">Valitse se, mikä sopii parhaiten</string>
|
||||
<string name="report_reason_personal">En pidä siitä</string>
|
||||
<string name="report_reason_personal_subtitle">Tätä ei halua nähdä</string>
|
||||
<string name="report_reason_spam">Se on roskapostia</string>
|
||||
<string name="report_reason_spam_subtitle">Haitalliset linkit, väärennetyt sitoutumiset tai toistuvat vastaukset</string>
|
||||
<string name="report_reason_violation">Se rikkoo palvelimen sääntöjä</string>
|
||||
<string name="report_reason_violation_subtitle">Tiedät, että se rikkoo tiettyjä sääntöjä</string>
|
||||
<string name="report_reason_other">Jotain muuta</string>
|
||||
<string name="report_reason_other_subtitle">Ongelma ei sovi muihin kategorioihin</string>
|
||||
<string name="report_choose_rule">Mitä sääntöjä rikotaan?</string>
|
||||
<string name="report_choose_rule_subtitle">Valitse kaikki sopivat</string>
|
||||
<string name="report_choose_posts">Onko julkaisuja, jotka tukevat tätä raporttia?</string>
|
||||
<string name="report_choose_posts_subtitle">Valitse kaikki sopivat</string>
|
||||
<string name="report_comment_title">Olisiko jotain muuta, mitä meidän pitäisi tietää?</string>
|
||||
<string name="report_comment_hint">Lisäkommentit</string>
|
||||
<string name="sending_report">Lähetetään raporttia…</string>
|
||||
<string name="report_sent_title">Kiitos raportista, tutkimme asiaa.</string>
|
||||
<string name="report_sent_subtitle">Sillä välin kun tarkistamme tätä, voit ryhtyä toimenpiteisiin käyttäjää @%s vastaan:</string>
|
||||
<string name="unfollow_user">Lopeta käyttäjän %s seuraaminen</string>
|
||||
<string name="unfollow">Lopeta seuraaminen</string>
|
||||
<string name="mute_user_explain">Et näe hänen viestejään. Hän voi silti seurata sinua ja nähdä viestisi. Hän ei tiedä, että on mykistetty.</string>
|
||||
<string name="block_user_explain">Et näe hänen viestejään, eikä hän voi nähdä viestejäsi tai seurata sinua. Hän näkevät, että olet estänyt hänet.</string>
|
||||
<string name="report_personal_title">Etkö halua nähdä tätä?</string>
|
||||
<string name="report_personal_subtitle">Tässä on vaihtoehtosi hallita näkemääsi Mastodonissa:</string>
|
||||
<string name="back">Takaisin</string>
|
||||
<string name="search_communities">Palvelimen nimi tai URL-osoite</string>
|
||||
<string name="instance_rules_title">Palvelimen säännöt</string>
|
||||
<string name="instance_rules_subtitle">Jatkamalla sitoudut noudattamaan seuraavia sääntöjä, jotka %s moderaattorit ovat asettaneet ja valvoneet.</string>
|
||||
<string name="signup_title">Luo tili</string>
|
||||
<string name="display_name">Nimi</string>
|
||||
<string name="username">Käyttäjätunnus</string>
|
||||
<string name="email">Sähköposti</string>
|
||||
<string name="password">Salasana</string>
|
||||
<string name="confirm_password">Vahvista salasana</string>
|
||||
<string name="password_note">Sisällytä suuraakkoset, erikoismerkit, ja numerot, jotta voit lisätä salasanan voimaa.</string>
|
||||
<string name="category_academia">Akateeminen</string>
|
||||
<string name="category_activism">Aktivismi</string>
|
||||
<string name="category_all">Kaikki</string>
|
||||
@@ -22,39 +176,424 @@
|
||||
<string name="category_music">Musiikki</string>
|
||||
<string name="category_regional">Alueellinen</string>
|
||||
<string name="category_tech">Teknologia</string>
|
||||
<string name="confirm_email_title">Tarkista sähköpostisi</string>
|
||||
<!-- %s is the email address -->
|
||||
<string name="confirm_email_subtitle">Napauta lähettämäämme linkkiä vahvistaaksesi tunnuksen %s. Odotamme täällä.</string>
|
||||
<string name="confirm_email_didnt_get">Etkö saanut linkkiä?</string>
|
||||
<string name="resend">Lähetä uudelleen</string>
|
||||
<string name="open_email_app">Avaa sähköpostiohjelma</string>
|
||||
<string name="resent_email">Vahvistusviesti lähetetty</string>
|
||||
<string name="compose_hint">Kirjoita tai liitä mitä mietit</string>
|
||||
<string name="content_warning">Sisältövaroitus</string>
|
||||
<string name="save">Tallenna</string>
|
||||
<string name="add_alt_text">Lisää selitys</string>
|
||||
<string name="visibility_public">Julkinen</string>
|
||||
<string name="visibility_followers_only">Vain seuraajat</string>
|
||||
<string name="visibility_private">Vain mainitsemani tilit</string>
|
||||
<string name="recent_searches">Viimeisimmät</string>
|
||||
<string name="skip">Ohita</string>
|
||||
<string name="notification_type_follow">Uudet seuraajat</string>
|
||||
<string name="notification_type_favorite">Suosikit</string>
|
||||
<string name="notification_type_reblog">Tehostukset</string>
|
||||
<string name="notification_type_mention">Maininnat</string>
|
||||
<string name="notification_type_poll">Kyselyt</string>
|
||||
<string name="choose_account">Valitse tili</string>
|
||||
<string name="err_not_logged_in">Kirjaudu ensin Mastodoniin</string>
|
||||
<plurals name="cant_add_more_than_x_attachments">
|
||||
<item quantity="one">Et voi lisätä enempää kuin %d medialiitteen</item>
|
||||
<item quantity="other">Et voi lisätä enempää kuin %d medialiitettä</item>
|
||||
</plurals>
|
||||
<string name="media_attachment_unsupported_type">Tiedosto %s on tyyppiä jota ei tueta</string>
|
||||
<string name="media_attachment_too_big">Tiedosto %1$s ylittää %2$s MB kokorajan</string>
|
||||
<string name="settings_theme">Ulkoasu</string>
|
||||
<string name="theme_auto">Käytä laitteen ulkoasua</string>
|
||||
<string name="theme_light">Vaalea</string>
|
||||
<string name="theme_dark">Tumma</string>
|
||||
<string name="settings_behavior">Toiminnot</string>
|
||||
<string name="settings_gif">Toista animoidut käyttäjäkuvat ja emojit</string>
|
||||
<string name="settings_custom_tabs">Käytä sovelluksen sisäistä selainta</string>
|
||||
<string name="settings_notifications">Ilmoitukset</string>
|
||||
<string name="settings_contribute">Osallistu Mastodoniin</string>
|
||||
<string name="settings_tos">Käyttöehdot</string>
|
||||
<string name="settings_privacy_policy">Tietosuojakäytäntö</string>
|
||||
<string name="settings_clear_cache">Tyhjennä median välimuisti</string>
|
||||
<string name="settings_app_version">Mastodon Android v%1$s (%2$d)</string>
|
||||
<string name="media_cache_cleared">Median välimuisti tyhjennetty</string>
|
||||
<string name="confirm_log_out">Kirjaudu ulos %s?</string>
|
||||
<string name="sensitive_content_explain">Kirjoittaja merkitsi tämän median arkaluontoiseksi.</string>
|
||||
<string name="avatar_description">Avaa profiili %s</string>
|
||||
<string name="more_options">Lisää asetuksia</string>
|
||||
<string name="new_post">Uusi julkaisu</string>
|
||||
<string name="button_reply">Vastaa</string>
|
||||
<string name="button_reblog">Tehosta</string>
|
||||
<string name="button_favorite">Suosikki</string>
|
||||
<string name="button_share">Jaa</string>
|
||||
<string name="media_no_description">Kuva ilma kuvausta</string>
|
||||
<string name="add_media">Lisää mediatiedosto</string>
|
||||
<string name="add_poll">Lisää kysely</string>
|
||||
<string name="emoji">Emoji</string>
|
||||
<string name="home_timeline">Kotiaikajana</string>
|
||||
<string name="my_profile">Oma profiili</string>
|
||||
<string name="media_viewer">Median katselin</string>
|
||||
<string name="follow_user">Follow %s</string>
|
||||
<string name="unfollowed_user">Käyttäjän %s seuraaminen lopetettu</string>
|
||||
<string name="followed_user">Seuraat nyt käyttäjää %s</string>
|
||||
<string name="following_user_requested">Käyttäjän %s seuraamista pyydetty</string>
|
||||
<string name="open_in_browser">Avaa selaimessa</string>
|
||||
<string name="hide_boosts_from_user">Piilota käyttäjän @%s tehostukset</string>
|
||||
<string name="show_boosts_from_user">Näytä tehostukset käyttäjältä @%s</string>
|
||||
<string name="signup_reason">Miksi haluat liittyä?</string>
|
||||
<string name="signup_reason_note">Tämä auttaa meitä arvioimaan hakemustasi.</string>
|
||||
<string name="clear">Tyhjennä</string>
|
||||
<string name="profile_header">Otsikon kuva</string>
|
||||
<string name="profile_picture">Profiilikuva</string>
|
||||
<string name="reorder">Järjestä uudelleen</string>
|
||||
<string name="download">Lataa</string>
|
||||
<string name="permission_required">Käyttöoikeus vaaditaan</string>
|
||||
<string name="storage_permission_to_download">Sovellus tarvitsee pääsyn tallennustilaan, jotta voit tallentaa tämän tiedoston.</string>
|
||||
<string name="open_settings">Avaa asetukset</string>
|
||||
<string name="error_saving_file">Virhe tallennettaessa tiedostoa</string>
|
||||
<string name="file_saved">Tiedosto tallennettu</string>
|
||||
<string name="downloading">Ladataan…</string>
|
||||
<string name="no_app_to_handle_action">Tätä toimintoa käsittelevää sovellusta ei ole</string>
|
||||
<string name="local_timeline">Paikallinen</string>
|
||||
<string name="trending_posts_info_banner">Nämä julkaisut ovat saamassa vetoa eri puolilla Mastodonia.</string>
|
||||
<string name="trending_links_info_banner">Näistä uutisista puhutaan Mastodonissa.</string>
|
||||
<!-- %s is the server domain -->
|
||||
<string name="local_timeline_info_banner">Nämä ovat viestit kaikilta palvelimesi (%s) käyttäjiltä.</string>
|
||||
<string name="recommended_accounts_info_banner">Muiden seuraamiesi perusteella saattaisit pitää näistä tileistä.</string>
|
||||
<string name="see_new_posts">Uusia julkaisuja</string>
|
||||
<string name="load_missing_posts">Lataa puuttuvat julkaisut</string>
|
||||
<string name="follow_back">Seuraa takaisin</string>
|
||||
<string name="button_follow_pending">Pyydetty</string>
|
||||
<string name="follows_you">Seuraa sinua</string>
|
||||
<string name="manually_approves_followers">Hyväksyy seuraajat käsin</string>
|
||||
<!-- translators: %,d is a valid placeholder, it formats the number with locale-dependent grouping separators -->
|
||||
<plurals name="x_followers">
|
||||
<item quantity="one">%d seuraaja</item>
|
||||
<item quantity="other">%d seuraajaa</item>
|
||||
</plurals>
|
||||
<plurals name="x_following">
|
||||
<item quantity="one">%d seurattu</item>
|
||||
<item quantity="other">%d seurattua</item>
|
||||
</plurals>
|
||||
<plurals name="x_favorites">
|
||||
<item quantity="one">%,d suosikki</item>
|
||||
<item quantity="other">%,d suosikkia</item>
|
||||
</plurals>
|
||||
<plurals name="x_reblogs">
|
||||
<item quantity="one">%,d tehostus</item>
|
||||
<item quantity="other">%,d tehostusta</item>
|
||||
</plurals>
|
||||
<string name="timestamp_via_app">%1$s sovelluksella %2$s</string>
|
||||
<string name="time_now">nyt</string>
|
||||
<string name="edit_history">Muokkaushistoria</string>
|
||||
<string name="last_edit_at_x">Muokattiin viimeksi %s</string>
|
||||
<string name="time_just_now">juuri nyt</string>
|
||||
<plurals name="x_seconds_ago">
|
||||
<item quantity="one">%d sekunti sitten</item>
|
||||
<item quantity="other">%d sekuntia sitten</item>
|
||||
</plurals>
|
||||
<plurals name="x_minutes_ago">
|
||||
<item quantity="one">%d minuutti sitten</item>
|
||||
<item quantity="other">%d minuuttia sitten</item>
|
||||
</plurals>
|
||||
<string name="edited_timestamp">muokattu %s</string>
|
||||
<string name="edit_original_post">Alkuperäinen viesti</string>
|
||||
<string name="edit_text_edited">Tekstiä muokattu</string>
|
||||
<string name="edit_spoiler_added">Sisältövaroitus</string>
|
||||
<string name="edit_spoiler_edited">Sisältövaroitus muokattu</string>
|
||||
<string name="edit_spoiler_removed">Sisältövaroitus poistettu</string>
|
||||
<string name="edit_poll_added">Kysely lisätty</string>
|
||||
<string name="edit_poll_edited">Kyselyä muokattu</string>
|
||||
<string name="edit_poll_removed">Kysely poistettu</string>
|
||||
<string name="edit_media_added">Mediatiedosto lisätty</string>
|
||||
<string name="edit_media_removed">Mediatiedosto poistettu</string>
|
||||
<string name="edit_media_reordered">Mediatiedostoja järjestetty</string>
|
||||
<string name="edit_marked_sensitive">Merkitty arkaluontoiseksi</string>
|
||||
<string name="edit_marked_not_sensitive">Merkitty ei arkaluontoiseksi</string>
|
||||
<string name="edit_multiple_changed">Julkaisu muokattu</string>
|
||||
<string name="edit">Muokkaa</string>
|
||||
<string name="discard_changes">Hylätäänkö muutokset?</string>
|
||||
<string name="upload_failed">Lataus epäonnistui</string>
|
||||
<string name="file_size_bytes">%d tavua</string>
|
||||
<string name="file_size_kb">%.2f KB</string>
|
||||
<string name="file_size_mb">%.2f MB</string>
|
||||
<string name="file_size_gb">%.2f GB</string>
|
||||
<string name="upload_processing">Käsitellään…</string>
|
||||
<!-- %s is version like 1.2.3 -->
|
||||
<!-- %s is version like 1.2.3 -->
|
||||
<!-- %s is file size -->
|
||||
<string name="download_update">Lataa (%s)</string>
|
||||
<string name="install_update">Asenna</string>
|
||||
<string name="privacy_policy_title">Yksityisyytesi</string>
|
||||
<string name="privacy_policy_subtitle">Vaikka Mastodon-sovellus ei kerää mitään tietoja, palvelimella, jonka olet rekisteröitynyt, voi olla eri käytäntö.\n\nJos olet eri mieltä käytännöstä %s, voit palata ja valita eri palvelin.</string>
|
||||
<string name="i_agree">Hyväksyn</string>
|
||||
<string name="empty_list">Luettelo on tyhjä</string>
|
||||
<string name="instance_signup_closed">Tämä palvelin ei hyväksy uusia rekisteröintejä.</string>
|
||||
<string name="text_copied">Kopioitu leikepöydälle</string>
|
||||
<string name="add_bookmark">Kirjanmerkki</string>
|
||||
<string name="remove_bookmark">Poista kirjanmerkki</string>
|
||||
<string name="bookmarks">Kirjanmerkit</string>
|
||||
<string name="your_favorites">Omat suosikit</string>
|
||||
<string name="login_title">Tervetuloa takaisin</string>
|
||||
<string name="login_subtitle">Kirjaudu sisään palvelimella, jossa olet luonut tilisi.</string>
|
||||
<string name="server_url">Palvelimen URL-osoite</string>
|
||||
<string name="signup_random_server_explain">Palvelin valitaan kielesi perusteella, jos jatkat ilman valintaa.</string>
|
||||
<string name="server_filter_any_language">Millä tahansa kielellä</string>
|
||||
<string name="server_filter_instant_signup">Välitön rekisteröityminen</string>
|
||||
<string name="server_filter_manual_review">Manuaalinen hyväksyntä</string>
|
||||
<string name="server_filter_any_signup_speed">Mikä tahansa rekisteröintinopeus</string>
|
||||
<string name="server_filter_region_europe">Eurooppa</string>
|
||||
<string name="server_filter_region_north_america">Pohjois-Amerikka</string>
|
||||
<string name="server_filter_region_south_america">Etelä-Amerikka</string>
|
||||
<string name="server_filter_region_africa">Afrikka</string>
|
||||
<string name="server_filter_region_asia">Aasia</string>
|
||||
<string name="server_filter_region_oceania">Oseania</string>
|
||||
<string name="not_accepting_new_members">Ei hyväksy uusia jäseniä</string>
|
||||
<string name="category_special_interests">Erityiset Kiinnostukset</string>
|
||||
<string name="signup_passwords_dont_match">Salasanat eivät täsmää</string>
|
||||
<string name="pick_server_for_me">Valitse minulle</string>
|
||||
<string name="profile_add_row">Lisää rivi</string>
|
||||
<string name="profile_setup">Profiilin asetukset</string>
|
||||
<string name="profile_setup_subtitle">Voit myös täyttää tämän myöhemmin Profiili-välilehdellä.</string>
|
||||
<string name="profile_setup_explanation">Voit lisätä enintään neljä profiilikenttää joissa on mitä haluat. Sijainti, linkit, pronominit — taivas on rajana.</string>
|
||||
<string name="popular_on_mastodon">Suosittua Mastodonissa</string>
|
||||
<string name="follow_all">Seuraa kaikkia</string>
|
||||
<string name="server_rules_disagree">Eri mieltä</string>
|
||||
<string name="privacy_policy_explanation">TL;DR: Emme kerää tai käsittele mitään.</string>
|
||||
<!-- %s is server domain -->
|
||||
<string name="server_policy_disagree">Eri mieltä tästä %s</string>
|
||||
<string name="profile_bio">Kuvaus</string>
|
||||
<!-- Shown in a progress dialog when you tap "follow all" -->
|
||||
<string name="sending_follows">Seurataan käyttäjiä…</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 ei salli ilmoittautumisia osoitteesta %2$s. Kokeile toista osoitetta tai <a>valitse toinen palvelin</a>.</string>
|
||||
<string name="spoiler_show">Näytä joka tapauksessa</string>
|
||||
<string name="spoiler_hide">Piilota uudelleen</string>
|
||||
<string name="poll_multiple_choice">Valitse yksi tai useampi</string>
|
||||
<string name="save_changes">Tallenna muutokset</string>
|
||||
<string name="profile_featured">Suositellut</string>
|
||||
<string name="profile_timeline">Aikajana</string>
|
||||
<string name="view_all">Näytä kaikki</string>
|
||||
<string name="profile_endorsed_accounts">Tilit</string>
|
||||
<string name="verified_link">Vahvistettu linkki</string>
|
||||
<string name="show">Näytä</string>
|
||||
<string name="hide">Piilota</string>
|
||||
<string name="join_default_server">Liity palvelimelle %s</string>
|
||||
<string name="pick_server">Valitse toinen palvelin</string>
|
||||
<string name="signup_or_login">tai</string>
|
||||
<string name="learn_more">Lue lisää</string>
|
||||
<string name="welcome_to_mastodon">Tervetuloa Mastodoniin</string>
|
||||
<string name="welcome_paragraph1">Mastodon on hajautettu sosiaalinen verkosto, joka tarkoittaa sitä, ettei sitä hallitse mikään yksittäinen yritys. Se koostuu monista itsenäisesti ylläpidetyistä palvelimista, jotka on liitetty yhteen.</string>
|
||||
<string name="what_are_servers">Mitä palvelimet ovat?</string>
|
||||
<string name="welcome_paragraph2">Jokainen Mastodon tili isännöi palvelimella - kullakin on omat arvot, säännöt, & ylläpitäjät. Riippumatta siitä, minkä valitset, voit seurata ja olla vuorovaikutuksessa ihmisten kanssa millä tahansa palvelimella.</string>
|
||||
<string name="opening_link">Avataan linkki…</string>
|
||||
<string name="link_not_supported">Tämä linkki ei ole tuettu sovelluksessa</string>
|
||||
<string name="log_out_all_accounts">Kirjaudu ulos kaikista tileistä</string>
|
||||
<string name="confirm_log_out_all_accounts">Kirjaudu ulos kaikista tileistä?</string>
|
||||
<string name="retry">Yritä uudelleen</string>
|
||||
<string name="post_failed">Viestin lähettäminen epäonnistui</string>
|
||||
<!-- %s is formatted file size ("467 KB image") -->
|
||||
<string name="attachment_description_image">%s kuva</string>
|
||||
<string name="attachment_description_video">%s video</string>
|
||||
<string name="attachment_description_audio">%s ääni</string>
|
||||
<string name="attachment_description_unknown">%s tiedosto</string>
|
||||
<string name="attachment_type_image">Kuva</string>
|
||||
<string name="attachment_type_video">Video</string>
|
||||
<string name="attachment_type_audio">Ääni</string>
|
||||
<string name="attachment_type_gif">GIF</string>
|
||||
<string name="attachment_type_unknown">Tiedosto</string>
|
||||
<string name="attachment_x_percent_uploaded">%d%% ladattu</string>
|
||||
<string name="add_poll_option">Lisää kyselyyn vaihtoehto</string>
|
||||
<string name="poll_length">Kyselyn kesto</string>
|
||||
<string name="poll_style">Tyyli</string>
|
||||
<string name="compose_poll_single_choice">Valitse yksi</string>
|
||||
<string name="compose_poll_multiple_choice">Monivalinta</string>
|
||||
<string name="delete_poll_option">Poista kyselyn vaihtoehto</string>
|
||||
<string name="poll_style_title">Kyselyn tyyli</string>
|
||||
<string name="alt_text">Selitys</string>
|
||||
<string name="help">Ohje</string>
|
||||
<string name="what_is_alt_text">Mikä on selitys?</string>
|
||||
<string name="alt_text_help">Kuvaselitys auttaa ihmisiä, joilla on näkövamma, hidas yhteys, tai jotka tarvitsevat lisäkontekstia. \n\nVoit parantaa saavutettavuutta ja kaikkien mahdollisuutta ymmärtää kirjoittamalla selkeän, lyhyen ja objektiivisen selityksen. \n\n<ul><li>Mainitse tärkeät elementit</li>\n<li>Anna tiivistelmä kuvissa olevista teksteistä</li>\n<li>Käytä normaalia lauserakennetta</li>\n<li>Vältä turhaa toistoa</li>\n<li>Keskity monimutkaisissa kuvioissa (kuten kartoissa ja taulukoissa) trendeihin ja tärkeimpiin tietoihin</li></ul></string>
|
||||
<string name="edit_post">Muokkaa julkaisua</string>
|
||||
<string name="no_verified_link">Ei todennettua linkkiä</string>
|
||||
<string name="compose_autocomplete_emoji_empty">Selaa emojeita</string>
|
||||
<string name="compose_autocomplete_users_empty">Löydä etsimäsi henkilöt</string>
|
||||
<string name="no_search_results">Näille hakusanoille ei löytynyt mitään</string>
|
||||
<string name="language">Kieli</string>
|
||||
<string name="language_default">Oletus</string>
|
||||
<string name="language_system">Järjestelmä</string>
|
||||
<string name="language_detecting">Tunnista kieli</string>
|
||||
<string name="language_cant_detect">Ei voi tunnistaa kieltä</string>
|
||||
<string name="language_detected">Tunnistettu</string>
|
||||
<string name="media_hidden">Media piilotettu</string>
|
||||
<string name="post_hidden">Julkaisu piilotettu</string>
|
||||
<string name="report_title_post">Raportoi julkaisu</string>
|
||||
<string name="forward_report_explanation">Tämä tili on toisella palvelimella. Haluatko lähettää nimettömän raportin myös sinne?</string>
|
||||
<!-- %s is the server domain -->
|
||||
<string name="forward_report_to_server">Välitä kohteeseen %s</string>
|
||||
<!-- Shown on the "stamp" on the screen that appears after you report a post/user. Please keep the translation short, preferably a single word -->
|
||||
<string name="reported">Raportoitu</string>
|
||||
<string name="report_unfollow_explanation">Jos et enää halua nähdä tämän käyttäjän julkaisuja kotiaikajanallasi, lopeta seuraaminen.</string>
|
||||
<string name="muted_user">Käyttäjä %s mykistetty</string>
|
||||
<string name="report_sent_already_blocked">Olet jo estänyt tämän käyttäjän, sinun ei tarvitse tehdä muuta sillä aikaa kun tarkastamme raporttisi.</string>
|
||||
<string name="report_personal_already_blocked">Olet jo estänyt tämän käyttäjän, joten sinun ei tarvitse tehdä mitään muuta.\n\nKiitos, että autat pitämään Mastodonin turvallisena paikkana kaikille!</string>
|
||||
<string name="blocked_user">Estetty %s</string>
|
||||
<string name="mark_all_notifications_read">Merkitse kaikki luetuksi</string>
|
||||
<string name="settings_display">Näyttö</string>
|
||||
<string name="settings_filters">Suodattimet</string>
|
||||
<string name="settings_server_explanation">Yleiskatsaus, säännöt, valvojat</string>
|
||||
<!-- %s is the app name (Mastodon, key app_name). I made it a placeholder so everything Just Works™ with forks -->
|
||||
<string name="about_app">Tietoa: %s</string>
|
||||
<string name="default_post_language">Julkaisun oletuskieli</string>
|
||||
<string name="settings_alt_text_reminders">Lisää muistutus kuvaselityksestä</string>
|
||||
<string name="settings_confirm_unfollow">Kysy ennen kuin käyttäjän seuraaminen lopetetaan</string>
|
||||
<string name="settings_confirm_boost">Kysy ennen tehostusta</string>
|
||||
<string name="settings_confirm_delete_post">Kysy ennen julkaisujen poistamista</string>
|
||||
<string name="pause_all_notifications">Keskeytä kaikki</string>
|
||||
<string name="pause_notifications_off">Pois Päältä</string>
|
||||
<string name="notifications_policy_anyone">Kuka tahansa</string>
|
||||
<string name="notifications_policy_followed">Seuraajasi</string>
|
||||
<string name="notifications_policy_follower">Seuraamasi henkilöt</string>
|
||||
<string name="notifications_policy_no_one">Ei kukaan</string>
|
||||
<string name="settings_notifications_policy">Ota vastaan ilmoituksia käyttäjältä</string>
|
||||
<string name="notification_type_mentions_and_replies">Maininnat ja vastaukset</string>
|
||||
<string name="pause_all_notifications_title">Keskeytä kaikki ilmoitukset</string>
|
||||
<plurals name="x_weeks">
|
||||
<item quantity="one">%d viikko</item>
|
||||
<item quantity="other">%d viikkoa</item>
|
||||
</plurals>
|
||||
<!-- %1$s is the date (may be relative, e.g. "today" or "yesterday"), %2$s is the time. You can reorder these placeholders if that works better for your language -->
|
||||
<string name="date_at_time">%1$s klo %2$s</string>
|
||||
<string name="today">tänään</string>
|
||||
<string name="yesterday">eilen</string>
|
||||
<string name="tomorrow">huomenna</string>
|
||||
<!-- %s is the timestamp ("tomorrow at 12:34") -->
|
||||
<string name="pause_notifications_ends">Päättyy %s</string>
|
||||
<!-- %s is the timestamp ("tomorrow at 12:34") -->
|
||||
<string name="pause_notifications_banner">Ilmoitukset jatkuvat %s.</string>
|
||||
<string name="resume_notifications_now">Jatka nyt</string>
|
||||
<string name="open_system_notification_settings">Siirry ilmoitusasetuksiin</string>
|
||||
<string name="about_server">Tietoja</string>
|
||||
<string name="server_rules">Säännöt</string>
|
||||
<string name="server_administrator">Ylläpitäjä</string>
|
||||
<string name="send_email_to_server_admin">Viestin ylläpitäjä</string>
|
||||
<string name="notifications_disabled_in_system">Ota ilmoitukset käyttöön laitteesi asetuksista nähdäksesi päivityksiä mistä tahansa.</string>
|
||||
<string name="settings_even_more">Vielä enemmän asetuksia</string>
|
||||
<string name="settings_show_cws">Näytä sisältövaroitukset</string>
|
||||
<string name="settings_hide_sensitive_media">Piilota arkaluontoiseksi merkitty media</string>
|
||||
<string name="settings_show_interaction_counts">Näytä reaktiolaskurit</string>
|
||||
<string name="settings_show_emoji_in_names">Mukautetut emojit näyttönimissä</string>
|
||||
<plurals name="in_x_seconds">
|
||||
<item quantity="one">%d sekunnin kuluttua</item>
|
||||
<item quantity="other">%d sekunnin kuluttua</item>
|
||||
</plurals>
|
||||
<plurals name="in_x_minutes">
|
||||
<item quantity="one">%d minuutin kuluttua</item>
|
||||
<item quantity="other">%d minuutin kuluttua</item>
|
||||
</plurals>
|
||||
<plurals name="in_x_hours">
|
||||
<item quantity="one">%d tunnin kuluttua</item>
|
||||
<item quantity="other">%d tunnin kuluttua</item>
|
||||
</plurals>
|
||||
<plurals name="x_hours_ago">
|
||||
<item quantity="one">%d tunti sitten</item>
|
||||
<item quantity="other">%d tuntia sitten</item>
|
||||
</plurals>
|
||||
<string name="alt_text_reminder_title">Mediasta puuttuu selitysteksti</string>
|
||||
<plurals name="alt_text_reminder_x_images">
|
||||
<item quantity="one">%s kuvastasi puuttuu selitysteksti. Julkaistaanko silti?</item>
|
||||
<item quantity="other">%s kuvastasi puuttuu selitysteksti. Julkaistaanko silti?</item>
|
||||
</plurals>
|
||||
<plurals name="alt_text_reminder_x_attachments">
|
||||
<item quantity="one">%s mediatiedostostasi puuttuu selitysteksti. Julkaistaanko silti?</item>
|
||||
<item quantity="other">%s mediatiedostostasi puuttuu selitysteksti. Julkaistaanko silti?</item>
|
||||
</plurals>
|
||||
<string name="count_one">Yksi</string>
|
||||
<string name="count_two">Kaksi</string>
|
||||
<string name="count_three">Kolme</string>
|
||||
<string name="count_four">Neljä</string>
|
||||
<string name="alt_text_reminder_post_anyway">Julkaise</string>
|
||||
<!-- %s is the username -->
|
||||
<string name="unfollow_confirmation">Lopeta käyttäjän %s seuraaminen?</string>
|
||||
<string name="filter_active">Aktiivinen</string>
|
||||
<string name="filter_inactive">Ei käytössä</string>
|
||||
<string name="settings_add_filter">Lisää suodatin</string>
|
||||
<string name="settings_edit_filter">Muokkaa suodatinta</string>
|
||||
<string name="settings_filter_duration">Kesto</string>
|
||||
<string name="settings_filter_muted_words">Mykistetyt sanat</string>
|
||||
<string name="settings_filter_context">Mykistä alkaen</string>
|
||||
<string name="settings_filter_show_cw">Näytä sisältövaroituksella</string>
|
||||
<string name="settings_filter_show_cw_explanation">Näytä vielä viestejä, jotka täsmäävät tähän suodattimeen, mutta sisällönvaroituksen takana</string>
|
||||
<string name="settings_delete_filter">Poista suodatin</string>
|
||||
<string name="filter_duration_forever">Ikuisesti</string>
|
||||
<!-- %s is the timestamp ("tomorrow at 12:34") -->
|
||||
<string name="settings_filter_ends">Päättyy %s</string>
|
||||
<plurals name="settings_x_muted_words">
|
||||
<item quantity="one">%d mykistetty sana tai lause</item>
|
||||
<item quantity="other">%d mykistettyä sanaa tai lauseita</item>
|
||||
</plurals>
|
||||
<string name="selection_2_options">%1$s ja %2$s</string>
|
||||
<string name="selection_3_options">%1$s, %2$s ja %3$s</string>
|
||||
<string name="selection_4_or_more">%1$s, %2$s, ja %3$d lisää</string>
|
||||
<string name="filter_context_home_lists">Koti & listat</string>
|
||||
<string name="filter_context_notifications">Ilmoitukset</string>
|
||||
<string name="filter_context_public_timelines">Julkiset aikajanat</string>
|
||||
<string name="filter_context_threads_replies">Langat & vastaukset</string>
|
||||
<string name="filter_context_profiles">Profiilit</string>
|
||||
<string name="settings_filter_title">Otsikko</string>
|
||||
<string name="settings_delete_filter_title">Poistetaanko suodatin “%s”?</string>
|
||||
<string name="settings_delete_filter_confirmation">Tämä suodatin poistetaan tililtäsi kaikissa laitteissa.</string>
|
||||
<string name="add_muted_word">Lisää mykistetty sana</string>
|
||||
<string name="edit_muted_word">Muokkaa mykistettyä sanaa</string>
|
||||
<string name="add">Lisää</string>
|
||||
<string name="filter_word_or_phrase">Sana tai lause</string>
|
||||
<string name="filter_add_word_help">Sanat ovat tapauskohtaisia ja vastaavat vain kokonaisia sanoja.\n\nJos suodattaa avainsana “Apple”, se piilottaa viestit sisältävät “omena” tai “aPPLe” mutta ei “ananas\".</string>
|
||||
<string name="settings_delete_filter_word">Poista sana “%s”?</string>
|
||||
<string name="enter_selection_mode">Valitse</string>
|
||||
<string name="select_all">Valitse kaikki</string>
|
||||
<string name="settings_filter_duration_title">Suodattimen kesto</string>
|
||||
<string name="filter_duration_custom">Mukautettu</string>
|
||||
<plurals name="settings_delete_x_filter_words">
|
||||
<item quantity="one">Poista %d sana?</item>
|
||||
<item quantity="other">Poista %d sanaa?</item>
|
||||
</plurals>
|
||||
<plurals name="x_items_selected">
|
||||
<item quantity="one">%d valittu</item>
|
||||
<item quantity="other">%d valittu</item>
|
||||
</plurals>
|
||||
<string name="required_form_field_blank">Tätä ei voi jättää tyhjäksi</string>
|
||||
<string name="filter_word_already_in_list">Jo luettelossa</string>
|
||||
<string name="app_update_ready">Sovelluksen päivitys valmis</string>
|
||||
<string name="app_update_version">Versio %s</string>
|
||||
<string name="downloading_update">Ladataan (%d%%)</string>
|
||||
<!-- Shown like a content warning, %s is the name of the filter -->
|
||||
<string name="post_matches_filter_x">Sopii suodattimeen ”%s”</string>
|
||||
<string name="search_mastodon">Etsi Mastodonista</string>
|
||||
<string name="clear_all">Tyhjennä kaikki</string>
|
||||
<string name="search_open_url">Avaa URL-osoite Mastodonissa</string>
|
||||
<string name="posts_matching_hashtag">Julkaisut joissa on \"%s\"</string>
|
||||
<string name="search_go_to_account">Siirry tiliin %s</string>
|
||||
<string name="posts_matching_string">Julkaisut joissa on \"%s\"</string>
|
||||
<string name="accounts_matching_string">Henkilöt jossa on \"%s\"</string>
|
||||
<!-- Shown in the post header. Please keep it short -->
|
||||
<string name="time_seconds_ago_short">%ds sitten</string>
|
||||
<string name="time_minutes_ago_short">%dm sitten</string>
|
||||
<string name="time_hours_ago_short">%dh sitten</string>
|
||||
<string name="time_days_ago_short">%dd sitten</string>
|
||||
<!-- %s is the name of the post language -->
|
||||
<string name="translate_post">Käännetty kielestä %s</string>
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
<string name="post_translated">Käännetty kielestä %1$s käyttäen %2$s</string>
|
||||
<string name="translation_show_original">Näytä alkuperäinen</string>
|
||||
<string name="translation_failed">Käännös epäonnistui. Ehkä järjestelmänvalvoja ei ole ottanut käyttöön käännöksiä tällä palvelimella tai tällä palvelimella on käynnissä vanhempi versio Mastodonista, jossa käännöksiä ei vielä tueta.</string>
|
||||
<plurals name="x_participants">
|
||||
<item quantity="one">%d osallistuja</item>
|
||||
<item quantity="other">%d osallistujaa</item>
|
||||
</plurals>
|
||||
<plurals name="x_posts_today">
|
||||
<item quantity="one">%,d viesti tänään</item>
|
||||
<item quantity="other">%,d viestiä tänään</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -368,7 +368,7 @@
|
||||
<string name="sk_icon_clapper_board">Klaffitaulu</string>
|
||||
<string name="sk_icon_pizza">Pitsa</string>
|
||||
<string name="sk_icon_recycle_bin">Roskakori</string>
|
||||
<string name="sk_edit_timelines">Muokkaa aikananoja</string>
|
||||
<string name="sk_edit_timelines">Muokkaa aikajanoja</string>
|
||||
<string name="sk_edit_timeline_tags_explanation">Ota huomioon, että palvelin tekee nämä operaatiot. Niiden yhdistelyä ei välttämättä tueta.</string>
|
||||
<string name="sk_notify_update">Tämä muokkaa tehostettua viestiä</string>
|
||||
<string name="sk_save_draft_message">Haluatko tallentaa muutokset tähän luonnokseen vai julkaista sen nyt\?</string>
|
||||
|
||||
@@ -211,12 +211,12 @@
|
||||
<item quantity="other">%,d tagasunod</item>
|
||||
</plurals>
|
||||
<plurals name="x_following">
|
||||
<item quantity="one">%, d Sumusunod</item>
|
||||
<item quantity="other">%, d Sumusunod</item>
|
||||
<item quantity="one">%,d Sumusunod</item>
|
||||
<item quantity="other">%,d Sumusunod</item>
|
||||
</plurals>
|
||||
<plurals name="x_favorites">
|
||||
<item quantity="one">%, d Paborito</item>
|
||||
<item quantity="other">%, d paborito</item>
|
||||
<item quantity="one">%,d Paborito</item>
|
||||
<item quantity="other">%,d paborito</item>
|
||||
</plurals>
|
||||
<string name="timestamp_via_app">%1$s sa pamamagitan ng %2$s</string>
|
||||
<string name="time_now">ngayon</string>
|
||||
@@ -284,4 +284,6 @@
|
||||
<!-- %s is the timestamp ("tomorrow at 12:34") -->
|
||||
<!-- Shown like a content warning, %s is the name of the filter -->
|
||||
<!-- Shown in the post header. Please keep it short -->
|
||||
<!-- %s is the name of the post language -->
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
</resources>
|
||||
|
||||
@@ -582,4 +582,18 @@
|
||||
<string name="time_minutes_ago_short">il y a %dm</string>
|
||||
<string name="time_hours_ago_short">Il y a %dh</string>
|
||||
<string name="time_days_ago_short">Il y a %dj</string>
|
||||
<!-- %s is the name of the post language -->
|
||||
<string name="translate_post">Traduire depuis %s</string>
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
<string name="post_translated">Traduit depuis %1$s via %2$s</string>
|
||||
<string name="translation_show_original">Afficher l’original</string>
|
||||
<string name="translation_failed">La traduction a échoué. Peut-être que l’administrateur n’a pas activé les traductions sur ce serveur ou que ce serveur utilise une ancienne version de Mastodon où les traductions ne sont pas encore prises en charge.</string>
|
||||
<plurals name="x_participants">
|
||||
<item quantity="one">%,d participant</item>
|
||||
<item quantity="other">%,d participants</item>
|
||||
</plurals>
|
||||
<plurals name="x_posts_today">
|
||||
<item quantity="one">%,d message aujourd’hui</item>
|
||||
<item quantity="other">%,d messages aujourd’hui</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -362,7 +362,7 @@
|
||||
<string name="sk_settings_display_pronouns_in_timelines">Afficher les pronoms dans les timelines</string>
|
||||
<string name="sk_settings_show_labels_in_navigation_bar">Afficher les étiquettes des onglets dans la barre de navigation</string>
|
||||
<string name="sk_settings_emoji_reactions">Activer les réactions emoji</string>
|
||||
<string name="sk_settings_emoji_reactions_explanation">Affiche les réactions emoji aux messages et vous permet d\'interagir avec elles. Certaines versions modifiées de Mastodon le supportent, mais pas Mastodon.</string>
|
||||
<string name="sk_settings_emoji_reactions_explanation">Affiche les réactions emoji aux messages et vous permet d\'ajouter les vôtres. Divers serveurs du Fediverse le prennent en charge, mais pas Mastodon.</string>
|
||||
<string name="sk_settings_emoji_reactions_in_lists">Afficher les réactions emoji dans les timelines</string>
|
||||
<string name="sk_settings_emoji_reactions_in_lists_explanation">Si les réactions emoji doivent être affichées sur les timelines. Si cette option est désactivée, les réactions emoji ne seront affichées que lors de l\'affichage d\'un fil de discussion.</string>
|
||||
<plurals name="sk_users_reacted_with">
|
||||
@@ -373,7 +373,7 @@
|
||||
<string name="sk_button_react">Réagissez avec des emojis</string>
|
||||
<string name="sk_again_for_system_keyboard">Appuyez à nouveau pour le clavier système</string>
|
||||
<string name="sk_enter_emoji_hint">Tapez pour réagir avec un emoji</string>
|
||||
<string name="sk_enter_emoji_toast">Vous devez saisir un emoji</string>
|
||||
<string name="sk_enter_emoji_toast">Veuillez saisir un emoji</string>
|
||||
<plurals name="sk_posts_count_label">
|
||||
<item quantity="one">message</item>
|
||||
<item quantity="many">messages</item>
|
||||
@@ -393,4 +393,12 @@
|
||||
<string name="sk_suicide_search_terms">Suicide</string>
|
||||
<string name="sk_search_suicide_title">Dans le cas où vous seriez en détresse…</string>
|
||||
<string name="sk_search_suicide_message">Si vous cherchez un signe pour ne pas vous suicider, le voici. Si vous êtes en détresse et/ou avez des pensées suicidaires, si vous voulez aider une personne en souffrance, vous pouvez contacter une ligne d\'assistance de prévention du suicide.</string>
|
||||
<string name="sk_trending_links_info_banner">Ce sont les actualités dont on parle sur votre serveur.</string>
|
||||
<string name="sk_load_missing_posts_above">Charger les messages les plus récents</string>
|
||||
<string name="sk_load_missing_posts_below">Charger les anciens messages</string>
|
||||
<string name="sk_time_seconds">%d secondes</string>
|
||||
<string name="sk_time_minutes">%d minutes</string>
|
||||
<string name="sk_time_hours">%d heures</string>
|
||||
<string name="sk_time_days">%d jours</string>
|
||||
<string name="sk_trending_posts_info_banner">Ce sont les publications qui gagnent du terrain sur votre serveur.</string>
|
||||
</resources>
|
||||
@@ -20,4 +20,6 @@
|
||||
<!-- %s is the timestamp ("tomorrow at 12:34") -->
|
||||
<!-- Shown like a content warning, %s is the name of the filter -->
|
||||
<!-- Shown in the post header. Please keep it short -->
|
||||
<!-- %s is the name of the post language -->
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
</resources>
|
||||
|
||||
@@ -640,4 +640,6 @@
|
||||
<string name="time_minutes_ago_short">%dm air ais</string>
|
||||
<string name="time_hours_ago_short">%du air ais</string>
|
||||
<string name="time_days_ago_short">%dl air ais</string>
|
||||
<!-- %s is the name of the post language -->
|
||||
<!-- %1$s is the language, %2$s is the name of the translation service -->
|
||||
</resources>
|
||||
|
||||
@@ -311,4 +311,88 @@
|
||||
<string name="sk_edit_timeline_tag_none">… ach gun gin sam bith dhen fheadhainn seo</string>
|
||||
<string name="sk_edit_timeline_tag_hint">Cuir a-steach taga hais…</string>
|
||||
<string name="sk_edit_timeline_tags_hint">Cuir a-steach tagaichean hais…</string>
|
||||
<string name="sk_settings_allow_remote_loading_explanation">Feuch liostaichean nas pongaile air luchd-leantainn, annsachdan is brosnachaidhean fhaighinn le luchdadh an fhiosrachaidh on ionstans tùsail.</string>
|
||||
<string name="sk_disable_pill_shaped_active_indicator">Cuir à comas taisbeanair an taba ghnìomhaich air a bheil cruth pile</string>
|
||||
<string name="sk_tab_profile">Pròifil</string>
|
||||
<string name="sk_time_seconds">%d diog(an)</string>
|
||||
<string name="sk_mute_label">Faide</string>
|
||||
<string name="sk_settings_show_emoji_reactions_only_opened">Nuair a bhios am post fosgailte a-mhàin</string>
|
||||
<string name="sk_time_hours">%d uair(ean) a thìde</string>
|
||||
<string name="sk_settings_allow_remote_loading">Luchdaich am fiosrachadh o ionstansan cèine</string>
|
||||
<string name="sk_settings_auto_reveal_anyone">Freagairtean le duine sam bith</string>
|
||||
<string name="sk_settings_content_types_explanation">Leigidh seo leat seòrsa susbainte ma Markdown a shuidheachadh nuair a chruthaicheas tu post. Thoir an aire nach cuir gach ionstans taic ri seo.</string>
|
||||
<string name="sk_tab_home">Dachaigh</string>
|
||||
<string name="sk_settings_show_emoji_reactions_hide_empty">Falaich freagairtean Emoji falamh</string>
|
||||
<string name="sk_settings_show_emoji_reactions">Seall freagairtean Emoji air loidhnichean-ama</string>
|
||||
<plurals name="sk_posts_count_label">
|
||||
<item quantity="one">phost</item>
|
||||
<item quantity="two">phost</item>
|
||||
<item quantity="few">postaichean</item>
|
||||
<item quantity="other">post</item>
|
||||
</plurals>
|
||||
<string name="sk_notification_mention">Thug %s iomradh ort</string>
|
||||
<string name="sk_open_in_app">Fosgail san aplacaid</string>
|
||||
<string name="sk_settings_emoji_reactions">Cuir an comas freagairtean Emoji</string>
|
||||
<string name="sk_suicide_search_terms">Fèin-mhurt</string>
|
||||
<string name="sk_duration_minutes_5">5 mionaidean</string>
|
||||
<string name="sk_advanced_options_show">Seall na roghainnean adhartach</string>
|
||||
<string name="sk_spoiler_show">Seall an t-susbaint</string>
|
||||
<string name="sk_settings_prefix_replies_never">Chan ann idir</string>
|
||||
<string name="sk_instance_info_unavailable">Chan eil fiosrachadh an ionstans ri fhaighinn an-dràsta fhèin</string>
|
||||
<string name="sk_settings_default_content_type_explanation">Bheir seo ro-thaghadh air seòrsa na susbainte dhut nuair a chruthaicheas tu post ùr, a’ tar-àithneadh an luach a chaidh a shuidheachadh sna “Roghainnean postaidh”.</string>
|
||||
<string name="sk_settings_auto_reveal_nobody">Chan ann idir</string>
|
||||
<string name="sk_settings_instance">Ionstans</string>
|
||||
<string name="sk_load_missing_posts_above">Luchdaich na postaichean nas ùire</string>
|
||||
<string name="sk_settings_display_pronouns_in_timelines">Seall na riochdairean air loidhnichean-ama</string>
|
||||
<string name="sk_switch_timeline">Geàrr leum gu loidhne-ama eile</string>
|
||||
<string name="sk_time_days">%d là(ithean)</string>
|
||||
<string name="sk_settings_show_labels_in_navigation_bar">Seall leubailean thabaichean air bàr na seòladaireachd</string>
|
||||
<string name="sk_external_share_or_open_title">Co-roinn no fosgail le cunntas</string>
|
||||
<string name="sk_tab_search">Lorg</string>
|
||||
<string name="sk_settings_auto_reveal_equal_spoilers">Nochd susbaint ron aon rabhadh susbainte ann am freagairtean gu fèin-obrachail</string>
|
||||
<string name="sk_settings_show_emoji_reactions_always">Seall am putan “Cuir ris” an-còmhnaidh</string>
|
||||
<string name="sk_settings_forward_report_default">Bun-roghainn dha “Sìn air adhart an gearan”</string>
|
||||
<string name="sk_duration_indefinite">Gun chrìoch</string>
|
||||
<string name="sk_search_suicide_hotlines">Lorg loidhne taice</string>
|
||||
<string name="sk_advanced_options_hide">Falaich na roghainnean adhartach</string>
|
||||
<string name="sk_duration_hours_1">Uair a thìde</string>
|
||||
<string name="sk_duration_hours_6">6 uairean a thìde</string>
|
||||
<string name="sk_enter_emoji_hint">Sgrìobh rudeigin airson freagairt le Emoji</string>
|
||||
<string name="sk_duration_days_7">Seachdain</string>
|
||||
<string name="sk_settings_true_black">Modh dubh dorcha</string>
|
||||
<string name="sk_no_remote_info_hint">chan eil am fiosrachadh cèin ri fhaighinn</string>
|
||||
<string name="sk_settings_auto_reveal_author">Freagairtean leis an aon ùghdar</string>
|
||||
<string name="sk_do_not_show_again">Na seall seo a-rithist</string>
|
||||
<string name="sk_suicide_helplines_url">https://findahelpline.com</string>
|
||||
<string name="sk_settings_display_pronouns_in_threads">Seall na riochdairean ann an snàithleanan</string>
|
||||
<string name="sk_error_loading_profile">Dh’fhàillig le luchdadh na pròifil slighe %s</string>
|
||||
<string name="sk_enter_emoji_toast">Cuir a-steach Emoji</string>
|
||||
<string name="sk_trending_posts_info_banner">Seo na postaichean fèillmhor air an fhrithealaiche agad.</string>
|
||||
<string name="sk_duration_minutes_30">Leth-uair a thìde</string>
|
||||
<string name="sk_settings_display_pronouns_in_user_listings">Seall na riochdairean air liostaichean luchd-cleachdadh</string>
|
||||
<string name="sk_button_react">Freagair le emoji</string>
|
||||
<string name="sk_duration_days_1">Latha</string>
|
||||
<string name="sk_load_missing_posts_below">Luchdaich na postaichean nas sine</string>
|
||||
<string name="sk_settings_emoji_reactions_explanation">Seallaidh seo freagairtean Emoji do phostaichean is leigidh e leat an fheadhainn agad fhèin a chur ris. Cuiridh grunn fhrithealaichean a’ cho-shaoghail taic ri seo ach cha chuir Mastodon.</string>
|
||||
<string name="sk_external_share_title">Co-roinn le cunntas</string>
|
||||
<string name="sk_list_exclusive_switch">Dèan an liosta às-dùnach</string>
|
||||
<string name="sk_duration_days_3">3 làithean</string>
|
||||
<string name="sk_settings_default_content_type">Seòrsa bunaiteach na susbainte</string>
|
||||
<string name="sk_search_suicide_title">Ma bhios tu iomagain ort…</string>
|
||||
<string name="sk_open_in_app_failed">Cha b’ urrainn dhuinn fhosgladh san aplacaid</string>
|
||||
<string name="sk_time_minutes">%d mionaid(ean)</string>
|
||||
<string name="sk_exclusive_list">Liosta às-dùnach</string>
|
||||
<plurals name="sk_users_reacted_with">
|
||||
<item quantity="one">Fhreagair %1$,d neach-cleachdaidh le %2$s</item>
|
||||
<item quantity="two">Fhreagair %1$,d luchd-cleachdaidh le %2$s</item>
|
||||
<item quantity="few">Fhreagair %1$,d luchd-cleachdaidh le %2$s</item>
|
||||
<item quantity="other">Fhreagair %1$,d luchd-cleachdaidh le %2$s</item>
|
||||
</plurals>
|
||||
<string name="sk_search_suicide_message">Ma tha thu a’ sireadh samhla nach cuir thu às dhiot fhèin, seo e. Saoil am bruidhinn thu ri loidhne theth ionadail mu fhèin-mhurt ma tha iomagain ort\?</string>
|
||||
<string name="sk_trending_links_info_banner">Seo na sgeulachdan naidheachd air a bheilear a’ bruidhinn air an fhrithealaiche agad.</string>
|
||||
<string name="sk_search_fediverse">Lorg air a’ cho-shaoghal</string>
|
||||
<string name="sk_post_contains_media">Tha meadhan sa phost</string>
|
||||
<string name="sk_tab_notifications">Brathan</string>
|
||||
<string name="sk_list_exclusive_switch_explanation">Cha nochd buill liosta às-dùnaich air loidhne-ama na dachaighe agad – ma chuireas an t-ionstans agad taic ri seo.</string>
|
||||
<string name="sk_pronouns_label">Riochdairean</string>
|
||||
</resources>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user