feat(widget): Add Local Stats glance widget (#4642)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich 2026-02-25 13:39:00 -06:00 committed by GitHub
parent 692ad78c80
commit 9970d31520
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 1256 additions and 24 deletions

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.core.datastore
import androidx.datastore.core.DataStore
import co.touchlab.kermit.Logger
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import okio.IOException
import org.meshtastic.proto.LocalStats
import javax.inject.Inject
import javax.inject.Singleton
/** Class that handles saving and retrieving [LocalStats] data. */
@Singleton
class LocalStatsDataSource @Inject constructor(private val localStatsStore: DataStore<LocalStats>) {
val localStatsFlow: Flow<LocalStats> =
localStatsStore.data.catch { exception ->
if (exception is IOException) {
Logger.e { "Error reading LocalStats: ${exception.message}" }
emit(LocalStats())
} else {
throw exception
}
}
suspend fun setLocalStats(stats: LocalStats) {
localStatsStore.updateData { stats }
}
suspend fun clearLocalStats() {
localStatsStore.updateData { LocalStats() }
}
}

View file

@ -43,10 +43,12 @@ import org.meshtastic.core.datastore.KEY_SHOW_IGNORED
import org.meshtastic.core.datastore.KEY_THEME
import org.meshtastic.core.datastore.serializer.ChannelSetSerializer
import org.meshtastic.core.datastore.serializer.LocalConfigSerializer
import org.meshtastic.core.datastore.serializer.LocalStatsSerializer
import org.meshtastic.core.datastore.serializer.ModuleConfigSerializer
import org.meshtastic.proto.ChannelSet
import org.meshtastic.proto.LocalConfig
import org.meshtastic.proto.LocalModuleConfig
import org.meshtastic.proto.LocalStats
import javax.inject.Qualifier
import javax.inject.Singleton
@ -129,4 +131,16 @@ object DataStoreModule {
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { ChannelSet() }),
scope = scope,
)
@Singleton
@Provides
fun provideLocalStatsDataStore(
@ApplicationContext appContext: Context,
@DataStoreScope scope: CoroutineScope,
): DataStore<LocalStats> = DataStoreFactory.create(
serializer = LocalStatsSerializer,
produceFile = { appContext.dataStoreFile("local_stats.pb") },
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { LocalStats() }),
scope = scope,
)
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.core.datastore.serializer
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.Serializer
import okio.IOException
import org.meshtastic.proto.LocalStats
import java.io.InputStream
import java.io.OutputStream
/** Serializer for the [LocalStats] object defined in telemetry.proto. */
@Suppress("BlockingMethodInNonBlockingContext")
object LocalStatsSerializer : Serializer<LocalStats> {
override val defaultValue: LocalStats = LocalStats()
override suspend fun readFrom(input: InputStream): LocalStats {
try {
return LocalStats.ADAPTER.decode(input)
} catch (exception: IOException) {
throw CorruptionException("Cannot read proto.", exception)
}
}
override suspend fun writeTo(t: LocalStats, output: OutputStream) = LocalStats.ADAPTER.encode(output, t)
}