implement fetching listings from remote instances
This commit is contained in:
parent
969f29e2e9
commit
4258c55b88
|
@ -48,6 +48,7 @@ public class GlobalUserPreferences{
|
||||||
public static boolean replyLineAboveHeader;
|
public static boolean replyLineAboveHeader;
|
||||||
public static boolean compactReblogReplyLine;
|
public static boolean compactReblogReplyLine;
|
||||||
public static boolean confirmBeforeReblog;
|
public static boolean confirmBeforeReblog;
|
||||||
|
public static boolean allowRemoteLoading;
|
||||||
public static String publishButtonText;
|
public static String publishButtonText;
|
||||||
public static ThemePreference theme;
|
public static ThemePreference theme;
|
||||||
public static ColorPreference color;
|
public static ColorPreference color;
|
||||||
|
@ -127,6 +128,7 @@ public class GlobalUserPreferences{
|
||||||
replyVisibility=prefs.getString("replyVisibility", null);
|
replyVisibility=prefs.getString("replyVisibility", null);
|
||||||
accountsWithContentTypesEnabled=prefs.getStringSet("accountsWithContentTypesEnabled", new HashSet<>());
|
accountsWithContentTypesEnabled=prefs.getStringSet("accountsWithContentTypesEnabled", new HashSet<>());
|
||||||
accountsDefaultContentTypes=fromJson(prefs.getString("accountsDefaultContentTypes", null), accountsDefaultContentTypesType, new HashMap<>());
|
accountsDefaultContentTypes=fromJson(prefs.getString("accountsDefaultContentTypes", null), accountsDefaultContentTypesType, new HashMap<>());
|
||||||
|
allowRemoteLoading=prefs.getBoolean("allowRemoteLoading", true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.PINK.name()));
|
color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.PINK.name()));
|
||||||
|
@ -176,6 +178,7 @@ public class GlobalUserPreferences{
|
||||||
.putString("replyVisibility", replyVisibility)
|
.putString("replyVisibility", replyVisibility)
|
||||||
.putStringSet("accountsWithContentTypesEnabled", accountsWithContentTypesEnabled)
|
.putStringSet("accountsWithContentTypesEnabled", accountsWithContentTypesEnabled)
|
||||||
.putString("accountsDefaultContentTypes", gson.toJson(accountsDefaultContentTypes))
|
.putString("accountsDefaultContentTypes", gson.toJson(accountsDefaultContentTypes))
|
||||||
|
.putBoolean("allowRemoteLoading", allowRemoteLoading)
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,11 @@ import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import androidx.annotation.CallSuper;
|
import androidx.annotation.CallSuper;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import me.grishka.appkit.api.APIRequest;
|
import me.grishka.appkit.api.APIRequest;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
|
@ -44,7 +46,7 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
|
||||||
TypeToken<T> respTypeToken;
|
TypeToken<T> respTypeToken;
|
||||||
Call okhttpCall;
|
Call okhttpCall;
|
||||||
Token token;
|
Token token;
|
||||||
boolean canceled;
|
boolean canceled, isRemote;
|
||||||
Map<String, String> headers;
|
Map<String, String> headers;
|
||||||
private ProgressDialog progressDialog;
|
private ProgressDialog progressDialog;
|
||||||
protected boolean removeUnsupportedItems;
|
protected boolean removeUnsupportedItems;
|
||||||
|
@ -101,6 +103,21 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MastodonAPIRequest<T> execRemote(String domain) {
|
||||||
|
return execRemote(domain, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MastodonAPIRequest<T> execRemote(String domain, @Nullable AccountSession remoteSession) {
|
||||||
|
this.isRemote = true;
|
||||||
|
return Optional.ofNullable(remoteSession)
|
||||||
|
.or(() -> AccountSessionManager.getInstance().getLoggedInAccounts().stream()
|
||||||
|
.filter(acc -> acc.domain.equals(domain))
|
||||||
|
.findAny())
|
||||||
|
.map(AccountSession::getID)
|
||||||
|
.map(this::exec)
|
||||||
|
.orElse(this.execNoAuth(domain));
|
||||||
|
}
|
||||||
|
|
||||||
public MastodonAPIRequest<T> wrapProgress(Activity activity, @StringRes int message, boolean cancelable){
|
public MastodonAPIRequest<T> wrapProgress(Activity activity, @StringRes int message, boolean cancelable){
|
||||||
return wrapProgress(activity, message, cancelable, null);
|
return wrapProgress(activity, message, cancelable, null);
|
||||||
}
|
}
|
||||||
|
@ -167,6 +184,7 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
|
||||||
@CallSuper
|
@CallSuper
|
||||||
public void validateAndPostprocessResponse(T respObj, Response httpResponse) throws IOException{
|
public void validateAndPostprocessResponse(T respObj, Response httpResponse) throws IOException{
|
||||||
if(respObj instanceof BaseModel){
|
if(respObj instanceof BaseModel){
|
||||||
|
((BaseModel) respObj).isRemote = isRemote;
|
||||||
((BaseModel) respObj).postprocess();
|
((BaseModel) respObj).postprocess();
|
||||||
}else if(respObj instanceof List){
|
}else if(respObj instanceof List){
|
||||||
if(removeUnsupportedItems){
|
if(removeUnsupportedItems){
|
||||||
|
@ -175,6 +193,7 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
|
||||||
Object item=itr.next();
|
Object item=itr.next();
|
||||||
if(item instanceof BaseModel){
|
if(item instanceof BaseModel){
|
||||||
try{
|
try{
|
||||||
|
((BaseModel) item).isRemote = isRemote;
|
||||||
((BaseModel) item).postprocess();
|
((BaseModel) item).postprocess();
|
||||||
}catch(ObjectValidationException x){
|
}catch(ObjectValidationException x){
|
||||||
Log.w(TAG, "Removing invalid object from list", x);
|
Log.w(TAG, "Removing invalid object from list", x);
|
||||||
|
@ -182,15 +201,20 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// no idea why we're post-processing twice, but well, as long
|
||||||
|
// as upstream does it like this, i don't wanna break anything
|
||||||
for(Object item:((List<?>) respObj)){
|
for(Object item:((List<?>) respObj)){
|
||||||
if(item instanceof BaseModel){
|
if(item instanceof BaseModel){
|
||||||
|
((BaseModel) item).isRemote = isRemote;
|
||||||
((BaseModel) item).postprocess();
|
((BaseModel) item).postprocess();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
for(Object item:((List<?>) respObj)){
|
for(Object item:((List<?>) respObj)){
|
||||||
if(item instanceof BaseModel)
|
if(item instanceof BaseModel) {
|
||||||
|
((BaseModel) item).isRemote = isRemote;
|
||||||
((BaseModel) item).postprocess();
|
((BaseModel) item).postprocess();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.joinmastodon.android.api.requests.accounts;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.Account;
|
||||||
|
|
||||||
|
public class GetAccountByHandle extends MastodonAPIRequest<Account>{
|
||||||
|
/**
|
||||||
|
* note that this method usually only returns a result if the instance already knows about an
|
||||||
|
* account - so it makes sense for looking up local users, search might be preferred otherwise
|
||||||
|
*/
|
||||||
|
public GetAccountByHandle(String acct){
|
||||||
|
super(HttpMethod.GET, "/accounts/lookup", Account.class);
|
||||||
|
addQueryParameter("acct", acct);
|
||||||
|
}
|
||||||
|
}
|
|
@ -160,6 +160,11 @@ public class AccountSessionManager{
|
||||||
return sessions.get(id);
|
return sessions.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public AccountSession tryGetAccount(Account account) {
|
||||||
|
return sessions.get(account.getDomainFromURL() + "_" + account.id);
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public AccountSession getLastActiveAccount(){
|
public AccountSession getLastActiveAccount(){
|
||||||
if(sessions.isEmpty() || lastActiveAccountID==null)
|
if(sessions.isEmpty() || lastActiveAccountID==null)
|
||||||
|
|
|
@ -45,6 +45,7 @@ import android.widget.Toolbar;
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.MastodonErrorResponse;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountByID;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountByID;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
||||||
|
@ -137,7 +138,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
private TextView followsYouView;
|
private TextView followsYouView;
|
||||||
private ViewGroup rolesView;
|
private ViewGroup rolesView;
|
||||||
|
|
||||||
private Account account;
|
private Account account, remoteAccount;
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private String domain;
|
private String domain;
|
||||||
private Relationship relationship;
|
private Relationship relationship;
|
||||||
|
@ -176,7 +177,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
|
|
||||||
accountID=getArguments().getString("account");
|
accountID=getArguments().getString("account");
|
||||||
domain=AccountSessionManager.getInstance().getAccount(accountID).domain;
|
domain=AccountSessionManager.getInstance().getAccount(accountID).domain;
|
||||||
if(getArguments().containsKey("profileAccount")){
|
if (getArguments().containsKey("remoteAccount")) {
|
||||||
|
remoteAccount = Parcels.unwrap(getArguments().getParcelable("remoteAccount"));
|
||||||
|
if(!getArguments().getBoolean("noAutoLoad", false))
|
||||||
|
loadData();
|
||||||
|
} else if(getArguments().containsKey("profileAccount")){
|
||||||
account=Parcels.unwrap(getArguments().getParcelable("profileAccount"));
|
account=Parcels.unwrap(getArguments().getParcelable("profileAccount"));
|
||||||
profileAccountID=account.id;
|
profileAccountID=account.id;
|
||||||
isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account);
|
isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||||
|
@ -347,36 +352,55 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
return sizeWrapper;
|
return sizeWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onAccountLoaded(Account result) {
|
||||||
|
account=result;
|
||||||
|
isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||||
|
bindHeaderView();
|
||||||
|
dataLoaded();
|
||||||
|
if(!tabLayoutMediator.isAttached())
|
||||||
|
tabLayoutMediator.attach();
|
||||||
|
if(!isOwnProfile)
|
||||||
|
loadRelationship();
|
||||||
|
else
|
||||||
|
AccountSessionManager.getInstance().updateAccountInfo(accountID, account);
|
||||||
|
if(refreshing){
|
||||||
|
refreshing=false;
|
||||||
|
refreshLayout.setRefreshing(false);
|
||||||
|
if(postsFragment.loaded)
|
||||||
|
postsFragment.onRefresh();
|
||||||
|
if(postsWithRepliesFragment.loaded)
|
||||||
|
postsWithRepliesFragment.onRefresh();
|
||||||
|
if(pinnedPostsFragment.loaded)
|
||||||
|
pinnedPostsFragment.onRefresh();
|
||||||
|
if(mediaFragment.loaded)
|
||||||
|
mediaFragment.onRefresh();
|
||||||
|
}
|
||||||
|
V.setVisibilityAnimated(fab, View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(){
|
protected void doLoadData(){
|
||||||
|
if (remoteAccount != null) {
|
||||||
|
UiUtils.lookupAccountHandle(getContext(), accountID, remoteAccount.getFullyQualifiedName(), (c, args) -> {
|
||||||
|
if (getContext() == null) return;
|
||||||
|
if (args == null || !args.containsKey("profileAccount")) {
|
||||||
|
onError(new MastodonErrorResponse(
|
||||||
|
getContext().getString(R.string.sk_error_loading_profile),
|
||||||
|
0, null
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onAccountLoaded(Parcels.unwrap(args.getParcelable("profileAccount")));
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
currentRequest=new GetAccountByID(profileAccountID)
|
currentRequest=new GetAccountByID(profileAccountID)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
if (getActivity() == null) return;
|
if (getActivity() == null) return;
|
||||||
account=result;
|
onAccountLoaded(result);
|
||||||
isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account);
|
|
||||||
bindHeaderView();
|
|
||||||
dataLoaded();
|
|
||||||
if(!tabLayoutMediator.isAttached())
|
|
||||||
tabLayoutMediator.attach();
|
|
||||||
if(!isOwnProfile)
|
|
||||||
loadRelationship();
|
|
||||||
else
|
|
||||||
AccountSessionManager.getInstance().updateAccountInfo(accountID, account);
|
|
||||||
if(refreshing){
|
|
||||||
refreshing=false;
|
|
||||||
refreshLayout.setRefreshing(false);
|
|
||||||
if(postsFragment.loaded)
|
|
||||||
postsFragment.onRefresh();
|
|
||||||
if(postsWithRepliesFragment.loaded)
|
|
||||||
postsWithRepliesFragment.onRefresh();
|
|
||||||
if(pinnedPostsFragment.loaded)
|
|
||||||
pinnedPostsFragment.onRefresh();
|
|
||||||
if(mediaFragment.loaded)
|
|
||||||
mediaFragment.onRefresh();
|
|
||||||
}
|
|
||||||
V.setVisibilityAnimated(fab, View.VISIBLE);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
|
@ -490,9 +514,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic, V.dp(100), V.dp(100)));
|
ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic, V.dp(100), V.dp(100)));
|
||||||
ViewImageLoader.load(cover, null, new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.header : account.headerStatic, 1000, 1000));
|
ViewImageLoader.load(cover, null, new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.header : account.headerStatic, 1000, 1000));
|
||||||
SpannableStringBuilder ssb=new SpannableStringBuilder(account.displayName);
|
SpannableStringBuilder ssb=new SpannableStringBuilder(account.displayName);
|
||||||
HtmlParser.parseCustomEmoji(ssb, account.emojis);
|
HtmlParser.parseCustomEmoji(ssb, account.emojis);
|
||||||
name.setText(ssb);
|
name.setText(ssb);
|
||||||
setTitle(ssb);
|
setTitle(ssb);
|
||||||
|
|
||||||
if (account.roles != null && !account.roles.isEmpty()) {
|
if (account.roles != null && !account.roles.isEmpty()) {
|
||||||
rolesView.setVisibility(View.VISIBLE);
|
rolesView.setVisibility(View.VISIBLE);
|
||||||
|
@ -511,13 +535,12 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
|
|
||||||
boolean isSelf=AccountSessionManager.getInstance().isSelf(accountID, account);
|
boolean isSelf=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||||
|
|
||||||
|
String acct = ((isSelf || account.isRemote)
|
||||||
|
? account.getFullyQualifiedName()
|
||||||
|
: account.acct);
|
||||||
if(account.locked){
|
if(account.locked){
|
||||||
ssb=new SpannableStringBuilder("@");
|
ssb=new SpannableStringBuilder("@");
|
||||||
ssb.append(account.acct);
|
ssb.append(acct);
|
||||||
if(isSelf){
|
|
||||||
ssb.append('@');
|
|
||||||
ssb.append(domain);
|
|
||||||
}
|
|
||||||
ssb.append(" ");
|
ssb.append(" ");
|
||||||
Drawable lock=username.getResources().getDrawable(R.drawable.ic_lock, getActivity().getTheme()).mutate();
|
Drawable lock=username.getResources().getDrawable(R.drawable.ic_lock, getActivity().getTheme()).mutate();
|
||||||
lock.setBounds(0, 0, lock.getIntrinsicWidth(), lock.getIntrinsicHeight());
|
lock.setBounds(0, 0, lock.getIntrinsicWidth(), lock.getIntrinsicHeight());
|
||||||
|
@ -526,7 +549,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
username.setText(ssb);
|
username.setText(ssb);
|
||||||
}else{
|
}else{
|
||||||
// noinspection SetTextI18n
|
// noinspection SetTextI18n
|
||||||
username.setText('@'+account.acct+(isSelf ? ('@'+domain) : ""));
|
username.setText('@'+acct);
|
||||||
}
|
}
|
||||||
CharSequence parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
CharSequence parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
||||||
if(TextUtils.isEmpty(parsedBio)){
|
if(TextUtils.isEmpty(parsedBio)){
|
||||||
|
|
|
@ -219,6 +219,11 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
||||||
GlobalUserPreferences.confirmBeforeReblog=i.checked;
|
GlobalUserPreferences.confirmBeforeReblog=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_allow_remote_loading, R.drawable.ic_fluent_communication_24_regular, GlobalUserPreferences.allowRemoteLoading, i->{
|
||||||
|
GlobalUserPreferences.allowRemoteLoading=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
items.add(new SmallTextItem(R.string.sk_settings_allow_remote_loading_explanation));
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.sk_timelines));
|
items.add(new HeaderItem(R.string.sk_timelines));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
|
items.add(new SwitchItem(R.string.sk_settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
|
||||||
|
|
|
@ -3,16 +3,27 @@ package org.joinmastodon.android.fragments.account_list;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.api.requests.accounts.GetAccountByHandle;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
public abstract class AccountRelatedAccountListFragment extends PaginatedAccountListFragment{
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public abstract class AccountRelatedAccountListFragment extends PaginatedAccountListFragment<Account> {
|
||||||
protected Account account;
|
protected Account account;
|
||||||
|
protected String initialSubtitle = "";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
account=Parcels.unwrap(getArguments().getParcelable("targetAccount"));
|
account=Parcels.unwrap(getArguments().getParcelable("targetAccount"));
|
||||||
|
if (getArguments().containsKey("remoteAccount")) {
|
||||||
|
remoteInfo = Parcels.unwrap(getArguments().getParcelable("remoteAccount"));
|
||||||
|
}
|
||||||
setTitle("@"+account.acct);
|
setTitle("@"+account.acct);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,4 +33,36 @@ public abstract class AccountRelatedAccountListFragment extends PaginatedAccount
|
||||||
? "/users/" + account.id
|
? "/users/" + account.id
|
||||||
: '@' + account.acct).build();
|
: '@' + account.acct).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRemoteDomain() {
|
||||||
|
return account.getDomainFromURL();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Account getCurrentInfo() {
|
||||||
|
return doneWithHomeInstance && remoteInfo != null ? remoteInfo : account;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MastodonAPIRequest<Account> loadRemoteInfo() {
|
||||||
|
return new GetAccountByHandle(account.acct);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AccountSession getRemoteSession() {
|
||||||
|
return Optional.ofNullable(remoteInfo)
|
||||||
|
.map(AccountSessionManager.getInstance()::tryGetAccount)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRemoteLoadingFailed() {
|
||||||
|
super.onRemoteLoadingFailed();
|
||||||
|
String prefix = initialSubtitle == null ? "" :
|
||||||
|
initialSubtitle + " " + getContext().getString(R.string.sk_separator) + " ";
|
||||||
|
String str = prefix +
|
||||||
|
getContext().getString(R.string.sk_no_remote_info_hint, getSession().domain);
|
||||||
|
setSubtitle(str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.joinmastodon.android.fragments.account_list;
|
package org.joinmastodon.android.fragments.account_list;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.app.assist.AssistContent;
|
import android.app.assist.AssistContent;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
@ -47,6 +48,7 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import androidx.annotation.CallSuper;
|
import androidx.annotation.CallSuper;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.APIRequest;
|
import me.grishka.appkit.api.APIRequest;
|
||||||
|
@ -243,10 +245,13 @@ public abstract class BaseAccountListFragment extends RecyclerFragment<BaseAccou
|
||||||
UiUtils.enablePopupMenuIcons(getActivity(), contextMenu);
|
UiUtils.enablePopupMenuIcons(getActivity(), contextMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
@Override
|
@Override
|
||||||
public void onBind(AccountItem item){
|
public void onBind(AccountItem item){
|
||||||
name.setText(item.parsedName);
|
name.setText(item.parsedName);
|
||||||
username.setText("@"+item.account.acct);
|
username.setText("@"+ (item.account.isRemote
|
||||||
|
? item.account.getFullyQualifiedName()
|
||||||
|
: item.account.acct));
|
||||||
bindRelationship();
|
bindRelationship();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,7 +287,8 @@ public abstract class BaseAccountListFragment extends RecyclerFragment<BaseAccou
|
||||||
public void onClick(){
|
public void onClick(){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putParcelable("profileAccount", Parcels.wrap(item.account));
|
if (item.account.isRemote) args.putParcelable("remoteAccount", Parcels.wrap(item.account));
|
||||||
|
else args.putParcelable("profileAccount", Parcels.wrap(item.account));
|
||||||
Nav.go(getActivity(), ProfileFragment.class, args);
|
Nav.go(getActivity(), ProfileFragment.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,5 +429,10 @@ public abstract class BaseAccountListFragment extends RecyclerFragment<BaseAccou
|
||||||
emojiHelper=new CustomEmojiHelper();
|
emojiHelper=new CustomEmojiHelper();
|
||||||
emojiHelper.setText(parsedName=HtmlParser.parseCustomEmoji(account.displayName, account.emojis));
|
emojiHelper.setText(parsedName=HtmlParser.parseCustomEmoji(account.displayName, account.emojis));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object obj) {
|
||||||
|
return obj instanceof AccountItem i && i.account.url.equals(account.url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,12 @@ public class FollowerListFragment extends AccountRelatedAccountListFragment{
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setSubtitle(getResources().getQuantityString(R.plurals.x_followers, (int)(account.followersCount%1000), account.followersCount));
|
setSubtitle(initialSubtitle = getResources().getQuantityString(R.plurals.x_followers, (int)(account.followersCount%1000), account.followersCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
||||||
return new GetAccountFollowers(account.id, maxID, count);
|
return new GetAccountFollowers(getCurrentInfo().id, maxID, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -13,12 +13,12 @@ public class FollowingListFragment extends AccountRelatedAccountListFragment{
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setSubtitle(getResources().getQuantityString(R.plurals.x_following, (int)(account.followingCount%1000), account.followingCount));
|
setSubtitle(initialSubtitle = getResources().getQuantityString(R.plurals.x_following, (int)(account.followingCount%1000), account.followingCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
||||||
return new GetAccountFollowing(account.id, maxID, count);
|
return new GetAccountFollowing(getCurrentInfo().id, maxID, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,33 +1,173 @@
|
||||||
package org.joinmastodon.android.fragments.account_list;
|
package org.joinmastodon.android.fragments.account_list;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import me.grishka.appkit.api.Callback;
|
||||||
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
public abstract class PaginatedAccountListFragment extends BaseAccountListFragment{
|
public abstract class PaginatedAccountListFragment<T> extends BaseAccountListFragment{
|
||||||
private String nextMaxID;
|
private String nextMaxID;
|
||||||
|
private MastodonAPIRequest<T> remoteInfoRequest;
|
||||||
|
protected boolean doneWithHomeInstance, remoteRequestFailed, startedRemoteLoading, remoteDisabled;
|
||||||
|
protected int localOffset;
|
||||||
|
protected T remoteInfo;
|
||||||
|
|
||||||
public abstract HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count);
|
public abstract HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count);
|
||||||
|
|
||||||
|
protected abstract MastodonAPIRequest<T> loadRemoteInfo();
|
||||||
|
public abstract T getCurrentInfo();
|
||||||
|
public abstract String getRemoteDomain();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
// already have remote info (e.g. from arguments), so no need to fetch it again
|
||||||
|
if (remoteInfo != null) {
|
||||||
|
onRemoteInfoLoaded(remoteInfo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteDisabled = !GlobalUserPreferences.allowRemoteLoading
|
||||||
|
|| getSession().domain.equals(getRemoteDomain());
|
||||||
|
if (!remoteDisabled) {
|
||||||
|
remoteInfoRequest = loadRemoteInfo().setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(T result) {
|
||||||
|
if (getContext() == null) return;
|
||||||
|
onRemoteInfoLoaded(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
if (getContext() == null) return;
|
||||||
|
onRemoteLoadingFailed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
remoteInfoRequest.execRemote(getRemoteDomain(), getRemoteSession());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* override to provide an ideal account session (e.g. if you're logged into the author's remote
|
||||||
|
* account) to make the remote request from. if null is provided, will try to get any session
|
||||||
|
* on the remote domain, or tries the request without authentication.
|
||||||
|
*/
|
||||||
|
protected AccountSession getRemoteSession() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onRemoteInfoLoaded(T info) {
|
||||||
|
this.remoteInfo = info;
|
||||||
|
this.remoteInfoRequest = null;
|
||||||
|
maybeStartLoadingRemote();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onRemoteLoadingFailed() {
|
||||||
|
this.remoteRequestFailed = true;
|
||||||
|
this.remoteInfo = null;
|
||||||
|
this.remoteInfoRequest = null;
|
||||||
|
if (doneWithHomeInstance) dataLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dataLoaded() {
|
||||||
|
super.dataLoaded();
|
||||||
|
footerProgress.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeStartLoadingRemote() {
|
||||||
|
if (startedRemoteLoading || remoteDisabled) return;
|
||||||
|
if (!remoteRequestFailed) {
|
||||||
|
if (data.size() == 0) showProgress();
|
||||||
|
else footerProgress.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
if (doneWithHomeInstance && remoteInfo != null) {
|
||||||
|
startedRemoteLoading = true;
|
||||||
|
loadData(localOffset, itemsPerPage * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRefresh() {
|
||||||
|
localOffset = 0;
|
||||||
|
doneWithHomeInstance = false;
|
||||||
|
startedRemoteLoading = false;
|
||||||
|
super.onRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadData(int offset, int count) {
|
||||||
|
// always subtract the amount loaded through the home instance once loading from remote
|
||||||
|
// since loadData gets called with data.size() (data includes both local and remote)
|
||||||
|
if (doneWithHomeInstance) offset -= localOffset;
|
||||||
|
super.loadData(offset, count);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
currentRequest=onCreateRequest(offset==0 ? null : nextMaxID, count)
|
MastodonAPIRequest<?> request = onCreateRequest(offset==0 ? null : nextMaxID, count)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Account> result){
|
public void onSuccess(HeaderPaginationList<Account> result){
|
||||||
|
boolean justRefreshed = !doneWithHomeInstance && offset == 0;
|
||||||
|
Collection<AccountItem> d = justRefreshed ? List.of() : data;
|
||||||
|
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
nextMaxID=null;
|
nextMaxID=null;
|
||||||
if (getActivity() == null) return;
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result.stream().map(AccountItem::new).collect(Collectors.toList()), nextMaxID!=null);
|
List<AccountItem> items = result.stream()
|
||||||
|
.filter(a -> d.size() > 1000 || d.stream()
|
||||||
|
.noneMatch(i -> i.account.url.equals(a.url)))
|
||||||
|
.map(AccountItem::new)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
boolean hasMore = nextMaxID != null;
|
||||||
|
|
||||||
|
if (!hasMore && !doneWithHomeInstance) {
|
||||||
|
// only runs last time data was fetched from the home instance
|
||||||
|
localOffset = d.size() + items.size();
|
||||||
|
doneWithHomeInstance = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onDataLoaded(items, hasMore);
|
||||||
|
if (doneWithHomeInstance) maybeStartLoadingRemote();
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.exec(accountID);
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
if (doneWithHomeInstance) {
|
||||||
|
onRemoteLoadingFailed();
|
||||||
|
onDataLoaded(Collections.emptyList(), false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.onError(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (doneWithHomeInstance && remoteInfo == null) return; // we are waiting
|
||||||
|
if (doneWithHomeInstance && remoteInfo != null) {
|
||||||
|
request.execRemote(getRemoteDomain(), getRemoteSession());
|
||||||
|
} else {
|
||||||
|
request.exec(accountID);
|
||||||
|
}
|
||||||
|
currentRequest = request;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -7,17 +7,23 @@ import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||||
import org.joinmastodon.android.api.requests.statuses.GetStatusFavorites;
|
import org.joinmastodon.android.api.requests.statuses.GetStatusFavorites;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
public class StatusFavoritesListFragment extends StatusRelatedAccountListFragment{
|
public class StatusFavoritesListFragment extends StatusRelatedAccountListFragment{
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
updateTitle(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateTitle(Status status) {
|
||||||
setTitle(getResources().getQuantityString(R.plurals.x_favorites, (int)(status.favouritesCount%1000), status.favouritesCount));
|
setTitle(getResources().getQuantityString(R.plurals.x_favorites, (int)(status.favouritesCount%1000), status.favouritesCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
||||||
return new GetStatusFavorites(status.id, maxID, count);
|
return new GetStatusFavorites(getCurrentInfo().id, maxID, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -7,17 +7,23 @@ import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||||
import org.joinmastodon.android.api.requests.statuses.GetStatusReblogs;
|
import org.joinmastodon.android.api.requests.statuses.GetStatusReblogs;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
public class StatusReblogsListFragment extends StatusRelatedAccountListFragment{
|
public class StatusReblogsListFragment extends StatusRelatedAccountListFragment{
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
updateTitle(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateTitle(Status status) {
|
||||||
setTitle(getResources().getQuantityString(R.plurals.x_reblogs, (int)(status.reblogsCount%1000), status.reblogsCount));
|
setTitle(getResources().getQuantityString(R.plurals.x_reblogs, (int)(status.reblogsCount%1000), status.reblogsCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
||||||
return new GetStatusReblogs(status.id, maxID, count);
|
return new GetStatusReblogs(getCurrentInfo().id, maxID, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -3,12 +3,27 @@ package org.joinmastodon.android.fragments.account_list;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.api.requests.statuses.GetStatusByID;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
public abstract class StatusRelatedAccountListFragment extends PaginatedAccountListFragment{
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public abstract class StatusRelatedAccountListFragment extends PaginatedAccountListFragment<Status> {
|
||||||
protected Status status;
|
protected Status status;
|
||||||
|
|
||||||
|
protected abstract void updateTitle(Status status);
|
||||||
|
|
||||||
|
protected MastodonAPIRequest<Status> loadRemoteInfo() {
|
||||||
|
String[] parts = status.url.split("/");
|
||||||
|
if (parts.length == 0) return null;
|
||||||
|
return new GetStatusByID(parts[parts.length - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
@ -17,7 +32,7 @@ public abstract class StatusRelatedAccountListFragment extends PaginatedAccountL
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasSubtitle(){
|
protected boolean hasSubtitle(){
|
||||||
return false;
|
return remoteRequestFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -28,4 +43,35 @@ public abstract class StatusRelatedAccountListFragment extends PaginatedAccountL
|
||||||
: '@' + status.account.acct + '/' + status.id)
|
: '@' + status.account.acct + '/' + status.id)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRemoteDomain() {
|
||||||
|
return Uri.parse(status.url).getHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status getCurrentInfo() {
|
||||||
|
return doneWithHomeInstance && remoteInfo != null ? remoteInfo : status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AccountSession getRemoteSession() {
|
||||||
|
return Optional.ofNullable(remoteInfo)
|
||||||
|
.map(s -> s.account)
|
||||||
|
.map(AccountSessionManager.getInstance()::tryGetAccount)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRemoteInfoLoaded(Status info) {
|
||||||
|
super.onRemoteInfoLoaded(info);
|
||||||
|
updateTitle(remoteInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRemoteLoadingFailed() {
|
||||||
|
super.onRemoteLoadingFailed();
|
||||||
|
setSubtitle(getContext().getString(R.string.sk_no_remote_info_hint, getSession().domain));
|
||||||
|
updateToolbar();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package org.joinmastodon.android.model;
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.joinmastodon.android.api.ObjectValidationException;
|
import org.joinmastodon.android.api.ObjectValidationException;
|
||||||
import org.joinmastodon.android.api.RequiredField;
|
import org.joinmastodon.android.api.RequiredField;
|
||||||
import org.parceler.Parcel;
|
import org.parceler.Parcel;
|
||||||
|
@ -135,6 +138,8 @@ public class Account extends BaseModel implements Searchable{
|
||||||
|
|
||||||
public List<Role> roles;
|
public List<Role> roles;
|
||||||
|
|
||||||
|
public @Nullable String fqn; // akkoma has this, mastodon't
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getQuery() {
|
public String getQuery() {
|
||||||
return url;
|
return url;
|
||||||
|
@ -162,6 +167,7 @@ public class Account extends BaseModel implements Searchable{
|
||||||
moved.postprocess();
|
moved.postprocess();
|
||||||
if(TextUtils.isEmpty(displayName))
|
if(TextUtils.isEmpty(displayName))
|
||||||
displayName=username;
|
displayName=username;
|
||||||
|
if(fqn == null) fqn = getFullyQualifiedName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLocal(){
|
public boolean isLocal(){
|
||||||
|
@ -173,6 +179,10 @@ public class Account extends BaseModel implements Searchable{
|
||||||
return parts.length==1 ? null : parts[1];
|
return parts.length==1 ? null : parts[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDomainFromURL() {
|
||||||
|
return Uri.parse(url).getHost();
|
||||||
|
}
|
||||||
|
|
||||||
public String getDisplayUsername(){
|
public String getDisplayUsername(){
|
||||||
return '@'+acct;
|
return '@'+acct;
|
||||||
}
|
}
|
||||||
|
@ -181,6 +191,10 @@ public class Account extends BaseModel implements Searchable{
|
||||||
return '@'+acct.split("@")[0];
|
return '@'+acct.split("@")[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getFullyQualifiedName() {
|
||||||
|
return fqn != null ? fqn : acct.split("@")[0] + "@" + getDomainFromURL();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(){
|
public String toString(){
|
||||||
return "Account{"+
|
return "Account{"+
|
||||||
|
|
|
@ -11,6 +11,14 @@ import androidx.annotation.CallSuper;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
public abstract class BaseModel implements Cloneable{
|
public abstract class BaseModel implements Cloneable{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* indicates the profile has been fetched from a foreign instance.
|
||||||
|
*
|
||||||
|
* @see MastodonAPIRequest#execRemote
|
||||||
|
*/
|
||||||
|
public transient boolean isRemote;
|
||||||
|
|
||||||
@CallSuper
|
@CallSuper
|
||||||
public void postprocess() throws ObjectValidationException{
|
public void postprocess() throws ObjectValidationException{
|
||||||
try{
|
try{
|
||||||
|
|
|
@ -56,6 +56,7 @@ import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
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.MastodonErrorResponse;
|
||||||
import org.joinmastodon.android.api.StatusInteractionController;
|
import org.joinmastodon.android.api.StatusInteractionController;
|
||||||
import org.joinmastodon.android.api.requests.accounts.SetAccountBlocked;
|
import org.joinmastodon.android.api.requests.accounts.SetAccountBlocked;
|
||||||
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
||||||
|
@ -1136,6 +1137,12 @@ public class UiUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void lookupAccountHandle(Context context, String accountID, String query, BiConsumer<Class<? extends Fragment>, Bundle> go) {
|
||||||
|
parseFediverseHandle(query).ifPresentOrElse(
|
||||||
|
handle -> lookupAccountHandle(context, accountID, handle, go),
|
||||||
|
() -> go.accept(null, null)
|
||||||
|
);
|
||||||
|
}
|
||||||
public static void lookupAccountHandle(Context context, String accountID, Pair<String, Optional<String>> queryHandle, BiConsumer<Class<? extends Fragment>, Bundle> go) {
|
public static void lookupAccountHandle(Context context, String accountID, Pair<String, Optional<String>> queryHandle, BiConsumer<Class<? extends Fragment>, Bundle> go) {
|
||||||
String fullHandle = ("@" + queryHandle.first) + (queryHandle.second.map(domain -> "@" + domain).orElse(""));
|
String fullHandle = ("@" + queryHandle.first) + (queryHandle.second.map(domain -> "@" + domain).orElse(""));
|
||||||
new GetSearchResults(fullHandle, GetSearchResults.Type.ACCOUNTS, true)
|
new GetSearchResults(fullHandle, GetSearchResults.Type.ACCOUNTS, true)
|
||||||
|
@ -1153,12 +1160,18 @@ public class UiUtils {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show();
|
||||||
|
args.putString("error", context.getString(R.string.sk_resource_not_found));
|
||||||
go.accept(null, null);
|
go.accept(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error) {
|
public void onError(ErrorResponse error) {
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
if (error instanceof MastodonErrorResponse e) {
|
||||||
|
args.putString("error", e.error);
|
||||||
|
args.putInt("httpStatus", e.httpStatus);
|
||||||
|
}
|
||||||
|
go.accept(null, args);
|
||||||
}
|
}
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M12 4.5c-4.694 0-8.5 3.806-8.5 8.5 0 2.345 0.948 4.466 2.484 6.005 0.293 0.293 0.293 0.768 0 1.06-0.294 0.293-0.769 0.293-1.061 0C3.118 18.256 2 15.758 2 13 2 7.477 6.477 3 12 3s10 4.477 10 10c0 2.758-1.118 5.256-2.923 7.065-0.292 0.293-0.767 0.293-1.06 0-0.293-0.292-0.294-0.767-0.001-1.06C19.552 17.467 20.5 15.345 20.5 13c0-4.694-3.806-8.5-8.5-8.5zM12 8c-2.761 0-5 2.239-5 5 0 1.382 0.56 2.632 1.466 3.537 0.293 0.293 0.293 0.768 0 1.06-0.292 0.294-0.767 0.294-1.06 0.001C6.229 16.423 5.5 14.796 5.5 13c0-3.59 2.91-6.5 6.5-6.5s6.5 2.91 6.5 6.5c0 1.796-0.73 3.423-1.906 4.598-0.293 0.293-0.768 0.293-1.06 0-0.293-0.293-0.293-0.768 0-1.06C16.44 15.631 17 14.381 17 13c0-2.761-2.239-5-5-5zm0 2.5c-1.38 0-2.5 1.12-2.5 2.5s1.12 2.5 2.5 2.5 2.5-1.12 2.5-2.5-1.12-2.5-2.5-2.5zM11 13c0-0.552 0.448-1 1-1s1 0.448 1 1-0.448 1-1 1-1-0.448-1-1z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -293,4 +293,8 @@
|
||||||
<string name="sk_open_in_app_failed">Could not open in app</string>
|
<string name="sk_open_in_app_failed">Could not open in app</string>
|
||||||
<string name="sk_external_share_title">Share with account</string>
|
<string name="sk_external_share_title">Share with account</string>
|
||||||
<string name="sk_external_share_or_open_title">Share or open with account</string>
|
<string name="sk_external_share_or_open_title">Share or open with account</string>
|
||||||
|
<string name="sk_no_remote_info_hint">remote info unavailable</string>
|
||||||
|
<string name="sk_error_loading_profile">Failed loading the profile on your home instance.</string>
|
||||||
|
<string name="sk_settings_allow_remote_loading">Load info from remote instances</string>
|
||||||
|
<string name="sk_settings_allow_remote_loading_explanation">Try fetching more accurate listings for followers, likes and boosts by loading the information from the instance of origin.</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in New Issue