diff --git a/mastodon/build.gradle b/mastodon/build.gradle index f618e1d0..5aeb6a5d 100644 --- a/mastodon/build.gradle +++ b/mastodon/build.gradle @@ -10,7 +10,7 @@ android { applicationId "org.joinmastodon.android" minSdk 23 targetSdk 31 - versionCode 6 + versionCode 7 versionName "0.1" } diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/accounts/GetAccountStatuses.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/accounts/GetAccountStatuses.java index 4c5e2913..1668ef9d 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/requests/accounts/GetAccountStatuses.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/accounts/GetAccountStatuses.java @@ -22,12 +22,17 @@ public class GetAccountStatuses extends MastodonAPIRequest>{ case DEFAULT -> addQueryParameter("exclude_replies", "true"); case INCLUDE_REPLIES -> {} case MEDIA -> addQueryParameter("only_media", "true"); + case NO_REBLOGS -> { + addQueryParameter("exclude_replies", "true"); + addQueryParameter("exclude_reblogs", "true"); + } } } public enum Filter{ DEFAULT, INCLUDE_REPLIES, - MEDIA + MEDIA, + NO_REBLOGS } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/reports/SendReport.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/reports/SendReport.java new file mode 100644 index 00000000..ed77f25c --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/reports/SendReport.java @@ -0,0 +1,30 @@ +package org.joinmastodon.android.api.requests.reports; + +import org.joinmastodon.android.api.MastodonAPIRequest; +import org.joinmastodon.android.model.ReportReason; + +import java.util.Collections; +import java.util.List; + +public class SendReport extends MastodonAPIRequest{ + public SendReport(String accountID, ReportReason reason, List statusIDs, List ruleIDs, String comment, boolean forward){ + super(HttpMethod.POST, "/reports", Object.class); + Body b=new Body(); + b.accountId=accountID; + b.statusIds=statusIDs; + b.comment=comment; + b.forward=forward; + b.category=reason; + b.ruleIds=ruleIDs; + setRequestBody(b); + } + + private static class Body{ + public String accountId; + public List statusIds; + public String comment; + public boolean forward; + public ReportReason category; + public List ruleIds; + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSession.java b/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSession.java index c69702f4..ec37a541 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSession.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSession.java @@ -4,6 +4,7 @@ import org.joinmastodon.android.api.MastodonAPIController; import org.joinmastodon.android.api.StatusInteractionController; import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.Application; +import org.joinmastodon.android.model.Instance; import org.joinmastodon.android.model.Token; public class AccountSession{ @@ -13,16 +14,19 @@ public class AccountSession{ public int tootCharLimit; public Application app; public long infoLastUpdated; + public long instanceLastUpdated; + public Instance instance; private transient MastodonAPIController apiController; private transient StatusInteractionController statusInteractionController; - AccountSession(Token token, Account self, Application app, String domain, int tootCharLimit){ + AccountSession(Token token, Account self, Application app, String domain, int tootCharLimit, Instance instance){ this.token=token; this.self=self; this.domain=domain; this.app=app; this.tootCharLimit=tootCharLimit; - infoLastUpdated=System.currentTimeMillis(); + this.instance=instance; + instanceLastUpdated=infoLastUpdated=System.currentTimeMillis(); } AccountSession(){} diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSessionManager.java b/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSessionManager.java index fcb719ed..ea100444 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSessionManager.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSessionManager.java @@ -13,6 +13,7 @@ import org.joinmastodon.android.R; import org.joinmastodon.android.api.MastodonAPIController; import org.joinmastodon.android.api.requests.instance.GetCustomEmojis; import org.joinmastodon.android.api.requests.accounts.GetOwnAccount; +import org.joinmastodon.android.api.requests.instance.GetInstance; import org.joinmastodon.android.api.requests.oauth.CreateOAuthApp; import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.Application; @@ -84,7 +85,7 @@ public class AccountSessionManager{ } public void addAccount(Instance instance, Token token, Account self, Application app){ - AccountSession session=new AccountSession(token, self, app, instance.uri, instance.maxTootChars); + AccountSession session=new AccountSession(token, self, app, instance.uri, instance.maxTootChars, instance); sessions.put(session.getID(), session); lastActiveAccountID=session.getID(); writeAccountsFile(); @@ -212,7 +213,7 @@ public class AccountSessionManager{ HashSet domains=new HashSet<>(); for(AccountSession session:sessions.values()){ domains.add(session.domain.toLowerCase()); - if(now-session.infoLastUpdated>24L*3600_000L){ + if(now-session.infoLastUpdated>24L*3600_000L || now-session.instanceLastUpdated>24L*360_000L*3L){ updateSessionLocalInfo(session); } } @@ -247,6 +248,21 @@ public class AccountSessionManager{ } }) .exec(session.getID()); + new GetInstance() + .setCallback(new Callback<>(){ + @Override + public void onSuccess(Instance result){ + session.instance=result; + session.instanceLastUpdated=System.currentTimeMillis(); + writeAccountsFile(); + } + + @Override + public void onError(ErrorResponse error){ + + } + }) + .exec(session.getID()); } private void updateCustomEmojis(String domain){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/events/FinishReportFragmentsEvent.java b/mastodon/src/main/java/org/joinmastodon/android/events/FinishReportFragmentsEvent.java new file mode 100644 index 00000000..64dafa62 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/events/FinishReportFragmentsEvent.java @@ -0,0 +1,9 @@ +package org.joinmastodon.android.events; + +public class FinishReportFragmentsEvent{ + public final String reportAccountID; + + public FinishReportFragmentsEvent(String reportAccountID){ + this.reportAccountID=reportAccountID; + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseReportChoiceFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseReportChoiceFragment.java new file mode 100644 index 00000000..bdf75c21 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseReportChoiceFragment.java @@ -0,0 +1,192 @@ +package org.joinmastodon.android.fragments; + +import android.app.Activity; +import android.os.Build; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import org.joinmastodon.android.E; +import org.joinmastodon.android.R; +import org.joinmastodon.android.model.Account; +import org.joinmastodon.android.model.Status; +import org.joinmastodon.android.ui.DividerItemDecoration; +import org.joinmastodon.android.ui.utils.UiUtils; +import org.parceler.Parcels; + +import java.util.ArrayList; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import me.grishka.appkit.fragments.ToolbarFragment; +import me.grishka.appkit.utils.BindableViewHolder; +import me.grishka.appkit.utils.MergeRecyclerAdapter; +import me.grishka.appkit.utils.SingleViewRecyclerAdapter; +import me.grishka.appkit.utils.V; +import me.grishka.appkit.views.UsableRecyclerView; + +public abstract class BaseReportChoiceFragment extends ToolbarFragment{ + private UsableRecyclerView list; + private MergeRecyclerAdapter adapter; + private Button btn; + private View buttonBar; + protected ArrayList items=new ArrayList<>(); + protected boolean isMultipleChoice; + protected ArrayList selectedIDs=new ArrayList<>(); + protected String accountID; + protected Account reportAccount; + protected Status reportStatus; + + @Override + public void onCreate(Bundle savedInstanceState){ + super.onCreate(savedInstanceState); + setRetainInstance(true); + E.register(this); + } + + @Override + public void onDestroy(){ + E.unregister(this); + super.onDestroy(); + } + + @Override + public void onAttach(Activity activity){ + super.onAttach(activity); + setNavigationBarColor(UiUtils.getThemeColor(activity, R.attr.colorWindowBackground)); + accountID=getArguments().getString("account"); + reportAccount=Parcels.unwrap(getArguments().getParcelable("reportAccount")); + reportStatus=Parcels.unwrap(getArguments().getParcelable("status")); + setTitle(getString(R.string.report_title, reportAccount.acct)); + } + + @Override + public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ + View view=inflater.inflate(R.layout.fragment_report_choice, container, false); + + list=view.findViewById(R.id.list); + list.setLayoutManager(new LinearLayoutManager(getActivity())); + populateItems(); + Item header=getHeaderItem(); + View headerView=inflater.inflate(R.layout.item_list_header, list, false); + TextView title=headerView.findViewById(R.id.title); + TextView subtitle=headerView.findViewById(R.id.subtitle); + title.setText(header.title); + subtitle.setText(header.subtitle); + + adapter=new MergeRecyclerAdapter(); + adapter.addAdapter(new SingleViewRecyclerAdapter(headerView)); + adapter.addAdapter(new ItemsAdapter()); + list.setAdapter(adapter); + list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 1, 16, 16, DividerItemDecoration.NOT_FIRST)); + + btn=view.findViewById(R.id.btn_next); + btn.setEnabled(!selectedIDs.isEmpty()); + btn.setOnClickListener(v->onButtonClick()); + buttonBar=view.findViewById(R.id.button_bar); + + return view; + } + + protected abstract Item getHeaderItem(); + protected abstract void populateItems(); + protected abstract void onButtonClick(); + + @Override + public void onApplyWindowInsets(WindowInsets insets){ + if(Build.VERSION.SDK_INT>=27){ + int inset=insets.getSystemWindowInsetBottom(); + buttonBar.setPadding(0, 0, 0, inset>0 ? Math.max(inset, V.dp(36)) : 0); + super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), 0)); + }else{ + super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom())); + } + } + + protected static class Item{ + public String title, subtitle, id; + + public Item(String title, String subtitle, String id){ + this.title=title; + this.subtitle=subtitle; + this.id=id; + } + } + + private class ItemsAdapter extends RecyclerView.Adapter{ + + @NonNull + @Override + public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){ + return new ItemViewHolder(); + } + + @Override + public void onBindViewHolder(@NonNull ItemViewHolder holder, int position){ + holder.bind(items.get(position)); + } + + @Override + public int getItemCount(){ + return items.size(); + } + } + + private class ItemViewHolder extends BindableViewHolder implements UsableRecyclerView.Clickable{ + private final TextView title, subtitle; + private final ImageView checkbox; + + public ItemViewHolder(){ + super(getActivity(), R.layout.item_report_choice, list); + title=findViewById(R.id.title); + subtitle=findViewById(R.id.subtitle); + checkbox=findViewById(R.id.checkbox); + } + + @Override + public void onBind(Item item){ + title.setText(item.title); + if(TextUtils.isEmpty(item.subtitle)){ + subtitle.setVisibility(View.GONE); + }else{ + subtitle.setVisibility(View.VISIBLE); + subtitle.setText(item.subtitle); + } + checkbox.setSelected(selectedIDs.contains(item.id)); + } + + @Override + public void onClick(){ + if(isMultipleChoice){ + if(selectedIDs.contains(item.id)) + selectedIDs.remove(item.id); + else + selectedIDs.add(item.id); + rebind(); + }else{ + if(!selectedIDs.contains(item.id)){ + if(!selectedIDs.isEmpty()){ + String prev=selectedIDs.remove(0); + for(int i=0;i exten protected PhotoViewer currentPhotoViewer; protected HashMap knownAccounts=new HashMap<>(); protected HashMap relationships=new HashMap<>(); + protected Rect tmpRect=new Rect(); public BaseStatusListFragment(){ super(20); @@ -259,7 +260,6 @@ public abstract class BaseStatusListFragment exten } }); list.addItemDecoration(new RecyclerView.ItemDecoration(){ - private Rect tmpRect=new Rect(); private Paint paint=new Paint(); { paint.setColor(UiUtils.getThemeColor(getActivity(), R.attr.colorPollVoted)); @@ -276,11 +276,7 @@ public abstract class BaseStatusListFragment exten RecyclerView.ViewHolder siblingHolder=parent.getChildViewHolder(bottomSibling); if(holder instanceof StatusDisplayItem.Holder && siblingHolder instanceof StatusDisplayItem.Holder && !((StatusDisplayItem.Holder) holder).getItemID().equals(((StatusDisplayItem.Holder) siblingHolder).getItemID())){ - parent.getDecoratedBoundsWithMargins(child, tmpRect); - tmpRect.offset(0, Math.round(child.getTranslationY())); - float y=tmpRect.bottom-V.dp(.5f); - paint.setAlpha(Math.round(255*child.getAlpha())); - c.drawLine(0, y, parent.getWidth(), y, paint); + drawDivider(child, bottomSibling, holder, siblingHolder, parent, c, paint); } } } @@ -398,6 +394,14 @@ public abstract class BaseStatusListFragment exten return 0; } + protected void drawDivider(View child, View bottomSibling, RecyclerView.ViewHolder holder, RecyclerView.ViewHolder siblingHolder, RecyclerView parent, Canvas c, Paint paint){ + parent.getDecoratedBoundsWithMargins(child, tmpRect); + tmpRect.offset(0, Math.round(child.getTranslationY())); + float y=tmpRect.bottom-V.dp(.5f); + paint.setAlpha(Math.round(255*child.getAlpha())); + c.drawLine(0, y, parent.getWidth(), y, paint); + } + public abstract void onItemClick(String id); protected void updatePoll(String itemID, Poll poll){ @@ -494,7 +498,7 @@ public abstract class BaseStatusListFragment exten status.spoilerRevealed=true; TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class); if(text!=null) - adapter.notifyItemChanged(text.getAbsoluteAdapterPosition()); + adapter.notifyItemChanged(text.getAbsoluteAdapterPosition()+getMainAdapterOffset()); HeaderStatusDisplayItem.Holder header=findHolderOfType(itemID, HeaderStatusDisplayItem.Holder.class); if(header!=null) header.rebind(); @@ -509,7 +513,7 @@ public abstract class BaseStatusListFragment exten if(!TextUtils.isEmpty(status.spoilerText)){ TextStatusDisplayItem.Holder text=findHolderOfType(holder.getItemID(), TextStatusDisplayItem.Holder.class); if(text!=null){ - adapter.notifyItemChanged(text.getAbsoluteAdapterPosition()); + adapter.notifyItemChanged(text.getAbsoluteAdapterPosition()+getMainAdapterOffset()); } } holder.rebind(); @@ -609,7 +613,7 @@ public abstract class BaseStatusListFragment exten @NonNull @Override public BindableViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){ - return (BindableViewHolder) StatusDisplayItem.createViewHolder(StatusDisplayItem.Type.values()[viewType], getActivity(), parent); + return (BindableViewHolder) StatusDisplayItem.createViewHolder(StatusDisplayItem.Type.values()[viewType & (~0x80000000)], getActivity(), parent); } @Override @@ -625,7 +629,7 @@ public abstract class BaseStatusListFragment exten @Override public int getItemViewType(int position){ - return displayItems.get(position).getType().ordinal(); + return displayItems.get(position).getType().ordinal() | 0x80000000; } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index 3b852cbf..b597101a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -476,6 +476,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList confirmToggleMuted(); }else if(id==R.id.block){ confirmToggleBlocked(); + }else if(id==R.id.report){ + Bundle args=new Bundle(); + args.putString("account", accountID); + args.putParcelable("reportAccount", Parcels.wrap(account)); + Nav.go(getActivity(), ReportReasonChoiceFragment.class, args); } return true; } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ReportAddPostsChoiceFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ReportAddPostsChoiceFragment.java new file mode 100644 index 00000000..44ece59e --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ReportAddPostsChoiceFragment.java @@ -0,0 +1,263 @@ +package org.joinmastodon.android.fragments; + +import android.app.Activity; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.util.SparseIntArray; +import android.view.View; +import android.view.WindowInsets; +import android.widget.Button; +import android.widget.TextView; + +import com.squareup.otto.Subscribe; + +import org.joinmastodon.android.E; +import org.joinmastodon.android.R; +import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses; +import org.joinmastodon.android.events.FinishReportFragmentsEvent; +import org.joinmastodon.android.model.Account; +import org.joinmastodon.android.model.Status; +import org.joinmastodon.android.ui.displayitems.AudioStatusDisplayItem; +import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem; +import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem; +import org.joinmastodon.android.ui.displayitems.LinkCardStatusDisplayItem; +import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem; +import org.joinmastodon.android.ui.displayitems.StatusDisplayItem; +import org.joinmastodon.android.ui.utils.UiUtils; +import org.parceler.Parcels; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import me.grishka.appkit.Nav; +import me.grishka.appkit.api.SimpleCallback; +import me.grishka.appkit.utils.MergeRecyclerAdapter; +import me.grishka.appkit.utils.SingleViewRecyclerAdapter; +import me.grishka.appkit.utils.V; + +public class ReportAddPostsChoiceFragment extends StatusListFragment{ + + private Button btn; + private View buttonBar; + private ArrayList selectedIDs=new ArrayList<>(); + private String accountID; + private Account reportAccount; + private Status reportStatus; + private SparseIntArray knownDisplayItemHeights=new SparseIntArray(); + private HashSet postsWithKnownNonHeaderHeights=new HashSet<>(); + + @Override + public void onCreate(Bundle savedInstanceState){ + super.onCreate(savedInstanceState); + setRetainInstance(true); + setListLayoutId(R.layout.fragment_content_report_posts); + setLayout(R.layout.fragment_report_posts); + } + + @Override + public void onAttach(Activity activity){ + super.onAttach(activity); + setNavigationBarColor(UiUtils.getThemeColor(activity, R.attr.colorWindowBackground)); + accountID=getArguments().getString("account"); + reportAccount=Parcels.unwrap(getArguments().getParcelable("reportAccount")); + reportStatus=Parcels.unwrap(getArguments().getParcelable("status")); + if(reportStatus!=null) + selectedIDs.add(reportStatus.id); + setTitle(getString(R.string.report_title, reportAccount.acct)); + loadData(); + } + + @Override + protected void doLoadData(int offset, int count){ + currentRequest=new GetAccountStatuses(reportAccount.id, offset>0 ? getMaxID() : null, null, count, GetAccountStatuses.Filter.NO_REBLOGS) + .setCallback(new SimpleCallback<>(this){ + @Override + public void onSuccess(List result){ + onDataLoaded(result, !result.isEmpty()); + } + }) + .exec(accountID); + } + + @Override + public void onItemClick(String id){ + if(selectedIDs.contains(id)) + selectedIDs.remove(id); + else + selectedIDs.add(id); + list.invalidate(); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState){ + super.onViewCreated(view, savedInstanceState); + btn=view.findViewById(R.id.btn_next); + btn.setEnabled(!selectedIDs.isEmpty()); + btn.setOnClickListener(this::onButtonClick); + buttonBar=view.findViewById(R.id.button_bar); + + list.addItemDecoration(new RecyclerView.ItemDecoration(){ + private Drawable uncheckedIcon=getResources().getDrawable(R.drawable.ic_fluent_radio_button_24_regular, getActivity().getTheme()).mutate(); + private Drawable checkedIcon=getResources().getDrawable(R.drawable.ic_fluent_checkmark_circle_24_filled, getActivity().getTheme()).mutate(); + { + int color=UiUtils.getThemeColor(getActivity(), android.R.attr.textColorSecondary); + checkedIcon.setTint(color); + uncheckedIcon.setTint(color); + checkedIcon.setBounds(0, 0, checkedIcon.getIntrinsicWidth(), checkedIcon.getIntrinsicHeight()); + uncheckedIcon.setBounds(0, 0, uncheckedIcon.getIntrinsicWidth(), uncheckedIcon.getIntrinsicHeight()); + } + + @Override + public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){ + RecyclerView.ViewHolder holder=parent.getChildViewHolder(view); + if(holder.getAbsoluteAdapterPosition()==0) + return; + outRect.left=V.dp(40); + if(holder instanceof ImageStatusDisplayItem.Holder){ + ImageStatusDisplayItem.Holder imgHolder=(ImageStatusDisplayItem.Holder) holder; + String siblingID; + if(holder.getAbsoluteAdapterPosition()) holder).getItemID(); + int height=tmpRect.height(); + if(holder instanceof ImageStatusDisplayItem.Holder){ + ImageStatusDisplayItem.Holder imgHolder=(ImageStatusDisplayItem.Holder) holder; + if(imgHolder.getItem().thisTile.startCol+imgHolder.getItem().thisTile.colSpan) holder).getItemID(); + if(!postID.equals(lastPostID)){ + lastPostID=postID; + if(!postsWithKnownNonHeaderHeights.contains(postID)) + continue; // We don't know full height of this post yet + int postHeight=0; + int heightOffset=0; + for(int j=holder.getAbsoluteAdapterPosition()-getMainAdapterOffset();j=0;j--){ + StatusDisplayItem item=displayItems.get(j); + if(!item.parentID.equals(postID)) + break; + int itemHeight=knownDisplayItemHeights.get(j+getMainAdapterOffset()); + postHeight+=itemHeight; + heightOffset+=itemHeight; + } + int y=Math.round(child.getY())+postHeight/2-heightOffset; + Drawable check=selectedIDs.contains(postID) ? checkedIcon : uncheckedIcon; + c.save(); + c.translate(V.dp(16), y-check.getIntrinsicHeight()/2f); + check.draw(c); + c.restore(); + } + } + } + } + }); + } + + @Override + protected int getMainAdapterOffset(){ + return 1; + } + + @Override + protected RecyclerView.Adapter getAdapter(){ + View headerView=getActivity().getLayoutInflater().inflate(R.layout.item_list_header, list, false); + TextView title=headerView.findViewById(R.id.title); + TextView subtitle=headerView.findViewById(R.id.subtitle); + title.setText(R.string.report_choose_posts); + subtitle.setText(R.string.report_choose_posts_subtitle); + + MergeRecyclerAdapter adapter=new MergeRecyclerAdapter(); + adapter.addAdapter(new SingleViewRecyclerAdapter(headerView)); + adapter.addAdapter(super.getAdapter()); + return adapter; + } + + @Override + protected List buildDisplayItems(Status s){ + return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, true, false); + } + + protected void drawDivider(View child, View bottomSibling, RecyclerView.ViewHolder holder, RecyclerView.ViewHolder siblingHolder, RecyclerView parent, Canvas c, Paint paint){ + parent.getDecoratedBoundsWithMargins(child, tmpRect); + tmpRect.offset(0, Math.round(child.getTranslationY())); + float y=tmpRect.bottom-V.dp(.5f); + paint.setAlpha(Math.round(255*child.getAlpha())); + c.drawLine(V.dp(16), y, parent.getWidth()-V.dp(16), y, paint); + } + + private void onButtonClick(View v){ + Bundle args=new Bundle(); + args.putString("account", accountID); + args.putParcelable("reportAccount", Parcels.wrap(reportAccount)); + args.putStringArrayList("statusIDs", selectedIDs); + args.putStringArrayList("ruleIDs", getArguments().getStringArrayList("ruleIDs")); + args.putString("reason", getArguments().getString("reason")); + Nav.go(getActivity(), ReportCommentFragment.class, args); + } + + @Override + public void onApplyWindowInsets(WindowInsets insets){ + if(Build.VERSION.SDK_INT>=27){ + int inset=insets.getSystemWindowInsetBottom(); + buttonBar.setPadding(0, 0, 0, inset>0 ? Math.max(inset, V.dp(36)) : 0); + super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), 0)); + }else{ + super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom())); + } + } + + @Subscribe + public void onFinishReportFragments(FinishReportFragmentsEvent ev){ + if(ev.reportAccountID.equals(reportAccount.id)) + Nav.finish(this); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ReportCommentFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ReportCommentFragment.java new file mode 100644 index 00000000..63a8942c --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ReportCommentFragment.java @@ -0,0 +1,127 @@ +package org.joinmastodon.android.fragments; + +import android.app.Activity; +import android.os.Build; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; + +import com.squareup.otto.Subscribe; + +import org.joinmastodon.android.E; +import org.joinmastodon.android.R; +import org.joinmastodon.android.api.requests.reports.SendReport; +import org.joinmastodon.android.events.FinishReportFragmentsEvent; +import org.joinmastodon.android.model.Account; +import org.joinmastodon.android.model.ReportReason; +import org.joinmastodon.android.ui.utils.UiUtils; +import org.parceler.Parcels; + +import java.util.ArrayList; + +import me.grishka.appkit.Nav; +import me.grishka.appkit.api.Callback; +import me.grishka.appkit.api.ErrorResponse; +import me.grishka.appkit.fragments.ToolbarFragment; +import me.grishka.appkit.utils.V; + +public class ReportCommentFragment extends ToolbarFragment{ + private String accountID; + private Account reportAccount; + private Button btn; + private View buttonBar; + private EditText commentEdit; + + @Override + public void onCreate(Bundle savedInstanceState){ + super.onCreate(savedInstanceState); + setRetainInstance(true); + E.register(this); + } + + @Override + public void onDestroy(){ + E.unregister(this); + super.onDestroy(); + } + + @Override + public void onAttach(Activity activity){ + super.onAttach(activity); + setNavigationBarColor(UiUtils.getThemeColor(activity, R.attr.colorWindowBackground)); + accountID=getArguments().getString("account"); + reportAccount=Parcels.unwrap(getArguments().getParcelable("reportAccount")); + setTitle(getString(R.string.report_title, reportAccount.acct)); + } + + + @Override + public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ + View view=inflater.inflate(R.layout.fragment_report_comment, container, false); + + TextView title=view.findViewById(R.id.title); + TextView subtitle=view.findViewById(R.id.subtitle); + title.setText(R.string.report_comment_title); + subtitle.setText(R.string.report_comment_subtitle); + + btn=view.findViewById(R.id.btn_next); + btn.setOnClickListener(this::onButtonClick); + buttonBar=view.findViewById(R.id.button_bar); + commentEdit=view.findViewById(R.id.text); + + return view; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState){ + super.onViewCreated(view, savedInstanceState); + view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), android.R.attr.colorBackground)); + } + + @Override + public void onApplyWindowInsets(WindowInsets insets){ + if(Build.VERSION.SDK_INT>=27){ + int inset=insets.getSystemWindowInsetBottom(); + buttonBar.setPadding(0, 0, 0, inset>0 ? Math.max(inset, V.dp(36)) : 0); + super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), 0)); + }else{ + super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom())); + } + } + + private void onButtonClick(View v){ + ReportReason reason=ReportReason.valueOf(getArguments().getString("reason")); + ArrayList statusIDs=getArguments().getStringArrayList("statusIDs"); + ArrayList ruleIDs=getArguments().getStringArrayList("ruleIDs"); + new SendReport(reportAccount.id, reason, statusIDs, ruleIDs, commentEdit.getText().toString(), false) + .setCallback(new Callback<>(){ + @Override + public void onSuccess(Object result){ + Bundle args=new Bundle(); + args.putString("account", accountID); + args.putParcelable("reportAccount", Parcels.wrap(reportAccount)); + args.putString("reason", reason.name()); + Nav.go(getActivity(), ReportDoneFragment.class, args); + buttonBar.postDelayed(()->E.post(new FinishReportFragmentsEvent(reportAccount.id)), 500); + } + + @Override + public void onError(ErrorResponse error){ + error.showToast(getActivity()); + } + }) + .wrapProgress(getActivity(), R.string.sending_report, false) + .exec(accountID); + } + + @Subscribe + public void onFinishReportFragments(FinishReportFragmentsEvent ev){ + if(ev.reportAccountID.equals(reportAccount.id)) + Nav.finish(this); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ReportDoneFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ReportDoneFragment.java new file mode 100644 index 00000000..a2077b11 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ReportDoneFragment.java @@ -0,0 +1,158 @@ +package org.joinmastodon.android.fragments; + +import android.app.Activity; +import android.os.Build; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; + +import com.squareup.otto.Subscribe; + +import org.joinmastodon.android.E; +import org.joinmastodon.android.R; +import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed; +import org.joinmastodon.android.api.requests.reports.SendReport; +import org.joinmastodon.android.events.FinishReportFragmentsEvent; +import org.joinmastodon.android.model.Account; +import org.joinmastodon.android.model.Relationship; +import org.joinmastodon.android.model.ReportReason; +import org.joinmastodon.android.ui.OutlineProviders; +import org.joinmastodon.android.ui.utils.UiUtils; +import org.parceler.Parcels; + +import java.util.ArrayList; + +import me.grishka.appkit.Nav; +import me.grishka.appkit.api.Callback; +import me.grishka.appkit.api.ErrorResponse; +import me.grishka.appkit.fragments.ToolbarFragment; +import me.grishka.appkit.imageloader.ViewImageLoader; +import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest; +import me.grishka.appkit.utils.CubicBezierInterpolator; +import me.grishka.appkit.utils.V; + +public class ReportDoneFragment extends ToolbarFragment{ + private String accountID; + private Account reportAccount; + private Button btn; + private View buttonBar; + private ReportReason reason; + + @Override + public void onCreate(Bundle savedInstanceState){ + super.onCreate(savedInstanceState); + setRetainInstance(true); + } + + @Override + public void onAttach(Activity activity){ + super.onAttach(activity); + setNavigationBarColor(UiUtils.getThemeColor(activity, R.attr.colorWindowBackground)); + accountID=getArguments().getString("account"); + reportAccount=Parcels.unwrap(getArguments().getParcelable("reportAccount")); + reason=ReportReason.valueOf(getArguments().getString("reason")); + setTitle(getString(R.string.report_title, reportAccount.acct)); + } + + + @Override + public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ + View view=inflater.inflate(R.layout.fragment_report_done, container, false); + + TextView title=view.findViewById(R.id.title); + TextView subtitle=view.findViewById(R.id.subtitle); + if(reason==ReportReason.PERSONAL){ + title.setText(R.string.report_personal_title); + subtitle.setText(R.string.report_personal_subtitle); + }else{ + title.setText(R.string.report_sent_title); + subtitle.setText(getString(R.string.report_sent_subtitle, '@'+reportAccount.acct)); + } + + btn=view.findViewById(R.id.btn_next); + btn.setOnClickListener(this::onButtonClick); + buttonBar=view.findViewById(R.id.button_bar); + btn.setText(R.string.done); + + if(reason!=ReportReason.PERSONAL){ + View doneOverlay=view.findViewById(R.id.reported_overlay); + doneOverlay.setOutlineProvider(OutlineProviders.roundedRect(7)); + ImageView ava=view.findViewById(R.id.avatar); + ava.setOutlineProvider(OutlineProviders.roundedRect(24)); + ava.setClipToOutline(true); + ViewImageLoader.load(ava, null, new UrlImageLoaderRequest(reportAccount.avatar)); + doneOverlay.setScaleX(1.5f); + doneOverlay.setScaleY(1.5f); + doneOverlay.setAlpha(0f); + doneOverlay.animate().scaleX(1f).scaleY(1f).alpha(1f).rotation(8.79f).setDuration(300).setStartDelay(300).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); + }else{ + view.findViewById(R.id.ava_reported).setVisibility(View.GONE); + } + + TextView unfollowTitle=view.findViewById(R.id.unfollow_title); + TextView muteTitle=view.findViewById(R.id.mute_title); + TextView blockTitle=view.findViewById(R.id.block_title); + + unfollowTitle.setText(getString(R.string.unfollow_user, '@'+reportAccount.acct)); + muteTitle.setText(getString(R.string.mute_user, '@'+reportAccount.acct)); + blockTitle.setText(getString(R.string.block_user, '@'+reportAccount.acct)); + + view.findViewById(R.id.unfollow_btn).setOnClickListener(v->onUnfollowClick()); + view.findViewById(R.id.mute_btn).setOnClickListener(v->onMuteClick()); + view.findViewById(R.id.block_btn).setOnClickListener(v->onBlockClick()); + + return view; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState){ + super.onViewCreated(view, savedInstanceState); + view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), android.R.attr.colorBackground)); + } + + @Override + public void onApplyWindowInsets(WindowInsets insets){ + if(Build.VERSION.SDK_INT>=27){ + int inset=insets.getSystemWindowInsetBottom(); + buttonBar.setPadding(0, 0, 0, inset>0 ? Math.max(inset, V.dp(36)) : 0); + super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), 0)); + }else{ + super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom())); + } + } + + private void onButtonClick(View v){ + Nav.finish(this); + } + + private void onUnfollowClick(){ + new SetAccountFollowed(reportAccount.id, false) + .setCallback(new Callback<>(){ + @Override + public void onSuccess(Relationship result){ + Nav.finish(ReportDoneFragment.this); + } + + @Override + public void onError(ErrorResponse error){ + error.showToast(getActivity()); + } + }) + .wrapProgress(getActivity(), R.string.loading, false) + .exec(accountID); + } + + private void onMuteClick(){ + UiUtils.confirmToggleMuteUser(getActivity(), accountID, reportAccount, false, rel->Nav.finish(this)); + } + + private void onBlockClick(){ + UiUtils.confirmToggleBlockUser(getActivity(), accountID, reportAccount, false, rel->Nav.finish(this)); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ReportReasonChoiceFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ReportReasonChoiceFragment.java new file mode 100644 index 00000000..6fd8cb3d --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ReportReasonChoiceFragment.java @@ -0,0 +1,51 @@ +package org.joinmastodon.android.fragments; + +import android.os.Bundle; + +import com.squareup.otto.Subscribe; + +import org.joinmastodon.android.R; +import org.joinmastodon.android.events.FinishReportFragmentsEvent; +import org.joinmastodon.android.model.ReportReason; +import org.parceler.Parcels; + +import me.grishka.appkit.Nav; + +public class ReportReasonChoiceFragment extends BaseReportChoiceFragment{ + @Override + protected Item getHeaderItem(){ + return new Item(getString(R.string.report_choose_reason), getString(R.string.report_choose_reason_subtitle), null); + } + + @Override + protected void populateItems(){ + items.add(new Item(getString(R.string.report_reason_personal), getString(R.string.report_reason_personal_subtitle), ReportReason.PERSONAL.name())); + items.add(new Item(getString(R.string.report_reason_spam), getString(R.string.report_reason_spam_subtitle), ReportReason.SPAM.name())); + items.add(new Item(getString(R.string.report_reason_violation), getString(R.string.report_reason_violation_subtitle), ReportReason.VIOLATION.name())); + items.add(new Item(getString(R.string.report_reason_other), getString(R.string.report_reason_other_subtitle), ReportReason.OTHER.name())); + } + + @Override + protected void onButtonClick(){ + ReportReason reason=ReportReason.valueOf(selectedIDs.get(0)); + Bundle args=new Bundle(); + args.putString("account", accountID); + args.putParcelable("status", Parcels.wrap(reportStatus)); + args.putParcelable("reportAccount", Parcels.wrap(reportAccount)); + args.putString("reason", reason.name()); + switch(reason){ + case PERSONAL -> { + Nav.go(getActivity(), ReportDoneFragment.class, args); + content.postDelayed(()->Nav.finish(this), 500); + } + case SPAM, OTHER -> Nav.go(getActivity(), ReportAddPostsChoiceFragment.class, args); + case VIOLATION -> Nav.go(getActivity(), ReportRuleChoiceFragment.class, args); + } + } + + @Subscribe + public void onFinishReportFragments(FinishReportFragmentsEvent ev){ + if(ev.reportAccountID.equals(reportAccount.id)) + Nav.finish(this); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ReportRuleChoiceFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ReportRuleChoiceFragment.java new file mode 100644 index 00000000..d6d1a685 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ReportRuleChoiceFragment.java @@ -0,0 +1,48 @@ +package org.joinmastodon.android.fragments; + +import android.os.Bundle; + +import com.squareup.otto.Subscribe; + +import org.joinmastodon.android.R; +import org.joinmastodon.android.api.session.AccountSessionManager; +import org.joinmastodon.android.events.FinishReportFragmentsEvent; +import org.joinmastodon.android.model.Instance; +import org.parceler.Parcels; + +import me.grishka.appkit.Nav; + +public class ReportRuleChoiceFragment extends BaseReportChoiceFragment{ + @Override + protected Item getHeaderItem(){ + return new Item(getString(R.string.report_choose_rule), getString(R.string.report_choose_rule_subtitle), null); + } + + @Override + protected void populateItems(){ + isMultipleChoice=true; + Instance inst=AccountSessionManager.getInstance().getAccount(accountID).instance; + if(inst!=null && inst.rules!=null){ + for(Instance.Rule rule:inst.rules){ + items.add(new Item(rule.text, null, rule.id)); + } + } + } + + @Override + protected void onButtonClick(){ + Bundle args=new Bundle(); + args.putString("account", accountID); + args.putParcelable("status", Parcels.wrap(reportStatus)); + args.putParcelable("reportAccount", Parcels.wrap(reportAccount)); + args.putString("reason", getArguments().getString("reason")); + args.putStringArrayList("ruleIDs", selectedIDs); + Nav.go(getActivity(), ReportAddPostsChoiceFragment.class, args); + } + + @Subscribe + public void onFinishReportFragments(FinishReportFragmentsEvent ev){ + if(ev.reportAccountID.equals(reportAccount.id)) + Nav.finish(this); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/ReportReason.java b/mastodon/src/main/java/org/joinmastodon/android/model/ReportReason.java new file mode 100644 index 00000000..6c3d93f8 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/model/ReportReason.java @@ -0,0 +1,13 @@ +package org.joinmastodon.android.model; + +import com.google.gson.annotations.SerializedName; + +public enum ReportReason{ + PERSONAL, + @SerializedName("spam") + SPAM, + @SerializedName("violation") + VIOLATION, + @SerializedName("other") + OTHER +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/DividerItemDecoration.java b/mastodon/src/main/java/org/joinmastodon/android/ui/DividerItemDecoration.java index 2a53702f..da710419 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/DividerItemDecoration.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/DividerItemDecoration.java @@ -7,6 +7,8 @@ import android.view.View; import org.joinmastodon.android.ui.utils.UiUtils; +import java.util.function.Predicate; + import androidx.annotation.AttrRes; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; @@ -15,13 +17,21 @@ import me.grishka.appkit.utils.V; public class DividerItemDecoration extends RecyclerView.ItemDecoration{ private Paint paint=new Paint(); private int paddingStart, paddingEnd; + private Predicate drawDividerPredicate; + + public static final Predicate NOT_FIRST=vh->vh.getAbsoluteAdapterPosition()>0; public DividerItemDecoration(Context context, @AttrRes int color, float thicknessDp, int paddingStartDp, int paddingEndDp){ + this(context, color, thicknessDp, paddingStartDp, paddingEndDp, null); + } + + public DividerItemDecoration(Context context, @AttrRes int color, float thicknessDp, int paddingStartDp, int paddingEndDp, Predicate drawDividerPredicate){ paint.setColor(UiUtils.getThemeColor(context, color)); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(V.dp(thicknessDp)); paddingStart=V.dp(paddingStartDp); paddingEnd=V.dp(paddingEndDp); + this.drawDividerPredicate=drawDividerPredicate; } @Override @@ -33,7 +43,7 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration{ for(int i=0;i 1.2 * maxRatio || avgRatio > 1.5 * maxRatio) &&*/ orients.equals("www")){ // 2nd and 3rd photos are on the next line + if(/*(ratios.get(0) > 1.2 * maxRatio || avgRatio > 1.5 * maxRatio) &&*/ orients.equals("www") || true){ // 2nd and 3rd photos are on the next line float hCover=Math.min(maxW/ratios.get(0), (maxH-marginH)*0.66f); float w2=((maxW-marginW)/2); float h=Math.min(maxH-hCover-marginH, Math.min(w2/ratios.get(1), w2/ratios.get(2))); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java index c57e476e..72a5e73c 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java @@ -19,6 +19,7 @@ import org.joinmastodon.android.R; import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.fragments.BaseStatusListFragment; import org.joinmastodon.android.fragments.ProfileFragment; +import org.joinmastodon.android.fragments.ReportReasonChoiceFragment; import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.Attachment; import org.joinmastodon.android.model.Status; @@ -121,7 +122,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{ name.setText(item.parsedName); username.setText('@'+item.user.acct); timestamp.setText(UiUtils.formatRelativeTimestamp(itemView.getContext(), item.createdAt)); - visibility.setVisibility(item.hasVisibilityToggle ? View.VISIBLE : View.GONE); + visibility.setVisibility(item.hasVisibilityToggle && !item.inset ? View.VISIBLE : View.GONE); if(item.hasVisibilityToggle){ visibility.setImageResource(item.status.spoilerRevealed ? R.drawable.ic_visibility_off : R.drawable.ic_visibility); } @@ -133,6 +134,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{ extraText.setText(item.extraText); } more.setVisibility(item.inset ? View.GONE : View.VISIBLE); + avatar.setClickable(!item.inset); } @Override @@ -178,7 +180,11 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{ }else if(id==R.id.block){ UiUtils.confirmToggleBlockUser(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), account, false, r->{}); }else if(id==R.id.report){ - + Bundle args=new Bundle(); + args.putString("account", item.parentFragment.getAccountID()); + args.putParcelable("status", Parcels.wrap(item.status)); + args.putParcelable("reportAccount", Parcels.wrap(item.status.account)); + Nav.go(item.parentFragment.getActivity(), ReportReasonChoiceFragment.class, args); } return true; }); diff --git a/mastodon/src/main/res/drawable/bg_edittext_dark.xml b/mastodon/src/main/res/drawable/bg_edittext_dark.xml new file mode 100644 index 00000000..15df6506 --- /dev/null +++ b/mastodon/src/main/res/drawable/bg_edittext_dark.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/bg_edittext_light.xml b/mastodon/src/main/res/drawable/bg_edittext_light.xml new file mode 100644 index 00000000..ecff06a8 --- /dev/null +++ b/mastodon/src/main/res/drawable/bg_edittext_light.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_fluent_checkmark_circle_24_filled.xml b/mastodon/src/main/res/drawable/ic_fluent_checkmark_circle_24_filled.xml new file mode 100644 index 00000000..bc54bc27 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_checkmark_circle_24_filled.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_radio_button_24_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_radio_button_24_regular.xml new file mode 100644 index 00000000..1ad58071 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_radio_button_24_regular.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_round_checkbox.xml b/mastodon/src/main/res/drawable/ic_round_checkbox.xml new file mode 100644 index 00000000..59527d1b --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_round_checkbox.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/reported_overlay.xml b/mastodon/src/main/res/drawable/reported_overlay.xml new file mode 100644 index 00000000..ee152cc1 --- /dev/null +++ b/mastodon/src/main/res/drawable/reported_overlay.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/reported_text.xml b/mastodon/src/main/res/drawable/reported_text.xml new file mode 100644 index 00000000..f0b638a8 --- /dev/null +++ b/mastodon/src/main/res/drawable/reported_text.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/layout/button_bar_one.xml b/mastodon/src/main/res/layout/button_bar_one.xml new file mode 100644 index 00000000..f85ac6d9 --- /dev/null +++ b/mastodon/src/main/res/layout/button_bar_one.xml @@ -0,0 +1,20 @@ + + + +