diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/MainActivity.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/MainActivity.kt index 75ee87ac..4724464b 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/MainActivity.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/MainActivity.kt @@ -191,7 +191,6 @@ class MainActivity : AppCompatActivity() { requestingAppMode = Pair(sideEffect.requestingMode, sideEffect.config) vpnActivity.launch(VpnService.prepare(this@MainActivity)) } - is GlobalSideEffect.ShareFile -> context.launchShareFile(sideEffect.file) is GlobalSideEffect.Snackbar -> scope.launch { snackbar.showSnackbar(sideEffect.message.asString(context)) diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/domain/sideeffect/GlobalSideEffect.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/domain/sideeffect/GlobalSideEffect.kt index 143f6ea3..a4d4719f 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/domain/sideeffect/GlobalSideEffect.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/domain/sideeffect/GlobalSideEffect.kt @@ -14,8 +14,6 @@ sealed class GlobalSideEffect { data object PopBackStack : GlobalSideEffect() - data class ShareFile(val file: File) : GlobalSideEffect() - data class LaunchUrl(val url: String) : GlobalSideEffect() data object ConfigChanged : GlobalSideEffect() diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/logs/LogsScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/logs/LogsScreen.kt index 7925b997..d43a6840 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/logs/LogsScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/logs/LogsScreen.kt @@ -69,7 +69,16 @@ fun LogsScreen(viewModel: LoggerViewModel = hiltViewModel()) { } if (showLogsSheet) { - LogsBottomSheet({ viewModel.exportLogs() }, { viewModel.deleteLogs() }) { + LogsBottomSheet( + { uri -> + viewModel.exportLogs(uri) + showLogsSheet = false + }, + { + viewModel.deleteLogs() + showLogsSheet = false + }, + ) { showLogsSheet = false } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/logs/components/LogsBottomSheet.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/logs/components/LogsBottomSheet.kt index 0b9af37e..1e5eae1c 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/logs/components/LogsBottomSheet.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/logs/components/LogsBottomSheet.kt @@ -1,24 +1,53 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.settings.logs.components +import android.net.Uri import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.FolderZip import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import com.zaneschepke.wireguardautotunnel.BuildConfig import com.zaneschepke.wireguardautotunnel.R +import com.zaneschepke.wireguardautotunnel.ui.common.functions.rememberFileExportLauncherForResult import com.zaneschepke.wireguardautotunnel.ui.common.sheet.CustomBottomSheet import com.zaneschepke.wireguardautotunnel.ui.common.sheet.SheetOption +import com.zaneschepke.wireguardautotunnel.util.Constants +import com.zaneschepke.wireguardautotunnel.util.FileUtils +import com.zaneschepke.wireguardautotunnel.util.extensions.hasSAFSupport @OptIn(ExperimentalMaterial3Api::class) @Composable -fun LogsBottomSheet(onExport: () -> Unit, onDelete: () -> Unit, onDismiss: () -> Unit) { +fun LogsBottomSheet(onExport: (file: Uri?) -> Unit, onDelete: () -> Unit, onDismiss: () -> Unit) { + val context = LocalContext.current + + val selectedTunnelsExportLauncher = + rememberFileExportLauncherForResult( + mimeType = FileUtils.ZIP_FILE_MIME_TYPE, + onResult = { file -> + if (file != null) { + onExport(file) + } else onDismiss() + }, + ) + + fun handleFileExport() { + if (context.hasSAFSupport(FileUtils.ZIP_FILE_MIME_TYPE)) { + selectedTunnelsExportLauncher.launch( + "${Constants.BASE_LOG_FILE_NAME}_${BuildConfig.VERSION_NAME}_${BuildConfig.FLAVOR}.zip" + ) + } else { + onExport(null) + } + } + CustomBottomSheet( listOf( SheetOption( Icons.Outlined.FolderZip, stringResource(R.string.export_logs), - onClick = onExport, + onClick = { handleFileExport() }, ), SheetOption( Icons.Outlined.Delete, diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunnels/components/ExportTunnelsBottomSheet.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunnels/components/ExportTunnelsBottomSheet.kt index 337b203d..059d34c7 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunnels/components/ExportTunnelsBottomSheet.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/tunnels/components/ExportTunnelsBottomSheet.kt @@ -11,9 +11,9 @@ import com.zaneschepke.wireguardautotunnel.domain.enums.ConfigType import com.zaneschepke.wireguardautotunnel.ui.common.functions.rememberFileExportLauncherForResult import com.zaneschepke.wireguardautotunnel.ui.common.sheet.CustomBottomSheet import com.zaneschepke.wireguardautotunnel.ui.common.sheet.SheetOption -import com.zaneschepke.wireguardautotunnel.util.Constants import com.zaneschepke.wireguardautotunnel.util.FileUtils import com.zaneschepke.wireguardautotunnel.util.extensions.hasSAFSupport +import java.time.Instant @Composable fun ExportTunnelsBottomSheet( @@ -37,7 +37,12 @@ fun ExportTunnelsBottomSheet( fun handleFileExport() { if (context.hasSAFSupport(FileUtils.ZIP_FILE_MIME_TYPE)) { - selectedTunnelsExportLauncher.launch(Constants.DEFAULT_EXPORT_FILE_NAME) + val fileName = + when (exportConfigType) { + ConfigType.AM -> "am_export_${Instant.now().epochSecond}.zip" + ConfigType.WG -> "wg_export_${Instant.now().epochSecond}.zip" + } + selectedTunnelsExportLauncher.launch(fileName) } else { onExport(exportConfigType, null) } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/Constants.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/Constants.kt index 1b5384e8..65bde77a 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/Constants.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/Constants.kt @@ -9,8 +9,6 @@ object Constants { const val SYSTEM_EXEMPT_SERVICE_TYPE_ID = 1 shl 10 const val SPECIAL_USE_SERVICE_TYPE_ID = 1 shl 30 - const val DEFAULT_EXPORT_FILE_NAME = "wgtunnel-export.zip" - const val QR_CODE_NAME_PROPERTY = "# Name =" const val FDROID_FLAVOR = "fdroid" diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/viewmodel/LoggerViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/viewmodel/LoggerViewModel.kt index 408790eb..6d60d584 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/viewmodel/LoggerViewModel.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/viewmodel/LoggerViewModel.kt @@ -1,7 +1,9 @@ package com.zaneschepke.wireguardautotunnel.viewmodel +import android.net.Uri import androidx.lifecycle.ViewModel import com.zaneschepke.logcatter.LogReader +import com.zaneschepke.wireguardautotunnel.BuildConfig import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.domain.repository.AppStateRepository import com.zaneschepke.wireguardautotunnel.domain.repository.GlobalEffectRepository @@ -11,7 +13,6 @@ import com.zaneschepke.wireguardautotunnel.util.Constants import com.zaneschepke.wireguardautotunnel.util.FileUtils import com.zaneschepke.wireguardautotunnel.util.StringValue import dagger.hilt.android.lifecycle.HiltViewModel -import java.time.Instant import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay @@ -71,18 +72,30 @@ constructor( globalEffectRepository.post(globalSideEffect) } - fun exportLogs() = intent { + fun exportLogs(uri: Uri?) = intent { val result = fileUtils.createNewShareFile( - "${Constants.BASE_LOG_FILE_NAME}-${Instant.now().epochSecond}.zip" + "${Constants.BASE_LOG_FILE_NAME}_${BuildConfig.VERSION_NAME}_${BuildConfig.FLAVOR}.zip" ) - result.onSuccess { file -> postSideEffect(GlobalSideEffect.ShareFile(file)) } - result.onFailure { error -> - val message = - error.message?.let { StringValue.DynamicString(it) } - ?: StringValue.StringResource(R.string.unknown_error) - postSideEffect(GlobalSideEffect.Toast(message)) + val onFailure = { action: Throwable -> + Timber.e(action) + intent { + postSideEffect( + GlobalSideEffect.Toast( + StringValue.StringResource( + R.string.export_failed, + ": ${action.localizedMessage}", + ) + ) + ) + } + Unit } + result.onSuccess { file -> + logReader.zipLogFiles(file.absolutePath) + fileUtils.exportFile(file, uri, FileUtils.ZIP_FILE_MIME_TYPE).onFailure(onFailure) + } + result.onFailure(onFailure) } fun deleteLogs() = intent { diff --git a/logcatter/src/main/java/com/zaneschepke/logcatter/LogReader.kt b/logcatter/src/main/java/com/zaneschepke/logcatter/LogReader.kt index 22a2252e..e6bd7c54 100644 --- a/logcatter/src/main/java/com/zaneschepke/logcatter/LogReader.kt +++ b/logcatter/src/main/java/com/zaneschepke/logcatter/LogReader.kt @@ -8,7 +8,7 @@ interface LogReader { fun stop() - fun zipLogFiles(path: String) + suspend fun zipLogFiles(path: String) suspend fun deleteAndClearLogs() diff --git a/logcatter/src/main/java/com/zaneschepke/logcatter/LogcatManager.kt b/logcatter/src/main/java/com/zaneschepke/logcatter/LogcatManager.kt index 3626664a..a0b39e9b 100644 --- a/logcatter/src/main/java/com/zaneschepke/logcatter/LogcatManager.kt +++ b/logcatter/src/main/java/com/zaneschepke/logcatter/LogcatManager.kt @@ -1,5 +1,6 @@ package com.zaneschepke.logcatter +import android.R.attr.path import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import com.zaneschepke.logcatter.model.LogMessage @@ -61,15 +62,13 @@ class LogcatManager(pid: Int, logDir: String, maxFileSize: Long, maxFolderSize: isStarted = false } - override fun zipLogFiles(path: String) { - logScope.launch { - val wasStarted = isStarted - stop() - fileManager.zipLogs(path) - if (wasStarted) { - logcatReader.clearLogs() - start() - } + override suspend fun zipLogFiles(path: String) { + val wasStarted = isStarted + stop() + fileManager.zipLogs(path) + if (wasStarted) { + logcatReader.clearLogs() + start() } }