Merge remote-tracking branch 'megalodon_main/main'
# Conflicts: # mastodon/build.gradle # mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java # mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java # mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java # mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java
This commit is contained in:
@@ -46,6 +46,7 @@ public class GlobalUserPreferences{
|
||||
public static boolean bottomEncoding;
|
||||
public static boolean collapseLongPosts;
|
||||
public static boolean spectatorMode;
|
||||
public static boolean autoHideFab;
|
||||
public static String publishButtonText;
|
||||
public static ThemePreference theme;
|
||||
public static ColorPreference color;
|
||||
@@ -99,6 +100,7 @@ public class GlobalUserPreferences{
|
||||
bottomEncoding=prefs.getBoolean("bottomEncoding", false);
|
||||
collapseLongPosts=prefs.getBoolean("collapseLongPosts", true);
|
||||
spectatorMode=prefs.getBoolean("spectatorMode", false);
|
||||
autoHideFab=prefs.getBoolean("autoHideFab", true);
|
||||
publishButtonText=prefs.getString("publishButtonText", "");
|
||||
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
||||
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
||||
@@ -147,6 +149,7 @@ public class GlobalUserPreferences{
|
||||
.putBoolean("prefixRepliesWithRe", prefixRepliesWithRe)
|
||||
.putBoolean("collapseLongPosts", collapseLongPosts)
|
||||
.putBoolean("spectatorMode", spectatorMode)
|
||||
.putBoolean("autoHideFab", autoHideFab)
|
||||
.putString("publishButtonText", publishButtonText)
|
||||
.putBoolean("bottomEncoding", bottomEncoding)
|
||||
.putInt("theme", theme.ordinal())
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewOutlineProvider;
|
||||
@@ -361,6 +362,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
});
|
||||
|
||||
actionButton.setOnClickListener(this::onActionButtonClick);
|
||||
actionButton.setOnLongClickListener(this::onActionButtonLongClick);
|
||||
notifyButton.setOnClickListener(this::onNotifyButtonClick);
|
||||
avatar.setOnClickListener(this::onAvatarClick);
|
||||
cover.setOnClickListener(this::onCoverClick);
|
||||
@@ -576,8 +578,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
for (Account.Role role : account.roles) {
|
||||
TextView roleText = new TextView(getActivity(), null, 0, R.style.role_label);
|
||||
roleText.setText(role.name);
|
||||
GradientDrawable bg = (GradientDrawable) roleText.getBackground().mutate();
|
||||
bg.setStroke(V.dp(2), Color.parseColor(role.color));
|
||||
if (!TextUtils.isEmpty(role.color) && role.color.startsWith("#")) try {
|
||||
GradientDrawable bg = (GradientDrawable) roleText.getBackground().mutate();
|
||||
bg.setStroke(V.dp(2), Color.parseColor(role.color));
|
||||
} catch (Exception ignored) {}
|
||||
rolesView.addView(roleText);
|
||||
}
|
||||
}
|
||||
@@ -706,6 +710,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
}else{
|
||||
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.bookmarks, R.id.followed_hashtags, R.id.favorites, R.id.scheduled);
|
||||
}
|
||||
boolean hasMultipleAccounts = AccountSessionManager.getInstance().getLoggedInAccounts().size() > 1;
|
||||
MenuItem openWithAccounts = menu.findItem(R.id.open_with_account);
|
||||
openWithAccounts.setVisible(hasMultipleAccounts);
|
||||
SubMenu accountsMenu = openWithAccounts.getSubMenu();
|
||||
if (hasMultipleAccounts) {
|
||||
accountsMenu.clear();
|
||||
UiUtils.populateAccountsMenu(accountID, accountsMenu, s-> UiUtils.openURL(
|
||||
getActivity(), s.getID(), account.url, false
|
||||
));
|
||||
}
|
||||
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getShortUsername()));
|
||||
if(isOwnProfile)
|
||||
return;
|
||||
@@ -880,33 +894,34 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
currentPhotoViewer.offsetView(0, oldScrollY-scrollY);
|
||||
}
|
||||
|
||||
int dy = scrollY - oldScrollY;
|
||||
|
||||
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
|
||||
TranslateAnimation animate = new TranslateAnimation(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
fab.getHeight() * 2);
|
||||
animate.setDuration(300);
|
||||
animate.setFillAfter(true);
|
||||
fab.startAnimation(animate);
|
||||
fab.setVisibility(View.INVISIBLE);
|
||||
scrollDiff = 0;
|
||||
} else if (dy < 0 && fab.getVisibility() != View.VISIBLE) {
|
||||
if (scrollDiff > 400) {
|
||||
fab.setVisibility(View.VISIBLE);
|
||||
if (GlobalUserPreferences.enableFabAutoHide) {
|
||||
int dy = scrollY - oldScrollY;
|
||||
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
|
||||
TranslateAnimation animate = new TranslateAnimation(
|
||||
0,
|
||||
0,
|
||||
fab.getHeight() * 2,
|
||||
0);
|
||||
0,
|
||||
fab.getHeight() * 2);
|
||||
animate.setDuration(300);
|
||||
animate.setFillAfter(true);
|
||||
fab.startAnimation(animate);
|
||||
fab.setVisibility(View.INVISIBLE);
|
||||
scrollDiff = 0;
|
||||
} else {
|
||||
scrollDiff += Math.abs(dy);
|
||||
} else if (dy < 0 && fab.getVisibility() != View.VISIBLE) {
|
||||
if (v.getScrollY() == 0 || scrollDiff > 400) {
|
||||
fab.setVisibility(View.VISIBLE);
|
||||
TranslateAnimation animate = new TranslateAnimation(
|
||||
0,
|
||||
0,
|
||||
fab.getHeight() * 2,
|
||||
0);
|
||||
animate.setDuration(300);
|
||||
animate.setFillAfter(true);
|
||||
fab.startAnimation(animate);
|
||||
scrollDiff = 0;
|
||||
} else {
|
||||
scrollDiff += Math.abs(dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -937,6 +952,31 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onActionButtonLongClick(View v) {
|
||||
if (isOwnProfile || AccountSessionManager.getInstance().getLoggedInAccounts().size() < 2) return false;
|
||||
UiUtils.pickAccount(getActivity(), accountID, R.string.sk_follow_as, R.drawable.ic_fluent_person_add_28_regular, session -> {
|
||||
UiUtils.lookupAccount(getActivity(), account, session.getID(), accountID, acc -> {
|
||||
if (acc == null) return;
|
||||
new SetAccountFollowed(acc.id, true, true).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(Relationship relationship) {
|
||||
Toast.makeText(
|
||||
getActivity(),
|
||||
getString(R.string.sk_followed_as, session.self.getShortUsername()),
|
||||
Toast.LENGTH_SHORT
|
||||
).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error) {
|
||||
error.showToast(getActivity());
|
||||
}
|
||||
}).exec(session.getID());
|
||||
});
|
||||
}, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setActionProgressVisible(boolean visible){
|
||||
actionButton.setTextVisible(!visible);
|
||||
actionProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
|
||||
@@ -14,7 +14,7 @@ import java.util.List;
|
||||
* Represents a user of Mastodon and their associated profile.
|
||||
*/
|
||||
@Parcel
|
||||
public class Account extends BaseModel{
|
||||
public class Account extends BaseModel implements Searchable{
|
||||
// Base attributes
|
||||
|
||||
/**
|
||||
@@ -135,6 +135,11 @@ public class Account extends BaseModel{
|
||||
|
||||
public List<Role> roles;
|
||||
|
||||
@Override
|
||||
public String getQuery() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Parcel
|
||||
public static class Role {
|
||||
public String name;
|
||||
|
||||
@@ -121,7 +121,7 @@ public class Instance extends BaseModel{
|
||||
ci.domain=uri;
|
||||
ci.normalizedDomain=IDN.toUnicode(uri);
|
||||
ci.description=Html.fromHtml(shortDescription).toString().trim();
|
||||
if(languages!=null){
|
||||
if(languages!=null && languages.size() > 0){
|
||||
ci.language=languages.get(0);
|
||||
ci.languages=languages;
|
||||
}else{
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
public interface Searchable {
|
||||
String getQuery();
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
@Parcel
|
||||
public class Status extends BaseModel implements DisplayItemsParent{
|
||||
public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
||||
@RequiredField
|
||||
public String id;
|
||||
@RequiredField
|
||||
@@ -163,4 +163,9 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||
s.filtered = List.of();
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQuery() {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +205,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
||||
String accountID = session.getID();
|
||||
args.putString("account", accountID);
|
||||
UiUtils.lookupStatus(v.getContext(), item.status, accountID, item.accountID, status -> {
|
||||
if (status == null) return;
|
||||
args.putParcelable("replyTo", Parcels.wrap(status));
|
||||
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
|
||||
});
|
||||
|
||||
@@ -481,7 +481,9 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||
if (hasMultipleAccounts && accountsMenu != null) {
|
||||
openWithAccounts.setVisible(true);
|
||||
accountsMenu.clear();
|
||||
populateAccountsMenu(accountsMenu);
|
||||
UiUtils.populateAccountsMenu(item.accountID, accountsMenu, s-> UiUtils.openURL(
|
||||
item.parentFragment.getActivity(), s.getID(), item.status.url, false
|
||||
));
|
||||
} else if (openWithAccounts != null) {
|
||||
openWithAccounts.setVisible(false);
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ import org.joinmastodon.android.model.Notification;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
import org.joinmastodon.android.model.ScheduledStatus;
|
||||
import org.joinmastodon.android.model.SearchResults;
|
||||
import org.joinmastodon.android.model.Searchable;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.StatusPrivacy;
|
||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||
@@ -107,8 +108,10 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -978,6 +981,8 @@ public class UiUtils{
|
||||
public static void pickInteractAs(Context context, String accountID, Status sourceStatus, Predicate<Status> checkInteracted, InteractionPerformer interactionPerformer, @StringRes int interactAsRes, @StringRes int interactedAsAccountRes, @StringRes int alreadyInteractedRes, @DrawableRes int iconRes) {
|
||||
pickAccount(context, accountID, interactAsRes, iconRes, session -> {
|
||||
lookupStatus(context, sourceStatus, session.getID(), accountID, status -> {
|
||||
if (status == null) return;
|
||||
|
||||
if (checkInteracted.test(status)) {
|
||||
Toast.makeText(context, alreadyInteractedRes, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
@@ -993,17 +998,33 @@ public class UiUtils{
|
||||
}, null);
|
||||
}
|
||||
|
||||
public static void lookupStatus(Context context, Status queryStatus, String targetAccountID, @Nullable String sourceAccountID, Consumer<Status> statusConsumer) {
|
||||
public static void lookupStatus(Context context, Status queryStatus, String targetAccountID, @Nullable String sourceAccountID, Consumer<Status> resultConsumer) {
|
||||
lookup(context, queryStatus, targetAccountID, sourceAccountID, GetSearchResults.Type.STATUSES, resultConsumer, results ->
|
||||
!results.statuses.isEmpty() ? Optional.of(results.statuses.get(0)) : Optional.empty()
|
||||
);
|
||||
}
|
||||
|
||||
public static void lookupAccount(Context context, Account queryAccount, String targetAccountID, @Nullable String sourceAccountID, Consumer<Account> resultConsumer) {
|
||||
lookup(context, queryAccount, targetAccountID, sourceAccountID, GetSearchResults.Type.ACCOUNTS, resultConsumer, results ->
|
||||
!results.accounts.isEmpty() ? Optional.of(results.accounts.get(0)) : Optional.empty()
|
||||
);
|
||||
}
|
||||
|
||||
public static <T extends Searchable> void lookup(Context context, T query, String targetAccountID, @Nullable String sourceAccountID, @Nullable GetSearchResults.Type type, Consumer<T> resultConsumer, Function<SearchResults, Optional<T>> extractResult) {
|
||||
if (sourceAccountID != null && targetAccountID.startsWith(sourceAccountID.substring(0, sourceAccountID.indexOf('_')))) {
|
||||
statusConsumer.accept(queryStatus);
|
||||
resultConsumer.accept(query);
|
||||
return;
|
||||
}
|
||||
|
||||
new GetSearchResults(queryStatus.url, GetSearchResults.Type.STATUSES, true).setCallback(new Callback<>() {
|
||||
new GetSearchResults(query.getQuery(), type, true).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(SearchResults results) {
|
||||
if (!results.statuses.isEmpty()) statusConsumer.accept(results.statuses.get(0));
|
||||
else Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show();
|
||||
Optional<T> result = extractResult.apply(results);
|
||||
if (result.isPresent()) resultConsumer.accept(result.get());
|
||||
else {
|
||||
Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show();
|
||||
resultConsumer.accept(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1239,6 +1260,17 @@ public class UiUtils{
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static void populateAccountsMenu(String excludeAccountID, Menu menu, Consumer<AccountSession> onClick) {
|
||||
List<AccountSession> sessions=AccountSessionManager.getInstance().getLoggedInAccounts();
|
||||
sessions.stream().filter(s -> !s.getID().equals(excludeAccountID)).forEach(s -> {
|
||||
String username = "@"+s.self.username+"@"+s.domain;
|
||||
menu.add(username).setOnMenuItemClickListener((c) -> {
|
||||
onClick.accept(s);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a View.OnClickListener to filter multiple clicks in succession.
|
||||
* Useful for buttons that perform some action that changes their state asynchronously.
|
||||
|
||||
Reference in New Issue
Block a user