AND-122 Mute, block, and domain block confirmation screens

This commit is contained in:
Grishka 2024-02-09 03:27:05 +03:00
parent e7295aac07
commit ad2ef39ace
27 changed files with 553 additions and 91 deletions

View File

@ -9,7 +9,7 @@ android {
applicationId "org.joinmastodon.android"
minSdk 23
targetSdk 33
versionCode 82
versionCode 84
versionName "2.2.4"
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"

View File

@ -29,8 +29,8 @@ import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.Translation;
import org.joinmastodon.android.ui.BetterItemAnimator;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.NonMutualPreReplySheet;
import org.joinmastodon.android.ui.OldPostPreReplySheet;
import org.joinmastodon.android.ui.sheets.NonMutualPreReplySheet;
import org.joinmastodon.android.ui.sheets.OldPostPreReplySheet;
import org.joinmastodon.android.ui.displayitems.AccountStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.HashtagStatusDisplayItem;

View File

@ -30,7 +30,7 @@ import org.joinmastodon.android.fragments.onboarding.OnboardingFollowSuggestions
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.PaginatedResponse;
import org.joinmastodon.android.ui.AccountSwitcherSheet;
import org.joinmastodon.android.ui.sheets.AccountSwitcherSheet;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.TabBar;

View File

@ -630,7 +630,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
}else if(id==R.id.open_in_browser){
UiUtils.launchWebBrowser(getActivity(), account.url);
}else if(id==R.id.block_domain){
UiUtils.confirmToggleBlockDomain(getActivity(), accountID, account.getDomain(), relationship.domainBlocking, ()->{
UiUtils.confirmToggleBlockDomain(getActivity(), accountID, account, relationship.domainBlocking, ()->{
relationship.domainBlocking=!relationship.domainBlocking;
updateRelationship();
});

View File

@ -24,7 +24,7 @@ import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.settings.SettingsMainFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.ui.AccountSwitcherSheet;
import org.joinmastodon.android.ui.sheets.AccountSwitcherSheet;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.io.File;

View File

@ -1,6 +1,5 @@
package org.joinmastodon.android.fragments.settings;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
@ -17,7 +16,7 @@ import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
import org.joinmastodon.android.model.viewmodel.ListItem;
import org.joinmastodon.android.ui.AccountSwitcherSheet;
import org.joinmastodon.android.ui.sheets.AccountSwitcherSheet;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.utils.HideableSingleViewRecyclerAdapter;
import org.joinmastodon.android.ui.utils.UiUtils;

View File

@ -0,0 +1,90 @@
package org.joinmastodon.android.ui.sheets;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.InsetDrawable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.ui.drawables.EmptyDrawable;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.ProgressBarButton;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.BottomSheet;
public abstract class AccountRestrictionConfirmationSheet extends BottomSheet{
private LinearLayout contentWrap;
protected Button cancelBtn;
protected ProgressBarButton confirmBtn, secondaryBtn;
protected TextView titleView, subtitleView;
protected ImageView icon;
protected boolean loading;
public AccountRestrictionConfirmationSheet(@NonNull Context context, Account user, ConfirmCallback confirmCallback){
super(context);
View content=context.getSystemService(LayoutInflater.class).inflate(R.layout.sheet_restrict_account, null);
setContentView(content);
setNavigationBarBackground(new ColorDrawable(UiUtils.alphaBlendColors(UiUtils.getThemeColor(context, R.attr.colorM3Surface),
UiUtils.getThemeColor(context, R.attr.colorM3Primary), 0.05f)), !UiUtils.isDarkTheme());
contentWrap=findViewById(R.id.content_wrap);
titleView=findViewById(R.id.title);
subtitleView=findViewById(R.id.text);
cancelBtn=findViewById(R.id.btn_cancel);
confirmBtn=findViewById(R.id.btn_confirm);
secondaryBtn=findViewById(R.id.btn_secondary);
icon=findViewById(R.id.icon);
contentWrap.setDividerDrawable(new EmptyDrawable(1, V.dp(8)));
contentWrap.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
confirmBtn.setOnClickListener(v->{
if(loading)
return;
loading=true;
confirmBtn.setProgressBarVisible(true);
confirmCallback.onConfirmed(this::dismiss, ()->{
confirmBtn.setProgressBarVisible(false);
loading=false;
});
});
cancelBtn.setOnClickListener(v->{
if(!loading)
dismiss();
});
}
protected void addRow(@DrawableRes int icon, CharSequence text){
TextView tv=new TextView(getContext());
tv.setTextAppearance(R.style.m3_body_large);
tv.setTextColor(UiUtils.getThemeColor(getContext(), R.attr.colorM3OnSurfaceVariant));
tv.setCompoundDrawableTintList(ColorStateList.valueOf(UiUtils.getThemeColor(getContext(), R.attr.colorM3Primary)));
tv.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
tv.setText(text);
InsetDrawable drawable=new InsetDrawable(getContext().getResources().getDrawable(icon, getContext().getTheme()), V.dp(8));
drawable.setBounds(0, 0, V.dp(40), V.dp(40));
tv.setCompoundDrawablesRelative(drawable, null, null, null);
tv.setCompoundDrawablePadding(V.dp(16));
contentWrap.addView(tv, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
protected void addRow(@DrawableRes int icon, @StringRes int text){
addRow(icon, getContext().getString(text));
}
public interface ConfirmCallback{
void onConfirmed(Runnable onSuccess, Runnable onError);
}
}

View File

@ -1,14 +1,12 @@
package org.joinmastodon.android.ui;
package org.joinmastodon.android.ui.sheets;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
@ -25,6 +23,9 @@ import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.HomeFragment;
import org.joinmastodon.android.fragments.SplashFragment;
import org.joinmastodon.android.ui.ClickableSingleViewRecyclerAdapter;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.CheckableRelativeLayout;
@ -37,7 +38,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.LinearLayoutManager;
import me.grishka.appkit.FragmentStackActivity;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;

View File

@ -0,0 +1,24 @@
package org.joinmastodon.android.ui.sheets;
import android.content.Context;
import android.view.View;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.Account;
import androidx.annotation.NonNull;
public class BlockAccountConfirmationSheet extends AccountRestrictionConfirmationSheet{
public BlockAccountConfirmationSheet(@NonNull Context context, Account user, ConfirmCallback confirmCallback){
super(context, user, confirmCallback);
titleView.setText(R.string.block_user_confirm_title);
confirmBtn.setText(R.string.do_block);
secondaryBtn.setVisibility(View.GONE);
icon.setImageResource(R.drawable.ic_block_24px);
subtitleView.setText(user.getDisplayUsername());
addRow(R.drawable.ic_campaign_24px, R.string.user_can_see_blocked);
addRow(R.drawable.ic_visibility_off_24px, R.string.user_cant_see_each_other_posts);
addRow(R.drawable.ic_alternate_email_24px, R.string.you_wont_see_user_mentions);
addRow(R.drawable.ic_reply_24px, R.string.user_cant_mention_or_follow_you);
}
}

View File

@ -0,0 +1,36 @@
package org.joinmastodon.android.ui.sheets;
import android.content.Context;
import android.view.View;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.Account;
import androidx.annotation.NonNull;
public class BlockDomainConfirmationSheet extends AccountRestrictionConfirmationSheet{
public BlockDomainConfirmationSheet(@NonNull Context context, Account user, ConfirmCallback confirmCallback, ConfirmCallback blockUserConfirmCallback){
super(context, user, confirmCallback);
titleView.setText(R.string.block_domain_confirm_title);
confirmBtn.setText(R.string.do_block_server);
secondaryBtn.setText(context.getString(R.string.block_user_x_instead, user.getDisplayUsername()));
icon.setImageResource(R.drawable.ic_domain_disabled_24px);
subtitleView.setText(user.getDomain());
addRow(R.drawable.ic_campaign_24px, R.string.users_cant_see_blocked);
addRow(R.drawable.ic_visibility_off_24px, R.string.you_wont_see_server_posts);
addRow(R.drawable.ic_person_remove_24px, R.string.server_followers_will_be_removed);
addRow(R.drawable.ic_reply_24px, R.string.server_cant_mention_or_follow_you);
addRow(R.drawable.ic_history_24px, R.string.server_can_interact_with_older);
secondaryBtn.setOnClickListener(v->{
if(loading)
return;
loading=true;
secondaryBtn.setProgressBarVisible(true);
blockUserConfirmCallback.onConfirmed(this::dismiss, ()->{
secondaryBtn.setProgressBarVisible(false);
loading=false;
});
});
}
}

View File

@ -0,0 +1,24 @@
package org.joinmastodon.android.ui.sheets;
import android.content.Context;
import android.view.View;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.Account;
import androidx.annotation.NonNull;
public class MuteAccountConfirmationSheet extends AccountRestrictionConfirmationSheet{
public MuteAccountConfirmationSheet(@NonNull Context context, Account user, ConfirmCallback confirmCallback){
super(context, user, confirmCallback);
titleView.setText(R.string.mute_user_confirm_title);
confirmBtn.setText(R.string.do_mute);
secondaryBtn.setVisibility(View.GONE);
icon.setImageResource(R.drawable.ic_volume_off_24px);
subtitleView.setText(user.getDisplayUsername());
addRow(R.drawable.ic_campaign_24px, R.string.user_wont_know_muted);
addRow(R.drawable.ic_visibility_off_24px, R.string.user_can_still_see_your_posts);
addRow(R.drawable.ic_alternate_email_24px, R.string.you_wont_see_user_mentions);
addRow(R.drawable.ic_reply_24px, R.string.user_can_mention_and_follow_you);
}
}

View File

@ -1,4 +1,4 @@
package org.joinmastodon.android.ui;
package org.joinmastodon.android.ui.sheets;
import android.annotation.SuppressLint;
import android.content.Context;
@ -14,6 +14,7 @@ import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.UiUtils;

View File

@ -1,4 +1,4 @@
package org.joinmastodon.android.ui;
package org.joinmastodon.android.ui.sheets;
import android.content.Context;

View File

@ -1,4 +1,4 @@
package org.joinmastodon.android.ui;
package org.joinmastodon.android.ui.sheets;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;

View File

@ -75,6 +75,10 @@ import org.joinmastodon.android.model.Relationship;
import org.joinmastodon.android.model.SearchResults;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.Snackbar;
import org.joinmastodon.android.ui.sheets.BlockAccountConfirmationSheet;
import org.joinmastodon.android.ui.sheets.BlockDomainConfirmationSheet;
import org.joinmastodon.android.ui.sheets.MuteAccountConfirmationSheet;
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
import org.joinmastodon.android.ui.text.SpacerSpan;
import org.parceler.Parcels;
@ -382,72 +386,142 @@ public class UiUtils{
}
public static void confirmToggleBlockUser(Activity activity, String accountID, Account account, boolean currentlyBlocked, Consumer<Relationship> resultCallback){
showConfirmationAlert(activity, activity.getString(currentlyBlocked ? R.string.confirm_unblock_title : R.string.confirm_block_title),
activity.getString(currentlyBlocked ? R.string.confirm_unblock : R.string.confirm_block, account.displayName),
activity.getString(currentlyBlocked ? R.string.do_unblock : R.string.do_block), ()->{
new SetAccountBlocked(account.id, !currentlyBlocked)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Relationship result){
resultCallback.accept(result);
if(!currentlyBlocked){
E.post(new RemoveAccountPostsEvent(accountID, account.id, false));
}
}
if(!currentlyBlocked){
new BlockAccountConfirmationSheet(activity, account, (onSuccess, onError)->{
new SetAccountBlocked(account.id, true)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Relationship result){
resultCallback.accept(result);
onSuccess.run();
E.post(new RemoveAccountPostsEvent(accountID, account.id, false));
}
@Override
public void onError(ErrorResponse error){
error.showToast(activity);
}
})
.wrapProgress(activity, R.string.loading, false)
.exec(accountID);
});
@Override
public void onError(ErrorResponse error){
error.showToast(activity);
onError.run();
}
})
.exec(accountID);
}).show();
}else{
new SetAccountBlocked(account.id, false)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Relationship result){
resultCallback.accept(result);
new Snackbar.Builder(activity)
.setText(activity.getString(R.string.unblocked_user_x, account.getDisplayUsername()))
.show();
}
@Override
public void onError(ErrorResponse error){
error.showToast(activity);
}
})
.wrapProgress(activity, R.string.loading, false)
.exec(accountID);
}
}
public static void confirmToggleBlockDomain(Activity activity, String accountID, String domain, boolean currentlyBlocked, Runnable resultCallback){
showConfirmationAlert(activity, activity.getString(currentlyBlocked ? R.string.confirm_unblock_domain_title : R.string.confirm_block_domain_title),
activity.getString(currentlyBlocked ? R.string.confirm_unblock : R.string.confirm_block, domain),
activity.getString(currentlyBlocked ? R.string.do_unblock : R.string.do_block), ()->{
new SetDomainBlocked(domain, !currentlyBlocked)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Object result){
resultCallback.run();
}
public static void confirmToggleBlockDomain(Activity activity, String accountID, Account account, boolean currentlyBlocked, Runnable resultCallback){
if(!currentlyBlocked){
new BlockDomainConfirmationSheet(activity, account, (onSuccess, onError)->{
new SetDomainBlocked(account.getDomain(), true)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Object result){
resultCallback.run();
onSuccess.run();
}
@Override
public void onError(ErrorResponse error){
error.showToast(activity);
}
})
.wrapProgress(activity, R.string.loading, false)
.exec(accountID);
});
@Override
public void onError(ErrorResponse error){
error.showToast(activity);
onError.run();
}
})
.exec(accountID);
}, (onSuccess, onError)->{
new SetAccountBlocked(account.id, true)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Relationship result){
resultCallback.run();
onSuccess.run();
E.post(new RemoveAccountPostsEvent(accountID, account.id, false));
}
@Override
public void onError(ErrorResponse error){
error.showToast(activity);
onError.run();
}
})
.exec(accountID);
}).show();
}else{
new SetDomainBlocked(account.getDomain(), false)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Object result){
resultCallback.run();
new Snackbar.Builder(activity)
.setText(activity.getString(R.string.unblocked_domain_x, account.getDomain()))
.show();
}
@Override
public void onError(ErrorResponse error){
error.showToast(activity);
}
})
.wrapProgress(activity, R.string.loading, false)
.exec(accountID);
}
}
public static void confirmToggleMuteUser(Activity activity, String accountID, Account account, boolean currentlyMuted, Consumer<Relationship> resultCallback){
showConfirmationAlert(activity, activity.getString(currentlyMuted ? R.string.confirm_unmute_title : R.string.confirm_mute_title),
activity.getString(currentlyMuted ? R.string.confirm_unmute : R.string.confirm_mute, account.displayName),
activity.getString(currentlyMuted ? R.string.do_unmute : R.string.do_mute), ()->{
new SetAccountMuted(account.id, !currentlyMuted)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Relationship result){
resultCallback.accept(result);
if(!currentlyMuted){
E.post(new RemoveAccountPostsEvent(accountID, account.id, false));
}
}
if(!currentlyMuted){
new MuteAccountConfirmationSheet(activity, account, (onSuccess, onError)->{
new SetAccountMuted(account.id, true)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Relationship result){
resultCallback.accept(result);
onSuccess.run();
E.post(new RemoveAccountPostsEvent(accountID, account.id, false));
}
@Override
public void onError(ErrorResponse error){
error.showToast(activity);
}
})
.wrapProgress(activity, R.string.loading, false)
.exec(accountID);
});
@Override
public void onError(ErrorResponse error){
error.showToast(activity);
onError.run();
}
})
.exec(accountID);
}).show();
}else{
new SetAccountMuted(account.id, false)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Relationship result){
resultCallback.accept(result);
new Snackbar.Builder(activity)
.setText(activity.getString(R.string.unmuted_user_x, account.getDisplayUsername()))
.show();
}
@Override
public void onError(ErrorResponse error){
error.showToast(activity);
}
})
.wrapProgress(activity, R.string.loading, false)
.exec(accountID);
}
}
public static void confirmDeletePost(Activity activity, String accountID, Status status, Consumer<Status> resultCallback){

View File

@ -28,7 +28,6 @@ import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import androidx.annotation.NonNull;

View File

@ -252,7 +252,7 @@ public class AccountViewHolder extends BindableViewHolder<AccountViewModel> impl
}else if(id==R.id.open_in_browser){
UiUtils.launchWebBrowser(fragment.getActivity(), account.url);
}else if(id==R.id.block_domain){
UiUtils.confirmToggleBlockDomain(fragment.getActivity(), accountID, account.getDomain(), relationship.domainBlocking, ()->{
UiUtils.confirmToggleBlockDomain(fragment.getActivity(), accountID, account, relationship.domainBlocking, ()->{
relationship.domainBlocking=!relationship.domainBlocking;
bindRelationship();
});

View File

@ -1,23 +1,42 @@
package org.joinmastodon.android.ui.views;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import org.joinmastodon.android.R;
public class ProgressBarButton extends Button{
private boolean textVisible=true;
private ProgressBar progressBar;
private int progressBarID;
public ProgressBarButton(Context context){
super(context);
this(context, null);
}
public ProgressBarButton(Context context, AttributeSet attrs){
super(context, attrs);
this(context, attrs, 0);
}
public ProgressBarButton(Context context, AttributeSet attrs, int defStyleAttr){
super(context, attrs, defStyleAttr);
public ProgressBarButton(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
TypedArray ta=context.obtainStyledAttributes(attrs, R.styleable.ProgressBarButton);
progressBarID=ta.getResourceId(R.styleable.ProgressBarButton_progressBar, 0);
ta.recycle();
}
@Override
protected void onAttachedToWindow(){
super.onAttachedToWindow();
if(progressBarID!=0){
progressBar=((ViewGroup)getParent()).findViewById(progressBarID);
}
}
public void setTextVisible(boolean textVisible){
@ -29,6 +48,19 @@ public class ProgressBarButton extends Button{
return textVisible;
}
public void setProgressBarVisible(boolean visible){
if(progressBar==null)
throw new IllegalStateException("progressBar is not set");
if(visible){
setTextVisible(false);
progressBar.setIndeterminateTintList(getTextColors());
progressBar.setVisibility(View.VISIBLE);
}else{
setTextVisible(true);
progressBar.setVisibility(View.GONE);
}
}
@Override
protected void onDraw(Canvas canvas){
if(textVisible){

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,22Q9.95,22 8.125,21.212Q6.3,20.425 4.938,19.062Q3.575,17.7 2.788,15.875Q2,14.05 2,12Q2,9.925 2.788,8.113Q3.575,6.3 4.938,4.938Q6.3,3.575 8.125,2.787Q9.95,2 12,2Q14.075,2 15.887,2.787Q17.7,3.575 19.062,4.938Q20.425,6.3 21.212,8.113Q22,9.925 22,12V13.45Q22,14.925 20.988,15.962Q19.975,17 18.5,17Q17.6,17 16.825,16.6Q16.05,16.2 15.55,15.55Q14.875,16.225 13.963,16.613Q13.05,17 12,17Q9.925,17 8.463,15.537Q7,14.075 7,12Q7,9.925 8.463,8.462Q9.925,7 12,7Q14.075,7 15.538,8.462Q17,9.925 17,12V13.45Q17,14.175 17.45,14.587Q17.9,15 18.5,15Q19.1,15 19.55,14.587Q20,14.175 20,13.45V12Q20,8.725 17.637,6.362Q15.275,4 12,4Q8.725,4 6.362,6.362Q4,8.725 4,12Q4,15.275 6.362,17.637Q8.725,20 12,20H17V22ZM12,15Q13.25,15 14.125,14.125Q15,13.25 15,12Q15,10.75 14.125,9.875Q13.25,9 12,9Q10.75,9 9.875,9.875Q9,10.75 9,12Q9,13.25 9.875,14.125Q10.75,15 12,15Z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,22Q9.925,22 8.1,21.212Q6.275,20.425 4.925,19.075Q3.575,17.725 2.788,15.9Q2,14.075 2,12Q2,9.925 2.788,8.1Q3.575,6.275 4.925,4.925Q6.275,3.575 8.1,2.787Q9.925,2 12,2Q14.075,2 15.9,2.787Q17.725,3.575 19.075,4.925Q20.425,6.275 21.212,8.1Q22,9.925 22,12Q22,14.075 21.212,15.9Q20.425,17.725 19.075,19.075Q17.725,20.425 15.9,21.212Q14.075,22 12,22ZM12,20Q15.35,20 17.675,17.675Q20,15.35 20,12Q20,10.65 19.562,9.4Q19.125,8.15 18.3,7.1L7.1,18.3Q8.15,19.125 9.4,19.562Q10.65,20 12,20ZM5.7,16.9 L16.9,5.7Q15.85,4.875 14.6,4.438Q13.35,4 12,4Q8.65,4 6.325,6.325Q4,8.65 4,12Q4,13.35 4.438,14.6Q4.875,15.85 5.7,16.9Z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M22,19.15 L20,17.15V9H11.85L10,7.15V5H7.85L5.85,3H12V7H22ZM20.5,23.3 L18.15,21H2V4.8L0.7,3.5L2.1,2.1L21.9,21.9ZM4,19H6V17H4ZM4,15H6V13H4ZM4,11H6V9H4ZM8,19H10V17H8ZM8,15H10V13H8ZM12,19H16.15L14.15,17H12ZM18,13H16V11H18Z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M19,19V15Q19,13.75 18.125,12.875Q17.25,12 16,12H6.8L10.4,15.6L9,17L3,11L9,5L10.4,6.4L6.8,10H16Q18.075,10 19.538,11.462Q21,12.925 21,15V19Z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M19.8,22.6 L16.775,19.575Q16.15,19.975 15.45,20.263Q14.75,20.55 14,20.725V18.675Q14.35,18.55 14.688,18.425Q15.025,18.3 15.325,18.125L12,14.8V20L7,15H3V9H6.2L1.4,4.2L2.8,2.8L21.2,21.2ZM19.6,16.8 L18.15,15.35Q18.575,14.575 18.788,13.725Q19,12.875 19,11.975Q19,9.625 17.625,7.775Q16.25,5.925 14,5.275V3.225Q17.1,3.925 19.05,6.362Q21,8.8 21,11.975Q21,13.3 20.638,14.525Q20.275,15.75 19.6,16.8ZM9.1,11.9ZM16.25,13.45 L14,11.2V7.95Q15.175,8.5 15.838,9.6Q16.5,10.7 16.5,12Q16.5,12.375 16.438,12.738Q16.375,13.1 16.25,13.45ZM12,9.2 L9.4,6.6 12,4ZM10,15.15V12.8L8.2,11H5V13H7.85Z"/>
</vector>

View File

@ -25,9 +25,9 @@
android:layout_alignParentStart="true"
android:layout_marginEnd="16dp"
android:background="@drawable/white_circle"
android:backgroundTint="?colorM3PrimaryContainer"
android:backgroundTint="?colorM3SecondaryContainer"
android:scaleType="center"
android:tint="?colorM3OnPrimaryContainer"
android:tint="?colorM3OnSecondaryContainer"
tools:src="@drawable/ic_waving_hand_24px"
android:importantForAccessibility="no"/>

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<org.joinmastodon.android.ui.views.CustomScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/bg_bottom_sheet"
android:outlineProvider="background"
android:elevation="1dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="16dp">
<View
android:id="@+id/handle"
android:layout_width="match_parent"
android:layout_height="36dp"
android:background="@drawable/bg_bottom_sheet_handle"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginEnd="16dp"
android:layout_centerVertical="true"
android:background="@drawable/white_circle"
android:backgroundTint="?colorM3SecondaryContainer"
android:scaleType="center"
android:tint="?colorM3OnSecondaryContainer"
tools:src="@drawable/ic_waving_hand_24px"
android:importantForAccessibility="no"/>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/icon"
android:textAppearance="@style/m3_title_large"
android:fontFamily="sans-serif"
android:textColor="?colorM3OnSurface"
tools:text="@string/mute_user_confirm_title"/>
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/icon"
android:layout_below="@id/title"
android:textAppearance="@style/m3_body_medium"
android:textColor="?colorM3OnSurfaceVariant"
tools:text="\@username"/>
</RelativeLayout>
<LinearLayout
android:id="@+id/content_wrap"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="32dp"
android:orientation="vertical"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp">
<org.joinmastodon.android.ui.views.ProgressBarButton
android:id="@+id/btn_confirm"
android:layout_width="match_parent"
android:layout_height="40dp"
style="@style/Widget.Mastodon.M3.Button.Filled"
app:progressBar="@id/confirm_progress"
tools:text="@string/got_it"/>
<ProgressBar
android:id="@+id/confirm_progress"
style="?android:progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:elevation="10dp"
android:indeterminate="true"
android:outlineProvider="none"
android:visibility="gone"
tools:visibility="visible"/>
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp">
<org.joinmastodon.android.ui.views.ProgressBarButton
android:id="@+id/btn_secondary"
android:layout_width="match_parent"
android:layout_height="40dp"
style="@style/Widget.Mastodon.M3.Button.Tonal"
app:progressBar="@id/secondary_progress"
tools:text="@string/got_it"/>
<ProgressBar
android:id="@+id/secondary_progress"
style="?android:progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:elevation="10dp"
android:indeterminate="true"
android:outlineProvider="none"
android:visibility="gone"
tools:visibility="visible"/>
</FrameLayout>
<Button
android:id="@+id/btn_cancel"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginBottom="8dp"
style="@style/Widget.Mastodon.M3.Button.Text"
android:text="@string/cancel"/>
</LinearLayout>
</org.joinmastodon.android.ui.views.CustomScrollView>

View File

@ -59,4 +59,8 @@
<attr name="aspectRatio" format="float"/>
<attr name="useHeight" format="boolean"/>
</declare-styleable>
<declare-styleable name="ProgressBarButton">
<attr name="progressBar" format="reference"/>
</declare-styleable>
</resources>

View File

@ -95,19 +95,9 @@
<item quantity="other">%,d votes</item>
</plurals>
<string name="poll_closed">Closed</string>
<string name="confirm_mute_title">Mute Account</string>
<string name="confirm_mute">Confirm to mute %s</string>
<string name="do_mute">Mute</string>
<string name="confirm_unmute_title">Unmute Account</string>
<string name="confirm_unmute">Confirm to unmute %s</string>
<string name="do_unmute">Unmute</string>
<string name="confirm_block_title">Block Account</string>
<string name="confirm_block_domain_title">Block Domain</string>
<string name="confirm_block">Confirm to block %s</string>
<string name="do_block">Block</string>
<string name="confirm_unblock_title">Unblock Account</string>
<string name="confirm_unblock_domain_title">Unblock Domain</string>
<string name="confirm_unblock">Confirm to unblock %s</string>
<string name="do_unblock">Unblock</string>
<string name="button_blocked">Blocked</string>
<string name="action_vote">Vote</string>
@ -679,4 +669,24 @@
<string name="this_invite_has_expired">This invite link has expired.</string>
<string name="invite_link_pasted">Link pasted from your clipboard.</string>
<string name="need_invite_to_join_server">To join %s, youll need an invite link from an existing user.</string>
<string name="mute_user_confirm_title">Mute user?</string>
<string name="user_wont_know_muted">They wont know theyve been muted.</string>
<string name="user_can_still_see_your_posts">They can still see your posts, but you wont see theirs.</string>
<string name="you_wont_see_user_mentions">You wont see mentions to them.</string>
<string name="user_can_mention_and_follow_you">They can mention and follow you, but you wont see them.</string>
<string name="unmuted_user_x">Unmuted %s</string>
<string name="block_user_confirm_title">Block user?</string>
<string name="user_can_see_blocked">They can see that theyre blocked.</string>
<string name="user_cant_see_each_other_posts">They cant see your posts and you wont see theirs.</string>
<string name="user_cant_mention_or_follow_you">They cant mention or follow you.</string>
<string name="unblocked_user_x">Unblocked %s</string>
<string name="block_domain_confirm_title">Block domain?</string>
<string name="do_block_server">Block server</string>
<string name="block_user_x_instead">Block %s instead</string>
<string name="users_cant_see_blocked">They wont know theyve been blocked.</string>
<string name="you_wont_see_server_posts">You wont see any posts from users on this server.</string>
<string name="server_followers_will_be_removed">Your followers from this server will be removed.</string>
<string name="server_cant_mention_or_follow_you">Nobody from this server can mention or follow you.</string>
<string name="server_can_interact_with_older">People from this server can interact with your old posts.</string>
<string name="unblocked_domain_x">Unblocked domain %s</string>
</resources>