diff options
7 files changed, 125 insertions, 17 deletions
diff --git a/app/src/main/java/org/pacien/tincapp/activities/start/ErrorNotificationFragment.kt b/app/src/main/java/org/pacien/tincapp/activities/start/ErrorNotificationFragment.kt index 08dab8d..f629623 100644 --- a/app/src/main/java/org/pacien/tincapp/activities/start/ErrorNotificationFragment.kt +++ b/app/src/main/java/org/pacien/tincapp/activities/start/ErrorNotificationFragment.kt | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Tinc Mesh VPN: Android client and user interface | 2 | * Tinc Mesh VPN: Android client and user interface |
3 | * Copyright (C) 2017-2023 Euxane P. TRAN-GIRARD | 3 | * Copyright (C) 2017-2024 Euxane P. TRAN-GIRARD |
4 | * | 4 | * |
5 | * This program is free software: you can redistribute it and/or modify | 5 | * This program is free software: you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | 6 | * it under the terms of the GNU General Public License as published by |
@@ -27,6 +27,8 @@ import org.pacien.tincapp.activities.BaseFragment | |||
27 | import org.pacien.tincapp.context.App | 27 | import org.pacien.tincapp.context.App |
28 | import org.pacien.tincapp.context.AppNotificationManager | 28 | import org.pacien.tincapp.context.AppNotificationManager |
29 | import org.pacien.tincapp.databinding.StartErrorNotificationBinding | 29 | import org.pacien.tincapp.databinding.StartErrorNotificationBinding |
30 | import org.pacien.tincapp.storageprovider.BrowseFilesIntents | ||
31 | import org.pacien.tincapp.storageprovider.FilesDocumentsProvider | ||
30 | 32 | ||
31 | /** | 33 | /** |
32 | * @author euxane | 34 | * @author euxane |
@@ -56,5 +58,7 @@ class ErrorNotificationFragment : BaseFragment() { | |||
56 | val maybeError = notificationManager.getError() | 58 | val maybeError = notificationManager.getError() |
57 | viewBinding.errorNotification = maybeError | 59 | viewBinding.errorNotification = maybeError |
58 | viewBinding.openManualAction = { App.openURL(maybeError?.manualLink!!) } | 60 | viewBinding.openManualAction = { App.openURL(maybeError?.manualLink!!) } |
61 | viewBinding.openConfigDirAction = { openDocumentTree(BrowseFilesIntents.networkConfigDirDocId(maybeError?.configDir)) } | ||
62 | viewBinding.openLogDirAction = { openDocumentTree(FilesDocumentsProvider.VIRTUAL_ROOT_LOGS) } | ||
59 | } | 63 | } |
60 | } | 64 | } |
diff --git a/app/src/main/java/org/pacien/tincapp/context/App.kt b/app/src/main/java/org/pacien/tincapp/context/App.kt index 6145c65..e6402d2 100644 --- a/app/src/main/java/org/pacien/tincapp/context/App.kt +++ b/app/src/main/java/org/pacien/tincapp/context/App.kt | |||
@@ -70,8 +70,20 @@ class App : Application() { | |||
70 | .packageManager | 70 | .packageManager |
71 | .getApplicationInfo(BuildConfig.APPLICATION_ID, 0) | 71 | .getApplicationInfo(BuildConfig.APPLICATION_ID, 0) |
72 | 72 | ||
73 | fun alert(@StringRes title: Int, msg: String, manualLink: String? = null) = | 73 | fun alert( |
74 | notificationManager.notifyError(appContext!!.getString(title), msg, manualLink) | 74 | @StringRes title: Int, |
75 | msg: String, | ||
76 | manualLink: String? = null, | ||
77 | configDir: String? = null, | ||
78 | proposeLogs: Boolean = false, | ||
79 | ) = | ||
80 | notificationManager.notifyError( | ||
81 | appContext!!.getString(title), | ||
82 | msg, | ||
83 | manualLink, | ||
84 | configDir, | ||
85 | proposeLogs, | ||
86 | ) | ||
75 | 87 | ||
76 | fun openURL(url: String) { | 88 | fun openURL(url: String) { |
77 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) | 89 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) |
diff --git a/app/src/main/java/org/pacien/tincapp/context/AppNotificationManager.kt b/app/src/main/java/org/pacien/tincapp/context/AppNotificationManager.kt index c7a5ade..f4332e9 100644 --- a/app/src/main/java/org/pacien/tincapp/context/AppNotificationManager.kt +++ b/app/src/main/java/org/pacien/tincapp/context/AppNotificationManager.kt | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Tinc Mesh VPN: Android client and user interface | 2 | * Tinc Mesh VPN: Android client and user interface |
3 | * Copyright (C) 2017-2023 Euxane P. TRAN-GIRARD | 3 | * Copyright (C) 2017-2024 Euxane P. TRAN-GIRARD |
4 | * | 4 | * |
5 | * This program is free software: you can redistribute it and/or modify | 5 | * This program is free software: you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | 6 | * it under the terms of the GNU General Public License as published by |
@@ -28,7 +28,9 @@ class AppNotificationManager(private val context: Context) { | |||
28 | data class ErrorNotification( | 28 | data class ErrorNotification( |
29 | val title: String, | 29 | val title: String, |
30 | val message: String, | 30 | val message: String, |
31 | val manualLink: String? | 31 | val manualLink: String?, |
32 | val configDir: String?, | ||
33 | val proposeLogs: Boolean, | ||
32 | ) | 34 | ) |
33 | 35 | ||
34 | companion object { | 36 | companion object { |
@@ -36,6 +38,8 @@ class AppNotificationManager(private val context: Context) { | |||
36 | private const val STORE_KEY_TITLE = "title" | 38 | private const val STORE_KEY_TITLE = "title" |
37 | private const val STORE_KEY_MESSAGE = "message" | 39 | private const val STORE_KEY_MESSAGE = "message" |
38 | private const val STORE_KEY_MANUAL_LINK = "manual_link" | 40 | private const val STORE_KEY_MANUAL_LINK = "manual_link" |
41 | private const val STORE_CONFIG_DIR = "config_dir" | ||
42 | private const val STORE_PROPOSE_LOGS = "propose_logs_dir" | ||
39 | } | 43 | } |
40 | 44 | ||
41 | private val store by lazy { context.getSharedPreferences(STORE_NAME, Context.MODE_PRIVATE)!! } | 45 | private val store by lazy { context.getSharedPreferences(STORE_NAME, Context.MODE_PRIVATE)!! } |
@@ -46,16 +50,26 @@ class AppNotificationManager(private val context: Context) { | |||
46 | return ErrorNotification( | 50 | return ErrorNotification( |
47 | store.getString(STORE_KEY_TITLE, null)!!, | 51 | store.getString(STORE_KEY_TITLE, null)!!, |
48 | store.getString(STORE_KEY_MESSAGE, null)!!, | 52 | store.getString(STORE_KEY_MESSAGE, null)!!, |
49 | store.getString(STORE_KEY_MANUAL_LINK, null) | 53 | store.getString(STORE_KEY_MANUAL_LINK, null), |
54 | store.getString(STORE_CONFIG_DIR, null), | ||
55 | store.getBoolean(STORE_PROPOSE_LOGS, false), | ||
50 | ) | 56 | ) |
51 | } | 57 | } |
52 | 58 | ||
53 | fun notifyError(title: String, message: String, manualLink: String? = null) { | 59 | fun notifyError( |
60 | title: String, | ||
61 | message: String, | ||
62 | manualLink: String? = null, | ||
63 | configDir: String? = null, | ||
64 | proposeLogs: Boolean = false, | ||
65 | ) { | ||
54 | store | 66 | store |
55 | .edit() | 67 | .edit() |
56 | .putString(STORE_KEY_TITLE, title) | 68 | .putString(STORE_KEY_TITLE, title) |
57 | .putString(STORE_KEY_MESSAGE, message) | 69 | .putString(STORE_KEY_MESSAGE, message) |
58 | .putString(STORE_KEY_MANUAL_LINK, manualLink) | 70 | .putString(STORE_KEY_MANUAL_LINK, manualLink) |
71 | .putString(STORE_CONFIG_DIR, configDir) | ||
72 | .putBoolean(STORE_PROPOSE_LOGS, proposeLogs) | ||
59 | .apply() | 73 | .apply() |
60 | } | 74 | } |
61 | 75 | ||
diff --git a/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt b/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt index 47ba0ec..77d7e96 100644 --- a/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt +++ b/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt | |||
@@ -123,11 +123,23 @@ class TincVpnService : VpnService() { | |||
123 | val interfaceCfg = try { | 123 | val interfaceCfg = try { |
124 | VpnInterfaceConfiguration.fromIfaceConfiguration(AppPaths.existing(AppPaths.netConfFile(netName))) | 124 | VpnInterfaceConfiguration.fromIfaceConfiguration(AppPaths.existing(AppPaths.netConfFile(netName))) |
125 | } catch (e: FileNotFoundException) { | 125 | } catch (e: FileNotFoundException) { |
126 | return reportError(resources.getString(R.string.notification_error_message_network_config_not_found_format, e.defaultMessage()), e, "configuration") | 126 | return reportError( |
127 | resources.getString(R.string.notification_error_message_network_config_not_found_format, e.defaultMessage()), | ||
128 | e, | ||
129 | docTopic = "configuration" | ||
130 | ) | ||
127 | } catch (e: ConversionException) { | 131 | } catch (e: ConversionException) { |
128 | return reportError(resources.getString(R.string.notification_error_message_network_config_invalid_format, e.defaultMessage()), e, "network-interface") | 132 | return reportError( |
133 | resources.getString(R.string.notification_error_message_network_config_invalid_format, e.defaultMessage()), | ||
134 | e, | ||
135 | docTopic = "network-interface", | ||
136 | ) | ||
129 | } catch (e: Exception) { | 137 | } catch (e: Exception) { |
130 | return reportError(resources.getString(R.string.notification_error_message_could_not_read_network_configuration_format, e.defaultMessage()), e) | 138 | return reportError( |
139 | resources.getString(R.string.notification_error_message_could_not_read_network_configuration_format, e.defaultMessage()), | ||
140 | e, | ||
141 | configDir = netName, | ||
142 | ) | ||
131 | } | 143 | } |
132 | 144 | ||
133 | val deviceFd = try { | 145 | val deviceFd = try { |
@@ -138,11 +150,24 @@ class TincVpnService : VpnService() { | |||
138 | .also { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) it.setMetered(false) } | 150 | .also { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) it.setMetered(false) } |
139 | .establish()!! | 151 | .establish()!! |
140 | } catch (e: IllegalArgumentException) { | 152 | } catch (e: IllegalArgumentException) { |
141 | return reportError(resources.getString(R.string.notification_error_message_network_config_invalid_format, e.defaultMessage()), e, "network-interface") | 153 | return reportError( |
154 | resources.getString(R.string.notification_error_message_network_config_invalid_format, e.defaultMessage()), | ||
155 | e, | ||
156 | docTopic = "network-interface", | ||
157 | configDir = netName, | ||
158 | ) | ||
142 | } catch (e: NullPointerException) { | 159 | } catch (e: NullPointerException) { |
143 | return reportError(resources.getString(R.string.notification_error_message_could_not_bind_iface), e) | 160 | return reportError( |
161 | resources.getString(R.string.notification_error_message_could_not_bind_iface), | ||
162 | e, | ||
163 | proposeLogs = true, | ||
164 | ) | ||
144 | } catch (e: Exception) { | 165 | } catch (e: Exception) { |
145 | return reportError(resources.getString(R.string.notification_error_message_could_not_configure_iface, e.defaultMessage()), e) | 166 | return reportError( |
167 | resources.getString(R.string.notification_error_message_could_not_configure_iface, e.defaultMessage()), | ||
168 | e, | ||
169 | proposeLogs = true, | ||
170 | ) | ||
146 | } |