From dd0d9e8786268800ff2c8e8b78ffca921c8ab88e Mon Sep 17 00:00:00 2001 From: F0x1d Date: Sun, 1 Oct 2023 00:24:56 +0300 Subject: [PATCH 1/7] [feat]: viewing LogFox exported logs in app --- app/src/main/AndroidManifest.xml | 13 ++++++- .../di/viewmodel/LogsViewModelModule.kt | 27 +++++++++++++ .../logfox/extensions/IntentExtensions.kt | 39 +++++++++++++++++++ .../extensions/logline/LogLineExtensions.kt | 7 ++-- .../java/com/f0x1d/logfox/model/LogLine.kt | 29 +++++--------- .../f0x1d/logfox/ui/activity/MainActivity.kt | 6 +++ .../f0x1d/logfox/ui/fragment/LogsFragment.kt | 34 ++++++++++++++++ .../f0x1d/logfox/viewmodel/LogsViewModel.kt | 32 ++++++++++++++- app/src/main/res/navigation/logs.xml | 5 +++ build.gradle | 4 +- 10 files changed, 168 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/com/f0x1d/logfox/di/viewmodel/LogsViewModelModule.kt create mode 100644 app/src/main/java/com/f0x1d/logfox/extensions/IntentExtensions.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cabb694f..1d4162c3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,7 +18,18 @@ android:theme="@style/Theme.LogFox" android:localeConfig="@xml/locales_config"> - + + + + + + + + + diff --git a/app/src/main/java/com/f0x1d/logfox/di/viewmodel/LogsViewModelModule.kt b/app/src/main/java/com/f0x1d/logfox/di/viewmodel/LogsViewModelModule.kt new file mode 100644 index 00000000..fd182b71 --- /dev/null +++ b/app/src/main/java/com/f0x1d/logfox/di/viewmodel/LogsViewModelModule.kt @@ -0,0 +1,27 @@ +package com.f0x1d.logfox.di.viewmodel + +import android.content.Intent +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 + @DeepLinkIntent + fun provideDeepLinkIntent(savedStateHandle: SavedStateHandle) = savedStateHandle.get( + NavController.KEY_DEEP_LINK_INTENT + ) +} + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class DeepLinkIntent \ No newline at end of file diff --git a/app/src/main/java/com/f0x1d/logfox/extensions/IntentExtensions.kt b/app/src/main/java/com/f0x1d/logfox/extensions/IntentExtensions.kt new file mode 100644 index 00000000..4c9b013d --- /dev/null +++ b/app/src/main/java/com/f0x1d/logfox/extensions/IntentExtensions.kt @@ -0,0 +1,39 @@ +package com.f0x1d.logfox.extensions + +import android.content.Context +import android.content.Intent +import androidx.documentfile.provider.DocumentFile +import com.f0x1d.logfox.extensions.logline.LogLine +import com.f0x1d.logfox.model.LogLine +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn + +fun Intent?.readFileContentsAsFlow(context: Context) = flow { + val intentData = this@readFileContentsAsFlow?.data + + if (intentData == null) { + emit(null) + } else { + context.contentResolver.openInputStream(intentData)?.use { + it.bufferedReader().useLines { lines -> + val logLines = lines.mapIndexed { index, line -> + // reversing ids not to mess with device logs + val id = -index.toLong() - 1 + + LogLine(id, line, context.packageManager) ?: LogLine( + id = id, + content = line, + original = line + ) + }.toList() + + emit(logLines) + } + } ?: emit(null) + } +}.flowOn(Dispatchers.IO) + +fun Intent.readFileName(context: Context): String? { + return DocumentFile.fromSingleUri(context, data ?: return null)?.name +} \ No newline at end of file diff --git a/app/src/main/java/com/f0x1d/logfox/extensions/logline/LogLineExtensions.kt b/app/src/main/java/com/f0x1d/logfox/extensions/logline/LogLineExtensions.kt index a91f0c92..13e572ed 100644 --- a/app/src/main/java/com/f0x1d/logfox/extensions/logline/LogLineExtensions.kt +++ b/app/src/main/java/com/f0x1d/logfox/extensions/logline/LogLineExtensions.kt @@ -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(200) @@ -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] @@ -38,7 +38,8 @@ fun LogLine( packageName, mapLevel(groupValues[5]), groupValues[6].trim(), - groupValues[7] + groupValues[7], + groupValues[0] ) } diff --git a/app/src/main/java/com/f0x1d/logfox/model/LogLine.kt b/app/src/main/java/com/f0x1d/logfox/model/LogLine.kt index feeaf60f..2b1c847c 100644 --- a/app/src/main/java/com/f0x1d/logfox/model/LogLine.kt +++ b/app/src/main/java/com/f0x1d/logfox/model/LogLine.kt @@ -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) { diff --git a/app/src/main/java/com/f0x1d/logfox/ui/activity/MainActivity.kt b/app/src/main/java/com/f0x1d/logfox/ui/activity/MainActivity.kt index 4f5c03dd..6b964c4b 100644 --- a/app/src/main/java/com/f0x1d/logfox/ui/activity/MainActivity.kt +++ b/app/src/main/java/com/f0x1d/logfox/ui/activity/MainActivity.kt @@ -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 @@ -76,6 +77,11 @@ class MainActivity: BaseViewModelActivity(), 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) diff --git a/app/src/main/java/com/f0x1d/logfox/ui/fragment/LogsFragment.kt b/app/src/main/java/com/f0x1d/logfox/ui/fragment/LogsFragment.kt index 1aeca4ee..40219b31 100644 --- a/app/src/main/java/com/f0x1d/logfox/ui/fragment/LogsFragment.kt +++ b/app/src/main/java/com/f0x1d/logfox/ui/fragment/LogsFragment.kt @@ -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 @@ -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 @@ -36,6 +38,12 @@ class LogsFragment: BaseViewModelFragment(), } 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?) { @@ -105,6 +113,27 @@ class LogsFragment: BaseViewModelFragment(), scrollLogToBottom() } + viewModel.viewingFileData.observe(viewLifecycleOwner) { + binding.toolbar.apply { + stopViewingFileBackPressedCallback.isEnabled = it + + menu.findItem(R.id.pause_item).isVisible = !it + + title = if (it) + viewModel.deepLinkIntent?.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) { @@ -127,6 +156,11 @@ class LogsFragment: BaseViewModelFragment(), 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?) { diff --git a/app/src/main/java/com/f0x1d/logfox/viewmodel/LogsViewModel.kt b/app/src/main/java/com/f0x1d/logfox/viewmodel/LogsViewModel.kt index df78bd51..52a75765 100644 --- a/app/src/main/java/com/f0x1d/logfox/viewmodel/LogsViewModel.kt +++ b/app/src/main/java/com/f0x1d/logfox/viewmodel/LogsViewModel.kt @@ -1,10 +1,13 @@ package com.f0x1d.logfox.viewmodel import android.app.Application +import android.content.Intent import androidx.lifecycle.asLiveData import com.f0x1d.logfox.database.AppDatabase import com.f0x1d.logfox.database.entity.UserFilter +import com.f0x1d.logfox.di.viewmodel.DeepLinkIntent import com.f0x1d.logfox.extensions.logline.filterAndSearch +import com.f0x1d.logfox.extensions.readFileContentsAsFlow import com.f0x1d.logfox.model.LogLine import com.f0x1d.logfox.repository.logging.LoggingRepository import com.f0x1d.logfox.utils.preferences.AppPreferences @@ -22,6 +25,7 @@ import javax.inject.Inject @HiltViewModel class LogsViewModel @Inject constructor( + @DeepLinkIntent val deepLinkIntent: Intent?, private val database: AppDatabase, private val loggingRepository: LoggingRepository, val appPreferences: AppPreferences, @@ -33,13 +37,31 @@ class LogsViewModel @Inject constructor( val paused = MutableStateFlow(false) val pausedData = paused.asLiveData() + val viewingFile = MutableStateFlow(deepLinkIntent != null) + val viewingFileData = viewingFile.asLiveData() + + @Suppress("UNCHECKED_CAST") val logs = combine( loggingRepository.logsFlow, + deepLinkIntent.readFileContentsAsFlow(ctx), + viewingFile, database.userFilterDao().getAllAsFlow(), query, paused - ) { logs, filters, query, paused -> - LogsData(logs, filters, query, paused) + ) { values -> + val logs = values[0] as List + val fileLogs = values[1] as List? + val viewingFile = values[2] as Boolean + val filters = values[3] as List + val query = values[4] as String? + val paused = values[5] as Boolean + + val resultLogs = when { + viewingFile -> fileLogs ?: logs + else -> logs + } + + LogsData(resultLogs, filters, query, paused) }.scan(null as LogsData?) { accumulator, data -> when { !data.paused -> data @@ -63,6 +85,12 @@ class LogsViewModel @Inject constructor( val resumeLoggingWithBottomTouch get() = appPreferences.resumeLoggingWithBottomTouch + fun stopViewingFile() = viewingFile.apply { + if (value) update { + false + } + } + fun query(query: String?) = this.query.update { query } fun clearLogs() = loggingRepository.clearLogs() diff --git a/app/src/main/res/navigation/logs.xml b/app/src/main/res/navigation/logs.xml index 2e8a2b0a..b8ace580 100644 --- a/app/src/main/res/navigation/logs.xml +++ b/app/src/main/res/navigation/logs.xml @@ -8,6 +8,11 @@ android:id="@+id/logsFragment" android:name="com.f0x1d.logfox.ui.fragment.LogsFragment" android:label="LogsFragment" > + + Date: Sun, 1 Oct 2023 12:48:28 +0300 Subject: [PATCH 2/7] [feat]: viewing LogFox recorded logs in app --- app/src/main/AndroidManifest.xml | 2 +- .../di/viewmodel/LogsViewModelModule.kt | 7 +-- .../logfox/extensions/ContextExtensions.kt | 3 +- .../f0x1d/logfox/extensions/FileExtensions.kt | 12 +++++ .../logfox/extensions/IntentExtensions.kt | 39 ---------------- .../f0x1d/logfox/extensions/UriExtensions.kt | 46 +++++++++++++++++++ .../f0x1d/logfox/service/LoggingService.kt | 8 ++-- .../logfox/ui/dialog/RecordingBottomSheet.kt | 8 ++++ .../f0x1d/logfox/ui/fragment/LogsFragment.kt | 6 +-- .../f0x1d/logfox/viewmodel/LogsViewModel.kt | 17 ++++--- app/src/main/res/drawable/ic_eye.xml | 5 ++ app/src/main/res/layout/sheet_recording.xml | 44 +++++++++++++++++- app/src/main/res/navigation/logs.xml | 6 +++ app/src/main/res/navigation/nav_graph.xml | 10 ++++ app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 16 files changed, 154 insertions(+), 61 deletions(-) create mode 100644 app/src/main/java/com/f0x1d/logfox/extensions/FileExtensions.kt delete mode 100644 app/src/main/java/com/f0x1d/logfox/extensions/IntentExtensions.kt create mode 100644 app/src/main/java/com/f0x1d/logfox/extensions/UriExtensions.kt create mode 100644 app/src/main/res/drawable/ic_eye.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d4162c3..ed9fefb5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,7 +20,7 @@ diff --git a/app/src/main/java/com/f0x1d/logfox/di/viewmodel/LogsViewModelModule.kt b/app/src/main/java/com/f0x1d/logfox/di/viewmodel/LogsViewModelModule.kt index fd182b71..0753977c 100644 --- a/app/src/main/java/com/f0x1d/logfox/di/viewmodel/LogsViewModelModule.kt +++ b/app/src/main/java/com/f0x1d/logfox/di/viewmodel/LogsViewModelModule.kt @@ -1,6 +1,7 @@ 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 @@ -16,12 +17,12 @@ object LogsViewModelModule { @Provides @ViewModelScoped - @DeepLinkIntent + @FileUri fun provideDeepLinkIntent(savedStateHandle: SavedStateHandle) = savedStateHandle.get( NavController.KEY_DEEP_LINK_INTENT - ) + )?.data ?: savedStateHandle.get("file_uri") } @Qualifier @Retention(AnnotationRetention.BINARY) -annotation class DeepLinkIntent \ No newline at end of file +annotation class FileUri \ No newline at end of file diff --git a/app/src/main/java/com/f0x1d/logfox/extensions/ContextExtensions.kt b/app/src/main/java/com/f0x1d/logfox/extensions/ContextExtensions.kt index aaee930b..72b1e465 100644 --- a/app/src/main/java/com/f0x1d/logfox/extensions/ContextExtensions.kt +++ b/app/src/main/java/com/f0x1d/logfox/extensions/ContextExtensions.kt @@ -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 @@ -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" diff --git a/app/src/main/java/com/f0x1d/logfox/extensions/FileExtensions.kt b/app/src/main/java/com/f0x1d/logfox/extensions/FileExtensions.kt new file mode 100644 index 00000000..ed662c50 --- /dev/null +++ b/app/src/main/java/com/f0x1d/logfox/extensions/FileExtensions.kt @@ -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 +) \ No newline at end of file diff --git a/app/src/main/java/com/f0x1d/logfox/extensions/IntentExtensions.kt b/app/src/main/java/com/f0x1d/logfox/extensions/IntentExtensions.kt deleted file mode 100644 index 4c9b013d..00000000 --- a/app/src/main/java/com/f0x1d/logfox/extensions/IntentExtensions.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.f0x1d.logfox.extensions - -import android.content.Context -import android.content.Intent -import androidx.documentfile.provider.DocumentFile -import com.f0x1d.logfox.extensions.logline.LogLine -import com.f0x1d.logfox.model.LogLine -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.flowOn - -fun Intent?.readFileContentsAsFlow(context: Context) = flow { - val intentData = this@readFileContentsAsFlow?.data - - if (intentData == null) { - emit(null) - } else { - context.contentResolver.openInputStream(intentData)?.use { - it.bufferedReader().useLines { lines -> - val logLines = lines.mapIndexed { index, line -> - // reversing ids not to mess with device logs - val id = -index.toLong() - 1 - - LogLine(id, line, context.packageManager) ?: LogLine( - id = id, - content = line, - original = line - ) - }.toList() - - emit(logLines) - } - } ?: emit(null) - } -}.flowOn(Dispatchers.IO) - -fun Intent.readFileName(context: Context): String? { - return DocumentFile.fromSingleUri(context, data ?: return null)?.name -} \ No newline at end of file diff --git a/app/src/main/java/com/f0x1d/logfox/extensions/UriExtensions.kt b/app/src/main/java/com/f0x1d/logfox/extensions/UriExtensions.kt new file mode 100644 index 00000000..bec22835 --- /dev/null +++ b/app/src/main/java/com/f0x1d/logfox/extensions/UriExtensions.kt @@ -0,0 +1,46 @@ +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.flow +import kotlinx.coroutines.flow.flowOn + +fun Uri?.readFileContentsAsFlow(context: Context, appPreferences: AppPreferences) = flow { + val uri = this@readFileContentsAsFlow + + if (uri == null) { + emit(null) + } else { + val logsDisplayLimit = appPreferences.logsDisplayLimit + + context.contentResolver.openInputStream(uri)?.use { + it.bufferedReader().useLines { lines -> + var id = -1L + val logLines = mutableListOf() + + 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(null) + } +}.flowOn(Dispatchers.IO) + +fun Uri.readFileName(context: Context) = DocumentFile.fromSingleUri(context, this)?.name \ No newline at end of file diff --git a/app/src/main/java/com/f0x1d/logfox/service/LoggingService.kt b/app/src/main/java/com/f0x1d/logfox/service/LoggingService.kt index e7947355..143a9c5d 100644 --- a/app/src/main/java/com/f0x1d/logfox/service/LoggingService.kt +++ b/app/src/main/java/com/f0x1d/logfox/service/LoggingService.kt @@ -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() { @@ -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 diff --git a/app/src/main/java/com/f0x1d/logfox/ui/dialog/RecordingBottomSheet.kt b/app/src/main/java/com/f0x1d/logfox/ui/dialog/RecordingBottomSheet.kt index 42b1af50..f2f56a24 100644 --- a/app/src/main/java/com/f0x1d/logfox/ui/dialog/RecordingBottomSheet.kt +++ b/app/src/main/java/com/f0x1d/logfox/ui/dialog/RecordingBottomSheet.kt @@ -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 @@ -45,6 +48,11 @@ class RecordingBottomSheet: BaseViewModelBottomSheet(), } viewModel.viewingFileData.observe(viewLifecycleOwner) { - binding.toolbar.apply { - stopViewingFileBackPressedCallback.isEnabled = it + stopViewingFileBackPressedCallback.isEnabled = it + binding.toolbar.apply { menu.findItem(R.id.pause_item).isVisible = !it title = if (it) - viewModel.deepLinkIntent?.readFileName(requireContext()) + viewModel.fileUri?.readFileName(requireContext()) else getString(R.string.app_name) if (it) setNavigationIcon(R.drawable.ic_clear) diff --git a/app/src/main/java/com/f0x1d/logfox/viewmodel/LogsViewModel.kt b/app/src/main/java/com/f0x1d/logfox/viewmodel/LogsViewModel.kt index 52a75765..23fbbd91 100644 --- a/app/src/main/java/com/f0x1d/logfox/viewmodel/LogsViewModel.kt +++ b/app/src/main/java/com/f0x1d/logfox/viewmodel/LogsViewModel.kt @@ -1,11 +1,11 @@ package com.f0x1d.logfox.viewmodel import android.app.Application -import android.content.Intent +import android.net.Uri import androidx.lifecycle.asLiveData import com.f0x1d.logfox.database.AppDatabase import com.f0x1d.logfox.database.entity.UserFilter -import com.f0x1d.logfox.di.viewmodel.DeepLinkIntent +import com.f0x1d.logfox.di.viewmodel.FileUri import com.f0x1d.logfox.extensions.logline.filterAndSearch import com.f0x1d.logfox.extensions.readFileContentsAsFlow import com.f0x1d.logfox.model.LogLine @@ -25,7 +25,7 @@ import javax.inject.Inject @HiltViewModel class LogsViewModel @Inject constructor( - @DeepLinkIntent val deepLinkIntent: Intent?, + @FileUri val fileUri: Uri?, private val database: AppDatabase, private val loggingRepository: LoggingRepository, val appPreferences: AppPreferences, @@ -37,13 +37,13 @@ class LogsViewModel @Inject constructor( val paused = MutableStateFlow(false) val pausedData = paused.asLiveData() - val viewingFile = MutableStateFlow(deepLinkIntent != null) + val viewingFile = MutableStateFlow(fileUri != null) val viewingFileData = viewingFile.asLiveData() @Suppress("UNCHECKED_CAST") val logs = combine( loggingRepository.logsFlow, - deepLinkIntent.readFileContentsAsFlow(ctx), + fileUri.readFileContentsAsFlow(ctx, appPreferences), viewingFile, database.userFilterDao().getAllAsFlow(), query, @@ -54,10 +54,13 @@ class LogsViewModel @Inject constructor( val viewingFile = values[2] as Boolean val filters = values[3] as List val query = values[4] as String? - val paused = values[5] as Boolean + var paused = values[5] as Boolean val resultLogs = when { - viewingFile -> fileLogs ?: logs + viewingFile -> { + paused = false + fileLogs ?: logs + } else -> logs } diff --git a/app/src/main/res/drawable/ic_eye.xml b/app/src/main/res/drawable/ic_eye.xml new file mode 100644 index 00000000..02cc2244 --- /dev/null +++ b/app/src/main/res/drawable/ic_eye.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/sheet_recording.xml b/app/src/main/res/layout/sheet_recording.xml index fd411fa2..07a7b4b3 100644 --- a/app/src/main/res/layout/sheet_recording.xml +++ b/app/src/main/res/layout/sheet_recording.xml @@ -64,7 +64,7 @@ android:layout_height="match_parent"> + + + + + + + diff --git a/app/src/main/res/navigation/logs.xml b/app/src/main/res/navigation/logs.xml index b8ace580..9eb516f3 100644 --- a/app/src/main/res/navigation/logs.xml +++ b/app/src/main/res/navigation/logs.xml @@ -13,6 +13,12 @@ app:mimeType="text/plain" app:action="android.intent.action.VIEW" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index df0ba26f..854e302b 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -106,4 +106,5 @@ Имя пакета Приложения Вы уверены? + Просмотр \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 89fd4c2c..dc581b8f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -115,4 +115,5 @@ Package name Apps Are you sure? + View \ No newline at end of file From cafb683cb3decb8821d1634c2f249a27a8408223 Mon Sep 17 00:00:00 2001 From: F0x1d Date: Sun, 1 Oct 2023 17:26:47 +0300 Subject: [PATCH 3/7] [fix]: few small fixes --- .../f0x1d/logfox/extensions/UriExtensions.kt | 11 +++++++---- .../logfox/utils/view/FontsInterceptor.kt | 1 + .../f0x1d/logfox/viewmodel/LogsViewModel.kt | 18 ++++++++---------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/f0x1d/logfox/extensions/UriExtensions.kt b/app/src/main/java/com/f0x1d/logfox/extensions/UriExtensions.kt index bec22835..4ca1edab 100644 --- a/app/src/main/java/com/f0x1d/logfox/extensions/UriExtensions.kt +++ b/app/src/main/java/com/f0x1d/logfox/extensions/UriExtensions.kt @@ -7,14 +7,15 @@ 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 { +fun Uri?.readFileContentsAsFlow(context: Context, appPreferences: AppPreferences) = flow> { val uri = this@readFileContentsAsFlow if (uri == null) { - emit(null) + emit(emptyList()) } else { val logsDisplayLimit = appPreferences.logsDisplayLimit @@ -39,8 +40,10 @@ fun Uri?.readFileContentsAsFlow(context: Context, appPreferences: AppPreferences emit(logLines) } - } ?: emit(null) + } ?: emit(emptyList()) } -}.flowOn(Dispatchers.IO) +}.flowOn(Dispatchers.IO).catch { + emit(emptyList()) +} fun Uri.readFileName(context: Context) = DocumentFile.fromSingleUri(context, this)?.name \ No newline at end of file diff --git a/app/src/main/java/com/f0x1d/logfox/utils/view/FontsInterceptor.kt b/app/src/main/java/com/f0x1d/logfox/utils/view/FontsInterceptor.kt index 6a0c39ea..342c3ee9 100644 --- a/app/src/main/java/com/f0x1d/logfox/utils/view/FontsInterceptor.kt +++ b/app/src/main/java/com/f0x1d/logfox/utils/view/FontsInterceptor.kt @@ -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 ) diff --git a/app/src/main/java/com/f0x1d/logfox/viewmodel/LogsViewModel.kt b/app/src/main/java/com/f0x1d/logfox/viewmodel/LogsViewModel.kt index 23fbbd91..d24f4004 100644 --- a/app/src/main/java/com/f0x1d/logfox/viewmodel/LogsViewModel.kt +++ b/app/src/main/java/com/f0x1d/logfox/viewmodel/LogsViewModel.kt @@ -50,27 +50,24 @@ class LogsViewModel @Inject constructor( paused ) { values -> val logs = values[0] as List - val fileLogs = values[1] as List? + val fileLogs = values[1] as List val viewingFile = values[2] as Boolean val filters = values[3] as List val query = values[4] as String? - var paused = values[5] as Boolean + val paused = values[5] as Boolean val resultLogs = when { - viewingFile -> { - paused = false - fileLogs ?: logs - } + viewingFile -> fileLogs else -> logs } - LogsData(resultLogs, filters, query, paused) + LogsData(resultLogs, viewingFile, filters, query, paused) }.scan(null as LogsData?) { accumulator, data -> when { - !data.paused -> data + !data.paused || data.viewingFile != accumulator?.viewingFile -> data - data.query != accumulator?.query -> data.copy(logs = accumulator?.logs ?: data.logs) - data.filters != accumulator?.filters -> data.copy(logs = accumulator?.logs ?: data.logs) + data.query != accumulator.query -> data.copy(logs = accumulator.logs) + data.filters != accumulator.filters -> data.copy(logs = accumulator.logs) else -> data.copy(logs = accumulator.logs, passing = false) } @@ -111,6 +108,7 @@ class LogsViewModel @Inject constructor( data class LogsData( val logs: List, + val viewingFile: Boolean, val filters: List, val query: String?, val paused: Boolean, From a10e94d61ba46cff03f7d12e1adf8e560313c0d8 Mon Sep 17 00:00:00 2001 From: F0x1d Date: Sun, 1 Oct 2023 17:36:45 +0300 Subject: [PATCH 4/7] [fix]: wrong fragment animation --- app/src/main/res/navigation/nav_graph.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 3185680b..bd57b3b8 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -22,7 +22,11 @@ + app:destination="@id/logs" + app:enterAnim="@anim/nav_default_enter_anim" + app:exitAnim="@anim/nav_default_exit_anim" + app:popEnterAnim="@anim/nav_default_pop_enter_anim" + app:popExitAnim="@anim/nav_default_pop_exit_anim"> Date: Sun, 1 Oct 2023 19:02:10 +0300 Subject: [PATCH 5/7] [feat]: using .log files --- app/src/main/AndroidManifest.xml | 13 +++++++++++-- .../repository/logging/RecordingsRepository.kt | 2 +- .../f0x1d/logfox/ui/dialog/RecordingBottomSheet.kt | 5 +++-- .../com/f0x1d/logfox/ui/fragment/LogsFragment.kt | 8 +++++--- .../com/f0x1d/logfox/utils/ExportImportUtils.kt | 4 ++-- app/src/main/res/drawable/ic_clear.xml | 1 - app/src/main/res/navigation/logs.xml | 2 +- 7 files changed, 23 insertions(+), 12 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ed9fefb5..a24ac453 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -25,9 +25,18 @@ + - - + + + + + + + + + + diff --git a/app/src/main/java/com/f0x1d/logfox/repository/logging/RecordingsRepository.kt b/app/src/main/java/com/f0x1d/logfox/repository/logging/RecordingsRepository.kt index da3a5c21..76b0c652 100644 --- a/app/src/main/java/com/f0x1d/logfox/repository/logging/RecordingsRepository.kt +++ b/app/src/main/java/com/f0x1d/logfox/repository/logging/RecordingsRepository.kt @@ -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() } } diff --git a/app/src/main/java/com/f0x1d/logfox/ui/dialog/RecordingBottomSheet.kt b/app/src/main/java/com/f0x1d/logfox/ui/dialog/RecordingBottomSheet.kt index f2f56a24..4f0dc044 100644 --- a/app/src/main/java/com/f0x1d/logfox/ui/dialog/RecordingBottomSheet.kt +++ b/app/src/main/java/com/f0x1d/logfox/ui/dialog/RecordingBottomSheet.kt @@ -34,7 +34,8 @@ class RecordingBottomSheet: BaseViewModelBottomSheet(), binding.toolbar.apply { menu.findItem(R.id.pause_item).isVisible = !it - title = if (it) - viewModel.fileUri?.readFileName(requireContext()) - else getString(R.string.app_name) + title = when (it) { + true -> viewModel.fileUri?.readFileName(requireContext()) + + else -> getString(R.string.app_name) + } if (it) setNavigationIcon(R.drawable.ic_clear) else { diff --git a/app/src/main/java/com/f0x1d/logfox/utils/ExportImportUtils.kt b/app/src/main/java/com/f0x1d/logfox/utils/ExportImportUtils.kt index fc20de28..8d522823 100644 --- a/app/src/main/java/com/f0x1d/logfox/utils/ExportImportUtils.kt +++ b/app/src/main/java/com/f0x1d/logfox/utils/ExportImportUtils.kt @@ -30,11 +30,11 @@ fun OutputStream.exportFilters(context: Context, filters: List) = 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 { diff --git a/app/src/main/res/drawable/ic_clear.xml b/app/src/main/res/drawable/ic_clear.xml index 0b370b77..2f0de038 100644 --- a/app/src/main/res/drawable/ic_clear.xml +++ b/app/src/main/res/drawable/ic_clear.xml @@ -1,5 +1,4 @@ Date: Mon, 2 Oct 2023 00:02:38 +0800 Subject: [PATCH 6/7] feat: update zh_rCN translation (#31) --- app/src/main/res/values-zh-rCN/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 23123b30..d13e29c7 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -107,4 +107,5 @@ 包名 应用 你确定? + 查看 \ No newline at end of file From c06591386b148571830f8dac77d80d98be54f04c Mon Sep 17 00:00:00 2001 From: F0x1d Date: Sun, 1 Oct 2023 19:14:15 +0300 Subject: [PATCH 7/7] [fix]: added EDIT intent action --- app/src/main/AndroidManifest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a24ac453..c34777b8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -24,6 +24,8 @@ android:exported="true"> + +