Reporting M3 redesign
This commit is contained in:
@@ -5,14 +5,9 @@ import android.content.res.Configuration;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Layout;
|
||||
import android.text.StaticLayout;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
@@ -31,18 +26,15 @@ import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.PollFooterStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.PollOptionStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.SpoilerStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
|
||||
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
|
||||
import org.joinmastodon.android.ui.utils.MediaAttachmentViewController;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.ui.views.MediaGridLayout;
|
||||
import org.joinmastodon.android.utils.TypedObjectPool;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -268,7 +260,11 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
private Rect tmpRect=new Rect();
|
||||
@Override
|
||||
public void getSelectorBounds(View view, Rect outRect){
|
||||
list.getDecoratedBoundsWithMargins(view, outRect);
|
||||
if(((UsableRecyclerView) list).isIncludeMarginsInItemHitbox()){
|
||||
list.getDecoratedBoundsWithMargins(view, outRect);
|
||||
}else{
|
||||
outRect.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
|
||||
}
|
||||
RecyclerView.ViewHolder holder=list.getChildViewHolder(view);
|
||||
if(holder instanceof StatusDisplayItem.Holder){
|
||||
if(((StatusDisplayItem.Holder<?>) holder).getItem().getType()==StatusDisplayItem.Type.GAP){
|
||||
@@ -430,6 +426,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
displayItems.subList(index+1, index+1+spoilerItem.contentItems.size()).clear();
|
||||
adapter.notifyItemRangeRemoved(index+1, spoilerItem.contentItems.size());
|
||||
}
|
||||
list.invalidateItemDecorations();
|
||||
}
|
||||
|
||||
public void onGapClick(GapStatusDisplayItem.Holder item){}
|
||||
@@ -557,6 +554,8 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
return attachmentViewsPool;
|
||||
}
|
||||
|
||||
protected void onModifyItemViewHolder(BindableViewHolder<StatusDisplayItem> holder){}
|
||||
|
||||
protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter<BindableViewHolder<StatusDisplayItem>> implements ImageLoaderRecyclerAdapter{
|
||||
|
||||
public DisplayItemsAdapter(){
|
||||
@@ -566,7 +565,9 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
@NonNull
|
||||
@Override
|
||||
public BindableViewHolder<StatusDisplayItem> onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
return (BindableViewHolder<StatusDisplayItem>) StatusDisplayItem.createViewHolder(StatusDisplayItem.Type.values()[viewType & (~0x80000000)], getActivity(), parent);
|
||||
BindableViewHolder<StatusDisplayItem> holder=(BindableViewHolder<StatusDisplayItem>) StatusDisplayItem.createViewHolder(StatusDisplayItem.Type.values()[viewType & (~0x80000000)], getActivity(), parent);
|
||||
onModifyItemViewHolder(holder);
|
||||
return holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.Activity;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.statuses.GetBookmarkedStatuses;
|
||||
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
|
||||
@@ -34,4 +35,9 @@ public class BookmarkedStatusListFragment extends StatusListFragment{
|
||||
})
|
||||
.exec(accountID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRemoveAccountPostsEvent(RemoveAccountPostsEvent ev){
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,6 +584,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putParcelable("reportAccount", Parcels.wrap(account));
|
||||
args.putParcelable("relationship", Parcels.wrap(relationship));
|
||||
Nav.go(getActivity(), ReportReasonChoiceFragment.class, args);
|
||||
}else if(id==R.id.open_in_browser){
|
||||
UiUtils.launchWebBrowser(getActivity(), account.url);
|
||||
|
||||
@@ -108,13 +108,7 @@ public class AccountActivationFragment extends ToolbarFragment{
|
||||
|
||||
@Override
|
||||
public void onApplyWindowInsets(WindowInsets insets){
|
||||
if(Build.VERSION.SDK_INT>=27){
|
||||
int inset=insets.getSystemWindowInsetBottom();
|
||||
contentView.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()));
|
||||
}
|
||||
super.onApplyWindowInsets(UiUtils.applyBottomInsetToFixedView(contentView, insets));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -151,13 +151,7 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
|
||||
|
||||
@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()));
|
||||
}
|
||||
super.onApplyWindowInsets(UiUtils.applyBottomInsetToFixedView(buttonBar, insets));
|
||||
}
|
||||
|
||||
private void loadServerPrivacyPolicy(){
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.joinmastodon.android.api.requests.instance.GetInstance;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.catalog.CatalogInstance;
|
||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
@@ -330,13 +331,7 @@ abstract class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInsta
|
||||
|
||||
@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()));
|
||||
}
|
||||
super.onApplyWindowInsets(UiUtils.applyBottomInsetToFixedView(buttonBar, insets));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -111,13 +111,7 @@ public class InstanceRulesFragment extends ToolbarFragment{
|
||||
|
||||
@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()));
|
||||
}
|
||||
super.onApplyWindowInsets(UiUtils.applyBottomInsetToFixedView(buttonBar, insets));
|
||||
}
|
||||
|
||||
private class ItemsAdapter extends RecyclerView.Adapter<ItemViewHolder>{
|
||||
|
||||
@@ -130,13 +130,7 @@ public class OnboardingFollowSuggestionsFragment extends BaseRecyclerFragment<Pa
|
||||
|
||||
@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()));
|
||||
}
|
||||
super.onApplyWindowInsets(UiUtils.applyBottomInsetToFixedView(buttonBar, insets));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -161,13 +161,7 @@ public class OnboardingProfileSetupFragment extends ToolbarFragment implements R
|
||||
|
||||
@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()));
|
||||
}
|
||||
super.onApplyWindowInsets(UiUtils.applyBottomInsetToFixedView(buttonBar, insets));
|
||||
}
|
||||
|
||||
private View makeFieldsRow(){
|
||||
|
||||
@@ -366,13 +366,7 @@ public class SignupFragment extends ToolbarFragment{
|
||||
|
||||
@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()));
|
||||
}
|
||||
super.onApplyWindowInsets(UiUtils.applyBottomInsetToFixedView(buttonBar, insets));
|
||||
}
|
||||
|
||||
private void onGoBackLinkClick(LinkSpan span){
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
package org.joinmastodon.android.fragments.report;
|
||||
|
||||
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.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.E;
|
||||
@@ -23,14 +21,9 @@ 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 MastodonToolbarFragment{
|
||||
@@ -38,12 +31,13 @@ public abstract class BaseReportChoiceFragment extends MastodonToolbarFragment{
|
||||
private MergeRecyclerAdapter adapter;
|
||||
private Button btn;
|
||||
private View buttonBar;
|
||||
protected ArrayList<Item> items=new ArrayList<>();
|
||||
protected ArrayList<ChoiceItem> items=new ArrayList<>();
|
||||
protected boolean isMultipleChoice;
|
||||
protected ArrayList<String> selectedIDs=new ArrayList<>();
|
||||
protected String accountID;
|
||||
protected Account reportAccount;
|
||||
protected Status reportStatus;
|
||||
protected ProgressBar progressBar;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
@@ -75,122 +69,33 @@ public abstract class BaseReportChoiceFragment extends MastodonToolbarFragment{
|
||||
list=view.findViewById(R.id.list);
|
||||
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
populateItems();
|
||||
Item header=getHeaderItem();
|
||||
ChoiceItem 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);
|
||||
TextView stepCounter=headerView.findViewById(R.id.step_counter);
|
||||
title.setText(header.title);
|
||||
subtitle.setText(header.subtitle);
|
||||
stepCounter.setText(getString(R.string.step_x_of_n, getStepNumber(), 3));
|
||||
|
||||
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);
|
||||
progressBar=view.findViewById(R.id.top_progress);
|
||||
|
||||
adapter=new MergeRecyclerAdapter();
|
||||
adapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
|
||||
adapter.addAdapter(new ChoiceItemsAdapter(getActivity(), isMultipleChoice, items, list, selectedIDs, btn::setEnabled));
|
||||
list.setAdapter(adapter);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
protected abstract Item getHeaderItem();
|
||||
protected abstract ChoiceItem getHeaderItem();
|
||||
protected abstract void populateItems();
|
||||
protected abstract void onButtonClick();
|
||||
protected abstract int getStepNumber();
|
||||
|
||||
@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<ItemViewHolder>{
|
||||
|
||||
@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<Item> 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<list.getChildCount();i++){
|
||||
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
||||
if(holder instanceof ItemViewHolder ivh && ivh.getItem().id.equals(prev)){
|
||||
ivh.rebind();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
selectedIDs.add(item.id);
|
||||
rebind();
|
||||
}
|
||||
}
|
||||
btn.setEnabled(!selectedIDs.isEmpty());
|
||||
}
|
||||
super.onApplyWindowInsets(UiUtils.applyBottomInsetToFixedView(buttonBar, insets));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.joinmastodon.android.fragments.report;
|
||||
|
||||
class ChoiceItem{
|
||||
public String title, subtitle, id;
|
||||
|
||||
public ChoiceItem(String title, String subtitle, String id){
|
||||
this.title=title;
|
||||
this.subtitle=subtitle;
|
||||
this.id=id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package org.joinmastodon.android.fragments.report;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.ui.views.CheckableLinearLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
class ChoiceItemViewHolder extends BindableViewHolder<ChoiceItem> implements UsableRecyclerView.Clickable{
|
||||
private final TextView title, subtitle;
|
||||
private final View checkbox;
|
||||
private final CheckableLinearLayout view;
|
||||
private final boolean isMultipleChoice;
|
||||
private final RecyclerView list;
|
||||
private final ArrayList<String> selectedIDs;
|
||||
private final Consumer<Boolean> buttonEnabledSetter;
|
||||
|
||||
public ChoiceItemViewHolder(Context context, boolean isMultipleChoice, RecyclerView list, ArrayList<String> selectedIDs, Consumer<Boolean> buttonEnabledSetter){
|
||||
super(context, R.layout.item_report_choice, list);
|
||||
this.buttonEnabledSetter=buttonEnabledSetter;
|
||||
this.isMultipleChoice=isMultipleChoice;
|
||||
this.list=list;
|
||||
this.selectedIDs=selectedIDs;
|
||||
title=findViewById(R.id.title);
|
||||
subtitle=findViewById(R.id.subtitle);
|
||||
checkbox=findViewById(R.id.checkbox);
|
||||
CompoundButton cb=isMultipleChoice ? new CheckBox(context) : new RadioButton(context);
|
||||
checkbox.setBackground(cb.getButtonDrawable());
|
||||
view=(CheckableLinearLayout) itemView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(ChoiceItem item){
|
||||
title.setText(item.title);
|
||||
if(TextUtils.isEmpty(item.subtitle)){
|
||||
subtitle.setVisibility(View.GONE);
|
||||
view.setMinimumHeight(V.dp(56));
|
||||
}else{
|
||||
subtitle.setVisibility(View.VISIBLE);
|
||||
subtitle.setText(item.subtitle);
|
||||
view.setMinimumHeight(V.dp(72));
|
||||
}
|
||||
view.setChecked(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<list.getChildCount(); i++){
|
||||
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
||||
if(holder instanceof ChoiceItemViewHolder ivh && ivh.getItem().id.equals(prev)){
|
||||
ivh.rebind();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
selectedIDs.add(item.id);
|
||||
rebind();
|
||||
}
|
||||
}
|
||||
buttonEnabledSetter.accept(!selectedIDs.isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.joinmastodon.android.fragments.report;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
class ChoiceItemsAdapter extends RecyclerView.Adapter<ChoiceItemViewHolder>{
|
||||
|
||||
private final Context context;
|
||||
private final boolean isMultipleChoice;
|
||||
private final ArrayList<ChoiceItem> items;
|
||||
private final RecyclerView list;
|
||||
private final ArrayList<String> selectedIDs;
|
||||
private final Consumer<Boolean> buttonEnabledSetter;
|
||||
|
||||
public ChoiceItemsAdapter(Context context, boolean isMultipleChoice, ArrayList<ChoiceItem> items, RecyclerView list, ArrayList<String> selectedIDs, Consumer<Boolean> buttonEnabledSetter){
|
||||
this.context=context;
|
||||
this.isMultipleChoice=isMultipleChoice;
|
||||
this.items=items;
|
||||
this.list=list;
|
||||
this.selectedIDs=selectedIDs;
|
||||
this.buttonEnabledSetter=buttonEnabledSetter;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ChoiceItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
return new ChoiceItemViewHolder(context, isMultipleChoice, list, selectedIDs, buttonEnabledSetter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ChoiceItemViewHolder holder, int position){
|
||||
holder.bind(items.get(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount(){
|
||||
return items.size();
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,11 @@ 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.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.squareup.otto.Subscribe;
|
||||
@@ -22,37 +20,34 @@ import org.joinmastodon.android.events.FinishReportFragmentsEvent;
|
||||
import org.joinmastodon.android.fragments.StatusListFragment;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
||||
import org.joinmastodon.android.ui.OutlineProviders;
|
||||
import org.joinmastodon.android.ui.displayitems.AudioStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.CheckableHeaderStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.LinkCardStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem;
|
||||
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 androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
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<String> selectedIDs=new ArrayList<>();
|
||||
private String accountID;
|
||||
private Account reportAccount;
|
||||
private Status reportStatus;
|
||||
private SparseIntArray knownDisplayItemHeights=new SparseIntArray();
|
||||
private HashSet<String> postsWithKnownNonHeaderHeights=new HashSet<>();
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
@@ -72,13 +67,15 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
|
||||
@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)
|
||||
if(reportStatus!=null){
|
||||
selectedIDs.add(reportStatus.id);
|
||||
setTitle(getString(R.string.report_title, reportAccount.acct));
|
||||
setTitle(R.string.report_title_post);
|
||||
}else{
|
||||
setTitle(getString(R.string.report_title, reportAccount.acct));
|
||||
}
|
||||
loadData();
|
||||
}
|
||||
|
||||
@@ -88,6 +85,9 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
|
||||
.setCallback(new SimpleCallback<>(this){
|
||||
@Override
|
||||
public void onSuccess(List<Status> result){
|
||||
for(Status s:result){
|
||||
s.sensitive=true;
|
||||
}
|
||||
onDataLoaded(result, !result.isEmpty());
|
||||
}
|
||||
})
|
||||
@@ -100,8 +100,10 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
|
||||
selectedIDs.remove(id);
|
||||
else
|
||||
selectedIDs.add(id);
|
||||
list.invalidate();
|
||||
btn.setEnabled(!selectedIDs.isEmpty());
|
||||
CheckableHeaderStatusDisplayItem.Holder holder=findHolderOfType(id, CheckableHeaderStatusDisplayItem.Holder.class);
|
||||
if(holder!=null)
|
||||
holder.rebind();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -110,88 +112,27 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
|
||||
btn=view.findViewById(R.id.btn_next);
|
||||
btn.setEnabled(!selectedIDs.isEmpty());
|
||||
btn.setOnClickListener(this::onButtonClick);
|
||||
view.findViewById(R.id.btn_back).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)
|
||||
if(holder.getAbsoluteAdapterPosition()==0 || holder instanceof CheckableHeaderStatusDisplayItem.Holder)
|
||||
return;
|
||||
outRect.left=V.dp(40);
|
||||
if(holder instanceof AudioStatusDisplayItem.Holder){
|
||||
outRect.bottom=V.dp(16);
|
||||
}else if(holder instanceof LinkCardStatusDisplayItem.Holder){
|
||||
}else if(holder instanceof LinkCardStatusDisplayItem.Holder || holder instanceof MediaGridStatusDisplayItem.Holder){
|
||||
outRect.bottom=V.dp(16);
|
||||
outRect.left+=V.dp(16);
|
||||
outRect.right=V.dp(16);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
|
||||
// 1st pass: update item heights
|
||||
for(int i=0;i<parent.getChildCount();i++){
|
||||
View child=parent.getChildAt(i);
|
||||
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
|
||||
if(holder instanceof StatusDisplayItem.Holder sdiHolder){
|
||||
parent.getDecoratedBoundsWithMargins(child, tmpRect);
|
||||
String id=sdiHolder.getItemID();
|
||||
int height=tmpRect.height();
|
||||
if(!(holder instanceof HeaderStatusDisplayItem.Holder) && !(holder instanceof ReblogOrReplyLineStatusDisplayItem.Holder))
|
||||
postsWithKnownNonHeaderHeights.add(id);
|
||||
knownDisplayItemHeights.put(holder.getAbsoluteAdapterPosition(), height);
|
||||
}
|
||||
}
|
||||
// 2nd pass: draw checkboxes
|
||||
String lastPostID=null;
|
||||
for(int i=0;i<parent.getChildCount();i++){
|
||||
View child=parent.getChildAt(i);
|
||||
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
|
||||
if(holder instanceof StatusDisplayItem.Holder<?> sdiHolder){
|
||||
String postID=sdiHolder.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<displayItems.size();j++){
|
||||
StatusDisplayItem item=displayItems.get(j);
|
||||
if(!item.parentID.equals(postID))
|
||||
break;
|
||||
postHeight+=knownDisplayItemHeights.get(j+getMainAdapterOffset());
|
||||
}
|
||||
for(int j=holder.getAbsoluteAdapterPosition()-getMainAdapterOffset()-1;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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ProgressBar topProgress=view.findViewById(R.id.top_progress);
|
||||
topProgress.setProgress(getArguments().containsKey("ruleIDs") ? 50 : 33);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -204,10 +145,8 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
|
||||
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);
|
||||
TextView stepCounter=headerView.findViewById(R.id.step_counter);
|
||||
title.setText(R.string.report_choose_posts);
|
||||
subtitle.setText(R.string.report_choose_posts_subtitle);
|
||||
stepCounter.setText(getString(R.string.step_x_of_n, 2, 3));
|
||||
|
||||
MergeRecyclerAdapter adapter=new MergeRecyclerAdapter();
|
||||
adapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
|
||||
@@ -216,17 +155,14 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
|
||||
}
|
||||
|
||||
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));
|
||||
if(reportStatus!=null)
|
||||
args.putBoolean("fromPost", true);
|
||||
if(v.getId()==R.id.btn_next){
|
||||
args.putStringArrayList("statusIDs", selectedIDs);
|
||||
}else{
|
||||
@@ -237,18 +173,13 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
|
||||
}
|
||||
args.putStringArrayList("ruleIDs", getArguments().getStringArrayList("ruleIDs"));
|
||||
args.putString("reason", getArguments().getString("reason"));
|
||||
args.putParcelable("relationship", getArguments().getParcelable("relationship"));
|
||||
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()));
|
||||
}
|
||||
super.onApplyWindowInsets(UiUtils.applyBottomInsetToFixedView(buttonBar, insets));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@@ -261,4 +192,32 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
|
||||
protected boolean wantsOverlaySystemNavigation(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean wantsElevationOnScrollEffect(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<StatusDisplayItem> buildDisplayItems(Status s){
|
||||
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, StatusDisplayItem.FLAG_INSET | StatusDisplayItem.FLAG_NO_FOOTER | StatusDisplayItem.FLAG_CHECKABLE | StatusDisplayItem.FLAG_MEDIA_FORCE_HIDDEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onModifyItemViewHolder(BindableViewHolder<StatusDisplayItem> holder){
|
||||
if((Object)holder instanceof MediaGridStatusDisplayItem.Holder h){
|
||||
View layout=h.getLayout();
|
||||
layout.setOutlineProvider(OutlineProviders.roundedRect(8));
|
||||
layout.setClipToOutline(true);
|
||||
View overlay=h.getSensitiveOverlay();
|
||||
overlay.setOutlineProvider(OutlineProviders.roundedRect(8));
|
||||
overlay.setClipToOutline(true);
|
||||
}else if((Object)holder instanceof CheckableHeaderStatusDisplayItem.Holder h){
|
||||
h.setIsChecked(this::isChecked);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isChecked(CheckableHeaderStatusDisplayItem.Holder holder){
|
||||
return selectedIDs.contains(holder.getItem().parentID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package org.joinmastodon.android.fragments.report;
|
||||
|
||||
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.EditText;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.squareup.otto.Subscribe;
|
||||
@@ -16,6 +18,7 @@ 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.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.events.FinishReportFragmentsEvent;
|
||||
import org.joinmastodon.android.fragments.MastodonToolbarFragment;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
@@ -28,8 +31,6 @@ 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 MastodonToolbarFragment{
|
||||
private String accountID;
|
||||
@@ -37,6 +38,8 @@ public class ReportCommentFragment extends MastodonToolbarFragment{
|
||||
private Button btn;
|
||||
private View buttonBar;
|
||||
private EditText commentEdit;
|
||||
private Switch forwardSwitch;
|
||||
private View forwardBtn;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
@@ -54,10 +57,12 @@ public class ReportCommentFragment extends MastodonToolbarFragment{
|
||||
@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));
|
||||
if(getArguments().getBoolean("fromPost", false))
|
||||
setTitle(R.string.report_title_post);
|
||||
else
|
||||
setTitle(getString(R.string.report_title, reportAccount.acct));
|
||||
}
|
||||
|
||||
|
||||
@@ -67,16 +72,23 @@ public class ReportCommentFragment extends MastodonToolbarFragment{
|
||||
|
||||
TextView title=view.findViewById(R.id.title);
|
||||
TextView subtitle=view.findViewById(R.id.subtitle);
|
||||
TextView stepCounter=view.findViewById(R.id.step_counter);
|
||||
title.setText(R.string.report_comment_title);
|
||||
subtitle.setVisibility(View.GONE);
|
||||
stepCounter.setText(getString(R.string.step_x_of_n, 3, 3));
|
||||
|
||||
btn=view.findViewById(R.id.btn_next);
|
||||
btn.setOnClickListener(this::onButtonClick);
|
||||
view.findViewById(R.id.btn_back).setOnClickListener(this::onButtonClick);
|
||||
buttonBar=view.findViewById(R.id.button_bar);
|
||||
commentEdit=view.findViewById(R.id.text);
|
||||
forwardSwitch=view.findViewById(R.id.forward_switch);
|
||||
forwardBtn=view.findViewById(R.id.forward_report);
|
||||
forwardBtn.setOnClickListener(v->forwardSwitch.toggle());
|
||||
String myDomain=AccountSessionManager.getInstance().getAccount(accountID).domain;
|
||||
if(!TextUtils.isEmpty(reportAccount.getDomain()) && !myDomain.equalsIgnoreCase(reportAccount.getDomain())){
|
||||
TextView forwardTitle=view.findViewById(R.id.forward_title);
|
||||
forwardTitle.setText(getString(R.string.forward_report_to_server, reportAccount.getDomain()));
|
||||
}else{
|
||||
forwardBtn.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
@@ -84,25 +96,21 @@ public class ReportCommentFragment extends MastodonToolbarFragment{
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), android.R.attr.colorBackground));
|
||||
|
||||
ProgressBar topProgress=view.findViewById(R.id.top_progress);
|
||||
topProgress.setProgress(getArguments().containsKey("ruleIDs") ? 75 : 66);
|
||||
}
|
||||
|
||||
@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()));
|
||||
}
|
||||
super.onApplyWindowInsets(UiUtils.applyBottomInsetToFixedView(buttonBar, insets));
|
||||
}
|
||||
|
||||
private void onButtonClick(View v){
|
||||
ReportReason reason=ReportReason.valueOf(getArguments().getString("reason"));
|
||||
ArrayList<String> statusIDs=getArguments().getStringArrayList("statusIDs");
|
||||
ArrayList<String> ruleIDs=getArguments().getStringArrayList("ruleIDs");
|
||||
new SendReport(reportAccount.id, reason, statusIDs, ruleIDs, v.getId()==R.id.btn_back ? null : commentEdit.getText().toString(), true)
|
||||
new SendReport(reportAccount.id, reason, statusIDs, ruleIDs, v.getId()==R.id.btn_back ? null : commentEdit.getText().toString(), forwardSwitch.isChecked())
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Object result){
|
||||
@@ -110,6 +118,8 @@ public class ReportCommentFragment extends MastodonToolbarFragment{
|
||||
args.putString("account", accountID);
|
||||
args.putParcelable("reportAccount", Parcels.wrap(reportAccount));
|
||||
args.putString("reason", reason.name());
|
||||
args.putBoolean("fromPost", getArguments().getBoolean("fromPost", false));
|
||||
args.putParcelable("relationship", getArguments().getParcelable("relationship"));
|
||||
Nav.go(getActivity(), ReportDoneFragment.class, args);
|
||||
buttonBar.postDelayed(()->E.post(new FinishReportFragmentsEvent(reportAccount.id)), 500);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package org.joinmastodon.android.fragments.report;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.animation.PathInterpolator;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
@@ -23,10 +25,13 @@ import org.joinmastodon.android.ui.OutlineProviders;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
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;
|
||||
@@ -38,6 +43,13 @@ public class ReportDoneFragment extends MastodonToolbarFragment{
|
||||
private Button btn;
|
||||
private View buttonBar;
|
||||
private ReportReason reason;
|
||||
private TextView unfollowTitle;
|
||||
private TextView muteTitle;
|
||||
private TextView blockTitle;
|
||||
private View unfollowBtn;
|
||||
private View muteBtn;
|
||||
private View blockBtn;
|
||||
private Relationship relationship;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
@@ -52,10 +64,13 @@ public class ReportDoneFragment extends MastodonToolbarFragment{
|
||||
accountID=getArguments().getString("account");
|
||||
reportAccount=Parcels.unwrap(getArguments().getParcelable("reportAccount"));
|
||||
reason=ReportReason.valueOf(getArguments().getString("reason"));
|
||||
setTitle(getString(R.string.report_title, reportAccount.acct));
|
||||
relationship=Parcels.unwrap(getArguments().getParcelable("relationship"));
|
||||
if(getArguments().getBoolean("fromPost", false))
|
||||
setTitle(R.string.report_title_post);
|
||||
else
|
||||
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);
|
||||
@@ -64,10 +79,18 @@ public class ReportDoneFragment extends MastodonToolbarFragment{
|
||||
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);
|
||||
if(relationship!=null && relationship.blocking){
|
||||
subtitle.setText(R.string.report_personal_already_blocked);
|
||||
}else{
|
||||
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));
|
||||
if(relationship!=null && relationship.blocking){
|
||||
subtitle.setText(R.string.report_sent_already_blocked);
|
||||
}else{
|
||||
subtitle.setText(getString(R.string.report_sent_subtitle, '@'+reportAccount.acct));
|
||||
}
|
||||
}
|
||||
|
||||
btn=view.findViewById(R.id.btn_next);
|
||||
@@ -76,31 +99,65 @@ public class ReportDoneFragment extends MastodonToolbarFragment{
|
||||
btn.setText(R.string.done);
|
||||
|
||||
if(reason!=ReportReason.PERSONAL){
|
||||
View doneOverlay=view.findViewById(R.id.reported_overlay);
|
||||
doneOverlay.setOutlineProvider(OutlineProviders.roundedRect(7));
|
||||
TextView stamp=view.findViewById(R.id.reported_stamp);
|
||||
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();
|
||||
stamp.setAlpha(0f);
|
||||
stamp.setTranslationX(V.dp(148));
|
||||
stamp.setTranslationY(V.dp(-296));
|
||||
stamp.setScaleX(3.5f);
|
||||
stamp.setScaleY(3.5f);
|
||||
ObjectAnimator alpha=ObjectAnimator.ofFloat(stamp, View.ALPHA, 1f).setDuration(400);
|
||||
alpha.setInterpolator(new PathInterpolator(0.16f, 1, 0.3f, 1));
|
||||
alpha.start();
|
||||
setupSpringAnimation(new SpringAnimation(stamp, DynamicAnimation.TRANSLATION_X, 0f));
|
||||
setupSpringAnimation(new SpringAnimation(stamp, DynamicAnimation.TRANSLATION_Y, 0f));
|
||||
setupSpringAnimation(new SpringAnimation(stamp, DynamicAnimation.SCALE_X, 1f));
|
||||
setupSpringAnimation(new SpringAnimation(stamp, DynamicAnimation.SCALE_Y, 1f));
|
||||
|
||||
}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=view.findViewById(R.id.unfollow_title);
|
||||
muteTitle=view.findViewById(R.id.mute_title);
|
||||
blockTitle=view.findViewById(R.id.block_title);
|
||||
unfollowBtn=view.findViewById(R.id.unfollow_btn);
|
||||
muteBtn=view.findViewById(R.id.mute_btn);
|
||||
blockBtn=view.findViewById(R.id.block_btn);
|
||||
|
||||
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));
|
||||
setIconToButton(R.drawable.ic_person_remove_20px, unfollowTitle);
|
||||
setIconToButton(R.drawable.ic_block_20px, blockTitle);
|
||||
setIconToButton(R.drawable.ic_volume_off_20px, muteTitle);
|
||||
|
||||
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());
|
||||
unfollowBtn.setOnClickListener(v->onUnfollowClick());
|
||||
muteBtn.setOnClickListener(v->onMuteClick());
|
||||
blockBtn.setOnClickListener(v->onBlockClick());
|
||||
|
||||
if(relationship!=null){
|
||||
if(relationship.blocking){
|
||||
muteBtn.setVisibility(View.GONE);
|
||||
view.findViewById(R.id.mute_explanation).setVisibility(View.GONE);
|
||||
unfollowBtn.setVisibility(View.GONE);
|
||||
view.findViewById(R.id.unfollow_explanation).setVisibility(View.GONE);
|
||||
blockBtn.setVisibility(View.GONE);
|
||||
view.findViewById(R.id.block_explanation).setVisibility(View.GONE);
|
||||
}else{
|
||||
if(relationship.muting){
|
||||
muteBtn.setVisibility(View.GONE);
|
||||
view.findViewById(R.id.mute_explanation).setVisibility(View.GONE);
|
||||
}
|
||||
if(!relationship.following){
|
||||
unfollowBtn.setVisibility(View.GONE);
|
||||
view.findViewById(R.id.unfollow_explanation).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
@@ -108,18 +165,11 @@ public class ReportDoneFragment extends MastodonToolbarFragment{
|
||||
@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()));
|
||||
}
|
||||
super.onApplyWindowInsets(UiUtils.applyBottomInsetToFixedView(buttonBar, insets));
|
||||
}
|
||||
|
||||
private void onButtonClick(View v){
|
||||
@@ -131,8 +181,13 @@ public class ReportDoneFragment extends MastodonToolbarFragment{
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Relationship result){
|
||||
Nav.finish(ReportDoneFragment.this);
|
||||
E.post(new RemoveAccountPostsEvent(accountID, reportAccount.id, true));
|
||||
unfollowTitle.setTextColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3OnSecondaryContainer));
|
||||
unfollowTitle.setText(getString(R.string.unfollowed_user, '@'+reportAccount.acct));
|
||||
setIconToButton(R.drawable.ic_check_24px, unfollowTitle);
|
||||
unfollowBtn.setBackgroundResource(R.drawable.bg_button_m3_tonal);
|
||||
unfollowBtn.setClickable(false);
|
||||
unfollowBtn.setFocusable(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -145,10 +200,50 @@ public class ReportDoneFragment extends MastodonToolbarFragment{
|
||||
}
|
||||
|
||||
private void onMuteClick(){
|
||||
UiUtils.confirmToggleMuteUser(getActivity(), accountID, reportAccount, false, rel->Nav.finish(this));
|
||||
UiUtils.confirmToggleMuteUser(getActivity(), accountID, reportAccount, false, rel->{
|
||||
muteTitle.setTextColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3OnSecondaryContainer));
|
||||
muteTitle.setText(getString(R.string.muted_user, '@'+reportAccount.acct));
|
||||
setIconToButton(R.drawable.ic_check_24px, muteTitle);
|
||||
muteBtn.setBackgroundResource(R.drawable.bg_button_m3_tonal);
|
||||
muteBtn.setClickable(false);
|
||||
muteBtn.setFocusable(false);
|
||||
});
|
||||
}
|
||||
|
||||
private void onBlockClick(){
|
||||
UiUtils.confirmToggleBlockUser(getActivity(), accountID, reportAccount, false, rel->Nav.finish(this));
|
||||
UiUtils.confirmToggleBlockUser(getActivity(), accountID, reportAccount, false, rel->{
|
||||
blockTitle.setTextColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3OnSecondaryContainer));
|
||||
blockTitle.setText(getString(R.string.blocked_user, '@'+reportAccount.acct));
|
||||
setIconToButton(R.drawable.ic_check_24px, blockTitle);
|
||||
blockBtn.setBackgroundResource(R.drawable.bg_button_m3_tonal);
|
||||
blockBtn.setClickable(false);
|
||||
blockBtn.setFocusable(false);
|
||||
if(unfollowBtn.isClickable())
|
||||
unfollowBtn.setEnabled(false);
|
||||
if(muteBtn.isClickable())
|
||||
muteBtn.setEnabled(false);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getNavigationIconDrawableResource(){
|
||||
return R.drawable.ic_baseline_close_24;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantsCustomNavigationIcon(){
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setIconToButton(@DrawableRes int icon, TextView button){
|
||||
Drawable d=getResources().getDrawable(icon, getActivity().getTheme()).mutate();
|
||||
d.setBounds(0, 0, V.dp(18), V.dp(18));
|
||||
d.setTintList(button.getTextColors());
|
||||
button.setCompoundDrawablesRelative(d, null, null, null);
|
||||
}
|
||||
|
||||
private void setupSpringAnimation(SpringAnimation anim){
|
||||
anim.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY).setStiffness(500);
|
||||
anim.start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,106 @@
|
||||
package org.joinmastodon.android.fragments.report;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.squareup.otto.Subscribe;
|
||||
|
||||
import org.joinmastodon.android.E;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.events.FinishReportFragmentsEvent;
|
||||
import org.joinmastodon.android.fragments.StatusListFragment;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
import org.joinmastodon.android.model.ReportReason;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.OutlineProviders;
|
||||
import org.joinmastodon.android.ui.displayitems.LinkCardStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import me.grishka.appkit.Nav;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.Nav;
|
||||
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 class ReportReasonChoiceFragment extends StatusListFragment{
|
||||
private MergeRecyclerAdapter mergeAdapter;
|
||||
private Button btn;
|
||||
private View buttonBar;
|
||||
protected ArrayList<ChoiceItem> items=new ArrayList<>();
|
||||
protected boolean isMultipleChoice;
|
||||
protected ArrayList<String> selectedIDs=new ArrayList<>();
|
||||
protected Account reportAccount;
|
||||
protected Status reportStatus;
|
||||
protected ProgressBar progressBar;
|
||||
private Relationship relationship;
|
||||
|
||||
public class ReportReasonChoiceFragment extends BaseReportChoiceFragment{
|
||||
@Override
|
||||
protected Item getHeaderItem(){
|
||||
return new Item(reportStatus!=null ? getString(R.string.report_choose_reason) : getString(R.string.report_choose_reason_account, reportAccount.acct), getString(R.string.report_choose_reason_subtitle), null);
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
setRetainInstance(true);
|
||||
setListLayoutId(R.layout.fragment_content_report_posts);
|
||||
setLayout(R.layout.fragment_report_posts);
|
||||
E.register(this);
|
||||
}
|
||||
|
||||
@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()));
|
||||
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"));
|
||||
if(reportStatus!=null){
|
||||
Status hiddenStatus=new Status(reportStatus);
|
||||
hiddenStatus.spoilerText=getString(R.string.post_hidden);
|
||||
onDataLoaded(Collections.singletonList(hiddenStatus));
|
||||
setTitle(R.string.report_title_post);
|
||||
}else{
|
||||
onDataLoaded(Collections.emptyList());
|
||||
setTitle(getString(R.string.report_title, reportAccount.acct));
|
||||
}
|
||||
relationship=Parcels.unwrap(getArguments().getParcelable("relationship"));
|
||||
if(relationship==null && reportStatus==null)
|
||||
loadRelationships(Collections.singleton(reportAccount.id));
|
||||
|
||||
items.add(new ChoiceItem(getString(R.string.report_reason_personal), getString(R.string.report_reason_personal_subtitle), ReportReason.PERSONAL.name()));
|
||||
items.add(new ChoiceItem(getString(R.string.report_reason_spam), getString(R.string.report_reason_spam_subtitle), ReportReason.SPAM.name()));
|
||||
Instance inst=AccountSessionManager.getInstance().getInstanceInfo(AccountSessionManager.getInstance().getAccount(accountID).domain);
|
||||
if(inst!=null && inst.rules!=null && !inst.rules.isEmpty()){
|
||||
items.add(new Item(getString(R.string.report_reason_violation), getString(R.string.report_reason_violation_subtitle), ReportReason.VIOLATION.name()));
|
||||
items.add(new ChoiceItem(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()));
|
||||
items.add(new ChoiceItem(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();
|
||||
@@ -38,6 +108,8 @@ public class ReportReasonChoiceFragment extends BaseReportChoiceFragment{
|
||||
args.putParcelable("status", Parcels.wrap(reportStatus));
|
||||
args.putParcelable("reportAccount", Parcels.wrap(reportAccount));
|
||||
args.putString("reason", reason.name());
|
||||
args.putBoolean("fromPost", reportStatus!=null);
|
||||
args.putParcelable("relationship", Parcels.wrap(relationship));
|
||||
switch(reason){
|
||||
case PERSONAL -> {
|
||||
Nav.go(getActivity(), ReportDoneFragment.class, args);
|
||||
@@ -48,14 +120,146 @@ public class ReportReasonChoiceFragment extends BaseReportChoiceFragment{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getStepNumber(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onFinishReportFragments(FinishReportFragmentsEvent ev){
|
||||
if(ev.reportAccountID.equals(reportAccount.id))
|
||||
Nav.finish(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplyWindowInsets(WindowInsets insets){
|
||||
super.onApplyWindowInsets(UiUtils.applyBottomInsetToFixedView(buttonBar, insets));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecyclerView.Adapter getAdapter(){
|
||||
MergeRecyclerAdapter adapter=new MergeRecyclerAdapter();
|
||||
|
||||
LayoutInflater inflater=getActivity().getLayoutInflater();
|
||||
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(reportStatus!=null ? getString(R.string.report_choose_reason) : getString(R.string.report_choose_reason_account, reportAccount.acct));
|
||||
subtitle.setText(getString(R.string.report_choose_reason_subtitle));
|
||||
|
||||
adapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
|
||||
adapter.addAdapter(super.getAdapter());
|
||||
adapter.addAdapter(new ChoiceItemsAdapter(getActivity(), isMultipleChoice, items, list, selectedIDs, btn::setEnabled));
|
||||
|
||||
return mergeAdapter=adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
btn=view.findViewById(R.id.btn_next);
|
||||
btn.setEnabled(!selectedIDs.isEmpty());
|
||||
btn.setOnClickListener(v->onButtonClick());
|
||||
buttonBar=view.findViewById(R.id.button_bar);
|
||||
progressBar=view.findViewById(R.id.top_progress);
|
||||
progressBar.setProgress(5);
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
((UsableRecyclerView)list).setIncludeMarginsInItemHitbox(false);
|
||||
|
||||
if(reportStatus!=null){
|
||||
list.addItemDecoration(new RecyclerView.ItemDecoration(){
|
||||
@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 instanceof LinkCardStatusDisplayItem.Holder || holder instanceof MediaGridStatusDisplayItem.Holder){
|
||||
outRect.left=V.dp(16);
|
||||
outRect.right=V.dp(16);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
list.addItemDecoration(new RecyclerView.ItemDecoration(){
|
||||
private Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
{
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
paint.setStrokeWidth(V.dp(1));
|
||||
paint.setColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3OutlineVariant));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
|
||||
int firstPos=list.getChildAdapterPosition(list.getChildAt(0));
|
||||
int lastPos=-1;
|
||||
for(int i=list.getChildCount()-1;i>=0;i--){
|
||||
lastPos=list.getChildAdapterPosition(list.getChildAt(i));
|
||||
if(lastPos!=-1)
|
||||
break;
|
||||
}
|
||||
int postStart=mergeAdapter.getPositionForAdapter(adapter);
|
||||
if(lastPos<postStart || firstPos>postStart+displayItems.size()){
|
||||
return;
|
||||
}
|
||||
|
||||
float top=V.dp(-12);
|
||||
float bottom=parent.getHeight()+V.dp(12);
|
||||
for(int i=0;i<parent.getChildCount();i++){
|
||||
View child=parent.getChildAt(i);
|
||||
int pos=parent.getChildAdapterPosition(child);
|
||||
if(pos==postStart)
|
||||
top=child.getY();
|
||||
if(pos==postStart+displayItems.size())
|
||||
bottom=child.getY()-V.dp(16);
|
||||
}
|
||||
|
||||
float off=paint.getStrokeWidth()/2f;
|
||||
c.drawRoundRect(V.dp(16)-off, top-off, parent.getWidth()-V.dp(16)+off, bottom+off, V.dp(12), V.dp(12), paint);
|
||||
}
|
||||
|
||||
@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 instanceof StatusDisplayItem.Holder<?>){
|
||||
outRect.left=outRect.right=V.dp(16);
|
||||
}
|
||||
int index=holder.getAbsoluteAdapterPosition()-mergeAdapter.getPositionForAdapter(adapter);
|
||||
if(index==displayItems.size()){
|
||||
outRect.top=V.dp(32);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean wantsOverlaySystemNavigation(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean wantsElevationOnScrollEffect(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<StatusDisplayItem> buildDisplayItems(Status s){
|
||||
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, StatusDisplayItem.FLAG_INSET | StatusDisplayItem.FLAG_NO_FOOTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onModifyItemViewHolder(BindableViewHolder<StatusDisplayItem> holder){
|
||||
if((Object)holder instanceof MediaGridStatusDisplayItem.Holder h){
|
||||
View layout=h.getLayout();
|
||||
layout.setOutlineProvider(OutlineProviders.roundedRect(8));
|
||||
layout.setClipToOutline(true);
|
||||
View overlay=h.getSensitiveOverlay();
|
||||
overlay.setOutlineProvider(OutlineProviders.roundedRect(8));
|
||||
overlay.setClipToOutline(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putRelationship(String id, Relationship rel){
|
||||
super.putRelationship(id, rel);
|
||||
if(id.equals(reportAccount.id))
|
||||
relationship=rel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.joinmastodon.android.fragments.report;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import com.squareup.otto.Subscribe;
|
||||
|
||||
@@ -14,8 +15,8 @@ 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);
|
||||
protected ChoiceItem getHeaderItem(){
|
||||
return new ChoiceItem(getString(R.string.report_choose_rule), getString(R.string.report_choose_rule_subtitle), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -24,7 +25,7 @@ public class ReportRuleChoiceFragment extends BaseReportChoiceFragment{
|
||||
Instance inst=AccountSessionManager.getInstance().getInstanceInfo(AccountSessionManager.getInstance().getAccount(accountID).domain);
|
||||
if(inst!=null && inst.rules!=null){
|
||||
for(Instance.Rule rule:inst.rules){
|
||||
items.add(new Item(rule.text, null, rule.id));
|
||||
items.add(new ChoiceItem(rule.text, null, rule.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,17 +38,19 @@ public class ReportRuleChoiceFragment extends BaseReportChoiceFragment{
|
||||
args.putParcelable("reportAccount", Parcels.wrap(reportAccount));
|
||||
args.putString("reason", getArguments().getString("reason"));
|
||||
args.putStringArrayList("ruleIDs", selectedIDs);
|
||||
args.putParcelable("relationship", getArguments().getParcelable("relationship"));
|
||||
Nav.go(getActivity(), ReportAddPostsChoiceFragment.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getStepNumber(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onFinishReportFragments(FinishReportFragmentsEvent ev){
|
||||
if(ev.reportAccountID.equals(reportAccount.id))
|
||||
Nav.finish(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
progressBar.setProgress(25);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
@Parcel
|
||||
public class Relationship extends BaseModel{
|
||||
@RequiredField
|
||||
public String id;
|
||||
|
||||
@@ -59,6 +59,41 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||
public transient boolean hasGapAfter;
|
||||
private transient String strippedText;
|
||||
|
||||
public Status(){}
|
||||
|
||||
public Status(Status other){
|
||||
this.id=other.id;
|
||||
this.uri=other.uri;
|
||||
this.createdAt=other.createdAt;
|
||||
this.account=other.account;
|
||||
this.content=other.content;
|
||||
this.visibility=other.visibility;
|
||||
this.sensitive=other.sensitive;
|
||||
this.spoilerText=other.spoilerText;
|
||||
this.mediaAttachments=other.mediaAttachments;
|
||||
this.application=other.application;
|
||||
this.mentions=other.mentions;
|
||||
this.tags=other.tags;
|
||||
this.emojis=other.emojis;
|
||||
this.reblogsCount=other.reblogsCount;
|
||||
this.favouritesCount=other.favouritesCount;
|
||||
this.repliesCount=other.repliesCount;
|
||||
this.editedAt=other.editedAt;
|
||||
this.url=other.url;
|
||||
this.inReplyToId=other.inReplyToId;
|
||||
this.inReplyToAccountId=other.inReplyToAccountId;
|
||||
this.reblog=other.reblog;
|
||||
this.poll=other.poll;
|
||||
this.card=other.card;
|
||||
this.language=other.language;
|
||||
this.text=other.text;
|
||||
this.favourited=other.favourited;
|
||||
this.reblogged=other.reblogged;
|
||||
this.muted=other.muted;
|
||||
this.bookmarked=other.bookmarked;
|
||||
this.pinned=other.pinned;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
super.postprocess();
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.joinmastodon.android.ui.displayitems;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.views.CheckableRelativeLayout;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class CheckableHeaderStatusDisplayItem extends HeaderStatusDisplayItem{
|
||||
public CheckableHeaderStatusDisplayItem(String parentID, Account user, Instant createdAt, BaseStatusListFragment parentFragment, String accountID, Status status, String extraText){
|
||||
super(parentID, user, createdAt, parentFragment, accountID, status, extraText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(){
|
||||
return Type.HEADER_CHECKABLE;
|
||||
}
|
||||
|
||||
public static class Holder extends HeaderStatusDisplayItem.Holder{
|
||||
private final View checkbox;
|
||||
private final CheckableRelativeLayout view;
|
||||
private Predicate<Holder> isChecked;
|
||||
|
||||
public Holder(Activity activity, ViewGroup parent){
|
||||
super(activity, R.layout.display_item_header_checkable, parent);
|
||||
checkbox=findViewById(R.id.checkbox);
|
||||
view=(CheckableRelativeLayout) itemView;
|
||||
checkbox.setBackground(new CheckBox(activity).getButtonDrawable());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(HeaderStatusDisplayItem item){
|
||||
super.onBind(item);
|
||||
if(isChecked!=null){
|
||||
view.setChecked(isChecked.test(this));
|
||||
}
|
||||
}
|
||||
|
||||
public void setIsChecked(Predicate<Holder> isChecked){
|
||||
this.isChecked=isChecked;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,16 @@ package org.joinmastodon.android.ui.displayitems;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.TextView;
|
||||
@@ -43,6 +41,7 @@ import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.APIRequest;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
@@ -114,7 +113,11 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||
private APIRequest<?> currentRelationshipRequest;
|
||||
|
||||
public Holder(Activity activity, ViewGroup parent){
|
||||
super(activity, R.layout.display_item_header, parent);
|
||||
this(activity, R.layout.display_item_header, parent);
|
||||
}
|
||||
|
||||
protected Holder(Activity activity, @LayoutRes int layout, ViewGroup parent){
|
||||
super(activity, layout, parent);
|
||||
name=findViewById(R.id.name);
|
||||
timeAndUsername=findViewById(R.id.time_and_username);
|
||||
avatar=findViewById(R.id.avatar);
|
||||
@@ -165,6 +168,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||
args.putString("account", item.parentFragment.getAccountID());
|
||||
args.putParcelable("status", Parcels.wrap(item.status));
|
||||
args.putParcelable("reportAccount", Parcels.wrap(item.status.account));
|
||||
args.putParcelable("relationship", Parcels.wrap(relationship));
|
||||
Nav.go(item.parentFragment.getActivity(), ReportReasonChoiceFragment.class, args);
|
||||
}else if(id==R.id.open_in_browser){
|
||||
UiUtils.launchWebBrowser(activity, item.status.url);
|
||||
|
||||
@@ -48,6 +48,7 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
||||
private final ArrayList<ImageLoaderRequest> requests=new ArrayList<>();
|
||||
public final Status status;
|
||||
public boolean sensitiveRevealed;
|
||||
public String sensitiveTitle;
|
||||
|
||||
public MediaGridStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, PhotoLayoutHelper.TiledLayoutResult tiledLayout, List<Attachment> attachments, Status status){
|
||||
super(parentID, parentFragment);
|
||||
@@ -103,6 +104,7 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
||||
private final LayerDrawable sensitiveOverlayBG;
|
||||
private static final ColorDrawable drawableForWhenThereIsNoBlurhash=new ColorDrawable(0xffffffff);
|
||||
private final TextView hideSensitiveButton;
|
||||
private final TextView sensitiveText;
|
||||
|
||||
private int altTextIndex=-1;
|
||||
private Animator altTextAnimator;
|
||||
@@ -112,7 +114,6 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
||||
wrapper=(FrameLayout)itemView;
|
||||
layout=new MediaGridLayout(activity);
|
||||
wrapper.addView(layout);
|
||||
wrapper.setPadding(0, 0, 0, V.dp(8));
|
||||
wrapper.setClipToPadding(false);
|
||||
|
||||
overlays=new MaxWidthFrameLayout(activity);
|
||||
@@ -136,15 +137,20 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
||||
|
||||
activity.getLayoutInflater().inflate(R.layout.overlay_image_sensitive, overlays);
|
||||
sensitiveOverlay=findViewById(R.id.sensitive_overlay);
|
||||
sensitiveOverlayBG=(LayerDrawable) sensitiveOverlay.getBackground();
|
||||
sensitiveOverlayBG=(LayerDrawable) sensitiveOverlay.getBackground().mutate();
|
||||
sensitiveOverlayBG.setDrawableByLayerId(R.id.left_drawable, new SpoilerStripesDrawable(false));
|
||||
sensitiveOverlayBG.setDrawableByLayerId(R.id.right_drawable, new SpoilerStripesDrawable(true));
|
||||
sensitiveOverlay.setBackground(sensitiveOverlayBG);
|
||||
sensitiveOverlay.setOnClickListener(v->revealSensitive());
|
||||
hideSensitiveButton.setOnClickListener(v->hideSensitive());
|
||||
|
||||
sensitiveText=findViewById(R.id.sensitive_text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(MediaGridStatusDisplayItem item){
|
||||
wrapper.setPadding(0, 0, 0, item.inset ? 0 : V.dp(8));
|
||||
|
||||
if(altTextAnimator!=null)
|
||||
altTextAnimator.cancel();
|
||||
|
||||
@@ -190,6 +196,10 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
||||
layout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
hideSensitiveButton.setVisibility(item.status.sensitive ? View.VISIBLE : View.GONE);
|
||||
if(!TextUtils.isEmpty(item.sensitiveTitle))
|
||||
sensitiveText.setText(item.sensitiveTitle);
|
||||
else
|
||||
sensitiveText.setText(R.string.sensitive_content_explain);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -346,5 +356,13 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
||||
item.sensitiveRevealed=false;
|
||||
V.setVisibilityAnimated(sensitiveOverlay, View.VISIBLE, ()->layout.setVisibility(View.INVISIBLE));
|
||||
}
|
||||
|
||||
public MediaGridLayout getLayout(){
|
||||
return layout;
|
||||
}
|
||||
|
||||
public View getSensitiveOverlay(){
|
||||
return sensitiveOverlay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,11 @@ public abstract class StatusDisplayItem{
|
||||
public boolean inset;
|
||||
public int index;
|
||||
|
||||
public static final int FLAG_INSET=1;
|
||||
public static final int FLAG_NO_FOOTER=1 << 1;
|
||||
public static final int FLAG_CHECKABLE=1 << 2;
|
||||
public static final int FLAG_MEDIA_FORCE_HIDDEN=1 << 3;
|
||||
|
||||
public StatusDisplayItem(String parentID, BaseStatusListFragment parentFragment){
|
||||
this.parentID=parentID;
|
||||
this.parentFragment=parentFragment;
|
||||
@@ -51,6 +56,7 @@ public abstract class StatusDisplayItem{
|
||||
public static BindableViewHolder<? extends StatusDisplayItem> createViewHolder(Type type, Activity activity, ViewGroup parent){
|
||||
return switch(type){
|
||||
case HEADER -> new HeaderStatusDisplayItem.Holder(activity, parent);
|
||||
case HEADER_CHECKABLE -> new CheckableHeaderStatusDisplayItem.Holder(activity, parent);
|
||||
case REBLOG_OR_REPLY_LINE -> new ReblogOrReplyLineStatusDisplayItem.Holder(activity, parent);
|
||||
case TEXT -> new TextStatusDisplayItem.Holder(activity, parent);
|
||||
case AUDIO -> new AudioStatusDisplayItem.Holder(activity, parent);
|
||||
@@ -70,6 +76,15 @@ public abstract class StatusDisplayItem{
|
||||
}
|
||||
|
||||
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean addFooter){
|
||||
int flags=0;
|
||||
if(inset)
|
||||
flags|=FLAG_INSET;
|
||||
if(!addFooter)
|
||||
flags|=FLAG_NO_FOOTER;
|
||||
return buildItems(fragment, status, accountID, parentObject, knownAccounts, flags);
|
||||
}
|
||||
|
||||
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, int flags){
|
||||
String parentID=parentObject.getID();
|
||||
ArrayList<StatusDisplayItem> items=new ArrayList<>();
|
||||
Status statusForContent=status.getContentStatus();
|
||||
@@ -80,7 +95,10 @@ public abstract class StatusDisplayItem{
|
||||
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.in_reply_to, account.displayName), account.emojis, R.drawable.ic_reply_20px));
|
||||
}
|
||||
HeaderStatusDisplayItem header;
|
||||
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null));
|
||||
if((flags & FLAG_CHECKABLE)!=0)
|
||||
items.add(header=new CheckableHeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null));
|
||||
else
|
||||
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null));
|
||||
|
||||
ArrayList<StatusDisplayItem> contentItems;
|
||||
if(!TextUtils.isEmpty(statusForContent.spoilerText)){
|
||||
@@ -99,7 +117,10 @@ public abstract class StatusDisplayItem{
|
||||
List<Attachment> imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList());
|
||||
if(!imageAttachments.isEmpty()){
|
||||
PhotoLayoutHelper.TiledLayoutResult layout=PhotoLayoutHelper.processThumbs(imageAttachments);
|
||||
contentItems.add(new MediaGridStatusDisplayItem(parentID, fragment, layout, imageAttachments, statusForContent));
|
||||
MediaGridStatusDisplayItem mediaGrid=new MediaGridStatusDisplayItem(parentID, fragment, layout, imageAttachments, statusForContent);
|
||||
if((flags & FLAG_MEDIA_FORCE_HIDDEN)!=0)
|
||||
mediaGrid.sensitiveTitle=fragment.getString(R.string.media_hidden);
|
||||
contentItems.add(mediaGrid);
|
||||
}
|
||||
for(Attachment att:statusForContent.mediaAttachments){
|
||||
if(att.type==Attachment.Type.AUDIO){
|
||||
@@ -112,12 +133,13 @@ public abstract class StatusDisplayItem{
|
||||
if(statusForContent.card!=null && statusForContent.mediaAttachments.isEmpty() && TextUtils.isEmpty(statusForContent.spoilerText)){
|
||||
contentItems.add(new LinkCardStatusDisplayItem(parentID, fragment, statusForContent));
|
||||
}
|
||||
if(addFooter){
|
||||
if((flags & FLAG_NO_FOOTER)==0){
|
||||
items.add(new FooterStatusDisplayItem(parentID, fragment, statusForContent, accountID));
|
||||
if(status.hasGapAfter && !(fragment instanceof ThreadFragment))
|
||||
items.add(new GapStatusDisplayItem(parentID, fragment));
|
||||
}
|
||||
int i=1;
|
||||
boolean inset=(flags & FLAG_INSET)!=0;
|
||||
for(StatusDisplayItem item:items){
|
||||
item.inset=inset;
|
||||
item.index=i++;
|
||||
@@ -156,7 +178,8 @@ public abstract class StatusDisplayItem{
|
||||
EXTENDED_FOOTER,
|
||||
MEDIA_GRID,
|
||||
SPOILER,
|
||||
SECTION_HEADER
|
||||
SECTION_HEADER,
|
||||
HEADER_CHECKABLE
|
||||
}
|
||||
|
||||
public static abstract class Holder<T extends StatusDisplayItem> extends BindableViewHolder<T> implements UsableRecyclerView.DisableableClickable{
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.Button;
|
||||
import android.widget.PopupMenu;
|
||||
@@ -740,4 +741,13 @@ public class UiUtils{
|
||||
ta.recycle();
|
||||
return d;
|
||||
}
|
||||
|
||||
public static WindowInsets applyBottomInsetToFixedView(View view, WindowInsets insets){
|
||||
if(Build.VERSION.SDK_INT>=27){
|
||||
int inset=insets.getSystemWindowInsetBottom();
|
||||
view.setPadding(0, 0, 0, inset>0 ? Math.max(inset, V.dp(40)) : 0);
|
||||
return insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), 0);
|
||||
}
|
||||
return insets;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +228,7 @@ public class AccountViewHolder extends BindableViewHolder<AccountViewModel> impl
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putParcelable("reportAccount", Parcels.wrap(account));
|
||||
args.putParcelable("relationship", Parcels.wrap(relationship));
|
||||
Nav.go(fragment.getActivity(), ReportReasonChoiceFragment.class, args);
|
||||
}else if(id==R.id.open_in_browser){
|
||||
UiUtils.launchWebBrowser(fragment.getActivity(), account.url);
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.joinmastodon.android.ui.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.Checkable;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
@@ -47,4 +48,11 @@ public class CheckableLinearLayout extends LinearLayout implements Checkable{
|
||||
}
|
||||
return drawableState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info){
|
||||
super.onInitializeAccessibilityNodeInfo(info);
|
||||
info.setCheckable(true);
|
||||
info.setChecked(checked);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.joinmastodon.android.ui.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.Checkable;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
public class CheckableRelativeLayout extends RelativeLayout implements Checkable{
|
||||
private boolean checked;
|
||||
private static final int[] CHECKED_STATE_SET = {
|
||||
android.R.attr.state_checked
|
||||
};
|
||||
|
||||
public CheckableRelativeLayout(Context context){
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public CheckableRelativeLayout(Context context, AttributeSet attrs){
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public CheckableRelativeLayout(Context context, AttributeSet attrs, int defStyle){
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(boolean checked){
|
||||
this.checked=checked;
|
||||
refreshDrawableState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked(){
|
||||
return checked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggle(){
|
||||
setChecked(!checked);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int[] onCreateDrawableState(int extraSpace) {
|
||||
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
|
||||
if (isChecked()) {
|
||||
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
|
||||
}
|
||||
return drawableState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info){
|
||||
super.onInitializeAccessibilityNodeInfo(info);
|
||||
info.setCheckable(true);
|
||||
info.setChecked(checked);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package org.joinmastodon.android.ui.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.Switch;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class M3Switch extends Switch{
|
||||
private boolean ignoreRequestLayout;
|
||||
private DummyDrawable dummyDrawable=new DummyDrawable();
|
||||
|
||||
public M3Switch(Context context){
|
||||
super(context);
|
||||
}
|
||||
|
||||
public M3Switch(Context context, AttributeSet attrs){
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public M3Switch(Context context, AttributeSet attrs, int defStyleAttr){
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
|
||||
ignoreRequestLayout=true;
|
||||
Drawable prevThumbDrawable=getThumbDrawable();
|
||||
setThumbDrawable(dummyDrawable);
|
||||
ignoreRequestLayout=false;
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
ignoreRequestLayout=true;
|
||||
setThumbDrawable(prevThumbDrawable);
|
||||
ignoreRequestLayout=false;
|
||||
try{
|
||||
Field fld=Switch.class.getDeclaredField("mThumbWidth");
|
||||
fld.setAccessible(true);
|
||||
fld.set(this, V.dp(32));
|
||||
}catch(Exception ignore){}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestLayout(){
|
||||
if(ignoreRequestLayout)
|
||||
return;
|
||||
super.requestLayout();
|
||||
}
|
||||
|
||||
private static class DummyDrawable extends Drawable{
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(@Nullable ColorFilter colorFilter){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicWidth(){
|
||||
return V.dp(26);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user