Compare commits
61 Commits
v1.1.4+for
...
v1.1.4+for
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
764ab60fea | ||
|
|
9b13bdb06f | ||
|
|
9283ca8878 | ||
|
|
679ede1124 | ||
|
|
f4b1bde8c5 | ||
|
|
eff6cc3e17 | ||
|
|
03044b86b1 | ||
|
|
90ff88f02b | ||
|
|
e1f378977a | ||
|
|
a0f3d2862c | ||
|
|
0c64145368 | ||
|
|
a1474fb461 | ||
|
|
965ebc8669 | ||
|
|
3514152439 | ||
|
|
1b1dc7085e | ||
|
|
94d004724f | ||
|
|
e9f5d235cb | ||
|
|
9692afb323 | ||
|
|
3ae37a700b | ||
|
|
b166ca705e | ||
|
|
e6a67543f4 | ||
|
|
7d38f031f1 | ||
|
|
cf864e6f49 | ||
|
|
f4b1629a1d | ||
|
|
1fbb97021e | ||
|
|
229c815a59 | ||
|
|
ee8d78637d | ||
|
|
4ede842171 | ||
|
|
8306e78ce3 | ||
|
|
65d093ee9d | ||
|
|
aa48233a75 | ||
|
|
ae50e618c0 | ||
|
|
defa8b014e | ||
|
|
159d91f390 | ||
|
|
430d4ec93b | ||
|
|
ae64784daf | ||
|
|
18df7c32a4 | ||
|
|
4615612a65 | ||
|
|
6b226cdcad | ||
|
|
1776709b38 | ||
|
|
75a35cd680 | ||
|
|
18d4f2fa36 | ||
|
|
bca936f6a2 | ||
|
|
0b59379c4e | ||
|
|
58abdad62a | ||
|
|
c693414cc3 | ||
|
|
ee158e1aba | ||
|
|
40d478aaec | ||
|
|
e7fb96b3ff | ||
|
|
ef75427b45 | ||
|
|
2ff4f00774 | ||
|
|
534fd8c119 | ||
|
|
2d2cd89454 | ||
|
|
9f3f5ca7c1 | ||
|
|
c0e6f17c83 | ||
|
|
677f1cb42d | ||
|
|
1ac6a04a46 | ||
|
|
21927d2e25 | ||
|
|
c35441f5f7 | ||
|
|
3718fe6601 | ||
|
|
21a526dda9 |
32
README.md
32
README.md
@@ -10,19 +10,33 @@
|
||||
|
||||
### Features
|
||||
|
||||
* [Add “Unlisted” as a post visibility option](https://github.com/sk22/mastodon-android-fork/tree/feature/enable-unlisted)
|
||||
* [Add “Unlisted” as a post visibility option](https://github.com/sk22/mastodon-android-fork/commits/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 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))
|
||||
* [Add “Federation” tab and change Discover tab order](https://github.com/sk22/mastodon-android-fork/commits/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/commits/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/commits/feature/pin-posts) ([Pull request](https://github.com/mastodon/mastodon-android/pull/140))
|
||||
* [Implement deleting and re-drafting](https://github.com/sk22/mastodon-android-fork/commits/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/commits/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/commits/feature/mark-media-as-sensitive)
|
||||
* [Add settings to hide replies and reposts from the timeline](https://github.com/sk22/mastodon-android-fork/commits/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))
|
||||
* [Lists view (viewing only, for now)](https://github.com/sk22/mastodon-android-fork/commits/list-timeline-views) based on [@obstsalatschuessel](https://github.com/obstsalatschuessel)'s [Pull request](https://github.com/mastodon/mastodon-android/pull/286)
|
||||
|
||||
### Behavior
|
||||
|
||||
* [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))
|
||||
* [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))
|
||||
* [Make back button return to the home tab before exiting the app](https://github.com/sk22/mastodon-android-fork/commits/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/commits/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/commits/feature/compose-image-description-full-image) ([Pull request](https://github.com/mastodon/mastodon-android/pull/182))
|
||||
* [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))
|
||||
* [Custom extended footer redesign](https://github.com/sk22/mastodon-android-fork/commits/compact-extended-footer)
|
||||
|
||||
### 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
|
||||
|
||||
|
||||
@@ -9,9 +9,13 @@ android {
|
||||
applicationId "org.joinmastodon.android.sk"
|
||||
minSdk 23
|
||||
targetSdk 33
|
||||
versionCode 29
|
||||
versionName "1.1.4+fork.29"
|
||||
versionCode 35
|
||||
versionName "1.1.4+fork.35"
|
||||
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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.joinmastodon.android.api.requests.lists;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.ListTimeline;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GetLists extends MastodonAPIRequest<List<ListTimeline>>{
|
||||
public GetLists() {
|
||||
super(HttpMethod.GET, "/lists", new TypeToken<>(){});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.joinmastodon.android.api.requests.timelines;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GetListTimeline extends MastodonAPIRequest<List<Status>> {
|
||||
public GetListTimeline(String listID, String maxID, String minID, int limit, String sinceID) {
|
||||
super(HttpMethod.GET, "/timelines/list/"+listID, new TypeToken<>(){});
|
||||
if(maxID!=null)
|
||||
addQueryParameter("max_id", maxID);
|
||||
if(minID!=null)
|
||||
addQueryParameter("min_id", minID);
|
||||
if(limit>0)
|
||||
addQueryParameter("limit", ""+limit);
|
||||
if(sinceID!=null)
|
||||
addQueryParameter("since_id", sinceID);
|
||||
}
|
||||
}
|
||||
@@ -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,6 +183,7 @@ 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;
|
||||
@@ -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,6 +581,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
}
|
||||
}
|
||||
|
||||
updateSensitive();
|
||||
|
||||
if(editingStatus!=null){
|
||||
updateCharCounter();
|
||||
visibilityBtn.setEnabled(false);
|
||||
@@ -678,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());
|
||||
}
|
||||
@@ -884,6 +900,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
uploadNextQueuedAttachment();
|
||||
}
|
||||
updatePublishButtonState();
|
||||
updateSensitive();
|
||||
if(getMediaAttachmentsCount()==MAX_ATTACHMENTS)
|
||||
mediaBtn.setEnabled(false);
|
||||
return true;
|
||||
@@ -1058,6 +1075,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
updatePublishButtonState();
|
||||
pollBtn.setEnabled(attachments.isEmpty());
|
||||
mediaBtn.setEnabled(true);
|
||||
updateSensitive();
|
||||
}
|
||||
|
||||
private void onRetryOrCancelMediaUploadClick(View v){
|
||||
@@ -1259,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(){
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.media.MediaRouter;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.timelines.GetListTimeline;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
|
||||
public class ListTimelineFragment extends StatusListFragment {
|
||||
private String listID;
|
||||
private String listTitle;
|
||||
private ImageButton fab;
|
||||
|
||||
public ListTimelineFragment() {
|
||||
setListLayoutId(R.layout.recycler_fragment_with_fab);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity){
|
||||
super.onAttach(activity);
|
||||
listID=getArguments().getString("listID");
|
||||
listTitle=getArguments().getString("listTitle");
|
||||
setTitle(listTitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count) {
|
||||
currentRequest=new GetListTimeline(listID, offset==0 ? null : getMaxID(), null, count, null)
|
||||
.setCallback(new SimpleCallback<>(this) {
|
||||
@Override
|
||||
public void onSuccess(List<Status> result) {
|
||||
onDataLoaded(result, !result.isEmpty());
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShown() {
|
||||
super.onShown();
|
||||
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
|
||||
loadData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
fab=view.findViewById(R.id.fab);
|
||||
fab.setOnClickListener(this::onFabClick);
|
||||
}
|
||||
|
||||
private void onFabClick(View v){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putString("prefilledText", listID+' ');
|
||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetFabBottomInset(int inset) {
|
||||
((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(24)+inset;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.lists.GetLists;
|
||||
import org.joinmastodon.android.fragments.ScrollableToTop;
|
||||
import org.joinmastodon.android.model.ListTimeline;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public class ListTimelinesFragment extends BaseRecyclerFragment<ListTimeline> implements ScrollableToTop {
|
||||
private String accountId;
|
||||
|
||||
public ListTimelinesFragment() {
|
||||
super(10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
accountId=getArguments().getString("account");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){
|
||||
currentRequest=new GetLists()
|
||||
.setCallback(new SimpleCallback<>(this) {
|
||||
@Override
|
||||
public void onSuccess(List<ListTimeline> result) {
|
||||
onDataLoaded(result, false);
|
||||
}
|
||||
})
|
||||
.exec(accountId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecyclerView.Adapter getAdapter() {
|
||||
return new ListsAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollToTop() {
|
||||
smoothScrollRecyclerViewToTop(list);
|
||||
}
|
||||
|
||||
private class ListsAdapter extends RecyclerView.Adapter<ListViewHolder>{
|
||||
@NonNull
|
||||
@Override
|
||||
public ListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
return new ListViewHolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ListViewHolder holder, int position) {
|
||||
holder.bind(data.get(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return data.size();
|
||||
}
|
||||
}
|
||||
|
||||
private class ListViewHolder extends BindableViewHolder<ListTimeline> implements UsableRecyclerView.Clickable{
|
||||
private final TextView title;
|
||||
|
||||
public ListViewHolder(){
|
||||
super(getActivity(), R.layout.item_list_timeline, list);
|
||||
title=findViewById(R.id.title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(ListTimeline item) {
|
||||
title.setText(item.title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
UiUtils.openListTimeline(getActivity(), accountId, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
private CoverImageView cover;
|
||||
private View avatarBorder;
|
||||
private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel, postsCount, postsLabel;
|
||||
private ProgressBarButton actionButton;
|
||||
private ProgressBarButton actionButton, notifyButton;
|
||||
private ViewPager2 pager;
|
||||
private NestedRecyclerScrollView scrollView;
|
||||
private AccountTimelineFragment postsFragment, postsWithRepliesFragment, pinnedPostsFragment, mediaFragment;
|
||||
@@ -109,7 +109,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
private float titleTransY;
|
||||
private View postsBtn, followersBtn, followingBtn;
|
||||
private EditText nameEdit, bioEdit;
|
||||
private ProgressBar actionProgress;
|
||||
private ProgressBar actionProgress, notifyProgress;
|
||||
private FrameLayout[] tabViews;
|
||||
private TabLayoutMediator tabLayoutMediator;
|
||||
private TextView followsYouView;
|
||||
@@ -181,6 +181,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);
|
||||
@@ -188,6 +189,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
nameEdit=content.findViewById(R.id.name_edit);
|
||||
bioEdit=content.findViewById(R.id.bio_edit);
|
||||
actionProgress=content.findViewById(R.id.action_progress);
|
||||
notifyProgress=content.findViewById(R.id.notify_progress);
|
||||
fab=content.findViewById(R.id.fab);
|
||||
followsYouView=content.findViewById(R.id.follows_you);
|
||||
|
||||
@@ -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);
|
||||
@@ -458,6 +461,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
UiUtils.loadCustomEmojiInTextView(name);
|
||||
UiUtils.loadCustomEmojiInTextView(bio);
|
||||
|
||||
notifyButton.setVisibility(View.GONE);
|
||||
if(AccountSessionManager.getInstance().isSelf(accountID, account)){
|
||||
actionButton.setText(R.string.edit_profile);
|
||||
}else{
|
||||
@@ -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){
|
||||
@@ -622,9 +626,14 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
private void updateRelationship(){
|
||||
invalidateOptionsMenu();
|
||||
actionButton.setVisibility(View.VISIBLE);
|
||||
notifyButton.setVisibility(relationship.following ? View.VISIBLE : View.GONE);
|
||||
UiUtils.setRelationshipToActionButton(relationship, actionButton);
|
||||
UiUtils.setRelationshipToActionButton(relationship, notifyButton, true);
|
||||
actionProgress.setIndeterminateTintList(actionButton.getTextColors());
|
||||
notifyProgress.setIndeterminateTintList(notifyButton.getTextColors());
|
||||
followsYouView.setVisibility(relationship.followedBy ? View.VISIBLE : View.GONE);
|
||||
notifyButton.setSelected(relationship.notifying);
|
||||
notifyButton.setContentDescription(getString(relationship.notifying ? R.string.user_post_notifications_on : R.string.user_post_notifications_off, '@'+account.username));
|
||||
}
|
||||
|
||||
private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){
|
||||
@@ -691,6 +700,12 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
actionButton.setClickable(!visible);
|
||||
}
|
||||
|
||||
private void setNotifyProgressVisible(boolean visible){
|
||||
notifyButton.setTextVisible(!visible);
|
||||
notifyProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
notifyButton.setClickable(!visible);
|
||||
}
|
||||
|
||||
private void loadAccountInfoAndEnterEditMode(){
|
||||
if(editModeLoading)
|
||||
return;
|
||||
@@ -859,6 +874,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::setNotifyProgressVisible, this::updateRelationship);
|
||||
}
|
||||
|
||||
private void onAvatarClick(View v){
|
||||
if(isInEditMode){
|
||||
startImagePicker(AVATAR_RESULT);
|
||||
|
||||
@@ -105,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();
|
||||
@@ -115,7 +129,6 @@ 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")));
|
||||
|
||||
@@ -124,6 +137,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
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));
|
||||
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -19,6 +19,7 @@ import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.fragments.ScrollableToTop;
|
||||
import org.joinmastodon.android.fragments.ListTimelinesFragment;
|
||||
import org.joinmastodon.android.ui.SimpleViewHolder;
|
||||
import org.joinmastodon.android.ui.tabs.TabLayout;
|
||||
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
|
||||
@@ -52,6 +53,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
private SearchFragment searchFragment;
|
||||
private LocalTimelineFragment localTimelineFragment;
|
||||
private FederatedTimelineFragment federatedTimelineFragment;
|
||||
private ListTimelinesFragment listTimelinesFragment;
|
||||
|
||||
private String accountID;
|
||||
private Runnable searchDebouncer=this::onSearchChangedDebounced;
|
||||
@@ -73,7 +75,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
tabLayout=view.findViewById(R.id.tabbar);
|
||||
pager=view.findViewById(R.id.pager);
|
||||
|
||||
tabViews=new FrameLayout[6];
|
||||
tabViews=new FrameLayout[7];
|
||||
for(int i=0;i<tabViews.length;i++){
|
||||
FrameLayout tabView=new FrameLayout(getActivity());
|
||||
tabView.setId(switch(i){
|
||||
@@ -83,6 +85,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
case 3 -> R.id.discover_posts;
|
||||
case 4 -> R.id.discover_news;
|
||||
case 5 -> R.id.discover_users;
|
||||
case 6 -> R.id.discover_lists;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+i);
|
||||
});
|
||||
tabView.setVisibility(View.GONE);
|
||||
@@ -131,6 +134,9 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
federatedTimelineFragment=new FederatedTimelineFragment();
|
||||
federatedTimelineFragment.setArguments(args);
|
||||
|
||||
listTimelinesFragment=new ListTimelinesFragment();
|
||||
listTimelinesFragment.setArguments(args);
|
||||
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.add(R.id.discover_posts, postsFragment)
|
||||
.add(R.id.discover_local_timeline, localTimelineFragment)
|
||||
@@ -138,6 +144,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
.add(R.id.discover_hashtags, hashtagsFragment)
|
||||
.add(R.id.discover_news, newsFragment)
|
||||
.add(R.id.discover_users, accountsFragment)
|
||||
.add(R.id.discover_lists, listTimelinesFragment)
|
||||
.commit();
|
||||
}
|
||||
|
||||
@@ -151,6 +158,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
case 3 -> R.string.posts;
|
||||
case 4 -> R.string.news;
|
||||
case 5 -> R.string.for_you;
|
||||
case 6 -> R.string.list_timelines;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+position);
|
||||
});
|
||||
tab.view.textView.setAllCaps(true);
|
||||
@@ -279,6 +287,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
case 3 -> postsFragment;
|
||||
case 4 -> newsFragment;
|
||||
case 5 -> accountsFragment;
|
||||
case 6 -> listTimelinesFragment;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+page);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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+
|
||||
'}';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
@Parcel
|
||||
public class ListTimeline extends BaseModel {
|
||||
@RequiredField
|
||||
public String id;
|
||||
@RequiredField
|
||||
public String title;
|
||||
@RequiredField
|
||||
public RepliesPolicy repliesPolicy;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "List{" +
|
||||
"id='" + id + '\'' +
|
||||
", title='" + title + '\'' +
|
||||
", repliesPolicy=" + repliesPolicy +
|
||||
'}';
|
||||
}
|
||||
|
||||
public enum RepliesPolicy{
|
||||
@SerializedName("followed")
|
||||
FOLLOWED,
|
||||
@SerializedName("list")
|
||||
LIST,
|
||||
@SerializedName("none")
|
||||
NONE
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.TypefaceSpan;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
@@ -45,18 +47,20 @@ public class ExtendedFooterStatusDisplayItem extends StatusDisplayItem{
|
||||
}
|
||||
|
||||
public static class Holder extends StatusDisplayItem.Holder<ExtendedFooterStatusDisplayItem>{
|
||||
private final TextView time, favoritesCount, reblogsCount, lastEditTime;
|
||||
private final View favorites, reblogs, editHistory;
|
||||
private final TextView time;
|
||||
private final Button favorites, reblogs, editHistory, applicationName;
|
||||
private final ImageView visibility;
|
||||
private final Context context;
|
||||
|
||||
public Holder(Context context, ViewGroup parent){
|
||||
super(context, R.layout.display_item_extended_footer, parent);
|
||||
this.context = context;
|
||||
reblogs=findViewById(R.id.reblogs);
|
||||
favorites=findViewById(R.id.favorites);
|
||||
editHistory=findViewById(R.id.edit_history);
|
||||
applicationName=findViewById(R.id.application_name);
|
||||
visibility=findViewById(R.id.visibility);
|
||||
time=findViewById(R.id.timestamp);
|
||||
favoritesCount=findViewById(R.id.favorites_count);
|
||||
reblogsCount=findViewById(R.id.reblogs_count);
|
||||
lastEditTime=findViewById(R.id.last_edited);
|
||||
|
||||
reblogs.setOnClickListener(v->startAccountListFragment(StatusReblogsListFragment.class));
|
||||
favorites.setOnClickListener(v->startAccountListFragment(StatusFavoritesListFragment.class));
|
||||
@@ -67,20 +71,35 @@ public class ExtendedFooterStatusDisplayItem extends StatusDisplayItem{
|
||||
@Override
|
||||
public void onBind(ExtendedFooterStatusDisplayItem item){
|
||||
Status s=item.status;
|
||||
favoritesCount.setText(String.format("%,d", s.favouritesCount));
|
||||
reblogsCount.setText(String.format("%,d", s.reblogsCount));
|
||||
favorites.setText(context.getResources().getQuantityString(R.plurals.x_favorites, (int)(s.favouritesCount%1000), s.favouritesCount));
|
||||
reblogs.setText(context.getResources().getQuantityString(R.plurals.x_reblogs, (int)(s.reblogsCount%1000), s.reblogsCount));
|
||||
if(s.editedAt!=null){
|
||||
editHistory.setVisibility(View.VISIBLE);
|
||||
lastEditTime.setText(item.parentFragment.getString(R.string.last_edit_at_x, UiUtils.formatRelativeTimestampAsMinutesAgo(itemView.getContext(), s.editedAt)));
|
||||
editHistory.setText(UiUtils.formatRelativeTimestampAsMinutesAgo(itemView.getContext(), s.editedAt));
|
||||
}else{
|
||||
editHistory.setVisibility(View.GONE);
|
||||
}
|
||||
String timeStr=TIME_FORMATTER.format(item.status.createdAt.atZone(ZoneId.systemDefault()));
|
||||
if(item.status.application!=null && !TextUtils.isEmpty(item.status.application.name)){
|
||||
time.setText(item.parentFragment.getString(R.string.timestamp_via_app, timeStr, item.status.application.name));
|
||||
}else{
|
||||
|
||||
if (item.status.application!=null && !TextUtils.isEmpty(item.status.application.name)) {
|
||||
time.setText(item.parentFragment.getString(R.string.timestamp_via_app, timeStr, ""));
|
||||
applicationName.setText(item.status.application.name);
|
||||
if (item.status.application.website != null && item.status.application.website.toLowerCase().startsWith("https://")) {
|
||||
applicationName.setOnClickListener(e -> UiUtils.openURL(context, null, item.status.application.website));
|
||||
} else {
|
||||
applicationName.setEnabled(false);
|
||||
}
|
||||
} else {
|
||||
time.setText(timeStr);
|
||||
applicationName.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
visibility.setImageResource(switch (s.visibility) {
|
||||
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
|
||||
case UNLISTED -> R.drawable.ic_fluent_people_20_regular;
|
||||
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_20_regular;
|
||||
case DIRECT -> R.drawable.ic_at_symbol;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -158,7 +158,7 @@ public abstract class StatusDisplayItem{
|
||||
}
|
||||
|
||||
public Holder(Context context, int layout, ViewGroup parent){
|
||||
super(context, layout, parent);
|
||||
super(context, layout, parent);
|
||||
}
|
||||
|
||||
public String getItemID(){
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,10 +50,12 @@ import org.joinmastodon.android.events.StatusUnpinnedEvent;
|
||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
||||
import org.joinmastodon.android.fragments.ListTimelineFragment;
|
||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||
import org.joinmastodon.android.fragments.ThreadFragment;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Emoji;
|
||||
import org.joinmastodon.android.model.ListTimeline;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.StatusPrivacy;
|
||||
@@ -80,6 +82,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;
|
||||
@@ -309,13 +312,22 @@ 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);
|
||||
}
|
||||
|
||||
public static void openListTimeline(Context context, String accountID, ListTimeline list){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putString("listID", list.id);
|
||||
args.putString("listTitle", list.title);
|
||||
Nav.go((Activity)context, ListTimelineFragment.class, args);
|
||||
}
|
||||
|
||||
public static void showConfirmationAlert(Context context, @StringRes int title, @StringRes int message, @StringRes int confirmButton, Runnable onConfirmed){
|
||||
showConfirmationAlert(context, context.getString(title), context.getString(message), context.getString(confirmButton), onConfirmed);
|
||||
}
|
||||
@@ -496,6 +508,11 @@ public class UiUtils{
|
||||
}
|
||||
|
||||
public static void setRelationshipToActionButton(Relationship relationship, Button button){
|
||||
setRelationshipToActionButton(relationship, button, false);
|
||||
}
|
||||
|
||||
public static void setRelationshipToActionButton(Relationship relationship, Button button, boolean keepText){
|
||||
CharSequence textBefore = keepText ? button.getText() : null;
|
||||
boolean secondaryStyle;
|
||||
if(relationship.blocking){
|
||||
button.setText(R.string.button_blocked);
|
||||
@@ -514,6 +531,8 @@ public class UiUtils{
|
||||
secondaryStyle=true;
|
||||
}
|
||||
|
||||
if (keepText) button.setText(textBefore);
|
||||
|
||||
button.setEnabled(!relationship.blockedBy);
|
||||
int attr=secondaryStyle ? R.attr.secondaryButtonStyle : android.R.attr.buttonStyle;
|
||||
TypedArray ta=button.getContext().obtainStyledAttributes(new int[]{attr});
|
||||
@@ -530,6 +549,25 @@ public class UiUtils{
|
||||
ta.recycle();
|
||||
}
|
||||
|
||||
public static void performToggleAccountNotifications(Activity activity, Account account, String accountID, Relationship relationship, Button button, Consumer<Boolean> progressCallback, Consumer<Relationship> resultCallback) {
|
||||
progressCallback.accept(true);
|
||||
new SetAccountFollowed(account.id, true, relationship.showingReblogs, !relationship.notifying)
|
||||
.setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(Relationship result) {
|
||||
resultCallback.accept(result);
|
||||
progressCallback.accept(false);
|
||||
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) {
|
||||
progressCallback.accept(false);
|
||||
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);
|
||||
@@ -537,7 +575,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){
|
||||
@@ -643,10 +681,10 @@ public class UiUtils{
|
||||
return GlobalUserPreferences.theme==GlobalUserPreferences.ThemePreference.DARK;
|
||||
}
|
||||
|
||||
public static void openURL(Context context, String accountID, String url){
|
||||
public static void openURL(Context context, @Nullable String accountID, String url){
|
||||
Uri uri=Uri.parse(url);
|
||||
String accountDomain=AccountSessionManager.getInstance().getAccount(accountID).domain;
|
||||
if("https".equals(uri.getScheme()) && accountDomain.equalsIgnoreCase(uri.getAuthority())){
|
||||
String accountDomain=accountID != null ? AccountSessionManager.getInstance().getAccount(accountID).domain : null;
|
||||
if(accountDomain!=null && "https".equals(uri.getScheme()) && accountDomain.equalsIgnoreCase(uri.getAuthority())){
|
||||
List<String> path=uri.getPathSegments();
|
||||
// Match URLs like https://mastodon.social/@Gargron/108132679274083591
|
||||
if(path.size()==2 && path.get(0).matches("^@[a-zA-Z0-9_]+$") && path.get(1).matches("^[0-9]+$")){
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
|
||||
<path android:pathData="M16.5 6.671c0.116 0 0.223 0.04 0.308 0.106l0.067 0.064 0.017 0.02C17.585 7.719 18 8.81 18 10c0 2.689-2.122 4.882-4.783 4.995L13 15H7c-0.102 0-0.203-0.003-0.303-0.009l1.657 1.655c0.173 0.174 0.192 0.443 0.057 0.638l-0.057 0.07C8.18 17.527 7.91 17.546 7.716 17.41l-0.07-0.057-2.5-2.5C4.973 14.68 4.954 14.41 5.09 14.216l0.057-0.07 2.5-2.5c0.196-0.195 0.512-0.195 0.708 0 0.173 0.174 0.192 0.443 0.057 0.638l-0.057 0.07-1.637 1.636 0.14 0.008L7 14h6c2.21 0 4-1.79 4-4 0-0.954-0.334-1.829-0.89-2.516C16.04 7.399 16 7.29 16 7.17c0-0.276 0.224-0.5 0.5-0.5zm-4.854-4.024c0.174-0.174 0.443-0.193 0.638-0.058l0.07 0.058 2.5 2.5 0.057 0.069c0.119 0.17 0.119 0.398 0 0.568l-0.057 0.07-2.5 2.5-0.07 0.057c-0.17 0.119-0.398 0.119-0.568 0l-0.07-0.057-0.057-0.07c-0.119-0.17-0.119-0.398 0-0.568l0.057-0.07 1.637-1.636-0.14-0.007L13 6H7c-2.21 0-4 1.79-4 4 0 0.956 0.336 1.834 0.895 2.522C3.96 12.606 4 12.714 4 12.832c0 0.275-0.224 0.5-0.5 0.5-0.167 0-0.315-0.083-0.406-0.208C2.41 12.268 2 11.182 2 10c0-2.689 2.122-4.882 4.783-4.995L7 5h6c0.102 0 0.203 0.003 0.303 0.009l-1.657-1.656-0.057-0.069c-0.135-0.195-0.116-0.464 0.057-0.637z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
|
||||
<path android:pathData="M2 10c0-4.418 3.582-8 8-8 4.419 0 8 3.582 8 8 0 4.419-3.581 8-8 8-4.418 0-8-3.581-8-8zm8-7C9.915 3 9.83 3.002 9.745 3.005c0.118 0.222 0.253 0.504 0.373 0.823 0.28 0.746 0.527 1.817 0.085 2.758C9.799 7.446 9.106 7.67 8.57 7.808L8.474 7.833c-0.506 0.13-0.755 0.194-0.93 0.46-0.17 0.257-0.129 0.574 0.037 1.113L7.619 9.53c0.067 0.211 0.144 0.457 0.184 0.688 0.05 0.286 0.06 0.636-0.113 0.97C7.51 11.53 7.276 11.762 7 11.912c-0.26 0.142-0.533 0.197-0.747 0.235l-0.088 0.015c-0.407 0.072-0.645 0.113-0.867 0.351-0.177 0.19-0.279 0.508-0.336 0.941-0.024 0.178-0.038 0.355-0.053 0.534l-0.007 0.095c-0.017 0.199-0.037 0.419-0.079 0.605l-0.005 0.02C6.1 16.116 7.947 17 10 17c1.35 0 2.612-0.383 3.682-1.045-0.086-0.086-0.181-0.189-0.275-0.307-0.271-0.34-0.609-0.909-0.492-1.57 0.056-0.313 0.226-0.581 0.397-0.794 0.175-0.216 0.386-0.417 0.576-0.592l0.128-0.117c0.146-0.133 0.273-0.25 0.382-0.363 0.147-0.154 0.191-0.237 0.2-0.263 0.068-0.226-0.013-0.404-0.126-0.492-0.094-0.073-0.295-0.142-0.61 0.058-0.12 0.075-0.228 0.141-0.323 0.191-0.086 0.045-0.205 0.102-0.336 0.122-0.157 0.025-0.375 0.002-0.544-0.177-0.129-0.136-0.164-0.302-0.178-0.375-0.016-0.09-0.024-0.19-0.03-0.276l-0.005-0.066c-0.006-0.074-0.011-0.15-0.02-0.238-0.02-0.221-0.057-0.496-0.143-0.825-0.127-0.491-0.44-0.888-0.764-1.3L11.377 8.39c-0.16-0.206-0.363-0.478-0.436-0.77-0.042-0.163-0.049-0.353 0.024-0.547 0.072-0.19 0.203-0.336 0.352-0.448 0.428-0.32 1.128-1.013 1.743-1.652 0.303-0.314 0.576-0.607 0.775-0.822l0.005-0.006C12.738 3.422 11.418 3 10 3zm4.638 1.757L14.569 4.83c-0.201 0.218-0.48 0.516-0.788 0.836-0.602 0.626-1.352 1.373-1.855 1.753 0.03 0.066 0.1 0.176 0.242 0.359l0.124 0.157c0.316 0.398 0.774 0.973 0.959 1.684 0.103 0.395 0.147 0.725 0.171 0.984l0.001 0.01c0.588-0.33 1.21-0.296 1.66 0.053 0.459 0.354 0.653 0.971 0.472 1.572-0.081 0.268-0.273 0.495-0.434 0.664-0.135 0.141-0.296 0.289-0.446 0.425-0.037 0.035-0.074 0.068-0.11 0.1-0.188 0.174-0.35 0.332-0.474 0.485-0.127 0.157-0.178 0.268-0.191 0.342-0.04 0.227 0.072 0.497 0.29 0.772 0.101 0.128 0.209 0.234 0.291 0.31l0.025 0.021C16.03 14.074 17 12.151 17 10.001c0-2.088-0.913-3.962-2.362-5.244zm-5.84-1.402c-0.053-0.096-0.1-0.173-0.133-0.228C5.437 3.75 3 6.591 3 10c0 1.283 0.345 2.486 0.947 3.52l0.023-0.198c0.063-0.467 0.193-1.059 0.597-1.491 0.462-0.495 1.026-0.588 1.404-0.65l0.108-0.019c0.203-0.036 0.336-0.07 0.443-0.128 0.093-0.05 0.19-0.133 0.28-0.309 0.03-0.054 0.048-0.147 0.016-0.336-0.028-0.16-0.08-0.327-0.146-0.536L6.625 9.7C6.472 9.203 6.25 8.438 6.709 7.742c0.4-0.607 1.04-0.762 1.477-0.869L8.32 6.84c0.467-0.12 0.772-0.242 0.978-0.68 0.261-0.556 0.143-1.292-0.116-1.98-0.124-0.33-0.27-0.618-0.384-0.825z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
|
||||
<path android:pathData="M17.181 2.926c-1.152-1.212-3.076-1.236-4.259-0.054l-9.375 9.375c-0.327 0.328-0.555 0.742-0.655 1.195l-0.878 3.95c-0.037 0.167 0.014 0.34 0.134 0.462 0.121 0.12 0.296 0.171 0.462 0.134l3.927-0.873c0.467-0.104 0.895-0.339 1.234-0.678l9.358-9.358c1.141-1.14 1.164-2.983 0.052-4.153zM13.63 3.58c0.785-0.785 2.063-0.77 2.828 0.035 0.738 0.777 0.722 2-0.035 2.757L15.75 7.043l-2.793-2.792 0.671-0.671zm-1.378 1.378l2.793 2.793-7.98 7.98c-0.204 0.204-0.462 0.345-0.744 0.408L3.16 16.84l0.708-3.182c0.059-0.267 0.193-0.512 0.387-0.705l7.996-7.996z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
|
||||
<path android:pathData="M4.5 6.75c0-1.243 1.007-2.25 2.25-2.25S9 5.507 9 6.75 7.993 9 6.75 9 4.5 7.993 4.5 6.75zM6.75 3.5C4.955 3.5 3.5 4.955 3.5 6.75S4.955 10 6.75 10 10 8.545 10 6.75 8.545 3.5 6.75 3.5zm5.687 11.645c0.538 0.22 1.215 0.355 2.063 0.355 1.881 0 2.921-0.668 3.469-1.434 0.264-0.37 0.396-0.74 0.462-1.017 0.033-0.14 0.05-0.257 0.06-0.343l0.007-0.105 0.001-0.033V12.5c0-0.828-0.671-1.5-1.5-1.5h-4.628c0.24 0.29 0.42 0.629 0.525 1H17c0.276 0 0.5 0.224 0.5 0.5v0.054l-0.005 0.05c-0.005 0.049-0.015 0.122-0.037 0.213-0.043 0.182-0.13 0.425-0.303 0.667-0.327 0.459-1.037 1.016-2.656 1.016-0.731 0-1.277-0.114-1.686-0.281-0.082 0.28-0.201 0.596-0.376 0.926zM1.5 13c0-1.105 0.895-2 2-2H10c1.105 0 2 0.895 2 2v0.084c0 0.01 0 0.023-0.002 0.04-0.001 0.033-0.004 0.08-0.01 0.135-0.01 0.113-0.032 0.268-0.075 0.453-0.085 0.368-0.254 0.86-0.595 1.354C10.617 16.08 9.263 17 6.75 17c-2.513 0-3.867-0.92-4.568-1.934-0.34-0.494-0.51-0.986-0.595-1.354-0.042-0.185-0.064-0.34-0.075-0.453-0.006-0.056-0.009-0.102-0.01-0.135L1.5 13.084V13zm1 0.06v0.018l0.007 0.083c0.007 0.076 0.023 0.189 0.054 0.326 0.064 0.277 0.191 0.644 0.444 1.01C3.492 15.201 4.513 16 6.75 16s3.258-0.799 3.745-1.503c0.253-0.366 0.38-0.733 0.444-1.01 0.031-0.137 0.047-0.25 0.054-0.326C10.997 13.123 11 13.095 11 13.078V13c0-0.552-0.448-1-1-1H3.5c-0.552 0-1 0.448-1 1v0.06zM13 7.5C13 6.672 13.672 6 14.5 6S16 6.672 16 7.5 15.328 9 14.5 9 13 8.328 13 7.5zM14.5 5C13.12 5 12 6.12 12 7.5s1.12 2.5 2.5 2.5S17 8.88 17 7.5 15.88 5 14.5 5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
|
||||
<path android:pathData="M4.5 5.75c0-1.243 1.007-2.25 2.25-2.25S9 4.507 9 5.75 7.993 8 6.75 8 4.5 6.993 4.5 5.75zM6.75 2.5C4.955 2.5 3.5 3.955 3.5 5.75S4.955 9 6.75 9 10 7.545 10 5.75 8.545 2.5 6.75 2.5zM1.5 12c0-1.105 0.895-2 2-2H10c0.361 0 0.7 0.096 0.993 0.263-0.277 0.23-0.531 0.486-0.758 0.765C10.159 11.01 10.08 11 10 11H3.5c-0.552 0-1 0.448-1 1v0.078l0.007 0.083c0.007 0.076 0.023 0.189 0.054 0.326 0.064 0.277 0.191 0.644 0.444 1.01C3.492 14.201 4.513 15 6.75 15c0.954 0 1.687-0.145 2.252-0.367 0.008 0.35 0.049 0.69 0.12 1.02C8.476 15.87 7.695 16 6.75 16c-2.513 0-3.867-0.92-4.568-1.934-0.34-0.494-0.51-0.986-0.595-1.354-0.042-0.185-0.064-0.34-0.075-0.453-0.006-0.056-0.009-0.102-0.01-0.135L1.5 12.084V12zM13 6.5C13 5.672 13.672 5 14.5 5S16 5.672 16 6.5 15.328 8 14.5 8 13 7.328 13 6.5zM14.5 4C13.12 4 12 5.12 12 6.5S13.12 9 14.5 9 17 7.88 17 6.5 15.88 4 14.5 4zM19 14.5c0 2.485-2.015 4.5-4.5 4.5S10 16.985 10 14.5s2.015-4.5 4.5-4.5 4.5 2.015 4.5 4.5zm-2.146-1.854c-0.196-0.195-0.512-0.195-0.708 0L13.5 15.293l-0.646-0.647c-0.196-0.195-0.512-0.195-0.708 0-0.195 0.196-0.195 0.512 0 0.708l1 1c0.196 0.195 0.512 0.195 0.708 0l3-3c0.195-0.196 0.195-0.512 0-0.708z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
|
||||
<path android:pathData="M9.104 2.9c0.367-0.744 1.427-0.744 1.794 0l1.93 3.91 4.317 0.628c0.82 0.12 1.148 1.127 0.554 1.706l-3.124 3.044 0.738 4.3c0.14 0.816-0.717 1.44-1.451 1.054l-3.86-2.03-3.862 2.03c-0.733 0.385-1.59-0.238-1.45-1.055l0.737-4.299-3.124-3.044C1.71 8.565 2.037 7.557 2.857 7.438l4.317-0.627 1.93-3.912zm0.897 0.442l-1.93 3.911C7.925 7.548 7.643 7.753 7.318 7.8L3 8.428l3.124 3.044c0.235 0.23 0.343 0.561 0.287 0.885l-0.737 4.3 3.86-2.03c0.292-0.153 0.64-0.153 0.931 0l3.861 2.03-0.737-4.3c-0.056-0.324 0.052-0.655 0.287-0.885L17 8.428 12.684 7.8c-0.325-0.047-0.607-0.252-0.752-0.547l-1.93-3.911z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -2,150 +2,107 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?colorBackgroundLightest">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/reblogs"
|
||||
<org.joinmastodon.android.ui.views.AutoOrientationLinearLayout
|
||||
android:id="@+id/button_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:background="?android:selectableItemBackground">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_fluent_arrow_repeat_all_24_regular"
|
||||
android:tint="?android:textColorSecondary"
|
||||
android:importantForAccessibility="no"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
<Button
|
||||
android:id="@+id/reblogs"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:minHeight="22dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/post_info_reblogs"
|
||||
android:textAppearance="@style/m3_body_large" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/reblogs_count"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/title"
|
||||
android:layout_alignStart="@id/title"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/m3_body_medium"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:textSize="14sp"
|
||||
android:minHeight="48dp"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:text="123 456"/>
|
||||
android:background="@drawable/bg_text_button"
|
||||
android:fontFamily="sans-serif"
|
||||
android:drawableStart="@drawable/ic_fluent_arrow_repeat_all_20_regular"
|
||||
android:drawablePadding="8dp"
|
||||
android:drawableTint="?android:textColorSecondary"
|
||||
tools:text="4 reblogs"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/favorites"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:background="?android:selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_fluent_star_24_regular"
|
||||
android:tint="?android:textColorSecondary"
|
||||
android:importantForAccessibility="no"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
<Button
|
||||
android:id="@+id/favorites"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:minHeight="22dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/post_info_favorites"
|
||||
android:textAppearance="@style/m3_body_large" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/favorites_count"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/title"
|
||||
android:layout_alignStart="@id/title"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/m3_body_medium"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:textSize="14sp"
|
||||
android:minHeight="48dp"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:text="123 456"/>
|
||||
android:background="@drawable/bg_text_button"
|
||||
android:fontFamily="sans-serif"
|
||||
android:drawableStart="@drawable/ic_fluent_star_20_regular"
|
||||
android:drawablePadding="8dp"
|
||||
android:drawableTint="?android:textColorSecondary"
|
||||
tools:text="12 favorites"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/edit_history"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:background="?android:selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_fluent_edit_24_regular"
|
||||
android:tint="?android:textColorSecondary"
|
||||
android:importantForAccessibility="no"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
<Button
|
||||
android:id="@+id/edit_history"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:minHeight="22dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/edit_history"
|
||||
android:textAppearance="@style/m3_body_large" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/last_edited"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/title"
|
||||
android:layout_alignStart="@id/title"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/m3_body_medium"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:textSize="14sp"
|
||||
android:minHeight="48dp"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:text="123 456"/>
|
||||
android:background="@drawable/bg_text_button"
|
||||
android:fontFamily="sans-serif"
|
||||
android:drawableStart="@drawable/ic_fluent_edit_20_regular"
|
||||
android:drawablePadding="8dp"
|
||||
android:drawableTint="?android:textColorSecondary"
|
||||
tools:text="edited"/>
|
||||
|
||||
</RelativeLayout>
|
||||
</org.joinmastodon.android.ui.views.AutoOrientationLinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timestamp"
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:gravity="start|center"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:minHeight="20dp"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:text="Dec 12, 2021, 12:42 PM via Mastodon for Android"/>
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:minHeight="48dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/visibility"
|
||||
android:layout_height="20dp"
|
||||
android:layout_width="20dp"
|
||||
android:src="@drawable/ic_fluent_earth_20_regular"
|
||||
android:tint="?android:textColorSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timestamp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:minHeight="20dp"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:text="Dec 12, 2021, 12:42 PM via "/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/application_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:padding="0dp"
|
||||
android:textSize="14sp"
|
||||
android:minHeight="48dp"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:background="@drawable/bg_text_button"
|
||||
android:fontFamily="sans-serif"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
tools:text="Mastodon for Android"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -166,31 +166,66 @@
|
||||
</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">
|
||||
|
||||
<FrameLayout
|
||||
android:clipToPadding="false"
|
||||
android:paddingVertical="16dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="4dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="Edit Profile"/>
|
||||
<ProgressBar
|
||||
android:id="@+id/action_progress"
|
||||
android:layout_height="wrap_content">
|
||||
<org.joinmastodon.android.ui.views.ProgressBarButton
|
||||
android:id="@+id/notify_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:drawableStart="@drawable/ic_fluent_alert_24_selector" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/notify_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>
|
||||
|
||||
<FrameLayout
|
||||
android:clipToPadding="false"
|
||||
android:paddingVertical="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingLeft="4dp"
|
||||
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"
|
||||
|
||||
28
mastodon/src/main/res/layout/item_list_timeline.xml
Normal file
28
mastodon/src/main/res/layout/item_list_timeline.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?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="56dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:tint="?android:textColorSecondary"
|
||||
android:src="@drawable/ic_fluent_people_community_24_regular"
|
||||
android:importantForAccessibility="no"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/m3_title_medium"
|
||||
android:fontFamily="sans-serif"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
tools:text="List"/>
|
||||
|
||||
</LinearLayout>
|
||||
8
mastodon/src/main/res/menu/hashtag_timeline.xml
Normal file
8
mastodon/src/main/res/menu/hashtag_timeline.xml
Normal 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>
|
||||
@@ -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>
|
||||
@@ -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 Beiträge von %s aktiviert</string>
|
||||
<string name="user_post_notifications_off">Benachrichtigungen über 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>
|
||||
@@ -395,4 +403,5 @@
|
||||
<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>
|
||||
<string name="list_timelines">Listen</string>
|
||||
</resources>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<item name="discover_users" type="id"/>
|
||||
<item name="discover_local_timeline" type="id"/>
|
||||
<item name="discover_federated_timeline" type="id"/>
|
||||
<item name="discover_lists" type="id"/>
|
||||
|
||||
<item name="notifications_all" type="id"/>
|
||||
<item name="notifications_mentions" type="id"/>
|
||||
|
||||
@@ -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>
|
||||
@@ -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">Turned on post notifications for %s</string>
|
||||
<string name="user_post_notifications_off">Turned off post notifications for %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>
|
||||
@@ -407,4 +413,5 @@
|
||||
<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>
|
||||
<string name="list_timelines">Lists</string>
|
||||
</resources>
|
||||
34
mastodon/src/main/res/xml/locales_config.xml
Normal file
34
mastodon/src/main/res/xml/locales_config.xml
Normal 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>
|
||||
Reference in New Issue
Block a user