Skip to content

Commit

Permalink
Record debug logs without foreground service
Browse files Browse the repository at this point in the history
Some overhaul of log recording to get rid of the `FOREGROUND_SERVICE_SPECIAL_USE` which is a headache with Google
  • Loading branch information
d4rken committed Sep 23, 2024
1 parent 0ee7915 commit 3eaf993
Show file tree
Hide file tree
Showing 44 changed files with 97 additions and 245 deletions.
9 changes: 0 additions & 9 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />

<uses-feature
android:name="android.hardware.bluetooth_le"
Expand Down Expand Up @@ -77,14 +76,6 @@
android:name=".common.debug.recording.ui.RecorderActivity"
android:theme="@style/AppThemeFloating" />

<service
android:name=".common.debug.recording.core.RecorderService"
android:foregroundServiceType="specialUse">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="Users can record a debug log to help developers with bugs. The debug log recording is controlled via notifications and hosted in a foreground service." />
</service>

<!-- Worker stuff-->
<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.debug.recording.ui.RecorderActivity
import eu.darken.capod.common.flow.DynamicStateFlow
import eu.darken.capod.common.startServiceCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.plus
import java.io.File
import javax.inject.Inject
Expand Down Expand Up @@ -55,8 +58,6 @@ class RecorderModule @Inject constructor(
newRecorder.start(createRecordingFilePath())
triggerFile.createNewFile()

context.startServiceCompat(Intent(context, RecorderService::class.java))

log(TAG, INFO) { "Build.Fingerprint: ${Build.FINGERPRINT}" }
log(TAG, INFO) { "BuildConfig.Versions: ${BuildConfigWrap.VERSION_DESCRIPTION_LONG}" }

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class RecorderActivityVM @Inject constructor(
}


val chooserIntent = Intent.createChooser(intent, context.getString(R.string.debug_debuglog_file_label))
val chooserIntent = Intent.createChooser(intent, context.getString(R.string.support_debuglog_label))
shareEvent.postValue(chooserIntent)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package eu.darken.capod.common.debug.recording.ui

import android.content.Context
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.darken.capod.R
import eu.darken.capod.common.PrivacyPolicy
import eu.darken.capod.common.WebpageTool

class RecorderConsentDialog(
private val context: Context,
private val webpageTool: WebpageTool
) {
fun showDialog(onStartRecord: () -> Unit) {
MaterialAlertDialogBuilder(context).apply {
setTitle(R.string.support_debuglog_label)
setMessage(R.string.settings_debuglog_explanation)
setPositiveButton(R.string.debug_debuglog_record_action) { _, _ -> onStartRecord() }
setNegativeButton(R.string.general_cancel_action) { _, _ -> }
setNeutralButton(R.string.settings_privacy_policy_label) { _, _ ->
webpageTool.open(PrivacyPolicy.URL)
}
}.show()
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
package eu.darken.capod.main.ui.settings.general.debug

import android.os.Bundle
import android.view.View
import androidx.annotation.Keep
import androidx.fragment.app.viewModels
import androidx.preference.Preference
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.R
import eu.darken.capod.common.debug.DebugSettings
import eu.darken.capod.common.observe2
import eu.darken.capod.common.uix.PreferenceFragment3
import javax.inject.Inject

Expand All @@ -25,17 +21,4 @@ class DebugSettingsFragment : PreferenceFragment3() {

override val preferenceFile: Int = R.xml.preferences_debug

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val logPref = findPreference<Preference>("debug.log.record")!!
vm.state.observe2(this) {
logPref.summary = it.currentLogPath?.path
}
logPref.setOnPreferenceClickListener {
vm.toggleRecorder()
true
}

super.onViewCreated(view, savedInstanceState)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,20 @@ import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.debug.DebugSettings
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.debug.recording.core.RecorderModule
import eu.darken.capod.common.uix.ViewModel3
import eu.darken.capod.main.core.GeneralSettings
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.onEach
import javax.inject.Inject

@HiltViewModel
class DebugSettingsFragmentVM @Inject constructor(
private val handle: SavedStateHandle,
dispatcherProvider: DispatcherProvider,
private val recorderModule: RecorderModule,
private val generalSettings: GeneralSettings,
private val debugSettings: DebugSettings,
) : ViewModel3(dispatcherProvider) {

val state = recorderModule.state.asLiveData2()

init {
debugSettings.showUnfiltered.flow
.distinctUntilChanged()
Expand All @@ -37,13 +32,6 @@ class DebugSettingsFragmentVM @Inject constructor(
.launchInViewModel()
}

fun toggleRecorder() = launch {
if (recorderModule.state.first().isRecording) {
recorderModule.stopRecorder()
} else {
recorderModule.startRecorder()
}
}

companion object {
private val TAG = logTag("Settings", "Debug", "VM")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import android.os.Bundle
import android.view.View
import androidx.annotation.Keep
import androidx.fragment.app.viewModels
import com.google.android.material.snackbar.Snackbar
import androidx.preference.Preference
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.R
import eu.darken.capod.common.ClipboardHelper
import eu.darken.capod.common.WebpageTool
import eu.darken.capod.common.debug.recording.ui.RecorderConsentDialog
import eu.darken.capod.common.observe2
import eu.darken.capod.common.uix.PreferenceFragment3
import eu.darken.capod.main.core.GeneralSettings
Expand All @@ -24,17 +25,36 @@ class SupportFragment : PreferenceFragment3() {

override val settings: GeneralSettings by lazy { generalSettings }

@Inject lateinit var clipboardHelper: ClipboardHelper
@Inject lateinit var webpageTool: WebpageTool

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

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
vm.clipboardEvent.observe2(this) { installId ->
Snackbar.make(requireView(), installId, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.general_copy_action) {
clipboardHelper.copyToClipboard(installId)
vm.recorderState.observe2(this) { state ->
debugLogPref.setIcon(
if (state.isRecording) R.drawable.ic_cancel
else R.drawable.ic_baseline_bug_report_24
)
debugLogPref.setTitle(
if (state.isRecording) R.string.debug_debuglog_stop_action
else R.string.debug_debuglog_record_action
)
debugLogPref.summary = when {
state.isRecording -> state.currentLogPath?.path
else -> getString(R.string.debug_debuglog_record_action)
}

debugLogPref.setOnPreferenceClickListener {
if (state.isRecording) {
vm.stopDebugLog()
} else {
RecorderConsentDialog(requireContext(), webpageTool).showDialog {
vm.startDebugLog()
}
}
.show()
true
}
}

super.onViewCreated(view, savedInstanceState)
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
package eu.darken.capod.main.ui.settings.support

import androidx.lifecycle.SavedStateHandle
import dagger.hilt.android.lifecycle.HiltViewModel
import eu.darken.capod.common.InstallId
import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.livedata.SingleLiveEvent
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.recording.core.RecorderModule
import eu.darken.capod.common.uix.ViewModel3
import javax.inject.Inject

@HiltViewModel
class SupportFragmentVM @Inject constructor(
private val handle: SavedStateHandle,
private val dispatcherProvider: DispatcherProvider,
private val installId: InstallId,
private val recorderModule: RecorderModule,
) : ViewModel3(dispatcherProvider) {

val clipboardEvent = SingleLiveEvent<String>()
val recorderState = recorderModule.state.asLiveData2()

fun copyInstallID() = launch {
clipboardEvent.postValue(installId.id)
fun startDebugLog() = launch {
log(TAG) { "startDebugLog()" }
recorderModule.startRecorder()
}

fun stopDebugLog() = launch {
log(TAG) { "stopDebugLog()" }
recorderModule.stopRecorder()
}
}
10 changes: 10 additions & 0 deletions app/src/main/res/drawable/ic_cancel.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z" />
</vector>
2 changes: 0 additions & 2 deletions app/src/main/res/values-ar/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@
<string name="debug_debuglog_size_label">الحجم</string>
<string name="debug_debuglog_size_compressed_label">الحجم المضغوط</string>
<string name="debug_notification_channel_label">اشعارات التصحيح</string>
<string name="debug_debuglog_file_label">ملف مسجل</string>
<string name="debug_debuglog_record_action">ملف التصحيح مسجل</string>
<string name="settings_support_installid_label">مُعرف التثبيت</string>
<string name="settings_support_installid_desc">تقارير الأخطاء والمشاكل التلقائية تكون مجهول المصدر. شارك مُعرف التثبيت الخاص بك إذا احتاج المطور إلى العثور على الأخطاء والمشاكل الخاصة بك.</string>
<string name="settings_support_label">الدعم</string>
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/res/values-az/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@
<string name="debug_debuglog_size_label">Həcm</string>
<string name="debug_debuglog_size_compressed_label">Sıxışdırılmış həcm</string>
<string name="debug_notification_channel_label">Sazlama bildirişləri</string>
<string name="debug_debuglog_file_label">Yazılmış jurnal faylı</string>
<string name="debug_debuglog_record_action">Sazlama jurnalına yaz</string>
<string name="settings_support_installid_label">Quraşdırma kimliyi</string>
<string name="settings_support_installid_desc">Avtomatik xəta hesabatları anonimdir. Tərtibatçının xəta hesabatlarınızı tapmasına ehtiyacı varsa quraşdırma kimliyinizi paylaşın.</string>
<string name="settings_support_label">Dəstək</string>
Expand Down
Loading

0 comments on commit 3eaf993

Please sign in to comment.