From 8f3c6c42ccc77dce706e47454f36b9021a248838 Mon Sep 17 00:00:00 2001 From: Robert Jonsson Date: Sun, 13 Sep 2026 19:09:41 +0200 Subject: [PATCH 01/10] fix: copy bundled workouts on fresh install (#1383) Fix an issue where bundled workouts were not copied to internal storage on fresh installs. - In `MainLayout.handleBundled()`, initialized `dstDir` using `dst` (the target subdirectory) instead of `dstBase` (the parent directory) so subdirectories like `app_workouts` are properly created. - Updated SharedPreferences logic to store the "installed" preference flag (`install_bundled_...`) only after a successful file copy, ensuring failed attempts can be retried on subsequent app launches. --- app/src/main/org/runnerup/view/MainLayout.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/org/runnerup/view/MainLayout.java b/app/src/main/org/runnerup/view/MainLayout.java index 0cb5ffd06..ebdf1cf20 100644 --- a/app/src/main/org/runnerup/view/MainLayout.java +++ b/app/src/main/org/runnerup/view/MainLayout.java @@ -315,7 +315,7 @@ private void handleBundled(AssetManager mgr, String srcBase, String dstBase) { if (!isFile) { // The request is hierarchical, source is still on a directory level - File dstDir = new File(dstBase); + File dstDir = new File(dst); //noinspection ResultOfMethodCallIgnored dstDir.mkdir(); if (!dstDir.isDirectory()) { @@ -348,14 +348,13 @@ private void handleBundled(AssetManager mgr, String srcBase, String dstBase) { continue; } - pref.edit().putBoolean(key, true).apply(); - Log.d(getClass().getName(), "Copying: " + dst); InputStream input = null; try { input = mgr.open(src); FileUtil.copy(input, dst); handleHooks(add); + pref.edit().putBoolean(key, true).apply(); // Store successfull copy } catch (IOException e) { e.printStackTrace(); } finally { From 9436e477dbbe080216518ee97e5c893ddc712934 Mon Sep 17 00:00:00 2001 From: Gerhard Olsson <6248932+gerhardol@users.noreply.github.com> Date: Mon, 14 Sep 2026 22:35:23 +0200 Subject: [PATCH 02/10] fix: analyzeReleaseR8Config warnings (#1385) - fix some gradle 10 syntax in builds.gradle and gradle.properties - use ivy repo for ant+ lib --- app/build.gradle | 14 +++----------- app/lint-baseline.xml | 13 +------------ build.gradle | 34 +++++++++++++++++++++------------- common/lint-baseline.xml | 2 +- gradle.properties | 9 --------- hrdevice/build.gradle | 4 ++-- hrdevice/lint-baseline.xml | 2 +- wear/lint-baseline.xml | 2 +- 8 files changed, 30 insertions(+), 50 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 03cbdea00..4ec65b24e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -100,7 +100,7 @@ android { lint { baseline = file('lint-baseline.xml') checkReleaseBuilds = true - if (mapboxEnabled) { + if (rootProject.ext.mapboxEnabled) { lintConfig = file('lint.xml') } else { lintConfig = file('lint-osmdroid.xml') @@ -131,7 +131,8 @@ android { repositories { google() - mavenCentral() //MapBox GraphView + mavenCentral() + maven { url = 'https://jitpack.io' } if (rootProject.ext.mapboxEnabled) { maven { url = 'https://api.mapbox.com/downloads/v2/releases/maven' @@ -183,15 +184,6 @@ dependencies { testImplementation "org.mockito:mockito-core:${rootProject.ext.mockitoVersion}" } -allprojects { - tasks.withType(JavaCompile).tap { - configureEach { - // options.compilerArgs << "-Xlint:deprecation"; - options.compilerArgs << "-Xlint:unchecked" - } - } -} - def props = new Properties() // For locally signed builds only, Google Play signs the bundle if (rootProject.file("release.properties").exists()) { diff --git a/app/lint-baseline.xml b/app/lint-baseline.xml index 8a682b1ce..163c54afe 100644 --- a/app/lint-baseline.xml +++ b/app/lint-baseline.xml @@ -1,5 +1,5 @@ - + - - - - - + diff --git a/gradle.properties b/gradle.properties index b33955542..b31340008 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,16 +2,11 @@ # Build with ./gradlew app:minifyLatestReleaseWithProguard # required for com.jjoe64:graphview:4.2.2 android.enableJetifier=true -android.useAndroidX=true -org.gradle.caching = true -org.gradle.parallel = true -org.gradle.configureondemand = true org.gradle.vfs.watch = true org.gradle.unsafe.configuration-cache=true android.nonTransitiveRClass=true android.nonFinalResIds=true org.gradle.jvmargs=-Xmx4096m --XX=MaxMetaspaceSize=1024m -Dfile.encoding=UTF-8 # org.runnerup.free=true @@ -21,9 +16,5 @@ org.gradle.jvmargs=-Xmx4096m # Disable map (i.e. neither MapBox nor osmdroid is used) # org.runnerup.nomap=true -# Disable warnings due to the way ant+ is consumed -# Using flatDir should be avoided because it doesn't support any meta-data formats -android.sync.suppressAgpWarnings=FLAT_DIR_REPOSITORY_USED - # Enabled parallel sync for Gradle 9.4+ org.gradle.tooling.parallel=true diff --git a/hrdevice/build.gradle b/hrdevice/build.gradle index cdfa31ac3..c151dd561 100644 --- a/hrdevice/build.gradle +++ b/hrdevice/build.gradle @@ -64,6 +64,6 @@ dependencies { implementation "androidx.preference:preference:${rootProject.ext.preference_version}" if (rootProject.ext.antPlusLibName) { - implementation(name: rootProject.ext.antPlusLibName, ext: 'aar') + implementation "local:${rootProject.ext.antPlusLibName}:1.0@aar" } -} \ No newline at end of file +} diff --git a/hrdevice/lint-baseline.xml b/hrdevice/lint-baseline.xml index 5a1071ecf..e8c77f8fe 100644 --- a/hrdevice/lint-baseline.xml +++ b/hrdevice/lint-baseline.xml @@ -1,5 +1,5 @@ - + - + Date: Wed, 16 Sep 2026 23:40:59 +0200 Subject: [PATCH 03/10] fix: gha tools install (#1388) --- .github/workflows/actions.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index f8a58e383..d5c0b898e 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -45,19 +45,23 @@ jobs: - name: Git LFS Pull run: git lfs pull - + - name: Setup Android SDK - uses: android-actions/setup-android@v4 + run: | + echo "ANDROID_HOME=/usr/local/lib/android/sdk" >> $GITHUB_ENV + echo "ANDROID_SDK_ROOT=/usr/local/lib/android/sdk" >> $GITHUB_ENV + echo "/usr/local/lib/android/sdk/cmdline-tools/latest/bin" >> $GITHUB_PATH + echo "/usr/local/lib/android/sdk/platform-tools" >> $GITHUB_PATH - name: Install Android SDK components run: | - sdkmanager "tools" - sdkmanager "build-tools;${ANDROID_BUILD_TOOLS}" - sdkmanager "platforms;android-${ANDROID_API}" + yes | sdkmanager --licenses + sdkmanager "platforms;android-${{ env.ANDROID_API }}" + sdkmanager "build-tools;${{ env.ANDROID_BUILD_TOOLS }}" sdkmanager "extras;android;m2repository" sdkmanager "extras;google;m2repository" sdkmanager "extras;google;google_play_services" - + - name: Prepare builddir run: | # secrets are only available for collabrators, other will build with OsmDroid (but a few more options than F-Droid still) From b8a2f8ebe40e4ec3f258b50e31b0dd1771d8fa2d Mon Sep 17 00:00:00 2001 From: Gerhard Olsson <6248932+gerhardol@users.noreply.github.com> Date: Wed, 16 Sep 2026 23:51:09 +0200 Subject: [PATCH 04/10] fix: ble hr deprecation fixes (#1387) Update code with api-13 to remove deprecated calls. (suppress for = 5.0 + * Connects to a Bluetooth Low Energy module for Android versions >= 5.0 This implementation + * suppresses some deprecated, keep compatibility with versions before api-33/android-13 * * @author jonas */ @@ -78,28 +80,37 @@ public class AndroidBLEHRProvider extends BtHRBase implements HRProvider { new BluetoothGattCallback() { @Override - public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic arg0) { + @SuppressWarnings("deprecation") + public void onCharacteristicChanged( + BluetoothGatt gatt, BluetoothGattCharacteristic btChar) { + byte[] value = btChar.getValue(); + onCharacteristicChanged(gatt, btChar, value); + } + + @Override + public void onCharacteristicChanged( + BluetoothGatt gatt, BluetoothGattCharacteristic btChar, byte[] value) { try { - if (!checkBtGattOnlyLogError(gatt)) { + if (!checkBtGatt(gatt, true)) { return; } - if (!arg0.getUuid().equals(HEART_RATE_MEASUREMENT_CHARAC)) { - log("onCharacteristicChanged(" + arg0 + ") != HEART_RATE ??"); + if (!btChar.getUuid().equals(HEART_RATE_MEASUREMENT_CHARAC)) { + log("onCharacteristicChanged(" + btChar + ") != HEART_RATE ??"); return; } - int length = arg0.getValue().length; - if (length == 0) { - log("onCharacteristicChanged length = 0"); + int length = value.length; + if (length < 2 || length < 3 && isHeartRateInUINT16(value[0])) { + log("onCharacteristicChanged: insufficient data, length=" + length); return; } int val; - if (isHeartRateInUINT16(arg0.getValue()[0])) { - val = arg0.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 1); + if (isHeartRateInUINT16(value[0])) { + val = ((value[2] & 0xFF) << 8) | (value[1] & 0xFF); } else { - val = arg0.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 1); + val = value[1] & 0xFF; } hrTimestamp = System.currentTimeMillis(); @@ -132,39 +143,45 @@ public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteris } } + @Override + @SuppressWarnings("deprecation") + public void onCharacteristicRead( + BluetoothGatt gatt, BluetoothGattCharacteristic btChar, int status) { + byte[] value = btChar.getValue(); + onCharacteristicRead(gatt, btChar, value, status); + } + @Override public void onCharacteristicRead( - BluetoothGatt gatt, BluetoothGattCharacteristic arg0, int status) { + BluetoothGatt gatt, BluetoothGattCharacteristic btChar, byte[] value, int status) { try { - log( - "onCharacteristicRead(): " - + gatt - + ", char: " - + arg0.getUuid() - + ", status: " - + status); + UUID charUuid = btChar.getUuid(); + log("onCharacteristicRead(): " + gatt + ", char: " + charUuid + ", status: " + status); - if (!checkBtGatt(gatt)) { + if (!checkBtGatt(gatt, false)) { return; } - UUID charUuid = arg0.getUuid(); if (charUuid.equals(FIRMWARE_REVISON_UUID)) { log("firmware => startHR()"); - // triggered from DummyReadForSecLevelCheck + // triggered from ReadDeviceInfoServices startHR(); } else if (mSupportPaired && charUuid.equals(HARDWARE_REVISON_UUID)) { // Some paired devices like Huami (MiBand) has no firmware uuid log("BLE hardware rev => startHR()"); - // triggered from DummyReadForSecLevelCheck + // triggered from ReadDeviceInfoServices startHR(); } else if (charUuid.equals(BATTERY_LEVEL_CHARAC)) { - log("batterylevel: " + arg0); - batteryLevel = arg0.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0); - log("Battery level: " + batteryLevel); + if (value != null && value.length >= 1) { + batteryLevel = value[0] & 0xFF; + log("Battery level: " + batteryLevel); + } else { + batteryLevel = HRProvider.BATTERY_LEVEL_UNAVAILABLE; + log("Battery level unavailable"); + } - log(" => startHR()"); - // triggered from DummyReadForSecLevelCheck + log("battery level => startHR()"); + // triggered from ReadDeviceInfoServices startHR(); } else { @@ -192,7 +209,7 @@ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState + mIsConnected + ", mIsDisconnecting: " + mIsDisconnecting); - if (!checkBtGatt(gatt)) { + if (!checkBtGatt(gatt, false)) { log("checkBtGatt => return"); return; } @@ -231,9 +248,16 @@ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState } @Override - public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor arg0, int status) { + @SuppressWarnings("deprecation") + public void onDescriptorRead( + BluetoothGatt gatt, BluetoothGattDescriptor btDesc, int status) { + onDescriptorRead(gatt, btDesc, status, btDesc.getValue()); + } - BluetoothGattCharacteristic mHRMcharac = arg0.getCharacteristic(); + @Override + public void onDescriptorRead( + BluetoothGatt gatt, BluetoothGattDescriptor btDesc, int status, byte[] value) { + BluetoothGattCharacteristic mHRMcharac = btDesc.getCharacteristic(); if (!enableNotification(gatt, true, mHRMcharac)) { reportConnectFailed("Failed to enable notification in onDescriptorRead"); } @@ -243,11 +267,11 @@ public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor arg0, i public void onServicesDiscovered(BluetoothGatt gatt, int status) { log("onServicesDiscoverd(): " + gatt + ", status: " + status); - if (!checkBtGatt(gatt)) { + if (!checkBtGatt(gatt, false)) { return; } - List list = btGatt.getServices(); + List list = gatt.getServices(); for (BluetoothGattService s : list) { log("Found service: " + s.getType() + ", " + s.getInstanceId() + ", " + s.getUuid()); for (BluetoothGattCharacteristic a : s.getCharacteristics()) { @@ -257,13 +281,13 @@ public void onServicesDiscovered(BluetoothGatt gatt, int status) { log(" serv: " + a.getUuid()); } - if (s.getUuid().equals(BATTERY_SERVICE)) { + if (s.getUuid().equals(BATTERY_SERVICE_UUID)) { hasBatteryService = true; } } - log(" => DummyRead"); - DummyReadForSecLevelCheck(gatt); + log(" => ReadDeviceInfoServices"); + ReadDeviceInfoServices(gatt); // if GATT_SUCCESS, continue in onCharacteristicRead // no report on error } @@ -271,7 +295,7 @@ public void onServicesDiscovered(BluetoothGatt gatt, int status) { /* * from Samsung HRPService.java */ - private void DummyReadForSecLevelCheck(BluetoothGatt btGatt) { + private void ReadDeviceInfoServices(BluetoothGatt btGatt) { if (btGatt == null) { return; } @@ -280,9 +304,13 @@ private void DummyReadForSecLevelCheck(BluetoothGatt btGatt) { return; } - BluetoothGattService disService = btGatt.getService(DIS_UUID); + BluetoothGattService disService = btGatt.getService(DEVICE_INFORMATION_SERVICE_UUID); if (disService == null) { - reportConnectFailed("Dis service not found"); + log("Device Information service not found"); + // This occurs for e.g. Garmin devices transmitting HR from a watch. + // Start HR (check HRP service) without reading characteristic (no trigger of + // onCharacteristicRead()) + startHR(); return; } BluetoothGattCharacteristic firmwareIdCharc = @@ -302,11 +330,12 @@ private void DummyReadForSecLevelCheck(BluetoothGatt btGatt) { } private boolean isHeartRateInUINT16(byte b) { + // Bluetooth SIG UUID 0x2A37 first bit in first byte describes the length return (b & 1) != 0; } private void startHR() { - BluetoothGattService mHRP = btGatt.getService(HRP_SERVICE); + BluetoothGattService mHRP = btGatt.getService(HEART_RATE_SERVICE_UUID); if (mHRP == null) { reportConnectFailed("HRP service not found!"); return; @@ -318,7 +347,8 @@ private void startHR() { reportConnectFailed("HEART RATE MEASUREMENT charateristic not found!"); return; } - BluetoothGattDescriptor mHRMccc = mHRMcharac.getDescriptor(CCC); + BluetoothGattDescriptor mHRMccc = + mHRMcharac.getDescriptor(CLIENT_CHARACTERISTIC_CONFIGURATION); if (mHRMccc == null) { reportConnectFailed("CCC for HEART RATE MEASUREMENT charateristic not found!"); return; @@ -330,7 +360,7 @@ private void startHR() { } private boolean readBatteryLevel() { - BluetoothGattService mBS = btGatt.getService(BATTERY_SERVICE); + BluetoothGattService mBS = btGatt.getService(BATTERY_SERVICE_UUID); if (mBS == null) { log("Battery service not found."); return false; @@ -392,7 +422,7 @@ private static BluetoothGattCharacteristic getHeartrateCharacteristic(BluetoothG if (gatt == null) { return null; } - BluetoothGattService mHRP = gatt.getService(HRP_SERVICE); + BluetoothGattService mHRP = gatt.getService(HEART_RATE_SERVICE_UUID); if (mHRP == null) { return null; } @@ -423,7 +453,9 @@ public void open(Handler handler, HRClient hrClient) { this.hrClient = hrClient; this.hrClientHandler = handler; if (btAdapter == null) { - btAdapter = BluetoothAdapter.getDefaultAdapter(); + @SuppressWarnings("deprecation") + BluetoothAdapter adapterDeprecated = BluetoothAdapter.getDefaultAdapter(); + btAdapter = adapterDeprecated; } hrClient.onOpenResult(btAdapter != null); @@ -468,18 +500,27 @@ private boolean enableNotification( return false; } - BluetoothGattDescriptor clientConfig = charac.getDescriptor(CCC); + BluetoothGattDescriptor clientConfig = + charac.getDescriptor(CLIENT_CHARACTERISTIC_CONFIGURATION); if (clientConfig == null) { log("clientConfig == null"); return false; } - if (onoff) { - clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); + boolean result; + byte[] notifValue = + onoff + ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE + : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + result = gatt.writeDescriptor(clientConfig, notifValue) == BluetoothStatusCodes.SUCCESS; } else { - clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); + @SuppressWarnings("deprecation") + boolean setValueResult = clientConfig.setValue(notifValue); + @SuppressWarnings("deprecation") + boolean writeDescResult = gatt.writeDescriptor(clientConfig); + result = setValueResult && writeDescResult; } - boolean result = gatt.writeDescriptor(clientConfig); if (result) { mNotificationsOn = onoff; } @@ -592,7 +633,9 @@ public void startScan() { mIsScanning = true; btScanner.startScan( Collections.singletonList( - new ScanFilter.Builder().setServiceUuid(new ParcelUuid(HRP_SERVICE)).build()), + new ScanFilter.Builder() + .setServiceUuid(new ParcelUuid(HEART_RATE_SERVICE_UUID)) + .build()), new ScanSettings.Builder().build(), mLeScanCallback); } @@ -636,7 +679,9 @@ private BluetoothGatt connectGatt(BluetoothDevice device, BluetoothGattCallback if (!checkPermission(Manifest.permission.BLUETOOTH_CONNECT, "connectGatt")) { return null; } - return device.connectGatt(context, false, callbacks); + @SuppressWarnings("deprecation") + BluetoothGatt gattDeprecated = device.connectGatt(context, false, callbacks); + return gattDeprecated; } private boolean connect(BluetoothGatt gatt) { @@ -843,7 +888,7 @@ boolean disableNotification(BluetoothGatt gatt) { return false; } - BluetoothGattService mHRP = gatt.getService(HRP_SERVICE); + BluetoothGattService mHRP = gatt.getService(HEART_RATE_SERVICE_UUID); if (mHRP == null) { log("disableNotification: HRP service not found!"); return false; @@ -851,7 +896,7 @@ boolean disableNotification(BluetoothGatt gatt) { BluetoothGattCharacteristic mHRMcharac = mHRP.getCharacteristic(HEART_RATE_MEASUREMENT_CHARAC); if (mHRMcharac == null) { - log("disableNotification: HEART RATE MEASUREMENT charateristic not found!"); + log("disableNotification: HEART RATE MEASUREMENT characteristic not found!"); return false; } @@ -943,16 +988,6 @@ public int getBatteryLevel() { return this.batteryLevel; } - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - private boolean checkBtGatt(BluetoothGatt gatt) { - return checkBtGatt(gatt, false); - } - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - private boolean checkBtGattOnlyLogError(BluetoothGatt gatt) { - return checkBtGatt(gatt, true); - } - private synchronized boolean checkBtGatt(BluetoothGatt gatt, boolean onlyLogError) { if (btGatt == null) { if (!onlyLogError) { diff --git a/hrdevice/src/org/runnerup/hr/BtHRBase.java b/hrdevice/src/org/runnerup/hr/BtHRBase.java index 5bbce1dbb..3ae3f3b32 100644 --- a/hrdevice/src/org/runnerup/hr/BtHRBase.java +++ b/hrdevice/src/org/runnerup/hr/BtHRBase.java @@ -22,14 +22,17 @@ import java.util.UUID; abstract class BtHRBase implements HRProvider { - static final UUID HRP_SERVICE = UUID.fromString("0000180D-0000-1000-8000-00805f9b34fb"); - static final UUID BATTERY_SERVICE = UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb"); + static final UUID HEART_RATE_SERVICE_UUID = + UUID.fromString("0000180D-0000-1000-8000-00805f9b34fb"); + static final UUID BATTERY_SERVICE_UUID = UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb"); static final UUID FIRMWARE_REVISON_UUID = UUID.fromString("00002a26-0000-1000-8000-00805f9b34fb"); - static final UUID DIS_UUID = UUID.fromString("0000180a-0000-1000-8000-00805f9b34fb"); + static final UUID DEVICE_INFORMATION_SERVICE_UUID = + UUID.fromString("0000180a-0000-1000-8000-00805f9b34fb"); static final UUID HEART_RATE_MEASUREMENT_CHARAC = UUID.fromString("00002A37-0000-1000-8000-00805f9b34fb"); static final UUID BATTERY_LEVEL_CHARAC = UUID.fromString("00002A19-0000-1000-8000-00805f9b34fb"); - static final UUID CCC = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); + static final UUID CLIENT_CHARACTERISTIC_CONFIGURATION = + UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); static final UUID HARDWARE_REVISON_UUID = UUID.fromString("00002a27-0000-1000-8000-00805f9b34fb"); HRProvider.HRClient hrClient; From 1a0c9bd572d28ba8d9c18ac11d0ca01ca82b82ec Mon Sep 17 00:00:00 2001 From: Andini <163494512+Threeguana@users.noreply.github.com> Date: Fri, 18 Sep 2026 03:56:11 +0700 Subject: [PATCH 05/10] fix: use Log.e instead of printStackTrace in AboutPreference (#1391) --- app/src/main/org/runnerup/widget/AboutPreference.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/org/runnerup/widget/AboutPreference.java b/app/src/main/org/runnerup/widget/AboutPreference.java index 2c2c4c7dc..b9b156e6d 100644 --- a/app/src/main/org/runnerup/widget/AboutPreference.java +++ b/app/src/main/org/runnerup/widget/AboutPreference.java @@ -24,6 +24,7 @@ import android.net.Uri; import android.os.Bundle; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import android.webkit.WebView; import androidx.annotation.NonNull; @@ -52,8 +53,7 @@ private void init(Context context) { + " v" + pInfo.versionName); } catch (NameNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.e(getClass().getName(), "Failed to get package info", e); } setNegativeButtonText(context.getString(org.runnerup.common.R.string.OK)); if (GoogleApiHelper.isGooglePlayServicesAvailable(context)) { @@ -92,7 +92,7 @@ public void onDialogClosed(boolean positiveResult) { Uri uri = Uri.parse("market://details?id=" + applicationId); this.requireContext().startActivity(new Intent(Intent.ACTION_VIEW, uri)); } catch (Exception ex) { - ex.printStackTrace(); + Log.e(AboutDialogFragment.class.getName(), "Failed to open Play Store", ex); } } } From 51d80b7aa86bce074a47d5b20ba9598e292da9d7 Mon Sep 17 00:00:00 2001 From: Andini <163494512+Threeguana@users.noreply.github.com> Date: Sat, 19 Sep 2026 17:09:50 +0700 Subject: [PATCH 06/10] refactor: migrate HistoryFragment to Activity Result API (#1390) Replace deprecated activity result handling with the Activity Result API while preserving existing behaviour. --- .../org/runnerup/view/HistoryFragment.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/app/src/main/org/runnerup/view/HistoryFragment.java b/app/src/main/org/runnerup/view/HistoryFragment.java index cf544215a..5bd91a950 100644 --- a/app/src/main/org/runnerup/view/HistoryFragment.java +++ b/app/src/main/org/runnerup/view/HistoryFragment.java @@ -31,6 +31,8 @@ import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatDelegate; @@ -61,6 +63,16 @@ public class HistoryFragment extends Fragment CursorAdapter cursorAdapter = null; View fab = null; + private final ActivityResultLauncher manualActivityLauncher = + registerForActivityResult( + new StartActivityForResult(), + result -> LoaderManager.getInstance(this).restartLoader(0, null, this)); + + private final ActivityResultLauncher detailActivityLauncher = + registerForActivityResult( + new StartActivityForResult(), + result -> LoaderManager.getInstance(this).restartLoader(0, null, this)); + public HistoryFragment() { super(R.layout.history); } @@ -76,8 +88,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat fab.setOnClickListener( v -> { Intent i = new Intent(context, ManualActivity.class); - // TODO: Use the Activity Result API - startActivityForResult(i, 0); + manualActivityLauncher.launch(i); }); mDB = DBHelper.getReadableDatabase(context); @@ -138,14 +149,7 @@ public void onItemClick(AdapterView arg0, View arg1, int position, long id) { Intent intent = new Intent(requireContext(), DetailActivity.class); intent.putExtra("ID", id); intent.putExtra("mode", "details"); - startActivityForResult(intent, 0); - } - - // TODO: Use Activity Result API - @Override - public void onActivityResult(int arg0, int arg1, Intent arg2) { - super.onActivityResult(arg0, arg1, arg2); - LoaderManager.getInstance(this).restartLoader(0, null, this); + detailActivityLauncher.launch(intent); } class HistoryListAdapter extends CursorAdapter { From 960762c5b4531b0450a700b00a8475436426cbe4 Mon Sep 17 00:00:00 2001 From: Gerhard Olsson <6248932+gerhardol@users.noreply.github.com> Date: Wed, 30 Sep 2026 23:22:06 +0200 Subject: [PATCH 07/10] fix: agp 9.4.1 (#1393) --- app/lint-baseline.xml | 2 +- build.gradle | 2 +- common/lint-baseline.xml | 2 +- hrdevice/lint-baseline.xml | 2 +- wear/build.gradle | 4 ++-- wear/lint-baseline.xml | 13 +------------ 6 files changed, 7 insertions(+), 18 deletions(-) diff --git a/app/lint-baseline.xml b/app/lint-baseline.xml index 163c54afe..914f595a2 100644 --- a/app/lint-baseline.xml +++ b/app/lint-baseline.xml @@ -1,5 +1,5 @@ - + - + diff --git a/hrdevice/lint-baseline.xml b/hrdevice/lint-baseline.xml index e8c77f8fe..61f072c5d 100644 --- a/hrdevice/lint-baseline.xml +++ b/hrdevice/lint-baseline.xml @@ -1,5 +1,5 @@ - + - - - - - + From d001387a2d256a5ade39df2365356d5b6709d991 Mon Sep 17 00:00:00 2001 From: Gerhard Olsson Date: Tue, 29 Sep 2026 23:10:41 +0200 Subject: [PATCH 08/10] fix: deprecated TabHost --- app/res/layout/detail.xml | 209 +++++++------- app/res/layout/start.xml | 269 +++++++++--------- .../org/runnerup/view/DetailActivity.java | 84 +++--- .../main/org/runnerup/view/StartFragment.java | 107 +++---- 4 files changed, 338 insertions(+), 331 deletions(-) diff --git a/app/res/layout/detail.xml b/app/res/layout/detail.xml index 826afc59c..baeed435f 100644 --- a/app/res/layout/detail.xml +++ b/app/res/layout/detail.xml @@ -88,140 +88,135 @@ - + android:layout_below="@id/appbarlayout" + android:orientation="vertical"> - + android:layout_height="wrap_content" /> - + - + android:layout_height="match_parent" + android:layout_margin="@dimen/activity_margin" + android:orientation="vertical"> - + android:layout_height="wrap_content" + android:layout_marginTop="1px" + android:prompt="@string/Type" + android:text="@string/Sport" /> - - - - - - - - - - - + + + + android:background="@android:color/black" + android:orientation="vertical" + android:visibility="gone"> - + android:layout_gravity="center" + android:text="@string/Heartrate_zones_distribution" + android:textAlignment="center" + android:textColor="@android:color/white" /> + - - - - - - + + + + + + + + android:layout_height="match_parent" /> + - - + + android:orientation="vertical" + android:weightSum="1" /> + - - - - - + + + + + + - - - - + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:minWidth="48dp" + android:minHeight="48dp" /> - + - + - + - - + + - + + + android:padding="16dp"> + android:gravity="bottom|start" + android:orientation="horizontal"> - - - - - - - - - - - - - - - - - - - + + + + + + - - + app:srcCompat="@drawable/ic_watch_white_24dp" /> - - - + + + + + - - -