diff --git a/mastodon/build.gradle b/mastodon/build.gradle index 75fc918ee..55bab126e 100644 --- a/mastodon/build.gradle +++ b/mastodon/build.gradle @@ -9,7 +9,7 @@ android { applicationId "org.joinmastodon.android" minSdk 23 targetSdk 33 - versionCode 54 + versionCode 55 versionName "1.3.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resConfigs "ar-rSA", "be-rBY", "bn-rBD", "bs-rBA", "ca-rES", "cs-rCZ", "da-rDK", "de-rDE", "el-rGR", "es-rES", "eu-rES", "fa-rIR", "fi-rFI", "fil-rPH", "fr-rFR", "ga-rIE", "gd-rGB", "gl-rES", "hi-rIN", "hr-rHR", "hu-rHU", "hy-rAM", "ig-rNG", "in-rID", "is-rIS", "it-rIT", "iw-rIL", "ja-rJP", "kab", "ko-rKR", "my-rMM", "nl-rNL", "no-rNO", "oc-rFR", "pl-rPL", "pt-rBR", "pt-rPT", "ro-rRO", "ru-rRU", "si-rLK", "sl-rSI", "sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "ur-rIN", "vi-rVN", "zh-rCN", "zh-rTW" diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java index dba169270..f1997c0be 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java @@ -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 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 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 exten return attachmentViewsPool; } + protected void onModifyItemViewHolder(BindableViewHolder holder){} + protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter> implements ImageLoaderRecyclerAdapter{ public DisplayItemsAdapter(){ @@ -566,7 +565,9 @@ public abstract class BaseStatusListFragment exten @NonNull @Override public BindableViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){ - return (BindableViewHolder) StatusDisplayItem.createViewHolder(StatusDisplayItem.Type.values()[viewType & (~0x80000000)], getActivity(), parent); + BindableViewHolder holder=(BindableViewHolder) StatusDisplayItem.createViewHolder(StatusDisplayItem.Type.values()[viewType & (~0x80000000)], getActivity(), parent); + onModifyItemViewHolder(holder); + return holder; } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BookmarkedStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BookmarkedStatusListFragment.java index 501ff1275..ecb737776 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BookmarkedStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BookmarkedStatusListFragment.java @@ -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 + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index 6d5876368..76a2c42dc 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -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); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/AccountActivationFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/AccountActivationFragment.java index b1a77e9c0..c72b028a0 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/AccountActivationFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/AccountActivationFragment.java @@ -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 diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/GoogleMadeMeAddThisFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/GoogleMadeMeAddThisFragment.java index bf5f7420e..0097c61db 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/GoogleMadeMeAddThisFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/GoogleMadeMeAddThisFragment.java @@ -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(){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceCatalogFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceCatalogFragment.java index ea70ff705..fb21ffedb 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceCatalogFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceCatalogFragment.java @@ -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=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 diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceRulesFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceRulesFragment.java index f5919b392..47e07d4d3 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceRulesFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceRulesFragment.java @@ -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{ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingFollowSuggestionsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingFollowSuggestionsFragment.java index 709174137..648270841 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingFollowSuggestionsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingFollowSuggestionsFragment.java @@ -130,13 +130,7 @@ public class OnboardingFollowSuggestionsFragment extends BaseRecyclerFragment=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 diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingProfileSetupFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingProfileSetupFragment.java index 599f837f0..77bd0b1a6 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingProfileSetupFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/OnboardingProfileSetupFragment.java @@ -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(){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/SignupFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/SignupFragment.java index a8df65063..7f4fe256a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/SignupFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/SignupFragment.java @@ -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){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/BaseReportChoiceFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/BaseReportChoiceFragment.java index 6d7991895..5d3bef588 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/BaseReportChoiceFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/BaseReportChoiceFragment.java @@ -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 items=new ArrayList<>(); + protected ArrayList items=new ArrayList<>(); protected boolean isMultipleChoice; protected ArrayList 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{ - - @NonNull - @Override - public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){ - return new ItemViewHolder(); - } - - @Override - public void onBindViewHolder(@NonNull ItemViewHolder holder, int position){ - holder.bind(items.get(position)); - } - - @Override - public int getItemCount(){ - return items.size(); - } - } - - private class ItemViewHolder extends BindableViewHolder implements UsableRecyclerView.Clickable{ - private final TextView title, subtitle; - private final ImageView checkbox; - - public ItemViewHolder(){ - super(getActivity(), R.layout.item_report_choice, list); - title=findViewById(R.id.title); - subtitle=findViewById(R.id.subtitle); - checkbox=findViewById(R.id.checkbox); - } - - @Override - public void onBind(Item item){ - title.setText(item.title); - if(TextUtils.isEmpty(item.subtitle)){ - subtitle.setVisibility(View.GONE); - }else{ - subtitle.setVisibility(View.VISIBLE); - subtitle.setText(item.subtitle); - } - checkbox.setSelected(selectedIDs.contains(item.id)); - } - - @Override - public void onClick(){ - if(isMultipleChoice){ - if(selectedIDs.contains(item.id)) - selectedIDs.remove(item.id); - else - selectedIDs.add(item.id); - rebind(); - }else{ - if(!selectedIDs.contains(item.id)){ - if(!selectedIDs.isEmpty()){ - String prev=selectedIDs.remove(0); - for(int i=0;i 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 selectedIDs; + private final Consumer buttonEnabledSetter; + + public ChoiceItemViewHolder(Context context, boolean isMultipleChoice, RecyclerView list, ArrayList selectedIDs, Consumer 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{ + + private final Context context; + private final boolean isMultipleChoice; + private final ArrayList items; + private final RecyclerView list; + private final ArrayList selectedIDs; + private final Consumer buttonEnabledSetter; + + public ChoiceItemsAdapter(Context context, boolean isMultipleChoice, ArrayList items, RecyclerView list, ArrayList selectedIDs, Consumer 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(); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportAddPostsChoiceFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportAddPostsChoiceFragment.java index 73c80ed70..44c0c5298 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportAddPostsChoiceFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportAddPostsChoiceFragment.java @@ -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 selectedIDs=new ArrayList<>(); private String accountID; private Account reportAccount; private Status reportStatus; - private SparseIntArray knownDisplayItemHeights=new SparseIntArray(); - private HashSet postsWithKnownNonHeaderHeights=new HashSet<>(); @Override public void onCreate(Bundle savedInstanceState){ @@ -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 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 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=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 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 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); + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportCommentFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportCommentFragment.java index d42f435eb..522be853a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportCommentFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportCommentFragment.java @@ -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 statusIDs=getArguments().getStringArrayList("statusIDs"); ArrayList 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); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportDoneFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportDoneFragment.java index dba213882..8ac1389b2 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportDoneFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportDoneFragment.java @@ -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(); } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportReasonChoiceFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportReasonChoiceFragment.java index be83dc475..e02e044da 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportReasonChoiceFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportReasonChoiceFragment.java @@ -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 items=new ArrayList<>(); + protected boolean isMultipleChoice; + protected ArrayList 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(lastPospostStart+displayItems.size()){ + return; + } + + float top=V.dp(-12); + float bottom=parent.getHeight()+V.dp(12); + for(int i=0;i){ + 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 buildDisplayItems(Status s){ + return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, StatusDisplayItem.FLAG_INSET | StatusDisplayItem.FLAG_NO_FOOTER); + } + + @Override + protected void onModifyItemViewHolder(BindableViewHolder 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; + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportRuleChoiceFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportRuleChoiceFragment.java index b93eb7861..f88e4fb24 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportRuleChoiceFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportRuleChoiceFragment.java @@ -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); + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/Relationship.java b/mastodon/src/main/java/org/joinmastodon/android/model/Relationship.java index e5f6ec13e..f5a08b749 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Relationship.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Relationship.java @@ -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; diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/Status.java b/mastodon/src/main/java/org/joinmastodon/android/model/Status.java index 37f4ba7d5..8bc1c03cb 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Status.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Status.java @@ -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(); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/CheckableHeaderStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/CheckableHeaderStatusDisplayItem.java new file mode 100644 index 000000000..631b98d0f --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/CheckableHeaderStatusDisplayItem.java @@ -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 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 isChecked){ + this.isChecked=isChecked; + } + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java index a91923120..c81b38ca2 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java @@ -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); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/MediaGridStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/MediaGridStatusDisplayItem.java index 3ab0e2b71..b0a80fb47 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/MediaGridStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/MediaGridStatusDisplayItem.java @@ -48,6 +48,7 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{ private final ArrayList requests=new ArrayList<>(); public final Status status; public boolean sensitiveRevealed; + public String sensitiveTitle; public MediaGridStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, PhotoLayoutHelper.TiledLayoutResult tiledLayout, List 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; + } } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java index 6181ab72a..55086715c 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java @@ -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 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 buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map 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 buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map knownAccounts, int flags){ String parentID=parentObject.getID(); ArrayList 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 contentItems; if(!TextUtils.isEmpty(statusForContent.spoilerText)){ @@ -99,7 +117,10 @@ public abstract class StatusDisplayItem{ List 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 extends BindableViewHolder implements UsableRecyclerView.DisableableClickable{ diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index b07679282..a8ed5d22e 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -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; + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/viewholders/AccountViewHolder.java b/mastodon/src/main/java/org/joinmastodon/android/ui/viewholders/AccountViewHolder.java index 08ddff4e4..5cec3b0fd 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/viewholders/AccountViewHolder.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/viewholders/AccountViewHolder.java @@ -228,6 +228,7 @@ public class AccountViewHolder extends BindableViewHolder 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); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/views/CheckableLinearLayout.java b/mastodon/src/main/java/org/joinmastodon/android/ui/views/CheckableLinearLayout.java index 193f4f0e2..785417442 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/views/CheckableLinearLayout.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/views/CheckableLinearLayout.java @@ -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); + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/views/CheckableRelativeLayout.java b/mastodon/src/main/java/org/joinmastodon/android/ui/views/CheckableRelativeLayout.java new file mode 100644 index 000000000..6e7f250bd --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/views/CheckableRelativeLayout.java @@ -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); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/views/M3Switch.java b/mastodon/src/main/java/org/joinmastodon/android/ui/views/M3Switch.java new file mode 100644 index 000000000..0e4bc02fb --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/views/M3Switch.java @@ -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); + } + } +} diff --git a/mastodon/src/main/res/color/mtrl_switch_thumb_icon_tint.xml b/mastodon/src/main/res/color/mtrl_switch_thumb_icon_tint.xml new file mode 100644 index 000000000..aeb41c288 --- /dev/null +++ b/mastodon/src/main/res/color/mtrl_switch_thumb_icon_tint.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + diff --git a/mastodon/src/main/res/color/mtrl_switch_thumb_tint.xml b/mastodon/src/main/res/color/mtrl_switch_thumb_tint.xml new file mode 100644 index 000000000..1e3f18502 --- /dev/null +++ b/mastodon/src/main/res/color/mtrl_switch_thumb_tint.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/mastodon/src/main/res/color/mtrl_switch_track_decoration_tint.xml b/mastodon/src/main/res/color/mtrl_switch_track_decoration_tint.xml new file mode 100644 index 000000000..78adf3cfa --- /dev/null +++ b/mastodon/src/main/res/color/mtrl_switch_track_decoration_tint.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/mastodon/src/main/res/color/mtrl_switch_track_tint.xml b/mastodon/src/main/res/color/mtrl_switch_track_tint.xml new file mode 100644 index 000000000..ec9f15836 --- /dev/null +++ b/mastodon/src/main/res/color/mtrl_switch_track_tint.xml @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/mastodon/src/main/res/drawable/bg_m3_bottom_bar.xml b/mastodon/src/main/res/drawable/bg_m3_bottom_bar.xml new file mode 100644 index 000000000..5a4b6e16a --- /dev/null +++ b/mastodon/src/main/res/drawable/bg_m3_bottom_bar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/bg_reported_stamp.xml b/mastodon/src/main/res/drawable/bg_reported_stamp.xml new file mode 100644 index 000000000..ccec8302a --- /dev/null +++ b/mastodon/src/main/res/drawable/bg_reported_stamp.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_block_20px.xml b/mastodon/src/main/res/drawable/ic_block_20px.xml new file mode 100644 index 000000000..7136c4c45 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_block_20px.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_check_24px.xml b/mastodon/src/main/res/drawable/ic_check_24px.xml new file mode 100644 index 000000000..2e04d2a5e --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_check_24px.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_person_remove_20px.xml b/mastodon/src/main/res/drawable/ic_person_remove_20px.xml new file mode 100644 index 000000000..9f22dcc71 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_person_remove_20px.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_volume_off_20px.xml b/mastodon/src/main/res/drawable/ic_volume_off_20px.xml new file mode 100644 index 000000000..94d8bec08 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_volume_off_20px.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/drawable/mtrl_switch_thumb.xml b/mastodon/src/main/res/drawable/mtrl_switch_thumb.xml new file mode 100644 index 000000000..1a41f6c03 --- /dev/null +++ b/mastodon/src/main/res/drawable/mtrl_switch_thumb.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mastodon/src/main/res/drawable/mtrl_switch_thumb_checked.xml b/mastodon/src/main/res/drawable/mtrl_switch_thumb_checked.xml new file mode 100644 index 000000000..1f1ade8ca --- /dev/null +++ b/mastodon/src/main/res/drawable/mtrl_switch_thumb_checked.xml @@ -0,0 +1,35 @@ + + + + + + + + + + diff --git a/mastodon/src/main/res/drawable/mtrl_switch_thumb_checked_pressed.xml b/mastodon/src/main/res/drawable/mtrl_switch_thumb_checked_pressed.xml new file mode 100644 index 000000000..b12bbe995 --- /dev/null +++ b/mastodon/src/main/res/drawable/mtrl_switch_thumb_checked_pressed.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/mastodon/src/main/res/drawable/mtrl_switch_thumb_checked_unchecked.xml b/mastodon/src/main/res/drawable/mtrl_switch_thumb_checked_unchecked.xml new file mode 100644 index 000000000..f7552d0b7 --- /dev/null +++ b/mastodon/src/main/res/drawable/mtrl_switch_thumb_checked_unchecked.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + diff --git a/mastodon/src/main/res/drawable/mtrl_switch_thumb_pressed.xml b/mastodon/src/main/res/drawable/mtrl_switch_thumb_pressed.xml new file mode 100644 index 000000000..e652dd189 --- /dev/null +++ b/mastodon/src/main/res/drawable/mtrl_switch_thumb_pressed.xml @@ -0,0 +1,35 @@ + + + + + + + + + + diff --git a/mastodon/src/main/res/drawable/mtrl_switch_thumb_pressed_checked.xml b/mastodon/src/main/res/drawable/mtrl_switch_thumb_pressed_checked.xml new file mode 100644 index 000000000..691c76bd8 --- /dev/null +++ b/mastodon/src/main/res/drawable/mtrl_switch_thumb_pressed_checked.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/mastodon/src/main/res/drawable/mtrl_switch_thumb_pressed_unchecked.xml b/mastodon/src/main/res/drawable/mtrl_switch_thumb_pressed_unchecked.xml new file mode 100644 index 000000000..8d5eac769 --- /dev/null +++ b/mastodon/src/main/res/drawable/mtrl_switch_thumb_pressed_unchecked.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/mastodon/src/main/res/drawable/mtrl_switch_thumb_unchecked.xml b/mastodon/src/main/res/drawable/mtrl_switch_thumb_unchecked.xml new file mode 100644 index 000000000..131b1fc26 --- /dev/null +++ b/mastodon/src/main/res/drawable/mtrl_switch_thumb_unchecked.xml @@ -0,0 +1,35 @@ + + + + + + + + + + diff --git a/mastodon/src/main/res/drawable/mtrl_switch_thumb_unchecked_checked.xml b/mastodon/src/main/res/drawable/mtrl_switch_thumb_unchecked_checked.xml new file mode 100644 index 000000000..9c5ca0d74 --- /dev/null +++ b/mastodon/src/main/res/drawable/mtrl_switch_thumb_unchecked_checked.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + diff --git a/mastodon/src/main/res/drawable/mtrl_switch_thumb_unchecked_pressed.xml b/mastodon/src/main/res/drawable/mtrl_switch_thumb_unchecked_pressed.xml new file mode 100644 index 000000000..a5d36a31d --- /dev/null +++ b/mastodon/src/main/res/drawable/mtrl_switch_thumb_unchecked_pressed.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/mastodon/src/main/res/drawable/mtrl_switch_track.xml b/mastodon/src/main/res/drawable/mtrl_switch_track.xml new file mode 100644 index 000000000..7b2a86fc0 --- /dev/null +++ b/mastodon/src/main/res/drawable/mtrl_switch_track.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/mastodon/src/main/res/drawable/mtrl_switch_track_decoration.xml b/mastodon/src/main/res/drawable/mtrl_switch_track_decoration.xml new file mode 100644 index 000000000..e9bd9bce9 --- /dev/null +++ b/mastodon/src/main/res/drawable/mtrl_switch_track_decoration.xml @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_emphasized.xml b/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_emphasized.xml new file mode 100644 index 000000000..01c9710aa --- /dev/null +++ b/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_emphasized.xml @@ -0,0 +1,19 @@ + + + + diff --git a/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_emphasized_accelerate.xml b/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_emphasized_accelerate.xml new file mode 100644 index 000000000..2527b6607 --- /dev/null +++ b/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_emphasized_accelerate.xml @@ -0,0 +1,22 @@ + + + + diff --git a/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_emphasized_decelerate.xml b/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_emphasized_decelerate.xml new file mode 100644 index 000000000..53179cfe3 --- /dev/null +++ b/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_emphasized_decelerate.xml @@ -0,0 +1,22 @@ + + + + diff --git a/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_linear.xml b/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_linear.xml new file mode 100644 index 000000000..a738c4682 --- /dev/null +++ b/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_linear.xml @@ -0,0 +1,22 @@ + + + + diff --git a/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_standard.xml b/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_standard.xml new file mode 100644 index 000000000..6fe4488c9 --- /dev/null +++ b/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_standard.xml @@ -0,0 +1,22 @@ + + + + diff --git a/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_standard_accelerate.xml b/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_standard_accelerate.xml new file mode 100644 index 000000000..513147958 --- /dev/null +++ b/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_standard_accelerate.xml @@ -0,0 +1,22 @@ + + + + diff --git a/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_standard_decelerate.xml b/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_standard_decelerate.xml new file mode 100644 index 000000000..d35341cfa --- /dev/null +++ b/mastodon/src/main/res/interpolator-v21/m3_sys_motion_easing_standard_decelerate.xml @@ -0,0 +1,22 @@ + + + + diff --git a/mastodon/src/main/res/layout/button_bar_one.xml b/mastodon/src/main/res/layout/button_bar_one.xml index f85ac6d94..0d27dc4a3 100644 --- a/mastodon/src/main/res/layout/button_bar_one.xml +++ b/mastodon/src/main/res/layout/button_bar_one.xml @@ -4,17 +4,15 @@ android:id="@+id/button_bar" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="?android:windowBackground" - android:outlineProvider="bounds" - android:elevation="3dp" + android:background="@drawable/bg_m3_bottom_bar" tools:showIn="@layout/fragment_report_choice">