Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class RecorderModule @Inject constructor(
private val triggerFile by lazy {
try {
File(context.getExternalFilesDir(null), FORCE_FILE)
} catch (e: Exception) {
} catch (_: Exception) {
File(
Environment.getExternalStorageDirectory(),
"/Android/data/${BuildConfigWrap.APPLICATION_ID}/files/$FORCE_FILE"
Expand Down Expand Up @@ -134,7 +134,7 @@ class RecorderModule @Inject constructor(
val logId = sdmId.id.take(4)
var sessionDir: File? = null

File(File(context.externalCacheDir, "debug/logs"), "${pkg}_${version}_${timestamp}_$logId").apply {
File(File(context.getExternalFilesDir(null), "debug/logs"), "${pkg}_${version}_${timestamp}_$logId").apply {
@Suppress("SetWorldWritable", "SetWorldReadable")
if (mkdirs()) {
log(TAG) { "Public session dir created" }
Expand Down Expand Up @@ -175,6 +175,20 @@ class RecorderModule @Inject constructor(
return currentPath
}

fun getLogDirectories(): List<File> {
val dirs = mutableListOf<File>()

// Primary location: external files dir
context.getExternalFilesDir(null)?.let { externalDir ->
File(externalDir, "debug/logs").takeIf { it.exists() }?.let { dirs.add(it) }
}

// Fallback location: cache dir
File(context.cacheDir, "debug/logs").takeIf { it.exists() }?.let { dirs.add(it) }

return dirs
}

private suspend fun logInfos() {
val pkgInfo = context.getPackageInfo()
log(TAG, INFO) { "APILEVEL: ${BuildWrap.VERSION.SDK_INT}" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package eu.darken.sdmse.main.ui.settings.support

import android.os.Bundle
import android.text.format.Formatter
import android.view.View
import androidx.annotation.Keep
import androidx.fragment.app.viewModels
import androidx.preference.Preference
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.sdmse.R
Expand Down Expand Up @@ -32,6 +34,7 @@ class SupportFragment : PreferenceFragment2() {

private val installIdPref by lazy { findPreference<Preference>("support.installid")!! }
private val debugLogPref by lazy { findPreference<Preference>("support.debuglog")!! }
private val debugLogFolderPref by lazy { findPreference<Preference>("support.debuglog.folder")!! }

override fun onPreferencesCreated() {
installIdPref.setOnPreferenceClickListener {
Expand Down Expand Up @@ -69,6 +72,37 @@ class SupportFragment : PreferenceFragment2() {
}
true
}
debugLogFolderPref.isEnabled = !isRecording
}

vm.debugLogFolderStats.observe2(this) { stats ->
if (stats.fileCount == 0) {
debugLogFolderPref.summary = getString(R.string.support_debuglog_folder_empty_desc)
} else {
val formattedSize = Formatter.formatShortFileSize(requireContext(), stats.totalSizeBytes)
debugLogFolderPref.summary = getString(
R.string.support_debuglog_folder_desc,
stats.fileCount,
formattedSize
)
}
}

debugLogFolderPref.setOnPreferenceClickListener {
val stats = vm.debugLogFolderStats.value ?: return@setOnPreferenceClickListener true
if (stats.fileCount == 0) {
Snackbar.make(requireView(), R.string.support_debuglog_folder_empty_desc, Snackbar.LENGTH_SHORT).show()
} else {
MaterialAlertDialogBuilder(requireContext()).apply {
setTitle(R.string.support_debuglog_folder_delete_confirmation_title)
setMessage(R.string.support_debuglog_folder_delete_confirmation_message)
setPositiveButton(eu.darken.sdmse.common.R.string.general_delete_action) { _, _ ->
vm.deleteAllDebugLogs()
}
setNegativeButton(eu.darken.sdmse.common.R.string.general_cancel_action) { _, _ -> }
}.show()
}
true
}

super.onViewCreated(view, savedInstanceState)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ import eu.darken.sdmse.common.SDMId
import eu.darken.sdmse.common.SingleLiveEvent
import eu.darken.sdmse.common.coroutine.DispatcherProvider
import eu.darken.sdmse.common.debug.logging.log
import eu.darken.sdmse.common.debug.logging.logTag
import eu.darken.sdmse.common.debug.recorder.core.RecorderModule
import eu.darken.sdmse.common.flow.replayingShare
import eu.darken.sdmse.common.uix.ViewModel3
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import javax.inject.Inject

Expand All @@ -23,17 +26,79 @@ class SupportViewModel @Inject constructor(

val isRecording = recorderModule.state.map { it.isRecording }.asLiveData2()

private val debugLogFolderStatsInternal = MutableStateFlow(DebugLogFolderStats())
val debugLogFolderStats = debugLogFolderStatsInternal
.replayingShare(vmScope)
.asLiveData2()

data class DebugLogFolderStats(
val fileCount: Int = 0,
val totalSizeBytes: Long = 0L,
)

init {
launch {
refreshDebugLogFolderStats()
}
}

fun copyInstallID() = launch {
clipboardEvent.postValue(sdmId.id)
}

fun startDebugLog() = launch {
log { "startDebugLog()" }
log(TAG) { "startDebugLog()" }
recorderModule.startRecorder()
}

fun stopDebugLog() = launch {
log { "stopDebugLog()" }
log(TAG) { "stopDebugLog()" }
recorderModule.stopRecorder()
}

fun refreshDebugLogFolderStats() = launch {
log(TAG) { "refreshDebugLogFolderStats()" }

val logDirs = recorderModule.getLogDirectories()
var fileCount = 0
var totalSize = 0L

logDirs.forEach { logDir ->
logDir.walkTopDown()
.filter { it.isFile }
.forEach { file ->
fileCount++
totalSize += file.length()
}
}

debugLogFolderStatsInternal.value = DebugLogFolderStats(
fileCount = fileCount,
totalSizeBytes = totalSize,
)

log(TAG) { "Debug log folder stats: $fileCount files, $totalSize bytes" }
}

fun deleteAllDebugLogs() = launch {
log(TAG) { "deleteAllDebugLogs()" }

val logDirs = recorderModule.getLogDirectories()
logDirs.forEach { logDir ->
logDir.listFiles()?.forEach { sessionDir ->
try {
sessionDir.deleteRecursively()
log(TAG) { "Deleted debug log session: ${sessionDir.name}" }
} catch (e: Exception) {
log(TAG) { "Failed to delete debug log session: ${sessionDir.name} - ${e.message}" }
}
}
}

refreshDebugLogFolderStats()
}

companion object {
private val TAG = logTag("Support", "ViewModel")
}
}
5 changes: 5 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@
<string name="support_installid_desc">Automatic error reports are anonymous. Share your install ID if the developer needs to find your error reports.</string>
<string name="support_debuglog_label">Debug log</string>
<string name="support_debuglog_desc">Record everything the app is doing into a text file that you can share.</string>
<string name="support_debuglog_folder_label">Debug log folder</string>
<string name="support_debuglog_folder_desc">%1$d files using %2$s</string>
<string name="support_debuglog_folder_empty_desc">No debug logs stored</string>
<string name="support_debuglog_folder_delete_confirmation_title">Delete all debug logs?</string>
<string name="support_debuglog_folder_delete_confirmation_message">This will permanently delete all stored debug log sessions. This action cannot be undone.</string>
<string name="discord_label">Discord</string>
<string name="discord_description">A place to hang out in and ask questions.</string>

Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/xml/file_provider_paths.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<cache-path
name="debug_logs"
path="debug/logs" />
<external-cache-path
<external-files-path
name="debug_logs"
path="debug/logs" />
<root-path
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/xml/preferences_support.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,10 @@
android:key="support.debuglog"
android:summary="@string/support_debuglog_desc"
android:title="@string/support_debuglog_label" />
<Preference
android:icon="@drawable/ic_baseline_folder_open_24"
android:key="support.debuglog.folder"
android:summary="@string/support_debuglog_folder_empty_desc"
android:title="@string/support_debuglog_folder_label" />
</PreferenceCategory>
</PreferenceScreen>