From e14273642ddcf02806912e4642212bd1b445fda5 Mon Sep 17 00:00:00 2001 From: tonlirise Date: Thu, 4 Aug 2022 12:10:02 +0700 Subject: [PATCH] Added ability to save and view logs --- kotlin-extensions/build.gradle | 7 ++ logging/build.gradle | 16 +++- logging/src/main/AndroidManifest.xml | 16 +++- .../core/log_file/DebugLogsDialogFragment.kt | 84 +++++++++++++++++++ .../roboswag/core/log_file/LogFileManager.kt | 60 +++++++++++++ .../roboswag/core/log_file/LogItemAdapter.kt | 35 ++++++++ .../res/layout/dialog_fragment_debug_logs.xml | 51 +++++++++++ logging/src/main/res/layout/log_item.xml | 28 +++++++ logging/src/main/res/values/styles.xml | 8 ++ logging/src/main/res/xml/provider_paths.xml | 6 ++ 10 files changed, 309 insertions(+), 2 deletions(-) create mode 100644 logging/src/main/java/ru/touchin/roboswag/core/log_file/DebugLogsDialogFragment.kt create mode 100644 logging/src/main/java/ru/touchin/roboswag/core/log_file/LogFileManager.kt create mode 100644 logging/src/main/java/ru/touchin/roboswag/core/log_file/LogItemAdapter.kt create mode 100644 logging/src/main/res/layout/dialog_fragment_debug_logs.xml create mode 100644 logging/src/main/res/layout/log_item.xml create mode 100644 logging/src/main/res/values/styles.xml create mode 100644 logging/src/main/res/xml/provider_paths.xml diff --git a/kotlin-extensions/build.gradle b/kotlin-extensions/build.gradle index afbabac..50960e5 100644 --- a/kotlin-extensions/build.gradle +++ b/kotlin-extensions/build.gradle @@ -2,6 +2,7 @@ apply from: "../android-configs/lib-config.gradle" dependencies { implementation "androidx.recyclerview:recyclerview" + implementation "androidx.coordinatorlayout:coordinatorlayout" implementation "androidx.fragment:fragment-ktx" implementation project(path: ':logging') @@ -12,6 +13,12 @@ dependencies { } } + implementation("androidx.coordinatorlayout:coordinatorlayout") { + version { + require '1.1.0' + } + } + implementation("androidx.fragment:fragment-ktx") { version { require '1.2.1' diff --git a/logging/build.gradle b/logging/build.gradle index 57d4392..aaf00db 100644 --- a/logging/build.gradle +++ b/logging/build.gradle @@ -2,9 +2,11 @@ apply from: "../android-configs/lib-config.gradle" dependencies { implementation "androidx.annotation:annotation" - implementation "com.google.firebase:firebase-crashlytics" + implementation "androidx.recyclerview:recyclerview" + implementation "androidx.constraintlayout:constraintlayout" + constraints { implementation("androidx.annotation:annotation") { version { @@ -17,5 +19,17 @@ dependencies { require '17.1.0' } } + + implementation("androidx.recyclerview:recyclerview") { + version { + require '1.1.0' + } + } + + implementation("androidx.constraintlayout:constraintlayout"){ + version { + require '2.2.0-alpha03' + } + } } } diff --git a/logging/src/main/AndroidManifest.xml b/logging/src/main/AndroidManifest.xml index 8ce26b3..b655062 100644 --- a/logging/src/main/AndroidManifest.xml +++ b/logging/src/main/AndroidManifest.xml @@ -1 +1,15 @@ - + + + + + + + \ No newline at end of file diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log_file/DebugLogsDialogFragment.kt b/logging/src/main/java/ru/touchin/roboswag/core/log_file/DebugLogsDialogFragment.kt new file mode 100644 index 0000000..f34cd81 --- /dev/null +++ b/logging/src/main/java/ru/touchin/roboswag/core/log_file/DebugLogsDialogFragment.kt @@ -0,0 +1,84 @@ +package ru.touchin.roboswag.core.log_file + +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import androidx.core.content.FileProvider +import androidx.fragment.app.DialogFragment +import androidx.recyclerview.widget.LinearLayoutManager +import ru.touchin.roboswag.core.log.BuildConfig +import ru.touchin.roboswag.core.log.R +import ru.touchin.roboswag.core.log.databinding.DialogFragmentDebugLogsBinding +import ru.touchin.roboswag.core.log_file.LogFileManager.Companion.getLogDirectory +import java.io.File + +class DebugLogsDialogFragment : DialogFragment() { + + private val logItemsList: MutableList = mutableListOf() + private lateinit var binding: DialogFragmentDebugLogsBinding + + override fun getTheme(): Int = R.style.DialogFullscreenTheme + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + binding = DialogFragmentDebugLogsBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initSpinner() + + binding.logsRecycler.layoutManager = LinearLayoutManager(requireContext()) + binding.logsRecycler.adapter = LogItemAdapter(requireContext(), logItemsList) + + binding.shareBtn.setOnClickListener { + val files = getLogDirectory(requireContext()).listFiles() + + if (!files.isNullOrEmpty()) { + val uri = FileProvider.getUriForFile( + requireContext(), + BuildConfig.LIBRARY_PACKAGE_NAME + LogFileManager.fileProviderName, + files.first() + ) + + val intent = Intent(Intent.ACTION_SEND) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + intent.setType("*/*") + intent.putExtra(Intent.EXTRA_STREAM, uri) + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent) + } + } + } + + private fun initSpinner() { + val priorityTitle = LogFileManager.Priority.values().map { it.title } + val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, priorityTitle) + adapter.setDropDownViewResource(android.R.layout.simple_dropdown_item_1line) + + binding.priorityFilter.adapter = adapter; + binding.priorityFilter.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { + val priority = LogFileManager.Priority.values()[position] + LogFileManager.saveLogcatToFile(requireContext(), priority.tag) + } + + override fun onNothingSelected(parent: AdapterView<*>) {} + } + binding.updateBtn.setOnClickListener { updateRecycler() } + } + + private fun updateRecycler() { + logItemsList.clear() + val files = getLogDirectory(requireContext()).listFiles() + if (!files.isNullOrEmpty()) { + File(files.firstOrNull()?.getAbsolutePath() ?: "") + .useLines { lines -> lines.forEach { logItemsList.add(it) } } + } + binding.logsRecycler.adapter?.notifyDataSetChanged() + } +} diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log_file/LogFileManager.kt b/logging/src/main/java/ru/touchin/roboswag/core/log_file/LogFileManager.kt new file mode 100644 index 0000000..c17477b --- /dev/null +++ b/logging/src/main/java/ru/touchin/roboswag/core/log_file/LogFileManager.kt @@ -0,0 +1,60 @@ +package ru.touchin.roboswag.core.log_file + +import android.content.Context +import java.io.File +import java.io.IOException +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +class LogFileManager { + + enum class Priority(val title: String, val tag: String) { + VERBOSE("VERBOSE", "*:V"), + DEBUG("DEBUG", "*:D"), + INFO("INFO", "*:I"), + WARNING("WARNING", "*:W"), + ERROR("ERROR", "*:E"), + ASSERT("ASSERT", "*:A") + } + + companion object { + private const val logDirecroryName = "log" + const val fileProviderName = ".fileprovider" + + fun getLogDirectory(context: Context) : File{ + val appDirectory = context.getExternalFilesDir(null) + return File(appDirectory.toString() + "/$logDirecroryName") + } + + fun saveLogcatToFile(context: Context, priorityTag: String) { + val logDirectory = initLogDirectory(context) + + val sdf = SimpleDateFormat("yyyy-MM-dd'T'HH_mm_ss_SSS", Locale.getDefault()) + val logFile = File(logDirectory, "logcat_${sdf.format(Date())}.txt") + + try { + Runtime.getRuntime().exec("logcat ${priorityTag} -f $logFile") + } catch (e: IOException) { + e.printStackTrace() + } + } + + private fun initLogDirectory(context: Context): File { + val appDirectory = context.getExternalFilesDir(null) + if (!appDirectory!!.exists()) { + appDirectory.mkdir() + } + + val logDirectory = File(appDirectory.toString() + "/$logDirecroryName") + if (!logDirectory.exists()) { + logDirectory.mkdir() + } + + for (file in logDirectory.listFiles()) { + file.delete() + } + return logDirectory + } + } +} diff --git a/logging/src/main/java/ru/touchin/roboswag/core/log_file/LogItemAdapter.kt b/logging/src/main/java/ru/touchin/roboswag/core/log_file/LogItemAdapter.kt new file mode 100644 index 0000000..8b4306e --- /dev/null +++ b/logging/src/main/java/ru/touchin/roboswag/core/log_file/LogItemAdapter.kt @@ -0,0 +1,35 @@ +package ru.touchin.roboswag.core.log_file + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import ru.touchin.roboswag.core.log.databinding.LogItemBinding + +class LogItemAdapter(private val context: Context, private val logItemList:MutableList) + : RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LogItemViewHolder { + val binding = LogItemBinding.inflate(LayoutInflater.from(context),parent,false) + return LogItemViewHolder(binding) + } + + override fun onBindViewHolder(holder: LogItemViewHolder, position: Int) { + val foodItem = logItemList[position] + holder.bind(foodItem) + } + + override fun getItemCount(): Int { + return logItemList.size + } + + class LogItemViewHolder(logItemLayoutBinding: LogItemBinding) + : RecyclerView.ViewHolder(logItemLayoutBinding.root){ + + private val binding = logItemLayoutBinding + + fun bind(logItem: String){ + binding.logDescription.text = logItem + } + } +} diff --git a/logging/src/main/res/layout/dialog_fragment_debug_logs.xml b/logging/src/main/res/layout/dialog_fragment_debug_logs.xml new file mode 100644 index 0000000..e90c427 --- /dev/null +++ b/logging/src/main/res/layout/dialog_fragment_debug_logs.xml @@ -0,0 +1,51 @@ + + + + + +