Open profiles from mentions
This commit is contained in:
parent
a91538582a
commit
c1b3a4c463
|
@ -0,0 +1,10 @@
|
||||||
|
package org.joinmastodon.android.api.requests.accounts;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.Account;
|
||||||
|
|
||||||
|
public class GetAccountByID extends MastodonAPIRequest<Account>{
|
||||||
|
public GetAccountByID(String id){
|
||||||
|
super(HttpMethod.GET, "/accounts/"+id, Account.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,7 @@ import android.widget.TextView;
|
||||||
import android.widget.Toolbar;
|
import android.widget.Toolbar;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
|
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;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
||||||
|
@ -71,6 +72,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import androidx.viewpager2.widget.ViewPager2;
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
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.api.SimpleCallback;
|
||||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.fragments.LoaderFragment;
|
import me.grishka.appkit.fragments.LoaderFragment;
|
||||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||||
|
@ -100,6 +102,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
private EditText nameEdit, bioEdit;
|
private EditText nameEdit, bioEdit;
|
||||||
private ProgressBar actionProgress;
|
private ProgressBar actionProgress;
|
||||||
private FrameLayout[] tabViews;
|
private FrameLayout[] tabViews;
|
||||||
|
private TabLayoutMediator tabLayoutMediator;
|
||||||
|
|
||||||
private Account account;
|
private Account account;
|
||||||
private String accountID;
|
private String accountID;
|
||||||
|
@ -110,6 +113,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
|
|
||||||
private boolean isInEditMode;
|
private boolean isInEditMode;
|
||||||
private Uri editNewAvatar, editNewCover;
|
private Uri editNewAvatar, editNewCover;
|
||||||
|
private String profileAccountID;
|
||||||
|
|
||||||
public ProfileFragment(){
|
public ProfileFragment(){
|
||||||
super(R.layout.loader_fragment_overlay_toolbar);
|
super(R.layout.loader_fragment_overlay_toolbar);
|
||||||
|
@ -119,16 +123,25 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
accountID=getArguments().getString("account");
|
|
||||||
|
|
||||||
|
accountID=getArguments().getString("account");
|
||||||
|
if(getArguments().containsKey("profileAccount")){
|
||||||
|
account=Parcels.unwrap(getArguments().getParcelable("profileAccount"));
|
||||||
|
isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||||
|
loaded=true;
|
||||||
|
if(!isOwnProfile)
|
||||||
|
loadRelationship();
|
||||||
|
}else{
|
||||||
|
profileAccountID=getArguments().getString("profileAccountID");
|
||||||
|
if(!getArguments().getBoolean("noAutoLoad", false))
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity){
|
public void onAttach(Activity activity){
|
||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
if(!getArguments().getBoolean("noAutoLoad", false))
|
|
||||||
loadData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -196,22 +209,13 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
pager.setAdapter(new ProfilePagerAdapter());
|
pager.setAdapter(new ProfilePagerAdapter());
|
||||||
pager.getLayoutParams().height=getResources().getDisplayMetrics().heightPixels;
|
pager.getLayoutParams().height=getResources().getDisplayMetrics().heightPixels;
|
||||||
|
|
||||||
if(getArguments().containsKey("profileAccount")){
|
|
||||||
account=Parcels.unwrap(getArguments().getParcelable("profileAccount"));
|
|
||||||
isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account);
|
|
||||||
bindHeaderView();
|
|
||||||
dataLoaded();
|
|
||||||
if(!isOwnProfile)
|
|
||||||
loadRelationship();
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollView.setScrollableChildSupplier(this::getScrollableRecyclerView);
|
scrollView.setScrollableChildSupplier(this::getScrollableRecyclerView);
|
||||||
|
|
||||||
sizeWrapper.addView(content);
|
sizeWrapper.addView(content);
|
||||||
|
|
||||||
tabbar.setTabTextColors(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorSecondary), UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
|
tabbar.setTabTextColors(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorSecondary), UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
|
||||||
tabbar.setTabTextSize(V.dp(16));
|
tabbar.setTabTextSize(V.dp(16));
|
||||||
new TabLayoutMediator(tabbar, pager, new TabLayoutMediator.TabConfigurationStrategy(){
|
tabLayoutMediator=new TabLayoutMediator(tabbar, pager, new TabLayoutMediator.TabConfigurationStrategy(){
|
||||||
@Override
|
@Override
|
||||||
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position){
|
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position){
|
||||||
tab.setText(switch(position){
|
tab.setText(switch(position){
|
||||||
|
@ -222,7 +226,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
default -> throw new IllegalStateException();
|
default -> throw new IllegalStateException();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).attach();
|
});
|
||||||
|
|
||||||
cover.setForeground(coverGradient);
|
cover.setForeground(coverGradient);
|
||||||
cover.setOutlineProvider(new ViewOutlineProvider(){
|
cover.setOutlineProvider(new ViewOutlineProvider(){
|
||||||
|
@ -236,11 +240,31 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
avatar.setOnClickListener(this::onAvatarClick);
|
avatar.setOnClickListener(this::onAvatarClick);
|
||||||
cover.setOnClickListener(this::onCoverClick);
|
cover.setOnClickListener(this::onCoverClick);
|
||||||
|
|
||||||
|
if(loaded){
|
||||||
|
bindHeaderView();
|
||||||
|
dataLoaded();
|
||||||
|
tabLayoutMediator.attach();
|
||||||
|
}
|
||||||
|
|
||||||
return sizeWrapper;
|
return sizeWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(){
|
protected void doLoadData(){
|
||||||
|
currentRequest=new GetAccountByID(profileAccountID)
|
||||||
|
.setCallback(new SimpleCallback<>(this){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Account result){
|
||||||
|
account=result;
|
||||||
|
isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||||
|
bindHeaderView();
|
||||||
|
dataLoaded();
|
||||||
|
tabLayoutMediator.attach();
|
||||||
|
if(!isOwnProfile)
|
||||||
|
loadRelationship();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -250,11 +274,15 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dataLoaded(){
|
public void dataLoaded(){
|
||||||
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
|
if(postsFragment==null){
|
||||||
postsFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.DEFAULT, true);
|
postsFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.DEFAULT, true);
|
||||||
postsWithRepliesFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.INCLUDE_REPLIES, false);
|
postsWithRepliesFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.INCLUDE_REPLIES, false);
|
||||||
mediaFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.MEDIA, false);
|
mediaFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.MEDIA, false);
|
||||||
aboutFragment=new ProfileAboutFragment();
|
aboutFragment=new ProfileAboutFragment();
|
||||||
aboutFragment.setFields(fields);
|
aboutFragment.setFields(fields);
|
||||||
|
}
|
||||||
pager.getAdapter().notifyDataSetChanged();
|
pager.getAdapter().notifyDataSetChanged();
|
||||||
super.dataLoaded();
|
super.dataLoaded();
|
||||||
}
|
}
|
||||||
|
@ -322,7 +350,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
name.setText(ssb);
|
name.setText(ssb);
|
||||||
setTitle(ssb);
|
setTitle(ssb);
|
||||||
username.setText('@'+account.acct);
|
username.setText('@'+account.acct);
|
||||||
bio.setText(HtmlParser.parse(account.note, account.emojis));
|
bio.setText(HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), accountID));
|
||||||
followersCount.setText(UiUtils.abbreviateNumber(account.followersCount));
|
followersCount.setText(UiUtils.abbreviateNumber(account.followersCount));
|
||||||
followingCount.setText(UiUtils.abbreviateNumber(account.followingCount));
|
followingCount.setText(UiUtils.abbreviateNumber(account.followingCount));
|
||||||
postsCount.setText(UiUtils.abbreviateNumber(account.statusesCount));
|
postsCount.setText(UiUtils.abbreviateNumber(account.statusesCount));
|
||||||
|
@ -347,7 +375,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||||
fields.add(joined);
|
fields.add(joined);
|
||||||
|
|
||||||
for(AccountField field:account.fields){
|
for(AccountField field:account.fields){
|
||||||
field.parsedValue=ssb=HtmlParser.parse(field.value, account.emojis);
|
field.parsedValue=ssb=HtmlParser.parse(field.value, account.emojis, Collections.emptyList(), accountID);
|
||||||
field.valueEmojis=ssb.getSpans(0, ssb.length(), CustomEmojiSpan.class);
|
field.valueEmojis=ssb.getSpans(0, ssb.length(), CustomEmojiSpan.class);
|
||||||
ssb=new SpannableStringBuilder(field.name);
|
ssb=new SpannableStringBuilder(field.name);
|
||||||
HtmlParser.parseCustomEmoji(ssb, account.emojis);
|
HtmlParser.parseCustomEmoji(ssb, account.emojis);
|
||||||
|
|
|
@ -69,7 +69,7 @@ public abstract class StatusDisplayItem{
|
||||||
}
|
}
|
||||||
items.add(new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID));
|
items.add(new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID));
|
||||||
if(!TextUtils.isEmpty(statusForContent.content))
|
if(!TextUtils.isEmpty(statusForContent.content))
|
||||||
items.add(new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis), fragment));
|
items.add(new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, accountID), fragment));
|
||||||
int photoIndex=0;
|
int photoIndex=0;
|
||||||
int totalPhotos=0;
|
int totalPhotos=0;
|
||||||
for(Attachment attachment:statusForContent.mediaAttachments){
|
for(Attachment attachment:statusForContent.mediaAttachments){
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.text.Spanned;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.joinmastodon.android.model.Emoji;
|
import org.joinmastodon.android.model.Emoji;
|
||||||
|
import org.joinmastodon.android.model.Mention;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
|
@ -40,7 +41,7 @@ public class HtmlParser{
|
||||||
* @param emojis Custom emojis that are present in source as <code>:code:</code>
|
* @param emojis Custom emojis that are present in source as <code>:code:</code>
|
||||||
* @return a spanned string
|
* @return a spanned string
|
||||||
*/
|
*/
|
||||||
public static SpannableStringBuilder parse(String source, List<Emoji> emojis){
|
public static SpannableStringBuilder parse(String source, List<Emoji> emojis, List<Mention> mentions, String accountID){
|
||||||
class SpanInfo{
|
class SpanInfo{
|
||||||
public Object span;
|
public Object span;
|
||||||
public int start;
|
public int start;
|
||||||
|
@ -53,6 +54,8 @@ public class HtmlParser{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, String> idsByUrl=mentions.stream().collect(Collectors.toMap(m->m.url, m->m.id));
|
||||||
|
|
||||||
final SpannableStringBuilder ssb=new SpannableStringBuilder();
|
final SpannableStringBuilder ssb=new SpannableStringBuilder();
|
||||||
Jsoup.parseBodyFragment(source).body().traverse(new NodeVisitor(){
|
Jsoup.parseBodyFragment(source).body().traverse(new NodeVisitor(){
|
||||||
private final ArrayList<SpanInfo> openSpans=new ArrayList<>();
|
private final ArrayList<SpanInfo> openSpans=new ArrayList<>();
|
||||||
|
@ -65,15 +68,22 @@ public class HtmlParser{
|
||||||
Element el=(Element)node;
|
Element el=(Element)node;
|
||||||
switch(el.nodeName()){
|
switch(el.nodeName()){
|
||||||
case "a" -> {
|
case "a" -> {
|
||||||
|
String href=el.attr("href");
|
||||||
LinkSpan.Type linkType;
|
LinkSpan.Type linkType;
|
||||||
if(el.hasClass("hashtag")){
|
if(el.hasClass("hashtag")){
|
||||||
linkType=LinkSpan.Type.HASHTAG;
|
linkType=LinkSpan.Type.HASHTAG;
|
||||||
}else if(el.hasClass("mention")){
|
}else if(el.hasClass("mention")){
|
||||||
|
String id=idsByUrl.get(href);
|
||||||
|
if(id!=null){
|
||||||
linkType=LinkSpan.Type.MENTION;
|
linkType=LinkSpan.Type.MENTION;
|
||||||
|
href=id;
|
||||||
}else{
|
}else{
|
||||||
linkType=LinkSpan.Type.URL;
|
linkType=LinkSpan.Type.URL;
|
||||||
}
|
}
|
||||||
openSpans.add(new SpanInfo(new LinkSpan(el.attr("href"), null, linkType), ssb.length(), el));
|
}else{
|
||||||
|
linkType=LinkSpan.Type.URL;
|
||||||
|
}
|
||||||
|
openSpans.add(new SpanInfo(new LinkSpan(href, null, linkType, accountID), ssb.length(), el));
|
||||||
}
|
}
|
||||||
case "br" -> ssb.append('\n');
|
case "br" -> ssb.append('\n');
|
||||||
case "span" -> {
|
case "span" -> {
|
||||||
|
|
|
@ -13,11 +13,13 @@ public class LinkSpan extends CharacterStyle {
|
||||||
private OnLinkClickListener listener;
|
private OnLinkClickListener listener;
|
||||||
private String link;
|
private String link;
|
||||||
private Type type;
|
private Type type;
|
||||||
|
private String accountID;
|
||||||
|
|
||||||
public LinkSpan(String link, OnLinkClickListener listener, Type type) {
|
public LinkSpan(String link, OnLinkClickListener listener, Type type, String accountID){
|
||||||
this.listener=listener;
|
this.listener=listener;
|
||||||
this.link=link;
|
this.link=link;
|
||||||
this.type=type;
|
this.type=type;
|
||||||
|
this.accountID=accountID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setColor(int c){
|
public void setColor(int c){
|
||||||
|
@ -36,7 +38,8 @@ public class LinkSpan extends CharacterStyle {
|
||||||
public void onClick(Context context){
|
public void onClick(Context context){
|
||||||
switch(getType()){
|
switch(getType()){
|
||||||
case URL -> UiUtils.launchWebBrowser(context, link);
|
case URL -> UiUtils.launchWebBrowser(context, link);
|
||||||
case HASHTAG, MENTION -> Toast.makeText(context, "Not implemented yet", Toast.LENGTH_SHORT).show();
|
case MENTION -> UiUtils.openProfileByID(context, accountID, link);
|
||||||
|
case HASHTAG -> Toast.makeText(context, "Not implemented yet", Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package org.joinmastodon.android.ui.utils;
|
package org.joinmastodon.android.ui.utils;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.drawable.Animatable;
|
import android.graphics.drawable.Animatable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.provider.OpenableColumns;
|
import android.provider.OpenableColumns;
|
||||||
|
@ -17,6 +19,7 @@ import android.widget.TextView;
|
||||||
|
|
||||||
import org.joinmastodon.android.MastodonApp;
|
import org.joinmastodon.android.MastodonApp;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||||
import org.joinmastodon.android.model.Emoji;
|
import org.joinmastodon.android.model.Emoji;
|
||||||
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
||||||
|
|
||||||
|
@ -29,6 +32,7 @@ import java.util.stream.Collectors;
|
||||||
import androidx.annotation.AttrRes;
|
import androidx.annotation.AttrRes;
|
||||||
import androidx.annotation.ColorRes;
|
import androidx.annotation.ColorRes;
|
||||||
import androidx.browser.customtabs.CustomTabsIntent;
|
import androidx.browser.customtabs.CustomTabsIntent;
|
||||||
|
import me.grishka.appkit.Nav;
|
||||||
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;
|
||||||
|
@ -161,4 +165,11 @@ public class UiUtils{
|
||||||
ta.recycle();
|
ta.recycle();
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void openProfileByID(Context context, String selfID, String id){
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", selfID);
|
||||||
|
args.putString("profileAccountID", id);
|
||||||
|
Nav.go((Activity)context, ProfileFragment.class, args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue