Domain badges & info sheet & my fanciest animation yet
This commit is contained in:
parent
efb8cd565b
commit
8dffbff97c
|
@ -62,10 +62,12 @@ import org.joinmastodon.android.ui.OutlineProviders;
|
|||
import org.joinmastodon.android.ui.SimpleViewHolder;
|
||||
import org.joinmastodon.android.ui.SingleImagePhotoViewerListener;
|
||||
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
|
||||
import org.joinmastodon.android.ui.sheets.DecentralizationExplainerSheet;
|
||||
import org.joinmastodon.android.ui.tabs.TabLayout;
|
||||
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
|
||||
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.joinmastodon.android.ui.text.ImageSpanThatDoesNotBreakShitForNoGoodReason;
|
||||
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.ui.views.CoverImageView;
|
||||
|
@ -107,7 +109,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||
private ImageView avatar;
|
||||
private CoverImageView cover;
|
||||
private View avatarBorder;
|
||||
private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel;
|
||||
private TextView name, username, usernameDomain, bio, followersCount, followersLabel, followingCount, followingLabel;
|
||||
private ProgressBarButton actionButton;
|
||||
private ViewPager2 pager;
|
||||
private NestedRecyclerScrollView scrollView;
|
||||
|
@ -185,6 +187,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||
avatarBorder=content.findViewById(R.id.avatar_border);
|
||||
name=content.findViewById(R.id.name);
|
||||
username=content.findViewById(R.id.username);
|
||||
usernameDomain=content.findViewById(R.id.username_domain);
|
||||
bio=content.findViewById(R.id.bio);
|
||||
followersCount=content.findViewById(R.id.followers_count);
|
||||
followersLabel=content.findViewById(R.id.followers_label);
|
||||
|
@ -320,6 +323,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||
nameEdit.addTextChangedListener(new SimpleTextWatcher(e->editDirty=true));
|
||||
bioEdit.addTextChangedListener(new SimpleTextWatcher(e->editDirty=true));
|
||||
|
||||
usernameDomain.setOnClickListener(v->new DecentralizationExplainerSheet(getActivity(), accountID, account).show());
|
||||
|
||||
return sizeWrapper;
|
||||
}
|
||||
|
||||
|
@ -499,22 +504,21 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||
boolean isSelf=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||
|
||||
if(account.locked){
|
||||
ssb=new SpannableStringBuilder("@");
|
||||
ssb.append(account.acct);
|
||||
if(isSelf){
|
||||
ssb.append('@');
|
||||
ssb.append(AccountSessionManager.getInstance().getAccount(accountID).domain);
|
||||
}
|
||||
ssb=new SpannableStringBuilder(account.username);
|
||||
ssb.append(" ");
|
||||
Drawable lock=username.getResources().getDrawable(R.drawable.ic_lock_fill1_20px, getActivity().getTheme()).mutate();
|
||||
lock.setBounds(0, 0, lock.getIntrinsicWidth(), lock.getIntrinsicHeight());
|
||||
lock.setTint(username.getCurrentTextColor());
|
||||
ssb.append(getString(R.string.manually_approves_followers), new ImageSpan(lock, ImageSpan.ALIGN_BOTTOM), 0);
|
||||
ssb.append(getString(R.string.manually_approves_followers), new ImageSpanThatDoesNotBreakShitForNoGoodReason(lock, ImageSpan.ALIGN_BOTTOM), 0);
|
||||
username.setText(ssb);
|
||||
}else{
|
||||
// noinspection SetTextI18n
|
||||
username.setText('@'+account.acct+(isSelf ? ('@'+AccountSessionManager.getInstance().getAccount(accountID).domain) : ""));
|
||||
username.setText(account.username);
|
||||
}
|
||||
String domain=account.getDomain();
|
||||
if(TextUtils.isEmpty(domain))
|
||||
domain=AccountSessionManager.get(accountID).domain;
|
||||
usernameDomain.setText(domain);
|
||||
|
||||
CharSequence parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID, account);
|
||||
if(TextUtils.isEmpty(parsedBio)){
|
||||
bio.setVisibility(View.GONE);
|
||||
|
|
|
@ -90,7 +90,7 @@ public class Snackbar{
|
|||
if(current!=null)
|
||||
current.dismiss();
|
||||
current=this;
|
||||
WindowManager.LayoutParams lp=new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_APPLICATION_PANEL, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, PixelFormat.TRANSLUCENT);
|
||||
WindowManager.LayoutParams lp=new WindowManager.LayoutParams(WindowManager.LayoutParams.LAST_APPLICATION_WINDOW, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, PixelFormat.TRANSLUCENT);
|
||||
lp.width=ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
lp.height=ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
lp.gravity=Gravity.BOTTOM;
|
||||
|
|
|
@ -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,67 @@
|
|||
package org.joinmastodon.android.ui.text;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.text.style.ImageSpan;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class ImageSpanThatDoesNotBreakShitForNoGoodReason extends ImageSpan{
|
||||
public ImageSpanThatDoesNotBreakShitForNoGoodReason(@NonNull Bitmap b){
|
||||
super(b);
|
||||
}
|
||||
|
||||
public ImageSpanThatDoesNotBreakShitForNoGoodReason(@NonNull Bitmap b, int verticalAlignment){
|
||||
super(b, verticalAlignment);
|
||||
}
|
||||
|
||||
public ImageSpanThatDoesNotBreakShitForNoGoodReason(@NonNull Context context, @NonNull Bitmap bitmap){
|
||||
super(context, bitmap);
|
||||
}
|
||||
|
||||
public ImageSpanThatDoesNotBreakShitForNoGoodReason(@NonNull Context context, @NonNull Bitmap bitmap, int verticalAlignment){
|
||||
super(context, bitmap, verticalAlignment);
|
||||
}
|
||||
|
||||
public ImageSpanThatDoesNotBreakShitForNoGoodReason(@NonNull Drawable drawable){
|
||||
super(drawable);
|
||||
}
|
||||
|
||||
public ImageSpanThatDoesNotBreakShitForNoGoodReason(@NonNull Drawable drawable, int verticalAlignment){
|
||||
super(drawable, verticalAlignment);
|
||||
}
|
||||
|
||||
public ImageSpanThatDoesNotBreakShitForNoGoodReason(@NonNull Drawable drawable, @NonNull String source){
|
||||
super(drawable, source);
|
||||
}
|
||||
|
||||
public ImageSpanThatDoesNotBreakShitForNoGoodReason(@NonNull Drawable drawable, @NonNull String source, int verticalAlignment){
|
||||
super(drawable, source, verticalAlignment);
|
||||
}
|
||||
|
||||
public ImageSpanThatDoesNotBreakShitForNoGoodReason(@NonNull Context context, @NonNull Uri uri){
|
||||
super(context, uri);
|
||||
}
|
||||
|
||||
public ImageSpanThatDoesNotBreakShitForNoGoodReason(@NonNull Context context, @NonNull Uri uri, int verticalAlignment){
|
||||
super(context, uri, verticalAlignment);
|
||||
}
|
||||
|
||||
public ImageSpanThatDoesNotBreakShitForNoGoodReason(@NonNull Context context, int resourceId){
|
||||
super(context, resourceId);
|
||||
}
|
||||
|
||||
public ImageSpanThatDoesNotBreakShitForNoGoodReason(@NonNull Context context, int resourceId, int verticalAlignment){
|
||||
super(context, resourceId, verticalAlignment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm){
|
||||
// Purposefully not touching the font metrics
|
||||
return getDrawable().getBounds().right;
|
||||
}
|
||||
}
|
|
@ -929,11 +929,15 @@ public class UiUtils{
|
|||
public static void maybeShowTextCopiedToast(Context context){
|
||||
//show toast, android from S_V2 on has built-in popup, as documented in
|
||||
//https://developer.android.com/develop/ui/views/touch-and-input/copy-paste#duplicate-notifications
|
||||
if(Build.VERSION.SDK_INT<=Build.VERSION_CODES.S_V2){
|
||||
if(needShowClipboardToast()){
|
||||
Toast.makeText(context, R.string.text_copied, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -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--);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape
|
||||
android:tint="@color/m3_primary_alpha11"
|
||||
android:tintMode="src_over">
|
||||
<solid android:color="?colorM3Surface" />
|
||||
<corners android:radius="8dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<stroke android:width="2dp" android:color="?colorM3OutlineVariant" android:dashWidth="5dp" android:dashGap="5dp"/>
|
||||
<corners android:radius="8dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
|
@ -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="M4,22Q3.175,22 2.588,21.413Q2,20.825 2,20V9Q2,8.175 2.588,7.587Q3.175,7 4,7H9V4Q9,3.175 9.588,2.587Q10.175,2 11,2H13Q13.825,2 14.413,2.587Q15,3.175 15,4V7H20Q20.825,7 21.413,7.587Q22,8.175 22,9V20Q22,20.825 21.413,21.413Q20.825,22 20,22ZM4,20H20Q20,20 20,20Q20,20 20,20V9Q20,9 20,9Q20,9 20,9H15Q15,9.825 14.413,10.412Q13.825,11 13,11H11Q10.175,11 9.588,10.412Q9,9.825 9,9H4Q4,9 4,9Q4,9 4,9V20Q4,20 4,20Q4,20 4,20ZM6,18H12V17.55Q12,17.125 11.762,16.762Q11.525,16.4 11.1,16.2Q10.6,15.975 10.088,15.863Q9.575,15.75 9,15.75Q8.425,15.75 7.913,15.863Q7.4,15.975 6.9,16.2Q6.475,16.4 6.238,16.762Q6,17.125 6,17.55ZM14,16.5H18V15H14ZM9,15Q9.625,15 10.062,14.562Q10.5,14.125 10.5,13.5Q10.5,12.875 10.062,12.438Q9.625,12 9,12Q8.375,12 7.938,12.438Q7.5,12.875 7.5,13.5Q7.5,14.125 7.938,14.562Q8.375,15 9,15ZM14,13.5H18V12H14ZM11,9H13V4H11ZM12,14.5Q12,14.5 12,14.5Q12,14.5 12,14.5Q12,14.5 12,14.5Q12,14.5 12,14.5Q12,14.5 12,14.5Q12,14.5 12,14.5Q12,14.5 12,14.5Q12,14.5 12,14.5Q12,14.5 12,14.5Q12,14.5 12,14.5Q12,14.5 12,14.5Q12,14.5 12,14.5Z"/>
|
||||
</vector>
|
|
@ -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,22ZM11,19.95V18Q10.175,18 9.588,17.413Q9,16.825 9,16V15L4.2,10.2Q4.125,10.65 4.062,11.1Q4,11.55 4,12Q4,15.025 5.988,17.3Q7.975,19.575 11,19.95ZM17.9,17.4Q18.925,16.275 19.462,14.887Q20,13.5 20,12Q20,9.55 18.638,7.525Q17.275,5.5 15,4.6V5Q15,5.825 14.413,6.412Q13.825,7 13,7H11V9Q11,9.425 10.713,9.712Q10.425,10 10,10H8V12H14Q14.425,12 14.713,12.287Q15,12.575 15,13V16H16Q16.65,16 17.175,16.387Q17.7,16.775 17.9,17.4Z"/>
|
||||
</vector>
|
|
@ -61,7 +61,7 @@
|
|||
android:layout_below="@id/cover"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="-44dp"
|
||||
android:layout_marginTop="-36dp"
|
||||
android:background="@drawable/profile_ava_bg"
|
||||
android:outlineProvider="@null">
|
||||
|
||||
|
@ -76,64 +76,63 @@
|
|||
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/profile_action_btn_wrap"
|
||||
android:layout_width="wrap_content"
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/cover"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:paddingStart="120dp">
|
||||
android:layout_toEndOf="@id/avatar_border"
|
||||
android:layout_marginTop="14dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/m3_title_large"
|
||||
android:textColor="?colorM3OnSurface"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
tools:text="Eugen" />
|
||||
|
||||
<org.joinmastodon.android.ui.views.ProgressBarButton
|
||||
android:id="@+id/profile_action_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
style="@style/Widget.Mastodon.M3.Button.Filled"
|
||||
android:minWidth="156dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
tools:text="@string/save_changes" />
|
||||
<org.joinmastodon.android.ui.views.WrappingLinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/name"
|
||||
android:layout_toEndOf="@id/avatar_border"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:horizontalGap="4dp"
|
||||
android:verticalGap="0dp">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/action_progress"
|
||||
style="?android:progressBarStyleSmall"
|
||||
<TextView
|
||||
android:id="@+id/username"
|
||||
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" />
|
||||
</FrameLayout>
|
||||
android:layout_height="20dp"
|
||||
android:textAppearance="@style/m3_body_medium"
|
||||
android:textColor="?colorM3OnSurfaceVariant"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
tools:text="Gargron" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/username_domain"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="20dp"
|
||||
android:textAppearance="@style/m3_label_small"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="4dp"
|
||||
android:textColor="?colorM3OnSurfaceVariant"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:background="@drawable/rect_4dp"
|
||||
android:backgroundTint="?colorM3SurfaceVariant"
|
||||
tools:text="mastodon.social"/>
|
||||
|
||||
</org.joinmastodon.android.ui.views.WrappingLinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/m3_title_large"
|
||||
android:textColor="?colorM3OnSurface"
|
||||
tools:text="Eugen" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:textAppearance="@style/m3_title_small"
|
||||
android:textColor="?colorM3OnSurfaceVariant"
|
||||
tools:text="\@Gargron" />
|
||||
|
||||
<org.joinmastodon.android.ui.views.LinkedTextView
|
||||
android:id="@+id/bio"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -192,18 +191,11 @@
|
|||
tools:text="followers" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="28dp"
|
||||
android:gravity="center"
|
||||
android:text="·"
|
||||
android:textAppearance="@style/m3_label_large"
|
||||
android:textColor="?colorM3OnSurfaceVariant" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/following_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="28dp"
|
||||
android:layout_marginStart="-4dp"
|
||||
android:background="@drawable/bg_button_borderless_rounded"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="horizontal"
|
||||
|
@ -289,6 +281,41 @@
|
|||
android:padding="16dp"
|
||||
tools:text="Founder, CEO and lead developer @Mastodon, Germany." />
|
||||
</org.joinmastodon.android.ui.views.FloatingHintEditTextLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginBottom="8dp">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/profile_action_btn_wrap"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<org.joinmastodon.android.ui.views.ProgressBarButton
|
||||
android:id="@+id/profile_action_btn"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
style="@style/Widget.Mastodon.M3.Button.Filled"
|
||||
android:minWidth="156dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
tools:text="@string/save_changes" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/action_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" />
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<org.joinmastodon.android.ui.tabs.TabLayout
|
||||
android:id="@+id/tabbar"
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
<?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">
|
||||
|
||||
<View
|
||||
android:id="@+id/sheet_handle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="36dp"
|
||||
android:background="@drawable/bg_bottom_sheet_handle"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@drawable/white_circle"
|
||||
android:backgroundTint="?colorM3SecondaryContainer"
|
||||
android:scaleType="center"
|
||||
android:tint="?colorM3OnSecondaryContainer"
|
||||
android:src="@drawable/ic_badge_24px"
|
||||
android:importantForAccessibility="no"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/m3_title_large"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textColor="?colorM3OnSurface"
|
||||
android:text="@string/handle_help_title"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/handle_wrap"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/bg_handle_help"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:padding="12dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/handle_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/m3_label_small"
|
||||
android:textColor="?colorM3OnSurfaceVariant"
|
||||
android:alpha="0.6"
|
||||
tools:text="@string/handle_title"/>
|
||||
|
||||
<org.joinmastodon.android.ui.views.RippleAnimationTextView
|
||||
android:id="@+id/handle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:textAppearance="@style/m3_body_large"
|
||||
android:textColor="?colorM3OnSurface"
|
||||
tools:text="\@Gargron@mastodon.social"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/username_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="center"
|
||||
android:tint="?colorM3Primary"
|
||||
android:src="@drawable/ic_alternate_email_24px"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/username_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:textAppearance="@style/m3_title_medium"
|
||||
android:textColor="?colorM3OnSurface"
|
||||
android:text="@string/username"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/username_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:layout_below="@id/username_title"
|
||||
android:textAppearance="@style/m3_body_small"
|
||||
android:textColor="?colorM3OnSurfaceVariant"
|
||||
tools:text="@string/handle_username_explanation"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/server_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="center"
|
||||
android:tint="?colorM3Primary"
|
||||
android:src="@drawable/ic_public_24px"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/server_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:textAppearance="@style/m3_title_medium"
|
||||
android:textColor="?colorM3OnSurface"
|
||||
android:text="@string/server"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/server_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:layout_below="@id/server_title"
|
||||
android:textAppearance="@style/m3_body_small"
|
||||
android:textColor="?colorM3OnSurfaceVariant"
|
||||
tools:text="@string/handle_server_explanation"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<org.joinmastodon.android.ui.views.LinkedTextView
|
||||
android:id="@+id/handle_explanation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:textAppearance="@style/m3_body_medium"
|
||||
android:textColor="?colorM3OnSurfaceVariant"
|
||||
tools:text="@string/handle_explanation"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_cancel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
style="@style/Widget.Mastodon.M3.Button.Filled"
|
||||
android:text="@string/got_it"/>
|
||||
|
||||
</LinearLayout>
|
||||
</org.joinmastodon.android.ui.views.CustomScrollView>
|
|
@ -689,4 +689,17 @@
|
|||
<string name="server_cant_mention_or_follow_you">Nobody from this server can 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>
|
||||
<string name="handle_help_title">What’s in a handle?</string>
|
||||
<string name="handle_title">Their handle</string>
|
||||
<string name="handle_username_explanation">Their unique identifier on their server. It’s possible to find users with the same username on different servers.</string>
|
||||
<string name="handle_title_own">Your handle</string>
|
||||
<string name="handle_username_explanation_own">Your unique identifier on this server. It’s possible to find users with the same username on different servers.</string>
|
||||
<string name="server">Server</string>
|
||||
<string name="handle_server_explanation">Their digital home, where all of their posts live.</string>
|
||||
<string name="handle_explanation">Since handles say who someone is and where they are, you can interact with people across the social web of <a>ActivityPub-powered platforms</a>.</string>
|
||||
<string name="handle_server_explanation_own">Your digital home, where all of your posts live. Don’t like this one? Transfer servers at any time and bring your followers, too.</string>
|
||||
<string name="handle_explanation_own">Because your handle says who you are and where you are, people can interact with you across the social web of <a>ActivityPub-powered platforms</a>.</string>
|
||||
<string name="what_is_activitypub_title">What’s ActivityPub?</string>
|
||||
<string name="what_is_activitypub">ActivityPub is like the language Mastodon speaks with other social networks.\n\nIt lets you connect and interact with people not just on Mastodon, but across different social apps too.</string>
|
||||
<string name="handle_copied">Handle copied to clipboard.</string>
|
||||
</resources>
|
Loading…
Reference in New Issue