Compare commits
157 Commits
1.1.4+fork
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
338d8405c0 | ||
|
|
2169afa8e7 | ||
|
|
508ec06d93 | ||
|
|
9fb39d9403 | ||
|
|
4879d74f80 | ||
|
|
ba3f6c4f95 | ||
|
|
481241c4f6 | ||
|
|
5798587dc6 | ||
|
|
066e3e08a2 | ||
|
|
16d6c14633 | ||
|
|
80a4a3551c | ||
|
|
74f3bb8708 | ||
|
|
c3e398b3c2 | ||
|
|
dcfa812c83 | ||
|
|
065e65d708 | ||
|
|
bca0dab381 | ||
|
|
4a45c1055e | ||
|
|
7c789746ce | ||
|
|
f46ce5576c | ||
|
|
730e6fc1fa | ||
|
|
cb36cc042c | ||
|
|
5d586418f9 | ||
|
|
44f1d026d6 | ||
|
|
defaa1095c | ||
|
|
c67f2f8027 | ||
|
|
452128565f | ||
|
|
6322d3c984 | ||
|
|
d68c820e58 | ||
|
|
79f37b4813 | ||
|
|
87460a2fb6 | ||
|
|
6774a642d9 | ||
|
|
f0bd9233b7 | ||
|
|
66efe750a8 | ||
|
|
3658fc423b | ||
|
|
3c3ad7447e | ||
|
|
b570064b99 | ||
|
|
d9f6ef69fe | ||
|
|
bdac1d5bb4 | ||
|
|
1eee1ead5e | ||
|
|
0ec51f5b34 | ||
|
|
6c6fb05a7a | ||
|
|
09a0faacba | ||
|
|
d0d1d15de5 | ||
|
|
e4e9516d5d | ||
|
|
05eceecbea | ||
|
|
71ba1bb0d5 | ||
|
|
2160a26648 | ||
|
|
5433eac9c9 | ||
|
|
0a68f86200 | ||
|
|
c91dae0346 | ||
|
|
e1df7e5077 | ||
|
|
0560b54559 | ||
|
|
c78db7e835 | ||
|
|
c837a2d4b6 | ||
|
|
70b91b7a9a | ||
|
|
27079a7ec5 | ||
|
|
9563df0574 | ||
|
|
638209cc13 | ||
|
|
224c731afa | ||
|
|
0bbf937531 | ||
|
|
3556c92c3e | ||
|
|
87c5b23196 | ||
|
|
c83910c885 | ||
|
|
586622e90d | ||
|
|
e5e2430e03 | ||
|
|
04bfdba50e | ||
|
|
7abf15e9e0 | ||
|
|
6b680831b8 | ||
|
|
6cbf100828 | ||
|
|
3e7bbebe7f | ||
|
|
56d344045a | ||
|
|
7ab634cc08 | ||
|
|
9f0db3ebb5 | ||
|
|
6415eb8590 | ||
|
|
87c77b84a4 | ||
|
|
0b7bb16f22 | ||
|
|
5164b5ba78 | ||
|
|
f3c28bc66a | ||
|
|
239f7eb9e7 | ||
|
|
d6daf7a553 | ||
|
|
dfb3b230e6 | ||
|
|
484a5c878f | ||
|
|
3f27cfb13b | ||
|
|
38e2ba6ccd | ||
|
|
3dad38e614 | ||
|
|
0865c9d1bd | ||
|
|
20a8783d84 | ||
|
|
0b96fb05fc | ||
|
|
8767d62de7 | ||
|
|
74fb04e2d4 | ||
|
|
2537460e16 | ||
|
|
be3dfde3be | ||
|
|
42025035ad | ||
|
|
6a667fdf32 | ||
|
|
bfafac3d4f | ||
|
|
0cafbe9f91 | ||
|
|
2fbf172729 | ||
|
|
bb9755f4af | ||
|
|
2a01377a8a | ||
|
|
61cc6d5d07 | ||
|
|
1d74a37f60 | ||
|
|
ef9645f9e7 | ||
|
|
6a103ca3f3 | ||
|
|
c22772121b | ||
|
|
de7bc69d2a | ||
|
|
2eccd572c9 | ||
|
|
824a62024b | ||
|
|
3a3cfda919 | ||
|
|
e29120cc51 | ||
|
|
197d5c6bc3 | ||
|
|
d143cc75db | ||
|
|
1635a06c54 | ||
|
|
76de0d8c70 | ||
|
|
402a995b8f | ||
|
|
f580ba7779 | ||
|
|
bc3869b920 | ||
|
|
020f4a5a1a | ||
|
|
b054caa967 | ||
|
|
82b7633650 | ||
|
|
33497864f2 | ||
|
|
4c9d1544fa | ||
|
|
bce2367cfc | ||
|
|
390ecc48fb | ||
|
|
9ed99edd6e | ||
|
|
4362490539 | ||
|
|
f5d225fc3e | ||
|
|
063e9287fd | ||
|
|
ba376908cd | ||
|
|
caddf0021c | ||
|
|
90645f4d90 | ||
|
|
1316fcae22 | ||
|
|
27dee7297b | ||
|
|
13ecba40ae | ||
|
|
e15dd0d8b3 | ||
|
|
1ab26bc665 | ||
|
|
e6758d8c01 | ||
|
|
4621787e34 | ||
|
|
10ad35a285 | ||
|
|
d10145a6ba | ||
|
|
c9792ced32 | ||
|
|
a3fb09a33c | ||
|
|
6d875fd890 | ||
|
|
5d87fb7b67 | ||
|
|
4cbb59850b | ||
|
|
a2022b25e5 | ||
|
|
0d168f93ed | ||
|
|
94ac5b9bb7 | ||
|
|
231ea46f9f | ||
|
|
600be455a3 | ||
|
|
a4df06726f | ||
|
|
e45e2c31d1 | ||
|
|
17dc0850d5 | ||
|
|
9667a32e44 | ||
|
|
4e6ba84bb3 | ||
|
|
b79ba71228 | ||
|
|
2903874dbc | ||
|
|
202a5f9581 |
@@ -1,11 +1,11 @@
|
|||||||

|

|
||||||
|
|
||||||
# Moshidon, the material you mastodon client!
|
# Moshidon, the material you mastodon client!
|
||||||
|
|
||||||
> 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.
|
||||||
|
|
||||||
|
|
||||||
[](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.apk)
|
[](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.apk)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Megalodon</title>
|
<title>Moshidon</title>
|
||||||
<link rel="icon" href="mastodon/src/main/res/mipmap-mdpi/ic_launcher_round.png">
|
<link rel="icon" href="mastodon/src/main/res/mipmap-mdpi/ic_launcher_round.png">
|
||||||
<link rel="me" href="https://floss.social/@mastodon">
|
<link rel="me" href="https://floss.social/@mastodon">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown.min.css">
|
||||||
|
|||||||
@@ -9,13 +9,10 @@ android {
|
|||||||
applicationId "org.joinmastodon.android.moshinda"
|
applicationId "org.joinmastodon.android.moshinda"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
versionCode 65
|
versionCode 72
|
||||||
versionName "1.1.4+fork.65.moshinda"
|
versionName "1.1.4+fork.72.moshinda"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
resConfigs "en", "ar-rSA", "bs-rBA", "ca-rES", "cs-rCZ", "de-rDE", "el-rGR", "es-rES",
|
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"
|
||||||
"eu-rES", "fi-rFI", "fr-rFR", "gl-rES", "hr-rHR", "hy-rAM", "it-rIT", "iw-rIL",
|
|
||||||
"ja-rJP", "kab", "ko-rKR", "oc-rFR", "pl-rPL", "pt-rBR", "pt-rPT", "ru-rRU",
|
|
||||||
"sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "vi-rVN", "zh-rCN", "zh-rTW"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
|
||||||
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/>
|
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/>
|
||||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
|
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||||
@@ -19,7 +20,7 @@
|
|||||||
android:localeConfig="@xml/locales_config"
|
android:localeConfig="@xml/locales_config"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:theme="@style/Theme.Mastodon.AutoLightDark.Original"
|
android:theme="@style/Theme.Mastodon.AutoLightDark"
|
||||||
android:largeHeap="true">
|
android:largeHeap="true">
|
||||||
|
|
||||||
<activity android:name=".MainActivity" android:exported="true" android:configChanges="orientation|screenSize" android:windowSoftInputMode="adjustResize" android:launchMode="singleTask">
|
<activity android:name=".MainActivity" android:exported="true" android:configChanges="orientation|screenSize" android:windowSoftInputMode="adjustResize" android:launchMode="singleTask">
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
|||||||
|
|
||||||
Intent intent=getIntent();
|
Intent intent=getIntent();
|
||||||
StringBuilder builder=new StringBuilder();
|
StringBuilder builder=new StringBuilder();
|
||||||
if (intent.hasExtra(Intent.EXTRA_SUBJECT)) builder.append(intent.getStringExtra(Intent.EXTRA_SUBJECT)).append("\n");
|
String subject = "";
|
||||||
|
if (intent.hasExtra(Intent.EXTRA_SUBJECT)) builder.append(subject = intent.getStringExtra(Intent.EXTRA_SUBJECT)).append("\n\n");
|
||||||
if (intent.hasExtra(Intent.EXTRA_TEXT)) builder.append(intent.getStringExtra(Intent.EXTRA_TEXT)).append("\n");
|
if (intent.hasExtra(Intent.EXTRA_TEXT)) builder.append(intent.getStringExtra(Intent.EXTRA_TEXT)).append("\n");
|
||||||
String text=builder.toString();
|
String text=builder.toString();
|
||||||
List<Uri> mediaUris;
|
List<Uri> mediaUris;
|
||||||
@@ -80,6 +81,8 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
|||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
if(!TextUtils.isEmpty(text))
|
if(!TextUtils.isEmpty(text))
|
||||||
args.putString("prefilledText", text);
|
args.putString("prefilledText", text);
|
||||||
|
if(!subject.isBlank())
|
||||||
|
args.putInt("selectionEnd", subject.length());
|
||||||
if(mediaUris!=null && !mediaUris.isEmpty())
|
if(mediaUris!=null && !mediaUris.isEmpty())
|
||||||
args.putParcelableArrayList("mediaAttachments", toArrayList(mediaUris));
|
args.putParcelableArrayList("mediaAttachments", toArrayList(mediaUris));
|
||||||
Fragment fragment=new ComposeFragment();
|
Fragment fragment=new ComposeFragment();
|
||||||
|
|||||||
@@ -1,7 +1,18 @@
|
|||||||
package org.joinmastodon.android;
|
package org.joinmastodon.android;
|
||||||
|
|
||||||
|
import static org.joinmastodon.android.api.MastodonAPIController.gson;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class GlobalUserPreferences{
|
public class GlobalUserPreferences{
|
||||||
public static boolean playGifs;
|
public static boolean playGifs;
|
||||||
@@ -14,14 +25,23 @@ public class GlobalUserPreferences{
|
|||||||
public static boolean showInteractionCounts;
|
public static boolean showInteractionCounts;
|
||||||
public static boolean alwaysExpandContentWarnings;
|
public static boolean alwaysExpandContentWarnings;
|
||||||
public static boolean disableMarquee;
|
public static boolean disableMarquee;
|
||||||
|
public static boolean disableSwipe;
|
||||||
public static boolean voteButtonForSingleChoice;
|
public static boolean voteButtonForSingleChoice;
|
||||||
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();
|
||||||
|
public static Map<String, List<String>> recentLanguages;
|
||||||
|
|
||||||
private static SharedPreferences getPrefs(){
|
private static SharedPreferences getPrefs(){
|
||||||
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
|
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <T> T fromJson(String json, Type type, T orElse) {
|
||||||
|
try { return gson.fromJson(json, type); }
|
||||||
|
catch (JsonSyntaxException ignored) { return orElse; }
|
||||||
|
}
|
||||||
|
|
||||||
public static void load(){
|
public static void load(){
|
||||||
SharedPreferences prefs=getPrefs();
|
SharedPreferences prefs=getPrefs();
|
||||||
playGifs=prefs.getBoolean("playGifs", true);
|
playGifs=prefs.getBoolean("playGifs", true);
|
||||||
@@ -34,9 +54,21 @@ public class GlobalUserPreferences{
|
|||||||
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);
|
||||||
|
disableSwipe=prefs.getBoolean("disableSwipe", false);
|
||||||
voteButtonForSingleChoice=prefs.getBoolean("voteButtonForSingleChoice", true);
|
voteButtonForSingleChoice=prefs.getBoolean("voteButtonForSingleChoice", true);
|
||||||
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
||||||
color=ColorPreference.values()[prefs.getInt("color", 1)];
|
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
||||||
|
|
||||||
|
try {
|
||||||
|
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
|
||||||
|
color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.MATERIAL3.name()));
|
||||||
|
}else{
|
||||||
|
color=ColorPreference.PURPLE;
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException|ClassCastException ignored) {
|
||||||
|
// invalid color name or color was previously saved as integer
|
||||||
|
color=ColorPreference.PURPLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void save(){
|
public static void save(){
|
||||||
@@ -51,19 +83,22 @@ public class GlobalUserPreferences{
|
|||||||
.putBoolean("showInteractionCounts", showInteractionCounts)
|
.putBoolean("showInteractionCounts", showInteractionCounts)
|
||||||
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
|
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
|
||||||
.putBoolean("disableMarquee", disableMarquee)
|
.putBoolean("disableMarquee", disableMarquee)
|
||||||
|
.putBoolean("disableSwipe", disableSwipe)
|
||||||
.putInt("theme", theme.ordinal())
|
.putInt("theme", theme.ordinal())
|
||||||
.putInt("color", color.ordinal())
|
.putString("color", color.name())
|
||||||
|
.putString("recentLanguages", gson.toJson(recentLanguages))
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ColorPreference{
|
public enum ColorPreference{
|
||||||
|
MATERIAL3,
|
||||||
PINK,
|
PINK,
|
||||||
PURPLE,
|
PURPLE,
|
||||||
GREEN,
|
GREEN,
|
||||||
BLUE,
|
BLUE,
|
||||||
ORANGE,
|
BROWN,
|
||||||
YELLOW,
|
RED,
|
||||||
MATERIAL3
|
YELLOW
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ThemePreference{
|
public enum ThemePreference{
|
||||||
@@ -72,3 +107,4 @@ public class GlobalUserPreferences{
|
|||||||
DARK
|
DARK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import org.joinmastodon.android.fragments.HomeFragment;
|
|||||||
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.fragments.onboarding.AccountActivationFragment;
|
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
|
||||||
import org.joinmastodon.android.fragments.onboarding.MoshidonLoginFragment;
|
import org.joinmastodon.android.fragments.onboarding.CustomLoginFragment;
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
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;
|
||||||
@@ -33,7 +33,7 @@ public class MainActivity extends FragmentStackActivity{
|
|||||||
|
|
||||||
if(savedInstanceState==null){
|
if(savedInstanceState==null){
|
||||||
if(AccountSessionManager.getInstance().getLoggedInAccounts().isEmpty()){
|
if(AccountSessionManager.getInstance().getLoggedInAccounts().isEmpty()){
|
||||||
showFragmentClearingBackStack(new MoshidonLoginFragment());
|
showFragmentClearingBackStack(new CustomLoginFragment());
|
||||||
}else{
|
}else{
|
||||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo();
|
AccountSessionManager.getInstance().maybeUpdateLocalInfo();
|
||||||
AccountSession session;
|
AccountSession session;
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public class OAuthActivity extends Activity{
|
|||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Account account){
|
public void onSuccess(Account account){
|
||||||
AccountSessionManager.getInstance().addAccount(instance, token, account, app, true);
|
AccountSessionManager.getInstance().addAccount(instance, token, account, app, null);
|
||||||
progress.dismiss();
|
progress.dismiss();
|
||||||
finish();
|
finish();
|
||||||
// not calling restartMainActivity() here on purpose to have it recreated (notice different flags)
|
// not calling restartMainActivity() here on purpose to have it recreated (notice different flags)
|
||||||
|
|||||||
@@ -137,13 +137,20 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
builder.setContentTitle(pn.title)
|
builder.setContentTitle(pn.title)
|
||||||
.setContentText(pn.body)
|
.setContentText(pn.body)
|
||||||
.setStyle(new Notification.BigTextStyle().bigText(pn.body))
|
.setStyle(new Notification.BigTextStyle().bigText(pn.body))
|
||||||
.setSmallIcon(R.drawable.ic_ntf_logo)
|
|
||||||
.setContentIntent(PendingIntent.getActivity(context, accountID.hashCode() & 0xFFFF, contentIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT))
|
.setContentIntent(PendingIntent.getActivity(context, accountID.hashCode() & 0xFFFF, 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)
|
||||||
.setColor(context.getColor(R.color.shortcut_icon_background));
|
.setColor(context.getColor(R.color.shortcut_icon_background));
|
||||||
|
switch (pn.notificationType) {
|
||||||
|
case FAVORITE -> builder.setSmallIcon(R.drawable.ic_fluent_star_24_filled);
|
||||||
|
case REBLOG -> builder.setSmallIcon(R.drawable.ic_fluent_arrow_repeat_all_24_filled);
|
||||||
|
case FOLLOW -> builder.setSmallIcon(R.drawable.ic_fluent_person_add_24_filled);
|
||||||
|
case MENTION -> builder.setSmallIcon(R.drawable.ic_fluent_mention_24_filled);
|
||||||
|
case POLL -> builder.setSmallIcon(R.drawable.ic_fluent_poll_24_filled);
|
||||||
|
default -> builder.setSmallIcon(R.drawable.ic_ntf_logo);
|
||||||
|
}
|
||||||
if(avatar!=null){
|
if(avatar!=null){
|
||||||
builder.setLargeIcon(UiUtils.getBitmapFromDrawable(avatar));
|
builder.setLargeIcon(UiUtils.getBitmapFromDrawable(avatar));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import androidx.annotation.CallSuper;
|
import androidx.annotation.CallSuper;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
@@ -101,9 +102,14 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public MastodonAPIRequest<T> wrapProgress(Activity activity, @StringRes int message, boolean cancelable){
|
public MastodonAPIRequest<T> wrapProgress(Activity activity, @StringRes int message, boolean cancelable){
|
||||||
|
return wrapProgress(activity, message, cancelable, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MastodonAPIRequest<T> wrapProgress(Activity activity, @StringRes int message, boolean cancelable, Consumer<ProgressDialog> transform){
|
||||||
progressDialog=new ProgressDialog(activity);
|
progressDialog=new ProgressDialog(activity);
|
||||||
progressDialog.setMessage(activity.getString(message));
|
progressDialog.setMessage(activity.getString(message));
|
||||||
progressDialog.setCancelable(cancelable);
|
progressDialog.setCancelable(cancelable);
|
||||||
|
if (transform != null) transform.accept(progressDialog);
|
||||||
if(cancelable){
|
if(cancelable){
|
||||||
progressDialog.setOnCancelListener(dialog->cancel());
|
progressDialog.setOnCancelListener(dialog->cancel());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
|||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
@@ -25,7 +26,7 @@ public class StatusInteractionController{
|
|||||||
this.accountID=accountID;
|
this.accountID=accountID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFavorited(Status status, boolean favorited){
|
public void setFavorited(Status status, boolean favorited, Consumer<Status> cb){
|
||||||
if(!Looper.getMainLooper().isCurrentThread())
|
if(!Looper.getMainLooper().isCurrentThread())
|
||||||
throw new IllegalStateException("Can only be called from main thread");
|
throw new IllegalStateException("Can only be called from main thread");
|
||||||
|
|
||||||
@@ -38,6 +39,8 @@ public class StatusInteractionController{
|
|||||||
@Override
|
@Override
|
||||||
public void onSuccess(Status result){
|
public void onSuccess(Status result){
|
||||||
runningFavoriteRequests.remove(status.id);
|
runningFavoriteRequests.remove(status.id);
|
||||||
|
result.favouritesCount = Math.max(0, status.favouritesCount) + (favorited ? 1 : -1);
|
||||||
|
cb.accept(result);
|
||||||
E.post(new StatusCountersUpdatedEvent(result));
|
E.post(new StatusCountersUpdatedEvent(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,24 +49,17 @@ public class StatusInteractionController{
|
|||||||
runningFavoriteRequests.remove(status.id);
|
runningFavoriteRequests.remove(status.id);
|
||||||
error.showToast(MastodonApp.context);
|
error.showToast(MastodonApp.context);
|
||||||
status.favourited=!favorited;
|
status.favourited=!favorited;
|
||||||
if(favorited)
|
cb.accept(status);
|
||||||
status.favouritesCount--;
|
|
||||||
else
|
|
||||||
status.favouritesCount++;
|
|
||||||
E.post(new StatusCountersUpdatedEvent(status));
|
E.post(new StatusCountersUpdatedEvent(status));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
runningFavoriteRequests.put(status.id, req);
|
runningFavoriteRequests.put(status.id, req);
|
||||||
status.favourited=favorited;
|
status.favourited=favorited;
|
||||||
if(favorited)
|
|
||||||
status.favouritesCount++;
|
|
||||||
else
|
|
||||||
status.favouritesCount--;
|
|
||||||
E.post(new StatusCountersUpdatedEvent(status));
|
E.post(new StatusCountersUpdatedEvent(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReblogged(Status status, boolean reblogged){
|
public void setReblogged(Status status, boolean reblogged, Consumer<Status> cb){
|
||||||
if(!Looper.getMainLooper().isCurrentThread())
|
if(!Looper.getMainLooper().isCurrentThread())
|
||||||
throw new IllegalStateException("Can only be called from main thread");
|
throw new IllegalStateException("Can only be called from main thread");
|
||||||
|
|
||||||
@@ -74,8 +70,11 @@ public class StatusInteractionController{
|
|||||||
SetStatusReblogged req=(SetStatusReblogged) new SetStatusReblogged(status.id, reblogged)
|
SetStatusReblogged req=(SetStatusReblogged) new SetStatusReblogged(status.id, reblogged)
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Status result){
|
public void onSuccess(Status reblog){
|
||||||
|
Status result = reblog.getContentStatus();
|
||||||
runningReblogRequests.remove(status.id);
|
runningReblogRequests.remove(status.id);
|
||||||
|
result.reblogsCount = Math.max(0, status.reblogsCount) + (reblogged ? 1 : -1);
|
||||||
|
cb.accept(result);
|
||||||
E.post(new StatusCountersUpdatedEvent(result));
|
E.post(new StatusCountersUpdatedEvent(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,24 +83,21 @@ public class StatusInteractionController{
|
|||||||
runningReblogRequests.remove(status.id);
|
runningReblogRequests.remove(status.id);
|
||||||
error.showToast(MastodonApp.context);
|
error.showToast(MastodonApp.context);
|
||||||
status.reblogged=!reblogged;
|
status.reblogged=!reblogged;
|
||||||
if(reblogged)
|
cb.accept(status);
|
||||||
status.reblogsCount--;
|
|
||||||
else
|
|
||||||
status.reblogsCount++;
|
|
||||||
E.post(new StatusCountersUpdatedEvent(status));
|
E.post(new StatusCountersUpdatedEvent(status));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
runningReblogRequests.put(status.id, req);
|
runningReblogRequests.put(status.id, req);
|
||||||
status.reblogged=reblogged;
|
status.reblogged=reblogged;
|
||||||
if(reblogged)
|
|
||||||
status.reblogsCount++;
|
|
||||||
else
|
|
||||||
status.reblogsCount--;
|
|
||||||
E.post(new StatusCountersUpdatedEvent(status));
|
E.post(new StatusCountersUpdatedEvent(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBookmarked(Status status, boolean bookmarked){
|
public void setBookmarked(Status status, boolean bookmarked){
|
||||||
|
setBookmarked(status, bookmarked, r->{});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBookmarked(Status status, boolean bookmarked, Consumer<Status> cb){
|
||||||
if(!Looper.getMainLooper().isCurrentThread())
|
if(!Looper.getMainLooper().isCurrentThread())
|
||||||
throw new IllegalStateException("Can only be called from main thread");
|
throw new IllegalStateException("Can only be called from main thread");
|
||||||
|
|
||||||
@@ -114,6 +110,7 @@ public class StatusInteractionController{
|
|||||||
@Override
|
@Override
|
||||||
public void onSuccess(Status result){
|
public void onSuccess(Status result){
|
||||||
runningBookmarkRequests.remove(status.id);
|
runningBookmarkRequests.remove(status.id);
|
||||||
|
cb.accept(result);
|
||||||
E.post(new StatusCountersUpdatedEvent(result));
|
E.post(new StatusCountersUpdatedEvent(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,6 +119,7 @@ public class StatusInteractionController{
|
|||||||
runningBookmarkRequests.remove(status.id);
|
runningBookmarkRequests.remove(status.id);
|
||||||
error.showToast(MastodonApp.context);
|
error.showToast(MastodonApp.context);
|
||||||
status.bookmarked=!bookmarked;
|
status.bookmarked=!bookmarked;
|
||||||
|
cb.accept(status);
|
||||||
E.post(new StatusCountersUpdatedEvent(status));
|
E.post(new StatusCountersUpdatedEvent(status));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,4 +7,15 @@ public class GetInstance extends MastodonAPIRequest<Instance>{
|
|||||||
public GetInstance(){
|
public GetInstance(){
|
||||||
super(HttpMethod.GET, "/instance", Instance.class);
|
super(HttpMethod.GET, "/instance", Instance.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class V2 extends MastodonAPIRequest<Instance.V2>{
|
||||||
|
public V2(){
|
||||||
|
super(HttpMethod.GET, "/instance", Instance.V2.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getPathPrefix() {
|
||||||
|
return "/api/v2";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
package org.joinmastodon.android.api.requests.statuses;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.api.AllFieldsAreRequired;
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
|
||||||
import org.joinmastodon.android.model.BaseModel;
|
|
||||||
import org.joinmastodon.android.model.Status;
|
|
||||||
import org.joinmastodon.android.model.StatusTranslation;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
public class GetStatusTranslation extends MastodonAPIRequest<StatusTranslation>{
|
|
||||||
public GetStatusTranslation(String id){
|
|
||||||
super(HttpMethod.POST, "/statuses/"+id+"/translate", StatusTranslation.class);
|
|
||||||
Request r = new Request();
|
|
||||||
setRequestBody(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Request{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.statuses;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.TranslatedStatus;
|
||||||
|
|
||||||
|
public class TranslateStatus extends MastodonAPIRequest<TranslatedStatus> {
|
||||||
|
public TranslateStatus(String id) {
|
||||||
|
super(HttpMethod.POST, "/statuses/"+id+"/translate", TranslatedStatus.class);
|
||||||
|
setRequestBody(new Object());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.tags;
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||||
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GetFollowedHashtags extends HeaderPaginationRequest<Hashtag> {
|
||||||
|
public GetFollowedHashtags(String maxID, String minID, int limit, String sinceID){
|
||||||
|
super(HttpMethod.GET, "/followed_tags", new TypeToken<>(){});
|
||||||
|
if(maxID!=null)
|
||||||
|
addQueryParameter("max_id", maxID);
|
||||||
|
if(minID!=null)
|
||||||
|
addQueryParameter("min_id", minID);
|
||||||
|
if(sinceID!=null)
|
||||||
|
addQueryParameter("since_id", sinceID);
|
||||||
|
if(limit>0)
|
||||||
|
addQueryParameter("limit", ""+limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package org.joinmastodon.android.api.session;
|
||||||
|
|
||||||
|
public class AccountActivationInfo{
|
||||||
|
public String email;
|
||||||
|
public long lastEmailConfirmationResend;
|
||||||
|
|
||||||
|
public AccountActivationInfo(String email, long lastEmailConfirmationResend){
|
||||||
|
this.email=email;
|
||||||
|
this.lastEmailConfirmationResend=lastEmailConfirmationResend;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import org.joinmastodon.android.api.StatusInteractionController;
|
|||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Application;
|
import org.joinmastodon.android.model.Application;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
|
import org.joinmastodon.android.model.Preferences;
|
||||||
import org.joinmastodon.android.model.PushSubscription;
|
import org.joinmastodon.android.model.PushSubscription;
|
||||||
import org.joinmastodon.android.model.Token;
|
import org.joinmastodon.android.model.Token;
|
||||||
|
|
||||||
@@ -28,17 +29,20 @@ public class AccountSession{
|
|||||||
public long filtersLastUpdated;
|
public long filtersLastUpdated;
|
||||||
public List<Filter> wordFilters=new ArrayList<>();
|
public List<Filter> wordFilters=new ArrayList<>();
|
||||||
public String pushAccountID;
|
public String pushAccountID;
|
||||||
|
public Preferences preferences;
|
||||||
|
public AccountActivationInfo activationInfo;
|
||||||
private transient MastodonAPIController apiController;
|
private transient MastodonAPIController apiController;
|
||||||
private transient StatusInteractionController statusInteractionController;
|
private transient StatusInteractionController statusInteractionController;
|
||||||
private transient CacheController cacheController;
|
private transient CacheController cacheController;
|
||||||
private transient PushSubscriptionManager pushSubscriptionManager;
|
private transient PushSubscriptionManager pushSubscriptionManager;
|
||||||
|
|
||||||
AccountSession(Token token, Account self, Application app, String domain, boolean activated){
|
AccountSession(Token token, Account self, Application app, String domain, boolean activated, AccountActivationInfo activationInfo){
|
||||||
this.token=token;
|
this.token=token;
|
||||||
this.self=self;
|
this.self=self;
|
||||||
this.domain=domain;
|
this.domain=domain;
|
||||||
this.app=app;
|
this.app=app;
|
||||||
this.activated=activated;
|
this.activated=activated;
|
||||||
|
this.activationInfo=activationInfo;
|
||||||
infoLastUpdated=System.currentTimeMillis();
|
infoLastUpdated=System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ 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;
|
||||||
import org.joinmastodon.android.api.PushSubscriptionManager;
|
import org.joinmastodon.android.api.PushSubscriptionManager;
|
||||||
|
import org.joinmastodon.android.api.requests.accounts.GetPreferences;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetWordFilters;
|
import org.joinmastodon.android.api.requests.accounts.GetWordFilters;
|
||||||
import org.joinmastodon.android.api.requests.instance.GetCustomEmojis;
|
import org.joinmastodon.android.api.requests.instance.GetCustomEmojis;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
||||||
@@ -34,6 +35,7 @@ import org.joinmastodon.android.model.Emoji;
|
|||||||
import org.joinmastodon.android.model.EmojiCategory;
|
import org.joinmastodon.android.model.EmojiCategory;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
|
import org.joinmastodon.android.model.Preferences;
|
||||||
import org.joinmastodon.android.model.Token;
|
import org.joinmastodon.android.model.Token;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -100,9 +102,10 @@ public class AccountSessionManager{
|
|||||||
maybeUpdateShortcuts();
|
maybeUpdateShortcuts();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAccount(Instance instance, Token token, Account self, Application app, boolean active){
|
public void addAccount(Instance instance, Token token, Account self, Application app, AccountActivationInfo activationInfo){
|
||||||
instances.put(instance.uri, instance);
|
instances.put(instance.uri, instance);
|
||||||
AccountSession session=new AccountSession(token, self, app, instance.uri, active);
|
updateInstanceInfoV2(instance);
|
||||||
|
AccountSession session=new AccountSession(token, self, app, instance.uri, activationInfo==null, activationInfo);
|
||||||
sessions.put(session.getID(), session);
|
sessions.put(session.getID(), session);
|
||||||
lastActiveAccountID=session.getID();
|
lastActiveAccountID=session.getID();
|
||||||
writeAccountsFile();
|
writeAccountsFile();
|
||||||
@@ -248,12 +251,13 @@ public class AccountSessionManager{
|
|||||||
HashSet<String> domains=new HashSet<>();
|
HashSet<String> domains=new HashSet<>();
|
||||||
for(AccountSession session:sessions.values()){
|
for(AccountSession session:sessions.values()){
|
||||||
domains.add(session.domain.toLowerCase());
|
domains.add(session.domain.toLowerCase());
|
||||||
if(now-session.infoLastUpdated>24L*3600_000L){
|
// if(now-session.infoLastUpdated>24L*3600_000L){
|
||||||
updateSessionLocalInfo(session);
|
updateSessionPreferences(session);
|
||||||
}
|
updateSessionLocalInfo(session);
|
||||||
if(now-session.filtersLastUpdated>3600_000L){
|
// }
|
||||||
updateSessionWordFilters(session);
|
// if(now-session.filtersLastUpdated>3600_000L){
|
||||||
}
|
updateSessionWordFilters(session);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
if(loadedInstances){
|
if(loadedInstances){
|
||||||
maybeUpdateCustomEmojis(domains);
|
maybeUpdateCustomEmojis(domains);
|
||||||
@@ -263,10 +267,10 @@ public class AccountSessionManager{
|
|||||||
private void maybeUpdateCustomEmojis(Set<String> domains){
|
private void maybeUpdateCustomEmojis(Set<String> domains){
|
||||||
long now=System.currentTimeMillis();
|
long now=System.currentTimeMillis();
|
||||||
for(String domain:domains){
|
for(String domain:domains){
|
||||||
Long lastUpdated=instancesLastUpdated.get(domain);
|
// Long lastUpdated=instancesLastUpdated.get(domain);
|
||||||
if(lastUpdated==null || now-lastUpdated>24L*3600_000L){
|
// if(lastUpdated==null || now-lastUpdated>24L*3600_000L){
|
||||||
updateInstanceInfo(domain);
|
updateInstanceInfo(domain);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,6 +292,18 @@ public class AccountSessionManager{
|
|||||||
.exec(session.getID());
|
.exec(session.getID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateSessionPreferences(AccountSession session){
|
||||||
|
new GetPreferences().setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Preferences preferences) {
|
||||||
|
session.preferences=preferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {}
|
||||||
|
}).exec(session.getID());
|
||||||
|
}
|
||||||
|
|
||||||
private void updateSessionWordFilters(AccountSession session){
|
private void updateSessionWordFilters(AccountSession session){
|
||||||
new GetWordFilters()
|
new GetWordFilters()
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@@ -313,6 +329,9 @@ public class AccountSessionManager{
|
|||||||
public void onSuccess(Instance instance){
|
public void onSuccess(Instance instance){
|
||||||
instances.put(domain, instance);
|
instances.put(domain, instance);
|
||||||
updateInstanceEmojis(instance, domain);
|
updateInstanceEmojis(instance, domain);
|
||||||
|
try {
|
||||||
|
updateInstanceInfoV2(instance);
|
||||||
|
} catch (Exception ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -323,6 +342,19 @@ public class AccountSessionManager{
|
|||||||
.execNoAuth(domain);
|
.execNoAuth(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateInstanceInfoV2(Instance instance) {
|
||||||
|
new GetInstance.V2().setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Instance.V2 v2) {
|
||||||
|
if (instance != null) instance.v2 = v2;
|
||||||
|
writeAccountsFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse errorResponse) {}
|
||||||
|
}).execNoAuth(instance.uri);
|
||||||
|
}
|
||||||
|
|
||||||
private void updateInstanceEmojis(Instance instance, String domain){
|
private void updateInstanceEmojis(Instance instance, String domain){
|
||||||
new GetCustomEmojis()
|
new GetCustomEmojis()
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@@ -398,6 +430,10 @@ public class AccountSessionManager{
|
|||||||
return instances.get(domain);
|
return instances.get(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Instance getInstanceInfoForAccount(String account) {
|
||||||
|
return AccountSessionManager.getInstance().getInstanceInfo(instance.getAccount(account).domain);
|
||||||
|
}
|
||||||
|
|
||||||
public void updateAccountInfo(String id, Account account){
|
public void updateAccountInfo(String id, Account account){
|
||||||
AccountSession session=getAccount(id);
|
AccountSession session=getAccount(id);
|
||||||
session.self=account;
|
session.self=account;
|
||||||
|
|||||||
@@ -462,21 +462,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
Status status=holder.getItem().status;
|
Status status=holder.getItem().status;
|
||||||
revealSpoiler(status, holder.getItemID());
|
revealSpoiler(status, holder.getItemID());
|
||||||
}
|
}
|
||||||
public void onRevealTranslationClick(HeaderStatusDisplayItem.Holder holder){
|
|
||||||
Status status=holder.getItem().status;
|
|
||||||
revealTranslation(status, holder.getItemID());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void revealTranslation(Status status, String itemID){
|
|
||||||
status.wantsTranslation=!status.wantsTranslation;
|
|
||||||
TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class);
|
|
||||||
if(text!=null)
|
|
||||||
adapter.notifyItemChanged(text.getAbsoluteAdapterPosition()-getMainAdapterOffset());
|
|
||||||
HeaderStatusDisplayItem.Holder header=findHolderOfType(itemID, HeaderStatusDisplayItem.Holder.class);
|
|
||||||
if(header!=null)
|
|
||||||
header.rebind();
|
|
||||||
updateImagesSpoilerState(status, itemID);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void revealSpoiler(Status status, String itemID){
|
protected void revealSpoiler(Status status, String itemID){
|
||||||
status.spoilerRevealed=true;
|
status.spoilerRevealed=true;
|
||||||
@@ -802,7 +787,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
currentMediaHiddenLayoutsWidth=width;
|
currentMediaHiddenLayoutsWidth=width;
|
||||||
String title=getString(R.string.sensitive_content);
|
String title=getString(R.string.sensitive_content);
|
||||||
TextPaint titlePaint=new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
TextPaint titlePaint=new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
||||||
titlePaint.setColor(getResources().getColor(R.color.gray_50));
|
titlePaint.setColor(UiUtils.getThemeColor(getContext(), R.attr.colorGray50));
|
||||||
titlePaint.setTextSize(V.dp(22));
|
titlePaint.setTextSize(V.dp(22));
|
||||||
titlePaint.setTypeface(mediumTypeface);
|
titlePaint.setTypeface(mediumTypeface);
|
||||||
mediaHiddenTitleLayout=StaticLayout.Builder.obtain(title, 0, title.length(), titlePaint, width)
|
mediaHiddenTitleLayout=StaticLayout.Builder.obtain(title, 0, title.length(), titlePaint, width)
|
||||||
@@ -813,7 +798,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
.setAlignment(Layout.Alignment.ALIGN_CENTER)
|
.setAlignment(Layout.Alignment.ALIGN_CENTER)
|
||||||
.build();
|
.build();
|
||||||
TextPaint textPaint=new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
TextPaint textPaint=new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
||||||
textPaint.setColor(getResources().getColor(R.color.gray_200));
|
textPaint.setColor(UiUtils.getThemeColor(getContext(), R.attr.colorGray200));
|
||||||
textPaint.setTextSize(V.dp(16));
|
textPaint.setTextSize(V.dp(16));
|
||||||
String text=getString(R.string.sensitive_content_explain);
|
String text=getString(R.string.sensitive_content_explain);
|
||||||
mediaHiddenTextLayout=StaticLayout.Builder.obtain(text, 0, text.length(), textPaint, width)
|
mediaHiddenTextLayout=StaticLayout.Builder.obtain(text, 0, text.length(), textPaint, width)
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import static org.joinmastodon.android.GlobalUserPreferences.recentLanguages;
|
||||||
|
import static org.joinmastodon.android.utils.MastodonLanguage.allLanguages;
|
||||||
|
import static org.joinmastodon.android.utils.MastodonLanguage.defaultRecentLanguages;
|
||||||
|
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
@@ -29,11 +33,13 @@ import android.text.Spanned;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.SubMenu;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewOutlineProvider;
|
import android.view.ViewOutlineProvider;
|
||||||
@@ -55,6 +61,7 @@ import android.widget.Toast;
|
|||||||
import com.twitter.twittertext.TwitterTextEmojiRegex;
|
import com.twitter.twittertext.TwitterTextEmojiRegex;
|
||||||
|
|
||||||
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;
|
||||||
@@ -95,6 +102,7 @@ import org.joinmastodon.android.ui.views.ComposeEditText;
|
|||||||
import org.joinmastodon.android.ui.views.ComposeMediaLayout;
|
import org.joinmastodon.android.ui.views.ComposeMediaLayout;
|
||||||
import org.joinmastodon.android.ui.views.ReorderableLinearLayout;
|
import org.joinmastodon.android.ui.views.ReorderableLinearLayout;
|
||||||
import org.joinmastodon.android.ui.views.SizeListenerLinearLayout;
|
import org.joinmastodon.android.ui.views.SizeListenerLinearLayout;
|
||||||
|
import org.joinmastodon.android.utils.MastodonLanguage;
|
||||||
import org.parceler.Parcel;
|
import org.parceler.Parcel;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
@@ -105,6 +113,7 @@ import java.time.temporal.ChronoUnit;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -145,7 +154,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private String accountID;
|
private String accountID;
|
||||||
private int charCount, charLimit, trimmedCharCount;
|
private int charCount, charLimit, trimmedCharCount;
|
||||||
|
|
||||||
private Button publishButton;
|
private Button publishButton, languageButton;
|
||||||
|
private PopupMenu languagePopup, visibilityPopup;
|
||||||
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, visibilityBtn;
|
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, visibilityBtn;
|
||||||
private ImageView sensitiveIcon;
|
private ImageView sensitiveIcon;
|
||||||
private ComposeMediaLayout attachmentsView;
|
private ComposeMediaLayout attachmentsView;
|
||||||
@@ -153,9 +163,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private ReorderableLinearLayout pollOptionsView;
|
private ReorderableLinearLayout pollOptionsView;
|
||||||
private View pollWrap;
|
private View pollWrap;
|
||||||
private View addPollOptionBtn;
|
private View addPollOptionBtn;
|
||||||
|
private View sensitiveItem;
|
||||||
private View pollAllowMultipleItem;
|
private View pollAllowMultipleItem;
|
||||||
private CheckBox pollAllowMultipleCheckbox;
|
private CheckBox pollAllowMultipleCheckbox;
|
||||||
private View sensitiveItem;
|
|
||||||
private TextView pollDurationView;
|
private TextView pollDurationView;
|
||||||
|
|
||||||
private ArrayList<DraftPollOption> pollOptions=new ArrayList<>();
|
private ArrayList<DraftPollOption> pollOptions=new ArrayList<>();
|
||||||
@@ -190,10 +200,17 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private boolean ignoreSelectionChanges=false;
|
private boolean ignoreSelectionChanges=false;
|
||||||
private Runnable updateUploadEtaRunnable;
|
private Runnable updateUploadEtaRunnable;
|
||||||
|
|
||||||
|
private String language;
|
||||||
|
private MastodonLanguage.LanguageResolver languageResolver;
|
||||||
|
|
||||||
|
private int navigationBarColorBefore;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
|
navigationBarColorBefore = getActivity().getWindow().getNavigationBarColor();
|
||||||
|
getActivity().getWindow().setNavigationBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLightest));
|
||||||
|
|
||||||
accountID=getArguments().getString("account");
|
accountID=getArguments().getString("account");
|
||||||
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
@@ -201,6 +218,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
instanceDomain=session.domain;
|
instanceDomain=session.domain;
|
||||||
customEmojis=AccountSessionManager.getInstance().getCustomEmojis(instanceDomain);
|
customEmojis=AccountSessionManager.getInstance().getCustomEmojis(instanceDomain);
|
||||||
instance=AccountSessionManager.getInstance().getInstanceInfo(instanceDomain);
|
instance=AccountSessionManager.getInstance().getInstanceInfo(instanceDomain);
|
||||||
|
languageResolver=new MastodonLanguage.LanguageResolver(instance);
|
||||||
if(getArguments().containsKey("editStatus")){
|
if(getArguments().containsKey("editStatus")){
|
||||||
editingStatus=Parcels.unwrap(getArguments().getParcelable("editStatus"));
|
editingStatus=Parcels.unwrap(getArguments().getParcelable("editStatus"));
|
||||||
redraftStatus=getArguments().getBoolean("redraftStatus");
|
redraftStatus=getArguments().getBoolean("redraftStatus");
|
||||||
@@ -220,7 +238,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
else
|
else
|
||||||
charLimit=500;
|
charLimit=500;
|
||||||
|
|
||||||
loadDefaultStatusVisibility(savedInstanceState);
|
if (editingStatus == null) loadDefaultStatusVisibility(savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -234,6 +252,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
UiUtils.removeCallbacks(updateUploadEtaRunnable);
|
UiUtils.removeCallbacks(updateUploadEtaRunnable);
|
||||||
updateUploadEtaRunnable=null;
|
updateUploadEtaRunnable=null;
|
||||||
}
|
}
|
||||||
|
getActivity().getWindow().setNavigationBarColor(navigationBarColorBefore);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -243,6 +262,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
wm=activity.getSystemService(WindowManager.class);
|
wm=activity.getSystemService(WindowManager.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
@Override
|
@Override
|
||||||
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
||||||
creatingView=true;
|
creatingView=true;
|
||||||
@@ -283,7 +303,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
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());
|
||||||
visibilityBtn.setOnClickListener(this::onVisibilityClick);
|
buildVisibilityPopup(visibilityBtn);
|
||||||
|
visibilityBtn.setOnClickListener(v->visibilityPopup.show());
|
||||||
|
visibilityBtn.setOnTouchListener(visibilityPopup.getDragToOpenListener());
|
||||||
sensitiveItem.setOnClickListener(v->toggleSensitive());
|
sensitiveItem.setOnClickListener(v->toggleSensitive());
|
||||||
emojiKeyboard.setOnIconChangedListener(new PopupKeyboard.OnIconChangeListener(){
|
emojiKeyboard.setOnIconChangedListener(new PopupKeyboard.OnIconChangeListener(){
|
||||||
@Override
|
@Override
|
||||||
@@ -375,7 +397,14 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
statusVisibility=editingStatus.visibility;
|
statusVisibility=editingStatus.visibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadDefaultStatusVisibility(savedInstanceState);
|
||||||
updateVisibilityIcon();
|
updateVisibilityIcon();
|
||||||
|
visibilityPopup.getMenu().findItem(switch(statusVisibility){
|
||||||
|
case PUBLIC -> R.id.vis_public;
|
||||||
|
case UNLISTED -> R.id.vis_unlisted;
|
||||||
|
case PRIVATE -> R.id.vis_followers;
|
||||||
|
case DIRECT -> R.id.vis_private;
|
||||||
|
}).setChecked(true);
|
||||||
|
|
||||||
autocompleteViewController=new ComposeAutocompleteViewController(getActivity(), accountID);
|
autocompleteViewController=new ComposeAutocompleteViewController(getActivity(), accountID);
|
||||||
autocompleteViewController.setCompletionSelectedListener(this::onAutocompleteOptionSelected);
|
autocompleteViewController.setCompletionSelectedListener(this::onAutocompleteOptionSelected);
|
||||||
@@ -403,6 +432,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
outState.putBoolean("sensitive", sensitive);
|
outState.putBoolean("sensitive", sensitive);
|
||||||
outState.putBoolean("hasSpoiler", hasSpoiler);
|
outState.putBoolean("hasSpoiler", hasSpoiler);
|
||||||
|
outState.putString("language", language);
|
||||||
if(!attachments.isEmpty()){
|
if(!attachments.isEmpty()){
|
||||||
ArrayList<Parcelable> serializedAttachments=new ArrayList<>(attachments.size());
|
ArrayList<Parcelable> serializedAttachments=new ArrayList<>(attachments.size());
|
||||||
for(DraftMediaAttachment att:attachments){
|
for(DraftMediaAttachment att:attachments){
|
||||||
@@ -494,14 +524,14 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
spoilerEdit.addTextChangedListener(new SimpleTextWatcher(e->updateCharCounter()));
|
spoilerEdit.addTextChangedListener(new SimpleTextWatcher(e->updateCharCounter()));
|
||||||
if(replyTo!=null){
|
if(replyTo!=null){
|
||||||
replyText.setText(getString(R.string.in_reply_to, replyTo.account.displayName));
|
replyText.setText(getString(R.string.in_reply_to, replyTo.account.displayName));
|
||||||
int visibilityNameRes = switch (statusVisibility) {
|
int visibilityNameRes = switch (replyTo.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;
|
||||||
};
|
};
|
||||||
replyText.setContentDescription(getString(R.string.in_reply_to, replyTo.account.displayName) + ". " + getString(R.string.post_visibility) + ": " + getString(visibilityNameRes));
|
replyText.setContentDescription(getString(R.string.in_reply_to, replyTo.account.displayName) + ". " + getString(R.string.post_visibility) + ": " + getString(visibilityNameRes));
|
||||||
Drawable visibilityIcon = getActivity().getDrawable(switch(statusVisibility){
|
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_people_community_20_regular;
|
||||||
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_20_regular;
|
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_20_regular;
|
||||||
@@ -536,14 +566,16 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
ignoreSelectionChanges=true;
|
ignoreSelectionChanges=true;
|
||||||
mainEditText.setSelection(mainEditText.length());
|
mainEditText.setSelection(mainEditText.length());
|
||||||
ignoreSelectionChanges=false;
|
ignoreSelectionChanges=false;
|
||||||
if(!TextUtils.isEmpty(replyTo.spoilerText) && AccountSessionManager.getInstance().isSelf(accountID, replyTo.account)){
|
if(!TextUtils.isEmpty(replyTo.spoilerText)){
|
||||||
hasSpoiler=true;
|
hasSpoiler=true;
|
||||||
spoilerEdit.setVisibility(View.VISIBLE);
|
spoilerEdit.setVisibility(View.VISIBLE);
|
||||||
spoilerEdit.setText(replyTo.spoilerText);
|
spoilerEdit.setText(replyTo.spoilerText);
|
||||||
spoilerBtn.setSelected(true);
|
spoilerBtn.setSelected(true);
|
||||||
}
|
}
|
||||||
|
if (replyTo.language != null && !replyTo.language.isEmpty()) updateLanguage(replyTo.language);
|
||||||
}
|
}
|
||||||
}else{
|
}else if (editingStatus==null || editingStatus.inReplyToId==null){
|
||||||
|
// TODO: remove workaround after https://github.com/mastodon/mastodon-android/issues/341 gets fixed
|
||||||
replyText.setVisibility(View.GONE);
|
replyText.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
if(savedInstanceState==null){
|
if(savedInstanceState==null){
|
||||||
@@ -553,6 +585,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
ignoreSelectionChanges=true;
|
ignoreSelectionChanges=true;
|
||||||
mainEditText.setSelection(mainEditText.length());
|
mainEditText.setSelection(mainEditText.length());
|
||||||
ignoreSelectionChanges=false;
|
ignoreSelectionChanges=false;
|
||||||
|
updateLanguage(editingStatus.language);
|
||||||
if(!editingStatus.mediaAttachments.isEmpty()){
|
if(!editingStatus.mediaAttachments.isEmpty()){
|
||||||
attachmentsView.setVisibility(View.VISIBLE);
|
attachmentsView.setVisibility(View.VISIBLE);
|
||||||
for(Attachment att:editingStatus.mediaAttachments){
|
for(Attachment att:editingStatus.mediaAttachments){
|
||||||
@@ -575,6 +608,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
ignoreSelectionChanges=false;
|
ignoreSelectionChanges=false;
|
||||||
initialText=prefilledText;
|
initialText=prefilledText;
|
||||||
}
|
}
|
||||||
|
if (getArguments().containsKey("selectionStart") || getArguments().containsKey("selectionEnd")) {
|
||||||
|
int selectionStart=getArguments().getInt("selectionStart", 0);
|
||||||
|
int selectionEnd=getArguments().getInt("selectionEnd", selectionStart);
|
||||||
|
mainEditText.setSelection(selectionStart, selectionEnd);
|
||||||
|
}
|
||||||
ArrayList<Uri> mediaUris=getArguments().getParcelableArrayList("mediaAttachments");
|
ArrayList<Uri> mediaUris=getArguments().getParcelableArrayList("mediaAttachments");
|
||||||
if(mediaUris!=null && !mediaUris.isEmpty()){
|
if(mediaUris!=null && !mediaUris.isEmpty()){
|
||||||
for(Uri uri:mediaUris){
|
for(Uri uri:mediaUris){
|
||||||
@@ -615,6 +653,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
sendError.setVisibility(View.GONE);
|
sendError.setVisibility(View.GONE);
|
||||||
sendProgress.setVisibility(View.GONE);
|
sendProgress.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
LinearLayout.LayoutParams langParams=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
langParams.setMarginEnd(V.dp(8));
|
||||||
|
wrap.addView(buildLanguageSelector(), langParams);
|
||||||
|
|
||||||
wrap.addView(publishButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
wrap.addView(publishButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
wrap.setPadding(V.dp(16), V.dp(4), V.dp(16), V.dp(8));
|
wrap.setPadding(V.dp(16), V.dp(4), V.dp(16), V.dp(8));
|
||||||
wrap.setClipToPadding(false);
|
wrap.setClipToPadding(false);
|
||||||
@@ -624,6 +666,59 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
updatePublishButtonState();
|
updatePublishButtonState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateLanguage(String lang) {
|
||||||
|
updateLanguage(languageResolver.from(lang));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLanguage(MastodonLanguage loc) {
|
||||||
|
language = loc.getLanguage();
|
||||||
|
languageButton.setText(loc.getLanguageName());
|
||||||
|
languageButton.setContentDescription(getActivity().getString(R.string.sk_post_language, loc.getDefaultName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
private Button buildLanguageSelector() {
|
||||||
|
TypedValue typedValue = new TypedValue();
|
||||||
|
getActivity().getTheme().resolveAttribute(android.R.attr.textColorSecondary, typedValue, true);
|
||||||
|
|
||||||
|
languageButton=new Button(getActivity());
|
||||||
|
languageButton.setTextColor(typedValue.data);
|
||||||
|
languageButton.setBackground(getActivity().getDrawable(R.drawable.bg_text_button));
|
||||||
|
languageButton.setPadding(V.dp(8), 0, V.dp(8), 0);
|
||||||
|
languageButton.setCompoundDrawablesRelativeWithIntrinsicBounds(getActivity().getDrawable(R.drawable.ic_fluent_local_language_16_regular), null, null, null);
|
||||||
|
languageButton.setCompoundDrawableTintList(languageButton.getTextColors());
|
||||||
|
languageButton.setCompoundDrawablePadding(V.dp(6));
|
||||||
|
|
||||||
|
languagePopup=new PopupMenu(getActivity(), languageButton);
|
||||||
|
languageButton.setOnTouchListener(languagePopup.getDragToOpenListener());
|
||||||
|
languageButton.setOnClickListener(v->languagePopup.show());
|
||||||
|
|
||||||
|
Preferences prefs = AccountSessionManager.getInstance().getAccount(accountID).preferences;
|
||||||
|
updateLanguage(prefs != null && prefs.postingDefaultLanguage != null && prefs.postingDefaultLanguage.length() > 0
|
||||||
|
? languageResolver.from(prefs.postingDefaultLanguage)
|
||||||
|
: languageResolver.getDefault());
|
||||||
|
|
||||||
|
Menu languageMenu = languagePopup.getMenu();
|
||||||
|
for (String recentLanguage : Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages)) {
|
||||||
|
MastodonLanguage l = languageResolver.from(recentLanguage);
|
||||||
|
languageMenu.add(0, allLanguages.indexOf(l), Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
SubMenu allLanguagesMenu = languageMenu.addSubMenu(R.string.sk_available_languages);
|
||||||
|
for (int i = 0; i < allLanguages.size(); i++) {
|
||||||
|
MastodonLanguage l = allLanguages.get(i);
|
||||||
|
allLanguagesMenu.add(0, i, Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
languagePopup.setOnMenuItemClickListener(i->{
|
||||||
|
if (i.hasSubMenu()) return false;
|
||||||
|
updateLanguage(allLanguages.get(i.getItemId()));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return languageButton;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item){
|
public boolean onOptionsItemSelected(MenuItem item){
|
||||||
return true;
|
return true;
|
||||||
@@ -697,6 +792,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
req.status=text;
|
req.status=text;
|
||||||
req.visibility=statusVisibility;
|
req.visibility=statusVisibility;
|
||||||
req.sensitive=sensitive;
|
req.sensitive=sensitive;
|
||||||
|
req.language=language;
|
||||||
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());
|
||||||
}
|
}
|
||||||
@@ -706,6 +802,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
if(!pollOptions.isEmpty()){
|
if(!pollOptions.isEmpty()){
|
||||||
req.poll=new CreateStatus.Request.Poll();
|
req.poll=new CreateStatus.Request.Poll();
|
||||||
req.poll.expiresIn=pollDuration;
|
req.poll.expiresIn=pollDuration;
|
||||||
|
req.poll.multiple=pollAllowMultipleItem.isSelected();
|
||||||
for(DraftPollOption opt:pollOptions)
|
for(DraftPollOption opt:pollOptions)
|
||||||
req.poll.options.add(opt.edit.getText().toString());
|
req.poll.options.add(opt.edit.getText().toString());
|
||||||
}
|
}
|
||||||
@@ -773,6 +870,14 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
.setCallback(resCallback)
|
.setCallback(resCallback)
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (replyTo == null) {
|
||||||
|
List<String> newRecentLanguages = new ArrayList<>(Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages));
|
||||||
|
newRecentLanguages.remove(language);
|
||||||
|
newRecentLanguages.add(0, language);
|
||||||
|
recentLanguages.put(accountID, newRecentLanguages.stream().limit(4).collect(Collectors.toList()));
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasDraft(){
|
private boolean hasDraft(){
|
||||||
@@ -1312,19 +1417,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
return attachments.size();
|
return attachments.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onVisibilityClick(View v){
|
private void buildVisibilityPopup(View v){
|
||||||
PopupMenu menu=new PopupMenu(getActivity(), v);
|
visibilityPopup=new PopupMenu(getActivity(), v);
|
||||||
menu.inflate(R.menu.compose_visibility);
|
visibilityPopup.inflate(R.menu.compose_visibility);
|
||||||
Menu m=menu.getMenu();
|
Menu m=visibilityPopup.getMenu();
|
||||||
UiUtils.enablePopupMenuIcons(getActivity(), menu);
|
UiUtils.enablePopupMenuIcons(getActivity(), visibilityPopup);
|
||||||
m.setGroupCheckable(0, true, true);
|
m.setGroupCheckable(0, true, true);
|
||||||
m.findItem(switch(statusVisibility){
|
visibilityPopup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
|
||||||
case PUBLIC -> R.id.vis_public;
|
|
||||||
case UNLISTED -> R.id.vis_unlisted;
|
|
||||||
case PRIVATE -> R.id.vis_followers;
|
|
||||||
case DIRECT -> R.id.vis_private;
|
|
||||||
}).setChecked(true);
|
|
||||||
menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onMenuItemClick(MenuItem item){
|
public boolean onMenuItemClick(MenuItem item){
|
||||||
int id=item.getItemId();
|
int id=item.getItemId();
|
||||||
@@ -1342,7 +1441,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
menu.show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadDefaultStatusVisibility(Bundle savedInstanceState) {
|
private void loadDefaultStatusVisibility(Bundle savedInstanceState) {
|
||||||
@@ -1356,34 +1454,23 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
||||||
}
|
}
|
||||||
|
|
||||||
new GetPreferences()
|
Preferences prefs = AccountSessionManager.getInstance().getAccount(accountID).preferences;
|
||||||
.setCallback(new Callback<>(){
|
if (prefs != null) {
|
||||||
@Override
|
// Only override the reply visibility if our preference is more private
|
||||||
public void onSuccess(Preferences result){
|
if (prefs.postingDefaultVisibility.isLessVisibleThan(statusVisibility)) {
|
||||||
// Only override the reply visibility if our preference is more private
|
statusVisibility = switch (prefs.postingDefaultVisibility) {
|
||||||
if (result.postingDefaultVisibility.isLessVisibleThan(statusVisibility)) {
|
case PUBLIC -> StatusPrivacy.PUBLIC;
|
||||||
statusVisibility = switch (result.postingDefaultVisibility) {
|
case UNLISTED -> StatusPrivacy.UNLISTED;
|
||||||
case PUBLIC -> StatusPrivacy.PUBLIC;
|
case PRIVATE -> StatusPrivacy.PRIVATE;
|
||||||
case UNLISTED -> StatusPrivacy.UNLISTED;
|
case DIRECT -> StatusPrivacy.DIRECT;
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
updateVisibilityIcon ();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error){
|
|
||||||
Log.w(TAG, "Unable to get user preferences to set default post privacy");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.exec(accountID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateVisibilityIcon(){
|
private void updateVisibilityIcon(){
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ public class FollowRequestsListFragment extends BaseRecyclerFragment<FollowReque
|
|||||||
RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter = getBindingAdapter();
|
RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter = getBindingAdapter();
|
||||||
if (!rel.requested && !rel.followedBy && adapter != null) {
|
if (!rel.requested && !rel.followedBy && adapter != null) {
|
||||||
data.remove(item);
|
data.remove(item);
|
||||||
adapter.notifyItemRemoved(getBindingAdapterPosition());
|
adapter.notifyItemRemoved(getLayoutPosition());
|
||||||
} else {
|
} else {
|
||||||
rebind();
|
rebind();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags;
|
||||||
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
|
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
|
public class FollowedHashtagsFragment extends BaseRecyclerFragment<Hashtag> implements ScrollableToTop {
|
||||||
|
private String nextMaxID;
|
||||||
|
private String accountId;
|
||||||
|
|
||||||
|
public FollowedHashtagsFragment() {
|
||||||
|
super(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
Bundle args=getArguments();
|
||||||
|
accountId=args.getString("account");
|
||||||
|
setTitle(R.string.sk_hashtags_you_follow);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onShown(){
|
||||||
|
super.onShown();
|
||||||
|
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doLoadData(int offset, int count){
|
||||||
|
currentRequest=new GetFollowedHashtags(offset==0 ? null : nextMaxID, null, count, null)
|
||||||
|
.setCallback(new SimpleCallback<>(this){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(HeaderPaginationList<Hashtag> result){
|
||||||
|
if(result.nextPageUri!=null)
|
||||||
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
|
else
|
||||||
|
nextMaxID=null;
|
||||||
|
onDataLoaded(result, nextMaxID!=null);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec(accountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RecyclerView.Adapter getAdapter() {
|
||||||
|
return new HashtagsAdapter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void scrollToTop() {
|
||||||
|
smoothScrollRecyclerViewToTop(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class HashtagsAdapter extends RecyclerView.Adapter<HashtagViewHolder>{
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public HashtagViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
|
return new HashtagViewHolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull HashtagViewHolder holder, int position) {
|
||||||
|
holder.bind(data.get(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return data.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class HashtagViewHolder extends BindableViewHolder<Hashtag> implements UsableRecyclerView.Clickable{
|
||||||
|
private final TextView title;
|
||||||
|
|
||||||
|
public HashtagViewHolder(){
|
||||||
|
super(getActivity(), R.layout.item_text, list);
|
||||||
|
title=findViewById(R.id.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBind(Hashtag item) {
|
||||||
|
title.setText(item.name);
|
||||||
|
title.setCompoundDrawablesRelativeWithIntrinsicBounds(itemView.getContext().getDrawable(R.drawable.ic_fluent_number_symbol_24_regular), null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick() {
|
||||||
|
UiUtils.openHashtagTimeline(getActivity(), accountId, item.name, item.following);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,9 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -23,17 +18,13 @@ import org.joinmastodon.android.model.ListTimeline;
|
|||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
import me.grishka.appkit.utils.V;
|
|
||||||
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 {
|
||||||
@@ -159,7 +150,7 @@ public class ListTimelinesFragment extends BaseRecyclerFragment<ListTimeline> im
|
|||||||
private final CheckBox listToggle;
|
private final CheckBox listToggle;
|
||||||
|
|
||||||
public ListViewHolder(){
|
public ListViewHolder(){
|
||||||
super(getActivity(), R.layout.item_list_timeline, list);
|
super(getActivity(), R.layout.item_text, list);
|
||||||
title=findViewById(R.id.title);
|
title=findViewById(R.id.title);
|
||||||
listToggle=findViewById(R.id.list_toggle);
|
listToggle=findViewById(R.id.list_toggle);
|
||||||
}
|
}
|
||||||
@@ -167,8 +158,10 @@ public class ListTimelinesFragment extends BaseRecyclerFragment<ListTimeline> im
|
|||||||
@Override
|
@Override
|
||||||
public void onBind(ListTimeline item) {
|
public void onBind(ListTimeline item) {
|
||||||
title.setText(item.title);
|
title.setText(item.title);
|
||||||
|
title.setCompoundDrawablesRelativeWithIntrinsicBounds(itemView.getContext().getDrawable(R.drawable.ic_fluent_people_community_24_regular), null, null, null);
|
||||||
if (profileAccountId != null) {
|
if (profileAccountId != null) {
|
||||||
Boolean checked = userInList.get(item.id);
|
Boolean checked = userInList.get(item.id);
|
||||||
|
listToggle.setVisibility(View.VISIBLE);
|
||||||
listToggle.setChecked(userInList.containsKey(item.id) && checked != null && checked);
|
listToggle.setChecked(userInList.containsKey(item.id) && checked != null && checked);
|
||||||
listToggle.setOnClickListener(this::onClickToggle);
|
listToggle.setOnClickListener(this::onClickToggle);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import android.widget.FrameLayout;
|
|||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetFollowRequests;
|
import org.joinmastodon.android.api.requests.accounts.GetFollowRequests;
|
||||||
import org.joinmastodon.android.events.FollowRequestHandledEvent;
|
import org.joinmastodon.android.events.FollowRequestHandledEvent;
|
||||||
@@ -109,6 +110,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
|||||||
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));
|
||||||
|
|
||||||
pager.setOffscreenPageLimit(4);
|
pager.setOffscreenPageLimit(4);
|
||||||
|
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
||||||
pager.setAdapter(new DiscoverPagerAdapter());
|
pager.setAdapter(new DiscoverPagerAdapter());
|
||||||
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
|
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import android.app.Activity;
|
|||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.ClipboardManager;
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Outline;
|
import android.graphics.Outline;
|
||||||
@@ -18,6 +19,8 @@ import android.graphics.drawable.Drawable;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.VibrationEffect;
|
||||||
|
import android.os.Vibrator;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.style.ImageSpan;
|
import android.text.style.ImageSpan;
|
||||||
@@ -234,6 +237,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}
|
}
|
||||||
|
|
||||||
pager.setOffscreenPageLimit(5);
|
pager.setOffscreenPageLimit(5);
|
||||||
|
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
||||||
pager.setAdapter(new ProfilePagerAdapter());
|
pager.setAdapter(new ProfilePagerAdapter());
|
||||||
pager.getLayoutParams().height=getResources().getDisplayMetrics().heightPixels;
|
pager.getLayoutParams().height=getResources().getDisplayMetrics().heightPixels;
|
||||||
|
|
||||||
@@ -288,10 +292,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
if(!username.contains("@")){
|
if(!username.contains("@")){
|
||||||
username+="@"+AccountSessionManager.getInstance().getAccount(accountID).domain;
|
username+="@"+AccountSessionManager.getInstance().getAccount(accountID).domain;
|
||||||
}
|
}
|
||||||
getActivity().getSystemService(ClipboardManager.class).setPrimaryClip(ClipData.newPlainText(null, "@"+username));
|
UiUtils.copyText(getActivity(), '@'+username);
|
||||||
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.TIRAMISU){ // Android 13+ SystemUI shows its own thing when you put things into the clipboard
|
|
||||||
Toast.makeText(getActivity(), R.string.text_copied, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -623,6 +624,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
args.putString("profileAccount", profileAccountID);
|
args.putString("profileAccount", profileAccountID);
|
||||||
args.putString("profileDisplayUsername", account.getDisplayUsername());
|
args.putString("profileDisplayUsername", account.getDisplayUsername());
|
||||||
Nav.go(getActivity(), ListTimelinesFragment.class, args);
|
Nav.go(getActivity(), ListTimelinesFragment.class, args);
|
||||||
|
}else if(id==R.id.followed_hashtags){
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
Nav.go(getActivity(), FollowedHashtagsFragment.class, args);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import android.os.Build;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
@@ -31,6 +32,7 @@ import com.squareup.otto.Subscribe;
|
|||||||
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.GlobalUserPreferences;
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences.ColorPreference;
|
||||||
import org.joinmastodon.android.MainActivity;
|
import org.joinmastodon.android.MainActivity;
|
||||||
import org.joinmastodon.android.MastodonApp;
|
import org.joinmastodon.android.MastodonApp;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
@@ -48,6 +50,7 @@ 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.ArrayList;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import androidx.annotation.DrawableRes;
|
import androidx.annotation.DrawableRes;
|
||||||
@@ -95,7 +98,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
items.add(new HeaderItem(R.string.settings_theme));
|
items.add(new HeaderItem(R.string.settings_theme));
|
||||||
items.add(themeItem=new ThemeItem());
|
items.add(themeItem=new ThemeItem());
|
||||||
items.add(new SwitchItem(R.string.theme_true_black, R.drawable.ic_fluent_dark_theme_24_regular, GlobalUserPreferences.trueBlackTheme, this::onTrueBlackThemeChanged));
|
items.add(new SwitchItem(R.string.theme_true_black, R.drawable.ic_fluent_dark_theme_24_regular, GlobalUserPreferences.trueBlackTheme, this::onTrueBlackThemeChanged));
|
||||||
items.add(new SwitchItem(R.string.disable_marquee, R.drawable.ic_fluent_text_more_24_regular, GlobalUserPreferences.disableMarquee, i->{
|
items.add(new SwitchItem(R.string.sk_disable_marquee, R.drawable.ic_fluent_text_more_24_regular, GlobalUserPreferences.disableMarquee, i->{
|
||||||
GlobalUserPreferences.disableMarquee=i.checked;
|
GlobalUserPreferences.disableMarquee=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
@@ -114,10 +117,15 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
GlobalUserPreferences.showInteractionCounts=i.checked;
|
GlobalUserPreferences.showInteractionCounts=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
items.add(new SwitchItem(R.string.settings_always_reveal_content_warnings, R.drawable.ic_fluent_chat_warning_24_regular, GlobalUserPreferences.alwaysExpandContentWarnings, i->{
|
items.add(new SwitchItem(R.string.sk_settings_always_reveal_content_warnings, R.drawable.ic_fluent_chat_warning_24_regular, GlobalUserPreferences.alwaysExpandContentWarnings, i->{
|
||||||
GlobalUserPreferences.alwaysExpandContentWarnings=i.checked;
|
GlobalUserPreferences.alwaysExpandContentWarnings=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.sk_tabs_disable_swipe, R.drawable.ic_fluent_swipe_right_24_regular, GlobalUserPreferences.disableSwipe, i->{
|
||||||
|
GlobalUserPreferences.disableSwipe=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
needAppRestart=true;
|
||||||
|
}));
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.home_timeline));
|
items.add(new HeaderItem(R.string.home_timeline));
|
||||||
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->{
|
||||||
@@ -157,11 +165,15 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
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 TextItem(R.string.settings_contribute_fork, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/LucasGGamerM/moshidon")));
|
items.add(new TextItem(R.string.sk_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/LucasGGamerM/moshidon")));
|
||||||
items.add(new TextItem(R.string.settings_clear_cache, this::clearImageCache));
|
items.add(new TextItem(R.string.settings_clear_cache, this::clearImageCache));
|
||||||
|
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.save();
|
||||||
|
})));
|
||||||
items.add(new TextItem(R.string.log_out, this::confirmLogOut));
|
items.add(new TextItem(R.string.log_out, this::confirmLogOut));
|
||||||
|
|
||||||
items.add(new FooterItem(getString(R.string.settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
|
items.add(new FooterItem(getString(R.string.sk_settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -698,20 +710,21 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
onColorPreferenceClick(pref);
|
onColorPreferenceClick(pref);
|
||||||
}
|
}
|
||||||
else if(id==R.id.orange_color) {
|
else if(id==R.id.orange_color) {
|
||||||
pref = GlobalUserPreferences.ColorPreference.ORANGE;
|
pref = ColorPreference.BROWN;
|
||||||
onColorPreferenceClick(pref);
|
onColorPreferenceClick(pref);
|
||||||
}
|
}
|
||||||
else if(id==R.id.yellow_color) {
|
else if(id==R.id.yellow_color) {
|
||||||
pref = GlobalUserPreferences.ColorPreference.YELLOW;
|
pref = GlobalUserPreferences.ColorPreference.YELLOW;
|
||||||
onColorPreferenceClick(pref);
|
onColorPreferenceClick(pref);
|
||||||
}
|
}
|
||||||
|
else if(id==R.id.red_color) {
|
||||||
|
pref = GlobalUserPreferences.ColorPreference.RED;
|
||||||
|
onColorPreferenceClick(pref);
|
||||||
|
}
|
||||||
else if(id==R.id.m3_color) {
|
else if(id==R.id.m3_color) {
|
||||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
pref = GlobalUserPreferences.ColorPreference.MATERIAL3;
|
pref = GlobalUserPreferences.ColorPreference.MATERIAL3;
|
||||||
onColorPreferenceClick(pref);
|
onColorPreferenceClick(pref);
|
||||||
}else{
|
|
||||||
Toast.makeText(getActivity(), R.string.sk_not_supported,
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -719,21 +732,23 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
// UiUtils.enablePopupMenuIcons(getActivity(), popupMenu);
|
// UiUtils.enablePopupMenuIcons(getActivity(), popupMenu);
|
||||||
|
popupMenu.getMenu().findItem(R.id.m3_color).setVisible(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S);
|
||||||
button.setOnTouchListener(popupMenu.getDragToOpenListener());
|
button.setOnTouchListener(popupMenu.getDragToOpenListener());
|
||||||
button.setOnClickListener(v->popupMenu.show());
|
button.setOnClickListener(v->popupMenu.show());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(ColorPicker item){
|
public void onBind(ColorPicker item){
|
||||||
icon.setImageResource(R.drawable.ic_color_theme_preference);
|
icon.setImageResource(R.drawable.ic_fluent_color_24_regular);
|
||||||
button.setText(switch(GlobalUserPreferences.color){
|
button.setText(switch(GlobalUserPreferences.color){
|
||||||
case PINK -> R.string.sk_color_theme_pink;
|
case PINK -> R.string.sk_color_palette_pink;
|
||||||
case PURPLE -> R.string.sk_color_theme_purple;
|
case PURPLE -> R.string.sk_color_palette_purple;
|
||||||
case GREEN -> R.string.sk_color_theme_green;
|
case GREEN -> R.string.sk_color_palette_green;
|
||||||
case BLUE -> R.string.sk_color_theme_blue;
|
case BLUE -> R.string.sk_color_palette_blue;
|
||||||
case ORANGE -> R.string.sk_color_theme_brown;
|
case BROWN -> R.string.sk_color_palette_brown;
|
||||||
case YELLOW -> R.string.sk_color_theme_yellow;
|
case YELLOW -> R.string.sk_color_palette_yellow;
|
||||||
case MATERIAL3 -> R.string.sk_color_theme_material_you;
|
case RED -> R.string.sk_color_palette_red;
|
||||||
|
case MATERIAL3 -> R.string.sk_color_palette_material3;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -853,10 +868,10 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
if (state == GithubSelfUpdater.UpdateState.CHECKING) return;
|
if (state == GithubSelfUpdater.UpdateState.CHECKING) return;
|
||||||
GithubSelfUpdater.UpdateInfo info=updater.getUpdateInfo();
|
GithubSelfUpdater.UpdateInfo info=updater.getUpdateInfo();
|
||||||
if(state!=GithubSelfUpdater.UpdateState.DOWNLOADED){
|
if(state!=GithubSelfUpdater.UpdateState.DOWNLOADED){
|
||||||
text.setText(getString(R.string.update_available, info.version));
|
text.setText(getString(R.string.sk_update_available, info.version));
|
||||||
button.setText(getString(R.string.download_update, UiUtils.formatFileSize(getActivity(), info.size, false)));
|
button.setText(getString(R.string.download_update, UiUtils.formatFileSize(getActivity(), info.size, false)));
|
||||||
}else{
|
}else{
|
||||||
text.setText(getString(R.string.update_ready, info.version));
|
text.setText(getString(R.string.sk_update_ready, info.version));
|
||||||
button.setText(R.string.install_update);
|
button.setText(R.string.install_update);
|
||||||
}
|
}
|
||||||
if(state==GithubSelfUpdater.UpdateState.DOWNLOADING){
|
if(state==GithubSelfUpdater.UpdateState.DOWNLOADING){
|
||||||
|
|||||||
@@ -1,20 +1,28 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
import android.text.style.ReplacementSpan;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.joinmastodon.android.MastodonApp;
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.fragments.onboarding.InstanceCatalogSignupFragment;
|
import org.joinmastodon.android.fragments.onboarding.InstanceCatalogSignupFragment;
|
||||||
import org.joinmastodon.android.fragments.onboarding.InstanceChooserLoginFragment;
|
import org.joinmastodon.android.fragments.onboarding.InstanceChooserLoginFragment;
|
||||||
import org.joinmastodon.android.ui.InterpolatingMotionEffect;
|
|
||||||
import org.joinmastodon.android.ui.views.SizeListenerFrameLayout;
|
import org.joinmastodon.android.ui.views.SizeListenerFrameLayout;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.fragments.AppKitFragment;
|
import me.grishka.appkit.fragments.AppKitFragment;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
@@ -23,12 +31,13 @@ public class SplashFragment extends AppKitFragment{
|
|||||||
|
|
||||||
private SizeListenerFrameLayout contentView;
|
private SizeListenerFrameLayout contentView;
|
||||||
private View artContainer, blueFill, greenFill;
|
private View artContainer, blueFill, greenFill;
|
||||||
private InterpolatingMotionEffect motionEffect;
|
private ViewPager2 pager;
|
||||||
|
private ViewGroup pagerDots;
|
||||||
|
private View artClouds, artPlaneElephant, artRightHill, artLeftHill, artCenterHill;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
motionEffect=new InterpolatingMotionEffect(MastodonApp.context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -37,15 +46,44 @@ public class SplashFragment extends AppKitFragment{
|
|||||||
contentView=(SizeListenerFrameLayout) inflater.inflate(R.layout.fragment_splash, container, false);
|
contentView=(SizeListenerFrameLayout) inflater.inflate(R.layout.fragment_splash, container, false);
|
||||||
contentView.findViewById(R.id.btn_get_started).setOnClickListener(this::onButtonClick);
|
contentView.findViewById(R.id.btn_get_started).setOnClickListener(this::onButtonClick);
|
||||||
contentView.findViewById(R.id.btn_log_in).setOnClickListener(this::onButtonClick);
|
contentView.findViewById(R.id.btn_log_in).setOnClickListener(this::onButtonClick);
|
||||||
|
artClouds=contentView.findViewById(R.id.art_clouds);
|
||||||
|
artPlaneElephant=contentView.findViewById(R.id.art_plane_elephant);
|
||||||
|
artRightHill=contentView.findViewById(R.id.art_right_hill);
|
||||||
|
artLeftHill=contentView.findViewById(R.id.art_left_hill);
|
||||||
|
artCenterHill=contentView.findViewById(R.id.art_center_hill);
|
||||||
|
pager=contentView.findViewById(R.id.pager);
|
||||||
|
pagerDots=contentView.findViewById(R.id.pager_dots);
|
||||||
|
pager.setAdapter(new PagerAdapter());
|
||||||
|
pager.setOffscreenPageLimit(3);
|
||||||
|
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
|
||||||
|
@Override
|
||||||
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels){
|
||||||
|
for(int i=0;i<pagerDots.getChildCount();i++){
|
||||||
|
float alpha;
|
||||||
|
if(i==position){
|
||||||
|
alpha=0.3f+0.7f*(1f-positionOffset);
|
||||||
|
}else if(i==position+1){
|
||||||
|
alpha=0.3f+0.7f*positionOffset;
|
||||||
|
}else{
|
||||||
|
alpha=0.3f;
|
||||||
|
}
|
||||||
|
pagerDots.getChildAt(i).setAlpha(alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
float parallaxProgress=(position+positionOffset)/2f;
|
||||||
|
artClouds.setTranslationX(V.dp(-27)*(position>=1 ? 1f : positionOffset));
|
||||||
|
artPlaneElephant.setTranslationX(V.dp(101.55f)*parallaxProgress);
|
||||||
|
artLeftHill.setTranslationX(V.dp(-88)*parallaxProgress);
|
||||||
|
artLeftHill.setTranslationY(V.dp(24)*parallaxProgress);
|
||||||
|
artRightHill.setTranslationX(V.dp(-88)*parallaxProgress);
|
||||||
|
artRightHill.setTranslationY(V.dp(-24)*parallaxProgress);
|
||||||
|
artCenterHill.setTranslationX(V.dp(-40)*parallaxProgress);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
artContainer=contentView.findViewById(R.id.art_container);
|
artContainer=contentView.findViewById(R.id.art_container);
|
||||||
blueFill=contentView.findViewById(R.id.blue_fill);
|
blueFill=contentView.findViewById(R.id.blue_fill);
|
||||||
greenFill=contentView.findViewById(R.id.green_fill);
|
greenFill=contentView.findViewById(R.id.green_fill);
|
||||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_clouds), V.dp(-5), V.dp(5), V.dp(-5), V.dp(5)));
|
|
||||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_right_hill), V.dp(-15), V.dp(25), V.dp(-10), V.dp(10)));
|
|
||||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_left_hill), V.dp(-25), V.dp(15), V.dp(-15), V.dp(15)));
|
|
||||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_center_hill), V.dp(-14), V.dp(14), V.dp(-5), V.dp(25)));
|
|
||||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_plane_elephant), V.dp(-20), V.dp(12), V.dp(-20), V.dp(12)));
|
|
||||||
|
|
||||||
contentView.setSizeListener(new SizeListenerFrameLayout.OnSizeChangedListener(){
|
contentView.setSizeListener(new SizeListenerFrameLayout.OnSizeChangedListener(){
|
||||||
@Override
|
@Override
|
||||||
@@ -72,10 +110,10 @@ public class SplashFragment extends AppKitFragment{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateArtSize(int w, int h){
|
private void updateArtSize(int w, int h){
|
||||||
float scale=w/(float)V.dp(412);
|
float scale=w/(float)V.dp(360);
|
||||||
artContainer.setScaleX(scale);
|
artContainer.setScaleX(scale);
|
||||||
artContainer.setScaleY(scale);
|
artContainer.setScaleY(scale);
|
||||||
blueFill.setScaleY(h/2f);
|
blueFill.setScaleY(artContainer.getBottom()-V.dp(90));
|
||||||
greenFill.setScaleY(h-artContainer.getBottom()+V.dp(90));
|
greenFill.setScaleY(h-artContainer.getBottom()+V.dp(90));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,15 +139,91 @@ public class SplashFragment extends AppKitFragment{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class PagerAdapter extends RecyclerView.Adapter<PagerViewHolder>{
|
||||||
protected void onShown(){
|
|
||||||
super.onShown();
|
@NonNull
|
||||||
motionEffect.activate();
|
@Override
|
||||||
|
public PagerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
|
return new PagerViewHolder(viewType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull PagerViewHolder holder, int position){}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount(){
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position){
|
||||||
|
return position;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class PagerViewHolder extends RecyclerView.ViewHolder{
|
||||||
protected void onHidden(){
|
public PagerViewHolder(int page){
|
||||||
super.onHidden();
|
super(new LinearLayout(getActivity()));
|
||||||
motionEffect.deactivate();
|
LinearLayout ll=(LinearLayout) itemView;
|
||||||
|
ll.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
int pad=V.dp(16);
|
||||||
|
ll.setPadding(pad, pad, pad, pad);
|
||||||
|
ll.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
|
||||||
|
TextView title=new TextView(getActivity());
|
||||||
|
title.setTextAppearance(R.style.m3_headline_medium);
|
||||||
|
title.setText(switch(page){
|
||||||
|
case 0 -> {
|
||||||
|
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 2 -> getString(R.string.welcome_page3_title);
|
||||||
|
default -> throw new IllegalStateException("Unexpected value: "+page);
|
||||||
|
});
|
||||||
|
title.setTextColor(0xFF17063B);
|
||||||
|
LinearLayout.LayoutParams lp=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(page==0 ? 46 : 36));
|
||||||
|
lp.bottomMargin=V.dp(page==0 ? 4 : 14);
|
||||||
|
ll.addView(title, lp);
|
||||||
|
|
||||||
|
TextView text=new TextView(getActivity());
|
||||||
|
text.setTextAppearance(R.style.m3_body_medium);
|
||||||
|
text.setText(switch(page){
|
||||||
|
case 0 -> R.string.welcome_page1_text;
|
||||||
|
case 1 -> R.string.welcome_page2_text;
|
||||||
|
case 2 -> R.string.welcome_page3_text;
|
||||||
|
default -> throw new IllegalStateException("Unexpected value: "+page);
|
||||||
|
});
|
||||||
|
text.setTextColor(0xFF17063B);
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
|
|||||||
public void onStatusCountersUpdated(StatusCountersUpdatedEvent ev){
|
public void onStatusCountersUpdated(StatusCountersUpdatedEvent ev){
|
||||||
for(Status s:data){
|
for(Status s:data){
|
||||||
if(s.getContentStatus().id.equals(ev.id)){
|
if(s.getContentStatus().id.equals(ev.id)){
|
||||||
s.update(ev);
|
s.getContentStatus().update(ev);
|
||||||
for(int i=0;i<list.getChildCount();i++){
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
||||||
if(holder instanceof FooterStatusDisplayItem.Holder footer && footer.getItem().status==s.getContentStatus()){
|
if(holder instanceof FooterStatusDisplayItem.Holder footer && footer.getItem().status==s.getContentStatus()){
|
||||||
@@ -189,8 +189,8 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(Status s:preloadedData){
|
for(Status s:preloadedData){
|
||||||
if(s.id.equals(ev.id)){
|
if(s.getContentStatus().id.equals(ev.id)){
|
||||||
s.update(ev);
|
s.getContentStatus().update(ev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.joinmastodon.android.R;
|
|||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||||
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
|
import org.joinmastodon.android.fragments.ListTimelinesFragment;
|
||||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||||
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
@@ -286,6 +287,7 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
|
|||||||
menu.findItem(R.id.mute).setTitle(getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
|
menu.findItem(R.id.mute).setTitle(getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
|
||||||
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
|
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
|
||||||
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getDisplayUsername()));
|
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getDisplayUsername()));
|
||||||
|
menu.findItem(R.id.manage_user_lists).setTitle(getString(R.string.sk_lists_with_user, account.getDisplayUsername())).setVisible(relationship.following);
|
||||||
MenuItem hideBoosts=menu.findItem(R.id.hide_boosts);
|
MenuItem hideBoosts=menu.findItem(R.id.hide_boosts);
|
||||||
MenuItem manageUserLists=menu.findItem(R.id.manage_user_lists);
|
MenuItem manageUserLists=menu.findItem(R.id.manage_user_lists);
|
||||||
if(relationship.following){
|
if(relationship.following){
|
||||||
@@ -372,6 +374,12 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
|
|||||||
})
|
})
|
||||||
.wrapProgress(getActivity(), R.string.loading, false)
|
.wrapProgress(getActivity(), R.string.loading, false)
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
|
}else if(id==R.id.manage_user_lists){
|
||||||
|
final Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
args.putString("profileAccount", account.id);
|
||||||
|
args.putString("profileDisplayUsername", account.getDisplayUsername());
|
||||||
|
Nav.go(getActivity(), ListTimelinesFragment.class, args);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
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));
|
||||||
|
|
||||||
pager.setOffscreenPageLimit(4);
|
pager.setOffscreenPageLimit(4);
|
||||||
|
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
||||||
pager.setAdapter(new DiscoverPagerAdapter());
|
pager.setAdapter(new DiscoverPagerAdapter());
|
||||||
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
|
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.joinmastodon.android.fragments.onboarding;
|
package org.joinmastodon.android.fragments.onboarding;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -12,19 +12,21 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.joinmastodon.android.MainActivity;
|
import org.joinmastodon.android.MainActivity;
|
||||||
import org.joinmastodon.android.MastodonApp;
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
||||||
import org.joinmastodon.android.api.requests.accounts.ResendConfirmationEmail;
|
import org.joinmastodon.android.api.requests.accounts.ResendConfirmationEmail;
|
||||||
import org.joinmastodon.android.api.requests.accounts.UpdateAccountCredentials;
|
import org.joinmastodon.android.api.requests.accounts.UpdateAccountCredentials;
|
||||||
|
import org.joinmastodon.android.api.session.AccountActivationInfo;
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
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.HomeFragment;
|
import org.joinmastodon.android.fragments.HomeFragment;
|
||||||
import org.joinmastodon.android.fragments.SettingsFragment;
|
import org.joinmastodon.android.fragments.SettingsFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
|
import org.joinmastodon.android.ui.AccountSwitcherSheet;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -35,40 +37,50 @@ import me.grishka.appkit.Nav;
|
|||||||
import me.grishka.appkit.api.APIRequest;
|
import me.grishka.appkit.api.APIRequest;
|
||||||
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.AppKitFragment;
|
import me.grishka.appkit.fragments.ToolbarFragment;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class AccountActivationFragment extends AppKitFragment{
|
public class AccountActivationFragment extends ToolbarFragment{
|
||||||
private String accountID;
|
private String accountID;
|
||||||
|
|
||||||
private Button btn, backBtn;
|
private Button openEmailBtn, resendBtn;
|
||||||
private View buttonBar;
|
private View contentView;
|
||||||
private Handler uiHandler=new Handler(Looper.getMainLooper());
|
private Handler uiHandler=new Handler(Looper.getMainLooper());
|
||||||
private Runnable pollRunnable=this::tryGetAccount;
|
private Runnable pollRunnable=this::tryGetAccount;
|
||||||
private APIRequest currentRequest;
|
private APIRequest currentRequest;
|
||||||
|
private Runnable resendTimer=this::updateResendTimer;
|
||||||
|
private long lastResendTime;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
accountID=getArguments().getString("account");
|
accountID=getArguments().getString("account");
|
||||||
|
setTitle(R.string.confirm_email_title);
|
||||||
|
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
|
lastResendTime=session.activationInfo!=null ? session.activationInfo.lastEmailConfirmationResend : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState){
|
public View onCreateContentView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState){
|
||||||
View view=inflater.inflate(R.layout.fragment_onboarding_activation, container, false);
|
View view=inflater.inflate(R.layout.fragment_onboarding_activation, container, false);
|
||||||
|
|
||||||
btn=view.findViewById(R.id.btn_next);
|
openEmailBtn=view.findViewById(R.id.btn_next);
|
||||||
btn.setOnClickListener(v->onButtonClick());
|
openEmailBtn.setOnClickListener(this::onOpenEmailClick);
|
||||||
btn.setOnLongClickListener(v->{
|
openEmailBtn.setOnLongClickListener(v->{
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
Nav.go(getActivity(), SettingsFragment.class, args);
|
Nav.go(getActivity(), SettingsFragment.class, args);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
buttonBar=view.findViewById(R.id.button_bar);
|
resendBtn=view.findViewById(R.id.btn_resend);
|
||||||
view.findViewById(R.id.btn_back).setOnClickListener(v->onBackButtonClick());
|
resendBtn.setOnClickListener(this::onResendClick);
|
||||||
|
TextView text=view.findViewById(R.id.subtitle);
|
||||||
|
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
|
text.setText(getString(R.string.confirm_email_subtitle, session.activationInfo!=null ? session.activationInfo.email : "?"));
|
||||||
|
updateResendTimer();
|
||||||
|
|
||||||
|
contentView=view;
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,14 +92,32 @@ public class AccountActivationFragment extends AppKitFragment{
|
|||||||
@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.colorBackgroundLight));
|
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
|
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
protected void onUpdateToolbar(){
|
||||||
|
// super.onUpdateToolbar();
|
||||||
|
getToolbar().setBackground(null);
|
||||||
|
getToolbar().setElevation(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canGoBack(){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onToolbarNavigationClick(){
|
||||||
|
new AccountSwitcherSheet(getActivity()).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApplyWindowInsets(WindowInsets insets){
|
public void onApplyWindowInsets(WindowInsets insets){
|
||||||
if(Build.VERSION.SDK_INT>=27){
|
if(Build.VERSION.SDK_INT>=27){
|
||||||
int inset=insets.getSystemWindowInsetBottom();
|
int inset=insets.getSystemWindowInsetBottom();
|
||||||
buttonBar.setPadding(0, 0, 0, inset>0 ? Math.max(inset, V.dp(36)) : 0);
|
contentView.setPadding(0, 0, 0, inset>0 ? Math.max(inset, V.dp(36)) : 0);
|
||||||
super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), 0));
|
super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), 0));
|
||||||
}else{
|
}else{
|
||||||
super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()));
|
super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()));
|
||||||
@@ -111,7 +141,7 @@ public class AccountActivationFragment extends AppKitFragment{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onButtonClick(){
|
private void onOpenEmailClick(View v){
|
||||||
try{
|
try{
|
||||||
startActivity(Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, Intent.CATEGORY_APP_EMAIL).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
startActivity(Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, Intent.CATEGORY_APP_EMAIL).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||||
}catch(ActivityNotFoundException x){
|
}catch(ActivityNotFoundException x){
|
||||||
@@ -119,12 +149,21 @@ public class AccountActivationFragment extends AppKitFragment{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onBackButtonClick(){
|
private void onResendClick(View v){
|
||||||
new ResendConfirmationEmail(null)
|
new ResendConfirmationEmail(null)
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Object result){
|
public void onSuccess(Object result){
|
||||||
Toast.makeText(getActivity(), R.string.resent_email, Toast.LENGTH_SHORT).show();
|
Toast.makeText(getActivity(), R.string.resent_email, Toast.LENGTH_SHORT).show();
|
||||||
|
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
|
if(session.activationInfo==null){
|
||||||
|
session.activationInfo=new AccountActivationInfo("?", System.currentTimeMillis());
|
||||||
|
}else{
|
||||||
|
session.activationInfo.lastEmailConfirmationResend=System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
lastResendTime=session.activationInfo.lastEmailConfirmationResend;
|
||||||
|
AccountSessionManager.getInstance().writeAccountsFile();
|
||||||
|
updateResendTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -152,7 +191,7 @@ public class AccountActivationFragment extends AppKitFragment{
|
|||||||
AccountSessionManager mgr=AccountSessionManager.getInstance();
|
AccountSessionManager mgr=AccountSessionManager.getInstance();
|
||||||
AccountSession session=mgr.getAccount(accountID);
|
AccountSession session=mgr.getAccount(accountID);
|
||||||
mgr.removeAccount(accountID);
|
mgr.removeAccount(accountID);
|
||||||
mgr.addAccount(mgr.getInstanceInfo(session.domain), session.token, result, session.app, true);
|
mgr.addAccount(mgr.getInstanceInfo(session.domain), session.token, result, session.app, null);
|
||||||
String newID=mgr.getLastActiveAccountID();
|
String newID=mgr.getLastActiveAccountID();
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", newID);
|
args.putString("account", newID);
|
||||||
@@ -189,4 +228,25 @@ public class AccountActivationFragment extends AppKitFragment{
|
|||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("DefaultLocale")
|
||||||
|
private void updateResendTimer(){
|
||||||
|
long sinceResend=System.currentTimeMillis()-lastResendTime;
|
||||||
|
if(sinceResend>59_000L){
|
||||||
|
resendBtn.setText(R.string.resend);
|
||||||
|
resendBtn.setEnabled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int seconds=(int)((60_000L-sinceResend)/1000L);
|
||||||
|
resendBtn.setText(String.format("%s (%d)", getString(R.string.resend), seconds));
|
||||||
|
if(resendBtn.isEnabled())
|
||||||
|
resendBtn.setEnabled(false);
|
||||||
|
resendBtn.postDelayed(resendTimer, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView(){
|
||||||
|
super.onDestroyView();
|
||||||
|
resendBtn.removeCallbacks(resendTimer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,11 +36,11 @@ import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
|||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class MoshidonLoginFragment extends InstanceCatalogFragment {
|
public class CustomLoginFragment extends InstanceCatalogFragment {
|
||||||
private View headerView;
|
private View headerView;
|
||||||
|
|
||||||
public MoshidonLoginFragment() {
|
public CustomLoginFragment() {
|
||||||
super(R.layout.fragment_moshidon_welcome, 1);
|
super(R.layout.fragment_welcome_custom, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -131,13 +131,13 @@ public class MoshidonLoginFragment extends InstanceCatalogFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RecyclerView.Adapter getAdapter(){
|
protected RecyclerView.Adapter getAdapter(){
|
||||||
headerView=getActivity().getLayoutInflater().inflate(R.layout.header_megalodon_welcome, list, false);
|
headerView=getActivity().getLayoutInflater().inflate(R.layout.header_welcome_custom, list, false);
|
||||||
searchEdit=headerView.findViewById(R.id.search_edit);
|
searchEdit=headerView.findViewById(R.id.search_edit);
|
||||||
searchEdit.setOnEditorActionListener(this::onSearchEnterPressed);
|
searchEdit.setOnEditorActionListener(this::onSearchEnterPressed);
|
||||||
|
|
||||||
headerView.findViewById(R.id.more).setVisibility(View.GONE);
|
headerView.findViewById(R.id.more).setVisibility(View.GONE);
|
||||||
headerView.findViewById(R.id.visibility).setVisibility(View.GONE);
|
headerView.findViewById(R.id.visibility).setVisibility(View.GONE);
|
||||||
((TextView) headerView.findViewById(R.id.username)).setText("@megalodon");
|
((TextView) headerView.findViewById(R.id.username)).setText("@moshidon");
|
||||||
((TextView) headerView.findViewById(R.id.name)).setText(R.string.sk_app_name);
|
((TextView) headerView.findViewById(R.id.name)).setText(R.string.sk_app_name);
|
||||||
((TextView) headerView.findViewById(R.id.timestamp)).setText(R.string.time_now);
|
((TextView) headerView.findViewById(R.id.timestamp)).setText(R.string.time_now);
|
||||||
((ImageView) headerView.findViewById(R.id.avatar)).setImageDrawable(getActivity().getDrawable(R.mipmap.ic_launcher));
|
((ImageView) headerView.findViewById(R.id.avatar)).setImageDrawable(getActivity().getDrawable(R.mipmap.ic_launcher));
|
||||||
@@ -203,7 +203,7 @@ public class MoshidonLoginFragment extends InstanceCatalogFragment {
|
|||||||
private final RadioButton radioButton;
|
private final RadioButton radioButton;
|
||||||
|
|
||||||
public InstanceViewHolder(){
|
public InstanceViewHolder(){
|
||||||
super(getActivity(), R.layout.item_megalodon_instance, list);
|
super(getActivity(), R.layout.item_instance_custom, list);
|
||||||
|
|
||||||
// itemView.setPadding(V.dp(16), V.dp(16), V.dp(16), V.dp(16));
|
// itemView.setPadding(V.dp(16), V.dp(16), V.dp(16), V.dp(16));
|
||||||
// TypedValue value = new TypedValue();
|
// TypedValue value = new TypedValue();
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.joinmastodon.android.fragments.onboarding;
|
package org.joinmastodon.android.fragments.onboarding;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
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;
|
||||||
@@ -15,6 +16,7 @@ import android.widget.TextView;
|
|||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
|
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.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
@@ -33,6 +35,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.fragments.AppKitFragment;
|
import me.grishka.appkit.fragments.AppKitFragment;
|
||||||
|
import me.grishka.appkit.fragments.ToolbarFragment;
|
||||||
import me.grishka.appkit.imageloader.ViewImageLoader;
|
import me.grishka.appkit.imageloader.ViewImageLoader;
|
||||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
@@ -46,7 +49,7 @@ import okhttp3.Request;
|
|||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import okhttp3.ResponseBody;
|
import okhttp3.ResponseBody;
|
||||||
|
|
||||||
public class GoogleMadeMeAddThisFragment extends AppKitFragment{
|
public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
|
||||||
private UsableRecyclerView list;
|
private UsableRecyclerView list;
|
||||||
private MergeRecyclerAdapter adapter;
|
private MergeRecyclerAdapter adapter;
|
||||||
private Button btn;
|
private Button btn;
|
||||||
@@ -60,6 +63,7 @@ public class GoogleMadeMeAddThisFragment extends AppKitFragment{
|
|||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
|
setTitle(R.string.privacy_policy_title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -82,37 +86,24 @@ public class GoogleMadeMeAddThisFragment extends AppKitFragment{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
||||||
View view=inflater.inflate(R.layout.fragment_onboarding_rules, container, false);
|
View view=inflater.inflate(R.layout.fragment_onboarding_rules, container, false);
|
||||||
|
|
||||||
list=view.findViewById(R.id.list);
|
list=view.findViewById(R.id.list);
|
||||||
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||||
View headerView=inflater.inflate(R.layout.item_list_header, list, false);
|
View headerView=inflater.inflate(R.layout.item_list_header_simple, list, false);
|
||||||
TextView title=headerView.findViewById(R.id.title);
|
TextView text=headerView.findViewById(R.id.text);
|
||||||
TextView subtitle=headerView.findViewById(R.id.subtitle);
|
text.setText(R.string.privacy_policy_subtitle);
|
||||||
headerView.findViewById(R.id.step_counter).setVisibility(View.GONE);
|
|
||||||
title.setText(R.string.privacy_policy_title);
|
|
||||||
subtitle.setText(R.string.privacy_policy_subtitle);
|
|
||||||
|
|
||||||
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.setSelector(null);
|
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorM3SurfaceVariant, 1, 56, 0, DividerItemDecoration.NOT_FIRST));
|
||||||
list.addItemDecoration(new RecyclerView.ItemDecoration(){
|
|
||||||
@Override
|
|
||||||
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
|
|
||||||
if(parent.getChildViewHolder(view) instanceof ItemViewHolder){
|
|
||||||
outRect.left=outRect.right=V.dp(18.5f);
|
|
||||||
outRect.top=V.dp(16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -120,7 +111,15 @@ public class GoogleMadeMeAddThisFragment extends AppKitFragment{
|
|||||||
@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.colorBackgroundLight));
|
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
|
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
protected void onUpdateToolbar(){
|
||||||
|
// super.onUpdateToolbar();
|
||||||
|
getToolbar().setBackground(null);
|
||||||
|
getToolbar().setElevation(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onButtonClick(){
|
protected void onButtonClick(){
|
||||||
@@ -192,24 +191,17 @@ public class GoogleMadeMeAddThisFragment extends AppKitFragment{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class ItemViewHolder extends BindableViewHolder<Item> implements UsableRecyclerView.Clickable{
|
private class ItemViewHolder extends BindableViewHolder<Item> implements UsableRecyclerView.Clickable{
|
||||||
private final TextView domain, title;
|
private final TextView title;
|
||||||
private final ImageView favicon;
|
|
||||||
|
|
||||||
public ItemViewHolder(){
|
public ItemViewHolder(){
|
||||||
super(getActivity(), R.layout.item_privacy_policy_link, list);
|
super(getActivity(), R.layout.item_privacy_policy_link, list);
|
||||||
domain=findViewById(R.id.domain);
|
|
||||||
title=findViewById(R.id.title);
|
title=findViewById(R.id.title);
|
||||||
favicon=findViewById(R.id.favicon);
|
title.setPaintFlags(title.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
||||||
itemView.setOutlineProvider(OutlineProviders.roundedRect(10));
|
|
||||||
itemView.setClipToOutline(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(Item item){
|
public void onBind(Item item){
|
||||||
domain.setText(item.domain);
|
|
||||||
title.setText(item.title);
|
title.setText(item.title);
|
||||||
|
|
||||||
ViewImageLoader.load(favicon, null, new UrlImageLoaderRequest(item.faviconUrl));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -187,6 +187,8 @@ abstract class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInsta
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void loadInstanceInfo(String _domain, boolean isFromRedirect){
|
protected void loadInstanceInfo(String _domain, boolean isFromRedirect){
|
||||||
|
if(TextUtils.isEmpty(_domain))
|
||||||
|
return;
|
||||||
String domain=normalizeInstanceDomain(_domain);
|
String domain=normalizeInstanceDomain(_domain);
|
||||||
Instance cachedInstance=instancesCache.get(domain);
|
Instance cachedInstance=instancesCache.get(domain);
|
||||||
if(cachedInstance!=null){
|
if(cachedInstance!=null){
|
||||||
@@ -222,7 +224,7 @@ abstract class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInsta
|
|||||||
}
|
}
|
||||||
if(Objects.equals(domain, currentSearchQuery) || Objects.equals(currentSearchQuery, redirects.get(domain)) || Objects.equals(currentSearchQuery, redirectsInverse.get(domain))){
|
if(Objects.equals(domain, currentSearchQuery) || Objects.equals(currentSearchQuery, redirects.get(domain)) || Objects.equals(currentSearchQuery, redirectsInverse.get(domain))){
|
||||||
boolean found=false;
|
boolean found=false;
|
||||||
for(CatalogInstance ci : filteredData){
|
for(CatalogInstance ci:filteredData){
|
||||||
if(ci.domain.equals(domain) && ci!=fakeInstance){
|
if(ci.domain.equals(domain) && ci!=fakeInstance){
|
||||||
found=true;
|
found=true;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,39 +1,52 @@
|
|||||||
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.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
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.os.LocaleList;
|
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
|
import android.view.Menu;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.WindowInsets;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.HorizontalScrollView;
|
||||||
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.PopupMenu;
|
||||||
import android.widget.RadioButton;
|
import android.widget.RadioButton;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
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.catalog.GetCatalogCategories;
|
import org.joinmastodon.android.api.requests.catalog.GetCatalogCategories;
|
||||||
import org.joinmastodon.android.api.requests.catalog.GetCatalogInstances;
|
import org.joinmastodon.android.api.requests.catalog.GetCatalogInstances;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.catalog.CatalogCategory;
|
import org.joinmastodon.android.model.catalog.CatalogCategory;
|
||||||
import org.joinmastodon.android.model.catalog.CatalogInstance;
|
import org.joinmastodon.android.model.catalog.CatalogInstance;
|
||||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.tabs.TabLayout;
|
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.joinmastodon.android.ui.views.FilterChipView;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Locale;
|
||||||
|
import java.util.Random;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -42,18 +55,36 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
import me.grishka.appkit.Nav;
|
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.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.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class InstanceCatalogSignupFragment extends InstanceCatalogFragment{
|
public class InstanceCatalogSignupFragment extends InstanceCatalogFragment implements OnBackPressedListener{
|
||||||
private View headerView;
|
|
||||||
private MastodonAPIRequest<?> getCategoriesRequest;
|
private MastodonAPIRequest<?> getCategoriesRequest;
|
||||||
private TabLayout categoriesList;
|
|
||||||
private String currentCategory="all";
|
private String currentCategory="all";
|
||||||
private List<CatalogCategory> categories=new ArrayList<>();
|
private List<CatalogCategory> categories=new ArrayList<>();
|
||||||
|
private View topBar;
|
||||||
|
|
||||||
|
private List<String> languages=Collections.emptyList();
|
||||||
|
private PopupMenu langFilterMenu, speedFilterMenu;
|
||||||
|
private SignupSpeedFilter currentSignupSpeedFilter=SignupSpeedFilter.INSTANT;
|
||||||
|
private String currentLanguage=null;
|
||||||
|
private boolean searchQueryMode;
|
||||||
|
private LinearLayout filtersWrap;
|
||||||
|
private HorizontalScrollView filtersScroll;
|
||||||
|
private ImageButton backBtn, clearSearchBtn;
|
||||||
|
|
||||||
|
private FilterChipView categoryGeneral, categorySpecialInterests;
|
||||||
|
private List<FilterChipView> regionalFilters;
|
||||||
|
private CatalogInstance.Region chosenRegion;
|
||||||
|
private CategoryChoice categoryChoice;
|
||||||
|
|
||||||
public InstanceCatalogSignupFragment(){
|
public InstanceCatalogSignupFragment(){
|
||||||
super(R.layout.fragment_onboarding_common, 10);
|
super(R.layout.fragment_onboarding_common, 10);
|
||||||
@@ -63,6 +94,12 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment{
|
|||||||
public void onAttach(Context context){
|
public void onAttach(Context context){
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
setRefreshEnabled(false);
|
setRefreshEnabled(false);
|
||||||
|
setRetainInstance(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +112,25 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment{
|
|||||||
if(getActivity()==null)
|
if(getActivity()==null)
|
||||||
return;
|
return;
|
||||||
onDataLoaded(sortInstances(result), false);
|
onDataLoaded(sortInstances(result), false);
|
||||||
|
|
||||||
|
if(langFilterMenu!=null){
|
||||||
|
Menu menu=langFilterMenu.getMenu();
|
||||||
|
menu.clear();
|
||||||
|
menu.add(0, 0, 0, R.string.server_filter_any_language);
|
||||||
|
languages=result.stream().map(i->i.language).distinct().filter(s->s.length()>0).sorted().collect(Collectors.toList());
|
||||||
|
int i=1;
|
||||||
|
for(String lang:languages){
|
||||||
|
Locale locale=Locale.forLanguageTag(lang);
|
||||||
|
String name=locale.getDisplayLanguage(locale);
|
||||||
|
if(name.equals(lang))
|
||||||
|
name=lang.toUpperCase();
|
||||||
|
else
|
||||||
|
name=name.substring(0, 1).toUpperCase()+name.substring(1);
|
||||||
|
menu.add(0, i, 0, name);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateFilteredList();
|
updateFilteredList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,14 +167,14 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateCategories(){
|
private void updateCategories(){
|
||||||
categoriesList.removeAllTabs();
|
// categoriesList.removeAllTabs();
|
||||||
for(CatalogCategory cat:categories){
|
// for(CatalogCategory cat:categories){
|
||||||
int titleRes=getTitleForCategory(cat.category);
|
// int titleRes=getTitleForCategory(cat.category);
|
||||||
TabLayout.Tab tab=categoriesList.newTab().setText(titleRes!=0 ? getString(titleRes) : cat.category).setCustomView(R.layout.item_instance_category);
|
// TabLayout.Tab tab=categoriesList.newTab().setText(titleRes!=0 ? getString(titleRes) : cat.category).setCustomView(R.layout.item_instance_category);
|
||||||
ImageView emoji=tab.getCustomView().findViewById(R.id.emoji);
|
// ImageView emoji=tab.getCustomView().findViewById(R.id.emoji);
|
||||||
emoji.setImageResource(getEmojiForCategory(cat.category));
|
// emoji.setImageResource(getEmojiForCategory(cat.category));
|
||||||
categoriesList.addTab(tab);
|
// categoriesList.addTab(tab);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -130,27 +186,77 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RecyclerView.Adapter getAdapter(){
|
protected RecyclerView.Adapter getAdapter(){
|
||||||
headerView=getActivity().getLayoutInflater().inflate(R.layout.header_onboarding_instance_catalog, list, false);
|
View headerView=new View(getActivity());
|
||||||
searchEdit=headerView.findViewById(R.id.search_edit);
|
headerView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1));
|
||||||
categoriesList=headerView.findViewById(R.id.categories_list);
|
|
||||||
categoriesList.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
|
|
||||||
@Override
|
|
||||||
public void onTabSelected(TabLayout.Tab tab){
|
|
||||||
CatalogCategory category=categories.get(tab.getPosition());
|
|
||||||
currentCategory=category.category;
|
|
||||||
updateFilteredList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
mergeAdapter=new MergeRecyclerAdapter();
|
||||||
public void onTabUnselected(TabLayout.Tab tab){
|
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
|
||||||
|
mergeAdapter.addAdapter(adapter=new InstancesAdapter());
|
||||||
}
|
return mergeAdapter;
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public void onTabReselected(TabLayout.Tab tab){
|
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
backBtn=view.findViewById(R.id.btn_back);
|
||||||
|
backBtn.setOnClickListener(v->{
|
||||||
|
if(searchQueryMode){
|
||||||
|
setSearchQueryMode(false);
|
||||||
|
}else{
|
||||||
|
Nav.finish(this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
clearSearchBtn=view.findViewById(R.id.clear);
|
||||||
|
clearSearchBtn.setOnClickListener(v->searchEdit.setText(""));
|
||||||
|
nextButton.setEnabled(true);
|
||||||
|
list.setItemAnimator(new BetterItemAnimator());
|
||||||
|
setStatusBarColor(0);
|
||||||
|
topBar=view.findViewById(R.id.top_bar);
|
||||||
|
|
||||||
|
LayerDrawable topBg=(LayerDrawable) topBar.getBackground().mutate();
|
||||||
|
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.setOnEditorActionListener(this::onSearchEnterPressed);
|
searchEdit.setOnEditorActionListener(this::onSearchEnterPressed);
|
||||||
searchEdit.addTextChangedListener(new TextWatcher(){
|
searchEdit.addTextChangedListener(new TextWatcher(){
|
||||||
@Override
|
@Override
|
||||||
@@ -166,42 +272,145 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterTextChanged(Editable s){
|
public void afterTextChanged(Editable s){
|
||||||
|
if((clearSearchBtn.getVisibility()==View.VISIBLE)!=(s.length()>0)){
|
||||||
|
clearSearchBtn.setVisibility(s.length()>0 ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
searchEdit.setOnFocusChangeListener((v, hasFocus)->{
|
||||||
|
if(hasFocus && !searchQueryMode){
|
||||||
|
setSearchQueryMode(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
mergeAdapter=new MergeRecyclerAdapter();
|
FilterChipView langFilter=new FilterChipView(getActivity());
|
||||||
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
|
langFilter.setDrawableEnd(R.drawable.ic_baseline_arrow_drop_down_18);
|
||||||
mergeAdapter.addAdapter(adapter=new InstancesAdapter());
|
langFilter.setText(R.string.server_filter_any_language);
|
||||||
return mergeAdapter;
|
langFilterMenu=new PopupMenu(getContext(), langFilter);
|
||||||
|
langFilter.setOnTouchListener(langFilterMenu.getDragToOpenListener());
|
||||||
|
langFilter.setOnClickListener(v->langFilterMenu.show());
|
||||||
|
filtersWrap=view.findViewById(R.id.filters_container);
|
||||||
|
filtersScroll=view.findViewById(R.id.filters_scroll);
|
||||||
|
filtersWrap.addView(langFilter, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
FilterChipView speedFilter=new FilterChipView(getActivity());
|
||||||
|
speedFilter.setDrawableEnd(R.drawable.ic_baseline_arrow_drop_down_18);
|
||||||
|
speedFilterMenu=new PopupMenu(getContext(), speedFilter);
|
||||||
|
speedFilterMenu.getMenu().add(0, 0, 0, R.string.server_filter_any_signup_speed);
|
||||||
|
speedFilterMenu.getMenu().add(0, 1, 0, R.string.server_filter_instant_signup);
|
||||||
|
speedFilterMenu.getMenu().add(0, 2, 0, R.string.server_filter_manual_review);
|
||||||
|
speedFilter.setOnTouchListener(speedFilterMenu.getDragToOpenListener());
|
||||||
|
speedFilter.setOnClickListener(v->speedFilterMenu.show());
|
||||||
|
speedFilter.setText(R.string.server_filter_instant_signup);
|
||||||
|
speedFilter.setSelected(true);
|
||||||
|
filtersWrap.addView(speedFilter, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
speedFilterMenu.setOnMenuItemClickListener(item->{
|
||||||
|
speedFilter.setText(item.getTitle());
|
||||||
|
speedFilter.setSelected(item.getItemId()>0);
|
||||||
|
currentSignupSpeedFilter=SignupSpeedFilter.values()[item.getItemId()];
|
||||||
|
updateFilteredList();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
langFilterMenu.setOnMenuItemClickListener(item->{
|
||||||
|
langFilter.setText(item.getTitle());
|
||||||
|
langFilter.setSelected(item.getItemId()>0);
|
||||||
|
currentLanguage=item.getItemId()==0 ? null : languages.get(item.getItemId()-1);
|
||||||
|
updateFilteredList();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
View divider=new View(getActivity());
|
||||||
|
divider.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Outline));
|
||||||
|
filtersWrap.addView(divider, new LinearLayout.LayoutParams(V.dp(.5f), ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
|
||||||
|
categoryGeneral=new FilterChipView(getActivity());
|
||||||
|
categoryGeneral.setText(R.string.category_general);
|
||||||
|
categoryGeneral.setTag(CategoryChoice.GENERAL);
|
||||||
|
categoryGeneral.setOnClickListener(this::onCategoryFilterClick);
|
||||||
|
filtersWrap.addView(categoryGeneral, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
categorySpecialInterests=new FilterChipView(getActivity());
|
||||||
|
categorySpecialInterests.setText(R.string.category_special_interests);
|
||||||
|
categorySpecialInterests.setTag(CategoryChoice.SPECIAL);
|
||||||
|
categorySpecialInterests.setOnClickListener(this::onCategoryFilterClick);
|
||||||
|
filtersWrap.addView(categorySpecialInterests, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
regionalFilters=Arrays.stream(CatalogInstance.Region.values()).map(r->{
|
||||||
|
FilterChipView fv=new FilterChipView(getActivity());
|
||||||
|
fv.setTag(r);
|
||||||
|
fv.setText(switch(r){
|
||||||
|
case EUROPE -> R.string.server_filter_region_europe;
|
||||||
|
case NORTH_AMERICA -> R.string.server_filter_region_north_america;
|
||||||
|
case SOUTH_AMERICA -> R.string.server_filter_region_south_america;
|
||||||
|
case AFRICA -> R.string.server_filter_region_africa;
|
||||||
|
case ASIA -> R.string.server_filter_region_asia;
|
||||||
|
case OCEANIA -> R.string.server_filter_region_oceania;
|
||||||
|
});
|
||||||
|
fv.setSelected(r==chosenRegion);
|
||||||
|
fv.setOnClickListener(this::onRegionFilterClick);
|
||||||
|
filtersWrap.addView(fv, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
return fv;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onRegionFilterClick(View v){
|
||||||
|
CatalogInstance.Region r=(CatalogInstance.Region) v.getTag();
|
||||||
|
if(chosenRegion==r){
|
||||||
|
chosenRegion=null;
|
||||||
|
v.setSelected(false);
|
||||||
|
}else{
|
||||||
|
if(chosenRegion!=null)
|
||||||
|
filtersWrap.findViewWithTag(chosenRegion).setSelected(false);
|
||||||
|
chosenRegion=r;
|
||||||
|
v.setSelected(true);
|
||||||
|
}
|
||||||
|
updateFilteredList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onCategoryFilterClick(View v){
|
||||||
|
CategoryChoice c=(CategoryChoice) v.getTag();
|
||||||
|
if(categoryChoice==c){
|
||||||
|
categoryChoice=null;
|
||||||
|
v.setSelected(false);
|
||||||
|
}else{
|
||||||
|
if(categoryChoice!=null)
|
||||||
|
filtersWrap.findViewWithTag(categoryChoice).setSelected(false);
|
||||||
|
categoryChoice=c;
|
||||||
|
v.setSelected(true);
|
||||||
|
}
|
||||||
|
updateFilteredList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
protected void onNextClick(View v){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
if(chosenInstance==null){
|
||||||
view.findViewById(R.id.btn_back).setOnClickListener(v->Nav.finish(this));
|
String lang=Locale.getDefault().getLanguage();
|
||||||
list.setItemAnimator(new BetterItemAnimator());
|
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());
|
||||||
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 1, 16, 16, DividerItemDecoration.NOT_FIRST));
|
if(instances.isEmpty()){
|
||||||
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLight));
|
instances=data.stream().filter(ci->!ci.approvalRequired && ("general".equals(ci.category) || (ci.categories!=null && ci.categories.contains("general")))).collect(Collectors.toList());
|
||||||
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLight));
|
}
|
||||||
|
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);
|
||||||
if(isSignup){
|
if(!instance.registrations){
|
||||||
if(!instance.registrations){
|
new M3AlertDialogBuilder(getActivity())
|
||||||
new M3AlertDialogBuilder(getActivity())
|
.setTitle(R.string.error)
|
||||||
.setTitle(R.string.error)
|
.setMessage(R.string.instance_signup_closed)
|
||||||
.setMessage(R.string.instance_signup_closed)
|
.setPositiveButton(R.string.ok, null)
|
||||||
.setPositiveButton(R.string.ok, null)
|
.show();
|
||||||
.show();
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
args.putParcelable("instance", Parcels.wrap(instance));
|
|
||||||
Nav.go(getActivity(), InstanceRulesFragment.class, args);
|
|
||||||
}else{
|
|
||||||
}
|
}
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putParcelable("instance", Parcels.wrap(instance));
|
||||||
|
Nav.go(getActivity(), InstanceRulesFragment.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// private String getEmojiForCategory(String category){
|
// private String getEmojiForCategory(String category){
|
||||||
@@ -265,11 +474,29 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment{
|
|||||||
protected void updateFilteredList(){
|
protected void updateFilteredList(){
|
||||||
ArrayList<CatalogInstance> prevData=new ArrayList<>(filteredData);
|
ArrayList<CatalogInstance> prevData=new ArrayList<>(filteredData);
|
||||||
filteredData.clear();
|
filteredData.clear();
|
||||||
for(CatalogInstance instance:data){
|
if(searchQueryMode){
|
||||||
if(currentCategory.equals("all") || instance.categories.contains(currentCategory)){
|
if(!TextUtils.isEmpty(currentSearchQuery)){
|
||||||
if(TextUtils.isEmpty(currentSearchQuery) || instance.domain.contains(currentSearchQuery)){
|
for(CatalogInstance instance:data){
|
||||||
if(instance.domain.equals(currentSearchQuery) || !isSignup || !instance.approvalRequired)
|
if(instance.domain.contains(currentSearchQuery)){
|
||||||
filteredData.add(instance);
|
filteredData.add(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
for(CatalogInstance instance:data){
|
||||||
|
if(categoryChoice==null || categoryChoice.matches(instance.category)){
|
||||||
|
if(chosenRegion==null || instance.region==chosenRegion){
|
||||||
|
boolean signupSpeedMatches=switch(currentSignupSpeedFilter){
|
||||||
|
case ANY -> true;
|
||||||
|
case INSTANT -> !instance.approvalRequired;
|
||||||
|
case REVIEWED -> instance.approvalRequired;
|
||||||
|
};
|
||||||
|
if(signupSpeedMatches){
|
||||||
|
if(currentLanguage==null || instance.languages.contains(currentLanguage)){
|
||||||
|
filteredData.add(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -296,8 +523,46 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment{
|
|||||||
}).dispatchUpdatesTo(adapter);
|
}).dispatchUpdatesTo(adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplyWindowInsets(WindowInsets insets){
|
||||||
|
topBar.setPadding(0, insets.getSystemWindowInsetTop(), 0, 0);
|
||||||
|
super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()));
|
||||||
|
}
|
||||||
|
|
||||||
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceCatalogSignupFragment.InstanceViewHolder>{
|
@Override
|
||||||
|
public boolean onBackPressed(){
|
||||||
|
if(searchQueryMode){
|
||||||
|
setSearchQueryMode(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSearchQueryMode(boolean enabled){
|
||||||
|
searchQueryMode=enabled;
|
||||||
|
RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) searchEdit.getLayoutParams();
|
||||||
|
if(searchQueryMode){
|
||||||
|
filtersScroll.setVisibility(View.GONE);
|
||||||
|
lp.removeRule(RelativeLayout.END_OF);
|
||||||
|
backBtn.setScaleX(0.83333333f);
|
||||||
|
backBtn.setScaleY(0.83333333f);
|
||||||
|
backBtn.setTranslationX(V.dp(8));
|
||||||
|
searchEdit.setCompoundDrawableTintList(ColorStateList.valueOf(0));
|
||||||
|
}else{
|
||||||
|
filtersScroll.setVisibility(View.VISIBLE);
|
||||||
|
searchEdit.clearFocus();
|
||||||
|
searchEdit.setText("");
|
||||||
|
lp.addRule(RelativeLayout.END_OF, R.id.btn_back);
|
||||||
|
getActivity().getSystemService(InputMethodManager.class).hideSoftInputFromWindow(searchEdit.getWindowToken(), 0);
|
||||||
|
backBtn.setScaleX(1);
|
||||||
|
backBtn.setScaleY(1);
|
||||||
|
backBtn.setTranslationX(0);
|
||||||
|
searchEdit.setCompoundDrawableTintList(ColorStateList.valueOf(UiUtils.getThemeColor(getActivity(), R.attr.colorM3OnSurfaceVariant)));
|
||||||
|
}
|
||||||
|
updateFilteredList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceCatalogSignupFragment.InstanceViewHolder> implements ImageLoaderRecyclerAdapter{
|
||||||
public InstancesAdapter(){
|
public InstancesAdapter(){
|
||||||
super(imgLoader);
|
super(imgLoader);
|
||||||
}
|
}
|
||||||
@@ -323,32 +588,53 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment{
|
|||||||
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.Clickable{
|
private class InstanceViewHolder extends BindableViewHolder<CatalogInstance> implements UsableRecyclerView.DisableableClickable, ImageLoaderViewHolder{
|
||||||
private final TextView title, description, userCount, lang;
|
private final TextView title, description;
|
||||||
private final RadioButton radioButton;
|
private final RadioButton radioButton;
|
||||||
|
private final ImageView thumbnail;
|
||||||
|
private boolean enabled;
|
||||||
|
|
||||||
public InstanceViewHolder(){
|
public InstanceViewHolder(){
|
||||||
super(getActivity(), R.layout.item_instance_catalog, list);
|
super(getActivity(), R.layout.item_instance_catalog, list);
|
||||||
title=findViewById(R.id.title);
|
title=findViewById(R.id.title);
|
||||||
description=findViewById(R.id.description);
|
description=findViewById(R.id.description);
|
||||||
userCount=findViewById(R.id.user_count);
|
|
||||||
lang=findViewById(R.id.lang);
|
|
||||||
radioButton=findViewById(R.id.radiobtn);
|
radioButton=findViewById(R.id.radiobtn);
|
||||||
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N){
|
thumbnail=findViewById(R.id.image);
|
||||||
UiUtils.fixCompoundDrawableTintOnAndroid6(userCount);
|
|
||||||
UiUtils.fixCompoundDrawableTintOnAndroid6(lang);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(CatalogInstance item){
|
public void onBind(CatalogInstance item){
|
||||||
title.setText(item.normalizedDomain);
|
title.setText(item.normalizedDomain);
|
||||||
description.setText(item.description);
|
|
||||||
userCount.setText(UiUtils.abbreviateNumber(item.totalUsers));
|
|
||||||
lang.setText(item.language.toUpperCase());
|
|
||||||
radioButton.setChecked(chosenInstance==item);
|
radioButton.setChecked(chosenInstance==item);
|
||||||
|
if(item.thumbnailRequest==null)
|
||||||
|
thumbnail.setImageDrawable(null);
|
||||||
|
Instance realInstance=instancesCache.get(item.normalizedDomain);
|
||||||
|
float alpha;
|
||||||
|
if(realInstance!=null && !realInstance.registrations){
|
||||||
|
alpha=0.38f;
|
||||||
|
description.setText(R.string.not_accepting_new_members);
|
||||||
|
enabled=false;
|
||||||
|
}else{
|
||||||
|
alpha=1f;
|
||||||
|
description.setText(item.description);
|
||||||
|
enabled=true;
|
||||||
|
}
|
||||||
|
title.setAlpha(alpha);
|
||||||
|
description.setAlpha(alpha);
|
||||||
|
radioButton.setAlpha(alpha);
|
||||||
|
thumbnail.setAlpha(alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -358,10 +644,17 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment{
|
|||||||
if(chosenInstance!=null){
|
if(chosenInstance!=null){
|
||||||
int idx=filteredData.indexOf(chosenInstance);
|
int idx=filteredData.indexOf(chosenInstance);
|
||||||
if(idx!=-1){
|
if(idx!=-1){
|
||||||
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(mergeAdapter.getPositionForAdapter(adapter)+idx);
|
boolean found=false;
|
||||||
if(holder instanceof InstanceCatalogSignupFragment.InstanceViewHolder ivh){
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
ivh.radioButton.setChecked(false);
|
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
||||||
|
if(holder.getAbsoluteAdapterPosition()==mergeAdapter.getPositionForAdapter(adapter)+idx && holder instanceof InstanceViewHolder ivh){
|
||||||
|
ivh.radioButton.setChecked(false);
|
||||||
|
found=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if(!found)
|
||||||
|
adapter.notifyItemChanged(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
radioButton.setChecked(true);
|
radioButton.setChecked(true);
|
||||||
@@ -370,5 +663,36 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment{
|
|||||||
chosenInstance=item;
|
chosenInstance=item;
|
||||||
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
|
||||||
|
public boolean isEnabled(){
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum SignupSpeedFilter{
|
||||||
|
ANY,
|
||||||
|
INSTANT,
|
||||||
|
REVIEWED
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum CategoryChoice{
|
||||||
|
GENERAL,
|
||||||
|
SPECIAL;
|
||||||
|
|
||||||
|
public boolean matches(String category){
|
||||||
|
boolean isGeneral=(category==null || "general".equals(category));
|
||||||
|
return (this==GENERAL)==isGeneral;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.joinmastodon.android.fragments.onboarding;
|
package org.joinmastodon.android.fragments.onboarding;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -22,14 +23,14 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.fragments.AppKitFragment;
|
import me.grishka.appkit.fragments.ToolbarFragment;
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
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.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class InstanceRulesFragment extends AppKitFragment{
|
public class InstanceRulesFragment extends ToolbarFragment{
|
||||||
private UsableRecyclerView list;
|
private UsableRecyclerView list;
|
||||||
private MergeRecyclerAdapter adapter;
|
private MergeRecyclerAdapter adapter;
|
||||||
private Button btn;
|
private Button btn;
|
||||||
@@ -47,31 +48,28 @@ public class InstanceRulesFragment extends AppKitFragment{
|
|||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
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"));
|
||||||
|
setTitle(R.string.instance_rules_title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
||||||
View view=inflater.inflate(R.layout.fragment_onboarding_rules, container, false);
|
View view=inflater.inflate(R.layout.fragment_onboarding_rules, container, false);
|
||||||
|
|
||||||
list=view.findViewById(R.id.list);
|
list=view.findViewById(R.id.list);
|
||||||
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||||
View headerView=inflater.inflate(R.layout.item_list_header, list, false);
|
View headerView=inflater.inflate(R.layout.item_list_header_simple, list, false);
|
||||||
TextView title=headerView.findViewById(R.id.title);
|
TextView text=headerView.findViewById(R.id.text);
|
||||||
TextView subtitle=headerView.findViewById(R.id.subtitle);
|
text.setText(getString(R.string.instance_rules_subtitle, instance.uri));
|
||||||
headerView.findViewById(R.id.step_counter).setVisibility(View.GONE);
|
|
||||||
title.setText(R.string.instance_rules_title);
|
|
||||||
subtitle.setText(getString(R.string.instance_rules_subtitle, instance.uri));
|
|
||||||
|
|
||||||
adapter=new MergeRecyclerAdapter();
|
adapter=new MergeRecyclerAdapter();
|
||||||
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, 16, 16, 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;
|
||||||
}
|
}
|
||||||
@@ -79,7 +77,15 @@ public class InstanceRulesFragment extends AppKitFragment{
|
|||||||
@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.colorBackgroundLight));
|
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
|
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
protected void onUpdateToolbar(){
|
||||||
|
// super.onUpdateToolbar();
|
||||||
|
getToolbar().setBackground(null);
|
||||||
|
getToolbar().setElevation(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onButtonClick(){
|
protected void onButtonClick(){
|
||||||
@@ -119,23 +125,22 @@ public class InstanceRulesFragment extends AppKitFragment{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class ItemViewHolder extends BindableViewHolder<Instance.Rule>{
|
private class ItemViewHolder extends BindableViewHolder<Instance.Rule>{
|
||||||
private final TextView title, subtitle;
|
private final TextView text, number;
|
||||||
private final ImageView checkbox;
|
|
||||||
|
|
||||||
public ItemViewHolder(){
|
public ItemViewHolder(){
|
||||||
super(getActivity(), R.layout.item_report_choice, list);
|
super(getActivity(), R.layout.item_server_rule, list);
|
||||||
title=findViewById(R.id.title);
|
text=findViewById(R.id.text);
|
||||||
subtitle=findViewById(R.id.subtitle);
|
number=findViewById(R.id.number);
|
||||||
checkbox=findViewById(R.id.checkbox);
|
|
||||||
subtitle.setVisibility(View.GONE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("DefaultLocale")
|
||||||
@Override
|
@Override
|
||||||
public void onBind(Instance.Rule item){
|
public void onBind(Instance.Rule item){
|
||||||
if(item.parsedText==null){
|
if(item.parsedText==null){
|
||||||
item.parsedText=HtmlParser.parseLinks(item.text);
|
item.parsedText=HtmlParser.parseLinks(item.text);
|
||||||
}
|
}
|
||||||
title.setText(item.parsedText);
|
text.setText(item.parsedText);
|
||||||
|
number.setText(String.format("%d", getAbsoluteAdapterPosition()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,14 +25,15 @@ import org.joinmastodon.android.api.MastodonDetailedErrorResponse;
|
|||||||
import org.joinmastodon.android.api.requests.accounts.RegisterAccount;
|
import org.joinmastodon.android.api.requests.accounts.RegisterAccount;
|
||||||
import org.joinmastodon.android.api.requests.oauth.CreateOAuthApp;
|
import org.joinmastodon.android.api.requests.oauth.CreateOAuthApp;
|
||||||
import org.joinmastodon.android.api.requests.oauth.GetOauthToken;
|
import org.joinmastodon.android.api.requests.oauth.GetOauthToken;
|
||||||
|
import org.joinmastodon.android.api.session.AccountActivationInfo;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Application;
|
import org.joinmastodon.android.model.Application;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.Token;
|
import org.joinmastodon.android.model.Token;
|
||||||
import org.joinmastodon.android.ui.OutlineProviders;
|
|
||||||
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.joinmastodon.android.ui.views.FloatingHintEditTextLayout;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -49,30 +50,28 @@ import me.grishka.appkit.Nav;
|
|||||||
import me.grishka.appkit.api.APIRequest;
|
import me.grishka.appkit.api.APIRequest;
|
||||||
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.AppKitFragment;
|
import me.grishka.appkit.fragments.ToolbarFragment;
|
||||||
import me.grishka.appkit.imageloader.ViewImageLoader;
|
import me.grishka.appkit.imageloader.ViewImageLoader;
|
||||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class SignupFragment extends AppKitFragment{
|
public class SignupFragment extends ToolbarFragment{
|
||||||
private static final int AVATAR_RESULT=198;
|
private static final int AVATAR_RESULT=198;
|
||||||
private static final String TAG="SignupFragment";
|
private static final String TAG="SignupFragment";
|
||||||
|
|
||||||
private Instance instance;
|
private Instance instance;
|
||||||
|
|
||||||
private EditText displayName, username, email, password, reason;
|
private EditText displayName, username, email, password, passwordConfirm, reason;
|
||||||
|
private FloatingHintEditTextLayout displayNameWrap, usernameWrap, emailWrap, passwordWrap, passwordConfirmWrap, reasonWrap;
|
||||||
private TextView reasonExplain;
|
private TextView reasonExplain;
|
||||||
private Button btn;
|
private Button btn;
|
||||||
private View buttonBar;
|
private View buttonBar;
|
||||||
private TextWatcher buttonStateUpdater=new SimpleTextWatcher(e->updateButtonState());
|
private TextWatcher buttonStateUpdater=new SimpleTextWatcher(e->updateButtonState());
|
||||||
private ImageView avatar;
|
|
||||||
private APIRequest currentBackgroundRequest;
|
private APIRequest currentBackgroundRequest;
|
||||||
private Application apiApplication;
|
private Application apiApplication;
|
||||||
private Token apiToken;
|
private Token apiToken;
|
||||||
private boolean submitAfterGettingToken;
|
private boolean submitAfterGettingToken;
|
||||||
private ProgressDialog progressDialog;
|
private ProgressDialog progressDialog;
|
||||||
private Uri avatarUri;
|
|
||||||
private File avatarFile;
|
|
||||||
private HashSet<EditText> errorFields=new HashSet<>();
|
private HashSet<EditText> errorFields=new HashSet<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -81,25 +80,30 @@ public class SignupFragment extends AppKitFragment{
|
|||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
instance=Parcels.unwrap(getArguments().getParcelable("instance"));
|
instance=Parcels.unwrap(getArguments().getParcelable("instance"));
|
||||||
createAppAndGetToken();
|
createAppAndGetToken();
|
||||||
|
setTitle(R.string.signup_title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState){
|
public View onCreateContentView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState){
|
||||||
View view=inflater.inflate(R.layout.fragment_onboarding_signup, container, false);
|
View view=inflater.inflate(R.layout.fragment_onboarding_signup, container, false);
|
||||||
|
|
||||||
TextView title=view.findViewById(R.id.title);
|
|
||||||
TextView domain=view.findViewById(R.id.domain);
|
TextView domain=view.findViewById(R.id.domain);
|
||||||
displayName=view.findViewById(R.id.display_name);
|
displayName=view.findViewById(R.id.display_name);
|
||||||
username=view.findViewById(R.id.username);
|
username=view.findViewById(R.id.username);
|
||||||
email=view.findViewById(R.id.email);
|
email=view.findViewById(R.id.email);
|
||||||
password=view.findViewById(R.id.password);
|
password=view.findViewById(R.id.password);
|
||||||
avatar=view.findViewById(R.id.avatar);
|
passwordConfirm=view.findViewById(R.id.password_confirm);
|
||||||
reason=view.findViewById(R.id.reason);
|
reason=view.findViewById(R.id.reason);
|
||||||
reasonExplain=view.findViewById(R.id.reason_explain);
|
reasonExplain=view.findViewById(R.id.reason_explain);
|
||||||
View avaWrap=view.findViewById(R.id.ava_wrap);
|
|
||||||
|
|
||||||
title.setText(getString(R.string.signup_title, instance.uri));
|
displayNameWrap=view.findViewById(R.id.display_name_wrap);
|
||||||
|
usernameWrap=view.findViewById(R.id.username_wrap);
|
||||||
|
emailWrap=view.findViewById(R.id.email_wrap);
|
||||||
|
passwordWrap=view.findViewById(R.id.password_wrap);
|
||||||
|
passwordConfirmWrap=view.findViewById(R.id.password_confirm_wrap);
|
||||||
|
reasonWrap=view.findViewById(R.id.reason_wrap);
|
||||||
|
|
||||||
domain.setText('@'+instance.uri);
|
domain.setText('@'+instance.uri);
|
||||||
|
|
||||||
username.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
username.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
||||||
@@ -114,23 +118,20 @@ public class SignupFragment extends AppKitFragment{
|
|||||||
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));
|
|
||||||
updateButtonState();
|
updateButtonState();
|
||||||
|
|
||||||
username.addTextChangedListener(buttonStateUpdater);
|
username.addTextChangedListener(buttonStateUpdater);
|
||||||
email.addTextChangedListener(buttonStateUpdater);
|
email.addTextChangedListener(buttonStateUpdater);
|
||||||
password.addTextChangedListener(buttonStateUpdater);
|
password.addTextChangedListener(buttonStateUpdater);
|
||||||
|
passwordConfirm.addTextChangedListener(buttonStateUpdater);
|
||||||
reason.addTextChangedListener(buttonStateUpdater);
|
reason.addTextChangedListener(buttonStateUpdater);
|
||||||
|
|
||||||
username.addTextChangedListener(new ErrorClearingListener(username));
|
username.addTextChangedListener(new ErrorClearingListener(username));
|
||||||
email.addTextChangedListener(new ErrorClearingListener(email));
|
email.addTextChangedListener(new ErrorClearingListener(email));
|
||||||
password.addTextChangedListener(new ErrorClearingListener(password));
|
password.addTextChangedListener(new ErrorClearingListener(password));
|
||||||
|
passwordConfirm.addTextChangedListener(new ErrorClearingListener(passwordConfirm));
|
||||||
reason.addTextChangedListener(new ErrorClearingListener(reason));
|
reason.addTextChangedListener(new ErrorClearingListener(reason));
|
||||||
|
|
||||||
avaWrap.setOutlineProvider(OutlineProviders.roundedRect(22));
|
|
||||||
avaWrap.setClipToOutline(true);
|
|
||||||
avaWrap.setOnClickListener(v->onAvatarClick());
|
|
||||||
|
|
||||||
if(!instance.approvalRequired){
|
if(!instance.approvalRequired){
|
||||||
reason.setVisibility(View.GONE);
|
reason.setVisibility(View.GONE);
|
||||||
reasonExplain.setVisibility(View.GONE);
|
reasonExplain.setVisibility(View.GONE);
|
||||||
@@ -142,10 +143,23 @@ public class SignupFragment extends AppKitFragment{
|
|||||||
@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.colorBackgroundLight));
|
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
|
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
protected void onUpdateToolbar(){
|
||||||
|
// super.onUpdateToolbar();
|
||||||
|
getToolbar().setBackground(null);
|
||||||
|
getToolbar().setElevation(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onButtonClick(){
|
private void onButtonClick(){
|
||||||
|
if(!password.getText().toString().equals(passwordConfirm.getText().toString())){
|
||||||
|
passwordConfirm.setError(getString(R.string.signup_passwords_dont_match));
|
||||||
|
passwordConfirmWrap.setErrorState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
showProgressDialog();
|
showProgressDialog();
|
||||||
if(currentBackgroundRequest!=null){
|
if(currentBackgroundRequest!=null){
|
||||||
submitAfterGettingToken=true;
|
submitAfterGettingToken=true;
|
||||||
@@ -160,32 +174,8 @@ public class SignupFragment extends AppKitFragment{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copyAvatar(Runnable onDone){
|
|
||||||
// Need to copy the avatar from the content provider to somewhere accessible in case the app gets killed between signup and account activation
|
|
||||||
Activity activity=getActivity();
|
|
||||||
MastodonAPIController.runInBackground(()->{
|
|
||||||
String origName=UiUtils.getFileName(avatarUri);
|
|
||||||
avatarFile=new File(activity.getCacheDir(), System.currentTimeMillis()+origName.substring(origName.lastIndexOf('.')));
|
|
||||||
try(InputStream in=activity.getContentResolver().openInputStream(avatarUri);
|
|
||||||
FileOutputStream out=new FileOutputStream(avatarFile)){
|
|
||||||
byte[] buf=new byte[10240];
|
|
||||||
int read;
|
|
||||||
while((read=in.read(buf))>0){
|
|
||||||
out.write(buf, 0, read);
|
|
||||||
}
|
|
||||||
}catch(IOException x){
|
|
||||||
Log.w(TAG, "copyAvatar: error copying", x);
|
|
||||||
}
|
|
||||||
activity.runOnUiThread(onDone);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void submit(){
|
private void submit(){
|
||||||
if(avatarUri!=null && (avatarFile==null || !avatarFile.exists())){
|
actuallySubmit();
|
||||||
copyAvatar(this::actuallySubmit);
|
|
||||||
}else{
|
|
||||||
actuallySubmit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void actuallySubmit(){
|
private void actuallySubmit(){
|
||||||
@@ -204,9 +194,7 @@ public class SignupFragment extends AppKitFragment{
|
|||||||
fakeAccount.acct=fakeAccount.username=username;
|
fakeAccount.acct=fakeAccount.username=username;
|
||||||
fakeAccount.id="tmp"+System.currentTimeMillis();
|
fakeAccount.id="tmp"+System.currentTimeMillis();
|
||||||
fakeAccount.displayName=displayName.getText().toString();
|
fakeAccount.displayName=displayName.getText().toString();
|
||||||
if(avatarFile!=null)
|
AccountSessionManager.getInstance().addAccount(instance, result, fakeAccount, apiApplication, new AccountActivationInfo(email, System.currentTimeMillis()));
|
||||||
fakeAccount.avatar=avatarFile.getAbsolutePath();
|
|
||||||
AccountSessionManager.getInstance().addAccount(instance, result, fakeAccount, apiApplication, false);
|
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", AccountSessionManager.getInstance().getLastActiveAccountID());
|
args.putString("account", AccountSessionManager.getInstance().getLastActiveAccountID());
|
||||||
Nav.goClearingStack(getActivity(), AccountActivationFragment.class, args);
|
Nav.goClearingStack(getActivity(), AccountActivationFragment.class, args);
|
||||||
@@ -225,6 +213,7 @@ public class SignupFragment extends AppKitFragment{
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
field.setError(fieldErrors.get(fieldName).stream().map(err->err.description).collect(Collectors.joining("\n")));
|
field.setError(fieldErrors.get(fieldName).stream().map(err->err.description).collect(Collectors.joining("\n")));
|
||||||
|
getFieldWrapByName(fieldName).setErrorState();
|
||||||
errorFields.add(field);
|
errorFields.add(field);
|
||||||
if(first){
|
if(first){
|
||||||
first=false;
|
first=false;
|
||||||
@@ -252,6 +241,16 @@ public class SignupFragment extends AppKitFragment{
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private FloatingHintEditTextLayout getFieldWrapByName(String name){
|
||||||
|
return switch(name){
|
||||||
|
case "email" -> emailWrap;
|
||||||
|
case "username" -> usernameWrap;
|
||||||
|
case "password" -> passwordWrap;
|
||||||
|
case "reason" -> reasonWrap;
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private void showProgressDialog(){
|
private void showProgressDialog(){
|
||||||
if(progressDialog==null){
|
if(progressDialog==null){
|
||||||
progressDialog=new ProgressDialog(getActivity());
|
progressDialog=new ProgressDialog(getActivity());
|
||||||
@@ -262,7 +261,7 @@ public class SignupFragment extends AppKitFragment{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateButtonState(){
|
private void updateButtonState(){
|
||||||
btn.setEnabled(username.length()>0 && email.length()>0 && email.getText().toString().contains("@") && password.length()>=8 && (!instance.approvalRequired || reason.length()>0));
|
btn.setEnabled(username.length()>0 && email.length()>0 && email.getText().toString().contains("@") && password.length()>=8 && passwordConfirm.length()>=8 && (!instance.approvalRequired || reason.length()>0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createAppAndGetToken(){
|
private void createAppAndGetToken(){
|
||||||
@@ -324,20 +323,6 @@ public class SignupFragment extends AppKitFragment{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data){
|
|
||||||
if(requestCode==AVATAR_RESULT && resultCode==Activity.RESULT_OK){
|
|
||||||
avatarUri=data.getData();
|
|
||||||
if(avatarFile!=null && avatarFile.exists())
|
|
||||||
avatarFile.delete();
|
|
||||||
ViewImageLoader.load(avatar, getResources().getDrawable(R.drawable.default_avatar), new UrlImageLoaderRequest(avatarUri, V.dp(100), V.dp(100)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onAvatarClick(){
|
|
||||||
startActivityForResult(new Intent(Intent.ACTION_GET_CONTENT).setType("image/*").addCategory(Intent.CATEGORY_OPENABLE), AVATAR_RESULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ErrorClearingListener implements TextWatcher{
|
private class ErrorClearingListener implements TextWatcher{
|
||||||
public final EditText editText;
|
public final EditText editText;
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ public class Instance extends BaseModel{
|
|||||||
// non-standard field in some Mastodon forks
|
// non-standard field in some Mastodon forks
|
||||||
public int maxTootChars;
|
public int maxTootChars;
|
||||||
|
|
||||||
|
public V2 v2;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postprocess() throws ObjectValidationException{
|
public void postprocess() throws ObjectValidationException{
|
||||||
super.postprocess();
|
super.postprocess();
|
||||||
@@ -176,4 +178,19 @@ public class Instance extends BaseModel{
|
|||||||
public int minExpiration;
|
public int minExpiration;
|
||||||
public int maxExpiration;
|
public int maxExpiration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Parcel
|
||||||
|
public static class V2 extends BaseModel {
|
||||||
|
public V2.Configuration configuration;
|
||||||
|
|
||||||
|
@Parcel
|
||||||
|
public static class Configuration {
|
||||||
|
public TranslationConfiguration translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parcel
|
||||||
|
public static class TranslationConfiguration{
|
||||||
|
public boolean enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 String translation;
|
||||||
|
|
||||||
public boolean favourited;
|
public boolean favourited;
|
||||||
public boolean reblogged;
|
public boolean reblogged;
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
package org.joinmastodon.android.model;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.api.ObjectValidationException;
|
|
||||||
import org.joinmastodon.android.api.RequiredField;
|
|
||||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
|
||||||
import org.parceler.Parcel;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Parcel
|
|
||||||
public class StatusTranslation extends BaseModel implements DisplayItemsParent{
|
|
||||||
// @RequiredField
|
|
||||||
public String id;
|
|
||||||
// @RequiredField
|
|
||||||
public String uri;
|
|
||||||
// @RequiredField
|
|
||||||
public Instant createdAt;
|
|
||||||
// @RequiredField
|
|
||||||
public Account account;
|
|
||||||
// @RequiredField
|
|
||||||
public String content;
|
|
||||||
// @RequiredField
|
|
||||||
public StatusPrivacy visibility;
|
|
||||||
public boolean sensitive;
|
|
||||||
// @RequiredField
|
|
||||||
public String spoilerText;
|
|
||||||
// @RequiredField
|
|
||||||
public List<Attachment> mediaAttachments;
|
|
||||||
public Application application;
|
|
||||||
// @RequiredField
|
|
||||||
public List<Mention> mentions;
|
|
||||||
// @RequiredField
|
|
||||||
public List<Hashtag> tags;
|
|
||||||
// @RequiredField
|
|
||||||
public List<Emoji> emojis;
|
|
||||||
public long reblogsCount;
|
|
||||||
public long favouritesCount;
|
|
||||||
public long repliesCount;
|
|
||||||
public Instant editedAt;
|
|
||||||
|
|
||||||
public String url;
|
|
||||||
public String inReplyToId;
|
|
||||||
public String inReplyToAccountId;
|
|
||||||
public Status reblog;
|
|
||||||
public Poll poll;
|
|
||||||
public Card card;
|
|
||||||
public String language;
|
|
||||||
public String text;
|
|
||||||
|
|
||||||
public boolean favourited;
|
|
||||||
public boolean reblogged;
|
|
||||||
public boolean muted;
|
|
||||||
public boolean bookmarked;
|
|
||||||
public boolean pinned;
|
|
||||||
|
|
||||||
public transient boolean spoilerRevealed;
|
|
||||||
public transient boolean hasGapAfter;
|
|
||||||
private transient String strippedText;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postprocess() throws ObjectValidationException{
|
|
||||||
super.postprocess();
|
|
||||||
// if(application!=null)
|
|
||||||
// application.postprocess();
|
|
||||||
// for(Mention m:mentions)
|
|
||||||
// m.postprocess();
|
|
||||||
// for(Hashtag t:tags)
|
|
||||||
// t.postprocess();
|
|
||||||
// for(Emoji e:emojis)
|
|
||||||
// e.postprocess();
|
|
||||||
// for(Attachment a:mediaAttachments)
|
|
||||||
// a.postprocess();
|
|
||||||
// account.postprocess();
|
|
||||||
// if(poll!=null)
|
|
||||||
// poll.postprocess();
|
|
||||||
// if(card!=null)
|
|
||||||
// card.postprocess();
|
|
||||||
// if(reblog!=null)
|
|
||||||
// reblog.postprocess();
|
|
||||||
|
|
||||||
// spoilerRevealed=GlobalUserPreferences.alwaysExpandContentWarnings || !sensitive;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(){
|
|
||||||
return "Status{"+
|
|
||||||
"id='"+id+'\''+
|
|
||||||
", uri='"+uri+'\''+
|
|
||||||
", createdAt="+createdAt+
|
|
||||||
", account="+account+
|
|
||||||
", content='"+content+'\''+
|
|
||||||
", visibility="+visibility+
|
|
||||||
", sensitive="+sensitive+
|
|
||||||
", spoilerText='"+spoilerText+'\''+
|
|
||||||
", mediaAttachments="+mediaAttachments+
|
|
||||||
", application="+application+
|
|
||||||
", mentions="+mentions+
|
|
||||||
", tags="+tags+
|
|
||||||
", emojis="+emojis+
|
|
||||||
", reblogsCount="+reblogsCount+
|
|
||||||
", favouritesCount="+favouritesCount+
|
|
||||||
", repliesCount="+repliesCount+
|
|
||||||
", url='"+url+'\''+
|
|
||||||
", inReplyToId='"+inReplyToId+'\''+
|
|
||||||
", inReplyToAccountId='"+inReplyToAccountId+'\''+
|
|
||||||
", reblog="+reblog+
|
|
||||||
", poll="+poll+
|
|
||||||
", card="+card+
|
|
||||||
", language='"+language+'\''+
|
|
||||||
", text='"+text+'\''+
|
|
||||||
", favourited="+favourited+
|
|
||||||
", reblogged="+reblogged+
|
|
||||||
", muted="+muted+
|
|
||||||
", bookmarked="+bookmarked+
|
|
||||||
", pinned="+pinned+
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getID(){
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(StatusCountersUpdatedEvent ev){
|
|
||||||
favouritesCount=ev.favorites;
|
|
||||||
reblogsCount=ev.reblogs;
|
|
||||||
repliesCount=ev.replies;
|
|
||||||
favourited=ev.favorited;
|
|
||||||
reblogged=ev.reblogged;
|
|
||||||
bookmarked=ev.bookmarked;
|
|
||||||
pinned=ev.pinned;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StatusTranslation getContentStatus(){
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getStrippedText(){
|
|
||||||
if(strippedText==null)
|
|
||||||
strippedText=HtmlParser.strip(content);
|
|
||||||
return strippedText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
|
public class TranslatedStatus extends BaseModel {
|
||||||
|
public String content;
|
||||||
|
public String detectedSourceLanguage;
|
||||||
|
public String provider;
|
||||||
|
}
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
package org.joinmastodon.android.model.catalog;
|
package org.joinmastodon.android.model.catalog;
|
||||||
|
|
||||||
|
import android.graphics.Region;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
import org.joinmastodon.android.api.AllFieldsAreRequired;
|
import org.joinmastodon.android.api.AllFieldsAreRequired;
|
||||||
import org.joinmastodon.android.api.ObjectValidationException;
|
import org.joinmastodon.android.api.ObjectValidationException;
|
||||||
import org.joinmastodon.android.model.BaseModel;
|
import org.joinmastodon.android.model.BaseModel;
|
||||||
@@ -7,13 +12,17 @@ import org.joinmastodon.android.model.BaseModel;
|
|||||||
import java.net.IDN;
|
import java.net.IDN;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
@AllFieldsAreRequired
|
@AllFieldsAreRequired
|
||||||
public class CatalogInstance extends BaseModel{
|
public class CatalogInstance extends BaseModel{
|
||||||
public String domain;
|
public String domain;
|
||||||
public String version;
|
public String version;
|
||||||
public String description;
|
public String description;
|
||||||
public List<String> languages;
|
public List<String> languages;
|
||||||
public String region;
|
@SerializedName("region")
|
||||||
|
private String _region;
|
||||||
public List<String> categories;
|
public List<String> categories;
|
||||||
public String proxiedThumbnail;
|
public String proxiedThumbnail;
|
||||||
public int totalUsers;
|
public int totalUsers;
|
||||||
@@ -22,7 +31,9 @@ public class CatalogInstance extends BaseModel{
|
|||||||
public String language;
|
public String language;
|
||||||
public String category;
|
public String category;
|
||||||
|
|
||||||
|
public transient Region region;
|
||||||
public transient String normalizedDomain;
|
public transient String normalizedDomain;
|
||||||
|
public transient UrlImageLoaderRequest thumbnailRequest;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postprocess() throws ObjectValidationException{
|
public void postprocess() throws ObjectValidationException{
|
||||||
@@ -31,6 +42,14 @@ public class CatalogInstance extends BaseModel{
|
|||||||
normalizedDomain=IDN.toUnicode(domain);
|
normalizedDomain=IDN.toUnicode(domain);
|
||||||
else
|
else
|
||||||
normalizedDomain=domain;
|
normalizedDomain=domain;
|
||||||
|
if(!TextUtils.isEmpty(_region)){
|
||||||
|
try{
|
||||||
|
region=Region.valueOf(_region.toUpperCase());
|
||||||
|
}catch(IllegalArgumentException ignore){}
|
||||||
|
}
|
||||||
|
if(!TextUtils.isEmpty(proxiedThumbnail)){
|
||||||
|
thumbnailRequest=new UrlImageLoaderRequest(proxiedThumbnail, 0, V.dp(56));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -50,4 +69,13 @@ public class CatalogInstance extends BaseModel{
|
|||||||
", category='"+category+'\''+
|
", category='"+category+'\''+
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum Region{
|
||||||
|
EUROPE,
|
||||||
|
NORTH_AMERICA,
|
||||||
|
SOUTH_AMERICA,
|
||||||
|
AFRICA,
|
||||||
|
ASIA,
|
||||||
|
OCEANIA
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import org.joinmastodon.android.R;
|
|||||||
import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
|
import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
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.onboarding.MoshidonLoginFragment;
|
import org.joinmastodon.android.fragments.onboarding.CustomLoginFragment;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -77,7 +77,7 @@ public class AccountSwitcherSheet extends BottomSheet{
|
|||||||
holder.avatar.setImageResource(R.drawable.ic_fluent_add_circle_24_filled);
|
holder.avatar.setImageResource(R.drawable.ic_fluent_add_circle_24_filled);
|
||||||
holder.avatar.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(activity, android.R.attr.textColorPrimary)));
|
holder.avatar.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(activity, android.R.attr.textColorPrimary)));
|
||||||
adapter.addAdapter(new ClickableSingleViewRecyclerAdapter(holder.itemView, ()->{
|
adapter.addAdapter(new ClickableSingleViewRecyclerAdapter(holder.itemView, ()->{
|
||||||
Nav.go(activity, MoshidonLoginFragment.class, null);
|
Nav.go(activity, CustomLoginFragment.class, null);
|
||||||
dismiss();
|
dismiss();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -240,7 +240,10 @@ public class AccountSwitcherSheet extends BottomSheet{
|
|||||||
|
|
||||||
public WrappedAccount(AccountSession session){
|
public WrappedAccount(AccountSession session){
|
||||||
this.session=session;
|
this.session=session;
|
||||||
req=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? session.self.avatar : session.self.avatarStatic, V.dp(50), V.dp(50));
|
if(session.self.avatar!=null)
|
||||||
|
req=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? session.self.avatar : session.self.avatarStatic, V.dp(50), V.dp(50));
|
||||||
|
else
|
||||||
|
req=null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import android.widget.ImageView;
|
|||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
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.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
@@ -23,7 +25,9 @@ import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
|||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.ProgressBarButton;
|
import org.joinmastodon.android.ui.views.ProgressBarButton;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
@@ -154,10 +158,18 @@ public class AccountCardStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
private void onFollowRequestButtonClick(View v) {
|
private void onFollowRequestButtonClick(View v) {
|
||||||
itemView.setHasTransientState(true);
|
itemView.setHasTransientState(true);
|
||||||
UiUtils.handleFollowRequest((Activity) v.getContext(), item.account, item.parentFragment.getAccountID(), item.notification.id , v == acceptButton, relationship, rel -> {
|
UiUtils.handleFollowRequest((Activity) v.getContext(), item.account, item.parentFragment.getAccountID(), null, v == acceptButton, relationship, rel -> {
|
||||||
itemView.setHasTransientState(false);
|
itemView.setHasTransientState(false);
|
||||||
item.parentFragment.putRelationship(item.account.id, rel);
|
item.parentFragment.putRelationship(item.account.id, rel);
|
||||||
rebind();
|
RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter = getBindingAdapter();
|
||||||
|
if (!rel.requested && !rel.followedBy && adapter != null) {
|
||||||
|
int index = item.parentFragment.getDisplayItems().indexOf(item);
|
||||||
|
item.parentFragment.getDisplayItems().remove(index);
|
||||||
|
item.parentFragment.getDisplayItems().remove(index - 1);
|
||||||
|
adapter.notifyItemRangeRemoved(getLayoutPosition()-1, 2);
|
||||||
|
} else {
|
||||||
|
rebind();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,15 @@ import android.app.Activity;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewConfiguration;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.accessibility.AccessibilityNodeInfo;
|
import android.view.accessibility.AccessibilityNodeInfo;
|
||||||
|
import android.view.animation.AlphaAnimation;
|
||||||
|
import android.view.animation.Animation;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -21,10 +26,8 @@ import org.joinmastodon.android.model.StatusPrivacy;
|
|||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class FooterStatusDisplayItem extends StatusDisplayItem{
|
public class FooterStatusDisplayItem extends StatusDisplayItem{
|
||||||
@@ -46,6 +49,10 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
public static class Holder extends StatusDisplayItem.Holder<FooterStatusDisplayItem>{
|
public static class Holder extends StatusDisplayItem.Holder<FooterStatusDisplayItem>{
|
||||||
private final TextView reply, boost, favorite, bookmark;
|
private final TextView reply, boost, favorite, bookmark;
|
||||||
private final ImageView share;
|
private final ImageView share;
|
||||||
|
private static final Animation opacityOut, opacityIn;
|
||||||
|
|
||||||
|
private View touchingView = null;
|
||||||
|
private final Runnable longClickRunnable = () -> { if (touchingView != null) touchingView.performLongClick(); };
|
||||||
|
|
||||||
private final View.AccessibilityDelegate buttonAccessibilityDelegate=new View.AccessibilityDelegate(){
|
private final View.AccessibilityDelegate buttonAccessibilityDelegate=new View.AccessibilityDelegate(){
|
||||||
@Override
|
@Override
|
||||||
@@ -56,6 +63,16 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static {
|
||||||
|
opacityOut = new AlphaAnimation(1, 0.7f);
|
||||||
|
opacityOut.setDuration(200);
|
||||||
|
opacityOut.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
|
opacityOut.setFillAfter(true);
|
||||||
|
opacityIn = new AlphaAnimation(0.7f, 1);
|
||||||
|
opacityIn.setDuration(300);
|
||||||
|
opacityIn.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
public Holder(Activity activity, ViewGroup parent){
|
public Holder(Activity activity, ViewGroup parent){
|
||||||
super(activity, R.layout.display_item_footer, parent);
|
super(activity, R.layout.display_item_footer, parent);
|
||||||
reply=findViewById(R.id.reply);
|
reply=findViewById(R.id.reply);
|
||||||
@@ -74,15 +91,22 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
View favorite=findViewById(R.id.favorite_btn);
|
View favorite=findViewById(R.id.favorite_btn);
|
||||||
View share=findViewById(R.id.share_btn);
|
View share=findViewById(R.id.share_btn);
|
||||||
View bookmark=findViewById(R.id.bookmark_btn);
|
View bookmark=findViewById(R.id.bookmark_btn);
|
||||||
|
reply.setOnTouchListener(this::onButtonTouch);
|
||||||
reply.setOnClickListener(this::onReplyClick);
|
reply.setOnClickListener(this::onReplyClick);
|
||||||
reply.setAccessibilityDelegate(buttonAccessibilityDelegate);
|
reply.setAccessibilityDelegate(buttonAccessibilityDelegate);
|
||||||
|
boost.setOnTouchListener(this::onButtonTouch);
|
||||||
boost.setOnClickListener(this::onBoostClick);
|
boost.setOnClickListener(this::onBoostClick);
|
||||||
|
boost.setOnLongClickListener(this::onBoostLongClick);
|
||||||
boost.setAccessibilityDelegate(buttonAccessibilityDelegate);
|
boost.setAccessibilityDelegate(buttonAccessibilityDelegate);
|
||||||
|
favorite.setOnTouchListener(this::onButtonTouch);
|
||||||
favorite.setOnClickListener(this::onFavoriteClick);
|
favorite.setOnClickListener(this::onFavoriteClick);
|
||||||
favorite.setAccessibilityDelegate(buttonAccessibilityDelegate);
|
favorite.setAccessibilityDelegate(buttonAccessibilityDelegate);
|
||||||
|
bookmark.setOnTouchListener(this::onButtonTouch);
|
||||||
bookmark.setOnClickListener(this::onBookmarkClick);
|
bookmark.setOnClickListener(this::onBookmarkClick);
|
||||||
bookmark.setAccessibilityDelegate(buttonAccessibilityDelegate);
|
bookmark.setAccessibilityDelegate(buttonAccessibilityDelegate);
|
||||||
|
share.setOnTouchListener(this::onButtonTouch);
|
||||||
share.setOnClickListener(this::onShareClick);
|
share.setOnClickListener(this::onShareClick);
|
||||||
|
share.setOnLongClickListener(this::onShareLongClick);
|
||||||
share.setAccessibilityDelegate(buttonAccessibilityDelegate);
|
share.setAccessibilityDelegate(buttonAccessibilityDelegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +132,32 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean onButtonTouch(View v, MotionEvent event){
|
||||||
|
boolean disabled = !v.isEnabled() || (v instanceof FrameLayout parentFrame &&
|
||||||
|
parentFrame.getChildCount() > 0 && !parentFrame.getChildAt(0).isEnabled());
|
||||||
|
int action = event.getAction();
|
||||||
|
long eventDuration = event.getEventTime() - event.getDownTime();
|
||||||
|
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
||||||
|
touchingView = null;
|
||||||
|
v.removeCallbacks(longClickRunnable);
|
||||||
|
v.animate().scaleX(1).scaleY(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
|
||||||
|
if (disabled) return true;
|
||||||
|
if (action == MotionEvent.ACTION_UP && eventDuration < ViewConfiguration.getLongPressTimeout()) v.performClick();
|
||||||
|
else v.startAnimation(opacityIn);
|
||||||
|
} else if (action == MotionEvent.ACTION_DOWN) {
|
||||||
|
touchingView = v;
|
||||||
|
// 20dp to center in middle of icon, because: (icon width = 24dp) / 2 + (paddingStart = 8dp)
|
||||||
|
v.setPivotX(V.dp(20));
|
||||||
|
v.animate().scaleX(0.85f).scaleY(0.85f).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(75).start();
|
||||||
|
if (disabled) return true;
|
||||||
|
v.postDelayed(longClickRunnable, ViewConfiguration.getLongPressTimeout());
|
||||||
|
v.startAnimation(opacityOut);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void onReplyClick(View v){
|
private void onReplyClick(View v){
|
||||||
|
v.startAnimation(opacityIn);
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", item.accountID);
|
args.putString("account", item.accountID);
|
||||||
args.putParcelable("replyTo", Parcels.wrap(item.status));
|
args.putParcelable("replyTo", Parcels.wrap(item.status));
|
||||||
@@ -116,29 +165,53 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onBoostClick(View v){
|
private void onBoostClick(View v){
|
||||||
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setReblogged(item.status, !item.status.reblogged);
|
boost.setSelected(!item.status.reblogged);
|
||||||
boost.setSelected(item.status.reblogged);
|
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setReblogged(item.status, !item.status.reblogged, r->{
|
||||||
bindButton(boost, item.status.reblogsCount);
|
v.startAnimation(opacityIn);
|
||||||
|
bindButton(boost, r.reblogsCount);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean onBoostLongClick(View v){
|
||||||
|
v.setAlpha(1);
|
||||||
|
v.setScaleX(1);
|
||||||
|
v.setScaleY(1);
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", item.accountID);
|
||||||
|
args.putString("prefilledText", "\n\n" + item.status.url);
|
||||||
|
args.putInt("selectionStart", 0);
|
||||||
|
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onFavoriteClick(View v){
|
private void onFavoriteClick(View v){
|
||||||
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setFavorited(item.status, !item.status.favourited);
|
favorite.setSelected(!item.status.favourited);
|
||||||
favorite.setSelected(item.status.favourited);
|
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setFavorited(item.status, !item.status.favourited, r->{
|
||||||
bindButton(favorite, item.status.favouritesCount);
|
v.startAnimation(opacityIn);
|
||||||
|
bindButton(favorite, r.favouritesCount);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onBookmarkClick(View v){
|
private void onBookmarkClick(View v){
|
||||||
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setBookmarked(item.status, !item.status.bookmarked);
|
|
||||||
bookmark.setSelected(item.status.bookmarked);
|
bookmark.setSelected(item.status.bookmarked);
|
||||||
|
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setBookmarked(item.status, !item.status.bookmarked, r->{
|
||||||
|
v.startAnimation(opacityIn);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onShareClick(View v){
|
private void onShareClick(View v){
|
||||||
|
v.startAnimation(opacityIn);
|
||||||
Intent intent=new Intent(Intent.ACTION_SEND);
|
Intent intent=new Intent(Intent.ACTION_SEND);
|
||||||
intent.setType("text/plain");
|
intent.setType("text/plain");
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, item.status.url);
|
intent.putExtra(Intent.EXTRA_TEXT, item.status.url);
|
||||||
v.getContext().startActivity(Intent.createChooser(intent, v.getContext().getString(R.string.share_toot_title)));
|
v.getContext().startActivity(Intent.createChooser(intent, v.getContext().getString(R.string.share_toot_title)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean onShareLongClick(View v){
|
||||||
|
UiUtils.copyText(v.getContext(), item.status.url);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private int descriptionForId(int id){
|
private int descriptionForId(int id){
|
||||||
if(id==R.id.reply_btn)
|
if(id==R.id.reply_btn)
|
||||||
return R.string.button_reply;
|
return R.string.button_reply;
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import org.joinmastodon.android.GlobalUserPreferences;
|
|||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||||
import org.joinmastodon.android.api.requests.statuses.GetStatusSourceText;
|
import org.joinmastodon.android.api.requests.statuses.GetStatusSourceText;
|
||||||
import org.joinmastodon.android.api.requests.statuses.GetStatusTranslation;
|
|
||||||
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;
|
||||||
@@ -32,9 +31,10 @@ import org.joinmastodon.android.fragments.ThreadFragment;
|
|||||||
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
||||||
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.Preferences;
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.StatusTranslation;
|
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;
|
||||||
@@ -43,6 +43,7 @@ import org.parceler.Parcels;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.APIRequest;
|
import me.grishka.appkit.api.APIRequest;
|
||||||
@@ -62,7 +63,6 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
private SpannableStringBuilder parsedName;
|
private SpannableStringBuilder parsedName;
|
||||||
public final Status status;
|
public final Status status;
|
||||||
private boolean hasVisibilityToggle;
|
private boolean hasVisibilityToggle;
|
||||||
private boolean hasTranslateToggle;
|
|
||||||
boolean needBottomPadding;
|
boolean needBottomPadding;
|
||||||
private String extraText;
|
private String extraText;
|
||||||
|
|
||||||
@@ -77,7 +77,6 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
HtmlParser.parseCustomEmoji(parsedName, user.emojis);
|
HtmlParser.parseCustomEmoji(parsedName, user.emojis);
|
||||||
emojiHelper.setText(parsedName);
|
emojiHelper.setText(parsedName);
|
||||||
if(status!=null){
|
if(status!=null){
|
||||||
hasTranslateToggle=true;
|
|
||||||
hasVisibilityToggle=status.sensitive || !TextUtils.isEmpty(status.spoilerText);
|
hasVisibilityToggle=status.sensitive || !TextUtils.isEmpty(status.spoilerText);
|
||||||
if(!hasVisibilityToggle && !status.mediaAttachments.isEmpty()){
|
if(!hasVisibilityToggle && !status.mediaAttachments.isEmpty()){
|
||||||
for(Attachment att:status.mediaAttachments){
|
for(Attachment att:status.mediaAttachments){
|
||||||
@@ -111,7 +110,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
public static class Holder extends StatusDisplayItem.Holder<HeaderStatusDisplayItem> implements ImageLoaderViewHolder{
|
public static class Holder extends StatusDisplayItem.Holder<HeaderStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||||
private final TextView name, username, timestamp, extraText;
|
private final TextView name, username, timestamp, extraText;
|
||||||
private final ImageView avatar, more, visibility, translate;
|
private final ImageView avatar, more, visibility;
|
||||||
private final PopupMenu optionsMenu;
|
private final PopupMenu optionsMenu;
|
||||||
private Relationship relationship;
|
private Relationship relationship;
|
||||||
private APIRequest<?> currentRelationshipRequest;
|
private APIRequest<?> currentRelationshipRequest;
|
||||||
@@ -125,7 +124,6 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
public Holder(Activity activity, ViewGroup parent){
|
public Holder(Activity activity, ViewGroup parent){
|
||||||
super(activity, R.layout.display_item_header, parent);
|
super(activity, R.layout.display_item_header, parent);
|
||||||
translate=findViewById(R.id.translate);
|
|
||||||
name=findViewById(R.id.name);
|
name=findViewById(R.id.name);
|
||||||
username=findViewById(R.id.username);
|
username=findViewById(R.id.username);
|
||||||
timestamp=findViewById(R.id.timestamp);
|
timestamp=findViewById(R.id.timestamp);
|
||||||
@@ -138,7 +136,6 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
avatar.setClipToOutline(true);
|
avatar.setClipToOutline(true);
|
||||||
more.setOnClickListener(this::onMoreClick);
|
more.setOnClickListener(this::onMoreClick);
|
||||||
visibility.setOnClickListener(v->item.parentFragment.onVisibilityIconClick(this));
|
visibility.setOnClickListener(v->item.parentFragment.onVisibilityIconClick(this));
|
||||||
translate.setOnClickListener(v->item.parentFragment.onRevealTranslationClick(this));
|
|
||||||
|
|
||||||
optionsMenu=new PopupMenu(activity, more);
|
optionsMenu=new PopupMenu(activity, more);
|
||||||
optionsMenu.inflate(R.menu.post);
|
optionsMenu.inflate(R.menu.post);
|
||||||
@@ -232,7 +229,6 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
else
|
else
|
||||||
timestamp.setText(item.parentFragment.getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(itemView.getContext(), item.status.editedAt)));
|
timestamp.setText(item.parentFragment.getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(itemView.getContext(), item.status.editedAt)));
|
||||||
visibility.setVisibility(item.hasVisibilityToggle && !item.inset ? View.VISIBLE : View.GONE);
|
visibility.setVisibility(item.hasVisibilityToggle && !item.inset ? View.VISIBLE : View.GONE);
|
||||||
translate.setVisibility(item.hasTranslateToggle ? View.VISIBLE : View.GONE);
|
|
||||||
if(item.hasVisibilityToggle){
|
if(item.hasVisibilityToggle){
|
||||||
visibility.setImageResource(item.status.spoilerRevealed ? R.drawable.ic_visibility_off : R.drawable.ic_visibility);
|
visibility.setImageResource(item.status.spoilerRevealed ? R.drawable.ic_visibility_off : R.drawable.ic_visibility);
|
||||||
visibility.setContentDescription(item.parentFragment.getString(item.status.spoilerRevealed ? R.string.hide_content : R.string.reveal_content));
|
visibility.setContentDescription(item.parentFragment.getString(item.status.spoilerRevealed ? R.string.hide_content : R.string.reveal_content));
|
||||||
@@ -240,9 +236,6 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
visibility.setTooltipText(visibility.getContentDescription());
|
visibility.setTooltipText(visibility.getContentDescription());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(item.hasTranslateToggle){
|
|
||||||
translate.setImageResource(item.status.wantsTranslation ? R.drawable.ic_translate_on : R.drawable.ic_translate_off);
|
|
||||||
}
|
|
||||||
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);
|
extraText.setVisibility(View.GONE);
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ public class LinkCardStatusDisplayItem extends StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onClick(View v){
|
private void onClick(View v){
|
||||||
UiUtils.launchWebBrowser(itemView.getContext(), item.status.card.url);
|
UiUtils.openURL(itemView.getContext(), item.parentFragment.getAccountID(), item.status.card.url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,16 +8,20 @@ import android.text.TextUtils;
|
|||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.statuses.GetStatusTranslation;
|
import org.joinmastodon.android.api.requests.statuses.TranslateStatus;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.StatusTranslation;
|
|
||||||
import org.joinmastodon.android.ui.drawables.SpoilerStripesDrawable;
|
import org.joinmastodon.android.ui.drawables.SpoilerStripesDrawable;
|
||||||
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
|
import org.joinmastodon.android.model.TranslatedStatus;
|
||||||
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.views.LinkedTextView;
|
import org.joinmastodon.android.ui.views.LinkedTextView;
|
||||||
@@ -35,18 +39,21 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
|||||||
private CharSequence parsedSpoilerText;
|
private CharSequence parsedSpoilerText;
|
||||||
public boolean textSelectable;
|
public boolean textSelectable;
|
||||||
public final Status status;
|
public final Status status;
|
||||||
|
public boolean translated = false;
|
||||||
|
public TranslatedStatus translation = null;
|
||||||
|
private AccountSession session;
|
||||||
|
|
||||||
public TextStatusDisplayItem(String parentID, CharSequence text, BaseStatusListFragment parentFragment, Status status){
|
public TextStatusDisplayItem(String parentID, CharSequence text, BaseStatusListFragment parentFragment, Status status){
|
||||||
super(parentID, parentFragment);
|
super(parentID, parentFragment);
|
||||||
this.text=text;
|
this.text=text;
|
||||||
this.status=status;
|
this.status=status;
|
||||||
// this.wantsTranslation=wantsTranslation;
|
|
||||||
emojiHelper.setText(text);
|
emojiHelper.setText(text);
|
||||||
if(!TextUtils.isEmpty(status.spoilerText)){
|
if(!TextUtils.isEmpty(status.spoilerText)){
|
||||||
parsedSpoilerText=HtmlParser.parseCustomEmoji(status.spoilerText, status.emojis);
|
parsedSpoilerText=HtmlParser.parseCustomEmoji(status.spoilerText, status.emojis);
|
||||||
spoilerEmojiHelper=new CustomEmojiHelper();
|
spoilerEmojiHelper=new CustomEmojiHelper();
|
||||||
spoilerEmojiHelper.setText(parsedSpoilerText);
|
spoilerEmojiHelper.setText(parsedSpoilerText);
|
||||||
}
|
}
|
||||||
|
session = AccountSessionManager.getInstance().getAccount(parentFragment.getAccountID());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -71,9 +78,10 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
|||||||
public static class Holder extends StatusDisplayItem.Holder<TextStatusDisplayItem> implements ImageLoaderViewHolder{
|
public static class Holder extends StatusDisplayItem.Holder<TextStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||||
private final LinkedTextView text;
|
private final LinkedTextView text;
|
||||||
private final LinearLayout spoilerHeader;
|
private final LinearLayout spoilerHeader;
|
||||||
private final TextView spoilerTitle, spoilerTitleInline;
|
private final TextView spoilerTitle, spoilerTitleInline, translateInfo;
|
||||||
private final View spoilerOverlay, borderTop, borderBottom;
|
private final View spoilerOverlay, borderTop, borderBottom, textWrap, translateWrap, translateProgress;
|
||||||
private final Drawable backgroundColor, borderColor;
|
private final Drawable backgroundColor, borderColor;
|
||||||
|
private final Button translateButton;
|
||||||
|
|
||||||
public Holder(Activity activity, ViewGroup parent){
|
public Holder(Activity activity, ViewGroup parent){
|
||||||
super(activity, R.layout.display_item_text, parent);
|
super(activity, R.layout.display_item_text, parent);
|
||||||
@@ -84,6 +92,11 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
|||||||
spoilerOverlay=findViewById(R.id.spoiler_overlay);
|
spoilerOverlay=findViewById(R.id.spoiler_overlay);
|
||||||
borderTop=findViewById(R.id.border_top);
|
borderTop=findViewById(R.id.border_top);
|
||||||
borderBottom=findViewById(R.id.border_bottom);
|
borderBottom=findViewById(R.id.border_bottom);
|
||||||
|
textWrap=findViewById(R.id.text_wrap);
|
||||||
|
translateWrap=findViewById(R.id.translate_wrap);
|
||||||
|
translateButton=findViewById(R.id.translate_btn);
|
||||||
|
translateInfo=findViewById(R.id.translate_info);
|
||||||
|
translateProgress=findViewById(R.id.translate_progress);
|
||||||
itemView.setOnClickListener(v->item.parentFragment.onRevealSpoilerClick(this));
|
itemView.setOnClickListener(v->item.parentFragment.onRevealSpoilerClick(this));
|
||||||
|
|
||||||
TypedValue outValue=new TypedValue();
|
TypedValue outValue=new TypedValue();
|
||||||
@@ -97,26 +110,9 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(TextStatusDisplayItem item){
|
public void onBind(TextStatusDisplayItem item){
|
||||||
if(item.status.wantsTranslation){
|
text.setText(item.translated
|
||||||
new GetStatusTranslation(item.status.id)
|
? HtmlParser.parse(item.translation.content, item.status.emojis, item.status.mentions, item.status.tags, item.parentFragment.getAccountID())
|
||||||
.setCallback(new Callback<StatusTranslation>(){
|
: item.text);
|
||||||
@Override
|
|
||||||
public void onSuccess(StatusTranslation status){
|
|
||||||
text.setText(status.getStrippedText());
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error){
|
|
||||||
item.status.wantsTranslation=false;
|
|
||||||
text.setText(item.text);
|
|
||||||
error.showToast(item.parentFragment.getActivity());
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
.wrapProgress(item.parentFragment.getActivity(), R.string.loading, true)
|
|
||||||
.exec(item.parentFragment.getAccountID());
|
|
||||||
}else{
|
|
||||||
text.setText(item.text);
|
|
||||||
}
|
|
||||||
text.setTextIsSelectable(item.textSelectable);
|
text.setTextIsSelectable(item.textSelectable);
|
||||||
spoilerTitleInline.setTextIsSelectable(item.textSelectable);
|
spoilerTitleInline.setTextIsSelectable(item.textSelectable);
|
||||||
text.setInvalidateOnEveryFrame(false);
|
text.setInvalidateOnEveryFrame(false);
|
||||||
@@ -130,20 +126,60 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
|||||||
if(item.status.spoilerRevealed){
|
if(item.status.spoilerRevealed){
|
||||||
spoilerOverlay.setVisibility(View.GONE);
|
spoilerOverlay.setVisibility(View.GONE);
|
||||||
spoilerHeader.setVisibility(View.VISIBLE);
|
spoilerHeader.setVisibility(View.VISIBLE);
|
||||||
text.setVisibility(View.VISIBLE);
|
textWrap.setVisibility(View.VISIBLE);
|
||||||
itemView.setClickable(false);
|
itemView.setClickable(false);
|
||||||
}else{
|
}else{
|
||||||
spoilerOverlay.setVisibility(View.VISIBLE);
|
spoilerOverlay.setVisibility(View.VISIBLE);
|
||||||
spoilerHeader.setVisibility(View.GONE);
|
spoilerHeader.setVisibility(View.GONE);
|
||||||
text.setVisibility(View.GONE);
|
textWrap.setVisibility(View.GONE);
|
||||||
itemView.setClickable(true);
|
itemView.setClickable(true);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
spoilerOverlay.setVisibility(View.GONE);
|
spoilerOverlay.setVisibility(View.GONE);
|
||||||
spoilerHeader.setVisibility(View.GONE);
|
spoilerHeader.setVisibility(View.GONE);
|
||||||
text.setVisibility(View.VISIBLE);
|
textWrap.setVisibility(View.VISIBLE);
|
||||||
itemView.setClickable(false);
|
itemView.setClickable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Instance instanceInfo = AccountSessionManager.getInstance().getInstanceInfo(item.session.domain);
|
||||||
|
boolean translateEnabled = instanceInfo.v2 != null && instanceInfo.v2.configuration.translation != null && instanceInfo.v2.configuration.translation.enabled;
|
||||||
|
|
||||||
|
translateWrap.setVisibility(translateEnabled &&
|
||||||
|
!item.status.visibility.isLessVisibleThan(StatusPrivacy.UNLISTED) &&
|
||||||
|
item.status.language != null &&
|
||||||
|
(item.session.preferences == null || !item.status.language.equalsIgnoreCase(item.session.preferences.postingDefaultLanguage))
|
||||||
|
? View.VISIBLE : View.GONE);
|
||||||
|
translateButton.setText(item.translated ? R.string.sk_translate_show_original : R.string.sk_translate_post);
|
||||||
|
translateInfo.setText(item.translated ? itemView.getResources().getString(R.string.sk_translated_using, item.translation.provider) : "");
|
||||||
|
translateButton.setOnClickListener(v->{
|
||||||
|
if (item.translation == null) {
|
||||||
|
translateProgress.setVisibility(View.VISIBLE);
|
||||||
|
translateButton.setClickable(false);
|
||||||
|
translateButton.setAlpha(.50f);
|
||||||
|
new TranslateStatus(item.status.id).setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(TranslatedStatus translatedStatus) {
|
||||||
|
item.translation = translatedStatus;
|
||||||
|
item.translated = true;
|
||||||
|
translateProgress.setVisibility(View.GONE);
|
||||||
|
translateButton.setClickable(true);
|
||||||
|
translateButton.setAlpha(1f);
|
||||||
|
rebind();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
translateProgress.setVisibility(View.GONE);
|
||||||
|
translateButton.setClickable(true);
|
||||||
|
translateButton.setAlpha(1f);
|
||||||
|
error.showToast(itemView.getContext());
|
||||||
|
}
|
||||||
|
}).exec(item.parentFragment.getAccountID());
|
||||||
|
} else {
|
||||||
|
item.translated = !item.translated;
|
||||||
|
rebind();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ public class HtmlParser{
|
|||||||
ssb.append("…", new DeleteWhenCopiedSpan(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
ssb.append("…", new DeleteWhenCopiedSpan(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
}else if(blockElements.contains(el.nodeName()) && node.nextSibling()!=null){
|
}else if(blockElements.contains(el.nodeName()) && node.nextSibling()!=null){
|
||||||
ssb.append("\n"); // line end
|
ssb.append("\n"); // line end
|
||||||
ssb.append("\n", new RelativeSizeSpan(0.75f), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // margin after block
|
ssb.append("\n", new RelativeSizeSpan(0.65f), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // margin after block
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package org.joinmastodon.android.ui.utils;
|
||||||
|
|
||||||
|
import static org.joinmastodon.android.GlobalUserPreferences.ColorPreference;
|
||||||
|
import static org.joinmastodon.android.GlobalUserPreferences.ThemePreference;
|
||||||
|
import static org.joinmastodon.android.GlobalUserPreferences.theme;
|
||||||
|
import static org.joinmastodon.android.GlobalUserPreferences.trueBlackTheme;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
|
||||||
|
import androidx.annotation.StyleRes;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ColorPalette {
|
||||||
|
public static final Map<GlobalUserPreferences.ColorPreference, ColorPalette> palettes = Map.of(
|
||||||
|
ColorPreference.MATERIAL3, new ColorPalette(R.style.ColorPalette_Material3)
|
||||||
|
.dark(R.style.ColorPalette_Material3_Dark, R.style.ColorPalette_Material3_AutoLightDark),
|
||||||
|
ColorPreference.PINK, new ColorPalette(R.style.ColorPalette_Pink),
|
||||||
|
ColorPreference.PURPLE, new ColorPalette(R.style.ColorPalette_Purple),
|
||||||
|
ColorPreference.GREEN, new ColorPalette(R.style.ColorPalette_Green),
|
||||||
|
ColorPreference.BLUE, new ColorPalette(R.style.ColorPalette_Blue),
|
||||||
|
ColorPreference.BROWN, new ColorPalette(R.style.ColorPalette_Brown),
|
||||||
|
ColorPreference.RED, new ColorPalette(R.style.ColorPalette_Red),
|
||||||
|
ColorPreference.YELLOW, new ColorPalette(R.style.ColorPalette_Yellow)
|
||||||
|
);
|
||||||
|
|
||||||
|
private @StyleRes int base;
|
||||||
|
private @StyleRes int autoDark;
|
||||||
|
private @StyleRes int light;
|
||||||
|
private @StyleRes int dark;
|
||||||
|
private @StyleRes int black;
|
||||||
|
private @StyleRes int autoBlack;
|
||||||
|
|
||||||
|
public ColorPalette(@StyleRes int baseRes) { base = baseRes; }
|
||||||
|
|
||||||
|
public ColorPalette(@StyleRes int lightRes, @StyleRes int darkRes, @StyleRes int autoDarkRes, @StyleRes int blackRes, @StyleRes int autoBlackRes) {
|
||||||
|
light = lightRes;
|
||||||
|
dark = darkRes;
|
||||||
|
autoDark = autoDarkRes;
|
||||||
|
black = blackRes;
|
||||||
|
autoBlack = autoBlackRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColorPalette light(@StyleRes int res) { light = res; return this; }
|
||||||
|
public ColorPalette dark(@StyleRes int res, @StyleRes int auto) { dark = res; autoDark = auto; return this; }
|
||||||
|
public ColorPalette black(@StyleRes int res, @StyleRes int auto) { dark = res; autoBlack = auto; return this; }
|
||||||
|
|
||||||
|
public void apply(Context context) {
|
||||||
|
if (!((dark != 0 && autoDark != 0) || (black != 0 && autoBlack != 0) || light != 0 || base != 0)) {
|
||||||
|
throw new IllegalStateException("Invalid color scheme definition");
|
||||||
|
}
|
||||||
|
|
||||||
|
Resources.Theme t = context.getTheme();
|
||||||
|
if (base != 0) t.applyStyle(base, true);
|
||||||
|
if (light != 0 && theme.equals(ThemePreference.LIGHT)) t.applyStyle(light, true);
|
||||||
|
else if (theme.equals(ThemePreference.DARK)) {
|
||||||
|
if (dark != 0 && !trueBlackTheme) t.applyStyle(dark, true);
|
||||||
|
else if (black != 0 && trueBlackTheme) t.applyStyle(black, true);
|
||||||
|
} else if (theme.equals(ThemePreference.AUTO)) {
|
||||||
|
if (autoDark != 0 && !trueBlackTheme) t.applyStyle(autoDark, true);
|
||||||
|
else if (autoBlack != 0 && trueBlackTheme) t.applyStyle(autoBlack, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,16 @@
|
|||||||
package org.joinmastodon.android.ui.utils;
|
package org.joinmastodon.android.ui.utils;
|
||||||
|
|
||||||
|
import static org.joinmastodon.android.GlobalUserPreferences.theme;
|
||||||
|
import static org.joinmastodon.android.GlobalUserPreferences.trueBlackTheme;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
@@ -11,6 +18,7 @@ import android.content.res.TypedArray;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Typeface;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.InsetDrawable;
|
import android.graphics.drawable.InsetDrawable;
|
||||||
@@ -19,10 +27,12 @@ import android.os.Build;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.os.VibrationEffect;
|
||||||
|
import android.os.Vibrator;
|
||||||
import android.provider.OpenableColumns;
|
import android.provider.OpenableColumns;
|
||||||
import android.provider.Settings;
|
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -42,6 +52,7 @@ import org.joinmastodon.android.api.requests.accounts.SetAccountMuted;
|
|||||||
import org.joinmastodon.android.api.requests.accounts.SetDomainBlocked;
|
import org.joinmastodon.android.api.requests.accounts.SetDomainBlocked;
|
||||||
import org.joinmastodon.android.api.requests.accounts.AuthorizeFollowRequest;
|
import org.joinmastodon.android.api.requests.accounts.AuthorizeFollowRequest;
|
||||||
import org.joinmastodon.android.api.requests.accounts.RejectFollowRequest;
|
import org.joinmastodon.android.api.requests.accounts.RejectFollowRequest;
|
||||||
|
import org.joinmastodon.android.api.requests.search.GetSearchResults;
|
||||||
import org.joinmastodon.android.api.requests.statuses.DeleteStatus;
|
import org.joinmastodon.android.api.requests.statuses.DeleteStatus;
|
||||||
import org.joinmastodon.android.api.requests.statuses.GetStatusByID;
|
import org.joinmastodon.android.api.requests.statuses.GetStatusByID;
|
||||||
import org.joinmastodon.android.api.requests.statuses.SetStatusPinned;
|
import org.joinmastodon.android.api.requests.statuses.SetStatusPinned;
|
||||||
@@ -52,8 +63,6 @@ import org.joinmastodon.android.events.NotificationDeletedEvent;
|
|||||||
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
||||||
import org.joinmastodon.android.events.StatusDeletedEvent;
|
import org.joinmastodon.android.events.StatusDeletedEvent;
|
||||||
import org.joinmastodon.android.events.StatusUnpinnedEvent;
|
import org.joinmastodon.android.events.StatusUnpinnedEvent;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
|
||||||
import org.joinmastodon.android.fragments.ComposeFragment;
|
|
||||||
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
||||||
import org.joinmastodon.android.fragments.ListTimelineFragment;
|
import org.joinmastodon.android.fragments.ListTimelineFragment;
|
||||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||||
@@ -62,6 +71,7 @@ import org.joinmastodon.android.model.Account;
|
|||||||
import org.joinmastodon.android.model.Emoji;
|
import org.joinmastodon.android.model.Emoji;
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
|
import org.joinmastodon.android.model.SearchResults;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
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;
|
||||||
@@ -70,6 +80,8 @@ import org.parceler.Parcels;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
@@ -656,93 +668,76 @@ public class UiUtils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void setUserPreferredTheme(Context context){
|
public static void setUserPreferredTheme(Context context){
|
||||||
// boolean isDarkTheme = isDarkTheme();
|
context.setTheme(switch (theme) {
|
||||||
switch(GlobalUserPreferences.color){
|
case LIGHT -> R.style.Theme_Mastodon_Light;
|
||||||
case PINK:
|
case DARK -> trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack : R.style.Theme_Mastodon_Dark;
|
||||||
context.setTheme(switch(GlobalUserPreferences.theme){
|
default -> trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack : R.style.Theme_Mastodon_AutoLightDark;
|
||||||
case AUTO ->
|
});
|
||||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack : R.style.Theme_Mastodon_AutoLightDark;
|
|
||||||
case LIGHT ->
|
|
||||||
R.style.Theme_Mastodon_Light;
|
|
||||||
case DARK ->
|
|
||||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack : R.style.Theme_Mastodon_Dark;
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case PURPLE:
|
|
||||||
context.setTheme(switch(GlobalUserPreferences.theme){
|
|
||||||
case AUTO ->
|
|
||||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Original : R.style.Theme_Mastodon_AutoLightDark_Original;
|
|
||||||
case LIGHT ->
|
|
||||||
R.style.Theme_Mastodon_Light_Original;
|
|
||||||
case DARK ->
|
|
||||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Original : R.style.Theme_Mastodon_Dark_Original;
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case GREEN:
|
|
||||||
context.setTheme(switch(GlobalUserPreferences.theme){
|
|
||||||
case AUTO ->
|
|
||||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Green : R.style.Theme_Mastodon_AutoLightDark_Green;
|
|
||||||
case LIGHT ->
|
|
||||||
R.style.Theme_Mastodon_Light_Green;
|
|
||||||
case DARK ->
|
|
||||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Green : R.style.Theme_Mastodon_Dark_Green;
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case BLUE:
|
|
||||||
context.setTheme(switch(GlobalUserPreferences.theme){
|
|
||||||
case AUTO ->
|
|
||||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Blue : R.style.Theme_Mastodon_AutoLightDark_Blue;
|
|
||||||
case LIGHT ->
|
|
||||||
R.style.Theme_Mastodon_Light_Blue;
|
|
||||||
case DARK ->
|
|
||||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Blue : R.style.Theme_Mastodon_Dark_Blue;
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case ORANGE:
|
|
||||||
context.setTheme(switch(GlobalUserPreferences.theme){
|
|
||||||
case AUTO ->
|
|
||||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Orange : R.style.Theme_Mastodon_AutoLightDark_Orange;
|
|
||||||
case LIGHT ->
|
|
||||||
R.style.Theme_Mastodon_Light_Orange;
|
|
||||||
case DARK ->
|
|
||||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Orange : R.style.Theme_Mastodon_Dark_Orange;
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case YELLOW:
|
|
||||||
context.setTheme(switch(GlobalUserPreferences.theme){
|
|
||||||
case AUTO ->
|
|
||||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Yellow : R.style.Theme_Mastodon_AutoLightDark_Yellow;
|
|
||||||
case LIGHT ->
|
|
||||||
R.style.Theme_Mastodon_Light_Yellow;
|
|
||||||
case DARK ->
|
|
||||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Yellow : R.style.Theme_Mastodon_Dark_Yellow;
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case MATERIAL3:
|
|
||||||
context.setTheme(switch(GlobalUserPreferences.theme){
|
|
||||||
case AUTO ->
|
|
||||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Material3 : R.style.Theme_Mastodon_AutoLightDark_Material3;
|
|
||||||
case LIGHT ->
|
|
||||||
R.style.Theme_Mastodon_Light_Material3;
|
|
||||||
case DARK ->
|
|
||||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Material3 : R.style.Theme_Mastodon_Dark_Material3;
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
ColorPalette palette = ColorPalette.palettes.get(GlobalUserPreferences.color);
|
||||||
|
if (palette != null) palette.apply(context);
|
||||||
}
|
}
|
||||||
public static boolean isDarkTheme(){
|
public static boolean isDarkTheme(){
|
||||||
if(GlobalUserPreferences.theme==GlobalUserPreferences.ThemePreference.AUTO)
|
if(theme==GlobalUserPreferences.ThemePreference.AUTO)
|
||||||
return (MastodonApp.context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)==Configuration.UI_MODE_NIGHT_YES;
|
return (MastodonApp.context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)==Configuration.UI_MODE_NIGHT_YES;
|
||||||
return GlobalUserPreferences.theme==GlobalUserPreferences.ThemePreference.DARK;
|
return theme==GlobalUserPreferences.ThemePreference.DARK;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void openURL(Context context, @Nullable String accountID, String url){
|
// https://mastodon.foo.bar/@User
|
||||||
|
// https://mastodon.foo.bar/@User/43456787654678
|
||||||
|
// https://pleroma.foo.bar/users/User
|
||||||
|
// https://pleroma.foo.bar/users/9qTHT2ANWUdXzENqC0
|
||||||
|
// https://pleroma.foo.bar/notice/9sBHWIlwwGZi5QGlHc
|
||||||
|
// https://pleroma.foo.bar/objects/d4643c42-3ae0-4b73-b8b0-c725f5819207
|
||||||
|
// https://friendica.foo.bar/profile/user
|
||||||
|
// https://friendica.foo.bar/display/d4643c42-3ae0-4b73-b8b0-c725f5819207
|
||||||
|
// https://misskey.foo.bar/notes/83w6r388br (always lowercase)
|
||||||
|
// https://pixelfed.social/p/connyduck/391263492998670833
|
||||||
|
// https://pixelfed.social/connyduck
|
||||||
|
// https://gts.foo.bar/@goblin/statuses/01GH9XANCJ0TA8Y95VE9H3Y0Q2
|
||||||
|
// https://gts.foo.bar/@goblin
|
||||||
|
// https://foo.microblog.pub/o/5b64045effd24f48a27d7059f6cb38f5
|
||||||
|
//
|
||||||
|
// COPIED FROM https://github.com/tuskyapp/Tusky/blob/develop/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt
|
||||||
|
public static boolean looksLikeMastodonUrl(String urlString) {
|
||||||
|
URI uri;
|
||||||
|
try {
|
||||||
|
uri = new URI(urlString);
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri.getQuery() != null || uri.getFragment() != null || uri.getPath() == null) return false;
|
||||||
|
|
||||||
|
String it = uri.getPath();
|
||||||
|
return it.matches("^/@[^/]+$") ||
|
||||||
|
it.matches("^/@[^/]+/\\d+$") ||
|
||||||
|
it.matches("^/users/\\w+$") ||
|
||||||
|
it.matches("^/notice/[a-zA-Z0-9]+$") ||
|
||||||
|
it.matches("^/objects/[-a-f0-9]+$") ||
|
||||||
|
it.matches("^/notes/[a-z0-9]+$") ||
|
||||||
|
it.matches("^/display/[-a-f0-9]+$") ||
|
||||||
|
it.matches("^/profile/\\w+$") ||
|
||||||
|
it.matches("^/p/\\w+/\\d+$") ||
|
||||||
|
it.matches("^/\\w+$") ||
|
||||||
|
it.matches("^/@[^/]+/statuses/[a-zA-Z0-9]+$") ||
|
||||||
|
it.matches("^/o/[a-f0-9]+$");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openURL(Context context, String accountID, String url){
|
||||||
|
Consumer<ProgressDialog> transformDialogForLookup = dialog -> {
|
||||||
|
dialog.setTitle(R.string.loading_fediverse_resource_title);
|
||||||
|
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.cancel), (d, which) -> d.cancel());
|
||||||
|
dialog.setButton(DialogInterface.BUTTON_POSITIVE, context.getString(R.string.open_in_browser), (d, which) -> {
|
||||||
|
d.cancel();
|
||||||
|
launchWebBrowser(context, url);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Uri uri=Uri.parse(url);
|
Uri uri=Uri.parse(url);
|
||||||
if(accountID!=null && "https".equals(uri.getScheme()) && AccountSessionManager.getInstance().getAccount(accountID).domain.equalsIgnoreCase(uri.getAuthority())){
|
List<String> path=uri.getPathSegments();
|
||||||
List<String> path=uri.getPathSegments();
|
if(accountID!=null && "https".equals(uri.getScheme())){
|
||||||
// Match URLs like https://mastodon.social/@Gargron/108132679274083591
|
if(path.size()==2 && path.get(0).matches("^@[a-zA-Z0-9_]+$") && path.get(1).matches("^[0-9]+$") && AccountSessionManager.getInstance().getAccount(accountID).domain.equalsIgnoreCase(uri.getAuthority())){
|
||||||
if(path.size()==2 && path.get(0).matches("^@[a-zA-Z0-9_]+$") && path.get(1).matches("^[0-9]+$")){
|
|
||||||
new GetStatusByID(path.get(1))
|
new GetStatusByID(path.get(1))
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
@@ -759,11 +754,61 @@ public class UiUtils{
|
|||||||
launchWebBrowser(context, url);
|
launchWebBrowser(context, url);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.wrapProgress((Activity)context, R.string.loading, true)
|
.wrapProgress((Activity)context, R.string.loading, true, transformDialogForLookup)
|
||||||
|
.exec(accountID);
|
||||||
|
return;
|
||||||
|
} else if (looksLikeMastodonUrl(url)) {
|
||||||
|
new GetSearchResults(url, null, true)
|
||||||
|
.setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(SearchResults results) {
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
if (!results.statuses.isEmpty()) {
|
||||||
|
args.putParcelable("status", Parcels.wrap(results.statuses.get(0)));
|
||||||
|
Nav.go((Activity) context, ThreadFragment.class, args);
|
||||||
|
} else if (!results.accounts.isEmpty()) {
|
||||||
|
args.putParcelable("profileAccount", Parcels.wrap(results.accounts.get(0)));
|
||||||
|
Nav.go((Activity) context, ProfileFragment.class, args);
|
||||||
|
} else {
|
||||||
|
launchWebBrowser(context, url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
error.showToast(context);
|
||||||
|
launchWebBrowser(context, url);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.wrapProgress((Activity)context, R.string.loading, true, transformDialogForLookup)
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
launchWebBrowser(context, url);
|
launchWebBrowser(context, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void copyText(Context context, String text) {
|
||||||
|
context.getSystemService(ClipboardManager.class).setPrimaryClip(ClipData.newPlainText(null, text));
|
||||||
|
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.TIRAMISU || UiUtils.isMIUI()){ // Android 13+ SystemUI shows its own thing when you put things into the clipboard
|
||||||
|
Toast.makeText(context, R.string.text_copied, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) vibrator.vibrate(VibrationEffect.createOneShot(50, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||||
|
else vibrator.vibrate(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getSystemProperty(String key){
|
||||||
|
try{
|
||||||
|
Class<?> props=Class.forName("android.os.SystemProperties");
|
||||||
|
Method get=props.getMethod("get", String.class);
|
||||||
|
return (String)get.invoke(null, key);
|
||||||
|
}catch(Exception ignore){}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMIUI(){
|
||||||
|
return !TextUtils.isEmpty(getSystemProperty("ro.miui.ui.version.code"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package org.joinmastodon.android.ui.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
|
public class FilterChipView extends Button{
|
||||||
|
|
||||||
|
private boolean currentlySelected;
|
||||||
|
|
||||||
|
public FilterChipView(Context context){
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FilterChipView(Context context, AttributeSet attrs){
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FilterChipView(Context context, AttributeSet attrs, int defStyle){
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
setCompoundDrawablePadding(V.dp(8));
|
||||||
|
setBackgroundResource(R.drawable.bg_filter_chip);
|
||||||
|
setTextAppearance(R.style.m3_label_large);
|
||||||
|
setTextColor(getResources().getColorStateList(R.color.filter_chip_text, context.getTheme()));
|
||||||
|
setCompoundDrawableTintList(ColorStateList.valueOf(UiUtils.getThemeColor(context, R.attr.colorM3OnSurface)));
|
||||||
|
updatePadding();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawableStateChanged(){
|
||||||
|
super.drawableStateChanged();
|
||||||
|
if(currentlySelected==isSelected())
|
||||||
|
return;
|
||||||
|
currentlySelected=isSelected();
|
||||||
|
Drawable start=currentlySelected ? getResources().getDrawable(R.drawable.ic_baseline_check_18, getContext().getTheme()) : null;
|
||||||
|
Drawable end=getCompoundDrawablesRelative()[2];
|
||||||
|
setCompoundDrawablesRelativeWithIntrinsicBounds(start, null, end, null);
|
||||||
|
updatePadding();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePadding(){
|
||||||
|
int vertical=V.dp(6);
|
||||||
|
Drawable[] drawables=getCompoundDrawablesRelative();
|
||||||
|
setPaddingRelative(V.dp(drawables[0]==null ? 16 : 8), vertical, V.dp(drawables[2]==null ? 16 : 8), vertical);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDrawableEnd(@DrawableRes int drawable){
|
||||||
|
setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, drawable, 0);
|
||||||
|
updatePadding();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,19 +5,34 @@ import android.animation.AnimatorListenerAdapter;
|
|||||||
import android.animation.AnimatorSet;
|
import android.animation.AnimatorSet;
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.ColorFilter;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
import android.graphics.Region;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.InsetDrawable;
|
||||||
|
import android.os.Build;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewTreeObserver;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
|
import androidx.annotation.Keep;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
@@ -28,6 +43,10 @@ public class FloatingHintEditTextLayout extends FrameLayout{
|
|||||||
private int offsetY;
|
private int offsetY;
|
||||||
private boolean hintVisible;
|
private boolean hintVisible;
|
||||||
private Animator currentAnim;
|
private Animator currentAnim;
|
||||||
|
private float animProgress;
|
||||||
|
private RectF tmpRect=new RectF();
|
||||||
|
private ColorStateList labelColors, origHintColors;
|
||||||
|
private boolean errorState;
|
||||||
|
|
||||||
public FloatingHintEditTextLayout(Context context){
|
public FloatingHintEditTextLayout(Context context){
|
||||||
this(context, null);
|
this(context, null);
|
||||||
@@ -44,7 +63,9 @@ public class FloatingHintEditTextLayout extends FrameLayout{
|
|||||||
TypedArray ta=context.obtainStyledAttributes(attrs, R.styleable.FloatingHintEditTextLayout);
|
TypedArray ta=context.obtainStyledAttributes(attrs, R.styleable.FloatingHintEditTextLayout);
|
||||||
labelTextSize=ta.getDimensionPixelSize(R.styleable.FloatingHintEditTextLayout_android_labelTextSize, V.dp(12));
|
labelTextSize=ta.getDimensionPixelSize(R.styleable.FloatingHintEditTextLayout_android_labelTextSize, V.dp(12));
|
||||||
offsetY=ta.getDimensionPixelOffset(R.styleable.FloatingHintEditTextLayout_editTextOffsetY, 0);
|
offsetY=ta.getDimensionPixelOffset(R.styleable.FloatingHintEditTextLayout_editTextOffsetY, 0);
|
||||||
|
labelColors=ta.getColorStateList(R.styleable.FloatingHintEditTextLayout_labelTextColor);
|
||||||
ta.recycle();
|
ta.recycle();
|
||||||
|
setAddStatesFromChildren(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -58,13 +79,15 @@ public class FloatingHintEditTextLayout extends FrameLayout{
|
|||||||
|
|
||||||
label=new TextView(getContext());
|
label=new TextView(getContext());
|
||||||
label.setTextSize(TypedValue.COMPLEX_UNIT_PX, labelTextSize);
|
label.setTextSize(TypedValue.COMPLEX_UNIT_PX, labelTextSize);
|
||||||
label.setTextColor(edit.getHintTextColors());
|
// label.setTextColor(labelColors==null ? edit.getHintTextColors() : labelColors);
|
||||||
|
origHintColors=edit.getHintTextColors();
|
||||||
label.setText(edit.getHint());
|
label.setText(edit.getHint());
|
||||||
label.setSingleLine();
|
label.setSingleLine();
|
||||||
label.setPivotX(0f);
|
label.setPivotX(0f);
|
||||||
label.setPivotY(0f);
|
label.setPivotY(0f);
|
||||||
|
label.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
|
||||||
LayoutParams lp=new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.TOP);
|
LayoutParams lp=new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.TOP);
|
||||||
lp.setMarginStart(edit.getPaddingStart());
|
lp.setMarginStart(edit.getPaddingStart()+((LayoutParams)edit.getLayoutParams()).getMarginStart());
|
||||||
addView(label, lp);
|
addView(label, lp);
|
||||||
|
|
||||||
hintVisible=edit.getText().length()==0;
|
hintVisible=edit.getText().length()==0;
|
||||||
@@ -75,6 +98,11 @@ public class FloatingHintEditTextLayout extends FrameLayout{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onTextChanged(Editable text){
|
private void onTextChanged(Editable text){
|
||||||
|
if(errorState){
|
||||||
|
errorState=false;
|
||||||
|
setForeground(getResources().getDrawable(R.drawable.bg_m3_outlined_text_field));
|
||||||
|
refreshDrawableState();
|
||||||
|
}
|
||||||
boolean newHintVisible=text.length()==0;
|
boolean newHintVisible=text.length()==0;
|
||||||
if(newHintVisible==hintVisible)
|
if(newHintVisible==hintVisible)
|
||||||
return;
|
return;
|
||||||
@@ -83,42 +111,210 @@ public class FloatingHintEditTextLayout extends FrameLayout{
|
|||||||
hintVisible=newHintVisible;
|
hintVisible=newHintVisible;
|
||||||
|
|
||||||
label.setAlpha(1);
|
label.setAlpha(1);
|
||||||
float scale=edit.getLineHeight()/(float)label.getLineHeight();
|
edit.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
||||||
float transY=edit.getHeight()/2f-edit.getLineHeight()/2f+(edit.getTop()-label.getTop())-(label.getHeight()/2f-label.getLineHeight()/2f);
|
|
||||||
|
|
||||||
AnimatorSet anim=new AnimatorSet();
|
|
||||||
if(hintVisible){
|
|
||||||
anim.playTogether(
|
|
||||||
ObjectAnimator.ofFloat(edit, TRANSLATION_Y, 0),
|
|
||||||
ObjectAnimator.ofFloat(label, SCALE_X, scale),
|
|
||||||
ObjectAnimator.ofFloat(label, SCALE_Y, scale),
|
|
||||||
ObjectAnimator.ofFloat(label, TRANSLATION_Y, transY)
|
|
||||||
);
|
|
||||||
edit.setHintTextColor(0);
|
|
||||||
}else{
|
|
||||||
label.setScaleX(scale);
|
|
||||||
label.setScaleY(scale);
|
|
||||||
label.setTranslationY(transY);
|
|
||||||
anim.playTogether(
|
|
||||||
ObjectAnimator.ofFloat(edit, TRANSLATION_Y, offsetY),
|
|
||||||
ObjectAnimator.ofFloat(label, SCALE_X, 1f),
|
|
||||||
ObjectAnimator.ofFloat(label, SCALE_Y, 1f),
|
|
||||||
ObjectAnimator.ofFloat(label, TRANSLATION_Y, 0f)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
anim.setDuration(150);
|
|
||||||
anim.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
|
||||||
anim.start();
|
|
||||||
anim.addListener(new AnimatorListenerAdapter(){
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation){
|
public boolean onPreDraw(){
|
||||||
currentAnim=null;
|
edit.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||||
|
|
||||||
|
float scale=edit.getLineHeight()/(float)label.getLineHeight();
|
||||||
|
float transY=edit.getHeight()/2f-edit.getLineHeight()/2f+(edit.getTop()-label.getTop())-(label.getHeight()/2f-label.getLineHeight()/2f);
|
||||||
|
|
||||||
|
AnimatorSet anim=new AnimatorSet();
|
||||||
if(hintVisible){
|
if(hintVisible){
|
||||||
label.setAlpha(0);
|
anim.playTogether(
|
||||||
edit.setHintTextColor(label.getTextColors());
|
ObjectAnimator.ofFloat(edit, TRANSLATION_Y, 0),
|
||||||
|
ObjectAnimator.ofFloat(label, SCALE_X, scale),
|
||||||
|
ObjectAnimator.ofFloat(label, SCALE_Y, scale),
|
||||||
|
ObjectAnimator.ofFloat(label, TRANSLATION_Y, transY),
|
||||||
|
ObjectAnimator.ofFloat(FloatingHintEditTextLayout.this, "animProgress", 0f)
|
||||||
|
);
|
||||||
|
edit.setHintTextColor(0);
|
||||||
|
}else{
|
||||||
|
label.setScaleX(scale);
|
||||||
|
label.setScaleY(scale);
|
||||||
|
label.setTranslationY(transY);
|
||||||
|
anim.playTogether(
|
||||||
|
ObjectAnimator.ofFloat(edit, TRANSLATION_Y, offsetY),
|
||||||
|
ObjectAnimator.ofFloat(label, SCALE_X, 1f),
|
||||||
|
ObjectAnimator.ofFloat(label, SCALE_Y, 1f),
|
||||||
|
ObjectAnimator.ofFloat(label, TRANSLATION_Y, 0f),
|
||||||
|
ObjectAnimator.ofFloat(FloatingHintEditTextLayout.this, "animProgress", 1f)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
anim.setDuration(150);
|
||||||
|
anim.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
|
anim.start();
|
||||||
|
anim.addListener(new AnimatorListenerAdapter(){
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation){
|
||||||
|
currentAnim=null;
|
||||||
|
if(hintVisible){
|
||||||
|
label.setAlpha(0);
|
||||||
|
edit.setHintTextColor(origHintColors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
currentAnim=anim;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
currentAnim=anim;
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
public void setAnimProgress(float progress){
|
||||||
|
animProgress=progress;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
public float getAnimProgress(){
|
||||||
|
return animProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDrawForeground(Canvas canvas){
|
||||||
|
if(getForeground()!=null && animProgress>0){
|
||||||
|
canvas.save();
|
||||||
|
float width=(label.getWidth()+V.dp(8))*animProgress;
|
||||||
|
float centerX=label.getLeft()+label.getWidth()/2f;
|
||||||
|
tmpRect.set(centerX-width/2f, label.getTop(), centerX+width/2f, label.getBottom());
|
||||||
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O)
|
||||||
|
canvas.clipOutRect(tmpRect);
|
||||||
|
else
|
||||||
|
canvas.clipRect(tmpRect, Region.Op.DIFFERENCE);
|
||||||
|
super.onDrawForeground(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
}else{
|
||||||
|
super.onDrawForeground(canvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setForeground(Drawable foreground){
|
||||||
|
super.setForeground(new PaddedForegroundDrawable(foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Drawable getForeground(){
|
||||||
|
if(super.getForeground() instanceof PaddedForegroundDrawable pfd){
|
||||||
|
return pfd.wrapped;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawableStateChanged(){
|
||||||
|
super.drawableStateChanged();
|
||||||
|
if(label==null || errorState)
|
||||||
|
return;
|
||||||
|
ColorStateList color=labelColors==null ? origHintColors : labelColors;
|
||||||
|
label.setTextColor(color.getColorForState(getDrawableState(), 0xff00ff00));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorState(){
|
||||||
|
if(errorState)
|
||||||
|
return;
|
||||||
|
errorState=true;
|
||||||
|
setForeground(getResources().getDrawable(R.drawable.bg_m3_outlined_text_field_error, getContext().getTheme()));
|
||||||
|
label.setTextColor(UiUtils.getThemeColor(getContext(), R.attr.colorM3Error));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PaddedForegroundDrawable extends Drawable{
|
||||||
|
private final Drawable wrapped;
|
||||||
|
|
||||||
|
private PaddedForegroundDrawable(Drawable wrapped){
|
||||||
|
this.wrapped=wrapped;
|
||||||
|
wrapped.setCallback(new Callback(){
|
||||||
|
@Override
|
||||||
|
public void invalidateDrawable(@NonNull Drawable who){
|
||||||
|
invalidateSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when){
|
||||||
|
scheduleSelf(what, when);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what){
|
||||||
|
unscheduleSelf(what);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(@NonNull Canvas canvas){
|
||||||
|
wrapped.draw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAlpha(int alpha){
|
||||||
|
wrapped.setAlpha(alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColorFilter(@Nullable ColorFilter colorFilter){
|
||||||
|
wrapped.setColorFilter(colorFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOpacity(){
|
||||||
|
return wrapped.getOpacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setState(@NonNull int[] stateSet){
|
||||||
|
return wrapped.setState(stateSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLayoutDirection(){
|
||||||
|
return wrapped.getLayoutDirection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAlpha(){
|
||||||
|
return wrapped.getAlpha();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ColorFilter getColorFilter(){
|
||||||
|
return wrapped.getColorFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStateful(){
|
||||||
|
return wrapped.isStateful();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public int[] getState(){
|
||||||
|
return wrapped.getState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Drawable getCurrent(){
|
||||||
|
return wrapped.getCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyTheme(@NonNull Resources.Theme t){
|
||||||
|
wrapped.applyTheme(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canApplyTheme(){
|
||||||
|
return wrapped.canApplyTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBoundsChange(@NonNull Rect bounds){
|
||||||
|
super.onBoundsChange(bounds);
|
||||||
|
LayoutParams lp=(LayoutParams) edit.getLayoutParams();
|
||||||
|
wrapped.setBounds(bounds.left+lp.leftMargin-V.dp(12), bounds.top, bounds.right-lp.rightMargin+V.dp(12), bounds.bottom);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
5
mastodon/src/main/res/color/button_text_m3_text.xml
Normal file
5
mastodon/src/main/res/color/button_text_m3_text.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="?colorM3Primary" android:state_enabled="true"/>
|
||||||
|
<item android:color="?colorM3OnSurface" android:alpha="0.38"/>
|
||||||
|
</selector>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:color="?colorSecondary"/>
|
<item android:color="?colorGray50"/>
|
||||||
</selector>
|
</selector>
|
||||||
5
mastodon/src/main/res/color/filter_chip_text.xml
Normal file
5
mastodon/src/main/res/color/filter_chip_text.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="?colorM3OnSecondaryContainer" android:state_selected="true"/>
|
||||||
|
<item android:color="?colorM3OnSurface"/>
|
||||||
|
</selector>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="?colorM3OnSecondaryContainer" android:alpha="0.12"/>
|
||||||
|
</selector>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="?colorM3OnSurfaceVariant" android:alpha="0.12"/>
|
||||||
|
</selector>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="?colorM3Primary" android:state_focused="true"/>
|
||||||
|
<item android:color="?colorM3OnSurfaceVariant"/>
|
||||||
|
</selector>
|
||||||
4
mastodon/src/main/res/color/m3_primary_overlay.xml
Normal file
4
mastodon/src/main/res/color/m3_primary_overlay.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="?colorM3Primary" android:alpha="0.12"/>
|
||||||
|
</selector>
|
||||||
6
mastodon/src/main/res/color/translate_icon.xml
Normal file
6
mastodon/src/main/res/color/translate_icon.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="?android:colorAccent" android:state_selected="true"/>
|
||||||
|
<item android:color="?android:textColorSecondary" android:state_enabled="true"/>
|
||||||
|
<item android:color="?colorSecondary"/>
|
||||||
|
</selector>
|
||||||
@@ -1,5 +1,18 @@
|
|||||||
<vector android:height="108dp" android:viewportHeight="320"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:viewportWidth="320" android:width="108dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
<path android:fillColor="#fff" android:pathData="M166.45,143.88c-4.6,-0.61 -9.13,5 -10.12,12.53s1.93,14.12 6.53,14.72 9.13,-5 10.12,-12.53S171.05,144.48 166.45,143.88ZM166.53,164.39a3.65,3.65 0,1 1,2 -4.78A3.66,3.66 0,0 1,166.53 164.39Z"/>
|
android:width="108dp"
|
||||||
<path android:fillColor="#fff" android:pathData="M233.41,296.2c-0.41,-7.41 -1.13,-14.84 -1.84,-22.24l-0.24,-2.52c0,-0.18 0,-0.36 -0.05,-0.54 -0.11,-1.1 -0.21,-2.19 -0.31,-3.29q-0.54,-5.83 -1,-11.72 -0.71,-8.82 -1.37,-17.7 -0.24,-3 -0.46,-5.91c-0.63,-7.89 -1.29,-15.8 -2.09,-23.68 -1.13,-11.06 -2.54,-22.09 -4.49,-33a6.62,6.62 0,0 0,-0.9 0.48c-8.37,5.44 -14.27,29.16 -17.82,37.65a1.24,1.24 0,0 1,-2.37 -0.36c-0.09,-0.92 -0.16,-1.83 -0.21,-2.75l2.78,-1.79 -2.91,-2.2c0,-0.23 0,-0.45 0,-0.68v-0.48c0,-0.26 0,-0.51 0,-0.77l2.71,-1.72 -2.58,-3c0,-0.62 0.05,-1.25 0.09,-1.87l3.57,-1 -3.19,-3.83c0.05,-0.58 0.12,-1.16 0.18,-1.74l4.77,-1.39 -4,-4.46 0.24,-1.5 5.55,-1.61 -4.54,-4c0.08,-0.4 0.17,-0.8 0.26,-1.2l5,-1.44 -3.93,-3.22c0.08,-0.34 0.15,-0.69 0.23,-1l6.51,-1.89 -4.68,-5.41c0.07,-0.25 0.14,-0.5 0.22,-0.75l5.89,-0.82 -3.72,-5.48 0.3,-0.69 5.59,1.21 -3.33,-5.65c0.18,-0.3 0.37,-0.59 0.55,-0.88l5.95,0.95 -2.68,-5 0,0c-1.39,-6.49 -6.72,-19.33 -6.72,-19.33a62.21,62.21 0,0 0,-5.78 -11.48c-9.59,-15.31 -27.16,-28.64 -44.56,-28.64a32.59,32.59 0,0 0,-7.59 0.89c-17.93,4.31 -32.31,24.52 -40.65,39.83 -9.37,17.22 -14.3,36.39 -18.8,55.34 -6.77,28.47 -10.5,57.56 -13.22,86.66 -2.25,24 -10,50.88 14.49,65.32 11.22,6.6 24.36,10.79 37.17,12.9a98,98 0,0 0,16 1.15c4.72,0 9.45,-0.22 14.16,-0.44l49.8,-2.29c0.72,0 1.46,-0.06 2.21,-0.06 4,0 8.15,0.65 10.26,3.85 0.25,-2.66 -2.08,-7 -4.86,-11.6l5.7,-1.29c0.15,-4.58 0.6,-8 2.5,-5.38 0.93,1.29 2.19,2.73 3.78,2.54 1.86,-0.23 2.65,-2.45 3,-4.3C234,319.42 234.06,307.82 233.41,296.2ZM158.81,334.39c-4.45,0.2 -9.06,0.42 -13.47,0.42a83.24,83.24 0,0 1,-13.54 -0.95c-12,-2 -23.1,-5.8 -32,-11 -5.66,-3.33 -8.42,-7.32 -9.52,-13.75 -1.25,-7.35 -0.11,-16.53 1.1,-26.24 0.45,-3.59 0.91,-7.3 1.26,-11 3.25,-34.69 7.22,-60.78 12.88,-84.59 4.38,-18.42 9,-36.24 17.39,-51.64 9.79,-18 21.37,-30.11 31,-32.42a17.66,17.66 0,0 1,4.08 -0.47c6.64,0 14.53,3.54 21.63,9.71 6.86,6 12.19,13.64 14.36,20.64 -5.36,13.5 -10.52,31.41 -12.51,44.37 -5.36,34.75 -9,99.6 -7.6,156.26Z"/>
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<group android:scaleX="2.5091188"
|
||||||
|
android:scaleY="2.5091188">
|
||||||
|
<path
|
||||||
|
android:pathData="m13.779,16.29c-1.097,0 -1.983,0.387 -2.658,1.141 -0.655,0.754 -0.981,1.771 -0.981,3.053l0,6.27L12.622,26.754L12.622,20.668c0,-1.284 0.539,-1.935 1.618,-1.935 1.192,0 1.791,0.773 1.791,2.3l0,3.331l2.468,0l0,-3.331c0,-1.527 0.598,-2.3 1.791,-2.3 1.078,0 1.618,0.651 1.618,1.935l0,6.085l2.482,0l0,-6.27c0,-1.281 -0.326,-2.299 -0.981,-3.053 -0.676,-0.754 -1.56,-1.141 -2.658,-1.141 -1.27,0 -2.232,0.488 -2.868,1.466L17.265,18.794 16.646,17.756C16.01,16.778 15.049,16.29 13.779,16.29Z"
|
||||||
|
android:strokeWidth="0.796"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m29.087,26.754q-1.113,0 -1.986,-0.493 -0.873,-0.507 -1.366,-1.366 -0.479,-0.873 -0.479,-1.958 0,-1.07 0.479,-1.944 0.493,-0.873 1.366,-1.366 0.873,-0.507 1.986,-0.507 1.099,0 1.972,0.507 0.873,0.493 1.352,1.366 0.493,0.873 0.493,1.944 0,1.085 -0.493,1.958 -0.479,0.859 -1.352,1.366 -0.873,0.493 -1.972,0.493zM29.087,25.049q0.535,0 0.986,-0.254 0.451,-0.254 0.718,-0.732 0.268,-0.479 0.268,-1.127 0,-0.634 -0.268,-1.113 -0.268,-0.479 -0.718,-0.732 -0.451,-0.254 -0.986,-0.254 -0.535,0 -0.986,0.254 -0.451,0.254 -0.732,0.732 -0.268,0.479 -0.268,1.113 0,0.634 0.268,1.127 0.282,0.479 0.732,0.732 0.451,0.254 0.986,0.254z"
|
||||||
|
android:strokeWidth="0.687"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</group>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
9
mastodon/src/main/res/drawable/bg_button_m3_text.xml
Normal file
9
mastodon/src/main/res/drawable/bg_button_m3_text.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple android:color="@color/m3_primary_overlay" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@android:id/mask">
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#000"/>
|
||||||
|
<corners android:radius="20dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<solid android:color="?colorWindowBackground"/>
|
<solid android:color="?colorGray800"/>
|
||||||
<corners android:radius="10dp"/>
|
<corners android:radius="10dp"/>
|
||||||
<padding android:top="16dp" android:left="16dp" android:right="16dp" android:bottom="16dp"/>
|
<padding android:top="16dp" android:left="16dp" android:right="16dp" android:bottom="16dp"/>
|
||||||
</shape>
|
</shape>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<solid android:color="@color/gray_25"/>
|
<solid android:color="?colorGray25"/>
|
||||||
<corners android:radius="10dp"/>
|
<corners android:radius="10dp"/>
|
||||||
<padding android:top="16dp" android:left="16dp" android:right="16dp" android:bottom="16dp"/>
|
<padding android:top="16dp" android:left="16dp" android:right="16dp" android:bottom="16dp"/>
|
||||||
</shape>
|
</shape>
|
||||||
29
mastodon/src/main/res/drawable/bg_filter_chip.xml
Normal file
29
mastodon/src/main/res/drawable/bg_filter_chip.xml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_selected="true">
|
||||||
|
<ripple android:color="@color/m3_on_secondary_container_overlay">
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<corners android:radius="8dp"/>
|
||||||
|
<solid android:color="?colorM3SecondaryContainer"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<ripple android:color="@color/m3_on_surface_variant_overlay">
|
||||||
|
<item android:id="@android:id/mask">
|
||||||
|
<shape>
|
||||||
|
<corners android:radius="8dp"/>
|
||||||
|
<solid android:color="#000"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<corners android:radius="8dp"/>
|
||||||
|
<stroke android:color="?colorM3Outline" android:width="1dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
|
</item>
|
||||||
|
</selector>
|
||||||
19
mastodon/src/main/res/drawable/bg_m3_outlined_text_field.xml
Normal file
19
mastodon/src/main/res/drawable/bg_m3_outlined_text_field.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:left="12dp" android:top="12dp" android:right="12dp" android:bottom="12dp">
|
||||||
|
<selector>
|
||||||
|
<item android:state_focused="true">
|
||||||
|
<shape>
|
||||||
|
<stroke android:color="?colorM3Primary" android:width="2dp"/>
|
||||||
|
<corners android:radius="4dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<stroke android:color="?colorM3Outline" android:width="1dp"/>
|
||||||
|
<corners android:radius="4dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</selector>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:left="12dp" android:top="12dp" android:right="12dp" android:bottom="12dp">
|
||||||
|
<shape>
|
||||||
|
<stroke android:color="?colorM3Error" android:width="2dp"/>
|
||||||
|
<corners android:radius="4dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
||||||
9
mastodon/src/main/res/drawable/bg_onboarding_panel.xml
Normal file
9
mastodon/src/main/res/drawable/bg_onboarding_panel.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item>
|
||||||
|
<color android:color="?colorM3Background"/>
|
||||||
|
</item>
|
||||||
|
<item android:id="@+id/color_overlay">
|
||||||
|
<color android:color="?colorM3Primary"/>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/highlight_over_dark">
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/highlight_over_dark">
|
||||||
<item>
|
<item>
|
||||||
<shape android:shape="oval">
|
<shape android:shape="oval">
|
||||||
<solid android:color="?colorSearchHint"/>
|
<solid android:color="?colorGray600"/>
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
</ripple>
|
</ripple>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?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>
|
||||||
<shape android:tint="@color/gray_800">
|
<shape android:tint="?colorGray800">
|
||||||
<solid android:color="#CC000000"/>
|
<solid android:color="#CC000000"/>
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
5
mastodon/src/main/res/drawable/empty_8dp.xml
Normal file
5
mastodon/src/main/res/drawable/empty_8dp.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<size android:width="8dp"/>
|
||||||
|
<solid android:color="#00000000"/>
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="18dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="18dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M7,10l5,5 5,-5z"/>
|
||||||
|
</vector>
|
||||||
5
mastodon/src/main/res/drawable/ic_baseline_check_18.xml
Normal file
5
mastodon/src/main/res/drawable/ic_baseline_check_18.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="18dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="18dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
||||||
|
</vector>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:state_selected="true" android:drawable="@drawable/ic_fluent_arrow_repeat_all_24_filled"/>
|
<item android:state_selected="true" android:drawable="@drawable/ic_fluent_arrow_repeat_all_24_very_filled"/>
|
||||||
<item android:state_enabled="true" android:drawable="@drawable/ic_fluent_arrow_repeat_all_24_regular"/>
|
<item android:state_enabled="true" android:drawable="@drawable/ic_fluent_arrow_repeat_all_24_regular"/>
|
||||||
<item android:drawable="@drawable/ic_fluent_arrow_repeat_all_off_24_regular"/>
|
<item android:drawable="@drawable/ic_fluent_arrow_repeat_all_off_24_regular"/>
|
||||||
</selector>
|
</selector>
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M12,22C6.49,22 2,17.51 2,12S6.49,2 12,2s10,4.04 10,9c0,3.31 -2.69,6 -6,6h-1.77c-0.28,0 -0.5,0.22 -0.5,0.5 0,0.12 0.05,0.23 0.13,0.33 0.41,0.47 0.64,1.06 0.64,1.67 0,1.38 -1.12,2.5 -2.5,2.5zM12,4c-4.41,0 -8,3.59 -8,8s3.59,8 8,8c0.28,0 0.5,-0.22 0.5,-0.5 0,-0.16 -0.08,-0.28 -0.14,-0.35 -0.41,-0.46 -0.63,-1.05 -0.63,-1.65 0,-1.38 1.12,-2.5 2.5,-2.5L16,15c2.21,0 4,-1.79 4,-4 0,-3.86 -3.59,-7 -8,-7z"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M6.5,11.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M9.5,7.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M14.5,7.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M17.5,11.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
|
|
||||||
</vector>
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<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,9.123C7.423,9.123 6.125,10.421 6.125,11.994C6.125,12.523 6.269,13.014 6.514,13.434C6.514,13.435 6.513,13.436 6.514,13.438L6.619,13.607L6.623,13.613C6.666,13.679 6.706,13.747 6.744,13.816L7.787,12.775C9.015,11.549 10.977,11.548 12.205,12.775C12.789,13.359 13.077,14.104 13.105,14.863L15.002,14.863C16.579,14.863 17.875,13.567 17.875,11.992C17.875,11.463 17.731,10.972 17.486,10.553L17.486,10.549L17.381,10.379C17.337,10.311 17.295,10.241 17.256,10.17L16.215,11.211C14.986,12.439 13.025,12.44 11.797,11.213C11.218,10.634 10.931,9.885 10.902,9.123L9,9.123z"
|
||||||
|
android:strokeWidth="3"
|
||||||
|
android:fillColor="#212121"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M20.787,8.06C20.603,7.828 20.319,7.678 20,7.678c-0.552,0 -1,0.447 -1,0.999 0,0.208 0.064,0.401 0.172,0.561C19.695,10.028 20,10.975 20,11.993c0,2.759 -2.238,4.996 -4.999,4.996l-5.586,-0.001 1.294,-1.291 0.084,-0.095c0.281,-0.362 0.279,-0.872 -0.006,-1.231L10.709,14.284 10.614,14.2C10.252,13.92 9.741,13.922 9.382,14.206L9.294,14.284 6.289,17.287 6.206,17.382c-0.281,0.362 -0.279,0.872 0.006,1.231l0.078,0.087 3.005,3.003 0.094,0.083c0.392,0.305 0.96,0.277 1.32,-0.083 0.363,-0.362 0.389,-0.934 0.078,-1.326l-0.078,-0.087 -1.304,-1.303 5.596,0.001 0.241,-0.004C18.996,18.857 22,15.776 22,11.993 22,10.534 21.552,9.178 20.787,8.057Z"
|
||||||
|
android:fillColor="#212121"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M14.712,2.289 L14.625,2.211C14.233,1.901 13.661,1.926 13.298,2.289L13.22,2.376C12.91,2.768 12.936,3.34 13.298,3.702l1.299,1.297 -5.598,0 -0.241,0.004C5.004,5.13 2,8.211 2,11.993c0,1.445 0.438,2.787 1.189,3.898 0.182,0.251 0.477,0.415 0.811,0.415 0.552,0 1,-0.447 1,-0.999C5,15.091 4.931,14.891 4.815,14.729L4.68,14.511C4.248,13.772 4,12.911 4,11.993 4,9.234 6.238,6.998 8.999,6.998L14.595,6.998 13.298,8.295 13.22,8.382c-0.311,0.392 -0.285,0.964 0.078,1.326 0.391,0.39 1.024,0.39 1.414,0L17.718,6.705 17.795,6.618C18.106,6.226 18.08,5.654 17.718,5.292Z"
|
||||||
|
android:strokeWidth="3"
|
||||||
|
android:fillColor="#212121"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M20.11,5.619L20.11,5.619A1.393,0.037 0,0 1,21.503 5.656L21.503,5.656A1.393,0.037 0,0 1,20.11 5.693L20.11,5.693A1.393,0.037 0,0 1,18.717 5.656L18.717,5.656A1.393,0.037 0,0 1,20.11 5.619z"
|
||||||
|
android:strokeWidth="52.513"
|
||||||
|
android:fillColor="#613583"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:fillAlpha="0.0156863"/>
|
||||||
|
</vector>
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
<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="M20.998 6.25c0-1.795-1.454-3.25-3.25-3.25H6.25C4.455 3 3 4.455 3 6.25v11.499c0 1.795 1.455 3.25 3.25 3.25h4.914l0.356-1.424 0.02-0.076H6.25c-0.204 0-0.4-0.035-0.582-0.1l5.807-5.685 0.083-0.07c0.291-0.213 0.7-0.19 0.966 0.07l2.079 2.036 1.06-1.06-2.09-2.048-0.128-0.116c-0.878-0.738-2.187-0.7-3.02 0.116l-5.822 5.7C4.536 18.157 4.5 17.957 4.5 17.75V6.25c0-0.966 0.783-1.75 1.75-1.75h11.499c0.966 0 1.75 0.784 1.75 1.75v4.983c0.478-0.19 0.993-0.264 1.5-0.22V6.25zm-3.495 2.502c0-1.244-1.008-2.252-2.252-2.252-1.244 0-2.252 1.008-2.252 2.252 0 1.243 1.008 2.252 2.252 2.252 1.244 0 2.252-1.009 2.252-2.252zm-3.004 0C14.499 8.336 14.836 8 15.251 8s0.752 0.336 0.752 0.752c0 0.415-0.337 0.752-0.752 0.752s-0.752-0.337-0.752-0.752zm4.6 3.917l-5.902 5.901c-0.345 0.345-0.589 0.776-0.707 1.248l-0.457 1.83c-0.2 0.797 0.522 1.518 1.318 1.319l1.83-0.458c0.472-0.118 0.904-0.362 1.248-0.706L22.33 15.9c0.892-0.893 0.892-2.34 0-3.232-0.893-0.893-2.34-0.893-3.233 0z" android:fillColor="@color/gray_25"/>
|
<path android:pathData="M20.998 6.25c0-1.795-1.454-3.25-3.25-3.25H6.25C4.455 3 3 4.455 3 6.25v11.499c0 1.795 1.455 3.25 3.25 3.25h4.914l0.356-1.424 0.02-0.076H6.25c-0.204 0-0.4-0.035-0.582-0.1l5.807-5.685 0.083-0.07c0.291-0.213 0.7-0.19 0.966 0.07l2.079 2.036 1.06-1.06-2.09-2.048-0.128-0.116c-0.878-0.738-2.187-0.7-3.02 0.116l-5.822 5.7C4.536 18.157 4.5 17.957 4.5 17.75V6.25c0-0.966 0.783-1.75 1.75-1.75h11.499c0.966 0 1.75 0.784 1.75 1.75v4.983c0.478-0.19 0.993-0.264 1.5-0.22V6.25zm-3.495 2.502c0-1.244-1.008-2.252-2.252-2.252-1.244 0-2.252 1.008-2.252 2.252 0 1.243 1.008 2.252 2.252 2.252 1.244 0 2.252-1.009 2.252-2.252zm-3.004 0C14.499 8.336 14.836 8 15.251 8s0.752 0.336 0.752 0.752c0 0.415-0.337 0.752-0.752 0.752s-0.752-0.337-0.752-0.752zm4.6 3.917l-5.902 5.901c-0.345 0.345-0.589 0.776-0.707 1.248l-0.457 1.83c-0.2 0.797 0.522 1.518 1.318 1.319l1.83-0.458c0.472-0.118 0.904-0.362 1.248-0.706L22.33 15.9c0.892-0.893 0.892-2.34 0-3.232-0.893-0.893-2.34-0.893-3.233 0z" android:fillColor="?colorGray25"/>
|
||||||
</vector>
|
</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.43 0 9.848 4.327 9.996 9.72L22 12v1.525c-0.014 2.198-1.8 3.975-4 3.975-1.446 0-2.712-0.767-3.415-1.916-0.8 0.834-1.882 1.35-3.085 1.35-2.515 0-4.5-2.25-4.5-4.967C7 9.25 8.985 7 11.5 7c0.937 0 1.8 0.312 2.514 0.844C14.087 7.366 14.501 7 15 7c0.513 0 0.935 0.386 0.993 0.883L16 8v5.5c0 1.105 0.895 2 2 2 1.054 0 1.918-0.816 1.994-1.85L20 13.474V12c0-4.418-3.582-8-8-8s-8 3.582-8 8 3.582 8 8 8c0.921 0 1.82-0.155 2.67-0.456 0.52-0.184 1.092 0.089 1.276 0.61 0.184 0.52-0.089 1.091-0.61 1.276C14.275 21.806 13.15 22 12 22 6.477 22 2 17.523 2 12S6.477 2 12 2zm-0.35 7.005L11.5 9C10.15 9 9 10.302 9 11.967c0 1.664 1.15 2.966 2.5 2.966 1.3 0 2.415-1.207 2.495-2.783L14 11.93c-0.017-1.586-1.075-2.834-2.35-2.925L11.5 9l0.15 0.005z" 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.987 2.89c0.077-0.408-0.19-0.8-0.598-0.877-0.407-0.077-0.799 0.19-0.876 0.598L8.494 7.998 3.75 8C3.335 8 3 8.336 3 8.75 3 9.166 3.336 9.5 3.75 9.5l4.46-0.002-0.946 5L2.75 14.5C2.335 14.5 2 14.836 2 15.25 2 15.665 2.336 16 2.75 16l4.23-0.002-0.967 5.116c-0.077 0.407 0.19 0.8 0.598 0.876 0.407 0.077 0.799-0.19 0.876-0.598l1.02-5.395 5.474-0.002-0.968 5.119c-0.077 0.407 0.19 0.8 0.598 0.876 0.407 0.077 0.799-0.19 0.876-0.598l1.021-5.398 4.742-0.002c0.415 0 0.75-0.336 0.75-0.75 0-0.415-0.336-0.75-0.75-0.75l-4.458 0.002 0.946-5 4.512-0.002c0.415 0 0.75-0.336 0.75-0.75s-0.336-0.75-0.75-0.75l-4.23 0.002 0.966-5.104c0.077-0.408-0.19-0.8-0.598-0.877-0.407-0.077-0.799 0.19-0.876 0.598l-1.018 5.384-5.474 0.002 0.966-5.107zm-1.25 6.608l5.474-0.003-0.946 5-5.474 0.002 0.946-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="M11 17.5c0-1.289 0.376-2.49 1.023-3.5h-7.77c-1.242 0-2.25 1.007-2.25 2.25v0.919c0 0.572 0.18 1.13 0.511 1.596C4.056 20.929 6.58 22 10 22c0.932 0 1.797-0.08 2.592-0.24C11.601 20.62 11 19.13 11 17.5zm4-10.495c0-2.761-2.238-5-5-5-2.761 0-5 2.239-5 5s2.239 5 5 5c2.762 0 5-2.239 5-5zM23 17.5c0-3.037-2.462-5.5-5.5-5.5-3.037 0-5.5 2.463-5.5 5.5 0 3.038 2.463 5.5 5.5 5.5 3.038 0 5.5-2.462 5.5-5.5zm-5.59-3.492L17.5 14l0.09 0.008c0.204 0.037 0.365 0.198 0.402 0.402L18 14.5V17h2.504l0.09 0.008c0.204 0.037 0.365 0.198 0.402 0.402l0.008 0.09-0.008 0.09c-0.037 0.204-0.198 0.365-0.402 0.402L20.504 18H18v2.5l-0.008 0.09c-0.037 0.204-0.198 0.365-0.402 0.402L17.5 21l-0.09-0.008c-0.204-0.037-0.364-0.198-0.402-0.402L17 20.5V18h-2.496l-0.09-0.008c-0.204-0.037-0.365-0.198-0.402-0.402l-0.008-0.09 0.008-0.09c0.037-0.204 0.198-0.365 0.402-0.402L14.504 17H17v-2.5l0.008-0.09c0.038-0.204 0.198-0.365 0.402-0.402z" 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="M5.997 12c0 0.38 0.282 0.694 0.647 0.743l0.102 0.007H19.44l-2.216 2.22c-0.266 0.266-0.29 0.683-0.073 0.976l0.073 0.084c0.266 0.267 0.682 0.29 0.975 0.073l0.084-0.073 3.497-3.5c0.267-0.266 0.29-0.683 0.073-0.976L21.78 11.47l-3.497-3.5c-0.292-0.293-0.767-0.293-1.06 0-0.265 0.266-0.29 0.683-0.072 0.976l0.073 0.084 2.216 2.22H6.746c-0.414 0-0.75 0.336-0.75 0.75zM2 12c0 2.761 2.237 5 4.996 5 2.143 0 3.972-1.351 4.68-3.25h-1.651C9.42 14.797 8.29 15.5 6.995 15.5 5.066 15.5 3.5 13.933 3.5 12s1.565-3.5 3.497-3.5c1.294 0 2.425 0.704 3.03 1.75h1.651C10.968 8.352 9.14 7 6.996 7 4.236 7 2 9.239 2 12z" 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.953 5.303c0.167-0.526-0.124-1.088-0.65-1.256-0.526-0.167-1.089 0.124-1.256 0.65-0.124 0.389-0.236 0.899-0.324 1.344-0.565 0.012-1.12 0-1.652-0.038-0.55-0.04-1.03 0.375-1.069 0.926-0.039 0.55 0.376 1.03 0.927 1.069 0.46 0.032 0.934 0.048 1.416 0.047-0.106 0.612-0.184 1.176-0.24 1.697-1.263 0.717-2.142 1.685-2.636 2.701-0.624 1.283-0.7 2.857 0.239 3.883 0.675 0.736 1.704 0.758 2.499 0.588 0.322-0.068 0.654-0.176 0.988-0.32 0.255 0.346 0.716 0.5 1.142 0.348 0.52-0.186 0.79-0.759 0.604-1.278L16.9 15.549c1.148-0.931 2.202-2.307 2.735-4.06 0.286 0.251 0.507 0.55 0.658 0.864 0.284 0.594 0.334 1.271 0.099 1.91-0.234 0.633-0.78 1.313-1.84 1.843-0.493 0.247-0.694 0.847-0.447 1.341 0.247 0.494 0.848 0.695 1.342 0.448 1.44-0.72 2.385-1.758 2.821-2.94 0.434-1.176 0.332-2.414-0.17-3.464-0.44-0.918-1.171-1.68-2.104-2.165C19.998 9.22 20 9.11 20 9c0-0.552-0.448-1-1-1-0.473 0-0.87 0.329-0.974 0.77-0.567-0.045-1.17-0.003-1.796 0.138 0.047-0.304 0.102-0.626 0.166-0.964 0.977-0.094 1.944-0.253 2.842-0.473 0.536-0.131 0.864-0.672 0.733-1.209-0.131-0.536-0.673-0.864-1.21-0.733-0.621 0.152-1.285 0.272-1.963 0.358 0.048-0.208 0.1-0.409 0.155-0.583zm-3.686 8.015c0.166-0.34 0.414-0.697 0.758-1.037 0.02 0.348 0.053 0.67 0.098 0.973 0.083 0.56 0.207 1.048 0.341 1.477-0.243 0.109-0.47 0.184-0.674 0.227-0.429 0.092-0.588 0.019-0.614 0.006l-0.004-0.001c-0.162-0.193-0.329-0.774 0.095-1.645zm4.498-2.562c-0.308 1.112-0.89 2.03-1.568 2.73-0.035-0.164-0.067-0.338-0.095-0.525-0.076-0.513-0.117-1.132-0.088-1.904 0.033-0.013 0.067-0.024 0.1-0.036l1.651-0.265zm-1.651 0.265l1.651-0.265c-0.495-0.025-1.049 0.053-1.651 0.265zM7.536 6.29c-2.02-0.589-3.67-0.038-4.456 0.33-0.5 0.234-0.716 0.83-0.482 1.33 0.234 0.5 0.83 0.715 1.33 0.481 0.53-0.248 1.66-0.626 3.048-0.222 0.365 0.107 0.569 0.248 0.69 0.37 0.122 0.123 0.204 0.27 0.258 0.454 0.067 0.225 0.087 0.446 0.09 0.69C7.84 9.68 7.655 9.64 7.459 9.606c-1.146-0.199-2.733-0.215-4.262 0.64-1.271 0.713-1.796 2.168-1.682 3.448 0.12 1.326 0.94 2.679 2.572 3.136 1.48 0.414 2.913-0.045 3.877-0.507l0.08-0.04c0.122 0.416 0.506 0.719 0.96 0.719 0.553 0 1-0.448 1-1V10.5c0-0.053 0.002-0.12 0.005-0.2 0.012-0.417 0.034-1.16-0.168-1.838C9.71 8.023 9.48 7.57 9.086 7.172S8.176 6.478 7.538 6.29H7.537zm-0.419 5.287c0.344 0.06 0.647 0.143 0.887 0.222v2.197c-0.224 0.156-0.538 0.35-0.904 0.525-0.792 0.38-1.682 0.605-2.473 0.384-0.698-0.195-1.06-0.742-1.119-1.389-0.062-0.693 0.243-1.286 0.667-1.523 0.987-0.553 2.06-0.569 2.943-0.415z" 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.961 5.987c0.13-0.394-0.082-0.818-0.475-0.949-0.393-0.13-0.818 0.083-0.948 0.476-0.148 0.445-0.304 1.04-0.42 1.512-0.736 0.026-1.446 0.018-2.067-0.024-0.413-0.028-0.771 0.284-0.8 0.697-0.027 0.413 0.285 0.771 0.698 0.8 0.563 0.038 1.19 0.05 1.844 0.037-0.144 0.765-0.241 1.449-0.3 2.064-1.215 0.642-2.061 1.504-2.539 2.412-0.613 1.166-0.66 2.556 0.206 3.443 0.571 0.584 1.408 0.64 2.129 0.485 0.405-0.087 0.825-0.248 1.24-0.474l0.018 0.046c0.145 0.389 0.577 0.586 0.965 0.44 0.388-0.144 0.585-0.576 0.44-0.964-0.056-0.152-0.114-0.291-0.17-0.424 1.023-0.936 1.915-2.308 2.285-4.047 0.638 0.312 1.07 0.836 1.278 1.4 0.227 0.613 0.213 1.312-0.097 1.952-0.308 0.636-0.933 1.265-2.015 1.681-0.387 0.149-0.58 0.583-0.431 0.97 0.149 0.386 0.582 0.579 0.969 0.43 1.387-0.533 2.33-1.402 2.827-2.427 0.494-1.02 0.518-2.143 0.154-3.127-0.414-1.116-1.3-2.01-2.505-2.424 0.002-0.073 0.003-0.147 0.003-0.222C19.25 9.336 18.914 9 18.5 9c-0.405 0-0.735 0.322-0.75 0.724-0.524-0.005-1.084 0.07-1.672 0.237 0.064-0.455 0.148-0.953 0.257-1.5 1.185-0.089 2.364-0.252 3.337-0.48 0.403-0.095 0.653-0.5 0.558-0.902-0.095-0.404-0.498-0.654-0.902-0.559-0.767 0.18-1.69 0.318-2.637 0.406 0.085-0.33 0.18-0.67 0.27-0.94zm-3.68 7.723c0.227-0.43 0.6-0.886 1.147-1.3 0.017 0.492 0.065 0.926 0.137 1.317 0.094 0.52 0.236 0.974 0.386 1.373-0.306 0.171-0.632 0.3-0.977 0.374-0.485 0.104-0.687-0.012-0.742-0.068-0.24-0.245-0.394-0.853 0.05-1.696zm4.315-2.487c-0.21 0.966-0.7 2.022-1.414 2.848-0.052-0.184-0.1-0.385-0.142-0.612-0.089-0.49-0.14-1.092-0.111-1.866 0.11-0.042 0.222-0.082 0.339-0.12 0.481-0.16 0.924-0.238 1.328-0.25zM7.96 6.53C5.72 5.878 3.958 6.681 3.385 6.997c-0.363 0.2-0.494 0.657-0.294 1.02C3.29 8.378 3.747 8.51 4.11 8.31c0.405-0.223 1.736-0.833 3.43-0.34 0.773 0.227 1.098 0.64 1.264 1.056 0.133 0.332 0.175 0.691 0.188 1.042-0.27-0.075-0.581-0.15-0.923-0.21-1.132-0.202-2.682-0.263-4.122 0.427-1.586 0.76-2.13 2.316-1.902 3.697 0.224 1.356 1.216 2.666 2.8 2.951 1.43 0.258 2.806-0.256 3.757-0.761 0.14-0.074 0.271-0.148 0.396-0.222v0.3c0 0.414 0.335 0.75 0.75 0.75 0.414 0 0.75-0.336 0.75-0.75v-5.79c0-0.454 0-1.236-0.3-1.99-0.331-0.829-1.005-1.578-2.236-1.94H7.96zm-0.153 4.804c0.483 0.086 0.898 0.206 1.19 0.305v2.506l-0.105 0.078C8.653 14.4 8.31 14.63 7.9 14.847c-0.84 0.447-1.85 0.779-2.788 0.61-0.845-0.152-1.445-0.866-1.586-1.72-0.138-0.83 0.179-1.673 1.07-2.1 1.037-0.496 2.226-0.478 3.212-0.303z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
||||||
18
mastodon/src/main/res/drawable/ic_launcher_monochrome.xml
Normal file
18
mastodon/src/main/res/drawable/ic_launcher_monochrome.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<group android:scaleX="2.5091188"
|
||||||
|
android:scaleY="2.5091188">
|
||||||
|
<path
|
||||||
|
android:pathData="m13.779,16.29c-1.097,0 -1.983,0.387 -2.658,1.141 -0.655,0.754 -0.981,1.771 -0.981,3.053l0,6.27L12.622,26.754L12.622,20.668c0,-1.284 0.539,-1.935 1.618,-1.935 1.192,0 1.791,0.773 1.791,2.3l0,3.331l2.468,0l0,-3.331c0,-1.527 0.598,-2.3 1.791,-2.3 1.078,0 1.618,0.651 1.618,1.935l0,6.085l2.482,0l0,-6.27c0,-1.281 -0.326,-2.299 -0.981,-3.053 -0.676,-0.754 -1.56,-1.141 -2.658,-1.141 -1.27,0 -2.232,0.488 -2.868,1.466L17.265,18.794 16.646,17.756C16.01,16.778 15.049,16.29 13.779,16.29Z"
|
||||||
|
android:strokeWidth="0.796"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m29.087,26.754q-1.113,0 -1.986,-0.493 -0.873,-0.507 -1.366,-1.366 -0.479,-0.873 -0.479,-1.958 0,-1.07 0.479,-1.944 0.493,-0.873 1.366,-1.366 0.873,-0.507 1.986,-0.507 1.099,0 1.972,0.507 0.873,0.493 1.352,1.366 0.493,0.873 0.493,1.944 0,1.085 -0.493,1.958 -0.479,0.859 -1.352,1.366 -0.873,0.493 -1.972,0.493zM29.087,25.049q0.535,0 0.986,-0.254 0.451,-0.254 0.718,-0.732 0.268,-0.479 0.268,-1.127 0,-0.634 -0.268,-1.113 -0.268,-0.479 -0.718,-0.732 -0.451,-0.254 -0.986,-0.254 -0.535,0 -0.986,0.254 -0.451,0.254 -0.732,0.732 -0.268,0.479 -0.268,1.113 0,0.634 0.268,1.127 0.282,0.479 0.732,0.732 0.451,0.254 0.986,0.254z"
|
||||||
|
android:strokeWidth="0.687"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
5
mastodon/src/main/res/drawable/ic_outline_email_24.xml
Normal file
5
mastodon/src/main/res/drawable/ic_outline_email_24.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M22,6c0,-1.1 -0.9,-2 -2,-2L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6zM20,6l-8,5 -8,-5h16zM20,18L4,18L4,8l8,5 8,-5v10z"/>
|
||||||
|
</vector>
|
||||||
5
mastodon/src/main/res/drawable/ic_outline_link_24.xml
Normal file
5
mastodon/src/main/res/drawable/ic_outline_link_24.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M17,7h-4v2h4c1.65,0 3,1.35 3,3s-1.35,3 -3,3h-4v2h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5zM11,15L7,15c-1.65,0 -3,-1.35 -3,-3s1.35,-3 3,-3h4L11,7L7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-2zM8,11h8v2L8,13z"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M2,17h20v2H2V17zM3.15,12.95L4,11.47l0.85,1.48l1.3,-0.75L5.3,10.72H7v-1.5H5.3l0.85,-1.47L4.85,7L4,8.47L3.15,7l-1.3,0.75L2.7,9.22H1v1.5h1.7L1.85,12.2L3.15,12.95zM9.85,12.2l1.3,0.75L12,11.47l0.85,1.48l1.3,-0.75l-0.85,-1.48H15v-1.5h-1.7l0.85,-1.47L12.85,7L12,8.47L11.15,7l-1.3,0.75l0.85,1.47H9v1.5h1.7L9.85,12.2zM23,9.22h-1.7l0.85,-1.47L20.85,7L20,8.47L19.15,7l-1.3,0.75l0.85,1.47H17v1.5h1.7l-0.85,1.48l1.3,0.75L20,11.47l0.85,1.48l1.3,-0.75l-0.85,-1.48H23V9.22z"/>
|
||||||
|
</vector>
|
||||||
5
mastodon/src/main/res/drawable/ic_outline_person_24.xml
Normal file
5
mastodon/src/main/res/drawable/ic_outline_person_24.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,6c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2m0,10c2.7,0 5.8,1.29 6,2L6,18c0.23,-0.72 3.31,-2 6,-2m0,-12C9.79,4 8,5.79 8,8s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
|
||||||
|
</vector>
|
||||||
@@ -5,5 +5,5 @@
|
|||||||
android:viewportHeight="4">
|
android:viewportHeight="4">
|
||||||
<path
|
<path
|
||||||
android:pathData="M4,2C4,3.1046 3.1046,4 2,4C0.8954,4 0,3.1046 0,2C0,0.8954 0.8954,0 2,0C3.1046,0 4,0.8954 4,2ZM10,2C10,3.1046 9.1046,4 8,4C6.8954,4 6,3.1046 6,2C6,0.8954 6.8954,0 8,0C9.1046,0 10,0.8954 10,2ZM14,4C15.1046,4 16,3.1046 16,2C16,0.8954 15.1046,0 14,0C12.8954,0 12,0.8954 12,2C12,3.1046 12.8954,4 14,4Z"
|
android:pathData="M4,2C4,3.1046 3.1046,4 2,4C0.8954,4 0,3.1046 0,2C0,0.8954 0.8954,0 2,0C3.1046,0 4,0.8954 4,2ZM10,2C10,3.1046 9.1046,4 8,4C6.8954,4 6,3.1046 6,2C6,0.8954 6.8954,0 8,0C9.1046,0 10,0.8954 10,2ZM14,4C15.1046,4 16,3.1046 16,2C16,0.8954 15.1046,0 14,0C12.8954,0 12,0.8954 12,2C12,3.1046 12.8954,4 14,4Z"
|
||||||
android:fillColor="@color/gray_500"/>
|
android:fillColor="?colorGray500"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
6
mastodon/src/main/res/drawable/ic_translate.xml
Normal file
6
mastodon/src/main/res/drawable/ic_translate.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_selected="true" android:drawable="@drawable/ic_fluent_translate_24_filled"/>
|
||||||
|
<item android:state_enabled="true" android:drawable="@drawable/ic_fluent_translate_24_regular"/>
|
||||||
|
<item android:drawable="@drawable/ic_fluent_translate_24_regular"/>
|
||||||
|
</selector>
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<vector android:height="24dp" android:tint="#000000"
|
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="?colorSecondary" android:pathData="M12.87,15.07l-2.54,-2.51 0.03,-0.03c1.74,-1.94 2.98,-4.17 3.71,-6.53L17,6L17,4h-7L10,2L8,2v2L1,4v1.99h11.17C11.5,7.92 10.44,9.75 9,11.35 8.07,10.32 7.3,9.19 6.69,8h-2c0.73,1.63 1.73,3.17 2.98,4.56l-5.09,5.02L4,19l5,-5 3.11,3.11 0.76,-2.04zM18.5,10h-2L12,22h2l1.12,-3h4.75L21,22h2l-4.5,-12zM15.88,17l1.62,-4.33L19.12,17h-3.24z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<vector android:height="24dp" android:tint="#000000"
|
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="?colorSecondary" android:pathData="M12.87,15.07l-2.54,-2.51 0.03,-0.03c1.74,-1.94 2.98,-4.17 3.71,-6.53L17,6L17,4h-7L10,2L8,2v2L1,4v1.99h11.17C11.5,7.92 10.44,9.75 9,11.35 8.07,10.32 7.3,9.19 6.69,8h-2c0.73,1.63 1.73,3.17 2.98,4.56l-5.09,5.02L4,19l5,-5 3.11,3.11 0.76,-2.04zM18.5,10h-2L12,22h2l1.12,-3h4.75L21,22h2l-4.5,-12zM15.88,17l1.62,-4.33L19.12,17h-3.24z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<corners android:radius="27dp"/>
|
<corners android:radius="27dp"/>
|
||||||
<stroke android:width="2dp" android:color="@color/gray_25"/>
|
<stroke android:width="2dp" android:color="?colorGray25"/>
|
||||||
</shape>
|
</shape>
|
||||||
5
mastodon/src/main/res/drawable/round_rect.xml
Normal file
5
mastodon/src/main/res/drawable/round_rect.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<corners android:radius="100dp"/>
|
||||||
|
<solid android:color="#000"/>
|
||||||
|
</shape>
|
||||||
@@ -2,14 +2,14 @@
|
|||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:gravity="center_vertical">
|
<item android:gravity="center_vertical">
|
||||||
<shape>
|
<shape>
|
||||||
<solid android:color="@color/gray_500"/>
|
<solid android:color="?colorGray500"/>
|
||||||
<corners android:radius="1dp"/>
|
<corners android:radius="1dp"/>
|
||||||
<size android:height="2dp"/>
|
<size android:height="2dp"/>
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
<item android:gravity="center_vertical" android:id="@android:id/secondaryProgress">
|
<item android:gravity="center_vertical" android:id="@android:id/secondaryProgress">
|
||||||
<clip>
|
<clip>
|
||||||
<shape android:tint="@color/gray_50">
|
<shape android:tint="?colorGray50">
|
||||||
<solid android:color="#40000000"/>
|
<solid android:color="#40000000"/>
|
||||||
<corners android:radius="1dp"/>
|
<corners android:radius="1dp"/>
|
||||||
<size android:height="2dp"/>
|
<size android:height="2dp"/>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<item android:gravity="center_vertical" android:id="@android:id/progress">
|
<item android:gravity="center_vertical" android:id="@android:id/progress">
|
||||||
<clip>
|
<clip>
|
||||||
<shape>
|
<shape>
|
||||||
<solid android:color="@color/gray_50"/>
|
<solid android:color="?colorGray50"/>
|
||||||
<corners android:radius="1dp"/>
|
<corners android:radius="1dp"/>
|
||||||
<size android:height="2dp"/>
|
<size android:height="2dp"/>
|
||||||
</shape>
|
</shape>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||||
<solid android:color="@color/gray_25"/>
|
<solid android:color="?colorGray25"/>
|
||||||
<size android:width="18dp" android:height="18dp"/>
|
<size android:width="18dp" android:height="18dp"/>
|
||||||
</shape>
|
</shape>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
android:width="257dp"
|
android:width="134dp"
|
||||||
android:height="67dp"
|
android:height="34dp"
|
||||||
android:viewportWidth="313"
|
android:viewportWidth="313"
|
||||||
android:viewportHeight="81">
|
android:viewportHeight="81">
|
||||||
<path
|
<path
|
||||||
@@ -20,8 +20,8 @@
|
|||||||
</path>
|
</path>
|
||||||
<path
|
<path
|
||||||
android:pathData="M14.81,23.2C14.81,20.72 16.77,18.72 19.2,18.72C21.62,18.72 23.58,20.73 23.58,23.2C23.58,25.67 21.62,27.68 19.2,27.68C16.77,27.68 14.81,25.67 14.81,23.2Z"
|
android:pathData="M14.81,23.2C14.81,20.72 16.77,18.72 19.2,18.72C21.62,18.72 23.58,20.73 23.58,23.2C23.58,25.67 21.62,27.68 19.2,27.68C16.77,27.68 14.81,25.67 14.81,23.2Z"
|
||||||
android:fillColor="#ffffff"/>
|
android:fillColor="#000"/>
|
||||||
<path
|
<path
|
||||||
android:pathData="M80.02,27.06V47.66H72.03V27.67C72.03,23.45 70.3,21.32 66.83,21.32C63,21.32 61.07,23.87 61.07,28.87V39.82H53.14V28.87C53.14,23.84 51.24,21.32 47.38,21.32C43.92,21.32 42.18,23.45 42.18,27.67V47.65H34.21V27.06C34.21,22.86 35.25,19.51 37.35,17.03C39.53,14.54 42.37,13.29 45.89,13.29C49.97,13.29 53.07,14.9 55.11,18.11L57.11,21.52L59.1,18.11C61.14,14.91 64.23,13.29 68.32,13.29C71.84,13.29 74.69,14.55 76.86,17.03C78.96,19.51 80.01,22.83 80.01,27.06H80.02ZM107.49,37.3C109.15,35.51 109.93,33.29 109.93,30.59C109.93,27.89 109.14,25.65 107.49,23.94C105.91,22.15 103.89,21.3 101.45,21.3C99.02,21.3 97.01,22.15 95.41,23.94C93.83,25.65 93.04,27.89 93.04,30.59C93.04,33.29 93.83,35.53 95.41,37.3C97,39 99.02,39.87 101.45,39.87C103.89,39.87 105.9,39.02 107.49,37.3ZM109.93,14.12H117.8V47.06H109.93V43.18C107.55,46.41 104.26,48 99.99,48C95.71,48 92.42,46.36 89.5,43C86.64,39.64 85.18,35.48 85.18,30.61C85.18,25.74 86.65,21.65 89.5,18.29C92.43,14.93 95.92,13.23 99.99,13.23C104.06,13.23 107.55,14.81 109.93,18.02V14.14V14.12ZM144.26,29.97C146.58,31.76 147.73,34.25 147.67,37.41C147.67,40.77 146.52,43.41 144.14,45.24C141.76,47.03 138.89,47.94 135.41,47.94C129.13,47.94 124.87,45.3 122.61,40.11L129.43,35.96C130.34,38.78 132.35,40.25 135.41,40.25C138.22,40.25 139.62,39.33 139.62,37.42C139.62,36.03 137.79,34.78 134.07,33.8C132.66,33.41 131.5,33.01 130.6,32.68C129.31,32.16 128.22,31.56 127.31,30.83C125.05,29.04 123.9,26.68 123.9,23.65C123.9,20.42 124.99,17.85 127.19,16C129.45,14.09 132.19,13.18 135.48,13.18C140.73,13.18 144.56,15.48 147.07,20.16L140.37,24.1C139.4,21.86 137.74,20.74 135.48,20.74C133.11,20.74 131.95,21.65 131.95,23.44C131.95,24.83 133.78,26.08 137.5,27.06C140.37,27.72 142.63,28.7 144.26,29.97H144.27H144.26ZM169.26,22.27H162.37V35.98C162.37,37.63 162.98,38.63 164.15,39.08C165,39.4 166.71,39.47 169.27,39.34V47.05C163.98,47.71 160.14,47.17 157.88,45.41C155.62,43.7 154.53,40.53 154.53,36V22.27H149.23V14.1H154.53V7.46L162.39,4.89V14.12H169.29V22.29H169.27L169.26,22.27ZM194.34,37.1C195.92,35.4 196.71,33.22 196.71,30.58C196.71,27.94 195.92,25.78 194.34,24.05C192.74,22.35 190.79,21.48 188.42,21.48C186.04,21.48 184.09,22.33 182.49,24.05C180.97,25.84 180.18,28 180.18,30.58C180.18,33.16 180.97,35.31 182.49,37.1C184.08,38.81 186.04,39.67 188.42,39.67C190.79,39.67 192.74,38.82 194.34,37.1ZM176.96,42.96C173.85,39.6 172.32,35.52 172.32,30.58C172.32,25.63 173.85,21.62 176.96,18.26C180.07,14.9 183.91,13.19 188.42,13.19C192.92,13.19 196.77,14.9 199.87,18.26C202.97,21.62 204.57,25.77 204.57,30.58C204.57,35.39 202.97,39.6 199.87,42.96C196.76,46.32 192.98,47.96 188.42,47.96C183.85,47.96 180.06,46.32 176.96,42.96ZM230.86,37.29C232.45,35.5 233.24,33.28 233.24,30.58C233.24,27.87 232.45,25.63 230.86,23.93C229.28,22.14 227.26,21.29 224.82,21.29C222.39,21.29 220.37,22.14 218.73,23.93C217.14,25.63 216.35,27.87 216.35,30.58C216.35,33.28 217.14,35.52 218.73,37.29C220.38,38.99 222.45,39.86 224.82,39.86C227.2,39.86 229.27,39 230.86,37.29ZM233.24,0.92H241.11V47.05H233.24V43.17C230.93,46.39 227.63,47.99 223.36,47.99C219.09,47.99 215.75,46.35 212.8,42.98C209.93,39.62 208.48,35.47 208.48,30.6C208.48,25.73 209.95,21.64 212.8,18.28C215.72,14.92 219.26,13.22 223.36,13.22C227.45,13.22 230.93,14.8 233.24,18.01V0.93V0.92ZM268.74,37.07C270.32,35.36 271.12,33.18 271.12,30.54C271.12,27.9 270.32,25.74 268.74,24.01C267.15,22.31 265.21,21.45 262.82,21.45C260.43,21.45 258.5,22.3 256.9,24.01C255.37,25.8 254.58,27.96 254.58,30.54C254.58,33.12 255.37,35.28 256.9,37.07C258.48,38.77 260.44,39.64 262.82,39.64C265.2,39.64 267.14,38.78 268.74,37.07ZM251.36,42.92C248.26,39.56 246.73,35.48 246.73,30.54C246.73,25.6 248.25,21.58 251.36,18.22C254.47,14.86 258.32,13.15 262.82,13.15C267.32,13.15 271.18,14.86 274.27,18.22C277.38,21.58 278.97,25.73 278.97,30.54C278.97,35.35 277.38,39.56 274.27,42.92C271.16,46.28 267.38,47.93 262.82,47.93C258.26,47.93 254.46,46.28 251.36,42.92ZM313,26.78V47.01H305.14V27.84C305.14,25.66 304.59,24.01 303.48,22.77C302.45,21.65 300.98,21.07 299.09,21.07C294.65,21.07 292.39,23.77 292.39,29.24V47.03H284.53V14.1H292.39V17.81C294.28,14.71 297.28,13.19 301.47,13.19C304.82,13.19 307.57,14.37 309.71,16.81C311.91,19.24 313,22.54 313,26.82"
|
android:pathData="M80.02,27.06V47.66H72.03V27.67C72.03,23.45 70.3,21.32 66.83,21.32C63,21.32 61.07,23.87 61.07,28.87V39.82H53.14V28.87C53.14,23.84 51.24,21.32 47.38,21.32C43.92,21.32 42.18,23.45 42.18,27.67V47.65H34.21V27.06C34.21,22.86 35.25,19.51 37.35,17.03C39.53,14.54 42.37,13.29 45.89,13.29C49.97,13.29 53.07,14.9 55.11,18.11L57.11,21.52L59.1,18.11C61.14,14.91 64.23,13.29 68.32,13.29C71.84,13.29 74.69,14.55 76.86,17.03C78.96,19.51 80.01,22.83 80.01,27.06H80.02ZM107.49,37.3C109.15,35.51 109.93,33.29 109.93,30.59C109.93,27.89 109.14,25.65 107.49,23.94C105.91,22.15 103.89,21.3 101.45,21.3C99.02,21.3 97.01,22.15 95.41,23.94C93.83,25.65 93.04,27.89 93.04,30.59C93.04,33.29 93.83,35.53 95.41,37.3C97,39 99.02,39.87 101.45,39.87C103.89,39.87 105.9,39.02 107.49,37.3ZM109.93,14.12H117.8V47.06H109.93V43.18C107.55,46.41 104.26,48 99.99,48C95.71,48 92.42,46.36 89.5,43C86.64,39.64 85.18,35.48 85.18,30.61C85.18,25.74 86.65,21.65 89.5,18.29C92.43,14.93 95.92,13.23 99.99,13.23C104.06,13.23 107.55,14.81 109.93,18.02V14.14V14.12ZM144.26,29.97C146.58,31.76 147.73,34.25 147.67,37.41C147.67,40.77 146.52,43.41 144.14,45.24C141.76,47.03 138.89,47.94 135.41,47.94C129.13,47.94 124.87,45.3 122.61,40.11L129.43,35.96C130.34,38.78 132.35,40.25 135.41,40.25C138.22,40.25 139.62,39.33 139.62,37.42C139.62,36.03 137.79,34.78 134.07,33.8C132.66,33.41 131.5,33.01 130.6,32.68C129.31,32.16 128.22,31.56 127.31,30.83C125.05,29.04 123.9,26.68 123.9,23.65C123.9,20.42 124.99,17.85 127.19,16C129.45,14.09 132.19,13.18 135.48,13.18C140.73,13.18 144.56,15.48 147.07,20.16L140.37,24.1C139.4,21.86 137.74,20.74 135.48,20.74C133.11,20.74 131.95,21.65 131.95,23.44C131.95,24.83 133.78,26.08 137.5,27.06C140.37,27.72 142.63,28.7 144.26,29.97H144.27H144.26ZM169.26,22.27H162.37V35.98C162.37,37.63 162.98,38.63 164.15,39.08C165,39.4 166.71,39.47 169.27,39.34V47.05C163.98,47.71 160.14,47.17 157.88,45.41C155.62,43.7 154.53,40.53 154.53,36V22.27H149.23V14.1H154.53V7.46L162.39,4.89V14.12H169.29V22.29H169.27L169.26,22.27ZM194.34,37.1C195.92,35.4 196.71,33.22 196.71,30.58C196.71,27.94 195.92,25.78 194.34,24.05C192.74,22.35 190.79,21.48 188.42,21.48C186.04,21.48 184.09,22.33 182.49,24.05C180.97,25.84 180.18,28 180.18,30.58C180.18,33.16 180.97,35.31 182.49,37.1C184.08,38.81 186.04,39.67 188.42,39.67C190.79,39.67 192.74,38.82 194.34,37.1ZM176.96,42.96C173.85,39.6 172.32,35.52 172.32,30.58C172.32,25.63 173.85,21.62 176.96,18.26C180.07,14.9 183.91,13.19 188.42,13.19C192.92,13.19 196.77,14.9 199.87,18.26C202.97,21.62 204.57,25.77 204.57,30.58C204.57,35.39 202.97,39.6 199.87,42.96C196.76,46.32 192.98,47.96 188.42,47.96C183.85,47.96 180.06,46.32 176.96,42.96ZM230.86,37.29C232.45,35.5 233.24,33.28 233.24,30.58C233.24,27.87 232.45,25.63 230.86,23.93C229.28,22.14 227.26,21.29 224.82,21.29C222.39,21.29 220.37,22.14 218.73,23.93C217.14,25.63 216.35,27.87 216.35,30.58C216.35,33.28 217.14,35.52 218.73,37.29C220.38,38.99 222.45,39.86 224.82,39.86C227.2,39.86 229.27,39 230.86,37.29ZM233.24,0.92H241.11V47.05H233.24V43.17C230.93,46.39 227.63,47.99 223.36,47.99C219.09,47.99 215.75,46.35 212.8,42.98C209.93,39.62 208.48,35.47 208.48,30.6C208.48,25.73 209.95,21.64 212.8,18.28C215.72,14.92 219.26,13.22 223.36,13.22C227.45,13.22 230.93,14.8 233.24,18.01V0.93V0.92ZM268.74,37.07C270.32,35.36 271.12,33.18 271.12,30.54C271.12,27.9 270.32,25.74 268.74,24.01C267.15,22.31 265.21,21.45 262.82,21.45C260.43,21.45 258.5,22.3 256.9,24.01C255.37,25.8 254.58,27.96 254.58,30.54C254.58,33.12 255.37,35.28 256.9,37.07C258.48,38.77 260.44,39.64 262.82,39.64C265.2,39.64 267.14,38.78 268.74,37.07ZM251.36,42.92C248.26,39.56 246.73,35.48 246.73,30.54C246.73,25.6 248.25,21.58 251.36,18.22C254.47,14.86 258.32,13.15 262.82,13.15C267.32,13.15 271.18,14.86 274.27,18.22C277.38,21.58 278.97,25.73 278.97,30.54C278.97,35.35 277.38,39.56 274.27,42.92C271.16,46.28 267.38,47.93 262.82,47.93C258.26,47.93 254.46,46.28 251.36,42.92ZM313,26.78V47.01H305.14V27.84C305.14,25.66 304.59,24.01 303.48,22.77C302.45,21.65 300.98,21.07 299.09,21.07C294.65,21.07 292.39,23.77 292.39,29.24V47.03H284.53V14.1H292.39V17.81C294.28,14.71 297.28,13.19 301.47,13.19C304.82,13.19 307.57,14.37 309.71,16.81C311.91,19.24 313,22.54 313,26.82"
|
||||||
android:fillColor="#ffffff"/>
|
android:fillColor="#000"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
android:shape="ring"
|
android:shape="ring"
|
||||||
android:thickness="4dp"
|
android:thickness="4dp"
|
||||||
android:useLevel="true">
|
android:useLevel="true">
|
||||||
<solid android:color="@color/gray_100"/>
|
<solid android:color="?colorGray100"/>
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
</layer-list>
|
</layer-list>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user