Compare commits

..

68 Commits

Author SHA1 Message Date
sk
1b1dc7085e update readme 2022-11-09 18:42:49 +01:00
sk
94d004724f Merge branch 'feature/post-notifications-toggle' into fork 2022-11-09 18:41:14 +01:00
sk
e9f5d235cb Implement post notifications button
Closes #10, mastodon#81
2022-11-09 18:39:28 +01:00
sk
9692afb323 bump version 2022-11-09 18:38:58 +01:00
sk
3ae37a700b Merge branch 'feature/post-notifications-toggle' into fork 2022-11-09 18:38:20 +01:00
sk
b166ca705e Implement post notifications button 2022-11-09 18:38:10 +01:00
sk
e6a67543f4 Bump version, update README 2022-11-09 15:54:33 +01:00
sk
7d38f031f1 Merge branch 'feature/follow_hashtags' into fork
Fixes #31, mastodon#233
2022-11-09 15:49:29 +01:00
sk
cf864e6f49 Implement following hashtags 2022-11-09 15:48:01 +01:00
sk
f4b1629a1d Merge branch 'feature/filter-home-timeline' into fork 2022-11-09 10:34:47 +01:00
sk
1fbb97021e align code 2022-11-09 10:34:36 +01:00
sk
229c815a59 move option to home timeline section 2022-11-09 10:32:12 +01:00
sk
ee8d78637d Merge branch 'settings/load-new-posts' into fork 2022-11-09 10:31:30 +01:00
sk
4ede842171 add option to disable automatically loading new posts 2022-11-09 10:30:25 +01:00
sk
8306e78ce3 Merge branch 'feature/filter-home-timeline' into fork 2022-11-09 10:12:16 +01:00
sk
65d093ee9d fix new posts not filtering 2022-11-09 10:11:55 +01:00
sk
aa48233a75 Merge branch 'main' into fork 2022-11-09 09:48:03 +01:00
Grishka
ae50e618c0 Fix #316 2022-11-08 09:41:07 +03:00
Grishka
defa8b014e Fix #315 2022-11-08 09:38:26 +03:00
Grishka
159d91f390 Crash fixes 2022-11-08 09:36:56 +03:00
sk
430d4ec93b Merge branch 'spoiler-height-independent' into fork 2022-11-08 00:55:10 +01:00
sk
ae64784daf revert increased vertical padding
i changed my mind
2022-11-08 00:54:48 +01:00
sk
18df7c32a4 Merge branch 'feature/filter-home-timeline' into fork 2022-11-08 00:27:31 +01:00
sk
4615612a65 use home_timeline string 2022-11-08 00:27:19 +01:00
sk
6b226cdcad bump version 2022-11-08 00:21:33 +01:00
sk
1776709b38 update readme 2022-11-08 00:21:17 +01:00
sk
75a35cd680 Merge branch 'feature/filter-home-timeline' into fork 2022-11-08 00:18:19 +01:00
sk
18d4f2fa36 add timeline filters for replies/boosts
fixes #32, mastodon#210
2022-11-08 00:13:06 +01:00
sk
bca936f6a2 move contribute to the spicy zone
i've been planning to do this for a while :D
2022-11-07 23:37:10 +01:00
sk
0b59379c4e update readme 2022-11-07 23:35:31 +01:00
sk
58abdad62a bump version 2022-11-07 21:18:41 +01:00
sk
c693414cc3 update readme 2022-11-07 21:18:37 +01:00
sk
ee158e1aba Merge branch 'feature/mark-media-as-sensitive' into fork 2022-11-07 21:04:44 +01:00
sk
40d478aaec tweak styles 2022-11-07 21:04:28 +01:00
sk
e7fb96b3ff Merge branch 'feature/mark-media-as-sensitive' into fork 2022-11-07 20:47:15 +01:00
sk
ef75427b45 fix option not being there when editing 2022-11-07 20:46:49 +01:00
sk
2ff4f00774 Merge branch 'feature/mark-media-as-sensitive' into fork 2022-11-07 20:17:39 +01:00
sk
534fd8c119 implement "mark media as sensitive" button
fixes #21
2022-11-07 20:15:16 +01:00
sk
2d2cd89454 Merge branch 'spoiler-height-independent' into fork 2022-11-07 17:32:55 +01:00
sk
9f3f5ca7c1 make spoiler height independent of content
fixes #12, fixes mastodon/mastodon-android#166
2022-11-07 17:32:37 +01:00
Samuel Kaiser
c0e6f17c83 Merge pull request #28 from br4yd/patch-1
Add German translation for the federated timeline
2022-11-07 16:57:21 +01:00
sk
677f1cb42d Merge remote-tracking branch 'origin/upstream' into fork 2022-11-07 16:56:27 +01:00
Gregory K
1ac6a04a46 Merge pull request #309 from julroy67/perapp-language
Add Android 13 per-app language preferences
2022-11-07 06:49:58 +03:00
Julien Humbert
21927d2e25 Add missing line in Android Manifest 2022-11-07 12:32:20 +09:00
Julien Humbert
c35441f5f7 Add per-app language preferences 2022-11-07 12:27:26 +09:00
br4yd
3718fe6601 Add German translation for the federated timeline
I added the translation strings for the federation timeline tab to the German translation file and also translated the values.
2022-11-06 22:21:53 +01:00
sk
095f234bd5 bump version 2022-11-06 11:16:31 +01:00
sk
f10c0e06db Merge branch 'fork' of github.com:sk22/mastodon-android-fork into fork 2022-11-06 11:13:42 +01:00
sk
ebbd5d1fa3 Merge branch 'upstream' into fork 2022-11-06 11:13:26 +01:00
Grishka
c8abf26040 Fix #301 2022-11-06 05:43:17 +03:00
Grishka
bc733af147 Actually no, this makes more sense 2022-11-06 05:39:22 +03:00
Grishka
77a2fd2a60 Fix #298 2022-11-06 05:36:43 +03:00
Samuel Kaiser
5cfc5eb08a Update README.md 2022-11-05 11:27:02 +01:00
Samuel Kaiser
b832d2df26 Update README.md 2022-11-04 18:02:33 +01:00
Samuel Kaiser
db34dc40ba Merge branch 'mastodon:master' into fork 2022-11-04 14:32:13 +01:00
Gregory K
bfa48c2d3e Merge pull request #289 from sk22/fix-editing-cw
Fix spoiler not being published when editing
2022-11-04 07:02:40 +03:00
Gregory K
b5e229a84d Merge pull request #288 from sk22/fix-editing-visibility
Fix wrong visibility displayed when editing
2022-11-04 07:02:19 +03:00
sk
ef207f885b increase update check interval 2022-11-04 03:37:32 +01:00
sk
c4eee28335 Merge branch 'feature/check-for-update-button' into fork 2022-11-04 03:34:06 +01:00
sk
6fe466779e add toash message 2022-11-04 03:33:41 +01:00
sk
e71db1b883 custom app name in strings 2022-11-04 03:11:10 +01:00
sk
b6efafe99d Merge branch 'feature/check-for-update-button' into fork 2022-11-04 03:09:39 +01:00
sk
688d466f8e implement manual update check settings item 2022-11-04 03:09:19 +01:00
Samuel Kaiser
3c5797932e add download badge 2022-11-03 17:42:31 +01:00
sk
48a5e262ce bump version 2022-11-03 17:18:57 +01:00
sk
63b0365208 remove reverted default visibility 2022-11-03 17:18:42 +01:00
sk
972c05d60b Revert "set unlisted as default visibility"
This reverts commit d34653750e.
2022-11-03 17:13:10 +01:00
Y32Gcnte8z
53f8f41d88 remain visibility when editing 2022-11-03 16:15:17 +01:00
49 changed files with 511 additions and 102 deletions

View File

@@ -4,24 +4,37 @@
> A fork of the [official Mastodon Android app](https://github.com/mastodon/mastodon-android) adding important features that are missing in the official app and possibly wont ever be implemented, such as the federated timeline, unlisted posting, bookmarks and an image description viewer.
[![Download latest release](https://img.shields.io/badge/dynamic/json?color=d92aad&label=download%20apk&query=%24.tag_name&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fsk22%2Fmastodon-android-fork%2Freleases%2Flatest&style=for-the-badge)](https://github.com/sk22/mastodon-android-fork/releases/latest/download/mastodos.apk)
## Changes
### Features
* [Add “Unlisted” as a post visibility option](https://github.com/sk22/mastodon-android-fork/tree/feature/enable-unlisted)
([Pull request](https://github.com/mastodon/mastodon-android/pull/103))
* [Add “Federation” tab and change Discover tab order](https://github.com/sk22/mastodon-android-fork/tree/feature/add-federated-timeline) ([Fixes issue](https://github.com/mastodon/mastodon-android/issues/8))
* [Add “Federation” tab and change Discover tab order](https://github.com/sk22/mastodon-android-fork/tree/feature/add-federated-timeline) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/8))
* [Add image description button and viewer](https://github.com/sk22/mastodon-android-fork/tree/feature/display-alt-text) ([Pull request](https://github.com/mastodon/mastodon-android/pull/129))
* [Implement pinning posts and displaying pinned posts](https://github.com/sk22/mastodon-android-fork/tree/feature/pin-posts) ([Pull request](https://github.com/mastodon/mastodon-android/pull/140))
* [Implement a bookmark button and list](https://github.com/sk22/mastodon-android-fork/tree/feature/bookmarks) ([Fixes issue](https://github.com/mastodon/mastodon-android/issues/22))
* [Implement deleting and re-drafting](https://github.com/sk22/mastodon-android-fork/tree/feature/delete-redraft) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/21))
* [Implement a bookmark button and list](https://github.com/sk22/mastodon-android-fork/tree/feature/bookmarks) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/22))
* [Add “Check for update” button in addition to integrated update checker](https://github.com/sk22/mastodon-android-fork/commits/feature/check-for-update-button)
* [Add “Mark media as sensitive” option](https://github.com/sk22/mastodon-android-fork/tree/feature/mark-media-as-sensitive)
* [Add settings to hide replies and reposts from the timeline](https://github.com/sk22/mastodon-android-fork/tree/feature/filter-home-timeline) ([Pull request](https://github.com/mastodon/mastodon-android/pull/317))
* [Follow and unfollow hashtags](https://github.com/sk22/mastodon-android-fork/commit/7d38f031f197aa6cefaf53e39d929538689c1e4e) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/233))
* [Notification bell for posts](https://github.com/sk22/mastodon-android-fork/commit/b166ca705eb9169025ef32bbe6315b42491b57ea) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/81))
### Behavior
* [Set “Unlisted” as default posting visibility](https://github.com/sk22/mastodon-android-fork/tree/feature/enable-unlisted-as-default)
* [Make back button return to the home tab before exiting the app](https://github.com/sk22/mastodon-android-fork/tree/feature/back-returns-home) ([Fixes issue](https://github.com/mastodon/mastodon-android/issues/118))
* [Always preserve content warnings when replying](https://github.com/sk22/mastodon-android-fork/tree/feature/always-preserve-cw) ([Fixes issue](https://github.com/mastodon/mastodon-android/issues/113))
* [Make back button return to the home tab before exiting the app](https://github.com/sk22/mastodon-android-fork/tree/feature/back-returns-home) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/118))
* [Always preserve content warnings when replying](https://github.com/sk22/mastodon-android-fork/tree/feature/always-preserve-cw) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/113))
* [Display full image when adding image description](https://github.com/sk22/mastodon-android-fork/tree/feature/compose-image-description-full-image) ([Pull request](https://github.com/mastodon/mastodon-android/pull/182))
* [Implement deleting and re-drafting](https://github.com/sk22/mastodon-android-fork/tree/feature/delete-redraft) ([Fixes issue](https://github.com/mastodon/mastodon-android/issues/21))
* [Set spoiler height independently to content height](https://github.com/sk22/mastodon-android-fork/commits/spoiler-height-independent) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/166))
### Installation
To install this app on your Android device, download the [latest release from GitHub](https://github.com/sk22/mastodon-android-fork/releases/latest/download/mastodos.apk) and open it. You might have to accept installing APK files from your browser when trying to install it. You can also take a look at all releases on the [Releases](https://github.com/sk22/mastodon-android-fork/releases) page.
Mastodos makes use of [Mastodon for Android](https://github.com/mastodon/mastodon-android)s automatic update checker. Mastodos will check for new updates available on GitHub and offer to download and install them. You can also manually press “Check for updates” at the bottom of the settings page!
### Branding

View File

@@ -9,9 +9,13 @@ android {
applicationId "org.joinmastodon.android.sk"
minSdk 23
targetSdk 33
versionCode 27
versionName "1.1.4+fork.27"
versionCode 33
versionName "1.1.4+fork.33"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resConfigs "en", "ar-rSA", "bs-rBA", "ca-rES", "cs-rCZ", "de-rDE", "el-rGR", "es-rES",
"eu-rES", "fi-rFI", "fr-rFR", "gl-rES", "hr-rHR", "hy-rAM", "it-rIT", "iw-rIL",
"ja-rJP", "kab", "ko-rKR", "oc-rFR", "pl-rPL", "pt-rBR", "pt-rPT", "ru-rRU",
"sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "vi-rVN", "zh-rCN", "zh-rTW"
}
buildTypes {

View File

@@ -26,6 +26,8 @@ import org.joinmastodon.android.api.MastodonAPIController;
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
import java.io.File;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -36,7 +38,7 @@ import okhttp3.Response;
@Keep
public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
private static final long CHECK_PERIOD=24*3600*1000L;
private static final long CHECK_PERIOD=6*3600*1000L;
private static final String TAG="GithubSelfUpdater";
private UpdateState state=UpdateState.NO_UPDATE;
@@ -101,6 +103,12 @@ public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
}
}
@Override
public void checkForUpdates() {
setState(UpdateState.CHECKING);
MastodonAPIController.runInBackground(this::actuallyCheckForUpdates);
}
private void actuallyCheckForUpdates(){
Request req=new Request.Builder()
.url("https://api.github.com/repos/sk22/mastodon-android-fork/releases/latest")

View File

@@ -16,6 +16,7 @@
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true"
android:localeConfig="@xml/locales_config"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/Theme.Mastodon.AutoLightDark"

View File

@@ -7,6 +7,9 @@ public class GlobalUserPreferences{
public static boolean playGifs;
public static boolean useCustomTabs;
public static boolean trueBlackTheme;
public static boolean showReplies;
public static boolean showBoosts;
public static boolean loadNewPosts;
public static ThemePreference theme;
private static SharedPreferences getPrefs(){
@@ -18,6 +21,9 @@ public class GlobalUserPreferences{
playGifs=prefs.getBoolean("playGifs", true);
useCustomTabs=prefs.getBoolean("useCustomTabs", true);
trueBlackTheme=prefs.getBoolean("trueBlackTheme", false);
showReplies=prefs.getBoolean("showReplies", true);
showBoosts=prefs.getBoolean("showBoosts", true);
loadNewPosts=prefs.getBoolean("loadNewPosts", true);
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
}
@@ -25,6 +31,9 @@ public class GlobalUserPreferences{
getPrefs().edit()
.putBoolean("playGifs", playGifs)
.putBoolean("useCustomTabs", useCustomTabs)
.putBoolean("showReplies", showReplies)
.putBoolean("showBoosts", showBoosts)
.putBoolean("loadNewPosts", loadNewPosts)
.putBoolean("trueBlackTheme", trueBlackTheme)
.putInt("theme", theme.ordinal())
.apply();

View File

@@ -12,6 +12,7 @@ import android.media.ExifInterface;
import android.net.Uri;
import android.os.Build;
import android.provider.OpenableColumns;
import android.text.TextUtils;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.ui.utils.UiUtils;
@@ -48,6 +49,8 @@ public class ResizedImageRequestBody extends CountingRequestBody{
}
contentType=MastodonApp.context.getContentResolver().getType(uri);
}
if(TextUtils.isEmpty(contentType))
contentType="image/jpeg";
if(needResize(opts.outWidth, opts.outHeight) || needCrop(opts.outWidth, opts.outHeight)){
Bitmap bitmap;
if(Build.VERSION.SDK_INT>=28){

View File

@@ -4,10 +4,10 @@ import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Relationship;
public class SetAccountFollowed extends MastodonAPIRequest<Relationship>{
public SetAccountFollowed(String id, boolean followed, boolean showReblogs){
public SetAccountFollowed(String id, boolean followed, boolean showReblogs, boolean notify){
super(HttpMethod.POST, "/accounts/"+id+"/"+(followed ? "follow" : "unfollow"), Relationship.class);
if(followed)
setRequestBody(new Request(showReblogs, null));
setRequestBody(new Request(showReblogs, notify));
else
setRequestBody(new Object());
}

View File

@@ -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 GetHashtag extends MastodonAPIRequest<Hashtag> {
public GetHashtag(String name){
super(HttpMethod.GET, "/tags/"+name, Hashtag.class);
}
}

View File

@@ -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 SetHashtagFollowed extends MastodonAPIRequest<Hashtag>{
public SetHashtagFollowed(String name, boolean followed){
super(HttpMethod.POST, "/tags/"+name+"/"+(followed ? "follow" : "unfollow"), Hashtag.class);
setRequestBody(new Object());
}
}

View File

@@ -4,7 +4,7 @@ import org.joinmastodon.android.model.Status;
public class StatusCountersUpdatedEvent{
public String id;
public int favorites, reblogs, replies;
public long favorites, reblogs, replies;
public boolean favorited, reblogged, pinned;
public StatusCountersUpdatedEvent(Status s){

View File

@@ -161,11 +161,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
private Button publishButton;
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, visibilityBtn;
private ImageView sensitiveIcon;
private ComposeMediaLayout attachmentsView;
private TextView replyText;
private ReorderableLinearLayout pollOptionsView;
private View pollWrap;
private View addPollOptionBtn;
private View sensitiveItem;
private TextView pollDurationView;
private ArrayList<DraftPollOption> pollOptions=new ArrayList<>();
@@ -181,11 +183,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
private String pollDurationStr;
private EditText spoilerEdit;
private boolean hasSpoiler;
private boolean sensitive;
private ProgressBar sendProgress;
private ImageView sendError;
private View sendingOverlay;
private WindowManager wm;
private StatusPrivacy statusVisibility=StatusPrivacy.UNLISTED;
private StatusPrivacy statusVisibility=StatusPrivacy.PUBLIC;
private ComposeAutocompleteSpan currentAutocompleteSpan;
private FrameLayout mainEditTextWrap;
private ComposeAutocompleteViewController autocompleteViewController;
@@ -290,6 +293,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
emojiBtn=view.findViewById(R.id.btn_emoji);
spoilerBtn=view.findViewById(R.id.btn_spoiler);
visibilityBtn=view.findViewById(R.id.btn_visibility);
sensitiveIcon=view.findViewById(R.id.sensitive_icon);
sensitiveItem=view.findViewById(R.id.sensitive_item);
replyText=view.findViewById(R.id.reply_text);
mediaBtn.setOnClickListener(v->openFilePicker());
@@ -297,6 +302,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
emojiBtn.setOnClickListener(v->emojiKeyboard.toggleKeyboardPopup(mainEditText));
spoilerBtn.setOnClickListener(v->toggleSpoiler());
visibilityBtn.setOnClickListener(this::onVisibilityClick);
sensitiveItem.setOnClickListener(v->toggleSensitive());
emojiKeyboard.setOnIconChangedListener(new PopupKeyboard.OnIconChangeListener(){
@Override
public void onIconChanged(int icon){
@@ -368,6 +374,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
spoilerBtn.setSelected(true);
}
sensitive = editingStatus != null ? editingStatus.sensitive
: (savedInstanceState != null && savedInstanceState.getBoolean("sensitive", false));
sensitiveIcon.setSelected(sensitive);
ArrayList<Parcelable> serializedAttachments=(savedInstanceState!=null ? savedInstanceState : getArguments())
.getParcelableArrayList("attachments");
if(serializedAttachments!=null){
@@ -387,6 +397,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
if(editingStatus!=null && editingStatus.visibility!=null) {
statusVisibility=editingStatus.visibility;
}
updateVisibilityIcon();
autocompleteViewController=new ComposeAutocompleteViewController(getActivity(), accountID);
@@ -411,16 +422,17 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
outState.putStringArrayList("pollOptions", opts);
outState.putInt("pollDuration", pollDuration);
outState.putString("pollDurationStr", pollDurationStr);
outState.putBoolean("hasSpoiler", hasSpoiler);
if(!attachments.isEmpty()){
ArrayList<Parcelable> serializedAttachments=new ArrayList<>(attachments.size());
for(DraftMediaAttachment att:attachments){
serializedAttachments.add(Parcels.wrap(att));
}
outState.putParcelableArrayList("attachments", serializedAttachments);
}
outState.putSerializable("visibility", statusVisibility);
}
outState.putBoolean("hasSpoiler", hasSpoiler);
outState.putBoolean("sensitive", sensitive);
if(!attachments.isEmpty()){
ArrayList<Parcelable> serializedAttachments=new ArrayList<>(attachments.size());
for(DraftMediaAttachment att:attachments){
serializedAttachments.add(Parcels.wrap(att));
}
outState.putParcelableArrayList("attachments", serializedAttachments);
}
outState.putSerializable("visibility", statusVisibility);
}
@Override
@@ -545,6 +557,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
da.serverAttachment=att;
da.description=att.description;
da.uri=Uri.parse(att.previewUrl);
da.state=AttachmentUploadState.DONE;
attachmentsView.addView(createMediaAttachmentView(da));
attachments.add(da);
}
@@ -568,8 +581,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
}
}
updateSensitive();
if(editingStatus!=null){
updateCharCounter();
visibilityBtn.setEnabled(false);
}
}
@@ -677,6 +693,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
CreateStatus.Request req=new CreateStatus.Request();
req.status=text;
req.visibility=statusVisibility;
req.sensitive=sensitive;
if(!attachments.isEmpty()){
req.mediaIds=attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
}
@@ -883,6 +900,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
uploadNextQueuedAttachment();
}
updatePublishButtonState();
updateSensitive();
if(getMediaAttachmentsCount()==MAX_ATTACHMENTS)
mediaBtn.setEnabled(false);
return true;
@@ -1057,6 +1075,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
updatePublishButtonState();
pollBtn.setEnabled(attachments.isEmpty());
mediaBtn.setEnabled(true);
updateSensitive();
}
private void onRetryOrCancelMediaUploadClick(View v){
@@ -1258,7 +1277,20 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
spoilerBtn.setSelected(false);
mainEditText.requestFocus();
updateCharCounter();
sensitiveIcon.setVisibility(getMediaAttachmentsCount() > 0 ? View.VISIBLE : View.GONE);
}
updateSensitive();
}
private void toggleSensitive() {
sensitive=!sensitive;
sensitiveIcon.setSelected(sensitive);
}
private void updateSensitive() {
sensitiveItem.setVisibility(View.GONE);
if (!attachments.isEmpty() && !hasSpoiler) sensitiveItem.setVisibility(View.VISIBLE);
if (attachments.isEmpty()) sensitive = false;
}
private int getMediaAttachmentsCount(){

View File

@@ -2,23 +2,34 @@ package org.joinmastodon.android.fragments;
import android.app.Activity;
import android.os.Bundle;
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.Toast;
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.requests.timelines.GetHashtagTimeline;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.model.Status;
import java.util.List;
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.V;
public class HashtagTimelineFragment extends StatusListFragment{
private String hashtag;
private boolean following;
private ImageButton fab;
private MenuItem followButton;
public HashtagTimelineFragment(){
setListLayoutId(R.layout.recycler_fragment_with_fab);
@@ -27,10 +38,61 @@ public class HashtagTimelineFragment extends StatusListFragment{
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
hashtag=getArguments().getString("hashtag");
updateTitle(getArguments().getString("hashtag"));
following=getArguments().getBoolean("following", false);
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);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.hashtag_timeline, menu);
followButton = menu.findItem(R.id.follow_hashtag);
updateFollowingState(following);
followButton.setOnMenuItemClickListener(i -> {
updateFollowingState(!following);
new SetHashtagFollowed(hashtag, following).setCallback(new Callback<>() {
@Override
public void onSuccess(Hashtag i) {
updateFollowingState(i.following);
Toast.makeText(getActivity(), getString(i.following ? R.string.followed_user : R.string.unfollowed_user, "#" + i.name), Toast.LENGTH_SHORT).show();
}
@Override
public void onError(ErrorResponse error) {
error.showToast(getActivity());
updateFollowingState(!following);
}
}).exec(accountID);
return true;
});
new GetHashtag(hashtag).setCallback(new Callback<>() {
@Override
public void onSuccess(Hashtag hashtag) {
updateTitle(hashtag.name);
updateFollowingState(hashtag.following);
}
@Override
public void onError(ErrorResponse error) {
error.showToast(getActivity());
}
}).exec(accountID);
}
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetHashtagTimeline(hashtag, offset==0 ? null : getMaxID(), null, count)

View File

@@ -24,6 +24,7 @@ import android.widget.Toolbar;
import com.squareup.otto.Subscribe;
import org.joinmastodon.android.E;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
import org.joinmastodon.android.api.session.AccountSessionManager;
@@ -73,6 +74,13 @@ public class HomeTimelineFragment extends StatusListFragment{
loadData();
}
private List<Status> filterPosts(List<Status> items) {
return items.stream().filter(i ->
(GlobalUserPreferences.showReplies || i.inReplyToId == null) &&
(GlobalUserPreferences.showBoosts || i.reblog == null)
).collect(Collectors.toList());
}
@Override
protected void doLoadData(int offset, int count){
AccountSessionManager.getInstance()
@@ -82,7 +90,8 @@ public class HomeTimelineFragment extends StatusListFragment{
public void onSuccess(CacheablePaginatedResponse<List<Status>> result){
if(getActivity()==null)
return;
onDataLoaded(result.items, !result.items.isEmpty());
List<Status> filteredItems = filterPosts(result.items);
onDataLoaded(filteredItems, !result.items.isEmpty());
maxID=result.maxID;
if(result.isFromCache())
loadNewPosts();
@@ -153,6 +162,7 @@ public class HomeTimelineFragment extends StatusListFragment{
}
private void loadNewPosts(){
if (!GlobalUserPreferences.loadNewPosts) return;
dataLoading=true;
// The idea here is that we request the timeline such that if there are fewer than `limit` posts,
// we'll get the currently topmost post as last in the response. This way we know there's no gap
@@ -164,6 +174,7 @@ public class HomeTimelineFragment extends StatusListFragment{
public void onSuccess(List<Status> result){
currentRequest=null;
dataLoading=false;
result = filterPosts(result);
if(result.isEmpty() || getActivity()==null)
return;
Status last=result.get(result.size()-1);

View File

@@ -99,6 +99,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
private View avatarBorder;
private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel, postsCount, postsLabel;
private ProgressBarButton actionButton;
private Button notifyButton;
private ViewPager2 pager;
private NestedRecyclerScrollView scrollView;
private AccountTimelineFragment postsFragment, postsWithRepliesFragment, pinnedPostsFragment, mediaFragment;
@@ -181,6 +182,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
postsLabel=content.findViewById(R.id.posts_label);
postsBtn=content.findViewById(R.id.posts_btn);
actionButton=content.findViewById(R.id.profile_action_btn);
notifyButton=content.findViewById(R.id.notify_btn);
pager=content.findViewById(R.id.pager);
scrollView=content.findViewById(R.id.scroller);
tabbar=content.findViewById(R.id.tabbar);
@@ -258,6 +260,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
});
actionButton.setOnClickListener(this::onActionButtonClick);
notifyButton.setOnClickListener(this::onNotifyButtonClick);
avatar.setOnClickListener(this::onAvatarClick);
cover.setOnClickListener(this::onCoverClick);
refreshLayout.setOnRefreshListener(this);
@@ -414,7 +417,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
private void bindHeaderView(){
setTitle(account.displayName);
setSubtitle(getResources().getQuantityString(R.plurals.x_posts, account.statusesCount, account.statusesCount));
setSubtitle(getResources().getQuantityString(R.plurals.x_posts, (int)(account.statusesCount%1000), account.statusesCount));
ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic, V.dp(100), V.dp(100)));
ViewImageLoader.load(cover, null, new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.header : account.headerStatic, 1000, 1000));
SpannableStringBuilder ssb=new SpannableStringBuilder(account.displayName);
@@ -451,15 +454,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
followersCount.setText(UiUtils.abbreviateNumber(account.followersCount));
followingCount.setText(UiUtils.abbreviateNumber(account.followingCount));
postsCount.setText(UiUtils.abbreviateNumber(account.statusesCount));
followersLabel.setText(getResources().getQuantityString(R.plurals.followers, Math.min(999, account.followersCount)));
followingLabel.setText(getResources().getQuantityString(R.plurals.following, Math.min(999, account.followingCount)));
postsLabel.setText(getResources().getQuantityString(R.plurals.posts, Math.min(999, account.statusesCount)));
followersLabel.setText(getResources().getQuantityString(R.plurals.followers, (int)Math.min(999, account.followersCount)));
followingLabel.setText(getResources().getQuantityString(R.plurals.following, (int)Math.min(999, account.followingCount)));
postsLabel.setText(getResources().getQuantityString(R.plurals.posts, (int)Math.min(999, account.statusesCount)));
UiUtils.loadCustomEmojiInTextView(name);
UiUtils.loadCustomEmojiInTextView(bio);
if(AccountSessionManager.getInstance().isSelf(accountID, account)){
actionButton.setText(R.string.edit_profile);
notifyButton.setVisibility(View.GONE);
}else{
actionButton.setVisibility(View.GONE);
}
@@ -577,7 +581,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
updateRelationship();
});
}else if(id==R.id.hide_boosts){
new SetAccountFollowed(account.id, true, !relationship.showingReblogs)
new SetAccountFollowed(account.id, true, !relationship.showingReblogs, relationship.notifying)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Relationship result){
@@ -625,6 +629,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
UiUtils.setRelationshipToActionButton(relationship, actionButton);
actionProgress.setIndeterminateTintList(actionButton.getTextColors());
followsYouView.setVisibility(relationship.followedBy ? View.VISIBLE : View.GONE);
notifyButton.setVisibility(relationship.following ? View.VISIBLE : View.GONE);
notifyButton.setSelected(relationship.notifying);
}
private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){
@@ -859,6 +865,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
return Collections.singletonList(att);
}
private void onNotifyButtonClick(View v) {
UiUtils.performToggleAccountNotifications(getActivity(), account, accountID, relationship, actionButton, this::updateRelationship);
}
private void onAvatarClick(View v){
if(isInEditMode){
startImagePicker(AVATAR_RESULT);

View File

@@ -15,6 +15,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.ImageButton;
@@ -71,6 +72,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
private PushSubscription pushSubscription;
private ImageView themeTransitionWindowView;
private TextItem checkForUpdateItem;
@Override
public void onCreate(Bundle savedInstanceState){
@@ -103,6 +105,20 @@ public class SettingsFragment extends MastodonToolbarFragment{
GlobalUserPreferences.save();
}));
items.add(new HeaderItem(R.string.home_timeline));
items.add(new SwitchItem(R.string.settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
GlobalUserPreferences.showReplies=i.checked;
GlobalUserPreferences.save();
}));
items.add(new SwitchItem(R.string.settings_show_boosts, R.drawable.ic_fluent_arrow_repeat_all_24_regular, GlobalUserPreferences.showBoosts, i->{
GlobalUserPreferences.showBoosts=i.checked;
GlobalUserPreferences.save();
}));
items.add(new SwitchItem(R.string.settings_load_new_posts, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.loadNewPosts, i->{
GlobalUserPreferences.loadNewPosts=i.checked;
GlobalUserPreferences.save();
}));
items.add(new HeaderItem(R.string.settings_notifications));
items.add(notificationPolicyItem=new NotificationPolicyItem());
PushSubscription pushSubscription=getPushSubscription();
@@ -113,11 +129,15 @@ public class SettingsFragment extends MastodonToolbarFragment{
items.add(new HeaderItem(R.string.settings_boring));
items.add(new TextItem(R.string.settings_account, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/auth/edit")));
items.add(new TextItem(R.string.settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/mastodon/mastodon-android")));
items.add(new TextItem(R.string.settings_tos, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms")));
items.add(new TextItem(R.string.settings_privacy_policy, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms")));
items.add(new RedHeaderItem(R.string.settings_spicy));
if (GithubSelfUpdater.needSelfUpdating()) {
checkForUpdateItem = new TextItem(R.string.check_for_update, GithubSelfUpdater.getInstance()::checkForUpdates);
items.add(checkForUpdateItem);
}
items.add(new TextItem(R.string.settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/mastodon/mastodon-android")));
items.add(new TextItem(R.string.settings_clear_cache, this::clearImageCache));
items.add(new TextItem(R.string.log_out, this::confirmLogOut));
@@ -326,11 +346,25 @@ public class SettingsFragment extends MastodonToolbarFragment{
@Subscribe
public void onSelfUpdateStateChanged(SelfUpdateStateChangedEvent ev){
if(items.get(0) instanceof UpdateItem item){
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(0);
if(holder instanceof UpdateViewHolder uvh){
uvh.bind(item);
}
checkForUpdateItem.loading = ev.state == GithubSelfUpdater.UpdateState.CHECKING;
if (list.findViewHolderForAdapterPosition(items.indexOf(checkForUpdateItem)) instanceof TextViewHolder tvh) tvh.rebind();
UpdateItem updateItem = null;
if(items.get(0) instanceof UpdateItem item0) {
updateItem = item0;
} else if (ev.state != GithubSelfUpdater.UpdateState.CHECKING
&& ev.state != GithubSelfUpdater.UpdateState.NO_UPDATE) {
updateItem = new UpdateItem();
items.add(0, updateItem);
list.setAdapter(new SettingsAdapter());
}
if(updateItem != null && list.findViewHolderForAdapterPosition(0) instanceof UpdateViewHolder uvh){
uvh.bind(updateItem);
}
if (ev.state == GithubSelfUpdater.UpdateState.NO_UPDATE) {
Toast.makeText(getActivity(), R.string.no_update_available, Toast.LENGTH_SHORT).show();
}
}
@@ -398,10 +432,16 @@ public class SettingsFragment extends MastodonToolbarFragment{
private class TextItem extends Item{
private String text;
private Runnable onClick;
private boolean loading;
public TextItem(@StringRes int text, Runnable onClick){
public TextItem(@StringRes int text, Runnable onClick) {
this(text, onClick, false);
}
public TextItem(@StringRes int text, Runnable onClick, boolean loading){
this.text=getString(text);
this.onClick=onClick;
this.loading=loading;
}
@Override
@@ -630,14 +670,18 @@ public class SettingsFragment extends MastodonToolbarFragment{
private class TextViewHolder extends BindableViewHolder<TextItem> implements UsableRecyclerView.Clickable{
private final TextView text;
private final ProgressBar progress;
public TextViewHolder(){
super(getActivity(), R.layout.item_settings_text, list);
text=(TextView) itemView;
text = itemView.findViewById(R.id.text);
progress = itemView.findViewById(R.id.progress);
}
@Override
public void onBind(TextItem item){
text.setText(item.text);
progress.animate().alpha(item.loading ? 1 : 0);
}
@Override
@@ -692,8 +736,9 @@ public class SettingsFragment extends MastodonToolbarFragment{
@Override
public void onBind(UpdateItem item){
GithubSelfUpdater updater=GithubSelfUpdater.getInstance();
GithubSelfUpdater.UpdateInfo info=updater.getUpdateInfo();
GithubSelfUpdater.UpdateState state=updater.getState();
if (state == GithubSelfUpdater.UpdateState.CHECKING) return;
GithubSelfUpdater.UpdateInfo info=updater.getUpdateInfo();
if(state!=GithubSelfUpdater.UpdateState.DOWNLOADED){
text.setText(getString(R.string.update_available, info.version));
button.setText(getString(R.string.download_update, UiUtils.formatFileSize(getActivity(), info.size, false)));

View File

@@ -353,7 +353,7 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
bindRelationship();
});
}else if(id==R.id.hide_boosts){
new SetAccountFollowed(account.id, true, !relationship.showingReblogs)
new SetAccountFollowed(account.id, true, !relationship.showingReblogs, relationship.notifying)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Relationship result){

View File

@@ -12,7 +12,7 @@ public class FollowerListFragment extends AccountRelatedAccountListFragment{
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setSubtitle(getResources().getQuantityString(R.plurals.x_followers, account.followersCount, account.followersCount));
setSubtitle(getResources().getQuantityString(R.plurals.x_followers, (int)(account.followersCount%1000), account.followersCount));
}
@Override

View File

@@ -12,7 +12,7 @@ public class FollowingListFragment extends AccountRelatedAccountListFragment{
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setSubtitle(getResources().getQuantityString(R.plurals.x_following, account.followingCount, account.followingCount));
setSubtitle(getResources().getQuantityString(R.plurals.x_following, (int)(account.followingCount%1000), account.followingCount));
}
@Override

View File

@@ -11,7 +11,7 @@ public class StatusFavoritesListFragment extends StatusRelatedAccountListFragmen
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setTitle(getResources().getQuantityString(R.plurals.x_favorites, status.favouritesCount, status.favouritesCount));
setTitle(getResources().getQuantityString(R.plurals.x_favorites, (int)(status.favouritesCount%1000), status.favouritesCount));
}
@Override

View File

@@ -11,7 +11,7 @@ public class StatusReblogsListFragment extends StatusRelatedAccountListFragment{
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setTitle(getResources().getQuantityString(R.plurals.x_reblogs, status.reblogsCount, status.reblogsCount));
setTitle(getResources().getQuantityString(R.plurals.x_reblogs, (int)(status.reblogsCount%1000), status.reblogsCount));
}
@Override

View File

@@ -220,9 +220,9 @@ public class DiscoverAccountsFragment extends BaseRecyclerFragment<DiscoverAccou
followersCount.setText(UiUtils.abbreviateNumber(item.account.followersCount));
followingCount.setText(UiUtils.abbreviateNumber(item.account.followingCount));
postsCount.setText(UiUtils.abbreviateNumber(item.account.statusesCount));
followersLabel.setText(getResources().getQuantityString(R.plurals.followers, Math.min(999, item.account.followersCount)));
followingLabel.setText(getResources().getQuantityString(R.plurals.following, Math.min(999, item.account.followingCount)));
postsLabel.setText(getResources().getQuantityString(R.plurals.posts, Math.min(999, item.account.statusesCount)));
followersLabel.setText(getResources().getQuantityString(R.plurals.followers, (int)Math.min(999, item.account.followersCount)));
followingLabel.setText(getResources().getQuantityString(R.plurals.following, (int)Math.min(999, item.account.followingCount)));
postsLabel.setText(getResources().getQuantityString(R.plurals.posts, (int)Math.min(999, item.account.statusesCount)));
relationship=relationships.get(item.account.id);
if(relationship==null){
actionWrap.setVisibility(View.GONE);

View File

@@ -91,6 +91,8 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
@Override
public void onItemClick(String id){
SearchResult res=getResultByID(id);
if(res==null)
return;
switch(res.type){
case ACCOUNT -> {
Bundle args=new Bundle();
@@ -98,7 +100,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);
case HASHTAG -> UiUtils.openHashtagTimeline(getActivity(), accountID, res.hashtag.name, res.hashtag.following);
case STATUS -> {
Status status=res.status.getContentStatus();
Bundle args=new Bundle();

View File

@@ -107,7 +107,7 @@ public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag> impl
@Override
public void onClick(){
UiUtils.openHashtagTimeline(getActivity(), accountID, item.name);
UiUtils.openHashtagTimeline(getActivity(), accountID, item.name, item.following);
}
}
}

View File

@@ -200,7 +200,6 @@ public class SignupFragment extends AppKitFragment{
@Override
public void onSuccess(Token result){
progressDialog.dismiss();
progressDialog=null;
Account fakeAccount=new Account();
fakeAccount.acct=fakeAccount.username=username;
fakeAccount.id="tmp"+System.currentTimeMillis();
@@ -238,7 +237,6 @@ public class SignupFragment extends AppKitFragment{
error.showToast(getActivity());
}
progressDialog.dismiss();
progressDialog=null;
}
})
.exec(instance.uri, apiToken);
@@ -255,9 +253,11 @@ public class SignupFragment extends AppKitFragment{
}
private void showProgressDialog(){
progressDialog=new ProgressDialog(getActivity());
progressDialog.setMessage(getString(R.string.loading));
progressDialog.setCancelable(false);
if(progressDialog==null){
progressDialog=new ProgressDialog(getActivity());
progressDialog.setMessage(getString(R.string.loading));
progressDialog.setCancelable(false);
}
progressDialog.show();
}
@@ -280,7 +280,6 @@ public class SignupFragment extends AppKitFragment{
if(submitAfterGettingToken){
submitAfterGettingToken=false;
progressDialog.dismiss();
progressDialog=null;
error.showToast(getActivity());
}
}
@@ -307,7 +306,6 @@ public class SignupFragment extends AppKitFragment{
if(submitAfterGettingToken){
submitAfterGettingToken=false;
progressDialog.dismiss();
progressDialog=null;
error.showToast(getActivity());
}
}

View File

@@ -125,7 +125,7 @@ public class ReportDoneFragment extends MastodonToolbarFragment{
}
private void onUnfollowClick(){
new SetAccountFollowed(reportAccount.id, false, false)
new SetAccountFollowed(reportAccount.id, false, false, false)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Relationship result){

View File

@@ -96,15 +96,15 @@ public class Account extends BaseModel{
/**
* How many statuses are attached to this account.
*/
public int statusesCount;
public long statusesCount;
/**
* The reported followers of this profile.
*/
public int followersCount;
public long followersCount;
/**
* The reported follows of this profile.
*/
public int followingCount;
public long followingCount;
// Optional attributes

View File

@@ -11,6 +11,7 @@ public class Hashtag extends BaseModel{
public String name;
@RequiredField
public String url;
public boolean following;
public List<History> history;
@Override
@@ -18,6 +19,7 @@ public class Hashtag extends BaseModel{
return "Hashtag{"+
"name='"+name+'\''+
", url='"+url+'\''+
", following="+following+
", history="+history+
'}';
}

View File

@@ -34,9 +34,9 @@ public class Status extends BaseModel implements DisplayItemsParent{
public List<Hashtag> tags;
@RequiredField
public List<Emoji> emojis;
public int reblogsCount;
public int favouritesCount;
public int repliesCount;
public long reblogsCount;
public long favouritesCount;
public long repliesCount;
public Instant editedAt;
public String url;

View File

@@ -115,9 +115,9 @@ public class AccountCardStatusDisplayItem extends StatusDisplayItem{
followersCount.setText(UiUtils.abbreviateNumber(item.account.followersCount));
followingCount.setText(UiUtils.abbreviateNumber(item.account.followingCount));
postsCount.setText(UiUtils.abbreviateNumber(item.account.statusesCount));
followersLabel.setText(item.parentFragment.getResources().getQuantityString(R.plurals.followers, Math.min(999, item.account.followersCount)));
followingLabel.setText(item.parentFragment.getResources().getQuantityString(R.plurals.following, Math.min(999, item.account.followingCount)));
postsLabel.setText(item.parentFragment.getResources().getQuantityString(R.plurals.posts, Math.min(999, item.account.statusesCount)));
followersLabel.setText(item.parentFragment.getResources().getQuantityString(R.plurals.followers, (int)Math.min(999, item.account.followersCount)));
followingLabel.setText(item.parentFragment.getResources().getQuantityString(R.plurals.following, (int)Math.min(999, item.account.followingCount)));
postsLabel.setText(item.parentFragment.getResources().getQuantityString(R.plurals.posts, (int)Math.min(999, item.account.statusesCount)));
relationship=item.parentFragment.getRelationship(item.account.id);
if(relationship==null){
actionWrap.setVisibility(View.GONE);

View File

@@ -97,7 +97,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|| (item.status.visibility==StatusPrivacy.PRIVATE && item.status.account.id.equals(AccountSessionManager.getInstance().getAccount(item.accountID).self.id)));
}
private void bindButton(TextView btn, int count){
private void bindButton(TextView btn, long count){
if(count>0 && !item.hideCounts){
btn.setText(DecimalFormat.getIntegerInstance().format(count));
btn.setCompoundDrawablePadding(V.dp(8));

View File

@@ -83,7 +83,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
itemView.setClickable(false);
}else{
spoilerOverlay.setVisibility(View.VISIBLE);
text.setVisibility(View.INVISIBLE);
text.setVisibility(View.GONE);
itemView.setClickable(true);
}
}else{

View File

@@ -34,7 +34,7 @@ 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);
case HASHTAG -> UiUtils.openHashtagTimeline(context, accountID, link, null);
}
}

View File

@@ -80,6 +80,7 @@ import java.util.function.Consumer;
import java.util.stream.Collectors;
import androidx.annotation.AttrRes;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.recyclerview.widget.DiffUtil;
@@ -193,6 +194,15 @@ public class UiUtils{
}
}
@SuppressLint("DefaultLocale")
public static String abbreviateNumber(long n){
if(n<1_000_000_000L)
return abbreviateNumber((int)n);
double a=n/1_000_000_000.0;
return a>99f ? String.format("%,dB", (int)Math.floor(a)) : String.format("%,.1fB", n/1_000_000_000.0);
}
/**
* Android 6.0 has a bug where start and end compound drawables don't get tinted.
* This works around it by setting the tint colors directly to the drawables.
@@ -300,10 +310,11 @@ public class UiUtils{
Nav.go((Activity)context, ProfileFragment.class, args);
}
public static void openHashtagTimeline(Context context, String accountID, String hashtag){
public static void openHashtagTimeline(Context context, String accountID, String hashtag, @Nullable Boolean following){
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);
}
@@ -521,6 +532,22 @@ public class UiUtils{
ta.recycle();
}
public static void performToggleAccountNotifications(Activity activity, Account account, String accountID, Relationship relationship, Button button, Consumer<Relationship> resultCallback) {
new SetAccountFollowed(account.id, true, relationship.showingReblogs, !relationship.notifying)
.setCallback(new Callback<>() {
@Override
public void onSuccess(Relationship result) {
resultCallback.accept(result);
Toast.makeText(activity, activity.getString(result.notifying ? R.string.user_post_notifications_on : R.string.user_post_notifications_off, '@'+account.username), Toast.LENGTH_SHORT).show();
}
@Override
public void onError(ErrorResponse error) {
error.showToast(activity);
}
}).exec(accountID);
}
public static void performAccountAction(Activity activity, Account account, String accountID, Relationship relationship, Button button, Consumer<Boolean> progressCallback, Consumer<Relationship> resultCallback){
if(relationship.blocking){
confirmToggleBlockUser(activity, accountID, account, true, resultCallback);
@@ -528,7 +555,7 @@ public class UiUtils{
confirmToggleMuteUser(activity, accountID, account, true, resultCallback);
}else{
progressCallback.accept(true);
new SetAccountFollowed(account.id, !relationship.following && !relationship.requested, true)
new SetAccountFollowed(account.id, !relationship.following && !relationship.requested, true, false)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Relationship result){

View File

@@ -23,6 +23,8 @@ public abstract class GithubSelfUpdater{
return BuildConfig.BUILD_TYPE.equals("githubRelease") || BuildConfig.BUILD_TYPE.equals("debug");
}
public abstract void checkForUpdates();
public abstract void maybeCheckForUpdates();
public abstract GithubSelfUpdater.UpdateState getState();

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M9.042 19.003h5.916c-0.238 1.418-1.472 2.498-2.958 2.498-1.486 0-2.72-1.08-2.958-2.498zm2.958-17c4.142 0 7.5 3.359 7.5 7.5v4l1.418 3.16c0.055 0.122 0.084 0.254 0.084 0.389 0 0.524-0.426 0.95-0.95 0.95h-16.1c-0.134 0-0.266-0.029-0.388-0.083-0.479-0.215-0.693-0.777-0.479-1.256l1.415-3.16V9.49l0.005-0.25C4.644 5.211 7.955 2.004 12 2.004z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M12 1.996c4.05 0 7.357 3.195 7.496 7.25l0.004 0.25v4.097l1.38 3.156c0.07 0.158 0.105 0.329 0.105 0.5 0 0.691-0.56 1.25-1.25 1.25L15 18.502c0 1.657-1.343 3-3 3-1.598 0-2.904-1.249-2.995-2.823L9 18.499H4.275c-0.171 0-0.34-0.034-0.498-0.103-0.633-0.275-0.924-1.01-0.649-1.644L4.5 13.594V9.496c0-4.155 3.352-7.5 7.5-7.5zM13.5 18.5l-3 0.002c0 0.829 0.672 1.5 1.5 1.5 0.78 0 1.42-0.595 1.493-1.355L13.5 18.5zM12 3.496c-3.32 0-6 2.674-6 6v4.41L4.656 17h14.697L18 13.907V9.509l-0.003-0.225C17.885 6.05 15.242 3.496 12 3.496z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!--~ Copyright (c) 2022. ~ Microsoft Corporation. All rights reserved.-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_fluent_alert_24_filled" android:state_activated="true"/>
<item android:drawable="@drawable/ic_fluent_alert_24_filled" android:state_checked="true"/>
<item android:drawable="@drawable/ic_fluent_alert_24_filled" android:state_selected="true"/>
<item android:drawable="@drawable/ic_fluent_alert_24_regular"/>
</selector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M4.209 10.733c-0.286 0.3-0.274 0.774 0.026 1.06 0.3 0.286 0.774 0.274 1.06-0.026l5.954-6.251V20.25c0 0.414 0.336 0.75 0.75 0.75 0.415 0 0.75-0.336 0.75-0.75V5.516l5.955 6.251c0.286 0.3 0.76 0.312 1.06 0.026 0.3-0.286 0.312-0.76 0.027-1.06l-7.067-7.42c-0.161-0.168-0.367-0.268-0.58-0.3C12.097 3.006 12.049 3 11.999 3c-0.05 0-0.098 0.005-0.145 0.014-0.213 0.031-0.418 0.131-0.578 0.3l-7.067 7.419z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M3 3.748c0-0.414 0.336-0.75 0.75-0.75h16.504c0.618 0 0.971 0.706 0.6 1.2L16.69 9.75l4.164 5.551c0.371 0.495 0.018 1.2-0.6 1.2H4.5v4.75c0 0.38-0.282 0.693-0.648 0.743L3.75 22c-0.38 0-0.693-0.282-0.743-0.648L3 21.25V3.748z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M3 3.748c0-0.414 0.336-0.75 0.75-0.75h16.504c0.618 0 0.971 0.706 0.6 1.2L16.69 9.75l4.164 5.551c0.371 0.495 0.018 1.2-0.6 1.2H4.5v4.75c0 0.38-0.282 0.693-0.648 0.743L3.75 22c-0.38 0-0.693-0.282-0.743-0.648L3 21.25V3.748zm15.754 0.75H4.5v10.503h14.254l-3.602-4.802c-0.2-0.266-0.2-0.633 0-0.9l3.602-4.8z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!--~ Copyright (c) 2022. ~ Microsoft Corporation. All rights reserved.-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_fluent_flag_24_filled" android:state_activated="true"/>
<item android:drawable="@drawable/ic_fluent_flag_24_filled" android:state_checked="true"/>
<item android:drawable="@drawable/ic_fluent_flag_24_filled" android:state_selected="true"/>
<item android:drawable="@drawable/ic_fluent_flag_24_regular"/>
</selector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M17.5 12c3.037 0 5.5 2.463 5.5 5.5 0 3.038-2.463 5.5-5.5 5.5-3.038 0-5.5-2.462-5.5-5.5 0-3.037 2.462-5.5 5.5-5.5zm-5.478 2C11.375 15.01 11 16.21 11 17.5c0 1.644 0.61 3.146 1.617 4.29-0.802 0.142-1.675 0.211-2.617 0.211-2.89 0-5.128-0.656-6.691-2-0.829-0.712-1.306-1.75-1.306-2.844V16.25c0-1.242 1.008-2.25 2.25-2.25h7.77zm3.07 0.966l-0.068 0.058-0.058 0.07c-0.118 0.17-0.118 0.398 0 0.568l0.058 0.07 1.77 1.769-1.768 1.767-0.058 0.069c-0.118 0.17-0.118 0.398 0 0.569l0.058 0.069 0.07 0.058c0.17 0.118 0.398 0.118 0.568 0l0.07-0.058 1.766-1.767 1.77 1.77 0.069 0.057c0.17 0.118 0.398 0.118 0.568 0l0.07-0.058 0.057-0.07c0.118-0.17 0.118-0.397 0-0.568l-0.058-0.069-1.769-1.77 1.772-1.768 0.058-0.07c0.118-0.17 0.118-0.398 0-0.568l-0.058-0.07-0.07-0.057c-0.17-0.119-0.397-0.119-0.568 0l-0.069 0.057-1.772 1.77-1.77-1.77-0.069-0.057c-0.146-0.102-0.334-0.116-0.492-0.044l-0.076 0.043zM10 2.005c2.761 0 5 2.239 5 5s-2.239 5-5 5-5-2.239-5-5 2.239-5 5-5z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -154,6 +154,38 @@
android:layout_gravity="center_horizontal"
android:visibility="gone"/>
<LinearLayout
android:id="@+id/sensitive_item"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center_vertical"
android:layoutDirection="locale"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:background="?android:selectableItemBackground"
android:visibility="gone">
<ImageView
android:id="@+id/sensitive_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="32dp"
android:src="@drawable/ic_fluent_flag_24_selector"
android:tint="?android:textColorPrimary"
android:importantForAccessibility="no"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="16sp"
android:singleLine="true"
android:text="@string/mark_media_as_sensitive" />
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -166,31 +166,46 @@
</LinearLayout>
</LinearLayout>
<FrameLayout
<LinearLayout
android:id="@+id/profile_action_btn_wrap"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_below="@id/profile_counters"
android:padding="16dp"
android:clipToPadding="false">
<org.joinmastodon.android.ui.views.ProgressBarButton
android:id="@+id/profile_action_btn"
android:layout_alignParentEnd="true"
android:clipToPadding="false"
android:padding="16dp">
<Button
android:id="@+id/notify_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Edit Profile"/>
<ProgressBar
android:id="@+id/action_progress"
android:layout_marginRight="8dp"
android:paddingHorizontal="8dp"
android:drawableStart="@drawable/ic_fluent_alert_24_selector"
style="@style/Widget.Mastodon.Button.Secondary_LightOnDark" />
<FrameLayout
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:indeterminateTint="?colorButtonText"
android:visibility="gone"/>
</FrameLayout>
android:layout_height="wrap_content">
<org.joinmastodon.android.ui.views.ProgressBarButton
android:id="@+id/profile_action_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Edit Profile" />
<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:indeterminateTint="?colorButtonText"
android:outlineProvider="none"
android:visibility="gone" />
</FrameLayout>
</LinearLayout>
<TextView
android:id="@+id/name"

View File

@@ -1,13 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
<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="48dp"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:layout_width="match_parent"
android:layout_height="48dp"
android:gravity="center_vertical"
android:textSize="16sp"
android:textColor="?android:textColorPrimary"
android:singleLine="true"
android:ellipsize="end"
tools:text="daffdsa"/>
android:layoutDirection="locale">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="48dp"
android:paddingRight="16dp"
android:gravity="center_vertical"
android:textSize="16sp"
android:textColor="?android:textColorPrimary"
android:singleLine="true"
android:ellipsize="end"
tools:text="Account settings"/>
<ProgressBar
android:id="@+id/progress"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:alpha="0"
/>
</LinearLayout>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/follow_hashtag"
android:icon="@drawable/ic_fluent_person_add_24_regular"
android:showAsAction="always"
android:title="@string/button_follow"/>
</menu>

View File

@@ -252,6 +252,9 @@
<string name="theme_dark">Dunkel</string>
<string name="theme_true_black">AMOLED-Modus</string>
<string name="settings_behavior">App-Verhalten</string>
<string name="settings_show_replies">Antworten anzeigen</string>
<string name="settings_show_boosts">Geteilte Beiträge anzeigen</string>
<string name="settings_load_new_posts">Automatisch neue Beiträge laden</string>
<string name="settings_gif">Spiele animierte GIFs, Avatare und Emojis ab</string>
<string name="settings_custom_tabs">In-App-Browser verwenden</string>
<string name="settings_notifications">Benachrichtigungen</string>
@@ -271,7 +274,7 @@
<string name="settings_privacy_policy">Datenschutzbestimmungen</string>
<string name="settings_spicy">Gefährliches</string>
<string name="settings_clear_cache">Medien-Cache leeren</string>
<string name="settings_app_version">Mastodon für Android v%1$s (%2$d)</string>
<string name="settings_app_version">Mastodos v%1$s (%2$d)</string>
<string name="media_cache_cleared">Medien-Cache geleert</string>
<string name="confirm_log_out">Bist du dir sicher, dass du dich abmelden möchtest?</string>
<string name="sensitive_content">Sensibler Inhalt</string>
@@ -288,6 +291,7 @@
<string name="button_share">Teilen</string>
<string name="media_no_description">Medien ohne Beschreibung</string>
<string name="add_media">Medien hinzufügen</string>
<string name="mark_media_as_sensitive">Medien als NSFW markieren</string>
<string name="add_poll">Umfrage hinzufügen</string>
<string name="emoji">Emoji</string>
<string name="post_visibility">Sichtbarkeit des Beitrages</string>
@@ -300,6 +304,8 @@
<string name="open_in_browser">Beitrag im Browser öffnen</string>
<string name="hide_boosts_from_user">Verberge geteilte Beiträge von %s</string>
<string name="show_boosts_from_user">Zeige geteilte Beiträge von %s</string>
<string name="user_post_notifications_on">Benachrichtigungen über neue Beiträge von %s aktiviert</string>
<string name="user_post_notifications_off">Benachrichtigungen über neue Beiträge von %s deaktiviert</string>
<string name="signup_reason">Weshalb möchtest du beitreten?</string>
<string name="signup_reason_note">Dies wird uns dabei helfen, deine Anmeldungsanfrage besser zu verarbeiten.</string>
<string name="clear">Löschen</string>
@@ -315,10 +321,12 @@
<string name="downloading">wird heruntergeladen </string>
<string name="no_app_to_handle_action">Es gibt keine App, um diese Aktion auszuführen</string>
<string name="local_timeline">Community</string>
<string name="federated_timeline">Föderation</string>
<string name="trending_posts_info_banner">Dies sind Beiträge, die auf deinem Mastodon-Server gerade angesagt sind.</string>
<string name="trending_hashtags_info_banner">Dies sind Hashtags, die auf deinem Mastodon-Server gerade angesagt sind.</string>
<string name="trending_links_info_banner">Dies sind journalistische Nachrichten, die auf deinem Mastodon-Server gerade am häufigsten geteilt werden.</string>
<string name="local_timeline_info_banner">Dies sind die neuesten Beiträge der Leute, die den gleichen Mastodon-Server verwenden wie du.</string>
<string name="federated_timeline_info_banner">Dies sind die neusten Beiträge der Leute, die in der Föderation deines Servers sind.</string>
<string name="dismiss">Verwerfen</string>
<string name="see_new_posts">Neue Beiträge anzeigen</string>
<string name="load_missing_posts">Weitere Beiträge laden</string>
@@ -387,10 +395,12 @@
<string name="upload_error_connection_lost">Dein Gerät hat gerade keinen Zugang zum Internet</string>
<string name="upload_processing">wird verarbeitet </string>
<!-- %s is version like 1.2.3 -->
<string name="update_available">Mastodon für Android %s kann nun heruntergeladen werden.</string>
<string name="update_available">Mastodos %s kann nun heruntergeladen werden.</string>
<!-- %s is version like 1.2.3 -->
<string name="update_ready">Mastodon für Android %s wurde heruntergeladen und kann nun installiert werden.</string>
<string name="update_ready">Mastodos %s wurde heruntergeladen und kann nun installiert werden.</string>
<!-- %s is file size -->
<string name="download_update">Download (%s)</string>
<string name="install_update">Installieren</string>
<string name="check_for_update">Auf Update prüfen</string>
<string name="no_update_available">Kein Update verfügbar</string>
</resources>

View File

@@ -256,6 +256,9 @@
<string name="theme_dark">Dark</string>
<string name="theme_true_black">True black mode</string>
<string name="settings_behavior">Behavior</string>
<string name="settings_show_replies">Show replies</string>
<string name="settings_show_boosts">Show boosts</string>
<string name="settings_load_new_posts">Automatically load new posts</string>
<string name="settings_gif">Play animated avatars and emoji</string>
<string name="settings_custom_tabs">Use in-app browser</string>
<string name="settings_notifications">Notifications</string>
@@ -275,7 +278,7 @@
<string name="settings_privacy_policy">Privacy policy</string>
<string name="settings_spicy">The spicy zone</string>
<string name="settings_clear_cache">Clear media cache</string>
<string name="settings_app_version">Mastodon for Android v%1$s (%2$d)</string>
<string name="settings_app_version">Mastodos v%1$s (%2$d)</string>
<string name="media_cache_cleared">Media cache cleared</string>
<string name="confirm_log_out">Are you sure you want to sign out?</string>
<string name="sensitive_content">Sensitive content</string>
@@ -294,6 +297,7 @@
<string name="bookmarks">Bookmarks</string>
<string name="media_no_description">Media without description</string>
<string name="add_media">Add media</string>
<string name="mark_media_as_sensitive">Mark media as sensitive</string>
<string name="add_poll">Add a poll</string>
<string name="emoji">Emoji</string>
<string name="post_visibility">Post visibility</string>
@@ -306,6 +310,8 @@
<string name="open_in_browser">Open in browser</string>
<string name="hide_boosts_from_user">Hide reblogs from %s</string>
<string name="show_boosts_from_user">Show reblogs from %s</string>
<string name="user_post_notifications_on">Notifying about posts by %s</string>
<string name="user_post_notifications_off">No longer notifying about posts by %s</string>
<string name="signup_reason">why do you want to join?</string>
<string name="signup_reason_note">This will help us review your application.</string>
<string name="clear">Clear</string>
@@ -396,12 +402,14 @@
<string name="upload_error_connection_lost">Your device lost connection to the internet</string>
<string name="upload_processing">Processing…</string>
<!-- %s is version like 1.2.3 -->
<string name="update_available">Mastodon for Android %s is ready to download.</string>
<string name="update_available">Mastodos %s is ready to download.</string>
<!-- %s is version like 1.2.3 -->
<string name="update_ready">Mastodon for Android %s is downloaded and ready to install.</string>
<string name="update_ready">Mastodos %s is downloaded and ready to install.</string>
<!-- %s is file size -->
<string name="download_update">Download (%s)</string>
<string name="install_update">Install</string>
<string name="check_for_update">Check for update</string>
<string name="no_update_available">No update available</string>
<string name="privacy_policy_title">Mastodon and your privacy</string>
<string name="privacy_policy_subtitle">Although the Mastodon app does not collect any data, the server you sign up through may have a different policy. Take a minute to review and agree to the Mastodon app privacy policy and your server\'s privacy policy.</string>
<string name="i_agree">I Agree</string>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en" />
<locale android:name="ar" />
<locale android:name="bs" />
<locale android:name="ca" />
<locale android:name="cs" />
<locale android:name="de" />
<locale android:name="el" />
<locale android:name="es" />
<locale android:name="eu" />
<locale android:name="fi" />
<locale android:name="fr" />
<locale android:name="gl" />
<locale android:name="hr" />
<locale android:name="hy" />
<locale android:name="it" />
<locale android:name="iw" />
<locale android:name="ja" />
<locale android:name="kab" />
<locale android:name="ko" />
<locale android:name="oc" />
<locale android:name="pl" />
<locale android:name="pt-BR" />
<locale android:name="pt-PT" />
<locale android:name="ru" />
<locale android:name="sv" />
<locale android:name="th" />
<locale android:name="tr" />
<locale android:name="uk" />
<locale android:name="vi" />
<locale android:name="zh-Hans" />
<locale android:name="zh-Hant" />
</locale-config>