diff options
Diffstat (limited to 'app/src/main/java/org/pacien/tincapp/context')
4 files changed, 130 insertions, 59 deletions
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 4d8d5d0..35e6d09 100644 --- a/app/src/main/java/org/pacien/tincapp/context/App.kt +++ b/app/src/main/java/org/pacien/tincapp/context/App.kt | |||
@@ -28,6 +28,7 @@ import android.os.Handler | |||
28 | import androidx.annotation.StringRes | 28 | import androidx.annotation.StringRes |
29 | import org.pacien.tincapp.BuildConfig | 29 | import org.pacien.tincapp.BuildConfig |
30 | import org.pacien.tincapp.R | 30 | import org.pacien.tincapp.R |
31 | import org.slf4j.Logger | ||
31 | import org.slf4j.LoggerFactory | 32 | import org.slf4j.LoggerFactory |
32 | 33 | ||
33 | /** | 34 | /** |
@@ -39,15 +40,17 @@ class App : Application() { | |||
39 | appContext = applicationContext | 40 | appContext = applicationContext |
40 | handler = Handler() | 41 | handler = Handler() |
41 | AppLogger.configure() | 42 | AppLogger.configure() |
42 | setupCrashHandler() | ||
43 | 43 | ||
44 | val logger = LoggerFactory.getLogger(this.javaClass) | 44 | val logger = LoggerFactory.getLogger(this.javaClass) |
45 | setupCrashHandler(logger) | ||
46 | |||
45 | logger.info("Starting tinc app {} ({} build), running on {} ({})", | 47 | logger.info("Starting tinc app {} ({} build), running on {} ({})", |
46 | BuildConfig.VERSION_NAME, BuildConfig.BUILD_TYPE, Build.VERSION.CODENAME, Build.VERSION.RELEASE) | 48 | BuildConfig.VERSION_NAME, BuildConfig.BUILD_TYPE, Build.VERSION.CODENAME, Build.VERSION.RELEASE) |
49 | |||
50 | StorageMigrator().migrate() | ||
47 | } | 51 | } |
48 | 52 | ||
49 | private fun setupCrashHandler() { | 53 | private fun setupCrashHandler(logger: Logger) { |
50 | val logger = LoggerFactory.getLogger(this.javaClass) | ||
51 | val systemCrashHandler = Thread.getDefaultUncaughtExceptionHandler()!! | 54 | val systemCrashHandler = Thread.getDefaultUncaughtExceptionHandler()!! |
52 | val crashRecorder = CrashRecorder(logger, systemCrashHandler) | 55 | val crashRecorder = CrashRecorder(logger, systemCrashHandler) |
53 | Thread.setDefaultUncaughtExceptionHandler(crashRecorder) | 56 | Thread.setDefaultUncaughtExceptionHandler(crashRecorder) |
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 38bf6e4..29f72de 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 App, an Android binding and user interface for the tinc mesh VPN daemon | 2 | * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon |
3 | * Copyright (C) 2017-2019 Pacien TRAN-GIRARD | 3 | * Copyright (C) 2017-2023 Pacien 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 |
@@ -18,67 +18,59 @@ | |||
18 | 18 | ||
19 | package org.pacien.tincapp.context | 19 | package org.pacien.tincapp.context |
20 | 20 | ||
21 | import android.app.NotificationChannel | ||
22 | import android.app.NotificationManager | ||
23 | import android.app.PendingIntent | ||
24 | import android.content.Context | 21 | import android.content.Context |
25 | import android.content.Intent | 22 | import android.content.SharedPreferences.OnSharedPreferenceChangeListener |
26 | import android.net.Uri | ||
27 | import android.os.Build | ||
28 | import androidx.annotation.RequiresApi | ||
29 | import androidx.core.app.NotificationCompat | ||
30 | import androidx.core.app.NotificationManagerCompat | ||
31 | import org.pacien.tincapp.R | ||
32 | 23 | ||
33 | /** | 24 | /** |
34 | * @author pacien | 25 | * @author pacien |
35 | */ | 26 | */ |
36 | class AppNotificationManager(private val context: Context) { | 27 | class AppNotificationManager(private val context: Context) { |
28 | data class ErrorNotification( | ||
29 | val title: String, | ||
30 | val message: String, | ||
31 | val manualLink: String? | ||
32 | ) | ||
33 | |||
37 | companion object { | 34 | companion object { |
38 | private const val CHANNEL_ID = "org.pacien.tincapp.notification.channels.error" | 35 | private val STORE_NAME = this::class.java.`package`!!.name |
39 | private const val ERROR_NOTIFICATION_ID = 0 | 36 | private const val STORE_KEY_TITLE = "title" |
37 | private const val STORE_KEY_MESSAGE = "message" | ||
38 | private const val STORE_KEY_MANUAL_LINK = "manual_link" | ||
40 | } | 39 | } |
41 | 40 | ||
42 | init { | 41 | private val store by lazy { context.getSharedPreferences(STORE_NAME, Context.MODE_PRIVATE)!! } |
43 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) registerChannel() | ||
44 | } | ||
45 | 42 | ||
46 | fun notifyError(title: String, message: String, manualLink: String? = null) { | 43 | fun getError(): ErrorNotification? { |
47 | val notification = NotificationCompat.Builder(context, CHANNEL_ID) | 44 | if (!store.contains(STORE_KEY_TITLE)) return null; |
48 | .setSmallIcon(R.drawable.ic_warning_primary_24dp) | ||
49 | .setContentTitle(title) | ||
50 | .setContentText(message) | ||
51 | .setStyle(NotificationCompat.BigTextStyle().bigText(message)) | ||
52 | .setHighPriority() | ||
53 | .setAutoCancel(true) | ||
54 | .apply { if (manualLink != null) setManualLink(manualLink) } | ||
55 | .build() | ||
56 | 45 | ||
57 | NotificationManagerCompat.from(context) | 46 | return ErrorNotification( |
58 | .notify(ERROR_NOTIFICATION_ID, notification) | 47 | store.getString(STORE_KEY_TITLE, null)!!, |
48 | store.getString(STORE_KEY_MESSAGE, null)!!, | ||
49 | store.getString(STORE_KEY_MANUAL_LINK, null) | ||
50 | ) | ||
59 | } | 51 | } |
60 | 52 | ||
61 | fun dismissAll() { | 53 | fun notifyError(title: String, message: String, manualLink: String? = null) { |
62 | NotificationManagerCompat.from(context).cancelAll() | 54 | store |
55 | .edit() | ||
56 | .putString(STORE_KEY_TITLE, title) | ||
57 | .putString(STORE_KEY_MESSAGE, message) | ||
58 | .putString(STORE_KEY_MANUAL_LINK, manualLink) | ||
59 | .apply() | ||
63 | } | 60 | } |
64 | 61 | ||
65 | @RequiresApi(Build.VERSION_CODES.O) | 62 | fun dismissAll() { |
66 | private fun registerChannel() { | 63 | store |
67 | val name = context.getString(R.string.notification_error_channel_name) | 64 | .edit() |
68 | val importance = NotificationManager.IMPORTANCE_HIGH | 65 | .clear() |
69 | val channel = NotificationChannel(CHANNEL_ID, name, importance) | 66 | .apply() |
70 | val notificationManager = context.getSystemService(NotificationManager::class.java) | ||
71 | notificationManager.createNotificationChannel(channel) | ||
72 | } | 67 | } |
73 | 68 | ||
74 | private fun NotificationCompat.Builder.setHighPriority() = apply { | 69 | fun registerListener(listener: OnSharedPreferenceChangeListener) { |
75 | priority = NotificationCompat.PRIORITY_MAX | 70 | store.registerOnSharedPreferenceChangeListener(listener) |
76 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) setDefaults(NotificationCompat.DEFAULT_SOUND) // force heads-up notification | ||
77 | } | 71 | } |
78 | 72 | ||
79 | private fun NotificationCompat.Builder.setManualLink(manualLink: String) = apply { | 73 | fun unregisterListener(listener: OnSharedPreferenceChangeListener) { |
80 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse(manualLink)) | 74 | store.unregisterOnSharedPreferenceChangeListener(listener) |
81 | val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0) | ||
82 | addAction(R.drawable.ic_help_primary_24dp, context.getString(R.string.notification_error_action_open_manual), pendingIntent) | ||
83 | } | 75 | } |
84 | } | 76 | } |
diff --git a/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt b/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt index bd8316a..9e69790 100644 --- a/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt +++ b/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon | 2 | * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon |
3 | * Copyright (C) 2017-2020 Pacien TRAN-GIRARD | 3 | * Copyright (C) 2017-2023 Pacien 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 |
@@ -18,7 +18,6 @@ | |||
18 | 18 | ||
19 | package org.pacien.tincapp.context | 19 | package org.pacien.tincapp.context |
20 | 20 | ||
21 | import android.os.Environment | ||
22 | import java.io.File | 21 | import java.io.File |
23 | import java.io.FileNotFoundException | 22 | import java.io.FileNotFoundException |
24 | 23 | ||
@@ -28,6 +27,10 @@ import java.io.FileNotFoundException | |||
28 | * @implNote Logs and PID files are stored in the cache directory for automatic collection. | 27 | * @implNote Logs and PID files are stored in the cache directory for automatic collection. |
29 | */ | 28 | */ |
30 | object AppPaths { | 29 | object AppPaths { |
30 | private const val APP_LOG_DIR = "log" | ||
31 | private const val APP_TINC_RUNTIME_DIR = "run" | ||
32 | private const val APP_TINC_NETWORKS_DIR = "networks" | ||
33 | |||
31 | private const val TINCD_BIN = "libtincd.so" | 34 | private const val TINCD_BIN = "libtincd.so" |
32 | private const val TINC_BIN = "libtinc.so" | 35 | private const val TINC_BIN = "libtinc.so" |
33 | 36 | ||
@@ -46,25 +49,27 @@ object AppPaths { | |||
46 | 49 | ||
47 | private val context by lazy { App.getContext() } | 50 | private val context by lazy { App.getContext() } |
48 | 51 | ||
49 | fun storageAvailable() = | 52 | private fun privateCacheDir() = context.cacheDir!! |
50 | Environment.getExternalStorageState().let { it == Environment.MEDIA_MOUNTED && it != Environment.M |