From 56835b4f2dbd46f0bbff222c6af2108b63f6a60f Mon Sep 17 00:00:00 2001 From: FineFindus Date: Mon, 22 May 2023 21:37:56 +0200 Subject: [PATCH] feat: show blocked domains --- .../requests/instance/GetDomainBlocks.java | 16 ++ .../fragments/InstanceBlockListFragment.java | 185 ++++++++++++++++++ .../fragments/InstanceInfoFragment.java | 5 +- .../android/model/DomainBlock.java | 27 +++ .../joinmastodon/android/model/Severity.java | 12 ++ ...ic_fluent_shield_prohibited_28_regular.xml | 3 + .../src/main/res/layout/item_server_block.xml | 50 +++++ mastodon/src/main/res/values/strings_mo.xml | 2 + 8 files changed, 298 insertions(+), 2 deletions(-) create mode 100644 mastodon/src/main/java/org/joinmastodon/android/api/requests/instance/GetDomainBlocks.java create mode 100644 mastodon/src/main/java/org/joinmastodon/android/fragments/InstanceBlockListFragment.java create mode 100644 mastodon/src/main/java/org/joinmastodon/android/model/DomainBlock.java create mode 100644 mastodon/src/main/java/org/joinmastodon/android/model/Severity.java create mode 100644 mastodon/src/main/res/drawable/ic_fluent_shield_prohibited_28_regular.xml create mode 100644 mastodon/src/main/res/layout/item_server_block.xml diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/instance/GetDomainBlocks.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/instance/GetDomainBlocks.java new file mode 100644 index 000000000..e2c8aa9cf --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/instance/GetDomainBlocks.java @@ -0,0 +1,16 @@ +package org.joinmastodon.android.api.requests.instance; + +import com.google.gson.reflect.TypeToken; + +import org.joinmastodon.android.api.MastodonAPIRequest; +import org.joinmastodon.android.model.DomainBlock; +import org.joinmastodon.android.model.ExtendedDescription; + +import java.util.List; + +public class GetDomainBlocks extends MastodonAPIRequest>{ + public GetDomainBlocks(){ + super(HttpMethod.GET, "/instance/domain_blocks", new TypeToken<>(){}); + } + +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/InstanceBlockListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/InstanceBlockListFragment.java new file mode 100644 index 000000000..4aa6fa1d1 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/InstanceBlockListFragment.java @@ -0,0 +1,185 @@ +package org.joinmastodon.android.fragments; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import android.text.Html; +import android.util.Log; +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 androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import org.joinmastodon.android.R; +import org.joinmastodon.android.api.requests.instance.GetDomainBlocks; +import org.joinmastodon.android.fragments.onboarding.GoogleMadeMeAddThisFragment; +import org.joinmastodon.android.model.DomainBlock; +import org.joinmastodon.android.model.Instance; +import org.joinmastodon.android.model.Severity; +import org.joinmastodon.android.ui.DividerItemDecoration; +import org.joinmastodon.android.ui.text.HtmlParser; +import org.joinmastodon.android.ui.utils.UiUtils; +import org.joinmastodon.android.utils.ElevationOnScrollListener; +import org.parceler.Parcels; + +import java.util.List; + +import me.grishka.appkit.Nav; +import me.grishka.appkit.api.SimpleCallback; +import me.grishka.appkit.fragments.LoaderFragment; +import me.grishka.appkit.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.FragmentRootLinearLayout; +import me.grishka.appkit.views.UsableRecyclerView; + +public class InstanceBlockListFragment extends LoaderFragment { + private UsableRecyclerView list; + private MergeRecyclerAdapter adapter; + private View buttonBar; + private Instance instance; + private ElevationOnScrollListener onScrollListener; + + private List domainBlocks; + + @Override + public void onCreate(Bundle savedInstanceState){ + super.onCreate(savedInstanceState); + setRetainInstance(true); + loadData(); + } + + @Override + public void onAttach(Activity activity){ + super.onAttach(activity); + setNavigationBarColor(UiUtils.getThemeColor(activity, R.attr.colorWindowBackground)); + instance=Parcels.unwrap(getArguments().getParcelable("instance")); + setTitle(R.string.mo_instance_info_moderated_servers); + } + + @Override + public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ + View view=inflater.inflate(R.layout.fragment_onboarding_rules, container, false); + + list=view.findViewById(R.id.list); + list.setLayoutManager(new LinearLayoutManager(getActivity())); + + adapter=new MergeRecyclerAdapter(); + adapter.addAdapter(new ItemsAdapter()); + list.setAdapter(adapter); + list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorM3SurfaceVariant, 1, 0, 0, DividerItemDecoration.NOT_FIRST)); + + buttonBar=view.findViewById(R.id.button_bar); + + view.findViewById(R.id.btn_back).setOnClickListener(v->Nav.finish(this)); + + return view; + } + + @Override + protected void doLoadData() { + currentRequest= new GetDomainBlocks().setCallback(new SimpleCallback<>(this) { + @Override + public void onSuccess(List result) { + domainBlocks=result; + dataLoaded(); + } + }).execNoAuth(instance.uri); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState){ + super.onViewCreated(view, savedInstanceState); + setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background)); + view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background)); + list.addOnScrollListener(onScrollListener=new ElevationOnScrollListener((FragmentRootLinearLayout) view, buttonBar, getToolbar())); + } + + @Override + protected void onUpdateToolbar(){ + super.onUpdateToolbar(); + getToolbar().setBackgroundResource(R.drawable.bg_onboarding_panel); + getToolbar().setElevation(0); + if(onScrollListener!=null){ + onScrollListener.setViews(buttonBar, getToolbar()); + } + } + + @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())); + } + } + + + @Override + public void onRefresh(){ + doLoadData(); + } + 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(domainBlocks.get(position)); + } + + @Override + public int getItemCount(){ + return domainBlocks.size(); + } + } + + private class ItemViewHolder extends BindableViewHolder{ + private final TextView instanceUri, reason; + private final ImageView severity; + + public ItemViewHolder(){ + super(getActivity(), R.layout.item_server_block, list); + instanceUri=findViewById(R.id.instance); + reason=findViewById(R.id.reason); + severity=findViewById(R.id.severity); + } + + @SuppressLint("DefaultLocale") + @Override + public void onBind(DomainBlock item){ + instanceUri.setText(item.domain); + reason.setText(item.comment); + switch (item.severity) { + case SILENCE -> { + severity.setImageDrawable(getContext().getDrawable(R.drawable.ic_fluent_speaker_mute_28_regular)); + severity.setContentDescription(getContext().getString(R.string.mo_severity_silence)); + } + case SUSPEND -> { + severity.setImageDrawable(getContext().getDrawable(R.drawable.ic_fluent_shield_prohibited_28_regular)); + severity.setContentDescription(getContext().getString(R.string.mo_severity_suspend)); + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + severity.setTooltipText(severity.getContentDescription()); + } + } + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/InstanceInfoFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/InstanceInfoFragment.java index dff8a3f28..10b74e141 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/InstanceInfoFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/InstanceInfoFragment.java @@ -178,7 +178,6 @@ public class InstanceInfoFragment extends LoaderFragment { instance = result; bindHeaderView(); dataLoaded(); - } }) .execNoAuth(targetDomain); @@ -349,7 +348,9 @@ public class InstanceInfoFragment extends LoaderFragment { args.putParcelable("instance", Parcels.wrap(instance)); Nav.go(getActivity(), InstanceRulesFragment.class, args); } else if (id==R.id.moderated_servers) { - + Bundle args=new Bundle(); + args.putParcelable("instance", Parcels.wrap(instance)); + Nav.go(getActivity(), InstanceBlockListFragment.class, args); } return true; } diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/DomainBlock.java b/mastodon/src/main/java/org/joinmastodon/android/model/DomainBlock.java new file mode 100644 index 000000000..00f974c43 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/model/DomainBlock.java @@ -0,0 +1,27 @@ +package org.joinmastodon.android.model; + +import org.joinmastodon.android.api.RequiredField; +import org.parceler.Parcel; + +@Parcel +public class DomainBlock extends BaseModel { + @RequiredField + public String domain; + @RequiredField + public String digest; + @RequiredField + public Severity severity; + public String comment; + + @Override + public String toString() { + return "DomainBlock{" + + "domain='" + domain + '\'' + + ", digest='" + digest + '\'' + + ", severity='" + severity + '\'' + + ", comment='" + comment + '\'' + + '}'; + } + + +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/Severity.java b/mastodon/src/main/java/org/joinmastodon/android/model/Severity.java new file mode 100644 index 000000000..96ffb8d67 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Severity.java @@ -0,0 +1,12 @@ +package org.joinmastodon.android.model; + +import com.google.gson.annotations.SerializedName; + +import org.parceler.Parcel; + +public enum Severity { + @SerializedName("silence") + SILENCE, + @SerializedName("suspend") + SUSPEND +} \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_fluent_shield_prohibited_28_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_shield_prohibited_28_regular.xml new file mode 100644 index 000000000..3c8aaab1e --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_shield_prohibited_28_regular.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/layout/item_server_block.xml b/mastodon/src/main/res/layout/item_server_block.xml new file mode 100644 index 000000000..2c679fd2d --- /dev/null +++ b/mastodon/src/main/res/layout/item_server_block.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/values/strings_mo.xml b/mastodon/src/main/res/values/strings_mo.xml index b086e5958..dc989981d 100644 --- a/mastodon/src/main/res/values/strings_mo.xml +++ b/mastodon/src/main/res/values/strings_mo.xml @@ -60,4 +60,6 @@ Local timeline Moderated servers + Silenced + Blocked \ No newline at end of file