feat: add instance info fragment
This commit is contained in:
@@ -0,0 +1,491 @@
|
|||||||
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.graphics.Outline;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewOutlineProvider;
|
||||||
|
import android.view.WindowInsets;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
|
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
|
||||||
|
import org.joinmastodon.android.model.Account;
|
||||||
|
import org.joinmastodon.android.model.AccountField;
|
||||||
|
import org.joinmastodon.android.model.Attachment;
|
||||||
|
import org.joinmastodon.android.model.Instance;
|
||||||
|
import org.joinmastodon.android.model.TimelineDefinition;
|
||||||
|
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||||
|
import org.joinmastodon.android.ui.SingleImagePhotoViewerListener;
|
||||||
|
import org.joinmastodon.android.ui.drawables.CoverOverlayGradientDrawable;
|
||||||
|
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
|
||||||
|
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
||||||
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
|
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.joinmastodon.android.ui.views.CoverImageView;
|
||||||
|
import org.joinmastodon.android.ui.views.LinkedTextView;
|
||||||
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import me.grishka.appkit.Nav;
|
||||||
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
import me.grishka.appkit.fragments.LoaderFragment;
|
||||||
|
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||||
|
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||||
|
import me.grishka.appkit.imageloader.ListImageLoaderWrapper;
|
||||||
|
import me.grishka.appkit.imageloader.RecyclerViewDelegate;
|
||||||
|
import me.grishka.appkit.imageloader.ViewImageLoader;
|
||||||
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
|
public class InstanceInfoFragment extends LoaderFragment {
|
||||||
|
|
||||||
|
private Instance instance;
|
||||||
|
private CoverImageView cover;
|
||||||
|
private TextView uri, description;
|
||||||
|
|
||||||
|
private Button timelineButton, pinButton, rulesButton, serversButton;
|
||||||
|
private final CoverOverlayGradientDrawable coverGradient=new CoverOverlayGradientDrawable();
|
||||||
|
private float titleTransY;
|
||||||
|
private int statusBarHeight;
|
||||||
|
|
||||||
|
private String accountID;
|
||||||
|
private Account account;
|
||||||
|
private String targetDomain;
|
||||||
|
private final ArrayList<AccountField> fields=new ArrayList<>();
|
||||||
|
|
||||||
|
private boolean refreshing;
|
||||||
|
private boolean updatedTimelines = false;
|
||||||
|
|
||||||
|
private static final int MAX_FIELDS=4;
|
||||||
|
|
||||||
|
// from ProfileAboutFragment
|
||||||
|
public UsableRecyclerView list;
|
||||||
|
private List<AccountField> metadataListData=Collections.emptyList();
|
||||||
|
private MetadataAdapter adapter;
|
||||||
|
private ListImageLoaderWrapper imgLoader;
|
||||||
|
|
||||||
|
public InstanceInfoFragment(){
|
||||||
|
super(R.layout.loader_fragment_overlay_toolbar);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
|
||||||
|
setRetainInstance(true);
|
||||||
|
|
||||||
|
accountID=getArguments().getString("account");
|
||||||
|
account= AccountSessionManager.getInstance().getAccount(accountID).self;
|
||||||
|
targetDomain=getArguments().getString("instanceDomain");
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Activity activity){
|
||||||
|
super.onAttach(activity);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
||||||
|
View content=inflater.inflate(R.layout.fragment_instance_info, container, false);
|
||||||
|
|
||||||
|
cover=content.findViewById(R.id.cover);
|
||||||
|
uri =content.findViewById(R.id.uri);
|
||||||
|
description=content.findViewById(R.id.description);
|
||||||
|
timelineButton=content.findViewById(R.id.timeline_btn);
|
||||||
|
pinButton=content.findViewById(R.id.timeline_pin_btn);
|
||||||
|
rulesButton=content.findViewById(R.id.rules_btn);
|
||||||
|
serversButton=content.findViewById(R.id.servers_btn);
|
||||||
|
list=content.findViewById(R.id.metadata);
|
||||||
|
|
||||||
|
cover.setForeground(coverGradient);
|
||||||
|
cover.setOutlineProvider(new ViewOutlineProvider(){
|
||||||
|
@Override
|
||||||
|
public void getOutline(View view, Outline outline){
|
||||||
|
outline.setEmpty();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
timelineButton.setOnClickListener(this::onTimelineButtonClick);
|
||||||
|
pinButton.setOnClickListener(this::onPinButtonClick);
|
||||||
|
rulesButton.setOnClickListener(this::onRulesButtonClick);
|
||||||
|
serversButton.setOnClickListener(this::onSeversButtonClick);
|
||||||
|
cover.setOnClickListener(this::onCoverClick);
|
||||||
|
|
||||||
|
if(loaded){
|
||||||
|
bindHeaderView();
|
||||||
|
dataLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
// from ProfileAboutFragment
|
||||||
|
list.setItemAnimator(new BetterItemAnimator());
|
||||||
|
list.setDrawSelectorOnTop(true);
|
||||||
|
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||||
|
imgLoader=new ListImageLoaderWrapper(getActivity(), list, new RecyclerViewDelegate(list), null);
|
||||||
|
list.setAdapter(adapter=new MetadataAdapter());
|
||||||
|
list.setClipToPadding(false);
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doLoadData(){
|
||||||
|
currentRequest=new GetInstance()
|
||||||
|
.setCallback(new SimpleCallback<>(this){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Instance result){
|
||||||
|
if (getActivity() == null) return;
|
||||||
|
instance = result;
|
||||||
|
bindHeaderView();
|
||||||
|
dataLoaded();
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//hack to get instance url for local and remote accounts
|
||||||
|
.execNoAuth(targetDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRefresh(){
|
||||||
|
if(refreshing)
|
||||||
|
return;
|
||||||
|
refreshing=true;
|
||||||
|
doLoadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dataLoaded(){
|
||||||
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
|
setFields(fields);
|
||||||
|
super.dataLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
updateToolbar();
|
||||||
|
// To avoid the callback triggering on first layout with position=0 before anything is instantiated
|
||||||
|
|
||||||
|
titleTransY=getToolbar().getLayoutParams().height;
|
||||||
|
if(toolbarTitleView!=null){
|
||||||
|
toolbarTitleView.setTranslationY(titleTransY);
|
||||||
|
toolbarSubtitleView.setTranslationY(titleTransY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView(){
|
||||||
|
super.onDestroyView();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (updatedTimelines) UiUtils.restartApp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigurationChanged(Configuration newConfig){
|
||||||
|
super.onConfigurationChanged(newConfig);
|
||||||
|
updateToolbar();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplyWindowInsets(WindowInsets insets){
|
||||||
|
statusBarHeight=insets.getSystemWindowInsetTop();
|
||||||
|
if(contentView!=null){
|
||||||
|
((ViewGroup.MarginLayoutParams) getToolbar().getLayoutParams()).topMargin=statusBarHeight;
|
||||||
|
}
|
||||||
|
super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void bindHeaderView(){
|
||||||
|
ViewImageLoader.load(cover, null, new UrlImageLoaderRequest(instance.thumbnail, 1000, 1000));
|
||||||
|
uri.setText(instance.title);
|
||||||
|
setTitle(instance.title);
|
||||||
|
|
||||||
|
CharSequence parsedDescription = HtmlParser.parse(TextUtils.isEmpty(instance.description) ? instance.shortDescription : instance.description, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
||||||
|
description.setText(parsedDescription);
|
||||||
|
|
||||||
|
fields.clear();
|
||||||
|
|
||||||
|
|
||||||
|
if (instance.contactAccount != null) {
|
||||||
|
AccountField admin = new AccountField();
|
||||||
|
admin.parsedName=admin.name= "Administered by";
|
||||||
|
admin.parsedValue=buildLinkText(instance.contactAccount.url, instance.contactAccount.getDisplayUsername() + "@" + instance.uri);
|
||||||
|
fields.add(admin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance.email != null) {
|
||||||
|
AccountField contact = new AccountField();
|
||||||
|
contact.parsedName = contact.name = "Contact";
|
||||||
|
contact.parsedValue=buildLinkText("mailto:" + instance.email, instance.email);
|
||||||
|
fields.add(contact);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance.stats != null) {
|
||||||
|
AccountField activeUsers = new AccountField();
|
||||||
|
activeUsers.parsedName = activeUsers.name = "users";
|
||||||
|
activeUsers.parsedValue= NumberFormat.getInstance().format(instance.stats.userCount);
|
||||||
|
fields.add(activeUsers);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setFields(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SpannableStringBuilder buildLinkText(String link, String text) {
|
||||||
|
String value = "<span class=\"h-card\"><a href=" + link + " class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">" + text + "</a></span>";
|
||||||
|
return HtmlParser.parse(value, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateToolbar(){
|
||||||
|
getToolbar().setBackgroundColor(0);
|
||||||
|
if(toolbarTitleView!=null){
|
||||||
|
toolbarTitleView.setTranslationY(titleTransY);
|
||||||
|
toolbarSubtitleView.setTranslationY(titleTransY);
|
||||||
|
}
|
||||||
|
getToolbar().setNavigationContentDescription(R.string.back);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean wantsLightStatusBar(){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getToolbarResource(){
|
||||||
|
return R.layout.profile_toolbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onTimelineButtonClick(View view) {
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
args.putString("domain", instance.uri);
|
||||||
|
Nav.go(getActivity(), CustomLocalTimelineFragment.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onPinButtonClick(View view) {
|
||||||
|
List<TimelineDefinition> timelines = GlobalUserPreferences.pinnedTimelines.get(accountID);
|
||||||
|
if (timelines == null) {
|
||||||
|
timelines = List.of(TimelineDefinition.HOME_TIMELINE);
|
||||||
|
}
|
||||||
|
timelines.add(TimelineDefinition.ofCustomLocalTimeline(instance.uri));
|
||||||
|
GlobalUserPreferences.pinnedTimelines.put(accountID, timelines);
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
updatedTimelines = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onRulesButtonClick(View view) {
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putParcelable("instance", Parcels.wrap(instance));
|
||||||
|
Nav.go(getActivity(), InstanceRulesFragment.class, args);
|
||||||
|
}
|
||||||
|
private void onSeversButtonClick(View view) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void onCoverClick(View v){
|
||||||
|
Drawable drawable=cover.getDrawable();
|
||||||
|
if(drawable==null || drawable instanceof ColorDrawable)
|
||||||
|
return;
|
||||||
|
new PhotoViewer(getActivity(), createFakeAttachments(instance.thumbnail, drawable), 0,
|
||||||
|
new SingleImagePhotoViewerListener(cover, cover, null, this, () -> {
|
||||||
|
}, () -> drawable, null, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Attachment> createFakeAttachments(String url, Drawable drawable){
|
||||||
|
Attachment att=new Attachment();
|
||||||
|
att.type=Attachment.Type.IMAGE;
|
||||||
|
att.url=url;
|
||||||
|
att.meta=new Attachment.Metadata();
|
||||||
|
att.meta.width=drawable.getIntrinsicWidth();
|
||||||
|
att.meta.height=drawable.getIntrinsicHeight();
|
||||||
|
return Collections.singletonList(att);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// from ProfileAboutFragment
|
||||||
|
public void setFields(ArrayList<AccountField> fields){
|
||||||
|
metadataListData=fields;
|
||||||
|
if (adapter != null) adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MetadataAdapter extends UsableRecyclerView.Adapter<BaseViewHolder> implements ImageLoaderRecyclerAdapter {
|
||||||
|
public MetadataAdapter(){
|
||||||
|
super(imgLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
|
return switch(viewType){
|
||||||
|
case 0 -> new AboutViewHolder();
|
||||||
|
case 1 -> new EditableAboutViewHolder();
|
||||||
|
case 2 -> new AddRowViewHolder();
|
||||||
|
default -> throw new IllegalStateException("Unexpected value: "+viewType);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(BaseViewHolder holder, int position){
|
||||||
|
if(position<metadataListData.size()){
|
||||||
|
holder.bind(metadataListData.get(position));
|
||||||
|
}else{
|
||||||
|
holder.bind(null);
|
||||||
|
}
|
||||||
|
super.onBindViewHolder(holder, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount(){
|
||||||
|
return metadataListData.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getImageCountForItem(int position){
|
||||||
|
// return metadataListData.get(position).emojiRequests.size();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageLoaderRequest getImageRequest(int position, int image){
|
||||||
|
return metadataListData.get(position).emojiRequests.get(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract class BaseViewHolder extends BindableViewHolder<AccountField> {
|
||||||
|
public BaseViewHolder(int layout){
|
||||||
|
super(getActivity(), layout, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AboutViewHolder extends BaseViewHolder implements ImageLoaderViewHolder {
|
||||||
|
private TextView title;
|
||||||
|
private LinkedTextView value;
|
||||||
|
|
||||||
|
public AboutViewHolder(){
|
||||||
|
super(R.layout.item_profile_about);
|
||||||
|
title=findViewById(R.id.title);
|
||||||
|
value=findViewById(R.id.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBind(AccountField item){
|
||||||
|
title.setText(item.parsedName);
|
||||||
|
value.setText(item.parsedValue);
|
||||||
|
if(item.verifiedAt!=null){
|
||||||
|
int textColor=UiUtils.isDarkTheme() ? 0xFF89bb9c : 0xFF5b8e63;
|
||||||
|
value.setTextColor(textColor);
|
||||||
|
value.setLinkTextColor(textColor);
|
||||||
|
Drawable check=getResources().getDrawable(R.drawable.ic_fluent_checkmark_24_regular, getActivity().getTheme()).mutate();
|
||||||
|
check.setTint(textColor);
|
||||||
|
value.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, check, null);
|
||||||
|
}else{
|
||||||
|
value.setTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
|
||||||
|
value.setLinkTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.colorAccent));
|
||||||
|
value.setCompoundDrawables(null, null, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setImage(int index, Drawable image){
|
||||||
|
CustomEmojiSpan span=index>=item.nameEmojis.length ? item.valueEmojis[index-item.nameEmojis.length] : item.nameEmojis[index];
|
||||||
|
span.setDrawable(image);
|
||||||
|
title.invalidate();
|
||||||
|
value.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearImage(int index){
|
||||||
|
setImage(index, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class EditableAboutViewHolder extends BaseViewHolder {
|
||||||
|
private EditText title;
|
||||||
|
private EditText value;
|
||||||
|
|
||||||
|
public EditableAboutViewHolder(){
|
||||||
|
super(R.layout.item_profile_about_editable);
|
||||||
|
title=findViewById(R.id.title);
|
||||||
|
value=findViewById(R.id.value);
|
||||||
|
findViewById(R.id.dragger_thingy).setOnLongClickListener(v-> true);
|
||||||
|
title.addTextChangedListener(new SimpleTextWatcher(e->item.name=e.toString()));
|
||||||
|
value.addTextChangedListener(new SimpleTextWatcher(e->item.value=e.toString()));
|
||||||
|
findViewById(R.id.remove_row_btn).setOnClickListener(this::onRemoveRowClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBind(AccountField item){
|
||||||
|
title.setText(item.name);
|
||||||
|
value.setText(item.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onRemoveRowClick(View v){
|
||||||
|
int pos=getAbsoluteAdapterPosition();
|
||||||
|
metadataListData.remove(pos);
|
||||||
|
adapter.notifyItemRemoved(pos);
|
||||||
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
|
BaseViewHolder vh=(BaseViewHolder) list.getChildViewHolder(list.getChildAt(i));
|
||||||
|
vh.rebind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AddRowViewHolder extends BaseViewHolder implements UsableRecyclerView.Clickable{
|
||||||
|
public AddRowViewHolder(){
|
||||||
|
super(R.layout.item_profile_about_add_row);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(){
|
||||||
|
metadataListData.add(new AccountField());
|
||||||
|
if(metadataListData.size()==MAX_FIELDS){ // replace this row with new row
|
||||||
|
adapter.notifyItemChanged(metadataListData.size()-1);
|
||||||
|
}else{
|
||||||
|
adapter.notifyItemInserted(metadataListData.size()-1);
|
||||||
|
rebind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBind(AccountField item) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import android.os.Bundle;
|
|||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.style.ImageSpan;
|
import android.text.style.ImageSpan;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
@@ -382,6 +383,13 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
followersBtn.setOnClickListener(this::onFollowersOrFollowingClick);
|
followersBtn.setOnClickListener(this::onFollowersOrFollowingClick);
|
||||||
followingBtn.setOnClickListener(this::onFollowersOrFollowingClick);
|
followingBtn.setOnClickListener(this::onFollowersOrFollowingClick);
|
||||||
|
|
||||||
|
username.setOnClickListener(v -> {
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
args.putString("instanceDomain", Uri.parse(account.url).getHost());
|
||||||
|
Nav.go(getActivity(), InstanceInfoFragment.class, args);
|
||||||
|
});
|
||||||
|
|
||||||
username.setOnLongClickListener(v->{
|
username.setOnLongClickListener(v->{
|
||||||
String usernameString=account.acct;
|
String usernameString=account.acct;
|
||||||
if(!usernameString.contains("@")){
|
if(!usernameString.contains("@")){
|
||||||
|
|||||||
151
mastodon/src/main/res/layout/fragment_instance_info.xml
Normal file
151
mastodon/src/main/res/layout/fragment_instance_info.xml
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<me.grishka.appkit.views.RecursiveSwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/refresh_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clipToPadding="false">
|
||||||
|
|
||||||
|
<org.joinmastodon.android.ui.views.CoverImageView
|
||||||
|
android:id="@+id/cover"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
android:background="?profileHeaderBackground"
|
||||||
|
android:contentDescription="@string/profile_header"
|
||||||
|
android:scaleType="centerCrop" />
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/uri"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/cover"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textAppearance="@style/m3_headline_small"
|
||||||
|
tools:text="floss.social" />
|
||||||
|
|
||||||
|
|
||||||
|
<org.joinmastodon.android.ui.views.LinkedTextView
|
||||||
|
android:id="@+id/description"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/uri"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:textAppearance="@style/m3_body_large"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="Founder, CEO and lead developer @Mastodon, Germany." />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/border_top"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0.5dp"
|
||||||
|
android:layout_below="@id/description"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="?attr/colorPollVoted" />
|
||||||
|
|
||||||
|
<me.grishka.appkit.views.UsableRecyclerView
|
||||||
|
android:id="@+id/metadata"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/border_top"
|
||||||
|
android:background="?colorBackgroundLightest"
|
||||||
|
android:paddingTop="4dp" />
|
||||||
|
|
||||||
|
<org.joinmastodon.android.ui.views.AutoOrientationLinearLayout
|
||||||
|
android:id="@+id/profile_counters"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/metadata"
|
||||||
|
android:background="?colorBackgroundLightest"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
android:paddingTop="8dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/posts_btn"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="36dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/timeline_btn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:background="@drawable/bg_inline_button"
|
||||||
|
android:elevation="0dp"
|
||||||
|
android:ellipsize="middle"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
android:maxWidth="140dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:stateListAnimator="@null"
|
||||||
|
android:text="View local timeline"
|
||||||
|
android:textColor="?android:textColorPrimary"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/timeline_pin_btn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end|center_vertical"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:backgroundTint="?colorBackgroundLightest"
|
||||||
|
android:contentDescription="@string/mo_personal_note_confirm"
|
||||||
|
android:drawableStart="@drawable/ic_fluent_pin_24_regular"
|
||||||
|
android:paddingHorizontal="8dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/rules_btn"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:background="@drawable/bg_inline_button"
|
||||||
|
android:elevation="0dp"
|
||||||
|
android:ellipsize="middle"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
android:maxWidth="140dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:stateListAnimator="@null"
|
||||||
|
android:text="@string/instance_rules_title"
|
||||||
|
android:textColor="?android:textColorPrimary"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/servers_btn"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:background="@drawable/bg_inline_button"
|
||||||
|
android:elevation="0dp"
|
||||||
|
android:ellipsize="middle"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
android:maxWidth="140dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:stateListAnimator="@null"
|
||||||
|
android:text="Moderated servers"
|
||||||
|
android:textColor="?android:textColorPrimary"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
|
||||||
|
</org.joinmastodon.android.ui.views.AutoOrientationLinearLayout>
|
||||||
|
</RelativeLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</me.grishka.appkit.views.RecursiveSwipeRefreshLayout>
|
||||||
Reference in New Issue
Block a user