Compare commits
95 Commits
v1.1.4+for
...
v1.1.4+for
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adf628de44 | ||
|
|
7e4169644d | ||
|
|
e42988b51e | ||
|
|
7c2fd2b734 | ||
|
|
569e4e13ea | ||
|
|
b7b5785a97 | ||
|
|
95d6e51ae0 | ||
|
|
fc43d919e5 | ||
|
|
a8d19529cd | ||
|
|
c2c44ab25e | ||
|
|
96a56873c3 | ||
|
|
0100967597 | ||
|
|
5769b378e2 | ||
|
|
2c61551e5c | ||
|
|
c9b11a23a1 | ||
|
|
456d05a489 | ||
|
|
8ca7d9d7b4 | ||
|
|
2321549dca | ||
|
|
5ae96328d5 | ||
|
|
dbf3da3629 | ||
|
|
9a351de9b4 | ||
|
|
f871fa6743 | ||
|
|
85f6441a56 | ||
|
|
dd8354f91b | ||
|
|
21d78de3cc | ||
|
|
158af27309 | ||
|
|
b0446af54a | ||
|
|
064f0155f6 | ||
|
|
fbf4522260 | ||
|
|
8918bd5ce2 | ||
|
|
187693883c | ||
|
|
40868f90f9 | ||
|
|
1f89acec34 | ||
|
|
d75ce99a68 | ||
|
|
0a8846fa2a | ||
|
|
5253e2e24a | ||
|
|
3565223611 | ||
|
|
2343d7a046 | ||
|
|
8256fbadb6 | ||
|
|
b898dc010e | ||
|
|
de369633ec | ||
|
|
3f075eab13 | ||
|
|
96e5f854a5 | ||
|
|
0f5211f718 | ||
|
|
480915f377 | ||
|
|
15d559ad6a | ||
|
|
3b7a6e9385 | ||
|
|
314a15973c | ||
|
|
e99917945a | ||
|
|
d3ba8a4d0f | ||
|
|
9162b31ac1 | ||
|
|
51a80f3e03 | ||
|
|
a8c49b59f6 | ||
|
|
c79942c13f | ||
|
|
da121495c0 | ||
|
|
2d710cb558 | ||
|
|
f0a51a15a9 | ||
|
|
ae68b1e646 | ||
|
|
5da58d7834 | ||
|
|
24023e9843 | ||
|
|
29d9871e77 | ||
|
|
32d182f03a | ||
|
|
da4f54751e | ||
|
|
393c538464 | ||
|
|
ddcf61dc95 | ||
|
|
5b70c035d2 | ||
|
|
289564381d | ||
|
|
3fbcce8570 | ||
|
|
505b755df6 | ||
|
|
4b9618cad5 | ||
|
|
e7b0e022d6 | ||
|
|
7ffc58d52c | ||
|
|
30151cafc2 | ||
|
|
d0ce157069 | ||
|
|
be7d65989d | ||
|
|
08656a2678 | ||
|
|
b0f76739ba | ||
|
|
a09efd084e | ||
|
|
0bb85d71e4 | ||
|
|
7248ab9801 | ||
|
|
cd4d83e139 | ||
|
|
f1f7c1341c | ||
|
|
a7ab6945f9 | ||
|
|
896583aeec | ||
|
|
eccda2ed61 | ||
|
|
b819c757b7 | ||
|
|
b1f7acb76b | ||
|
|
c23df0c065 | ||
|
|
e514e24036 | ||
|
|
fdf9417c5b | ||
|
|
859468a03f | ||
|
|
4623d403f7 | ||
|
|
038c3c0fb9 | ||
|
|
7f0e2bf03d | ||
|
|
521ba8766c |
77
README.md
77
README.md
@@ -6,8 +6,11 @@
|
||||
|
||||
[](https://github.com/sk22/mastodos/releases/latest/download/mastodos.apk)
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
|
||||
## Key features
|
||||
|
||||
### **Unlisted posting**
|
||||
@@ -52,38 +55,70 @@ To install this app on your Android device, download the [latest release from Gi
|
||||
|
||||
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!
|
||||
|
||||
### Other sources
|
||||
|
||||
* **[Izzy's F-Droid repository](https://apt.izzysoft.de/fdroid/repo)**: https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk
|
||||
|
||||
---
|
||||
|
||||
## Release variants
|
||||
|
||||
All downloads can be found on the [Releases](https://github.com/sk22/mastodos/releases) page.
|
||||
|
||||
**`mastodos.apk`**
|
||||
|
||||
Variant with an integrated updater. If you download Mastodos from here (and not from an app store), just download the regular `mastodos.apk`.
|
||||
|
||||
**`mastodon-1234abc.apk`**
|
||||
|
||||
This is an **unmodified version** of the official [Mastodon for Android](https://github.com/mastodon/mastodon-android) app the respective Mastodos release is based on. Should you find any bugs in Mastodos (which you will), try to see if it occurs with this variant, too. The last 7 digits of the file name are important to know which version of the official app you're using.
|
||||
|
||||
<!-- **`mastodon-fdroid.apk`**
|
||||
|
||||
Variant without the integrated updater. This is the variant to be published to F-Droid.org where an integrated updater is not necessary. -->
|
||||
|
||||
---
|
||||
|
||||
## Detailed changes
|
||||
|
||||
### 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)
|
||||
* [Clickable reply/boost line above posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:clickable-boost-reply-line)
|
||||
* [Long-click to copy username from profile](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:feature/copy-username)
|
||||
|
||||
### 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))
|
||||
* [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)
|
||||
|
||||
### Visual
|
||||
|
||||
* [Custom extended footer redesign](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:compact-extended-footer)
|
||||
* [Improvements to the true black mode](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:true-black-improvements)
|
||||
* [Profile header tweaks](https://github.com/mastodon/mastodon-android/compare/master...sk22:mastodos:ui/profile-header-tweaks)
|
||||
|
||||
### Branding
|
||||
|
||||
@@ -91,7 +126,7 @@ Mastodos makes use of [Mastodon for Android](https://github.com/mastodon/mastodo
|
||||
* Pink primary color
|
||||
* Custom icon: Modulate upstream icon using ImageMagick
|
||||
```bash
|
||||
mogrify -modulate 90,100,140 mastodon/src/main/res/mipmap-*/ic_launcher*.png
|
||||
mogrify -modulate 90,100,140 mastodon/src/main/res/mipmap-*/ic_launcher*.png mastodon/src/main/ic_launcher-playstore.png
|
||||
```
|
||||
|
||||
## Building
|
||||
@@ -105,3 +140,7 @@ As this app is using Java 17 features, you need JDK 17 or newer to build it. Oth
|
||||
## License
|
||||
|
||||
This project is released under the [GPL-3 License](./LICENSE).
|
||||
|
||||
## Links
|
||||
|
||||
<a rel="me" href="https://floss.social/@mastodos">@mastodos@floss.social</a>
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
title: Mastodos
|
||||
theme: minima
|
||||
layout: default
|
||||
|
||||
17
_layouts/default.html
Normal file
17
_layouts/default.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Mastodos</title>
|
||||
<link rel="icon" href="mastodon/src/main/res/mipmap-mdpi/ic_launcher_round.png">
|
||||
<link rel="me" href="https://floss.social/@mastodon">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown.min.css">
|
||||
</head>
|
||||
<body class="markdown-body">
|
||||
<div style="margin: 0 auto; max-width: 45rem; padding: 2rem 1rem">
|
||||
{{ content }}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
3
get-upstream-commit.sh
Executable file
3
get-upstream-commit.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
git rev-parse --short --verify upstream/master
|
||||
BIN
img/banner.png
Normal file
BIN
img/banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 501 KiB |
@@ -9,8 +9,8 @@ android {
|
||||
applicationId "org.joinmastodon.android.sk"
|
||||
minSdk 23
|
||||
targetSdk 33
|
||||
versionCode 40
|
||||
versionName "1.1.4+fork.40"
|
||||
versionCode 47
|
||||
versionName "1.1.4+fork.47"
|
||||
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,13 @@ android {
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
// minifyEnabled true
|
||||
// shrinkResources true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
debug{
|
||||
debuggable true
|
||||
versionNameSuffix '-debug'
|
||||
}
|
||||
appcenterPrivateBeta{
|
||||
initWith release
|
||||
|
||||
@@ -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")){
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/>
|
||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
|
||||
<permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 48 KiB |
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -73,7 +73,7 @@ public class CacheController{
|
||||
status.hasGapAfter=((flags & POST_FLAG_GAP_AFTER)!=0);
|
||||
newMaxID=status.id;
|
||||
for(Filter filter:filters){
|
||||
if(filter.matches(status.getContentStatus().content))
|
||||
if(filter.matches(status))
|
||||
continue outer;
|
||||
}
|
||||
result.add(status);
|
||||
@@ -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();
|
||||
@@ -145,7 +146,7 @@ public class CacheController{
|
||||
newMaxID=ntf.id;
|
||||
if(ntf.status!=null){
|
||||
for(Filter filter:filters){
|
||||
if(filter.matches(ntf.status.getContentStatus().content))
|
||||
if(filter.matches(ntf.status))
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
@@ -159,21 +160,21 @@ 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){
|
||||
callback.onSuccess(new PaginatedResponse<>(result.stream().filter(ntf->{
|
||||
if(ntf.status!=null){
|
||||
for(Filter filter:filters){
|
||||
if(filter.matches(ntf.status.getContentStatus().content)){
|
||||
if(filter.matches(ntf.status)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -268,7 +268,7 @@ public class HomeTimelineFragment extends StatusListFragment{
|
||||
if(idsBelowGap.contains(s.id))
|
||||
break;
|
||||
for(Filter filter:filters){
|
||||
if(filter.matches(s.getContentStatus().content)){
|
||||
if(filter.matches(s)){
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import static android.content.Context.CLIPBOARD_SERVICE;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Outline;
|
||||
@@ -14,6 +19,8 @@ import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ImageSpan;
|
||||
@@ -31,9 +38,11 @@ import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
@@ -435,15 +444,25 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
ssb.append(AccountSessionManager.getInstance().getAccount(accountID).domain);
|
||||
}
|
||||
ssb.append(" ");
|
||||
Drawable lock=username.getResources().getDrawable(R.drawable.ic_fluent_lock_closed_20_filled, getActivity().getTheme()).mutate();
|
||||
Drawable lock=username.getResources().getDrawable(R.drawable.ic_lock, getActivity().getTheme()).mutate();
|
||||
lock.setBounds(0, 0, lock.getIntrinsicWidth(), lock.getIntrinsicHeight());
|
||||
lock.setTint(username.getCurrentTextColor());
|
||||
ssb.append(getString(R.string.manually_approves_followers), new ImageSpan(lock, ImageSpan.ALIGN_BOTTOM), 0);
|
||||
ssb.append(getString(R.string.manually_approves_followers), new ImageSpan(lock, ImageSpan.ALIGN_BASELINE), 0);
|
||||
username.setText(ssb);
|
||||
}else{
|
||||
// noinspection SetTextI18n
|
||||
username.setText('@'+account.acct+(isSelf ? ('@'+AccountSessionManager.getInstance().getAccount(accountID).domain) : ""));
|
||||
}
|
||||
username.setOnLongClickListener(l->{
|
||||
ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(CLIPBOARD_SERVICE);
|
||||
Vibrator v = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText("Username", '@'+account.acct+'@'+AccountSessionManager.getInstance().getAccount(accountID).domain);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
Toast.makeText(getActivity(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) v.vibrate(VibrationEffect.createOneShot(50, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||
else v.vibrate(50);
|
||||
return true;
|
||||
});
|
||||
CharSequence parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
||||
if(TextUtils.isEmpty(parsedBio)){
|
||||
bio.setVisibility(View.GONE);
|
||||
@@ -648,7 +667,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
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));
|
||||
if (getActivity() != null) 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){
|
||||
|
||||
@@ -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->{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ public class ThreadFragment extends StatusListFragment{
|
||||
return statuses;
|
||||
return statuses.stream().filter(status->{
|
||||
for(Filter filter:filters){
|
||||
if(filter.matches(status.getContentStatus().content))
|
||||
if(filter.matches(status))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -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)){
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -50,6 +50,10 @@ public class Filter extends BaseModel{
|
||||
return pattern.matcher(text).find();
|
||||
}
|
||||
|
||||
public boolean matches(Status status){
|
||||
return matches(status.getContentStatus().getStrippedText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "Filter{"+
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.api.ObjectValidationException;
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.time.Instant;
|
||||
@@ -57,6 +58,7 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||
|
||||
public transient boolean spoilerRevealed;
|
||||
public transient boolean hasGapAfter;
|
||||
private transient String strippedText;
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
@@ -134,4 +136,10 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||
public Status getContentStatus(){
|
||||
return reblog!=null ? reblog : this;
|
||||
}
|
||||
|
||||
public String getStrippedText(){
|
||||
if(strippedText==null)
|
||||
strippedText=HtmlParser.strip(content);
|
||||
return strippedText;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,8 @@ public class StatusFilterPredicate implements Predicate<Status>{
|
||||
|
||||
@Override
|
||||
public boolean test(Status status){
|
||||
CharSequence content=status.getContentStatus().content;
|
||||
for(Filter filter:filters){
|
||||
if(filter.matches(content))
|
||||
if(filter.matches(status))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -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>
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_selected="true" android:drawable="@drawable/ic_fluent_arrow_repeat_all_24_filled"/>
|
||||
<item android:state_enabled="true" android:drawable="@drawable/ic_fluent_arrow_repeat_all_24_regular"/>
|
||||
<item android:drawable="@drawable/ic_fluent_arrow_repeat_all_off_24_regular"/>
|
||||
</selector>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
6
mastodon/src/main/res/drawable/ic_lock.xml
Normal file
6
mastodon/src/main/res/drawable/ic_lock.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="16dp" android:height="16dp" android:viewportWidth="16" android:viewportHeight="16">
|
||||
<group android:translateX="-2" android:translateY="-1">
|
||||
<path android:pathData="M10 2c1.657 0 3 1.343 3 3v1h1c1.105 0 2 0.895 2 2v7c0 1.105-0.895 2-2 2H6c-1.105 0-2-0.895-2-2V8c0-1.105 0.895-2 2-2h1V5c0-1.657 1.343-3 3-3zm0 8.5c-0.552 0-1 0.448-1 1s0.448 1 1 1 1-0.448 1-1-0.448-1-1-1zM10 4C9.448 4 9 4.448 9 5v1h2V5c0-0.552-0.448-1-1-1z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -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>
|
||||
|
||||
@@ -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_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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
<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="?profileHeaderBackground"
|
||||
android:contentDescription="@string/profile_header"
|
||||
android:scaleType="centerCrop"/>
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/follows_you"
|
||||
@@ -54,8 +54,8 @@
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/avatar_border"
|
||||
android:layout_width="102dp"
|
||||
android:layout_height="102dp"
|
||||
android:layout_width="112dp"
|
||||
android:layout_height="112dp"
|
||||
android:layout_below="@id/cover"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginTop="-40dp"
|
||||
@@ -65,8 +65,8 @@
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="98dp"
|
||||
android:layout_height="98dp"
|
||||
android:layout_width="108dp"
|
||||
android:layout_height="108dp"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="@string/profile_picture"
|
||||
@@ -234,22 +234,27 @@
|
||||
android:layout_below="@id/avatar_border"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginTop="17dp"
|
||||
android:layout_marginBottom="0.4dp"
|
||||
android:layout_toStartOf="@id/profile_action_btn_wrap"
|
||||
android:textAppearance="@style/m3_headline_small"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/m3_headline_small"
|
||||
tools:text="Eugen" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_below="@id/name"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_toStartOf="@id/profile_action_btn_wrap"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:textAppearance="@style/m3_title_medium"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:text="\@Gargron"/>
|
||||
tools:text="\@Gargron" />
|
||||
|
||||
<org.joinmastodon.android.ui.views.LinkedTextView
|
||||
android:id="@+id/bio"
|
||||
@@ -257,7 +262,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/username"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:textAppearance="@style/m3_body_large"
|
||||
android:textSize="16sp"
|
||||
|
||||
8
mastodon/src/main/res/menu/notifications.xml
Normal file
8
mastodon/src/main/res/menu/notifications.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_requests"
|
||||
android:icon="@drawable/ic_follow_requests_24_badged"
|
||||
android:showAsAction="always"
|
||||
android:title="@string/follow_requests" />
|
||||
</menu>
|
||||
@@ -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>
|
||||
|
||||
@@ -406,11 +406,14 @@
|
||||
<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>
|
||||
<string name="copied_to_clipboard">In die Zwischenablage kopiert</string>
|
||||
</resources>
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
</plurals>
|
||||
<string name="posts">게시물</string>
|
||||
<string name="posts_and_replies">게시물과 답장</string>
|
||||
<string name="pinned_posts">고정됨</string>
|
||||
<string name="media">미디어</string>
|
||||
<string name="profile_about">정보</string>
|
||||
<string name="button_follow">팔로우</string>
|
||||
@@ -108,9 +109,20 @@
|
||||
<string name="action_vote">투표</string>
|
||||
<string name="tap_to_reveal">눌러서 확인</string>
|
||||
<string name="delete">삭제</string>
|
||||
<string name="delete_and_redraft">삭제하고 다시 쓰기</string>
|
||||
<string name="confirm_delete_title">게시물 삭제</string>
|
||||
<string name="confirm_delete_and_redraft_title">게시물 삭제하고 다시 쓰기</string>
|
||||
<string name="confirm_delete">정말로 이 게시물을 삭제하시겠습니까?</string>
|
||||
<string name="confirm_delete_and_redraft">정말로 이 게시물을 삭제하고 다시 쓰시겠습니까?</string>
|
||||
<string name="deleting">삭제 중…</string>
|
||||
<string name="pin_post">고정</string>
|
||||
<string name="confirm_pin_post_title">게시물 고정</string>
|
||||
<string name="confirm_pin_post">정말로 이 게시물을 고정하시겠습니까?</string>
|
||||
<string name="pinning">고정 중…</string>
|
||||
<string name="unpin_post">고정 해제</string>
|
||||
<string name="confirm_unpin_post_title">게시물 고정 해제</string>
|
||||
<string name="confirm_unpin_post">정말로 이 게시물을 고정 해제하시겠습니까?</string>
|
||||
<string name="unpinning">고정 해제 중…</string>
|
||||
<string name="notification_channel_audio_player">오디오 재생</string>
|
||||
<string name="play">재생</string>
|
||||
<string name="pause">일시정지</string>
|
||||
@@ -189,6 +201,7 @@
|
||||
<string name="compose_hint">무슨 생각을 하고 있는지 입력하거나 붙여넣으세요</string>
|
||||
<string name="content_warning">열람 주의</string>
|
||||
<string name="add_image_description">이미지 설명 추가…</string>
|
||||
<string name="image_description">이미지 설명</string>
|
||||
<string name="retry_upload">업로드 재시도</string>
|
||||
<string name="edit_image">이미지 편집</string>
|
||||
<string name="save">저장</string>
|
||||
@@ -196,6 +209,7 @@
|
||||
<string name="alt_text_subtitle">대체 텍스트는 저시력자나 시각장애인들에게 당신의 사진을 설명합니다. 문맥을 파악할 수 있는 설명들만 넣도록 하세요.</string>
|
||||
<string name="alt_text_hint">예시: 강아지가 의심스러운 가는 눈초리로 카메라를 쳐다보고 있습니다.</string>
|
||||
<string name="visibility_public">공개</string>
|
||||
<string name="visibility_unlisted">타임라인에 비표시</string>
|
||||
<string name="visibility_followers_only">팔로워 전용</string>
|
||||
<string name="visibility_private">멘션한 사람만</string>
|
||||
<string name="search_all">모두</string>
|
||||
@@ -221,8 +235,12 @@
|
||||
<string name="theme_dark">어두움</string>
|
||||
<string name="theme_true_black">진정한 검정 모드</string>
|
||||
<string name="settings_behavior">동작</string>
|
||||
<string name="settings_show_replies">답장 표시</string>
|
||||
<string name="settings_show_boosts">리블로그 표시</string>
|
||||
<string name="settings_load_new_posts">자동으로 새 포스트 불러오기</string>
|
||||
<string name="settings_gif">아바타와 에모지의 애니메이션을 재생</string>
|
||||
<string name="settings_custom_tabs">앱내 브라우저 사용</string>
|
||||
<string name="settings_show_interaction_counts">상호 작용 수 표시</string>
|
||||
<string name="settings_notifications">알림</string>
|
||||
<string name="notify_me_when">알림을 보낼 조건</string>
|
||||
<string name="notify_anyone">누구든</string>
|
||||
@@ -255,8 +273,11 @@
|
||||
<string name="button_reblog">리블로그</string>
|
||||
<string name="button_favorite">좋아요</string>
|
||||
<string name="button_share">공유</string>
|
||||
<string name="button_bookmark">보관</string>
|
||||
<string name="bookmarks">보관함</string>
|
||||
<string name="media_no_description">설명이 없는 미디어</string>
|
||||
<string name="add_media">미디어 추가</string>
|
||||
<string name="mark_media_as_sensitive">미디어를 민감함으로 설정하기</string>
|
||||
<string name="add_poll">투표 추가</string>
|
||||
<string name="emoji">에모지</string>
|
||||
<string name="post_visibility">게시물 공개범위</string>
|
||||
@@ -269,6 +290,8 @@
|
||||
<string name="open_in_browser">브라우저에서 열기</string>
|
||||
<string name="hide_boosts_from_user">%s의 리블로그를 숨기기</string>
|
||||
<string name="show_boosts_from_user">%s의 리블로그를 보이기</string>
|
||||
<string name="user_post_notifications_on">%s의 게시물 알림 켜기</string>
|
||||
<string name="user_post_notifications_off">%s의 게시물 알림 끄기</string>
|
||||
<string name="signup_reason">가입하려는 이유가 무엇인가요?</string>
|
||||
<string name="signup_reason_note">이 정보는 우리가 심사를 하는 데에 참고할 수 있습니다.</string>
|
||||
<string name="clear">모두 지우기</string>
|
||||
@@ -283,11 +306,13 @@
|
||||
<string name="file_saved">파일 저장됨</string>
|
||||
<string name="downloading">다운로드중…</string>
|
||||
<string name="no_app_to_handle_action">이 동작을 처리할 앱이 없습니다</string>
|
||||
<string name="local_timeline">커뮤니티</string>
|
||||
<string name="local_timeline">로컬</string>
|
||||
<string name="federated_timeline">연합</string>
|
||||
<string name="trending_posts_info_banner">당신이 속한 마스토돈 서버에서 관심을 끌고 있는 게시물들입니다.</string>
|
||||
<string name="trending_hashtags_info_banner">당신이 속한 마스토돈 서버에서 관심을 끌고 있는 해시태그들입니다.</string>
|
||||
<string name="trending_links_info_banner">당신이 속한 마스토돈 서버에서 가장 많이 공유되고 있는 소식들입니다.</string>
|
||||
<string name="local_timeline_info_banner">당신과 같은 마스토돈 서버에 존재하는 사람들이 공유한 최신 게시물들입니다.</string>
|
||||
<string name="federated_timeline_info_banner">당신이 속한 연합에 존재하는 사람들이 공유한 최신 게시물들입니다.</string>
|
||||
<string name="dismiss">지우기</string>
|
||||
<string name="see_new_posts">새 게시물 보기</string>
|
||||
<string name="load_missing_posts">빈 게시물 불러오기</string>
|
||||
@@ -356,6 +381,18 @@
|
||||
<!-- %s is file size -->
|
||||
<string name="download_update">다운로드 (%s)</string>
|
||||
<string name="install_update">설치</string>
|
||||
<string name="check_for_update">업데이트 확인</string>
|
||||
<string name="no_update_available">사용 가능한 업데이트 없음</string>
|
||||
<string name="privacy_policy_title">마스토돈 및 개인정보</string>
|
||||
<string name="i_agree">동의합니다</string>
|
||||
<string name="list_timelines">리스트</string>
|
||||
<string name="favorited_posts">좋아요</string>
|
||||
<string name="follow_requests">팔로우 요청</string>
|
||||
<string name="accept_follow_request">팔로우 요청 허가</string>
|
||||
<string name="reject_follow_request">팔로우 요청 거부</string>
|
||||
<string name="lists_with_user">%s 님이 포함된 리스트</string>
|
||||
<string name="empty_list">리스트에 아무 것도 없습니다</string>
|
||||
<string name="instance_signup_closed">이 서버는 가입이 막혀 있는 상태입니다.</string>
|
||||
<string name="settings_always_reveal_content_warnings">열람주의 툿을 항상 펼치기</string>
|
||||
<string name="disable_marquee">제목 표시줄의 텍스트 스크롤 비활성화</string>
|
||||
</resources>
|
||||
|
||||
@@ -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"/>
|
||||
@@ -15,6 +16,7 @@
|
||||
<attr name="colorSearchHint" format="color"/>
|
||||
<attr name="colorTabInactive" format="color"/>
|
||||
<attr name="colorAccentLightest" format="color"/>
|
||||
<attr name="profileHeaderBackground" format="color"/>
|
||||
|
||||
<attr name="primaryLargeButtonStyle" format="reference"/>
|
||||
<attr name="secondaryLargeButtonStyle" format="reference"/>
|
||||
|
||||
@@ -18,4 +18,5 @@
|
||||
|
||||
<item name="notifications_all" type="id"/>
|
||||
<item name="notifications_mentions" type="id"/>
|
||||
<item name="notifications_posts" type="id"/>
|
||||
</resources>
|
||||
@@ -416,9 +416,13 @@
|
||||
<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>
|
||||
<string name="copied_to_clipboard">Copied to clipboard</string>
|
||||
</resources>
|
||||
|
||||
@@ -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>
|
||||
@@ -35,6 +36,7 @@
|
||||
<item name="colorSearchHint">@color/gray_600</item>
|
||||
<item name="colorTabInactive">@color/gray_400</item>
|
||||
<item name="colorAccentLightest">@color/primary_100</item>
|
||||
<item name="profileHeaderBackground">@color/gray_500</item>
|
||||
|
||||
<item name="buttonBackground">@drawable/bg_button_primary_dark_on_light</item>
|
||||
<item name="android:editTextBackground">@drawable/bg_edittext_light</item>
|
||||
@@ -67,6 +69,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>
|
||||
@@ -78,6 +81,7 @@
|
||||
<item name="colorAccentLight">@color/primary_600</item>
|
||||
<item name="colorAccentLightest">@color/primary_800</item>
|
||||
<item name="colorTabInactive">@color/gray_400</item>
|
||||
<item name="profileHeaderBackground">?colorWindowBackground</item>
|
||||
|
||||
<!-- TODO dark colors -->
|
||||
<item name="colorSearchField">@color/gray_700</item>
|
||||
@@ -93,7 +97,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 +125,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>
|
||||
|
||||
@@ -1,43 +1,40 @@
|
||||
<?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="bn-BD"/>
|
||||
<locale android:name="bs" />
|
||||
<locale android:name="ca" />
|
||||
<locale android:name="cs" />
|
||||
<locale android:name="de" />
|
||||
<locale android:name="gd-GB"/>
|
||||
<locale android:name="el" />
|
||||
<locale android:name="es" />
|
||||
<locale android:name="eu" />
|
||||
<locale android:name="fi" />
|
||||
<locale android:name="fr" />
|
||||
<loacle android:name="sl"/>
|
||||
<locale android:name="gl" />
|
||||
<locale android:name="hi-IN"/>
|
||||
<locale android:name="hr" />
|
||||
<locale android:name="hy" />
|
||||
<locale android:name="id"/>
|
||||
<locale android:name="it" />
|
||||
<locale android:name="iw" />
|
||||
<locale android:name="ja" />
|
||||
<locale android:name="kab" />
|
||||
<locale android:name="ko" />
|
||||
<locale android:name="nl"/>
|
||||
<locale android:name="oc" />
|
||||
<locale android:name="pl" />
|
||||
<locale android:name="pt-BR" />
|
||||
<locale android:name="pt-PT" />
|
||||
<locale android:name="ro"/>
|
||||
<locale android:name="ru" />
|
||||
<locale android:name="si-LK"/>
|
||||
<locale android:name="sl"/>
|
||||
<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 android:name="ar-SA"/>
|
||||
<locale android:name="bn-BD"/>
|
||||
<locale android:name="bs-BA"/>
|
||||
<locale android:name="ca-ES"/>
|
||||
<locale android:name="cs-CZ"/>
|
||||
<locale android:name="de-DE"/>
|
||||
<locale android:name="el-GR"/>
|
||||
<locale android:name="en"/>
|
||||
<locale android:name="es-ES"/>
|
||||
<locale android:name="eu-ES"/>
|
||||
<locale android:name="fi-FI"/>
|
||||
<locale android:name="fr-FR"/>
|
||||
<locale android:name="gd-GB"/>
|
||||
<locale android:name="gl-ES"/>
|
||||
<locale android:name="hi-IN"/>
|
||||
<locale android:name="hr-HR"/>
|
||||
<locale android:name="hy-AM"/>
|
||||
<locale android:name="in-ID"/>
|
||||
<locale android:name="it-IT"/>
|
||||
<locale android:name="iw-IL"/>
|
||||
<locale android:name="ja-JP"/>
|
||||
<locale android:name="kab"/>
|
||||
<locale android:name="ko-KR"/>
|
||||
<locale android:name="nl-NL"/>
|
||||
<locale android:name="oc-FR"/>
|
||||
<locale android:name="pl-PL"/>
|
||||
<locale android:name="pt-BR"/>
|
||||
<locale android:name="pt-PT"/>
|
||||
<locale android:name="ru-RU"/>
|
||||
<locale android:name="si-LK"/>
|
||||
<locale android:name="sv-SE"/>
|
||||
<locale android:name="th-TH"/>
|
||||
<locale android:name="tr-TR"/>
|
||||
<locale android:name="uk-UA"/>
|
||||
<locale android:name="vi-VN"/>
|
||||
<locale android:name="zh-CN"/>
|
||||
<locale android:name="zh-TW"/>
|
||||
</locale-config>
|
||||
44
tools/GenerateLocaleConfig.java
Normal file
44
tools/GenerateLocaleConfig.java
Normal file
@@ -0,0 +1,44 @@
|
||||
// Run: java tools/GenerateLocaleConfig.java
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
public class GenerateLocaleConfig{
|
||||
public static void main(String[] args) throws IOException{
|
||||
File dir=new File("../mastodon/src/main/res/");
|
||||
if(!dir.exists())
|
||||
dir=new File("mastodon/src/main/res");
|
||||
if(!dir.exists())
|
||||
throw new RuntimeException("Please run from project directory (can't find mastodon/src/main/res)");
|
||||
|
||||
ArrayList<String> locales=new ArrayList<>();
|
||||
locales.add("en");
|
||||
|
||||
for(File file:dir.listFiles()){
|
||||
String name=file.getName();
|
||||
if(file.isDirectory() && name.startsWith("values-")){
|
||||
if(new File(file, "strings.xml").exists()){
|
||||
locales.add(name.substring(name.indexOf('-')+1).replace("-r", "-"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
locales.sort(String::compareTo);
|
||||
try(OutputStreamWriter writer=new OutputStreamWriter(new FileOutputStream(new File(dir, "xml/locales_config.xml")), StandardCharsets.UTF_8)){
|
||||
writer.write("""
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
""");
|
||||
for(String locale : locales){
|
||||
writer.write("\t<locale android:name=\"");
|
||||
writer.write(locale);
|
||||
writer.write("\"/>\n");
|
||||
}
|
||||
writer.write("</locale-config>");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user