Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Viewing LogFox exported logs in app #30

Merged
merged 7 commits into from
Oct 1, 2023
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
24 changes: 23 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,29 @@
android:theme="@style/Theme.LogFox"
android:localeConfig="@xml/locales_config">

<activity android:name=".ui.activity.MainActivity" android:exported="true">
<activity
android:name=".ui.activity.MainActivity"
android:launchMode="singleTop"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.EDIT" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="content"/>
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.log" />
<data android:pathPattern=".*\\..*\\.log" />
<data android:pathPattern=".*\\..*\\..*\\.log" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.log" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.log" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.log" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.log" />
<data android:host="*" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.f0x1d.logfox.di.viewmodel

import android.content.Intent
import android.net.Uri
import androidx.lifecycle.SavedStateHandle
import androidx.navigation.NavController
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped
import javax.inject.Qualifier

@Module
@InstallIn(ViewModelComponent::class)
object LogsViewModelModule {

@Provides
@ViewModelScoped
@FileUri
fun provideDeepLinkIntent(savedStateHandle: SavedStateHandle) = savedStateHandle.get<Intent>(
NavController.KEY_DEEP_LINK_INTENT
)?.data ?: savedStateHandle.get<Uri>("file_uri")
}

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class FileUri
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.f0x1d.logfox.R
import com.f0x1d.logfox.repository.logging.LoggingRepository
import com.f0x1d.logfox.service.LoggingService
Expand Down Expand Up @@ -75,7 +74,7 @@ fun Context.shareIntent(text: String) = baseShareIntent {
}

fun Context.shareFileIntent(file: File) = baseShareIntent {
val uri = FileProvider.getUriForFile(this, "com.f0x1d.logfox.provider", file)
val uri = file.asUri(this)

it.putExtra(Intent.EXTRA_STREAM, uri)
it.type = "text/plain"
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/java/com/f0x1d/logfox/extensions/FileExtensions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.f0x1d.logfox.extensions

import android.content.Context
import androidx.core.content.FileProvider
import com.f0x1d.logfox.BuildConfig
import java.io.File

fun File.asUri(context: Context) = FileProvider.getUriForFile(
context,
"${BuildConfig.APPLICATION_ID}.provider",
this
)
49 changes: 49 additions & 0 deletions app/src/main/java/com/f0x1d/logfox/extensions/UriExtensions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.f0x1d.logfox.extensions

import android.content.Context
import android.net.Uri
import androidx.documentfile.provider.DocumentFile
import com.f0x1d.logfox.extensions.logline.LogLine
import com.f0x1d.logfox.model.LogLine
import com.f0x1d.logfox.utils.preferences.AppPreferences
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn

fun Uri?.readFileContentsAsFlow(context: Context, appPreferences: AppPreferences) = flow<List<LogLine>> {
val uri = this@readFileContentsAsFlow

if (uri == null) {
emit(emptyList())
} else {
val logsDisplayLimit = appPreferences.logsDisplayLimit

context.contentResolver.openInputStream(uri)?.use {
it.bufferedReader().useLines { lines ->
var id = -1L
val logLines = mutableListOf<LogLine>()

for (line in lines) {
val logLine = LogLine(id, line, context.packageManager) ?: LogLine(
id = id,
content = line,
original = line
)

logLines.add(logLine)
if (logLines.size >= logsDisplayLimit)
break

id -= 1
}

emit(logLines)
}
} ?: emit(emptyList())
}
}.flowOn(Dispatchers.IO).catch {
emit(emptyList())
}

fun Uri.readFileName(context: Context) = DocumentFile.fromSingleUri(context, this)?.name
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.f0x1d.logfox.model.LogLevel
import com.f0x1d.logfox.model.LogLine
import com.f0x1d.logfox.utils.UIDS

private val logRegex = "(.{23}) (.{1,5}) (.{1,5}) (.{1,5}) (.) (.+?): (.+)".toRegex()
private val logRegex = "(.{14}) (.{1,5}) (.{1,5}) (.{1,5}) (.) (.+?): (.+)".toRegex()
// time, uid, pid, tid, level, tag, message

private val uidsCache = LruCache<String, String>(200)
Expand All @@ -15,7 +15,7 @@ fun LogLine(
id: Long,
line: String,
packageManager: PackageManager
) = logRegex.find(line)?.run {
) = logRegex.find(line.trim())?.run {
val uid = groupValues[2].replace(" ", "")
val integerUid = uid.toIntOrNull() ?: UIDS.MAPPINGS[uid]

Expand All @@ -38,7 +38,8 @@ fun LogLine(
packageName,
mapLevel(groupValues[5]),
groupValues[6].trim(),
groupValues[7]
groupValues[7],
groupValues[0]
)
}

Expand Down
29 changes: 9 additions & 20 deletions app/src/main/java/com/f0x1d/logfox/model/LogLine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,19 @@ import com.f0x1d.logfox.extensions.logsTimeFormatted

data class LogLine(
val id: Long,
val dateAndTime: Long,
val uid: String,
val pid: String,
val tid: String,
val packageName: String?,
val level: LogLevel,
val tag: String,
val dateAndTime: Long = System.currentTimeMillis(),
val uid: String = "",
val pid: String = "",
val tid: String = "",
val packageName: String? = null,
val level: LogLevel = LogLevel.INFO,
val tag: String = "",
val content: String,
val original: String,

val logsDateFormatted: String = dateAndTime.logsDateFormatted,
val logsTimeFormatted: String = dateAndTime.logsTimeFormatted
) {
val original = buildString {
append("$logsDateFormatted ")
append("$logsTimeFormatted ")
append("$uid ")
append("$pid ")
append("$tid ")
if (packageName != null)
append("$packageName ")
append("${level.letter}/$tag: ")
append(content)
}
}
)

@Keep
enum class LogLevel(val letter: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class RecordingsRepository @Inject constructor(

recordingTime = System.currentTimeMillis()
fileMutex.withLock {
recordingFile = File(recordingDir, "${recordingTime.exportFormatted}.txt").apply {
recordingFile = File(recordingDir, "${recordingTime.exportFormatted}.log").apply {
createNewFile()
}
}
Expand Down
8 changes: 3 additions & 5 deletions app/src/main/java/com/f0x1d/logfox/service/LoggingService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import com.f0x1d.logfox.LogFoxApp
import com.f0x1d.logfox.R
import com.f0x1d.logfox.extensions.EXIT_APP_INTENT_ID
import com.f0x1d.logfox.extensions.STOP_LOGGING_SERVICE_INTENT_ID
import com.f0x1d.logfox.extensions.activityManager
import com.f0x1d.logfox.extensions.makeOpenAppPendingIntent
import com.f0x1d.logfox.extensions.makeServicePendingIntent
import com.f0x1d.logfox.repository.logging.LoggingRepository
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.update
import javax.inject.Inject
import kotlin.system.exitProcess

@AndroidEntryPoint
class LoggingService: Service() {
Expand Down Expand Up @@ -68,11 +68,9 @@ class LoggingService: Service() {

private fun killApp() {
loggingRepository.stopLogging()
activityManager.appTasks.forEach {
it.finishAndRemoveTask()
}

stopService()

exitProcess(0)
}

override fun onBind(p0: Intent?) = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.f0x1d.logfox.ui.activity

import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
Expand Down Expand Up @@ -76,6 +77,11 @@ class MainActivity: BaseViewModelActivity<MainViewModel, ActivityMainBinding>(),
binding.bottomNavigation.visibility = if (barShown) View.VISIBLE else View.GONE
}

override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
navController.handleDeepLink(intent)
}

override fun onDestroy() {
super.onDestroy()
navController.removeOnDestinationChangedListener(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.viewModels
import androidx.lifecycle.asLiveData
import androidx.navigation.fragment.findNavController
import com.f0x1d.logfox.NavGraphDirections
import com.f0x1d.logfox.databinding.SheetRecordingBinding
import com.f0x1d.logfox.extensions.asUri
import com.f0x1d.logfox.extensions.exportFormatted
import com.f0x1d.logfox.extensions.logToZip
import com.f0x1d.logfox.extensions.shareFileIntent
Expand All @@ -31,7 +34,8 @@ class RecordingBottomSheet: BaseViewModelBottomSheet<RecordingViewModel, SheetRe
viewModel.recording.value ?: return@registerForActivityResult
)
}
private val logExportLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) {
// no plain because android will append .txt itself
private val logExportLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("text/*")) {
viewModel.exportFile(it ?: return@registerForActivityResult)
}

Expand All @@ -45,8 +49,13 @@ class RecordingBottomSheet: BaseViewModelBottomSheet<RecordingViewModel, SheetRe

binding.timeText.text = logRecording.dateAndTime.toLocaleString()

binding.viewLayout.setOnClickListener {
findNavController().navigate(NavGraphDirections.actionGlobalLogsFragment(
File(logRecording.file).asUri(requireContext())
))
}
binding.exportLayout.setOnClickListener {
logExportLauncher.launch("${logRecording.dateAndTime.exportFormatted}.txt")
logExportLauncher.launch("${logRecording.dateAndTime.exportFormatted}.log")
}
binding.shareLayout.setOnClickListener {
requireContext().shareFileIntent(File(logRecording.file))
Expand Down
36 changes: 36 additions & 0 deletions app/src/main/java/com/f0x1d/logfox/ui/fragment/LogsFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback
import androidx.hilt.navigation.fragment.hiltNavGraphViewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
Expand All @@ -13,6 +14,7 @@ import com.f0x1d.logfox.R
import com.f0x1d.logfox.adapter.LogsAdapter
import com.f0x1d.logfox.databinding.FragmentLogsBinding
import com.f0x1d.logfox.extensions.copyText
import com.f0x1d.logfox.extensions.readFileName
import com.f0x1d.logfox.extensions.sendKillApp
import com.f0x1d.logfox.extensions.sendStopService
import com.f0x1d.logfox.extensions.setClickListenerOn
Expand All @@ -36,6 +38,12 @@ class LogsFragment: BaseViewModelFragment<LogsViewModel, FragmentLogsBinding>(),
}
private var changingState = false

private val stopViewingFileBackPressedCallback = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
viewModel.stopViewingFile()
}
}

override fun inflateBinding(inflater: LayoutInflater, container: ViewGroup?) = FragmentLogsBinding.inflate(inflater, container, false)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Expand Down Expand Up @@ -105,6 +113,29 @@ class LogsFragment: BaseViewModelFragment<LogsViewModel, FragmentLogsBinding>(),
scrollLogToBottom()
}

viewModel.viewingFileData.observe(viewLifecycleOwner) {
stopViewingFileBackPressedCallback.isEnabled = it

binding.toolbar.apply {
menu.findItem(R.id.pause_item).isVisible = !it

title = when (it) {
true -> viewModel.fileUri?.readFileName(requireContext())

else -> getString(R.string.app_name)
}

if (it) setNavigationIcon(R.drawable.ic_clear)
else {
navigationIcon = null
}

setNavigationOnClickListener {
viewModel.stopViewingFile()
}
}
}

viewModel.logs.observe(viewLifecycleOwner) {
adapter.submitList(null)
adapter.submitList(it ?: return@observe) {
Expand All @@ -127,6 +158,11 @@ class LogsFragment: BaseViewModelFragment<LogsViewModel, FragmentLogsBinding>(),
viewModel.serviceRunningData.observe(viewLifecycleOwner) { running ->
binding.toolbar.menu.findItem(R.id.service_status_item).setTitle(if (running) R.string.stop_service else R.string.start_service)
}

requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
stopViewingFileBackPressedCallback
)
}

override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/com/f0x1d/logfox/utils/ExportImportUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ fun OutputStream.exportFilters(context: Context, filters: List<UserFilter>) = us
}

fun OutputStream.exportCrashToZip(context: Context, log: String) = exportToZip(context) {
putZipEntry("log.txt", log.encodeToByteArray())
putZipEntry("crash.log", log.encodeToByteArray())
}

fun OutputStream.exportLogToZip(context: Context, logFile: String) = exportToZip(context) {
putZipEntry("log.txt", File(logFile))
putZipEntry("crash.log", File(logFile))
}

private fun OutputStream.exportToZip(context: Context, block: ZipOutputStream.() -> Unit) = use {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class FontsInterceptor(context: Context): Interceptor {
R.id.share_text to boldSansTypeface,
R.id.zip_text to boldSansTypeface,
R.id.export_text to boldSansTypeface,
R.id.view_text to boldSansTypeface,
R.id.including_button to boldSansTypeface
)

Expand Down
Loading
Loading