5 Commits

Author SHA1 Message Date
Michael Mikovsky 76a1b1ff70 Update version for new release 2026-03-05 15:21:12 -07:00
Michael Mikovsky 82465f213f Fix downloading new fields on tablets 2026-03-05 15:10:58 -07:00
Daniel Carta 3c7a879f51 Updated Fields 2026-02-13 17:26:07 -07:00
Daniel Carta 3157de62ff Update build.gradle.kts 2026-02-12 19:17:19 -07:00
Daniel Carta 5ddc032be9 Update to 2026
Updates app to 2026, also a few minor UI fixes.
2026-02-10 10:23:05 -07:00
27 changed files with 60 additions and 185 deletions
Vendored
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+2 -2
View File
@@ -25,8 +25,8 @@ android {
applicationId = "com.ridgebotics.ridgescout"
minSdk = 24
targetSdk = 34
versionCode = 12 // **IMPORTANT** Increment this before releasing on github
versionName = "2.0"// **IMPORTANT** Change this before releasing on github (<Year num since 2024>.<Update Version>)
versionCode = 15 // **IMPORTANT** Increment this before releasing on github
versionName = "3.1"// **IMPORTANT** Change this before releasing on github (<Year num since 2024>.<Update Version>)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -47,11 +47,13 @@ public class MainActivity extends AppCompatActivity {
// Load default match fields
if(!FileEditor.fileExist(Fields.matchFieldsFilename)){
Fields.save(Fields.matchFieldsFilename, Fields.default_match_fields);
FileEditor.toTheArchaicPeriod(Fields.matchFieldsFilename);
}
// Load default pit fields
if(!FileEditor.fileExist(Fields.pitsFieldsFilename)){
Fields.save(Fields.pitsFieldsFilename, Fields.default_pit_fields);
FileEditor.toTheArchaicPeriod(Fields.pitsFieldsFilename);
}
// get time zone for FTP file transfer
@@ -30,36 +30,7 @@ public class Fields {
public static final FieldType[][] default_match_fields = new FieldType[][] {
{
new FieldposType(uuid(),"Auto start pos", "Where does the robot start its auto?", new int[]{0,0}),
new TallyType(uuid(),"Auto L4 Coral", "How many coral did this robot score in L4 during auto?", 0),
new TallyType(uuid(),"Auto L3 Coral", "How many coral did this robot score in L3 during auto?", 0),
new TallyType(uuid(),"Auto L2 Coral", "How many coral did this robot score in L2 during auto?", 0),
new TallyType(uuid(),"Auto L1/Trough Coral", "How many coral did this robot score in L1 during auto?", 0),
new TallyType(uuid(),"Auto Processor Algae", "How many algae did this robot score in the Barge during auto?", 0),
new TallyType(uuid(),"Auto Barge Algae", "How many algae did this robot score in the Barge during auto?", 0),
new DropdownType(uuid(),"Auto Quality", "How did the robot drive during auto?", new String[]{"Smooth", "Jittery"}, 0),
new TextType(uuid(),"Auto Comments", "Anything interesting about auto", ""),
new TallyType(uuid(),"Teleop L4 Coral", "How many coral did this robot score in L4 during auto?", 0),
new TallyType(uuid(),"Teleop L3 Coral", "How many coral did this robot score in L3 during auto?", 0),
new TallyType(uuid(),"Teleop L2 Coral", "How many coral did this robot score in L2 during auto?", 0),
new TallyType(uuid(),"Teleop L1 Coral", "How many coral did this robot score in L1 during auto?", 0),
new TallyType(uuid(),"Teleop Processor Algae", "How many algae did this robot score in the Barge during auto?", 0),
new TallyType(uuid(),"Teleop Barge Algae", "How many algae did this robot score in the Barge during auto?", 0),
new CheckboxType(uuid(),"Upper Algae Removal", "Did the robot remove upper Algae?", 0),
new CheckboxType(uuid(),"Lower Algae Removal", "Did the robot remove lower Algae?", 0),
new DropdownType(uuid(),"Teleop Quality", "How did the robot drive during Teleop?", new String[]{"Smooth", "Jittery"}, 0),
new TextType(uuid(),"Teleop Comments", "Anything interesting about Teleop", ""),
new DropdownType(uuid(),"Climb State", "What was the final condition of the robot?", new String[]{"Nothing", "Continued Cycling", "Park", "Attempted Shallow", "Shallow", "Attempted Deep", "Deep"}, 0),
new DropdownType(uuid(),"Robot Condition", "Was anything broken?", new String[]{"Everything was working", "Something was maybe broken", "Something was broken", "Robot was disabled for part of the match", "Missing robot"}, 0),
new TextType(uuid(),"Other Comments", "Any other comments you have", "")
new FieldposType(uuid(),"Auto start pos", "Where does the robot start its auto?", new int[]{0,0})
}
};
@@ -69,14 +40,6 @@ public class Fields {
new DropdownType(uuid(),"Intake type", "What type of intake does this team have?", new String[]{"Ground only", "Player Station only", "Both", "Other, Info in comments"}, 0),
new DropdownType(uuid(),"Intake Consistency", "How consistent is the robot at intakeing?", new String[]{"Does not work", "Worked a few times during testing", "Works most of the time", "Fails sometimes", "Never fails"}, 0),
new DropdownType(uuid(),"Score Area", "What does this robot score?", new String[]{"Only Algae", "Mostly Algae", "Both", "Mostly Coral", "Only Coral"}, 0),
new CheckboxType(uuid(),"L4 Scoring", "Will the robot score in Layer 4?", 0),
new CheckboxType(uuid(),"L3 Scoring", "Will the robot score in Layer 3?", 0),
new CheckboxType(uuid(),"L2 Scoring", "Will the robot score in Layer 3?", 0),
new CheckboxType(uuid(),"L1/Trough Scoring", "Will the robot score in Layer 1?", 0),
new CheckboxType(uuid(),"Processor Scoring", "Will the robot score in the Processor?", 0),
new CheckboxType(uuid(),"Barge Scoring", "Will the robot score algae in the Barge?", 0),
new DropdownType(uuid(),"Scoring Consistency", "How consistent is the robot at Scoring?", new String[]{"Does not work", "Worked a few times during testing", "Works most of the time", "Fails sometimes", "Never fails"}, 0),
new TextType(uuid(),"Auto Capability", "What autos does this team have?", ""),
@@ -112,7 +112,7 @@ public class DropdownType extends FieldType {
.layout_match_wrap()
.padding(20)
.size(18)
.align_center()
.align_left()
.build());
}
@@ -121,7 +121,7 @@ public class NumberType extends FieldType {
if(data.isNull()) return;
parent.addView(new TextViewBuilder(parent.getContext(), String.valueOf((int) data.get()))
.layout_match_wrap()
.align_center()
.align_left()
.size(24)
.build());
}
@@ -106,7 +106,7 @@ public class TallyType extends FieldType {
if(data.isNull()) return;
parent.addView(new TextViewBuilder(parent.getContext(), String.valueOf((int) data.get()))
.layout_match_wrap()
.align_center()
.align_left()
.size(24)
.build());
}
@@ -115,7 +115,7 @@ public class TextType extends FieldType {
if(data.isNull()) return;
parent.addView(new TextViewBuilder(parent.getContext(), (String) data.get())
.layout_match_wrap()
.align_center()
.align_left()
.size(18)
.build());
}
@@ -11,6 +11,7 @@ import static com.ridgebotics.ridgescout.utility.DataManager.pit_transferValues;
import static com.ridgebotics.ridgescout.utility.DataManager.pit_values;
import android.content.Intent;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.view.Gravity;
@@ -221,19 +222,18 @@ public class TeamsFragment extends Fragment {
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[matchIndex], match_values, match_transferValues);
binding.matchArea.addView(
new TextViewBuilder(getContext(), "M" + (match_num) + " " + split[2] + "-" + split[3] + " by " + psda.username)
.align_center()
.size(30)
.padding(0,0,40,5)
.build()
);
TextView title = new TextViewBuilder(getContext(),
"M" + (match_num) + " " + split[2] + "-" + split[3] + " by " + psda.username)
.align_center()
.size(30)
.padding(0, 0, 40, 5)
.build();
title.setPaintFlags(title.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
binding.matchArea.addView(title);
for (int i = 0; i < psda.data.array.length; i++) {
TextViewBuilder tv = new TextViewBuilder(getContext(), match_latest_values[i].name)
.align_center()
.align_left()
.size(25);
if (psda.data.array[i].isNull()) {
@@ -282,6 +282,7 @@ public class TeamsFragment extends Fragment {
.build()
);
if(data[i] != null)
match_latest_values[i].add_compiled_view(binding.matchArea, data[i]);
}
@@ -307,13 +308,13 @@ public class TeamsFragment extends Fragment {
for(int i = 0; i < match_latest_values.length; i++){
binding.matchArea.addView(
new TextViewBuilder(getContext(), match_latest_values[i].name)
TextView tv = new TextViewBuilder(getContext(), match_latest_values[i].name)
.align_center()
.size(30)
.padding(0,0,20,5)
.build()
);
.build();
tv.setPaintFlags(tv.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
binding.matchArea.addView(tv);
if(data[i] != null)
match_latest_values[i].add_history_view(binding.matchArea, data[i]);
@@ -152,6 +152,8 @@ public class SettingsFragment extends Fragment {
manager.addItem(new CheckboxSettingsItem(EnableQuickAllianceChangeKey, "Enable quick alliance swap", null));
manager.addItem(new DropdownSettingsItem(FieldImageKey, "Field Image", new String[]{
"2026",
"2026 (Flipped)",
"2025",
"2025 (Flipped)"
}));
@@ -174,23 +174,24 @@ public class HttpSync extends Thread {
TransferFile localFile = findInFileArray(localFiles, remoteFile.filename);
boolean shouldUpload;
boolean shouldDownload;
// If there is no file on the sever, upload.
if(localFile == null) {
shouldUpload = true;
shouldDownload = true;
} else {
// If the remote file is the same as the local one, do nothing.
boolean checksumsEqual = !Objects.equals(localFile.checksum, remoteFile.checksum);
boolean checksumsNotEqual = !Objects.equals(localFile.checksum, remoteFile.checksum);
// If the local file is updated after the remote file
boolean after = after(remoteFile.updated, localFile.updated);
// If the local file and remote file's upload dates are exactly the same
boolean datesEqual = !localFile.updated.equals(remoteFile.updated);
boolean datesNotEqual = !localFile.updated.equals(remoteFile.updated);
shouldUpload = (!checksumsEqual && (after) && !datesEqual);
shouldDownload = checksumsNotEqual && after;
}
if(shouldUpload) {
if(shouldDownload) {
downloadFile(remoteFile, serverIP);
// await();
Log.d(getClass().toString(), "RemoteFile: " + remoteFile.filename + ", " + remoteFile.checksum + ", " + remoteFile.updated + ": Downloaded");
@@ -87,10 +87,6 @@ public class CodeScannerView extends Fragment {
);
}
}
private static final int BLOCK_SIZE = 32; // Size of each block in pixels
private Bitmap toGreyscale(Image image){
// Turns out the "Y" In YUV is the Luminance of the pixel.
// Makes converting to greyscale 1000x easier
@@ -98,17 +94,12 @@ public class CodeScannerView extends Fragment {
final int width = image.getWidth();
final int height = image.getHeight();
int[] pixels = new int[width * height];
for (int i = 0; i < width*height; i++) {
// int L = levelMap[yBuffer.get(i) & 0xff];
int L = yBuffer.get(i) & 0xff;
int L = levelMap[yBuffer.get(i) & 0xff];
pixels[i] = 0xff000000 | (L << 16) | (L << 8) | L;
}
threshold(pixels, width, height);
Matrix matrix = new Matrix();
matrix.postRotate(90);
@@ -121,110 +112,6 @@ public class CodeScannerView extends Fragment {
return bitmap;
}
/**
* Performs mean block binarization.
* * Note: The function name 'toGreyscale' is kept per your request,
* but this method actually performs binarization (Black/White).
*
* @param image The array of pixels (ARGB format, standard in Android)
* @param width The width of the image
* @param height The height of the image
*/
private void threshold(int[] image, int width, int height) {
// 1. Setup Block Grid Dimensions
// We use Math.ceil to ensure partial blocks at the edges are counted
int gridCols = (int) Math.ceil((double) width / BLOCK_SIZE);
int gridRows = (int) Math.ceil((double) height / BLOCK_SIZE);
// Arrays to store statistics for each block
int[] blockSums = new int[gridCols * gridRows];
int[] blockCounts = new int[gridCols * gridRows];
int[] blockMeans = new int[gridCols * gridRows];
// --- PASS 1: Compute Block Statistics (Mean Intensity) ---
for (int y = 0; y < height; y++) {
int blockY = y / BLOCK_SIZE;
for (int x = 0; x < width; x++) {
int blockX = x / BLOCK_SIZE;
int blockIndex = blockY * gridCols + blockX;
// Extract average grayscale value from ARGB pixel
int pixel = image[y * width + x];
int r = (pixel >> 16) & 0xFF;
int g = (pixel >> 8) & 0xFF;
int b = pixel & 0xFF;
// Simple average (matches BoofCV's typical approach for generic buffers)
int gray = (r + g + b) / 3;
blockSums[blockIndex] += gray;
blockCounts[blockIndex]++;
}
}
// Calculate the mean for every block
for (int i = 0; i < blockSums.length; i++) {
if (blockCounts[i] > 0) {
blockMeans[i] = blockSums[i] / blockCounts[i];
}
}
// --- PASS 2: Apply Threshold using Local 3x3 Block Region ---
for (int blockY = 0; blockY < gridRows; blockY++) {
for (int blockX = 0; blockX < gridCols; blockX++) {
// Calculate the threshold for this specific block
// by averaging the means of the surrounding 3x3 blocks.
// This corresponds to BoofCV's `thresholdFromLocalBlocks` logic.
long localSum = 0;
int localCount = 0;
int startGridY = Math.max(0, blockY - 1);
int endGridY = Math.min(gridRows - 1, blockY + 1);
int startGridX = Math.max(0, blockX - 1);
int endGridX = Math.min(gridCols - 1, blockX + 1);
for (int ny = startGridY; ny <= endGridY; ny++) {
for (int nx = startGridX; nx <= endGridX; nx++) {
localSum += blockMeans[ny * gridCols + nx];
localCount++;
}
}
int threshold = (localCount > 0) ? (int) (localSum / localCount) : 127;
// Apply this threshold to all pixels within the current block
int startPixelX = blockX * BLOCK_SIZE;
int startPixelY = blockY * BLOCK_SIZE;
// Handle image boundary (if image size isn't perfectly divisible by block size)
int endPixelX = Math.min(startPixelX + BLOCK_SIZE, width);
int endPixelY = Math.min(startPixelY + BLOCK_SIZE, height);
for (int y = startPixelY; y < endPixelY; y++) {
for (int x = startPixelX; x < endPixelX; x++) {
int index = y * width + x;
// Recalculate gray to compare against threshold
int pixel = image[index];
int r = (pixel >> 16) & 0xFF;
int g = (pixel >> 8) & 0xFF;
int b = pixel & 0xFF;
int gray = (r + g + b) / 3;
// Binarize: Black if <= threshold, White if > threshold
if (gray <= threshold) {
image[index] = 0xFF000000; // Black (Alpha 255)
} else {
image[index] = 0xFFFFFFFF; // White (Alpha 255)
}
}
}
}
}
}
public void scanQRCode(Bitmap bitmap) {
// CodeScanTask async = new CodeScanTask();
@@ -81,6 +81,12 @@ public class FieldPosView extends FrameLayout {
case "2025 (Flipped)":
setImageResource(R.drawable.field_2025_flipped);
break;
case "2026":
setImageResource(R.drawable.field_2026);
break;
case "2026 (Flipped)":
setImageResource(R.drawable.field_2026_flipped);
break;
}
}
@@ -263,6 +263,11 @@ public final class FileEditor {
}
}
// Sets the date modified to a long, long time ago
public static boolean toTheArchaicPeriod(String name) {
return new File(baseDir + name).setLastModified(0);
}
public static boolean createFile(String filepath){
if(fileExist(filepath)){
return true;
@@ -46,8 +46,8 @@ public class SettingsManager {
hm.put(UnameKey, "Username");
hm.put(SelEVCodeKey, "unset");
hm.put(WifiModeKey, false);
hm.put(YearNumKey, 2025);
hm.put(FieldImageKey, "2025");
hm.put(YearNumKey, 2026);
hm.put(FieldImageKey, "2026");
hm.put(MatchNumKey, 0);
hm.put(AllyPosKey, "red-1");
hm.put(DataModeKey, 0);
BIN
View File
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 636 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 KiB

@@ -60,7 +60,7 @@
android:layout_height="wrap_content"
android:text="Pit Data"
android:textAlignment="center"
android:textSize="24sp"
android:textSize="35sp"
app:layout_constraintTop_toBottomOf="@+id/team_description2"
tools:layout_editor_absoluteX="0dp" />
@@ -73,6 +73,11 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<View
android:id="@+id/divider3"
android:layout_width="match_parent"
android:layout_height="12dp"
android:background="?android:attr/listDivider" />
</LinearLayout>
<TextView
@@ -81,7 +86,7 @@
android:layout_height="wrap_content"
android:text="Match Data"
android:textAlignment="center"
android:textSize="24sp"
android:textSize="35sp"
app:layout_constraintTop_toBottomOf="@+id/team_description2"
tools:layout_editor_absoluteX="0dp" />
@@ -139,9 +144,13 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<View
android:id="@+id/divider2"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
</LinearLayout>
</LinearLayout>
</ScrollView>
+1 -1
View File
@@ -1,5 +1,5 @@
[versions]
agp = "8.13.0"
agp = "8.13.2"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
-1
View File
@@ -17,7 +17,6 @@ dependencyResolutionManagement {
google()
mavenCentral()
maven ( url = "https://jitpack.io" )
jcenter()
}
}