Merge remote-tracking branch 'origin/master'
# Conflicts: # mastodon/src/main/res/values-de-rDE/strings_mo.xml # metadata/es-ES/changelogs/80.txt # metadata/es-ES/changelogs/81.txt # metadata/es-ES/full_description.txt
This commit is contained in:
commit
ac1dbc0f90
19
README.md
19
README.md
|
@ -5,7 +5,7 @@
|
||||||
> A fork of [megalodon](https://github.com/sk22/megalodon) which is a fork of [official Mastodon Android app](https://github.com/mastodon/mastodon-android) adding important features that are missing in the official app and possibly won’t ever be implemented, such as the federated timeline, unlisted posting, bookmarks and an image description viewer.
|
> A fork of [megalodon](https://github.com/sk22/megalodon) which is a fork of [official Mastodon Android app](https://github.com/mastodon/mastodon-android) adding important features that are missing in the official app and possibly won’t ever be implemented, such as the federated timeline, unlisted posting, bookmarks and an image description viewer.
|
||||||
|
|
||||||
|
|
||||||
[![Download latest release](https://img.shields.io/badge/dynamic/json?color=d92aad&label=download%20apk&query=%24.tag_name&url=https%3A%2F%2Fapi.github.com%2Frepos%2FLucasGGamerM%2Fmoshidon%2Freleases%2Flatest&style=for-the-badge)](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.apk)
|
[![Download latest release](https://img.shields.io/badge/dynamic/json?color=282C37&label=download%20apk&query=%24.tag_name&url=https%3A%2F%2Fapi.github.com%2Frepos%2FLucasGGamerM%2Fmoshidon%2Freleases%2Flatest&style=for-the-badge)](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.apk)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -14,15 +14,6 @@
|
||||||
|
|
||||||
### **Material you theme support on Android 12+ devices!**
|
### **Material you theme support on Android 12+ devices!**
|
||||||
|
|
||||||
### **Translate button**
|
|
||||||
|
|
||||||
**Allows you to translate posts in instances with the translate feature!**
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
|
|
||||||
![Screenshot_20221209-135457_1](https://user-images.githubusercontent.com/71328265/206753830-cdb8bc65-7732-4a6a-8bcd-bbc4ca311d19.png)
|
|
||||||
![Screenshot_20221209-135409_1](https://user-images.githubusercontent.com/71328265/206753831-7af92a48-d7a5-4780-9beb-90acef4e141b.png)
|
|
||||||
|
|
||||||
### **Color themes**
|
### **Color themes**
|
||||||
|
|
||||||
**Allows you to change theme within the app. Supports Purple, pink, green, blue, orange and yellow!**
|
**Allows you to change theme within the app. Supports Purple, pink, green, blue, orange and yellow!**
|
||||||
|
@ -88,7 +79,12 @@ Variant with an integrated updater. If you download Moshidon from here (and not
|
||||||
|
|
||||||
* [Add “Unlisted” as a post visibility option](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/enable-unlisted)
|
* [Add “Unlisted” as a post visibility option](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/enable-unlisted)
|
||||||
([Pull request](https://github.com/mastodon/mastodon-android/pull/103))
|
([Pull request](https://github.com/mastodon/mastodon-android/pull/103))
|
||||||
* [Add “Federation” tab and change Discover tab order](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/add-federated-timeline) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/8))
|
* Adding a useful private profile note box!*
|
||||||
|
* Auto hiding the compose button on scroll!*
|
||||||
|
* Adding the hability to remind yourself to add alt text to images!*
|
||||||
|
* Adding the ability to have drafts!*
|
||||||
|
* Also adding the ability to view announcements from your instance!*
|
||||||
|
* Adding the ability to post for local timeline only (Only on instances that support it!)*
|
||||||
* [Add image description button and viewer](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/display-alt-text) ([Pull request](https://github.com/mastodon/mastodon-android/pull/129))
|
* [Add image description button and viewer](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon: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:megalodon:feature/pin-posts) ([Pull request](https://github.com/mastodon/mastodon-android/pull/140))
|
* [Implement pinning posts and displaying pinned posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon: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:megalodon:feature/delete-redraft) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/21))
|
* [Implement deleting and re-drafting](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/delete-redraft) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/21))
|
||||||
|
@ -110,6 +106,7 @@ Variant with an integrated updater. If you download Moshidon from here (and not
|
||||||
|
|
||||||
### Behavior
|
### Behavior
|
||||||
|
|
||||||
|
* Adding a bottom option for the publish button, allowing for easier user on larger screens!
|
||||||
* [Make back button return to the home tab before exiting the app](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/back-returns-home) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/118))
|
* [Make back button return to the home tab before exiting the app](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon: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:megalodon:feature/always-preserve-cw) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/113))
|
* [Always preserve content warnings when replying](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon: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:megalodon:feature/compose-image-description-full-image) ([Pull request](https://github.com/mastodon/mastodon-android/pull/182))
|
* [Display full image when adding image description](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/compose-image-description-full-image) ([Pull request](https://github.com/mastodon/mastodon-android/pull/182))
|
||||||
|
|
|
@ -9,8 +9,8 @@ android {
|
||||||
applicationId "org.joinmastodon.android.moshinda"
|
applicationId "org.joinmastodon.android.moshinda"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
versionCode 87
|
versionCode 89
|
||||||
versionName "1.1.4+fork.87.moshinda"
|
versionName "1.1.4+fork.89.moshinda"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
resConfigs "ar-rSA", "be-rBY", "bn-rBD", "bs-rBA", "ca-rES", "cs-rCZ", "de-rDE", "el-rGR", "es-rES", "eu-rES", "fi-rFI", "fil-rPH", "fr-rFR", "ga-rIE", "gd-rGB", "gl-rES", "hi-rIN", "hr-rHR", "hu-rHU", "hy-rAM", "in-rID", "is-rIS", "it-rIT", "iw-rIL", "ja-rJP", "kab", "ko-rKR", "nl-rNL", "oc-rFR", "pl-rPL", "pt-rBR", "pt-rPT", "ro-rRO", "ru-rRU", "si-rLK", "sl-rSI", "sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "vi-rVN", "zh-rCN", "zh-rTW"
|
resConfigs "ar-rSA", "be-rBY", "bn-rBD", "bs-rBA", "ca-rES", "cs-rCZ", "de-rDE", "el-rGR", "es-rES", "eu-rES", "fi-rFI", "fil-rPH", "fr-rFR", "ga-rIE", "gd-rGB", "gl-rES", "hi-rIN", "hr-rHR", "hu-rHU", "hy-rAM", "in-rID", "is-rIS", "it-rIT", "iw-rIL", "ja-rJP", "kab", "ko-rKR", "nl-rNL", "oc-rFR", "pl-rPL", "pt-rBR", "pt-rPT", "ro-rRO", "ru-rRU", "si-rLK", "sl-rSI", "sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "vi-rVN", "zh-rCN", "zh-rTW"
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,14 @@ import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
import org.joinmastodon.android.BuildConfig;
|
import org.joinmastodon.android.BuildConfig;
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.MastodonApp;
|
import org.joinmastodon.android.MastodonApp;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
|
@ -117,60 +119,66 @@ public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
|
||||||
.build();
|
.build();
|
||||||
Call call=MastodonAPIController.getHttpClient().newCall(req);
|
Call call=MastodonAPIController.getHttpClient().newCall(req);
|
||||||
try(Response resp=call.execute()){
|
try(Response resp=call.execute()){
|
||||||
JsonObject obj=JsonParser.parseReader(resp.body().charStream()).getAsJsonObject();
|
JsonArray arr=JsonParser.parseReader(resp.body().charStream()).getAsJsonArray();
|
||||||
String changelog=obj.get("body").getAsString();
|
for (JsonElement jsonElement : arr) {
|
||||||
String tag=obj.get("tag_name").getAsString();
|
JsonObject obj = jsonElement.getAsJsonObject();
|
||||||
Pattern pattern=Pattern.compile("v?(\\d+)\\.(\\d+)\\.(\\d+)\\+fork\\.(\\d+)");
|
if (obj.get("prerelease").getAsBoolean() && !GlobalUserPreferences.enablePreReleases) continue;
|
||||||
Matcher matcher=pattern.matcher(tag);
|
|
||||||
if(!matcher.find()){
|
|
||||||
Log.w(TAG, "actuallyCheckForUpdates: release tag has wrong format: "+tag);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int newMajor=Integer.parseInt(matcher.group(1)),
|
|
||||||
newMinor=Integer.parseInt(matcher.group(2)),
|
|
||||||
newRevision=Integer.parseInt(matcher.group(3)),
|
|
||||||
newForkNumber=Integer.parseInt(matcher.group(4));
|
|
||||||
matcher=pattern.matcher(BuildConfig.VERSION_NAME);
|
|
||||||
String[] currentParts=BuildConfig.VERSION_NAME.split("[.+]");
|
|
||||||
if(!matcher.find()){
|
|
||||||
Log.w(TAG, "actuallyCheckForUpdates: current version has wrong format: "+BuildConfig.VERSION_NAME);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int curMajor=Integer.parseInt(matcher.group(1)),
|
|
||||||
curMinor=Integer.parseInt(matcher.group(2)),
|
|
||||||
curRevision=Integer.parseInt(matcher.group(3)),
|
|
||||||
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){
|
|
||||||
String version=newMajor+"."+newMinor+"."+newRevision+"+fork."+newForkNumber;
|
|
||||||
Log.d(TAG, "actuallyCheckForUpdates: new version: "+version);
|
|
||||||
for(JsonElement el:obj.getAsJsonArray("assets")){
|
|
||||||
JsonObject asset=el.getAsJsonObject();
|
|
||||||
if("moshidon.apk".equals(asset.get("name").getAsString()) && "application/vnd.android.package-archive".equals(asset.get("content_type").getAsString()) && "uploaded".equals(asset.get("state").getAsString())){
|
|
||||||
long size=asset.get("size").getAsLong();
|
|
||||||
String url=asset.get("browser_download_url").getAsString();
|
|
||||||
|
|
||||||
UpdateInfo info=new UpdateInfo();
|
String tag=obj.get("tag_name").getAsString();
|
||||||
info.size=size;
|
String changelog=obj.get("body").getAsString();
|
||||||
info.version=version;
|
Pattern pattern=Pattern.compile("v?(\\d+)\\.(\\d+)\\.(\\d+)\\+fork\\.(\\d+)");
|
||||||
info.changelog=changelog;
|
Matcher matcher=pattern.matcher(tag);
|
||||||
this.info=info;
|
if(!matcher.find()){
|
||||||
|
Log.w(TAG, "actuallyCheckForUpdates: release tag has wrong format: "+tag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int newMajor=Integer.parseInt(matcher.group(1)),
|
||||||
|
newMinor=Integer.parseInt(matcher.group(2)),
|
||||||
|
newRevision=Integer.parseInt(matcher.group(3)),
|
||||||
|
newForkNumber=Integer.parseInt(matcher.group(4));
|
||||||
|
matcher=pattern.matcher(BuildConfig.VERSION_NAME);
|
||||||
|
String[] currentParts=BuildConfig.VERSION_NAME.split("[.+]");
|
||||||
|
if(!matcher.find()){
|
||||||
|
Log.w(TAG, "actuallyCheckForUpdates: current version has wrong format: "+BuildConfig.VERSION_NAME);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int curMajor=Integer.parseInt(matcher.group(1)),
|
||||||
|
curMinor=Integer.parseInt(matcher.group(2)),
|
||||||
|
curRevision=Integer.parseInt(matcher.group(3)),
|
||||||
|
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){
|
||||||
|
String version=newMajor+"."+newMinor+"."+newRevision+"+fork."+newForkNumber;
|
||||||
|
Log.d(TAG, "actuallyCheckForUpdates: new version: "+version);
|
||||||
|
for(JsonElement el:obj.getAsJsonArray("assets")){
|
||||||
|
JsonObject asset=el.getAsJsonObject();
|
||||||
|
if("moshidon.apk".equals(asset.get("name").getAsString()) && "application/vnd.android.package-archive".equals(asset.get("content_type").getAsString()) && "uploaded".equals(asset.get("state").getAsString())){
|
||||||
|
long size=asset.get("size").getAsLong();
|
||||||
|
String url=asset.get("browser_download_url").getAsString();
|
||||||
|
|
||||||
getPrefs().edit()
|
UpdateInfo info=new UpdateInfo();
|
||||||
.putLong("apkSize", size)
|
info.size=size;
|
||||||
.putString("version", version)
|
info.version=version;
|
||||||
.putString("apkURL", url)
|
info.changelog=changelog;
|
||||||
.putInt("checkedByBuild", BuildConfig.VERSION_CODE)
|
this.info=info;
|
||||||
.putString("changelog", changelog)
|
|
||||||
.remove("downloadID")
|
|
||||||
.apply();
|
|
||||||
|
|
||||||
break;
|
getPrefs().edit()
|
||||||
|
.putLong("apkSize", size)
|
||||||
|
.putString("version", version)
|
||||||
|
.putString("apkURL", url)
|
||||||
|
.putString("changelog", changelog)
|
||||||
|
.putInt("checkedByBuild", BuildConfig.VERSION_CODE)
|
||||||
|
.remove("downloadID")
|
||||||
|
.apply();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
getPrefs().edit().putLong("lastCheck", System.currentTimeMillis()).apply();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
getPrefs().edit().putLong("lastCheck", System.currentTimeMillis()).apply();
|
|
||||||
}catch(Exception x){
|
}catch(Exception x){
|
||||||
Log.w(TAG, "actuallyCheckForUpdates", x);
|
Log.w(TAG, "actuallyCheckForUpdates", x);
|
||||||
}finally{
|
}finally{
|
||||||
|
|
|
@ -12,10 +12,17 @@
|
||||||
|
|
||||||
<permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
|
<permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
|
||||||
|
|
||||||
|
<queries>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.PROCESS_TEXT" />
|
||||||
|
<data android:mimeType="text/plain" />
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MastodonApp"
|
android:name=".MastodonApp"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:label="@string/app_name"
|
android:label="@string/mo_app_name"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:localeConfig="@xml/locales_config"
|
android:localeConfig="@xml/locales_config"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
|
|
@ -83,3 +83,7 @@ mirr0r.city underage
|
||||||
nnia.space underage
|
nnia.space underage
|
||||||
ignorelist.com malicious
|
ignorelist.com malicious
|
||||||
repl.co malicious
|
repl.co malicious
|
||||||
|
|
||||||
|
# custom
|
||||||
|
|
||||||
|
pawoo.net csam
|
||||||
|
|
Can't render this file because it has a wrong number of fields in line 2.
|
|
@ -9,10 +9,14 @@ import android.os.Build;
|
||||||
import com.google.gson.JsonSyntaxException;
|
import com.google.gson.JsonSyntaxException;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.model.TimelineDefinition;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class GlobalUserPreferences{
|
public class GlobalUserPreferences{
|
||||||
public static boolean playGifs;
|
public static boolean playGifs;
|
||||||
|
@ -21,7 +25,7 @@ public class GlobalUserPreferences{
|
||||||
public static boolean showReplies;
|
public static boolean showReplies;
|
||||||
public static boolean showBoosts;
|
public static boolean showBoosts;
|
||||||
public static boolean loadNewPosts;
|
public static boolean loadNewPosts;
|
||||||
public static boolean showFederatedTimeline;
|
public static boolean showNewPostsButton;
|
||||||
public static boolean showInteractionCounts;
|
public static boolean showInteractionCounts;
|
||||||
public static boolean alwaysExpandContentWarnings;
|
public static boolean alwaysExpandContentWarnings;
|
||||||
public static boolean disableMarquee;
|
public static boolean disableMarquee;
|
||||||
|
@ -33,13 +37,21 @@ public class GlobalUserPreferences{
|
||||||
public static boolean relocatePublishButton;
|
public static boolean relocatePublishButton;
|
||||||
public static boolean reduceMotion;
|
public static boolean reduceMotion;
|
||||||
public static boolean keepOnlyLatestNotification;
|
public static boolean keepOnlyLatestNotification;
|
||||||
public static boolean disableFab;
|
public static boolean enableFabAutoHide;
|
||||||
|
public static boolean disableAltTextReminder;
|
||||||
|
public static boolean showAltIndicator;
|
||||||
|
public static boolean showNoAltIndicator;
|
||||||
|
public static boolean enablePreReleases;
|
||||||
public static String publishButtonText;
|
public static String publishButtonText;
|
||||||
public static ThemePreference theme;
|
public static ThemePreference theme;
|
||||||
public static ColorPreference color;
|
public static ColorPreference color;
|
||||||
|
|
||||||
private final static Type recentLanguagesType = new TypeToken<Map<String, List<String>>>() {}.getType();
|
private final static Type recentLanguagesType = new TypeToken<Map<String, List<String>>>() {}.getType();
|
||||||
|
private final static Type pinnedTimelinesType = new TypeToken<Map<String, List<TimelineDefinition>>>() {}.getType();
|
||||||
public static Map<String, List<String>> recentLanguages;
|
public static Map<String, List<String>> recentLanguages;
|
||||||
|
public static Map<String, List<TimelineDefinition>> pinnedTimelines;
|
||||||
|
public static Set<String> accountsWithLocalOnlySupport;
|
||||||
|
public static Set<String> accountsInGlitchMode;
|
||||||
|
|
||||||
private final static Type recentEmojisType = new TypeToken<Map<String, Integer>>() {}.getType();
|
private final static Type recentEmojisType = new TypeToken<Map<String, Integer>>() {}.getType();
|
||||||
public static Map<String, Integer> recentEmojis;
|
public static Map<String, Integer> recentEmojis;
|
||||||
|
@ -49,6 +61,7 @@ public class GlobalUserPreferences{
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> T fromJson(String json, Type type, T orElse) {
|
private static <T> T fromJson(String json, Type type, T orElse) {
|
||||||
|
if (json == null) return orElse;
|
||||||
try { return gson.fromJson(json, type); }
|
try { return gson.fromJson(json, type); }
|
||||||
catch (JsonSyntaxException ignored) { return orElse; }
|
catch (JsonSyntaxException ignored) { return orElse; }
|
||||||
}
|
}
|
||||||
|
@ -61,8 +74,8 @@ public class GlobalUserPreferences{
|
||||||
showReplies=prefs.getBoolean("showReplies", true);
|
showReplies=prefs.getBoolean("showReplies", true);
|
||||||
showBoosts=prefs.getBoolean("showBoosts", true);
|
showBoosts=prefs.getBoolean("showBoosts", true);
|
||||||
loadNewPosts=prefs.getBoolean("loadNewPosts", true);
|
loadNewPosts=prefs.getBoolean("loadNewPosts", true);
|
||||||
|
showNewPostsButton=prefs.getBoolean("showNewPostsButton", true);
|
||||||
uniformNotificationIcon=prefs.getBoolean("uniformNotificationIcon", true);
|
uniformNotificationIcon=prefs.getBoolean("uniformNotificationIcon", true);
|
||||||
showFederatedTimeline=prefs.getBoolean("showFederatedTimeline", !BuildConfig.BUILD_TYPE.equals("playRelease"));
|
|
||||||
showInteractionCounts=prefs.getBoolean("showInteractionCounts", false);
|
showInteractionCounts=prefs.getBoolean("showInteractionCounts", false);
|
||||||
alwaysExpandContentWarnings=prefs.getBoolean("alwaysExpandContentWarnings", false);
|
alwaysExpandContentWarnings=prefs.getBoolean("alwaysExpandContentWarnings", false);
|
||||||
disableMarquee=prefs.getBoolean("disableMarquee", false);
|
disableMarquee=prefs.getBoolean("disableMarquee", false);
|
||||||
|
@ -73,11 +86,19 @@ public class GlobalUserPreferences{
|
||||||
enableDeleteNotifications=prefs.getBoolean("enableDeleteNotifications", true);
|
enableDeleteNotifications=prefs.getBoolean("enableDeleteNotifications", true);
|
||||||
reduceMotion=prefs.getBoolean("reduceMotion", false);
|
reduceMotion=prefs.getBoolean("reduceMotion", false);
|
||||||
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
|
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
|
||||||
disableFab=prefs.getBoolean("disableFab", false);
|
enableFabAutoHide=prefs.getBoolean("enableFabAutoHide", true);
|
||||||
|
disableAltTextReminder=prefs.getBoolean("disableAltTextReminder", false);
|
||||||
|
showAltIndicator=prefs.getBoolean("showAltIndicator", true);
|
||||||
|
showNoAltIndicator=prefs.getBoolean("showNoAltIndicator", true);
|
||||||
|
enablePreReleases=prefs.getBoolean("enablePreReleases", false);
|
||||||
|
publishButtonText=prefs.getString("publishButtonText", "");
|
||||||
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
||||||
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
||||||
recentEmojis=fromJson(prefs.getString("recentEmojis", "{}"), recentEmojisType, new HashMap<>());
|
recentEmojis=fromJson(prefs.getString("recentEmojis", "{}"), recentEmojisType, new HashMap<>());
|
||||||
publishButtonText=prefs.getString("publishButtonText", "");
|
publishButtonText=prefs.getString("publishButtonText", "");
|
||||||
|
pinnedTimelines=fromJson(prefs.getString("pinnedTimelines", null), pinnedTimelinesType, new HashMap<>());
|
||||||
|
accountsWithLocalOnlySupport=prefs.getStringSet("accountsWithLocalOnlySupport", new HashSet<>());
|
||||||
|
accountsInGlitchMode=prefs.getStringSet("accountsInGlitchMode", new HashSet<>());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
|
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
|
||||||
|
@ -98,7 +119,7 @@ public class GlobalUserPreferences{
|
||||||
.putBoolean("showReplies", showReplies)
|
.putBoolean("showReplies", showReplies)
|
||||||
.putBoolean("showBoosts", showBoosts)
|
.putBoolean("showBoosts", showBoosts)
|
||||||
.putBoolean("loadNewPosts", loadNewPosts)
|
.putBoolean("loadNewPosts", loadNewPosts)
|
||||||
.putBoolean("showFederatedTimeline", showFederatedTimeline)
|
.putBoolean("showNewPostsButton", showNewPostsButton)
|
||||||
.putBoolean("trueBlackTheme", trueBlackTheme)
|
.putBoolean("trueBlackTheme", trueBlackTheme)
|
||||||
.putBoolean("showInteractionCounts", showInteractionCounts)
|
.putBoolean("showInteractionCounts", showInteractionCounts)
|
||||||
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
|
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
|
||||||
|
@ -110,12 +131,19 @@ public class GlobalUserPreferences{
|
||||||
.putBoolean("enableDeleteNotifications", enableDeleteNotifications)
|
.putBoolean("enableDeleteNotifications", enableDeleteNotifications)
|
||||||
.putBoolean("reduceMotion", reduceMotion)
|
.putBoolean("reduceMotion", reduceMotion)
|
||||||
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
|
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
|
||||||
.putBoolean("disableFab", disableFab)
|
.putBoolean("enableFabAutoHide", enableFabAutoHide)
|
||||||
|
.putBoolean("disableAltTextReminder", disableAltTextReminder)
|
||||||
|
.putBoolean("showAltIndicator", showAltIndicator)
|
||||||
|
.putBoolean("showNoAltIndicator", showNoAltIndicator)
|
||||||
|
.putBoolean("enablePreReleases", enablePreReleases)
|
||||||
.putString("publishButtonText", publishButtonText)
|
.putString("publishButtonText", publishButtonText)
|
||||||
.putInt("theme", theme.ordinal())
|
.putInt("theme", theme.ordinal())
|
||||||
.putString("color", color.name())
|
.putString("color", color.name())
|
||||||
.putString("recentLanguages", gson.toJson(recentLanguages))
|
.putString("recentLanguages", gson.toJson(recentLanguages))
|
||||||
|
.putString("pinnedTimelines", gson.toJson(pinnedTimelines))
|
||||||
.putString("recentEmojis", gson.toJson(recentEmojis))
|
.putString("recentEmojis", gson.toJson(recentEmojis))
|
||||||
|
.putStringSet("accountsWithLocalOnlySupport", accountsWithLocalOnlySupport)
|
||||||
|
.putStringSet("accountsInGlitchMode", accountsInGlitchMode)
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -144,26 +144,28 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||||
builder.setContentTitle(pn.title)
|
builder.setContentTitle(pn.title)
|
||||||
.setContentText(pn.body)
|
.setContentText(pn.body)
|
||||||
.setContentTitle(pn.title)
|
.setContentTitle(pn.title)
|
||||||
.setStyle(new Notification.InboxStyle()
|
.setStyle(new Notification.BigTextStyle().bigText(pn.body))
|
||||||
.addLine(pn.body))
|
.setSmallIcon(R.drawable.ic_ntf_logo)
|
||||||
.setContentIntent(PendingIntent.getActivity(context, notificationId, contentIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT))
|
.setContentIntent(PendingIntent.getActivity(context, notificationId, contentIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT))
|
||||||
.setWhen(notification==null ? System.currentTimeMillis() : notification.createdAt.toEpochMilli())
|
.setWhen(notification==null ? System.currentTimeMillis() : notification.createdAt.toEpochMilli())
|
||||||
.setShowWhen(true)
|
.setShowWhen(true)
|
||||||
.setCategory(Notification.CATEGORY_SOCIAL)
|
.setCategory(Notification.CATEGORY_SOCIAL)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setGroup(accountID)
|
.setColor(context.getColor(R.color.shortcut_icon_background))
|
||||||
.setColor(context.getColor(R.color.shortcut_icon_background));
|
.setGroup(accountID);
|
||||||
if(!GlobalUserPreferences.uniformNotificationIcon){
|
|
||||||
switch (pn.notificationType) {
|
if (!GlobalUserPreferences.uniformNotificationIcon) {
|
||||||
case FAVORITE -> builder.setSmallIcon(R.drawable.ic_fluent_star_24_filled);
|
builder.setSmallIcon(switch (pn.notificationType) {
|
||||||
case REBLOG -> builder.setSmallIcon(R.drawable.ic_fluent_arrow_repeat_all_24_filled);
|
case FAVORITE -> R.drawable.ic_fluent_star_24_filled;
|
||||||
case FOLLOW -> builder.setSmallIcon(R.drawable.ic_fluent_person_add_24_filled);
|
case REBLOG -> R.drawable.ic_fluent_arrow_repeat_all_24_filled;
|
||||||
case MENTION -> builder.setSmallIcon(R.drawable.ic_fluent_mention_24_filled);
|
case FOLLOW -> R.drawable.ic_fluent_person_add_24_filled;
|
||||||
case POLL -> builder.setSmallIcon(R.drawable.ic_fluent_poll_24_filled);
|
case MENTION -> R.drawable.ic_fluent_mention_24_filled;
|
||||||
default -> builder.setSmallIcon(R.drawable.ic_ntf_logo);
|
case POLL -> R.drawable.ic_fluent_poll_24_filled;
|
||||||
}
|
case STATUS -> R.drawable.ic_fluent_chat_24_filled;
|
||||||
}else{
|
case UPDATE -> R.drawable.ic_fluent_history_24_filled;
|
||||||
builder.setSmallIcon(R.drawable.ic_ntf_logo);
|
case REPORT -> R.drawable.ic_fluent_warning_24_filled;
|
||||||
|
case SIGN_UP -> R.drawable.ic_fluent_person_available_24_filled;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(avatar!=null){
|
if(avatar!=null){
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.joinmastodon.android.api.requests.lists;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
|
|
||||||
|
public class GetList extends MastodonAPIRequest<ListTimeline> {
|
||||||
|
public GetList(String id) {
|
||||||
|
super(HttpMethod.GET, "/lists/" + id, ListTimeline.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,6 +39,7 @@ public class CreateStatus extends MastodonAPIRequest<Status>{
|
||||||
public Poll poll;
|
public Poll poll;
|
||||||
public String inReplyToId;
|
public String inReplyToId;
|
||||||
public boolean sensitive;
|
public boolean sensitive;
|
||||||
|
public boolean localOnly;
|
||||||
public String spoilerText;
|
public String spoilerText;
|
||||||
public StatusPrivacy visibility;
|
public StatusPrivacy visibility;
|
||||||
public Instant scheduledAt;
|
public Instant scheduledAt;
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package org.joinmastodon.android.events;
|
||||||
|
|
||||||
|
public class HashtagUpdatedEvent {
|
||||||
|
public final String name;
|
||||||
|
public final boolean following;
|
||||||
|
|
||||||
|
public HashtagUpdatedEvent(String name, boolean following) {
|
||||||
|
this.name = name;
|
||||||
|
this.following = following;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.joinmastodon.android.events;
|
||||||
|
|
||||||
|
public class ListDeletedEvent {
|
||||||
|
public final String id;
|
||||||
|
|
||||||
|
public ListDeletedEvent(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.joinmastodon.android.events;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
|
|
||||||
|
public class ListUpdatedCreatedEvent {
|
||||||
|
public final String id;
|
||||||
|
public final String title;
|
||||||
|
public final ListTimeline.RepliesPolicy repliesPolicy;
|
||||||
|
|
||||||
|
public ListUpdatedCreatedEvent(String id, String title, ListTimeline.RepliesPolicy repliesPolicy) {
|
||||||
|
this.id = id;
|
||||||
|
this.title = title;
|
||||||
|
this.repliesPolicy = repliesPolicy;
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,8 +60,7 @@ public class AccountTimelineFragment extends StatusListFragment{
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(getActivity()==null)
|
if(getActivity()==null) return;
|
||||||
return;
|
|
||||||
onDataLoaded(result, !result.isEmpty());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -77,12 +77,7 @@ public class AnnouncementsFragment extends BaseStatusListFragment<Announcement>
|
||||||
public void onMarkAsRead(String id) {
|
public void onMarkAsRead(String id) {
|
||||||
if (unreadIDs == null) return;
|
if (unreadIDs == null) return;
|
||||||
unreadIDs.remove(id);
|
unreadIDs.remove(id);
|
||||||
if (unreadIDs.size() == 0) setResult(true, null);
|
if (unreadIDs.isEmpty()) setResult(true, null);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -97,11 +92,13 @@ public class AnnouncementsFragment extends BaseStatusListFragment<Announcement>
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Announcement> result){
|
public void onSuccess(List<Announcement> result){
|
||||||
|
if (getActivity() == null) return;
|
||||||
List<Announcement> unread = result.stream().filter(a -> !a.read).collect(toList());
|
List<Announcement> unread = result.stream().filter(a -> !a.read).collect(toList());
|
||||||
List<Announcement> read = result.stream().filter(a -> a.read).collect(toList());
|
List<Announcement> read = result.stream().filter(a -> a.read).collect(toList());
|
||||||
onDataLoaded(unread, true);
|
onDataLoaded(unread, true);
|
||||||
onDataLoaded(read, false);
|
onDataLoaded(read, false);
|
||||||
unreadIDs = unread.stream().map(a -> a.id).collect(toList());
|
if (unread.isEmpty()) setResult(true, null);
|
||||||
|
else unreadIDs = unread.stream().map(a -> a.id).collect(toList());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
|
|
|
@ -13,7 +13,6 @@ import android.text.Layout;
|
||||||
import android.text.StaticLayout;
|
import android.text.StaticLayout;
|
||||||
import android.text.TextPaint;
|
import android.text.TextPaint;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
|
@ -75,8 +74,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
protected String accountID;
|
protected String accountID;
|
||||||
protected PhotoViewer currentPhotoViewer;
|
protected PhotoViewer currentPhotoViewer;
|
||||||
protected ImageButton fab;
|
protected ImageButton fab;
|
||||||
protected boolean isScrollingUp = false;
|
protected int scrollDiff = 0;
|
||||||
// protected boolean isFirstLaunch = true;
|
|
||||||
protected HashMap<String, Account> knownAccounts=new HashMap<>();
|
protected HashMap<String, Account> knownAccounts=new HashMap<>();
|
||||||
protected HashMap<String, Relationship> relationships=new HashMap<>();
|
protected HashMap<String, Relationship> relationships=new HashMap<>();
|
||||||
protected Rect tmpRect=new Rect();
|
protected Rect tmpRect=new Rect();
|
||||||
|
@ -280,28 +278,30 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
fab=view.findViewById(R.id.fab);
|
fab=view.findViewById(R.id.fab);
|
||||||
|
|
||||||
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
|
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
|
||||||
@Override
|
@Override
|
||||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
|
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
|
||||||
if(currentPhotoViewer!=null)
|
if(currentPhotoViewer!=null)
|
||||||
currentPhotoViewer.offsetView(-dx, -dy);
|
currentPhotoViewer.offsetView(-dx, -dy);
|
||||||
|
|
||||||
if (fab!=null && !GlobalUserPreferences.disableFab) {
|
if (fab!=null && GlobalUserPreferences.enableFabAutoHide) {
|
||||||
if (dy > 0 /*&& !isFirstLaunch*/) {
|
if(dy > 0){
|
||||||
if (isScrollingUp /*&& !isFirstLaunch*/) {
|
scrollDiff = 0;
|
||||||
fab.setVisibility(View.INVISIBLE);
|
}
|
||||||
TranslateAnimation animate = new TranslateAnimation(
|
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
|
||||||
0,
|
TranslateAnimation animate = new TranslateAnimation(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
fab.getHeight() * 2);
|
0,
|
||||||
animate.setDuration(300);
|
fab.getHeight() * 2);
|
||||||
animate.setFillAfter(true);
|
animate.setDuration(300);
|
||||||
fab.startAnimation(animate);
|
animate.setFillAfter(true);
|
||||||
isScrollingUp = false;
|
fab.startAnimation(animate);
|
||||||
}
|
fab.setVisibility(View.INVISIBLE);
|
||||||
} else {
|
scrollDiff = 0;
|
||||||
if (!isScrollingUp) {
|
} else if (dy < 0 && fab.getVisibility() != View.VISIBLE) {
|
||||||
|
if (scrollDiff > 800) {
|
||||||
fab.setVisibility(View.VISIBLE);
|
fab.setVisibility(View.VISIBLE);
|
||||||
TranslateAnimation animate = new TranslateAnimation(
|
TranslateAnimation animate = new TranslateAnimation(
|
||||||
0,
|
0,
|
||||||
|
@ -311,7 +311,9 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
animate.setDuration(300);
|
animate.setDuration(300);
|
||||||
animate.setFillAfter(true);
|
animate.setFillAfter(true);
|
||||||
fab.startAnimation(animate);
|
fab.startAnimation(animate);
|
||||||
isScrollingUp = true;
|
scrollDiff = 0;
|
||||||
|
} else {
|
||||||
|
scrollDiff += Math.abs(dy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ public class BookmarkedStatusListFragment extends StatusListFragment{
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Status> result){
|
public void onSuccess(HeaderPaginationList<Status> result){
|
||||||
|
if (getActivity() == null) return;
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
|
|
|
@ -152,6 +152,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
private static final int IMAGE_DESCRIPTION_RESULT=363;
|
private static final int IMAGE_DESCRIPTION_RESULT=363;
|
||||||
private static final int SCHEDULED_STATUS_OPENED_RESULT=161;
|
private static final int SCHEDULED_STATUS_OPENED_RESULT=161;
|
||||||
private static final int MAX_ATTACHMENTS=4;
|
private static final int MAX_ATTACHMENTS=4;
|
||||||
|
private static final String GLITCH_LOCAL_ONLY_SUFFIX = "👁";
|
||||||
|
private static final Pattern GLITCH_LOCAL_ONLY_PATTERN = Pattern.compile("[\\s\\S]*" + GLITCH_LOCAL_ONLY_SUFFIX + "[\uFE00-\uFE0F]*");
|
||||||
private static final String TAG="ComposeFragment";
|
private static final String TAG="ComposeFragment";
|
||||||
|
|
||||||
private static final Pattern MENTION_PATTERN=Pattern.compile("(^|[^\\/\\w])@(([a-z0-9_]+)@[a-z0-9\\.\\-]+[a-z0-9]+)", Pattern.CASE_INSENSITIVE);
|
private static final Pattern MENTION_PATTERN=Pattern.compile("(^|[^\\/\\w])@(([a-z0-9_]+)@[a-z0-9\\.\\-]+[a-z0-9]+)", Pattern.CASE_INSENSITIVE);
|
||||||
|
@ -164,7 +166,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
private final BreakIterator breakIterator=BreakIterator.getCharacterInstance();
|
private final BreakIterator breakIterator=BreakIterator.getCharacterInstance();
|
||||||
|
|
||||||
private SizeListenerLinearLayout contentView;
|
private SizeListenerLinearLayout contentView;
|
||||||
private TextView selfName, selfUsername;
|
private TextView selfName, selfUsername, selfExtraText, extraText;
|
||||||
private ImageView selfAvatar;
|
private ImageView selfAvatar;
|
||||||
private Account self;
|
private Account self;
|
||||||
private String instanceDomain;
|
private String instanceDomain;
|
||||||
|
@ -213,6 +215,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
private View sendingOverlay;
|
private View sendingOverlay;
|
||||||
private WindowManager wm;
|
private WindowManager wm;
|
||||||
private StatusPrivacy statusVisibility=StatusPrivacy.PUBLIC;
|
private StatusPrivacy statusVisibility=StatusPrivacy.PUBLIC;
|
||||||
|
private boolean localOnly;
|
||||||
private ComposeAutocompleteSpan currentAutocompleteSpan;
|
private ComposeAutocompleteSpan currentAutocompleteSpan;
|
||||||
private FrameLayout mainEditTextWrap;
|
private FrameLayout mainEditTextWrap;
|
||||||
private ComposeAutocompleteViewController autocompleteViewController;
|
private ComposeAutocompleteViewController autocompleteViewController;
|
||||||
|
@ -247,9 +250,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
instance=AccountSessionManager.getInstance().getInstanceInfo(instanceDomain);
|
instance=AccountSessionManager.getInstance().getInstanceInfo(instanceDomain);
|
||||||
languageResolver=new MastodonLanguage.LanguageResolver(instance);
|
languageResolver=new MastodonLanguage.LanguageResolver(instance);
|
||||||
redraftStatus=getArguments().getBoolean("redraftStatus", false);
|
redraftStatus=getArguments().getBoolean("redraftStatus", false);
|
||||||
if(getArguments().containsKey("editStatus")){
|
if(getArguments().containsKey("editStatus"))
|
||||||
editingStatus=Parcels.unwrap(getArguments().getParcelable("editStatus"));
|
editingStatus=Parcels.unwrap(getArguments().getParcelable("editStatus"));
|
||||||
}
|
if(getArguments().containsKey("replyTo"))
|
||||||
|
replyTo=Parcels.unwrap(getArguments().getParcelable("replyTo"));
|
||||||
if(instance==null){
|
if(instance==null){
|
||||||
Nav.finish(this);
|
Nav.finish(this);
|
||||||
return;
|
return;
|
||||||
|
@ -323,6 +327,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
selfName=view.findViewById(R.id.self_name);
|
selfName=view.findViewById(R.id.self_name);
|
||||||
selfUsername=view.findViewById(R.id.self_username);
|
selfUsername=view.findViewById(R.id.self_username);
|
||||||
selfAvatar=view.findViewById(R.id.self_avatar);
|
selfAvatar=view.findViewById(R.id.self_avatar);
|
||||||
|
selfExtraText=view.findViewById(R.id.self_extra_text);
|
||||||
HtmlParser.setTextWithCustomEmoji(selfName, self.displayName, self.emojis);
|
HtmlParser.setTextWithCustomEmoji(selfName, self.displayName, self.emojis);
|
||||||
selfUsername.setText('@'+self.username+'@'+instanceDomain);
|
selfUsername.setText('@'+self.username+'@'+instanceDomain);
|
||||||
ViewImageLoader.load(selfAvatar, null, new UrlImageLoaderRequest(self.avatar));
|
ViewImageLoader.load(selfAvatar, null, new UrlImageLoaderRequest(self.avatar));
|
||||||
|
@ -348,10 +353,26 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
sensitiveItem=view.findViewById(R.id.sensitive_item);
|
sensitiveItem=view.findViewById(R.id.sensitive_item);
|
||||||
replyText=view.findViewById(R.id.reply_text);
|
replyText=view.findViewById(R.id.reply_text);
|
||||||
|
|
||||||
mediaBtn.setOnClickListener(v->openFilePicker());
|
if (isPhotoPickerAvailable()) {
|
||||||
|
PopupMenu attachPopup = new PopupMenu(getContext(), mediaBtn);
|
||||||
|
attachPopup.inflate(R.menu.attach);
|
||||||
|
attachPopup.setOnMenuItemClickListener(i -> {
|
||||||
|
openFilePicker(i.getItemId() == R.id.media);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
UiUtils.enablePopupMenuIcons(getContext(), attachPopup);
|
||||||
|
mediaBtn.setOnClickListener(v->attachPopup.show());
|
||||||
|
mediaBtn.setOnTouchListener(attachPopup.getDragToOpenListener());
|
||||||
|
} else {
|
||||||
|
mediaBtn.setOnClickListener(v -> openFilePicker(false));
|
||||||
|
}
|
||||||
pollBtn.setOnClickListener(v->togglePoll());
|
pollBtn.setOnClickListener(v->togglePoll());
|
||||||
emojiBtn.setOnClickListener(v->emojiKeyboard.toggleKeyboardPopup(mainEditText));
|
emojiBtn.setOnClickListener(v->emojiKeyboard.toggleKeyboardPopup(mainEditText));
|
||||||
spoilerBtn.setOnClickListener(v->toggleSpoiler());
|
spoilerBtn.setOnClickListener(v->toggleSpoiler());
|
||||||
|
|
||||||
|
localOnly = savedInstanceState != null ? savedInstanceState.getBoolean("localOnly") :
|
||||||
|
editingStatus != null ? editingStatus.localOnly : replyTo != null && replyTo.localOnly;
|
||||||
|
|
||||||
buildVisibilityPopup(visibilityBtn);
|
buildVisibilityPopup(visibilityBtn);
|
||||||
visibilityBtn.setOnClickListener(v->visibilityPopup.show());
|
visibilityBtn.setOnClickListener(v->visibilityPopup.show());
|
||||||
visibilityBtn.setOnTouchListener(visibilityPopup.getDragToOpenListener());
|
visibilityBtn.setOnTouchListener(visibilityPopup.getDragToOpenListener());
|
||||||
|
@ -426,6 +447,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
spoilerBg.setDrawableByLayerId(R.id.right_drawable, new SpoilerStripesDrawable());
|
spoilerBg.setDrawableByLayerId(R.id.right_drawable, new SpoilerStripesDrawable());
|
||||||
spoilerEdit.setBackground(spoilerBg);
|
spoilerEdit.setBackground(spoilerBg);
|
||||||
if((savedInstanceState!=null && savedInstanceState.getBoolean("hasSpoiler", false)) || hasSpoiler){
|
if((savedInstanceState!=null && savedInstanceState.getBoolean("hasSpoiler", false)) || hasSpoiler){
|
||||||
|
hasSpoiler=true;
|
||||||
spoilerEdit.setVisibility(View.VISIBLE);
|
spoilerEdit.setVisibility(View.VISIBLE);
|
||||||
spoilerBtn.setSelected(true);
|
spoilerBtn.setSelected(true);
|
||||||
}else if(editingStatus!=null && !TextUtils.isEmpty(editingStatus.spoilerText)){
|
}else if(editingStatus!=null && !TextUtils.isEmpty(editingStatus.spoilerText)){
|
||||||
|
@ -469,7 +491,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
case UNLISTED -> R.id.vis_unlisted;
|
case UNLISTED -> R.id.vis_unlisted;
|
||||||
case PRIVATE -> R.id.vis_followers;
|
case PRIVATE -> R.id.vis_followers;
|
||||||
case DIRECT -> R.id.vis_private;
|
case DIRECT -> R.id.vis_private;
|
||||||
|
case LOCAL -> R.id.vis_local;
|
||||||
}).setChecked(true);
|
}).setChecked(true);
|
||||||
|
visibilityPopup.getMenu().findItem(R.id.local_only).setChecked(localOnly);
|
||||||
|
|
||||||
autocompleteViewController=new ComposeAutocompleteViewController(getActivity(), accountID);
|
autocompleteViewController=new ComposeAutocompleteViewController(getActivity(), accountID);
|
||||||
autocompleteViewController.setCompletionSelectedListener(this::onAutocompleteOptionSelected);
|
autocompleteViewController.setCompletionSelectedListener(this::onAutocompleteOptionSelected);
|
||||||
|
@ -496,6 +520,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
outState.putBoolean("pollAllowMultiple", pollAllowMultipleItem.isSelected());
|
outState.putBoolean("pollAllowMultiple", pollAllowMultipleItem.isSelected());
|
||||||
}
|
}
|
||||||
outState.putBoolean("sensitive", sensitive);
|
outState.putBoolean("sensitive", sensitive);
|
||||||
|
outState.putBoolean("localOnly", localOnly);
|
||||||
outState.putBoolean("hasSpoiler", hasSpoiler);
|
outState.putBoolean("hasSpoiler", hasSpoiler);
|
||||||
outState.putString("language", language);
|
outState.putString("language", language);
|
||||||
if(!attachments.isEmpty()){
|
if(!attachments.isEmpty()){
|
||||||
|
@ -619,6 +644,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
View originalPost = view.findViewById(R.id.original_post);
|
View originalPost = view.findViewById(R.id.original_post);
|
||||||
|
extraText = view.findViewById(R.id.extra_text);
|
||||||
originalPost.setVisibility(View.VISIBLE);
|
originalPost.setVisibility(View.VISIBLE);
|
||||||
originalPost.setOnClickListener(v->{
|
originalPost.setOnClickListener(v->{
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
|
@ -651,9 +677,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
view.findViewById(R.id.visibility).setVisibility(View.GONE);
|
view.findViewById(R.id.visibility).setVisibility(View.GONE);
|
||||||
Drawable visibilityIcon = getActivity().getDrawable(switch(replyTo.visibility){
|
Drawable visibilityIcon = getActivity().getDrawable(switch(replyTo.visibility){
|
||||||
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
|
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
|
||||||
case UNLISTED -> R.drawable.ic_fluent_people_community_20_regular;
|
case UNLISTED -> R.drawable.ic_fluent_lock_open_20_regular;
|
||||||
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_20_regular;
|
case PRIVATE -> R.drawable.ic_fluent_lock_closed_20_filled;
|
||||||
case DIRECT -> R.drawable.ic_fluent_mention_20_regular;
|
case DIRECT -> R.drawable.ic_fluent_mention_20_regular;
|
||||||
|
case LOCAL -> R.drawable.ic_fluent_eye_20_regular;
|
||||||
});
|
});
|
||||||
ImageView moreBtn = view.findViewById(R.id.more);
|
ImageView moreBtn = view.findViewById(R.id.more);
|
||||||
moreBtn.setImageDrawable(visibilityIcon);
|
moreBtn.setImageDrawable(visibilityIcon);
|
||||||
|
@ -672,7 +699,14 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
else view.findViewById(R.id.display_item_text).setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(16)));
|
else view.findViewById(R.id.display_item_text).setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(16)));
|
||||||
|
|
||||||
replyText.setText(getString(R.string.in_reply_to, replyTo.account.displayName));
|
replyText.setText(getString(R.string.in_reply_to, replyTo.account.displayName));
|
||||||
replyText.setContentDescription(getString(R.string.in_reply_to, replyTo.account.displayName) + ". " + getString(R.string.post_visibility) + ": " + UiUtils.getVisibilityText(replyTo));
|
int visibilityNameRes = switch (replyTo.visibility) {
|
||||||
|
case PUBLIC -> R.string.visibility_public;
|
||||||
|
case UNLISTED -> R.string.sk_visibility_unlisted;
|
||||||
|
case PRIVATE -> R.string.visibility_followers_only;
|
||||||
|
case DIRECT -> R.string.visibility_private;
|
||||||
|
case LOCAL -> R.string.sk_local_only;
|
||||||
|
};
|
||||||
|
replyText.setContentDescription(getString(R.string.in_reply_to, replyTo.account.displayName) + ". " + getString(R.string.post_visibility) + ": " + getString(visibilityNameRes));
|
||||||
replyText.setOnClickListener(v->{
|
replyText.setOnClickListener(v->{
|
||||||
scrollView.smoothScrollTo(0, 0);
|
scrollView.smoothScrollTo(0, 0);
|
||||||
});
|
});
|
||||||
|
@ -755,6 +789,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSensitive();
|
updateSensitive();
|
||||||
|
updateHeaders();
|
||||||
|
|
||||||
if(editingStatus!=null){
|
if(editingStatus!=null){
|
||||||
updateCharCounter();
|
updateCharCounter();
|
||||||
|
@ -809,6 +844,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
draftsBtn.setOnTouchListener(draftOptionsPopup.getDragToOpenListener());
|
draftsBtn.setOnTouchListener(draftOptionsPopup.getDragToOpenListener());
|
||||||
updateScheduledAt(scheduledAt != null ? scheduledAt : scheduledStatus != null ? scheduledStatus.scheduledAt : null);
|
updateScheduledAt(scheduledAt != null ? scheduledAt : scheduledStatus != null ? scheduledStatus.scheduledAt : null);
|
||||||
buildLanguageSelector(languageButton);
|
buildLanguageSelector(languageButton);
|
||||||
|
|
||||||
|
if (editingStatus != null && scheduledStatus == null) {
|
||||||
|
// editing an already published post
|
||||||
|
draftsBtn.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateToUnsentPosts() {
|
private void navigateToUnsentPosts() {
|
||||||
|
@ -844,7 +884,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
btn.setOnClickListener(v->languagePopup.show());
|
btn.setOnClickListener(v->languagePopup.show());
|
||||||
|
|
||||||
Preferences prefs = AccountSessionManager.getInstance().getAccount(accountID).preferences;
|
Preferences prefs = AccountSessionManager.getInstance().getAccount(accountID).preferences;
|
||||||
updateLanguage(prefs != null && prefs.postingDefaultLanguage != null && prefs.postingDefaultLanguage.length() > 0
|
if (language != null) updateLanguage(language);
|
||||||
|
else updateLanguage(prefs != null && prefs.postingDefaultLanguage != null && prefs.postingDefaultLanguage.length() > 0
|
||||||
? languageResolver.from(prefs.postingDefaultLanguage)
|
? languageResolver.from(prefs.postingDefaultLanguage)
|
||||||
: languageResolver.getDefault());
|
: languageResolver.getDefault());
|
||||||
|
|
||||||
|
@ -896,6 +937,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
if(hasSpoiler){
|
if(hasSpoiler){
|
||||||
charCount+=spoilerEdit.length();
|
charCount+=spoilerEdit.length();
|
||||||
}
|
}
|
||||||
|
if (localOnly && GlobalUserPreferences.accountsInGlitchMode.contains(accountID)) {
|
||||||
|
charCount -= GLITCH_LOCAL_ONLY_SUFFIX.length();
|
||||||
|
}
|
||||||
charCounter.setText(String.valueOf(charLimit-charCount));
|
charCounter.setText(String.valueOf(charLimit-charCount));
|
||||||
trimmedCharCount=text.toString().trim().length();
|
trimmedCharCount=text.toString().trim().length();
|
||||||
updatePublishButtonState();
|
updatePublishButtonState();
|
||||||
|
@ -928,7 +972,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
nonDoneAttachmentCount++;
|
nonDoneAttachmentCount++;
|
||||||
}
|
}
|
||||||
publishButton.setEnabled((trimmedCharCount>0 || !attachments.isEmpty()) && charCount<=charLimit && nonDoneAttachmentCount==0 && (pollOptions.isEmpty() || nonEmptyPollOptionsCount>1));
|
publishButton.setEnabled((trimmedCharCount>0 || !attachments.isEmpty()) && charCount<=charLimit && nonDoneAttachmentCount==0 && (pollOptions.isEmpty() || nonEmptyPollOptionsCount>1));
|
||||||
sendError.setVisibility(View.GONE);
|
// sendError.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onCustomEmojiClick(Emoji emoji){
|
private void onCustomEmojiClick(Emoji emoji){
|
||||||
|
@ -996,15 +1040,49 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
}
|
}
|
||||||
|
|
||||||
private void publish(){
|
private void publish(){
|
||||||
|
publish(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void publish(boolean force){
|
||||||
String text=mainEditText.getText().toString();
|
String text=mainEditText.getText().toString();
|
||||||
CreateStatus.Request req=new CreateStatus.Request();
|
CreateStatus.Request req=new CreateStatus.Request();
|
||||||
|
if (localOnly &&
|
||||||
|
GlobalUserPreferences.accountsInGlitchMode.contains(accountID) &&
|
||||||
|
!GLITCH_LOCAL_ONLY_PATTERN.matcher(text).matches()) {
|
||||||
|
text += " " + GLITCH_LOCAL_ONLY_SUFFIX;
|
||||||
|
}
|
||||||
req.status=text;
|
req.status=text;
|
||||||
req.visibility=statusVisibility;
|
req.localOnly=localOnly;
|
||||||
|
req.visibility=localOnly && instance.pleroma != null ? StatusPrivacy.LOCAL : statusVisibility;
|
||||||
req.sensitive=sensitive;
|
req.sensitive=sensitive;
|
||||||
req.language=language;
|
req.language=language;
|
||||||
req.scheduledAt = scheduledAt;
|
req.scheduledAt = scheduledAt;
|
||||||
if(!attachments.isEmpty()){
|
if(!attachments.isEmpty()){
|
||||||
req.mediaIds=attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
|
req.mediaIds=attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
|
||||||
|
Optional<DraftMediaAttachment> withoutAltText = attachments.stream().filter(a -> a.description == null || a.description.isBlank()).findFirst();
|
||||||
|
boolean isDraft = scheduledAt != null && scheduledAt.isAfter(DRAFTS_AFTER_INSTANT);
|
||||||
|
if (!force && !GlobalUserPreferences.disableAltTextReminder && !isDraft && withoutAltText.isPresent()) {
|
||||||
|
new M3AlertDialogBuilder(getActivity())
|
||||||
|
.setTitle(R.string.sk_alt_text_missing_title)
|
||||||
|
.setMessage(R.string.sk_alt_text_missing)
|
||||||
|
.setPositiveButton(R.string.add_alt_text, (d, w) -> editMediaDescription(withoutAltText.get()))
|
||||||
|
.setNegativeButton(R.string.sk_publish_anyway, (d, w) -> publish(true))
|
||||||
|
.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ask whether to publish now when editing an existing draft
|
||||||
|
if (!force && editingStatus != null && scheduledAt != null && scheduledAt.isAfter(DRAFTS_AFTER_INSTANT)) {
|
||||||
|
new M3AlertDialogBuilder(getActivity())
|
||||||
|
.setTitle(R.string.sk_save_draft)
|
||||||
|
.setMessage(R.string.sk_save_draft_message)
|
||||||
|
.setPositiveButton(R.string.save, (d, w) -> publish(true))
|
||||||
|
.setNegativeButton(R.string.publish, (d, w) -> {
|
||||||
|
updateScheduledAt(null);
|
||||||
|
publish();
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if(replyTo!=null || (editingStatus != null && editingStatus.inReplyToId!=null)){
|
if(replyTo!=null || (editingStatus != null && editingStatus.inReplyToId!=null)){
|
||||||
req.inReplyToId=editingStatus!=null ? editingStatus.inReplyToId : replyTo.id;
|
req.inReplyToId=editingStatus!=null ? editingStatus.inReplyToId : replyTo.id;
|
||||||
|
@ -1206,9 +1284,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
*
|
*
|
||||||
* <p>For earlier versions use the built in docs ui via {@link Intent#ACTION_GET_CONTENT}
|
* <p>For earlier versions use the built in docs ui via {@link Intent#ACTION_GET_CONTENT}
|
||||||
*/
|
*/
|
||||||
private void openFilePicker(){
|
private void openFilePicker(boolean photoPicker){
|
||||||
Intent intent;
|
Intent intent;
|
||||||
boolean usePhotoPicker = isPhotoPickerAvailable();
|
boolean usePhotoPicker = photoPicker && isPhotoPickerAvailable();
|
||||||
if (usePhotoPicker) {
|
if (usePhotoPicker) {
|
||||||
intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
|
intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
|
||||||
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit());
|
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit());
|
||||||
|
@ -1646,18 +1724,20 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
menu.getMenu().add(0, 2, 0, getResources().getQuantityString(R.plurals.x_minutes, 30, 30));
|
menu.getMenu().add(0, 2, 0, getResources().getQuantityString(R.plurals.x_minutes, 30, 30));
|
||||||
menu.getMenu().add(0, 3, 0, getResources().getQuantityString(R.plurals.x_hours, 1, 1));
|
menu.getMenu().add(0, 3, 0, getResources().getQuantityString(R.plurals.x_hours, 1, 1));
|
||||||
menu.getMenu().add(0, 4, 0, getResources().getQuantityString(R.plurals.x_hours, 6, 6));
|
menu.getMenu().add(0, 4, 0, getResources().getQuantityString(R.plurals.x_hours, 6, 6));
|
||||||
menu.getMenu().add(0, 5, 0, getResources().getQuantityString(R.plurals.x_days, 1, 1));
|
menu.getMenu().add(0, 5, 0, getResources().getQuantityString(R.plurals.x_hours, 12, 12));
|
||||||
menu.getMenu().add(0, 6, 0, getResources().getQuantityString(R.plurals.x_days, 3, 3));
|
menu.getMenu().add(0, 6, 0, getResources().getQuantityString(R.plurals.x_days, 1, 1));
|
||||||
menu.getMenu().add(0, 7, 0, getResources().getQuantityString(R.plurals.x_days, 7, 7));
|
menu.getMenu().add(0, 7, 0, getResources().getQuantityString(R.plurals.x_days, 3, 3));
|
||||||
|
menu.getMenu().add(0, 8, 0, getResources().getQuantityString(R.plurals.x_days, 7, 7));
|
||||||
menu.setOnMenuItemClickListener(item->{
|
menu.setOnMenuItemClickListener(item->{
|
||||||
pollDuration=switch(item.getItemId()){
|
pollDuration=switch(item.getItemId()){
|
||||||
case 1 -> 5*60;
|
case 1 -> 5*60;
|
||||||
case 2 -> 30*60;
|
case 2 -> 30*60;
|
||||||
case 3 -> 3600;
|
case 3 -> 3600;
|
||||||
case 4 -> 6*3600;
|
case 4 -> 6*3600;
|
||||||
case 5 -> 24*3600;
|
case 5 -> 12*3600;
|
||||||
case 6 -> 3*24*3600;
|
case 6 -> 24*3600;
|
||||||
case 7 -> 7*24*3600;
|
case 7 -> 3*24*3600;
|
||||||
|
case 8 -> 7*24*3600;
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+item.getItemId());
|
default -> throw new IllegalStateException("Unexpected value: "+item.getItemId());
|
||||||
};
|
};
|
||||||
pollDurationView.setText(getString(R.string.compose_poll_duration, pollDurationStr=item.getTitle().toString()));
|
pollDurationView.setText(getString(R.string.compose_poll_duration, pollDurationStr=item.getTitle().toString()));
|
||||||
|
@ -1766,12 +1846,33 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
return attachments.size();
|
return attachments.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateHeaders() {
|
||||||
|
UiUtils.setExtraTextInfo(getContext(), selfExtraText, statusVisibility, localOnly);
|
||||||
|
if (replyTo != null) UiUtils.setExtraTextInfo(getContext(), extraText, replyTo.visibility, replyTo.localOnly);
|
||||||
|
}
|
||||||
|
|
||||||
private void buildVisibilityPopup(View v){
|
private void buildVisibilityPopup(View v){
|
||||||
visibilityPopup=new PopupMenu(getActivity(), v);
|
visibilityPopup=new PopupMenu(getActivity(), v);
|
||||||
visibilityPopup.inflate(R.menu.compose_visibility);
|
visibilityPopup.inflate(R.menu.compose_visibility);
|
||||||
Menu m=visibilityPopup.getMenu();
|
Menu m=visibilityPopup.getMenu();
|
||||||
|
MenuItem localOnlyItem = visibilityPopup.getMenu().findItem(R.id.local_only);
|
||||||
|
boolean prefsSaysSupported = GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID);
|
||||||
|
if (instance.pleroma != null) {
|
||||||
|
m.findItem(R.id.vis_local).setVisible(true);
|
||||||
|
} else if (localOnly || prefsSaysSupported) {
|
||||||
|
localOnlyItem.setVisible(true);
|
||||||
|
localOnlyItem.setChecked(localOnly);
|
||||||
|
Status status = editingStatus != null ? editingStatus : replyTo;
|
||||||
|
if (!prefsSaysSupported) {
|
||||||
|
GlobalUserPreferences.accountsWithLocalOnlySupport.add(accountID);
|
||||||
|
if (GLITCH_LOCAL_ONLY_PATTERN.matcher(status.getStrippedText()).matches()) {
|
||||||
|
GlobalUserPreferences.accountsInGlitchMode.add(accountID);
|
||||||
|
}
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
UiUtils.enablePopupMenuIcons(getActivity(), visibilityPopup);
|
UiUtils.enablePopupMenuIcons(getActivity(), visibilityPopup);
|
||||||
m.setGroupCheckable(0, true, true);
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) m.setGroupDividerEnabled(true);
|
||||||
visibilityPopup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
|
visibilityPopup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
|
||||||
@Override
|
@Override
|
||||||
public boolean onMenuItemClick(MenuItem item){
|
public boolean onMenuItemClick(MenuItem item){
|
||||||
|
@ -1784,41 +1885,44 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
statusVisibility=StatusPrivacy.PRIVATE;
|
statusVisibility=StatusPrivacy.PRIVATE;
|
||||||
}else if(id==R.id.vis_private){
|
}else if(id==R.id.vis_private){
|
||||||
statusVisibility=StatusPrivacy.DIRECT;
|
statusVisibility=StatusPrivacy.DIRECT;
|
||||||
|
}else if(id==R.id.vis_local){
|
||||||
|
statusVisibility=StatusPrivacy.LOCAL;
|
||||||
|
}
|
||||||
|
if (id == R.id.local_only) {
|
||||||
|
localOnly = !item.isChecked();
|
||||||
|
item.setChecked(localOnly);
|
||||||
|
} else {
|
||||||
|
item.setChecked(true);
|
||||||
}
|
}
|
||||||
item.setChecked(true);
|
|
||||||
updateVisibilityIcon();
|
updateVisibilityIcon();
|
||||||
|
updateHeaders();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadDefaultStatusVisibility(Bundle savedInstanceState) {
|
private void loadDefaultStatusVisibility(Bundle savedInstanceState) {
|
||||||
if(getArguments().containsKey("replyTo")){
|
if(replyTo != null) statusVisibility = replyTo.visibility;
|
||||||
replyTo=Parcels.unwrap(getArguments().getParcelable("replyTo"));
|
|
||||||
statusVisibility = replyTo.visibility;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A saved privacy setting from a previous compose session wins over the reply visibility
|
// A saved privacy setting from a previous compose session wins over the reply visibility
|
||||||
if(savedInstanceState !=null){
|
if(savedInstanceState !=null){
|
||||||
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
||||||
}
|
}
|
||||||
|
|
||||||
Preferences prefs = AccountSessionManager.getInstance().getAccount(accountID).preferences;
|
AccountSessionManager asm = AccountSessionManager.getInstance();
|
||||||
|
Preferences prefs = asm.getAccount(accountID).preferences;
|
||||||
if (prefs != null) {
|
if (prefs != null) {
|
||||||
// Only override the reply visibility if our preference is more private
|
// Only override the reply visibility if our preference is more private
|
||||||
if (prefs.postingDefaultVisibility.isLessVisibleThan(statusVisibility)) {
|
// (and we're not replying to ourselves, or not at all)
|
||||||
statusVisibility = switch (prefs.postingDefaultVisibility) {
|
if (prefs.postingDefaultVisibility.isLessVisibleThan(statusVisibility) &&
|
||||||
case PUBLIC -> StatusPrivacy.PUBLIC;
|
(replyTo == null || !asm.isSelf(accountID, replyTo.account))) {
|
||||||
case UNLISTED -> StatusPrivacy.UNLISTED;
|
statusVisibility = prefs.postingDefaultVisibility;
|
||||||
case PRIVATE -> StatusPrivacy.PRIVATE;
|
|
||||||
case DIRECT -> StatusPrivacy.DIRECT;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A saved privacy setting from a previous compose session wins over all
|
// A saved privacy setting from a previous compose session wins over all
|
||||||
if(savedInstanceState !=null){
|
if(savedInstanceState !=null){
|
||||||
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1828,9 +1932,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
}
|
}
|
||||||
visibilityBtn.setImageResource(switch(statusVisibility){
|
visibilityBtn.setImageResource(switch(statusVisibility){
|
||||||
case PUBLIC -> R.drawable.ic_fluent_earth_24_regular;
|
case PUBLIC -> R.drawable.ic_fluent_earth_24_regular;
|
||||||
case UNLISTED -> R.drawable.ic_fluent_people_community_24_regular;
|
case UNLISTED -> R.drawable.ic_fluent_lock_open_24_regular;
|
||||||
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_24_regular;
|
case PRIVATE -> R.drawable.ic_fluent_lock_closed_24_filled;
|
||||||
case DIRECT -> R.drawable.ic_fluent_mention_24_regular;
|
case DIRECT -> R.drawable.ic_fluent_mention_24_regular;
|
||||||
|
case LOCAL -> R.drawable.ic_fluent_eye_24_regular;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1952,6 +2057,14 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
private void editMediaDescription(DraftMediaAttachment att) {
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
args.putString("attachment", att.serverAttachment.id);
|
||||||
|
args.putParcelable("uri", att.uri);
|
||||||
|
args.putString("existingDescription", att.description);
|
||||||
|
Nav.goForResult(getActivity(), ComposeImageDescriptionFragment.class, args, IMAGE_DESCRIPTION_RESULT, this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence getTitle(){
|
public CharSequence getTitle(){
|
||||||
|
|
|
@ -0,0 +1,352 @@
|
||||||
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import static android.view.Menu.NONE;
|
||||||
|
|
||||||
|
import static org.joinmastodon.android.ui.utils.UiUtils.makeBackItem;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.SubMenu;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.PopupMenu;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.requests.lists.GetLists;
|
||||||
|
import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags;
|
||||||
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
|
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||||
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
|
import org.joinmastodon.android.model.TimelineDefinition;
|
||||||
|
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||||
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.joinmastodon.android.ui.views.TextInputFrameLayout;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import me.grishka.appkit.api.Callback;
|
||||||
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
|
public class EditTimelinesFragment extends BaseRecyclerFragment<TimelineDefinition> implements ScrollableToTop {
|
||||||
|
private String accountID;
|
||||||
|
private TimelinesAdapter adapter;
|
||||||
|
private final ItemTouchHelper itemTouchHelper;
|
||||||
|
private Menu optionsMenu;
|
||||||
|
private boolean updated;
|
||||||
|
private final Map<MenuItem, TimelineDefinition> timelineByMenuItem = new HashMap<>();
|
||||||
|
private final List<ListTimeline> listTimelines = new ArrayList<>();
|
||||||
|
private final List<Hashtag> hashtags = new ArrayList<>();
|
||||||
|
|
||||||
|
public EditTimelinesFragment() {
|
||||||
|
super(10);
|
||||||
|
ItemTouchHelper.SimpleCallback itemTouchCallback = new ItemTouchHelperCallback() ;
|
||||||
|
itemTouchHelper = new ItemTouchHelper(itemTouchCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
setTitle(R.string.sk_timelines);
|
||||||
|
accountID = getArguments().getString("account");
|
||||||
|
|
||||||
|
new GetLists().setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<ListTimeline> result) {
|
||||||
|
listTimelines.addAll(result);
|
||||||
|
updateOptionsMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
error.showToast(getContext());
|
||||||
|
}
|
||||||
|
}).exec(accountID);
|
||||||
|
|
||||||
|
new GetFollowedHashtags().setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(HeaderPaginationList<Hashtag> result) {
|
||||||
|
hashtags.addAll(result);
|
||||||
|
updateOptionsMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
error.showToast(getContext());
|
||||||
|
}
|
||||||
|
}).exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onShown(){
|
||||||
|
super.onShown();
|
||||||
|
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading) loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
itemTouchHelper.attachToRecyclerView(list);
|
||||||
|
refreshLayout.setEnabled(false);
|
||||||
|
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 0.5f, 56, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
this.optionsMenu = menu;
|
||||||
|
updateOptionsMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.menu_back) {
|
||||||
|
updateOptionsMenu();
|
||||||
|
optionsMenu.performIdentifierAction(R.id.menu_add_timeline, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
TimelineDefinition tl = timelineByMenuItem.get(item);
|
||||||
|
if (tl != null) {
|
||||||
|
data.add(tl.copy());
|
||||||
|
adapter.notifyItemInserted(data.size());
|
||||||
|
saveTimelines();
|
||||||
|
updateOptionsMenu();
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTimelineToOptions(TimelineDefinition tl, Menu menu) {
|
||||||
|
if (data.contains(tl)) return;
|
||||||
|
MenuItem item = menu.add(0, View.generateViewId(), Menu.NONE, tl.getTitle(getContext()));
|
||||||
|
item.setIcon(tl.getIcon().iconRes);
|
||||||
|
timelineByMenuItem.put(item, tl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateOptionsMenu() {
|
||||||
|
if (getActivity() == null) return;
|
||||||
|
optionsMenu.clear();
|
||||||
|
timelineByMenuItem.clear();
|
||||||
|
|
||||||
|
SubMenu menu = optionsMenu.addSubMenu(0, R.id.menu_add_timeline, NONE, R.string.sk_timelines_add);
|
||||||
|
menu.getItem().setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||||
|
menu.getItem().setIcon(R.drawable.ic_fluent_add_24_regular);
|
||||||
|
|
||||||
|
SubMenu timelinesMenu = menu.addSubMenu(R.string.sk_timeline);
|
||||||
|
timelinesMenu.getItem().setIcon(R.drawable.ic_fluent_timeline_24_regular);
|
||||||
|
SubMenu listsMenu = menu.addSubMenu(R.string.sk_list);
|
||||||
|
listsMenu.getItem().setIcon(R.drawable.ic_fluent_people_24_regular);
|
||||||
|
SubMenu hashtagsMenu = menu.addSubMenu(R.string.sk_hashtag);
|
||||||
|
hashtagsMenu.getItem().setIcon(R.drawable.ic_fluent_number_symbol_24_regular);
|
||||||
|
|
||||||
|
makeBackItem(timelinesMenu);
|
||||||
|
makeBackItem(listsMenu);
|
||||||
|
makeBackItem(hashtagsMenu);
|
||||||
|
|
||||||
|
TimelineDefinition.ALL_TIMELINES.forEach(tl -> addTimelineToOptions(tl, timelinesMenu));
|
||||||
|
listTimelines.stream().map(TimelineDefinition::ofList).forEach(tl -> addTimelineToOptions(tl, listsMenu));
|
||||||
|
hashtags.stream().map(TimelineDefinition::ofHashtag).forEach(tl -> addTimelineToOptions(tl, hashtagsMenu));
|
||||||
|
|
||||||
|
timelinesMenu.getItem().setVisible(timelinesMenu.size() > 0);
|
||||||
|
listsMenu.getItem().setVisible(listsMenu.size() > 0);
|
||||||
|
hashtagsMenu.getItem().setVisible(hashtagsMenu.size() > 0);
|
||||||
|
|
||||||
|
UiUtils.enableOptionsMenuIcons(getContext(), optionsMenu, R.id.menu_add_timeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveTimelines() {
|
||||||
|
updated = true;
|
||||||
|
GlobalUserPreferences.pinnedTimelines.put(accountID, data.size() > 0 ? data : List.of(TimelineDefinition.HOME_TIMELINE));
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeTimeline(int position) {
|
||||||
|
data.remove(position);
|
||||||
|
adapter.notifyItemRemoved(position);
|
||||||
|
saveTimelines();
|
||||||
|
updateOptionsMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doLoadData(int offset, int count){
|
||||||
|
onDataLoaded(GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.DEFAULT_TIMELINES), false);
|
||||||
|
updateOptionsMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RecyclerView.Adapter<TimelineViewHolder> getAdapter() {
|
||||||
|
return adapter = new TimelinesAdapter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void scrollToTop() {
|
||||||
|
smoothScrollRecyclerViewToTop(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (updated) UiUtils.restartApp();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TimelinesAdapter extends RecyclerView.Adapter<TimelineViewHolder>{
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public TimelineViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
|
return new TimelineViewHolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull TimelineViewHolder holder, int position) {
|
||||||
|
holder.bind(data.get(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return data.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TimelineViewHolder extends BindableViewHolder<TimelineDefinition> implements UsableRecyclerView.Clickable{
|
||||||
|
private final TextView title;
|
||||||
|
private final ImageView dragger;
|
||||||
|
|
||||||
|
public TimelineViewHolder(){
|
||||||
|
super(getActivity(), R.layout.item_text, list);
|
||||||
|
title=findViewById(R.id.title);
|
||||||
|
dragger=findViewById(R.id.dragger_thingy);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
@Override
|
||||||
|
public void onBind(TimelineDefinition item) {
|
||||||
|
title.setText(item.getTitle(getContext()));
|
||||||
|
title.setCompoundDrawablesRelativeWithIntrinsicBounds(itemView.getContext().getDrawable(item.getIcon().iconRes), null, null, null);
|
||||||
|
dragger.setVisibility(View.VISIBLE);
|
||||||
|
dragger.setOnTouchListener((View v, MotionEvent event) -> {
|
||||||
|
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||||
|
itemTouchHelper.startDrag(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
@Override
|
||||||
|
public void onClick() {
|
||||||
|
Context ctx = getContext();
|
||||||
|
LinearLayout view = (LinearLayout) getActivity().getLayoutInflater()
|
||||||
|
.inflate(R.layout.edit_timeline, (ViewGroup) itemView, false);
|
||||||
|
|
||||||
|
TextInputFrameLayout inputLayout = view.findViewById(R.id.input);
|
||||||
|
EditText editText = inputLayout.getEditText();
|
||||||
|
editText.setText(item.getCustomTitle());
|
||||||
|
editText.setHint(item.getDefaultTitle(ctx));
|
||||||
|
|
||||||
|
ImageButton btn = view.findViewById(R.id.button);
|
||||||
|
PopupMenu popup = new PopupMenu(ctx, btn);
|
||||||
|
TimelineDefinition.Icon currentIcon = item.getIcon();
|
||||||
|
btn.setImageResource(currentIcon.iconRes);
|
||||||
|
btn.setContentDescription(ctx.getString(currentIcon.nameRes));
|
||||||
|
btn.setOnTouchListener(popup.getDragToOpenListener());
|
||||||
|
btn.setOnClickListener(l -> popup.show());
|
||||||
|
|
||||||
|
Menu menu = popup.getMenu();
|
||||||
|
TimelineDefinition.Icon defaultIcon = item.getDefaultIcon();
|
||||||
|
menu.add(0, currentIcon.ordinal(), NONE, currentIcon.nameRes).setIcon(currentIcon.iconRes);
|
||||||
|
if (!currentIcon.equals(defaultIcon)) {
|
||||||
|
menu.add(0, defaultIcon.ordinal(), NONE, defaultIcon.nameRes).setIcon(defaultIcon.iconRes);
|
||||||
|
}
|
||||||
|
for (TimelineDefinition.Icon icon : TimelineDefinition.Icon.values()) {
|
||||||
|
if (icon.hidden || icon.equals(item.getIcon())) continue;
|
||||||
|
menu.add(0, icon.ordinal(), NONE, icon.nameRes).setIcon(icon.iconRes);
|
||||||
|
}
|
||||||
|
UiUtils.enablePopupMenuIcons(ctx, popup);
|
||||||
|
|
||||||
|
popup.setOnMenuItemClickListener(menuItem -> {
|
||||||
|
TimelineDefinition.Icon icon = TimelineDefinition.Icon.values()[menuItem.getItemId()];
|
||||||
|
btn.setImageResource(icon.iconRes);
|
||||||
|
btn.setContentDescription(ctx.getString(icon.nameRes));
|
||||||
|
item.setIcon(icon);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
new M3AlertDialogBuilder(ctx)
|
||||||
|
.setTitle(R.string.sk_edit_timeline)
|
||||||
|
.setView(view)
|
||||||
|
.setPositiveButton(R.string.save, (d, which) -> {
|
||||||
|
item.setTitle(editText.getText().toString().trim());
|
||||||
|
rebind();
|
||||||
|
saveTimelines();
|
||||||
|
})
|
||||||
|
.setNeutralButton(R.string.sk_remove, (d, which) ->
|
||||||
|
removeTimeline(getAbsoluteAdapterPosition()))
|
||||||
|
.setNegativeButton(R.string.cancel, (d, which) -> {})
|
||||||
|
.show();
|
||||||
|
|
||||||
|
btn.requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ItemTouchHelperCallback extends ItemTouchHelper.SimpleCallback {
|
||||||
|
public ItemTouchHelperCallback() {
|
||||||
|
super(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
|
||||||
|
int fromPosition = viewHolder.getAbsoluteAdapterPosition();
|
||||||
|
int toPosition = target.getAbsoluteAdapterPosition();
|
||||||
|
if (Math.max(fromPosition, toPosition) >= data.size() || Math.min(fromPosition, toPosition) < 0) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
Collections.swap(data, fromPosition, toPosition);
|
||||||
|
adapter.notifyItemMoved(fromPosition, toPosition);
|
||||||
|
saveTimelines();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
|
||||||
|
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG && viewHolder != null) {
|
||||||
|
viewHolder.itemView.animate().alpha(0.65f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
|
||||||
|
super.clearView(recyclerView, viewHolder);
|
||||||
|
viewHolder.itemView.animate().alpha(1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
|
||||||
|
int position = viewHolder.getAbsoluteAdapterPosition();
|
||||||
|
removeTimeline(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ public class FavoritedStatusListFragment extends StatusListFragment{
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Status> result){
|
public void onSuccess(HeaderPaginationList<Status> result){
|
||||||
|
if (getActivity() == null) return;
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
|
|
|
@ -80,6 +80,7 @@ public class FollowRequestsListFragment extends BaseRecyclerFragment<FollowReque
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Account> result){
|
public void onSuccess(HeaderPaginationList<Account> result){
|
||||||
|
if (getActivity() == null) return;
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
|
|
|
@ -55,6 +55,7 @@ public class FollowedHashtagsFragment extends BaseRecyclerFragment<Hashtag> impl
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Hashtag> result){
|
public void onSuccess(HeaderPaginationList<Hashtag> result){
|
||||||
|
if (getActivity() == null) return;
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.HapticFeedbackConstants;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
@ -10,12 +11,15 @@ import android.view.ViewGroup;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.tags.GetHashtag;
|
import org.joinmastodon.android.api.requests.tags.GetHashtag;
|
||||||
import org.joinmastodon.android.api.requests.tags.SetHashtagFollowed;
|
import org.joinmastodon.android.api.requests.tags.SetHashtagFollowed;
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
|
||||||
|
import org.joinmastodon.android.events.HashtagUpdatedEvent;
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.model.TimelineDefinition;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -26,7 +30,7 @@ import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class HashtagTimelineFragment extends StatusListFragment{
|
public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||||
private String hashtag;
|
private String hashtag;
|
||||||
private boolean following;
|
private boolean following;
|
||||||
private ImageButton fab;
|
private ImageButton fab;
|
||||||
|
@ -41,7 +45,6 @@ public class HashtagTimelineFragment extends StatusListFragment{
|
||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
updateTitle(getArguments().getString("hashtag"));
|
updateTitle(getArguments().getString("hashtag"));
|
||||||
following=getArguments().getBoolean("following", false);
|
following=getArguments().getBoolean("following", false);
|
||||||
|
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,35 +57,20 @@ public class HashtagTimelineFragment extends StatusListFragment{
|
||||||
this.following = newFollowing;
|
this.following = newFollowing;
|
||||||
followButton.setTitle(getString(newFollowing ? R.string.unfollow_user : R.string.follow_user, "#" + hashtag));
|
followButton.setTitle(getString(newFollowing ? R.string.unfollow_user : R.string.follow_user, "#" + hashtag));
|
||||||
followButton.setIcon(newFollowing ? R.drawable.ic_fluent_person_delete_24_filled : R.drawable.ic_fluent_person_add_24_regular);
|
followButton.setIcon(newFollowing ? R.drawable.ic_fluent_person_delete_24_filled : R.drawable.ic_fluent_person_add_24_regular);
|
||||||
|
E.post(new HashtagUpdatedEvent(hashtag, following));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.hashtag_timeline, menu);
|
inflater.inflate(R.menu.hashtag_timeline, menu);
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
followButton = menu.findItem(R.id.follow_hashtag);
|
followButton = menu.findItem(R.id.follow_hashtag);
|
||||||
updateFollowingState(following);
|
updateFollowingState(following);
|
||||||
|
|
||||||
followButton.setOnMenuItemClickListener(i -> {
|
|
||||||
updateFollowingState(!following);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getActivity());
|
|
||||||
updateFollowingState(!following);
|
|
||||||
}
|
|
||||||
}).exec(accountID);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
new GetHashtag(hashtag).setCallback(new Callback<>() {
|
new GetHashtag(hashtag).setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Hashtag hashtag) {
|
public void onSuccess(Hashtag hashtag) {
|
||||||
|
if (getActivity() == null) return;
|
||||||
updateTitle(hashtag.name);
|
updateTitle(hashtag.name);
|
||||||
updateFollowingState(hashtag.following);
|
updateFollowingState(hashtag.following);
|
||||||
}
|
}
|
||||||
|
@ -94,12 +82,44 @@ public class HashtagTimelineFragment extends StatusListFragment{
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (super.onOptionsItemSelected(item)) return true;
|
||||||
|
if (item.getItemId() == R.id.follow_hashtag) {
|
||||||
|
updateFollowingState(!following);
|
||||||
|
getToolbar().performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
||||||
|
new SetHashtagFollowed(hashtag, following).setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Hashtag i) {
|
||||||
|
if (getActivity() == null) return;
|
||||||
|
if (i.following == following) Toast.makeText(getActivity(), getString(i.following ? R.string.followed_user : R.string.unfollowed_user, "#" + i.name), Toast.LENGTH_SHORT).show();
|
||||||
|
updateFollowingState(i.following);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
error.showToast(getActivity());
|
||||||
|
updateFollowingState(!following);
|
||||||
|
}
|
||||||
|
}).exec(accountID);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TimelineDefinition makeTimelineDefinition() {
|
||||||
|
return TimelineDefinition.ofHashtag(hashtag);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
currentRequest=new GetHashtagTimeline(hashtag, offset==0 ? null : getMaxID(), null, count)
|
currentRequest=new GetHashtagTimeline(hashtag, offset==0 ? null : getMaxID(), null, count)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result, !result.isEmpty());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import static org.joinmastodon.android.GlobalUserPreferences.showFederatedTimeline;
|
import static org.joinmastodon.android.GlobalUserPreferences.reduceMotion;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
@ -41,21 +41,26 @@ import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.announcements.GetAnnouncements;
|
import org.joinmastodon.android.api.requests.announcements.GetAnnouncements;
|
||||||
import org.joinmastodon.android.api.requests.lists.GetLists;
|
import org.joinmastodon.android.api.requests.lists.GetLists;
|
||||||
import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags;
|
import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags;
|
||||||
|
import org.joinmastodon.android.events.HashtagUpdatedEvent;
|
||||||
|
import org.joinmastodon.android.events.ListDeletedEvent;
|
||||||
|
import org.joinmastodon.android.events.ListUpdatedCreatedEvent;
|
||||||
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
||||||
import org.joinmastodon.android.fragments.discover.FederatedTimelineFragment;
|
|
||||||
import org.joinmastodon.android.fragments.discover.LocalTimelineFragment;
|
|
||||||
import org.joinmastodon.android.model.Announcement;
|
import org.joinmastodon.android.model.Announcement;
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
|
import org.joinmastodon.android.model.TimelineDefinition;
|
||||||
import org.joinmastodon.android.ui.SimpleViewHolder;
|
import org.joinmastodon.android.ui.SimpleViewHolder;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
|
@ -69,14 +74,12 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
private static final int ANNOUNCEMENTS_RESULT = 654;
|
private static final int ANNOUNCEMENTS_RESULT = 654;
|
||||||
|
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private MenuItem announcements;
|
private MenuItem announcements, announcementsAction, settings, settingsAction;
|
||||||
// private ImageView toolbarLogo;
|
// private ImageView toolbarLogo;
|
||||||
private Button toolbarShowNewPostsBtn;
|
private Button toolbarShowNewPostsBtn;
|
||||||
private boolean newPostsBtnShown;
|
private boolean newPostsBtnShown;
|
||||||
private AnimatorSet currentNewPostsAnim;
|
private AnimatorSet currentNewPostsAnim;
|
||||||
private ViewPager2 pager;
|
private ViewPager2 pager;
|
||||||
private final List<Fragment> fragments = new ArrayList<>();
|
|
||||||
private final List<FrameLayout> tabViews = new ArrayList<>();
|
|
||||||
private View switcher;
|
private View switcher;
|
||||||
private FrameLayout toolbarFrame;
|
private FrameLayout toolbarFrame;
|
||||||
private ImageView timelineIcon;
|
private ImageView timelineIcon;
|
||||||
|
@ -85,11 +88,29 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
private PopupMenu switcherPopup;
|
private PopupMenu switcherPopup;
|
||||||
private final Map<Integer, ListTimeline> listItems = new HashMap<>();
|
private final Map<Integer, ListTimeline> listItems = new HashMap<>();
|
||||||
private final Map<Integer, Hashtag> hashtagsItems = new HashMap<>();
|
private final Map<Integer, Hashtag> hashtagsItems = new HashMap<>();
|
||||||
|
private List<TimelineDefinition> timelineDefinitions;
|
||||||
|
private int count;
|
||||||
|
private Fragment[] fragments;
|
||||||
|
private FrameLayout[] tabViews;
|
||||||
|
private TimelineDefinition[] timelines;
|
||||||
|
private final Map<Integer, TimelineDefinition> timelinesByMenuItem = new HashMap<>();
|
||||||
|
private SubMenu hashtagsMenu, listsMenu;
|
||||||
|
private PopupMenu overflowPopup;
|
||||||
|
private View overflowActionView = null;
|
||||||
|
private boolean announcementsBadged, settingsBadged;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
E.register(this);
|
||||||
accountID = getArguments().getString("account");
|
accountID = getArguments().getString("account");
|
||||||
|
timelineDefinitions = GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.DEFAULT_TIMELINES);
|
||||||
|
assert timelineDefinitions != null;
|
||||||
|
if (timelineDefinitions.size() == 0) timelineDefinitions = List.of(TimelineDefinition.HOME_TIMELINE);
|
||||||
|
count = timelineDefinitions.size();
|
||||||
|
fragments = new Fragment[count];
|
||||||
|
tabViews = new FrameLayout[count];
|
||||||
|
timelines = new TimelineDefinition[count];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -104,36 +125,40 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
pager = new ViewPager2(getContext());
|
pager = new ViewPager2(getContext());
|
||||||
toolbarFrame = (FrameLayout) LayoutInflater.from(getContext()).inflate(R.layout.home_toolbar, getToolbar(), false);
|
toolbarFrame = (FrameLayout) LayoutInflater.from(getContext()).inflate(R.layout.home_toolbar, getToolbar(), false);
|
||||||
|
|
||||||
if (fragments.size() == 0) {
|
if (fragments[0] == null) {
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putBoolean("__is_tab", true);
|
args.putBoolean("__is_tab", true);
|
||||||
|
|
||||||
fragments.add(new HomeTimelineFragment());
|
|
||||||
fragments.add(new LocalTimelineFragment());
|
|
||||||
if (showFederatedTimeline) fragments.add(new FederatedTimelineFragment());
|
|
||||||
args=new Bundle(args);
|
|
||||||
args.putBoolean("onlyPosts", true);
|
args.putBoolean("onlyPosts", true);
|
||||||
NotificationsListFragment postsFragment=new NotificationsListFragment();
|
|
||||||
postsFragment.setArguments(args);
|
for (int i = 0; i < timelineDefinitions.size(); i++) {
|
||||||
fragments.add(postsFragment);
|
TimelineDefinition tl = timelineDefinitions.get(i);
|
||||||
|
fragments[i] = tl.getFragment();
|
||||||
|
timelines[i] = tl;
|
||||||
|
}
|
||||||
|
|
||||||
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
|
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
|
||||||
for (int i = 0; i < fragments.size(); i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
fragments.get(i).setArguments(args);
|
fragments[i].setArguments(timelines[i].populateArguments(new Bundle(args)));
|
||||||
FrameLayout tabView = new FrameLayout(getActivity());
|
FrameLayout tabView = new FrameLayout(getActivity());
|
||||||
tabView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
tabView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
tabView.setVisibility(View.GONE);
|
tabView.setVisibility(View.GONE);
|
||||||
tabView.setId(i + 1);
|
tabView.setId(i + 1);
|
||||||
transaction.add(i + 1, fragments.get(i));
|
transaction.add(i + 1, fragments[i]);
|
||||||
view.addView(tabView);
|
view.addView(tabView);
|
||||||
tabViews.add(tabView);
|
tabViews[i] = tabView;
|
||||||
}
|
}
|
||||||
transaction.commit();
|
transaction.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
view.addView(pager, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
view.addView(pager, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
|
||||||
|
overflowActionView = UiUtils.makeOverflowActionView(getContext());
|
||||||
|
overflowPopup = new PopupMenu(getContext(), overflowActionView);
|
||||||
|
overflowPopup.setOnMenuItemClickListener(this::onOptionsItemSelected);
|
||||||
|
overflowActionView.setOnClickListener(l -> overflowPopup.show());
|
||||||
|
overflowActionView.setOnTouchListener(overflowPopup.getDragToOpenListener());
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,37 +172,36 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
collapsedChevron = toolbarFrame.findViewById(R.id.collapsed_chevron);
|
collapsedChevron = toolbarFrame.findViewById(R.id.collapsed_chevron);
|
||||||
switcher = toolbarFrame.findViewById(R.id.switcher_btn);
|
switcher = toolbarFrame.findViewById(R.id.switcher_btn);
|
||||||
switcherPopup = new PopupMenu(getContext(), switcher);
|
switcherPopup = new PopupMenu(getContext(), switcher);
|
||||||
switcherPopup.inflate(R.menu.home_switcher);
|
|
||||||
switcherPopup.setOnMenuItemClickListener(this::onSwitcherItemSelected);
|
switcherPopup.setOnMenuItemClickListener(this::onSwitcherItemSelected);
|
||||||
UiUtils.enablePopupMenuIcons(getContext(), switcherPopup);
|
UiUtils.enablePopupMenuIcons(getContext(), switcherPopup);
|
||||||
switcher.setOnClickListener(v->{
|
switcher.setOnClickListener(v->switcherPopup.show());
|
||||||
updateSwitcherMenu();
|
switcher.setOnTouchListener(switcherPopup.getDragToOpenListener());
|
||||||
switcherPopup.show();
|
updateSwitcherMenu();
|
||||||
});
|
|
||||||
View.OnTouchListener listener = switcherPopup.getDragToOpenListener();
|
|
||||||
switcher.setOnTouchListener((v, m)-> {
|
|
||||||
updateSwitcherMenu();
|
|
||||||
return listener.onTouch(v, m);
|
|
||||||
});
|
|
||||||
|
|
||||||
UiUtils.reduceSwipeSensitivity(pager);
|
UiUtils.reduceSwipeSensitivity(pager);
|
||||||
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
||||||
pager.setAdapter(new HomePagerAdapter());
|
pager.setAdapter(new HomePagerAdapter());
|
||||||
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
|
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPageSelected(int position){
|
public void onPageSelected(int position){
|
||||||
|
if (!reduceMotion) {
|
||||||
|
// setting this here because page transformer appears to fire too late so the
|
||||||
|
// animation can appear bumpy, especially when navigating to a further-away tab
|
||||||
|
switcher.setScaleY(0.85f);
|
||||||
|
switcher.setScaleX(0.85f);
|
||||||
|
switcher.setAlpha(0.65f);
|
||||||
|
}
|
||||||
updateSwitcherIcon(position);
|
updateSwitcherIcon(position);
|
||||||
if (position==0) return;
|
if (!timelines[position].equals(TimelineDefinition.HOME_TIMELINE)) hideNewPostsButton();
|
||||||
hideNewPostsButton();
|
if (fragments[position] instanceof BaseRecyclerFragment<?> page){
|
||||||
if (fragments.get(position) instanceof BaseRecyclerFragment<?> page){
|
|
||||||
if(!page.loaded && !page.isDataLoading()) page.loadData();
|
if(!page.loaded && !page.isDataLoading()) page.loadData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!GlobalUserPreferences.reduceMotion) {
|
if (!reduceMotion) {
|
||||||
pager.setPageTransformer((v, pos) -> {
|
pager.setPageTransformer((v, pos) -> {
|
||||||
if (tabViews.get(pager.getCurrentItem()) != v) return;
|
if (reduceMotion || tabViews[pager.getCurrentItem()] != v) return;
|
||||||
float scaleFactor = Math.max(0.85f, 1 - Math.abs(pos) * 0.06f);
|
float scaleFactor = Math.max(0.85f, 1 - Math.abs(pos) * 0.06f);
|
||||||
switcher.setScaleY(scaleFactor);
|
switcher.setScaleY(scaleFactor);
|
||||||
switcher.setScaleX(scaleFactor);
|
switcher.setScaleX(scaleFactor);
|
||||||
|
@ -187,15 +211,37 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
|
|
||||||
updateToolbarLogo();
|
updateToolbarLogo();
|
||||||
|
|
||||||
|
ViewTreeObserver vto = getToolbar().getViewTreeObserver();
|
||||||
|
if (vto.isAlive()) {
|
||||||
|
vto.addOnGlobalLayoutListener(() -> {
|
||||||
|
Toolbar t = getToolbar();
|
||||||
|
if (t == null) return;
|
||||||
|
int toolbarWidth = t.getWidth();
|
||||||
|
if (toolbarWidth == 0) return;
|
||||||
|
|
||||||
|
int toolbarFrameWidth = toolbarFrame.getWidth();
|
||||||
|
int padding = toolbarWidth - toolbarFrameWidth;
|
||||||
|
FrameLayout parent = ((FrameLayout) toolbarShowNewPostsBtn.getParent());
|
||||||
|
if (padding == parent.getPaddingStart()) return;
|
||||||
|
|
||||||
|
// toolbar frame goes from screen edge to beginning of right-aligned option buttons.
|
||||||
|
// centering button by applying the same space on the left
|
||||||
|
parent.setPaddingRelative(padding, 0, 0, 0);
|
||||||
|
toolbarShowNewPostsBtn.setMaxWidth(toolbarWidth - padding * 2);
|
||||||
|
|
||||||
|
switcher.setPivotX(V.dp(28)); // padding + half of icon
|
||||||
|
switcher.setPivotY(switcher.getHeight() / 2f);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if(GithubSelfUpdater.needSelfUpdating()){
|
if(GithubSelfUpdater.needSelfUpdating()){
|
||||||
E.register(this);
|
|
||||||
updateUpdateState(GithubSelfUpdater.getInstance().getState());
|
updateUpdateState(GithubSelfUpdater.getInstance().getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
new GetLists().setCallback(new Callback<>() {
|
new GetLists().setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<ListTimeline> lists) {
|
public void onSuccess(List<ListTimeline> lists) {
|
||||||
addItemsToMap(lists, listItems);
|
updateList(lists, listItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -207,7 +253,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
new GetFollowedHashtags().setCallback(new Callback<>() {
|
new GetFollowedHashtags().setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Hashtag> hashtags) {
|
public void onSuccess(HeaderPaginationList<Hashtag> hashtags) {
|
||||||
addItemsToMap(hashtags, hashtagsItems);
|
updateList(hashtags, hashtagsItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -215,6 +261,47 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
error.showToast(getContext());
|
error.showToast(getContext());
|
||||||
}
|
}
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
|
|
||||||
|
new GetAnnouncements(false).setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<Announcement> result) {
|
||||||
|
if (getActivity() == null) return;
|
||||||
|
if (result.stream().anyMatch(a -> !a.read)) {
|
||||||
|
announcementsBadged = true;
|
||||||
|
announcements.setVisible(false);
|
||||||
|
announcementsAction.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
error.showToast(getActivity());
|
||||||
|
}
|
||||||
|
}).exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addListsToOverflowMenu() {
|
||||||
|
Context ctx = getContext();
|
||||||
|
listsMenu.clear();
|
||||||
|
listsMenu.getItem().setVisible(listItems.size() > 0);
|
||||||
|
UiUtils.insetPopupMenuIcon(ctx, UiUtils.makeBackItem(listsMenu));
|
||||||
|
listItems.forEach((id, list) -> {
|
||||||
|
MenuItem item = listsMenu.add(Menu.NONE, id, Menu.NONE, list.title);
|
||||||
|
item.setIcon(R.drawable.ic_fluent_people_24_regular);
|
||||||
|
UiUtils.insetPopupMenuIcon(ctx, item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addHashtagsToOverflowMenu() {
|
||||||
|
Context ctx = getContext();
|
||||||
|
hashtagsMenu.clear();
|
||||||
|
hashtagsMenu.getItem().setVisible(hashtagsItems.size() > 0);
|
||||||
|
UiUtils.insetPopupMenuIcon(ctx, UiUtils.makeBackItem(hashtagsMenu));
|
||||||
|
hashtagsItems.forEach((id, hashtag) -> {
|
||||||
|
MenuItem item = hashtagsMenu.add(Menu.NONE, id, Menu.NONE, hashtag.name);
|
||||||
|
item.setIcon(R.drawable.ic_fluent_number_symbol_24_regular);
|
||||||
|
UiUtils.insetPopupMenuIcon(ctx, item);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateToolbarLogo(){
|
public void updateToolbarLogo(){
|
||||||
|
@ -229,11 +316,6 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
|
|
||||||
updateSwitcherIcon(pager.getCurrentItem());
|
updateSwitcherIcon(pager.getCurrentItem());
|
||||||
|
|
||||||
// toolbarLogo=new ImageView(getActivity());
|
|
||||||
// toolbarLogo.setScaleType(ImageView.ScaleType.CENTER);
|
|
||||||
// toolbarLogo.setImageResource(R.drawable.logo);
|
|
||||||
// toolbarLogo.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary)));
|
|
||||||
|
|
||||||
toolbarShowNewPostsBtn=toolbarFrame.findViewById(R.id.show_new_posts_btn);
|
toolbarShowNewPostsBtn=toolbarFrame.findViewById(R.id.show_new_posts_btn);
|
||||||
toolbarShowNewPostsBtn.setCompoundDrawableTintList(toolbarShowNewPostsBtn.getTextColors());
|
toolbarShowNewPostsBtn.setCompoundDrawableTintList(toolbarShowNewPostsBtn.getTextColors());
|
||||||
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N) UiUtils.fixCompoundDrawableTintOnAndroid6(toolbarShowNewPostsBtn);
|
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N) UiUtils.fixCompoundDrawableTintOnAndroid6(toolbarShowNewPostsBtn);
|
||||||
|
@ -254,118 +336,90 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
toolbarShowNewPostsBtn.setScaleY(.8f);
|
toolbarShowNewPostsBtn.setScaleY(.8f);
|
||||||
timelineTitle.setVisibility(View.VISIBLE);
|
timelineTitle.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ViewTreeObserver vto = toolbar.getViewTreeObserver();
|
private void updateOverflowMenu() {
|
||||||
if (vto.isAlive()) {
|
if (getActivity() == null) return;
|
||||||
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
Menu m = overflowPopup.getMenu();
|
||||||
@Override
|
m.clear();
|
||||||
public void onGlobalLayout() {
|
overflowPopup.inflate(R.menu.home_overflow);
|
||||||
Toolbar t = getToolbar();
|
announcements = m.findItem(R.id.announcements);
|
||||||
if (t == null) return;
|
settings = m.findItem(R.id.settings);
|
||||||
int toolbarWidth = t.getWidth();
|
hashtagsMenu = m.findItem(R.id.hashtags).getSubMenu();
|
||||||
if (toolbarWidth == 0) return;
|
listsMenu = m.findItem(R.id.lists).getSubMenu();
|
||||||
t.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
|
||||||
|
|
||||||
int toolbarFrameWidth = toolbarFrame.getWidth();
|
announcements.setVisible(!announcementsBadged);
|
||||||
int padding = toolbarWidth - toolbarFrameWidth;
|
announcementsAction.setVisible(announcementsBadged);
|
||||||
// toolbar frame goes from screen edge to beginning of right-aligned option buttons.
|
settings.setVisible(!settingsBadged);
|
||||||
// centering button by applying the same space on the left
|
settingsAction.setVisible(settingsBadged);
|
||||||
((FrameLayout) toolbarShowNewPostsBtn.getParent()).setPaddingRelative(padding, 0, 0, 0);
|
|
||||||
toolbarShowNewPostsBtn.setMaxWidth(toolbarWidth - padding * 2);
|
|
||||||
|
|
||||||
switcher.setPivotX(V.dp(28)); // padding + half of icon
|
UiUtils.enablePopupMenuIcons(getContext(), overflowPopup);
|
||||||
switcher.setPivotY(switcher.getHeight() / 2f);
|
|
||||||
timelineTitle.setPivotX(timelineTitle.getWidth() - V.dp(8));
|
addListsToOverflowMenu();
|
||||||
timelineTitle.setPivotY(timelineTitle.getHeight() / 2f);
|
addHashtagsToOverflowMenu();
|
||||||
}
|
|
||||||
});
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
m.setGroupDividerEnabled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
||||||
inflater.inflate(R.menu.home, menu);
|
inflater.inflate(R.menu.home, menu);
|
||||||
announcements = menu.findItem(R.id.announcements);
|
|
||||||
|
|
||||||
new GetAnnouncements(false).setCallback(new Callback<>() {
|
menu.findItem(R.id.overflow).setActionView(overflowActionView);
|
||||||
@Override
|
announcementsAction = menu.findItem(R.id.announcements_action);
|
||||||
public void onSuccess(List<Announcement> result) {
|
settingsAction = menu.findItem(R.id.settings_action);
|
||||||
boolean hasUnread = result.stream().anyMatch(a -> !a.read);
|
|
||||||
announcements.setIcon(hasUnread ? R.drawable.ic_announcements_24_badged : R.drawable.ic_fluent_megaphone_24_regular);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
updateOverflowMenu();
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getActivity());
|
|
||||||
}
|
|
||||||
}).exec(accountID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void addItemsToMap(List<T> addItems, Map<Integer, T> items) {
|
private <T> void updateList(List<T> addItems, Map<Integer, T> items) {
|
||||||
if (addItems.size() == 0) return;
|
if (addItems.size() == 0) return;
|
||||||
for (int i = 0; i < addItems.size(); i++) items.put(View.generateViewId(), addItems.get(i));
|
for (int i = 0; i < addItems.size(); i++) items.put(View.generateViewId(), addItems.get(i));
|
||||||
updateSwitcherMenu();
|
updateOverflowMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSwitcherMenu() {
|
private void updateSwitcherMenu() {
|
||||||
Context context = getContext();
|
Menu switcherMenu = switcherPopup.getMenu();
|
||||||
switcherPopup.getMenu().findItem(R.id.federated).setVisible(showFederatedTimeline);
|
switcherMenu.clear();
|
||||||
|
timelinesByMenuItem.clear();
|
||||||
|
|
||||||
if (!listItems.isEmpty()) {
|
for (TimelineDefinition tl : timelines) {
|
||||||
MenuItem listsItem = switcherPopup.getMenu().findItem(R.id.lists);
|
int menuItemId = View.generateViewId();
|
||||||
listsItem.setVisible(true);
|
timelinesByMenuItem.put(menuItemId, tl);
|
||||||
SubMenu listsMenu = listsItem.getSubMenu();
|
MenuItem item = switcherMenu.add(0, menuItemId, 0, tl.getTitle(getContext()));
|
||||||
listsMenu.clear();
|
item.setIcon(tl.getIcon().iconRes);
|
||||||
listItems.forEach((id, list) -> {
|
|
||||||
MenuItem item = listsMenu.add(Menu.NONE, id, Menu.NONE, list.title);
|
|
||||||
item.setIcon(R.drawable.ic_fluent_people_list_24_regular);
|
|
||||||
UiUtils.insetPopupMenuIcon(context, item);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hashtagsItems.isEmpty()) {
|
UiUtils.enablePopupMenuIcons(getContext(), switcherPopup);
|
||||||
MenuItem hashtagsItem = switcherPopup.getMenu().findItem(R.id.followed_hashtags);
|
|
||||||
hashtagsItem.setVisible(true);
|
|
||||||
SubMenu hashtagsMenu = hashtagsItem.getSubMenu();
|
|
||||||
hashtagsMenu.clear();
|
|
||||||
hashtagsItems.forEach((id, hashtag) -> {
|
|
||||||
MenuItem item = hashtagsMenu.add(Menu.NONE, id, Menu.NONE, hashtag.name);
|
|
||||||
item.setIcon(R.drawable.ic_fluent_number_symbol_24_regular);
|
|
||||||
UiUtils.insetPopupMenuIcon(context, item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean onSwitcherItemSelected(MenuItem item) {
|
private boolean onSwitcherItemSelected(MenuItem item) {
|
||||||
int id = item.getItemId();
|
int id = item.getItemId();
|
||||||
ListTimeline list;
|
|
||||||
Hashtag hashtag;
|
Bundle args = new Bundle();
|
||||||
if (id == R.id.home) {
|
args.putString("account", accountID);
|
||||||
navigateTo(0);
|
|
||||||
|
if (id == R.id.menu_back) {
|
||||||
|
switcher.post(() -> switcherPopup.show());
|
||||||
return true;
|
return true;
|
||||||
} else if (id == R.id.local) {
|
|
||||||
navigateTo(1);
|
|
||||||
return true;
|
|
||||||
} else if (id == R.id.federated) {
|
|
||||||
navigateTo(2);
|
|
||||||
return true;
|
|
||||||
} else if (id == R.id.post_notifications) {
|
|
||||||
navigateTo(showFederatedTimeline ? 3 : 2);
|
|
||||||
} else if ((list = listItems.get(id)) != null) {
|
|
||||||
Bundle args = new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
args.putString("listID", list.id);
|
|
||||||
args.putString("listTitle", list.title);
|
|
||||||
args.putInt("repliesPolicy", list.repliesPolicy.ordinal());
|
|
||||||
Nav.go(getActivity(), ListTimelineFragment.class, args);
|
|
||||||
} else if ((hashtag = hashtagsItems.get(id)) != null) {
|
|
||||||
UiUtils.openHashtagTimeline(getActivity(), accountID, hashtag.name, hashtag.following);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimelineDefinition tl = timelinesByMenuItem.get(id);
|
||||||
|
if (tl != null) {
|
||||||
|
for (int i = 0; i < timelines.length; i++) {
|
||||||
|
if (timelines[i] == tl) {
|
||||||
|
navigateTo(i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateTo(int i) {
|
private void navigateTo(int i) {
|
||||||
navigateTo(i, !GlobalUserPreferences.reduceMotion);
|
navigateTo(i, !reduceMotion);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateTo(int i, boolean smooth) {
|
private void navigateTo(int i, boolean smooth) {
|
||||||
|
@ -374,38 +428,43 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSwitcherIcon(int i) {
|
private void updateSwitcherIcon(int i) {
|
||||||
// todo: refactor when implementing pinned tabs
|
timelineIcon.setImageResource(timelines[i].getIcon().iconRes);
|
||||||
if (i == (showFederatedTimeline ? 3 : 2)) {
|
timelineTitle.setText(timelines[i].getTitle(getContext()));
|
||||||
timelineIcon.setImageResource(R.drawable.ic_fluent_alert_24_regular);
|
|
||||||
timelineTitle.setText(R.string.sk_notify_posts);
|
|
||||||
} else {
|
|
||||||
timelineIcon.setImageResource(switch (i) {
|
|
||||||
default -> R.drawable.ic_fluent_home_24_regular;
|
|
||||||
case 1 -> R.drawable.ic_fluent_people_community_24_regular;
|
|
||||||
case 2 -> R.drawable.ic_fluent_earth_24_regular;
|
|
||||||
});
|
|
||||||
timelineTitle.setText(switch (i) {
|
|
||||||
default -> R.string.sk_timeline_home;
|
|
||||||
case 1 -> R.string.sk_timeline_local;
|
|
||||||
case 2 -> R.string.sk_timeline_federated;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item){
|
public boolean onOptionsItemSelected(MenuItem item){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
if (item.getItemId() == R.id.settings) Nav.go(getActivity(), SettingsFragment.class, args);
|
int id = item.getItemId();
|
||||||
if (item.getItemId() == R.id.announcements) {
|
ListTimeline list;
|
||||||
|
Hashtag hashtag;
|
||||||
|
|
||||||
|
if (item.getItemId() == R.id.menu_back) {
|
||||||
|
getToolbar().post(() -> overflowPopup.show());
|
||||||
|
return true;
|
||||||
|
} else if (id == R.id.settings || id == R.id.settings_action) {
|
||||||
|
Nav.go(getActivity(), SettingsFragment.class, args);
|
||||||
|
} else if (id == R.id.announcements || id == R.id.announcements_action) {
|
||||||
Nav.goForResult(getActivity(), AnnouncementsFragment.class, args, ANNOUNCEMENTS_RESULT, this);
|
Nav.goForResult(getActivity(), AnnouncementsFragment.class, args, ANNOUNCEMENTS_RESULT, this);
|
||||||
|
} else if (id == R.id.edit_timelines) {
|
||||||
|
Nav.go(getActivity(), EditTimelinesFragment.class, args);
|
||||||
|
} else if ((list = listItems.get(id)) != null) {
|
||||||
|
args.putString("listID", list.id);
|
||||||
|
args.putString("listTitle", list.title);
|
||||||
|
if (list.repliesPolicy != null) args.putInt("repliesPolicy", list.repliesPolicy.ordinal());
|
||||||
|
Nav.go(getActivity(), ListTimelineFragment.class, args);
|
||||||
|
} else if ((hashtag = hashtagsItems.get(id)) != null) {
|
||||||
|
args.putString("hashtag", hashtag.name);
|
||||||
|
args.putBoolean("following", hashtag.following);
|
||||||
|
Nav.go(getActivity(), HashtagTimelineFragment.class, args);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void scrollToTop(){
|
public void scrollToTop(){
|
||||||
((ScrollableToTop) fragments.get(pager.getCurrentItem())).scrollToTop();
|
((ScrollableToTop) fragments[pager.getCurrentItem()]).scrollToTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hideNewPostsButton(){
|
public void hideNewPostsButton(){
|
||||||
|
@ -426,7 +485,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, .8f),
|
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, .8f),
|
||||||
ObjectAnimator.ofFloat(collapsedChevron, View.ALPHA, 0f)
|
ObjectAnimator.ofFloat(collapsedChevron, View.ALPHA, 0f)
|
||||||
);
|
);
|
||||||
set.setDuration(GlobalUserPreferences.reduceMotion ? 0 : 300);
|
set.setDuration(reduceMotion ? 0 : 300);
|
||||||
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
set.addListener(new AnimatorListenerAdapter(){
|
set.addListener(new AnimatorListenerAdapter(){
|
||||||
@Override
|
@Override
|
||||||
|
@ -441,7 +500,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showNewPostsButton(){
|
public void showNewPostsButton(){
|
||||||
if(newPostsBtnShown || pager == null || pager.getCurrentItem() != 0)
|
if(newPostsBtnShown || pager == null || !timelines[pager.getCurrentItem()].equals(TimelineDefinition.HOME_TIMELINE))
|
||||||
return;
|
return;
|
||||||
newPostsBtnShown=true;
|
newPostsBtnShown=true;
|
||||||
if(currentNewPostsAnim!=null){
|
if(currentNewPostsAnim!=null){
|
||||||
|
@ -459,7 +518,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, 1f),
|
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, 1f),
|
||||||
ObjectAnimator.ofFloat(collapsedChevron, View.ALPHA, 1f)
|
ObjectAnimator.ofFloat(collapsedChevron, View.ALPHA, 1f)
|
||||||
);
|
);
|
||||||
set.setDuration(GlobalUserPreferences.reduceMotion ? 0 : 300);
|
set.setDuration(reduceMotion ? 0 : 300);
|
||||||
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
set.addListener(new AnimatorListenerAdapter(){
|
set.addListener(new AnimatorListenerAdapter(){
|
||||||
@Override
|
@Override
|
||||||
|
@ -484,15 +543,20 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFragmentResult(int reqCode, boolean noMoreUnread, Bundle result){
|
public void onFragmentResult(int reqCode, boolean success, Bundle result){
|
||||||
if (reqCode == ANNOUNCEMENTS_RESULT && noMoreUnread) {
|
if (reqCode == ANNOUNCEMENTS_RESULT && success) {
|
||||||
announcements.setIcon(R.drawable.ic_fluent_megaphone_24_regular);
|
announcementsBadged = false;
|
||||||
|
announcements.setVisible(true);
|
||||||
|
announcementsAction.setVisible(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUpdateState(GithubSelfUpdater.UpdateState state){
|
private void updateUpdateState(GithubSelfUpdater.UpdateState state){
|
||||||
if(state!=GithubSelfUpdater.UpdateState.NO_UPDATE && state!=GithubSelfUpdater.UpdateState.CHECKING)
|
if(state!=GithubSelfUpdater.UpdateState.NO_UPDATE && state!=GithubSelfUpdater.UpdateState.CHECKING) {
|
||||||
getToolbar().getMenu().findItem(R.id.settings).setIcon(R.drawable.ic_settings_24_badged);
|
settingsBadged = true;
|
||||||
|
settingsAction.setVisible(true);
|
||||||
|
settings.setVisible(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
|
@ -512,11 +576,26 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView(){
|
public void onDestroyView(){
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
|
if (overflowPopup != null) {
|
||||||
|
overflowPopup.dismiss();
|
||||||
|
overflowPopup = null;
|
||||||
|
}
|
||||||
|
if (switcherPopup != null) {
|
||||||
|
switcherPopup.dismiss();
|
||||||
|
switcherPopup = null;
|
||||||
|
}
|
||||||
if(GithubSelfUpdater.needSelfUpdating()){
|
if(GithubSelfUpdater.needSelfUpdating()){
|
||||||
E.unregister(this);
|
E.unregister(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onShown() {
|
||||||
|
super.onShown();
|
||||||
|
Object pinnedTimelines = GlobalUserPreferences.pinnedTimelines.get(accountID);
|
||||||
|
if (pinnedTimelines != null && timelineDefinitions != pinnedTimelines) UiUtils.restartApp();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewStateRestored(Bundle savedInstanceState) {
|
public void onViewStateRestored(Bundle savedInstanceState) {
|
||||||
super.onViewStateRestored(savedInstanceState);
|
super.onViewStateRestored(savedInstanceState);
|
||||||
|
@ -530,11 +609,59 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
outState.putInt("selectedTab", pager.getCurrentItem());
|
outState.putInt("selectedTab", pager.getCurrentItem());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onHashtagUpdatedEvent(HashtagUpdatedEvent event) {
|
||||||
|
handleListEvent(hashtagsItems, h -> h.name.equalsIgnoreCase(event.name), event.following, () -> {
|
||||||
|
Hashtag hashtag = new Hashtag();
|
||||||
|
hashtag.name = event.name;
|
||||||
|
hashtag.following = true;
|
||||||
|
return hashtag;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onListDeletedEvent(ListDeletedEvent event) {
|
||||||
|
handleListEvent(listItems, l -> l.id.equals(event.id), false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onListUpdatedCreatedEvent(ListUpdatedCreatedEvent event) {
|
||||||
|
handleListEvent(listItems, l -> l.id.equals(event.id), true, () -> {
|
||||||
|
ListTimeline list = new ListTimeline();
|
||||||
|
list.id = event.id;
|
||||||
|
list.title = event.title;
|
||||||
|
list.repliesPolicy = event.repliesPolicy;
|
||||||
|
return list;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> void handleListEvent(
|
||||||
|
Map<Integer, T> existingThings,
|
||||||
|
Predicate<T> matchExisting,
|
||||||
|
boolean shouldBeInList,
|
||||||
|
Supplier<T> makeNewThing
|
||||||
|
) {
|
||||||
|
Optional<Map.Entry<Integer, T>> existingThing = existingThings.entrySet().stream()
|
||||||
|
.filter(e -> matchExisting.test(e.getValue())).findFirst();
|
||||||
|
if (shouldBeInList) {
|
||||||
|
existingThings.put(existingThing.isPresent()
|
||||||
|
? existingThing.get().getKey() : View.generateViewId(), makeNewThing.get());
|
||||||
|
updateOverflowMenu();
|
||||||
|
} else if (existingThing.isPresent() && !shouldBeInList) {
|
||||||
|
existingThings.remove(existingThing.get().getKey());
|
||||||
|
updateOverflowMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Hashtag> getHashtags() {
|
||||||
|
return hashtagsItems.values();
|
||||||
|
}
|
||||||
|
|
||||||
private class HomePagerAdapter extends RecyclerView.Adapter<SimpleViewHolder> {
|
private class HomePagerAdapter extends RecyclerView.Adapter<SimpleViewHolder> {
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
FrameLayout tabView = tabViews.get(viewType % getItemCount());
|
FrameLayout tabView = tabViews[viewType % getItemCount()];
|
||||||
((ViewGroup)tabView.getParent()).removeView(tabView);
|
((ViewGroup)tabView.getParent()).removeView(tabView);
|
||||||
tabView.setVisibility(View.VISIBLE);
|
tabView.setVisibility(View.VISIBLE);
|
||||||
return new SimpleViewHolder(tabView);
|
return new SimpleViewHolder(tabView);
|
||||||
|
@ -545,7 +672,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount(){
|
public int getItemCount(){
|
||||||
return fragments.size();
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -58,8 +58,7 @@ public class HomeTimelineFragment extends FabStatusListFragment {
|
||||||
.getHomeTimeline(offset>0 ? maxID : null, count, refreshing, new SimpleCallback<>(this){
|
.getHomeTimeline(offset>0 ? maxID : null, count, refreshing, new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(CacheablePaginatedResponse<List<Status>> result){
|
public void onSuccess(CacheablePaginatedResponse<List<Status>> result){
|
||||||
if(getActivity()==null)
|
if (getActivity() == null) return;
|
||||||
return;
|
|
||||||
List<Status> filteredItems = filterPosts(result.items);
|
List<Status> filteredItems = filterPosts(result.items);
|
||||||
onDataLoaded(filteredItems, !result.items.isEmpty());
|
onDataLoaded(filteredItems, !result.items.isEmpty());
|
||||||
maxID=result.maxID;
|
maxID=result.maxID;
|
||||||
|
@ -150,7 +149,7 @@ public class HomeTimelineFragment extends FabStatusListFragment {
|
||||||
toAdd=toAdd.stream().filter(filterPredicate).collect(Collectors.toList());
|
toAdd=toAdd.stream().filter(filterPredicate).collect(Collectors.toList());
|
||||||
if(!toAdd.isEmpty()){
|
if(!toAdd.isEmpty()){
|
||||||
prependItems(toAdd, true);
|
prependItems(toAdd, true);
|
||||||
if (parent != null) parent.showNewPostsButton();
|
if (parent != null && GlobalUserPreferences.showNewPostsButton) parent.showNewPostsButton();
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(toAdd, false);
|
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(toAdd, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
public interface IsOnTop {
|
||||||
|
boolean isOnTop();
|
||||||
|
|
||||||
|
default boolean isRecyclerViewOnTop(@Nullable RecyclerView list) {
|
||||||
|
if (list == null) return true;
|
||||||
|
return !list.canScrollVertically(-1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,12 +9,18 @@ import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.lists.CreateList;
|
import org.joinmastodon.android.api.requests.lists.GetList;
|
||||||
import org.joinmastodon.android.api.requests.lists.UpdateList;
|
import org.joinmastodon.android.api.requests.lists.UpdateList;
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetListTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetListTimeline;
|
||||||
|
import org.joinmastodon.android.events.ListDeletedEvent;
|
||||||
|
import org.joinmastodon.android.events.ListUpdatedCreatedEvent;
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.model.TimelineDefinition;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.ListTimelineEditor;
|
import org.joinmastodon.android.ui.views.ListTimelineEditor;
|
||||||
|
@ -28,9 +34,10 @@ import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
|
|
||||||
public class ListTimelineFragment extends StatusListFragment {
|
public class ListTimelineFragment extends PinnableStatusListFragment {
|
||||||
private String listID;
|
private String listID;
|
||||||
private String listTitle;
|
private String listTitle;
|
||||||
|
@Nullable
|
||||||
private ListTimeline.RepliesPolicy repliesPolicy;
|
private ListTimeline.RepliesPolicy repliesPolicy;
|
||||||
private ImageButton fab;
|
private ImageButton fab;
|
||||||
|
|
||||||
|
@ -48,39 +55,58 @@ public class ListTimelineFragment extends StatusListFragment {
|
||||||
|
|
||||||
setTitle(listTitle);
|
setTitle(listTitle);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
|
new GetList(listID).setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(ListTimeline listTimeline) {
|
||||||
|
if (getActivity() == null) return;
|
||||||
|
// TODO: save updated info
|
||||||
|
if (!listTimeline.title.equals(listTitle)) setTitle(listTimeline.title);
|
||||||
|
if (listTimeline.repliesPolicy != null && !listTimeline.repliesPolicy.equals(repliesPolicy)) {
|
||||||
|
repliesPolicy = listTimeline.repliesPolicy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
error.showToast(getContext());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
inflater.inflate(R.menu.list, menu);
|
inflater.inflate(R.menu.list, menu);
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
UiUtils.enableOptionsMenuIcons(getContext(), menu, R.id.pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
Bundle args = new Bundle();
|
if (super.onOptionsItemSelected(item)) return true;
|
||||||
args.putString("listID", listID);
|
|
||||||
if (item.getItemId() == R.id.edit) {
|
if (item.getItemId() == R.id.edit) {
|
||||||
ListTimelineEditor editor = new ListTimelineEditor(getContext());
|
ListTimelineEditor editor = new ListTimelineEditor(getContext());
|
||||||
editor.applyList(listTitle, repliesPolicy);
|
editor.applyList(listTitle, repliesPolicy);
|
||||||
new M3AlertDialogBuilder(getActivity())
|
new M3AlertDialogBuilder(getActivity())
|
||||||
.setTitle(R.string.sk_edit_list_title)
|
.setTitle(R.string.sk_edit_list_title)
|
||||||
.setIcon(R.drawable.ic_fluent_people_list_28_regular)
|
.setIcon(R.drawable.ic_fluent_people_28_regular)
|
||||||
.setView(editor)
|
.setView(editor)
|
||||||
.setPositiveButton(R.string.save, (d, which) -> {
|
.setPositiveButton(R.string.save, (d, which) -> {
|
||||||
new UpdateList(listID, editor.getTitle(), editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
String newTitle = editor.getTitle().trim();
|
||||||
|
setTitle(newTitle);
|
||||||
|
new UpdateList(listID, newTitle, editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(ListTimeline list) {
|
public void onSuccess(ListTimeline list) {
|
||||||
|
if (getActivity() == null) return;
|
||||||
setTitle(list.title);
|
setTitle(list.title);
|
||||||
listTitle = list.title;
|
listTitle = list.title;
|
||||||
repliesPolicy = list.repliesPolicy;
|
repliesPolicy = list.repliesPolicy;
|
||||||
args.putString("listTitle", listTitle);
|
E.post(new ListUpdatedCreatedEvent(listID, listTitle, repliesPolicy));
|
||||||
args.putInt("repliesPolicy", repliesPolicy.ordinal());
|
|
||||||
setResult(true, args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error) {
|
public void onError(ErrorResponse error) {
|
||||||
|
setTitle(listTitle);
|
||||||
error.showToast(getContext());
|
error.showToast(getContext());
|
||||||
}
|
}
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
|
@ -89,20 +115,25 @@ public class ListTimelineFragment extends StatusListFragment {
|
||||||
.show();
|
.show();
|
||||||
} else if (item.getItemId() == R.id.delete) {
|
} else if (item.getItemId() == R.id.delete) {
|
||||||
UiUtils.confirmDeleteList(getActivity(), accountID, listID, listTitle, () -> {
|
UiUtils.confirmDeleteList(getActivity(), accountID, listID, listTitle, () -> {
|
||||||
args.putBoolean("deleted", true);
|
E.post(new ListDeletedEvent(listID));
|
||||||
setResult(true, args);
|
|
||||||
Nav.finish(this);
|
Nav.finish(this);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TimelineDefinition makeTimelineDefinition() {
|
||||||
|
return TimelineDefinition.ofList(listID, listTitle);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count) {
|
protected void doLoadData(int offset, int count) {
|
||||||
currentRequest=new GetListTimeline(listID, offset==0 ? null : getMaxID(), null, count, null)
|
currentRequest=new GetListTimeline(listID, offset==0 ? null : getMaxID(), null, count, null)
|
||||||
.setCallback(new SimpleCallback<>(this) {
|
.setCallback(new SimpleCallback<>(this) {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result) {
|
public void onSuccess(List<Status> result) {
|
||||||
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result, !result.isEmpty());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -12,12 +12,17 @@ import android.widget.TextView;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.api.requests.lists.AddAccountsToList;
|
import org.joinmastodon.android.api.requests.lists.AddAccountsToList;
|
||||||
import org.joinmastodon.android.api.requests.lists.CreateList;
|
import org.joinmastodon.android.api.requests.lists.CreateList;
|
||||||
import org.joinmastodon.android.api.requests.lists.GetLists;
|
import org.joinmastodon.android.api.requests.lists.GetLists;
|
||||||
import org.joinmastodon.android.api.requests.lists.RemoveAccountsFromList;
|
import org.joinmastodon.android.api.requests.lists.RemoveAccountsFromList;
|
||||||
|
import org.joinmastodon.android.events.ListDeletedEvent;
|
||||||
|
import org.joinmastodon.android.events.ListUpdatedCreatedEvent;
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
|
@ -37,210 +42,219 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class ListTimelinesFragment extends BaseRecyclerFragment<ListTimeline> implements ScrollableToTop {
|
public class ListTimelinesFragment extends BaseRecyclerFragment<ListTimeline> implements ScrollableToTop {
|
||||||
private static final int LIST_CHANGED_RESULT = 987;
|
private String accountId;
|
||||||
|
private String profileAccountId;
|
||||||
|
private final HashMap<String, Boolean> userInListBefore = new HashMap<>();
|
||||||
|
private final HashMap<String, Boolean> userInList = new HashMap<>();
|
||||||
|
private ListsAdapter adapter;
|
||||||
|
|
||||||
private String accountId;
|
public ListTimelinesFragment() {
|
||||||
private String profileAccountId;
|
super(10);
|
||||||
private String profileDisplayUsername;
|
}
|
||||||
private HashMap<String, Boolean> userInListBefore = new HashMap<>();
|
|
||||||
private HashMap<String, Boolean> userInList = new HashMap<>();
|
|
||||||
private int inProgress = 0;
|
|
||||||
private ListsAdapter adapter;
|
|
||||||
|
|
||||||
public ListTimelinesFragment() {
|
@Override
|
||||||
super(10);
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
}
|
super.onCreate(savedInstanceState);
|
||||||
|
Bundle args=getArguments();
|
||||||
|
accountId=args.getString("account");
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
E.register(this);
|
||||||
|
|
||||||
@Override
|
if(args.containsKey("profileAccount")){
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
profileAccountId=args.getString("profileAccount");
|
||||||
super.onCreate(savedInstanceState);
|
String profileDisplayUsername = args.getString("profileDisplayUsername");
|
||||||
Bundle args=getArguments();
|
setTitle(getString(R.string.sk_lists_with_user, profileDisplayUsername));
|
||||||
accountId=args.getString("account");
|
} else {
|
||||||
setHasOptionsMenu(true);
|
setTitle(R.string.sk_your_lists);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(args.containsKey("profileAccount")){
|
@Override
|
||||||
profileAccountId=args.getString("profileAccount");
|
protected void onShown(){
|
||||||
profileDisplayUsername=args.getString("profileDisplayUsername");
|
super.onShown();
|
||||||
setTitle(getString(R.string.sk_lists_with_user, profileDisplayUsername));
|
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
|
||||||
} else {
|
loadData();
|
||||||
setTitle(R.string.sk_your_lists);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onShown(){
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
super.onShown();
|
super.onViewCreated(view, savedInstanceState);
|
||||||
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
|
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 0.5f, 56, 16));
|
||||||
loadData();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
inflater.inflate(R.menu.menu_list, menu);
|
||||||
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 0.5f, 56, 16));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
inflater.inflate(R.menu.menu_list, menu);
|
if (item.getItemId() == R.id.create) {
|
||||||
}
|
ListTimelineEditor editor = new ListTimelineEditor(getContext());
|
||||||
|
new M3AlertDialogBuilder(getActivity())
|
||||||
|
.setTitle(R.string.sk_create_list_title)
|
||||||
|
.setIcon(R.drawable.ic_fluent_people_add_28_regular)
|
||||||
|
.setView(editor)
|
||||||
|
.setPositiveButton(R.string.sk_create, (d, which) ->
|
||||||
|
new CreateList(editor.getTitle(), editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(ListTimeline list) {
|
||||||
|
saveListMembership(list.id, true);
|
||||||
|
data.add(0, list);
|
||||||
|
adapter.notifyItemRangeInserted(0, 1);
|
||||||
|
E.post(new ListUpdatedCreatedEvent(list.id, list.title, list.repliesPolicy));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public void onError(ErrorResponse error) {
|
||||||
if (item.getItemId() == R.id.create) {
|
error.showToast(getContext());
|
||||||
ListTimelineEditor editor = new ListTimelineEditor(getContext());
|
}
|
||||||
new M3AlertDialogBuilder(getActivity())
|
}).exec(accountId)
|
||||||
.setTitle(R.string.sk_create_list_title)
|
)
|
||||||
.setIcon(R.drawable.ic_fluent_people_add_28_regular)
|
.setNegativeButton(R.string.cancel, (d, which) -> {})
|
||||||
.setView(editor)
|
.show();
|
||||||
.setPositiveButton(R.string.sk_create, (d, which) -> {
|
}
|
||||||
new CreateList(editor.getTitle(), editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
return true;
|
||||||
@Override
|
}
|
||||||
public void onSuccess(ListTimeline list) {
|
|
||||||
saveListMembership(list.id, true);
|
|
||||||
data.add(0, list);
|
|
||||||
adapter.notifyItemRangeInserted(0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private void saveListMembership(String listId, boolean isMember) {
|
||||||
public void onError(ErrorResponse error) {
|
userInList.put(listId, isMember);
|
||||||
error.showToast(getContext());
|
List<String> accountIdList = Collections.singletonList(profileAccountId);
|
||||||
}
|
MastodonAPIRequest<Object> req = isMember ? new AddAccountsToList(listId, accountIdList) : new RemoveAccountsFromList(listId, accountIdList);
|
||||||
}).exec(accountId);
|
req.setCallback(new Callback<>() {
|
||||||
})
|
@Override
|
||||||
.setNegativeButton(R.string.cancel, (d, which) -> {})
|
public void onSuccess(Object o) {}
|
||||||
.show();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveListMembership(String listId, boolean isMember) {
|
@Override
|
||||||
userInList.put(listId, isMember);
|
public void onError(ErrorResponse error) {
|
||||||
List<String> accountIdList = Collections.singletonList(profileAccountId);
|
error.showToast(getContext());
|
||||||
MastodonAPIRequest<Object> req = isMember ? new AddAccountsToList(listId, accountIdList) : new RemoveAccountsFromList(listId, accountIdList);
|
}
|
||||||
req.setCallback(new SimpleCallback<>(this) {
|
}).exec(accountId);
|
||||||
@Override
|
}
|
||||||
public void onSuccess(Object o) {}
|
|
||||||
}).exec(accountId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
userInListBefore.clear();
|
userInListBefore.clear();
|
||||||
userInList.clear();
|
userInList.clear();
|
||||||
currentRequest=(profileAccountId != null ? new GetLists(profileAccountId) : new GetLists())
|
currentRequest=(profileAccountId != null ? new GetLists(profileAccountId) : new GetLists())
|
||||||
.setCallback(new SimpleCallback<>(this) {
|
.setCallback(new SimpleCallback<>(this) {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<ListTimeline> lists) {
|
public void onSuccess(List<ListTimeline> lists) {
|
||||||
for (ListTimeline l : lists) userInListBefore.put(l.id, true);
|
if (getActivity() == null) return;
|
||||||
userInList.putAll(userInListBefore);
|
for (ListTimeline l : lists) userInListBefore.put(l.id, true);
|
||||||
if (profileAccountId == null || !lists.isEmpty()) onDataLoaded(lists, false);
|
userInList.putAll(userInListBefore);
|
||||||
if (profileAccountId == null) return;
|
if (profileAccountId == null || !lists.isEmpty()) onDataLoaded(lists, false);
|
||||||
|
if (profileAccountId == null) return;
|
||||||
|
|
||||||
currentRequest=new GetLists().setCallback(new SimpleCallback<>(ListTimelinesFragment.this) {
|
currentRequest=new GetLists().setCallback(new SimpleCallback<>(ListTimelinesFragment.this) {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<ListTimeline> allLists) {
|
public void onSuccess(List<ListTimeline> allLists) {
|
||||||
List<ListTimeline> newLists = new ArrayList<>();
|
if (getActivity() == null) return;
|
||||||
for (ListTimeline l : allLists) {
|
List<ListTimeline> newLists = new ArrayList<>();
|
||||||
if (lists.stream().noneMatch(e -> e.id.equals(l.id))) newLists.add(l);
|
for (ListTimeline l : allLists) {
|
||||||
if (!userInListBefore.containsKey(l.id)) {
|
if (lists.stream().noneMatch(e -> e.id.equals(l.id))) newLists.add(l);
|
||||||
userInListBefore.put(l.id, false);
|
if (!userInListBefore.containsKey(l.id)) {
|
||||||
}
|
userInListBefore.put(l.id, false);
|
||||||
}
|
}
|
||||||
userInList.putAll(userInListBefore);
|
}
|
||||||
onDataLoaded(newLists, false);
|
userInList.putAll(userInListBefore);
|
||||||
}
|
onDataLoaded(newLists, false);
|
||||||
}).exec(accountId);
|
}
|
||||||
}
|
}).exec(accountId);
|
||||||
})
|
}
|
||||||
.exec(accountId);
|
})
|
||||||
}
|
.exec(accountId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Subscribe
|
||||||
public void onFragmentResult(int reqCode, boolean listChanged, Bundle result){
|
public void onListDeletedEvent(ListDeletedEvent event) {
|
||||||
if (reqCode == LIST_CHANGED_RESULT && listChanged) {
|
for (int i = 0; i < data.size(); i++) {
|
||||||
String listID = result.getString("listID");
|
ListTimeline item = data.get(i);
|
||||||
for (int i = 0; i < data.size(); i++) {
|
if (item.id.equals(event.id)) {
|
||||||
ListTimeline item = data.get(i);
|
data.remove(i);
|
||||||
if (item.id.equals(listID)) {
|
adapter.notifyItemRemoved(i);
|
||||||
if (result.getBoolean("deleted")) {
|
break;
|
||||||
data.remove(i);
|
}
|
||||||
adapter.notifyItemRemoved(i);
|
}
|
||||||
} else {
|
}
|
||||||
item.title = result.getString("listTitle", item.title);
|
|
||||||
item.repliesPolicy = ListTimeline.RepliesPolicy.values()[result.getInt("repliesPolicy")];
|
|
||||||
adapter.notifyItemChanged(i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Subscribe
|
||||||
protected RecyclerView.Adapter<ListViewHolder> getAdapter() {
|
public void onListUpdatedCreatedEvent(ListUpdatedCreatedEvent event) {
|
||||||
return adapter = new ListsAdapter();
|
for (int i = 0; i < data.size(); i++) {
|
||||||
}
|
ListTimeline item = data.get(i);
|
||||||
|
if (item.id.equals(event.id)) {
|
||||||
|
item.title = event.title;
|
||||||
|
item.repliesPolicy = event.repliesPolicy;
|
||||||
|
adapter.notifyItemChanged(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void scrollToTop() {
|
protected RecyclerView.Adapter<ListViewHolder> getAdapter() {
|
||||||
smoothScrollRecyclerViewToTop(list);
|
return adapter = new ListsAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ListsAdapter extends RecyclerView.Adapter<ListViewHolder>{
|
@Override
|
||||||
@NonNull
|
public void scrollToTop() {
|
||||||
@Override
|
smoothScrollRecyclerViewToTop(list);
|
||||||
public ListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
}
|
||||||
return new ListViewHolder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private class ListsAdapter extends RecyclerView.Adapter<ListViewHolder>{
|
||||||
public void onBindViewHolder(@NonNull ListViewHolder holder, int position) {
|
@NonNull
|
||||||
holder.bind(data.get(position));
|
@Override
|
||||||
}
|
public ListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
|
return new ListViewHolder();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public void onBindViewHolder(@NonNull ListViewHolder holder, int position) {
|
||||||
return data.size();
|
holder.bind(data.get(position));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private class ListViewHolder extends BindableViewHolder<ListTimeline> implements UsableRecyclerView.Clickable{
|
@Override
|
||||||
private final TextView title;
|
public int getItemCount() {
|
||||||
private final CheckBox listToggle;
|
return data.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ListViewHolder(){
|
private class ListViewHolder extends BindableViewHolder<ListTimeline> implements UsableRecyclerView.Clickable{
|
||||||
super(getActivity(), R.layout.item_text, list);
|
private final TextView title;
|
||||||
title=findViewById(R.id.title);
|
private final CheckBox listToggle;
|
||||||
listToggle=findViewById(R.id.list_toggle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public ListViewHolder(){
|
||||||
public void onBind(ListTimeline item) {
|
super(getActivity(), R.layout.item_text, list);
|
||||||
title.setText(item.title);
|
title=findViewById(R.id.title);
|
||||||
title.setCompoundDrawablesRelativeWithIntrinsicBounds(itemView.getContext().getDrawable(R.drawable.ic_fluent_people_list_24_regular), null, null, null);
|
listToggle=findViewById(R.id.list_toggle);
|
||||||
if (profileAccountId != null) {
|
}
|
||||||
Boolean checked = userInList.get(item.id);
|
|
||||||
listToggle.setVisibility(View.VISIBLE);
|
|
||||||
listToggle.setChecked(userInList.containsKey(item.id) && checked != null && checked);
|
|
||||||
listToggle.setOnClickListener(this::onClickToggle);
|
|
||||||
} else {
|
|
||||||
listToggle.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onClickToggle(View view) {
|
@Override
|
||||||
saveListMembership(item.id, listToggle.isChecked());
|
public void onBind(ListTimeline item) {
|
||||||
}
|
title.setText(item.title);
|
||||||
|
title.setCompoundDrawablesRelativeWithIntrinsicBounds(itemView.getContext().getDrawable(R.drawable.ic_fluent_people_24_regular), null, null, null);
|
||||||
|
if (profileAccountId != null) {
|
||||||
|
Boolean checked = userInList.get(item.id);
|
||||||
|
listToggle.setVisibility(View.VISIBLE);
|
||||||
|
listToggle.setChecked(userInList.containsKey(item.id) && checked != null && checked);
|
||||||
|
listToggle.setOnClickListener(this::onClickToggle);
|
||||||
|
} else {
|
||||||
|
listToggle.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
private void onClickToggle(View view) {
|
||||||
public void onClick() {
|
saveListMembership(item.id, listToggle.isChecked());
|
||||||
Bundle args=new Bundle();
|
}
|
||||||
args.putString("account", accountId);
|
|
||||||
args.putString("listID", item.id);
|
@Override
|
||||||
args.putString("listTitle", item.title);
|
public void onClick() {
|
||||||
args.putInt("repliesPolicy", item.repliesPolicy.ordinal());
|
Bundle args=new Bundle();
|
||||||
Nav.goForResult(getActivity(), ListTimelineFragment.class, args, LIST_CHANGED_RESULT, ListTimelinesFragment.this);
|
args.putString("account", accountId);
|
||||||
}
|
args.putString("listID", item.id);
|
||||||
}
|
args.putString("listTitle", item.title);
|
||||||
|
if (item.repliesPolicy != null) args.putInt("repliesPolicy", item.repliesPolicy.ordinal());
|
||||||
|
Nav.go(getActivity(), ListTimelineFragment.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,18 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||||
|
|
||||||
tabLayout.setTabTextSize(V.dp(16));
|
tabLayout.setTabTextSize(V.dp(16));
|
||||||
tabLayout.setTabTextColors(UiUtils.getThemeColor(getActivity(), R.attr.colorTabInactive), UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
|
tabLayout.setTabTextColors(UiUtils.getThemeColor(getActivity(), R.attr.colorTabInactive), UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
|
||||||
|
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onTabSelected(TabLayout.Tab tab) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTabUnselected(TabLayout.Tab tab) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTabReselected(TabLayout.Tab tab) {
|
||||||
|
scrollToTop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
pager.setOffscreenPageLimit(4);
|
pager.setOffscreenPageLimit(4);
|
||||||
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
||||||
|
@ -183,6 +195,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||||
new GetFollowRequests(null, 1).setCallback(new Callback<>() {
|
new GetFollowRequests(null, 1).setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Account> accounts) {
|
public void onSuccess(HeaderPaginationList<Account> accounts) {
|
||||||
|
if (getActivity() == null) return;
|
||||||
getToolbar().getMenu().findItem(R.id.follow_requests).setVisible(!accounts.isEmpty());
|
getToolbar().getMenu().findItem(R.id.follow_requests).setVisible(!accounts.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,6 +224,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||||
protected void updateToolbar(){
|
protected void updateToolbar(){
|
||||||
super.updateToolbar();
|
super.updateToolbar();
|
||||||
getToolbar().setOutlineProvider(null);
|
getToolbar().setOutlineProvider(null);
|
||||||
|
getToolbar().setOnClickListener(v->scrollToTop());
|
||||||
}
|
}
|
||||||
|
|
||||||
private NotificationsListFragment getFragmentForPage(int page){
|
private NotificationsListFragment getFragmentForPage(int page){
|
||||||
|
|
|
@ -2,8 +2,7 @@ package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Menu;
|
import android.text.TextUtils;
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
|
@ -14,14 +13,19 @@ import org.joinmastodon.android.api.requests.markers.SaveMarkers;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.PollUpdatedEvent;
|
import org.joinmastodon.android.events.PollUpdatedEvent;
|
||||||
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
||||||
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
import org.joinmastodon.android.model.PaginatedResponse;
|
import org.joinmastodon.android.model.PaginatedResponse;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.AccountStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
|
||||||
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
|
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -41,6 +45,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
private boolean onlyMentions;
|
private boolean onlyMentions;
|
||||||
private boolean onlyPosts;
|
private boolean onlyPosts;
|
||||||
private String maxID;
|
private String maxID;
|
||||||
|
private final DiscoverInfoBannerHelper bannerHelper = new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.POST_NOTIFICATIONS);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
@ -71,6 +76,8 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<StatusDisplayItem> buildDisplayItems(Notification n){
|
protected List<StatusDisplayItem> buildDisplayItems(Notification n){
|
||||||
|
Account reportTarget = n.report == null ? null : n.report.targetAccount == null ? null :
|
||||||
|
n.report.targetAccount;
|
||||||
String extraText=switch(n.type){
|
String extraText=switch(n.type){
|
||||||
case FOLLOW -> getString(R.string.user_followed_you);
|
case FOLLOW -> getString(R.string.user_followed_you);
|
||||||
case FOLLOW_REQUEST -> getString(R.string.user_sent_follow_request);
|
case FOLLOW_REQUEST -> getString(R.string.user_sent_follow_request);
|
||||||
|
@ -78,8 +85,11 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
case REBLOG -> getString(R.string.notification_boosted);
|
case REBLOG -> getString(R.string.notification_boosted);
|
||||||
case FAVORITE -> getString(R.string.user_favorited);
|
case FAVORITE -> getString(R.string.user_favorited);
|
||||||
case POLL -> getString(R.string.poll_ended);
|
case POLL -> getString(R.string.poll_ended);
|
||||||
|
case UPDATE -> getString(R.string.sk_post_edited);
|
||||||
|
case SIGN_UP -> getString(R.string.sk_signed_up);
|
||||||
|
case REPORT -> getString(R.string.sk_reported);
|
||||||
};
|
};
|
||||||
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, null, extraText, n, null) : null;
|
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, n.status, extraText, n, null) : null;
|
||||||
if(n.status!=null){
|
if(n.status!=null){
|
||||||
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, titleItem!=null, titleItem==null, n);
|
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, titleItem!=null, titleItem==null, n);
|
||||||
if(titleItem!=null){
|
if(titleItem!=null){
|
||||||
|
@ -93,8 +103,13 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
items.add(0, titleItem);
|
items.add(0, titleItem);
|
||||||
return items;
|
return items;
|
||||||
}else if(titleItem!=null){
|
}else if(titleItem!=null){
|
||||||
AccountCardStatusDisplayItem card=new AccountCardStatusDisplayItem(n.id, this, n.account, n);
|
AccountCardStatusDisplayItem card=new AccountCardStatusDisplayItem(n.id, this,
|
||||||
return Arrays.asList(titleItem, card);
|
reportTarget != null ? reportTarget : n.account, n);
|
||||||
|
TextStatusDisplayItem text = n.report != null && !TextUtils.isEmpty(n.report.comment) ?
|
||||||
|
new TextStatusDisplayItem(n.id, n.report.comment, this,
|
||||||
|
Status.ofFake(n.id, n.report.comment, n.createdAt), true) :
|
||||||
|
null;
|
||||||
|
return text == null ? Arrays.asList(titleItem, card) : Arrays.asList(titleItem, text, card);
|
||||||
}else{
|
}else{
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
@ -115,8 +130,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
.getNotifications(offset>0 ? maxID : null, count, onlyMentions, onlyPosts, refreshing, new SimpleCallback<>(this){
|
.getNotifications(offset>0 ? maxID : null, count, onlyMentions, onlyPosts, refreshing, new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(PaginatedResponse<List<Notification>> result){
|
public void onSuccess(PaginatedResponse<List<Notification>> result){
|
||||||
if(getActivity()==null)
|
if (getActivity() == null) return;
|
||||||
return;
|
|
||||||
if(refreshing)
|
if(refreshing)
|
||||||
relationships.clear();
|
relationships.clear();
|
||||||
onDataLoaded(result.items.stream().filter(n->n.type!=null).collect(Collectors.toList()), !result.items.isEmpty());
|
onDataLoaded(result.items.stream().filter(n->n.type!=null).collect(Collectors.toList()), !result.items.isEmpty());
|
||||||
|
@ -163,6 +177,9 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
if(status.inReplyToAccountId!=null && knownAccounts.containsKey(status.inReplyToAccountId))
|
if(status.inReplyToAccountId!=null && knownAccounts.containsKey(status.inReplyToAccountId))
|
||||||
args.putParcelable("inReplyToAccount", Parcels.wrap(knownAccounts.get(status.inReplyToAccountId)));
|
args.putParcelable("inReplyToAccount", Parcels.wrap(knownAccounts.get(status.inReplyToAccountId)));
|
||||||
Nav.go(getActivity(), ThreadFragment.class, args);
|
Nav.go(getActivity(), ThreadFragment.class, args);
|
||||||
|
}else if(n.report != null){
|
||||||
|
String domain = AccountSessionManager.getInstance().getAccount(accountID).domain;
|
||||||
|
UiUtils.launchWebBrowser(getActivity(), "https://"+domain+"/admin/reports/"+n.report.id);
|
||||||
}else{
|
}else{
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
|
@ -175,6 +192,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
list.addItemDecoration(new InsetStatusItemDecoration(this));
|
list.addItemDecoration(new InsetStatusItemDecoration(this));
|
||||||
|
if (onlyPosts) bannerHelper.maybeAddBanner(contentWrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Notification getNotificationByID(String id){
|
private Notification getNotificationByID(String id){
|
||||||
|
@ -231,4 +249,4 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||||
displayItems.subList(index, lastIndex).clear();
|
displayItems.subList(index, lastIndex).clear();
|
||||||
adapter.notifyItemRangeRemoved(index, lastIndex-index);
|
adapter.notifyItemRangeRemoved(index, lastIndex-index);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.HapticFeedbackConstants;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.model.TimelineDefinition;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class PinnableStatusListFragment extends StatusListFragment {
|
||||||
|
protected boolean pinnedUpdated;
|
||||||
|
protected List<TimelineDefinition> pinnedTimelines;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
pinnedTimelines = new ArrayList<>(GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.DEFAULT_TIMELINES));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
updatePinButton(menu.findItem(R.id.pin));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isPinned() {
|
||||||
|
return pinnedTimelines.contains(makeTimelineDefinition());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updatePinButton(MenuItem pin) {
|
||||||
|
boolean pinned = isPinned();
|
||||||
|
pin.setIcon(pinned ?
|
||||||
|
R.drawable.ic_fluent_pin_24_filled :
|
||||||
|
R.drawable.ic_fluent_pin_24_regular);
|
||||||
|
pin.setTitle(pinned ? R.string.sk_unpin_timeline : R.string.sk_pin_timeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract TimelineDefinition makeTimelineDefinition();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.pin) {
|
||||||
|
togglePin(item);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void togglePin(MenuItem pin) {
|
||||||
|
pinnedUpdated = true;
|
||||||
|
getToolbar().performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
||||||
|
TimelineDefinition def = makeTimelineDefinition();
|
||||||
|
boolean pinned = isPinned();
|
||||||
|
if (pinned) pinnedTimelines.remove(def);
|
||||||
|
else pinnedTimelines.add(def);
|
||||||
|
Toast.makeText(getContext(), pinned ? R.string.sk_unpinned_timeline : R.string.sk_pinned_timeline, Toast.LENGTH_SHORT).show();
|
||||||
|
GlobalUserPreferences.pinnedTimelines.put(accountID, pinnedTimelines);
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
updatePinButton(pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Bundle getResultArgs() {
|
||||||
|
return new Bundle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
Bundle resultArgs = getResultArgs();
|
||||||
|
if (pinnedUpdated) {
|
||||||
|
resultArgs.putBoolean("pinnedUpdated", true);
|
||||||
|
setResult(true, resultArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -380,6 +380,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
|
if (getActivity() == null) return;
|
||||||
account=result;
|
account=result;
|
||||||
isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account);
|
isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||||
bindHeaderView();
|
bindHeaderView();
|
||||||
|
@ -767,6 +768,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateRelationship(){
|
private void updateRelationship(){
|
||||||
|
if (getActivity() == null) return;
|
||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
actionButton.setVisibility(View.VISIBLE);
|
actionButton.setVisibility(View.VISIBLE);
|
||||||
notifyButton.setVisibility(relationship.following ? View.VISIBLE : View.GONE);
|
notifyButton.setVisibility(relationship.following ? View.VISIBLE : View.GONE);
|
||||||
|
@ -780,7 +782,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
setNote(relationship.note);
|
setNote(relationship.note);
|
||||||
aboutFragment.setNote(relationship.note, accountID, profileAccountID);
|
aboutFragment.setNote(relationship.note, accountID, profileAccountID);
|
||||||
}
|
}
|
||||||
if (getActivity() != null) notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username));
|
notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageButton getFab() {
|
public ImageButton getFab() {
|
||||||
|
@ -867,8 +869,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
editModeLoading=false;
|
editModeLoading=false;
|
||||||
if(getActivity()==null)
|
if (getActivity() == null) return;
|
||||||
return;
|
|
||||||
enterEditMode(result);
|
enterEditMode(result);
|
||||||
setActionProgressVisible(false);
|
setActionProgressVisible(false);
|
||||||
}
|
}
|
||||||
|
@ -876,8 +877,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error){
|
public void onError(ErrorResponse error){
|
||||||
editModeLoading=false;
|
editModeLoading=false;
|
||||||
if(getActivity()==null)
|
if (getActivity() == null) return;
|
||||||
return;
|
|
||||||
error.showToast(getActivity());
|
error.showToast(getActivity());
|
||||||
setActionProgressVisible(false);
|
setActionProgressVisible(false);
|
||||||
}
|
}
|
||||||
|
@ -980,6 +980,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
account=result;
|
account=result;
|
||||||
AccountSessionManager.getInstance().updateAccountInfo(accountID, account);
|
AccountSessionManager.getInstance().updateAccountInfo(accountID, account);
|
||||||
|
if (getActivity() == null) return;
|
||||||
exitEditMode();
|
exitEditMode();
|
||||||
setActionProgressVisible(false);
|
setActionProgressVisible(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,6 +109,7 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
nextMaxID=null;
|
nextMaxID=null;
|
||||||
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result, nextMaxID!=null);
|
onDataLoaded(result, nextMaxID!=null);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,6 +9,8 @@ import android.graphics.Canvas;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.LruCache;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
@ -78,6 +80,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
private ArrayList<Item> items=new ArrayList<>();
|
private ArrayList<Item> items=new ArrayList<>();
|
||||||
private ThemeItem themeItem;
|
private ThemeItem themeItem;
|
||||||
private NotificationPolicyItem notificationPolicyItem;
|
private NotificationPolicyItem notificationPolicyItem;
|
||||||
|
private SwitchItem showNewPostsButtonItem, glitchModeItem;
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private boolean needUpdateNotificationSettings;
|
private boolean needUpdateNotificationSettings;
|
||||||
private boolean needAppRestart;
|
private boolean needAppRestart;
|
||||||
|
@ -123,6 +126,8 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
needAppRestart=true;
|
needAppRestart=true;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
items.add(new ButtonItem(R.string.sk_settings_color_palette, R.drawable.ic_fluent_color_24_regular, b->{
|
items.add(new ButtonItem(R.string.sk_settings_color_palette, R.drawable.ic_fluent_color_24_regular, b->{
|
||||||
PopupMenu popupMenu=new PopupMenu(getActivity(), b, Gravity.CENTER_HORIZONTAL);
|
PopupMenu popupMenu=new PopupMenu(getActivity(), b, Gravity.CENTER_HORIZONTAL);
|
||||||
popupMenu.inflate(R.menu.color_palettes);
|
popupMenu.inflate(R.menu.color_palettes);
|
||||||
|
@ -176,13 +181,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
});}
|
});}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.settings_behavior));
|
items.add(new HeaderItem(R.string.settings_behavior));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_show_federated_timeline, R.drawable.ic_fluent_earth_24_regular, GlobalUserPreferences.showFederatedTimeline, i->{
|
|
||||||
GlobalUserPreferences.showFederatedTimeline=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
needAppRestart=true;
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.settings_gif, R.drawable.ic_fluent_gif_24_regular, GlobalUserPreferences.playGifs, i->{
|
items.add(new SwitchItem(R.string.settings_gif, R.drawable.ic_fluent_gif_24_regular, GlobalUserPreferences.playGifs, i->{
|
||||||
GlobalUserPreferences.playGifs=i.checked;
|
GlobalUserPreferences.playGifs=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
|
@ -210,27 +209,30 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
needAppRestart=true;
|
needAppRestart=true;
|
||||||
}));
|
}));
|
||||||
items.add(new SwitchItem(R.string.mo_hide_compose_button_while_scrolling_setting, R.drawable.ic_fluent_edit_24_regular, GlobalUserPreferences.disableFab, i->{
|
items.add(new SwitchItem(R.string.mo_hide_compose_button_while_scrolling_setting, R.drawable.ic_fluent_edit_24_regular, GlobalUserPreferences.enableFabAutoHide, i->{
|
||||||
GlobalUserPreferences.disableFab=i.checked;
|
GlobalUserPreferences.enableFabAutoHide =i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
needAppRestart=true;
|
needAppRestart=true;
|
||||||
}));
|
}));
|
||||||
// items.add(new SwitchItem(R.string.sk_enable_delete_notifications, R.drawable.ic_fluent_delete_24_regular, GlobalUserPreferences.enableDeleteNotifications, i->{
|
|
||||||
// GlobalUserPreferences.enableDeleteNotifications=i.checked;
|
|
||||||
// GlobalUserPreferences.save();
|
|
||||||
// needAppRestart=true;
|
|
||||||
// }));
|
|
||||||
items.add(new SwitchItem(R.string.mo_relocate_publish_button, R.drawable.ic_fluent_arrow_autofit_down_24_regular, GlobalUserPreferences.relocatePublishButton, i->{
|
items.add(new SwitchItem(R.string.mo_relocate_publish_button, R.drawable.ic_fluent_arrow_autofit_down_24_regular, GlobalUserPreferences.relocatePublishButton, i->{
|
||||||
GlobalUserPreferences.relocatePublishButton=i.checked;
|
GlobalUserPreferences.relocatePublishButton=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_single_notification, R.drawable.ic_fluent_convert_range_24_regular, GlobalUserPreferences.keepOnlyLatestNotification, i->{
|
||||||
|
GlobalUserPreferences.keepOnlyLatestNotification=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
// items.add(new SwitchItem(R.string.sk_settings_translate_only_opened, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
|
||||||
|
// GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
|
||||||
|
// GlobalUserPreferences.save();
|
||||||
|
// }));
|
||||||
// items.add(new SwitchItem(R.string.sk_settings_hide_translate_in_timeline, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
|
// items.add(new SwitchItem(R.string.sk_settings_hide_translate_in_timeline, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
|
||||||
// GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
|
// GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
|
||||||
// GlobalUserPreferences.save();
|
// GlobalUserPreferences.save();
|
||||||
// needAppRestart=true;
|
// needAppRestart=true;
|
||||||
// }));
|
// }));
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.home_timeline));
|
items.add(new HeaderItem(R.string.sk_timelines));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
|
items.add(new SwitchItem(R.string.sk_settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
|
||||||
GlobalUserPreferences.showReplies=i.checked;
|
GlobalUserPreferences.showReplies=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
|
@ -239,23 +241,45 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
GlobalUserPreferences.showBoosts=i.checked;
|
GlobalUserPreferences.showBoosts=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_load_new_posts, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.loadNewPosts, i->{
|
items.add(new SwitchItem(R.string.sk_settings_load_new_posts, R.drawable.ic_fluent_arrow_sync_24_regular, GlobalUserPreferences.loadNewPosts, i->{
|
||||||
GlobalUserPreferences.loadNewPosts=i.checked;
|
GlobalUserPreferences.loadNewPosts=i.checked;
|
||||||
|
showNewPostsButtonItem.enabled = i.checked;
|
||||||
|
if (!i.checked) {
|
||||||
|
GlobalUserPreferences.showNewPostsButton = false;
|
||||||
|
showNewPostsButtonItem.checked = false;
|
||||||
|
}
|
||||||
|
if (list.findViewHolderForAdapterPosition(items.indexOf(showNewPostsButtonItem)) instanceof SwitchViewHolder svh) svh.rebind();
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
|
items.add(showNewPostsButtonItem = new SwitchItem(R.string.sk_settings_show_new_posts_button, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.showNewPostsButton, i->{
|
||||||
|
GlobalUserPreferences.showNewPostsButton=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_show_alt_indicator, R.drawable.ic_fluent_scan_text_24_regular, GlobalUserPreferences.showAltIndicator, i->{
|
||||||
|
GlobalUserPreferences.showAltIndicator=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
needAppRestart=true;
|
||||||
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_show_no_alt_indicator, R.drawable.ic_fluent_important_24_regular, GlobalUserPreferences.showNoAltIndicator, i->{
|
||||||
|
GlobalUserPreferences.showNoAltIndicator=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
needAppRestart=true;
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.settings_notifications));
|
items.add(new HeaderItem(R.string.settings_notifications));
|
||||||
items.add(notificationPolicyItem=new NotificationPolicyItem());
|
items.add(notificationPolicyItem=new NotificationPolicyItem());
|
||||||
PushSubscription pushSubscription=getPushSubscription();
|
PushSubscription pushSubscription=getPushSubscription();
|
||||||
items.add(new SwitchItem(R.string.notify_favorites, R.drawable.ic_fluent_star_24_regular, pushSubscription.alerts.favourite, i->onNotificationsChanged(PushNotification.Type.FAVORITE, i.checked)));
|
boolean switchEnabled=pushSubscription.policy!=PushSubscription.Policy.NONE;
|
||||||
items.add(new SwitchItem(R.string.notify_follow, R.drawable.ic_fluent_person_add_24_regular, pushSubscription.alerts.follow, i->onNotificationsChanged(PushNotification.Type.FOLLOW, i.checked)));
|
|
||||||
items.add(new SwitchItem(R.string.notify_reblog, R.drawable.ic_fluent_arrow_repeat_all_24_regular, pushSubscription.alerts.reblog, i->onNotificationsChanged(PushNotification.Type.REBLOG, i.checked)));
|
items.add(new SwitchItem(R.string.notify_favorites, R.drawable.ic_fluent_star_24_regular, pushSubscription.alerts.favourite, i->onNotificationsChanged(PushNotification.Type.FAVORITE, i.checked), switchEnabled));
|
||||||
items.add(new SwitchItem(R.string.notify_mention, R.drawable.ic_fluent_mention_24_regular, pushSubscription.alerts.mention, i->onNotificationsChanged(PushNotification.Type.MENTION, i.checked)));
|
items.add(new SwitchItem(R.string.notify_follow, R.drawable.ic_fluent_person_add_24_regular, pushSubscription.alerts.follow, i->onNotificationsChanged(PushNotification.Type.FOLLOW, i.checked), switchEnabled));
|
||||||
items.add(new SwitchItem(R.string.sk_notify_posts, R.drawable.ic_fluent_alert_24_regular, pushSubscription.alerts.status, i->onNotificationsChanged(PushNotification.Type.STATUS, i.checked)));
|
items.add(new SwitchItem(R.string.notify_reblog, R.drawable.ic_fluent_arrow_repeat_all_24_regular, pushSubscription.alerts.reblog, i->onNotificationsChanged(PushNotification.Type.REBLOG, i.checked), switchEnabled));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_single_notification, R.drawable.ic_fluent_convert_range_24_regular, GlobalUserPreferences.keepOnlyLatestNotification, i->{
|
items.add(new SwitchItem(R.string.notify_mention, R.drawable.ic_fluent_mention_24_regular, pushSubscription.alerts.mention, i->onNotificationsChanged(PushNotification.Type.MENTION, i.checked), switchEnabled));
|
||||||
GlobalUserPreferences.keepOnlyLatestNotification=i.checked;
|
items.add(new SwitchItem(R.string.sk_notify_posts, R.drawable.ic_fluent_chat_24_regular, pushSubscription.alerts.status, i->onNotificationsChanged(PushNotification.Type.STATUS, i.checked), switchEnabled));
|
||||||
GlobalUserPreferences.save();
|
items.add(new SwitchItem(R.string.sk_notify_update, R.drawable.ic_fluent_history_24_regular, pushSubscription.alerts.update, i->onNotificationsChanged(PushNotification.Type.UPDATE, i.checked), switchEnabled));
|
||||||
}));
|
items.add(new SwitchItem(R.string.sk_notify_poll_results, R.drawable.ic_fluent_poll_24_regular, pushSubscription.alerts.poll, i->onNotificationsChanged(PushNotification.Type.POLL, i.checked), switchEnabled));
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.settings_account));
|
items.add(new HeaderItem(R.string.settings_account));
|
||||||
items.add(new TextItem(R.string.sk_settings_profile, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/settings/profile"), R.drawable.ic_fluent_open_24_regular));
|
items.add(new TextItem(R.string.sk_settings_profile, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/settings/profile"), R.drawable.ic_fluent_open_24_regular));
|
||||||
|
@ -273,6 +297,35 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
items.add(new TextItem(R.string.settings_tos, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms"), R.drawable.ic_fluent_open_24_regular));
|
items.add(new TextItem(R.string.settings_tos, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms"), R.drawable.ic_fluent_open_24_regular));
|
||||||
items.add(new TextItem(R.string.settings_privacy_policy, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms"), R.drawable.ic_fluent_open_24_regular));
|
items.add(new TextItem(R.string.settings_privacy_policy, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms"), R.drawable.ic_fluent_open_24_regular));
|
||||||
items.add(new TextItem(R.string.log_out, this::confirmLogOut, R.drawable.ic_fluent_sign_out_24_regular));
|
items.add(new TextItem(R.string.log_out, this::confirmLogOut, R.drawable.ic_fluent_sign_out_24_regular));
|
||||||
|
if (!TextUtils.isEmpty(instance.version)) items.add(new SmallTextItem(getString(R.string.sk_settings_server_version, instance.version)));
|
||||||
|
|
||||||
|
items.add(new HeaderItem(R.string.sk_instance_features));
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_support_local_only, 0, GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID), i->{
|
||||||
|
glitchModeItem.enabled = i.checked;
|
||||||
|
if (i.checked) {
|
||||||
|
GlobalUserPreferences.accountsWithLocalOnlySupport.add(accountID);
|
||||||
|
if (instance.pleroma == null) GlobalUserPreferences.accountsInGlitchMode.add(accountID);
|
||||||
|
} else {
|
||||||
|
GlobalUserPreferences.accountsWithLocalOnlySupport.remove(accountID);
|
||||||
|
GlobalUserPreferences.accountsInGlitchMode.remove(accountID);
|
||||||
|
}
|
||||||
|
glitchModeItem.checked = GlobalUserPreferences.accountsInGlitchMode.contains(accountID);
|
||||||
|
if (list.findViewHolderForAdapterPosition(items.indexOf(glitchModeItem)) instanceof SwitchViewHolder svh) svh.rebind();
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
items.add(new SmallTextItem(getString(R.string.sk_settings_local_only_explanation)));
|
||||||
|
items.add(glitchModeItem = new SwitchItem(R.string.sk_settings_glitch_instance, 0, GlobalUserPreferences.accountsInGlitchMode.contains(accountID), i->{
|
||||||
|
if (i.checked) {
|
||||||
|
GlobalUserPreferences.accountsInGlitchMode.add(accountID);
|
||||||
|
} else {
|
||||||
|
GlobalUserPreferences.accountsInGlitchMode.remove(accountID);
|
||||||
|
}
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
glitchModeItem.enabled = GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID);
|
||||||
|
items.add(new SmallTextItem(getString(R.string.sk_settings_glitch_mode_explanation)));
|
||||||
|
|
||||||
|
|
||||||
boolean translationAvailable = instance.v2 != null && instance.v2.configuration.translation != null && instance.v2.configuration.translation.enabled;
|
boolean translationAvailable = instance.v2 != null && instance.v2.configuration.translation != null && instance.v2.configuration.translation.enabled;
|
||||||
items.add(new SmallTextItem(getString(translationAvailable ?
|
items.add(new SmallTextItem(getString(translationAvailable ?
|
||||||
R.string.sk_settings_translation_availability_note_available :
|
R.string.sk_settings_translation_availability_note_available :
|
||||||
|
@ -280,21 +333,27 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
|
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.sk_settings_about));
|
items.add(new HeaderItem(R.string.sk_settings_about));
|
||||||
// items.add(new TextItem(R.string.sk_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/sk22/megalodon"), R.drawable.ic_fluent_open_24_regular));
|
|
||||||
// items.add(new TextItem(R.string.sk_settings_donate, ()->UiUtils.launchWebBrowser(getActivity(), "https://ko-fi.com/xsk22"), R.drawable.ic_fluent_heart_24_regular));
|
|
||||||
if (GithubSelfUpdater.needSelfUpdating()) {
|
if (GithubSelfUpdater.needSelfUpdating()) {
|
||||||
checkForUpdateItem = new TextItem(R.string.sk_check_for_update, GithubSelfUpdater.getInstance()::checkForUpdates);
|
checkForUpdateItem = new TextItem(R.string.sk_check_for_update, GithubSelfUpdater.getInstance()::checkForUpdates);
|
||||||
items.add(checkForUpdateItem);
|
items.add(checkForUpdateItem);
|
||||||
|
items.add(new SwitchItem(R.string.sk_updater_enable_pre_releases, 0, GlobalUserPreferences.enablePreReleases, i->{
|
||||||
|
GlobalUserPreferences.enablePreReleases=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
items.add(new TextItem(R.string.mo_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/LucasGGamerM/moshidon"), R.drawable.ic_fluent_open_24_regular));
|
items.add(new TextItem(R.string.mo_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/LucasGGamerM/moshidon"), R.drawable.ic_fluent_open_24_regular));
|
||||||
items.add(new TextItem(R.string.sk_settings_donate, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/sponsors/LucasGGamerM"), R.drawable.ic_fluent_heart_24_regular));
|
items.add(new TextItem(R.string.sk_settings_donate, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/sponsors/LucasGGamerM"), R.drawable.ic_fluent_heart_24_regular));
|
||||||
// items.add(new TextItem(R.string.settings_clear_cache, this::clearImageCache));
|
|
||||||
clearImageCacheItem = new TextItem(R.string.settings_clear_cache, UiUtils.formatFileSize(getContext(), imageCache.getDiskCache().size(), true), this::clearImageCache, 0);
|
LruCache<?, ?> cache = imageCache == null ? null : imageCache.getLruCache();
|
||||||
|
clearImageCacheItem = new TextItem(R.string.settings_clear_cache, UiUtils.formatFileSize(getContext(), cache != null ? cache.size() : 0, true), this::clearImageCache, 0);
|
||||||
items.add(clearImageCacheItem);
|
items.add(clearImageCacheItem);
|
||||||
items.add(new TextItem(R.string.sk_clear_recent_languages, ()->UiUtils.showConfirmationAlert(getActivity(), R.string.sk_clear_recent_languages, R.string.sk_confirm_clear_recent_languages, R.string.clear, ()->{
|
items.add(new TextItem(R.string.sk_clear_recent_languages, ()->UiUtils.showConfirmationAlert(getActivity(), R.string.sk_clear_recent_languages, R.string.sk_confirm_clear_recent_languages, R.string.clear, ()->{
|
||||||
GlobalUserPreferences.recentLanguages.remove(accountID);
|
GlobalUserPreferences.recentLanguages.remove(accountID);
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
})));
|
})));
|
||||||
|
|
||||||
items.add(new TextItem(R.string.mo_clear_recent_emoji, ()-> {
|
items.add(new TextItem(R.string.mo_clear_recent_emoji, ()-> {
|
||||||
GlobalUserPreferences.recentEmojis.clear();
|
GlobalUserPreferences.recentEmojis.clear();
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
|
@ -354,11 +413,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
if(needUpdateNotificationSettings && PushSubscriptionManager.arePushNotificationsAvailable()){
|
if(needUpdateNotificationSettings && PushSubscriptionManager.arePushNotificationsAvailable()){
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().updatePushSettings(pushSubscription);
|
AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().updatePushSettings(pushSubscription);
|
||||||
}
|
}
|
||||||
if(needAppRestart){
|
if(needAppRestart) UiUtils.restartApp();
|
||||||
Intent intent = Intent.makeRestartActivityTask(MastodonApp.context.getPackageManager().getLaunchIntentForPackage(MastodonApp.context.getPackageName()).getComponent());
|
|
||||||
MastodonApp.context.startActivity(intent);
|
|
||||||
Runtime.getRuntime().exit(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -462,8 +517,10 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
case FAVORITE -> subscription.alerts.favourite=enabled;
|
case FAVORITE -> subscription.alerts.favourite=enabled;
|
||||||
case FOLLOW -> subscription.alerts.follow=enabled;
|
case FOLLOW -> subscription.alerts.follow=enabled;
|
||||||
case REBLOG -> subscription.alerts.reblog=enabled;
|
case REBLOG -> subscription.alerts.reblog=enabled;
|
||||||
case MENTION -> subscription.alerts.mention=subscription.alerts.poll=enabled;
|
case MENTION -> subscription.alerts.mention=enabled;
|
||||||
|
case POLL -> subscription.alerts.poll=enabled;
|
||||||
case STATUS -> subscription.alerts.status=enabled;
|
case STATUS -> subscription.alerts.status=enabled;
|
||||||
|
case UPDATE -> subscription.alerts.update=enabled;
|
||||||
}
|
}
|
||||||
needUpdateNotificationSettings=true;
|
needUpdateNotificationSettings=true;
|
||||||
}
|
}
|
||||||
|
@ -488,9 +545,13 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
list.getAdapter().notifyItemChanged(index);
|
list.getAdapter().notifyItemChanged(index);
|
||||||
}
|
}
|
||||||
if((prevPolicy==PushSubscription.Policy.NONE)!=(policy==PushSubscription.Policy.NONE)){
|
if((prevPolicy==PushSubscription.Policy.NONE)!=(policy==PushSubscription.Policy.NONE)){
|
||||||
|
boolean newState=policy!=PushSubscription.Policy.NONE;
|
||||||
|
for(PushNotification.Type value : PushNotification.Type.values()){
|
||||||
|
onNotificationsChanged(value, newState);
|
||||||
|
}
|
||||||
index++;
|
index++;
|
||||||
while(items.get(index) instanceof SwitchItem si){
|
while(items.get(index) instanceof SwitchItem si){
|
||||||
si.enabled=si.checked=policy!=PushSubscription.Policy.NONE;
|
si.enabled=si.checked=newState;
|
||||||
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(index);
|
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(index);
|
||||||
if(holder!=null)
|
if(holder!=null)
|
||||||
((BindableViewHolder<?>)holder).rebind();
|
((BindableViewHolder<?>)holder).rebind();
|
||||||
|
@ -530,6 +591,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onLoggedOut(){
|
private void onLoggedOut(){
|
||||||
|
if (getActivity() == null) return;
|
||||||
AccountSessionManager.getInstance().removeAccount(accountID);
|
AccountSessionManager.getInstance().removeAccount(accountID);
|
||||||
getActivity().finish();
|
getActivity().finish();
|
||||||
Intent intent=new Intent(getActivity(), MainActivity.class);
|
Intent intent=new Intent(getActivity(), MainActivity.class);
|
||||||
|
@ -607,7 +669,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
this.onChanged=onChanged;
|
this.onChanged=onChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SwitchItem(@StringRes int text, int icon, boolean checked, Consumer<SwitchItem> onChanged, boolean enabled){
|
public SwitchItem(@StringRes int text, @DrawableRes int icon, boolean checked, Consumer<SwitchItem> onChanged, boolean enabled){
|
||||||
this.text=getString(text);
|
this.text=getString(text);
|
||||||
this.icon=icon;
|
this.icon=icon;
|
||||||
this.checked=checked;
|
this.checked=checked;
|
||||||
|
@ -806,7 +868,12 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
@Override
|
@Override
|
||||||
public void onBind(SwitchItem item){
|
public void onBind(SwitchItem item){
|
||||||
text.setText(item.text);
|
text.setText(item.text);
|
||||||
icon.setImageResource(item.icon);
|
if (item.icon == 0) {
|
||||||
|
icon.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
icon.setVisibility(View.VISIBLE);
|
||||||
|
icon.setImageResource(item.icon);
|
||||||
|
}
|
||||||
checkbox.setChecked(item.checked && item.enabled);
|
checkbox.setChecked(item.checked && item.enabled);
|
||||||
checkbox.setEnabled(item.enabled);
|
checkbox.setEnabled(item.enabled);
|
||||||
}
|
}
|
||||||
|
@ -981,21 +1048,19 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
|
|
||||||
private class SmallTextViewHolder extends BindableViewHolder<SmallTextItem> {
|
private class SmallTextViewHolder extends BindableViewHolder<SmallTextItem> {
|
||||||
private final TextView text;
|
private final TextView text;
|
||||||
;
|
|
||||||
|
|
||||||
public SmallTextViewHolder(){
|
public SmallTextViewHolder(){
|
||||||
super(getActivity(), R.layout.item_settings_text, list);
|
super(getActivity(), R.layout.item_settings_text, list);
|
||||||
text = itemView.findViewById(R.id.text);
|
text = itemView.findViewById(R.id.text);
|
||||||
|
text.setTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorSecondary));
|
||||||
|
text.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||||
|
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
|
||||||
|
text.setPaddingRelative(text.getPaddingStart(), 0, text.getPaddingEnd(), text.getPaddingBottom());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(SmallTextItem item){
|
public void onBind(SmallTextItem item){
|
||||||
text.setText(item.text);
|
text.setText(item.text);
|
||||||
TypedValue val = new TypedValue();
|
|
||||||
getContext().getTheme().resolveAttribute(android.R.attr.textColorSecondary, val, true);
|
|
||||||
text.setTextColor(getResources().getColor(val.resourceId, getContext().getTheme()));
|
|
||||||
text.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
|
|
||||||
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -173,16 +173,7 @@ public class SplashFragment extends AppKitFragment{
|
||||||
TextView title=new TextView(getActivity());
|
TextView title=new TextView(getActivity());
|
||||||
title.setTextAppearance(R.style.m3_headline_medium);
|
title.setTextAppearance(R.style.m3_headline_medium);
|
||||||
title.setText(switch(page){
|
title.setText(switch(page){
|
||||||
case 0 -> {
|
case 0 -> getString(R.string.welcome_page1_title);
|
||||||
String src=getString(R.string.welcome_page1_title);
|
|
||||||
SpannableString ss=new SpannableString(src);
|
|
||||||
int start=src.indexOf("{logo}");
|
|
||||||
if(start!=-1){
|
|
||||||
LogoSpan span=new LogoSpan(getResources().getDrawable(R.drawable.splash_logo, getActivity().getTheme()));
|
|
||||||
ss.setSpan(span, start, start+6, 0);
|
|
||||||
}
|
|
||||||
yield ss;
|
|
||||||
}
|
|
||||||
case 1 -> getString(R.string.welcome_page2_title);
|
case 1 -> getString(R.string.welcome_page2_title);
|
||||||
case 2 -> getString(R.string.welcome_page3_title);
|
case 2 -> getString(R.string.welcome_page3_title);
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+page);
|
default -> throw new IllegalStateException("Unexpected value: "+page);
|
||||||
|
@ -204,26 +195,4 @@ public class SplashFragment extends AppKitFragment{
|
||||||
ll.addView(text, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
ll.addView(text, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class LogoSpan extends ReplacementSpan{
|
|
||||||
private final Drawable drawable;
|
|
||||||
|
|
||||||
private LogoSpan(Drawable drawable){
|
|
||||||
this.drawable=drawable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm){
|
|
||||||
return drawable.getIntrinsicWidth();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint){
|
|
||||||
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
|
|
||||||
canvas.save();
|
|
||||||
canvas.translate(x, y-V.dp(20));
|
|
||||||
drawable.draw(canvas);
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ public class StatusEditHistoryFragment extends StatusListFragment{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
Collections.sort(result, Comparator.comparing((Status s)->s.createdAt).reversed());
|
Collections.sort(result, Comparator.comparing((Status s)->s.createdAt).reversed());
|
||||||
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result, false);
|
onDataLoaded(result, false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -139,7 +140,8 @@ public class StatusEditHistoryFragment extends StatusListFragment{
|
||||||
action=getString(R.string.edit_multiple_changed);
|
action=getString(R.string.edit_multiple_changed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
items.add(0, new ReblogOrReplyLineStatusDisplayItem(s.id, this, action+" · "+date, Collections.emptyList(), 0, null, null));
|
String sep = getString(R.string.sk_separator);
|
||||||
|
items.add(0, new ReblogOrReplyLineStatusDisplayItem(s.id, this, action+" "+sep+" "+date, Collections.emptyList(), 0, null, null));
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,8 +61,7 @@ public class ThreadFragment extends StatusListFragment{
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(StatusContext result){
|
public void onSuccess(StatusContext result){
|
||||||
if(getActivity()==null)
|
if (getActivity() == null) return;
|
||||||
return;
|
|
||||||
if(refreshing){
|
if(refreshing){
|
||||||
data.clear();
|
data.clear();
|
||||||
displayItems.clear();
|
displayItems.clear();
|
||||||
|
@ -126,4 +125,14 @@ public class ThreadFragment extends StatusListFragment{
|
||||||
public boolean isItemEnabled(String id){
|
public boolean isItemEnabled(String id){
|
||||||
return !id.equals(mainStatus.id);
|
return !id.equals(mainStatus.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean wantsLightStatusBar(){
|
||||||
|
return !UiUtils.isDarkTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean wantsLightNavigationBar(){
|
||||||
|
return !UiUtils.isDarkTheme();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,6 +101,7 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
|
||||||
for(Relationship rel:result){
|
for(Relationship rel:result){
|
||||||
relationships.put(rel.id, rel);
|
relationships.put(rel.id, rel);
|
||||||
}
|
}
|
||||||
|
if (getActivity() == null) return;
|
||||||
if(list==null)
|
if(list==null)
|
||||||
return;
|
return;
|
||||||
for(int i=0;i<list.getChildCount();i++){
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
|
@ -370,6 +371,7 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Relationship result){
|
public void onSuccess(Relationship result){
|
||||||
relationships.put(AccountViewHolder.this.item.account.id, result);
|
relationships.put(AccountViewHolder.this.item.account.id, result);
|
||||||
|
if (getActivity() == null) return;
|
||||||
bindRelationship();
|
bindRelationship();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ public abstract class PaginatedAccountListFragment extends BaseAccountListFragme
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
nextMaxID=null;
|
nextMaxID=null;
|
||||||
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result.stream().map(AccountItem::new).collect(Collectors.toList()), nextMaxID!=null);
|
onDataLoaded(result.stream().map(AccountItem::new).collect(Collectors.toList()), nextMaxID!=null);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -73,6 +73,7 @@ public class DiscoverAccountsFragment extends BaseRecyclerFragment<DiscoverAccou
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<FollowSuggestion> result){
|
public void onSuccess(List<FollowSuggestion> result){
|
||||||
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result.stream().map(fs->new AccountWrapper(fs.account)).collect(Collectors.toList()), false);
|
onDataLoaded(result.stream().map(fs->new AccountWrapper(fs.account)).collect(Collectors.toList()), false);
|
||||||
loadRelationships();
|
loadRelationships();
|
||||||
}
|
}
|
||||||
|
@ -107,6 +108,7 @@ public class DiscoverAccountsFragment extends BaseRecyclerFragment<DiscoverAccou
|
||||||
public void onSuccess(List<Relationship> result){
|
public void onSuccess(List<Relationship> result){
|
||||||
relationshipsRequest=null;
|
relationshipsRequest=null;
|
||||||
relationships=result.stream().collect(Collectors.toMap(rel->rel.id, Function.identity()));
|
relationships=result.stream().collect(Collectors.toMap(rel->rel.id, Function.identity()));
|
||||||
|
if (getActivity() == null) return;
|
||||||
if(list==null)
|
if(list==null)
|
||||||
return;
|
return;
|
||||||
for(int i=0;i<list.getChildCount();i++){
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
|
|
|
@ -62,7 +62,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private Runnable searchDebouncer=this::onSearchChangedDebounced;
|
private Runnable searchDebouncer=this::onSearchChangedDebounced;
|
||||||
|
|
||||||
private final boolean noFederated = !GlobalUserPreferences.showFederatedTimeline;
|
// private final boolean noFederated = !GlobalUserPreferences.showFederatedTimeline;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
@ -324,7 +324,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||||
}
|
}
|
||||||
|
|
||||||
private Fragment getFragmentForPage(int page){
|
private Fragment getFragmentForPage(int page){
|
||||||
if (noFederated && page > 0) page++;
|
// if (noFederated && page > 0) page++;
|
||||||
|
|
||||||
// return switch(page){
|
// return switch(page){
|
||||||
// case 0 -> localTimelineFragment;
|
// case 0 -> localTimelineFragment;
|
||||||
|
|
|
@ -58,6 +58,7 @@ public class DiscoverNewsFragment extends BaseRecyclerFragment<Card> implements
|
||||||
imageRequests=result.stream()
|
imageRequests=result.stream()
|
||||||
.map(card->TextUtils.isEmpty(card.image) ? null : new UrlImageLoaderRequest(card.image, V.dp(150), V.dp(150)))
|
.map(card->TextUtils.isEmpty(card.image) ? null : new UrlImageLoaderRequest(card.image, V.dp(150), V.dp(150)))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result, false);
|
onDataLoaded(result, false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -21,6 +21,7 @@ public class DiscoverPostsFragment extends StatusListFragment{
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result, !result.isEmpty());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
|
|
|
@ -29,6 +29,7 @@ public class FederatedTimelineFragment extends FabStatusListFragment {
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(!result.isEmpty())
|
if(!result.isEmpty())
|
||||||
maxID=result.get(result.size()-1).id;
|
maxID=result.get(result.size()-1).id;
|
||||||
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList()), !result.isEmpty());
|
onDataLoaded(result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList()), !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -29,6 +29,7 @@ public class LocalTimelineFragment extends FabStatusListFragment {
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(!result.isEmpty())
|
if(!result.isEmpty())
|
||||||
maxID=result.get(result.size()-1).id;
|
maxID=result.get(result.size()-1).id;
|
||||||
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList()), !result.isEmpty());
|
onDataLoaded(result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList()), !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -62,6 +62,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
|
||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
loadData();
|
loadData();
|
||||||
|
resetEmptyText();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -70,6 +71,10 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||||
imm=activity.getSystemService(InputMethodManager.class);
|
imm=activity.getSystemService(InputMethodManager.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void resetEmptyText() {
|
||||||
|
setEmptyText(R.string.sk_recent_searches_placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<StatusDisplayItem> buildDisplayItems(SearchResult s){
|
protected List<StatusDisplayItem> buildDisplayItems(SearchResult s){
|
||||||
return switch(s.type){
|
return switch(s.type){
|
||||||
|
@ -119,6 +124,8 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
|
if (getActivity() == null) return;
|
||||||
|
resetEmptyText();
|
||||||
if(isInRecentMode()){
|
if(isInRecentMode()){
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().getRecentSearches(sr->{
|
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().getRecentSearches(sr->{
|
||||||
if(getActivity()==null)
|
if(getActivity()==null)
|
||||||
|
@ -128,11 +135,13 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||||
onDataLoaded(sr, false);
|
onDataLoaded(sr, false);
|
||||||
});
|
});
|
||||||
}else{
|
}else{
|
||||||
|
setEmptyText(R.string.sk_searching);
|
||||||
progressVisibilityListener.onProgressVisibilityChanged(true);
|
progressVisibilityListener.onProgressVisibilityChanged(true);
|
||||||
currentRequest=new GetSearchResults(currentQuery, null, true)
|
currentRequest=new GetSearchResults(currentQuery, null, true)
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(SearchResults result){
|
public void onSuccess(SearchResults result){
|
||||||
|
setEmptyText(R.string.sk_no_results);
|
||||||
ArrayList<SearchResult> results=new ArrayList<>();
|
ArrayList<SearchResult> results=new ArrayList<>();
|
||||||
if(result.accounts!=null){
|
if(result.accounts!=null){
|
||||||
for(Account acc:result.accounts)
|
for(Account acc:result.accounts)
|
||||||
|
@ -148,11 +157,13 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||||
}
|
}
|
||||||
prevDisplayItems=new ArrayList<>(displayItems);
|
prevDisplayItems=new ArrayList<>(displayItems);
|
||||||
unfilteredResults=results;
|
unfilteredResults=results;
|
||||||
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(filterSearchResults(results), false);
|
onDataLoaded(filterSearchResults(results), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error){
|
public void onError(ErrorResponse error){
|
||||||
|
resetEmptyText();
|
||||||
currentRequest=null;
|
currentRequest=null;
|
||||||
Activity a=getActivity();
|
Activity a=getActivity();
|
||||||
if(a==null)
|
if(a==null)
|
||||||
|
|
|
@ -43,6 +43,7 @@ public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag> impl
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Hashtag> result){
|
public void onSuccess(List<Hashtag> result){
|
||||||
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result, false);
|
onDataLoaded(result, false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.graphics.Paint;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -19,6 +20,7 @@ import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||||
import org.joinmastodon.android.ui.OutlineProviders;
|
import org.joinmastodon.android.ui.OutlineProviders;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.joinmastodon.android.utils.ElevationOnScrollListener;
|
||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
@ -42,6 +44,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||||
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
import okhttp3.Call;
|
import okhttp3.Call;
|
||||||
import okhttp3.Callback;
|
import okhttp3.Callback;
|
||||||
|
@ -58,6 +61,7 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
|
||||||
private ArrayList<Item> items=new ArrayList<>();
|
private ArrayList<Item> items=new ArrayList<>();
|
||||||
private Call currentRequest;
|
private Call currentRequest;
|
||||||
private ItemsAdapter itemsAdapter;
|
private ItemsAdapter itemsAdapter;
|
||||||
|
private ElevationOnScrollListener onScrollListener;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
@ -72,7 +76,7 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
|
||||||
setNavigationBarColor(UiUtils.getThemeColor(activity, R.attr.colorWindowBackground));
|
setNavigationBarColor(UiUtils.getThemeColor(activity, R.attr.colorWindowBackground));
|
||||||
instance=Parcels.unwrap(getArguments().getParcelable("instance"));
|
instance=Parcels.unwrap(getArguments().getParcelable("instance"));
|
||||||
|
|
||||||
items.add(new Item("Mastodon for Android Privacy Policy", "joinmastodon.org", "https://joinmastodon.org/android/privacy", "https://joinmastodon.org/favicon-32x32.png"));
|
items.add(new Item("Mastodon for Android Privacy Policy", getString(R.string.privacy_policy_explanation), "joinmastodon.org", "https://joinmastodon.org/android/privacy", "https://joinmastodon.org/favicon-32x32.png"));
|
||||||
loadServerPrivacyPolicy();
|
loadServerPrivacyPolicy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,18 +97,24 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
|
||||||
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||||
View headerView=inflater.inflate(R.layout.item_list_header_simple, list, false);
|
View headerView=inflater.inflate(R.layout.item_list_header_simple, list, false);
|
||||||
TextView text=headerView.findViewById(R.id.text);
|
TextView text=headerView.findViewById(R.id.text);
|
||||||
text.setText(R.string.privacy_policy_subtitle);
|
text.setText(getString(R.string.privacy_policy_subtitle, instance.uri));
|
||||||
|
|
||||||
adapter=new MergeRecyclerAdapter();
|
adapter=new MergeRecyclerAdapter();
|
||||||
adapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
|
adapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
|
||||||
adapter.addAdapter(itemsAdapter=new ItemsAdapter());
|
adapter.addAdapter(itemsAdapter=new ItemsAdapter());
|
||||||
list.setAdapter(adapter);
|
list.setAdapter(adapter);
|
||||||
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorM3SurfaceVariant, 1, 56, 0, DividerItemDecoration.NOT_FIRST));
|
|
||||||
|
|
||||||
btn=view.findViewById(R.id.btn_next);
|
btn=view.findViewById(R.id.btn_next);
|
||||||
btn.setOnClickListener(v->onButtonClick());
|
btn.setOnClickListener(v->onButtonClick());
|
||||||
buttonBar=view.findViewById(R.id.button_bar);
|
buttonBar=view.findViewById(R.id.button_bar);
|
||||||
|
|
||||||
|
Button backBtn=view.findViewById(R.id.btn_back);
|
||||||
|
backBtn.setText(getString(R.string.server_policy_disagree, instance.uri));
|
||||||
|
backBtn.setOnClickListener(v->{
|
||||||
|
setResult(false, null);
|
||||||
|
Nav.finish(this);
|
||||||
|
});
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,13 +123,17 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
|
list.addOnScrollListener(onScrollListener=new ElevationOnScrollListener((FragmentRootLinearLayout) view, buttonBar, getToolbar()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Override
|
@Override
|
||||||
protected void onUpdateToolbar(){
|
protected void onUpdateToolbar(){
|
||||||
// super.onUpdateToolbar();
|
super.onUpdateToolbar();
|
||||||
getToolbar().setBackground(null);
|
getToolbar().setBackgroundResource(R.drawable.bg_onboarding_panel);
|
||||||
getToolbar().setElevation(0);
|
getToolbar().setElevation(0);
|
||||||
|
if(onScrollListener!=null){
|
||||||
|
onScrollListener.setViews(buttonBar, getToolbar());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onButtonClick(){
|
protected void onButtonClick(){
|
||||||
|
@ -158,7 +172,7 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
|
||||||
if(!response.isSuccessful())
|
if(!response.isSuccessful())
|
||||||
return;
|
return;
|
||||||
Document doc=Jsoup.parse(Objects.requireNonNull(body).byteStream(), Objects.requireNonNull(body.contentType()).charset(StandardCharsets.UTF_8).name(), req.url().toString());
|
Document doc=Jsoup.parse(Objects.requireNonNull(body).byteStream(), Objects.requireNonNull(body.contentType()).charset(StandardCharsets.UTF_8).name(), req.url().toString());
|
||||||
final Item item=new Item(doc.title(), instance.uri, req.url().toString(), "https://"+instance.uri+"/favicon.ico");
|
final Item item=new Item(doc.title(), null, instance.uri, req.url().toString(), "https://"+instance.uri+"/favicon.ico");
|
||||||
Activity activity=getActivity();
|
Activity activity=getActivity();
|
||||||
if(activity!=null){
|
if(activity!=null){
|
||||||
activity.runOnUiThread(()->{
|
activity.runOnUiThread(()->{
|
||||||
|
@ -192,16 +206,23 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
|
||||||
|
|
||||||
private class ItemViewHolder extends BindableViewHolder<Item> implements UsableRecyclerView.Clickable{
|
private class ItemViewHolder extends BindableViewHolder<Item> implements UsableRecyclerView.Clickable{
|
||||||
private final TextView title;
|
private final TextView title;
|
||||||
|
private final TextView subtitle;
|
||||||
|
|
||||||
public ItemViewHolder(){
|
public ItemViewHolder(){
|
||||||
super(getActivity(), R.layout.item_privacy_policy_link, list);
|
super(getActivity(), R.layout.item_privacy_policy_link, list);
|
||||||
title=findViewById(R.id.title);
|
title=findViewById(R.id.title);
|
||||||
title.setPaintFlags(title.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
subtitle=findViewById(R.id.subtitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(Item item){
|
public void onBind(Item item){
|
||||||
title.setText(item.title);
|
title.setText(item.title);
|
||||||
|
if(TextUtils.isEmpty(item.subtitle)){
|
||||||
|
subtitle.setVisibility(View.GONE);
|
||||||
|
}else{
|
||||||
|
subtitle.setVisibility(View.VISIBLE);
|
||||||
|
subtitle.setText(item.subtitle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -211,10 +232,11 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Item{
|
private static class Item{
|
||||||
public String title, domain, url, faviconUrl;
|
public String title, subtitle, domain, url, faviconUrl;
|
||||||
|
|
||||||
public Item(String title, String domain, String url, String faviconUrl){
|
public Item(String title, String subtitle, String domain, String url, String faviconUrl){
|
||||||
this.title=title;
|
this.title=title;
|
||||||
|
this.subtitle=subtitle;
|
||||||
this.domain=domain;
|
this.domain=domain;
|
||||||
this.url=url;
|
this.url=url;
|
||||||
this.faviconUrl=faviconUrl;
|
this.faviconUrl=faviconUrl;
|
||||||
|
|
|
@ -1,14 +1,8 @@
|
||||||
package org.joinmastodon.android.fragments.onboarding;
|
package org.joinmastodon.android.fragments.onboarding;
|
||||||
|
|
||||||
import android.animation.Animator;
|
|
||||||
import android.animation.AnimatorListenerAdapter;
|
|
||||||
import android.animation.AnimatorSet;
|
|
||||||
import android.animation.ObjectAnimator;
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.graphics.drawable.LayerDrawable;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -20,7 +14,6 @@ import android.view.WindowInsets;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.HorizontalScrollView;
|
import android.widget.HorizontalScrollView;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.PopupMenu;
|
import android.widget.PopupMenu;
|
||||||
import android.widget.RadioButton;
|
import android.widget.RadioButton;
|
||||||
|
@ -38,6 +31,7 @@ import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.FilterChipView;
|
import org.joinmastodon.android.ui.views.FilterChipView;
|
||||||
|
import org.joinmastodon.android.utils.ElevationOnScrollListener;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -56,11 +50,7 @@ import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
|
||||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
|
||||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||||
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
@ -215,47 +205,7 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||||
setStatusBarColor(0);
|
setStatusBarColor(0);
|
||||||
topBar=view.findViewById(R.id.top_bar);
|
topBar=view.findViewById(R.id.top_bar);
|
||||||
|
|
||||||
LayerDrawable topBg=(LayerDrawable) topBar.getBackground().mutate();
|
list.addOnScrollListener(new ElevationOnScrollListener(null, topBar, buttonBar));
|
||||||
topBar.setBackground(topBg);
|
|
||||||
Drawable topOverlay=topBg.findDrawableByLayerId(R.id.color_overlay);
|
|
||||||
topOverlay.setAlpha(0);
|
|
||||||
|
|
||||||
LayerDrawable btmBg=(LayerDrawable) buttonBar.getBackground().mutate();
|
|
||||||
buttonBar.setBackground(btmBg);
|
|
||||||
Drawable btmOverlay=btmBg.findDrawableByLayerId(R.id.color_overlay);
|
|
||||||
btmOverlay.setAlpha(0);
|
|
||||||
|
|
||||||
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
|
|
||||||
private boolean isAtTop=true;
|
|
||||||
private Animator currentPanelsAnim;
|
|
||||||
@Override
|
|
||||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
|
|
||||||
boolean newAtTop=recyclerView.getChildCount()==0 || (recyclerView.getChildAdapterPosition(recyclerView.getChildAt(0))==0 && recyclerView.getChildAt(0).getTop()==recyclerView.getPaddingTop());
|
|
||||||
if(newAtTop!=isAtTop){
|
|
||||||
isAtTop=newAtTop;
|
|
||||||
if(currentPanelsAnim!=null)
|
|
||||||
currentPanelsAnim.cancel();
|
|
||||||
|
|
||||||
AnimatorSet set=new AnimatorSet();
|
|
||||||
set.playTogether(
|
|
||||||
ObjectAnimator.ofInt(topOverlay, "alpha", isAtTop ? 0 : 20),
|
|
||||||
ObjectAnimator.ofInt(btmOverlay, "alpha", isAtTop ? 0 : 20),
|
|
||||||
ObjectAnimator.ofFloat(topBar, View.TRANSLATION_Z, isAtTop ? 0 : V.dp(3)),
|
|
||||||
ObjectAnimator.ofFloat(buttonBar, View.TRANSLATION_Z, isAtTop ? 0 : V.dp(3))
|
|
||||||
);
|
|
||||||
set.setDuration(150);
|
|
||||||
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
|
||||||
set.addListener(new AnimatorListenerAdapter(){
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animator animation){
|
|
||||||
currentPanelsAnim=null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
set.start();
|
|
||||||
currentPanelsAnim=set;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
searchEdit=view.findViewById(R.id.search_edit);
|
searchEdit=view.findViewById(R.id.search_edit);
|
||||||
searchEdit.setOnEditorActionListener(this::onSearchEnterPressed);
|
searchEdit.setOnEditorActionListener(this::onSearchEnterPressed);
|
||||||
|
@ -366,6 +316,9 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
focusThing=view.findViewById(R.id.focus_thing);
|
focusThing=view.findViewById(R.id.focus_thing);
|
||||||
focusThing.requestFocus();
|
focusThing.requestFocus();
|
||||||
|
|
||||||
|
view.findViewById(R.id.btn_random_instance).setOnClickListener(this::onPickRandomInstanceClick);
|
||||||
|
nextButton.setEnabled(chosenInstance!=null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onRegionFilterClick(View v){
|
private void onRegionFilterClick(View v){
|
||||||
|
@ -396,22 +349,6 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||||
updateFilteredList();
|
updateFilteredList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onNextClick(View v){
|
|
||||||
if(chosenInstance==null){
|
|
||||||
String lang=Locale.getDefault().getLanguage();
|
|
||||||
List<CatalogInstance> instances=data.stream().filter(ci->!ci.approvalRequired && ("general".equals(ci.category) || (ci.categories!=null && ci.categories.contains("general"))) && (lang.equals(ci.language) || (ci.languages!=null && ci.languages.contains(lang)))).collect(Collectors.toList());
|
|
||||||
if(instances.isEmpty()){
|
|
||||||
instances=data.stream().filter(ci->!ci.approvalRequired && ("general".equals(ci.category) || (ci.categories!=null && ci.categories.contains("general")))).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
if(instances.isEmpty()){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
chosenInstance=instances.get(new Random().nextInt(instances.size()));
|
|
||||||
}
|
|
||||||
super.onNextClick(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void proceedWithAuthOrSignup(Instance instance){
|
protected void proceedWithAuthOrSignup(Instance instance){
|
||||||
getActivity().getSystemService(InputMethodManager.class).hideSoftInputFromWindow(contentView.getWindowToken(), 0);
|
getActivity().getSystemService(InputMethodManager.class).hideSoftInputFromWindow(contentView.getWindowToken(), 0);
|
||||||
|
@ -428,6 +365,19 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||||
Nav.go(getActivity(), InstanceRulesFragment.class, args);
|
Nav.go(getActivity(), InstanceRulesFragment.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onPickRandomInstanceClick(View v){
|
||||||
|
String lang=Locale.getDefault().getLanguage();
|
||||||
|
List<CatalogInstance> instances=data.stream().filter(ci->!ci.approvalRequired && ("general".equals(ci.category) || (ci.categories!=null && ci.categories.contains("general"))) && (lang.equals(ci.language) || (ci.languages!=null && ci.languages.contains(lang)))).collect(Collectors.toList());
|
||||||
|
if(instances.isEmpty()){
|
||||||
|
instances=data.stream().filter(ci->!ci.approvalRequired && ("general".equals(ci.category) || (ci.categories!=null && ci.categories.contains("general")))).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
if(instances.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chosenInstance=instances.get(new Random().nextInt(instances.size()));
|
||||||
|
onNextClick(v);
|
||||||
|
}
|
||||||
|
|
||||||
// private String getEmojiForCategory(String category){
|
// private String getEmojiForCategory(String category){
|
||||||
// return switch(category){
|
// return switch(category){
|
||||||
// case "all" -> "💬";
|
// case "all" -> "💬";
|
||||||
|
@ -577,7 +527,7 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||||
updateFilteredList();
|
updateFilteredList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceCatalogSignupFragment.InstanceViewHolder> implements ImageLoaderRecyclerAdapter{
|
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceCatalogSignupFragment.InstanceViewHolder>{
|
||||||
public InstancesAdapter(){
|
public InstancesAdapter(){
|
||||||
super(imgLoader);
|
super(imgLoader);
|
||||||
}
|
}
|
||||||
|
@ -603,22 +553,11 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||||
public int getItemViewType(int position){
|
public int getItemViewType(int position){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getImageCountForItem(int position){
|
|
||||||
return filteredData.get(position).thumbnailRequest!=null ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ImageLoaderRequest getImageRequest(int position, int image){
|
|
||||||
return filteredData.get(position).thumbnailRequest;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InstanceViewHolder extends BindableViewHolder<CatalogInstance> implements UsableRecyclerView.DisableableClickable, ImageLoaderViewHolder{
|
private class InstanceViewHolder extends BindableViewHolder<CatalogInstance> implements UsableRecyclerView.DisableableClickable{
|
||||||
private final TextView title, description;
|
private final TextView title, description;
|
||||||
private final RadioButton radioButton;
|
private final RadioButton radioButton;
|
||||||
private final ImageView thumbnail;
|
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
|
|
||||||
public InstanceViewHolder(){
|
public InstanceViewHolder(){
|
||||||
|
@ -626,15 +565,12 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||||
title=findViewById(R.id.title);
|
title=findViewById(R.id.title);
|
||||||
description=findViewById(R.id.description);
|
description=findViewById(R.id.description);
|
||||||
radioButton=findViewById(R.id.radiobtn);
|
radioButton=findViewById(R.id.radiobtn);
|
||||||
thumbnail=findViewById(R.id.image);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(CatalogInstance item){
|
public void onBind(CatalogInstance item){
|
||||||
title.setText(item.normalizedDomain);
|
title.setText(item.normalizedDomain);
|
||||||
radioButton.setChecked(chosenInstance==item);
|
radioButton.setChecked(chosenInstance==item);
|
||||||
if(item.thumbnailRequest==null)
|
|
||||||
thumbnail.setImageDrawable(null);
|
|
||||||
Instance realInstance=instancesCache.get(item.normalizedDomain);
|
Instance realInstance=instancesCache.get(item.normalizedDomain);
|
||||||
float alpha;
|
float alpha;
|
||||||
if(realInstance!=null && !realInstance.registrations){
|
if(realInstance!=null && !realInstance.registrations){
|
||||||
|
@ -649,7 +585,6 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||||
title.setAlpha(alpha);
|
title.setAlpha(alpha);
|
||||||
description.setAlpha(alpha);
|
description.setAlpha(alpha);
|
||||||
radioButton.setAlpha(alpha);
|
radioButton.setAlpha(alpha);
|
||||||
thumbnail.setAlpha(alpha);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -672,6 +607,9 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||||
adapter.notifyItemChanged(idx);
|
adapter.notifyItemChanged(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(!nextButton.isEnabled()){
|
||||||
|
nextButton.setEnabled(true);
|
||||||
|
}
|
||||||
radioButton.setChecked(true);
|
radioButton.setChecked(true);
|
||||||
if(chosenInstance==null)
|
if(chosenInstance==null)
|
||||||
nextButton.setEnabled(true);
|
nextButton.setEnabled(true);
|
||||||
|
@ -679,16 +617,6 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||||
loadInstanceInfo(chosenInstance.domain, false);
|
loadInstanceInfo(chosenInstance.domain, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setImage(int index, Drawable image){
|
|
||||||
thumbnail.setImageDrawable(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearImage(int index){
|
|
||||||
setImage(index, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled(){
|
public boolean isEnabled(){
|
||||||
return enabled;
|
return enabled;
|
||||||
|
@ -710,4 +638,5 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||||
return (this==GENERAL)==isGeneral;
|
return (this==GENERAL)==isGeneral;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package org.joinmastodon.android.fragments.onboarding;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
@ -18,6 +17,7 @@ import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.joinmastodon.android.utils.ElevationOnScrollListener;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
@ -29,6 +29,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||||
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class InstanceRulesFragment extends ToolbarFragment{
|
public class InstanceRulesFragment extends ToolbarFragment{
|
||||||
|
@ -37,6 +38,9 @@ public class InstanceRulesFragment extends ToolbarFragment{
|
||||||
private Button btn;
|
private Button btn;
|
||||||
private View buttonBar;
|
private View buttonBar;
|
||||||
private Instance instance;
|
private Instance instance;
|
||||||
|
private ElevationOnScrollListener onScrollListener;
|
||||||
|
|
||||||
|
private static final int RULES_REQUEST=376;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
@ -66,33 +70,47 @@ public class InstanceRulesFragment extends ToolbarFragment{
|
||||||
adapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
|
adapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
|
||||||
adapter.addAdapter(new ItemsAdapter());
|
adapter.addAdapter(new ItemsAdapter());
|
||||||
list.setAdapter(adapter);
|
list.setAdapter(adapter);
|
||||||
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 1, 56, 0, DividerItemDecoration.NOT_FIRST));
|
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorM3SurfaceVariant, 1, 56, 0, DividerItemDecoration.NOT_FIRST));
|
||||||
|
|
||||||
btn=view.findViewById(R.id.btn_next);
|
btn=view.findViewById(R.id.btn_next);
|
||||||
btn.setOnClickListener(v->onButtonClick());
|
btn.setOnClickListener(v->onButtonClick());
|
||||||
buttonBar=view.findViewById(R.id.button_bar);
|
buttonBar=view.findViewById(R.id.button_bar);
|
||||||
|
|
||||||
|
view.findViewById(R.id.btn_back).setOnClickListener(v->Nav.finish(this));
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
// setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
// view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
|
list.addOnScrollListener(onScrollListener=new ElevationOnScrollListener((FragmentRootLinearLayout) view, buttonBar, getToolbar()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Override
|
@Override
|
||||||
protected void onUpdateToolbar(){
|
protected void onUpdateToolbar(){
|
||||||
// super.onUpdateToolbar();
|
super.onUpdateToolbar();
|
||||||
getToolbar().setBackground(null);
|
getToolbar().setBackgroundResource(R.drawable.bg_onboarding_panel);
|
||||||
getToolbar().setElevation(0);
|
getToolbar().setElevation(0);
|
||||||
|
if(onScrollListener!=null){
|
||||||
|
onScrollListener.setViews(buttonBar, getToolbar());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onButtonClick(){
|
protected void onButtonClick(){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putParcelable("instance", Parcels.wrap(instance));
|
args.putParcelable("instance", Parcels.wrap(instance));
|
||||||
Nav.go(getActivity(), GoogleMadeMeAddThisFragment.class, args);
|
Nav.goForResult(getActivity(), GoogleMadeMeAddThisFragment.class, args, RULES_REQUEST, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFragmentResult(int reqCode, boolean success, Bundle result){
|
||||||
|
super.onFragmentResult(reqCode, success, result);
|
||||||
|
if(reqCode==RULES_REQUEST && !success){
|
||||||
|
Nav.finish(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -89,6 +89,7 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result, !result.isEmpty());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -42,17 +42,9 @@ public class Announcement extends BaseModel implements DisplayItemsParent {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Status toStatus() {
|
public Status toStatus() {
|
||||||
Status s = new Status();
|
Status s = Status.ofFake(id, content, publishedAt);
|
||||||
s.id = id;
|
|
||||||
s.mediaAttachments = List.of();
|
|
||||||
s.createdAt = startsAt != null ? startsAt : publishedAt;
|
s.createdAt = startsAt != null ? startsAt : publishedAt;
|
||||||
if (updatedAt != null) s.editedAt = updatedAt;
|
if (updatedAt != null) s.editedAt = updatedAt;
|
||||||
s.content = s.text = content;
|
|
||||||
s.spoilerText = "";
|
|
||||||
s.visibility = StatusPrivacy.PUBLIC;
|
|
||||||
s.mentions = List.of();
|
|
||||||
s.tags = List.of();
|
|
||||||
s.emojis = List.of();
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,26 +47,26 @@ public class Attachment extends BaseModel{
|
||||||
|
|
||||||
public int getWidth(){
|
public int getWidth(){
|
||||||
if(meta==null)
|
if(meta==null)
|
||||||
return 0;
|
return 1920;
|
||||||
if(meta.width>0)
|
if(meta.width>0)
|
||||||
return meta.width;
|
return meta.width;
|
||||||
if(meta.original!=null && meta.original.width>0)
|
if(meta.original!=null && meta.original.width>0)
|
||||||
return meta.original.width;
|
return meta.original.width;
|
||||||
if(meta.small!=null && meta.small.width>0)
|
if(meta.small!=null && meta.small.width>0)
|
||||||
return meta.small.width;
|
return meta.small.width;
|
||||||
return 0;
|
return 1920;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getHeight(){
|
public int getHeight(){
|
||||||
if(meta==null)
|
if(meta==null)
|
||||||
return 0;
|
return 1080;
|
||||||
if(meta.height>0)
|
if(meta.height>0)
|
||||||
return meta.height;
|
return meta.height;
|
||||||
if(meta.original!=null && meta.original.height>0)
|
if(meta.original!=null && meta.original.height>0)
|
||||||
return meta.original.height;
|
return meta.original.height;
|
||||||
if(meta.small!=null && meta.small.height>0)
|
if(meta.small!=null && meta.small.height>0)
|
||||||
return meta.small.height;
|
return meta.small.height;
|
||||||
return 0;
|
return 1080;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getDuration(){
|
public double getDuration(){
|
||||||
|
|
|
@ -84,6 +84,8 @@ public class Instance extends BaseModel{
|
||||||
|
|
||||||
public V2 v2;
|
public V2 v2;
|
||||||
|
|
||||||
|
public Pleroma pleroma;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postprocess() throws ObjectValidationException{
|
public void postprocess() throws ObjectValidationException{
|
||||||
super.postprocess();
|
super.postprocess();
|
||||||
|
@ -193,4 +195,9 @@ public class Instance extends BaseModel{
|
||||||
public boolean enabled;
|
public boolean enabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Parcel
|
||||||
|
public static class Pleroma extends BaseModel {
|
||||||
|
// metadata etc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package org.joinmastodon.android.model;
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
import org.joinmastodon.android.api.RequiredField;
|
import org.joinmastodon.android.api.RequiredField;
|
||||||
|
@ -11,9 +13,9 @@ public class ListTimeline extends BaseModel {
|
||||||
public String id;
|
public String id;
|
||||||
@RequiredField
|
@RequiredField
|
||||||
public String title;
|
public String title;
|
||||||
@RequiredField
|
|
||||||
public RepliesPolicy repliesPolicy;
|
public RepliesPolicy repliesPolicy;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "List{" +
|
return "List{" +
|
||||||
|
|
|
@ -18,8 +18,8 @@ public class Notification extends BaseModel implements DisplayItemsParent{
|
||||||
public Instant createdAt;
|
public Instant createdAt;
|
||||||
@RequiredField
|
@RequiredField
|
||||||
public Account account;
|
public Account account;
|
||||||
|
|
||||||
public Status status;
|
public Status status;
|
||||||
|
public Report report;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postprocess() throws ObjectValidationException{
|
public void postprocess() throws ObjectValidationException{
|
||||||
|
@ -48,6 +48,19 @@ public class Notification extends BaseModel implements DisplayItemsParent{
|
||||||
@SerializedName("poll")
|
@SerializedName("poll")
|
||||||
POLL,
|
POLL,
|
||||||
@SerializedName("status")
|
@SerializedName("status")
|
||||||
STATUS
|
STATUS,
|
||||||
|
@SerializedName("update")
|
||||||
|
UPDATE,
|
||||||
|
@SerializedName("admin.sign_up")
|
||||||
|
SIGN_UP,
|
||||||
|
@SerializedName("admin.report")
|
||||||
|
REPORT
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parcel
|
||||||
|
public static class Report {
|
||||||
|
public String id;
|
||||||
|
public String comment;
|
||||||
|
public Account targetAccount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,13 @@ public class PushNotification extends BaseModel{
|
||||||
@SerializedName("poll")
|
@SerializedName("poll")
|
||||||
POLL(R.string.notification_type_poll),
|
POLL(R.string.notification_type_poll),
|
||||||
@SerializedName("status")
|
@SerializedName("status")
|
||||||
STATUS(R.string.sk_notification_type_status);
|
STATUS(R.string.sk_notification_type_status),
|
||||||
|
@SerializedName("update")
|
||||||
|
UPDATE(R.string.sk_notification_type_update),
|
||||||
|
@SerializedName("admin.sign_up")
|
||||||
|
SIGN_UP(R.string.sk_sign_ups),
|
||||||
|
@SerializedName("admin.report")
|
||||||
|
REPORT(R.string.sk_new_reports);
|
||||||
|
|
||||||
@StringRes
|
@StringRes
|
||||||
public final int localizedName;
|
public final int localizedName;
|
||||||
|
|
|
@ -45,10 +45,19 @@ public class PushSubscription extends BaseModel implements Cloneable{
|
||||||
public boolean mention;
|
public boolean mention;
|
||||||
public boolean poll;
|
public boolean poll;
|
||||||
public boolean status;
|
public boolean status;
|
||||||
|
public boolean update;
|
||||||
|
|
||||||
|
// set to true here because i didn't add any items for those to the settings
|
||||||
|
// (so i don't have to determine whether the user is an admin to show the items or not, and
|
||||||
|
// admins can still disable those through the android notifications settings)
|
||||||
|
@SerializedName("admin.sign_up")
|
||||||
|
public boolean adminSignUp = true;
|
||||||
|
@SerializedName("admin.report")
|
||||||
|
public boolean adminReport = true;
|
||||||
|
|
||||||
public static Alerts ofAll(){
|
public static Alerts ofAll(){
|
||||||
Alerts alerts=new Alerts();
|
Alerts alerts=new Alerts();
|
||||||
alerts.follow=alerts.favourite=alerts.reblog=alerts.mention=alerts.poll=alerts.status=true;
|
alerts.follow=alerts.favourite=alerts.reblog=alerts.mention=alerts.poll=alerts.status=alerts.update=true;
|
||||||
return alerts;
|
return alerts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +70,9 @@ public class PushSubscription extends BaseModel implements Cloneable{
|
||||||
", mention="+mention+
|
", mention="+mention+
|
||||||
", poll="+poll+
|
", poll="+poll+
|
||||||
", status="+status+
|
", status="+status+
|
||||||
|
", update="+update+
|
||||||
|
", adminSignUp="+adminSignUp+
|
||||||
|
", adminReport="+adminReport+
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||||
public Card card;
|
public Card card;
|
||||||
public String language;
|
public String language;
|
||||||
public String text;
|
public String text;
|
||||||
|
public boolean localOnly;
|
||||||
|
|
||||||
public boolean favourited;
|
public boolean favourited;
|
||||||
public boolean reblogged;
|
public boolean reblogged;
|
||||||
|
@ -83,6 +84,7 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||||
reblog.postprocess();
|
reblog.postprocess();
|
||||||
|
|
||||||
spoilerRevealed=GlobalUserPreferences.alwaysExpandContentWarnings || !sensitive;
|
spoilerRevealed=GlobalUserPreferences.alwaysExpandContentWarnings || !sensitive;
|
||||||
|
if (visibility.equals(StatusPrivacy.LOCAL)) localOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -144,4 +146,18 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||||
strippedText=HtmlParser.strip(content);
|
strippedText=HtmlParser.strip(content);
|
||||||
return strippedText;
|
return strippedText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Status ofFake(String id, String text, Instant createdAt) {
|
||||||
|
Status s = new Status();
|
||||||
|
s.id = id;
|
||||||
|
s.mediaAttachments = List.of();
|
||||||
|
s.createdAt = createdAt;
|
||||||
|
s.content = s.text = text;
|
||||||
|
s.spoilerText = "";
|
||||||
|
s.visibility = StatusPrivacy.PUBLIC;
|
||||||
|
s.mentions = List.of();
|
||||||
|
s.tags = List.of();
|
||||||
|
s.emojis = List.of();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,9 @@ public enum StatusPrivacy{
|
||||||
@SerializedName("private")
|
@SerializedName("private")
|
||||||
PRIVATE(2),
|
PRIVATE(2),
|
||||||
@SerializedName("direct")
|
@SerializedName("direct")
|
||||||
DIRECT(3);
|
DIRECT(3),
|
||||||
|
@SerializedName("local")
|
||||||
|
LOCAL(4); // akkoma
|
||||||
|
|
||||||
private int privacy;
|
private int privacy;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,252 @@
|
||||||
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
|
import android.app.Fragment;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.BuildConfig;
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
||||||
|
import org.joinmastodon.android.fragments.HomeTimelineFragment;
|
||||||
|
import org.joinmastodon.android.fragments.ListTimelineFragment;
|
||||||
|
import org.joinmastodon.android.fragments.NotificationsListFragment;
|
||||||
|
import org.joinmastodon.android.fragments.discover.FederatedTimelineFragment;
|
||||||
|
import org.joinmastodon.android.fragments.discover.LocalTimelineFragment;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class TimelineDefinition {
|
||||||
|
private TimelineType type;
|
||||||
|
private String title;
|
||||||
|
private @Nullable Icon icon;
|
||||||
|
|
||||||
|
private @Nullable String listId;
|
||||||
|
private @Nullable String listTitle;
|
||||||
|
|
||||||
|
private @Nullable String hashtagName;
|
||||||
|
|
||||||
|
public static TimelineDefinition ofList(String listId, String listTitle) {
|
||||||
|
TimelineDefinition def = new TimelineDefinition(TimelineType.LIST);
|
||||||
|
def.listId = listId;
|
||||||
|
def.listTitle = listTitle;
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TimelineDefinition ofList(ListTimeline list) {
|
||||||
|
return ofList(list.id, list.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TimelineDefinition ofHashtag(String hashtag) {
|
||||||
|
TimelineDefinition def = new TimelineDefinition(TimelineType.HASHTAG);
|
||||||
|
def.hashtagName = hashtag;
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TimelineDefinition ofHashtag(Hashtag hashtag) {
|
||||||
|
return ofHashtag(hashtag.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public TimelineDefinition() {}
|
||||||
|
|
||||||
|
public TimelineDefinition(TimelineType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle(Context ctx) {
|
||||||
|
return title != null ? title : getDefaultTitle(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCustomTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title == null || title.isBlank() ? null : title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDefaultTitle(Context ctx) {
|
||||||
|
return switch (type) {
|
||||||
|
case HOME -> ctx.getString(R.string.sk_timeline_home);
|
||||||
|
case LOCAL -> ctx.getString(R.string.sk_timeline_local);
|
||||||
|
case FEDERATED -> ctx.getString(R.string.sk_timeline_federated);
|
||||||
|
case POST_NOTIFICATIONS -> ctx.getString(R.string.sk_timeline_posts);
|
||||||
|
case LIST -> listTitle;
|
||||||
|
case HASHTAG -> hashtagName;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Icon getDefaultIcon() {
|
||||||
|
return switch (type) {
|
||||||
|
case HOME -> Icon.HOME;
|
||||||
|
case LOCAL -> Icon.LOCAL;
|
||||||
|
case FEDERATED -> Icon.FEDERATED;
|
||||||
|
case POST_NOTIFICATIONS -> Icon.POST_NOTIFICATIONS;
|
||||||
|
case LIST -> Icon.LIST;
|
||||||
|
case HASHTAG -> Icon.HASHTAG;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Fragment getFragment() {
|
||||||
|
return switch (type) {
|
||||||
|
case HOME -> new HomeTimelineFragment();
|
||||||
|
case LOCAL -> new LocalTimelineFragment();
|
||||||
|
case FEDERATED -> new FederatedTimelineFragment();
|
||||||
|
case LIST -> new ListTimelineFragment();
|
||||||
|
case HASHTAG -> new HashtagTimelineFragment();
|
||||||
|
case POST_NOTIFICATIONS -> new NotificationsListFragment();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Icon getIcon() {
|
||||||
|
return icon == null ? getDefaultIcon() : icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIcon(@Nullable Icon icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimelineType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
TimelineDefinition that = (TimelineDefinition) o;
|
||||||
|
if (type != that.type) return false;
|
||||||
|
if (type == TimelineType.LIST) return Objects.equals(listId, that.listId);
|
||||||
|
if (type == TimelineType.HASHTAG) return Objects.equals(hashtagName.toLowerCase(), that.hashtagName.toLowerCase());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = type.ordinal();
|
||||||
|
result = 31 * result + (listId != null ? listId.hashCode() : 0);
|
||||||
|
result = 31 * result + (hashtagName.toLowerCase() != null ? hashtagName.toLowerCase().hashCode() : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimelineDefinition copy() {
|
||||||
|
TimelineDefinition def = new TimelineDefinition(type);
|
||||||
|
def.title = title;
|
||||||
|
def.listId = listId;
|
||||||
|
def.listTitle = listTitle;
|
||||||
|
def.hashtagName = hashtagName;
|
||||||
|
def.icon = icon == null ? null : Icon.values()[icon.ordinal()];
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle populateArguments(Bundle args) {
|
||||||
|
if (type == TimelineType.LIST) {
|
||||||
|
args.putString("listTitle", title);
|
||||||
|
args.putString("listID", listId);
|
||||||
|
} else if (type == TimelineType.HASHTAG) {
|
||||||
|
args.putString("hashtag", hashtagName);
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TimelineType { HOME, LOCAL, FEDERATED, POST_NOTIFICATIONS, LIST, HASHTAG }
|
||||||
|
|
||||||
|
public enum Icon {
|
||||||
|
HEART(R.drawable.ic_fluent_heart_24_regular, R.string.sk_icon_heart),
|
||||||
|
STAR(R.drawable.ic_fluent_star_24_regular, R.string.sk_icon_star),
|
||||||
|
PEOPLE(R.drawable.ic_fluent_people_24_regular, R.string.sk_icon_people),
|
||||||
|
CITY(R.drawable.ic_fluent_city_24_regular, R.string.sk_icon_city),
|
||||||
|
IMAGE(R.drawable.ic_fluent_image_24_regular, R.string.sk_icon_image),
|
||||||
|
NEWS(R.drawable.ic_fluent_news_24_regular, R.string.sk_icon_news),
|
||||||
|
COLOR_PALETTE(R.drawable.ic_fluent_color_24_regular, R.string.sk_icon_color_palette),
|
||||||
|
CAT(R.drawable.ic_fluent_animal_cat_24_regular, R.string.sk_icon_cat),
|
||||||
|
DOG(R.drawable.ic_fluent_animal_dog_24_regular, R.string.sk_icon_dog),
|
||||||
|
RABBIT(R.drawable.ic_fluent_animal_rabbit_24_regular, R.string.sk_icon_rabbit),
|
||||||
|
TURTLE(R.drawable.ic_fluent_animal_turtle_24_regular, R.string.sk_icon_turtle),
|
||||||
|
ACADEMIC_CAP(R.drawable.ic_fluent_hat_graduation_24_regular, R.string.sk_icon_academic_cap),
|
||||||
|
BOT(R.drawable.ic_fluent_bot_24_regular, R.string.sk_icon_bot),
|
||||||
|
IMPORTANT(R.drawable.ic_fluent_important_24_regular, R.string.sk_icon_important),
|
||||||
|
PIN(R.drawable.ic_fluent_pin_24_regular, R.string.sk_icon_pin),
|
||||||
|
SHIELD(R.drawable.ic_fluent_shield_24_regular, R.string.sk_icon_shield),
|
||||||
|
CHAT(R.drawable.ic_fluent_chat_multiple_24_regular, R.string.sk_icon_chat),
|
||||||
|
TAG(R.drawable.ic_fluent_tag_24_regular, R.string.sk_icon_tag),
|
||||||
|
TRAIN(R.drawable.ic_fluent_vehicle_subway_24_regular, R.string.sk_icon_train),
|
||||||
|
BICYCLE(R.drawable.ic_fluent_vehicle_bicycle_24_regular, R.string.sk_icon_bicycle),
|
||||||
|
MAP(R.drawable.ic_fluent_map_24_regular, R.string.sk_icon_map),
|
||||||
|
BACKPACK(R.drawable.ic_fluent_backpack_24_regular, R.string.sk_icon_backpack),
|
||||||
|
BRIEFCASE(R.drawable.ic_fluent_briefcase_24_regular, R.string.sk_icon_briefcase),
|
||||||
|
BOOK(R.drawable.ic_fluent_book_open_24_regular, R.string.sk_icon_book),
|
||||||
|
LANGUAGE(R.drawable.ic_fluent_local_language_24_regular, R.string.sk_icon_language),
|
||||||
|
WEATHER(R.drawable.ic_fluent_weather_rain_showers_day_24_regular, R.string.sk_icon_weather),
|
||||||
|
APERTURE(R.drawable.ic_fluent_scan_24_regular, R.string.sk_icon_aperture),
|
||||||
|
MUSIC(R.drawable.ic_fluent_music_note_2_24_regular, R.string.sk_icon_music),
|
||||||
|
LOCATION(R.drawable.ic_fluent_location_24_regular, R.string.sk_icon_location),
|
||||||
|
GLOBE(R.drawable.ic_fluent_globe_24_regular, R.string.sk_icon_globe),
|
||||||
|
MEGAPHONE(R.drawable.ic_fluent_megaphone_loud_24_regular, R.string.sk_icon_megaphone),
|
||||||
|
MICROPHONE(R.drawable.ic_fluent_mic_24_regular, R.string.sk_icon_microphone),
|
||||||
|
MICROSCOPE(R.drawable.ic_fluent_microscope_24_regular, R.string.sk_icon_microscope),
|
||||||
|
STETHOSCOPE(R.drawable.ic_fluent_stethoscope_24_regular, R.string.sk_icon_stethoscope),
|
||||||
|
KEYBOARD(R.drawable.ic_fluent_midi_24_regular, R.string.sk_icon_keyboard),
|
||||||
|
COFFEE(R.drawable.ic_fluent_drink_coffee_24_regular, R.string.sk_icon_coffee),
|
||||||
|
CLAPPER_BOARD(R.drawable.ic_fluent_movies_and_tv_24_regular, R.string.sk_icon_clapper_board),
|
||||||
|
LAUGH(R.drawable.ic_fluent_emoji_laugh_24_regular, R.string.sk_icon_laugh),
|
||||||
|
BALLOON(R.drawable.ic_fluent_balloon_24_regular, R.string.sk_icon_balloon),
|
||||||
|
PI(R.drawable.ic_fluent_pi_24_regular, R.string.sk_icon_pi),
|
||||||
|
MATH_FORMULA(R.drawable.ic_fluent_math_formula_24_regular, R.string.sk_icon_math_formula),
|
||||||
|
GAMES(R.drawable.ic_fluent_games_24_regular, R.string.sk_icon_games),
|
||||||
|
CODE(R.drawable.ic_fluent_code_24_regular, R.string.sk_icon_code),
|
||||||
|
BUG(R.drawable.ic_fluent_bug_24_regular, R.string.sk_icon_bug),
|
||||||
|
LIGHT_BULB(R.drawable.ic_fluent_lightbulb_24_regular, R.string.sk_icon_light_bulb),
|
||||||
|
FIRE(R.drawable.ic_fluent_fire_24_regular, R.string.sk_icon_fire),
|
||||||
|
LEAVES(R.drawable.ic_fluent_leaf_three_24_regular, R.string.sk_icon_leaves),
|
||||||
|
SPORT(R.drawable.ic_fluent_sport_24_regular, R.string.sk_icon_sport),
|
||||||
|
HEALTH(R.drawable.ic_fluent_heart_pulse_24_regular, R.string.sk_icon_health),
|
||||||
|
PIZZA(R.drawable.ic_fluent_food_pizza_24_regular, R.string.sk_icon_pizza),
|
||||||
|
GAVEL(R.drawable.ic_fluent_gavel_24_regular, R.string.sk_icon_gavel),
|
||||||
|
GAUGE(R.drawable.ic_fluent_gauge_24_regular, R.string.sk_icon_gauge),
|
||||||
|
HEADPHONES(R.drawable.ic_fluent_headphones_sound_wave_24_regular, R.string.sk_icon_headphones),
|
||||||
|
HUMAN(R.drawable.ic_fluent_accessibility_24_regular, R.string.sk_icon_human),
|
||||||
|
|
||||||
|
HOME(R.drawable.ic_fluent_home_24_regular, R.string.sk_timeline_home, true),
|
||||||
|
LOCAL(R.drawable.ic_fluent_people_community_24_regular, R.string.sk_timeline_local, true),
|
||||||
|
FEDERATED(R.drawable.ic_fluent_earth_24_regular, R.string.sk_timeline_federated, true),
|
||||||
|
POST_NOTIFICATIONS(R.drawable.ic_fluent_chat_24_regular, R.string.sk_timeline_posts, true),
|
||||||
|
LIST(R.drawable.ic_fluent_people_24_regular, R.string.sk_list, true),
|
||||||
|
HASHTAG(R.drawable.ic_fluent_number_symbol_24_regular, R.string.sk_hashtag, true);
|
||||||
|
|
||||||
|
public final int iconRes, nameRes;
|
||||||
|
public final boolean hidden;
|
||||||
|
|
||||||
|
Icon(@DrawableRes int iconRes, @StringRes int nameRes) {
|
||||||
|
this(iconRes, nameRes, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Icon(@DrawableRes int iconRes, @StringRes int nameRes, boolean hidden) {
|
||||||
|
this.iconRes = iconRes;
|
||||||
|
this.nameRes = nameRes;
|
||||||
|
this.hidden = hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final TimelineDefinition HOME_TIMELINE = new TimelineDefinition(TimelineType.HOME);
|
||||||
|
public static final TimelineDefinition LOCAL_TIMELINE = new TimelineDefinition(TimelineType.LOCAL);
|
||||||
|
public static final TimelineDefinition FEDERATED_TIMELINE = new TimelineDefinition(TimelineType.FEDERATED);
|
||||||
|
public static final TimelineDefinition POSTS_TIMELINE = new TimelineDefinition(TimelineType.POST_NOTIFICATIONS);
|
||||||
|
|
||||||
|
public static final List<TimelineDefinition> DEFAULT_TIMELINES = BuildConfig.BUILD_TYPE.equals("playRelease")
|
||||||
|
? List.of(HOME_TIMELINE.copy(), LOCAL_TIMELINE.copy())
|
||||||
|
: List.of(HOME_TIMELINE.copy(), LOCAL_TIMELINE.copy(), FEDERATED_TIMELINE.copy());
|
||||||
|
public static final List<TimelineDefinition> ALL_TIMELINES = List.of(
|
||||||
|
HOME_TIMELINE.copy(),
|
||||||
|
LOCAL_TIMELINE.copy(),
|
||||||
|
FEDERATED_TIMELINE.copy(),
|
||||||
|
POSTS_TIMELINE.copy()
|
||||||
|
);
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ package org.joinmastodon.android.ui.displayitems;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -81,7 +80,7 @@ public class ExtendedFooterStatusDisplayItem extends StatusDisplayItem{
|
||||||
editHistory.setVisibility(View.GONE);
|
editHistory.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
String timeStr=TIME_FORMATTER.format(item.status.createdAt.atZone(ZoneId.systemDefault()));
|
String timeStr=TIME_FORMATTER.format(item.status.createdAt.atZone(ZoneId.systemDefault()));
|
||||||
|
|
||||||
if (item.status.application!=null && !TextUtils.isEmpty(item.status.application.name)) {
|
if (item.status.application!=null && !TextUtils.isEmpty(item.status.application.name)) {
|
||||||
time.setText(item.parentFragment.getString(R.string.timestamp_via_app, timeStr, ""));
|
time.setText(item.parentFragment.getString(R.string.timestamp_via_app, timeStr, ""));
|
||||||
applicationName.setText(item.status.application.name);
|
applicationName.setText(item.status.application.name);
|
||||||
|
@ -97,15 +96,11 @@ public class ExtendedFooterStatusDisplayItem extends StatusDisplayItem{
|
||||||
|
|
||||||
visibility.setImageResource(switch (s.visibility) {
|
visibility.setImageResource(switch (s.visibility) {
|
||||||
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
|
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
|
||||||
case UNLISTED -> R.drawable.ic_fluent_people_community_20_regular;
|
case UNLISTED -> R.drawable.ic_fluent_lock_open_20_regular;
|
||||||
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_20_regular;
|
case PRIVATE -> R.drawable.ic_fluent_lock_closed_20_filled;
|
||||||
case DIRECT -> R.drawable.ic_fluent_mention_24_regular;
|
case DIRECT -> R.drawable.ic_fluent_mention_20_regular;
|
||||||
|
case LOCAL -> R.drawable.ic_fluent_eye_20_regular;
|
||||||
});
|
});
|
||||||
|
|
||||||
visibility.setContentDescription(UiUtils.getVisibilityText(s));
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
visibility.setTooltipText(visibility.getContentDescription());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -139,4 +134,4 @@ public class ExtendedFooterStatusDisplayItem extends StatusDisplayItem{
|
||||||
Nav.go(item.parentFragment.getActivity(), StatusEditHistoryFragment.class, args);
|
Nav.go(item.parentFragment.getActivity(), StatusEditHistoryFragment.class, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -151,7 +151,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
||||||
boost.setSelected(item.status.reblogged);
|
boost.setSelected(item.status.reblogged);
|
||||||
favorite.setSelected(item.status.favourited);
|
favorite.setSelected(item.status.favourited);
|
||||||
bookmark.setSelected(item.status.bookmarked);
|
bookmark.setSelected(item.status.bookmarked);
|
||||||
boost.setEnabled(item.status.visibility==StatusPrivacy.PUBLIC || item.status.visibility==StatusPrivacy.UNLISTED
|
boost.setEnabled(item.status.visibility==StatusPrivacy.PUBLIC || item.status.visibility==StatusPrivacy.UNLISTED || item.status.visibility==StatusPrivacy.LOCAL
|
||||||
|| (item.status.visibility==StatusPrivacy.PRIVATE && item.status.account.id.equals(AccountSessionManager.getInstance().getAccount(item.accountID).self.id)));
|
|| (item.status.visibility==StatusPrivacy.PRIVATE && item.status.account.id.equals(AccountSessionManager.getInstance().getAccount(item.accountID).self.id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,8 +253,8 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
||||||
|
|
||||||
Drawable checkMark = ctx.getDrawable(R.drawable.ic_fluent_checkmark_circle_20_regular);
|
Drawable checkMark = ctx.getDrawable(R.drawable.ic_fluent_checkmark_circle_20_regular);
|
||||||
Drawable publicDrawable = ctx.getDrawable(R.drawable.ic_fluent_earth_24_regular);
|
Drawable publicDrawable = ctx.getDrawable(R.drawable.ic_fluent_earth_24_regular);
|
||||||
Drawable unlistedDrawable = ctx.getDrawable(R.drawable.ic_fluent_people_community_24_regular);
|
Drawable unlistedDrawable = ctx.getDrawable(R.drawable.ic_fluent_lock_open_24_regular);
|
||||||
Drawable followersDrawable = ctx.getDrawable(R.drawable.ic_fluent_people_checkmark_24_regular);
|
Drawable followersDrawable = ctx.getDrawable(R.drawable.ic_fluent_lock_closed_24_regular);
|
||||||
|
|
||||||
StatusPrivacy defaultVisibility = session.preferences != null ? session.preferences.postingDefaultVisibility : null;
|
StatusPrivacy defaultVisibility = session.preferences != null ? session.preferences.postingDefaultVisibility : null;
|
||||||
// e.g. post visibility is unlisted, but default is public
|
// e.g. post visibility is unlisted, but default is public
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.fragments.ComposeFragment;
|
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||||
|
import org.joinmastodon.android.fragments.ListTimelinesFragment;
|
||||||
import org.joinmastodon.android.fragments.NotificationsListFragment;
|
import org.joinmastodon.android.fragments.NotificationsListFragment;
|
||||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||||
import org.joinmastodon.android.fragments.ThreadFragment;
|
import org.joinmastodon.android.fragments.ThreadFragment;
|
||||||
|
@ -44,6 +45,7 @@ import org.joinmastodon.android.model.Notification;
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.model.ScheduledStatus;
|
import org.joinmastodon.android.model.ScheduledStatus;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
@ -53,6 +55,7 @@ import java.time.Instant;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.FormatStyle;
|
import java.time.format.FormatStyle;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -183,7 +186,8 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
final Bundle args=new Bundle();
|
final Bundle args=new Bundle();
|
||||||
args.putString("account", item.parentFragment.getAccountID());
|
args.putString("account", item.parentFragment.getAccountID());
|
||||||
args.putParcelable("editStatus", Parcels.wrap(item.status));
|
args.putParcelable("editStatus", Parcels.wrap(item.status));
|
||||||
if (id==R.id.delete_and_redraft) {
|
boolean redraft = id==R.id.delete_and_redraft;
|
||||||
|
if (redraft) {
|
||||||
args.putBoolean("redraftStatus", true);
|
args.putBoolean("redraftStatus", true);
|
||||||
if (item.parentFragment instanceof ThreadFragment thread && !thread.isItemEnabled(item.status.id)) {
|
if (item.parentFragment instanceof ThreadFragment thread && !thread.isItemEnabled(item.status.id)) {
|
||||||
// ("enabled" = clickable; opened status is not clickable)
|
// ("enabled" = clickable; opened status is not clickable)
|
||||||
|
@ -191,7 +195,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
args.putBoolean("navigateToStatus", true);
|
args.putBoolean("navigateToStatus", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(TextUtils.isEmpty(item.status.content) && TextUtils.isEmpty(item.status.spoilerText)){
|
if(!redraft && TextUtils.isEmpty(item.status.content) && TextUtils.isEmpty(item.status.spoilerText)){
|
||||||
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
|
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
|
||||||
}else if(item.scheduledStatus!=null){
|
}else if(item.scheduledStatus!=null){
|
||||||
args.putString("sourceText", item.status.text);
|
args.putString("sourceText", item.status.text);
|
||||||
|
@ -206,7 +210,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
public void onSuccess(GetStatusSourceText.Response result){
|
public void onSuccess(GetStatusSourceText.Response result){
|
||||||
args.putString("sourceText", result.text);
|
args.putString("sourceText", result.text);
|
||||||
args.putString("sourceSpoiler", result.spoilerText);
|
args.putString("sourceSpoiler", result.spoilerText);
|
||||||
if (id==R.id.delete_and_redraft) {
|
if (redraft) {
|
||||||
UiUtils.confirmDeletePost(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.status, s->{
|
UiUtils.confirmDeletePost(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.status, s->{
|
||||||
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
|
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
|
||||||
}, true);
|
}, true);
|
||||||
|
@ -264,6 +268,12 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
UiUtils.confirmToggleBlockDomain(activity, item.parentFragment.getAccountID(), account.getDomain(), relationship!=null && relationship.domainBlocking, ()->{});
|
UiUtils.confirmToggleBlockDomain(activity, item.parentFragment.getAccountID(), account.getDomain(), relationship!=null && relationship.domainBlocking, ()->{});
|
||||||
}else if(id==R.id.bookmark){
|
}else if(id==R.id.bookmark){
|
||||||
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setBookmarked(item.status, !item.status.bookmarked);
|
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setBookmarked(item.status, !item.status.bookmarked);
|
||||||
|
}else if(id==R.id.manage_user_lists){
|
||||||
|
final Bundle args=new Bundle();
|
||||||
|
args.putString("account", item.parentFragment.getAccountID());
|
||||||
|
args.putString("profileAccount", account.id);
|
||||||
|
args.putString("profileDisplayUsername", account.getDisplayUsername());
|
||||||
|
Nav.go(item.parentFragment.getActivity(), ListTimelinesFragment.class, args);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
@ -329,12 +339,15 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
}
|
}
|
||||||
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : 0);
|
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : 0);
|
||||||
if(TextUtils.isEmpty(item.extraText)){
|
if(TextUtils.isEmpty(item.extraText)){
|
||||||
extraText.setVisibility(View.GONE);
|
if (item.status != null) {
|
||||||
|
UiUtils.setExtraTextInfo(item.parentFragment.getContext(), extraText, item.status.visibility, item.status.localOnly);
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
extraText.setVisibility(View.VISIBLE);
|
extraText.setVisibility(View.VISIBLE);
|
||||||
extraText.setText(item.extraText);
|
extraText.setText(item.extraText);
|
||||||
}
|
}
|
||||||
more.setVisibility(item.inset ? View.GONE : View.VISIBLE);
|
more.setVisibility(item.inset || (item.notification != null && item.notification.report != null)
|
||||||
|
? View.GONE : View.VISIBLE);
|
||||||
avatar.setClickable(!item.inset);
|
avatar.setClickable(!item.inset);
|
||||||
avatar.setContentDescription(item.parentFragment.getString(R.string.avatar_description, item.user.acct));
|
avatar.setContentDescription(item.parentFragment.getString(R.string.avatar_description, item.user.acct));
|
||||||
if(currentRelationshipRequest!=null){
|
if(currentRelationshipRequest!=null){
|
||||||
|
@ -362,6 +375,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
public void onSuccess(Object o) {
|
public void onSuccess(Object o) {
|
||||||
item.consumeReadAnnouncement.accept(item.announcement.id);
|
item.consumeReadAnnouncement.accept(item.announcement.id);
|
||||||
item.announcement.read = true;
|
item.announcement.read = true;
|
||||||
|
if (item.parentFragment.getActivity() == null) return;
|
||||||
rebind();
|
rebind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,6 +448,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateOptionsMenu(){
|
private void updateOptionsMenu(){
|
||||||
|
if (item.parentFragment.getActivity() == null) return;
|
||||||
if (item.announcement != null) return;
|
if (item.announcement != null) return;
|
||||||
boolean hasMultipleAccounts = AccountSessionManager.getInstance().getLoggedInAccounts().size() > 1;
|
boolean hasMultipleAccounts = AccountSessionManager.getInstance().getLoggedInAccounts().size() > 1;
|
||||||
Menu menu=optionsMenu.getMenu();
|
Menu menu=optionsMenu.getMenu();
|
||||||
|
@ -464,6 +479,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
MenuItem block=menu.findItem(R.id.block);
|
MenuItem block=menu.findItem(R.id.block);
|
||||||
MenuItem report=menu.findItem(R.id.report);
|
MenuItem report=menu.findItem(R.id.report);
|
||||||
MenuItem follow=menu.findItem(R.id.follow);
|
MenuItem follow=menu.findItem(R.id.follow);
|
||||||
|
MenuItem manageUserLists = menu.findItem(R.id.manage_user_lists);
|
||||||
MenuItem bookmark=menu.findItem(R.id.bookmark);
|
MenuItem bookmark=menu.findItem(R.id.bookmark);
|
||||||
bookmark.setVisible(false);
|
bookmark.setVisible(false);
|
||||||
/* disabled in megalodon: add/remove bookmark is already available through status footer
|
/* disabled in megalodon: add/remove bookmark is already available through status footer
|
||||||
|
@ -480,6 +496,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
report.setVisible(false);
|
report.setVisible(false);
|
||||||
follow.setVisible(false);
|
follow.setVisible(false);
|
||||||
blockDomain.setVisible(false);
|
blockDomain.setVisible(false);
|
||||||
|
manageUserLists.setVisible(false);
|
||||||
}else{
|
}else{
|
||||||
mute.setVisible(true);
|
mute.setVisible(true);
|
||||||
block.setVisible(true);
|
block.setVisible(true);
|
||||||
|
@ -500,6 +517,8 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
boolean following = relationship!=null && relationship.following;
|
boolean following = relationship!=null && relationship.following;
|
||||||
follow.setTitle(item.parentFragment.getString(following ? R.string.unfollow_user : R.string.follow_user, account.getShortUsername()));
|
follow.setTitle(item.parentFragment.getString(following ? R.string.unfollow_user : R.string.follow_user, account.getShortUsername()));
|
||||||
follow.setIcon(following ? R.drawable.ic_fluent_person_delete_24_regular : R.drawable.ic_fluent_person_add_24_regular);
|
follow.setIcon(following ? R.drawable.ic_fluent_person_delete_24_regular : R.drawable.ic_fluent_person_add_24_regular);
|
||||||
|
manageUserLists.setVisible(relationship != null && relationship.following);
|
||||||
|
manageUserLists.setTitle(item.parentFragment.getString(R.string.sk_lists_with_user, account.getShortUsername()));
|
||||||
UiUtils.insetPopupMenuIcon(item.parentFragment.getContext(), follow);
|
UiUtils.insetPopupMenuIcon(item.parentFragment.getContext(), follow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,11 @@ import android.view.ViewGroup;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.ImageView;
|
||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
|
@ -22,6 +24,7 @@ import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
||||||
|
|
||||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class PhotoStatusDisplayItem extends ImageStatusDisplayItem{
|
public class PhotoStatusDisplayItem extends ImageStatusDisplayItem{
|
||||||
public PhotoStatusDisplayItem(String parentID, Status status, Attachment photo, BaseStatusListFragment parentFragment, int index, int totalPhotos, PhotoLayoutHelper.TiledLayoutResult tiledLayout, PhotoLayoutHelper.TiledLayoutResult.Tile thisTile){
|
public PhotoStatusDisplayItem(String parentID, Status status, Attachment photo, BaseStatusListFragment parentFragment, int index, int totalPhotos, PhotoLayoutHelper.TiledLayoutResult tiledLayout, PhotoLayoutHelper.TiledLayoutResult.Tile thisTile){
|
||||||
|
@ -37,10 +40,12 @@ public class PhotoStatusDisplayItem extends ImageStatusDisplayItem{
|
||||||
public static class Holder extends ImageStatusDisplayItem.Holder<PhotoStatusDisplayItem>{
|
public static class Holder extends ImageStatusDisplayItem.Holder<PhotoStatusDisplayItem>{
|
||||||
private final FrameLayout altTextWrapper;
|
private final FrameLayout altTextWrapper;
|
||||||
private final TextView altTextButton;
|
private final TextView altTextButton;
|
||||||
|
private final ImageView noAltTextButton;
|
||||||
private final View altTextScroller;
|
private final View altTextScroller;
|
||||||
private final ImageButton altTextClose;
|
private final ImageButton altTextClose;
|
||||||
private final TextView altText;
|
private final TextView altText, noAltText;
|
||||||
|
|
||||||
|
private View altOrNoAltButton;
|
||||||
private boolean altTextShown;
|
private boolean altTextShown;
|
||||||
private AnimatorSet currentAnim;
|
private AnimatorSet currentAnim;
|
||||||
|
|
||||||
|
@ -48,11 +53,14 @@ public class PhotoStatusDisplayItem extends ImageStatusDisplayItem{
|
||||||
super(activity, R.layout.display_item_photo, parent);
|
super(activity, R.layout.display_item_photo, parent);
|
||||||
altTextWrapper=findViewById(R.id.alt_text_wrapper);
|
altTextWrapper=findViewById(R.id.alt_text_wrapper);
|
||||||
altTextButton=findViewById(R.id.alt_button);
|
altTextButton=findViewById(R.id.alt_button);
|
||||||
|
noAltTextButton=findViewById(R.id.no_alt_button);
|
||||||
altTextScroller=findViewById(R.id.alt_text_scroller);
|
altTextScroller=findViewById(R.id.alt_text_scroller);
|
||||||
altTextClose=findViewById(R.id.alt_text_close);
|
altTextClose=findViewById(R.id.alt_text_close);
|
||||||
altText=findViewById(R.id.alt_text);
|
altText=findViewById(R.id.alt_text);
|
||||||
|
noAltText=findViewById(R.id.no_alt_text);
|
||||||
|
|
||||||
altTextButton.setOnClickListener(this::onShowHideClick);
|
altTextButton.setOnClickListener(this::onShowHideClick);
|
||||||
|
noAltTextButton.setOnClickListener(this::onShowHideClick);
|
||||||
altTextClose.setOnClickListener(this::onShowHideClick);
|
altTextClose.setOnClickListener(this::onShowHideClick);
|
||||||
// altTextScroller.setNestedScrollingEnabled(true);
|
// altTextScroller.setNestedScrollingEnabled(true);
|
||||||
}
|
}
|
||||||
|
@ -60,22 +68,48 @@ public class PhotoStatusDisplayItem extends ImageStatusDisplayItem{
|
||||||
@Override
|
@Override
|
||||||
public void onBind(ImageStatusDisplayItem item){
|
public void onBind(ImageStatusDisplayItem item){
|
||||||
super.onBind(item);
|
super.onBind(item);
|
||||||
|
boolean altTextMissing = TextUtils.isEmpty(item.attachment.description);
|
||||||
|
altOrNoAltButton = altTextMissing ? noAltTextButton : altTextButton;
|
||||||
altTextShown=false;
|
altTextShown=false;
|
||||||
if(currentAnim!=null)
|
if(currentAnim!=null)
|
||||||
currentAnim.cancel();
|
currentAnim.cancel();
|
||||||
|
|
||||||
altTextScroller.setVisibility(View.GONE);
|
altTextScroller.setVisibility(View.GONE);
|
||||||
altTextClose.setVisibility(View.GONE);
|
altTextClose.setVisibility(View.GONE);
|
||||||
altTextButton.setVisibility(View.VISIBLE);
|
altTextButton.setVisibility(View.VISIBLE);
|
||||||
if(TextUtils.isEmpty(item.attachment.description)){
|
noAltTextButton.setVisibility(View.VISIBLE);
|
||||||
altTextWrapper.setVisibility(View.GONE);
|
altTextButton.setAlpha(1f);
|
||||||
|
noAltTextButton.setAlpha(1f);
|
||||||
|
altTextWrapper.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
if (altTextMissing){
|
||||||
|
if (GlobalUserPreferences.showNoAltIndicator) {
|
||||||
|
noAltTextButton.setVisibility(View.VISIBLE);
|
||||||
|
noAltText.setVisibility(View.VISIBLE);
|
||||||
|
altTextWrapper.setBackgroundResource(R.drawable.bg_image_no_alt_overlay);
|
||||||
|
altTextButton.setVisibility(View.GONE);
|
||||||
|
altText.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
altTextWrapper.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
altTextWrapper.setVisibility(View.VISIBLE);
|
if (GlobalUserPreferences.showAltIndicator) {
|
||||||
altText.setText(item.attachment.description);
|
noAltTextButton.setVisibility(View.GONE);
|
||||||
|
noAltText.setVisibility(View.GONE);
|
||||||
|
altTextWrapper.setBackgroundResource(R.drawable.bg_image_alt_overlay);
|
||||||
|
altTextButton.setVisibility(View.VISIBLE);
|
||||||
|
altTextButton.setText(R.string.sk_alt_button);
|
||||||
|
altText.setVisibility(View.VISIBLE);
|
||||||
|
altText.setText(item.attachment.description);
|
||||||
|
altText.setPadding(0, 0, 0, 0);
|
||||||
|
} else {
|
||||||
|
altTextWrapper.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onShowHideClick(View v){
|
private void onShowHideClick(View v){
|
||||||
boolean show=v.getId()==R.id.alt_button;
|
boolean show=v.getId()==R.id.alt_button || v.getId()==R.id.no_alt_button;
|
||||||
|
|
||||||
if(altTextShown==show)
|
if(altTextShown==show)
|
||||||
return;
|
return;
|
||||||
|
@ -87,7 +121,7 @@ public class PhotoStatusDisplayItem extends ImageStatusDisplayItem{
|
||||||
altTextScroller.setVisibility(View.VISIBLE);
|
altTextScroller.setVisibility(View.VISIBLE);
|
||||||
altTextClose.setVisibility(View.VISIBLE);
|
altTextClose.setVisibility(View.VISIBLE);
|
||||||
}else{
|
}else{
|
||||||
altTextButton.setVisibility(View.VISIBLE);
|
altOrNoAltButton.setVisibility(View.VISIBLE);
|
||||||
// Hide these views temporarily so FrameLayout measures correctly
|
// Hide these views temporarily so FrameLayout measures correctly
|
||||||
altTextScroller.setVisibility(View.GONE);
|
altTextScroller.setVisibility(View.GONE);
|
||||||
altTextClose.setVisibility(View.GONE);
|
altTextClose.setVisibility(View.GONE);
|
||||||
|
@ -114,7 +148,7 @@ public class PhotoStatusDisplayItem extends ImageStatusDisplayItem{
|
||||||
ObjectAnimator.ofInt(altTextWrapper, "left", prevLeft, altTextWrapper.getLeft()),
|
ObjectAnimator.ofInt(altTextWrapper, "left", prevLeft, altTextWrapper.getLeft()),
|
||||||
ObjectAnimator.ofInt(altTextWrapper, "right", prevRight, altTextWrapper.getRight()),
|
ObjectAnimator.ofInt(altTextWrapper, "right", prevRight, altTextWrapper.getRight()),
|
||||||
ObjectAnimator.ofInt(altTextWrapper, "top", prevTop, altTextWrapper.getTop()),
|
ObjectAnimator.ofInt(altTextWrapper, "top", prevTop, altTextWrapper.getTop()),
|
||||||
ObjectAnimator.ofFloat(altTextButton, View.ALPHA, show ? 1f : 0f, show ? 0f : 1f),
|
ObjectAnimator.ofFloat(altOrNoAltButton, View.ALPHA, show ? 1f : 0f, show ? 0f : 1f),
|
||||||
ObjectAnimator.ofFloat(altTextScroller, View.ALPHA, show ? 0f : 1f, show ? 1f : 0f),
|
ObjectAnimator.ofFloat(altTextScroller, View.ALPHA, show ? 0f : 1f, show ? 1f : 0f),
|
||||||
ObjectAnimator.ofFloat(altTextClose, View.ALPHA, show ? 0f : 1f, show ? 1f : 0f)
|
ObjectAnimator.ofFloat(altTextClose, View.ALPHA, show ? 0f : 1f, show ? 1f : 0f)
|
||||||
);
|
);
|
||||||
|
@ -124,7 +158,7 @@ public class PhotoStatusDisplayItem extends ImageStatusDisplayItem{
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation){
|
public void onAnimationEnd(Animator animation){
|
||||||
if(show){
|
if(show){
|
||||||
altTextButton.setVisibility(View.GONE);
|
altOrNoAltButton.setVisibility(View.GONE);
|
||||||
}else{
|
}else{
|
||||||
altTextScroller.setVisibility(View.GONE);
|
altTextScroller.setVisibility(View.GONE);
|
||||||
altTextClose.setVisibility(View.GONE);
|
altTextClose.setVisibility(View.GONE);
|
||||||
|
|
|
@ -39,10 +39,11 @@ public class PollFooterStatusDisplayItem extends StatusDisplayItem{
|
||||||
@Override
|
@Override
|
||||||
public void onBind(PollFooterStatusDisplayItem item){
|
public void onBind(PollFooterStatusDisplayItem item){
|
||||||
String text=item.parentFragment.getResources().getQuantityString(R.plurals.x_voters, item.poll.votersCount, item.poll.votersCount);
|
String text=item.parentFragment.getResources().getQuantityString(R.plurals.x_voters, item.poll.votersCount, item.poll.votersCount);
|
||||||
|
String sep=item.parentFragment.getString(R.string.sk_separator);
|
||||||
if(item.poll.expiresAt!=null && !item.poll.isExpired()){
|
if(item.poll.expiresAt!=null && !item.poll.isExpired()){
|
||||||
text+=" · "+UiUtils.formatTimeLeft(itemView.getContext(), item.poll.expiresAt);
|
text+=" "+sep+" "+UiUtils.formatTimeLeft(itemView.getContext(), item.poll.expiresAt);
|
||||||
}else if(item.poll.isExpired()){
|
}else if(item.poll.isExpired()){
|
||||||
text+=" · "+item.parentFragment.getString(R.string.poll_closed);
|
text+=" "+sep+" "+item.parentFragment.getString(R.string.poll_closed);
|
||||||
}
|
}
|
||||||
this.text.setText(text);
|
this.text.setText(text);
|
||||||
button.setVisibility(item.poll.isExpired() || item.poll.voted || (!item.poll.multiple && !GlobalUserPreferences.voteButtonForSingleChoice) ? View.GONE : View.VISIBLE);
|
button.setVisibility(item.poll.isExpired() || item.poll.voted || (!item.poll.multiple && !GlobalUserPreferences.voteButtonForSingleChoice) ? View.GONE : View.VISIBLE);
|
||||||
|
|
|
@ -55,8 +55,8 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
|
||||||
this.visibility = visibility;
|
this.visibility = visibility;
|
||||||
this.iconEnd = visibility != null ? switch (visibility) {
|
this.iconEnd = visibility != null ? switch (visibility) {
|
||||||
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
|
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
|
||||||
case UNLISTED -> R.drawable.ic_fluent_people_community_20_regular;
|
case UNLISTED -> R.drawable.ic_fluent_lock_open_20_regular;
|
||||||
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_20_regular;
|
case PRIVATE -> R.drawable.ic_fluent_lock_closed_20_filled;
|
||||||
default -> 0;
|
default -> 0;
|
||||||
} : 0;
|
} : 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,11 +10,16 @@ import android.view.ViewGroup;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
|
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
||||||
|
import org.joinmastodon.android.fragments.HomeTabFragment;
|
||||||
|
import org.joinmastodon.android.fragments.HomeTimelineFragment;
|
||||||
|
import org.joinmastodon.android.fragments.ListTimelineFragment;
|
||||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||||
import org.joinmastodon.android.fragments.ThreadFragment;
|
import org.joinmastodon.android.fragments.ThreadFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
import org.joinmastodon.android.model.DisplayItemsParent;
|
import org.joinmastodon.android.model.DisplayItemsParent;
|
||||||
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
import org.joinmastodon.android.model.Poll;
|
import org.joinmastodon.android.model.Poll;
|
||||||
import org.joinmastodon.android.model.ScheduledStatus;
|
import org.joinmastodon.android.model.ScheduledStatus;
|
||||||
|
@ -27,6 +32,7 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
|
@ -100,6 +106,25 @@ public abstract class StatusDisplayItem{
|
||||||
args.putParcelable("profileAccount", Parcels.wrap(account));
|
args.putParcelable("profileAccount", Parcels.wrap(account));
|
||||||
Nav.go(fragment.getActivity(), ProfileFragment.class, args);
|
Nav.go(fragment.getActivity(), ProfileFragment.class, args);
|
||||||
}));
|
}));
|
||||||
|
} else if (
|
||||||
|
!(status.tags.isEmpty() ||
|
||||||
|
fragment instanceof HashtagTimelineFragment ||
|
||||||
|
fragment instanceof ListTimelineFragment
|
||||||
|
) && fragment.getParentFragment() instanceof HomeTabFragment home
|
||||||
|
) {
|
||||||
|
home.getHashtags().stream()
|
||||||
|
.filter(followed -> status.tags.stream()
|
||||||
|
.anyMatch(hashtag -> followed.name.equalsIgnoreCase(hashtag.name)))
|
||||||
|
.findAny()
|
||||||
|
// post contains a hashtag the user is following
|
||||||
|
.ifPresent(hashtag -> items.add(new ReblogOrReplyLineStatusDisplayItem(
|
||||||
|
parentID, fragment, hashtag.name, List.of(),
|
||||||
|
R.drawable.ic_fluent_number_symbol_20_filled, null,
|
||||||
|
i -> {
|
||||||
|
args.putString("hashtag", hashtag.name);
|
||||||
|
Nav.go(fragment.getActivity(), HashtagTimelineFragment.class, args);
|
||||||
|
}
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
HeaderStatusDisplayItem header;
|
HeaderStatusDisplayItem header;
|
||||||
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null, notification, scheduledStatus));
|
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null, notification, scheduledStatus));
|
||||||
|
|
|
@ -145,8 +145,9 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||||
}
|
}
|
||||||
|
|
||||||
Instance instanceInfo = AccountSessionManager.getInstance().getInstanceInfo(item.session.domain);
|
Instance instanceInfo = AccountSessionManager.getInstance().getInstanceInfo(item.session.domain);
|
||||||
boolean translateEnabled = !item.disableTranslate && instanceInfo.v2 != null &&
|
boolean translateEnabled = !item.disableTranslate && instanceInfo != null &&
|
||||||
instanceInfo.v2.configuration.translation != null && instanceInfo.v2.configuration.translation.enabled;
|
instanceInfo.v2 != null && instanceInfo.v2.configuration.translation != null &&
|
||||||
|
instanceInfo.v2.configuration.translation.enabled;
|
||||||
|
|
||||||
translateWrap.setVisibility(translateEnabled &&
|
translateWrap.setVisibility(translateEnabled &&
|
||||||
!item.status.visibility.isLessVisibleThan(StatusPrivacy.UNLISTED) &&
|
!item.status.visibility.isLessVisibleThan(StatusPrivacy.UNLISTED) &&
|
||||||
|
@ -165,6 +166,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||||
public void onSuccess(TranslatedStatus translatedStatus) {
|
public void onSuccess(TranslatedStatus translatedStatus) {
|
||||||
item.translation = translatedStatus;
|
item.translation = translatedStatus;
|
||||||
item.translated = true;
|
item.translated = true;
|
||||||
|
if (item.parentFragment.getActivity() == null) return;
|
||||||
translateProgress.setVisibility(View.GONE);
|
translateProgress.setVisibility(View.GONE);
|
||||||
translateButton.setClickable(true);
|
translateButton.setClickable(true);
|
||||||
translateButton.animate().alpha(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(50).start();
|
translateButton.animate().alpha(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(50).start();
|
||||||
|
|
|
@ -37,6 +37,7 @@ public class DiscoverInfoBannerHelper{
|
||||||
case TRENDING_LINKS -> R.string.trending_links_info_banner;
|
case TRENDING_LINKS -> R.string.trending_links_info_banner;
|
||||||
case LOCAL_TIMELINE -> R.string.local_timeline_info_banner;
|
case LOCAL_TIMELINE -> R.string.local_timeline_info_banner;
|
||||||
case FEDERATED_TIMELINE -> R.string.sk_federated_timeline_info_banner;
|
case FEDERATED_TIMELINE -> R.string.sk_federated_timeline_info_banner;
|
||||||
|
case POST_NOTIFICATIONS -> R.string.sk_notify_posts_info_banner;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,6 +62,7 @@ public class DiscoverInfoBannerHelper{
|
||||||
TRENDING_LINKS,
|
TRENDING_LINKS,
|
||||||
LOCAL_TIMELINE,
|
LOCAL_TIMELINE,
|
||||||
FEDERATED_TIMELINE,
|
FEDERATED_TIMELINE,
|
||||||
|
POST_NOTIFICATIONS,
|
||||||
// ACCOUNTS
|
// ACCOUNTS
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package org.joinmastodon.android.ui.utils;
|
package org.joinmastodon.android.ui.utils;
|
||||||
|
|
||||||
|
import static android.view.Menu.NONE;
|
||||||
import static org.joinmastodon.android.GlobalUserPreferences.theme;
|
import static org.joinmastodon.android.GlobalUserPreferences.theme;
|
||||||
import static org.joinmastodon.android.GlobalUserPreferences.trueBlackTheme;
|
import static org.joinmastodon.android.GlobalUserPreferences.trueBlackTheme;
|
||||||
|
|
||||||
|
@ -35,9 +36,12 @@ import android.util.Log;
|
||||||
import android.view.HapticFeedbackConstants;
|
import android.view.HapticFeedbackConstants;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.SubMenu;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.PopupMenu;
|
import android.widget.PopupMenu;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
@ -71,18 +75,17 @@ import org.joinmastodon.android.events.StatusDeletedEvent;
|
||||||
import org.joinmastodon.android.events.StatusUnpinnedEvent;
|
import org.joinmastodon.android.events.StatusUnpinnedEvent;
|
||||||
import org.joinmastodon.android.fragments.ComposeFragment;
|
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||||
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
||||||
import org.joinmastodon.android.fragments.ListTimelineFragment;
|
|
||||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||||
import org.joinmastodon.android.fragments.ThreadFragment;
|
import org.joinmastodon.android.fragments.ThreadFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Emoji;
|
import org.joinmastodon.android.model.Emoji;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.model.ScheduledStatus;
|
import org.joinmastodon.android.model.ScheduledStatus;
|
||||||
import org.joinmastodon.android.model.SearchResults;
|
import org.joinmastodon.android.model.SearchResults;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
@ -97,6 +100,7 @@ import java.time.ZoneId;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.FormatStyle;
|
import java.time.format.FormatStyle;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -372,6 +376,7 @@ public class UiUtils{
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Relationship result){
|
public void onSuccess(Relationship result){
|
||||||
|
if (activity == null) return;
|
||||||
resultCallback.accept(result);
|
resultCallback.accept(result);
|
||||||
if(!currentlyBlocked){
|
if(!currentlyBlocked){
|
||||||
E.post(new RemoveAccountPostsEvent(accountID, account.id, false));
|
E.post(new RemoveAccountPostsEvent(accountID, account.id, false));
|
||||||
|
@ -400,6 +405,7 @@ public class UiUtils{
|
||||||
new SetAccountBlocked(account.id, false).setCallback(new Callback<>() {
|
new SetAccountBlocked(account.id, false).setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Relationship relationship) {
|
public void onSuccess(Relationship relationship) {
|
||||||
|
if (activity == null) return;
|
||||||
Toast.makeText(activity, R.string.sk_remove_follower_success, Toast.LENGTH_SHORT).show();
|
Toast.makeText(activity, R.string.sk_remove_follower_success, Toast.LENGTH_SHORT).show();
|
||||||
resultCallback.accept(relationship);
|
resultCallback.accept(relationship);
|
||||||
}
|
}
|
||||||
|
@ -507,7 +513,7 @@ public class UiUtils{
|
||||||
() -> new DeleteStatus.Scheduled(status.id)
|
() -> new DeleteStatus.Scheduled(status.id)
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Object nothing){
|
public void onSuccess(Object o){
|
||||||
resultCallback.run();
|
resultCallback.run();
|
||||||
E.post(new ScheduledStatusDeletedEvent(status.id, accountID));
|
E.post(new ScheduledStatusDeletedEvent(status.id, accountID));
|
||||||
}
|
}
|
||||||
|
@ -772,11 +778,20 @@ public class UiUtils{
|
||||||
item.setTitle(ssb);
|
item.setTitle(ssb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void resetPopupItemTint(MenuItem item) {
|
||||||
|
if(Build.VERSION.SDK_INT>=26) {
|
||||||
|
item.setIconTintList(null);
|
||||||
|
} else {
|
||||||
|
Drawable icon=item.getIcon().mutate();
|
||||||
|
icon.setTintList(null);
|
||||||
|
item.setIcon(icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void enableOptionsMenuIcons(Context context, Menu menu, @IdRes int... asAction) {
|
public static void enableOptionsMenuIcons(Context context, Menu menu, @IdRes int... asAction) {
|
||||||
if(menu.getClass().getSimpleName().equals("MenuBuilder")){
|
if(menu.getClass().getSimpleName().equals("MenuBuilder")){
|
||||||
try {
|
try {
|
||||||
Method m = menu.getClass().getDeclaredMethod(
|
Method m = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);
|
||||||
"setOptionalIconsVisible", Boolean.TYPE);
|
|
||||||
m.setAccessible(true);
|
m.setAccessible(true);
|
||||||
m.invoke(menu, true);
|
m.invoke(menu, true);
|
||||||
enableMenuIcons(context, menu, asAction);
|
enableMenuIcons(context, menu, asAction);
|
||||||
|
@ -789,6 +804,8 @@ public class UiUtils{
|
||||||
ColorStateList iconTint=ColorStateList.valueOf(UiUtils.getThemeColor(context, android.R.attr.textColorSecondary));
|
ColorStateList iconTint=ColorStateList.valueOf(UiUtils.getThemeColor(context, android.R.attr.textColorSecondary));
|
||||||
for(int i=0;i<m.size();i++){
|
for(int i=0;i<m.size();i++){
|
||||||
MenuItem item=m.getItem(i);
|
MenuItem item=m.getItem(i);
|
||||||
|
SubMenu subMenu = item.getSubMenu();
|
||||||
|
if (subMenu != null) enableMenuIcons(context, subMenu, exclude);
|
||||||
if (item.getIcon() == null || Arrays.stream(exclude).anyMatch(id -> id == item.getItemId())) continue;
|
if (item.getIcon() == null || Arrays.stream(exclude).anyMatch(id -> id == item.getItemId())) continue;
|
||||||
insetPopupMenuIcon(item, iconTint);
|
insetPopupMenuIcon(item, iconTint);
|
||||||
}
|
}
|
||||||
|
@ -887,6 +904,35 @@ public class UiUtils{
|
||||||
builder.show();
|
builder.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void restartApp() {
|
||||||
|
Intent intent = Intent.makeRestartActivityTask(MastodonApp.context.getPackageManager().getLaunchIntentForPackage(MastodonApp.context.getPackageName()).getComponent());
|
||||||
|
MastodonApp.context.startActivity(intent);
|
||||||
|
Runtime.getRuntime().exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MenuItem makeBackItem(Menu m) {
|
||||||
|
MenuItem back = m.add(0, R.id.menu_back, NONE, R.string.back);
|
||||||
|
back.setIcon(R.drawable.ic_fluent_arrow_left_24_regular);
|
||||||
|
return back;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setExtraTextInfo(Context ctx, TextView extraText, StatusPrivacy visibility, boolean localOnly) {
|
||||||
|
List<String> extraParts = new ArrayList<>();
|
||||||
|
if (localOnly || (visibility != null && visibility.equals(StatusPrivacy.LOCAL)))
|
||||||
|
extraParts.add(ctx.getString(R.string.sk_inline_local_only));
|
||||||
|
if (visibility != null && visibility.equals(StatusPrivacy.DIRECT))
|
||||||
|
extraParts.add(ctx.getString(R.string.sk_inline_direct));
|
||||||
|
if (!extraParts.isEmpty()) {
|
||||||
|
String sep = ctx.getString(R.string.sk_separator);
|
||||||
|
extraText.setText(String.join(" " + sep + " ", extraParts));
|
||||||
|
extraText.setVisibility(View.VISIBLE);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
extraText.setVisibility(View.GONE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface InteractionPerformer {
|
public interface InteractionPerformer {
|
||||||
void interact(StatusInteractionController ic, Status status, Consumer<Status> resultConsumer);
|
void interact(StatusInteractionController ic, Status status, Consumer<Status> resultConsumer);
|
||||||
|
@ -1055,14 +1101,14 @@ public class UiUtils{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getVisibilityText(Status status) {
|
// public static String getVisibilityText(Status status) {
|
||||||
return MastodonApp.context.getString(switch (status.visibility) {
|
// return MastodonApp.context.getString(switch (status.visibility) {
|
||||||
case PUBLIC -> R.string.visibility_public;
|
// case PUBLIC -> R.string.visibility_public;
|
||||||
case UNLISTED -> R.string.sk_visibility_unlisted;
|
// case UNLISTED -> R.string.sk_visibility_unlisted;
|
||||||
case PRIVATE -> R.string.visibility_followers_only;
|
// case PRIVATE -> R.string.visibility_followers_only;
|
||||||
case DIRECT -> R.string.visibility_private;
|
// case DIRECT -> R.string.visibility_private;;
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
// https://github.com/tuskyapp/Tusky/pull/3148
|
// https://github.com/tuskyapp/Tusky/pull/3148
|
||||||
public static void reduceSwipeSensitivity(ViewPager2 pager) {
|
public static void reduceSwipeSensitivity(ViewPager2 pager) {
|
||||||
|
@ -1078,4 +1124,50 @@ public class UiUtils{
|
||||||
Log.e("reduceSwipeSensitivity", Log.getStackTraceString(ex));
|
Log.e("reduceSwipeSensitivity", Log.getStackTraceString(ex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static View makeOverflowActionView(Context ctx) {
|
||||||
|
// container needs tooltip, content description
|
||||||
|
LinearLayout container = new LinearLayout(ctx, null, 0, R.style.Widget_Mastodon_ActionButton_Overflow) {
|
||||||
|
@Override
|
||||||
|
public CharSequence getAccessibilityClassName() {
|
||||||
|
return Button.class.getName();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// image needs, well, the image, and the paddings
|
||||||
|
ImageView image = new ImageView(ctx, null, 0, R.style.Widget_Mastodon_ActionButton_Overflow);
|
||||||
|
|
||||||
|
image.setDuplicateParentStateEnabled(true);
|
||||||
|
image.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
|
||||||
|
image.setClickable(false);
|
||||||
|
image.setFocusable(false);
|
||||||
|
image.setEnabled(false);
|
||||||
|
|
||||||
|
// problem: as per overflow action button defaults, the padding on left and right is unequal
|
||||||
|
// so (however the native overflow button manages this), the ripple background is off-center
|
||||||
|
|
||||||
|
// workaround: set both paddings to the smaller, left one…
|
||||||
|
int end = image.getPaddingEnd();
|
||||||
|
int start = image.getPaddingStart();
|
||||||
|
int paddingDiff = end - start; // what's missing to the long padding
|
||||||
|
image.setPaddingRelative(start, image.getPaddingTop(), start, image.getPaddingBottom());
|
||||||
|
|
||||||
|
// …and add the missing padding to the right on the container
|
||||||
|
container.setPaddingRelative(0, 0, paddingDiff, 0);
|
||||||
|
container.setBackground(null);
|
||||||
|
container.setClickable(true);
|
||||||
|
container.setFocusable(true);
|
||||||
|
|
||||||
|
container.addView(image);
|
||||||
|
|
||||||
|
// fucking finally
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int alphaBlendColors(int color1, int color2, float alpha){
|
||||||
|
float alpha0=1f-alpha;
|
||||||
|
int r=Math.round(((color1 >> 16) & 0xFF)*alpha0+((color2 >> 16) & 0xFF)*alpha);
|
||||||
|
int g=Math.round(((color1 >> 8) & 0xFF)*alpha0+((color2 >> 8) & 0xFF)*alpha);
|
||||||
|
int b=Math.round((color1 & 0xFF)*alpha0+(color2 & 0xFF)*alpha);
|
||||||
|
return 0xFF000000 | (r << 16) | (g << 8) | b;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,9 @@ import android.widget.Button;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.PopupMenu;
|
import android.widget.PopupMenu;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
|
|
||||||
|
@ -37,9 +40,9 @@ public class ListTimelineEditor extends LinearLayout {
|
||||||
setRepliesPolicy(ListTimeline.RepliesPolicy.LIST);
|
setRepliesPolicy(ListTimeline.RepliesPolicy.LIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyList(String title, ListTimeline.RepliesPolicy policy) {
|
public void applyList(String title, @Nullable ListTimeline.RepliesPolicy policy) {
|
||||||
input.getEditText().setText(title);
|
input.getEditText().setText(title);
|
||||||
setRepliesPolicy(policy);
|
if (policy != null) setRepliesPolicy(policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
|
@ -50,7 +53,7 @@ public class ListTimelineEditor extends LinearLayout {
|
||||||
return policy;
|
return policy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRepliesPolicy(ListTimeline.RepliesPolicy policy) {
|
public void setRepliesPolicy(@NonNull ListTimeline.RepliesPolicy policy) {
|
||||||
this.policy = policy;
|
this.policy = policy;
|
||||||
switch (policy) {
|
switch (policy) {
|
||||||
case FOLLOWED -> button.setText(R.string.sk_list_replies_policy_followed);
|
case FOLLOWED -> button.setText(R.string.sk_list_replies_policy_followed);
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
package org.joinmastodon.android.utils;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.AnimatorSet;
|
||||||
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.LayerDrawable;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
||||||
|
|
||||||
|
public class ElevationOnScrollListener extends RecyclerView.OnScrollListener implements View.OnScrollChangeListener{
|
||||||
|
private boolean isAtTop;
|
||||||
|
private Animator currentPanelsAnim;
|
||||||
|
private View[] views;
|
||||||
|
private FragmentRootLinearLayout fragmentRootLayout;
|
||||||
|
|
||||||
|
public ElevationOnScrollListener(FragmentRootLinearLayout fragmentRootLayout, View... views){
|
||||||
|
isAtTop=true;
|
||||||
|
this.fragmentRootLayout=fragmentRootLayout;
|
||||||
|
this.views=views;
|
||||||
|
for(View v:views){
|
||||||
|
Drawable bg=v.getBackground().mutate();
|
||||||
|
v.setBackground(bg);
|
||||||
|
if(bg instanceof LayerDrawable ld){
|
||||||
|
Drawable overlay=ld.findDrawableByLayerId(R.id.color_overlay);
|
||||||
|
if(overlay!=null){
|
||||||
|
overlay.setAlpha(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setViews(View... views){
|
||||||
|
List<View> oldViews=Arrays.asList(this.views);
|
||||||
|
this.views=views;
|
||||||
|
for(View v:views){
|
||||||
|
if(oldViews.contains(v))
|
||||||
|
continue;
|
||||||
|
Drawable bg=v.getBackground().mutate();
|
||||||
|
v.setBackground(bg);
|
||||||
|
if(bg instanceof LayerDrawable ld){
|
||||||
|
Drawable overlay=ld.findDrawableByLayerId(R.id.color_overlay);
|
||||||
|
if(overlay!=null){
|
||||||
|
overlay.setAlpha(isAtTop ? 0 : 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v.setTranslationZ(isAtTop ? 0 : V.dp(3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
|
||||||
|
boolean newAtTop=recyclerView.getChildCount()==0 || (recyclerView.getChildAdapterPosition(recyclerView.getChildAt(0))==0 && recyclerView.getChildAt(0).getTop()==recyclerView.getPaddingTop());
|
||||||
|
handleScroll(recyclerView.getContext(), newAtTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){
|
||||||
|
handleScroll(v.getContext(), scrollY==0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleScroll(Context context, boolean newAtTop){
|
||||||
|
if(newAtTop!=isAtTop){
|
||||||
|
isAtTop=newAtTop;
|
||||||
|
if(currentPanelsAnim!=null)
|
||||||
|
currentPanelsAnim.cancel();
|
||||||
|
|
||||||
|
AnimatorSet set=new AnimatorSet();
|
||||||
|
ArrayList<Animator> anims=new ArrayList<>();
|
||||||
|
for(View v:views){
|
||||||
|
if(v.getBackground() instanceof LayerDrawable ld){
|
||||||
|
Drawable overlay=ld.findDrawableByLayerId(R.id.color_overlay);
|
||||||
|
if(overlay!=null){
|
||||||
|
anims.add(ObjectAnimator.ofInt(overlay, "alpha", isAtTop ? 0 : 20));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
anims.add(ObjectAnimator.ofFloat(v, View.TRANSLATION_Z, isAtTop ? 0 : V.dp(3)));
|
||||||
|
}
|
||||||
|
if(fragmentRootLayout!=null){
|
||||||
|
int color;
|
||||||
|
if(isAtTop){
|
||||||
|
color=UiUtils.getThemeColor(context, R.attr.toolbarBackground);
|
||||||
|
}else{
|
||||||
|
color=UiUtils.alphaBlendColors(UiUtils.getThemeColor(context, R.attr.toolbarBackground), UiUtils.getThemeColor(context, R.attr.colorWindowBackground), 0.07843137f);
|
||||||
|
}
|
||||||
|
anims.add(ObjectAnimator.ofArgb(fragmentRootLayout, "statusBarColor", color));
|
||||||
|
}
|
||||||
|
set.playTogether(anims);
|
||||||
|
set.setDuration(150);
|
||||||
|
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
|
set.addListener(new AnimatorListenerAdapter(){
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation){
|
||||||
|
currentPanelsAnim=null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
set.start();
|
||||||
|
currentPanelsAnim=set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#D49E050A"/>
|
||||||
|
<corners android:radius="20dp"/>
|
||||||
|
</shape>
|
|
@ -1,9 +1,9 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item>
|
<item>
|
||||||
<color android:color="?colorM3Background"/>
|
<color android:color="?colorWindowBackground"/>
|
||||||
</item>
|
</item>
|
||||||
<item android:id="@+id/color_overlay">
|
<item android:id="@+id/color_overlay">
|
||||||
<color android:color="?colorM3Primary"/>
|
<color android:color="?toolbarBackground"/>
|
||||||
</item>
|
</item>
|
||||||
</layer-list>
|
</layer-list>
|
|
@ -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="M10.5 5c0 0.641 0.402 1.188 0.968 1.403 0.35 0.085 0.714 0.085 1.063 0C13.098 6.19 13.5 5.641 13.5 5c0-0.828-0.672-1.5-1.5-1.5-0.829 0-1.5 0.672-1.5 1.5zM9.026 5.399C9.01 5.269 9 5.135 9 5c0-1.657 1.343-3 3-3s3 1.343 3 3c0 0.135-0.009 0.268-0.026 0.399l2.876-1.221c1.143-0.485 2.468 0.044 2.962 1.184 0.495 1.143-0.034 2.467-1.181 2.954l-3.628 1.54v3.717l1.874 5.444c0.404 1.175-0.22 2.455-1.395 2.86-1.175 0.404-2.455-0.22-2.86-1.395L12 15.772l-1.622 4.71c-0.404 1.175-1.685 1.799-2.86 1.394-1.175-0.404-1.8-1.685-1.394-2.86l1.878-5.456V9.857L4.37 8.316c-1.147-0.487-1.676-1.81-1.18-2.954 0.493-1.14 1.818-1.67 2.96-1.184l2.877 1.22zm1.996 2.438c-0.165-0.045-0.328-0.1-0.487-0.168l-4.971-2.11c-0.385-0.164-0.833 0.016-1 0.399-0.163 0.38 0.011 0.816 0.392 0.977L8.74 8.542c0.462 0.196 0.761 0.649 0.761 1.15v3.91c0 0.138-0.023 0.275-0.068 0.406l-1.892 5.497c-0.135 0.392 0.073 0.818 0.465 0.953 0.391 0.135 0.818-0.073 0.953-0.465l2.108-6.123c0.306-0.888 1.56-0.884 1.864 0l2.108 6.123c0.135 0.392 0.562 0.6 0.954 0.465 0.391-0.134 0.6-0.561 0.465-0.953l-1.888-5.483c-0.046-0.131-0.069-0.268-0.069-0.407V9.69c0-0.501 0.3-0.954 0.762-1.15l3.78-1.605c0.381-0.161 0.555-0.598 0.391-0.977-0.166-0.383-0.614-0.563-0.999-0.4l-4.97 2.11c-0.16 0.068-0.323 0.125-0.489 0.17C12.671 7.942 12.342 8 12 8c-0.342 0-0.671-0.057-0.978-0.163z" 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="M11.75 3c0.38 0 0.693 0.282 0.743 0.648L12.5 3.75 12.501 11h7.253c0.415 0 0.75 0.336 0.75 0.75 0 0.38-0.282 0.694-0.648 0.743L19.754 12.5h-7.253l0.002 7.25c0 0.413-0.335 0.75-0.75 0.75-0.38 0-0.693-0.283-0.743-0.649l-0.007-0.102-0.002-7.249H3.752c-0.414 0-0.75-0.336-0.75-0.75 0-0.38 0.282-0.694 0.648-0.743L3.752 11h7.25L11 3.75C11 3.336 11.336 3 11.75 3z" 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="M15.492 3.507c-0.84 0.075-1.499 0.782-1.499 1.643V10c0 0.414-0.335 0.75-0.75 0.75-1.443 0-2.457 0.588-3.207 1.488-0.772 0.928-1.275 2.206-1.59 3.557-0.313 1.343-0.427 2.696-0.462 3.722C7.973 19.9 7.97 20.232 7.974 20.5h7.019v-0.75c0-1.243-1.007-2.25-2.249-2.25h-1.25c-0.413 0-0.749-0.336-0.749-0.75S11.081 16 11.495 16h1.249c2.07 0 3.748 1.679 3.748 3.75v0.75h0.75c0.413 0 0.75-0.336 0.75-0.75v-10c0-0.414 0.335-0.75 0.749-0.75h0.506c0.98 0 1.578-1.076 1.062-1.909l-0.62-1C19.462 5.724 19.06 5.5 18.628 5.5h-2.386c-0.414 0-0.75-0.336-0.75-0.75V3.507zM6.474 20.5c-0.003-0.284-0.001-0.634 0.012-1.033 0.036-1.083 0.157-2.542 0.5-4.012 0.34-1.462 0.915-2.996 1.899-4.177 0.872-1.047 2.055-1.801 3.61-1.985V5.15c0-1.74 1.409-3.15 3.147-3.15 0.746 0 1.35 0.604 1.35 1.35V4h1.636c0.95 0 1.834 0.492 2.335 1.3l0.62 1c1.092 1.763-0.084 4.02-2.093 4.19v9.26c0 1.243-1.006 2.25-2.248 2.25H5.795C3.7 22 2 20.3 2 18.202c0-0.963 0.365-1.889 1.021-2.592l1.135-1.217c0.764-0.819 1.02-1.99 0.665-3.053-0.15-0.45-0.403-0.86-0.738-1.195L2.969 9.03c-0.293-0.292-0.293-0.767 0-1.06 0.293-0.293 0.767-0.293 1.06 0l1.114 1.114c0.5 0.5 0.877 1.11 1.1 1.782 0.528 1.584 0.148 3.33-0.99 4.55l-1.136 1.217C3.72 17.06 3.499 17.62 3.499 18.202c0 1.269 1.029 2.298 2.297 2.298h0.678z" 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="M13.25 2h3.185c0.509 0 1.007 0.141 1.44 0.408l2.768 1.703C20.865 4.248 21 4.49 21 4.75v1.5c0 1.243-1.007 2.25-2.25 2.25H18.5v9.047c0.997 0.134 1.695 0.555 2.095 1.275 0.223 0.403 0.318 0.84 0.362 1.243C21 20.456 21 20.865 21 21.225v0.025c0 0.414-0.336 0.75-0.75 0.75H5.833C3.716 22 2 20.284 2 18.167c0-1.398 0.749-2.62 1.864-3.29 0.355-0.213 0.816-0.097 1.029 0.258 0.213 0.355 0.098 0.816-0.258 1.029C3.954 16.573 3.5 17.317 3.5 18.167c0 1.288 1.045 2.333 2.333 2.333 0.319 0 0.517-0.083 0.649-0.176 0.139-0.098 0.248-0.238 0.332-0.407 0.084-0.17 0.133-0.35 0.16-0.493 0.013-0.07 0.02-0.126 0.023-0.162L7 19.227v-0.035l0.004-0.143c0.005-0.122 0.013-0.296 0.03-0.51 0.032-0.426 0.098-1.017 0.23-1.676 0.26-1.292 0.796-2.968 1.952-4.14 0.848-0.86 1.309-2.119 1.547-3.364C11 8.13 11 7.008 11 6.5V4.25C11 3.008 12.006 2 13.25 2zm-5.5 17.25l0.75 0.026v0.006l-0.001 0.01-0.002 0.028-0.007 0.09c-0.007 0.073-0.02 0.173-0.042 0.29-0.04 0.213-0.113 0.5-0.251 0.8h7.25c-0.09-0.494-0.34-1.006-1.013-1.32L14.4 19.164c-0.052-0.028-0.202-0.077-0.45-0.116-0.23-0.036-0.484-0.055-0.701-0.055l-0.283 0.004-0.141 0.002h-0.06c-0.415 0.009-0.757-0.321-0.764-0.735-0.008-0.414 0.322-0.756 0.736-0.764H12.8l0.143-0.002 0.307-0.004c0.235 0 0.495 0.016 0.75 0.047v-2.287c0-0.414 0.336-0.75 0.75-0.75s0.75 0.336 0.75 0.75v2.804c1.065 0.682 1.374 1.703 1.463 2.443h2.524c-0.004-0.093-0.011-0.183-0.021-0.27-0.033-0.301-0.094-0.52-0.183-0.68C19.153 19.316 18.85 19 17.75 19 17.336 19 17 18.664 17 18.25V7.75C17 7.336 17.336 7 17.75 7h1c0.414 0 0.75-0.336 0.75-0.75V5.17l-2.411-1.485C16.892 3.564 16.666 3.5 16.435 3.5h-3.186c-0.414 0-0.749 0.335-0.749 0.75v2.232c0.013 0.228 0.092 0.425 0.205 0.554 0.101 0.115 0.26 0.214 0.545 0.214 0.287 0 0.453-0.101 0.556-0.219C13.92 6.9 14 6.696 14 6.45c0-0.414 0.336-0.75 0.75-0.75s0.75 0.336 0.75 0.75c0 0.554-0.177 1.125-0.566 1.569-0.4 0.457-0.984 0.731-1.684 0.731-0.312 0-0.599-0.054-0.855-0.154-0.038 0.332-0.09 0.684-0.158 1.045-0.262 1.366-0.801 2.968-1.953 4.136-0.845 0.856-1.308 2.18-1.549 3.38-0.117 0.588-0.176 1.117-0.206 1.498-0.015 0.19-0.022 0.343-0.025 0.446L8.5 19.217v0.059L7.75 19.25zm8.5 2.75v-0.75z" 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="M7.75 19c-1.463 0-2.658-1.141-2.745-2.582L5 16.25V15c-1.657 0-3-1.344-3-3 0-1.657 1.343-3 3-3 1.074 0 2.031 0.57 2.562 1.44 0.512-0.247 1.08-0.398 1.68-0.432L9.5 10h4l0.248 0.007 0.245 0.02 0.127 0.017 0.11-0.167 0.131-0.178-1.777-1.777c-0.7-0.7-0.773-1.788-0.22-2.57l0.103-0.134 0.117-0.128c0.74-0.74 1.918-0.78 2.705-0.117l0.127 0.117 5.412 5.413c1.563 1.562 1.563 4.095 0 5.657-0.72 0.72-1.665 1.122-2.642 1.167l-0.225 0.004-0.182-0.007-0.026 0.064c-0.386 0.85-1.189 1.462-2.138 1.588l-0.192 0.019L15.249 19H7.75zm6.605-12.849c-0.196-0.196-0.514-0.196-0.71 0-0.175 0.175-0.194 0.445-0.058 0.641l0.058 0.07 2.808 2.808-0.638 0.642c-0.21 0.21-0.385 0.444-0.523 0.696L15.195 11.2l-0.266 0.584-0.618-0.173c-0.196-0.055-0.398-0.09-0.604-0.104L13.499 11.5H9.5c-0.7 0-1.343 0.24-1.853 0.64l-0.143 0.12-0.165 0.16-0.093 0.1-0.111 0.134-0.121 0.167-0.042 0.064c-0.041 0.063-0.08 0.129-0.115 0.196l-0.058 0.112-0.062 0.137-0.034 0.084-0.051 0.142-0.055 0.184c-0.025 0.097-0.046 0.197-0.062 0.297L6.524 14.12l-0.018 0.19L6.5 14.5v1.75c0 0.648 0.492 1.18 1.122 1.244L7.75 17.5h4.251v-0.246c0-0.648-0.491-1.18-1.121-1.244l-0.128-0.006h-1c-0.414 0-0.75-0.336-0.75-0.75 0-0.38 0.282-0.694 0.648-0.743l0.102-0.007h1c1.462 0 2.658 1.141 2.745 2.582l0.005 0.168L13.5 17.5h1.748c0.592 0 1.095-0.414 1.219-0.975l0.02-0.122 0.1-0.84 0.822 0.198c0.842 0.204 1.734-0.038 2.358-0.662 0.934-0.934 0.974-2.423 0.122-3.405l-0.122-0.13-5.413-5.413zM5 10.501C4.17 10.5 3.5 11.17 3.5 12c0 0.78 0.595 1.42 1.355 1.492L5 13.5l0.115-0.005 0.005-0.025c0.035-0.15 0.077-0.295 0.126-0.438l0.097-0.255 0.106-0.236 0.057-0.113 0.13-0.234 0.088-0.14 0.145-0.21 0.074-0.098 0.108-0.134 0.129-0.147 0.15-0.157c-0.21-0.4-0.59-0.69-1.037-0.778l-0.151-0.022L5 10.5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M10.997 5.998c2.623 0 4.946 1.665 5.8 4.126l0.075 0.233 0.044 0.144h2.33c1.462 0 2.658 1.142 2.744 2.582l0.005 0.167v1c0 0.917-0.707 1.67-1.606 1.743l-0.143 0.005H18.62l0.241 0.584c0.35 0.846-0.015 1.808-0.813 2.22l-0.137 0.064c-0.159 0.065-0.326 0.107-0.496 0.124l-0.171 0.008h-1.787c-0.625 0-1.199-0.333-1.51-0.867l-0.072-0.137-0.539-1.143 0.054-0.007c-1.4 0.186-2.817 0.208-4.221 0.066l-0.497-0.057-0.535 1.136c-0.289 0.614-0.905 1.005-1.583 1.005H4.75c-0.229 0-0.455-0.045-0.667-0.132-0.893-0.367-1.319-1.39-0.951-2.283l0.433-1.05c-0.94-0.569-1.57-1.6-1.57-2.78 0-0.38 0.282-0.693 0.648-0.742L2.745 12h1.88l0.497-1.643c0.784-2.588 3.17-4.359 5.875-4.359zm6.777 9.694c-0.771 0.31-1.559 0.564-2.356 0.764l-0.549 0.129 0.362 0.77c0.025 0.053 0.067 0.094 0.117 0.119l0.053 0.018 0.056 0.007h1.787c0.033 0 0.065-0.007 0.095-0.02 0.107-0.043 0.166-0.152 0.153-0.26l-0.017-0.065-0.478-1.156h-0.043l0.411-0.148 0.409-0.159zm-13.552 0l0.39 0.151L5 15.984 4.518 17.15c-0.052 0.127 0.009 0.273 0.136 0.326 0.03 0.012 0.063 0.019 0.096 0.019h1.804l0.057-0.007c0.073-0.017 0.136-0.067 0.17-0.137l0.359-0.763 0.044 0.01c-1.005-0.216-1.996-0.518-2.962-0.906zm6.775-8.195c-1.971 0-3.718 1.245-4.371 3.087l-0.069 0.207-1.135 3.75 0.163 0.059c3.372 1.158 7.025 1.202 10.42 0.133L16.41 14.6l0.162-0.059-1.136-3.75c-0.55-1.816-2.145-3.1-4.006-3.274l-0.216-0.015-0.218-0.005zm-6.977 6.5l0.151-0.5L3.664 13.5l0.026 0.05c0.086 0.166 0.198 0.316 0.33 0.446zM17.37 12l0.756 2.498 2.12 0.001c0.118 0 0.217-0.082 0.243-0.192l0.007-0.058V13.25c0-0.647-0.492-1.179-1.122-1.243l-0.128-0.006H17.37z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M14.723 16.221c-0.293 0.293-0.293 0.768 0 1.06 0.293 0.294 0.768 0.293 1.061 0l4.997-5.003c0.292-0.293 0.292-0.768 0-1.06L15.783 6.22c-0.293-0.293-0.768-0.293-1.06 0-0.294 0.293-0.294 0.767-0.001 1.06l3.72 3.72H10.6c-1.595 0-2.81 0.242-3.889 0.764L6.466 11.89c-1.109 0.593-1.983 1.467-2.576 2.576C3.28 15.606 3 16.884 3 18.6c0 0.414 0.336 0.75 0.75 0.75S4.5 19.014 4.5 18.6c0-1.484 0.228-2.52 0.713-3.428 0.453-0.847 1.113-1.507 1.96-1.96 0.838-0.448 1.786-0.676 3.094-0.709L10.6 12.5h7.837l-3.715 3.721z" 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="M16.25 5.18c-0.25 0.33-0.187 0.8 0.142 1.051C18.18 7.595 19.25 9.708 19.25 12c0 3.736-2.826 6.812-6.457 7.207l0.677-0.677c0.293-0.293 0.293-0.767 0-1.06-0.267-0.267-0.683-0.29-0.977-0.073L12.41 17.47l-2 2c-0.266 0.266-0.29 0.683-0.073 0.976l0.073 0.084 2 2c0.293 0.293 0.768 0.293 1.06 0 0.267-0.266 0.291-0.683 0.073-0.976L13.47 21.47l-0.75-0.75c4.495-0.365 8.03-4.13 8.03-8.72 0-2.765-1.292-5.317-3.448-6.961-0.33-0.252-0.8-0.188-1.051 0.141zm-5.72-3.71c-0.293 0.293-0.293 0.767 0 1.06l0.75 0.75C6.784 3.645 3.25 7.41 3.25 12c0 2.645 1.181 5.097 3.18 6.75 0.32 0.263 0.793 0.218 1.057-0.102 0.263-0.319 0.218-0.792-0.1-1.055-1.66-1.37-2.637-3.4-2.637-5.593 0-3.736 2.825-6.811 6.456-7.207L10.53 5.47c-0.293 0.293-0.293 0.767 0 1.06 0.293 0.293 0.768 0.293 1.061 0l2-2c0.293-0.293 0.293-0.767 0-1.06l-2-2c-0.293-0.293-0.768-0.293-1.06 0z" 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="M10.417 8C9.082 8 8 9.082 8 10.417 8 11.29 8.709 12 9.583 12h4.834C15.29 12 16 11.291 16 10.417 16 9.082 14.918 8 13.583 8h-3.166zM9.5 10.417C9.5 9.91 9.91 9.5 10.417 9.5h3.166c0.507 0 0.917 0.41 0.917 0.917 0 0.046-0.037 0.083-0.083 0.083H9.583c-0.046 0-0.083-0.037-0.083-0.083zM12 2c-1.961 0-3.57 1.506-3.736 3.424C5.728 6.766 4 9.431 4 12.5v6.25C4 20.545 5.455 22 7.25 22h9.5c1.795 0 3.25-1.455 3.25-3.25V12.5c0-3.069-1.728-5.734-4.264-7.076C15.571 3.506 13.961 2 12 2zm6.5 12h-13v-1.5C5.5 8.91 8.41 6 12 6s6.5 2.91 6.5 6.5V14zM8 17.25C8 17.664 8.336 18 8.75 18s0.75-0.336 0.75-0.75v-1.536h9v3.036c0 0.966-0.784 1.75-1.75 1.75h-9.5c-0.966 0-1.75-0.784-1.75-1.75v-3.036H8v1.536zM12 4.5c-0.698 0-1.374 0.09-2.02 0.257C10.347 4.012 11.114 3.5 12 3.5c0.886 0 1.653 0.512 2.02 1.257C13.374 4.59 12.698 4.5 12 4.5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M12.018 5.665c0.095-0.404 0.498-0.654 0.902-0.559 1.468 0.346 2.623 1.499 2.972 2.966 0.095 0.403-0.154 0.807-0.557 0.903-0.403 0.096-0.807-0.153-0.903-0.556-0.217-0.915-0.94-1.637-1.856-1.853-0.403-0.095-0.653-0.498-0.558-0.901zM5 9c0-3.866 3.134-7 7-7s7 3.134 7 7c0 2.025-0.67 4.236-1.85 5.956-1.042 1.52-2.543 2.731-4.4 2.992v0.302c0 0.414 0.336 0.75 0.75 0.75h3.25c1.243 0 2.25 1.007 2.25 2.25v1c0 0.414-0.336 0.75-0.75 0.75s-0.75-0.336-0.75-0.75v-1c0-0.414-0.336-0.75-0.75-0.75H13.5c-1.243 0-2.25-1.007-2.25-2.25v-0.302c-1.857-0.261-3.357-1.473-4.4-2.992C5.67 13.236 5 11.025 5 9zm7-5.5C8.962 3.5 6.5 5.962 6.5 9c0 1.725 0.58 3.64 1.586 5.107C9.094 15.575 10.453 16.5 12 16.5c1.547 0 2.906-0.925 3.914-2.393C16.92 12.639 17.5 10.725 17.5 9c0-3.038-2.462-5.5-5.5-5.5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M12 19.137C11.499 19.67 10.788 20 10 20H3.75C2.784 20 2 19.216 2 18.25V5.75C2 4.784 2.784 4 3.75 4H10c0.788 0 1.499 0.331 2 0.862C12.501 4.332 13.212 4 14 4h6.25C21.216 4 22 4.784 22 5.75v12.5c0 0.966-0.784 1.75-1.75 1.75H14c-0.788 0-1.499-0.331-2-0.863zM3.5 5.75v12.5c0 0.138 0.112 0.25 0.25 0.25H10c0.69 0 1.25-0.56 1.25-1.25V6.75c0-0.69-0.56-1.25-1.25-1.25H3.75C3.612 5.5 3.5 5.612 3.5 5.75zm9.25 11.5c0 0.69 0.56 1.25 1.25 1.25h6.25c0.138 0 0.25-0.112 0.25-0.25V5.75c0-0.138-0.112-0.25-0.25-0.25H14c-0.69 0-1.25 0.56-1.25 1.25v10.5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M17.753 14c1.243 0 2.25 1.007 2.25 2.25v0.904c0 1.094-0.477 2.133-1.307 2.846-1.565 1.344-3.806 2-6.696 2-2.89 0-5.128-0.656-6.69-2-0.83-0.712-1.306-1.75-1.306-2.843v-0.908c0-1.242 1.007-2.25 2.25-2.25h11.5zm0 1.5h-11.5c-0.413 0-0.75 0.335-0.75 0.75v0.907c0 0.655 0.287 1.278 0.784 1.706C7.545 19.945 9.441 20.5 12 20.5c2.56 0 4.458-0.557 5.72-1.64 0.497-0.427 0.783-1.05 0.783-1.707V16.25c0-0.414-0.335-0.75-0.75-0.75zM11.9 2.006L12 2c0.38 0 0.693 0.282 0.743 0.648L12.75 2.75v0.749h3.5c1.243 0 2.25 1.008 2.25 2.25v4.505c0 1.243-1.007 2.25-2.25 2.25h-8.5c-1.242 0-2.25-1.007-2.25-2.25V5.75c0-1.243 1.008-2.25 2.25-2.25l3.5-0.001V2.75c0-0.38 0.283-0.693 0.649-0.743L12 2l-0.102 0.007zM16.25 5h-8.5C7.336 5 7 5.335 7 5.75v4.504c0 0.414 0.336 0.75 0.75 0.75h8.5c0.415 0 0.75-0.336 0.75-0.75V5.75C17 5.335 16.665 5 16.25 5zm-6.5 1.5c0.69 0 1.249 0.559 1.249 1.249 0 0.69-0.56 1.25-1.25 1.25S8.5 8.438 8.5 7.748s0.56-1.25 1.25-1.25zm4.492 0c0.69 0 1.25 0.559 1.25 1.249 0 0.69-0.56 1.25-1.25 1.25s-1.249-0.56-1.249-1.25 0.56-1.25 1.25-1.25z" 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="M8.75 3h6.5c0.38 0 0.694 0.282 0.743 0.648L16 3.75V7h1.75C19.545 7 21 8.455 21 10.25v6.5c0 1.795-1.455 3.25-3.25 3.25H6.25C4.455 20 3 18.545 3 16.75v-6.5C3 8.455 4.455 7 6.25 7H8V3.75c0-0.38 0.282-0.693 0.648-0.743L8.75 3h6.5-6.5zm9 5.5H6.25c-0.966 0-1.75 0.784-1.75 1.75v6.5c0 0.966 0.784 1.75 1.75 1.75h11.5c0.966 0 1.75-0.784 1.75-1.75v-6.5c0-0.966-0.784-1.75-1.75-1.75zm-3.25-4h-5V7h5V4.5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M10.5 2.752c0-0.414-0.336-0.75-0.75-0.75S9 2.338 9 2.752v0.752c0 0.632 0.196 1.22 0.53 1.703-1.393 0.48-2.415 1.755-2.52 3.284H6.752c-1.242-0.005-2.246-1.017-2.24-2.26l0.006-1.485C4.52 4.332 4.186 3.995 3.772 3.993 3.358 3.99 3.02 4.326 3.019 4.74L3.013 6.225c-0.01 2.072 1.663 3.757 3.734 3.766H7v1.51H2.75C2.336 11.5 2 11.836 2 12.25 2 12.665 2.336 13 2.75 13H7v1.992H6.747C4.676 15 3.004 16.687 3.013 18.758l0.006 1.486c0.001 0.414 0.338 0.749 0.753 0.747 0.414-0.002 0.748-0.339 0.747-0.753l-0.006-1.486c-0.006-1.242 0.998-2.254 2.24-2.26h0.473c0.635 2.034 2.532 3.51 4.774 3.51s4.14-1.476 4.774-3.51h0.473c1.242 0.006 2.246 1.018 2.24 2.26l-0.006 1.486c-0.001 0.414 0.333 0.751 0.747 0.753 0.414 0.002 0.752-0.333 0.753-0.747l0.006-1.486c0.01-2.07-1.663-3.756-3.734-3.765H17V13h4.251c0.415 0 0.75-0.336 0.75-0.75 0-0.415-0.335-0.75-0.75-0.75H17V9.99h0.253c2.071-0.009 3.743-1.694 3.735-3.766L20.98 4.74c-0.001-0.414-0.339-0.749-0.753-0.747-0.414 0.002-0.748 0.339-0.747 0.753l0.006 1.486c0.006 1.242-0.997 2.254-2.24 2.259H16.99c-0.106-1.529-1.128-2.805-2.52-3.284C14.803 4.723 15 4.137 15 3.504V2.752c0-0.414-0.336-0.75-0.75-0.75s-0.75 0.336-0.75 0.75v0.752c0 0.828-0.672 1.5-1.5 1.5s-1.5-0.671-1.5-1.5V2.752zm-2 6.002c0-1.243 1.007-2.25 2.25-2.25h2.5c1.243 0 2.25 1.007 2.25 2.25v6.247c0 1.933-1.567 3.5-3.5 3.5s-3.5-1.567-3.5-3.5V8.754z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10c-1.64 0-3.225-0.396-4.644-1.142l-4.29 1.117c-0.455 0.119-0.92-0.154-1.037-0.608-0.037-0.14-0.037-0.288 0-0.428l1.116-4.289C2.397 15.23 2 13.643 2 12 2 6.477 6.477 2 12 2zm1.252 11H8.75l-0.102 0.007C8.282 13.057 8 13.37 8 13.75s0.282 0.694 0.648 0.743L8.75 14.5h4.502l0.101-0.007c0.367-0.05 0.649-0.363 0.649-0.743s-0.282-0.694-0.649-0.743L13.252 13zm1.998-3.5h-6.5L8.648 9.507C8.282 9.557 8 9.87 8 10.25s0.282 0.694 0.648 0.743L8.75 11h6.5l0.102-0.007C15.718 10.943 16 10.63 16 10.25s-0.282-0.694-0.648-0.743L15.25 9.5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10c-1.618 0-3.182-0.385-4.587-1.112l-3.826 1.067c-0.665 0.186-1.354-0.202-1.54-0.867-0.062-0.22-0.062-0.453 0-0.673l1.068-3.823C2.386 15.186 2 13.62 2 12 2 6.477 6.477 2 12 2zm0 1.5c-4.694 0-8.5 3.806-8.5 8.5 0 1.47 0.373 2.883 1.073 4.137l0.15 0.27-1.112 3.984 3.986-1.112 0.27 0.15C9.12 20.13 10.532 20.5 12 20.5c4.694 0 8.5-3.806 8.5-8.5S16.694 3.5 12 3.5zM8.75 13h4.498c0.415 0 0.75 0.336 0.75 0.75 0 0.38-0.282 0.694-0.648 0.743L13.248 14.5H8.75C8.336 14.5 8 14.164 8 13.75c0-0.38 0.282-0.694 0.648-0.743L8.75 13h4.498H8.75zm0-3.5h6.505c0.414 0 0.75 0.336 0.75 0.75 0 0.38-0.283 0.694-0.649 0.743L15.255 11H8.75C8.336 11 8 10.664 8 10.25c0-0.38 0.282-0.694 0.648-0.743L8.75 9.5h6.505H8.75z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M12 1.996c0.414 0 0.75 0.336 0.75 0.75V4h2c0.966 0 1.75 0.784 1.75 1.75V11h2.25c0.966 0 1.75 0.784 1.75 1.75v7.5c0 0.967-0.784 1.75-1.75 1.75H5.25c-0.966 0-1.75-0.783-1.75-1.75v-8.43c0-0.619 0.327-1.192 0.86-1.507L7.5 8.458V5.749C7.5 4.783 8.284 4 9.25 4h2V2.746c0-0.414 0.336-0.75 0.75-0.75zM9 8.018c0.81 0.12 1.5 0.806 1.5 1.734v10.747h3v-7.75c0-0.881 0.652-1.61 1.5-1.732V5.75c0-0.138-0.112-0.25-0.25-0.25h-5.5C9.112 5.5 9 5.611 9 5.75v2.269zm6.25 4.481c-0.138 0-0.25 0.112-0.25 0.25v7.75h3.75c0.138 0 0.25-0.111 0.25-0.25v-7.5c0-0.138-0.112-0.25-0.25-0.25h-3.5zM8.623 9.537l-3.5 2.068C5.047 11.65 5 11.732 5 11.82v8.43c0 0.137 0.112 0.25 0.25 0.25H9V9.751c0-0.194-0.21-0.314-0.377-0.215z" 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="M8.066 18.943l6.5-14.5c0.17-0.378 0.613-0.547 0.99-0.377 0.347 0.155 0.518 0.54 0.414 0.895l-0.036 0.096-6.5 14.5c-0.17 0.378-0.613 0.547-0.99 0.378-0.347-0.156-0.518-0.542-0.414-0.896l0.036-0.096 6.5-14.5-6.5 14.5zM2.22 11.47l4.25-4.25c0.293-0.293 0.767-0.293 1.06 0 0.267 0.266 0.29 0.683 0.073 0.976L7.53 8.281 3.81 12l3.72 3.719c0.293 0.293 0.293 0.768 0 1.06-0.266 0.267-0.683 0.291-0.976 0.073L6.47 16.78l-4.25-4.25c-0.267-0.266-0.29-0.682-0.073-0.976L2.22 11.47l4.25-4.25-4.25 4.25zm14.25-4.25c0.266-0.266 0.683-0.29 0.976-0.073L17.53 7.22l4.25 4.25c0.267 0.266 0.29 0.683 0.073 0.976l-0.073 0.085-4.25 4.25c-0.293 0.292-0.767 0.292-1.06 0-0.267-0.267-0.29-0.683-0.073-0.977l0.073-0.084L20.19 12l-3.72-3.72c-0.293-0.292-0.293-0.767 0-1.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="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M18.5 20c0 0.275-0.224 0.5-0.5 0.5H6c-0.276 0-0.5-0.225-0.5-0.5V4c0-0.275 0.224-0.5 0.5-0.5h6V8c0 1.104 0.896 2 2 2h4.5v10zm-5-15.379L17.378 8.5H14c-0.276 0-0.5-0.225-0.5-0.5V4.621zm5.914 3.793l-5.829-5.828C13.559 2.56 13.527 2.54 13.5 2.516c-0.071-0.064-0.141-0.127-0.219-0.18-0.04-0.027-0.086-0.045-0.128-0.068-0.071-0.04-0.141-0.084-0.216-0.116-0.197-0.082-0.409-0.123-0.624-0.138C12.266 2.011 12.22 2 12.172 2H6C4.896 2 4 2.896 4 4v16c0 1.104 0.896 2 2 2h12c1.104 0 2-0.896 2-2V9.828c0-0.53-0.211-1.039-0.586-1.414z" 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="M5.5 2.75C5.5 2.336 5.164 2 4.75 2S4 2.336 4 2.75c0 1.27 0.928 1.917 1.506 2.32L5.57 5.114C6.25 5.59 6.5 5.82 6.5 6.25 6.5 6.664 6.836 7 7.25 7S8 6.664 8 6.25c0-1.27-0.928-1.917-1.506-2.32L6.43 3.886C5.75 3.41 5.5 3.18 5.5 2.75zM3 9.821C3 8.816 3.816 8 4.821 8H17.18C18.185 8 19 8.816 19 9.821V10.5h0.75c1.795 0 3.25 1.455 3.25 3.25S21.545 17 19.75 17h-1.331c-1.187 2.932-4.062 5-7.419 5-4.418 0-8-3.582-8-8V9.821zm14.5 0c0-0.177-0.144-0.321-0.321-0.321H4.82C4.644 9.5 4.5 9.644 4.5 9.821V14c0 3.59 2.91 6.5 6.5 6.5s6.5-2.91 6.5-6.5V9.821zM19.75 12H19v2c0 0.513-0.048 1.014-0.14 1.5h0.89c0.966 0 1.75-0.784 1.75-1.75S20.716 12 19.75 12zm-11-10C9.164 2 9.5 2.336 9.5 2.75c0 0.431 0.25 0.66 0.93 1.136l0.064 0.044C11.072 4.333 12 4.98 12 6.25 12 6.664 11.664 7 11.25 7S10.5 6.664 10.5 6.25c0-0.431-0.25-0.66-0.93-1.136L9.506 5.07C8.928 4.667 8 4.02 8 2.75 8 2.336 8.336 2 8.75 2zm4.75 0.75C13.5 2.336 13.164 2 12.75 2S12 2.336 12 2.75c0 1.27 0.928 1.917 1.506 2.32l0.064 0.044C14.25 5.59 14.5 5.82 14.5 6.25 14.5 6.664 14.836 7 15.25 7S16 6.664 16 6.25c0-1.27-0.928-1.917-1.506-2.32L14.43 3.886C13.75 3.41 13.5 3.18 13.5 2.75z" 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="M6.75 12c-0.212 0-0.412 0.089-0.555 0.244-0.142 0.156-0.212 0.364-0.193 0.574C6.282 15.892 8.552 18.5 12 18.5c3.448 0 5.717-2.608 5.998-5.682 0.019-0.21-0.051-0.418-0.193-0.574C17.663 12.09 17.462 12 17.25 12H6.749zM12 17c-2.257 0-3.871-1.48-4.368-3.5h8.736C15.872 15.52 14.258 17 12 17zm3.25-8.25c-0.408 0-0.71 0.287-0.758 0.61-0.06 0.41-0.442 0.693-0.852 0.632-0.41-0.06-0.693-0.442-0.632-0.852 0.162-1.09 1.123-1.89 2.242-1.89s2.08 0.8 2.242 1.89c0.06 0.41-0.222 0.791-0.632 0.852-0.41 0.06-0.791-0.222-0.852-0.632-0.048-0.323-0.35-0.61-0.758-0.61zM7.992 9.36C8.04 9.037 8.342 8.75 8.75 8.75c0.409 0 0.71 0.287 0.758 0.61 0.06 0.41 0.442 0.693 0.852 0.632 0.41-0.06 0.693-0.442 0.632-0.852-0.161-1.09-1.123-1.89-2.242-1.89s-2.08 0.8-2.242 1.89C6.448 9.55 6.731 9.931 7.14 9.992c0.41 0.06 0.791-0.222 0.852-0.632zM12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zM3.5 12c0-4.694 3.806-8.5 8.5-8.5s8.5 3.806 8.5 8.5-3.806 8.5-8.5 8.5-8.5-3.806-8.5-8.5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
|
||||||
|
<path android:pathData="M3.26 11.602C3.942 8.327 6.793 6 10 6c3.206 0 6.057 2.327 6.74 5.602 0.057 0.27 0.322 0.444 0.593 0.387 0.27-0.056 0.443-0.32 0.387-0.591C16.943 7.673 13.693 5 10 5c-3.693 0-6.943 2.673-7.72 6.398-0.056 0.27 0.117 0.535 0.388 0.591 0.27 0.057 0.535-0.117 0.591-0.387zM10 8c-1.933 0-3.5 1.567-3.5 3.5S8.067 15 10 15s3.5-1.567 3.5-3.5S11.933 8 10 8zm-2.5 3.5C7.5 10.12 8.62 9 10 9s2.5 1.12 2.5 2.5S11.38 14 10 14s-2.5-1.12-2.5-2.5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M12 9.005c2.209 0 4 1.79 4 4 0 2.209-1.791 4-4 4-2.21 0-4-1.791-4-4 0-2.21 1.79-4 4-4zm0 1.5c-1.38 0-2.5 1.119-2.5 2.5 0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5c0-1.381-1.12-2.5-2.5-2.5zM12 5.5c4.613 0 8.596 3.15 9.701 7.564 0.1 0.402-0.144 0.81-0.545 0.91-0.402 0.1-0.81-0.143-0.91-0.545C19.307 9.678 15.92 7 12 7c-3.923 0-7.31 2.68-8.247 6.433-0.1 0.402-0.508 0.646-0.91 0.546-0.401-0.1-0.646-0.507-0.546-0.91C3.4 8.654 7.384 5.5 12 5.5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M12.538 4.295c0.323-0.244 0.644-0.431 0.93-0.563 0.088 2.127 1.127 3.726 2.129 5.14l0.272 0.382c1.083 1.518 2.011 2.819 2.112 4.54 0.117 1.982-0.377 3.657-1.336 4.824C15.7 19.77 14.24 20.5 12.25 20.5c-2.061 0-3.61-0.529-4.691-1.416-1.077-0.883-1.764-2.181-1.994-3.872-0.226-1.663 0.176-2.845 0.611-3.603 0.086-0.149 0.173-0.282 0.258-0.4l0.315 0.598c0.551 1.046 1.83 1.472 2.898 0.965 1.306-0.62 1.584-2.213 1.17-3.33-0.29-0.783-0.445-1.737-0.11-2.694 0.377-1.072 1.091-1.896 1.831-2.454zM6.156 9.322L6.154 9.325 6.15 9.328 6.14 9.336 6.11 9.36C6.084 9.38 6.052 9.406 6.013 9.44c-0.076 0.067-0.18 0.164-0.301 0.29-0.24 0.253-0.548 0.629-0.837 1.132-0.582 1.015-1.071 2.528-0.796 4.551 0.271 1.997 1.11 3.666 2.528 4.83C8.021 21.404 9.935 22 12.25 22c2.387 0 4.293-0.895 5.554-2.43 1.25-1.521 1.808-3.596 1.675-5.864-0.128-2.176-1.313-3.827-2.36-5.285L16.82 8.004c-1.142-1.612-2.043-3.097-1.824-5.175 0.022-0.212-0.046-0.423-0.189-0.58C14.665 2.09 14.463 2 14.25 2c-0.382 0-0.82 0.118-1.242 0.296-0.436 0.183-0.91 0.452-1.373 0.8-0.925 0.698-1.85 1.75-2.343 3.156C8.8 7.654 9.05 8.99 9.41 9.963c0.237 0.639-0.02 1.27-0.406 1.454-0.342 0.162-0.752 0.026-0.928-0.31L7.27 9.576C7.167 9.38 6.985 9.24 6.77 9.193c-0.214-0.049-0.439 0-0.615 0.13z" 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 10.99c0.552 0 1-0.447 1-1 0-0.552-0.448-1-1-1s-1 0.448-1 1c0 0.553 0.448 1 1 1zm5 1.998c0 0.553-0.448 1-1 1s-1-0.447-1-1c0-0.552 0.448-1 1-1s1 0.448 1 1zm-5 3.998c0.552 0 1-0.448 1-1 0-0.553-0.448-1-1-1s-1 0.447-1 1c0 0.552 0.448 1 1 1zM5 4.66c0-1.497 1.23-2.805 2.82-2.648C13.146 2.539 17.882 5.07 21.262 8.84c1.07 1.193 0.737 2.964-0.479 3.845-1.582 1.148-3.94 2.857-5.283 3.833-0.002 0.437-0.002 0.721-0.001 1.092v0.628c0 0.946-0.763 1.75-1.75 1.75-0.105 0-0.207-0.009-0.306-0.026-0.204 0.826-0.932 1.527-1.944 1.527-0.7 0-1.262-0.335-1.609-0.815l-1.325 0.957c-1.488 1.074-3.57 0.011-3.569-1.826L5 4.661zm2.673-1.155C7.064 3.446 6.499 3.949 6.499 4.66v0.52c5.254 0.22 9.911 2.749 12.985 6.594l0.419-0.304c0.578-0.42 0.652-1.173 0.242-1.63-3.138-3.5-7.533-5.848-12.472-6.335zm-1.177 16.3c0 0.612 0.694 0.967 1.19 0.608l2.128-1.534c0.229-0.164 0.53-0.186 0.78-0.058s0.408 0.386 0.408 0.667c0 0.27 0.209 0.5 0.497 0.5 0.292 0 0.502-0.233 0.502-0.5v-1.251c0-0.415 0.335-0.75 0.75-0.75 0.414 0 0.75 0.335 0.75 0.75 0 0.14 0.111 0.25 0.248 0.25 0.138 0 0.25-0.111 0.25-0.25v-0.622c-0.001-0.47-0.002-0.808 0.005-1.489 0.002-0.237 0.116-0.46 0.308-0.6 0.902-0.656 2.496-1.812 3.956-2.87-2.801-3.474-7.016-5.756-11.77-5.974L6.497 19.806z" 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="M14.998 5c3.866 0 7 3.134 7 7 0 3.786-3.005 6.87-6.76 6.996L14.998 19H9.002c-3.866 0-7-3.134-7-7 0-3.785 3.005-6.87 6.76-6.996L9.002 5h5.996zm0 1.5H9.002c-3.038 0-5.5 2.462-5.5 5.5 0 2.963 2.344 5.38 5.279 5.496L9.002 17.5h5.996c3.037 0 5.5-2.462 5.5-5.5 0-2.963-2.344-5.38-5.279-5.496L14.998 6.5zM8 9c0.414 0 0.75 0.336 0.75 0.75v1.498h1.5c0.414 0 0.75 0.337 0.75 0.75 0 0.415-0.336 0.75-0.75 0.75h-1.5v1.502C8.75 14.664 8.414 15 8 15s-0.75-0.336-0.75-0.75v-1.502h-1.5c-0.414 0-0.75-0.335-0.75-0.75 0-0.413 0.336-0.75 0.75-0.75h1.5V9.75C7.25 9.336 7.586 9 8 9zm6.75 3.5c0.69 0 1.25 0.56 1.25 1.25S15.44 15 14.75 15s-1.25-0.56-1.25-1.25 0.56-1.25 1.25-1.25zm2-3.5C17.44 9 18 9.56 18 10.25s-0.56 1.25-1.25 1.25-1.25-0.56-1.25-1.25S16.06 9 16.75 9z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue