mirror of
https://github.com/Dark98/SliceBeam.git
synced 2026-07-03 08:39:04 +00:00
New snackbars
This commit is contained in:
@@ -2,14 +2,20 @@ package ru.ytkab0bp.slicebeam;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.Process;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Base64;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
@@ -28,6 +34,7 @@ import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -40,23 +47,27 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
|
||||
import ru.ytkab0bp.slicebeam.components.ChangeLogBottomSheet;
|
||||
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
|
||||
import ru.ytkab0bp.slicebeam.config.ConfigObject;
|
||||
import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
|
||||
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
|
||||
import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
|
||||
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
|
||||
import ru.ytkab0bp.slicebeam.navigation.MobileNavigationDelegate;
|
||||
import ru.ytkab0bp.slicebeam.navigation.NavigationDelegate;
|
||||
import ru.ytkab0bp.slicebeam.slic3r.Model;
|
||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
|
||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rRuntimeError;
|
||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
||||
import ru.ytkab0bp.slicebeam.utils.IOUtils;
|
||||
import ru.ytkab0bp.slicebeam.utils.Prefs;
|
||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
||||
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
public final static int REQUEST_CODE_OPEN_FILE = 1, REQUEST_CODE_EXPORT_GCODE = 2,
|
||||
@@ -496,6 +507,51 @@ public class MainActivity extends AppCompatActivity {
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void loadFile(File f, boolean autoorient) {
|
||||
String tag = UUID.randomUUID().toString();
|
||||
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileOpenFileLoading).tag(tag));
|
||||
IOUtils.IO_POOL.submit(() -> {
|
||||
Process.setThreadPriority(-20);
|
||||
if (delegate.getCurrentFragment() instanceof BedFragment) {
|
||||
BedFragment fragment = (BedFragment) delegate.getCurrentFragment();
|
||||
try {
|
||||
boolean gcode = f.getName().endsWith(".gcode");
|
||||
if (gcode) {
|
||||
fragment.loadGCode(f);
|
||||
} else {
|
||||
fragment.loadModel(f);
|
||||
}
|
||||
fragment.getGlView().queueEvent(() -> {
|
||||
if (!gcode) {
|
||||
SliceBeam.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
|
||||
}
|
||||
Model model = fragment.getGlView().getRenderer().getModel();
|
||||
int i = model.getObjectsCount() - 1;
|
||||
if (autoorient) {
|
||||
model.autoOrient(i);
|
||||
fragment.getGlView().getRenderer().invalidateGlModel(i);
|
||||
fragment.getGlView().requestRender();
|
||||
}
|
||||
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
|
||||
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileOpenFileLoaded));
|
||||
if (model.isBigObject(i)) {
|
||||
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.MenuFileOpenFileBigObject));
|
||||
}
|
||||
});
|
||||
} catch (Slic3rRuntimeError e) {
|
||||
Log.e("MainActivity", "Failed to load model", e);
|
||||
f.delete();
|
||||
|
||||
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
|
||||
.setTitle(R.string.MenuFileOpenFileFailed)
|
||||
.setMessage(e.toString())
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void loadFile(Uri uri) {
|
||||
if (uri == null) return;
|
||||
|
||||
@@ -534,30 +590,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
fos.close();
|
||||
in.close();
|
||||
|
||||
ViewUtils.postOnMainThread(() -> {
|
||||
if (delegate.getCurrentFragment() instanceof BedFragment) {
|
||||
BedFragment fragment = (BedFragment) delegate.getCurrentFragment();
|
||||
try {
|
||||
if (f.getName().endsWith(".gcode")) {
|
||||
fragment.loadGCode(f);
|
||||
} else {
|
||||
fragment.loadModel(f);
|
||||
SliceBeam.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
|
||||
}
|
||||
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileOpenFileLoaded));
|
||||
} catch (Slic3rRuntimeError e) {
|
||||
Log.e("MainActivity", "Failed to load model", e);
|
||||
f.delete();
|
||||
|
||||
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
|
||||
.setTitle(R.string.MenuFileOpenFileFailed)
|
||||
.setMessage(e.toString())
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show());
|
||||
}
|
||||
}
|
||||
});
|
||||
loadFile(f, false);
|
||||
} catch (Exception e) {
|
||||
Log.e("MainActivity", "Failed to write cache file", e);
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package ru.ytkab0bp.slicebeam.events;
|
||||
|
||||
import ru.ytkab0bp.eventbus.Event;
|
||||
|
||||
@Event
|
||||
public class NeedDismissSnackbarEvent {
|
||||
public final String tag;
|
||||
|
||||
public NeedDismissSnackbarEvent(String tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,18 @@ package ru.ytkab0bp.slicebeam.events;
|
||||
|
||||
import ru.ytkab0bp.eventbus.Event;
|
||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
||||
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
|
||||
|
||||
@Event
|
||||
public class NeedSnackbarEvent {
|
||||
public final CharSequence title;
|
||||
public SnackbarsLayout.Type type = SnackbarsLayout.Type.DONE;
|
||||
public String tag;
|
||||
|
||||
public NeedSnackbarEvent(SnackbarsLayout.Type type, CharSequence title) {
|
||||
this.type = type;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public NeedSnackbarEvent(CharSequence title) {
|
||||
this.title = title;
|
||||
@@ -14,4 +22,14 @@ public class NeedSnackbarEvent {
|
||||
public NeedSnackbarEvent(int title, Object... args) {
|
||||
this.title = SliceBeam.INSTANCE.getString(title, args);
|
||||
}
|
||||
|
||||
public NeedSnackbarEvent(SnackbarsLayout.Type type, int title, Object... args) {
|
||||
this.type = type;
|
||||
this.title = SliceBeam.INSTANCE.getString(title, args);
|
||||
}
|
||||
|
||||
public NeedSnackbarEvent tag(String tag) {
|
||||
this.tag = tag;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,11 @@ import android.webkit.WebView;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.dynamicanimation.animation.FloatValueHolder;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
import com.google.android.material.navigation.NavigationBarView;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -38,6 +36,7 @@ import ru.ytkab0bp.slicebeam.components.bed_menu.SliceMenu;
|
||||
import ru.ytkab0bp.slicebeam.components.bed_menu.TransformMenu;
|
||||
import ru.ytkab0bp.slicebeam.config.ConfigObject;
|
||||
import ru.ytkab0bp.slicebeam.events.FlattenModeResetEvent;
|
||||
import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
|
||||
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
|
||||
import ru.ytkab0bp.slicebeam.events.SlicingProgressEvent;
|
||||
import ru.ytkab0bp.slicebeam.navigation.Fragment;
|
||||
@@ -51,6 +50,7 @@ import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
||||
import ru.ytkab0bp.slicebeam.view.BedSwipeDownLayout;
|
||||
import ru.ytkab0bp.slicebeam.view.DividerView;
|
||||
import ru.ytkab0bp.slicebeam.view.GLView;
|
||||
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
|
||||
import ru.ytkab0bp.slicebeam.view.ThemeBottomNavigationView;
|
||||
import ru.ytkab0bp.slicebeam.view.ThemeRailNavigationView;
|
||||
|
||||
@@ -59,7 +59,7 @@ public class BedFragment extends Fragment {
|
||||
private final static int MENU_SIZE_DP = 80;
|
||||
|
||||
private FrameLayout overlayLayout;
|
||||
private CoordinatorLayout snackbarsLayout;
|
||||
private SnackbarsLayout snackbarsLayout;
|
||||
private GLView glView;
|
||||
private NavigationBarView navigationView;
|
||||
|
||||
@@ -126,7 +126,16 @@ public class BedFragment extends Fragment {
|
||||
|
||||
@EventHandler(runOnMainThread = true)
|
||||
public void onNeedSnackbar(NeedSnackbarEvent e) {
|
||||
Snackbar.make(snackbarsLayout, e.title, Snackbar.LENGTH_SHORT).show();
|
||||
SnackbarsLayout.Snackbar s = new SnackbarsLayout.Snackbar(e.type, e.title);
|
||||
if (e.tag != null) {
|
||||
s.tag(e.tag);
|
||||
}
|
||||
snackbarsLayout.show(s);
|
||||
}
|
||||
|
||||
@EventHandler(runOnMainThread = true)
|
||||
public void onDismissSnackbar(NeedDismissSnackbarEvent e) {
|
||||
snackbarsLayout.dismiss(e.tag);
|
||||
}
|
||||
|
||||
public void showUnfoldMenu(UnfoldMenu menu, View from) {
|
||||
@@ -380,7 +389,7 @@ public class BedFragment extends Fragment {
|
||||
} else {
|
||||
overlayLayout.addView(contentView = ll);
|
||||
}
|
||||
overlayLayout.addView(snackbarsLayout = new CoordinatorLayout(ctx), new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) {{
|
||||
overlayLayout.addView(snackbarsLayout = new SnackbarsLayout(ctx), new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) {{
|
||||
if (portrait) {
|
||||
bottomMargin = ViewUtils.dp(80 * 2);
|
||||
} else {
|
||||
@@ -390,7 +399,7 @@ public class BedFragment extends Fragment {
|
||||
return overlayLayout;
|
||||
}
|
||||
|
||||
public CoordinatorLayout getSnackbarsLayout() {
|
||||
public SnackbarsLayout getSnackbarsLayout() {
|
||||
return snackbarsLayout;
|
||||
}
|
||||
|
||||
@@ -498,7 +507,6 @@ public class BedFragment extends Fragment {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
glView.getRenderer().setModel(model = m);
|
||||
glView.queueEvent(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -507,6 +515,8 @@ public class BedFragment extends Fragment {
|
||||
ViewUtils.postOnMainThread(()-> glView.queueEvent(this));
|
||||
return;
|
||||
}
|
||||
glView.getRenderer().setModel(model = m);
|
||||
|
||||
Vec3d center = bed.getVolumeMin().center(bed.getVolumeMax());
|
||||
Vec3d objMin = new Vec3d(), objMax = new Vec3d();
|
||||
Vec3d objTranslate = new Vec3d();
|
||||
|
||||
@@ -147,10 +147,22 @@ public class Model {
|
||||
return list;
|
||||
}
|
||||
|
||||
public int getExtruder(int i) {
|
||||
return Native.model_get_extruder(pointer, i);
|
||||
}
|
||||
|
||||
public void setExtruder(int i, int extruder) {
|
||||
Native.model_set_extruder(pointer, i, extruder);
|
||||
}
|
||||
|
||||
public void autoOrient(int i) {
|
||||
Native.model_auto_orient(pointer, i);
|
||||
}
|
||||
|
||||
public boolean isBigObject(int i) {
|
||||
return Native.model_is_big_object(pointer, i);
|
||||
}
|
||||
|
||||
public GCodeProcessorResult slice(String configPath, String gcodePath, SliceListener listener) throws Slic3rRuntimeError {
|
||||
return new GCodeProcessorResult(Native.model_slice(pointer, configPath, gcodePath, listener));
|
||||
}
|
||||
|
||||
@@ -72,6 +72,9 @@ class Native {
|
||||
static native void model_flatten_rotate(long ptr, int i, long surfacePtr);
|
||||
static native long[] model_create_flatten_planes(long ptr, int i);
|
||||
static native void model_auto_orient(long ptr, int i);
|
||||
static native boolean model_is_big_object(long ptr, int i);
|
||||
static native int model_get_extruder(long ptr, int i);
|
||||
static native void model_set_extruder(long ptr, int i, int extruder);
|
||||
static native long model_slice(long ptr, String configPath, String path, SliceListener listener) throws Slic3rRuntimeError;
|
||||
static native void model_release(long ptr);
|
||||
|
||||
|
||||
@@ -47,6 +47,12 @@ public class BeamTheme {
|
||||
colors.put(R.attr.yTrackColor, 0xff00bf00);
|
||||
colors.put(R.attr.zTrackColor, 0xff0000bf);
|
||||
|
||||
colors.put(R.attr.snackbarBase, 0xFFEEEEEE);
|
||||
colors.put(R.attr.snackbarDone, 0xFF56AB2F);
|
||||
colors.put(R.attr.snackbarWarning, 0xFFAE660C);
|
||||
colors.put(R.attr.snackbarInfo, 0xFF009DC6);
|
||||
colors.put(R.attr.snackbarError, 0xFFDC100E);
|
||||
|
||||
colors.put(android.R.attr.textColorPrimary, 0xff000000);
|
||||
colors.put(android.R.attr.textColorSecondary, 0x99000000);
|
||||
colors.put(android.R.attr.windowBackground, 0xffffffff);
|
||||
@@ -73,6 +79,8 @@ public class BeamTheme {
|
||||
colors.put(R.attr.yTrackColor, 0xff00ee00);
|
||||
colors.put(R.attr.zTrackColor, 0xff0000ee);
|
||||
|
||||
colors.put(R.attr.snackbarBase, 0xFF212121);
|
||||
|
||||
colors.put(android.R.attr.textColorPrimary, 0xffffffff);
|
||||
colors.put(android.R.attr.textColorSecondary, 0x99ffffff);
|
||||
colors.put(android.R.attr.windowBackground, 0xff121212);
|
||||
|
||||
@@ -15,10 +15,15 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import ru.ytkab0bp.slicebeam.config.ConfigObject;
|
||||
|
||||
public class IOUtils {
|
||||
public static ExecutorService IO_POOL = Executors.newCachedThreadPool();
|
||||
|
||||
public static String readString(InputStream in) throws IOException {
|
||||
return readString(in, false);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
package ru.ytkab0bp.slicebeam.view;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Outline;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.dynamicanimation.animation.FloatValueHolder;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import ru.ytkab0bp.slicebeam.R;
|
||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
||||
|
||||
public class SnackbarsLayout extends FrameLayout {
|
||||
public SnackbarsLayout(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public void show(Snackbar snackbar) {
|
||||
SnackbarView v = new SnackbarView(getContext()).bind(snackbar);
|
||||
addView(v);
|
||||
applyTransforms();
|
||||
new SpringAnimation(new FloatValueHolder(0))
|
||||
.setMinimumVisibleChange(1 / 500f)
|
||||
.setSpring(new SpringForce(1f)
|
||||
.setStiffness(1000f)
|
||||
.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY))
|
||||
.addUpdateListener((animation, value, velocity) -> {
|
||||
v.progress = value;
|
||||
applyTransforms();
|
||||
})
|
||||
.start();
|
||||
|
||||
if (snackbar.lifetime > 0) {
|
||||
ViewUtils.postOnMainThread(v::dismiss, snackbar.lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
public void dismiss(String tag) {
|
||||
for (int i = 0, s = getChildCount(); i < s; i++) {
|
||||
SnackbarView snackbar = (SnackbarView) getChildAt(i);
|
||||
if (Objects.equals(snackbar.snackbar.tag, tag)) {
|
||||
snackbar.dismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
applyTransforms();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
applyTransforms();
|
||||
}
|
||||
|
||||
private void applyTransforms() {
|
||||
float y = getHeight() - ViewUtils.dp(8);
|
||||
|
||||
for (int i = getChildCount() - 1; i >= 0; i--) {
|
||||
SnackbarView snackbar = (SnackbarView) getChildAt(i);
|
||||
if (snackbar.getTag() == null) {
|
||||
snackbar.setAlpha(snackbar.progress);
|
||||
}
|
||||
|
||||
float yOff = snackbar.getAlpha() * snackbar.progress * (snackbar.getHeight() + ViewUtils.dp(8));
|
||||
y -= yOff;
|
||||
snackbar.setTranslationY(y);
|
||||
}
|
||||
}
|
||||
|
||||
private class SnackbarView extends LinearLayout {
|
||||
private final static int MARGIN_DP = 8;
|
||||
|
||||
private ProgressBar progressBar;
|
||||
private ImageView icon;
|
||||
private TextView title;
|
||||
private Snackbar snackbar;
|
||||
|
||||
private float progress;
|
||||
|
||||
private GestureDetector gestureDetector;
|
||||
|
||||
SnackbarView(Context context) {
|
||||
super(context);
|
||||
|
||||
setElevation(ViewUtils.dp(4));
|
||||
setClipToOutline(true);
|
||||
setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
outline.setRoundRect(0, 0, getWidth(), getHeight(), ViewUtils.dp(16));
|
||||
}
|
||||
});
|
||||
setAlpha(0f);
|
||||
setPadding(ViewUtils.dp(10), ViewUtils.dp(10), ViewUtils.dp(10), ViewUtils.dp(10));
|
||||
setMinimumHeight(ViewUtils.dp(48));
|
||||
|
||||
setOrientation(HORIZONTAL);
|
||||
setGravity(Gravity.CENTER_VERTICAL);
|
||||
|
||||
FrameLayout fl = new FrameLayout(context);
|
||||
icon = new ImageView(context);
|
||||
fl.addView(icon, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
progressBar = new ProgressBar(context);
|
||||
progressBar.setVisibility(GONE);
|
||||
fl.addView(progressBar, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
addView(fl, new LinearLayout.LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28)) {{
|
||||
setMarginStart(ViewUtils.dp(4));
|
||||
setMarginEnd(ViewUtils.dp(14));
|
||||
}});
|
||||
title = new TextView(context);
|
||||
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||
title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||
title.setMaxLines(2);
|
||||
title.setEllipsize(TextUtils.TruncateAt.END);
|
||||
addView(title);
|
||||
|
||||
setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||
leftMargin = topMargin = rightMargin = bottomMargin = ViewUtils.dp(MARGIN_DP);
|
||||
}});
|
||||
|
||||
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
|
||||
@Override
|
||||
public boolean onDown(@NonNull MotionEvent e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(@Nullable MotionEvent e1, @NonNull MotionEvent e2, float distanceX, float distanceY) {
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
|
||||
float off = e2.getX() - e1.getX();
|
||||
setTranslationX(off);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY) {
|
||||
if (snackbar.type == Type.LOADING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Math.abs(velocityX) >= 1500) {
|
||||
if (velocityX > 0) {
|
||||
animateTo(getWidth() + ViewUtils.dp(MARGIN_DP), true);
|
||||
} else {
|
||||
animateTo(-getWidth() - ViewUtils.dp(MARGIN_DP), true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void dismiss() {
|
||||
setTag(1);
|
||||
|
||||
new SpringAnimation(new FloatValueHolder(0))
|
||||
.setMinimumVisibleChange(1 / 500f)
|
||||
.setSpring(new SpringForce(1f)
|
||||
.setStiffness(1000f)
|
||||
.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY))
|
||||
.addUpdateListener((animation, value, velocity) -> {
|
||||
setAlpha(1f - value);
|
||||
applyTransforms();
|
||||
})
|
||||
.addEndListener((animation, canceled, value, velocity) -> {
|
||||
if (getParent() == null) return;
|
||||
((ViewGroup) getParent()).removeView(this);
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
private void animateTo(float x, boolean remove) {
|
||||
if (remove) {
|
||||
setTag(1);
|
||||
}
|
||||
float start = getTranslationX();
|
||||
new SpringAnimation(new FloatValueHolder(0))
|
||||
.setMinimumVisibleChange(1 / 500f)
|
||||
.setSpring(new SpringForce(1f)
|
||||
.setStiffness(1000f)
|
||||
.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY))
|
||||
.addUpdateListener((animation, value, velocity) -> {
|
||||
setTranslationX(ViewUtils.lerp(start, x, value));
|
||||
if (remove) {
|
||||
progress = 1f - value;
|
||||
applyTransforms();
|
||||
}
|
||||
})
|
||||
.addEndListener((animation, canceled, value, velocity) -> {
|
||||
if (remove) {
|
||||
((ViewGroup) getParent()).removeView(SnackbarView.this);
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if ((event.getActionMasked() == MotionEvent.ACTION_UP || event.getActionMasked() == MotionEvent.ACTION_CANCEL) && getTag() == null) {
|
||||
animateTo(0, false);
|
||||
}
|
||||
|
||||
MotionEvent ev = MotionEvent.obtain(event);
|
||||
ev.offsetLocation(getTranslationX(), 0);
|
||||
boolean ret = gestureDetector.onTouchEvent(ev);
|
||||
ev.recycle();
|
||||
return ret;
|
||||
}
|
||||
|
||||
SnackbarView bind(Snackbar snackbar) {
|
||||
this.snackbar = snackbar;
|
||||
|
||||
progressBar.setVisibility(snackbar.type == Type.LOADING ? VISIBLE : GONE);
|
||||
icon.setVisibility(snackbar.type == Type.LOADING ? GONE : VISIBLE);
|
||||
|
||||
title.setText(snackbar.title);
|
||||
switch (snackbar.type) {
|
||||
case DONE:
|
||||
icon.setImageResource(R.drawable.done_outline_28);
|
||||
icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.snackbarDone)));
|
||||
title.setTextColor(ThemesRepo.getColor(R.attr.snackbarDone));
|
||||
setBackgroundColor(ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.snackbarBase), ThemesRepo.getColor(R.attr.snackbarDone), 0.15f));
|
||||
break;
|
||||
case WARNING:
|
||||
icon.setImageResource(R.drawable.warning_triangle_outline_28);
|
||||
icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.snackbarWarning)));
|
||||
title.setTextColor(ThemesRepo.getColor(R.attr.snackbarWarning));
|
||||
setBackgroundColor(ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.snackbarBase), ThemesRepo.getColor(R.attr.snackbarWarning), 0.15f));
|
||||
break;
|
||||
case LOADING:
|
||||
progressBar.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.snackbarInfo)));
|
||||
title.setTextColor(ThemesRepo.getColor(R.attr.snackbarInfo));
|
||||
setBackgroundColor(ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.snackbarBase), ThemesRepo.getColor(R.attr.snackbarInfo), 0.15f));
|
||||
break;
|
||||
case INFO:
|
||||
icon.setImageResource(R.drawable.info_outline_28);
|
||||
icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.snackbarInfo)));
|
||||
title.setTextColor(ThemesRepo.getColor(R.attr.snackbarInfo));
|
||||
setBackgroundColor(ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.snackbarBase), ThemesRepo.getColor(R.attr.snackbarInfo), 0.15f));
|
||||
break;
|
||||
case ERROR:
|
||||
icon.setImageResource(R.drawable.error_outline_28);
|
||||
icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.snackbarError)));
|
||||
title.setTextColor(ThemesRepo.getColor(R.attr.snackbarError));
|
||||
setBackgroundColor(ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.snackbarBase), ThemesRepo.getColor(R.attr.snackbarError), 0.15f));
|
||||
break;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Snackbar {
|
||||
public CharSequence title;
|
||||
public Type type;
|
||||
public int lifetime = 2500;
|
||||
public String tag;
|
||||
|
||||
public Snackbar(Type type, CharSequence title) {
|
||||
this.type = type;
|
||||
this.title = title;
|
||||
|
||||
if (type == Type.WARNING || type == Type.ERROR) {
|
||||
lifetime = 5000;
|
||||
}
|
||||
}
|
||||
|
||||
public Snackbar tag(String tag) {
|
||||
this.lifetime = 0;
|
||||
this.tag = tag;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
DONE, WARNING, INFO, ERROR,
|
||||
LOADING // Must use tag
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user