Skip to content
This repository has been archived by the owner on Oct 28, 2024. It is now read-only.

Commit

Permalink
apply snackbar & inverse colors
Browse files Browse the repository at this point in the history
  • Loading branch information
sk22 committed Dec 5, 2023
1 parent b2fc3af commit a9a4778
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 0 deletions.
217 changes: 217 additions & 0 deletions mastodon/src/main/java/org/joinmastodon/android/ui/Snackbar.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
package org.joinmastodon.android.ui;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

import org.joinmastodon.android.R;
import org.joinmastodon.android.ui.utils.UiUtils;

import androidx.annotation.Keep;
import androidx.annotation.StringRes;
import me.grishka.appkit.utils.CubicBezierInterpolator;
import me.grishka.appkit.utils.V;

public class Snackbar{
private static Snackbar current;

private final Context context;
private int bottomOffset;
private FrameLayout windowView;
private LinearLayout contentView;
private boolean hasAction;
private AnimatableOutlineProvider outlineProvider;
private Animator currentAnim;
private Runnable dismissRunnable=this::dismiss;

private Snackbar(Context context, String text, String action, Runnable onActionClick, int bottomOffset){
this.context=context;
this.bottomOffset=bottomOffset;
hasAction=onActionClick!=null;

windowView=new FrameLayout(context);
windowView.setClipToPadding(false);
contentView=new LinearLayout(context);
contentView.setOrientation(LinearLayout.HORIZONTAL);
contentView.setBaselineAligned(false);
contentView.setBackgroundColor(UiUtils.getThemeColor(context, R.attr.colorM3SurfaceInverse));
contentView.setOutlineProvider(outlineProvider=new AnimatableOutlineProvider(contentView));
contentView.setClipToOutline(true);
contentView.setElevation(V.dp(6));
contentView.setPaddingRelative(V.dp(16), 0, V.dp(8), 0);
FrameLayout.LayoutParams lp=new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lp.leftMargin=lp.topMargin=lp.rightMargin=lp.bottomMargin=V.dp(16);
windowView.addView(contentView, lp);

TextView textView=new TextView(context);
textView.setTextAppearance(R.style.m3_body_medium);
textView.setTextColor(UiUtils.getThemeColor(context, R.attr.colorM3OnSurfaceInverse));
textView.setMaxLines(2);
textView.setEllipsize(TextUtils.TruncateAt.END);
textView.setText(text);
textView.setMinHeight(V.dp(48));
textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.START);
textView.setPadding(0, V.dp(14), 0, V.dp(14));
contentView.addView(textView, new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));

if(action!=null){
Button button=new Button(context);
int primaryInverse=UiUtils.getThemeColor(context, R.attr.colorM3PrimaryInverse);
button.setTextColor(primaryInverse);
button.setBackgroundResource(R.drawable.bg_rect_4dp_ripple);
button.setBackgroundTintList(ColorStateList.valueOf(primaryInverse));
button.setText(action);
button.setPadding(V.dp(8), 0, V.dp(8), 0);
button.setOnClickListener(v->onActionClick.run());
LinearLayout.LayoutParams blp=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, V.dp(40));
blp.leftMargin=blp.topMargin=blp.rightMargin=blp.bottomMargin=V.dp(4);
contentView.addView(button, blp);
}
}

public void show(){
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);
lp.width=ViewGroup.LayoutParams.MATCH_PARENT;
lp.height=ViewGroup.LayoutParams.WRAP_CONTENT;
lp.gravity=Gravity.BOTTOM;
lp.y=bottomOffset;
WindowManager wm=context.getSystemService(WindowManager.class);
wm.addView(windowView, lp);
windowView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
@Override
public boolean onPreDraw(){
windowView.getViewTreeObserver().removeOnPreDrawListener(this);

AnimatorSet set=new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(outlineProvider, "fraction", 0, 1),
ObjectAnimator.ofFloat(contentView, View.ALPHA, 0, 1)
);
set.setInterpolator(AnimationUtils.loadInterpolator(context, R.interpolator.m3_sys_motion_easing_standard_decelerate));
set.setDuration(350);
set.addListener(new AnimatorListenerAdapter(){
@Override
public void onAnimationEnd(Animator animation){
currentAnim=null;
}
});
currentAnim=set;
set.start();

return true;
}
});
windowView.postDelayed(dismissRunnable, 4000);
}

public void dismiss(){
current=null;
if(currentAnim!=null){
currentAnim.cancel();
}
windowView.removeCallbacks(dismissRunnable);
ObjectAnimator anim=ObjectAnimator.ofFloat(contentView, View.ALPHA, 0);
anim.setInterpolator(CubicBezierInterpolator.DEFAULT);
anim.setDuration(200);
anim.addListener(new AnimatorListenerAdapter(){
@Override
public void onAnimationEnd(Animator animation){
WindowManager wm=context.getSystemService(WindowManager.class);
wm.removeView(windowView);
}
});
anim.start();
}

private static class AnimatableOutlineProvider extends ViewOutlineProvider{
private float fraction=1f;
private final View view;

private AnimatableOutlineProvider(View view){
this.view=view;
}

@Override
public void getOutline(View view, Outline outline){
outline.setRoundRect(0, Math.round(view.getHeight()*(1f-fraction)), view.getWidth(), view.getHeight(), V.dp(4));
}

@Keep
public float getFraction(){
return fraction;
}

@Keep
public void setFraction(float fraction){
this.fraction=fraction;
view.invalidateOutline();
}
}

public static class Builder{
private final Context context;
private String text;
private String action;
private Runnable onActionClick;
private int bottomOffset;

public Builder(Context context){
this.context=context;
}

public Builder setText(String text){
this.text=text;
return this;
}

public Builder setText(@StringRes int res){
text=context.getString(res);
return this;
}

public Builder setAction(String action, Runnable onActionClick){
this.action=action;
this.onActionClick=onActionClick;
return this;
}

public Builder setAction(@StringRes int action, Runnable onActionClick){
this.action=context.getString(action);
this.onActionClick=onActionClick;
return this;
}

public Builder setBottomOffset(int offset){
bottomOffset=offset;
return this;
}

public Snackbar create(){
return new Snackbar(context, text, action, onActionClick, bottomOffset);
}

public void show(){
create().show();
}
}
}
2 changes: 2 additions & 0 deletions mastodon/src/main/res/values/attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
<attr name="colorM3Background" format="color"/>
<attr name="colorM3OnBackground" format="color"/>
<attr name="colorM3Surface" format="color"/>
<attr name="colorM3SurfaceInverse" format="color"/>
<attr name="colorM3OnSurface" format="color"/>
<attr name="colorM3OnSurfaceInverse" format="color"/>
<attr name="colorM3SurfaceVariant" format="color"/>
<attr name="colorM3OnSurfaceVariant" format="color"/>
<attr name="colorM3Outline" format="color"/>
Expand Down
9 changes: 9 additions & 0 deletions mastodon/src/main/res/values/palettes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
<item name="colorM3PressedOverlay">@color/white</item>
<item name="colorM3DarkOnSurface">?colorGray100</item>
<item name="colorM3PrimaryInverse">?colorPrimary200</item>
<item name="colorM3SurfaceInverse">?colorGray900</item>
<item name="colorM3OnSurfaceInverse">?colorGray100</item>

<item name="colorFavorite">@color/favorite_selected</item>
<item name="colorLike">@color/like_selected</item>
Expand Down Expand Up @@ -136,6 +138,9 @@
<item name="colorM3Outline">?colorNeutral500</item>
<item name="colorM3OutlineVariant">?colorNeutral700</item>
<item name="colorM3PressedOverlay">?colorPrimary200</item>
<item name="colorM3PrimaryInverse">?colorPrimary200</item>
<item name="colorM3SurfaceInverse">?colorGray25</item>
<item name="colorM3OnSurfaceInverse">?colorGray900</item>

<item name="colorM3DisabledBackground">#1FE3E3E3</item>
<item name="colorM3Error">#F2B8B5</item>
Expand Down Expand Up @@ -184,6 +189,8 @@
<item name="colorM3OutlineVariant">@color/m3_sys_light_outline_variant</item>
<item name="colorM3PressedOverlay">@color/m3_sys_light_on_primary</item>
<item name="colorM3PrimaryInverse">@color/m3_sys_dark_primary</item>
<item name="colorM3SurfaceInverse">@color/m3_sys_dark_surface</item>
<item name="colorM3OnSurfaceInverse">@color/m3_sys_dark_on_surface</item>
<item name="colorM3DarkOnSurface">@color/m3_sys_dark_on_surface</item>

<item name="colorPrimary25">@color/m3_accent1_25</item>
Expand Down Expand Up @@ -272,6 +279,8 @@
<item name="colorM3OutlineVariant">@color/m3_sys_dark_outline_variant</item>
<item name="colorM3PressedOverlay">@color/m3_sys_dark_primary</item>
<item name="colorM3PrimaryInverse">@color/m3_sys_light_primary</item>
<item name="colorM3SurfaceInverse">@color/m3_sys_light_surface</item>
<item name="colorM3OnSurfaceInverse">@color/m3_sys_light_on_surface</item>
</style>

<style name="ColorPalette.Pink">
Expand Down

0 comments on commit a9a4778

Please sign in to comment.