Commit 56813ad4 authored by Ricki Hirner's avatar Ricki Hirner

Logging: Use FileProvider instead of external file

parent 036fc405
Pipeline #41938832 passed with stages
in 10 minutes and 25 seconds
......@@ -8,45 +8,52 @@
package at.bitfire.davdroid.log
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Process
import android.preference.PreferenceManager
import android.util.Log
import android.widget.Toast
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.FileProvider
import at.bitfire.davdroid.R
import at.bitfire.davdroid.ui.AppSettingsActivity
import at.bitfire.davdroid.ui.NotificationUtils
import org.apache.commons.lang3.time.DateFormatUtils
import java.io.File
import java.io.IOException
import java.util.logging.FileHandler
import java.util.logging.Level
object Logger {
@SuppressLint("StaticFieldLeak") // we'll only keep an app context
object Logger : SharedPreferences.OnSharedPreferenceChangeListener {
const val LOG_TO_EXTERNAL_STORAGE = "log_to_external_storage"
private const val LOG_TO_FILE = "log_to_file"
val log = java.util.logging.Logger.getLogger("davdroid")!!
private lateinit var context: Context
private lateinit var preferences: SharedPreferences
fun initialize(appContext: Context) {
preferences = PreferenceManager.getDefaultSharedPreferences(appContext)
preferences.registerOnSharedPreferenceChangeListener { _, s ->
if (s == LOG_TO_EXTERNAL_STORAGE)
reinitialize(appContext.applicationContext)
fun initialize(someContext: Context) {
context = someContext.applicationContext
preferences = PreferenceManager.getDefaultSharedPreferences(context)
preferences.registerOnSharedPreferenceChangeListener(this)
reinitialize()
}
reinitialize(appContext)
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
if (key == LOG_TO_FILE) {
log.info("Logging settings changed; re-initializing logger")
reinitialize()
}
}
private fun reinitialize(context: Context) {
val logToFile = preferences.getBoolean(LOG_TO_EXTERNAL_STORAGE, false)
private fun reinitialize() {
val logToFile = preferences.getBoolean(LOG_TO_FILE, false)
val logVerbose = logToFile || Log.isLoggable(log.name, Log.DEBUG)
log.info("Verbose logging: $logVerbose; to file: $logToFile")
......@@ -65,42 +72,64 @@ object Logger {
if (logToFile) {
val builder = NotificationUtils.newBuilder(context, NotificationUtils.CHANNEL_DEBUG)
builder .setSmallIcon(R.drawable.ic_sd_storage_notification)
.setContentTitle(context.getString(R.string.logging_davx5_file_logging))
.setLocalOnly(true)
.setContentTitle(context.getString(R.string.logging_notification_title))
val dir = context.getExternalFilesDir(null)
if (dir != null)
try {
val fileName = File(dir, "davdroid-${Process.myPid()}-${DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMdd-HHmmss")}.txt").toString()
log.info("Logging to $fileName")
val logDir = debugDir(context) ?: return
val logFile = File(logDir, "davx5-log.txt")
val fileHandler = FileHandler(fileName)
try {
val fileHandler = FileHandler(logFile.toString(), true)
fileHandler.formatter = PlainTextFormatter.DEFAULT
rootLogger.addHandler(fileHandler)
val prefIntent = Intent(context, AppSettingsActivity::class.java)
prefIntent.putExtra(AppSettingsActivity.EXTRA_SCROLL_TO, LOG_TO_EXTERNAL_STORAGE)
prefIntent.putExtra(AppSettingsActivity.EXTRA_SCROLL_TO, LOG_TO_FILE)
builder .setContentText(dir.path)
builder .setContentText(logDir.path)
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentText(context.getString(R.string.logging_notification_text))
.setContentIntent(PendingIntent.getActivity(context, 0, prefIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setStyle(NotificationCompat.BigTextStyle().bigText(context.getString(R.string.logging_to_external_storage, dir.path)))
.setOngoing(true)
// add "Share" action
val logFileUri = FileProvider.getUriForFile(context, context.getString(R.string.authority_log_provider), logFile)
log.fine("Now logging to file: $logFile -> $logFileUri")
val shareIntent = Intent(Intent.ACTION_SEND)
shareIntent.setDataAndType(logFileUri, "text/plain")
shareIntent.putExtra(Intent.EXTRA_SUBJECT, "DAVx⁵ logs")
shareIntent.putExtra(Intent.EXTRA_STREAM, logFileUri)
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
val shareAction = NotificationCompat.Action.Builder(R.drawable.ic_share_action,
context.getString(R.string.logging_notification_share_log),
PendingIntent.getActivity(context, 0, shareIntent, PendingIntent.FLAG_UPDATE_CURRENT))
builder.addAction(shareAction.build())
} catch(e: IOException) {
log.log(Level.SEVERE, "Couldn't create external log file", e)
val message = context.getString(R.string.logging_couldnt_create_file, e.localizedMessage)
builder .setContentText(message)
.setStyle(NotificationCompat.BigTextStyle().bigText(message))
.setCategory(NotificationCompat.CATEGORY_ERROR)
log.log(Level.SEVERE, "Couldn't create log file", e)
Toast.makeText(context, context.getString(R.string.logging_couldnt_create_file), Toast.LENGTH_LONG).show()
}
else
builder.setContentText(context.getString(R.string.logging_no_external_storage))
nm.notify(NotificationUtils.NOTIFY_EXTERNAL_FILE_LOGGING, builder.build())
} else
} else {
nm.cancel(NotificationUtils.NOTIFY_EXTERNAL_FILE_LOGGING)
// delete old logs
debugDir(context)?.deleteRecursively()
}
}
private fun debugDir(context: Context): File? {
val dir = File(context.filesDir, "debug")
if (dir.exists() && dir.isDirectory)
return dir
if (dir.mkdir())
return dir
Toast.makeText(context, context.getString(R.string.logging_couldnt_create_file), Toast.LENGTH_LONG).show()
return null
}
}
\ No newline at end of file
......@@ -120,12 +120,6 @@ class AppSettingsActivity: AppCompatActivity() {
// security settings
val prefDistrustSystemCerts = findPreference(Settings.DISTRUST_SYSTEM_CERTIFICATES) as SwitchPreferenceCompat
prefDistrustSystemCerts.isChecked = settings.getBoolean(Settings.DISTRUST_SYSTEM_CERTIFICATES) ?: Settings.DISTRUST_SYSTEM_CERTIFICATES_DEFAULT
// debugging settings
val prefLogToExternalStorage = findPreference(Logger.LOG_TO_EXTERNAL_STORAGE) as SwitchPreferenceCompat
prefLogToExternalStorage.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
true
}
}
override fun onSettingsChanged() {
......
......@@ -57,10 +57,10 @@
<string name="about_license_info_no_warranty">This program comes with ABSOLUTELY NO WARRANTY. It is free software, and you are welcome to redistribute it under certain conditions.</string>
<!-- global settings -->
<string name="logging_davx5_file_logging">DAVx⁵ file logging</string>
<string name="logging_to_external_storage">Logging to external storage: %s</string>
<string name="logging_couldnt_create_file">Couldn\'t create external log file: %s</string>
<string name="logging_no_external_storage">External storage not found</string>
<string name="logging_couldnt_create_file">Couldn\'t create log file</string>
<string name="logging_notification_title">DAVx⁵ logging</string>
<string name="logging_notification_text">Now logging all DAVx⁵ activities</string>
<string name="logging_notification_share_log">Share log</string>
<!-- AccountsActivity -->
<string name="navigation_drawer_open">Open navigation drawer</string>
......@@ -87,10 +87,12 @@
<!-- AppSettingsActivity -->
<string name="app_settings">Settings</string>
<string name="app_settings_user_interface">User interface</string>
<string name="app_settings_reset_hints">Reset hints</string>
<string name="app_settings_reset_hints_summary">Re-enables hints which have been dismissed previously</string>
<string name="app_settings_reset_hints_success">All hints will be shown again</string>
<string name="app_settings_debug">Debugging</string>
<string name="app_settings_show_debug_info">Show debug info</string>
<string name="app_settings_show_debug_info_details">View/share software and configuration details</string>
<string name="app_settings_logging">Verbose logging</string>
<string name="app_settings_logging_on">Logging is active</string>
<string name="app_settings_logging_off">Logging is disabled</string>
<string name="app_settings_connection">Connection</string>
<string name="app_settings_override_proxy">Override proxy settings</string>
<string name="app_settings_override_proxy_on">Use custom proxy settings</string>
......@@ -104,12 +106,10 @@
<string name="app_settings_reset_certificates">Reset (un)trusted certificates</string>
<string name="app_settings_reset_certificates_summary">Resets trust of all custom certificates</string>
<string name="app_settings_reset_certificates_success">All custom certificates have been cleared</string>
<string name="app_settings_debug">Debugging</string>
<string name="app_settings_log_to_external_storage">Log to external file</string>
<string name="app_settings_log_to_external_storage_on">Logging to external storage (if available)</string>
<string name="app_settings_log_to_external_storage_off">External file logging is disabled</string>
<string name="app_settings_show_debug_info">Show debug info</string>
<string name="app_settings_show_debug_info_details">View/share software and configuration details</string>
<string name="app_settings_user_interface">User interface</string>
<string name="app_settings_reset_hints">Reset hints</string>
<string name="app_settings_reset_hints_summary">Re-enables hints which have been dismissed previously</string>
<string name="app_settings_reset_hints_success">All hints will be shown again</string>
<!-- AccountActivity -->
<string name="account_synchronize_now">Synchronize now</string>
......
......@@ -8,6 +8,5 @@
-->
<paths>
<!-- debug info can be deleted after it has been used, so let's use a cache dir -->
<cache-path name="debug-info" path="debug-info/"/>
<files-path name="debug" path="debug/"/>
</paths>
\ No newline at end of file
......@@ -11,13 +11,6 @@
<PreferenceCategory android:title="@string/app_settings_debug">
<SwitchPreferenceCompat
android:key="log_to_external_storage"
android:title="@string/app_settings_log_to_external_storage"
android:icon="@drawable/ic_adb_dark"
android:summaryOn="@string/app_settings_log_to_external_storage_on"
android:summaryOff="@string/app_settings_log_to_external_storage_off"/>
<Preference
android:title="@string/app_settings_show_debug_info"
android:summary="@string/app_settings_show_debug_info_details"
......@@ -27,6 +20,13 @@
android:targetClass="at.bitfire.davdroid.ui.DebugInfoActivity"/>
</Preference>
<SwitchPreferenceCompat
android:key="log_to_file"
android:title="@string/app_settings_logging"
android:icon="@drawable/ic_adb_dark"
android:summaryOn="@string/app_settings_logging_on"
android:summaryOff="@string/app_settings_logging_off"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/app_settings_connection">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment