Additional debug info in application logs

pull/3670/head
Alex Baker 6 months ago
parent f4e0d519d7
commit bbac4da7d0

@ -112,6 +112,20 @@ class TasksJsonExporter @Inject constructor(
}
}
suspend fun doSettingsExport(os: OutputStream?) = withContext(Dispatchers.IO) {
val writer = os!!.bufferedWriter()
with (JsonWriter(writer)) {
write("{")
write("version", BuildConfig.VERSION_CODE)
write("timestamp", currentTimeMillis())
write("\"data\":{")
writePreferences()
write("}")
write("}")
}
writer.flush()
}
@Throws(IOException::class)
private suspend fun doTasksExport(os: OutputStream?, taskIds: List<Long>) = withContext(Dispatchers.IO) {
val writer = os!!.bufferedWriter()
@ -146,11 +160,7 @@ class TasksJsonExporter @Inject constructor(
write("caldavCalendars", caldavDao.getCalendars())
write("taskListMetadata", taskListMetadataDao.getAll())
write("taskAttachments", taskAttachmentDao.getAttachments())
write("intPrefs", preferences.getPrefs(Integer::class.java))
write("longPrefs", preferences.getPrefs(java.lang.Long::class.java))
write("stringPrefs", preferences.getPrefs(String::class.java))
write("boolPrefs", preferences.getPrefs(java.lang.Boolean::class.java))
write("setPrefs", preferences.getPrefs(Set::class.java) as Map<String, Set<String>>, lastItem = true)
writePreferences()
write("}")
write("}")
}
@ -159,6 +169,14 @@ class TasksJsonExporter @Inject constructor(
exportCount = taskIds.size
}
private fun JsonWriter.writePreferences() {
write("intPrefs", preferences.getPrefs(Integer::class.java))
write("longPrefs", preferences.getPrefs(java.lang.Long::class.java))
write("stringPrefs", preferences.getPrefs(String::class.java))
write("boolPrefs", preferences.getPrefs(java.lang.Boolean::class.java))
write("setPrefs", preferences.getPrefs(Set::class.java) as Map<String, Set<String>>, lastItem = true)
}
private fun onFinishExport(outputFile: String) = post {
context?.toast(
R.string.export_toast,

@ -4,12 +4,14 @@ import android.annotation.SuppressLint
import android.app.Application
import android.os.Process
import android.util.Log
import dagger.Lazy
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.tasks.BuildConfig
import org.tasks.backup.TasksJsonExporter
import org.tasks.logging.LogFormatter.Companion.LINE_SEPARATOR
import org.tasks.preferences.Device
import timber.log.Timber
@ -27,6 +29,8 @@ import javax.inject.Singleton
@Singleton
class FileLogger @Inject constructor(
private val context: Application,
private val device: Lazy<Device>,
private val tasksJsonExporter: Lazy<TasksJsonExporter>,
) : Timber.DebugTree() {
private val logDirectory = File(context.cacheDir, "logs").apply { mkdirs() }
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
@ -82,7 +86,10 @@ class FileLogger @Inject constructor(
Timber.e(e, "Failed to save logcat")
}
zos.putNextEntry(ZipEntry("device.txt"))
zos.write(Device(context).debugInfo.toByteArray())
zos.write(device.get().debugInfo.toByteArray())
zos.closeEntry()
zos.putNextEntry(ZipEntry("settings.json"))
tasksJsonExporter.get().doSettingsExport(zos)
zos.closeEntry()
fileHandler.flush()
logDirectory

@ -1,77 +0,0 @@
package org.tasks.preferences;
import static java.util.Arrays.asList;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Build;
import android.speech.RecognizerIntent;
import com.google.common.base.Joiner;
import org.tasks.BuildConfig;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.qualifiers.ApplicationContext;
import timber.log.Timber;
public class Device {
private final Context context;
@Inject
public Device(@ApplicationContext Context context) {
this.context = context;
}
public boolean hasCamera() {
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
}
public boolean hasMicrophone() {
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE);
}
public boolean voiceInputAvailable() {
PackageManager pm = context.getPackageManager();
List<ResolveInfo> activities =
pm.queryIntentActivities(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
return (activities.size() != 0);
}
public String getDebugInfo() {
try {
return Joiner.on("\n")
.join(
asList(
"",
"----------",
"Tasks: "
+ BuildConfig.VERSION_NAME
+ " ("
+ BuildConfig.FLAVOR
+ " build "
+ BuildConfig.VERSION_CODE
+ ")",
"Android: " + Build.VERSION.RELEASE + " (" + Build.DISPLAY + ")",
"Locale: " + java.util.Locale.getDefault(),
"Model: " + Build.MANUFACTURER + " " + Build.MODEL,
"Product: " + Build.PRODUCT + " (" + Build.DEVICE + ")",
"Kernel: "
+ System.getProperty("os.version")
+ " ("
+ Build.VERSION.INCREMENTAL
+ ")",
"----------",
""));
} catch (Exception e) {
Timber.e(e);
}
return "";
}
}

@ -0,0 +1,47 @@
package org.tasks.preferences
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.speech.RecognizerIntent
import dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.BuildConfig
import java.util.Locale
import javax.inject.Inject
class Device @Inject constructor(
@ApplicationContext private val context: Context,
private val permissionChecker: PermissionChecker,
) {
@SuppressLint("UnsupportedChromeOsCameraSystemFeature")
fun hasCamera() = context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
fun hasMicrophone() = context.packageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)
fun voiceInputAvailable(): Boolean {
val pm = context.packageManager
val activities =
pm.queryIntentActivities(Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0)
return (activities.size != 0)
}
val debugInfo: String
get() = """
----------
Tasks: ${BuildConfig.VERSION_NAME} (${BuildConfig.FLAVOR} build ${BuildConfig.VERSION_CODE})
Android: ${Build.VERSION.RELEASE} (${Build.DISPLAY})
Locale: ${Locale.getDefault()}
Model: ${Build.MANUFACTURER} ${Build.MODEL}
Product: ${Build.PRODUCT} (${Build.DEVICE})
Kernel: ${System.getProperty("os.version")} (${Build.VERSION.INCREMENTAL})
----------
notifications: ${permissionChecker.hasNotificationPermission()}
reminders: ${permissionChecker.hasAlarmsAndRemindersPermission()}
background location: ${permissionChecker.canAccessBackgroundLocation()}
foreground location: ${permissionChecker.canAccessForegroundLocation()}
calendar: ${permissionChecker.canAccessCalendars()}
----------
""".trimIndent()
}

@ -41,9 +41,12 @@ public class PermissionChecker {
return !atLeastTiramisu() || checkPermissions(permission.POST_NOTIFICATIONS);
}
public boolean hasAlarmsAndRemindersPermission() {
return org.tasks.extensions.Context.INSTANCE.canScheduleExactAlarms(context);
}
public boolean canNotify() {
return org.tasks.extensions.Context.INSTANCE.canScheduleExactAlarms(context)
&& hasNotificationPermission();
return hasAlarmsAndRemindersPermission() && hasNotificationPermission();
}
private boolean checkPermissions(String... permissions) {

@ -220,7 +220,7 @@ class TaskListViewModel @Inject constructor(
fun dismissBanner(tookAction: Boolean = false) {
when (state.value.banner) {
Banner.NotificationsDisabled -> preferences.warnNotificationsDisabled = tookAction
Banner.AlarmsDisabled -> preferences.warnAlarmsDisabled = false
Banner.AlarmsDisabled -> preferences.warnAlarmsDisabled = tookAction
Banner.QuietHoursEnabled -> preferences.warnQuietHoursDisabled = false
Banner.BegForMoney -> {
preferences.lastSubscribeRequest = currentTimeMillis()

Loading…
Cancel
Save