diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e3c017b..cd898e2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -133,8 +133,6 @@ android { packaging { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" } } } -val generalImplementation by configurations - dependencies { implementation(project(":logcatter")) diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt index cdfbe96..359aa0f 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt @@ -60,6 +60,7 @@ import com.zaneschepke.wireguardautotunnel.ui.screens.support.SupportScreen import com.zaneschepke.wireguardautotunnel.ui.screens.support.logs.LogsScreen import com.zaneschepke.wireguardautotunnel.ui.theme.WireguardAutoTunnelTheme import com.zaneschepke.wireguardautotunnel.util.Constants +import com.zaneschepke.wireguardautotunnel.util.extensions.requestAutoTunnelTileServiceUpdate import com.zaneschepke.wireguardautotunnel.util.extensions.requestTunnelTileServiceStateUpdate import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -103,6 +104,12 @@ class MainActivity : AppCompatActivity() { context.requestTunnelTileServiceStateUpdate() } + with(appUiState.settings) { + LaunchedEffect(isAutoTunnelPaused, isAutoTunnelEnabled) { + this@MainActivity.requestAutoTunnelTileServiceUpdate() + } + } + CompositionLocalProvider(LocalNavController provides navController) { SnackbarControllerProvider { host -> WireguardAutoTunnelTheme { diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/functions/Functions.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/functions/Functions.kt index 726f8bf..9332655 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/functions/Functions.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/functions/Functions.kt @@ -10,6 +10,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.runtime.Composable import com.zaneschepke.wireguardautotunnel.util.Constants +import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv @Composable fun rememberFileImportLauncherForResult(onNoFileExplorer: () -> Unit, onData: (data: Uri) -> Unit): ManagedActivityResultLauncher { @@ -17,7 +18,11 @@ fun rememberFileImportLauncherForResult(onNoFileExplorer: () -> Unit, onData: (d object : ActivityResultContracts.GetContent() { override fun createIntent(context: Context, input: String): Intent { val intent = super.createIntent(context, input).apply { - type = Constants.ALLOWED_FILE_TYPES + type = if (context.isRunningOnTv()) { + Constants.ALLOWED_TV_FILE_TYPES + } else { + Constants.ALL_FILE_TYPES + } } /* AndroidTV now comes with stubs that do nothing but display a Toast less helpful than diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt index 3d4ee3d..e5ae7e1 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt @@ -31,7 +31,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext @@ -181,7 +180,7 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState, TunnelImportSheet( showBottomSheet, onDismiss = { showBottomSheet = false }, - onFileClick = { tunnelFileImportResultLauncher.launch(Constants.ALLOWED_FILE_TYPES) }, + onFileClick = { tunnelFileImportResultLauncher.launch(Constants.ALLOWED_TV_FILE_TYPES) }, onQrClick = { launchQrScanner() }, onManualImportClick = { navController.navigate( diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelStatisticsRow.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelStatisticsRow.kt index 18ecba3..c07fdc4 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelStatisticsRow.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/components/TunnelStatisticsRow.kt @@ -37,8 +37,11 @@ fun TunnelStatisticsRow(statistics: TunnelStatistics?, tunnelConfig: TunnelConfi val peerTxMB = NumberUtils.bytesToMB(peerTx).toThreeDecimalPlaceString() val peerRxMB = NumberUtils.bytesToMB(peerRx).toThreeDecimalPlaceString() val handshake = statistics?.peerStats(it.publicKey)?.latestHandshakeEpochMillis?.let { - if(it == 0L) stringResource(R.string.never) else - "${NumberUtils.getSecondsBetweenTimestampAndNow(it)} ${stringResource(R.string.sec)}" + if (it == 0L) { + stringResource(R.string.never) + } else { + "${NumberUtils.getSecondsBetweenTimestampAndNow(it)} ${stringResource(R.string.sec)}" + } } ?: stringResource(R.string.never) Column( verticalArrangement = Arrangement.spacedBy(10.dp), diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/pinlock/PinLockScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/pinlock/PinLockScreen.kt index c3942d8..f1052d7 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/pinlock/PinLockScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/pinlock/PinLockScreen.kt @@ -22,7 +22,7 @@ fun PinLockScreen(appViewModel: AppViewModel) { PinLock( title = { pinExists -> Text( - color = MaterialTheme.colorScheme.onSecondary, + color = MaterialTheme.colorScheme.onSurface, text = if (pinExists) { stringResource(id = R.string.enter_pin) @@ -33,7 +33,8 @@ fun PinLockScreen(appViewModel: AppViewModel) { }, ) }, - color = MaterialTheme.colorScheme.secondary, + backgroundColor = MaterialTheme.colorScheme.surface, + textColor = MaterialTheme.colorScheme.onSurface, onPinCorrect = { // pin is correct, navigate or hide pin lock if (context.isRunningOnTv()) { 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 640dcd8..03375d1 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/Constants.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/Constants.kt @@ -13,11 +13,11 @@ object Constants { const val URI_CONTENT_SCHEME = "content" const val TEXT_MIME_TYPE = "text/plain" const val ZIP_FILE_MIME_TYPE = "application/zip" - const val ALLOWED_FILE_TYPES = "${TEXT_MIME_TYPE}|${ZIP_FILE_MIME_TYPE}" + const val ALLOWED_TV_FILE_TYPES = "${TEXT_MIME_TYPE}|${ZIP_FILE_MIME_TYPE}" + const val ALL_FILE_TYPES = "*/*" const val GOOGLE_TV_EXPLORER_STUB = "com.google.android.tv.frameworkpackagestubs" const val ANDROID_TV_EXPLORER_STUB = "com.android.tv.frameworkpackagestubs" const val VPN_SETTINGS_PACKAGE = "android.net.vpn.SETTINGS" - const val EMAIL_MIME_TYPE = "plain/text" const val SYSTEM_EXEMPT_SERVICE_TYPE_ID = 1024 const val SUBSCRIPTION_TIMEOUT = 5_000L diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/FileUtils.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/FileUtils.kt index 5a35a05..3bf37ef 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/FileUtils.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/FileUtils.kt @@ -12,8 +12,6 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext import timber.log.Timber import java.io.File -import java.io.FileInputStream -import java.io.FileOutputStream import java.io.OutputStream import java.time.Instant import java.util.zip.ZipEntry @@ -23,23 +21,6 @@ class FileUtils( private val context: Context, private val ioDispatcher: CoroutineDispatcher, ) { - suspend fun readBytesFromFile(file: File): ByteArray { - return withContext(ioDispatcher) { - FileInputStream(file).use { - it.readBytes() - } - } - } - - suspend fun readTextFromFileName(fileName: String): String { - return withContext(ioDispatcher) { - context.assets.open(fileName).use { stream -> - stream.bufferedReader(Charsets.UTF_8).use { - it.readText() - } - } - } - } fun createWgFiles(tunnels: TunnelConfigs): List { return tunnels.map { config -> @@ -61,43 +42,6 @@ class FileUtils( } } - suspend fun saveByteArrayToDownloads(content: ByteArray, fileName: String): Result { - return withContext(ioDispatcher) { - try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - val contentValues = - ContentValues().apply { - put(MediaColumns.DISPLAY_NAME, fileName) - put(MediaColumns.MIME_TYPE, Constants.TEXT_MIME_TYPE) - put(MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) - } - val resolver = context.contentResolver - val uri = - resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues) - if (uri != null) { - resolver.openOutputStream(uri).use { output -> - output?.write(content) - } - } - } else { - val target = - File( - Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_DOWNLOADS, - ), - fileName, - ) - FileOutputStream(target).use { output -> - output.write(content) - } - } - Result.success(Unit) - } catch (e: Exception) { - Result.failure(e) - } - } - } - suspend fun saveFilesToZip(files: List): Result { return withContext(ioDispatcher) { try { @@ -124,7 +68,7 @@ class FileUtils( } // TODO issue with android 9 - private fun createDownloadsFileOutputStream(fileName: String, mimeType: String = Constants.ALLOWED_FILE_TYPES): OutputStream? { + private fun createDownloadsFileOutputStream(fileName: String, mimeType: String = Constants.ALL_FILE_TYPES): OutputStream? { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val resolver = context.contentResolver val contentValues = diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/ContextExtensions.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/ContextExtensions.kt index 7089b8c..fb73efb 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/ContextExtensions.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/extensions/ContextExtensions.kt @@ -44,21 +44,21 @@ fun Context.showToast(resId: Int) { ).show() } -fun Context.launchSupportEmail(): Result { - return runCatching { - val intent = - Intent(Intent.ACTION_SENDTO).apply { - type = Constants.EMAIL_MIME_TYPE - putExtra(Intent.EXTRA_EMAIL, arrayOf(getString(R.string.my_email))) - putExtra(Intent.EXTRA_SUBJECT, getString(R.string.email_subject)) - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } +fun Context.launchSupportEmail() { + val intent = + Intent(Intent.ACTION_SENDTO).apply { + data = Uri.parse("mailto:") + putExtra(Intent.EXTRA_EMAIL, arrayOf(getString(R.string.my_email))) + putExtra(Intent.EXTRA_SUBJECT, getString(R.string.email_subject)) + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + if (intent.resolveActivity(packageManager) != null) { startActivity( Intent.createChooser(intent, getString(R.string.email_chooser)).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }, ) - }.onFailure { + } else { showToast(R.string.no_email_detected) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bd890fc..1d0c734 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ kotlinx-serialization-json = "1.7.3" lifecycle-runtime-compose = "2.8.6" material3 = "1.3.0" navigationCompose = "2.8.2" -pinLockCompose = "1.0.3" +pinLockCompose = "1.0.4" roomVersion = "2.6.1" timber = "5.0.1" tunnel = "1.2.1"