chore: readd all the new bottom sheet code
This commit is contained in:
@@ -13,7 +13,7 @@ import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||
import org.joinmastodon.android.ui.AccountSwitcherSheet;
|
||||
import org.joinmastodon.android.ui.sheets.AccountSwitcherSheet;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.jsoup.internal.StringUtil;
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ import android.app.Fragment;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.assist.AssistContent;
|
||||
import android.graphics.drawable.RippleDrawable;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Outline;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
@@ -37,7 +35,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -18,7 +17,7 @@ import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.joinmastodon.android.ui;
|
||||
package org.joinmastodon.android.ui.sheets;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
@@ -8,7 +8,6 @@ import android.graphics.drawable.Animatable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
@@ -27,8 +26,10 @@ import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.HomeFragment;
|
||||
import org.joinmastodon.android.fragments.SplashFragment;
|
||||
import org.joinmastodon.android.fragments.onboarding.CustomWelcomeFragment;
|
||||
import org.joinmastodon.android.ui.ClickableSingleViewRecyclerAdapter;
|
||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||
import org.joinmastodon.android.ui.OutlineProviders;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.ui.views.CheckableRelativeLayout;
|
||||
|
||||
@@ -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_fluent_shield_24_regular);
|
||||
subtitleView.setText(user.getDisplayUsername());
|
||||
addRow(R.drawable.ic_campaign_24px, R.string.user_can_see_blocked);
|
||||
addRow(R.drawable.ic_fluent_eye_off_24_regular, R.string.user_cant_see_each_other_posts);
|
||||
addRow(R.drawable.ic_fluent_mention_24_regular, R.string.you_wont_see_user_mentions);
|
||||
addRow(R.drawable.ic_fluent_arrow_reply_24_regular, R.string.user_cant_mention_or_follow_you);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package org.joinmastodon.android.ui.sheets;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
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.M3AlertDialogBuilder;
|
||||
import org.joinmastodon.android.ui.Snackbar;
|
||||
import org.joinmastodon.android.ui.text.LinkSpan;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.ui.views.RippleAnimationTextView;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.nodes.Node;
|
||||
import org.jsoup.nodes.TextNode;
|
||||
import org.jsoup.select.NodeVisitor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import me.grishka.appkit.views.BottomSheet;
|
||||
|
||||
public class DecentralizationExplainerSheet extends BottomSheet{
|
||||
private final String handleStr;
|
||||
|
||||
public DecentralizationExplainerSheet(@NonNull Context context, String accountID, Account account){
|
||||
super(context);
|
||||
View content=context.getSystemService(LayoutInflater.class).inflate(R.layout.sheet_decentralization_info, null);
|
||||
setContentView(content);
|
||||
setNavigationBarBackground(new ColorDrawable(UiUtils.alphaBlendColors(UiUtils.getThemeColor(context, R.attr.colorM3Surface),
|
||||
UiUtils.getThemeColor(context, R.attr.colorM3Primary), 0.05f)), !UiUtils.isDarkTheme());
|
||||
|
||||
TextView handleTitle=findViewById(R.id.handle_title);
|
||||
RippleAnimationTextView handle=findViewById(R.id.handle);
|
||||
TextView usernameExplanation=findViewById(R.id.username_text);
|
||||
TextView serverExplanation=findViewById(R.id.server_text);
|
||||
TextView handleExplanation=findViewById(R.id.handle_explanation);
|
||||
findViewById(R.id.btn_cancel).setOnClickListener(v->dismiss());
|
||||
|
||||
String domain=account.getDomain();
|
||||
if(TextUtils.isEmpty(domain))
|
||||
domain=AccountSessionManager.get(accountID).domain;
|
||||
handleStr="@"+account.username+"@"+domain;
|
||||
boolean isSelf=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||
|
||||
handleTitle.setText(isSelf ? R.string.handle_title_own : R.string.handle_title);
|
||||
handle.setText(handleStr);
|
||||
usernameExplanation.setText(isSelf ? R.string.handle_username_explanation_own : R.string.handle_username_explanation);
|
||||
serverExplanation.setText(isSelf ? R.string.handle_server_explanation_own : R.string.handle_server_explanation);
|
||||
|
||||
String explanation=context.getString(isSelf ? R.string.handle_explanation_own : R.string.handle_explanation);
|
||||
SpannableStringBuilder ssb=new SpannableStringBuilder();
|
||||
Jsoup.parseBodyFragment(explanation).body().traverse(new NodeVisitor(){
|
||||
private int spanStart;
|
||||
@Override
|
||||
public void head(Node node, int depth){
|
||||
if(node instanceof TextNode tn){
|
||||
ssb.append(tn.text());
|
||||
}else if(node instanceof Element){
|
||||
spanStart=ssb.length();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tail(Node node, int depth){
|
||||
if(node instanceof Element){
|
||||
ssb.setSpan(new LinkSpan("", DecentralizationExplainerSheet.this::showActivityPubAlert, LinkSpan.Type.CUSTOM, null, null, null), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
}
|
||||
});
|
||||
handleExplanation.setText(ssb);
|
||||
|
||||
findViewById(R.id.handle_wrap).setOnClickListener(v->{
|
||||
context.getSystemService(ClipboardManager.class).setPrimaryClip(ClipData.newPlainText(null, handleStr));
|
||||
if(UiUtils.needShowClipboardToast()){
|
||||
new Snackbar.Builder(context)
|
||||
.setText(R.string.handle_copied)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
String _domain=domain;
|
||||
findViewById(R.id.username_row).setOnClickListener(v->handle.animate(1, account.username.length()+1));
|
||||
findViewById(R.id.server_row).setOnClickListener(v->handle.animate(handleStr.length()-_domain.length(), handleStr.length()));
|
||||
}
|
||||
|
||||
private void showActivityPubAlert(LinkSpan s){
|
||||
new M3AlertDialogBuilder(getContext())
|
||||
.setTitle(R.string.what_is_activitypub_title)
|
||||
.setMessage(R.string.what_is_activitypub)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
@@ -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_fluent_speaker_mute_24_regular);
|
||||
subtitleView.setText(user.getDisplayUsername());
|
||||
addRow(R.drawable.ic_campaign_24px, R.string.user_wont_know_muted);
|
||||
addRow(R.drawable.ic_fluent_eye_off_24_regular, R.string.user_can_still_see_your_posts);
|
||||
addRow(R.drawable.ic_fluent_mention_24_regular, R.string.you_wont_see_user_mentions);
|
||||
addRow(R.drawable.ic_fluent_arrow_reply_24_regular, R.string.user_can_mention_and_follow_you);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package org.joinmastodon.android.ui.sheets;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
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;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import me.grishka.appkit.imageloader.ViewImageLoader;
|
||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class NonMutualPreReplySheet extends PreReplySheet{
|
||||
private boolean fullBioShown=false;
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public NonMutualPreReplySheet(@NonNull Context context, ResultListener resultListener, Account account, String accountID){
|
||||
super(context, resultListener);
|
||||
icon.setImageResource(R.drawable.ic_waving_hand_24px);
|
||||
title.setText(R.string.non_mutual_sheet_title);
|
||||
text.setText(R.string.non_mutual_sheet_text);
|
||||
|
||||
LinearLayout userInfo=new LinearLayout(context);
|
||||
userInfo.setOrientation(LinearLayout.HORIZONTAL);
|
||||
userInfo.setBackgroundResource(R.drawable.bg_user_info);
|
||||
UiUtils.setAllPaddings(userInfo, 12);
|
||||
|
||||
ImageView ava=new ImageView(context);
|
||||
ava.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
ava.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
|
||||
ava.setOutlineProvider(OutlineProviders.roundedRect(12));
|
||||
ava.setClipToOutline(true);
|
||||
ava.setForeground(context.getResources().getDrawable(R.drawable.fg_user_info_ava, context.getTheme()));
|
||||
userInfo.addView(ava, UiUtils.makeLayoutParams(56, 56, 0, 0, 12, 0));
|
||||
ViewImageLoader.loadWithoutAnimation(ava, context.getResources().getDrawable(R.drawable.image_placeholder), new UrlImageLoaderRequest(account.avatarStatic, V.dp(56), V.dp(56)));
|
||||
|
||||
LinearLayout nameAndFields=new LinearLayout(context);
|
||||
nameAndFields.setOrientation(LinearLayout.VERTICAL);
|
||||
nameAndFields.setMinimumHeight(V.dp(56));
|
||||
nameAndFields.setGravity(Gravity.CENTER_VERTICAL);
|
||||
userInfo.addView(nameAndFields, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
TextView name=new TextView(context);
|
||||
name.setSingleLine();
|
||||
name.setEllipsize(TextUtils.TruncateAt.END);
|
||||
name.setTextAppearance(R.style.m3_title_medium);
|
||||
name.setTextColor(UiUtils.getThemeColor(context, R.attr.colorM3OnSurface));
|
||||
if(AccountSessionManager.get(accountID).getLocalPreferences().customEmojiInNames){
|
||||
name.setText(HtmlParser.parseCustomEmoji(account.displayName, account.emojis));
|
||||
UiUtils.loadCustomEmojiInTextView(name);
|
||||
}else{
|
||||
name.setText(account.displayName);
|
||||
}
|
||||
name.setGravity(Gravity.CENTER_VERTICAL | Gravity.START);
|
||||
nameAndFields.addView(name, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(24)));
|
||||
if(!TextUtils.isEmpty(account.note)){
|
||||
CharSequence strippedBio=HtmlParser.parseCustomEmoji(HtmlParser.stripAndRemoveInvisibleSpans(account.note), account.emojis);
|
||||
TextView bioShort=new TextView(context);
|
||||
bioShort.setTextAppearance(R.style.m3_body_medium);
|
||||
bioShort.setTextColor(UiUtils.getThemeColor(context, R.attr.colorM3Secondary));
|
||||
bioShort.setMaxLines(2);
|
||||
bioShort.setEllipsize(TextUtils.TruncateAt.END);
|
||||
bioShort.setText(strippedBio);
|
||||
nameAndFields.addView(bioShort, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
TextView bioFull=new TextView(context);
|
||||
bioFull.setTextAppearance(R.style.m3_body_medium);
|
||||
bioFull.setTextColor(UiUtils.getThemeColor(context, R.attr.colorM3Secondary));
|
||||
bioFull.setText(strippedBio);
|
||||
bioFull.setVisibility(View.GONE);
|
||||
nameAndFields.addView(bioFull, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
nameAndFields.setOnClickListener(v->{
|
||||
UiUtils.beginLayoutTransition((ViewGroup) getWindow().getDecorView());
|
||||
fullBioShown=!fullBioShown;
|
||||
if(fullBioShown){
|
||||
bioFull.setVisibility(View.VISIBLE);
|
||||
bioShort.setVisibility(View.GONE);
|
||||
}else{
|
||||
bioFull.setVisibility(View.GONE);
|
||||
bioShort.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
UiUtils.loadCustomEmojiInTextView(bioShort);
|
||||
UiUtils.loadCustomEmojiInTextView(bioFull);
|
||||
}else{
|
||||
TextView username=new TextView(context);
|
||||
username.setTextAppearance(R.style.m3_body_medium);
|
||||
username.setTextColor(UiUtils.getThemeColor(context, R.attr.colorM3Secondary));
|
||||
username.setSingleLine();
|
||||
username.setEllipsize(TextUtils.TruncateAt.END);
|
||||
username.setText(account.getDisplayUsername());
|
||||
username.setGravity(Gravity.CENTER_VERTICAL | Gravity.START);
|
||||
nameAndFields.addView(username, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(20)));
|
||||
}
|
||||
|
||||
contentWrap.addView(userInfo, UiUtils.makeLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0, 0, 8));
|
||||
|
||||
for(int i=0;i<3;i++){
|
||||
View item=context.getSystemService(LayoutInflater.class).inflate(R.layout.item_other_numbered_rule, contentWrap, false);
|
||||
TextView number=item.findViewById(R.id.number);
|
||||
number.setText(String.format("%d", i+1));
|
||||
TextView title=item.findViewById(R.id.title);
|
||||
TextView text=item.findViewById(R.id.text);
|
||||
title.setText(switch(i){
|
||||
case 0 -> R.string.non_mutual_title1;
|
||||
case 1 -> R.string.non_mutual_title2;
|
||||
case 2 -> R.string.non_mutual_title3;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+i);
|
||||
});
|
||||
text.setText(switch(i){
|
||||
case 0 -> R.string.non_mutual_text1;
|
||||
case 1 -> R.string.non_mutual_text2;
|
||||
case 2 -> R.string.non_mutual_text3;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+i);
|
||||
});
|
||||
contentWrap.addView(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.joinmastodon.android.ui.sheets;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class OldPostPreReplySheet extends PreReplySheet{
|
||||
public OldPostPreReplySheet(@NonNull Context context, ResultListener resultListener, Status status){
|
||||
super(context, resultListener);
|
||||
int months=(int)status.createdAt.atZone(ZoneId.systemDefault()).until(ZonedDateTime.now(), ChronoUnit.MONTHS);
|
||||
String monthsStr=months>24 ? context.getString(R.string.more_than_two_years) : context.getResources().getQuantityString(R.plurals.x_months, months, months);
|
||||
title.setText(context.getString(R.string.old_post_sheet_title, monthsStr));
|
||||
text.setText(R.string.old_post_sheet_text);
|
||||
icon.setImageResource(R.drawable.ic_fluent_history_24_regular);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.joinmastodon.android.ui.sheets;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
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.ui.utils.UiUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import me.grishka.appkit.views.BottomSheet;
|
||||
|
||||
public abstract class PreReplySheet extends BottomSheet{
|
||||
protected ImageView icon;
|
||||
protected TextView title, text;
|
||||
protected Button gotItButton, dontRemindButton;
|
||||
protected LinearLayout contentWrap;
|
||||
|
||||
public PreReplySheet(@NonNull Context context, ResultListener resultListener){
|
||||
super(context);
|
||||
|
||||
View content=context.getSystemService(LayoutInflater.class).inflate(R.layout.sheet_pre_reply, null);
|
||||
setContentView(content);
|
||||
|
||||
setNavigationBarBackground(new ColorDrawable(UiUtils.alphaBlendColors(UiUtils.getThemeColor(context, R.attr.colorM3Surface),
|
||||
UiUtils.getThemeColor(context, R.attr.colorM3Primary), 0.05f)), !UiUtils.isDarkTheme());
|
||||
|
||||
icon=findViewById(R.id.icon);
|
||||
title=findViewById(R.id.title);
|
||||
text=findViewById(R.id.text);
|
||||
gotItButton=findViewById(R.id.btn_got_it);
|
||||
dontRemindButton=findViewById(R.id.btn_dont_remind_again);
|
||||
contentWrap=findViewById(R.id.content_wrap);
|
||||
|
||||
gotItButton.setOnClickListener(v->{
|
||||
dismiss();
|
||||
resultListener.onButtonClicked(false);
|
||||
});
|
||||
dontRemindButton.setOnClickListener(v->{
|
||||
dismiss();
|
||||
resultListener.onButtonClicked(true);
|
||||
});
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ResultListener{
|
||||
void onButtonClicked(boolean notAgain);
|
||||
}
|
||||
}
|
||||
@@ -1847,6 +1847,10 @@ public class UiUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean needShowClipboardToast(){
|
||||
return Build.VERSION.SDK_INT<=Build.VERSION_CODES.S_V2;
|
||||
}
|
||||
|
||||
public static void setAllPaddings(View view, int paddingDp){
|
||||
int pad=V.dp(paddingDp);
|
||||
view.setPadding(pad, pad, pad, pad);
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
package org.joinmastodon.android.ui.views;
|
||||
|
||||
import android.animation.ArgbEvaluator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.text.Layout;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.dynamicanimation.animation.FloatValueHolder;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
import me.grishka.appkit.utils.CustomViewHelper;
|
||||
|
||||
public class RippleAnimationTextView extends TextView implements CustomViewHelper{
|
||||
private final Paint animationPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private CharacterAnimationState[] charStates;
|
||||
private final ArgbEvaluator colorEvaluator=new ArgbEvaluator();
|
||||
private int runningAnimCount=0;
|
||||
private Runnable[] delayedAnimations1, delayedAnimations2;
|
||||
|
||||
public RippleAnimationTextView(Context context){
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public RippleAnimationTextView(Context context, AttributeSet attrs){
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public RippleAnimationTextView(Context context, AttributeSet attrs, int defStyle){
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter){
|
||||
super.onTextChanged(text, start, lengthBefore, lengthAfter);
|
||||
if(charStates!=null){
|
||||
for(CharacterAnimationState state:charStates){
|
||||
state.colorAnimation.cancel();
|
||||
state.shadowAnimation.cancel();
|
||||
state.scaleAnimation.cancel();
|
||||
}
|
||||
for(Runnable r:delayedAnimations1){
|
||||
if(r!=null)
|
||||
removeCallbacks(r);
|
||||
}
|
||||
for(Runnable r:delayedAnimations2){
|
||||
if(r!=null)
|
||||
removeCallbacks(r);
|
||||
}
|
||||
}
|
||||
charStates=new CharacterAnimationState[lengthAfter];
|
||||
delayedAnimations1=new Runnable[lengthAfter];
|
||||
delayedAnimations2=new Runnable[lengthAfter];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas){
|
||||
if(runningAnimCount==0 && !areThereDelayedAnimations()){
|
||||
super.onDraw(canvas);
|
||||
return;
|
||||
}
|
||||
Layout layout=getLayout();
|
||||
animationPaint.set(getPaint());
|
||||
CharSequence text=layout.getText();
|
||||
for(int i=0;i<layout.getLineCount();i++){
|
||||
int baseline=layout.getLineBaseline(i);
|
||||
for(int offset=layout.getLineStart(i); offset<layout.getLineEnd(i); offset++){
|
||||
float x=layout.getPrimaryHorizontal(offset);
|
||||
CharacterAnimationState state=charStates[offset];
|
||||
if(state==null || state.scaleAnimation==null){
|
||||
animationPaint.setColor(getCurrentTextColor());
|
||||
animationPaint.clearShadowLayer();
|
||||
canvas.drawText(text, offset, offset+1, x, baseline, animationPaint);
|
||||
}else{
|
||||
animationPaint.setColor((int)colorEvaluator.evaluate(Math.max(0, Math.min(1, state.color.getValue())), getCurrentTextColor(), getLinkTextColors().getDefaultColor()));
|
||||
float scale=state.scale.getValue();
|
||||
int shadowAlpha=Math.round(255*Math.max(0, Math.min(1, state.shadowAlpha.getValue())));
|
||||
animationPaint.setShadowLayer(dp(4), 0, dp(3), (getPaint().linkColor & 0xFFFFFF) | (shadowAlpha << 24));
|
||||
canvas.save();
|
||||
canvas.scale(scale, scale, x, baseline);
|
||||
canvas.drawText(text, offset, offset+1, x, baseline, animationPaint);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void animate(int startIndex, int endIndex){
|
||||
for(int i=startIndex;i<endIndex;i++){
|
||||
CharacterAnimationState _state=charStates[i];
|
||||
if(_state==null){
|
||||
_state=charStates[i]=new CharacterAnimationState();
|
||||
}
|
||||
CharacterAnimationState state=_state;
|
||||
int finalI=i;
|
||||
postOnAnimationDelayed(()->{
|
||||
if(!state.colorAnimation.isRunning())
|
||||
runningAnimCount++;
|
||||
state.colorAnimation.animateToFinalPosition(1f);
|
||||
if(!state.shadowAnimation.isRunning())
|
||||
runningAnimCount++;
|
||||
state.shadowAnimation.animateToFinalPosition(0.3f);
|
||||
if(!state.scaleAnimation.isRunning())
|
||||
runningAnimCount++;
|
||||
state.scaleAnimation.animateToFinalPosition(1.2f);
|
||||
invalidate();
|
||||
|
||||
if(delayedAnimations1[finalI]!=null)
|
||||
removeCallbacks(delayedAnimations1[finalI]);
|
||||
if(delayedAnimations2[finalI]!=null)
|
||||
removeCallbacks(delayedAnimations2[finalI]);
|
||||
Runnable delay1=()->{
|
||||
if(!state.colorAnimation.isRunning())
|
||||
runningAnimCount++;
|
||||
state.colorAnimation.animateToFinalPosition(0f);
|
||||
if(!state.shadowAnimation.isRunning())
|
||||
runningAnimCount++;
|
||||
state.shadowAnimation.animateToFinalPosition(0f);
|
||||
invalidate();
|
||||
delayedAnimations1[finalI]=null;
|
||||
};
|
||||
Runnable delay2=()->{
|
||||
if(!state.scaleAnimation.isRunning())
|
||||
runningAnimCount++;
|
||||
state.scaleAnimation.animateToFinalPosition(1f);
|
||||
delayedAnimations2[finalI]=null;
|
||||
};
|
||||
delayedAnimations1[finalI]=delay1;
|
||||
delayedAnimations2[finalI]=delay2;
|
||||
postOnAnimationDelayed(delay1, 2000);
|
||||
postOnAnimationDelayed(delay2, 100);
|
||||
}, 20L*(i-startIndex));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean areThereDelayedAnimations(){
|
||||
for(Runnable r:delayedAnimations1){
|
||||
if(r!=null)
|
||||
return true;
|
||||
}
|
||||
for(Runnable r:delayedAnimations2){
|
||||
if(r!=null)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private class CharacterAnimationState extends FloatValueHolder{
|
||||
private final SpringAnimation scaleAnimation, colorAnimation, shadowAnimation;
|
||||
private final FloatValueHolder scale=new FloatValueHolder(1), color=new FloatValueHolder(), shadowAlpha=new FloatValueHolder();
|
||||
|
||||
public CharacterAnimationState(){
|
||||
scaleAnimation=new SpringAnimation(scale);
|
||||
colorAnimation=new SpringAnimation(color);
|
||||
shadowAnimation=new SpringAnimation(shadowAlpha);
|
||||
setupSpring(scaleAnimation);
|
||||
setupSpring(colorAnimation);
|
||||
setupSpring(shadowAnimation);
|
||||
}
|
||||
|
||||
private void setupSpring(SpringAnimation anim){
|
||||
anim.setMinimumVisibleChange(0.01f);
|
||||
anim.setSpring(new SpringForce().setStiffness(500f).setDampingRatio(0.175f));
|
||||
anim.addEndListener((animation, canceled, value, velocity)->runningAnimCount--);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user