diff --git a/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/FileService.kt b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/FileService.kt new file mode 100644 index 000000000..18d6b17bf --- /dev/null +++ b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/FileService.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 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 . + */ +package org.meshtastic.core.repository + +import okio.BufferedSink +import okio.BufferedSource +import org.meshtastic.core.common.util.MeshtasticUri + +/** + * Abstracts file system operations (like reading from or writing to URIs) + * so that ViewModels can remain platform-independent. + */ +interface FileService { + /** + * Opens a file or URI for writing and provides a [BufferedSink]. + * The sink is automatically closed after [block] execution. + * Returns true if successful, false otherwise. + */ + suspend fun write(uri: MeshtasticUri, block: suspend (BufferedSink) -> Unit): Boolean + + /** + * Opens a file or URI for reading and provides a [BufferedSource]. + * The source is automatically closed after [block] execution. + * Returns true if successful, false otherwise. + */ + suspend fun read(uri: MeshtasticUri, block: suspend (BufferedSource) -> Unit): Boolean +} diff --git a/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/LocationService.kt b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/LocationService.kt new file mode 100644 index 000000000..28be8b8a3 --- /dev/null +++ b/core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/LocationService.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 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 . + */ +package org.meshtastic.core.repository + +/** + * Abstracts high-level location requests (such as one-off current location) + * that may require platform-specific permission checks or hardware interactions. + */ +interface LocationService { + /** + * Requests the current location, if permissions and hardware allow. + * Returns null if unavailable or if permissions are not granted. + */ + suspend fun getCurrentLocation(): Location? +} diff --git a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/AndroidFileService.kt b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/AndroidFileService.kt new file mode 100644 index 000000000..e24d2f21e --- /dev/null +++ b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/AndroidFileService.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 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 . + */ +package org.meshtastic.core.service + +import android.app.Application +import co.touchlab.kermit.Logger +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import okio.BufferedSink +import okio.BufferedSource +import okio.buffer +import okio.sink +import okio.source +import org.koin.core.annotation.Single +import org.meshtastic.core.common.util.MeshtasticUri +import org.meshtastic.core.common.util.toAndroidUri +import org.meshtastic.core.repository.FileService + +@Single +class AndroidFileService(private val context: Application) : FileService { + override suspend fun write(uri: MeshtasticUri, block: suspend (BufferedSink) -> Unit): Boolean = + withContext(Dispatchers.IO) { + try { + context.contentResolver.openFileDescriptor(uri.toAndroidUri(), "wt")?.use { pfd -> + java.io.FileOutputStream(pfd.fileDescriptor).sink().buffer().use { sink -> + block(sink) + } + } + true + } catch (e: Exception) { + Logger.e(e) { "Failed to write to URI: $uri" } + false + } + } + + override suspend fun read(uri: MeshtasticUri, block: suspend (BufferedSource) -> Unit): Boolean = + withContext(Dispatchers.IO) { + try { + context.contentResolver.openInputStream(uri.toAndroidUri())?.use { inputStream -> + inputStream.source().buffer().use { source -> + block(source) + } + } + true + } catch (e: Exception) { + Logger.e(e) { "Failed to read from URI: $uri" } + false + } + } +} diff --git a/core/service/src/androidMain/kotlin/org/meshtastic/core/service/AndroidLocationService.kt b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/AndroidLocationService.kt new file mode 100644 index 000000000..70ed92287 --- /dev/null +++ b/core/service/src/androidMain/kotlin/org/meshtastic/core/service/AndroidLocationService.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 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 . + */ +package org.meshtastic.core.service + +import android.Manifest +import android.app.Application +import android.content.pm.PackageManager +import androidx.core.content.ContextCompat +import kotlinx.coroutines.flow.firstOrNull +import org.koin.core.annotation.Single +import org.meshtastic.core.repository.Location +import org.meshtastic.core.repository.LocationRepository +import org.meshtastic.core.repository.LocationService + +@Single +class AndroidLocationService( + private val context: Application, + private val locationRepository: LocationRepository +) : LocationService { + + override suspend fun getCurrentLocation(): Location? { + val hasPermission = ContextCompat.checkSelfPermission( + context, + Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + + if (!hasPermission) { + return null + } + + return locationRepository.getLocations().firstOrNull() + } +} diff --git a/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/JvmFileService.kt b/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/JvmFileService.kt new file mode 100644 index 000000000..a611330bb --- /dev/null +++ b/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/JvmFileService.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) 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 . + */ +package org.meshtastic.core.service + +import co.touchlab.kermit.Logger +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import okio.BufferedSink +import okio.BufferedSource +import okio.buffer +import okio.sink +import okio.source +import org.koin.core.annotation.Single +import org.meshtastic.core.common.util.MeshtasticUri +import org.meshtastic.core.repository.FileService +import java.io.File + +@Single +class JvmFileService : FileService { + override suspend fun write(uri: MeshtasticUri, block: suspend (BufferedSink) -> Unit): Boolean = + withContext(Dispatchers.IO) { + try { + // Treat uriString as a local file path + val file = File(uri.uriString) + file.parentFile?.mkdirs() + file.sink().buffer().use { sink -> + block(sink) + } + true + } catch (e: Exception) { + Logger.e(e) { "Failed to write to URI: $uri" } + false + } + } + + override suspend fun read(uri: MeshtasticUri, block: suspend (BufferedSource) -> Unit): Boolean = + withContext(Dispatchers.IO) { + try { + val file = File(uri.uriString) + file.source().buffer().use { source -> + block(source) + } + true + } catch (e: Exception) { + Logger.e(e) { "Failed to read from URI: $uri" } + false + } + } +} diff --git a/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/JvmLocationService.kt b/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/JvmLocationService.kt new file mode 100644 index 000000000..7e0124dab --- /dev/null +++ b/core/service/src/jvmMain/kotlin/org/meshtastic/core/service/JvmLocationService.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 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 . + */ +package org.meshtastic.core.service + +import org.koin.core.annotation.Single +import org.meshtastic.core.repository.Location +import org.meshtastic.core.repository.LocationService + +@Single +class JvmLocationService : LocationService { + override suspend fun getCurrentLocation(): Location? { + // Location services on JVM/Desktop are currently stubbed + return null + } +}