diff --git a/app/src/main/java/com/evsuite/abrp/MainActivity.java b/app/src/main/java/com/evsuite/abrp/MainActivity.java
index 22bb3eb..6a136c9 100644
--- a/app/src/main/java/com/evsuite/abrp/MainActivity.java
+++ b/app/src/main/java/com/evsuite/abrp/MainActivity.java
@@ -41,7 +41,7 @@ public class MainActivity extends AppCompatActivity {
private static final int LOCATION_PERMISSION_REQUEST = 100;
/** Shared with {@link AbrpUploadService}, which reads it on every tick. */
static final String WIFI_AUTO_KEY = "wifi_auto_connect";
- private static final String REPOSITORY_URL = "https://github.com/fatihdonmezdev/EVABRP";
+ private static final String REPOSITORY_URL = "https://github.com/fatihdonmezdev/MG4ABRP";
/** Top-bar tabs, in page order. Parallel to {@link #panes}. */
private static final int[] TAB_IDS =
@@ -230,6 +230,7 @@ public class MainActivity extends AppCompatActivity {
setUpPager();
findViewById(R.id.about_button).setOnClickListener(v -> showAbout());
+ findViewById(R.id.update_button).setOnClickListener(v -> checkForUpdate(v));
bindCadenceControls();
@@ -629,6 +630,30 @@ public class MainActivity extends AppCompatActivity {
AbrpUploadService.reloadSettings();
}
+ // ---------- Update check ----------
+
+ /**
+ * Checks for a newer build, and installs one if it is there.
+ *
+ * The button is disabled for the duration rather than guarded by a flag: the whole
+ * sequence — API call, download, {@code pm install} — runs on a background thread, and
+ * a second press would start a second download of the same APK. It comes back on when
+ * the result lands, whatever that result is.
+ *
+ * The outcome goes to a toast because there is nowhere better: the check is reachable
+ * from every page, so no one page owns the answer.
+ */
+ private void checkForUpdate(View button) {
+ button.setEnabled(false);
+ android.widget.Toast.makeText(this, R.string.update_checking,
+ android.widget.Toast.LENGTH_SHORT).show();
+ UpdateHook.checkInBackground(this, message -> {
+ button.setEnabled(true);
+ android.widget.Toast.makeText(this, message,
+ android.widget.Toast.LENGTH_LONG).show();
+ });
+ }
+
// ---------- Wi-Fi page ----------
private void bindWifiPane() {
diff --git a/app/src/main/res/drawable/ic_refresh.xml b/app/src/main/res/drawable/ic_refresh.xml
new file mode 100644
index 0000000..a4c3357
--- /dev/null
+++ b/app/src/main/res/drawable/ic_refresh.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 98b0c16..f2b3d5d 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -92,8 +92,21 @@
android:text="@string/tab_log"
app:icon="@drawable/ic_tab_log" />
-
+
+
+
Shows vehicle telemetry upload status
ABRP Uploader
Stop
+ Güncelleme kontrol et
+ Kontrol ediliyor…
About
Fatih\'sMG4 Hakkında
Version %1$s
Fork of malys / EVABRPUploader\nand Leon Kernan / ABRP_Uploader
- github.com/fatihdonmezdev/EVABRP
+ github.com/fatihdonmezdev/MG4ABRP
MG4 EH32 · SWI69
EVABRPUploader logo
QR code for the project repository
diff --git a/app/src/stable/java/com/evsuite/abrp/UpdateHook.java b/app/src/stable/java/com/evsuite/abrp/UpdateHook.java
index 10b38ea..36f81a0 100644
--- a/app/src/stable/java/com/evsuite/abrp/UpdateHook.java
+++ b/app/src/stable/java/com/evsuite/abrp/UpdateHook.java
@@ -13,8 +13,21 @@ final class UpdateHook {
private UpdateHook() { }
+ /**
+ * Mirrors the unstable seam's shape so shared UI code compiles in both flavours.
+ * Nothing implements it here.
+ */
+ interface Listener {
+ void onResult(String message);
+ }
+
/** Does nothing. Stable users install updates themselves. */
static void checkInBackground(Context context) { }
+ /** Answers immediately that this channel does not self-update. */
+ static void checkInBackground(Context context, Listener listener) {
+ if (listener != null) listener.onResult("Bu sürümde otomatik güncelleme yok");
+ }
+
static boolean isSupported() { return false; }
}
diff --git a/app/src/unstable/java/com/evsuite/abrp/OtaUpdater.java b/app/src/unstable/java/com/evsuite/abrp/OtaUpdater.java
index c906411..6d13261 100644
--- a/app/src/unstable/java/com/evsuite/abrp/OtaUpdater.java
+++ b/app/src/unstable/java/com/evsuite/abrp/OtaUpdater.java
@@ -46,7 +46,7 @@ final class OtaUpdater {
* nothing until this repository publishes a release, which is the intended quiet.
*/
private static final String RELEASES_API =
- "https://api.github.com/repos/fatihdonmezdev/EVABRP/releases";
+ "https://api.github.com/repos/fatihdonmezdev/MG4ABRP/releases";
/**
* Hosts an update may come from. The githubusercontent entries are the CDNs GitHub
diff --git a/app/src/unstable/java/com/evsuite/abrp/UpdateHook.java b/app/src/unstable/java/com/evsuite/abrp/UpdateHook.java
index 371c072..34e47ad 100644
--- a/app/src/unstable/java/com/evsuite/abrp/UpdateHook.java
+++ b/app/src/unstable/java/com/evsuite/abrp/UpdateHook.java
@@ -1,13 +1,89 @@
package com.evsuite.abrp;
import android.content.Context;
+import android.util.Log;
+
+import java.io.File;
+
/**
- * OTA is suspended while the suite safety and legal audit is open.
- * Keep this flavour seam inert until a reviewed change explicitly re-enables it.
+ * Drives {@link OtaUpdater} — UNSTABLE BUILDS ONLY.
+ *
+ * Upstream keeps this seam inert pending a safety and legal audit of the published suite.
+ * This fork publishes its own builds from its own repository, signed with the platform key
+ * the head unit already trusts, so that audit does not govern here and the seam is live.
+ *
+ * Installation goes through {@code pm install -r}, which only a platform-signed build may
+ * call. {@link OtaUpdater#install} checks the downloaded APK's certificate against the
+ * running app's first and refuses a mismatch, so an update signed with any other key is
+ * deleted rather than installed — the same check that makes an upstream APK unusable here.
*/
final class UpdateHook {
+
+ private static final String TAG = "EVABRP.Update";
+
private UpdateHook() { }
- static boolean isSupported() { return false; }
- static void checkInBackground(Context context) { }
+ static boolean isSupported() { return true; }
+
+ /** Result of one check, for the UI to report. */
+ interface Listener {
+ void onResult(String message);
+ }
+
+ /** Fire-and-forget check on app start. Nothing is reported; failures stay in the log. */
+ static void checkInBackground(Context context) {
+ checkInBackground(context, null);
+ }
+
+ /**
+ * Checks, downloads and installs in one background pass.
+ *
+ * The whole sequence runs off the main thread because every step blocks: the release
+ * API call, the download, and {@code pm install}. [listener] is called back on the main
+ * thread, so a UI caller can write straight to a view.
+ */
+ static void checkInBackground(Context context, Listener listener) {
+ Context app = context.getApplicationContext();
+ android.os.Handler main = new android.os.Handler(android.os.Looper.getMainLooper());
+ new Thread(() -> {
+ String message;
+ try {
+ message = runCheck(app);
+ } catch (Throwable t) {
+ Log.w(TAG, "Update check threw", t);
+ message = "Güncelleme kontrolü başarısız";
+ }
+ if (listener != null) {
+ final String result = message;
+ main.post(() -> listener.onResult(result));
+ }
+ }, "ota-check").start();
+ }
+
+ /** The blocking part. Returns a line describing what happened, for the UI. */
+ private static String runCheck(Context app) {
+ String current;
+ try {
+ current = app.getPackageManager()
+ .getPackageInfo(app.getPackageName(), 0).versionName;
+ } catch (Exception e) {
+ return "Sürüm okunamadı";
+ }
+
+ OtaUpdater.Update update = OtaUpdater.check(current);
+ if (update == null) return "Güncel (" + current + ")";
+
+ Log.i(TAG, "Update available: " + update.versionName);
+ File apk = OtaUpdater.download(app, update);
+ if (apk == null) return "İndirme başarısız (" + update.versionName + ")";
+
+ // The cached APK is cleared whichever way install goes: a rejected archive must not
+ // linger, and an accepted one has already been copied out by the package manager.
+ boolean installed = OtaUpdater.install(app, apk);
+ if (!apk.delete()) Log.w(TAG, "Could not remove cached OTA APK");
+
+ return installed
+ ? "Güncellendi: " + update.versionName + " — yeniden başlatın"
+ : "Kurulum reddedildi (imza uyuşmuyor?)";
+ }
}