App 2.19.3: update log on the Logs tab

Every update and install step, with the platform's own result, kept
across the restart an install causes, so a failed install on the car
can be read off the screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
This commit is contained in:
2026-10-23 21:15:44 +02:00
co-authored by Claude Opus 5.5
parent 50217aef74
commit 51beea5ed8
11 changed files with 147 additions and 7 deletions
@@ -81,6 +81,7 @@ public class AbrpUploadService extends Service {
super.onCreate();
prefs = getSharedPreferences("abrp_prefs", MODE_PRIVATE);
uploadLog.clear();
UpdateLog.noteVersion(this, BuildConfig.VERSION_NAME);
scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
Thread t = new Thread(r, "abrp-upload");
t.setDaemon(false);
@@ -285,6 +285,7 @@ public class MainActivity extends AppCompatActivity {
panes = new View[] { vehiclePane, consumptionPane, chargingPane, servicePane, serverPane,
settingsPane, logPane };
UpdateLog.noteVersion(this, BuildConfig.VERSION_NAME);
bindVehiclePane();
bindConsumptionPane();
bindChargingPane();
@@ -1792,13 +1793,21 @@ public class MainActivity extends AppCompatActivity {
private void refreshCallLog() {
java.util.List<UploadLog.Entry> entries = AbrpUploadService.log().recent();
// Update steps first: after a failed install, they are what the driver came here for.
java.util.List<String> updates = UpdateLog.lines(this);
StringBuilder sb = new StringBuilder();
if (!updates.isEmpty()) {
sb.append(getString(R.string.update_log_title));
for (String line : updates) sb.append('\n').append(line);
sb.append('\n');
}
if (entries.isEmpty()) {
callLogText.setText(R.string.call_log_empty);
if (sb.length() == 0) callLogText.setText(R.string.call_log_empty);
else callLogText.setText(sb.append('\n').append(getString(R.string.call_log_empty)).toString());
return;
}
java.text.SimpleDateFormat format =
new java.text.SimpleDateFormat("HH:mm:ss", java.util.Locale.US);
StringBuilder sb = new StringBuilder();
for (UploadLog.Entry entry : entries) {
if (sb.length() > 0) sb.append('\n');
sb.append(entry.success ? "OK " : "ERR ")
@@ -0,0 +1,78 @@
package com.evsuite.abrp;
import android.content.Context;
import android.content.SharedPreferences;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
* The last {@link #MAX_LINES} steps of updating the app — checks, USB installs, and the
* platform's own words about an install — shown on the Logs tab.
*
* Kept apart from {@link UploadLog}, whose failures decide the service's health, and stored in
* preferences rather than memory: a successful install restarts the process, and the car has
* no adb, so this is the only place the driver can read why an install failed. It never holds
* a URL or a credential, only what happened.
*/
final class UpdateLog {
static final int MAX_LINES = 10;
private static final String PREFS = "update_log";
private static final String KEY = "lines";
private static final String KEY_VERSION = "version";
private UpdateLog() {}
static void record(Context context, String what) {
if (context == null || what == null) return;
String line = new SimpleDateFormat("dd.MM. HH:mm:ss", Locale.US).format(new Date()) + " " + what;
synchronized (UpdateLog.class) {
SharedPreferences prefs = context.getApplicationContext()
.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
List<String> lines = append(split(prefs.getString(KEY, null)), line, MAX_LINES);
prefs.edit().putString(KEY, String.join("\n", lines)).commit();
}
}
/**
* Records the version the app now runs, once per version: after an install, the line that
* says it worked, since the installing process does not live to say so.
*/
static void noteVersion(Context context, String version) {
if (context == null || version == null) return;
SharedPreferences prefs = context.getApplicationContext()
.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
String last = prefs.getString(KEY_VERSION, null);
if (version.equals(last)) return;
prefs.edit().putString(KEY_VERSION, version).commit();
if (last != null) record(context, "Now running " + version + " (was " + last + ")");
}
/** Newest first. */
static List<String> lines(Context context) {
SharedPreferences prefs = context.getApplicationContext()
.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
return split(prefs.getString(KEY, null));
}
/** {@code line} in front, newlines flattened, at most {@code max} lines kept. */
static List<String> append(List<String> newestFirst, String line, int max) {
List<String> out = new ArrayList<>();
out.add(line.replace('\n', ' ').replace('\r', ' '));
for (String l : newestFirst) {
if (out.size() >= max) break;
out.add(l);
}
return out;
}
private static List<String> split(String stored) {
if (stored == null || stored.isEmpty()) return new ArrayList<>();
return new ArrayList<>(Arrays.asList(stored.split("\n")));
}
}