Compare commits

..

70 Commits

Author SHA1 Message Date
LucasGGamerM
0b96fb05fc Bumping version number 2022-12-18 20:57:47 -03:00
LucasGGamerM
8767d62de7 Fixing a fucking NPE 2022-12-18 20:54:42 -03:00
LucasGGamerM
74fb04e2d4 Disabling the Translate button on statuses that are from the same default language, or that are private or direct messages. 2022-12-18 20:04:32 -03:00
LucasGGamerM
2537460e16 Tis broken 2022-12-18 19:43:48 -03:00
LucasGGamerM
be3dfde3be Changing out (again) the link back to moshidons github 2022-12-18 17:58:21 -03:00
LucasGGamerM
42025035ad Caching the status translation for easier use 2022-12-18 17:57:09 -03:00
LucasGGamerM
6a667fdf32 Bump version number 2022-12-18 14:11:18 -03:00
LucasGGamerM
bfafac3d4f Fixing the translate icon tint, changing translate icon to fluent icon. Making it better overall 2022-12-18 14:09:21 -03:00
LucasGGamerM
0cafbe9f91 Logic side done for the red theme. 2022-12-18 12:54:17 -03:00
LucasGGamerM
2fbf172729 Styles and colors.xml side done. Putting a string in there as well :D 2022-12-18 12:41:40 -03:00
sk
bb9755f4af Making the boost icon better 2022-12-18 11:12:55 -03:00
LucasGGamerM
2a01377a8a Why tf did this revert to the old thing? 2022-12-18 11:04:47 -03:00
LucasGGamerM
61cc6d5d07 Checking out of the account list fragment thing 2022-12-18 11:04:32 -03:00
LucasGGamerM
1d74a37f60 Dunno why, just want to commit this 2022-12-18 11:04:05 -03:00
LucasGGamerM
ef9645f9e7 Ranaming stuffs back to moshidon 2022-12-18 11:02:51 -03:00
sk
6a103ca3f3 fix text view cutting off text
closes #157
2022-12-18 10:52:01 -03:00
LucasGGamerM
c22772121b Ranaming stuffs back to moshidon 2022-12-17 21:23:52 -03:00
kaea
de7bc69d2a Translated using Weblate (Polish)
Currently translated at 100.0% (59 of 59 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/
2022-12-17 21:21:55 -03:00
itslameni
2eccd572c9 Translated using Weblate (Russian)
Currently translated at 37.5% (3 of 8 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ru/
2022-12-17 21:21:55 -03:00
itslameni
824a62024b A whole lot of stuffs, including merging stuffs from weblate 2022-12-17 21:21:54 -03:00
tippete
3a3cfda919 Translated using Weblate (Italian)
Currently translated at 100.0% (59 of 59 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/it/
2022-12-17 21:21:05 -03:00
AiOO
e29120cc51 Translated using Weblate (Korean)
Currently translated at 100.0% (8 of 8 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ko/
2022-12-17 21:21:03 -03:00
nitrogenez
197d5c6bc3 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (59 of 59 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2022-12-17 21:21:00 -03:00
jonta
d143cc75db Translated using Weblate (Portuguese (Brazil))
Currently translated at 93.2% (55 of 59 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2022-12-17 21:21:00 -03:00
kaea
1635a06c54 Translated using Weblate (Polish)
Currently translated at 100.0% (59 of 59 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/
2022-12-17 21:21:00 -03:00
Choukajohn
76de0d8c70 Translated using Weblate (French)
Currently translated at 100.0% (59 of 59 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2022-12-17 21:21:00 -03:00
AiOO
402a995b8f Translated using Weblate (Korean)
Currently translated at 100.0% (59 of 59 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2022-12-17 21:21:00 -03:00
sk
f580ba7779 Refactoring names to custom. Thank you @sk22 for this amazing piece of commit 2022-12-17 21:19:40 -03:00
sk
bc3869b920 fix follow request list issues
closes #153
2022-12-17 21:09:56 -03:00
LucasGGamerM
020f4a5a1a Hiding material you in older android versions fix 2022-12-17 21:00:02 -03:00
sk
b054caa967 Hiding material you in older android versions 2022-12-17 20:52:11 -03:00
LucasGGamerM
82b7633650 Renaming stuff 2022-12-16 19:13:11 -03:00
LucasGGamerM
33497864f2 Bumping version number 2022-12-16 12:47:17 -03:00
LucasGGamerM
4c9d1544fa Refactoring the purple/pink theme. Merge this @sk22, as its exactly what you asked earlier 2022-12-16 12:43:43 -03:00
LucasGGamerM
bce2367cfc Making material you default for devices that support it. For devices that dont, still setting purple as the main theme. 2022-12-16 12:07:29 -03:00
sk
390ecc48fb Refactoring a string for easier translation 2022-12-16 11:44:18 -03:00
LucasGGamerM
9ed99edd6e Its unbroken now! ITS WORKING! 2022-12-16 11:28:48 -03:00
LucasGGamerM
4362490539 Fixing the instance thing again 2022-12-16 11:17:43 -03:00
LucasGGamerM
f5d225fc3e Still broken 2022-12-16 11:16:10 -03:00
LucasGGamerM
063e9287fd A little better. Some refactoring done. 2022-12-16 11:09:42 -03:00
LucasGGamerM
ba376908cd Get instance things. Still figuring out the merge 2022-12-16 11:03:24 -03:00
sk
caddf0021c A bunch of stuff. I dont know wtf I am doing 2022-12-16 10:54:50 -03:00
Grishka
90645f4d90 Adding stuff from upstream 2022-12-16 10:30:42 -03:00
LucasGGamerM
1316fcae22 Fixing compile problems. Adding another one 2022-12-16 10:23:45 -03:00
LucasGGamerM
27dee7297b Changing things back to default 2022-12-16 10:13:26 -03:00
sk
13ecba40ae use default posting language from server 2022-12-16 10:08:19 -03:00
LucasGGamerM
e15dd0d8b3 Merge remote-tracking branch 'origin/master' 2022-12-15 15:31:16 -03:00
LucasGGamerM
1ab26bc665 Merge pull request #7
Add Android 13 monochrome icon
2022-12-15 15:28:33 -03:00
LucasGGamerM
e6758d8c01 Merge branch 'fork' 2022-12-15 15:25:01 -03:00
LucasGGamerM
4621787e34 Imma just wait until @sk22 works on this again 2022-12-15 15:24:47 -03:00
LucasGGamerM
10ad35a285 Deleting the entries for the default language, as I will instead get them from the server 2022-12-15 15:05:22 -03:00
LucasGGamerM
d10145a6ba Removing this function call because its useless as i will get the default language from the server, and not from this. 2022-12-15 15:03:33 -03:00
LucasGGamerM
c9792ced32 Merge remote-tracking branch 'origin/fork' into fork 2022-12-15 15:01:21 -03:00
LucasGGamerM
a3fb09a33c Tis broken. Just need to fix MastodonLanguage.java getDefaultLanguge. I am still trying to figure out where it is stored 2022-12-15 14:56:11 -03:00
LucasGGamerM
6d875fd890 It now properly sets a default on every change. Not the final implementation yet 2022-12-14 20:39:02 -03:00
LucasGGamerM
5d87fb7b67 MastodonLanguage util side done 2022-12-14 20:15:29 -03:00
LucasGGamerM
4cbb59850b Superduper buggy. But the preferences side is done, so make sure to merge this commit as well @sk22 2022-12-14 19:59:53 -03:00
LucasGGamerM
a2022b25e5 Fixing compile problems. Merging the language picker from megalodon. 2022-12-14 19:23:06 -03:00
Florian Obernberger
0d168f93ed Add Android 13 monochrome icon 2022-12-14 23:03:41 +01:00
LucasGGamerM
94ac5b9bb7 Merge branch 'feature/language-selector' into fork
# Conflicts:
#	mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java
#	mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java
#	mastodon/src/main/res/values/strings.xml
2022-12-14 19:03:15 -03:00
sk
231ea46f9f change wording in string 2022-12-10 22:29:22 +01:00
sk
600be455a3 save languages per account 2022-12-10 22:17:51 +01:00
sk
a4df06726f don't add to recent languages if replying 2022-12-10 21:31:04 +01:00
sk
e45e2c31d1 use mastodon languages list
fix #139
2022-12-10 21:07:34 +01:00
sk
17dc0850d5 apply language when replying 2022-12-09 20:53:56 +01:00
sk
9667a32e44 save recently used languages 2022-12-09 20:48:51 +01:00
sk
4e6ba84bb3 implement language selector 2022-12-09 19:34:43 +01:00
Gregory K
b79ba71228 Merge pull request #445 from sk22/better-inline-emoji-search
Improve inline emoji search
2022-12-08 23:22:24 +03:00
Grishka
2903874dbc Splash fragment redesign 2022-12-08 23:22:10 +03:00
Grishka
202a5f9581 Fix #443 again 2022-12-08 01:48:23 +03:00
102 changed files with 2596 additions and 793 deletions

View File

@@ -1,4 +1,4 @@
![Pink logo with pink shark](mastodon/src/main/res/mipmap-xhdpi/ic_launcher_round.png) ![MoshidonLogo](mastodon/src/main/res/mipmap-xhdpi/ic_launcher_round.png)
# Moshidon, the material you mastodon client! # Moshidon, the material you mastodon client!

View File

@@ -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 68
versionName "1.1.4+fork.65.moshinda" versionName "1.1.4+fork.68.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 {

View File

@@ -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;
@@ -18,10 +29,19 @@ public class GlobalUserPreferences{
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;
public static Map<String, String> defaultLanguages;
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);
@@ -36,9 +56,15 @@ public class GlobalUserPreferences{
disableMarquee=prefs.getBoolean("disableMarquee", false); disableMarquee=prefs.getBoolean("disableMarquee", 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)];
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
color=ColorPreference.values()[prefs.getInt("color", 0)];
}else{
color=ColorPreference.values()[prefs.getInt("color", 1)]; color=ColorPreference.values()[prefs.getInt("color", 1)];
} }
}
public static void save(){ public static void save(){
getPrefs().edit() getPrefs().edit()
.putBoolean("playGifs", playGifs) .putBoolean("playGifs", playGifs)
@@ -52,18 +78,20 @@ public class GlobalUserPreferences{
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings) .putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
.putBoolean("disableMarquee", disableMarquee) .putBoolean("disableMarquee", disableMarquee)
.putInt("theme", theme.ordinal()) .putInt("theme", theme.ordinal())
.putString("recentLanguages", gson.toJson(recentLanguages))
.putInt("color", color.ordinal()) .putInt("color", color.ordinal())
.apply(); .apply();
} }
public enum ColorPreference{ public enum ColorPreference{
PINK, MATERIAL3,
PURPLE, PURPLE,
PINK,
GREEN, GREEN,
BLUE, BLUE,
ORANGE, ORANGE,
YELLOW, YELLOW,
MATERIAL3 RED
} }
public enum ThemePreference{ public enum ThemePreference{

View File

@@ -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;

View File

@@ -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)

View File

@@ -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");
@@ -76,6 +72,8 @@ public class StatusInteractionController{
@Override @Override
public void onSuccess(Status result){ public void onSuccess(Status result){
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 +82,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 +109,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 +118,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));
} }
}) })

View File

@@ -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";
}
}
} }

View File

@@ -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;
}
}

View File

@@ -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();
} }

View File

@@ -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,9 @@ 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); 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 +250,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){
updateSessionPreferences(session);
updateSessionLocalInfo(session); updateSessionLocalInfo(session);
} // }
if(now-session.filtersLastUpdated>3600_000L){ // if(now-session.filtersLastUpdated>3600_000L){
updateSessionWordFilters(session); updateSessionWordFilters(session);
} // }
} }
if(loadedInstances){ if(loadedInstances){
maybeUpdateCustomEmojis(domains); maybeUpdateCustomEmojis(domains);
@@ -263,10 +266,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 +291,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 +328,11 @@ 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 {
if (Integer.parseInt(instance.version.split("\\.")[0]) >= 4) {
updateInstanceInfoV2(domain);
}
} catch (Exception ignored) {}
} }
@Override @Override
@@ -323,6 +343,19 @@ public class AccountSessionManager{
.execNoAuth(domain); .execNoAuth(domain);
} }
public void updateInstanceInfoV2(String domain) {
new GetInstance.V2().setCallback(new Callback<>() {
@Override
public void onSuccess(Instance.V2 v2) {
Instance instanceInfo = instances.get(domain);
if (instanceInfo != null) instanceInfo.v2 = v2;
}
@Override
public void onError(ErrorResponse errorResponse) {}
}).execNoAuth(domain);
}
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 +431,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;

View File

@@ -462,12 +462,13 @@ 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){
public void onRevealTranslationClick(HeaderStatusDisplayItem.Holder holder, View v){
Status status=holder.getItem().status; Status status=holder.getItem().status;
revealTranslation(status, holder.getItemID()); revealTranslation(status, holder.getItemID(), v);
} }
protected void revealTranslation(Status status, String itemID){ protected void revealTranslation(Status status, String itemID, View v){
status.wantsTranslation=!status.wantsTranslation; status.wantsTranslation=!status.wantsTranslation;
TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class); TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class);
if(text!=null) if(text!=null)

View File

@@ -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;
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,6 +200,9 @@ 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;
@Override @Override
public void onCreate(Bundle savedInstanceState){ public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -201,6 +214,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");
@@ -219,8 +233,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
charLimit=instance.configuration.statuses.maxCharacters; charLimit=instance.configuration.statuses.maxCharacters;
else else
charLimit=500; charLimit=500;
loadDefaultStatusVisibility(savedInstanceState);
} }
@Override @Override
@@ -375,6 +387,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
statusVisibility=editingStatus.visibility; statusVisibility=editingStatus.visibility;
} }
loadDefaultStatusVisibility(savedInstanceState);
updateVisibilityIcon(); updateVisibilityIcon();
autocompleteViewController=new ComposeAutocompleteViewController(getActivity(), accountID); autocompleteViewController=new ComposeAutocompleteViewController(getActivity(), accountID);
@@ -403,6 +416,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 +508,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 +550,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 +569,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){
@@ -615,6 +632,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 +645,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 +771,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 +781,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 +849,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(){
@@ -1356,13 +1440,11 @@ 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
public void onSuccess(Preferences result){
// Only override the reply visibility if our preference is more private // Only override the reply visibility if our preference is more private
if (result.postingDefaultVisibility.isLessVisibleThan(statusVisibility)) { if (prefs.postingDefaultVisibility.isLessVisibleThan(statusVisibility)) {
statusVisibility = switch (result.postingDefaultVisibility) { statusVisibility = switch (prefs.postingDefaultVisibility) {
case PUBLIC -> StatusPrivacy.PUBLIC; case PUBLIC -> StatusPrivacy.PUBLIC;
case UNLISTED -> StatusPrivacy.UNLISTED; case UNLISTED -> StatusPrivacy.UNLISTED;
case PRIVATE -> StatusPrivacy.PRIVATE; case PRIVATE -> StatusPrivacy.PRIVATE;
@@ -1374,16 +1456,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
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(){

View File

@@ -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();
} }

View File

@@ -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,7 +117,7 @@ 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();
})); }));
@@ -157,11 +160,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
@@ -705,6 +712,10 @@ public class SettingsFragment extends MastodonToolbarFragment{
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;
@@ -719,6 +730,7 @@ 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());
} }
@@ -733,7 +745,8 @@ public class SettingsFragment extends MastodonToolbarFragment{
case BLUE -> R.string.sk_color_theme_blue; case BLUE -> R.string.sk_color_theme_blue;
case ORANGE -> R.string.sk_color_theme_brown; case ORANGE -> R.string.sk_color_theme_brown;
case YELLOW -> R.string.sk_color_theme_yellow; case YELLOW -> R.string.sk_color_theme_yellow;
case MATERIAL3 -> R.string.sk_color_theme_material_you; case RED -> R.string.sk_color_theme_red;
case MATERIAL3 -> R.string.sk_color_theme_material3;
}); });
} }
} }
@@ -853,10 +866,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){

View File

@@ -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;
} }
private class PagerAdapter extends RecyclerView.Adapter<PagerViewHolder>{
@NonNull
@Override @Override
protected void onShown(){ public PagerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
super.onShown(); return new PagerViewHolder(viewType);
motionEffect.activate();
} }
@Override @Override
protected void onHidden(){ public void onBindViewHolder(@NonNull PagerViewHolder holder, int position){}
super.onHidden();
motionEffect.deactivate(); @Override
public int getItemCount(){
return 3;
}
@Override
public int getItemViewType(int position){
return position;
}
}
private class PagerViewHolder extends RecyclerView.ViewHolder{
public PagerViewHolder(int page){
super(new LinearLayout(getActivity()));
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();
}
} }
} }

View File

@@ -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;
} }

View File

@@ -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);
}
} }

View File

@@ -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();

View File

@@ -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

View File

@@ -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;

View File

@@ -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(){ mergeAdapter=new MergeRecyclerAdapter();
@Override mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
public void onTabSelected(TabLayout.Tab tab){ mergeAdapter.addAdapter(adapter=new InstancesAdapter());
CatalogCategory category=categories.get(tab.getPosition()); return mergeAdapter;
currentCategory=category.category;
updateFilteredList();
} }
@SuppressLint("ClickableViewAccessibility")
@Override @Override
public void onTabUnselected(TabLayout.Tab tab){ public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
} backBtn=view.findViewById(R.id.btn_back);
backBtn.setOnClickListener(v->{
@Override if(searchQueryMode){
public void onTabReselected(TabLayout.Tab tab){ 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,29 +272,134 @@ 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)
@@ -200,8 +411,6 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment{
Bundle args=new Bundle(); Bundle args=new Bundle();
args.putParcelable("instance", Parcels.wrap(instance)); args.putParcelable("instance", Parcels.wrap(instance));
Nav.go(getActivity(), InstanceRulesFragment.class, args); Nav.go(getActivity(), InstanceRulesFragment.class, args);
}else{
}
} }
// private String getEmojiForCategory(String category){ // private String getEmojiForCategory(String category){
@@ -265,14 +474,32 @@ 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();
if(searchQueryMode){
if(!TextUtils.isEmpty(currentSearchQuery)){
for(CatalogInstance instance:data){ for(CatalogInstance instance:data){
if(currentCategory.equals("all") || instance.categories.contains(currentCategory)){ if(instance.domain.contains(currentSearchQuery)){
if(TextUtils.isEmpty(currentSearchQuery) || instance.domain.contains(currentSearchQuery)){
if(instance.domain.equals(currentSearchQuery) || !isSignup || !instance.approvalRequired)
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);
}
}
}
}
}
}
DiffUtil.calculateDiff(new DiffUtil.Callback(){ DiffUtil.calculateDiff(new DiffUtil.Callback(){
@Override @Override
public int getOldListSize(){ public int getOldListSize(){
@@ -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;
} }
private class InstanceViewHolder extends BindableViewHolder<CatalogInstance> implements UsableRecyclerView.Clickable{ @Override
private final TextView title, description, userCount, lang; public ImageLoaderRequest getImageRequest(int position, int image){
return filteredData.get(position).thumbnailRequest;
}
}
private class InstanceViewHolder extends BindableViewHolder<CatalogInstance> implements UsableRecyclerView.DisableableClickable, ImageLoaderViewHolder{
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,11 +644,18 @@ 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++){
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
if(holder.getAbsoluteAdapterPosition()==mergeAdapter.getPositionForAdapter(adapter)+idx && holder instanceof InstanceViewHolder ivh){
ivh.radioButton.setChecked(false); ivh.radioButton.setChecked(false);
found=true;
break;
} }
} }
if(!found)
adapter.notifyItemChanged(idx);
}
} }
radioButton.setChecked(true); radioButton.setChecked(true);
if(chosenInstance==null) if(chosenInstance==null)
@@ -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;
}
} }
} }

View File

@@ -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()));
} }
} }
} }

View File

@@ -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().equals(passwordConfirm.getText())){
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,33 +174,9 @@ 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())){
copyAvatar(this::actuallySubmit);
}else{
actuallySubmit(); actuallySubmit();
} }
}
private void actuallySubmit(){ private void actuallySubmit(){
String username=this.username.getText().toString().trim(); String username=this.username.getText().toString().trim();
@@ -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;

View File

@@ -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;
}
}
} }

View File

@@ -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;

View File

@@ -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
}
} }

View File

@@ -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;
if(session.self.avatar!=null)
req=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? session.self.avatar : session.self.avatarStatic, V.dp(50), V.dp(50)); req=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? session.self.avatar : session.self.avatarStatic, V.dp(50), V.dp(50));
else
req=null;
} }
} }
} }

View File

@@ -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);
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(); rebind();
}
}); });
} }

View File

@@ -4,9 +4,14 @@ 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.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.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
@@ -24,7 +29,7 @@ import org.parceler.Parcels;
import java.text.DecimalFormat; 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 +51,7 @@ 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 final View.AccessibilityDelegate buttonAccessibilityDelegate=new View.AccessibilityDelegate(){ private final View.AccessibilityDelegate buttonAccessibilityDelegate=new View.AccessibilityDelegate(){
@Override @Override
@@ -56,6 +62,16 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
} }
}; };
static {
opacityOut = new AlphaAnimation(1, 0.5f);
opacityOut.setDuration(200);
opacityOut.setInterpolator(CubicBezierInterpolator.DEFAULT);
opacityOut.setFillAfter(true);
opacityIn = new AlphaAnimation(0.5f, 1);
opacityIn.setDuration(150);
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,14 +90,19 @@ 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.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.setAccessibilityDelegate(buttonAccessibilityDelegate); share.setAccessibilityDelegate(buttonAccessibilityDelegate);
} }
@@ -115,21 +136,43 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args); Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
} }
private boolean onButtonTouch(View v, MotionEvent event){
int action = event.getAction();
// 20dp to center in middle of icon, because: (icon width = 24dp) / 2 + (paddingStart = 8dp)
v.setPivotX(V.dp(20));
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
v.animate().scaleX(1).scaleY(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(100).start();
if (action == MotionEvent.ACTION_UP) v.performClick();
} else if (action == MotionEvent.ACTION_DOWN) {
v.animate().scaleX(0.85f).scaleY(0.85f).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(50).start();
}
return true;
}
private void onBoostClick(View v){ private void onBoostClick(View v){
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setReblogged(item.status, !item.status.reblogged); v.startAnimation(opacityOut);
boost.setSelected(item.status.reblogged); boost.setSelected(!item.status.reblogged);
bindButton(boost, item.status.reblogsCount); AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setReblogged(item.status, !item.status.reblogged, r->{
v.startAnimation(opacityIn);
bindButton(boost, r.reblogsCount);
});
} }
private void onFavoriteClick(View v){ private void onFavoriteClick(View v){
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setFavorited(item.status, !item.status.favourited); v.startAnimation(opacityOut);
favorite.setSelected(item.status.favourited); favorite.setSelected(!item.status.favourited);
bindButton(favorite, item.status.favouritesCount); AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setFavorited(item.status, !item.status.favourited, r->{
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); v.startAnimation(opacityOut);
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){

View File

@@ -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;
@@ -76,8 +77,10 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
this.status=status; this.status=status;
HtmlParser.parseCustomEmoji(parsedName, user.emojis); HtmlParser.parseCustomEmoji(parsedName, user.emojis);
emojiHelper.setText(parsedName); emojiHelper.setText(parsedName);
Preferences prefs = AccountSessionManager.getInstance().getAccount(accountID).preferences;
if(status!=null){ if(status!=null){
hasTranslateToggle=true; hasTranslateToggle = !(Objects.equals(status.language, prefs.postingDefaultLanguage) || (status.visibility == StatusPrivacy.DIRECT || status.visibility == StatusPrivacy.PRIVATE));
// hasTranslateToggle = !status.language.equals(prefs.postingDefaultLanguage) || (status.visibility==StatusPrivacy.PRIVATE);
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){
@@ -138,7 +141,7 @@ 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)); translate.setOnClickListener(v->item.parentFragment.onRevealTranslationClick(this, v));
optionsMenu=new PopupMenu(activity, more); optionsMenu=new PopupMenu(activity, more);
optionsMenu.inflate(R.menu.post); optionsMenu.inflate(R.menu.post);
@@ -241,7 +244,8 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
} }
} }
if(item.hasTranslateToggle){ if(item.hasTranslateToggle){
translate.setImageResource(item.status.wantsTranslation ? R.drawable.ic_translate_on : R.drawable.ic_translate_off); translate.setImageResource(R.drawable.ic_translate);
translate.setSelected(item.status.wantsTranslation);
} }
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)){

View File

@@ -97,12 +97,13 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
@Override @Override
public void onBind(TextStatusDisplayItem item){ public void onBind(TextStatusDisplayItem item){
if(item.status.wantsTranslation){ if(item.status.wantsTranslation && item.status.translation == null){
new GetStatusTranslation(item.status.id) new GetStatusTranslation(item.status.id)
.setCallback(new Callback<StatusTranslation>(){ .setCallback(new Callback<StatusTranslation>(){
@Override @Override
public void onSuccess(StatusTranslation status){ public void onSuccess(StatusTranslation statusTranslation){
text.setText(status.getStrippedText()); item.status.translation = statusTranslation.getStrippedText();
text.setText(item.status.translation);
} }
@Override @Override
public void onError(ErrorResponse error){ public void onError(ErrorResponse error){
@@ -110,10 +111,15 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
text.setText(item.text); text.setText(item.text);
error.showToast(item.parentFragment.getActivity()); error.showToast(item.parentFragment.getActivity());
} }
}) })
.wrapProgress(item.parentFragment.getActivity(), R.string.loading, true) .wrapProgress(item.parentFragment.getActivity(), R.string.loading, true)
.exec(item.parentFragment.getAccountID()); .exec(item.parentFragment.getAccountID());
}
if(item.status.wantsTranslation){
if(item.status.translation != null)
{
text.setText(item.status.translation);
}
}else{ }else{
text.setText(item.text); text.setText(item.text);
} }

View File

@@ -718,6 +718,16 @@ public class UiUtils{
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Yellow : R.style.Theme_Mastodon_Dark_Yellow; GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Yellow : R.style.Theme_Mastodon_Dark_Yellow;
}); });
break; break;
case RED:
context.setTheme(switch(GlobalUserPreferences.theme){
case AUTO ->
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Red : R.style.Theme_Mastodon_AutoLightDark_Red;
case LIGHT ->
R.style.Theme_Mastodon_Light_Red;
case DARK ->
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Red : R.style.Theme_Mastodon_Dark_Red;
});
break;
case MATERIAL3: case MATERIAL3:
context.setTheme(switch(GlobalUserPreferences.theme){ context.setTheme(switch(GlobalUserPreferences.theme){
case AUTO -> case AUTO ->

View File

@@ -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();
}
}

View File

@@ -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,6 +111,11 @@ public class FloatingHintEditTextLayout extends FrameLayout{
hintVisible=newHintVisible; hintVisible=newHintVisible;
label.setAlpha(1); label.setAlpha(1);
edit.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
@Override
public boolean onPreDraw(){
edit.getViewTreeObserver().removeOnPreDrawListener(this);
float scale=edit.getLineHeight()/(float)label.getLineHeight(); 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); float transY=edit.getHeight()/2f-edit.getLineHeight()/2f+(edit.getTop()-label.getTop())-(label.getHeight()/2f-label.getLineHeight()/2f);
@@ -92,7 +125,8 @@ public class FloatingHintEditTextLayout extends FrameLayout{
ObjectAnimator.ofFloat(edit, TRANSLATION_Y, 0), ObjectAnimator.ofFloat(edit, TRANSLATION_Y, 0),
ObjectAnimator.ofFloat(label, SCALE_X, scale), ObjectAnimator.ofFloat(label, SCALE_X, scale),
ObjectAnimator.ofFloat(label, SCALE_Y, scale), ObjectAnimator.ofFloat(label, SCALE_Y, scale),
ObjectAnimator.ofFloat(label, TRANSLATION_Y, transY) ObjectAnimator.ofFloat(label, TRANSLATION_Y, transY),
ObjectAnimator.ofFloat(FloatingHintEditTextLayout.this, "animProgress", 0f)
); );
edit.setHintTextColor(0); edit.setHintTextColor(0);
}else{ }else{
@@ -103,7 +137,8 @@ public class FloatingHintEditTextLayout extends FrameLayout{
ObjectAnimator.ofFloat(edit, TRANSLATION_Y, offsetY), ObjectAnimator.ofFloat(edit, TRANSLATION_Y, offsetY),
ObjectAnimator.ofFloat(label, SCALE_X, 1f), ObjectAnimator.ofFloat(label, SCALE_X, 1f),
ObjectAnimator.ofFloat(label, SCALE_Y, 1f), ObjectAnimator.ofFloat(label, SCALE_Y, 1f),
ObjectAnimator.ofFloat(label, TRANSLATION_Y, 0f) ObjectAnimator.ofFloat(label, TRANSLATION_Y, 0f),
ObjectAnimator.ofFloat(FloatingHintEditTextLayout.this, "animProgress", 1f)
); );
} }
anim.setDuration(150); anim.setDuration(150);
@@ -115,10 +150,171 @@ public class FloatingHintEditTextLayout extends FrameLayout{
currentAnim=null; currentAnim=null;
if(hintVisible){ if(hintVisible){
label.setAlpha(0); label.setAlpha(0);
edit.setHintTextColor(label.getTextColors()); edit.setHintTextColor(origHintColors);
} }
} }
}); });
currentAnim=anim; currentAnim=anim;
return true;
}
});
}
@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

View 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>

View 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>

View 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="?colorM3OnSecondaryContainer" android:alpha="0.12"/>
</selector>

View 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="?colorM3OnSurfaceVariant" android:alpha="0.12"/>
</selector>

View 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_focused="true"/>
<item android:color="?colorM3OnSurfaceVariant"/>
</selector>

View 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>

View 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>

View File

@@ -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>

View 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>

View 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>

View 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>

View File

@@ -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>

View 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>

View 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>

View 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="M7,10l5,5 5,-5z"/>
</vector>

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="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>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="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>

View 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>

View 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>

View 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>

View 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="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>

View 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>

View 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>

View File

@@ -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>

View File

@@ -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>

View 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>

View File

@@ -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>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="#fff"/>
</shape>

View File

@@ -42,8 +42,8 @@
android:layout_toStartOf="@id/visibility" android:layout_toStartOf="@id/visibility"
android:background="?android:selectableItemBackgroundBorderless" android:background="?android:selectableItemBackgroundBorderless"
android:scaleType="center" android:scaleType="center"
android:src="@drawable/ic_visibility" android:src="@drawable/ic_translate"
android:tint="?android:textColorSecondary" /> android:tint="@color/translate_icon" />
<ImageView <ImageView
android:id="@+id/avatar" android:id="@+id/avatar"

View File

@@ -180,7 +180,7 @@
android:layout_weight="1" android:layout_weight="1"
android:textSize="16sp" android:textSize="16sp"
android:singleLine="true" android:singleLine="true"
android:text="@string/poll_allow_multiple" /> android:text="@string/sk_poll_allow_multiple" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<me.grishka.appkit.views.FragmentRootLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
tools:context=".fragments.onboarding.AccountActivationFragment">
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1">
android:background="?colorBackgroundLight">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -17,40 +17,67 @@
android:orientation="vertical" android:orientation="vertical"
android:clipChildren="false"> android:clipChildren="false">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/m3_headline_medium"
android:minHeight="36dp"
android:layout_marginTop="32dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="12dp"
android:gravity="center_vertical"
android:text="@string/confirm_email_title"/>
<TextView <TextView
android:id="@+id/subtitle" android:id="@+id/subtitle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="16dp" android:layout_marginStart="56dp"
android:layout_marginRight="16dp" android:layout_marginEnd="24dp"
android:textAppearance="@style/m3_title_medium" android:layout_marginTop="12dp"
android:textColor="?android:textColorSecondary" android:textAppearance="@style/m3_body_large"
android:textColor="?colorM3OnSurface"
android:text="@string/confirm_email_subtitle"/> android:text="@string/confirm_email_subtitle"/>
<ImageView <ImageView
android:layout_width="wrap_content" android:layout_width="230dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:layout_margin="32dp" android:layout_marginTop="12dp"
android:importantForAccessibility="no"
android:adjustViewBounds="true"
android:src="@drawable/confirm_email_art"/> android:src="@drawable/confirm_email_art"/>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
<include layout="@layout/button_bar_activation" /> <LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center_horizontal">
</me.grishka.appkit.views.FragmentRootLinearLayout> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/m3_label_large"
android:textColor="?colorM3OnSurfaceVariant"
android:text="@string/confirm_email_didnt_get"/>
<Button
android:id="@+id/btn_resend"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:text="@string/resend"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:fontFeatureSettings="'tnum'"
style="@style/Widget.Mastodon.M3.Button.Text"/>
</LinearLayout>
<Button
android:id="@+id/btn_next"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:minWidth="145dp"
style="@style/Widget.Mastodon.M3.Button.Filled"
android:text="@string/open_email_app" />
</LinearLayout>

View File

@@ -5,14 +5,95 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:orientation="vertical"
android:id="@+id/appkit_loader_root" android:id="@+id/appkit_loader_root"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android">
android:background="?colorBackgroundLight">
<RelativeLayout
android:id="@+id/top_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:background="@drawable/bg_onboarding_panel">
<EditText
android:id="@+id/search_edit"
android:layout_width="match_parent"
android:layout_height="48dp"
android:elevation="0dp"
android:inputType="textFilter|textUri"
android:layout_toEndOf="@+id/btn_back"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="16dp"
android:textAppearance="@style/m3_body_large"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:paddingStart="12dp"
android:paddingEnd="40dp"
android:drawableStart="@drawable/ic_m3_search"
android:drawablePadding="8dp"
android:drawableTint="?colorM3OnSurfaceVariant"
android:gravity="center_vertical"
android:textColorHint="?colorM3OnSurfaceVariant"
android:textColor="?colorM3OnSurfaceVariant"
android:background="@drawable/round_rect"
android:backgroundTint="?colorM3SurfaceVariant"
android:hint="@string/search_communities"/>
<ImageButton
android:id="@+id/btn_back"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginTop="20dp"
android:layout_marginStart="8dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:background="?android:selectableItemBackgroundBorderless"
android:tint="?colorM3OnSurface"
android:contentDescription="@string/back"
android:src="@drawable/ic_arrow_back"/>
<ImageButton
android:id="@+id/clear"
android:layout_width="24dp"
android:layout_height="24dp"
android:background="?android:selectableItemBackgroundBorderless"
android:layout_alignEnd="@id/search_edit"
android:layout_alignTop="@id/search_edit"
android:layout_margin="12dp"
android:contentDescription="@string/clear"
android:tint="?colorM3OnSurfaceVariant"
android:visibility="gone"
android:src="@drawable/ic_m3_cancel"/>
<HorizontalScrollView
android:id="@+id/filters_scroll"
android:layout_below="@id/search_edit"
android:layout_width="match_parent"
android:layout_height="48dp"
android:scrollbars="none">
<LinearLayout
android:id="@+id/filters_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingTop="8dp"
android:paddingRight="16dp"
android:paddingBottom="8dp"
android:paddingLeft="16dp"
android:showDividers="middle"
android:divider="@drawable/empty_8dp">
</LinearLayout>
</HorizontalScrollView>
</RelativeLayout>
<FrameLayout <FrameLayout
android:id="@+id/appkit_loader_content" android:id="@+id/appkit_loader_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0px" android:layout_height="0px"
android:layout_weight="1"> android:layout_weight="1"
android:background="?colorM3Surface">
<include layout="@layout/loading" <include layout="@layout/loading"
android:id="@+id/loading"/> android:id="@+id/loading"/>
@@ -33,32 +114,29 @@
android:id="@+id/button_bar" android:id="@+id/button_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?colorBackgroundLight" android:orientation="vertical"
android:outlineProvider="bounds" android:background="@drawable/bg_onboarding_panel">
android:orientation="horizontal"
android:elevation="3dp">
<Button <TextView
android:id="@+id/btn_back" android:layout_width="match_parent"
android:layout_width="wrap_content" android:layout_height="match_parent"
android:layout_height="wrap_content" android:layout_marginLeft="16dp"
android:layout_margin="16dp" android:layout_marginRight="16dp"
android:minWidth="145dp" android:layout_marginTop="8dp"
style="?secondaryLargeButtonStyle" android:textAppearance="@style/m3_body_small"
android:text="@string/back"/> android:textColor="?colorM3OnSurfaceVariant"
android:text="@string/signup_random_server_explain"/>
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"/>
<Button <Button
android:id="@+id/btn_next" android:id="@+id/btn_next"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="16dp" android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:minWidth="145dp" android:minWidth="145dp"
style="?primaryLargeButtonStyle" style="@style/Widget.Mastodon.M3.Button.Filled"
android:text="@string/next" /> android:text="@string/next" />
</LinearLayout> </LinearLayout>

View File

@@ -8,39 +8,24 @@
android:id="@+id/list" android:id="@+id/list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"/>
android:background="?colorBackgroundLight"/>
<LinearLayout <LinearLayout
android:id="@+id/button_bar" android:id="@+id/button_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?colorBackgroundLight" android:orientation="horizontal">
android:outlineProvider="bounds"
android:orientation="horizontal"
android:elevation="3dp">
<Button
android:id="@+id/btn_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:minWidth="145dp"
style="?secondaryLargeButtonStyle"
android:text="@string/back"/>
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"/>
<Button <Button
android:id="@+id/btn_next" android:id="@+id/btn_next"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="16dp" android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:minWidth="145dp" android:minWidth="145dp"
style="?primaryLargeButtonStyle" style="@style/Widget.Mastodon.M3.Button.Filled"
android:text="@string/i_agree" /> android:text="@string/i_agree" />
</LinearLayout> </LinearLayout>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<me.grishka.appkit.views.FragmentRootLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <me.grishka.appkit.views.FragmentRootLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@@ -8,8 +9,7 @@
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1">
android:background="?colorBackgroundLight">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -17,68 +17,58 @@
android:orientation="vertical" android:orientation="vertical"
android:clipChildren="false"> android:clipChildren="false">
<TextView <org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
android:id="@+id/title" android:id="@+id/display_name_wrap"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="80dp"
android:layout_marginBottom="24dp" android:paddingTop="4dp"
android:textAppearance="@style/m3_headline_medium" app:labelTextColor="@color/m3_outlined_text_field_label"
android:minHeight="36dp" android:foreground="@drawable/bg_m3_outlined_text_field">
android:layout_marginTop="32dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:gravity="center_vertical"
tools:text="@string/signup_title"/>
<FrameLayout
android:id="@+id/ava_wrap"
android:layout_width="88dp"
android:layout_height="88dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="24dp">
<ImageView
android:id="@+id/avatar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/default_avatar"/>
<TextView
android:layout_width="match_parent"
android:layout_height="22dp"
android:layout_gravity="bottom"
android:gravity="center"
android:background="@color/gray_800t"
android:textAppearance="@style/m3_label_large"
android:textColor="#eee"
android:text="@string/edit_photo"/>
</FrameLayout>
<EditText <EditText
android:id="@+id/display_name" android:id="@+id/display_name"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="56dp"
android:layout_marginLeft="16dp" android:layout_marginStart="56dp"
android:layout_marginRight="16dp" android:layout_marginEnd="24dp"
android:layout_marginTop="8dp"
android:padding="16dp"
android:background="@null"
android:elevation="0dp"
android:inputType="textPersonName|textCapWords" android:inputType="textPersonName|textCapWords"
android:autofillHints="name" android:autofillHints="name"
android:singleLine="true" android:singleLine="true"
android:hint="@string/display_name"/> android:hint="@string/display_name"/>
<FrameLayout <View
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="start|top"
android:layout_marginStart="16dp"
android:layout_marginTop="12dp"
android:backgroundTint="?colorM3OnSurfaceVariant"
android:background="@drawable/ic_outline_person_24"/>
</org.joinmastodon.android.ui.views.FloatingHintEditTextLayout>
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
android:id="@+id/username_wrap"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="80dp"
android:layout_marginTop="8dp" android:paddingTop="4dp"
android:layout_marginBottom="24dp" app:labelTextColor="@color/m3_outlined_text_field_label"
android:layout_marginLeft="16dp" android:foreground="@drawable/bg_m3_outlined_text_field">
android:layout_marginRight="16dp">
<EditText <EditText
android:id="@+id/username" android:id="@+id/username"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="56dp"
android:layout_marginStart="56dp"
android:layout_marginEnd="24dp"
android:layout_marginTop="8dp"
android:padding="16dp"
android:background="@null"
android:elevation="0dp"
android:inputType="textFilter|textNoSuggestions" android:inputType="textFilter|textNoSuggestions"
android:autofillHints="username" android:autofillHints="username"
android:singleLine="true" android:singleLine="true"
@@ -89,70 +79,152 @@
android:id="@+id/domain" android:id="@+id/domain"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:elevation="5dp"
android:layout_gravity="right|center_vertical" android:layout_gravity="right|center_vertical"
android:layout_marginRight="20dp"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingRight="16dp" android:paddingRight="16dp"
android:textAppearance="@style/m3_title_medium" android:textAppearance="@style/m3_body_large"
tools:text="\@mastodon.social"/> tools:text="\@mastodon.social"/>
</FrameLayout> </org.joinmastodon.android.ui.views.FloatingHintEditTextLayout>
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
android:id="@+id/email_wrap"
android:layout_width="match_parent"
android:layout_height="80dp"
android:paddingTop="4dp"
app:labelTextColor="@color/m3_outlined_text_field_label"
android:foreground="@drawable/bg_m3_outlined_text_field">
<EditText <EditText
android:id="@+id/email" android:id="@+id/email"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="56dp"
android:layout_marginLeft="16dp" android:layout_marginStart="56dp"
android:layout_marginRight="16dp" android:layout_marginEnd="24dp"
android:layout_marginBottom="8dp" android:layout_marginTop="8dp"
android:padding="16dp"
android:background="@null"
android:elevation="0dp"
android:inputType="textEmailAddress" android:inputType="textEmailAddress"
android:autofillHints="emailAddress" android:autofillHints="emailAddress"
android:singleLine="true" android:singleLine="true"
android:hint="@string/email"/> android:hint="@string/email"/>
<View
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="start|top"
android:layout_marginStart="16dp"
android:layout_marginTop="12dp"
android:backgroundTint="?colorM3OnSurfaceVariant"
android:background="@drawable/ic_outline_email_24"/>
</org.joinmastodon.android.ui.views.FloatingHintEditTextLayout>
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
android:id="@+id/password_wrap"
android:layout_width="match_parent"
android:layout_height="80dp"
android:paddingTop="4dp"
app:labelTextColor="@color/m3_outlined_text_field_label"
android:foreground="@drawable/bg_m3_outlined_text_field">
<EditText <EditText
android:id="@+id/password" android:id="@+id/password"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="56dp"
android:layout_marginLeft="16dp" android:layout_marginStart="56dp"
android:layout_marginRight="16dp" android:layout_marginEnd="24dp"
android:layout_marginTop="8dp"
android:padding="16dp"
android:background="@null"
android:elevation="0dp"
android:inputType="textPassword" android:inputType="textPassword"
android:autofillHints="password" android:autofillHints="password"
android:singleLine="true" android:singleLine="true"
android:fontFamily="sans-serif" android:fontFamily="sans-serif"
android:hint="@string/password"/> android:hint="@string/password"/>
<View
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="start|top"
android:layout_marginStart="16dp"
android:layout_marginTop="12dp"
android:backgroundTint="?colorM3OnSurfaceVariant"
android:background="@drawable/ic_outline_password_24"/>
</org.joinmastodon.android.ui.views.FloatingHintEditTextLayout>
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
android:id="@+id/password_confirm_wrap"
android:layout_width="match_parent"
android:layout_height="80dp"
android:paddingTop="4dp"
app:labelTextColor="@color/m3_outlined_text_field_label"
android:foreground="@drawable/bg_m3_outlined_text_field">
<EditText
android:id="@+id/password_confirm"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_marginStart="56dp"
android:layout_marginEnd="24dp"
android:layout_marginTop="8dp"
android:padding="16dp"
android:background="@null"
android:elevation="0dp"
android:inputType="textPassword"
android:autofillHints="password"
android:singleLine="true"
android:fontFamily="sans-serif"
android:hint="@string/confirm_password"/>
</org.joinmastodon.android.ui.views.FloatingHintEditTextLayout>
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginStart="56dp"
android:layout_marginLeft="16dp" android:layout_marginEnd="20dp"
android:layout_marginRight="16dp" android:textAppearance="@style/m3_body_small"
android:layout_marginBottom="16dp" android:textColor="?colorM3OnSurfaceVariant"
android:textAppearance="@style/m3_body_medium"
android:textColor="?android:textColorSecondary"
android:text="@string/password_note"/> android:text="@string/password_note"/>
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
android:id="@+id/reason_wrap"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="4dp"
app:labelTextColor="@color/m3_outlined_text_field_label"
android:foreground="@drawable/bg_m3_outlined_text_field">
<EditText <EditText
android:id="@+id/reason" android:id="@+id/reason"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="56dp"
android:layout_marginEnd="24dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginLeft="16dp" android:layout_marginBottom="12dp"
android:layout_marginRight="16dp" android:padding="16dp"
android:background="@null"
android:elevation="0dp"
android:inputType="textCapSentences|textMultiLine" android:inputType="textCapSentences|textMultiLine"
android:hint="@string/signup_reason"/> android:hint="@string/signup_reason"/>
</org.joinmastodon.android.ui.views.FloatingHintEditTextLayout>
<TextView <TextView
android:id="@+id/reason_explain" android:id="@+id/reason_explain"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginStart="56dp"
android:layout_marginLeft="16dp" android:layout_marginEnd="20dp"
android:layout_marginRight="16dp" android:textAppearance="@style/m3_body_small"
android:layout_marginBottom="16dp" android:textColor="?colorM3OnSurfaceVariant"
android:textAppearance="@style/m3_body_medium"
android:textColor="?android:textColorSecondary"
android:text="@string/signup_reason_note"/> android:text="@string/signup_reason_note"/>
</LinearLayout> </LinearLayout>
@@ -163,32 +235,18 @@
android:id="@+id/button_bar" android:id="@+id/button_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?colorBackgroundLight" android:orientation="horizontal">
android:outlineProvider="bounds"
android:orientation="horizontal"
android:elevation="3dp">
<Button
android:id="@+id/btn_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:minWidth="145dp"
style="?secondaryLargeButtonStyle"
android:text="@string/back"/>
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"/>
<Button <Button
android:id="@+id/btn_next" android:id="@+id/btn_next"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="16dp" android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:minWidth="145dp" android:minWidth="145dp"
style="?primaryLargeButtonStyle" style="@style/Widget.Mastodon.M3.Button.Filled"
android:text="@string/next" /> android:text="@string/next" />
</LinearLayout> </LinearLayout>

View File

@@ -85,7 +85,7 @@
<LinearLayout <LinearLayout
android:id="@+id/posts_btn" android:id="@+id/posts_btn"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="56dp" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:gravity="center_horizontal" android:gravity="center_horizontal"
@@ -112,7 +112,7 @@
<LinearLayout <LinearLayout
android:id="@+id/following_btn" android:id="@+id/following_btn"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="56dp" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:padding="4dp" android:padding="4dp"
@@ -140,7 +140,7 @@
<LinearLayout <LinearLayout
android:id="@+id/followers_btn" android:id="@+id/followers_btn"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="56dp" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:padding="4dp" android:padding="4dp"

View File

@@ -1,12 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<org.joinmastodon.android.ui.views.SizeListenerFrameLayout <org.joinmastodon.android.ui.views.SizeListenerFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
android:clipToPadding="false" android:clipToPadding="false">
android:clipChildren="false">
<View <View
android:id="@+id/blue_fill" android:id="@+id/blue_fill"
@@ -18,51 +17,60 @@
<FrameLayout <FrameLayout
android:id="@+id/art_container" android:id="@+id/art_container"
android:layout_width="450dp" android:layout_width="360dp"
android:layout_height="631dp" android:layout_height="640dp"
android:layout_marginTop="40dp" android:layout_gravity="center"
android:layout_gravity="center"> tools:ignore="rtlHardcoded">
<ImageView <ImageView
android:id="@+id/art_clouds" android:id="@+id/art_clouds"
android:layout_width="450dp" android:layout_width="414dp"
android:layout_height="589dp" android:layout_height="541dp"
android:layout_gravity="bottom|center_horizontal" android:layout_marginTop="91dp"
android:layout_gravity="top|left"
android:alpha="0.3"
android:importantForAccessibility="no"
android:src="@drawable/splash_art_layer0"/> android:src="@drawable/splash_art_layer0"/>
<ImageView
android:id="@+id/art_plane_elephant"
android:layout_width="245.64dp"
android:layout_height="72.65dp"
android:layout_marginLeft="-101.55dp"
android:layout_marginTop="238.12dp"
android:layout_gravity="left|top"
android:alpha="0.3"
android:importantForAccessibility="no"
android:src="@drawable/splash_art_layer4"/>
<ImageView <ImageView
android:id="@+id/art_right_hill" android:id="@+id/art_right_hill"
android:layout_width="218dp" android:layout_width="150.84dp"
android:layout_height="255dp" android:layout_height="176.44dp"
android:layout_gravity="bottom|right" android:layout_gravity="top|left"
android:layout_marginBottom="156dp" android:layout_marginLeft="322dp"
android:layout_marginRight="11dp" android:layout_marginTop="310dp"
android:importantForAccessibility="no"
android:src="@drawable/splash_art_layer1"/> android:src="@drawable/splash_art_layer1"/>
<ImageView <ImageView
android:id="@+id/art_left_hill" android:id="@+id/art_left_hill"
android:layout_width="285dp" android:layout_width="197.2dp"
android:layout_height="222dp" android:layout_height="153.61dp"
android:layout_gravity="bottom|left" android:layout_gravity="top|left"
android:layout_marginLeft="-6dp" android:layout_marginTop="294dp"
android:layout_marginBottom="243dp" android:importantForAccessibility="no"
android:src="@drawable/splash_art_layer2"/> android:src="@drawable/splash_art_layer2"/>
<ImageView <ImageView
android:id="@+id/art_center_hill" android:id="@+id/art_center_hill"
android:layout_width="457dp" android:layout_width="400dp"
android:layout_height="397dp" android:layout_height="346dp"
android:layout_gravity="bottom|center_horizontal" android:layout_gravity="top|left"
android:layout_marginBottom="51dp" android:layout_marginTop="294dp"
android:importantForAccessibility="no"
android:src="@drawable/splash_art_layer3"/> android:src="@drawable/splash_art_layer3"/>
<ImageView
android:id="@+id/art_plane_elephant"
android:layout_width="355dp"
android:layout_height="105dp"
android:layout_gravity="left|top"
android:src="@drawable/splash_art_layer4"/>
</FrameLayout> </FrameLayout>
<View <View
@@ -73,36 +81,66 @@
android:transformPivotY="1px" android:transformPivotY="1px"
android:background="#478E6A"/> android:background="#478E6A"/>
<org.joinmastodon.android.ui.views.SplashLogoView
android:layout_width="261dp"
android:layout_height="71dp"
android:layout_gravity="center_horizontal|top"
android:layout_marginTop="24dp"
android:scaleType="center"
android:importantForAccessibility="no"
android:src="@drawable/splash_logo"/>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_gravity="bottom"
android:padding="16dp"
android:clipToPadding="false" android:clipToPadding="false"
android:orientation="vertical"> android:orientation="vertical">
<Button
android:id="@+id/btn_get_started" <androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<LinearLayout
android:id="@+id/pager_dots"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="13dp" android:layout_gravity="center_horizontal"
style="@style/Widget.Mastodon.Button.Large.Primary_LightOnDark" android:layout_marginBottom="16dp"
android:text="@string/get_started"/> android:orientation="horizontal">
<View
android:layout_width="8dp"
android:layout_height="8dp"/>
<!-- android:background="@drawable/white_circle"/>-->
<View
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:alpha="0.3"/>
<!-- android:background="@drawable/white_circle"-->
<View
android:layout_width="8dp"
android:layout_height="8dp"
android:alpha="0.3"/>
<!-- android:background="@drawable/white_circle"-->
</LinearLayout>
<Button <Button
android:id="@+id/btn_log_in" android:id="@+id/btn_log_in"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
style="@style/Widget.Mastodon.Button.Large.Primary_DarkOnLight" android:layout_marginLeft="16dp"
android:background="@drawable/bg_button_green" android:layout_marginRight="16dp"
android:text="@string/log_in"/> style="@style/Widget.Mastodon.M3.Button.Text"
android:textColor="#fff"
android:text="@string/already_have_account"/>
<Button
android:id="@+id/btn_get_started"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
style="@style/Widget.Mastodon.M3.Button.Filled"
android:text="@string/get_started"/>
</LinearLayout> </LinearLayout>
</org.joinmastodon.android.ui.views.SizeListenerFrameLayout> </org.joinmastodon.android.ui.views.SizeListenerFrameLayout>

View File

@@ -1,62 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:layout_marginTop="32dp"
android:layout_marginLeft="19dp"
android:layout_marginRight="19dp"
android:textAppearance="@style/m3_headline_medium"
android:minHeight="36dp"
android:gravity="center_vertical"
android:text="@string/instance_catalog_title"/>
<TextView
android:id="@+id/subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="19dp"
android:layout_marginRight="19dp"
android:layout_marginBottom="24dp"
android:textAppearance="@style/m3_title_medium"
android:textColor="?android:textColorSecondary"
android:text="@string/instance_catalog_subtitle"/>
<org.joinmastodon.android.ui.tabs.TabLayout
android:id="@+id/categories_list"
android:layout_width="match_parent"
android:layout_height="72dp"
android:background="@drawable/bg_catalog_tabs"
app:tabIndicator="@drawable/mtrl_tabs_default_indicator"
app:tabIndicatorAnimationMode="elastic"
app:tabIndicatorColor="?android:textColorPrimary"
app:tabMinWidth="120dp"
app:tabMaxWidth="120dp"
app:tabMode="scrollable"/>
<EditText
android:id="@+id/search_edit"
style="@android:style/Widget.EditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="19dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="3dp"
android:drawableStart="@drawable/ic_fluent_search_20_regular"
android:drawablePadding="8dp"
android:drawableTint="?android:textColorSecondary"
android:hint="@string/search_communities"
android:imeOptions="actionGo"
android:inputType="textFilter|textNoSuggestions"
android:singleLine="true" />
</LinearLayout>

View File

@@ -18,6 +18,17 @@
android:focusable="false" android:focusable="false"
android:clickable="false"/> android:clickable="false"/>
<ImageView
android:id="@+id/image"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_marginEnd="16dp"
android:importantForAccessibility="no"
android:layout_toEndOf="@id/radiobtn"
android:scaleType="centerCrop"
android:visibility="gone"
tools:src="#0f0"/>
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/m3_body_large"
android:textColor="?colorM3OnSurface"
android:paddingStart="56dp"
android:paddingTop="12dp"
android:paddingEnd="24dp"
android:paddingBottom="12dp">
</TextView>

View File

@@ -1,44 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:elevation="3dp"
android:background="?colorBackgroundLightest"
android:foreground="?android:selectableItemBackground">
<ImageView
android:id="@+id/favicon"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginTop="2dp"
android:importantForAccessibility="no"
tools:src="#0f0"/>
<TextView
android:id="@+id/domain"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_toEndOf="@id/favicon"
android:layout_marginStart="4dp"
android:singleLine="true"
android:ellipsize="end"
android:textAppearance="@style/m3_title_small"
android:textColor="?android:textColorPrimary"
android:gravity="center_vertical"
tools:text="joinmastodon.org"/>
<TextView
android:id="@+id/title" android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="24dp" android:layout_height="wrap_content"
android:layout_below="@id/domain" android:paddingTop="12dp"
android:layout_marginTop="6dp" android:paddingEnd="24dp"
android:textAppearance="@style/m3_title_medium" android:paddingBottom="12dp"
android:singleLine="true" android:paddingStart="16dp"
android:ellipsize="end" android:textSize="16sp"
android:gravity="center_vertical" android:textColor="?colorM3Primary"
tools:text="Mastodon for Android privacy policy"/> android:drawableStart="@drawable/ic_outline_link_24"
android:drawablePadding="16dp"
android:drawableTint="?colorM3Primary"
tools:text="Privacy Policy - example.social">
</RelativeLayout> </TextView>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="12dp"
android:paddingEnd="24dp"
android:paddingBottom="12dp"
android:paddingStart="16dp"
android:baselineAligned="false">
<TextView
android:id="@+id/number"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
android:textColor="?colorM3Primary"
android:fontFamily="sans-serif-condensed"
android:textStyle="bold"
android:textSize="22dp"
android:gravity="center"
android:includeFontPadding="false"
tools:text="1"/>
<org.joinmastodon.android.ui.views.LinkedTextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:textAppearance="@style/m3_body_large"
tools:text="No discrimination, including (but not limited to) racism, sexism, homophobia or transphobia."/>
</LinearLayout>

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/m3_color" android:title="@string/sk_color_theme_material3"/>
<item android:id="@+id/purple_color" android:title="@string/sk_color_theme_purple"/> <item android:id="@+id/purple_color" android:title="@string/sk_color_theme_purple"/>
<item android:id="@+id/pink_color" android:title="@string/sk_color_theme_pink"/> <item android:id="@+id/pink_color" android:title="@string/sk_color_theme_pink"/>
<item android:id="@+id/green_color" android:title="@string/sk_color_theme_green"/> <item android:id="@+id/green_color" android:title="@string/sk_color_theme_green"/>
<item android:id="@+id/blue_color" android:title="@string/sk_color_theme_blue"/> <item android:id="@+id/blue_color" android:title="@string/sk_color_theme_blue"/>
<item android:id="@+id/orange_color" android:title="@string/sk_color_theme_brown"/> <item android:id="@+id/orange_color" android:title="@string/sk_color_theme_brown"/>
<item android:id="@+id/yellow_color" android:title="@string/sk_color_theme_yellow"/> <item android:id="@+id/yellow_color" android:title="@string/sk_color_theme_yellow"/>
<item android:id="@+id/m3_color" android:title="@string/sk_color_theme_material_you"/> <item android:id="@+id/red_color" android:title="@string/sk_color_theme_red"/>
</menu> </menu>

View File

@@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/shortcut_icon_background"/> <background android:drawable="@color/shortcut_icon_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
</adaptive-icon> </adaptive-icon>

View File

@@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/shortcut_icon_background"/> <background android:drawable="@color/shortcut_icon_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
</adaptive-icon> </adaptive-icon>

View File

@@ -56,4 +56,7 @@
<string name="sk_language_name">%s (%s)</string> <string name="sk_language_name">%s (%s)</string>
<string name="sk_confirm_clear_recent_languages">Êtes-vous sûr de vouloir effacer vos langues récemment utilisées \?</string> <string name="sk_confirm_clear_recent_languages">Êtes-vous sûr de vouloir effacer vos langues récemment utilisées \?</string>
<string name="sk_clear_recent_languages">Effacer les langues récemment utilisées</string> <string name="sk_clear_recent_languages">Effacer les langues récemment utilisées</string>
<string name="sk_example_domain">exemple.social</string>
<string name="sk_welcome_title">Bienvenue !</string>
<string name="sk_welcome_text">Le requin vous salue ! Pour commencer, veuillez entrer le nom de domaine de votre instance personnelle ci-dessous.</string>
</resources> </resources>

View File

@@ -47,4 +47,16 @@
<string name="sk_settings_show_federated_timeline">Mostra timeline federata</string> <string name="sk_settings_show_federated_timeline">Mostra timeline federata</string>
<string name="sk_disable_marquee">Disabilita scorrimento titoli</string> <string name="sk_disable_marquee">Disabilita scorrimento titoli</string>
<string name="sk_reject_follow_request">Rifiuta richiesta di seguirti</string> <string name="sk_reject_follow_request">Rifiuta richiesta di seguirti</string>
<string name="sk_translate_post">Traduci</string>
<string name="sk_translate_show_original">Mostra originale</string>
<string name="sk_translated_using">Tradotto usando %s</string>
<string name="sk_post_language">Lingua: %s</string>
<string name="sk_language_name">%s (%s)</string>
<string name="sk_confirm_clear_recent_languages">Sei sicuro di voler cancellare le lingue usate di recente\?</string>
<string name="sk_clear_recent_languages">Cancella lingue usate di recente</string>
<string name="sk_welcome_title">Benvenuto!</string>
<string name="sk_example_domain">example.social</string>
<string name="sk_poll_allow_multiple">Permetti scelta multipla</string>
<string name="sk_available_languages">Lingue disponibili</string>
<string name="sk_welcome_text">Lo squalo ti saluta! Per iniziare inserisci il dominio dell\'istanza a cui sei iscritto.</string>
</resources> </resources>

View File

@@ -56,4 +56,7 @@
<string name="sk_available_languages">모든 언어</string> <string name="sk_available_languages">모든 언어</string>
<string name="sk_clear_recent_languages">최근 사용한 언어 지우기</string> <string name="sk_clear_recent_languages">최근 사용한 언어 지우기</string>
<string name="sk_confirm_clear_recent_languages">정말로 최근 사용한 언어를 지우시겠습니까\?</string> <string name="sk_confirm_clear_recent_languages">정말로 최근 사용한 언어를 지우시겠습니까\?</string>
<string name="sk_example_domain">example.social</string>
<string name="sk_welcome_title">환영합니다!</string>
<string name="sk_welcome_text">상어가 당신을 맞이합니다! 시작하기 위해, 아래에 사용하시는 인스턴스의 도메인 이름을 입력해주세요.</string>
</resources> </resources>

View File

@@ -18,6 +18,9 @@
<style name="Theme.Mastodon.AutoLightDark.Yellow" parent="Theme.Mastodon.Dark.Yellow"/> <style name="Theme.Mastodon.AutoLightDark.Yellow" parent="Theme.Mastodon.Dark.Yellow"/>
<style name="Theme.Mastodon.AutoLightDark.TrueBlack.Yellow" parent="Theme.Mastodon.Dark.TrueBlack.Yellow"/> <style name="Theme.Mastodon.AutoLightDark.TrueBlack.Yellow" parent="Theme.Mastodon.Dark.TrueBlack.Yellow"/>
<style name="Theme.Mastodon.AutoLightDark.Red" parent="Theme.Mastodon.Dark.Red"/>
<style name="Theme.Mastodon.AutoLightDark.TrueBlack.Red" parent="Theme.Mastodon.Dark.TrueBlack.Red"/>
<style name="Theme.Mastodon.AutoLightDark.Material3" parent="Theme.Mastodon.Dark.Material3"/> <style name="Theme.Mastodon.AutoLightDark.Material3" parent="Theme.Mastodon.Dark.Material3"/>
<style name="Theme.Mastodon.AutoLightDark.TrueBlack.Material3" parent="Theme.Mastodon.Dark.TrueBlack.Material3"/> <style name="Theme.Mastodon.AutoLightDark.TrueBlack.Material3" parent="Theme.Mastodon.Dark.TrueBlack.Material3"/>
</resources> </resources>

View File

@@ -15,7 +15,7 @@
<string name="sk_mark_media_as_sensitive">Oznacz jako wrażliwe</string> <string name="sk_mark_media_as_sensitive">Oznacz jako wrażliwe</string>
<string name="sk_user_post_notifications_on">Włączono powiadomienia dla postu %s</string> <string name="sk_user_post_notifications_on">Włączono powiadomienia dla postu %s</string>
<string name="sk_user_post_notifications_off">Wyłączono powiadomienia dla postu %s</string> <string name="sk_user_post_notifications_off">Wyłączono powiadomienia dla postu %s</string>
<string name="sk_federated_timeline">Globalne</string> <string name="sk_federated_timeline">fediwersum</string>
<string name="sk_federated_timeline_info_banner">To są najnowsze post ze znanej sieci fediwersum dla twojego serwera.</string> <string name="sk_federated_timeline_info_banner">To są najnowsze post ze znanej sieci fediwersum dla twojego serwera.</string>
<string name="sk_update_available">Moshidon %s jest dostępny do pobrania.</string> <string name="sk_update_available">Moshidon %s jest dostępny do pobrania.</string>
<string name="sk_update_ready">Moshidon %s został pobrany i jest gotowy do instalacji.</string> <string name="sk_update_ready">Moshidon %s został pobrany i jest gotowy do instalacji.</string>
@@ -56,4 +56,7 @@
<string name="sk_language_name">%s (%s)</string> <string name="sk_language_name">%s (%s)</string>
<string name="sk_confirm_clear_recent_languages">Czy na pewno chcesz wyczyścić ostatnio użyte języki\?</string> <string name="sk_confirm_clear_recent_languages">Czy na pewno chcesz wyczyścić ostatnio użyte języki\?</string>
<string name="sk_clear_recent_languages">Wyczyść ostatnio użyte języki</string> <string name="sk_clear_recent_languages">Wyczyść ostatnio użyte języki</string>
<string name="sk_example_domain">przykład.social</string>
<string name="sk_welcome_title">Witaj!</string>
<string name="sk_welcome_text">Rekin się kłania! Aby zacząć, wpisz adres swojej instancji poniżej.</string>
</resources> </resources>

View File

@@ -54,4 +54,5 @@
<string name="sk_language_name">%s (%s)</string> <string name="sk_language_name">%s (%s)</string>
<string name="sk_confirm_clear_recent_languages">Tem certeza de que deseja limpar os idiomas usados recentemente\?</string> <string name="sk_confirm_clear_recent_languages">Tem certeza de que deseja limpar os idiomas usados recentemente\?</string>
<string name="sk_clear_recent_languages">Limpar idiomas usados recentemente</string> <string name="sk_clear_recent_languages">Limpar idiomas usados recentemente</string>
<string name="sk_notify_posts">Notificações de posts</string>
</resources> </resources>

View File

@@ -15,4 +15,42 @@
<string name="sk_visibility_unlisted">Скрытый</string> <string name="sk_visibility_unlisted">Скрытый</string>
<string name="sk_mark_media_as_sensitive">Отметить медиафайл как деликатный</string> <string name="sk_mark_media_as_sensitive">Отметить медиафайл как деликатный</string>
<string name="sk_settings_app_version">Moshidon v%1$s (%2$d)</string> <string name="sk_settings_app_version">Moshidon v%1$s (%2$d)</string>
<string name="sk_notification_type_status">Публикации</string>
<string name="sk_notify_posts">Отправляет публикацию</string>
<string name="sk_settings_color_picker">Цветовая схема</string>
<string name="sk_color_theme_pink">Розовый</string>
<string name="sk_color_theme_purple">Фиолетовый</string>
<string name="sk_color_theme_brown">Коричневый</string>
<string name="sk_color_theme_yellow">Жёлтый</string>
<string name="sk_translate_show_original">Показать оригинальную публикацию</string>
<string name="sk_translated_using">Переведено через %s</string>
<string name="sk_available_languages">Доступные языки</string>
<string name="sk_language_name">%s (%s)</string>
<string name="sk_welcome_title">Добро пожаловать!</string>
<string name="sk_example_domain">example.social</string>
<string name="sk_settings_load_new_posts">Автоматически загружать новые публикации</string>
<string name="sk_settings_show_replies">Показывать ответы на публикации</string>
<string name="sk_federated_timeline">Федерация</string>
<string name="sk_update_available">Moshidon %s готов к скачиванию.</string>
<string name="sk_update_ready">Moshidon %s скачан и готов к установке новой версии.</string>
<string name="sk_check_for_update">Проверить обновления</string>
<string name="sk_follow_requests">Запросы на подписку</string>
<string name="sk_lists_with_user">Списки с %s</string>
<string name="sk_confirm_unpin_post">Вы точно хотите открепить эту публикацию от профиля\?</string>
<string name="sk_unpinning">Открепление публикации…</string>
<string name="sk_federated_timeline_info_banner">Это самые последние публикации людей из вашей федерации.</string>
<string name="sk_no_update_available">Обновление отсутствует</string>
<string name="sk_list_timelines">Списки</string>
<string name="sk_accept_follow_request">Принять запрос на подписку</string>
<string name="sk_reject_follow_request">Отклонить заявку на подписку</string>
<string name="sk_settings_always_reveal_content_warnings">Всегда раскрывать предупреждения о непристойном контенте</string>
<string name="sk_settings_contribute">Внести свой код в Moshidon</string>
<string name="sk_settings_show_federated_timeline">Показывать федеративную временную шкалу</string>
<string name="sk_color_theme_green">Зелёный</string>
<string name="sk_color_theme_blue">Синий</string>
<string name="sk_translate_post">Перевести</string>
<string name="sk_post_language">Язык: %s</string>
<string name="sk_clear_recent_languages">Очистить недавно использованные языки</string>
<string name="sk_confirm_clear_recent_languages">Вы точно хотите очистить недавно использованные языки\?</string>
<string name="sk_welcome_text">Акула приветствует вас! Чтобы начать, пожалуйста, введите домен своего домашнего сервера (инстанса) ниже.</string>
</resources> </resources>

View File

@@ -56,4 +56,7 @@
<string name="sk_no_update_available">Немає доступних оновлень</string> <string name="sk_no_update_available">Немає доступних оновлень</string>
<string name="sk_list_timelines">Списки</string> <string name="sk_list_timelines">Списки</string>
<string name="sk_settings_show_federated_timeline">Показувати федеративну стрічку</string> <string name="sk_settings_show_federated_timeline">Показувати федеративну стрічку</string>
<string name="sk_example_domain">example.social</string>
<string name="sk_welcome_title">Вітаємо!</string>
<string name="sk_welcome_text">Акулка вас вітає! Щоб розпочати, введіть нижче доменне ім’я вашого інстансу.</string>
</resources> </resources>

View File

@@ -5,4 +5,5 @@
<color name="blue_navigation_bar_bg">@color/blue_gray_50</color> <color name="blue_navigation_bar_bg">@color/blue_gray_50</color>
<color name="orange_navigation_bar_bg">@color/orange_gray_50</color> <color name="orange_navigation_bar_bg">@color/orange_gray_50</color>
<color name="yellow_navigation_bar_bg">@color/yellow_gray_50</color> <color name="yellow_navigation_bar_bg">@color/yellow_gray_50</color>
<color name="red_navigation_bar_bg">@color/red_gray_50</color>
</resources> </resources>

View File

@@ -39,6 +39,10 @@
<attr name="colorM3Outline" format="color"/> <attr name="colorM3Outline" format="color"/>
<attr name="colorM3DisabledBackground" format="color"/> <attr name="colorM3DisabledBackground" format="color"/>
<attr name="colorM3PressedOverlay" format="color"/> <attr name="colorM3PressedOverlay" format="color"/>
<attr name="colorM3Error" format="color"/>
<attr name="colorM3OnError" format="color"/>
<attr name="colorM3ErrorContainer" format="color"/>
<attr name="colorM3OnErrorContainer" format="color"/>
<attr name="primaryLargeButtonStyle" format="reference"/> <attr name="primaryLargeButtonStyle" format="reference"/>
<attr name="secondaryLargeButtonStyle" format="reference"/> <attr name="secondaryLargeButtonStyle" format="reference"/>
@@ -50,5 +54,6 @@
<declare-styleable name="FloatingHintEditTextLayout"> <declare-styleable name="FloatingHintEditTextLayout">
<attr name="editTextOffsetY" format="dimension"/> <attr name="editTextOffsetY" format="dimension"/>
<attr name="android:labelTextSize" format="dimension"/> <attr name="android:labelTextSize" format="dimension"/>
<attr name="labelTextColor" format="color"/>
</declare-styleable> </declare-styleable>
</resources> </resources>

View File

@@ -91,6 +91,18 @@
<color name="yellow_primary_800">#3c2f00</color> <color name="yellow_primary_800">#3c2f00</color>
<color name="yellow_primary_900">#231b00</color> <color name="yellow_primary_900">#231b00</color>
<color name="red_primary_25">#FFFBFA</color>
<color name="red_primary_50">#FEF3F2</color>
<color name="red_primary_100">#FEE4E2</color>
<color name="red_primary_200">#FECDCA</color>
<color name="red_primary_300">#FDA29B</color>
<color name="red_primary_400">#F97066</color>
<color name="red_primary_500">#F04438</color>
<color name="red_primary_600">#D92D20</color>
<color name="red_primary_700">#B42318</color>
<color name="red_primary_800">#912018</color>
<color name="red_primary_900">#7A271A</color>
<color name="custom_gray_900">#000000</color> <color name="custom_gray_900">#000000</color>
<color name="custom_gray_800t">#cc171717</color> <color name="custom_gray_800t">#cc171717</color>
<color name="custom_gray_800">#171717</color> <color name="custom_gray_800">#171717</color>
@@ -147,6 +159,14 @@
<color name="yellow_gray_50">#fff0ca</color> <color name="yellow_gray_50">#fff0ca</color>
<color name="yellow_gray_25">#f7f8fa</color> <color name="yellow_gray_25">#f7f8fa</color>
<color name="red_gray_400">#d69f84</color>
<color name="red_gray_300">#f4ba9e</color>
<color name="red_gray_200">#ffdbcb</color>
<color name="red_gray_100">#ffedea</color>
<color name="red_gray_50t">#ffede6ca</color>
<color name="red_gray_50">#ffede6</color>
<color name="red_gray_25">#f7f8fa</color>
<color name="error_25">#FFFBFA</color> <color name="error_25">#FFFBFA</color>
<color name="error_50">#FEF3F2</color> <color name="error_50">#FEF3F2</color>
<color name="error_100">#FEE4E2</color> <color name="error_100">#FEE4E2</color>
@@ -211,6 +231,7 @@
<color name="favorite_selected">@color/warning_500</color> <color name="favorite_selected">@color/warning_500</color>
<color name="bookmark_selected">@color/success_500</color> <color name="bookmark_selected">@color/success_500</color>
<color name="boost_selected">?android:colorPrimary</color> <color name="boost_selected">?android:colorPrimary</color>
<color name="translate_selected">?android:colorPrimary</color>
<color name="shortcut_icon_background">#282C37</color> <color name="shortcut_icon_background">#282C37</color>
<!-- <color name="shortcut_icon_foreground">@color/primary_700</color>--> <!-- <color name="shortcut_icon_foreground">@color/primary_700</color>-->

View File

@@ -3,6 +3,7 @@
<string name="app_name" translatable="false">Moshidon</string> <string name="app_name" translatable="false">Moshidon</string>
<string name="get_started">Get started</string> <string name="get_started">Get started</string>
<string name="already_have_account">I already have an account</string>
<string name="log_in">Log in</string> <string name="log_in">Log in</string>
<string name="next">Next</string> <string name="next">Next</string>
<string name="loading_instance">Retrieving server info…</string> <string name="loading_instance">Retrieving server info…</string>
@@ -178,15 +179,16 @@
<string name="back">Back</string> <string name="back">Back</string>
<string name="instance_catalog_title">Mastodon is made of users on different servers.</string> <string name="instance_catalog_title">Mastodon is made of users on different servers.</string>
<string name="instance_catalog_subtitle">Pick a server based on your interests, region, or a general purpose one. You can still connect with everyone, regardless of server.</string> <string name="instance_catalog_subtitle">Pick a server based on your interests, region, or a general purpose one. You can still connect with everyone, regardless of server.</string>
<string name="search_communities">Search servers or enter URL</string> <string name="search_communities">Server name or URL</string>
<string name="instance_rules_title">Some ground rules</string> <string name="instance_rules_title">Server Rules</string>
<string name="instance_rules_subtitle">Take a minute to review the rules set and enforced by %s admins.</string> <string name="instance_rules_subtitle">By continuing, you agree to follow by the following rules set and enforced by the %s moderators.</string>
<string name="signup_title">Let\'s get you set up on %s</string> <string name="signup_title">Create Account</string>
<string name="edit_photo">edit</string> <string name="edit_photo">edit</string>
<string name="display_name">display name</string> <string name="display_name">Name</string>
<string name="username">username</string> <string name="username">Username</string>
<string name="email">email</string> <string name="email">Email</string>
<string name="password">password</string> <string name="password">Password</string>
<string name="confirm_password">Confirm password</string>
<string name="password_note">Include capital letters, special characters, and numbers to increase your password strength.</string> <string name="password_note">Include capital letters, special characters, and numbers to increase your password strength.</string>
<string name="category_academia">Academia</string> <string name="category_academia">Academia</string>
<string name="category_activism">Activism</string> <string name="category_activism">Activism</string>
@@ -201,8 +203,10 @@
<string name="category_music">Music</string> <string name="category_music">Music</string>
<string name="category_regional">Regional</string> <string name="category_regional">Regional</string>
<string name="category_tech">Tech</string> <string name="category_tech">Tech</string>
<string name="confirm_email_title">One last thing</string> <string name="confirm_email_title">Check Your Inbox</string>
<string name="confirm_email_subtitle">Tap the link we emailed to you to verify your account.</string> <!-- %s is the email address -->
<string name="confirm_email_subtitle">Tap the link we sent you to verify %s. We\'ll wait right here.</string>
<string name="confirm_email_didnt_get">Didn\'t get a link?</string>
<string name="resend">Resend</string> <string name="resend">Resend</string>
<string name="open_email_app">Open email app</string> <string name="open_email_app">Open email app</string>
<string name="resent_email">Confirmation email sent</string> <string name="resent_email">Confirmation email sent</string>
@@ -290,7 +294,7 @@
<string name="open_in_browser">Open in browser</string> <string name="open_in_browser">Open in browser</string>
<string name="hide_boosts_from_user">Hide reblogs from %s</string> <string name="hide_boosts_from_user">Hide reblogs from %s</string>
<string name="show_boosts_from_user">Show reblogs from %s</string> <string name="show_boosts_from_user">Show reblogs from %s</string>
<string name="signup_reason">why do you want to join?</string> <string name="signup_reason">Why do you want to join?</string>
<string name="signup_reason_note">This will help us review your application.</string> <string name="signup_reason_note">This will help us review your application.</string>
<string name="clear">Clear</string> <string name="clear">Clear</string>
<string name="profile_header">Header image</string> <string name="profile_header">Header image</string>
@@ -384,7 +388,7 @@
<!-- %s is file size --> <!-- %s is file size -->
<string name="download_update">Download (%s)</string> <string name="download_update">Download (%s)</string>
<string name="install_update">Install</string> <string name="install_update">Install</string>
<string name="privacy_policy_title">Mastodon and your privacy</string> <string name="privacy_policy_title">Your Privacy</string>
<string name="privacy_policy_subtitle">Although the Mastodon app does not collect any data, the server you sign up through may have a different policy. Take a minute to review and agree to the Mastodon app privacy policy and your server\'s privacy policy.</string> <string name="privacy_policy_subtitle">Although the Mastodon app does not collect any data, the server you sign up through may have a different policy. Take a minute to review and agree to the Mastodon app privacy policy and your server\'s privacy policy.</string>
<string name="i_agree">I Agree</string> <string name="i_agree">I Agree</string>
<string name="empty_list">This list is empty</string> <string name="empty_list">This list is empty</string>
@@ -408,5 +412,30 @@
<string name="login_title">Welcome Back</string> <string name="login_title">Welcome Back</string>
<string name="login_subtitle">Log in with the server where you created your account.</string> <string name="login_subtitle">Log in with the server where you created your account.</string>
<string name="server_url">Server URL</string> <string name="server_url">Server URL</string>
<string name="poll_allow_multiple">Allow multiple choices</string> <!-- {logo} is a placeholder that is replaced with the Mastodon logo image at runtime. Please copy it into your translation as is. -->
<string name="welcome_page1_title">What is {logo}?</string>
<string name="welcome_page1_text">Imagine you have an email address that ends with @example.com.\n\nYou can still send and receive emails from anyone, even if their email ends in @gmail.com or @icloud.com or @example.com.</string>
<string name="welcome_page2_title">Mastodon is like that.</string>
<string name="welcome_page2_text">Your handle might be @gothgirl654@example.social, but you can still follow, reblog, and chat with @fallout5ever@example.online.</string>
<string name="welcome_page3_title">How do I pick a server?</string>
<string name="welcome_page3_text">Different people choose different servers for any number of reasons. art.example is a great place for artists, while glasgow.example might be a good pick for Scots.\n\nYou cant go wrong with any of our recommend servers, so regardless of which one you pick (or if you enter your own in the server search bar), youll never miss a beat anywhere.</string>
<string name="signup_random_server_explain">We\'ll pick a server based on your language if you continue without making a selection.</string>
<string name="server_filter_any_language">Any Language</string>
<string name="server_filter_instant_signup">Instant Sign-up</string>
<string name="server_filter_manual_review">Manual Review</string>
<string name="server_filter_any_signup_speed">Any Sign-up Speed</string>
<string name="server_filter_region_europe">Europe</string>
<string name="server_filter_region_north_america">North America</string>
<string name="server_filter_region_south_america">South America</string>
<string name="server_filter_region_africa">Africa</string>
<string name="server_filter_region_asia">Asia</string>
<string name="server_filter_region_oceania">Oceania</string>
<string name="not_accepting_new_members">Not accepting new members</string>
<string name="category_special_interests">Special Interests</string>
<string name="signup_passwords_dont_match">Passwords don\'t match</string>
<string name="post_language">Language: %s</string>
<string name="available_languages">Available languages</string>
<string name="language_name">%s (%s)</string>
<string name="clear_recent_languages">Clear recent languages</string>
<string name="confirm_clear_recent_languages">Are you sure you want to clear your recently used languages?</string>
</resources> </resources>

View File

@@ -47,7 +47,8 @@
<string name="sk_color_theme_blue">Blue</string> <string name="sk_color_theme_blue">Blue</string>
<string name="sk_color_theme_brown">Orange</string> <string name="sk_color_theme_brown">Orange</string>
<string name="sk_color_theme_yellow">Yellow</string> <string name="sk_color_theme_yellow">Yellow</string>
<string name="sk_color_theme_material_you">Material You</string> <string name="sk_color_theme_red">Red</string>
<string name="sk_color_theme_material3">Material You</string>
<string name="sk_not_supported">Not supported on your device</string> <string name="sk_not_supported">Not supported on your device</string>
<string name="sk_poll_allow_multiple">Allow multiple choices</string> <string name="sk_poll_allow_multiple">Allow multiple choices</string>
<string name="sk_translate_post">Translate</string> <string name="sk_translate_post">Translate</string>

View File

@@ -14,16 +14,16 @@
<item name="primaryLargeButtonStyle">@style/Widget.Mastodon.Button.Large.Primary_DarkOnLight</item> <item name="primaryLargeButtonStyle">@style/Widget.Mastodon.Button.Large.Primary_DarkOnLight</item>
<item name="secondaryLargeButtonStyle">@style/Widget.Mastodon.Button.Large.Secondary_DarkOnLight</item> <item name="secondaryLargeButtonStyle">@style/Widget.Mastodon.Button.Large.Secondary_DarkOnLight</item>
<item name="android:colorAccent">@color/primary_700</item> <item name="android:colorAccent">@color/primary_700</item>
<item name="android:colorPrimary">@color/gray_800</item> <item name="android:colorPrimary">@color/custom_gray_800</item>
<item name="android:colorBackground">@color/gray_100</item> <item name="android:colorBackground">@color/gray_100</item>
<item name="android:textColorPrimary">@color/gray_800</item> <item name="android:textColorPrimary">@color/custom_gray_800</item>
<item name="android:textColorSecondary">@color/gray_500</item> <item name="android:textColorSecondary">@color/custom_gray_500</item>
<item name="colorButtonText">@color/gray_50</item> <item name="colorButtonText">@color/gray_50</item>
<item name="colorSecondary">#E9EDF2</item> <item name="colorSecondary">#E9EDF2</item>
<item name="colorBackgroundLight">@color/gray_50</item> <item name="colorBackgroundLight">@color/gray_50</item>
<item name="colorBackgroundLightest">@color/gray_25</item> <item name="colorBackgroundLightest">@color/gray_25</item>
<item name="colorBackgroundPopup">?colorBackgroundLightest</item> <item name="colorBackgroundPopup">?colorBackgroundLightest</item>
<item name="colorDarkIcon">@color/gray_900</item> <item name="colorDarkIcon">@color/custom_gray_900</item>
<item name="colorWindowBackground">@color/white</item> <item name="colorWindowBackground">@color/white</item>
<item name="android:statusBarColor">@color/gray_50</item> <item name="android:statusBarColor">@color/gray_50</item>
<item name="android:navigationBarColor">@color/navigation_bar_bg</item> <item name="android:navigationBarColor">@color/navigation_bar_bg</item>
@@ -33,10 +33,10 @@
<item name="colorPollVoted">@color/gray_300</item> <item name="colorPollVoted">@color/gray_300</item>
<item name="colorAccentLight">@color/primary_600</item> <item name="colorAccentLight">@color/primary_600</item>
<item name="colorSearchField">@color/gray_200</item> <item name="colorSearchField">@color/gray_200</item>
<item name="colorSearchHint">@color/gray_600</item> <item name="colorSearchHint">@color/custom_gray_600</item>
<item name="colorTabInactive">@color/gray_400</item> <item name="colorTabInactive">@color/gray_400</item>
<item name="colorAccentLightest">@color/primary_100</item> <item name="colorAccentLightest">@color/primary_100</item>
<item name="profileHeaderBackground">@color/gray_500</item> <item name="profileHeaderBackground">@color/custom_gray_500</item>
<item name="buttonBackground">@drawable/bg_button_primary_dark_on_light</item> <item name="buttonBackground">@drawable/bg_button_primary_dark_on_light</item>
<item name="android:editTextBackground">@drawable/bg_edittext_light</item> <item name="android:editTextBackground">@drawable/bg_edittext_light</item>
@@ -68,6 +68,10 @@
<item name="colorM3Outline">@color/m3_sys_light_outline</item> <item name="colorM3Outline">@color/m3_sys_light_outline</item>
<item name="colorM3DisabledBackground">#1F1F1F1F</item> <item name="colorM3DisabledBackground">#1F1F1F1F</item>
<item name="colorM3PressedOverlay">@color/m3_sys_light_on_primary</item> <item name="colorM3PressedOverlay">@color/m3_sys_light_on_primary</item>
<item name="colorM3Error">#B3261E</item>
<item name="colorM3OnError">#FFF</item>
<item name="colorM3ErrorContainer">#F9DEDC</item>
<item name="colorM3OnErrorContainer">#410E0B</item>
</style> </style>
<style name="Theme.Mastodon.Dark" parent="Theme.AppKit"> <style name="Theme.Mastodon.Dark" parent="Theme.AppKit">
@@ -85,29 +89,29 @@
<item name="secondaryLargeButtonStyle">@style/Widget.Mastodon.Button.Large.Secondary_LightOnDark</item> <item name="secondaryLargeButtonStyle">@style/Widget.Mastodon.Button.Large.Secondary_LightOnDark</item>
<item name="android:colorAccent">@color/primary_400</item> <item name="android:colorAccent">@color/primary_400</item>
<item name="android:colorPrimary">@color/gray_50</item> <item name="android:colorPrimary">@color/gray_50</item>
<item name="android:colorBackground">@color/gray_700</item> <item name="android:colorBackground">@color/custom_gray_700</item>
<item name="android:textColorPrimary">@color/gray_50</item> <item name="android:textColorPrimary">@color/gray_50</item>
<item name="android:textColorSecondary">@color/gray_400</item> <item name="android:textColorSecondary">@color/gray_400</item>
<item name="colorButtonText">@color/gray_800</item> <item name="colorButtonText">@color/custom_gray_800</item>
<item name="colorSecondary">#E9EDF2</item> <item name="colorSecondary">#E9EDF2</item>
<item name="colorBackgroundLight">@color/gray_700</item> <item name="colorBackgroundLight">@color/custom_gray_700</item>
<item name="colorBackgroundLightest">@color/gray_900</item> <item name="colorBackgroundLightest">@color/custom_gray_900</item>
<item name="colorBackgroundPopup">?colorBackgroundLightest</item> <item name="colorBackgroundPopup">?colorBackgroundLightest</item>
<item name="colorDarkIcon">@color/gray_25</item> <item name="colorDarkIcon">@color/gray_25</item>
<item name="colorWindowBackground">@color/gray_800</item> <item name="colorWindowBackground">@color/custom_gray_800</item>
<item name="android:statusBarColor">@color/gray_800</item> <item name="android:statusBarColor">@color/custom_gray_800</item>
<item name="android:navigationBarColor">@color/gray_800</item> <item name="android:navigationBarColor">@color/custom_gray_800</item>
<item name="android:actionBarTheme">@style/Theme.Mastodon.Toolbar.Dark</item> <item name="android:actionBarTheme">@style/Theme.Mastodon.Toolbar.Dark</item>
<item name="android:alertDialogTheme">@style/Theme.Mastodon.Dialog.Alert.Dark</item> <item name="android:alertDialogTheme">@style/Theme.Mastodon.Dialog.Alert.Dark</item>
<item name="colorPollMostVoted">@color/primary_700</item> <item name="colorPollMostVoted">@color/primary_700</item>
<item name="colorPollVoted">@color/gray_600</item> <item name="colorPollVoted">@color/custom_gray_600</item>
<item name="colorAccentLight">@color/primary_600</item> <item name="colorAccentLight">@color/primary_600</item>
<item name="colorAccentLightest">@color/primary_800</item> <item name="colorAccentLightest">@color/primary_800</item>
<item name="colorTabInactive">@color/gray_400</item> <item name="colorTabInactive">@color/gray_400</item>
<item name="profileHeaderBackground">?colorWindowBackground</item> <item name="profileHeaderBackground">?colorWindowBackground</item>
<!-- TODO dark colors --> <!-- TODO dark colors -->
<item name="colorSearchField">@color/gray_700</item> <item name="colorSearchField">@color/custom_gray_700</item>
<item name="colorSearchHint">@color/gray_300</item> <item name="colorSearchHint">@color/gray_300</item>
<item name="buttonBackground">@drawable/bg_button_primary_light_on_dark</item> <item name="buttonBackground">@drawable/bg_button_primary_light_on_dark</item>
@@ -140,6 +144,10 @@
<item name="colorM3Outline">@color/m3_sys_dark_outline</item> <item name="colorM3Outline">@color/m3_sys_dark_outline</item>
<item name="colorM3DisabledBackground">#1FE3E3E3</item> <item name="colorM3DisabledBackground">#1FE3E3E3</item>
<item name="colorM3PressedOverlay">@color/m3_sys_dark_primary</item> <item name="colorM3PressedOverlay">@color/m3_sys_dark_primary</item>
<item name="colorM3Error">#F2B8B5</item>
<item name="colorM3OnError">#601410</item>
<item name="colorM3ErrorContainer">#8C1D18</item>
<item name="colorM3OnErrorContainer">#F9DEDC</item>
</style> </style>
<style name="Theme.Mastodon.Dark.TrueBlack"> <style name="Theme.Mastodon.Dark.TrueBlack">
@@ -149,10 +157,10 @@
<item name="android:actionBarTheme">@style/Theme.Mastodon.Toolbar.Dark.TrueBlack</item> <item name="android:actionBarTheme">@style/Theme.Mastodon.Toolbar.Dark.TrueBlack</item>
<item name="colorBackgroundLight">@color/black</item> <item name="colorBackgroundLight">@color/black</item>
<item name="colorButtonText">@color/black</item> <item name="colorButtonText">@color/black</item>
<item name="colorPollVoted">@color/gray_800</item> <item name="colorPollVoted">@color/custom_gray_800</item>
<item name="colorSearchField">@color/gray_900</item> <item name="colorSearchField">@color/custom_gray_900</item>
<item name="colorBackgroundLightest">@color/black</item> <item name="colorBackgroundLightest">@color/black</item>
<item name="colorBackgroundPopup">@color/gray_900</item> <item name="colorBackgroundPopup">@color/custom_gray_900</item>
<item name="colorWindowBackground">@color/black</item> <item name="colorWindowBackground">@color/black</item>
<item name="colorM3Background">#000</item> <item name="colorM3Background">#000</item>
@@ -682,12 +690,104 @@
<item name="android:textColorSecondary">@color/custom_gray_800</item> <item name="android:textColorSecondary">@color/custom_gray_800</item>
</style> </style>
<style name="Theme.Mastodon.Light.Red" parent="Theme.Mastodon.Light.CustomBase">
<item name="android:colorAccent">@color/red_primary_700</item>
<item name="android:colorBackground">@color/red_gray_100</item>
<item name="colorButtonText">@color/red_gray_50</item>
<item name="colorBackgroundLight">@color/red_gray_50</item>
<item name="colorBackgroundLightest">@color/red_gray_25</item>
<item name="android:statusBarColor">@color/red_gray_50</item>
<item name="android:navigationBarColor">@color/red_navigation_bar_bg</item>
<item name="android:actionBarTheme">@style/Theme.Mastodon.Toolbar.Red</item>
<item name="android:alertDialogTheme">@style/Theme.Mastodon.Dialog.Alert.Red</item>
<item name="colorPollMostVoted">@color/red_primary_500</item>
<item name="colorPollVoted">@color/red_gray_300</item>
<item name="colorAccentLight">@color/red_primary_600</item>
<item name="colorSearchField">@color/red_gray_200</item>
<item name="colorTabInactive">@color/red_gray_400</item>
<item name="colorAccentLightest">@color/red_primary_100</item>
<item name="colorSecondary">@color/red_gray_50</item>
</style>
<style name="Theme.Mastodon.Dark.Red" parent="Theme.Mastodon.Dark.CustomBase">
<item name="android:colorAccent">@color/red_primary_400</item>
<item name="android:colorPrimary">@color/red_gray_50</item>
<item name="android:textColorPrimary">@color/red_gray_50</item>
<item name="android:textColorSecondary">@color/red_gray_400</item>
<item name="android:alertDialogTheme">@style/Theme.Mastodon.Dialog.Alert.Dark.Red</item>
<item name="colorPollMostVoted">@color/red_primary_700</item>
<item name="colorAccentLight">@color/red_primary_600</item>
<item name="colorAccentLightest">@color/red_primary_800</item>
<item name="colorTabInactive">@color/red_gray_400</item>
<item name="colorSearchHint">@color/red_gray_300</item>
<item name="colorSecondary">@color/red_gray_50</item>
</style>
<style name="Theme.Mastodon.Dark.TrueBlack.Red" parent="Theme.Mastodon.Dark.Red">
<item name="android:colorAccent">@color/red_primary_400</item>
<item name="colorPollMostVoted">@color/red_primary_700</item>
<item name="colorAccentLight">@color/red_primary_600</item>
<item name="colorAccentLightest">@color/red_primary_800</item>
<item name="colorSecondary">@color/red_gray_50</item>
<item name="colorPollVoted">@color/custom_gray_800</item>
<item name="colorSearchField">@color/custom_gray_900</item>
<item name="colorBackgroundPopup">@color/custom_gray_900</item>
<item name="android:navigationBarColor">@color/black</item>
<item name="android:colorBackground">@color/black</item>
<item name="android:statusBarColor">@color/black</item>
<item name="android:actionBarTheme">@style/Theme.Mastodon.Toolbar.Dark.TrueBlack</item>
<item name="colorBackgroundLight">@color/black</item>
<item name="colorWindowBackground">@color/black</item>
<item name="colorButtonText">@color/black</item>
<item name="colorBackgroundLightest">@color/black</item>
</style>
<style name="Theme.Mastodon.AutoLightDark.Red" parent="Theme.Mastodon.Light.Red"/>
<style name="Theme.Mastodon.AutoLightDark.TrueBlack.Red" parent="Theme.Mastodon.Light.Red"/>
<style name="Theme.Mastodon.Dialog.Alert.Dark.Red" parent="android:Theme.Material.Dialog.Alert">
<item name="android:windowTitleStyle">@style/alert_title</item>
<item name="android:dialogPreferredPadding">24dp</item>
<item name="android:windowBackground">@drawable/bg_alert</item>
<item name="android:buttonBarButtonStyle">@style/Widget.Mastodon.ButtonBarButton</item>
<!-- colors -->
<item name="android:colorAccent">@color/red_primary_600</item>
<item name="android:colorPrimary">@color/red_gray_50</item>
<item name="android:colorBackground">@color/custom_gray_700</item>
<item name="android:textColorPrimary">@color/red_gray_50</item>
<item name="android:textColorSecondary">@color/red_gray_400</item>
</style>
<style name="Theme.Mastodon.Dialog.Alert.Red" parent="android:Theme.Material.Light.Dialog.Alert">
<item name="android:windowTitleStyle">@style/alert_title</item>
<item name="android:dialogPreferredPadding">24dp</item>
<item name="android:windowBackground">@drawable/bg_alert</item>
<item name="android:buttonBarButtonStyle">@style/Widget.Mastodon.ButtonBarButton</item>
<!-- colors -->
<item name="android:colorAccent">@color/red_primary_700</item>
<item name="android:colorPrimary">@color/custom_gray_800</item>
<item name="android:colorBackground">@color/red_gray_100</item>
<item name="android:textColorPrimary">@color/custom_gray_800</item>
<item name="android:textColorSecondary">@color/custom_gray_500</item>
</style>
<style name="Theme.Mastodon.Toolbar.Red" parent="android:ThemeOverlay.Material.ActionBar">
<item name="android:colorPrimary">@color/red_gray_50</item>
<item name="android:textColorPrimary">@color/custom_gray_800</item>
<item name="android:textColorSecondary">@color/custom_gray_800</item>
</style>
<style name="Theme.Mastodon.Dark.Original" parent="Theme.Mastodon.Dark"> <style name="Theme.Mastodon.Dark.Original" parent="Theme.Mastodon.Dark">
<item name="android:colorAccent">@color/original_primary_400</item> <item name="android:colorAccent">@color/original_primary_400</item>
<item name="colorPollMostVoted">@color/original_primary_700</item> <item name="colorPollMostVoted">@color/original_primary_700</item>
<item name="colorAccentLight">@color/original_primary_600</item> <item name="colorAccentLight">@color/original_primary_600</item>
<item name="colorAccentLightest">@color/original_primary_800</item> <item name="colorAccentLightest">@color/original_primary_800</item>
<item name="android:alertDialogTheme">@style/Theme.Mastodon.Dialog.Alert.Dark.Original</item>
</style> </style>
<style name="Theme.Mastodon.Dark.TrueBlack.Original" parent="Theme.Mastodon.Dark.TrueBlack"> <style name="Theme.Mastodon.Dark.TrueBlack.Original" parent="Theme.Mastodon.Dark.TrueBlack">
@@ -695,6 +795,7 @@
<item name="colorPollMostVoted">@color/original_primary_700</item> <item name="colorPollMostVoted">@color/original_primary_700</item>
<item name="colorAccentLight">@color/original_primary_600</item> <item name="colorAccentLight">@color/original_primary_600</item>
<item name="colorAccentLightest">@color/original_primary_800</item> <item name="colorAccentLightest">@color/original_primary_800</item>
<item name="android:alertDialogTheme">@style/Theme.Mastodon.Dialog.Alert.Dark.Original</item>
</style> </style>
<style name="Theme.Mastodon.Light.Original" parent="Theme.Mastodon.Light"> <style name="Theme.Mastodon.Light.Original" parent="Theme.Mastodon.Light">
@@ -702,20 +803,48 @@
<item name="colorPollMostVoted">@color/original_primary_700</item> <item name="colorPollMostVoted">@color/original_primary_700</item>
<item name="colorAccentLight">@color/original_primary_600</item> <item name="colorAccentLight">@color/original_primary_600</item>
<item name="colorAccentLightest">@color/original_primary_800</item> <item name="colorAccentLightest">@color/original_primary_800</item>
<item name="android:alertDialogTheme">@style/Theme.Mastodon.Dialog.Alert.Original</item>
</style> </style>
<style name="Theme.Mastodon.AutoLightDark.Original" parent="Theme.Mastodon.Light.Original"/> <style name="Theme.Mastodon.AutoLightDark.Original" parent="Theme.Mastodon.Light.Original"/>
<style name="Theme.Mastodon.AutoLightDark.TrueBlack.Original" parent="Theme.Mastodon.Light.Original"/> <style name="Theme.Mastodon.AutoLightDark.TrueBlack.Original" parent="Theme.Mastodon.Light.Original"/>
<style name="Theme.Mastodon.Dialog.Alert.Dark.Original" parent="android:Theme.Material.Dialog.Alert">
<item name="android:windowTitleStyle">@style/alert_title</item>
<item name="android:dialogPreferredPadding">24dp</item>
<item name="android:windowBackground">@drawable/bg_alert</item>
<item name="android:buttonBarButtonStyle">@style/Widget.Mastodon.ButtonBarButton</item>
<!-- colors -->
<item name="android:colorAccent">@color/original_primary_600</item>
<item name="android:colorPrimary">@color/gray_50</item>
<item name="android:colorBackground">@color/custom_gray_700</item>
<item name="android:textColorPrimary">@color/gray_50</item>
<item name="android:textColorSecondary">@color/gray_400</item>
</style>
<style name="Theme.Mastodon.Dialog.Alert.Original" parent="android:Theme.Material.Light.Dialog.Alert">
<item name="android:windowTitleStyle">@style/alert_title</item>
<item name="android:dialogPreferredPadding">24dp</item>
<item name="android:windowBackground">@drawable/bg_alert</item>
<item name="android:buttonBarButtonStyle">@style/Widget.Mastodon.ButtonBarButton</item>
<!-- colors -->
<item name="android:colorAccent">@color/original_primary_700</item>
<item name="android:colorPrimary">@color/custom_gray_800</item>
<item name="android:colorBackground">@color/gray_100</item>
<item name="android:textColorPrimary">@color/custom_gray_800</item>
<item name="android:textColorSecondary">@color/custom_gray_500</item>
</style>
<style name="Theme.Mastodon.Toolbar" parent="android:ThemeOverlay.Material.ActionBar"> <style name="Theme.Mastodon.Toolbar" parent="android:ThemeOverlay.Material.ActionBar">
<item name="android:colorPrimary">@color/gray_50</item> <item name="android:colorPrimary">@color/gray_50</item>
<item name="android:textColorPrimary">@color/gray_800</item> <item name="android:textColorPrimary">@color/custom_gray_800</item>
<item name="android:textColorSecondary">@color/gray_800</item> <item name="android:textColorSecondary">@color/custom_gray_800</item>
</style> </style>
<style name="Theme.Mastodon.Toolbar.Dark" parent="android:ThemeOverlay.Material.Dark.ActionBar"> <style name="Theme.Mastodon.Toolbar.Dark" parent="android:ThemeOverlay.Material.Dark.ActionBar">
<item name="android:colorPrimary">@color/gray_800</item> <item name="android:colorPrimary">@color/custom_gray_800</item>
<item name="android:textColorPrimary">@color/gray_50</item> <item name="android:textColorPrimary">@color/gray_50</item>
<item name="android:textColorSecondary">@color/gray_50</item> <item name="android:textColorSecondary">@color/gray_50</item>
</style> </style>
@@ -817,10 +946,10 @@
<!-- colors --> <!-- colors -->
<item name="android:colorAccent">@color/primary_700</item> <item name="android:colorAccent">@color/primary_700</item>
<item name="android:colorPrimary">@color/gray_800</item> <item name="android:colorPrimary">@color/custom_gray_800</item>
<item name="android:colorBackground">@color/gray_100</item> <item name="android:colorBackground">@color/gray_100</item>
<item name="android:textColorPrimary">@color/gray_800</item> <item name="android:textColorPrimary">@color/custom_gray_800</item>
<item name="android:textColorSecondary">@color/gray_500</item> <item name="android:textColorSecondary">@color/custom_gray_500</item>
</style> </style>
<style name="Theme.Mastodon.Dialog.Alert.Dark" parent="android:Theme.Material.Dialog.Alert"> <style name="Theme.Mastodon.Dialog.Alert.Dark" parent="android:Theme.Material.Dialog.Alert">
@@ -832,7 +961,7 @@
<!-- colors --> <!-- colors -->
<item name="android:colorAccent">@color/primary_600</item> <item name="android:colorAccent">@color/primary_600</item>
<item name="android:colorPrimary">@color/gray_50</item> <item name="android:colorPrimary">@color/gray_50</item>
<item name="android:colorBackground">@color/gray_700</item> <item name="android:colorBackground">@color/custom_gray_700</item>
<item name="android:textColorPrimary">@color/gray_50</item> <item name="android:textColorPrimary">@color/gray_50</item>
<item name="android:textColorSecondary">@color/gray_400</item> <item name="android:textColorSecondary">@color/gray_400</item>
</style> </style>
@@ -866,6 +995,13 @@
<item name="android:paddingRight">24dp</item> <item name="android:paddingRight">24dp</item>
</style> </style>
<style name="Widget.Mastodon.M3.Button.Text">
<item name="android:background">@drawable/bg_button_m3_text</item>
<item name="android:textColor">@color/button_text_m3_text</item>
<item name="android:paddingLeft">24dp</item>
<item name="android:paddingRight">24dp</item>
</style>
<style name="alert_title"> <style name="alert_title">
<item name="android:textColor">?android:textColorPrimary</item> <item name="android:textColor">?android:textColorPrimary</item>
<item name="android:textSize">24dp</item> <item name="android:textSize">24dp</item>
@@ -885,6 +1021,12 @@
<item name="android:lineSpacingExtra">4dp</item> <item name="android:lineSpacingExtra">4dp</item>
</style> </style>
<style name="m3_body_small">
<item name="android:textSize">12dp</item>
<item name="android:textColor">?android:textColorSecondary</item>
<item name="android:lineSpacingExtra">2dp</item>
</style>
<style name="m3_title_medium"> <style name="m3_title_medium">
<item name="android:fontFamily">sans-serif-medium</item> <item name="android:fontFamily">sans-serif-medium</item>
<item name="android:textSize">16dp</item> <item name="android:textSize">16dp</item>

View File

@@ -1,10 +1,10 @@
Megalodon은 <a href="https://github.com/mastodon/mastodon-android">공식 마스토돈 안드로이드 앱</a>의 수정된 버전으로, 공식 앱에서 누락된 연합 타임라인, 타임라인에 비표시하여 게시, 이미지 설명 표시와 같은 중요한 기능들을 추가했습니다. Megalodon은 <a href="https://github.com/mastodon/mastodon-android">공식 마스토돈 안드로이드 앱</a>의 수정된 버전으로, 공식 앱에서 누락된 연합 타임라인, 타임라인에 비표시하여 게시, 이미지 설명 뷰어와 같은 중요한 기능들을 추가했습니다.
<b>주요 기능</b> <b>주요 기능</b>
- <b>타임라인에 비표시하여 게시</b>: 모두가 볼 수 있지만 트렌드, 해시태그 및 공개된 타임라인에 보이지 않습니다. - <b>타임라인에 비표시하여 게시</b>: 모두가 볼 수 있지만 트렌드, 해시태그 및 공개된 타임라인에 보이지 않습니다.
- <b>연합 타임라인</b>: 사용 중인 인스턴스에 연결된 연합우주의 사람들이 올린 모든 공개된 게시물을 확인하세요. - <b>연합 타임라인</b>: 사용 중인 인스턴스에 연결된 연합우주의 사람들이 올린 모든 공개된 게시물을 확인하세요.
- <b>이미지 설명 표시</b>: 이미지나 동영상에 대체 텍스트가 있는지 빠르게 확인하세요. - <b>이미지 설명 뷰어</b>: 이미지나 동영상에 대체 텍스트가 있는지 빠르게 확인하세요.
- <b>게시물 고정</b>: 가장 중요한 게시물을 프로필에 고정하고 “고정됨” 탭으로 다른 사용자가 고정한 게시물을 확인하세요. - <b>게시물 고정</b>: 가장 중요한 게시물을 프로필에 고정하고 “고정됨” 탭으로 다른 사용자가 고정한 게시물을 확인하세요.
- <b>해시태그 팔로우</b>: 특정 해시태그를 팔로우하고 해시태그가 포함된 새로운 게시물을 홈 타임라인에서 바로 확인하세요. - <b>해시태그 팔로우</b>: 특정 해시태그를 팔로우하고 해시태그가 포함된 새로운 게시물을 홈 타임라인에서 바로 확인하세요.
- <b>팔로우 요청에 응답</b>: 알림 또는 전용 팔로우 요청 목록을 통해 팔로우 요청을 허가하거나 거부하세요. - <b>팔로우 요청에 응답</b>: 알림 또는 전용 팔로우 요청 목록을 통해 팔로우 요청을 허가하거나 거부하세요.

Some files were not shown because too many files have changed in this diff Show More