forked from upstream-mirrors/OpenTracks
Added profiles for custom layouts.
Users can add severals custom layouts and select one of them in recording fragment. The last selected layout is the one that will be use in the next recording. Fixes #938.
This commit is contained in:
@@ -1,10 +0,0 @@
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
public final class CsvConstants {
|
||||
private CsvConstants() {
|
||||
|
||||
}
|
||||
|
||||
public static final String LINE_SEPARATOR = ";";
|
||||
public static final String ITEM_SEPARATOR = ",";
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.material.navigation.NavigationBarView;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public final class CsvLayoutUtils {
|
||||
public static final String LINE_SEPARATOR = "\n";
|
||||
public static final String ITEM_SEPARATOR = ";";
|
||||
public static final String PROPERTY_SEPARATOR = ",";
|
||||
|
||||
private CsvLayoutUtils() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param csvLine Layout description in a CSV format.
|
||||
* @return All CSV parts from the csvLine or null if it's malformed.
|
||||
*/
|
||||
@Nullable
|
||||
public static List<String> getCsvLineParts(String csvLine) {
|
||||
if (csvLine == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The line must have three items, at least: layout's name, number of columns and one field.
|
||||
List<String> csvParts = Arrays.asList(csvLine.split(ITEM_SEPARATOR));
|
||||
if (csvParts.size() < 3 || !hasValue(csvParts.get(0)) || !isInt(csvParts.get(1))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return csvParts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param csvField Layout's field in a CSV format.
|
||||
* @return List of field parts or null if it's malformed.
|
||||
*/
|
||||
@Nullable
|
||||
public static String[] getCsvFieldParts(@Nullable String csvField) {
|
||||
if (csvField == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Field must have three items: key, is visible (0 or 1), is primary (0 or 1).
|
||||
String[] fieldParts = csvField.split(CsvLayoutUtils.PROPERTY_SEPARATOR);
|
||||
if (fieldParts.length < 3 || !hasValue(fieldParts[0]) || !hasZeroOneValue(fieldParts[1]) || !hasZeroOneValue(fieldParts[2])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fieldParts;
|
||||
}
|
||||
|
||||
private static boolean hasValue(String value) {
|
||||
return value != null && !value.isEmpty();
|
||||
}
|
||||
|
||||
private static boolean isInt(String value) {
|
||||
if (value == null || value.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Integer.parseInt(value);
|
||||
} catch (NumberFormatException nfe) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean hasZeroOneValue(String value) {
|
||||
return isInt(value) && (Integer.parseInt(value) == 0 || Integer.parseInt(value) == 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
abstract public class RecyclerViewSwipeDeleteCallback extends ItemTouchHelper.Callback {
|
||||
|
||||
private final Paint clearPaint;
|
||||
private final ColorDrawable background;
|
||||
private final int backgroundColor;
|
||||
private final Drawable deleteDrawable;
|
||||
private final int intrinsicWidth;
|
||||
private final int intrinsicHeight;
|
||||
|
||||
public RecyclerViewSwipeDeleteCallback(Context context) {
|
||||
background = new ColorDrawable();
|
||||
backgroundColor = context.getResources().getColor(R.color.colorBackgroundDelete);
|
||||
clearPaint = new Paint();
|
||||
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
||||
deleteDrawable = ContextCompat.getDrawable(context, R.drawable.ic_delete_forever_24dp);
|
||||
deleteDrawable.setTint(context.getResources().getColor(R.color.colorForegroundDelete));
|
||||
intrinsicWidth = deleteDrawable.getIntrinsicWidth();
|
||||
intrinsicHeight = deleteDrawable.getIntrinsicHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
|
||||
return makeMovementFlags(0, ItemTouchHelper.LEFT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
|
||||
|
||||
View itemView = viewHolder.itemView;
|
||||
int itemHeight = itemView.getHeight();
|
||||
|
||||
boolean isCancelled = dX == 0 && !isCurrentlyActive;
|
||||
|
||||
if (isCancelled) {
|
||||
clearCanvas(c, itemView.getRight() + dX, (float) itemView.getTop(), (float) itemView.getRight(), (float) itemView.getBottom());
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
|
||||
return;
|
||||
}
|
||||
|
||||
background.setColor(backgroundColor);
|
||||
background.setBounds(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom());
|
||||
background.draw(c);
|
||||
|
||||
int deleteIconTop = itemView.getTop() + (itemHeight - intrinsicHeight) / 2;
|
||||
int deleteIconMargin = (itemHeight - intrinsicHeight) / 2;
|
||||
int deleteIconLeft = itemView.getRight() - deleteIconMargin - intrinsicWidth;
|
||||
int deleteIconRight = itemView.getRight() - deleteIconMargin;
|
||||
int deleteIconBottom = deleteIconTop + intrinsicHeight;
|
||||
|
||||
deleteDrawable.setBounds(deleteIconLeft, deleteIconTop, deleteIconRight, deleteIconBottom);
|
||||
deleteDrawable.draw(c);
|
||||
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
|
||||
}
|
||||
|
||||
private void clearCanvas(Canvas c, Float left, Float top, Float right, Float bottom) {
|
||||
c.drawRect(left, top, right, bottom, clearPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
|
||||
return 0.7f;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ public class StatisticsUtils {
|
||||
}
|
||||
|
||||
public static Layout filterVisible(Layout layout, boolean visible) {
|
||||
Layout result = new Layout(layout.getProfile());
|
||||
Layout result = new Layout(layout.getName());
|
||||
result.addFields(layout.getFields().stream().filter(f -> f.isVisible() == visible).collect(Collectors.toList()));
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user