diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/BaseActivitySummaryProvider.kt b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/BaseActivitySummaryProvider.kt
new file mode 100644
index 0000000000..c0c3c4f686
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/BaseActivitySummaryProvider.kt
@@ -0,0 +1,79 @@
+/* Copyright (C) 2026 Thomas Kuehne
+
+ This file is part of Gadgetbridge.
+
+ Gadgetbridge is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Gadgetbridge is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see . */
+package nodomain.freeyourgadget.gadgetbridge.devices
+
+import android.content.Context
+import android.widget.Toast
+import nodomain.freeyourgadget.gadgetbridge.GBApplication
+import nodomain.freeyourgadget.gadgetbridge.R
+import nodomain.freeyourgadget.gadgetbridge.database.DBHelper
+import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary
+import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice
+import nodomain.freeyourgadget.gadgetbridge.util.GB
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+class BaseActivitySummaryProvider(val device: GBDevice, val session: DaoSession) :
+ PersistanceProvider {
+ override fun persistSamples(
+ samples: List, context: Context?
+ ): Boolean {
+ if (samples.isEmpty()) {
+ return true
+ }
+
+ LOG.debug(
+ "Will persist {} {}", samples.size, javaClass.getSimpleName().replace("Provider", "")
+ )
+
+ try {
+ val session = this.session
+
+ val gbDevice = this.device
+ val device = DBHelper.findDevice(gbDevice, session)
+ if (device == null) {
+ LOG.warn("Device not found in database for '{}'", gbDevice.getAliasOrName())
+ return false
+ }
+ val deviceId = device.id
+
+ val user = DBHelper.getUser(session)
+ val userId = user.id
+
+ for (sample in samples) {
+ sample.deviceId = deviceId
+ sample.userId = userId
+ }
+
+ val dao = session.baseActivitySummaryDao
+ dao.insertOrReplaceInTx(samples)
+ } catch (e: Exception) {
+ LOG.error("Error saving samples", e)
+ val ctx = context ?: GBApplication.getContext()
+ val message: String =
+ ctx.getString(R.string.persisting_samples_failed, e.localizedMessage)
+ GB.toast(ctx, message, Toast.LENGTH_LONG, GB.ERROR, e)
+ return false
+ }
+ return true
+ }
+
+ companion object {
+ internal val LOG: Logger by lazy { LoggerFactory.getLogger(BaseActivitySummaryProvider::class.java) }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/PersistanceProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/PersistanceProvider.java
new file mode 100644
index 0000000000..e591811a3d
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/PersistanceProvider.java
@@ -0,0 +1,34 @@
+/* Copyright (C) 2026 Thomas Kuehne
+
+ This file is part of Gadgetbridge.
+
+ Gadgetbridge is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Gadgetbridge is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see . */
+package nodomain.freeyourgadget.gadgetbridge.devices;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.List;
+
+public interface PersistanceProvider {
+ /// Set the relevant metadata for all samples like device ID, user ID, etc. and then
+ /// stores them all in the database. Existing samples with identical primary key
+ /// already stored in the database will be replaced.
+ ///
+ /// @param context optional context for displaying error messages
+ /// @return {@code false} if at least one sample couldn't be persisted.
+ boolean persistSamples(@NonNull List samples, @Nullable Context context);
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3cbf10877f..c0baae93c3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1436,6 +1436,7 @@
Empty Database
Export and Import
Warning! By pushing this button you will wipe your database and start from scratch.
+ Error saving samples: %1$s
Cancel
Delete
Keep Files