Compare commits

..

64 Commits

Author SHA1 Message Date
sk
40868f90f9 bump version 2022-11-20 18:07:21 +01:00
sk
1f89acec34 Merge branch 'clickable-boost-reply-line' into fork 2022-11-20 17:46:28 +01:00
sk
d75ce99a68 make reblog/reply line clickable
fixes #62
see mastodon#287
2022-11-20 17:46:17 +01:00
sk
0a8846fa2a Merge branch 'main' into fork 2022-11-20 17:21:28 +01:00
sk
5253e2e24a Merge branch 'use-bold-boost-icon' into fork 2022-11-20 17:11:42 +01:00
sk
3565223611 use bold boost icon
fixes #363
2022-11-20 17:09:23 +01:00
sk
2343d7a046 disable updater for debug build 2022-11-20 16:10:01 +01:00
sk
8256fbadb6 make release debuggable 2022-11-20 16:09:42 +01:00
Grishka
b898dc010e Show an error if a server has signups closed
closes #377
2022-11-20 13:36:23 +04:00
Grishka
de369633ec Fix #386 2022-11-20 12:54:56 +04:00
Gregory K
3f075eab13 Merge pull request #387 from sk22/fix-screenreader-middle-dot
Omit “middle dot” for screen reader
2022-11-20 07:54:06 +03:00
sk
96e5f854a5 Merge branch 'ui/profile-header-tweaks' into fork 2022-11-20 02:22:21 +01:00
sk
0f5211f718 change header height 2022-11-20 02:20:13 +01:00
sk
480915f377 change headerless header color 2022-11-20 02:20:01 +01:00
sk
15d559ad6a Revert "Updated Catalan strings"
This reverts commit 2d710cb558.
2022-11-20 02:02:29 +01:00
sk
3b7a6e9385 bump version 2022-11-20 01:37:02 +01:00
sk
314a15973c Merge branch 'fix-screenreader-middle-dot' into fork 2022-11-20 01:35:40 +01:00
sk
e99917945a omit middle dot for screen reader 2022-11-20 01:34:02 +01:00
sk
d3ba8a4d0f Merge branch 'fork' of github.com:sk22/mastodos into fork 2022-11-20 01:22:03 +01:00
sk22
9162b31ac1 Merge pull request #58 from rbnval/patch-1
Updated Catalan strings
2022-11-20 01:21:49 +01:00
sk22
51a80f3e03 remove whitespaces 2022-11-20 01:21:32 +01:00
sk
a8c49b59f6 Merge branch 'ui/larger-post-buttons' into fork 2022-11-20 01:16:39 +01:00
sk
c79942c13f make three-dot/visibility buttons larger
see mastodon#337
2022-11-20 01:15:53 +01:00
rbnval
da121495c0 Update strings.xml 2022-11-20 01:14:46 +01:00
rbnval
2d710cb558 Updated Catalan strings 2022-11-20 01:03:50 +01:00
sk
f0a51a15a9 Merge branch 'feature/follow_hashtags' into fork 2022-11-20 00:18:00 +01:00
sk
ae68b1e646 add error check 2022-11-20 00:17:16 +01:00
sk
5da58d7834 Merge branch 'main' into feature/follow_hashtags 2022-11-20 00:04:35 +01:00
sk
24023e9843 Merge branch 'true-black-improvements' into fork 2022-11-20 00:02:21 +01:00
sk
29d9871e77 improve true black styles 2022-11-20 00:01:53 +01:00
sk
32d182f03a Merge branch 'feature/display-reply-visibility' into fork 2022-11-19 23:44:07 +01:00
sk
da4f54751e don't display when visibility is unknown 2022-11-19 23:43:52 +01:00
sk
393c538464 bump version 2022-11-19 22:54:21 +01:00
sk
ddcf61dc95 Merge branch 'true-black-improvements' into fork 2022-11-19 22:41:50 +01:00
sk
5b70c035d2 fix #54, #55 2022-11-19 22:40:13 +01:00
sk
289564381d bump version 2022-11-19 20:41:27 +01:00
sk
3fbcce8570 Merge branch 'true-black-improvements' into fork 2022-11-19 20:38:16 +01:00
sk
505b755df6 use @color/black instead of #000
to avoid crash when getting null resource ID
2022-11-19 20:38:01 +01:00
sk
4b9618cad5 bump version 2022-11-19 20:18:50 +01:00
sk
e7b0e022d6 Merge branch 'main' into fork 2022-11-19 20:18:20 +01:00
sk
7ffc58d52c update readme 2022-11-19 20:17:51 +01:00
Grishka
30151cafc2 Yes I can flip that to true 2022-11-19 23:13:50 +04:00
sk
d0ce157069 Merge branch 'feature/posts-notifications-tab' into fork 2022-11-19 20:13:16 +01:00
sk
be7d65989d add posts notifications tab
closes #38
2022-11-19 20:12:52 +01:00
sk
08656a2678 Merge branch 'feature/display-reply-visibility' into fork 2022-11-19 19:39:26 +01:00
sk
b0f76739ba implement showing original visibility on reply
closes #41
2022-11-19 19:39:09 +01:00
sk
a09efd084e Merge branch 'true-black-improvements' into fork 2022-11-19 16:36:34 +01:00
sk
0bb85d71e4 improve real black mode
closes #48, improves upon mastodon#84
2022-11-19 16:36:12 +01:00
thomas
7248ab9801 true dark improvement 2022-11-19 16:31:26 +01:00
sk
cd4d83e139 Merge branch 'settings/disable-marquee' into fork 2022-11-19 16:11:41 +01:00
sk
f1f7c1341c add option to disable scrolling title bars
closes #50
as mentioned in mastodon#305
2022-11-19 16:09:45 +01:00
sk
a7ab6945f9 Merge branch 'feature/lists' into fork 2022-11-19 15:48:59 +01:00
sk
896583aeec fix compose button prepending list id
closes #51
2022-11-19 15:48:47 +01:00
sk
eccda2ed61 bump version 2022-11-17 21:25:35 +01:00
sk
b819c757b7 Merge branch 'feature/cw-above-text' into fork 2022-11-17 21:24:59 +01:00
sk
b1f7acb76b fix content warnings overflowing notifications 2022-11-17 21:24:43 +01:00
sk
c23df0c065 tweak content warning styles 2022-11-17 20:46:39 +01:00
sk
e514e24036 Merge branch 'feature/follow-requests' into fork 2022-11-17 20:13:26 +01:00
sk
fdf9417c5b hide follow requests icon when none waiting 2022-11-17 20:13:14 +01:00
sk
859468a03f update readme 2022-11-17 19:57:31 +01:00
sk
4623d403f7 Merge branch 'feature/follow-requests' into fork 2022-11-17 19:49:25 +01:00
sk
038c3c0fb9 hide follow requests button unless profile locked 2022-11-17 19:48:59 +01:00
sk
7f0e2bf03d Merge branch 'feature/follow-requests' into fork 2022-11-17 18:53:42 +01:00
sk
521ba8766c implement follow requests list
closes #39
2022-11-17 18:52:22 +01:00
43 changed files with 742 additions and 86 deletions

View File

@@ -58,32 +58,36 @@ Mastodos makes use of [Mastodon for Android](https://github.com/mastodon/mastodo
### Features
* [Add “Unlisted” as a post visibility option](https://github.com/sk22/mastodos/commits/feature/enable-unlisted)
* [Add “Unlisted” as a post visibility option](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos: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/mastodos/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/mastodos/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/mastodos/commits/feature/pin-posts) ([Pull request](https://github.com/mastodon/mastodon-android/pull/140))
* [Implement deleting and re-drafting](https://github.com/sk22/mastodos/commits/feature/delete-redraft) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/21))
* [Implement a bookmark button and list](https://github.com/sk22/mastodos/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/mastodos/commits/feature/check-for-update-button)
* [Add “Mark media as sensitive” option](https://github.com/sk22/mastodos/commits/feature/mark-media-as-sensitive)
* [Add settings to hide replies and reposts from the timeline](https://github.com/sk22/mastodos/commits/feature/filter-home-timeline) ([Pull request](https://github.com/mastodon/mastodon-android/pull/317))
* [Add “Federation” tab and change Discover tab order](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/add-federated-timeline) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/8))
* [Add image description button and viewer](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/display-alt-text) ([Pull request](https://github.com/mastodon/mastodon-android/pull/129))
* [Implement pinning posts and displaying pinned posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/pin-posts) ([Pull request](https://github.com/mastodon/mastodon-android/pull/140))
* [Implement deleting and re-drafting](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/delete-redraft) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/21))
* [Implement a bookmark button and list](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos: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/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/check-for-update-button)
* [Add “Mark media as sensitive” option](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/mark-media-as-sensitive)
* [Add settings to hide replies and reposts from the timeline](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/filter-home-timeline) ([Pull request](https://github.com/mastodon/mastodon-android/pull/317))
* [Follow and unfollow hashtags](https://github.com/sk22/mastodos/commit/7d38f031f197aa6cefaf53e39d929538689c1e4e) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/233))
* [Notification bell for posts](https://github.com/sk22/mastodos/commit/b166ca705eb9169025ef32bbe6315b42491b57ea) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/81))
* [Viewing lists and adding/removing users from lists](https://github.com/sk22/mastodos/commits/list-timeline-views) based on [@obstsalatschuessel](https://github.com/obstsalatschuessel)'s [Pull request](https://github.com/mastodon/mastodon-android/pull/286)
* [List favorited posts](https://github.com/sk22/mastodos/commits/feature/favs-list)
* [Accept/reject follow requests](https://github.com/sk22/mastodos/commits/feature/follow-requests)
* [Viewing lists and adding/removing users from lists](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:list-timeline-views) based on [@obstsalatschuessel](https://github.com/obstsalatschuessel)'s [Pull request](https://github.com/mastodon/mastodon-android/pull/286)
* [List favorited posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/favs-list)
* [Accept/reject follow requests](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/follow-requests)
* [Display content warning title above text](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/cw-above-text)
* [Add notifications tab for posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/posts-notifications-tab)
* [Show visibility of original post when replying](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/display-reply-visibility)
* [Improvements to the true black mode](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:true-black-improvements)
### Behavior
* [Make back button return to the home tab before exiting the app](https://github.com/sk22/mastodos/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/mastodos/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/mastodos/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/mastodos/commits/spoiler-height-independent) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/166))
* [Custom extended footer redesign](https://github.com/sk22/mastodos/commits/compact-extended-footer)
* [Option to hide interaction numbers](https://github.com/sk22/mastodos/commits/settings/hide-interaction-numbers)
* [Make back button return to the home tab before exiting the app](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/back-returns-home) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/118))
* [Always preserve content warnings when replying](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/always-preserve-cw) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/113))
* [Display full image when adding image description](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos: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/mastodon/mastodon-android/compare/master...sk22:mastodos:spoiler-height-independent) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/166))
* [Custom extended footer redesign](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:compact-extended-footer)
* [Option to hide interaction numbers](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:settings/hide-interaction-numbers)
* [Option to always reveal content warnings](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/cw-above-text)
* [Option to disable scrolling title bars](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:settings/disable-marquee)
### Branding

View File

@@ -9,8 +9,8 @@ android {
applicationId "org.joinmastodon.android.sk"
minSdk 23
targetSdk 33
versionCode 40
versionName "1.1.4+fork.40"
versionCode 46
versionName "1.1.4+fork.46"
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",
@@ -20,12 +20,14 @@ android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
// minifyEnabled true
// shrinkResources true
debuggable true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug{
debuggable true
versionNameSuffix '-debug'
}
appcenterPrivateBeta{
initWith release

View File

@@ -139,7 +139,7 @@ public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
curForkNumber=Integer.parseInt(matcher.group(4));
long newVersion=((long)newMajor << 32) | ((long)newMinor << 16) | newRevision;
long curVersion=((long)curMajor << 32) | ((long)curMinor << 16) | curRevision;
if(newVersion>curVersion || newForkNumber>curForkNumber || BuildConfig.DEBUG){
if(newVersion>curVersion || newForkNumber>curForkNumber){
String version=newMajor+"."+newMinor+"."+newRevision+"+fork."+newForkNumber;
Log.d(TAG, "actuallyCheckForUpdates: new version: "+version);
for(JsonElement el:obj.getAsJsonArray("assets")){

View File

@@ -12,9 +12,10 @@ public class GlobalUserPreferences{
public static boolean loadNewPosts;
public static boolean showInteractionCounts;
public static boolean alwaysExpandContentWarnings;
public static boolean disableMarquee;
public static ThemePreference theme;
private static SharedPreferences getPrefs(){
private static SharedPreferences getPrefs(){
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
}
@@ -28,6 +29,7 @@ public class GlobalUserPreferences{
loadNewPosts=prefs.getBoolean("loadNewPosts", true);
showInteractionCounts=prefs.getBoolean("showInteractionCounts", false);
alwaysExpandContentWarnings=prefs.getBoolean("alwaysExpandContentWarnings", false);
disableMarquee=prefs.getBoolean("disableMarquee", false);
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
}
@@ -41,6 +43,7 @@ public class GlobalUserPreferences{
.putBoolean("trueBlackTheme", trueBlackTheme)
.putBoolean("showInteractionCounts", showInteractionCounts)
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
.putBoolean("disableMarquee", disableMarquee)
.putInt("theme", theme.ordinal())
.apply();
}

View File

@@ -35,7 +35,7 @@ import me.grishka.appkit.utils.WorkerThread;
public class CacheController{
private static final String TAG="CacheController";
private static final int DB_VERSION=2;
private static final int DB_VERSION=3;
private static final WorkerThread databaseThread=new WorkerThread("databaseThread");
private static final Handler uiHandler=new Handler(Looper.getMainLooper());
@@ -126,14 +126,15 @@ public class CacheController{
});
}
public void getNotifications(String maxID, int count, boolean onlyMentions, boolean forceReload, Callback<PaginatedResponse<List<Notification>>> callback){
public void getNotifications(String maxID, int count, boolean onlyMentions, boolean onlyPosts, boolean forceReload, Callback<PaginatedResponse<List<Notification>>> callback){
cancelDelayedClose();
databaseThread.postRunnable(()->{
try{
List<Filter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.NOTIFICATIONS)).collect(Collectors.toList());
if(!forceReload){
SQLiteDatabase db=getOrOpenDatabase();
try(Cursor cursor=db.query(onlyMentions ? "notifications_mentions" : "notifications_all", new String[]{"json"}, maxID==null ? null : "`id`<?", maxID==null ? null : new String[]{maxID}, null, null, "`id` DESC", count+"")){
String table=onlyPosts ? "notifications_posts" : onlyMentions ? "notifications_mentions" : "notifications_all";
try(Cursor cursor=db.query(table, new String[]{"json"}, maxID==null ? null : "`id`<?", maxID==null ? null : new String[]{maxID}, null, null, "`id` DESC", count+"")){
if(cursor.getCount()==count){
ArrayList<Notification> result=new ArrayList<>();
cursor.moveToFirst();
@@ -159,7 +160,7 @@ public class CacheController{
Log.w(TAG, "getNotifications: corrupted notification object in database", x);
}
}
new GetNotifications(maxID, count, onlyMentions ? EnumSet.of(Notification.Type.MENTION): EnumSet.allOf(Notification.Type.class))
new GetNotifications(maxID, count, onlyPosts ? EnumSet.of(Notification.Type.STATUS) : onlyMentions ? EnumSet.of(Notification.Type.MENTION): EnumSet.allOf(Notification.Type.class))
.setCallback(new Callback<>(){
@Override
public void onSuccess(List<Notification> result){
@@ -173,7 +174,7 @@ public class CacheController{
}
return true;
}).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id));
putNotifications(result, onlyMentions, maxID==null);
putNotifications(result, onlyMentions, onlyPosts, maxID==null);
}
@Override
@@ -191,9 +192,9 @@ public class CacheController{
}, 0);
}
private void putNotifications(List<Notification> notifications, boolean onlyMentions, boolean clear){
private void putNotifications(List<Notification> notifications, boolean onlyMentions, boolean onlyPosts, boolean clear){
runOnDbThread((db)->{
String table=onlyMentions ? "notifications_mentions" : "notifications_all";
String table=onlyPosts ? "notifications_posts" : onlyMentions ? "notifications_mentions" : "notifications_all";
if(clear)
db.delete(table, null, null);
ContentValues values=new ContentValues(3);
@@ -317,6 +318,7 @@ public class CacheController{
`type` INTEGER NOT NULL
)""");
createRecentSearchesTable(db);
createPostsNotificationsTable(db);
}
@Override
@@ -324,6 +326,9 @@ public class CacheController{
if(oldVersion==1){
createRecentSearchesTable(db);
}
if(oldVersion==2){
createPostsNotificationsTable(db);
}
}
private void createRecentSearchesTable(SQLiteDatabase db){
@@ -334,6 +339,16 @@ public class CacheController{
`time` INTEGER NOT NULL
)""");
}
private void createPostsNotificationsTable(SQLiteDatabase db){
db.execSQL("""
CREATE TABLE `notifications_posts` (
`id` VARCHAR(25) NOT NULL PRIMARY KEY,
`json` TEXT NOT NULL,
`flags` INTEGER NOT NULL DEFAULT 0,
`type` INTEGER NOT NULL
)""");
}
}
@FunctionalInterface

View File

@@ -1,4 +1,4 @@
package org.joinmastodon.android.api.requests.follow_requests;
package org.joinmastodon.android.api.requests.accounts;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Relationship;

View File

@@ -0,0 +1,50 @@
package org.joinmastodon.android.api.requests.accounts;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.FollowSuggestion;
import java.io.IOException;
import java.util.List;
import okhttp3.Response;
public class GetFollowRequests extends MastodonAPIRequest<List<Account>>{
private String maxId;
public GetFollowRequests(String maxID, String minID, int limit){
super(HttpMethod.GET, "/follow_requests", new TypeToken<>(){});
if(maxID!=null)
addQueryParameter("max_id", maxID);
if(minID!=null)
addQueryParameter("min_id", minID);
if(limit>0)
addQueryParameter("limit", ""+limit);
}
@Override
public void validateAndPostprocessResponse(List<Account> respObj, Response httpResponse) throws IOException {
super.validateAndPostprocessResponse(respObj, httpResponse);
// <https://mastodon.social/api/v1/follow_requests?max_id=268962>; rel="next",
// <https://mastodon.social/api/v1/follow_requests?min_id=268981>; rel="prev"
String link=httpResponse.header("link");
// parsing link header by hand; using a library would be cleaner
// (also, the functionality should be part of the max id logics and implemented in MastodonAPIRequest)
if(link==null) return;
String maxIdEq="max_id=";
for(String s : link.split(",")) {
if(s.contains("rel=\"next\"")) {
int start=s.indexOf(maxIdEq)+maxIdEq.length();
int end=s.indexOf('>');
if(start<0 || start>end) return;
this.maxId=s.substring(start, end);
}
}
}
public String getMaxId() {
return maxId;
}
}

View File

@@ -1,4 +1,4 @@
package org.joinmastodon.android.api.requests.follow_requests;
package org.joinmastodon.android.api.requests.accounts;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Relationship;

View File

@@ -0,0 +1,18 @@
package org.joinmastodon.android.events;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Relationship;
public class FollowRequestHandledEvent {
public String accountID;
public boolean accepted;
public Account account;
public Relationship relationship;
public FollowRequestHandledEvent(String accountID, boolean accepted, Account account, Relationship rel){
this.accountID=accountID;
this.accepted=accepted;
this.account=account;
this.relationship=rel;
}
}

View File

@@ -19,6 +19,7 @@ import android.view.WindowInsets;
import android.widget.Toolbar;
import org.joinmastodon.android.E;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
import org.joinmastodon.android.api.requests.polls.SubmitPollVote;
@@ -81,6 +82,10 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
if(GlobalUserPreferences.disableMarquee){
setTitleMarqueeEnabled(false);
setSubtitleMarqueeEnabled(false);
}
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
setRetainInstance(true);
}

View File

@@ -13,6 +13,7 @@ import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.RenderEffect;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.icu.text.BreakIterator;
import android.media.MediaMetadataRetriever;
@@ -502,6 +503,24 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
spoilerEdit.addTextChangedListener(new SimpleTextWatcher(e->updateCharCounter()));
if(replyTo!=null){
replyText.setText(getString(R.string.in_reply_to, replyTo.account.displayName));
int visibilityNameRes = switch (statusVisibility) {
case PUBLIC -> R.string.visibility_public;
case UNLISTED -> R.string.visibility_unlisted;
case PRIVATE -> R.string.visibility_followers_only;
case DIRECT -> R.string.visibility_private;
};
replyText.setContentDescription(getString(R.string.in_reply_to, replyTo.account.displayName) + ". " + getString(R.string.post_visibility) + ": " + getString(visibilityNameRes));
Drawable visibilityIcon = getActivity().getDrawable(switch(statusVisibility){
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
case UNLISTED -> R.drawable.ic_fluent_people_community_20_regular;
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_20_regular;
case DIRECT -> R.drawable.ic_at_symbol;
});
visibilityIcon.setBounds(0, 0, V.dp(20), V.dp(20));
Drawable replyArrow = getActivity().getDrawable(R.drawable.ic_fluent_arrow_reply_20_filled);
replyArrow.setBounds(0, 0, V.dp(20), V.dp(20));
replyText.setCompoundDrawables(replyArrow, null, visibilityIcon, null);
ArrayList<String> mentions=new ArrayList<>();
String ownID=AccountSessionManager.getInstance().getAccount(accountID).self.id;
if(!replyTo.account.id.equals(ownID))

View File

@@ -0,0 +1,344 @@
package org.joinmastodon.android.fragments;
import android.app.Activity;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
import org.joinmastodon.android.api.requests.accounts.GetFollowRequests;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Relationship;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.ProgressBarButton;
import org.parceler.Parcels;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.api.SimpleCallback;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.UsableRecyclerView;
public class FollowRequestsListFragment extends BaseRecyclerFragment<FollowRequestsListFragment.AccountWrapper> implements ScrollableToTop{
private String accountID;
private Map<String, Relationship> relationships=Collections.emptyMap();
private GetAccountRelationships relationshipsRequest;
private String lastMaxId=null;
public FollowRequestsListFragment(){
super(20);
}
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
accountID=getArguments().getString("account");
loadData();
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
setTitle(R.string.follow_requests);
}
@Override
protected void doLoadData(int offset, int count){
if(relationshipsRequest!=null){
relationshipsRequest.cancel();
relationshipsRequest=null;
}
currentRequest=new GetFollowRequests(offset>0 ? lastMaxId : null, null, count)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Account> result){
onDataLoaded(result.stream().map(AccountWrapper::new).collect(Collectors.toList()), false);
loadRelationships();
}
})
.exec(accountID);
}
@Override
protected RecyclerView.Adapter getAdapter(){
return new AccountsAdapter();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
list.addItemDecoration(new RecyclerView.ItemDecoration(){
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
outRect.bottom=outRect.left=outRect.right=V.dp(16);
if(parent.getChildAdapterPosition(view)==0)
outRect.top=V.dp(16);
}
});
((UsableRecyclerView)list).setDrawSelectorOnTop(true);
}
private void loadRelationships(){
relationships=Collections.emptyMap();
relationshipsRequest=new GetAccountRelationships(data.stream().map(fs->fs.account.id).collect(Collectors.toList()));
relationshipsRequest.setCallback(new Callback<>(){
@Override
public void onSuccess(List<Relationship> result){
relationshipsRequest=null;
relationships=result.stream().collect(Collectors.toMap(rel->rel.id, Function.identity()));
if(list==null)
return;
for(int i=0;i<list.getChildCount();i++){
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
if(holder instanceof AccountViewHolder avh)
avh.rebind();
}
}
@Override
public void onError(ErrorResponse error){
relationshipsRequest=null;
}
}).exec(accountID);
}
@Override
public void onDestroyView(){
super.onDestroyView();
if(relationshipsRequest!=null){
relationshipsRequest.cancel();
relationshipsRequest=null;
}
}
@Override
public void scrollToTop(){
smoothScrollRecyclerViewToTop(list);
}
private class AccountsAdapter extends UsableRecyclerView.Adapter<AccountViewHolder> implements ImageLoaderRecyclerAdapter{
public AccountsAdapter(){
super(imgLoader);
}
@Override
public void onBindViewHolder(AccountViewHolder holder, int position){
holder.bind(data.get(position));
super.onBindViewHolder(holder, position);
}
@NonNull
@Override
public AccountViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
return new AccountViewHolder();
}
@Override
public int getItemCount(){
return data.size();
}
@Override
public int getImageCountForItem(int position){
return 2+data.get(position).emojiHelper.getImageCount();
}
@Override
public ImageLoaderRequest getImageRequest(int position, int image){
AccountWrapper item=data.get(position);
if(image==0)
return item.avaRequest;
else if(image==1)
return item.coverRequest;
else
return item.emojiHelper.getImageRequest(image-2);
}
}
// literally the same as AccountCardStatusDisplayItem and DiscoverAccountsFragment. code should be generalized
private class AccountViewHolder extends BindableViewHolder<AccountWrapper> implements ImageLoaderViewHolder, UsableRecyclerView.Clickable{
private final ImageView cover, avatar;
private final TextView name, username, bio, followersCount, followingCount, postsCount, followersLabel, followingLabel, postsLabel;
private final ProgressBarButton actionButton, acceptButton, rejectButton;
private final ProgressBar actionProgress, acceptProgress, rejectProgress;
private final View actionWrap, acceptWrap, rejectWrap;
private Relationship relationship;
public AccountViewHolder(){
super(getActivity(), R.layout.item_discover_account, list);
cover=findViewById(R.id.cover);
avatar=findViewById(R.id.avatar);
name=findViewById(R.id.name);
username=findViewById(R.id.username);
bio=findViewById(R.id.bio);
followersCount=findViewById(R.id.followers_count);
followersLabel=findViewById(R.id.followers_label);
followingCount=findViewById(R.id.following_count);
followingLabel=findViewById(R.id.following_label);
postsCount=findViewById(R.id.posts_count);
postsLabel=findViewById(R.id.posts_label);
actionButton=findViewById(R.id.action_btn);
actionProgress=findViewById(R.id.action_progress);
actionWrap=findViewById(R.id.action_btn_wrap);
acceptButton=findViewById(R.id.accept_btn);
acceptProgress=findViewById(R.id.accept_progress);
acceptWrap=findViewById(R.id.accept_btn_wrap);
rejectButton=findViewById(R.id.reject_btn);
rejectProgress=findViewById(R.id.reject_progress);
rejectWrap=findViewById(R.id.reject_btn_wrap);
itemView.setOutlineProvider(OutlineProviders.roundedRect(6));
itemView.setClipToOutline(true);
avatar.setOutlineProvider(OutlineProviders.roundedRect(12));
avatar.setClipToOutline(true);
cover.setOutlineProvider(OutlineProviders.roundedRect(3));
cover.setClipToOutline(true);
actionButton.setOnClickListener(this::onActionButtonClick);
acceptButton.setOnClickListener(this::onFollowRequestButtonClick);
rejectButton.setOnClickListener(this::onFollowRequestButtonClick);
}
@Override
public void onBind(AccountWrapper item){
name.setText(item.parsedName);
username.setText('@'+item.account.acct);
bio.setText(item.parsedBio);
followersCount.setText(UiUtils.abbreviateNumber(item.account.followersCount));
followingCount.setText(UiUtils.abbreviateNumber(item.account.followingCount));
postsCount.setText(UiUtils.abbreviateNumber(item.account.statusesCount));
followersLabel.setText(getResources().getQuantityString(R.plurals.followers, (int)Math.min(999, item.account.followersCount)));
followingLabel.setText(getResources().getQuantityString(R.plurals.following, (int)Math.min(999, item.account.followingCount)));
postsLabel.setText(getResources().getQuantityString(R.plurals.posts, (int)Math.min(999, item.account.statusesCount)));
relationship=relationships.get(item.account.id);
if(relationship == null || !relationship.followedBy){
actionWrap.setVisibility(View.GONE);
acceptWrap.setVisibility(View.VISIBLE);
rejectWrap.setVisibility(View.VISIBLE);
// i hate that i wasn't able to do this in xml
acceptButton.setCompoundDrawableTintList(acceptButton.getTextColors());
acceptProgress.setIndeterminateTintList(acceptButton.getTextColors());
rejectButton.setCompoundDrawableTintList(rejectButton.getTextColors());
rejectProgress.setIndeterminateTintList(rejectButton.getTextColors());
}else if(relationship==null){
actionWrap.setVisibility(View.GONE);
acceptWrap.setVisibility(View.GONE);
rejectWrap.setVisibility(View.GONE);
}else{
actionWrap.setVisibility(View.VISIBLE);
acceptWrap.setVisibility(View.GONE);
rejectWrap.setVisibility(View.GONE);
UiUtils.setRelationshipToActionButton(relationship, actionButton);
}
}
@Override
public void setImage(int index, Drawable image){
if(index==0){
avatar.setImageDrawable(image);
}else if(index==1){
cover.setImageDrawable(image);
}else{
item.emojiHelper.setImageDrawable(index-2, image);
name.invalidate();
bio.invalidate();
}
if(image instanceof Animatable a && !a.isRunning())
a.start();
}
@Override
public void clearImage(int index){
setImage(index, null);
}
@Override
public void onClick(){
Bundle args=new Bundle();
args.putString("account", accountID);
args.putParcelable("profileAccount", Parcels.wrap(item.account));
Nav.go(getActivity(), ProfileFragment.class, args);
}
private void onFollowRequestButtonClick(View v) {
itemView.setHasTransientState(true);
UiUtils.handleFollowRequest((Activity) v.getContext(), item.account, accountID, null, v == acceptButton, relationship, rel -> {
itemView.setHasTransientState(false);
relationships.put(item.account.id, rel);
RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter = getBindingAdapter();
if (!rel.requested && !rel.followedBy && adapter != null) {
data.remove(item);
adapter.notifyItemRemoved(getBindingAdapterPosition());
} else {
rebind();
}
});
}
private void onActionButtonClick(View v){
itemView.setHasTransientState(true);
UiUtils.performAccountAction(getActivity(), item.account, accountID, relationship, actionButton, this::setActionProgressVisible, rel->{
itemView.setHasTransientState(false);
relationships.put(item.account.id, rel);
rebind();
});
}
private void setActionProgressVisible(boolean visible){
actionButton.setTextVisible(!visible);
actionProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
actionButton.setClickable(!visible);
}
}
protected class AccountWrapper{
public Account account;
public ImageLoaderRequest avaRequest, coverRequest;
public CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
public CharSequence parsedName, parsedBio;
public AccountWrapper(Account account){
this.account=account;
if(!TextUtils.isEmpty(account.avatar))
avaRequest=new UrlImageLoaderRequest(account.avatar, V.dp(50), V.dp(50));
if(!TextUtils.isEmpty(account.header))
coverRequest=new UrlImageLoaderRequest(account.header, 1000, 1000);
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
if(account.emojis.isEmpty()){
parsedName=account.displayName;
}else{
parsedName=HtmlParser.parseCustomEmoji(account.displayName, account.emojis);
emojiHelper.setText(new SpannableStringBuilder(parsedName).append(parsedBio));
}
}
}
}

View File

@@ -66,8 +66,8 @@ public class HashtagTimelineFragment extends StatusListFragment{
new SetHashtagFollowed(hashtag, following).setCallback(new Callback<>() {
@Override
public void onSuccess(Hashtag i) {
if (i.following == following) Toast.makeText(getActivity(), getString(i.following ? R.string.followed_user : R.string.unfollowed_user, "#" + i.name), Toast.LENGTH_SHORT).show();
updateFollowingState(i.following);
Toast.makeText(getActivity(), getString(i.following ? R.string.followed_user : R.string.unfollowed_user, "#" + i.name), Toast.LENGTH_SHORT).show();
}
@Override

View File

@@ -41,6 +41,7 @@ public class ListTimelineFragment extends StatusListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
// TODO: implement edit, delete
// inflater.inflate(R.menu.list, menu);
}
@@ -73,7 +74,6 @@ public class ListTimelineFragment extends StatusListFragment {
private void onFabClick(View v){
Bundle args=new Bundle();
args.putString("account", accountID);
args.putString("prefilledText", listID+' ');
Nav.go(getActivity(), ComposeFragment.class, args);
}

View File

@@ -2,16 +2,22 @@ package org.joinmastodon.android.fragments;
import android.app.Activity;
import android.app.Fragment;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import org.joinmastodon.android.E;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetFollowRequests;
import org.joinmastodon.android.events.FollowRequestHandledEvent;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.ui.SimpleViewHolder;
import org.joinmastodon.android.ui.tabs.TabLayout;
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
@@ -20,8 +26,15 @@ import org.joinmastodon.android.ui.utils.UiUtils;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;
import com.squareup.otto.Subscribe;
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.fragments.BaseRecyclerFragment;
import me.grishka.appkit.fragments.ToolbarFragment;
import me.grishka.appkit.utils.V;
public class NotificationsFragment extends MastodonToolbarFragment implements ScrollableToTop{
@@ -31,7 +44,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
private FrameLayout[] tabViews;
private TabLayoutMediator tabLayoutMediator;
private NotificationsListFragment allNotificationsFragment, mentionsFragment;
private NotificationsListFragment allNotificationsFragment, mentionsFragment, postsFragment;
private String accountID;
@@ -42,14 +55,36 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
setRetainInstance(true);
accountID=getArguments().getString("account");
E.register(this);
}
@Override
public void onDestroy() {
super.onDestroy();
E.unregister(this);
}
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
setHasOptionsMenu(true);
setTitle(R.string.notifications);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
inflater.inflate(R.menu.notifications, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() != R.id.follow_requests) return false;
Bundle args=new Bundle();
args.putString("account", accountID);
Nav.go(getActivity(), FollowRequestsListFragment.class, args);
return true;
}
@Override
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
LinearLayout view=(LinearLayout) inflater.inflate(R.layout.fragment_notifications, container, false);
@@ -57,12 +92,13 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
tabLayout=view.findViewById(R.id.tabbar);
pager=view.findViewById(R.id.pager);
tabViews=new FrameLayout[2];
tabViews=new FrameLayout[3];
for(int i=0;i<tabViews.length;i++){
FrameLayout tabView=new FrameLayout(getActivity());
tabView.setId(switch(i){
case 0 -> R.id.notifications_all;
case 1 -> R.id.notifications_mentions;
case 2 -> R.id.notifications_posts;
default -> throw new IllegalStateException("Unexpected value: "+i);
});
tabView.setVisibility(View.GONE);
@@ -101,9 +137,15 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
mentionsFragment=new NotificationsListFragment();
mentionsFragment.setArguments(args);
args=new Bundle(args);
args.putBoolean("onlyPosts", true);
postsFragment=new NotificationsListFragment();
postsFragment.setArguments(args);
getChildFragmentManager().beginTransaction()
.add(R.id.notifications_all, allNotificationsFragment)
.add(R.id.notifications_mentions, mentionsFragment)
.add(R.id.notifications_posts, postsFragment)
.commit();
}
@@ -113,6 +155,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
tab.setText(switch(position){
case 0 -> R.string.all_notifications;
case 1 -> R.string.mentions;
case 2 -> R.string.posts;
default -> throw new IllegalStateException("Unexpected value: "+position);
});
tab.view.textView.setAllCaps(true);
@@ -123,12 +166,30 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
return view;
}
public void refreshFollowRequestsBadge() {
new GetFollowRequests(null, null, 1).setCallback(new Callback<>() {
@Override
public void onSuccess(List<Account> accounts) {
getToolbar().getMenu().findItem(R.id.follow_requests).setVisible(!accounts.isEmpty());
}
@Override
public void onError(ErrorResponse errorResponse) {}
}).exec(accountID);
}
@Subscribe
public void onFollowRequestHandled(FollowRequestHandledEvent ev) {
refreshFollowRequestsBadge();
}
@Override
public void scrollToTop(){
getFragmentForPage(pager.getCurrentItem()).scrollToTop();
}
public void loadData(){
refreshFollowRequestsBadge();
if(allNotificationsFragment!=null && !allNotificationsFragment.loaded && !allNotificationsFragment.dataLoading)
allNotificationsFragment.loadData();
}
@@ -143,6 +204,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
return switch(page){
case 0 -> allNotificationsFragment;
case 1 -> mentionsFragment;
case 2 -> postsFragment;
default -> throw new IllegalStateException("Unexpected value: "+page);
};
}
@@ -163,7 +225,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
@Override
public int getItemCount(){
return 2;
return 3;
}
@Override

View File

@@ -35,6 +35,7 @@ import me.grishka.appkit.utils.V;
public class NotificationsListFragment extends BaseStatusListFragment<Notification>{
private boolean onlyMentions;
private boolean onlyPosts;
private String maxID;
@Override
@@ -53,6 +54,15 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
public void onAttach(Activity activity){
super.onAttach(activity);
onlyMentions=getArguments().getBoolean("onlyMentions", false);
onlyPosts=getArguments().getBoolean("onlyPosts", false);
}
@Override
public void onRefresh() {
super.onRefresh();
if (getParentFragment() instanceof NotificationsFragment notificationsFragment) {
notificationsFragment.refreshFollowRequestsBadge();
}
}
@Override
@@ -98,7 +108,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
protected void doLoadData(int offset, int count){
AccountSessionManager.getInstance()
.getAccount(accountID).getCacheController()
.getNotifications(offset>0 ? maxID : null, count, onlyMentions, refreshing, new SimpleCallback<>(this){
.getNotifications(offset>0 ? maxID : null, count, onlyMentions, onlyPosts, refreshing, new SimpleCallback<>(this){
@Override
public void onSuccess(PaginatedResponse<List<Notification>> result){
if(getActivity()==null)

View File

@@ -94,6 +94,10 @@ public class SettingsFragment extends MastodonToolbarFragment{
items.add(new HeaderItem(R.string.settings_theme));
items.add(themeItem=new ThemeItem());
items.add(new SwitchItem(R.string.theme_true_black, R.drawable.ic_fluent_dark_theme_24_regular, GlobalUserPreferences.trueBlackTheme, this::onTrueBlackThemeChanged));
items.add(new SwitchItem(R.string.disable_marquee, R.drawable.ic_fluent_text_more_24_regular, GlobalUserPreferences.disableMarquee, i->{
GlobalUserPreferences.disableMarquee=i.checked;
GlobalUserPreferences.save();
}));
items.add(new HeaderItem(R.string.settings_behavior));
items.add(new SwitchItem(R.string.settings_gif, R.drawable.ic_fluent_gif_24_regular, GlobalUserPreferences.playGifs, i->{

View File

@@ -139,7 +139,7 @@ public class StatusEditHistoryFragment extends StatusListFragment{
action=getString(R.string.edit_multiple_changed);
}
}
items.add(0, new ReblogOrReplyLineStatusDisplayItem(s.id, this, action+" · "+date, Collections.emptyList(), 0));
items.add(0, new ReblogOrReplyLineStatusDisplayItem(s.id, this, action+" · "+date, Collections.emptyList(), 0, null));
}
return items;
}

View File

@@ -53,6 +53,7 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -295,6 +296,14 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
private void proceedWithAuthOrSignup(Instance instance){
getActivity().getSystemService(InputMethodManager.class).hideSoftInputFromWindow(contentView.getWindowToken(), 0);
if(isSignup){
if(!instance.registrations){
new M3AlertDialogBuilder(getActivity())
.setTitle(R.string.error)
.setMessage(R.string.instance_signup_closed)
.setPositiveButton(R.string.ok, null)
.show();
return;
}
Bundle args=new Bundle();
args.putParcelable("instance", Parcels.wrap(instance));
Nav.go(getActivity(), InstanceRulesFragment.class, args);
@@ -478,7 +487,7 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
instanceProgressDialog=null;
proceedWithAuthOrSignup(result);
}
if(domain.equals(currentSearchQuery) || currentSearchQuery.equals(redirects.get(domain)) || currentSearchQuery.equals(redirectsInverse.get(domain))){
if(Objects.equals(domain, currentSearchQuery) || Objects.equals(currentSearchQuery, redirects.get(domain)) || Objects.equals(currentSearchQuery, redirectsInverse.get(domain))){
boolean found=false;
for(CatalogInstance ci:filteredData){
if(ci.domain.equals(domain)){

View File

@@ -102,7 +102,7 @@ public class ReportCommentFragment extends MastodonToolbarFragment{
ReportReason reason=ReportReason.valueOf(getArguments().getString("reason"));
ArrayList<String> statusIDs=getArguments().getStringArrayList("statusIDs");
ArrayList<String> ruleIDs=getArguments().getStringArrayList("ruleIDs");
new SendReport(reportAccount.id, reason, statusIDs, ruleIDs, v.getId()==R.id.btn_back ? null : commentEdit.getText().toString(), false)
new SendReport(reportAccount.id, reason, statusIDs, ruleIDs, v.getId()==R.id.btn_back ? null : commentEdit.getText().toString(), true)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Object result){

View File

@@ -4,6 +4,7 @@ import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.SpannableStringBuilder;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
@@ -17,6 +18,8 @@ import org.joinmastodon.android.ui.utils.UiUtils;
import java.util.List;
import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
@@ -25,14 +28,16 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
@DrawableRes
private int icon;
private CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
private View.OnClickListener handleClick;
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon){
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon, @Nullable View.OnClickListener handleClick){
super(parentID, parentFragment);
SpannableStringBuilder ssb=new SpannableStringBuilder(text);
HtmlParser.parseCustomEmoji(ssb, emojis);
this.text=ssb;
emojiHelper.setText(ssb);
this.icon=icon;
this.handleClick=handleClick;
}
@Override
@@ -61,6 +66,7 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
public void onBind(ReblogOrReplyLineStatusDisplayItem item){
text.setText(item.text);
text.setCompoundDrawablesRelativeWithIntrinsicBounds(item.icon, 0, 0, 0);
if(item.handleClick!=null) text.setOnClickListener(item.handleClick);
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N)
UiUtils.fixCompoundDrawableTintOnAndroid6(text);
}

View File

@@ -2,12 +2,14 @@ package org.joinmastodon.android.ui.displayitems;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.fragments.ThreadFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Attachment;
@@ -16,6 +18,7 @@ import org.joinmastodon.android.model.Poll;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.PhotoLayoutHelper;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.parceler.Parcels;
import java.util.ArrayList;
import java.util.List;
@@ -23,6 +26,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import me.grishka.appkit.Nav;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.views.UsableRecyclerView;
@@ -73,11 +77,19 @@ public abstract class StatusDisplayItem{
String parentID=parentObject.getID();
ArrayList<StatusDisplayItem> items=new ArrayList<>();
Status statusForContent=status.getContentStatus();
Bundle args=new Bundle();
args.putString("account", accountID);
if(status.reblog!=null){
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.user_boosted, status.account.displayName), status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20_filled));
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.user_boosted, status.account.displayName), status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20_filled, i->{
args.putParcelable("profileAccount", Parcels.wrap(status.account));
Nav.go(fragment.getActivity(), ProfileFragment.class, args);
}));
}else if(status.inReplyToAccountId!=null && knownAccounts.containsKey(status.inReplyToAccountId)){
Account account=Objects.requireNonNull(knownAccounts.get(status.inReplyToAccountId));
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.in_reply_to, account.displayName), account.emojis, R.drawable.ic_fluent_arrow_reply_20_filled));
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.in_reply_to, account.displayName), account.emojis, R.drawable.ic_fluent_arrow_reply_20_filled, i->{
args.putParcelable("profileAccount", Parcels.wrap(account));
Nav.go(fragment.getActivity(), ProfileFragment.class, args);
}));
}
HeaderStatusDisplayItem header;
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null));

View File

@@ -5,8 +5,10 @@ import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.joinmastodon.android.R;
@@ -20,6 +22,7 @@ import org.joinmastodon.android.ui.views.LinkedTextView;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.MovieDrawable;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.utils.V;
public class TextStatusDisplayItem extends StatusDisplayItem{
private CharSequence text;
@@ -61,8 +64,10 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
public static class Holder extends StatusDisplayItem.Holder<TextStatusDisplayItem> implements ImageLoaderViewHolder{
private final LinkedTextView text;
private final LinearLayout spoilerHeader;
private final TextView spoilerTitle, spoilerTitleInline;
private final View spoilerOverlay, spoilerHeader;
private final View spoilerOverlay, borderTop, borderBottom;
private final Drawable backgroundColor, borderColor;
public Holder(Activity activity, ViewGroup parent){
super(activity, R.layout.display_item_text, parent);
@@ -71,14 +76,29 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
spoilerTitleInline=findViewById(R.id.spoiler_title_inline);
spoilerHeader=findViewById(R.id.spoiler_header);
spoilerOverlay=findViewById(R.id.spoiler_overlay);
borderTop=findViewById(R.id.border_top);
borderBottom=findViewById(R.id.border_bottom);
itemView.setOnClickListener(v->item.parentFragment.onRevealSpoilerClick(this));
TypedValue outValue=new TypedValue();
activity.getTheme().resolveAttribute(R.attr.colorBackgroundLight, outValue, true);
backgroundColor=activity.getDrawable(outValue.resourceId);
// activity.getTheme().resolveAttribute(R.attr.colorBackgroundLightest, outValue, true);
// backgroundColorInset=activity.getDrawable(outValue.resourceId);
activity.getTheme().resolveAttribute(R.attr.colorPollVoted, outValue, true);
borderColor=activity.getDrawable(outValue.resourceId);
}
@Override
public void onBind(TextStatusDisplayItem item){
text.setText(item.text);
text.setTextIsSelectable(item.textSelectable);
spoilerTitleInline.setTextIsSelectable(item.textSelectable);
text.setInvalidateOnEveryFrame(false);
spoilerTitleInline.setBackground(item.inset ? null : backgroundColor);
spoilerTitleInline.setPadding(spoilerTitleInline.getPaddingLeft(), item.inset ? 0 : V.dp(14), spoilerTitleInline.getPaddingRight(), item.inset ? 0 : V.dp(14));
borderTop.setBackground(item.inset ? null : borderColor);
borderBottom.setBackground(item.inset ? null : borderColor);
if(!TextUtils.isEmpty(item.status.spoilerText)){
spoilerTitle.setText(item.parsedSpoilerText);
spoilerTitleInline.setText(item.parsedSpoilerText);

View File

@@ -39,13 +39,14 @@ import org.joinmastodon.android.api.requests.accounts.SetAccountBlocked;
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
import org.joinmastodon.android.api.requests.accounts.SetAccountMuted;
import org.joinmastodon.android.api.requests.accounts.SetDomainBlocked;
import org.joinmastodon.android.api.requests.follow_requests.AuthorizeFollowRequest;
import org.joinmastodon.android.api.requests.follow_requests.RejectFollowRequest;
import org.joinmastodon.android.api.requests.accounts.AuthorizeFollowRequest;
import org.joinmastodon.android.api.requests.accounts.RejectFollowRequest;
import org.joinmastodon.android.api.requests.statuses.DeleteStatus;
import org.joinmastodon.android.api.requests.statuses.GetStatusByID;
import org.joinmastodon.android.api.requests.statuses.SetStatusPinned;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
import org.joinmastodon.android.events.FollowRequestHandledEvent;
import org.joinmastodon.android.events.NotificationDeletedEvent;
import org.joinmastodon.android.events.StatusDeletedEvent;
import org.joinmastodon.android.events.StatusUnpinnedEvent;
@@ -536,11 +537,12 @@ public class UiUtils{
}
public static void handleFollowRequest(Activity activity, Account account, String accountID, String notificationID, boolean accepted, Relationship relationship, Consumer<Relationship> resultCallback) {
public static void handleFollowRequest(Activity activity, Account account, String accountID, @Nullable String notificationID, boolean accepted, Relationship relationship, Consumer<Relationship> resultCallback) {
if (accepted) {
new AuthorizeFollowRequest(account.id).setCallback(new Callback<>() {
@Override
public void onSuccess(Relationship rel) {
E.post(new FollowRequestHandledEvent(accountID, true, account, rel));
resultCallback.accept(rel);
}
@@ -554,7 +556,8 @@ public class UiUtils{
new RejectFollowRequest(account.id).setCallback(new Callback<>() {
@Override
public void onSuccess(Relationship rel) {
E.post(new NotificationDeletedEvent(notificationID));
E.post(new FollowRequestHandledEvent(accountID, false, account, rel));
if (notificationID != null) E.post(new NotificationDeletedEvent(notificationID));
resultCallback.accept(rel);
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?colorBackgroundLightest"/>
<solid android:color="?colorBackgroundPopup"/>
<corners android:radius="10dp"/>
<padding android:top="8dp" android:bottom="8dp"/>
</shape>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true" android:drawable="@drawable/ic_fluent_arrow_repeat_all_24_regular"/>
<item android:state_enabled="true" android:drawable="@drawable/ic_fluent_arrow_repeat_all_24_selector"/>
<item android:drawable="@drawable/ic_fluent_arrow_repeat_all_off_24_regular"/>
</selector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M14.712 2.289L14.625 2.21c-0.392-0.31-0.964-0.285-1.327 0.078L13.22 2.376c-0.31 0.392-0.285 0.964 0.078 1.326l1.299 1.297H8.999L8.76 5.003C5.004 5.13 2 8.211 2 11.993c0 1.445 0.438 2.788 1.189 3.899C3.37 16.143 3.666 16.307 4 16.307c0.552 0 1-0.448 1-1 0-0.216-0.069-0.416-0.185-0.578L4.68 14.51C4.248 13.77 4 12.91 4 11.993c0-2.759 2.238-4.995 5-4.995h5.595l-1.297 1.297-0.078 0.087c-0.31 0.392-0.285 0.964 0.078 1.326 0.39 0.39 1.024 0.39 1.414 0l3.006-3.003 0.077-0.087c0.311-0.392 0.285-0.964-0.078-1.326l-3.005-3.003zm6.075 5.771C20.602 7.827 20.319 7.678 20 7.678c-0.552 0-1 0.448-1 1 0 0.208 0.064 0.4 0.172 0.56 0.523 0.79 0.828 1.737 0.828 2.755 0 2.76-2.238 4.996-5 4.996H9.416l1.294-1.292 0.083-0.095c0.281-0.361 0.28-0.871-0.006-1.23l-0.077-0.088-0.095-0.084c-0.362-0.28-0.873-0.278-1.232 0.006l-0.088 0.078-3.005 3.003-0.083 0.095c-0.281 0.361-0.28 0.872 0.006 1.231L6.289 18.7l3.005 3.003 0.095 0.084c0.392 0.304 0.96 0.277 1.32-0.084 0.362-0.362 0.388-0.933 0.077-1.326L10.71 20.29l-1.304-1.303h5.596l0.24-0.003C18.996 18.857 22 15.776 22 11.994c0-1.46-0.448-2.816-1.213-3.937V8.06z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

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

View File

@@ -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="M8.75 3.75C8.75 2.231 7.519 1 6 1S3.25 2.231 3.25 3.75 4.481 6.5 6 6.5s2.75-1.231 2.75-2.75zm-4.5 0C4.25 2.784 5.034 2 6 2s1.75 0.784 1.75 1.75S6.966 5.5 6 5.5 4.25 4.716 4.25 3.75zM2.5 7.5h4.183c-0.164 0.31-0.286 0.646-0.358 1H2.5C2.224 8.5 2 8.724 2 9v0.5c0 1.26 1.099 2.614 3.096 2.93-0.322 0.22-0.59 0.513-0.781 0.854C2.205 12.713 1 11.087 1 9.5V9c0-0.828 0.672-1.5 1.5-1.5zm5.379 0c0.504-0.61 1.267-1 2.121-1 0.854 0 1.617 0.39 2.121 1 0.24 0.29 0.42 0.629 0.525 1 0.068 0.238 0.104 0.49 0.104 0.75 0 1.07-0.611 1.997-1.503 2.452-0.355 0.18-0.754 0.287-1.177 0.297L10 12H9.93c-0.423-0.011-0.822-0.117-1.177-0.298C7.861 11.247 7.25 10.32 7.25 9.25c0-0.26 0.036-0.512 0.104-0.75 0.104-0.371 0.285-0.71 0.525-1zm0.54 1C8.31 8.727 8.25 8.982 8.25 9.25c0 0.714 0.428 1.328 1.04 1.6C9.509 10.947 9.749 11 10 11c0.252 0 0.492-0.053 0.71-0.15 0.612-0.272 1.04-0.886 1.04-1.6 0-0.268-0.06-0.523-0.168-0.75-0.246-0.516-0.737-0.894-1.322-0.98C10.175 7.506 10.088 7.5 10 7.5c-0.088 0-0.175 0.006-0.26 0.02C9.155 7.605 8.664 7.983 8.418 8.5zm7.266 4.784c-0.19-0.341-0.459-0.634-0.781-0.853C16.9 12.114 18 10.759 18 9.5V9c0-0.276-0.224-0.5-0.5-0.5h-3.825c-0.072-0.354-0.194-0.69-0.357-1H17.5C18.328 7.5 19 8.172 19 9v0.5c0 1.587-1.206 3.212-3.315 3.784zm-1.198 0.087C14.223 13.14 13.878 13 13.5 13h-7c-0.432 0-0.821 0.183-1.095 0.475C5.154 13.743 5 14.104 5 14.5V15c0 1.971 1.86 4 5 4 3.14 0 5-2.029 5-4v-0.5c0-0.45-0.198-0.854-0.513-1.13zM6 14.5C6 14.224 6.224 14 6.5 14h7c0.276 0 0.5 0.224 0.5 0.5V15c0 1.438-1.432 3-4 3s-4-1.562-4-3v-0.5zM14 1c1.519 0 2.75 1.231 2.75 2.75S15.519 6.5 14 6.5s-2.75-1.231-2.75-2.75S12.481 1 14 1zm0 1c-0.966 0-1.75 0.784-1.75 1.75S13.034 5.5 14 5.5s1.75-0.784 1.75-1.75S14.966 2 14 2z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M9.75 2c0.301 0 0.573 0.18 0.69 0.457l6.972 16.431C16.951 18.345 16.267 18 15.5 18c-0.031 0-0.063 0-0.094 0.002L13.92 14.5H5.58l-2.14 5.043c-0.161 0.381-0.601 0.56-0.983 0.397-0.381-0.161-0.559-0.602-0.397-0.983l7-16.5C9.177 2.18 9.45 2 9.75 2zm3.534 11L9.75 4.67 6.216 13h7.068zM12 20.5c0 0.828-0.672 1.5-1.5 1.5S9 21.328 9 20.5 9.672 19 10.5 19s1.5 0.672 1.5 1.5zm3.5 1.5c0.828 0 1.5-0.672 1.5-1.5S16.328 19 15.5 19 14 19.672 14 20.5s0.672 1.5 1.5 1.5zm5 0c0.828 0 1.5-0.672 1.5-1.5S21.328 19 20.5 19 19 19.672 19 20.5s0.672 1.5 1.5 1.5z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_fluent_person_add_24_regular" android:left="2dp" android:right="2dp" android:top="2dp" android:bottom="2dp"/>
<item android:width="14dp" android:height="14dp" android:gravity="top|right">
<shape android:shape="oval">
<stroke android:color="?android:colorPrimary" android:width="2dp"/>
<solid android:color="@color/primary_600"/>
</shape>
</item>
</layer-list>

View File

@@ -9,26 +9,29 @@
<ImageView
android:id="@+id/more"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="-6dp"
android:layout_marginRight="-8dp"
android:background="?android:selectableItemBackgroundBorderless"
android:scaleType="center"
android:tint="?android:textColorSecondary"
android:contentDescription="@string/more_options"
android:src="@drawable/ic_post_more" />
android:scaleType="center"
android:src="@drawable/ic_post_more"
android:tint="?android:textColorSecondary" />
<ImageView
android:id="@+id/visibility"
android:layout_width="24dp"
android:layout_height="20dp"
android:layout_alignParentEnd="true"
android:layout_below="@id/more"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginTop="-6dp"
android:layout_marginRight="6dp"
android:layout_toLeftOf="@id/more"
android:background="?android:selectableItemBackgroundBorderless"
android:scaleType="center"
android:tint="?android:textColorSecondary"
android:src="@drawable/ic_visibility" />
android:src="@drawable/ic_visibility"
android:tint="?android:textColorSecondary" />
<ImageView
android:id="@+id/avatar"
@@ -42,9 +45,9 @@
android:id="@+id/name_wrap"
android:layout_width="match_parent"
android:layout_height="24dp"
android:layout_toEndOf="@id/avatar"
android:layout_toStartOf="@id/more"
android:layout_marginEnd="8dp">
android:layout_marginEnd="8dp"
android:layout_toStartOf="@id/visibility"
android:layout_toEndOf="@id/avatar">
<TextView
android:id="@+id/name"
@@ -52,8 +55,8 @@
android:layout_height="24dp"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="@style/m3_title_medium"
android:textAlignment="viewStart"
android:textAppearance="@style/m3_title_medium"
tools:text="Eugen" />
<TextView
@@ -62,10 +65,10 @@
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="@style/m3_title_medium"
android:fontFamily="sans-serif"
android:singleLine="true"
android:textAlignment="viewStart"
android:textAppearance="@style/m3_title_medium"
tools:text="boosted your cat picture" />
</org.joinmastodon.android.ui.views.HeaderSubtitleLinearLayout>
@@ -74,8 +77,9 @@
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_below="@id/name_wrap"
android:layout_toEndOf="@id/avatar"
android:layout_marginEnd="8dp"
android:layout_toStartOf="@id/visibility"
android:layout_toEndOf="@id/avatar"
android:layoutDirection="locale"
android:orientation="horizontal">
@@ -94,6 +98,7 @@
android:layout_height="20dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:importantForAccessibility="no"
android:text="·"
android:textAppearance="@style/m3_title_small" />
@@ -101,8 +106,8 @@
android:id="@+id/timestamp"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:textAppearance="@style/m3_title_small"
android:singleLine="true"
android:textAppearance="@style/m3_title_small"
tools:text="3h" />
</org.joinmastodon.android.ui.views.HeaderSubtitleLinearLayout>

View File

@@ -2,15 +2,16 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="-6dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp">
android:layout_marginBottom="-12dp">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:paddingTop="16dp"
android:paddingBottom="6dp"
android:background="?android:selectableItemBackground"
android:textAppearance="@style/m3_title_small"
android:drawableStart="@drawable/ic_fluent_arrow_repeat_all_20_filled"
android:drawableTint="?android:textColorSecondary"

View File

@@ -14,9 +14,12 @@
<LinearLayout
android:id="@+id/spoiler_header"
android:orientation="vertical"
android:layout_marginTop="6dp"
android:layout_marginBottom="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<View
android:id="@+id/border_top"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="?attr/colorPollVoted"/>
@@ -24,18 +27,17 @@
<TextView
android:id="@+id/spoiler_title_inline"
android:paddingHorizontal="16dp"
android:paddingVertical="12dp"
android:paddingVertical="14dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="0dp"
android:textAppearance="@style/m3_title_medium"
android:background="?colorBackgroundLight"
tools:text="CW title"/>
<View
android:id="@+id/border_bottom"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginBottom="12dp"
android:background="?attr/colorPollVoted"/>
</LinearLayout>

View File

@@ -40,6 +40,7 @@
android:layout_marginTop="16dp"
android:textAppearance="@style/m3_title_small"
android:drawableStart="@drawable/ic_fluent_arrow_reply_20_filled"
tools:drawableEnd="@drawable/ic_fluent_earth_20_regular"
android:drawableTint="?android:textColorSecondary"
android:drawablePadding="6dp"
android:singleLine="true"

View File

@@ -28,8 +28,8 @@
<org.joinmastodon.android.ui.views.CoverImageView
android:id="@+id/cover"
android:layout_width="match_parent"
android:layout_height="229dp"
android:background="#808080"
android:layout_height="200dp"
android:background="?colorWindowBackground"
android:contentDescription="@string/profile_header"
android:scaleType="centerCrop"/>

View File

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

View File

@@ -367,5 +367,8 @@
<string name="install_update">Instal·lar</string>
<string name="privacy_policy_title">Mastodon i la teva privacitat</string>
<string name="privacy_policy_subtitle">Tot i que l\'aplicació Mastodon no recull cap dada, el servidor mitjançant el qual et registres pot tenir una política diferent. Pren un minut per revisar i acceptar la política de privadesa de l\'aplicació Mastodon i la política de privadesa del teu servidor.</string>
<string name="i_agree">Hi estic d\'acord</string>
<string name="i_agree">D\'acord</string>
<!-- Missing strings -->
<string name="bookmarks">Marcadors</string>
<string name="pinned_posts">Fixat</string>
</resources>

View File

@@ -406,11 +406,13 @@
<string name="no_update_available">Kein Update verfügbar</string>
<string name="list_timelines">Listen</string>
<string name="favorited_posts">Favorisierte Beiträge</string>
<string name="follow_requests">Folgeanfragen</string>
<string name="accept_follow_request">Folgeanfrage akzeptieren</string>
<string name="reject_follow_request">Folgeanfrage ablehnen</string>
<string name="lists_with_user">Listen mit %s</string>
<string name="privacy_policy_title">Mastodon und Ihre Privatsphäre</string>
<string name="privacy_policy_subtitle">Obwohl die Mastodon-App keine Daten sammelt, kann der Server, über den Sie sich anmelden, eine andere Richtlinie haben. Nehmen Sie sich eine Minute Zeit, um die Mastodon-Datenschutzrichtlinien und die Datenschutzrichtlinien Ihres Servers zu lesen und zu akzeptieren.</string>
<string name="i_agree">Ich stimme zu</string>
<string name="settings_always_reveal_content_warnings">Inhaltswarnungen immer ausklappen</string>
<string name="settings_always_reveal_content_warnings">Inhaltswarnungen immer ausklappen</string>
<string name="disable_marquee">Laufschrift in Titelleisten deaktivieren</string>
</resources>

View File

@@ -4,6 +4,7 @@
<attr name="colorSecondary" format="color"/>
<attr name="colorBackgroundLight" format="color"/>
<attr name="colorBackgroundLightest" format="color"/>
<attr name="colorBackgroundPopup" format="color"/>
<attr name="colorDarkIcon" format="color"/>
<attr name="colorPollMostVoted" format="color"/>
<attr name="colorPollVoted" format="color"/>

View File

@@ -18,4 +18,5 @@
<item name="notifications_all" type="id"/>
<item name="notifications_mentions" type="id"/>
<item name="notifications_posts" type="id"/>
</resources>

View File

@@ -416,9 +416,12 @@
<string name="i_agree">I Agree</string>
<string name="list_timelines">Lists</string>
<string name="favorited_posts">Favorited posts</string>
<string name="follow_requests">Follow requests</string>
<string name="accept_follow_request">Accept follow request</string>
<string name="reject_follow_request">Reject follow request</string>
<string name="lists_with_user">Lists with %s</string>
<string name="empty_list">This list is empty</string>
<string name="instance_signup_closed">This server does not accept new registrations.</string>
<string name="settings_always_reveal_content_warnings">Always reveal content warnings</string>
<string name="disable_marquee">Disable scrolling text in title bars</string>
</resources>

View File

@@ -22,6 +22,7 @@
<item name="colorSecondary">#E9EDF2</item>
<item name="colorBackgroundLight">@color/gray_50</item>
<item name="colorBackgroundLightest">@color/gray_25</item>
<item name="colorBackgroundPopup">?colorBackgroundLightest</item>
<item name="colorDarkIcon">@color/gray_900</item>
<item name="colorWindowBackground">@color/white</item>
<item name="android:statusBarColor">@color/gray_50</item>
@@ -67,6 +68,7 @@
<item name="colorSecondary">#E9EDF2</item>
<item name="colorBackgroundLight">@color/gray_700</item>
<item name="colorBackgroundLightest">@color/gray_900</item>
<item name="colorBackgroundPopup">?colorBackgroundLightest</item>
<item name="colorDarkIcon">@color/gray_25</item>
<item name="colorWindowBackground">@color/gray_800</item>
<item name="android:statusBarColor">@color/gray_800</item>
@@ -93,7 +95,17 @@
</style>
<style name="Theme.Mastodon.Dark.TrueBlack">
<item name="colorWindowBackground">#000</item>
<item name="android:navigationBarColor">@color/black</item>
<item name="android:colorBackground">@color/black</item>
<item name="android:statusBarColor">@color/black</item>
<item name="android:actionBarTheme">@style/Theme.Mastodon.Toolbar.Dark.TrueBlack</item>
<item name="colorBackgroundLight">@color/black</item>
<item name="colorWindowBackground">@color/black</item>
<item name="colorButtonText">@color/black</item>
<item name="colorPollVoted">@color/gray_800</item>
<item name="colorSearchField">@color/gray_900</item>
<item name="colorBackgroundLightest">@color/black</item>
<item name="colorBackgroundPopup">@color/gray_900</item>
</style>
<style name="Theme.Mastodon.AutoLightDark" parent="Theme.Mastodon.Light"/>
@@ -111,6 +123,10 @@
<item name="android:textColorSecondary">@color/gray_50</item>
</style>
<style name="Theme.Mastodon.Toolbar.Dark.TrueBlack" parent="android:ThemeOverlay.Material.Dark.ActionBar">
<item name="android:colorPrimary">@color/black</item>
</style>
<style name="Theme.Mastodon.Toolbar.Profile">
<item name="android:textColorPrimary">@color/gray_50</item>
<item name="android:textColorSecondary">@color/gray_50</item>