Implemented file logging settings

Implemented log rotation, log deletion
Minor fixes
This commit is contained in:
Nite 2020-10-02 18:47:21 +02:00
parent 5de20861ca
commit 6e7ebeabef
No known key found for this signature in database
GPG Key ID: 1D1AD59B1C6386C1
18 changed files with 278 additions and 19 deletions

View File

@ -22,6 +22,7 @@ import org.moire.ultrasonic.featureflags.Feature;
import org.moire.ultrasonic.featureflags.FeatureStorage; import org.moire.ultrasonic.featureflags.FeatureStorage;
import org.moire.ultrasonic.filepicker.FilePickerDialog; import org.moire.ultrasonic.filepicker.FilePickerDialog;
import org.moire.ultrasonic.filepicker.OnFileSelectedListener; import org.moire.ultrasonic.filepicker.OnFileSelectedListener;
import org.moire.ultrasonic.log.FileLoggerTree;
import org.moire.ultrasonic.provider.SearchSuggestionProvider; import org.moire.ultrasonic.provider.SearchSuggestionProvider;
import org.moire.ultrasonic.service.Consumer; import org.moire.ultrasonic.service.Consumer;
import org.moire.ultrasonic.service.MediaPlayerController; import org.moire.ultrasonic.service.MediaPlayerController;
@ -70,6 +71,7 @@ public class SettingsFragment extends PreferenceFragment
private PreferenceCategory serversCategory; private PreferenceCategory serversCategory;
private Preference resumeOnBluetoothDevice; private Preference resumeOnBluetoothDevice;
private Preference pauseOnBluetoothDevice; private Preference pauseOnBluetoothDevice;
private CheckBoxPreference debugLogToFile;
private SharedPreferences settings; private SharedPreferences settings;
@ -118,6 +120,7 @@ public class SettingsFragment extends PreferenceFragment
serversCategory = (PreferenceCategory) findPreference(Constants.PREFERENCES_KEY_SERVERS_KEY); serversCategory = (PreferenceCategory) findPreference(Constants.PREFERENCES_KEY_SERVERS_KEY);
resumeOnBluetoothDevice = findPreference(Constants.PREFERENCES_KEY_RESUME_ON_BLUETOOTH_DEVICE); resumeOnBluetoothDevice = findPreference(Constants.PREFERENCES_KEY_RESUME_ON_BLUETOOTH_DEVICE);
pauseOnBluetoothDevice = findPreference(Constants.PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE); pauseOnBluetoothDevice = findPreference(Constants.PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE);
debugLogToFile = (CheckBoxPreference) findPreference(Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE);
sharingDefaultGreeting.setText(Util.getShareGreeting(getActivity())); sharingDefaultGreeting.setText(Util.getShareGreeting(getActivity()));
setupClearSearchPreference(); setupClearSearchPreference();
@ -171,6 +174,8 @@ public class SettingsFragment extends PreferenceFragment
setBluetoothPreferences(sharedPreferences.getBoolean(key, true)); setBluetoothPreferences(sharedPreferences.getBoolean(key, true));
} else if (Constants.PREFERENCES_KEY_IMAGE_LOADER_CONCURRENCY.equals(key)) { } else if (Constants.PREFERENCES_KEY_IMAGE_LOADER_CONCURRENCY.equals(key)) {
setImageLoaderConcurrency(Integer.parseInt(sharedPreferences.getString(key, "5"))); setImageLoaderConcurrency(Integer.parseInt(sharedPreferences.getString(key, "5")));
} else if (Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE.equals(key)) {
setDebugLogToFile(sharedPreferences.getBoolean(key, false));
} }
} }
@ -415,6 +420,13 @@ public class SettingsFragment extends PreferenceFragment
sendBluetoothAlbumArt.setChecked(false); sendBluetoothAlbumArt.setChecked(false);
sendBluetoothAlbumArt.setEnabled(false); sendBluetoothAlbumArt.setEnabled(false);
} }
if (debugLogToFile.isChecked()) {
debugLogToFile.setSummary(getString(R.string.settings_debug_log_path,
FileUtil.getUltrasonicDirectory(getActivity()), FileLoggerTree.FILENAME));
} else {
debugLogToFile.setSummary("");
}
} }
private static void setImageLoaderConcurrency(int concurrency) { private static void setImageLoaderConcurrency(int concurrency) {
@ -482,4 +494,47 @@ public class SettingsFragment extends PreferenceFragment
// Clear download queue. // Clear download queue.
mediaPlayerControllerLazy.getValue().clear(); mediaPlayerControllerLazy.getValue().clear();
} }
private void setDebugLogToFile(boolean writeLog) {
if (writeLog) {
FileLoggerTree.Companion.plantToTimberForest(getActivity().getApplicationContext());
Timber.i("Enabled debug logging to file");
} else {
FileLoggerTree.Companion.uprootFromTimberForest();
Timber.i("Disabled debug logging to file");
int fileNum = FileLoggerTree.Companion.getLogFileNumber(getActivity());
long fileSize = FileLoggerTree.Companion.getLogFileSizes(getActivity());
String message = getString(R.string.settings_debug_log_summary,
String.valueOf(fileNum),
String.valueOf(Math.ceil(fileSize / 1000000d)),
FileUtil.getUltrasonicDirectory(getActivity()));
new AlertDialog.Builder(getActivity())
.setMessage(message)
.setIcon(android.R.drawable.ic_dialog_info)
.setNegativeButton(R.string.settings_debug_log_keep, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.cancel();
}
})
.setPositiveButton(R.string.settings_debug_log_delete, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
FileLoggerTree.Companion.deleteLogFiles(getActivity());
Timber.i("Deleted debug log files");
dialogInterface.dismiss();
new AlertDialog.Builder(getActivity()).setMessage(R.string.settings_debug_log_deleted)
.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
}).create().show();
}
})
.create().show();
}
}
} }

View File

@ -134,6 +134,7 @@ public final class Constants
public static final String PREFERENCES_KEY_FIRST_RUN_EXECUTED = "firstRunExecuted"; public static final String PREFERENCES_KEY_FIRST_RUN_EXECUTED = "firstRunExecuted";
public static final String PREFERENCES_KEY_RESUME_ON_BLUETOOTH_DEVICE = "resumeOnBluetoothDevice"; public static final String PREFERENCES_KEY_RESUME_ON_BLUETOOTH_DEVICE = "resumeOnBluetoothDevice";
public static final String PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE = "pauseOnBluetoothDevice"; public static final String PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE = "pauseOnBluetoothDevice";
public static final String PREFERENCES_KEY_DEBUG_LOG_TO_FILE = "debugLogToFile";
public static final int PREFERENCE_VALUE_ALL = 0; public static final int PREFERENCE_VALUE_ALL = 0;
public static final int PREFERENCE_VALUE_A2DP = 1; public static final int PREFERENCE_VALUE_A2DP = 1;

View File

@ -1457,4 +1457,11 @@ public class Util
SharedPreferences preferences = getPreferences(context); SharedPreferences preferences = getPreferences(context);
return preferences.getInt(Constants.PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE, Constants.PREFERENCE_VALUE_A2DP); return preferences.getInt(Constants.PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE, Constants.PREFERENCE_VALUE_A2DP);
} }
public static boolean getDebugLogToFile(Context context)
{
SharedPreferences preferences = getPreferences(context);
return preferences.getBoolean(Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE, false);
}
} }

View File

@ -2,7 +2,6 @@ package org.moire.ultrasonic.app
import androidx.multidex.MultiDexApplication import androidx.multidex.MultiDexApplication
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
import org.koin.core.logger.Level import org.koin.core.logger.Level
import org.moire.ultrasonic.BuildConfig import org.moire.ultrasonic.BuildConfig
@ -13,8 +12,8 @@ import org.moire.ultrasonic.di.featureFlagsModule
import org.moire.ultrasonic.di.mediaPlayerModule import org.moire.ultrasonic.di.mediaPlayerModule
import org.moire.ultrasonic.di.musicServiceModule import org.moire.ultrasonic.di.musicServiceModule
import org.moire.ultrasonic.log.FileLoggerTree import org.moire.ultrasonic.log.FileLoggerTree
import org.moire.ultrasonic.log.TimberKoinLogger
import org.moire.ultrasonic.log.timberLogger import org.moire.ultrasonic.log.timberLogger
import org.moire.ultrasonic.util.Util
import timber.log.Timber import timber.log.Timber
import timber.log.Timber.DebugTree import timber.log.Timber.DebugTree
@ -24,7 +23,9 @@ class UApp : MultiDexApplication() {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Timber.plant(DebugTree()) Timber.plant(DebugTree())
Timber.plant(FileLoggerTree(this)) }
if (Util.getDebugLogToFile(this)) {
FileLoggerTree.plantToTimberForest(this)
} }
startKoin { startKoin {

View File

@ -1,8 +1,8 @@
@file:JvmName("MusicServiceModule") @file:JvmName("MusicServiceModule")
package org.moire.ultrasonic.di package org.moire.ultrasonic.di
import okhttp3.logging.HttpLoggingInterceptor
import kotlin.math.abs import kotlin.math.abs
import okhttp3.logging.HttpLoggingInterceptor
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
import org.koin.core.qualifier.named import org.koin.core.qualifier.named
import org.koin.dsl.module import org.koin.dsl.module

View File

@ -1,37 +1,106 @@
package org.moire.ultrasonic.log package org.moire.ultrasonic.log
import android.content.Context import android.content.Context
import java.io.File
import java.io.FileWriter
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import org.moire.ultrasonic.util.FileUtil import org.moire.ultrasonic.util.FileUtil
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
import timber.log.Timber import timber.log.Timber
import java.io.File
import java.io.FileWriter
/** /**
* A Timber Tree which can be used to log to a file * A Timber Tree which can be used to log to a file
* Subclass of the DebugTree so it inherits the Tag handling * Subclass of the DebugTree so it inherits the Tag handling
*/ */
class FileLoggerTree(val context: Context) : Timber.DebugTree() { class FileLoggerTree(val context: Context) : Timber.DebugTree() {
private val filename = "ultrasonic.log" private val dateFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault())
/**
* Writes a log entry to file
*/
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
var file: File? = null
var writer: FileWriter? = null var writer: FileWriter? = null
callNum++
try { try {
file = File(FileUtil.getUltrasonicDirectory(context), filename) getNextLogFile()
writer = FileWriter(file, true) writer = FileWriter(file, true)
val exceptionString = t?.toString() ?: ""; val exceptionString = t?.toString() ?: ""
writer.write("${logLevelToString(priority)} $tag $message $exceptionString\n") val time: String = dateFormat.format(Date())
writer.flush() synchronized(file!!) {
writer.write(
"$time: ${logLevelToString(priority)} $tag $message $exceptionString\n"
)
writer.flush()
}
} catch (x: Throwable) { } catch (x: Throwable) {
super.e(x, "Failed to write log to %s", file) // Using base class DebugTree here, we don't want to try to log this into file
super.log(6, TAG, String.format("Failed to write log to %s", file), x)
} finally { } finally {
if (writer != null) Util.close(writer) if (writer != null) Util.close(writer)
} }
} }
private fun logLevelToString(logLevel: Int) : String { /**
return when(logLevel) { * Sets the file to log into
* This function also rotates the log files periodically, when they reach the predefined size
*/
private fun getNextLogFile() {
if (file == null) {
synchronized(this) {
if (file != null) return
getNumberedFile(false)
// Using base class DebugTree here, we don't want to try to log this into file
super.log(4, TAG,String.format("Logging into file %s", file?.name), null)
return
}
}
if (callNum % 100 == 0) {
// Gain some performance by only executing this rarely
if (file!!.length() > MAX_LOGFILE_LENGTH) {
synchronized(this) {
if (file!!.length() <= MAX_LOGFILE_LENGTH) return
getNumberedFile(true)
// Using base class DebugTree here, we don't want to try to log this into file
super.log(
4,
TAG,
String.format("Log file rotated, logging into file %s", file?.name),
null
)
}
}
}
}
/**
* Checks the number of log files
* @param next: if false, sets the current log file with the greatest number
* if true, sets a new file for logging with the next number
*/
private fun getNumberedFile(next: Boolean) {
var fileNum = 1
val fileList = getLogFileList(context)
if (!fileList.isNullOrEmpty()) {
fileList.sortByDescending { t -> t.name }
val lastFile = fileList[0]
val number = fileNumberRegex.find(lastFile.name)?.groups?.get(1)?.value
if (number != null) {
fileNum = number.toInt()
}
}
if (next) fileNum++
file = File(
FileUtil.getUltrasonicDirectory(context),
FILENAME.replace("*", fileNum.toString())
)
}
private fun logLevelToString(logLevel: Int): String {
return when (logLevel) {
2 -> "V" 2 -> "V"
3 -> "D" 3 -> "D"
4 -> "I" 4 -> "I"
@ -41,4 +110,60 @@ class FileLoggerTree(val context: Context) : Timber.DebugTree() {
else -> "U" else -> "U"
} }
} }
}
companion object {
val TAG = FileLoggerTree::class.simpleName
@Volatile private var file: File? = null
const val FILENAME = "ultrasonic.*.log"
private val fileNameRegex = Regex(
FILENAME.replace(".", "\\.").replace("*", "\\d*")
)
private val fileNumberRegex = Regex(
FILENAME.replace(".", "\\.").replace("*", "(\\d*)")
)
const val MAX_LOGFILE_LENGTH = 10000000
var callNum = 0
fun plantToTimberForest(context: Context) {
if (!Timber.forest().any { t -> t is FileLoggerTree }) {
Timber.plant(FileLoggerTree(context))
}
}
fun uprootFromTimberForest() {
val fileLoggerTree = Timber.forest().singleOrNull { t -> t is FileLoggerTree }
?: return
Timber.uproot(fileLoggerTree)
file = null
}
fun getLogFileNumber(context: Context): Int {
val fileList = getLogFileList(context)
if (!fileList.isNullOrEmpty()) return fileList.size
return 0
}
fun getLogFileSizes(context: Context): Long {
var sizeSum: Long = 0
val fileList = getLogFileList(context)
if (fileList.isNullOrEmpty()) return sizeSum
for (file in fileList) {
sizeSum += file.length()
}
return sizeSum
}
fun deleteLogFiles(context: Context) {
val fileList = getLogFileList(context)
if (fileList.isNullOrEmpty()) return
for (file in fileList) {
file.delete()
}
}
private fun getLogFileList(context: Context): Array<File> {
val directory = FileUtil.getUltrasonicDirectory(context)
return directory.listFiles { t -> t.name.matches(fileNameRegex) }
}
}
}

View File

@ -19,7 +19,7 @@ fun KoinApplication.timberLogger(
/** /**
* Timber Logger implementation for Koin * Timber Logger implementation for Koin
*/ */
class TimberKoinLogger (level: Level = Level.INFO) : Logger(level) { class TimberKoinLogger(level: Level = Level.INFO) : Logger(level) {
override fun log(level: Level, msg: MESSAGE) { override fun log(level: Level, msg: MESSAGE) {
if (this.level <= level) { if (this.level <= level) {

View File

@ -10,4 +10,4 @@ class TimberOkHttpLogger : HttpLoggingInterceptor.Logger {
override fun log(message: String) { override fun log(message: String) {
Timber.d(message) Timber.d(message)
} }
} }

View File

@ -401,6 +401,13 @@
<string name="settings.playback.bluetooth_all">All Bluetooth devices</string> <string name="settings.playback.bluetooth_all">All Bluetooth devices</string>
<string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string> <string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string>
<string name="settings.playback.bluetooth_disabled">Disabled</string> <string name="settings.playback.bluetooth_disabled">Disabled</string>
<string name="settings.debug.title">Debug options</string>
<string name="settings.debug.log_to_file">Write debug log to file</string>
<string name="settings.debug.log_path">The log files are available at %1$s/%2$s</string>
<string name="settings.debug.log_summary">There are %1$s log files taking up ~%2$s MB space in the %3$s directory. Do you want to keep these?</string>
<string name="settings.debug.log_keep">Keep files</string>
<string name="settings.debug.log_delete">Delete files</string>
<string name="settings.debug.log_deleted">Deleted log files.</string>
<string name="permissions.access_error">Ultrasonic can\'t access the music file cache. Cache location was reset to the default path.</string> <string name="permissions.access_error">Ultrasonic can\'t access the music file cache. Cache location was reset to the default path.</string>
<string name="permissions.message_box_title">Warning</string> <string name="permissions.message_box_title">Warning</string>

View File

@ -402,6 +402,13 @@
<string name="settings.playback.bluetooth_all">Todos los dispositivos Bluetooth</string> <string name="settings.playback.bluetooth_all">Todos los dispositivos Bluetooth</string>
<string name="settings.playback.bluetooth_a2dp">Solo dispositivos de audio (A2DP)</string> <string name="settings.playback.bluetooth_a2dp">Solo dispositivos de audio (A2DP)</string>
<string name="settings.playback.bluetooth_disabled">Deshabilitado</string> <string name="settings.playback.bluetooth_disabled">Deshabilitado</string>
<string name="settings.debug.title">Debug options</string>
<string name="settings.debug.log_to_file">Write debug log to file</string>
<string name="settings.debug.log_path">The log files are available at %1$s/%2$s</string>
<string name="settings.debug.log_summary">There are %1$s log files taking up ~%2$s MB space in the %3$s directory. Do you want to keep these?</string>
<string name="settings.debug.log_keep">Keep files</string>
<string name="settings.debug.log_delete">Delete files</string>
<string name="settings.debug.log_deleted">Deleted log files.</string>
<string name="permissions.access_error">Ultrasonic no puede acceder a la caché de los ficheros de música. La ubicación de la caché se restableció a la ruta predeterminada.</string> <string name="permissions.access_error">Ultrasonic no puede acceder a la caché de los ficheros de música. La ubicación de la caché se restableció a la ruta predeterminada.</string>
<string name="permissions.message_box_title">Atención</string> <string name="permissions.message_box_title">Atención</string>

View File

@ -402,6 +402,13 @@
<string name="settings.playback.bluetooth_all">All Bluetooth devices</string> <string name="settings.playback.bluetooth_all">All Bluetooth devices</string>
<string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string> <string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string>
<string name="settings.playback.bluetooth_disabled">Disabled</string> <string name="settings.playback.bluetooth_disabled">Disabled</string>
<string name="settings.debug.title">Debug options</string>
<string name="settings.debug.log_to_file">Write debug log to file</string>
<string name="settings.debug.log_path">The log files are available at %1$s/%2$s</string>
<string name="settings.debug.log_summary">There are %1$s log files taking up ~%2$s MB space in the %3$s directory. Do you want to keep these?</string>
<string name="settings.debug.log_keep">Keep files</string>
<string name="settings.debug.log_delete">Delete files</string>
<string name="settings.debug.log_deleted">Deleted log files.</string>
<string name="permissions.access_error">Ultrasonic can\'t access the music file cache. Cache location was reset to the default path.</string> <string name="permissions.access_error">Ultrasonic can\'t access the music file cache. Cache location was reset to the default path.</string>
<string name="permissions.message_box_title">Warning</string> <string name="permissions.message_box_title">Warning</string>

View File

@ -402,6 +402,13 @@
<string name="settings.playback.bluetooth_all">Minden Bluetooth eszköz</string> <string name="settings.playback.bluetooth_all">Minden Bluetooth eszköz</string>
<string name="settings.playback.bluetooth_a2dp">Csak audio (A2DP) eszközök</string> <string name="settings.playback.bluetooth_a2dp">Csak audio (A2DP) eszközök</string>
<string name="settings.playback.bluetooth_disabled">Kikapcsolva</string> <string name="settings.playback.bluetooth_disabled">Kikapcsolva</string>
<string name="settings.debug.title">Hibakeresési lehetőségek</string>
<string name="settings.debug.log_to_file">Hibakeresési napló írása fájlba</string>
<string name="settings.debug.log_path">A naplófájlok elérhetőek a következő helyen: %1$s/%2$s</string>
<string name="settings.debug.log_summary">%1$s napló fájl ~%2$s MB méretben található a %3$s könyvtárban. Meg szeretnéd atrtani ezeket?</string>
<string name="settings.debug.log_keep">Fájlok megtartása</string>
<string name="settings.debug.log_delete">Fájlok törlése</string>
<string name="settings.debug.log_deleted">Naplófájlok törölve.</string>
<string name="permissions.access_error">Az Ultrasonic nem éri el a zenei fájl gyorsítótárat. A gyorsítótár helye visszaállítva az alapbeállításra.</string> <string name="permissions.access_error">Az Ultrasonic nem éri el a zenei fájl gyorsítótárat. A gyorsítótár helye visszaállítva az alapbeállításra.</string>
<string name="permissions.message_box_title">Figyelem</string> <string name="permissions.message_box_title">Figyelem</string>

View File

@ -402,6 +402,13 @@
<string name="settings.playback.bluetooth_all">All Bluetooth devices</string> <string name="settings.playback.bluetooth_all">All Bluetooth devices</string>
<string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string> <string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string>
<string name="settings.playback.bluetooth_disabled">Disabled</string> <string name="settings.playback.bluetooth_disabled">Disabled</string>
<string name="settings.debug.title">Debug options</string>
<string name="settings.debug.log_to_file">Write debug log to file</string>
<string name="settings.debug.log_path">The log files are available at %1$s/%2$s</string>
<string name="settings.debug.log_summary">There are %1$s log files taking up ~%2$s MB space in the %3$s directory. Do you want to keep these?</string>
<string name="settings.debug.log_keep">Keep files</string>
<string name="settings.debug.log_delete">Delete files</string>
<string name="settings.debug.log_deleted">Deleted log files.</string>
<string name="permissions.access_error">Ultrasonic can\'t access the music file cache. Cache location was reset to the default path.</string> <string name="permissions.access_error">Ultrasonic can\'t access the music file cache. Cache location was reset to the default path.</string>
<string name="permissions.message_box_title">Warning</string> <string name="permissions.message_box_title">Warning</string>

View File

@ -402,6 +402,13 @@ ponieważ api Subsonic nie wspiera nowego sposobu autoryzacji dla użytkowników
<string name="settings.playback.bluetooth_all">All Bluetooth devices</string> <string name="settings.playback.bluetooth_all">All Bluetooth devices</string>
<string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string> <string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string>
<string name="settings.playback.bluetooth_disabled">Disabled</string> <string name="settings.playback.bluetooth_disabled">Disabled</string>
<string name="settings.debug.title">Debug options</string>
<string name="settings.debug.log_to_file">Write debug log to file</string>
<string name="settings.debug.log_path">The log files are available at %1$s/%2$s</string>
<string name="settings.debug.log_summary">There are %1$s log files taking up ~%2$s MB space in the %3$s directory. Do you want to keep these?</string>
<string name="settings.debug.log_keep">Keep files</string>
<string name="settings.debug.log_delete">Delete files</string>
<string name="settings.debug.log_deleted">Deleted log files.</string>
<string name="permissions.access_error">Ultrasonic can\'t access the music file cache. Cache location was reset to the default path.</string> <string name="permissions.access_error">Ultrasonic can\'t access the music file cache. Cache location was reset to the default path.</string>
<string name="permissions.message_box_title">Warning</string> <string name="permissions.message_box_title">Warning</string>

View File

@ -402,6 +402,13 @@
<string name="settings.playback.bluetooth_all">All Bluetooth devices</string> <string name="settings.playback.bluetooth_all">All Bluetooth devices</string>
<string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string> <string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string>
<string name="settings.playback.bluetooth_disabled">Disabled</string> <string name="settings.playback.bluetooth_disabled">Disabled</string>
<string name="settings.debug.title">Debug options</string>
<string name="settings.debug.log_to_file">Write debug log to file</string>
<string name="settings.debug.log_path">The log files are available at %1$s/%2$s</string>
<string name="settings.debug.log_summary">There are %1$s log files taking up ~%2$s MB space in the %3$s directory. Do you want to keep these?</string>
<string name="settings.debug.log_keep">Keep files</string>
<string name="settings.debug.log_delete">Delete files</string>
<string name="settings.debug.log_deleted">Deleted log files.</string>
<string name="permissions.access_error">Ultrasonic can\'t access the music file cache. Cache location was reset to the default path.</string> <string name="permissions.access_error">Ultrasonic can\'t access the music file cache. Cache location was reset to the default path.</string>
<string name="permissions.message_box_title">Warning</string> <string name="permissions.message_box_title">Warning</string>

View File

@ -402,6 +402,13 @@
<string name="settings.playback.bluetooth_all">All Bluetooth devices</string> <string name="settings.playback.bluetooth_all">All Bluetooth devices</string>
<string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string> <string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string>
<string name="settings.playback.bluetooth_disabled">Disabled</string> <string name="settings.playback.bluetooth_disabled">Disabled</string>
<string name="settings.debug.title">Debug options</string>
<string name="settings.debug.log_to_file">Write debug log to file</string>
<string name="settings.debug.log_path">The log files are available at %1$s/%2$s</string>
<string name="settings.debug.log_summary">There are %1$s log files taking up ~%2$s MB space in the %3$s directory. Do you want to keep these?</string>
<string name="settings.debug.log_keep">Keep files</string>
<string name="settings.debug.log_delete">Delete files</string>
<string name="settings.debug.log_deleted">Deleted log files.</string>
<string name="permissions.access_error">Ultrasonic can\'t access the music file cache. Cache location was reset to the default path.</string> <string name="permissions.access_error">Ultrasonic can\'t access the music file cache. Cache location was reset to the default path.</string>
<string name="permissions.message_box_title">Warning</string> <string name="permissions.message_box_title">Warning</string>

View File

@ -405,6 +405,13 @@
<string name="settings.playback.bluetooth_all">All Bluetooth devices</string> <string name="settings.playback.bluetooth_all">All Bluetooth devices</string>
<string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string> <string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string>
<string name="settings.playback.bluetooth_disabled">Disabled</string> <string name="settings.playback.bluetooth_disabled">Disabled</string>
<string name="settings.debug.title">Debug options</string>
<string name="settings.debug.log_to_file">Write debug log to file</string>
<string name="settings.debug.log_path">The log files are available at %1$s/%2$s</string>
<string name="settings.debug.log_summary">There are %1$s log files taking up ~%2$s MB space in the %3$s directory. Do you want to keep these?</string>
<string name="settings.debug.log_keep">Keep files</string>
<string name="settings.debug.log_delete">Delete files</string>
<string name="settings.debug.log_deleted">Deleted log files.</string>
<string name="permissions.access_error">Ultrasonic can\'t access the music file cache. Cache location was reset to the default path.</string> <string name="permissions.access_error">Ultrasonic can\'t access the music file cache. Cache location was reset to the default path.</string>
<string name="permissions.message_box_title">Warning</string> <string name="permissions.message_box_title">Warning</string>

View File

@ -305,7 +305,14 @@
a:title="@string/feature_flags_five_star_rating_title" a:title="@string/feature_flags_five_star_rating_title"
a:summary="@string/feature_flags_five_star_rating_description" a:summary="@string/feature_flags_five_star_rating_description"
/> />
</PreferenceCategory>
<PreferenceCategory a:title="@string/settings.debug.title">
<CheckBoxPreference
a:defaultValue="false"
a:key="debugLogToFile"
a:title="@string/settings.debug.log_to_file"
a:summary=""
/>
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>